@deepstrike/wasm 0.2.16 → 0.2.18

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.
@@ -30,7 +30,6 @@ export class HarnessLoop {
30
30
  }
31
31
  async *runStreaming(request) {
32
32
  const kernel = await import("@deepstrike/wasm-kernel");
33
- const pipeline = new kernel.EvalPipeline({ extractSkillOnPass: true });
34
33
  const criteria = request.criteria ?? [];
35
34
  let currentGoal = request.goal;
36
35
  let lastIterations = 0;
@@ -62,26 +61,23 @@ export class HarnessLoop {
62
61
  }
63
62
  }
64
63
  yield { type: "supervising" };
65
- const evalAction = pipeline.feedOutcome(request.goal, criteria, lastResult, attempt);
66
- if (evalAction.kind !== "evaluate")
67
- break;
64
+ // #6 (0.5.0): eval/verdict compute is the kernel's stateless free functions (was EvalPipeline).
65
+ const evalMsgs = kernel.buildEvalMessages(request.goal, criteria, lastResult, attempt, true);
68
66
  let evalText = "";
69
67
  const evalContext = {
70
68
  systemText: "",
71
- turns: (evalAction.messages ?? []),
69
+ turns: evalMsgs,
72
70
  };
73
71
  for await (const evt of this.evalProvider.stream(evalContext, [], undefined)) {
74
72
  if (evt.type === "text_delta")
75
73
  evalText += evt.delta;
76
74
  }
77
- const doneAction = pipeline.feedEvalResult(evalText);
78
- if (doneAction.kind !== "done")
79
- break;
75
+ const parsed = kernel.parseVerdict(evalText);
80
76
  const verdict = {
81
- passed: doneAction.passed ?? false,
82
- overallScore: doneAction.overallScore ?? 0,
83
- feedback: doneAction.feedback ?? "",
84
- details: (doneAction.details ?? []),
77
+ passed: parsed.passed,
78
+ overallScore: parsed.overallScore,
79
+ feedback: parsed.feedback,
80
+ details: (parsed.details ?? []),
85
81
  };
86
82
  if (verdict.passed) {
87
83
  yield { type: "done", verdict, iterations: lastIterations, totalTokens: lastTotalTokens, status: lastStatus };
@@ -90,7 +86,6 @@ export class HarnessLoop {
90
86
  yield { type: "revising", verdict };
91
87
  currentGoal = `${request.goal}\n\n[Attempt ${attempt} feedback: ${verdict.feedback}]`;
92
88
  lastResult = "";
93
- pipeline.reset();
94
89
  }
95
90
  yield { type: "max_attempts_reached" };
96
91
  }
package/dist/index.d.ts CHANGED
@@ -4,7 +4,8 @@ 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
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, workflowNodeSpecToKernel, submitWorkflowNodesToKernel, submitWorkflowNodesTool, fanoutSynthesize, generateAndFilter, verifyRules } from "./runtime/types/agent.js";
7
+ export { workflowSpecToKernel, workflowNodeSpecToKernel, submitWorkflowNodesToKernel, submitWorkflowToKernel, submitWorkflowNodesTool, startWorkflowTool, fanoutSynthesize, generateAndFilter, verifyRules } from "./runtime/types/agent.js";
8
+ export { loopInstruction, classifyInstruction, judgeGoal, extractLoopContinue, extractClassifyBranch, extractJudgeWinner, } from "./runtime/workflow-control-flow.js";
8
9
  export { Governance } from "./governance.js";
9
10
  export type { GovernanceVerdict } from "./governance.js";
10
11
  export { AnthropicProvider } from "./providers/anthropic.js";
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
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, workflowNodeSpecToKernel, submitWorkflowNodesToKernel, submitWorkflowNodesTool, fanoutSynthesize, generateAndFilter, verifyRules } from "./runtime/types/agent.js";
4
+ export { workflowSpecToKernel, workflowNodeSpecToKernel, submitWorkflowNodesToKernel, submitWorkflowToKernel, submitWorkflowNodesTool, startWorkflowTool, fanoutSynthesize, generateAndFilter, verifyRules } from "./runtime/types/agent.js";
5
+ export { loopInstruction, classifyInstruction, judgeGoal, extractLoopContinue, extractClassifyBranch, extractJudgeWinner, } from "./runtime/workflow-control-flow.js";
5
6
  export { Governance } from "./governance.js";
6
7
  export { AnthropicProvider } from "./providers/anthropic.js";
7
8
  export { OpenAIProvider, QwenProvider, DeepSeekProvider, MiniMaxProvider, KimiProvider } from "./providers/openai.js";
