@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 +5 -1
- package/dist/index.js +4 -1
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +7 -5
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +3 -2
- package/dist/runtime/execution-plane.d.ts +5 -0
- package/dist/runtime/execution-plane.js +3 -1
- package/dist/runtime/kernel-step.js +8 -1
- package/dist/runtime/process-sandbox-plane.js +14 -8
- package/dist/runtime/runner.d.ts +69 -0
- package/dist/runtime/runner.js +261 -35
- package/dist/runtime/sub-agent-orchestrator.d.ts +11 -0
- package/dist/runtime/sub-agent-orchestrator.js +78 -13
- package/dist/runtime/workflow-control-flow.d.ts +17 -0
- package/dist/runtime/workflow-control-flow.js +78 -0
- package/dist/runtime/workflow-store.d.ts +15 -0
- package/dist/runtime/workflow-store.js +47 -0
- package/dist/runtime/worktree-plane.d.ts +43 -0
- package/dist/runtime/worktree-plane.js +81 -0
- package/dist/tools/index.d.ts +9 -3
- package/dist/tools/index.js +2 -2
- package/dist/types/agent.d.ts +63 -0
- package/dist/types/agent.js +184 -44
- package/dist/types.d.ts +6 -1
- package/package.json +2 -2
package/dist/types/agent.js
CHANGED
|
@@ -85,6 +85,11 @@ export function subAgentResultToKernel(result) {
|
|
|
85
85
|
: null,
|
|
86
86
|
turns_used: result.result.turnsUsed,
|
|
87
87
|
total_tokens_used: result.result.totalTokensUsed,
|
|
88
|
+
// A#2: control-flow signals — additive, omitted on the wire when unset so a plain spawn's
|
|
89
|
+
// result is byte-identical to before. The kernel reads each only for the matching node kind.
|
|
90
|
+
...(result.result.loopContinue !== undefined ? { loop_continue: result.result.loopContinue } : {}),
|
|
91
|
+
...(result.result.classifyBranch !== undefined ? { classify_branch: result.result.classifyBranch } : {}),
|
|
92
|
+
...(result.result.tournamentWinner !== undefined ? { tournament_winner: result.result.tournamentWinner } : {}),
|
|
88
93
|
},
|
|
89
94
|
};
|
|
90
95
|
}
|
|
@@ -107,22 +112,49 @@ export function workflowBudgetNote(budget) {
|
|
|
107
112
|
if (budget.concurrency_remaining != null && budget.max_concurrent_subagents != null) {
|
|
108
113
|
parts.push(`concurrency ${budget.running_subagents}/${budget.max_concurrent_subagents} running, ${budget.concurrency_remaining} free`);
|
|
109
114
|
}
|
|
115
|
+
if (budget.tokens_remaining != null && budget.tokens_max != null) {
|
|
116
|
+
parts.push(`tokens ${budget.tokens_used ?? 0}/${budget.tokens_max} used, ${budget.tokens_remaining} remaining`);
|
|
117
|
+
}
|
|
110
118
|
if (parts.length === 0)
|
|
111
119
|
return "";
|
|
112
120
|
return (`[workflow budget] ${parts.join(" · ")}. ` +
|
|
113
|
-
"If you submit more workflow nodes, keep the batch within the remaining node budget.");
|
|
121
|
+
"If you submit more workflow nodes, keep the batch within the remaining node and token budget.");
|
|
122
|
+
}
|
|
123
|
+
/** Normalize a `WorkflowTaskSpec` (object or bare goal string) to the kernel's `RuntimeTask` JSON. */
|
|
124
|
+
function workflowTaskToKernel(t) {
|
|
125
|
+
const task = typeof t === "string" ? { goal: t } : t;
|
|
126
|
+
return {
|
|
127
|
+
goal: task.goal,
|
|
128
|
+
// `criteria` is required by the kernel's RuntimeTask serde (no default).
|
|
129
|
+
criteria: task.criteria ?? [],
|
|
130
|
+
...(task.lane ? { lane: task.lane } : {}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** Lower a node's control-flow kind to the kernel's serde-tagged `NodeKind` JSON, or `undefined` for
|
|
134
|
+
* a plain spawn. `reducer` / `loop` / `classify` / `tournament` are mutually exclusive — declaring
|
|
135
|
+
* more than one is a spec error (a node has exactly one kind). */
|
|
136
|
+
function nodeKindToKernel(n) {
|
|
137
|
+
const declared = [n.reducer != null, n.loop != null, n.classify != null, n.tournament != null].filter(Boolean).length;
|
|
138
|
+
if (declared > 1) {
|
|
139
|
+
throw new Error("a workflow node may declare at most one of: reducer, loop, classify, tournament");
|
|
140
|
+
}
|
|
141
|
+
if (n.reducer != null)
|
|
142
|
+
return { type: "reduce", reducer: n.reducer };
|
|
143
|
+
if (n.loop != null)
|
|
144
|
+
return { type: "loop", max_iters: n.loop.maxIters };
|
|
145
|
+
if (n.classify != null) {
|
|
146
|
+
return { type: "classify", branches: n.classify.branches.map(b => ({ label: b.label, nodes: b.nodes })) };
|
|
147
|
+
}
|
|
148
|
+
if (n.tournament != null)
|
|
149
|
+
return { type: "tournament", entrants: n.tournament.entrants.map(workflowTaskToKernel) };
|
|
150
|
+
return undefined;
|
|
114
151
|
}
|
|
115
152
|
/** Map one host `WorkflowNodeSpec` to its snake_case kernel JSON. Shared by `load_workflow` (the
|
|
116
153
|
* whole spec) and `submit_workflow_nodes` (R3-1 runtime append) so the two encodings never drift. */
|
|
117
154
|
export function workflowNodeSpecToKernel(n) {
|
|
118
|
-
const
|
|
155
|
+
const kind = nodeKindToKernel(n);
|
|
119
156
|
return {
|
|
120
|
-
task:
|
|
121
|
-
goal: task.goal,
|
|
122
|
-
// `criteria` is required by the kernel's RuntimeTask serde (no default).
|
|
123
|
-
criteria: task.criteria ?? [],
|
|
124
|
-
...(task.lane ? { lane: task.lane } : {}),
|
|
125
|
-
},
|
|
157
|
+
task: workflowTaskToKernel(n.task),
|
|
126
158
|
role: n.role,
|
|
127
159
|
// role/isolation/context_inheritance have no serde default in the kernel — always emit.
|
|
128
160
|
isolation: n.isolation ?? "shared",
|
|
@@ -130,8 +162,10 @@ export function workflowNodeSpecToKernel(n) {
|
|
|
130
162
|
...(n.modelHint ? { model_hint: n.modelHint } : {}),
|
|
131
163
|
...(n.trust && n.trust !== "trusted" ? { trust: n.trust } : {}),
|
|
132
164
|
...(n.outputSchema ? { output_schema: n.outputSchema } : {}),
|
|
133
|
-
// G2:
|
|
134
|
-
...(
|
|
165
|
+
// A#2/G2: loop / classify / tournament / reduce lower to a serde-tagged `NodeKind`; spawn omits it.
|
|
166
|
+
...(kind ? { kind } : {}),
|
|
167
|
+
// M4/G5: per-node token cap (additive; omitted when unset).
|
|
168
|
+
...(n.tokenBudget != null ? { token_budget: n.tokenBudget } : {}),
|
|
135
169
|
...(n.dependsOn && n.dependsOn.length ? { depends_on: n.dependsOn } : {}),
|
|
136
170
|
};
|
|
137
171
|
}
|
|
@@ -149,24 +183,94 @@ export function submitWorkflowNodesToKernel(nodes, submitterAgentId) {
|
|
|
149
183
|
...(submitterAgentId ? { submitter_agent_id: submitterAgentId } : {}),
|
|
150
184
|
};
|
|
151
185
|
}
|
|
152
|
-
/**
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
186
|
+
/** M5/G1: map an agent-authored spec to the `submit_workflow` kernel event body (the agent-reachable
|
|
187
|
+
* `Syscall::LoadWorkflow`). The kernel bootstraps the DAG when none is active, else flattens onto it.
|
|
188
|
+
* `parentSessionId` seeds child session ids on bootstrap; `submitterAgentId` carries G1 trust coercion
|
|
189
|
+
* on the flatten case (a quarantined author's nodes are coerced quarantined). */
|
|
190
|
+
export function submitWorkflowToKernel(spec, parentSessionId, submitterAgentId) {
|
|
191
|
+
return {
|
|
192
|
+
kind: "submit_workflow",
|
|
193
|
+
spec: workflowSpecToKernel(spec),
|
|
194
|
+
parent_session_id: parentSessionId,
|
|
195
|
+
...(submitterAgentId ? { submitter_agent_id: submitterAgentId } : {}),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/** Shared JSON-Schema for a workflow-node batch (a DAG). Used by both `submit_workflow_nodes`
|
|
199
|
+
* (append) and `start_workflow` (M5 v1: author a sub-workflow), so the two tools never drift. */
|
|
200
|
+
const workflowNodesArraySchema = {
|
|
201
|
+
type: "array",
|
|
202
|
+
description: "Workflow nodes (a DAG); each runs as a gated sub-agent. A node may declare ONE control-flow kind " +
|
|
203
|
+
"— `loop` / `classify` / `tournament` / `reducer` — otherwise it is a plain spawn. `dependsOn` and " +
|
|
204
|
+
"`classify.branches[].nodes` are batch-relative (index 0 = this batch's first node).",
|
|
205
|
+
items: {
|
|
160
206
|
type: "object",
|
|
161
207
|
properties: {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
208
|
+
task: {
|
|
209
|
+
description: "The node's goal: a string, or an object { goal, criteria?, lane? }.",
|
|
210
|
+
oneOf: [
|
|
211
|
+
{ type: "string" },
|
|
212
|
+
{
|
|
213
|
+
type: "object",
|
|
214
|
+
properties: {
|
|
215
|
+
goal: { type: "string" },
|
|
216
|
+
criteria: { type: "array", items: { type: "string" } },
|
|
217
|
+
},
|
|
218
|
+
required: ["goal"],
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
role: { type: "string", enum: ["explore", "plan", "implement", "verify", "custom"] },
|
|
223
|
+
isolation: { type: "string", enum: ["shared", "read_only", "worktree", "remote"] },
|
|
224
|
+
contextInheritance: { type: "string", enum: ["none", "system_only", "full"] },
|
|
225
|
+
trust: { type: "string", enum: ["trusted", "quarantined"] },
|
|
226
|
+
outputSchema: {
|
|
227
|
+
type: "object",
|
|
228
|
+
description: "Optional JSON Schema the node's output must conform to (validated + retried SDK-side).",
|
|
229
|
+
},
|
|
230
|
+
modelHint: {
|
|
231
|
+
type: "string",
|
|
232
|
+
description: "Preferred model for this node (e.g. \"opus\"/\"sonnet\"/\"haiku\"); the host routes it.",
|
|
233
|
+
},
|
|
234
|
+
reducer: {
|
|
235
|
+
type: "string",
|
|
236
|
+
description: "Make this a deterministic reduce node (no LLM); names a registered reducer.",
|
|
237
|
+
},
|
|
238
|
+
loop: {
|
|
239
|
+
type: "object",
|
|
240
|
+
description: "Make this a loop node: re-run its agent up to maxIters times, ending early when it reports done.",
|
|
241
|
+
properties: { maxIters: { type: "integer", description: "Hard iteration cap." } },
|
|
242
|
+
required: ["maxIters"],
|
|
243
|
+
},
|
|
244
|
+
classify: {
|
|
245
|
+
type: "object",
|
|
246
|
+
description: "Make this a classify node: its agent picks one branch label; that branch's nodes run, the rest are pruned.",
|
|
247
|
+
properties: {
|
|
248
|
+
branches: {
|
|
249
|
+
type: "array",
|
|
250
|
+
items: {
|
|
251
|
+
type: "object",
|
|
252
|
+
properties: {
|
|
253
|
+
label: { type: "string" },
|
|
254
|
+
nodes: {
|
|
255
|
+
type: "array",
|
|
256
|
+
items: { type: "integer" },
|
|
257
|
+
description: "Batch-relative indices of the nodes to run when this branch is chosen.",
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
required: ["label", "nodes"],
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
required: ["branches"],
|
|
265
|
+
},
|
|
266
|
+
tournament: {
|
|
267
|
+
type: "object",
|
|
268
|
+
description: "Make this a tournament controller: generate each entrant, then pairwise-judge to one winner (this node's task is the criterion).",
|
|
269
|
+
properties: {
|
|
270
|
+
entrants: {
|
|
271
|
+
type: "array",
|
|
272
|
+
description: "≥2 candidate tasks to generate and judge.",
|
|
273
|
+
items: {
|
|
170
274
|
oneOf: [
|
|
171
275
|
{ type: "string" },
|
|
172
276
|
{
|
|
@@ -179,31 +283,63 @@ export const submitWorkflowNodesTool = {
|
|
|
179
283
|
},
|
|
180
284
|
],
|
|
181
285
|
},
|
|
182
|
-
role: { type: "string", enum: ["explore", "plan", "implement", "verify", "custom"] },
|
|
183
|
-
isolation: { type: "string", enum: ["shared", "read_only", "worktree", "remote"] },
|
|
184
|
-
contextInheritance: { type: "string", enum: ["none", "system_only", "full"] },
|
|
185
|
-
trust: { type: "string", enum: ["trusted", "quarantined"] },
|
|
186
|
-
outputSchema: {
|
|
187
|
-
type: "object",
|
|
188
|
-
description: "Optional JSON Schema the node's output must conform to (validated + retried SDK-side).",
|
|
189
|
-
},
|
|
190
|
-
reducer: {
|
|
191
|
-
type: "string",
|
|
192
|
-
description: "Make this a deterministic reduce node (no LLM); names a registered reducer.",
|
|
193
|
-
},
|
|
194
|
-
dependsOn: {
|
|
195
|
-
type: "array",
|
|
196
|
-
items: { type: "integer" },
|
|
197
|
-
description: "Batch-relative, backward-only dependency indices within this submission.",
|
|
198
|
-
},
|
|
199
286
|
},
|
|
200
|
-
required: ["task", "role"],
|
|
201
287
|
},
|
|
288
|
+
required: ["entrants"],
|
|
289
|
+
},
|
|
290
|
+
tokenBudget: {
|
|
291
|
+
type: "integer",
|
|
292
|
+
description: "Cap this node's child run at this many cumulative tokens.",
|
|
293
|
+
},
|
|
294
|
+
dependsOn: {
|
|
295
|
+
type: "array",
|
|
296
|
+
items: { type: "integer" },
|
|
297
|
+
description: "Batch-relative, backward-only dependency indices within this submission.",
|
|
202
298
|
},
|
|
203
299
|
},
|
|
300
|
+
required: ["task", "role"],
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
/** R3-1: the tool a workflow-coordinator node's agent calls to append work to the running DAG
|
|
304
|
+
* (true loop-until-done / dynamic fan-out). Give it to nodes meant to fan out; the runner intercepts
|
|
305
|
+
* the call and routes the nodes to the parent kernel (the child's own kernel holds no workflow). */
|
|
306
|
+
export const submitWorkflowNodesTool = {
|
|
307
|
+
name: "submit_workflow_nodes",
|
|
308
|
+
description: "Append new nodes to the running workflow DAG (dynamic fan-out / loop-until-done). Each node " +
|
|
309
|
+
"spawns as a gated sub-agent. Use when you discover more work that should run as its own node. " +
|
|
310
|
+
"A node may declare ONE control-flow kind — `loop` (re-run until done), `classify` (route to one " +
|
|
311
|
+
"branch), `tournament` (pairwise-judge candidates to a winner), or `reducer` (deterministic, no " +
|
|
312
|
+
"LLM) — otherwise it is a plain spawn. Within a submission, `dependsOn` and `classify.branches[].nodes` " +
|
|
313
|
+
"are batch-relative (index 0 = this batch's first node).",
|
|
314
|
+
parameters: JSON.stringify({
|
|
315
|
+
type: "object",
|
|
316
|
+
properties: { nodes: workflowNodesArraySchema },
|
|
204
317
|
required: ["nodes"],
|
|
205
318
|
}),
|
|
206
319
|
};
|
|
320
|
+
/** M5 v1 (flatten): the tool an agent calls to **author a sub-workflow** — a cohesive DAG of nodes
|
|
321
|
+
* (incl. loop/classify/tournament/reduce) composed onto the running workflow. Mechanically it lowers
|
|
322
|
+
* to the same append path as `submit_workflow_nodes` (a `WorkflowSpec` is a node batch), but reads as
|
|
323
|
+
* "write a harness" rather than "append nodes". v2 adds top-level bootstrap (the `LoadWorkflow`
|
|
324
|
+
* kernel syscall) so a plain run can start a workflow from scratch. */
|
|
325
|
+
export const startWorkflowTool = {
|
|
326
|
+
name: "start_workflow",
|
|
327
|
+
description: "Author and run a sub-workflow: a DAG of nodes (fan-out / classify / tournament / loop / reduce) " +
|
|
328
|
+
"composed onto the current run. Use to structure a multi-step task as its own harness. The nodes " +
|
|
329
|
+
"spawn as gated sub-agents; `dependsOn` / `classify.branches[].nodes` are spec-relative.",
|
|
330
|
+
parameters: JSON.stringify({
|
|
331
|
+
type: "object",
|
|
332
|
+
properties: {
|
|
333
|
+
spec: {
|
|
334
|
+
type: "object",
|
|
335
|
+
description: "The workflow specification.",
|
|
336
|
+
properties: { nodes: workflowNodesArraySchema },
|
|
337
|
+
required: ["nodes"],
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
required: ["spec"],
|
|
341
|
+
}),
|
|
342
|
+
};
|
|
207
343
|
/** Build a sub-agent run spec for a kernel-generated workflow node. */
|
|
208
344
|
export function workflowNodeToSpec(node, parentSessionId) {
|
|
209
345
|
return {
|
|
@@ -216,6 +352,10 @@ export function workflowNodeToSpec(node, parentSessionId) {
|
|
|
216
352
|
role: node.role,
|
|
217
353
|
isolation: node.isolation,
|
|
218
354
|
goal: node.goal,
|
|
355
|
+
// M1/G3: carry the node's model preference so the orchestrator can route to a provider.
|
|
356
|
+
...(node.model_hint ? { modelHint: node.model_hint } : {}),
|
|
357
|
+
// M4/G5: carry the node's token cap so the orchestrator can bound the child run.
|
|
358
|
+
...(node.token_budget != null ? { tokenBudget: node.token_budget } : {}),
|
|
219
359
|
};
|
|
220
360
|
}
|
|
221
361
|
/** Build the host manifest for a kernel-generated workflow node. */
|
package/dist/types.d.ts
CHANGED
|
@@ -304,7 +304,12 @@ export interface LLMProvider {
|
|
|
304
304
|
*/
|
|
305
305
|
assessReplayability?(context: RenderedContext, extensions?: Record<string, unknown>): ReplayabilityAssessment;
|
|
306
306
|
complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
|
|
307
|
-
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, state?: ProviderRunState
|
|
307
|
+
stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, state?: ProviderRunState,
|
|
308
|
+
/** #2-B-ii: when provided, a preempting `InterruptNow` (or `interrupt()`) aborts the in-flight
|
|
309
|
+
* request. SDK-client providers should forward it to the client (`{ signal }`); the runner also
|
|
310
|
+
* breaks the consume loop on abort, so providers that ignore it still stop processing immediately
|
|
311
|
+
* (only the socket lingers). Optional ⇒ backward-compatible; providers may ignore it. */
|
|
312
|
+
signal?: AbortSignal): AsyncIterable<StreamEvent>;
|
|
308
313
|
}
|
|
309
314
|
/**
|
|
310
315
|
* Optional async summarizer called after context compression.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstrike/sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.17",
|
|
4
4
|
"description": "DeepStrike Node.js SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@anthropic-ai/sdk": "^0.99.0",
|
|
23
|
-
"@deepstrike/core": "0.2.
|
|
23
|
+
"@deepstrike/core": "0.2.17",
|
|
24
24
|
"@google/generative-ai": "^0.24.1",
|
|
25
25
|
"openai": "^5.23.2"
|
|
26
26
|
},
|