@deepstrike/wasm 0.2.49 → 0.2.51
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 +2 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/runtime/canonical-kernel-step.d.ts +146 -0
- package/dist/runtime/canonical-kernel-step.js +1484 -0
- package/dist/runtime/execution-plane.d.ts +0 -3
- package/dist/runtime/execution-plane.js +0 -25
- package/dist/runtime/facade.js +3 -0
- package/dist/runtime/index.d.ts +7 -7
- package/dist/runtime/index.js +4 -3
- package/dist/runtime/kernel-event-log.d.ts +0 -1
- package/dist/runtime/kernel-event-log.js +7 -13
- package/dist/runtime/kernel-journal.d.ts +332 -0
- package/dist/runtime/kernel-journal.js +681 -0
- package/dist/runtime/kernel-step.d.ts +34 -89
- package/dist/runtime/kernel-step.js +67 -167
- package/dist/runtime/os-snapshot.d.ts +2 -2
- package/dist/runtime/os-snapshot.js +2 -6
- package/dist/runtime/payload-store.d.ts +30 -0
- package/dist/runtime/payload-store.js +66 -0
- package/dist/runtime/runner.d.ts +72 -79
- package/dist/runtime/runner.js +424 -637
- package/dist/runtime/session-log.d.ts +24 -27
- package/dist/runtime/session-log.js +11 -52
- package/dist/runtime/session-repair.d.ts +2 -36
- package/dist/runtime/session-repair.js +2 -47
- package/dist/runtime/sub-agent-orchestrator.d.ts +2 -2
- package/dist/runtime/sub-agent-orchestrator.js +36 -40
- package/dist/runtime/types/agent.d.ts +21 -16
- package/dist/runtime/types/agent.js +21 -41
- package/package.json +4 -3
- package/dist/runtime/kernel-rebuild.d.ts +0 -8
- package/dist/runtime/kernel-rebuild.js +0 -65
- package/dist/runtime/kernel-transaction-log.d.ts +0 -61
- package/dist/runtime/kernel-transaction-log.js +0 -140
- package/dist/runtime/large-result-spool.d.ts +0 -57
- package/dist/runtime/large-result-spool.js +0 -169
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import type { ProviderReplay, ToolCall, ToolErrorKind } from "../types.js";
|
|
2
2
|
import type { MemoryRecall, MemoryScope } from "../memory/index.js";
|
|
3
3
|
import type { KernelPrimitive } from "./kernel-event-log.js";
|
|
4
|
-
import type {
|
|
5
|
-
export interface KernelTransactionEntry {
|
|
6
|
-
log_seq: number;
|
|
7
|
-
transaction: KernelTransaction;
|
|
8
|
-
}
|
|
4
|
+
import type { KernelJournal } from "./kernel-journal.js";
|
|
9
5
|
export type RollbackReason = {
|
|
10
6
|
kind: "fatal_tool_error";
|
|
11
7
|
tool_name: string;
|
|
@@ -98,14 +94,6 @@ export type SessionEvent = {
|
|
|
98
94
|
kind: "page_in";
|
|
99
95
|
turn: number;
|
|
100
96
|
entry_count: number;
|
|
101
|
-
} | {
|
|
102
|
-
kind: "large_result_spooled";
|
|
103
|
-
turn: number;
|
|
104
|
-
call_id: string;
|
|
105
|
-
tool: string;
|
|
106
|
-
original_size: number;
|
|
107
|
-
preview_size: number;
|
|
108
|
-
spool_ref?: string;
|
|
109
97
|
} | {
|
|
110
98
|
kind: "rollbacked";
|
|
111
99
|
turn: number;
|
|
@@ -204,7 +192,9 @@ export type SessionEvent = {
|
|
|
204
192
|
kind: "agent_process_changed";
|
|
205
193
|
turn: number;
|
|
206
194
|
agent_id: string;
|
|
207
|
-
|
|
195
|
+
parent_task_id?: string;
|
|
196
|
+
/** Host audit identity; canonical kernel observations never populate it. */
|
|
197
|
+
parent_session_id?: string;
|
|
208
198
|
role: string;
|
|
209
199
|
isolation: string;
|
|
210
200
|
context_inheritance: string;
|
|
@@ -267,6 +257,11 @@ export type SessionEvent = {
|
|
|
267
257
|
turn: number;
|
|
268
258
|
node_outcomes: import("./types/agent.js").KernelWorkflowNodeOutcome[];
|
|
269
259
|
total_nodes: number;
|
|
260
|
+
} | {
|
|
261
|
+
kind: "kernel_observation";
|
|
262
|
+
turn: number;
|
|
263
|
+
observation_kind: string;
|
|
264
|
+
raw: Record<string, unknown>;
|
|
270
265
|
} | {
|
|
271
266
|
kind: "run_terminal";
|
|
272
267
|
reason: string;
|
|
@@ -277,6 +272,11 @@ export type SessionEvent = {
|
|
|
277
272
|
compressed_seq: number;
|
|
278
273
|
summary: string;
|
|
279
274
|
};
|
|
275
|
+
/**
|
|
276
|
+
* The business-projection log (spec §9.2): run started/terminal, stream events, observations,
|
|
277
|
+
* provider/tool presentation, and audit metadata. Canonical durable records live exclusively in
|
|
278
|
+
* `KernelJournal` (spec §9.1).
|
|
279
|
+
*/
|
|
280
280
|
export interface SessionLog {
|
|
281
281
|
append(sessionId: string, event: SessionEvent): Promise<number>;
|
|
282
282
|
read(sessionId: string, fromSeq?: number, primitiveFilter?: KernelPrimitive): Promise<Array<{
|
|
@@ -284,18 +284,20 @@ export interface SessionLog {
|
|
|
284
284
|
event: SessionEvent;
|
|
285
285
|
}>>;
|
|
286
286
|
latestSeq(sessionId: string): Promise<number>;
|
|
287
|
-
appendKernelGenesis(sessionId: string, genesis: KernelOperationGenesis): Promise<KernelGenesisReceipt>;
|
|
288
|
-
readKernelGenesis(sessionId: string, operationId: string): Promise<KernelOperationGenesis | undefined>;
|
|
289
|
-
compareAndAppendKernelTransaction(sessionId: string, expectedTransactionHead: string, transaction: KernelTransaction): Promise<DurableAppendReceipt>;
|
|
290
|
-
readKernelTransactions(sessionId: string, operationId: string, fromStepSeq?: number): Promise<KernelTransactionEntry[]>;
|
|
291
|
-
kernelTransactionHead(sessionId: string, operationId: string): Promise<string | undefined>;
|
|
292
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* **Single-isolate dev/test implementation** of both capabilities (spec §9.4: one class may
|
|
290
|
+
* implement several capabilities; the *interfaces* stay separate). Its `KernelJournal` half is
|
|
291
|
+
* `InMemoryKernelJournal`, whose CAS is atomic within one isolate only. A host whose journal must
|
|
292
|
+
* outlive its isolate — or be shared with another one — injects a `DriverKernelJournal` over a
|
|
293
|
+
* durable `JournalStorageDriver` instead.
|
|
294
|
+
*/
|
|
293
295
|
export declare class InMemorySessionLog implements SessionLog {
|
|
294
296
|
private store;
|
|
297
|
+
/** Business event sequence space only — journal records number themselves by `step_seq`. */
|
|
295
298
|
private seqCounters;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
private operationKey;
|
|
299
|
+
/** The durable transaction capability, held rather than inherited (spec §9.1/§9.4). */
|
|
300
|
+
readonly kernelJournal: KernelJournal;
|
|
299
301
|
private nextSeq;
|
|
300
302
|
append(sessionId: string, event: SessionEvent): Promise<number>;
|
|
301
303
|
read(sessionId: string, fromSeq?: number, primitiveFilter?: KernelPrimitive): Promise<Array<{
|
|
@@ -303,9 +305,4 @@ export declare class InMemorySessionLog implements SessionLog {
|
|
|
303
305
|
event: SessionEvent;
|
|
304
306
|
}>>;
|
|
305
307
|
latestSeq(sessionId: string): Promise<number>;
|
|
306
|
-
appendKernelGenesis(sessionId: string, genesis: KernelOperationGenesis): Promise<KernelGenesisReceipt>;
|
|
307
|
-
readKernelGenesis(sessionId: string, operationId: string): Promise<KernelOperationGenesis | undefined>;
|
|
308
|
-
compareAndAppendKernelTransaction(sessionId: string, expectedTransactionHead: string, transaction: KernelTransaction): Promise<DurableAppendReceipt>;
|
|
309
|
-
readKernelTransactions(sessionId: string, operationId: string, fromStepSeq?: number): Promise<KernelTransactionEntry[]>;
|
|
310
|
-
kernelTransactionHead(sessionId: string, operationId: string): Promise<string | undefined>;
|
|
311
308
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { primitiveForKind } from "./kernel-event-log.js";
|
|
2
|
-
import {
|
|
2
|
+
import { InMemoryKernelJournal } from "./kernel-journal.js";
|
|
3
|
+
/**
|
|
4
|
+
* **Single-isolate dev/test implementation** of both capabilities (spec §9.4: one class may
|
|
5
|
+
* implement several capabilities; the *interfaces* stay separate). Its `KernelJournal` half is
|
|
6
|
+
* `InMemoryKernelJournal`, whose CAS is atomic within one isolate only. A host whose journal must
|
|
7
|
+
* outlive its isolate — or be shared with another one — injects a `DriverKernelJournal` over a
|
|
8
|
+
* durable `JournalStorageDriver` instead.
|
|
9
|
+
*/
|
|
3
10
|
export class InMemorySessionLog {
|
|
4
11
|
store = new Map();
|
|
12
|
+
/** Business event sequence space only — journal records number themselves by `step_seq`. */
|
|
5
13
|
seqCounters = new Map();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
operationKey(sessionId, operationId) {
|
|
9
|
-
return `${sessionId}\u0000${operationId}`;
|
|
10
|
-
}
|
|
14
|
+
/** The durable transaction capability, held rather than inherited (spec §9.1/§9.4). */
|
|
15
|
+
kernelJournal = new InMemoryKernelJournal();
|
|
11
16
|
nextSeq(sessionId) {
|
|
12
17
|
const seq = this.seqCounters.get(sessionId) ?? 0;
|
|
13
18
|
this.seqCounters.set(sessionId, seq + 1);
|
|
@@ -34,50 +39,4 @@ export class InMemorySessionLog {
|
|
|
34
39
|
async latestSeq(sessionId) {
|
|
35
40
|
return (this.seqCounters.get(sessionId) ?? 0) - 1;
|
|
36
41
|
}
|
|
37
|
-
async appendKernelGenesis(sessionId, genesis) {
|
|
38
|
-
await verifyKernelOperationGenesis(genesis);
|
|
39
|
-
const operationKey = this.operationKey(sessionId, genesis.operation_id);
|
|
40
|
-
const existing = this.genesisStore.get(operationKey);
|
|
41
|
-
if (existing) {
|
|
42
|
-
if (existing.genesis.genesis_digest !== genesis.genesis_digest) {
|
|
43
|
-
throw new KernelLogConflictError("session already has a different kernel operation genesis");
|
|
44
|
-
}
|
|
45
|
-
return { log_seq: existing.log_seq, genesis_digest: genesis.genesis_digest };
|
|
46
|
-
}
|
|
47
|
-
const log_seq = this.nextSeq(sessionId);
|
|
48
|
-
this.genesisStore.set(operationKey, { log_seq, genesis });
|
|
49
|
-
return { log_seq, genesis_digest: genesis.genesis_digest };
|
|
50
|
-
}
|
|
51
|
-
async readKernelGenesis(sessionId, operationId) {
|
|
52
|
-
return this.genesisStore.get(this.operationKey(sessionId, operationId))?.genesis;
|
|
53
|
-
}
|
|
54
|
-
async compareAndAppendKernelTransaction(sessionId, expectedTransactionHead, transaction) {
|
|
55
|
-
await verifyKernelTransaction(transaction);
|
|
56
|
-
const operationKey = this.operationKey(sessionId, transaction.operation_id);
|
|
57
|
-
const genesis = this.genesisStore.get(operationKey)?.genesis;
|
|
58
|
-
if (!genesis)
|
|
59
|
-
throw new KernelLogIntegrityError("kernel transaction requires a durable genesis");
|
|
60
|
-
if (transaction.operation_id !== genesis.operation_id) {
|
|
61
|
-
throw new KernelLogIntegrityError("kernel transaction operation_id does not match genesis");
|
|
62
|
-
}
|
|
63
|
-
const head = await this.kernelTransactionHead(sessionId, transaction.operation_id);
|
|
64
|
-
if (head !== expectedTransactionHead || transaction.previous_transaction_digest !== head) {
|
|
65
|
-
throw new KernelLogConflictError("kernel transaction head changed before compare-and-append");
|
|
66
|
-
}
|
|
67
|
-
const entries = this.transactionStore.get(operationKey) ?? [];
|
|
68
|
-
verifyKernelTransactionSuccessor(entries.at(-1)?.transaction, transaction);
|
|
69
|
-
const log_seq = this.nextSeq(sessionId);
|
|
70
|
-
entries.push({ log_seq, transaction });
|
|
71
|
-
this.transactionStore.set(operationKey, entries);
|
|
72
|
-
return { log_seq, transaction_digest: transaction.transaction_digest };
|
|
73
|
-
}
|
|
74
|
-
async readKernelTransactions(sessionId, operationId, fromStepSeq = 1) {
|
|
75
|
-
return (this.transactionStore.get(this.operationKey(sessionId, operationId)) ?? []).filter(entry => entry.transaction.step_seq >= fromStepSeq);
|
|
76
|
-
}
|
|
77
|
-
async kernelTransactionHead(sessionId, operationId) {
|
|
78
|
-
const operationKey = this.operationKey(sessionId, operationId);
|
|
79
|
-
const entries = this.transactionStore.get(operationKey) ?? [];
|
|
80
|
-
return entries.at(-1)?.transaction.transaction_digest
|
|
81
|
-
?? this.genesisStore.get(operationKey)?.genesis.genesis_digest;
|
|
82
|
-
}
|
|
83
42
|
}
|
|
@@ -37,8 +37,7 @@ export declare function buildRunTerminalEvent(input: {
|
|
|
37
37
|
}): Extract<SessionEvent, {
|
|
38
38
|
kind: "run_terminal";
|
|
39
39
|
}>;
|
|
40
|
-
/** Build
|
|
41
|
-
* result-borne control signals + output so resume replays control flow and re-seeds outputs. */
|
|
40
|
+
/** Build the audit projection emitted after a workflow node finishes. */
|
|
42
41
|
export declare function buildWorkflowNodeCompletedEvent(input: {
|
|
43
42
|
turn: number;
|
|
44
43
|
agentId: string;
|
|
@@ -51,29 +50,7 @@ export declare function buildWorkflowNodeCompletedEvent(input: {
|
|
|
51
50
|
}): Extract<SessionEvent, {
|
|
52
51
|
kind: "workflow_node_completed";
|
|
53
52
|
}>;
|
|
54
|
-
/**
|
|
55
|
-
export interface RecoveredNodeOutcome {
|
|
56
|
-
agentId: string;
|
|
57
|
-
status: WorkflowNodeStatus;
|
|
58
|
-
termination: string;
|
|
59
|
-
classifyBranch?: string;
|
|
60
|
-
tournamentWinner?: string;
|
|
61
|
-
loopContinue?: boolean;
|
|
62
|
-
output?: Message;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Recover completed workflow node records from a session event stream. Scans for
|
|
66
|
-
* workflow_node_completed events with termination "completed" and returns them WITH their
|
|
67
|
-
* result-borne control signals (W-1) — resumeWorkflow lowers these to the kernel's
|
|
68
|
-
* `resumed_outcomes` so a classifier re-prunes and a loop stop is honored, and re-seeds the
|
|
69
|
-
* driver's outputs map from the persisted output text.
|
|
70
|
-
*/
|
|
71
|
-
export declare function recoverWorkflowNodeOutcomes(events: Array<{
|
|
72
|
-
seq: number;
|
|
73
|
-
event: SessionEvent;
|
|
74
|
-
}>): RecoveredNodeOutcome[];
|
|
75
|
-
/** R3-1: build workflow_nodes_submitted for persistence after a runtime submission, so resume can
|
|
76
|
-
* re-apply it. `nodes` is the kernel-shape (snake_case) submitted node array. */
|
|
53
|
+
/** Build the audit projection emitted after a runtime workflow submission. */
|
|
77
54
|
export declare function buildWorkflowNodesSubmittedEvent(input: {
|
|
78
55
|
turn: number;
|
|
79
56
|
nodes: Record<string, unknown>[];
|
|
@@ -82,14 +59,3 @@ export declare function buildWorkflowNodesSubmittedEvent(input: {
|
|
|
82
59
|
}): Extract<SessionEvent, {
|
|
83
60
|
kind: "workflow_nodes_submitted";
|
|
84
61
|
}>;
|
|
85
|
-
/** R3-1: recover the runtime submission batches (in order) to rebuild `resumed_submissions` for
|
|
86
|
-
* resumeWorkflow, so dynamically-appended nodes are reconstructed.
|
|
87
|
-
* `submitters` is parallel to `submissions` (undefined = host/bootstrap submission). */
|
|
88
|
-
export declare function recoverSubmittedWorkflowNodes(events: Array<{
|
|
89
|
-
seq: number;
|
|
90
|
-
event: SessionEvent;
|
|
91
|
-
}>): {
|
|
92
|
-
submissions: Record<string, unknown>[][];
|
|
93
|
-
bases: number[];
|
|
94
|
-
submitters: Array<string | undefined>;
|
|
95
|
-
};
|
|
@@ -48,8 +48,7 @@ export function buildRunTerminalEvent(input) {
|
|
|
48
48
|
total_tokens: Math.max(0, input.totalTokens),
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
-
/** Build
|
|
52
|
-
* result-borne control signals + output so resume replays control flow and re-seeds outputs. */
|
|
51
|
+
/** Build the audit projection emitted after a workflow node finishes. */
|
|
53
52
|
export function buildWorkflowNodeCompletedEvent(input) {
|
|
54
53
|
return {
|
|
55
54
|
kind: "workflow_node_completed",
|
|
@@ -63,32 +62,7 @@ export function buildWorkflowNodeCompletedEvent(input) {
|
|
|
63
62
|
...(input.output ? { output: input.output } : {}),
|
|
64
63
|
};
|
|
65
64
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Recover completed workflow node records from a session event stream. Scans for
|
|
68
|
-
* workflow_node_completed events with termination "completed" and returns them WITH their
|
|
69
|
-
* result-borne control signals (W-1) — resumeWorkflow lowers these to the kernel's
|
|
70
|
-
* `resumed_outcomes` so a classifier re-prunes and a loop stop is honored, and re-seeds the
|
|
71
|
-
* driver's outputs map from the persisted output text.
|
|
72
|
-
*/
|
|
73
|
-
export function recoverWorkflowNodeOutcomes(events) {
|
|
74
|
-
const completed = [];
|
|
75
|
-
for (const { event } of events) {
|
|
76
|
-
if (event.kind === "workflow_node_completed") {
|
|
77
|
-
completed.push({
|
|
78
|
-
agentId: event.agent_id,
|
|
79
|
-
status: event.status,
|
|
80
|
-
termination: event.termination,
|
|
81
|
-
...(event.classify_branch !== undefined ? { classifyBranch: event.classify_branch } : {}),
|
|
82
|
-
...(event.tournament_winner !== undefined ? { tournamentWinner: event.tournament_winner } : {}),
|
|
83
|
-
...(event.loop_continue !== undefined ? { loopContinue: event.loop_continue } : {}),
|
|
84
|
-
...(event.output !== undefined ? { output: event.output } : {}),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return completed;
|
|
89
|
-
}
|
|
90
|
-
/** R3-1: build workflow_nodes_submitted for persistence after a runtime submission, so resume can
|
|
91
|
-
* re-apply it. `nodes` is the kernel-shape (snake_case) submitted node array. */
|
|
65
|
+
/** Build the audit projection emitted after a runtime workflow submission. */
|
|
92
66
|
export function buildWorkflowNodesSubmittedEvent(input) {
|
|
93
67
|
return {
|
|
94
68
|
kind: "workflow_nodes_submitted",
|
|
@@ -98,22 +72,3 @@ export function buildWorkflowNodesSubmittedEvent(input) {
|
|
|
98
72
|
...(input.submitterAgentId !== undefined ? { submitter_agent_id: input.submitterAgentId } : {}),
|
|
99
73
|
};
|
|
100
74
|
}
|
|
101
|
-
/** R3-1: recover the runtime submission batches (in order) to rebuild `resumed_submissions` for
|
|
102
|
-
* resumeWorkflow, so dynamically-appended nodes are reconstructed.
|
|
103
|
-
* `submitters` is parallel to `submissions` (undefined = host/bootstrap submission). */
|
|
104
|
-
export function recoverSubmittedWorkflowNodes(events) {
|
|
105
|
-
const submissions = [];
|
|
106
|
-
const bases = [];
|
|
107
|
-
const submitters = [];
|
|
108
|
-
for (const { event } of events) {
|
|
109
|
-
if (event.kind === "workflow_nodes_submitted") {
|
|
110
|
-
submissions.push(event.nodes);
|
|
111
|
-
submitters.push(event.submitter_agent_id);
|
|
112
|
-
if (event.base_index === undefined) {
|
|
113
|
-
throw new Error("workflow_nodes_submitted is missing required base_index");
|
|
114
|
-
}
|
|
115
|
-
bases.push(event.base_index);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return { submissions, bases, submitters };
|
|
119
|
-
}
|
|
@@ -39,5 +39,5 @@ export declare class SubAgentOrchestrator {
|
|
|
39
39
|
run(ctx: SubAgentRunContext): Promise<SubAgentResult>;
|
|
40
40
|
}
|
|
41
41
|
export declare const defaultSubAgentOrchestrator: SubAgentOrchestrator;
|
|
42
|
-
/**
|
|
43
|
-
export declare function spawnStandalone(parentOpts: RuntimeOptions, parentSessionId: string, spec: AgentRunSpec, orchestrator?: SubAgentOrchestrator): Promise<SubAgentResult>;
|
|
42
|
+
/** Canonical single-node root workflow for harness / coordinator use. */
|
|
43
|
+
export declare function spawnStandalone(parentOpts: RuntimeOptions, parentSessionId: string, spec: AgentRunSpec, orchestrator?: SubAgentOrchestrator, contextInput?: string): Promise<SubAgentResult>;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { agentRunSpecToKernel, findSpawnProcessObservation, spawnObservationToManifest } from "./types/agent.js";
|
|
2
1
|
import { FilteredExecutionPlane } from "./filtered-plane.js";
|
|
3
|
-
import { kernelApply } from "./kernel-step.js";
|
|
4
2
|
/** #2-B-ii: bridge a parent AbortSignal to a child runner's `interrupt()` (fires now if already aborted). */
|
|
5
3
|
function linkAbort(signal, runner) {
|
|
6
4
|
if (!signal)
|
|
@@ -121,8 +119,6 @@ export class SubAgentOrchestrator {
|
|
|
121
119
|
dreamStore: metaTools.has("memory") ? ctx.parentOpts.dreamStore : undefined,
|
|
122
120
|
knowledgeSource: metaTools.has("knowledge") ? ctx.parentOpts.knowledgeSource : undefined,
|
|
123
121
|
enablePlanTool: metaTools.has("update_plan") ? ctx.parentOpts.enablePlanTool : undefined,
|
|
124
|
-
// M5 v2.1: a workflow node's `start_workflow` flattens to the parent kernel (no nested pivot).
|
|
125
|
-
isWorkflowNode: ctx.isWorkflowNode,
|
|
126
122
|
// The child runs under ITS OWN spec, never the parent's: the spread above would otherwise
|
|
127
123
|
// leak the parent's `runSpec` (identity, capability filter — and an armed `loopRound`,
|
|
128
124
|
// giving every child a phantom pace tool). A loop-node iteration carries its own minimal
|
|
@@ -167,42 +163,42 @@ export class SubAgentOrchestrator {
|
|
|
167
163
|
}
|
|
168
164
|
}
|
|
169
165
|
export const defaultSubAgentOrchestrator = new SubAgentOrchestrator();
|
|
170
|
-
/**
|
|
171
|
-
export async function spawnStandalone(parentOpts, parentSessionId, spec, orchestrator = defaultSubAgentOrchestrator) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
maxTokens: parentOpts.maxTokens,
|
|
175
|
-
maxTurns: parentOpts.maxTurns ?? 25,
|
|
176
|
-
timeoutMs: parentOpts.timeoutMs,
|
|
177
|
-
});
|
|
178
|
-
const pending = [];
|
|
179
|
-
kernelApply(runtime, pending, { kind: "start_run", task: { goal: "coordinator", criteria: [] } });
|
|
180
|
-
const observations = kernelApply(runtime, pending, {
|
|
181
|
-
kind: "spawn_sub_agent",
|
|
182
|
-
spec: agentRunSpecToKernel(spec),
|
|
183
|
-
parent_session_id: parentSessionId,
|
|
184
|
-
});
|
|
185
|
-
const spawned = findSpawnProcessObservation(observations);
|
|
186
|
-
if (!spawned) {
|
|
187
|
-
throw new Error("spawn_sub_agent did not emit agent_process_changed");
|
|
166
|
+
/** Canonical single-node root workflow for harness / coordinator use. */
|
|
167
|
+
export async function spawnStandalone(parentOpts, parentSessionId, spec, orchestrator = defaultSubAgentOrchestrator, contextInput) {
|
|
168
|
+
if (spec.tokenBudget !== undefined || spec.maxTurns !== undefined || spec.maxWallMs !== undefined) {
|
|
169
|
+
throw new Error("spawnStandalone cannot represent per-node resource caps under canonical ABI v3");
|
|
188
170
|
}
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
171
|
+
const { RuntimeRunner } = await import("./runner.js");
|
|
172
|
+
let captured;
|
|
173
|
+
const bridge = {
|
|
174
|
+
async run(ctx) {
|
|
175
|
+
const child = await orchestrator.run({
|
|
176
|
+
...ctx,
|
|
177
|
+
parentSessionId,
|
|
178
|
+
spec,
|
|
179
|
+
manifest: {
|
|
180
|
+
...ctx.manifest,
|
|
181
|
+
agent_id: spec.identity.agentId,
|
|
182
|
+
parent_session_id: parentSessionId,
|
|
183
|
+
},
|
|
184
|
+
...(contextInput ? { contextInput } : {}),
|
|
185
|
+
});
|
|
186
|
+
captured = child;
|
|
187
|
+
return { ...child, agentId: ctx.manifest.agent_id };
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
const runner = new RuntimeRunner({ ...parentOpts, subAgentOrchestrator: bridge });
|
|
191
|
+
const outcome = await runner.runWorkflow({
|
|
192
|
+
nodes: [{
|
|
193
|
+
task: spec.goal,
|
|
194
|
+
role: spec.role,
|
|
195
|
+
isolation: spec.isolation,
|
|
196
|
+
...(spec.modelHint ? { modelHint: spec.modelHint } : {}),
|
|
197
|
+
}],
|
|
207
198
|
});
|
|
199
|
+
if (outcome.rejection)
|
|
200
|
+
throw new Error(`canonical standalone spawn rejected: ${outcome.rejection.reason}`);
|
|
201
|
+
if (!captured)
|
|
202
|
+
throw new Error("canonical standalone spawn completed without a child result");
|
|
203
|
+
return captured;
|
|
208
204
|
}
|
|
@@ -41,6 +41,14 @@ export interface AgentRunSpec {
|
|
|
41
41
|
/** ③ loop-agent rounds: presence makes this run ONE round of a paced loop (gates the
|
|
42
42
|
* kernel `pace` meta-tool and arms the pacing trap). */
|
|
43
43
|
loopRound?: LoopRoundSpec;
|
|
44
|
+
/** Exposure baseline — the PRE-ACTIVATION tool surface *under* the `capabilityFilter` ceiling.
|
|
45
|
+
* The ceiling bounds what this run may EVER expose; the baseline selects which of those are
|
|
46
|
+
* advertised before any skill activates, so `exposed = meta ∪ ((baseline ∪ stableCore ∪
|
|
47
|
+
* ⋃ activeSkills.allowed_tools) ∩ ceiling)`. That makes narrow→wide progressive disclosure
|
|
48
|
+
* expressible. Absent ⇒ legacy behavior. `[]` is meaningful and distinct from absent: the minimal
|
|
49
|
+
* surface (meta-tools + stable-core only). Entries outside the ceiling silently intersect away.
|
|
50
|
+
* Lowered from `RuntimeOptions.baselineToolIds`. */
|
|
51
|
+
exposureBaseline?: string[];
|
|
44
52
|
/** M1/G3: per-agent model preference; the host resolves it via `RuntimeOptions.providerFor`.
|
|
45
53
|
* Host-side routing only — not sent to the kernel. */
|
|
46
54
|
modelHint?: string;
|
|
@@ -71,12 +79,6 @@ export interface AgentProcessChangedObservation {
|
|
|
71
79
|
permitted_capability_ids?: string[];
|
|
72
80
|
result_termination?: string;
|
|
73
81
|
}
|
|
74
|
-
/** Map kernel spawn observation → host manifest. */
|
|
75
|
-
export declare function spawnObservationToManifest(obs: AgentProcessChangedObservation | Record<string, unknown>, spec: AgentRunSpec, parentSessionId: string): AgentProcessChangedObservation;
|
|
76
|
-
export declare function findSpawnProcessObservation(observations: Array<{
|
|
77
|
-
kind: string;
|
|
78
|
-
agent_id?: string;
|
|
79
|
-
}>): AgentProcessChangedObservation | undefined;
|
|
80
82
|
export interface LoopResult {
|
|
81
83
|
termination: TerminationReason | string;
|
|
82
84
|
finalMessage?: Message;
|
|
@@ -125,6 +127,18 @@ export declare function milestoneCheckResultToKernel(result: MilestoneCheckResul
|
|
|
125
127
|
export declare function subAgentResultToKernel(result: SubAgentResult): Record<string, unknown>;
|
|
126
128
|
export declare function milestoneCheckPass(phaseId: string): MilestoneCheckResult;
|
|
127
129
|
export declare function milestoneCheckFail(phaseId: string, reason: string): MilestoneCheckResult;
|
|
130
|
+
/**
|
|
131
|
+
* R-B27: the conservative resolution the runner feeds back when an `evaluate_milestone` effect
|
|
132
|
+
* arrives with no phase `verifier` and no host `onMilestoneEvaluate` hook. Nothing can attest the
|
|
133
|
+
* phase, but the kernel is already holding the effect in its pending table — returning without an
|
|
134
|
+
* answer leaves a dangling effect that a logical-checkpoint recovery cannot resolve. The current
|
|
135
|
+
* `MilestoneResult` wire has no error field, so "could not be verified" is expressed with the
|
|
136
|
+
* shape the wire does have: `passed: false` (the phase does not advance — fail-closed).
|
|
137
|
+
*
|
|
138
|
+
* The string is part of the cross-SDK contract: Node (`node/src/types/agent.ts`),
|
|
139
|
+
* Python (`deepstrike/types/agent.py`) and Rust must feed back the byte-identical reason.
|
|
140
|
+
*/
|
|
141
|
+
export declare const MILESTONE_UNVERIFIED_REASON = "milestone unverified: no verifier configured and no host evaluation hook (fail-closed)";
|
|
128
142
|
/** A task for a workflow node: a full object, or a bare goal string. */
|
|
129
143
|
export type WorkflowTaskSpec = {
|
|
130
144
|
goal: string;
|
|
@@ -267,19 +281,10 @@ export interface WorkflowBudget {
|
|
|
267
281
|
}
|
|
268
282
|
/** G4: a concise budget note appended to a coordinator node's goal. "" when nothing is bounded. */
|
|
269
283
|
export declare function workflowBudgetNote(budget: WorkflowBudget | undefined): string;
|
|
270
|
-
/** Map one host `WorkflowNodeSpec` to its snake_case
|
|
284
|
+
/** Map one host `WorkflowNodeSpec` to its snake_case canonical JSON. Shared by the workflow root and
|
|
271
285
|
* `submit_workflow_nodes` (R3-1) so the two encodings never drift. */
|
|
272
286
|
export declare function workflowNodeSpecToKernel(n: WorkflowNodeSpec): Record<string, unknown>;
|
|
273
287
|
export declare function workflowSpecToKernel(spec: WorkflowSpec): Record<string, unknown>;
|
|
274
|
-
/** R3-1: map a batch of host nodes to the `submit_workflow_nodes` kernel event body. G1: pass
|
|
275
|
-
* `submitterAgentId` so the kernel enforces no-privilege-escalation (quarantined submitter ⇒ its
|
|
276
|
-
* nodes coerced to quarantined). Omitted ⇒ no coercion. */
|
|
277
|
-
export declare function submitWorkflowNodesToKernel(nodes: WorkflowNodeSpec[], submitterAgentId?: string): Record<string, unknown>;
|
|
278
|
-
/** M5/G1: map an agent-authored spec to the `submit_workflow` kernel event body (the agent-reachable
|
|
279
|
-
* `Syscall::LoadWorkflow`). The kernel bootstraps the DAG when none is active, else flattens onto it.
|
|
280
|
-
* `parentSessionId` seeds child session ids on bootstrap; `submitterAgentId` carries G1 trust coercion
|
|
281
|
-
* on the flatten case. */
|
|
282
|
-
export declare function submitWorkflowToKernel(spec: WorkflowSpec, parentSessionId: string, submitterAgentId?: string): Record<string, unknown>;
|
|
283
288
|
export declare const submitWorkflowNodesTool: ToolSchema;
|
|
284
289
|
/** M5 v1 (flatten): the tool an agent calls to author a sub-workflow — a cohesive DAG of nodes
|
|
285
290
|
* composed onto the running workflow. Lowers to the same append path as `submit_workflow_nodes`
|
|
@@ -1,22 +1,4 @@
|
|
|
1
1
|
import { getKernel } from "../kernel.js";
|
|
2
|
-
/** Map kernel spawn observation → host manifest. */
|
|
3
|
-
export function spawnObservationToManifest(obs, spec, parentSessionId) {
|
|
4
|
-
const o = obs;
|
|
5
|
-
return {
|
|
6
|
-
kind: "agent_process_changed",
|
|
7
|
-
turn: o.turn,
|
|
8
|
-
agent_id: String(o.agent_id ?? spec.identity.agentId),
|
|
9
|
-
parent_session_id: String(o.parent_session_id ?? parentSessionId),
|
|
10
|
-
role: String(o.role ?? spec.role),
|
|
11
|
-
isolation: String(o.isolation ?? spec.isolation ?? "shared"),
|
|
12
|
-
context_inheritance: String(o.context_inheritance ?? "none"),
|
|
13
|
-
permitted_capability_ids: o.permitted_capability_ids ?? [],
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
export function findSpawnProcessObservation(observations) {
|
|
17
|
-
const hit = observations.find(o => o.kind === "agent_process_changed" && typeof o.agent_id === "string");
|
|
18
|
-
return hit;
|
|
19
|
-
}
|
|
20
2
|
export function agentIdentitySub(agentId, sessionId, parentSessionId) {
|
|
21
3
|
return {
|
|
22
4
|
agentId,
|
|
@@ -54,6 +36,11 @@ export function agentRunSpecToKernel(spec) {
|
|
|
54
36
|
...(spec.loopRound.defaultAction !== undefined ? { default_action: spec.loopRound.defaultAction } : {}),
|
|
55
37
|
};
|
|
56
38
|
}
|
|
39
|
+
// Exposure baseline: `undefined` ⇒ omit the field entirely (kernel `None` = legacy behavior);
|
|
40
|
+
// `[]` ⇒ send `[]` (kernel `Some([])` = the minimal surface). The unset/minimal distinction is
|
|
41
|
+
// load-bearing, so this is deliberately NOT the `length > 0` idiom `allowedToolIds` uses.
|
|
42
|
+
if (spec.exposureBaseline !== undefined)
|
|
43
|
+
out.exposure_baseline = [...spec.exposureBaseline];
|
|
57
44
|
return out;
|
|
58
45
|
}
|
|
59
46
|
export function milestoneContractToKernel(contract) {
|
|
@@ -91,6 +78,9 @@ export function subAgentResultToKernel(result) {
|
|
|
91
78
|
const finalMessage = result.result.finalMessage;
|
|
92
79
|
return {
|
|
93
80
|
agent_id: result.agentId,
|
|
81
|
+
...(result.submittedNodes?.length
|
|
82
|
+
? { submitted_nodes: result.submittedNodes.map(workflowNodeSpecToKernel) }
|
|
83
|
+
: {}),
|
|
94
84
|
result: {
|
|
95
85
|
termination: result.result.termination,
|
|
96
86
|
final_message: finalMessage
|
|
@@ -121,6 +111,18 @@ export function milestoneCheckPass(phaseId) {
|
|
|
121
111
|
export function milestoneCheckFail(phaseId, reason) {
|
|
122
112
|
return { phaseId, passed: false, reason };
|
|
123
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* R-B27: the conservative resolution the runner feeds back when an `evaluate_milestone` effect
|
|
116
|
+
* arrives with no phase `verifier` and no host `onMilestoneEvaluate` hook. Nothing can attest the
|
|
117
|
+
* phase, but the kernel is already holding the effect in its pending table — returning without an
|
|
118
|
+
* answer leaves a dangling effect that a logical-checkpoint recovery cannot resolve. The current
|
|
119
|
+
* `MilestoneResult` wire has no error field, so "could not be verified" is expressed with the
|
|
120
|
+
* shape the wire does have: `passed: false` (the phase does not advance — fail-closed).
|
|
121
|
+
*
|
|
122
|
+
* The string is part of the cross-SDK contract: Node (`node/src/types/agent.ts`),
|
|
123
|
+
* Python (`deepstrike/types/agent.py`) and Rust must feed back the byte-identical reason.
|
|
124
|
+
*/
|
|
125
|
+
export const MILESTONE_UNVERIFIED_REASON = "milestone unverified: no verifier configured and no host evaluation hook (fail-closed)";
|
|
124
126
|
export function workflowNodeStatusFromTermination(termination) {
|
|
125
127
|
if (termination === "completed")
|
|
126
128
|
return "completed";
|
|
@@ -197,7 +199,7 @@ function nodeKindToKernel(n) {
|
|
|
197
199
|
return { type: "tournament", entrants: n.tournament.entrants.map(workflowTaskToKernel) };
|
|
198
200
|
return undefined;
|
|
199
201
|
}
|
|
200
|
-
/** Map one host `WorkflowNodeSpec` to its snake_case
|
|
202
|
+
/** Map one host `WorkflowNodeSpec` to its snake_case canonical JSON. Shared by the workflow root and
|
|
201
203
|
* `submit_workflow_nodes` (R3-1) so the two encodings never drift. */
|
|
202
204
|
export function workflowNodeSpecToKernel(n) {
|
|
203
205
|
const kind = nodeKindToKernel(n);
|
|
@@ -223,28 +225,6 @@ export function workflowNodeSpecToKernel(n) {
|
|
|
223
225
|
export function workflowSpecToKernel(spec) {
|
|
224
226
|
return { nodes: spec.nodes.map(workflowNodeSpecToKernel) };
|
|
225
227
|
}
|
|
226
|
-
/** R3-1: map a batch of host nodes to the `submit_workflow_nodes` kernel event body. G1: pass
|
|
227
|
-
* `submitterAgentId` so the kernel enforces no-privilege-escalation (quarantined submitter ⇒ its
|
|
228
|
-
* nodes coerced to quarantined). Omitted ⇒ no coercion. */
|
|
229
|
-
export function submitWorkflowNodesToKernel(nodes, submitterAgentId) {
|
|
230
|
-
return {
|
|
231
|
-
kind: "submit_workflow_nodes",
|
|
232
|
-
nodes: nodes.map(workflowNodeSpecToKernel),
|
|
233
|
-
...(submitterAgentId ? { submitter_agent_id: submitterAgentId } : {}),
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
/** M5/G1: map an agent-authored spec to the `submit_workflow` kernel event body (the agent-reachable
|
|
237
|
-
* `Syscall::LoadWorkflow`). The kernel bootstraps the DAG when none is active, else flattens onto it.
|
|
238
|
-
* `parentSessionId` seeds child session ids on bootstrap; `submitterAgentId` carries G1 trust coercion
|
|
239
|
-
* on the flatten case. */
|
|
240
|
-
export function submitWorkflowToKernel(spec, parentSessionId, submitterAgentId) {
|
|
241
|
-
return {
|
|
242
|
-
kind: "submit_workflow",
|
|
243
|
-
spec: workflowSpecToKernel(spec),
|
|
244
|
-
parent_session_id: parentSessionId,
|
|
245
|
-
...(submitterAgentId ? { submitter_agent_id: submitterAgentId } : {}),
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
228
|
/** R3-1: the tool a workflow-coordinator node's agent calls to append work to the running DAG. */
|
|
249
229
|
/** Shared JSON-Schema for a workflow-node batch (a DAG). Used by both `submit_workflow_nodes`
|
|
250
230
|
* (append) and `start_workflow` (M5 v1: author a sub-workflow), so the two tools never drift. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstrike/wasm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.51",
|
|
4
4
|
"description": "DeepStrike WASM SDK — browser, Cloudflare Workers, Deno Deploy",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build:wasm": "wasm-pack build ../crates/deepstrike-wasm --target bundler --out-dir ../../wasm/pkg",
|
|
14
14
|
"build": "tsc",
|
|
15
|
-
"test": "node --experimental-vm-modules node_modules/.bin/jest"
|
|
15
|
+
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
|
16
|
+
"test:canonical-binding": "wasm-pack build ../crates/deepstrike-wasm --target nodejs --dev --out-dir ../../wasm/pkg-node && node tests/canonical-binding.node.cjs"
|
|
16
17
|
},
|
|
17
18
|
"dependencies": {
|
|
18
|
-
"@deepstrike/wasm-kernel": "0.2.
|
|
19
|
+
"@deepstrike/wasm-kernel": "0.2.51"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
21
22
|
"@types/jest": "^30.0.0",
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { KernelRuntimeHandle } from "./kernel-step.js";
|
|
2
|
-
import { type KernelOperationCursor, type KernelOperationGenesis, type KernelTransaction } from "./kernel-transaction-log.js";
|
|
3
|
-
export interface KernelRebuildResult {
|
|
4
|
-
runtime: KernelRuntimeHandle;
|
|
5
|
-
cursor: KernelOperationCursor;
|
|
6
|
-
}
|
|
7
|
-
/** Deterministically fold an authoritative stream into a fresh WASM runtime. */
|
|
8
|
-
export declare function rebuildKernelRuntime(runtime: KernelRuntimeHandle, genesis: KernelOperationGenesis, transactions: readonly KernelTransaction[]): Promise<KernelRebuildResult>;
|