Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions specs/001-system-invariants/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ related:
| AnyProvider enum | `crates/zeph-llm/src/any.rs` |
| Message / MessagePart | `crates/zeph-llm/src/provider.rs` |
| ToolExecutor trait | `crates/zeph-tools/src/executor.rs` |
| Config struct | `crates/zeph-core/src/config/types/mod.rs` |
| Config struct | `crates/zeph-config/src/root.rs` |
| Feature flags | `Cargo.toml` |

---
Expand Down Expand Up @@ -158,7 +158,7 @@ file I/O in the agent loop.

## 8a. Provider Registry Contract

`[[llm.providers]]` (`crates/zeph-config/src/providers.rs`) is the **single source of truth** for all LLM provider declarations:
`[[llm.providers]]` (`crates/zeph-config/src/providers/` — `mod.rs`, `entry.rs`, `llm.rs`, `router.rs`, `thinking.rs`, `candle.rs`) is the **single source of truth** for all LLM provider declarations:

- All providers are declared **once** in `[[llm.providers]]` with a `name` field; no other section duplicates provider credentials or model names
- Subsystems that call LLMs reference a provider by name via a `*_provider` config field (e.g., `extract_provider = "fast"`)
Expand All @@ -177,7 +177,7 @@ See `.local/specs/022-config-simplification/spec.md` for the full schema and exa

Feature flags (`Cargo.toml [features]`):

- `default` includes features required for the standard CLI experience: database backend (`sqlite`), scheduler, task metrics, profiling instrumentation, and OS-level sandbox availability. See spec 029 §3.1 for the canonical list.
- `default = ["scheduler", "sqlite"]` — the minimal set required for the standard CLI experience: cron scheduler and the SQLite database backend. Per-task CPU/wall-time metrics are an always-on capability (no flag, see spec 029 §3.3), not a default feature. See spec 029 §3.1 for the canonical list.
- Default features must satisfy two criteria: (a) the feature gates a real optional dependency (not a pure behavioral marker), and (b) the feature has `Tested` status in coverage-status.md with no open P0/P1 issues.
- **Always-on** (compiled in without feature flags): openai, compatible, orchestrator, router, self-learning, qdrant, vault-age, mcp
- New optional crates: `dep:zeph-<name>` in the feature definition — never unconditionally import
Expand Down
94 changes: 51 additions & 43 deletions specs/002-agent-loop/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ related:
| File | Contents |
|---|---|
| `crates/zeph-core/src/agent/mod.rs` | `Agent<C>`, `run()`, `process_user_message()`, sub-state structs |
| `crates/zeph-core/src/agent/feedback_detector.rs` | `FeedbackDetector`, `CorrectionSignal` |
| `crates/zeph-agent-feedback/src/lib.rs` | `FeedbackDetector`, `CorrectionSignal` (re-exported into `zeph-core` via `crates/zeph-core/src/agent/corrections.rs` as `feedback_detector`) |
| `crates/zeph-core/src/agent/error.rs` | `AgentError` typed hierarchy |
| `crates/zeph-core/src/channel.rs` | `Channel` trait, `ChannelError` |

