@tangle-network/agent-runtime 0.140.0 → 0.141.0
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/{activation-eLJ6uRlP.js → activation-4Aa3qXFV.js} +3 -3
- package/dist/{activation-eLJ6uRlP.js.map → activation-4Aa3qXFV.js.map} +1 -1
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +2 -2
- package/dist/analyst-loop.d.ts +1 -1
- package/dist/{authoring-J93uFAwd.js → authoring-Bzow2zKI.js} +2 -2
- package/dist/{authoring-J93uFAwd.js.map → authoring-Bzow2zKI.js.map} +1 -1
- package/dist/candidate-execution/index.js +4 -4
- package/dist/{candidate-execution-uuUeFSc6.js → candidate-execution-CD5PoNmC.js} +4 -4
- package/dist/{candidate-execution-uuUeFSc6.js.map → candidate-execution-CD5PoNmC.js.map} +1 -1
- package/dist/conversation.d.ts +1 -1
- package/dist/durable.d.ts +174 -2
- package/dist/durable.js +433 -2
- package/dist/durable.js.map +1 -1
- package/dist/{environment-provider-Bu3S3v78.d.ts → environment-provider-B985TeIr.d.ts} +3 -3
- package/dist/environment-provider.d.ts +1 -1
- package/dist/{graph-BbMjq7-x.js → graph-BHR5SFUh.js} +4 -4
- package/dist/{graph-BbMjq7-x.js.map → graph-BHR5SFUh.js.map} +1 -1
- package/dist/{improvement-cycle-D6rhMJR0.js → improvement-cycle-H_27-d1O.js} +4 -4
- package/dist/{improvement-cycle-D6rhMJR0.js.map → improvement-cycle-H_27-d1O.js.map} +1 -1
- package/dist/{index-CTz17pH6.d.ts → index-CTI6v7V6.d.ts} +3 -3
- package/dist/{index-COnuT0h7.d.ts → index-D4dUTLcs.d.ts} +7 -7
- package/dist/{index-C6yfz0QN.d.ts → index-Y7Wnmlip.d.ts} +4 -4
- package/dist/index.d.ts +10 -10
- package/dist/index.js +12 -12
- package/dist/intelligence.d.ts +5 -5
- package/dist/intelligence.js +5 -5
- package/dist/kernel.d.ts +6 -6
- package/dist/kernel.js +7 -7
- package/dist/{knowledge-CD-_FbJT.js → knowledge-C3qWCMJi.js} +5 -5
- package/dist/{knowledge-CD-_FbJT.js.map → knowledge-C3qWCMJi.js.map} +1 -1
- package/dist/knowledge.d.ts +1 -1
- package/dist/knowledge.js +1 -1
- package/dist/{loop-runner-bin-DUNxyuaG.d.ts → loop-runner-bin-Clm1wCGW.d.ts} +3 -3
- package/dist/{loop-runner-bin-BBz_ACfQ.js → loop-runner-bin-Cv-x6lzB.js} +3 -3
- package/dist/{loop-runner-bin-BBz_ACfQ.js.map → loop-runner-bin-Cv-x6lzB.js.map} +1 -1
- package/dist/loop-runner-bin.d.ts +1 -1
- package/dist/loop-runner-bin.js +1 -1
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/index.d.ts +5 -5
- package/dist/mcp/index.js +4 -4
- package/dist/{openai-tools-yoRRzAUW.d.ts → openai-tools-5p7xigAz.d.ts} +2 -2
- package/dist/{openai-tools-DIKr72_l.js → openai-tools-DabO2cOu.js} +2 -2
- package/dist/{openai-tools-DIKr72_l.js.map → openai-tools-DabO2cOu.js.map} +1 -1
- package/dist/{prepare-DK1qnk7-.js → prepare-D3n7cA39.js} +2 -2
- package/dist/{prepare-DK1qnk7-.js.map → prepare-D3n7cA39.js.map} +1 -1
- package/dist/primeintellect/index.d.ts +1 -1
- package/dist/profiles.d.ts +1 -1
- package/dist/{protected-model-port-DHeCm_UF.js → protected-model-port-bq6bD9YX.js} +2 -2
- package/dist/{protected-model-port-DHeCm_UF.js.map → protected-model-port-bq6bD9YX.js.map} +1 -1
- package/dist/{redact-CLw2B7Qj.d.ts → redact-D1qhbHWB.d.ts} +3 -3
- package/dist/{runtime-CVNWEw16.d.ts → runtime-CMIt1tII.d.ts} +4 -4
- package/dist/{runtime-tTA2iCZQ.js → runtime-Dy_u99XS.js} +8 -8
- package/dist/{runtime-tTA2iCZQ.js.map → runtime-Dy_u99XS.js.map} +1 -1
- package/dist/{runtime-hooks-sbRpjStq.d.ts → runtime-hooks-Bj6wJHlH.d.ts} +10 -1
- package/dist/{runtime-hooks-C7iJOWm3.js → runtime-hooks-tXpAarhW.js} +28 -2
- package/dist/runtime-hooks-tXpAarhW.js.map +1 -0
- package/dist/{spawn-journal-DGXei3xG.js → spawn-journal-D2HKUImC.js} +56 -3
- package/dist/spawn-journal-D2HKUImC.js.map +1 -0
- package/dist/{stream-agent-turn-RjrKGk6S.d.ts → stream-agent-turn-D8Jaq70U.d.ts} +3 -3
- package/dist/{structural-rollout-7LP9Ex0F.js → structural-rollout-sr5KFnq4.js} +3 -3
- package/dist/{structural-rollout-7LP9Ex0F.js.map → structural-rollout-sr5KFnq4.js.map} +1 -1
- package/dist/{supervise-Cx1QY_hj.js → supervise-CqN9ThOr.js} +4 -5
- package/dist/{supervise-Cx1QY_hj.js.map → supervise-CqN9ThOr.js.map} +1 -1
- package/dist/{supervisor-BV72aDmP.js → supervisor-3cq3UHmU.js} +3 -3
- package/dist/{supervisor-BV72aDmP.js.map → supervisor-3cq3UHmU.js.map} +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +12 -12
- package/dist/tool-loop.d.ts +1 -1
- package/dist/tool-loop.js +1 -1
- package/dist/{types-D4arYjUl.d.ts → types-CNGHfnk3.d.ts} +3 -3
- package/dist/{types-C244Huc9.d.ts → types-D9WBXD7G.d.ts} +2 -2
- package/dist/{workspace-archive-HYlsPhfm.js → workspace-archive-C52LjkeN.js} +2 -2
- package/dist/{workspace-archive-HYlsPhfm.js.map → workspace-archive-C52LjkeN.js.map} +1 -1
- package/package.json +1 -1
- package/dist/jsonl-file-DtxhdAkZ.js +0 -57
- package/dist/jsonl-file-DtxhdAkZ.js.map +0 -1
- package/dist/runtime-hooks-C7iJOWm3.js.map +0 -1
- package/dist/spawn-journal-DGXei3xG.js.map +0 -1
package/dist/durable.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Oo as SupervisorProfile, mo as supervise, uo as SuperviseOptions } from "./index-D4dUTLcs.js";
|
|
2
|
+
import { l as RuntimeHooks, o as RuntimeHookEvent, r as RuntimeDecisionPoint } from "./runtime-hooks-Bj6wJHlH.js";
|
|
3
|
+
import { x as NodeId } from "./types-CNGHfnk3.js";
|
|
2
4
|
//#region src/durable/chat-engine.d.ts
|
|
3
5
|
/**
|
|
4
6
|
* `handleChatTurn` is a framework-neutral chat-turn HTTP orchestrator.
|
|
@@ -131,6 +133,176 @@ declare function deriveExecutionId(input: {
|
|
|
131
133
|
turnIndex: number;
|
|
132
134
|
}): string;
|
|
133
135
|
//#endregion
|
|
136
|
+
//#region src/durable/observer-journal.d.ts
|
|
137
|
+
type ObserverRecordKind = 'event' | 'decision';
|
|
138
|
+
/**
|
|
139
|
+
* One immutable record in the observer plane. `sequence` is journal order, not
|
|
140
|
+
* execution order; causal/runtime order remains available on the underlying event.
|
|
141
|
+
* `previousDigest` + `digest` make deletion, reordering, or mutation detectable.
|
|
142
|
+
*/
|
|
143
|
+
interface ObserverRecord {
|
|
144
|
+
readonly schemaVersion: 1;
|
|
145
|
+
readonly pursuitId: string;
|
|
146
|
+
readonly sequence: number;
|
|
147
|
+
readonly kind: ObserverRecordKind;
|
|
148
|
+
readonly observedAt: number;
|
|
149
|
+
readonly previousDigest?: string;
|
|
150
|
+
readonly event?: RuntimeHookEvent;
|
|
151
|
+
readonly decision?: RuntimeDecisionPoint;
|
|
152
|
+
readonly digest: string;
|
|
153
|
+
}
|
|
154
|
+
interface ObserverJournal {
|
|
155
|
+
appendEvent(event: RuntimeHookEvent): Promise<ObserverRecord>;
|
|
156
|
+
appendDecision(point: RuntimeDecisionPoint): Promise<ObserverRecord>;
|
|
157
|
+
read(): Promise<readonly ObserverRecord[]>;
|
|
158
|
+
hooks(): RuntimeHooks;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Durable, append-only third-person history for one concrete Runtime execution.
|
|
162
|
+
* It consumes Runtime's existing hook stream and does not participate in execution
|
|
163
|
+
* decisions. A broken observer therefore cannot change what an agent is allowed to do.
|
|
164
|
+
*
|
|
165
|
+
* The write discipline deliberately matches `FileSpawnJournal`: serialized appends,
|
|
166
|
+
* torn-tail recovery, short-write handling, and fsync before acknowledgement. One
|
|
167
|
+
* execution owns one journal file; higher-level pursuit aggregation joins isolated
|
|
168
|
+
* journals by `pursuitId` instead of making independent processes share a write head.
|
|
169
|
+
*/
|
|
170
|
+
declare class FileObserverJournal implements ObserverJournal {
|
|
171
|
+
readonly path: string;
|
|
172
|
+
readonly pursuitId: string;
|
|
173
|
+
private tail;
|
|
174
|
+
private initialized;
|
|
175
|
+
private sequence;
|
|
176
|
+
private previousDigest;
|
|
177
|
+
private appendFailure;
|
|
178
|
+
constructor(path: string, pursuitId: string);
|
|
179
|
+
hooks(): RuntimeHooks;
|
|
180
|
+
appendEvent(event: RuntimeHookEvent): Promise<ObserverRecord>;
|
|
181
|
+
appendDecision(point: RuntimeDecisionPoint): Promise<ObserverRecord>;
|
|
182
|
+
read(): Promise<readonly ObserverRecord[]>;
|
|
183
|
+
private enqueue;
|
|
184
|
+
private initialize;
|
|
185
|
+
private readExistingUnsafe;
|
|
186
|
+
private writeRecord;
|
|
187
|
+
private assertComplete;
|
|
188
|
+
}
|
|
189
|
+
/** Verify identity, monotonic sequence, payload shape, and the complete digest chain. */
|
|
190
|
+
declare function verifyObserverRecords(records: readonly ObserverRecord[], pursuitId?: string): readonly ObserverRecord[];
|
|
191
|
+
/** Compute the canonical SHA-256 digest for an unsigned observer record. */
|
|
192
|
+
declare function observerRecordDigest(record: Omit<ObserverRecord, 'digest'>): string;
|
|
193
|
+
/** Build the canonical durable observer hook in one call. */
|
|
194
|
+
declare function createFileObserverHooks(path: string, pursuitId: string): {
|
|
195
|
+
readonly journal: FileObserverJournal;
|
|
196
|
+
readonly hooks: RuntimeHooks;
|
|
197
|
+
};
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/durable/observer-projection.d.ts
|
|
200
|
+
type PursuitRunStatus = 'running' | 'done' | 'failed';
|
|
201
|
+
interface PursuitRunProjection {
|
|
202
|
+
readonly runId: string;
|
|
203
|
+
readonly status: PursuitRunStatus;
|
|
204
|
+
readonly settledAt?: number;
|
|
205
|
+
readonly error?: string;
|
|
206
|
+
readonly firstSequence: number;
|
|
207
|
+
readonly lastSequence: number;
|
|
208
|
+
readonly firstObservedAt: number;
|
|
209
|
+
readonly lastObservedAt: number;
|
|
210
|
+
readonly eventCount: number;
|
|
211
|
+
readonly decisionCount: number;
|
|
212
|
+
readonly targets: Readonly<Record<string, number>>;
|
|
213
|
+
readonly decisions: Readonly<Record<string, number>>;
|
|
214
|
+
}
|
|
215
|
+
type PursuitNodeStatus = 'running' | 'done' | 'down';
|
|
216
|
+
interface PursuitNodeProjection {
|
|
217
|
+
readonly id: string;
|
|
218
|
+
readonly parentId?: string;
|
|
219
|
+
/** Node ids are scoped to this concrete Runtime tree; `(runId,id)` is identity. */
|
|
220
|
+
readonly runId: string;
|
|
221
|
+
readonly label?: string;
|
|
222
|
+
readonly runtime?: string;
|
|
223
|
+
readonly depth?: number;
|
|
224
|
+
readonly assignmentId?: string;
|
|
225
|
+
readonly identity?: unknown;
|
|
226
|
+
readonly budget?: unknown;
|
|
227
|
+
readonly status: PursuitNodeStatus;
|
|
228
|
+
readonly settledAt?: number;
|
|
229
|
+
readonly spent?: unknown;
|
|
230
|
+
readonly outRef?: string;
|
|
231
|
+
readonly score?: number;
|
|
232
|
+
readonly valid?: boolean;
|
|
233
|
+
readonly reason?: string;
|
|
234
|
+
readonly infra?: boolean;
|
|
235
|
+
readonly wait?: unknown;
|
|
236
|
+
readonly firstSequence: number;
|
|
237
|
+
readonly lastSequence: number;
|
|
238
|
+
readonly firstObservedAt: number;
|
|
239
|
+
readonly lastObservedAt: number;
|
|
240
|
+
readonly eventCount: number;
|
|
241
|
+
}
|
|
242
|
+
interface PursuitProjection {
|
|
243
|
+
readonly pursuitId: string;
|
|
244
|
+
/** Number of records in this concrete execution journal. */
|
|
245
|
+
readonly sequence: number;
|
|
246
|
+
/** Digest-chain tip for this concrete execution journal. */
|
|
247
|
+
readonly chainTip: string;
|
|
248
|
+
readonly firstObservedAt: number;
|
|
249
|
+
readonly lastObservedAt: number;
|
|
250
|
+
readonly runs: readonly PursuitRunProjection[];
|
|
251
|
+
readonly nodes: readonly PursuitNodeProjection[];
|
|
252
|
+
readonly eventCount: number;
|
|
253
|
+
readonly decisionCount: number;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Fold one append-only execution journal into a deterministic operator projection.
|
|
257
|
+
*
|
|
258
|
+
* This is intentionally a READ model, not another state machine: it does not own
|
|
259
|
+
* execution, cannot steer agents, and can be rebuilt from the journal at any time.
|
|
260
|
+
* Projection verifies the complete hash chain first, so an operator view can never
|
|
261
|
+
* silently render a mutated or reordered observer history as trustworthy state.
|
|
262
|
+
*
|
|
263
|
+
* Topology comes only from Runtime's canonical `agent.spawn` facts. Terminal node
|
|
264
|
+
* state comes only from `agent.child`; concrete run state comes only from the root
|
|
265
|
+
* `agent.run` lifecycle emitted by `supervisePursuit`. Node identity is scoped to the
|
|
266
|
+
* concrete Runtime run so independent trees may both contain `root:s0` without aliasing.
|
|
267
|
+
*/
|
|
268
|
+
declare function projectPursuit(records: readonly ObserverRecord[]): PursuitProjection;
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/durable/supervise-pursuit.d.ts
|
|
271
|
+
interface SupervisePursuitOptions extends SuperviseOptions {
|
|
272
|
+
/** Stable objective identity spanning concrete Runtime runs. */
|
|
273
|
+
readonly pursuitId: string;
|
|
274
|
+
/**
|
|
275
|
+
* One concrete Runtime execution owns one durable directory and observer journal.
|
|
276
|
+
* A pursuit spanning several runs reuses `pursuitId` across distinct `runDir`s;
|
|
277
|
+
* Intelligence joins those isolated projections without a shared write head.
|
|
278
|
+
*/
|
|
279
|
+
readonly runDir: string;
|
|
280
|
+
}
|
|
281
|
+
interface SupervisedPursuitResult<Result> {
|
|
282
|
+
readonly result: Result;
|
|
283
|
+
readonly pursuit: PursuitProjection;
|
|
284
|
+
readonly observerPath: string;
|
|
285
|
+
}
|
|
286
|
+
/** A failed Runtime execution whose complete third-person projection was retained. */
|
|
287
|
+
declare class SupervisePursuitError extends Error {
|
|
288
|
+
readonly pursuit: PursuitProjection;
|
|
289
|
+
readonly observerPath: string;
|
|
290
|
+
constructor(cause: unknown, pursuit: PursuitProjection, observerPath: string);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* One-call durable pursuit execution over the canonical `supervise()` kernel.
|
|
294
|
+
*
|
|
295
|
+
* This is an adapter, not a second executor: it composes a durable third-person
|
|
296
|
+
* observer into Runtime's existing recursive hook stream and then rebuilds the
|
|
297
|
+
* operator projection after the same `supervise()` call settles. Agents never
|
|
298
|
+
* receive the observer path or projection and their behavior does not depend on it.
|
|
299
|
+
*
|
|
300
|
+
* Every concrete execution writes only inside its own `runDir`. Cross-run pursuit
|
|
301
|
+
* aggregation is therefore lock-free at the observer layer: reuse `pursuitId` across
|
|
302
|
+
* run directories and let Intelligence join the independently verified projections.
|
|
303
|
+
*/
|
|
304
|
+
declare function supervisePursuit(profile: SupervisorProfile, task: unknown, opts: SupervisePursuitOptions): Promise<SupervisedPursuitResult<Awaited<ReturnType<typeof supervise>>>>;
|
|
305
|
+
//#endregion
|
|
134
306
|
//#region src/durable/supervision-discovery.d.ts
|
|
135
307
|
interface DurableCoordinationStreamIdentity {
|
|
136
308
|
readonly runId: string;
|
|
@@ -163,5 +335,5 @@ interface DurableSupervisionDiscovery {
|
|
|
163
335
|
*/
|
|
164
336
|
declare function discoverDurableSupervisionRun(runDir: string): Promise<DurableSupervisionDiscovery>;
|
|
165
337
|
//#endregion
|
|
166
|
-
export { type ChatStreamEvent, type ChatTurnHooks, type ChatTurnIdentity, type ChatTurnProducer, type ChatTurnResult, type DurableCoordinationStreamIdentity, type DurableSupervisionDiscovery, type RunChatTurnInput, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn };
|
|
338
|
+
export { type ChatStreamEvent, type ChatTurnHooks, type ChatTurnIdentity, type ChatTurnProducer, type ChatTurnResult, type DurableCoordinationStreamIdentity, type DurableSupervisionDiscovery, FileObserverJournal, type ObserverJournal, type ObserverRecord, type ObserverRecordKind, type PursuitNodeProjection, type PursuitNodeStatus, type PursuitProjection, type PursuitRunProjection, type PursuitRunStatus, type RunChatTurnInput, SupervisePursuitError, type SupervisePursuitOptions, type SupervisedPursuitResult, createFileObserverHooks, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, supervisePursuit, verifyObserverRecords };
|
|
167
339
|
//# sourceMappingURL=durable.d.ts.map
|
package/dist/durable.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as prepareJsonlAppend, f as writeAllBytes, u as parseCommittedJsonLines } from "./spawn-journal-D2HKUImC.js";
|
|
2
|
+
import { a as withPursuitContext, t as composeRuntimeHooks } from "./runtime-hooks-tXpAarhW.js";
|
|
3
|
+
import { n as supervise } from "./supervise-CqN9ThOr.js";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
2
5
|
import { resolve } from "node:path";
|
|
6
|
+
import { readFile } from "node:fs/promises";
|
|
3
7
|
//#region src/durable/chat-engine.ts
|
|
4
8
|
const encoder = new TextEncoder();
|
|
5
9
|
function encodeLine(event) {
|
|
@@ -121,6 +125,433 @@ function deriveExecutionId(input) {
|
|
|
121
125
|
return executionId;
|
|
122
126
|
}
|
|
123
127
|
//#endregion
|
|
128
|
+
//#region src/durable/observer-journal.ts
|
|
129
|
+
/**
|
|
130
|
+
* Durable, append-only third-person history for one concrete Runtime execution.
|
|
131
|
+
* It consumes Runtime's existing hook stream and does not participate in execution
|
|
132
|
+
* decisions. A broken observer therefore cannot change what an agent is allowed to do.
|
|
133
|
+
*
|
|
134
|
+
* The write discipline deliberately matches `FileSpawnJournal`: serialized appends,
|
|
135
|
+
* torn-tail recovery, short-write handling, and fsync before acknowledgement. One
|
|
136
|
+
* execution owns one journal file; higher-level pursuit aggregation joins isolated
|
|
137
|
+
* journals by `pursuitId` instead of making independent processes share a write head.
|
|
138
|
+
*/
|
|
139
|
+
var FileObserverJournal = class {
|
|
140
|
+
path;
|
|
141
|
+
pursuitId;
|
|
142
|
+
tail = Promise.resolve();
|
|
143
|
+
initialized = false;
|
|
144
|
+
sequence = 0;
|
|
145
|
+
previousDigest;
|
|
146
|
+
appendFailure;
|
|
147
|
+
constructor(path, pursuitId) {
|
|
148
|
+
const stableId = pursuitId.trim();
|
|
149
|
+
if (stableId.length === 0) throw new TypeError("FileObserverJournal: pursuitId must be non-empty");
|
|
150
|
+
this.path = resolve(path);
|
|
151
|
+
this.pursuitId = stableId;
|
|
152
|
+
}
|
|
153
|
+
hooks() {
|
|
154
|
+
return withPursuitContext(this.pursuitId, {
|
|
155
|
+
onEvent: (event) => this.appendEvent(event).then(() => void 0),
|
|
156
|
+
onDecisionPoint: (point) => this.appendDecision(point).then(() => void 0)
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
appendEvent(event) {
|
|
160
|
+
return this.enqueue("event", event);
|
|
161
|
+
}
|
|
162
|
+
appendDecision(point) {
|
|
163
|
+
return this.enqueue("decision", point);
|
|
164
|
+
}
|
|
165
|
+
async read() {
|
|
166
|
+
await this.tail;
|
|
167
|
+
this.assertComplete();
|
|
168
|
+
let text;
|
|
169
|
+
try {
|
|
170
|
+
text = await readFile(this.path, "utf8");
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (isNoEnt(error)) return [];
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
return verifyObserverRecords(parseCommittedJsonLines(text, this.path), this.pursuitId);
|
|
176
|
+
}
|
|
177
|
+
enqueue(kind, value) {
|
|
178
|
+
if (value.pursuitId !== this.pursuitId) return Promise.reject(/* @__PURE__ */ new Error(`FileObserverJournal: ${kind} pursuitId ${String(value.pursuitId)} does not match ${this.pursuitId}`));
|
|
179
|
+
let result;
|
|
180
|
+
const operation = this.tail.then(async () => {
|
|
181
|
+
this.assertComplete();
|
|
182
|
+
try {
|
|
183
|
+
await this.initialize();
|
|
184
|
+
} catch (error) {
|
|
185
|
+
this.appendFailure ??= toError(error);
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
const unsigned = {
|
|
189
|
+
schemaVersion: 1,
|
|
190
|
+
pursuitId: this.pursuitId,
|
|
191
|
+
sequence: this.sequence + 1,
|
|
192
|
+
kind,
|
|
193
|
+
observedAt: Date.now(),
|
|
194
|
+
...this.previousDigest ? { previousDigest: this.previousDigest } : {},
|
|
195
|
+
...kind === "event" ? { event: value } : { decision: value }
|
|
196
|
+
};
|
|
197
|
+
const record = Object.freeze({
|
|
198
|
+
...unsigned,
|
|
199
|
+
digest: observerRecordDigest(unsigned)
|
|
200
|
+
});
|
|
201
|
+
try {
|
|
202
|
+
await this.writeRecord(record);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
this.appendFailure ??= toError(error);
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
this.sequence = record.sequence;
|
|
208
|
+
this.previousDigest = record.digest;
|
|
209
|
+
result = record;
|
|
210
|
+
});
|
|
211
|
+
this.tail = operation.then(() => void 0, () => void 0);
|
|
212
|
+
return operation.then(() => {
|
|
213
|
+
if (!result) throw new Error("FileObserverJournal: append completed without a record");
|
|
214
|
+
return result;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
async initialize() {
|
|
218
|
+
if (this.initialized) return;
|
|
219
|
+
const tail = verifyObserverRecords(await this.readExistingUnsafe(), this.pursuitId).at(-1);
|
|
220
|
+
this.sequence = tail?.sequence ?? 0;
|
|
221
|
+
this.previousDigest = tail?.digest;
|
|
222
|
+
this.initialized = true;
|
|
223
|
+
}
|
|
224
|
+
async readExistingUnsafe() {
|
|
225
|
+
let text;
|
|
226
|
+
try {
|
|
227
|
+
text = await readFile(this.path, "utf8");
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if (isNoEnt(error)) return [];
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
return parseCommittedJsonLines(text, this.path);
|
|
233
|
+
}
|
|
234
|
+
async writeRecord(record) {
|
|
235
|
+
const fs = await import("node:fs/promises");
|
|
236
|
+
const path = await import("node:path");
|
|
237
|
+
await fs.mkdir(path.dirname(this.path), { recursive: true });
|
|
238
|
+
const needsSeparator = await prepareJsonlAppend(this.path);
|
|
239
|
+
const handle = await fs.open(this.path, "a");
|
|
240
|
+
try {
|
|
241
|
+
await writeAllBytes(handle, `${needsSeparator ? "\n" : ""}${JSON.stringify(record)}\n`);
|
|
242
|
+
await handle.sync();
|
|
243
|
+
} finally {
|
|
244
|
+
await handle.close();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
assertComplete() {
|
|
248
|
+
if (!this.appendFailure) return;
|
|
249
|
+
throw new Error("FileObserverJournal: a prior durable append failed; observer completeness is unknown", { cause: this.appendFailure });
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
/** Verify identity, monotonic sequence, payload shape, and the complete digest chain. */
|
|
253
|
+
function verifyObserverRecords(records, pursuitId) {
|
|
254
|
+
let previousDigest;
|
|
255
|
+
let expectedSequence = 1;
|
|
256
|
+
for (const record of records) {
|
|
257
|
+
if (record.schemaVersion !== 1) throw new Error("observer journal: unsupported schemaVersion");
|
|
258
|
+
if (pursuitId !== void 0 && record.pursuitId !== pursuitId) throw new Error(`observer journal: pursuit identity mismatch at sequence ${record.sequence}`);
|
|
259
|
+
if (record.sequence !== expectedSequence) throw new Error(`observer journal: non-contiguous sequence ${record.sequence}; expected ${expectedSequence}`);
|
|
260
|
+
if (record.previousDigest !== previousDigest) throw new Error(`observer journal: digest-chain break at sequence ${record.sequence}`);
|
|
261
|
+
if (record.kind === "event" === (record.event === void 0)) throw new Error(`observer journal: invalid event payload at sequence ${record.sequence}`);
|
|
262
|
+
if (record.kind === "decision" === (record.decision === void 0)) throw new Error(`observer journal: invalid decision payload at sequence ${record.sequence}`);
|
|
263
|
+
if (record.event !== void 0 && record.event.pursuitId !== record.pursuitId) throw new Error(`observer journal: nested event pursuit mismatch at sequence ${record.sequence}`);
|
|
264
|
+
if (record.decision !== void 0 && record.decision.pursuitId !== record.pursuitId) throw new Error(`observer journal: nested decision pursuit mismatch at sequence ${record.sequence}`);
|
|
265
|
+
const { digest, ...unsigned } = record;
|
|
266
|
+
if (digest !== observerRecordDigest(unsigned)) throw new Error(`observer journal: digest mismatch at sequence ${record.sequence}`);
|
|
267
|
+
previousDigest = digest;
|
|
268
|
+
expectedSequence += 1;
|
|
269
|
+
}
|
|
270
|
+
return Object.freeze([...records]);
|
|
271
|
+
}
|
|
272
|
+
/** Compute the canonical SHA-256 digest for an unsigned observer record. */
|
|
273
|
+
function observerRecordDigest(record) {
|
|
274
|
+
return createHash("sha256").update(JSON.stringify(record)).digest("hex");
|
|
275
|
+
}
|
|
276
|
+
/** Build the canonical durable observer hook in one call. */
|
|
277
|
+
function createFileObserverHooks(path, pursuitId) {
|
|
278
|
+
const journal = new FileObserverJournal(path, pursuitId);
|
|
279
|
+
return {
|
|
280
|
+
journal,
|
|
281
|
+
hooks: journal.hooks()
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function isNoEnt(error) {
|
|
285
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
286
|
+
}
|
|
287
|
+
function toError(error) {
|
|
288
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
289
|
+
}
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/durable/observer-projection.ts
|
|
292
|
+
/**
|
|
293
|
+
* Fold one append-only execution journal into a deterministic operator projection.
|
|
294
|
+
*
|
|
295
|
+
* This is intentionally a READ model, not another state machine: it does not own
|
|
296
|
+
* execution, cannot steer agents, and can be rebuilt from the journal at any time.
|
|
297
|
+
* Projection verifies the complete hash chain first, so an operator view can never
|
|
298
|
+
* silently render a mutated or reordered observer history as trustworthy state.
|
|
299
|
+
*
|
|
300
|
+
* Topology comes only from Runtime's canonical `agent.spawn` facts. Terminal node
|
|
301
|
+
* state comes only from `agent.child`; concrete run state comes only from the root
|
|
302
|
+
* `agent.run` lifecycle emitted by `supervisePursuit`. Node identity is scoped to the
|
|
303
|
+
* concrete Runtime run so independent trees may both contain `root:s0` without aliasing.
|
|
304
|
+
*/
|
|
305
|
+
function projectPursuit(records) {
|
|
306
|
+
if (records.length === 0) throw new TypeError("projectPursuit: at least one observer record is required");
|
|
307
|
+
const pursuitId = records[0].pursuitId;
|
|
308
|
+
const verified = verifyObserverRecords(records, pursuitId);
|
|
309
|
+
const runs = /* @__PURE__ */ new Map();
|
|
310
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
311
|
+
let eventCount = 0;
|
|
312
|
+
let decisionCount = 0;
|
|
313
|
+
for (const record of verified) {
|
|
314
|
+
const observed = record.event ?? record.decision;
|
|
315
|
+
if (!observed) throw new Error(`projectPursuit: record ${record.sequence} has no observation`);
|
|
316
|
+
const run = getRun(runs, observed.runId, record);
|
|
317
|
+
run.lastSequence = record.sequence;
|
|
318
|
+
run.lastObservedAt = record.observedAt;
|
|
319
|
+
if (record.event) {
|
|
320
|
+
eventCount += 1;
|
|
321
|
+
run.eventCount += 1;
|
|
322
|
+
increment(run.targets, record.event.target);
|
|
323
|
+
projectRunActivity(run, record);
|
|
324
|
+
projectSpawnNode(nodes, record);
|
|
325
|
+
projectNodeActivity(nodes, record);
|
|
326
|
+
} else if (record.decision) {
|
|
327
|
+
decisionCount += 1;
|
|
328
|
+
run.decisionCount += 1;
|
|
329
|
+
increment(run.decisions, record.decision.kind);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const first = verified[0];
|
|
333
|
+
const last = verified.at(-1);
|
|
334
|
+
return Object.freeze({
|
|
335
|
+
pursuitId,
|
|
336
|
+
sequence: last.sequence,
|
|
337
|
+
chainTip: last.digest,
|
|
338
|
+
firstObservedAt: first.observedAt,
|
|
339
|
+
lastObservedAt: last.observedAt,
|
|
340
|
+
runs: Object.freeze([...runs.values()].sort((a, b) => a.firstSequence - b.firstSequence || a.runId.localeCompare(b.runId)).map(freezeRun)),
|
|
341
|
+
nodes: Object.freeze([...nodes.values()].sort((a, b) => a.firstSequence - b.firstSequence || a.runId.localeCompare(b.runId) || a.id.localeCompare(b.id)).map(freezeNode)),
|
|
342
|
+
eventCount,
|
|
343
|
+
decisionCount
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
function getRun(runs, runId, record) {
|
|
347
|
+
const existing = runs.get(runId);
|
|
348
|
+
if (existing) return existing;
|
|
349
|
+
const created = {
|
|
350
|
+
runId,
|
|
351
|
+
status: "running",
|
|
352
|
+
firstSequence: record.sequence,
|
|
353
|
+
lastSequence: record.sequence,
|
|
354
|
+
firstObservedAt: record.observedAt,
|
|
355
|
+
lastObservedAt: record.observedAt,
|
|
356
|
+
eventCount: 0,
|
|
357
|
+
decisionCount: 0,
|
|
358
|
+
targets: {},
|
|
359
|
+
decisions: {}
|
|
360
|
+
};
|
|
361
|
+
runs.set(runId, created);
|
|
362
|
+
return created;
|
|
363
|
+
}
|
|
364
|
+
function projectRunActivity(run, record) {
|
|
365
|
+
const event = record.event;
|
|
366
|
+
if (event?.target !== "agent.run") return;
|
|
367
|
+
const payload = objectRecord(event.payload);
|
|
368
|
+
const status = stringField(payload, "status");
|
|
369
|
+
if (event.phase === "after" || status === "done") {
|
|
370
|
+
run.status = "done";
|
|
371
|
+
run.settledAt = record.observedAt;
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (event.phase !== "error" && status !== "failed") return;
|
|
375
|
+
run.status = "failed";
|
|
376
|
+
run.settledAt = record.observedAt;
|
|
377
|
+
const error = stringField(payload, "error");
|
|
378
|
+
if (error) run.error = error;
|
|
379
|
+
}
|
|
380
|
+
function nodeKey(runId, nodeId) {
|
|
381
|
+
return `${runId}\u0000${nodeId}`;
|
|
382
|
+
}
|
|
383
|
+
function projectSpawnNode(nodes, record) {
|
|
384
|
+
const event = record.event;
|
|
385
|
+
if (event?.target !== "agent.spawn") return;
|
|
386
|
+
const payload = objectRecord(event.payload);
|
|
387
|
+
const childId = stringField(payload, "childId");
|
|
388
|
+
if (!childId) return;
|
|
389
|
+
const key = nodeKey(event.runId, childId);
|
|
390
|
+
const existing = nodes.get(key);
|
|
391
|
+
if (existing) {
|
|
392
|
+
existing.lastSequence = record.sequence;
|
|
393
|
+
existing.lastObservedAt = record.observedAt;
|
|
394
|
+
existing.eventCount += 1;
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const label = stringField(payload, "label");
|
|
398
|
+
const runtime = stringField(payload, "runtime");
|
|
399
|
+
const depth = numberField(payload, "depth");
|
|
400
|
+
const assignmentId = stringField(payload, "assignmentId");
|
|
401
|
+
nodes.set(key, {
|
|
402
|
+
id: childId,
|
|
403
|
+
...event.parentId ? { parentId: event.parentId } : {},
|
|
404
|
+
runId: event.runId,
|
|
405
|
+
...label ? { label } : {},
|
|
406
|
+
...runtime ? { runtime } : {},
|
|
407
|
+
...depth !== void 0 ? { depth } : {},
|
|
408
|
+
...assignmentId ? { assignmentId } : {},
|
|
409
|
+
...payload && Object.hasOwn(payload, "identity") ? { identity: payload.identity } : {},
|
|
410
|
+
...payload && Object.hasOwn(payload, "budget") ? { budget: payload.budget } : {},
|
|
411
|
+
status: "running",
|
|
412
|
+
firstSequence: record.sequence,
|
|
413
|
+
lastSequence: record.sequence,
|
|
414
|
+
firstObservedAt: record.observedAt,
|
|
415
|
+
lastObservedAt: record.observedAt,
|
|
416
|
+
eventCount: 1
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
function projectNodeActivity(nodes, record) {
|
|
420
|
+
const event = record.event;
|
|
421
|
+
if (event === void 0) return;
|
|
422
|
+
if (event.target === "agent.spawn") return;
|
|
423
|
+
const payload = objectRecord(event.payload);
|
|
424
|
+
const nodeId = stringField(payload, "childId") ?? stringField(payload, "nodeId") ?? stringField(payload, "workerId");
|
|
425
|
+
if (!nodeId) return;
|
|
426
|
+
const node = nodes.get(nodeKey(event.runId, nodeId));
|
|
427
|
+
if (!node) return;
|
|
428
|
+
node.lastSequence = record.sequence;
|
|
429
|
+
node.lastObservedAt = record.observedAt;
|
|
430
|
+
node.eventCount += 1;
|
|
431
|
+
if (event.target !== "agent.child") return;
|
|
432
|
+
const status = stringField(payload, "status");
|
|
433
|
+
if (status !== "done" && status !== "down") return;
|
|
434
|
+
node.status = status;
|
|
435
|
+
node.settledAt = record.observedAt;
|
|
436
|
+
if (payload && Object.hasOwn(payload, "spent")) node.spent = payload.spent;
|
|
437
|
+
const outRef = stringField(payload, "outRef");
|
|
438
|
+
if (outRef) node.outRef = outRef;
|
|
439
|
+
const score = numberField(payload, "score");
|
|
440
|
+
if (score !== void 0) node.score = score;
|
|
441
|
+
const valid = booleanField(payload, "valid");
|
|
442
|
+
if (valid !== void 0) node.valid = valid;
|
|
443
|
+
const reason = stringField(payload, "reason");
|
|
444
|
+
if (reason) node.reason = reason;
|
|
445
|
+
const infra = booleanField(payload, "infra");
|
|
446
|
+
if (infra !== void 0) node.infra = infra;
|
|
447
|
+
if (payload && Object.hasOwn(payload, "wait")) node.wait = payload.wait;
|
|
448
|
+
}
|
|
449
|
+
function freezeRun(run) {
|
|
450
|
+
return Object.freeze({
|
|
451
|
+
...run,
|
|
452
|
+
targets: Object.freeze({ ...run.targets }),
|
|
453
|
+
decisions: Object.freeze({ ...run.decisions })
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
function freezeNode(node) {
|
|
457
|
+
return Object.freeze({ ...node });
|
|
458
|
+
}
|
|
459
|
+
function increment(target, key) {
|
|
460
|
+
target[key] = (target[key] ?? 0) + 1;
|
|
461
|
+
}
|
|
462
|
+
function objectRecord(value) {
|
|
463
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
464
|
+
}
|
|
465
|
+
function stringField(value, key) {
|
|
466
|
+
const field = value?.[key];
|
|
467
|
+
return typeof field === "string" && field.length > 0 ? field : void 0;
|
|
468
|
+
}
|
|
469
|
+
function numberField(value, key) {
|
|
470
|
+
const field = value?.[key];
|
|
471
|
+
return typeof field === "number" && Number.isFinite(field) ? field : void 0;
|
|
472
|
+
}
|
|
473
|
+
function booleanField(value, key) {
|
|
474
|
+
const field = value?.[key];
|
|
475
|
+
return typeof field === "boolean" ? field : void 0;
|
|
476
|
+
}
|
|
477
|
+
//#endregion
|
|
478
|
+
//#region src/durable/supervise-pursuit.ts
|
|
479
|
+
/** A failed Runtime execution whose complete third-person projection was retained. */
|
|
480
|
+
var SupervisePursuitError = class extends Error {
|
|
481
|
+
pursuit;
|
|
482
|
+
observerPath;
|
|
483
|
+
constructor(cause, pursuit, observerPath) {
|
|
484
|
+
super(`supervisePursuit: ${errorMessage(cause)}`, { cause });
|
|
485
|
+
this.name = "SupervisePursuitError";
|
|
486
|
+
this.pursuit = pursuit;
|
|
487
|
+
this.observerPath = observerPath;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
/**
|
|
491
|
+
* One-call durable pursuit execution over the canonical `supervise()` kernel.
|
|
492
|
+
*
|
|
493
|
+
* This is an adapter, not a second executor: it composes a durable third-person
|
|
494
|
+
* observer into Runtime's existing recursive hook stream and then rebuilds the
|
|
495
|
+
* operator projection after the same `supervise()` call settles. Agents never
|
|
496
|
+
* receive the observer path or projection and their behavior does not depend on it.
|
|
497
|
+
*
|
|
498
|
+
* Every concrete execution writes only inside its own `runDir`. Cross-run pursuit
|
|
499
|
+
* aggregation is therefore lock-free at the observer layer: reuse `pursuitId` across
|
|
500
|
+
* run directories and let Intelligence join the independently verified projections.
|
|
501
|
+
*/
|
|
502
|
+
async function supervisePursuit(profile, task, opts) {
|
|
503
|
+
const pursuitId = opts.pursuitId.trim();
|
|
504
|
+
if (pursuitId.length === 0) throw new TypeError("supervisePursuit: pursuitId must be non-empty");
|
|
505
|
+
const runDir = opts.runDir.trim();
|
|
506
|
+
if (runDir.length === 0) throw new TypeError("supervisePursuit: runDir must be non-empty");
|
|
507
|
+
const observerPath = resolve(runDir, "observer.jsonl");
|
|
508
|
+
const { pursuitId: _pursuitId, hooks, ...superviseOptions } = opts;
|
|
509
|
+
const observer = createFileObserverHooks(observerPath, pursuitId);
|
|
510
|
+
const runId = superviseOptions.runId ?? "supervise";
|
|
511
|
+
const now = superviseOptions.now ?? Date.now;
|
|
512
|
+
await observer.journal.appendEvent(rootEvent(pursuitId, runId, "before", now()));
|
|
513
|
+
try {
|
|
514
|
+
const result = await supervise(profile, task, {
|
|
515
|
+
...superviseOptions,
|
|
516
|
+
hooks: withPursuitContext(pursuitId, composeRuntimeHooks(observer.hooks, hooks))
|
|
517
|
+
});
|
|
518
|
+
await observer.journal.appendEvent(rootEvent(pursuitId, runId, "after", now(), { status: "done" }));
|
|
519
|
+
return Object.freeze({
|
|
520
|
+
result,
|
|
521
|
+
pursuit: projectPursuit(await observer.journal.read()),
|
|
522
|
+
observerPath
|
|
523
|
+
});
|
|
524
|
+
} catch (error) {
|
|
525
|
+
let pursuit;
|
|
526
|
+
let observerError;
|
|
527
|
+
try {
|
|
528
|
+
await observer.journal.appendEvent(rootEvent(pursuitId, runId, "error", now(), {
|
|
529
|
+
status: "failed",
|
|
530
|
+
error: errorMessage(error)
|
|
531
|
+
}));
|
|
532
|
+
pursuit = projectPursuit(await observer.journal.read());
|
|
533
|
+
} catch (failure) {
|
|
534
|
+
observerError = failure;
|
|
535
|
+
}
|
|
536
|
+
if (observerError !== void 0 || pursuit === void 0) throw new Error("supervisePursuit: Runtime failed and durable observer completeness could not be proven", { cause: new AggregateError(observerError === void 0 ? [error] : [error, observerError]) });
|
|
537
|
+
throw new SupervisePursuitError(error, pursuit, observerPath);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
function rootEvent(pursuitId, runId, phase, timestamp, payload) {
|
|
541
|
+
return Object.freeze({
|
|
542
|
+
id: `${runId}:pursuit:${phase}:${timestamp}`,
|
|
543
|
+
pursuitId,
|
|
544
|
+
runId,
|
|
545
|
+
target: "agent.run",
|
|
546
|
+
phase,
|
|
547
|
+
timestamp,
|
|
548
|
+
...payload ? { payload: Object.freeze({ ...payload }) } : {}
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
function errorMessage(error) {
|
|
552
|
+
return error instanceof Error ? error.message : String(error);
|
|
553
|
+
}
|
|
554
|
+
//#endregion
|
|
124
555
|
//#region src/durable/supervision-discovery.ts
|
|
125
556
|
/**
|
|
126
557
|
* Discover the stable identities recorded by Runtime's durable supervision
|
|
@@ -200,6 +631,6 @@ function isNoEntError(error) {
|
|
|
200
631
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
201
632
|
}
|
|
202
633
|
//#endregion
|
|
203
|
-
export { deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn };
|
|
634
|
+
export { FileObserverJournal, SupervisePursuitError, createFileObserverHooks, deriveExecutionId, discoverDurableSupervisionRun, handleChatTurn, observerRecordDigest, projectPursuit, supervisePursuit, verifyObserverRecords };
|
|
204
635
|
|
|
205
636
|
//# sourceMappingURL=durable.js.map
|