@deepstrike/wasm 0.2.36 → 0.2.38
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 +2 -2
- package/dist/index.js +1 -1
- package/dist/runtime/facade.js +11 -7
- package/dist/runtime/kernel-event-log.d.ts +0 -6
- package/dist/runtime/kernel-event-log.js +57 -62
- package/dist/runtime/kernel-step.d.ts +17 -0
- package/dist/runtime/kernel-step.js +12 -0
- package/dist/runtime/os-snapshot.d.ts +0 -1
- package/dist/runtime/os-snapshot.js +0 -19
- package/dist/runtime/runner.d.ts +22 -3
- package/dist/runtime/runner.js +192 -25
- package/dist/runtime/session-log.d.ts +30 -54
- package/dist/runtime/session-repair.d.ts +29 -6
- package/dist/runtime/session-repair.js +37 -8
- package/dist/runtime/sub-agent-orchestrator.d.ts +7 -0
- package/dist/runtime/sub-agent-orchestrator.js +33 -3
- package/dist/runtime/types/agent.d.ts +36 -1
- package/dist/runtime/types/agent.js +54 -1
- package/dist/runtime/workflow-control-flow.d.ts +10 -2
- package/dist/runtime/workflow-control-flow.js +27 -6
- package/dist/types.d.ts +34 -1
- package/package.json +2 -2
|
@@ -48,24 +48,38 @@ export function buildRunTerminalEvent(input) {
|
|
|
48
48
|
total_tokens: Math.max(0, input.totalTokens),
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
+
/** Build workflow_node_completed for persistence after a node finishes. W-1: carries the
|
|
52
|
+
* result-borne control signals + output so resume replays control flow and re-seeds outputs. */
|
|
51
53
|
export function buildWorkflowNodeCompletedEvent(input) {
|
|
52
54
|
return {
|
|
53
55
|
kind: "workflow_node_completed",
|
|
54
56
|
turn: input.turn,
|
|
55
57
|
agent_id: input.agentId,
|
|
56
58
|
termination: input.termination,
|
|
59
|
+
...(input.classifyBranch !== undefined ? { classify_branch: input.classifyBranch } : {}),
|
|
60
|
+
...(input.tournamentWinner !== undefined ? { tournament_winner: input.tournamentWinner } : {}),
|
|
61
|
+
...(input.loopContinue !== undefined ? { loop_continue: input.loopContinue } : {}),
|
|
62
|
+
...(input.output ? { output: input.output } : {}),
|
|
57
63
|
};
|
|
58
64
|
}
|
|
59
65
|
/**
|
|
60
|
-
* Recover completed workflow node
|
|
61
|
-
*
|
|
62
|
-
*
|
|
66
|
+
* Recover completed workflow node records from a session event stream. Scans for
|
|
67
|
+
* workflow_node_completed events with termination "completed" and returns them WITH their
|
|
68
|
+
* result-borne control signals (W-1) — resumeWorkflow lowers these to the kernel's
|
|
69
|
+
* `resumed_results` so a classifier re-prunes and a loop stop is honored, and re-seeds the
|
|
70
|
+
* driver's outputs map from the persisted output text.
|
|
63
71
|
*/
|
|
64
72
|
export function recoverCompletedWorkflowNodes(events) {
|
|
65
73
|
const completed = [];
|
|
66
74
|
for (const { event } of events) {
|
|
67
75
|
if (event.kind === "workflow_node_completed" && event.termination === "completed") {
|
|
68
|
-
completed.push(
|
|
76
|
+
completed.push({
|
|
77
|
+
agentId: event.agent_id,
|
|
78
|
+
...(event.classify_branch !== undefined ? { classifyBranch: event.classify_branch } : {}),
|
|
79
|
+
...(event.tournament_winner !== undefined ? { tournamentWinner: event.tournament_winner } : {}),
|
|
80
|
+
...(event.loop_continue !== undefined ? { loopContinue: event.loop_continue } : {}),
|
|
81
|
+
...(event.output !== undefined ? { output: event.output } : {}),
|
|
82
|
+
});
|
|
69
83
|
}
|
|
70
84
|
}
|
|
71
85
|
return completed;
|
|
@@ -73,15 +87,30 @@ export function recoverCompletedWorkflowNodes(events) {
|
|
|
73
87
|
/** R3-1: build workflow_nodes_submitted for persistence after a runtime submission, so resume can
|
|
74
88
|
* re-apply it. `nodes` is the kernel-shape (snake_case) submitted node array. */
|
|
75
89
|
export function buildWorkflowNodesSubmittedEvent(input) {
|
|
76
|
-
return {
|
|
90
|
+
return {
|
|
91
|
+
kind: "workflow_nodes_submitted",
|
|
92
|
+
turn: input.turn,
|
|
93
|
+
nodes: input.nodes,
|
|
94
|
+
...(input.baseIndex !== undefined ? { base_index: input.baseIndex } : {}),
|
|
95
|
+
...(input.submitterAgentId !== undefined ? { submitter_agent_id: input.submitterAgentId } : {}),
|
|
96
|
+
};
|
|
77
97
|
}
|
|
78
98
|
/** R3-1: recover the runtime submission batches (in order) to rebuild `resumed_submissions` for
|
|
79
|
-
* resumeWorkflow, so dynamically-appended nodes are reconstructed.
|
|
99
|
+
* resumeWorkflow, so dynamically-appended nodes are reconstructed.
|
|
100
|
+
* `submitters` is parallel to `submissions` (undefined = host/bootstrap submission). */
|
|
80
101
|
export function recoverSubmittedWorkflowNodes(events) {
|
|
81
102
|
const submissions = [];
|
|
103
|
+
const bases = [];
|
|
104
|
+
const submitters = [];
|
|
82
105
|
for (const { event } of events) {
|
|
83
|
-
if (event.kind === "workflow_nodes_submitted")
|
|
106
|
+
if (event.kind === "workflow_nodes_submitted") {
|
|
84
107
|
submissions.push(event.nodes);
|
|
108
|
+
submitters.push(event.submitter_agent_id);
|
|
109
|
+
// Absent on legacy logs → order-only replay (bases array stays parallel-short only
|
|
110
|
+
// if ALL records carry it; a mixed log degrades to order-only for safety).
|
|
111
|
+
if (event.base_index !== undefined)
|
|
112
|
+
bases.push(event.base_index);
|
|
113
|
+
}
|
|
85
114
|
}
|
|
86
|
-
return submissions;
|
|
115
|
+
return { submissions, bases: bases.length === submissions.length ? bases : [], submitters };
|
|
87
116
|
}
|
|
@@ -10,6 +10,13 @@ export interface SubAgentRunContext {
|
|
|
10
10
|
/** M5 v2.1: set when this child is a workflow node — propagated so a nested `start_workflow`
|
|
11
11
|
* FLATTENS to the parent kernel rather than auto-pivoting into its own bootstrap. */
|
|
12
12
|
isWorkflowNode?: boolean;
|
|
13
|
+
/** W-N1 tool exposure. The kernel omits an EMPTY `permitted_capability_ids` on the wire, so a
|
|
14
|
+
* grant-less workflow node is indistinguishable from a zero-cap spawn at the manifest — the
|
|
15
|
+
* caller states intent instead: `"inherit"` = run on the parent's execution plane with its
|
|
16
|
+
* meta-tool availability (trusted workflow nodes — they carried no grant list by design, and
|
|
17
|
+
* filtering on the missing list ran every DAG node TOOL-LESS); `"filtered"` (default) = filter
|
|
18
|
+
* to the manifest grants, empty ⇒ deny-all (spawn path, quarantined nodes). */
|
|
19
|
+
toolAccess?: "inherit" | "filtered";
|
|
13
20
|
/** #2-B-ii: parent-controlled abort — when the kernel preempts this node (`AgentPreempted`), the
|
|
14
21
|
* orchestrator interrupts the child runner, cancelling its in-flight LLM call. */
|
|
15
22
|
abortSignal?: AbortSignal;
|
|
@@ -49,12 +49,32 @@ function deriveMetaTools(permitted, opts) {
|
|
|
49
49
|
metaTools.add("update_plan");
|
|
50
50
|
return metaTools;
|
|
51
51
|
}
|
|
52
|
+
/** W-N1: meta-tools by source availability alone — a `toolAccess: "inherit"` child gets the same
|
|
53
|
+
* meta surface a top-level run of these options would. */
|
|
54
|
+
function availableMetaTools(opts) {
|
|
55
|
+
const metaTools = new Set();
|
|
56
|
+
if (opts.skillContentMap?.size)
|
|
57
|
+
metaTools.add("skill");
|
|
58
|
+
if (opts.dreamStore)
|
|
59
|
+
metaTools.add("memory");
|
|
60
|
+
if (opts.knowledgeSource)
|
|
61
|
+
metaTools.add("knowledge");
|
|
62
|
+
if (opts.enablePlanTool)
|
|
63
|
+
metaTools.add("update_plan");
|
|
64
|
+
return metaTools;
|
|
65
|
+
}
|
|
52
66
|
/** Host-side driver for kernel-isolated sub-agent runs. */
|
|
53
67
|
export class SubAgentOrchestrator {
|
|
54
68
|
async run(ctx) {
|
|
69
|
+
// W-N1: "inherit" runs the child on the parent's plane with availability-derived meta-tools
|
|
70
|
+
// (trusted workflow nodes); "filtered" (default) filters to the manifest grants, empty ⇒
|
|
71
|
+
// deny-all (spawn path, quarantined nodes).
|
|
72
|
+
const inherit = ctx.toolAccess === "inherit";
|
|
55
73
|
const permitted = new Set(ctx.manifest.permitted_capability_ids ?? []);
|
|
56
|
-
const metaTools = deriveMetaTools(permitted, ctx.parentOpts);
|
|
57
|
-
const
|
|
74
|
+
const metaTools = inherit ? availableMetaTools(ctx.parentOpts) : deriveMetaTools(permitted, ctx.parentOpts);
|
|
75
|
+
const execPlane = inherit
|
|
76
|
+
? ctx.parentOpts.executionPlane
|
|
77
|
+
: new FilteredExecutionPlane(ctx.parentOpts.executionPlane, permitted, metaTools);
|
|
58
78
|
let systemPrompt = ctx.parentOpts.systemPrompt;
|
|
59
79
|
let inheritEvents;
|
|
60
80
|
if (ctx.manifest.context_inheritance === "full") {
|
|
@@ -77,7 +97,7 @@ export class SubAgentOrchestrator {
|
|
|
77
97
|
// O3: per-child turn / wall-clock caps (fall back to the inherited limits).
|
|
78
98
|
maxTurns: ctx.spec.maxTurns ?? ctx.parentOpts.maxTurns,
|
|
79
99
|
timeoutMs: ctx.spec.maxWallMs ?? ctx.parentOpts.timeoutMs,
|
|
80
|
-
executionPlane:
|
|
100
|
+
executionPlane: execPlane,
|
|
81
101
|
agentId: ctx.spec.identity.agentId,
|
|
82
102
|
systemPrompt,
|
|
83
103
|
sessionLog: ctx.sessionLog,
|
|
@@ -87,6 +107,13 @@ export class SubAgentOrchestrator {
|
|
|
87
107
|
enablePlanTool: metaTools.has("update_plan") ? ctx.parentOpts.enablePlanTool : undefined,
|
|
88
108
|
// M5 v2.1: a workflow node's `start_workflow` flattens to the parent kernel (no nested pivot).
|
|
89
109
|
isWorkflowNode: ctx.isWorkflowNode,
|
|
110
|
+
// The child runs under ITS OWN spec, never the parent's: the spread above would otherwise
|
|
111
|
+
// leak the parent's `runSpec` (identity, capability filter — and an armed `loopRound`,
|
|
112
|
+
// giving every child a phantom pace tool). A loop-node iteration carries its own minimal
|
|
113
|
+
// spec to arm the pacing trap (DW-3); everything else runs spec-less as before.
|
|
114
|
+
runSpec: ctx.spec.loopRound
|
|
115
|
+
? { identity: ctx.spec.identity, role: ctx.spec.role, goal: ctx.spec.goal, loopRound: ctx.spec.loopRound }
|
|
116
|
+
: undefined,
|
|
90
117
|
});
|
|
91
118
|
// #2-B-ii: parent preempt → interrupt the child (cancels its in-flight LLM call).
|
|
92
119
|
linkAbort(ctx.abortSignal, childRunner);
|
|
@@ -112,6 +139,9 @@ export class SubAgentOrchestrator {
|
|
|
112
139
|
...(finalText
|
|
113
140
|
? { finalMessage: { role: "assistant", content: finalText, toolCalls: [] } }
|
|
114
141
|
: {}),
|
|
142
|
+
// DW-3: surface the kernel-adjudicated pace decision (loop-node iterations consume it as the
|
|
143
|
+
// continuation vocabulary). SDK-internal; stripped by subAgentResultToKernel.
|
|
144
|
+
...(done?.paceDecision ? { paceDecision: done.paceDecision } : {}),
|
|
115
145
|
};
|
|
116
146
|
return {
|
|
117
147
|
agentId: ctx.spec.identity.agentId,
|
|
@@ -19,6 +19,16 @@ export interface AgentCapabilityFilter {
|
|
|
19
19
|
allowedKinds?: string[];
|
|
20
20
|
allowedIds?: string[];
|
|
21
21
|
}
|
|
22
|
+
export interface LoopRoundSpec {
|
|
23
|
+
/** Hard round cap across the loop's lifetime; continue/sleep at the cap is coerced to stop. */
|
|
24
|
+
maxRounds?: number;
|
|
25
|
+
/** Sleep clamp floor (ms). */
|
|
26
|
+
minSleepMs?: number;
|
|
27
|
+
/** Sleep clamp ceiling (ms). */
|
|
28
|
+
maxSleepMs?: number;
|
|
29
|
+
/** Fallback when a round ends without a pace call: "stop" (goal loops, default) | "sleep" (cron loops). */
|
|
30
|
+
defaultAction?: "stop" | "sleep";
|
|
31
|
+
}
|
|
22
32
|
export interface AgentRunSpec {
|
|
23
33
|
identity: AgentIdentity;
|
|
24
34
|
role: KernelAgentRole;
|
|
@@ -28,6 +38,9 @@ export interface AgentRunSpec {
|
|
|
28
38
|
capabilityFilter?: AgentCapabilityFilter;
|
|
29
39
|
milestones?: MilestoneContract;
|
|
30
40
|
metadata?: Record<string, unknown>;
|
|
41
|
+
/** ③ loop-agent rounds: presence makes this run ONE round of a paced loop (gates the
|
|
42
|
+
* kernel `pace` meta-tool and arms the pacing trap). */
|
|
43
|
+
loopRound?: LoopRoundSpec;
|
|
31
44
|
/** M1/G3: per-agent model preference; the host resolves it via `RuntimeOptions.providerFor`.
|
|
32
45
|
* Host-side routing only — not sent to the kernel. */
|
|
33
46
|
modelHint?: string;
|
|
@@ -70,6 +83,11 @@ export interface LoopResult {
|
|
|
70
83
|
classifyBranch?: string;
|
|
71
84
|
/** A#2 tournament verdict: a judge reports the winning entrant's agent id here. Sent only when set. */
|
|
72
85
|
tournamentWinner?: string;
|
|
86
|
+
/** ③ loop-agent pacing: the kernel-adjudicated after-round decision, surfaced by the orchestrator
|
|
87
|
+
* from the child's done event. For a loop-node iteration this is the PRIMARY continuation
|
|
88
|
+
* vocabulary (stop → loopContinue=false); the legacy text-sniffed signal is the fallback.
|
|
89
|
+
* SDK-internal — stripped by `subAgentResultToKernel`. */
|
|
90
|
+
paceDecision?: import("../kernel-step.js").PaceDecision;
|
|
73
91
|
}
|
|
74
92
|
export interface SubAgentResult {
|
|
75
93
|
agentId: string;
|
|
@@ -141,6 +159,10 @@ export interface WorkflowNodeSpec {
|
|
|
141
159
|
};
|
|
142
160
|
/** M4/G5: cap this node's child run at `tokenBudget` cumulative tokens (the per-node "use N tokens"). */
|
|
143
161
|
tokenBudget?: number;
|
|
162
|
+
/** O3: cap this node's child run at `maxTurns` provider turns (falls back to the parent's). */
|
|
163
|
+
maxTurns?: number;
|
|
164
|
+
/** O3: cap this node's child run at `maxWallMs` wall-clock milliseconds. */
|
|
165
|
+
maxWallMs?: number;
|
|
144
166
|
/** Indices of nodes this node depends on. */
|
|
145
167
|
dependsOn?: number[];
|
|
146
168
|
}
|
|
@@ -156,11 +178,14 @@ export interface WorkflowSpawnInfo {
|
|
|
156
178
|
isolation: string;
|
|
157
179
|
context_inheritance: string;
|
|
158
180
|
model_hint?: string;
|
|
181
|
+
/** W3 trust level: `"trusted"` | `"quarantined"`. */
|
|
182
|
+
trust?: string;
|
|
159
183
|
/** G3: JSON Schema the node's output must conform to (carried verbatim from the spec). */
|
|
160
184
|
output_schema?: Record<string, unknown>;
|
|
161
185
|
/** G2: for a reduce node, the registered reducer name to run (no LLM). */
|
|
162
186
|
reducer?: string;
|
|
163
|
-
/**
|
|
187
|
+
/** The dependency agent ids for EVERY dependent node (W-N2: a DAG edge carries data). A reduce
|
|
188
|
+
* node's registered function consumes them; every other node gets its deps' outputs in context. */
|
|
164
189
|
input_agent_ids?: string[];
|
|
165
190
|
/** A#2: present only for a tournament *judge* spawn — the two entrant agent ids whose produced
|
|
166
191
|
* outputs this judge compares. The runner looks them up and reports the winner as `tournamentWinner`. */
|
|
@@ -176,6 +201,10 @@ export interface WorkflowSpawnInfo {
|
|
|
176
201
|
classify_labels?: string[];
|
|
177
202
|
/** M4/G5: the node's per-node cumulative token cap, if set — the runner caps the child run here. */
|
|
178
203
|
token_budget?: number;
|
|
204
|
+
/** O3: per-node turn cap → the child run's `maxTurns`. */
|
|
205
|
+
max_turns?: number;
|
|
206
|
+
/** O3: per-node wall-clock cap (ms) → the child run's timeout. */
|
|
207
|
+
max_wall_ms?: number;
|
|
179
208
|
}
|
|
180
209
|
/** G4 budget-as-signal: the workflow's remaining headroom under the active quota, carried on the
|
|
181
210
|
* `workflow_batch_spawned` observation so a coordinator node can scale its next submission. */
|
|
@@ -225,3 +254,9 @@ export declare function generateAndFilter(generators: WorkflowTaskSpec[], filter
|
|
|
225
254
|
* re-checks flags. Verifiers run read-only with no inherited author context (bias-resistant).
|
|
226
255
|
*/
|
|
227
256
|
export declare function verifyRules(rules: WorkflowTaskSpec[], skeptic?: WorkflowTaskSpec): WorkflowSpec;
|
|
257
|
+
/**
|
|
258
|
+
* Generate → evaluate loop template (parity with node/python `genEval`): a worker Loop node
|
|
259
|
+
* (worktree isolation, full context) feeding a fresh-context verifier whose output is pinned
|
|
260
|
+
* to the kernel's verdict schema. Async because the wasm kernel module loads lazily.
|
|
261
|
+
*/
|
|
262
|
+
export declare function genEval(worker: WorkflowTaskSpec, evaluate: WorkflowTaskSpec, maxIters?: number, extractSkillOnPass?: boolean): Promise<WorkflowSpec>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getKernel } from "../kernel.js";
|
|
1
2
|
/** Map kernel spawn observation → host manifest. */
|
|
2
3
|
export function spawnObservationToManifest(obs, spec, parentSessionId) {
|
|
3
4
|
const o = obs;
|
|
@@ -45,6 +46,14 @@ export function agentRunSpecToKernel(spec) {
|
|
|
45
46
|
out.verification_contract_id = spec.verificationContractId;
|
|
46
47
|
if (spec.milestones)
|
|
47
48
|
out.milestones = milestoneContractToKernel(spec.milestones);
|
|
49
|
+
if (spec.loopRound) {
|
|
50
|
+
out.loop_round = {
|
|
51
|
+
...(spec.loopRound.maxRounds !== undefined ? { max_rounds: spec.loopRound.maxRounds } : {}),
|
|
52
|
+
...(spec.loopRound.minSleepMs !== undefined ? { min_sleep_ms: spec.loopRound.minSleepMs } : {}),
|
|
53
|
+
...(spec.loopRound.maxSleepMs !== undefined ? { max_sleep_ms: spec.loopRound.maxSleepMs } : {}),
|
|
54
|
+
...(spec.loopRound.defaultAction !== undefined ? { default_action: spec.loopRound.defaultAction } : {}),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
48
57
|
return out;
|
|
49
58
|
}
|
|
50
59
|
export function milestoneContractToKernel(contract) {
|
|
@@ -175,6 +184,9 @@ export function workflowNodeSpecToKernel(n) {
|
|
|
175
184
|
...(kind ? { kind } : {}),
|
|
176
185
|
// M4/G5: per-node token cap (additive; omitted when unset).
|
|
177
186
|
...(n.tokenBudget != null ? { token_budget: n.tokenBudget } : {}),
|
|
187
|
+
// O3: per-node turn / wall-clock caps (additive; omitted when unset).
|
|
188
|
+
...(n.maxTurns != null ? { max_turns: n.maxTurns } : {}),
|
|
189
|
+
...(n.maxWallMs != null ? { max_wall_ms: n.maxWallMs } : {}),
|
|
178
190
|
...(n.dependsOn && n.dependsOn.length ? { depends_on: n.dependsOn } : {}),
|
|
179
191
|
};
|
|
180
192
|
}
|
|
@@ -302,10 +314,15 @@ export const startWorkflowTool = {
|
|
|
302
314
|
};
|
|
303
315
|
/** Build a sub-agent run spec for a kernel-generated workflow node. */
|
|
304
316
|
export function workflowNodeToSpec(node, parentSessionId) {
|
|
317
|
+
// W-N6 transcript-as-carry: a loop node's iterations share ONE stable session id (the `-i{k}`
|
|
318
|
+
// suffix names the spawn, not the session), so iteration k replays the transcript of 0..k-1 —
|
|
319
|
+
// "do the next increment" actually sees the previous increments. The agent_id keeps the
|
|
320
|
+
// per-iteration suffix (kernel completion routing).
|
|
321
|
+
const sessionNodeId = node.loop_max_iters != null ? node.agent_id.replace(/-i\d+$/, "") : node.agent_id;
|
|
305
322
|
return {
|
|
306
323
|
identity: {
|
|
307
324
|
agentId: node.agent_id,
|
|
308
|
-
sessionId: `${parentSessionId}-${
|
|
325
|
+
sessionId: `${parentSessionId}-${sessionNodeId}`,
|
|
309
326
|
isSubAgent: true,
|
|
310
327
|
parentSessionId,
|
|
311
328
|
},
|
|
@@ -316,6 +333,14 @@ export function workflowNodeToSpec(node, parentSessionId) {
|
|
|
316
333
|
...(node.model_hint ? { modelHint: node.model_hint } : {}),
|
|
317
334
|
// M4/G5: carry the node's token cap so the orchestrator can bound the child run.
|
|
318
335
|
...(node.token_budget != null ? { tokenBudget: node.token_budget } : {}),
|
|
336
|
+
// O3: carry the node's turn / wall-clock caps (the orchestrator already honors these).
|
|
337
|
+
...(node.max_turns != null ? { maxTurns: node.max_turns } : {}),
|
|
338
|
+
...(node.max_wall_ms != null ? { maxWallMs: node.max_wall_ms } : {}),
|
|
339
|
+
// DW-3 one continuation vocabulary: a loop ITERATION runs with the pacing trap armed, so the
|
|
340
|
+
// agent signals continue/stop through the kernel-adjudicated `pace` meta-tool instead of a
|
|
341
|
+
// text-sniffed JSON blob. One iteration = one round; the DAG (not max_rounds) caps iterations,
|
|
342
|
+
// and default stop means "ended without pacing" = done — the CC silence-is-completion contract.
|
|
343
|
+
...(node.loop_max_iters != null ? { loopRound: { defaultAction: "stop" } } : {}),
|
|
319
344
|
};
|
|
320
345
|
}
|
|
321
346
|
/** Build the host manifest for a kernel-generated workflow node. */
|
|
@@ -391,3 +416,31 @@ export function verifyRules(rules, skeptic) {
|
|
|
391
416
|
}
|
|
392
417
|
return { nodes };
|
|
393
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Generate → evaluate loop template (parity with node/python `genEval`): a worker Loop node
|
|
421
|
+
* (worktree isolation, full context) feeding a fresh-context verifier whose output is pinned
|
|
422
|
+
* to the kernel's verdict schema. Async because the wasm kernel module loads lazily.
|
|
423
|
+
*/
|
|
424
|
+
export async function genEval(worker, evaluate, maxIters = 3, extractSkillOnPass = true) {
|
|
425
|
+
const kernel = await getKernel();
|
|
426
|
+
const schema = JSON.parse(kernel.verdictOutputSchema(extractSkillOnPass));
|
|
427
|
+
return {
|
|
428
|
+
nodes: [
|
|
429
|
+
{
|
|
430
|
+
task: asTask(worker),
|
|
431
|
+
role: "implement",
|
|
432
|
+
isolation: "worktree",
|
|
433
|
+
contextInheritance: "full",
|
|
434
|
+
loop: { maxIters: Math.max(1, maxIters) },
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
task: asTask(evaluate),
|
|
438
|
+
role: "verify",
|
|
439
|
+
isolation: "read_only",
|
|
440
|
+
contextInheritance: "none",
|
|
441
|
+
dependsOn: [0],
|
|
442
|
+
outputSchema: schema,
|
|
443
|
+
},
|
|
444
|
+
],
|
|
445
|
+
};
|
|
446
|
+
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
/** Instruction appended to a loop
|
|
2
|
-
|
|
1
|
+
/** Instruction appended to a loop iteration's goal. DW-3: the continuation verb is the
|
|
2
|
+
* kernel-adjudicated `pace` meta-tool (armed on every iteration run), not a text blob — one
|
|
3
|
+
* vocabulary shared with the round-level loop agent. Iterations share one session, so "the work
|
|
4
|
+
* so far" is simply the visible transcript. */
|
|
5
|
+
export declare function loopInstruction(maxIters: number, iteration?: number): string;
|
|
6
|
+
/** W-N2: dependency outputs appended to a dependent node's goal — a DAG edge carries data, not
|
|
7
|
+
* just ordering (fan-out→synthesize was an uninformed synthesis without this). Each dependency's
|
|
8
|
+
* output is clipped so a chain of large nodes can't blow the child's context; empty/unknown
|
|
9
|
+
* outputs are skipped. Returns "" when the node has no dependencies. */
|
|
10
|
+
export declare function dependencyOutputsNote(inputAgentIds: string[] | undefined, outputs: Map<string, string> | undefined, maxPerDep?: number): string;
|
|
3
11
|
/** Instruction appended to a classify node's goal: pick exactly one of the kernel's branch labels. */
|
|
4
12
|
export declare function classifyInstruction(labels: string[]): string;
|
|
5
13
|
/** Build a tournament judge's goal: the controller's criterion + the two candidates to compare. */
|
|
@@ -6,12 +6,33 @@
|
|
|
6
6
|
//! decision from the node's agent and extracts the matching result signal (`loopContinue` /
|
|
7
7
|
//! `classifyBranch` / `tournamentWinner`) the kernel reads back.
|
|
8
8
|
import { extractJsonValue } from "./output-schema.js";
|
|
9
|
-
/** Instruction appended to a loop
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
/** Instruction appended to a loop iteration's goal. DW-3: the continuation verb is the
|
|
10
|
+
* kernel-adjudicated `pace` meta-tool (armed on every iteration run), not a text blob — one
|
|
11
|
+
* vocabulary shared with the round-level loop agent. Iterations share one session, so "the work
|
|
12
|
+
* so far" is simply the visible transcript. */
|
|
13
|
+
export function loopInstruction(maxIters, iteration = 0) {
|
|
14
|
+
return (`This task runs as a LOOP — this is iteration ${iteration + 1} of up to ${maxIters}. Your prior ` +
|
|
15
|
+
`iterations' work (if any) is visible above; do the NEXT increment now. Then call the \`pace\` tool: ` +
|
|
16
|
+
`\`{"next": "continue"}\` to request another iteration, or \`{"next": "stop"}\` when the overall ` +
|
|
17
|
+
`task is complete. Ending without calling \`pace\` also completes the loop.`);
|
|
18
|
+
}
|
|
19
|
+
/** W-N2: dependency outputs appended to a dependent node's goal — a DAG edge carries data, not
|
|
20
|
+
* just ordering (fan-out→synthesize was an uninformed synthesis without this). Each dependency's
|
|
21
|
+
* output is clipped so a chain of large nodes can't blow the child's context; empty/unknown
|
|
22
|
+
* outputs are skipped. Returns "" when the node has no dependencies. */
|
|
23
|
+
export function dependencyOutputsNote(inputAgentIds, outputs, maxPerDep = 8_000) {
|
|
24
|
+
if (!inputAgentIds?.length || !outputs)
|
|
25
|
+
return "";
|
|
26
|
+
const blocks = inputAgentIds
|
|
27
|
+
.map(id => {
|
|
28
|
+
const out = outputs.get(id) ?? "";
|
|
29
|
+
if (!out)
|
|
30
|
+
return "";
|
|
31
|
+
const clipped = out.length > maxPerDep ? `${out.slice(0, maxPerDep)}\n…[truncated]` : out;
|
|
32
|
+
return `[dependency ${id} output]\n${clipped}`;
|
|
33
|
+
})
|
|
34
|
+
.filter(Boolean);
|
|
35
|
+
return blocks.join("\n\n");
|
|
15
36
|
}
|
|
16
37
|
/** Instruction appended to a classify node's goal: pick exactly one of the kernel's branch labels. */
|
|
17
38
|
export function classifyInstruction(labels) {
|
package/dist/types.d.ts
CHANGED
|
@@ -105,7 +105,8 @@ export interface DoneEvent extends StreamEvent {
|
|
|
105
105
|
iterations: number;
|
|
106
106
|
totalTokens: number;
|
|
107
107
|
status: string;
|
|
108
|
-
dreamResult?: import("./memory/index.js").DreamResult;
|
|
108
|
+
dreamResult?: import("./memory/index.js").DreamResult; /** ③ loop-agent: the kernel-adjudicated after-round decision (absent on non-loop runs). */
|
|
109
|
+
paceDecision?: import("./runtime/kernel-step.js").PaceDecision;
|
|
109
110
|
}
|
|
110
111
|
export interface ErrorEvent extends StreamEvent {
|
|
111
112
|
type: "error";
|
|
@@ -154,6 +155,38 @@ export interface ToolArgumentRepairedEvent extends StreamEvent {
|
|
|
154
155
|
originalArguments: string;
|
|
155
156
|
repairedArguments: string;
|
|
156
157
|
}
|
|
158
|
+
/** Kernel session-entropy measurement at a completed turn boundary (see the Node SDK's
|
|
159
|
+
* `EntropySample` for the canonical documentation; this is the WASM mirror). */
|
|
160
|
+
export interface EntropySample {
|
|
161
|
+
turn: number;
|
|
162
|
+
score: number;
|
|
163
|
+
scoreVersion: number;
|
|
164
|
+
rho: number;
|
|
165
|
+
repeatPressure: number;
|
|
166
|
+
failureRate: number;
|
|
167
|
+
rollbacksInWindow: number;
|
|
168
|
+
windowTurns: number;
|
|
169
|
+
}
|
|
170
|
+
/** One kernel entropy sample, emitted once per completed turn (a heartbeat watch source). */
|
|
171
|
+
export interface EntropySampleEvent extends StreamEvent {
|
|
172
|
+
type: "entropy_sample";
|
|
173
|
+
sample: EntropySample;
|
|
174
|
+
}
|
|
175
|
+
/** The opt-in kernel entropy watch tripped (see `RunnerOptions.entropyWatch`). */
|
|
176
|
+
export interface EntropyAlertEvent extends StreamEvent {
|
|
177
|
+
type: "entropy_alert";
|
|
178
|
+
turn: number;
|
|
179
|
+
score: number;
|
|
180
|
+
threshold: number;
|
|
181
|
+
}
|
|
182
|
+
/** Opt-in kernel-side threshold watch over the per-turn entropy score (Node SDK mirror). */
|
|
183
|
+
export interface EntropyWatchOptions {
|
|
184
|
+
enabled?: boolean;
|
|
185
|
+
threshold?: number;
|
|
186
|
+
hysteresis?: number;
|
|
187
|
+
cooldownTurns?: number;
|
|
188
|
+
notifyModel?: boolean;
|
|
189
|
+
}
|
|
157
190
|
/**
|
|
158
191
|
* Opaque per-run state owned by the provider (e.g. OpenAI Responses continuation).
|
|
159
192
|
* The framework creates and threads this object; providers may read/write it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstrike/wasm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.38",
|
|
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.
|
|
18
|
+
"@deepstrike/wasm-kernel": "0.2.38"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@types/jest": "^30.0.0",
|