# 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.
