@deepstrike/wasm 0.2.5 → 0.2.7

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?" }))
@@ -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
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
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, AgentProcessChangedObservation, 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, WorkflowSpec, WorkflowNodeSpec, WorkflowTaskSpec, WorkflowSpawnInfo, } from "./runtime/types/agent.js";
7
+ export { workflowSpecToKernel, fanoutSynthesize, generateAndFilter, verifyRules } from "./runtime/types/agent.js";
7
8
  export { Governance } from "./governance.js";
8
9
  export type { GovernanceVerdict } from "./governance.js";
9
10
  export { AnthropicProvider } from "./providers/anthropic.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
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
+ export { workflowSpecToKernel, fanoutSynthesize, generateAndFilter, verifyRules } from "./runtime/types/agent.js";
4
5
  export { Governance } from "./governance.js";
5
6
  export { AnthropicProvider } from "./providers/anthropic.js";
6
7
  export { OpenAIProvider, QwenProvider, DeepSeekProvider, MiniMaxProvider, KimiProvider } from "./providers/openai.js";
@@ -1,10 +1,11 @@
1
- import type { RenderedContext, ToolSchema, StreamEvent, LLMProvider, Message, ProviderReplay } from "../types.js";
1
+ import type { RenderedContext, ToolSchema, StreamEvent, LLMProvider, Message, ProviderDescriptor, ProviderReplay } from "../types.js";
2
2
  export declare class AnthropicProvider implements LLMProvider {
3
3
  private readonly apiKey;
4
4
  private readonly model;
5
5
  private readonly maxTokens;
6
6
  private nativeAssistantBlocks;
7
7
  constructor(apiKey: string, model?: string, maxTokens?: number);
8
+ descriptor(): ProviderDescriptor;
8
9
  peekProviderReplay(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
9
10
  seedProviderReplay(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
10
11
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
@@ -17,6 +17,22 @@ export class AnthropicProvider {
17
17
  this.model = model;
18
18
  this.maxTokens = maxTokens;
19
19
  }
20
+ descriptor() {
21
+ return {
22
+ provider: "anthropic",
23
+ protocol: "anthropic-messages",
24
+ model: this.model,
25
+ reasoning: {
26
+ supported: true,
27
+ preserveAcrossToolTurns: true,
28
+ requiresReplayForToolTurns: true,
29
+ },
30
+ toolCalls: {
31
+ supported: true,
32
+ requiresStrictPairing: true,
33
+ },
34
+ };
35
+ }
20
36
  peekProviderReplay(message) {
21
37
  const blocks = this.nativeAssistantBlocks.get(assistantReplayKey(message));
22
38
  return blocks?.length ? { native_blocks: blocks } : undefined;
@@ -24,7 +40,13 @@ export class AnthropicProvider {
24
40
  seedProviderReplay(message, replay) {
25
41
  if (replay.native_blocks?.length) {
26
42
  this.nativeAssistantBlocks.set(assistantReplayKey(message), replay.native_blocks);
43
+ return;
27
44
  }
45
+ // Legacy log without persisted native blocks: reconstruct neutral
46
+ // text + tool_use blocks so a tool-use turn can be replayed.
47
+ const blocks = reconstructAnthropicBlocks(message);
48
+ if (blocks.length)
49
+ this.nativeAssistantBlocks.set(assistantReplayKey(message), blocks);
28
50
  }
29
51
  async complete(context, tools, extensions) {
30
52
  return collectStreamMessage(this.stream(context, tools, extensions));
@@ -154,3 +176,26 @@ export class AnthropicProvider {
154
176
  this.nativeAssistantBlocks.set(assistantReplayKey(message), blocks);
155
177
  }
156
178
  }
179
+ /**
180
+ * Reconstruct Anthropic assistant content blocks from a neutral transcript when
181
+ * no provider replay was persisted. Only meaningful for tool-use turns.
182
+ */
183
+ function reconstructAnthropicBlocks(message) {
184
+ const toolCalls = message.toolCalls ?? [];
185
+ if (!toolCalls.length)
186
+ return [];
187
+ const blocks = [];
188
+ if (message.content)
189
+ blocks.push({ type: "text", text: message.content });
190
+ for (const tc of toolCalls) {
191
+ let input = {};
192
+ try {
193
+ input = JSON.parse(tc.arguments || "{}");
194
+ }
195
+ catch {
196
+ input = {};
197
+ }
198
+ blocks.push({ type: "tool_use", id: tc.id, name: tc.name, input });
199
+ }
200
+ return blocks;
201
+ }
@@ -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";
@@ -12,6 +12,7 @@ export function categoryForKind(kind) {
12
12
  case "large_result_spooled":
13
13
  case "memory_written":
14
14
  case "memory_queried":
15
+ case "memory_validation_failed":
15
16
  return "mm";
16
17
  case "agent_process_changed":
17
18
  return "proc";
@@ -190,6 +191,33 @@ export function kernelObservationToSessionEvent(obs, turn, opts = {}) {
190
191
  requested_k: obs.requested_k ?? 0,
191
192
  requires_async_response: obs.requires_async_response ?? false,
192
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
+ case "workflow_batch_spawned": {
202
+ const nodes = obs.nodes ?? [];
203
+ return withCategory({
204
+ kind: "workflow_batch_spawned",
205
+ turn: t,
206
+ node_count: nodes.length,
207
+ node_ids: nodes.map((n) => n.agent_id ?? ""),
208
+ });
209
+ }
210
+ case "workflow_completed": {
211
+ const completed = obs.completed ?? [];
212
+ const failed = obs.failed ?? [];
213
+ return withCategory({
214
+ kind: "workflow_completed",
215
+ turn: t,
216
+ completed,
217
+ failed,
218
+ total_nodes: completed.length + failed.length,
219
+ });
220
+ }
193
221
  default:
194
222
  return null;
195
223
  }
@@ -91,6 +91,18 @@ export interface KernelObservation {
91
91
  query_context?: string;
92
92
  requested_k?: number;
93
93
  requires_async_response?: boolean;
94
+ /** memory_validation_failed (Phase 7). */
95
+ error?: string;
96
+ nodes?: Array<{
97
+ agent_id: string;
98
+ goal: string;
99
+ role: string;
100
+ isolation: string;
101
+ context_inheritance: string;
102
+ model_hint?: string;
103
+ }>;
104
+ completed?: string[];
105
+ failed?: string[];
94
106
  }
95
107
  export declare function toolSchemaToKernel(schema: ToolSchema): Record<string, unknown>;
96
108
  export declare function skillMetadataToKernel(skill: SkillMetadata): Record<string, unknown>;
@@ -1,7 +1,30 @@
1
1
  import type { GovernancePolicy } from "../governance.js";
2
+ export type OsProfileId = "native";
3
+ export interface NativeOsProfile {
4
+ id: OsProfileId;
5
+ attentionPolicy: {
6
+ maxQueueSize?: number;
7
+ };
8
+ governancePolicy: GovernancePolicy;
9
+ }
2
10
  /** Default attention policy for native profile smoke tests. */
3
11
  export declare const DEFAULT_NATIVE_ATTENTION_POLICY: {
4
12
  maxQueueSize: number;
5
13
  };
6
14
  /** Permissive governance policy for native runs that do not need AskUser. */
7
15
  export declare const DEFAULT_NATIVE_GOVERNANCE_POLICY: GovernancePolicy;
16
+ /** Default restrictive sandbox policy template requiring confirmation for modification/execution. */
17
+ export declare const DEFAULT_SANDBOX_POLICY: GovernancePolicy;
18
+ /** Resolve a named OS profile into concrete kernel-owned policy defaults. */
19
+ export declare function osProfile(profile?: OsProfileId | NativeOsProfile): NativeOsProfile;
20
+ /** Assert that a runtime is using a valid native microkernel policy profile. */
21
+ export declare function assertNativeProfile(profile?: OsProfileId | NativeOsProfile): NativeOsProfile;
22
+ /**
23
+ * Validates the declarative policies statically to prevent runtime crashes when loaded into the microkernel.
24
+ */
25
+ export declare function validateDeclarativePolicy(govPolicy?: GovernancePolicy, attentionPolicy?: {
26
+ maxQueueSize?: number;
27
+ }): {
28
+ valid: boolean;
29
+ errors: string[];
30
+ };
@@ -4,3 +4,68 @@ export const DEFAULT_NATIVE_ATTENTION_POLICY = { maxQueueSize: 64 };
4
4
  export const DEFAULT_NATIVE_GOVERNANCE_POLICY = {
5
5
  rules: [{ pattern: "*", action: "allow" }],
6
6
  };
7
+ /** Default restrictive sandbox policy template requiring confirmation for modification/execution. */
8
+ export const DEFAULT_SANDBOX_POLICY = {
9
+ rules: [
10
+ { pattern: "read_file", action: "allow" },
11
+ { pattern: "write_file", action: "ask_user" },
12
+ { pattern: "run_command", action: "ask_user" },
13
+ { pattern: "*", action: "deny" },
14
+ ],
15
+ };
16
+ /** Resolve a named OS profile into concrete kernel-owned policy defaults. */
17
+ export function osProfile(profile = "native") {
18
+ if (typeof profile !== "string")
19
+ return profile;
20
+ if (profile !== "native")
21
+ throw new Error(`Unsupported OS profile: ${profile}`);
22
+ return {
23
+ id: "native",
24
+ attentionPolicy: DEFAULT_NATIVE_ATTENTION_POLICY,
25
+ governancePolicy: DEFAULT_NATIVE_GOVERNANCE_POLICY,
26
+ };
27
+ }
28
+ /** Assert that a runtime is using a valid native microkernel policy profile. */
29
+ export function assertNativeProfile(profile = "native") {
30
+ const resolved = osProfile(profile);
31
+ if (resolved.id !== "native") {
32
+ throw new Error(`Unsupported OS profile: ${resolved.id}`);
33
+ }
34
+ const validation = validateDeclarativePolicy(resolved.governancePolicy, resolved.attentionPolicy);
35
+ if (!validation.valid) {
36
+ throw new Error(`Invalid native OS profile: ${validation.errors.join("; ")}`);
37
+ }
38
+ return resolved;
39
+ }
40
+ /**
41
+ * Validates the declarative policies statically to prevent runtime crashes when loaded into the microkernel.
42
+ */
43
+ export function validateDeclarativePolicy(govPolicy, attentionPolicy) {
44
+ const errors = [];
45
+ if (govPolicy) {
46
+ if (!Array.isArray(govPolicy.rules)) {
47
+ errors.push("GovernancePolicy rules must be an array");
48
+ }
49
+ else {
50
+ govPolicy.rules.forEach((rule, idx) => {
51
+ if (!rule.pattern || typeof rule.pattern !== "string") {
52
+ errors.push(`Rule[${idx}] pattern is missing or not a string`);
53
+ }
54
+ if (!["allow", "deny", "ask_user"].includes(rule.action)) {
55
+ errors.push(`Rule[${idx}] action '${rule.action}' is invalid. Allowed: allow, deny, ask_user`);
56
+ }
57
+ });
58
+ }
59
+ }
60
+ if (attentionPolicy) {
61
+ if (attentionPolicy.maxQueueSize !== undefined) {
62
+ if (typeof attentionPolicy.maxQueueSize !== "number" || attentionPolicy.maxQueueSize <= 0) {
63
+ errors.push("AttentionPolicy maxQueueSize must be a positive integer");
64
+ }
65
+ }
66
+ }
67
+ return {
68
+ valid: errors.length === 0,
69
+ errors,
70
+ };
71
+ }
@@ -1,6 +1,12 @@
1
- import type { LLMProvider, Message, ProviderReplay, ToolCall } from "../types.js";
1
+ import type { LLMProvider, Message, ProviderDescriptor, ProviderReplay, ToolCall } from "../types.js";
2
2
  import type { SessionEvent } from "./session-log.js";
3
3
  export declare function assistantReplayKey(message: Pick<Message, "content" | "toolCalls">): string;
4
+ /**
5
+ * A stored replay may only be seeded into a provider speaking the same wire
6
+ * protocol; on a cross-protocol fallback the incompatible envelope is skipped so
7
+ * the new provider re-serializes neutral context instead.
8
+ */
9
+ export declare function isReplayCompatibleWithProvider(replay: ProviderReplay, descriptor: ProviderDescriptor | undefined): boolean;
4
10
  export declare function seedProviderReplayFromEvents(provider: LLMProvider, events: Array<{
5
11
  event: SessionEvent;
6
12
  }>): void;
@@ -1,21 +1,46 @@
1
- import { effectiveProviderReplay } from "./session-repair.js";
2
1
  export function assistantReplayKey(message) {
3
2
  return JSON.stringify({
4
3
  content: message.content,
5
4
  toolCalls: message.toolCalls ?? [],
6
5
  });
7
6
  }
7
+ /** Infer the wire protocol a stored replay envelope belongs to (explicit, or by shape for legacy logs). */
8
+ function replayProtocol(replay) {
9
+ if (replay.protocol)
10
+ return replay.protocol;
11
+ if (replay.native_blocks?.length)
12
+ return "anthropic-messages";
13
+ if (replay.reasoning_content != null || replay.reasoning_details !== undefined)
14
+ return "openai-chat";
15
+ return undefined;
16
+ }
17
+ /**
18
+ * A stored replay may only be seeded into a provider speaking the same wire
19
+ * protocol; on a cross-protocol fallback the incompatible envelope is skipped so
20
+ * the new provider re-serializes neutral context instead.
21
+ */
22
+ export function isReplayCompatibleWithProvider(replay, descriptor) {
23
+ if (!descriptor)
24
+ return true;
25
+ const protocol = replayProtocol(replay);
26
+ if (!protocol)
27
+ return true;
28
+ return protocol === descriptor.protocol;
29
+ }
8
30
  export function seedProviderReplayFromEvents(provider, events) {
9
31
  if (!provider.seedProviderReplay)
10
32
  return;
33
+ const descriptor = provider.descriptor?.();
11
34
  for (const { event } of events) {
12
35
  if (event.kind !== "llm_completed")
13
36
  continue;
14
37
  const toolCalls = event.tool_calls ?? [];
15
- const replay = effectiveProviderReplay(event.content, toolCalls, event.provider_replay);
16
- if (!replay)
38
+ const stored = event.provider_replay;
39
+ if (stored && !isReplayCompatibleWithProvider(stored, descriptor))
17
40
  continue;
18
- provider.seedProviderReplay({ content: event.content, toolCalls }, replay);
41
+ // Pass the message even with no persisted replay: a provider may reconstruct
42
+ // a legacy replay (e.g. Anthropic native_blocks) from the neutral transcript.
43
+ provider.seedProviderReplay({ content: event.content, toolCalls }, stored ?? {});
19
44
  }
20
45
  }
21
46
  export function peekProviderReplay(provider, content, toolCalls) {
@@ -6,9 +6,39 @@ import type { SignalSource } from "../signals/index.js";
6
6
  import type { SessionLog, SessionEvent } from "./session-log.js";
7
7
  import type { ExecutionPlane } from "./execution-plane.js";
8
8
  import { type GovernancePolicy } from "../governance.js";
9
- import type { AgentRunSpec, SubAgentResult, MilestonePolicy, MilestoneContract, MilestoneCheckResult } from "./types/agent.js";
9
+ import type { AgentRunSpec, SubAgentResult, MilestonePolicy, MilestoneContract, MilestoneCheckResult, WorkflowSpec } from "./types/agent.js";
10
10
  import { type SubAgentOrchestrator } from "./sub-agent-orchestrator.js";
11
+ import { type NativeOsProfile, type OsProfileId } from "./os-profile.js";
11
12
  import { LargeResultSpool } from "./large-result-spool.js";
13
+ export interface MemoryWriteRateLimit {
14
+ maxWrites: number;
15
+ windowMs: number;
16
+ }
17
+ export interface ResourceQuota {
18
+ /** Max sub-agents in the `running` state at once; further spawns are denied while at cap. */
19
+ maxConcurrentSubagents?: number;
20
+ /** Max sub-agent nesting depth (direct children of the root loop are depth 1). */
21
+ maxSpawnDepth?: number;
22
+ /** Rolling-window memory-write rate limit: at most `maxWrites` per any `windowMs` span. */
23
+ memoryWritesPerWindow?: MemoryWriteRateLimit;
24
+ }
25
+ export interface SchedulerBudget {
26
+ maxWallMs?: number;
27
+ }
28
+ /**
29
+ * Long-term memory policy (`set_memory_policy`) — opt-in, kernel-enforced. `validationEnabled:
30
+ * false` admits writes without validation, `maxContentBytes` / `maxNameLength` override the
31
+ * validation limits, and `retrievalTopK` caps `query_memory` breadth. `memoryPath` /
32
+ * `staleWarningDays` are carried for SDK recall I/O. Omitted fields keep the kernel defaults.
33
+ */
34
+ export interface MemoryPolicy {
35
+ memoryPath?: string;
36
+ staleWarningDays?: number;
37
+ retrievalTopK?: number;
38
+ validationEnabled?: boolean;
39
+ maxContentBytes?: number;
40
+ maxNameLength?: number;
41
+ }
12
42
  export interface RuntimeOptions {
13
43
  provider: LLMProvider;
14
44
  sessionLog: SessionLog;
@@ -25,10 +55,17 @@ export interface RuntimeOptions {
25
55
  knowledgeSource?: KnowledgeSource;
26
56
  signalSource?: SignalSource;
27
57
  extensions?: Record<string, unknown>;
58
+ /** Named or concrete OS profile. Defaults to the native microkernel profile. */
59
+ osProfile?: OsProfileId | NativeOsProfile;
28
60
  governancePolicy?: GovernancePolicy;
29
61
  attentionPolicy?: {
30
62
  maxQueueSize?: number;
31
63
  };
64
+ schedulerBudget?: SchedulerBudget;
65
+ resourceQuota?: ResourceQuota;
66
+ memoryPolicy?: MemoryPolicy;
67
+ tokenizer?: string;
68
+ enablePlanTool?: boolean;
32
69
  onToolSuspend?: (event: ToolSuspendEvent) => Promise<unknown> | unknown;
33
70
  onPermissionRequest?: (event: PermissionRequestEvent) => Promise<PermissionResponse | boolean> | PermissionResponse | boolean;
34
71
  subAgentOrchestrator?: SubAgentOrchestrator;
@@ -76,6 +113,26 @@ export declare class RuntimeRunner {
76
113
  dream(agentId: string, nowMs?: number): Promise<DreamResult>;
77
114
  private execute;
78
115
  spawnSubAgent(spec: AgentRunSpec): Promise<SubAgentResult>;
116
+ /**
117
+ * W0-ABI: run a declarative workflow DAG. The kernel owns the DAG and gates every node spawn
118
+ * through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
119
+ * feeds their results back, and loops until the kernel reports the workflow complete.
120
+ */
121
+ runWorkflow(spec: WorkflowSpec, opts?: {
122
+ resumedCompleted?: string[];
123
+ }): Promise<{
124
+ completed: string[];
125
+ failed: string[];
126
+ }>;
127
+ /**
128
+ * Resume a workflow from the parent session's completed nodes.
129
+ * Reads the session log, extracts completed workflow node agent_ids, and
130
+ * calls runWorkflow with resumedCompleted so the kernel skips those nodes.
131
+ */
132
+ resumeWorkflow(spec: WorkflowSpec): Promise<{
133
+ completed: string[];
134
+ failed: string[];
135
+ }>;
79
136
  private appendObservations;
80
137
  private archiveSemanticPageOut;
81
138
  }
@@ -3,12 +3,12 @@ import { governancePolicyToKernelEvent } from "../governance.js";
3
3
  import { getKernel } from "./kernel.js";
4
4
  import { peekProviderReplay, seedProviderReplayFromEvents } from "./provider-replay.js";
5
5
  import { sanitizeReplayText } from "./replay-sanitize.js";
6
- import { buildLlmCompletedEvent, buildRunTerminalEvent, repairEventsForRecovery } from "./session-repair.js";
6
+ import { buildLlmCompletedEvent, buildRunTerminalEvent, buildWorkflowNodeCompletedEvent, recoverCompletedWorkflowNodes, repairEventsForRecovery, } from "./session-repair.js";
7
7
  import { forceCompact, kernelAction, kernelApply, kernelMaybeAction, messageToKernelMessage, skillMetadataToKernel, toolResultToKernel, toolSchemaToKernel, } from "./kernel-step.js";
8
- import { agentRunSpecToKernel, findSpawnProcessObservation, milestoneCheckPass, milestoneCheckResultToKernel, spawnObservationToManifest, subAgentResultToKernel, } from "./types/agent.js";
8
+ import { agentRunSpecToKernel, findSpawnProcessObservation, milestoneCheckPass, milestoneCheckResultToKernel, spawnObservationToManifest, subAgentResultToKernel, workflowNodeToManifest, workflowNodeToSpec, workflowSpecToKernel, } from "./types/agent.js";
9
9
  import { defaultSubAgentOrchestrator } from "./sub-agent-orchestrator.js";
10
10
  import { kernelObservationToSessionEvent, withCategory } from "./kernel-event-log.js";
11
- import { DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY } from "./os-profile.js";
11
+ import { assertNativeProfile } from "./os-profile.js";
12
12
  import { LargeResultSpool } from "./large-result-spool.js";
13
13
  export class RuntimeRunner {
14
14
  opts;
@@ -259,6 +259,18 @@ export class RuntimeRunner {
259
259
  timeoutMs: effectiveTimeoutMs !== undefined ? BigInt(effectiveTimeoutMs) : undefined,
260
260
  });
261
261
  this.activeKernel = runtime;
262
+ if (this.opts.tokenizer) {
263
+ kernelApply(runtime, this.pendingObservations, {
264
+ kind: "set_tokenizer",
265
+ name: this.opts.tokenizer,
266
+ });
267
+ }
268
+ if (this.opts.enablePlanTool !== undefined) {
269
+ kernelApply(runtime, this.pendingObservations, {
270
+ kind: "set_plan_tool_enabled",
271
+ enabled: this.opts.enablePlanTool,
272
+ });
273
+ }
262
274
  kernelApply(runtime, this.pendingObservations, {
263
275
  kind: "set_tools",
264
276
  tools: this.opts.executionPlane.schemas().map(toolSchemaToKernel),
@@ -273,7 +285,7 @@ export class RuntimeRunner {
273
285
  if (this.opts.initialMemory) {
274
286
  for (const mem of this.opts.initialMemory) {
275
287
  kernelApply(runtime, this.pendingObservations, {
276
- kind: "add_memory_message",
288
+ kind: "add_knowledge_message",
277
289
  content: mem,
278
290
  tokens: Math.max(1, Math.ceil(mem.length / 4)),
279
291
  });
@@ -327,8 +339,9 @@ export class RuntimeRunner {
327
339
  if (this.opts.runSpec) {
328
340
  startPayload.run_spec = agentRunSpecToKernel(this.opts.runSpec);
329
341
  }
330
- const attentionPolicy = this.opts.attentionPolicy ?? DEFAULT_NATIVE_ATTENTION_POLICY;
331
- const governancePolicy = this.opts.governancePolicy ?? DEFAULT_NATIVE_GOVERNANCE_POLICY;
342
+ const osProfile = assertNativeProfile(this.opts.osProfile ?? "native");
343
+ const attentionPolicy = this.opts.attentionPolicy ?? osProfile.attentionPolicy;
344
+ const governancePolicy = this.opts.governancePolicy ?? osProfile.governancePolicy;
332
345
  kernelApply(runtime, this.pendingObservations, governancePolicyToKernelEvent(governancePolicy));
333
346
  kernelApply(runtime, this.pendingObservations, {
334
347
  kind: "set_attention_policy",
@@ -336,6 +349,46 @@ export class RuntimeRunner {
336
349
  ? { max_queue_size: attentionPolicy.maxQueueSize }
337
350
  : {}),
338
351
  });
352
+ if (this.opts.schedulerBudget) {
353
+ kernelApply(runtime, this.pendingObservations, {
354
+ kind: "set_scheduler_budget",
355
+ ...(this.opts.schedulerBudget.maxWallMs !== undefined
356
+ ? { max_wall_ms: this.opts.schedulerBudget.maxWallMs }
357
+ : {}),
358
+ });
359
+ }
360
+ if (this.opts.resourceQuota) {
361
+ const q = this.opts.resourceQuota;
362
+ kernelApply(runtime, this.pendingObservations, {
363
+ kind: "set_resource_quota",
364
+ quota: {
365
+ ...(q.maxConcurrentSubagents !== undefined
366
+ ? { max_concurrent_subagents: q.maxConcurrentSubagents }
367
+ : {}),
368
+ ...(q.maxSpawnDepth !== undefined ? { max_spawn_depth: q.maxSpawnDepth } : {}),
369
+ ...(q.memoryWritesPerWindow !== undefined
370
+ ? {
371
+ memory_writes_per_window: [
372
+ q.memoryWritesPerWindow.maxWrites,
373
+ q.memoryWritesPerWindow.windowMs,
374
+ ],
375
+ }
376
+ : {}),
377
+ },
378
+ });
379
+ }
380
+ if (this.opts.memoryPolicy) {
381
+ const m = this.opts.memoryPolicy;
382
+ kernelApply(runtime, this.pendingObservations, {
383
+ kind: "set_memory_policy",
384
+ ...(m.memoryPath !== undefined ? { memory_path: m.memoryPath } : {}),
385
+ ...(m.staleWarningDays !== undefined ? { stale_warning_days: m.staleWarningDays } : {}),
386
+ ...(m.retrievalTopK !== undefined ? { retrieval_top_k: m.retrievalTopK } : {}),
387
+ ...(m.validationEnabled !== undefined ? { validation_enabled: m.validationEnabled } : {}),
388
+ ...(m.maxContentBytes !== undefined ? { max_content_bytes: m.maxContentBytes } : {}),
389
+ ...(m.maxNameLength !== undefined ? { max_name_length: m.maxNameLength } : {}),
390
+ });
391
+ }
339
392
  let action = resumeMidRun
340
393
  ? kernelAction(runtime, this.pendingObservations, { kind: "resume" })
341
394
  : kernelAction(runtime, this.pendingObservations, startPayload);
@@ -653,6 +706,68 @@ export class RuntimeRunner {
653
706
  });
654
707
  return result;
655
708
  }
709
+ /**
710
+ * W0-ABI: run a declarative workflow DAG. The kernel owns the DAG and gates every node spawn
711
+ * through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
712
+ * feeds their results back, and loops until the kernel reports the workflow complete.
713
+ */
714
+ async runWorkflow(spec, opts) {
715
+ if (!this.activeKernel || !this.currentSessionId) {
716
+ throw new Error("runWorkflow requires an active parent run");
717
+ }
718
+ const parentSessionId = this.currentSessionId;
719
+ const runtime = this.activeKernel;
720
+ const orchestrator = this.opts.subAgentOrchestrator ?? defaultSubAgentOrchestrator;
721
+ let observations = kernelApply(runtime, this.pendingObservations, {
722
+ kind: "load_workflow",
723
+ spec: workflowSpecToKernel(spec),
724
+ parent_session_id: parentSessionId,
725
+ // W0-ABI resume: skip nodes already completed before an interruption.
726
+ ...(opts?.resumedCompleted && opts.resumedCompleted.length ? { resumed_completed: opts.resumedCompleted } : {}),
727
+ });
728
+ for (;;) {
729
+ const done = observations.find(o => o.kind === "workflow_completed");
730
+ if (done)
731
+ return { completed: done.completed ?? [], failed: done.failed ?? [] };
732
+ const batch = observations.find(o => o.kind === "workflow_batch_spawned");
733
+ const nodes = batch?.nodes ?? [];
734
+ if (nodes.length === 0)
735
+ return { completed: [], failed: [] };
736
+ const results = await Promise.all(nodes.map(node => orchestrator.run({
737
+ parentOpts: this.opts,
738
+ parentSessionId,
739
+ spec: workflowNodeToSpec(node, parentSessionId),
740
+ manifest: workflowNodeToManifest(node, parentSessionId),
741
+ sessionLog: this.opts.sessionLog,
742
+ })));
743
+ observations = [];
744
+ for (const result of results) {
745
+ observations = kernelApply(runtime, this.pendingObservations, {
746
+ kind: "sub_agent_completed",
747
+ result: subAgentResultToKernel(result),
748
+ });
749
+ // Persist node completion for resume recovery.
750
+ await this.opts.sessionLog.append(parentSessionId, buildWorkflowNodeCompletedEvent({
751
+ turn: runtime.turn(),
752
+ agentId: result.agentId,
753
+ termination: result.result.termination,
754
+ }));
755
+ }
756
+ }
757
+ }
758
+ /**
759
+ * Resume a workflow from the parent session's completed nodes.
760
+ * Reads the session log, extracts completed workflow node agent_ids, and
761
+ * calls runWorkflow with resumedCompleted so the kernel skips those nodes.
762
+ */
763
+ async resumeWorkflow(spec) {
764
+ if (!this.currentSessionId) {
765
+ throw new Error("resumeWorkflow requires an active parent run");
766
+ }
767
+ const events = await this.opts.sessionLog.read(this.currentSessionId);
768
+ const resumedCompleted = recoverCompletedWorkflowNodes(events);
769
+ return this.runWorkflow(spec, { resumedCompleted });
770
+ }
656
771
  async appendObservations(sessionId, runtime, nextArchiveStart) {
657
772
  const turn = runtime.turn();
658
773
  const preservedRefs = runtime.preservedRefs();
@@ -225,6 +225,35 @@ export type SessionEvent = {
225
225
  query_context: string;
226
226
  requested_k: number;
227
227
  requires_async_response: boolean;
228
+ } | {
229
+ kind: "memory_validation_failed";
230
+ turn: number;
231
+ category?: KernelEventCategory;
232
+ primitive?: KernelPrimitive;
233
+ memory_id: string;
234
+ error: string;
235
+ } | {
236
+ kind: "workflow_node_completed";
237
+ turn: number;
238
+ category?: KernelEventCategory;
239
+ primitive?: KernelPrimitive;
240
+ agent_id: string;
241
+ termination: string;
242
+ } | {
243
+ kind: "workflow_batch_spawned";
244
+ turn: number;
245
+ category?: KernelEventCategory;
246
+ primitive?: KernelPrimitive;
247
+ node_count: number;
248
+ node_ids: string[];
249
+ } | {
250
+ kind: "workflow_completed";
251
+ turn: number;
252
+ category?: KernelEventCategory;
253
+ primitive?: KernelPrimitive;
254
+ completed: string[];
255
+ failed: string[];
256
+ total_nodes: number;
228
257
  } | {
229
258
  kind: "run_terminal";
230
259
  reason: string;
@@ -1,8 +1,13 @@
1
1
  import type { ProviderReplay, ToolCall } from "../types.js";
2
2
  import type { SessionEvent } from "./session-log.js";
3
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;
4
+ /**
5
+ * Normalize a persisted llm_completed event for recovery. Content is sanitized
6
+ * and token_count backfilled, but the stored `provider_replay` envelope is
7
+ * passed through verbatim — this layer is provider-neutral and never
8
+ * synthesizes protocol-specific replay shapes. Legacy reconstruction is the
9
+ * responsibility of the target provider's `seedProviderReplay`.
10
+ */
6
11
  export declare function normalizeLlmCompleted(event: Extract<SessionEvent, {
7
12
  kind: "llm_completed";
8
13
  }>, maxBytes?: number): Extract<SessionEvent, {
@@ -31,3 +36,19 @@ export declare function buildRunTerminalEvent(input: {
31
36
  }): Extract<SessionEvent, {
32
37
  kind: "run_terminal";
33
38
  }>;
39
+ export declare function buildWorkflowNodeCompletedEvent(input: {
40
+ turn: number;
41
+ agentId: string;
42
+ termination: string;
43
+ }): Extract<SessionEvent, {
44
+ kind: "workflow_node_completed";
45
+ }>;
46
+ /**
47
+ * Recover completed workflow node agent_ids from a session event stream.
48
+ * Scans for workflow_node_completed events and returns the agent_ids whose
49
+ * termination was "completed". Used to rebuild resumedCompleted for resumeWorkflow.
50
+ */
51
+ export declare function recoverCompletedWorkflowNodes(events: Array<{
52
+ seq: number;
53
+ event: SessionEvent;
54
+ }>): string[];
@@ -3,40 +3,17 @@ export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay
3
3
  function estimateTokenCount(text) {
4
4
  return Math.max(1, Math.ceil(text.length / 4));
5
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
- }
6
+ /**
7
+ * Normalize a persisted llm_completed event for recovery. Content is sanitized
8
+ * and token_count backfilled, but the stored `provider_replay` envelope is
9
+ * passed through verbatim — this layer is provider-neutral and never
10
+ * synthesizes protocol-specific replay shapes. Legacy reconstruction is the
11
+ * responsibility of the target provider's `seedProviderReplay`.
12
+ */
36
13
  export function normalizeLlmCompleted(event, maxBytes) {
37
14
  const content = sanitizeReplayText(event.content ?? "", maxBytes);
38
15
  const toolCalls = event.tool_calls ?? [];
39
- const providerReplay = effectiveProviderReplay(content, toolCalls, event.provider_replay);
16
+ const providerReplay = event.provider_replay;
40
17
  return {
41
18
  kind: "llm_completed",
42
19
  turn: event.turn,
@@ -71,3 +48,25 @@ export function buildRunTerminalEvent(input) {
71
48
  total_tokens: Math.max(0, input.totalTokens),
72
49
  };
73
50
  }
51
+ export function buildWorkflowNodeCompletedEvent(input) {
52
+ return {
53
+ kind: "workflow_node_completed",
54
+ turn: input.turn,
55
+ agent_id: input.agentId,
56
+ termination: input.termination,
57
+ };
58
+ }
59
+ /**
60
+ * Recover completed workflow node agent_ids from a session event stream.
61
+ * Scans for workflow_node_completed events and returns the agent_ids whose
62
+ * termination was "completed". Used to rebuild resumedCompleted for resumeWorkflow.
63
+ */
64
+ export function recoverCompletedWorkflowNodes(events) {
65
+ const completed = [];
66
+ for (const { event } of events) {
67
+ if (event.kind === "workflow_node_completed" && event.termination === "completed") {
68
+ completed.push(event.agent_id);
69
+ }
70
+ }
71
+ return completed;
72
+ }
@@ -75,3 +75,47 @@ export declare function milestoneCheckResultToKernel(result: MilestoneCheckResul
75
75
  export declare function subAgentResultToKernel(result: SubAgentResult): Record<string, unknown>;
76
76
  export declare function milestoneCheckPass(phaseId: string): MilestoneCheckResult;
77
77
  export declare function milestoneCheckFail(phaseId: string, reason: string): MilestoneCheckResult;
78
+ /** A task for a workflow node: a full object, or a bare goal string. */
79
+ export type WorkflowTaskSpec = {
80
+ goal: string;
81
+ criteria?: string[];
82
+ lane?: string;
83
+ } | string;
84
+ /** One node in a declarative workflow DAG (camelCase host shape). */
85
+ export interface WorkflowNodeSpec {
86
+ task: WorkflowTaskSpec;
87
+ role: KernelAgentRole;
88
+ isolation?: AgentIsolation;
89
+ contextInheritance?: ContextInheritance;
90
+ modelHint?: string;
91
+ /** Indices of nodes this node depends on. */
92
+ dependsOn?: number[];
93
+ }
94
+ /** A declarative workflow DAG the kernel runs node-by-node, gating each spawn. */
95
+ export interface WorkflowSpec {
96
+ nodes: WorkflowNodeSpec[];
97
+ }
98
+ /** Per-node spawn descriptor carried in the `workflow_batch_spawned` observation. */
99
+ export interface WorkflowSpawnInfo {
100
+ agent_id: string;
101
+ goal: string;
102
+ role: string;
103
+ isolation: string;
104
+ context_inheritance: string;
105
+ model_hint?: string;
106
+ }
107
+ /** Map a host `WorkflowSpec` to the snake_case kernel JSON (`load_workflow.spec`). */
108
+ export declare function workflowSpecToKernel(spec: WorkflowSpec): Record<string, unknown>;
109
+ /** Build a sub-agent run spec for a kernel-generated workflow node. */
110
+ export declare function workflowNodeToSpec(node: WorkflowSpawnInfo, parentSessionId: string): AgentRunSpec;
111
+ /** Build the host manifest for a kernel-generated workflow node. */
112
+ export declare function workflowNodeToManifest(node: WorkflowSpawnInfo, parentSessionId: string): AgentProcessChangedObservation;
113
+ /** N parallel read-only Explore workers feeding a single Plan synthesizer (barrier). */
114
+ export declare function fanoutSynthesize(workers: WorkflowTaskSpec[], synthesize: WorkflowTaskSpec): WorkflowSpec;
115
+ /** N parallel Implement generators feeding a single Verify filter/dedupe step (barrier). */
116
+ export declare function generateAndFilter(generators: WorkflowTaskSpec[], filter: WorkflowTaskSpec): WorkflowSpec;
117
+ /**
118
+ * One fresh-context verifier per rule/claim (parallel) + optional skeptic that depends on all and
119
+ * re-checks flags. Verifiers run read-only with no inherited author context (bias-resistant).
120
+ */
121
+ export declare function verifyRules(rules: WorkflowTaskSpec[], skeptic?: WorkflowTaskSpec): WorkflowSpec;
@@ -94,3 +94,111 @@ export function milestoneCheckPass(phaseId) {
94
94
  export function milestoneCheckFail(phaseId, reason) {
95
95
  return { phaseId, passed: false, reason };
96
96
  }
97
+ /** Map a host `WorkflowSpec` to the snake_case kernel JSON (`load_workflow.spec`). */
98
+ export function workflowSpecToKernel(spec) {
99
+ return {
100
+ nodes: spec.nodes.map(n => {
101
+ const task = typeof n.task === "string" ? { goal: n.task } : n.task;
102
+ return {
103
+ task: {
104
+ goal: task.goal,
105
+ // `criteria` is required by the kernel's RuntimeTask serde (no default).
106
+ criteria: task.criteria ?? [],
107
+ ...(task.lane ? { lane: task.lane } : {}),
108
+ },
109
+ role: n.role,
110
+ isolation: n.isolation ?? "shared",
111
+ context_inheritance: n.contextInheritance ?? "none",
112
+ ...(n.modelHint ? { model_hint: n.modelHint } : {}),
113
+ ...(n.dependsOn && n.dependsOn.length ? { depends_on: n.dependsOn } : {}),
114
+ };
115
+ }),
116
+ };
117
+ }
118
+ /** Build a sub-agent run spec for a kernel-generated workflow node. */
119
+ export function workflowNodeToSpec(node, parentSessionId) {
120
+ return {
121
+ identity: {
122
+ agentId: node.agent_id,
123
+ sessionId: `${parentSessionId}-${node.agent_id}`,
124
+ isSubAgent: true,
125
+ parentSessionId,
126
+ },
127
+ role: node.role,
128
+ isolation: node.isolation,
129
+ goal: node.goal,
130
+ };
131
+ }
132
+ /** Build the host manifest for a kernel-generated workflow node. */
133
+ export function workflowNodeToManifest(node, parentSessionId) {
134
+ return {
135
+ kind: "agent_process_changed",
136
+ agent_id: node.agent_id,
137
+ parent_session_id: parentSessionId,
138
+ role: node.role,
139
+ isolation: node.isolation,
140
+ context_inheritance: node.context_inheritance,
141
+ };
142
+ }
143
+ // ─── W1/W2 workflow templates (the six patterns as one-liners) ───
144
+ // Roles carry the kernel's role_defaults isolation/inheritance so host-built specs match the
145
+ // core `orchestration::workflow` constructors (e.g. verifiers stay bias-resistant).
146
+ function asTask(t) {
147
+ return typeof t === "string" ? { goal: t } : t;
148
+ }
149
+ /** N parallel read-only Explore workers feeding a single Plan synthesizer (barrier). */
150
+ export function fanoutSynthesize(workers, synthesize) {
151
+ const nodes = workers.map(t => ({
152
+ task: asTask(t),
153
+ role: "explore",
154
+ isolation: "read_only",
155
+ contextInheritance: "system_only",
156
+ }));
157
+ nodes.push({
158
+ task: asTask(synthesize),
159
+ role: "plan",
160
+ isolation: "shared",
161
+ contextInheritance: "full",
162
+ dependsOn: workers.map((_, i) => i),
163
+ });
164
+ return { nodes };
165
+ }
166
+ /** N parallel Implement generators feeding a single Verify filter/dedupe step (barrier). */
167
+ export function generateAndFilter(generators, filter) {
168
+ const nodes = generators.map(t => ({
169
+ task: asTask(t),
170
+ role: "implement",
171
+ isolation: "worktree",
172
+ contextInheritance: "full",
173
+ }));
174
+ nodes.push({
175
+ task: asTask(filter),
176
+ role: "verify",
177
+ isolation: "read_only",
178
+ contextInheritance: "none",
179
+ dependsOn: generators.map((_, i) => i),
180
+ });
181
+ return { nodes };
182
+ }
183
+ /**
184
+ * One fresh-context verifier per rule/claim (parallel) + optional skeptic that depends on all and
185
+ * re-checks flags. Verifiers run read-only with no inherited author context (bias-resistant).
186
+ */
187
+ export function verifyRules(rules, skeptic) {
188
+ const nodes = rules.map(t => ({
189
+ task: asTask(t),
190
+ role: "verify",
191
+ isolation: "read_only",
192
+ contextInheritance: "none",
193
+ }));
194
+ if (skeptic !== undefined) {
195
+ nodes.push({
196
+ task: asTask(skeptic),
197
+ role: "verify",
198
+ isolation: "read_only",
199
+ contextInheritance: "none",
200
+ dependsOn: rules.map((_, i) => i),
201
+ });
202
+ }
203
+ return { nodes };
204
+ }
package/dist/types.d.ts CHANGED
@@ -112,9 +112,31 @@ export interface ToolArgumentRepairedEvent extends StreamEvent {
112
112
  * The framework creates and threads this object; providers may read/write it.
113
113
  */
114
114
  export type ProviderRunState = Record<string, unknown>;
115
+ export type ProviderProtocol = "anthropic-messages" | "openai-chat" | "openai-responses" | "gemini";
116
+ export interface ProviderDescriptor {
117
+ provider: string;
118
+ protocol: ProviderProtocol;
119
+ model: string;
120
+ reasoning: {
121
+ supported: boolean;
122
+ preserveAcrossToolTurns: boolean;
123
+ requiresReplayForToolTurns?: boolean;
124
+ };
125
+ toolCalls: {
126
+ supported: boolean;
127
+ requiresStrictPairing: boolean;
128
+ };
129
+ }
115
130
  export interface ProviderReplay {
131
+ schema_version?: 1 | 2;
132
+ provider?: string;
133
+ protocol?: ProviderProtocol;
134
+ model?: string;
116
135
  native_blocks?: Array<Record<string, unknown>>;
117
136
  reasoning_content?: string;
137
+ reasoning_details?: unknown;
138
+ native_message?: unknown;
139
+ tool_calls?: unknown[];
118
140
  }
119
141
  export interface LLMProvider {
120
142
  createRunState?(): ProviderRunState;
@@ -122,6 +144,7 @@ export interface LLMProvider {
122
144
  maxTurns?: number;
123
145
  timeoutMs?: number;
124
146
  };
147
+ descriptor?(): ProviderDescriptor;
125
148
  peekProviderReplay?(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
126
149
  seedProviderReplay?(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
127
150
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstrike/wasm",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
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.2.5"
18
+ "@deepstrike/wasm-kernel": "0.2.7"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/jest": "^30.0.0",