@f-o-h/cli 0.1.54 → 0.1.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/foh.js +198 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,10 +101,10 @@ scaffold lane:
|
|
|
101
101
|
FOH_CLI_SPEND_POLICY=no_spend foh setup --org <org-id> --agent-template <template-id> --agent-name "Demo Agent" --phone-mode observe --json
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
`--phone-mode observe` checks whether a contact phone already exists without
|
|
105
|
-
buying one. `--phone-mode skip` bypasses the phone step. `--phone-mode purchase`
|
|
106
|
-
is the explicit paid contact path and is fail-closed when
|
|
107
|
-
`FOH_CLI_SPEND_POLICY=no_spend` is set.
|
|
104
|
+
`--phone-mode observe` checks whether a contact phone already exists without
|
|
105
|
+
buying one. `--phone-mode skip` bypasses the phone step. `--phone-mode purchase`
|
|
106
|
+
is the explicit paid contact path and is fail-closed when
|
|
107
|
+
`FOH_CLI_SPEND_POLICY=no_spend` is set.
|
|
108
108
|
|
|
109
109
|
If managed-number provisioning is blocked by account/provider capacity or empty
|
|
110
110
|
reserve inventory, proof reports `provider_capacity_blocked`; fix capacity or
|
package/dist/foh.js
CHANGED
|
@@ -16021,6 +16021,19 @@ function getTtsProviders(catalog) {
|
|
|
16021
16021
|
const fromProviders = Array.isArray(catalog.providers?.tts) ? catalog.providers?.tts : [];
|
|
16022
16022
|
return fromProviders.map((provider) => String(provider || "").trim().toLowerCase()).filter((provider) => provider.length > 0).sort();
|
|
16023
16023
|
}
|
|
16024
|
+
function getSttProviders(catalog) {
|
|
16025
|
+
const providerRows = Array.isArray(catalog.voice_catalog?.providers) ? catalog.voice_catalog?.providers : [];
|
|
16026
|
+
const fromRows = providerRows.filter((provider) => provider?.kind === "stt").map((provider) => String(provider.id || "").trim().toLowerCase()).filter((provider) => provider.length > 0);
|
|
16027
|
+
if (fromRows.length > 0) return [...new Set(fromRows)].sort();
|
|
16028
|
+
const fromProviders = Array.isArray(catalog.providers?.stt) ? catalog.providers?.stt : [];
|
|
16029
|
+
return fromProviders.map((provider) => String(provider || "").trim().toLowerCase()).filter((provider) => provider.length > 0).sort();
|
|
16030
|
+
}
|
|
16031
|
+
function resolveDefaultSttProvider(providers) {
|
|
16032
|
+
for (const preferred of ["deepgram", "azure", "twilio", "none"]) {
|
|
16033
|
+
if (providers.includes(preferred)) return preferred;
|
|
16034
|
+
}
|
|
16035
|
+
return providers[0] || "deepgram";
|
|
16036
|
+
}
|
|
16024
16037
|
function registerVoice(program3) {
|
|
16025
16038
|
const voice = program3.command("voice").description("Manage voice provider configuration");
|
|
16026
16039
|
voice.command("verify").description("Run voice verification lanes (quick/full/release) through authenticated API orchestration").option("--mode <m>", "Verification mode: quick, full, release", "release").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
@@ -16248,11 +16261,12 @@ function registerVoice(program3) {
|
|
|
16248
16261
|
const allReady = providers.length > 0 && providers.every((provider) => provider?.ready === true);
|
|
16249
16262
|
if (!allReady) markCommandFailed(1);
|
|
16250
16263
|
}));
|
|
16251
|
-
voice.command("configure").description("Configure voice settings for an agent (does not publish unless --publish)").requiredOption("--agent <id>", "Agent ID").requiredOption("--provider <p>", "TTS provider: openai, azure, twilio").requiredOption("--voice <id>", "Voice ID").option("--stt-provider <p>", "STT provider (default:
|
|
16264
|
+
voice.command("configure").description("Configure voice settings for an agent (does not publish unless --publish)").requiredOption("--agent <id>", "Agent ID").requiredOption("--provider <p>", "TTS provider: openai, azure, twilio").requiredOption("--voice <id>", "Voice ID").option("--stt-provider <p>", "STT provider (default: best available, preferring deepgram)").option("--publish", "Validate, simulation-certify, then publish after configuring").option("--cert-mode <m>", "Simulation cert mode before publish: quick, full, stress", "full").option("--cert-adaptive-runs <n>", "Adaptive runs for full/stress certification (default: 30)", "30").option("--cert-max-improvement-rounds <n>", "Max prompt improvement rounds before publish (0-5)", "1").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
16252
16265
|
const provider = String(opts.provider || "").trim().toLowerCase();
|
|
16253
16266
|
const voiceId = String(opts.voice || "").trim();
|
|
16254
16267
|
const catalog = await getSpeechCatalog(opts.apiUrl);
|
|
16255
16268
|
const supportedProviders = getTtsProviders(catalog);
|
|
16269
|
+
const supportedSttProviders = getSttProviders(catalog);
|
|
16256
16270
|
if (!supportedProviders.includes(provider)) {
|
|
16257
16271
|
throw new FohError({
|
|
16258
16272
|
step: "voice.configure",
|
|
@@ -16271,10 +16285,19 @@ function registerVoice(program3) {
|
|
|
16271
16285
|
statusCode: 400
|
|
16272
16286
|
});
|
|
16273
16287
|
}
|
|
16288
|
+
const sttProvider = String(opts.sttProvider || resolveDefaultSttProvider(supportedSttProviders)).trim().toLowerCase();
|
|
16289
|
+
if (supportedSttProviders.length > 0 && !supportedSttProviders.includes(sttProvider)) {
|
|
16290
|
+
throw new FohError({
|
|
16291
|
+
step: "voice.configure",
|
|
16292
|
+
error: `STT provider "${sttProvider}" is not available in speech catalog`,
|
|
16293
|
+
remediation: `Use --stt-provider ${resolveDefaultSttProvider(supportedSttProviders)} or run: foh voice list-providers${opts.apiUrl ? ` --api-url ${opts.apiUrl}` : ""}`,
|
|
16294
|
+
statusCode: 400
|
|
16295
|
+
});
|
|
16296
|
+
}
|
|
16274
16297
|
const voiceConfig = {
|
|
16275
16298
|
tts_provider: provider,
|
|
16276
16299
|
tts_voice_id: voiceId,
|
|
16277
|
-
stt_provider:
|
|
16300
|
+
stt_provider: sttProvider
|
|
16278
16301
|
};
|
|
16279
16302
|
const draft = await apiFetch(`/v1/console/agents/${opts.agent}/draft`, {
|
|
16280
16303
|
method: "PATCH",
|
|
@@ -32830,7 +32853,7 @@ var StdioServerTransport = class {
|
|
|
32830
32853
|
};
|
|
32831
32854
|
|
|
32832
32855
|
// src/lib/cli-version.ts
|
|
32833
|
-
var CLI_VERSION = "0.1.
|
|
32856
|
+
var CLI_VERSION = "0.1.56";
|
|
32834
32857
|
|
|
32835
32858
|
// src/commands/mcp-serve.ts
|
|
32836
32859
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -34479,6 +34502,28 @@ function normalizeSetupPhoneMode(raw) {
|
|
|
34479
34502
|
reasonCode: "setup_invalid_phone_mode"
|
|
34480
34503
|
});
|
|
34481
34504
|
}
|
|
34505
|
+
function complianceSkipDetail(phoneMode) {
|
|
34506
|
+
return {
|
|
34507
|
+
reason_code: `compliance_skipped_phone_mode_${phoneMode}`,
|
|
34508
|
+
phone_mode: phoneMode,
|
|
34509
|
+
spend_policy: resolveCliSpendPolicy(),
|
|
34510
|
+
spend_class: "free",
|
|
34511
|
+
safe_to_retry: true,
|
|
34512
|
+
operator_note: "Compliance is only required before paid FOH-owned phone purchase."
|
|
34513
|
+
};
|
|
34514
|
+
}
|
|
34515
|
+
function isMissingAgentTestsError(error2) {
|
|
34516
|
+
if (!(error2 instanceof FohError)) return false;
|
|
34517
|
+
if (error2.statusCode !== 404) return false;
|
|
34518
|
+
const text = [
|
|
34519
|
+
error2.error,
|
|
34520
|
+
error2.reasonCode,
|
|
34521
|
+
error2.detail?.error,
|
|
34522
|
+
error2.detail?.message,
|
|
34523
|
+
error2.detail?.reason_code
|
|
34524
|
+
].filter((value) => value !== void 0 && value !== null).join(" ").toLowerCase();
|
|
34525
|
+
return text.includes("no tests found") || text.includes("tests_not_found") || text.includes("agent_tests_not_configured");
|
|
34526
|
+
}
|
|
34482
34527
|
function buildMissingOptionsPlan(missing, opts) {
|
|
34483
34528
|
const missingFlags = missing.map(optionNameToFlag);
|
|
34484
34529
|
const signInUrl = buildConsoleSignInUrl(resolveConsoleBaseUrl(opts.consoleUrl));
|
|
@@ -34722,6 +34767,9 @@ function registerSetup(program3) {
|
|
|
34722
34767
|
});
|
|
34723
34768
|
await step("submit_compliance", "Submit standard compliance", async () => {
|
|
34724
34769
|
if (opts.skipCompliance) return { step: "submit_compliance", status: "skipped", detail: "--skip-compliance" };
|
|
34770
|
+
if (phoneMode !== "purchase") {
|
|
34771
|
+
return { step: "submit_compliance", status: "skipped", detail: complianceSkipDetail(phoneMode) };
|
|
34772
|
+
}
|
|
34725
34773
|
const status = await apiFetch("/v1/console/org/compliance/status", {
|
|
34726
34774
|
orgId: opts.org,
|
|
34727
34775
|
apiUrlOverride: opts.apiUrl
|
|
@@ -34742,6 +34790,9 @@ function registerSetup(program3) {
|
|
|
34742
34790
|
});
|
|
34743
34791
|
await step("wait_compliance", "Poll until compliance approved", async () => {
|
|
34744
34792
|
if (opts.skipCompliance) return { step: "wait_compliance", status: "skipped", detail: "--skip-compliance" };
|
|
34793
|
+
if (phoneMode !== "purchase") {
|
|
34794
|
+
return { step: "wait_compliance", status: "skipped", detail: complianceSkipDetail(phoneMode) };
|
|
34795
|
+
}
|
|
34745
34796
|
await pollUntil(
|
|
34746
34797
|
async () => {
|
|
34747
34798
|
const status = await apiFetch("/v1/console/org/compliance/status", {
|
|
@@ -34930,7 +34981,7 @@ function registerSetup(program3) {
|
|
|
34930
34981
|
voice: {
|
|
34931
34982
|
tts_provider: opts.voiceProvider,
|
|
34932
34983
|
tts_voice_id: opts.voiceId,
|
|
34933
|
-
stt_provider: "
|
|
34984
|
+
stt_provider: "deepgram"
|
|
34934
34985
|
}
|
|
34935
34986
|
}),
|
|
34936
34987
|
apiUrlOverride: opts.apiUrl
|
|
@@ -34940,10 +34991,30 @@ function registerSetup(program3) {
|
|
|
34940
34991
|
await step("run_smoke_test", "Run all agent tests", async () => {
|
|
34941
34992
|
if (opts.skipTests) return { step: "run_smoke_test", status: "skipped", detail: "--skip-tests" };
|
|
34942
34993
|
const resolvedAgentId = requireAgentId("run_smoke_test");
|
|
34943
|
-
|
|
34944
|
-
|
|
34945
|
-
|
|
34946
|
-
|
|
34994
|
+
let batch;
|
|
34995
|
+
try {
|
|
34996
|
+
batch = await apiFetch(`/v1/console/agents/${resolvedAgentId}/tests/run-all`, {
|
|
34997
|
+
method: "POST",
|
|
34998
|
+
apiUrlOverride: opts.apiUrl
|
|
34999
|
+
});
|
|
35000
|
+
} catch (error2) {
|
|
35001
|
+
if (isMissingAgentTestsError(error2)) {
|
|
35002
|
+
return {
|
|
35003
|
+
step: "run_smoke_test",
|
|
35004
|
+
status: "skipped",
|
|
35005
|
+
detail: {
|
|
35006
|
+
reason_code: "agent_tests_not_configured",
|
|
35007
|
+
safe_to_retry: true,
|
|
35008
|
+
next_commands: [
|
|
35009
|
+
`foh test run --agent ${resolvedAgentId} --json`,
|
|
35010
|
+
`foh prove --agent ${resolvedAgentId} --mission widget --json`
|
|
35011
|
+
],
|
|
35012
|
+
operator_note: "No saved agent tests exist yet; setup continues to simulation certification and widget proof."
|
|
35013
|
+
}
|
|
35014
|
+
};
|
|
35015
|
+
}
|
|
35016
|
+
throw error2;
|
|
35017
|
+
}
|
|
34947
35018
|
if (!batch.batch_id) return { step: "run_smoke_test", status: "done" };
|
|
34948
35019
|
const result = await pollUntil(
|
|
34949
35020
|
async () => {
|
|
@@ -39179,6 +39250,8 @@ var CODEX_EXECUTOR_DENIED_ENV_PREFIXES = [
|
|
|
39179
39250
|
"OPENAI_",
|
|
39180
39251
|
"XAI_",
|
|
39181
39252
|
"ANTHROPIC_",
|
|
39253
|
+
"GOOGLE_",
|
|
39254
|
+
"GEMINI_",
|
|
39182
39255
|
"WHATSAPP_",
|
|
39183
39256
|
"TWILIO_",
|
|
39184
39257
|
"STRIPE_"
|
|
@@ -39382,9 +39455,22 @@ function resolveCodexProbeCommand() {
|
|
|
39382
39455
|
function resolveCodexExecutionCommand() {
|
|
39383
39456
|
return resolveCodexProbeCommand();
|
|
39384
39457
|
}
|
|
39458
|
+
function resolveGeminiProbeCommand() {
|
|
39459
|
+
if (process.platform !== "win32") return "gemini";
|
|
39460
|
+
const appData = process.env.APPDATA;
|
|
39461
|
+
if (appData) {
|
|
39462
|
+
const appDataShim = (0, import_path13.join)(appData, "npm", "gemini.cmd");
|
|
39463
|
+
if ((0, import_fs15.existsSync)(appDataShim)) return appDataShim;
|
|
39464
|
+
}
|
|
39465
|
+
return "gemini.cmd";
|
|
39466
|
+
}
|
|
39467
|
+
function resolveRunnerExecutionCommand(runner) {
|
|
39468
|
+
return runner === "gemini" ? resolveGeminiProbeCommand() : resolveCodexExecutionCommand();
|
|
39469
|
+
}
|
|
39385
39470
|
function validateCodexRunner(options) {
|
|
39386
39471
|
if (options.skipRunnerProbe) {
|
|
39387
39472
|
return {
|
|
39473
|
+
runner: "codex",
|
|
39388
39474
|
binaryChecked: false,
|
|
39389
39475
|
requiredFlagsChecked: false,
|
|
39390
39476
|
version: null,
|
|
@@ -39444,6 +39530,7 @@ ${yoloHelp.stderr}`;
|
|
|
39444
39530
|
}
|
|
39445
39531
|
if (supportsModernApprovalMode) {
|
|
39446
39532
|
return {
|
|
39533
|
+
runner: "codex",
|
|
39447
39534
|
binaryChecked: true,
|
|
39448
39535
|
requiredFlagsChecked: true,
|
|
39449
39536
|
version: versionText,
|
|
@@ -39459,6 +39546,7 @@ ${yoloHelp.stderr}`;
|
|
|
39459
39546
|
};
|
|
39460
39547
|
}
|
|
39461
39548
|
return {
|
|
39549
|
+
runner: "codex",
|
|
39462
39550
|
binaryChecked: true,
|
|
39463
39551
|
requiredFlagsChecked: true,
|
|
39464
39552
|
version: versionText,
|
|
@@ -39473,6 +39561,77 @@ ${yoloHelp.stderr}`;
|
|
|
39473
39561
|
execArgs: ["--full-auto"]
|
|
39474
39562
|
};
|
|
39475
39563
|
}
|
|
39564
|
+
function validateGeminiRunner(options) {
|
|
39565
|
+
const execArgs = [
|
|
39566
|
+
"--approval-mode",
|
|
39567
|
+
"yolo",
|
|
39568
|
+
"--sandbox",
|
|
39569
|
+
"--output-format",
|
|
39570
|
+
"stream-json",
|
|
39571
|
+
"--prompt",
|
|
39572
|
+
"Execute the FOH external-agent prompt supplied on stdin."
|
|
39573
|
+
];
|
|
39574
|
+
if (options.skipRunnerProbe) {
|
|
39575
|
+
return {
|
|
39576
|
+
runner: "gemini",
|
|
39577
|
+
binaryChecked: false,
|
|
39578
|
+
requiredFlagsChecked: false,
|
|
39579
|
+
version: null,
|
|
39580
|
+
rootHelpChecked: false,
|
|
39581
|
+
execHelpChecked: false,
|
|
39582
|
+
supportsModernApprovalMode: false,
|
|
39583
|
+
supportsLegacyFullAuto: false,
|
|
39584
|
+
supportsYoloAlias: null,
|
|
39585
|
+
yoloPolicy: "not_checked",
|
|
39586
|
+
supportsGeminiHeadless: true,
|
|
39587
|
+
automationMode: "gemini-yolo",
|
|
39588
|
+
globalArgs: [],
|
|
39589
|
+
execArgs
|
|
39590
|
+
};
|
|
39591
|
+
}
|
|
39592
|
+
const probe = options.runnerProbe ?? defaultRunnerProbe;
|
|
39593
|
+
const probeCommand = resolveGeminiProbeCommand();
|
|
39594
|
+
const version2 = probe(probeCommand, ["--version"]);
|
|
39595
|
+
if (version2.error || version2.status !== 0) {
|
|
39596
|
+
throw new ExternalAgentExecutorError("external_agent_runner_binary_missing", "Gemini runner probe failed: `gemini --version` did not exit 0.");
|
|
39597
|
+
}
|
|
39598
|
+
const versionText = `${version2.stdout}
|
|
39599
|
+
${version2.stderr}`.trim() || null;
|
|
39600
|
+
const help = probe(probeCommand, ["--help"]);
|
|
39601
|
+
if (help.error || help.status !== 0) {
|
|
39602
|
+
throw new ExternalAgentExecutorError("external_agent_runner_help_unavailable", "Gemini runner probe failed: `gemini --help` did not exit 0.");
|
|
39603
|
+
}
|
|
39604
|
+
const helpText = `${help.stdout}
|
|
39605
|
+
${help.stderr}`;
|
|
39606
|
+
const requiredFlags = ["--prompt", "--approval-mode", "--sandbox", "--output-format"];
|
|
39607
|
+
const missing = requiredFlags.filter((flag) => !helpText.includes(flag));
|
|
39608
|
+
if (missing.length > 0) {
|
|
39609
|
+
throw new ExternalAgentExecutorError(
|
|
39610
|
+
"external_agent_runner_required_flags_missing",
|
|
39611
|
+
`Gemini runner is missing required headless flag(s): ${missing.join(", ")}`
|
|
39612
|
+
);
|
|
39613
|
+
}
|
|
39614
|
+
return {
|
|
39615
|
+
runner: "gemini",
|
|
39616
|
+
binaryChecked: true,
|
|
39617
|
+
requiredFlagsChecked: true,
|
|
39618
|
+
version: versionText,
|
|
39619
|
+
rootHelpChecked: true,
|
|
39620
|
+
execHelpChecked: true,
|
|
39621
|
+
supportsModernApprovalMode: false,
|
|
39622
|
+
supportsLegacyFullAuto: false,
|
|
39623
|
+
supportsYoloAlias: helpText.includes("--yolo"),
|
|
39624
|
+
yoloPolicy: helpText.includes("--yolo") ? "observed_not_canonical" : "unsupported",
|
|
39625
|
+
supportsGeminiHeadless: true,
|
|
39626
|
+
automationMode: "gemini-yolo",
|
|
39627
|
+
globalArgs: [],
|
|
39628
|
+
execArgs
|
|
39629
|
+
};
|
|
39630
|
+
}
|
|
39631
|
+
function validateRunner(options, runner) {
|
|
39632
|
+
if (runner === "gemini") return validateGeminiRunner(options);
|
|
39633
|
+
return validateCodexRunner(options);
|
|
39634
|
+
}
|
|
39476
39635
|
function normalizeCodexSandboxBackend(value) {
|
|
39477
39636
|
const normalized = (value || "default").trim().toLowerCase();
|
|
39478
39637
|
if (normalized === "default" || normalized === "legacy-landlock") return normalized;
|
|
@@ -39520,12 +39679,12 @@ function promptVersionFromPath(promptPath) {
|
|
|
39520
39679
|
}
|
|
39521
39680
|
function createExternalAgentExecutorPlan(options) {
|
|
39522
39681
|
const runner = String(options.runner || "codex");
|
|
39523
|
-
if (runner !== "codex") {
|
|
39682
|
+
if (runner !== "codex" && runner !== "gemini") {
|
|
39524
39683
|
throw new ExternalAgentExecutorError("unsupported_external_agent_runner", `Unsupported runner: ${runner}`);
|
|
39525
39684
|
}
|
|
39526
39685
|
const batchPath = (0, import_path13.resolve)(options.batchPath);
|
|
39527
39686
|
const batch = readBatch(batchPath);
|
|
39528
|
-
const runnerProbe =
|
|
39687
|
+
const runnerProbe = validateRunner(options, runner);
|
|
39529
39688
|
const codexSandboxBackend = normalizeCodexSandboxBackend(options.codexSandboxBackend);
|
|
39530
39689
|
const codexSandboxMode = normalizeCodexSandboxMode(options.codexSandboxMode);
|
|
39531
39690
|
const codexNetworkAccess = options.codexNetworkAccess === true;
|
|
@@ -39564,12 +39723,16 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39564
39723
|
promptVersion: promptVersionFromPath(promptPath)
|
|
39565
39724
|
});
|
|
39566
39725
|
const promptVersion = String(env[EXTERNAL_AGENT_PROMPT_VERSION_ENV] || "unknown");
|
|
39567
|
-
const
|
|
39568
|
-
const
|
|
39569
|
-
const
|
|
39726
|
+
const outputStem = runner === "gemini" ? "gemini" : "codex";
|
|
39727
|
+
const jsonlPath = (0, import_path13.join)(runDir, `${outputStem}-exec.jsonl`);
|
|
39728
|
+
const lastMessagePath = (0, import_path13.join)(runDir, `${outputStem}-last-message.md`);
|
|
39729
|
+
const stderrPath = (0, import_path13.join)(runDir, `${outputStem}-stderr.txt`);
|
|
39570
39730
|
const runPath = (0, import_path13.join)(runDir, "run.json");
|
|
39571
39731
|
const artifactSafetyPath = (0, import_path13.join)(runDir, "artifact-safety.json");
|
|
39572
|
-
const args = [
|
|
39732
|
+
const args = runner === "gemini" ? [
|
|
39733
|
+
...runnerProbe.globalArgs,
|
|
39734
|
+
...runnerProbe.execArgs
|
|
39735
|
+
] : [
|
|
39573
39736
|
...runnerProbe.globalArgs,
|
|
39574
39737
|
"exec",
|
|
39575
39738
|
...codexConfigArgs({ backend: codexSandboxBackend, networkAccess: codexNetworkAccess }),
|
|
@@ -39594,7 +39757,7 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39594
39757
|
run_dir: runDir,
|
|
39595
39758
|
prompt_path: promptPath,
|
|
39596
39759
|
workspace_dir: workspaceDir,
|
|
39597
|
-
command:
|
|
39760
|
+
command: runner,
|
|
39598
39761
|
args,
|
|
39599
39762
|
env_keys: Object.keys(env).sort(),
|
|
39600
39763
|
outputs: {
|
|
@@ -39624,6 +39787,7 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39624
39787
|
denied_env_names: [...CODEX_EXECUTOR_DENIED_ENV_NAMES],
|
|
39625
39788
|
runner_probe: {
|
|
39626
39789
|
binary_checked: runnerProbe.binaryChecked,
|
|
39790
|
+
runner: runnerProbe.runner,
|
|
39627
39791
|
required_flags_checked: runnerProbe.requiredFlagsChecked,
|
|
39628
39792
|
version: runnerProbe.version,
|
|
39629
39793
|
root_help_checked: runnerProbe.rootHelpChecked,
|
|
@@ -39633,9 +39797,11 @@ function createExternalAgentExecutorPlan(options) {
|
|
|
39633
39797
|
supports_yolo_alias: runnerProbe.supportsYoloAlias,
|
|
39634
39798
|
selected_automation_mode: runnerProbe.automationMode,
|
|
39635
39799
|
canonical_command_policy: "explicit-flags",
|
|
39636
|
-
yolo_policy: runnerProbe.yoloPolicy
|
|
39800
|
+
yolo_policy: runnerProbe.yoloPolicy,
|
|
39801
|
+
...runnerProbe.runner === "gemini" ? { supports_gemini_headless: runnerProbe.supportsGeminiHeadless } : {}
|
|
39637
39802
|
},
|
|
39638
|
-
|
|
39803
|
+
runner_automation_mode: runnerProbe.automationMode,
|
|
39804
|
+
codex_automation_mode: runner === "codex" ? runnerProbe.automationMode : null,
|
|
39639
39805
|
codex_sandbox_mode: codexSandboxMode,
|
|
39640
39806
|
codex_sandbox_backend: codexSandboxBackend,
|
|
39641
39807
|
codex_network_access: codexNetworkAccess
|
|
@@ -39698,7 +39864,7 @@ function relativeArtifactName(path2) {
|
|
|
39698
39864
|
return (0, import_path13.basename)(path2);
|
|
39699
39865
|
}
|
|
39700
39866
|
function classifyRun(input) {
|
|
39701
|
-
if (input.timedOut) return { status: "hold", reasonCode:
|
|
39867
|
+
if (input.timedOut) return { status: "hold", reasonCode: `${input.run.command}_runner_timeout` };
|
|
39702
39868
|
if (!input.artifactSafetyOk) return { status: "fail", reasonCode: "external_agent_artifact_safety_blocked" };
|
|
39703
39869
|
const completedCommands = readCommandRecords(input.run.run_dir).filter((record2) => record2.phase === "completed");
|
|
39704
39870
|
const observedVersions = completedCommands.map((record2) => String(record2.cli_version || "").trim()).filter((version2) => /^\d+\.\d+\.\d+$/.test(version2));
|
|
@@ -39783,7 +39949,7 @@ ${stderr}`;
|
|
|
39783
39949
|
if (/browser|approve|approval|login|auth|sign in/i.test(combined) && !proofArtifactPasses(input.run.run_dir)) {
|
|
39784
39950
|
return { status: "hold", reasonCode: "auth_browser_approval_required" };
|
|
39785
39951
|
}
|
|
39786
|
-
if (input.exitCode !== 0) return { status: "hold", reasonCode:
|
|
39952
|
+
if (input.exitCode !== 0) return { status: "hold", reasonCode: `${input.run.command}_runner_nonzero_exit` };
|
|
39787
39953
|
if (proofArtifactPasses(input.run.run_dir)) return { status: "pass", reasonCode: null };
|
|
39788
39954
|
return { status: "hold", reasonCode: "external_agent_proof_artifact_missing" };
|
|
39789
39955
|
}
|
|
@@ -39797,7 +39963,7 @@ function buildExecutedRunArtifact(input) {
|
|
|
39797
39963
|
failure_reason_code: input.reasonCode,
|
|
39798
39964
|
model_provider: input.run.model_provider,
|
|
39799
39965
|
model_name: input.run.model_name,
|
|
39800
|
-
agent_shell:
|
|
39966
|
+
agent_shell: `${input.run.command}-exec`,
|
|
39801
39967
|
workspace_type: "clean-no-repo-programmatic",
|
|
39802
39968
|
prompt_version: input.run.prompt_version,
|
|
39803
39969
|
prompt_path: "prompt.txt",
|
|
@@ -39847,11 +40013,13 @@ function buildExecutedRunArtifact(input) {
|
|
|
39847
40013
|
improvement_packet: input.status === "pass" ? null : "improvement-packet.json",
|
|
39848
40014
|
agent_metadata: agentMetadata.path,
|
|
39849
40015
|
notes: (0, import_fs15.existsSync)((0, import_path13.join)(input.run.run_dir, "notes.md")) ? "notes.md" : null,
|
|
39850
|
-
|
|
39851
|
-
|
|
40016
|
+
runner_last_message: relativeArtifactName(input.run.outputs.last_message),
|
|
40017
|
+
runner_stderr: relativeArtifactName(input.run.outputs.stderr),
|
|
40018
|
+
codex_last_message: input.run.command === "codex" ? relativeArtifactName(input.run.outputs.last_message) : null,
|
|
40019
|
+
codex_stderr: input.run.command === "codex" ? relativeArtifactName(input.run.outputs.stderr) : null,
|
|
39852
40020
|
artifact_safety: relativeArtifactName(input.run.outputs.artifact_safety)
|
|
39853
40021
|
},
|
|
39854
|
-
summary: input.status === "pass" ?
|
|
40022
|
+
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}.`,
|
|
39855
40023
|
next_commands: input.status === "pass" ? [externalAgentSummaryCommand((0, import_path13.dirname)(input.run.run_dir))] : [
|
|
39856
40024
|
"foh eval external-agent scan-artifacts --run-dir <run_dir> --private-repo-root <private_repo_root> --write-redacted --json",
|
|
39857
40025
|
"foh bug improve --from external-agent-run --file <run_dir>/run.json --out <run_dir>/improvement-packet.json --json",
|
|
@@ -39859,7 +40027,7 @@ function buildExecutedRunArtifact(input) {
|
|
|
39859
40027
|
]
|
|
39860
40028
|
};
|
|
39861
40029
|
}
|
|
39862
|
-
function
|
|
40030
|
+
function spawnRunner(input) {
|
|
39863
40031
|
return new Promise((resolveRun) => {
|
|
39864
40032
|
const started = Date.now();
|
|
39865
40033
|
const commandInvocation = buildCommandInvocation(input.command, input.args);
|
|
@@ -39908,15 +40076,18 @@ function spawnCodex(input) {
|
|
|
39908
40076
|
}
|
|
39909
40077
|
function buildCommandInvocation(command, args) {
|
|
39910
40078
|
if (process.platform === "win32" && command.toLowerCase().endsWith(".cmd")) {
|
|
39911
|
-
const
|
|
40079
|
+
const binDir = (0, import_path13.dirname)(command);
|
|
40080
|
+
const codexEntrypoint = (0, import_path13.join)(binDir, "node_modules", "@openai", "codex", "bin", "codex.js");
|
|
39912
40081
|
if ((0, import_fs15.existsSync)(codexEntrypoint)) return { command: process.execPath, args: [codexEntrypoint, ...args] };
|
|
40082
|
+
const geminiEntrypoint = (0, import_path13.join)(binDir, "node_modules", "@google", "gemini-cli", "bundle", "gemini.js");
|
|
40083
|
+
if ((0, import_fs15.existsSync)(geminiEntrypoint)) return { command: process.execPath, args: ["--no-warnings=DEP0040", geminiEntrypoint, ...args] };
|
|
39913
40084
|
}
|
|
39914
40085
|
return { command, args };
|
|
39915
40086
|
}
|
|
39916
40087
|
async function executeExternalAgentExecutorPlan(plan, options = {}) {
|
|
39917
40088
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
39918
40089
|
const results = [];
|
|
39919
|
-
const runnerCommand = options.runnerCommand ||
|
|
40090
|
+
const runnerCommand = options.runnerCommand || resolveRunnerExecutionCommand(plan.runner);
|
|
39920
40091
|
const authPreflight = options.env ? await runExternalAgentEvalAuthPreflight(options.env) : null;
|
|
39921
40092
|
if (authPreflight && !authPreflight.ok) {
|
|
39922
40093
|
const endedAt2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -39966,7 +40137,7 @@ async function executeExternalAgentExecutorPlan(plan, options = {}) {
|
|
|
39966
40137
|
runDir: commandCaptureDir,
|
|
39967
40138
|
promptVersion: run.prompt_version
|
|
39968
40139
|
});
|
|
39969
|
-
const spawned = await
|
|
40140
|
+
const spawned = await spawnRunner({
|
|
39970
40141
|
command: runnerCommand,
|
|
39971
40142
|
args: run.args,
|
|
39972
40143
|
cwd: run.workspace_dir,
|