@tangle-network/agent-runtime 0.102.0 → 0.102.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +5 -3
- package/dist/agent.js.map +1 -1
- package/dist/analyst-loop.d.ts +1 -1
- package/dist/candidate-execution/index.js +5 -4
- package/dist/{chunk-SPCD4IQQ.js → chunk-377GOJHJ.js} +2 -2
- package/dist/{chunk-LKKIM5QD.js → chunk-3DW5DEKQ.js} +3 -3
- package/dist/{chunk-4AMAFNUD.js → chunk-7FJJUC73.js} +311 -40
- package/dist/chunk-7FJJUC73.js.map +1 -0
- package/dist/{chunk-DA7YL6I4.js → chunk-CK76OZPM.js} +3 -3
- package/dist/{chunk-KSBUSEUA.js → chunk-EKUMRQNR.js} +2 -2
- package/dist/{chunk-23Z7N5IG.js → chunk-J6V6DWWS.js} +5 -5
- package/dist/{chunk-AIBIRAJ6.js → chunk-KSM6GFHP.js} +2 -2
- package/dist/chunk-L5DST3QC.js +351 -0
- package/dist/chunk-L5DST3QC.js.map +1 -0
- package/dist/{chunk-TGDHHHH4.js → chunk-LJLG734E.js} +12 -253
- package/dist/chunk-LJLG734E.js.map +1 -0
- package/dist/{chunk-MAONWP4R.js → chunk-QCXD7NHM.js} +611 -1419
- package/dist/chunk-QCXD7NHM.js.map +1 -0
- package/dist/chunk-ROILMKDL.js +1338 -0
- package/dist/chunk-ROILMKDL.js.map +1 -0
- package/dist/{chunk-N2CEE5Q3.js → chunk-SCOHHMKU.js} +3 -3
- package/dist/chunk-SQ3ZJH3Y.js +3082 -0
- package/dist/chunk-SQ3ZJH3Y.js.map +1 -0
- package/dist/chunk-UPWGXKXB.js +254 -0
- package/dist/chunk-UPWGXKXB.js.map +1 -0
- package/dist/{chunk-KVJQV342.js → chunk-ZUCUAFVF.js} +26 -1778
- package/dist/chunk-ZUCUAFVF.js.map +1 -0
- package/dist/{completion-gate-C80jiRfN.d.ts → completion-gate-C6dhVN-N.d.ts} +2 -2
- package/dist/{coordination-BFE3Den7.d.ts → coordination-D9l_YmwW.d.ts} +6 -6
- package/dist/environment-provider.d.ts +2 -2
- package/dist/{improve-CIml6hT3.d.ts → improve-B40dRu0X.d.ts} +40 -19
- package/dist/index.d.ts +329 -21
- package/dist/index.js +541 -137
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +5 -5
- package/dist/intelligence.js +7 -5
- package/dist/intelligence.js.map +1 -1
- package/dist/knowledge.d.ts +8 -8
- package/dist/knowledge.js +9 -7
- package/dist/{local-harness-ZqCx51u7.d.ts → local-harness-CtK9dSny.d.ts} +14 -2
- package/dist/{loop-runner-bin-Cn1N2rRo.d.ts → loop-runner-bin-PnWslIw4.d.ts} +2 -2
- package/dist/loop-runner-bin.d.ts +6 -6
- package/dist/loop-runner-bin.js +6 -4
- package/dist/loops.d.ts +231 -460
- package/dist/loops.js +58 -37
- package/dist/mcp/bin.js +2 -2
- package/dist/mcp/index.d.ts +184 -12
- package/dist/mcp/index.js +31 -9
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/memory-bin.d.ts +1 -0
- package/dist/mcp/memory-bin.js +22 -0
- package/dist/mcp/memory-bin.js.map +1 -0
- package/dist/profiles.d.ts +3 -3
- package/dist/structural-rollout-D7xik9XR.d.ts +446 -0
- package/dist/{substrate-DO2GHNg2.d.ts → substrate-Dyiqmd3o.d.ts} +1 -1
- package/dist/{supervise-BLPI50-w.d.ts → supervise-BQdNAXtK.d.ts} +4 -4
- package/dist/testing.js +10 -8
- package/dist/testing.js.map +1 -1
- package/dist/{types-B3vAW0Oq.d.ts → types-CLat4bT9.d.ts} +1 -1
- package/dist/{types-CmnA2iL3.d.ts → types-D870JlH0.d.ts} +2 -2
- package/dist/{worktree-fanout-DCA3G4bO.d.ts → worktree-fanout-DsIR4d6w.d.ts} +234 -234
- package/package.json +4 -3
- package/dist/chunk-4AMAFNUD.js.map +0 -1
- package/dist/chunk-IKGCKPDX.js +0 -1115
- package/dist/chunk-IKGCKPDX.js.map +0 -1
- package/dist/chunk-KVJQV342.js.map +0 -1
- package/dist/chunk-MAONWP4R.js.map +0 -1
- package/dist/chunk-TGDHHHH4.js.map +0 -1
- package/dist/{chunk-SPCD4IQQ.js.map → chunk-377GOJHJ.js.map} +0 -0
- package/dist/{chunk-LKKIM5QD.js.map → chunk-3DW5DEKQ.js.map} +0 -0
- package/dist/{chunk-DA7YL6I4.js.map → chunk-CK76OZPM.js.map} +0 -0
- package/dist/{chunk-KSBUSEUA.js.map → chunk-EKUMRQNR.js.map} +0 -0
- package/dist/{chunk-23Z7N5IG.js.map → chunk-J6V6DWWS.js.map} +0 -0
- package/dist/{chunk-AIBIRAJ6.js.map → chunk-KSM6GFHP.js.map} +0 -0
- package/dist/{chunk-N2CEE5Q3.js.map → chunk-SCOHHMKU.js.map} +0 -0
- package/dist/{sanitize-BTSsdBXw.d.ts → sanitize-DEbPNtyI.d.ts} +58 -58
package/dist/chunk-IKGCKPDX.js
DELETED
|
@@ -1,1115 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ConfigError
|
|
3
|
-
} from "./chunk-YEJR7IXO.js";
|
|
4
|
-
|
|
5
|
-
// src/mcp/codex-diagnostics.ts
|
|
6
|
-
import { constants } from "fs";
|
|
7
|
-
import { open } from "fs/promises";
|
|
8
|
-
import { stripVTControlCharacters } from "util";
|
|
9
|
-
var CodexExecutionDiagnosticError = class extends Error {
|
|
10
|
-
constructor(reason, diagnostic, cause) {
|
|
11
|
-
super(
|
|
12
|
-
`runLocalHarness: reproducible Codex execution failed: ${reason}; diagnostic=${JSON.stringify(diagnostic)}`,
|
|
13
|
-
{ cause }
|
|
14
|
-
);
|
|
15
|
-
this.reason = reason;
|
|
16
|
-
this.diagnostic = diagnostic;
|
|
17
|
-
this.name = "CodexExecutionDiagnosticError";
|
|
18
|
-
}
|
|
19
|
-
reason;
|
|
20
|
-
diagnostic;
|
|
21
|
-
code = "CODEX_EXECUTION_DIAGNOSTIC";
|
|
22
|
-
};
|
|
23
|
-
var CODEX_FAILURE_STREAM_MAX_CHARS = 4096;
|
|
24
|
-
var CODEX_AUTH_MAX_BYTES = 1024 * 1024;
|
|
25
|
-
var CODEX_REDACTED = "<REDACTED_CREDENTIAL>";
|
|
26
|
-
var codexSensitiveEnvironmentName = /(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|AUTH|COOKIE)/i;
|
|
27
|
-
function createCodexExecutionDiagnosticError(opts) {
|
|
28
|
-
const redact = (value) => redactCodexDiagnostic(value, opts.codexHome, opts.redactionValues);
|
|
29
|
-
const boundedStdout = boundCodexDiagnostic(redact(opts.stdout));
|
|
30
|
-
const boundedStderr = boundCodexDiagnostic(redact(opts.stderr));
|
|
31
|
-
const diagnostic = {
|
|
32
|
-
exitCode: opts.exitCode,
|
|
33
|
-
killedBySignal: opts.killedBySignal,
|
|
34
|
-
timedOut: opts.timedOut,
|
|
35
|
-
durationMs: opts.durationMs,
|
|
36
|
-
stdout: boundedStdout.value,
|
|
37
|
-
stderr: boundedStderr.value,
|
|
38
|
-
stdoutTruncated: boundedStdout.truncated,
|
|
39
|
-
stderrTruncated: boundedStderr.truncated
|
|
40
|
-
};
|
|
41
|
-
const safeReason = boundCodexDiagnostic(redact(opts.reason), 1024).value;
|
|
42
|
-
return new CodexExecutionDiagnosticError(
|
|
43
|
-
safeReason,
|
|
44
|
-
diagnostic,
|
|
45
|
-
redactCodexErrorCause(opts.cause, safeReason, redact)
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
function redactCodexHome(value, codexHome) {
|
|
49
|
-
return codexHome ? value.replaceAll(codexHome, "<ISOLATED_CODEX_HOME>") : value;
|
|
50
|
-
}
|
|
51
|
-
function collectCodexDiagnosticRedactionValues(env, authText) {
|
|
52
|
-
const values = Object.entries(env).filter(
|
|
53
|
-
([name, value]) => codexSensitiveEnvironmentName.test(name) && typeof value === "string"
|
|
54
|
-
).map(([, value]) => value);
|
|
55
|
-
try {
|
|
56
|
-
collectStringLeaves(JSON.parse(authText), values);
|
|
57
|
-
} catch {
|
|
58
|
-
}
|
|
59
|
-
const encoded = values.filter((value) => value.length >= 8).flatMap((value) => [
|
|
60
|
-
Buffer.from(value, "utf8").toString("base64"),
|
|
61
|
-
Buffer.from(value, "utf8").toString("base64url"),
|
|
62
|
-
encodeURIComponent(value),
|
|
63
|
-
JSON.stringify(value).slice(1, -1)
|
|
64
|
-
]);
|
|
65
|
-
return [.../* @__PURE__ */ new Set([...values, ...encoded])];
|
|
66
|
-
}
|
|
67
|
-
async function readCodexAuthRedactionText(path) {
|
|
68
|
-
const handle = await open(path, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
69
|
-
try {
|
|
70
|
-
const stats = await handle.stat();
|
|
71
|
-
if (!stats.isFile() || !Number.isSafeInteger(stats.size) || stats.size > CODEX_AUTH_MAX_BYTES) {
|
|
72
|
-
throw new Error("Codex auth.json must be a regular file no larger than 1 MiB");
|
|
73
|
-
}
|
|
74
|
-
const buffer = Buffer.alloc(stats.size);
|
|
75
|
-
let offset = 0;
|
|
76
|
-
while (offset < buffer.length) {
|
|
77
|
-
const { bytesRead } = await handle.read(buffer, offset, buffer.length - offset, offset);
|
|
78
|
-
if (bytesRead === 0) break;
|
|
79
|
-
offset += bytesRead;
|
|
80
|
-
}
|
|
81
|
-
return buffer.subarray(0, offset).toString("utf8");
|
|
82
|
-
} finally {
|
|
83
|
-
await handle.close();
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
function boundCodexDiagnostic(value, maxChars = CODEX_FAILURE_STREAM_MAX_CHARS) {
|
|
87
|
-
if (value.length <= maxChars) return { value, truncated: false };
|
|
88
|
-
const marker = "\n<... OUTPUT TRUNCATED ...>\n";
|
|
89
|
-
const contentChars = maxChars - marker.length;
|
|
90
|
-
const headChars = Math.floor(contentChars / 2);
|
|
91
|
-
const tailChars = contentChars - headChars;
|
|
92
|
-
return {
|
|
93
|
-
value: `${value.slice(0, headChars)}${marker}${value.slice(-tailChars)}`,
|
|
94
|
-
truncated: true
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
function redactCodexDiagnostic(value, codexHome, protectedValues) {
|
|
98
|
-
let redacted = redactCodexHome(stripVTControlCharacters(value), codexHome);
|
|
99
|
-
const exactValues = [...new Set(protectedValues.filter((item) => item.length >= 6))].sort(
|
|
100
|
-
(a, b) => b.length - a.length
|
|
101
|
-
);
|
|
102
|
-
for (const secret of exactValues) redacted = redacted.replaceAll(secret, CODEX_REDACTED);
|
|
103
|
-
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(
|
|
104
|
-
/((?:["']?(?: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,
|
|
105
|
-
`$1$2${CODEX_REDACTED}$2`
|
|
106
|
-
).replace(
|
|
107
|
-
/((?:["']?(?: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,
|
|
108
|
-
`$1${CODEX_REDACTED}`
|
|
109
|
-
);
|
|
110
|
-
for (const secret of exactValues) redacted = redacted.replaceAll(secret, CODEX_REDACTED);
|
|
111
|
-
return redacted;
|
|
112
|
-
}
|
|
113
|
-
function redactCodexErrorCause(cause, safeReason, redact) {
|
|
114
|
-
if (!(cause instanceof Error)) return void 0;
|
|
115
|
-
const safeCause = new Error(safeReason);
|
|
116
|
-
safeCause.name = cause.name;
|
|
117
|
-
if (cause.stack) safeCause.stack = boundCodexDiagnostic(redact(cause.stack)).value;
|
|
118
|
-
return safeCause;
|
|
119
|
-
}
|
|
120
|
-
function collectStringLeaves(value, output) {
|
|
121
|
-
if (typeof value === "string") {
|
|
122
|
-
output.push(value);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (Array.isArray(value)) {
|
|
126
|
-
for (const item of value) collectStringLeaves(item, output);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
if (value !== null && typeof value === "object") {
|
|
130
|
-
for (const item of Object.values(value)) collectStringLeaves(item, output);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// src/mcp/local-harness.ts
|
|
135
|
-
import { spawn } from "child_process";
|
|
136
|
-
import { createHash, randomUUID } from "crypto";
|
|
137
|
-
import { constants as constants2, createReadStream } from "fs";
|
|
138
|
-
import {
|
|
139
|
-
access,
|
|
140
|
-
chmod,
|
|
141
|
-
copyFile,
|
|
142
|
-
mkdir,
|
|
143
|
-
mkdtemp,
|
|
144
|
-
open as open2,
|
|
145
|
-
readFile,
|
|
146
|
-
realpath,
|
|
147
|
-
rm,
|
|
148
|
-
symlink,
|
|
149
|
-
writeFile
|
|
150
|
-
} from "fs/promises";
|
|
151
|
-
import { homedir, tmpdir } from "os";
|
|
152
|
-
import { basename, delimiter, dirname, isAbsolute, join, resolve, sep } from "path";
|
|
153
|
-
var codexReasoningEffort = {
|
|
154
|
-
none: "none",
|
|
155
|
-
minimal: "minimal",
|
|
156
|
-
low: "low",
|
|
157
|
-
medium: "medium",
|
|
158
|
-
high: "high",
|
|
159
|
-
xhigh: "xhigh",
|
|
160
|
-
ultracode: "xhigh"
|
|
161
|
-
};
|
|
162
|
-
function codexReasoningArgs(reasoningEffort) {
|
|
163
|
-
const mapped = codexReasoningEffort[reasoningEffort];
|
|
164
|
-
if (mapped === void 0) {
|
|
165
|
-
throw new Error(
|
|
166
|
-
`harnessInvocation: unsupported Codex reasoning effort ${String(reasoningEffort)}`
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
return ["-c", `model_reasoning_effort="${mapped}"`];
|
|
170
|
-
}
|
|
171
|
-
var HARNESS_INVOCATIONS = {
|
|
172
|
-
claude: {
|
|
173
|
-
command: "claude",
|
|
174
|
-
// `-p` IS headless/print mode; the old `--headless` flag was removed from the CLI.
|
|
175
|
-
// Permission bypass is an explicit per-run opt-in below, never the public default.
|
|
176
|
-
buildArgs: (taskPrompt) => ["-p", taskPrompt],
|
|
177
|
-
modelArgs: (model) => ["-m", model]
|
|
178
|
-
},
|
|
179
|
-
codex: {
|
|
180
|
-
command: "codex",
|
|
181
|
-
buildArgs: (taskPrompt) => ["exec", taskPrompt],
|
|
182
|
-
modelArgs: (model) => ["-m", model],
|
|
183
|
-
reasoningArgs: codexReasoningArgs
|
|
184
|
-
},
|
|
185
|
-
opencode: {
|
|
186
|
-
command: "opencode",
|
|
187
|
-
buildArgs: (taskPrompt) => ["run", taskPrompt],
|
|
188
|
-
modelArgs: (model) => ["-m", model]
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
var CODEX_REPRODUCIBLE_ARGS = [
|
|
192
|
-
"--ephemeral",
|
|
193
|
-
"--ignore-rules",
|
|
194
|
-
"--json",
|
|
195
|
-
"-c",
|
|
196
|
-
'approval_policy="never"',
|
|
197
|
-
"-c",
|
|
198
|
-
'web_search="disabled"',
|
|
199
|
-
"-c",
|
|
200
|
-
"project_doc_max_bytes=0",
|
|
201
|
-
"-c",
|
|
202
|
-
"skills.include_instructions=false",
|
|
203
|
-
"-c",
|
|
204
|
-
"include_apps_instructions=false",
|
|
205
|
-
"--disable",
|
|
206
|
-
"tool_suggest",
|
|
207
|
-
"-c",
|
|
208
|
-
'features.multi_agent_v2.root_agent_usage_hint_text=""',
|
|
209
|
-
"-c",
|
|
210
|
-
'features.multi_agent_v2.subagent_usage_hint_text=""',
|
|
211
|
-
"-c",
|
|
212
|
-
'features.multi_agent_v2.multi_agent_mode_hint_text=""',
|
|
213
|
-
"--strict-config"
|
|
214
|
-
];
|
|
215
|
-
function buildHarnessArgs(harness, taskPrompt, options = {}) {
|
|
216
|
-
const args = HARNESS_INVOCATIONS[harness].buildArgs(taskPrompt);
|
|
217
|
-
if (harness === "claude" && options.dangerouslySkipPermissions) {
|
|
218
|
-
args.push("--dangerously-skip-permissions");
|
|
219
|
-
}
|
|
220
|
-
if (options.codexReproducible) {
|
|
221
|
-
if (harness !== "codex") {
|
|
222
|
-
throw new Error("harnessInvocation: codexReproducible requires the Codex harness");
|
|
223
|
-
}
|
|
224
|
-
args.push(...CODEX_REPRODUCIBLE_ARGS);
|
|
225
|
-
}
|
|
226
|
-
return args;
|
|
227
|
-
}
|
|
228
|
-
function harnessInvocation(harness, profile, taskPrompt, options = {}) {
|
|
229
|
-
const invocation = HARNESS_INVOCATIONS[harness];
|
|
230
|
-
if (!invocation) {
|
|
231
|
-
throw new Error(`harnessInvocation: unknown harness ${String(harness)}`);
|
|
232
|
-
}
|
|
233
|
-
if (options.codexReproducible) {
|
|
234
|
-
const model2 = profile.model?.default;
|
|
235
|
-
if (typeof model2 !== "string" || model2.trim().length === 0) {
|
|
236
|
-
throw new Error("harnessInvocation: codexReproducible requires profile.model.default");
|
|
237
|
-
}
|
|
238
|
-
if (profile.model?.reasoningEffort === void 0) {
|
|
239
|
-
throw new Error("harnessInvocation: codexReproducible requires profile.model.reasoningEffort");
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
const systemPrompt = profile.prompt?.systemPrompt;
|
|
243
|
-
const instructions = profile.prompt?.instructions;
|
|
244
|
-
if (instructions !== void 0 && (!Array.isArray(instructions) || instructions.some((instruction) => typeof instruction !== "string"))) {
|
|
245
|
-
throw new Error("harnessInvocation: profile.prompt.instructions must be an array of strings");
|
|
246
|
-
}
|
|
247
|
-
const promptSections = [
|
|
248
|
-
...typeof systemPrompt === "string" && systemPrompt.trim().length > 0 ? [systemPrompt] : [],
|
|
249
|
-
...instructions?.filter((instruction) => instruction.trim().length > 0) ?? [],
|
|
250
|
-
taskPrompt
|
|
251
|
-
];
|
|
252
|
-
const composedPrompt = promptSections.join("\n\n");
|
|
253
|
-
const args = buildHarnessArgs(harness, composedPrompt, options);
|
|
254
|
-
const model = profile.model?.default;
|
|
255
|
-
if (typeof model === "string" && model.length > 0) {
|
|
256
|
-
args.push(...invocation.modelArgs(model));
|
|
257
|
-
}
|
|
258
|
-
const reasoningEffort = profile.model?.reasoningEffort;
|
|
259
|
-
if (reasoningEffort !== void 0 && invocation.reasoningArgs) {
|
|
260
|
-
args.push(...invocation.reasoningArgs(reasoningEffort));
|
|
261
|
-
}
|
|
262
|
-
return { command: invocation.command, args, prompt: composedPrompt };
|
|
263
|
-
}
|
|
264
|
-
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
265
|
-
var processKillGraceMs = 250;
|
|
266
|
-
async function runLocalHarness(options) {
|
|
267
|
-
const { harness, cwd, taskPrompt } = options;
|
|
268
|
-
if (options.codexReproducible && harness !== "codex") {
|
|
269
|
-
throw new Error("runLocalHarness: codexReproducible requires the Codex harness");
|
|
270
|
-
}
|
|
271
|
-
if (options.codexReproducible && process.platform !== "linux") {
|
|
272
|
-
throw new Error("runLocalHarness: codexReproducible currently requires Linux");
|
|
273
|
-
}
|
|
274
|
-
if (options.codexReadDeniedPaths !== void 0 && !options.codexReproducible) {
|
|
275
|
-
throw new Error("runLocalHarness: codexReadDeniedPaths requires codexReproducible");
|
|
276
|
-
}
|
|
277
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
278
|
-
const spawnImpl = options.spawn ?? spawn;
|
|
279
|
-
const invocation = HARNESS_INVOCATIONS[harness];
|
|
280
|
-
if (!invocation) {
|
|
281
|
-
throw new Error(`runLocalHarness: unknown harness ${String(harness)}`);
|
|
282
|
-
}
|
|
283
|
-
const startedAt = Date.now();
|
|
284
|
-
const requestedCommand = options.invocation?.command ?? invocation.command;
|
|
285
|
-
const args = options.invocation ? [...options.invocation.args] : buildHarnessArgs(harness, taskPrompt, options);
|
|
286
|
-
if (options.codexReproducible) assertCodexReproducibleInvocation(requestedCommand, args);
|
|
287
|
-
const baseEnv = { ...options.env ?? process.env, PWD: cwd };
|
|
288
|
-
const isolated = options.codexReproducible ? await isolateCodexHome({
|
|
289
|
-
baseEnv,
|
|
290
|
-
cwd,
|
|
291
|
-
command: requestedCommand,
|
|
292
|
-
readDeniedPaths: options.codexReadDeniedPaths ?? [],
|
|
293
|
-
resolveExecutable: options.resolveCodexExecutable ?? resolveCodexNativeExecutable
|
|
294
|
-
}) : {
|
|
295
|
-
env: baseEnv,
|
|
296
|
-
command: requestedCommand,
|
|
297
|
-
cleanup: async () => void 0,
|
|
298
|
-
controlledConfigSha256: "",
|
|
299
|
-
executableSha256: "",
|
|
300
|
-
readDeniedPathsSha256: "",
|
|
301
|
-
readDeniedPathCount: 0,
|
|
302
|
-
readDeniedPaths: [],
|
|
303
|
-
diagnosticRedactionValues: [],
|
|
304
|
-
ambientAuth: "",
|
|
305
|
-
isolatedAuth: "",
|
|
306
|
-
writeProbe: "",
|
|
307
|
-
stagedWriteProbe: "",
|
|
308
|
-
deniedProbePaths: []
|
|
309
|
-
};
|
|
310
|
-
const env = isolated.env;
|
|
311
|
-
const command = isolated.command;
|
|
312
|
-
if (options.codexReproducible) assertCodexReproducibleInvocation(command, args);
|
|
313
|
-
try {
|
|
314
|
-
const evidence = options.codexReproducible ? await collectCodexExecutionEvidence({
|
|
315
|
-
command,
|
|
316
|
-
args,
|
|
317
|
-
cwd,
|
|
318
|
-
env,
|
|
319
|
-
spawn: spawnImpl,
|
|
320
|
-
controlledConfigSha256: isolated.controlledConfigSha256,
|
|
321
|
-
executableSha256: isolated.executableSha256,
|
|
322
|
-
readDeniedPathsSha256: isolated.readDeniedPathsSha256,
|
|
323
|
-
readDeniedPathCount: isolated.readDeniedPathCount,
|
|
324
|
-
readDeniedPaths: isolated.readDeniedPaths,
|
|
325
|
-
ambientAuth: isolated.ambientAuth,
|
|
326
|
-
isolatedAuth: isolated.isolatedAuth,
|
|
327
|
-
writeProbe: isolated.writeProbe,
|
|
328
|
-
stagedWriteProbe: isolated.stagedWriteProbe,
|
|
329
|
-
deniedProbePaths: isolated.deniedProbePaths,
|
|
330
|
-
...options.signal ? { signal: options.signal } : {}
|
|
331
|
-
}) : void 0;
|
|
332
|
-
return await new Promise((resolve2, reject) => {
|
|
333
|
-
let child;
|
|
334
|
-
try {
|
|
335
|
-
child = spawnImpl(command, args, {
|
|
336
|
-
cwd,
|
|
337
|
-
env,
|
|
338
|
-
stdio: "pipe",
|
|
339
|
-
detached: process.platform !== "win32"
|
|
340
|
-
});
|
|
341
|
-
} catch (err) {
|
|
342
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
child.stdin?.end();
|
|
346
|
-
let stdout = "";
|
|
347
|
-
let stderr = "";
|
|
348
|
-
let timedOut = false;
|
|
349
|
-
let settled = false;
|
|
350
|
-
let forceKillTimer = null;
|
|
351
|
-
let terminationStarted = false;
|
|
352
|
-
const terminate = () => {
|
|
353
|
-
if (terminationStarted) return;
|
|
354
|
-
terminationStarted = true;
|
|
355
|
-
signalProcessTree(child, "SIGTERM");
|
|
356
|
-
forceKillTimer = setTimeout(() => signalProcessTree(child, "SIGKILL"), processKillGraceMs);
|
|
357
|
-
forceKillTimer.unref?.();
|
|
358
|
-
};
|
|
359
|
-
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
360
|
-
timedOut = true;
|
|
361
|
-
terminate();
|
|
362
|
-
}, timeoutMs) : null;
|
|
363
|
-
if (timer && typeof timer.unref === "function") {
|
|
364
|
-
;
|
|
365
|
-
timer.unref();
|
|
366
|
-
}
|
|
367
|
-
const onAbort = () => {
|
|
368
|
-
terminate();
|
|
369
|
-
};
|
|
370
|
-
if (options.signal) {
|
|
371
|
-
if (options.signal.aborted) onAbort();
|
|
372
|
-
else options.signal.addEventListener("abort", onAbort, { once: true });
|
|
373
|
-
}
|
|
374
|
-
child.stdout?.on("data", (chunk) => {
|
|
375
|
-
stdout += String(chunk);
|
|
376
|
-
});
|
|
377
|
-
child.stderr?.on("data", (chunk) => {
|
|
378
|
-
stderr += String(chunk);
|
|
379
|
-
});
|
|
380
|
-
const finalize = (result) => {
|
|
381
|
-
if (settled) return;
|
|
382
|
-
settled = true;
|
|
383
|
-
if (timer) clearTimeout(timer);
|
|
384
|
-
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
385
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
386
|
-
resolve2(result);
|
|
387
|
-
};
|
|
388
|
-
child.on("error", (err) => {
|
|
389
|
-
if (settled) return;
|
|
390
|
-
settled = true;
|
|
391
|
-
if (timer) clearTimeout(timer);
|
|
392
|
-
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
393
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
394
|
-
reject(err);
|
|
395
|
-
});
|
|
396
|
-
child.on("close", (code, signal) => {
|
|
397
|
-
const durationMs = Date.now() - startedAt;
|
|
398
|
-
try {
|
|
399
|
-
const usage = options.codexReproducible ? parseCodexTokenUsage(stdout) : void 0;
|
|
400
|
-
finalize({
|
|
401
|
-
exitCode: code,
|
|
402
|
-
stdout,
|
|
403
|
-
stderr: redactCodexHome(stderr, env.CODEX_HOME),
|
|
404
|
-
killedBySignal: signal,
|
|
405
|
-
durationMs,
|
|
406
|
-
timedOut,
|
|
407
|
-
...usage ? { usage } : {},
|
|
408
|
-
...evidence ? { evidence } : {}
|
|
409
|
-
});
|
|
410
|
-
} catch (err) {
|
|
411
|
-
if (settled) return;
|
|
412
|
-
settled = true;
|
|
413
|
-
if (timer) clearTimeout(timer);
|
|
414
|
-
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
415
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
416
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
417
|
-
reject(
|
|
418
|
-
options.codexReproducible ? createCodexExecutionDiagnosticError({
|
|
419
|
-
reason,
|
|
420
|
-
exitCode: code,
|
|
421
|
-
killedBySignal: signal,
|
|
422
|
-
timedOut,
|
|
423
|
-
durationMs,
|
|
424
|
-
stdout,
|
|
425
|
-
stderr,
|
|
426
|
-
codexHome: env.CODEX_HOME,
|
|
427
|
-
redactionValues: isolated.diagnosticRedactionValues,
|
|
428
|
-
cause: err
|
|
429
|
-
}) : err instanceof Error ? err : new Error(reason)
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
});
|
|
433
|
-
});
|
|
434
|
-
} finally {
|
|
435
|
-
await isolated.cleanup();
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
var CODEX_EXECUTION_POLICY = {
|
|
439
|
-
sessionPersistence: "ephemeral",
|
|
440
|
-
userConfig: false,
|
|
441
|
-
rules: false,
|
|
442
|
-
projectInstructions: false,
|
|
443
|
-
skillInstructions: false,
|
|
444
|
-
appInstructions: false,
|
|
445
|
-
toolSuggestions: false,
|
|
446
|
-
multiAgentInstructions: false,
|
|
447
|
-
sandbox: "workspace-write",
|
|
448
|
-
permissionProfile: "agent_runtime_reproducible",
|
|
449
|
-
approvalPolicy: "never",
|
|
450
|
-
shellNetwork: false,
|
|
451
|
-
webSearch: false,
|
|
452
|
-
serviceTier: "default",
|
|
453
|
-
shellEnvironment: "core-filtered",
|
|
454
|
-
loginShell: false,
|
|
455
|
-
credentialsReadable: false,
|
|
456
|
-
hostHomeReadable: false,
|
|
457
|
-
procEnvironment: "private-sanitized",
|
|
458
|
-
sensitiveEnvironmentNamesVisible: false,
|
|
459
|
-
parentRepoRead: false,
|
|
460
|
-
gitMetadata: false,
|
|
461
|
-
temporaryDirectory: "workspace-private",
|
|
462
|
-
stagedExecutable: "static-elf-read-only",
|
|
463
|
-
callerReadDeniedPaths: "enforced",
|
|
464
|
-
containerSockets: false
|
|
465
|
-
};
|
|
466
|
-
var FORBIDDEN_CODEX_PROMPT_MARKERS = [
|
|
467
|
-
"<skills_instructions>",
|
|
468
|
-
"# AGENTS.md instructions",
|
|
469
|
-
"<apps_instructions>",
|
|
470
|
-
"<recommended_plugins>",
|
|
471
|
-
"<multi_agent_mode>",
|
|
472
|
-
"You are `/root`, the primary agent"
|
|
473
|
-
];
|
|
474
|
-
async function collectCodexExecutionEvidence(opts) {
|
|
475
|
-
assertCodexReproducibleInvocation(opts.command, opts.args);
|
|
476
|
-
const prompt = opts.args[1];
|
|
477
|
-
if (opts.args[0] !== "exec" || typeof prompt !== "string" || prompt.length === 0) {
|
|
478
|
-
throw new Error("runLocalHarness: reproducible Codex invocation must be codex exec <prompt>");
|
|
479
|
-
}
|
|
480
|
-
const cliVersion = (await runCodexProbe(opts, ["--version"])).stdout.trim();
|
|
481
|
-
if (!/^codex-cli \d+\.\d+\.\d+(?:[-+].+)?$/.test(cliVersion)) {
|
|
482
|
-
throw new Error(
|
|
483
|
-
`runLocalHarness: unexpected Codex CLI version output ${JSON.stringify(cliVersion)}`
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
const hiddenEnvironmentChecks = [
|
|
487
|
-
"CODEX_HOME",
|
|
488
|
-
"CODEX_ACCESS_TOKEN",
|
|
489
|
-
"CODEX_API_KEY",
|
|
490
|
-
"OPENAI_API_KEY"
|
|
491
|
-
].map((name) => `test -z "\${${name}:-}"`).join(" && ");
|
|
492
|
-
const sandboxProof = [
|
|
493
|
-
hiddenEnvironmentChecks,
|
|
494
|
-
"test -r .",
|
|
495
|
-
"! test -r .git",
|
|
496
|
-
'touch "$1"',
|
|
497
|
-
'rm "$1"',
|
|
498
|
-
'staged_dir=$(dirname "$2")',
|
|
499
|
-
'test -x "$staged_dir/codex"',
|
|
500
|
-
'! chmod 700 "$staged_dir" 2>/dev/null',
|
|
501
|
-
'! rm "$staged_dir/codex" 2>/dev/null',
|
|
502
|
-
'! touch "$2" 2>/dev/null',
|
|
503
|
-
"shift 2",
|
|
504
|
-
'for path in "$@"; do ! test -r "$path" || exit 1; done',
|
|
505
|
-
'touch "$TMPDIR/.agent-runtime-tmp-proof"',
|
|
506
|
-
'rm "$TMPDIR/.agent-runtime-tmp-proof"'
|
|
507
|
-
].join(" && ");
|
|
508
|
-
await runCodexProbe(opts, [
|
|
509
|
-
"sandbox",
|
|
510
|
-
"-P",
|
|
511
|
-
CODEX_PERMISSION_PROFILE,
|
|
512
|
-
"-C",
|
|
513
|
-
opts.cwd,
|
|
514
|
-
"sh",
|
|
515
|
-
"-c",
|
|
516
|
-
sandboxProof,
|
|
517
|
-
"sh",
|
|
518
|
-
opts.writeProbe,
|
|
519
|
-
opts.stagedWriteProbe,
|
|
520
|
-
...opts.deniedProbePaths
|
|
521
|
-
]);
|
|
522
|
-
await runCodexProbe(opts, [
|
|
523
|
-
"sandbox",
|
|
524
|
-
"-P",
|
|
525
|
-
CODEX_PERMISSION_PROFILE,
|
|
526
|
-
"-C",
|
|
527
|
-
opts.cwd,
|
|
528
|
-
"python3",
|
|
529
|
-
"-c",
|
|
530
|
-
`import os,re
|
|
531
|
-
pattern=re.compile(br'(TOKEN|KEY|SECRET|PASSWORD|CREDENTIAL|AUTH|COOKIE|CODEX_HOME)', re.I)
|
|
532
|
-
paths=('/proc/1/environ', '/proc/self/environ', f'/proc/{os.getppid()}/environ')
|
|
533
|
-
for path in dict.fromkeys(paths):
|
|
534
|
-
data=open(path,'rb').read().split(b'\\0')
|
|
535
|
-
names=(item.split(b'=',1)[0] for item in data if b'=' in item)
|
|
536
|
-
assert not any(pattern.search(name) for name in names), path`
|
|
537
|
-
]);
|
|
538
|
-
await runCodexProbe(opts, [
|
|
539
|
-
"sandbox",
|
|
540
|
-
"-P",
|
|
541
|
-
CODEX_PERMISSION_PROFILE,
|
|
542
|
-
"-C",
|
|
543
|
-
opts.cwd,
|
|
544
|
-
"python3",
|
|
545
|
-
"-c",
|
|
546
|
-
"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)",
|
|
547
|
-
"/var/run/docker.sock",
|
|
548
|
-
"/run/docker.sock",
|
|
549
|
-
"/var/run/containerd/containerd.sock",
|
|
550
|
-
"/run/containerd/containerd.sock"
|
|
551
|
-
]);
|
|
552
|
-
const debugArgs = codexPromptDebugArgs(opts.args, prompt);
|
|
553
|
-
const renderedPrompt = (await runCodexProbe(opts, debugArgs)).stdout;
|
|
554
|
-
let parsedPrompt;
|
|
555
|
-
try {
|
|
556
|
-
parsedPrompt = JSON.parse(renderedPrompt);
|
|
557
|
-
} catch {
|
|
558
|
-
throw new Error("runLocalHarness: codex debug prompt-input returned invalid JSON");
|
|
559
|
-
}
|
|
560
|
-
const promptEvidenceText = normalizeCodexDebugPromptNewlines(prompt);
|
|
561
|
-
if (!Array.isArray(parsedPrompt) || !jsonTextContains(parsedPrompt, promptEvidenceText)) {
|
|
562
|
-
throw new Error("runLocalHarness: Codex prompt evidence did not contain the exact task prompt");
|
|
563
|
-
}
|
|
564
|
-
const promptContextWithoutTask = JSON.stringify(redactJsonText(parsedPrompt, promptEvidenceText));
|
|
565
|
-
for (const marker of FORBIDDEN_CODEX_PROMPT_MARKERS) {
|
|
566
|
-
if (promptContextWithoutTask.includes(marker)) {
|
|
567
|
-
throw new Error(`runLocalHarness: Codex prompt evidence contains forbidden marker ${marker}`);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
for (const marker of ["<permissions instructions>", "<environment_context>"]) {
|
|
571
|
-
if (!renderedPrompt.includes(marker)) {
|
|
572
|
-
throw new Error(`runLocalHarness: Codex prompt evidence is missing required marker ${marker}`);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
const nonPromptArgs = [
|
|
576
|
-
"codex",
|
|
577
|
-
...opts.args.map((arg, index) => index === 1 ? "<PROMPT>" : arg)
|
|
578
|
-
];
|
|
579
|
-
return {
|
|
580
|
-
cliVersion,
|
|
581
|
-
executableSha256: opts.executableSha256,
|
|
582
|
-
requestedPromptSha256: sha256(prompt),
|
|
583
|
-
effectivePromptSha256: sha256(renderedPrompt),
|
|
584
|
-
nonPromptArgsSha256: sha256(JSON.stringify(nonPromptArgs)),
|
|
585
|
-
controlledConfigSha256: opts.controlledConfigSha256,
|
|
586
|
-
readDeniedPathsSha256: opts.readDeniedPathsSha256,
|
|
587
|
-
readDeniedPathCount: opts.readDeniedPathCount,
|
|
588
|
-
readDeniedPaths: [...opts.readDeniedPaths],
|
|
589
|
-
policy: { ...CODEX_EXECUTION_POLICY }
|
|
590
|
-
};
|
|
591
|
-
}
|
|
592
|
-
function assertCodexReproducibleInvocation(command, args) {
|
|
593
|
-
const executable = basename(command);
|
|
594
|
-
if (executable !== "codex" && executable !== "codex.exe") {
|
|
595
|
-
throw new Error("runLocalHarness: reproducible Codex invocation must use the Codex executable");
|
|
596
|
-
}
|
|
597
|
-
const prompt = args[1];
|
|
598
|
-
const expectedPrefix = ["exec", prompt, ...CODEX_REPRODUCIBLE_ARGS];
|
|
599
|
-
const hasExactPrefix = expectedPrefix.every((arg, index) => args[index] === arg);
|
|
600
|
-
const tail = args.slice(expectedPrefix.length);
|
|
601
|
-
const validModel = tail[0] === "-m" && typeof tail[1] === "string" && tail[1].length > 0 && !tail[1].startsWith("-");
|
|
602
|
-
const validReasoning = tail[2] === "-c" && typeof tail[3] === "string" && /^model_reasoning_effort="(?:none|minimal|low|medium|high|xhigh)"$/.test(tail[3]);
|
|
603
|
-
if (!hasExactPrefix || tail.length !== 4 || !validModel || !validReasoning) {
|
|
604
|
-
throw new Error(
|
|
605
|
-
"runLocalHarness: reproducible Codex invocation does not match the required isolated argv"
|
|
606
|
-
);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
function codexPromptDebugArgs(execArgs, prompt) {
|
|
610
|
-
const debugArgs = ["debug", "prompt-input"];
|
|
611
|
-
for (let index = 2; index < execArgs.length; index += 1) {
|
|
612
|
-
const arg = execArgs[index];
|
|
613
|
-
const value = execArgs[index + 1];
|
|
614
|
-
if (arg === "-c" && value !== void 0) {
|
|
615
|
-
debugArgs.push("-c", value);
|
|
616
|
-
index += 1;
|
|
617
|
-
} else if (arg === "--disable" && value !== void 0) {
|
|
618
|
-
debugArgs.push("--disable", value);
|
|
619
|
-
index += 1;
|
|
620
|
-
} else if (arg === "-m" && value !== void 0) {
|
|
621
|
-
debugArgs.push("-c", `model=${JSON.stringify(value)}`);
|
|
622
|
-
index += 1;
|
|
623
|
-
} else if (arg === "-s" && value !== void 0) {
|
|
624
|
-
debugArgs.push("-c", `sandbox_mode=${JSON.stringify(value)}`);
|
|
625
|
-
index += 1;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
debugArgs.push(prompt);
|
|
629
|
-
return debugArgs;
|
|
630
|
-
}
|
|
631
|
-
function runCodexProbe(opts, args) {
|
|
632
|
-
return new Promise((resolve2, reject) => {
|
|
633
|
-
let child;
|
|
634
|
-
try {
|
|
635
|
-
child = opts.spawn(opts.command, args, {
|
|
636
|
-
cwd: opts.cwd,
|
|
637
|
-
env: opts.env,
|
|
638
|
-
stdio: "pipe",
|
|
639
|
-
detached: process.platform !== "win32"
|
|
640
|
-
});
|
|
641
|
-
} catch (err) {
|
|
642
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
643
|
-
return;
|
|
644
|
-
}
|
|
645
|
-
child.stdin?.end();
|
|
646
|
-
let stdout = "";
|
|
647
|
-
let stderr = "";
|
|
648
|
-
let settled = false;
|
|
649
|
-
let forceKillTimer = null;
|
|
650
|
-
const timer = setTimeout(() => {
|
|
651
|
-
signalProcessTree(child, "SIGTERM");
|
|
652
|
-
forceKillTimer = setTimeout(() => signalProcessTree(child, "SIGKILL"), processKillGraceMs);
|
|
653
|
-
forceKillTimer.unref?.();
|
|
654
|
-
}, 1e4);
|
|
655
|
-
timer.unref?.();
|
|
656
|
-
const onAbort = () => {
|
|
657
|
-
signalProcessTree(child, "SIGTERM");
|
|
658
|
-
if (!forceKillTimer) {
|
|
659
|
-
forceKillTimer = setTimeout(() => signalProcessTree(child, "SIGKILL"), processKillGraceMs);
|
|
660
|
-
forceKillTimer.unref?.();
|
|
661
|
-
}
|
|
662
|
-
};
|
|
663
|
-
if (opts.signal) {
|
|
664
|
-
if (opts.signal.aborted) onAbort();
|
|
665
|
-
else opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
666
|
-
}
|
|
667
|
-
child.stdout?.on("data", (chunk) => {
|
|
668
|
-
stdout += String(chunk);
|
|
669
|
-
});
|
|
670
|
-
child.stderr?.on("data", (chunk) => {
|
|
671
|
-
stderr += String(chunk);
|
|
672
|
-
});
|
|
673
|
-
child.on("error", (err) => {
|
|
674
|
-
if (settled) return;
|
|
675
|
-
settled = true;
|
|
676
|
-
clearTimeout(timer);
|
|
677
|
-
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
678
|
-
opts.signal?.removeEventListener("abort", onAbort);
|
|
679
|
-
reject(err);
|
|
680
|
-
});
|
|
681
|
-
child.on("close", (code, signal) => {
|
|
682
|
-
if (settled) return;
|
|
683
|
-
settled = true;
|
|
684
|
-
clearTimeout(timer);
|
|
685
|
-
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
686
|
-
opts.signal?.removeEventListener("abort", onAbort);
|
|
687
|
-
if (code !== 0) {
|
|
688
|
-
const safeStderr = redactCodexHome(stderr, opts.env.CODEX_HOME);
|
|
689
|
-
reject(
|
|
690
|
-
new Error(
|
|
691
|
-
`runLocalHarness: Codex evidence probe failed (exit ${String(code)}, signal ${String(signal)}): ${safeStderr.trim()}`
|
|
692
|
-
)
|
|
693
|
-
);
|
|
694
|
-
return;
|
|
695
|
-
}
|
|
696
|
-
resolve2({ stdout, stderr });
|
|
697
|
-
});
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
function sha256(value) {
|
|
701
|
-
return createHash("sha256").update(value).digest("hex");
|
|
702
|
-
}
|
|
703
|
-
function normalizeCodexDebugPromptNewlines(value) {
|
|
704
|
-
return value.replaceAll("\r\n", "\n");
|
|
705
|
-
}
|
|
706
|
-
function jsonTextContains(value, needle) {
|
|
707
|
-
if (typeof value === "string") return value.includes(needle);
|
|
708
|
-
if (Array.isArray(value)) return value.some((item) => jsonTextContains(item, needle));
|
|
709
|
-
if (isRecord(value)) return Object.values(value).some((item) => jsonTextContains(item, needle));
|
|
710
|
-
return false;
|
|
711
|
-
}
|
|
712
|
-
function redactJsonText(value, text) {
|
|
713
|
-
if (typeof value === "string") return value.replaceAll(text, "");
|
|
714
|
-
if (Array.isArray(value)) return value.map((item) => redactJsonText(item, text));
|
|
715
|
-
if (isRecord(value)) {
|
|
716
|
-
return Object.fromEntries(
|
|
717
|
-
Object.entries(value).map(([key, item]) => [key, redactJsonText(item, text)])
|
|
718
|
-
);
|
|
719
|
-
}
|
|
720
|
-
return value;
|
|
721
|
-
}
|
|
722
|
-
var CODEX_AUTH_ENV = ["CODEX_ACCESS_TOKEN", "CODEX_API_KEY", "OPENAI_API_KEY"];
|
|
723
|
-
var CODEX_PERMISSION_PROFILE = "agent_runtime_reproducible";
|
|
724
|
-
async function isolateCodexHome(opts) {
|
|
725
|
-
const isolatedHome = await mkdtemp(join(tmpdir(), "agent-runtime-codex-"));
|
|
726
|
-
await chmod(isolatedHome, 448);
|
|
727
|
-
let workspaceTemp = "";
|
|
728
|
-
let stagedExecutableDir = "";
|
|
729
|
-
let ownsStagedExecutableDir = false;
|
|
730
|
-
let writeProbe = "";
|
|
731
|
-
const cleanup = async () => {
|
|
732
|
-
if (ownsStagedExecutableDir) {
|
|
733
|
-
await chmod(stagedExecutableDir, 448).catch(() => void 0);
|
|
734
|
-
}
|
|
735
|
-
await Promise.all([
|
|
736
|
-
...writeProbe ? [rm(writeProbe, { force: true })] : [],
|
|
737
|
-
...workspaceTemp ? [rm(workspaceTemp, { recursive: true, force: true })] : [],
|
|
738
|
-
...ownsStagedExecutableDir ? [rm(stagedExecutableDir, { recursive: true, force: true })] : [],
|
|
739
|
-
rm(isolatedHome, { recursive: true, force: true })
|
|
740
|
-
]);
|
|
741
|
-
};
|
|
742
|
-
try {
|
|
743
|
-
const readDeniedPaths = await normalizeReadDeniedPaths(opts.readDeniedPaths);
|
|
744
|
-
const candidateRoot = await realpath(opts.cwd);
|
|
745
|
-
writeProbe = join(candidateRoot, `.agent-runtime-write-proof-${randomUUID()}`);
|
|
746
|
-
const stagedPath = join(candidateRoot, ".agent-runtime-bin");
|
|
747
|
-
if (readDeniedPaths.some(
|
|
748
|
-
(path) => isSameOrDescendant(candidateRoot, path) || isSameOrDescendant(stagedPath, path) || isSameOrDescendant(path, stagedPath)
|
|
749
|
-
)) {
|
|
750
|
-
throw new Error(
|
|
751
|
-
"runLocalHarness: codexReadDeniedPaths cannot contain the candidate root or staged executable"
|
|
752
|
-
);
|
|
753
|
-
}
|
|
754
|
-
const nativeExecutable = await opts.resolveExecutable(opts.command, opts.baseEnv);
|
|
755
|
-
if (!await isStaticElfExecutable(nativeExecutable)) {
|
|
756
|
-
throw new Error(
|
|
757
|
-
"runLocalHarness: reproducible Codex mode requires a statically linked Linux Codex ELF"
|
|
758
|
-
);
|
|
759
|
-
}
|
|
760
|
-
const sourceExecutableSha256 = await sha256File(nativeExecutable);
|
|
761
|
-
stagedExecutableDir = stagedPath;
|
|
762
|
-
try {
|
|
763
|
-
await mkdir(stagedExecutableDir, { mode: 448 });
|
|
764
|
-
ownsStagedExecutableDir = true;
|
|
765
|
-
} catch (err) {
|
|
766
|
-
throw new Error(
|
|
767
|
-
`runLocalHarness: candidate-private .agent-runtime-bin must not already exist: ${String(err.code ?? err)}`
|
|
768
|
-
);
|
|
769
|
-
}
|
|
770
|
-
const stagedExecutable = join(stagedExecutableDir, "codex");
|
|
771
|
-
await copyFile(nativeExecutable, stagedExecutable, constants2.COPYFILE_FICLONE);
|
|
772
|
-
await chmod(stagedExecutable, 320);
|
|
773
|
-
const executableSha256 = await sha256File(stagedExecutable);
|
|
774
|
-
if (executableSha256 !== sourceExecutableSha256 || !await isStaticElfExecutable(stagedExecutable)) {
|
|
775
|
-
throw new Error("runLocalHarness: staged Codex executable digest mismatch");
|
|
776
|
-
}
|
|
777
|
-
await chmod(stagedExecutableDir, 320);
|
|
778
|
-
workspaceTemp = await mkdtemp(join(candidateRoot, ".agent-runtime-tmp-"));
|
|
779
|
-
await chmod(workspaceTemp, 448);
|
|
780
|
-
const ambientHome = resolve(opts.baseEnv.CODEX_HOME?.trim() || join(homedir(), ".codex"));
|
|
781
|
-
const ambientAuth = join(ambientHome, "auth.json");
|
|
782
|
-
const isolatedAuth = join(isolatedHome, "auth.json");
|
|
783
|
-
let authText = "";
|
|
784
|
-
try {
|
|
785
|
-
await access(ambientAuth, constants2.R_OK);
|
|
786
|
-
authText = await readCodexAuthRedactionText(ambientAuth);
|
|
787
|
-
await symlink(ambientAuth, isolatedAuth);
|
|
788
|
-
} catch (err) {
|
|
789
|
-
throw new Error(
|
|
790
|
-
`runLocalHarness: reproducible Codex mode requires readable auth.json: ${String(err.code ?? err)}`
|
|
791
|
-
);
|
|
792
|
-
}
|
|
793
|
-
const deniedHostRoots = [homedir(), opts.baseEnv.HOME?.trim(), ambientHome].filter((path) => Boolean(path)).map((path) => resolve(path)).filter((path, index, paths) => paths.indexOf(path) === index);
|
|
794
|
-
const controlledConfig = codexControlledConfig({
|
|
795
|
-
ambientTmp: opts.baseEnv.TMPDIR?.trim() || tmpdir(),
|
|
796
|
-
deniedHostRoots,
|
|
797
|
-
workspaceTemp,
|
|
798
|
-
stagedExecutableDir,
|
|
799
|
-
readDeniedPaths
|
|
800
|
-
});
|
|
801
|
-
await writeFile(join(isolatedHome, "config.toml"), controlledConfig, { mode: 384 });
|
|
802
|
-
const env = { ...opts.baseEnv };
|
|
803
|
-
for (const name of Object.keys(env)) {
|
|
804
|
-
if (codexSensitiveEnvironmentName.test(name) || CODEX_AUTH_ENV.some((authName) => authName === name)) {
|
|
805
|
-
delete env[name];
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
env.CODEX_HOME = isolatedHome;
|
|
809
|
-
const home = homedir();
|
|
810
|
-
const deniedProbePaths = [
|
|
811
|
-
...deniedHostRoots,
|
|
812
|
-
ambientAuth,
|
|
813
|
-
isolatedAuth,
|
|
814
|
-
join(home, ".claude", ".credentials.json"),
|
|
815
|
-
join(home, ".claude.json"),
|
|
816
|
-
join(home, ".local", "share", "opencode", "auth.json"),
|
|
817
|
-
join(home, ".agents"),
|
|
818
|
-
join(home, "Documents"),
|
|
819
|
-
join(home, "Downloads"),
|
|
820
|
-
join(home, ".npm"),
|
|
821
|
-
join(home, "code"),
|
|
822
|
-
...readDeniedPaths
|
|
823
|
-
].filter((path, index, paths) => paths.indexOf(path) === index);
|
|
824
|
-
return {
|
|
825
|
-
env,
|
|
826
|
-
command: stagedExecutable,
|
|
827
|
-
cleanup,
|
|
828
|
-
controlledConfigSha256: sha256(controlledConfig),
|
|
829
|
-
executableSha256,
|
|
830
|
-
readDeniedPaths,
|
|
831
|
-
readDeniedPathsSha256: sha256(JSON.stringify(readDeniedPaths)),
|
|
832
|
-
readDeniedPathCount: readDeniedPaths.length,
|
|
833
|
-
diagnosticRedactionValues: collectCodexDiagnosticRedactionValues(opts.baseEnv, authText),
|
|
834
|
-
ambientAuth,
|
|
835
|
-
isolatedAuth,
|
|
836
|
-
writeProbe,
|
|
837
|
-
stagedWriteProbe: join(stagedExecutableDir, "write-proof"),
|
|
838
|
-
deniedProbePaths
|
|
839
|
-
};
|
|
840
|
-
} catch (err) {
|
|
841
|
-
await cleanup();
|
|
842
|
-
throw err;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
function codexControlledConfig(opts) {
|
|
846
|
-
const rules = /* @__PURE__ */ new Map();
|
|
847
|
-
const broadDeniedPaths = minimalPathRoots([
|
|
848
|
-
...opts.deniedHostRoots.map((path) => resolve(path)),
|
|
849
|
-
tmpdir(),
|
|
850
|
-
opts.ambientTmp,
|
|
851
|
-
"/proc",
|
|
852
|
-
"/run/docker",
|
|
853
|
-
"/run/containerd"
|
|
854
|
-
]);
|
|
855
|
-
for (const path of broadDeniedPaths) {
|
|
856
|
-
rules.set(path, "deny");
|
|
857
|
-
}
|
|
858
|
-
const workspaceRoot = dirname(opts.workspaceTemp);
|
|
859
|
-
for (const path of opts.readDeniedPaths) {
|
|
860
|
-
const alreadyDenied = broadDeniedPaths.some((root) => isSameOrDescendant(path, root));
|
|
861
|
-
if (!alreadyDenied || isSameOrDescendant(path, workspaceRoot)) rules.set(path, "deny");
|
|
862
|
-
}
|
|
863
|
-
if (!rules.has(opts.workspaceTemp)) rules.set(opts.workspaceTemp, "write");
|
|
864
|
-
if (!rules.has(opts.stagedExecutableDir)) rules.set(opts.stagedExecutableDir, "read");
|
|
865
|
-
const filesystemRules = [...rules].sort(([a], [b]) => a.localeCompare(b)).map(([path, access2]) => `${JSON.stringify(path)} = ${JSON.stringify(access2)}`).join("\n");
|
|
866
|
-
return `default_permissions = "${CODEX_PERMISSION_PROFILE}"
|
|
867
|
-
approval_policy = "never"
|
|
868
|
-
allow_login_shell = false
|
|
869
|
-
service_tier = "default"
|
|
870
|
-
|
|
871
|
-
[shell_environment_policy]
|
|
872
|
-
inherit = "core"
|
|
873
|
-
ignore_default_excludes = false
|
|
874
|
-
exclude = ["^CODEX_HOME$", "(?i).*TOKEN.*", "(?i).*KEY.*", "(?i).*SECRET.*", "(?i).*PASSWORD.*", "(?i).*CREDENTIAL.*", "(?i).*AUTH.*", "(?i).*COOKIE.*"]
|
|
875
|
-
set = { TMPDIR = ${JSON.stringify(opts.workspaceTemp)} }
|
|
876
|
-
|
|
877
|
-
[permissions.${CODEX_PERMISSION_PROFILE}.filesystem]
|
|
878
|
-
":minimal" = "read"
|
|
879
|
-
${filesystemRules}
|
|
880
|
-
":slash_tmp" = "deny"
|
|
881
|
-
|
|
882
|
-
[permissions.${CODEX_PERMISSION_PROFILE}.filesystem.":workspace_roots"]
|
|
883
|
-
"." = "write"
|
|
884
|
-
".git" = "deny"
|
|
885
|
-
|
|
886
|
-
[permissions.${CODEX_PERMISSION_PROFILE}.network]
|
|
887
|
-
enabled = false
|
|
888
|
-
|
|
889
|
-
[permissions.${CODEX_PERMISSION_PROFILE}.network.unix_sockets]
|
|
890
|
-
"/var/run/docker.sock" = "deny"
|
|
891
|
-
"/run/docker.sock" = "deny"
|
|
892
|
-
"/var/run/containerd/containerd.sock" = "deny"
|
|
893
|
-
"/run/containerd/containerd.sock" = "deny"
|
|
894
|
-
`;
|
|
895
|
-
}
|
|
896
|
-
function isSameOrDescendant(path, root) {
|
|
897
|
-
const normalizedPath = resolve(path);
|
|
898
|
-
const normalizedRoot = resolve(root);
|
|
899
|
-
return normalizedPath === normalizedRoot || normalizedPath.startsWith(`${normalizedRoot}${sep}`);
|
|
900
|
-
}
|
|
901
|
-
function minimalPathRoots(paths) {
|
|
902
|
-
const ordered = [...new Set(paths.map((path) => resolve(path)))].sort(
|
|
903
|
-
(a, b) => a.length - b.length || a.localeCompare(b)
|
|
904
|
-
);
|
|
905
|
-
return ordered.filter(
|
|
906
|
-
(path, index) => !ordered.slice(0, index).some((root) => isSameOrDescendant(path, root))
|
|
907
|
-
);
|
|
908
|
-
}
|
|
909
|
-
async function normalizeReadDeniedPaths(paths) {
|
|
910
|
-
if (!Array.isArray(paths)) {
|
|
911
|
-
throw new Error("runLocalHarness: codexReadDeniedPaths must be an array of absolute paths");
|
|
912
|
-
}
|
|
913
|
-
const normalized = /* @__PURE__ */ new Set();
|
|
914
|
-
for (const path of paths) {
|
|
915
|
-
if (typeof path !== "string" || path.length === 0 || path.includes("\0") || !isAbsolute(path)) {
|
|
916
|
-
throw new Error("runLocalHarness: codexReadDeniedPaths must contain absolute paths");
|
|
917
|
-
}
|
|
918
|
-
const absolute = resolve(path);
|
|
919
|
-
normalized.add(absolute);
|
|
920
|
-
try {
|
|
921
|
-
normalized.add(await realpath(absolute));
|
|
922
|
-
} catch (err) {
|
|
923
|
-
if (err.code !== "ENOENT") throw err;
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
return [...normalized].sort();
|
|
927
|
-
}
|
|
928
|
-
async function resolveCodexNativeExecutable(command, env) {
|
|
929
|
-
const candidates = command.includes(sep) || isAbsolute(command) ? [resolve(command)] : (env.PATH ?? "").split(delimiter).filter(Boolean).map((entry) => join(entry, command));
|
|
930
|
-
const seen = /* @__PURE__ */ new Set();
|
|
931
|
-
for (const candidate of candidates) {
|
|
932
|
-
let resolved;
|
|
933
|
-
try {
|
|
934
|
-
await access(candidate, constants2.X_OK);
|
|
935
|
-
resolved = await realpath(candidate);
|
|
936
|
-
} catch {
|
|
937
|
-
continue;
|
|
938
|
-
}
|
|
939
|
-
if (seen.has(resolved)) continue;
|
|
940
|
-
seen.add(resolved);
|
|
941
|
-
if (await isStaticElfExecutable(resolved)) return resolved;
|
|
942
|
-
const packageRoot = await findCodexPackageRoot(resolved);
|
|
943
|
-
if (!packageRoot) continue;
|
|
944
|
-
const bundled = await findBundledCodexExecutable(packageRoot);
|
|
945
|
-
if (bundled) return bundled;
|
|
946
|
-
}
|
|
947
|
-
throw new Error(
|
|
948
|
-
"runLocalHarness: could not resolve the native Codex executable from the requested command"
|
|
949
|
-
);
|
|
950
|
-
}
|
|
951
|
-
async function findCodexPackageRoot(file) {
|
|
952
|
-
let cursor = dirname(file);
|
|
953
|
-
while (true) {
|
|
954
|
-
try {
|
|
955
|
-
const parsed = JSON.parse(await readFile(join(cursor, "package.json"), "utf8"));
|
|
956
|
-
if (parsed.name === "@openai/codex") return cursor;
|
|
957
|
-
} catch {
|
|
958
|
-
}
|
|
959
|
-
const parent = dirname(cursor);
|
|
960
|
-
if (parent === cursor) return void 0;
|
|
961
|
-
cursor = parent;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
async function findBundledCodexExecutable(packageRoot) {
|
|
965
|
-
const target = linuxCodexTarget();
|
|
966
|
-
const candidates = [
|
|
967
|
-
join(
|
|
968
|
-
packageRoot,
|
|
969
|
-
"node_modules",
|
|
970
|
-
"@openai",
|
|
971
|
-
target.packageName,
|
|
972
|
-
"vendor",
|
|
973
|
-
target.triple,
|
|
974
|
-
"bin",
|
|
975
|
-
"codex"
|
|
976
|
-
),
|
|
977
|
-
join(packageRoot, "vendor", target.triple, "bin", "codex")
|
|
978
|
-
];
|
|
979
|
-
for (const executable of candidates) {
|
|
980
|
-
if (await isStaticElfExecutable(executable)) return executable;
|
|
981
|
-
}
|
|
982
|
-
return void 0;
|
|
983
|
-
}
|
|
984
|
-
function linuxCodexTarget() {
|
|
985
|
-
if (process.platform !== "linux") {
|
|
986
|
-
throw new Error("runLocalHarness: reproducible Codex mode requires Linux");
|
|
987
|
-
}
|
|
988
|
-
if (process.arch === "x64") {
|
|
989
|
-
return { packageName: "codex-linux-x64", triple: "x86_64-unknown-linux-musl" };
|
|
990
|
-
}
|
|
991
|
-
if (process.arch === "arm64") {
|
|
992
|
-
return { packageName: "codex-linux-arm64", triple: "aarch64-unknown-linux-musl" };
|
|
993
|
-
}
|
|
994
|
-
throw new Error(`runLocalHarness: unsupported Linux architecture ${process.arch}`);
|
|
995
|
-
}
|
|
996
|
-
async function isStaticElfExecutable(path) {
|
|
997
|
-
let handle;
|
|
998
|
-
try {
|
|
999
|
-
await access(path, constants2.X_OK);
|
|
1000
|
-
handle = await open2(path, "r");
|
|
1001
|
-
const header = Buffer.alloc(64);
|
|
1002
|
-
const { bytesRead } = await handle.read(header, 0, header.length, 0);
|
|
1003
|
-
if (bytesRead !== header.length || !header.subarray(0, 4).equals(Buffer.from([127, 69, 76, 70])) || header[4] !== 2 || header[5] !== 1) {
|
|
1004
|
-
return false;
|
|
1005
|
-
}
|
|
1006
|
-
const programHeaderOffset = Number(header.readBigUInt64LE(32));
|
|
1007
|
-
const programHeaderSize = header.readUInt16LE(54);
|
|
1008
|
-
const programHeaderCount = header.readUInt16LE(56);
|
|
1009
|
-
const expectedMachine = process.arch === "x64" ? 62 : process.arch === "arm64" ? 183 : -1;
|
|
1010
|
-
if (![2, 3].includes(header.readUInt16LE(16)) || header.readUInt16LE(18) !== expectedMachine || !Number.isSafeInteger(programHeaderOffset) || programHeaderOffset < 64 || programHeaderSize < 56 || programHeaderSize > 1024 || programHeaderCount === 0 || programHeaderCount > 1024) {
|
|
1011
|
-
return false;
|
|
1012
|
-
}
|
|
1013
|
-
const tableSize = programHeaderSize * programHeaderCount;
|
|
1014
|
-
const table = Buffer.alloc(tableSize);
|
|
1015
|
-
const tableRead = await handle.read(table, 0, tableSize, programHeaderOffset);
|
|
1016
|
-
if (tableRead.bytesRead !== tableSize) return false;
|
|
1017
|
-
for (let index = 0; index < programHeaderCount; index += 1) {
|
|
1018
|
-
const type = table.readUInt32LE(index * programHeaderSize);
|
|
1019
|
-
if (type === 3) return false;
|
|
1020
|
-
}
|
|
1021
|
-
return true;
|
|
1022
|
-
} catch {
|
|
1023
|
-
return false;
|
|
1024
|
-
} finally {
|
|
1025
|
-
await handle?.close();
|
|
1026
|
-
}
|
|
1027
|
-
}
|
|
1028
|
-
function sha256File(path) {
|
|
1029
|
-
return new Promise((resolveDigest, reject) => {
|
|
1030
|
-
const hash = createHash("sha256");
|
|
1031
|
-
const stream = createReadStream(path);
|
|
1032
|
-
stream.on("error", reject);
|
|
1033
|
-
stream.on("data", (chunk) => hash.update(chunk));
|
|
1034
|
-
stream.on("end", () => resolveDigest(hash.digest("hex")));
|
|
1035
|
-
});
|
|
1036
|
-
}
|
|
1037
|
-
function signalProcessTree(child, signal) {
|
|
1038
|
-
if (process.platform !== "win32" && typeof child.pid === "number") {
|
|
1039
|
-
try {
|
|
1040
|
-
process.kill(-child.pid, signal);
|
|
1041
|
-
return;
|
|
1042
|
-
} catch (err) {
|
|
1043
|
-
if (err.code === "ESRCH") return;
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
try {
|
|
1047
|
-
child.kill(signal);
|
|
1048
|
-
} catch {
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
function parseCodexTokenUsage(stdout) {
|
|
1052
|
-
const completed = [];
|
|
1053
|
-
for (const [index, line] of stdout.split(/\r?\n/).entries()) {
|
|
1054
|
-
if (line.trim().length === 0) continue;
|
|
1055
|
-
let event2;
|
|
1056
|
-
try {
|
|
1057
|
-
event2 = JSON.parse(line);
|
|
1058
|
-
} catch {
|
|
1059
|
-
throw new Error(`runLocalHarness: Codex JSONL line ${index + 1} is not valid JSON`);
|
|
1060
|
-
}
|
|
1061
|
-
if (isRecord(event2) && event2.type === "turn.completed") completed.push(event2);
|
|
1062
|
-
}
|
|
1063
|
-
if (completed.length !== 1) {
|
|
1064
|
-
throw new Error(
|
|
1065
|
-
`runLocalHarness: expected exactly one Codex turn.completed usage event, received ${completed.length}`
|
|
1066
|
-
);
|
|
1067
|
-
}
|
|
1068
|
-
const event = completed[0];
|
|
1069
|
-
if (!isRecord(event) || !isRecord(event.usage)) {
|
|
1070
|
-
throw new Error("runLocalHarness: Codex turn.completed event is missing usage");
|
|
1071
|
-
}
|
|
1072
|
-
const usage = event.usage;
|
|
1073
|
-
const inputTokens = naturalNumber(usage.input_tokens, "input_tokens");
|
|
1074
|
-
const cachedInputTokens = naturalNumber(usage.cached_input_tokens, "cached_input_tokens");
|
|
1075
|
-
const outputTokens = naturalNumber(usage.output_tokens, "output_tokens");
|
|
1076
|
-
const reasoningOutputTokens = naturalNumber(
|
|
1077
|
-
usage.reasoning_output_tokens,
|
|
1078
|
-
"reasoning_output_tokens"
|
|
1079
|
-
);
|
|
1080
|
-
if (cachedInputTokens > inputTokens) {
|
|
1081
|
-
throw new Error("runLocalHarness: Codex cached_input_tokens exceeds input_tokens");
|
|
1082
|
-
}
|
|
1083
|
-
if (reasoningOutputTokens > outputTokens) {
|
|
1084
|
-
throw new Error("runLocalHarness: Codex reasoning_output_tokens exceeds output_tokens");
|
|
1085
|
-
}
|
|
1086
|
-
return { inputTokens, cachedInputTokens, outputTokens, reasoningOutputTokens };
|
|
1087
|
-
}
|
|
1088
|
-
function isRecord(value) {
|
|
1089
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1090
|
-
}
|
|
1091
|
-
function naturalNumber(value, field) {
|
|
1092
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
1093
|
-
throw new Error(`runLocalHarness: Codex usage.${field} must be a non-negative safe integer`);
|
|
1094
|
-
}
|
|
1095
|
-
return value;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
// src/runtime/supervise/model-policy.ts
|
|
1099
|
-
function assertModelAllowed(model, allowed) {
|
|
1100
|
-
if (!allowed || model === void 0) return;
|
|
1101
|
-
if (!allowed.includes(model)) {
|
|
1102
|
-
throw new ConfigError(
|
|
1103
|
-
`model ${JSON.stringify(model)} is not in the allowed set ${JSON.stringify([...allowed])}`
|
|
1104
|
-
);
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
export {
|
|
1109
|
-
CodexExecutionDiagnosticError,
|
|
1110
|
-
harnessInvocation,
|
|
1111
|
-
runLocalHarness,
|
|
1112
|
-
parseCodexTokenUsage,
|
|
1113
|
-
assertModelAllowed
|
|
1114
|
-
};
|
|
1115
|
-
//# sourceMappingURL=chunk-IKGCKPDX.js.map
|