Skip to main content

deepstrike_core/runtime/kernel/wire/
syscall.rs

1//! P1 syscall requests and their causation (spec §7.6).
2//!
3//! A syscall is **not** a sixth wire input class. It reaches the kernel one of exactly two ways,
4//! and in both the caller is derived from kernel-owned state rather than declared by the host:
5//!
6//! * the current operation's provider tool call — recognised while resolving the provider effect;
7//! * a child's request, attached to its `ChildCompleted` fact — the parent kernel derives the
8//!   caller from the attempt.
9//!
10//! There is no `AgentRequest { actor_id }`, and no SDK-side path that fills an actor in.
11
12use serde::{Deserialize, Serialize};
13
14use super::command::TaskUpdate;
15use super::root::{WorkflowNode, WorkflowSpec};
16use super::scalar::{AttemptId, CallId, EffectId, HandleId, TaskId};
17
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19#[serde(tag = "kind", rename_all = "snake_case")]
20pub enum SyscallRequest {
21    SubmitWorkflow(SubmitWorkflowRequest),
22    AppendWorkflowNodes(AppendWorkflowNodesRequest),
23    ActivateSkill(ActivateSkillRequest),
24    /// The model's task update. Distinct from `HostCommand::UpdateTask` precisely because the
25    /// two have different authority.
26    UpdateTask(UpdateTaskRequest),
27    RequestMemoryWrite(RequestMemoryWriteRequest),
28    RequestMemoryQuery(RequestMemoryQueryRequest),
29    SendMessage(SendMessageRequest),
30    PublishChannel(PublishChannelRequest),
31    ReceiveMailbox(ReceiveMailboxRequest),
32    ReceiveChannel(ReceiveChannelRequest),
33    ReadObject(ReadObjectRequest),
34    /// P3 handle page-in. Opposite in meaning to the host's `SeedKnowledge` command (DEC-9).
35    PageIn(PageInRequest),
36}
37
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39#[serde(deny_unknown_fields)]
40pub struct SubmitWorkflowRequest {
41    pub spec: WorkflowSpec,
42}
43
44#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
45#[serde(deny_unknown_fields)]
46pub struct AppendWorkflowNodesRequest {
47    pub nodes: Vec<WorkflowNode>,
48}
49
50#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
51#[serde(deny_unknown_fields)]
52pub struct ActivateSkillRequest {
53    pub name: String,
54    /// Auto-deactivate after this many turns. `None` ⇒ until explicitly deactivated.
55    #[serde(default, skip_serializing_if = "Option::is_none")]
56    pub lease_turns: Option<u32>,
57}
58
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
60#[serde(deny_unknown_fields)]
61pub struct UpdateTaskRequest {
62    pub update: TaskUpdate,
63}
64
65#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
66#[serde(deny_unknown_fields)]
67pub struct RequestMemoryWriteRequest {
68    pub proposal: MemoryWriteProposal,
69}
70
71/// A **proposal**, not a record. It carries no tenant/namespace, no record id, no author or trust
72/// level, no timestamp and no session provenance: the kernel derives all of those from the
73/// operation's memory binding, the envelope's accepted time and the syscall causation.
74#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75#[serde(deny_unknown_fields)]
76pub struct MemoryWriteProposal {
77    pub name: String,
78    pub kind: MemoryKind,
79    pub content: String,
80    #[serde(default, skip_serializing_if = "String::is_empty")]
81    pub description: String,
82    #[serde(default, skip_serializing_if = "Vec::is_empty")]
83    pub evidence_refs: Vec<String>,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
87#[serde(rename_all = "snake_case")]
88pub enum MemoryKind {
89    User,
90    Feedback,
91    Project,
92    Reference,
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96#[serde(deny_unknown_fields)]
97pub struct RequestMemoryQueryRequest {
98    pub query: MemoryQueryProposal,
99}
100
101#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
102#[serde(deny_unknown_fields)]
103pub struct MemoryQueryProposal {
104    #[serde(default, skip_serializing_if = "String::is_empty")]
105    pub text: String,
106    #[serde(default, skip_serializing_if = "Vec::is_empty")]
107    pub kinds: Vec<MemoryKind>,
108    #[serde(default, skip_serializing_if = "Option::is_none")]
109    pub limit: Option<u32>,
110}
111
112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
113#[serde(deny_unknown_fields)]
114pub struct PageInRequest {
115    /// Must already be reachable in the current P3 handle table.
116    pub handle_id: HandleId,
117}
118
119#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
120#[serde(deny_unknown_fields)]
121pub struct SendMessageRequest {
122    pub message_id: String,
123    pub to: TaskId,
124    pub message_kind: String,
125    pub payload_handle: HandleId,
126    #[serde(default, skip_serializing_if = "Option::is_none")]
127    pub ttl_turns: Option<u32>,
128}
129
130#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
131#[serde(deny_unknown_fields)]
132pub struct PublishChannelRequest {
133    pub channel_id: String,
134    pub message_id: String,
135    pub subscribers: Vec<TaskId>,
136    pub message_kind: String,
137    pub payload_handle: HandleId,
138    #[serde(default, skip_serializing_if = "Option::is_none")]
139    pub ttl_turns: Option<u32>,
140}
141
142#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
143#[serde(deny_unknown_fields)]
144pub struct ReceiveMailboxRequest {
145    #[serde(default = "default_receive_limit")]
146    pub limit: u32,
147}
148
149#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
150#[serde(deny_unknown_fields)]
151pub struct ReceiveChannelRequest {
152    pub channel_id: String,
153}
154
155fn default_receive_limit() -> u32 {
156    16
157}
158
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
160#[serde(deny_unknown_fields)]
161pub struct ReadObjectRequest {
162    pub object_id: u32,
163}
164
165/// How the kernel derived the caller of a syscall. Never host-supplied.
166#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
167#[serde(tag = "kind", rename_all = "snake_case")]
168pub enum SyscallCausation {
169    /// A tool call inside the provider result the kernel is currently resolving.
170    ProviderTool(ProviderToolCausation),
171    /// A request a child attached to its completion fact.
172    ChildAttempt(ChildAttemptCausation),
173}
174
175#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
176#[serde(deny_unknown_fields)]
177pub struct ProviderToolCausation {
178    pub provider_effect_id: EffectId,
179    pub call_id: CallId,
180    pub task_id: TaskId,
181}
182
183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
184#[serde(deny_unknown_fields)]
185pub struct ChildAttemptCausation {
186    pub task_id: TaskId,
187    pub attempt_id: AttemptId,
188    /// Position in the child's `parent_requests` list. Derived from that stable order — a host
189    /// never picks it.
190    pub request_seq: u32,
191}