@deepstrike/wasm 0.2.79 → 0.2.81
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 +1 -0
- package/dist/index.js +1 -0
- package/dist/runtime/canonical-kernel-step.d.ts +6 -0
- package/dist/runtime/canonical-kernel-step.js +40 -0
- package/dist/runtime/dynamic-workflow.d.ts +114 -0
- package/dist/runtime/dynamic-workflow.js +190 -0
- package/dist/runtime/kernel-event-log.js +5 -0
- package/dist/runtime/kernel-step.d.ts +6 -1
- package/dist/runtime/kernel-step.js +7 -0
- package/dist/runtime/runner.d.ts +13 -0
- package/dist/runtime/runner.js +103 -0
- package/dist/runtime/session-event-registry.generated.d.ts +1 -1
- package/dist/runtime/session-event-registry.generated.js +1 -1
- package/dist/runtime/session-log.d.ts +34 -1
- package/dist/runtime/types/agent.d.ts +6 -0
- package/dist/types.d.ts +22 -0
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -58,3 +58,4 @@ export { memoryFetchRecall } from "./memory/recall-projection.js";
|
|
|
58
58
|
export type { MemoryCandidate, MemoryCandidateReceipt, MemoryCandidateSink, MemoryCandidateSource } from "./memory/candidate-sink.js";
|
|
59
59
|
export { resolveMemoryRuntimePolicy } from "./memory/runtime-policy.js";
|
|
60
60
|
export type { MemoryRuntimePolicy, MemoryRetrievalPolicy, MemoryWritePolicy, ResolvedMemoryRuntimePolicy } from "./memory/runtime-policy.js";
|
|
61
|
+
export { DynamicWorkflowContextImpl, DynamicWorkflowLimitError, dynamicAgentTask, type DynamicWorkflowAgentOptions, type DynamicWorkflowAgentRequest, type DynamicWorkflowAgentResult, type DynamicWorkflowBlackboard, type DynamicWorkflowContext, type DynamicWorkflowLimits, type DynamicWorkflowProgress, type DynamicWorkflowRun, } from "./runtime/dynamic-workflow.js";
|
package/dist/index.js
CHANGED
|
@@ -31,3 +31,4 @@ export { DurableContentError, decodeDurableContent, decodeDurableToolResult, enc
|
|
|
31
31
|
export { InMemoryMemoryCandidateSink, memoryCandidateDuplicateReason, memoryCandidateId, memoryCandidatePayloadDigest } from "./memory/candidate-sink.js";
|
|
32
32
|
export { memoryFetchRecall } from "./memory/recall-projection.js";
|
|
33
33
|
export { resolveMemoryRuntimePolicy } from "./memory/runtime-policy.js";
|
|
34
|
+
export { DynamicWorkflowContextImpl, DynamicWorkflowLimitError, dynamicAgentTask, } from "./runtime/dynamic-workflow.js";
|
|
@@ -155,6 +155,12 @@ export declare class CanonicalRunnerRuntime {
|
|
|
155
155
|
resumeAction(): KernelRunnerAction | null;
|
|
156
156
|
startAgent(taskValue: Record<string, unknown>, runSpecValue?: Record<string, unknown>): Promise<KernelRunnerAction | null>;
|
|
157
157
|
startWorkflow(specValue: Record<string, unknown>): Promise<KernelRunnerAction | null>;
|
|
158
|
+
/** Plan task 6.3 · open a dynamic workflow root: the host program appends nodes to it. */
|
|
159
|
+
startDynamicWorkflow(): Promise<KernelRunnerAction | null>;
|
|
160
|
+
/** Append host-program nodes to the active dynamic workflow root. */
|
|
161
|
+
appendWorkflowNodes(specValue: Record<string, unknown>, plan?: Record<string, unknown>): Promise<KernelRunnerAction | null>;
|
|
162
|
+
/** Close the dynamic workflow root so the kernel commits its terminal. */
|
|
163
|
+
completeDynamicWorkflow(): Promise<KernelRunnerAction | null>;
|
|
158
164
|
private appendBase;
|
|
159
165
|
applyHostEvent(event: Record<string, unknown>): Promise<KernelRunnerAction | null>;
|
|
160
166
|
private ensureConfigured;
|
|
@@ -532,6 +532,7 @@ function canonicalProviderMessage(raw, appendBase = 0) {
|
|
|
532
532
|
}
|
|
533
533
|
: {}),
|
|
534
534
|
...(raw.token_count !== undefined ? { tokens: Number(raw.token_count) } : {}),
|
|
535
|
+
...(raw.replay && typeof raw.replay === "object" ? { replay: raw.replay } : {}),
|
|
535
536
|
};
|
|
536
537
|
}
|
|
537
538
|
function canonicalProviderToolArguments(name, argumentsValue, appendBase) {
|
|
@@ -913,6 +914,36 @@ export class CanonicalRunnerRuntime {
|
|
|
913
914
|
this.workflowNodeCount = spec.nodes.length;
|
|
914
915
|
return action;
|
|
915
916
|
}
|
|
917
|
+
/** Plan task 6.3 · open a dynamic workflow root: the host program appends nodes to it. */
|
|
918
|
+
async startDynamicWorkflow() {
|
|
919
|
+
await this.ensureConfigured();
|
|
920
|
+
const action = await this.commit({
|
|
921
|
+
kind: "start_operation",
|
|
922
|
+
entry: { kind: "dynamic_workflow" },
|
|
923
|
+
initial_context: this.initialContext,
|
|
924
|
+
});
|
|
925
|
+
this.started = true;
|
|
926
|
+
this.workflowNodeCount = 0;
|
|
927
|
+
return action;
|
|
928
|
+
}
|
|
929
|
+
/** Append host-program nodes to the active dynamic workflow root. */
|
|
930
|
+
async appendWorkflowNodes(specValue, plan) {
|
|
931
|
+
await this.ensureConfigured();
|
|
932
|
+
if (!this.started)
|
|
933
|
+
throw new Error("cannot append dynamic workflow nodes before operation start");
|
|
934
|
+
return this.commit({
|
|
935
|
+
kind: "host_control",
|
|
936
|
+
command: {
|
|
937
|
+
kind: "append_workflow_nodes",
|
|
938
|
+
nodes: canonicalWorkflowSpec(specValue, true, this.appendBase()).nodes,
|
|
939
|
+
...(plan ? { plan } : {}),
|
|
940
|
+
},
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
/** Close the dynamic workflow root so the kernel commits its terminal. */
|
|
944
|
+
async completeDynamicWorkflow() {
|
|
945
|
+
return this.commit({ kind: "host_control", command: { kind: "complete_dynamic_workflow" } });
|
|
946
|
+
}
|
|
916
947
|
appendBase() {
|
|
917
948
|
return this.workflowNodeCount ?? `wf-append-${crypto.randomUUID().slice(0, 8)}`;
|
|
918
949
|
}
|
|
@@ -960,6 +991,13 @@ export class CanonicalRunnerRuntime {
|
|
|
960
991
|
...(providerStopReason(event.stop_reason)
|
|
961
992
|
? { stop_reason: providerStopReason(event.stop_reason) }
|
|
962
993
|
: {}),
|
|
994
|
+
// Plan task 5.1 · what the provider did to the context server-side.
|
|
995
|
+
...(event.provider_compaction && typeof event.provider_compaction === "object"
|
|
996
|
+
? { provider_compaction: event.provider_compaction }
|
|
997
|
+
: {}),
|
|
998
|
+
...(Array.isArray(event.cleared_tool_results) && event.cleared_tool_results.length
|
|
999
|
+
? { cleared_tool_results: event.cleared_tool_results.map(String) }
|
|
1000
|
+
: {}),
|
|
963
1001
|
},
|
|
964
1002
|
},
|
|
965
1003
|
},
|
|
@@ -1271,6 +1309,7 @@ export class CanonicalRunnerRuntime {
|
|
|
1271
1309
|
...(event.key ? { key: event.key } : {}),
|
|
1272
1310
|
...(event.tokens !== undefined ? { tokens: Number(event.tokens) } : {}),
|
|
1273
1311
|
...(event.pinned ? { pinned: true } : {}),
|
|
1312
|
+
...(event.inherited_from ? { inherited_from: event.inherited_from } : {}),
|
|
1274
1313
|
}],
|
|
1275
1314
|
},
|
|
1276
1315
|
};
|
|
@@ -1569,6 +1608,7 @@ export class CanonicalRunnerRuntime {
|
|
|
1569
1608
|
...(event.key ? { key: event.key } : {}),
|
|
1570
1609
|
...(event.tokens !== undefined ? { tokens: Number(event.tokens) } : {}),
|
|
1571
1610
|
...(event.pinned ? { pinned: true } : {}),
|
|
1611
|
+
...(event.inherited_from ? { inherited_from: event.inherited_from } : {}),
|
|
1572
1612
|
});
|
|
1573
1613
|
return true;
|
|
1574
1614
|
case "set_available_skills":
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan task 6.3 · dynamic workflows: a host program decides, step by step, which agents to run.
|
|
3
|
+
*
|
|
4
|
+
* Each `agent` (or `parallelAgents` batch) becomes one submission the kernel admits onto the
|
|
5
|
+
* operation's dynamic workflow root — the kernel still owns the DAG, its budgets and every spawn —
|
|
6
|
+
* and the program awaits the typed result before deciding what comes next. The vocabulary matches
|
|
7
|
+
* the Node SDK's `runDynamicWorkflow`; replay of a saved run and the sandboxed script executors are
|
|
8
|
+
* Node-only (they need a filesystem and `node:vm`).
|
|
9
|
+
*/
|
|
10
|
+
import type { KernelAgentRole, WorkflowNodeSpec, WorkflowOutcome, WorkflowSpec } from "./types/agent.js";
|
|
11
|
+
export interface DynamicWorkflowLimits {
|
|
12
|
+
maxAgentsPerRun?: number;
|
|
13
|
+
maxConcurrentAgents?: number;
|
|
14
|
+
maxBatchSize?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface DynamicWorkflowAgentOptions {
|
|
17
|
+
label?: string;
|
|
18
|
+
role?: KernelAgentRole;
|
|
19
|
+
modelHint?: string;
|
|
20
|
+
trust?: WorkflowNodeSpec["trust"];
|
|
21
|
+
isolation?: WorkflowNodeSpec["isolation"];
|
|
22
|
+
outputSchema?: Record<string, unknown>;
|
|
23
|
+
tokenBudget?: number;
|
|
24
|
+
maxTurns?: number;
|
|
25
|
+
maxWallMs?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface DynamicWorkflowAgentRequest {
|
|
28
|
+
prompt: string;
|
|
29
|
+
options?: DynamicWorkflowAgentOptions;
|
|
30
|
+
}
|
|
31
|
+
export interface DynamicWorkflowAgentResult<T = string> {
|
|
32
|
+
value: T;
|
|
33
|
+
text: string;
|
|
34
|
+
nodeId: string;
|
|
35
|
+
status: string;
|
|
36
|
+
termination?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface DynamicWorkflowPhaseProgress {
|
|
39
|
+
name: string;
|
|
40
|
+
status: "running" | "completed" | "failed";
|
|
41
|
+
agentsStarted: number;
|
|
42
|
+
agentsCompleted: number;
|
|
43
|
+
}
|
|
44
|
+
export interface DynamicWorkflowProgress {
|
|
45
|
+
runId: string;
|
|
46
|
+
agentsStarted: number;
|
|
47
|
+
agentsCompleted: number;
|
|
48
|
+
activeAgents: number;
|
|
49
|
+
phase?: string;
|
|
50
|
+
phases: DynamicWorkflowPhaseProgress[];
|
|
51
|
+
logs: Array<{
|
|
52
|
+
message: string;
|
|
53
|
+
fields?: Record<string, unknown>;
|
|
54
|
+
}>;
|
|
55
|
+
}
|
|
56
|
+
export interface DynamicWorkflowRun<T> {
|
|
57
|
+
runId: string;
|
|
58
|
+
value: T;
|
|
59
|
+
progress: DynamicWorkflowProgress;
|
|
60
|
+
}
|
|
61
|
+
/** Plan task 6.2 · state the program shares with the agents it runs (see the Node SDK). */
|
|
62
|
+
export interface DynamicWorkflowBlackboard {
|
|
63
|
+
put(key: string, value: unknown, options?: {
|
|
64
|
+
scope?: "workflow" | "phase";
|
|
65
|
+
}): Promise<void>;
|
|
66
|
+
get(key: string): unknown;
|
|
67
|
+
delete(key: string): Promise<void>;
|
|
68
|
+
}
|
|
69
|
+
export interface DynamicWorkflowContext<TArgs extends Record<string, unknown> = Record<string, unknown>> {
|
|
70
|
+
readonly args: Readonly<TArgs>;
|
|
71
|
+
readonly progress: DynamicWorkflowProgress;
|
|
72
|
+
readonly blackboard: DynamicWorkflowBlackboard;
|
|
73
|
+
agent<T = string>(prompt: string, options?: DynamicWorkflowAgentOptions): Promise<DynamicWorkflowAgentResult<T> | null>;
|
|
74
|
+
parallelAgents<T>(items: readonly T[], worker: (item: T, index: number) => DynamicWorkflowAgentRequest): Promise<Array<DynamicWorkflowAgentResult | null>>;
|
|
75
|
+
parallel<T, R>(items: readonly T[], worker: (item: T, index: number) => Promise<R> | R): Promise<R[]>;
|
|
76
|
+
pipeline<T, R>(items: readonly T[], worker: (item: T, index: number) => Promise<R> | R): Promise<R[]>;
|
|
77
|
+
phase<T>(name: string, body: () => Promise<T> | T): Promise<T>;
|
|
78
|
+
log(message: string, fields?: Record<string, unknown>): void;
|
|
79
|
+
}
|
|
80
|
+
export declare class DynamicWorkflowLimitError extends Error {
|
|
81
|
+
readonly code: "dynamic_workflow_limit";
|
|
82
|
+
}
|
|
83
|
+
/** What the context needs from the runner: admit and drive one submission, write the blackboard. */
|
|
84
|
+
export interface DynamicWorkflowHost {
|
|
85
|
+
runWorkflow(spec: WorkflowSpec, plan: Record<string, unknown>): Promise<WorkflowOutcome>;
|
|
86
|
+
writeBlackboard(key: string, content: string | undefined): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
export declare function dynamicAgentTask(prompt: string, options?: DynamicWorkflowAgentOptions): DynamicWorkflowAgentRequest;
|
|
89
|
+
export declare class DynamicWorkflowContextImpl<TArgs extends Record<string, unknown>> implements DynamicWorkflowContext<TArgs> {
|
|
90
|
+
private readonly host;
|
|
91
|
+
readonly args: Readonly<TArgs>;
|
|
92
|
+
readonly progress: DynamicWorkflowProgress;
|
|
93
|
+
private readonly limits;
|
|
94
|
+
private readonly phaseStack;
|
|
95
|
+
private readonly board;
|
|
96
|
+
private nextNode;
|
|
97
|
+
private sequence;
|
|
98
|
+
private activeSlots;
|
|
99
|
+
private readonly waitingSlots;
|
|
100
|
+
readonly blackboard: DynamicWorkflowBlackboard;
|
|
101
|
+
constructor(host: DynamicWorkflowHost, runId: string, args: TArgs, limits?: DynamicWorkflowLimits);
|
|
102
|
+
agent<T = string>(prompt: string, options?: DynamicWorkflowAgentOptions): Promise<DynamicWorkflowAgentResult<T> | null>;
|
|
103
|
+
parallelAgents<T>(items: readonly T[], worker: (item: T, index: number) => DynamicWorkflowAgentRequest): Promise<Array<DynamicWorkflowAgentResult | null>>;
|
|
104
|
+
parallel<T, R>(items: readonly T[], worker: (item: T, index: number) => Promise<R> | R): Promise<R[]>;
|
|
105
|
+
pipeline<T, R>(items: readonly T[], worker: (item: T, index: number) => Promise<R> | R): Promise<R[]>;
|
|
106
|
+
phase<T>(name: string, body: () => Promise<T> | T): Promise<T>;
|
|
107
|
+
log(message: string, fields?: Record<string, unknown>): void;
|
|
108
|
+
private assertBatch;
|
|
109
|
+
private blackboardPut;
|
|
110
|
+
private blackboardDelete;
|
|
111
|
+
private acquireSlot;
|
|
112
|
+
private releaseSlot;
|
|
113
|
+
private runBatch;
|
|
114
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { sha256Hex } from "./sha256.js";
|
|
2
|
+
const DEFAULT_LIMITS = { maxAgentsPerRun: 64, maxConcurrentAgents: 8, maxBatchSize: 16 };
|
|
3
|
+
export class DynamicWorkflowLimitError extends Error {
|
|
4
|
+
code = "dynamic_workflow_limit";
|
|
5
|
+
}
|
|
6
|
+
export function dynamicAgentTask(prompt, options) {
|
|
7
|
+
if (!prompt.trim())
|
|
8
|
+
throw new Error("dynamic workflow agent prompt must not be empty");
|
|
9
|
+
return { prompt, ...(options ? { options } : {}) };
|
|
10
|
+
}
|
|
11
|
+
export class DynamicWorkflowContextImpl {
|
|
12
|
+
host;
|
|
13
|
+
args;
|
|
14
|
+
progress;
|
|
15
|
+
limits;
|
|
16
|
+
phaseStack = [];
|
|
17
|
+
board = new Map();
|
|
18
|
+
nextNode = 0;
|
|
19
|
+
sequence = 0;
|
|
20
|
+
activeSlots = 0;
|
|
21
|
+
waitingSlots = [];
|
|
22
|
+
blackboard = Object.freeze({
|
|
23
|
+
put: (key, value, options) => this.blackboardPut(key, value, options),
|
|
24
|
+
get: (key) => this.board.get(key)?.value,
|
|
25
|
+
delete: (key) => this.blackboardDelete(key),
|
|
26
|
+
});
|
|
27
|
+
constructor(host, runId, args, limits) {
|
|
28
|
+
this.host = host;
|
|
29
|
+
this.args = Object.freeze({ ...args });
|
|
30
|
+
this.limits = { ...DEFAULT_LIMITS, ...limits };
|
|
31
|
+
this.progress = { runId, agentsStarted: 0, agentsCompleted: 0, activeAgents: 0, phases: [], logs: [] };
|
|
32
|
+
}
|
|
33
|
+
async agent(prompt, options) {
|
|
34
|
+
return (await this.runBatch([dynamicAgentTask(prompt, options)]))[0];
|
|
35
|
+
}
|
|
36
|
+
async parallelAgents(items, worker) {
|
|
37
|
+
this.assertBatch(items.length, "parallelAgents");
|
|
38
|
+
return this.runBatch(items.map((item, index) => worker(item, index)));
|
|
39
|
+
}
|
|
40
|
+
async parallel(items, worker) {
|
|
41
|
+
this.assertBatch(items.length, "parallel");
|
|
42
|
+
return Promise.all(items.map((item, index) => worker(item, index)));
|
|
43
|
+
}
|
|
44
|
+
async pipeline(items, worker) {
|
|
45
|
+
const out = [];
|
|
46
|
+
for (let index = 0; index < items.length; index++)
|
|
47
|
+
out.push(await worker(items[index], index));
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
async phase(name, body) {
|
|
51
|
+
if (!name.trim())
|
|
52
|
+
throw new Error("dynamic workflow phase name must not be empty");
|
|
53
|
+
const phase = { name, status: "running", agentsStarted: 0, agentsCompleted: 0 };
|
|
54
|
+
this.progress.phases.push(phase);
|
|
55
|
+
this.phaseStack.push(phase);
|
|
56
|
+
this.progress.phase = name;
|
|
57
|
+
try {
|
|
58
|
+
const result = await body();
|
|
59
|
+
phase.status = "completed";
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
phase.status = "failed";
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
this.phaseStack.pop();
|
|
68
|
+
this.progress.phase = this.phaseStack.at(-1)?.name;
|
|
69
|
+
for (const [key, entry] of [...this.board]) {
|
|
70
|
+
if (entry.phase === phase)
|
|
71
|
+
await this.blackboardDelete(key);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
log(message, fields) {
|
|
76
|
+
this.progress.logs.push({ message, ...(fields ? { fields } : {}) });
|
|
77
|
+
}
|
|
78
|
+
assertBatch(size, operation) {
|
|
79
|
+
if (size > this.limits.maxBatchSize) {
|
|
80
|
+
throw new DynamicWorkflowLimitError(`${operation} exceeded maxBatchSize (${this.limits.maxBatchSize})`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async blackboardPut(key, value, options = {}) {
|
|
84
|
+
if (!/^[A-Za-z0-9_.:-]{1,128}$/.test(key)) {
|
|
85
|
+
throw new Error("dynamic workflow blackboard keys are 1-128 characters of letters, digits, _ . : -");
|
|
86
|
+
}
|
|
87
|
+
const scope = options.scope ?? "workflow";
|
|
88
|
+
if (scope !== "workflow" && scope !== "phase")
|
|
89
|
+
throw new Error(`unknown blackboard scope ${String(scope)}`);
|
|
90
|
+
const phase = scope === "phase" ? this.phaseStack.at(-1) : undefined;
|
|
91
|
+
if (scope === "phase" && !phase)
|
|
92
|
+
throw new Error("a phase-scoped blackboard entry needs an enclosing phase");
|
|
93
|
+
const content = typeof value === "string" ? value : JSON.stringify(value);
|
|
94
|
+
if (content === undefined)
|
|
95
|
+
throw new Error("dynamic workflow blackboard values must be serializable");
|
|
96
|
+
this.board.set(key, { value: typeof value === "string" ? value : JSON.parse(content), ...(phase ? { phase } : {}) });
|
|
97
|
+
await this.host.writeBlackboard(key, content);
|
|
98
|
+
}
|
|
99
|
+
async blackboardDelete(key) {
|
|
100
|
+
if (!this.board.delete(key))
|
|
101
|
+
return;
|
|
102
|
+
await this.host.writeBlackboard(key, undefined);
|
|
103
|
+
}
|
|
104
|
+
async acquireSlot() {
|
|
105
|
+
if (this.activeSlots < this.limits.maxConcurrentAgents) {
|
|
106
|
+
this.activeSlots += 1;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
await new Promise(resolve => this.waitingSlots.push(resolve));
|
|
110
|
+
}
|
|
111
|
+
releaseSlot() {
|
|
112
|
+
const next = this.waitingSlots.shift();
|
|
113
|
+
if (next)
|
|
114
|
+
next();
|
|
115
|
+
else
|
|
116
|
+
this.activeSlots -= 1;
|
|
117
|
+
}
|
|
118
|
+
async runBatch(requests) {
|
|
119
|
+
if (requests.length === 0)
|
|
120
|
+
return [];
|
|
121
|
+
if (this.progress.agentsStarted + requests.length > this.limits.maxAgentsPerRun) {
|
|
122
|
+
throw new DynamicWorkflowLimitError(`dynamic workflow exceeded maxAgentsPerRun (${this.limits.maxAgentsPerRun})`);
|
|
123
|
+
}
|
|
124
|
+
const sequence = this.sequence++;
|
|
125
|
+
const entries = requests.map(request => {
|
|
126
|
+
const options = request.options ?? {};
|
|
127
|
+
const nodeId = options.label?.trim() || `dynamic-agent-${this.nextNode++}`;
|
|
128
|
+
const fingerprint = `sha256:${sha256Hex(JSON.stringify({ prompt: request.prompt, options }))}`;
|
|
129
|
+
const node = {
|
|
130
|
+
nodeId,
|
|
131
|
+
task: request.prompt,
|
|
132
|
+
role: options.role ?? "implement",
|
|
133
|
+
...(options.modelHint ? { modelHint: options.modelHint } : {}),
|
|
134
|
+
...(options.trust ? { trust: options.trust } : {}),
|
|
135
|
+
...(options.isolation ? { isolation: options.isolation } : {}),
|
|
136
|
+
...(options.outputSchema ? { outputSchema: options.outputSchema } : {}),
|
|
137
|
+
...(options.tokenBudget !== undefined ? { tokenBudget: options.tokenBudget } : {}),
|
|
138
|
+
...(options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {}),
|
|
139
|
+
...(options.maxWallMs !== undefined ? { maxWallMs: options.maxWallMs } : {}),
|
|
140
|
+
};
|
|
141
|
+
return { nodeId, request, fingerprint, node };
|
|
142
|
+
});
|
|
143
|
+
const phase = this.phaseStack.at(-1);
|
|
144
|
+
this.progress.agentsStarted += entries.length;
|
|
145
|
+
this.progress.activeAgents += entries.length;
|
|
146
|
+
if (phase)
|
|
147
|
+
phase.agentsStarted += entries.length;
|
|
148
|
+
await this.acquireSlot();
|
|
149
|
+
let outcome;
|
|
150
|
+
try {
|
|
151
|
+
outcome = await this.host.runWorkflow({ nodes: entries.map(entry => entry.node) }, {
|
|
152
|
+
run_id: this.progress.runId,
|
|
153
|
+
sequence,
|
|
154
|
+
nodes: entries.map(entry => ({ node_id: entry.nodeId, depends_on: [], prompt_fingerprint: entry.fingerprint, replay: "executed" })),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
this.releaseSlot();
|
|
159
|
+
this.progress.activeAgents -= entries.length;
|
|
160
|
+
}
|
|
161
|
+
if (outcome.rejection)
|
|
162
|
+
throw new Error(`dynamic workflow submission rejected: ${outcome.rejection.reason}`);
|
|
163
|
+
const byNode = new Map(outcome.nodeOutcomes.map(node => [node.nodeId, node]));
|
|
164
|
+
return entries.map(entry => {
|
|
165
|
+
this.progress.agentsCompleted += 1;
|
|
166
|
+
if (phase)
|
|
167
|
+
phase.agentsCompleted += 1;
|
|
168
|
+
const node = byNode.get(entry.nodeId);
|
|
169
|
+
const text = outcome.outputs[entry.nodeId];
|
|
170
|
+
if (text === undefined && (!node || node.status !== "completed"))
|
|
171
|
+
return null;
|
|
172
|
+
let value = text ?? "";
|
|
173
|
+
if (entry.request.options?.outputSchema) {
|
|
174
|
+
try {
|
|
175
|
+
value = JSON.parse(text ?? "");
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
value = null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
value,
|
|
183
|
+
text: text ?? "",
|
|
184
|
+
nodeId: entry.nodeId,
|
|
185
|
+
status: node?.status ?? "completed",
|
|
186
|
+
...(node?.termination ? { termination: node.termination } : {}),
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -84,6 +84,11 @@ function projectKernelObservation(obs, turn, opts = {}) {
|
|
|
84
84
|
...(obs.history && obs.history.length > 0 ? { history: obs.history } : {}),
|
|
85
85
|
...(obs.history_items && obs.history_items.length > 0 ? { history_items: obs.history_items } : {}),
|
|
86
86
|
...(obs.history_items_omitted ? { history_items_omitted: obs.history_items_omitted } : {}),
|
|
87
|
+
...(obs.edges && obs.edges.length > 0 ? { edges: obs.edges } : {}),
|
|
88
|
+
...(obs.edges_omitted ? { edges_omitted: obs.edges_omitted } : {}),
|
|
89
|
+
...(obs.waste_items && obs.waste_items.length > 0 ? { waste_items: obs.waste_items } : {}),
|
|
90
|
+
...(obs.external_edges && obs.external_edges.length > 0 ? { external_edges: obs.external_edges } : {}),
|
|
91
|
+
...(obs.parent_items_used && obs.parent_items_used.length > 0 ? { parent_items_used: obs.parent_items_used } : {}),
|
|
87
92
|
};
|
|
88
93
|
case "rollbacked":
|
|
89
94
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ModelMessage, RenderedContext, ToolCall, ToolExecutionResult, ToolSchema } from "../types.js";
|
|
2
|
-
import type { ContextHistoryItemRecord, ContextItemRecord, ContextOriginTally, RollbackReason } from "./session-log.js";
|
|
2
|
+
import type { ContextExternalEdge, ContextExternalItem, ContextHistoryItemRecord, ContextItemEdge, ContextItemRecord, ContextOriginTally, RollbackReason } from "./session-log.js";
|
|
3
3
|
interface TaskUpdate {
|
|
4
4
|
plan?: string[];
|
|
5
5
|
currentStep?: number;
|
|
@@ -139,6 +139,11 @@ export interface KernelObservation {
|
|
|
139
139
|
history?: ContextOriginTally[];
|
|
140
140
|
history_items?: ContextHistoryItemRecord[];
|
|
141
141
|
history_items_omitted?: number;
|
|
142
|
+
edges?: ContextItemEdge[];
|
|
143
|
+
edges_omitted?: number;
|
|
144
|
+
waste_items?: number[];
|
|
145
|
+
external_edges?: ContextExternalEdge[];
|
|
146
|
+
parent_items_used?: ContextExternalItem[];
|
|
142
147
|
archived_count?: number;
|
|
143
148
|
turn?: number;
|
|
144
149
|
checkpoint_history_len?: number;
|
|
@@ -220,6 +220,10 @@ export function kernelMessageToSdk(raw) {
|
|
|
220
220
|
isError: raw.is_error === true,
|
|
221
221
|
}];
|
|
222
222
|
}
|
|
223
|
+
if (typeof raw.item_id === "number")
|
|
224
|
+
message.itemId = raw.item_id;
|
|
225
|
+
if (raw.replay && typeof raw.replay === "object")
|
|
226
|
+
message.providerReplay = raw.replay;
|
|
223
227
|
return message;
|
|
224
228
|
}
|
|
225
229
|
export function renderedContextToSdk(raw) {
|
|
@@ -235,5 +239,8 @@ export function renderedContextToSdk(raw) {
|
|
|
235
239
|
ctx.stateTurn = kernelMessageToSdk(rawStateTurn);
|
|
236
240
|
if (typeof frozenLen === "number")
|
|
237
241
|
ctx.frozenPrefixLen = frozenLen;
|
|
242
|
+
const estimated = raw.estimated_tokens ?? raw.estimatedTokens;
|
|
243
|
+
if (typeof estimated === "number" && estimated > 0)
|
|
244
|
+
ctx.estimatedTokens = estimated;
|
|
238
245
|
return ctx;
|
|
239
246
|
}
|
package/dist/runtime/runner.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { SessionLog, SessionEvent } from "./session-log.js";
|
|
|
9
9
|
import type { ExecutionPlane } from "./execution-plane.js";
|
|
10
10
|
import { type GovernancePolicy, type LivePolicyPatch } from "../governance.js";
|
|
11
11
|
import type { KernelJournal } from "./kernel-journal.js";
|
|
12
|
+
import { type DynamicWorkflowContext, type DynamicWorkflowLimits, type DynamicWorkflowRun } from "./dynamic-workflow.js";
|
|
12
13
|
import type { AgentRunSpec, MilestonePolicy, MilestoneContract, MilestoneCheckResult, WorkflowSpec, InheritedKnowledge, WorkflowOutcome } from "./types/agent.js";
|
|
13
14
|
import { type SubAgentOrchestrator } from "./sub-agent-orchestrator.js";
|
|
14
15
|
import { type NativeOsProfile, type OsProfileId, type SignalPolicy } from "./os-profile.js";
|
|
@@ -459,6 +460,18 @@ export declare class RuntimeRunner {
|
|
|
459
460
|
/** Standalone session id when bootstrapping (no active parent run). Defaults to a fresh uuid. */
|
|
460
461
|
sessionId?: string;
|
|
461
462
|
}): Promise<WorkflowOutcome>;
|
|
463
|
+
/**
|
|
464
|
+
* Plan task 6.3 · run a host program that decides which agents to run as it goes. Each
|
|
465
|
+
* `agent` / `parallelAgents` call is admitted by the kernel onto this operation's dynamic
|
|
466
|
+
* workflow root and driven to its typed outcome before the call returns; submissions and
|
|
467
|
+
* blackboard writes reach the kernel one at a time, in program order. Requires a standalone runner.
|
|
468
|
+
*/
|
|
469
|
+
runDynamicWorkflow<TArgs extends Record<string, unknown>, T>(program: (context: DynamicWorkflowContext<TArgs>) => Promise<T> | T, opts?: {
|
|
470
|
+
sessionId?: string;
|
|
471
|
+
runId?: string;
|
|
472
|
+
args?: TArgs;
|
|
473
|
+
limits?: DynamicWorkflowLimits;
|
|
474
|
+
}): Promise<DynamicWorkflowRun<T>>;
|
|
462
475
|
/**
|
|
463
476
|
* #2-B-ii: while a workflow batch is in flight, poll the signal source; a Critical `InterruptNow`
|
|
464
477
|
* routes through the kernel (root in `SubAgentAwait` → preempt → `AgentPreempted` + tears the
|
package/dist/runtime/runner.js
CHANGED
|
@@ -11,6 +11,7 @@ import { memoryFetchRecall } from "../memory/recall-projection.js";
|
|
|
11
11
|
import { MemoryWriter } from "../memory/writer.js";
|
|
12
12
|
import { CanonicalRunnerRuntime, canonicalKernelAction, canonicalKernelApply, isInvalidInputError, canonicalKernelMaybeAction, canonicalStartAgent, canonicalStartWorkflow, sha256 as canonicalSha256, } from "./canonical-kernel-step.js";
|
|
13
13
|
import { peekProviderReplay, seedProviderReplayFromEvents } from "./provider-replay.js";
|
|
14
|
+
import { DynamicWorkflowContextImpl, } from "./dynamic-workflow.js";
|
|
14
15
|
import { sanitizeReplayText } from "./replay-sanitize.js";
|
|
15
16
|
import { decodeDurableContent, decodeDurableToolResult, durableBlocksToToolOutput, toolOutputBlocksToDurable } from "./durable-content.js";
|
|
16
17
|
import { formatToolError } from "../tools/errors.js";
|
|
@@ -765,6 +766,7 @@ export class RuntimeRunner {
|
|
|
765
766
|
...(entry.key ? { key: entry.key } : {}),
|
|
766
767
|
tokens: entry.tokens ?? Math.max(1, Math.ceil(entry.content.length / 4)),
|
|
767
768
|
...(entry.pinned ? { pinned: true } : {}),
|
|
769
|
+
...(entry.inherited_from ? { inherited_from: entry.inherited_from } : {}),
|
|
768
770
|
});
|
|
769
771
|
}
|
|
770
772
|
if (this.opts.initialMemory) {
|
|
@@ -957,6 +959,12 @@ export class RuntimeRunner {
|
|
|
957
959
|
// I5: the kernel itself withholds statically denied tools (`hide_denied_tools`) and renders
|
|
958
960
|
// the denial note — the host sends exactly the context and surface the kernel committed.
|
|
959
961
|
const context = action.context;
|
|
962
|
+
// Plan task 1.1c · the kernel keeps provider replay with each turn's item; hand it to the
|
|
963
|
+
// provider keyed by the turn exactly as rendered, so its own lookup finds it.
|
|
964
|
+
for (const turn of context.turns) {
|
|
965
|
+
if (turn.providerReplay)
|
|
966
|
+
this.opts.provider.seedProviderReplay?.(turn, turn.providerReplay);
|
|
967
|
+
}
|
|
960
968
|
const tools = action.tools;
|
|
961
969
|
let turnTokens = 0;
|
|
962
970
|
let turnInputTokens = 0;
|
|
@@ -1161,6 +1169,21 @@ export class RuntimeRunner {
|
|
|
1161
1169
|
// P4-S1: land the attempt evidence BEFORE the kernel resolution commits — the record
|
|
1162
1170
|
// describes the transport fact, which exists regardless of what the kernel decides next.
|
|
1163
1171
|
const providerReplay = peekProviderReplay(this.opts.provider, finalText, finalToolCalls);
|
|
1172
|
+
// Plan task 1.1c · the kernel keeps the replay with the turn's item from here on.
|
|
1173
|
+
if (providerReplay !== undefined) {
|
|
1174
|
+
providerEvent.message = { ...providerEvent.message, replay: providerReplay };
|
|
1175
|
+
}
|
|
1176
|
+
// Plan task 5.1 · server-side context edits become facts the kernel absorbs.
|
|
1177
|
+
const contextEdits = this.opts.provider.takeContextEdits?.();
|
|
1178
|
+
if (contextEdits?.compaction) {
|
|
1179
|
+
providerEvent.provider_compaction = {
|
|
1180
|
+
through_item: contextEdits.compaction.throughItem,
|
|
1181
|
+
replay: contextEdits.compaction.replay,
|
|
1182
|
+
summary_tokens: contextEdits.compaction.summaryTokens,
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
if (contextEdits?.clearedToolResults?.length)
|
|
1186
|
+
providerEvent.cleared_tool_results = contextEdits.clearedToolResults;
|
|
1164
1187
|
const wireEvidence = {
|
|
1165
1188
|
protocol: this.providerRoute.protocol,
|
|
1166
1189
|
request_fingerprint: providerPlan.fingerprint,
|
|
@@ -1888,6 +1911,86 @@ export class RuntimeRunner {
|
|
|
1888
1911
|
}
|
|
1889
1912
|
}
|
|
1890
1913
|
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Plan task 6.3 · run a host program that decides which agents to run as it goes. Each
|
|
1916
|
+
* `agent` / `parallelAgents` call is admitted by the kernel onto this operation's dynamic
|
|
1917
|
+
* workflow root and driven to its typed outcome before the call returns; submissions and
|
|
1918
|
+
* blackboard writes reach the kernel one at a time, in program order. Requires a standalone runner.
|
|
1919
|
+
*/
|
|
1920
|
+
async runDynamicWorkflow(program, opts = {}) {
|
|
1921
|
+
if (this.activeKernel || this.currentSessionId) {
|
|
1922
|
+
throw new Error("dynamic workflows require a standalone RuntimeRunner");
|
|
1923
|
+
}
|
|
1924
|
+
const sessionId = opts.sessionId ?? `dw-${crypto.randomUUID()}`;
|
|
1925
|
+
const runId = opts.runId ?? crypto.randomUUID();
|
|
1926
|
+
try {
|
|
1927
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
1928
|
+
kind: "run_started",
|
|
1929
|
+
run_id: runId,
|
|
1930
|
+
goal: "dynamic-workflow",
|
|
1931
|
+
criteria: [],
|
|
1932
|
+
...(this.opts.agentId ? { agent_id: this.opts.agentId } : {}),
|
|
1933
|
+
route: this.providerRoute,
|
|
1934
|
+
});
|
|
1935
|
+
const runtime = await this.initializeWorkflowKernel(sessionId, runId);
|
|
1936
|
+
await runtime.startDynamicWorkflow();
|
|
1937
|
+
runtime.drainHostObservations();
|
|
1938
|
+
let tail = Promise.resolve();
|
|
1939
|
+
const exclusive = (work) => {
|
|
1940
|
+
const next = tail.then(work);
|
|
1941
|
+
tail = next.catch(() => undefined);
|
|
1942
|
+
return next;
|
|
1943
|
+
};
|
|
1944
|
+
const host = {
|
|
1945
|
+
runWorkflow: (spec, plan) => exclusive(async () => {
|
|
1946
|
+
const start = this.pendingObservations.length;
|
|
1947
|
+
const action = await runtime.appendWorkflowNodes(workflowSpecToKernel(spec), plan);
|
|
1948
|
+
this.pendingObservations.push(...runtime.drainHostObservations());
|
|
1949
|
+
const observations = this.pendingObservations.slice(start);
|
|
1950
|
+
const rejected = observations.find(o => o.kind === "control_request_rejected");
|
|
1951
|
+
if (!action || rejected) {
|
|
1952
|
+
return { nodeOutcomes: [], outputs: {}, rejection: { operation: "submit_workflow_nodes", reason: rejected?.reason ?? "the kernel admitted no nodes" } };
|
|
1953
|
+
}
|
|
1954
|
+
const submitted = observations.find(o => o.kind === "workflow_nodes_submitted");
|
|
1955
|
+
const base = Number.isInteger(submitted?.base) ? submitted.base : 0;
|
|
1956
|
+
const outcome = await this.driveWorkflow(action, observations, sessionId, runtime, new Map());
|
|
1957
|
+
// The kernel names appended nodes by position (wf-node<base+i>); the program by its own ids.
|
|
1958
|
+
const toHost = new Map(spec.nodes.map((node, index) => [`wf-node${base + index}`, node.nodeId ?? `wf-node${base + index}`]));
|
|
1959
|
+
return {
|
|
1960
|
+
...outcome,
|
|
1961
|
+
nodeOutcomes: outcome.nodeOutcomes.map(node => ({ ...node, nodeId: toHost.get(node.nodeId) ?? node.nodeId })),
|
|
1962
|
+
outputs: Object.fromEntries(Object.entries(outcome.outputs).map(([id, output]) => [toHost.get(id) ?? id, output])),
|
|
1963
|
+
};
|
|
1964
|
+
}),
|
|
1965
|
+
// Plan task 6.2 · a keyed kernel item on this operation; agents spawned later inherit it.
|
|
1966
|
+
writeBlackboard: (key, content) => exclusive(async () => {
|
|
1967
|
+
await this.commitKernelApply(runtime, this.pendingObservations, content === undefined
|
|
1968
|
+
? { kind: "remove_knowledge", key: `blackboard:${key}` }
|
|
1969
|
+
: { kind: "add_knowledge_message", key: `blackboard:${key}`, content, tokens: Math.max(1, Math.ceil(content.length / 4)) });
|
|
1970
|
+
}),
|
|
1971
|
+
};
|
|
1972
|
+
const context = new DynamicWorkflowContextImpl(host, runId, (opts.args ?? {}), opts.limits);
|
|
1973
|
+
const value = await program(context);
|
|
1974
|
+
const closing = await exclusive(async () => {
|
|
1975
|
+
const action = await runtime.completeDynamicWorkflow();
|
|
1976
|
+
this.pendingObservations.push(...runtime.drainHostObservations());
|
|
1977
|
+
return action;
|
|
1978
|
+
});
|
|
1979
|
+
const terminal = closing ?? runtime.resumeAction();
|
|
1980
|
+
if (!terminal || terminal.kind !== "done") {
|
|
1981
|
+
throw new Error("dynamic workflow close did not produce a terminal kernel action");
|
|
1982
|
+
}
|
|
1983
|
+
await this.appendObservations(sessionId, runtime, 0);
|
|
1984
|
+
return { runId, value, progress: context.progress };
|
|
1985
|
+
}
|
|
1986
|
+
finally {
|
|
1987
|
+
this.rejectPendingControl("the run ended before the control command was applied");
|
|
1988
|
+
this.activeKernel = null;
|
|
1989
|
+
this.currentSessionId = null;
|
|
1990
|
+
this.abortController = null;
|
|
1991
|
+
this.pendingObservations = [];
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1891
1994
|
/**
|
|
1892
1995
|
* #2-B-ii: while a workflow batch is in flight, poll the signal source; a Critical `InterruptNow`
|
|
1893
1996
|
* routes through the kernel (root in `SubAgentAwait` → preempt → `AgentPreempted` + tears the
|
|
@@ -77,7 +77,7 @@ export declare const SESSION_EVENT_REGISTRY: {
|
|
|
77
77
|
};
|
|
78
78
|
readonly context_lifecycle_reported: {
|
|
79
79
|
readonly required: readonly ["cache_loss_tokens", "knowledge_entries", "knowledge_referenced", "recall_expanded", "recalled", "regret", "turn", "waste_tokens"];
|
|
80
|
-
readonly optional: readonly ["history", "history_items", "history_items_omitted", "items"];
|
|
80
|
+
readonly optional: readonly ["edges", "edges_omitted", "external_edges", "history", "history_items", "history_items_omitted", "items", "parent_items_used", "waste_items"];
|
|
81
81
|
};
|
|
82
82
|
readonly suspended: {
|
|
83
83
|
readonly required: readonly ["reason", "turn"];
|
|
@@ -21,7 +21,7 @@ export const SESSION_EVENT_REGISTRY = {
|
|
|
21
21
|
rollbacked: { required: ["checkpoint_history_len", "turn"], optional: ["reason"] },
|
|
22
22
|
capability_changed: { required: ["added", "removed", "turn"], optional: ["capability_id", "change_kind", "mount_reason", "mounted_by", "version"] },
|
|
23
23
|
context_renewed: { required: ["handoff_ref", "sprint", "turn"], optional: [] },
|
|
24
|
-
context_lifecycle_reported: { required: ["cache_loss_tokens", "knowledge_entries", "knowledge_referenced", "recall_expanded", "recalled", "regret", "turn", "waste_tokens"], optional: ["history", "history_items", "history_items_omitted", "items"] },
|
|
24
|
+
context_lifecycle_reported: { required: ["cache_loss_tokens", "knowledge_entries", "knowledge_referenced", "recall_expanded", "recalled", "regret", "turn", "waste_tokens"], optional: ["edges", "edges_omitted", "external_edges", "history", "history_items", "history_items_omitted", "items", "parent_items_used", "waste_items"] },
|
|
25
25
|
suspended: { required: ["reason", "turn"], optional: ["pending_calls"] },
|
|
26
26
|
resumed: { required: ["turn"], optional: ["approved", "denied"] },
|
|
27
27
|
tool_gated: { required: ["call_id", "reason", "tool", "turn"], optional: [] },
|
|
@@ -53,14 +53,18 @@ export interface ContextOriginTally {
|
|
|
53
53
|
}
|
|
54
54
|
/** One knowledge item's lifecycle in a `context_lifecycle_reported` event (plan task 4.1). */
|
|
55
55
|
export interface ContextItemRecord {
|
|
56
|
+
/** The item's id, shared with history items and item-graph edges. */
|
|
57
|
+
id?: number;
|
|
56
58
|
key?: string;
|
|
57
|
-
origin: "system" | "skill" | "memory_recall" | "reference" | "tool_result" | "dependency_output" | "user" | "assistant" | "task_state" | "signal" | "provider_summary";
|
|
59
|
+
origin: "system" | "skill" | "memory_recall" | "reference" | "blackboard" | "tool_result" | "dependency_output" | "user" | "assistant" | "task_state" | "signal" | "provider_summary";
|
|
58
60
|
tokens: number;
|
|
59
61
|
value: {
|
|
60
62
|
injected_turn: number;
|
|
61
63
|
ref_count: number;
|
|
62
64
|
last_referenced_step?: number;
|
|
63
65
|
last_referenced_turn?: number;
|
|
66
|
+
strong_refs?: number;
|
|
67
|
+
lexical_refs?: number;
|
|
64
68
|
expanded?: boolean;
|
|
65
69
|
paged_in_after_evict?: boolean;
|
|
66
70
|
};
|
|
@@ -68,6 +72,26 @@ export interface ContextItemRecord {
|
|
|
68
72
|
end?: "evicted" | "expired" | "removed" | "compacted" | "renewed";
|
|
69
73
|
}
|
|
70
74
|
/** One history item's lifecycle in a `context_lifecycle_reported` event (plan task 4.1). */
|
|
75
|
+
/** One directed edge of the item graph: `from` answers, references, reads back or summarizes `to`. */
|
|
76
|
+
export interface ContextItemEdge {
|
|
77
|
+
from: number;
|
|
78
|
+
to: number;
|
|
79
|
+
relation: "answers" | "references_strong" | "references_lexical" | "reads_back" | "summarizes" | "expands";
|
|
80
|
+
turn: number;
|
|
81
|
+
}
|
|
82
|
+
/** An item of another run: a parent operation's item, or a workflow dependency's output. */
|
|
83
|
+
export interface ContextExternalItem {
|
|
84
|
+
operation?: string;
|
|
85
|
+
agent?: string;
|
|
86
|
+
item?: number;
|
|
87
|
+
}
|
|
88
|
+
/** An edge from an item of this run to an item of another run. */
|
|
89
|
+
export interface ContextExternalEdge {
|
|
90
|
+
from: number;
|
|
91
|
+
to: ContextExternalItem;
|
|
92
|
+
relation: "inherited_from" | "depends_on";
|
|
93
|
+
turn: number;
|
|
94
|
+
}
|
|
71
95
|
export interface ContextHistoryItemRecord {
|
|
72
96
|
id: number;
|
|
73
97
|
origin: ContextItemRecord["origin"];
|
|
@@ -216,6 +240,15 @@ export type SessionEvent = {
|
|
|
216
240
|
/** The latest history items one by one (at most 128); older ones are only counted. */
|
|
217
241
|
history_items?: ContextHistoryItemRecord[];
|
|
218
242
|
history_items_omitted?: number;
|
|
243
|
+
/** The item graph: resident edges, then the latest edges touching an item that left. */
|
|
244
|
+
edges?: ContextItemEdge[];
|
|
245
|
+
edges_omitted?: number;
|
|
246
|
+
/** Knowledge item ids nothing referenced or read back. */
|
|
247
|
+
waste_items?: number[];
|
|
248
|
+
/** Edges to items of other runs: inherited copies and dependency reads. */
|
|
249
|
+
external_edges?: ContextExternalEdge[];
|
|
250
|
+
/** The parent run's items whose inherited copies this run used. */
|
|
251
|
+
parent_items_used?: ContextExternalItem[];
|
|
219
252
|
} | {
|
|
220
253
|
kind: "suspended";
|
|
221
254
|
turn: number;
|
|
@@ -214,6 +214,12 @@ export interface InheritedKnowledge {
|
|
|
214
214
|
key?: string;
|
|
215
215
|
tokens?: number;
|
|
216
216
|
pinned?: boolean;
|
|
217
|
+
/** Plan task 7.4 · the parent run's item this is a copy of; reported as an `inherited_from` edge. */
|
|
218
|
+
inherited_from?: {
|
|
219
|
+
operation?: string;
|
|
220
|
+
agent?: string;
|
|
221
|
+
item?: number;
|
|
222
|
+
};
|
|
217
223
|
}
|
|
218
224
|
/** Per-node spawn descriptor carried in the `workflow_batch_spawned` observation. */
|
|
219
225
|
export interface WorkflowSpawnInfo {
|
package/dist/types.d.ts
CHANGED
|
@@ -46,6 +46,12 @@ export interface ModelMessage {
|
|
|
46
46
|
/** Multimodal parts (text + image/audio). When present, providers render these
|
|
47
47
|
* instead of the plain `content` string. */
|
|
48
48
|
contentParts?: ContentPart[];
|
|
49
|
+
/** The kernel item this rendered turn is (plan task 1.1c): stable across compaction, renewal
|
|
50
|
+
* and restore. Absent on messages the kernel did not render. */
|
|
51
|
+
itemId?: number;
|
|
52
|
+
/** Provider state the kernel keeps with this turn's item (reasoning, native blocks) and renders
|
|
53
|
+
* back with it; the runner seeds it into the provider before a call (plan task 1.1c). */
|
|
54
|
+
providerReplay?: ProviderReplay;
|
|
49
55
|
}
|
|
50
56
|
export interface ToolCall {
|
|
51
57
|
id: string;
|
|
@@ -79,6 +85,9 @@ export interface RenderedContext {
|
|
|
79
85
|
* Anthropic pins a deep cache breakpoint here instead of the rolling pair. */
|
|
80
86
|
frozenPrefixLen?: number;
|
|
81
87
|
budgetOverflow?: ContextBudgetOverflow;
|
|
88
|
+
/** The kernel's estimate of this request's input tokens, tool schemas included, calibrated
|
|
89
|
+
* against what the provider counted for earlier requests (plan task 5.1). */
|
|
90
|
+
estimatedTokens?: number;
|
|
82
91
|
}
|
|
83
92
|
export interface ContextBudgetOverflow {
|
|
84
93
|
kind: "fixed_context" | "protected_tail";
|
|
@@ -269,6 +278,15 @@ export interface PromptMeasurement {
|
|
|
269
278
|
source: MeasurementSource;
|
|
270
279
|
confidence: MeasurementConfidence;
|
|
271
280
|
}
|
|
281
|
+
/** Server-side context edits a provider reports for one call (plan task 5.1). */
|
|
282
|
+
export interface ProviderContextEdits {
|
|
283
|
+
compaction?: {
|
|
284
|
+
throughItem: number;
|
|
285
|
+
replay: ProviderReplay;
|
|
286
|
+
summaryTokens: number;
|
|
287
|
+
};
|
|
288
|
+
clearedToolResults?: string[];
|
|
289
|
+
}
|
|
272
290
|
export interface ProviderReplay {
|
|
273
291
|
provider?: string;
|
|
274
292
|
protocol: GenerationProtocol;
|
|
@@ -320,6 +338,10 @@ export interface LLMProvider {
|
|
|
320
338
|
};
|
|
321
339
|
};
|
|
322
340
|
peekProviderReplay?(message: Pick<ModelMessage, "content" | "toolCalls">): ProviderReplay | undefined;
|
|
341
|
+
/** Plan task 5.1 · what the last call did to the context server-side, for the kernel to absorb:
|
|
342
|
+
* a summary of history through `throughItem` (the `itemId` of the last rendered turn it covers)
|
|
343
|
+
* and the call ids of tool results it cleared. Read once per completed call. */
|
|
344
|
+
takeContextEdits?(): ProviderContextEdits | undefined;
|
|
323
345
|
seedProviderReplay?(message: Pick<ModelMessage, "content" | "toolCalls">, replay: ProviderReplay): void;
|
|
324
346
|
/** P4-S1 mirror: transport facts of the most recent execution (host evidence only, B7). */
|
|
325
347
|
peekTransportTelemetry?(): ProviderTransportTelemetry | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstrike/wasm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.81",
|
|
4
4
|
"description": "DeepStrike WASM SDK — browser, Cloudflare Workers, Deno Deploy",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"build:wasm": "wasm-pack build ../crates/deepstrike-wasm --target bundler --out-dir ../../wasm/pkg",
|
|
15
15
|
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
|
|
16
16
|
"test": "node --experimental-vm-modules node_modules/.bin/jest",
|
|
17
|
-
"test:canonical-binding": "wasm-pack build ../crates/deepstrike-wasm --target nodejs --dev --out-dir ../../wasm/pkg-node && node tests/canonical-binding.node.cjs && node tests/golden-fixtures-sweep.node.cjs && npm run build && node tests/boundary-p0-regressions.node.mjs && node tests/boundary-p1-regressions.node.mjs && node tests/boundary-p2-regressions.node.mjs && node tests/memory-candidate-binding.node.mjs && node tests/memory-recall-renewal.node.mjs"
|
|
17
|
+
"test:canonical-binding": "wasm-pack build ../crates/deepstrike-wasm --target nodejs --dev --out-dir ../../wasm/pkg-node && node tests/canonical-binding.node.cjs && node tests/golden-fixtures-sweep.node.cjs && npm run build && node tests/boundary-p0-regressions.node.mjs && node tests/boundary-p1-regressions.node.mjs && node tests/boundary-p2-regressions.node.mjs && node tests/memory-candidate-binding.node.mjs && node tests/memory-recall-renewal.node.mjs && node tests/dynamic-workflow.node.mjs"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@deepstrike/wasm-kernel": "0.2.
|
|
20
|
+
"@deepstrike/wasm-kernel": "0.2.81"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/jest": "^30.0.0",
|