1use crate::error::KernelResult;
12use crate::event::{EventRecord, TokenUsage};
13use crate::ids::{ApprovalId, BranchId, RunId, SessionId, ToolRunId};
14use crate::policy::Capability;
15use crate::tool::{ToolCall, ToolOutcome};
16use async_trait::async_trait;
17use chrono::{DateTime, Utc};
18use futures_util::stream::BoxStream;
19use serde::{Deserialize, Serialize};
20
21pub type EventRecordStream = BoxStream<'static, KernelResult<EventRecord>>;
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ModelCompletionRequest {
25 pub session_id: SessionId,
26 pub branch_id: BranchId,
27 pub run_id: RunId,
28 pub step_index: u32,
29 pub objective: String,
30 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub proposed_tool: Option<ToolCall>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35#[serde(tag = "type", rename_all = "snake_case")]
36pub enum ModelDirective {
37 TextDelta {
38 delta: String,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 index: Option<u32>,
41 },
42 Message {
43 role: String,
44 content: String,
45 },
46 ToolCall {
47 call: ToolCall,
48 },
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52#[serde(rename_all = "snake_case")]
53pub enum ModelStopReason {
54 Completed,
55 ToolCall,
56 MaxIterations,
57 Cancelled,
58 Error,
59 Other(String),
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct ModelCompletion {
64 pub provider: String,
65 pub model: String,
66 #[serde(default)]
67 pub directives: Vec<ModelDirective>,
68 pub stop_reason: ModelStopReason,
69 #[serde(default, skip_serializing_if = "Option::is_none")]
70 pub usage: Option<TokenUsage>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 pub final_answer: Option<String>,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct ToolExecutionRequest {
77 pub session_id: SessionId,
78 pub workspace_root: String,
79 pub call: ToolCall,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ToolExecutionReport {
84 pub tool_run_id: ToolRunId,
85 pub call_id: String,
86 pub tool_name: String,
87 pub exit_status: i32,
88 pub duration_ms: u64,
89 pub outcome: ToolOutcome,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct PolicyGateDecision {
94 #[serde(default)]
95 pub allowed: Vec<Capability>,
96 #[serde(default)]
97 pub requires_approval: Vec<Capability>,
98 #[serde(default)]
99 pub denied: Vec<Capability>,
100}
101
102impl PolicyGateDecision {
103 pub fn is_allowed_now(&self) -> bool {
104 self.denied.is_empty() && self.requires_approval.is_empty()
105 }
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct ApprovalRequest {
110 pub session_id: SessionId,
111 pub call_id: String,
112 pub tool_name: String,
113 pub capability: Capability,
114 pub reason: String,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct ApprovalTicket {
119 pub approval_id: ApprovalId,
120 pub session_id: SessionId,
121 pub call_id: String,
122 pub tool_name: String,
123 pub capability: Capability,
124 pub reason: String,
125 pub created_at: DateTime<Utc>,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct ApprovalResolution {
130 pub approval_id: ApprovalId,
131 pub approved: bool,
132 pub actor: String,
133 pub resolved_at: DateTime<Utc>,
134}
135
136#[async_trait]
137pub trait EventStorePort: Send + Sync {
138 async fn append(&self, event: EventRecord) -> KernelResult<EventRecord>;
139 async fn read(
140 &self,
141 session_id: SessionId,
142 branch_id: BranchId,
143 from_sequence: u64,
144 limit: usize,
145 ) -> KernelResult<Vec<EventRecord>>;
146 async fn head(&self, session_id: SessionId, branch_id: BranchId) -> KernelResult<u64>;
147 async fn subscribe(
148 &self,
149 session_id: SessionId,
150 branch_id: BranchId,
151 after_sequence: u64,
152 ) -> KernelResult<EventRecordStream>;
153}
154
155#[async_trait]
156pub trait ModelProviderPort: Send + Sync {
157 async fn complete(&self, request: ModelCompletionRequest) -> KernelResult<ModelCompletion>;
158}
159
160#[async_trait]
161pub trait ToolHarnessPort: Send + Sync {
162 async fn execute(&self, request: ToolExecutionRequest) -> KernelResult<ToolExecutionReport>;
163}
164
165#[async_trait]
166pub trait PolicyGatePort: Send + Sync {
167 async fn evaluate(
168 &self,
169 session_id: SessionId,
170 requested: Vec<Capability>,
171 ) -> KernelResult<PolicyGateDecision>;
172
173 async fn set_policy(
174 &self,
175 _session_id: SessionId,
176 _policy: crate::policy::PolicySet,
177 ) -> KernelResult<()> {
178 Ok(())
179 }
180}
181
182#[async_trait]
183pub trait ApprovalPort: Send + Sync {
184 async fn enqueue(&self, request: ApprovalRequest) -> KernelResult<ApprovalTicket>;
185 async fn list_pending(&self, session_id: SessionId) -> KernelResult<Vec<ApprovalTicket>>;
186 async fn resolve(
187 &self,
188 approval_id: ApprovalId,
189 approved: bool,
190 actor: String,
191 ) -> KernelResult<ApprovalResolution>;
192}