@deepstrike/wasm 0.2.2 → 0.2.6

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
@@ -39,6 +39,11 @@ const runner = new RuntimeRunner({
39
39
  executionPlane: plane,
40
40
  maxTokens: 32_000,
41
41
  maxTurns: 10,
42
+ resourceQuota: {
43
+ maxConcurrentSubagents: 4,
44
+ maxSpawnDepth: 2,
45
+ memoryWritesPerWindow: { maxWrites: 20, windowMs: 60_000 },
46
+ },
42
47
  })
43
48
 
44
49
  const answer = await collectText(runner.run({ sessionId: "demo", goal: "What is 2 + 3?" }))
@@ -75,7 +80,7 @@ src/
75
80
 
76
81
  The kernel (`@deepstrike/wasm-kernel`, Rust/wasm-bindgen) owns:
77
82
  - `KernelRuntime.step()` — drives `call_provider → execute_tool → evaluate_milestone → done`
78
- - `ContextEngine` — 5-partition context with pressure-based compression
83
+ - `ContextEngine` — 4-slot context with tiered history compression
79
84
  - `Governance` — tool veto authority
80
85
  - `SignalRouter` — external interrupt queue
81
86
 
@@ -90,6 +95,8 @@ The kernel (`@deepstrike/wasm-kernel`, Rust/wasm-bindgen) owns:
90
95
 
91
96
  The WASM SDK ships **no `readFile` built-in**. Tools must be pure JS / serializable data. Skills use `skillContentMap` on `RuntimeOptions` (no filesystem).
92
97
 
98
+ `resourceQuota` is supported in the runner and maps to the kernel `set_resource_quota` event during run setup. The write-rate quota is enforced for kernel memory-write syscalls; WASM does not yet expose runner-level `writeMemory` / `queryMemory` helpers.
99
+
93
100
  ---
94
101
 
95
102
  ## Providers
@@ -146,6 +153,27 @@ plane.register(fetchUrl)
146
153
 
147
154
  ---
148
155
 
156
+ ## Context model (four slots)
157
+
158
+ Same as Node/Python — only **history** is compressed. WASM exposes `systemStable`, `systemKnowledge`, and `turns` on `call_provider.context`.
159
+
160
+ ```typescript
161
+ const runner = new RuntimeRunner({
162
+ provider,
163
+ sessionLog: new InMemorySessionLog(),
164
+ executionPlane: plane,
165
+ maxTokens: 32_000,
166
+ initialMemory: ["User prefers concise answers."], // → Slot 2
167
+ systemPrompt: "You are a helpful assistant.", // → Slot 1
168
+ })
169
+ ```
170
+
171
+ IndexedDB / KV can back `DreamStore` for cross-session memory. Meta-tool retrieval still lands in **history**.
172
+
173
+ See [docs/concepts/context-slots-compression.md](../docs/concepts/context-slots-compression.md).
174
+
175
+ ---
176
+
149
177
  ## Governance
150
178
 
151
179
  ```typescript
@@ -167,7 +195,7 @@ const runner = new RuntimeRunner({
167
195
 
168
196
  ## Memory
169
197
 
170
- `WorkingMemory` is an in-process scratch pad for within-run state:
198
+ `WorkingMemory` is an SDK-side scratch pad not the removed kernel `working` partition. Structured task state renders into Slot 3 (`turns[0]`).
171
199
 
172
200
  ```typescript
173
201
  import { WorkingMemory } from "@deepstrike/wasm"
@@ -177,10 +205,14 @@ mem.set("step", 1)
177
205
  mem.get("step") // 1
178
206
  ```
179
207
 
208
+ 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.
209
+
180
210
  ---
181
211
 
182
212
  ## Knowledge
183
213
 
214
+ Runtime `knowledge(query)` results → **history** (tool results). Durable preload → Slot 2 via `initialMemory`.
215
+
184
216
  ```typescript
185
217
  import type { KnowledgeSource } from "@deepstrike/wasm"
186
218
 
