@deepstrike/wasm 0.2.2 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,7 +75,7 @@ src/
75
75
 
76
76
  The kernel (`@deepstrike/wasm-kernel`, Rust/wasm-bindgen) owns:
77
77
  - `KernelRuntime.step()` — drives `call_provider → execute_tool → evaluate_milestone → done`
78
- - `ContextEngine` — 5-partition context with pressure-based compression
78
+ - `ContextEngine` — 4-slot context with tiered history compression
79
79
  - `Governance` — tool veto authority
80
80
  - `SignalRouter` — external interrupt queue
81
81
 
@@ -146,6 +146,27 @@ plane.register(fetchUrl)
146
146
 
147
147
  ---
148
148
 
149
+ ## Context model (four slots)
150
+
151
+ Same as Node/Python — only **history** is compressed. WASM exposes `systemStable`, `systemKnowledge`, and `turns` on `call_provider.context`.
152
+
153
+ ```typescript
154
+ const runner = new RuntimeRunner({
155
+ provider,
156
+ sessionLog: new InMemorySessionLog(),
157
+ executionPlane: plane,
158
+ maxTokens: 32_000,
159
+ initialMemory: ["User prefers concise answers."], // → Slot 2
160
+ systemPrompt: "You are a helpful assistant.", // → Slot 1
161
+ })
162
+ ```
163
+
164
+ IndexedDB / KV can back `DreamStore` for cross-session memory. Meta-tool retrieval still lands in **history**.
165
+
166
+ See [docs/concepts/context-slots-compression.md](../docs/concepts/context-slots-compression.md).
167
+
168
+ ---
169
+
149
170
  ## Governance
150
171
 
151
172
  ```typescript
@@ -167,7 +188,7 @@ const runner = new RuntimeRunner({
167
188
 
168
189
  ## Memory
169
190
 
170
- `WorkingMemory` is an in-process scratch pad for within-run state:
191
+ `WorkingMemory` is an SDK-side scratch pad not the removed kernel `working` partition. Structured task state renders into Slot 3 (`turns[0]`).
171
192
 
172
193
  ```typescript
173
194
  import { WorkingMemory } from "@deepstrike/wasm"
@@ -177,10 +198,14 @@ mem.set("step", 1)
177
198
  mem.get("step") // 1
178
199
  ```
179
200
 
201
+ For cross-session recall, implement `DreamStore` and set `agentId` on `RuntimeRunner`. In-session `memory(query)` results appear in **history**; preload durable blocks with `initialMemory` → Slot 2.
202
+
180
203
  ---
181
204
 
182
205
  ## Knowledge
183
206
 
207
+ Runtime `knowledge(query)` results → **history** (tool results). Durable preload → Slot 2 via `initialMemory`.
208
+
184
209
  ```typescript
185
210
  import type { KnowledgeSource } from "@deepstrike/wasm"
186
211
 
