@deepstrike/wasm 0.1.15 → 0.2.1

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.
@@ -1,4 +1,23 @@
1
- import type { ProviderReplay, ToolCall } from "../types.js";
1
+ import type { ProviderReplay, ToolCall, ToolErrorKind } from "../types.js";
2
+ export type RollbackReason = {
3
+ kind: "fatal_tool_error";
4
+ tool_name: string;
5
+ error: string;
6
+ } | {
7
+ kind: "governance_denied";
8
+ tool_name: string;
9
+ reason: string;
10
+ } | {
11
+ kind: "provider_failure";
12
+ error: string;
13
+ } | {
14
+ kind: "timeout";
15
+ } | {
16
+ kind: "user_interrupt";
17
+ } | {
18
+ kind: "malformed_replay";
19
+ reason: string;
20
+ };
2
21
  export type SessionEvent = {
3
22
  kind: "run_started";
4
23
  run_id: string;
@@ -24,12 +43,85 @@ export type SessionEvent = {
24
43
  call_id: string;
25
44
  output: string;
26
45
  is_error?: boolean;
46
+ is_fatal?: boolean;
47
+ error_kind?: ToolErrorKind;
27
48
  token_count?: number;
28
49
  }>;
50
+ } | {
51
+ kind: "tool_argument_repaired";
52
+ turn: number;
53
+ tool: string;
54
+ original_arguments: string;
55
+ repaired_arguments: string;
56
+ } | {
57
+ kind: "tool_denied";
58
+ turn: number;
59
+ call_id: string;
60
+ tool_name: string;
61
+ reason: string;
62
+ } | {
63
+ kind: "permission_requested";
64
+ turn: number;
65
+ tool: string;
66
+ arguments: string;
67
+ reason?: string;
68
+ } | {
69
+ kind: "permission_resolved";
70
+ turn: number;
71
+ approved: boolean;
72
+ responder: string;
29
73
  } | {
30
74
  kind: "compressed";
31
75
  turn: number;
32
76
  archived_seq_range: [number, number];
77
+ action?: "snip_compact" | "micro_compact" | "context_collapse" | "auto_compact";
78
+ summary?: string;
79
+ summary_tokens?: number;
80
+ archive_ref?: string;
81
+ preserved_refs?: string[];
82
+ } | {
83
+ kind: "rollbacked";
84
+ turn: number;
85
+ checkpoint_history_len: number;
86
+ reason?: RollbackReason;
87
+ } | {
88
+ kind: "capability_changed";
89
+ turn: number;
90
+ added: string[];
91
+ removed: string[];
92
+ change_kind?: string;
93
+ capability_id?: string;
94
+ version?: string;
95
+ mounted_by?: string;
96
+ mount_reason?: string;
97
+ } | {
98
+ kind: "milestone_advanced";
99
+ turn: number;
100
+ phase_id: string;
101
+ capabilities_unlocked: string[];
102
+ } | {
103
+ kind: "milestone_blocked";
104
+ turn: number;
105
+ phase_id: string;
106
+ reason: string;
107
+ } | {
108
+ kind: "milestone_evidence";
109
+ turn: number;
110
+ phase_id: string;
111
+ evidence: string[];
112
+ } | {
113
+ kind: "checkpoint_taken";
114
+ turn: number;
115
+ history_len: number;
116
+ } | {
117
+ kind: "agent_spawned";
118
+ turn: number;
119
+ agent_id: string;
120
+ parent_session_id: string;
121
+ role: string;
122
+ isolation: string;
123
+ context_inheritance: string;
124
+ permitted_capability_ids: string[];
33
125
  } | {
34
126
  kind: "run_terminal";
35
127
  reason: string;
@@ -0,0 +1,33 @@
1
+ import type { ProviderReplay, ToolCall } from "../types.js";
2
+ import type { SessionEvent } from "./session-log.js";
3
+ export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay-sanitize.js";
4
+ export declare function synthesizeProviderReplay(content: string, toolCalls: ToolCall[]): ProviderReplay | undefined;
5
+ export declare function effectiveProviderReplay(content: string, toolCalls: ToolCall[], stored?: ProviderReplay): ProviderReplay | undefined;
6
+ export declare function normalizeLlmCompleted(event: Extract<SessionEvent, {
7
+ kind: "llm_completed";
8
+ }>, maxBytes?: number): Extract<SessionEvent, {
9
+ kind: "llm_completed";
10
+ }>;
11
+ export declare function repairEventsForRecovery(events: Array<{
12
+ seq: number;
13
+ event: SessionEvent;
14
+ }>, maxBytes?: number): Array<{
15
+ seq: number;
16
+ event: SessionEvent;
17
+ }>;
18
+ export declare function buildLlmCompletedEvent(input: {
19
+ turn: number;
20
+ content: string;
21
+ tokenCount?: number;
22
+ toolCalls: ToolCall[];
23
+ providerReplay?: ProviderReplay;
24
+ }): Extract<SessionEvent, {
25
+ kind: "llm_completed";
26
+ }>;
27
+ export declare function buildRunTerminalEvent(input: {
28
+ reason: string;
29
+ turnsUsed: number;
30
+ totalTokens: number;
31
+ }): Extract<SessionEvent, {
32
+ kind: "run_terminal";
33
+ }>;
@@ -0,0 +1,73 @@
1
+ import { sanitizeReplayText } from "./replay-sanitize.js";
2
+ export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay-sanitize.js";
3
+ function estimateTokenCount(text) {
4
+ return Math.max(1, Math.ceil(text.length / 4));
5
+ }
6
+ function parseToolInput(args) {
7
+ try {
8
+ return JSON.parse(args || "{}");
9
+ }
10
+ catch {
11
+ return {};
12
+ }
13
+ }
14
+ export function synthesizeProviderReplay(content, toolCalls) {
15
+ if (!toolCalls.length)
16
+ return undefined;
17
+ const blocks = [];
18
+ if (content)
19
+ blocks.push({ type: "text", text: content });
20
+ for (const tc of toolCalls) {
21
+ blocks.push({
22
+ type: "tool_use",
23
+ id: tc.id,
24
+ name: tc.name,
25
+ input: parseToolInput(tc.arguments),
26
+ });
27
+ }
28
+ return { native_blocks: blocks };
29
+ }
30
+ export function effectiveProviderReplay(content, toolCalls, stored) {
31
+ if (stored?.native_blocks?.length || stored?.reasoning_content != null) {
32
+ return stored;
33
+ }
34
+ return synthesizeProviderReplay(content, toolCalls);
35
+ }
36
+ export function normalizeLlmCompleted(event, maxBytes) {
37
+ const content = sanitizeReplayText(event.content ?? "", maxBytes);
38
+ const toolCalls = event.tool_calls ?? [];
39
+ const providerReplay = effectiveProviderReplay(content, toolCalls, event.provider_replay);
40
+ return {
41
+ kind: "llm_completed",
42
+ turn: event.turn,
43
+ content,
44
+ tool_calls: toolCalls,
45
+ token_count: event.token_count ?? estimateTokenCount(content),
46
+ ...(providerReplay ? { provider_replay: providerReplay } : {}),
47
+ };
48
+ }
49
+ export function repairEventsForRecovery(events, maxBytes) {
50
+ return events.map(entry => {
51
+ if (entry.event.kind !== "llm_completed")
52
+ return entry;
53
+ return { ...entry, event: normalizeLlmCompleted(entry.event, maxBytes) };
54
+ });
55
+ }
56
+ export function buildLlmCompletedEvent(input) {
57
+ return normalizeLlmCompleted({
58
+ kind: "llm_completed",
59
+ turn: input.turn,
60
+ content: sanitizeReplayText(input.content),
61
+ tool_calls: input.toolCalls ?? [],
62
+ token_count: input.tokenCount,
63
+ provider_replay: input.providerReplay,
64
+ });
65
+ }
66
+ export function buildRunTerminalEvent(input) {
67
+ return {
68
+ kind: "run_terminal",
69
+ reason: input.reason,
70
+ turns_used: Math.max(0, input.turnsUsed),
71
+ total_tokens: Math.max(0, input.totalTokens),
72
+ };
73
+ }
@@ -0,0 +1,17 @@
1
+ import type { AgentRunSpec, AgentSpawnedObservation, SubAgentResult } from "./types/agent.js";
2
+ import type { RuntimeOptions } from "./runner.js";
3
+ import type { SessionLog } from "./session-log.js";
4
+ export interface SubAgentRunContext {
5
+ parentOpts: RuntimeOptions;
6
+ parentSessionId: string;
7
+ spec: AgentRunSpec;
8
+ manifest: AgentSpawnedObservation;
9
+ sessionLog: SessionLog;
10
+ }
11
+ /** Host-side driver for kernel-isolated sub-agent runs. */
12
+ export declare class SubAgentOrchestrator {
13
+ run(ctx: SubAgentRunContext): Promise<SubAgentResult>;
14
+ }
15
+ export declare const defaultSubAgentOrchestrator: SubAgentOrchestrator;
16
+ /** Kernel spawn without an active parent run loop (harness / coordinator use). */
17
+ export declare function spawnStandalone(parentOpts: RuntimeOptions, parentSessionId: string, spec: AgentRunSpec, orchestrator?: SubAgentOrchestrator): Promise<SubAgentResult>;
@@ -0,0 +1,112 @@
1
+ import { agentRunSpecToKernel } from "./types/agent.js";
2
+ import { FilteredExecutionPlane } from "./filtered-plane.js";
3
+ import { kernelApply } from "./kernel-step.js";
4
+ function terminationFromStatus(status) {
5
+ const normalized = status.toLowerCase();
6
+ if (normalized === "completed" ||
7
+ normalized === "max_turns" ||
8
+ normalized === "token_budget" ||
9
+ normalized === "timeout" ||
10
+ normalized === "user_abort" ||
11
+ normalized === "error" ||
12
+ normalized === "milestone_exceeded") {
13
+ return normalized;
14
+ }
15
+ return status;
16
+ }
17
+ /** Host-side driver for kernel-isolated sub-agent runs. */
18
+ export class SubAgentOrchestrator {
19
+ async run(ctx) {
20
+ const permitted = new Set(ctx.manifest.permitted_capability_ids ?? []);
21
+ const filteredPlane = new FilteredExecutionPlane(ctx.parentOpts.executionPlane, permitted);
22
+ let systemPrompt = ctx.parentOpts.systemPrompt;
23
+ let inheritEvents;
24
+ if (ctx.manifest.context_inheritance === "full") {
25
+ inheritEvents = await ctx.sessionLog.read(ctx.parentSessionId);
26
+ }
27
+ else if (ctx.manifest.context_inheritance === "system_only") {
28
+ const parentEvents = await ctx.sessionLog.read(ctx.parentSessionId);
29
+ const started = parentEvents.find(e => e.event.kind === "run_started");
30
+ if (started?.event.kind === "run_started" && started.event.system_prompt) {
31
+ systemPrompt = started.event.system_prompt;
32
+ }
33
+ }
34
+ const { RuntimeRunner } = await import("./runner.js");
35
+ const childRunner = new RuntimeRunner({
36
+ ...ctx.parentOpts,
37
+ executionPlane: filteredPlane,
38
+ agentId: ctx.spec.identity.agentId,
39
+ systemPrompt,
40
+ sessionLog: ctx.sessionLog,
41
+ });
42
+ let done;
43
+ let finalText = "";
44
+ for await (const evt of childRunner.run({
45
+ sessionId: ctx.spec.identity.sessionId,
46
+ goal: ctx.spec.goal,
47
+ })) {
48
+ if (evt.type === "text_delta")
49
+ finalText += evt.delta;
50
+ if (evt.type === "done")
51
+ done = evt;
52
+ }
53
+ const loopResult = {
54
+ termination: terminationFromStatus(done?.status ?? "error"),
55
+ turnsUsed: done?.iterations ?? 0,
56
+ totalTokensUsed: done?.totalTokens ?? 0,
57
+ ...(finalText
58
+ ? { finalMessage: { role: "assistant", content: finalText, toolCalls: [] } }
59
+ : {}),
60
+ };
61
+ return { agentId: ctx.spec.identity.agentId, result: loopResult };
62
+ }
63
+ }
64
+ export const defaultSubAgentOrchestrator = new SubAgentOrchestrator();
65
+ /** Kernel spawn without an active parent run loop (harness / coordinator use). */
66
+ export async function spawnStandalone(parentOpts, parentSessionId, spec, orchestrator = defaultSubAgentOrchestrator) {
67
+ const kernel = await (await import("./kernel.js")).getKernel();
68
+ const runtime = new kernel.KernelRuntime({
69
+ maxTokens: parentOpts.maxTokens,
70
+ maxTurns: parentOpts.maxTurns ?? 25,
71
+ timeoutMs: parentOpts.timeoutMs !== undefined ? BigInt(parentOpts.timeoutMs) : undefined,
72
+ });
73
+ const pending = [];
74
+ kernelApply(runtime, pending, { kind: "start_run", task: { goal: "coordinator", criteria: [] } });
75
+ const observations = kernelApply(runtime, pending, {
76
+ kind: "spawn_sub_agent",
77
+ spec: agentRunSpecToKernel(spec),
78
+ parent_session_id: parentSessionId,
79
+ });
80
+ const spawned = observations.find(o => o.kind === "agent_spawned");
81
+ if (!spawned || typeof spawned.agent_id !== "string") {
82
+ throw new Error("spawn_sub_agent did not emit agent_spawned");
83
+ }
84
+ const turn = spawned.turn ?? 0;
85
+ await parentOpts.sessionLog.append(parentSessionId, {
86
+ kind: "agent_spawned",
87
+ turn,
88
+ agent_id: spawned.agent_id,
89
+ parent_session_id: spawned.parent_session_id ?? parentSessionId,
90
+ role: spawned.role ?? spec.role,
91
+ isolation: spawned.isolation ?? spec.isolation ?? "shared",
92
+ context_inheritance: spawned.context_inheritance ?? "none",
93
+ permitted_capability_ids: spawned.permitted_capability_ids ?? [],
94
+ });
95
+ const manifest = {
96
+ kind: "agent_spawned",
97
+ turn,
98
+ agent_id: spawned.agent_id,
99
+ parent_session_id: spawned.parent_session_id ?? parentSessionId,
100
+ role: spawned.role ?? spec.role,
101
+ isolation: spawned.isolation ?? spec.isolation ?? "shared",
102
+ context_inheritance: spawned.context_inheritance ?? "none",
103
+ permitted_capability_ids: spawned.permitted_capability_ids ?? [],
104
+ };
105
+ return orchestrator.run({
106
+ parentOpts,
107
+ parentSessionId,
108
+ spec,
109
+ manifest,
110
+ sessionLog: parentOpts.sessionLog,
111
+ });
112
+ }
@@ -0,0 +1,68 @@
1
+ import type { Message } from "../../types.js";
2
+ export type KernelAgentRole = "explore" | "plan" | "implement" | "verify" | "custom";
3
+ export type AgentIsolation = "shared" | "read_only" | "worktree" | "remote";
4
+ export type ContextInheritance = "none" | "system_only" | "full";
5
+ export type TerminationReason = "completed" | "max_turns" | "token_budget" | "timeout" | "user_abort" | "error" | "milestone_exceeded";
6
+ export type MilestonePolicy = "require_verifier" | "terminate" | "auto_pass";
7
+ export interface AgentIdentity {
8
+ agentId: string;
9
+ sessionId: string;
10
+ isSubAgent: boolean;
11
+ parentSessionId?: string;
12
+ }
13
+ export interface AgentCapabilityFilter {
14
+ allowedKinds?: string[];
15
+ allowedIds?: string[];
16
+ }
17
+ export interface AgentRunSpec {
18
+ identity: AgentIdentity;
19
+ role: KernelAgentRole;
20
+ isolation?: AgentIsolation;
21
+ goal: string;
22
+ verificationContractId?: string;
23
+ capabilityFilter?: AgentCapabilityFilter;
24
+ milestones?: MilestoneContract;
25
+ metadata?: Record<string, unknown>;
26
+ }
27
+ export interface AgentSpawnedObservation {
28
+ kind: "agent_spawned";
29
+ turn?: number;
30
+ agent_id: string;
31
+ parent_session_id: string;
32
+ role: string;
33
+ isolation: string;
34
+ context_inheritance: string;
35
+ permitted_capability_ids: string[];
36
+ }
37
+ export interface LoopResult {
38
+ termination: TerminationReason | string;
39
+ finalMessage?: Message;
40
+ turnsUsed: number;
41
+ totalTokensUsed: number;
42
+ }
43
+ export interface SubAgentResult {
44
+ agentId: string;
45
+ result: LoopResult;
46
+ }
47
+ export interface MilestoneCheckResult {
48
+ phaseId: string;
49
+ passed: boolean;
50
+ reason?: string;
51
+ }
52
+ export interface MilestonePhase {
53
+ id: string;
54
+ criteria?: string[];
55
+ unlocks?: Array<Record<string, unknown>>;
56
+ verifier?: Record<string, unknown>;
57
+ requiredEvidence?: string[];
58
+ }
59
+ export interface MilestoneContract {
60
+ phases: MilestonePhase[];
61
+ }
62
+ export declare function agentIdentitySub(agentId: string, sessionId: string, parentSessionId?: string): AgentIdentity;
63
+ export declare function agentRunSpecToKernel(spec: AgentRunSpec): Record<string, unknown>;
64
+ export declare function milestoneContractToKernel(contract: MilestoneContract): Record<string, unknown>;
65
+ export declare function milestoneCheckResultToKernel(result: MilestoneCheckResult): Record<string, unknown>;
66
+ export declare function subAgentResultToKernel(result: SubAgentResult): Record<string, unknown>;
67
+ export declare function milestoneCheckPass(phaseId: string): MilestoneCheckResult;
68
+ export declare function milestoneCheckFail(phaseId: string, reason: string): MilestoneCheckResult;
@@ -0,0 +1,78 @@
1
+ export function agentIdentitySub(agentId, sessionId, parentSessionId) {
2
+ return {
3
+ agentId,
4
+ sessionId,
5
+ isSubAgent: true,
6
+ ...(parentSessionId ? { parentSessionId } : {}),
7
+ };
8
+ }
9
+ export function agentRunSpecToKernel(spec) {
10
+ const out = {
11
+ identity: {
12
+ agent_id: spec.identity.agentId,
13
+ session_id: spec.identity.sessionId,
14
+ is_sub_agent: spec.identity.isSubAgent,
15
+ ...(spec.identity.parentSessionId ? { parent_session_id: spec.identity.parentSessionId } : {}),
16
+ },
17
+ role: spec.role,
18
+ isolation: spec.isolation ?? "shared",
19
+ goal: spec.goal,
20
+ capability_filter: {
21
+ allowed_kinds: spec.capabilityFilter?.allowedKinds ?? [],
22
+ allowed_ids: spec.capabilityFilter?.allowedIds ?? [],
23
+ },
24
+ metadata: spec.metadata ?? null,
25
+ };
26
+ if (spec.verificationContractId)
27
+ out.verification_contract_id = spec.verificationContractId;
28
+ if (spec.milestones)
29
+ out.milestones = milestoneContractToKernel(spec.milestones);
30
+ return out;
31
+ }
32
+ export function milestoneContractToKernel(contract) {
33
+ return {
34
+ phases: contract.phases.map(phase => ({
35
+ id: phase.id,
36
+ criteria: phase.criteria ?? [],
37
+ unlocks: phase.unlocks ?? [],
38
+ ...(phase.verifier ? { verifier: phase.verifier } : {}),
39
+ required_evidence: phase.requiredEvidence ?? [],
40
+ })),
41
+ };
42
+ }
43
+ export function milestoneCheckResultToKernel(result) {
44
+ return {
45
+ phase_id: result.phaseId,
46
+ passed: result.passed,
47
+ ...(result.reason ? { reason: result.reason } : {}),
48
+ };
49
+ }
50
+ export function subAgentResultToKernel(result) {
51
+ const finalMessage = result.result.finalMessage;
52
+ return {
53
+ agent_id: result.agentId,
54
+ result: {
55
+ termination: result.result.termination,
56
+ final_message: finalMessage
57
+ ? {
58
+ role: finalMessage.role,
59
+ content: finalMessage.content,
60
+ tool_calls: (finalMessage.toolCalls ?? []).map(tc => ({
61
+ id: tc.id,
62
+ name: tc.name,
63
+ arguments: JSON.parse(tc.arguments || "{}"),
64
+ })),
65
+ ...(finalMessage.tokenCount !== undefined ? { token_count: finalMessage.tokenCount } : {}),
66
+ }
67
+ : null,
68
+ turns_used: result.result.turnsUsed,
69
+ total_tokens_used: result.result.totalTokensUsed,
70
+ },
71
+ };
72
+ }
73
+ export function milestoneCheckPass(phaseId) {
74
+ return { phaseId, passed: true };
75
+ }
76
+ export function milestoneCheckFail(phaseId, reason) {
77
+ return { phaseId, passed: false, reason };
78
+ }
package/dist/types.d.ts CHANGED
@@ -9,10 +9,13 @@ export interface ToolCall {
9
9
  name: string;
10
10
  arguments: string;
11
11
  }
12
+ export type ToolErrorKind = "recoverable" | "fatal" | "governance_denied" | "provider_failure" | "timeout" | "user_interrupt";
12
13
  export interface ToolResult {
13
14
  callId: string;
14
15
  output: string;
15
16
  isError: boolean;
17
+ isFatal?: boolean;
18
+ errorKind?: ToolErrorKind;
16
19
  tokenCount?: number;
17
20
  }
18
21
  export interface ToolSchema {
@@ -52,6 +55,8 @@ export interface ToolResultEvent extends StreamEvent {
52
55
  name: string;
53
56
  content: string;
54
57
  isError: boolean;
58
+ isFatal?: boolean;
59
+ errorKind?: ToolErrorKind;
55
60
  }
56
61
  export interface DoneEvent extends StreamEvent {
57
62
  type: "done";
@@ -70,6 +75,19 @@ export interface PermissionRequestEvent extends StreamEvent {
70
75
  arguments: string;
71
76
  reason: string;
72
77
  }
78
+ export interface ToolDeniedEvent extends StreamEvent {
79
+ type: "tool_denied";
80
+ callId: string;
81
+ toolName: string;
82
+ reason: string;
83
+ }
84
+ export interface ToolArgumentRepairedEvent extends StreamEvent {
85
+ type: "tool_argument_repaired";
86
+ callId: string;
87
+ name: string;
88
+ originalArguments: string;
89
+ repairedArguments: string;
90
+ }
73
91
  /**
74
92
  * Opaque per-run state owned by the provider (e.g. OpenAI Responses continuation).
75
93
  * The framework creates and threads this object; providers may read/write it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstrike/wasm",
3
- "version": "0.1.15",
3
+ "version": "0.2.1",
4
4
  "description": "DeepStrike WASM SDK — browser, Cloudflare Workers, Deno Deploy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "test": "node --experimental-vm-modules node_modules/.bin/jest"
16
16
  },
17
17
  "dependencies": {
18
- "@deepstrike/wasm-kernel": "0.1.15"
18
+ "@deepstrike/wasm-kernel": "0.2.1"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/jest": "^30.0.0",