@@ -9,6 +9,6 @@ export declare class AnthropicProvider implements LLMProvider {
9
9
  peekProviderReplay(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
10
10
  seedProviderReplay(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
11
11
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
12
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
12
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: unknown, signal?: AbortSignal): AsyncIterable<StreamEvent>;
13
13
  private rememberNativeBlocks;
14
14
  }
@@ -109,7 +109,7 @@ export class AnthropicProvider {
109
109
  async complete(context, tools, extensions) {
110
110
  return collectStreamMessage(this.stream(context, tools, extensions));
111
111
  }
112
- async *stream(context, tools, extensions) {
112
+ async *stream(context, tools, extensions, _state, signal) {
113
113
  const systemBlocks = [];
114
114
  if (context.systemStable) {
115
115
  systemBlocks.push({ type: "text", text: context.systemStable, cache_control: { type: "ephemeral" } });
@@ -150,6 +150,7 @@ export class AnthropicProvider {
150
150
  "anthropic-beta": "prompt-caching-2024-07-31",
151
151
  },
152
152
  body: JSON.stringify(body),
153
+ ...(signal ? { signal } : {}), // #2-B-ii: a preempt aborts the in-flight request at the socket.
153
154
  });
154
155
  if (!resp.ok)
155
156
  throw new Error(`Anthropic ${resp.status}: ${await resp.text()}`);
@@ -13,20 +13,20 @@ export declare class OpenAIProvider implements LLMProvider {
13
13
  };
14
14
  }[];
15
15
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
16
- protected streamInner(context: RenderedContext, tools: ToolSchema[], extraBody: Record<string, unknown>, exposeReasoning?: boolean): AsyncIterable<StreamEvent>;
17
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
16
+ protected streamInner(context: RenderedContext, tools: ToolSchema[], extraBody: Record<string, unknown>, exposeReasoning?: boolean, signal?: AbortSignal): AsyncIterable<StreamEvent>;
17
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: unknown, signal?: AbortSignal): AsyncIterable<StreamEvent>;
18
18
  }
19
19
  export declare class QwenProvider extends OpenAIProvider {
20
20
  constructor(apiKey: string, model?: string);
21
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
21
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: unknown, signal?: AbortSignal): AsyncIterable<StreamEvent>;
22
22
  }
23
23
  export declare class DeepSeekProvider extends OpenAIProvider {
24
24
  constructor(apiKey: string, model?: string);
25
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
25
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: unknown, signal?: AbortSignal): AsyncIterable<StreamEvent>;
26
26
  }
27
27
  export declare class MiniMaxProvider extends OpenAIProvider {
28
28
  constructor(apiKey: string, model?: string);
29
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
29
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: unknown, signal?: AbortSignal): AsyncIterable<StreamEvent>;
30
30
  }
