@f-o-h/cli 0.1.86 → 0.1.87

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.
Files changed (2) hide show
  1. package/dist/foh.js +1487 -780
  2. package/package.json +1 -1
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 = resolve15.call(this, root, ref);
6049
+ let _sch = resolve16.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 resolve15(root, ref) {
6076
+ function resolve16(root, ref) {
6077
6077
  let sch;
6078
6078
  while (typeof (sch = this.refs[ref]) == "string")
6079
6079
  ref = sch;
@@ -6648,7 +6648,7 @@ var require_fast_uri = __commonJS({
6648
6648
  }
6649
6649
  return uri;
6650
6650
  }
6651
- function resolve15(baseURI, relativeURI, options) {
6651
+ function resolve16(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;
@@ -6875,7 +6875,7 @@ var require_fast_uri = __commonJS({
6875
6875
  var fastUri = {
6876
6876
  SCHEMES,
6877
6877
  normalize,
6878
- resolve: resolve15,
6878
+ resolve: resolve16,
6879
6879
  resolveComponent,
6880
6880
  equal,
6881
6881
  serialize,
@@ -10183,21 +10183,21 @@ async function promptLine(label, {
10183
10183
  allowEmpty = false,
10184
10184
  defaultValue
10185
10185
  } = {}) {
10186
- return await new Promise((resolve15) => {
10186
+ return await new Promise((resolve16) => {
10187
10187
  const suffix = defaultValue ? ` [${defaultValue}]` : "";
10188
10188
  const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout, terminal: true });
10189
10189
  rl.question(`${label}${suffix}: `, (answer) => {
10190
10190
  rl.close();
10191
10191
  const value = String(answer ?? "").trim();
10192
10192
  if (!value && typeof defaultValue === "string") {
10193
- resolve15(defaultValue);
10193
+ resolve16(defaultValue);
10194
10194
  return;
10195
10195
  }
10196
10196
  if (!value && !allowEmpty) {
10197
- resolve15("");
10197
+ resolve16("");
10198
10198
  return;
10199
10199
  }
10200
- resolve15(value);
10200
+ resolve16(value);
10201
10201
  });
10202
10202
  });
10203
10203
  }
@@ -10205,7 +10205,7 @@ async function promptSecret(label) {
10205
10205
  if (!process.stdin.isTTY || !process.stdout.isTTY || typeof process.stdin.setRawMode !== "function") {
10206
10206
  return await promptLine(label);
10207
10207
  }
10208
- return await new Promise((resolve15) => {
10208
+ return await new Promise((resolve16) => {
10209
10209
  const stdin = process.stdin;
10210
10210
  const stdout = process.stdout;
10211
10211
  const wasRaw = Boolean(stdin.isRaw);
@@ -10219,7 +10219,7 @@ async function promptSecret(label) {
10219
10219
  const finish = () => {
10220
10220
  cleanup();
10221
10221
  stdout.write("\n");
10222
- resolve15(value);
10222
+ resolve16(value);
10223
10223
  };
10224
10224
  const onData = (chunk) => {
10225
10225
  const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
@@ -10228,7 +10228,7 @@ async function promptSecret(label) {
10228
10228
  cleanup();
10229
10229
  process.exitCode = 130;
10230
10230
  stdout.write("\n");
10231
- return resolve15("");
10231
+ return resolve16("");
10232
10232
  }
10233
10233
  if (char === "\r" || char === "\n") {
10234
10234
  finish();
@@ -10583,7 +10583,7 @@ async function storeAuthenticatedSession(params) {
10583
10583
  return output;
10584
10584
  }
10585
10585
  function sleep(ms) {
10586
- return new Promise((resolve15) => setTimeout(resolve15, ms));
10586
+ return new Promise((resolve16) => setTimeout(resolve16, ms));
10587
10587
  }
10588
10588
  function hasExplicitTimeoutFlag(argv = process.argv) {
10589
10589
  return argv.some((arg) => arg === "--timeout-seconds" || arg.startsWith("--timeout-seconds="));
@@ -11141,7 +11141,7 @@ async function pollUntil(check2, opts) {
11141
11141
  }
11142
11142
  }
11143
11143
  function sleep2(ms) {
11144
- return new Promise((resolve15) => setTimeout(resolve15, ms));
11144
+ return new Promise((resolve16) => setTimeout(resolve16, ms));
11145
11145
  }
11146
11146
 
11147
11147
  // src/commands/compliance.ts
@@ -14265,8 +14265,8 @@ function registerAgentGuardrailCommands(agent) {
14265
14265
  try {
14266
14266
  rule = JSON.parse(opts.rule);
14267
14267
  } catch {
14268
- const { readFileSync: readFileSync19 } = await import("fs");
14269
- rule = JSON.parse(readFileSync19(opts.rule, "utf-8"));
14268
+ const { readFileSync: readFileSync20 } = await import("fs");
14269
+ rule = JSON.parse(readFileSync20(opts.rule, "utf-8"));
14270
14270
  }
14271
14271
  const data = await apiFetch(`/v1/console/agents/${opts.agent}/guardrails`, {
14272
14272
  method: "POST",
@@ -14835,9 +14835,9 @@ function registerAgent(program3) {
14835
14835
  process.stdout.write(yaml);
14836
14836
  return;
14837
14837
  }
14838
- const { writeFileSync: writeFileSync15 } = await import("fs");
14838
+ const { writeFileSync: writeFileSync16 } = await import("fs");
14839
14839
  const outputPath = opts.output ?? "tenant.yaml";
14840
- writeFileSync15(
14840
+ writeFileSync16(
14841
14841
  outputPath,
14842
14842
  `# tenant.yaml - Front Of House agent manifest
14843
14843
  # Edit this file and run: foh plan tenant.yaml
@@ -15368,9 +15368,6 @@ function registerInstagramChannelCommands(instagram, addCommonOptions) {
15368
15368
  }));
15369
15369
  }
15370
15370
 
15371
- // src/commands/channel-whatsapp.ts
15372
- var import_node_fs2 = require("node:fs");
15373
-
15374
15371
  // src/commands/channel-whatsapp-helpers.ts
15375
15372
  function parsePositiveNumber(value, fallback) {
15376
15373
  if (value === void 0 || value === null || String(value).trim() === "") return fallback;
@@ -15549,6 +15546,9 @@ function resolveLiveProof({
15549
15546
  };
15550
15547
  }
15551
15548
 
15549
+ // src/commands/channel-whatsapp-onboarding.ts
15550
+ var import_node_fs2 = require("node:fs");
15551
+
15552
15552
  // src/commands/channel-whatsapp-setup.ts
15553
15553
  var import_node_crypto = require("node:crypto");
15554
15554
  var WHATSAPP_WEBHOOK_CHALLENGE_TIMEOUT_MS = 1e4;
@@ -15657,7 +15657,7 @@ function assertProofPass(strict, reasons) {
15657
15657
  }
15658
15658
  }
15659
15659
 
15660
- // src/commands/channel-whatsapp.ts
15660
+ // src/commands/channel-whatsapp-onboarding.ts
15661
15661
  async function runWhatsAppOnboardingSession(params) {
15662
15662
  return await apiFetch("/v1/console/channels/whatsapp/onboarding-session", {
15663
15663
  method: "POST",
@@ -15765,11 +15765,11 @@ async function runWhatsAppOnboardingWizard(opts) {
15765
15765
  }
15766
15766
  ]
15767
15767
  });
15768
- let accessToken = String(wizardState.accessToken || "").trim();
15768
+ const accessToken = String(wizardState.accessToken || "").trim();
15769
15769
  let wabaId = String(opts.wabaId || "").trim();
15770
15770
  let phoneNumberId = String(opts.phoneNumberId || "").trim();
15771
- let verifyToken = String(opts.verifyToken || "").trim();
15772
- let appSecret = String(wizardState.appSecret || "").trim();
15771
+ const verifyToken = String(opts.verifyToken || "").trim();
15772
+ const appSecret = String(wizardState.appSecret || "").trim();
15773
15773
  if (!accessToken) {
15774
15774
  throw new FohError({
15775
15775
  step: "channel.whatsapp.onboard",
@@ -15846,7 +15846,7 @@ async function runWhatsAppOnboardingWizard(opts) {
15846
15846
  remediation: "Run again with --phone-number-id and --waba-id, or use `foh channel whatsapp start` for deterministic next steps."
15847
15847
  });
15848
15848
  }
15849
- function registerWhatsAppChannelCommands(whatsapp, addCommonOptions) {
15849
+ function registerWhatsAppOnboardingCommands(whatsapp, addCommonOptions) {
15850
15850
  addCommonOptions(
15851
15851
  whatsapp.command("start").description("[Deprecated wrapper] Start onboarding using the canonical session flow")
15852
15852
  ).option("--access-token <token>", "Meta access token with WhatsApp Business access").option("--waba-id <id>", "Optional explicit WhatsApp Business Account id when /me discovery is restricted").option("--phone-number-id <id>", "Optional explicit phone number id when discovery returns multiple candidates").option("--agent-id <id>", "Agent to bind to the WhatsApp channel").option("--verify-token <token>", "Optional webhook verify token (auto-generated when omitted)").option("--app-secret <secret>", "Optional Meta app secret (required for full signature-ready closure)").option("--business-slug <slug>", "Business identifier for operator-level targeting").option("--audio-enabled <bool>", "Enable inbound WhatsApp audio fallback auto-reply (true/false)", "true").option("--wizard", "Run guided onboarding with automatic recovery prompts").action(async (opts) => withCommandErrorHandling(async () => {
@@ -16062,6 +16062,11 @@ function registerWhatsAppChannelCommands(whatsapp, addCommonOptions) {
16062
16062
  legacy_wrapper: legacy
16063
16063
  }, { json: opts.json ?? false });
16064
16064
  }));
16065
+ }
16066
+
16067
+ // src/commands/channel-whatsapp.ts
16068
+ function registerWhatsAppChannelCommands(whatsapp, addCommonOptions) {
16069
+ registerWhatsAppOnboardingCommands(whatsapp, addCommonOptions);
16065
16070
  addCommonOptions(
16066
16071
  whatsapp.command("status").description("Get WhatsApp channel status")
16067
16072
  ).action(async (opts) => withCommandErrorHandling(async () => {
@@ -16438,11 +16443,11 @@ function registerVoice(program3) {
16438
16443
  }
16439
16444
  const outputPath = String(opts.out || `foh-voice-preview-${provider}-${voiceId}.mp3`).trim();
16440
16445
  const audio = Buffer.from(await res.arrayBuffer());
16441
- const { mkdirSync: mkdirSync10, writeFileSync: writeFileSync15 } = await import("fs");
16442
- const { dirname: dirname13, resolve: resolve15 } = await import("path");
16443
- const absolutePath = resolve15(outputPath);
16444
- mkdirSync10(dirname13(absolutePath), { recursive: true });
16445
- writeFileSync15(absolutePath, audio);
16446
+ const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync16 } = await import("fs");
16447
+ const { dirname: dirname13, resolve: resolve16 } = await import("path");
16448
+ const absolutePath = resolve16(outputPath);
16449
+ mkdirSync11(dirname13(absolutePath), { recursive: true });
16450
+ writeFileSync16(absolutePath, audio);
16446
16451
  format({
16447
16452
  status: "ok",
16448
16453
  provider,
@@ -30933,7 +30938,7 @@ var Protocol = class {
30933
30938
  return;
30934
30939
  }
30935
30940
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
30936
- await new Promise((resolve15) => setTimeout(resolve15, pollInterval));
30941
+ await new Promise((resolve16) => setTimeout(resolve16, pollInterval));
30937
30942
  options?.signal?.throwIfAborted();
30938
30943
  }
30939
30944
  } catch (error2) {
@@ -30950,7 +30955,7 @@ var Protocol = class {
30950
30955
  */
30951
30956
  request(request, resultSchema, options) {
30952
30957
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
30953
- return new Promise((resolve15, reject) => {
30958
+ return new Promise((resolve16, reject) => {
30954
30959
  const earlyReject = (error2) => {
30955
30960
  reject(error2);
30956
30961
  };
@@ -31028,7 +31033,7 @@ var Protocol = class {
31028
31033
  if (!parseResult.success) {
31029
31034
  reject(parseResult.error);
31030
31035
  } else {
31031
- resolve15(parseResult.data);
31036
+ resolve16(parseResult.data);
31032
31037
  }
31033
31038
  } catch (error2) {
31034
31039
  reject(error2);
@@ -31289,12 +31294,12 @@ var Protocol = class {
31289
31294
  }
31290
31295
  } catch {
31291
31296
  }
31292
- return new Promise((resolve15, reject) => {
31297
+ return new Promise((resolve16, reject) => {
31293
31298
  if (signal.aborted) {
31294
31299
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
31295
31300
  return;
31296
31301
  }
31297
- const timeoutId = setTimeout(resolve15, interval);
31302
+ const timeoutId = setTimeout(resolve16, interval);
31298
31303
  signal.addEventListener("abort", () => {
31299
31304
  clearTimeout(timeoutId);
31300
31305
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -32394,7 +32399,7 @@ var McpServer = class {
32394
32399
  let task = createTaskResult.task;
32395
32400
  const pollInterval = task.pollInterval ?? 5e3;
32396
32401
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
32397
- await new Promise((resolve15) => setTimeout(resolve15, pollInterval));
32402
+ await new Promise((resolve16) => setTimeout(resolve16, pollInterval));
32398
32403
  const updatedTask = await extra.taskStore.getTask(taskId);
32399
32404
  if (!updatedTask) {
32400
32405
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -33043,19 +33048,19 @@ var StdioServerTransport = class {
33043
33048
  this.onclose?.();
33044
33049
  }
33045
33050
  send(message) {
33046
- return new Promise((resolve15) => {
33051
+ return new Promise((resolve16) => {
33047
33052
  const json3 = serializeMessage(message);
33048
33053
  if (this._stdout.write(json3)) {
33049
- resolve15();
33054
+ resolve16();
33050
33055
  } else {
33051
- this._stdout.once("drain", resolve15);
33056
+ this._stdout.once("drain", resolve16);
33052
33057
  }
33053
33058
  });
33054
33059
  }
33055
33060
  };
33056
33061
 
33057
33062
  // src/lib/cli-version.ts
33058
- var injectedVersion = true ? String("0.1.86").trim() : "";
33063
+ var injectedVersion = true ? String("0.1.87").trim() : "";
33059
33064
  var envVersion = String(process.env.FOH_CLI_VERSION || process.env.npm_package_version || "").trim();
33060
33065
  var CLI_VERSION = injectedVersion || envVersion || "0.0.0-dev";
33061
33066
 
@@ -33242,7 +33247,7 @@ async function runFohCli(params) {
33242
33247
  effectiveArgv.push("--json");
33243
33248
  }
33244
33249
  const command = `foh ${effectiveArgv.join(" ")}`;
33245
- return await new Promise((resolve15) => {
33250
+ return await new Promise((resolve16) => {
33246
33251
  const child = (0, import_node_child_process.spawn)(process.execPath, [cliEntry, ...effectiveArgv], {
33247
33252
  stdio: ["ignore", "pipe", "pipe"],
33248
33253
  env: {
@@ -33267,7 +33272,7 @@ async function runFohCli(params) {
33267
33272
  });
33268
33273
  child.once("error", (error2) => {
33269
33274
  clearTimeout(timeoutHandle);
33270
- resolve15({
33275
+ resolve16({
33271
33276
  ok: false,
33272
33277
  command,
33273
33278
  argv: effectiveArgv,
@@ -33283,7 +33288,7 @@ async function runFohCli(params) {
33283
33288
  const stderrText = finalizeBoundedText(stderrBuffer);
33284
33289
  const exitCode = Number.isFinite(code ?? NaN) ? Number(code) : 1;
33285
33290
  const stdoutJson = tryParseJson(stdoutText);
33286
- resolve15({
33291
+ resolve16({
33287
33292
  ok: !timedOut && exitCode === 0,
33288
33293
  command,
33289
33294
  argv: effectiveArgv,
@@ -35453,8 +35458,8 @@ function registerSetup(program3) {
35453
35458
  }
35454
35459
  try {
35455
35460
  const manifest = await agentExport(resolvedAgentId, { apiUrlOverride: opts.apiUrl });
35456
- const { writeFileSync: writeFileSync15 } = await import("fs");
35457
- writeFileSync15(
35461
+ const { writeFileSync: writeFileSync16 } = await import("fs");
35462
+ writeFileSync16(
35458
35463
  "tenant.yaml",
35459
35464
  `# tenant.yaml - Front Of House agent manifest
35460
35465
  # Edit this file and run: foh plan tenant.yaml
@@ -35624,8 +35629,8 @@ function registerSim(program3) {
35624
35629
  }
35625
35630
  const cert = response.certificate;
35626
35631
  if (opts.out) {
35627
- const { writeFileSync: writeFileSync15 } = await import("fs");
35628
- writeFileSync15(opts.out, JSON.stringify(cert, null, 2) + "\n", "utf-8");
35632
+ const { writeFileSync: writeFileSync16 } = await import("fs");
35633
+ writeFileSync16(opts.out, JSON.stringify(cert, null, 2) + "\n", "utf-8");
35629
35634
  process.stderr.write(` Certificate written to ${opts.out}
35630
35635
  `);
35631
35636
  }
@@ -35675,8 +35680,8 @@ function registerSim(program3) {
35675
35680
  });
35676
35681
  }
35677
35682
  if (opts.out) {
35678
- const { writeFileSync: writeFileSync15 } = await import("fs");
35679
- writeFileSync15(opts.out, JSON.stringify(response.certificate, null, 2) + "\n", "utf-8");
35683
+ const { writeFileSync: writeFileSync16 } = await import("fs");
35684
+ writeFileSync16(opts.out, JSON.stringify(response.certificate, null, 2) + "\n", "utf-8");
35680
35685
  process.stderr.write(` Final certificate written to ${opts.out}
35681
35686
  `);
35682
35687
  }
@@ -38542,6 +38547,629 @@ function registerProve(program3) {
38542
38547
  }));
38543
38548
  }
38544
38549
 
38550
+ // src/commands/objective.ts
38551
+ var import_node_fs5 = require("node:fs");
38552
+ var import_node_path2 = require("node:path");
38553
+ var DEFAULT_OBJECTIVE_REPORT_PATH = "test-results/objective-status.latest.json";
38554
+ function asRecord3(value) {
38555
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
38556
+ }
38557
+ function normalizeString(value) {
38558
+ return typeof value === "string" ? value.trim() : "";
38559
+ }
38560
+ function uniqueStrings(values) {
38561
+ return Array.from(new Set(values.map(normalizeString).filter(Boolean)));
38562
+ }
38563
+ function asArray(value) {
38564
+ return Array.isArray(value) ? value : [];
38565
+ }
38566
+ function collectStringArrays(value, keys, output = []) {
38567
+ if (!value || typeof value !== "object") return output;
38568
+ if (Array.isArray(value)) {
38569
+ for (const item of value) collectStringArrays(item, keys, output);
38570
+ return output;
38571
+ }
38572
+ for (const [key, child] of Object.entries(value)) {
38573
+ if (keys.has(key) && Array.isArray(child)) {
38574
+ for (const item of child) {
38575
+ if (typeof item === "string") output.push(item);
38576
+ }
38577
+ } else if (child && typeof child === "object") {
38578
+ collectStringArrays(child, keys, output);
38579
+ }
38580
+ }
38581
+ return output;
38582
+ }
38583
+ function collectArtifactRefs(value, output = []) {
38584
+ if (!value || typeof value !== "object") return output;
38585
+ if (Array.isArray(value)) {
38586
+ for (const item of value) collectArtifactRefs(item, output);
38587
+ return output;
38588
+ }
38589
+ for (const [key, child] of Object.entries(value)) {
38590
+ if (typeof child === "string") {
38591
+ const normalized = child.trim();
38592
+ if (normalized && (key.toLowerCase().includes("artifact") || key.toLowerCase().includes("report") || normalized.includes("test-results/") || normalized.includes("test-results\\"))) {
38593
+ output.push(normalized);
38594
+ }
38595
+ } else if (child && typeof child === "object") {
38596
+ collectArtifactRefs(child, output);
38597
+ }
38598
+ }
38599
+ return output;
38600
+ }
38601
+ function summarizeSourceReport(value) {
38602
+ const report = asRecord3(value);
38603
+ return {
38604
+ decision: firstString(report, ["decision", "current_decision", "status"]) || null,
38605
+ go_live_allowed: report.go_live_allowed === true || report.customer_live_ready === true || report.production_ready === true,
38606
+ allowed_mode: firstString(report, ["allowed_mode", "highest_allowed_mode", "current_mode"]) || null,
38607
+ blocked_modes: uniqueStrings(collectStringArrays(report, /* @__PURE__ */ new Set(["blocked_modes"]))),
38608
+ reason_codes: uniqueStrings(collectStringArrays(report, /* @__PURE__ */ new Set(["reason_codes", "blocker_reason_codes"]))),
38609
+ next_commands: uniqueStrings(collectStringArrays(report, /* @__PURE__ */ new Set(["next_commands"]))),
38610
+ evidence_refs: uniqueStrings(collectArtifactRefs(report))
38611
+ };
38612
+ }
38613
+ function finiteNumber(value) {
38614
+ const number3 = Number(value);
38615
+ return Number.isFinite(number3) ? number3 : null;
38616
+ }
38617
+ function normalizeCustomerEvidenceActions(value) {
38618
+ return asArray(value).map(asRecord3).map((action) => ({
38619
+ id: firstString(action, ["id", "action_id"]),
38620
+ title: firstString(action, ["title"]) || null,
38621
+ owner: firstString(action, ["owner"]) || null,
38622
+ blocker_count: finiteNumber(action.blocker_count) ?? (uniqueStrings(asArray(action.reason_codes)).length || null),
38623
+ target_evidence_paths: uniqueStrings([
38624
+ ...asArray(action.target_evidence_paths),
38625
+ firstString(action, ["target_evidence_path"])
38626
+ ]),
38627
+ validator_commands: uniqueStrings([
38628
+ ...asArray(action.validator_commands),
38629
+ ...asArray(action.next_commands)
38630
+ ]),
38631
+ required_evidence: firstString(action, ["required_evidence"]) || null,
38632
+ unlocks: firstString(action, ["unlocks"]) || null
38633
+ })).filter((action) => normalizeString(action.id));
38634
+ }
38635
+ function normalizeCustomerEvidenceActionPacket(value) {
38636
+ const packet = asRecord3(value);
38637
+ const actions = normalizeCustomerEvidenceActions(packet.actions);
38638
+ if (actions.length === 0) return void 0;
38639
+ return {
38640
+ packet_role: firstString(packet, ["packet_role"]) || "compressed_customer_operator_action_plan",
38641
+ action_count: finiteNumber(packet.action_count) ?? actions.length,
38642
+ external_hold_count: finiteNumber(packet.external_hold_count),
38643
+ raw_reason_code_count: finiteNumber(packet.raw_reason_code_count),
38644
+ compression_ratio: finiteNumber(packet.compression_ratio),
38645
+ actions,
38646
+ validation_commands: uniqueStrings(asArray(packet.validation_commands)),
38647
+ instructions: uniqueStrings(asArray(packet.instructions))
38648
+ };
38649
+ }
38650
+ function customerEvidenceActionPacketFromSources(...sources) {
38651
+ for (const source of sources) {
38652
+ const report = asRecord3(source);
38653
+ const requestPackets = asRecord3(report.request_packets);
38654
+ const operatorStatus = asRecord3(report.operator_status);
38655
+ const candidates = [
38656
+ requestPackets.customer_operator_action_packet,
38657
+ report.customer_operator_action_packet,
38658
+ report.customer_evidence_action_packet,
38659
+ operatorStatus.customer_operator_action_packet,
38660
+ operatorStatus.customer_evidence_action_packet,
38661
+ operatorStatus.customer_actions ? {
38662
+ packet_role: "compressed_customer_operator_action_plan",
38663
+ action_count: asArray(operatorStatus.customer_actions).length,
38664
+ actions: operatorStatus.customer_actions,
38665
+ validation_commands: collectStringArrays(operatorStatus, /* @__PURE__ */ new Set(["next_commands"]))
38666
+ } : null,
38667
+ report.customer_actions ? { actions: report.customer_actions } : null
38668
+ ];
38669
+ for (const candidate of candidates) {
38670
+ const normalized = normalizeCustomerEvidenceActionPacket(candidate);
38671
+ if (normalized) return normalized;
38672
+ }
38673
+ }
38674
+ return void 0;
38675
+ }
38676
+ function normalizeObjectiveArtifactPath(value) {
38677
+ return value.trim() ? (0, import_node_path2.resolve)(value.trim()) : "";
38678
+ }
38679
+ function resolveObjectiveArtifactPath(value) {
38680
+ return normalizeObjectiveArtifactPath(value);
38681
+ }
38682
+ function firstNonEmptyObject(value) {
38683
+ const record2 = asRecord3(value);
38684
+ return Object.keys(record2).length > 0 ? record2 : void 0;
38685
+ }
38686
+ function pickEvidencePacket(value) {
38687
+ const record2 = firstNonEmptyObject(value);
38688
+ if (!record2) return void 0;
38689
+ const payload = firstNonEmptyObject(record2.payload);
38690
+ return payload ?? record2;
38691
+ }
38692
+ function readEvidencePacketFromPlan(path2) {
38693
+ const plan = asRecord3(readJsonArtifact(path2));
38694
+ const sourceReports = asRecord3(plan.source_reports);
38695
+ return pickEvidencePacket(plan.evidence) ?? pickEvidencePacket(plan.evidence_packet) ?? pickEvidencePacket(sourceReports.customer_live_status && asRecord3(sourceReports.customer_live_status).evidence) ?? pickEvidencePacket(asRecord3(sourceReports.setup_workflow).evidence_packet) ?? {};
38696
+ }
38697
+ function inferEvidenceFromProofPaths(evidence) {
38698
+ const record2 = asRecord3(evidence);
38699
+ if (!record2 || Object.keys(record2).length === 0) return evidence;
38700
+ return record2.evidence ?? evidence;
38701
+ }
38702
+ async function resolveEvidenceInput(opts) {
38703
+ if (opts.evidence) {
38704
+ return inferEvidenceFromProofPaths(await parseJsonOption(opts.evidence, "--evidence"));
38705
+ }
38706
+ if (!opts.plan) {
38707
+ throw new FohError({
38708
+ step: "objective.apply",
38709
+ error: "Missing objective evidence input: pass --evidence or --plan.",
38710
+ remediation: "Provide either --evidence <json|@file> or --plan <path-to-plan-json>.",
38711
+ statusCode: 400
38712
+ });
38713
+ }
38714
+ const planPath = resolveObjectiveArtifactPath(opts.plan);
38715
+ return inferEvidenceFromProofPaths(readEvidencePacketFromPlan(planPath));
38716
+ }
38717
+ function firstString(record2, keys) {
38718
+ for (const key of keys) {
38719
+ const value = normalizeString(record2[key]);
38720
+ if (value) return value;
38721
+ }
38722
+ return "";
38723
+ }
38724
+ function firstStringFromUnknown(value, keys) {
38725
+ return firstString(asRecord3(value), keys);
38726
+ }
38727
+ function truthy(record2, keys) {
38728
+ return keys.some((key) => record2[key] === true);
38729
+ }
38730
+ function statusFromDecision(value) {
38731
+ const normalized = value.toLowerCase();
38732
+ if (/fail|failed|error/.test(normalized)) return "fail";
38733
+ if (/ready|pass|passed|success|go_live/.test(normalized)) return "pass";
38734
+ return "hold";
38735
+ }
38736
+ function firstCustomerEvidenceAction(packet) {
38737
+ const actions = asArray(packet?.actions).map(asRecord3).filter((action) => normalizeString(action.id));
38738
+ return actions[0] ?? null;
38739
+ }
38740
+ function buildDeveloperReadinessPacket(input) {
38741
+ const businessName = resolveBusinessName(input.opts);
38742
+ const sourceUrl = normalizeString(input.opts.sourceUrl);
38743
+ const businessObjective = normalizeString(input.opts.businessObjective);
38744
+ const location = resolveLocation(input.opts);
38745
+ const tools = parseCsvOption(input.opts.tools ?? "widget,voice,email,callback,calendar,crm,webhook,whatsapp") ?? [];
38746
+ const setupDecision = firstString(input.setupWorkflow, ["decision", "current_decision", "status"]);
38747
+ const liveDecision = firstString(input.customerLiveStatus, ["decision", "current_decision", "status"]);
38748
+ const explicitGoLive = truthy(input.customerLiveStatus, ["go_live_allowed", "customer_live_ready", "production_ready"]) || truthy(input.setupWorkflow, ["go_live_allowed", "customer_live_ready", "production_ready"]);
38749
+ const firstAction = firstCustomerEvidenceAction(input.customerEvidenceActionPacket);
38750
+ const firstActionValidator = firstAction ? uniqueStrings(asArray(firstAction.validator_commands))[0] ?? null : null;
38751
+ const firstNextCommand = input.nextCommands[0] ?? null;
38752
+ return {
38753
+ schema_version: "developer_objective_readiness_packet.v1",
38754
+ packet_role: "coding_agent_objective_readiness",
38755
+ decision: input.classification.status,
38756
+ reason_code: input.classification.reasonCode,
38757
+ business_context: {
38758
+ business_name: businessName || null,
38759
+ business_objective: businessObjective || null,
38760
+ source_url: sourceUrl || null,
38761
+ location: location || null,
38762
+ target_mode: normalizeString(input.opts.targetMode) || "customer_owned_voice_trial",
38763
+ tools
38764
+ },
38765
+ input_completeness: {
38766
+ business_name: Boolean(businessName),
38767
+ source_url: Boolean(sourceUrl),
38768
+ business_objective: Boolean(businessObjective),
38769
+ requested_tools: tools.length > 0,
38770
+ target_mode: Boolean(normalizeString(input.opts.targetMode) || "customer_owned_voice_trial")
38771
+ },
38772
+ readiness_dimensions: {
38773
+ setup_workflow: {
38774
+ status: statusFromDecision(setupDecision),
38775
+ decision: setupDecision || null
38776
+ },
38777
+ customer_live_status: {
38778
+ status: explicitGoLive ? "pass" : statusFromDecision(liveDecision),
38779
+ decision: liveDecision || null
38780
+ },
38781
+ customer_evidence_actions: {
38782
+ status: firstAction ? "hold" : "pass",
38783
+ action_count: finiteNumber(input.customerEvidenceActionPacket?.action_count) ?? asArray(input.customerEvidenceActionPacket?.actions).length,
38784
+ first_action_id: firstAction ? normalizeString(firstAction.id) : null,
38785
+ first_validator_command: firstActionValidator
38786
+ },
38787
+ agent_operability: {
38788
+ status: firstNextCommand ? "pass" : "fail",
38789
+ first_next_command: firstNextCommand,
38790
+ normal_path_requires_raw_artifacts: false
38791
+ }
38792
+ },
38793
+ next_command: firstNextCommand,
38794
+ claim_boundaries: {
38795
+ developer_setup_loop_ready: Boolean(firstNextCommand),
38796
+ customer_live_claim_allowed: explicitGoLive && input.classification.status === "pass",
38797
+ production_claim_allowed: explicitGoLive && input.classification.status === "pass",
38798
+ do_not_claim: explicitGoLive && input.classification.status === "pass" ? [] : ["customer-live", "production-ready", "fully autonomous customer setup"]
38799
+ },
38800
+ agent_instructions: [
38801
+ "Use this packet before source_reports or raw artifacts.",
38802
+ "Execute next_command first, then rerun objective status.",
38803
+ "Treat customer evidence actions as request work, not proof."
38804
+ ]
38805
+ };
38806
+ }
38807
+ function resolveObjectiveReportPath(value) {
38808
+ const raw = normalizeString(value);
38809
+ if (!raw || raw === "latest") return (0, import_node_path2.resolve)(DEFAULT_OBJECTIVE_REPORT_PATH);
38810
+ return (0, import_node_path2.resolve)(raw);
38811
+ }
38812
+ function writeJsonArtifact2(path2, value) {
38813
+ (0, import_node_fs5.mkdirSync)((0, import_node_path2.dirname)(path2), { recursive: true });
38814
+ (0, import_node_fs5.writeFileSync)(path2, `${JSON.stringify(value, null, 2)}
38815
+ `, "utf8");
38816
+ }
38817
+ function readJsonArtifact(path2) {
38818
+ return JSON.parse((0, import_node_fs5.readFileSync)(path2, "utf8"));
38819
+ }
38820
+ function buildSetupBody(opts) {
38821
+ const tools = parseCsvOption(opts.tools ?? "widget,voice,email,callback,calendar,crm,webhook,whatsapp") ?? [];
38822
+ const businessName = resolveBusinessName(opts);
38823
+ const location = resolveLocation(opts);
38824
+ const body = {
38825
+ agency_name: businessName,
38826
+ business_objective: normalizeString(opts.businessObjective) || null,
38827
+ requested_tool_surface: tools,
38828
+ target_exposure_mode: normalizeString(opts.targetMode) || "customer_owned_voice_trial"
38829
+ };
38830
+ if (opts.sourceUrl) body.source_url = normalizeString(opts.sourceUrl);
38831
+ if (location) body.branch_location = location;
38832
+ return body;
38833
+ }
38834
+ function buildStatusParams(opts) {
38835
+ const params = new URLSearchParams();
38836
+ const businessName = resolveBusinessName(opts);
38837
+ const location = resolveLocation(opts);
38838
+ if (opts.environment) params.set("environment", normalizeString(opts.environment));
38839
+ if (businessName) params.set("agency_name", businessName);
38840
+ if (opts.sourceUrl) params.set("source_url", normalizeString(opts.sourceUrl));
38841
+ if (location) params.set("branch_location", location);
38842
+ if (opts.tools) params.set("tools", normalizeString(opts.tools));
38843
+ if (opts.targetMode) params.set("target_mode", normalizeString(opts.targetMode));
38844
+ return params;
38845
+ }
38846
+ function resolveBusinessName(opts) {
38847
+ return normalizeString(opts.businessName) || normalizeString(opts.agencyName);
38848
+ }
38849
+ function resolveLocation(opts) {
38850
+ return normalizeString(opts.location) || normalizeString(opts.branchLocation);
38851
+ }
38852
+ function assertBusinessName(opts, step) {
38853
+ if (resolveBusinessName(opts)) return;
38854
+ throw new FohError({
38855
+ step,
38856
+ error: "Missing business name.",
38857
+ remediation: "Pass --business-name <name>. Legacy --agency-name <name> is still accepted.",
38858
+ statusCode: 400
38859
+ });
38860
+ }
38861
+ function classifyStatus(input) {
38862
+ const setup = asRecord3(input.setupWorkflow);
38863
+ const live = asRecord3(input.customerLiveStatus);
38864
+ const allReasonCodes = uniqueStrings([
38865
+ ...collectStringArrays(setup, /* @__PURE__ */ new Set(["reason_codes", "blocker_reason_codes"])),
38866
+ ...collectStringArrays(live, /* @__PURE__ */ new Set(["reason_codes", "blocker_reason_codes"]))
38867
+ ]);
38868
+ const setupDecision = firstString(setup, ["decision", "current_decision", "status"]);
38869
+ const liveDecision = firstString(live, ["decision", "current_decision", "status"]);
38870
+ const explicitGoLive = truthy(live, ["go_live_allowed", "customer_live_ready", "production_ready"]) || truthy(setup, ["go_live_allowed", "customer_live_ready", "production_ready"]);
38871
+ const hasBlockingReason = allReasonCodes.some((code) => !/_ok$|passed|ready|no_findings/.test(code));
38872
+ const hasHoldDecision = [setupDecision, liveDecision].some(
38873
+ (decision) => /hold|blocked|fail|failed|no_go|developer_supervised|founder_assisted/.test(decision.toLowerCase())
38874
+ );
38875
+ if (explicitGoLive && !hasBlockingReason && !hasHoldDecision) {
38876
+ return {
38877
+ status: "pass",
38878
+ reasonCode: "objective_ready",
38879
+ summary: "Objective evidence is ready for the requested launch mode."
38880
+ };
38881
+ }
38882
+ return {
38883
+ status: "hold",
38884
+ reasonCode: allReasonCodes[0] || "objective_needs_next_action",
38885
+ summary: "Objective is not yet proven for the requested mode; follow next_commands exactly."
38886
+ };
38887
+ }
38888
+ function buildObjectiveReport(input) {
38889
+ const setup = asRecord3(input.setupWorkflow);
38890
+ const live = asRecord3(input.customerLiveStatus);
38891
+ const classification = classifyStatus(input);
38892
+ const reasonCodes = uniqueStrings([
38893
+ classification.reasonCode,
38894
+ ...collectStringArrays(setup, /* @__PURE__ */ new Set(["reason_codes", "blocker_reason_codes"])),
38895
+ ...collectStringArrays(live, /* @__PURE__ */ new Set(["reason_codes", "blocker_reason_codes"]))
38896
+ ]);
38897
+ const debugSource = normalizeString(input.opts.out) || "latest";
38898
+ const nextCommands = dedupeCommands([
38899
+ ...collectStringArrays(setup, /* @__PURE__ */ new Set(["next_commands"])),
38900
+ ...collectStringArrays(live, /* @__PURE__ */ new Set(["next_commands"])),
38901
+ `foh objective debug --from ${debugSource} --json`
38902
+ ]);
38903
+ const allowedMode = firstString(live, ["allowed_mode", "highest_allowed_mode", "current_mode"]) || firstString(setup, ["allowed_mode", "highest_allowed_mode", "current_mode"]) || null;
38904
+ const blockedModes = uniqueStrings([
38905
+ ...collectStringArrays(setup, /* @__PURE__ */ new Set(["blocked_modes"])),
38906
+ ...collectStringArrays(live, /* @__PURE__ */ new Set(["blocked_modes"]))
38907
+ ]);
38908
+ const evidenceRefs = uniqueStrings([
38909
+ ...collectArtifactRefs(setup),
38910
+ ...collectArtifactRefs(live)
38911
+ ]);
38912
+ const customerEvidenceActionPacket = customerEvidenceActionPacketFromSources(live, setup);
38913
+ const developerReadinessPacket = buildDeveloperReadinessPacket({
38914
+ opts: input.opts,
38915
+ setupWorkflow: setup,
38916
+ customerLiveStatus: live,
38917
+ classification,
38918
+ nextCommands,
38919
+ customerEvidenceActionPacket
38920
+ });
38921
+ return cliEnvelope({
38922
+ schemaVersion: "agent_workbench_report.v1",
38923
+ status: classification.status,
38924
+ reasonCode: classification.reasonCode,
38925
+ summary: classification.summary,
38926
+ ids: {
38927
+ org_id: input.opts.org ?? null
38928
+ },
38929
+ artifacts: {
38930
+ evidence_refs: evidenceRefs
38931
+ },
38932
+ nextCommands,
38933
+ spendClass: "free",
38934
+ safeToRetry: true,
38935
+ extra: {
38936
+ objective: {
38937
+ business_objective: normalizeString(input.opts.businessObjective) || null,
38938
+ business_name: resolveBusinessName(input.opts) || null,
38939
+ agency_name: normalizeString(input.opts.agencyName) || resolveBusinessName(input.opts) || null,
38940
+ source_url: normalizeString(input.opts.sourceUrl) || null,
38941
+ location: resolveLocation(input.opts) || null,
38942
+ branch_location: normalizeString(input.opts.branchLocation) || resolveLocation(input.opts) || null,
38943
+ target_mode: normalizeString(input.opts.targetMode) || "customer_owned_voice_trial",
38944
+ tools: parseCsvOption(input.opts.tools ?? "widget,voice,email,callback,calendar,crm,webhook,whatsapp") ?? []
38945
+ },
38946
+ allowed_mode: allowedMode,
38947
+ blocked_modes: blockedModes,
38948
+ reason_codes: reasonCodes,
38949
+ next_action: nextCommands[0] ?? null,
38950
+ developer_readiness_packet: developerReadinessPacket,
38951
+ customer_evidence_action_packet: customerEvidenceActionPacket ?? null,
38952
+ source_report_summaries: {
38953
+ setup_workflow: summarizeSourceReport(setup),
38954
+ customer_live_status: summarizeSourceReport(live)
38955
+ },
38956
+ artifact_policy: {
38957
+ normal_path_fields: ["status", "reason_code", "reason_codes", "next_action", "next_commands", "developer_readiness_packet", "customer_evidence_action_packet", "source_report_summaries", "agent_guidance"],
38958
+ diagnostic_fields: ["source_reports", "artifacts.evidence_refs"],
38959
+ raw_artifact_reads_required: false,
38960
+ mutable_latest_json_policy: "diagnostic_only",
38961
+ immutable_evidence_refs_required: true
38962
+ },
38963
+ source_reports: {
38964
+ setup_workflow: setup,
38965
+ customer_live_status: live
38966
+ },
38967
+ agent_guidance: {
38968
+ normal_user_path: "Use objective commands first; inspect source_reports only when debug asks for it.",
38969
+ do_not_infer_from_raw_artifacts: true
38970
+ }
38971
+ }
38972
+ });
38973
+ }
38974
+ function withObjectiveArtifactPath(report, artifactPath) {
38975
+ return {
38976
+ ...report,
38977
+ artifacts: {
38978
+ ...asRecord3(report.artifacts),
38979
+ objective_report: artifactPath
38980
+ },
38981
+ artifact_path: artifactPath
38982
+ };
38983
+ }
38984
+ function stripDiagnosticField(target, fieldPath) {
38985
+ const parts = fieldPath.split(".").map((part) => part.trim()).filter(Boolean);
38986
+ if (parts.length === 0) return;
38987
+ let current = target;
38988
+ for (let index = 0; index < parts.length - 1; index += 1) {
38989
+ const next = asRecord3(current?.[parts[index]]);
38990
+ if (Object.keys(next).length === 0) return;
38991
+ current = next;
38992
+ }
38993
+ if (!current) return;
38994
+ delete current[parts[parts.length - 1]];
38995
+ }
38996
+ function buildObjectiveNormalPathOutput(report) {
38997
+ const output = JSON.parse(JSON.stringify(report));
38998
+ const artifactPolicy = asRecord3(output.artifact_policy);
38999
+ const diagnosticFields = uniqueStrings(asArray(artifactPolicy.diagnostic_fields).map((value) => normalizeString(value)));
39000
+ for (const fieldPath of diagnosticFields) stripDiagnosticField(output, fieldPath);
39001
+ return output;
39002
+ }
39003
+ function classifyReasonCode(reasonCode) {
39004
+ const normalized = reasonCode.toLowerCase();
39005
+ if (/credential|token|auth|login|permission|scope|key|secret/.test(normalized)) return "credentials";
39006
+ if (/source|official|fact|approval|conflict|provenance|unknown|intake/.test(normalized)) return "source_facts";
39007
+ if (/customer.*evidence|evidence|signoff|ownership|customer_owned|live.*validation/.test(normalized)) return "customer_evidence";
39008
+ if (/voice|stt|tts|turn|audio|latency|tool|webhook|calendar|crm|email|callback|whatsapp/.test(normalized)) return "voice_or_tool_quality";
39009
+ if (/deploy|production|backup|rollback|runtime|env|release|drift/.test(normalized)) return "deploy_or_runtime";
39010
+ if (/docs|api|route|command|registry|schema|contract/.test(normalized)) return "docs_api_mismatch";
39011
+ if (/proof|simulation|cert|widget|artifact|gate|readiness/.test(normalized)) return "runtime_proof";
39012
+ return "uncategorized";
39013
+ }
39014
+ function buildDebugReport(sourcePath, objectiveReport) {
39015
+ const report = asRecord3(objectiveReport);
39016
+ const reasonCodes = uniqueStrings([
39017
+ firstString(report, ["reason_code"]),
39018
+ ...collectStringArrays(report, /* @__PURE__ */ new Set(["reason_codes", "blocker_reason_codes"]))
39019
+ ]);
39020
+ const nextCommands = dedupeCommands([
39021
+ ...collectStringArrays(report, /* @__PURE__ */ new Set(["next_commands"])),
39022
+ "foh objective status --business-name <name> --source-url <official_url> --out test-results/objective-status.latest.json --json"
39023
+ ]);
39024
+ const categories = uniqueStrings(reasonCodes.map(classifyReasonCode));
39025
+ const status = firstString(report, ["status"]);
39026
+ const isReady = report.ok === true || status === "pass" || status === "success";
39027
+ const dominantCategory = categories.find((category) => category !== "uncategorized") ?? categories[0] ?? "uncategorized";
39028
+ const sourceReports = asRecord3(report.source_reports);
39029
+ const customerEvidenceActionPacket = firstNonEmptyObject(report.customer_evidence_action_packet);
39030
+ const developerReadinessPacket = firstNonEmptyObject(report.developer_readiness_packet);
39031
+ if (isReady && reasonCodes.length <= 1 && reasonCodes[0] === "objective_ready") {
39032
+ return cliEnvelope({
39033
+ schemaVersion: "agent_workbench_debug.v1",
39034
+ status: "pass",
39035
+ reasonCode: "objective_debug_no_blockers",
39036
+ summary: "Objective report is ready; no repair packet is required.",
39037
+ artifacts: { objective_report: sourcePath },
39038
+ nextCommands,
39039
+ spendClass: "free",
39040
+ safeToRetry: true,
39041
+ extra: {
39042
+ blocker_categories: [],
39043
+ dominant_category: null,
39044
+ repair_packet: null
39045
+ }
39046
+ });
39047
+ }
39048
+ return cliEnvelope({
39049
+ schemaVersion: "agent_workbench_debug.v1",
39050
+ status: "hold",
39051
+ reasonCode: "objective_debug_repair_packet_ready",
39052
+ summary: `Objective is held by ${dominantCategory}; execute the first repair command before rerunning status.`,
39053
+ artifacts: { objective_report: sourcePath },
39054
+ nextCommands,
39055
+ spendClass: "free",
39056
+ safeToRetry: true,
39057
+ extra: {
39058
+ blocker_categories: categories,
39059
+ dominant_category: dominantCategory,
39060
+ repair_packet: {
39061
+ category: dominantCategory,
39062
+ reason_codes: reasonCodes,
39063
+ first_repair_command: nextCommands[0] ?? null,
39064
+ developer_readiness_packet: developerReadinessPacket ?? null,
39065
+ customer_evidence_action_packet: customerEvidenceActionPacket ?? null,
39066
+ setup_decision: firstStringFromUnknown(sourceReports.setup_workflow, ["decision", "current_decision", "status"]) || null,
39067
+ customer_live_decision: firstStringFromUnknown(sourceReports.customer_live_status, ["decision", "current_decision", "status"]) || null,
39068
+ principle: "Do not infer readiness from raw artifacts; repair the named blocker and rerun objective status."
39069
+ }
39070
+ }
39071
+ });
39072
+ }
39073
+ function registerObjective(program3) {
39074
+ const objective = program3.command("objective").description("Agent-native objective workflow: plan, apply, prove, status, debug");
39075
+ objective.command("plan").description("Generate an objective setup/workflow plan").option("--business-name <name>", "Business trading name").option("--agency-name <name>", "Legacy alias for --business-name").option("--business-objective <text>", "Natural-language business objective").option("--source-url <url>", "Official source URL").option("--location <value>", "Location or branch this setup represents").option("--branch-location <value>", "Legacy alias for --location").option("--tools <csv>", "Requested tool surfaces", "widget,voice,email,callback,calendar,crm,webhook,whatsapp").option("--target-mode <mode>", "Target launch/exposure mode", "customer_owned_voice_trial").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--out <path>", "Write objective plan JSON to this file path").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
39076
+ assertBusinessName(opts, "objective.plan");
39077
+ const report = await apiFetch("/v1/console/agency-setup/workflow", {
39078
+ method: "POST",
39079
+ body: JSON.stringify(buildSetupBody(opts)),
39080
+ orgId: opts.org,
39081
+ apiUrlOverride: opts.apiUrl
39082
+ });
39083
+ const outPath = opts.out ? resolveObjectiveReportPath(opts.out) : null;
39084
+ const output = outPath ? { ...asRecord3(report), artifact_path: outPath } : report;
39085
+ if (outPath) writeJsonArtifact2(outPath, output);
39086
+ format(output, { json: opts.json ?? false });
39087
+ }));
39088
+ objective.command("apply").description("Apply objective evidence to release launch packet gating").option("--evidence <json|@file>", "Redacted evidence payload JSON, or @/path/to/file.json").option("--plan <path>", "Optional objective plan file to derive evidence payload from").option("--dry-run", "Validate evidence payload without persisting it").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--out <path>", "Write objective apply result JSON to this file path").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
39089
+ const evidence = await resolveEvidenceInput(opts);
39090
+ const body = {
39091
+ ...evidence && typeof evidence === "object" && !Array.isArray(evidence) ? evidence : { evidence }
39092
+ };
39093
+ if (!opts.dryRun) body.apply = true;
39094
+ const report = await apiFetch("/v1/console/release-launch-packet/evidence-dry-run", {
39095
+ method: "POST",
39096
+ body: JSON.stringify(body),
39097
+ orgId: opts.org,
39098
+ apiUrlOverride: opts.apiUrl
39099
+ });
39100
+ const outPath = opts.out ? resolveObjectiveReportPath(opts.out) : null;
39101
+ const output = outPath ? { ...asRecord3(report), artifact_path: outPath } : report;
39102
+ if (outPath) writeJsonArtifact2(outPath, output);
39103
+ format(output, { json: opts.json ?? false });
39104
+ }));
39105
+ objective.command("prove").description("Run objective proof status against customer-live gate").option("--business-name <name>", "Business trading name").option("--agency-name <name>", "Legacy alias for --business-name").option("--business-objective <text>", "Natural-language business objective").option("--source-url <url>", "Official source URL").option("--location <value>", "Location or branch this setup represents").option("--branch-location <value>", "Legacy alias for --location").option("--tools <csv>", "Requested tool surfaces", "widget,voice,email,callback,calendar,crm,webhook,whatsapp").option("--target-mode <mode>", "Target launch/exposure mode", "customer_owned_voice_trial").option("--environment <value>", "Environment filter").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--out <path>", "Write objective proof JSON to this file path").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
39106
+ assertBusinessName(opts, "objective.prove");
39107
+ const status = await apiFetch(withQuery("/v1/console/customer-live-status", buildStatusParams(opts)), {
39108
+ orgId: opts.org,
39109
+ apiUrlOverride: opts.apiUrl
39110
+ });
39111
+ const outPath = opts.out ? resolveObjectiveReportPath(opts.out) : null;
39112
+ const output = outPath ? { ...asRecord3(status), artifact_path: outPath } : status;
39113
+ if (outPath) writeJsonArtifact2(outPath, output);
39114
+ format(output, { json: opts.json ?? false });
39115
+ }));
39116
+ objective.command("status").description("Compose setup and launch evidence into one agent workbench status envelope").option("--business-name <name>", "Business trading name").option("--agency-name <name>", "Legacy alias for --business-name").option("--business-objective <text>", "Natural-language business objective").option("--source-url <url>", "Official source URL").option("--location <value>", "Location or branch this setup represents").option("--branch-location <value>", "Legacy alias for --location").option("--tools <csv>", "Requested tool surfaces", "widget,voice,email,callback,calendar,crm,webhook,whatsapp").option("--target-mode <mode>", "Target launch/exposure mode", "customer_owned_voice_trial").option("--environment <value>", "Environment filter").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--out <path>", "Write objective report JSON to this file path").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
39117
+ assertBusinessName(opts, "objective.status");
39118
+ const setupWorkflow = await apiFetch("/v1/console/agency-setup/workflow", {
39119
+ method: "POST",
39120
+ body: JSON.stringify(buildSetupBody(opts)),
39121
+ orgId: opts.org,
39122
+ apiUrlOverride: opts.apiUrl
39123
+ });
39124
+ const customerLiveStatus = await apiFetch(withQuery("/v1/console/customer-live-status", buildStatusParams(opts)), {
39125
+ orgId: opts.org,
39126
+ apiUrlOverride: opts.apiUrl
39127
+ });
39128
+ const report = buildObjectiveReport({ opts, setupWorkflow, customerLiveStatus });
39129
+ const artifactPath = resolveObjectiveReportPath(opts.out);
39130
+ const fullOutput = withObjectiveArtifactPath(report, artifactPath);
39131
+ writeJsonArtifact2(artifactPath, fullOutput);
39132
+ format(buildObjectiveNormalPathOutput(fullOutput), { json: opts.json ?? false });
39133
+ }));
39134
+ objective.command("debug").description("Explain the next debugging action for an objective report").option("--from <source>", "Report source alias or path", "latest").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
39135
+ const sourcePath = resolveObjectiveReportPath(opts.from);
39136
+ if (!(0, import_node_fs5.existsSync)(sourcePath)) {
39137
+ format(cliEnvelope({
39138
+ schemaVersion: "agent_workbench_debug.v1",
39139
+ status: "fail",
39140
+ reasonCode: "objective_report_not_found",
39141
+ summary: `Objective report not found at ${sourcePath}.`,
39142
+ artifacts: { objective_report: sourcePath },
39143
+ nextCommands: [
39144
+ `foh objective status --business-name <name> --source-url <official_url> --out ${DEFAULT_OBJECTIVE_REPORT_PATH} --json`
39145
+ ],
39146
+ spendClass: "free",
39147
+ safeToRetry: true
39148
+ }), { json: opts.json ?? false });
39149
+ return;
39150
+ }
39151
+ let objectiveReport;
39152
+ try {
39153
+ objectiveReport = readJsonArtifact(sourcePath);
39154
+ } catch {
39155
+ format(cliEnvelope({
39156
+ schemaVersion: "agent_workbench_debug.v1",
39157
+ status: "fail",
39158
+ reasonCode: "objective_report_invalid",
39159
+ summary: `Objective report at ${sourcePath} is not valid JSON.`,
39160
+ artifacts: { objective_report: sourcePath },
39161
+ nextCommands: [
39162
+ `foh objective status --business-name <name> --source-url <official_url> --out ${DEFAULT_OBJECTIVE_REPORT_PATH} --json`
39163
+ ],
39164
+ spendClass: "free",
39165
+ safeToRetry: true
39166
+ }), { json: opts.json ?? false });
39167
+ return;
39168
+ }
39169
+ format(buildDebugReport(sourcePath, objectiveReport), { json: opts.json ?? false });
39170
+ }));
39171
+ }
39172
+
38545
39173
  // src/commands/interactive.ts
