@deepstrike/sdk 0.2.15 → 0.2.17

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/dist/index.d.ts CHANGED
@@ -2,6 +2,10 @@ export { RuntimeRunner, collectText } from "./runtime/runner.js";
2
2
  export type { RuntimeOptions, SchedulerBudget } from "./runtime/runner.js";
3
3
  export { builtinReducers, resolveReducer } from "./runtime/reducers.js";
4
4
  export type { Reducer, ReducerRegistry, ReducerInput } from "./runtime/reducers.js";
5
+ export { loopInstruction, classifyInstruction, judgeGoal, extractLoopContinue, extractClassifyBranch, extractJudgeWinner, } from "./runtime/workflow-control-flow.js";
6
+ export { WorktreeExecutionPlane, GitWorktreeManager } from "./runtime/worktree-plane.js";
7
+ export type { WorktreeManager } from "./runtime/worktree-plane.js";
8
+ export { FileWorkflowStore } from "./runtime/workflow-store.js";
5
9
  export type { MemoryPolicy, MemoryWriteRateLimit, ResourceQuota } from "./kernel.js";
6
10
  export { KernelPrimitivesDashboard } from "./runtime/kernel-primitives-dashboard.js";
7
11
  export { FilteredExecutionPlane } from "./runtime/filtered-plane.js";
@@ -64,7 +68,7 @@ export { SinglePassHarness, EvalLoopHarness, HarnessLoop } from "./harness/harne
64
68
  export type { HarnessRequest, HarnessOutcome, HarnessLoopOptions, QualityGate } from "./harness/harness.js";
65
69
  export type { Message, ToolCall, ToolResult, ToolSchema, ContentPart, TextPart, ImagePart, AudioPart, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolChunk, ToolDeltaEvent, ToolSuspendEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, PermissionResolvedEvent, PermissionResponse, LLMProvider, RetryConfig, TokenUsage, ProviderToolSpec, ProviderRunState, ProviderReplay, RenderedContext, ReplayabilityAssessment, } from "./types.js";
66
70
  export type { AgentCapabilityFilter, AgentIdentity, AgentIsolation, AgentRunSpec, AgentProcessChangedObservation, ContextInheritance, KernelAgentRole, LoopResult, MilestoneCheckResult, MilestoneContract, MilestonePhase, MilestonePolicy, SubAgentResult, TerminationReason, WorkflowSpec, WorkflowNodeSpec, WorkflowTaskSpec, WorkflowSpawnInfo, } from "./types/agent.js";
67
- export { agentIdentitySub, agentRunSpecToKernel, milestoneCheckFail, milestoneCheckPass, milestoneCheckResultToKernel, subAgentResultToKernel, workflowSpecToKernel, workflowNodeSpecToKernel, submitWorkflowNodesToKernel, submitWorkflowNodesTool, fanoutSynthesize, generateAndFilter, verifyRules, } from "./types/agent.js";
71
+ export { agentIdentitySub, agentRunSpecToKernel, milestoneCheckFail, milestoneCheckPass, milestoneCheckResultToKernel, subAgentResultToKernel, workflowSpecToKernel, workflowNodeSpecToKernel, submitWorkflowNodesToKernel, submitWorkflowToKernel, submitWorkflowNodesTool, startWorkflowTool, fanoutSynthesize, generateAndFilter, verifyRules, } from "./types/agent.js";
68
72
  export type { AcceptanceCriterion, VerificationContract, ContractCheckResult, } from "./collaboration/contract.js";
69
73
  export { ContractBuilder, formatContractForSystemPrompt, contractToCriteriaStrings, } from "./collaboration/contract.js";
70
74
  export { AgentPool } from "./collaboration/pool.js";
package/dist/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  // ── Runtime (Layer 1.5) ────────────────────────────────────────────────────
2
2
  export { RuntimeRunner, collectText } from "./runtime/runner.js";
3
3
  export { builtinReducers, resolveReducer } from "./runtime/reducers.js";
