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