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
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

## [0.9.6] - 2026-02-15

### Changed
- **BREAKING**: `ToolDef` schema field replaced `Vec<ParamDef>` with `schemars::Schema` auto-derived from Rust structs via `#[derive(JsonSchema)]`
- **BREAKING**: `ParamDef` and `ParamType` removed from `zeph-tools` public API
- **BREAKING**: `ToolRegistry::new()` replaced with `ToolRegistry::from_definitions()`; registry no longer hardcodes built-in tools — each executor owns its definitions via `tool_definitions()`
- **BREAKING**: `Channel` trait now requires `ChannelError` enum with typed error handling replacing `anyhow::Result`
- **BREAKING**: `Agent::new()` signature changed to accept new field grouping; agent struct refactored into 5 inner structs for improved organization
- **BREAKING**: `AgentError` enum introduced with 7 typed variants replacing scattered `anyhow::Error` handling
- `ToolDef` now includes `InvocationHint` (FencedBlock/ToolCall) so LLM prompt shows exact invocation format per tool
- `web_scrape` tool definition includes all parameters (`url`, `select`, `extract`, `limit`) auto-derived from `ScrapeInstruction`
- `ShellExecutor` and `WebScrapeExecutor` now implement `tool_definitions()` for single source of truth
- Replaced `tokio` "full" feature with granular features in zeph-core (async-io, macros, rt, sync, time)
- Removed `anyhow` dependency from zeph-channels
- Message persistence now uses `MessageKind` enum instead of `is_summary` bool for qdrant storage

### Added
- `ChannelError` enum with typed variants for channel operation failures
- `AgentError` enum with 7 typed variants for agent operation failures (streaming, persistence, configuration, etc.)
- Workspace-level `qdrant` feature flag for optional semantic memory support
- Type aliases consolidated into zeph-llm: `EmbedFuture` and `EmbedFn` with typed `LlmError`
- `streaming.rs` and `persistence.rs` modules extracted from agent module for improved code organization
- `MessageKind` enum for distinguishing summary and regular messages in storage

### Removed
- `anyhow::Result` from Channel trait (replaced with `ChannelError`)
- Direct `anyhow::Error` usage in agent module (replaced with `AgentError`)

## [0.9.5] - 2026-02-14

Expand Down Expand Up @@ -699,3 +719,26 @@ let agent = Agent::new(provider, channel, &skills_prompt, executor);
- Agent::run() uses channel.recv()/send() instead of direct I/O
- Agent calls channel.send_typing() before each LLM request
- Agent::run() uses tokio::select! to race channel messages against shutdown signal

[Unreleased]: https://github.com/bug-ops/zeph/compare/v0.9.6...HEAD
[0.9.6]: https://github.com/bug-ops/zeph/compare/v0.9.5...v0.9.6
[0.9.5]: https://github.com/bug-ops/zeph/compare/v0.9.4...v0.9.5
[0.9.4]: https://github.com/bug-ops/zeph/compare/v0.9.3...v0.9.4
[0.9.3]: https://github.com/bug-ops/zeph/compare/v0.9.2...v0.9.3
[0.9.2]: https://github.com/bug-ops/zeph/compare/v0.9.1...v0.9.2
[0.9.1]: https://github.com/bug-ops/zeph/compare/v0.9.0...v0.9.1
[0.9.0]: https://github.com/bug-ops/zeph/compare/v0.8.2...v0.9.0
[0.8.2]: https://github.com/bug-ops/zeph/compare/v0.8.1...v0.8.2
[0.8.1]: https://github.com/bug-ops/zeph/compare/v0.8.0...v0.8.1
[0.8.0]: https://github.com/bug-ops/zeph/compare/v0.7.1...v0.8.0
[0.7.1]: https://github.com/bug-ops/zeph/compare/v0.7.0...v0.7.1
[0.7.0]: https://github.com/bug-ops/zeph/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/bug-ops/zeph/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/bug-ops/zeph/compare/v0.4.3...v0.5.0
[0.4.3]: https://github.com/bug-ops/zeph/compare/v0.4.2...v0.4.3
[0.4.2]: https://github.com/bug-ops/zeph/compare/v0.4.1...v0.4.2
[0.4.1]: https://github.com/bug-ops/zeph/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/bug-ops/zeph/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/bug-ops/zeph/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/bug-ops/zeph/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/bug-ops/zeph/releases/tag/v0.1.0
22 changes: 11 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 11 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ resolver = "3"
[workspace.package]
edition = "2024"
rust-version = "1.88"
version = "0.9.5"
version = "0.9.6"
authors = ["bug-ops"]
license = "MIT"
repository = "https://github.com/bug-ops/zeph"
Expand Down Expand Up @@ -59,16 +59,16 @@ tree-sitter = "0.26"
unicode-width = "0.2"
url = "2.5"
uuid = "1.20"
zeph-a2a = { path = "crates/zeph-a2a", version = "0.9.4" }
zeph-channels = { path = "crates/zeph-channels", version = "0.9.4" }
zeph-core = { path = "crates/zeph-core", version = "0.9.4" }
zeph-index = { path = "crates/zeph-index", version = "0.9.4" }
zeph-llm = { path = "crates/zeph-llm", version = "0.9.4" }
zeph-mcp = { path = "crates/zeph-mcp", version = "0.9.4" }
zeph-memory = { path = "crates/zeph-memory", version = "0.9.4" }
zeph-skills = { path = "crates/zeph-skills", version = "0.9.4" }
zeph-tools = { path = "crates/zeph-tools", version = "0.9.4" }
zeph-tui = { path = "crates/zeph-tui", version = "0.9.4" }
zeph-a2a = { path = "crates/zeph-a2a", version = "0.9.6" }
zeph-channels = { path = "crates/zeph-channels", version = "0.9.6" }
zeph-core = { path = "crates/zeph-core", version = "0.9.6" }
zeph-index = { path = "crates/zeph-index", version = "0.9.6" }
zeph-llm = { path = "crates/zeph-llm", version = "0.9.6" }
zeph-mcp = { path = "crates/zeph-mcp", version = "0.9.6" }
zeph-memory = { path = "crates/zeph-memory", version = "0.9.6" }
zeph-skills = { path = "crates/zeph-skills", version = "0.9.6" }
zeph-tools = { path = "crates/zeph-tools", version = "0.9.6" }
zeph-tui = { path = "crates/zeph-tui", version = "0.9.6" }

