@tangle-network/agent-runtime 0.94.0 → 0.94.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/dist/agent.js +4 -4
- package/dist/{agentic-generator-B8oeE2Yv.d.ts → agentic-generator-D9ZmAuiL.d.ts} +1 -1
- package/dist/candidate-execution/index.js +3 -3
- package/dist/{chunk-OO4EK3JB.js → chunk-D336OZHK.js} +6 -3
- package/dist/chunk-D336OZHK.js.map +1 -0
- package/dist/{chunk-4GBT4WQ6.js → chunk-HPCJBBLS.js} +2 -2
- package/dist/{chunk-RTVVH2KL.js → chunk-JEP2WM24.js} +2 -2
- package/dist/{chunk-55P6V5JT.js → chunk-K7OPNHU4.js} +2 -2
- package/dist/{chunk-7I2AOKCI.js → chunk-KHLE3C5G.js} +2 -2
- package/dist/{chunk-4E2GM63H.js → chunk-MZH4HORF.js} +2 -2
- package/dist/chunk-PJNGPWTA.js +634 -0
- package/dist/chunk-PJNGPWTA.js.map +1 -0
- package/dist/{chunk-MRYPFDHJ.js → chunk-RTSZNYXM.js} +3 -3
- package/dist/{chunk-H2D3DCEB.js → chunk-SJHFJOCY.js} +2 -2
- package/dist/{chunk-FHZHADIM.js → chunk-URMJTSXB.js} +52 -10
- package/dist/chunk-URMJTSXB.js.map +1 -0
- package/dist/{chunk-3D2RHC4K.js → chunk-WIPGQ4GT.js} +1 -1
- package/dist/{chunk-3D2RHC4K.js.map → chunk-WIPGQ4GT.js.map} +1 -1
- package/dist/{completion-gate-BDkEYB_-.d.ts → completion-gate-BAj3dXeW.d.ts} +14 -2
- package/dist/{coordination-EGoRbbsd.d.ts → coordination-B3ZuApR_.d.ts} +5 -3
- package/dist/environment-provider.d.ts +1 -1
- package/dist/{improve-ZzTEkpF_.d.ts → improve-r8SvIxYv.d.ts} +2 -2
- package/dist/index.d.ts +11 -11
- package/dist/index.js +11 -11
- package/dist/intelligence.d.ts +3 -3
- package/dist/intelligence.js +5 -5
- package/dist/knowledge.d.ts +5 -5
- package/dist/knowledge.js +4 -4
- package/dist/lifecycle.d.ts +3 -3
- package/dist/lifecycle.js +2 -2
- package/dist/{local-harness-dcD5WTTr.d.ts → local-harness-B7bNuNJw.d.ts} +49 -1
- package/dist/{loop-runner-bin-BRXC1RwC.d.ts → loop-runner-bin-D4bMIxmB.d.ts} +2 -2
- package/dist/loop-runner-bin.d.ts +5 -5
- package/dist/loop-runner-bin.js +5 -5
- package/dist/loops.d.ts +11 -11
- package/dist/loops.js +4 -4
- package/dist/mcp/bin.js +3 -3
- package/dist/mcp/index.d.ts +7 -7
- package/dist/mcp/index.js +7 -5
- package/dist/mcp/index.js.map +1 -1
- package/dist/{mcp-serve-verifier-Bg4C3p5S.d.ts → mcp-serve-verifier-BuXjaZDE.d.ts} +1 -1
- package/dist/{supervise-Gmf709kn.d.ts → supervise-DTNcLDl2.d.ts} +3 -3
- package/dist/{types-Cg_teUPO.d.ts → types-Bb-CqxJH.d.ts} +3 -0
- package/dist/{worktree-fanout-BrB4att3.d.ts → worktree-fanout-IMKqdV_E.d.ts} +13 -13
- package/package.json +1 -1
- package/dist/chunk-FHZHADIM.js.map +0 -1
- package/dist/chunk-OO4EK3JB.js.map +0 -1
- package/dist/chunk-PX6SXX3M.js +0 -146
- package/dist/chunk-PX6SXX3M.js.map +0 -1
- /package/dist/{chunk-4GBT4WQ6.js.map → chunk-HPCJBBLS.js.map} +0 -0
- /package/dist/{chunk-RTVVH2KL.js.map → chunk-JEP2WM24.js.map} +0 -0
- /package/dist/{chunk-55P6V5JT.js.map → chunk-K7OPNHU4.js.map} +0 -0
- /package/dist/{chunk-7I2AOKCI.js.map → chunk-KHLE3C5G.js.map} +0 -0
- /package/dist/{chunk-4E2GM63H.js.map → chunk-MZH4HORF.js.map} +0 -0
- /package/dist/{chunk-MRYPFDHJ.js.map → chunk-RTSZNYXM.js.map} +0 -0
- /package/dist/{chunk-H2D3DCEB.js.map → chunk-SJHFJOCY.js.map} +0 -0
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
// src/mcp/local-harness.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { createHash } from "crypto";
|
|
4
|
+
import { constants } from "fs";
|
|
5
|
+
import { access, chmod, mkdtemp, rm, symlink, writeFile } from "fs/promises";
|
|
6
|
+
import { homedir, tmpdir } from "os";
|
|
7
|
+
import { basename, join } from "path";
|
|
8
|
+
var codexReasoningEffort = {
|
|
9
|
+
none: "none",
|
|
10
|
+
minimal: "minimal",
|
|
11
|
+
low: "low",
|
|
12
|
+
medium: "medium",
|
|
13
|
+
high: "high",
|
|
14
|
+
xhigh: "xhigh",
|
|
15
|
+
ultracode: "xhigh"
|
|
16
|
+
};
|
|
17
|
+
function codexReasoningArgs(reasoningEffort) {
|
|
18
|
+
const mapped = codexReasoningEffort[reasoningEffort];
|
|
19
|
+
if (mapped === void 0) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`harnessInvocation: unsupported Codex reasoning effort ${String(reasoningEffort)}`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return ["-c", `model_reasoning_effort="${mapped}"`];
|
|
25
|
+
}
|
|
26
|
+
var HARNESS_INVOCATIONS = {
|
|
27
|
+
claude: {
|
|
28
|
+
command: "claude",
|
|
29
|
+
// `-p` IS headless/print mode; the old `--headless` flag was removed from the CLI.
|
|
30
|
+
// Permission bypass is an explicit per-run opt-in below, never the public default.
|
|
31
|
+
buildArgs: (taskPrompt) => ["-p", taskPrompt],
|
|
32
|
+
modelArgs: (model) => ["-m", model]
|
|
33
|
+
},
|
|
34
|
+
codex: {
|
|
35
|
+
command: "codex",
|
|
36
|
+
buildArgs: (taskPrompt) => ["exec", taskPrompt],
|
|
37
|
+
modelArgs: (model) => ["-m", model],
|
|
38
|
+
reasoningArgs: codexReasoningArgs
|
|
39
|
+
},
|
|
40
|
+
opencode: {
|
|
41
|
+
command: "opencode",
|
|
42
|
+
buildArgs: (taskPrompt) => ["run", taskPrompt],
|
|
43
|
+
modelArgs: (model) => ["-m", model]
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var CODEX_REPRODUCIBLE_ARGS = [
|
|
47
|
+
"--ephemeral",
|
|
48
|
+
"--ignore-rules",
|
|
49
|
+
"--json",
|
|
50
|
+
"-c",
|
|
51
|
+
'approval_policy="never"',
|
|
52
|
+
"-c",
|
|
53
|
+
'web_search="disabled"',
|
|
54
|
+
"-c",
|
|
55
|
+
"project_doc_max_bytes=0",
|
|
56
|
+
"-c",
|
|
57
|
+
"skills.include_instructions=false",
|
|
58
|
+
"-c",
|
|
59
|
+
"include_apps_instructions=false",
|
|
60
|
+
"--disable",
|
|
61
|
+
"tool_suggest",
|
|
62
|
+
"-c",
|
|
63
|
+
'features.multi_agent_v2.root_agent_usage_hint_text=""',
|
|
64
|
+
"-c",
|
|
65
|
+
'features.multi_agent_v2.subagent_usage_hint_text=""',
|
|
66
|
+
"-c",
|
|
67
|
+
'features.multi_agent_v2.multi_agent_mode_hint_text=""',
|
|
68
|
+
"--strict-config"
|
|
69
|
+
];
|
|
70
|
+
function buildHarnessArgs(harness, taskPrompt, options = {}) {
|
|
71
|
+
const args = HARNESS_INVOCATIONS[harness].buildArgs(taskPrompt);
|
|
72
|
+
if (harness === "claude" && options.dangerouslySkipPermissions) {
|
|
73
|
+
args.push("--dangerously-skip-permissions");
|
|
74
|
+
}
|
|
75
|
+
if (options.codexReproducible) {
|
|
76
|
+
if (harness !== "codex") {
|
|
77
|
+
throw new Error("harnessInvocation: codexReproducible requires the Codex harness");
|
|
78
|
+
}
|
|
79
|
+
args.push(...CODEX_REPRODUCIBLE_ARGS);
|
|
80
|
+
}
|
|
81
|
+
return args;
|
|
82
|
+
}
|
|
83
|
+
function harnessInvocation(harness, profile, taskPrompt, options = {}) {
|
|
84
|
+
const invocation = HARNESS_INVOCATIONS[harness];
|
|
85
|
+
if (!invocation) {
|
|
86
|
+
throw new Error(`harnessInvocation: unknown harness ${String(harness)}`);
|
|
87
|
+
}
|
|
88
|
+
if (options.codexReproducible) {
|
|
89
|
+
const model2 = profile.model?.default;
|
|
90
|
+
if (typeof model2 !== "string" || model2.trim().length === 0) {
|
|
91
|
+
throw new Error("harnessInvocation: codexReproducible requires profile.model.default");
|
|
92
|
+
}
|
|
93
|
+
if (profile.model?.reasoningEffort === void 0) {
|
|
94
|
+
throw new Error("harnessInvocation: codexReproducible requires profile.model.reasoningEffort");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const systemPrompt = profile.prompt?.systemPrompt;
|
|
98
|
+
const instructions = profile.prompt?.instructions;
|
|
99
|
+
if (instructions !== void 0 && (!Array.isArray(instructions) || instructions.some((instruction) => typeof instruction !== "string"))) {
|
|
100
|
+
throw new Error("harnessInvocation: profile.prompt.instructions must be an array of strings");
|
|
101
|
+
}
|
|
102
|
+
const promptSections = [
|
|
103
|
+
...typeof systemPrompt === "string" && systemPrompt.trim().length > 0 ? [systemPrompt] : [],
|
|
104
|
+
...instructions?.filter((instruction) => instruction.trim().length > 0) ?? [],
|
|
105
|
+
taskPrompt
|
|
106
|
+
];
|
|
107
|
+
const composedPrompt = promptSections.join("\n\n");
|
|
108
|
+
const args = buildHarnessArgs(harness, composedPrompt, options);
|
|
109
|
+
const model = profile.model?.default;
|
|
110
|
+
if (typeof model === "string" && model.length > 0) {
|
|
111
|
+
args.push(...invocation.modelArgs(model));
|
|
112
|
+
}
|
|
113
|
+
const reasoningEffort = profile.model?.reasoningEffort;
|
|
114
|
+
if (reasoningEffort !== void 0 && invocation.reasoningArgs) {
|
|
115
|
+
args.push(...invocation.reasoningArgs(reasoningEffort));
|
|
116
|
+
}
|
|
117
|
+
return { command: invocation.command, args };
|
|
118
|
+
}
|
|
119
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
120
|
+
async function runLocalHarness(options) {
|
|
121
|
+
const { harness, cwd, taskPrompt } = options;
|
|
122
|
+
if (options.codexReproducible && harness !== "codex") {
|
|
123
|
+
throw new Error("runLocalHarness: codexReproducible requires the Codex harness");
|
|
124
|
+
}
|
|
125
|
+
if (options.codexReproducible && process.platform !== "linux") {
|
|
126
|
+
throw new Error("runLocalHarness: codexReproducible currently requires Linux");
|
|
127
|
+
}
|
|
128
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
129
|
+
const spawnImpl = options.spawn ?? spawn;
|
|
130
|
+
const invocation = HARNESS_INVOCATIONS[harness];
|
|
131
|
+
if (!invocation) {
|
|
132
|
+
throw new Error(`runLocalHarness: unknown harness ${String(harness)}`);
|
|
133
|
+
}
|
|
134
|
+
const startedAt = Date.now();
|
|
135
|
+
const command = options.invocation?.command ?? invocation.command;
|
|
136
|
+
const args = options.invocation ? [...options.invocation.args] : buildHarnessArgs(harness, taskPrompt, options);
|
|
137
|
+
if (options.codexReproducible) assertCodexReproducibleInvocation(command, args);
|
|
138
|
+
const baseEnv = options.env ?? process.env;
|
|
139
|
+
const isolated = options.codexReproducible ? await isolateCodexHome(baseEnv, cwd) : {
|
|
140
|
+
env: baseEnv,
|
|
141
|
+
cleanup: async () => void 0,
|
|
142
|
+
controlledConfigSha256: "",
|
|
143
|
+
ambientAuth: "",
|
|
144
|
+
isolatedAuth: "",
|
|
145
|
+
writeProbe: ""
|
|
146
|
+
};
|
|
147
|
+
const env = isolated.env;
|
|
148
|
+
try {
|
|
149
|
+
const evidence = options.codexReproducible ? await collectCodexExecutionEvidence({
|
|
150
|
+
command,
|
|
151
|
+
args,
|
|
152
|
+
cwd,
|
|
153
|
+
env,
|
|
154
|
+
spawn: spawnImpl,
|
|
155
|
+
controlledConfigSha256: isolated.controlledConfigSha256,
|
|
156
|
+
ambientAuth: isolated.ambientAuth,
|
|
157
|
+
isolatedAuth: isolated.isolatedAuth,
|
|
158
|
+
writeProbe: isolated.writeProbe,
|
|
159
|
+
...options.signal ? { signal: options.signal } : {}
|
|
160
|
+
}) : void 0;
|
|
161
|
+
return await new Promise((resolve, reject) => {
|
|
162
|
+
let child;
|
|
163
|
+
try {
|
|
164
|
+
child = spawnImpl(command, args, { cwd, env, stdio: "pipe" });
|
|
165
|
+
} catch (err) {
|
|
166
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
child.stdin?.end();
|
|
170
|
+
let stdout = "";
|
|
171
|
+
let stderr = "";
|
|
172
|
+
let timedOut = false;
|
|
173
|
+
let settled = false;
|
|
174
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
175
|
+
timedOut = true;
|
|
176
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
177
|
+
}, timeoutMs) : null;
|
|
178
|
+
if (timer && typeof timer.unref === "function") {
|
|
179
|
+
;
|
|
180
|
+
timer.unref();
|
|
181
|
+
}
|
|
182
|
+
const onAbort = () => {
|
|
183
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
184
|
+
};
|
|
185
|
+
if (options.signal) {
|
|
186
|
+
if (options.signal.aborted) onAbort();
|
|
187
|
+
else options.signal.addEventListener("abort", onAbort, { once: true });
|
|
188
|
+
}
|
|
189
|
+
child.stdout?.on("data", (chunk) => {
|
|
190
|
+
stdout += String(chunk);
|
|
191
|
+
});
|
|
192
|
+
child.stderr?.on("data", (chunk) => {
|
|
193
|
+
stderr += String(chunk);
|
|
194
|
+
});
|
|
195
|
+
const finalize = (result) => {
|
|
196
|
+
if (settled) return;
|
|
197
|
+
settled = true;
|
|
198
|
+
if (timer) clearTimeout(timer);
|
|
199
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
200
|
+
resolve(result);
|
|
201
|
+
};
|
|
202
|
+
child.on("error", (err) => {
|
|
203
|
+
if (settled) return;
|
|
204
|
+
settled = true;
|
|
205
|
+
if (timer) clearTimeout(timer);
|
|
206
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
207
|
+
reject(err);
|
|
208
|
+
});
|
|
209
|
+
child.on("close", (code, signal) => {
|
|
210
|
+
try {
|
|
211
|
+
const usage = options.codexReproducible ? parseCodexTokenUsage(stdout) : void 0;
|
|
212
|
+
finalize({
|
|
213
|
+
exitCode: code,
|
|
214
|
+
stdout,
|
|
215
|
+
stderr: redactCodexHome(stderr, env.CODEX_HOME),
|
|
216
|
+
killedBySignal: signal,
|
|
217
|
+
durationMs: Date.now() - startedAt,
|
|
218
|
+
timedOut,
|
|
219
|
+
...usage ? { usage } : {},
|
|
220
|
+
...evidence ? { evidence } : {}
|
|
221
|
+
});
|
|
222
|
+
} catch (err) {
|
|
223
|
+
if (settled) return;
|
|
224
|
+
settled = true;
|
|
225
|
+
if (timer) clearTimeout(timer);
|
|
226
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
227
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
} finally {
|
|
232
|
+
await isolated.cleanup();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
var CODEX_EXECUTION_POLICY = {
|
|
236
|
+
sessionPersistence: "ephemeral",
|
|
237
|
+
userConfig: false,
|
|
238
|
+
rules: false,
|
|
239
|
+
projectInstructions: false,
|
|
240
|
+
skillInstructions: false,
|
|
241
|
+
appInstructions: false,
|
|
242
|
+
toolSuggestions: false,
|
|
243
|
+
multiAgentInstructions: false,
|
|
244
|
+
sandbox: "workspace-write",
|
|
245
|
+
permissionProfile: "agent_runtime_reproducible",
|
|
246
|
+
approvalPolicy: "never",
|
|
247
|
+
shellNetwork: false,
|
|
248
|
+
webSearch: false,
|
|
249
|
+
serviceTier: "default",
|
|
250
|
+
shellEnvironment: "core-filtered",
|
|
251
|
+
loginShell: false,
|
|
252
|
+
credentialsReadable: false,
|
|
253
|
+
parentRepoRead: false,
|
|
254
|
+
gitMetadata: false,
|
|
255
|
+
temporaryDirectory: "workspace-private",
|
|
256
|
+
containerSockets: false
|
|
257
|
+
};
|
|
258
|
+
var FORBIDDEN_CODEX_PROMPT_MARKERS = [
|
|
259
|
+
"<skills_instructions>",
|
|
260
|
+
"# AGENTS.md instructions",
|
|
261
|
+
"<apps_instructions>",
|
|
262
|
+
"<recommended_plugins>",
|
|
263
|
+
"<multi_agent_mode>",
|
|
264
|
+
"You are `/root`, the primary agent"
|
|
265
|
+
];
|
|
266
|
+
async function collectCodexExecutionEvidence(opts) {
|
|
267
|
+
assertCodexReproducibleInvocation(opts.command, opts.args);
|
|
268
|
+
const prompt = opts.args[1];
|
|
269
|
+
if (opts.args[0] !== "exec" || typeof prompt !== "string" || prompt.length === 0) {
|
|
270
|
+
throw new Error("runLocalHarness: reproducible Codex invocation must be codex exec <prompt>");
|
|
271
|
+
}
|
|
272
|
+
const cliVersion = (await runCodexProbe(opts, ["--version"])).stdout.trim();
|
|
273
|
+
if (!/^codex-cli \d+\.\d+\.\d+(?:[-+].+)?$/.test(cliVersion)) {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`runLocalHarness: unexpected Codex CLI version output ${JSON.stringify(cliVersion)}`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
const hiddenEnvironmentChecks = [
|
|
279
|
+
"CODEX_HOME",
|
|
280
|
+
"CODEX_ACCESS_TOKEN",
|
|
281
|
+
"CODEX_API_KEY",
|
|
282
|
+
"OPENAI_API_KEY"
|
|
283
|
+
].map((name) => `test -z "\${${name}:-}"`).join(" && ");
|
|
284
|
+
const sandboxProof = [
|
|
285
|
+
hiddenEnvironmentChecks,
|
|
286
|
+
"test -r .",
|
|
287
|
+
'touch "$1"',
|
|
288
|
+
'rm "$1"',
|
|
289
|
+
'! test -r "$2"',
|
|
290
|
+
'! test -r "$3"',
|
|
291
|
+
'! test -r "$4"',
|
|
292
|
+
'touch "$TMPDIR/.agent-runtime-tmp-proof"',
|
|
293
|
+
'rm "$TMPDIR/.agent-runtime-tmp-proof"'
|
|
294
|
+
].join(" && ");
|
|
295
|
+
await runCodexProbe(opts, [
|
|
296
|
+
"sandbox",
|
|
297
|
+
"-P",
|
|
298
|
+
CODEX_PERMISSION_PROFILE,
|
|
299
|
+
"-C",
|
|
300
|
+
opts.cwd,
|
|
301
|
+
"sh",
|
|
302
|
+
"-c",
|
|
303
|
+
sandboxProof,
|
|
304
|
+
"sh",
|
|
305
|
+
opts.writeProbe,
|
|
306
|
+
opts.ambientAuth,
|
|
307
|
+
opts.isolatedAuth,
|
|
308
|
+
join(homedir(), "code")
|
|
309
|
+
]);
|
|
310
|
+
await runCodexProbe(opts, [
|
|
311
|
+
"sandbox",
|
|
312
|
+
"-P",
|
|
313
|
+
CODEX_PERMISSION_PROFILE,
|
|
314
|
+
"-C",
|
|
315
|
+
opts.cwd,
|
|
316
|
+
"python3",
|
|
317
|
+
"-c",
|
|
318
|
+
"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)",
|
|
319
|
+
"/var/run/docker.sock",
|
|
320
|
+
"/run/docker.sock",
|
|
321
|
+
"/var/run/containerd/containerd.sock",
|
|
322
|
+
"/run/containerd/containerd.sock"
|
|
323
|
+
]);
|
|
324
|
+
const debugArgs = codexPromptDebugArgs(opts.args, prompt);
|
|
325
|
+
const renderedPrompt = (await runCodexProbe(opts, debugArgs)).stdout;
|
|
326
|
+
let parsedPrompt;
|
|
327
|
+
try {
|
|
328
|
+
parsedPrompt = JSON.parse(renderedPrompt);
|
|
329
|
+
} catch {
|
|
330
|
+
throw new Error("runLocalHarness: codex debug prompt-input returned invalid JSON");
|
|
331
|
+
}
|
|
332
|
+
if (!Array.isArray(parsedPrompt) || !jsonTextContains(parsedPrompt, prompt)) {
|
|
333
|
+
throw new Error("runLocalHarness: Codex prompt evidence did not contain the exact task prompt");
|
|
334
|
+
}
|
|
335
|
+
const promptContextWithoutTask = JSON.stringify(redactJsonText(parsedPrompt, prompt));
|
|
336
|
+
for (const marker of FORBIDDEN_CODEX_PROMPT_MARKERS) {
|
|
337
|
+
if (promptContextWithoutTask.includes(marker)) {
|
|
338
|
+
throw new Error(`runLocalHarness: Codex prompt evidence contains forbidden marker ${marker}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
for (const marker of ["<permissions instructions>", "<environment_context>"]) {
|
|
342
|
+
if (!renderedPrompt.includes(marker)) {
|
|
343
|
+
throw new Error(`runLocalHarness: Codex prompt evidence is missing required marker ${marker}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const nonPromptArgs = [
|
|
347
|
+
opts.command,
|
|
348
|
+
...opts.args.map((arg, index) => index === 1 ? "<PROMPT>" : arg)
|
|
349
|
+
];
|
|
350
|
+
return {
|
|
351
|
+
cliVersion,
|
|
352
|
+
effectivePromptSha256: sha256(renderedPrompt),
|
|
353
|
+
nonPromptArgsSha256: sha256(JSON.stringify(nonPromptArgs)),
|
|
354
|
+
controlledConfigSha256: opts.controlledConfigSha256,
|
|
355
|
+
policy: { ...CODEX_EXECUTION_POLICY }
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function assertCodexReproducibleInvocation(command, args) {
|
|
359
|
+
const executable = basename(command);
|
|
360
|
+
if (executable !== "codex" && executable !== "codex.exe") {
|
|
361
|
+
throw new Error("runLocalHarness: reproducible Codex invocation must use the Codex executable");
|
|
362
|
+
}
|
|
363
|
+
const prompt = args[1];
|
|
364
|
+
const expectedPrefix = ["exec", prompt, ...CODEX_REPRODUCIBLE_ARGS];
|
|
365
|
+
const hasExactPrefix = expectedPrefix.every((arg, index) => args[index] === arg);
|
|
366
|
+
const tail = args.slice(expectedPrefix.length);
|
|
367
|
+
const validModel = tail[0] === "-m" && typeof tail[1] === "string" && tail[1].length > 0 && !tail[1].startsWith("-");
|
|
368
|
+
const validReasoning = tail[2] === "-c" && typeof tail[3] === "string" && /^model_reasoning_effort="(?:none|minimal|low|medium|high|xhigh)"$/.test(tail[3]);
|
|
369
|
+
if (!hasExactPrefix || tail.length !== 4 || !validModel || !validReasoning) {
|
|
370
|
+
throw new Error(
|
|
371
|
+
"runLocalHarness: reproducible Codex invocation does not match the required isolated argv"
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function codexPromptDebugArgs(execArgs, prompt) {
|
|
376
|
+
const debugArgs = ["debug", "prompt-input"];
|
|
377
|
+
for (let index = 2; index < execArgs.length; index += 1) {
|
|
378
|
+
const arg = execArgs[index];
|
|
379
|
+
const value = execArgs[index + 1];
|
|
380
|
+
if (arg === "-c" && value !== void 0) {
|
|
381
|
+
debugArgs.push("-c", value);
|
|
382
|
+
index += 1;
|
|
383
|
+
} else if (arg === "--disable" && value !== void 0) {
|
|
384
|
+
debugArgs.push("--disable", value);
|
|
385
|
+
index += 1;
|
|
386
|
+
} else if (arg === "-m" && value !== void 0) {
|
|
387
|
+
debugArgs.push("-c", `model=${JSON.stringify(value)}`);
|
|
388
|
+
index += 1;
|
|
389
|
+
} else if (arg === "-s" && value !== void 0) {
|
|
390
|
+
debugArgs.push("-c", `sandbox_mode=${JSON.stringify(value)}`);
|
|
391
|
+
index += 1;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
debugArgs.push(prompt);
|
|
395
|
+
return debugArgs;
|
|
396
|
+
}
|
|
397
|
+
function runCodexProbe(opts, args) {
|
|
398
|
+
return new Promise((resolve, reject) => {
|
|
399
|
+
let child;
|
|
400
|
+
try {
|
|
401
|
+
child = opts.spawn(opts.command, args, { cwd: opts.cwd, env: opts.env, stdio: "pipe" });
|
|
402
|
+
} catch (err) {
|
|
403
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
child.stdin?.end();
|
|
407
|
+
let stdout = "";
|
|
408
|
+
let stderr = "";
|
|
409
|
+
let settled = false;
|
|
410
|
+
const timer = setTimeout(() => {
|
|
411
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
412
|
+
}, 1e4);
|
|
413
|
+
timer.unref?.();
|
|
414
|
+
const onAbort = () => {
|
|
415
|
+
if (!child.killed) child.kill("SIGTERM");
|
|
416
|
+
};
|
|
417
|
+
if (opts.signal) {
|
|
418
|
+
if (opts.signal.aborted) onAbort();
|
|
419
|
+
else opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
420
|
+
}
|
|
421
|
+
child.stdout?.on("data", (chunk) => {
|
|
422
|
+
stdout += String(chunk);
|
|
423
|
+
});
|
|
424
|
+
child.stderr?.on("data", (chunk) => {
|
|
425
|
+
stderr += String(chunk);
|
|
426
|
+
});
|
|
427
|
+
child.on("error", (err) => {
|
|
428
|
+
if (settled) return;
|
|
429
|
+
settled = true;
|
|
430
|
+
clearTimeout(timer);
|
|
431
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
432
|
+
reject(err);
|
|
433
|
+
});
|
|
434
|
+
child.on("close", (code, signal) => {
|
|
435
|
+
if (settled) return;
|
|
436
|
+
settled = true;
|
|
437
|
+
clearTimeout(timer);
|
|
438
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
439
|
+
if (code !== 0) {
|
|
440
|
+
const safeStderr = redactCodexHome(stderr, opts.env.CODEX_HOME);
|
|
441
|
+
reject(
|
|
442
|
+
new Error(
|
|
443
|
+
`runLocalHarness: Codex evidence probe failed (exit ${String(code)}, signal ${String(signal)}): ${safeStderr.trim()}`
|
|
444
|
+
)
|
|
445
|
+
);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
resolve({ stdout, stderr });
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
function redactCodexHome(value, codexHome) {
|
|
453
|
+
return codexHome ? value.replaceAll(codexHome, "<ISOLATED_CODEX_HOME>") : value;
|
|
454
|
+
}
|
|
455
|
+
function sha256(value) {
|
|
456
|
+
return createHash("sha256").update(value).digest("hex");
|
|
457
|
+
}
|
|
458
|
+
function jsonTextContains(value, needle) {
|
|
459
|
+
if (typeof value === "string") return value.includes(needle);
|
|
460
|
+
if (Array.isArray(value)) return value.some((item) => jsonTextContains(item, needle));
|
|
461
|
+
if (isRecord(value)) return Object.values(value).some((item) => jsonTextContains(item, needle));
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
function redactJsonText(value, text) {
|
|
465
|
+
if (typeof value === "string") return value.replaceAll(text, "");
|
|
466
|
+
if (Array.isArray(value)) return value.map((item) => redactJsonText(item, text));
|
|
467
|
+
if (isRecord(value)) {
|
|
468
|
+
return Object.fromEntries(
|
|
469
|
+
Object.entries(value).map(([key, item]) => [key, redactJsonText(item, text)])
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
return value;
|
|
473
|
+
}
|
|
474
|
+
var CODEX_AUTH_ENV = ["CODEX_ACCESS_TOKEN", "CODEX_API_KEY", "OPENAI_API_KEY"];
|
|
475
|
+
var CODEX_PERMISSION_PROFILE = "agent_runtime_reproducible";
|
|
476
|
+
async function isolateCodexHome(baseEnv, cwd) {
|
|
477
|
+
const isolatedHome = await mkdtemp(join(tmpdir(), "agent-runtime-codex-"));
|
|
478
|
+
await chmod(isolatedHome, 448);
|
|
479
|
+
let workspaceTemp = "";
|
|
480
|
+
const writeProbe = join(cwd, ".agent-runtime-sandbox-write-proof");
|
|
481
|
+
const cleanup = async () => {
|
|
482
|
+
await Promise.all([
|
|
483
|
+
rm(writeProbe, { force: true }),
|
|
484
|
+
...workspaceTemp ? [rm(workspaceTemp, { recursive: true, force: true })] : [],
|
|
485
|
+
rm(isolatedHome, { recursive: true, force: true })
|
|
486
|
+
]);
|
|
487
|
+
};
|
|
488
|
+
try {
|
|
489
|
+
workspaceTemp = await mkdtemp(join(cwd, ".agent-runtime-tmp-"));
|
|
490
|
+
await chmod(workspaceTemp, 448);
|
|
491
|
+
const ambientHome = baseEnv.CODEX_HOME?.trim() || join(homedir(), ".codex");
|
|
492
|
+
const ambientAuth = join(ambientHome, "auth.json");
|
|
493
|
+
const isolatedAuth = join(isolatedHome, "auth.json");
|
|
494
|
+
let linkedAuth = false;
|
|
495
|
+
try {
|
|
496
|
+
await access(ambientAuth, constants.R_OK);
|
|
497
|
+
await symlink(ambientAuth, isolatedAuth);
|
|
498
|
+
linkedAuth = true;
|
|
499
|
+
} catch (err) {
|
|
500
|
+
if (err.code !== "ENOENT") throw err;
|
|
501
|
+
}
|
|
502
|
+
if (!linkedAuth && !CODEX_AUTH_ENV.some((name) => Boolean(baseEnv[name]))) {
|
|
503
|
+
throw new Error(
|
|
504
|
+
"runLocalHarness: reproducible Codex mode requires readable auth.json or an API token environment variable"
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
const controlledConfig = codexControlledConfig({
|
|
508
|
+
ambientHome,
|
|
509
|
+
ambientTmp: baseEnv.TMPDIR?.trim() || tmpdir(),
|
|
510
|
+
workspaceTemp
|
|
511
|
+
});
|
|
512
|
+
await writeFile(join(isolatedHome, "config.toml"), controlledConfig, { mode: 384 });
|
|
513
|
+
return {
|
|
514
|
+
env: { ...baseEnv, CODEX_HOME: isolatedHome },
|
|
515
|
+
cleanup,
|
|
516
|
+
controlledConfigSha256: sha256(controlledConfig),
|
|
517
|
+
ambientAuth,
|
|
518
|
+
isolatedAuth,
|
|
519
|
+
writeProbe
|
|
520
|
+
};
|
|
521
|
+
} catch (err) {
|
|
522
|
+
await cleanup();
|
|
523
|
+
throw err;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function codexControlledConfig(opts) {
|
|
527
|
+
const home = homedir();
|
|
528
|
+
const deniedPaths = [
|
|
529
|
+
opts.ambientHome,
|
|
530
|
+
opts.ambientTmp,
|
|
531
|
+
join(home, "code"),
|
|
532
|
+
join(home, "company"),
|
|
533
|
+
join(home, ".ssh"),
|
|
534
|
+
join(home, ".aws"),
|
|
535
|
+
join(home, ".config"),
|
|
536
|
+
join(home, ".cache"),
|
|
537
|
+
join(home, ".docker"),
|
|
538
|
+
join(home, ".kube"),
|
|
539
|
+
join(home, ".gnupg"),
|
|
540
|
+
join(home, ".pki"),
|
|
541
|
+
join(home, ".npmrc"),
|
|
542
|
+
join(home, ".netrc"),
|
|
543
|
+
join(home, ".git-credentials"),
|
|
544
|
+
"/run/docker",
|
|
545
|
+
"/run/containerd",
|
|
546
|
+
opts.workspaceTemp
|
|
547
|
+
];
|
|
548
|
+
const filesystemRules = deniedPaths.map(
|
|
549
|
+
(path) => `${JSON.stringify(path)} = ${path === opts.workspaceTemp ? '"write"' : '"deny"'}`
|
|
550
|
+
).join("\n");
|
|
551
|
+
return `default_permissions = "${CODEX_PERMISSION_PROFILE}"
|
|
552
|
+
approval_policy = "never"
|
|
553
|
+
allow_login_shell = false
|
|
554
|
+
service_tier = "default"
|
|
555
|
+
|
|
556
|
+
[shell_environment_policy]
|
|
557
|
+
inherit = "core"
|
|
558
|
+
ignore_default_excludes = false
|
|
559
|
+
exclude = ["^CODEX_HOME$", "(?i).*TOKEN.*", "(?i).*KEY.*", "(?i).*SECRET.*", "(?i).*PASSWORD.*", "(?i).*CREDENTIAL.*", "(?i).*AUTH.*", "(?i).*COOKIE.*"]
|
|
560
|
+
set = { TMPDIR = ${JSON.stringify(opts.workspaceTemp)} }
|
|
561
|
+
|
|
562
|
+
[permissions.${CODEX_PERMISSION_PROFILE}]
|
|
563
|
+
extends = ":workspace"
|
|
564
|
+
|
|
565
|
+
[permissions.${CODEX_PERMISSION_PROFILE}.filesystem]
|
|
566
|
+
${filesystemRules}
|
|
567
|
+
":slash_tmp" = "deny"
|
|
568
|
+
|
|
569
|
+
[permissions.${CODEX_PERMISSION_PROFILE}.filesystem.":workspace_roots"]
|
|
570
|
+
"." = "write"
|
|
571
|
+
|
|
572
|
+
[permissions.${CODEX_PERMISSION_PROFILE}.network]
|
|
573
|
+
enabled = false
|
|
574
|
+
|
|
575
|
+
[permissions.${CODEX_PERMISSION_PROFILE}.network.unix_sockets]
|
|
576
|
+
"/var/run/docker.sock" = "deny"
|
|
577
|
+
"/run/docker.sock" = "deny"
|
|
578
|
+
"/var/run/containerd/containerd.sock" = "deny"
|
|
579
|
+
"/run/containerd/containerd.sock" = "deny"
|
|
580
|
+
`;
|
|
581
|
+
}
|
|
582
|
+
function parseCodexTokenUsage(stdout) {
|
|
583
|
+
const completed = [];
|
|
584
|
+
for (const [index, line] of stdout.split(/\r?\n/).entries()) {
|
|
585
|
+
if (line.trim().length === 0) continue;
|
|
586
|
+
let event2;
|
|
587
|
+
try {
|
|
588
|
+
event2 = JSON.parse(line);
|
|
589
|
+
} catch {
|
|
590
|
+
throw new Error(`runLocalHarness: Codex JSONL line ${index + 1} is not valid JSON`);
|
|
591
|
+
}
|
|
592
|
+
if (isRecord(event2) && event2.type === "turn.completed") completed.push(event2);
|
|
593
|
+
}
|
|
594
|
+
if (completed.length !== 1) {
|
|
595
|
+
throw new Error(
|
|
596
|
+
`runLocalHarness: expected exactly one Codex turn.completed usage event, received ${completed.length}`
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
const event = completed[0];
|
|
600
|
+
if (!isRecord(event) || !isRecord(event.usage)) {
|
|
601
|
+
throw new Error("runLocalHarness: Codex turn.completed event is missing usage");
|
|
602
|
+
}
|
|
603
|
+
const usage = event.usage;
|
|
604
|
+
const inputTokens = naturalNumber(usage.input_tokens, "input_tokens");
|
|
605
|
+
const cachedInputTokens = naturalNumber(usage.cached_input_tokens, "cached_input_tokens");
|
|
606
|
+
const outputTokens = naturalNumber(usage.output_tokens, "output_tokens");
|
|
607
|
+
const reasoningOutputTokens = naturalNumber(
|
|
608
|
+
usage.reasoning_output_tokens,
|
|
609
|
+
"reasoning_output_tokens"
|
|
610
|
+
);
|
|
611
|
+
if (cachedInputTokens > inputTokens) {
|
|
612
|
+
throw new Error("runLocalHarness: Codex cached_input_tokens exceeds input_tokens");
|
|
613
|
+
}
|
|
614
|
+
if (reasoningOutputTokens > outputTokens) {
|
|
615
|
+
throw new Error("runLocalHarness: Codex reasoning_output_tokens exceeds output_tokens");
|
|
616
|
+
}
|
|
617
|
+
return { inputTokens, cachedInputTokens, outputTokens, reasoningOutputTokens };
|
|
618
|
+
}
|
|
619
|
+
function isRecord(value) {
|
|
620
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
621
|
+
}
|
|
622
|
+
function naturalNumber(value, field) {
|
|
623
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
624
|
+
throw new Error(`runLocalHarness: Codex usage.${field} must be a non-negative safe integer`);
|
|
625
|
+
}
|
|
626
|
+
return value;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
export {
|
|
630
|
+
harnessInvocation,
|
|
631
|
+
runLocalHarness,
|
|
632
|
+
parseCodexTokenUsage
|
|
633
|
+
};
|
|
634
|
+
//# sourceMappingURL=chunk-PJNGPWTA.js.map
|