Expand Down Expand Up @@ -115,7 +115,7 @@ Agent<C: Channel> {

## HiAgent Subgoal-Aware Compaction

`crates/zeph-core/src/agent/compaction_strategy.rs`, `crates/zeph-core/src/agent/mod.rs`. Issue #2022.
`crates/zeph-agent-context/src/compaction.rs` (`SubgoalRegistry`), `crates/zeph-core/src/agent/context/summarization/{mod.rs,pruning.rs,compaction.rs}`. Issue #2022.

### Overview

Expand Down Expand Up @@ -205,7 +205,7 @@ Provider preference per channel is persisted to SQLite (#3308, #3385):
- Restored automatically on next session start
- Identity keyed by `(channel_type, channel_id)`; CLI/TUI use `channel_id = ""`
- Controlled by `[session] provider_persistence = true` (default enabled)
- Migrations: SQLite `079_channel_preferences.sql`, Postgres `075_channel_preferences.sql`
- Migrations: SQLite `080_channel_preferences.sql`, Postgres `075_channel_preferences.sql`

### Key Invariants

Expand Down Expand Up @@ -307,60 +307,68 @@ completion_phrases = ["task complete", "done", "finished", "completed"]

## TACO Output Compression (#3591)

TACO (Tool-output Automatic Compression and Offload) compresses large tool outputs
before they are injected into the context window. This is a targeted pre-injection
pass, distinct from the turn-level compaction that runs at the 60/90% pressure gates.
TACO (self-evolving tool output compression, `crates/zeph-tools/src/compression/`)
compresses large tool outputs before they are injected into the context window.
This is a targeted pre-injection pass, distinct from the turn-level compaction that
runs at the 60/90% pressure gates. It is **rule-based**, not a per-call LLM
summarization pass — regex rules matched against the raw output, not a threshold-
triggered prompt.

### Architecture

- `OutputCompressor` — core async trait (`compress(tool_name, output) -> Result<Option<String>, CompressionError>`)
- `IdentityCompressor` — default no-op, wired in when `[tools.compression] enabled = false`
- `RuleBasedCompressor` — regex rules loaded from SQLite/Postgres, compiled into
`parking_lot::RwLock<Vec<CompiledRule>>`; hit counts tracked in a
`dashmap::DashMap<String, AtomicU64>` so a rules-vec swap on reload cannot lose
unflushed counters
- `CompressedExecutor<E>` — decorator wrapping the root `ToolExecutor`; applies the
compressor to successful `ToolOutput.summary` strings only, on error the raw
result is returned unchanged
- `CompressionRuleStore` — SQLite/Postgres-backed rule persistence
- `safe_compile` — DoS-safe regex compilation with a configurable timeout

### When It Fires

TACO is evaluated after each tool call result is received, before the result is
appended to `messages`:
`CompressedExecutor::maybe_compress` runs after the wrapped executor returns a
successful `ToolOutput`:

1. Measure the raw tool output token count via `tiktoken-rs`
2. If `token_count > taco_threshold` AND the tool is in the compressible-tool set
→ run TACO compression
3. Compressed result replaces the raw result in `messages`
1. If `output.summary.lines().count() < min_lines_to_compress` → skip, output passed through
2. Otherwise, `RuleBasedCompressor::compress` applies rules in `id`-ascending order
(deterministic); a rule is skipped when its `glob` is set and does not match
`tool_name`; the first successful match wins
3. `Ok(None)` (no rule matched) or `Err(...)` → original `summary` is kept unchanged;
compression errors are logged as warnings but never propagate to the caller

### Compression Strategy
### Self-Evolution

TACO compression uses a fast prompt to summarize the tool output:

```
System: You are a concise tool-output summarizer. Preserve all data values,
file paths, exit codes, and structured content. Remove verbose headers and
repeated patterns. Target: under {target_tokens} tokens.
Tool output:
{raw_output}
```

The compressed result is tagged with `MessagePart::TacoCompressed` so the TUI and
audit log can distinguish it from raw output.

### Compressible Tool Set

Default: `["shell", "web_scrape", "read"]`. Configurable via
`[tools.taco] compressible_tools`. MCP tools are excluded from TACO by default
because their structured output schema is unknown.
New compression rules are periodically generated by an LLM call gated by
`evolution_provider` (empty string = evolution disabled) and rate-limited by
`evolution_min_interval_secs`. Rule count is capped at `max_rules`, pruning the
lowest-hit rules above the cap.

### Config

`[tools.compression]` TOML section (`ToolCompressionConfig`, `crates/zeph-config/src/tools.rs`):

```toml
[tools.taco]
enabled = false # default off (opt-in)
taco_threshold = 2000 # tokens; compress outputs above this
target_tokens = 500 # target compressed size
taco_provider = "" # [[llm.providers]] name; empty = primary
compressible_tools = ["shell", "web_scrape", "read"]
[tools.compression]
enabled = false # default off (opt-in)
min_lines_to_compress = 10 # skip outputs with fewer lines
evolution_provider = "" # [[llm.providers]] name; empty = self-evolution disabled
evolution_min_interval_secs = 3600 # rate limit between self-evolution runs
max_rules = 200 # prune lowest-hit rules above this
regex_compile_timeout_ms = 500 # DoS-safe compile timeout
```

### Key Invariants

- TACO fires only on output that exceeds `taco_threshold`; short outputs are passed through untouched
- On compression failure (provider error, timeout) the **raw output is used** — TACO is best-effort
- NEVER compress `tool_result` messages from `execute_tool_call_confirmed` (fenced-block path) — user-approved results must not be silently summarized
- NEVER apply TACO to thinking blocks or system prompt parts
- `taco_provider` is resolved via the provider registry at runtime; empty = primary provider
- Compressed results carry `MessagePart::TacoCompressed` to make compression auditable
- **T1**: `IdentityCompressor` is the only compressor when `[tools.compression] enabled = false`
- **T4**: audit logging happens inside individual tool implementations, not inside `CompressedExecutor` — because compression wraps *outside* the tool boundary, audit JSONL always records the raw pre-compression payload
- Rules are applied in `id`-ascending order (deterministic); the first successful match wins
- `regex::Regex::replace_all` guarantees linear time (no catastrophic backtracking); no `catch_unwind` needed around it
- On compression failure (bad pattern, DB error) the **raw output is used** — TACO is best-effort
- `evolution_provider` is resolved via the provider registry at runtime; empty = self-evolution disabled (not "primary provider")

---

Expand Down
14 changes: 8 additions & 6 deletions specs/003-llm-providers/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ related:
|---|---|
| `crates/zeph-llm/src/provider.rs` | `LlmProvider` trait, `Message`, `MessagePart`, `ChatResponse` |
| `crates/zeph-llm/src/any.rs` | `AnyProvider` enum dispatch |
| `crates/zeph-llm/src/claude.rs` | Claude impl, `split_system_into_blocks`, prompt caching |
| `crates/zeph-llm/src/openai.rs` | OpenAI impl |
| `crates/zeph-llm/src/claude/mod.rs` | Claude impl, `split_system_into_blocks`, prompt caching |
| `crates/zeph-llm/src/openai/mod.rs` | OpenAI impl |
| `crates/zeph-llm/src/ollama.rs` | Ollama impl |
| `crates/zeph-llm/src/compatible.rs` | OpenAI-compatible HTTP impl |
| `crates/zeph-llm/src/gemini.rs` | Gemini impl |
| `crates/zeph-llm/src/orchestrator.rs` | Multi-provider routing, Thompson Sampling, EMA |
| `crates/zeph-llm/src/gemini/mod.rs` | Gemini impl |
| `crates/zeph-llm/src/router/` (`thompson.rs`, `reputation.rs`, `builder.rs`, `config.rs`) | Multi-provider routing, Thompson Sampling, EMA reputation |

---

Expand Down Expand Up @@ -79,7 +79,9 @@ trait LlmProvider: Send + Sync {
Runtime dispatch — no `Box<dyn LlmProvider>` in hot paths:

```
AnyProvider { Claude, OpenAI, Ollama, Compatible, Candle, Gemini }
AnyProvider { Ollama, Claude, OpenAi, Gemini, Candle (feature "candle"),
Compatible, Router(Box<RouterProvider>), Triage(Box<TriageRouter>),
Gonka (feature "gonka"), Cocoon (feature "cocoon") }
```

## Implementations
Expand Down Expand Up @@ -133,7 +135,7 @@ The `prompt_cache_ttl` field defaults to `None` (interpreted as `ephemeral`). Sp

## Orchestrator

`crates/zeph-llm/src/orchestrator.rs` — multi-provider routing:
`crates/zeph-llm/src/router/` (`RouterProvider`, `thompson.rs`, `reputation.rs`) — multi-provider routing:

- **Rule-based routing**: match provider by name pattern, task type, or cost threshold
- **Thompson Sampling router**: Beta-distribution exploration/exploitation for model selection
Expand Down
2 changes: 1 addition & 1 deletion specs/004-memory/004-11-memory-hela-mem.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ folding_cooldown_turns = 10

## 5. Acceptance Criteria

- [x] `graph_edges` table has `weight` column after migration 077; existing rows default to 1.0 (HL-F1 — implemented #3344)
- [x] `graph_edges` table has `weight` column after migration 078 (`078_hebbian_edge_weight.sql`); existing rows default to 1.0 (HL-F1 — implemented #3344)
- [x] Co-activated node pairs show incremented weights after retrieval when `hebbian.enabled = true` (HL-F2 — implemented #3344)
- [x] Consolidation pass runs on background task (HL-F3/F4 — implemented #3345, #3380)
- [x] Spreading activation retrieval (`hela_spreading_recall`) implemented with BFS, multiplicative path weights, and circuit breaker (HL-F5 — implemented #3346)
Expand Down
4 changes: 2 additions & 2 deletions specs/004-memory/004-12-memory-reasoning-bank.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,12 @@ Token budget enforced by existing `ContextBudget` mechanism.

---

## 6. Implementation Priority
## 7. Implementation Priority

**P2 — Medium priority.** Core read/write path is 2 weeks estimated complexity. MaTTS scaling variant (more compute per task for diverse experiences) is a separate follow-up sprint. Recommend: SQLite table + distillation pipeline first, Qdrant retrieval second, context injection third.

---

## 7. Related Issues
## 8. Related Issues

- Closes #3312
8 changes: 4 additions & 4 deletions specs/004-memory/004-13-memory-memcot.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ related:

| File | Contents |
|---|---|
| `crates/zeph-memory/src/memcot/mod.rs` | Module root; `MemCotRecall` entry point |
| `crates/zeph-memory/src/memcot/accumulator.rs` | `SemanticStateAccumulator` — per-turn state tracking |
| `crates/zeph-memory/src/memcot/zoom.rs` | `zoom_in()` and `zoom_out()` retrieval views |
| `crates/zeph-memory/src/memcot/config.rs` | `MemCotConfig` TOML bindings |
| `crates/zeph-core/src/agent/memcot/mod.rs` | Module root; `MemCotRecall` entry point |
| `crates/zeph-core/src/agent/memcot/accumulator.rs` | `SemanticStateAccumulator` — per-turn state tracking |
| `crates/zeph-core/src/agent/memcot/metrics.rs` | MemCoT metrics |
| `crates/zeph-memory/src/recall_view.rs` | `zoom_in()` and `zoom_out()` retrieval views (no separate `zoom.rs`/`config.rs`) |

---

Expand Down
20 changes: 10 additions & 10 deletions specs/004-memory/004-14-memory-tiering-rfc-decision.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ Zeph's memory subsystem already implements:
| | Async consolidation daemon | Yes — HeLa-Mem consolidation pass exists | Exact algorithm differs | *Subsumed* — HeLa-Mem covers the pattern |
| | PPO-based weight adaptation | No — no RL loop in retrieval path | RL infrastructure missing | *Partial gap* — defer to future cycle (P4 research) |
| **BudgetMem** | Per-query budget-tier routing | Partial — routing exists but not cost-aware | Cost model missing | *Adopt* — layer cost-aware router onto existing tiers |
| | Low/Mid/High tier selection | Yes — [[024-complexity-triage-routing]] implements complexity-based dispatch | Same as complexity routing | *Subsumed* — reuse existing routing layer |
| | Low/Mid/High tier selection | Yes — [[023-complexity-triage-routing/spec]] implements complexity-based dispatch | Same as complexity routing | *Subsumed* — reuse existing routing layer |
| **Multi-Layer** | Working/episodic/semantic separation | Yes — SQLite working/episodic; Qdrant semantic | Explicit retrieval gating missing | *Adopt* — formalize tier-aware recall gating in SemanticMemory |
| | Adaptive retrieval gating | Partial — MemMachine depth is configurable | Policy-based adaptive gating is new | *Partial gap* — layer adaptive gating logic |
| **LCM** | Immutable message log | Yes — SQLite messages table is immutable | Complete match | *Subsumed* — log already exists |
| | Derived active context | Yes — [[021-zeph-context]] compaction state machine | Complete match | *Subsumed* — compaction strategy is active context |
| | Derived active context | Yes — [[021-zeph-context/spec]] compaction state machine | Complete match | *Subsumed* — compaction strategy is active context |
| **MemRouter** | Learned write-side admission | No — A-MAC is rule-based; no learned gate | Neural classifier missing | *Partial gap* — extend A-MAC with optional learned gate (P3) |
| | Embedding-based routing | Yes — MMR and SYNAPSE use embeddings | Same capability | *Subsumed* — already applies embeddings |

Expand All @@ -127,15 +127,15 @@ Zeph's memory subsystem already implements:
2. **Three additive improvements** are valuable and feasible:
- Formalize tier-aware retrieval gating (Multi-Layer §3)
- Add frequency signal to A-MAC (MEMTIER §3.2)
- Layer cost-aware routing onto triage router (BudgetMem + [[024-complexity-triage-routing]])
- Layer cost-aware routing onto triage router (BudgetMem + [[023-complexity-triage-routing/spec]])

3. **RL adaptation (MEMTIER PPO)** is deferred — infrastructure cost is high for a P4 feature; HeLa-Mem consolidation addresses most of the value

4. **Learned admission (MemRouter)** is a P3 follow-up — valuable but requires labeled training data; A-MAC is sufficient for MVP

### Non-Adopted Papers

- **LCM**: Fully subsumed by existing [[021-zeph-context]] and message immutability invariant
- **LCM**: Fully subsumed by existing [[021-zeph-context/spec]] and message immutability invariant
- **MEMTIER's PPO loop**: Deferred pending RL infrastructure (P4 research track)
- **MemRouter's neural gate**: Deferred to P3 pending labeled dataset for training

Expand Down Expand Up @@ -183,15 +183,15 @@ When a query of complexity C arrives:
THEN search all three tiers, aggregate by relevance
```

Complexity classification already exists in [[024-complexity-triage-routing]]; apply same classification to memory tiers.
Complexity classification already exists in [[023-complexity-triage-routing/spec]]; apply same classification to memory tiers.

**Rationale**: Multi-Layer Framework showed +4pp improvement by avoiding full semantic search for simple queries, reducing token cost.

### 5.3 Triage Router Extension: Cost-Aware Tier Selection

**File**: `specs/024-complexity-triage-routing/spec.md`
**File**: `specs/023-complexity-triage-routing/spec.md`

Extend [[024-complexity-triage-routing]] to include memory tier cost model:
Extend [[023-complexity-triage-routing/spec]] to include memory tier cost model:

```toml
[memory.tier_routing]
Expand Down Expand Up @@ -235,7 +235,7 @@ This maps BudgetMem's Low/Mid/High routing onto Zeph's existing complexity triag

3. **Phase 3 (P4)**: RL weight adaptation (defer)
- Requires reward signal infrastructure
- Blocked on [[042-experiments]] completing experiment framework
- Blocked on [[041-experiments/spec]] completing experiment framework

---

Expand Down Expand Up @@ -265,5 +265,5 @@ This maps BudgetMem's Low/Mid/High routing onto Zeph's existing complexity triag
- [[004-memory/spec]] — Parent spec
- [[004-3-admission-control]] — A-MAC (to be extended)
- [[004-10-memory-memmachine-retrieval]] — Retrieval depth (complements tier gating)
- [[024-complexity-triage-routing]] — Complexity-based routing (to be extended)
- [[021-zeph-context]] — Context budget (orthogonal; no changes needed)
- [[023-complexity-triage-routing/spec]] — Complexity-based routing (to be extended)
- [[021-zeph-context/spec]] — Context budget (orthogonal; no changes needed)
Loading
Loading