4
+ export { loopInstruction, classifyInstruction, judgeGoal, extractLoopContinue, extractClassifyBranch, extractJudgeWinner, } from "./runtime/workflow-control-flow.js";
5
+ export { WorktreeExecutionPlane, GitWorktreeManager } from "./runtime/worktree-plane.js";
6
+ export { FileWorkflowStore } from "./runtime/workflow-store.js";
4
7
  export { KernelPrimitivesDashboard } from "./runtime/kernel-primitives-dashboard.js";
5
8
  export { FilteredExecutionPlane } from "./runtime/filtered-plane.js";
6
9
  export { SubAgentOrchestrator, defaultSubAgentOrchestrator, spawnStandalone } from "./runtime/sub-agent-orchestrator.js";
@@ -43,7 +46,7 @@ export { PermissionManager, PermissionMode } from "./safety/permissions.js";
43
46
  export { Governance, governancePolicyToKernelEvent } from "./governance.js";
44
47
  // ── Harness ────────────────────────────────────────────────────────────────
45
48
  export { SinglePassHarness, EvalLoopHarness, HarnessLoop } from "./harness/harness.js";
46
- export { agentIdentitySub, agentRunSpecToKernel, milestoneCheckFail, milestoneCheckPass, milestoneCheckResultToKernel, subAgentResultToKernel, workflowSpecToKernel, workflowNodeSpecToKernel, submitWorkflowNodesToKernel, submitWorkflowNodesTool, fanoutSynthesize, generateAndFilter, verifyRules, } from "./types/agent.js";
49
+ export { agentIdentitySub, agentRunSpecToKernel, milestoneCheckFail, milestoneCheckPass, milestoneCheckResultToKernel, subAgentResultToKernel, workflowSpecToKernel, workflowNodeSpecToKernel, submitWorkflowNodesToKernel, submitWorkflowToKernel, submitWorkflowNodesTool, startWorkflowTool, fanoutSynthesize, generateAndFilter, verifyRules, } from "./types/agent.js";
47
50
  export { ContractBuilder, formatContractForSystemPrompt, contractToCriteriaStrings, } from "./collaboration/contract.js";
48
51
  export { AgentPool } from "./collaboration/pool.js";
49
52
  export { KERNEL_ROLE_MAP } from "./collaboration/pool.js";
@@ -1,4 +1,4 @@
1
- import type { Message, ProviderDescriptor, ProviderReplay, RenderedContext, ToolSchema, StreamEvent, LLMProvider, RuntimePolicy } from "../types.js";
1
+ import type { Message, ProviderDescriptor, ProviderReplay, ProviderRunState, RenderedContext, ToolSchema, StreamEvent, LLMProvider, RuntimePolicy } from "../types.js";
2
2
  interface AnthropicProviderOptions {
3
3
  baseURL?: string;
4
4
  authMode?: "api-key" | "bearer";
@@ -29,7 +29,7 @@ export declare class AnthropicProvider implements LLMProvider {
29
29
  */
30
30
  private buildTools;
31
31
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
32
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
32
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: ProviderRunState, signal?: AbortSignal): AsyncIterable<StreamEvent>;
33
33
  private requestExtensions;
34
34
  private hasBetas;
35
35
  private createMessage;
@@ -128,7 +128,7 @@ export class AnthropicProvider {
128
128
  }
129
129
  throw lastErr;
130
130
  }
131
- async *stream(context, tools, extensions) {
131
+ async *stream(context, tools, extensions, _state, signal) {
132
132
  const system = this.buildSystem(context);
133
133
  const msgs = this.buildMessages(context);
134
134
  assertCacheBudget(system, tools.length);
@@ -144,7 +144,7 @@ export class AnthropicProvider {
144
144
  ...(system ? { system } : {}),
145
145
  messages: msgs,
146
146
  ...(tools.length ? { tools: this.buildTools(tools, !Array.isArray(system)) } : {}),
147
- }, extensions);
147
+ }, extensions, signal);
148
148
  let uncachedInput = 0;
