Files
dirigent/crates/dirigent_protocol/README.md
T
2026-05-08 01:59:04 +02:00

353 lines
9.9 KiB
Markdown

# Dirigent Protocol
**Version:** 0.2.0
A Rust protocol library for agent-client interactions, aligned with **Agent-Client Protocol (ACP)** and **Model Context Protocol (MCP)** standards.
## Overview
The Dirigent Protocol provides a structured, streaming-first event model for real-time agent interactions. It's designed to support multi-agent orchestration, tool execution, and rich content streaming while maintaining compatibility with standard protocols.
## Features
- **ACP-Style Streaming**: Real-time content updates via `SessionUpdate` events
- **MCP-Compatible Content**: Structured `ContentBlock` representation
- **Tool Lifecycle Management**: Complete tool call tracking from initiation to completion
- **Provider Agnostic**: Adapter system for integrating different AI providers
- **Type-Safe**: Strongly-typed Rust API with comprehensive serde support
- **Extensible**: Provider metadata and extensibility hooks
## Quick Start
Add to your `Cargo.toml`:
```toml
[dependencies]
dirigent_protocol = "0.2"
```
### Basic Usage
```rust
use dirigent_protocol::{Event, SessionUpdate, ContentBlock};
fn handle_event(event: Event) {
match event {
Event::SessionUpdate { session_id, update } => {
match update {
SessionUpdate::AgentMessageChunk { message_id, content, .. } => {
if let ContentBlock::Text { text } = content {
println!("Agent says: {}", text);
}
}
SessionUpdate::ToolCall { tool_call, .. } => {
println!("Tool called: {}", tool_call.tool_name);
}
_ => {}
}
}
Event::SessionCreated { session } => {
println!("New session: {}", session.id);
}
_ => {}
}
}
```
## Core Concepts
### SessionUpdate Event Model
The protocol uses `SessionUpdate` for all streaming content:
```rust
pub enum SessionUpdate {
UserMessageChunk { message_id: String, content: ContentBlock, _meta: Option<Meta> },
AgentMessageChunk { message_id: String, content: ContentBlock, _meta: Option<Meta> },
AgentThoughtChunk { message_id: String, content: ContentBlock, _meta: Option<Meta> },
ToolCall { message_id: String, tool_call: ToolCall, _meta: Option<Meta> },
ToolCallUpdate { message_id: String, tool_call_id: String, tool_call: ToolCall, _meta: Option<Meta> },
}
```
### ContentBlock Types
Structured content representation:
```rust
pub enum ContentBlock {
Text { text: String },
ResourceLink {
uri: String,
name: Option<String>,
mime_type: Option<String>,
},
}
```
### Tool Call Lifecycle
Complete tool execution tracking:
```rust
pub struct ToolCall {
pub id: ToolCallId,
pub tool_name: String,
pub status: ToolCallStatus, // Pending → Running → Completed/Error
pub content: Vec<ContentBlock>,
pub raw_input: Option<Value>,
pub raw_output: Option<Value>,
pub title: Option<String>,
pub error: Option<String>,
pub metadata: Option<Value>,
}
```
## Documentation
- **[Streaming Model](docs/streaming_model.md)** - Detailed guide to the SessionUpdate event system
- **[Migration Guide](docs/migration_from_0.1.md)** - Upgrading from 0.1.x to 0.2.0
- **[CHANGELOG.md](CHANGELOG.md)** - Version history and breaking changes
- **[Examples](examples/)** - Working code examples
## Examples
### Streaming Text
```rust
use dirigent_protocol::{Event, SessionUpdate, ContentBlock};
// Agent streaming response
Event::SessionUpdate {
session_id: "session_123".to_string(),
update: SessionUpdate::AgentMessageChunk {
message_id: "msg_1".to_string(),
content: ContentBlock::Text {
text: "Hello, world!".to_string(),
},
_meta: None,
},
}
```
### Tool Execution
```rust
use dirigent_protocol::{Event, SessionUpdate, ToolCall, ToolCallStatus};
// Tool call initiated
Event::SessionUpdate {
session_id: "session_123".to_string(),
update: SessionUpdate::ToolCall {
message_id: "msg_1".to_string(),
tool_call: ToolCall {
id: "call_1".to_string(),
tool_name: "bash".to_string(),
status: ToolCallStatus::Pending,
content: vec![],
raw_input: Some(json!({"command": "ls"})),
raw_output: None,
title: Some("List files".to_string()),
error: None,
metadata: None,
},
_meta: None,
},
}
// Tool call completed
Event::SessionUpdate {
session_id: "session_123".to_string(),
update: SessionUpdate::ToolCallUpdate {
message_id: "msg_1".to_string(),
tool_call_id: "call_1".to_string(),
tool_call: ToolCall {
id: "call_1".to_string(),
tool_name: "bash".to_string(),
status: ToolCallStatus::Completed,
content: vec![
ContentBlock::Text {
text: "file1.txt\nfile2.txt".to_string(),
},
],
raw_input: Some(json!({"command": "ls"})),
raw_output: Some(json!({"exit_code": 0})),
title: Some("List files".to_string()),
error: None,
metadata: None,
},
_meta: None,
},
}
```
### Agent Thinking
```rust
use dirigent_protocol::{Event, SessionUpdate, ContentBlock};
// Agent internal reasoning
Event::SessionUpdate {
session_id: "session_123".to_string(),
update: SessionUpdate::AgentThoughtChunk {
message_id: "msg_1".to_string(),
content: ContentBlock::Text {
text: "Analyzing the user's request...".to_string(),
},
_meta: None,
},
}
```
## Adapters
The protocol includes adapter modules for translating provider-specific events:
- **OpenCode Adapter**: Converts OpenCode.ai events to Dirigent Protocol
- **REST Adapter**: Translates REST API responses
### Using an Adapter
```rust
use dirigent_protocol::adapters::opencode::OpenCodeAdapter;
let adapter = OpenCodeAdapter::new();
let dirigent_events = adapter.translate_event(opencode_event);
```
## Version History
### 0.2.0 (Current)
**Breaking Changes:**
- Removed `Event::MessagePartAdded` (replaced with `SessionUpdate`)
**New Features:**
- ACP-style `SessionUpdate` event system
- MCP-compatible `ContentBlock` types
- Structured `ToolCall` with lifecycle tracking
- Provider metadata via `_meta` field
See [CHANGELOG.md](CHANGELOG.md) for details.
### 0.1.0
Initial release with basic event types and adapters.
## Migration from 0.1.x
If you're upgrading from version 0.1.x, see the [Migration Guide](docs/migration_from_0.1.md) for detailed instructions and examples.
**Quick Summary:**
- Replace `Event::MessagePartAdded` with `Event::SessionUpdate`
- Use `SessionUpdate` variants instead of `MessagePart`
- Handle tool lifecycle with `ToolCall` and `ToolCallUpdate`
- Access `ContentBlock::Text { text }` instead of `MessagePart::Text { content }`
## Architecture
```
dirigent_protocol/
├── src/
│ ├── types/ # Core types
│ │ ├── content.rs # ContentBlock definitions
│ │ ├── updates.rs # SessionUpdate variants
│ │ ├── tool.rs # Tool call types
│ │ └── meta.rs # Provider metadata
│ ├── session.rs # Session types
│ ├── conversation.rs # Message types
│ ├── events.rs # Event enum
│ └── adapters/ # Provider adapters
│ ├── opencode.rs # OpenCode adapter
│ └── rest.rs # REST adapter
├── docs/ # Documentation
├── examples/ # Code examples
└── tests/ # Integration tests
```
## Event Types
The protocol defines several event categories:
### Session Events
- `SessionCreated` - New session started
- `SessionUpdated` - Session metadata changed
- `SessionDeleted` - Session removed
- `SessionsListed` - Available sessions returned
### Streaming Events
- `SessionUpdate` - Real-time content/tool updates (see SessionUpdate variants above)
### Message Events
- `MessageStarted` - Message creation initiated
- `MessageCompleted` - Message finalized
- `MessageDeleted` - Message removed
### System Events
- `ConnectorStateChanged` - Connector status changed
- `Error` - Error occurred
## Best Practices
### For Consumers
1. **Use SessionUpdate for streaming**: The new event model provides granular updates
2. **Track tools by ID**: Maintain a HashMap of tool calls keyed by `tool_call_id`
3. **Replace tool state on update**: `ToolCallUpdate` sends complete state, not deltas
4. **Distinguish thoughts from messages**: Render `AgentThoughtChunk` differently (e.g., collapsible sections)
5. **Handle optional fields**: Provider metadata (`_meta`) may not always be present
### For Adapters
1. **Preserve provider info**: Store original IDs in `_meta.provider` for debugging
2. **Send complete tool state**: Include all tool call fields in updates
3. **Use appropriate chunk types**: Choose User/Agent/Thought for correct semantics
4. **Keep metadata minimal**: Avoid large payloads in `_meta` (use excerpts)
## Testing
Run the test suite:
```bash
cargo test
```
Run with output:
```bash
cargo test -- --nocapture
```
## Contributing
Contributions welcome! Please ensure:
- All tests pass
- New features include tests
- Documentation is updated
- Code follows Rust conventions
## License
[License information here]
## Related Projects
- **dirigent_core** - Multi-agent orchestration runtime
- **opencode_client** - OpenCode.ai HTTP client library
- **dirigent_archive** - Session persistence
## Support
For questions or issues:
- Check the [documentation](docs/)
- Review [examples](examples/)
- Open an issue on the repository
## Standards Alignment
This protocol is designed to align with:
- **Agent-Client Protocol (ACP)** - Streaming model and event types
- **Model Context Protocol (MCP)** - Content block structure
Differences from standards are documented for compatibility and future convergence.