31
31
  export declare class KimiProvider extends OpenAIProvider {
32
32
  constructor(apiKey: string, model?: string);
@@ -17,7 +17,7 @@ export class OpenAIProvider {
17
17
  async complete(context, tools, extensions) {
18
18
  return collectStreamMessage(this.stream(context, tools, extensions));
19
19
  }
20
- async *streamInner(context, tools, extraBody, exposeReasoning = false) {
20
+ async *streamInner(context, tools, extraBody, exposeReasoning = false, signal) {
21
21
  const body = {
22
22
  model: this.model,
23
23
  messages: toOpenAIMessages(context),
@@ -29,6 +29,7 @@ export class OpenAIProvider {
29
29
  method: "POST",
30
30
  headers: { "Authorization": `Bearer ${this.apiKey}`, "Content-Type": "application/json" },
31
31
  body: JSON.stringify(body),
32
+ ...(signal ? { signal } : {}), // #2-B-ii: a preempt aborts the in-flight request at the socket.
32
33
  });
33
34
  if (!resp.ok)
34
35
  throw new Error(`OpenAI ${resp.status}: ${await resp.text()}`);
@@ -79,16 +80,16 @@ export class OpenAIProvider {
79
80
  yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
80
81
  }
81
82
  }
82
- async *stream(context, tools, extensions) {
83
+ async *stream(context, tools, extensions, _state, signal) {
83
84
  const { expose_reasoning: _, exposeReasoning: __, ...passthrough } = extensions ?? {};
84
- yield* this.streamInner(context, tools, passthrough);
85
+ yield* this.streamInner(context, tools, passthrough, false, signal);
85
86
  }
86
87
  }
87
88
  export class QwenProvider extends OpenAIProvider {
88
89
  constructor(apiKey, model = "qwen-max") {
89
90
  super(apiKey, model, "https://dashscope.aliyuncs.com/compatible-mode/v1");
90
91
  }
91
- async *stream(context, tools, extensions) {
92
+ async *stream(context, tools, extensions, _state, signal) {
92
93
  const enableThinking = Boolean(extensions?.enableThinking);
93
94
  const thinkingBudget = extensions?.thinkingBudget;
94
95
  const { enableThinking: _, thinkingBudget: __, expose_reasoning: ___, exposeReasoning: ____, ...passthrough } = extensions ?? {};
@@ -96,31 +97,31 @@ export class QwenProvider extends OpenAIProvider {
96
97
  ...passthrough,
97
98
  ...(enableThinking ? { enable_thinking: true, ...(thinkingBudget ? { thinking_budget: thinkingBudget } : {}) } : {}),
98
99
  };
99
- yield* this.streamInner(context, tools, extra, enableThinking);
100
+ yield* this.streamInner(context, tools, extra, enableThinking, signal);
100
101
  }
101
102
  }
102
103
  export class DeepSeekProvider extends OpenAIProvider {
103
104
  constructor(apiKey, model = "deepseek-chat") {
104
105
  super(apiKey, model, "https://api.deepseek.com/v1");
105
106
  }
106
- async *stream(context, tools, extensions) {
107
+ async *stream(context, tools, extensions, _state, signal) {
107
108
  const exposeReasoning = Boolean(extensions?.exposeReasoning);
108
109
  const isReasoner = DEEPSEEK_REASONERS.has(this.model);
109
110
  const filteredTools = isReasoner ? [] : tools;
110
111
  const { exposeReasoning: _, expose_reasoning: __, ...passthrough } = extensions ?? {};
111
- yield* this.streamInner(context, filteredTools, passthrough, exposeReasoning);
112
+ yield* this.streamInner(context, filteredTools, passthrough, exposeReasoning, signal);
112
113
  }
113
114
  }
114
115
  export class MiniMaxProvider extends OpenAIProvider {
115
116
  constructor(apiKey, model = "MiniMax-Text-01") {
116
117
  super(apiKey, model, "https://api.minimax.chat/v1");
117
118
  }
118
- async *stream(context, tools, extensions) {
119
+ async *stream(context, tools, extensions, _state, signal) {
119
120
  const exposeReasoning = Boolean(extensions?.exposeReasoning);
120
121
  const isReasoner = MINIMAX_REASONERS.has(this.model);
121
122
  const filteredTools = isReasoner ? [] : tools;
122
123
  const { exposeReasoning: _, expose_reasoning: __, ...passthrough } = extensions ?? {};
123
- yield* this.streamInner(context, filteredTools, passthrough, exposeReasoning);
124
+ yield* this.streamInner(context, filteredTools, passthrough, exposeReasoning, signal);
124
125
  }
125
126
  }
126
127
  export class KimiProvider extends OpenAIProvider {
@@ -18,6 +18,9 @@ export interface RunContext {
18
18
  onToolSuspend?: (event: ToolSuspendEvent) => Promise<unknown> | unknown;
19
19
  onPermissionRequest?: (event: PermissionRequestEvent) => Promise<PermissionResponse | boolean> | PermissionResponse | boolean;
20
20
  resultSpool?: LargeResultSpool;
21
+ /** M3/G4: working directory a tool should run in. WASM has no filesystem, so this is carried for
22
+ * tool-ABI parity with Node/Python rather than consumed by a worktree plane. */
23
+ cwd?: string;
21
24
  }
22
25
  export interface ExecutionPlane {
23
26
  register(...tools: RegisteredTool[]): this;
@@ -73,7 +73,9 @@ export class LocalExecutionPlane {
73
73
  }
74
74
  try {
75
75
  const args = JSON.parse(call.arguments || "{}");
76
- const output = await registered.execute(args);
76
+ // M3/G4: pass the run context for tool-ABI parity with Node/Python (`RunContext` is
77
+ // structurally assignable to the tool's `ToolExecContext`).
78
+ const output = await registered.execute(args, ctx);
77
79
  yield { type: "tool_result", callId: call.id, name: call.name, content: String(output), isError: false };
78
80
  }
79
81
  catch (err) {
@@ -42,6 +42,12 @@ export interface MemoryPolicy {
42
42
  }
43
43
  export interface RuntimeOptions {
44
44
  provider: LLMProvider;
45
+ /** M1/G3 intelligence routing: resolve a per-node provider from a workflow node's `modelHint`.
46
+ * Returns undefined ⇒ fall back to `provider`. Without this hook the hint is a no-op. */
47
+ providerFor?: (modelHint: string) => LLMProvider | undefined;
48
+ /** M4/G5: cumulative token cap for this run (the kernel's `max_total_tokens`); a node's `tokenBudget`
49
+ * flows here for its child run. Undefined ⇒ the kernel default. */
50
+ maxTotalTokens?: number;
45
51
  sessionLog: SessionLog;
46
52
  executionPlane: ExecutionPlane;
47
53
  maxTokens: number;
@@ -70,6 +76,10 @@ export interface RuntimeOptions {
70
76
  onToolSuspend?: (event: ToolSuspendEvent) => Promise<unknown> | unknown;
71
77
  onPermissionRequest?: (event: PermissionRequestEvent) => Promise<PermissionResponse | boolean> | PermissionResponse | boolean;
72
78
  subAgentOrchestrator?: SubAgentOrchestrator;
79
+ /** M5 v2.1: marks this runner as a workflow node (child of the workflow driver). A workflow node's
80
+ * `start_workflow` FLATTENS to the parent kernel; a top-level run (unset) AUTO-PIVOTS — bootstraps +
81
+ * drives the authored workflow in its own kernel, then resumes the reason loop with the outcome. */
82
+ isWorkflowNode?: boolean;
73
83
  /** G2: custom reducers for `NodeKind::Reduce` workflow nodes, merged over the built-ins. */
74
84
  reducers?: ReducerRegistry;
75
85
  milestonePolicy?: MilestonePolicy;
@@ -88,11 +98,16 @@ export interface RuntimeOptions {
88
98
  export declare class RuntimeRunner {
89
99
  private readonly opts;
90
100
  private interrupted;
101
+ /** #2-B-ii: aborts the in-flight provider stream on interrupt/preempt. Recreated per `execute`. */
102
+ private abortController;
91
103
  private pendingObservations;
92
104
  private activeKernel;
93
105
  private currentSessionId;
94
106
  private nextArchiveStart;
95
107
  private localPageOutCache;
108
+ /** M5 v2.1: sub-workflow specs a top-level agent authored via `start_workflow`, awaiting auto-drive
109
+ * at the next safe point (after the tool turn resolves, kernel back in Reason). */
110
+ private pendingAuthoredWorkflows;
96
111
  private pendingSpoolOutputs;
97
112
  constructor(opts: RuntimeOptions);
98
113
  get hostOptions(): RuntimeOptions;
@@ -140,7 +155,42 @@ export declare class RuntimeRunner {
140
155
  }): Promise<{
141
156
  completed: string[];
142
157
  failed: string[];
158
+ outputs: Record<string, string>;
143
159
  }>;
160
+ /**
161
+ * M5/G1: bootstrap an **agent-authored** workflow ("the model writes its own harness"). Routes the
162
+ * spec through the agent-reachable `Syscall::LoadWorkflow` (`submit_workflow`): with no workflow
163
+ * active the kernel bootstraps the DAG, else it flattens onto the running one (bootstrap-or-flatten —
164
+ * one kernel, one quota). The same shared driver runs the resulting batches.
165
+ */
166
+ bootstrapWorkflow(spec: WorkflowSpec, opts?: {
167
+ submitterAgentId?: string;
168
+ }): Promise<{
169
+ completed: string[];
170
+ failed: string[];
171
+ outputs: Record<string, string>;
172
+ }>;
173
+ /**
174
+ * M5 v2.1: drive the sub-workflow(s) a top-level agent authored via `start_workflow`, at the safe
175
+ * point (tool turn resolved → kernel in Reason). Each runs in THIS kernel (the kernel resumes the
176
+ * reason loop on `workflow_completed`), then the outcome is injected as a user message and a fresh
177
+ * `call_provider` is synthesized from the updated context (the workflow drive consumed its own
178
+ * kernel actions — same re-render pattern as the reactive-compact retry path).
179
+ */
180
+ private driveAuthoredWorkflows;
181
+ /**
182
+ * #2-B-ii: while a workflow batch is in flight, poll the signal source; a Critical `InterruptNow`
183
+ * routes through the kernel (root in `SubAgentAwait` → preempt → `AgentPreempted` + tears the
184
+ * `WorkflowRun` down), and we abort the matching children's in-flight LLM calls. Returns the
185
+ * torn-down outcome on preemption, else `null`. No-op without a signal source.
186
+ */
187
+ private monitorWorkflowPreemption;
188
+ /**
189
+ * Shared workflow driver for `runWorkflow` (host `load_workflow`) and `bootstrapWorkflow` (agent
190
+ * `submit_workflow`): run each kernel-emitted batch in parallel, feed completions back (appending any
191
+ * agent-submitted nodes first), and loop until the kernel reports the workflow complete.
192
+ */
193
+ private driveWorkflow;
144
194
  /**
145
195
  * Resume a workflow from the parent session's completed nodes.
146
196
  * Reads the session log, extracts completed workflow node agent_ids, and