@tangle-network/agent-runtime 0.105.0 → 0.106.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/activation-CRjVfOiy.js +203 -0
- package/dist/activation-CRjVfOiy.js.map +1 -0
- package/dist/activation-DyWB0K6E.d.ts +98 -0
- package/dist/agent.d.ts +216 -234
- package/dist/agent.js +615 -547
- package/dist/agent.js.map +1 -1
- package/dist/analyst-loop-DGdtVENg.js +517 -0
- package/dist/analyst-loop-DGdtVENg.js.map +1 -0
- package/dist/analyst-loop.d.ts +10 -43
- package/dist/analyst-loop.js +2 -11
- package/dist/backends-CiOCyRHb.js +743 -0
- package/dist/backends-CiOCyRHb.js.map +1 -0
- package/dist/candidate-execution/index.d.ts +4 -192
- package/dist/candidate-execution/index.js +5 -66
- package/dist/candidate-execution-DqMbv1rU.js +488 -0
- package/dist/candidate-execution-DqMbv1rU.js.map +1 -0
- package/dist/coder-yhVWbdWc.js +40 -0
- package/dist/coder-yhVWbdWc.js.map +1 -0
- package/dist/conversation-BpLQZGPH.js +1265 -0
- package/dist/conversation-BpLQZGPH.js.map +1 -0
- package/dist/conversation.d.ts +2 -804
- package/dist/conversation.js +2 -55
- package/dist/environment-provider-CfLUmKmf.js +1168 -0
- package/dist/environment-provider-CfLUmKmf.js.map +1 -0
- package/dist/environment-provider-IUGU3epE.d.ts +964 -0
- package/dist/environment-provider.d.ts +2 -78
- package/dist/environment-provider.js +2 -18
- package/dist/errors-DEAvWQPy.js +111 -0
- package/dist/errors-DEAvWQPy.js.map +1 -0
- package/dist/improvement-adapter-Cr5TBSji.d.ts +166 -0
- package/dist/improvement-cycle-C095kS_g.js +2624 -0
- package/dist/improvement-cycle-C095kS_g.js.map +1 -0
- package/dist/index-BGdxAwfO.d.ts +838 -0
- package/dist/index-C-FYUuFG.d.ts +193 -0
- package/dist/index-CbNM40xG.d.ts +140 -0
- package/dist/index-DruSKs3Z.d.ts +7224 -0
- package/dist/index-Efjb3nrQ.d.ts +671 -0
- package/dist/index.d.ts +14 -1058
- package/dist/index.js +2106 -2082
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +768 -889
- package/dist/intelligence.js +1466 -1367
- package/dist/intelligence.js.map +1 -1
- package/dist/{kb-gate-CwHO0vz6.d.ts → kb-gate-C8z2juK8.d.ts} +33 -31
- package/dist/kb-gate-DpaSwXVx.js +80 -0
- package/dist/kb-gate-DpaSwXVx.js.map +1 -0
- package/dist/knowledge-Ku-mPP4f.js +433 -0
- package/dist/knowledge-Ku-mPP4f.js.map +1 -0
- package/dist/knowledge.d.ts +2 -150
- package/dist/knowledge.js +2 -40
- package/dist/local-harness-Dh8PJ0ot.d.ts +455 -0
- package/dist/loop-runner-bin-BXsFEUcf.d.ts +162 -0
- package/dist/loop-runner-bin-CRagyuNb.js +268 -0
- package/dist/loop-runner-bin-CRagyuNb.js.map +1 -0
- package/dist/loop-runner-bin.d.ts +2 -15
- package/dist/loop-runner-bin.js +2 -24
- package/dist/loops.d.ts +5 -4164
- package/dist/loops.js +8 -419
- package/dist/mcp/bin.d.ts +1 -1
- package/dist/mcp/bin.js +116 -146
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +295 -436
- package/dist/mcp/index.js +1242 -1177
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/memory-bin.d.ts +1 -1
- package/dist/mcp/memory-bin.js +37 -16
- package/dist/mcp/memory-bin.js.map +1 -1
- package/dist/memory-server-DL6cE2Ag.js +234 -0
- package/dist/memory-server-DL6cE2Ag.js.map +1 -0
- package/dist/model-resolution-Btd9iIKV.js +98 -0
- package/dist/model-resolution-Btd9iIKV.js.map +1 -0
- package/dist/openai-tools-B68JaOCx.d.ts +24 -0
- package/dist/openai-tools-CKLy1C7M.js +44 -0
- package/dist/openai-tools-CKLy1C7M.js.map +1 -0
- package/dist/otel-export-D34IQW1C.js +843 -0
- package/dist/otel-export-D34IQW1C.js.map +1 -0
- package/dist/platform.d.ts +188 -185
- package/dist/platform.js +185 -218
- package/dist/platform.js.map +1 -1
- package/dist/prepare-B53sPV7M.js +4292 -0
- package/dist/prepare-B53sPV7M.js.map +1 -0
- package/dist/primeintellect/index.d.ts +149 -145
- package/dist/primeintellect/index.js +626 -1003
- package/dist/primeintellect/index.js.map +1 -1
- package/dist/profiles.d.ts +296 -490
- package/dist/profiles.js +722 -749
- package/dist/profiles.js.map +1 -1
- package/dist/protected-model-port-BLPH5VVv.js +722 -0
- package/dist/protected-model-port-BLPH5VVv.js.map +1 -0
- package/dist/protected-model-port-BP6Z4eau.d.ts +283 -0
- package/dist/protected-redaction--F3v1oo8.js +200 -0
- package/dist/protected-redaction--F3v1oo8.js.map +1 -0
- package/dist/redact-D-u-rrcn.js +82 -0
- package/dist/redact-D-u-rrcn.js.map +1 -0
- package/dist/redact-kbIOjfjM.d.ts +452 -0
- package/dist/researcher-CoVqNhfI.js +433 -0
- package/dist/researcher-CoVqNhfI.js.map +1 -0
- package/dist/runtime-5zM5XxLr.js +5408 -0
- package/dist/runtime-5zM5XxLr.js.map +1 -0
- package/dist/sandbox-events-DeI5xX8P.js +316 -0
- package/dist/sandbox-events-DeI5xX8P.js.map +1 -0
- package/dist/spawn-journal-CwPvKUTa.js +456 -0
- package/dist/spawn-journal-CwPvKUTa.js.map +1 -0
- package/dist/structural-rollout-IXUEplky.js +1445 -0
- package/dist/structural-rollout-IXUEplky.js.map +1 -0
- package/dist/substrate-B0TYNrXn.js +27 -0
- package/dist/substrate-B0TYNrXn.js.map +1 -0
- package/dist/{substrate-Dyiqmd3o.d.ts → substrate-BcnuSHXm.d.ts} +57 -54
- package/dist/supervise-BiRutHS9.js +7321 -0
- package/dist/supervise-BiRutHS9.js.map +1 -0
- package/dist/supervisor-DTKhF-RV.js +3826 -0
- package/dist/supervisor-DTKhF-RV.js.map +1 -0
- package/dist/testing.d.ts +4 -3
- package/dist/testing.js +2478 -2637
- package/dist/testing.js.map +1 -1
- package/dist/tool-server-RcWgLIsL.js +141 -0
- package/dist/tool-server-RcWgLIsL.js.map +1 -0
- package/dist/trace-propagation-CJJC7SVB.js +57 -0
- package/dist/trace-propagation-CJJC7SVB.js.map +1 -0
- package/dist/types-BaIpb0XT.d.ts +492 -0
- package/dist/types-BevOjfTY.d.ts +765 -0
- package/dist/types-C9j4qg6l.d.ts +500 -0
- package/dist/types-Cja5OTGh.d.ts +190 -0
- package/dist/util-MVgdwuIS.js +145 -0
- package/dist/util-MVgdwuIS.js.map +1 -0
- package/dist/workspace-archive-BosQiotA.js +575 -0
- package/dist/workspace-archive-BosQiotA.js.map +1 -0
- package/package.json +44 -56
- package/skills/build-with-agent-runtime/SKILL.md +1 -0
- package/dist/activation-Cm-9MMX1.d.ts +0 -69
- package/dist/analyst-loop.js.map +0 -1
- package/dist/backends-BO4Jqoe2.d.ts +0 -134
- package/dist/candidate-execution/index.js.map +0 -1
- package/dist/chunk-2KGAN2HM.js +0 -1256
- package/dist/chunk-2KGAN2HM.js.map +0 -1
- package/dist/chunk-2VZYRB55.js +0 -834
- package/dist/chunk-2VZYRB55.js.map +0 -1
- package/dist/chunk-3MDZX7YU.js +0 -125
- package/dist/chunk-3MDZX7YU.js.map +0 -1
- package/dist/chunk-5IBQAPVB.js +0 -29
- package/dist/chunk-5IBQAPVB.js.map +0 -1
- package/dist/chunk-5QOB7H74.js +0 -387
- package/dist/chunk-5QOB7H74.js.map +0 -1
- package/dist/chunk-C5RTIBNZ.js +0 -48
- package/dist/chunk-C5RTIBNZ.js.map +0 -1
- package/dist/chunk-CTRA64LY.js +0 -680
- package/dist/chunk-CTRA64LY.js.map +0 -1
- package/dist/chunk-DPEUKJRO.js +0 -27
- package/dist/chunk-DPEUKJRO.js.map +0 -1
- package/dist/chunk-FD2MBMOH.js +0 -494
- package/dist/chunk-FD2MBMOH.js.map +0 -1
- package/dist/chunk-H5QPIZNX.js +0 -907
- package/dist/chunk-H5QPIZNX.js.map +0 -1
- package/dist/chunk-HLKC4UYB.js +0 -3514
- package/dist/chunk-HLKC4UYB.js.map +0 -1
- package/dist/chunk-HNP72PNU.js +0 -192
- package/dist/chunk-HNP72PNU.js.map +0 -1
- package/dist/chunk-ISPWRSEC.js +0 -408
- package/dist/chunk-ISPWRSEC.js.map +0 -1
- package/dist/chunk-KRBFHMV6.js +0 -1228
- package/dist/chunk-KRBFHMV6.js.map +0 -1
- package/dist/chunk-M22PL4GH.js +0 -171
- package/dist/chunk-M22PL4GH.js.map +0 -1
- package/dist/chunk-NURIQBJQ.js +0 -72
- package/dist/chunk-NURIQBJQ.js.map +0 -1
- package/dist/chunk-OPVWXJ2H.js +0 -75
- package/dist/chunk-OPVWXJ2H.js.map +0 -1
- package/dist/chunk-PZZKQVQV.js +0 -52
- package/dist/chunk-PZZKQVQV.js.map +0 -1
- package/dist/chunk-Q2JSAVQ3.js +0 -671
- package/dist/chunk-Q2JSAVQ3.js.map +0 -1
- package/dist/chunk-RDOAVVHY.js +0 -699
- package/dist/chunk-RDOAVVHY.js.map +0 -1
- package/dist/chunk-SGKPNBXE.js +0 -60
- package/dist/chunk-SGKPNBXE.js.map +0 -1
- package/dist/chunk-SMQXZGLZ.js +0 -351
- package/dist/chunk-SMQXZGLZ.js.map +0 -1
- package/dist/chunk-UHEZW5BU.js +0 -2918
- package/dist/chunk-UHEZW5BU.js.map +0 -1
- package/dist/chunk-UPWGXKXB.js +0 -254
- package/dist/chunk-UPWGXKXB.js.map +0 -1
- package/dist/chunk-VISA6CI3.js +0 -200
- package/dist/chunk-VISA6CI3.js.map +0 -1
- package/dist/chunk-WMTCUOQL.js +0 -474
- package/dist/chunk-WMTCUOQL.js.map +0 -1
- package/dist/chunk-WRTOVNN4.js +0 -4728
- package/dist/chunk-WRTOVNN4.js.map +0 -1
- package/dist/chunk-WSTRQZYQ.js +0 -5576
- package/dist/chunk-WSTRQZYQ.js.map +0 -1
- package/dist/chunk-XBG2W2VW.js +0 -6434
- package/dist/chunk-XBG2W2VW.js.map +0 -1
- package/dist/chunk-YEJR7IXO.js +0 -68
- package/dist/chunk-YEJR7IXO.js.map +0 -1
- package/dist/chunk-YJZA2BIK.js +0 -1338
- package/dist/chunk-YJZA2BIK.js.map +0 -1
- package/dist/completion-gate-BMy5LGoP.d.ts +0 -256
- package/dist/conversation.js.map +0 -1
- package/dist/coordination-BZZSVYpZ.d.ts +0 -2570
- package/dist/environment-provider.js.map +0 -1
- package/dist/improvement-adapter-HAZz-7vK.d.ts +0 -203
- package/dist/knowledge.js.map +0 -1
- package/dist/local-harness-BDNcl6jI.d.ts +0 -175
- package/dist/loop-runner-bin-68aoM9-7.d.ts +0 -199
- package/dist/loop-runner-bin.js.map +0 -1
- package/dist/loops.js.map +0 -1
- package/dist/openai-tools-fnj6SRVg.d.ts +0 -46
- package/dist/protected-model-port-B4t-OQjL.d.ts +0 -271
- package/dist/redact-BuFjxBUJ.d.ts +0 -481
- package/dist/sanitize-DEbPNtyI.d.ts +0 -328
- package/dist/structural-rollout-DEh6CEsa.d.ts +0 -446
- package/dist/supervise-4_48wwvy.d.ts +0 -379
- package/dist/types-BCoemcXU.d.ts +0 -802
- package/dist/types-BwoZWq-i.d.ts +0 -510
- package/dist/types-Cbp72qyu.d.ts +0 -491
- package/dist/types-CmYCMbFT.d.ts +0 -203
- package/dist/types-CvNwMzQt.d.ts +0 -957
- package/dist/worktree-fanout-DxhAWr5Z.d.ts +0 -999
package/dist/chunk-HLKC4UYB.js
DELETED
|
@@ -1,3514 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
contentAddress,
|
|
3
|
-
materializeTreeView,
|
|
4
|
-
pendingWaits,
|
|
5
|
-
replaySpawnTree
|
|
6
|
-
} from "./chunk-ISPWRSEC.js";
|
|
7
|
-
import {
|
|
8
|
-
addTokenUsage,
|
|
9
|
-
zeroTokenUsage
|
|
10
|
-
} from "./chunk-3MDZX7YU.js";
|
|
11
|
-
import {
|
|
12
|
-
RuntimeRunStateError,
|
|
13
|
-
ValidationError
|
|
14
|
-
} from "./chunk-YEJR7IXO.js";
|
|
15
|
-
|
|
16
|
-
// src/mcp/codex-diagnostics.ts
|
|
17
|
-
import { constants } from "fs";
|
|
18
|
-
import { open } from "fs/promises";
|
|
19
|
-
import { stripVTControlCharacters } from "util";
|
|
20
|
-
var CodexExecutionDiagnosticError = class extends Error {
|
|
21
|
-
constructor(reason, diagnostic, cause) {
|
|
22
|
-
super(
|
|
23
|
-
`runLocalHarness: reproducible Codex execution failed: ${reason}; diagnostic=${JSON.stringify(diagnostic)}`,
|
|
24
|
-
{ cause }
|
|
25
|
-
);
|
|
26
|
-
this.reason = reason;
|
|
27
|
-
this.diagnostic = diagnostic;
|
|
28
|
-
this.name = "CodexExecutionDiagnosticError";
|
|
29
|
-
}
|
|
30
|
-
reason;
|
|
31
|
-
diagnostic;
|
|
32
|
-
code = "CODEX_EXECUTION_DIAGNOSTIC";
|
|
33
|
-
};
|
|
34
|
-
var CODEX_FAILURE_STREAM_MAX_CHARS = 4096;
|
|
35
|
-
var CODEX_AUTH_MAX_BYTES = 1024 * 1024;
|
|
36
|
-
var CODEX_REDACTED = "<REDACTED_CREDENTIAL>";
|
|
37
|
-
var codexSensitiveEnvironmentName = /(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|AUTH|COOKIE)/i;
|
|
38
|
-
function createCodexExecutionDiagnosticError(opts) {
|
|
39
|
-
const redact = (value) => redactCodexDiagnostic(value, opts.codexHome, opts.redactionValues);
|
|
40
|
-
const boundedStdout = boundCodexDiagnostic(redact(opts.stdout));
|
|
41
|
-
const boundedStderr = boundCodexDiagnostic(redact(opts.stderr));
|
|
42
|
-
const diagnostic = {
|
|
43
|
-
exitCode: opts.exitCode,
|
|
44
|
-
killedBySignal: opts.killedBySignal,
|
|
45
|
-
timedOut: opts.timedOut,
|
|
46
|
-
...opts.aborted !== void 0 ? { aborted: opts.aborted } : {},
|
|
47
|
-
durationMs: opts.durationMs,
|
|
48
|
-
stdout: boundedStdout.value,
|
|
49
|
-
stderr: boundedStderr.value,
|
|
50
|
-
stdoutTruncated: boundedStdout.truncated,
|
|
51
|
-
stderrTruncated: boundedStderr.truncated
|
|
52
|
-
};
|
|
53
|
-
const safeReason = boundCodexDiagnostic(redact(opts.reason), 1024).value;
|
|
54
|
-
return new CodexExecutionDiagnosticError(
|
|
55
|
-
safeReason,
|
|
56
|
-
diagnostic,
|
|
57
|
-
redactCodexErrorCause(opts.cause, safeReason, redact)
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
function redactCodexHome(value, codexHome) {
|
|
61
|
-
return codexHome ? value.replaceAll(codexHome, "<ISOLATED_CODEX_HOME>") : value;
|
|
62
|
-
}
|
|
63
|
-
function collectCodexDiagnosticRedactionValues(env, authText) {
|
|
64
|
-
const values = Object.entries(env).filter(
|
|
65
|
-
([name, value]) => codexSensitiveEnvironmentName.test(name) && typeof value === "string"
|
|
66
|
-
).map(([, value]) => value);
|
|
67
|
-
try {
|
|
68
|
-
collectStringLeaves(JSON.parse(authText), values);
|
|
69
|
-
} catch {
|
|
70
|
-
}
|
|
71
|
-
const encoded = values.filter((value) => value.length >= 8).flatMap((value) => [
|
|
72
|
-
Buffer.from(value, "utf8").toString("base64"),
|
|
73
|
-
Buffer.from(value, "utf8").toString("base64url"),
|
|
74
|
-
encodeURIComponent(value),
|
|
75
|
-
JSON.stringify(value).slice(1, -1)
|
|
76
|
-
]);
|
|
77
|
-
return [.../* @__PURE__ */ new Set([...values, ...encoded])];
|
|
78
|
-
}
|
|
79
|
-
async function readCodexAuthRedactionText(path) {
|
|
80
|
-
const handle = await open(path, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
81
|
-
try {
|
|
82
|
-
const stats = await handle.stat();
|
|
83
|
-
if (!stats.isFile() || !Number.isSafeInteger(stats.size) || stats.size > CODEX_AUTH_MAX_BYTES) {
|
|
84
|
-
throw new Error("Codex auth.json must be a regular file no larger than 1 MiB");
|
|
85
|
-
}
|
|
86
|
-
const buffer = Buffer.alloc(stats.size);
|
|
87
|
-
let offset = 0;
|
|
88
|
-
while (offset < buffer.length) {
|
|
89
|
-
const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);
|
|
90
|
-
if (bytesRead === 0) break;
|
|
91
|
-
offset += bytesRead;
|
|
92
|
-
}
|
|
93
|
-
return buffer.subarray(0, offset).toString("utf8");
|
|
94
|
-
} finally {
|
|
95
|
-
await handle.close();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function boundCodexDiagnostic(value, maxChars = CODEX_FAILURE_STREAM_MAX_CHARS) {
|
|
99
|
-
if (value.length <= maxChars) return { value, truncated: false };
|
|
100
|
-
const marker = "\n<... OUTPUT TRUNCATED ...>\n";
|
|
101
|
-
const contentChars = maxChars - marker.length;
|
|
102
|
-
const headChars = Math.floor(contentChars / 2);
|
|
103
|
-
const tailChars = contentChars - headChars;
|
|
104
|
-
return {
|
|
105
|
-
value: `${value.slice(0, headChars)}${marker}${value.slice(-tailChars)}`,
|
|
106
|
-
truncated: true
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
function redactCodexDiagnostic(value, codexHome, protectedValues) {
|
|
110
|
-
let redacted = redactCodexHome(stripVTControlCharacters(value), codexHome);
|
|
111
|
-
const exactValues = [...new Set(protectedValues.filter((item) => item.length >= 6))].sort(
|
|
112
|
-
(a, b) => b.length - a.length
|
|
113
|
-
);
|
|
114
|
-
for (const secret of exactValues) redacted = redacted.replaceAll(secret, CODEX_REDACTED);
|
|
115
|
-
redacted = redacted.replace(/(\bBearer\s+)[A-Za-z0-9._~+/=-]{6,}/gi, `$1${CODEX_REDACTED}`).replace(/\b(?:sk|rk|pk)-[A-Za-z0-9._-]{8,}\b/g, CODEX_REDACTED).replace(/\beyJ[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\.[A-Za-z0-9_-]{6,}\b/g, CODEX_REDACTED).replace(
|
|
116
|
-
/((?:["']?(?:api[-_]?key|access[-_]?token|refresh[-_]?token|id[-_]?token|client[-_]?secret|private[-_]?key|session[-_]?(?:id|token)|secret|password|passwd|authorization|credential|cookie)["']?)\s*[:=]\s*)(["'])[^"'\r\n]*\2/gi,
|
|
117
|
-
`$1$2${CODEX_REDACTED}$2`
|
|
118
|
-
).replace(
|
|
119
|
-
/((?:["']?(?:api[-_]?key|access[-_]?token|refresh[-_]?token|id[-_]?token|client[-_]?secret|private[-_]?key|session[-_]?(?:id|token)|secret|password|passwd|authorization|credential|cookie)["']?)\s*[:=]\s*)([^\s,;}]+)/gi,
|
|
120
|
-
`$1${CODEX_REDACTED}`
|
|
121
|
-
);
|
|
122
|
-
for (const secret of exactValues) redacted = redacted.replaceAll(secret, CODEX_REDACTED);
|
|
123
|
-
return redacted;
|
|
124
|
-
}
|
|
125
|
-
function redactCodexErrorCause(cause, safeReason, redact) {
|
|
126
|
-
if (!(cause instanceof Error)) return void 0;
|
|
127
|
-
const safeCause = new Error(safeReason);
|
|
128
|
-
safeCause.name = cause.name;
|
|
129
|
-
if (cause.stack) safeCause.stack = boundCodexDiagnostic(redact(cause.stack)).value;
|
|
130
|
-
return safeCause;
|
|
131
|
-
}
|
|
132
|
-
function collectStringLeaves(value, output) {
|
|
133
|
-
if (typeof value === "string") {
|
|
134
|
-
output.push(value);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
if (Array.isArray(value)) {
|
|
138
|
-
for (const item of value) collectStringLeaves(item, output);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if (value !== null && typeof value === "object") {
|
|
142
|
-
for (const item of Object.values(value)) collectStringLeaves(item, output);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// src/mcp/local-harness.ts
|
|
147
|
-
import { spawn } from "child_process";
|
|
148
|
-
import { createHash, randomUUID } from "crypto";
|
|
149
|
-
import { constants as constants2, createReadStream } from "fs";
|
|
150
|
-
import {
|
|
151
|
-
access,
|
|
152
|
-
chmod,
|
|
153
|
-
copyFile,
|
|
154
|
-
mkdir,
|
|
155
|
-
mkdtemp,
|
|
156
|
-
open as open2,
|
|
157
|
-
readFile,
|
|
158
|
-
realpath,
|
|
159
|
-
rm,
|
|
160
|
-
symlink,
|
|
161
|
-
writeFile
|
|
162
|
-
} from "fs/promises";
|
|
163
|
-
import { homedir, tmpdir } from "os";
|
|
164
|
-
import { basename, delimiter, dirname, isAbsolute, join, resolve, sep } from "path";
|
|
165
|
-
var codexReasoningEffort = {
|
|
166
|
-
none: "none",
|
|
167
|
-
minimal: "minimal",
|
|
168
|
-
low: "low",
|
|
169
|
-
medium: "medium",
|
|
170
|
-
high: "high",
|
|
171
|
-
xhigh: "xhigh",
|
|
172
|
-
ultracode: "xhigh"
|
|
173
|
-
};
|
|
174
|
-
function codexReasoningArgs(reasoningEffort) {
|
|
175
|
-
const mapped = codexReasoningEffort[reasoningEffort];
|
|
176
|
-
if (mapped === void 0) {
|
|
177
|
-
throw new Error(
|
|
178
|
-
`harnessInvocation: unsupported Codex reasoning effort ${String(reasoningEffort)}`
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
return ["-c", `model_reasoning_effort="${mapped}"`];
|
|
182
|
-
}
|
|
183
|
-
var HARNESS_INVOCATIONS = {
|
|
184
|
-
claude: {
|
|
185
|
-
command: "claude",
|
|
186
|
-
// `-p` IS headless/print mode; the old `--headless` flag was removed from the CLI.
|
|
187
|
-
// Permission bypass is an explicit per-run opt-in below, never the public default.
|
|
188
|
-
buildArgs: (taskPrompt) => ["-p", taskPrompt],
|
|
189
|
-
modelArgs: (model) => ["-m", model]
|
|
190
|
-
},
|
|
191
|
-
codex: {
|
|
192
|
-
command: "codex",
|
|
193
|
-
buildArgs: (taskPrompt) => ["exec", taskPrompt],
|
|
194
|
-
modelArgs: (model) => ["-m", model],
|
|
195
|
-
reasoningArgs: codexReasoningArgs
|
|
196
|
-
},
|
|
197
|
-
opencode: {
|
|
198
|
-
command: "opencode",
|
|
199
|
-
buildArgs: (taskPrompt) => ["run", taskPrompt],
|
|
200
|
-
modelArgs: (model) => ["-m", model]
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
var CODEX_REPRODUCIBLE_ARGS = [
|
|
204
|
-
"--ephemeral",
|
|
205
|
-
"--ignore-rules",
|
|
206
|
-
"--json",
|
|
207
|
-
"-c",
|
|
208
|
-
'approval_policy="never"',
|
|
209
|
-
"-c",
|
|
210
|
-
'web_search="disabled"',
|
|
211
|
-
"-c",
|
|
212
|
-
"project_doc_max_bytes=0",
|
|
213
|
-
"-c",
|
|
214
|
-
"skills.include_instructions=false",
|
|
215
|
-
"-c",
|
|
216
|
-
"include_apps_instructions=false",
|
|
217
|
-
"--disable",
|
|
218
|
-
"tool_suggest",
|
|
219
|
-
"-c",
|
|
220
|
-
'features.multi_agent_v2.root_agent_usage_hint_text=""',
|
|
221
|
-
"-c",
|
|
222
|
-
'features.multi_agent_v2.subagent_usage_hint_text=""',
|
|
223
|
-
"-c",
|
|
224
|
-
'features.multi_agent_v2.multi_agent_mode_hint_text=""',
|
|
225
|
-
"--strict-config"
|
|
226
|
-
];
|
|
227
|
-
function buildHarnessArgs(harness, taskPrompt, options = {}) {
|
|
228
|
-
const args = HARNESS_INVOCATIONS[harness].buildArgs(taskPrompt);
|
|
229
|
-
if (harness === "claude" && options.dangerouslySkipPermissions) {
|
|
230
|
-
args.push("--dangerously-skip-permissions");
|
|
231
|
-
}
|
|
232
|
-
if (options.codexReproducible) {
|
|
233
|
-
if (harness !== "codex") {
|
|
234
|
-
throw new Error("harnessInvocation: codexReproducible requires the Codex harness");
|
|
235
|
-
}
|
|
236
|
-
args.push(...CODEX_REPRODUCIBLE_ARGS);
|
|
237
|
-
}
|
|
238
|
-
return args;
|
|
239
|
-
}
|
|
240
|
-
function harnessInvocation(harness, profile, taskPrompt, options = {}) {
|
|
241
|
-
const invocation = HARNESS_INVOCATIONS[harness];
|
|
242
|
-
if (!invocation) {
|
|
243
|
-
throw new Error(`harnessInvocation: unknown harness ${String(harness)}`);
|
|
244
|
-
}
|
|
245
|
-
if (options.codexReproducible) {
|
|
246
|
-
const model2 = profile.model?.default;
|
|
247
|
-
if (typeof model2 !== "string" || model2.trim().length === 0) {
|
|
248
|
-
throw new Error("harnessInvocation: codexReproducible requires profile.model.default");
|
|
249
|
-
}
|
|
250
|
-
if (profile.model?.reasoningEffort === void 0) {
|
|
251
|
-
throw new Error("harnessInvocation: codexReproducible requires profile.model.reasoningEffort");
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
const systemPrompt = profile.prompt?.systemPrompt;
|
|
255
|
-
const instructions = profile.prompt?.instructions;
|
|
256
|
-
if (instructions !== void 0 && (!Array.isArray(instructions) || instructions.some((instruction) => typeof instruction !== "string"))) {
|
|
257
|
-
throw new Error("harnessInvocation: profile.prompt.instructions must be an array of strings");
|
|
258
|
-
}
|
|
259
|
-
const promptSections = [
|
|
260
|
-
...typeof systemPrompt === "string" && systemPrompt.trim().length > 0 ? [systemPrompt] : [],
|
|
261
|
-
...instructions?.filter((instruction) => instruction.trim().length > 0) ?? [],
|
|
262
|
-
taskPrompt
|
|
263
|
-
];
|
|
264
|
-
const composedPrompt = promptSections.join("\n\n");
|
|
265
|
-
const args = buildHarnessArgs(harness, composedPrompt, options);
|
|
266
|
-
const model = profile.model?.default;
|
|
267
|
-
if (typeof model === "string" && model.length > 0) {
|
|
268
|
-
args.push(...invocation.modelArgs(model));
|
|
269
|
-
}
|
|
270
|
-
const reasoningEffort = profile.model?.reasoningEffort;
|
|
271
|
-
if (reasoningEffort !== void 0 && invocation.reasoningArgs) {
|
|
272
|
-
args.push(...invocation.reasoningArgs(reasoningEffort));
|
|
273
|
-
}
|
|
274
|
-
return { command: invocation.command, args, prompt: composedPrompt };
|
|
275
|
-
}
|
|
276
|
-
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
277
|
-
var DEFAULT_MAX_OUTPUT_BYTES = 64 * 1024 * 1024;
|
|
278
|
-
var processKillGraceMs = 250;
|
|
279
|
-
var processGroupExitConfirmMs = 1e3;
|
|
280
|
-
var processGroupExitPollMs = 10;
|
|
281
|
-
var RollingByteCapture = class {
|
|
282
|
-
constructor(maxBytes) {
|
|
283
|
-
this.maxBytes = maxBytes;
|
|
284
|
-
}
|
|
285
|
-
maxBytes;
|
|
286
|
-
chunks = [];
|
|
287
|
-
bytes = 0;
|
|
288
|
-
append(chunk) {
|
|
289
|
-
if (this.maxBytes === 0) return;
|
|
290
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
|
|
291
|
-
this.chunks.push(buffer);
|
|
292
|
-
this.bytes += buffer.length;
|
|
293
|
-
let excess = this.bytes - this.maxBytes;
|
|
294
|
-
while (excess > 0 && this.chunks.length > 0) {
|
|
295
|
-
const first = this.chunks[0];
|
|
296
|
-
if (first.length <= excess) {
|
|
297
|
-
this.chunks.shift();
|
|
298
|
-
this.bytes -= first.length;
|
|
299
|
-
excess -= first.length;
|
|
300
|
-
} else {
|
|
301
|
-
this.chunks[0] = first.subarray(excess);
|
|
302
|
-
this.bytes -= excess;
|
|
303
|
-
excess = 0;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
text() {
|
|
308
|
-
return Buffer.concat(this.chunks, this.bytes).toString("utf8");
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
async function runLocalHarness(options) {
|
|
312
|
-
const { harness, cwd, taskPrompt } = options;
|
|
313
|
-
if (options.codexReproducible && harness !== "codex") {
|
|
314
|
-
throw new Error("runLocalHarness: codexReproducible requires the Codex harness");
|
|
315
|
-
}
|
|
316
|
-
if (options.codexReproducible && process.platform !== "linux") {
|
|
317
|
-
throw new Error("runLocalHarness: codexReproducible currently requires Linux");
|
|
318
|
-
}
|
|
319
|
-
if (options.codexReadDeniedPaths !== void 0 && !options.codexReproducible) {
|
|
320
|
-
throw new Error("runLocalHarness: codexReadDeniedPaths requires codexReproducible");
|
|
321
|
-
}
|
|
322
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
323
|
-
const maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
324
|
-
if (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes < 0) {
|
|
325
|
-
throw new Error("runLocalHarness: maxOutputBytes must be a non-negative safe integer");
|
|
326
|
-
}
|
|
327
|
-
const spawnImpl = options.spawn ?? spawn;
|
|
328
|
-
const invocation = HARNESS_INVOCATIONS[harness];
|
|
329
|
-
if (!invocation) {
|
|
330
|
-
throw new Error(`runLocalHarness: unknown harness ${String(harness)}`);
|
|
331
|
-
}
|
|
332
|
-
const startedAt = Date.now();
|
|
333
|
-
const requestedCommand = options.invocation?.command ?? invocation.command;
|
|
334
|
-
const args = options.invocation ? [...options.invocation.args] : buildHarnessArgs(harness, taskPrompt, options);
|
|
335
|
-
if (options.codexReproducible) assertCodexReproducibleInvocation(requestedCommand, args);
|
|
336
|
-
options.signal?.throwIfAborted();
|
|
337
|
-
const baseEnv = { ...options.env ?? process.env, PWD: cwd };
|
|
338
|
-
const isolated = options.codexReproducible ? await isolateCodexHome({
|
|
339
|
-
baseEnv,
|
|
340
|
-
cwd,
|
|
341
|
-
command: requestedCommand,
|
|
342
|
-
readDeniedPaths: options.codexReadDeniedPaths ?? [],
|
|
343
|
-
resolveExecutable: options.resolveCodexExecutable ?? resolveCodexNativeExecutable
|
|
344
|
-
}) : {
|
|
345
|
-
env: baseEnv,
|
|
346
|
-
command: requestedCommand,
|
|
347
|
-
cleanup: async () => void 0,
|
|
348
|
-
controlledConfigSha256: "",
|
|
349
|
-
executableSha256: "",
|
|
350
|
-
readDeniedPathsSha256: "",
|
|
351
|
-
readDeniedPathCount: 0,
|
|
352
|
-
readDeniedPaths: [],
|
|
353
|
-
diagnosticRedactionValues: [],
|
|
354
|
-
ambientAuth: "",
|
|
355
|
-
isolatedAuth: "",
|
|
356
|
-
writeProbe: "",
|
|
357
|
-
stagedWriteProbe: "",
|
|
358
|
-
deniedProbePaths: []
|
|
359
|
-
};
|
|
360
|
-
const env = isolated.env;
|
|
361
|
-
const command = isolated.command;
|
|
362
|
-
if (options.codexReproducible) assertCodexReproducibleInvocation(command, args);
|
|
363
|
-
try {
|
|
364
|
-
const evidence = options.codexReproducible ? await collectCodexExecutionEvidence({
|
|
365
|
-
command,
|
|
366
|
-
args,
|
|
367
|
-
cwd,
|
|
368
|
-
env,
|
|
369
|
-
spawn: spawnImpl,
|
|
370
|
-
controlledConfigSha256: isolated.controlledConfigSha256,
|
|
371
|
-
executableSha256: isolated.executableSha256,
|
|
372
|
-
readDeniedPathsSha256: isolated.readDeniedPathsSha256,
|
|
373
|
-
readDeniedPathCount: isolated.readDeniedPathCount,
|
|
374
|
-
readDeniedPaths: isolated.readDeniedPaths,
|
|
375
|
-
ambientAuth: isolated.ambientAuth,
|
|
376
|
-
isolatedAuth: isolated.isolatedAuth,
|
|
377
|
-
writeProbe: isolated.writeProbe,
|
|
378
|
-
stagedWriteProbe: isolated.stagedWriteProbe,
|
|
379
|
-
deniedProbePaths: isolated.deniedProbePaths,
|
|
380
|
-
...options.signal ? { signal: options.signal } : {}
|
|
381
|
-
}) : void 0;
|
|
382
|
-
return await new Promise((resolve2, reject) => {
|
|
383
|
-
let child;
|
|
384
|
-
try {
|
|
385
|
-
options.signal?.throwIfAborted();
|
|
386
|
-
child = spawnImpl(command, args, {
|
|
387
|
-
cwd,
|
|
388
|
-
env,
|
|
389
|
-
stdio: "pipe",
|
|
390
|
-
detached: process.platform !== "win32"
|
|
391
|
-
});
|
|
392
|
-
} catch (err) {
|
|
393
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
child.stdin?.end();
|
|
397
|
-
const stdoutCapture = new RollingByteCapture(maxOutputBytes);
|
|
398
|
-
const stderrCapture = new RollingByteCapture(maxOutputBytes);
|
|
399
|
-
let timedOut = false;
|
|
400
|
-
let aborted = false;
|
|
401
|
-
let settled = false;
|
|
402
|
-
let leaderClosed = false;
|
|
403
|
-
let timer = null;
|
|
404
|
-
let termination = null;
|
|
405
|
-
const clearLifecycle = () => {
|
|
406
|
-
if (timer) clearTimeout(timer);
|
|
407
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
408
|
-
};
|
|
409
|
-
const rejectOnce = (err) => {
|
|
410
|
-
if (settled) return;
|
|
411
|
-
settled = true;
|
|
412
|
-
clearLifecycle();
|
|
413
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
414
|
-
};
|
|
415
|
-
const terminate = () => {
|
|
416
|
-
if (!termination) {
|
|
417
|
-
termination = terminateProcessTreeAndConfirm(child, () => leaderClosed);
|
|
418
|
-
void termination.catch(rejectOnce);
|
|
419
|
-
}
|
|
420
|
-
return termination;
|
|
421
|
-
};
|
|
422
|
-
timer = timeoutMs > 0 ? setTimeout(() => {
|
|
423
|
-
timedOut = true;
|
|
424
|
-
void terminate();
|
|
425
|
-
}, timeoutMs) : null;
|
|
426
|
-
if (timer && typeof timer.unref === "function") {
|
|
427
|
-
;
|
|
428
|
-
timer.unref();
|
|
429
|
-
}
|
|
430
|
-
const onAbort = () => {
|
|
431
|
-
aborted = true;
|
|
432
|
-
void terminate();
|
|
433
|
-
};
|
|
434
|
-
if (options.signal) {
|
|
435
|
-
if (options.signal.aborted) onAbort();
|
|
436
|
-
else options.signal.addEventListener("abort", onAbort, { once: true });
|
|
437
|
-
}
|
|
438
|
-
child.stdout?.on("data", (chunk) => {
|
|
439
|
-
stdoutCapture.append(chunk);
|
|
440
|
-
});
|
|
441
|
-
child.stderr?.on("data", (chunk) => {
|
|
442
|
-
stderrCapture.append(chunk);
|
|
443
|
-
});
|
|
444
|
-
const finalize = (result) => {
|
|
445
|
-
if (settled) return;
|
|
446
|
-
settled = true;
|
|
447
|
-
clearLifecycle();
|
|
448
|
-
resolve2(result);
|
|
449
|
-
};
|
|
450
|
-
child.on("error", (err) => {
|
|
451
|
-
leaderClosed = true;
|
|
452
|
-
if (timer) clearTimeout(timer);
|
|
453
|
-
void (async () => {
|
|
454
|
-
try {
|
|
455
|
-
if (termination) await termination;
|
|
456
|
-
rejectOnce(err);
|
|
457
|
-
} catch (terminationError) {
|
|
458
|
-
rejectOnce(terminationError);
|
|
459
|
-
}
|
|
460
|
-
})();
|
|
461
|
-
});
|
|
462
|
-
child.on("close", (code, signal) => {
|
|
463
|
-
leaderClosed = true;
|
|
464
|
-
if (timer) clearTimeout(timer);
|
|
465
|
-
void (async () => {
|
|
466
|
-
const stdout = stdoutCapture.text();
|
|
467
|
-
const stderr = stderrCapture.text();
|
|
468
|
-
try {
|
|
469
|
-
if (!termination && process.platform !== "win32" && typeof child.pid === "number" && processGroupExists(child.pid)) {
|
|
470
|
-
await terminate();
|
|
471
|
-
}
|
|
472
|
-
if (termination) await termination;
|
|
473
|
-
const durationMs = Date.now() - startedAt;
|
|
474
|
-
const usage = options.codexReproducible ? parseCodexTokenUsage(stdout) : void 0;
|
|
475
|
-
finalize({
|
|
476
|
-
exitCode: code,
|
|
477
|
-
stdout,
|
|
478
|
-
stderr: redactCodexHome(stderr, env.CODEX_HOME),
|
|
479
|
-
killedBySignal: signal,
|
|
480
|
-
durationMs,
|
|
481
|
-
timedOut,
|
|
482
|
-
aborted,
|
|
483
|
-
...usage ? { usage } : {},
|
|
484
|
-
...evidence ? { evidence } : {}
|
|
485
|
-
});
|
|
486
|
-
} catch (err) {
|
|
487
|
-
if (settled) return;
|
|
488
|
-
settled = true;
|
|
489
|
-
clearLifecycle();
|
|
490
|
-
const durationMs = Date.now() - startedAt;
|
|
491
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
492
|
-
reject(
|
|
493
|
-
options.codexReproducible ? createCodexExecutionDiagnosticError({
|
|
494
|
-
reason,
|
|
495
|
-
exitCode: code,
|
|
496
|
-
killedBySignal: signal,
|
|
497
|
-
timedOut,
|
|
498
|
-
aborted,
|
|
499
|
-
durationMs,
|
|
500
|
-
stdout,
|
|
501
|
-
stderr,
|
|
502
|
-
codexHome: env.CODEX_HOME,
|
|
503
|
-
redactionValues: isolated.diagnosticRedactionValues,
|
|
504
|
-
cause: err
|
|
505
|
-
}) : err instanceof Error ? err : new Error(reason)
|
|
506
|
-
);
|
|
507
|
-
}
|
|
508
|
-
})();
|
|
509
|
-
});
|
|
510
|
-
});
|
|
511
|
-
} finally {
|
|
512
|
-
await isolated.cleanup();
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
var CODEX_EXECUTION_POLICY = {
|
|
516
|
-
sessionPersistence: "ephemeral",
|
|
517
|
-
userConfig: false,
|
|
518
|
-
rules: false,
|
|
519
|
-
projectInstructions: false,
|
|
520
|
-
skillInstructions: false,
|
|
521
|
-
appInstructions: false,
|
|
522
|
-
toolSuggestions: false,
|
|
523
|
-
multiAgentInstructions: false,
|
|
524
|
-
sandbox: "workspace-write",
|
|
525
|
-
permissionProfile: "agent_runtime_reproducible",
|
|
526
|
-
approvalPolicy: "never",
|
|
527
|
-
shellNetwork: false,
|
|
528
|
-
webSearch: false,
|
|
529
|
-
serviceTier: "default",
|
|
530
|
-
shellEnvironment: "core-filtered",
|
|
531
|
-
loginShell: false,
|
|
532
|
-
credentialsReadable: false,
|
|
533
|
-
hostHomeReadable: false,
|
|
534
|
-
procEnvironment: "private-sanitized",
|
|
535
|
-
sensitiveEnvironmentNamesVisible: false,
|
|
536
|
-
parentRepoRead: false,
|
|
537
|
-
gitMetadata: false,
|
|
538
|
-
temporaryDirectory: "workspace-private",
|
|
539
|
-
stagedExecutable: "static-elf-read-only",
|
|
540
|
-
callerReadDeniedPaths: "enforced",
|
|
541
|
-
containerSockets: false
|
|
542
|
-
};
|
|
543
|
-
var FORBIDDEN_CODEX_PROMPT_MARKERS = [
|
|
544
|
-
"<skills_instructions>",
|
|
545
|
-
"# AGENTS.md instructions",
|
|
546
|
-
"<apps_instructions>",
|
|
547
|
-
"<recommended_plugins>",
|
|
548
|
-
"<multi_agent_mode>",
|
|
549
|
-
"You are `/root`, the primary agent"
|
|
550
|
-
];
|
|
551
|
-
async function collectCodexExecutionEvidence(opts) {
|
|
552
|
-
assertCodexReproducibleInvocation(opts.command, opts.args);
|
|
553
|
-
const prompt = opts.args[1];
|
|
554
|
-
if (opts.args[0] !== "exec" || typeof prompt !== "string" || prompt.length === 0) {
|
|
555
|
-
throw new Error("runLocalHarness: reproducible Codex invocation must be codex exec <prompt>");
|
|
556
|
-
}
|
|
557
|
-
const cliVersion = (await runCodexProbe(opts, ["--version"])).stdout.trim();
|
|
558
|
-
if (!/^codex-cli \d+\.\d+\.\d+(?:[-+].+)?$/.test(cliVersion)) {
|
|
559
|
-
throw new Error(
|
|
560
|
-
`runLocalHarness: unexpected Codex CLI version output ${JSON.stringify(cliVersion)}`
|
|
561
|
-
);
|
|
562
|
-
}
|
|
563
|
-
const hiddenEnvironmentChecks = [
|
|
564
|
-
"CODEX_HOME",
|
|
565
|
-
"CODEX_ACCESS_TOKEN",
|
|
566
|
-
"CODEX_API_KEY",
|
|
567
|
-
"OPENAI_API_KEY"
|
|
568
|
-
].map((name) => `test -z "\${${name}:-}"`).join(" && ");
|
|
569
|
-
const sandboxProof = [
|
|
570
|
-
hiddenEnvironmentChecks,
|
|
571
|
-
"test -r .",
|
|
572
|
-
"! test -r .git",
|
|
573
|
-
'touch "$1"',
|
|
574
|
-
'rm "$1"',
|
|
575
|
-
'staged_dir=$(dirname "$2")',
|
|
576
|
-
'test -x "$staged_dir/codex"',
|
|
577
|
-
'! chmod 700 "$staged_dir" 2>/dev/null',
|
|
578
|
-
'! rm "$staged_dir/codex" 2>/dev/null',
|
|
579
|
-
'! touch "$2" 2>/dev/null',
|
|
580
|
-
"shift 2",
|
|
581
|
-
'for path in "$@"; do ! test -r "$path" || exit 1; done',
|
|
582
|
-
'touch "$TMPDIR/.agent-runtime-tmp-proof"',
|
|
583
|
-
'rm "$TMPDIR/.agent-runtime-tmp-proof"'
|
|
584
|
-
].join(" && ");
|
|
585
|
-
await runCodexProbe(opts, [
|
|
586
|
-
"sandbox",
|
|
587
|
-
"-P",
|
|
588
|
-
CODEX_PERMISSION_PROFILE,
|
|
589
|
-
"-C",
|
|
590
|
-
opts.cwd,
|
|
591
|
-
"sh",
|
|
592
|
-
"-c",
|
|
593
|
-
sandboxProof,
|
|
594
|
-
"sh",
|
|
595
|
-
opts.writeProbe,
|
|
596
|
-
opts.stagedWriteProbe,
|
|
597
|
-
...opts.deniedProbePaths
|
|
598
|
-
]);
|
|
599
|
-
await runCodexProbe(opts, [
|
|
600
|
-
"sandbox",
|
|
601
|
-
"-P",
|
|
602
|
-
CODEX_PERMISSION_PROFILE,
|
|
603
|
-
"-C",
|
|
604
|
-
opts.cwd,
|
|
605
|
-
"python3",
|
|
606
|
-
"-c",
|
|
607
|
-
`import os,re
|
|
608
|
-
pattern=re.compile(br'(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|AUTH|COOKIE|CODEX_HOME)', re.I)
|
|
609
|
-
paths=('/proc/1/environ', '/proc/self/environ', f'/proc/{os.getppid()}/environ')
|
|
610
|
-
for path in dict.fromkeys(paths):
|
|
611
|
-
data=open(path,'rb').read().split(b'\\0')
|
|
612
|
-
names=(item.split(b'=',1)[0] for item in data if b'=' in item)
|
|
613
|
-
assert not any(pattern.search(name) for name in names), path`
|
|
614
|
-
]);
|
|
615
|
-
await runCodexProbe(opts, [
|
|
616
|
-
"sandbox",
|
|
617
|
-
"-P",
|
|
618
|
-
CODEX_PERMISSION_PROFILE,
|
|
619
|
-
"-C",
|
|
620
|
-
opts.cwd,
|
|
621
|
-
"python3",
|
|
622
|
-
"-c",
|
|
623
|
-
"import socket,sys\nfor path in sys.argv[1:]:\n s=socket.socket(socket.AF_UNIX)\n try: s.connect(path)\n except OSError: continue\n raise SystemExit(1)",
|
|
624
|
-
"/var/run/docker.sock",
|
|
625
|
-
"/run/docker.sock",
|
|
626
|
-
"/var/run/containerd/containerd.sock",
|
|
627
|
-
"/run/containerd/containerd.sock"
|
|
628
|
-
]);
|
|
629
|
-
const debugArgs = codexPromptDebugArgs(opts.args, prompt);
|
|
630
|
-
const renderedPrompt = (await runCodexProbe(opts, debugArgs)).stdout;
|
|
631
|
-
let parsedPrompt;
|
|
632
|
-
try {
|
|
633
|
-
parsedPrompt = JSON.parse(renderedPrompt);
|
|
634
|
-
} catch {
|
|
635
|
-
throw new Error("runLocalHarness: codex debug prompt-input returned invalid JSON");
|
|
636
|
-
}
|
|
637
|
-
const promptEvidenceText = normalizeCodexDebugPromptNewlines(prompt);
|
|
638
|
-
if (!Array.isArray(parsedPrompt) || !jsonTextContains(parsedPrompt, promptEvidenceText)) {
|
|
639
|
-
throw new Error("runLocalHarness: Codex prompt evidence did not contain the exact task prompt");
|
|
640
|
-
}
|
|
641
|
-
const promptContextWithoutTask = JSON.stringify(redactJsonText(parsedPrompt, promptEvidenceText));
|
|
642
|
-
for (const marker of FORBIDDEN_CODEX_PROMPT_MARKERS) {
|
|
643
|
-
if (promptContextWithoutTask.includes(marker)) {
|
|
644
|
-
throw new Error(`runLocalHarness: Codex prompt evidence contains forbidden marker ${marker}`);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
for (const marker of ["<permissions instructions>", "<environment_context>"]) {
|
|
648
|
-
if (!renderedPrompt.includes(marker)) {
|
|
649
|
-
throw new Error(`runLocalHarness: Codex prompt evidence is missing required marker ${marker}`);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
const nonPromptArgs = [
|
|
653
|
-
"codex",
|
|
654
|
-
...opts.args.map((arg, index) => index === 1 ? "<PROMPT>" : arg)
|
|
655
|
-
];
|
|
656
|
-
return {
|
|
657
|
-
cliVersion,
|
|
658
|
-
executableSha256: opts.executableSha256,
|
|
659
|
-
requestedPromptSha256: sha256(prompt),
|
|
660
|
-
effectivePromptSha256: sha256(renderedPrompt),
|
|
661
|
-
nonPromptArgsSha256: sha256(JSON.stringify(nonPromptArgs)),
|
|
662
|
-
controlledConfigSha256: opts.controlledConfigSha256,
|
|
663
|
-
readDeniedPathsSha256: opts.readDeniedPathsSha256,
|
|
664
|
-
readDeniedPathCount: opts.readDeniedPathCount,
|
|
665
|
-
readDeniedPaths: [...opts.readDeniedPaths],
|
|
666
|
-
policy: { ...CODEX_EXECUTION_POLICY }
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
function assertCodexReproducibleInvocation(command, args) {
|
|
670
|
-
const executable = basename(command);
|
|
671
|
-
if (executable !== "codex" && executable !== "codex.exe") {
|
|
672
|
-
throw new Error("runLocalHarness: reproducible Codex invocation must use the Codex executable");
|
|
673
|
-
}
|
|
674
|
-
const prompt = args[1];
|
|
675
|
-
const expectedPrefix = ["exec", prompt, ...CODEX_REPRODUCIBLE_ARGS];
|
|
676
|
-
const hasExactPrefix = expectedPrefix.every((arg, index) => args[index] === arg);
|
|
677
|
-
const tail = args.slice(expectedPrefix.length);
|
|
678
|
-
const validModel = tail[0] === "-m" && typeof tail[1] === "string" && tail[1].length > 0 && !tail[1].startsWith("-");
|
|
679
|
-
const validReasoning = tail[2] === "-c" && typeof tail[3] === "string" && /^model_reasoning_effort="(?:none|minimal|low|medium|high|xhigh)"$/.test(tail[3]);
|
|
680
|
-
if (!hasExactPrefix || tail.length !== 4 || !validModel || !validReasoning) {
|
|
681
|
-
throw new Error(
|
|
682
|
-
"runLocalHarness: reproducible Codex invocation does not match the required isolated argv"
|
|
683
|
-
);
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
function codexPromptDebugArgs(execArgs, prompt) {
|
|
687
|
-
const debugArgs = ["debug", "prompt-input"];
|
|
688
|
-
for (let index = 2; index < execArgs.length; index += 1) {
|
|
689
|
-
const arg = execArgs[index];
|
|
690
|
-
const value = execArgs[index + 1];
|
|
691
|
-
if (arg === "-c" && value !== void 0) {
|
|
692
|
-
debugArgs.push("-c", value);
|
|
693
|
-
index += 1;
|
|
694
|
-
} else if (arg === "--disable" && value !== void 0) {
|
|
695
|
-
debugArgs.push("--disable", value);
|
|
696
|
-
index += 1;
|
|
697
|
-
} else if (arg === "-m" && value !== void 0) {
|
|
698
|
-
debugArgs.push("-c", `model=${JSON.stringify(value)}`);
|
|
699
|
-
index += 1;
|
|
700
|
-
} else if (arg === "-s" && value !== void 0) {
|
|
701
|
-
debugArgs.push("-c", `sandbox_mode=${JSON.stringify(value)}`);
|
|
702
|
-
index += 1;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
debugArgs.push(prompt);
|
|
706
|
-
return debugArgs;
|
|
707
|
-
}
|
|
708
|
-
function runCodexProbe(opts, args) {
|
|
709
|
-
return new Promise((resolve2, reject) => {
|
|
710
|
-
let child;
|
|
711
|
-
try {
|
|
712
|
-
child = opts.spawn(opts.command, args, {
|
|
713
|
-
cwd: opts.cwd,
|
|
714
|
-
env: opts.env,
|
|
715
|
-
stdio: "pipe",
|
|
716
|
-
detached: process.platform !== "win32"
|
|
717
|
-
});
|
|
718
|
-
} catch (err) {
|
|
719
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
child.stdin?.end();
|
|
723
|
-
let stdout = "";
|
|
724
|
-
let stderr = "";
|
|
725
|
-
let settled = false;
|
|
726
|
-
let leaderClosed = false;
|
|
727
|
-
let termination = null;
|
|
728
|
-
let terminationReason = null;
|
|
729
|
-
const clearLifecycle = () => {
|
|
730
|
-
clearTimeout(timer);
|
|
731
|
-
opts.signal?.removeEventListener("abort", onAbort);
|
|
732
|
-
};
|
|
733
|
-
const rejectOnce = (err) => {
|
|
734
|
-
if (settled) return;
|
|
735
|
-
settled = true;
|
|
736
|
-
clearLifecycle();
|
|
737
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
738
|
-
};
|
|
739
|
-
const terminate = (reason) => {
|
|
740
|
-
if (reason) terminationReason ??= reason;
|
|
741
|
-
if (!termination) {
|
|
742
|
-
termination = terminateProcessTreeAndConfirm(child, () => leaderClosed);
|
|
743
|
-
void termination.catch(rejectOnce);
|
|
744
|
-
}
|
|
745
|
-
return termination;
|
|
746
|
-
};
|
|
747
|
-
const timer = setTimeout(() => {
|
|
748
|
-
void terminate("timed out");
|
|
749
|
-
}, 1e4);
|
|
750
|
-
timer.unref?.();
|
|
751
|
-
const onAbort = () => {
|
|
752
|
-
void terminate("aborted");
|
|
753
|
-
};
|
|
754
|
-
if (opts.signal) {
|
|
755
|
-
if (opts.signal.aborted) onAbort();
|
|
756
|
-
else opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
757
|
-
}
|
|
758
|
-
child.stdout?.on("data", (chunk) => {
|
|
759
|
-
stdout += String(chunk);
|
|
760
|
-
});
|
|
761
|
-
child.stderr?.on("data", (chunk) => {
|
|
762
|
-
stderr += String(chunk);
|
|
763
|
-
});
|
|
764
|
-
child.on("error", (err) => {
|
|
765
|
-
leaderClosed = true;
|
|
766
|
-
clearTimeout(timer);
|
|
767
|
-
void (async () => {
|
|
768
|
-
try {
|
|
769
|
-
if (termination) await termination;
|
|
770
|
-
rejectOnce(err);
|
|
771
|
-
} catch (terminationError) {
|
|
772
|
-
rejectOnce(terminationError);
|
|
773
|
-
}
|
|
774
|
-
})();
|
|
775
|
-
});
|
|
776
|
-
child.on("close", (code, signal) => {
|
|
777
|
-
leaderClosed = true;
|
|
778
|
-
clearTimeout(timer);
|
|
779
|
-
void (async () => {
|
|
780
|
-
try {
|
|
781
|
-
if (!termination && process.platform !== "win32" && typeof child.pid === "number" && processGroupExists(child.pid)) {
|
|
782
|
-
await terminate();
|
|
783
|
-
}
|
|
784
|
-
if (termination) await termination;
|
|
785
|
-
if (settled) return;
|
|
786
|
-
settled = true;
|
|
787
|
-
clearLifecycle();
|
|
788
|
-
if (code !== 0 || terminationReason) {
|
|
789
|
-
const safeStderr = redactCodexHome(stderr, opts.env.CODEX_HOME);
|
|
790
|
-
reject(
|
|
791
|
-
new Error(
|
|
792
|
-
`runLocalHarness: Codex evidence probe ${terminationReason ?? "failed"} (exit ${String(code)}, signal ${String(signal)}): ${safeStderr.trim()}`
|
|
793
|
-
)
|
|
794
|
-
);
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
resolve2({ stdout, stderr });
|
|
798
|
-
} catch (err) {
|
|
799
|
-
rejectOnce(err);
|
|
800
|
-
}
|
|
801
|
-
})();
|
|
802
|
-
});
|
|
803
|
-
});
|
|
804
|
-
}
|
|
805
|
-
function sha256(value) {
|
|
806
|
-
return createHash("sha256").update(value).digest("hex");
|
|
807
|
-
}
|
|
808
|
-
function normalizeCodexDebugPromptNewlines(value) {
|
|
809
|
-
return value.replaceAll("\r\n", "\n");
|
|
810
|
-
}
|
|
811
|
-
function jsonTextContains(value, needle) {
|
|
812
|
-
if (typeof value === "string") return value.includes(needle);
|
|
813
|
-
if (Array.isArray(value)) return value.some((item) => jsonTextContains(item, needle));
|
|
814
|
-
if (isRecord(value)) return Object.values(value).some((item) => jsonTextContains(item, needle));
|
|
815
|
-
return false;
|
|
816
|
-
}
|
|
817
|
-
function redactJsonText(value, text) {
|
|
818
|
-
if (typeof value === "string") return value.replaceAll(text, "");
|
|
819
|
-
if (Array.isArray(value)) return value.map((item) => redactJsonText(item, text));
|
|
820
|
-
if (isRecord(value)) {
|
|
821
|
-
return Object.fromEntries(
|
|
822
|
-
Object.entries(value).map(([key, item]) => [key, redactJsonText(item, text)])
|
|
823
|
-
);
|
|
824
|
-
}
|
|
825
|
-
return value;
|
|
826
|
-
}
|
|
827
|
-
var CODEX_AUTH_ENV = ["CODEX_ACCESS_TOKEN", "CODEX_API_KEY", "OPENAI_API_KEY"];
|
|
828
|
-
var CODEX_PERMISSION_PROFILE = "agent_runtime_reproducible";
|
|
829
|
-
async function isolateCodexHome(opts) {
|
|
830
|
-
const isolatedHome = await mkdtemp(join(tmpdir(), "agent-runtime-codex-"));
|
|
831
|
-
await chmod(isolatedHome, 448);
|
|
832
|
-
let workspaceTemp = "";
|
|
833
|
-
let stagedExecutableDir = "";
|
|
834
|
-
let ownsStagedExecutableDir = false;
|
|
835
|
-
let writeProbe = "";
|
|
836
|
-
const cleanup = async () => {
|
|
837
|
-
if (ownsStagedExecutableDir) {
|
|
838
|
-
await chmod(stagedExecutableDir, 448).catch(() => void 0);
|
|
839
|
-
}
|
|
840
|
-
await Promise.all([
|
|
841
|
-
...writeProbe ? [rm(writeProbe, { force: true })] : [],
|
|
842
|
-
...workspaceTemp ? [rm(workspaceTemp, { recursive: true, force: true })] : [],
|
|
843
|
-
...ownsStagedExecutableDir ? [rm(stagedExecutableDir, { recursive: true, force: true })] : [],
|
|
844
|
-
rm(isolatedHome, { recursive: true, force: true })
|
|
845
|
-
]);
|
|
846
|
-
};
|
|
847
|
-
try {
|
|
848
|
-
const readDeniedPaths = await normalizeReadDeniedPaths(opts.readDeniedPaths);
|
|
849
|
-
const candidateRoot = await realpath(opts.cwd);
|
|
850
|
-
writeProbe = join(candidateRoot, `.agent-runtime-write-proof-${randomUUID()}`);
|
|
851
|
-
const stagedPath = join(candidateRoot, ".agent-runtime-bin");
|
|
852
|
-
if (readDeniedPaths.some(
|
|
853
|
-
(path) => isSameOrDescendant(candidateRoot, path) || isSameOrDescendant(stagedPath, path) || isSameOrDescendant(path, stagedPath)
|
|
854
|
-
)) {
|
|
855
|
-
throw new Error(
|
|
856
|
-
"runLocalHarness: codexReadDeniedPaths cannot contain the candidate root or staged executable"
|
|
857
|
-
);
|
|
858
|
-
}
|
|
859
|
-
const nativeExecutable = await opts.resolveExecutable(opts.command, opts.baseEnv);
|
|
860
|
-
if (!await isStaticElfExecutable(nativeExecutable)) {
|
|
861
|
-
throw new Error(
|
|
862
|
-
"runLocalHarness: reproducible Codex mode requires a statically linked Linux Codex ELF"
|
|
863
|
-
);
|
|
864
|
-
}
|
|
865
|
-
const sourceExecutableSha256 = await sha256File(nativeExecutable);
|
|
866
|
-
stagedExecutableDir = stagedPath;
|
|
867
|
-
try {
|
|
868
|
-
await mkdir(stagedExecutableDir, { mode: 448 });
|
|
869
|
-
ownsStagedExecutableDir = true;
|
|
870
|
-
} catch (err) {
|
|
871
|
-
throw new Error(
|
|
872
|
-
`runLocalHarness: candidate-private .agent-runtime-bin must not already exist: ${String(err.code ?? err)}`
|
|
873
|
-
);
|
|
874
|
-
}
|
|
875
|
-
const stagedExecutable = join(stagedExecutableDir, "codex");
|
|
876
|
-
await copyFile(nativeExecutable, stagedExecutable, constants2.COPYFILE_FICLONE);
|
|
877
|
-
await chmod(stagedExecutable, 320);
|
|
878
|
-
const executableSha256 = await sha256File(stagedExecutable);
|
|
879
|
-
if (executableSha256 !== sourceExecutableSha256 || !await isStaticElfExecutable(stagedExecutable)) {
|
|
880
|
-
throw new Error("runLocalHarness: staged Codex executable digest mismatch");
|
|
881
|
-
}
|
|
882
|
-
await chmod(stagedExecutableDir, 320);
|
|
883
|
-
workspaceTemp = await mkdtemp(join(candidateRoot, ".agent-runtime-tmp-"));
|
|
884
|
-
await chmod(workspaceTemp, 448);
|
|
885
|
-
const ambientHome = resolve(opts.baseEnv.CODEX_HOME?.trim() || join(homedir(), ".codex"));
|
|
886
|
-
const ambientAuth = join(ambientHome, "auth.json");
|
|
887
|
-
const isolatedAuth = join(isolatedHome, "auth.json");
|
|
888
|
-
let authText = "";
|
|
889
|
-
try {
|
|
890
|
-
await access(ambientAuth, constants2.R_OK);
|
|
891
|
-
authText = await readCodexAuthRedactionText(ambientAuth);
|
|
892
|
-
await symlink(ambientAuth, isolatedAuth);
|
|
893
|
-
} catch (err) {
|
|
894
|
-
throw new Error(
|
|
895
|
-
`runLocalHarness: reproducible Codex mode requires readable auth.json: ${String(err.code ?? err)}`
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
const deniedHostRoots = [homedir(), opts.baseEnv.HOME?.trim(), ambientHome].filter((path) => Boolean(path)).map((path) => resolve(path)).filter((path, index, paths) => paths.indexOf(path) === index);
|
|
899
|
-
const controlledConfig = codexControlledConfig({
|
|
900
|
-
ambientTmp: opts.baseEnv.TMPDIR?.trim() || tmpdir(),
|
|
901
|
-
deniedHostRoots,
|
|
902
|
-
workspaceTemp,
|
|
903
|
-
stagedExecutableDir,
|
|
904
|
-
readDeniedPaths
|
|
905
|
-
});
|
|
906
|
-
await writeFile(join(isolatedHome, "config.toml"), controlledConfig, { mode: 384 });
|
|
907
|
-
const env = { ...opts.baseEnv };
|
|
908
|
-
for (const name of Object.keys(env)) {
|
|
909
|
-
if (codexSensitiveEnvironmentName.test(name) || CODEX_AUTH_ENV.some((authName) => authName === name)) {
|
|
910
|
-
delete env[name];
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
env.CODEX_HOME = isolatedHome;
|
|
914
|
-
const home = homedir();
|
|
915
|
-
const deniedProbePaths = [
|
|
916
|
-
...deniedHostRoots,
|
|
917
|
-
ambientAuth,
|
|
918
|
-
isolatedAuth,
|
|
919
|
-
join(home, ".claude", ".credentials.json"),
|
|
920
|
-
join(home, ".claude.json"),
|
|
921
|
-
join(home, ".local", "share", "opencode", "auth.json"),
|
|
922
|
-
join(home, ".agents"),
|
|
923
|
-
join(home, "Documents"),
|
|
924
|
-
join(home, "Downloads"),
|
|
925
|
-
join(home, ".npm"),
|
|
926
|
-
join(home, "code"),
|
|
927
|
-
...readDeniedPaths
|
|
928
|
-
].filter((path, index, paths) => paths.indexOf(path) === index);
|
|
929
|
-
return {
|
|
930
|
-
env,
|
|
931
|
-
command: stagedExecutable,
|
|
932
|
-
cleanup,
|
|
933
|
-
controlledConfigSha256: sha256(controlledConfig),
|
|
934
|
-
executableSha256,
|
|
935
|
-
readDeniedPaths,
|
|
936
|
-
readDeniedPathsSha256: sha256(JSON.stringify(readDeniedPaths)),
|
|
937
|
-
readDeniedPathCount: readDeniedPaths.length,
|
|
938
|
-
diagnosticRedactionValues: collectCodexDiagnosticRedactionValues(opts.baseEnv, authText),
|
|
939
|
-
ambientAuth,
|
|
940
|
-
isolatedAuth,
|
|
941
|
-
writeProbe,
|
|
942
|
-
stagedWriteProbe: join(stagedExecutableDir, "write-proof"),
|
|
943
|
-
deniedProbePaths
|
|
944
|
-
};
|
|
945
|
-
} catch (err) {
|
|
946
|
-
await cleanup();
|
|
947
|
-
throw err;
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
function codexControlledConfig(opts) {
|
|
951
|
-
const rules = /* @__PURE__ */ new Map();
|
|
952
|
-
const broadDeniedPaths = minimalPathRoots([
|
|
953
|
-
...opts.deniedHostRoots.map((path) => resolve(path)),
|
|
954
|
-
tmpdir(),
|
|
955
|
-
opts.ambientTmp,
|
|
956
|
-
"/proc",
|
|
957
|
-
"/run/docker",
|
|
958
|
-
"/run/containerd"
|
|
959
|
-
]);
|
|
960
|
-
for (const path of broadDeniedPaths) {
|
|
961
|
-
rules.set(path, "deny");
|
|
962
|
-
}
|
|
963
|
-
const workspaceRoot = dirname(opts.workspaceTemp);
|
|
964
|
-
for (const path of opts.readDeniedPaths) {
|
|
965
|
-
const alreadyDenied = broadDeniedPaths.some((root) => isSameOrDescendant(path, root));
|
|
966
|
-
if (!alreadyDenied || isSameOrDescendant(path, workspaceRoot)) rules.set(path, "deny");
|
|
967
|
-
}
|
|
968
|
-
if (!rules.has(opts.workspaceTemp)) rules.set(opts.workspaceTemp, "write");
|
|
969
|
-
if (!rules.has(opts.stagedExecutableDir)) rules.set(opts.stagedExecutableDir, "read");
|
|
970
|
-
const filesystemRules = [...rules].sort(([a], [b]) => a.localeCompare(b)).map(([path, access2]) => `${JSON.stringify(path)} = ${JSON.stringify(access2)}`).join("\n");
|
|
971
|
-
return `default_permissions = "${CODEX_PERMISSION_PROFILE}"
|
|
972
|
-
approval_policy = "never"
|
|
973
|
-
allow_login_shell = false
|
|
974
|
-
service_tier = "default"
|
|
975
|
-
|
|
976
|
-
[shell_environment_policy]
|
|
977
|
-
inherit = "core"
|
|
978
|
-
ignore_default_excludes = false
|
|
979
|
-
exclude = ["^CODEX_HOME$", "(?i).*TOKEN.*", "(?i).*KEY.*", "(?i).*SECRET.*", "(?i).*PASSWORD.*", "(?i).*CREDENTIAL.*", "(?i).*AUTH.*", "(?i).*COOKIE.*"]
|
|
980
|
-
set = { TMPDIR = ${JSON.stringify(opts.workspaceTemp)} }
|
|
981
|
-
|
|
982
|
-
[permissions.${CODEX_PERMISSION_PROFILE}.filesystem]
|
|
983
|
-
":minimal" = "read"
|
|
984
|
-
${filesystemRules}
|
|
985
|
-
":slash_tmp" = "deny"
|
|
986
|
-
|
|
987
|
-
[permissions.${CODEX_PERMISSION_PROFILE}.filesystem.":workspace_roots"]
|
|
988
|
-
"." = "write"
|
|
989
|
-
".git" = "deny"
|
|
990
|
-
|
|
991
|
-
[permissions.${CODEX_PERMISSION_PROFILE}.network]
|
|
992
|
-
enabled = false
|
|
993
|
-
|
|
994
|
-
[permissions.${CODEX_PERMISSION_PROFILE}.network.unix_sockets]
|
|
995
|
-
"/var/run/docker.sock" = "deny"
|
|
996
|
-
"/run/docker.sock" = "deny"
|
|
997
|
-
"/var/run/containerd/containerd.sock" = "deny"
|
|
998
|
-
"/run/containerd/containerd.sock" = "deny"
|
|
999
|
-
`;
|
|
1000
|
-
}
|
|
1001
|
-
function isSameOrDescendant(path, root) {
|
|
1002
|
-
const normalizedPath = resolve(path);
|
|
1003
|
-
const normalizedRoot = resolve(root);
|
|
1004
|
-
return normalizedPath === normalizedRoot || normalizedPath.startsWith(`${normalizedRoot}${sep}`);
|
|
1005
|
-
}
|
|
1006
|
-
function minimalPathRoots(paths) {
|
|
1007
|
-
const ordered = [...new Set(paths.map((path) => resolve(path)))].sort(
|
|
1008
|
-
(a, b) => a.length - b.length || a.localeCompare(b)
|
|
1009
|
-
);
|
|
1010
|
-
return ordered.filter(
|
|
1011
|
-
(path, index) => !ordered.slice(0, index).some((root) => isSameOrDescendant(path, root))
|
|
1012
|
-
);
|
|
1013
|
-
}
|
|
1014
|
-
async function normalizeReadDeniedPaths(paths) {
|
|
1015
|
-
if (!Array.isArray(paths)) {
|
|
1016
|
-
throw new Error("runLocalHarness: codexReadDeniedPaths must be an array of absolute paths");
|
|
1017
|
-
}
|
|
1018
|
-
const normalized = /* @__PURE__ */ new Set();
|
|
1019
|
-
for (const path of paths) {
|
|
1020
|
-
if (typeof path !== "string" || path.length === 0 || path.includes("\0") || !isAbsolute(path)) {
|
|
1021
|
-
throw new Error("runLocalHarness: codexReadDeniedPaths must contain absolute paths");
|
|
1022
|
-
}
|
|
1023
|
-
const absolute = resolve(path);
|
|
1024
|
-
normalized.add(absolute);
|
|
1025
|
-
try {
|
|
1026
|
-
normalized.add(await realpath(absolute));
|
|
1027
|
-
} catch (err) {
|
|
1028
|
-
if (err.code !== "ENOENT") throw err;
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
return [...normalized].sort();
|
|
1032
|
-
}
|
|
1033
|
-
async function resolveCodexNativeExecutable(command, env) {
|
|
1034
|
-
const candidates = command.includes(sep) || isAbsolute(command) ? [resolve(command)] : (env.PATH ?? "").split(delimiter).filter(Boolean).map((entry) => join(entry, command));
|
|
1035
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1036
|
-
for (const candidate of candidates) {
|
|
1037
|
-
let resolved;
|
|
1038
|
-
try {
|
|
1039
|
-
await access(candidate, constants2.X_OK);
|
|
1040
|
-
resolved = await realpath(candidate);
|
|
1041
|
-
} catch {
|
|
1042
|
-
continue;
|
|
1043
|
-
}
|
|
1044
|
-
if (seen.has(resolved)) continue;
|
|
1045
|
-
seen.add(resolved);
|
|
1046
|
-
if (await isStaticElfExecutable(resolved)) return resolved;
|
|
1047
|
-
const packageRoot = await findCodexPackageRoot(resolved);
|
|
1048
|
-
if (!packageRoot) continue;
|
|
1049
|
-
const bundled = await findBundledCodexExecutable(packageRoot);
|
|
1050
|
-
if (bundled) return bundled;
|
|
1051
|
-
}
|
|
1052
|
-
throw new Error(
|
|
1053
|
-
"runLocalHarness: could not resolve the native Codex executable from the requested command"
|
|
1054
|
-
);
|
|
1055
|
-
}
|
|
1056
|
-
async function findCodexPackageRoot(file) {
|
|
1057
|
-
let cursor = dirname(file);
|
|
1058
|
-
while (true) {
|
|
1059
|
-
try {
|
|
1060
|
-
const parsed = JSON.parse(await readFile(join(cursor, "package.json"), "utf8"));
|
|
1061
|
-
if (parsed.name === "@openai/codex") return cursor;
|
|
1062
|
-
} catch {
|
|
1063
|
-
}
|
|
1064
|
-
const parent = dirname(cursor);
|
|
1065
|
-
if (parent === cursor) return void 0;
|
|
1066
|
-
cursor = parent;
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
async function findBundledCodexExecutable(packageRoot) {
|
|
1070
|
-
const target = linuxCodexTarget();
|
|
1071
|
-
const candidates = [
|
|
1072
|
-
join(
|
|
1073
|
-
packageRoot,
|
|
1074
|
-
"node_modules",
|
|
1075
|
-
"@openai",
|
|
1076
|
-
target.packageName,
|
|
1077
|
-
"vendor",
|
|
1078
|
-
target.triple,
|
|
1079
|
-
"bin",
|
|
1080
|
-
"codex"
|
|
1081
|
-
),
|
|
1082
|
-
join(packageRoot, "vendor", target.triple, "bin", "codex")
|
|
1083
|
-
];
|
|
1084
|
-
for (const executable of candidates) {
|
|
1085
|
-
if (await isStaticElfExecutable(executable)) return executable;
|
|
1086
|
-
}
|
|
1087
|
-
return void 0;
|
|
1088
|
-
}
|
|
1089
|
-
function linuxCodexTarget() {
|
|
1090
|
-
if (process.platform !== "linux") {
|
|
1091
|
-
throw new Error("runLocalHarness: reproducible Codex mode requires Linux");
|
|
1092
|
-
}
|
|
1093
|
-
if (process.arch === "x64") {
|
|
1094
|
-
return { packageName: "codex-linux-x64", triple: "x86_64-unknown-linux-musl" };
|
|
1095
|
-
}
|
|
1096
|
-
if (process.arch === "arm64") {
|
|
1097
|
-
return { packageName: "codex-linux-arm64", triple: "aarch64-unknown-linux-musl" };
|
|
1098
|
-
}
|
|
1099
|
-
throw new Error(`runLocalHarness: unsupported Linux architecture ${process.arch}`);
|
|
1100
|
-
}
|
|
1101
|
-
async function isStaticElfExecutable(path) {
|
|
1102
|
-
let handle;
|
|
1103
|
-
try {
|
|
1104
|
-
await access(path, constants2.X_OK);
|
|
1105
|
-
handle = await open2(path, "r");
|
|
1106
|
-
const header = Buffer.alloc(64);
|
|
1107
|
-
const { bytesRead } = await handle.read(header, 0, header.length, 0);
|
|
1108
|
-
if (bytesRead !== header.length || !header.subarray(0, 4).equals(Buffer.from([127, 69, 76, 70])) || header[4] !== 2 || header[5] !== 1) {
|
|
1109
|
-
return false;
|
|
1110
|
-
}
|
|
1111
|
-
const programHeaderOffset = Number(header.readBigUInt64LE(32));
|
|
1112
|
-
const programHeaderSize = header.readUInt16LE(54);
|
|
1113
|
-
const programHeaderCount = header.readUInt16LE(56);
|
|
1114
|
-
const expectedMachine = process.arch === "x64" ? 62 : process.arch === "arm64" ? 183 : -1;
|
|
1115
|
-
if (![2, 3].includes(header.readUInt16LE(16)) || header.readUInt16LE(18) !== expectedMachine || !Number.isSafeInteger(programHeaderOffset) || programHeaderOffset < 64 || programHeaderSize < 56 || programHeaderSize > 1024 || programHeaderCount === 0 || programHeaderCount > 1024) {
|
|
1116
|
-
return false;
|
|
1117
|
-
}
|
|
1118
|
-
const tableSize = programHeaderSize * programHeaderCount;
|
|
1119
|
-
const table = Buffer.alloc(tableSize);
|
|
1120
|
-
const tableRead = await handle.read(table, 0, tableSize, programHeaderOffset);
|
|
1121
|
-
if (tableRead.bytesRead !== tableSize) return false;
|
|
1122
|
-
for (let index = 0; index < programHeaderCount; index += 1) {
|
|
1123
|
-
const type = table.readUInt32LE(index * programHeaderSize);
|
|
1124
|
-
if (type === 3) return false;
|
|
1125
|
-
}
|
|
1126
|
-
return true;
|
|
1127
|
-
} catch {
|
|
1128
|
-
return false;
|
|
1129
|
-
} finally {
|
|
1130
|
-
await handle?.close();
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
function sha256File(path) {
|
|
1134
|
-
return new Promise((resolveDigest, reject) => {
|
|
1135
|
-
const hash = createHash("sha256");
|
|
1136
|
-
const stream = createReadStream(path);
|
|
1137
|
-
stream.on("error", reject);
|
|
1138
|
-
stream.on("data", (chunk) => hash.update(chunk));
|
|
1139
|
-
stream.on("end", () => resolveDigest(hash.digest("hex")));
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
1142
|
-
function signalProcessTree(child, signal) {
|
|
1143
|
-
if (process.platform !== "win32" && typeof child.pid === "number") {
|
|
1144
|
-
try {
|
|
1145
|
-
process.kill(-child.pid, signal);
|
|
1146
|
-
return;
|
|
1147
|
-
} catch (err) {
|
|
1148
|
-
if (err.code === "ESRCH") return;
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
try {
|
|
1152
|
-
child.kill(signal);
|
|
1153
|
-
} catch {
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
async function terminateProcessTreeAndConfirm(child, leaderClosed, context = "runLocalHarness") {
|
|
1157
|
-
signalProcessTree(child, "SIGTERM");
|
|
1158
|
-
if (process.platform === "win32" || typeof child.pid !== "number") {
|
|
1159
|
-
const graceDeadline2 = Date.now() + processKillGraceMs;
|
|
1160
|
-
while (!leaderClosed() && Date.now() < graceDeadline2) {
|
|
1161
|
-
await delayUntilNextProcessCheck(graceDeadline2);
|
|
1162
|
-
}
|
|
1163
|
-
if (!leaderClosed()) signalProcessTree(child, "SIGKILL");
|
|
1164
|
-
return;
|
|
1165
|
-
}
|
|
1166
|
-
const processGroupId = child.pid;
|
|
1167
|
-
const graceDeadline = Date.now() + processKillGraceMs;
|
|
1168
|
-
if (await waitForProcessGroupExit(processGroupId, graceDeadline)) return;
|
|
1169
|
-
signalProcessTree(child, "SIGKILL");
|
|
1170
|
-
const killDeadline = Date.now() + processGroupExitConfirmMs;
|
|
1171
|
-
if (await waitForProcessGroupExit(processGroupId, killDeadline)) return;
|
|
1172
|
-
throw new Error(`${context}: process group ${processGroupId} survived SIGKILL`);
|
|
1173
|
-
}
|
|
1174
|
-
async function waitForProcessGroupExit(processGroupId, deadline) {
|
|
1175
|
-
while (processGroupExists(processGroupId)) {
|
|
1176
|
-
if (Date.now() >= deadline) return false;
|
|
1177
|
-
await delayUntilNextProcessCheck(deadline);
|
|
1178
|
-
}
|
|
1179
|
-
return true;
|
|
1180
|
-
}
|
|
1181
|
-
async function delayUntilNextProcessCheck(deadline) {
|
|
1182
|
-
const delayMs = Math.min(processGroupExitPollMs, Math.max(0, deadline - Date.now()));
|
|
1183
|
-
if (delayMs > 0) await new Promise((resolve2) => setTimeout(resolve2, delayMs));
|
|
1184
|
-
}
|
|
1185
|
-
function processGroupExists(processGroupId) {
|
|
1186
|
-
try {
|
|
1187
|
-
process.kill(-processGroupId, 0);
|
|
1188
|
-
return true;
|
|
1189
|
-
} catch (err) {
|
|
1190
|
-
if (err.code === "ESRCH") return false;
|
|
1191
|
-
return true;
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
function parseCodexTokenUsage(stdout) {
|
|
1195
|
-
const completed = [];
|
|
1196
|
-
for (const [index, line] of stdout.split(/\r?\n/).entries()) {
|
|
1197
|
-
if (line.trim().length === 0) continue;
|
|
1198
|
-
let event2;
|
|
1199
|
-
try {
|
|
1200
|
-
event2 = JSON.parse(line);
|
|
1201
|
-
} catch {
|
|
1202
|
-
throw new Error(`runLocalHarness: Codex JSONL line ${index + 1} is not valid JSON`);
|
|
1203
|
-
}
|
|
1204
|
-
if (isRecord(event2) && event2.type === "turn.completed") completed.push(event2);
|
|
1205
|
-
}
|
|
1206
|
-
if (completed.length !== 1) {
|
|
1207
|
-
throw new Error(
|
|
1208
|
-
`runLocalHarness: expected exactly one Codex turn.completed usage event, received ${completed.length}`
|
|
1209
|
-
);
|
|
1210
|
-
}
|
|
1211
|
-
const event = completed[0];
|
|
1212
|
-
if (!isRecord(event) || !isRecord(event.usage)) {
|
|
1213
|
-
throw new Error("runLocalHarness: Codex turn.completed event is missing usage");
|
|
1214
|
-
}
|
|
1215
|
-
const usage = event.usage;
|
|
1216
|
-
const inputTokens = naturalNumber(usage.input_tokens, "input_tokens");
|
|
1217
|
-
const cachedInputTokens = naturalNumber(usage.cached_input_tokens, "cached_input_tokens");
|
|
1218
|
-
const outputTokens = naturalNumber(usage.output_tokens, "output_tokens");
|
|
1219
|
-
const reasoningOutputTokens = naturalNumber(
|
|
1220
|
-
usage.reasoning_output_tokens,
|
|
1221
|
-
"reasoning_output_tokens"
|
|
1222
|
-
);
|
|
1223
|
-
if (cachedInputTokens > inputTokens) {
|
|
1224
|
-
throw new Error("runLocalHarness: Codex cached_input_tokens exceeds input_tokens");
|
|
1225
|
-
}
|
|
1226
|
-
if (reasoningOutputTokens > outputTokens) {
|
|
1227
|
-
throw new Error("runLocalHarness: Codex reasoning_output_tokens exceeds output_tokens");
|
|
1228
|
-
}
|
|
1229
|
-
return { inputTokens, cachedInputTokens, outputTokens, reasoningOutputTokens };
|
|
1230
|
-
}
|
|
1231
|
-
function isRecord(value) {
|
|
1232
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1233
|
-
}
|
|
1234
|
-
function naturalNumber(value, field) {
|
|
1235
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
1236
|
-
throw new Error(`runLocalHarness: Codex usage.${field} must be a non-negative safe integer`);
|
|
1237
|
-
}
|
|
1238
|
-
return value;
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
// src/mcp/worktree.ts
|
|
1242
|
-
import { spawn as spawn2 } from "child_process";
|
|
1243
|
-
import { isAbsolute as isAbsolute2, win32 } from "path";
|
|
1244
|
-
async function runGitAsync(args, cwd, runner) {
|
|
1245
|
-
if (runner) return runner(args, { cwd });
|
|
1246
|
-
return new Promise((resolve2, reject) => {
|
|
1247
|
-
const proc = spawn2("git", args, { cwd, stdio: "pipe" });
|
|
1248
|
-
let stdout = "";
|
|
1249
|
-
let stderr = "";
|
|
1250
|
-
proc.stdout?.on("data", (c) => {
|
|
1251
|
-
stdout += String(c);
|
|
1252
|
-
});
|
|
1253
|
-
proc.stderr?.on("data", (c) => {
|
|
1254
|
-
stderr += String(c);
|
|
1255
|
-
});
|
|
1256
|
-
proc.on("error", reject);
|
|
1257
|
-
proc.on("close", (code) => resolve2({ stdout, stderr, exitCode: code ?? -1 }));
|
|
1258
|
-
});
|
|
1259
|
-
}
|
|
1260
|
-
function ensureGitOk(step, result) {
|
|
1261
|
-
if (result.exitCode !== 0) {
|
|
1262
|
-
throw gitFailure(step, result);
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
function gitFailure(step, result) {
|
|
1266
|
-
return new Error(
|
|
1267
|
-
`worktree: git ${step} failed (exit ${result.exitCode}): ${result.stderr.slice(0, 400)}`
|
|
1268
|
-
);
|
|
1269
|
-
}
|
|
1270
|
-
async function createWorktree(options) {
|
|
1271
|
-
const variants = options.variantsDir ?? ".agent-worktrees";
|
|
1272
|
-
const baseRef = options.baseRef ?? "HEAD";
|
|
1273
|
-
const branch = `delegate/${options.runId}`;
|
|
1274
|
-
const path = `${options.repoRoot.replace(/\/+$/, "")}/${variants}/${options.runId}`;
|
|
1275
|
-
const headSha = await runGitAsync(["rev-parse", baseRef], options.repoRoot, options.runGit);
|
|
1276
|
-
ensureGitOk(`rev-parse ${baseRef}`, headSha);
|
|
1277
|
-
const add = await runGitAsync(
|
|
1278
|
-
["worktree", "add", "-b", branch, path, baseRef],
|
|
1279
|
-
options.repoRoot,
|
|
1280
|
-
options.runGit
|
|
1281
|
-
);
|
|
1282
|
-
ensureGitOk(`worktree add ${path}`, add);
|
|
1283
|
-
return { path, baseSha: headSha.stdout.trim(), branch };
|
|
1284
|
-
}
|
|
1285
|
-
async function captureWorktreeDiff(options) {
|
|
1286
|
-
const baseRef = options.baseRef ?? options.worktree.baseSha;
|
|
1287
|
-
const pathspecs = worktreeDiffPathspecs(options.excludePaths ?? []);
|
|
1288
|
-
const staged = await runGitAsync(
|
|
1289
|
-
["add", "-A", ...pathspecs.length > 0 ? ["--", ".", ...pathspecs] : []],
|
|
1290
|
-
options.worktree.path,
|
|
1291
|
-
options.runGit
|
|
1292
|
-
);
|
|
1293
|
-
ensureGitOk("add -A", staged);
|
|
1294
|
-
const patch = await runGitAsync(
|
|
1295
|
-
["diff", "--cached", baseRef, ...pathspecs.length > 0 ? ["--", ".", ...pathspecs] : []],
|
|
1296
|
-
options.worktree.path,
|
|
1297
|
-
options.runGit
|
|
1298
|
-
);
|
|
1299
|
-
ensureGitOk(`diff --cached ${baseRef}`, patch);
|
|
1300
|
-
const shortstat = await runGitAsync(
|
|
1301
|
-
[
|
|
1302
|
-
"diff",
|
|
1303
|
-
"--cached",
|
|
1304
|
-
"--shortstat",
|
|
1305
|
-
baseRef,
|
|
1306
|
-
...pathspecs.length > 0 ? ["--", ".", ...pathspecs] : []
|
|
1307
|
-
],
|
|
1308
|
-
options.worktree.path,
|
|
1309
|
-
options.runGit
|
|
1310
|
-
);
|
|
1311
|
-
ensureGitOk(`diff --cached --shortstat ${baseRef}`, shortstat);
|
|
1312
|
-
const stats = parseShortstat(shortstat.stdout);
|
|
1313
|
-
return { patch: patch.stdout, stats };
|
|
1314
|
-
}
|
|
1315
|
-
function worktreeDiffPathspecs(paths) {
|
|
1316
|
-
const normalized = /* @__PURE__ */ new Set();
|
|
1317
|
-
for (const path of paths) {
|
|
1318
|
-
if (typeof path !== "string" || path.length === 0 || path.includes("\\") || hasControlCharacter(path) || isAbsolute2(path) || win32.isAbsolute(path)) {
|
|
1319
|
-
throw new Error(
|
|
1320
|
-
`worktree: excluded path must be a canonical repository-relative path: ${path}`
|
|
1321
|
-
);
|
|
1322
|
-
}
|
|
1323
|
-
const segments = path.split("/");
|
|
1324
|
-
if (segments.some((segment) => segment.length === 0 || segment === "." || segment === "..")) {
|
|
1325
|
-
throw new Error(
|
|
1326
|
-
`worktree: excluded path must be a canonical repository-relative path: ${path}`
|
|
1327
|
-
);
|
|
1328
|
-
}
|
|
1329
|
-
normalized.add(path);
|
|
1330
|
-
}
|
|
1331
|
-
return [...normalized].sort((left, right) => left.localeCompare(right)).map((path) => `:(top,exclude,literal)${path}`);
|
|
1332
|
-
}
|
|
1333
|
-
function hasControlCharacter(value) {
|
|
1334
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
1335
|
-
const code = value.charCodeAt(index);
|
|
1336
|
-
if (code < 32 || code === 127) return true;
|
|
1337
|
-
}
|
|
1338
|
-
return false;
|
|
1339
|
-
}
|
|
1340
|
-
function parseShortstat(text) {
|
|
1341
|
-
const out = { filesChanged: 0, insertions: 0, deletions: 0 };
|
|
1342
|
-
const filesMatch = text.match(/(\d+)\s+files?\s+changed/);
|
|
1343
|
-
if (filesMatch?.[1]) out.filesChanged = Number(filesMatch[1]);
|
|
1344
|
-
const insertMatch = text.match(/(\d+)\s+insertions?/);
|
|
1345
|
-
if (insertMatch?.[1]) out.insertions = Number(insertMatch[1]);
|
|
1346
|
-
const deleteMatch = text.match(/(\d+)\s+deletions?/);
|
|
1347
|
-
if (deleteMatch?.[1]) out.deletions = Number(deleteMatch[1]);
|
|
1348
|
-
return out;
|
|
1349
|
-
}
|
|
1350
|
-
async function removeWorktree(options) {
|
|
1351
|
-
const force = options.force ?? true;
|
|
1352
|
-
const args = ["worktree", "remove"];
|
|
1353
|
-
if (force) args.push("--force");
|
|
1354
|
-
args.push(options.worktree.path);
|
|
1355
|
-
const removal = await runGitAsync(args, options.repoRoot, options.runGit);
|
|
1356
|
-
const removalError = removal.exitCode !== 0 && !/not a working tree/iu.test(removal.stderr) ? gitFailure(`worktree remove ${options.worktree.path}`, removal) : void 0;
|
|
1357
|
-
const branchRemoval = await runGitAsync(
|
|
1358
|
-
["branch", "-D", options.worktree.branch],
|
|
1359
|
-
options.repoRoot,
|
|
1360
|
-
options.runGit
|
|
1361
|
-
);
|
|
1362
|
-
const branchError = branchRemoval.exitCode !== 0 && !/branch .+ not found/iu.test(branchRemoval.stderr) ? gitFailure(`branch -D ${options.worktree.branch}`, branchRemoval) : void 0;
|
|
1363
|
-
if (removalError && branchError) {
|
|
1364
|
-
throw new AggregateError(
|
|
1365
|
-
[removalError, branchError],
|
|
1366
|
-
"worktree: failed to remove worktree and branch"
|
|
1367
|
-
);
|
|
1368
|
-
}
|
|
1369
|
-
if (removalError) throw removalError;
|
|
1370
|
-
if (branchError) throw branchError;
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
// src/runtime/router-client.ts
|
|
1374
|
-
import { estimateCost, isModelPriced } from "@tangle-network/agent-eval";
|
|
1375
|
-
|
|
1376
|
-
// src/runtime/tool-loop.ts
|
|
1377
|
-
function estimateConversationTokens(messages) {
|
|
1378
|
-
let chars = 0;
|
|
1379
|
-
for (const m of messages) {
|
|
1380
|
-
const content = m.content;
|
|
1381
|
-
chars += typeof content === "string" ? content.length : safeJsonLength(content);
|
|
1382
|
-
const calls = m.tool_calls;
|
|
1383
|
-
if (calls) for (const c of calls) chars += c.function?.arguments?.length ?? 0;
|
|
1384
|
-
}
|
|
1385
|
-
return Math.ceil(chars / 4);
|
|
1386
|
-
}
|
|
1387
|
-
function safeJsonLength(value) {
|
|
1388
|
-
if (value === void 0 || value === null) return 0;
|
|
1389
|
-
try {
|
|
1390
|
-
return JSON.stringify(value)?.length ?? 0;
|
|
1391
|
-
} catch {
|
|
1392
|
-
return String(value).length;
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
async function maybeCompact(messages, c, turn) {
|
|
1396
|
-
const head = c.preserveHead !== void 0 && c.preserveHead > 0 ? c.preserveHead : 2;
|
|
1397
|
-
if (messages.length <= head + 1) return false;
|
|
1398
|
-
const estimate = c.estimateTokens ?? estimateConversationTokens;
|
|
1399
|
-
const before = estimate(messages);
|
|
1400
|
-
if (before <= c.thresholdTokens) return false;
|
|
1401
|
-
const digest = await c.distill([...messages]);
|
|
1402
|
-
messages.splice(head, messages.length - head, {
|
|
1403
|
-
role: "user",
|
|
1404
|
-
content: `[earlier work compacted to save context \u2014 progress so far]
|
|
1405
|
-
${digest}`
|
|
1406
|
-
});
|
|
1407
|
-
c.onCompact?.({ turn, beforeTokens: before, afterTokens: estimate(messages) });
|
|
1408
|
-
return true;
|
|
1409
|
-
}
|
|
1410
|
-
async function runBrainLoop(opts) {
|
|
1411
|
-
const maxTurns = opts.maxTurns ?? 4;
|
|
1412
|
-
const messages = [...opts.initialMessages];
|
|
1413
|
-
let toolCalls = 0;
|
|
1414
|
-
let lastText = "";
|
|
1415
|
-
const usage = { input: 0, output: 0 };
|
|
1416
|
-
const toolTrace = [];
|
|
1417
|
-
for (let turn = 1; turn <= maxTurns; turn += 1) {
|
|
1418
|
-
if (opts.hooks?.stopBefore?.(turn)) break;
|
|
1419
|
-
await opts.hooks?.beforeTurn?.(turn, messages);
|
|
1420
|
-
if (opts.compaction) await maybeCompact(messages, opts.compaction, turn);
|
|
1421
|
-
const r = await opts.chat(messages, opts.tools);
|
|
1422
|
-
if (r.usage) {
|
|
1423
|
-
usage.input += r.usage.input;
|
|
1424
|
-
usage.output += r.usage.output;
|
|
1425
|
-
opts.hooks?.onUsage?.(r.usage);
|
|
1426
|
-
}
|
|
1427
|
-
if (r.content) lastText = r.content;
|
|
1428
|
-
if (r.toolCalls.length === 0) {
|
|
1429
|
-
if (r.content) messages.push({ role: "assistant", content: r.content });
|
|
1430
|
-
return { final: lastText, turns: turn, toolCalls, toolTrace, usage, messages };
|
|
1431
|
-
}
|
|
1432
|
-
messages.push({
|
|
1433
|
-
role: "assistant",
|
|
1434
|
-
content: r.content ?? "",
|
|
1435
|
-
tool_calls: r.toolCalls.map((tc) => ({
|
|
1436
|
-
id: tc.id,
|
|
1437
|
-
type: "function",
|
|
1438
|
-
function: { name: tc.name, arguments: tc.arguments }
|
|
1439
|
-
}))
|
|
1440
|
-
});
|
|
1441
|
-
for (const tc of r.toolCalls) {
|
|
1442
|
-
toolCalls += 1;
|
|
1443
|
-
let args = {};
|
|
1444
|
-
try {
|
|
1445
|
-
args = JSON.parse(tc.arguments);
|
|
1446
|
-
} catch {
|
|
1447
|
-
messages.push({
|
|
1448
|
-
role: "tool",
|
|
1449
|
-
tool_call_id: tc.id,
|
|
1450
|
-
content: `error: arguments were not valid JSON: ${tc.arguments.slice(0, 200)}`
|
|
1451
|
-
});
|
|
1452
|
-
continue;
|
|
1453
|
-
}
|
|
1454
|
-
const out = await opts.execute(tc.name, args);
|
|
1455
|
-
messages.push({ role: "tool", tool_call_id: tc.id, content: out });
|
|
1456
|
-
toolTrace.push({ name: tc.name, args: tc.arguments, result: out });
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
return { final: lastText, turns: maxTurns, toolCalls, toolTrace, usage, messages };
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
// src/runtime/router-client.ts
|
|
1463
|
-
async function routerChatWithUsage(cfg, messages, opts) {
|
|
1464
|
-
const url = `${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`;
|
|
1465
|
-
const headers = { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` };
|
|
1466
|
-
let temperature = opts?.temperature ?? 0.2;
|
|
1467
|
-
const body = () => ({
|
|
1468
|
-
model: cfg.model,
|
|
1469
|
-
messages,
|
|
1470
|
-
temperature,
|
|
1471
|
-
max_tokens: opts?.maxTokens ?? 8192,
|
|
1472
|
-
...opts?.reasoningEffort ? { reasoning_effort: opts.reasoningEffort } : {}
|
|
1473
|
-
});
|
|
1474
|
-
if (cfg.complete) return parseChatResult(await cfg.complete(body()), cfg.model);
|
|
1475
|
-
let lastErr = "";
|
|
1476
|
-
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
1477
|
-
const res = await fetch(url, {
|
|
1478
|
-
method: "POST",
|
|
1479
|
-
headers,
|
|
1480
|
-
body: JSON.stringify(body()),
|
|
1481
|
-
...opts?.signal ? { signal: opts.signal } : {}
|
|
1482
|
-
});
|
|
1483
|
-
if (res.ok) return parseChatResult(await res.json(), cfg.model);
|
|
1484
|
-
const status = res.status;
|
|
1485
|
-
const text = (await res.text()).slice(0, 200);
|
|
1486
|
-
lastErr = `router ${status}: ${text}`;
|
|
1487
|
-
if (status === 400 && /temperature/i.test(text) && temperature !== 1) {
|
|
1488
|
-
temperature = 1;
|
|
1489
|
-
continue;
|
|
1490
|
-
}
|
|
1491
|
-
if (![408, 425, 429, 500, 502, 503, 504, 520, 522, 524].includes(status))
|
|
1492
|
-
throw new Error(lastErr);
|
|
1493
|
-
if (attempt < 4) await new Promise((r) => setTimeout(r, 800 * 2 ** attempt));
|
|
1494
|
-
}
|
|
1495
|
-
throw new Error(`${lastErr} (exhausted retries)`);
|
|
1496
|
-
}
|
|
1497
|
-
function parseChatResult(json, model) {
|
|
1498
|
-
const data = json;
|
|
1499
|
-
const u = data.usage;
|
|
1500
|
-
const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
|
|
1501
|
-
const costUsd = usage && isModelPriced(model) ? estimateCost(usage.input, usage.output, model) : void 0;
|
|
1502
|
-
const msg = data.choices?.[0]?.message;
|
|
1503
|
-
const { content, reasoning } = splitReasoning(
|
|
1504
|
-
msg?.content ?? "",
|
|
1505
|
-
msg?.reasoning ?? msg?.reasoning_content
|
|
1506
|
-
);
|
|
1507
|
-
return {
|
|
1508
|
-
content,
|
|
1509
|
-
...reasoning ? { reasoning } : {},
|
|
1510
|
-
...usage ? { usage } : {},
|
|
1511
|
-
...costUsd !== void 0 ? { costUsd } : {}
|
|
1512
|
-
};
|
|
1513
|
-
}
|
|
1514
|
-
function splitReasoning(rawContent, fieldReasoning) {
|
|
1515
|
-
const open3 = rawContent.indexOf("<think>");
|
|
1516
|
-
if (open3 !== -1) {
|
|
1517
|
-
const close = rawContent.indexOf("</think>", open3);
|
|
1518
|
-
const inline = close !== -1 ? rawContent.slice(open3 + 7, close) : rawContent.slice(open3 + 7);
|
|
1519
|
-
const rest = close !== -1 ? rawContent.slice(0, open3) + rawContent.slice(close + 8) : rawContent.slice(0, open3);
|
|
1520
|
-
const reasoning = [fieldReasoning, inline.trim()].filter(Boolean).join("\n");
|
|
1521
|
-
return { content: rest.trim(), ...reasoning ? { reasoning } : {} };
|
|
1522
|
-
}
|
|
1523
|
-
return { content: rawContent, ...fieldReasoning ? { reasoning: fieldReasoning } : {} };
|
|
1524
|
-
}
|
|
1525
|
-
async function routerChatWithTools(cfg, messages, tools, opts) {
|
|
1526
|
-
const body = {
|
|
1527
|
-
model: cfg.model,
|
|
1528
|
-
messages,
|
|
1529
|
-
tools,
|
|
1530
|
-
tool_choice: opts?.toolChoice ?? "auto",
|
|
1531
|
-
temperature: opts?.temperature ?? 0.3,
|
|
1532
|
-
...opts?.maxTokens ? { max_tokens: opts.maxTokens } : {}
|
|
1533
|
-
};
|
|
1534
|
-
const raw = cfg.complete ? await cfg.complete(body) : await (async () => {
|
|
1535
|
-
const res = await fetch(`${cfg.routerBaseUrl.replace(/\/$/, "")}/chat/completions`, {
|
|
1536
|
-
method: "POST",
|
|
1537
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${cfg.routerKey}` },
|
|
1538
|
-
body: JSON.stringify(body),
|
|
1539
|
-
...opts?.signal ? { signal: opts.signal } : {}
|
|
1540
|
-
});
|
|
1541
|
-
if (!res.ok) throw new Error(`router ${res.status}: ${(await res.text()).slice(0, 200)}`);
|
|
1542
|
-
return res.json();
|
|
1543
|
-
})();
|
|
1544
|
-
const data = raw;
|
|
1545
|
-
const msg = data.choices?.[0]?.message;
|
|
1546
|
-
const toolCalls = (msg?.tool_calls ?? []).map((tc, i) => ({
|
|
1547
|
-
id: tc.id ?? `call_${i}`,
|
|
1548
|
-
name: tc.function?.name ?? "",
|
|
1549
|
-
arguments: tc.function?.arguments ?? "{}"
|
|
1550
|
-
}));
|
|
1551
|
-
const u = data.usage;
|
|
1552
|
-
const usage = u && typeof u.prompt_tokens === "number" && typeof u.completion_tokens === "number" ? { input: u.prompt_tokens, output: u.completion_tokens } : void 0;
|
|
1553
|
-
const costUsd = usage && isModelPriced(cfg.model) ? estimateCost(usage.input, usage.output, cfg.model) : void 0;
|
|
1554
|
-
return {
|
|
1555
|
-
content: msg?.content ?? null,
|
|
1556
|
-
toolCalls,
|
|
1557
|
-
...usage ? { usage } : {},
|
|
1558
|
-
...costUsd !== void 0 ? { costUsd } : {}
|
|
1559
|
-
};
|
|
1560
|
-
}
|
|
1561
|
-
async function routerToolLoop(cfg, system, user, tools, execute, opts) {
|
|
1562
|
-
const initialMessages = opts?.initialMessages ?? [
|
|
1563
|
-
{ role: "system", content: system },
|
|
1564
|
-
{ role: "user", content: user }
|
|
1565
|
-
];
|
|
1566
|
-
return runBrainLoop({
|
|
1567
|
-
chat: (messages, toolSpecs) => routerChatWithTools(cfg, messages, toolSpecs, {
|
|
1568
|
-
...opts?.temperature !== void 0 ? { temperature: opts.temperature } : {},
|
|
1569
|
-
...opts?.maxTokens ? { maxTokens: opts.maxTokens } : {},
|
|
1570
|
-
...opts?.signal ? { signal: opts.signal } : {}
|
|
1571
|
-
}),
|
|
1572
|
-
tools,
|
|
1573
|
-
execute,
|
|
1574
|
-
initialMessages,
|
|
1575
|
-
maxTurns: opts?.maxTurns ?? 4
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
function routerBrain(cfg, opts = {}) {
|
|
1579
|
-
const temperature = opts.temperature ?? 0.4;
|
|
1580
|
-
return (messages, tools) => routerChatWithTools(cfg, messages, tools, { temperature, toolChoice: "auto" });
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
// src/runtime-hooks.ts
|
|
1584
|
-
function defineRuntimeHooks(hooks) {
|
|
1585
|
-
return hooks;
|
|
1586
|
-
}
|
|
1587
|
-
function composeRuntimeHooks(...entries) {
|
|
1588
|
-
const hooks = entries.filter((entry) => !!entry);
|
|
1589
|
-
return {
|
|
1590
|
-
onEvent: hooks.some((hook) => hook.onEvent) ? (event, context) => {
|
|
1591
|
-
const pending = [];
|
|
1592
|
-
for (const hook of hooks) {
|
|
1593
|
-
const result = hook.onEvent?.(event, context);
|
|
1594
|
-
if (isThenable(result)) pending.push(Promise.resolve(result));
|
|
1595
|
-
}
|
|
1596
|
-
if (pending.length > 0) return Promise.all(pending).then(() => void 0);
|
|
1597
|
-
return void 0;
|
|
1598
|
-
} : void 0,
|
|
1599
|
-
onDecisionPoint: hooks.some((hook) => hook.onDecisionPoint) ? (point, context) => {
|
|
1600
|
-
const pending = [];
|
|
1601
|
-
for (const hook of hooks) {
|
|
1602
|
-
const result = hook.onDecisionPoint?.(point, context);
|
|
1603
|
-
if (isThenable(result)) pending.push(Promise.resolve(result));
|
|
1604
|
-
}
|
|
1605
|
-
if (pending.length > 0) return Promise.all(pending).then(() => void 0);
|
|
1606
|
-
return void 0;
|
|
1607
|
-
} : void 0,
|
|
1608
|
-
onHookError: hooks.some((hook) => hook.onHookError) ? (error, context) => {
|
|
1609
|
-
const pending = [];
|
|
1610
|
-
for (const hook of hooks) {
|
|
1611
|
-
const result = hook.onHookError?.(error, context);
|
|
1612
|
-
if (isThenable(result)) pending.push(Promise.resolve(result));
|
|
1613
|
-
}
|
|
1614
|
-
if (pending.length > 0) return Promise.all(pending).then(() => void 0);
|
|
1615
|
-
return void 0;
|
|
1616
|
-
} : void 0
|
|
1617
|
-
};
|
|
1618
|
-
}
|
|
1619
|
-
function notifyRuntimeHookEvent(hooks, event, context = {}) {
|
|
1620
|
-
const onEvent = hooks?.onEvent;
|
|
1621
|
-
if (!onEvent) return;
|
|
1622
|
-
try {
|
|
1623
|
-
const result = onEvent(event, context);
|
|
1624
|
-
if (isThenable(result)) {
|
|
1625
|
-
void result.catch((error) => {
|
|
1626
|
-
notifyRuntimeHookError(hooks, toError(error), {
|
|
1627
|
-
hook: "onEvent",
|
|
1628
|
-
eventId: event.id,
|
|
1629
|
-
target: event.target,
|
|
1630
|
-
phase: event.phase
|
|
1631
|
-
});
|
|
1632
|
-
});
|
|
1633
|
-
}
|
|
1634
|
-
} catch (error) {
|
|
1635
|
-
notifyRuntimeHookError(hooks, toError(error), {
|
|
1636
|
-
hook: "onEvent",
|
|
1637
|
-
eventId: event.id,
|
|
1638
|
-
target: event.target,
|
|
1639
|
-
phase: event.phase
|
|
1640
|
-
});
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
function notifyRuntimeDecisionPoint(hooks, point, context = {}) {
|
|
1644
|
-
const onDecisionPoint = hooks?.onDecisionPoint;
|
|
1645
|
-
if (!onDecisionPoint) return;
|
|
1646
|
-
try {
|
|
1647
|
-
const result = onDecisionPoint(point, context);
|
|
1648
|
-
if (isThenable(result)) {
|
|
1649
|
-
void result.catch((error) => {
|
|
1650
|
-
notifyRuntimeHookError(hooks, toError(error), {
|
|
1651
|
-
hook: "onDecisionPoint",
|
|
1652
|
-
decisionId: point.id,
|
|
1653
|
-
decisionKind: point.kind
|
|
1654
|
-
});
|
|
1655
|
-
});
|
|
1656
|
-
}
|
|
1657
|
-
} catch (error) {
|
|
1658
|
-
notifyRuntimeHookError(hooks, toError(error), {
|
|
1659
|
-
hook: "onDecisionPoint",
|
|
1660
|
-
decisionId: point.id,
|
|
1661
|
-
decisionKind: point.kind
|
|
1662
|
-
});
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
function notifyRuntimeHookError(hooks, error, context) {
|
|
1666
|
-
try {
|
|
1667
|
-
const result = hooks?.onHookError?.(error, context);
|
|
1668
|
-
if (isThenable(result)) void result.catch(() => void 0);
|
|
1669
|
-
} catch {
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
function isThenable(value) {
|
|
1673
|
-
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
1674
|
-
}
|
|
1675
|
-
function toError(error) {
|
|
1676
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
// src/runtime/supervise/progress.ts
|
|
1680
|
-
var DEFAULT_STALL_AFTER_MS = 18e4;
|
|
1681
|
-
function createActivityLog(limit = 12) {
|
|
1682
|
-
const notes = [];
|
|
1683
|
-
const cap = Math.max(1, Math.floor(limit));
|
|
1684
|
-
return {
|
|
1685
|
-
push(note) {
|
|
1686
|
-
notes.push(note);
|
|
1687
|
-
if (notes.length > cap) notes.splice(0, notes.length - cap);
|
|
1688
|
-
},
|
|
1689
|
-
read: () => [...notes],
|
|
1690
|
-
last: () => notes[notes.length - 1],
|
|
1691
|
-
size: () => notes.length
|
|
1692
|
-
};
|
|
1693
|
-
}
|
|
1694
|
-
function readWorkerProgress(scope, executor, now, stallAfterMs = DEFAULT_STALL_AFTER_MS) {
|
|
1695
|
-
const live = scope.status !== "done" && scope.status !== "failed" && scope.status !== "cancelled";
|
|
1696
|
-
const executorActivityAt = executor?.recentActivity?.length ? executor.recentActivity[executor.recentActivity.length - 1].at : 0;
|
|
1697
|
-
const lastActivityAt = Math.max(scope.lastActivityAt, executorActivityAt);
|
|
1698
|
-
const idleMs = Math.max(0, now - lastActivityAt);
|
|
1699
|
-
const note = executor?.note;
|
|
1700
|
-
return {
|
|
1701
|
-
id: scope.id,
|
|
1702
|
-
status: scope.status,
|
|
1703
|
-
live,
|
|
1704
|
-
steerable: scope.steerable,
|
|
1705
|
-
startedAt: scope.startedAt,
|
|
1706
|
-
lastActivityAt,
|
|
1707
|
-
idleMs,
|
|
1708
|
-
// Only a LIVE worker can stall; a settled one is simply finished.
|
|
1709
|
-
stalled: live && idleMs > stallAfterMs,
|
|
1710
|
-
stallAfterMs,
|
|
1711
|
-
turns: executor?.turns ?? scope.turns,
|
|
1712
|
-
tokens: scope.tokens,
|
|
1713
|
-
usd: scope.usd,
|
|
1714
|
-
pendingMessages: executor?.pendingMessages ?? 0,
|
|
1715
|
-
recentActivity: executor?.recentActivity ?? [],
|
|
1716
|
-
...note ? { note } : {}
|
|
1717
|
-
};
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1720
|
-
// src/runtime/supervise/wait.ts
|
|
1721
|
-
function timerAt(ms, now) {
|
|
1722
|
-
return { kind: "timer", untilMs: now + ms };
|
|
1723
|
-
}
|
|
1724
|
-
function pollFor(probe, opts, now) {
|
|
1725
|
-
return {
|
|
1726
|
-
kind: "poll",
|
|
1727
|
-
probe,
|
|
1728
|
-
intervalMs: opts.intervalMs,
|
|
1729
|
-
...opts.timeoutMs !== void 0 ? { timeoutAtMs: now + opts.timeoutMs } : {},
|
|
1730
|
-
...opts.args ? { args: opts.args } : {}
|
|
1731
|
-
};
|
|
1732
|
-
}
|
|
1733
|
-
function createWaitProbes(entries) {
|
|
1734
|
-
const map = new Map(Object.entries(entries));
|
|
1735
|
-
return { resolve: (name) => map.get(name) };
|
|
1736
|
-
}
|
|
1737
|
-
function isWaitOutcome(value) {
|
|
1738
|
-
return typeof value === "object" && value !== null && value.waitOutcome === true;
|
|
1739
|
-
}
|
|
1740
|
-
function waitUntil(spec) {
|
|
1741
|
-
return spec.kind === "timer" ? spec.untilMs : spec.timeoutAtMs;
|
|
1742
|
-
}
|
|
1743
|
-
function validateWaitSpec(spec) {
|
|
1744
|
-
if (spec.kind === "timer") {
|
|
1745
|
-
return Number.isFinite(spec.untilMs) ? null : "timer.untilMs must be a finite epoch-ms instant";
|
|
1746
|
-
}
|
|
1747
|
-
if (typeof spec.probe !== "string" || spec.probe.length === 0) {
|
|
1748
|
-
return "poll.probe must be a non-empty probe name";
|
|
1749
|
-
}
|
|
1750
|
-
if (!Number.isFinite(spec.intervalMs) || spec.intervalMs <= 0) {
|
|
1751
|
-
return "poll.intervalMs must be > 0";
|
|
1752
|
-
}
|
|
1753
|
-
if (spec.timeoutAtMs !== void 0 && !Number.isFinite(spec.timeoutAtMs)) {
|
|
1754
|
-
return "poll.timeoutAtMs must be a finite epoch-ms instant";
|
|
1755
|
-
}
|
|
1756
|
-
return null;
|
|
1757
|
-
}
|
|
1758
|
-
function assertWaitWithinDeadline(spec, deadlineMs) {
|
|
1759
|
-
if (deadlineMs <= 0) return true;
|
|
1760
|
-
const until = waitUntil(spec);
|
|
1761
|
-
if (until === void 0) return false;
|
|
1762
|
-
return until <= deadlineMs;
|
|
1763
|
-
}
|
|
1764
|
-
async function runWait(args) {
|
|
1765
|
-
const { spec, label, armedAt, resumed, signal, now } = args;
|
|
1766
|
-
const sleep = args.sleep ?? defaultSleep;
|
|
1767
|
-
if (signal.aborted) return { kind: "cancelled", reason: "aborted before arming" };
|
|
1768
|
-
if (spec.kind === "timer") {
|
|
1769
|
-
const remaining = spec.untilMs - now();
|
|
1770
|
-
if (remaining > 0) await sleep(remaining, signal);
|
|
1771
|
-
if (signal.aborted) return { kind: "cancelled", reason: "wait aborted" };
|
|
1772
|
-
return {
|
|
1773
|
-
kind: "woke",
|
|
1774
|
-
outcome: outcome("timer", "fired", label, spec.untilMs, armedAt, now(), 0, 0, resumed)
|
|
1775
|
-
};
|
|
1776
|
-
}
|
|
1777
|
-
const probe = args.probes?.resolve(spec.probe);
|
|
1778
|
-
if (!probe) {
|
|
1779
|
-
throw new ValidationError(
|
|
1780
|
-
`wait '${label}': poll probe '${spec.probe}' is not registered in this run's WaitProbeRegistry`
|
|
1781
|
-
);
|
|
1782
|
-
}
|
|
1783
|
-
let polls = 0;
|
|
1784
|
-
let probeErrors = 0;
|
|
1785
|
-
for (; ; ) {
|
|
1786
|
-
if (signal.aborted) return { kind: "cancelled", reason: "wait aborted" };
|
|
1787
|
-
polls += 1;
|
|
1788
|
-
let fired = false;
|
|
1789
|
-
try {
|
|
1790
|
-
fired = await probe(spec.args, signal);
|
|
1791
|
-
} catch {
|
|
1792
|
-
probeErrors += 1;
|
|
1793
|
-
}
|
|
1794
|
-
if (fired) {
|
|
1795
|
-
return {
|
|
1796
|
-
kind: "woke",
|
|
1797
|
-
outcome: outcome(
|
|
1798
|
-
"poll",
|
|
1799
|
-
"fired",
|
|
1800
|
-
label,
|
|
1801
|
-
spec.timeoutAtMs,
|
|
1802
|
-
armedAt,
|
|
1803
|
-
now(),
|
|
1804
|
-
polls,
|
|
1805
|
-
probeErrors,
|
|
1806
|
-
resumed
|
|
1807
|
-
)
|
|
1808
|
-
};
|
|
1809
|
-
}
|
|
1810
|
-
if (spec.timeoutAtMs !== void 0 && now() >= spec.timeoutAtMs) {
|
|
1811
|
-
return {
|
|
1812
|
-
kind: "woke",
|
|
1813
|
-
outcome: outcome(
|
|
1814
|
-
"poll",
|
|
1815
|
-
"timeout",
|
|
1816
|
-
label,
|
|
1817
|
-
spec.timeoutAtMs,
|
|
1818
|
-
armedAt,
|
|
1819
|
-
now(),
|
|
1820
|
-
polls,
|
|
1821
|
-
probeErrors,
|
|
1822
|
-
resumed
|
|
1823
|
-
)
|
|
1824
|
-
};
|
|
1825
|
-
}
|
|
1826
|
-
const nextCheck = spec.timeoutAtMs === void 0 ? spec.intervalMs : Math.min(spec.intervalMs, Math.max(0, spec.timeoutAtMs - now()));
|
|
1827
|
-
await sleep(nextCheck, signal);
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
function outcome(kind, settled, label, untilMs, armedAt, wokenAt, polls, probeErrors, resumed) {
|
|
1831
|
-
return {
|
|
1832
|
-
waitOutcome: true,
|
|
1833
|
-
kind,
|
|
1834
|
-
settled,
|
|
1835
|
-
label,
|
|
1836
|
-
...untilMs !== void 0 ? { untilMs } : {},
|
|
1837
|
-
armedAt,
|
|
1838
|
-
wokenAt,
|
|
1839
|
-
polls,
|
|
1840
|
-
probeErrors,
|
|
1841
|
-
resumed
|
|
1842
|
-
};
|
|
1843
|
-
}
|
|
1844
|
-
function defaultSleep(ms, signal) {
|
|
1845
|
-
if (ms <= 0 || signal.aborted) return Promise.resolve();
|
|
1846
|
-
return new Promise((resolve2) => {
|
|
1847
|
-
const timer = setTimeout(() => {
|
|
1848
|
-
signal.removeEventListener("abort", onAbort);
|
|
1849
|
-
resolve2();
|
|
1850
|
-
}, ms);
|
|
1851
|
-
function onAbort() {
|
|
1852
|
-
clearTimeout(timer);
|
|
1853
|
-
resolve2();
|
|
1854
|
-
}
|
|
1855
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
1856
|
-
});
|
|
1857
|
-
}
|
|
1858
|
-
|
|
1859
|
-
// src/runtime/supervise/scope.ts
|
|
1860
|
-
var nestedScopeSeamKey = "nested-scope";
|
|
1861
|
-
function makeNestedScopeSeam(args, childNodeId) {
|
|
1862
|
-
return {
|
|
1863
|
-
depth: args.depth,
|
|
1864
|
-
...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
|
|
1865
|
-
journalRoot: args.root,
|
|
1866
|
-
mount(nestedRoot, signal) {
|
|
1867
|
-
return createScope({
|
|
1868
|
-
parentId: childNodeId,
|
|
1869
|
-
root: nestedRoot,
|
|
1870
|
-
pool: args.pool,
|
|
1871
|
-
journal: args.journal,
|
|
1872
|
-
blobs: args.blobs,
|
|
1873
|
-
executors: args.executors,
|
|
1874
|
-
// Re-seed the parent's NON-recursion seams (sandbox/router for leaf grandchildren);
|
|
1875
|
-
// the nested scope adds its OWN nested-scope seam per child in `spawn`.
|
|
1876
|
-
seams: args.seams,
|
|
1877
|
-
depth: args.depth + 1,
|
|
1878
|
-
...args.maxDepth !== void 0 ? { maxDepth: args.maxDepth } : {},
|
|
1879
|
-
signal,
|
|
1880
|
-
...args.now ? { now: args.now } : {},
|
|
1881
|
-
...args.hooks ? { hooks: args.hooks } : {}
|
|
1882
|
-
});
|
|
1883
|
-
}
|
|
1884
|
-
};
|
|
1885
|
-
}
|
|
1886
|
-
function createScope(args) {
|
|
1887
|
-
const children = /* @__PURE__ */ new Map();
|
|
1888
|
-
let spawnOrdinal = args.resumeFrom ? args.resumeFrom.maxSpawnOrdinal + 1 : 0;
|
|
1889
|
-
let cursorSeq = args.resumeFrom ? args.resumeFrom.maxCursorSeq + 1 : 0;
|
|
1890
|
-
let waitOrdinal = args.resumeFrom ? args.resumeFrom.maxWaitOrdinal + 1 : 0;
|
|
1891
|
-
let meterSeq = 0;
|
|
1892
|
-
const now = args.now ?? Date.now;
|
|
1893
|
-
const unclaimedWaits = new Map(
|
|
1894
|
-
(args.resumeFrom?.waits ?? []).map((w) => [w.label, w])
|
|
1895
|
-
);
|
|
1896
|
-
function spawn4(agent, task, opts) {
|
|
1897
|
-
if (args.maxDepth !== void 0 && args.depth >= args.maxDepth) {
|
|
1898
|
-
return { ok: false, reason: "depth-exceeded" };
|
|
1899
|
-
}
|
|
1900
|
-
const spec = agent.executorSpec;
|
|
1901
|
-
if (!isAgentSpec(spec)) {
|
|
1902
|
-
throw new ValidationError(
|
|
1903
|
-
`scope.spawn: agent "${agent.name}" exposes no \`executorSpec\` (AgentSpec) to resolve a Executor`
|
|
1904
|
-
);
|
|
1905
|
-
}
|
|
1906
|
-
const resolved = args.executors.resolve(spec);
|
|
1907
|
-
if (!resolved.succeeded) throw new ValidationError(`scope.spawn: ${resolved.error}`);
|
|
1908
|
-
const reservation = args.pool.reserve(opts.budget);
|
|
1909
|
-
if (!reservation.ok) return { ok: false, reason: reservation.reason };
|
|
1910
|
-
try {
|
|
1911
|
-
const ordinal = spawnOrdinal++;
|
|
1912
|
-
const id = `${args.parentId}:s${ordinal}`;
|
|
1913
|
-
const childAbort = new AbortController();
|
|
1914
|
-
const cascadeAbort = () => childAbort.abort();
|
|
1915
|
-
if (args.signal.aborted) childAbort.abort();
|
|
1916
|
-
else args.signal.addEventListener("abort", cascadeAbort, { once: true });
|
|
1917
|
-
const ctx = {
|
|
1918
|
-
signal: childAbort.signal,
|
|
1919
|
-
seams: { ...args.seams, [nestedScopeSeamKey]: makeNestedScopeSeam(args, id) }
|
|
1920
|
-
};
|
|
1921
|
-
const executor = resolved.value(spec, ctx);
|
|
1922
|
-
const handle = {
|
|
1923
|
-
id,
|
|
1924
|
-
label: opts.label,
|
|
1925
|
-
get status() {
|
|
1926
|
-
return children.get(id)?.status ?? "cancelled";
|
|
1927
|
-
},
|
|
1928
|
-
abort(reason) {
|
|
1929
|
-
childAbort.abort(reason);
|
|
1930
|
-
}
|
|
1931
|
-
};
|
|
1932
|
-
const live = {
|
|
1933
|
-
id,
|
|
1934
|
-
status: "acquiring",
|
|
1935
|
-
runtime: executor.runtime,
|
|
1936
|
-
budget: opts.budget,
|
|
1937
|
-
label: opts.label,
|
|
1938
|
-
spent: zeroSpend(),
|
|
1939
|
-
settled: void 0,
|
|
1940
|
-
delivered: false,
|
|
1941
|
-
executorDone: false,
|
|
1942
|
-
startedAt: now(),
|
|
1943
|
-
lastActivityAt: now(),
|
|
1944
|
-
...executor.deliver ? { deliver: executor.deliver.bind(executor) } : {},
|
|
1945
|
-
...executor.progress ? { readProgress: executor.progress.bind(executor) } : {},
|
|
1946
|
-
...executor.traceSource ? { readTraceSource: executor.traceSource.bind(executor) } : {}
|
|
1947
|
-
};
|
|
1948
|
-
children.set(id, live);
|
|
1949
|
-
void args.journal.appendEvent(args.root, {
|
|
1950
|
-
kind: "spawned",
|
|
1951
|
-
id,
|
|
1952
|
-
parent: args.parentId,
|
|
1953
|
-
label: opts.label,
|
|
1954
|
-
budget: opts.budget,
|
|
1955
|
-
runtime: executor.runtime,
|
|
1956
|
-
seq: ordinal,
|
|
1957
|
-
at: new Date(now()).toISOString()
|
|
1958
|
-
});
|
|
1959
|
-
notifyRuntimeHookEvent(
|
|
1960
|
-
args.hooks,
|
|
1961
|
-
{
|
|
1962
|
-
id: `${id}:spawn`,
|
|
1963
|
-
runId: args.root,
|
|
1964
|
-
target: "agent.spawn",
|
|
1965
|
-
phase: "after",
|
|
1966
|
-
timestamp: now(),
|
|
1967
|
-
stepIndex: ordinal,
|
|
1968
|
-
parentId: args.parentId,
|
|
1969
|
-
payload: {
|
|
1970
|
-
childId: id,
|
|
1971
|
-
label: opts.label,
|
|
1972
|
-
runtime: executor.runtime,
|
|
1973
|
-
budget: opts.budget,
|
|
1974
|
-
depth: args.depth
|
|
1975
|
-
}
|
|
1976
|
-
},
|
|
1977
|
-
{ signal: args.signal }
|
|
1978
|
-
);
|
|
1979
|
-
const settled = runChild(
|
|
1980
|
-
live,
|
|
1981
|
-
executor,
|
|
1982
|
-
childAbort,
|
|
1983
|
-
task,
|
|
1984
|
-
opts,
|
|
1985
|
-
args.pool,
|
|
1986
|
-
reservation.ticket,
|
|
1987
|
-
args.blobs,
|
|
1988
|
-
now
|
|
1989
|
-
).then((s) => {
|
|
1990
|
-
live.resolved = s;
|
|
1991
|
-
return s;
|
|
1992
|
-
}).finally(() => {
|
|
1993
|
-
args.signal.removeEventListener("abort", cascadeAbort);
|
|
1994
|
-
});
|
|
1995
|
-
live.settled = settled;
|
|
1996
|
-
return { ok: true, handle };
|
|
1997
|
-
} catch (err) {
|
|
1998
|
-
args.pool.reconcile(reservation.ticket, zeroSpend());
|
|
1999
|
-
throw err;
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
async function next() {
|
|
2003
|
-
const undelivered = () => [...children.values()].filter((c) => !c.delivered);
|
|
2004
|
-
if (undelivered().length === 0) return null;
|
|
2005
|
-
for (; ; ) {
|
|
2006
|
-
const pending = undelivered();
|
|
2007
|
-
if (pending.length === 0) return null;
|
|
2008
|
-
const ready = pending.find((c) => c.resolved !== void 0);
|
|
2009
|
-
const chosen = ready ?? await raceFirstSettled(pending);
|
|
2010
|
-
if (chosen.delivered) continue;
|
|
2011
|
-
chosen.delivered = true;
|
|
2012
|
-
const seq = cursorSeq++;
|
|
2013
|
-
const settlement = chosen.resolved;
|
|
2014
|
-
if (!settlement) {
|
|
2015
|
-
throw new ValidationError(
|
|
2016
|
-
`scope.next: child '${chosen.id}' won the settle race without a resolved value`
|
|
2017
|
-
);
|
|
2018
|
-
}
|
|
2019
|
-
return finalizeSettlement(chosen, settlement, seq, args, now);
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
async function nextResolved() {
|
|
2023
|
-
for (; ; ) {
|
|
2024
|
-
const candidates = [...children.values()].filter((c) => !c.delivered);
|
|
2025
|
-
const pick = candidates.find((c) => c.resolved !== void 0) ?? candidates.find((c) => c.executorDone);
|
|
2026
|
-
if (!pick) return null;
|
|
2027
|
-
const settlement = await pick.settled;
|
|
2028
|
-
if (pick.delivered) continue;
|
|
2029
|
-
pick.delivered = true;
|
|
2030
|
-
const seq = cursorSeq++;
|
|
2031
|
-
return finalizeSettlement(pick, settlement, seq, args, now);
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
|
-
function send(nodeId, msg) {
|
|
2035
|
-
const child = children.get(nodeId);
|
|
2036
|
-
if (!child || child.delivered || !child.deliver) return false;
|
|
2037
|
-
child.deliver(msg);
|
|
2038
|
-
child.lastActivityAt = now();
|
|
2039
|
-
return true;
|
|
2040
|
-
}
|
|
2041
|
-
function wait(spec, opts) {
|
|
2042
|
-
if (validateWaitSpec(spec) !== null) return { ok: false, reason: "invalid-spec" };
|
|
2043
|
-
if (spec.kind === "poll" && args.probes?.resolve(spec.probe) === void 0) {
|
|
2044
|
-
return { ok: false, reason: "unknown-probe" };
|
|
2045
|
-
}
|
|
2046
|
-
const adopted = unclaimedWaits.get(opts.label);
|
|
2047
|
-
if (adopted) unclaimedWaits.delete(opts.label);
|
|
2048
|
-
const effectiveSpec = adopted?.spec ?? spec;
|
|
2049
|
-
const armedAt = adopted?.armedAt ?? now();
|
|
2050
|
-
if (!assertWaitWithinDeadline(effectiveSpec, args.pool.readout().deadlineMs)) {
|
|
2051
|
-
return { ok: false, reason: "deadline-exceeded" };
|
|
2052
|
-
}
|
|
2053
|
-
const id = adopted ? adopted.id : `${args.parentId}:w${waitOrdinal}`;
|
|
2054
|
-
const ordinal = adopted ? adopted.ordinal : waitOrdinal;
|
|
2055
|
-
if (!adopted) waitOrdinal += 1;
|
|
2056
|
-
const waitAbort = new AbortController();
|
|
2057
|
-
const cascadeAbort = () => waitAbort.abort();
|
|
2058
|
-
if (args.signal.aborted) waitAbort.abort();
|
|
2059
|
-
else args.signal.addEventListener("abort", cascadeAbort, { once: true });
|
|
2060
|
-
const handle = {
|
|
2061
|
-
id,
|
|
2062
|
-
label: opts.label,
|
|
2063
|
-
get status() {
|
|
2064
|
-
return children.get(id)?.status ?? "cancelled";
|
|
2065
|
-
},
|
|
2066
|
-
abort(reason) {
|
|
2067
|
-
waitAbort.abort(reason);
|
|
2068
|
-
}
|
|
2069
|
-
};
|
|
2070
|
-
const live = {
|
|
2071
|
-
id,
|
|
2072
|
-
status: "waiting",
|
|
2073
|
-
runtime: "wait",
|
|
2074
|
-
// A wait's recorded budget is zero on every channel — nothing was reserved, so nothing may
|
|
2075
|
-
// be reconciled, and a journal reader sums it as the zero it truly is.
|
|
2076
|
-
budget: { maxIterations: 0, maxTokens: 0 },
|
|
2077
|
-
label: opts.label,
|
|
2078
|
-
spent: zeroSpend(),
|
|
2079
|
-
settled: void 0,
|
|
2080
|
-
delivered: false,
|
|
2081
|
-
executorDone: false,
|
|
2082
|
-
startedAt: armedAt,
|
|
2083
|
-
lastActivityAt: now(),
|
|
2084
|
-
wait: { spec: effectiveSpec, armedAt, label: opts.label }
|
|
2085
|
-
};
|
|
2086
|
-
children.set(id, live);
|
|
2087
|
-
if (!adopted) {
|
|
2088
|
-
void args.journal.appendEvent(args.root, {
|
|
2089
|
-
kind: "waiting",
|
|
2090
|
-
id,
|
|
2091
|
-
parent: args.parentId,
|
|
2092
|
-
label: opts.label,
|
|
2093
|
-
spec: effectiveSpec,
|
|
2094
|
-
armedAt,
|
|
2095
|
-
seq: ordinal,
|
|
2096
|
-
at: new Date(now()).toISOString()
|
|
2097
|
-
});
|
|
2098
|
-
}
|
|
2099
|
-
notifyRuntimeHookEvent(
|
|
2100
|
-
args.hooks,
|
|
2101
|
-
{
|
|
2102
|
-
id: `${id}:waiting`,
|
|
2103
|
-
runId: args.root,
|
|
2104
|
-
target: "agent.spawn",
|
|
2105
|
-
phase: "after",
|
|
2106
|
-
timestamp: now(),
|
|
2107
|
-
stepIndex: ordinal,
|
|
2108
|
-
parentId: args.parentId,
|
|
2109
|
-
payload: {
|
|
2110
|
-
childId: id,
|
|
2111
|
-
label: opts.label,
|
|
2112
|
-
runtime: "wait",
|
|
2113
|
-
wait: effectiveSpec,
|
|
2114
|
-
armedAt,
|
|
2115
|
-
resumed: adopted !== void 0
|
|
2116
|
-
}
|
|
2117
|
-
},
|
|
2118
|
-
{ signal: args.signal }
|
|
2119
|
-
);
|
|
2120
|
-
const settled = runWait({
|
|
2121
|
-
spec: effectiveSpec,
|
|
2122
|
-
label: opts.label,
|
|
2123
|
-
armedAt,
|
|
2124
|
-
resumed: adopted !== void 0,
|
|
2125
|
-
signal: waitAbort.signal,
|
|
2126
|
-
...args.probes ? { probes: args.probes } : {},
|
|
2127
|
-
now,
|
|
2128
|
-
...args.waitSleep ? { sleep: args.waitSleep } : {}
|
|
2129
|
-
}).then(async (resolution) => {
|
|
2130
|
-
live.executorDone = true;
|
|
2131
|
-
live.lastActivityAt = now();
|
|
2132
|
-
if (resolution.kind === "cancelled") {
|
|
2133
|
-
return { kind: "down", reason: resolution.reason, infra: false, restartCount: 0 };
|
|
2134
|
-
}
|
|
2135
|
-
const outRef = contentAddress(resolution.outcome);
|
|
2136
|
-
await args.blobs.put(outRef, resolution.outcome);
|
|
2137
|
-
return { kind: "done", out: resolution.outcome, outRef, spent: zeroSpend() };
|
|
2138
|
-
}).catch((err) => {
|
|
2139
|
-
live.executorDone = true;
|
|
2140
|
-
return { kind: "down", reason: errMessage(err), infra: true, restartCount: 0 };
|
|
2141
|
-
}).then((s) => {
|
|
2142
|
-
live.resolved = s;
|
|
2143
|
-
return s;
|
|
2144
|
-
}).finally(() => {
|
|
2145
|
-
args.signal.removeEventListener("abort", cascadeAbort);
|
|
2146
|
-
});
|
|
2147
|
-
live.settled = settled;
|
|
2148
|
-
return { ok: true, handle };
|
|
2149
|
-
}
|
|
2150
|
-
function progress(nodeId, opts = {}) {
|
|
2151
|
-
const child = children.get(nodeId);
|
|
2152
|
-
if (!child) return void 0;
|
|
2153
|
-
let fromExecutor;
|
|
2154
|
-
try {
|
|
2155
|
-
fromExecutor = child.readProgress?.();
|
|
2156
|
-
} catch {
|
|
2157
|
-
fromExecutor = void 0;
|
|
2158
|
-
}
|
|
2159
|
-
return readWorkerProgress(
|
|
2160
|
-
{
|
|
2161
|
-
id: child.id,
|
|
2162
|
-
status: child.status,
|
|
2163
|
-
steerable: child.deliver !== void 0 && !child.delivered,
|
|
2164
|
-
startedAt: child.startedAt,
|
|
2165
|
-
lastActivityAt: child.lastActivityAt,
|
|
2166
|
-
turns: child.spent.iterations,
|
|
2167
|
-
tokens: child.spent.tokens,
|
|
2168
|
-
usd: child.spent.usd
|
|
2169
|
-
},
|
|
2170
|
-
fromExecutor,
|
|
2171
|
-
opts.now ?? now(),
|
|
2172
|
-
opts.stallAfterMs ?? DEFAULT_STALL_AFTER_MS
|
|
2173
|
-
);
|
|
2174
|
-
}
|
|
2175
|
-
function traceSource(nodeId) {
|
|
2176
|
-
const child = children.get(nodeId);
|
|
2177
|
-
if (!child) return void 0;
|
|
2178
|
-
try {
|
|
2179
|
-
return child.readTraceSource?.();
|
|
2180
|
-
} catch {
|
|
2181
|
-
return void 0;
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2184
|
-
async function meter(spend, detail) {
|
|
2185
|
-
const seq = meterSeq++;
|
|
2186
|
-
args.pool.observe(spend);
|
|
2187
|
-
await args.journal.appendEvent(args.root, {
|
|
2188
|
-
kind: "metered",
|
|
2189
|
-
id: args.parentId,
|
|
2190
|
-
spend,
|
|
2191
|
-
seq,
|
|
2192
|
-
at: new Date(now()).toISOString()
|
|
2193
|
-
});
|
|
2194
|
-
notifyRuntimeHookEvent(
|
|
2195
|
-
args.hooks,
|
|
2196
|
-
{
|
|
2197
|
-
id: `${args.parentId}:meter:${seq}`,
|
|
2198
|
-
runId: args.root,
|
|
2199
|
-
target: "agent.turn",
|
|
2200
|
-
phase: "after",
|
|
2201
|
-
timestamp: now(),
|
|
2202
|
-
parentId: args.parentId,
|
|
2203
|
-
payload: { spend, ...detail ?? {} }
|
|
2204
|
-
},
|
|
2205
|
-
{ signal: args.signal }
|
|
2206
|
-
);
|
|
2207
|
-
}
|
|
2208
|
-
const resume = args.resumeFrom ? {
|
|
2209
|
-
settled: args.resumeFrom.settled,
|
|
2210
|
-
view: args.resumeFrom.view,
|
|
2211
|
-
waits: args.resumeFrom.waits
|
|
2212
|
-
} : void 0;
|
|
2213
|
-
return {
|
|
2214
|
-
spawn: spawn4,
|
|
2215
|
-
next,
|
|
2216
|
-
nextResolved,
|
|
2217
|
-
send,
|
|
2218
|
-
wait,
|
|
2219
|
-
progress,
|
|
2220
|
-
traceSource,
|
|
2221
|
-
signal: args.signal,
|
|
2222
|
-
meter,
|
|
2223
|
-
...resume ? { resume } : {},
|
|
2224
|
-
get view() {
|
|
2225
|
-
return makeTreeView(args.parentId, children);
|
|
2226
|
-
},
|
|
2227
|
-
get budget() {
|
|
2228
|
-
return args.pool.readout();
|
|
2229
|
-
}
|
|
2230
|
-
};
|
|
2231
|
-
}
|
|
2232
|
-
async function raceFirstSettled(pending) {
|
|
2233
|
-
return Promise.race(pending.map((c) => c.settled.then(() => c)));
|
|
2234
|
-
}
|
|
2235
|
-
async function finalizeSettlement(child, settlement, seq, args, now) {
|
|
2236
|
-
const handle = frozenHandle(child);
|
|
2237
|
-
if (child.wait) return finalizeWait(child, settlement, seq, args, now, handle);
|
|
2238
|
-
if (settlement.kind === "down") {
|
|
2239
|
-
child.status = "failed";
|
|
2240
|
-
await args.journal.appendEvent(args.root, {
|
|
2241
|
-
kind: "settled",
|
|
2242
|
-
id: child.id,
|
|
2243
|
-
status: "down",
|
|
2244
|
-
spent: child.spent,
|
|
2245
|
-
infra: settlement.infra,
|
|
2246
|
-
seq,
|
|
2247
|
-
at: new Date(now()).toISOString()
|
|
2248
|
-
});
|
|
2249
|
-
if (settlement.metered) {
|
|
2250
|
-
await args.journal.appendEvent(args.root, {
|
|
2251
|
-
kind: "metered",
|
|
2252
|
-
id: child.id,
|
|
2253
|
-
spend: settlement.metered,
|
|
2254
|
-
seq,
|
|
2255
|
-
at: new Date(now()).toISOString()
|
|
2256
|
-
});
|
|
2257
|
-
}
|
|
2258
|
-
notifyRuntimeHookEvent(
|
|
2259
|
-
args.hooks,
|
|
2260
|
-
{
|
|
2261
|
-
id: `${child.id}:settled`,
|
|
2262
|
-
runId: args.root,
|
|
2263
|
-
target: "agent.child",
|
|
2264
|
-
phase: "after",
|
|
2265
|
-
timestamp: now(),
|
|
2266
|
-
stepIndex: seq,
|
|
2267
|
-
parentId: args.parentId,
|
|
2268
|
-
payload: {
|
|
2269
|
-
childId: child.id,
|
|
2270
|
-
status: "down",
|
|
2271
|
-
reason: settlement.reason,
|
|
2272
|
-
infra: settlement.infra,
|
|
2273
|
-
spent: child.spent
|
|
2274
|
-
}
|
|
2275
|
-
},
|
|
2276
|
-
{ signal: args.signal }
|
|
2277
|
-
);
|
|
2278
|
-
return {
|
|
2279
|
-
kind: "down",
|
|
2280
|
-
handle,
|
|
2281
|
-
reason: settlement.reason,
|
|
2282
|
-
infra: settlement.infra,
|
|
2283
|
-
restartCount: settlement.restartCount,
|
|
2284
|
-
seq
|
|
2285
|
-
};
|
|
2286
|
-
}
|
|
2287
|
-
child.status = "done";
|
|
2288
|
-
child.outRef = settlement.outRef;
|
|
2289
|
-
child.spent = settlement.spent;
|
|
2290
|
-
await args.journal.appendEvent(args.root, {
|
|
2291
|
-
kind: "settled",
|
|
2292
|
-
id: child.id,
|
|
2293
|
-
status: "done",
|
|
2294
|
-
outRef: settlement.outRef,
|
|
2295
|
-
...settlement.verdict ? { verdict: settlement.verdict } : {},
|
|
2296
|
-
spent: settlement.spent,
|
|
2297
|
-
seq,
|
|
2298
|
-
at: new Date(now()).toISOString()
|
|
2299
|
-
});
|
|
2300
|
-
if (settlement.metered) {
|
|
2301
|
-
await args.journal.appendEvent(args.root, {
|
|
2302
|
-
kind: "metered",
|
|
2303
|
-
id: child.id,
|
|
2304
|
-
spend: settlement.metered,
|
|
2305
|
-
seq,
|
|
2306
|
-
at: new Date(now()).toISOString()
|
|
2307
|
-
});
|
|
2308
|
-
}
|
|
2309
|
-
notifyRuntimeHookEvent(
|
|
2310
|
-
args.hooks,
|
|
2311
|
-
{
|
|
2312
|
-
id: `${child.id}:settled`,
|
|
2313
|
-
runId: args.root,
|
|
2314
|
-
target: "agent.child",
|
|
2315
|
-
phase: "after",
|
|
2316
|
-
timestamp: now(),
|
|
2317
|
-
stepIndex: seq,
|
|
2318
|
-
parentId: args.parentId,
|
|
2319
|
-
payload: {
|
|
2320
|
-
childId: child.id,
|
|
2321
|
-
status: "done",
|
|
2322
|
-
outRef: settlement.outRef,
|
|
2323
|
-
score: settlement.verdict?.score,
|
|
2324
|
-
valid: settlement.verdict?.valid,
|
|
2325
|
-
spent: settlement.spent
|
|
2326
|
-
}
|
|
2327
|
-
},
|
|
2328
|
-
{ signal: args.signal }
|
|
2329
|
-
);
|
|
2330
|
-
return {
|
|
2331
|
-
kind: "done",
|
|
2332
|
-
handle,
|
|
2333
|
-
out: settlement.out,
|
|
2334
|
-
outRef: settlement.outRef,
|
|
2335
|
-
...settlement.verdict ? { verdict: settlement.verdict } : {},
|
|
2336
|
-
spent: settlement.spent,
|
|
2337
|
-
seq
|
|
2338
|
-
};
|
|
2339
|
-
}
|
|
2340
|
-
async function finalizeWait(child, settlement, seq, args, now, handle) {
|
|
2341
|
-
const at = new Date(now()).toISOString();
|
|
2342
|
-
if (settlement.kind === "down") {
|
|
2343
|
-
child.status = "cancelled";
|
|
2344
|
-
await args.journal.appendEvent(args.root, {
|
|
2345
|
-
kind: "woken",
|
|
2346
|
-
id: child.id,
|
|
2347
|
-
by: "cancelled",
|
|
2348
|
-
seq,
|
|
2349
|
-
at
|
|
2350
|
-
});
|
|
2351
|
-
return {
|
|
2352
|
-
kind: "down",
|
|
2353
|
-
handle,
|
|
2354
|
-
reason: settlement.reason,
|
|
2355
|
-
infra: settlement.infra,
|
|
2356
|
-
restartCount: settlement.restartCount,
|
|
2357
|
-
seq
|
|
2358
|
-
};
|
|
2359
|
-
}
|
|
2360
|
-
child.status = "done";
|
|
2361
|
-
child.outRef = settlement.outRef;
|
|
2362
|
-
const out = settlement.out;
|
|
2363
|
-
await args.journal.appendEvent(args.root, {
|
|
2364
|
-
kind: "woken",
|
|
2365
|
-
id: child.id,
|
|
2366
|
-
by: out.settled,
|
|
2367
|
-
outRef: settlement.outRef,
|
|
2368
|
-
seq,
|
|
2369
|
-
at
|
|
2370
|
-
});
|
|
2371
|
-
notifyRuntimeHookEvent(
|
|
2372
|
-
args.hooks,
|
|
2373
|
-
{
|
|
2374
|
-
id: `${child.id}:woken`,
|
|
2375
|
-
runId: args.root,
|
|
2376
|
-
target: "agent.child",
|
|
2377
|
-
phase: "after",
|
|
2378
|
-
timestamp: now(),
|
|
2379
|
-
stepIndex: seq,
|
|
2380
|
-
parentId: args.parentId,
|
|
2381
|
-
payload: { childId: child.id, status: "done", wait: out }
|
|
2382
|
-
},
|
|
2383
|
-
{ signal: args.signal }
|
|
2384
|
-
);
|
|
2385
|
-
return {
|
|
2386
|
-
kind: "done",
|
|
2387
|
-
handle,
|
|
2388
|
-
out: settlement.out,
|
|
2389
|
-
outRef: settlement.outRef,
|
|
2390
|
-
spent: settlement.spent,
|
|
2391
|
-
seq
|
|
2392
|
-
};
|
|
2393
|
-
}
|
|
2394
|
-
async function runChild(live, executor, childAbort, task, opts, pool, ticket, blobs, now) {
|
|
2395
|
-
let reconciled = false;
|
|
2396
|
-
const reconcileOnce = (spend) => {
|
|
2397
|
-
if (reconciled) return;
|
|
2398
|
-
reconciled = true;
|
|
2399
|
-
pool.reconcile(ticket, clampSpend(spend, opts.budget));
|
|
2400
|
-
};
|
|
2401
|
-
try {
|
|
2402
|
-
live.status = "running";
|
|
2403
|
-
const ran = executor.execute(task, childAbort.signal);
|
|
2404
|
-
let artifact;
|
|
2405
|
-
if (isAsyncIterable(ran)) {
|
|
2406
|
-
const spend = await foldStream(ran, (running) => {
|
|
2407
|
-
live.spent = running;
|
|
2408
|
-
live.lastActivityAt = now();
|
|
2409
|
-
});
|
|
2410
|
-
live.spent = spend;
|
|
2411
|
-
artifact = executor.resultArtifact();
|
|
2412
|
-
reconcileOnce(spend);
|
|
2413
|
-
} else {
|
|
2414
|
-
const terminal = await ran;
|
|
2415
|
-
live.spent = terminal.spent;
|
|
2416
|
-
artifact = terminal;
|
|
2417
|
-
reconcileOnce(terminal.spent);
|
|
2418
|
-
}
|
|
2419
|
-
live.executorDone = true;
|
|
2420
|
-
const ownMetered = executor.metered?.();
|
|
2421
|
-
if (childAbort.signal.aborted) {
|
|
2422
|
-
await teardownSafe(executor, opts.shutdown ?? "brutalKill");
|
|
2423
|
-
return downRecord("aborted before settle", true, ownMetered);
|
|
2424
|
-
}
|
|
2425
|
-
const outRef = contentAddress(artifact.out);
|
|
2426
|
-
await blobs.put(outRef, artifact.out);
|
|
2427
|
-
await teardownSafe(executor, opts.shutdown ?? "infinity");
|
|
2428
|
-
return {
|
|
2429
|
-
kind: "done",
|
|
2430
|
-
out: artifact.out,
|
|
2431
|
-
outRef,
|
|
2432
|
-
...artifact.verdict ? { verdict: artifact.verdict } : {},
|
|
2433
|
-
spent: live.spent,
|
|
2434
|
-
...ownMetered ? { metered: ownMetered } : {}
|
|
2435
|
-
};
|
|
2436
|
-
} catch (err) {
|
|
2437
|
-
live.executorDone = true;
|
|
2438
|
-
reconcileOnce(live.spent);
|
|
2439
|
-
await teardownSafe(executor, "brutalKill");
|
|
2440
|
-
const aborted = childAbort.signal.aborted || isAbortError(err);
|
|
2441
|
-
return downRecord(errMessage(err), aborted || isInfraError(err), executor.metered?.());
|
|
2442
|
-
}
|
|
2443
|
-
}
|
|
2444
|
-
function settledToIteration(settled) {
|
|
2445
|
-
if (settled.kind === "down") {
|
|
2446
|
-
throw new ValidationError(
|
|
2447
|
-
`settledToIteration: cannot adapt a 'down' settlement (node '${settled.handle.id}', seq ${settled.seq}) to an Iteration`
|
|
2448
|
-
);
|
|
2449
|
-
}
|
|
2450
|
-
return {
|
|
2451
|
-
index: settled.seq,
|
|
2452
|
-
task: void 0,
|
|
2453
|
-
agentRunName: settled.handle.label,
|
|
2454
|
-
output: settled.out,
|
|
2455
|
-
...settled.verdict ? { verdict: settled.verdict } : {},
|
|
2456
|
-
events: [],
|
|
2457
|
-
startedAt: 0,
|
|
2458
|
-
endedAt: settled.spent.ms,
|
|
2459
|
-
costUsd: settled.spent.usd,
|
|
2460
|
-
tokenUsage: { input: settled.spent.tokens.input, output: settled.spent.tokens.output }
|
|
2461
|
-
};
|
|
2462
|
-
}
|
|
2463
|
-
function makeTreeView(root, children) {
|
|
2464
|
-
const nodes = [...children.values()].map((c) => ({
|
|
2465
|
-
id: c.id,
|
|
2466
|
-
parent: root,
|
|
2467
|
-
label: c.label,
|
|
2468
|
-
status: c.status,
|
|
2469
|
-
runtime: c.runtime,
|
|
2470
|
-
budget: c.budget,
|
|
2471
|
-
spent: c.spent,
|
|
2472
|
-
...c.outRef ? { outRef: c.outRef } : {}
|
|
2473
|
-
}));
|
|
2474
|
-
return {
|
|
2475
|
-
root,
|
|
2476
|
-
nodes,
|
|
2477
|
-
inFlight: nodes.filter((n) => n.status === "running" || n.status === "acquiring").length,
|
|
2478
|
-
waiting: nodes.filter((n) => n.status === "waiting").length
|
|
2479
|
-
};
|
|
2480
|
-
}
|
|
2481
|
-
function frozenHandle(child) {
|
|
2482
|
-
return {
|
|
2483
|
-
id: child.id,
|
|
2484
|
-
label: child.label,
|
|
2485
|
-
status: child.status,
|
|
2486
|
-
abort() {
|
|
2487
|
-
}
|
|
2488
|
-
};
|
|
2489
|
-
}
|
|
2490
|
-
async function foldStream(stream, onProgress) {
|
|
2491
|
-
const tokens = { input: 0, output: 0 };
|
|
2492
|
-
let usd = 0;
|
|
2493
|
-
let iterations = 0;
|
|
2494
|
-
for await (const ev of stream) {
|
|
2495
|
-
if (ev.kind === "tokens") {
|
|
2496
|
-
tokens.input += ev.input;
|
|
2497
|
-
tokens.output += ev.output;
|
|
2498
|
-
} else if (ev.kind === "cost") {
|
|
2499
|
-
usd += ev.usd;
|
|
2500
|
-
} else {
|
|
2501
|
-
iterations += 1;
|
|
2502
|
-
}
|
|
2503
|
-
onProgress?.({ iterations, tokens: { ...tokens }, usd, ms: 0 });
|
|
2504
|
-
}
|
|
2505
|
-
return { iterations, tokens, usd, ms: 0 };
|
|
2506
|
-
}
|
|
2507
|
-
function clampSpend(spend, budget) {
|
|
2508
|
-
const totalTokens2 = spend.tokens.input + spend.tokens.output;
|
|
2509
|
-
const tokensOk = totalTokens2 <= budget.maxTokens;
|
|
2510
|
-
const itersOk = spend.iterations <= budget.maxIterations;
|
|
2511
|
-
const usdOk = budget.maxUsd === void 0 || spend.usd <= budget.maxUsd;
|
|
2512
|
-
if (tokensOk && itersOk && usdOk) return spend;
|
|
2513
|
-
const ratio = !tokensOk && totalTokens2 > 0 ? budget.maxTokens / totalTokens2 : 1;
|
|
2514
|
-
return {
|
|
2515
|
-
iterations: Math.min(spend.iterations, budget.maxIterations),
|
|
2516
|
-
tokens: ratio < 1 ? {
|
|
2517
|
-
input: Math.floor(spend.tokens.input * ratio),
|
|
2518
|
-
output: Math.floor(spend.tokens.output * ratio)
|
|
2519
|
-
} : spend.tokens,
|
|
2520
|
-
usd: budget.maxUsd === void 0 ? spend.usd : Math.min(spend.usd, budget.maxUsd),
|
|
2521
|
-
...spend.usdKnown === false ? { usdKnown: false } : {},
|
|
2522
|
-
ms: spend.ms
|
|
2523
|
-
};
|
|
2524
|
-
}
|
|
2525
|
-
async function teardownSafe(executor, grace) {
|
|
2526
|
-
try {
|
|
2527
|
-
await executor.teardown(grace);
|
|
2528
|
-
} catch {
|
|
2529
|
-
}
|
|
2530
|
-
}
|
|
2531
|
-
function downRecord(reason, infra, metered) {
|
|
2532
|
-
return { kind: "down", reason, infra, restartCount: 0, ...metered ? { metered } : {} };
|
|
2533
|
-
}
|
|
2534
|
-
function zeroSpend() {
|
|
2535
|
-
return { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
2536
|
-
}
|
|
2537
|
-
function isAsyncIterable(value) {
|
|
2538
|
-
return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
|
|
2539
|
-
}
|
|
2540
|
-
function isAgentSpec(value) {
|
|
2541
|
-
if (typeof value !== "object" || value === null) return false;
|
|
2542
|
-
const v = value;
|
|
2543
|
-
return "profile" in v && "harness" in v;
|
|
2544
|
-
}
|
|
2545
|
-
function isAbortError(err) {
|
|
2546
|
-
return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
|
|
2547
|
-
}
|
|
2548
|
-
function isInfraError(err) {
|
|
2549
|
-
return err instanceof ValidationError;
|
|
2550
|
-
}
|
|
2551
|
-
function errMessage(err) {
|
|
2552
|
-
if (err instanceof Error) return err.message;
|
|
2553
|
-
return String(err);
|
|
2554
|
-
}
|
|
2555
|
-
|
|
2556
|
-
// src/runtime/supervise/budget.ts
|
|
2557
|
-
function spendFromUsageEvents(events) {
|
|
2558
|
-
const tokens = zeroTokenUsage();
|
|
2559
|
-
let usd = 0;
|
|
2560
|
-
let iterations = 0;
|
|
2561
|
-
for (const ev of events) {
|
|
2562
|
-
if (ev.kind === "tokens") {
|
|
2563
|
-
addTokenUsage(tokens, { input: ev.input, output: ev.output });
|
|
2564
|
-
} else if (ev.kind === "cost") {
|
|
2565
|
-
usd += ev.usd;
|
|
2566
|
-
} else {
|
|
2567
|
-
iterations += 1;
|
|
2568
|
-
}
|
|
2569
|
-
}
|
|
2570
|
-
return { iterations, tokens, usd, ms: 0 };
|
|
2571
|
-
}
|
|
2572
|
-
async function foldUsage(events) {
|
|
2573
|
-
if (Array.isArray(events)) return spendFromUsageEvents(events);
|
|
2574
|
-
const tokens = zeroTokenUsage();
|
|
2575
|
-
let usd = 0;
|
|
2576
|
-
let iterations = 0;
|
|
2577
|
-
for await (const ev of events) {
|
|
2578
|
-
if (ev.kind === "tokens") {
|
|
2579
|
-
addTokenUsage(tokens, { input: ev.input, output: ev.output });
|
|
2580
|
-
} else if (ev.kind === "cost") {
|
|
2581
|
-
usd += ev.usd;
|
|
2582
|
-
} else {
|
|
2583
|
-
iterations += 1;
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
return { iterations, tokens, usd, ms: 0 };
|
|
2587
|
-
}
|
|
2588
|
-
function totalTokens(usage) {
|
|
2589
|
-
return usage.input + usage.output;
|
|
2590
|
-
}
|
|
2591
|
-
function createBudgetPool(root, now = Date.now) {
|
|
2592
|
-
let freeTokens = root.maxTokens;
|
|
2593
|
-
let reservedTokens = 0;
|
|
2594
|
-
let committedTokens = 0;
|
|
2595
|
-
const usdCapped = root.maxUsd !== void 0;
|
|
2596
|
-
let freeUsd = root.maxUsd ?? 0;
|
|
2597
|
-
let reservedUsd = 0;
|
|
2598
|
-
let committedUsd = 0;
|
|
2599
|
-
let freeIterations = root.maxIterations;
|
|
2600
|
-
let reservedIterations = 0;
|
|
2601
|
-
let committedIterations = 0;
|
|
2602
|
-
const absoluteDeadlineMs = root.deadlineMs !== void 0 ? now() + root.deadlineMs : 0;
|
|
2603
|
-
let nextTicketId = 0;
|
|
2604
|
-
const open3 = /* @__PURE__ */ new Set();
|
|
2605
|
-
function reserve(b) {
|
|
2606
|
-
const wantTokens = b.maxTokens;
|
|
2607
|
-
const wantUsd = b.maxUsd ?? 0;
|
|
2608
|
-
const wantIterations = b.maxIterations;
|
|
2609
|
-
if (wantTokens > freeTokens) return { ok: false, reason: "budget-exhausted" };
|
|
2610
|
-
if (wantIterations > freeIterations) return { ok: false, reason: "budget-exhausted" };
|
|
2611
|
-
if (wantUsd > 0 && (!usdCapped || wantUsd > freeUsd)) {
|
|
2612
|
-
return { ok: false, reason: "budget-exhausted" };
|
|
2613
|
-
}
|
|
2614
|
-
freeTokens -= wantTokens;
|
|
2615
|
-
reservedTokens += wantTokens;
|
|
2616
|
-
freeIterations -= wantIterations;
|
|
2617
|
-
reservedIterations += wantIterations;
|
|
2618
|
-
if (wantUsd > 0) {
|
|
2619
|
-
freeUsd -= wantUsd;
|
|
2620
|
-
reservedUsd += wantUsd;
|
|
2621
|
-
}
|
|
2622
|
-
const id = nextTicketId++;
|
|
2623
|
-
open3.add(id);
|
|
2624
|
-
return {
|
|
2625
|
-
ok: true,
|
|
2626
|
-
ticket: { id, reserved: { tokens: wantTokens, usd: wantUsd, iterations: wantIterations } }
|
|
2627
|
-
};
|
|
2628
|
-
}
|
|
2629
|
-
function reconcile(ticket, spent) {
|
|
2630
|
-
if (!open3.has(ticket.id)) {
|
|
2631
|
-
throw new Error(`budget pool: reconcile of unknown or already-settled ticket ${ticket.id}`);
|
|
2632
|
-
}
|
|
2633
|
-
open3.delete(ticket.id);
|
|
2634
|
-
const { tokens: rTokens, usd: rUsd, iterations: rIterations } = ticket.reserved;
|
|
2635
|
-
if (usdCapped && spent.usdKnown === false) {
|
|
2636
|
-
throw new Error(
|
|
2637
|
-
`budget pool: ticket ${ticket.id} reported unknown dollar cost under a dollar-capped budget`
|
|
2638
|
-
);
|
|
2639
|
-
}
|
|
2640
|
-
const spentTokens = totalTokens(spent.tokens);
|
|
2641
|
-
if (spentTokens > rTokens) {
|
|
2642
|
-
throw new Error(
|
|
2643
|
-
`budget pool: ticket ${ticket.id} spent ${spentTokens} tokens > reserved ${rTokens}`
|
|
2644
|
-
);
|
|
2645
|
-
}
|
|
2646
|
-
if (spent.iterations > rIterations) {
|
|
2647
|
-
throw new Error(
|
|
2648
|
-
`budget pool: ticket ${ticket.id} spent ${spent.iterations} iterations > reserved ${rIterations}`
|
|
2649
|
-
);
|
|
2650
|
-
}
|
|
2651
|
-
if (usdCapped && spent.usd > rUsd) {
|
|
2652
|
-
throw new Error(`budget pool: ticket ${ticket.id} spent $${spent.usd} > reserved $${rUsd}`);
|
|
2653
|
-
}
|
|
2654
|
-
reservedTokens -= rTokens;
|
|
2655
|
-
committedTokens += spentTokens;
|
|
2656
|
-
freeTokens += rTokens - spentTokens;
|
|
2657
|
-
reservedIterations -= rIterations;
|
|
2658
|
-
committedIterations += spent.iterations;
|
|
2659
|
-
freeIterations += rIterations - spent.iterations;
|
|
2660
|
-
if (usdCapped && rUsd > 0) {
|
|
2661
|
-
reservedUsd -= rUsd;
|
|
2662
|
-
committedUsd += spent.usd;
|
|
2663
|
-
freeUsd += rUsd - spent.usd;
|
|
2664
|
-
} else {
|
|
2665
|
-
committedUsd += spent.usd;
|
|
2666
|
-
}
|
|
2667
|
-
}
|
|
2668
|
-
function observe(spend) {
|
|
2669
|
-
if (usdCapped && spend.usdKnown === false) {
|
|
2670
|
-
throw new Error(
|
|
2671
|
-
"budget pool: cannot observe unknown dollar cost under a dollar-capped budget"
|
|
2672
|
-
);
|
|
2673
|
-
}
|
|
2674
|
-
const tokens = totalTokens(spend.tokens);
|
|
2675
|
-
freeTokens -= tokens;
|
|
2676
|
-
committedTokens += tokens;
|
|
2677
|
-
freeIterations -= spend.iterations;
|
|
2678
|
-
committedIterations += spend.iterations;
|
|
2679
|
-
committedUsd += spend.usd;
|
|
2680
|
-
if (usdCapped) freeUsd -= spend.usd;
|
|
2681
|
-
}
|
|
2682
|
-
function readout() {
|
|
2683
|
-
return {
|
|
2684
|
-
tokensLeft: freeTokens,
|
|
2685
|
-
usdLeft: usdCapped ? freeUsd : 0,
|
|
2686
|
-
usdCapped,
|
|
2687
|
-
deadlineMs: absoluteDeadlineMs,
|
|
2688
|
-
reservedTokens
|
|
2689
|
-
};
|
|
2690
|
-
}
|
|
2691
|
-
function assertNoOpenTickets() {
|
|
2692
|
-
if (open3.size > 0) {
|
|
2693
|
-
throw new Error(
|
|
2694
|
-
`budget pool: ${open3.size} reservation(s) still open at join barrier (leaked ticket ids: ${[...open3].join(", ")}) \u2014 conserved-pool invariant violated`
|
|
2695
|
-
);
|
|
2696
|
-
}
|
|
2697
|
-
}
|
|
2698
|
-
return {
|
|
2699
|
-
reserve,
|
|
2700
|
-
reconcile,
|
|
2701
|
-
spendFrom: foldUsage,
|
|
2702
|
-
readout,
|
|
2703
|
-
observe,
|
|
2704
|
-
assertNoOpenTickets
|
|
2705
|
-
};
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
|
-
// src/runtime/supervise/supervisor.ts
|
|
2709
|
-
function maxSeqOf(events, pred) {
|
|
2710
|
-
let max = -1;
|
|
2711
|
-
for (const ev of events) if (pred(ev) && ev.seq > max) max = ev.seq;
|
|
2712
|
-
return max;
|
|
2713
|
-
}
|
|
2714
|
-
var defaultMaxDepth = 4;
|
|
2715
|
-
function createSupervisor() {
|
|
2716
|
-
let attached;
|
|
2717
|
-
async function run(root, task, opts) {
|
|
2718
|
-
const now = opts.now ?? Date.now;
|
|
2719
|
-
const pool = createBudgetPool(opts.budget, now);
|
|
2720
|
-
const prior = opts.resume === true ? await opts.journal.loadTree(opts.runId) : void 0;
|
|
2721
|
-
const resuming = prior !== void 0 && prior.length > 0;
|
|
2722
|
-
let resumeFrom;
|
|
2723
|
-
if (resuming) {
|
|
2724
|
-
const settled = await replaySpawnTree(opts.journal, opts.blobs, opts.runId);
|
|
2725
|
-
const view = materializeTreeView(prior);
|
|
2726
|
-
resumeFrom = {
|
|
2727
|
-
settled,
|
|
2728
|
-
view,
|
|
2729
|
-
maxSpawnOrdinal: maxSeqOf(prior, (ev) => ev.kind === "spawned"),
|
|
2730
|
-
maxCursorSeq: maxSeqOf(
|
|
2731
|
-
prior,
|
|
2732
|
-
(ev) => ev.kind === "settled" || ev.kind === "cancelled" || ev.kind === "woken"
|
|
2733
|
-
),
|
|
2734
|
-
maxWaitOrdinal: maxSeqOf(prior, (ev) => ev.kind === "waiting"),
|
|
2735
|
-
// Waits armed but never woken: the run died mid-wait. They ride onto `Scope.resume.waits`,
|
|
2736
|
-
// and re-arming the same label adopts the ORIGINAL absolute deadline rather than
|
|
2737
|
-
// restarting the countdown from this process's clock.
|
|
2738
|
-
waits: pendingWaits(prior)
|
|
2739
|
-
};
|
|
2740
|
-
} else {
|
|
2741
|
-
await opts.journal.beginTree(opts.runId, new Date(now()).toISOString());
|
|
2742
|
-
await opts.journal.appendEvent(opts.runId, {
|
|
2743
|
-
kind: "spawned",
|
|
2744
|
-
id: opts.runId,
|
|
2745
|
-
label: "root",
|
|
2746
|
-
budget: opts.budget,
|
|
2747
|
-
runtime: "inline",
|
|
2748
|
-
seq: 0,
|
|
2749
|
-
at: new Date(now()).toISOString()
|
|
2750
|
-
});
|
|
2751
|
-
}
|
|
2752
|
-
const controller = new AbortController();
|
|
2753
|
-
const cascadeAbort = (reason) => {
|
|
2754
|
-
if (controller.signal.aborted) return;
|
|
2755
|
-
controller.abort(reason);
|
|
2756
|
-
};
|
|
2757
|
-
const onCallerAbort = () => cascadeAbort("caller signal aborted");
|
|
2758
|
-
if (opts.signal) {
|
|
2759
|
-
if (opts.signal.aborted) cascadeAbort("caller signal aborted");
|
|
2760
|
-
else opts.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
2761
|
-
}
|
|
2762
|
-
const breaker = createIntensityBreaker(opts, () => cascadeAbort("intensity breaker tripped"));
|
|
2763
|
-
const journal = wrapJournalForBreaker(opts.journal, breaker);
|
|
2764
|
-
const scope = createScope({
|
|
2765
|
-
parentId: opts.runId,
|
|
2766
|
-
root: opts.runId,
|
|
2767
|
-
pool,
|
|
2768
|
-
journal,
|
|
2769
|
-
blobs: opts.blobs,
|
|
2770
|
-
executors: opts.executors,
|
|
2771
|
-
seams: {},
|
|
2772
|
-
depth: 0,
|
|
2773
|
-
maxDepth: opts.maxDepth ?? defaultMaxDepth,
|
|
2774
|
-
signal: controller.signal,
|
|
2775
|
-
now,
|
|
2776
|
-
hooks: opts.hooks,
|
|
2777
|
-
...opts.probes ? { probes: opts.probes } : {},
|
|
2778
|
-
...resumeFrom ? { resumeFrom } : {}
|
|
2779
|
-
});
|
|
2780
|
-
const openScope = scope;
|
|
2781
|
-
if (attached) {
|
|
2782
|
-
attached.bind({ scope: openScope, cascadeAbort, signal: pushRootSignal(cascadeAbort) });
|
|
2783
|
-
}
|
|
2784
|
-
let actOutcome;
|
|
2785
|
-
try {
|
|
2786
|
-
const out = await root.act(task, scope);
|
|
2787
|
-
actOutcome = { ok: true, out };
|
|
2788
|
-
} catch (error) {
|
|
2789
|
-
actOutcome = { ok: false, error };
|
|
2790
|
-
} finally {
|
|
2791
|
-
await drainLiveChildren(openScope, controller);
|
|
2792
|
-
if (opts.signal) opts.signal.removeEventListener("abort", onCallerAbort);
|
|
2793
|
-
if (attached) attached.unbind();
|
|
2794
|
-
}
|
|
2795
|
-
const tree = scope.view;
|
|
2796
|
-
if (actOutcome.ok) {
|
|
2797
|
-
pool.assertNoOpenTickets();
|
|
2798
|
-
const out = actOutcome.out;
|
|
2799
|
-
if (out !== void 0) {
|
|
2800
|
-
const outRef = contentAddress(out);
|
|
2801
|
-
await opts.blobs.put(outRef, out);
|
|
2802
|
-
const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
|
|
2803
|
-
return {
|
|
2804
|
-
kind: "winner",
|
|
2805
|
-
out,
|
|
2806
|
-
outRef,
|
|
2807
|
-
tree,
|
|
2808
|
-
spentTotal: addSpend(childWork, driverInference),
|
|
2809
|
-
...isNonEmptySpend(driverInference) ? { spentBreakdown: { driverInference, childWork } } : {}
|
|
2810
|
-
};
|
|
2811
|
-
}
|
|
2812
|
-
return noWinner();
|
|
2813
|
-
}
|
|
2814
|
-
return noWinner();
|
|
2815
|
-
async function noWinner() {
|
|
2816
|
-
const { childWork, driverInference } = await spentFromJournal(journal, opts.runId);
|
|
2817
|
-
return {
|
|
2818
|
-
kind: "no-winner",
|
|
2819
|
-
reason: classifyNoWinner(controller, pool, opts, breaker),
|
|
2820
|
-
tree,
|
|
2821
|
-
downCount: breaker.downCount(),
|
|
2822
|
-
spentTotal: addSpend(childWork, driverInference)
|
|
2823
|
-
};
|
|
2824
|
-
}
|
|
2825
|
-
}
|
|
2826
|
-
function attach(h) {
|
|
2827
|
-
const control = rootControls.get(h);
|
|
2828
|
-
if (!control) {
|
|
2829
|
-
throw new RuntimeRunStateError(
|
|
2830
|
-
"supervisor.attach: handle was not minted by createRootHandle (no control channel)"
|
|
2831
|
-
);
|
|
2832
|
-
}
|
|
2833
|
-
attached = control;
|
|
2834
|
-
}
|
|
2835
|
-
return { run, attach };
|
|
2836
|
-
}
|
|
2837
|
-
var rootControls = /* @__PURE__ */ new WeakMap();
|
|
2838
|
-
function pushRootSignal(cascadeAbort) {
|
|
2839
|
-
return (msg) => {
|
|
2840
|
-
if (msg.kind === "cancel") cascadeAbort(msg.reason ?? "root signal: cancel");
|
|
2841
|
-
};
|
|
2842
|
-
}
|
|
2843
|
-
function createIntensityBreaker(opts, trip) {
|
|
2844
|
-
const max = opts.maxRestarts;
|
|
2845
|
-
const within = opts.withinMs;
|
|
2846
|
-
const armed = max !== void 0 && within !== void 0;
|
|
2847
|
-
const recent = [];
|
|
2848
|
-
let total = 0;
|
|
2849
|
-
let isTripped = false;
|
|
2850
|
-
return {
|
|
2851
|
-
recordDown(at) {
|
|
2852
|
-
total += 1;
|
|
2853
|
-
if (!armed || isTripped) return;
|
|
2854
|
-
recent.push(at);
|
|
2855
|
-
const cutoff = at - within;
|
|
2856
|
-
while (recent.length > 0 && recent[0] < cutoff) recent.shift();
|
|
2857
|
-
if (recent.length > max) {
|
|
2858
|
-
isTripped = true;
|
|
2859
|
-
trip();
|
|
2860
|
-
}
|
|
2861
|
-
},
|
|
2862
|
-
tripped() {
|
|
2863
|
-
return isTripped;
|
|
2864
|
-
},
|
|
2865
|
-
downCount() {
|
|
2866
|
-
return total;
|
|
2867
|
-
}
|
|
2868
|
-
};
|
|
2869
|
-
}
|
|
2870
|
-
function wrapJournalForBreaker(journal, breaker) {
|
|
2871
|
-
return {
|
|
2872
|
-
loadTree: (root) => journal.loadTree(root),
|
|
2873
|
-
beginTree: (root, at) => journal.beginTree(root, at),
|
|
2874
|
-
appendEvent: (root, ev) => {
|
|
2875
|
-
if (ev.kind === "settled" && ev.status === "down") breaker.recordDown(Date.parse(ev.at));
|
|
2876
|
-
return journal.appendEvent(root, ev);
|
|
2877
|
-
}
|
|
2878
|
-
};
|
|
2879
|
-
}
|
|
2880
|
-
async function drainLiveChildren(scope, controller) {
|
|
2881
|
-
const view = scope.view;
|
|
2882
|
-
const hasLive = view.inFlight > 0 || view.waiting > 0;
|
|
2883
|
-
if (!hasLive) return;
|
|
2884
|
-
if (!controller.signal.aborted) controller.abort();
|
|
2885
|
-
await Promise.allSettled([drainCursor(scope)]);
|
|
2886
|
-
}
|
|
2887
|
-
async function drainCursor(scope) {
|
|
2888
|
-
for (; ; ) {
|
|
2889
|
-
const settled = await scope.next();
|
|
2890
|
-
if (settled === null) return;
|
|
2891
|
-
}
|
|
2892
|
-
}
|
|
2893
|
-
function classifyNoWinner(controller, pool, opts, breaker) {
|
|
2894
|
-
if (breaker.tripped()) return "all-children-down";
|
|
2895
|
-
if (controller.signal.aborted) return "aborted";
|
|
2896
|
-
if (poolExhausted(pool, opts)) return "budget-exhausted";
|
|
2897
|
-
return "all-children-down";
|
|
2898
|
-
}
|
|
2899
|
-
function poolExhausted(pool, opts) {
|
|
2900
|
-
const r = pool.readout();
|
|
2901
|
-
if (r.tokensLeft <= 0) return true;
|
|
2902
|
-
if (opts.budget.maxUsd !== void 0 && r.usdLeft <= 0) return true;
|
|
2903
|
-
if (opts.budget.deadlineMs !== void 0 && r.deadlineMs > 0 && (opts.now ?? Date.now)() >= r.deadlineMs) {
|
|
2904
|
-
return true;
|
|
2905
|
-
}
|
|
2906
|
-
return false;
|
|
2907
|
-
}
|
|
2908
|
-
async function spentFromJournal(journal, root) {
|
|
2909
|
-
const events = await journal.loadTree(root);
|
|
2910
|
-
if (events === void 0) {
|
|
2911
|
-
throw new RuntimeRunStateError(
|
|
2912
|
-
`supervisor: spawn tree '${root}' is missing from the journal after run (corrupted log)`
|
|
2913
|
-
);
|
|
2914
|
-
}
|
|
2915
|
-
const childWork = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
2916
|
-
const driverInference = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
2917
|
-
for (const ev of events) {
|
|
2918
|
-
if (ev.kind === "settled") accumulate(childWork, ev.spent);
|
|
2919
|
-
else if (ev.kind === "metered") accumulate(driverInference, ev.spend);
|
|
2920
|
-
}
|
|
2921
|
-
return { childWork, driverInference };
|
|
2922
|
-
}
|
|
2923
|
-
function accumulate(a, b) {
|
|
2924
|
-
a.iterations += b.iterations;
|
|
2925
|
-
a.tokens.input += b.tokens.input;
|
|
2926
|
-
a.tokens.output += b.tokens.output;
|
|
2927
|
-
a.usd += b.usd;
|
|
2928
|
-
if (b.usdKnown === false) a.usdKnown = false;
|
|
2929
|
-
a.ms += b.ms;
|
|
2930
|
-
}
|
|
2931
|
-
function addSpend(a, b) {
|
|
2932
|
-
return {
|
|
2933
|
-
iterations: a.iterations + b.iterations,
|
|
2934
|
-
tokens: { input: a.tokens.input + b.tokens.input, output: a.tokens.output + b.tokens.output },
|
|
2935
|
-
usd: a.usd + b.usd,
|
|
2936
|
-
...a.usdKnown === false || b.usdKnown === false ? { usdKnown: false } : {},
|
|
2937
|
-
ms: a.ms + b.ms
|
|
2938
|
-
};
|
|
2939
|
-
}
|
|
2940
|
-
function isNonEmptySpend(s) {
|
|
2941
|
-
return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
|
|
2942
|
-
}
|
|
2943
|
-
|
|
2944
|
-
// src/mcp/worktree-harness.ts
|
|
2945
|
-
import { spawn as spawn3 } from "child_process";
|
|
2946
|
-
import { createHash as createHash2 } from "crypto";
|
|
2947
|
-
import {
|
|
2948
|
-
applyWorkspacePlan,
|
|
2949
|
-
materializeProfile
|
|
2950
|
-
} from "@tangle-network/agent-profile-materialize";
|
|
2951
|
-
var defaultCheckOutputCap = 16e3;
|
|
2952
|
-
async function runWorktreeHarness(opts) {
|
|
2953
|
-
opts.signal?.throwIfAborted();
|
|
2954
|
-
const runHarness = opts.runHarness ?? runLocalHarness;
|
|
2955
|
-
const runCommand = opts.runCommand ?? defaultRunCommand;
|
|
2956
|
-
const checkTimeoutMs = opts.checkTimeoutMs ?? opts.harnessTimeoutMs ?? 5 * 60 * 1e3;
|
|
2957
|
-
const cap = opts.checkOutputCap ?? defaultCheckOutputCap;
|
|
2958
|
-
const worktree = await createWorktree({
|
|
2959
|
-
repoRoot: opts.repoRoot,
|
|
2960
|
-
runId: opts.runId,
|
|
2961
|
-
...opts.baseRef ? { baseRef: opts.baseRef } : {},
|
|
2962
|
-
...opts.runGit ? { runGit: opts.runGit } : {}
|
|
2963
|
-
});
|
|
2964
|
-
const cleanup = () => removeWorktree({
|
|
2965
|
-
worktree,
|
|
2966
|
-
repoRoot: opts.repoRoot,
|
|
2967
|
-
...opts.runGit ? { runGit: opts.runGit } : {}
|
|
2968
|
-
});
|
|
2969
|
-
try {
|
|
2970
|
-
assertSupportedWorktreeProfile(opts.profile, opts.harness);
|
|
2971
|
-
assertSafeProfileResourcePaths(opts.profile);
|
|
2972
|
-
const resourceInstructions = resolveResourceInstructions(opts.profile);
|
|
2973
|
-
const workspaceProfile = materializationOnlyProfile(opts.profile);
|
|
2974
|
-
const plan = materializeProfile(workspaceProfile, opts.harness);
|
|
2975
|
-
if (plan.unsupported.length > 0) {
|
|
2976
|
-
throw new Error(
|
|
2977
|
-
`runWorktreeHarness: profile cannot be materialized for ${opts.harness}: ${plan.unsupported.map(({ dimension, reason }) => `${dimension}: ${reason}`).join("; ")}`
|
|
2978
|
-
);
|
|
2979
|
-
}
|
|
2980
|
-
assertSafeMaterializedPaths(plan);
|
|
2981
|
-
const applied = applyWorkspacePlan(plan, worktree.path, { existingFiles: "reject" });
|
|
2982
|
-
if (applied.unsupported.length > 0) {
|
|
2983
|
-
throw new Error("runWorktreeHarness: applied profile unexpectedly retained unsupported rows");
|
|
2984
|
-
}
|
|
2985
|
-
const invocationProfile = profileWithResourceInstructions(opts.profile, resourceInstructions);
|
|
2986
|
-
const { command, args } = harnessInvocation(opts.harness, invocationProfile, opts.taskPrompt, {
|
|
2987
|
-
// This helper created the candidate worktree above; autonomous Claude
|
|
2988
|
-
// edits are permitted only inside that isolated checkout.
|
|
2989
|
-
dangerouslySkipPermissions: opts.harness === "claude",
|
|
2990
|
-
...opts.codexReproducible ? { codexReproducible: true } : {}
|
|
2991
|
-
});
|
|
2992
|
-
const harnessResult = await runHarness({
|
|
2993
|
-
harness: opts.harness,
|
|
2994
|
-
cwd: worktree.path,
|
|
2995
|
-
taskPrompt: opts.taskPrompt,
|
|
2996
|
-
invocation: { command, args: [...args, ...applied.flags] },
|
|
2997
|
-
env: { ...process.env, ...applied.env },
|
|
2998
|
-
...opts.codexReproducible ? { codexReproducible: true } : {},
|
|
2999
|
-
...opts.codexReadDeniedPaths ? { codexReadDeniedPaths: opts.codexReadDeniedPaths } : {},
|
|
3000
|
-
...opts.harnessTimeoutMs !== void 0 ? { timeoutMs: opts.harnessTimeoutMs } : {},
|
|
3001
|
-
...opts.signal ? { signal: opts.signal } : {}
|
|
3002
|
-
});
|
|
3003
|
-
if (harnessResult.aborted || opts.signal?.aborted) {
|
|
3004
|
-
throw new Error("runWorktreeHarness: harness was cancelled by the caller", {
|
|
3005
|
-
cause: opts.signal?.reason
|
|
3006
|
-
});
|
|
3007
|
-
}
|
|
3008
|
-
opts.signal?.throwIfAborted();
|
|
3009
|
-
const diff = await captureWorktreeDiff({
|
|
3010
|
-
worktree,
|
|
3011
|
-
excludePaths: applied.written,
|
|
3012
|
-
...opts.runGit ? { runGit: opts.runGit } : {}
|
|
3013
|
-
});
|
|
3014
|
-
opts.signal?.throwIfAborted();
|
|
3015
|
-
const checks = await runWorktreeChecks({
|
|
3016
|
-
worktreePath: worktree.path,
|
|
3017
|
-
...opts.testCmd !== void 0 ? { testCmd: opts.testCmd } : {},
|
|
3018
|
-
...opts.typecheckCmd !== void 0 ? { typecheckCmd: opts.typecheckCmd } : {},
|
|
3019
|
-
timeoutMs: checkTimeoutMs,
|
|
3020
|
-
cap,
|
|
3021
|
-
runCommand,
|
|
3022
|
-
...opts.signal ? { signal: opts.signal } : {}
|
|
3023
|
-
});
|
|
3024
|
-
opts.signal?.throwIfAborted();
|
|
3025
|
-
const result = {
|
|
3026
|
-
branch: worktree.branch,
|
|
3027
|
-
patch: diff.patch,
|
|
3028
|
-
stats: diff.stats,
|
|
3029
|
-
profileMaterialization: profileMaterializationReceipt(applied, resourceInstructions),
|
|
3030
|
-
harness: {
|
|
3031
|
-
name: opts.harness,
|
|
3032
|
-
exitCode: harnessResult.exitCode,
|
|
3033
|
-
timedOut: harnessResult.timedOut,
|
|
3034
|
-
killedBySignal: harnessResult.killedBySignal,
|
|
3035
|
-
durationMs: harnessResult.durationMs,
|
|
3036
|
-
stdout: harnessResult.stdout,
|
|
3037
|
-
stderr: harnessResult.stderr,
|
|
3038
|
-
...harnessResult.usage ? { usage: harnessResult.usage } : {},
|
|
3039
|
-
...harnessResult.evidence ? {
|
|
3040
|
-
cliVersion: harnessResult.evidence.cliVersion,
|
|
3041
|
-
executableSha256: harnessResult.evidence.executableSha256,
|
|
3042
|
-
requestedPromptSha256: harnessResult.evidence.requestedPromptSha256,
|
|
3043
|
-
effectivePromptSha256: harnessResult.evidence.effectivePromptSha256,
|
|
3044
|
-
nonPromptArgsSha256: harnessResult.evidence.nonPromptArgsSha256,
|
|
3045
|
-
controlledConfigSha256: harnessResult.evidence.controlledConfigSha256,
|
|
3046
|
-
readDeniedPaths: [...harnessResult.evidence.readDeniedPaths],
|
|
3047
|
-
readDeniedPathsSha256: harnessResult.evidence.readDeniedPathsSha256,
|
|
3048
|
-
readDeniedPathCount: harnessResult.evidence.readDeniedPathCount,
|
|
3049
|
-
executionPolicy: harnessResult.evidence.policy
|
|
3050
|
-
} : {}
|
|
3051
|
-
},
|
|
3052
|
-
...checks ? { checks } : {}
|
|
3053
|
-
};
|
|
3054
|
-
return { worktree, result, cleanup };
|
|
3055
|
-
} catch (err) {
|
|
3056
|
-
try {
|
|
3057
|
-
await cleanup();
|
|
3058
|
-
} catch (cleanupError) {
|
|
3059
|
-
throw new AggregateError(
|
|
3060
|
-
[err, cleanupError],
|
|
3061
|
-
"runWorktreeHarness: run failed and worktree cleanup also failed"
|
|
3062
|
-
);
|
|
3063
|
-
}
|
|
3064
|
-
throw err;
|
|
3065
|
-
}
|
|
3066
|
-
}
|
|
3067
|
-
function resolveResourceInstructions(profile) {
|
|
3068
|
-
const instructions = profile.resources?.instructions;
|
|
3069
|
-
if (instructions === void 0) return void 0;
|
|
3070
|
-
if (typeof instructions === "string")
|
|
3071
|
-
return instructions.trim().length > 0 ? instructions : void 0;
|
|
3072
|
-
if (instructions.kind === "inline" && typeof instructions.content === "string") {
|
|
3073
|
-
return instructions.content.trim().length > 0 ? instructions.content : void 0;
|
|
3074
|
-
}
|
|
3075
|
-
throw new Error(
|
|
3076
|
-
"runWorktreeHarness: resources.instructions must be a string or inline resource; remote refs require pre-resolution"
|
|
3077
|
-
);
|
|
3078
|
-
}
|
|
3079
|
-
function profileWithResourceInstructions(profile, resourceInstructions) {
|
|
3080
|
-
let resources;
|
|
3081
|
-
if (profile.resources) {
|
|
3082
|
-
const { instructions: _instructions, ...structuralResources } = profile.resources;
|
|
3083
|
-
resources = structuralResources;
|
|
3084
|
-
}
|
|
3085
|
-
return {
|
|
3086
|
-
...profile,
|
|
3087
|
-
...resources ? { resources } : {},
|
|
3088
|
-
...resourceInstructions === void 0 ? {} : {
|
|
3089
|
-
prompt: {
|
|
3090
|
-
...profile.prompt,
|
|
3091
|
-
instructions: [...profile.prompt?.instructions ?? [], resourceInstructions]
|
|
3092
|
-
}
|
|
3093
|
-
}
|
|
3094
|
-
};
|
|
3095
|
-
}
|
|
3096
|
-
function materializationOnlyProfile(profile) {
|
|
3097
|
-
const {
|
|
3098
|
-
prompt: _prompt,
|
|
3099
|
-
model: _model,
|
|
3100
|
-
resources: profileResources,
|
|
3101
|
-
...structuralProfile
|
|
3102
|
-
} = profile;
|
|
3103
|
-
let resources;
|
|
3104
|
-
if (profileResources) {
|
|
3105
|
-
const { instructions: _instructions, ...fileBackedResources } = profileResources;
|
|
3106
|
-
resources = fileBackedResources;
|
|
3107
|
-
}
|
|
3108
|
-
return {
|
|
3109
|
-
...structuralProfile,
|
|
3110
|
-
...resources ? { resources } : {}
|
|
3111
|
-
};
|
|
3112
|
-
}
|
|
3113
|
-
function assertSupportedWorktreeProfile(profile, harness) {
|
|
3114
|
-
const unsupportedAxes = [
|
|
3115
|
-
hasEntries(profile.tools) ? "tools" : null,
|
|
3116
|
-
hasEntries(profile.permissions) ? "permissions" : null,
|
|
3117
|
-
profile.connections && profile.connections.length > 0 ? "connections" : null,
|
|
3118
|
-
hasEntries(profile.confidential) ? "confidential" : null,
|
|
3119
|
-
hasEntries(profile.modes) ? "modes" : null,
|
|
3120
|
-
hasEntries(profile.extensions) ? "extensions" : null
|
|
3121
|
-
].filter((axis) => axis !== null);
|
|
3122
|
-
if (profile.model?.reasoningEffort !== void 0 && harness !== "codex") {
|
|
3123
|
-
unsupportedAxes.push("model.reasoningEffort");
|
|
3124
|
-
}
|
|
3125
|
-
for (const [name, server] of Object.entries(profile.mcp ?? {})) {
|
|
3126
|
-
const path = `mcp[${JSON.stringify(name)}]`;
|
|
3127
|
-
if (server.enabled === false && harness !== "opencode") {
|
|
3128
|
-
unsupportedAxes.push(`${path}.enabled`);
|
|
3129
|
-
}
|
|
3130
|
-
if (hasEntries(server.headers) && harness === "codex") {
|
|
3131
|
-
unsupportedAxes.push(`${path}.headers`);
|
|
3132
|
-
}
|
|
3133
|
-
if (server.cwd !== void 0 && harness === "opencode") {
|
|
3134
|
-
unsupportedAxes.push(`${path}.cwd`);
|
|
3135
|
-
}
|
|
3136
|
-
}
|
|
3137
|
-
if (harness === "claude") {
|
|
3138
|
-
for (const [event, commands] of Object.entries(profile.hooks ?? {})) {
|
|
3139
|
-
for (const [index, command] of commands.entries()) {
|
|
3140
|
-
const path = `hooks[${JSON.stringify(event)}][${index}]`;
|
|
3141
|
-
if (hasEntries(command.env)) unsupportedAxes.push(`${path}.env`);
|
|
3142
|
-
if (command.blocking !== void 0) unsupportedAxes.push(`${path}.blocking`);
|
|
3143
|
-
}
|
|
3144
|
-
}
|
|
3145
|
-
}
|
|
3146
|
-
for (const [name, subagent] of Object.entries(profile.subagents ?? {})) {
|
|
3147
|
-
const path = `subagents[${JSON.stringify(name)}]`;
|
|
3148
|
-
if (hasEntries(subagent.permissions)) unsupportedAxes.push(`${path}.permissions`);
|
|
3149
|
-
if (subagent.maxSteps !== void 0) unsupportedAxes.push(`${path}.maxSteps`);
|
|
3150
|
-
if (hasEntries(subagent.tools) && harness !== "claude") {
|
|
3151
|
-
unsupportedAxes.push(`${path}.tools`);
|
|
3152
|
-
}
|
|
3153
|
-
}
|
|
3154
|
-
if (unsupportedAxes.length > 0) {
|
|
3155
|
-
throw new Error(
|
|
3156
|
-
`runWorktreeHarness: profile requests unsupported worktree behavior: ${unsupportedAxes.join(", ")}`
|
|
3157
|
-
);
|
|
3158
|
-
}
|
|
3159
|
-
}
|
|
3160
|
-
function hasEntries(value) {
|
|
3161
|
-
return value !== void 0 && Object.keys(value).length > 0;
|
|
3162
|
-
}
|
|
3163
|
-
function assertSafeMaterializedPaths(plan) {
|
|
3164
|
-
for (const file of plan.files) {
|
|
3165
|
-
if (file.relPath.split("/").some(isGitMetadataSegment)) {
|
|
3166
|
-
throw new Error(
|
|
3167
|
-
`runWorktreeHarness: profile file cannot target reserved Git metadata: ${file.relPath}`
|
|
3168
|
-
);
|
|
3169
|
-
}
|
|
3170
|
-
}
|
|
3171
|
-
}
|
|
3172
|
-
function assertSafeProfileResourcePaths(profile) {
|
|
3173
|
-
for (const file of profile.resources?.files ?? []) {
|
|
3174
|
-
if (file.path.split("/").some(isGitMetadataSegment)) {
|
|
3175
|
-
throw new Error(
|
|
3176
|
-
`runWorktreeHarness: profile file cannot target reserved Git metadata: ${file.path}`
|
|
3177
|
-
);
|
|
3178
|
-
}
|
|
3179
|
-
}
|
|
3180
|
-
}
|
|
3181
|
-
function isGitMetadataSegment(segment) {
|
|
3182
|
-
const windowsCanonical = segment.replace(/[ .]+$/u, "").toLowerCase();
|
|
3183
|
-
return windowsCanonical === ".git" || windowsCanonical.startsWith(".git:");
|
|
3184
|
-
}
|
|
3185
|
-
function profileMaterializationReceipt(applied, resourceInstructions) {
|
|
3186
|
-
const instructionBytes = resourceInstructions === void 0 ? null : Buffer.from(resourceInstructions, "utf8");
|
|
3187
|
-
return {
|
|
3188
|
-
workspacePlanDigest: applied.workspacePlanDigest,
|
|
3189
|
-
writtenPaths: [...applied.written],
|
|
3190
|
-
unsupported: [...applied.unsupported],
|
|
3191
|
-
environmentNames: Object.keys(applied.env).sort(),
|
|
3192
|
-
flags: [...applied.flags],
|
|
3193
|
-
resourceInstructions: instructionBytes ? {
|
|
3194
|
-
delivery: "invocation-prompt",
|
|
3195
|
-
sha256: `sha256:${createHash2("sha256").update(instructionBytes).digest("hex")}`,
|
|
3196
|
-
byteLength: instructionBytes.byteLength
|
|
3197
|
-
} : { delivery: "none", sha256: null, byteLength: 0 }
|
|
3198
|
-
};
|
|
3199
|
-
}
|
|
3200
|
-
async function runWorktreeChecks(opts) {
|
|
3201
|
-
opts.signal?.throwIfAborted();
|
|
3202
|
-
if (opts.testCmd === void 0 && opts.typecheckCmd === void 0) return void 0;
|
|
3203
|
-
const runCommand = opts.runCommand ?? defaultRunCommand;
|
|
3204
|
-
const run = async (command) => {
|
|
3205
|
-
opts.signal?.throwIfAborted();
|
|
3206
|
-
const res = await runCommand({
|
|
3207
|
-
command,
|
|
3208
|
-
cwd: opts.worktreePath,
|
|
3209
|
-
timeoutMs: opts.timeoutMs,
|
|
3210
|
-
...opts.signal ? { signal: opts.signal } : {}
|
|
3211
|
-
});
|
|
3212
|
-
opts.signal?.throwIfAborted();
|
|
3213
|
-
return {
|
|
3214
|
-
command,
|
|
3215
|
-
passed: res.exitCode === 0,
|
|
3216
|
-
exitCode: res.exitCode,
|
|
3217
|
-
output: res.output.length > opts.cap ? res.output.slice(-opts.cap) : res.output
|
|
3218
|
-
};
|
|
3219
|
-
};
|
|
3220
|
-
const checks = {};
|
|
3221
|
-
if (opts.testCmd !== void 0) checks.tests = await run(opts.testCmd);
|
|
3222
|
-
opts.signal?.throwIfAborted();
|
|
3223
|
-
if (opts.typecheckCmd !== void 0) checks.typecheck = await run(opts.typecheckCmd);
|
|
3224
|
-
opts.signal?.throwIfAborted();
|
|
3225
|
-
return checks;
|
|
3226
|
-
}
|
|
3227
|
-
async function runSettledCommand(opts) {
|
|
3228
|
-
opts.signal?.throwIfAborted();
|
|
3229
|
-
return new Promise((resolve2, reject) => {
|
|
3230
|
-
const child = spawn3(opts.command, opts.args ?? [], {
|
|
3231
|
-
cwd: opts.cwd,
|
|
3232
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3233
|
-
detached: process.platform !== "win32"
|
|
3234
|
-
});
|
|
3235
|
-
const stdout = [];
|
|
3236
|
-
const stderr = [];
|
|
3237
|
-
let settled = false;
|
|
3238
|
-
let leaderClosed = false;
|
|
3239
|
-
let timedOut = false;
|
|
3240
|
-
let termination = null;
|
|
3241
|
-
const terminate = () => {
|
|
3242
|
-
termination ??= terminateProcessTreeAndConfirm(child, () => leaderClosed, "defaultRunCommand");
|
|
3243
|
-
return termination;
|
|
3244
|
-
};
|
|
3245
|
-
const timer = setTimeout(() => {
|
|
3246
|
-
timedOut = true;
|
|
3247
|
-
void terminate().catch(fail);
|
|
3248
|
-
}, opts.timeoutMs);
|
|
3249
|
-
const onAbort = () => void terminate().catch(fail);
|
|
3250
|
-
if (opts.signal) {
|
|
3251
|
-
if (opts.signal.aborted) onAbort();
|
|
3252
|
-
else opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
3253
|
-
}
|
|
3254
|
-
child.stdout?.on("data", (d) => stdout.push(String(d)));
|
|
3255
|
-
child.stderr?.on("data", (d) => stderr.push(String(d)));
|
|
3256
|
-
const finish = (fn) => {
|
|
3257
|
-
if (settled) return;
|
|
3258
|
-
settled = true;
|
|
3259
|
-
clearTimeout(timer);
|
|
3260
|
-
opts.signal?.removeEventListener("abort", onAbort);
|
|
3261
|
-
fn();
|
|
3262
|
-
};
|
|
3263
|
-
function fail(err) {
|
|
3264
|
-
finish(() => reject(err instanceof Error ? err : new Error(String(err))));
|
|
3265
|
-
}
|
|
3266
|
-
child.on("error", (err) => {
|
|
3267
|
-
leaderClosed = true;
|
|
3268
|
-
void (termination ?? Promise.resolve()).then(() => fail(err), fail);
|
|
3269
|
-
});
|
|
3270
|
-
child.on("close", (code, killedBySignal) => {
|
|
3271
|
-
leaderClosed = true;
|
|
3272
|
-
void (async () => {
|
|
3273
|
-
try {
|
|
3274
|
-
await (termination ?? terminate());
|
|
3275
|
-
opts.signal?.throwIfAborted();
|
|
3276
|
-
finish(
|
|
3277
|
-
() => resolve2({
|
|
3278
|
-
exitCode: code,
|
|
3279
|
-
stdout: stdout.join(""),
|
|
3280
|
-
stderr: stderr.join(""),
|
|
3281
|
-
killedBySignal,
|
|
3282
|
-
timedOut
|
|
3283
|
-
})
|
|
3284
|
-
);
|
|
3285
|
-
} catch (err) {
|
|
3286
|
-
fail(err);
|
|
3287
|
-
}
|
|
3288
|
-
})();
|
|
3289
|
-
});
|
|
3290
|
-
});
|
|
3291
|
-
}
|
|
3292
|
-
async function defaultRunCommand(opts) {
|
|
3293
|
-
const result = await runSettledCommand({
|
|
3294
|
-
command: "/bin/sh",
|
|
3295
|
-
args: ["-c", opts.command],
|
|
3296
|
-
cwd: opts.cwd,
|
|
3297
|
-
timeoutMs: opts.timeoutMs,
|
|
3298
|
-
...opts.signal ? { signal: opts.signal } : {}
|
|
3299
|
-
});
|
|
3300
|
-
return { exitCode: result.exitCode, output: result.stdout + result.stderr };
|
|
3301
|
-
}
|
|
3302
|
-
|
|
3303
|
-
// src/runtime/supervise/driver-executor.ts
|
|
3304
|
-
var driverRuntime = "driver";
|
|
3305
|
-
var driverRole = "driver";
|
|
3306
|
-
function isDriverSpec(spec) {
|
|
3307
|
-
const role = spec.profile.metadata?.role;
|
|
3308
|
-
if (role !== driverRole) return false;
|
|
3309
|
-
const driver = spec.driver;
|
|
3310
|
-
if (!isAgent(driver)) {
|
|
3311
|
-
throw new ValidationError(
|
|
3312
|
-
"driverExecutor: a driver-role spec must carry a `driver` Agent to run inside its nested scope"
|
|
3313
|
-
);
|
|
3314
|
-
}
|
|
3315
|
-
return true;
|
|
3316
|
-
}
|
|
3317
|
-
var driverExecutorFactory = (spec, ctx) => {
|
|
3318
|
-
if (!isDriverSpec(spec)) {
|
|
3319
|
-
throw new ValidationError(
|
|
3320
|
-
'driverExecutorFactory: spec is not a driver child (no role:"driver" marker)'
|
|
3321
|
-
);
|
|
3322
|
-
}
|
|
3323
|
-
const driver = spec.driver;
|
|
3324
|
-
const journal = spec.journal;
|
|
3325
|
-
const seam = readNestedScopeSeam(ctx);
|
|
3326
|
-
let artifact;
|
|
3327
|
-
let meteredSpend;
|
|
3328
|
-
return {
|
|
3329
|
-
runtime: driverRuntime,
|
|
3330
|
-
async execute(task, signal) {
|
|
3331
|
-
const nestedRoot = nestedTreeKey(seam, journal);
|
|
3332
|
-
await journal.beginTree(nestedRoot, (/* @__PURE__ */ new Date(0)).toISOString());
|
|
3333
|
-
const nestedScope = seam.mount(nestedRoot, signal);
|
|
3334
|
-
try {
|
|
3335
|
-
const out = await driver.act(task, nestedScope);
|
|
3336
|
-
const events = await loadTreeEvents(journal, nestedRoot);
|
|
3337
|
-
const settled = events.filter(isSettled);
|
|
3338
|
-
meteredSpend = nonZeroOrUndef(sumMetered(events));
|
|
3339
|
-
const verdict = deriveDeliveryVerdict(settled);
|
|
3340
|
-
artifact = {
|
|
3341
|
-
outRef: `${driverRuntime}:${nestedRoot}`,
|
|
3342
|
-
out,
|
|
3343
|
-
spent: sumSpend(settled),
|
|
3344
|
-
...verdict ? { verdict } : {}
|
|
3345
|
-
};
|
|
3346
|
-
return artifact;
|
|
3347
|
-
} catch (err) {
|
|
3348
|
-
meteredSpend = await safeSumMetered(journal, nestedRoot);
|
|
3349
|
-
throw err;
|
|
3350
|
-
}
|
|
3351
|
-
},
|
|
3352
|
-
metered() {
|
|
3353
|
-
return meteredSpend;
|
|
3354
|
-
},
|
|
3355
|
-
teardown() {
|
|
3356
|
-
return Promise.resolve({ destroyed: true });
|
|
3357
|
-
},
|
|
3358
|
-
resultArtifact() {
|
|
3359
|
-
if (!artifact) {
|
|
3360
|
-
throw new ValidationError("driverExecutor: resultArtifact() read before execute()");
|
|
3361
|
-
}
|
|
3362
|
-
return artifact;
|
|
3363
|
-
}
|
|
3364
|
-
};
|
|
3365
|
-
};
|
|
3366
|
-
function withDriverExecutor(base) {
|
|
3367
|
-
return {
|
|
3368
|
-
register: base.register.bind(base),
|
|
3369
|
-
resolve(spec) {
|
|
3370
|
-
const role = spec.profile.metadata?.role;
|
|
3371
|
-
if (role === driverRole && !spec.executor) {
|
|
3372
|
-
return { succeeded: true, value: driverExecutorFactory };
|
|
3373
|
-
}
|
|
3374
|
-
return base.resolve(spec);
|
|
3375
|
-
}
|
|
3376
|
-
};
|
|
3377
|
-
}
|
|
3378
|
-
function nestedTreeKey(seam, journal) {
|
|
3379
|
-
return `${seam.journalRoot}/d${nextNestOrdinal(journal)}`;
|
|
3380
|
-
}
|
|
3381
|
-
var nestCounters = /* @__PURE__ */ new WeakMap();
|
|
3382
|
-
function nextNestOrdinal(journal) {
|
|
3383
|
-
let c = nestCounters.get(journal);
|
|
3384
|
-
if (!c) {
|
|
3385
|
-
c = { n: 0 };
|
|
3386
|
-
nestCounters.set(journal, c);
|
|
3387
|
-
}
|
|
3388
|
-
return c.n++;
|
|
3389
|
-
}
|
|
3390
|
-
async function loadTreeEvents(journal, nestedRoot) {
|
|
3391
|
-
const events = await journal.loadTree(nestedRoot);
|
|
3392
|
-
if (events === void 0) {
|
|
3393
|
-
throw new ValidationError(
|
|
3394
|
-
`driverExecutor: nested tree '${nestedRoot}' missing from the journal after run (corrupted log)`
|
|
3395
|
-
);
|
|
3396
|
-
}
|
|
3397
|
-
return events;
|
|
3398
|
-
}
|
|
3399
|
-
function isSettled(ev) {
|
|
3400
|
-
return ev.kind === "settled";
|
|
3401
|
-
}
|
|
3402
|
-
function sumSpend(settled) {
|
|
3403
|
-
const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
3404
|
-
for (const ev of settled) {
|
|
3405
|
-
total.iterations += ev.spent.iterations;
|
|
3406
|
-
total.tokens.input += ev.spent.tokens.input;
|
|
3407
|
-
total.tokens.output += ev.spent.tokens.output;
|
|
3408
|
-
total.usd += ev.spent.usd;
|
|
3409
|
-
if (ev.spent.usdKnown === false) total.usdKnown = false;
|
|
3410
|
-
total.ms += ev.spent.ms;
|
|
3411
|
-
}
|
|
3412
|
-
return total;
|
|
3413
|
-
}
|
|
3414
|
-
function sumMetered(events) {
|
|
3415
|
-
const total = { iterations: 0, tokens: { input: 0, output: 0 }, usd: 0, ms: 0 };
|
|
3416
|
-
for (const ev of events) {
|
|
3417
|
-
if (ev.kind !== "metered") continue;
|
|
3418
|
-
total.iterations += ev.spend.iterations;
|
|
3419
|
-
total.tokens.input += ev.spend.tokens.input;
|
|
3420
|
-
total.tokens.output += ev.spend.tokens.output;
|
|
3421
|
-
total.usd += ev.spend.usd;
|
|
3422
|
-
if (ev.spend.usdKnown === false) total.usdKnown = false;
|
|
3423
|
-
total.ms += ev.spend.ms;
|
|
3424
|
-
}
|
|
3425
|
-
return total;
|
|
3426
|
-
}
|
|
3427
|
-
function isNonZeroSpend(s) {
|
|
3428
|
-
return s.iterations > 0 || s.tokens.input > 0 || s.tokens.output > 0 || s.usd > 0 || s.ms > 0;
|
|
3429
|
-
}
|
|
3430
|
-
function nonZeroOrUndef(s) {
|
|
3431
|
-
return isNonZeroSpend(s) ? s : void 0;
|
|
3432
|
-
}
|
|
3433
|
-
async function safeSumMetered(journal, nestedRoot) {
|
|
3434
|
-
try {
|
|
3435
|
-
return nonZeroOrUndef(sumMetered(await loadTreeEvents(journal, nestedRoot)));
|
|
3436
|
-
} catch {
|
|
3437
|
-
return void 0;
|
|
3438
|
-
}
|
|
3439
|
-
}
|
|
3440
|
-
function deriveDeliveryVerdict(settled) {
|
|
3441
|
-
let sawChild = false;
|
|
3442
|
-
let anyValid = false;
|
|
3443
|
-
let bestValidScore;
|
|
3444
|
-
let bestDoneScore;
|
|
3445
|
-
for (const ev of settled) {
|
|
3446
|
-
sawChild = true;
|
|
3447
|
-
if (ev.status !== "done") continue;
|
|
3448
|
-
const score = ev.verdict?.score;
|
|
3449
|
-
if (score !== void 0 && (bestDoneScore === void 0 || score > bestDoneScore)) {
|
|
3450
|
-
bestDoneScore = score;
|
|
3451
|
-
}
|
|
3452
|
-
if (ev.verdict?.valid === true) {
|
|
3453
|
-
anyValid = true;
|
|
3454
|
-
if (score !== void 0 && (bestValidScore === void 0 || score > bestValidScore)) {
|
|
3455
|
-
bestValidScore = score;
|
|
3456
|
-
}
|
|
3457
|
-
}
|
|
3458
|
-
}
|
|
3459
|
-
if (!sawChild) return void 0;
|
|
3460
|
-
return {
|
|
3461
|
-
valid: anyValid,
|
|
3462
|
-
score: anyValid ? bestValidScore ?? 1 : bestDoneScore ?? 0
|
|
3463
|
-
};
|
|
3464
|
-
}
|
|
3465
|
-
function readNestedScopeSeam(ctx) {
|
|
3466
|
-
const seam = ctx.seams[nestedScopeSeamKey];
|
|
3467
|
-
if (!seam || typeof seam.mount !== "function") {
|
|
3468
|
-
throw new ValidationError(
|
|
3469
|
-
`driverExecutor: missing required seam "${nestedScopeSeamKey}" \u2014 a driver child must be spawned through a Scope that seeds it (the keystone scope does)`
|
|
3470
|
-
);
|
|
3471
|
-
}
|
|
3472
|
-
return seam;
|
|
3473
|
-
}
|
|
3474
|
-
function isAgent(value) {
|
|
3475
|
-
return typeof value === "object" && value !== null && typeof value.act === "function" && typeof value.name === "string";
|
|
3476
|
-
}
|
|
3477
|
-
|
|
3478
|
-
export {
|
|
3479
|
-
CodexExecutionDiagnosticError,
|
|
3480
|
-
harnessInvocation,
|
|
3481
|
-
runLocalHarness,
|
|
3482
|
-
parseCodexTokenUsage,
|
|
3483
|
-
createWorktree,
|
|
3484
|
-
captureWorktreeDiff,
|
|
3485
|
-
removeWorktree,
|
|
3486
|
-
runWorktreeHarness,
|
|
3487
|
-
runWorktreeChecks,
|
|
3488
|
-
runSettledCommand,
|
|
3489
|
-
runBrainLoop,
|
|
3490
|
-
routerChatWithUsage,
|
|
3491
|
-
routerChatWithTools,
|
|
3492
|
-
routerToolLoop,
|
|
3493
|
-
routerBrain,
|
|
3494
|
-
defineRuntimeHooks,
|
|
3495
|
-
composeRuntimeHooks,
|
|
3496
|
-
notifyRuntimeHookEvent,
|
|
3497
|
-
notifyRuntimeDecisionPoint,
|
|
3498
|
-
DEFAULT_STALL_AFTER_MS,
|
|
3499
|
-
createActivityLog,
|
|
3500
|
-
readWorkerProgress,
|
|
3501
|
-
timerAt,
|
|
3502
|
-
pollFor,
|
|
3503
|
-
createWaitProbes,
|
|
3504
|
-
isWaitOutcome,
|
|
3505
|
-
waitUntil,
|
|
3506
|
-
validateWaitSpec,
|
|
3507
|
-
createScope,
|
|
3508
|
-
settledToIteration,
|
|
3509
|
-
withDriverExecutor,
|
|
3510
|
-
spendFromUsageEvents,
|
|
3511
|
-
createBudgetPool,
|
|
3512
|
-
createSupervisor
|
|
3513
|
-
};
|
|
3514
|
-
//# sourceMappingURL=chunk-HLKC4UYB.js.map
|