[workspace.lints.clippy]
all = "warn"
Expand Down
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,13 @@ cargo build --release --features tui
## Architecture

```
zeph (binary)
├── zeph-core — agent loop, config, config hot-reload, context builder, metrics
├── zeph-llm — LlmProvider: Ollama, Claude, OpenAI, Candle, orchestrator
├── zeph-skills — SKILL.md parser, embedding matcher, hot-reload, self-learning
├── zeph-memory — SQLite + Qdrant, semantic recall, summarization
zeph (binary) — bootstrap, AnyChannel dispatch, vault resolution (anyhow for top-level errors)
├── zeph-core — Agent split into 7 submodules (context, streaming, persistence,
│ learning, mcp, index), typed AgentError/ChannelError, config hot-reload
├── zeph-llm — LlmProvider: Ollama, Claude, OpenAI, Candle, orchestrator,
│ typed LlmError, EmbedFuture/EmbedFn type aliases
├── zeph-skills — SKILL.md parser, embedding matcher, hot-reload, self-learning, typed SkillError
├── zeph-memory — SQLite + Qdrant, semantic recall, summarization, typed MemoryError
├── zeph-index — AST-based code indexing, semantic retrieval, repo map (optional)
├── zeph-channels — Telegram adapter (teloxide) with streaming
├── zeph-tools — schemars-driven tool registry (shell, file ops, web scrape), composite dispatch
Expand All @@ -118,6 +120,12 @@ zeph (binary)
└── zeph-tui — ratatui TUI dashboard with live agent metrics (optional)
```

**Error handling:** Typed errors throughout all library crates -- `AgentError` (7 variants), `ChannelError` (4 variants), `LlmError`, `MemoryError`, `SkillError`. `anyhow` is used only in `main.rs` for top-level orchestration.

**Agent decomposition:** The agent module in `zeph-core` is split into 7 submodules (`mod.rs`, `context.rs`, `streaming.rs`, `persistence.rs`, `learning.rs`, `mcp.rs`, `index.rs`) with 5 inner field-grouping structs (`MemoryState`, `SkillState`, `ContextState`, `McpState`, `IndexState`).

**MessageKind enum:** Replaces the previous `is_summary` boolean with an explicit variant-based message classification.

> [!IMPORTANT]
> Requires Rust 1.88+ (Edition 2024). Native async traits — no `async-trait` crate dependency.