@@ -230,6 +255,8 @@ for await (const event of loop.runStreaming({
230
255
 
231
256
  ## Signals & interrupts
232
257
 
258
+ Delivered signals fold into Slot 3 (`turns[0]`) and are cleared after each render — they do not survive renewal.
259
+
233
260
  ```typescript
234
261
  import { ScheduledPrompt } from "@deepstrike/wasm"
235
262
  import type { SignalSource, RuntimeSignal } from "@deepstrike/wasm"
@@ -23,4 +23,36 @@ export declare class Governance {
23
23
  setTime(nowMs: number): this;
24
24
  evaluate(toolName: string, argsJson: string): GovernanceVerdict;
25
25
  }
26
+ type GovernancePolicyAction = "allow" | "deny" | "ask_user";
27
+ export interface GovernancePolicy {
28
+ defaultAction?: GovernancePolicyAction;
29
+ rules?: {
30
+ pattern: string;
31
+ action: GovernancePolicyAction;
32
+ }[];
33
+ vetoes?: string[];
34
+ rateLimits?: {
35
+ tool: string;
36
+ maxCalls: number;
37
+ windowMs: number;
38
+ }[];
39
+ constraints?: GovernanceConstraint[];
40
+ }
41
+ export type GovernanceConstraint = {
42
+ kind: "required";
43
+ tool: string;
44
+ path: string;
45
+ } | {
46
+ kind: "enum";
47
+ tool: string;
48
+ path: string;
49
+ values: string[];
50
+ } | {
51
+ kind: "range";
52
+ tool: string;
53
+ path: string;
54
+ min?: number;
55
+ max?: number;
56
+ };
57
+ export declare function governancePolicyToKernelEvent(policy: GovernancePolicy): Record<string, unknown>;
26
58
  export {};
@@ -51,3 +51,21 @@ export class Governance {
51
51
  return this._inner.evaluate(toolName, argsJson);
52
52
  }
53
53
  }
54
+ export function governancePolicyToKernelEvent(policy) {
55
+ return {
56
+ kind: "load_governance_policy",
57
+ ...(policy.defaultAction ? { default_action: policy.defaultAction } : {}),
58
+ rules: (policy.rules ?? []).map(r => ({ tool_pattern: r.pattern, action: r.action })),
59
+ vetoed_tools: policy.vetoes ?? [],
60
+ rate_limits: (policy.rateLimits ?? []).map(rl => ({
61
+ tool: rl.tool,
62
+ max_calls: rl.maxCalls,
63
+ window_ms: rl.windowMs,
64
+ })),
65
+ constraints: (policy.constraints ?? []).map(c => c.kind === "enum"
66
+ ? { kind: "enum", tool: c.tool, path: c.path, values: c.values }
67
+ : c.kind === "range"
68
+ ? { kind: "range", tool: c.tool, path: c.path, ...(c.min !== undefined ? { min: c.min } : {}), ...(c.max !== undefined ? { max: c.max } : {}) }
69
+ : { kind: "required", tool: c.tool, path: c.path }),
70
+ };
71
+ }
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export type { RuntimeOptions, SessionEvent, SessionLog, RunContext, ExecutionPla
3
3
  export { FilteredExecutionPlane } from "./runtime/filtered-plane.js";
4
4
  export { SubAgentOrchestrator, defaultSubAgentOrchestrator, spawnStandalone } from "./runtime/sub-agent-orchestrator.js";
5
5
  export type { SubAgentRunContext } from "./runtime/sub-agent-orchestrator.js";
6
- export type { AgentCapabilityFilter, AgentIdentity, AgentIsolation, AgentRunSpec, AgentSpawnedObservation, ContextInheritance, KernelAgentRole, LoopResult, MilestoneCheckResult, MilestoneContract, MilestonePhase, MilestonePolicy, SubAgentResult, TerminationReason, } from "./runtime/types/agent.js";
6
+ export type { AgentCapabilityFilter, AgentIdentity, AgentIsolation, AgentRunSpec, AgentProcessChangedObservation, ContextInheritance, KernelAgentRole, LoopResult, MilestoneCheckResult, MilestoneContract, MilestonePhase, MilestonePolicy, SubAgentResult, TerminationReason, } from "./runtime/types/agent.js";
7
7
  export { Governance } from "./governance.js";
8
8
  export type { GovernanceVerdict } from "./governance.js";
9
9
  export { AnthropicProvider } from "./providers/anthropic.js";
@@ -19,4 +19,4 @@ export { ScheduledPrompt } from "./signals/index.js";
19
19
  export type { RuntimeSignal, SignalSource } from "./signals/index.js";
20
20
  export { PermissionManager, PermissionMode } from "./safety/index.js";
21
21
  export type { PermissionDecision } from "./safety/index.js";
22
- export type { Message, ToolCall, ToolResult, ToolSchema, RenderedContext, ProviderRunState, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, LLMProvider, } from "./types.js";
22
+ export type { Message, ToolCall, ToolResult, ToolSchema, RenderedContext, ProviderRunState, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, PermissionResolvedEvent, PermissionResponse, LLMProvider, } from "./types.js";
@@ -1,6 +1,11 @@
1
1
  import { assistantReplayKey, collectStreamMessage, toAnthropicMessages } from "./base.js";
2
2
  function buildAnthropicTools(tools) {
3
- return tools.map(t => ({ name: t.name, description: t.description, input_schema: JSON.parse(t.parameters) }));
3
+ return tools.map((t, i) => ({
4
+ name: t.name,
5
+ description: t.description,
6
+ input_schema: JSON.parse(t.parameters),
7
+ ...(i === tools.length - 1 ? { cache_control: { type: "ephemeral" } } : {}),
8
+ }));
4
9
  }
5
10
  export class AnthropicProvider {
6
11
  apiKey;
@@ -25,7 +30,14 @@ export class AnthropicProvider {
25
30
  return collectStreamMessage(this.stream(context, tools, extensions));
26
31
  }
27
32
  async *stream(context, tools, extensions) {
28
- const system = context.systemText || undefined;
33
+ const systemBlocks = [];
34
+ if (context.systemStable) {
35
+ systemBlocks.push({ type: "text", text: context.systemStable, cache_control: { type: "ephemeral" } });
36
+ }
37
+ if (context.systemKnowledge) {
38
+ systemBlocks.push({ type: "text", text: context.systemKnowledge, cache_control: { type: "ephemeral" } });
39
+ }
40
+ const system = systemBlocks.length ? systemBlocks : (context.systemText || undefined);
29
41
  const msgs = toAnthropicMessages(context, message => this.nativeAssistantBlocks.get(assistantReplayKey(message)));
30
42
  const body = {
31
43
  model: this.model,
@@ -44,6 +56,7 @@ export class AnthropicProvider {
44
56
  "x-api-key": this.apiKey,
45
57
  "anthropic-version": "2023-06-01",
46
58
  "content-type": "application/json",
59
+ "anthropic-beta": "prompt-caching-2024-07-31",
47
60
  },
48
61
  body: JSON.stringify(body),
49
62
  });
@@ -74,9 +87,13 @@ export class AnthropicProvider {
74
87
  if (evt.type === "message_start" || evt.type === "message_delta") {
75
88
  const usage = (evt.usage ?? evt.message?.usage);
76
89
  if (usage?.input_tokens != null) {
90
+ const inputTokens = usage.input_tokens ?? 0;
91
+ const outputTokens = usage.output_tokens ?? 0;
77
92
  yield {
78
93
  type: "usage",
79
- totalTokens: usage.input_tokens + (usage.output_tokens ?? 0),
94
+ totalTokens: inputTokens + outputTokens,
95
+ inputTokens,
96
+ outputTokens,
80
97
  };
81
98
  }
82
99
  }
@@ -57,19 +57,29 @@ export function toAnthropicMessages(context, nativeReplay) {
57
57
  }
58
58
  result.push({ role: msg.role, content: msg.content });
59
59
  }
60
+ if (context.systemVolatile && result.length > 0) {
61
+ const last = result[result.length - 1];
62
+ if (last.role === "user") {
63
+ last.content = `${String(last.content ?? "")}\n\n[SYSTEM REMINDER]\n${context.systemVolatile}`;
64
+ }
65
+ }
60
66
  return result;
61
67
  }
62
68
  /** Collect a non-streaming assistant Message from stream events. */
63
69
  export async function collectStreamMessage(stream) {
64
70
  let content = "";
65
71
  const toolCalls = [];
72
+ let outputTokens;
66
73
  for await (const evt of stream) {
67
74
  if (evt.type === "text_delta" && evt.delta)
68
75
  content += evt.delta;
69
76
  else if (evt.type === "tool_call" && evt.id && evt.name) {
70
77
  toolCalls.push({ id: evt.id, name: evt.name, arguments: JSON.stringify(evt.arguments ?? {}) });
71
78
  }
79
+ else if (evt.type === "usage") {
80
+ outputTokens = evt.outputTokens ?? evt.totalTokens;
81
+ }
72
82
  }
73
- return { role: "assistant", content, ...(toolCalls.length ? { toolCalls } : {}) };
83
+ return { role: "assistant", content, ...(outputTokens ? { tokenCount: outputTokens } : {}), ...(toolCalls.length ? { toolCalls } : {}) };
74
84
  }
75
85
  export { assistantReplayKey };
@@ -1,8 +1,8 @@
1
- import type { ToolCall, ToolSchema, StreamEvent } from "../types.js";
1
+ import type { ToolCall, ToolSchema, StreamEvent, PermissionRequestEvent, PermissionResponse } from "../types.js";
2
2
  import type { RegisteredTool } from "../tools/index.js";
3
3
  import type { DreamStore } from "../memory/index.js";
4
4
  import type { KnowledgeSource } from "../knowledge/index.js";
5
- import type { Governance } from "../governance.js";
5
+ import { LargeResultSpool } from "./large-result-spool.js";
6
6
  export interface ToolSuspendEvent {
7
7
  type: "tool_suspend";
8
8
  callId: string;
@@ -15,8 +15,9 @@ export interface RunContext {
15
15
  skillContentMap?: Map<string, string>;
16
16
  dreamStore?: DreamStore;
17
17
  knowledgeSource?: KnowledgeSource;
18
- governance?: Governance;
19
18
  onToolSuspend?: (event: ToolSuspendEvent) => Promise<unknown> | unknown;
19
+ onPermissionRequest?: (event: PermissionRequestEvent) => Promise<PermissionResponse | boolean> | PermissionResponse | boolean;
20
+ resultSpool?: LargeResultSpool;
20
21
  }
21
22
  export interface ExecutionPlane {
22
23
  register(...tools: RegisteredTool[]): this;
@@ -30,4 +31,6 @@ export declare class LocalExecutionPlane implements ExecutionPlane {
30
31
  unregister(name: string): this;
31
32
  schemas(): ToolSchema[];
32
33
  executeAll(calls: ToolCall[], ctx: RunContext): AsyncIterable<StreamEvent>;
34
+ private tryReadSpooledArgument;
33
35
  }
36
+ export declare function resolvePermissionRequest(request: PermissionRequestEvent, ctx: RunContext): Promise<PermissionResponse>;
@@ -1,3 +1,4 @@
1
+ import { LargeResultSpool } from "./large-result-spool.js";
1
2
  function stripFrontmatter(content) {
2
3
  const s = content.trimStart();
3
4
  if (!s.startsWith("---"))
@@ -21,29 +22,7 @@ export class LocalExecutionPlane {
21
22
  return Array.from(this.tools.values()).map(t => t.schema);
22
23
  }
23
24
  async *executeAll(calls, ctx) {
24
- const permitted = [];
25
- for (const c of calls) {
26
- if (ctx.governance) {
27
- ctx.governance.setTime(Date.now());
28
- const v = ctx.governance.evaluate(c.name, c.arguments);
29
- if (v.kind === "deny") {
30
- yield { type: "tool_denied", callId: c.id, toolName: c.name, reason: v.reason ?? "" };
31
- yield { type: "tool_result", callId: c.id, name: c.name, content: `permission denied: ${v.reason ?? ""}`, isError: true, isFatal: false, errorKind: "governance_denied" };
32
- continue;
33
- }
34
- if (v.kind === "rate_limited") {
35
- yield { type: "tool_denied", callId: c.id, toolName: c.name, reason: "rate limited" };
36
- yield { type: "tool_result", callId: c.id, name: c.name, content: "rate limited", isError: true, isFatal: false, errorKind: "recoverable" };
37
- continue;
38
- }
39
- if (v.kind === "ask_user") {
40
- yield { type: "permission_request", callId: c.id, toolName: c.name, arguments: c.arguments, reason: v.reason ?? "" };
41
- yield { type: "tool_result", callId: c.id, name: c.name, content: "awaiting user approval", isError: true, isFatal: false, errorKind: "recoverable" };
42
- continue;
43
- }
44
- }
45
- permitted.push(c);
46
- }
25
+ const permitted = calls;
47
26
  const skillCalls = permitted.filter(c => c.name === "skill");
48
27
  const memoryCalls = permitted.filter(c => c.name === "memory");
49
28
  const knowledgeCalls = permitted.filter(c => c.name === "knowledge");
@@ -82,6 +61,11 @@ export class LocalExecutionPlane {
82
61
  yield { type: "tool_result", callId: c.id, name: c.name, content, isError: false };
83
62
  }
84
63
  for (const call of regularCalls) {
64
+ const spooledContent = await this.tryReadSpooledArgument(call, ctx);
65
+ if (spooledContent !== null) {
66
+ yield { type: "tool_result", callId: call.id, name: call.name, content: spooledContent, isError: false };
67
+ continue;
68
+ }
85
69
  const registered = this.tools.get(call.name);
86
70
  if (!registered) {
87
71
  yield { type: "tool_result", callId: call.id, name: call.name, content: `unknown tool: ${call.name}`, isError: true, isFatal: false, errorKind: "recoverable" };
@@ -105,6 +89,25 @@ export class LocalExecutionPlane {
105
89
  }
106
90
  }
107
91
  }
92
+ async tryReadSpooledArgument(call, ctx) {
93
+ const isReadTool = ["read", "read_file", "view_file", "read_spooled_result"].includes(call.name);
94
+ if (!isReadTool)
95
+ return null;
96
+ try {
97
+ const args = JSON.parse(call.arguments || "{}");
98
+ for (const val of Object.values(args)) {
99
+ if (typeof val === "string" && (val.startsWith(".spool/") || val.includes("/.spool/"))) {
100
+ const spool = ctx.resultSpool ?? new LargeResultSpool();
101
+ const content = await spool.readSpooledResult(val);
102
+ return content;
103
+ }
104
+ }
105
+ }
106
+ catch {
107
+ // Ignore errors
108
+ }
109
+ return null;
110
+ }
108
111
  }
109
112
  function tryParseJson(s) {
110
113
  try {
@@ -114,3 +117,31 @@ function tryParseJson(s) {
114
117
  return null;
115
118
  }
116
119
  }
120
+ export async function resolvePermissionRequest(request, ctx) {
121
+ if (!ctx.onPermissionRequest) {
122
+ return {
123
+ approved: false,
124
+ responder: "policy_gate",
125
+ reason: "no permission handler configured",
126
+ };
127
+ }
128
+ try {
129
+ return normalizePermissionResponse(await ctx.onPermissionRequest(request));
130
+ }
131
+ catch (err) {
132
+ return {
133
+ approved: false,
134
+ responder: "permission_handler",
135
+ reason: `permission handler failed: ${String(err)}`,
136
+ };
137
+ }
138
+ }
139
+ function normalizePermissionResponse(value) {
140
+ if (typeof value === "boolean")
141
+ return { approved: value, responder: "host" };
142
+ return {
143
+ approved: Boolean(value.approved),
144
+ responder: value.responder ?? "host",
145
+ ...(value.reason ? { reason: value.reason } : {}),
146
+ };
147
+ }
@@ -0,0 +1,26 @@
1
+ import type { KernelObservation } from "./kernel-step.js";
2
+ import type { SessionEvent } from "./session-log.js";
3
+ /** Agent OS kernel event category (Phase 5). */
4
+ export type KernelEventCategory = "syscall" | "sched" | "mm" | "proc" | "ipc";
5
+ export declare function categoryForKind(kind: string): KernelEventCategory;
6
+ export declare function withCategory<T extends {
7
+ kind: string;
8
+ }>(event: T): T & {
9
+ category: KernelEventCategory;
10
+ primitive: KernelPrimitive;
11
+ };
12
+ type CompressionAction = Extract<SessionEvent, {
13
+ kind: "compressed";
14
+ }>["action"];
15
+ export declare function kernelObservationToSessionEvent(obs: KernelObservation, turn: number, opts?: {
16
+ nextArchiveStart?: number;
17
+ latestSeq?: number;
18
+ archiveRef?: string;
19
+ preservedRefs?: string[];
20
+ compressionAction?: (action?: string) => CompressionAction;
21
+ spoolRef?: string;
22
+ }): SessionEvent | null;
23
+ export type KernelPrimitive = "syscall" | "sched" | "mm";
24
+ export declare function primitiveForCategory(category: KernelEventCategory): KernelPrimitive;
25
+ export declare function primitiveForKind(kind: string): KernelPrimitive;
26
+ export {};
@@ -0,0 +1,212 @@
1
+ export function categoryForKind(kind) {
2
+ switch (kind) {
3
+ case "tool_gated":
4
+ case "capability_changed":
5
+ return "syscall";
6
+ case "compressed":
7
+ case "page_out":
8
+ case "page_in":
9
+ case "page_in_requested":
10
+ case "renewed":
11
+ case "context_renewed":
12
+ case "large_result_spooled":
13
+ case "memory_written":
14
+ case "memory_queried":
15
+ return "mm";
16
+ case "agent_process_changed":
17
+ return "proc";
18
+ case "signal_disposed":
19
+ return "ipc";
20
+ default:
21
+ return "sched";
22
+ }
23
+ }
24
+ export function withCategory(event) {
25
+ const category = categoryForKind(event.kind);
26
+ return {
27
+ ...event,
28
+ category,
29
+ primitive: primitiveForCategory(category),
30
+ };
31
+ }
32
+ export function kernelObservationToSessionEvent(obs, turn, opts = {}) {
33
+ const t = obs.turn ?? turn;
34
+ const compressionAction = opts.compressionAction ?? (() => undefined);
35
+ switch (obs.kind) {
36
+ case "page_out":
37
+ return withCategory({
38
+ kind: "page_out",
39
+ turn: t,
40
+ action: compressionAction(obs.action),
41
+ summary: obs.summary,
42
+ tier_hint: obs.tier_hint ?? "durable",
43
+ message_count: Array.isArray(obs.archived) ? obs.archived.length : 0,
44
+ });
45
+ case "compressed": {
46
+ const latest = opts.latestSeq ?? -1;
47
+ const start = opts.nextArchiveStart ?? 0;
48
+ if (latest < start)
49
+ return null;
50
+ return withCategory({
51
+ kind: "compressed",
52
+ turn: t,
53
+ archived_seq_range: [start, latest],
54
+ action: compressionAction(obs.action),
55
+ summary: obs.summary,
56
+ summary_tokens: obs.summary ? Math.max(1, Math.ceil(obs.summary.length / 4)) : undefined,
57
+ archive_ref: opts.archiveRef,
58
+ preserved_refs: opts.preservedRefs ?? [],
59
+ });
60
+ }
61
+ case "renewed":
62
+ return withCategory({
63
+ kind: "context_renewed",
64
+ turn: t,
65
+ sprint: obs.sprint ?? 0,
66
+ handoff_ref: "",
67
+ });
68
+ case "rollbacked":
69
+ return withCategory({
70
+ kind: "rollbacked",
71
+ turn: t,
72
+ checkpoint_history_len: obs.checkpoint_history_len ?? 0,
73
+ reason: obs.reason,
74
+ });
75
+ case "capability_changed":
76
+ return withCategory({
77
+ kind: "capability_changed",
78
+ turn: t,
79
+ added: obs.added ?? [],
80
+ removed: obs.removed ?? [],
81
+ ...(obs.change_kind != null && { change_kind: obs.change_kind }),
82
+ ...(obs.capability_id != null && { capability_id: obs.capability_id }),
83
+ ...(obs.version != null && { version: obs.version }),
84
+ ...(obs.mounted_by != null && { mounted_by: obs.mounted_by }),
85
+ ...(obs.mount_reason != null && { mount_reason: obs.mount_reason }),
86
+ });
87
+ case "milestone_advanced":
88
+ return withCategory({
89
+ kind: "milestone_advanced",
90
+ turn: t,
91
+ phase_id: obs.phase_id ?? "",
92
+ capabilities_unlocked: obs.capabilities_unlocked ?? [],
93
+ });
94
+ case "milestone_blocked":
95
+ return withCategory({
96
+ kind: "milestone_blocked",
97
+ turn: t,
98
+ phase_id: obs.phase_id ?? "",
99
+ reason: typeof obs.reason === "string" ? obs.reason : "",
100
+ });
101
+ case "milestone_evidence":
102
+ return withCategory({
103
+ kind: "milestone_evidence",
104
+ turn: t,
105
+ phase_id: obs.phase_id ?? "",
106
+ evidence: obs.evidence ?? [],
107
+ });
108
+ case "checkpoint_taken":
109
+ return withCategory({
110
+ kind: "checkpoint_taken",
111
+ turn: t,
112
+ history_len: obs.history_len ?? 0,
113
+ });
114
+ case "agent_process_changed":
115
+ return withCategory({
116
+ kind: "agent_process_changed",
117
+ turn: t,
118
+ agent_id: obs.agent_id ?? "",
119
+ parent_session_id: obs.parent_session_id ?? "",
120
+ role: obs.role ?? "",
121
+ isolation: obs.isolation ?? "",
122
+ context_inheritance: obs.context_inheritance ?? "",
123
+ state: obs.state ?? "running",
124
+ permitted_capability_ids: obs.permitted_capability_ids ?? [],
125
+ ...(obs.result_termination
126
+ ? { result_termination: obs.result_termination }
127
+ : {}),
128
+ });
129
+ case "tool_gated":
130
+ return withCategory({
131
+ kind: "tool_gated",
132
+ turn: t,
133
+ call_id: obs.call_id ?? "",
134
+ tool: obs.tool ?? "",
135
+ reason: typeof obs.reason === "string" ? obs.reason : "",
136
+ });
137
+ case "signal_disposed":
138
+ return withCategory({
139
+ kind: "signal_disposed",
140
+ turn: t,
141
+ signal_id: obs.signal_id ?? "",
142
+ disposition: obs.disposition ?? "",
143
+ queue_depth: obs.queue_depth ?? 0,
144
+ });
145
+ case "budget_exceeded":
146
+ return withCategory({
147
+ kind: "budget_exceeded",
148
+ turn: t,
149
+ budget: obs.budget ?? "",
150
+ });
151
+ case "suspended":
152
+ return withCategory({
153
+ kind: "suspended",
154
+ turn: t,
155
+ reason: typeof obs.reason === "string" ? obs.reason : "",
156
+ pending_calls: obs.pending_calls ?? [],
157
+ });
158
+ case "resumed":
159
+ return withCategory({
160
+ kind: "resumed",
161
+ turn: t,
162
+ approved: obs.approved ?? [],
163
+ denied: obs.denied ?? [],
164
+ });
165
+ case "page_in_requested":
166
+ return null;
167
+ case "large_result_spooled":
168
+ return withCategory({
169
+ kind: "large_result_spooled",
170
+ turn: t,
171
+ call_id: obs.call_id ?? "",
172
+ tool: obs.tool ?? "",
173
+ original_size: obs.original_size ?? 0,
174
+ preview_size: obs.preview_size ?? 0,
175
+ spool_ref: opts.spoolRef,
176
+ });
177
+ case "memory_written":
178
+ return withCategory({
179
+ kind: "memory_written",
180
+ turn: t,
181
+ memory_id: obs.memory_id ?? "",
182
+ memory_kind: obs.memory_kind ?? "",
183
+ size_bytes: obs.size_bytes ?? 0,
184
+ });
185
+ case "memory_queried":
186
+ return withCategory({
187
+ kind: "memory_queried",
188
+ turn: t,
189
+ query_context: obs.query_context ?? "",
190
+ requested_k: obs.requested_k ?? 0,
191
+ requires_async_response: obs.requires_async_response ?? false,
192
+ });
193
+ default:
194
+ return null;
195
+ }
196
+ }
197
+ export function primitiveForCategory(category) {
198
+ switch (category) {
199
+ case "syscall":
200
+ return "syscall";
201
+ case "mm":
202
+ return "mm";
203
+ case "proc":
204
+ case "ipc":
205
+ case "sched":
206
+ default:
207
+ return "sched";
208
+ }
209
+ }
210
+ export function primitiveForKind(kind) {
211
+ return primitiveForCategory(categoryForKind(kind));
212
+ }
@@ -65,7 +65,7 @@ export interface KernelObservation {
65
65
  phase_id?: string;
66
66
  capabilities_unlocked?: string[];
67
67
  evidence?: string[];
68
- reason?: RollbackReason;
68
+ reason?: RollbackReason | string;
69
69
  agent_id?: string;
70
70
  parent_session_id?: string;
71
71
  role?: string;
@@ -73,6 +73,24 @@ export interface KernelObservation {
73
73
  context_inheritance?: string;
74
74
  permitted_capability_ids?: string[];
75
75
  history_len?: number;
76
+ tier_hint?: string;
77
+ call_id?: string;
78
+ tool?: string;
79
+ signal_id?: string;
80
+ disposition?: string;
81
+ queue_depth?: number;
82
+ budget?: string;
83
+ pending_calls?: string[];
84
+ approved?: string[];
85
+ denied?: string[];
86
+ original_size?: number;
87
+ preview_size?: number;
88
+ memory_id?: string;
89
+ memory_kind?: string;
90
+ size_bytes?: number;
91
+ query_context?: string;
92
+ requested_k?: number;
93
+ requires_async_response?: boolean;
76
94
  }
77
95
  export declare function toolSchemaToKernel(schema: ToolSchema): Record<string, unknown>;
78
96
  export declare function skillMetadataToKernel(skill: SkillMetadata): Record<string, unknown>;
@@ -84,5 +102,7 @@ export declare function capabilitySkill(skill: SkillMetadata): Record<string, un
84
102
  export declare function capabilityMarker(kind: string, id: string, description: string): Record<string, unknown>;
85
103
  export declare function kernelApply(runtime: KernelRuntimeHandle, pending: KernelObservation[], event: Record<string, unknown>): KernelObservation[];
86
104
  export declare function kernelAction(runtime: KernelRuntimeHandle, pending: KernelObservation[], event: Record<string, unknown>): KernelRunnerAction;
105
+ /** Like kernelAction but tolerates zero-action steps (e.g. queued signals). */
106
+ export declare function kernelMaybeAction(runtime: KernelRuntimeHandle, pending: KernelObservation[], event: Record<string, unknown>): KernelRunnerAction | null;
87
107
  export declare function forceCompact(runtime: KernelRuntimeHandle, pending: KernelObservation[]): boolean;
88
108
  export {};
@@ -114,6 +114,8 @@ function kernelMessageToSdk(raw) {
114
114
  function renderedContextToSdk(raw) {
115
115
  return {
116
116
  systemText: String(raw.system_text ?? raw.systemText ?? ""),
117
+ systemStable: String(raw.system_stable ?? raw.systemStable ?? ""),
118
+ systemKnowledge: String(raw.system_knowledge ?? raw.systemKnowledge ?? ""),
117
119
  turns: (raw.turns ?? []).map(kernelMessageToSdk),
118
120
  };
119
121
  }
@@ -176,6 +178,13 @@ export function kernelAction(runtime, pending, event) {
176
178
  throw new Error("kernel transition must return one action");
177
179
  return mapKernelAction(raw);
178
180
  }
181
+ /** Like kernelAction but tolerates zero-action steps (e.g. queued signals). */
182
+ export function kernelMaybeAction(runtime, pending, event) {
183
+ const step = parseStep(runtime.step(stepInput(event)));
184
+ pending.push(...step.observations);
185
+ const raw = step.actions[0];
186
+ return raw ? mapKernelAction(raw) : null;
187
+ }
179
188
  export function forceCompact(runtime, pending) {
180
189
  return kernelApply(runtime, pending, { kind: "force_compact" }).some(o => o.kind === "compressed");
181
190
  }