@tangle-network/agent-runtime 0.306.2 → 0.308.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-wZ-8vOjV.d.ts → activation-5Usfsfzr.d.ts} +3 -3
- package/dist/{activation-kaJzXM7Y.js → activation-DbnacUhI.js} +2 -2
- package/dist/{activation-kaJzXM7Y.js.map → activation-DbnacUhI.js.map} +1 -1
- package/dist/agent.d.ts +2 -2
- package/dist/agent.js +2 -2
- package/dist/{coordination-driver-Y2gf8pBT.js → coordination-driver-BYIjecHz.js} +2 -2
- package/dist/{coordination-driver-Y2gf8pBT.js.map → coordination-driver-BYIjecHz.js.map} +1 -1
- package/dist/{delegate-B7bwEFQl.js → delegate-KCK7WdZv.js} +2 -2
- package/dist/{delegate-B7bwEFQl.js.map → delegate-KCK7WdZv.js.map} +1 -1
- package/dist/durable.d.ts +2 -2
- package/dist/durable.js +55 -4
- package/dist/durable.js.map +1 -1
- package/dist/{graph-VUwNFYff.js → graph-BUu0KQVW.js} +5 -4
- package/dist/graph-BUu0KQVW.js.map +1 -0
- package/dist/{improvement-cycle-DuIGusyA.js → improvement-cycle-BylwWCcl.js} +7 -3
- package/dist/{improvement-cycle-DuIGusyA.js.map → improvement-cycle-BylwWCcl.js.map} +1 -1
- package/dist/{index-DflDZq_I.d.ts → index-EibbsKsH.d.ts} +72 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +7 -7
- package/dist/intelligence.d.ts +3 -3
- package/dist/intelligence.js +3 -3
- package/dist/kernel.d.ts +4 -4
- package/dist/kernel.js +8 -8
- package/dist/{loop-runner-bin-Dp1V3P2W.js → loop-runner-bin-DcF6ZEWi.js} +3 -3
- package/dist/{loop-runner-bin-Dp1V3P2W.js.map → loop-runner-bin-DcF6ZEWi.js.map} +1 -1
- package/dist/{loop-runner-bin-DZfRUVWy.d.ts → loop-runner-bin-jST1hm-z.d.ts} +3 -3
- 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-YO30D2s3.d.ts → openai-tools-BuFqhp5i.d.ts} +2 -2
- package/dist/profiles.d.ts +1 -1
- package/dist/{provision-supervisor-CfK8fla-.js → provision-supervisor-Cvp4Nm-y.js} +3 -3
- package/dist/{provision-supervisor-CfK8fla-.js.map → provision-supervisor-Cvp4Nm-y.js.map} +1 -1
- package/dist/{runtime-D452tieK.js → runtime-Y46Lwt_d.js} +8 -8
- package/dist/{runtime-D452tieK.js.map → runtime-Y46Lwt_d.js.map} +1 -1
- package/dist/{search-executor-D3AT8_hb.js → search-executor-BPpfxu9c.js} +2 -2
- package/dist/{search-executor-D3AT8_hb.js.map → search-executor-BPpfxu9c.js.map} +1 -1
- package/dist/{server-D1Tfxo-v.js → server-DIrQYUnn.js} +3 -3
- package/dist/{server-D1Tfxo-v.js.map → server-DIrQYUnn.js.map} +1 -1
- package/dist/{stream-agent-turn-B1giFgOg.d.ts → stream-agent-turn-op3m9IEY.d.ts} +2 -2
- package/dist/{substrate-L_sC7jCD.d.ts → substrate-B1VzcyoB.d.ts} +2 -2
- package/dist/{supervise-CtewGpxh.js → supervise-CmeN8ipv.js} +164 -190
- package/dist/supervise-CmeN8ipv.js.map +1 -0
- package/dist/{supervisor-DYKPbcZa.js → supervisor-CSMb8HBU.js} +1796 -1038
- package/dist/supervisor-CSMb8HBU.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-DeuEkBhI.d.ts → types-B2IOlN8R.d.ts} +152 -67
- package/package.json +3 -3
- package/skills/profile-authoring/SKILL.md +6 -0
- package/dist/graph-VUwNFYff.js.map +0 -1
- package/dist/supervise-CtewGpxh.js.map +0 -1
- package/dist/supervisor-DYKPbcZa.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as RetainedInteractiveAdmissionError, d as RetainedRunDispatchBindingError, f as RuntimeRunStateError, i as ConfigError, l as RetainedInteractiveBindingError, m as ValidationError$1, r as BackendTransportError, t as AgentEvalError$1, u as RetainedRunAdmissionError } from "./errors-DodWX-cb.js";
|
|
2
|
-
import { $ as randomUuid, B as addTokenUsage, G as errorMessage, H as cloneSpend, K as fnv1a32Hex, N as deepFreezeCandidate, O as verifyWorkspaceSnapshotArtifacts, Q as randomSuffix, U as cloneTokenUsage, V as chargedTokens, W as deleteBoxSafe, X as mapWithConcurrency, Y as isAsyncIterable$1, Z as promptCacheTokenClasses, _t as armDeadlineTimer, a as verifyAgentCandidateWorkspaceArtifacts, at as unmeteredSpend, bt as singleFlightTeardown, ct as zeroTokenUsage, dt as resourceTelemetry, et as roundBoxMinutes, ft as withBudgetResources, gt as ROOT_DEADLINE_REASON, i as readAgentCandidateWorkspaceArtifactFiles, it as throwIfAborted, k as verifiedArtifactChunks, lt as addResourceSpend, mt as CHILD_DEADLINE_REASON, nt as stringifySafe, o as verifyAgentCandidateWorkspaceTree, ot as withTimeout, pt as ACCEPTED_RESULT_GRACE_MS, q as hasCompleteCacheBreakdown, rt as throwAbort, st as zeroSpend, tt as sleep, u as verifyAgentCandidateWorkspaceArchive, ut as assertResources, vt as boundedChildDeadlineAt, xt as teardownExecutor, yt as isDeadlineAbortReason, z as addSpend } from "./workspace-streams-DaQceg2p.js";
|
|
2
|
+
import { $ as randomUuid, B as addTokenUsage, G as errorMessage, H as cloneSpend, K as fnv1a32Hex, N as deepFreezeCandidate, O as verifyWorkspaceSnapshotArtifacts, Q as randomSuffix, St as teardownSurfaces, U as cloneTokenUsage, V as chargedTokens, W as deleteBoxSafe, X as mapWithConcurrency, Y as isAsyncIterable$1, Z as promptCacheTokenClasses, _t as armDeadlineTimer, a as verifyAgentCandidateWorkspaceArtifacts, at as unmeteredSpend, bt as singleFlightTeardown, ct as zeroTokenUsage, dt as resourceTelemetry, et as roundBoxMinutes, ft as withBudgetResources, gt as ROOT_DEADLINE_REASON, i as readAgentCandidateWorkspaceArtifactFiles, it as throwIfAborted, k as verifiedArtifactChunks, lt as addResourceSpend, mt as CHILD_DEADLINE_REASON, nt as stringifySafe, o as verifyAgentCandidateWorkspaceTree, ot as withTimeout, pt as ACCEPTED_RESULT_GRACE_MS, q as hasCompleteCacheBreakdown, rt as throwAbort, st as zeroSpend, tt as sleep, u as verifyAgentCandidateWorkspaceArchive, ut as assertResources, vt as boundedChildDeadlineAt, xt as teardownExecutor, yt as isDeadlineAbortReason, z as addSpend } from "./workspace-streams-DaQceg2p.js";
|
|
3
3
|
import { runTaskQueue } from "./task-queue.js";
|
|
4
4
|
import { _ as agentHarness, a as concreteProfileModel, c as profileModelExecutionSettings, d as assertRouterAutoDeclaration, f as assertRouterAutoResponse, g as observedModelMatchesDeclared, h as observedModelHasSnapshot, l as profileProviderModel, m as mergeObservedModelIdentity, o as enforceTokenLimits, s as profileBridgeWireModel, t as assertExecutableAgentProfile, u as resolveRouterRetryPolicy } from "./model-policy-DydyWTQg.js";
|
|
5
5
|
import { n as assertNoSymlinkDescendant, r as publishExclusiveDurableFile } from "./durable-file-DWQA4ooo.js";
|
|
@@ -380,6 +380,29 @@ const decodeCodexTurnUsage = (event) => {
|
|
|
380
380
|
...record.cacheWriteInputTokens === void 0 ? {} : { cacheWriteInput: record.cacheWriteInputTokens }
|
|
381
381
|
};
|
|
382
382
|
};
|
|
383
|
+
/** Claude's adapter forwards one delta per response snapshot, keyed and deduplicated there. */
|
|
384
|
+
const decodeClaudeAssistantUsage = (event) => {
|
|
385
|
+
if (String(event.type ?? "") !== "raw") return void 0;
|
|
386
|
+
const envelope = plainRecord$2(event.data);
|
|
387
|
+
const data = envelope?.type === "raw" && plainRecord$2(envelope.event) !== void 0 ? plainRecord$2(envelope.event) : envelope;
|
|
388
|
+
if (data?.type !== "assistant.usage") return void 0;
|
|
389
|
+
if (typeof data.message_id !== "string" || data.message_id.length === 0) throw new ValidationError$1("claude assistant.usage: missing response identity");
|
|
390
|
+
const usage = plainRecord$2(data.usage);
|
|
391
|
+
if (!usage) throw new ValidationError$1("claude assistant.usage: usage must be an object");
|
|
392
|
+
const fresh = naturalNumber$1(usage.input_tokens, "input_tokens", "claude assistant.usage");
|
|
393
|
+
const cachedInput = naturalNumber$1(usage.cache_read_input_tokens, "cache_read_input_tokens", "claude assistant.usage");
|
|
394
|
+
const cacheWriteInput = naturalNumber$1(usage.cache_creation_input_tokens, "cache_creation_input_tokens", "claude assistant.usage");
|
|
395
|
+
const output = naturalNumber$1(usage.output_tokens, "output_tokens", "claude assistant.usage");
|
|
396
|
+
const input = fresh + cachedInput + cacheWriteInput;
|
|
397
|
+
if (!Number.isSafeInteger(input)) throw new ValidationError$1("claude assistant.usage: total input tokens exceed a safe integer");
|
|
398
|
+
return {
|
|
399
|
+
harness: "claude-code",
|
|
400
|
+
input,
|
|
401
|
+
output,
|
|
402
|
+
cachedInput,
|
|
403
|
+
cacheWriteInput
|
|
404
|
+
};
|
|
405
|
+
};
|
|
383
406
|
/**
|
|
384
407
|
* The harness → usage-decoder registry. Add a harness by adding one entry.
|
|
385
408
|
*
|
|
@@ -387,7 +410,10 @@ const decodeCodexTurnUsage = (event) => {
|
|
|
387
410
|
* can produce does not compile. A harness absent from this registry reports usage through the
|
|
388
411
|
* canonical events or not at all.
|
|
389
412
|
*/
|
|
390
|
-
const harnessUsageDecoders = {
|
|
413
|
+
const harnessUsageDecoders = {
|
|
414
|
+
codex: decodeCodexTurnUsage,
|
|
415
|
+
"claude-code": decodeClaudeAssistantUsage
|
|
416
|
+
};
|
|
391
417
|
/**
|
|
392
418
|
* Decode a sandbox event with one harness's adapter, or `undefined` when the event carries no
|
|
393
419
|
* harness-native usage.
|
|
@@ -6635,6 +6661,30 @@ async function captureProviderCheckpointWorkspace(port, context, marker) {
|
|
|
6635
6661
|
clearDeadline();
|
|
6636
6662
|
}
|
|
6637
6663
|
}
|
|
6664
|
+
/** Verify a running workspace copy before treating it as retained evidence. */
|
|
6665
|
+
async function captureProviderLiveWorkspace(port, context) {
|
|
6666
|
+
assertProviderWorkspaceRetentionPort(port, "provider workspace retention");
|
|
6667
|
+
if (port.captureLive === void 0) throw new ValidationError$1("provider workspace retention: the port has no captureLive");
|
|
6668
|
+
const timeoutMs = port.liveTimeoutMs ?? port.timeoutMs;
|
|
6669
|
+
const controller = new AbortController();
|
|
6670
|
+
const clearDeadline = armDeadlineTimer(timeoutMs, () => controller.abort(/* @__PURE__ */ new Error(`live workspace capture timed out after ${timeoutMs}ms`)), true);
|
|
6671
|
+
try {
|
|
6672
|
+
return await runAbortable(() => withCaptureSlot(checkpointSlots(port), controller.signal, async () => {
|
|
6673
|
+
const tree = detachedSnapshot(await port.captureLive({
|
|
6674
|
+
...context,
|
|
6675
|
+
signal: controller.signal
|
|
6676
|
+
}), "live workspace capture tree");
|
|
6677
|
+
if (tree?.kind !== "agent-candidate-workspace-tree") throw new Error("live workspace capture returned no workspace tree");
|
|
6678
|
+
if ((await verifyAgentCandidateWorkspaceTree(tree, port.artifacts, {
|
|
6679
|
+
...port.limits === void 0 ? {} : { limits: port.limits },
|
|
6680
|
+
signal: controller.signal
|
|
6681
|
+
})).files.length !== tree.files) throw new Error("live workspace capture tree misstates its file count");
|
|
6682
|
+
return tree;
|
|
6683
|
+
}), controller.signal, `live workspace capture timed out after ${timeoutMs}ms`);
|
|
6684
|
+
} finally {
|
|
6685
|
+
clearDeadline();
|
|
6686
|
+
}
|
|
6687
|
+
}
|
|
6638
6688
|
/** Validate the executable retention port at the create boundary. */
|
|
6639
6689
|
function assertProviderWorkspaceRetentionPort(port, context) {
|
|
6640
6690
|
if (!Number.isSafeInteger(port.timeoutMs) || port.timeoutMs <= 0) throw new ValidationError$1(`${context}: workspaceRetention.timeoutMs must be a positive safe integer`);
|
|
@@ -6643,7 +6693,14 @@ function assertProviderWorkspaceRetentionPort(port, context) {
|
|
|
6643
6693
|
if (typeof port.capture !== "function") throw new ValidationError$1(`${context}: workspaceRetention.capture is required`);
|
|
6644
6694
|
if (port.captureNative !== void 0 && typeof port.captureNative !== "function") throw new ValidationError$1(`${context}: workspaceRetention.captureNative must be a function`);
|
|
6645
6695
|
if (port.captureCheckpoint !== void 0 && typeof port.captureCheckpoint !== "function") throw new ValidationError$1(`${context}: workspaceRetention.captureCheckpoint must be a function`);
|
|
6646
|
-
|
|
6696
|
+
if (port.captureLive !== void 0 && typeof port.captureLive !== "function") throw new ValidationError$1(`${context}: workspaceRetention.captureLive must be a function`);
|
|
6697
|
+
if (port.liveIntervalMs !== void 0 && port.captureLive === void 0) throw new ValidationError$1(`${context}: workspaceRetention.liveIntervalMs requires captureLive`);
|
|
6698
|
+
for (const key of [
|
|
6699
|
+
"nativeIntervalMs",
|
|
6700
|
+
"nativeTimeoutMs",
|
|
6701
|
+
"liveIntervalMs",
|
|
6702
|
+
"liveTimeoutMs"
|
|
6703
|
+
]) {
|
|
6647
6704
|
const value = port[key];
|
|
6648
6705
|
if (value !== void 0 && (!Number.isSafeInteger(value) || value <= 0)) throw new ValidationError$1(`${context}: workspaceRetention.${key} must be a positive safe integer`);
|
|
6649
6706
|
}
|
|
@@ -9694,6 +9751,9 @@ var SpawnEventIndex = class {
|
|
|
9694
9751
|
if (node?.materialized) throw new Error(`spawn journal corrupted: duplicate materialization receipt for node '${event.id}' in tree '${this.root}'`);
|
|
9695
9752
|
if (!node?.spawned) throw new Error(`spawn journal corrupted: materialization for node '${event.id}' precedes its spawn in tree '${this.root}'`);
|
|
9696
9753
|
}
|
|
9754
|
+
if (event.kind === "seat-materialized") {
|
|
9755
|
+
if (!node?.materialized || node.authoredProfileDigest === void 0 || event.receipt.status !== "known" || event.receipt.authoredProfileDigest !== node.authoredProfileDigest || !Number.isSafeInteger(event.segmentIndex) || event.segmentIndex <= node.latestSeatMaterialization) throw new Error(`spawn journal corrupted: invalid seat materialization for node '${event.id}' in tree '${this.root}'`);
|
|
9756
|
+
}
|
|
9697
9757
|
if (event.kind === "execution-bound") {
|
|
9698
9758
|
if (node?.bindings.has(event.binding.attemptId)) throw new Error(`spawn journal corrupted: duplicate execution binding for node '${event.id}' attempt '${event.binding.attemptId}' in tree '${this.root}'`);
|
|
9699
9759
|
if (!node?.materialized) throw new Error(`spawn journal corrupted: execution binding for node '${event.id}' precedes materialization in tree '${this.root}'`);
|
|
@@ -9703,41 +9763,53 @@ var SpawnEventIndex = class {
|
|
|
9703
9763
|
/** Called only after validation and, for the file writer, a successful durable append. */
|
|
9704
9764
|
add(event) {
|
|
9705
9765
|
if (!outsideCursorNamespace(event)) this.cursors.add(event.seq);
|
|
9706
|
-
if (event.kind !== "spawned" && event.kind !== "materialized" && event.kind !== "execution-bound" && event.kind !== "execution-input" && event.kind !== "execution-admitted" && event.kind !== "execution-result" && event.kind !== "environment-teardown" && !closesCursorSlot(event)) return;
|
|
9766
|
+
if (event.kind !== "spawned" && event.kind !== "materialized" && event.kind !== "seat-materialized" && event.kind !== "execution-bound" && event.kind !== "execution-input" && event.kind !== "execution-admitted" && event.kind !== "execution-result" && event.kind !== "execution-abandoned" && event.kind !== "environment-teardown" && event.kind !== "environment-terminal" && !closesCursorSlot(event)) return;
|
|
9707
9767
|
let node = this.nodes.get(event.id);
|
|
9708
9768
|
if (!node) {
|
|
9709
9769
|
node = {
|
|
9710
9770
|
spawned: false,
|
|
9711
9771
|
closed: false,
|
|
9712
9772
|
materialized: false,
|
|
9773
|
+
latestSeatMaterialization: 0,
|
|
9713
9774
|
bindings: /* @__PURE__ */ new Set(),
|
|
9714
9775
|
inputs: /* @__PURE__ */ new Set(),
|
|
9715
9776
|
input: false,
|
|
9716
9777
|
result: false,
|
|
9778
|
+
abandoned: false,
|
|
9717
9779
|
admissions: /* @__PURE__ */ new Map(),
|
|
9718
|
-
|
|
9780
|
+
unusableEnvironments: /* @__PURE__ */ new Set()
|
|
9719
9781
|
};
|
|
9720
9782
|
this.nodes.set(event.id, node);
|
|
9721
9783
|
}
|
|
9722
9784
|
if (event.kind === "environment-teardown") {
|
|
9723
|
-
if (event.destroyed) node.
|
|
9785
|
+
if (event.destroyed) node.unusableEnvironments.add(event.environmentId);
|
|
9786
|
+
return;
|
|
9787
|
+
}
|
|
9788
|
+
if (event.kind === "environment-terminal") {
|
|
9789
|
+
node.unusableEnvironments.add(event.environmentId);
|
|
9724
9790
|
return;
|
|
9725
9791
|
}
|
|
9726
9792
|
if (event.kind === "execution-admitted" && event.admission.phase === "environment") node.lastEnvironmentId = event.admission.environmentId;
|
|
9727
9793
|
if (event.kind === "spawned") node.spawned = true;
|
|
9728
|
-
else if (event.kind === "materialized")
|
|
9794
|
+
else if (event.kind === "materialized") {
|
|
9795
|
+
node.materialized = true;
|
|
9796
|
+
node.authoredProfileDigest = event.receipt.authoredProfileDigest;
|
|
9797
|
+
} else if (event.kind === "seat-materialized") node.latestSeatMaterialization = event.segmentIndex;
|
|
9729
9798
|
else if (event.kind === "execution-bound") node.bindings.add(event.binding.attemptId);
|
|
9730
9799
|
else if (event.kind === "execution-input") {
|
|
9731
9800
|
node.inputs.add(event.seq);
|
|
9732
9801
|
node.input = true;
|
|
9802
|
+
node.latestInput = event.seq;
|
|
9733
9803
|
node.result = false;
|
|
9804
|
+
node.abandoned = false;
|
|
9734
9805
|
node.admissions.clear();
|
|
9735
9806
|
} else if (event.kind === "execution-admitted") node.admissions.set(event.admission.phase, event.admission);
|
|
9736
9807
|
else if (event.kind === "execution-result") node.result = true;
|
|
9808
|
+
else if (event.kind === "execution-abandoned") node.abandoned = true;
|
|
9737
9809
|
if (closesCursorSlot(event)) node.closed = true;
|
|
9738
9810
|
}
|
|
9739
9811
|
assertRetained(node, event) {
|
|
9740
|
-
if (event.kind !== "execution-input" && event.kind !== "execution-admitted" && event.kind !== "execution-result") return;
|
|
9812
|
+
if (event.kind !== "execution-input" && event.kind !== "execution-admitted" && event.kind !== "execution-result" && event.kind !== "execution-abandoned") return;
|
|
9741
9813
|
function fail(reason) {
|
|
9742
9814
|
throw new Error(`spawn journal corrupted: retained execution '${event.id}' ${reason}`);
|
|
9743
9815
|
}
|
|
@@ -9748,10 +9820,18 @@ var SpawnEventIndex = class {
|
|
|
9748
9820
|
if (node.inputs.has(event.seq)) fail("has duplicate input sequence");
|
|
9749
9821
|
const environment = node.admissions.get("environment");
|
|
9750
9822
|
const ranIn = environment?.phase === "environment" ? environment.environmentId : node.lastEnvironmentId;
|
|
9751
|
-
const
|
|
9752
|
-
if (node.input && !node.result && !
|
|
9823
|
+
const closed = node.abandoned || ranIn !== void 0 && node.unusableEnvironments.has(ranIn);
|
|
9824
|
+
if (node.input && !node.result && !closed) fail("input replaces an unfinished invocation");
|
|
9825
|
+
return;
|
|
9826
|
+
}
|
|
9827
|
+
if (event.kind === "execution-abandoned") {
|
|
9828
|
+
if (!node.input || node.latestInput !== event.inputSeq) fail("abandons an invocation that is not its latest");
|
|
9829
|
+
if (node.result) fail("abandons an invocation that has a result");
|
|
9830
|
+
if (node.abandoned) fail("abandons an invocation twice");
|
|
9831
|
+
if (!Number.isSafeInteger(event.failures) || event.failures < 1) fail("abandons without a failure count");
|
|
9753
9832
|
return;
|
|
9754
9833
|
}
|
|
9834
|
+
if (node.abandoned) fail(`${event.kind} follows its abandonment`);
|
|
9755
9835
|
if (event.kind === "execution-result") {
|
|
9756
9836
|
if (node.result) fail("has duplicate result");
|
|
9757
9837
|
if (!node.admissions.has("dispatched")) fail("result precedes dispatch");
|
|
@@ -9802,6 +9882,7 @@ const outsideKinds = /* @__PURE__ */ new Set([
|
|
|
9802
9882
|
"execution-input",
|
|
9803
9883
|
"execution-admitted",
|
|
9804
9884
|
"execution-result",
|
|
9885
|
+
"execution-abandoned",
|
|
9805
9886
|
"execution-evidence",
|
|
9806
9887
|
"progress",
|
|
9807
9888
|
"reconciled",
|
|
@@ -9810,6 +9891,7 @@ const outsideKinds = /* @__PURE__ */ new Set([
|
|
|
9810
9891
|
"teardown-pending",
|
|
9811
9892
|
"teardown-confirmed",
|
|
9812
9893
|
"environment-teardown",
|
|
9894
|
+
"environment-terminal",
|
|
9813
9895
|
"workspace-checkpoint-requested",
|
|
9814
9896
|
"workspace-checkpoint",
|
|
9815
9897
|
"workspace-checkpoint-fork-requested",
|
|
@@ -9819,6 +9901,9 @@ const outsideKinds = /* @__PURE__ */ new Set([
|
|
|
9819
9901
|
"workspace-restored",
|
|
9820
9902
|
"workspace-capture",
|
|
9821
9903
|
"trace-unpropagated",
|
|
9904
|
+
"seat-segment",
|
|
9905
|
+
"seat-materialized",
|
|
9906
|
+
"seat-paused",
|
|
9822
9907
|
"paused",
|
|
9823
9908
|
"driver-attempt",
|
|
9824
9909
|
"node-inputs-resolved",
|
|
@@ -9884,11 +9969,12 @@ async function replaySpawnTree(journal, blobs, root) {
|
|
|
9884
9969
|
if (ev.kind === "reconciled") continue;
|
|
9885
9970
|
if (ev.kind === "materialized") continue;
|
|
9886
9971
|
if (ev.kind === "execution-bound") continue;
|
|
9887
|
-
if (ev.kind === "execution-input" || ev.kind === "execution-admitted" || ev.kind === "execution-result" || ev.kind === "execution-evidence") continue;
|
|
9972
|
+
if (ev.kind === "execution-input" || ev.kind === "execution-admitted" || ev.kind === "execution-result" || ev.kind === "execution-abandoned" || ev.kind === "execution-evidence") continue;
|
|
9888
9973
|
if (ev.kind === "edge") continue;
|
|
9889
9974
|
if (ev.kind === "teardown-unconfirmed") continue;
|
|
9890
9975
|
if (ev.kind === "teardown-pending" || ev.kind === "teardown-confirmed") continue;
|
|
9891
9976
|
if (ev.kind === "environment-teardown") continue;
|
|
9977
|
+
if (ev.kind === "environment-terminal") continue;
|
|
9892
9978
|
if (ev.kind === "workspace-checkpoint-requested") continue;
|
|
9893
9979
|
if (ev.kind === "workspace-checkpoint") continue;
|
|
9894
9980
|
if (ev.kind === "workspace-checkpoint-fork-requested") continue;
|
|
@@ -9898,6 +9984,7 @@ async function replaySpawnTree(journal, blobs, root) {
|
|
|
9898
9984
|
if (ev.kind === "workspace-restored") continue;
|
|
9899
9985
|
if (ev.kind === "workspace-capture") continue;
|
|
9900
9986
|
if (ev.kind === "trace-unpropagated") continue;
|
|
9987
|
+
if (ev.kind === "seat-segment" || ev.kind === "seat-materialized" || ev.kind === "seat-paused") continue;
|
|
9901
9988
|
if (ev.kind === "paused") continue;
|
|
9902
9989
|
if (ev.kind === "driver-attempt") continue;
|
|
9903
9990
|
if (ev.kind === "node-inputs-resolved") continue;
|
|
@@ -10049,7 +10136,7 @@ function materializeTreeView(events) {
|
|
|
10049
10136
|
if (Number.isFinite(settledAt)) node.settledAt = settledAt;
|
|
10050
10137
|
}
|
|
10051
10138
|
for (const ev of events) {
|
|
10052
|
-
if (ev.kind !== "materialized") continue;
|
|
10139
|
+
if (ev.kind !== "materialized" && ev.kind !== "seat-materialized") continue;
|
|
10053
10140
|
const node = requireNode(nodes, ev.id);
|
|
10054
10141
|
node.materialization = ev.receipt;
|
|
10055
10142
|
node.runtime = ev.receipt.runtime;
|
|
@@ -10141,6 +10228,87 @@ function traceEvidenceFor(event) {
|
|
|
10141
10228
|
};
|
|
10142
10229
|
}
|
|
10143
10230
|
//#endregion
|
|
10231
|
+
//#region src/runtime/supervise/upstream-unavailable.ts
|
|
10232
|
+
/**
|
|
10233
|
+
* An upstream that cannot serve NOW: how Runtime recognizes one, and how long an agent waits for it.
|
|
10234
|
+
*
|
|
10235
|
+
* A model provider's exhausted quota, a rate limit, an overload, or the router's own refused
|
|
10236
|
+
* credential is not a fault in the agent: the same request succeeds when capacity returns. A
|
|
10237
|
+
* driver that meets one re-enters after a pause (`runDriverWithRetry`), and a leaf continues in
|
|
10238
|
+
* its environment after the same pause (`providerAsExecutor`). Both read the refusal and the pause
|
|
10239
|
+
* from here, so an outage costs a director and its workers the same number of turns.
|
|
10240
|
+
*
|
|
10241
|
+
* This module imports nothing, so the provider executor can read it without the error classes of
|
|
10242
|
+
* `../../errors`.
|
|
10243
|
+
*/
|
|
10244
|
+
/**
|
|
10245
|
+
* Machine codes that say the upstream cannot serve now rather than that the request is wrong.
|
|
10246
|
+
* Each is a code an upstream publishes, not prose: the router's
|
|
10247
|
+
* (`lib/model-substitution.ts`, `lib/upstream-error-triage.ts`) and the OpenAI- and Anthropic-shaped
|
|
10248
|
+
* error types a relayed body carries.
|
|
10249
|
+
*/
|
|
10250
|
+
const UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
|
|
10251
|
+
"provider_quota_exhausted",
|
|
10252
|
+
"provider_quota_exceeded",
|
|
10253
|
+
"provider_rate_limit",
|
|
10254
|
+
"upstream_unavailable",
|
|
10255
|
+
"provider_key_invalid",
|
|
10256
|
+
"rate_limit_exceeded",
|
|
10257
|
+
"rate_limit_error",
|
|
10258
|
+
"overloaded_error"
|
|
10259
|
+
]);
|
|
10260
|
+
/** 429 Too Many Requests, 503 Service Unavailable, and 529, which Anthropic uses for overload. */
|
|
10261
|
+
const UNAVAILABLE_STATUSES = /* @__PURE__ */ new Set([
|
|
10262
|
+
429,
|
|
10263
|
+
503,
|
|
10264
|
+
529
|
|
10265
|
+
]);
|
|
10266
|
+
/**
|
|
10267
|
+
* A harness that flattens the router's JSON body into its own failure text leaves the router's
|
|
10268
|
+
* code in that text: `opencode execution failed: No provider served model "..."
|
|
10269
|
+
* (provider_quota_exhausted)`. The code is matched as a whole token, and a status only in the
|
|
10270
|
+
* `status code <n>` framing a harness prints, so prose that merely mentions a quota does not match.
|
|
10271
|
+
*/
|
|
10272
|
+
function unavailableSignalInText(text) {
|
|
10273
|
+
for (const token of text.toLowerCase().matchAll(/[a-z_]+/gu)) if (UNAVAILABLE_CODES.has(token[0])) return token[0];
|
|
10274
|
+
const status = text.match(/\bstatus code (\d{3})\b/u)?.[1];
|
|
10275
|
+
return status !== void 0 && UNAVAILABLE_STATUSES.has(Number(status)) ? `http-${status}` : void 0;
|
|
10276
|
+
}
|
|
10277
|
+
/**
|
|
10278
|
+
* The code or status that marks a failed turn outcome as an upstream capacity refusal, or
|
|
10279
|
+
* `undefined`. The outcome's own `errorCode` decides first; the failure text is read only when no
|
|
10280
|
+
* code or numeric status decided, because a harness CLI can report the router's refusal as text.
|
|
10281
|
+
*/
|
|
10282
|
+
function unavailableSignalOfFailure(failure) {
|
|
10283
|
+
const code = failure.errorCode?.toLowerCase();
|
|
10284
|
+
if (code !== void 0 && UNAVAILABLE_CODES.has(code)) return code;
|
|
10285
|
+
if (failure.statusCode !== void 0) return UNAVAILABLE_STATUSES.has(failure.statusCode) ? `http-${failure.statusCode}` : void 0;
|
|
10286
|
+
return unavailableSignalInText(failure.error);
|
|
10287
|
+
}
|
|
10288
|
+
const DEFAULT_UNAVAILABLE_PAUSE_MS = 15e3;
|
|
10289
|
+
const DEFAULT_MAX_UNAVAILABLE_PAUSE_MS = 3e5;
|
|
10290
|
+
/**
|
|
10291
|
+
* The pause after the `consecutive`-th refusal in a row (1-based): the first pause, doubled per
|
|
10292
|
+
* earlier refusal, up to the ceiling. The one rule for a driver that re-enters and a leaf that
|
|
10293
|
+
* continues in its environment, so an outage costs both the same number of turns.
|
|
10294
|
+
*/
|
|
10295
|
+
function unavailablePauseMs(consecutive, policy = {}) {
|
|
10296
|
+
return cappedDoublingMs(consecutive, policy.unavailablePauseMs ?? DEFAULT_UNAVAILABLE_PAUSE_MS, policy.maxUnavailablePauseMs ?? DEFAULT_MAX_UNAVAILABLE_PAUSE_MS);
|
|
10297
|
+
}
|
|
10298
|
+
/** `first` doubled per earlier consecutive wait (`consecutive` is 1-based), up to `ceiling`. */
|
|
10299
|
+
function cappedDoublingMs(consecutive, first, ceiling) {
|
|
10300
|
+
const start = Math.max(0, first);
|
|
10301
|
+
return Math.min(Math.max(start, ceiling), start * 2 ** Math.max(0, consecutive - 1));
|
|
10302
|
+
}
|
|
10303
|
+
/**
|
|
10304
|
+
* Spread a wait over `[ms / 2, ms]`. Every driver a Platform restart cut off fails within the same
|
|
10305
|
+
* seconds; without jitter their doubling schedules stay aligned and they all re-enter together
|
|
10306
|
+
* when the service returns. `random` returns a value in `[0, 1)`; 0 keeps the full wait.
|
|
10307
|
+
*/
|
|
10308
|
+
function jitteredMs(ms, random) {
|
|
10309
|
+
return ms - Math.floor(ms / 2 * Math.min(1, Math.max(0, random())));
|
|
10310
|
+
}
|
|
10311
|
+
//#endregion
|
|
10144
10312
|
//#region src/runtime/supervise/retained-executor.ts
|
|
10145
10313
|
/** Scope owns the durable writer; provider executors only publish sanitized admissions. */
|
|
10146
10314
|
const retainedExecutorSeamKey = "runtime.retainedExecutor";
|
|
@@ -10239,6 +10407,7 @@ function classifyRetainedPendingCause(cause, phase = "execution") {
|
|
|
10239
10407
|
if (name === "ZodError" || Array.isArray(Reflect.get(value, "issues")) || code === "JSON_BOUND_VIOLATION") return at === "admission" ? "request-rejected" : "provider-contract";
|
|
10240
10408
|
if (code !== void 0 && transportCodes.has(code)) return "transport";
|
|
10241
10409
|
if (name === "NetworkError" || name === "ServerError") return "transport";
|
|
10410
|
+
if (status === void 0 && at === "admission" && code !== void 0 && UNAVAILABLE_CODES.has(code.toLowerCase())) return "request-rejected";
|
|
10242
10411
|
if (status !== void 0) {
|
|
10243
10412
|
if (status >= 500) return "transport";
|
|
10244
10413
|
if (at === "admission" && status !== 408) return "request-rejected";
|
|
@@ -10517,87 +10686,6 @@ function sandboxSessionTraceSource(box, sessionId, opts = {}) {
|
|
|
10517
10686
|
});
|
|
10518
10687
|
}
|
|
10519
10688
|
//#endregion
|
|
10520
|
-
//#region src/runtime/supervise/upstream-unavailable.ts
|
|
10521
|
-
/**
|
|
10522
|
-
* An upstream that cannot serve NOW: how Runtime recognizes one, and how long an agent waits for it.
|
|
10523
|
-
*
|
|
10524
|
-
* A model provider's exhausted quota, a rate limit, an overload, or the router's own refused
|
|
10525
|
-
* credential is not a fault in the agent: the same request succeeds when capacity returns. A
|
|
10526
|
-
* driver that meets one re-enters after a pause (`runDriverWithRetry`), and a leaf continues in
|
|
10527
|
-
* its environment after the same pause (`providerAsExecutor`). Both read the refusal and the pause
|
|
10528
|
-
* from here, so an outage costs a director and its workers the same number of turns.
|
|
10529
|
-
*
|
|
10530
|
-
* This module imports nothing, so the provider executor can read it without the error classes of
|
|
10531
|
-
* `../../errors`.
|
|
10532
|
-
*/
|
|
10533
|
-
/**
|
|
10534
|
-
* Machine codes that say the upstream cannot serve now rather than that the request is wrong.
|
|
10535
|
-
* Each is a code an upstream publishes, not prose: the router's
|
|
10536
|
-
* (`lib/model-substitution.ts`, `lib/upstream-error-triage.ts`) and the OpenAI- and Anthropic-shaped
|
|
10537
|
-
* error types a relayed body carries.
|
|
10538
|
-
*/
|
|
10539
|
-
const UNAVAILABLE_CODES = /* @__PURE__ */ new Set([
|
|
10540
|
-
"provider_quota_exhausted",
|
|
10541
|
-
"provider_quota_exceeded",
|
|
10542
|
-
"provider_rate_limit",
|
|
10543
|
-
"upstream_unavailable",
|
|
10544
|
-
"provider_key_invalid",
|
|
10545
|
-
"rate_limit_exceeded",
|
|
10546
|
-
"rate_limit_error",
|
|
10547
|
-
"overloaded_error"
|
|
10548
|
-
]);
|
|
10549
|
-
/** 429 Too Many Requests, 503 Service Unavailable, and 529, which Anthropic uses for overload. */
|
|
10550
|
-
const UNAVAILABLE_STATUSES = /* @__PURE__ */ new Set([
|
|
10551
|
-
429,
|
|
10552
|
-
503,
|
|
10553
|
-
529
|
|
10554
|
-
]);
|
|
10555
|
-
/**
|
|
10556
|
-
* A harness that flattens the router's JSON body into its own failure text leaves the router's
|
|
10557
|
-
* code in that text: `opencode execution failed: No provider served model "..."
|
|
10558
|
-
* (provider_quota_exhausted)`. The code is matched as a whole token, and a status only in the
|
|
10559
|
-
* `status code <n>` framing a harness prints, so prose that merely mentions a quota does not match.
|
|
10560
|
-
*/
|
|
10561
|
-
function unavailableSignalInText(text) {
|
|
10562
|
-
for (const token of text.toLowerCase().matchAll(/[a-z_]+/gu)) if (UNAVAILABLE_CODES.has(token[0])) return token[0];
|
|
10563
|
-
const status = text.match(/\bstatus code (\d{3})\b/u)?.[1];
|
|
10564
|
-
return status !== void 0 && UNAVAILABLE_STATUSES.has(Number(status)) ? `http-${status}` : void 0;
|
|
10565
|
-
}
|
|
10566
|
-
/**
|
|
10567
|
-
* The code or status that marks a failed turn outcome as an upstream capacity refusal, or
|
|
10568
|
-
* `undefined`. The outcome's own `errorCode` decides first; the failure text is read only when no
|
|
10569
|
-
* code or numeric status decided, because a harness CLI can report the router's refusal as text.
|
|
10570
|
-
*/
|
|
10571
|
-
function unavailableSignalOfFailure(failure) {
|
|
10572
|
-
const code = failure.errorCode?.toLowerCase();
|
|
10573
|
-
if (code !== void 0 && UNAVAILABLE_CODES.has(code)) return code;
|
|
10574
|
-
if (failure.statusCode !== void 0) return UNAVAILABLE_STATUSES.has(failure.statusCode) ? `http-${failure.statusCode}` : void 0;
|
|
10575
|
-
return unavailableSignalInText(failure.error);
|
|
10576
|
-
}
|
|
10577
|
-
const DEFAULT_UNAVAILABLE_PAUSE_MS = 15e3;
|
|
10578
|
-
const DEFAULT_MAX_UNAVAILABLE_PAUSE_MS = 3e5;
|
|
10579
|
-
/**
|
|
10580
|
-
* The pause after the `consecutive`-th refusal in a row (1-based): the first pause, doubled per
|
|
10581
|
-
* earlier refusal, up to the ceiling. The one rule for a driver that re-enters and a leaf that
|
|
10582
|
-
* continues in its environment, so an outage costs both the same number of turns.
|
|
10583
|
-
*/
|
|
10584
|
-
function unavailablePauseMs(consecutive, policy = {}) {
|
|
10585
|
-
return cappedDoublingMs(consecutive, policy.unavailablePauseMs ?? DEFAULT_UNAVAILABLE_PAUSE_MS, policy.maxUnavailablePauseMs ?? DEFAULT_MAX_UNAVAILABLE_PAUSE_MS);
|
|
10586
|
-
}
|
|
10587
|
-
/** `first` doubled per earlier consecutive wait (`consecutive` is 1-based), up to `ceiling`. */
|
|
10588
|
-
function cappedDoublingMs(consecutive, first, ceiling) {
|
|
10589
|
-
const start = Math.max(0, first);
|
|
10590
|
-
return Math.min(Math.max(start, ceiling), start * 2 ** Math.max(0, consecutive - 1));
|
|
10591
|
-
}
|
|
10592
|
-
/**
|
|
10593
|
-
* Spread a wait over `[ms / 2, ms]`. Every driver a Platform restart cut off fails within the same
|
|
10594
|
-
* seconds; without jitter their doubling schedules stay aligned and they all re-enter together
|
|
10595
|
-
* when the service returns. `random` returns a value in `[0, 1)`; 0 keeps the full wait.
|
|
10596
|
-
*/
|
|
10597
|
-
function jitteredMs(ms, random) {
|
|
10598
|
-
return ms - Math.floor(ms / 2 * Math.min(1, Math.max(0, random())));
|
|
10599
|
-
}
|
|
10600
|
-
//#endregion
|
|
10601
10689
|
//#region src/runtime/supervise/workspace-checkpoint.ts
|
|
10602
10690
|
/** Workspace-relative path of the marker Runtime writes before every checkpoint. */
|
|
10603
10691
|
const WORKSPACE_CHECKPOINT_MARKER_PATH = ".agent-runtime-checkpoint";
|
|
@@ -10930,6 +11018,7 @@ function createProviderExecutor(provider, profile, ctx, options, placement) {
|
|
|
10930
11018
|
if (workspaceSnapshot !== void 0) return workspaceSnapshot;
|
|
10931
11019
|
if (workspaceCapturePromise === void 0) {
|
|
10932
11020
|
workspaceOutcome = outcome;
|
|
11021
|
+
stopLiveMirror();
|
|
10933
11022
|
if (nativeTimer !== void 0) clearTimeout(nativeTimer);
|
|
10934
11023
|
nativeTimer = void 0;
|
|
10935
11024
|
const captureContext = {
|
|
@@ -10945,7 +11034,7 @@ function createProviderExecutor(provider, profile, ctx, options, placement) {
|
|
|
10945
11034
|
const reportTiming = retention?.onWorkspaceCapture;
|
|
10946
11035
|
let timing;
|
|
10947
11036
|
const capturedExecutionId = workspaceExecutionId;
|
|
10948
|
-
workspaceCapturePromise = (nativeInFlight
|
|
11037
|
+
workspaceCapturePromise = Promise.all([nativeInFlight, liveInFlight]).then(() => captureProviderWorkspaceSnapshot(workspaceRetention, captureContext, "environment", reportTiming === void 0 ? void 0 : (measured) => {
|
|
10949
11038
|
timing = measured;
|
|
10950
11039
|
})).finally(async () => {
|
|
10951
11040
|
if (timing === void 0 || reportTiming === void 0) return;
|
|
@@ -10998,6 +11087,38 @@ function createProviderExecutor(provider, profile, ctx, options, placement) {
|
|
|
10998
11087
|
const nativePort = options.workspaceRetention?.captureNative === void 0 ? void 0 : options.workspaceRetention;
|
|
10999
11088
|
let nativeInFlight;
|
|
11000
11089
|
let nativeTimer;
|
|
11090
|
+
const livePort = options.workspaceRetention?.captureLive === void 0 ? void 0 : options.workspaceRetention;
|
|
11091
|
+
let liveInFlight;
|
|
11092
|
+
let liveTimer;
|
|
11093
|
+
const stopLiveMirror = () => {
|
|
11094
|
+
if (liveTimer !== void 0) clearTimeout(liveTimer);
|
|
11095
|
+
liveTimer = void 0;
|
|
11096
|
+
};
|
|
11097
|
+
const startLiveMirror = (next) => {
|
|
11098
|
+
if (livePort?.liveIntervalMs === void 0) return;
|
|
11099
|
+
stopLiveMirror();
|
|
11100
|
+
const schedule = () => {
|
|
11101
|
+
liveTimer = setTimeout(() => {
|
|
11102
|
+
if (liveInFlight === void 0 && workspaceCapturePromise === void 0) {
|
|
11103
|
+
const tracked = captureProviderLiveWorkspace(livePort, {
|
|
11104
|
+
environment: next,
|
|
11105
|
+
executionId: workspaceExecutionId,
|
|
11106
|
+
...workspaceControlRef === void 0 ? {} : { controlRef: workspaceControlRef },
|
|
11107
|
+
...node === void 0 ? {} : { node },
|
|
11108
|
+
providerSessionId,
|
|
11109
|
+
nativeSessionId: null,
|
|
11110
|
+
profile: createProfile
|
|
11111
|
+
}).then(() => void 0, () => void 0).finally(() => {
|
|
11112
|
+
if (liveInFlight === tracked) liveInFlight = void 0;
|
|
11113
|
+
});
|
|
11114
|
+
liveInFlight = tracked;
|
|
11115
|
+
}
|
|
11116
|
+
schedule();
|
|
11117
|
+
}, livePort.liveIntervalMs);
|
|
11118
|
+
liveTimer.unref?.();
|
|
11119
|
+
};
|
|
11120
|
+
schedule();
|
|
11121
|
+
};
|
|
11001
11122
|
let nativeHoldsTranscript = false;
|
|
11002
11123
|
const stopNativeMirror = async () => {
|
|
11003
11124
|
if (nativeTimer !== void 0) clearTimeout(nativeTimer);
|
|
@@ -11052,6 +11173,7 @@ function createProviderExecutor(provider, profile, ctx, options, placement) {
|
|
|
11052
11173
|
return tracked;
|
|
11053
11174
|
};
|
|
11054
11175
|
const startNativeMirror = (next) => {
|
|
11176
|
+
startLiveMirror(next);
|
|
11055
11177
|
if (nativePort === void 0) return;
|
|
11056
11178
|
if (nativeTimer !== void 0) clearTimeout(nativeTimer);
|
|
11057
11179
|
const interval = nativePort.nativeIntervalMs ?? 12e4;
|
|
@@ -11699,6 +11821,7 @@ async function* streamProviderExecutor(args) {
|
|
|
11699
11821
|
handCaptureToOwner(settledResult, outcome, true);
|
|
11700
11822
|
}
|
|
11701
11823
|
} catch (error) {
|
|
11824
|
+
yield* creditUsage(usageLedger.settleTurn(args.profile.name ?? "agent"));
|
|
11702
11825
|
failure = source.retained ? new RetainedExecutionPendingError(error) : error;
|
|
11703
11826
|
failed = true;
|
|
11704
11827
|
if (args.options.workspaceRetention !== void 0) args.onUnsettledFailure();
|
|
@@ -20886,337 +21009,984 @@ function singleShotDriver(maxIterations) {
|
|
|
20886
21009
|
};
|
|
20887
21010
|
}
|
|
20888
21011
|
//#endregion
|
|
20889
|
-
//#region src/runtime/supervise/
|
|
21012
|
+
//#region src/runtime/supervise/continuation.ts
|
|
20890
21013
|
/**
|
|
21014
|
+
* The continuation: what a manager with a completion check is told when its turn ends and the
|
|
21015
|
+
* check is unmet, and the one rule that decides whether it is sent back.
|
|
20891
21016
|
*
|
|
20892
|
-
*
|
|
20893
|
-
*
|
|
20894
|
-
*
|
|
20895
|
-
*
|
|
20896
|
-
*
|
|
20897
|
-
*
|
|
20898
|
-
* nothing that holds N slots full, which is why a 5-worker run can peak at 2 live workers and sit
|
|
20899
|
-
* ~50% idle. This is a policy over the EXISTING `Scope.spawn` / `Scope.next` primitives: it adds
|
|
20900
|
-
* no second admission path, so the conserved budget pool stays the only fence on total work and
|
|
20901
|
-
* `width` is only a fence on simultaneous work.
|
|
20902
|
-
*
|
|
20903
|
-
* Fail-closed by construction: an admission rejection (`budget-exhausted` / `depth-exceeded`) is
|
|
20904
|
-
* recorded and STOPS further admission — the loop then drains what is already live and returns.
|
|
20905
|
-
* It never retries a rejected spawn against the same pool, and it never spawns past `width`.
|
|
20906
|
-
*
|
|
20907
|
-
* ── Concurrency ────────────────────────────────────────────────────────────────────────────────
|
|
20908
|
-
*
|
|
20909
|
-
* `width` bounds the children THIS dispatcher holds in flight. The tree-wide bound on working
|
|
20910
|
-
* agents is `SuperviseOptions.workerSlots` (`./worker-slots`): a spawn past it is queued by the
|
|
20911
|
-
* scope, not refused, so a `width` above the free slots only lengthens the allocator's queue.
|
|
20912
|
-
* `SandboxLineage`'s fork concurrency bounds boxes inside ONE leaf's fork wave, a different unit.
|
|
21017
|
+
* Measured on discovery-lab before this module (2026-09-24, 670 settled runs, 1,596 re-entries):
|
|
21018
|
+
* 1,565 re-entries followed a failed turn and resent the original task, and 31 carried note text.
|
|
21019
|
+
* The note Runtime wrote itself held seven fixed lines and no field for the check's verdict. Of 40
|
|
21020
|
+
* episodes where the director ended on its own and the outside check disagreed, 33 were told
|
|
21021
|
+
* nothing before they ended. 100 of 236 resubmits sent the identical packet. So the director
|
|
21022
|
+
* mostly graded itself, and when it was wrong it heard nothing it could use.
|
|
20913
21023
|
*
|
|
20914
|
-
*
|
|
21024
|
+
* The design (discovery `docs/38-one-loop-and-continuation.md`) makes three things single, and
|
|
21025
|
+
* this module holds the part Runtime owns:
|
|
20915
21026
|
*
|
|
20916
|
-
*
|
|
20917
|
-
*
|
|
20918
|
-
*
|
|
20919
|
-
*
|
|
20920
|
-
*
|
|
20921
|
-
*
|
|
20922
|
-
*
|
|
20923
|
-
*
|
|
20924
|
-
*
|
|
21027
|
+
* - ONE CHECK. A check reading is a {@link CheckVerdict}: pass, the items it read, the composite,
|
|
21028
|
+
* and one `FAIL <item> <where>: <reason>` line per failed item. The same check decides
|
|
21029
|
+
* `submit_result`, runs when a turn ends without an accepted result, scores the finished run,
|
|
21030
|
+
* and ranks versions. A check that could not run gives no verdict ({@link CheckUnavailableError}),
|
|
21031
|
+
* and the loop pauses instead of blaming the director.
|
|
21032
|
+
* - ONE RESEND RULE. {@link ContinuationPolicy} is required for a manager with a check. It holds a
|
|
21033
|
+
* deadline and `maxBarren`, the re-entered turns in a row that may end without progress. There
|
|
21034
|
+
* is no re-prompt count and no default: a retry default in source is a research decision.
|
|
21035
|
+
* - ONE NOTE. {@link composeContinuationNote} writes it. Runtime owns the facts and their order;
|
|
21036
|
+
* the {@link ContinuationProfile} owns every instruction word, so an optimizer can change the
|
|
21037
|
+
* words without touching code. A play may append a section, never remove or rewrite one.
|
|
20925
21038
|
*
|
|
20926
|
-
*
|
|
21039
|
+
* Evidence behind the note's shape: located errors help (given the first mistake's true location,
|
|
21040
|
+
* fix rates rose 23.5 to 43.9 points, Tyen 2023); unsupported pressure does not (threats and tips
|
|
21041
|
+
* had no significant effect, Wharton 2025; challenges without evidence flipped correct answers,
|
|
21042
|
+
* FlipFlop); repeated summaries drop detail while itemized additions do not (ACE). So the note is
|
|
21043
|
+
* a list of located facts, findings are added and marked resolved rather than rewritten, and the
|
|
21044
|
+
* profile, not this file, decides how blunt the words are.
|
|
20927
21045
|
*/
|
|
20928
21046
|
/**
|
|
20929
|
-
*
|
|
20930
|
-
*
|
|
20931
|
-
*
|
|
20932
|
-
* The loop is: fill free slots from `nextUnit` → `await scope.next()` → deliver the settlement →
|
|
20933
|
-
* refill → repeat. Because the refill happens immediately after each settlement rather than after
|
|
20934
|
-
* a whole round, a slow child never idles the other slots.
|
|
21047
|
+
* The check could not run: its box did not start, its judge's provider refused, its program
|
|
21048
|
+
* crashed. It is not a verdict on the result. Inside a run the loop pauses, as it does for an
|
|
21049
|
+
* unavailable upstream, and the refusal tells the director its result was not judged.
|
|
20935
21050
|
*/
|
|
20936
|
-
|
|
20937
|
-
|
|
20938
|
-
|
|
20939
|
-
|
|
20940
|
-
const live = /* @__PURE__ */ new Set();
|
|
20941
|
-
let admitted = 0;
|
|
20942
|
-
let peakLive = 0;
|
|
20943
|
-
let queueDry = false;
|
|
20944
|
-
let stopReason = "drained";
|
|
20945
|
-
const fill = async () => {
|
|
20946
|
-
while (live.size < opts.width) {
|
|
20947
|
-
if (queueDry) return;
|
|
20948
|
-
if (scope.signal.aborted) {
|
|
20949
|
-
stopReason = "aborted";
|
|
20950
|
-
queueDry = true;
|
|
20951
|
-
return;
|
|
20952
|
-
}
|
|
20953
|
-
if (opts.shouldStop?.() === true) {
|
|
20954
|
-
stopReason = "stopped";
|
|
20955
|
-
queueDry = true;
|
|
20956
|
-
return;
|
|
20957
|
-
}
|
|
20958
|
-
const unit = await opts.nextUnit();
|
|
20959
|
-
if (unit === void 0) {
|
|
20960
|
-
queueDry = true;
|
|
20961
|
-
return;
|
|
20962
|
-
}
|
|
20963
|
-
const res = scope.spawn(unit.agent, unit.task, unit.opts);
|
|
20964
|
-
if (!res.ok) {
|
|
20965
|
-
rejected.push(`${unit.opts.label}: ${res.reason}`);
|
|
20966
|
-
stopReason = "not-admitted";
|
|
20967
|
-
queueDry = true;
|
|
20968
|
-
return;
|
|
20969
|
-
}
|
|
20970
|
-
live.add(res.handle.id);
|
|
20971
|
-
admitted += 1;
|
|
20972
|
-
if (live.size > peakLive) peakLive = live.size;
|
|
20973
|
-
}
|
|
20974
|
-
};
|
|
20975
|
-
await fill();
|
|
20976
|
-
while (live.size > 0) {
|
|
20977
|
-
const s = await scope.next();
|
|
20978
|
-
if (s === null) break;
|
|
20979
|
-
live.delete(s.handle.id);
|
|
20980
|
-
settled.push(s);
|
|
20981
|
-
await opts.onSettled?.(s);
|
|
20982
|
-
await fill();
|
|
21051
|
+
var CheckUnavailableError = class extends Error {
|
|
21052
|
+
constructor(message, options) {
|
|
21053
|
+
super(message, options);
|
|
21054
|
+
this.name = "CheckUnavailableError";
|
|
20983
21055
|
}
|
|
20984
|
-
|
|
20985
|
-
|
|
20986
|
-
|
|
20987
|
-
|
|
20988
|
-
|
|
20989
|
-
|
|
20990
|
-
|
|
21056
|
+
};
|
|
21057
|
+
/** Read a check function's return value into a verdict. Anything but `true` or a passing verdict
|
|
21058
|
+
* is a failure: a check fails closed. */
|
|
21059
|
+
function checkVerdictOf(outcome) {
|
|
21060
|
+
if (outcome === true) return { pass: true };
|
|
21061
|
+
if (typeof outcome !== "object" || outcome === null || Array.isArray(outcome)) return { pass: false };
|
|
21062
|
+
const raw = outcome;
|
|
21063
|
+
const items = typeof raw.items === "object" && raw.items !== null && !Array.isArray(raw.items) ? Object.fromEntries(Object.entries(raw.items).filter((entry) => typeof entry[1] === "number" && Number.isFinite(entry[1]))) : void 0;
|
|
21064
|
+
const failures = Array.isArray(raw.failures) ? raw.failures.filter((line) => typeof line === "string" && line.trim() !== "") : void 0;
|
|
21065
|
+
return Object.freeze({
|
|
21066
|
+
pass: raw.pass === true,
|
|
21067
|
+
...items === void 0 ? {} : { items: Object.freeze(items) },
|
|
21068
|
+
...finite(raw.composite) ? { composite: raw.composite } : {},
|
|
21069
|
+
...finite(raw.threshold) ? { threshold: raw.threshold } : {},
|
|
21070
|
+
...failures === void 0 ? {} : { failures: Object.freeze(failures) },
|
|
21071
|
+
...typeof raw.review === "string" && raw.review.trim() !== "" ? { review: raw.review.trim() } : {}
|
|
21072
|
+
});
|
|
20991
21073
|
}
|
|
20992
21074
|
/**
|
|
20993
|
-
*
|
|
20994
|
-
*
|
|
20995
|
-
*
|
|
21075
|
+
* Read an agent-eval `JudgeScore` from a check program into a verdict.
|
|
21076
|
+
*
|
|
21077
|
+
* Each dimension is a checked item. `notes` lines that start with `FAIL ` are the failures; the
|
|
21078
|
+
* remaining lines are the check's review. The verdict passes when `composite >= threshold` and the
|
|
21079
|
+
* score is not marked `failed`; a `failed` score means the judge itself did not run, which is
|
|
21080
|
+
* {@link CheckUnavailableError}, not a verdict.
|
|
20996
21081
|
*/
|
|
20997
|
-
function
|
|
20998
|
-
if (
|
|
20999
|
-
|
|
21000
|
-
|
|
21001
|
-
|
|
21002
|
-
|
|
21003
|
-
|
|
21004
|
-
|
|
21005
|
-
|
|
21006
|
-
|
|
21007
|
-
|
|
21008
|
-
|
|
21009
|
-
return {
|
|
21010
|
-
agent: u.agent,
|
|
21011
|
-
task: u.task,
|
|
21012
|
-
opts: {
|
|
21013
|
-
budget,
|
|
21014
|
-
label: u.label
|
|
21015
|
-
}
|
|
21016
|
-
};
|
|
21017
|
-
};
|
|
21018
|
-
}
|
|
21019
|
-
//#endregion
|
|
21020
|
-
//#region src/runtime/retained-interactive-handle.ts
|
|
21021
|
-
function createRetainedInteractiveRunHandle(environment, inputRef, capabilities, requestedStart) {
|
|
21022
|
-
const ref = freezeInteractiveRef(inputRef);
|
|
21023
|
-
const source = environment.interactive(ref);
|
|
21024
|
-
if (!sameInteractiveRef(source.ref, ref)) throw new Error("provider reconstructed a different interactive process");
|
|
21025
|
-
if (!source.sendPrompt) throw new Error("provider declared interactive prompt support but exposed no prompt method");
|
|
21026
|
-
return Object.freeze({
|
|
21027
|
-
ref,
|
|
21028
|
-
capabilities,
|
|
21029
|
-
claimControl: async (request, options) => exactClaim(ref, request, await awaitAbortable$1(Promise.resolve().then(() => source.claimControl(request, options)), options?.signal)),
|
|
21030
|
-
status: async (options) => exactStatus(ref, await awaitAbortable$1(Promise.resolve().then(() => source.status(options)), options?.signal), requestedStart),
|
|
21031
|
-
attach: async (request, options) => exactTerminal(ref, request, await awaitAbortable$1(Promise.resolve().then(() => source.attach(request, options)), options?.signal)),
|
|
21032
|
-
sendPrompt: async (command, options) => exactPrompt(ref, await awaitAbortable$1(Promise.resolve().then(() => source.sendPrompt(command, options)), options?.signal), command),
|
|
21033
|
-
stop: async (command, options) => exactStop(ref, await awaitAbortable$1(Promise.resolve().then(() => source.stop(command, options)), options?.signal), command)
|
|
21082
|
+
function verdictFromJudgeScore(score, threshold) {
|
|
21083
|
+
if (score.failed === true) throw new CheckUnavailableError(`the check's judge did not run: ${score.notes}`);
|
|
21084
|
+
const lines = score.notes.split("\n").map((line) => line.trimEnd());
|
|
21085
|
+
const failures = lines.filter((line) => line.startsWith("FAIL "));
|
|
21086
|
+
const review = lines.filter((line) => !line.startsWith("FAIL ") && line.trim() !== "").join("\n");
|
|
21087
|
+
return checkVerdictOf({
|
|
21088
|
+
pass: score.composite >= threshold,
|
|
21089
|
+
items: score.dimensions,
|
|
21090
|
+
composite: score.composite,
|
|
21091
|
+
threshold,
|
|
21092
|
+
failures,
|
|
21093
|
+
...review === "" ? {} : { review }
|
|
21034
21094
|
});
|
|
21035
21095
|
}
|
|
21036
|
-
|
|
21037
|
-
|
|
21038
|
-
|
|
21039
|
-
|
|
21040
|
-
const acknowledgement = parseProviderAcknowledgement(AgentInteractiveSessionControlClaimAcknowledgementSchema, value, "control claim");
|
|
21041
|
-
if (!agentInteractiveSessionControlClaimAcknowledgementMatchesRequest(request, acknowledgement)) throw new RetainedRunProviderContractError("provider returned a control claim acknowledgement for another request");
|
|
21042
|
-
if (acknowledgement.control && !agentInteractiveSessionControlClaimMatchesRef(ref, acknowledgement.control)) throw new RetainedRunProviderContractError("provider returned a control claim for another interactive process");
|
|
21043
|
-
return acknowledgement;
|
|
21044
|
-
}
|
|
21045
|
-
function exactPrompt(ref, value, command) {
|
|
21046
|
-
const acknowledgement = parseProviderAcknowledgement(AgentInteractiveSessionPromptAcknowledgementSchema, value, "prompt");
|
|
21047
|
-
if (!agentInteractiveSessionPromptAcknowledgementMatchesCommand(command, acknowledgement)) throw new RetainedRunProviderContractError("provider returned a prompt acknowledgement for another request");
|
|
21048
|
-
if (!agentInteractiveSessionControlClaimMatchesRef(ref, acknowledgement.control)) throw new RetainedRunProviderContractError("provider returned a prompt acknowledgement for another interactive process");
|
|
21049
|
-
return acknowledgement;
|
|
21096
|
+
/** Items that fail in `verdict`: every item below 1, or the named items of its FAIL lines. */
|
|
21097
|
+
function failedItems(verdict) {
|
|
21098
|
+
if (verdict.items !== void 0) return Object.entries(verdict.items).filter(([, value]) => value < 1).map(([name]) => name);
|
|
21099
|
+
return (verdict.failures ?? []).map((line) => line.slice(5).split(/[\s:]/u)[0] ?? "");
|
|
21050
21100
|
}
|
|
21051
|
-
|
|
21052
|
-
|
|
21053
|
-
|
|
21054
|
-
if (!agentInteractiveSessionControlClaimMatchesRef(ref, acknowledgement.control)) throw new RetainedRunProviderContractError("provider returned a stop acknowledgement for another interactive process");
|
|
21055
|
-
return acknowledgement;
|
|
21101
|
+
/** Items that pass in `verdict`. */
|
|
21102
|
+
function passedItems(verdict) {
|
|
21103
|
+
return Object.entries(verdict.items ?? {}).filter(([, value]) => value >= 1).map(([name]) => name);
|
|
21056
21104
|
}
|
|
21057
|
-
|
|
21058
|
-
|
|
21059
|
-
|
|
21060
|
-
|
|
21061
|
-
|
|
21105
|
+
/** Resolve and check a policy before any compute. Returns the deadline in epoch ms. */
|
|
21106
|
+
function admitContinuationPolicy(policy, context) {
|
|
21107
|
+
if (typeof policy !== "object" || policy === null) throw new ValidationError$1(`${context}: continuation must be an object`);
|
|
21108
|
+
const deadlineMs = typeof policy.deadline === "string" ? Date.parse(policy.deadline) : policy.deadline;
|
|
21109
|
+
if (typeof deadlineMs !== "number" || !Number.isFinite(deadlineMs) || deadlineMs <= 0) throw new ValidationError$1(`${context}: continuation.deadline must be a finite time (epoch ms or ISO 8601)`);
|
|
21110
|
+
if (!Number.isInteger(policy.maxBarren) || policy.maxBarren < 1) throw new ValidationError$1(`${context}: continuation.maxBarren must be an integer >= 1`);
|
|
21111
|
+
if (policy.failures !== "verbatim" && policy.failures !== "off") throw new ValidationError$1(`${context}: continuation.failures must be 'verbatim' or 'off'`);
|
|
21112
|
+
if (policy.panel !== "on" && policy.panel !== "off") throw new ValidationError$1(`${context}: continuation.panel must be 'on' or 'off'`);
|
|
21113
|
+
if (policy.bar !== "on" && policy.bar !== "off") throw new ValidationError$1(`${context}: continuation.bar must be 'on' or 'off'`);
|
|
21114
|
+
if (policy.panel === "on") {
|
|
21115
|
+
if (typeof policy.runPanel !== "function") throw new ValidationError$1(`${context}: continuation.panel 'on' needs runPanel`);
|
|
21116
|
+
const usd = policy.panelUsd;
|
|
21117
|
+
if (usd === void 0 || !finite(usd.perContinuation) || !finite(usd.perRun) || usd.perContinuation <= 0 || usd.perRun < usd.perContinuation) throw new ValidationError$1(`${context}: continuation.panel 'on' needs panelUsd with 0 < perContinuation <= perRun`);
|
|
21062
21118
|
}
|
|
21119
|
+
if (policy.append !== void 0 && typeof policy.append !== "function") throw new ValidationError$1(`${context}: continuation.append must be a function`);
|
|
21120
|
+
admitContinuationProfile(policy.profile, context);
|
|
21121
|
+
return deadlineMs;
|
|
21063
21122
|
}
|
|
21064
|
-
|
|
21065
|
-
|
|
21066
|
-
|
|
21067
|
-
|
|
21068
|
-
|
|
21069
|
-
|
|
21070
|
-
|
|
21123
|
+
/** The facts a profile template may name. */
|
|
21124
|
+
const CONTINUATION_FACTS = [
|
|
21125
|
+
"composite",
|
|
21126
|
+
"threshold",
|
|
21127
|
+
"failed",
|
|
21128
|
+
"total",
|
|
21129
|
+
"reads",
|
|
21130
|
+
"owed",
|
|
21131
|
+
"settled",
|
|
21132
|
+
"delivered",
|
|
21133
|
+
"continuation"
|
|
21134
|
+
];
|
|
21135
|
+
const factNames = new Set(CONTINUATION_FACTS);
|
|
21136
|
+
const placeholder = /\{([a-zA-Z]+)\}/gu;
|
|
21137
|
+
function admitContinuationProfile(profile, context) {
|
|
21138
|
+
if (typeof profile !== "object" || profile === null) throw new ValidationError$1(`${context}: continuation.profile must be an object`);
|
|
21139
|
+
if (typeof profile.id !== "string" || profile.id.trim() === "") throw new ValidationError$1(`${context}: continuation.profile.id must be non-empty`);
|
|
21140
|
+
const texts = [
|
|
21141
|
+
["opening", profile.opening],
|
|
21142
|
+
["plan", profile.plan],
|
|
21143
|
+
["rules", profile.rules],
|
|
21144
|
+
...Object.entries(profile.headings ?? {}).map(([key, value]) => [`headings.${key}`, value]),
|
|
21145
|
+
...profile.barRule === void 0 ? [] : [["barRule", profile.barRule]]
|
|
21146
|
+
];
|
|
21147
|
+
for (const key of [
|
|
21148
|
+
"failures",
|
|
21149
|
+
"protected",
|
|
21150
|
+
"changed",
|
|
21151
|
+
"findings",
|
|
21152
|
+
"bar",
|
|
21153
|
+
"plan",
|
|
21154
|
+
"rules"
|
|
21155
|
+
]) if (typeof profile.headings?.[key] !== "string") throw new ValidationError$1(`${context}: continuation.profile.headings.${key} must be a string`);
|
|
21156
|
+
for (const [key, text] of texts) {
|
|
21157
|
+
if (typeof text !== "string") throw new ValidationError$1(`${context}: continuation.profile.${key} must be a string`);
|
|
21158
|
+
for (const match of text.matchAll(placeholder)) if (!factNames.has(match[1] ?? "")) throw new ValidationError$1(`${context}: continuation.profile.${key} names {${match[1]}}, which is not a fact Runtime supplies (${CONTINUATION_FACTS.join(", ")})`);
|
|
21071
21159
|
}
|
|
21072
|
-
if (
|
|
21073
|
-
if (
|
|
21074
|
-
|
|
21160
|
+
if (profile.review !== void 0) {
|
|
21161
|
+
if (typeof profile.review !== "string" || profile.review.trim() === "") throw new ValidationError$1(`${context}: continuation.profile.review must be non-empty`);
|
|
21162
|
+
for (const match of profile.review.matchAll(placeholder)) if (![
|
|
21163
|
+
"version",
|
|
21164
|
+
"path",
|
|
21165
|
+
"score"
|
|
21166
|
+
].includes(match[1] ?? "")) throw new ValidationError$1(`${context}: continuation.profile.review names {${match[1]}}; it may name {version}, {path} and {score}`);
|
|
21075
21167
|
}
|
|
21076
|
-
|
|
21168
|
+
if (profile.opening.trim() === "") throw new ValidationError$1(`${context}: continuation.profile.opening must be non-empty`);
|
|
21169
|
+
for (const [index, question] of (profile.questions ?? []).entries()) if (typeof question !== "string" || question.trim() === "") throw new ValidationError$1(`${context}: continuation.profile.questions[${index}] must be a non-empty string`);
|
|
21077
21170
|
}
|
|
21078
|
-
|
|
21079
|
-
|
|
21080
|
-
|
|
21081
|
-
|
|
21082
|
-
|
|
21083
|
-
if (
|
|
21084
|
-
if (
|
|
21085
|
-
return
|
|
21086
|
-
}
|
|
21087
|
-
function sameInteractiveRef(left, right) {
|
|
21088
|
-
return canonicalCandidateDigest(left) === canonicalCandidateDigest(right);
|
|
21171
|
+
/** Findings reach the note only when every citation resolves, at least two distinct spans are
|
|
21172
|
+
* cited, and the independent verifier agreed. The best method in Who&When found the failing step
|
|
21173
|
+
* 14.2% of the time, so an unverified finding is noise with a citation. */
|
|
21174
|
+
function admitFinding(finding) {
|
|
21175
|
+
if (!finding.verified) return false;
|
|
21176
|
+
if (finding.citations.length === 0) return false;
|
|
21177
|
+
if (!finding.citations.every((citation) => citation.resolved)) return false;
|
|
21178
|
+
return new Set(finding.citations.map((citation) => citation.uri)).size >= 2;
|
|
21089
21179
|
}
|
|
21090
|
-
|
|
21091
|
-
|
|
21180
|
+
/** About 1,500 tokens of findings. */
|
|
21181
|
+
const FINDINGS_CHAR_CAP = 6e3;
|
|
21092
21182
|
/**
|
|
21093
|
-
*
|
|
21094
|
-
*
|
|
21095
|
-
*
|
|
21096
|
-
* @stable
|
|
21183
|
+
* Rank the admitted findings by how many failed items they explain, drop repeats, and keep them
|
|
21184
|
+
* within the note's cap. Earlier findings stay as they were written (ACE: itemized additions keep
|
|
21185
|
+
* detail that repeated rewrites lose); a finding whose items all pass now is marked resolved.
|
|
21097
21186
|
*/
|
|
21098
|
-
|
|
21099
|
-
|
|
21100
|
-
const
|
|
21101
|
-
|
|
21102
|
-
|
|
21103
|
-
|
|
21104
|
-
|
|
21105
|
-
const
|
|
21106
|
-
|
|
21107
|
-
|
|
21108
|
-
|
|
21109
|
-
|
|
21110
|
-
|
|
21111
|
-
|
|
21112
|
-
|
|
21113
|
-
|
|
21114
|
-
const
|
|
21115
|
-
|
|
21116
|
-
|
|
21117
|
-
|
|
21118
|
-
|
|
21119
|
-
|
|
21120
|
-
|
|
21121
|
-
|
|
21122
|
-
|
|
21123
|
-
metadata: retainedEnvironmentMetadata(startOptions.environment.metadata, startOptions.environment.idempotencyKey)
|
|
21124
|
-
}), startOptions.signal);
|
|
21125
|
-
let capabilities;
|
|
21126
|
-
try {
|
|
21127
|
-
assertExactEnvironment(startOptions.provider.name, environment);
|
|
21128
|
-
startOptions.signal?.throwIfAborted();
|
|
21129
|
-
capabilities = interactiveCapabilitiesForEnvironment(startOptions.provider.name, providerCapabilities, environment);
|
|
21130
|
-
assertInteractiveMethods(startOptions.provider.name, environment);
|
|
21131
|
-
} catch (error) {
|
|
21132
|
-
await destroyUnusedEnvironment(environment, error);
|
|
21133
|
-
throw error;
|
|
21187
|
+
function distillFindings(prior, proposed, failing, continuation) {
|
|
21188
|
+
const key = (claim) => claim.toLowerCase().replace(/\s+/gu, " ").trim();
|
|
21189
|
+
const known = new Set(prior.map((finding) => key(finding.claim)));
|
|
21190
|
+
const carried = prior.map((finding) => finding.resolvedAt === void 0 && (finding.items?.length ?? 0) > 0 && (finding.items ?? []).every((item) => !failing.has(item)) ? {
|
|
21191
|
+
...finding,
|
|
21192
|
+
resolvedAt: continuation
|
|
21193
|
+
} : finding);
|
|
21194
|
+
const fresh = proposed.filter(admitFinding).filter((finding) => {
|
|
21195
|
+
const k = key(finding.claim);
|
|
21196
|
+
if (known.has(k)) return false;
|
|
21197
|
+
known.add(k);
|
|
21198
|
+
return true;
|
|
21199
|
+
}).map((finding) => ({
|
|
21200
|
+
finding,
|
|
21201
|
+
links: (finding.items ?? []).filter((item) => failing.has(item)).length
|
|
21202
|
+
})).sort((a, b) => b.links - a.links);
|
|
21203
|
+
const out = [...carried];
|
|
21204
|
+
let used = carried.filter((finding) => finding.resolvedAt === void 0).reduce((sum, finding) => sum + finding.claim.length, 0);
|
|
21205
|
+
for (const { finding } of fresh) {
|
|
21206
|
+
if (used + finding.claim.length > FINDINGS_CHAR_CAP) break;
|
|
21207
|
+
used += finding.claim.length;
|
|
21208
|
+
out.push({
|
|
21209
|
+
...finding,
|
|
21210
|
+
admittedAt: continuation
|
|
21211
|
+
});
|
|
21134
21212
|
}
|
|
21135
|
-
|
|
21136
|
-
|
|
21137
|
-
|
|
21138
|
-
|
|
21139
|
-
|
|
21140
|
-
|
|
21141
|
-
|
|
21142
|
-
|
|
21143
|
-
|
|
21144
|
-
|
|
21145
|
-
|
|
21146
|
-
|
|
21147
|
-
|
|
21148
|
-
|
|
21149
|
-
|
|
21150
|
-
}
|
|
21151
|
-
return
|
|
21213
|
+
return out;
|
|
21214
|
+
}
|
|
21215
|
+
/** Expand the profile's question templates over the failed items and the settled workers. */
|
|
21216
|
+
function expandQuestions(templates, failing, workers) {
|
|
21217
|
+
const out = [];
|
|
21218
|
+
for (const template of templates) {
|
|
21219
|
+
const hasItem = template.includes("{item}");
|
|
21220
|
+
const hasWorker = template.includes("{worker}");
|
|
21221
|
+
if (!hasItem && !hasWorker) {
|
|
21222
|
+
out.push(template);
|
|
21223
|
+
continue;
|
|
21224
|
+
}
|
|
21225
|
+
const items = hasItem ? failing : [""];
|
|
21226
|
+
const each = hasWorker ? workers.map((worker) => `${worker.id} (${worker.label})`) : [""];
|
|
21227
|
+
for (const item of items) for (const worker of each) out.push(template.replaceAll("{item}", item).replaceAll("{worker}", worker));
|
|
21228
|
+
}
|
|
21229
|
+
return out;
|
|
21152
21230
|
}
|
|
21153
21231
|
/**
|
|
21154
|
-
*
|
|
21155
|
-
*
|
|
21156
|
-
* Resolves to the handle of the admitted process (resumed through the exact,
|
|
21157
|
-
* idempotent start request), or `null` when the provider no longer holds the
|
|
21158
|
-
* environment (not found; nothing remains to stop). @stable
|
|
21232
|
+
* Write one continuation note, sections 1 to 8 of docs/38. Section 9, the run's state, is
|
|
21233
|
+
* `composeReentryTask`, which wraps this text. Pure: the same input writes the same note.
|
|
21159
21234
|
*/
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
|
|
21163
|
-
const
|
|
21164
|
-
|
|
21165
|
-
|
|
21166
|
-
|
|
21167
|
-
|
|
21168
|
-
|
|
21169
|
-
|
|
21170
|
-
|
|
21171
|
-
|
|
21172
|
-
|
|
21173
|
-
|
|
21235
|
+
function composeContinuationNote(input) {
|
|
21236
|
+
const { profile, verdict } = input;
|
|
21237
|
+
const items = verdict?.items ?? {};
|
|
21238
|
+
const total = Object.keys(items).length;
|
|
21239
|
+
const failing = verdict === void 0 ? [] : failedItems(verdict);
|
|
21240
|
+
const facts = {
|
|
21241
|
+
composite: verdict?.composite === void 0 ? "unknown" : format(verdict.composite),
|
|
21242
|
+
threshold: verdict?.threshold === void 0 ? "unknown" : format(verdict.threshold),
|
|
21243
|
+
failed: verdict === void 0 || total === 0 && (verdict.failures?.length ?? 0) === 0 ? "unknown" : String(failing.length),
|
|
21244
|
+
total: total === 0 ? "unknown" : String(total),
|
|
21245
|
+
reads: String(input.reads),
|
|
21246
|
+
owed: input.owed?.trim() || "not described",
|
|
21247
|
+
settled: String(input.progress.settledCount),
|
|
21248
|
+
delivered: String(input.progress.deliveredCount ?? 0),
|
|
21249
|
+
continuation: String(input.continuation)
|
|
21250
|
+
};
|
|
21251
|
+
const fill = (text) => text.replace(placeholder, (_, name) => facts[name] ?? `{${name}}`);
|
|
21252
|
+
const sections = [verdict === void 0 ? "No check verdict is available yet." + (input.canSubmitResult === true ? " Submit a result through submit_result to request the check." : "") : verdict.pass ? "The check passes on the current state, but no result has been accepted." + (input.canSubmitResult === true ? " Call submit_result to request acceptance." : "") : fill(profile.opening).trim()];
|
|
21253
|
+
const section = (heading, body) => {
|
|
21254
|
+
if (body.length === 0) return;
|
|
21255
|
+
sections.push([...heading.trim() === "" ? [] : [`## ${fill(heading)}`], ...body].join("\n"));
|
|
21256
|
+
};
|
|
21257
|
+
if (input.failures === "verbatim" && verdict !== void 0) {
|
|
21258
|
+
const lines = verdict.failures ?? [];
|
|
21259
|
+
const shown = lines.slice(0, 40);
|
|
21260
|
+
const more = lines.length - shown.length;
|
|
21261
|
+
section(profile.headings.failures, [...shown, ...more > 0 ? [input.canReadMore ? `${more} more failure lines: call read_continuation with continuation ${input.continuation}.` : `${more} more failure lines are not shown.`] : []]);
|
|
21262
|
+
const passing = passedItems(verdict);
|
|
21263
|
+
section(profile.headings.protected, passing.length === 0 ? [] : [passing.join(", ")]);
|
|
21264
|
+
if (input.previous !== void 0) {
|
|
21265
|
+
const before = new Set(failedItems(input.previous));
|
|
21266
|
+
const now = new Set(failing);
|
|
21267
|
+
const fixed = [...before].filter((item) => !now.has(item));
|
|
21268
|
+
const broke = [...now].filter((item) => !before.has(item));
|
|
21269
|
+
section(profile.headings.changed, [
|
|
21270
|
+
...fixed.length === 0 ? [] : [`Now passing: ${fixed.join(", ")}.`],
|
|
21271
|
+
...broke.length === 0 ? [] : [`Newly failing: ${broke.join(", ")}.`],
|
|
21272
|
+
...fixed.length === 0 && broke.length === 0 ? ["No item changed since the last note."] : [],
|
|
21273
|
+
...input.previous.composite !== void 0 && verdict.composite !== void 0 ? [`Composite: ${format(input.previous.composite)} then ${format(verdict.composite)}.`] : []
|
|
21274
|
+
]);
|
|
21275
|
+
}
|
|
21174
21276
|
}
|
|
21175
|
-
|
|
21176
|
-
|
|
21177
|
-
|
|
21178
|
-
|
|
21179
|
-
|
|
21180
|
-
|
|
21181
|
-
|
|
21182
|
-
|
|
21183
|
-
|
|
21184
|
-
|
|
21185
|
-
|
|
21186
|
-
}
|
|
21187
|
-
|
|
21188
|
-
|
|
21189
|
-
|
|
21190
|
-
|
|
21191
|
-
|
|
21192
|
-
|
|
21193
|
-
|
|
21194
|
-
|
|
21277
|
+
if (input.findings !== void 0) {
|
|
21278
|
+
const open = input.findings.filter((finding) => finding.resolvedAt === void 0);
|
|
21279
|
+
const resolved = input.findings.filter((finding) => finding.resolvedAt === input.continuation);
|
|
21280
|
+
section(profile.headings.findings, [...open.map((finding) => `- [hypothesis] ${finding.claim}` + (finding.items?.length ? ` (items: ${finding.items.join(", ")})` : "") + ` Evidence: ${finding.citations.map((citation) => citation.uri).join(", ")}`), ...resolved.map((finding) => `- [resolved] ${finding.claim}`)]);
|
|
21281
|
+
}
|
|
21282
|
+
if (input.bar !== void 0 && verdict !== void 0) {
|
|
21283
|
+
const body = [];
|
|
21284
|
+
if (profile.barRule?.trim()) body.push(fill(profile.barRule).trim());
|
|
21285
|
+
for (const [item, value] of Object.entries(items)) {
|
|
21286
|
+
const best = input.bar.best?.verdict.items?.[item];
|
|
21287
|
+
body.push(`- ${item}: ${value >= 1 ? "passes" : `fails (${format(value)})`}` + (best === void 0 ? "" : ` / ${input.bar.best?.label}: ${best >= 1 ? "passes" : `fails (${format(best)})`}`));
|
|
21288
|
+
}
|
|
21289
|
+
if (input.bar.best?.verdict.composite !== void 0) body.push(`Best version ${input.bar.best.label}: composite ${format(input.bar.best.verdict.composite)}.`);
|
|
21290
|
+
if (input.bar.reference?.trim()) body.push(`Reference result: ${input.bar.reference.trim()}`);
|
|
21291
|
+
if (verdict.review?.trim()) body.push(`The check's review: ${verdict.review.trim()}`);
|
|
21292
|
+
section(profile.headings.bar, body);
|
|
21293
|
+
}
|
|
21294
|
+
section(profile.headings.plan, profile.plan.trim() === "" ? [] : [fill(profile.plan).trim()]);
|
|
21295
|
+
section(profile.headings.rules, [...profile.rules.trim() === "" ? [] : [fill(profile.rules).trim()], ...input.sealed ? ["The score comes from cases you cannot see."] : []]);
|
|
21296
|
+
for (const appended of input.appended ?? []) section(appended.heading, appended.text.trim() === "" ? [] : [appended.text.trim()]);
|
|
21297
|
+
return sections.join("\n\n");
|
|
21195
21298
|
}
|
|
21196
|
-
function
|
|
21197
|
-
|
|
21198
|
-
const
|
|
21199
|
-
|
|
21200
|
-
|
|
21201
|
-
|
|
21202
|
-
|
|
21203
|
-
|
|
21204
|
-
|
|
21299
|
+
function summarizeVerdict(verdict) {
|
|
21300
|
+
if (verdict === void 0) return null;
|
|
21301
|
+
const total = Object.keys(verdict.items ?? {}).length;
|
|
21302
|
+
return {
|
|
21303
|
+
pass: verdict.pass,
|
|
21304
|
+
...verdict.composite === void 0 ? {} : { composite: verdict.composite },
|
|
21305
|
+
...total === 0 ? {} : {
|
|
21306
|
+
failed: failedItems(verdict).length,
|
|
21307
|
+
total
|
|
21308
|
+
}
|
|
21205
21309
|
};
|
|
21206
|
-
return request.run.sessionId === historicalId("retained-session") && request.run.executionId === historicalId("retained-execution");
|
|
21207
21310
|
}
|
|
21208
|
-
|
|
21209
|
-
|
|
21210
|
-
options.signal?.throwIfAborted();
|
|
21211
|
-
const ref = AgentInteractiveSessionRefSchema.parse(options.ref);
|
|
21212
|
-
if (ref.run.provider !== options.provider.name) throw new Error("interactive session reference belongs to another provider");
|
|
21213
|
-
const { environment, capabilities } = await reconstructEnvironment(options.provider, ref.run.environmentId, options.signal);
|
|
21214
|
-
if (!environment || !capabilities) return null;
|
|
21215
|
-
const handle = createRetainedInteractiveRunHandle(environment, ref, capabilities);
|
|
21216
|
-
await handle.status({ signal: options.signal });
|
|
21217
|
-
return handle;
|
|
21311
|
+
function noteDigest(note) {
|
|
21312
|
+
return `sha256:${createHash("sha256").update(note).digest("hex")}`;
|
|
21218
21313
|
}
|
|
21219
|
-
|
|
21314
|
+
/** A run directory's continuation files: the root's at `<runDir>/continuations/<n>/`, a nested
|
|
21315
|
+
* manager's at `<runDir>/continuations/managers/<owner>/<n>/`. */
|
|
21316
|
+
const CONTINUATIONS_DIR = "continuations";
|
|
21317
|
+
/**
|
|
21318
|
+
* Write one continuation's files under `<dir>/<n>/`: `note.md`, `verdict.json`, and
|
|
21319
|
+
* `panel.jsonl` when the panel ran. The director reads them through `read_continuation`, because
|
|
21320
|
+
* its box cannot read the driver's run directory.
|
|
21321
|
+
*/
|
|
21322
|
+
async function writeContinuationFiles(dir, continuation, files) {
|
|
21323
|
+
const at = join(dir, String(continuation));
|
|
21324
|
+
await mkdir(at, { recursive: true });
|
|
21325
|
+
await writeFile(join(at, "note.md"), `${files.note}\n`, "utf8");
|
|
21326
|
+
await writeFile(join(at, "verdict.json"), `${JSON.stringify(files.verdict ?? null, null, 2)}\n`, "utf8");
|
|
21327
|
+
if (files.panel !== void 0) await writeFile(join(at, "panel.jsonl"), files.panel.map((line) => JSON.stringify(line)).join("\n") + (files.panel.length ? "\n" : ""), "utf8");
|
|
21328
|
+
}
|
|
21329
|
+
/** The best composite across `reads`, or `undefined` when no read scored. */
|
|
21330
|
+
function bestComposite(reads) {
|
|
21331
|
+
let best;
|
|
21332
|
+
for (const read of reads) {
|
|
21333
|
+
const value = read.verdict?.composite;
|
|
21334
|
+
if (value !== void 0 && (best === void 0 || value > best)) best = value;
|
|
21335
|
+
}
|
|
21336
|
+
return best;
|
|
21337
|
+
}
|
|
21338
|
+
function finite(value) {
|
|
21339
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
21340
|
+
}
|
|
21341
|
+
function format(value) {
|
|
21342
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/u, "");
|
|
21343
|
+
}
|
|
21344
|
+
/** Build the continuation state for one manager with a check. */
|
|
21345
|
+
function createContinuationKeeper(input) {
|
|
21346
|
+
const { policy } = input;
|
|
21347
|
+
const failures = input.feedback === "pass-only" ? "off" : policy.failures;
|
|
21348
|
+
const entries = [];
|
|
21349
|
+
const notes = [];
|
|
21350
|
+
let findings = [];
|
|
21351
|
+
let panelUsd = 0;
|
|
21352
|
+
let panelUsdKnown = true;
|
|
21353
|
+
const latestVerdict = () => {
|
|
21354
|
+
const reads = input.reads();
|
|
21355
|
+
for (let i = reads.length - 1; i >= 0; i -= 1) {
|
|
21356
|
+
const read = reads[i];
|
|
21357
|
+
if (read?.verdict !== void 0) return {
|
|
21358
|
+
verdict: read.verdict,
|
|
21359
|
+
read: read.read
|
|
21360
|
+
};
|
|
21361
|
+
}
|
|
21362
|
+
return { read: 0 };
|
|
21363
|
+
};
|
|
21364
|
+
const closePrevious = () => {
|
|
21365
|
+
const last = entries.at(-1);
|
|
21366
|
+
const lastNote = notes.at(-1);
|
|
21367
|
+
if (last === void 0 || lastNote === void 0) return;
|
|
21368
|
+
const latest = latestVerdict();
|
|
21369
|
+
entries[entries.length - 1] = {
|
|
21370
|
+
...last,
|
|
21371
|
+
after: latest.read > lastNote.read ? summarizeVerdict(latest.verdict) : null
|
|
21372
|
+
};
|
|
21373
|
+
};
|
|
21374
|
+
return {
|
|
21375
|
+
async compose(context) {
|
|
21376
|
+
closePrevious();
|
|
21377
|
+
const continuation = entries.length + 1;
|
|
21378
|
+
const { verdict, read } = latestVerdict();
|
|
21379
|
+
const failing = verdict === void 0 ? [] : failedItems(verdict);
|
|
21380
|
+
let panelRecord;
|
|
21381
|
+
let panelLines;
|
|
21382
|
+
if (policy.panel === "on" && policy.runPanel !== void 0 && policy.panelUsd !== void 0) {
|
|
21383
|
+
const workers = input.workers();
|
|
21384
|
+
const questions = expandQuestions(policy.profile.questions ?? [], failing, workers);
|
|
21385
|
+
const left = policy.panelUsd.perRun - panelUsd;
|
|
21386
|
+
const usdCap = Math.min(policy.panelUsd.perContinuation, left);
|
|
21387
|
+
const asking = questions.length > 0 && usdCap > 0 && panelUsdKnown;
|
|
21388
|
+
let traces;
|
|
21389
|
+
let unavailable;
|
|
21390
|
+
if (asking) try {
|
|
21391
|
+
traces = await input.traces?.();
|
|
21392
|
+
if (traces === void 0) unavailable = "the run has recorded no trace yet";
|
|
21393
|
+
} catch (error) {
|
|
21394
|
+
unavailable = `the run's traces could not be read: ${error instanceof Error ? error.message : String(error)}`;
|
|
21395
|
+
}
|
|
21396
|
+
if (asking && traces !== void 0) {
|
|
21397
|
+
const result = await policy.runPanel({
|
|
21398
|
+
continuation,
|
|
21399
|
+
task: input.task,
|
|
21400
|
+
...verdict === void 0 ? {} : { verdict },
|
|
21401
|
+
reads: input.reads(),
|
|
21402
|
+
questions,
|
|
21403
|
+
traces,
|
|
21404
|
+
workers,
|
|
21405
|
+
...policy.best === void 0 ? {} : { bar: policy.best },
|
|
21406
|
+
...policy.reference === void 0 ? {} : { reference: policy.reference },
|
|
21407
|
+
usdCap,
|
|
21408
|
+
signal: context.signal
|
|
21409
|
+
});
|
|
21410
|
+
if (result.usd === null) panelUsdKnown = false;
|
|
21411
|
+
else panelUsd += result.usd;
|
|
21412
|
+
findings = distillFindings(findings, result.findings, new Set(failing), continuation);
|
|
21413
|
+
panelRecord = {
|
|
21414
|
+
asked: questions.length,
|
|
21415
|
+
proposed: result.findings.length,
|
|
21416
|
+
admitted: findings.filter((finding) => finding.admittedAt === continuation).length,
|
|
21417
|
+
usd: result.usd
|
|
21418
|
+
};
|
|
21419
|
+
panelLines = [...result.findings.map((finding) => ({
|
|
21420
|
+
kind: "finding",
|
|
21421
|
+
admitted: admitFinding(finding),
|
|
21422
|
+
...finding
|
|
21423
|
+
})), ...result.receipts === void 0 ? [] : [{
|
|
21424
|
+
kind: "receipts",
|
|
21425
|
+
receipts: result.receipts
|
|
21426
|
+
}]];
|
|
21427
|
+
} else panelRecord = {
|
|
21428
|
+
asked: 0,
|
|
21429
|
+
proposed: 0,
|
|
21430
|
+
admitted: 0,
|
|
21431
|
+
usd: 0,
|
|
21432
|
+
...unavailable === void 0 ? {} : { unavailable }
|
|
21433
|
+
};
|
|
21434
|
+
}
|
|
21435
|
+
const appended = policy.append ? await policy.append({
|
|
21436
|
+
continuation,
|
|
21437
|
+
...verdict === void 0 ? {} : { verdict },
|
|
21438
|
+
reads: input.reads(),
|
|
21439
|
+
signal: context.signal
|
|
21440
|
+
}) : void 0;
|
|
21441
|
+
const note = composeContinuationNote({
|
|
21442
|
+
profile: policy.profile,
|
|
21443
|
+
continuation,
|
|
21444
|
+
...verdict === void 0 ? {} : { verdict },
|
|
21445
|
+
...notes.at(-1)?.verdict === void 0 ? {} : { previous: notes.at(-1)?.verdict },
|
|
21446
|
+
reads: input.reads().length,
|
|
21447
|
+
failures,
|
|
21448
|
+
...policy.panel === "on" ? { findings } : {},
|
|
21449
|
+
...policy.bar === "on" ? { bar: {
|
|
21450
|
+
...policy.best === void 0 ? {} : { best: policy.best },
|
|
21451
|
+
...policy.reference === void 0 ? {} : { reference: policy.reference }
|
|
21452
|
+
} } : {},
|
|
21453
|
+
...appended === void 0 ? {} : { appended: [appended] },
|
|
21454
|
+
...input.owed === void 0 ? {} : { owed: input.owed },
|
|
21455
|
+
progress: context.progress,
|
|
21456
|
+
canReadMore: input.canReadMore,
|
|
21457
|
+
canSubmitResult: input.canSubmitResult === true,
|
|
21458
|
+
...input.sealed === true ? { sealed: true } : {}
|
|
21459
|
+
});
|
|
21460
|
+
notes.push({
|
|
21461
|
+
note,
|
|
21462
|
+
...verdict === void 0 ? {} : { verdict },
|
|
21463
|
+
...panelLines === void 0 ? {} : { panel: panelLines },
|
|
21464
|
+
read
|
|
21465
|
+
});
|
|
21466
|
+
entries.push({
|
|
21467
|
+
continuation,
|
|
21468
|
+
attempt: context.attempt,
|
|
21469
|
+
noteDigest: noteDigest(note),
|
|
21470
|
+
profile: policy.profile.id,
|
|
21471
|
+
switches: {
|
|
21472
|
+
failures,
|
|
21473
|
+
panel: policy.panel,
|
|
21474
|
+
bar: policy.bar
|
|
21475
|
+
},
|
|
21476
|
+
before: summarizeVerdict(verdict),
|
|
21477
|
+
after: null,
|
|
21478
|
+
...panelRecord === void 0 ? {} : { panel: panelRecord },
|
|
21479
|
+
appended: appended === void 0 ? 0 : 1
|
|
21480
|
+
});
|
|
21481
|
+
if (input.dir !== void 0) await writeContinuationFiles(input.dir, continuation, {
|
|
21482
|
+
note,
|
|
21483
|
+
...verdict === void 0 ? {} : { verdict },
|
|
21484
|
+
...panelLines === void 0 ? {} : { panel: panelLines }
|
|
21485
|
+
});
|
|
21486
|
+
return note;
|
|
21487
|
+
},
|
|
21488
|
+
read(continuation) {
|
|
21489
|
+
const index = continuation === void 0 ? notes.length - 1 : continuation - 1;
|
|
21490
|
+
const entry = notes[index];
|
|
21491
|
+
if (entry === void 0) return {
|
|
21492
|
+
found: false,
|
|
21493
|
+
reason: notes.length === 0 ? "no continuation has been sent in this run" : `continuations run from 1 to ${notes.length}`
|
|
21494
|
+
};
|
|
21495
|
+
return {
|
|
21496
|
+
found: true,
|
|
21497
|
+
continuation: index + 1,
|
|
21498
|
+
note: entry.note,
|
|
21499
|
+
verdict: failures === "off" && entry.verdict !== void 0 ? { pass: entry.verdict.pass } : entry.verdict ?? null,
|
|
21500
|
+
findings: findings.filter((finding) => finding.admittedAt <= index + 1)
|
|
21501
|
+
};
|
|
21502
|
+
},
|
|
21503
|
+
entries() {
|
|
21504
|
+
closePrevious();
|
|
21505
|
+
return entries.map((entry) => ({ ...entry }));
|
|
21506
|
+
}
|
|
21507
|
+
};
|
|
21508
|
+
}
|
|
21509
|
+
//#endregion
|
|
21510
|
+
//#region src/runtime/supervise/completion-gate.ts
|
|
21511
|
+
/**
|
|
21512
|
+
*
|
|
21513
|
+
* The completion-oracle: **settled ⟺ DELIVERED.**
|
|
21514
|
+
*
|
|
21515
|
+
* Foreman's one hard lesson (0/18 self-improvement deliverables) — "done" must mean a check
|
|
21516
|
+
* PASSED, not the agent's say-so. `gateOnDeliverable` wraps an `Executor` so its settlement
|
|
21517
|
+
* is `valid` ONLY when the deliverable check passes. The child still RUNS and settles (its
|
|
21518
|
+
* spend is conserved into the pool either way), but a child that ran WITHOUT delivering
|
|
21519
|
+
* settles `valid:false` — so a keep-best driver never counts it as done, and a gate never
|
|
21520
|
+
* inflates with self-judged wins.
|
|
21521
|
+
*
|
|
21522
|
+
* Dual-purpose by construction:
|
|
21523
|
+
* - product: the agent fleet only advances on real, checked deliverables.
|
|
21524
|
+
* - proof: the gate's `valid` is the honest settle — equal-k comparisons can't be gamed by an
|
|
21525
|
+
* arm that "ran" without producing the artifact.
|
|
21526
|
+
*
|
|
21527
|
+
* The check is a DEPLOYABLE oracle — a test command, a state verifier, the commit0 judge —
|
|
21528
|
+
* read off the child's output, never the model judging itself. A throwing check is
|
|
21529
|
+
* fail-closed (not delivered), never a crash.
|
|
21530
|
+
*
|
|
21531
|
+
* @experimental
|
|
21532
|
+
*/
|
|
21533
|
+
/**
|
|
21534
|
+
* Wrap an `Executor` so its settlement `valid` reflects the deliverable check, not the
|
|
21535
|
+
* inner verdict. Handles both `execute` shapes (one-shot `Promise<ExecutorResult>` and
|
|
21536
|
+
* streaming `AsyncIterable<UsageEvent>` + `resultArtifact()`); the check runs once the inner
|
|
21537
|
+
* executor has produced its output. The inner `score` is preserved; only `valid` is gated.
|
|
21538
|
+
*/
|
|
21539
|
+
function gateOnDeliverable(inner, deliverable) {
|
|
21540
|
+
let gated;
|
|
21541
|
+
const check = async (out, baseScore) => {
|
|
21542
|
+
let delivered;
|
|
21543
|
+
try {
|
|
21544
|
+
delivered = checkVerdictOf(await deliverable.check(out)).pass;
|
|
21545
|
+
} catch {
|
|
21546
|
+
delivered = false;
|
|
21547
|
+
}
|
|
21548
|
+
return {
|
|
21549
|
+
valid: delivered,
|
|
21550
|
+
score: baseScore ?? (delivered ? 1 : 0)
|
|
21551
|
+
};
|
|
21552
|
+
};
|
|
21553
|
+
/**
|
|
21554
|
+
* Ask the delivery question once, from whatever the inner executor managed to produce.
|
|
21555
|
+
*
|
|
21556
|
+
* Fail-closed on the artifact being unavailable: an executor that never produced one delivered
|
|
21557
|
+
* nothing, and leaving `gated` unset keeps the existing invalid-by-default reading.
|
|
21558
|
+
*/
|
|
21559
|
+
const settleVerdict = async () => {
|
|
21560
|
+
let art;
|
|
21561
|
+
try {
|
|
21562
|
+
art = inner.resultArtifact();
|
|
21563
|
+
} catch {
|
|
21564
|
+
return;
|
|
21565
|
+
}
|
|
21566
|
+
gated = await check(art.out, art.verdict?.score);
|
|
21567
|
+
};
|
|
21568
|
+
const gateExecution = (r) => {
|
|
21569
|
+
if (isAsyncIterable$1(r)) return (async function* () {
|
|
21570
|
+
try {
|
|
21571
|
+
for await (const ev of r) yield ev;
|
|
21572
|
+
} finally {
|
|
21573
|
+
await settleVerdict();
|
|
21574
|
+
}
|
|
21575
|
+
})();
|
|
21576
|
+
return (async () => {
|
|
21577
|
+
let res;
|
|
21578
|
+
try {
|
|
21579
|
+
res = await r;
|
|
21580
|
+
} catch (error) {
|
|
21581
|
+
await settleVerdict();
|
|
21582
|
+
throw error;
|
|
21583
|
+
}
|
|
21584
|
+
gated = await check(res.out, res.verdict?.score);
|
|
21585
|
+
return {
|
|
21586
|
+
...res,
|
|
21587
|
+
verdict: gated
|
|
21588
|
+
};
|
|
21589
|
+
})();
|
|
21590
|
+
};
|
|
21591
|
+
return inheritRuntimeOwnedExecutorAttestation(inner, {
|
|
21592
|
+
runtime: inner.runtime,
|
|
21593
|
+
...inner.budgetExempt !== void 0 ? { budgetExempt: inner.budgetExempt } : {},
|
|
21594
|
+
...inner.deliver ? { deliver: (m) => inner.deliver?.(m) } : {},
|
|
21595
|
+
...inner.progress ? { progress: () => inner.progress?.() } : {},
|
|
21596
|
+
...inner.traceSource ? { traceSource: () => inner.traceSource?.() } : {},
|
|
21597
|
+
...inner.metered ? { metered: () => inner.metered?.() } : {},
|
|
21598
|
+
...inner.accounting ? { accounting: () => inner.accounting?.() } : {},
|
|
21599
|
+
...inner.harnessTranscript ? { harnessTranscript: () => inner.harnessTranscript?.() } : {},
|
|
21600
|
+
...inner.harnessTranscriptSettled ? { harnessTranscriptSettled: () => inner.harnessTranscriptSettled?.() ?? Promise.resolve() } : {},
|
|
21601
|
+
execute: (task, signal) => gateExecution(inner.execute(task, signal)),
|
|
21602
|
+
...inner.recover ? { recover: (task, signal) => gateExecution(inner.recover(task, signal)) } : {},
|
|
21603
|
+
teardown: (grace) => inner.teardown(grace),
|
|
21604
|
+
...teardownSurfaces(inner),
|
|
21605
|
+
resultArtifact() {
|
|
21606
|
+
const art = inner.resultArtifact();
|
|
21607
|
+
return {
|
|
21608
|
+
...art,
|
|
21609
|
+
verdict: gated ?? art.verdict
|
|
21610
|
+
};
|
|
21611
|
+
}
|
|
21612
|
+
});
|
|
21613
|
+
}
|
|
21614
|
+
/**
|
|
21615
|
+
* Transform a Runtime executor's terminal artifact without losing its private
|
|
21616
|
+
* profile-materialization attestation or altering its measured spend. This is
|
|
21617
|
+
* the composition point for deterministic post-processing and grading; callers
|
|
21618
|
+
* must not rebuild an Executor around a model transport merely to change `out`.
|
|
21619
|
+
*/
|
|
21620
|
+
function mapExecutorResult(inner, map) {
|
|
21621
|
+
let mapped;
|
|
21622
|
+
const settle = async (result, task) => {
|
|
21623
|
+
const transformed = await map(result, task);
|
|
21624
|
+
mapped = {
|
|
21625
|
+
outRef: transformed.outRef,
|
|
21626
|
+
out: transformed.out,
|
|
21627
|
+
...transformed.verdict ? { verdict: transformed.verdict } : {},
|
|
21628
|
+
spent: result.spent
|
|
21629
|
+
};
|
|
21630
|
+
return mapped;
|
|
21631
|
+
};
|
|
21632
|
+
return inheritRuntimeOwnedExecutorAttestation(inner, {
|
|
21633
|
+
runtime: inner.runtime,
|
|
21634
|
+
...inner.budgetExempt !== void 0 ? { budgetExempt: inner.budgetExempt } : {},
|
|
21635
|
+
...inner.deliver ? { deliver: (message) => inner.deliver?.(message) } : {},
|
|
21636
|
+
...inner.progress ? { progress: () => inner.progress?.() } : {},
|
|
21637
|
+
...inner.traceSource ? { traceSource: () => inner.traceSource?.() } : {},
|
|
21638
|
+
...inner.accounting ? { accounting: () => inner.accounting?.() } : {},
|
|
21639
|
+
...inner.metered ? { metered: () => inner.metered?.() } : {},
|
|
21640
|
+
...inner.harnessTranscript ? { harnessTranscript: () => inner.harnessTranscript?.() } : {},
|
|
21641
|
+
...inner.harnessTranscriptSettled ? { harnessTranscriptSettled: () => inner.harnessTranscriptSettled?.() ?? Promise.resolve() } : {},
|
|
21642
|
+
execute(task, signal) {
|
|
21643
|
+
const execution = inner.execute(task, signal);
|
|
21644
|
+
if (isAsyncIterable$1(execution)) return (async function* () {
|
|
21645
|
+
for await (const event of execution) yield event;
|
|
21646
|
+
await settle(inner.resultArtifact(), task);
|
|
21647
|
+
})();
|
|
21648
|
+
return (async () => settle(await execution, task))();
|
|
21649
|
+
},
|
|
21650
|
+
teardown: (grace) => inner.teardown(grace),
|
|
21651
|
+
...teardownSurfaces(inner),
|
|
21652
|
+
resultArtifact() {
|
|
21653
|
+
if (!mapped) throw new Error("mapExecutorResult: resultArtifact() read before execute()");
|
|
21654
|
+
return mapped;
|
|
21655
|
+
}
|
|
21656
|
+
});
|
|
21657
|
+
}
|
|
21658
|
+
//#endregion
|
|
21659
|
+
//#region src/runtime/supervise/dispatch.ts
|
|
21660
|
+
/**
|
|
21661
|
+
*
|
|
21662
|
+
* `rollingDispatch` — the REFILLING dispatch policy over a `Scope`. It keeps `width` children in
|
|
21663
|
+
* flight and admits the next queued unit of work the instant one settles, instead of waiting for
|
|
21664
|
+
* a whole round to drain (`fanout`) or for a driver to decide again (`spawn → await → spawn`).
|
|
21665
|
+
*
|
|
21666
|
+
* The gap it closes: `fanout` opens every item at once and then drains — one round, no refill —
|
|
21667
|
+
* and a driver brain's manual loop opens one worker per model turn. Between those two there was
|
|
21668
|
+
* nothing that holds N slots full, which is why a 5-worker run can peak at 2 live workers and sit
|
|
21669
|
+
* ~50% idle. This is a policy over the EXISTING `Scope.spawn` / `Scope.next` primitives: it adds
|
|
21670
|
+
* no second admission path, so the conserved budget pool stays the only fence on total work and
|
|
21671
|
+
* `width` is only a fence on simultaneous work.
|
|
21672
|
+
*
|
|
21673
|
+
* Fail-closed by construction: an admission rejection (`budget-exhausted` / `depth-exceeded`) is
|
|
21674
|
+
* recorded and STOPS further admission — the loop then drains what is already live and returns.
|
|
21675
|
+
* It never retries a rejected spawn against the same pool, and it never spawns past `width`.
|
|
21676
|
+
*
|
|
21677
|
+
* ── Concurrency ────────────────────────────────────────────────────────────────────────────────
|
|
21678
|
+
*
|
|
21679
|
+
* `width` bounds the children THIS dispatcher holds in flight. The tree-wide bound on working
|
|
21680
|
+
* agents is `SuperviseOptions.workerSlots` (`./worker-slots`): a spawn past it is queued by the
|
|
21681
|
+
* scope, not refused, so a `width` above the free slots only lengthens the allocator's queue.
|
|
21682
|
+
* `SandboxLineage`'s fork concurrency bounds boxes inside ONE leaf's fork wave, a different unit.
|
|
21683
|
+
*
|
|
21684
|
+
* ── Why this is not a copy of the kernel's batch loop ──────────────────────────────────────────
|
|
21685
|
+
*
|
|
21686
|
+
* `runBatch` (`src/runtime/run-loop.ts`) runs the same hold-N-slots-full shape over the KERNEL's
|
|
21687
|
+
* substrate: bare promises in a `Set`, raced with `Promise.race`, bounded by `maxConcurrency`.
|
|
21688
|
+
* This one runs it over the SUPERVISOR's substrate — `Scope.spawn`'s atomic reservation against
|
|
21689
|
+
* the conserved pool, `Scope.next`'s journaled settlement cursor, and the blob store behind each
|
|
21690
|
+
* result. Neither can be expressed in the other's terms without dragging its whole substrate
|
|
21691
|
+
* along: the kernel loop has no budget to reserve and no cursor to order settlements by, and this
|
|
21692
|
+
* one cannot race raw promises because a settlement is only real once it is journaled. The shape
|
|
21693
|
+
* repeating across the two deliberate layers is resonance, not duplication — do not "unify" them
|
|
21694
|
+
* into a shared helper that would have to know about both.
|
|
21695
|
+
*
|
|
21696
|
+
* @experimental
|
|
21697
|
+
*/
|
|
21698
|
+
/**
|
|
21699
|
+
* Run the refilling dispatch loop over `scope` until the queue is dry (or a stop fires) and every
|
|
21700
|
+
* admitted child has settled. Returns the settlements in cursor order plus the admission ledger.
|
|
21701
|
+
*
|
|
21702
|
+
* The loop is: fill free slots from `nextUnit` → `await scope.next()` → deliver the settlement →
|
|
21703
|
+
* refill → repeat. Because the refill happens immediately after each settlement rather than after
|
|
21704
|
+
* a whole round, a slow child never idles the other slots.
|
|
21705
|
+
*/
|
|
21706
|
+
async function rollingDispatch(scope, opts) {
|
|
21707
|
+
if (!Number.isInteger(opts.width) || opts.width < 1) throw new ValidationError$1(`rollingDispatch: width must be a positive integer, got ${String(opts.width)}`);
|
|
21708
|
+
const settled = [];
|
|
21709
|
+
const rejected = [];
|
|
21710
|
+
const live = /* @__PURE__ */ new Set();
|
|
21711
|
+
let admitted = 0;
|
|
21712
|
+
let peakLive = 0;
|
|
21713
|
+
let queueDry = false;
|
|
21714
|
+
let stopReason = "drained";
|
|
21715
|
+
const fill = async () => {
|
|
21716
|
+
while (live.size < opts.width) {
|
|
21717
|
+
if (queueDry) return;
|
|
21718
|
+
if (scope.signal.aborted) {
|
|
21719
|
+
stopReason = "aborted";
|
|
21720
|
+
queueDry = true;
|
|
21721
|
+
return;
|
|
21722
|
+
}
|
|
21723
|
+
if (opts.shouldStop?.() === true) {
|
|
21724
|
+
stopReason = "stopped";
|
|
21725
|
+
queueDry = true;
|
|
21726
|
+
return;
|
|
21727
|
+
}
|
|
21728
|
+
const unit = await opts.nextUnit();
|
|
21729
|
+
if (unit === void 0) {
|
|
21730
|
+
queueDry = true;
|
|
21731
|
+
return;
|
|
21732
|
+
}
|
|
21733
|
+
const res = scope.spawn(unit.agent, unit.task, unit.opts);
|
|
21734
|
+
if (!res.ok) {
|
|
21735
|
+
rejected.push(`${unit.opts.label}: ${res.reason}`);
|
|
21736
|
+
stopReason = "not-admitted";
|
|
21737
|
+
queueDry = true;
|
|
21738
|
+
return;
|
|
21739
|
+
}
|
|
21740
|
+
live.add(res.handle.id);
|
|
21741
|
+
admitted += 1;
|
|
21742
|
+
if (live.size > peakLive) peakLive = live.size;
|
|
21743
|
+
}
|
|
21744
|
+
};
|
|
21745
|
+
await fill();
|
|
21746
|
+
while (live.size > 0) {
|
|
21747
|
+
const s = await scope.next();
|
|
21748
|
+
if (s === null) break;
|
|
21749
|
+
live.delete(s.handle.id);
|
|
21750
|
+
settled.push(s);
|
|
21751
|
+
await opts.onSettled?.(s);
|
|
21752
|
+
await fill();
|
|
21753
|
+
}
|
|
21754
|
+
return {
|
|
21755
|
+
settled,
|
|
21756
|
+
admitted,
|
|
21757
|
+
rejected,
|
|
21758
|
+
stopReason,
|
|
21759
|
+
peakLive
|
|
21760
|
+
};
|
|
21761
|
+
}
|
|
21762
|
+
/**
|
|
21763
|
+
* Free worker slots under a simultaneity cap: `cap - live`, floored at 0, or `null` when there is
|
|
21764
|
+
* no cap (the conserved pool is then the only fence and "free slots" is not a finite number).
|
|
21765
|
+
* The one place the answer is computed, so the driver-facing tool payload and a dispatcher agree.
|
|
21766
|
+
*/
|
|
21767
|
+
function freeSlots(liveCount, cap) {
|
|
21768
|
+
if (cap === void 0 || cap <= 0) return null;
|
|
21769
|
+
return Math.max(0, cap - liveCount);
|
|
21770
|
+
}
|
|
21771
|
+
/** Convenience: a `DispatchUnit` factory over a fixed array of tasks, for the common case where
|
|
21772
|
+
* the queue is known up front and only the refill behavior is wanted. */
|
|
21773
|
+
function queueOf(units, budget) {
|
|
21774
|
+
let i = 0;
|
|
21775
|
+
return () => {
|
|
21776
|
+
const u = units[i];
|
|
21777
|
+
if (u === void 0) return void 0;
|
|
21778
|
+
i += 1;
|
|
21779
|
+
return {
|
|
21780
|
+
agent: u.agent,
|
|
21781
|
+
task: u.task,
|
|
21782
|
+
opts: {
|
|
21783
|
+
budget,
|
|
21784
|
+
label: u.label
|
|
21785
|
+
}
|
|
21786
|
+
};
|
|
21787
|
+
};
|
|
21788
|
+
}
|
|
21789
|
+
//#endregion
|
|
21790
|
+
//#region src/runtime/retained-interactive-handle.ts
|
|
21791
|
+
function createRetainedInteractiveRunHandle(environment, inputRef, capabilities, requestedStart) {
|
|
21792
|
+
const ref = freezeInteractiveRef(inputRef);
|
|
21793
|
+
const source = environment.interactive(ref);
|
|
21794
|
+
if (!sameInteractiveRef(source.ref, ref)) throw new Error("provider reconstructed a different interactive process");
|
|
21795
|
+
if (!source.sendPrompt) throw new Error("provider declared interactive prompt support but exposed no prompt method");
|
|
21796
|
+
return Object.freeze({
|
|
21797
|
+
ref,
|
|
21798
|
+
capabilities,
|
|
21799
|
+
claimControl: async (request, options) => exactClaim(ref, request, await awaitAbortable$1(Promise.resolve().then(() => source.claimControl(request, options)), options?.signal)),
|
|
21800
|
+
status: async (options) => exactStatus(ref, await awaitAbortable$1(Promise.resolve().then(() => source.status(options)), options?.signal), requestedStart),
|
|
21801
|
+
attach: async (request, options) => exactTerminal(ref, request, await awaitAbortable$1(Promise.resolve().then(() => source.attach(request, options)), options?.signal)),
|
|
21802
|
+
sendPrompt: async (command, options) => exactPrompt(ref, await awaitAbortable$1(Promise.resolve().then(() => source.sendPrompt(command, options)), options?.signal), command),
|
|
21803
|
+
stop: async (command, options) => exactStop(ref, await awaitAbortable$1(Promise.resolve().then(() => source.stop(command, options)), options?.signal), command)
|
|
21804
|
+
});
|
|
21805
|
+
}
|
|
21806
|
+
function freezeInteractiveRef(value) {
|
|
21807
|
+
return detachedSnapshot(AgentInteractiveSessionRefSchema.parse(value), "interactive session ref");
|
|
21808
|
+
}
|
|
21809
|
+
function exactClaim(ref, request, value) {
|
|
21810
|
+
const acknowledgement = parseProviderAcknowledgement(AgentInteractiveSessionControlClaimAcknowledgementSchema, value, "control claim");
|
|
21811
|
+
if (!agentInteractiveSessionControlClaimAcknowledgementMatchesRequest(request, acknowledgement)) throw new RetainedRunProviderContractError("provider returned a control claim acknowledgement for another request");
|
|
21812
|
+
if (acknowledgement.control && !agentInteractiveSessionControlClaimMatchesRef(ref, acknowledgement.control)) throw new RetainedRunProviderContractError("provider returned a control claim for another interactive process");
|
|
21813
|
+
return acknowledgement;
|
|
21814
|
+
}
|
|
21815
|
+
function exactPrompt(ref, value, command) {
|
|
21816
|
+
const acknowledgement = parseProviderAcknowledgement(AgentInteractiveSessionPromptAcknowledgementSchema, value, "prompt");
|
|
21817
|
+
if (!agentInteractiveSessionPromptAcknowledgementMatchesCommand(command, acknowledgement)) throw new RetainedRunProviderContractError("provider returned a prompt acknowledgement for another request");
|
|
21818
|
+
if (!agentInteractiveSessionControlClaimMatchesRef(ref, acknowledgement.control)) throw new RetainedRunProviderContractError("provider returned a prompt acknowledgement for another interactive process");
|
|
21819
|
+
return acknowledgement;
|
|
21820
|
+
}
|
|
21821
|
+
function exactStop(ref, value, command) {
|
|
21822
|
+
const acknowledgement = parseProviderAcknowledgement(AgentInteractiveSessionStopAcknowledgementSchema, value, "stop");
|
|
21823
|
+
if (!agentInteractiveSessionStopAcknowledgementMatchesCommand(command, acknowledgement)) throw new RetainedRunProviderContractError("provider returned a stop acknowledgement for another request");
|
|
21824
|
+
if (!agentInteractiveSessionControlClaimMatchesRef(ref, acknowledgement.control)) throw new RetainedRunProviderContractError("provider returned a stop acknowledgement for another interactive process");
|
|
21825
|
+
return acknowledgement;
|
|
21826
|
+
}
|
|
21827
|
+
function parseProviderAcknowledgement(schema, value, operation) {
|
|
21828
|
+
try {
|
|
21829
|
+
return schema.parse(value);
|
|
21830
|
+
} catch {
|
|
21831
|
+
throw new RetainedRunProviderContractError(`provider returned an invalid interactive ${operation} acknowledgement`);
|
|
21832
|
+
}
|
|
21833
|
+
}
|
|
21834
|
+
function exactStatus(ref, value, requestedStart) {
|
|
21835
|
+
let status;
|
|
21836
|
+
try {
|
|
21837
|
+
status = AgentInteractiveSessionStatusSchema.parse(value);
|
|
21838
|
+
} catch (error) {
|
|
21839
|
+
if (requestedStart === void 0) throw error;
|
|
21840
|
+
throw new RetainedInteractiveBindingError(detachedSnapshot(requestedStart, "interactive status binding request"), {}, { cause: error });
|
|
21841
|
+
}
|
|
21842
|
+
if (!agentInteractiveSessionStatusMatchesRef(ref, status)) {
|
|
21843
|
+
if (requestedStart !== void 0) throw new RetainedInteractiveBindingError(detachedSnapshot(requestedStart, "interactive status binding request"), detachedSnapshot({ status: detachedSnapshot(status, "interactive provider status") }, "interactive provider status result"), { cause: /* @__PURE__ */ new Error("provider returned status for another interactive process") });
|
|
21844
|
+
throw new Error("provider returned status for another interactive process");
|
|
21845
|
+
}
|
|
21846
|
+
return status;
|
|
21847
|
+
}
|
|
21848
|
+
function exactTerminal(ref, request, terminal) {
|
|
21849
|
+
const terminalRef = TerminalSessionRefSchema.parse(terminal.ref);
|
|
21850
|
+
TerminalReplayWindowSchema.parse(terminal.cursors);
|
|
21851
|
+
if (terminalRef.parentExecutionId !== ref.run.executionId) throw new Error("provider attached a terminal from another interactive run");
|
|
21852
|
+
AgentInteractiveSessionControlClaimSchema.parse(terminal.control);
|
|
21853
|
+
if (!agentInteractiveSessionControlClaimMatchesRef(ref, terminal.control)) throw new Error("provider attached a terminal with a claim for another interactive process");
|
|
21854
|
+
if (canonicalCandidateDigest(terminal.control) !== canonicalCandidateDigest(request.control)) throw new Error("provider attached a terminal with a different interactive control claim");
|
|
21855
|
+
return terminal;
|
|
21856
|
+
}
|
|
21857
|
+
function sameInteractiveRef(left, right) {
|
|
21858
|
+
return canonicalCandidateDigest(left) === canonicalCandidateDigest(right);
|
|
21859
|
+
}
|
|
21860
|
+
//#endregion
|
|
21861
|
+
//#region src/runtime/retained-interactive.ts
|
|
21862
|
+
/**
|
|
21863
|
+
* Start one retry-safe native coding-agent TUI without dispatching a headless turn.
|
|
21864
|
+
* The intent admission is durable before provider.create; the environment and
|
|
21865
|
+
* process admissions follow only after their exact provider coordinates exist.
|
|
21866
|
+
* @stable
|
|
21867
|
+
*/
|
|
21868
|
+
async function startRetainedInteractiveRun(options) {
|
|
21869
|
+
options.signal?.throwIfAborted();
|
|
21870
|
+
const startOptions = options;
|
|
21871
|
+
assertStableText(startOptions.environment.idempotencyKey, "environment idempotency key");
|
|
21872
|
+
assertStableText(startOptions.interactiveIdempotencyKey, "interactive idempotency key");
|
|
21873
|
+
if (typeof startOptions.onAdmission !== "function") throw new Error("startRetainedInteractiveRun requires an awaited onAdmission durability hook");
|
|
21874
|
+
if (!startOptions.provider.get) throw new Error(`provider "${startOptions.provider.name}" cannot reconstruct an environment by id`);
|
|
21875
|
+
const profile = agentProfileSchema.parse(startOptions.environment.profile);
|
|
21876
|
+
if (profile.harness === void 0) throw new Error("retained interactive runs require AgentProfile.harness");
|
|
21877
|
+
const requestedProfileDigest = canonicalAgentProfileDigest(profile);
|
|
21878
|
+
const identity = startOptions.intent === void 0 ? mintRetainedIdentity(startOptions.environment.idempotencyKey, startOptions.interactiveIdempotencyKey) : {
|
|
21879
|
+
sessionId: startOptions.intent.sessionId,
|
|
21880
|
+
executionId: startOptions.intent.executionId
|
|
21881
|
+
};
|
|
21882
|
+
assertStableText(identity.sessionId, "retained session id");
|
|
21883
|
+
assertStableText(identity.executionId, "retained execution id");
|
|
21884
|
+
const intent = interactiveIntent(startOptions, profile, identity);
|
|
21885
|
+
if (startOptions.intent === void 0) await admitDurably(startOptions.onAdmission, intent);
|
|
21886
|
+
else assertExactInteractiveIntent(startOptions.intent, intent);
|
|
21887
|
+
const providerCapabilities = AgentEnvironmentCapabilitiesSchema.parse(await awaitAbortable$1(Promise.resolve().then(() => startOptions.provider.capabilities()), startOptions.signal));
|
|
21888
|
+
assertInteractiveCapabilities(startOptions.provider.name, providerCapabilities);
|
|
21889
|
+
const environment = await createInteractiveEnvironment(() => startOptions.provider.create({
|
|
21890
|
+
...startOptions.environment,
|
|
21891
|
+
profile,
|
|
21892
|
+
signal: startOptions.signal,
|
|
21893
|
+
metadata: retainedEnvironmentMetadata(startOptions.environment.metadata, startOptions.environment.idempotencyKey)
|
|
21894
|
+
}), startOptions.signal);
|
|
21895
|
+
let capabilities;
|
|
21896
|
+
try {
|
|
21897
|
+
assertExactEnvironment(startOptions.provider.name, environment);
|
|
21898
|
+
startOptions.signal?.throwIfAborted();
|
|
21899
|
+
capabilities = interactiveCapabilitiesForEnvironment(startOptions.provider.name, providerCapabilities, environment);
|
|
21900
|
+
assertInteractiveMethods(startOptions.provider.name, environment);
|
|
21901
|
+
} catch (error) {
|
|
21902
|
+
await destroyUnusedEnvironment(environment, error);
|
|
21903
|
+
throw error;
|
|
21904
|
+
}
|
|
21905
|
+
const request = interactiveRequest(startOptions, environment, profile, requestedProfileDigest, identity);
|
|
21906
|
+
await admitDurably(startOptions.onAdmission, {
|
|
21907
|
+
phase: "interactive_environment",
|
|
21908
|
+
provider: startOptions.provider.name,
|
|
21909
|
+
environmentId: environment.id,
|
|
21910
|
+
idempotencyKey: startOptions.environment.idempotencyKey,
|
|
21911
|
+
interactiveIdempotencyKey: startOptions.interactiveIdempotencyKey,
|
|
21912
|
+
request
|
|
21913
|
+
});
|
|
21914
|
+
const ref = exactStartedRef(request, await startInteractiveProcess(environment, () => environment.startInteractive(request, { signal: startOptions.signal }), startOptions.signal));
|
|
21915
|
+
await admitDurably(startOptions.onAdmission, {
|
|
21916
|
+
phase: "interactive_started",
|
|
21917
|
+
idempotencyKey: startOptions.environment.idempotencyKey,
|
|
21918
|
+
interactiveIdempotencyKey: startOptions.interactiveIdempotencyKey,
|
|
21919
|
+
ref
|
|
21920
|
+
});
|
|
21921
|
+
return createRetainedInteractiveRunHandle(environment, ref, capabilities, request);
|
|
21922
|
+
}
|
|
21923
|
+
/**
|
|
21924
|
+
* Retry one exact start after its provider response may have been lost.
|
|
21925
|
+
*
|
|
21926
|
+
* Resolves to the handle of the admitted process (resumed through the exact,
|
|
21927
|
+
* idempotent start request), or `null` when the provider no longer holds the
|
|
21928
|
+
* environment (not found; nothing remains to stop). @stable
|
|
21929
|
+
*/
|
|
21930
|
+
async function recoverRetainedInteractiveRun(options) {
|
|
21931
|
+
options.signal?.throwIfAborted();
|
|
21932
|
+
if (typeof options.onAdmission !== "function") throw new Error("recoverRetainedInteractiveRun requires an awaited onAdmission durability hook");
|
|
21933
|
+
const admission = options.admission;
|
|
21934
|
+
if (admission.provider !== options.provider.name) throw new Error("interactive admission belongs to another provider");
|
|
21935
|
+
if (admission.phase === "interactive_intent") {
|
|
21936
|
+
if (options.replay === void 0) throw new Error("recoverRetainedInteractiveRun requires the original start material for an interactive intent");
|
|
21937
|
+
return startRetainedInteractiveRun({
|
|
21938
|
+
provider: options.provider,
|
|
21939
|
+
...options.replay,
|
|
21940
|
+
intent: admission,
|
|
21941
|
+
onAdmission: options.onAdmission,
|
|
21942
|
+
signal: options.signal
|
|
21943
|
+
});
|
|
21944
|
+
}
|
|
21945
|
+
const request = exactRecoveryRequest(admission);
|
|
21946
|
+
const { environment, capabilities } = await reconstructEnvironment(options.provider, admission.environmentId, options.signal);
|
|
21947
|
+
if (!environment || !capabilities) return null;
|
|
21948
|
+
const ref = exactStartedRef(request, await awaitAbortable$1(Promise.resolve().then(() => environment.startInteractive(request, { signal: options.signal })), options.signal));
|
|
21949
|
+
await admitDurably(options.onAdmission, {
|
|
21950
|
+
phase: "interactive_started",
|
|
21951
|
+
idempotencyKey: admission.idempotencyKey,
|
|
21952
|
+
interactiveIdempotencyKey: admission.interactiveIdempotencyKey,
|
|
21953
|
+
ref
|
|
21954
|
+
});
|
|
21955
|
+
return createRetainedInteractiveRunHandle(environment, ref, capabilities, request);
|
|
21956
|
+
}
|
|
21957
|
+
function exactRecoveryRequest(admission) {
|
|
21958
|
+
assertStableText(admission.environmentId, "interactive environment id");
|
|
21959
|
+
assertStableText(admission.idempotencyKey, "environment idempotency key");
|
|
21960
|
+
assertStableText(admission.interactiveIdempotencyKey, "interactive idempotency key");
|
|
21961
|
+
const request = exactAgentInteractiveSessionStart(admission.request);
|
|
21962
|
+
const identity = mintRetainedIdentity(admission.idempotencyKey, admission.interactiveIdempotencyKey);
|
|
21963
|
+
if (request.run.provider !== admission.provider || request.run.environmentId !== admission.environmentId || (request.run.sessionId !== identity.sessionId || request.run.executionId !== identity.executionId) && !matchesHistoricalInteractiveIdentity(admission, request)) throw new Error("interactive admission does not match its recovery coordinates");
|
|
21964
|
+
return request;
|
|
21965
|
+
}
|
|
21966
|
+
function matchesHistoricalInteractiveIdentity(admission, request) {
|
|
21967
|
+
const base = `${encodeURIComponent(admission.idempotencyKey)}:${encodeURIComponent(admission.interactiveIdempotencyKey)}`;
|
|
21968
|
+
const digest = canonicalCandidateDigest({
|
|
21969
|
+
kind: "retained-identity.v1",
|
|
21970
|
+
base
|
|
21971
|
+
}).slice(7);
|
|
21972
|
+
const historicalId = (prefix) => {
|
|
21973
|
+
const readable = `${prefix}:${base}`;
|
|
21974
|
+
return readable.length <= 128 ? readable : `${prefix}:${digest}`;
|
|
21975
|
+
};
|
|
21976
|
+
return request.run.sessionId === historicalId("retained-session") && request.run.executionId === historicalId("retained-execution");
|
|
21977
|
+
}
|
|
21978
|
+
/** Rebuild controls for one exact provider-owned coding-agent process. @stable */
|
|
21979
|
+
async function reconnectRetainedInteractiveRun(options) {
|
|
21980
|
+
options.signal?.throwIfAborted();
|
|
21981
|
+
const ref = AgentInteractiveSessionRefSchema.parse(options.ref);
|
|
21982
|
+
if (ref.run.provider !== options.provider.name) throw new Error("interactive session reference belongs to another provider");
|
|
21983
|
+
const { environment, capabilities } = await reconstructEnvironment(options.provider, ref.run.environmentId, options.signal);
|
|
21984
|
+
if (!environment || !capabilities) return null;
|
|
21985
|
+
const handle = createRetainedInteractiveRunHandle(environment, ref, capabilities);
|
|
21986
|
+
await handle.status({ signal: options.signal });
|
|
21987
|
+
return handle;
|
|
21988
|
+
}
|
|
21989
|
+
function interactiveRequest(options, environment, profile, requestedProfileDigest, identity) {
|
|
21220
21990
|
const start = {
|
|
21221
21991
|
profile,
|
|
21222
21992
|
requestedProfileDigest,
|
|
@@ -21798,6 +22568,25 @@ function stableText(value, label) {
|
|
|
21798
22568
|
return text;
|
|
21799
22569
|
}
|
|
21800
22570
|
//#endregion
|
|
22571
|
+
//#region src/runtime/supervise/provider-visible-profile.ts
|
|
22572
|
+
/** Runtime-owned coordination is mounted under this MCP alias. */
|
|
22573
|
+
const coordinationMcpAlias = "agent-runtime-coordination";
|
|
22574
|
+
/** A profile declares Runtime-owned tools with this provider-neutral prefix. */
|
|
22575
|
+
const coordinationProfileToolPrefix = `${coordinationMcpAlias.replaceAll("-", "_")}_`;
|
|
22576
|
+
/** Remove Runtime-only grants and seat policy from the provider-facing profile. */
|
|
22577
|
+
function providerVisibleProfile(profile) {
|
|
22578
|
+
const { seats: _seats, ...withoutSeats } = profile;
|
|
22579
|
+
if (profile.tools === void 0) return withoutSeats;
|
|
22580
|
+
const providerTools = Object.fromEntries(Object.entries(profile.tools).filter(([name]) => !name.startsWith(coordinationProfileToolPrefix)));
|
|
22581
|
+
if (Object.keys(providerTools).length === Object.keys(profile.tools).length) return withoutSeats;
|
|
22582
|
+
if (Object.keys(providerTools).length > 0) return {
|
|
22583
|
+
...withoutSeats,
|
|
22584
|
+
tools: providerTools
|
|
22585
|
+
};
|
|
22586
|
+
const { tools: _runtimeTools, ...withoutTools } = withoutSeats;
|
|
22587
|
+
return withoutTools;
|
|
22588
|
+
}
|
|
22589
|
+
//#endregion
|
|
21801
22590
|
//#region src/runtime/supervise/terminal-record.ts
|
|
21802
22591
|
/** The settlement fields every writer copies, in the terminal record's key order. The
|
|
21803
22592
|
* `reconciled` record is built from this same spread so it and the terminal record can never
|
|
@@ -21950,7 +22739,7 @@ async function healReleasedSlots(opts, signal, now) {
|
|
|
21950
22739
|
};
|
|
21951
22740
|
await opts.journal.appendEvent(tree.root, terminalDownEvent(subject, settlement, settledSeq, floor.at, "released"));
|
|
21952
22741
|
healed += 1;
|
|
21953
|
-
const materialized = owned.find((event) => event.kind === "materialized");
|
|
22742
|
+
const materialized = [...owned].reverse().find((event) => event.kind === "materialized" || event.kind === "seat-materialized");
|
|
21954
22743
|
const lastReceipt = receipts[receipts.length - 1];
|
|
21955
22744
|
notifyRuntimeHookEvent(opts.hooks, {
|
|
21956
22745
|
id: `${spawned.id}:released`,
|
|
@@ -22032,7 +22821,7 @@ async function prepareInterruptedExecutors(opts, events, signal, now, parentId =
|
|
|
22032
22821
|
});
|
|
22033
22822
|
if (!prepared) continue;
|
|
22034
22823
|
}
|
|
22035
|
-
const priorMaterialization = owned.find((event) => event.kind === "materialized")?.receipt;
|
|
22824
|
+
const priorMaterialization = [...owned].reverse().find((event) => event.kind === "materialized" || event.kind === "seat-materialized")?.receipt;
|
|
22036
22825
|
const continuation = latestInput !== void 0 && inputs.length > 1 && node.ownedTreeRoot === void 0 ? await leafContinuation(node, owned, latestInput, opts) : void 0;
|
|
22037
22826
|
recoveries.push({
|
|
22038
22827
|
...detachedSnapshot({
|
|
@@ -22100,7 +22889,7 @@ async function leafContinuation(node, owned, latestInput, opts) {
|
|
|
22100
22889
|
}
|
|
22101
22890
|
async function assertRecordedResult(node, result, events, opts) {
|
|
22102
22891
|
const before = events.slice(0, events.indexOf(result));
|
|
22103
|
-
const receipt = before.find((event) => event.kind === "materialized")?.receipt;
|
|
22892
|
+
const receipt = [...before].reverse().find((event) => event.kind === "materialized" || event.kind === "seat-materialized")?.receipt;
|
|
22104
22893
|
const binding = [...before].reverse().find((event) => event.kind === "execution-bound")?.binding;
|
|
22105
22894
|
if (receipt?.status !== "known" || binding?.status !== "known" || receipt.runtime !== node.runtime || receipt.authoredProfileDigest !== node.identity?.profileDigest || binding.materializationReceiptDigest !== canonicalCandidateDigest(receipt)) throw new RuntimeRunStateError(`retained result '${node.id}' has no matching execution evidence`);
|
|
22106
22895
|
assertValidSpend(result.spent, `retained result '${result.id}'`);
|
|
@@ -22570,13 +23359,9 @@ function registerScopeRetainedOwner(scope, args) {
|
|
|
22570
23359
|
const taskEvent = [...events].reverse().find((event) => event.kind === "execution-input");
|
|
22571
23360
|
const start = taskEvent === void 0 ? 0 : events.indexOf(taskEvent);
|
|
22572
23361
|
const attempt = events.slice(start);
|
|
22573
|
-
const beforeInput = taskEvent === void 0 ? [] : events.slice(0, start);
|
|
22574
|
-
const priorInput = [...beforeInput].reverse().find((event) => event.kind === "execution-input");
|
|
22575
|
-
const priorAttempt = priorInput === void 0 ? [] : beforeInput.slice(beforeInput.indexOf(priorInput));
|
|
22576
23362
|
const admissions = attempt.flatMap((event) => event.kind === "execution-admitted" ? [event.admission] : []);
|
|
22577
23363
|
const acceptedRef = [...attempt].reverse().find((event) => event.kind === "execution-result");
|
|
22578
|
-
const
|
|
22579
|
-
const priorSession = priorResult?.kind === "execution-result" ? priorAttempt.slice(0, priorAttempt.indexOf(priorResult)).reverse().flatMap((event) => event.kind === "execution-admitted" && event.admission.phase === "environment" ? [event.admission] : [])[0] : void 0;
|
|
23364
|
+
const priorSession = taskEvent === void 0 ? void 0 : committedSession(events, start);
|
|
22580
23365
|
let sequence = Math.max(0, ...events.map((event) => "seq" in event ? event.seq : 0));
|
|
22581
23366
|
const state = {
|
|
22582
23367
|
args,
|
|
@@ -22679,9 +23464,81 @@ function registerScopeRetainedOwner(scope, args) {
|
|
|
22679
23464
|
};
|
|
22680
23465
|
owners.set(scope, state);
|
|
22681
23466
|
}
|
|
23467
|
+
/**
|
|
23468
|
+
* The environment admission an invocation that starts at `before` continues in: that of the
|
|
23469
|
+
* latest earlier invocation with a committed result, looking past invocations Runtime abandoned,
|
|
23470
|
+
* which never committed one. A live process keeps the same session across an abandonment, so a
|
|
23471
|
+
* resumed one must derive it the same way or its replay of the next invocation would not match
|
|
23472
|
+
* the material that invocation admitted.
|
|
23473
|
+
*/
|
|
23474
|
+
function committedSession(events, before) {
|
|
23475
|
+
let end = before;
|
|
23476
|
+
for (;;) {
|
|
23477
|
+
const head = events.slice(0, end);
|
|
23478
|
+
const input = [...head].reverse().find((event) => event.kind === "execution-input");
|
|
23479
|
+
if (input === void 0) return void 0;
|
|
23480
|
+
const start = head.indexOf(input);
|
|
23481
|
+
const attempt = head.slice(start);
|
|
23482
|
+
const result = [...attempt].reverse().find((event) => event.kind === "execution-result");
|
|
23483
|
+
if (result !== void 0) return attempt.slice(0, attempt.indexOf(result)).reverse().flatMap((event) => event.kind === "execution-admitted" && event.admission.phase === "environment" ? [event.admission] : [])[0];
|
|
23484
|
+
if (!attempt.some((event) => event.kind === "execution-abandoned")) return void 0;
|
|
23485
|
+
end = start;
|
|
23486
|
+
}
|
|
23487
|
+
}
|
|
23488
|
+
/** Bounds the stop Runtime asks for before abandoning a dispatched invocation. */
|
|
23489
|
+
const ABANDON_STOP_TIMEOUT_MS = 3e4;
|
|
23490
|
+
/**
|
|
23491
|
+
* Abandon the owner's in-flight invocation, which the driver retry loop asks for when the same
|
|
23492
|
+
* reconciliation keeps failing the same way (`DriverRetryRun.resolvePending`).
|
|
23493
|
+
*
|
|
23494
|
+
* Runtime stops the invocation at the provider when it had dispatched, then journals what the
|
|
23495
|
+
* provider confirmed. The journal refuses every later admission or result of the invocation, so
|
|
23496
|
+
* only the replacement the next {@link prepareScopeRetainedOwnerTask} starts can commit a result.
|
|
23497
|
+
* Answers `false`, journaling nothing, when this owner has no unfinished invocation to abandon.
|
|
23498
|
+
*/
|
|
23499
|
+
async function abandonScopeRetainedOwnerInvocation(scope, failure, failures) {
|
|
23500
|
+
const state = owners.get(scope);
|
|
23501
|
+
if (state === void 0 || state.inputSequence === void 0) return false;
|
|
23502
|
+
const { args } = state;
|
|
23503
|
+
const owned = (await args.journal.loadTree(args.rootId) ?? []).filter((event) => event.id === args.nodeId);
|
|
23504
|
+
const latestInput = [...owned].reverse().find((event) => event.kind === "execution-input");
|
|
23505
|
+
if (latestInput?.kind !== "execution-input" || latestInput.seq !== state.inputSequence) return false;
|
|
23506
|
+
const attempt = owned.slice(owned.indexOf(latestInput));
|
|
23507
|
+
if (attempt.some((event) => event.kind === "execution-result" || event.kind === "execution-abandoned")) return false;
|
|
23508
|
+
const dispatched = [...attempt].reverse().flatMap((event) => event.kind === "execution-admitted" && event.admission.phase === "dispatched" ? [event.admission] : [])[0];
|
|
23509
|
+
const stop = dispatched === void 0 || state.provider === void 0 ? void 0 : await stopRetainedNativeExecution({
|
|
23510
|
+
provider: state.provider,
|
|
23511
|
+
controlRef: dispatched.controlRef,
|
|
23512
|
+
signal: AbortSignal.any([scope.signal, AbortSignal.timeout(ABANDON_STOP_TIMEOUT_MS)]),
|
|
23513
|
+
now: args.now
|
|
23514
|
+
});
|
|
23515
|
+
scope.signal.throwIfAborted();
|
|
23516
|
+
const stopped = stop?.effect === "cancelled" || stop?.effect === "not_live";
|
|
23517
|
+
await args.journal.appendEvent(args.rootId, {
|
|
23518
|
+
kind: "execution-abandoned",
|
|
23519
|
+
id: args.nodeId,
|
|
23520
|
+
inputSeq: latestInput.seq,
|
|
23521
|
+
pendingCause: failure.pendingCause,
|
|
23522
|
+
failures,
|
|
23523
|
+
detail: errorText(errMessage(failure)).slice(0, 2048),
|
|
23524
|
+
outcome: stopped ? "stopped" : "uncertain",
|
|
23525
|
+
...stop === void 0 ? {} : { stop: {
|
|
23526
|
+
executionId: stop.executionId,
|
|
23527
|
+
effect: stop.effect,
|
|
23528
|
+
...stop.error === void 0 ? {} : { error: stop.error }
|
|
23529
|
+
} },
|
|
23530
|
+
seq: state.nextSequence(),
|
|
23531
|
+
at: new Date(args.now()).toISOString()
|
|
23532
|
+
});
|
|
23533
|
+
return true;
|
|
23534
|
+
}
|
|
22682
23535
|
function scopeRetainedOwnerContext(scope) {
|
|
22683
23536
|
return owners.get(scope)?.context;
|
|
22684
23537
|
}
|
|
23538
|
+
/** The journal tree that owns this manager turn, including nested driver children. */
|
|
23539
|
+
function scopeRetainedOwnerJournalRoot(scope) {
|
|
23540
|
+
return owners.get(scope)?.args.rootId;
|
|
23541
|
+
}
|
|
22685
23542
|
/**
|
|
22686
23543
|
* Bind cleanup before replay can return an already accepted owner result. `checkpointCapture` is
|
|
22687
23544
|
* where each checkpoint's files are stored before the checkpoint is deleted; without it, no
|
|
@@ -22796,6 +23653,59 @@ function noteScopeRetainedOwnerCoordination(scope) {
|
|
|
22796
23653
|
delete state.checkpointing;
|
|
22797
23654
|
});
|
|
22798
23655
|
}
|
|
23656
|
+
/** A committed turn is the seat boundary. Keep its native home for a same-harness provider
|
|
23657
|
+
* continuation; only a provider/harness change needs a new environment. */
|
|
23658
|
+
async function prepareScopeRetainedOwnerSeatSwitch(scope, mode) {
|
|
23659
|
+
const state = owners.get(scope);
|
|
23660
|
+
if (!state?.acceptedRef || !state.acceptedConsumed) throw new ValidationError$1("seat switch requires a committed owner turn");
|
|
23661
|
+
const accepted = state.acceptedRef;
|
|
23662
|
+
await scopeRetainedOwnerTurnEnded(scope);
|
|
23663
|
+
await state.checkpointing;
|
|
23664
|
+
await checkpointOwnerWorkspace(scope, state);
|
|
23665
|
+
const owned = (await state.args.journal.loadTree(state.args.rootId) ?? []).filter((event) => event.id === state.args.nodeId);
|
|
23666
|
+
const acceptedIndex = owned.findIndex((event) => event.kind === "execution-result" && event.seq === accepted.seq && event.outRef === accepted.outRef);
|
|
23667
|
+
if (acceptedIndex < 0) throw new ValidationError$1("seat switch has no committed result in its journal");
|
|
23668
|
+
const source = owned.slice(0, acceptedIndex).reverse().find((event) => event.kind === "execution-admitted" && event.admission.phase === "environment");
|
|
23669
|
+
const sourceEnvironmentId = source?.kind === "execution-admitted" && source.admission.phase === "environment" ? source.admission.environmentId : void 0;
|
|
23670
|
+
const checkpoint = sourceEnvironmentId !== void 0 ? owned.slice(acceptedIndex + 1).reverse().find((event) => event.kind === "workspace-checkpoint" && event.environmentId === sourceEnvironmentId) : void 0;
|
|
23671
|
+
if (checkpoint?.kind !== "workspace-checkpoint") throw new ValidationError$1("seat switch requires a checkpoint of the committed source environment");
|
|
23672
|
+
if (mode === "replace-environment") {
|
|
23673
|
+
await retireSeatEnvironment(scope, state);
|
|
23674
|
+
state.forceNewSession = true;
|
|
23675
|
+
}
|
|
23676
|
+
return {
|
|
23677
|
+
checkpointAt: checkpoint.at,
|
|
23678
|
+
checkpointId: checkpoint.checkpoint.checkpointId,
|
|
23679
|
+
sourceEnvironmentId: checkpoint.environmentId
|
|
23680
|
+
};
|
|
23681
|
+
}
|
|
23682
|
+
/** The next seat receives fresh create-time credentials. Release the stopped source only after
|
|
23683
|
+
* its checkpoint exists, then commit the teardown so recovery never tries its old session. */
|
|
23684
|
+
async function retireSeatEnvironment(scope, state) {
|
|
23685
|
+
const { provider, args } = state;
|
|
23686
|
+
if (!provider?.get) throw new ValidationError$1("seat switch requires a provider with exact environment lookup");
|
|
23687
|
+
const owned = (await args.journal.loadTree(args.rootId) ?? []).filter((event) => event.id === args.nodeId);
|
|
23688
|
+
const admitted = [...owned].reverse().find((event) => event.kind === "execution-admitted" && event.admission.phase === "environment");
|
|
23689
|
+
if (admitted?.kind !== "execution-admitted" || admitted.admission.phase !== "environment") throw new ValidationError$1("seat switch has no committed source environment");
|
|
23690
|
+
const environmentId = admitted.admission.environmentId;
|
|
23691
|
+
if (owned.some((event) => event.kind === "environment-teardown" && event.destroyed && event.environmentId === environmentId)) return;
|
|
23692
|
+
const signal = AbortSignal.any([scope.signal, AbortSignal.timeout(3e4)]);
|
|
23693
|
+
const environment = await runAbortable(() => provider.get(environmentId), signal, "seat switch environment lookup timed out");
|
|
23694
|
+
if (environment !== null) {
|
|
23695
|
+
if (environment.id !== environmentId || environment.provider !== provider.name || !environment.destroy) throw new ValidationError$1("seat switch cannot retire the exact source environment");
|
|
23696
|
+
await runAbortable(() => environment.destroy(), signal, "seat switch environment retirement timed out");
|
|
23697
|
+
}
|
|
23698
|
+
await args.journal.appendEvent(args.rootId, {
|
|
23699
|
+
kind: "environment-teardown",
|
|
23700
|
+
id: args.nodeId,
|
|
23701
|
+
provider: provider.name,
|
|
23702
|
+
environmentId,
|
|
23703
|
+
destroyed: true,
|
|
23704
|
+
detail: "seat changed after a committed turn",
|
|
23705
|
+
seq: state.nextSequence(),
|
|
23706
|
+
at: new Date(args.now()).toISOString()
|
|
23707
|
+
});
|
|
23708
|
+
}
|
|
22799
23709
|
async function checkpointOwnerWorkspace(scope, state) {
|
|
22800
23710
|
const { provider, args } = state;
|
|
22801
23711
|
if (provider === void 0) return;
|
|
@@ -22973,7 +23883,8 @@ async function cleanCheckpoints(state, branching, checkpoints, events, parent) {
|
|
|
22973
23883
|
for (const event of checkpoints) {
|
|
22974
23884
|
const captured = await captureBeforeCleanup(state, branching, event, events, parent);
|
|
22975
23885
|
if ("reason" in captured) {
|
|
22976
|
-
|
|
23886
|
+
state.captureRefused ??= /* @__PURE__ */ new Set();
|
|
23887
|
+
state.captureRefused.add(event.checkpoint.checkpointId);
|
|
22977
23888
|
refused.push(captured);
|
|
22978
23889
|
await state.args.journal.appendEvent(state.args.rootId, {
|
|
22979
23890
|
kind: "workspace-checkpoint-cleanup",
|
|
@@ -23193,8 +24104,8 @@ async function settleCaptureForks(state) {
|
|
|
23193
24104
|
* environment needs one; a live environment is continued, never copied.
|
|
23194
24105
|
*/
|
|
23195
24106
|
function restoreBefore(owned, inputIndex) {
|
|
23196
|
-
const
|
|
23197
|
-
return owned.slice(0, inputIndex < 0 ? owned.length : inputIndex).filter((event) => event.kind === "workspace-checkpoint" &&
|
|
24107
|
+
const unusable = unusableEnvironmentIds(owned);
|
|
24108
|
+
return owned.slice(0, inputIndex < 0 ? owned.length : inputIndex).filter((event) => event.kind === "workspace-checkpoint" && unusable.has(event.environmentId)).at(-1);
|
|
23198
24109
|
}
|
|
23199
24110
|
async function restoreCapable(provider) {
|
|
23200
24111
|
if (provider === void 0) return false;
|
|
@@ -23215,7 +24126,8 @@ async function scopeRetainedOwnerRestorePoint(scope) {
|
|
|
23215
24126
|
const latest = restoreBefore((await state.args.journal.loadTree(state.args.rootId) ?? []).filter((event) => event.id === state.args.nodeId), -1);
|
|
23216
24127
|
return latest === void 0 ? void 0 : {
|
|
23217
24128
|
checkpointId: latest.checkpoint.checkpointId,
|
|
23218
|
-
takenAt: latest.at
|
|
24129
|
+
takenAt: latest.at,
|
|
24130
|
+
sourceEnvironmentId: latest.checkpoint.source.environmentId
|
|
23219
24131
|
};
|
|
23220
24132
|
}
|
|
23221
24133
|
/** @internal Whether another owner release could still destroy what the last one left. */
|
|
@@ -23539,9 +24451,9 @@ function hasDurableWorkspaceCapture(output, snapshot, environmentId, executionId
|
|
|
23539
24451
|
const linked = agentCandidateWorkspaceSnapshotEvidenceSchema.safeParse(receipt.snapshot);
|
|
23540
24452
|
return linked.success && linked.data.digest === parsed.data.digest && JSON.stringify(linked.data.manifest) === JSON.stringify(parsed.data.manifest) && JSON.stringify(linked.data.archive) === JSON.stringify(parsed.data.archive);
|
|
23541
24453
|
}
|
|
23542
|
-
/**
|
|
23543
|
-
function
|
|
23544
|
-
return new Set(owned.flatMap((event) => event.kind === "environment-teardown" && event.destroyed ? [event.environmentId] : []));
|
|
24454
|
+
/** Environments that cannot execute another turn, whether absent or terminal at the provider. */
|
|
24455
|
+
function unusableEnvironmentIds(owned) {
|
|
24456
|
+
return new Set(owned.flatMap((event) => event.kind === "environment-terminal" || event.kind === "environment-teardown" && event.destroyed ? [event.environmentId] : []));
|
|
23545
24457
|
}
|
|
23546
24458
|
/** The environment the owner's next invocation would continue in, when there is one: the latest
|
|
23547
24459
|
* environment admission of this owner, which is either the current invocation's own (recovered
|
|
@@ -23570,8 +24482,9 @@ function continuedEnvironment(owned) {
|
|
|
23570
24482
|
* commissioned settled `done` two minutes later, unread.
|
|
23571
24483
|
*
|
|
23572
24484
|
* The reconciliation refusal protects against paying twice for an execution that may still run.
|
|
23573
|
-
* An environment the provider no longer holds
|
|
23574
|
-
*
|
|
24485
|
+
* An environment the provider no longer holds or reports terminal runs nothing. Runtime records
|
|
24486
|
+
* the provider's exact observation before the next prepare starts a new invocation. A terminal
|
|
24487
|
+
* record still requires cleanup at settlement; it is not a teardown receipt.
|
|
23575
24488
|
*/
|
|
23576
24489
|
async function reconcileScopeRetainedOwnerEnvironment(scope) {
|
|
23577
24490
|
const state = owners.get(scope);
|
|
@@ -23581,7 +24494,7 @@ async function reconcileScopeRetainedOwnerEnvironment(scope) {
|
|
|
23581
24494
|
const continued = continuedEnvironment(owned);
|
|
23582
24495
|
if (continued === void 0) return { state: "none" };
|
|
23583
24496
|
const environmentId = continued.admission.environmentId;
|
|
23584
|
-
if (
|
|
24497
|
+
if (unusableEnvironmentIds(owned).has(environmentId)) return {
|
|
23585
24498
|
state: "lost",
|
|
23586
24499
|
environmentId
|
|
23587
24500
|
};
|
|
@@ -23590,21 +24503,31 @@ async function reconcileScopeRetainedOwnerEnvironment(scope) {
|
|
|
23590
24503
|
environmentId
|
|
23591
24504
|
};
|
|
23592
24505
|
let environment;
|
|
24506
|
+
let terminalStatus;
|
|
23593
24507
|
try {
|
|
23594
24508
|
environment = await runAbortable(() => provider.get(environmentId), AbortSignal.any([scope.signal, AbortSignal.timeout(3e4)]), "retained owner environment check timed out");
|
|
24509
|
+
if (environment !== null) {
|
|
24510
|
+
const found = environment;
|
|
24511
|
+
if (found.id !== environmentId || found.provider !== provider.name) return {
|
|
24512
|
+
state: "unknown",
|
|
24513
|
+
environmentId
|
|
24514
|
+
};
|
|
24515
|
+
const status = await runAbortable(() => found.status(), AbortSignal.any([scope.signal, AbortSignal.timeout(3e4)]), "retained owner environment status timed out");
|
|
24516
|
+
if (status === "failed" || status === "expired") terminalStatus = status;
|
|
24517
|
+
}
|
|
23595
24518
|
} catch {
|
|
23596
24519
|
return {
|
|
23597
24520
|
state: "unknown",
|
|
23598
24521
|
environmentId
|
|
23599
24522
|
};
|
|
23600
24523
|
}
|
|
23601
|
-
if (environment !== null) return {
|
|
24524
|
+
if (environment !== null && terminalStatus === void 0) return {
|
|
23602
24525
|
state: "live",
|
|
23603
24526
|
environmentId,
|
|
23604
24527
|
inFlight: continued.inFlight
|
|
23605
24528
|
};
|
|
23606
24529
|
scope.signal.throwIfAborted();
|
|
23607
|
-
await args.journal.appendEvent(args.rootId, {
|
|
24530
|
+
await args.journal.appendEvent(args.rootId, terminalStatus === void 0 ? {
|
|
23608
24531
|
kind: "environment-teardown",
|
|
23609
24532
|
id: args.nodeId,
|
|
23610
24533
|
provider: provider.name,
|
|
@@ -23613,6 +24536,14 @@ async function reconcileScopeRetainedOwnerEnvironment(scope) {
|
|
|
23613
24536
|
detail: "lost: the provider no longer holds this environment, so the next invocation starts in a new one",
|
|
23614
24537
|
seq: state.nextSequence(),
|
|
23615
24538
|
at: new Date(args.now()).toISOString()
|
|
24539
|
+
} : {
|
|
24540
|
+
kind: "environment-terminal",
|
|
24541
|
+
id: args.nodeId,
|
|
24542
|
+
provider: provider.name,
|
|
24543
|
+
environmentId,
|
|
24544
|
+
status: terminalStatus,
|
|
24545
|
+
seq: state.nextSequence(),
|
|
24546
|
+
at: new Date(args.now()).toISOString()
|
|
23616
24547
|
});
|
|
23617
24548
|
return {
|
|
23618
24549
|
state: "lost",
|
|
@@ -23635,14 +24566,19 @@ async function prepareScopeRetainedOwnerTask(scope, task) {
|
|
|
23635
24566
|
const accepted = [...attempt].reverse().find((event) => event.kind === "execution-result");
|
|
23636
24567
|
if (accepted?.kind === "execution-result") state.acceptedRef = accepted;
|
|
23637
24568
|
}
|
|
23638
|
-
const
|
|
24569
|
+
const unusable = unusableEnvironmentIds(owned);
|
|
23639
24570
|
const inFlight = continuedEnvironment(owned);
|
|
23640
|
-
if (inFlight?.inFlight &&
|
|
24571
|
+
if (inFlight?.inFlight && unusable.has(inFlight.admission.environmentId)) {
|
|
23641
24572
|
delete state.inputSequence;
|
|
23642
24573
|
delete state.taskRef;
|
|
23643
24574
|
state.admissions.length = 0;
|
|
23644
24575
|
delete state.priorSession;
|
|
23645
24576
|
}
|
|
24577
|
+
if (latestInput?.kind === "execution-input" && state.inputSequence === latestInput.seq && owned.slice(owned.indexOf(latestInput)).some((event) => event.kind === "execution-abandoned")) {
|
|
24578
|
+
delete state.inputSequence;
|
|
24579
|
+
delete state.taskRef;
|
|
24580
|
+
state.admissions.length = 0;
|
|
24581
|
+
}
|
|
23646
24582
|
if (state.prepared && state.acceptedRef && state.acceptedConsumed) {
|
|
23647
24583
|
const currentEnvironment = [...state.admissions].reverse().find((admission) => admission.phase === "environment");
|
|
23648
24584
|
if (currentEnvironment?.phase === "environment") state.priorSession = currentEnvironment;
|
|
@@ -23653,7 +24589,15 @@ async function prepareScopeRetainedOwnerTask(scope, task) {
|
|
|
23653
24589
|
state.admissions.length = 0;
|
|
23654
24590
|
delete state.acceptedConsumed;
|
|
23655
24591
|
}
|
|
23656
|
-
if (state.priorSession !== void 0 &&
|
|
24592
|
+
if (state.priorSession !== void 0 && unusable.has(state.priorSession.environmentId)) delete state.priorSession;
|
|
24593
|
+
const segmentStarts = owned.filter((event) => event.kind === "seat-segment" && event.phase === "started");
|
|
24594
|
+
const latestSeat = segmentStarts.at(-1);
|
|
24595
|
+
const previousSeat = segmentStarts.at(-2);
|
|
24596
|
+
if (latestSeat && previousSeat && latestSeat.provider !== previousSeat.provider && owned.indexOf(latestSeat) > owned.indexOf(latestInput)) state.forceNewSession = true;
|
|
24597
|
+
if (state.forceNewSession) {
|
|
24598
|
+
delete state.priorSession;
|
|
24599
|
+
delete state.forceNewSession;
|
|
24600
|
+
}
|
|
23657
24601
|
state.prepared = true;
|
|
23658
24602
|
const capable = await restoreCapable(state.provider);
|
|
23659
24603
|
const restoreFor = (inputIndex) => {
|
|
@@ -23724,6 +24668,227 @@ function consumeScopeRetainedOwnerResult(scope) {
|
|
|
23724
24668
|
state.acceptedConsumed = true;
|
|
23725
24669
|
}
|
|
23726
24670
|
//#endregion
|
|
24671
|
+
//#region src/runtime/supervise/seat-chain.ts
|
|
24672
|
+
var SeatChainExhaustedError = class extends Error {
|
|
24673
|
+
resumeAt;
|
|
24674
|
+
constructor(resumeAt) {
|
|
24675
|
+
super(`authored subscription seat chain exhausted; resume at ${resumeAt}`);
|
|
24676
|
+
this.name = "SeatChainExhaustedError";
|
|
24677
|
+
this.resumeAt = resumeAt;
|
|
24678
|
+
}
|
|
24679
|
+
};
|
|
24680
|
+
/** One logical node's durable chain. The spawn journal is the sole transition authority. */
|
|
24681
|
+
var SeatChain = class {
|
|
24682
|
+
profile;
|
|
24683
|
+
nodeId;
|
|
24684
|
+
rootId;
|
|
24685
|
+
journal;
|
|
24686
|
+
select;
|
|
24687
|
+
now;
|
|
24688
|
+
onSegment;
|
|
24689
|
+
beforeStart;
|
|
24690
|
+
observedThrough = -1;
|
|
24691
|
+
constructor(profile, nodeId, rootId, journal, select, now = Date.now, onSegment, beforeStart) {
|
|
24692
|
+
this.profile = profile;
|
|
24693
|
+
this.nodeId = nodeId;
|
|
24694
|
+
this.rootId = rootId;
|
|
24695
|
+
this.journal = journal;
|
|
24696
|
+
this.select = select;
|
|
24697
|
+
this.now = now;
|
|
24698
|
+
this.onSegment = onSegment;
|
|
24699
|
+
this.beforeStart = beforeStart;
|
|
24700
|
+
}
|
|
24701
|
+
async next(afterUsageLimit = false, afterRecheck = false) {
|
|
24702
|
+
const stages = authoredSeatStages(this.profile);
|
|
24703
|
+
if (!stages) throw new ValidationError$1("SeatChain requires authored AgentProfile.seats");
|
|
24704
|
+
const allEvents = await this.journal.loadTree(this.rootId) ?? [];
|
|
24705
|
+
const events = allEvents.filter((event) => event.kind === "seat-segment" && event.id === this.nodeId);
|
|
24706
|
+
const destroyed = new Set(allEvents.filter((event) => event.kind === "environment-teardown" && event.id === this.nodeId && event.destroyed).map((event) => event.environmentId));
|
|
24707
|
+
let admittedEnvironmentId;
|
|
24708
|
+
for (const event of allEvents) if (event.kind === "execution-admitted" && event.id === this.nodeId && event.admission.phase === "environment") admittedEnvironmentId = event.admission.environmentId;
|
|
24709
|
+
const retainedEnvironmentId = admittedEnvironmentId !== void 0 && !destroyed.has(admittedEnvironmentId) ? admittedEnvironmentId : void 0;
|
|
24710
|
+
await this.reconcile(events);
|
|
24711
|
+
const starts = events.filter((event) => event.phase === "started");
|
|
24712
|
+
const last = starts.at(-1);
|
|
24713
|
+
const closed = last && events.some((event) => event.phase === "ended" && event.segmentIndex === last.segmentIndex);
|
|
24714
|
+
if (last && !closed && afterUsageLimit) await this.end(last, "usage-limit");
|
|
24715
|
+
const resume = last && !closed && !afterUsageLimit;
|
|
24716
|
+
const segmentIndex = resume ? last.segmentIndex : (last?.segmentIndex ?? -1) + 1;
|
|
24717
|
+
const firstStageIndex = afterRecheck ? 0 : last?.stageIndex ?? 0;
|
|
24718
|
+
let earliestReset;
|
|
24719
|
+
for (let stageIndex = firstStageIndex; stageIndex < stages.length; stageIndex += 1) {
|
|
24720
|
+
const stage = stages[stageIndex];
|
|
24721
|
+
const excludedSeatIds = (afterRecheck ? [] : starts).filter((event) => event.stageIndex === stageIndex && (!resume || event.seat !== last.seat)).map((event) => event.seat);
|
|
24722
|
+
const prior = last === void 0 ? void 0 : {
|
|
24723
|
+
seat: last.seat,
|
|
24724
|
+
harness: last.harness,
|
|
24725
|
+
provider: last.provider,
|
|
24726
|
+
model: last.model
|
|
24727
|
+
};
|
|
24728
|
+
const preceding = resume ? starts.at(-2) : last;
|
|
24729
|
+
const requiresNativeTurnGrant = Boolean(preceding && preceding.provider === stage.provider && preceding.harness === stage.harness && (!resume || preceding.seat !== last.seat));
|
|
24730
|
+
const selection = checkedSeatSelection(stage, await this.select({
|
|
24731
|
+
profile: this.profile,
|
|
24732
|
+
nodeId: this.nodeId,
|
|
24733
|
+
stage,
|
|
24734
|
+
stageIndex,
|
|
24735
|
+
segmentIndex,
|
|
24736
|
+
excludedSeatIds,
|
|
24737
|
+
requiresNativeTurnGrant,
|
|
24738
|
+
...retainedEnvironmentId === void 0 ? {} : { retainedEnvironmentId },
|
|
24739
|
+
...prior === void 0 ? {} : { previous: prior },
|
|
24740
|
+
...resume ? { resumeSeatId: last.seat } : {}
|
|
24741
|
+
}));
|
|
24742
|
+
if ("resumeAt" in selection) {
|
|
24743
|
+
const reset = Date.parse(selection.resumeAt);
|
|
24744
|
+
earliestReset = earliestReset === void 0 ? reset : Math.min(earliestReset, reset);
|
|
24745
|
+
if (resume) break;
|
|
24746
|
+
continue;
|
|
24747
|
+
}
|
|
24748
|
+
if (resume && selection.seat !== last.seat) throw new ValidationError$1("selectSeat: resume changed the committed seat");
|
|
24749
|
+
if (excludedSeatIds.includes(selection.seat)) throw new ValidationError$1("selectSeat: selected an exhausted seat");
|
|
24750
|
+
if (preceding && preceding.seat !== selection.seat && preceding.provider === stage.provider && preceding.harness === stage.harness && selection.nativeTurnGrant !== true) throw new ValidationError$1("selectSeat: same-environment seat requires a verified native turn grant");
|
|
24751
|
+
const active = {
|
|
24752
|
+
seat: selection.seat,
|
|
24753
|
+
stageIndex,
|
|
24754
|
+
segmentIndex,
|
|
24755
|
+
stage,
|
|
24756
|
+
backend: selection.backend,
|
|
24757
|
+
...selection.nativeTurnGrant === true ? { nativeTurnGrant: true } : {},
|
|
24758
|
+
...preceding === void 0 ? {} : { previous: {
|
|
24759
|
+
seat: preceding.seat,
|
|
24760
|
+
harness: preceding.harness,
|
|
24761
|
+
provider: preceding.provider,
|
|
24762
|
+
model: preceding.model
|
|
24763
|
+
} }
|
|
24764
|
+
};
|
|
24765
|
+
if (!resume) {
|
|
24766
|
+
await this.beforeStart?.(active, prior);
|
|
24767
|
+
const at = new Date(this.now()).toISOString();
|
|
24768
|
+
await this.journal.appendEvent(this.rootId, {
|
|
24769
|
+
kind: "seat-segment",
|
|
24770
|
+
id: this.nodeId,
|
|
24771
|
+
segmentIndex,
|
|
24772
|
+
stageIndex,
|
|
24773
|
+
phase: "started",
|
|
24774
|
+
seat: selection.seat,
|
|
24775
|
+
harness: stage.harness,
|
|
24776
|
+
provider: stage.provider,
|
|
24777
|
+
model: stage.model,
|
|
24778
|
+
seq: segmentIndex,
|
|
24779
|
+
at
|
|
24780
|
+
});
|
|
24781
|
+
await this.onSegment?.({
|
|
24782
|
+
seat: selection.seat,
|
|
24783
|
+
harness: stage.harness,
|
|
24784
|
+
provider: stage.provider,
|
|
24785
|
+
model: stage.model,
|
|
24786
|
+
startedAt: at
|
|
24787
|
+
}, "started");
|
|
24788
|
+
this.observedThrough = events.length;
|
|
24789
|
+
return active;
|
|
24790
|
+
}
|
|
24791
|
+
return active;
|
|
24792
|
+
}
|
|
24793
|
+
const recheckAt = Math.max(earliestReset ?? 0, this.now() + 12e4);
|
|
24794
|
+
throw new SeatChainExhaustedError(new Date(recheckAt).toISOString());
|
|
24795
|
+
}
|
|
24796
|
+
async close(reason) {
|
|
24797
|
+
const events = (await this.journal.loadTree(this.rootId) ?? []).filter((event) => event.kind === "seat-segment" && event.id === this.nodeId);
|
|
24798
|
+
await this.reconcile(events);
|
|
24799
|
+
const last = events.filter((event) => event.phase === "started").at(-1);
|
|
24800
|
+
if (last && !events.some((event) => event.phase === "ended" && event.segmentIndex === last.segmentIndex)) await this.end(last, reason);
|
|
24801
|
+
}
|
|
24802
|
+
async end(start, reason) {
|
|
24803
|
+
const at = new Date(this.now()).toISOString();
|
|
24804
|
+
await this.journal.appendEvent(this.rootId, {
|
|
24805
|
+
...start,
|
|
24806
|
+
phase: "ended",
|
|
24807
|
+
reason,
|
|
24808
|
+
at
|
|
24809
|
+
});
|
|
24810
|
+
await this.onSegment?.({
|
|
24811
|
+
seat: start.seat,
|
|
24812
|
+
harness: start.harness,
|
|
24813
|
+
provider: start.provider,
|
|
24814
|
+
model: start.model,
|
|
24815
|
+
startedAt: start.at,
|
|
24816
|
+
endedAt: at,
|
|
24817
|
+
reason
|
|
24818
|
+
}, "ended");
|
|
24819
|
+
this.observedThrough += 1;
|
|
24820
|
+
}
|
|
24821
|
+
/** A crash may land between the authoritative journal append and observer delivery. On
|
|
24822
|
+
* reconstruction, deliver every unobserved boundary again; the observer projection folds
|
|
24823
|
+
* repeated boundaries by seat and start time, so a callback never decides execution identity. */
|
|
24824
|
+
async reconcile(events) {
|
|
24825
|
+
const starts = /* @__PURE__ */ new Map();
|
|
24826
|
+
for (let index = 0; index < events.length; index += 1) {
|
|
24827
|
+
const event = events[index];
|
|
24828
|
+
if (event.phase === "started") starts.set(event.segmentIndex, event);
|
|
24829
|
+
if (index <= this.observedThrough) continue;
|
|
24830
|
+
const start = event.phase === "started" ? event : starts.get(event.segmentIndex);
|
|
24831
|
+
if (!start) throw new ValidationError$1("seat segment ended without a committed start");
|
|
24832
|
+
await this.onSegment?.({
|
|
24833
|
+
seat: start.seat,
|
|
24834
|
+
harness: start.harness,
|
|
24835
|
+
provider: start.provider,
|
|
24836
|
+
model: start.model,
|
|
24837
|
+
startedAt: start.at,
|
|
24838
|
+
...event.phase === "ended" ? {
|
|
24839
|
+
endedAt: event.at,
|
|
24840
|
+
reason: event.reason
|
|
24841
|
+
} : {}
|
|
24842
|
+
}, event.phase);
|
|
24843
|
+
this.observedThrough = index;
|
|
24844
|
+
}
|
|
24845
|
+
}
|
|
24846
|
+
};
|
|
24847
|
+
/** Preserve the authored profile for identity; only the provider-facing copy changes stages. */
|
|
24848
|
+
function seatExecutionProfile(profile, stage) {
|
|
24849
|
+
const { seats: _seats, ...withoutSeats } = profile;
|
|
24850
|
+
return {
|
|
24851
|
+
...withoutSeats,
|
|
24852
|
+
harness: stage.harness,
|
|
24853
|
+
...stage.tools === void 0 ? {} : { tools: stage.tools },
|
|
24854
|
+
...stage.permissions === void 0 ? {} : { permissions: stage.permissions },
|
|
24855
|
+
model: {
|
|
24856
|
+
...profile.model,
|
|
24857
|
+
default: stage.model,
|
|
24858
|
+
provider: stage.provider
|
|
24859
|
+
}
|
|
24860
|
+
};
|
|
24861
|
+
}
|
|
24862
|
+
function authoredSeatStages(profile) {
|
|
24863
|
+
const seats = profile.seats;
|
|
24864
|
+
if (seats === void 0) return void 0;
|
|
24865
|
+
if (!Array.isArray(seats) || seats.length === 0) throw new ValidationError$1("AgentProfile.seats must be a nonempty ordered chain");
|
|
24866
|
+
return seats;
|
|
24867
|
+
}
|
|
24868
|
+
const USAGE_LIMIT_CODES = /* @__PURE__ */ new Set([
|
|
24869
|
+
"subscription_usage_limit",
|
|
24870
|
+
"usage_limit_reached",
|
|
24871
|
+
"usage_limit_exceeded",
|
|
24872
|
+
"weekly_limit_reached",
|
|
24873
|
+
"session_limit_reached"
|
|
24874
|
+
]);
|
|
24875
|
+
/** Normalize a native harness refusal into a typed limit. A plain HTTP 429 is insufficient. */
|
|
24876
|
+
function subscriptionUsageLimitSignal(failure) {
|
|
24877
|
+
if (failure.errorCode && USAGE_LIMIT_CODES.has(failure.errorCode.toLowerCase())) return "subscription-usage-limit";
|
|
24878
|
+
if (/\bYou've hit your (?:usage|weekly|session) limit\b/iu.test(failure.error)) return "subscription-usage-limit";
|
|
24879
|
+
}
|
|
24880
|
+
/** An ineligible or misconfigured account must never turn an authored stage into another model. */
|
|
24881
|
+
function checkedSeatSelection(stage, selected) {
|
|
24882
|
+
if ("resumeAt" in selected) {
|
|
24883
|
+
if (!Number.isFinite(Date.parse(selected.resumeAt))) throw new ValidationError$1("selectSeat: resumeAt must be an ISO timestamp");
|
|
24884
|
+
return selected;
|
|
24885
|
+
}
|
|
24886
|
+
if (!selected.seat.trim()) throw new ValidationError$1("selectSeat: seat must be nonempty");
|
|
24887
|
+
if (stage.selector.kind === "seat" && stage.selector.id !== selected.seat) throw new ValidationError$1("selectSeat: selection does not match the authored seat");
|
|
24888
|
+
if (selected.backend.backend !== "provider") throw new ValidationError$1("selectSeat: selection must use a provider backend");
|
|
24889
|
+
return selected;
|
|
24890
|
+
}
|
|
24891
|
+
//#endregion
|
|
23727
24892
|
//#region src/runtime/supervise/scope.ts
|
|
23728
24893
|
/**
|
|
23729
24894
|
*
|
|
@@ -25139,7 +26304,23 @@ async function publishOwnerMaterialization(state, runtime, declaration, bindingI
|
|
|
25139
26304
|
await rejectOwnerMaterialization(state);
|
|
25140
26305
|
throw new ValidationError$1("scope owner returned invalid materialization evidence", { cause: error });
|
|
25141
26306
|
}
|
|
25142
|
-
|
|
26307
|
+
const committed = state.receipt ?? state.prior;
|
|
26308
|
+
if (committed?.status === "known" && canonicalCandidateDigest(committed) !== canonicalCandidateDigest(receipt)) {
|
|
26309
|
+
const events = await state.journal.loadTree(state.retainedRoot) ?? [];
|
|
26310
|
+
const started = [...events].reverse().find((event) => event.id === state.nodeId && event.kind === "seat-segment" && event.phase === "started");
|
|
26311
|
+
const latestMaterialization = [...events].reverse().find((event) => event.id === state.nodeId && (event.kind === "materialized" || event.kind === "seat-materialized"));
|
|
26312
|
+
const lastSegmentIndex = latestMaterialization?.kind === "seat-materialized" ? latestMaterialization.segmentIndex : 0;
|
|
26313
|
+
const authored = agentProfileSchema.safeParse(state.authoredProfile);
|
|
26314
|
+
const authoredProfile = authored.success ? authored.data : void 0;
|
|
26315
|
+
const stage = authoredProfile && started?.kind === "seat-segment" ? authoredSeatStages(authoredProfile)?.[started.stageIndex] : void 0;
|
|
26316
|
+
if (authoredProfile && stage && started?.kind === "seat-segment" && started.segmentIndex > lastSegmentIndex && started.harness === stage.harness && started.provider === stage.provider && started.model === stage.model && receipt.status === "known" && receipt.effectiveProfileDigest === canonicalAgentProfileDigest(providerVisibleProfile(seatExecutionProfile(authoredProfile, stage))) && receipt.model.status === "known" && receipt.model.id === stage.model) {
|
|
26317
|
+
await appendOwnerSeatMaterialization(state, started.segmentIndex, receipt, binding);
|
|
26318
|
+
state.onReceipt?.(receipt, binding);
|
|
26319
|
+
state.publishedThisProcess = true;
|
|
26320
|
+
return;
|
|
26321
|
+
}
|
|
26322
|
+
}
|
|
26323
|
+
if (state.prior !== void 0 && state.receipt === void 0) {
|
|
25143
26324
|
if (canonicalCandidateDigest(state.prior) !== canonicalCandidateDigest(receipt)) {
|
|
25144
26325
|
await rejectOwnerMaterialization(state);
|
|
25145
26326
|
throw new ValidationError$1("scope owner materialization changed across resume; backend, model, execution identity, and plan must match");
|
|
@@ -25221,12 +26402,12 @@ async function restoreScopeOwnerAcceptedExecution(scope) {
|
|
|
25221
26402
|
const resultIndex = latestResult === void 0 ? -1 : owned.indexOf(latestResult);
|
|
25222
26403
|
const result = owned[resultIndex];
|
|
25223
26404
|
if (inputIndex < 0 || resultIndex <= inputIndex || result?.kind !== "execution-result") throw new ValidationError$1("scope owner has no accepted retained invocation");
|
|
25224
|
-
const receiptEvent = owned.slice(0, resultIndex).reverse().find((event) => event.kind === "materialized");
|
|
26405
|
+
const receiptEvent = owned.slice(0, resultIndex).reverse().find((event) => event.kind === "materialized" || event.kind === "seat-materialized");
|
|
25225
26406
|
const bindingEvent = owned.slice(inputIndex, resultIndex).reverse().find((event) => event.kind === "execution-bound");
|
|
25226
|
-
if (receiptEvent?.kind !== "materialized" || bindingEvent?.kind !== "execution-bound") throw new ValidationError$1("accepted scope owner result has no preceding materialization evidence");
|
|
26407
|
+
if (receiptEvent?.kind !== "materialized" && receiptEvent?.kind !== "seat-materialized" || bindingEvent?.kind !== "execution-bound") throw new ValidationError$1("accepted scope owner result has no preceding materialization evidence");
|
|
25227
26408
|
const { receipt } = receiptEvent;
|
|
25228
26409
|
const { binding } = bindingEvent;
|
|
25229
|
-
if (receipt.status !== "known" || binding.status !== "known" || receipt.runtime !== state.runtime || receipt.authoredProfileDigest !== state.authoredProfileDigest || binding.materializationReceiptDigest !== canonicalCandidateDigest(receipt) || state.prior !== void 0 && canonicalCandidateDigest(state.prior) !== canonicalCandidateDigest(receipt)) throw new ValidationError$1("accepted scope owner materialization does not match the resumed owner");
|
|
26410
|
+
if (receipt.status !== "known" || binding.status !== "known" || receipt.runtime !== state.runtime || receipt.authoredProfileDigest !== state.authoredProfileDigest || binding.materializationReceiptDigest !== canonicalCandidateDigest(receipt) || (state.receipt ?? state.prior) !== void 0 && canonicalCandidateDigest(state.receipt ?? state.prior) !== canonicalCandidateDigest(receipt)) throw new ValidationError$1("accepted scope owner materialization does not match the resumed owner");
|
|
25230
26411
|
assertValidSpend(result.spent, "accepted scope owner result");
|
|
25231
26412
|
const output = await state.blobs.get(result.outRef);
|
|
25232
26413
|
if (output === void 0 || contentAddress(output) !== result.outRef) throw new ValidationError$1("accepted scope owner output is missing or corrupt");
|
|
@@ -25318,6 +26499,18 @@ async function appendOwnerMaterialization(state, receipt, binding) {
|
|
|
25318
26499
|
state.receipt = receipt;
|
|
25319
26500
|
await appendOwnerBinding(state, binding);
|
|
25320
26501
|
}
|
|
26502
|
+
async function appendOwnerSeatMaterialization(state, segmentIndex, receipt, binding) {
|
|
26503
|
+
await appendOwnerEvidence(state, {
|
|
26504
|
+
kind: "seat-materialized",
|
|
26505
|
+
id: state.nodeId,
|
|
26506
|
+
segmentIndex,
|
|
26507
|
+
receipt,
|
|
26508
|
+
seq: segmentIndex,
|
|
26509
|
+
at: new Date(state.now()).toISOString()
|
|
26510
|
+
});
|
|
26511
|
+
state.receipt = receipt;
|
|
26512
|
+
await appendOwnerBinding(state, binding);
|
|
26513
|
+
}
|
|
25321
26514
|
async function appendOwnerBinding(state, binding) {
|
|
25322
26515
|
await appendOwnerEvidence(state, {
|
|
25323
26516
|
kind: "execution-bound",
|
|
@@ -25332,10 +26525,10 @@ async function appendOwnerEvidence(state, event) {
|
|
|
25332
26525
|
for (const root of /* @__PURE__ */ new Set([state.root, state.retainedRoot])) await appendExactMaterializationEvent(state.journal, root, event);
|
|
25333
26526
|
}
|
|
25334
26527
|
async function appendExactMaterializationEvent(journal, root, event) {
|
|
25335
|
-
const prior = (await journal.loadTree(root))?.find((item) => item.id === event.id && (event.kind === "materialized" ? item.kind === "materialized" : item.kind === "execution-bound" && item.binding.attemptId === event.binding.attemptId));
|
|
25336
|
-
if (prior?.kind === "materialized" || prior?.kind === "execution-bound") {
|
|
25337
|
-
const previous = prior.kind === "
|
|
25338
|
-
const next = event.kind === "
|
|
26528
|
+
const prior = (await journal.loadTree(root))?.find((item) => item.id === event.id && (event.kind === "materialized" ? item.kind === "materialized" : event.kind === "seat-materialized" ? item.kind === "seat-materialized" && item.segmentIndex === event.segmentIndex : item.kind === "execution-bound" && item.binding.attemptId === event.binding.attemptId));
|
|
26529
|
+
if (prior?.kind === "materialized" || prior?.kind === "seat-materialized" || prior?.kind === "execution-bound") {
|
|
26530
|
+
const previous = prior.kind === "execution-bound" ? prior.binding : prior.receipt;
|
|
26531
|
+
const next = event.kind === "execution-bound" ? event.binding : event.receipt;
|
|
25339
26532
|
if (canonicalCandidateDigest(previous) !== canonicalCandidateDigest(next)) throw new ValidationError$1("scope execution evidence conflicts with its committed record");
|
|
25340
26533
|
return;
|
|
25341
26534
|
}
|
|
@@ -26213,7 +27406,7 @@ function isInfraError(err) {
|
|
|
26213
27406
|
* journal so the executor can run its `act` inside a nested scope. `act` fails loud if
|
|
26214
27407
|
* called directly — a driver child runs THROUGH its nested-scope executor, never as a root.
|
|
26215
27408
|
*/
|
|
26216
|
-
function driverChild(profile, driver, journal, execution, acceptedSubmission, recoverExecutor, unassessedOutput) {
|
|
27409
|
+
function driverChild(profile, driver, journal, execution, acceptedSubmission, recoverExecutor, unassessedOutput, leafDeliverable) {
|
|
26217
27410
|
const name = profile.name ?? driver.name;
|
|
26218
27411
|
const traceSource = driver.traceSource;
|
|
26219
27412
|
const progress = driver.progress;
|
|
@@ -26228,6 +27421,7 @@ function driverChild(profile, driver, journal, execution, acceptedSubmission, re
|
|
|
26228
27421
|
journal,
|
|
26229
27422
|
...acceptedSubmission ? { acceptedSubmission } : {},
|
|
26230
27423
|
...unassessedOutput ? { unassessedOutput } : {},
|
|
27424
|
+
...leafDeliverable ? { leafDeliverable } : {},
|
|
26231
27425
|
...recoverExecutor ? { recoverExecutor } : {},
|
|
26232
27426
|
...traceSource ? { traceSource } : {},
|
|
26233
27427
|
...progress ? { progress } : {},
|
|
@@ -26435,7 +27629,7 @@ function withDriverExecutor(base) {
|
|
|
26435
27629
|
const spec = executableAgentSpecSnapshot(rawSpec, "driver executor registry");
|
|
26436
27630
|
if (spec.driverRuntime === "driver" && !spec.executor) return {
|
|
26437
27631
|
succeeded: true,
|
|
26438
|
-
value: ((_ignored, context) => driverExecutorFactory(spec, context))
|
|
27632
|
+
value: ((_ignored, context) => isDriverSpec(spec) && spec.leafDeliverable ? attestNestedDriverTreeOwner(gateOnDeliverable(driverExecutorFactory(spec, context), spec.leafDeliverable)) : driverExecutorFactory(spec, context))
|
|
26439
27633
|
};
|
|
26440
27634
|
const resolved = base.resolve(spec);
|
|
26441
27635
|
if (!resolved.succeeded) return resolved;
|
|
@@ -26454,623 +27648,125 @@ async function closeNestedScope(scope, controller, reason) {
|
|
|
26454
27648
|
}
|
|
26455
27649
|
/** The nested tree's full event list — the one evidence the spend, verdict, AND driver-inference
|
|
26456
27650
|
* roll-ups read off the same journal the supervisor sums. */
|
|
26457
|
-
async function loadTreeEvents(journal, nestedRoot) {
|
|
26458
|
-
const events = await journal.loadTree(nestedRoot);
|
|
26459
|
-
if (events === void 0) throw new ValidationError$1(`driverExecutor: nested tree '${nestedRoot}' missing from the journal after run (corrupted log)`);
|
|
26460
|
-
return events;
|
|
26461
|
-
}
|
|
26462
|
-
function isSettled(ev) {
|
|
26463
|
-
return ev.kind === "settled" || ev.kind === "cancelled" && ev.spent !== void 0;
|
|
26464
|
-
}
|
|
26465
|
-
/** Sum the conserved spend over the nested tree's settled events — the honest per-channel
|
|
26466
|
-
* roll-up of the whole sub-tree's child WORK. */
|
|
26467
|
-
function sumSpend(settled) {
|
|
26468
|
-
return settled.reduce((total, ev) => addSpend(total, ev.spent), zeroSpend());
|
|
26469
|
-
}
|
|
26470
|
-
/** Sum the nested tree's `metered` events — the sub-tree's whole driver INFERENCE (this driver's
|
|
26471
|
-
* own turns + any sub-driver inference already re-homed into this tree). Re-homed up to the parent
|
|
26472
|
-
* as one `metered` event; never reconciled (already pool-debited live via `observe`). */
|
|
26473
|
-
function sumMetered(events) {
|
|
26474
|
-
return events.reduce((total, ev) => ev.kind === "metered" ? addSpend(total, ev.spend) : total, zeroSpend());
|
|
26475
|
-
}
|
|
26476
|
-
/** An all-zero spend that carries an UNKNOWN marker counts as non-zero: a sub-driver whose turns
|
|
26477
|
-
* went unmeasured did real work, and dropping its `metered` event here would re-hide upstream the
|
|
26478
|
-
* very turn the driver refused to skip. */
|
|
26479
|
-
function isNonZeroSpend(s) {
|
|
26480
|
-
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.keys(s.resources ?? {}).length > 0;
|
|
26481
|
-
}
|
|
26482
|
-
/** A spend, or `undefined` when it is all-zero — so `metered()` returns undefined for a driver
|
|
26483
|
-
* whose sub-tree did no inference (and the parent journals no empty `metered` event). */
|
|
26484
|
-
function nonZeroOrUndef(s) {
|
|
26485
|
-
return isNonZeroSpend(s) ? s : void 0;
|
|
26486
|
-
}
|
|
26487
|
-
/** Sum the nested tree's metered events, tolerating a missing tree (a crash before `beginTree`
|
|
26488
|
-
* landed) — never throw here, or it would mask the original `act` error on the crash path. */
|
|
26489
|
-
async function safeRollup(journal, nestedRoot) {
|
|
26490
|
-
try {
|
|
26491
|
-
const events = await loadTreeEvents(journal, nestedRoot);
|
|
26492
|
-
const childWork = sumSpend(events.filter(isSettled));
|
|
26493
|
-
const metered = nonZeroOrUndef(sumMetered(events));
|
|
26494
|
-
return {
|
|
26495
|
-
metered,
|
|
26496
|
-
accounting: {
|
|
26497
|
-
reported: childWork,
|
|
26498
|
-
reservation: addSpend(childWork, metered ?? zeroSpend())
|
|
26499
|
-
}
|
|
26500
|
-
};
|
|
26501
|
-
} catch {
|
|
26502
|
-
return;
|
|
26503
|
-
}
|
|
26504
|
-
}
|
|
26505
|
-
/** Derive the driver child's delivery verdict from an accepted direct submission, or from its
|
|
26506
|
-
* DIRECT children's settlements and the manager's actual finalized output: `valid` iff the
|
|
26507
|
-
* manager's own assignment check accepted a submission, or a direct child delivered AND the
|
|
26508
|
-
* finalizer returned a defined output;
|
|
26509
|
-
* `score` = the best delivered score. Returns `undefined` when no child settled at all (the
|
|
26510
|
-
* driver itself produced nothing to bubble a verdict from). Fail-closed: a child whose verdict
|
|
26511
|
-
* carried no `valid` counts as not-delivered. */
|
|
26512
|
-
function deriveDeliveryVerdict(settled, finalizedOutput, acceptedSubmission) {
|
|
26513
|
-
if (acceptedSubmission) return {
|
|
26514
|
-
valid: true,
|
|
26515
|
-
score: 1
|
|
26516
|
-
};
|
|
26517
|
-
let sawChild = false;
|
|
26518
|
-
let anyValid = false;
|
|
26519
|
-
let bestValidScore;
|
|
26520
|
-
let bestDoneScore;
|
|
26521
|
-
for (const ev of settled) {
|
|
26522
|
-
sawChild = true;
|
|
26523
|
-
if (ev.status !== "done") continue;
|
|
26524
|
-
const score = ev.verdict?.score;
|
|
26525
|
-
if (score !== void 0 && (bestDoneScore === void 0 || score > bestDoneScore)) bestDoneScore = score;
|
|
26526
|
-
if (ev.verdict?.valid === true) {
|
|
26527
|
-
anyValid = true;
|
|
26528
|
-
if (score !== void 0 && (bestValidScore === void 0 || score > bestValidScore)) bestValidScore = score;
|
|
26529
|
-
}
|
|
26530
|
-
}
|
|
26531
|
-
if (!sawChild) return void 0;
|
|
26532
|
-
const accepted = anyValid && finalizedOutput !== void 0;
|
|
26533
|
-
return {
|
|
26534
|
-
valid: accepted,
|
|
26535
|
-
score: accepted ? bestValidScore ?? 1 : bestDoneScore ?? 0
|
|
26536
|
-
};
|
|
26537
|
-
}
|
|
26538
|
-
function readNestedScopeSeam(ctx) {
|
|
26539
|
-
const seam = ctx.seams[nestedScopeSeamKey];
|
|
26540
|
-
if (!seam || typeof seam.mount !== "function") throw new ValidationError$1(`driverExecutor: missing required seam "${nestedScopeSeamKey}" — a driver child must be spawned through a Scope that seeds it (the keystone scope does)`);
|
|
26541
|
-
return seam;
|
|
26542
|
-
}
|
|
26543
|
-
function isAgent(value) {
|
|
26544
|
-
return typeof value === "object" && value !== null && typeof value.act === "function" && typeof value.name === "string";
|
|
26545
|
-
}
|
|
26546
|
-
/** Parent journals may already contain an interrupted driver's inference prefix. */
|
|
26547
|
-
function unreportedSpend(total, prior) {
|
|
26548
|
-
const tokens = { ...total.tokens };
|
|
26549
|
-
for (const key of [
|
|
26550
|
-
"input",
|
|
26551
|
-
"output",
|
|
26552
|
-
"freshInput",
|
|
26553
|
-
"cacheRead",
|
|
26554
|
-
"cacheWrite"
|
|
26555
|
-
]) {
|
|
26556
|
-
const value = total.tokens[key];
|
|
26557
|
-
if (value !== void 0) tokens[key] = Math.max(0, value - (prior.tokens[key] ?? 0));
|
|
26558
|
-
}
|
|
26559
|
-
return {
|
|
26560
|
-
...total,
|
|
26561
|
-
...addResourceSpend(total.resources === void 0 ? void 0 : Object.fromEntries(Object.entries(total.resources).map(([name, value]) => {
|
|
26562
|
-
const previous = prior.resources?.[name];
|
|
26563
|
-
if (previous && previous.unit !== value.unit) throw new ValidationError$1(`resource ${name}: unit mismatch`);
|
|
26564
|
-
return [name, {
|
|
26565
|
-
...value,
|
|
26566
|
-
amount: Math.max(0, value.amount - (previous?.amount ?? 0)),
|
|
26567
|
-
known: value.known && (previous?.known ?? true)
|
|
26568
|
-
}];
|
|
26569
|
-
}))),
|
|
26570
|
-
iterations: Math.max(0, total.iterations - prior.iterations),
|
|
26571
|
-
tokens,
|
|
26572
|
-
usd: Math.max(0, total.usd - prior.usd),
|
|
26573
|
-
ms: Math.max(0, total.ms - prior.ms),
|
|
26574
|
-
...total.usdEstimated === void 0 ? {} : { usdEstimated: Math.max(0, total.usdEstimated - (prior.usdEstimated ?? 0)) },
|
|
26575
|
-
...total.boxMinutes === void 0 ? {} : { boxMinutes: Math.max(0, total.boxMinutes - (prior.boxMinutes ?? 0)) }
|
|
26576
|
-
};
|
|
26577
|
-
}
|
|
26578
|
-
//#endregion
|
|
26579
|
-
//#region src/runtime/supervise/continuation.ts
|
|
26580
|
-
/**
|
|
26581
|
-
* The continuation: what a manager with a completion check is told when its turn ends and the
|
|
26582
|
-
* check is unmet, and the one rule that decides whether it is sent back.
|
|
26583
|
-
*
|
|
26584
|
-
* Measured on discovery-lab before this module (2026-09-24, 670 settled runs, 1,596 re-entries):
|
|
26585
|
-
* 1,565 re-entries followed a failed turn and resent the original task, and 31 carried note text.
|
|
26586
|
-
* The note Runtime wrote itself held seven fixed lines and no field for the check's verdict. Of 40
|
|
26587
|
-
* episodes where the director ended on its own and the outside check disagreed, 33 were told
|
|
26588
|
-
* nothing before they ended. 100 of 236 resubmits sent the identical packet. So the director
|
|
26589
|
-
* mostly graded itself, and when it was wrong it heard nothing it could use.
|
|
26590
|
-
*
|
|
26591
|
-
* The design (discovery `docs/38-one-loop-and-continuation.md`) makes three things single, and
|
|
26592
|
-
* this module holds the part Runtime owns:
|
|
26593
|
-
*
|
|
26594
|
-
* - ONE CHECK. A check reading is a {@link CheckVerdict}: pass, the items it read, the composite,
|
|
26595
|
-
* and one `FAIL <item> <where>: <reason>` line per failed item. The same check decides
|
|
26596
|
-
* `submit_result`, runs when a turn ends without an accepted result, scores the finished run,
|
|
26597
|
-
* and ranks versions. A check that could not run gives no verdict ({@link CheckUnavailableError}),
|
|
26598
|
-
* and the loop pauses instead of blaming the director.
|
|
26599
|
-
* - ONE RESEND RULE. {@link ContinuationPolicy} is required for a manager with a check. It holds a
|
|
26600
|
-
* deadline and `maxBarren`, the re-entered turns in a row that may end without progress. There
|
|
26601
|
-
* is no re-prompt count and no default: a retry default in source is a research decision.
|
|
26602
|
-
* - ONE NOTE. {@link composeContinuationNote} writes it. Runtime owns the facts and their order;
|
|
26603
|
-
* the {@link ContinuationProfile} owns every instruction word, so an optimizer can change the
|
|
26604
|
-
* words without touching code. A play may append a section, never remove or rewrite one.
|
|
26605
|
-
*
|
|
26606
|
-
* Evidence behind the note's shape: located errors help (given the first mistake's true location,
|
|
26607
|
-
* fix rates rose 23.5 to 43.9 points, Tyen 2023); unsupported pressure does not (threats and tips
|
|
26608
|
-
* had no significant effect, Wharton 2025; challenges without evidence flipped correct answers,
|
|
26609
|
-
* FlipFlop); repeated summaries drop detail while itemized additions do not (ACE). So the note is
|
|
26610
|
-
* a list of located facts, findings are added and marked resolved rather than rewritten, and the
|
|
26611
|
-
* profile, not this file, decides how blunt the words are.
|
|
26612
|
-
*/
|
|
26613
|
-
/**
|
|
26614
|
-
* The check could not run: its box did not start, its judge's provider refused, its program
|
|
26615
|
-
* crashed. It is not a verdict on the result. Inside a run the loop pauses, as it does for an
|
|
26616
|
-
* unavailable upstream, and the refusal tells the director its result was not judged.
|
|
26617
|
-
*/
|
|
26618
|
-
var CheckUnavailableError = class extends Error {
|
|
26619
|
-
constructor(message, options) {
|
|
26620
|
-
super(message, options);
|
|
26621
|
-
this.name = "CheckUnavailableError";
|
|
26622
|
-
}
|
|
26623
|
-
};
|
|
26624
|
-
/** Read a check function's return value into a verdict. Anything but `true` or a passing verdict
|
|
26625
|
-
* is a failure: a check fails closed. */
|
|
26626
|
-
function checkVerdictOf(outcome) {
|
|
26627
|
-
if (outcome === true) return { pass: true };
|
|
26628
|
-
if (typeof outcome !== "object" || outcome === null || Array.isArray(outcome)) return { pass: false };
|
|
26629
|
-
const raw = outcome;
|
|
26630
|
-
const items = typeof raw.items === "object" && raw.items !== null && !Array.isArray(raw.items) ? Object.fromEntries(Object.entries(raw.items).filter((entry) => typeof entry[1] === "number" && Number.isFinite(entry[1]))) : void 0;
|
|
26631
|
-
const failures = Array.isArray(raw.failures) ? raw.failures.filter((line) => typeof line === "string" && line.trim() !== "") : void 0;
|
|
26632
|
-
return Object.freeze({
|
|
26633
|
-
pass: raw.pass === true,
|
|
26634
|
-
...items === void 0 ? {} : { items: Object.freeze(items) },
|
|
26635
|
-
...finite(raw.composite) ? { composite: raw.composite } : {},
|
|
26636
|
-
...finite(raw.threshold) ? { threshold: raw.threshold } : {},
|
|
26637
|
-
...failures === void 0 ? {} : { failures: Object.freeze(failures) },
|
|
26638
|
-
...typeof raw.review === "string" && raw.review.trim() !== "" ? { review: raw.review.trim() } : {}
|
|
26639
|
-
});
|
|
26640
|
-
}
|
|
26641
|
-
/**
|
|
26642
|
-
* Read an agent-eval `JudgeScore` from a check program into a verdict.
|
|
26643
|
-
*
|
|
26644
|
-
* Each dimension is a checked item. `notes` lines that start with `FAIL ` are the failures; the
|
|
26645
|
-
* remaining lines are the check's review. The verdict passes when `composite >= threshold` and the
|
|
26646
|
-
* score is not marked `failed`; a `failed` score means the judge itself did not run, which is
|
|
26647
|
-
* {@link CheckUnavailableError}, not a verdict.
|
|
26648
|
-
*/
|
|
26649
|
-
function verdictFromJudgeScore(score, threshold) {
|
|
26650
|
-
if (score.failed === true) throw new CheckUnavailableError(`the check's judge did not run: ${score.notes}`);
|
|
26651
|
-
const lines = score.notes.split("\n").map((line) => line.trimEnd());
|
|
26652
|
-
const failures = lines.filter((line) => line.startsWith("FAIL "));
|
|
26653
|
-
const review = lines.filter((line) => !line.startsWith("FAIL ") && line.trim() !== "").join("\n");
|
|
26654
|
-
return checkVerdictOf({
|
|
26655
|
-
pass: score.composite >= threshold,
|
|
26656
|
-
items: score.dimensions,
|
|
26657
|
-
composite: score.composite,
|
|
26658
|
-
threshold,
|
|
26659
|
-
failures,
|
|
26660
|
-
...review === "" ? {} : { review }
|
|
26661
|
-
});
|
|
26662
|
-
}
|
|
26663
|
-
/** Items that fail in `verdict`: every item below 1, or the named items of its FAIL lines. */
|
|
26664
|
-
function failedItems(verdict) {
|
|
26665
|
-
if (verdict.items !== void 0) return Object.entries(verdict.items).filter(([, value]) => value < 1).map(([name]) => name);
|
|
26666
|
-
return (verdict.failures ?? []).map((line) => line.slice(5).split(/[\s:]/u)[0] ?? "");
|
|
27651
|
+
async function loadTreeEvents(journal, nestedRoot) {
|
|
27652
|
+
const events = await journal.loadTree(nestedRoot);
|
|
27653
|
+
if (events === void 0) throw new ValidationError$1(`driverExecutor: nested tree '${nestedRoot}' missing from the journal after run (corrupted log)`);
|
|
27654
|
+
return events;
|
|
26667
27655
|
}
|
|
26668
|
-
|
|
26669
|
-
|
|
26670
|
-
return Object.entries(verdict.items ?? {}).filter(([, value]) => value >= 1).map(([name]) => name);
|
|
27656
|
+
function isSettled(ev) {
|
|
27657
|
+
return ev.kind === "settled" || ev.kind === "cancelled" && ev.spent !== void 0;
|
|
26671
27658
|
}
|
|
26672
|
-
/**
|
|
26673
|
-
|
|
26674
|
-
|
|
26675
|
-
|
|
26676
|
-
if (typeof deadlineMs !== "number" || !Number.isFinite(deadlineMs) || deadlineMs <= 0) throw new ValidationError$1(`${context}: continuation.deadline must be a finite time (epoch ms or ISO 8601)`);
|
|
26677
|
-
if (!Number.isInteger(policy.maxBarren) || policy.maxBarren < 1) throw new ValidationError$1(`${context}: continuation.maxBarren must be an integer >= 1`);
|
|
26678
|
-
if (policy.failures !== "verbatim" && policy.failures !== "off") throw new ValidationError$1(`${context}: continuation.failures must be 'verbatim' or 'off'`);
|
|
26679
|
-
if (policy.panel !== "on" && policy.panel !== "off") throw new ValidationError$1(`${context}: continuation.panel must be 'on' or 'off'`);
|
|
26680
|
-
if (policy.bar !== "on" && policy.bar !== "off") throw new ValidationError$1(`${context}: continuation.bar must be 'on' or 'off'`);
|
|
26681
|
-
if (policy.panel === "on") {
|
|
26682
|
-
if (typeof policy.runPanel !== "function") throw new ValidationError$1(`${context}: continuation.panel 'on' needs runPanel`);
|
|
26683
|
-
const usd = policy.panelUsd;
|
|
26684
|
-
if (usd === void 0 || !finite(usd.perContinuation) || !finite(usd.perRun) || usd.perContinuation <= 0 || usd.perRun < usd.perContinuation) throw new ValidationError$1(`${context}: continuation.panel 'on' needs panelUsd with 0 < perContinuation <= perRun`);
|
|
26685
|
-
}
|
|
26686
|
-
if (policy.append !== void 0 && typeof policy.append !== "function") throw new ValidationError$1(`${context}: continuation.append must be a function`);
|
|
26687
|
-
admitContinuationProfile(policy.profile, context);
|
|
26688
|
-
return deadlineMs;
|
|
27659
|
+
/** Sum the conserved spend over the nested tree's settled events — the honest per-channel
|
|
27660
|
+
* roll-up of the whole sub-tree's child WORK. */
|
|
27661
|
+
function sumSpend(settled) {
|
|
27662
|
+
return settled.reduce((total, ev) => addSpend(total, ev.spent), zeroSpend());
|
|
26689
27663
|
}
|
|
26690
|
-
/**
|
|
26691
|
-
|
|
26692
|
-
|
|
26693
|
-
|
|
26694
|
-
"
|
|
26695
|
-
"total",
|
|
26696
|
-
"reads",
|
|
26697
|
-
"owed",
|
|
26698
|
-
"settled",
|
|
26699
|
-
"delivered",
|
|
26700
|
-
"continuation"
|
|
26701
|
-
];
|
|
26702
|
-
const factNames = new Set(CONTINUATION_FACTS);
|
|
26703
|
-
const placeholder = /\{([a-zA-Z]+)\}/gu;
|
|
26704
|
-
function admitContinuationProfile(profile, context) {
|
|
26705
|
-
if (typeof profile !== "object" || profile === null) throw new ValidationError$1(`${context}: continuation.profile must be an object`);
|
|
26706
|
-
if (typeof profile.id !== "string" || profile.id.trim() === "") throw new ValidationError$1(`${context}: continuation.profile.id must be non-empty`);
|
|
26707
|
-
const texts = [
|
|
26708
|
-
["opening", profile.opening],
|
|
26709
|
-
["plan", profile.plan],
|
|
26710
|
-
["rules", profile.rules],
|
|
26711
|
-
...Object.entries(profile.headings ?? {}).map(([key, value]) => [`headings.${key}`, value]),
|
|
26712
|
-
...profile.barRule === void 0 ? [] : [["barRule", profile.barRule]]
|
|
26713
|
-
];
|
|
26714
|
-
for (const key of [
|
|
26715
|
-
"failures",
|
|
26716
|
-
"protected",
|
|
26717
|
-
"changed",
|
|
26718
|
-
"findings",
|
|
26719
|
-
"bar",
|
|
26720
|
-
"plan",
|
|
26721
|
-
"rules"
|
|
26722
|
-
]) if (typeof profile.headings?.[key] !== "string") throw new ValidationError$1(`${context}: continuation.profile.headings.${key} must be a string`);
|
|
26723
|
-
for (const [key, text] of texts) {
|
|
26724
|
-
if (typeof text !== "string") throw new ValidationError$1(`${context}: continuation.profile.${key} must be a string`);
|
|
26725
|
-
for (const match of text.matchAll(placeholder)) if (!factNames.has(match[1] ?? "")) throw new ValidationError$1(`${context}: continuation.profile.${key} names {${match[1]}}, which is not a fact Runtime supplies (${CONTINUATION_FACTS.join(", ")})`);
|
|
26726
|
-
}
|
|
26727
|
-
if (profile.review !== void 0) {
|
|
26728
|
-
if (typeof profile.review !== "string" || profile.review.trim() === "") throw new ValidationError$1(`${context}: continuation.profile.review must be non-empty`);
|
|
26729
|
-
for (const match of profile.review.matchAll(placeholder)) if (![
|
|
26730
|
-
"version",
|
|
26731
|
-
"path",
|
|
26732
|
-
"score"
|
|
26733
|
-
].includes(match[1] ?? "")) throw new ValidationError$1(`${context}: continuation.profile.review names {${match[1]}}; it may name {version}, {path} and {score}`);
|
|
26734
|
-
}
|
|
26735
|
-
if (profile.opening.trim() === "") throw new ValidationError$1(`${context}: continuation.profile.opening must be non-empty`);
|
|
26736
|
-
for (const [index, question] of (profile.questions ?? []).entries()) if (typeof question !== "string" || question.trim() === "") throw new ValidationError$1(`${context}: continuation.profile.questions[${index}] must be a non-empty string`);
|
|
27664
|
+
/** Sum the nested tree's `metered` events — the sub-tree's whole driver INFERENCE (this driver's
|
|
27665
|
+
* own turns + any sub-driver inference already re-homed into this tree). Re-homed up to the parent
|
|
27666
|
+
* as one `metered` event; never reconciled (already pool-debited live via `observe`). */
|
|
27667
|
+
function sumMetered(events) {
|
|
27668
|
+
return events.reduce((total, ev) => ev.kind === "metered" ? addSpend(total, ev.spend) : total, zeroSpend());
|
|
26737
27669
|
}
|
|
26738
|
-
/**
|
|
26739
|
-
*
|
|
26740
|
-
*
|
|
26741
|
-
function
|
|
26742
|
-
|
|
26743
|
-
if (finding.citations.length === 0) return false;
|
|
26744
|
-
if (!finding.citations.every((citation) => citation.resolved)) return false;
|
|
26745
|
-
return new Set(finding.citations.map((citation) => citation.uri)).size >= 2;
|
|
27670
|
+
/** An all-zero spend that carries an UNKNOWN marker counts as non-zero: a sub-driver whose turns
|
|
27671
|
+
* went unmeasured did real work, and dropping its `metered` event here would re-hide upstream the
|
|
27672
|
+
* very turn the driver refused to skip. */
|
|
27673
|
+
function isNonZeroSpend(s) {
|
|
27674
|
+
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.keys(s.resources ?? {}).length > 0;
|
|
26746
27675
|
}
|
|
26747
|
-
/**
|
|
26748
|
-
|
|
26749
|
-
|
|
26750
|
-
|
|
26751
|
-
* within the note's cap. Earlier findings stay as they were written (ACE: itemized additions keep
|
|
26752
|
-
* detail that repeated rewrites lose); a finding whose items all pass now is marked resolved.
|
|
26753
|
-
*/
|
|
26754
|
-
function distillFindings(prior, proposed, failing, continuation) {
|
|
26755
|
-
const key = (claim) => claim.toLowerCase().replace(/\s+/gu, " ").trim();
|
|
26756
|
-
const known = new Set(prior.map((finding) => key(finding.claim)));
|
|
26757
|
-
const carried = prior.map((finding) => finding.resolvedAt === void 0 && (finding.items?.length ?? 0) > 0 && (finding.items ?? []).every((item) => !failing.has(item)) ? {
|
|
26758
|
-
...finding,
|
|
26759
|
-
resolvedAt: continuation
|
|
26760
|
-
} : finding);
|
|
26761
|
-
const fresh = proposed.filter(admitFinding).filter((finding) => {
|
|
26762
|
-
const k = key(finding.claim);
|
|
26763
|
-
if (known.has(k)) return false;
|
|
26764
|
-
known.add(k);
|
|
26765
|
-
return true;
|
|
26766
|
-
}).map((finding) => ({
|
|
26767
|
-
finding,
|
|
26768
|
-
links: (finding.items ?? []).filter((item) => failing.has(item)).length
|
|
26769
|
-
})).sort((a, b) => b.links - a.links);
|
|
26770
|
-
const out = [...carried];
|
|
26771
|
-
let used = carried.filter((finding) => finding.resolvedAt === void 0).reduce((sum, finding) => sum + finding.claim.length, 0);
|
|
26772
|
-
for (const { finding } of fresh) {
|
|
26773
|
-
if (used + finding.claim.length > FINDINGS_CHAR_CAP) break;
|
|
26774
|
-
used += finding.claim.length;
|
|
26775
|
-
out.push({
|
|
26776
|
-
...finding,
|
|
26777
|
-
admittedAt: continuation
|
|
26778
|
-
});
|
|
26779
|
-
}
|
|
26780
|
-
return out;
|
|
27676
|
+
/** A spend, or `undefined` when it is all-zero — so `metered()` returns undefined for a driver
|
|
27677
|
+
* whose sub-tree did no inference (and the parent journals no empty `metered` event). */
|
|
27678
|
+
function nonZeroOrUndef(s) {
|
|
27679
|
+
return isNonZeroSpend(s) ? s : void 0;
|
|
26781
27680
|
}
|
|
26782
|
-
/**
|
|
26783
|
-
|
|
26784
|
-
|
|
26785
|
-
|
|
26786
|
-
const
|
|
26787
|
-
const
|
|
26788
|
-
|
|
26789
|
-
|
|
26790
|
-
|
|
26791
|
-
|
|
26792
|
-
|
|
26793
|
-
|
|
26794
|
-
|
|
27681
|
+
/** Sum the nested tree's metered events, tolerating a missing tree (a crash before `beginTree`
|
|
27682
|
+
* landed) — never throw here, or it would mask the original `act` error on the crash path. */
|
|
27683
|
+
async function safeRollup(journal, nestedRoot) {
|
|
27684
|
+
try {
|
|
27685
|
+
const events = await loadTreeEvents(journal, nestedRoot);
|
|
27686
|
+
const childWork = sumSpend(events.filter(isSettled));
|
|
27687
|
+
const metered = nonZeroOrUndef(sumMetered(events));
|
|
27688
|
+
return {
|
|
27689
|
+
metered,
|
|
27690
|
+
accounting: {
|
|
27691
|
+
reported: childWork,
|
|
27692
|
+
reservation: addSpend(childWork, metered ?? zeroSpend())
|
|
27693
|
+
}
|
|
27694
|
+
};
|
|
27695
|
+
} catch {
|
|
27696
|
+
return;
|
|
26795
27697
|
}
|
|
26796
|
-
return out;
|
|
26797
27698
|
}
|
|
26798
|
-
/**
|
|
26799
|
-
*
|
|
26800
|
-
*
|
|
26801
|
-
|
|
26802
|
-
|
|
26803
|
-
|
|
26804
|
-
|
|
26805
|
-
|
|
26806
|
-
|
|
26807
|
-
|
|
26808
|
-
|
|
26809
|
-
threshold: verdict?.threshold === void 0 ? "unknown" : format(verdict.threshold),
|
|
26810
|
-
failed: verdict === void 0 || total === 0 && (verdict.failures?.length ?? 0) === 0 ? "unknown" : String(failing.length),
|
|
26811
|
-
total: total === 0 ? "unknown" : String(total),
|
|
26812
|
-
reads: String(input.reads),
|
|
26813
|
-
owed: input.owed?.trim() || "not described",
|
|
26814
|
-
settled: String(input.progress.settledCount),
|
|
26815
|
-
delivered: String(input.progress.deliveredCount ?? 0),
|
|
26816
|
-
continuation: String(input.continuation)
|
|
26817
|
-
};
|
|
26818
|
-
const fill = (text) => text.replace(placeholder, (_, name) => facts[name] ?? `{${name}}`);
|
|
26819
|
-
const sections = [verdict === void 0 ? "No check verdict is available yet." + (input.canSubmitResult === true ? " Submit a result through submit_result to request the check." : "") : verdict.pass ? "The check passes on the current state, but no result has been accepted." + (input.canSubmitResult === true ? " Call submit_result to request acceptance." : "") : fill(profile.opening).trim()];
|
|
26820
|
-
const section = (heading, body) => {
|
|
26821
|
-
if (body.length === 0) return;
|
|
26822
|
-
sections.push([...heading.trim() === "" ? [] : [`## ${fill(heading)}`], ...body].join("\n"));
|
|
27699
|
+
/** Derive the driver child's delivery verdict from an accepted direct submission, or from its
|
|
27700
|
+
* DIRECT children's settlements and the manager's actual finalized output: `valid` iff the
|
|
27701
|
+
* manager's own assignment check accepted a submission, or a direct child delivered AND the
|
|
27702
|
+
* finalizer returned a defined output;
|
|
27703
|
+
* `score` = the best delivered score. Returns `undefined` when no child settled at all (the
|
|
27704
|
+
* driver itself produced nothing to bubble a verdict from). Fail-closed: a child whose verdict
|
|
27705
|
+
* carried no `valid` counts as not-delivered. */
|
|
27706
|
+
function deriveDeliveryVerdict(settled, finalizedOutput, acceptedSubmission) {
|
|
27707
|
+
if (acceptedSubmission) return {
|
|
27708
|
+
valid: true,
|
|
27709
|
+
score: 1
|
|
26823
27710
|
};
|
|
26824
|
-
|
|
26825
|
-
|
|
26826
|
-
|
|
26827
|
-
|
|
26828
|
-
|
|
26829
|
-
|
|
26830
|
-
|
|
26831
|
-
|
|
26832
|
-
|
|
26833
|
-
|
|
26834
|
-
|
|
26835
|
-
|
|
26836
|
-
section(profile.headings.changed, [
|
|
26837
|
-
...fixed.length === 0 ? [] : [`Now passing: ${fixed.join(", ")}.`],
|
|
26838
|
-
...broke.length === 0 ? [] : [`Newly failing: ${broke.join(", ")}.`],
|
|
26839
|
-
...fixed.length === 0 && broke.length === 0 ? ["No item changed since the last note."] : [],
|
|
26840
|
-
...input.previous.composite !== void 0 && verdict.composite !== void 0 ? [`Composite: ${format(input.previous.composite)} then ${format(verdict.composite)}.`] : []
|
|
26841
|
-
]);
|
|
26842
|
-
}
|
|
26843
|
-
}
|
|
26844
|
-
if (input.findings !== void 0) {
|
|
26845
|
-
const open = input.findings.filter((finding) => finding.resolvedAt === void 0);
|
|
26846
|
-
const resolved = input.findings.filter((finding) => finding.resolvedAt === input.continuation);
|
|
26847
|
-
section(profile.headings.findings, [...open.map((finding) => `- [hypothesis] ${finding.claim}` + (finding.items?.length ? ` (items: ${finding.items.join(", ")})` : "") + ` Evidence: ${finding.citations.map((citation) => citation.uri).join(", ")}`), ...resolved.map((finding) => `- [resolved] ${finding.claim}`)]);
|
|
26848
|
-
}
|
|
26849
|
-
if (input.bar !== void 0 && verdict !== void 0) {
|
|
26850
|
-
const body = [];
|
|
26851
|
-
if (profile.barRule?.trim()) body.push(fill(profile.barRule).trim());
|
|
26852
|
-
for (const [item, value] of Object.entries(items)) {
|
|
26853
|
-
const best = input.bar.best?.verdict.items?.[item];
|
|
26854
|
-
body.push(`- ${item}: ${value >= 1 ? "passes" : `fails (${format(value)})`}` + (best === void 0 ? "" : ` / ${input.bar.best?.label}: ${best >= 1 ? "passes" : `fails (${format(best)})`}`));
|
|
26855
|
-
}
|
|
26856
|
-
if (input.bar.best?.verdict.composite !== void 0) body.push(`Best version ${input.bar.best.label}: composite ${format(input.bar.best.verdict.composite)}.`);
|
|
26857
|
-
if (input.bar.reference?.trim()) body.push(`Reference result: ${input.bar.reference.trim()}`);
|
|
26858
|
-
if (verdict.review?.trim()) body.push(`The check's review: ${verdict.review.trim()}`);
|
|
26859
|
-
section(profile.headings.bar, body);
|
|
26860
|
-
}
|
|
26861
|
-
section(profile.headings.plan, profile.plan.trim() === "" ? [] : [fill(profile.plan).trim()]);
|
|
26862
|
-
section(profile.headings.rules, [...profile.rules.trim() === "" ? [] : [fill(profile.rules).trim()], ...input.sealed ? ["The score comes from cases you cannot see."] : []]);
|
|
26863
|
-
for (const appended of input.appended ?? []) section(appended.heading, appended.text.trim() === "" ? [] : [appended.text.trim()]);
|
|
26864
|
-
return sections.join("\n\n");
|
|
26865
|
-
}
|
|
26866
|
-
function summarizeVerdict(verdict) {
|
|
26867
|
-
if (verdict === void 0) return null;
|
|
26868
|
-
const total = Object.keys(verdict.items ?? {}).length;
|
|
26869
|
-
return {
|
|
26870
|
-
pass: verdict.pass,
|
|
26871
|
-
...verdict.composite === void 0 ? {} : { composite: verdict.composite },
|
|
26872
|
-
...total === 0 ? {} : {
|
|
26873
|
-
failed: failedItems(verdict).length,
|
|
26874
|
-
total
|
|
27711
|
+
let sawChild = false;
|
|
27712
|
+
let anyValid = false;
|
|
27713
|
+
let bestValidScore;
|
|
27714
|
+
let bestDoneScore;
|
|
27715
|
+
for (const ev of settled) {
|
|
27716
|
+
sawChild = true;
|
|
27717
|
+
if (ev.status !== "done") continue;
|
|
27718
|
+
const score = ev.verdict?.score;
|
|
27719
|
+
if (score !== void 0 && (bestDoneScore === void 0 || score > bestDoneScore)) bestDoneScore = score;
|
|
27720
|
+
if (ev.verdict?.valid === true) {
|
|
27721
|
+
anyValid = true;
|
|
27722
|
+
if (score !== void 0 && (bestValidScore === void 0 || score > bestValidScore)) bestValidScore = score;
|
|
26875
27723
|
}
|
|
27724
|
+
}
|
|
27725
|
+
if (!sawChild) return void 0;
|
|
27726
|
+
const accepted = anyValid && finalizedOutput !== void 0;
|
|
27727
|
+
return {
|
|
27728
|
+
valid: accepted,
|
|
27729
|
+
score: accepted ? bestValidScore ?? 1 : bestDoneScore ?? 0
|
|
26876
27730
|
};
|
|
26877
27731
|
}
|
|
26878
|
-
function
|
|
26879
|
-
|
|
27732
|
+
function readNestedScopeSeam(ctx) {
|
|
27733
|
+
const seam = ctx.seams[nestedScopeSeamKey];
|
|
27734
|
+
if (!seam || typeof seam.mount !== "function") throw new ValidationError$1(`driverExecutor: missing required seam "${nestedScopeSeamKey}" — a driver child must be spawned through a Scope that seeds it (the keystone scope does)`);
|
|
27735
|
+
return seam;
|
|
26880
27736
|
}
|
|
26881
|
-
|
|
26882
|
-
|
|
26883
|
-
const CONTINUATIONS_DIR = "continuations";
|
|
26884
|
-
/**
|
|
26885
|
-
* Write one continuation's files under `<dir>/<n>/`: `note.md`, `verdict.json`, and
|
|
26886
|
-
* `panel.jsonl` when the panel ran. The director reads them through `read_continuation`, because
|
|
26887
|
-
* its box cannot read the driver's run directory.
|
|
26888
|
-
*/
|
|
26889
|
-
async function writeContinuationFiles(dir, continuation, files) {
|
|
26890
|
-
const at = join(dir, String(continuation));
|
|
26891
|
-
await mkdir(at, { recursive: true });
|
|
26892
|
-
await writeFile(join(at, "note.md"), `${files.note}\n`, "utf8");
|
|
26893
|
-
await writeFile(join(at, "verdict.json"), `${JSON.stringify(files.verdict ?? null, null, 2)}\n`, "utf8");
|
|
26894
|
-
if (files.panel !== void 0) await writeFile(join(at, "panel.jsonl"), files.panel.map((line) => JSON.stringify(line)).join("\n") + (files.panel.length ? "\n" : ""), "utf8");
|
|
27737
|
+
function isAgent(value) {
|
|
27738
|
+
return typeof value === "object" && value !== null && typeof value.act === "function" && typeof value.name === "string";
|
|
26895
27739
|
}
|
|
26896
|
-
/**
|
|
26897
|
-
function
|
|
26898
|
-
|
|
26899
|
-
for (const
|
|
26900
|
-
|
|
26901
|
-
|
|
27740
|
+
/** Parent journals may already contain an interrupted driver's inference prefix. */
|
|
27741
|
+
function unreportedSpend(total, prior) {
|
|
27742
|
+
const tokens = { ...total.tokens };
|
|
27743
|
+
for (const key of [
|
|
27744
|
+
"input",
|
|
27745
|
+
"output",
|
|
27746
|
+
"freshInput",
|
|
27747
|
+
"cacheRead",
|
|
27748
|
+
"cacheWrite"
|
|
27749
|
+
]) {
|
|
27750
|
+
const value = total.tokens[key];
|
|
27751
|
+
if (value !== void 0) tokens[key] = Math.max(0, value - (prior.tokens[key] ?? 0));
|
|
26902
27752
|
}
|
|
26903
|
-
return best;
|
|
26904
|
-
}
|
|
26905
|
-
function finite(value) {
|
|
26906
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
26907
|
-
}
|
|
26908
|
-
function format(value) {
|
|
26909
|
-
return Number.isInteger(value) ? String(value) : value.toFixed(3).replace(/0+$/u, "");
|
|
26910
|
-
}
|
|
26911
|
-
/** Build the continuation state for one manager with a check. */
|
|
26912
|
-
function createContinuationKeeper(input) {
|
|
26913
|
-
const { policy } = input;
|
|
26914
|
-
const failures = input.feedback === "pass-only" ? "off" : policy.failures;
|
|
26915
|
-
const entries = [];
|
|
26916
|
-
const notes = [];
|
|
26917
|
-
let findings = [];
|
|
26918
|
-
let panelUsd = 0;
|
|
26919
|
-
let panelUsdKnown = true;
|
|
26920
|
-
const latestVerdict = () => {
|
|
26921
|
-
const reads = input.reads();
|
|
26922
|
-
for (let i = reads.length - 1; i >= 0; i -= 1) {
|
|
26923
|
-
const read = reads[i];
|
|
26924
|
-
if (read?.verdict !== void 0) return {
|
|
26925
|
-
verdict: read.verdict,
|
|
26926
|
-
read: read.read
|
|
26927
|
-
};
|
|
26928
|
-
}
|
|
26929
|
-
return { read: 0 };
|
|
26930
|
-
};
|
|
26931
|
-
const closePrevious = () => {
|
|
26932
|
-
const last = entries.at(-1);
|
|
26933
|
-
const lastNote = notes.at(-1);
|
|
26934
|
-
if (last === void 0 || lastNote === void 0) return;
|
|
26935
|
-
const latest = latestVerdict();
|
|
26936
|
-
entries[entries.length - 1] = {
|
|
26937
|
-
...last,
|
|
26938
|
-
after: latest.read > lastNote.read ? summarizeVerdict(latest.verdict) : null
|
|
26939
|
-
};
|
|
26940
|
-
};
|
|
26941
27753
|
return {
|
|
26942
|
-
|
|
26943
|
-
|
|
26944
|
-
const
|
|
26945
|
-
|
|
26946
|
-
|
|
26947
|
-
|
|
26948
|
-
|
|
26949
|
-
|
|
26950
|
-
|
|
26951
|
-
|
|
26952
|
-
|
|
26953
|
-
|
|
26954
|
-
|
|
26955
|
-
|
|
26956
|
-
|
|
26957
|
-
|
|
26958
|
-
traces = await input.traces?.();
|
|
26959
|
-
if (traces === void 0) unavailable = "the run has recorded no trace yet";
|
|
26960
|
-
} catch (error) {
|
|
26961
|
-
unavailable = `the run's traces could not be read: ${error instanceof Error ? error.message : String(error)}`;
|
|
26962
|
-
}
|
|
26963
|
-
if (asking && traces !== void 0) {
|
|
26964
|
-
const result = await policy.runPanel({
|
|
26965
|
-
continuation,
|
|
26966
|
-
task: input.task,
|
|
26967
|
-
...verdict === void 0 ? {} : { verdict },
|
|
26968
|
-
reads: input.reads(),
|
|
26969
|
-
questions,
|
|
26970
|
-
traces,
|
|
26971
|
-
workers,
|
|
26972
|
-
...policy.best === void 0 ? {} : { bar: policy.best },
|
|
26973
|
-
...policy.reference === void 0 ? {} : { reference: policy.reference },
|
|
26974
|
-
usdCap,
|
|
26975
|
-
signal: context.signal
|
|
26976
|
-
});
|
|
26977
|
-
if (result.usd === null) panelUsdKnown = false;
|
|
26978
|
-
else panelUsd += result.usd;
|
|
26979
|
-
findings = distillFindings(findings, result.findings, new Set(failing), continuation);
|
|
26980
|
-
panelRecord = {
|
|
26981
|
-
asked: questions.length,
|
|
26982
|
-
proposed: result.findings.length,
|
|
26983
|
-
admitted: findings.filter((finding) => finding.admittedAt === continuation).length,
|
|
26984
|
-
usd: result.usd
|
|
26985
|
-
};
|
|
26986
|
-
panelLines = [...result.findings.map((finding) => ({
|
|
26987
|
-
kind: "finding",
|
|
26988
|
-
admitted: admitFinding(finding),
|
|
26989
|
-
...finding
|
|
26990
|
-
})), ...result.receipts === void 0 ? [] : [{
|
|
26991
|
-
kind: "receipts",
|
|
26992
|
-
receipts: result.receipts
|
|
26993
|
-
}]];
|
|
26994
|
-
} else panelRecord = {
|
|
26995
|
-
asked: 0,
|
|
26996
|
-
proposed: 0,
|
|
26997
|
-
admitted: 0,
|
|
26998
|
-
usd: 0,
|
|
26999
|
-
...unavailable === void 0 ? {} : { unavailable }
|
|
27000
|
-
};
|
|
27001
|
-
}
|
|
27002
|
-
const appended = policy.append ? await policy.append({
|
|
27003
|
-
continuation,
|
|
27004
|
-
...verdict === void 0 ? {} : { verdict },
|
|
27005
|
-
reads: input.reads(),
|
|
27006
|
-
signal: context.signal
|
|
27007
|
-
}) : void 0;
|
|
27008
|
-
const note = composeContinuationNote({
|
|
27009
|
-
profile: policy.profile,
|
|
27010
|
-
continuation,
|
|
27011
|
-
...verdict === void 0 ? {} : { verdict },
|
|
27012
|
-
...notes.at(-1)?.verdict === void 0 ? {} : { previous: notes.at(-1)?.verdict },
|
|
27013
|
-
reads: input.reads().length,
|
|
27014
|
-
failures,
|
|
27015
|
-
...policy.panel === "on" ? { findings } : {},
|
|
27016
|
-
...policy.bar === "on" ? { bar: {
|
|
27017
|
-
...policy.best === void 0 ? {} : { best: policy.best },
|
|
27018
|
-
...policy.reference === void 0 ? {} : { reference: policy.reference }
|
|
27019
|
-
} } : {},
|
|
27020
|
-
...appended === void 0 ? {} : { appended: [appended] },
|
|
27021
|
-
...input.owed === void 0 ? {} : { owed: input.owed },
|
|
27022
|
-
progress: context.progress,
|
|
27023
|
-
canReadMore: input.canReadMore,
|
|
27024
|
-
canSubmitResult: input.canSubmitResult === true,
|
|
27025
|
-
...input.sealed === true ? { sealed: true } : {}
|
|
27026
|
-
});
|
|
27027
|
-
notes.push({
|
|
27028
|
-
note,
|
|
27029
|
-
...verdict === void 0 ? {} : { verdict },
|
|
27030
|
-
...panelLines === void 0 ? {} : { panel: panelLines },
|
|
27031
|
-
read
|
|
27032
|
-
});
|
|
27033
|
-
entries.push({
|
|
27034
|
-
continuation,
|
|
27035
|
-
attempt: context.attempt,
|
|
27036
|
-
noteDigest: noteDigest(note),
|
|
27037
|
-
profile: policy.profile.id,
|
|
27038
|
-
switches: {
|
|
27039
|
-
failures,
|
|
27040
|
-
panel: policy.panel,
|
|
27041
|
-
bar: policy.bar
|
|
27042
|
-
},
|
|
27043
|
-
before: summarizeVerdict(verdict),
|
|
27044
|
-
after: null,
|
|
27045
|
-
...panelRecord === void 0 ? {} : { panel: panelRecord },
|
|
27046
|
-
appended: appended === void 0 ? 0 : 1
|
|
27047
|
-
});
|
|
27048
|
-
if (input.dir !== void 0) await writeContinuationFiles(input.dir, continuation, {
|
|
27049
|
-
note,
|
|
27050
|
-
...verdict === void 0 ? {} : { verdict },
|
|
27051
|
-
...panelLines === void 0 ? {} : { panel: panelLines }
|
|
27052
|
-
});
|
|
27053
|
-
return note;
|
|
27054
|
-
},
|
|
27055
|
-
read(continuation) {
|
|
27056
|
-
const index = continuation === void 0 ? notes.length - 1 : continuation - 1;
|
|
27057
|
-
const entry = notes[index];
|
|
27058
|
-
if (entry === void 0) return {
|
|
27059
|
-
found: false,
|
|
27060
|
-
reason: notes.length === 0 ? "no continuation has been sent in this run" : `continuations run from 1 to ${notes.length}`
|
|
27061
|
-
};
|
|
27062
|
-
return {
|
|
27063
|
-
found: true,
|
|
27064
|
-
continuation: index + 1,
|
|
27065
|
-
note: entry.note,
|
|
27066
|
-
verdict: failures === "off" && entry.verdict !== void 0 ? { pass: entry.verdict.pass } : entry.verdict ?? null,
|
|
27067
|
-
findings: findings.filter((finding) => finding.admittedAt <= index + 1)
|
|
27068
|
-
};
|
|
27069
|
-
},
|
|
27070
|
-
entries() {
|
|
27071
|
-
closePrevious();
|
|
27072
|
-
return entries.map((entry) => ({ ...entry }));
|
|
27073
|
-
}
|
|
27754
|
+
...total,
|
|
27755
|
+
...addResourceSpend(total.resources === void 0 ? void 0 : Object.fromEntries(Object.entries(total.resources).map(([name, value]) => {
|
|
27756
|
+
const previous = prior.resources?.[name];
|
|
27757
|
+
if (previous && previous.unit !== value.unit) throw new ValidationError$1(`resource ${name}: unit mismatch`);
|
|
27758
|
+
return [name, {
|
|
27759
|
+
...value,
|
|
27760
|
+
amount: Math.max(0, value.amount - (previous?.amount ?? 0)),
|
|
27761
|
+
known: value.known && (previous?.known ?? true)
|
|
27762
|
+
}];
|
|
27763
|
+
}))),
|
|
27764
|
+
iterations: Math.max(0, total.iterations - prior.iterations),
|
|
27765
|
+
tokens,
|
|
27766
|
+
usd: Math.max(0, total.usd - prior.usd),
|
|
27767
|
+
ms: Math.max(0, total.ms - prior.ms),
|
|
27768
|
+
...total.usdEstimated === void 0 ? {} : { usdEstimated: Math.max(0, total.usdEstimated - (prior.usdEstimated ?? 0)) },
|
|
27769
|
+
...total.boxMinutes === void 0 ? {} : { boxMinutes: Math.max(0, total.boxMinutes - (prior.boxMinutes ?? 0)) }
|
|
27074
27770
|
};
|
|
27075
27771
|
}
|
|
27076
27772
|
//#endregion
|
|
@@ -27167,6 +27863,19 @@ const DEFAULT_MAX_ATTEMPTS = Number.POSITIVE_INFINITY;
|
|
|
27167
27863
|
const DEFAULT_INITIAL_BACKOFF_MS = 2e3;
|
|
27168
27864
|
const DEFAULT_MAX_BACKOFF_MS = 3e4;
|
|
27169
27865
|
const DEFAULT_TRANSIENT_OUTAGE_MS = 9e5;
|
|
27866
|
+
/** Consecutive identical reconciliation failures of one pending invocation that resolve it. */
|
|
27867
|
+
const SAME_PENDING_LIMIT = 2;
|
|
27868
|
+
/**
|
|
27869
|
+
* The pending invocation a failure left, when abandoning it is the resolution: the provider
|
|
27870
|
+
* answered and could not resolve the execution (`unobservable`), or a nested manager's recovery
|
|
27871
|
+
* could not be rebuilt (`nested-recovery`). A lost transport waits for the provider instead, in
|
|
27872
|
+
* the outage window: abandoning during a provider outage would start a second execution beside
|
|
27873
|
+
* one that may still run. A request refused before it ran has nothing to abandon: a capacity
|
|
27874
|
+
* refusal pauses and any other refusal is terminal. A contract violation stays terminal (#1204).
|
|
27875
|
+
*/
|
|
27876
|
+
function abandonablePending(error) {
|
|
27877
|
+
return error instanceof RetainedExecutionPendingError && (error.pendingCause === "unobservable" || error.pendingCause === "nested-recovery") ? error : void 0;
|
|
27878
|
+
}
|
|
27170
27879
|
/**
|
|
27171
27880
|
* Bridge error classes the bridge itself never retries: a request that fails identically on
|
|
27172
27881
|
* every attempt, mapped below 5xx on its HTTP path (`parse_error` 400, the other two 501). On the
|
|
@@ -27229,11 +27938,18 @@ function upstreamUnavailableSignal(error) {
|
|
|
27229
27938
|
if (!(error instanceof Error)) return void 0;
|
|
27230
27939
|
if (isProviderSchemaRejection(error)) return void 0;
|
|
27231
27940
|
if (error instanceof CheckUnavailableError) return "check-unavailable";
|
|
27232
|
-
if (error instanceof HarnessTurnFailedError)
|
|
27233
|
-
|
|
27234
|
-
|
|
27235
|
-
|
|
27236
|
-
|
|
27941
|
+
if (error instanceof HarnessTurnFailedError) {
|
|
27942
|
+
const usageLimit = subscriptionUsageLimitSignal({
|
|
27943
|
+
error: error.message,
|
|
27944
|
+
...error.errorCode === void 0 ? {} : { errorCode: error.errorCode }
|
|
27945
|
+
});
|
|
27946
|
+
if (usageLimit !== void 0) return usageLimit;
|
|
27947
|
+
return unavailableSignalOfFailure({
|
|
27948
|
+
error: error.message,
|
|
27949
|
+
...error.errorCode === void 0 ? {} : { errorCode: error.errorCode },
|
|
27950
|
+
...error.statusCode === void 0 ? {} : { statusCode: error.statusCode }
|
|
27951
|
+
});
|
|
27952
|
+
}
|
|
27237
27953
|
if (error instanceof BackendTransportError) {
|
|
27238
27954
|
const code = error.upstreamCode?.toLowerCase();
|
|
27239
27955
|
if (code !== void 0 && UNAVAILABLE_CODES.has(code)) return code;
|
|
@@ -27241,10 +27957,29 @@ function upstreamUnavailableSignal(error) {
|
|
|
27241
27957
|
return unavailableSignalInText(errorProperty(error, "message") ?? "");
|
|
27242
27958
|
}
|
|
27243
27959
|
if (error instanceof AgentEvalError$1) return void 0;
|
|
27960
|
+
const code = capacityCode(error);
|
|
27961
|
+
if (code !== void 0) return code;
|
|
27244
27962
|
const status = errorHttpStatus(error);
|
|
27245
27963
|
if (status !== void 0) return UNAVAILABLE_STATUSES.has(status) ? `http-${status}` : void 0;
|
|
27246
27964
|
return unavailableSignalInText(errorProperty(error, "message") ?? "");
|
|
27247
27965
|
}
|
|
27966
|
+
/**
|
|
27967
|
+
* The upstream capacity code an error carries as its own `code` field, or `undefined`. A provider
|
|
27968
|
+
* SDK names a capacity refusal with the router's code when no HTTP response exists to carry a
|
|
27969
|
+
* status: agent-provider-tangle's `TangleCredentialCapacityError` (`provider_quota_exhausted`,
|
|
27970
|
+
* `upstream_unavailable`) when the subscription account owner has no account with room.
|
|
27971
|
+
*/
|
|
27972
|
+
function capacityCode(error) {
|
|
27973
|
+
let code;
|
|
27974
|
+
try {
|
|
27975
|
+
code = Reflect.get(error, "code");
|
|
27976
|
+
} catch {
|
|
27977
|
+
return;
|
|
27978
|
+
}
|
|
27979
|
+
if (typeof code !== "string") return void 0;
|
|
27980
|
+
const normalized = code.toLowerCase();
|
|
27981
|
+
return UNAVAILABLE_CODES.has(normalized) ? normalized : void 0;
|
|
27982
|
+
}
|
|
27248
27983
|
/** True when the upstream refused for capacity and the same request will succeed later. */
|
|
27249
27984
|
function isUpstreamUnavailable(error) {
|
|
27250
27985
|
return upstreamUnavailableSignal(error) !== void 0;
|
|
@@ -27305,7 +28040,7 @@ function retainedAdmissionCause(error) {
|
|
|
27305
28040
|
if (errorProperty(value, "name") === "RetainedRunProviderContractError") return void 0;
|
|
27306
28041
|
const status = errorHttpStatus(value);
|
|
27307
28042
|
if (status !== void 0 && status >= 400) return error.pendingCause === "request-rejected" || UNAVAILABLE_STATUSES.has(status) ? value : void 0;
|
|
27308
|
-
if (error.pendingCause === "request-rejected" && (value instanceof AgentEvalError$1 || isProviderSchemaRejection(value))) return value;
|
|
28043
|
+
if (error.pendingCause === "request-rejected" && (value instanceof AgentEvalError$1 || isProviderSchemaRejection(value) || capacityCode(value) !== void 0)) return value;
|
|
27309
28044
|
const members = Reflect.get(value, "errors");
|
|
27310
28045
|
if (Array.isArray(members)) queue.push(...members.slice(0, 12 - steps));
|
|
27311
28046
|
queue.push(value.cause);
|
|
@@ -27435,6 +28170,8 @@ async function runDriverWithRetry(run) {
|
|
|
27435
28170
|
let chargedFailures = 0;
|
|
27436
28171
|
let outageSince;
|
|
27437
28172
|
let consecutivePauses = 0;
|
|
28173
|
+
let samePending;
|
|
28174
|
+
let abandonments = 0;
|
|
27438
28175
|
let pauses = 0;
|
|
27439
28176
|
let continuations = 0;
|
|
27440
28177
|
let wakes = 0;
|
|
@@ -27493,6 +28230,12 @@ async function runDriverWithRetry(run) {
|
|
|
27493
28230
|
const classification = classifyDriverFailure(error, run.signal);
|
|
27494
28231
|
const after = run.progress();
|
|
27495
28232
|
const progressed = madeProgress(before, after);
|
|
28233
|
+
if (progressed) abandonments = 0;
|
|
28234
|
+
const pending = abandonablePending(error);
|
|
28235
|
+
samePending = pending === void 0 || classification !== "transient" ? void 0 : {
|
|
28236
|
+
cause: pending.pendingCause,
|
|
28237
|
+
count: samePending?.cause === pending.pendingCause ? samePending.count + 1 : 1
|
|
28238
|
+
};
|
|
27496
28239
|
if (classification === "unavailable") {
|
|
27497
28240
|
if (progressed) barrenReentries = 0;
|
|
27498
28241
|
const stop = (() => {
|
|
@@ -27531,6 +28274,7 @@ async function runDriverWithRetry(run) {
|
|
|
27531
28274
|
pause: pauses
|
|
27532
28275
|
};
|
|
27533
28276
|
await sleep(pause, run.signal);
|
|
28277
|
+
if (outageSince !== void 0) outageSince += now() - startedAt;
|
|
27534
28278
|
if (run.signal.aborted) throw new DriverAttemptsExhaustedError(error, attempts, "aborted");
|
|
27535
28279
|
const afterPause = budgetStop(run.budget(), now());
|
|
27536
28280
|
if (afterPause) throw new DriverAttemptsExhaustedError(error, attempts, afterPause);
|
|
@@ -27555,19 +28299,30 @@ async function runDriverWithRetry(run) {
|
|
|
27555
28299
|
if (progressed) return void 0;
|
|
27556
28300
|
return consecutiveBarren + 1 >= maxConsecutive ? "no-progress" : void 0;
|
|
27557
28301
|
})();
|
|
28302
|
+
let abandoned = false;
|
|
28303
|
+
if (pending !== void 0 && samePending !== void 0 && run.resolvePending !== void 0 && retryEnabled && !run.signal.aborted && abandonments < maxConsecutive && (stop === "no-progress" || stop === void 0 && samePending.count >= SAME_PENDING_LIMIT)) {
|
|
28304
|
+
abandoned = await run.resolvePending(pending, samePending.count);
|
|
28305
|
+
if (abandoned) {
|
|
28306
|
+
abandonments += 1;
|
|
28307
|
+
samePending = void 0;
|
|
28308
|
+
consecutiveBarren = 0;
|
|
28309
|
+
outageSince = void 0;
|
|
28310
|
+
}
|
|
28311
|
+
}
|
|
28312
|
+
const retryStop = abandoned ? void 0 : stop;
|
|
27558
28313
|
if (progressed) barrenReentries = 0;
|
|
27559
|
-
if (
|
|
28314
|
+
if (retryStop !== void 0) {
|
|
27560
28315
|
await emit({
|
|
27561
28316
|
attempt,
|
|
27562
28317
|
durationMs,
|
|
27563
28318
|
error: errMessage(error),
|
|
27564
28319
|
classification,
|
|
27565
28320
|
madeProgress: progressed,
|
|
27566
|
-
stop
|
|
28321
|
+
stop: retryStop
|
|
27567
28322
|
});
|
|
27568
|
-
throw new DriverAttemptsExhaustedError(error, attempts,
|
|
28323
|
+
throw new DriverAttemptsExhaustedError(error, attempts, retryStop);
|
|
27569
28324
|
}
|
|
27570
|
-
consecutiveBarren = progressed ? 0 : consecutiveBarren + 1;
|
|
28325
|
+
if (!abandoned) consecutiveBarren = progressed ? 0 : consecutiveBarren + 1;
|
|
27571
28326
|
const backoff = jitteredMs(cappedDoublingMs(consecutiveBarren, initialBackoff, maxBackoff), random);
|
|
27572
28327
|
await emit({
|
|
27573
28328
|
attempt,
|
|
@@ -27575,6 +28330,7 @@ async function runDriverWithRetry(run) {
|
|
|
27575
28330
|
error: errMessage(error),
|
|
27576
28331
|
classification,
|
|
27577
28332
|
madeProgress: progressed,
|
|
28333
|
+
...abandoned ? { abandoned: true } : {},
|
|
27578
28334
|
retryInMs: backoff
|
|
27579
28335
|
});
|
|
27580
28336
|
reentry = {
|
|
@@ -27591,6 +28347,8 @@ async function runDriverWithRetry(run) {
|
|
|
27591
28347
|
consecutiveBarren = 0;
|
|
27592
28348
|
consecutivePauses = 0;
|
|
27593
28349
|
outageSince = void 0;
|
|
28350
|
+
samePending = void 0;
|
|
28351
|
+
abandonments = 0;
|
|
27594
28352
|
const durationMs = now() - startedAt;
|
|
27595
28353
|
const after = run.progress();
|
|
27596
28354
|
const progressed = madeProgress(before, after);
|
|
@@ -28778,6 +29536,6 @@ function isNonEmptySpend(s) {
|
|
|
28778
29536
|
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);
|
|
28779
29537
|
}
|
|
28780
29538
|
//#endregion
|
|
28781
|
-
export {
|
|
29539
|
+
export { environmentReader as $, projectSandboxOutcome as $i, parseWorkerToolTraceArtifact as $n, sanitizeAgentRuntimeEvent as $r, PEER_MAIL_WIRE_KEY as $t, scopeOwnerExecutorNodeContext as A, worktreeCliProfileMaterialization as Ai, resolveAgentEnvironmentProvider as An, resolveHarnessTranscriptCapture as Ar, captureReusableExecutorConfig as At, consumeScopeRetainedOwnerResult as B, recordRuntimeOwnedDriveHarnessProviderEvidence as Bi, InMemoryResultBlobStore as Bn, buildLoopOtelSpans as Br, createInPlaceCliExecutor as Bt, deriveNodeExecutionIdentity as C, promptOnlyProfileMaterialization as Ci, readPromptOptions as Cn, abortError as Cr, createContinuationKeeper as Ct, recordScopeOwnerMaterialization as D, sandboxActProfileMaterialization as Di, createAgentEnvironmentProviderRegistry as Dn, harnessTranscriptArtifact as Dr, passedItems as Dt, recordScopeOwnerDriverAttempt as E, renderUnsupported as Ei, LEAF_CONTINUATION_TASK as En, captureHarnessTranscript as Er, failedItems as Et, seatExecutionProfile as F, inheritRuntimeOwnedExecutorAttestation as Fi, retainedExecutorSeamKey as Fn, readTraceContextFromEnv as Fr, snapshotExecutorConfig as Ft, scopeRetainedOwnerContext as G, runtimeOwnedPendingExecutorMaterialization as Gi, replaySpawnTree as Gn, flatOtelSpan as Gr, workerTraceSeamKey as Gt, prepareScopeRetainedOwnerSeatSwitch as H, runtimeOwnedExecutorExecutionBinding as Hi, insideCursorNamespace as Hn, buildRuntimeEventOtelSpans as Hr, readWorkerTraceContext as Ht, abandonScopeRetainedOwnerInvocation as I, knownExecutionBindingReceipt as Ii, unavailablePauseMs as In, traceContextToEnv as Ir, createWorktreeCliExecutor as It, scopeRetainedOwnerResourceReader as J, unknownMaterializationReceipt as Ji, readCommittedJsonLines as Jn, padSpanId as Jr, createActivityLog as Jt, scopeRetainedOwnerJournalRoot as K, runtimeOwnedScopeOwnerRuntime as Ki, isNoEntError as Kn, generateSpanId as Kr, taskToPrompt as Kt, beginScopeRetainedOwnerDrive as L, knownMaterializationReceipt as Li, unavailableSignalOfFailure as Ln, defaultRedactor as Lr, DEFAULT_SANDBOX_STEERING_MAX_TURNS as Lt, SeatChain as M, attestRuntimeOwnedScopeOwner as Mi, decodeToolPart as Mn, contentAddress as Mr, cliWorktreeExecutor as Mt, SeatChainExhaustedError as N, authoredProfileDigest as Ni, sandboxSessionTraceSource as Nn, createPropagatingTraceEmitter as Nr, createExecutor as Nt, recordScopeOwnerPause as O, unsupportedProfileDimensions as Oi, providerAsExecutor as On, persistHarnessTranscript as Or, verdictFromJudgeScore as Ot, authoredSeatStages as P, finalizeRuntimeOwnedPendingExecutor as Pi, registerRetainedExecutorPreparation as Pn, mergeTraceEnv as Pr, createExecutorRegistry as Pt, SPAWN_RESOURCE_PATH_MAX_BYTES as Q, detachedSnapshot as Qi, isTraceAnalysisStore as Qn, createRuntimeStreamEventCollector as Qr, DEFAULT_PEER_MAIL_LIMITS as Qt, bindScopeRetainedOwnerEnvironmentId as R, newExecutionAttemptId as Ri, FileResultBlobStore as Rn, defaultRedactorIdentityMaterial as Rr, createSteerableSandboxSession as Rt, createScope as S, promptModelProfileMaterialization as Si, assertBoxlessPromptOptions as Sn, RunCancellationReason as Sr, checkVerdictOf as St, meterRuntimeOwnedProviderAttempt as T, renderProfileMaterializationIssues as Ti, runBrainLoop as Tn, runAbortable as Tr, expandQuestions as Tt, prepareScopeRetainedOwnerTask as U, runtimeOwnedExecutorMaterialization as Ui, loadSpawnForest as Un, createOpenInferenceFileExporter as Ur, workerTraceEnv as Ut, noteScopeRetainedOwnerCoordination as V, runtimeOwnedDriveHarnessProviderEvidence as Vi, InMemorySpawnJournal as Vn, buildLoopSpanNodes as Vr, WORKER_TRACE_PROPAGATION as Vt, reconcileScopeRetainedOwnerEnvironment as W, runtimeOwnedExecutorProviderEvidence as Wi, materializeTreeView as Wn, createOtelExporter as Wr, workerTraceHeaders as Wt, scopeRetainedOwnerResult as X, executableAgentSpecSnapshot as Xi, WORKER_TOOL_TRACE_SCHEMA_VERSION as Xn, toOtelAttributes as Xr, createInbox as Xt, scopeRetainedOwnerRestorePoint as Y, executableAgentProfileSnapshot as Yi, writeAllBytes as Yn, padTraceId as Yr, readWorkerProgress as Yt, scopeRetainedOwnerTurnEnded as Z, detachedFrozen as Zi, captureWorkerTraceEvidence as Zn, createRuntimeEventCollector as Zr, AUTHORITY_MARKERS as Zt, isDriverSpec as _, promptOptionsFromAgentTurnInput as _a, controlProfileMaterialization as _i, runAgentRounds as _n, retainedCreateMaterial as _r, CONTINUATION_FACTS as _t, collectDelivered as a, extractLlmCallEvent as aa, createWorktree as ai, isLiveNodeStatus as an, createBudgetPool as ar, interactiveAdmissionSeamKey as at, assertRecursiveReservationPolicy as b, profileMaterializationAxes$1 as bi, SandboxEvidenceRetentionError as bn, createWorkerSlots as br, admitFinding as bt, runTree as c, mapSandboxToolEvent as ca, LOCAL_HARNESSES as ci, bridgeModelRouteRefusal as cn, spendFromUsageEvents as cr, reconnectRetainedInteractiveRun as ct, classifyDriverFailure as d, sandboxProgressEvents as da, localHarnessExecutable as di, harnessUsageIsEmpty as dn, bridgeStopSignalKey as dr, freeSlots as dt, readSandboxOutcome as ea, sanitizeKnowledgeReadinessReport as ei, claimsAuthority as en, workerTraceAnalysisStore as er, hostDirectoryReader as et, runDriverWithRetry as f, sumSandboxUsage as fa, parseCodexTokenUsage as fi, readCodexRolloutSession as fn, errMessage as fr, queueOf as ft, driverExecutorFactory as g, promptFromAgentTurnInput as ga, assertProfileMaterialization as gi, isTerminalDecision as gn, startRetainedRunInEnvironment as gr, CONTINUATIONS_DIR as gt, driverChild as h, awaitAbortable$1 as ha, AGENT_PROFILE_MATERIALIZATION_AXES as hi, defaultSelectWinner as hn, startRetainedRun as hr, mapExecutorResult as ht, bestDelivered as i, createSandboxUsageLedger as ia, captureWorktreeDiff as ii, peerMailVerbNames as in, assertValidBudget as ir, providerVisibleProfile as it, settledToIteration as j, attestRuntimeOwnedPendingExecutor as ji, createPushTraceSource as jn, retainHarnessTranscript as jr, cliInPlaceExecutor as jt, restoreScopeOwnerAcceptedExecution as k, validateProfileMaterialization as ki, providerAsSandboxClient as kn, readHarnessTranscript as kr, bindReusableExecutorExecutionId as kt, DriverAttemptsExhaustedError as l, notifySandboxEventObserver as la, harnessInvocation as li, addHarnessUsage as ln, spendFromUsageTotals as lr, recoverRetainedInteractiveRun as lt, upstreamUnavailableSignal as m, abortError$1 as ma, terminateProcessTreeAndConfirm as mi, createSandboxForSpec as mn, recoverRetainedRun as mr, gateOnDeliverable as mt, createRootHandle as n, canonicalStreamEventFromSandboxEvent as na, runSettledCommand as ni, isPeerMailEnvelope as nn, BudgetReconcileFault as nr, coordinationMcpAlias as nt, pickBestDelivered as o, isSandboxTerminalEvent as oa, removeWorktree as oi, isTerminalNodeStatus as on, meterUsageEvent as or, attachWorker as ot, summarizeDriverAttempts as p, decodeHarnessUsage as pa, CodexExecutionDiagnosticError as pi, TERMINAL_DECISIONS as pn, reconnectRetainedRun as pr, rollingDispatch as pt, scopeRetainedOwnerPriorSpend as q, unknownExecutionBindingReceipt as qi, prepareJsonlAppend as qn, loopEventToOtelSpan as qr, DEFAULT_STALL_AFTER_MS as qt, createSupervisor as r, createSandboxToolPartState as ra, runWorktreeHarness as ri, peerMailTools as rn, ReservationWaitRefused as rr, coordinationProfileToolPrefix as rt, runFinalizer as s, mapSandboxEvent as sa, DEFAULT_LOCAL_HARNESS as si, bridgeAdmissionRefusal as sn, newUsageTotals as sr, readWorkerInteractiveBinding as st, DEFAULT_MAX_DEPTH as t, assertSandboxServedModel as ta, sanitizeRuntimeStreamEvent as ti, createPeerMailbox as tn, executorFailure as tr, resolveSpawnResourcePaths as tt, HarnessTurnFailedError as u, sandboxEventServedBackend as ua, harnessSupportsReasoningEffort as ui, createCodexRolloutStoreReader as un, bridgeRuntimeAttachmentsKey as ur, startRetainedInteractiveRun as ut, withDriverExecutor as v, providerMessageText as va, defineProfileMaterializationContract as vi, createSandboxLineage as vn, destroyInteractiveEnvironment as vr, CheckUnavailableError as vt, meterRuntimeOwnedAccounting as w, promptResourceProfileMaterialization as wi, routerBrain as wn, linkAbort as wr, distillFindings as wt, beginScopeOwnerAttempt as x, promptControlProfileMaterialization as xi, captureBeforeDestroy as xn, openSlotGroup as xr, bestComposite as xt, SUBTREE_RESULT_LIMIT as y, fullProfileMaterialization as yi, acquireSandbox as yn, environmentGone as yr, admitContinuationPolicy as yt, bindScopeRetainedOwnerProvider as z, providerAttemptEvidence as zi, FileSpawnJournal as zn, resolveRedactor as zr, createRouterTranscript as zt };
|
|
28782
29540
|
|
|
28783
|
-
//# sourceMappingURL=supervisor-
|
|
29541
|
+
//# sourceMappingURL=supervisor-CSMb8HBU.js.map
|