@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 +36 -2
- package/dist/governance.d.ts +32 -0
- package/dist/governance.js +18 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/providers/anthropic.js +20 -3
- package/dist/providers/base.js +11 -1
- package/dist/runtime/execution-plane.d.ts +6 -3
- package/dist/runtime/execution-plane.js +54 -23
- package/dist/runtime/index.d.ts +3 -1
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/kernel-event-log.d.ts +26 -0
- package/dist/runtime/kernel-event-log.js +220 -0
- package/dist/runtime/kernel-step.d.ts +23 -1
- package/dist/runtime/kernel-step.js +9 -0
- package/dist/runtime/large-result-spool.d.ts +49 -0
- package/dist/runtime/large-result-spool.js +137 -0
- package/dist/runtime/os-profile.d.ts +30 -0
- package/dist/runtime/os-profile.js +71 -0
- package/dist/runtime/os-snapshot.d.ts +31 -0
- package/dist/runtime/os-snapshot.js +108 -0
- package/dist/runtime/runner.d.ts +57 -5
- package/dist/runtime/runner.js +359 -148
- package/dist/runtime/session-log.d.ts +117 -3
- package/dist/runtime/session-log.js +10 -2
- package/dist/runtime/sub-agent-orchestrator.d.ts +2 -2
- package/dist/runtime/sub-agent-orchestrator.js +14 -23
- package/dist/runtime/types/agent.d.ts +12 -3
- package/dist/runtime/types/agent.js +18 -0
- package/dist/types.d.ts +24 -0
- package/package.json +2 -2
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` —
|
|
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
|
|
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"
|
package/dist/governance.d.ts
CHANGED
|
@@ -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 {};
|
package/dist/governance.js
CHANGED
|
@@ -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,
|
|
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(
|
|
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
|
|
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:
|
|
94
|
+
totalTokens: inputTokens + outputTokens,
|
|
95
|
+
inputTokens,
|
|
96
|
+
outputTokens,
|
|
80
97
|
};
|
|
81
98
|
}
|
|
82
99
|
}
|
package/dist/providers/base.js
CHANGED
|
@@ -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
|
|
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
|
+
}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/runtime/index.js
CHANGED
|
@@ -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
|
+
}
|