deck_core/traits.rs
1//! Backend-agnostic trait surface. Each `deck-*` crate is a concrete
2//! implementation of one or more of these traits; the orchestrator depends
3//! only on these abstractions.
4
5use async_trait::async_trait;
6use futures::stream::BoxStream;
7
8use crate::error::Result;
9use crate::message::{Message, ToolCall, ToolResult};
10
11/// A streamable LLM backend (ollama HTTP, llama.cpp in-process, etc).
12#[async_trait]
13pub trait LlmBackend: Send + Sync {
14 /// Human-readable backend identifier (e.g. `"ollama:llama3.1"`).
15 fn id(&self) -> String;
16
17 /// Single-shot completion. Implementations MAY buffer a streamed
18 /// response internally — see [`stream`](Self::stream) for the streamed
19 /// API.
20 async fn complete(&self, model: &str, messages: &[Message]) -> Result<Message>;
21
22 /// Streamed completion. Yields incremental [`Message`] deltas; the last
23 /// delta has the final assistant turn.
24 async fn stream(
25 &self,
26 model: &str,
27 messages: &[Message],
28 ) -> Result<BoxStream<'static, Result<Message>>>;
29}
30
31/// An MCP client connected to a single server (one trait instance per
32/// declared server). Implementations wrap `rmcp` transports.
33#[async_trait]
34pub trait McpClient: Send + Sync {
35 fn server_name(&self) -> &str;
36
37 /// List the tools the server advertises.
38 async fn list_tools(&self) -> Result<Vec<ToolDescriptor>>;
39
40 /// Invoke a tool. The orchestrator is responsible for the human-in-the-
41 /// loop approval; this trait is the raw RPC.
42 async fn call(&self, call: &ToolCall) -> Result<ToolResult>;
43}
44
45#[derive(Debug, Clone)]
46pub struct ToolDescriptor {
47 pub name: String,
48 pub description: String,
49 pub json_schema: serde_json::Value,
50}
51
52/// Encrypted, durable session/context store.
53#[async_trait]
54pub trait Store: Send + Sync {
55 /// Persist (append) a message to a session log.
56 async fn append(&self, session: crate::message::SessionId, msg: &Message) -> Result<()>;
57
58 /// Load all messages for a session, in append order.
59 async fn load(&self, session: crate::message::SessionId) -> Result<Vec<Message>>;
60
61 /// Enumerate sessions stored in this deck.
62 async fn list(&self) -> Result<Vec<crate::message::SessionId>>;
63}
64
65/// Sandbox boundary: wrap an MCP server child process so that any tool
66/// invocation can only access an explicitly granted set of paths and
67/// syscalls.
68pub trait Sandbox: Send + Sync {
69 /// Returns a tag like `"seccomp+landlock"`, `"unsupported(macos)"`.
70 fn availability(&self) -> &'static str;
71
72 /// Whether the current platform can actually enforce a policy.
73 fn enforces(&self) -> bool;
74}