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