@f-o-h/cli 0.1.41 → 0.1.43
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 +3 -0
- package/dist/foh.js +495 -188
- package/examples/external-agent-metadata.example.json +19 -0
- package/examples/external-agent-run.example.json +1 -0
- package/examples/improvement-packet.example.json +6 -0
- package/package.json +1 -1
- package/schemas/external-agent-metadata.schema.json +30 -0
- package/schemas/external-agent-run.schema.json +1 -0
- package/schemas/improvement-packet.schema.json +30 -8
package/dist/foh.js
CHANGED
|
@@ -6046,7 +6046,7 @@ var require_compile = __commonJS({
|
|
|
6046
6046
|
const schOrFunc = root.refs[ref];
|
|
6047
6047
|
if (schOrFunc)
|
|
6048
6048
|
return schOrFunc;
|
|
6049
|
-
let _sch =
|
|
6049
|
+
let _sch = resolve13.call(this, root, ref);
|
|
6050
6050
|
if (_sch === void 0) {
|
|
6051
6051
|
const schema2 = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
6052
6052
|
const { schemaId } = this.opts;
|
|
@@ -6073,7 +6073,7 @@ var require_compile = __commonJS({
|
|
|
6073
6073
|
function sameSchemaEnv(s1, s2) {
|
|
6074
6074
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
6075
6075
|
}
|
|
6076
|
-
function
|
|
6076
|
+
function resolve13(root, ref) {
|
|
6077
6077
|
let sch;
|
|
6078
6078
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
6079
6079
|
ref = sch;
|
|
@@ -6648,55 +6648,55 @@ var require_fast_uri = __commonJS({
|
|
|
6648
6648
|
}
|
|
6649
6649
|
return uri;
|
|
6650
6650
|
}
|
|
6651
|
-
function
|
|
6651
|
+
function resolve13(baseURI, relativeURI, options) {
|
|
6652
6652
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
6653
6653
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
6654
6654
|
schemelessOptions.skipEscape = true;
|
|
6655
6655
|
return serialize(resolved, schemelessOptions);
|
|
6656
6656
|
}
|
|
6657
|
-
function resolveComponent(base,
|
|
6657
|
+
function resolveComponent(base, relative3, options, skipNormalization) {
|
|
6658
6658
|
const target = {};
|
|
6659
6659
|
if (!skipNormalization) {
|
|
6660
6660
|
base = parse3(serialize(base, options), options);
|
|
6661
|
-
|
|
6661
|
+
relative3 = parse3(serialize(relative3, options), options);
|
|
6662
6662
|
}
|
|
6663
6663
|
options = options || {};
|
|
6664
|
-
if (!options.tolerant &&
|
|
6665
|
-
target.scheme =
|
|
6666
|
-
target.userinfo =
|
|
6667
|
-
target.host =
|
|
6668
|
-
target.port =
|
|
6669
|
-
target.path = removeDotSegments(
|
|
6670
|
-
target.query =
|
|
6664
|
+
if (!options.tolerant && relative3.scheme) {
|
|
6665
|
+
target.scheme = relative3.scheme;
|
|
6666
|
+
target.userinfo = relative3.userinfo;
|
|
6667
|
+
target.host = relative3.host;
|
|
6668
|
+
target.port = relative3.port;
|
|
6669
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
6670
|
+
target.query = relative3.query;
|
|
6671
6671
|
} else {
|
|
6672
|
-
if (
|
|
6673
|
-
target.userinfo =
|
|
6674
|
-
target.host =
|
|
6675
|
-
target.port =
|
|
6676
|
-
target.path = removeDotSegments(
|
|
6677
|
-
target.query =
|
|
6672
|
+
if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
|
|
6673
|
+
target.userinfo = relative3.userinfo;
|
|
6674
|
+
target.host = relative3.host;
|
|
6675
|
+
target.port = relative3.port;
|
|
6676
|
+
target.path = removeDotSegments(relative3.path || "");
|
|
6677
|
+
target.query = relative3.query;
|
|
6678
6678
|
} else {
|
|
6679
|
-
if (!
|
|
6679
|
+
if (!relative3.path) {
|
|
6680
6680
|
target.path = base.path;
|
|
6681
|
-
if (
|
|
6682
|
-
target.query =
|
|
6681
|
+
if (relative3.query !== void 0) {
|
|
6682
|
+
target.query = relative3.query;
|
|
6683
6683
|
} else {
|
|
6684
6684
|
target.query = base.query;
|
|
6685
6685
|
}
|
|
6686
6686
|
} else {
|
|
6687
|
-
if (
|
|
6688
|
-
target.path = removeDotSegments(
|
|
6687
|
+
if (relative3.path[0] === "/") {
|
|
6688
|
+
target.path = removeDotSegments(relative3.path);
|
|
6689
6689
|
} else {
|
|
6690
6690
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
6691
|
-
target.path = "/" +
|
|
6691
|
+
target.path = "/" + relative3.path;
|
|
6692
6692
|
} else if (!base.path) {
|
|
6693
|
-
target.path =
|
|
6693
|
+
target.path = relative3.path;
|
|
6694
6694
|
} else {
|
|
6695
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
6695
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
|
|
6696
6696
|
}
|
|
6697
6697
|
target.path = removeDotSegments(target.path);
|
|
6698
6698
|
}
|
|
6699
|
-
target.query =
|
|
6699
|
+
target.query = relative3.query;
|
|
6700
6700
|
}
|
|
6701
6701
|
target.userinfo = base.userinfo;
|
|
6702
6702
|
target.host = base.host;
|
|
@@ -6704,7 +6704,7 @@ var require_fast_uri = __commonJS({
|
|
|
6704
6704
|
}
|
|
6705
6705
|
target.scheme = base.scheme;
|
|
6706
6706
|
}
|
|
6707
|
-
target.fragment =
|
|
6707
|
+
target.fragment = relative3.fragment;
|
|
6708
6708
|
return target;
|
|
6709
6709
|
}
|
|
6710
6710
|
function equal(uriA, uriB, options) {
|
|
@@ -6875,7 +6875,7 @@ var require_fast_uri = __commonJS({
|
|
|
6875
6875
|
var fastUri = {
|
|
6876
6876
|
SCHEMES,
|
|
6877
6877
|
normalize,
|
|
6878
|
-
resolve:
|
|
6878
|
+
resolve: resolve13,
|
|
6879
6879
|
resolveComponent,
|
|
6880
6880
|
equal,
|
|
6881
6881
|
serialize,
|
|
@@ -7213,8 +7213,8 @@ var require_core = __commonJS({
|
|
|
7213
7213
|
return this;
|
|
7214
7214
|
}
|
|
7215
7215
|
case "object": {
|
|
7216
|
-
const
|
|
7217
|
-
this._cache.delete(
|
|
7216
|
+
const cacheKey3 = schemaKeyRef;
|
|
7217
|
+
this._cache.delete(cacheKey3);
|
|
7218
7218
|
let id = schemaKeyRef[this.opts.schemaId];
|
|
7219
7219
|
if (id) {
|
|
7220
7220
|
id = (0, resolve_1.normalizeId)(id);
|
|
@@ -10158,21 +10158,21 @@ async function promptLine(label, {
|
|
|
10158
10158
|
allowEmpty = false,
|
|
10159
10159
|
defaultValue
|
|
10160
10160
|
} = {}) {
|
|
10161
|
-
return await new Promise((
|
|
10161
|
+
return await new Promise((resolve13) => {
|
|
10162
10162
|
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
10163
10163
|
const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout, terminal: true });
|
|
10164
10164
|
rl.question(`${label}${suffix}: `, (answer) => {
|
|
10165
10165
|
rl.close();
|
|
10166
10166
|
const value = String(answer ?? "").trim();
|
|
10167
10167
|
if (!value && typeof defaultValue === "string") {
|
|
10168
|
-
|
|
10168
|
+
resolve13(defaultValue);
|
|
10169
10169
|
return;
|
|
10170
10170
|
}
|
|
10171
10171
|
if (!value && !allowEmpty) {
|
|
10172
|
-
|
|
10172
|
+
resolve13("");
|
|
10173
10173
|
return;
|
|
10174
10174
|
}
|
|
10175
|
-
|
|
10175
|
+
resolve13(value);
|
|
10176
10176
|
});
|
|
10177
10177
|
});
|
|
10178
10178
|
}
|
|
@@ -10180,7 +10180,7 @@ async function promptSecret(label) {
|
|
|
10180
10180
|
if (!process.stdin.isTTY || !process.stdout.isTTY || typeof process.stdin.setRawMode !== "function") {
|
|
10181
10181
|
return await promptLine(label);
|
|
10182
10182
|
}
|
|
10183
|
-
return await new Promise((
|
|
10183
|
+
return await new Promise((resolve13) => {
|
|
10184
10184
|
const stdin = process.stdin;
|
|
10185
10185
|
const stdout = process.stdout;
|
|
10186
10186
|
const wasRaw = Boolean(stdin.isRaw);
|
|
@@ -10194,7 +10194,7 @@ async function promptSecret(label) {
|
|
|
10194
10194
|
const finish = () => {
|
|
10195
10195
|
cleanup();
|
|
10196
10196
|
stdout.write("\n");
|
|
10197
|
-
|
|
10197
|
+
resolve13(value);
|
|
10198
10198
|
};
|
|
10199
10199
|
const onData = (chunk) => {
|
|
10200
10200
|
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
@@ -10203,7 +10203,7 @@ async function promptSecret(label) {
|
|
|
10203
10203
|
cleanup();
|
|
10204
10204
|
process.exitCode = 130;
|
|
10205
10205
|
stdout.write("\n");
|
|
10206
|
-
return
|
|
10206
|
+
return resolve13("");
|
|
10207
10207
|
}
|
|
10208
10208
|
if (char === "\r" || char === "\n") {
|
|
10209
10209
|
finish();
|
|
@@ -10472,7 +10472,7 @@ async function storeAuthenticatedSession(params) {
|
|
|
10472
10472
|
return output;
|
|
10473
10473
|
}
|
|
10474
10474
|
function sleep(ms) {
|
|
10475
|
-
return new Promise((
|
|
10475
|
+
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
10476
10476
|
}
|
|
10477
10477
|
function hasExplicitTimeoutFlag(argv = process.argv) {
|
|
10478
10478
|
return argv.some((arg) => arg === "--timeout-seconds" || arg.startsWith("--timeout-seconds="));
|
|
@@ -11024,7 +11024,7 @@ async function pollUntil(check2, opts) {
|
|
|
11024
11024
|
}
|
|
11025
11025
|
}
|
|
11026
11026
|
function sleep2(ms) {
|
|
11027
|
-
return new Promise((
|
|
11027
|
+
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
11028
11028
|
}
|
|
11029
11029
|
|
|
11030
11030
|
// src/commands/compliance.ts
|
|
@@ -14119,8 +14119,8 @@ function registerAgentGuardrailCommands(agent) {
|
|
|
14119
14119
|
try {
|
|
14120
14120
|
rule = JSON.parse(opts.rule);
|
|
14121
14121
|
} catch {
|
|
14122
|
-
const { readFileSync:
|
|
14123
|
-
rule = JSON.parse(
|
|
14122
|
+
const { readFileSync: readFileSync14 } = await import("fs");
|
|
14123
|
+
rule = JSON.parse(readFileSync14(opts.rule, "utf-8"));
|
|
14124
14124
|
}
|
|
14125
14125
|
const data = await apiFetch(`/v1/console/agents/${opts.agent}/guardrails`, {
|
|
14126
14126
|
method: "POST",
|
|
@@ -14757,9 +14757,9 @@ function registerAgent(program3) {
|
|
|
14757
14757
|
process.stdout.write(yaml);
|
|
14758
14758
|
return;
|
|
14759
14759
|
}
|
|
14760
|
-
const { writeFileSync:
|
|
14760
|
+
const { writeFileSync: writeFileSync11 } = await import("fs");
|
|
14761
14761
|
const outputPath = opts.output ?? "tenant.yaml";
|
|
14762
|
-
|
|
14762
|
+
writeFileSync11(
|
|
14763
14763
|
outputPath,
|
|
14764
14764
|
`# tenant.yaml - Front Of House agent manifest
|
|
14765
14765
|
# Edit this file and run: foh plan tenant.yaml
|
|
@@ -16194,11 +16194,11 @@ function registerVoice(program3) {
|
|
|
16194
16194
|
}
|
|
16195
16195
|
const outputPath = String(opts.out || `foh-voice-preview-${provider}-${voiceId}.mp3`).trim();
|
|
16196
16196
|
const audio = Buffer.from(await res.arrayBuffer());
|
|
16197
|
-
const { mkdirSync:
|
|
16198
|
-
const { dirname:
|
|
16199
|
-
const absolutePath =
|
|
16200
|
-
|
|
16201
|
-
|
|
16197
|
+
const { mkdirSync: mkdirSync8, writeFileSync: writeFileSync11 } = await import("fs");
|
|
16198
|
+
const { dirname: dirname8, resolve: resolve13 } = await import("path");
|
|
16199
|
+
const absolutePath = resolve13(outputPath);
|
|
16200
|
+
mkdirSync8(dirname8(absolutePath), { recursive: true });
|
|
16201
|
+
writeFileSync11(absolutePath, audio);
|
|
16202
16202
|
format({
|
|
16203
16203
|
status: "ok",
|
|
16204
16204
|
provider,
|
|
@@ -30679,7 +30679,7 @@ var Protocol = class {
|
|
|
30679
30679
|
return;
|
|
30680
30680
|
}
|
|
30681
30681
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
30682
|
-
await new Promise((
|
|
30682
|
+
await new Promise((resolve13) => setTimeout(resolve13, pollInterval));
|
|
30683
30683
|
options?.signal?.throwIfAborted();
|
|
30684
30684
|
}
|
|
30685
30685
|
} catch (error2) {
|
|
@@ -30696,7 +30696,7 @@ var Protocol = class {
|
|
|
30696
30696
|
*/
|
|
30697
30697
|
request(request, resultSchema, options) {
|
|
30698
30698
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
30699
|
-
return new Promise((
|
|
30699
|
+
return new Promise((resolve13, reject) => {
|
|
30700
30700
|
const earlyReject = (error2) => {
|
|
30701
30701
|
reject(error2);
|
|
30702
30702
|
};
|
|
@@ -30774,7 +30774,7 @@ var Protocol = class {
|
|
|
30774
30774
|
if (!parseResult.success) {
|
|
30775
30775
|
reject(parseResult.error);
|
|
30776
30776
|
} else {
|
|
30777
|
-
|
|
30777
|
+
resolve13(parseResult.data);
|
|
30778
30778
|
}
|
|
30779
30779
|
} catch (error2) {
|
|
30780
30780
|
reject(error2);
|
|
@@ -31035,12 +31035,12 @@ var Protocol = class {
|
|
|
31035
31035
|
}
|
|
31036
31036
|
} catch {
|
|
31037
31037
|
}
|
|
31038
|
-
return new Promise((
|
|
31038
|
+
return new Promise((resolve13, reject) => {
|
|
31039
31039
|
if (signal.aborted) {
|
|
31040
31040
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
31041
31041
|
return;
|
|
31042
31042
|
}
|
|
31043
|
-
const timeoutId = setTimeout(
|
|
31043
|
+
const timeoutId = setTimeout(resolve13, interval);
|
|
31044
31044
|
signal.addEventListener("abort", () => {
|
|
31045
31045
|
clearTimeout(timeoutId);
|
|
31046
31046
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -32140,7 +32140,7 @@ var McpServer = class {
|
|
|
32140
32140
|
let task = createTaskResult.task;
|
|
32141
32141
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
32142
32142
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
32143
|
-
await new Promise((
|
|
32143
|
+
await new Promise((resolve13) => setTimeout(resolve13, pollInterval));
|
|
32144
32144
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
32145
32145
|
if (!updatedTask) {
|
|
32146
32146
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -32789,19 +32789,19 @@ var StdioServerTransport = class {
|
|
|
32789
32789
|
this.onclose?.();
|
|
32790
32790
|
}
|
|
32791
32791
|
send(message) {
|
|
32792
|
-
return new Promise((
|
|
32792
|
+
return new Promise((resolve13) => {
|
|
32793
32793
|
const json3 = serializeMessage(message);
|
|
32794
32794
|
if (this._stdout.write(json3)) {
|
|
32795
|
-
|
|
32795
|
+
resolve13();
|
|
32796
32796
|
} else {
|
|
32797
|
-
this._stdout.once("drain",
|
|
32797
|
+
this._stdout.once("drain", resolve13);
|
|
32798
32798
|
}
|
|
32799
32799
|
});
|
|
32800
32800
|
}
|
|
32801
32801
|
};
|
|
32802
32802
|
|
|
32803
32803
|
// src/lib/cli-version.ts
|
|
32804
|
-
var CLI_VERSION = "0.1.
|
|
32804
|
+
var CLI_VERSION = "0.1.42";
|
|
32805
32805
|
|
|
32806
32806
|
// src/commands/mcp-serve.ts
|
|
32807
32807
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -32986,7 +32986,7 @@ async function runFohCli(params) {
|
|
|
32986
32986
|
effectiveArgv.push("--json");
|
|
32987
32987
|
}
|
|
32988
32988
|
const command = `foh ${effectiveArgv.join(" ")}`;
|
|
32989
|
-
return await new Promise((
|
|
32989
|
+
return await new Promise((resolve13) => {
|
|
32990
32990
|
const child = (0, import_node_child_process.spawn)(process.execPath, [cliEntry, ...effectiveArgv], {
|
|
32991
32991
|
stdio: ["ignore", "pipe", "pipe"],
|
|
32992
32992
|
env: {
|
|
@@ -33011,7 +33011,7 @@ async function runFohCli(params) {
|
|
|
33011
33011
|
});
|
|
33012
33012
|
child.once("error", (error2) => {
|
|
33013
33013
|
clearTimeout(timeoutHandle);
|
|
33014
|
-
|
|
33014
|
+
resolve13({
|
|
33015
33015
|
ok: false,
|
|
33016
33016
|
command,
|
|
33017
33017
|
argv: effectiveArgv,
|
|
@@ -33027,7 +33027,7 @@ async function runFohCli(params) {
|
|
|
33027
33027
|
const stderrText = finalizeBoundedText(stderrBuffer);
|
|
33028
33028
|
const exitCode = Number.isFinite(code ?? NaN) ? Number(code) : 1;
|
|
33029
33029
|
const stdoutJson = tryParseJson(stdoutText);
|
|
33030
|
-
|
|
33030
|
+
resolve13({
|
|
33031
33031
|
ok: !timedOut && exitCode === 0,
|
|
33032
33032
|
command,
|
|
33033
33033
|
argv: effectiveArgv,
|
|
@@ -34405,6 +34405,12 @@ var SETUP_STEP_ORDER = [
|
|
|
34405
34405
|
"publish_agent",
|
|
34406
34406
|
"emit_summary"
|
|
34407
34407
|
];
|
|
34408
|
+
function extractGuardrailsList(response) {
|
|
34409
|
+
if (Array.isArray(response)) return response;
|
|
34410
|
+
if (Array.isArray(response.guardrails)) return response.guardrails;
|
|
34411
|
+
if (Array.isArray(response.rules)) return response.rules;
|
|
34412
|
+
return [];
|
|
34413
|
+
}
|
|
34408
34414
|
function resolveResumeIndex(resumeFromRaw) {
|
|
34409
34415
|
if (!resumeFromRaw) {
|
|
34410
34416
|
return { resumeFrom: null, resumeIndex: 0 };
|
|
@@ -34696,7 +34702,10 @@ function registerSetup(program3) {
|
|
|
34696
34702
|
}
|
|
34697
34703
|
await apiFetch("/v1/console/org/compliance/submit", {
|
|
34698
34704
|
method: "POST",
|
|
34699
|
-
body: JSON.stringify({
|
|
34705
|
+
body: JSON.stringify({
|
|
34706
|
+
type: "standard",
|
|
34707
|
+
friendlyName: opts.agentName
|
|
34708
|
+
}),
|
|
34700
34709
|
orgId: opts.org,
|
|
34701
34710
|
apiUrlOverride: opts.apiUrl
|
|
34702
34711
|
});
|
|
@@ -34842,12 +34851,18 @@ function registerSetup(program3) {
|
|
|
34842
34851
|
const existing = await apiFetch(`/v1/console/agents/${resolvedAgentId}/guardrails`, {
|
|
34843
34852
|
apiUrlOverride: opts.apiUrl
|
|
34844
34853
|
});
|
|
34845
|
-
if (
|
|
34854
|
+
if (extractGuardrailsList(existing).length > 0) {
|
|
34846
34855
|
return { step: "seed_guardrails", status: "skipped", detail: "guardrails already set" };
|
|
34847
34856
|
}
|
|
34848
34857
|
await apiFetch(`/v1/console/agents/${resolvedAgentId}/guardrails`, {
|
|
34849
34858
|
method: "POST",
|
|
34850
|
-
body: JSON.stringify({
|
|
34859
|
+
body: JSON.stringify({
|
|
34860
|
+
rule_id: "no-silent-drop",
|
|
34861
|
+
type: "focus",
|
|
34862
|
+
name: "NoSilentDrop",
|
|
34863
|
+
mode: "log",
|
|
34864
|
+
topics: ["property", "real estate", "viewing", "valuation", "buyer", "seller", "landlord", "tenant"]
|
|
34865
|
+
}),
|
|
34851
34866
|
apiUrlOverride: opts.apiUrl
|
|
34852
34867
|
});
|
|
34853
34868
|
return { step: "seed_guardrails", status: "done" };
|
|
@@ -35011,8 +35026,8 @@ function registerSetup(program3) {
|
|
|
35011
35026
|
}
|
|
35012
35027
|
try {
|
|
35013
35028
|
const manifest = await agentExport(resolvedAgentId, { apiUrlOverride: opts.apiUrl });
|
|
35014
|
-
const { writeFileSync:
|
|
35015
|
-
|
|
35029
|
+
const { writeFileSync: writeFileSync11 } = await import("fs");
|
|
35030
|
+
writeFileSync11(
|
|
35016
35031
|
"tenant.yaml",
|
|
35017
35032
|
`# tenant.yaml - Front Of House agent manifest
|
|
35018
35033
|
# Edit this file and run: foh plan tenant.yaml
|
|
@@ -35182,8 +35197,8 @@ function registerSim(program3) {
|
|
|
35182
35197
|
}
|
|
35183
35198
|
const cert = response.certificate;
|
|
35184
35199
|
if (opts.out) {
|
|
35185
|
-
const { writeFileSync:
|
|
35186
|
-
|
|
35200
|
+
const { writeFileSync: writeFileSync11 } = await import("fs");
|
|
35201
|
+
writeFileSync11(opts.out, JSON.stringify(cert, null, 2) + "\n", "utf-8");
|
|
35187
35202
|
process.stderr.write(` Certificate written to ${opts.out}
|
|
35188
35203
|
`);
|
|
35189
35204
|
}
|
|
@@ -35233,8 +35248,8 @@ function registerSim(program3) {
|
|
|
35233
35248
|
});
|
|
35234
35249
|
}
|
|
35235
35250
|
if (opts.out) {
|
|
35236
|
-
const { writeFileSync:
|
|
35237
|
-
|
|
35251
|
+
const { writeFileSync: writeFileSync11 } = await import("fs");
|
|
35252
|
+
writeFileSync11(opts.out, JSON.stringify(response.certificate, null, 2) + "\n", "utf-8");
|
|
35238
35253
|
process.stderr.write(` Final certificate written to ${opts.out}
|
|
35239
35254
|
`);
|
|
35240
35255
|
}
|
|
@@ -36678,7 +36693,7 @@ function inferReasonCode(artifact) {
|
|
|
36678
36693
|
function inferPromotionDecision(sourceType, reasonCode) {
|
|
36679
36694
|
const reason = String(reasonCode || "").toLowerCase();
|
|
36680
36695
|
if (sourceType === "external_agent_run") {
|
|
36681
|
-
if (reason.includes("friendlyname") || reason.includes("api") || reason.includes("500")) return "fix_api";
|
|
36696
|
+
if (reason.includes("friendlyname") || reason.includes("guardrail") || reason.includes("rule_id") || reason.includes("api") || reason.includes("http_4") || reason.includes("http_5") || reason.includes("404") || reason.includes("500") || reason.includes("roundtrip")) return "fix_api";
|
|
36682
36697
|
if (reason.includes("contact_phone") || reason.includes("voice_contact") || reason.includes("voice_not_included") || reason.includes("agent_limit") || reason.includes("org_") || reason.includes("template_apply")) return "fix_config";
|
|
36683
36698
|
if (reason.includes("exec_policy") || reason.includes("policy_blocked") || reason.includes("auth") || reason.includes("config")) return "fix_config";
|
|
36684
36699
|
if (reason.includes("cli") || reason.includes("command") || reason.includes("flag")) return "fix_cli";
|
|
@@ -36690,6 +36705,24 @@ function inferPromotionDecision(sourceType, reasonCode) {
|
|
|
36690
36705
|
if (sourceType === "replay_failure" || sourceType === "runtime_miss") return "add_test";
|
|
36691
36706
|
return "fix_runtime";
|
|
36692
36707
|
}
|
|
36708
|
+
function inferOwnerSubsystem(sourceType, reasonCode) {
|
|
36709
|
+
const reason = String(reasonCode || "").toLowerCase();
|
|
36710
|
+
if (sourceType === "external_agent_run") {
|
|
36711
|
+
if (reason.includes("simulation") || reason.includes("certification") || reason.includes("scenario")) return "dojo_certification";
|
|
36712
|
+
if (reason.includes("contact_phone") || reason.includes("voice_contact") || reason.includes("voice_not_included") || reason.includes("byon") || reason.includes("provider_capacity")) return "voice_contact";
|
|
36713
|
+
if (reason.includes("exec_policy") || reason.includes("policy_blocked") || reason.includes("sandbox") || reason.includes("runner") || reason.includes("codex")) return "infra_runner";
|
|
36714
|
+
if (reason.includes("friendlyname") || reason.includes("guardrail") || reason.includes("rule_id") || reason.includes("api") || reason.includes("http_4") || reason.includes("http_5") || reason.includes("404") || reason.includes("500") || reason.includes("roundtrip")) return "api_contract";
|
|
36715
|
+
if (reason.includes("cli") || reason.includes("command") || reason.includes("flag")) return "cli";
|
|
36716
|
+
if (reason.includes("docs") || reason.includes("unclear") || reason.includes("not_found")) return "docs";
|
|
36717
|
+
if (reason.includes("runtime") || reason.includes("widget") || reason.includes("proof")) return "runtime";
|
|
36718
|
+
return "product_ux";
|
|
36719
|
+
}
|
|
36720
|
+
if (sourceType === "knowledge_miss") return "docs";
|
|
36721
|
+
if (sourceType === "replay_failure" || sourceType === "runtime_miss") return "runtime";
|
|
36722
|
+
if (sourceType === "proof_failure" || sourceType === "live_proof_failure") return "runtime";
|
|
36723
|
+
if (sourceType === "setup_failure") return "product_ux";
|
|
36724
|
+
return "product_ux";
|
|
36725
|
+
}
|
|
36693
36726
|
function collectIds(artifact, explicit = {}) {
|
|
36694
36727
|
const source = getPath2(artifact, "source");
|
|
36695
36728
|
const ids = {
|
|
@@ -36729,6 +36762,13 @@ function defaultNextCommands(input) {
|
|
|
36729
36762
|
if (input.sourceType === "external_agent_run" && input.sourceArtifactPath) {
|
|
36730
36763
|
commands.push(`foh bug improve --from external-agent-run --file ${input.sourceArtifactPath} --json`);
|
|
36731
36764
|
}
|
|
36765
|
+
if (input.ownerSubsystem === "dojo_certification") {
|
|
36766
|
+
commands.push(input.ids.agent_id ? `foh sim certify-loop --agent ${input.ids.agent_id} --json` : "foh sim certify-loop --agent <agent_id> --json");
|
|
36767
|
+
}
|
|
36768
|
+
if (input.ownerSubsystem === "voice_contact") {
|
|
36769
|
+
commands.push("foh provision status --json");
|
|
36770
|
+
commands.push(input.ids.agent_id ? `foh prove --agent ${input.ids.agent_id} --mission voice --contact-path auto --json` : "foh prove --mission voice --contact-path auto --json");
|
|
36771
|
+
}
|
|
36732
36772
|
if (input.sourceArtifactPath) {
|
|
36733
36773
|
commands.push(`foh bug report --out test-results/bug-report.from-improvement.json --command "investigate ${input.reasonCode}" --request-url https://front-of-house-api.stldocs.app/api --response-status 500 --next-check "Review ${(0, import_path7.basename)(input.sourceArtifactPath)}" --json`);
|
|
36734
36774
|
}
|
|
@@ -36784,12 +36824,13 @@ function buildImprovementPacket(input) {
|
|
|
36784
36824
|
});
|
|
36785
36825
|
}
|
|
36786
36826
|
const promotionDecision = parseEnum(input.promotionDecision, IMPROVEMENT_DECISIONS, "--recommendation") ?? inferPromotionDecision(sourceType, reasonCode);
|
|
36827
|
+
const ownerSubsystem = inferOwnerSubsystem(sourceType, reasonCode);
|
|
36787
36828
|
const evidenceSummary = redactString(
|
|
36788
36829
|
nonEmpty2(input.evidenceSummary) ?? nonEmpty2(getPath2(artifact, "summary")) ?? `Improvement candidate generated from ${sourceType} with reason ${reasonCode}.`
|
|
36789
36830
|
);
|
|
36790
36831
|
const nextCommands = Array.from(new Set([
|
|
36791
36832
|
...input.nextCommands ?? [],
|
|
36792
|
-
...defaultNextCommands({ sourceType, ids, sourceArtifactPath: input.sourceArtifactPath, reasonCode })
|
|
36833
|
+
...defaultNextCommands({ sourceType, ids, sourceArtifactPath: input.sourceArtifactPath, reasonCode, ownerSubsystem })
|
|
36793
36834
|
].map((command) => command.trim()).filter(Boolean)));
|
|
36794
36835
|
const packet = {
|
|
36795
36836
|
schema_version: "foh_improvement_packet.v1",
|
|
@@ -36797,6 +36838,12 @@ function buildImprovementPacket(input) {
|
|
|
36797
36838
|
source_type: sourceType,
|
|
36798
36839
|
reason_code: reasonCode,
|
|
36799
36840
|
promotion_decision: promotionDecision,
|
|
36841
|
+
owner_subsystem: ownerSubsystem,
|
|
36842
|
+
routing: {
|
|
36843
|
+
owner_subsystem: ownerSubsystem,
|
|
36844
|
+
promotion_decision: promotionDecision,
|
|
36845
|
+
reason_family: ownerSubsystem
|
|
36846
|
+
},
|
|
36800
36847
|
ids,
|
|
36801
36848
|
evidence: {
|
|
36802
36849
|
summary: evidenceSummary,
|
|
@@ -37173,6 +37220,106 @@ function registerBug(program3) {
|
|
|
37173
37220
|
});
|
|
37174
37221
|
}
|
|
37175
37222
|
|
|
37223
|
+
// src/lib/proof-cache.ts
|
|
37224
|
+
var import_node_crypto2 = require("node:crypto");
|
|
37225
|
+
var import_node_fs2 = require("node:fs");
|
|
37226
|
+
var import_node_path = require("node:path");
|
|
37227
|
+
var DEFAULT_MAX_AGE_MS = 15 * 60 * 1e3;
|
|
37228
|
+
var DEFAULT_WAIT_MS = 180 * 1e3;
|
|
37229
|
+
var DEFAULT_POLL_MS = 500;
|
|
37230
|
+
function sleep3(ms) {
|
|
37231
|
+
return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
|
|
37232
|
+
}
|
|
37233
|
+
function stableJson(value) {
|
|
37234
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
37235
|
+
if (value && typeof value === "object") {
|
|
37236
|
+
return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`).join(",")}}`;
|
|
37237
|
+
}
|
|
37238
|
+
return JSON.stringify(value);
|
|
37239
|
+
}
|
|
37240
|
+
function cacheKey2(kind, keyParts) {
|
|
37241
|
+
return (0, import_node_crypto2.createHash)("sha256").update(stableJson({ kind, keyParts, schema_version: "foh_proof_cache_key.v1" })).digest("hex").slice(0, 32);
|
|
37242
|
+
}
|
|
37243
|
+
function publicPath(filePath) {
|
|
37244
|
+
const rel = (0, import_node_path.relative)(process.cwd(), filePath).replaceAll("\\", "/");
|
|
37245
|
+
return rel.startsWith("..") ? filePath.replaceAll("\\", "/") : rel;
|
|
37246
|
+
}
|
|
37247
|
+
function readFreshCache(filePath, maxAgeMs) {
|
|
37248
|
+
try {
|
|
37249
|
+
const payload = JSON.parse((0, import_node_fs2.readFileSync)(filePath, "utf8"));
|
|
37250
|
+
const createdAt = Date.parse(String(payload.created_at || ""));
|
|
37251
|
+
if (!Number.isFinite(createdAt)) return null;
|
|
37252
|
+
if (Date.now() - createdAt > maxAgeMs) return null;
|
|
37253
|
+
return payload.value ?? null;
|
|
37254
|
+
} catch {
|
|
37255
|
+
return null;
|
|
37256
|
+
}
|
|
37257
|
+
}
|
|
37258
|
+
function writeCache(filePath, value) {
|
|
37259
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path.dirname)(filePath), { recursive: true });
|
|
37260
|
+
(0, import_node_fs2.writeFileSync)(filePath, `${JSON.stringify({
|
|
37261
|
+
schema_version: "foh_proof_cache_entry.v1",
|
|
37262
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
37263
|
+
value
|
|
37264
|
+
}, null, 2)}
|
|
37265
|
+
`, "utf8");
|
|
37266
|
+
}
|
|
37267
|
+
function resolveProofCacheDir(input) {
|
|
37268
|
+
const value = String(input || process.env.FOH_PROOF_CACHE_DIR || "").trim();
|
|
37269
|
+
if (!value) return null;
|
|
37270
|
+
return (0, import_node_path.isAbsolute)(value) ? value : (0, import_node_path.resolve)(process.cwd(), value);
|
|
37271
|
+
}
|
|
37272
|
+
async function withProofCache(options, run) {
|
|
37273
|
+
const resolvedDir = resolveProofCacheDir(options.cacheDir);
|
|
37274
|
+
if (!resolvedDir) {
|
|
37275
|
+
return {
|
|
37276
|
+
value: await run(),
|
|
37277
|
+
metadata: { hit: false, key: "disabled", cache_path: "", waited_ms: 0 }
|
|
37278
|
+
};
|
|
37279
|
+
}
|
|
37280
|
+
const key = cacheKey2(options.kind, options.keyParts);
|
|
37281
|
+
const cachePath = (0, import_node_path.join)(resolvedDir, `${key}.json`);
|
|
37282
|
+
const lockPath = (0, import_node_path.join)(resolvedDir, `${key}.lock`);
|
|
37283
|
+
const maxAgeMs = options.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
37284
|
+
const waitMs = options.waitMs ?? Number(process.env.FOH_PROOF_CACHE_WAIT_MS || DEFAULT_WAIT_MS);
|
|
37285
|
+
const pollMs = options.pollMs ?? DEFAULT_POLL_MS;
|
|
37286
|
+
(0, import_node_fs2.mkdirSync)(resolvedDir, { recursive: true });
|
|
37287
|
+
const existing = readFreshCache(cachePath, maxAgeMs);
|
|
37288
|
+
if (existing) {
|
|
37289
|
+
return {
|
|
37290
|
+
value: existing,
|
|
37291
|
+
metadata: { hit: true, key, cache_path: publicPath(cachePath), waited_ms: 0 }
|
|
37292
|
+
};
|
|
37293
|
+
}
|
|
37294
|
+
let lockOwner = false;
|
|
37295
|
+
try {
|
|
37296
|
+
(0, import_node_fs2.mkdirSync)(lockPath);
|
|
37297
|
+
lockOwner = true;
|
|
37298
|
+
} catch {
|
|
37299
|
+
const started = Date.now();
|
|
37300
|
+
while (Date.now() - started < waitMs) {
|
|
37301
|
+
await sleep3(pollMs);
|
|
37302
|
+
const waitedValue = readFreshCache(cachePath, maxAgeMs);
|
|
37303
|
+
if (waitedValue) {
|
|
37304
|
+
return {
|
|
37305
|
+
value: waitedValue,
|
|
37306
|
+
metadata: { hit: true, key, cache_path: publicPath(cachePath), waited_ms: Date.now() - started }
|
|
37307
|
+
};
|
|
37308
|
+
}
|
|
37309
|
+
}
|
|
37310
|
+
}
|
|
37311
|
+
try {
|
|
37312
|
+
const value = await run();
|
|
37313
|
+
writeCache(cachePath, value);
|
|
37314
|
+
return {
|
|
37315
|
+
value,
|
|
37316
|
+
metadata: { hit: false, key, cache_path: publicPath(cachePath), waited_ms: 0 }
|
|
37317
|
+
};
|
|
37318
|
+
} finally {
|
|
37319
|
+
if (lockOwner) (0, import_node_fs2.rmSync)(lockPath, { recursive: true, force: true });
|
|
37320
|
+
}
|
|
37321
|
+
}
|
|
37322
|
+
|
|
37176
37323
|
// src/commands/prove.ts
|
|
37177
37324
|
function categoryForCheck(name) {
|
|
37178
37325
|
if (name === "auth") return "auth";
|
|
@@ -37268,11 +37415,12 @@ function isProviderCapacityBlocked(onboarding) {
|
|
|
37268
37415
|
return /maximum number of subaccounts|subaccount limit|reserve[- ]number pool|reserve pool exhausted|global safety limit/.test(message);
|
|
37269
37416
|
}
|
|
37270
37417
|
function registerProve(program3) {
|
|
37271
|
-
program3.command("prove").description("Produce one setup/runtime proof bundle for an agent").option("--agent <id>", "Agent ID to prove").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--cert-mode <m>", "Simulation cert mode: quick, full, stress", "quick").option("--cert-adaptive-runs <n>", "Adaptive runs for full/stress certification", "30").option("--cert-max-improvement-rounds <n>", "Max prompt improvement rounds in cert loop (0-5)", "1").option("--mission <mission>", "Proof mission: setup, widget, voice, publish", "setup").option("--contact-path <mode>", "Voice contact path: auto, managed, or byon", "auto").option("--mutation-mode <mode>", "Proof mutation mode: read-only or ensure", "read-only").option("--repair", "Alias for --mutation-mode ensure").option("--require-phone", "Hold proof if no phone/contact number is provisioned").option("--skip-cert", "Skip simulation certification check").option("--skip-smoke", "Skip widget runtime smoke check").option("--skip-voice-health", "Skip realtime voice provider health check").option("--out <path>", "Write signed proof report JSON to this path").option("--strict", "Exit non-zero unless all non-skipped checks pass").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
37418
|
+
program3.command("prove").description("Produce one setup/runtime proof bundle for an agent").option("--agent <id>", "Agent ID to prove").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--cert-mode <m>", "Simulation cert mode: quick, full, stress", "quick").option("--cert-adaptive-runs <n>", "Adaptive runs for full/stress certification", "30").option("--cert-max-improvement-rounds <n>", "Max prompt improvement rounds in cert loop (0-5)", "1").option("--mission <mission>", "Proof mission: setup, widget, voice, publish", "setup").option("--contact-path <mode>", "Voice contact path: auto, managed, or byon", "auto").option("--mutation-mode <mode>", "Proof mutation mode: read-only or ensure", "read-only").option("--repair", "Alias for --mutation-mode ensure").option("--require-phone", "Hold proof if no phone/contact number is provisioned").option("--skip-cert", "Skip simulation certification check").option("--skip-smoke", "Skip widget runtime smoke check").option("--skip-voice-health", "Skip realtime voice provider health check").option("--proof-cache-dir <path>", "Optional local proof cache directory for shared certification results").option("--out <path>", "Write signed proof report JSON to this path").option("--strict", "Exit non-zero unless all non-skipped checks pass").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
37272
37419
|
const checks = [];
|
|
37273
37420
|
const mission = normalizeMission(opts.mission);
|
|
37274
37421
|
const contactPath = normalizeContactPath(opts.contactPath);
|
|
37275
37422
|
const mutationMode = normalizeMutationMode(opts.mutationMode, Boolean(opts.repair));
|
|
37423
|
+
let validationFingerprint = null;
|
|
37276
37424
|
const ctx = {
|
|
37277
37425
|
tokenPresent: false,
|
|
37278
37426
|
traceIds: [],
|
|
@@ -37354,6 +37502,7 @@ function registerProve(program3) {
|
|
|
37354
37502
|
apiUrlOverride: opts.apiUrl
|
|
37355
37503
|
});
|
|
37356
37504
|
const issues = Array.isArray(validation.issues) ? validation.issues : [];
|
|
37505
|
+
validationFingerprint = validation;
|
|
37357
37506
|
if (validation.ok === false || issues.length > 0) {
|
|
37358
37507
|
checks.push(hold("agent_validation", "agent_validation_issues", `Agent validation returned ${issues.length} issue(s).`, `foh agent validate --agent ${ctx.agentId} --json`, validation));
|
|
37359
37508
|
} else {
|
|
@@ -37526,21 +37675,42 @@ function registerProve(program3) {
|
|
|
37526
37675
|
} else {
|
|
37527
37676
|
try {
|
|
37528
37677
|
const certMode = normalizeAgentCertMode(opts.certMode);
|
|
37529
|
-
const
|
|
37678
|
+
const agentId = ctx.agentId;
|
|
37679
|
+
const adaptiveRuns = Math.max(1, Number(opts.certAdaptiveRuns ?? 30) || 30);
|
|
37680
|
+
const maxImprovementRounds = Math.max(0, Math.min(5, Number(opts.certMaxImprovementRounds ?? 1) || 1));
|
|
37681
|
+
const cached2 = await withProofCache({
|
|
37682
|
+
cacheDir: opts.proofCacheDir,
|
|
37683
|
+
kind: "simulation_certification",
|
|
37684
|
+
keyParts: {
|
|
37685
|
+
agent_id: agentId,
|
|
37686
|
+
org_id: ctx.orgId,
|
|
37687
|
+
api_url: opts.apiUrl || ctx.apiUrl || null,
|
|
37688
|
+
cert_mode: certMode,
|
|
37689
|
+
adaptive_runs: adaptiveRuns,
|
|
37690
|
+
max_improvement_rounds: maxImprovementRounds,
|
|
37691
|
+
validation_fingerprint: validationFingerprint
|
|
37692
|
+
}
|
|
37693
|
+
}, () => runSetupCertifyLoop(agentId, {
|
|
37530
37694
|
mode: certMode,
|
|
37531
|
-
adaptiveRuns
|
|
37532
|
-
maxImprovementRounds
|
|
37695
|
+
adaptiveRuns,
|
|
37696
|
+
maxImprovementRounds,
|
|
37533
37697
|
orgId: ctx.orgId,
|
|
37534
37698
|
apiUrlOverride: opts.apiUrl
|
|
37535
|
-
});
|
|
37699
|
+
}));
|
|
37700
|
+
const loop = cached2.value;
|
|
37701
|
+
const loopWithCache = {
|
|
37702
|
+
...loop,
|
|
37703
|
+
proof_cache: cached2.metadata
|
|
37704
|
+
};
|
|
37536
37705
|
if (!loop.overall_pass) {
|
|
37537
|
-
checks.push(hold("simulation_certification", "simulation_certification_failed", "Simulation certification did not pass.", `foh sim certify-loop --agent ${
|
|
37706
|
+
checks.push(hold("simulation_certification", "simulation_certification_failed", "Simulation certification did not pass.", `foh sim certify-loop --agent ${agentId} --${certMode === "quick" ? "full" : certMode} --json`, loopWithCache));
|
|
37538
37707
|
} else {
|
|
37539
37708
|
checks.push(pass("simulation_certification", "Simulation certification passed.", {
|
|
37540
37709
|
mode: loop.mode,
|
|
37541
37710
|
attempts: loop.attempts?.length ?? 0,
|
|
37542
37711
|
improvement_runs: loop.improvement_runs,
|
|
37543
|
-
scenario_summary: loop.certificate?.scenario_summary
|
|
37712
|
+
scenario_summary: loop.certificate?.scenario_summary,
|
|
37713
|
+
proof_cache: cached2.metadata
|
|
37544
37714
|
}));
|
|
37545
37715
|
}
|
|
37546
37716
|
} catch (error2) {
|
|
@@ -38034,7 +38204,7 @@ async function runSelf(args, apiUrlOverride) {
|
|
|
38034
38204
|
if (apiUrlOverride && !spawnArgs.includes("--api-url")) {
|
|
38035
38205
|
spawnArgs.push("--api-url", apiUrlOverride);
|
|
38036
38206
|
}
|
|
38037
|
-
return await new Promise((
|
|
38207
|
+
return await new Promise((resolve13, reject) => {
|
|
38038
38208
|
const child = (0, import_child_process2.spawn)(process.execPath, [process.argv[1], ...spawnArgs], {
|
|
38039
38209
|
stdio: "inherit",
|
|
38040
38210
|
env: {
|
|
@@ -38044,7 +38214,7 @@ async function runSelf(args, apiUrlOverride) {
|
|
|
38044
38214
|
}
|
|
38045
38215
|
});
|
|
38046
38216
|
child.once("error", reject);
|
|
38047
|
-
child.once("close", (code) =>
|
|
38217
|
+
child.once("close", (code) => resolve13(typeof code === "number" ? code : 1));
|
|
38048
38218
|
});
|
|
38049
38219
|
}
|
|
38050
38220
|
function shouldUseInteractiveHome(argv) {
|
|
@@ -38422,17 +38592,17 @@ function detectUpdateAvailability(currentVersion, cwd = process.cwd()) {
|
|
|
38422
38592
|
async function applyRepoUpdate(repoRoot) {
|
|
38423
38593
|
const scriptPath = (0, import_path9.join)(repoRoot, "scripts", "Install-FohCli.ps1");
|
|
38424
38594
|
if (process.platform === "win32") {
|
|
38425
|
-
return await new Promise((
|
|
38595
|
+
return await new Promise((resolve13, reject) => {
|
|
38426
38596
|
const child = (0, import_child_process3.spawn)(
|
|
38427
38597
|
"powershell",
|
|
38428
38598
|
["-ExecutionPolicy", "Bypass", "-File", scriptPath],
|
|
38429
38599
|
{ stdio: "inherit" }
|
|
38430
38600
|
);
|
|
38431
38601
|
child.once("error", reject);
|
|
38432
|
-
child.once("close", (code) =>
|
|
38602
|
+
child.once("close", (code) => resolve13(typeof code === "number" ? code : 1));
|
|
38433
38603
|
});
|
|
38434
38604
|
}
|
|
38435
|
-
return await new Promise((
|
|
38605
|
+
return await new Promise((resolve13, reject) => {
|
|
38436
38606
|
const child = (0, import_child_process3.spawn)(
|
|
38437
38607
|
"corepack",
|
|
38438
38608
|
["pnpm", "cli:install:global"],
|
|
@@ -38442,7 +38612,7 @@ async function applyRepoUpdate(repoRoot) {
|
|
|
38442
38612
|
}
|
|
38443
38613
|
);
|
|
38444
38614
|
child.once("error", reject);
|
|
38445
|
-
child.once("close", (code) =>
|
|
38615
|
+
child.once("close", (code) => resolve13(typeof code === "number" ? code : 1));
|
|
38446
38616
|
});
|
|
38447
38617
|
}
|
|
38448
38618
|
function shouldShowUpdateNotice(argv = process.argv) {
|
|
@@ -38578,8 +38748,8 @@ function registerUpdate(program3) {
|
|
|
38578
38748
|
}
|
|
38579
38749
|
|
|
38580
38750
|
// src/commands/eval.ts
|
|
38581
|
-
var
|
|
38582
|
-
var
|
|
38751
|
+
var import_fs16 = require("fs");
|
|
38752
|
+
var import_path14 = require("path");
|
|
38583
38753
|
var import_child_process5 = require("child_process");
|
|
38584
38754
|
|
|
38585
38755
|
// src/lib/external-agent-artifact-safety.ts
|
|
@@ -38869,6 +39039,8 @@ function completeExternalAgentCliInvocation(capture, input) {
|
|
|
38869
39039
|
const completedAt = input.completedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
38870
39040
|
const output = redactPath(String(input.output || "").slice(0, 256 * 1024));
|
|
38871
39041
|
const parsed = parseEnvelope(output);
|
|
39042
|
+
const exitCode = Number.isInteger(input.exitCode) ? Number(input.exitCode) : null;
|
|
39043
|
+
const status = parsed.status ?? (exitCode === 0 ? "pass" : exitCode === null ? null : "fail");
|
|
38872
39044
|
let artifact = null;
|
|
38873
39045
|
if (output.trim()) {
|
|
38874
39046
|
artifact = outputArtifactName(capture.commandId);
|
|
@@ -38888,8 +39060,8 @@ function completeExternalAgentCliInvocation(capture, input) {
|
|
|
38888
39060
|
started_at: capture.startedAt,
|
|
38889
39061
|
completed_at: completedAt,
|
|
38890
39062
|
duration_ms: Math.max(0, Date.parse(completedAt) - Date.parse(capture.startedAt)),
|
|
38891
|
-
exit_code:
|
|
38892
|
-
status
|
|
39063
|
+
exit_code: exitCode,
|
|
39064
|
+
status,
|
|
38893
39065
|
reason_code: parsed.reasonCode,
|
|
38894
39066
|
check_reason_codes: parsed.checkReasonCodes,
|
|
38895
39067
|
output_artifact: artifact,
|
|
@@ -38912,10 +39084,62 @@ function readCommandRecords(runDir) {
|
|
|
38912
39084
|
}
|
|
38913
39085
|
|
|
38914
39086
|
// src/lib/external-agent-executor.ts
|
|
38915
|
-
var
|
|
39087
|
+
var import_fs15 = require("fs");
|
|
38916
39088
|
var import_os2 = require("os");
|
|
38917
|
-
var
|
|
39089
|
+
var import_path13 = require("path");
|
|
38918
39090
|
var import_child_process4 = require("child_process");
|
|
39091
|
+
|
|
39092
|
+
// src/lib/external-agent-metadata.ts
|
|
39093
|
+
var import_fs14 = require("fs");
|
|
39094
|
+
var import_path12 = require("path");
|
|
39095
|
+
var EXTERNAL_AGENT_METADATA_FILENAMES = [
|
|
39096
|
+
"external-agent-metadata.json",
|
|
39097
|
+
"agent-metadata.json"
|
|
39098
|
+
];
|
|
39099
|
+
var PUBLIC_DOC_URL_RE = /^https:\/\/frontofhouse\.okii\.uk(?:\/[A-Za-z0-9._~:/?#[\]@!$&'()*+,;=%-]*)?$/;
|
|
39100
|
+
function normalizeDocUrl(value) {
|
|
39101
|
+
const raw = typeof value === "string" ? value : value && typeof value === "object" && typeof value.url === "string" ? String(value.url) : "";
|
|
39102
|
+
const url2 = raw.trim().replace(/[.?!:]+$/g, "");
|
|
39103
|
+
if (!PUBLIC_DOC_URL_RE.test(url2)) return null;
|
|
39104
|
+
return url2;
|
|
39105
|
+
}
|
|
39106
|
+
function collectDocsFrom(value, docs) {
|
|
39107
|
+
if (Array.isArray(value)) {
|
|
39108
|
+
for (const entry of value) {
|
|
39109
|
+
const url2 = normalizeDocUrl(entry);
|
|
39110
|
+
if (url2) docs.add(url2);
|
|
39111
|
+
}
|
|
39112
|
+
}
|
|
39113
|
+
}
|
|
39114
|
+
function readExternalAgentMetadata(runDir) {
|
|
39115
|
+
for (const filename of EXTERNAL_AGENT_METADATA_FILENAMES) {
|
|
39116
|
+
const path2 = (0, import_path12.join)(runDir, filename);
|
|
39117
|
+
if (!(0, import_fs14.existsSync)(path2)) continue;
|
|
39118
|
+
try {
|
|
39119
|
+
const parsed = JSON.parse((0, import_fs14.readFileSync)(path2, "utf8"));
|
|
39120
|
+
const docs = /* @__PURE__ */ new Set();
|
|
39121
|
+
collectDocsFrom(parsed.docs_pages_used, docs);
|
|
39122
|
+
collectDocsFrom(parsed.docs_pages_observed, docs);
|
|
39123
|
+
collectDocsFrom(parsed.docs_used, docs);
|
|
39124
|
+
collectDocsFrom(parsed.public_docs_used, docs);
|
|
39125
|
+
return {
|
|
39126
|
+
path: filename,
|
|
39127
|
+
docs_pages_used: Array.from(docs).sort()
|
|
39128
|
+
};
|
|
39129
|
+
} catch {
|
|
39130
|
+
return {
|
|
39131
|
+
path: filename,
|
|
39132
|
+
docs_pages_used: []
|
|
39133
|
+
};
|
|
39134
|
+
}
|
|
39135
|
+
}
|
|
39136
|
+
return {
|
|
39137
|
+
path: null,
|
|
39138
|
+
docs_pages_used: []
|
|
39139
|
+
};
|
|
39140
|
+
}
|
|
39141
|
+
|
|
39142
|
+
// src/lib/external-agent-executor.ts
|
|
38919
39143
|
var CODEX_EXECUTOR_DENIED_ENV_PREFIXES = [
|
|
38920
39144
|
"SUPABASE_",
|
|
38921
39145
|
"DATABASE_",
|
|
@@ -38985,7 +39209,7 @@ function buildCodexExecutorEnv(input) {
|
|
|
38985
39209
|
env[childKey] = value;
|
|
38986
39210
|
}
|
|
38987
39211
|
}
|
|
38988
|
-
env.npm_config_cache = (0,
|
|
39212
|
+
env.npm_config_cache = (0, import_path13.join)((0, import_path13.dirname)(input.runDir), ".npm-cache");
|
|
38989
39213
|
env.npm_config_prefer_online = "true";
|
|
38990
39214
|
env.npm_config_update_notifier = "false";
|
|
38991
39215
|
env.npm_config_yes = "true";
|
|
@@ -38996,14 +39220,14 @@ function buildCodexExecutorEnv(input) {
|
|
|
38996
39220
|
return env;
|
|
38997
39221
|
}
|
|
38998
39222
|
function normalizeForCompare(path2) {
|
|
38999
|
-
const resolved = (0,
|
|
39223
|
+
const resolved = (0, import_path13.resolve)(path2);
|
|
39000
39224
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
39001
39225
|
}
|
|
39002
39226
|
function isPathInside(childPath, parentPath) {
|
|
39003
39227
|
const child = normalizeForCompare(childPath);
|
|
39004
39228
|
const parent = normalizeForCompare(parentPath);
|
|
39005
|
-
const rel = (0,
|
|
39006
|
-
return rel === "" || !!rel && !rel.startsWith("..") && !(0,
|
|
39229
|
+
const rel = (0, import_path13.relative)(parent, child);
|
|
39230
|
+
return rel === "" || !!rel && !rel.startsWith("..") && !(0, import_path13.isAbsolute)(rel);
|
|
39007
39231
|
}
|
|
39008
39232
|
function requireString(value, field) {
|
|
39009
39233
|
if (typeof value !== "string" || value.trim() === "") {
|
|
@@ -39012,10 +39236,10 @@ function requireString(value, field) {
|
|
|
39012
39236
|
return value;
|
|
39013
39237
|
}
|
|
39014
39238
|
function readBatch(batchPath) {
|
|
39015
|
-
if (!(0,
|
|
39239
|
+
if (!(0, import_fs15.existsSync)(batchPath)) {
|
|
39016
39240
|
throw new ExternalAgentExecutorError("external_agent_batch_not_found", `Batch file not found: ${batchPath}`);
|
|
39017
39241
|
}
|
|
39018
|
-
const parsed = JSON.parse((0,
|
|
39242
|
+
const parsed = JSON.parse((0, import_fs15.readFileSync)(batchPath, "utf8"));
|
|
39019
39243
|
if (parsed.schema_version !== "external_agent_batch_plan.v1") {
|
|
39020
39244
|
throw new ExternalAgentExecutorError("invalid_external_agent_batch", "Batch schema_version must be external_agent_batch_plan.v1.");
|
|
39021
39245
|
}
|
|
@@ -39040,12 +39264,29 @@ function defaultRunnerProbe(command, args) {
|
|
|
39040
39264
|
function quotePowerShellArg(value) {
|
|
39041
39265
|
return `'${value.replace(/'/g, "''")}'`;
|
|
39042
39266
|
}
|
|
39267
|
+
function quoteShellArg(value) {
|
|
39268
|
+
const text = String(value);
|
|
39269
|
+
if (/^[A-Za-z0-9_./:=@-]+$/.test(text)) return text;
|
|
39270
|
+
return `"${text.replace(/(["$`])/g, "\\$1")}"`;
|
|
39271
|
+
}
|
|
39272
|
+
function externalAgentSummaryCommand(root) {
|
|
39273
|
+
return [
|
|
39274
|
+
"node",
|
|
39275
|
+
"scripts/summarize-external-agent-runs.mjs",
|
|
39276
|
+
"--root",
|
|
39277
|
+
quoteShellArg(root),
|
|
39278
|
+
"--out",
|
|
39279
|
+
quoteShellArg((0, import_path13.join)(root, "latest-summary.json")),
|
|
39280
|
+
"--report",
|
|
39281
|
+
quoteShellArg((0, import_path13.join)(root, "summary.report.json"))
|
|
39282
|
+
].join(" ");
|
|
39283
|
+
}
|
|
39043
39284
|
function resolveCodexProbeCommand() {
|
|
39044
39285
|
if (process.platform !== "win32") return "codex";
|
|
39045
39286
|
const appData = process.env.APPDATA;
|
|
39046
39287
|
if (appData) {
|
|
39047
|
-
const appDataShim = (0,
|
|
39048
|
-
if ((0,
|
|
39288
|
+
const appDataShim = (0, import_path13.join)(appData, "npm", "codex.cmd");
|
|
39289
|
+
if ((0, import_fs15.existsSync)(appDataShim)) return appDataShim;
|
|
39049
39290
|
}
|
|
39050
39291
|
return "codex.cmd";
|
|
39051
39292
|
}
|
|
@@ -39057,6 +39298,13 @@ function validateCodexRunner(options) {
|
|
|
39057
39298
|
return {
|
|
39058
39299
|
binaryChecked: false,
|
|
39059
39300
|
requiredFlagsChecked: false,
|
|
39301
|
+
version: null,
|
|
39302
|
+
rootHelpChecked: false,
|
|
39303
|
+
execHelpChecked: false,
|
|
39304
|
+
supportsModernApprovalMode: true,
|
|
39305
|
+
supportsLegacyFullAuto: false,
|
|
39306
|
+
supportsYoloAlias: null,
|
|
39307
|
+
yoloPolicy: "not_checked",
|
|
39060
39308
|
automationMode: "ask-for-approval-never",
|
|
39061
39309
|
globalArgs: ["--ask-for-approval", "never"],
|
|
39062
39310
|
execArgs: []
|
|
@@ -39068,6 +39316,8 @@ function validateCodexRunner(options) {
|
|
|
39068
39316
|
if (version2.error || version2.status !== 0) {
|
|
39069
39317
|
throw new ExternalAgentExecutorError("external_agent_runner_binary_missing", "Codex runner probe failed: `codex --version` did not exit 0.");
|
|
39070
39318
|
}
|
|
39319
|
+
const versionText = `${version2.stdout}
|
|
39320
|
+
${version2.stderr}`.trim() || null;
|
|
39071
39321
|
const help = probe(probeCommand, ["exec", "--help"]);
|
|
39072
39322
|
if (help.error || help.status !== 0) {
|
|
39073
39323
|
throw new ExternalAgentExecutorError("external_agent_runner_help_unavailable", "Codex runner probe failed: `codex exec --help` did not exit 0.");
|
|
@@ -39077,6 +39327,11 @@ ${help.stderr}`;
|
|
|
39077
39327
|
const rootHelp = probe(probeCommand, ["--help"]);
|
|
39078
39328
|
const rootHelpText = `${rootHelp.stdout}
|
|
39079
39329
|
${rootHelp.stderr}`;
|
|
39330
|
+
const yoloHelp = probe(probeCommand, ["--yolo", "--help"]);
|
|
39331
|
+
const yoloHelpText = `${yoloHelp.stdout}
|
|
39332
|
+
${yoloHelp.stderr}`;
|
|
39333
|
+
const supportsYoloAlias = yoloHelp.status === 0 && /(?:Usage:\s*codex|Codex CLI)/i.test(yoloHelpText);
|
|
39334
|
+
const yoloPolicy = supportsYoloAlias ? "observed_not_canonical" : "unsupported";
|
|
39080
39335
|
const commonExecFlags = [
|
|
39081
39336
|
"--cd",
|
|
39082
39337
|
"--skip-git-repo-check",
|
|
@@ -39102,6 +39357,13 @@ ${rootHelp.stderr}`;
|
|
|
39102
39357
|
return {
|
|
39103
39358
|
binaryChecked: true,
|
|
39104
39359
|
requiredFlagsChecked: true,
|
|
39360
|
+
version: versionText,
|
|
39361
|
+
rootHelpChecked: rootHelp.status === 0,
|
|
39362
|
+
execHelpChecked: true,
|
|
39363
|
+
supportsModernApprovalMode,
|
|
39364
|
+
supportsLegacyFullAuto,
|
|
39365
|
+
supportsYoloAlias,
|
|
39366
|
+
yoloPolicy,
|
|
39105
39367
|
automationMode: "ask-for-approval-never",
|
|
39106
39368
|
globalArgs: ["--ask-for-approval", "never"],
|
|
39107
39369
|
execArgs: []
|
|
@@ -39110,6 +39372,13 @@ ${rootHelp.stderr}`;
|
|
|
39110
39372
|
return {
|
|
39111
39373
|
binaryChecked: true,
|
|
39112
39374
|
requiredFlagsChecked: true,
|
|
39375
|
+
version: versionText,
|
|
39376
|
+
rootHelpChecked: rootHelp.status === 0,
|
|
39377
|
+
execHelpChecked: true,
|
|
39378
|
+
supportsModernApprovalMode,
|
|
39379
|
+
supportsLegacyFullAuto,
|
|
39380
|
+
supportsYoloAlias,
|
|
39381
|
+
yoloPolicy,
|
|
39113
39382
|
automationMode: "full-auto",
|
|
39114
39383
|
globalArgs: [],
|
|
39115
39384
|
execArgs: ["--full-auto"]
|
|
@@ -39123,6 +39392,14 @@ function normalizeCodexSandboxBackend(value) {
|
|
|
39123
39392
|
`Unsupported Codex sandbox backend: ${value}. Use default or legacy-landlock.`
|
|
39124
39393
|
);
|
|
39125
39394
|
}
|
|
39395
|
+
function normalizeCodexSandboxMode(value) {
|
|
39396
|
+
const normalized = (value || "workspace-write").trim().toLowerCase();
|
|
39397
|
+
if (normalized === "workspace-write" || normalized === "danger-full-access") return normalized;
|
|
39398
|
+
throw new ExternalAgentExecutorError(
|
|
39399
|
+
"invalid_codex_sandbox_mode",
|
|
39400
|
+
`Unsupported Codex sandbox mode: ${value}. Use workspace-write or danger-full-access.`
|
|
39401
|
+
);
|
|
39402
|
+
}
|
|
39126
39403
|
function codexConfigArgs(input) {
|
|
39127
39404
|
const args = [];
|
|
39128
39405
|
if (input.backend === "legacy-landlock") {
|
|
@@ -39142,13 +39419,13 @@ function safeRunId(value) {
|
|
|
39142
39419
|
return value.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "") || "run";
|
|
39143
39420
|
}
|
|
39144
39421
|
function resolveWorkspaceRoot(input) {
|
|
39145
|
-
if (input.workspaceRoot) return (0,
|
|
39146
|
-
const batchStem = (0,
|
|
39147
|
-
const repoStem = (0,
|
|
39148
|
-
return (0,
|
|
39422
|
+
if (input.workspaceRoot) return (0, import_path13.resolve)(input.workspaceRoot);
|
|
39423
|
+
const batchStem = (0, import_path13.basename)((0, import_path13.resolve)(input.batchPath)).replace(/[^a-zA-Z0-9_.-]+/g, "-");
|
|
39424
|
+
const repoStem = (0, import_path13.basename)((0, import_path13.resolve)(input.privateRepoRoot)).replace(/[^a-zA-Z0-9_.-]+/g, "-");
|
|
39425
|
+
return (0, import_path13.resolve)((0, import_os2.tmpdir)(), "foh-external-agent-workspaces", repoStem, batchStem);
|
|
39149
39426
|
}
|
|
39150
39427
|
function promptVersionFromPath(promptPath) {
|
|
39151
|
-
const raw = (0,
|
|
39428
|
+
const raw = (0, import_fs15.readFileSync)(promptPath, "utf8");
|
|
39152
39429
|
if (raw.includes("Do not assume access to the private source repository")) return "blank-setup.v1";
|
|
39153
39430
|
return "unknown";
|
|
39154
39431
|
}
|
|
@@ -39157,12 +39434,13 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39157
39434
|
if (runner !== "codex") {
|
|
39158
39435
|
throw new ExternalAgentExecutorError("unsupported_external_agent_runner", `Unsupported runner: ${runner}`);
|
|
39159
39436
|
}
|
|
39160
|
-
const batchPath = (0,
|
|
39437
|
+
const batchPath = (0, import_path13.resolve)(options.batchPath);
|
|
39161
39438
|
const batch = readBatch(batchPath);
|
|
39162
39439
|
const runnerProbe = validateCodexRunner(options);
|
|
39163
39440
|
const codexSandboxBackend = normalizeCodexSandboxBackend(options.codexSandboxBackend);
|
|
39441
|
+
const codexSandboxMode = normalizeCodexSandboxMode(options.codexSandboxMode);
|
|
39164
39442
|
const codexNetworkAccess = options.codexNetworkAccess === true;
|
|
39165
|
-
const privateRepoRoot = (0,
|
|
39443
|
+
const privateRepoRoot = (0, import_path13.resolve)(options.privateRepoRoot || options.cwd || process.cwd());
|
|
39166
39444
|
const workspaceRoot = resolveWorkspaceRoot({ batchPath, workspaceRoot: options.workspaceRoot, privateRepoRoot });
|
|
39167
39445
|
if (isPathInside(workspaceRoot, privateRepoRoot)) {
|
|
39168
39446
|
throw new ExternalAgentExecutorError(
|
|
@@ -39170,17 +39448,17 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39170
39448
|
`Workspace root must be outside the private repository. workspace=${workspaceRoot} repo=${privateRepoRoot}`
|
|
39171
39449
|
);
|
|
39172
39450
|
}
|
|
39173
|
-
(0,
|
|
39174
|
-
const batchDir = (0,
|
|
39451
|
+
(0, import_fs15.mkdirSync)(workspaceRoot, { recursive: true });
|
|
39452
|
+
const batchDir = (0, import_path13.resolve)(String(batch.batch_dir || (0, import_path13.resolve)(batchPath, "..")));
|
|
39175
39453
|
const timeoutMinutes = Number.isFinite(options.timeoutMinutes) && Number(options.timeoutMinutes) > 0 ? Number(options.timeoutMinutes) : 30;
|
|
39176
39454
|
const runs = batch.runs.map((run) => {
|
|
39177
39455
|
const runId = safeRunId(requireString(run.run_id, "runs[].run_id"));
|
|
39178
|
-
const runDir = (0,
|
|
39179
|
-
const promptPath = (0,
|
|
39180
|
-
const workspaceDir = (0,
|
|
39181
|
-
(0,
|
|
39182
|
-
(0,
|
|
39183
|
-
(0,
|
|
39456
|
+
const runDir = (0, import_path13.resolve)(requireString(run.run_dir, `runs[${runId}].run_dir`));
|
|
39457
|
+
const promptPath = (0, import_path13.resolve)(requireString(run.prompt_path, `runs[${runId}].prompt_path`));
|
|
39458
|
+
const workspaceDir = (0, import_path13.join)(workspaceRoot, runId);
|
|
39459
|
+
(0, import_fs15.mkdirSync)(workspaceDir, { recursive: true });
|
|
39460
|
+
(0, import_fs15.writeFileSync)(
|
|
39461
|
+
(0, import_path13.join)(workspaceDir, "README.md"),
|
|
39184
39462
|
[
|
|
39185
39463
|
"# FOH External-Agent Workspace",
|
|
39186
39464
|
"",
|
|
@@ -39197,11 +39475,11 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39197
39475
|
promptVersion: promptVersionFromPath(promptPath)
|
|
39198
39476
|
});
|
|
39199
39477
|
const promptVersion = String(env[EXTERNAL_AGENT_PROMPT_VERSION_ENV] || "unknown");
|
|
39200
|
-
const jsonlPath = (0,
|
|
39201
|
-
const lastMessagePath = (0,
|
|
39202
|
-
const stderrPath = (0,
|
|
39203
|
-
const runPath = (0,
|
|
39204
|
-
const artifactSafetyPath = (0,
|
|
39478
|
+
const jsonlPath = (0, import_path13.join)(runDir, "codex-exec.jsonl");
|
|
39479
|
+
const lastMessagePath = (0, import_path13.join)(runDir, "codex-last-message.md");
|
|
39480
|
+
const stderrPath = (0, import_path13.join)(runDir, "codex-stderr.txt");
|
|
39481
|
+
const runPath = (0, import_path13.join)(runDir, "run.json");
|
|
39482
|
+
const artifactSafetyPath = (0, import_path13.join)(runDir, "artifact-safety.json");
|
|
39205
39483
|
const args = [
|
|
39206
39484
|
...runnerProbe.globalArgs,
|
|
39207
39485
|
"exec",
|
|
@@ -39212,7 +39490,7 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39212
39490
|
"--ephemeral",
|
|
39213
39491
|
"--ignore-rules",
|
|
39214
39492
|
"--sandbox",
|
|
39215
|
-
|
|
39493
|
+
codexSandboxMode,
|
|
39216
39494
|
...runnerProbe.execArgs,
|
|
39217
39495
|
"--json",
|
|
39218
39496
|
"--output-last-message",
|
|
@@ -39257,9 +39535,19 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39257
39535
|
denied_env_names: [...CODEX_EXECUTOR_DENIED_ENV_NAMES],
|
|
39258
39536
|
runner_probe: {
|
|
39259
39537
|
binary_checked: runnerProbe.binaryChecked,
|
|
39260
|
-
required_flags_checked: runnerProbe.requiredFlagsChecked
|
|
39538
|
+
required_flags_checked: runnerProbe.requiredFlagsChecked,
|
|
39539
|
+
version: runnerProbe.version,
|
|
39540
|
+
root_help_checked: runnerProbe.rootHelpChecked,
|
|
39541
|
+
exec_help_checked: runnerProbe.execHelpChecked,
|
|
39542
|
+
supports_modern_approval_mode: runnerProbe.supportsModernApprovalMode,
|
|
39543
|
+
supports_legacy_full_auto: runnerProbe.supportsLegacyFullAuto,
|
|
39544
|
+
supports_yolo_alias: runnerProbe.supportsYoloAlias,
|
|
39545
|
+
selected_automation_mode: runnerProbe.automationMode,
|
|
39546
|
+
canonical_command_policy: "explicit-flags",
|
|
39547
|
+
yolo_policy: runnerProbe.yoloPolicy
|
|
39261
39548
|
},
|
|
39262
39549
|
codex_automation_mode: runnerProbe.automationMode,
|
|
39550
|
+
codex_sandbox_mode: codexSandboxMode,
|
|
39263
39551
|
codex_sandbox_backend: codexSandboxBackend,
|
|
39264
39552
|
codex_network_access: codexNetworkAccess
|
|
39265
39553
|
},
|
|
@@ -39267,55 +39555,55 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39267
39555
|
};
|
|
39268
39556
|
}
|
|
39269
39557
|
function writeExternalAgentExecutorPlan(plan) {
|
|
39270
|
-
const path2 = (0,
|
|
39271
|
-
(0,
|
|
39272
|
-
(0,
|
|
39558
|
+
const path2 = (0, import_path13.join)(plan.batch_dir, "executor-plan.json");
|
|
39559
|
+
(0, import_fs15.mkdirSync)(plan.batch_dir, { recursive: true });
|
|
39560
|
+
(0, import_fs15.writeFileSync)(path2, `${JSON.stringify(plan, null, 2)}
|
|
39273
39561
|
`, "utf8");
|
|
39274
39562
|
return path2;
|
|
39275
39563
|
}
|
|
39276
39564
|
function proofArtifactPasses(runDir) {
|
|
39277
|
-
const proofPath = (0,
|
|
39278
|
-
if (!(0,
|
|
39565
|
+
const proofPath = (0, import_path13.join)(runDir, "proof.json");
|
|
39566
|
+
if (!(0, import_fs15.existsSync)(proofPath)) return false;
|
|
39279
39567
|
try {
|
|
39280
|
-
const parsed = JSON.parse((0,
|
|
39568
|
+
const parsed = JSON.parse((0, import_fs15.readFileSync)(proofPath, "utf8"));
|
|
39281
39569
|
return parsed.ok === true || parsed.status === "pass" || parsed.status === "passed";
|
|
39282
39570
|
} catch {
|
|
39283
39571
|
return false;
|
|
39284
39572
|
}
|
|
39285
39573
|
}
|
|
39286
39574
|
function readIfExists(path2) {
|
|
39287
|
-
return (0,
|
|
39575
|
+
return (0, import_fs15.existsSync)(path2) ? (0, import_fs15.readFileSync)(path2, "utf8") : "";
|
|
39288
39576
|
}
|
|
39289
39577
|
function redactArtifactFile(path2, input = {}) {
|
|
39290
|
-
if (!(0,
|
|
39291
|
-
const original = (0,
|
|
39578
|
+
if (!(0, import_fs15.existsSync)(path2)) return;
|
|
39579
|
+
const original = (0, import_fs15.readFileSync)(path2, "utf8");
|
|
39292
39580
|
const redacted = redactExternalAgentArtifactText(original, input);
|
|
39293
|
-
if (redacted !== original) (0,
|
|
39581
|
+
if (redacted !== original) (0, import_fs15.writeFileSync)(path2, redacted, "utf8");
|
|
39294
39582
|
}
|
|
39295
39583
|
function redactOutputArtifacts(run, input = {}) {
|
|
39296
39584
|
redactArtifactFile(run.outputs.jsonl, input);
|
|
39297
39585
|
redactArtifactFile(run.outputs.last_message, input);
|
|
39298
39586
|
redactArtifactFile(run.outputs.stderr, input);
|
|
39299
|
-
redactArtifactFile((0,
|
|
39300
|
-
if (!(0,
|
|
39301
|
-
for (const name of (0,
|
|
39587
|
+
redactArtifactFile((0, import_path13.join)(run.run_dir, "commands.ndjson"), input);
|
|
39588
|
+
if (!(0, import_fs15.existsSync)(run.run_dir)) return;
|
|
39589
|
+
for (const name of (0, import_fs15.readdirSync)(run.run_dir)) {
|
|
39302
39590
|
if (name.startsWith("command-output-cmd_") && !name.endsWith(".redacted")) {
|
|
39303
|
-
redactArtifactFile((0,
|
|
39591
|
+
redactArtifactFile((0, import_path13.join)(run.run_dir, name), input);
|
|
39304
39592
|
}
|
|
39305
39593
|
}
|
|
39306
39594
|
}
|
|
39307
39595
|
function copyCommandCaptureArtifacts(input) {
|
|
39308
|
-
const commandLog = (0,
|
|
39309
|
-
if (!(0,
|
|
39310
|
-
(0,
|
|
39311
|
-
for (const name of (0,
|
|
39596
|
+
const commandLog = (0, import_path13.join)(input.captureDir, "commands.ndjson");
|
|
39597
|
+
if (!(0, import_fs15.existsSync)(commandLog)) return;
|
|
39598
|
+
(0, import_fs15.writeFileSync)((0, import_path13.join)(input.runDir, "commands.ndjson"), (0, import_fs15.readFileSync)(commandLog, "utf8"), "utf8");
|
|
39599
|
+
for (const name of (0, import_fs15.readdirSync)(input.captureDir)) {
|
|
39312
39600
|
if (name.startsWith("command-output-cmd_")) {
|
|
39313
|
-
(0,
|
|
39601
|
+
(0, import_fs15.copyFileSync)((0, import_path13.join)(input.captureDir, name), (0, import_path13.join)(input.runDir, name));
|
|
39314
39602
|
}
|
|
39315
39603
|
}
|
|
39316
39604
|
}
|
|
39317
39605
|
function relativeArtifactName(path2) {
|
|
39318
|
-
return (0,
|
|
39606
|
+
return (0, import_path13.basename)(path2);
|
|
39319
39607
|
}
|
|
39320
39608
|
function classifyRun(input) {
|
|
39321
39609
|
if (input.timedOut) return { status: "hold", reasonCode: "codex_runner_timeout" };
|
|
@@ -39367,7 +39655,7 @@ ${stderr}`;
|
|
|
39367
39655
|
if (/(?:blocked|rejected|declined) by policy|EXEC_POLICY_BLOCKED|command execution was rejected|shell commands were rejected/i.test(combined)) {
|
|
39368
39656
|
return { status: "hold", reasonCode: "codex_exec_policy_blocked" };
|
|
39369
39657
|
}
|
|
39370
|
-
if (/bwrap:.*(?:RTM_NEWADDR|Operation not permitted)|bubblewrap.*(?:RTM_NEWADDR|Operation not permitted)|Failed RTM_NEWADDR|ENV_SANDBOX_EXEC_BLOCKED/i.test(combined)) {
|
|
39658
|
+
if (/bwrap:.*(?:RTM_NEWADDR|Operation not permitted|setting up uid map: Permission denied)|bubblewrap.*(?:RTM_NEWADDR|Operation not permitted|setting up uid map: Permission denied)|Failed RTM_NEWADDR|ENV_SANDBOX_EXEC_BLOCKED|permission profiles requiring direct runtime enforcement are incompatible with --use-legacy-landlock|legacy[_ -]?landlock.*incompatible/i.test(combined)) {
|
|
39371
39659
|
return { status: "hold", reasonCode: "codex_sandbox_exec_blocked" };
|
|
39372
39660
|
}
|
|
39373
39661
|
if (/ENV_NETWORK_DNS_BLOCK|Could not resolve host|npm ping.*timeout|NO_EXECUTABLE_INSTALL/i.test(combined)) {
|
|
@@ -39409,6 +39697,7 @@ ${stderr}`;
|
|
|
39409
39697
|
}
|
|
39410
39698
|
function buildExecutedRunArtifact(input) {
|
|
39411
39699
|
const commands = readCommandRecords(input.run.run_dir);
|
|
39700
|
+
const agentMetadata = readExternalAgentMetadata(input.run.run_dir);
|
|
39412
39701
|
return {
|
|
39413
39702
|
schema_version: "external_agent_run.v1",
|
|
39414
39703
|
run_id: input.run.run_id,
|
|
@@ -39440,7 +39729,7 @@ function buildExecutedRunArtifact(input) {
|
|
|
39440
39729
|
"npx --yes @f-o-h/cli@latest"
|
|
39441
39730
|
],
|
|
39442
39731
|
commands_run: commands.map((command) => command.command),
|
|
39443
|
-
docs_pages_used:
|
|
39732
|
+
docs_pages_used: agentMetadata.docs_pages_used,
|
|
39444
39733
|
eval_state: {
|
|
39445
39734
|
org_reuse_expected: true,
|
|
39446
39735
|
agent_reuse_expected: true,
|
|
@@ -39454,21 +39743,22 @@ function buildExecutedRunArtifact(input) {
|
|
|
39454
39743
|
},
|
|
39455
39744
|
artifacts: {
|
|
39456
39745
|
terminal_transcript: relativeArtifactName(input.run.outputs.jsonl),
|
|
39457
|
-
command_log: (0,
|
|
39458
|
-
proof_bundle: (0,
|
|
39459
|
-
replay_packet: (0,
|
|
39460
|
-
knowledge_packet: (0,
|
|
39746
|
+
command_log: (0, import_fs15.existsSync)((0, import_path13.join)(input.run.run_dir, "commands.ndjson")) ? "commands.ndjson" : null,
|
|
39747
|
+
proof_bundle: (0, import_fs15.existsSync)((0, import_path13.join)(input.run.run_dir, "proof.json")) ? "proof.json" : null,
|
|
39748
|
+
replay_packet: (0, import_fs15.existsSync)((0, import_path13.join)(input.run.run_dir, "replay.json")) ? "replay.json" : null,
|
|
39749
|
+
knowledge_packet: (0, import_fs15.existsSync)((0, import_path13.join)(input.run.run_dir, "knowledge.json")) ? "knowledge.json" : null,
|
|
39461
39750
|
improvement_packet: input.status === "pass" ? null : "improvement-packet.json",
|
|
39462
|
-
|
|
39751
|
+
agent_metadata: agentMetadata.path,
|
|
39752
|
+
notes: (0, import_fs15.existsSync)((0, import_path13.join)(input.run.run_dir, "notes.md")) ? "notes.md" : null,
|
|
39463
39753
|
codex_last_message: relativeArtifactName(input.run.outputs.last_message),
|
|
39464
39754
|
codex_stderr: relativeArtifactName(input.run.outputs.stderr),
|
|
39465
39755
|
artifact_safety: relativeArtifactName(input.run.outputs.artifact_safety)
|
|
39466
39756
|
},
|
|
39467
39757
|
summary: input.status === "pass" ? "Controlled Codex external-agent run produced passing proof evidence." : `Controlled Codex external-agent run ended as ${input.status} with reason ${input.reasonCode}.`,
|
|
39468
|
-
next_commands: input.status === "pass" ? [
|
|
39758
|
+
next_commands: input.status === "pass" ? [externalAgentSummaryCommand((0, import_path13.dirname)(input.run.run_dir))] : [
|
|
39469
39759
|
"foh eval external-agent scan-artifacts --run-dir <run_dir> --private-repo-root <private_repo_root> --write-redacted --json",
|
|
39470
39760
|
"foh bug improve --from external-agent-run --file <run_dir>/run.json --out <run_dir>/improvement-packet.json --json",
|
|
39471
|
-
|
|
39761
|
+
externalAgentSummaryCommand((0, import_path13.dirname)(input.run.run_dir))
|
|
39472
39762
|
]
|
|
39473
39763
|
};
|
|
39474
39764
|
}
|
|
@@ -39483,8 +39773,8 @@ function spawnCodex(input) {
|
|
|
39483
39773
|
stdio: ["pipe", "pipe", "pipe"],
|
|
39484
39774
|
windowsHide: true
|
|
39485
39775
|
});
|
|
39486
|
-
const stdout = (0,
|
|
39487
|
-
const stderr = (0,
|
|
39776
|
+
const stdout = (0, import_fs15.createWriteStream)(input.stdoutPath, { flags: "w" });
|
|
39777
|
+
const stderr = (0, import_fs15.createWriteStream)(input.stderrPath, { flags: "w" });
|
|
39488
39778
|
child.stdout.pipe(stdout);
|
|
39489
39779
|
child.stderr.pipe(stderr);
|
|
39490
39780
|
child.stdin.end(input.prompt);
|
|
@@ -39521,8 +39811,8 @@ function spawnCodex(input) {
|
|
|
39521
39811
|
}
|
|
39522
39812
|
function buildCommandInvocation(command, args) {
|
|
39523
39813
|
if (process.platform === "win32" && command.toLowerCase().endsWith(".cmd")) {
|
|
39524
|
-
const codexEntrypoint = (0,
|
|
39525
|
-
if ((0,
|
|
39814
|
+
const codexEntrypoint = (0, import_path13.join)((0, import_path13.dirname)(command), "node_modules", "@openai", "codex", "bin", "codex.js");
|
|
39815
|
+
if ((0, import_fs15.existsSync)(codexEntrypoint)) return { command: process.execPath, args: [codexEntrypoint, ...args] };
|
|
39526
39816
|
}
|
|
39527
39817
|
return { command, args };
|
|
39528
39818
|
}
|
|
@@ -39532,8 +39822,8 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
|
|
|
39532
39822
|
const runnerCommand = options.runnerCommand || resolveCodexExecutionCommand();
|
|
39533
39823
|
for (const run of plan.runs) {
|
|
39534
39824
|
const runStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
39535
|
-
const commandCaptureDir = (0,
|
|
39536
|
-
(0,
|
|
39825
|
+
const commandCaptureDir = (0, import_path13.join)(run.workspace_dir, ".foh-capture");
|
|
39826
|
+
(0, import_fs15.mkdirSync)(commandCaptureDir, { recursive: true });
|
|
39537
39827
|
const env = buildCodexExecutorEnv({
|
|
39538
39828
|
sourceEnv: options.env,
|
|
39539
39829
|
runDir: commandCaptureDir,
|
|
@@ -39544,7 +39834,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
|
|
|
39544
39834
|
args: run.args,
|
|
39545
39835
|
cwd: run.workspace_dir,
|
|
39546
39836
|
env,
|
|
39547
|
-
prompt: (0,
|
|
39837
|
+
prompt: (0, import_fs15.readFileSync)(run.prompt_path, "utf8"),
|
|
39548
39838
|
stdoutPath: run.outputs.jsonl,
|
|
39549
39839
|
stderrPath: run.outputs.stderr,
|
|
39550
39840
|
timeoutMs: plan.timeout_minutes * 60 * 1e3
|
|
@@ -39557,7 +39847,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
|
|
|
39557
39847
|
privateRepoRoot,
|
|
39558
39848
|
writeRedacted: true
|
|
39559
39849
|
});
|
|
39560
|
-
(0,
|
|
39850
|
+
(0, import_fs15.writeFileSync)(run.outputs.artifact_safety, `${JSON.stringify(artifactSafety, null, 2)}
|
|
39561
39851
|
`, "utf8");
|
|
39562
39852
|
const runEndedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
39563
39853
|
const classification = classifyRun({
|
|
@@ -39576,7 +39866,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
|
|
|
39576
39866
|
timedOut: spawned.timedOut,
|
|
39577
39867
|
durationMs: spawned.durationMs
|
|
39578
39868
|
});
|
|
39579
|
-
(0,
|
|
39869
|
+
(0, import_fs15.writeFileSync)(run.outputs.run, `${JSON.stringify(runArtifact, null, 2)}
|
|
39580
39870
|
`, "utf8");
|
|
39581
39871
|
results.push({
|
|
39582
39872
|
run_id: run.run_id,
|
|
@@ -39609,7 +39899,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
|
|
|
39609
39899
|
var DEFAULT_PROMPT_VERSION = "blank-setup.v1";
|
|
39610
39900
|
var DEFAULT_BATCH_MODELS = "openai/codex,anthropic/claude,cursor/agent";
|
|
39611
39901
|
var PROMPTS = {
|
|
39612
|
-
"blank-setup.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`, because cached older packages can produce invalid evidence. Install or verify the FOH CLI, authenticate or reach a deterministic auth blocker, then create or configure a Front Of House voice agent and website widget. Prefer the certification-oriented buyer templates: run `npx --yes @f-o-h/cli@latest templates list --category buyer --json` and use `UK Buyer Qualification` or `Viewing Booking` when available; do not use a greeting-only template for proof/certification. Prefer `npx --yes @f-o-h/cli@latest setup --phone-mode observe` for the free scaffold path: agent, widget, voice config, smoke test, certification, and publish readiness together. Treat phone-number purchasing as an explicit paid/scarce contact-path step, not part of high-volume eval setup. If `FOH_CLI_SPEND_POLICY=no_spend` is active and a command returns `paid_resource_blocked_by_spend_policy`, do not try to bypass it; continue widget/setup proof and report that exact reason code for the phone path. Run proof/smoke/certification where available, including widget proof and voice proof. If voice proof returns `contact_phone_missing` or `voice_contact_expected_no_spend_hold`, report that exact reason code unless a BYON/customer-approved phone path already exists. Produce a final evidence summary with commands run, docs used, artifacts created, and any blocker reason codes. Do not assume access to the private source repository.",
|
|
39902
|
+
"blank-setup.v1": "Go to https://frontofhouse.okii.uk. Use only public docs, public API docs, and the public npm CLI package. Always invoke the CLI with `npx --yes @f-o-h/cli@latest ...`; do not use unpinned `npx @f-o-h/cli ...`, because cached older packages can produce invalid evidence. Install or verify the FOH CLI, authenticate or reach a deterministic auth blocker, then create or configure a Front Of House voice agent and website widget. Prefer the certification-oriented buyer templates: run `npx --yes @f-o-h/cli@latest templates list --category buyer --json` and use `UK Buyer Qualification` or `Viewing Booking` when available; do not use a greeting-only template for proof/certification. Prefer `npx --yes @f-o-h/cli@latest setup --phone-mode observe` for the free scaffold path: agent, widget, voice config, smoke test, certification, and publish readiness together. Treat phone-number purchasing as an explicit paid/scarce contact-path step, not part of high-volume eval setup. If `FOH_CLI_SPEND_POLICY=no_spend` is active and a command returns `paid_resource_blocked_by_spend_policy`, do not try to bypass it; continue widget/setup proof and report that exact reason code for the phone path. Run proof/smoke/certification where available, including widget proof and voice proof. When running more than one `foh prove` mission for the same agent, pass `--proof-cache-dir .foh/proof-cache` so simulation certification can be shared instead of recomputed. If voice proof returns `contact_phone_missing` or `voice_contact_expected_no_spend_hold`, report that exact reason code unless a BYON/customer-approved phone path already exists. If `FOH_EXTERNAL_AGENT_RUN_DIR` is set, write `${FOH_EXTERNAL_AGENT_RUN_DIR}/external-agent-metadata.json` with `schema_version`, `docs_pages_used`, key decisions, and blocker reason codes before finishing. Produce a final evidence summary with commands run, docs used, artifacts created, and any blocker reason codes. Do not assume access to the private source repository.",
|
|
39613
39903
|
"debug-proof-failure.v1": "You are given a FOH proof or debug artifact. Use public docs and FOH CLI/API behavior to classify whether the blocker is docs, auth, org setup, agent config, widget, channel, runtime, or product bug. Produce a redacted improvement packet or the exact command needed to produce one. Do not ask the human to interpret logs manually unless no machine-readable artifact exists.",
|
|
39614
39904
|
"knowledge-miss.v1": "A FOH agent failed to answer a business question. Use CLI/API/docs to determine whether this is a knowledge-ingestion issue, retrieval issue, config issue, prompt/behavior issue, or runtime issue. Prefer foh knowledge query, transcript export, replay, and foh bug improve artifacts over screenshots.",
|
|
39615
39905
|
"replay-failure.v1": "You are given a FOH transcript or replay artifact. Use CLI/API/docs to replay or inspect the failed interaction, identify expected vs actual behavior, and produce a scenario-test or improvement-packet candidate."
|
|
@@ -39624,13 +39914,13 @@ function defaultRunDir(modelName, promptVersion) {
|
|
|
39624
39914
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 23);
|
|
39625
39915
|
const safeModel = String(modelName || "unknown-model").toLowerCase().replace(/[^a-z0-9_-]+/g, "-");
|
|
39626
39916
|
const safePrompt = String(promptVersion || DEFAULT_PROMPT_VERSION).toLowerCase().replace(/[^a-z0-9_.-]+/g, "-");
|
|
39627
|
-
return (0,
|
|
39917
|
+
return (0, import_path14.resolve)("test-results", "external-agent-runs", date4, `${safeModel}-${safePrompt}-${stamp}`);
|
|
39628
39918
|
}
|
|
39629
39919
|
function defaultBatchDir(promptVersion) {
|
|
39630
39920
|
const date4 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
39631
39921
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").replace("T", "-").slice(0, 23);
|
|
39632
39922
|
const safePrompt = String(promptVersion || DEFAULT_PROMPT_VERSION).toLowerCase().replace(/[^a-z0-9_.-]+/g, "-");
|
|
39633
|
-
return (0,
|
|
39923
|
+
return (0, import_path14.resolve)("test-results", "external-agent-runs", date4, `batch-${safePrompt}-${stamp}`);
|
|
39634
39924
|
}
|
|
39635
39925
|
function safeSlug(value) {
|
|
39636
39926
|
return String(value || "unknown").toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
@@ -39640,6 +39930,20 @@ function quoteArg(value) {
|
|
|
39640
39930
|
if (/^[A-Za-z0-9_./:=@-]+$/.test(text)) return text;
|
|
39641
39931
|
return `"${text.replace(/(["$`])/g, "\\$1")}"`;
|
|
39642
39932
|
}
|
|
39933
|
+
function externalAgentSummaryCommand2(root) {
|
|
39934
|
+
const summaryPath = (0, import_path14.join)(root, "latest-summary.json");
|
|
39935
|
+
const reportPath = (0, import_path14.join)(root, "summary.report.json");
|
|
39936
|
+
return [
|
|
39937
|
+
"node",
|
|
39938
|
+
"scripts/summarize-external-agent-runs.mjs",
|
|
39939
|
+
"--root",
|
|
39940
|
+
quoteArg(root),
|
|
39941
|
+
"--out",
|
|
39942
|
+
quoteArg(summaryPath),
|
|
39943
|
+
"--report",
|
|
39944
|
+
quoteArg(reportPath)
|
|
39945
|
+
].join(" ");
|
|
39946
|
+
}
|
|
39643
39947
|
function parseModelSpec(raw) {
|
|
39644
39948
|
const [provider, ...nameParts] = String(raw || "").split("/");
|
|
39645
39949
|
const name = nameParts.join("/");
|
|
@@ -39658,14 +39962,14 @@ function inferShell(raw) {
|
|
|
39658
39962
|
}
|
|
39659
39963
|
function writePrompt(runDir, promptVersion) {
|
|
39660
39964
|
const prompt = PROMPTS[promptVersion] ?? PROMPTS[DEFAULT_PROMPT_VERSION];
|
|
39661
|
-
const path2 = (0,
|
|
39662
|
-
(0,
|
|
39965
|
+
const path2 = (0, import_path14.join)(runDir, "prompt.txt");
|
|
39966
|
+
(0, import_fs16.writeFileSync)(path2, `${prompt}
|
|
39663
39967
|
`, "utf8");
|
|
39664
39968
|
return path2;
|
|
39665
39969
|
}
|
|
39666
39970
|
function writeSession(runDir, session) {
|
|
39667
|
-
const path2 = (0,
|
|
39668
|
-
(0,
|
|
39971
|
+
const path2 = (0, import_path14.join)(runDir, "session.json");
|
|
39972
|
+
(0, import_fs16.writeFileSync)(path2, `${JSON.stringify(session, null, 2)}
|
|
39669
39973
|
`, "utf8");
|
|
39670
39974
|
return path2;
|
|
39671
39975
|
}
|
|
@@ -39684,6 +39988,7 @@ function buildDefaultEvalState() {
|
|
|
39684
39988
|
}
|
|
39685
39989
|
function buildRunArtifact(input) {
|
|
39686
39990
|
const commands = readCommandRecords(input.runDir);
|
|
39991
|
+
const agentMetadata = readExternalAgentMetadata(input.runDir);
|
|
39687
39992
|
const startedAt = String(input.session.started_at);
|
|
39688
39993
|
const endedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
39689
39994
|
const status = input.status;
|
|
@@ -39717,7 +40022,7 @@ function buildRunArtifact(input) {
|
|
|
39717
40022
|
"npx --yes @f-o-h/cli@latest"
|
|
39718
40023
|
],
|
|
39719
40024
|
commands_run: commands.map((command) => command.command),
|
|
39720
|
-
docs_pages_used:
|
|
40025
|
+
docs_pages_used: agentMetadata.docs_pages_used,
|
|
39721
40026
|
eval_state: buildDefaultEvalState(),
|
|
39722
40027
|
artifacts: {
|
|
39723
40028
|
terminal_transcript: null,
|
|
@@ -39726,12 +40031,13 @@ function buildRunArtifact(input) {
|
|
|
39726
40031
|
replay_packet: null,
|
|
39727
40032
|
knowledge_packet: null,
|
|
39728
40033
|
improvement_packet: status === "pass" ? null : "improvement-packet.json",
|
|
40034
|
+
agent_metadata: agentMetadata.path,
|
|
39729
40035
|
notes: "notes.md"
|
|
39730
40036
|
},
|
|
39731
40037
|
summary: status === "pass" ? "External-agent capture session completed and was marked pass." : `External-agent capture session completed with ${commands.length} captured FOH command(s); classify and improve reason ${reasonCode}.`,
|
|
39732
|
-
next_commands: status === "pass" ? [
|
|
39733
|
-
`foh bug improve --from external-agent-run --file ${(0,
|
|
39734
|
-
|
|
40038
|
+
next_commands: status === "pass" ? [externalAgentSummaryCommand2((0, import_path14.dirname)(input.runDir))] : [
|
|
40039
|
+
`foh bug improve --from external-agent-run --file ${(0, import_path14.join)(input.runDir, "run.json")} --out ${(0, import_path14.join)(input.runDir, "improvement-packet.json")} --json`,
|
|
40040
|
+
externalAgentSummaryCommand2((0, import_path14.dirname)(input.runDir))
|
|
39735
40041
|
]
|
|
39736
40042
|
};
|
|
39737
40043
|
}
|
|
@@ -39740,13 +40046,13 @@ function registerEval(program3) {
|
|
|
39740
40046
|
const external = evalCommand.command("external-agent").description("Capture clean external coding-agent setup attempts");
|
|
39741
40047
|
external.command("batch").description("Create a deterministic multi-model external-agent batch plan").option("--models <list>", "Comma-separated provider/model list", DEFAULT_BATCH_MODELS).option("--prompt-version <version>", "Prompt version", DEFAULT_PROMPT_VERSION).option("--workspace-type <type>", "Workspace type label", "clean-no-repo").option("--agent-shell <name>", "Agent shell label", "vscode-terminal").option("--out-dir <path>", "Batch output directory").option("--json", "Output as JSON").action(async (opts) => {
|
|
39742
40048
|
const promptVersion = String(opts.promptVersion || DEFAULT_PROMPT_VERSION);
|
|
39743
|
-
const batchDir = (0,
|
|
40049
|
+
const batchDir = (0, import_path14.resolve)(String(opts.outDir || defaultBatchDir(promptVersion)));
|
|
39744
40050
|
const models = parseModelList(String(opts.models || DEFAULT_BATCH_MODELS));
|
|
39745
|
-
(0,
|
|
40051
|
+
(0, import_fs16.mkdirSync)(batchDir, { recursive: true });
|
|
39746
40052
|
const runs = models.map((model, index) => {
|
|
39747
40053
|
const runId = `${String(index + 1).padStart(2, "0")}-${safeSlug(model.provider)}-${safeSlug(model.name)}`;
|
|
39748
|
-
const runDir = (0,
|
|
39749
|
-
(0,
|
|
40054
|
+
const runDir = (0, import_path14.join)(batchDir, runId);
|
|
40055
|
+
(0, import_fs16.mkdirSync)(runDir, { recursive: true });
|
|
39750
40056
|
const promptPath = writePrompt(runDir, promptVersion);
|
|
39751
40057
|
const commandArgs = [
|
|
39752
40058
|
"eval",
|
|
@@ -39785,10 +40091,10 @@ function registerEval(program3) {
|
|
|
39785
40091
|
agent_shell: String(opts.agentShell || "vscode-terminal"),
|
|
39786
40092
|
run_count: runs.length,
|
|
39787
40093
|
runs,
|
|
39788
|
-
summary_command:
|
|
40094
|
+
summary_command: externalAgentSummaryCommand2(batchDir)
|
|
39789
40095
|
};
|
|
39790
|
-
const batchPath = (0,
|
|
39791
|
-
(0,
|
|
40096
|
+
const batchPath = (0, import_path14.join)(batchDir, "batch.json");
|
|
40097
|
+
(0, import_fs16.writeFileSync)(batchPath, `${JSON.stringify(batch, null, 2)}
|
|
39792
40098
|
`, "utf8");
|
|
39793
40099
|
format(cliEnvelope({
|
|
39794
40100
|
schemaVersion: "external_agent_batch_plan_result.v1",
|
|
@@ -39808,8 +40114,8 @@ function registerEval(program3) {
|
|
|
39808
40114
|
external.command("run").description("Launch an instrumented shell and emit external_agent_run.v1 when it exits").option("--model-provider <name>", "Model provider label", "unknown").option("--model-name <name>", "Model name label", "unknown-model").option("--prompt-version <version>", "Prompt version", DEFAULT_PROMPT_VERSION).option("--workspace-type <type>", "Workspace type label", "clean-no-repo").option("--agent-shell <name>", "Agent shell label", "vscode-terminal").option("--out-dir <path>", "Run output directory").option("--status <status>", "Final status when not interactively classified: pass|hold|fail", "hold").option("--reason-code <code>", "Failure/hold reason code", "external_agent_run_needs_review").option("--shell <command>", "Shell command to launch for capture").option("--no-shell", "Do not launch a shell; create/finalize artifacts immediately").option("--json", "Output as JSON").action(async (opts) => {
|
|
39809
40115
|
const status = normalizeStatus(opts.status);
|
|
39810
40116
|
const promptVersion = String(opts.promptVersion || DEFAULT_PROMPT_VERSION);
|
|
39811
|
-
const runDir = (0,
|
|
39812
|
-
(0,
|
|
40117
|
+
const runDir = (0, import_path14.resolve)(String(opts.outDir || defaultRunDir(opts.modelName, promptVersion)));
|
|
40118
|
+
(0, import_fs16.mkdirSync)(runDir, { recursive: true });
|
|
39813
40119
|
const runId = runDir.split(/[\\/]/).filter(Boolean).slice(-1)[0];
|
|
39814
40120
|
const promptPath = writePrompt(runDir, promptVersion);
|
|
39815
40121
|
const shell = inferShell(opts.shell);
|
|
@@ -39832,7 +40138,7 @@ function registerEval(program3) {
|
|
|
39832
40138
|
}
|
|
39833
40139
|
};
|
|
39834
40140
|
writeSession(runDir, session);
|
|
39835
|
-
(0,
|
|
40141
|
+
(0, import_fs16.writeFileSync)((0, import_path14.join)(runDir, "notes.md"), "# External Agent Run Notes\n\n", "utf8");
|
|
39836
40142
|
let shellExitCode = null;
|
|
39837
40143
|
if (opts.shell !== false) {
|
|
39838
40144
|
process.stdout.write(`
|
|
@@ -39854,8 +40160,8 @@ Exit the shell to finalize run.json.
|
|
|
39854
40160
|
shellExitCode = typeof result.status === "number" ? result.status : null;
|
|
39855
40161
|
}
|
|
39856
40162
|
const artifact = buildRunArtifact({ runDir, session, status, reasonCode: opts.reasonCode, shellExitCode });
|
|
39857
|
-
const runPath = (0,
|
|
39858
|
-
(0,
|
|
40163
|
+
const runPath = (0, import_path14.join)(runDir, "run.json");
|
|
40164
|
+
(0, import_fs16.writeFileSync)(runPath, `${JSON.stringify(artifact, null, 2)}
|
|
39859
40165
|
`, "utf8");
|
|
39860
40166
|
format(cliEnvelope({
|
|
39861
40167
|
schemaVersion: "external_agent_capture_result.v1",
|
|
@@ -39865,7 +40171,7 @@ Exit the shell to finalize run.json.
|
|
|
39865
40171
|
artifacts: {
|
|
39866
40172
|
run: runPath,
|
|
39867
40173
|
prompt: promptPath,
|
|
39868
|
-
commands: (0,
|
|
40174
|
+
commands: (0, import_path14.join)(runDir, "commands.ndjson")
|
|
39869
40175
|
},
|
|
39870
40176
|
nextCommands: artifact.next_commands,
|
|
39871
40177
|
extra: { run: artifact }
|
|
@@ -39890,7 +40196,7 @@ Exit the shell to finalize run.json.
|
|
|
39890
40196
|
}), { json: Boolean(opts.json) });
|
|
39891
40197
|
if (!report.ok) process.exitCode = 1;
|
|
39892
40198
|
});
|
|
39893
|
-
external.command("execute").description("Create a guarded dry-run executor plan for programmable external-agent runners").requiredOption("--batch <path>", "Path to external_agent_batch_plan.v1 batch.json").option("--runner <runner>", "Runner implementation", "codex").option("--workspace-root <path>", "Clean executor workspace root; must be outside the private repo").option("--private-repo-root <path>", "Private repository root to guard against").option("--timeout-minutes <minutes>", "Per-run timeout planned for future execution", "30").option("--codex-sandbox-backend <backend>", "Codex sandbox backend override: default|legacy-landlock", "default").option("--codex-network-access", "Allow Codex workspace-write sandbox network access for public docs/npm probes").option("--skip-runner-probe", "Skip local runner binary/help probing").option("--dry-run", "Write the executor plan without launching the runner", true).option("--live", "Launch one controlled external-agent run after writing the guarded plan").option("--json", "Output as JSON").action(async (opts) => {
|
|
40199
|
+
external.command("execute").description("Create a guarded dry-run executor plan for programmable external-agent runners").requiredOption("--batch <path>", "Path to external_agent_batch_plan.v1 batch.json").option("--runner <runner>", "Runner implementation", "codex").option("--workspace-root <path>", "Clean executor workspace root; must be outside the private repo").option("--private-repo-root <path>", "Private repository root to guard against").option("--timeout-minutes <minutes>", "Per-run timeout planned for future execution", "30").option("--codex-sandbox-backend <backend>", "Codex sandbox backend override: default|legacy-landlock", "default").option("--codex-sandbox-mode <mode>", "Codex sandbox mode: workspace-write|danger-full-access", "workspace-write").option("--codex-network-access", "Allow Codex workspace-write sandbox network access for public docs/npm probes").option("--skip-runner-probe", "Skip local runner binary/help probing").option("--dry-run", "Write the executor plan without launching the runner", true).option("--live", "Launch one controlled external-agent run after writing the guarded plan").option("--json", "Output as JSON").action(async (opts) => {
|
|
39894
40200
|
try {
|
|
39895
40201
|
const plan = createExternalAgentExecutorPlan({
|
|
39896
40202
|
batchPath: String(opts.batch),
|
|
@@ -39899,6 +40205,7 @@ Exit the shell to finalize run.json.
|
|
|
39899
40205
|
privateRepoRoot: opts.privateRepoRoot ? String(opts.privateRepoRoot) : void 0,
|
|
39900
40206
|
timeoutMinutes: Number(opts.timeoutMinutes || 30),
|
|
39901
40207
|
codexSandboxBackend: String(opts.codexSandboxBackend || "default"),
|
|
40208
|
+
codexSandboxMode: String(opts.codexSandboxMode || "workspace-write"),
|
|
39902
40209
|
codexNetworkAccess: Boolean(opts.codexNetworkAccess),
|
|
39903
40210
|
skipRunnerProbe: Boolean(opts.skipRunnerProbe),
|
|
39904
40211
|
cwd: process.cwd()
|
|
@@ -39922,8 +40229,8 @@ Exit the shell to finalize run.json.
|
|
|
39922
40229
|
const result = await executeExternalAgentExecutorPlan(plan, {
|
|
39923
40230
|
privateRepoRoot: plan.private_repo_root
|
|
39924
40231
|
});
|
|
39925
|
-
const resultPath = (0,
|
|
39926
|
-
(0,
|
|
40232
|
+
const resultPath = (0, import_path14.join)(plan.batch_dir, "execution-result.json");
|
|
40233
|
+
(0, import_fs16.writeFileSync)(resultPath, `${JSON.stringify(result, null, 2)}
|
|
39927
40234
|
`, "utf8");
|
|
39928
40235
|
format(cliEnvelope({
|
|
39929
40236
|
schemaVersion: "external_agent_execution_result.v1",
|
|
@@ -39937,7 +40244,7 @@ Exit the shell to finalize run.json.
|
|
|
39937
40244
|
},
|
|
39938
40245
|
nextCommands: [
|
|
39939
40246
|
...result.results.map((run) => `foh eval external-agent scan-artifacts --run-dir ${quoteArg(plan.runs.find((item) => item.run_id === run.run_id)?.run_dir || ".")} --private-repo-root ${quoteArg(plan.private_repo_root)} --write-redacted --json`),
|
|
39940
|
-
|
|
40247
|
+
externalAgentSummaryCommand2(plan.batch_dir)
|
|
39941
40248
|
],
|
|
39942
40249
|
extra: { result }
|
|
39943
40250
|
}), { json: Boolean(opts.json) });
|