38546
39174
  var import_readline2 = require("readline");
38547
39175
  var import_child_process2 = require("child_process");
@@ -38651,6 +39279,66 @@ var COMMAND_SURFACE_DEFINITIONS = [
38651
39279
  shellSlash: "/status",
38652
39280
  includeInSuggestions: true
38653
39281
  },
39282
+ {
39283
+ id: "objective_status",
39284
+ commandPath: ["objective", "status"],
39285
+ invokeArgs: ["objective", "status", "--help"],
39286
+ label: "objective status",
39287
+ descriptionFallback: "Compose setup and launch evidence into one agent workbench status envelope.",
39288
+ requiresAuth: true,
39289
+ requiresOrg: true,
39290
+ mutatesState: "read",
39291
+ examples: ['foh objective status --business-name "Acme" --source-url https://example.com --out test-results/objective-status.latest.json --json'],
39292
+ homeContexts: ["authenticated_with_org"],
39293
+ homeAliases: ["objective", "status objective"],
39294
+ shellSlash: "/objective",
39295
+ includeInSuggestions: true
39296
+ },
39297
+ {
39298
+ id: "objective_plan",
39299
+ commandPath: ["objective", "plan"],
39300
+ invokeArgs: ["objective", "plan", "--help"],
39301
+ label: "objective plan",
39302
+ descriptionFallback: "Generate an objective setup/workflow plan.",
39303
+ requiresAuth: true,
39304
+ requiresOrg: true,
39305
+ mutatesState: "read",
39306
+ examples: ['foh objective plan --business-name "Acme" --business-objective "Qualify leads" --source-url https://example.com --out test-results/objective-plan.latest.json --json'],
39307
+ homeContexts: ["authenticated_with_org"],
39308
+ homeAliases: ["objective plan"],
39309
+ shellSlash: "/objective-plan",
39310
+ includeInSuggestions: true
39311
+ },
39312
+ {
39313
+ id: "objective_apply",
39314
+ commandPath: ["objective", "apply"],
39315
+ invokeArgs: ["objective", "apply", "--help"],
39316
+ label: "objective apply",
39317
+ descriptionFallback: "Apply objective evidence packets against customer-live launch packet gates.",
39318
+ requiresAuth: true,
39319
+ requiresOrg: true,
39320
+ mutatesState: "write",
39321
+ examples: ["foh objective apply --evidence @test-results/evidence.json --org <org-id> --out test-results/objective-apply.latest.json --json"],
39322
+ homeContexts: ["authenticated_with_org"],
39323
+ homeAliases: ["objective apply"],
39324
+ shellSlash: "/objective-apply",
39325
+ includeInSuggestions: true
39326
+ },
39327
+ {
39328
+ id: "objective_prove",
39329
+ commandPath: ["objective", "prove"],
39330
+ invokeArgs: ["objective", "prove", "--help"],
39331
+ label: "objective prove",
39332
+ descriptionFallback: "Check one objective against customer-live proof gates.",
39333
+ requiresAuth: true,
39334
+ requiresOrg: true,
39335
+ mutatesState: "read",
39336
+ examples: ['foh objective prove --business-name "Acme" --source-url https://example.com --environment staging --json'],
39337
+ homeContexts: ["authenticated_with_org"],
39338
+ homeAliases: ["objective prove"],
39339
+ shellSlash: "/objective-prove",
39340
+ includeInSuggestions: true
39341
+ },
38654
39342
  {
38655
39343
  id: "templates_list",
38656
39344
  commandPath: ["templates", "list"],
@@ -38791,6 +39479,7 @@ var HOME_QUICK_ACTION_ORDER = {
38791
39479
  unauthenticated: ["auth_login", "interactive_shell", "full_help", "auth_login_help"],
38792
39480
  authenticated_no_org: ["org_list", "interactive_shell", "org_use_help", "auth_whoami", "full_help"],
38793
39481
  authenticated_with_org: [
39482
+ "objective_status",
38794
39483
  "tenant_status",
38795
39484
  "interactive_shell",
38796
39485
  "templates_list",
@@ -39133,7 +39822,7 @@ async function runSelf(args, apiUrlOverride) {
39133
39822
  if (apiUrlOverride && !spawnArgs.includes("--api-url")) {
39134
39823
  spawnArgs.push("--api-url", apiUrlOverride);
39135
39824
  }
39136
- return await new Promise((resolve15, reject) => {
39825
+ return await new Promise((resolve16, reject) => {
39137
39826
  const child = (0, import_child_process2.spawn)(process.execPath, [process.argv[1], ...spawnArgs], {
39138
39827
  stdio: "inherit",
39139
39828
  env: {
@@ -39143,7 +39832,7 @@ async function runSelf(args, apiUrlOverride) {
39143
39832
  }
39144
39833
  });
39145
39834
  child.once("error", reject);
39146
- child.once("close", (code) => resolve15(typeof code === "number" ? code : 1));
39835
+ child.once("close", (code) => resolve16(typeof code === "number" ? code : 1));
39147
39836
  });
39148
39837
  }
39149
39838
  function resolveInteractiveState(apiUrlOverride) {
@@ -39330,10 +40019,10 @@ async function confirmMutationIfNeeded(rl, args) {
39330
40019
  const commandEntry = getCommandGraphEntryForArgs(args);
39331
40020
  const label = commandEntry ? `foh ${commandEntry.args.join(" ")}` : `foh ${args.join(" ")}`;
39332
40021
  const warning = mutationState === "high_risk" ? "high-risk" : "write";
39333
- const approved = await new Promise((resolve15) => {
40022
+ const approved = await new Promise((resolve16) => {
39334
40023
  rl.question(`Confirm ${warning} command ${label}? [y/N]: `, (answer) => {
39335
40024
  const normalized = answer.trim().toLowerCase();
39336
- resolve15(normalized === "y" || normalized === "yes");
40025
+ resolve16(normalized === "y" || normalized === "yes");
39337
40026
  });
39338
40027
  });
39339
40028
  if (approved) return { approved: true };
@@ -39501,7 +40190,7 @@ function registerInteractive(program3) {
39501
40190
  rl.on("SIGINT", () => {
39502
40191
  rl.close();
39503
40192
  });
39504
- await new Promise((resolve15) => {
40193
+ await new Promise((resolve16) => {
39505
40194
  rl.on("close", () => {
39506
40195
  process.stdout.write("\n");
39507
40196
  memory.history = rl.history.slice(0, 500);
@@ -39509,7 +40198,7 @@ function registerInteractive(program3) {
39509
40198
  flushInteractiveSessionArtifact(sessionArtifact);
39510
40199
  flushInteractiveSessionReport(sessionArtifact);
39511
40200
  saveInteractiveShellMemory(memory);
39512
- resolve15();
40201
+ resolve16();
39513
40202
  });
39514
40203
  });
39515
40204
  });
@@ -39841,7 +40530,7 @@ async function runSelf2(args, apiUrlOverride) {
39841
40530
  if (apiUrlOverride && !spawnArgs.includes("--api-url")) {
39842
40531
  spawnArgs.push("--api-url", apiUrlOverride);
39843
40532
  }
39844
- return await new Promise((resolve15, reject) => {
40533
+ return await new Promise((resolve16, reject) => {
39845
40534
  const child = (0, import_child_process3.spawn)(process.execPath, [process.argv[1], ...spawnArgs], {
39846
40535
  stdio: "inherit",
39847
40536
  env: {
@@ -39851,7 +40540,7 @@ async function runSelf2(args, apiUrlOverride) {
39851
40540
  }
39852
40541
  });
39853
40542
  child.once("error", reject);
39854
- child.once("close", (code) => resolve15(typeof code === "number" ? code : 1));
40543
+ child.once("close", (code) => resolve16(typeof code === "number" ? code : 1));
39855
40544
  });
39856
40545
  }
39857
40546
  function shouldUseInteractiveHome(argv) {
@@ -40200,17 +40889,17 @@ function detectUpdateAvailability(currentVersion, cwd = process.cwd()) {
40200
40889
  async function applyRepoUpdate(repoRoot) {
40201
40890
  const scriptPath = (0, import_path10.join)(repoRoot, "scripts", "Install-FohCli.ps1");
40202
40891
  if (process.platform === "win32") {
40203
- return await new Promise((resolve15, reject) => {
40892
+ return await new Promise((resolve16, reject) => {
40204
40893
  const child = (0, import_child_process4.spawn)(
40205
40894
  "powershell",
40206
40895
  ["-ExecutionPolicy", "Bypass", "-File", scriptPath],
40207
40896
  { stdio: "inherit" }
40208
40897
  );
40209
40898
  child.once("error", reject);
40210
- child.once("close", (code) => resolve15(typeof code === "number" ? code : 1));
40899
+ child.once("close", (code) => resolve16(typeof code === "number" ? code : 1));
40211
40900
  });
40212
40901
  }
40213
- return await new Promise((resolve15, reject) => {
40902
+ return await new Promise((resolve16, reject) => {
40214
40903
  const child = (0, import_child_process4.spawn)(
40215
40904
  "corepack",
40216
40905
  ["pnpm", "cli:install:global"],
@@ -40220,7 +40909,7 @@ async function applyRepoUpdate(repoRoot) {
40220
40909
  }
40221
40910
  );
40222
40911
  child.once("error", reject);
40223
- child.once("close", (code) => resolve15(typeof code === "number" ? code : 1));
40912
+ child.once("close", (code) => resolve16(typeof code === "number" ? code : 1));
40224
40913
  });
40225
40914
  }
40226
40915
  function shouldShowUpdateNotice(argv = process.argv) {
@@ -40692,9 +41381,9 @@ function readCommandRecords(runDir) {
40692
41381
  }
40693
41382
 
40694
41383
  // src/lib/external-agent-executor.ts
40695
- var import_fs20 = require("fs");
41384
+ var import_fs19 = require("fs");
40696
41385
  var import_os3 = require("os");
40697
- var import_path19 = require("path");
41386
+ var import_path18 = require("path");
40698
41387
  var import_child_process6 = require("child_process");
40699
41388
 
40700
41389
  // src/lib/external-agent-executor-env.ts
@@ -40865,695 +41554,174 @@ function copyExternalAgentCommandCaptureArtifacts(input) {
40865
41554
  }
40866
41555
 
40867
41556
  // src/lib/external-agent-executor-classification.ts
40868
- var import_fs18 = require("fs");
40869
- var import_path17 = require("path");
40870
-
40871
- // src/lib/external-agent-run-summary.ts
40872
41557
  var import_fs17 = require("fs");
40873
41558
  var import_path16 = require("path");
40874
- var REQUIRED_RUN_FIELDS = [
40875
- "schema_version",
40876
- "run_id",
40877
- "status",
40878
- "model_provider",
40879
- "model_name",
40880
- "prompt_version",
40881
- "started_at",
40882
- "manual_intervention_count",
40883
- "environment",
40884
- "public_entrypoints",
40885
- "commands_run",
40886
- "docs_pages_used",
40887
- "artifacts"
40888
- ];
40889
- var VALID_STATUSES = /* @__PURE__ */ new Set(["pass", "hold", "fail"]);
40890
- var DOC_URL_RE = /https:\/\/frontofhouse\.okii\.uk\/[^\s"'`)<>,;\\\]}]*/g;
40891
- function quoteShellArg(value) {
40892
- const text = String(value);
40893
- if (/^[A-Za-z0-9_./:=@-]+$/.test(text)) return text;
40894
- return `"${text.replace(/(["$`])/g, "\\$1")}"`;
41559
+ function proofArtifactPasses(runDir) {
41560
+ const proofPath = (0, import_path16.join)(runDir, "proof.json");
41561
+ if (!(0, import_fs17.existsSync)(proofPath)) return false;
41562
+ try {
41563
+ const parsed = JSON.parse((0, import_fs17.readFileSync)(proofPath, "utf8"));
41564
+ return parsed.ok === true || parsed.status === "pass" || parsed.status === "passed";
41565
+ } catch {
41566
+ return false;
41567
+ }
40895
41568
  }
40896
- function externalAgentSummaryCommand(root) {
40897
- const summaryPath = (0, import_path16.join)(root, "latest-summary.json");
40898
- const reportPath = (0, import_path16.join)(root, "summary.report.json");
41569
+ function readIfExists(path2) {
41570
+ return (0, import_fs17.existsSync)(path2) ? (0, import_fs17.readFileSync)(path2, "utf8") : "";
41571
+ }
41572
+ function relativeArtifactName(path2) {
41573
+ return (0, import_path16.basename)(path2);
41574
+ }
41575
+ function externalAgentSummaryTemplateCommand() {
40899
41576
  return [
40900
41577
  "foh",
40901
41578
  "eval",
40902
41579
  "external-agent",
40903
41580
  "summary",
40904
41581
  "--root",
40905
- quoteShellArg(root),
41582
+ "<batch_dir>",
40906
41583
  "--out",
40907
- quoteShellArg(summaryPath),
41584
+ "<batch_dir>/latest-summary.json",
40908
41585
  "--report",
40909
- quoteShellArg(reportPath),
41586
+ "<batch_dir>/summary.report.json",
40910
41587
  "--json"
40911
41588
  ].join(" ");
40912
41589
  }
40913
- function readJson(filePath) {
40914
- return JSON.parse((0, import_fs17.readFileSync)(filePath, "utf8").replace(/^\uFEFF/, ""));
40915
- }
40916
- function readNdjson(filePath) {
40917
- if (!(0, import_fs17.existsSync)(filePath)) return [];
40918
- return (0, import_fs17.readFileSync)(filePath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
40919
- try {
40920
- const parsed = JSON.parse(line);
40921
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
40922
- } catch {
40923
- return null;
40924
- }
40925
- }).filter((record2) => Boolean(record2));
40926
- }
40927
- function asObject(value) {
40928
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
40929
- }
40930
- function toArray2(value) {
40931
- return Array.isArray(value) ? value : [];
40932
- }
40933
- function increment(map3, key, amount = 1) {
40934
- const normalized = String(key || "unknown");
40935
- map3.set(normalized, (map3.get(normalized) || 0) + amount);
40936
- }
40937
- function ranked(map3) {
40938
- return Array.from(map3.entries()).map(([key, count]) => ({ key, count })).sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
40939
- }
40940
- function collectDocUrls(text) {
40941
- return Array.from(new Set((String(text || "").match(DOC_URL_RE) || []).map((url2) => url2.replace(/[.?!:]+$/g, "")).filter((url2) => url2.startsWith("https://frontofhouse.okii.uk/")))).sort();
40942
- }
40943
- function findRunCandidates(root) {
40944
- if (!(0, import_fs17.existsSync)(root)) return [];
40945
- const candidates = [];
40946
- const seenRunDirs = /* @__PURE__ */ new Set();
40947
- const captureDirs = [];
40948
- const stack = [root];
40949
- while (stack.length > 0) {
40950
- const current = stack.pop();
40951
- if (!current) continue;
40952
- for (const entry of (0, import_fs17.readdirSync)(current, { withFileTypes: true })) {
40953
- const absolute = (0, import_path16.join)(current, entry.name);
40954
- if (entry.isDirectory()) {
40955
- stack.push(absolute);
40956
- } else if (entry.isFile() && entry.name === "run.json") {
40957
- candidates.push({ path: absolute, synthetic: false });
40958
- seenRunDirs.add((0, import_path16.dirname)(absolute));
40959
- } else if (entry.isFile() && entry.name === "commands.ndjson") {
40960
- captureDirs.push(current);
40961
- }
40962
- }
41590
+ function classifyExternalAgentRun(input) {
41591
+ if (input.timedOut) return { status: "hold", reasonCode: `${input.run.command}_runner_timeout` };
41592
+ if (!input.artifactSafetyOk) return { status: "fail", reasonCode: "external_agent_artifact_safety_blocked" };
41593
+ const completedCommands = readCommandRecords(input.run.run_dir).filter((record2) => record2.phase === "completed");
41594
+ const observedVersions = completedCommands.map((record2) => String(record2.cli_version || "").trim()).filter((version2) => /^\d+\.\d+\.\d+$/.test(version2));
41595
+ if (observedVersions.some((version2) => version2 !== CLI_VERSION)) {
41596
+ return { status: "hold", reasonCode: "external_agent_cli_version_drift" };
40963
41597
  }
40964
- for (const captureDir of captureDirs) {
40965
- if (seenRunDirs.has(captureDir)) continue;
40966
- candidates.push({ path: (0, import_path16.join)(captureDir, "run.json"), synthetic: true });
41598
+ const commandReasonCodes2 = completedCommands.flatMap((record2) => [
41599
+ String(record2.reason_code || ""),
41600
+ ...Array.isArray(record2.check_reason_codes) ? record2.check_reason_codes.map((code) => String(code || "")) : []
41601
+ ]).filter(Boolean);
41602
+ const hasCommandReason = (pattern) => commandReasonCodes2.some((reason) => pattern.test(reason));
41603
+ if (hasCommandReason(new RegExp(PAID_RESOURCE_BLOCKED_REASON_CODE, "i"))) {
41604
+ return { status: "hold", reasonCode: PAID_RESOURCE_BLOCKED_REASON_CODE };
40967
41605
  }
40968
- return candidates.sort((a, b) => a.path.localeCompare(b.path));
40969
- }
40970
- function validateExternalAgentRun(value) {
40971
- const findings = [];
40972
- const run = asObject(value);
40973
- if (!run) return [{ id: "run_not_object", detail: "run artifact must be an object" }];
40974
- for (const field of REQUIRED_RUN_FIELDS) {
40975
- if (!(field in run)) findings.push({ id: "required_field_missing", field });
41606
+ if (hasCommandReason(/provider_capacity_blocked/i)) {
41607
+ return { status: "hold", reasonCode: "provider_capacity_blocked" };
40976
41608
  }
40977
- if (run.schema_version !== "external_agent_run.v1") {
40978
- findings.push({ id: "schema_version_invalid", expected: "external_agent_run.v1", actual: run.schema_version ?? null });
41609
+ if (hasCommandReason(/byon_voice_number_not_configured/i)) {
41610
+ return { status: "hold", reasonCode: "byon_voice_number_not_configured" };
40979
41611
  }
40980
- if (!VALID_STATUSES.has(String(run.status || ""))) {
40981
- findings.push({ id: "status_invalid", expected: Array.from(VALID_STATUSES), actual: run.status ?? null });
41612
+ if (hasCommandReason(/contact_phone_provisioning_failed/i)) {
41613
+ return { status: "hold", reasonCode: "voice_contact_phone_provisioning_failed" };
40982
41614
  }
40983
- if ((run.status === "hold" || run.status === "fail") && !String(run.failure_reason_code || "").trim()) {
40984
- findings.push({ id: "failure_reason_code_missing" });
41615
+ if (hasCommandReason(/voice_contact_expected_no_spend_hold/i)) {
41616
+ return { status: "hold", reasonCode: "voice_contact_expected_no_spend_hold" };
40985
41617
  }
40986
- if (!Number.isInteger(run.manual_intervention_count) || Number(run.manual_intervention_count) < 0) {
40987
- findings.push({ id: "manual_intervention_count_invalid" });
41618
+ if (hasCommandReason(/contact_phone_missing/i)) {
41619
+ return { status: "hold", reasonCode: "voice_contact_phone_missing" };
40988
41620
  }
40989
- if (!Array.isArray(run.commands_run)) findings.push({ id: "commands_run_invalid" });
40990
- if (!Array.isArray(run.docs_pages_used)) findings.push({ id: "docs_pages_used_invalid" });
40991
- if (!asObject(run.environment)) findings.push({ id: "environment_invalid" });
40992
- if (!asObject(run.artifacts)) findings.push({ id: "artifacts_invalid" });
40993
- if (toArray2(run.public_entrypoints).length === 0) findings.push({ id: "public_entrypoints_missing" });
40994
- return findings;
40995
- }
40996
- function runSortTime(run) {
40997
- const raw = String(run.ended_at || run.started_at || "");
40998
- const time3 = Date.parse(raw);
40999
- return Number.isFinite(time3) ? time3 : 0;
41000
- }
41001
- function latestCommandTime(commands) {
41002
- const times = commands.map((command) => String(command.completed_at || command.started_at || command.recorded_at || "")).map((raw) => ({ raw, time: Date.parse(raw) })).filter((entry) => Number.isFinite(entry.time)).sort((a, b) => b.time - a.time);
41003
- return times[0]?.raw ?? null;
41004
- }
41005
- function firstCommandTime(commands) {
41006
- const times = commands.map((command) => String(command.started_at || command.recorded_at || command.completed_at || "")).map((raw) => ({ raw, time: Date.parse(raw) })).filter((entry) => Number.isFinite(entry.time)).sort((a, b) => a.time - b.time);
41007
- return times[0]?.raw ?? null;
41008
- }
41009
- function commandReasonCodes(commands) {
41010
- const codes = /* @__PURE__ */ new Set();
41011
- for (const command of commands) {
41012
- if (command.reason_code) codes.add(String(command.reason_code));
41013
- for (const reasonCode of toArray2(command.check_reason_codes)) {
41014
- if (reasonCode) codes.add(String(reasonCode));
41015
- }
41621
+ if (hasCommandReason(/sim(?:ulation)?[_-]?cert(?:ify|ification)?.*failed|simulation_certification_failed/i)) {
41622
+ return { status: "hold", reasonCode: "simulation_certification_failed" };
41016
41623
  }
41017
- return Array.from(codes);
41018
- }
41019
- function syntheticStatusFromCommands(commands) {
41020
- const commandReasons = commandReasonCodes(commands);
41021
- const failed = commands.find((command) => {
41022
- const status = String(command.status || "").toLowerCase();
41023
- return status === "fail" || typeof command.exit_code === "number" && command.exit_code !== 0 && status !== "hold";
41024
- });
41025
- if (failed) {
41026
- return {
41027
- status: "fail",
41028
- reasonCode: String(failed.reason_code || commandReasons[0] || "external_agent_command_failed")
41029
- };
41624
+ if (hasCommandReason(/proof_held/i)) {
41625
+ return { status: "hold", reasonCode: "external_agent_proof_held" };
41030
41626
  }
41031
- const held = commands.find((command) => String(command.status || "").toLowerCase() === "hold");
41032
- if (held) {
41033
- return {
41034
- status: "hold",
41035
- reasonCode: String(held.reason_code || commandReasons[0] || "external_agent_command_held")
41036
- };
41627
+ if (hasCommandReason(/agent_limit_reached/i)) {
41628
+ return { status: "hold", reasonCode: "eval_org_agent_limit_reached" };
41037
41629
  }
41038
- if (commands.length === 0) {
41039
- return { status: "hold", reasonCode: "external_agent_capture_empty" };
41630
+ const agentMetadata = readExternalAgentMetadata(input.run.run_dir);
41631
+ const metadataBlockerCodes = agentMetadata.blocker_reason_codes;
41632
+ const hasMetadataBlocker = (pattern) => metadataBlockerCodes.some((reason) => pattern.test(reason));
41633
+ if (hasMetadataBlocker(/^customer_owned_requirement_unverified:/i)) {
41634
+ return { status: "hold", reasonCode: "customer_owned_requirements_unverified_expected_hold" };
41040
41635
  }
41041
- return { status: "pass", reasonCode: null };
41636
+ if (hasMetadataBlocker(/^api_health:/i) && metadataBlockerCodes.some((reason) => /^customer_owned_requirement_unverified:/i.test(reason))) {
41637
+ return { status: "hold", reasonCode: "customer_owned_requirements_unverified_expected_hold" };
41638
+ }
41639
+ const firstCommandReasonCode = commandReasonCodes2.find((reason) => reason.trim().length > 0) ?? null;
41640
+ if (firstCommandReasonCode) {
41641
+ return { status: "hold", reasonCode: firstCommandReasonCode };
41642
+ }
41643
+ const lastMessage = readIfExists(input.run.outputs.last_message);
41644
+ const stderr = readIfExists(input.run.outputs.stderr);
41645
+ const combined = `${lastMessage}
41646
+ ${stderr}`;
41647
+ if (/need[^.\n]*(?:private|source)[^.\n]*repo|cannot[^.\n]*without[^.\n]*(?:private|source)[^.\n]*repo|clone[^.\n]*(?:private|source)[^.\n]*repo/i.test(combined)) {
41648
+ return { status: "fail", reasonCode: "private_repo_assumption_detected" };
41649
+ }
41650
+ if (/(?:blocked|rejected|declined) by policy|EXEC_POLICY_BLOCKED|command execution was rejected|shell commands were rejected/i.test(combined)) {
41651
+ return { status: "hold", reasonCode: "codex_exec_policy_blocked" };
41652
+ }
41653
+ 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)) {
41654
+ return { status: "hold", reasonCode: "codex_sandbox_exec_blocked" };
41655
+ }
41656
+ if (/ENV_NETWORK_DNS_BLOCK|Could not resolve host|npm ping.*timeout|NO_EXECUTABLE_INSTALL/i.test(combined)) {
41657
+ return { status: "hold", reasonCode: "codex_network_dns_blocked" };
41658
+ }
41659
+ if (new RegExp(PAID_RESOURCE_BLOCKED_REASON_CODE, "i").test(combined)) {
41660
+ return { status: "hold", reasonCode: PAID_RESOURCE_BLOCKED_REASON_CODE };
41661
+ }
41662
+ if (/provider_capacity_blocked/i.test(combined)) {
41663
+ return { status: "hold", reasonCode: "provider_capacity_blocked" };
41664
+ }
41665
+ if (/byon_voice_number_not_configured/i.test(combined)) {
41666
+ return { status: "hold", reasonCode: "byon_voice_number_not_configured" };
41667
+ }
41668
+ if (/contact_phone_provisioning_failed/i.test(combined)) {
41669
+ return { status: "hold", reasonCode: "voice_contact_phone_provisioning_failed" };
41670
+ }
41671
+ if (/voice_contact_expected_no_spend_hold/i.test(combined)) {
41672
+ return { status: "hold", reasonCode: "voice_contact_expected_no_spend_hold" };
41673
+ }
41674
+ if (/contact_phone_missing/i.test(combined)) {
41675
+ return { status: "hold", reasonCode: "voice_contact_phone_missing" };
41676
+ }
41677
+ if (/simulation_certification_failed/i.test(combined)) {
41678
+ return { status: "hold", reasonCode: "simulation_certification_failed" };
41679
+ }
41680
+ if (/proof_held/i.test(combined)) {
41681
+ return { status: "hold", reasonCode: "external_agent_proof_held" };
41682
+ }
41683
+ if (/agent_limit_reached/i.test(combined)) {
41684
+ return { status: "hold", reasonCode: "eval_org_agent_limit_reached" };
41685
+ }
41686
+ if (/browser|approve|approval|login|auth|sign in/i.test(combined) && !proofArtifactPasses(input.run.run_dir)) {
41687
+ return { status: "hold", reasonCode: "auth_browser_approval_required" };
41688
+ }
41689
+ if (input.exitCode !== 0) return { status: "hold", reasonCode: `${input.run.command}_runner_nonzero_exit` };
41690
+ if (proofArtifactPasses(input.run.run_dir)) return { status: "pass", reasonCode: null };
41691
+ return { status: "hold", reasonCode: "external_agent_proof_artifact_missing" };
41042
41692
  }
41043
- function synthesizeRunFromCapture(runPath) {
41044
- const runDir = (0, import_path16.dirname)(runPath);
41045
- const commands = collapseCommandRecords(readNdjson((0, import_path16.join)(runDir, "commands.ndjson")));
41046
- const metadata = asObject((0, import_fs17.existsSync)((0, import_path16.join)(runDir, "external-agent-metadata.json")) ? readJson((0, import_path16.join)(runDir, "external-agent-metadata.json")) : {});
41047
- const blockerCodes = toArray2(metadata?.blocker_reason_codes).map(String).filter(Boolean);
41048
- const commandClassification = syntheticStatusFromCommands(commands);
41049
- const status = commandClassification.status === "fail" ? "fail" : blockerCodes.length > 0 ? "hold" : commandClassification.status;
41050
- const reasonCode = commandClassification.status === "fail" ? commandClassification.reasonCode : blockerCodes[0] || commandClassification.reasonCode;
41051
- const firstCommand = commands[0] || {};
41052
- const startedAt = firstCommandTime(commands) || (/* @__PURE__ */ new Date(0)).toISOString();
41053
- const endedAt = latestCommandTime(commands) || startedAt;
41054
- const docs = toArray2(metadata?.docs_pages_used).map(String).filter(Boolean);
41055
- const runId = (0, import_path16.dirname)(runPath).split(/[\\/]/).filter(Boolean).slice(-3).join("-") || "capture-only-run";
41693
+ function buildExecutedExternalAgentRunArtifact(input) {
41694
+ const commands = readCommandRecords(input.run.run_dir);
41695
+ const agentMetadata = readExternalAgentMetadata(input.run.run_dir);
41056
41696
  return {
41057
41697
  schema_version: "external_agent_run.v1",
41058
- run_id: runId,
41059
- status,
41060
- failure_reason_code: status === "pass" ? null : reasonCode || "external_agent_capture_unfinalized",
41061
- model_provider: "unknown",
41062
- model_name: "unknown",
41063
- prompt_version: String(firstCommand.prompt_version || "unknown"),
41064
- started_at: startedAt,
41065
- ended_at: endedAt,
41698
+ run_id: input.run.run_id,
41699
+ status: input.status,
41700
+ failure_reason_code: input.reasonCode,
41701
+ model_provider: input.run.model_provider,
41702
+ model_name: input.run.model_name,
41703
+ runner_model: input.run.runner_model,
41704
+ agent_shell: `${input.run.command}-exec`,
41705
+ workspace_type: "clean-no-repo-programmatic",
41706
+ prompt_version: input.run.prompt_version,
41707
+ prompt_path: "prompt.txt",
41708
+ started_at: input.startedAt,
41709
+ ended_at: input.endedAt,
41066
41710
  manual_intervention_count: 0,
41711
+ manual_interventions: [],
41067
41712
  environment: {
41068
- foh_cli_version: firstCommand.cli_version || null,
41069
- capture_only: true
41713
+ os: process.platform,
41714
+ node_version: process.version,
41715
+ npm_version: null,
41716
+ foh_cli_version: CLI_VERSION,
41717
+ runner_exit_code: input.exitCode,
41718
+ runner_timed_out: input.timedOut,
41719
+ duration_ms: input.durationMs
41070
41720
  },
41071
41721
  public_entrypoints: [
41072
41722
  "https://frontofhouse.okii.uk",
41073
- "npx --yes @f-o-h/cli@latest"
41074
- ],
41075
- commands_run: commands.map((command) => String(command.command || "")).filter(Boolean),
41076
- docs_pages_used: docs,
41077
- artifacts: {
41078
- command_log: "commands.ndjson",
41079
- agent_metadata: (0, import_fs17.existsSync)((0, import_path16.join)(runDir, "external-agent-metadata.json")) ? "external-agent-metadata.json" : null,
41080
- capture_only: true
41081
- },
41082
- summary: "Synthetic run record created from external-agent capture artifacts because run.json was missing."
41083
- };
41084
- }
41085
- function cohortIdForRunPath(root, runPath) {
41086
- const normalized = (0, import_path16.relative)(root, (0, import_path16.dirname)(runPath)).replaceAll("\\", "/");
41087
- const parts = normalized.split("/").filter(Boolean);
41088
- if (parts.length === 0) return ".";
41089
- if (/^\d{4}-\d{2}-\d{2}$/.test(parts[0]) && parts[1]) return `${parts[0]}/${parts[1]}`;
41090
- return parts[0];
41091
- }
41092
- function readRunRecords(root, cwd) {
41093
- const records = [];
41094
- const invalid_runs = [];
41095
- for (const candidate of findRunCandidates(root)) {
41096
- const file2 = candidate.path;
41097
- try {
41098
- const parsed = candidate.synthetic ? synthesizeRunFromCapture(file2) : readJson(file2);
41099
- const findings = validateExternalAgentRun(parsed);
41100
- if (findings.length > 0) {
41101
- invalid_runs.push({ path: (0, import_path16.relative)(cwd, file2).replaceAll("\\", "/"), findings });
41102
- continue;
41103
- }
41104
- const run = parsed;
41105
- records.push({
41106
- path: file2,
41107
- run,
41108
- cohort_id: cohortIdForRunPath(root, file2),
41109
- sort_time: runSortTime(run)
41110
- });
41111
- } catch (error2) {
41112
- invalid_runs.push({
41113
- path: (0, import_path16.relative)(cwd, file2).replaceAll("\\", "/"),
41114
- findings: [{ id: "json_parse_failed", detail: error2 instanceof Error ? error2.message : String(error2) }]
41115
- });
41116
- }
41117
- }
41118
- return { records, invalid_runs };
41119
- }
41120
- function latestCohortId(records) {
41121
- return records.slice().sort((a, b) => b.sort_time - a.sort_time || b.path.localeCompare(a.path))[0]?.cohort_id ?? null;
41122
- }
41123
- function ownerSubsystemFor(reasonCode) {
41124
- const reason = String(reasonCode || "").toLowerCase();
41125
- if (reason.includes("simulation") || reason.includes("certification") || reason.includes("scenario")) return "dojo_certification";
41126
- if (reason.includes("contact_phone") || reason.includes("voice_contact") || reason.includes("provider_capacity") || reason.includes("byon")) return "voice_contact";
41127
- if (reason.includes("exec_policy") || reason.includes("policy_blocked") || reason.includes("sandbox") || reason.includes("runner") || reason.includes("codex")) return "infra_runner";
41128
- if (reason.includes("api") || reason.includes("http_4") || reason.includes("http_5") || reason.includes("404") || reason.includes("500") || reason.includes("roundtrip")) return "api_contract";
41129
- if (reason.includes("cli") || reason.includes("command") || reason.includes("flag")) return "cli";
41130
- if (reason.includes("docs") || reason.includes("unclear") || reason.includes("not_found")) return "docs";
41131
- if (reason.includes("auth") || reason.includes("org") || reason.includes("config")) return "infra_runner";
41132
- if (reason.includes("runtime") || reason.includes("widget") || reason.includes("proof")) return "runtime";
41133
- return "product_ux";
41134
- }
41135
- function recommendedFixFor(reasonCode) {
41136
- const owner = ownerSubsystemFor(reasonCode);
41137
- if (owner === "api_contract") return "fix_api";
41138
- if (owner === "cli") return "fix_cli";
41139
- if (owner === "docs") return "fix_docs";
41140
- if (owner === "runtime") return "fix_runtime";
41141
- if (owner === "dojo_certification") return "add_test";
41142
- return "fix_config";
41143
- }
41144
- function collapseCommandRecords(records) {
41145
- const order = [];
41146
- const byId = /* @__PURE__ */ new Map();
41147
- for (const record2 of records) {
41148
- const id = String(record2.command_id || `${record2.recorded_at || ""}:${record2.command || ""}`);
41149
- if (!byId.has(id)) order.push(id);
41150
- const previous = byId.get(id);
41151
- byId.set(id, record2.phase === "completed" ? record2 : previous || record2);
41152
- }
41153
- return order.map((id) => byId.get(id)).filter((record2) => Boolean(record2));
41154
- }
41155
- function readCommandOutputJson(runDir, command) {
41156
- const artifact = typeof command.output_artifact === "string" && command.output_artifact.trim() ? command.output_artifact.trim() : "";
41157
- if (!artifact || artifact.includes("..") || artifact.includes("/") || artifact.includes("\\")) return null;
41158
- const artifactPath = (0, import_path16.join)(runDir, artifact);
41159
- if (!(0, import_fs17.existsSync)(artifactPath)) return null;
41160
- try {
41161
- const text = (0, import_fs17.readFileSync)(artifactPath, "utf8");
41162
- const firstObject = text.indexOf("{");
41163
- const lastObject = text.lastIndexOf("}");
41164
- if (firstObject < 0 || lastObject <= firstObject) return null;
41165
- return JSON.parse(text.slice(firstObject, lastObject + 1));
41166
- } catch {
41167
- return null;
41168
- }
41169
- }
41170
- function commandTimingBreakdown(command, output) {
41171
- const schemaVersion = String(output?.schema_version || "");
41172
- if (schemaVersion === "foh_cli_proof_report.v1") {
41173
- const timing = asObject(output?.timing) || {};
41174
- const cacheSources = /* @__PURE__ */ new Map();
41175
- let cacheWaitMs = 0;
41176
- let cacheHitCount = 0;
41177
- let cacheMissCount = 0;
41178
- for (const check2 of toArray2(output?.checks)) {
41179
- const detail = asObject(check2)?.detail;
41180
- const proofCache = asObject(asObject(detail)?.proof_cache);
41181
- if (!proofCache) continue;
41182
- if (proofCache["hit"] === true) cacheHitCount += 1;
41183
- if (proofCache["miss"] === true) cacheMissCount += 1;
41184
- cacheWaitMs += Number(proofCache["waited_ms"] || 0);
41185
- increment(cacheSources, proofCache["source"] || "unknown");
41186
- }
41187
- return {
41188
- kind: "proof",
41189
- command: command.command || "",
41190
- total_ms: Number(timing.total_ms || command.duration_ms || 0),
41191
- total_check_duration_ms: Number(timing.total_check_duration_ms || 0),
41192
- slow_checks: toArray2(timing.slow_checks).slice(0, 5),
41193
- cache_wait_ms: cacheWaitMs,
41194
- cache_hit_count: cacheHitCount,
41195
- cache_miss_count: cacheMissCount,
41196
- cache_sources: ranked(cacheSources)
41197
- };
41198
- }
41199
- if (schemaVersion === "foh_certification_run.v1") {
41200
- const timing = asObject(output?.timing) || {};
41201
- const certificate = asObject(output?.certificate) || {};
41202
- return {
41203
- kind: "certification",
41204
- command: command.command || "",
41205
- total_ms: Number(timing.total_ms || command.duration_ms || 0),
41206
- api_ms: Number(timing.api_ms || 0),
41207
- status: output?.status || null,
41208
- reason_code: output?.reason_code || null,
41209
- scenario_summary: certificate.scenario_summary || null
41210
- };
41211
- }
41212
- return null;
41213
- }
41214
- function analyzeRunArtifacts(runPath, run, cwd) {
41215
- const runDir = (0, import_path16.dirname)(runPath);
41216
- const commands = collapseCommandRecords(readNdjson((0, import_path16.join)(runDir, "commands.ndjson")));
41217
- const reasonCounts = /* @__PURE__ */ new Map();
41218
- const slowSteps = [];
41219
- const timingBreakdowns = [];
41220
- let completed = 0;
41221
- let withDuration = 0;
41222
- let totalDuration = 0;
41223
- for (const command of commands) {
41224
- const output = readCommandOutputJson(runDir, command);
41225
- const breakdown = commandTimingBreakdown(command, output);
41226
- if (breakdown) timingBreakdowns.push({
41227
- run_id: run.run_id,
41228
- run_path: (0, import_path16.relative)(cwd, runPath).replaceAll("\\", "/"),
41229
- ...breakdown
41230
- });
41231
- if (command.phase === "completed" || command.completed_at) completed += 1;
41232
- if (typeof command.duration_ms === "number") {
41233
- withDuration += 1;
41234
- totalDuration += command.duration_ms;
41235
- slowSteps.push({
41236
- run_id: run.run_id,
41237
- run_path: (0, import_path16.relative)(cwd, runPath).replaceAll("\\", "/"),
41238
- command: command.command || "",
41239
- duration_ms: command.duration_ms,
41240
- status: command.status || null,
41241
- reason_code: command.reason_code || null,
41242
- check_reason_codes: Array.isArray(command.check_reason_codes) ? command.check_reason_codes : []
41243
- });
41244
- }
41245
- if (command.reason_code) increment(reasonCounts, command.reason_code);
41246
- for (const reasonCode of toArray2(command.check_reason_codes)) {
41247
- if (reasonCode) increment(reasonCounts, reasonCode);
41248
- }
41249
- }
41250
- const codexEvents = readNdjson((0, import_path16.join)(runDir, "codex-exec.jsonl"));
41251
- const codexDocs = /* @__PURE__ */ new Set();
41252
- let codexCommandExecutions = 0;
41253
- let codexFailedExitCodes = 0;
41254
- for (const event of codexEvents) {
41255
- const item = asObject(event.item) || event;
41256
- if (item.type === "command_execution" && item.status === "completed") {
41257
- codexCommandExecutions += 1;
41258
- if (typeof item.exit_code === "number" && item.exit_code !== 0) codexFailedExitCodes += 1;
41259
- }
41260
- for (const url2 of collectDocUrls(JSON.stringify(event))) codexDocs.add(url2);
41261
- }
41262
- const docs = /* @__PURE__ */ new Set([
41263
- ...toArray2(run.docs_pages_used).map(String),
41264
- ...Array.from(codexDocs)
41265
- ]);
41266
- return {
41267
- command_log_present: (0, import_fs17.existsSync)((0, import_path16.join)(runDir, "commands.ndjson")),
41268
- command_count: commands.length,
41269
- completed_command_count: completed,
41270
- missing_completion_count: Math.max(0, commands.length - completed),
41271
- commands_with_duration_count: withDuration,
41272
- total_command_duration_ms: totalDuration,
41273
- command_reason_codes: ranked(reasonCounts),
41274
- slow_steps: slowSteps.sort((a, b) => Number(b.duration_ms) - Number(a.duration_ms)).slice(0, 10),
41275
- timing_breakdowns: timingBreakdowns,
41276
- docs_pages_observed: Array.from(docs).sort(),
41277
- codex_command_execution_completed_count: codexCommandExecutions,
41278
- codex_failed_exit_code_count: codexFailedExitCodes
41279
- };
41280
- }
41281
- function summarizeExternalAgentRuns(options) {
41282
- const cwd = (0, import_path16.resolve)(options.cwd || process.cwd());
41283
- const root = (0, import_path16.resolve)(cwd, options.root);
41284
- const loaded = readRunRecords(root, cwd);
41285
- const selectedCohortId = options.cohortId || (options.currentBaselineOnly ? latestCohortId(loaded.records) : null);
41286
- const records = selectedCohortId ? loaded.records.filter((record2) => record2.cohort_id === selectedCohortId) : loaded.records;
41287
- const statusCounts = /* @__PURE__ */ new Map();
41288
- const modelCounts = /* @__PURE__ */ new Map();
41289
- const failureCounts = /* @__PURE__ */ new Map();
41290
- const commandReasonCounts = /* @__PURE__ */ new Map();
41291
- const docsCounts = /* @__PURE__ */ new Map();
41292
- const slowSteps = [];
41293
- const timingBreakdowns = [];
41294
- let manualInterventions = 0;
41295
- let commandCount = 0;
41296
- let completedCommandCount = 0;
41297
- let missingCompletionCount = 0;
41298
- let commandsWithDurationCount = 0;
41299
- let totalCommandDurationMs = 0;
41300
- let commandLogRunCount = 0;
41301
- let codexCommandExecutions = 0;
41302
- let codexFailedExitCodes = 0;
41303
- for (const record2 of records) {
41304
- const run = record2.run;
41305
- increment(statusCounts, run.status);
41306
- increment(modelCounts, `${run.model_provider}/${run.model_name}`);
41307
- manualInterventions += Number(run.manual_intervention_count || 0);
41308
- if (run.status !== "pass") increment(failureCounts, run.failure_reason_code || "unknown");
41309
- const artifactSummary = analyzeRunArtifacts(record2.path, run, cwd);
41310
- if (artifactSummary.command_log_present) commandLogRunCount += 1;
41311
- commandCount += Number(artifactSummary.command_count || 0);
41312
- completedCommandCount += Number(artifactSummary.completed_command_count || 0);
41313
- missingCompletionCount += Number(artifactSummary.missing_completion_count || 0);
41314
- commandsWithDurationCount += Number(artifactSummary.commands_with_duration_count || 0);
41315
- totalCommandDurationMs += Number(artifactSummary.total_command_duration_ms || 0);
41316
- codexCommandExecutions += Number(artifactSummary.codex_command_execution_completed_count || 0);
41317
- codexFailedExitCodes += Number(artifactSummary.codex_failed_exit_code_count || 0);
41318
- for (const row of toArray2(artifactSummary.slow_steps)) slowSteps.push(row);
41319
- for (const row of toArray2(artifactSummary.timing_breakdowns)) timingBreakdowns.push(row);
41320
- for (const row of toArray2(artifactSummary.command_reason_codes)) {
41321
- const entry = asObject(row);
41322
- if (entry) increment(commandReasonCounts, entry.key, Number(entry.count || 1));
41323
- }
41324
- for (const page of toArray2(artifactSummary.docs_pages_observed)) increment(docsCounts, page);
41325
- }
41326
- const topFailures = ranked(failureCounts);
41327
- const commandReasonCodes2 = ranked(commandReasonCounts);
41328
- const recommendedFixes = topFailures.map((failure) => ({
41329
- reason_code: failure.key,
41330
- count: failure.count,
41331
- recommended_fix: recommendedFixFor(failure.key),
41332
- owner_subsystem: ownerSubsystemFor(failure.key)
41333
- }));
41334
- const nextRecommendedFix = recommendedFixes[0] || null;
41335
- return {
41336
- schema_version: "external_agent_run_summary.v1",
41337
- generated_at: (/* @__PURE__ */ new Date()).toISOString(),
41338
- root: (0, import_path16.relative)(cwd, root).replaceAll("\\", "/") || ".",
41339
- cohort_id: selectedCohortId,
41340
- current_baseline_only: Boolean(selectedCohortId),
41341
- run_count: records.length,
41342
- invalid_run_count: selectedCohortId ? 0 : loaded.invalid_runs.length,
41343
- status_counts: Object.fromEntries(statusCounts),
41344
- model_counts: ranked(modelCounts),
41345
- manual_intervention_count: manualInterventions,
41346
- top_failure_reason_codes: topFailures,
41347
- docs_pages_observed: ranked(docsCounts),
41348
- command_telemetry: {
41349
- run_count_with_command_log: commandLogRunCount,
41350
- command_count: commandCount,
41351
- completed_command_count: completedCommandCount,
41352
- missing_completion_count: missingCompletionCount,
41353
- commands_with_duration_count: commandsWithDurationCount,
41354
- total_command_duration_ms: totalCommandDurationMs,
41355
- command_reason_codes: commandReasonCodes2,
41356
- slow_steps: slowSteps.sort((a, b) => Number(b.duration_ms || 0) - Number(a.duration_ms || 0) || String(a.command || "").localeCompare(String(b.command || ""))).slice(0, 20),
41357
- timing_breakdowns: timingBreakdowns.sort((a, b) => Number(b.total_ms || 0) - Number(a.total_ms || 0) || String(a.command || "").localeCompare(String(b.command || ""))).slice(0, 20)
41358
- },
41359
- codex_telemetry: {
41360
- command_execution_completed_count: codexCommandExecutions,
41361
- failed_exit_code_count: codexFailedExitCodes
41362
- },
41363
- recommended_fixes: recommendedFixes,
41364
- next_recommended_fix: nextRecommendedFix,
41365
- fix_selection_policy: {
41366
- mode: "coherent_failure_cluster_first",
41367
- rule: "Fix the highest-impact owner subsystem locally with focused proof, then rerun the same prompt once externally.",
41368
- run_failure_weight: 3,
41369
- command_reason_weight: 1
41370
- },
41371
- next_commands: nextRecommendedFix ? [`foh bug improve --from external-agent-run --file <run_dir>/run.json --json`] : [],
41372
- invalid_runs: selectedCohortId ? [] : loaded.invalid_runs,
41373
- run_paths: records.map((record2) => (0, import_path16.relative)(cwd, record2.path).replaceAll("\\", "/")).sort()
41374
- };
41375
- }
41376
- function runExternalAgentRunSummary(options) {
41377
- const summary = summarizeExternalAgentRuns(options);
41378
- const invalidRuns = toArray2(summary.invalid_runs);
41379
- const status = invalidRuns.length > 0 ? "failed" : "passed";
41380
- const report = {
41381
- report_schema_version: "script_report.v1",
41382
- script: "foh eval external-agent summary",
41383
- checked_at: (/* @__PURE__ */ new Date()).toISOString(),
41384
- status,
41385
- errors: invalidRuns.map((entry) => {
41386
- const object3 = asObject(entry);
41387
- return `${object3?.path || "unknown"}: ${JSON.stringify(object3?.findings || [])}`;
41388
- }),
41389
- warnings: Number(summary.run_count || 0) === 0 ? ["no external-agent run artifacts found"] : [],
41390
- report: summary
41391
- };
41392
- if (options.out) {
41393
- (0, import_fs17.mkdirSync)((0, import_path16.dirname)((0, import_path16.resolve)(options.cwd || process.cwd(), options.out)), { recursive: true });
41394
- (0, import_fs17.writeFileSync)((0, import_path16.resolve)(options.cwd || process.cwd(), options.out), `${JSON.stringify(summary, null, 2)}
41395
- `, "utf8");
41396
- }
41397
- if (options.report) {
41398
- (0, import_fs17.mkdirSync)((0, import_path16.dirname)((0, import_path16.resolve)(options.cwd || process.cwd(), options.report)), { recursive: true });
41399
- (0, import_fs17.writeFileSync)((0, import_path16.resolve)(options.cwd || process.cwd(), options.report), `${JSON.stringify(report, null, 2)}
41400
- `, "utf8");
41401
- }
41402
- return { summary, report };
41403
- }
41404
-
41405
- // src/lib/external-agent-executor-classification.ts
41406
- function proofArtifactPasses(runDir) {
41407
- const proofPath = (0, import_path17.join)(runDir, "proof.json");
41408
- if (!(0, import_fs18.existsSync)(proofPath)) return false;
41409
- try {
41410
- const parsed = JSON.parse((0, import_fs18.readFileSync)(proofPath, "utf8"));
41411
- return parsed.ok === true || parsed.status === "pass" || parsed.status === "passed";
41412
- } catch {
41413
- return false;
41414
- }
41415
- }
41416
- function readIfExists(path2) {
41417
- return (0, import_fs18.existsSync)(path2) ? (0, import_fs18.readFileSync)(path2, "utf8") : "";
41418
- }
41419
- function relativeArtifactName(path2) {
41420
- return (0, import_path17.basename)(path2);
41421
- }
41422
- function classifyExternalAgentRun(input) {
41423
- if (input.timedOut) return { status: "hold", reasonCode: `${input.run.command}_runner_timeout` };
41424
- if (!input.artifactSafetyOk) return { status: "fail", reasonCode: "external_agent_artifact_safety_blocked" };
41425
- const completedCommands = readCommandRecords(input.run.run_dir).filter((record2) => record2.phase === "completed");
41426
- const observedVersions = completedCommands.map((record2) => String(record2.cli_version || "").trim()).filter((version2) => /^\d+\.\d+\.\d+$/.test(version2));
41427
- if (observedVersions.some((version2) => version2 !== CLI_VERSION)) {
41428
- return { status: "hold", reasonCode: "external_agent_cli_version_drift" };
41429
- }
41430
- const commandReasonCodes2 = completedCommands.flatMap((record2) => [
41431
- String(record2.reason_code || ""),
41432
- ...Array.isArray(record2.check_reason_codes) ? record2.check_reason_codes.map((code) => String(code || "")) : []
41433
- ]).filter(Boolean);
41434
- const hasCommandReason = (pattern) => commandReasonCodes2.some((reason) => pattern.test(reason));
41435
- if (hasCommandReason(new RegExp(PAID_RESOURCE_BLOCKED_REASON_CODE, "i"))) {
41436
- return { status: "hold", reasonCode: PAID_RESOURCE_BLOCKED_REASON_CODE };
41437
- }
41438
- if (hasCommandReason(/provider_capacity_blocked/i)) {
41439
- return { status: "hold", reasonCode: "provider_capacity_blocked" };
41440
- }
41441
- if (hasCommandReason(/byon_voice_number_not_configured/i)) {
41442
- return { status: "hold", reasonCode: "byon_voice_number_not_configured" };
41443
- }
41444
- if (hasCommandReason(/contact_phone_provisioning_failed/i)) {
41445
- return { status: "hold", reasonCode: "voice_contact_phone_provisioning_failed" };
41446
- }
41447
- if (hasCommandReason(/voice_contact_expected_no_spend_hold/i)) {
41448
- return { status: "hold", reasonCode: "voice_contact_expected_no_spend_hold" };
41449
- }
41450
- if (hasCommandReason(/contact_phone_missing/i)) {
41451
- return { status: "hold", reasonCode: "voice_contact_phone_missing" };
41452
- }
41453
- if (hasCommandReason(/sim(?:ulation)?[_-]?cert(?:ify|ification)?.*failed|simulation_certification_failed/i)) {
41454
- return { status: "hold", reasonCode: "simulation_certification_failed" };
41455
- }
41456
- if (hasCommandReason(/proof_held/i)) {
41457
- return { status: "hold", reasonCode: "external_agent_proof_held" };
41458
- }
41459
- if (hasCommandReason(/agent_limit_reached/i)) {
41460
- return { status: "hold", reasonCode: "eval_org_agent_limit_reached" };
41461
- }
41462
- const agentMetadata = readExternalAgentMetadata(input.run.run_dir);
41463
- const metadataBlockerCodes = agentMetadata.blocker_reason_codes;
41464
- const hasMetadataBlocker = (pattern) => metadataBlockerCodes.some((reason) => pattern.test(reason));
41465
- if (hasMetadataBlocker(/^customer_owned_requirement_unverified:/i)) {
41466
- return { status: "hold", reasonCode: "customer_owned_requirements_unverified_expected_hold" };
41467
- }
41468
- if (hasMetadataBlocker(/^api_health:/i) && metadataBlockerCodes.some((reason) => /^customer_owned_requirement_unverified:/i.test(reason))) {
41469
- return { status: "hold", reasonCode: "customer_owned_requirements_unverified_expected_hold" };
41470
- }
41471
- const firstCommandReasonCode = commandReasonCodes2.find((reason) => reason.trim().length > 0) ?? null;
41472
- if (firstCommandReasonCode) {
41473
- return { status: "hold", reasonCode: firstCommandReasonCode };
41474
- }
41475
- const lastMessage = readIfExists(input.run.outputs.last_message);
41476
- const stderr = readIfExists(input.run.outputs.stderr);
41477
- const combined = `${lastMessage}
41478
- ${stderr}`;
41479
- if (/need[^.\n]*(?:private|source)[^.\n]*repo|cannot[^.\n]*without[^.\n]*(?:private|source)[^.\n]*repo|clone[^.\n]*(?:private|source)[^.\n]*repo/i.test(combined)) {
41480
- return { status: "fail", reasonCode: "private_repo_assumption_detected" };
41481
- }
41482
- if (/(?:blocked|rejected|declined) by policy|EXEC_POLICY_BLOCKED|command execution was rejected|shell commands were rejected/i.test(combined)) {
41483
- return { status: "hold", reasonCode: "codex_exec_policy_blocked" };
41484
- }
41485
- 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)) {
41486
- return { status: "hold", reasonCode: "codex_sandbox_exec_blocked" };
41487
- }
41488
- if (/ENV_NETWORK_DNS_BLOCK|Could not resolve host|npm ping.*timeout|NO_EXECUTABLE_INSTALL/i.test(combined)) {
41489
- return { status: "hold", reasonCode: "codex_network_dns_blocked" };
41490
- }
41491
- if (new RegExp(PAID_RESOURCE_BLOCKED_REASON_CODE, "i").test(combined)) {
41492
- return { status: "hold", reasonCode: PAID_RESOURCE_BLOCKED_REASON_CODE };
41493
- }
41494
- if (/provider_capacity_blocked/i.test(combined)) {
41495
- return { status: "hold", reasonCode: "provider_capacity_blocked" };
41496
- }
41497
- if (/byon_voice_number_not_configured/i.test(combined)) {
41498
- return { status: "hold", reasonCode: "byon_voice_number_not_configured" };
41499
- }
41500
- if (/contact_phone_provisioning_failed/i.test(combined)) {
41501
- return { status: "hold", reasonCode: "voice_contact_phone_provisioning_failed" };
41502
- }
41503
- if (/voice_contact_expected_no_spend_hold/i.test(combined)) {
41504
- return { status: "hold", reasonCode: "voice_contact_expected_no_spend_hold" };
41505
- }
41506
- if (/contact_phone_missing/i.test(combined)) {
41507
- return { status: "hold", reasonCode: "voice_contact_phone_missing" };
41508
- }
41509
- if (/simulation_certification_failed/i.test(combined)) {
41510
- return { status: "hold", reasonCode: "simulation_certification_failed" };
41511
- }
41512
- if (/proof_held/i.test(combined)) {
41513
- return { status: "hold", reasonCode: "external_agent_proof_held" };
41514
- }
41515
- if (/agent_limit_reached/i.test(combined)) {
41516
- return { status: "hold", reasonCode: "eval_org_agent_limit_reached" };
41517
- }
41518
- if (/browser|approve|approval|login|auth|sign in/i.test(combined) && !proofArtifactPasses(input.run.run_dir)) {
41519
- return { status: "hold", reasonCode: "auth_browser_approval_required" };
41520
- }
41521
- if (input.exitCode !== 0) return { status: "hold", reasonCode: `${input.run.command}_runner_nonzero_exit` };
41522
- if (proofArtifactPasses(input.run.run_dir)) return { status: "pass", reasonCode: null };
41523
- return { status: "hold", reasonCode: "external_agent_proof_artifact_missing" };
41524
- }
41525
- function buildExecutedExternalAgentRunArtifact(input) {
41526
- const commands = readCommandRecords(input.run.run_dir);
41527
- const agentMetadata = readExternalAgentMetadata(input.run.run_dir);
41528
- return {
41529
- schema_version: "external_agent_run.v1",
41530
- run_id: input.run.run_id,
41531
- status: input.status,
41532
- failure_reason_code: input.reasonCode,
41533
- model_provider: input.run.model_provider,
41534
- model_name: input.run.model_name,
41535
- runner_model: input.run.runner_model,
41536
- agent_shell: `${input.run.command}-exec`,
41537
- workspace_type: "clean-no-repo-programmatic",
41538
- prompt_version: input.run.prompt_version,
41539
- prompt_path: "prompt.txt",
41540
- started_at: input.startedAt,
41541
- ended_at: input.endedAt,
41542
- manual_intervention_count: 0,
41543
- manual_interventions: [],
41544
- environment: {
41545
- os: process.platform,
41546
- node_version: process.version,
41547
- npm_version: null,
41548
- foh_cli_version: CLI_VERSION,
41549
- runner_exit_code: input.exitCode,
41550
- runner_timed_out: input.timedOut,
41551
- duration_ms: input.durationMs
41552
- },
41553
- public_entrypoints: [
41554
- "https://frontofhouse.okii.uk",
41555
- "https://frontofhouse.okii.uk/llms.txt",
41556
- "https://frontofhouse.okii.uk/openapi.yaml",
41723
+ "https://frontofhouse.okii.uk/llms.txt",
41724
+ "https://frontofhouse.okii.uk/openapi.yaml",
41557
41725
  "npx --yes @f-o-h/cli@latest"
41558
41726
  ],
41559
41727
  commands_run: commands.map((command) => command.command),
@@ -41576,13 +41744,13 @@ function buildExecutedExternalAgentRunArtifact(input) {
41576
41744
  },
41577
41745
  artifacts: {
41578
41746
  terminal_transcript: relativeArtifactName(input.run.outputs.jsonl),
41579
- command_log: (0, import_fs18.existsSync)((0, import_path17.join)(input.run.run_dir, "commands.ndjson")) ? "commands.ndjson" : null,
41580
- proof_bundle: (0, import_fs18.existsSync)((0, import_path17.join)(input.run.run_dir, "proof.json")) ? "proof.json" : null,
41581
- replay_packet: (0, import_fs18.existsSync)((0, import_path17.join)(input.run.run_dir, "replay.json")) ? "replay.json" : null,
41582
- knowledge_packet: (0, import_fs18.existsSync)((0, import_path17.join)(input.run.run_dir, "knowledge.json")) ? "knowledge.json" : null,
41747
+ command_log: (0, import_fs17.existsSync)((0, import_path16.join)(input.run.run_dir, "commands.ndjson")) ? "commands.ndjson" : null,
41748
+ proof_bundle: (0, import_fs17.existsSync)((0, import_path16.join)(input.run.run_dir, "proof.json")) ? "proof.json" : null,
41749
+ replay_packet: (0, import_fs17.existsSync)((0, import_path16.join)(input.run.run_dir, "replay.json")) ? "replay.json" : null,
41750
+ knowledge_packet: (0, import_fs17.existsSync)((0, import_path16.join)(input.run.run_dir, "knowledge.json")) ? "knowledge.json" : null,
41583
41751
  improvement_packet: input.status === "pass" ? null : "improvement-packet.json",
41584
41752
  agent_metadata: agentMetadata.path,
41585
- notes: (0, import_fs18.existsSync)((0, import_path17.join)(input.run.run_dir, "notes.md")) ? "notes.md" : null,
41753
+ notes: (0, import_fs17.existsSync)((0, import_path16.join)(input.run.run_dir, "notes.md")) ? "notes.md" : null,
41586
41754
  runner_last_message: relativeArtifactName(input.run.outputs.last_message),
41587
41755
  runner_stderr: relativeArtifactName(input.run.outputs.stderr),
41588
41756
  codex_last_message: input.run.command === "codex" ? relativeArtifactName(input.run.outputs.last_message) : null,
@@ -41590,25 +41758,25 @@ function buildExecutedExternalAgentRunArtifact(input) {
41590
41758
  artifact_safety: relativeArtifactName(input.run.outputs.artifact_safety)
41591
41759
  },
41592
41760
  summary: input.status === "pass" ? `Controlled ${input.run.command} external-agent run produced passing proof evidence.` : `Controlled ${input.run.command} external-agent run ended as ${input.status} with reason ${input.reasonCode}.`,
41593
- next_commands: input.status === "pass" ? [externalAgentSummaryCommand((0, import_path17.dirname)(input.run.run_dir))] : [
41761
+ next_commands: input.status === "pass" ? [externalAgentSummaryTemplateCommand()] : [
41594
41762
  "foh eval external-agent scan-artifacts --run-dir <run_dir> --private-repo-root <private_repo_root> --write-redacted --json",
41595
41763
  "foh bug improve --from external-agent-run --file <run_dir>/run.json --out <run_dir>/improvement-packet.json --json",
41596
- externalAgentSummaryCommand((0, import_path17.dirname)(input.run.run_dir))
41764
+ externalAgentSummaryTemplateCommand()
41597
41765
  ]
41598
41766
  };
41599
41767
  }
41600
41768
 
41601
41769
  // src/lib/external-agent-runner-execution.ts
41602
41770
  var import_child_process5 = require("child_process");
41603
- var import_fs19 = require("fs");
41604
- var import_path18 = require("path");
41771
+ var import_fs18 = require("fs");
41772
+ var import_path17 = require("path");
41605
41773
  function buildCommandInvocation(command, args) {
41606
- if (process.platform === "win32" && command.toLowerCase().endsWith(".cmd")) {
41607
- const binDir = (0, import_path18.dirname)(command);
41608
- const codexEntrypoint = (0, import_path18.join)(binDir, "node_modules", "@openai", "codex", "bin", "codex.js");
41609
- if ((0, import_fs19.existsSync)(codexEntrypoint)) return { command: process.execPath, args: [codexEntrypoint, ...args] };
41610
- const geminiEntrypoint = (0, import_path18.join)(binDir, "node_modules", "@google", "gemini-cli", "bundle", "gemini.js");
41611
- if ((0, import_fs19.existsSync)(geminiEntrypoint)) return { command: process.execPath, args: ["--no-warnings=DEP0040", geminiEntrypoint, ...args] };
41774
+ if (command.toLowerCase().endsWith(".cmd")) {
41775
+ const binDir = (0, import_path17.dirname)(command);
41776
+ const codexEntrypoint = (0, import_path17.join)(binDir, "node_modules", "@openai", "codex", "bin", "codex.js");
41777
+ if ((0, import_fs18.existsSync)(codexEntrypoint)) return { command: process.execPath, args: [codexEntrypoint, ...args] };
41778
+ const geminiEntrypoint = (0, import_path17.join)(binDir, "node_modules", "@google", "gemini-cli", "bundle", "gemini.js");
41779
+ if ((0, import_fs18.existsSync)(geminiEntrypoint)) return { command: process.execPath, args: ["--no-warnings=DEP0040", geminiEntrypoint, ...args] };
41612
41780
  }
41613
41781
  return { command, args };
41614
41782
  }
@@ -41623,8 +41791,8 @@ function spawnExternalAgentRunner(input) {
41623
41791
  stdio: ["pipe", "pipe", "pipe"],
41624
41792
  windowsHide: true
41625
41793
  });
41626
- const stdout = (0, import_fs19.createWriteStream)(input.stdoutPath, { flags: "w" });
41627
- const stderr = (0, import_fs19.createWriteStream)(input.stderrPath, { flags: "w" });
41794
+ const stdout = (0, import_fs18.createWriteStream)(input.stdoutPath, { flags: "w" });
41795
+ const stderr = (0, import_fs18.createWriteStream)(input.stderrPath, { flags: "w" });
41628
41796
  child.stdout.pipe(stdout);
41629
41797
  child.stderr.pipe(stderr);
41630
41798
  child.stdin.end(input.prompt);
@@ -41736,14 +41904,14 @@ async function runExternalAgentEvalAuthPreflight(env = process.env, options = {}
41736
41904
  };
41737
41905
  }
41738
41906
  function normalizeForCompare(path2) {
41739
- const resolved = (0, import_path19.resolve)(path2);
41907
+ const resolved = (0, import_path18.resolve)(path2);
41740
41908
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
41741
41909
  }
41742
41910
  function isPathInside(childPath, parentPath) {
41743
41911
  const child = normalizeForCompare(childPath);
41744
41912
  const parent = normalizeForCompare(parentPath);
41745
- const rel = (0, import_path19.relative)(parent, child);
41746
- return rel === "" || !!rel && !rel.startsWith("..") && !(0, import_path19.isAbsolute)(rel);
41913
+ const rel = (0, import_path18.relative)(parent, child);
41914
+ return rel === "" || !!rel && !rel.startsWith("..") && !(0, import_path18.isAbsolute)(rel);
41747
41915
  }
41748
41916
  function requireString(value, field) {
41749
41917
  if (typeof value !== "string" || value.trim() === "") {
@@ -41752,10 +41920,10 @@ function requireString(value, field) {
41752
41920
  return value;
41753
41921
  }
41754
41922
  function readBatch(batchPath) {
41755
- if (!(0, import_fs20.existsSync)(batchPath)) {
41923
+ if (!(0, import_fs19.existsSync)(batchPath)) {
41756
41924
  throw new ExternalAgentExecutorError("external_agent_batch_not_found", `Batch file not found: ${batchPath}`);
41757
41925
  }
41758
- const parsed = JSON.parse((0, import_fs20.readFileSync)(batchPath, "utf8"));
41926
+ const parsed = JSON.parse((0, import_fs19.readFileSync)(batchPath, "utf8"));
41759
41927
  if (parsed.schema_version !== "external_agent_batch_plan.v1") {
41760
41928
  throw new ExternalAgentExecutorError("invalid_external_agent_batch", "Batch schema_version must be external_agent_batch_plan.v1.");
41761
41929
  }
@@ -41792,8 +41960,8 @@ function resolveCodexProbeCommand() {
41792
41960
  if (process.platform !== "win32") return "codex";
41793
41961
  const appData = process.env.APPDATA;
41794
41962
  if (appData) {
41795
- const appDataShim = (0, import_path19.join)(appData, "npm", "codex.cmd");
41796
- if ((0, import_fs20.existsSync)(appDataShim)) return appDataShim;
41963
+ const appDataShim = (0, import_path18.join)(appData, "npm", "codex.cmd");
41964
+ if ((0, import_fs19.existsSync)(appDataShim)) return appDataShim;
41797
41965
  }
41798
41966
  return "codex.cmd";
41799
41967
  }
@@ -41804,8 +41972,8 @@ function resolveGeminiProbeCommand() {
41804
41972
  if (process.platform !== "win32") return "gemini";
41805
41973
  const appData = process.env.APPDATA;
41806
41974
  if (appData) {
41807
- const appDataShim = (0, import_path19.join)(appData, "npm", "gemini.cmd");
41808
- if ((0, import_fs20.existsSync)(appDataShim)) return appDataShim;
41975
+ const appDataShim = (0, import_path18.join)(appData, "npm", "gemini.cmd");
41976
+ if ((0, import_fs19.existsSync)(appDataShim)) return appDataShim;
41809
41977
  }
41810
41978
  return "gemini.cmd";
41811
41979
  }
@@ -42076,34 +42244,34 @@ function safeRunId(value) {
42076
42244
  return value.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "") || "run";
42077
42245
  }
42078
42246
  function resolveWorkspaceRoot(input) {
42079
- if (input.workspaceRoot) return (0, import_path19.resolve)(input.workspaceRoot);
42080
- const batchStem = (0, import_path19.basename)((0, import_path19.resolve)(input.batchPath)).replace(/[^a-zA-Z0-9_.-]+/g, "-");
42081
- const repoStem = (0, import_path19.basename)((0, import_path19.resolve)(input.privateRepoRoot)).replace(/[^a-zA-Z0-9_.-]+/g, "-");
42082
- return (0, import_path19.resolve)((0, import_os3.tmpdir)(), "foh-external-agent-workspaces", repoStem, batchStem);
42247
+ if (input.workspaceRoot) return (0, import_path18.resolve)(input.workspaceRoot);
42248
+ const batchStem = (0, import_path18.basename)((0, import_path18.resolve)(input.batchPath)).replace(/[^a-zA-Z0-9_.-]+/g, "-");
42249
+ const repoStem = (0, import_path18.basename)((0, import_path18.resolve)(input.privateRepoRoot)).replace(/[^a-zA-Z0-9_.-]+/g, "-");
42250
+ return (0, import_path18.resolve)((0, import_os3.tmpdir)(), "foh-external-agent-workspaces", repoStem, batchStem);
42083
42251
  }
42084
42252
  function findNearestGitRoot(startPath) {
42085
- let current = (0, import_path19.resolve)(startPath);
42253
+ let current = (0, import_path18.resolve)(startPath);
42086
42254
  while (true) {
42087
- if ((0, import_fs20.existsSync)((0, import_path19.join)(current, ".git"))) return current;
42088
- const parent = (0, import_path19.dirname)(current);
42255
+ if ((0, import_fs19.existsSync)((0, import_path18.join)(current, ".git"))) return current;
42256
+ const parent = (0, import_path18.dirname)(current);
42089
42257
  if (parent === current) return null;
42090
42258
  current = parent;
42091
42259
  }
42092
42260
  }
42093
42261
  function resolvePrivateRepoRoot(input) {
42094
42262
  if (input.explicitPrivateRepoRoot) {
42095
- return { root: (0, import_path19.resolve)(input.explicitPrivateRepoRoot), explicit: true };
42263
+ return { root: (0, import_path18.resolve)(input.explicitPrivateRepoRoot), explicit: true };
42096
42264
  }
42097
- const cwd = (0, import_path19.resolve)(input.cwd || process.cwd());
42265
+ const cwd = (0, import_path18.resolve)(input.cwd || process.cwd());
42098
42266
  const gitRoot = findNearestGitRoot(cwd);
42099
42267
  if (gitRoot) return { root: gitRoot, explicit: false };
42100
42268
  return {
42101
- root: (0, import_path19.join)(cwd, ".foh-no-private-repo-root-sentinel"),
42269
+ root: (0, import_path18.join)(cwd, ".foh-no-private-repo-root-sentinel"),
42102
42270
  explicit: false
42103
42271
  };
42104
42272
  }
42105
42273
  function promptVersionFromPath(promptPath) {
42106
- const raw = (0, import_fs20.readFileSync)(promptPath, "utf8");
42274
+ const raw = (0, import_fs19.readFileSync)(promptPath, "utf8");
42107
42275
  if (raw.includes("arbitrary-agency setup context") || raw.includes("Mission: prove whether an arbitrary estate agency") || raw.includes("ops reporting agency-setup-preview") || raw.includes("ops reporting agency-setup-workflow")) return "arbitrary-agency-setup-release.v1";
42108
42276
  if (raw.includes("Do not assume access to the private source repository")) return "blank-setup.v1";
42109
42277
  return "unknown";
@@ -42113,7 +42281,7 @@ function createExternalAgentExecutorPlan(options) {
42113
42281
  if (runner !== "codex" && runner !== "gemini") {
42114
42282
  throw new ExternalAgentExecutorError("unsupported_external_agent_runner", `Unsupported runner: ${runner}`);
42115
42283
  }
42116
- const batchPath = (0, import_path19.resolve)(options.batchPath);
42284
+ const batchPath = (0, import_path18.resolve)(options.batchPath);
42117
42285
  const batch = readBatch(batchPath);
42118
42286
  const runnerProbe = validateRunner(options, runner);
42119
42287
  const codexSandboxBackend = normalizeCodexSandboxBackend(options.codexSandboxBackend);
@@ -42132,17 +42300,17 @@ function createExternalAgentExecutorPlan(options) {
42132
42300
  `Workspace root must be outside the private repository. workspace=${workspaceRoot} repo=${privateRepoRoot}`
42133
42301
  );
42134
42302
  }
42135
- (0, import_fs20.mkdirSync)(workspaceRoot, { recursive: true });
42136
- const batchDir = (0, import_path19.resolve)(String(batch.batch_dir || (0, import_path19.resolve)(batchPath, "..")));
42303
+ (0, import_fs19.mkdirSync)(workspaceRoot, { recursive: true });
42304
+ const batchDir = (0, import_path18.resolve)(String(batch.batch_dir || (0, import_path18.resolve)(batchPath, "..")));
42137
42305
  const timeoutMinutes = Number.isFinite(options.timeoutMinutes) && Number(options.timeoutMinutes) > 0 ? Number(options.timeoutMinutes) : 30;
42138
42306
  const runs = batch.runs.map((run) => {
42139
42307
  const runId = safeRunId(requireString(run.run_id, "runs[].run_id"));
42140
- const runDir = (0, import_path19.resolve)(requireString(run.run_dir, `runs[${runId}].run_dir`));
42141
- const promptPath = (0, import_path19.resolve)(requireString(run.prompt_path, `runs[${runId}].prompt_path`));
42142
- const workspaceDir = (0, import_path19.join)(workspaceRoot, runId);
42143
- (0, import_fs20.mkdirSync)(workspaceDir, { recursive: true });
42144
- (0, import_fs20.writeFileSync)(
42145
- (0, import_path19.join)(workspaceDir, "README.md"),
42308
+ const runDir = (0, import_path18.resolve)(requireString(run.run_dir, `runs[${runId}].run_dir`));
42309
+ const promptPath = (0, import_path18.resolve)(requireString(run.prompt_path, `runs[${runId}].prompt_path`));
42310
+ const workspaceDir = (0, import_path18.join)(workspaceRoot, runId);
42311
+ (0, import_fs19.mkdirSync)(workspaceDir, { recursive: true });
42312
+ (0, import_fs19.writeFileSync)(
42313
+ (0, import_path18.join)(workspaceDir, "README.md"),
42146
42314
  [
42147
42315
  "# FOH External-Agent Workspace",
42148
42316
  "",
@@ -42160,11 +42328,11 @@ function createExternalAgentExecutorPlan(options) {
42160
42328
  });
42161
42329
  const promptVersion = String(env[EXTERNAL_AGENT_PROMPT_VERSION_ENV] || "unknown");
42162
42330
  const outputStem = runner === "gemini" ? "gemini" : "codex";
42163
- const jsonlPath = (0, import_path19.join)(runDir, `${outputStem}-exec.jsonl`);
42164
- const lastMessagePath = (0, import_path19.join)(runDir, `${outputStem}-last-message.md`);
42165
- const stderrPath = (0, import_path19.join)(runDir, `${outputStem}-stderr.txt`);
42166
- const runPath = (0, import_path19.join)(runDir, "run.json");
42167
- const artifactSafetyPath = (0, import_path19.join)(runDir, "artifact-safety.json");
42331
+ const jsonlPath = (0, import_path18.join)(runDir, `${outputStem}-exec.jsonl`);
42332
+ const lastMessagePath = (0, import_path18.join)(runDir, `${outputStem}-last-message.md`);
42333
+ const stderrPath = (0, import_path18.join)(runDir, `${outputStem}-stderr.txt`);
42334
+ const runPath = (0, import_path18.join)(runDir, "run.json");
42335
+ const artifactSafetyPath = (0, import_path18.join)(runDir, "artifact-safety.json");
42168
42336
  const args = runner === "gemini" ? [
42169
42337
  ...runnerProbe.globalArgs,
42170
42338
  ...runnerProbe.execArgs
@@ -42255,9 +42423,9 @@ function createExternalAgentExecutorPlan(options) {
42255
42423
  };
42256
42424
  }
42257
42425
  function writeExternalAgentExecutorPlan(plan) {
42258
- const path2 = (0, import_path19.join)(plan.batch_dir, "executor-plan.json");
42259
- (0, import_fs20.mkdirSync)(plan.batch_dir, { recursive: true });
42260
- (0, import_fs20.writeFileSync)(path2, `${JSON.stringify(plan, null, 2)}
42426
+ const path2 = (0, import_path18.join)(plan.batch_dir, "executor-plan.json");
42427
+ (0, import_fs19.mkdirSync)(plan.batch_dir, { recursive: true });
42428
+ (0, import_fs19.writeFileSync)(path2, `${JSON.stringify(plan, null, 2)}
42261
42429
  `, "utf8");
42262
42430
  return path2;
42263
42431
  }
@@ -42272,7 +42440,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
42272
42440
  if (authPreflight && !authPreflight.ok) {
42273
42441
  const endedAt2 = (/* @__PURE__ */ new Date()).toISOString();
42274
42442
  const blockedResults = plan.runs.map((run) => {
42275
- (0, import_fs20.mkdirSync)(run.run_dir, { recursive: true });
42443
+ (0, import_fs19.mkdirSync)(run.run_dir, { recursive: true });
42276
42444
  const runArtifact = buildExecutedExternalAgentRunArtifact({
42277
42445
  run,
42278
42446
  startedAt,
@@ -42283,7 +42451,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
42283
42451
  timedOut: false,
42284
42452
  durationMs: 0
42285
42453
  });
42286
- (0, import_fs20.writeFileSync)(run.outputs.run, `${JSON.stringify(runArtifact, null, 2)}
42454
+ (0, import_fs19.writeFileSync)(run.outputs.run, `${JSON.stringify(runArtifact, null, 2)}
42287
42455
  `, "utf8");
42288
42456
  return {
42289
42457
  run_id: run.run_id,
@@ -42310,8 +42478,8 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
42310
42478
  }
42311
42479
  for (const run of plan.runs) {
42312
42480
  const runStartedAt = (/* @__PURE__ */ new Date()).toISOString();
42313
- const commandCaptureDir = (0, import_path19.join)(run.workspace_dir, ".foh-capture");
42314
- (0, import_fs20.mkdirSync)(commandCaptureDir, { recursive: true });
42481
+ const commandCaptureDir = (0, import_path18.join)(run.workspace_dir, ".foh-capture");
42482
+ (0, import_fs19.mkdirSync)(commandCaptureDir, { recursive: true });
42315
42483
  const env = buildCodexExecutorEnv({
42316
42484
  sourceEnv: options.env,
42317
42485
  runDir: commandCaptureDir,
@@ -42322,7 +42490,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
42322
42490
  args: run.args,
42323
42491
  cwd: run.workspace_dir,
42324
42492
  env,
42325
- prompt: (0, import_fs20.readFileSync)(run.prompt_path, "utf8"),
42493
+ prompt: (0, import_fs19.readFileSync)(run.prompt_path, "utf8"),
42326
42494
  stdoutPath: run.outputs.jsonl,
42327
42495
  stderrPath: run.outputs.stderr,
42328
42496
  timeoutMs: plan.timeout_minutes * 60 * 1e3
@@ -42335,7 +42503,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
42335
42503
  privateRepoRoot,
42336
42504
  writeRedacted: true
42337
42505
  });
42338
- (0, import_fs20.writeFileSync)(run.outputs.artifact_safety, `${JSON.stringify(artifactSafety, null, 2)}
42506
+ (0, import_fs19.writeFileSync)(run.outputs.artifact_safety, `${JSON.stringify(artifactSafety, null, 2)}
42339
42507
  `, "utf8");
42340
42508
  const runEndedAt = (/* @__PURE__ */ new Date()).toISOString();
42341
42509
  const classification = classifyExternalAgentRun({
@@ -42354,7 +42522,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
42354
42522
  timedOut: spawned.timedOut,
42355
42523
  durationMs: spawned.durationMs
42356
42524
  });
42357
- (0, import_fs20.writeFileSync)(run.outputs.run, `${JSON.stringify(runArtifact, null, 2)}
42525
+ (0, import_fs19.writeFileSync)(run.outputs.run, `${JSON.stringify(runArtifact, null, 2)}
42358
42526
  `, "utf8");
42359
42527
  results.push({
42360
42528
  run_id: run.run_id,
@@ -42384,6 +42552,540 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
42384
42552
  };
42385
42553
  }
42386
42554
 
42555
+ // src/lib/external-agent-run-summary.ts
42556
+ var import_fs20 = require("fs");
42557
+ var import_path19 = require("path");
42558
+ var REQUIRED_RUN_FIELDS = [
42559
+ "schema_version",
42560
+ "run_id",
42561
+ "status",
42562
+ "model_provider",
42563
+ "model_name",
42564
+ "prompt_version",
42565
+ "started_at",
42566
+ "manual_intervention_count",
42567
+ "environment",
42568
+ "public_entrypoints",
42569
+ "commands_run",
42570
+ "docs_pages_used",
42571
+ "artifacts"
42572
+ ];
42573
+ var VALID_STATUSES = /* @__PURE__ */ new Set(["pass", "hold", "fail"]);
42574
+ var DOC_URL_RE = /https:\/\/frontofhouse\.okii\.uk\/[^\s"'`)<>,;\\\]}]*/g;
42575
+ function quoteShellArg(value) {
42576
+ const text = String(value);
42577
+ if (/^[A-Za-z0-9_./:=@-]+$/.test(text)) return text;
42578
+ return `"${text.replace(/(["$`])/g, "\\$1")}"`;
42579
+ }
42580
+ function externalAgentSummaryCommand(root) {
42581
+ const summaryPath = (0, import_path19.join)(root, "latest-summary.json");
42582
+ const reportPath = (0, import_path19.join)(root, "summary.report.json");
42583
+ return [
42584
+ "foh",
42585
+ "eval",
42586
+ "external-agent",
42587
+ "summary",
42588
+ "--root",
42589
+ quoteShellArg(root),
42590
+ "--out",
42591
+ quoteShellArg(summaryPath),
42592
+ "--report",
42593
+ quoteShellArg(reportPath),
42594
+ "--json"
42595
+ ].join(" ");
42596
+ }
42597
+ function readJson(filePath) {
42598
+ return JSON.parse((0, import_fs20.readFileSync)(filePath, "utf8").replace(/^\uFEFF/, ""));
42599
+ }
42600
+ function readNdjson(filePath) {
42601
+ if (!(0, import_fs20.existsSync)(filePath)) return [];
42602
+ return (0, import_fs20.readFileSync)(filePath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => {
42603
+ try {
42604
+ const parsed = JSON.parse(line);
42605
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
42606
+ } catch {
42607
+ return null;
42608
+ }
42609
+ }).filter((record2) => Boolean(record2));
42610
+ }
42611
+ function asObject(value) {
42612
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
42613
+ }
42614
+ function toArray2(value) {
42615
+ return Array.isArray(value) ? value : [];
42616
+ }
42617
+ function increment(map3, key, amount = 1) {
42618
+ const normalized = String(key || "unknown");
42619
+ map3.set(normalized, (map3.get(normalized) || 0) + amount);
42620
+ }
42621
+ function ranked(map3) {
42622
+ return Array.from(map3.entries()).map(([key, count]) => ({ key, count })).sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
42623
+ }
42624
+ function collectDocUrls(text) {
42625
+ return Array.from(new Set((String(text || "").match(DOC_URL_RE) || []).map((url2) => url2.replace(/[.?!:]+$/g, "")).filter((url2) => url2.startsWith("https://frontofhouse.okii.uk/")))).sort();
42626
+ }
42627
+ function findRunCandidates(root) {
42628
+ if (!(0, import_fs20.existsSync)(root)) return [];
42629
+ const candidates = [];
42630
+ const seenRunDirs = /* @__PURE__ */ new Set();
42631
+ const captureDirs = [];
42632
+ const stack = [root];
42633
+ while (stack.length > 0) {
42634
+ const current = stack.pop();
42635
+ if (!current) continue;
42636
+ for (const entry of (0, import_fs20.readdirSync)(current, { withFileTypes: true })) {
42637
+ const absolute = (0, import_path19.join)(current, entry.name);
42638
+ if (entry.isDirectory()) {
42639
+ stack.push(absolute);
42640
+ } else if (entry.isFile() && entry.name === "run.json") {
42641
+ candidates.push({ path: absolute, synthetic: false });
42642
+ seenRunDirs.add((0, import_path19.dirname)(absolute));
42643
+ } else if (entry.isFile() && entry.name === "commands.ndjson") {
42644
+ captureDirs.push(current);
42645
+ }
42646
+ }
42647
+ }
42648
+ for (const captureDir of captureDirs) {
42649
+ if (seenRunDirs.has(captureDir)) continue;
42650
+ candidates.push({ path: (0, import_path19.join)(captureDir, "run.json"), synthetic: true });
42651
+ }
42652
+ return candidates.sort((a, b) => a.path.localeCompare(b.path));
42653
+ }
42654
+ function validateExternalAgentRun(value) {
42655
+ const findings = [];
42656
+ const run = asObject(value);
42657
+ if (!run) return [{ id: "run_not_object", detail: "run artifact must be an object" }];
42658
+ for (const field of REQUIRED_RUN_FIELDS) {
42659
+ if (!(field in run)) findings.push({ id: "required_field_missing", field });
42660
+ }
42661
+ if (run.schema_version !== "external_agent_run.v1") {
42662
+ findings.push({ id: "schema_version_invalid", expected: "external_agent_run.v1", actual: run.schema_version ?? null });
42663
+ }
42664
+ if (!VALID_STATUSES.has(String(run.status || ""))) {
42665
+ findings.push({ id: "status_invalid", expected: Array.from(VALID_STATUSES), actual: run.status ?? null });
42666
+ }
42667
+ if ((run.status === "hold" || run.status === "fail") && !String(run.failure_reason_code || "").trim()) {
42668
+ findings.push({ id: "failure_reason_code_missing" });
42669
+ }
42670
+ if (!Number.isInteger(run.manual_intervention_count) || Number(run.manual_intervention_count) < 0) {
42671
+ findings.push({ id: "manual_intervention_count_invalid" });
42672
+ }
42673
+ if (!Array.isArray(run.commands_run)) findings.push({ id: "commands_run_invalid" });
42674
+ if (!Array.isArray(run.docs_pages_used)) findings.push({ id: "docs_pages_used_invalid" });
42675
+ if (!asObject(run.environment)) findings.push({ id: "environment_invalid" });
42676
+ if (!asObject(run.artifacts)) findings.push({ id: "artifacts_invalid" });
42677
+ if (toArray2(run.public_entrypoints).length === 0) findings.push({ id: "public_entrypoints_missing" });
42678
+ return findings;
42679
+ }
42680
+ function runSortTime(run) {
42681
+ const raw = String(run.ended_at || run.started_at || "");
42682
+ const time3 = Date.parse(raw);
42683
+ return Number.isFinite(time3) ? time3 : 0;
42684
+ }
42685
+ function latestCommandTime(commands) {
42686
+ const times = commands.map((command) => String(command.completed_at || command.started_at || command.recorded_at || "")).map((raw) => ({ raw, time: Date.parse(raw) })).filter((entry) => Number.isFinite(entry.time)).sort((a, b) => b.time - a.time);
42687
+ return times[0]?.raw ?? null;
42688
+ }
42689
+ function firstCommandTime(commands) {
42690
+ const times = commands.map((command) => String(command.started_at || command.recorded_at || command.completed_at || "")).map((raw) => ({ raw, time: Date.parse(raw) })).filter((entry) => Number.isFinite(entry.time)).sort((a, b) => a.time - b.time);
42691
+ return times[0]?.raw ?? null;
42692
+ }
42693
+ function commandReasonCodes(commands) {
42694
+ const codes = /* @__PURE__ */ new Set();
42695
+ for (const command of commands) {
42696
+ if (command.reason_code) codes.add(String(command.reason_code));
42697
+ for (const reasonCode of toArray2(command.check_reason_codes)) {
42698
+ if (reasonCode) codes.add(String(reasonCode));
42699
+ }
42700
+ }
42701
+ return Array.from(codes);
42702
+ }
42703
+ function syntheticStatusFromCommands(commands) {
42704
+ const commandReasons = commandReasonCodes(commands);
42705
+ const failed = commands.find((command) => {
42706
+ const status = String(command.status || "").toLowerCase();
42707
+ return status === "fail" || typeof command.exit_code === "number" && command.exit_code !== 0 && status !== "hold";
42708
+ });
42709
+ if (failed) {
42710
+ return {
42711
+ status: "fail",
42712
+ reasonCode: String(failed.reason_code || commandReasons[0] || "external_agent_command_failed")
42713
+ };
42714
+ }
42715
+ const held = commands.find((command) => String(command.status || "").toLowerCase() === "hold");
42716
+ if (held) {
42717
+ return {
42718
+ status: "hold",
42719
+ reasonCode: String(held.reason_code || commandReasons[0] || "external_agent_command_held")
42720
+ };
42721
+ }
42722
+ if (commands.length === 0) {
42723
+ return { status: "hold", reasonCode: "external_agent_capture_empty" };
42724
+ }
42725
+ return { status: "pass", reasonCode: null };
42726
+ }
42727
+ function synthesizeRunFromCapture(runPath) {
42728
+ const runDir = (0, import_path19.dirname)(runPath);
42729
+ const commands = collapseCommandRecords(readNdjson((0, import_path19.join)(runDir, "commands.ndjson")));
42730
+ const metadata = asObject((0, import_fs20.existsSync)((0, import_path19.join)(runDir, "external-agent-metadata.json")) ? readJson((0, import_path19.join)(runDir, "external-agent-metadata.json")) : {});
42731
+ const blockerCodes = toArray2(metadata?.blocker_reason_codes).map(String).filter(Boolean);
42732
+ const commandClassification = syntheticStatusFromCommands(commands);
42733
+ const status = commandClassification.status === "fail" ? "fail" : blockerCodes.length > 0 ? "hold" : commandClassification.status;
42734
+ const reasonCode = commandClassification.status === "fail" ? commandClassification.reasonCode : blockerCodes[0] || commandClassification.reasonCode;
42735
+ const firstCommand = commands[0] || {};
42736
+ const startedAt = firstCommandTime(commands) || (/* @__PURE__ */ new Date(0)).toISOString();
42737
+ const endedAt = latestCommandTime(commands) || startedAt;
42738
+ const docs = toArray2(metadata?.docs_pages_used).map(String).filter(Boolean);
42739
+ const runId = (0, import_path19.dirname)(runPath).split(/[\\/]/).filter(Boolean).slice(-3).join("-") || "capture-only-run";
42740
+ return {
42741
+ schema_version: "external_agent_run.v1",
42742
+ run_id: runId,
42743
+ status,
42744
+ failure_reason_code: status === "pass" ? null : reasonCode || "external_agent_capture_unfinalized",
42745
+ model_provider: "unknown",
42746
+ model_name: "unknown",
42747
+ prompt_version: String(firstCommand.prompt_version || "unknown"),
42748
+ started_at: startedAt,
42749
+ ended_at: endedAt,
42750
+ manual_intervention_count: 0,
42751
+ environment: {
42752
+ foh_cli_version: firstCommand.cli_version || null,
42753
+ capture_only: true
42754
+ },
42755
+ public_entrypoints: [
42756
+ "https://frontofhouse.okii.uk",
42757
+ "npx --yes @f-o-h/cli@latest"
42758
+ ],
42759
+ commands_run: commands.map((command) => String(command.command || "")).filter(Boolean),
42760
+ docs_pages_used: docs,
42761
+ artifacts: {
42762
+ command_log: "commands.ndjson",
42763
+ agent_metadata: (0, import_fs20.existsSync)((0, import_path19.join)(runDir, "external-agent-metadata.json")) ? "external-agent-metadata.json" : null,
42764
+ capture_only: true
42765
+ },
42766
+ summary: "Synthetic run record created from external-agent capture artifacts because run.json was missing."
42767
+ };
42768
+ }
42769
+ function cohortIdForRunPath(root, runPath) {
42770
+ const normalized = (0, import_path19.relative)(root, (0, import_path19.dirname)(runPath)).replaceAll("\\", "/");
42771
+ const parts = normalized.split("/").filter(Boolean);
42772
+ if (parts.length === 0) return ".";
42773
+ if (/^\d{4}-\d{2}-\d{2}$/.test(parts[0]) && parts[1]) return `${parts[0]}/${parts[1]}`;
42774
+ return parts[0];
42775
+ }
42776
+ function readRunRecords(root, cwd) {
42777
+ const records = [];
42778
+ const invalid_runs = [];
42779
+ for (const candidate of findRunCandidates(root)) {
42780
+ const file2 = candidate.path;
42781
+ try {
42782
+ const parsed = candidate.synthetic ? synthesizeRunFromCapture(file2) : readJson(file2);
42783
+ const findings = validateExternalAgentRun(parsed);
42784
+ if (findings.length > 0) {
42785
+ invalid_runs.push({ path: (0, import_path19.relative)(cwd, file2).replaceAll("\\", "/"), findings });
42786
+ continue;
42787
+ }
42788
+ const run = parsed;
42789
+ records.push({
42790
+ path: file2,
42791
+ run,
42792
+ cohort_id: cohortIdForRunPath(root, file2),
42793
+ sort_time: runSortTime(run)
42794
+ });
42795
+ } catch (error2) {
42796
+ invalid_runs.push({
42797
+ path: (0, import_path19.relative)(cwd, file2).replaceAll("\\", "/"),
42798
+ findings: [{ id: "json_parse_failed", detail: error2 instanceof Error ? error2.message : String(error2) }]
42799
+ });
42800
+ }
42801
+ }
42802
+ return { records, invalid_runs };
42803
+ }
42804
+ function latestCohortId(records) {
42805
+ return records.slice().sort((a, b) => b.sort_time - a.sort_time || b.path.localeCompare(a.path))[0]?.cohort_id ?? null;
42806
+ }
42807
+ function ownerSubsystemFor(reasonCode) {
42808
+ const reason = String(reasonCode || "").toLowerCase();
42809
+ if (reason.includes("simulation") || reason.includes("certification") || reason.includes("scenario")) return "dojo_certification";
42810
+ if (reason.includes("contact_phone") || reason.includes("voice_contact") || reason.includes("provider_capacity") || reason.includes("byon")) return "voice_contact";
42811
+ if (reason.includes("exec_policy") || reason.includes("policy_blocked") || reason.includes("sandbox") || reason.includes("runner") || reason.includes("codex")) return "infra_runner";
42812
+ if (reason.includes("api") || reason.includes("http_4") || reason.includes("http_5") || reason.includes("404") || reason.includes("500") || reason.includes("roundtrip")) return "api_contract";
42813
+ if (reason.includes("cli") || reason.includes("command") || reason.includes("flag")) return "cli";
42814
+ if (reason.includes("docs") || reason.includes("unclear") || reason.includes("not_found")) return "docs";
42815
+ if (reason.includes("auth") || reason.includes("org") || reason.includes("config")) return "infra_runner";
42816
+ if (reason.includes("runtime") || reason.includes("widget") || reason.includes("proof")) return "runtime";
42817
+ return "product_ux";
42818
+ }
42819
+ function recommendedFixFor(reasonCode) {
42820
+ const owner = ownerSubsystemFor(reasonCode);
42821
+ if (owner === "api_contract") return "fix_api";
42822
+ if (owner === "cli") return "fix_cli";
42823
+ if (owner === "docs") return "fix_docs";
42824
+ if (owner === "runtime") return "fix_runtime";
42825
+ if (owner === "dojo_certification") return "add_test";
42826
+ return "fix_config";
42827
+ }
42828
+ function collapseCommandRecords(records) {
42829
+ const order = [];
42830
+ const byId = /* @__PURE__ */ new Map();
42831
+ for (const record2 of records) {
42832
+ const id = String(record2.command_id || `${record2.recorded_at || ""}:${record2.command || ""}`);
42833
+ if (!byId.has(id)) order.push(id);
42834
+ const previous = byId.get(id);
42835
+ byId.set(id, record2.phase === "completed" ? record2 : previous || record2);
42836
+ }
42837
+ return order.map((id) => byId.get(id)).filter((record2) => Boolean(record2));
42838
+ }
42839
+ function readCommandOutputJson(runDir, command) {
42840
+ const artifact = typeof command.output_artifact === "string" && command.output_artifact.trim() ? command.output_artifact.trim() : "";
42841
+ if (!artifact || artifact.includes("..") || artifact.includes("/") || artifact.includes("\\")) return null;
42842
+ const artifactPath = (0, import_path19.join)(runDir, artifact);
42843
+ if (!(0, import_fs20.existsSync)(artifactPath)) return null;
42844
+ try {
42845
+ const text = (0, import_fs20.readFileSync)(artifactPath, "utf8");
42846
+ const firstObject = text.indexOf("{");
42847
+ const lastObject = text.lastIndexOf("}");
42848
+ if (firstObject < 0 || lastObject <= firstObject) return null;
42849
+ return JSON.parse(text.slice(firstObject, lastObject + 1));
42850
+ } catch {
42851
+ return null;
42852
+ }
42853
+ }
42854
+ function commandTimingBreakdown(command, output) {
42855
+ const schemaVersion = String(output?.schema_version || "");
42856
+ if (schemaVersion === "foh_cli_proof_report.v1") {
42857
+ const timing = asObject(output?.timing) || {};
42858
+ const cacheSources = /* @__PURE__ */ new Map();
42859
+ let cacheWaitMs = 0;
42860
+ let cacheHitCount = 0;
42861
+ let cacheMissCount = 0;
42862
+ for (const check2 of toArray2(output?.checks)) {
42863
+ const detail = asObject(check2)?.detail;
42864
+ const proofCache = asObject(asObject(detail)?.proof_cache);
42865
+ if (!proofCache) continue;
42866
+ if (proofCache["hit"] === true) cacheHitCount += 1;
42867
+ if (proofCache["miss"] === true) cacheMissCount += 1;
42868
+ cacheWaitMs += Number(proofCache["waited_ms"] || 0);
42869
+ increment(cacheSources, proofCache["source"] || "unknown");
42870
+ }
42871
+ return {
42872
+ kind: "proof",
42873
+ command: command.command || "",
42874
+ total_ms: Number(timing.total_ms || command.duration_ms || 0),
42875
+ total_check_duration_ms: Number(timing.total_check_duration_ms || 0),
42876
+ slow_checks: toArray2(timing.slow_checks).slice(0, 5),
42877
+ cache_wait_ms: cacheWaitMs,
42878
+ cache_hit_count: cacheHitCount,
42879
+ cache_miss_count: cacheMissCount,
42880
+ cache_sources: ranked(cacheSources)
42881
+ };
42882
+ }
42883
+ if (schemaVersion === "foh_certification_run.v1") {
42884
+ const timing = asObject(output?.timing) || {};
42885
+ const certificate = asObject(output?.certificate) || {};
42886
+ return {
42887
+ kind: "certification",
42888
+ command: command.command || "",
42889
+ total_ms: Number(timing.total_ms || command.duration_ms || 0),
42890
+ api_ms: Number(timing.api_ms || 0),
42891
+ status: output?.status || null,
42892
+ reason_code: output?.reason_code || null,
42893
+ scenario_summary: certificate.scenario_summary || null
42894
+ };
42895
+ }
42896
+ return null;
42897
+ }
42898
+ function analyzeRunArtifacts(runPath, run, cwd) {
42899
+ const runDir = (0, import_path19.dirname)(runPath);
42900
+ const commands = collapseCommandRecords(readNdjson((0, import_path19.join)(runDir, "commands.ndjson")));
42901
+ const reasonCounts = /* @__PURE__ */ new Map();
42902
+ const slowSteps = [];
42903
+ const timingBreakdowns = [];
42904
+ let completed = 0;
42905
+ let withDuration = 0;
42906
+ let totalDuration = 0;
42907
+ for (const command of commands) {
42908
+ const output = readCommandOutputJson(runDir, command);
42909
+ const breakdown = commandTimingBreakdown(command, output);
42910
+ if (breakdown) timingBreakdowns.push({
42911
+ run_id: run.run_id,
42912
+ run_path: (0, import_path19.relative)(cwd, runPath).replaceAll("\\", "/"),
42913
+ ...breakdown
42914
+ });
42915
+ if (command.phase === "completed" || command.completed_at) completed += 1;
42916
+ if (typeof command.duration_ms === "number") {
42917
+ withDuration += 1;
42918
+ totalDuration += command.duration_ms;
42919
+ slowSteps.push({
42920
+ run_id: run.run_id,
42921
+ run_path: (0, import_path19.relative)(cwd, runPath).replaceAll("\\", "/"),
42922
+ command: command.command || "",
42923
+ duration_ms: command.duration_ms,
42924
+ status: command.status || null,
42925
+ reason_code: command.reason_code || null,
42926
+ check_reason_codes: Array.isArray(command.check_reason_codes) ? command.check_reason_codes : []
42927
+ });
42928
+ }
42929
+ if (command.reason_code) increment(reasonCounts, command.reason_code);
42930
+ for (const reasonCode of toArray2(command.check_reason_codes)) {
42931
+ if (reasonCode) increment(reasonCounts, reasonCode);
42932
+ }
42933
+ }
42934
+ const codexEvents = readNdjson((0, import_path19.join)(runDir, "codex-exec.jsonl"));
42935
+ const codexDocs = /* @__PURE__ */ new Set();
42936
+ let codexCommandExecutions = 0;
42937
+ let codexFailedExitCodes = 0;
42938
+ for (const event of codexEvents) {
42939
+ const item = asObject(event.item) || event;
42940
+ if (item.type === "command_execution" && item.status === "completed") {
42941
+ codexCommandExecutions += 1;
42942
+ if (typeof item.exit_code === "number" && item.exit_code !== 0) codexFailedExitCodes += 1;
42943
+ }
42944
+ for (const url2 of collectDocUrls(JSON.stringify(event))) codexDocs.add(url2);
42945
+ }
42946
+ const docs = /* @__PURE__ */ new Set([
42947
+ ...toArray2(run.docs_pages_used).map(String),
42948
+ ...Array.from(codexDocs)
42949
+ ]);
42950
+ return {
42951
+ command_log_present: (0, import_fs20.existsSync)((0, import_path19.join)(runDir, "commands.ndjson")),
42952
+ command_count: commands.length,
42953
+ completed_command_count: completed,
42954
+ missing_completion_count: Math.max(0, commands.length - completed),
42955
+ commands_with_duration_count: withDuration,
42956
+ total_command_duration_ms: totalDuration,
42957
+ command_reason_codes: ranked(reasonCounts),
42958
+ slow_steps: slowSteps.sort((a, b) => Number(b.duration_ms) - Number(a.duration_ms)).slice(0, 10),
42959
+ timing_breakdowns: timingBreakdowns,
42960
+ docs_pages_observed: Array.from(docs).sort(),
42961
+ codex_command_execution_completed_count: codexCommandExecutions,
42962
+ codex_failed_exit_code_count: codexFailedExitCodes
42963
+ };
42964
+ }
42965
+ function summarizeExternalAgentRuns(options) {
42966
+ const cwd = (0, import_path19.resolve)(options.cwd || process.cwd());
42967
+ const root = (0, import_path19.resolve)(cwd, options.root);
42968
+ const loaded = readRunRecords(root, cwd);
42969
+ const selectedCohortId = options.cohortId || (options.currentBaselineOnly ? latestCohortId(loaded.records) : null);
42970
+ const records = selectedCohortId ? loaded.records.filter((record2) => record2.cohort_id === selectedCohortId) : loaded.records;
42971
+ const statusCounts = /* @__PURE__ */ new Map();
42972
+ const modelCounts = /* @__PURE__ */ new Map();
42973
+ const failureCounts = /* @__PURE__ */ new Map();
42974
+ const commandReasonCounts = /* @__PURE__ */ new Map();
42975
+ const docsCounts = /* @__PURE__ */ new Map();
42976
+ const slowSteps = [];
42977
+ const timingBreakdowns = [];
42978
+ let manualInterventions = 0;
42979
+ let commandCount = 0;
42980
+ let completedCommandCount = 0;
42981
+ let missingCompletionCount = 0;
42982
+ let commandsWithDurationCount = 0;
42983
+ let totalCommandDurationMs = 0;
42984
+ let commandLogRunCount = 0;
42985
+ let codexCommandExecutions = 0;
42986
+ let codexFailedExitCodes = 0;
42987
+ for (const record2 of records) {
42988
+ const run = record2.run;
42989
+ increment(statusCounts, run.status);
42990
+ increment(modelCounts, `${run.model_provider}/${run.model_name}`);
42991
+ manualInterventions += Number(run.manual_intervention_count || 0);
42992
+ if (run.status !== "pass") increment(failureCounts, run.failure_reason_code || "unknown");
42993
+ const artifactSummary = analyzeRunArtifacts(record2.path, run, cwd);
42994
+ if (artifactSummary.command_log_present) commandLogRunCount += 1;
42995
+ commandCount += Number(artifactSummary.command_count || 0);
42996
+ completedCommandCount += Number(artifactSummary.completed_command_count || 0);
42997
+ missingCompletionCount += Number(artifactSummary.missing_completion_count || 0);
42998
+ commandsWithDurationCount += Number(artifactSummary.commands_with_duration_count || 0);
42999
+ totalCommandDurationMs += Number(artifactSummary.total_command_duration_ms || 0);
43000
+ codexCommandExecutions += Number(artifactSummary.codex_command_execution_completed_count || 0);
43001
+ codexFailedExitCodes += Number(artifactSummary.codex_failed_exit_code_count || 0);
43002
+ for (const row of toArray2(artifactSummary.slow_steps)) slowSteps.push(row);
43003
+ for (const row of toArray2(artifactSummary.timing_breakdowns)) timingBreakdowns.push(row);
43004
+ for (const row of toArray2(artifactSummary.command_reason_codes)) {
43005
+ const entry = asObject(row);
43006
+ if (entry) increment(commandReasonCounts, entry.key, Number(entry.count || 1));
43007
+ }
43008
+ for (const page of toArray2(artifactSummary.docs_pages_observed)) increment(docsCounts, page);
43009
+ }
43010
+ const topFailures = ranked(failureCounts);
43011
+ const commandReasonCodes2 = ranked(commandReasonCounts);
43012
+ const recommendedFixes = topFailures.map((failure) => ({
43013
+ reason_code: failure.key,
43014
+ count: failure.count,
43015
+ recommended_fix: recommendedFixFor(failure.key),
43016
+ owner_subsystem: ownerSubsystemFor(failure.key)
43017
+ }));
43018
+ const nextRecommendedFix = recommendedFixes[0] || null;
43019
+ return {
43020
+ schema_version: "external_agent_run_summary.v1",
43021
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
43022
+ root: (0, import_path19.relative)(cwd, root).replaceAll("\\", "/") || ".",
43023
+ cohort_id: selectedCohortId,
43024
+ current_baseline_only: Boolean(selectedCohortId),
43025
+ run_count: records.length,
43026
+ invalid_run_count: selectedCohortId ? 0 : loaded.invalid_runs.length,
43027
+ status_counts: Object.fromEntries(statusCounts),
43028
+ model_counts: ranked(modelCounts),
43029
+ manual_intervention_count: manualInterventions,
43030
+ top_failure_reason_codes: topFailures,
43031
+ docs_pages_observed: ranked(docsCounts),
43032
+ command_telemetry: {
43033
+ run_count_with_command_log: commandLogRunCount,
43034
+ command_count: commandCount,
43035
+ completed_command_count: completedCommandCount,
43036
+ missing_completion_count: missingCompletionCount,
43037
+ commands_with_duration_count: commandsWithDurationCount,
43038
+ total_command_duration_ms: totalCommandDurationMs,
43039
+ command_reason_codes: commandReasonCodes2,
43040
+ slow_steps: slowSteps.sort((a, b) => Number(b.duration_ms || 0) - Number(a.duration_ms || 0) || String(a.command || "").localeCompare(String(b.command || ""))).slice(0, 20),
43041
+ timing_breakdowns: timingBreakdowns.sort((a, b) => Number(b.total_ms || 0) - Number(a.total_ms || 0) || String(a.command || "").localeCompare(String(b.command || ""))).slice(0, 20)
43042
+ },
43043
+ codex_telemetry: {
43044
+ command_execution_completed_count: codexCommandExecutions,
43045
+ failed_exit_code_count: codexFailedExitCodes
43046
+ },
43047
+ recommended_fixes: recommendedFixes,
43048
+ next_recommended_fix: nextRecommendedFix,
43049
+ fix_selection_policy: {
43050
+ mode: "coherent_failure_cluster_first",
43051
+ rule: "Fix the highest-impact owner subsystem locally with focused proof, then rerun the same prompt once externally.",
43052
+ run_failure_weight: 3,
43053
+ command_reason_weight: 1
43054
+ },
43055
+ next_commands: nextRecommendedFix ? [`foh bug improve --from external-agent-run --file <run_dir>/run.json --json`] : [],
43056
+ invalid_runs: selectedCohortId ? [] : loaded.invalid_runs,
43057
+ run_paths: records.map((record2) => (0, import_path19.relative)(cwd, record2.path).replaceAll("\\", "/")).sort()
43058
+ };
43059
+ }
43060
+ function runExternalAgentRunSummary(options) {
43061
+ const summary = summarizeExternalAgentRuns(options);
43062
+ const invalidRuns = toArray2(summary.invalid_runs);
43063
+ const status = invalidRuns.length > 0 ? "failed" : "passed";
43064
+ const report = {
43065
+ report_schema_version: "script_report.v1",
43066
+ script: "foh eval external-agent summary",
43067
+ checked_at: (/* @__PURE__ */ new Date()).toISOString(),
43068
+ status,
43069
+ errors: invalidRuns.map((entry) => {
43070
+ const object3 = asObject(entry);
43071
+ return `${object3?.path || "unknown"}: ${JSON.stringify(object3?.findings || [])}`;
43072
+ }),
43073
+ warnings: Number(summary.run_count || 0) === 0 ? ["no external-agent run artifacts found"] : [],
43074
+ report: summary
43075
+ };
43076
+ if (options.out) {
43077
+ (0, import_fs20.mkdirSync)((0, import_path19.dirname)((0, import_path19.resolve)(options.cwd || process.cwd(), options.out)), { recursive: true });
43078
+ (0, import_fs20.writeFileSync)((0, import_path19.resolve)(options.cwd || process.cwd(), options.out), `${JSON.stringify(summary, null, 2)}
43079
+ `, "utf8");
43080
+ }
43081
+ if (options.report) {
43082
+ (0, import_fs20.mkdirSync)((0, import_path19.dirname)((0, import_path19.resolve)(options.cwd || process.cwd(), options.report)), { recursive: true });
43083
+ (0, import_fs20.writeFileSync)((0, import_path19.resolve)(options.cwd || process.cwd(), options.report), `${JSON.stringify(report, null, 2)}
43084
+ `, "utf8");
43085
+ }
43086
+ return { summary, report };
43087
+ }
43088
+
42387
43089
  // src/commands/eval.ts
42388
43090
  var DEFAULT_PROMPT_VERSION = "blank-setup.v1";
42389
43091
  var DEFAULT_BATCH_MODELS = "openai/codex,anthropic/claude,cursor/agent";
@@ -43066,6 +43768,10 @@ function installSoftExitTrap() {
43066
43768
  // src/lib/mission-help.ts
43067
43769
  var CLI_MISSION_EXAMPLES = [
43068
43770
  { mission: "Start", command: "foh start", description: "guided setup and next action selector" },
43771
+ { mission: "Objective Plan", command: "foh objective plan --business-name <name> --source-url <url> --out test-results/objective-plan.latest.json --json", description: "generate setup and onboarding context" },
43772
+ { mission: "Objective Apply", command: "foh objective apply --evidence <json|@file> --out test-results/objective-apply.latest.json --json", description: "submit verified evidence into customer-live gating path" },
43773
+ { mission: "Objective Prove", command: "foh objective prove --business-name <name> --source-url <url> --out test-results/objective-live.latest.json --json", description: "run customer-live proof check directly" },
43774
+ { mission: "Objective Status", command: "foh objective status --business-name <name> --source-url <url> --out test-results/objective-status.latest.json --json", description: "compose setup and live status into one report envelope" },
43069
43775
  { mission: "Setup", command: "foh setup --phone-mode observe --json", description: "create or update agent, widget, voice config, and proof scaffold" },
43070
43776
  { mission: "Prove", command: "foh prove --agent <agent_id> --mission widget --json", description: "produce a machine-readable proof report" },
43071
43777
  { mission: "Publish", command: "foh publish --agent <agent_id> --json", description: "publish when proof and release evidence pass" },
@@ -43170,6 +43876,7 @@ registerCertify(program2);
43170
43876
  registerDiag(program2);
43171
43877
  registerBug(program2);
43172
43878
  registerProve(program2);
43879
+ registerObjective(program2);
43173
43880
  registerInteractive(program2);
43174
43881
  registerAgentPublishCommand(program2, { publicAlias: true });
43175
43882
  registerEval(program2);