@tangle-network/agent-runtime 0.237.0 → 0.239.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-CJqtz9SS.d.ts → activation-CRDZUoUE.d.ts} +2 -2
- package/dist/{activation-B1Ioaoxu.js → activation-CUhkPtm0.js} +2 -2
- package/dist/{activation-B1Ioaoxu.js.map → activation-CUhkPtm0.js.map} +1 -1
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +2 -2
- package/dist/{coordination-driver-DD8ffHLE.js → coordination-driver-m-2dzvu6.js} +8 -260
- package/dist/coordination-driver-m-2dzvu6.js.map +1 -0
- package/dist/{delegate-C0G0SOif.js → delegate-DRQtaWF1.js} +2 -2
- package/dist/{delegate-C0G0SOif.js.map → delegate-DRQtaWF1.js.map} +1 -1
- package/dist/durable.d.ts +2 -2
- package/dist/durable.js +26 -37
- package/dist/durable.js.map +1 -1
- package/dist/{graph-DBemtsCf.js → graph-B17RjVhE.js} +3 -3
- package/dist/{graph-DBemtsCf.js.map → graph-B17RjVhE.js.map} +1 -1
- package/dist/{improvement-cycle-Ac5z5H7M.js → improvement-cycle-BP4rDivU.js} +3 -3
- package/dist/{improvement-cycle-Ac5z5H7M.js.map → improvement-cycle-BP4rDivU.js.map} +1 -1
- package/dist/{index-BfUDevdx.d.ts → index-DTGWDyrF.d.ts} +129 -5
- package/dist/index.d.ts +6 -6
- package/dist/index.js +8 -16
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +3 -3
- package/dist/intelligence.js +3 -3
- package/dist/kernel.d.ts +3 -3
- package/dist/kernel.js +8 -8
- package/dist/{loop-runner-bin-DNVhcUz2.d.ts → loop-runner-bin-BGi5UT85.d.ts} +3 -3
- package/dist/{loop-runner-bin-B3NW0VPt.js → loop-runner-bin-mrOuv9SU.js} +3 -3
- package/dist/{loop-runner-bin-B3NW0VPt.js.map → loop-runner-bin-mrOuv9SU.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 +6 -6
- package/dist/mcp/index.js +5 -5
- package/dist/{openai-tools-C5SS8ngZ.d.ts → openai-tools-BPPAXxP2.d.ts} +2 -2
- package/dist/profiles.d.ts +2 -2
- package/dist/{provision-supervisor-Ch-vCAbq.js → provision-supervisor-CNdrsjvc.js} +3 -3
- package/dist/{provision-supervisor-Ch-vCAbq.js.map → provision-supervisor-CNdrsjvc.js.map} +1 -1
- package/dist/{runtime-BDwTXkDM.js → runtime-BsPY_v8H.js} +11 -20
- package/dist/runtime-BsPY_v8H.js.map +1 -0
- package/dist/{server-D4Ypvef9.js → server-b8uEBoK0.js} +3 -3
- package/dist/{server-D4Ypvef9.js.map → server-b8uEBoK0.js.map} +1 -1
- package/dist/{stream-agent-turn-DkiyfYi8.d.ts → stream-agent-turn-xgmm-M55.d.ts} +2 -2
- package/dist/{structural-rollout-D-Ougv7r.js → structural-rollout-sy2sH_7-.js} +2 -2
- package/dist/{structural-rollout-D-Ougv7r.js.map → structural-rollout-sy2sH_7-.js.map} +1 -1
- package/dist/{substrate-CfzWdBcq.d.ts → substrate-B_9stFWH.d.ts} +2 -2
- package/dist/{supervise-gSGndJJo.js → supervise-CBd3FyCS.js} +42 -16
- package/dist/supervise-CBd3FyCS.js.map +1 -0
- package/dist/{supervisor-B5wyKVRd.js → supervisor-De0lytFU.js} +539 -63
- package/dist/supervisor-De0lytFU.js.map +1 -0
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +12 -12
- package/dist/tui/index.d.ts +1 -1
- package/dist/tui/index.js +1 -1
- package/dist/{types-palY_bT_.d.ts → types-CE7ES_TD.d.ts} +10 -4
- package/package.json +1 -1
- package/dist/coordination-driver-DD8ffHLE.js.map +0 -1
- package/dist/runtime-BDwTXkDM.js.map +0 -1
- package/dist/supervise-gSGndJJo.js.map +0 -1
- package/dist/supervisor-B5wyKVRd.js.map +0 -1
|
@@ -287,9 +287,9 @@ async function exactSessionResult(session) {
|
|
|
287
287
|
return session.result();
|
|
288
288
|
}
|
|
289
289
|
function assertEventBinding(source, controlRef) {
|
|
290
|
-
const transport = isRecord$
|
|
291
|
-
const payload = isRecord$
|
|
292
|
-
const providerEvent = isRecord$
|
|
290
|
+
const transport = isRecord$3(source) ? source : void 0;
|
|
291
|
+
const payload = isRecord$3(source.data) ? source.data : void 0;
|
|
292
|
+
const providerEvent = isRecord$3(source.providerEvent) ? source.providerEvent : void 0;
|
|
293
293
|
const bindings = [
|
|
294
294
|
{
|
|
295
295
|
value: transport,
|
|
@@ -317,7 +317,7 @@ function assertCanonicalEventBinding(controlRef, event) {
|
|
|
317
317
|
const binding = event.request.binding;
|
|
318
318
|
if (binding.runId !== controlRef.runId || binding.provider !== controlRef.provider || binding.environmentId !== controlRef.environmentId || binding.sessionId !== controlRef.sessionId || binding.executionId !== controlRef.executionId || binding.interactionId !== event.request.id) throw new Error("provider returned an interaction for another retained execution");
|
|
319
319
|
}
|
|
320
|
-
function isRecord$
|
|
320
|
+
function isRecord$3(value) {
|
|
321
321
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
322
322
|
}
|
|
323
323
|
function assertStableText(value, label) {
|
|
@@ -391,7 +391,7 @@ function delay(ms, signal) {
|
|
|
391
391
|
*/
|
|
392
392
|
function parseCodexUsageRecord(usage, label) {
|
|
393
393
|
const record = plainRecord$2(usage);
|
|
394
|
-
if (record === void 0) throw new ValidationError$1(`${label}: usage must be an object, received ${describe(usage)}`);
|
|
394
|
+
if (record === void 0) throw new ValidationError$1(`${label}: usage must be an object, received ${describe$1(usage)}`);
|
|
395
395
|
const inputTokens = naturalNumber$1(record.input_tokens, "input_tokens", label);
|
|
396
396
|
const cachedInputTokens = naturalNumber$1(record.cached_input_tokens, "cached_input_tokens", label);
|
|
397
397
|
const outputTokens = naturalNumber$1(record.output_tokens, "output_tokens", label);
|
|
@@ -471,10 +471,10 @@ function plainRecord$2(value) {
|
|
|
471
471
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
472
472
|
}
|
|
473
473
|
function naturalNumber$1(value, field, label) {
|
|
474
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new ValidationError$1(`${label}: usage.${field} must be a non-negative safe integer, received ${describe(value)}`);
|
|
474
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new ValidationError$1(`${label}: usage.${field} must be a non-negative safe integer, received ${describe$1(value)}`);
|
|
475
475
|
return value;
|
|
476
476
|
}
|
|
477
|
-
function describe(value) {
|
|
477
|
+
function describe$1(value) {
|
|
478
478
|
if (value === null) return "null";
|
|
479
479
|
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
480
480
|
if (typeof value === "string") return JSON.stringify(value);
|
|
@@ -518,7 +518,7 @@ function assertRuntimeTimestamp(value, label) {
|
|
|
518
518
|
const harnessNativePayloadType = "raw";
|
|
519
519
|
/** Parse canonical payload fields without treating transport identity as event data. */
|
|
520
520
|
function parseCanonicalTransportEvent(type, data, normalized, source) {
|
|
521
|
-
if (!isRecord$
|
|
521
|
+
if (!isRecord$2(data)) throw new Error(`${source} emitted a canonical event without an object payload`);
|
|
522
522
|
const outerType = String(type ?? "");
|
|
523
523
|
const harnessNative = outerType === harnessNativePayloadType;
|
|
524
524
|
if (normalized !== void 0) {
|
|
@@ -540,10 +540,10 @@ function parseCanonicalTransportEvent(type, data, normalized, source) {
|
|
|
540
540
|
}
|
|
541
541
|
/** Extract shared event identity without privileging one provider wire shape. */
|
|
542
542
|
function extractTransportEventIdentity(event) {
|
|
543
|
-
const record = isRecord$
|
|
544
|
-
const data = isRecord$
|
|
545
|
-
const providerEvent = isRecord$
|
|
546
|
-
const providerData = isRecord$
|
|
543
|
+
const record = isRecord$2(event) ? event : {};
|
|
544
|
+
const data = isRecord$2(record.data) ? record.data : {};
|
|
545
|
+
const providerEvent = isRecord$2(record.providerEvent) ? record.providerEvent : {};
|
|
546
|
+
const providerData = isRecord$2(providerEvent.data) ? providerEvent.data : {};
|
|
547
547
|
const eventId = canonicalEventId(record, data, providerEvent, providerData);
|
|
548
548
|
const cursor = stableString(record.cursor ?? data.cursor ?? providerEvent.cursor ?? providerData.cursor);
|
|
549
549
|
const sequence = optionalSequence(record.sequence ?? data.sequence ?? providerEvent.sequence ?? providerData.sequence);
|
|
@@ -591,7 +591,7 @@ function optionalTimestamp(value) {
|
|
|
591
591
|
assertRuntimeTimestamp(value, "occurredAt");
|
|
592
592
|
return value;
|
|
593
593
|
}
|
|
594
|
-
function isRecord$
|
|
594
|
+
function isRecord$2(value) {
|
|
595
595
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
596
596
|
}
|
|
597
597
|
//#endregion
|
|
@@ -2365,22 +2365,66 @@ function providerAttemptEvidence(model) {
|
|
|
2365
2365
|
}
|
|
2366
2366
|
//#endregion
|
|
2367
2367
|
//#region src/durable/jsonl-file.ts
|
|
2368
|
-
/**
|
|
2369
|
-
* A
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2368
|
+
/** Read a fixed file prefix one record at a time, never allocating the whole journal as a string.
|
|
2369
|
+
* A reader cannot chase concurrent appends forever. Missing files are empty only when requested;
|
|
2370
|
+
* corruption, short reads, and other I/O errors remain failures. Closing the iterator closes the fd. */
|
|
2371
|
+
async function* readCommittedJsonLines(path, options = {}) {
|
|
2372
|
+
const fs = await import("node:fs/promises");
|
|
2373
|
+
let handle;
|
|
2374
|
+
try {
|
|
2375
|
+
handle = await fs.open(path, "r");
|
|
2376
|
+
} catch (error) {
|
|
2377
|
+
if (options.allowMissing && isNoEntError(error)) return;
|
|
2378
|
+
throw error;
|
|
2379
|
+
}
|
|
2380
|
+
try {
|
|
2381
|
+
const { size } = await handle.stat();
|
|
2382
|
+
if (size === 0) return;
|
|
2383
|
+
const { StringDecoder } = await import("node:string_decoder");
|
|
2384
|
+
const decoder = new StringDecoder("utf8");
|
|
2385
|
+
const stream = handle.createReadStream({
|
|
2386
|
+
autoClose: false,
|
|
2387
|
+
end: size - 1
|
|
2388
|
+
});
|
|
2389
|
+
const fragments = [];
|
|
2390
|
+
let lineNumber = 1;
|
|
2376
2391
|
try {
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2392
|
+
for await (const chunk of stream) {
|
|
2393
|
+
options.onBytes?.(chunk);
|
|
2394
|
+
const text = decoder.write(chunk);
|
|
2395
|
+
let start = 0;
|
|
2396
|
+
let end = text.indexOf("\n", start);
|
|
2397
|
+
while (end !== -1) {
|
|
2398
|
+
fragments.push(text.slice(start, end));
|
|
2399
|
+
const record = parseRecord(fragments.join(""), path, lineNumber++, true);
|
|
2400
|
+
fragments.length = 0;
|
|
2401
|
+
if (record !== void 0) yield record.value;
|
|
2402
|
+
start = end + 1;
|
|
2403
|
+
end = text.indexOf("\n", start);
|
|
2404
|
+
}
|
|
2405
|
+
if (start < text.length) fragments.push(text.slice(start));
|
|
2406
|
+
}
|
|
2407
|
+
if (stream.bytesRead !== size) throw new Error(`${path}: journal changed while reading its committed prefix`);
|
|
2408
|
+
fragments.push(decoder.end());
|
|
2409
|
+
const record = parseRecord(fragments.join(""), path, lineNumber, false);
|
|
2410
|
+
if (record !== void 0) yield record.value;
|
|
2411
|
+
} finally {
|
|
2412
|
+
stream.destroy();
|
|
2381
2413
|
}
|
|
2414
|
+
} finally {
|
|
2415
|
+
await handle.close();
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
/** An invalid unterminated tail may be a torn write; other parse failures are not evidence of one. */
|
|
2419
|
+
function parseRecord(line, source, lineNumber, terminated) {
|
|
2420
|
+
if (line.length === 0) return void 0;
|
|
2421
|
+
try {
|
|
2422
|
+
return { value: JSON.parse(line) };
|
|
2423
|
+
} catch (cause) {
|
|
2424
|
+
if (!(cause instanceof SyntaxError)) throw cause;
|
|
2425
|
+
if (!terminated) return void 0;
|
|
2426
|
+
throw new Error(`${source}: malformed JSONL record at line ${lineNumber}`, { cause });
|
|
2382
2427
|
}
|
|
2383
|
-
return records;
|
|
2384
2428
|
}
|
|
2385
2429
|
/** FileHandle.write may legally make a short write. Loop until every byte is appended. */
|
|
2386
2430
|
async function writeAllBytes(handle, value) {
|
|
@@ -3599,13 +3643,13 @@ function normalizeCodexDebugPromptNewlines(value) {
|
|
|
3599
3643
|
function jsonTextContains(value, needle) {
|
|
3600
3644
|
if (typeof value === "string") return value.includes(needle);
|
|
3601
3645
|
if (Array.isArray(value)) return value.some((item) => jsonTextContains(item, needle));
|
|
3602
|
-
if (isRecord(value)) return Object.values(value).some((item) => jsonTextContains(item, needle));
|
|
3646
|
+
if (isRecord$1(value)) return Object.values(value).some((item) => jsonTextContains(item, needle));
|
|
3603
3647
|
return false;
|
|
3604
3648
|
}
|
|
3605
3649
|
function redactJsonText(value, text) {
|
|
3606
3650
|
if (typeof value === "string") return value.replaceAll(text, "");
|
|
3607
3651
|
if (Array.isArray(value)) return value.map((item) => redactJsonText(item, text));
|
|
3608
|
-
if (isRecord(value)) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactJsonText(item, text)]));
|
|
3652
|
+
if (isRecord$1(value)) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactJsonText(item, text)]));
|
|
3609
3653
|
return value;
|
|
3610
3654
|
}
|
|
3611
3655
|
const CODEX_AUTH_ENV = [
|
|
@@ -3997,11 +4041,11 @@ function parseCodexTokenUsage(stdout) {
|
|
|
3997
4041
|
} catch {
|
|
3998
4042
|
throw new ValidationError$1(`runLocalHarness: Codex JSONL line ${index + 1} is not valid JSON`);
|
|
3999
4043
|
}
|
|
4000
|
-
if (isRecord(event) && event.type === "turn.completed") completed.push(event);
|
|
4044
|
+
if (isRecord$1(event) && event.type === "turn.completed") completed.push(event);
|
|
4001
4045
|
}
|
|
4002
4046
|
if (completed.length !== 1) throw new ValidationError$1(`runLocalHarness: expected exactly one Codex turn.completed usage event, received ${completed.length}`);
|
|
4003
4047
|
const event = completed[0];
|
|
4004
|
-
if (!isRecord(event) || !isRecord(event.usage)) throw new ValidationError$1("runLocalHarness: Codex turn.completed event is missing usage");
|
|
4048
|
+
if (!isRecord$1(event) || !isRecord$1(event.usage)) throw new ValidationError$1("runLocalHarness: Codex turn.completed event is missing usage");
|
|
4005
4049
|
const record = parseCodexUsageRecord(event.usage, "runLocalHarness: Codex");
|
|
4006
4050
|
return {
|
|
4007
4051
|
inputTokens: record.inputTokens,
|
|
@@ -4011,7 +4055,7 @@ function parseCodexTokenUsage(stdout) {
|
|
|
4011
4055
|
...record.cacheWriteInputTokens === void 0 ? {} : { cacheWriteInputTokens: record.cacheWriteInputTokens }
|
|
4012
4056
|
};
|
|
4013
4057
|
}
|
|
4014
|
-
function isRecord(value) {
|
|
4058
|
+
function isRecord$1(value) {
|
|
4015
4059
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4016
4060
|
}
|
|
4017
4061
|
//#endregion
|
|
@@ -8083,7 +8127,8 @@ async function* streamProviderExecutor(args) {
|
|
|
8083
8127
|
settled = {
|
|
8084
8128
|
...result.outcome?.status === "failed" ? { outcome: {
|
|
8085
8129
|
success: false,
|
|
8086
|
-
...result.outcome.error ? { error: result.outcome.error } : {}
|
|
8130
|
+
...result.outcome.error ? { error: result.outcome.error } : {},
|
|
8131
|
+
...result.outcome.errorCode ? { errorCode: result.outcome.errorCode } : {}
|
|
8087
8132
|
} } : {},
|
|
8088
8133
|
outRef: contentRef$1(`provider:${args.provider.name}`, result),
|
|
8089
8134
|
out: result,
|
|
@@ -13292,12 +13337,20 @@ function priceUnreceiptedWork(work) {
|
|
|
13292
13337
|
const outcomeSchema = AgentTurnResultSchema.pick({
|
|
13293
13338
|
success: true,
|
|
13294
13339
|
error: true
|
|
13295
|
-
}).strict();
|
|
13340
|
+
}).extend({ errorCode: AgentTurnResultSchema.shape.error }).strict();
|
|
13296
13341
|
/** Read only the executor envelope; application output and grades are not execution status. */
|
|
13297
|
-
function
|
|
13342
|
+
function executorFailure(result) {
|
|
13298
13343
|
if (result.outcome === void 0) return void 0;
|
|
13299
13344
|
const outcome = outcomeSchema.parse(result.outcome);
|
|
13300
|
-
|
|
13345
|
+
if (outcome.success) return void 0;
|
|
13346
|
+
return {
|
|
13347
|
+
error: outcome.error ?? "Agent execution failed",
|
|
13348
|
+
...outcome.errorCode === void 0 ? {} : { errorCode: outcome.errorCode }
|
|
13349
|
+
};
|
|
13350
|
+
}
|
|
13351
|
+
/** The failure text alone, for callers that record a settlement reason. */
|
|
13352
|
+
function executorFailureReason(result) {
|
|
13353
|
+
return executorFailure(result)?.error;
|
|
13301
13354
|
}
|
|
13302
13355
|
/** Preserve deterministic result pointers across built-in executors and replay.
|
|
13303
13356
|
* FNV-1a is an identity convention here, not a cryptographic integrity check. */
|
|
@@ -15572,18 +15625,10 @@ var FileSpawnJournal = class {
|
|
|
15572
15625
|
this.path = path;
|
|
15573
15626
|
}
|
|
15574
15627
|
async loadTree(root) {
|
|
15575
|
-
const fs = await import("node:fs/promises");
|
|
15576
|
-
let text;
|
|
15577
|
-
try {
|
|
15578
|
-
text = await fs.readFile(this.path, "utf8");
|
|
15579
|
-
} catch (err) {
|
|
15580
|
-
if (isNoEntError(err)) return void 0;
|
|
15581
|
-
throw err;
|
|
15582
|
-
}
|
|
15583
15628
|
let begun = false;
|
|
15584
15629
|
const events = [];
|
|
15585
15630
|
const index = new SpawnEventIndex(root);
|
|
15586
|
-
for (const record of
|
|
15631
|
+
for await (const record of readCommittedJsonLines(this.path, { allowMissing: true })) {
|
|
15587
15632
|
if (record.root !== root) continue;
|
|
15588
15633
|
if (record.kind === "begin") begun = true;
|
|
15589
15634
|
else {
|
|
@@ -15630,26 +15675,22 @@ var FileSpawnJournal = class {
|
|
|
15630
15675
|
});
|
|
15631
15676
|
}
|
|
15632
15677
|
async validationIndex() {
|
|
15633
|
-
const fs = await import("node:fs/promises");
|
|
15634
15678
|
const stamp = await this.fileStamp();
|
|
15635
15679
|
if (this.appendIndex && this.appendIndex.stamp === stamp) return this.appendIndex;
|
|
15636
15680
|
this.appendIndex = void 0;
|
|
15637
15681
|
const trees = /* @__PURE__ */ new Map();
|
|
15638
|
-
if (stamp !== void 0) {
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
if (!tree) throw new Error(`spawn journal corrupted: event for tree '${record.root}' precedes its begin record`);
|
|
15649
|
-
tree.index.assert(record.event);
|
|
15650
|
-
tree.index.add(record.event);
|
|
15651
|
-
}
|
|
15682
|
+
if (stamp !== void 0) for await (const record of readCommittedJsonLines(this.path)) if (record.kind === "begin") {
|
|
15683
|
+
if (!trees.has(record.root)) trees.set(record.root, {
|
|
15684
|
+
begunAt: record.at,
|
|
15685
|
+
index: new SpawnEventIndex(record.root)
|
|
15686
|
+
});
|
|
15687
|
+
} else {
|
|
15688
|
+
const tree = trees.get(record.root);
|
|
15689
|
+
if (!tree) throw new Error(`spawn journal corrupted: event for tree '${record.root}' precedes its begin record`);
|
|
15690
|
+
tree.index.assert(record.event);
|
|
15691
|
+
tree.index.add(record.event);
|
|
15652
15692
|
}
|
|
15693
|
+
if (await this.fileStamp() !== stamp) throw new Error("spawn journal changed while rebuilding its append index");
|
|
15653
15694
|
this.appendIndex = {
|
|
15654
15695
|
stamp,
|
|
15655
15696
|
trees
|
|
@@ -19058,6 +19099,9 @@ function errorProperty(error, field) {
|
|
|
19058
19099
|
return `[unreadable error ${field}]`;
|
|
19059
19100
|
}
|
|
19060
19101
|
}
|
|
19102
|
+
/** An HTTP status carried on a thrown error, read without trusting its getter. Provider SDKs put
|
|
19103
|
+
* one on their own error classes, so this is how a refusal is recognised outside the transport
|
|
19104
|
+
* taxonomy — for the persisted message here, and for the driver's retry verdict. */
|
|
19061
19105
|
function errorHttpStatus(error) {
|
|
19062
19106
|
try {
|
|
19063
19107
|
const status = Reflect.get(error, "status");
|
|
@@ -20397,6 +20441,314 @@ async function prepareScopeResume(opts, events, signal, now, parentId = opts.run
|
|
|
20397
20441
|
};
|
|
20398
20442
|
}
|
|
20399
20443
|
//#endregion
|
|
20444
|
+
//#region src/mcp/tools/spawn-resource-paths.ts
|
|
20445
|
+
/**
|
|
20446
|
+
* Inline resources a manager hands a child by PATH instead of by content.
|
|
20447
|
+
*
|
|
20448
|
+
* The canonical profile schema knows two resource kinds, `inline` (a content string) and `github`.
|
|
20449
|
+
* A manager that wants a child to receive a data file therefore had one way to do it: emit the
|
|
20450
|
+
* bytes inside its own `spawn` tool call. That is a transcription by a language model, and it does
|
|
20451
|
+
* not survive size. Measured 2026-09-12 (discovery-lab `mech-interp-foundations-glm2-b-20260912b`):
|
|
20452
|
+
* a 29,144-character base64 payload on the manager's disk reached three children as 15,928
|
|
20453
|
+
* characters with 11 substitutions, and a fourth child received the literal placeholder the
|
|
20454
|
+
* manager meant to replace. Seven blind-check attempts across two runs delivered no data.
|
|
20455
|
+
*
|
|
20456
|
+
* `{ kind: 'inline', name, path }` lets the coordination server be the transport: it reads the
|
|
20457
|
+
* file from the manager's workspace, fills `content`, and the profile that reaches the schema,
|
|
20458
|
+
* the journal, and the provider is an ordinary inline resource. Nothing downstream changes; the
|
|
20459
|
+
* model's output stops carrying the bytes.
|
|
20460
|
+
*
|
|
20461
|
+
* Where the bytes come from is a {@link SpawnResourceReader}, and there are two:
|
|
20462
|
+
* - a HOST DIRECTORY, for a manager whose driver runs on this host (a loopback bridge with a
|
|
20463
|
+
* `cwd`), read through the descriptor-identity checks below;
|
|
20464
|
+
* - the manager's OWN EXECUTION ENVIRONMENT, for a manager inside a provider sandbox, read
|
|
20465
|
+
* through the environment's `read()`. The coordination server cannot see a sandbox filesystem,
|
|
20466
|
+
* but the provider that created the sandbox can, and it is already in hand.
|
|
20467
|
+
*
|
|
20468
|
+
* The second reader exists because the first one's absence was not a refusal a manager could
|
|
20469
|
+
* work around. Measured 2026-09-17 (discovery-lab `mech-interp-foundations-sandbox-a-20260917h`):
|
|
20470
|
+
* a sandbox-rooted director, refused by path, fell back to emitting its files as content and got
|
|
20471
|
+
* 5 characters wrong in 24,008, destroying 3 of 7 files including its instrument and both
|
|
20472
|
+
* drivers. At that rate 20 KB of mounts all arrive intact about 2% of the time, and gzip makes it
|
|
20473
|
+
* worse by turning one wrong character into total loss. Four consecutive runs in that lane lost
|
|
20474
|
+
* their research children to this. There is no encoding that fixes a transcription channel; the
|
|
20475
|
+
* only fix is a channel with no model in it.
|
|
20476
|
+
*
|
|
20477
|
+
* Fail-closed rules, each with the reason a manager needs to fix its call:
|
|
20478
|
+
* - no reader available → refused (neither a host directory nor an environment with `read`)
|
|
20479
|
+
* - absolute path or `..` escape → refused by both readers; a symlink that resolves outside the
|
|
20480
|
+
* root → refused by the host reader (the environment reader delegates containment to the
|
|
20481
|
+
* provider, which serves only the sandbox's own workspace)
|
|
20482
|
+
* - not a regular file → refused
|
|
20483
|
+
* - over {@link SPAWN_RESOURCE_PATH_MAX_BYTES} → refused naming the bound
|
|
20484
|
+
* - bytes that are not valid UTF-8 → refused; `content` is a string, so binary data is encoded
|
|
20485
|
+
* (base64) by the manager first, exactly as it would be for a content resource
|
|
20486
|
+
*/
|
|
20487
|
+
const SPAWN_RESOURCE_PATH_MAX_BYTES = 4 * 1024 * 1024;
|
|
20488
|
+
/** The host-directory reader: a manager whose workspace is a directory this process can open. */
|
|
20489
|
+
function hostDirectoryReader(root) {
|
|
20490
|
+
return {
|
|
20491
|
+
describe: `workspace root ${root}`,
|
|
20492
|
+
read: (requested) => readUnderRoot(root, requested)
|
|
20493
|
+
};
|
|
20494
|
+
}
|
|
20495
|
+
/**
|
|
20496
|
+
* The environment reader: a manager running inside a provider environment whose `read()` serves
|
|
20497
|
+
* its own workspace. The provider owns containment — it will not serve a path outside the box —
|
|
20498
|
+
* but the relative-path and `..` rules are enforced here too, so the refusal names the rule the
|
|
20499
|
+
* manager broke rather than whatever the provider says about an escape it declined.
|
|
20500
|
+
*
|
|
20501
|
+
* `read()` returns a string, so an environment cannot hand back bytes that are not UTF-8; the
|
|
20502
|
+
* provider has already decoded. The size bound is checked on what arrives.
|
|
20503
|
+
*/
|
|
20504
|
+
function environmentReader(environment) {
|
|
20505
|
+
return {
|
|
20506
|
+
describe: `environment ${environment.id}`,
|
|
20507
|
+
async read(requested) {
|
|
20508
|
+
if (requested.length === 0) return {
|
|
20509
|
+
ok: false,
|
|
20510
|
+
reason: "path is empty"
|
|
20511
|
+
};
|
|
20512
|
+
if (isAbsolute(requested)) return {
|
|
20513
|
+
ok: false,
|
|
20514
|
+
reason: `path must be relative to the manager's workspace, not absolute (${requested})`
|
|
20515
|
+
};
|
|
20516
|
+
if (requested.split(/[\\/]/u).includes("..")) return {
|
|
20517
|
+
ok: false,
|
|
20518
|
+
reason: `path ${requested} leaves the manager's workspace`
|
|
20519
|
+
};
|
|
20520
|
+
let content;
|
|
20521
|
+
try {
|
|
20522
|
+
content = await environment.read(requested);
|
|
20523
|
+
} catch (error) {
|
|
20524
|
+
return {
|
|
20525
|
+
ok: false,
|
|
20526
|
+
reason: `path ${requested}: ${describe(error)} (read from environment ${environment.id})`
|
|
20527
|
+
};
|
|
20528
|
+
}
|
|
20529
|
+
const bytes = Buffer.from(content, "utf8");
|
|
20530
|
+
if (bytes.length > 4194304) return {
|
|
20531
|
+
ok: false,
|
|
20532
|
+
reason: `path ${requested} is ${bytes.length} bytes; an inline resource is at most ${SPAWN_RESOURCE_PATH_MAX_BYTES} bytes`
|
|
20533
|
+
};
|
|
20534
|
+
return {
|
|
20535
|
+
ok: true,
|
|
20536
|
+
content,
|
|
20537
|
+
byteLength: bytes.length,
|
|
20538
|
+
sha256: createHash("sha256").update(bytes).digest("hex")
|
|
20539
|
+
};
|
|
20540
|
+
}
|
|
20541
|
+
};
|
|
20542
|
+
}
|
|
20543
|
+
function isRecord(value) {
|
|
20544
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20545
|
+
}
|
|
20546
|
+
/** An inline ref that names a path and carries no content. Anything else is left untouched so the
|
|
20547
|
+
* canonical schema keeps its verdict on it. */
|
|
20548
|
+
function pathInlineRef(value) {
|
|
20549
|
+
if (!isRecord(value)) return void 0;
|
|
20550
|
+
if (value.kind !== "inline" || value.content !== void 0) return void 0;
|
|
20551
|
+
if (typeof value.path !== "string" || typeof value.name !== "string") return void 0;
|
|
20552
|
+
return {
|
|
20553
|
+
kind: "inline",
|
|
20554
|
+
name: value.name,
|
|
20555
|
+
path: value.path
|
|
20556
|
+
};
|
|
20557
|
+
}
|
|
20558
|
+
async function readUnderRoot(root, requested) {
|
|
20559
|
+
if (requested.length === 0) return {
|
|
20560
|
+
ok: false,
|
|
20561
|
+
reason: "path is empty"
|
|
20562
|
+
};
|
|
20563
|
+
if (isAbsolute(requested)) return {
|
|
20564
|
+
ok: false,
|
|
20565
|
+
reason: `path must be relative to the workspace root, not absolute (${requested})`
|
|
20566
|
+
};
|
|
20567
|
+
const rootReal = await realpath(root).catch(() => void 0);
|
|
20568
|
+
if (rootReal === void 0) return {
|
|
20569
|
+
ok: false,
|
|
20570
|
+
reason: `workspace root ${root} does not exist`
|
|
20571
|
+
};
|
|
20572
|
+
const candidate = resolve(rootReal, requested);
|
|
20573
|
+
if (!inside(rootReal, candidate)) return {
|
|
20574
|
+
ok: false,
|
|
20575
|
+
reason: `path ${requested} leaves the workspace root`
|
|
20576
|
+
};
|
|
20577
|
+
let handle;
|
|
20578
|
+
try {
|
|
20579
|
+
handle = await open(candidate, "r");
|
|
20580
|
+
} catch (error) {
|
|
20581
|
+
return {
|
|
20582
|
+
ok: false,
|
|
20583
|
+
reason: `path ${requested}: ${describe(error)}`
|
|
20584
|
+
};
|
|
20585
|
+
}
|
|
20586
|
+
try {
|
|
20587
|
+
const info = await handle.stat();
|
|
20588
|
+
if (!info.isFile()) return {
|
|
20589
|
+
ok: false,
|
|
20590
|
+
reason: `path ${requested} is not a regular file`
|
|
20591
|
+
};
|
|
20592
|
+
const real = await realpath(candidate).catch(() => void 0);
|
|
20593
|
+
if (real === void 0 || !inside(rootReal, real)) return {
|
|
20594
|
+
ok: false,
|
|
20595
|
+
reason: `path ${requested} resolves outside the workspace root`
|
|
20596
|
+
};
|
|
20597
|
+
const named = await stat(real).catch(() => void 0);
|
|
20598
|
+
if (named === void 0 || named.dev !== info.dev || named.ino !== info.ino) return {
|
|
20599
|
+
ok: false,
|
|
20600
|
+
reason: `path ${requested} changed while it was being read; retry the spawn`
|
|
20601
|
+
};
|
|
20602
|
+
if (info.size > 4194304) return {
|
|
20603
|
+
ok: false,
|
|
20604
|
+
reason: `path ${requested} is ${info.size} bytes; an inline resource is at most ${SPAWN_RESOURCE_PATH_MAX_BYTES} bytes`
|
|
20605
|
+
};
|
|
20606
|
+
const bytes = await handle.readFile();
|
|
20607
|
+
if (bytes.length > 4194304) return {
|
|
20608
|
+
ok: false,
|
|
20609
|
+
reason: `path ${requested} is ${bytes.length} bytes; an inline resource is at most ${SPAWN_RESOURCE_PATH_MAX_BYTES} bytes`
|
|
20610
|
+
};
|
|
20611
|
+
const content = bytes.toString("utf8");
|
|
20612
|
+
if (!Buffer.from(content, "utf8").equals(bytes)) return {
|
|
20613
|
+
ok: false,
|
|
20614
|
+
reason: `path ${requested} is not valid UTF-8; encode binary data (base64) before handing it to a child`
|
|
20615
|
+
};
|
|
20616
|
+
return {
|
|
20617
|
+
ok: true,
|
|
20618
|
+
content,
|
|
20619
|
+
byteLength: bytes.length,
|
|
20620
|
+
sha256: createHash("sha256").update(bytes).digest("hex")
|
|
20621
|
+
};
|
|
20622
|
+
} catch (error) {
|
|
20623
|
+
return {
|
|
20624
|
+
ok: false,
|
|
20625
|
+
reason: `path ${requested}: ${describe(error)}`
|
|
20626
|
+
};
|
|
20627
|
+
} finally {
|
|
20628
|
+
await handle.close().catch(() => void 0);
|
|
20629
|
+
}
|
|
20630
|
+
}
|
|
20631
|
+
/** `target` is `rootReal` itself or strictly below it; both must already be absolute. */
|
|
20632
|
+
function inside(rootReal, target) {
|
|
20633
|
+
const rel = relative(rootReal, target);
|
|
20634
|
+
return rel !== "" && !rel.startsWith(`..${sep}`) && rel !== ".." && !isAbsolute(rel);
|
|
20635
|
+
}
|
|
20636
|
+
function describe(error) {
|
|
20637
|
+
const code = error?.code;
|
|
20638
|
+
if (code === "ENOENT") return "does not exist under the workspace root";
|
|
20639
|
+
if (code === "EACCES" || code === "EPERM") return "is not readable by the runtime process";
|
|
20640
|
+
if (code === "EISDIR") return "is not a regular file";
|
|
20641
|
+
return error instanceof Error ? error.message : String(error);
|
|
20642
|
+
}
|
|
20643
|
+
/**
|
|
20644
|
+
* Replace every `{ kind: 'inline', name, path }` under `profile.resources` with the inline
|
|
20645
|
+
* resource its bytes make. Returns the profile unchanged (same reference) when nothing names a
|
|
20646
|
+
* path, so callers pay nothing on the common case.
|
|
20647
|
+
*
|
|
20648
|
+
* `source` is a host directory (a string, kept for the existing call sites), a
|
|
20649
|
+
* {@link SpawnResourceReader}, or `undefined` when the manager has neither — in which case a path
|
|
20650
|
+
* is refused with the reason and what to do instead.
|
|
20651
|
+
*/
|
|
20652
|
+
async function resolveSpawnResourcePaths(profile, source) {
|
|
20653
|
+
if (!isRecord(profile) || !isRecord(profile.resources)) return {
|
|
20654
|
+
ok: true,
|
|
20655
|
+
profile,
|
|
20656
|
+
resolved: []
|
|
20657
|
+
};
|
|
20658
|
+
const reader = typeof source === "string" ? hostDirectoryReader(source) : source;
|
|
20659
|
+
const resources = profile.resources;
|
|
20660
|
+
const resolved = [];
|
|
20661
|
+
let touched = false;
|
|
20662
|
+
const next = { ...resources };
|
|
20663
|
+
const substitute = async (at, ref) => {
|
|
20664
|
+
if (reader === void 0) return { error: "this manager has no workspace root the coordination server may read (no host directory and no environment with read), so a resource by path cannot be resolved; pass content, or a github resource" };
|
|
20665
|
+
const read = await reader.read(ref.path);
|
|
20666
|
+
if (!read.ok) return { error: read.reason };
|
|
20667
|
+
resolved.push({
|
|
20668
|
+
at,
|
|
20669
|
+
path: ref.path,
|
|
20670
|
+
byteLength: read.byteLength,
|
|
20671
|
+
sha256: read.sha256
|
|
20672
|
+
});
|
|
20673
|
+
touched = true;
|
|
20674
|
+
return {
|
|
20675
|
+
kind: "inline",
|
|
20676
|
+
name: ref.name,
|
|
20677
|
+
content: read.content
|
|
20678
|
+
};
|
|
20679
|
+
};
|
|
20680
|
+
for (const list of [
|
|
20681
|
+
"tools",
|
|
20682
|
+
"skills",
|
|
20683
|
+
"agents"
|
|
20684
|
+
]) {
|
|
20685
|
+
const entries = resources[list];
|
|
20686
|
+
if (!Array.isArray(entries)) continue;
|
|
20687
|
+
const out = [];
|
|
20688
|
+
for (const [index, entry] of entries.entries()) {
|
|
20689
|
+
const ref = pathInlineRef(entry);
|
|
20690
|
+
if (ref === void 0) {
|
|
20691
|
+
out.push(entry);
|
|
20692
|
+
continue;
|
|
20693
|
+
}
|
|
20694
|
+
const at = `${list}[${index}]`;
|
|
20695
|
+
const replaced = await substitute(at, ref);
|
|
20696
|
+
if ("error" in replaced) return {
|
|
20697
|
+
ok: false,
|
|
20698
|
+
at,
|
|
20699
|
+
reason: replaced.error
|
|
20700
|
+
};
|
|
20701
|
+
out.push(replaced);
|
|
20702
|
+
}
|
|
20703
|
+
next[list] = out;
|
|
20704
|
+
}
|
|
20705
|
+
if (Array.isArray(resources.files)) {
|
|
20706
|
+
const out = [];
|
|
20707
|
+
for (const [index, mount] of resources.files.entries()) {
|
|
20708
|
+
const ref = isRecord(mount) ? pathInlineRef(mount.resource) : void 0;
|
|
20709
|
+
if (ref === void 0 || !isRecord(mount)) {
|
|
20710
|
+
out.push(mount);
|
|
20711
|
+
continue;
|
|
20712
|
+
}
|
|
20713
|
+
const at = `files[${index}].resource`;
|
|
20714
|
+
const replaced = await substitute(at, ref);
|
|
20715
|
+
if ("error" in replaced) return {
|
|
20716
|
+
ok: false,
|
|
20717
|
+
at,
|
|
20718
|
+
reason: replaced.error
|
|
20719
|
+
};
|
|
20720
|
+
out.push({
|
|
20721
|
+
...mount,
|
|
20722
|
+
resource: replaced
|
|
20723
|
+
});
|
|
20724
|
+
}
|
|
20725
|
+
next.files = out;
|
|
20726
|
+
}
|
|
20727
|
+
const instructions = pathInlineRef(resources.instructions);
|
|
20728
|
+
if (instructions !== void 0) {
|
|
20729
|
+
const replaced = await substitute("instructions", instructions);
|
|
20730
|
+
if ("error" in replaced) return {
|
|
20731
|
+
ok: false,
|
|
20732
|
+
at: "instructions",
|
|
20733
|
+
reason: replaced.error
|
|
20734
|
+
};
|
|
20735
|
+
next.instructions = replaced;
|
|
20736
|
+
}
|
|
20737
|
+
if (!touched) return {
|
|
20738
|
+
ok: true,
|
|
20739
|
+
profile,
|
|
20740
|
+
resolved: []
|
|
20741
|
+
};
|
|
20742
|
+
return {
|
|
20743
|
+
ok: true,
|
|
20744
|
+
profile: {
|
|
20745
|
+
...profile,
|
|
20746
|
+
resources: next
|
|
20747
|
+
},
|
|
20748
|
+
resolved
|
|
20749
|
+
};
|
|
20750
|
+
}
|
|
20751
|
+
//#endregion
|
|
20400
20752
|
//#region src/runtime/supervise/retained-scope-owner.ts
|
|
20401
20753
|
const owners = /* @__PURE__ */ new WeakMap();
|
|
20402
20754
|
/** The scope owns these writers; provider adapters receive only the restricted context. */
|
|
@@ -20484,6 +20836,84 @@ function bindScopeRetainedOwnerProvider(scope, provider) {
|
|
|
20484
20836
|
const state = owners.get(scope);
|
|
20485
20837
|
if (state) state.provider = provider;
|
|
20486
20838
|
}
|
|
20839
|
+
/**
|
|
20840
|
+
* Bind where the owner's CURRENT environment id can be read from. The drive harness supplies a
|
|
20841
|
+
* resolver over its active executor's materialization receipt, which every provider path
|
|
20842
|
+
* publishes once the environment exists. The `environment` admission is written only on the
|
|
20843
|
+
* retained path, and the production tangle provider declares no `retainedControl`, so a reader
|
|
20844
|
+
* that scanned admissions alone would refuse every read on a real sandbox root while looking
|
|
20845
|
+
* correct in every test that uses the retained fixture.
|
|
20846
|
+
*/
|
|
20847
|
+
function bindScopeRetainedOwnerEnvironmentId(scope, liveEnvironmentId) {
|
|
20848
|
+
const state = owners.get(scope);
|
|
20849
|
+
if (state) state.liveEnvironmentId = liveEnvironmentId;
|
|
20850
|
+
}
|
|
20851
|
+
/**
|
|
20852
|
+
* The owner's environment as a source of by-path spawn resources.
|
|
20853
|
+
*
|
|
20854
|
+
* Built once per scope, before the environment exists: the coordination tools are created ahead
|
|
20855
|
+
* of the owner's first turn, and the environment is admitted during it. So the reader resolves
|
|
20856
|
+
* its target on every read — the latest environment admission on this node, reconstructed through
|
|
20857
|
+
* the bound provider the same way cleanup reconstructs it, with the same identity check. A manager
|
|
20858
|
+
* that has not yet been admitted, or whose provider was never bound, gets a refusal that names the
|
|
20859
|
+
* missing piece rather than a read from nowhere.
|
|
20860
|
+
*
|
|
20861
|
+
* The environment's `read()` serves the sandbox's own workspace; containment against that
|
|
20862
|
+
* workspace is the provider's. The relative-path and `..` rules are the reader's, in
|
|
20863
|
+
* `environmentReader`, so a manager is refused for the rule it broke.
|
|
20864
|
+
*/
|
|
20865
|
+
function scopeRetainedOwnerResourceReader(scope) {
|
|
20866
|
+
const state = owners.get(scope);
|
|
20867
|
+
if (state === void 0) return void 0;
|
|
20868
|
+
const latestEnvironmentId = () => state.liveEnvironmentId?.() ?? [...state.admissions].reverse().flatMap((admission) => admission.phase === "environment" ? [admission.environmentId] : [])[0];
|
|
20869
|
+
return {
|
|
20870
|
+
get describe() {
|
|
20871
|
+
return `environment ${latestEnvironmentId() ?? "<not yet admitted>"}`;
|
|
20872
|
+
},
|
|
20873
|
+
async read(path) {
|
|
20874
|
+
const provider = state.provider;
|
|
20875
|
+
if (provider === void 0) return {
|
|
20876
|
+
ok: false,
|
|
20877
|
+
reason: "the manager’s environment provider is not bound yet"
|
|
20878
|
+
};
|
|
20879
|
+
const environmentId = latestEnvironmentId();
|
|
20880
|
+
if (environmentId === void 0) return {
|
|
20881
|
+
ok: false,
|
|
20882
|
+
reason: "the manager’s environment has not been admitted yet"
|
|
20883
|
+
};
|
|
20884
|
+
if (!provider.get) return {
|
|
20885
|
+
ok: false,
|
|
20886
|
+
reason: `provider ${provider.name} cannot reconstruct the manager’s environment`
|
|
20887
|
+
};
|
|
20888
|
+
let environment;
|
|
20889
|
+
try {
|
|
20890
|
+
environment = await provider.get(environmentId);
|
|
20891
|
+
} catch (error) {
|
|
20892
|
+
return {
|
|
20893
|
+
ok: false,
|
|
20894
|
+
reason: `environment ${environmentId}: ${error instanceof Error ? error.message : String(error)}`
|
|
20895
|
+
};
|
|
20896
|
+
}
|
|
20897
|
+
if (environment === null) return {
|
|
20898
|
+
ok: false,
|
|
20899
|
+
reason: `environment ${environmentId} is gone`
|
|
20900
|
+
};
|
|
20901
|
+
if (environment.id !== environmentId || environment.provider !== provider.name) return {
|
|
20902
|
+
ok: false,
|
|
20903
|
+
reason: `provider returned another environment for ${environmentId}`
|
|
20904
|
+
};
|
|
20905
|
+
if (typeof environment.read !== "function") return {
|
|
20906
|
+
ok: false,
|
|
20907
|
+
reason: `environment ${environmentId} does not expose read; its provider cannot serve a resource by path`
|
|
20908
|
+
};
|
|
20909
|
+
const read = environment.read.bind(environment);
|
|
20910
|
+
return environmentReader({
|
|
20911
|
+
id: environment.id,
|
|
20912
|
+
read
|
|
20913
|
+
}).read(path);
|
|
20914
|
+
}
|
|
20915
|
+
};
|
|
20916
|
+
}
|
|
20487
20917
|
/** The existing scope settlement barrier releases the owner's environment after all its turns. */
|
|
20488
20918
|
async function releaseScopeRetainedOwnerEnvironment(scope) {
|
|
20489
20919
|
const state = owners.get(scope);
|
|
@@ -23369,7 +23799,9 @@ function unreportedSpend(total, prior) {
|
|
|
23369
23799
|
* so retrying re-runs a decision rather than recovering from an accident. They fail immediately.
|
|
23370
23800
|
* - TRANSIENT failures are everything foreign: a harness process that exited without a reason, a
|
|
23371
23801
|
* stream that cut mid-turn, a 5xx, a socket reset. Those are accidents, and they are exactly
|
|
23372
|
-
* what a retry exists for.
|
|
23802
|
+
* what a retry exists for. A harness turn that RETURNED a failed outcome is the same accident
|
|
23803
|
+
* reported as a value rather than thrown, so the drive raises {@link HarnessTurnFailedError}
|
|
23804
|
+
* and it is classified here like any other failure.
|
|
23373
23805
|
*
|
|
23374
23806
|
* The one loop the budget alone cannot bound is a driver that dies INSTANTLY and repeatedly — a
|
|
23375
23807
|
* dead-on-arrival credential, a harness that refuses to start. Spending nothing, it would retry
|
|
@@ -23431,6 +23863,28 @@ const DETERMINISTIC_BRIDGE_CODES = /* @__PURE__ */ new Set([
|
|
|
23431
23863
|
"capability_denied"
|
|
23432
23864
|
]);
|
|
23433
23865
|
/**
|
|
23866
|
+
* A driver's harness turn ran to its terminal result, and that result reported failure.
|
|
23867
|
+
*
|
|
23868
|
+
* A provider executor reports a failed turn as a returned outcome, not a thrown error, so without
|
|
23869
|
+
* this error a failed turn reads as a completed drive and never reaches the retry decision.
|
|
23870
|
+
* Measured 2026-09-17 on a Discovery director placed on tangle-sandbox with the opencode harness:
|
|
23871
|
+
* a 53-minute turn ended `status code 524`, and another ended `Invalid API key` after a platform
|
|
23872
|
+
* key expired mid-turn. A new turn succeeds in both cases.
|
|
23873
|
+
*
|
|
23874
|
+
* `errorCode` is the provider's machine code when it reported one. The classifier reads the code
|
|
23875
|
+
* and never the text, because the text is written for people and changes without notice.
|
|
23876
|
+
*/
|
|
23877
|
+
var HarnessTurnFailedError = class extends Error {
|
|
23878
|
+
runtime;
|
|
23879
|
+
errorCode;
|
|
23880
|
+
constructor(runtime, failure) {
|
|
23881
|
+
super(`${runtime} harness turn ended with a failed outcome` + (failure.errorCode === void 0 ? "" : ` (${failure.errorCode})`) + `: ${failure.error}`);
|
|
23882
|
+
this.name = "HarnessTurnFailedError";
|
|
23883
|
+
this.runtime = runtime;
|
|
23884
|
+
if (failure.errorCode !== void 0) this.errorCode = failure.errorCode;
|
|
23885
|
+
}
|
|
23886
|
+
};
|
|
23887
|
+
/**
|
|
23434
23888
|
* Classify one driver failure. Runtime's own typed refusals are decisions and stay terminal;
|
|
23435
23889
|
* anything foreign is an accident and is retryable. A `BackendTransportError` is split by status
|
|
23436
23890
|
* because the taxonomy already promises consumers may branch on it: a 5xx/429/408 is the upstream
|
|
@@ -23440,6 +23894,7 @@ const DETERMINISTIC_BRIDGE_CODES = /* @__PURE__ */ new Set([
|
|
|
23440
23894
|
function classifyDriverFailure(error, signal) {
|
|
23441
23895
|
if (signal?.aborted) return "terminal";
|
|
23442
23896
|
if (error instanceof Error && errorProperty(error, "name") === "AbortError") return "terminal";
|
|
23897
|
+
if (error instanceof HarnessTurnFailedError) return error.errorCode !== void 0 && DETERMINISTIC_BRIDGE_CODES.has(error.errorCode) ? "terminal" : "transient";
|
|
23443
23898
|
if (error instanceof BackendTransportError) {
|
|
23444
23899
|
if (error.upstreamCode !== void 0 && DETERMINISTIC_BRIDGE_CODES.has(error.upstreamCode)) return "terminal";
|
|
23445
23900
|
const status = error.status;
|
|
@@ -23449,7 +23904,28 @@ function classifyDriverFailure(error, signal) {
|
|
|
23449
23904
|
}
|
|
23450
23905
|
if (error instanceof ValidationError$1 || error instanceof ConfigError || error instanceof RuntimeRunStateError) return "terminal";
|
|
23451
23906
|
if (error instanceof AgentEvalError$1) return "terminal";
|
|
23452
|
-
return "transient";
|
|
23907
|
+
return foreignHttpStatusVerdict(error) ?? "transient";
|
|
23908
|
+
}
|
|
23909
|
+
/**
|
|
23910
|
+
* A foreign error that still carries an HTTP status is a refusal we can read.
|
|
23911
|
+
*
|
|
23912
|
+
* A provider SDK throws its own error classes, so an environment `create` that the platform
|
|
23913
|
+
* refused arrives here as neither a `BackendTransportError` nor an `AgentEvalError`, and the
|
|
23914
|
+
* default sends it to `transient`. Measured 2026-09-16: a Tangle Sandbox create refused
|
|
23915
|
+
* `HTTP 400 {"code":"CONFIG_ERROR"}` for a key whose budget was fully reserved retried 14-22
|
|
23916
|
+
* times per node while the run showed a durable intent and no other event, so eleven of twelve
|
|
23917
|
+
* roots sat for 25 minutes with nothing to diagnose. The status was on the error the whole time.
|
|
23918
|
+
*
|
|
23919
|
+
* Reading it applies the same rule the transport branch already promises consumers: 408, 429 and
|
|
23920
|
+
* 5xx are the upstream having a bad moment, and any other 4xx is a request that will fail
|
|
23921
|
+
* identically forever. Anything without a plain numeric status keeps the historical default.
|
|
23922
|
+
*/
|
|
23923
|
+
function foreignHttpStatusVerdict(error) {
|
|
23924
|
+
if (!(error instanceof Error)) return void 0;
|
|
23925
|
+
const status = errorHttpStatus(error);
|
|
23926
|
+
if (status === void 0 || status < 400) return void 0;
|
|
23927
|
+
if (status === 408 || status === 429 || status >= 500) return "transient";
|
|
23928
|
+
return "terminal";
|
|
23453
23929
|
}
|
|
23454
23930
|
/** The budget's own verdict on whether another attempt may run at all. */
|
|
23455
23931
|
function budgetStop(budget, atMs) {
|
|
@@ -24570,6 +25046,6 @@ function isNonEmptySpend(s) {
|
|
|
24570
25046
|
return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0 || s.tokensKnown === false || s.usdKnown === false || Object.values(s.resources ?? {}).some((resource) => resource.amount > 0 || !resource.known);
|
|
24571
25047
|
}
|
|
24572
25048
|
//#endregion
|
|
24573
|
-
export {
|
|
25049
|
+
export { startRetainedInteractiveRun as $, decodeHarnessUsage as $i, contentAddress as $n, attestRuntimeOwnedPendingExecutor as $r, isPeerMailEnvelope as $t, validateWaitSpec as A, mapSandboxToolEvent as Ai, providerAsSandboxClient as An, localHarnessExecutable as Ar, workerTraceAnalysisStore as At, SPAWN_RESOURCE_PATH_MAX_BYTES as B, isAbortError$2 as Bi, linkAbort as Bn, promptModelProfileMaterialization as Br, workerTraceEnv as Bt, restoreScopeOwnerAcceptedExecution as C, assertSandboxServedModel as Ci, routerBrain as Cn, runWorktreeHarness as Cr, materializeTreeView as Ct, isWaitOutcome as D, extractLlmCallEvent as Di, DEFAULT_SANDBOX_IDLE_TIMEOUT_SECONDS as Dn, DEFAULT_LOCAL_HARNESS as Dr, captureWorkerTraceEvidence as Dt, createWaitProbes as E, createSandboxUsageLedger as Ei, observedModelMatchesDeclared as En, removeWorktree as Er, WORKER_TOOL_TRACE_SCHEMA_VERSION as Et, prepareScopeRetainedOwnerTask as F, addSpend as Fi, sandboxSessionTraceSource as Fn, controlProfileMaterialization as Fr, newUsageTotals as Ft, readWorkerInteractiveAdmissions as G, throwAbort as Gi, startRetainedRunInEnvironment as Gn, sandboxActProfileMaterialization as Gr, createActivityLog as Gt, hostDirectoryReader as H, randomSuffix as Hi, reconnectRetainedRun as Hn, promptResourceProfileMaterialization as Hr, workerTraceSeamKey as Ht, scopeRetainedOwnerContext as I, chargedTokens as Ii, registerRetainedExecutorPreparation as In, defineProfileMaterializationContract as Ir, spendFromUsageEvents as It, readWorkerInteractiveBinding as J, withTimeout as Ji, defaultRedactorIdentityMaterial as Jn, worktreeCliProfileMaterialization as Jr, AUTHORITY_MARKERS as Jt, workerInteractiveAdmissionFile as K, throwIfAborted as Ki, retainedCreateMaterial as Kn, unsupportedProfileDimensions as Kr, readWorkerProgress as Kt, scopeRetainedOwnerPriorSpend as L, cloneSpend as Li, retainedExecutorSeamKey as Ln, fullProfileMaterialization as Lr, spendFromUsageTotals as Lt, bindScopeRetainedOwnerEnvironmentId as M, sandboxEventServedBackend as Mi, sandboxClientAsProvider as Mn, CodexExecutionDiagnosticError as Mr, assertValidBudget as Mt, bindScopeRetainedOwnerProvider as N, sandboxProgressEvents as Ni, createPushTraceSource as Nn, AGENT_PROFILE_MATERIALIZATION_AXES as Nr, createBudgetPool as Nt, pollFor as O, isSandboxTerminalEvent as Oi, createAgentEnvironmentProviderRegistry as On, LOCAL_HARNESSES as Or, isTraceAnalysisStore as Ot, consumeScopeRetainedOwnerResult as P, sumSandboxUsage as Pi, decodeToolPart as Pn, assertProfileMaterialization as Pr, meterUsageEvent as Pt, recoverRetainedInteractiveRun as Q, withBudgetResources as Qi, persistHarnessTranscript as Qn, writeAllBytes as Qr, createPeerMailbox as Qt, scopeRetainedOwnerResourceReader as R, deleteBoxSafe as Ri, RunCancellationReason as Rn, profileMaterializationAxes$1 as Rr, WORKER_TRACE_PROPAGATION as Rt, recordScopeOwnerMaterialization as S, readSandboxOutcome as Si, readPromptOptions as Sn, runSettledCommand as Sr, loadSpawnForest as St, settledToIteration as T, createSandboxToolPartState as Ti, canonicalObservedModelParts as Tn, createWorktree as Tr, replaySpawnTree as Tt, resolveSpawnResourcePaths as U, sleep as Ui, recoverRetainedRun as Un, renderProfileMaterializationIssues as Ur, taskToPrompt as Ut, environmentReader as V, promptCacheTokenClasses as Vi, runAbortable as Vn, promptOnlyProfileMaterialization as Vr, workerTraceHeaders as Vt, interactiveAdmissionSeamKey as W, stringifySafe as Wi, startRetainedRun as Wn, renderUnsupported as Wr, DEFAULT_STALL_AFTER_MS as Wt, workerInteractiveBindingsDir as X, addResourceSpend as Xi, captureHarnessTranscript as Xn, prepareJsonlAppend as Xr, PEER_MAIL_WIRE_KEY as Xt, workerInteractiveBindingFile as Y, zeroSpend as Yi, resolveRedactor as Yn, isNoEntError as Yr, DEFAULT_PEER_MAIL_LIMITS as Yt, reconnectRetainedInteractiveRun as Z, resourceTelemetry as Zi, harnessTranscriptArtifact as Zn, readCommittedJsonLines as Zr, claimsAuthority as Zt, beginScopeOwnerAttempt as _, executableAgentProfileSnapshot as _i, runAgentRounds as _n, createRuntimeEventCollector as _r, FileResultBlobStore as _t, pickBestDelivered as a, promptFromAgentTurnInput as aa, knownMaterializationReceipt as ai, bridgeAdmissionRefusal as an, buildLoopOtelSpans as ar, rollingDispatch as at, meterRuntimeOwnedAccounting as b, detachedSnapshot as bi, acquireSandbox as bn, sanitizeKnowledgeReadinessReport as br, InMemorySpawnJournal as bt, DriverAttemptsExhaustedError as c, recordRuntimeOwnedDriveHarnessProviderEvidence as ci, createCodexRolloutStoreReader as cn, createOpenInferenceFileExporter as cr, cliInPlaceExecutor as ct, defaultUnmetContractSteer as d, runtimeOwnedExecutorMaterialization as di, bridgeRuntimeAttachmentsKey as dn, flatOtelSpan as dr, createExecutorRegistry as dt, abortError$2 as ea, attestRuntimeOwnedScopeOwner as ei, peerMailTools as en, createPropagatingTraceEmitter as er, destroyInteractiveEnvironment as et, runDriverWithRetry as f, runtimeOwnedExecutorProviderEvidence as fi, bridgeStopSignalKey as fn, generateSpanId as fr, snapshotExecutorConfig as ft, withDriverExecutor as g, unknownMaterializationReceipt as gi, isTerminalDecision as gn, toOtelAttributes as gr, createInPlaceCliExecutor as gt, isDriverSpec as h, unknownExecutionBindingReceipt as hi, defaultSelectWinner as hn, padTraceId as hr, createSteerableSandboxSession as ht, collectDelivered as i, teardownExecutor as ia, knownExecutionBindingReceipt as ii, executorFailure as in, INTELLIGENCE_WIRE_VERSION as ir, queueOf as it, waitUntil as j, notifySandboxEventObserver as ji, resolveAgentEnvironmentProvider as jn, parseCodexTokenUsage as jr, BudgetReconcileFault as jt, timerAt as k, mapSandboxEvent as ki, providerAsExecutor as kn, harnessSupportsReasoningEffort as kr, parseWorkerToolTraceArtifact as kt, HarnessTurnFailedError as l, runtimeOwnedDriveHarnessProviderEvidence as li, harnessUsageIsEmpty as ln, createOtelExporter as lr, cliWorktreeExecutor as lt, driverExecutorFactory as m, runtimeOwnedScopeOwnerRuntime as mi, createSandboxForSpec as mn, padSpanId as mr, DEFAULT_SANDBOX_STEERING_MAX_TURNS as mt, createSupervisor as n, DEFAULT_SUCCESSFUL_SHUTDOWN_MS as na, finalizeRuntimeOwnedPendingExecutor as ni, isLiveNodeStatus as nn, readTraceContextFromEnv as nr, effectiveConcurrency as nt, runFinalizer as o, promptOptionsFromAgentTurnInput as oa, newExecutionAttemptId as oi, bridgeModelRouteRefusal as on, buildLoopSpanNodes as or, bindReusableExecutorExecutionId as ot, driverChild as p, runtimeOwnedPendingExecutorMaterialization as pi, TERMINAL_DECISIONS as pn, loopEventToOtelSpan as pr, createWorktreeCliExecutor as pt, attachWorker as q, unmeteredSpend as qi, defaultRedactor as qn, validateProfileMaterialization as qr, createInbox as qt, bestDelivered as r, armDeadlineTimer as ra, inheritRuntimeOwnedExecutorAttestation as ri, isTerminalNodeStatus as rn, traceContextToEnv as rr, freeSlots as rt, runTree as s, providerMessageText as sa, providerAttemptEvidence as si, addHarnessUsage as sn, buildRuntimeEventOtelSpans as sr, captureReusableExecutorConfig as st, createRootHandle as t, awaitAbortable$1 as ta, authoredProfileDigest as ti, peerMailVerbNames as tn, mergeTraceEnv as tr, errMessage as tt, classifyDriverFailure as u, runtimeOwnedExecutorExecutionBinding as ui, readCodexRolloutSession as un, exportEvalRuns as ur, createExecutor as ut, createScope as v, executableAgentSpecSnapshot as vi, createSandboxLineage as vn, createRuntimeStreamEventCollector as vr, FileSpawnJournal as vt, scopeOwnerExecutorNodeContext as w, canonicalStreamEventFromSandboxEvent as wi, runBrainLoop as wn, captureWorktreeDiff as wr, pendingWaits as wt, meterRuntimeOwnedProviderAttempt as x, projectSandboxOutcome as xi, assertBoxlessPromptOptions as xn, sanitizeRuntimeStreamEvent as xr, insideCursorNamespace as xt, deriveNodeExecutionIdentity as y, detachedFrozen as yi, probeSandboxCapabilities as yn, sanitizeAgentRuntimeEvent as yr, InMemoryResultBlobStore as yt, scopeRetainedOwnerResult as z, hasCompleteCacheBreakdown as zi, abortError as zn, promptControlProfileMaterialization as zr, readWorkerTraceContext as zt };
|
|
24574
25050
|
|
|
24575
|
-
//# sourceMappingURL=supervisor-
|
|
25051
|
+
//# sourceMappingURL=supervisor-De0lytFU.js.map
|