@deepstrike/sdk 0.2.38 → 0.2.40
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 +61 -31
- package/dist/collaboration/contract.d.ts +2 -2
- package/dist/collaboration/contract.js +2 -2
- package/dist/collaboration/handoff.d.ts +2 -14
- package/dist/collaboration/handoff.js +1 -17
- package/dist/collaboration/harness.d.ts +16 -47
- package/dist/collaboration/harness.js +57 -158
- package/dist/collaboration/index.d.ts +2 -2
- package/dist/collaboration/index.js +1 -2
- package/dist/collaboration/modes/creator-verifier.js +42 -6
- package/dist/collaboration/pool.d.ts +8 -0
- package/dist/collaboration/pool.js +56 -3
- package/dist/harness/harness.d.ts +97 -97
- package/dist/harness/harness.js +190 -144
- package/dist/harness/judge.d.ts +3 -2
- package/dist/harness/judge.js +7 -3
- package/dist/harness/public.d.ts +5 -3
- package/dist/harness/public.js +3 -2
- package/dist/index.d.ts +22 -7
- package/dist/index.js +8 -1
- package/dist/kernel.d.ts +12 -39
- package/dist/memory/agent.d.ts +8 -41
- package/dist/memory/agent.js +34 -117
- package/dist/memory/extraction.d.ts +4 -0
- package/dist/memory/extraction.js +79 -0
- package/dist/memory/in-memory-store.d.ts +28 -15
- package/dist/memory/in-memory-store.js +74 -25
- package/dist/memory/protocols.d.ts +56 -56
- package/dist/memory/protocols.js +1 -0
- package/dist/memory/public.d.ts +3 -1
- package/dist/memory/public.js +1 -0
- package/dist/memory/ranking.d.ts +33 -0
- package/dist/memory/ranking.js +77 -0
- package/dist/memory/retention.d.ts +17 -0
- package/dist/memory/retention.js +54 -0
- package/dist/os/public.d.ts +3 -3
- package/dist/os/public.js +1 -1
- package/dist/providers/base.d.ts +8 -0
- package/dist/providers/base.js +0 -0
- package/dist/providers/gemini.js +12 -0
- package/dist/providers/ollama.js +3 -1
- package/dist/providers/openai-responses.js +9 -2
- package/dist/providers/profiles.d.ts +5 -3
- package/dist/providers/profiles.js +3 -3
- package/dist/runtime/context-policy.d.ts +35 -0
- package/dist/runtime/context-policy.js +66 -0
- package/dist/runtime/eval.d.ts +6 -2
- package/dist/runtime/eval.js +2 -2
- package/dist/runtime/event-stream.d.ts +9 -0
- package/dist/runtime/event-stream.js +25 -11
- package/dist/runtime/execution-plane.d.ts +5 -1
- package/dist/runtime/execution-plane.js +9 -3
- package/dist/runtime/facade.js +2 -1
- package/dist/runtime/kernel-event-log.d.ts +0 -2
- package/dist/runtime/kernel-event-log.js +61 -13
- package/dist/runtime/kernel-primitives-dashboard.js +1 -1
- package/dist/runtime/kernel-rebuild.d.ts +13 -0
- package/dist/runtime/kernel-rebuild.js +75 -0
- package/dist/runtime/kernel-step.d.ts +157 -8
- package/dist/runtime/kernel-step.js +220 -7
- package/dist/runtime/kernel-transaction-log.d.ts +61 -0
- package/dist/runtime/kernel-transaction-log.js +149 -0
- package/dist/runtime/large-result-spool.d.ts +3 -1
- package/dist/runtime/large-result-spool.js +24 -5
- package/dist/runtime/loop-driver.d.ts +1 -1
- package/dist/runtime/loop-driver.js +2 -7
- package/dist/runtime/mcp-proxy-plane.d.ts +1 -0
- package/dist/runtime/mcp-proxy-plane.js +23 -6
- package/dist/runtime/os-profile.d.ts +9 -10
- package/dist/runtime/os-profile.js +14 -10
- package/dist/runtime/os-snapshot.d.ts +19 -0
- package/dist/runtime/os-snapshot.js +33 -3
- package/dist/runtime/process-sandbox-plane.js +16 -11
- package/dist/runtime/reaction-checkpoint.d.ts +51 -0
- package/dist/runtime/reaction-checkpoint.js +83 -0
- package/dist/runtime/reactive-session.d.ts +9 -3
- package/dist/runtime/reactive-session.js +44 -14
- package/dist/runtime/reliability.d.ts +48 -0
- package/dist/runtime/reliability.js +86 -0
- package/dist/runtime/remote-vpc-plane.js +4 -3
- package/dist/runtime/run-group.d.ts +38 -35
- package/dist/runtime/run-group.js +97 -54
- package/dist/runtime/runner.d.ts +117 -78
- package/dist/runtime/runner.js +1516 -1197
- package/dist/runtime/session-log.d.ts +71 -12
- package/dist/runtime/session-log.js +192 -32
- package/dist/runtime/session-repair.d.ts +11 -7
- package/dist/runtime/session-repair.js +11 -8
- package/dist/runtime/sub-agent-orchestrator.d.ts +6 -2
- package/dist/runtime/sub-agent-orchestrator.js +45 -18
- package/dist/signals/gateway.d.ts +23 -10
- package/dist/signals/gateway.js +81 -17
- package/dist/signals/scheduled.js +0 -1
- package/dist/signals/types.d.ts +21 -13
- package/dist/skills/loader.js +12 -2
- package/dist/tools/index.d.ts +2 -0
- package/dist/types/agent.d.ts +40 -1
- package/dist/types/agent.js +61 -1
- package/dist/types.d.ts +6 -1
- package/dist/workflow/public.d.ts +2 -3
- package/dist/workflow/public.js +0 -1
- package/package.json +2 -2
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { KernelPrimitive } from "./kernel-event-log.js";
|
|
2
2
|
import type { ContentPart, ProviderReplay, ToolCall, ToolErrorKind } from "../types.js";
|
|
3
|
+
import type { MemoryRecall, MemoryScope } from "../memory/protocols.js";
|
|
4
|
+
import type { DurableAppendReceipt, KernelGenesisReceipt, KernelOperationGenesis, KernelTransaction } from "./kernel-transaction-log.js";
|
|
5
|
+
export interface KernelTransactionEntry {
|
|
6
|
+
log_seq: number;
|
|
7
|
+
transaction: KernelTransaction;
|
|
8
|
+
}
|
|
3
9
|
export type RollbackReason = {
|
|
4
10
|
kind: "fatal_tool_error";
|
|
5
11
|
tool_name: string;
|
|
@@ -79,7 +85,6 @@ export type SessionEvent = {
|
|
|
79
85
|
action?: "snip_compact" | "micro_compact" | "context_collapse" | "auto_compact";
|
|
80
86
|
summary?: string;
|
|
81
87
|
summary_tokens?: number;
|
|
82
|
-
archive_ref?: string;
|
|
83
88
|
preserved_refs?: string[];
|
|
84
89
|
} | {
|
|
85
90
|
kind: "page_out";
|
|
@@ -88,6 +93,7 @@ export type SessionEvent = {
|
|
|
88
93
|
summary?: string;
|
|
89
94
|
tier_hint?: string;
|
|
90
95
|
message_count?: number;
|
|
96
|
+
archive_ref?: string;
|
|
91
97
|
} | {
|
|
92
98
|
kind: "page_in";
|
|
93
99
|
turn: number;
|
|
@@ -137,8 +143,11 @@ export type SessionEvent = {
|
|
|
137
143
|
tool: string;
|
|
138
144
|
reason: string;
|
|
139
145
|
} | {
|
|
140
|
-
kind: "
|
|
146
|
+
kind: "signal_delivery_disposed";
|
|
141
147
|
turn: number;
|
|
148
|
+
operation_id: string;
|
|
149
|
+
delivery_id: string;
|
|
150
|
+
attempt: number;
|
|
142
151
|
signal_id: string;
|
|
143
152
|
disposition: string;
|
|
144
153
|
queue_depth: number;
|
|
@@ -146,6 +155,22 @@ export type SessionEvent = {
|
|
|
146
155
|
kind: "budget_exceeded";
|
|
147
156
|
turn: number;
|
|
148
157
|
budget: string;
|
|
158
|
+
operation_id: string;
|
|
159
|
+
reservation_id?: string;
|
|
160
|
+
} | {
|
|
161
|
+
kind: "budget_usage_reported";
|
|
162
|
+
turn: number;
|
|
163
|
+
operation_id: string;
|
|
164
|
+
reservation_id: string;
|
|
165
|
+
tokens: number;
|
|
166
|
+
subagents: number;
|
|
167
|
+
rounds: number;
|
|
168
|
+
} | {
|
|
169
|
+
kind: "operation_cancelled";
|
|
170
|
+
turn: number;
|
|
171
|
+
operation_id: string;
|
|
172
|
+
reason: "user" | "deadline" | "lease_lost" | "host_shutdown";
|
|
173
|
+
pending_call_ids: string[];
|
|
149
174
|
} | {
|
|
150
175
|
kind: "milestone_advanced";
|
|
151
176
|
turn: number;
|
|
@@ -189,37 +214,49 @@ export type SessionEvent = {
|
|
|
189
214
|
} | {
|
|
190
215
|
kind: "memory_written";
|
|
191
216
|
turn: number;
|
|
192
|
-
|
|
217
|
+
record_id: string;
|
|
218
|
+
scope: MemoryScope;
|
|
193
219
|
memory_kind: string;
|
|
220
|
+
name: string;
|
|
194
221
|
size_bytes: number;
|
|
195
222
|
} | {
|
|
196
223
|
kind: "memory_queried";
|
|
197
224
|
turn: number;
|
|
198
|
-
|
|
225
|
+
scope: MemoryScope;
|
|
226
|
+
query: string;
|
|
199
227
|
requested_k: number;
|
|
200
228
|
requires_async_response: boolean;
|
|
201
229
|
} | {
|
|
202
230
|
kind: "memory_validation_failed";
|
|
203
231
|
turn: number;
|
|
204
|
-
|
|
232
|
+
record_id: string;
|
|
233
|
+
error: string;
|
|
234
|
+
} | {
|
|
235
|
+
kind: "memory_write_failed";
|
|
236
|
+
turn: number;
|
|
237
|
+
record_id: string;
|
|
238
|
+
error: string;
|
|
239
|
+
} | {
|
|
240
|
+
kind: "memory_query_failed";
|
|
241
|
+
turn: number;
|
|
242
|
+
scope: MemoryScope;
|
|
243
|
+
query: string;
|
|
205
244
|
error: string;
|
|
206
245
|
} | {
|
|
207
246
|
kind: "memory_retrieval_result";
|
|
208
|
-
|
|
209
|
-
selection_rationale: string;
|
|
247
|
+
hits: MemoryRecall[];
|
|
210
248
|
} | {
|
|
211
249
|
kind: "workflow_node_completed";
|
|
212
250
|
turn: number;
|
|
213
251
|
agent_id: string;
|
|
252
|
+
status: import("../types/agent.js").WorkflowNodeStatus;
|
|
214
253
|
termination: string;
|
|
215
254
|
/** W-1: result-borne control signals, persisted so resume replays control flow faithfully —
|
|
216
255
|
* a classifier re-prunes its rejected branches, a recorded loop stop is honored. */
|
|
217
256
|
classify_branch?: string;
|
|
218
257
|
tournament_winner?: string;
|
|
219
258
|
loop_continue?: boolean;
|
|
220
|
-
|
|
221
|
-
* post-resume reduce/judge/dependent nodes still see their dependencies' outputs. */
|
|
222
|
-
output?: string;
|
|
259
|
+
output?: import("../types.js").Message;
|
|
223
260
|
} | {
|
|
224
261
|
kind: "workflow_nodes_submitted";
|
|
225
262
|
turn: number;
|
|
@@ -239,8 +276,7 @@ export type SessionEvent = {
|
|
|
239
276
|
} | {
|
|
240
277
|
kind: "workflow_completed";
|
|
241
278
|
turn: number;
|
|
242
|
-
|
|
243
|
-
failed: string[];
|
|
279
|
+
node_outcomes: import("../types/agent.js").KernelWorkflowNodeOutcome[];
|
|
244
280
|
total_nodes: number;
|
|
245
281
|
} | {
|
|
246
282
|
kind: "run_terminal";
|
|
@@ -283,19 +319,35 @@ export interface SessionLog {
|
|
|
283
319
|
event: SessionEvent;
|
|
284
320
|
}>>;
|
|
285
321
|
latestSeq(sessionId: string): Promise<number>;
|
|
322
|
+
appendKernelGenesis(sessionId: string, genesis: KernelOperationGenesis): Promise<KernelGenesisReceipt>;
|
|
323
|
+
readKernelGenesis(sessionId: string, operationId: string): Promise<KernelOperationGenesis | undefined>;
|
|
324
|
+
compareAndAppendKernelTransaction(sessionId: string, expectedTransactionHead: string, transaction: KernelTransaction): Promise<DurableAppendReceipt>;
|
|
325
|
+
readKernelTransactions(sessionId: string, operationId: string, fromStepSeq?: number): Promise<KernelTransactionEntry[]>;
|
|
326
|
+
kernelTransactionHead(sessionId: string, operationId: string): Promise<string | undefined>;
|
|
286
327
|
}
|
|
287
328
|
export declare class InMemorySessionLog implements SessionLog {
|
|
288
329
|
private store;
|
|
330
|
+
private seqCounters;
|
|
331
|
+
private genesisStore;
|
|
332
|
+
private transactionStore;
|
|
333
|
+
private operationKey;
|
|
334
|
+
private nextSeq;
|
|
289
335
|
append(sessionId: string, event: SessionEvent): Promise<number>;
|
|
290
336
|
read(sessionId: string, fromSeq?: number, primitiveFilter?: KernelPrimitive): Promise<Array<{
|
|
291
337
|
seq: number;
|
|
292
338
|
event: SessionEvent;
|
|
293
339
|
}>>;
|
|
294
340
|
latestSeq(sessionId: string): Promise<number>;
|
|
341
|
+
appendKernelGenesis(sessionId: string, genesis: KernelOperationGenesis): Promise<KernelGenesisReceipt>;
|
|
342
|
+
readKernelGenesis(sessionId: string, operationId: string): Promise<KernelOperationGenesis | undefined>;
|
|
343
|
+
compareAndAppendKernelTransaction(sessionId: string, expectedTransactionHead: string, transaction: KernelTransaction): Promise<DurableAppendReceipt>;
|
|
344
|
+
readKernelTransactions(sessionId: string, operationId: string, fromStepSeq?: number): Promise<KernelTransactionEntry[]>;
|
|
345
|
+
kernelTransactionHead(sessionId: string, operationId: string): Promise<string | undefined>;
|
|
295
346
|
}
|
|
296
347
|
export declare class FileSessionLog implements SessionLog {
|
|
297
348
|
private dir;
|
|
298
349
|
private seqCounters;
|
|
350
|
+
private readonly appends;
|
|
299
351
|
constructor(dir: string);
|
|
300
352
|
private path;
|
|
301
353
|
private nextSeq;
|
|
@@ -305,4 +357,11 @@ export declare class FileSessionLog implements SessionLog {
|
|
|
305
357
|
event: SessionEvent;
|
|
306
358
|
}>>;
|
|
307
359
|
latestSeq(sessionId: string): Promise<number>;
|
|
360
|
+
appendKernelGenesis(sessionId: string, genesis: KernelOperationGenesis): Promise<KernelGenesisReceipt>;
|
|
361
|
+
readKernelGenesis(sessionId: string, operationId: string): Promise<KernelOperationGenesis | undefined>;
|
|
362
|
+
compareAndAppendKernelTransaction(sessionId: string, expectedTransactionHead: string, transaction: KernelTransaction): Promise<DurableAppendReceipt>;
|
|
363
|
+
readKernelTransactions(sessionId: string, operationId: string, fromStepSeq?: number): Promise<KernelTransactionEntry[]>;
|
|
364
|
+
kernelTransactionHead(sessionId: string, operationId: string): Promise<string | undefined>;
|
|
365
|
+
private appendRecord;
|
|
366
|
+
private readRecords;
|
|
308
367
|
}
|
|
@@ -1,15 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mkdir } from "node:fs/promises";
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { access, mkdir, open as openFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { createInterface } from "node:readline";
|
|
5
5
|
import { primitiveForKind } from "./kernel-event-log.js";
|
|
6
|
+
import { KeyedSerialExecutor } from "./reliability.js";
|
|
7
|
+
import { KernelLogConflictError, KernelLogIntegrityError, verifyKernelOperationGenesis, verifyKernelTransaction, verifyKernelTransactionSuccessor, } from "./kernel-transaction-log.js";
|
|
6
8
|
export class InMemorySessionLog {
|
|
7
9
|
store = new Map();
|
|
10
|
+
seqCounters = new Map();
|
|
11
|
+
genesisStore = new Map();
|
|
12
|
+
transactionStore = new Map();
|
|
13
|
+
operationKey(sessionId, operationId) {
|
|
14
|
+
return `${sessionId}\u0000${operationId}`;
|
|
15
|
+
}
|
|
16
|
+
nextSeq(sessionId) {
|
|
17
|
+
const seq = this.seqCounters.get(sessionId) ?? 0;
|
|
18
|
+
this.seqCounters.set(sessionId, seq + 1);
|
|
19
|
+
return seq;
|
|
20
|
+
}
|
|
8
21
|
async append(sessionId, event) {
|
|
9
22
|
if (!this.store.has(sessionId))
|
|
10
23
|
this.store.set(sessionId, []);
|
|
11
24
|
const entries = this.store.get(sessionId);
|
|
12
|
-
const seq =
|
|
25
|
+
const seq = this.nextSeq(sessionId);
|
|
13
26
|
entries.push({ seq, event });
|
|
14
27
|
return seq;
|
|
15
28
|
}
|
|
@@ -24,8 +37,53 @@ export class InMemorySessionLog {
|
|
|
24
37
|
});
|
|
25
38
|
}
|
|
26
39
|
async latestSeq(sessionId) {
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
return (this.seqCounters.get(sessionId) ?? 0) - 1;
|
|
41
|
+
}
|
|
42
|
+
async appendKernelGenesis(sessionId, genesis) {
|
|
43
|
+
verifyKernelOperationGenesis(genesis);
|
|
44
|
+
const operationKey = this.operationKey(sessionId, genesis.operation_id);
|
|
45
|
+
const existing = this.genesisStore.get(operationKey);
|
|
46
|
+
if (existing) {
|
|
47
|
+
if (existing.genesis.genesis_digest !== genesis.genesis_digest) {
|
|
48
|
+
throw new KernelLogConflictError("session already has a different kernel operation genesis");
|
|
49
|
+
}
|
|
50
|
+
return { log_seq: existing.log_seq, genesis_digest: genesis.genesis_digest };
|
|
51
|
+
}
|
|
52
|
+
const log_seq = this.nextSeq(sessionId);
|
|
53
|
+
this.genesisStore.set(operationKey, { log_seq, genesis });
|
|
54
|
+
return { log_seq, genesis_digest: genesis.genesis_digest };
|
|
55
|
+
}
|
|
56
|
+
async readKernelGenesis(sessionId, operationId) {
|
|
57
|
+
return this.genesisStore.get(this.operationKey(sessionId, operationId))?.genesis;
|
|
58
|
+
}
|
|
59
|
+
async compareAndAppendKernelTransaction(sessionId, expectedTransactionHead, transaction) {
|
|
60
|
+
verifyKernelTransaction(transaction);
|
|
61
|
+
const operationKey = this.operationKey(sessionId, transaction.operation_id);
|
|
62
|
+
const genesis = this.genesisStore.get(operationKey)?.genesis;
|
|
63
|
+
if (!genesis)
|
|
64
|
+
throw new KernelLogIntegrityError("kernel transaction requires a durable genesis");
|
|
65
|
+
if (transaction.operation_id !== genesis.operation_id) {
|
|
66
|
+
throw new KernelLogIntegrityError("kernel transaction operation_id does not match genesis");
|
|
67
|
+
}
|
|
68
|
+
const head = await this.kernelTransactionHead(sessionId, transaction.operation_id);
|
|
69
|
+
if (head !== expectedTransactionHead || transaction.previous_transaction_digest !== head) {
|
|
70
|
+
throw new KernelLogConflictError("kernel transaction head changed before compare-and-append");
|
|
71
|
+
}
|
|
72
|
+
const entries = this.transactionStore.get(operationKey) ?? [];
|
|
73
|
+
verifyKernelTransactionSuccessor(entries.at(-1)?.transaction, transaction);
|
|
74
|
+
const log_seq = this.nextSeq(sessionId);
|
|
75
|
+
entries.push({ log_seq, transaction });
|
|
76
|
+
this.transactionStore.set(operationKey, entries);
|
|
77
|
+
return { log_seq, transaction_digest: transaction.transaction_digest };
|
|
78
|
+
}
|
|
79
|
+
async readKernelTransactions(sessionId, operationId, fromStepSeq = 1) {
|
|
80
|
+
return (this.transactionStore.get(this.operationKey(sessionId, operationId)) ?? []).filter(entry => entry.transaction.step_seq >= fromStepSeq);
|
|
81
|
+
}
|
|
82
|
+
async kernelTransactionHead(sessionId, operationId) {
|
|
83
|
+
const operationKey = this.operationKey(sessionId, operationId);
|
|
84
|
+
const entries = this.transactionStore.get(operationKey) ?? [];
|
|
85
|
+
return entries.at(-1)?.transaction.transaction_digest
|
|
86
|
+
?? this.genesisStore.get(operationKey)?.genesis.genesis_digest;
|
|
29
87
|
}
|
|
30
88
|
}
|
|
31
89
|
// Single-writer per session. Safe for concurrent appends within one instance.
|
|
@@ -34,6 +92,7 @@ export class FileSessionLog {
|
|
|
34
92
|
dir;
|
|
35
93
|
// Lazy-initialized per-session counter. Avoids re-reading the file on every append.
|
|
36
94
|
seqCounters = new Map();
|
|
95
|
+
appends = new KeyedSerialExecutor();
|
|
37
96
|
constructor(dir) {
|
|
38
97
|
this.dir = dir;
|
|
39
98
|
}
|
|
@@ -42,54 +101,155 @@ export class FileSessionLog {
|
|
|
42
101
|
}
|
|
43
102
|
async nextSeq(sessionId) {
|
|
44
103
|
if (!this.seqCounters.has(sessionId)) {
|
|
45
|
-
const existing = await this.
|
|
46
|
-
this.seqCounters.set(sessionId, existing.
|
|
104
|
+
const existing = await this.readRecords(sessionId);
|
|
105
|
+
this.seqCounters.set(sessionId, existing.reduce((next, record) => Math.max(next, record.seq + 1), 0));
|
|
47
106
|
}
|
|
48
107
|
const seq = this.seqCounters.get(sessionId);
|
|
49
108
|
this.seqCounters.set(sessionId, seq + 1);
|
|
50
109
|
return seq;
|
|
51
110
|
}
|
|
52
111
|
async append(sessionId, event) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
ws.write(JSON.stringify({ seq, event }) + "\n", err => {
|
|
58
|
-
if (err)
|
|
59
|
-
reject(err);
|
|
60
|
-
else
|
|
61
|
-
resolve();
|
|
62
|
-
});
|
|
63
|
-
ws.end();
|
|
112
|
+
return this.appends.run(sessionId, async () => {
|
|
113
|
+
const seq = await this.nextSeq(sessionId);
|
|
114
|
+
await this.appendRecord(sessionId, { seq, event });
|
|
115
|
+
return seq;
|
|
64
116
|
});
|
|
65
|
-
return seq;
|
|
66
117
|
}
|
|
67
118
|
async read(sessionId, fromSeq = 0, primitiveFilter) {
|
|
68
119
|
const results = [];
|
|
120
|
+
for (const record of await this.readRecords(sessionId)) {
|
|
121
|
+
if (!("event" in record) || record.seq < fromSeq)
|
|
122
|
+
continue;
|
|
123
|
+
if (primitiveFilter && primitiveForKind(record.event.kind) !== primitiveFilter)
|
|
124
|
+
continue;
|
|
125
|
+
results.push({ seq: record.seq, event: record.event });
|
|
126
|
+
}
|
|
127
|
+
return results;
|
|
128
|
+
}
|
|
129
|
+
async latestSeq(sessionId) {
|
|
130
|
+
const records = await this.readRecords(sessionId);
|
|
131
|
+
return records.reduce((latest, record) => Math.max(latest, record.seq), -1);
|
|
132
|
+
}
|
|
133
|
+
async appendKernelGenesis(sessionId, genesis) {
|
|
134
|
+
return this.appends.run(sessionId, async () => {
|
|
135
|
+
verifyKernelOperationGenesis(genesis);
|
|
136
|
+
const existing = (await this.readRecords(sessionId)).find((record) => "record_type" in record
|
|
137
|
+
&& record.record_type === "kernel_genesis"
|
|
138
|
+
&& record.genesis.operation_id === genesis.operation_id);
|
|
139
|
+
if (existing) {
|
|
140
|
+
if (existing.genesis.genesis_digest !== genesis.genesis_digest) {
|
|
141
|
+
throw new KernelLogConflictError("session already has a different kernel operation genesis");
|
|
142
|
+
}
|
|
143
|
+
return { log_seq: existing.seq, genesis_digest: genesis.genesis_digest };
|
|
144
|
+
}
|
|
145
|
+
const log_seq = await this.nextSeq(sessionId);
|
|
146
|
+
await this.appendRecord(sessionId, {
|
|
147
|
+
seq: log_seq,
|
|
148
|
+
record_type: "kernel_genesis",
|
|
149
|
+
genesis,
|
|
150
|
+
});
|
|
151
|
+
return { log_seq, genesis_digest: genesis.genesis_digest };
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
async readKernelGenesis(sessionId, operationId) {
|
|
155
|
+
const record = (await this.readRecords(sessionId)).find((entry) => "record_type" in entry
|
|
156
|
+
&& entry.record_type === "kernel_genesis"
|
|
157
|
+
&& entry.genesis.operation_id === operationId);
|
|
158
|
+
return record?.genesis;
|
|
159
|
+
}
|
|
160
|
+
async compareAndAppendKernelTransaction(sessionId, expectedTransactionHead, transaction) {
|
|
161
|
+
return this.appends.run(sessionId, async () => {
|
|
162
|
+
verifyKernelTransaction(transaction);
|
|
163
|
+
const records = await this.readRecords(sessionId);
|
|
164
|
+
const genesisRecord = records.find((record) => "record_type" in record
|
|
165
|
+
&& record.record_type === "kernel_genesis"
|
|
166
|
+
&& record.genesis.operation_id === transaction.operation_id);
|
|
167
|
+
if (!genesisRecord)
|
|
168
|
+
throw new KernelLogIntegrityError("kernel transaction requires a durable genesis");
|
|
169
|
+
if (transaction.operation_id !== genesisRecord.genesis.operation_id) {
|
|
170
|
+
throw new KernelLogIntegrityError("kernel transaction operation_id does not match genesis");
|
|
171
|
+
}
|
|
172
|
+
const transactions = records.filter((record) => "record_type" in record
|
|
173
|
+
&& record.record_type === "kernel_transaction"
|
|
174
|
+
&& record.transaction.operation_id === transaction.operation_id);
|
|
175
|
+
const head = transactions.at(-1)?.transaction.transaction_digest
|
|
176
|
+
?? genesisRecord.genesis.genesis_digest;
|
|
177
|
+
if (head !== expectedTransactionHead || transaction.previous_transaction_digest !== head) {
|
|
178
|
+
throw new KernelLogConflictError("kernel transaction head changed before compare-and-append");
|
|
179
|
+
}
|
|
180
|
+
verifyKernelTransactionSuccessor(transactions.at(-1)?.transaction, transaction);
|
|
181
|
+
const log_seq = await this.nextSeq(sessionId);
|
|
182
|
+
await this.appendRecord(sessionId, {
|
|
183
|
+
seq: log_seq,
|
|
184
|
+
record_type: "kernel_transaction",
|
|
185
|
+
transaction,
|
|
186
|
+
});
|
|
187
|
+
return { log_seq, transaction_digest: transaction.transaction_digest };
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
async readKernelTransactions(sessionId, operationId, fromStepSeq = 1) {
|
|
191
|
+
return (await this.readRecords(sessionId))
|
|
192
|
+
.filter((record) => "record_type" in record
|
|
193
|
+
&& record.record_type === "kernel_transaction"
|
|
194
|
+
&& record.transaction.operation_id === operationId
|
|
195
|
+
&& record.transaction.step_seq >= fromStepSeq)
|
|
196
|
+
.map(record => ({ log_seq: record.seq, transaction: record.transaction }));
|
|
197
|
+
}
|
|
198
|
+
async kernelTransactionHead(sessionId, operationId) {
|
|
199
|
+
const records = await this.readRecords(sessionId);
|
|
200
|
+
const transaction = records.filter((record) => "record_type" in record
|
|
201
|
+
&& record.record_type === "kernel_transaction"
|
|
202
|
+
&& record.transaction.operation_id === operationId).at(-1);
|
|
203
|
+
if (transaction)
|
|
204
|
+
return transaction.transaction.transaction_digest;
|
|
205
|
+
return records.find((record) => "record_type" in record
|
|
206
|
+
&& record.record_type === "kernel_genesis"
|
|
207
|
+
&& record.genesis.operation_id === operationId)?.genesis.genesis_digest;
|
|
208
|
+
}
|
|
209
|
+
async appendRecord(sessionId, record) {
|
|
210
|
+
await mkdir(this.dir, { recursive: true });
|
|
211
|
+
const path = this.path(sessionId);
|
|
212
|
+
let isNewFile = false;
|
|
213
|
+
try {
|
|
214
|
+
await access(path);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
isNewFile = true;
|
|
218
|
+
}
|
|
219
|
+
const file = await openFile(path, "a");
|
|
220
|
+
try {
|
|
221
|
+
await file.appendFile(`${JSON.stringify(record)}\n`, "utf8");
|
|
222
|
+
await file.sync();
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
await file.close();
|
|
226
|
+
}
|
|
227
|
+
if (isNewFile) {
|
|
228
|
+
const directory = await openFile(this.dir, "r");
|
|
229
|
+
try {
|
|
230
|
+
await directory.sync();
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
await directory.close();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async readRecords(sessionId) {
|
|
238
|
+
const records = [];
|
|
69
239
|
try {
|
|
70
240
|
const rl = createInterface({
|
|
71
241
|
input: createReadStream(this.path(sessionId)),
|
|
72
242
|
crlfDelay: Infinity,
|
|
73
243
|
});
|
|
74
244
|
for await (const line of rl) {
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
const entry = JSON.parse(line);
|
|
78
|
-
if (entry.seq >= fromSeq) {
|
|
79
|
-
if (primitiveFilter && primitiveForKind(entry.event.kind) !== primitiveFilter)
|
|
80
|
-
continue;
|
|
81
|
-
results.push(entry);
|
|
82
|
-
}
|
|
245
|
+
if (line.trim())
|
|
246
|
+
records.push(JSON.parse(line));
|
|
83
247
|
}
|
|
84
248
|
}
|
|
85
249
|
catch (err) {
|
|
86
250
|
if (err.code !== "ENOENT")
|
|
87
251
|
throw err;
|
|
88
252
|
}
|
|
89
|
-
return
|
|
90
|
-
}
|
|
91
|
-
async latestSeq(sessionId) {
|
|
92
|
-
const entries = await this.read(sessionId);
|
|
93
|
-
return entries.length - 1;
|
|
253
|
+
return records;
|
|
94
254
|
}
|
|
95
255
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ProviderReplay, ToolCall } from "../types.js";
|
|
1
|
+
import type { Message, ProviderReplay, ToolCall } from "../types.js";
|
|
2
2
|
import type { SessionEvent } from "./session-log.js";
|
|
3
|
+
import type { WorkflowNodeStatus } from "../types/agent.js";
|
|
3
4
|
export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay-sanitize.js";
|
|
4
5
|
/**
|
|
5
6
|
* Normalize a persisted llm_completed event for recovery.
|
|
@@ -46,33 +47,36 @@ export declare function buildRunTerminalEvent(input: {
|
|
|
46
47
|
export declare function buildWorkflowNodeCompletedEvent(input: {
|
|
47
48
|
turn: number;
|
|
48
49
|
agentId: string;
|
|
50
|
+
status: WorkflowNodeStatus;
|
|
49
51
|
termination: string;
|
|
50
52
|
classifyBranch?: string;
|
|
51
53
|
tournamentWinner?: string;
|
|
52
54
|
loopContinue?: boolean;
|
|
53
|
-
output?:
|
|
55
|
+
output?: Message;
|
|
54
56
|
}): Extract<SessionEvent, {
|
|
55
57
|
kind: "workflow_node_completed";
|
|
56
58
|
}>;
|
|
57
59
|
/** One recovered node completion: the agent id plus its persisted control signals and output. */
|
|
58
|
-
export interface
|
|
60
|
+
export interface RecoveredNodeOutcome {
|
|
59
61
|
agentId: string;
|
|
62
|
+
status: WorkflowNodeStatus;
|
|
63
|
+
termination: string;
|
|
60
64
|
classifyBranch?: string;
|
|
61
65
|
tournamentWinner?: string;
|
|
62
66
|
loopContinue?: boolean;
|
|
63
|
-
output?:
|
|
67
|
+
output?: Message;
|
|
64
68
|
}
|
|
65
69
|
/**
|
|
66
70
|
* Recover completed workflow node records from a session event stream. Scans for
|
|
67
71
|
* workflow_node_completed events with termination "completed" and returns them WITH their
|
|
68
72
|
* result-borne control signals (W-1) — resumeWorkflow lowers these to the kernel's
|
|
69
|
-
* `
|
|
73
|
+
* `resumed_outcomes` so a classifier re-prunes and a loop stop is honored, and re-seeds the
|
|
70
74
|
* driver's outputs map from the persisted output text.
|
|
71
75
|
*/
|
|
72
|
-
export declare function
|
|
76
|
+
export declare function recoverWorkflowNodeOutcomes(events: Array<{
|
|
73
77
|
seq: number;
|
|
74
78
|
event: SessionEvent;
|
|
75
|
-
}>):
|
|
79
|
+
}>): RecoveredNodeOutcome[];
|
|
76
80
|
/** R3-1: build workflow_nodes_submitted for persistence after a runtime submission, so resume can
|
|
77
81
|
* re-apply it. `nodes` is the kernel-shape (snake_case) submitted node array. */
|
|
78
82
|
export declare function buildWorkflowNodesSubmittedEvent(input: {
|
|
@@ -60,6 +60,7 @@ export function buildWorkflowNodeCompletedEvent(input) {
|
|
|
60
60
|
kind: "workflow_node_completed",
|
|
61
61
|
turn: input.turn,
|
|
62
62
|
agent_id: input.agentId,
|
|
63
|
+
status: input.status,
|
|
63
64
|
termination: input.termination,
|
|
64
65
|
...(input.classifyBranch !== undefined ? { classify_branch: input.classifyBranch } : {}),
|
|
65
66
|
...(input.tournamentWinner !== undefined ? { tournament_winner: input.tournamentWinner } : {}),
|
|
@@ -71,15 +72,17 @@ export function buildWorkflowNodeCompletedEvent(input) {
|
|
|
71
72
|
* Recover completed workflow node records from a session event stream. Scans for
|
|
72
73
|
* workflow_node_completed events with termination "completed" and returns them WITH their
|
|
73
74
|
* result-borne control signals (W-1) — resumeWorkflow lowers these to the kernel's
|
|
74
|
-
* `
|
|
75
|
+
* `resumed_outcomes` so a classifier re-prunes and a loop stop is honored, and re-seeds the
|
|
75
76
|
* driver's outputs map from the persisted output text.
|
|
76
77
|
*/
|
|
77
|
-
export function
|
|
78
|
+
export function recoverWorkflowNodeOutcomes(events) {
|
|
78
79
|
const completed = [];
|
|
79
80
|
for (const { event } of events) {
|
|
80
|
-
if (event.kind === "workflow_node_completed"
|
|
81
|
+
if (event.kind === "workflow_node_completed") {
|
|
81
82
|
completed.push({
|
|
82
83
|
agentId: event.agent_id,
|
|
84
|
+
status: event.status,
|
|
85
|
+
termination: event.termination,
|
|
83
86
|
...(event.classify_branch !== undefined ? { classifyBranch: event.classify_branch } : {}),
|
|
84
87
|
...(event.tournament_winner !== undefined ? { tournamentWinner: event.tournament_winner } : {}),
|
|
85
88
|
...(event.loop_continue !== undefined ? { loopContinue: event.loop_continue } : {}),
|
|
@@ -111,11 +114,11 @@ export function recoverSubmittedWorkflowNodes(events) {
|
|
|
111
114
|
if (event.kind === "workflow_nodes_submitted") {
|
|
112
115
|
submissions.push(event.nodes);
|
|
113
116
|
submitters.push(event.submitter_agent_id);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
if (event.base_index === undefined) {
|
|
118
|
+
throw new Error("workflow_nodes_submitted is missing required base_index");
|
|
119
|
+
}
|
|
120
|
+
bases.push(event.base_index);
|
|
118
121
|
}
|
|
119
122
|
}
|
|
120
|
-
return { submissions, bases
|
|
123
|
+
return { submissions, bases, submitters };
|
|
121
124
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { StreamEvent } from "../types.js";
|
|
2
|
-
import type { AgentRunSpec, AgentProcessChangedObservation, SubAgentResult } from "../types/agent.js";
|
|
2
|
+
import type { AgentRunSpec, AgentProcessChangedObservation, LoopResult, SubAgentResult } from "../types/agent.js";
|
|
3
3
|
import type { RuntimeOptions } from "./runner.js";
|
|
4
4
|
import type { SessionLog } from "./session-log.js";
|
|
5
|
+
import type { AttemptOutcome } from "../harness/harness.js";
|
|
5
6
|
export interface SubAgentRunContext {
|
|
6
7
|
parentOpts: RuntimeOptions;
|
|
7
8
|
parentSessionId: string;
|
|
@@ -27,6 +28,8 @@ export interface SubAgentRunContext {
|
|
|
27
28
|
* `InterruptNow` → `AgentPreempted`), the orchestrator interrupts the child runner, cancelling its
|
|
28
29
|
* in-flight LLM call. */
|
|
29
30
|
abortSignal?: AbortSignal;
|
|
31
|
+
/** AttemptLoop carry material; injected through the child's journaled signal channel. */
|
|
32
|
+
contextInput?: string;
|
|
30
33
|
}
|
|
31
34
|
/** M1/G3 intelligence routing: resolve the provider for a sub-agent from its spec's `modelHint`.
|
|
32
35
|
* Falls back to the parent provider when there is no hint or no `providerFor` hook resolves it. */
|
|
@@ -42,5 +45,6 @@ export declare class SubAgentOrchestrator {
|
|
|
42
45
|
run(ctx: SubAgentRunContext): Promise<SubAgentResult>;
|
|
43
46
|
}
|
|
44
47
|
export declare const defaultSubAgentOrchestrator: SubAgentOrchestrator;
|
|
48
|
+
export declare function attemptOutcomeToLoopResult(outcome: AttemptOutcome): LoopResult;
|
|
45
49
|
/** Kernel spawn without an active parent run loop (harness / coordinator use). */
|
|
46
|
-
export declare function spawnStandalone(parentOpts: RuntimeOptions, parentSessionId: string, spec: AgentRunSpec, orchestrator?: SubAgentOrchestrator): Promise<SubAgentResult>;
|
|
50
|
+
export declare function spawnStandalone(parentOpts: RuntimeOptions, parentSessionId: string, spec: AgentRunSpec, orchestrator?: SubAgentOrchestrator, contextInput?: string): Promise<SubAgentResult>;
|