353 lines
9.9 KiB
Markdown
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.
|