Expand All @@ -132,9 +140,10 @@ Deep dive: [Architecture overview](https://bug-ops.github.io/zeph/architecture/o
| `mcp` | On | MCP client for external tool servers |
| `candle` | On | Local HuggingFace inference (GGUF) |
| `orchestrator` | On | Multi-model routing with fallback |
| `qdrant` | On | Qdrant vector search for skills and MCP tools (opt-out) |
| `self-learning` | On | Skill evolution system |
| `vault-age` | On | Age-encrypted secret storage |
| `index` | Off | AST-based code indexing and semantic retrieval |
| `index` | On | AST-based code indexing and semantic retrieval |
| `metal` | Off | Metal GPU acceleration (macOS) |
| `tui` | Off | ratatui TUI dashboard with real-time metrics |
| `cuda` | Off | CUDA GPU acceleration (Linux) |
Expand Down
18 changes: 12 additions & 6 deletions crates/zeph-core/src/agent/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl<P: LlmProvider + Clone + 'static, C: Channel, T: ToolExecutor> Agent<P, C,
should
}

pub(super) async fn compact_context(&mut self) -> anyhow::Result<()> {
pub(super) async fn compact_context(&mut self) -> Result<(), super::error::AgentError> {
let preserve_tail = self.context_state.compaction_preserve_tail;

if self.messages.len() <= preserve_tail + 1 {
Expand Down Expand Up @@ -182,7 +182,7 @@ impl<P: LlmProvider + Clone + 'static, C: Channel, T: ToolExecutor> Agent<P, C,
clippy::cast_possible_truncation,
clippy::cast_sign_loss
)]
pub(super) async fn maybe_compact(&mut self) -> anyhow::Result<()> {
pub(super) async fn maybe_compact(&mut self) -> Result<(), super::error::AgentError> {
if !self.should_compact() {
return Ok(());
}
Expand Down Expand Up @@ -236,7 +236,7 @@ impl<P: LlmProvider + Clone + 'static, C: Channel, T: ToolExecutor> Agent<P, C,
&mut self,
query: &str,
token_budget: usize,
) -> anyhow::Result<()> {
) -> Result<(), super::error::AgentError> {
self.remove_recall_messages();

let Some(memory) = &self.memory_state.memory else {
Expand Down Expand Up @@ -333,7 +333,7 @@ impl<P: LlmProvider + Clone + 'static, C: Channel, T: ToolExecutor> Agent<P, C,
&mut self,
query: &str,
token_budget: usize,
) -> anyhow::Result<()> {
) -> Result<(), super::error::AgentError> {
self.remove_cross_session_messages();

let (Some(memory), Some(cid)) =
Expand Down Expand Up @@ -380,7 +380,10 @@ impl<P: LlmProvider + Clone + 'static, C: Channel, T: ToolExecutor> Agent<P, C,
Ok(())
}

async fn inject_summaries(&mut self, token_budget: usize) -> anyhow::Result<()> {
async fn inject_summaries(
&mut self,
token_budget: usize,
) -> Result<(), super::error::AgentError> {
self.remove_summary_messages();

let (Some(memory), Some(cid)) =
Expand Down Expand Up @@ -465,7 +468,10 @@ impl<P: LlmProvider + Clone + 'static, C: Channel, T: ToolExecutor> Agent<P, C,
}
}

pub(super) async fn prepare_context(&mut self, query: &str) -> anyhow::Result<()> {
pub(super) async fn prepare_context(
&mut self,
query: &str,
) -> Result<(), super::error::AgentError> {
let Some(ref budget) = self.context_state.budget else {
return Ok(());
};
Expand Down
23 changes: 23 additions & 0 deletions crates/zeph-core/src/agent/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#[derive(Debug, thiserror::Error)]
pub enum AgentError {
#[error(transparent)]
Llm(#[from] zeph_llm::LlmError),

#[error(transparent)]
Channel(#[from] crate::channel::ChannelError),

#[error(transparent)]
Memory(#[from] zeph_memory::MemoryError),

#[error(transparent)]
Skill(#[from] zeph_skills::SkillError),

#[error(transparent)]
Tool(#[from] zeph_tools::executor::ToolError),

#[error("I/O error: {0}")]
Io(#[from] std::io::Error),

#[error("{0}")]
Other(String),
}
7 changes: 5 additions & 2 deletions crates/zeph-core/src/agent/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ impl<P: LlmProvider + Clone + 'static, C: Channel, T: ToolExecutor> Agent<P, C,
&mut self,
query: &str,
token_budget: usize,
) -> anyhow::Result<()> {
) -> Result<(), super::error::AgentError> {
let Some(retriever) = &self.index.retriever else {
return Ok(());
};
if token_budget == 0 {
return Ok(());
}

let result = retriever.retrieve(query, token_budget).await?;
let result = retriever
.retrieve(query, token_budget)
.await
.map_err(|e| super::error::AgentError::Other(format!("{e:#}")))?;
let context_text = zeph_index::retriever::format_as_context(&result);

if !context_text.is_empty() {
Expand Down
Loading
Loading