149
149
  let cacheReadTokens = 0;
150
150
  let cacheCreationTokens = 0;
@@ -228,10 +228,12 @@ export class AnthropicProvider {
228
228
  ? this.client.beta.messages.create(params)
229
229
  : this.client.messages.create(params);
230
230
  }
231
- streamMessage(params, extensions) {
231
+ streamMessage(params, extensions, signal) {
232
+ // #2-B-ii: forward the abort signal as a request option so a preempt cancels the HTTP request.
233
+ const opts = signal ? { signal } : undefined;
232
234
  return (this.hasBetas(extensions)
233
- ? this.client.beta.messages.stream(params)
234
- : this.client.messages.stream(params));
235
+ ? this.client.beta.messages.stream(params, opts)
236
+ : this.client.messages.stream(params, opts));
235
237
  }
236
238
  buildSystem(context) {
237
239
  // B3 note: the system shape is content-driven — 0 blocks (string), 1 block
@@ -1,5 +1,5 @@
1
1
  import OpenAI from "openai";
2
- import type { Message, ProviderDescriptor, ProviderReplay, RenderedContext, ToolSchema, StreamEvent, LLMProvider, RuntimePolicy } from "../types.js";
2
+ import type { Message, ProviderDescriptor, ProviderReplay, ProviderRunState, RenderedContext, ToolSchema, StreamEvent, LLMProvider, RuntimePolicy } from "../types.js";
3
3
  import { CircuitBreaker } from "./base.js";
4
4
  import { OpenAIChatAdapter } from "./openai-chat.js";
5
5
  import type { ReplayabilityAssessment } from "./replay-validator.js";
@@ -30,7 +30,7 @@ export declare class OpenAIChatProvider implements LLMProvider {
30
30
  peekProviderReplay(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
31
31
  seedProviderReplay(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
32
32
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
33
- stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
33
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, _state?: ProviderRunState, signal?: AbortSignal): AsyncIterable<StreamEvent>;
34
34
  /**
35
35
  * Default `prompt_cache_key` derived from the cacheable prefix (system prompt +
36
36
  * tool names) so requests for the same agent config route to the same cache.
@@ -122,7 +122,7 @@ export class OpenAIChatProvider {
122
122
  }
123
123
  throw lastErr;
124
124
  }
125
- async *stream(context, tools, extensions) {
125
+ async *stream(context, tools, extensions, _state, signal) {
126
126
  const msgs = this.buildChatMessages(context, extensions);
127
127
  const toolCallBufs = {};
128
128
  const emittedToolCallIndexes = new Set();
@@ -137,7 +137,8 @@ export class OpenAIChatProvider {
137
137
  ...(tools.length ? { tools: this.chat.buildTools(tools) } : {}),
138
138
  stream: true,
139
139
  stream_options: { include_usage: true },
140
- });
140
+ // #2-B-ii: forward the abort signal so a preempt cancels the in-flight HTTP request.
141
+ }, signal ? { signal } : undefined);
141
142
  let totalTokens = 0;
142
143
  let inputTokens = 0;
143
144
  let outputTokens = 0;
@@ -11,6 +11,11 @@ export interface RunContext {
11
11
  onToolSuspend?: (event: ToolSuspendEvent) => Promise<unknown> | unknown;
12
12
  onPermissionRequest?: (event: PermissionRequestEvent) => Promise<PermissionResponse | boolean> | PermissionResponse | boolean;
13
13
  resultSpool?: LargeResultSpool;
14
+ /** M3/G4 worktree isolation: the working directory a sub-agent's tools should run in (the git
15
+ * worktree created for an `isolation: "worktree"` node). Injected by `WorktreeExecutionPlane`; a
16
+ * cwd-aware execution plane / tool reads it to scope filesystem + subprocess work. Undefined ⇒
17
+ * the plane's own default cwd. */
18
+ cwd?: string;
14
19
  }
15
20
  export interface ExecutionPlane {
16
21
  register(...tools: RegisteredTool[]): this;
@@ -117,7 +117,9 @@ export class LocalExecutionPlane {
117
117
  repairedArguments: JSON.stringify(args),
118
118
  };
119
119
  }
120
- const output = await registered.execute(args);
120
+ // M3/G4: pass the run context (incl. `cwd`) so cwd-aware tools scope their work to the
121
+ // sub-agent's worktree. `RunContext` is structurally assignable to the tool's `ToolExecContext`.
122
+ const output = await registered.execute(args, ctx);
121
123
  if (isAsyncIterable(output)) {
122
124
  let combined = "";
123
125
  const iterator = output[Symbol.asyncIterator]();
@@ -192,12 +192,19 @@ function kernelMessageToSdk(raw) {
192
192
  return message;
193
193
  }
194
194
  function renderedContextToSdk(raw) {
195
- return {
195
+ const rawStateTurn = (raw.state_turn ?? raw.stateTurn);
196
+ const frozenLen = (raw.frozen_prefix_len ?? raw.frozenPrefixLen);
197
+ const ctx = {
196
198
  systemText: String(raw.system_text ?? raw.systemText ?? ""),
197
199
  systemStable: String(raw.system_stable ?? raw.systemStable ?? ""),
198
200
  systemKnowledge: String(raw.system_knowledge ?? raw.systemKnowledge ?? ""),
199
201
  turns: (raw.turns ?? []).map(kernelMessageToSdk),
200
202
  };
203
+ if (rawStateTurn)
204
+ ctx.stateTurn = kernelMessageToSdk(rawStateTurn);
205
+ if (typeof frozenLen === "number")
206
+ ctx.frozenPrefixLen = frozenLen;
207
+ return ctx;
201
208
  }
202
209
  function mapKernelAction(raw) {
203
210
  switch (raw.kind) {
@@ -38,7 +38,7 @@ export class ProcessSandboxPlane extends LocalExecutionPlane {
38
38
  }
39
39
  return env;
40
40
  }
41
- runSubprocess(cmd, argv) {
41
+ runSubprocess(cmd, argv, cwd) {
42
42
  return new Promise(resolve => {
43
43
  const chunks = [];
44
44
  let totalBytes = 0;
@@ -51,7 +51,7 @@ export class ProcessSandboxPlane extends LocalExecutionPlane {
51
51
  resolve({ output, isError });
52
52
  };
53
53
  const child = spawn(cmd, argv, {
54
- cwd: this.sandboxDir,
54
+ cwd,
55
55
  env: this.buildEnv(),
56
56
  stdio: ["ignore", "pipe", "pipe"],
57
57
  });
@@ -83,9 +83,12 @@ export class ProcessSandboxPlane extends LocalExecutionPlane {
83
83
  command: { type: "string", description: "The bash command to execute." },
84
84
  },
85
85
  required: ["command"],
86
- }, async (args) => {
87
- await mkdir(this.sandboxDir, { recursive: true });
88
- const { output, isError } = await this.runSubprocess("bash", ["-c", String(args.command)]);
86
+ }, async (args, ctx) => {
87
+ // M3/G4: run in the sub-agent's worktree when one was injected, else the sandbox dir.
88
+ const cwd = ctx?.cwd ?? this.sandboxDir;
89
+ if (!ctx?.cwd)
90
+ await mkdir(this.sandboxDir, { recursive: true });
91
+ const { output, isError } = await this.runSubprocess("bash", ["-c", String(args.command)], cwd);
89
92
  if (isError && !output.trim())
90
93
  return "Process exited with non-zero status and produced no output.";
91
94
  return output || "(no output)";
@@ -98,9 +101,12 @@ export class ProcessSandboxPlane extends LocalExecutionPlane {
98
101
  code: { type: "string", description: "The JavaScript code to evaluate." },
99
102
  },
100
103
  required: ["code"],
101
- }, async (args) => {
102
- await mkdir(this.sandboxDir, { recursive: true });
103
- const { output, isError } = await this.runSubprocess("node", ["-e", String(args.code)]);
104
+ }, async (args, ctx) => {
105
+ // M3/G4: run in the sub-agent's worktree when one was injected, else the sandbox dir.
106
+ const cwd = ctx?.cwd ?? this.sandboxDir;
107
+ if (!ctx?.cwd)
108
+ await mkdir(this.sandboxDir, { recursive: true });
109
+ const { output, isError } = await this.runSubprocess("node", ["-e", String(args.code)], cwd);
104
110
  if (isError && !output.trim())
105
111
  return "Script exited with non-zero status and produced no output.";
106
112
  return output || "(no output)";
@@ -17,6 +17,19 @@ export interface SchedulerBudget {
17
17
  }
18
18
  export interface RuntimeOptions {
19
19
  provider: LLMProvider;
20
+ /** M4/G5: cumulative token cap for this run (the kernel's `max_total_tokens`). A workflow node's
21
+ * `tokenBudget` flows here for its child run, so an expensive node self-terminates at the cap.
22
+ * Undefined ⇒ the kernel default. */
23
+ maxTotalTokens?: number;
24
+ /** M1/G3 intelligence routing: resolve a per-node provider from a workflow node's `modelHint`
25
+ * (e.g. "opus" / "sonnet" / "haiku"). Returns undefined ⇒ fall back to `provider`. A workflow
26
+ * node carrying `model_hint` runs against the resolved provider; without this hook the hint is a
27
+ * no-op (the kernel still carries it for audit). */
28
+ providerFor?: (modelHint: string) => LLMProvider | undefined;
29
+ /** M3/G4 worktree isolation: when set, an `isolation: "worktree"` sub-agent runs inside a git
30
+ * worktree this manager creates (and removes on completion), injected as `RunContext.cwd`.
31
+ * Undefined ⇒ worktree nodes fall back to the inherited plane (no isolation). */
32
+ worktreeManager?: import("./worktree-plane.js").WorktreeManager;
20
33
  sessionLog: SessionLog;
21
34
  executionPlane: ExecutionPlane;
22
35
  maxTokens: number;
@@ -89,6 +102,13 @@ export interface RuntimeOptions {
89
102
  milestoneContract?: MilestoneContract;
90
103
  /** Custom sub-agent host driver; defaults to SubAgentOrchestrator. */
91
104
  subAgentOrchestrator?: SubAgentOrchestrator;
105
+ /** M5 v2.1: marks this runner as executing AS a workflow node (a child spawned by the workflow
106
+ * driver). A workflow node's `start_workflow` FLATTENS to the parent kernel (emits
107
+ * `workflow_nodes_submitted` for `runWorkflow` to append). A top-level run (this flag unset)
108
+ * instead AUTO-PIVOTS: it bootstraps + drives the authored workflow in its own kernel and resumes
109
+ * the reason loop with the outcome. The orchestrator sets this on workflow-node children so a
110
+ * nested `start_workflow` flattens rather than recursing. */
111
+ isWorkflowNode?: boolean;
92
112
  /**
93
113
  * When set, sub-agents run through a HarnessLoop with this config.
94
114
  * The eval provider evaluates the sub-agent's output against the criteria
@@ -123,6 +143,9 @@ export interface RuntimeOptions {
123
143
  export declare class RuntimeRunner {
124
144
  private readonly opts;
125
145
  private interrupted;
146
+ /** #2-B-ii: aborts the in-flight provider stream when the run is interrupted/preempted. Recreated
147
+ * per `execute`; `interrupt()` fires it so a Critical `InterruptNow` cancels the live LLM call. */
148
+ private abortController;
126
149
  private activeKernel;
127
150
  private pendingObservations;
128
151
  private currentSessionId;
@@ -131,6 +154,9 @@ export declare class RuntimeRunner {
131
154
  private pendingSpoolOutputs;
132
155
  /** Local cache of paged-out/archived messages for priority memory retrieval. */
133
156
  private localPageOutCache;
157
+ /** M5 v2.1: sub-workflow specs a top-level agent authored via `start_workflow`, awaiting auto-drive
158
+ * at the next safe point (after the tool turn resolves, kernel back in Reason — not suspended). */
159
+ private pendingAuthoredWorkflows;
134
160
  private dashboard;
135
161
  constructor(opts: RuntimeOptions);
136
162
  /** Host configuration (for coordinator / sub-agent spawn). */
@@ -191,7 +217,50 @@ export declare class RuntimeRunner {
191
217
  }): Promise<{
192
218
  completed: string[];
193
219
  failed: string[];
220
+ outputs: Record<string, string>;
194
221
  }>;
222
+ /**
223
+ * M5/G1: bootstrap an **agent-authored** workflow ("the model writes its own harness"). Unlike
224
+ * `runWorkflow` (the host fires the privileged `load_workflow`), this routes the spec through the
225
+ * agent-reachable `Syscall::LoadWorkflow` (the `submit_workflow` event): with no workflow active the
226
+ * kernel **bootstraps** the DAG; if one is already active it **flattens** the spec's nodes onto it
227
+ * (bootstrap-or-flatten — one kernel, one quota, never a workflow stack). Gated by the same
228
+ * `max_workflow_nodes` backstop as runtime submission, so an authored harness can't overgrow the run.
229
+ * The resulting batches are driven by the same shared driver as `runWorkflow`.
230
+ */
231
+ bootstrapWorkflow(spec: WorkflowSpec, opts?: {
232
+ submitterAgentId?: string;
233
+ }): Promise<{
234
+ completed: string[];
235
+ failed: string[];
236
+ outputs: Record<string, string>;
237
+ }>;
238
+ /**
239
+ * M5 v2.1: drive the sub-workflow(s) a top-level agent authored via `start_workflow`. Called at the
240
+ * verified-safe point (right after the tool turn resolved to `call_provider` — kernel in Reason, not
241
+ * suspended). For each authored spec: `bootstrapWorkflow` runs it in THIS kernel (the kernel resumes
242
+ * the agent reason loop on `workflow_completed` — `finish_workflow` sets phase=Reason), then the
243
+ * outcome is injected as a user message so the agent's next turn sees the result. Returns a fresh
244
+ * `call_provider` synthesized from the updated context (the workflow drive consumed its own kernel
245
+ * actions, so we re-render — the same pattern as the reactive-compact retry path).
246
+ */
247
+ private driveAuthoredWorkflows;
248
+ /**
249
+ * #2-B-ii: while a workflow batch is in flight, poll the signal source. A Critical `InterruptNow`
250
+ * routes through the kernel (which, with the root suspended in `SubAgentAwait`, preempts — marks the
251
+ * running nodes `UserAbort`, tears the `WorkflowRun` down, emits `AgentPreempted`); we then abort the
252
+ * matching children's in-flight LLM calls (via their per-node `AbortController` → `interrupt()`).
253
+ * Returns the torn-down workflow's outcome on preemption, else `null`. No-op (null) without a signal
254
+ * source. Non-preempting signals (queue/observe/soft-interrupt) are still applied as they arrive.
255
+ */
256
+ private monitorWorkflowPreemption;
257
+ /**
258
+ * Shared workflow driver for `runWorkflow` (host `load_workflow`) and `bootstrapWorkflow` (agent
259
+ * `submit_workflow`): given the observations from the initial load/bootstrap, run each kernel-emitted
260
+ * batch in parallel, feed completions back (appending any agent-submitted nodes first), and loop
261
+ * until the kernel reports the workflow complete. Returns the completed / failed node agent-ids.
262
+ */
263
+ private driveWorkflow;
195
264
  /**
196
265
  * Resume a workflow from the parent session's completed nodes.
197
266
  * Reads the session log, extracts completed workflow node agent_ids, and