Skip to main content

deepstrike_core/runtime/
session.rs

1use serde::{Deserialize, Serialize};
2
3
4use crate::types::message::{Message, ToolCall, ToolResult};
5
6/// Provider-native replay payload persisted in `llm_completed` for wake/preload recovery.
7///
8/// The core is provider-neutral: it persists and round-trips the replay envelope
9/// verbatim without interpreting protocol-specific shapes. `native_blocks` and
10/// `reasoning_content` are modeled explicitly because the recovery path reads
11/// them; every other envelope field (`schema_version`, `provider`, `protocol`,
12/// `model`, `reasoning_details`, `native_message`, `tool_calls`, …) is preserved
13/// through `extra` so SDK-owned protocol metadata is never dropped.
14#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
15pub struct ProviderReplay {
16    #[serde(default, skip_serializing_if = "Option::is_none")]
17    pub native_blocks: Option<Vec<serde_json::Value>>,
18    #[serde(default, skip_serializing_if = "Option::is_none")]
19    pub reasoning_content: Option<String>,
20    #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
21    pub extra: serde_json::Map<String, serde_json::Value>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
25#[serde(tag = "kind", rename_all = "snake_case")]
26pub enum RollbackReason {
27    FatalToolError { tool_name: String, error: String },
28    GovernanceDenied { tool_name: String, reason: String },
29    ProviderFailure { error: String },
30    Timeout,
31    UserInterrupt,
32    MalformedReplay { reason: String },
33}
34
35/// Append-only session event kinds for the unified Agent OS Runtime.
36///
37/// Combines execution loop events with OS-level lifecycle control,
38/// capability manifest auditing, and governance gates.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(tag = "kind", rename_all = "snake_case")]
41pub enum SessionEvent {
42    // ─── 1. Execution & Inference Loop ───
43    RunStarted {
44        run_id: String,
45        goal: String,
46        #[serde(default)]
47        criteria: Vec<String>,
48        agent_id: Option<String>,
49        system_prompt: Option<String>,
50    },
51    LlmCompleted {
52        turn: u32,
53        message: Message,
54        #[serde(default, skip_serializing_if = "Option::is_none")]
55        provider_replay: Option<ProviderReplay>,
56    },
57    ToolRequested {
58        turn: u32,
59        calls: Vec<ToolCall>,
60    },
61    ToolCompleted {
62        turn: u32,
63        results: Vec<ToolResult>,
64    },
65    Compressed {
66        turn: u32,
67        archived_seq_range: (u64, u64),
68        #[serde(default, skip_serializing_if = "Option::is_none")]
69        action: Option<String>,
70        #[serde(default, skip_serializing_if = "Option::is_none")]
71        summary: Option<String>,
72        #[serde(default, skip_serializing_if = "Option::is_none")]
73        summary_tokens: Option<u32>,
74        #[serde(default, skip_serializing_if = "Option::is_none")]
75        archive_ref: Option<String>,
76        #[serde(default, skip_serializing_if = "Vec::is_empty")]
77        preserved_refs: Vec<String>,
78    },
79    /// Working memory paged out for long-term storage (kernel `page_out`).
80    PageOut {
81        turn: u32,
82        #[serde(default, skip_serializing_if = "Option::is_none")]
83        action: Option<String>,
84        #[serde(default, skip_serializing_if = "Option::is_none")]
85        summary: Option<String>,
86        #[serde(default, skip_serializing_if = "Option::is_none")]
87        tier_hint: Option<String>,
88        #[serde(default)]
89        message_count: u32,
90    },
91    /// Long-term entries injected into knowledge partition (SDK `page_in`).
92    PageIn {
93        turn: u32,
94        entry_count: u32,
95    },
96    /// Large tool result spooled to disk by the SDK (kernel Layer 1).
97    LargeResultSpooled {
98        turn: u32,
99        call_id: String,
100        tool: String,
101        original_size: u32,
102        preview_size: u32,
103        #[serde(default, skip_serializing_if = "Option::is_none")]
104        spool_ref: Option<String>,
105    },
106    RunTerminal {
107        reason: String,
108        turns_used: u32,
109        total_tokens: u64,
110    },
111
112    // ─── 2. Kernel Governance & Security Gates ───
113    /// Tool arguments automatically repaired under white-listed heuristics.
114    ToolArgumentRepaired {
115        turn: u32,
116        tool: String,
117        original_arguments: String,
118        repaired_arguments: String,
119    },
120    /// Escalated permission gate requested for a tool, suspending current execution.
121    PermissionRequested {
122        turn: u32,
123        tool: String,
124        arguments: String,
125        reason: Option<String>,
126    },
127    /// Permission decision resolved by the user or an automated policy engine.
128    PermissionResolved {
129        turn: u32,
130        approved: bool,
131        responder: String, // "user" | "policy_gate"
132    },
133    /// Tool blocked monotonically by security governance policy or denial of consent.
134    ToolDenied {
135        turn: u32,
136        call_id: String,
137        tool_name: String,
138        reason: String,
139    },
140
141    // ─── 3. Dynamic Capability & Context Restructuring ───
142    /// Model-visible capabilities dynamically updated (e.g., loading skills or mounting MCPs).
143    CapabilityChanged {
144        turn: u32,
145        #[serde(default, skip_serializing_if = "Vec::is_empty")]
146        added: Vec<String>,
147        #[serde(default, skip_serializing_if = "Vec::is_empty")]
148        removed: Vec<String>,
149        #[serde(default, skip_serializing_if = "Option::is_none")]
150        change_kind: Option<String>,
151        #[serde(default, skip_serializing_if = "Option::is_none")]
152        capability_id: Option<String>,
153        #[serde(default, skip_serializing_if = "Option::is_none")]
154        version: Option<String>,
155        #[serde(default, skip_serializing_if = "Option::is_none")]
156        mounted_by: Option<String>,
157        #[serde(default, skip_serializing_if = "Option::is_none")]
158        mount_reason: Option<String>,
159    },
160    /// Context reset and sprint rotated after a context boundary handoff.
161    ContextRenewed {
162        turn: u32,
163        sprint: u32,
164        handoff_ref: String,
165    },
166
167    /// Execution paused (waiting for human-in-the-loop interaction or long-running tasks).
168    Suspended {
169        turn: u32,
170        reason: String,
171        #[serde(default, skip_serializing_if = "Vec::is_empty")]
172        pending_calls: Vec<String>,
173    },
174    /// Execution resumed.
175    Resumed {
176        turn: u32,
177        #[serde(default, skip_serializing_if = "Vec::is_empty")]
178        approved: Vec<String>,
179        #[serde(default, skip_serializing_if = "Vec::is_empty")]
180        denied: Vec<String>,
181    },
182    /// Kernel governance gate: tool requires approval before execution.
183    ToolGated {
184        turn: u32,
185        call_id: String,
186        tool: String,
187        reason: String,
188    },
189    /// In-kernel signal disposition (attention policy).
190    SignalDisposed {
191        turn: u32,
192        signal_id: String,
193        disposition: String,
194        queue_depth: u32,
195    },
196    /// Scheduler budget axis exhausted.
197    BudgetExceeded {
198        turn: u32,
199        budget: String,
200    },
201    /// Checkpoint taken at the start of a turn transaction (before LLM call).
202    CheckpointTaken {
203        turn: u32,
204        history_len: u32,
205    },
206    /// Transaction rollback indicating state was restored to a checkpoint.
207    Rollbacked {
208        turn: u32,
209        checkpoint_history_len: u32,
210        #[serde(default, skip_serializing_if = "Option::is_none")]
211        reason: Option<RollbackReason>,
212    },
213
214    // ─── 4. Process Table ───
215    /// Kernel process table changed for a spawned sub-agent.
216    AgentProcessChanged {
217        turn: u32,
218        agent_id: String,
219        parent_session_id: String,
220        role: String,
221        isolation: String,
222        context_inheritance: String,
223        state: String,
224        #[serde(default, skip_serializing_if = "Vec::is_empty")]
225        permitted_capability_ids: Vec<String>,
226        #[serde(default, skip_serializing_if = "Option::is_none")]
227        result_termination: Option<String>,
228    },
229
230    // ─── 5. Milestone Contracts ───
231    /// Milestone phase criteria passed — capabilities unlocked, phase advanced.
232    MilestoneAdvanced {
233        turn: u32,
234        phase_id: String,
235        #[serde(default)]
236        capabilities_unlocked: Vec<String>,
237    },
238    /// Milestone phase criteria not met — run continues without advancing the phase.
239    MilestoneBlocked {
240        turn: u32,
241        phase_id: String,
242        reason: String,
243    },
244
245    // ─── 6. Long-Term Memory (Phase 7) ───
246    /// Memory entry written successfully (SDK → kernel acknowledgment).
247    MemoryWritten {
248        turn: u32,
249        memory_id: String,
250        memory_kind: String,
251        size_bytes: u32,
252    },
253    /// Memory query request (kernel → SDK; SDK should respond asynchronously).
254    MemoryQueried {
255        turn: u32,
256        query_context: String,
257        requested_k: usize,
258        requires_async_response: bool,
259    },
260    /// Memory validation failed (kernel rejected a write request).
261    MemoryValidationFailed {
262        turn: u32,
263        memory_id: String,
264        error: String,
265    },
266    /// Memory retrieval result (SDK → kernel via Resume or other async mechanism).
267    MemoryRetrievalResult {
268        retrieval: crate::mm::memory::MemoryRetrieval,
269    },
270}
271
272impl SessionEvent {
273    /// Event `kind` string (snake_case tag).
274    pub fn kind_str(&self) -> &'static str {
275        match self {
276            Self::RunStarted { .. } => "run_started",
277            Self::LlmCompleted { .. } => "llm_completed",
278            Self::ToolRequested { .. } => "tool_requested",
279            Self::ToolCompleted { .. } => "tool_completed",
280            Self::Compressed { .. } => "compressed",
281            Self::PageOut { .. } => "page_out",
282            Self::PageIn { .. } => "page_in",
283            Self::LargeResultSpooled { .. } => "large_result_spooled",
284            Self::RunTerminal { .. } => "run_terminal",
285            Self::ToolArgumentRepaired { .. } => "tool_argument_repaired",
286            Self::PermissionRequested { .. } => "permission_requested",
287            Self::PermissionResolved { .. } => "permission_resolved",
288            Self::ToolDenied { .. } => "tool_denied",
289            Self::CapabilityChanged { .. } => "capability_changed",
290            Self::ContextRenewed { .. } => "context_renewed",
291            Self::Suspended { .. } => "suspended",
292            Self::Resumed { .. } => "resumed",
293            Self::ToolGated { .. } => "tool_gated",
294            Self::SignalDisposed { .. } => "signal_disposed",
295            Self::BudgetExceeded { .. } => "budget_exceeded",
296            Self::CheckpointTaken { .. } => "checkpoint_taken",
297            Self::Rollbacked { .. } => "rollbacked",
298            Self::AgentProcessChanged { .. } => "agent_process_changed",
299            Self::MilestoneAdvanced { .. } => "milestone_advanced",
300            Self::MilestoneBlocked { .. } => "milestone_blocked",
301            Self::MemoryWritten { .. } => "memory_written",
302            Self::MemoryQueried { .. } => "memory_queried",
303            Self::MemoryValidationFailed { .. } => "memory_validation_failed",
304            Self::MemoryRetrievalResult { .. } => "memory_retrieval_result",
305        }
306    }
307
308    /// Whether this event is a kernel OS decision (replay ignores for message reconstruction).
309    pub fn is_kernel_os_event(&self) -> bool {
310        matches!(
311            self,
312            Self::Compressed { .. }
313                | Self::PageOut { .. }
314                | Self::PageIn { .. }
315                | Self::LargeResultSpooled { .. }
316                | Self::CapabilityChanged { .. }
317                | Self::ContextRenewed { .. }
318                | Self::Suspended { .. }
319                | Self::Resumed { .. }
320                | Self::ToolGated { .. }
321                | Self::SignalDisposed { .. }
322                | Self::BudgetExceeded { .. }
323                | Self::CheckpointTaken { .. }
324                | Self::Rollbacked { .. }
325                | Self::AgentProcessChanged { .. }
326                | Self::MilestoneAdvanced { .. }
327                | Self::MilestoneBlocked { .. }
328                | Self::MemoryWritten { .. }
329                | Self::MemoryQueried { .. }
330                | Self::MemoryValidationFailed { .. }
331        )
332    }
333}