@@ -230,6 +262,8 @@ for await (const event of loop.runStreaming({
230
262
 
231
263
  ## Signals & interrupts
232
264
 
265
+ Delivered signals fold into Slot 3 (`turns[0]`) and are cleared after each render — they do not survive renewal.
266
+
233
267
  ```typescript
234
268
  import { ScheduledPrompt } from "@deepstrike/wasm"
235
269
  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
@@ -1,9 +1,9 @@
1
- export { RuntimeRunner, collectText, InMemorySessionLog, LocalExecutionPlane, } from "./runtime/index.js";
2
- export type { RuntimeOptions, SessionEvent, SessionLog, RunContext, ExecutionPlane, } from "./runtime/index.js";
1
+ export { RuntimeRunner, collectText, InMemorySessionLog, LocalExecutionPlane, DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, DEFAULT_SANDBOX_POLICY, assertNativeProfile, osProfile, validateDeclarativePolicy, } from "./runtime/index.js";
2
+ export type { NativeOsProfile, OsProfileId, MemoryPolicy, MemoryWriteRateLimit, ResourceQuota, RuntimeOptions, SchedulerBudget, SessionEvent, SessionLog, RunContext, ExecutionPlane, } from "./runtime/index.js";
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";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { RuntimeRunner, collectText, InMemorySessionLog, LocalExecutionPlane, } from "./runtime/index.js";
1
+ export { RuntimeRunner, collectText, InMemorySessionLog, LocalExecutionPlane, DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, DEFAULT_SANDBOX_POLICY, assertNativeProfile, osProfile, validateDeclarativePolicy, } from "./runtime/index.js";
2
2
  export { FilteredExecutionPlane } from "./runtime/filtered-plane.js";
3
3
  export { SubAgentOrchestrator, defaultSubAgentOrchestrator, spawnStandalone } from "./runtime/sub-agent-orchestrator.js";
4
4
  export { Governance } from "./governance.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
+ }
@@ -2,6 +2,8 @@ export type { SessionEvent, SessionLog } from "./session-log.js";
2
2
  export { InMemorySessionLog } from "./session-log.js";
3
3
  export type { RunContext, ExecutionPlane } from "./execution-plane.js";
4
4
  export { LocalExecutionPlane } from "./execution-plane.js";
5
- export type { RuntimeOptions } from "./runner.js";
5
+ export type { MemoryPolicy, MemoryWriteRateLimit, ResourceQuota, RuntimeOptions, SchedulerBudget } from "./runner.js";
6
6
  export { RuntimeRunner, collectText } from "./runner.js";
7
7
  export { getKernel } from "./kernel.js";
8
+ export { DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, DEFAULT_SANDBOX_POLICY, assertNativeProfile, osProfile, validateDeclarativePolicy, } from "./os-profile.js";
9
+ export type { NativeOsProfile, OsProfileId } from "./os-profile.js";
@@ -2,3 +2,4 @@ export { InMemorySessionLog } from "./session-log.js";
2
2
  export { LocalExecutionPlane } from "./execution-plane.js";
3
3
  export { RuntimeRunner, collectText } from "./runner.js";
4
4
  export { getKernel } from "./kernel.js";
5
+ export { DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, DEFAULT_SANDBOX_POLICY, assertNativeProfile, osProfile, validateDeclarativePolicy, } from "./os-profile.js";
@@ -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,220 @@
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
+ case "memory_validation_failed":
16
+ return "mm";
17
+ case "agent_process_changed":
18
+ return "proc";
19
+ case "signal_disposed":
20
+ return "ipc";
21
+ default:
22
+ return "sched";
23
+ }
24
+ }
25
+ export function withCategory(event) {
26
+ const category = categoryForKind(event.kind);
27
+ return {
28
+ ...event,
29
+ category,
30
+ primitive: primitiveForCategory(category),
31
+ };
32
+ }
33
+ export function kernelObservationToSessionEvent(obs, turn, opts = {}) {
34
+ const t = obs.turn ?? turn;
35
+ const compressionAction = opts.compressionAction ?? (() => undefined);
36
+ switch (obs.kind) {
37
+ case "page_out":
38
+ return withCategory({
39
+ kind: "page_out",
40
+ turn: t,
41
+ action: compressionAction(obs.action),
42
+ summary: obs.summary,
43
+ tier_hint: obs.tier_hint ?? "durable",
44
+ message_count: Array.isArray(obs.archived) ? obs.archived.length : 0,
45
+ });
46
+ case "compressed": {
47
+ const latest = opts.latestSeq ?? -1;
48
+ const start = opts.nextArchiveStart ?? 0;
49
+ if (latest < start)
50
+ return null;
51
+ return withCategory({
52
+ kind: "compressed",
53
+ turn: t,
54
+ archived_seq_range: [start, latest],
55
+ action: compressionAction(obs.action),
56
+ summary: obs.summary,
57
+ summary_tokens: obs.summary ? Math.max(1, Math.ceil(obs.summary.length / 4)) : undefined,
58
+ archive_ref: opts.archiveRef,
59
+ preserved_refs: opts.preservedRefs ?? [],
60
+ });
61
+ }
62
+ case "renewed":
63
+ return withCategory({
64
+ kind: "context_renewed",
65
+ turn: t,
66
+ sprint: obs.sprint ?? 0,
67
+ handoff_ref: "",
68
+ });
69
+ case "rollbacked":
70
+ return withCategory({
71
+ kind: "rollbacked",
72
+ turn: t,
73
+ checkpoint_history_len: obs.checkpoint_history_len ?? 0,
74
+ reason: obs.reason,
75
+ });
76
+ case "capability_changed":
77
+ return withCategory({
78
+ kind: "capability_changed",
79
+ turn: t,
80
+ added: obs.added ?? [],
81
+ removed: obs.removed ?? [],
82
+ ...(obs.change_kind != null && { change_kind: obs.change_kind }),
83
+ ...(obs.capability_id != null && { capability_id: obs.capability_id }),
84
+ ...(obs.version != null && { version: obs.version }),
85
+ ...(obs.mounted_by != null && { mounted_by: obs.mounted_by }),
86
+ ...(obs.mount_reason != null && { mount_reason: obs.mount_reason }),
87
+ });
88
+ case "milestone_advanced":
89
+ return withCategory({
90
+ kind: "milestone_advanced",
91
+ turn: t,
92
+ phase_id: obs.phase_id ?? "",
93
+ capabilities_unlocked: obs.capabilities_unlocked ?? [],
94
+ });
95
+ case "milestone_blocked":
96
+ return withCategory({
97
+ kind: "milestone_blocked",
98
+ turn: t,
99
+ phase_id: obs.phase_id ?? "",
100
+ reason: typeof obs.reason === "string" ? obs.reason : "",
101
+ });
102
+ case "milestone_evidence":
103
+ return withCategory({
104
+ kind: "milestone_evidence",
105
+ turn: t,
106
+ phase_id: obs.phase_id ?? "",
107
+ evidence: obs.evidence ?? [],
108
+ });
109
+ case "checkpoint_taken":
110
+ return withCategory({
111
+ kind: "checkpoint_taken",
112
+ turn: t,
113
+ history_len: obs.history_len ?? 0,
114
+ });
115
+ case "agent_process_changed":
116
+ return withCategory({
117
+ kind: "agent_process_changed",
118
+ turn: t,
119
+ agent_id: obs.agent_id ?? "",
120
+ parent_session_id: obs.parent_session_id ?? "",
121
+ role: obs.role ?? "",
122
+ isolation: obs.isolation ?? "",
123
+ context_inheritance: obs.context_inheritance ?? "",
124
+ state: obs.state ?? "running",
125
+ permitted_capability_ids: obs.permitted_capability_ids ?? [],
126
+ ...(obs.result_termination
127
+ ? { result_termination: obs.result_termination }
128
+ : {}),
129
+ });
130
+ case "tool_gated":
131
+ return withCategory({
132
+ kind: "tool_gated",
133
+ turn: t,
134
+ call_id: obs.call_id ?? "",
135
+ tool: obs.tool ?? "",
136
+ reason: typeof obs.reason === "string" ? obs.reason : "",
137
+ });
138
+ case "signal_disposed":
139
+ return withCategory({
140
+ kind: "signal_disposed",
141
+ turn: t,
142
+ signal_id: obs.signal_id ?? "",
143
+ disposition: obs.disposition ?? "",
144
+ queue_depth: obs.queue_depth ?? 0,
145
+ });
146
+ case "budget_exceeded":
147
+ return withCategory({
148
+ kind: "budget_exceeded",
149
+ turn: t,
150
+ budget: obs.budget ?? "",
151
+ });
152
+ case "suspended":
153
+ return withCategory({
154
+ kind: "suspended",
155
+ turn: t,
156
+ reason: typeof obs.reason === "string" ? obs.reason : "",
157
+ pending_calls: obs.pending_calls ?? [],
158
+ });
159
+ case "resumed":
160
+ return withCategory({
161
+ kind: "resumed",
162
+ turn: t,
163
+ approved: obs.approved ?? [],
164
+ denied: obs.denied ?? [],
165
+ });
166
+ case "page_in_requested":
167
+ return null;
168
+ case "large_result_spooled":
169
+ return withCategory({
170
+ kind: "large_result_spooled",
171
+ turn: t,
172
+ call_id: obs.call_id ?? "",
173
+ tool: obs.tool ?? "",
174
+ original_size: obs.original_size ?? 0,
175
+ preview_size: obs.preview_size ?? 0,
176
+ spool_ref: opts.spoolRef,
177
+ });
178
+ case "memory_written":
179
+ return withCategory({
180
+ kind: "memory_written",
181
+ turn: t,
182
+ memory_id: obs.memory_id ?? "",
183
+ memory_kind: obs.memory_kind ?? "",
184
+ size_bytes: obs.size_bytes ?? 0,
185
+ });
186
+ case "memory_queried":
187
+ return withCategory({
188
+ kind: "memory_queried",
189
+ turn: t,
190
+ query_context: obs.query_context ?? "",
191
+ requested_k: obs.requested_k ?? 0,
192
+ requires_async_response: obs.requires_async_response ?? false,
193
+ });
194
+ case "memory_validation_failed":
195
+ return withCategory({
196
+ kind: "memory_validation_failed",
197
+ turn: t,
198
+ memory_id: obs.memory_id ?? "",
199
+ error: obs.error ?? "",
200
+ });
201
+ default:
202
+ return null;
203
+ }
204
+ }
205
+ export function primitiveForCategory(category) {
206
+ switch (category) {
207
+ case "syscall":
208
+ return "syscall";
209
+ case "mm":
210
+ return "mm";
211
+ case "proc":
212
+ case "ipc":
213
+ case "sched":
214
+ default:
215
+ return "sched";
216
+ }
217
+ }
218
+ export function primitiveForKind(kind) {
219
+ return primitiveForCategory(categoryForKind(kind));
220
+ }