llm-cli-gateway 1.17.1 → 1.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { randomUUID } from "crypto";
5
5
  import { existsSync, readFileSync, readdirSync, renameSync, unlinkSync } from "fs";
6
6
  import { dirname, join } from "path";
7
7
  import { fileURLToPath } from "url";
8
- import { z } from "zod";
8
+ import { z } from "zod/v3";
9
9
  import { executeCli, killAllProcessGroups } from "./executor.js";
10
10
  import { parseStreamJson } from "./stream-json-parser.js";
11
11
  import { parseCodexJsonStream } from "./codex-json-parser.js";
@@ -28,7 +28,7 @@ import { buildClaudeMcpConfig, CLAUDE_MCP_SERVER_NAMES, } from "./claude-mcp-con
28
28
  import { resolveGrokSessionArgs, resolveMistralSessionArgs, resolveCodexSessionArgs, sanitizeCliArgValues, prepareMistralRequest as buildMistralCliInvocation, MISTRAL_AGENT_MODES, GATEWAY_SESSION_PREFIX, resolveClaudePermissionFlags, resolveCodexSandboxFlags, CLAUDE_PERMISSION_MODES, GEMINI_APPROVAL_MODES, CODEX_SANDBOX_MODES, CODEX_ASK_FOR_APPROVAL_MODES, CLAUDE_EFFORT_LEVELS, prepareClaudeHighImpactFlags, validateClaudeAgentsMap, prepareCodexHighImpactFlags, prepareCodexForkRequest, CODEX_CONFIG_OVERRIDES_SCHEMA, prepareGeminiHighImpactFlags, prependGeminiAttachments, resolveGeminiSessionPlan, GEMINI_HIGH_IMPACT_PARAMS_SCHEMA, } from "./request-helpers.js";
29
29
  import { createFlightRecorder } from "./flight-recorder.js";
30
30
  import { resolvePromptInput, PromptPartsSchema, assembleClaudeCacheBlocks, } from "./prompt-parts.js";
31
- import { computeSessionCacheStats, computeTtlRemaining } from "./cache-stats.js";
31
+ import { computeSessionCacheStats, computeTtlRemaining, readPersistedRequest, PERSISTED_REQUEST_DEFAULT_MAX_CHARS, } from "./cache-stats.js";
32
32
  import { getCliVersions, runCliUpgrade } from "./cli-updater.js";
33
33
  import { startHttpGateway } from "./http-transport.js";
34
34
  import { printDoctorJson } from "./doctor.js";
@@ -161,12 +161,13 @@ Tools: claude_request, codex_request, gemini_request, grok_request, mistral_requ
161
161
  Validation: validate_with_models, second_opinion, compare_answers, red_team_review, consensus_check, ask_model, synthesize_validation
162
162
  Jobs: llm_job_status, llm_job_result, llm_job_cancel
163
163
  Sessions: session_create, session_list, session_set_active, session_get, session_delete, session_clear_all
164
- Other: list_models, cli_versions, upstream_contracts, cli_upgrade, approval_list, llm_process_health
164
+ Other: list_models, cli_versions, upstream_contracts (use --probe-installed after CLI upgrades to detect drift), cli_upgrade, approval_list, llm_process_health, llm_request_result (read back any persisted request — sync or async — by correlationId)
165
165
 
166
166
  Key behaviors:
167
167
  - Sync auto-defers at ${SYNC_DEADLINE_MS}ms. Poll deferred jobs via llm_job_status/llm_job_result.
168
168
  - Sessions: Claude --continue, Gemini --resume, Grok --resume/--continue, Mistral --resume/--continue (current Vibe defaults session logging on; doctor flags explicit session_logging.enabled=false), Codex \`exec resume <ID>\` / \`exec resume --last\` (all real CLI continuity). For Codex, sessionId must be a real Codex UUID (from ~/.codex/sessions/); gateway-generated gw-* IDs are rejected.
169
169
  - Approval gates: opt-in via approvalStrategy:"mcp_managed".
170
+ - Upstream drift detection: After upgrading any provider CLI (especially grok), use the upstream_contracts tool with probeInstalled: true (or the CLI command "llm-cli-gateway contracts --json --probe-installed"). This is the primary reliable way to detect when an installed binary has gained or lost flags compared to the gateway's declared contract. The probe is safe and read-only.
170
171
  - Idle timeout kills stuck processes (default 10min, configurable via idleTimeoutMs).
171
172
 
172
173
  Skills (full docs via MCP resources):
@@ -1338,7 +1339,7 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1338
1339
  }
1339
1340
  }
1340
1341
  // Resume mode: codex exec resume <SESSION_ID|--last> [flags] PROMPT
1341
- // Note: `codex exec resume` does NOT accept `--full-auto`; the original
1342
+ // Note: `codex exec resume` does NOT accept sandbox policy flags; the original
1342
1343
  // session's approval policy is inherited. We silently drop fullAuto on resume.
1343
1344
  let sessionPlan;
1344
1345
  try {
@@ -1363,16 +1364,16 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1363
1364
  // Codex sandbox / approval: resolve modern flags + legacy fullAuto shorthand.
1364
1365
  // `codex exec resume` rejects all of these (the original session's policy is
1365
1366
  // inherited), so we only emit them when starting a NEW session.
1367
+ const sandboxFlags = resolveCodexSandboxFlags({
1368
+ sandboxMode: params.sandboxMode,
1369
+ askForApproval: params.askForApproval,
1370
+ fullAuto: params.fullAuto,
1371
+ useLegacyFullAutoFlag: params.useLegacyFullAutoFlag,
1372
+ });
1373
+ if (sandboxFlags.warning) {
1374
+ runtime.logger.warn(`[${corrId}] ${sandboxFlags.warning}`);
1375
+ }
1366
1376
  if (sessionPlan.mode === "new") {
1367
- const sandboxFlags = resolveCodexSandboxFlags({
1368
- sandboxMode: params.sandboxMode,
1369
- askForApproval: params.askForApproval,
1370
- fullAuto: params.fullAuto,
1371
- useLegacyFullAutoFlag: params.useLegacyFullAutoFlag,
1372
- });
1373
- if (sandboxFlags.warning) {
1374
- runtime.logger.warn(`[${corrId}] ${sandboxFlags.warning}`);
1375
- }
1376
1377
  args.push(...sandboxFlags.args);
1377
1378
  }
1378
1379
  if (params.dangerouslyBypassApprovalsAndSandbox) {
@@ -1386,19 +1387,18 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1386
1387
  args.push("--json");
1387
1388
  }
1388
1389
  args.push("--skip-git-repo-check");
1389
- // U26: High-impact feature flags. `--search` is rejected by
1390
- // `codex exec resume` (resume inherits the original session's web-search
1391
- // state), so we only emit it on a NEW session. `--output-schema`,
1392
- // `-c key=value`, profile, ephemeral, images, and the ignore-* flags are
1393
- // all accepted on resume per `codex exec resume --help` (codex-cli 0.133.0)
1394
- // and are emitted in both branches.
1390
+ // U26: High-impact feature flags. `--search` is retained as a compatibility
1391
+ // input but current `codex exec` no longer accepts it, so the helper warns
1392
+ // and emits no argv. `--profile` is accepted for new sessions only. The other
1393
+ // flags here are accepted on resume per `codex exec resume --help` and are
1394
+ // emitted in both branches.
1395
1395
  let highImpactCleanup;
1396
1396
  if (sessionPlan.mode === "new") {
1397
1397
  // Phase 4 slice ζ: emit working-dir and add-dir on new sessions only.
1398
1398
  // Both flags are listed in CODEX_RESUME_FILTERED_FLAGS — resume inherits
1399
1399
  // the original session's cwd and writable-dir policy, so emitting them
1400
1400
  // on resume would be silently stripped (wasteful + misleading on argv
1401
- // logs). Gating here mirrors `--search` / `--sandbox` / `--full-auto`.
1401
+ // logs). Gating here mirrors `--search` / `--sandbox`.
1402
1402
  if (params.workingDir) {
1403
1403
  args.push("-C", params.workingDir);
1404
1404
  }
@@ -1420,13 +1420,20 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1420
1420
  if (high.missingImagePath) {
1421
1421
  return createErrorResponse(params.operation, 1, "", corrId, new Error(`images: path does not exist: ${high.missingImagePath}`));
1422
1422
  }
1423
+ if (high.warning) {
1424
+ runtime.logger.warn(`[${corrId}] ${high.warning}`);
1425
+ }
1423
1426
  args.push(...high.args);
1424
1427
  highImpactCleanup = high.cleanup;
1425
1428
  }
1426
1429
  else {
1430
+ if (params.profile) {
1431
+ runtime.logger.warn(`[${corrId}] profile is ignored on Codex resume because current codex exec resume does not accept --profile.`);
1432
+ }
1427
1433
  const high = prepareCodexHighImpactFlags({
1428
1434
  outputSchema: params.outputSchema,
1429
- profile: params.profile,
1435
+ search: params.search,
1436
+ profile: undefined,
1430
1437
  configOverrides: params.configOverrides,
1431
1438
  ephemeral: params.ephemeral,
1432
1439
  images: params.images,
@@ -1436,6 +1443,9 @@ export function prepareCodexRequest(params, runtime = resolveGatewayServerRuntim
1436
1443
  if (high.missingImagePath) {
1437
1444
  return createErrorResponse(params.operation, 1, "", corrId, new Error(`images: path does not exist: ${high.missingImagePath}`));
1438
1445
  }
1446
+ if (high.warning) {
1447
+ runtime.logger.warn(`[${corrId}] ${high.warning}`);
1448
+ }
1439
1449
  args.push(...high.args);
1440
1450
  highImpactCleanup = high.cleanup;
1441
1451
  }
@@ -2861,7 +2871,7 @@ export function createGatewayServer(deps = {}) {
2861
2871
  .optional()
2862
2872
  .describe("Claude --agent: dispatch to a named single sub-agent."),
2863
2873
  agents: z
2864
- .record(z.record(z.unknown()))
2874
+ .record(z.string(), z.record(z.string(), z.unknown()))
2865
2875
  .optional()
2866
2876
  .describe("Claude --agents: inline JSON map of agent name → { description, prompt, tools?, model? }."),
2867
2877
  forkSession: z
@@ -2902,7 +2912,7 @@ export function createGatewayServer(deps = {}) {
2902
2912
  .optional()
2903
2913
  .describe("Claude --fallback-model: model name to auto-fallback to when the default model is overloaded (effective only with --print, which the gateway always uses)."),
2904
2914
  jsonSchema: z
2905
- .union([z.string(), z.record(z.unknown())])
2915
+ .union([z.string(), z.record(z.string(), z.unknown())])
2906
2916
  .optional()
2907
2917
  .describe("Claude --json-schema: JSON Schema literal (NOT a path) constraining structured output. Object values are JSON.stringify-d; string values are passed verbatim. Use with outputFormat='json'."),
2908
2918
  // Phase 4 slice ζ — Claude additional-workspace-dirs parity
@@ -3170,7 +3180,7 @@ export function createGatewayServer(deps = {}) {
3170
3180
  fullAuto: z
3171
3181
  .boolean()
3172
3182
  .default(false)
3173
- .describe("DEPRECATED: prefer `sandboxMode` + `askForApproval`. Expands to `--sandbox workspace-write --ask-for-approval never`."),
3183
+ .describe("DEPRECATED: prefer `sandboxMode`. Expands to `--sandbox workspace-write`; current Codex no longer accepts approval-policy flags."),
3174
3184
  sandboxMode: z
3175
3185
  .enum(CODEX_SANDBOX_MODES)
3176
3186
  .optional()
@@ -3178,11 +3188,11 @@ export function createGatewayServer(deps = {}) {
3178
3188
  askForApproval: z
3179
3189
  .enum(CODEX_ASK_FOR_APPROVAL_MODES)
3180
3190
  .optional()
3181
- .describe("Codex --ask-for-approval: untrusted|on-request|never."),
3191
+ .describe("DEPRECATED compatibility input: accepted but ignored because current Codex no longer accepts --ask-for-approval."),
3182
3192
  useLegacyFullAutoFlag: z
3183
3193
  .boolean()
3184
3194
  .default(false)
3185
- .describe("Escape hatch: emit `--full-auto` directly instead of expanding (deprecated)."),
3195
+ .describe("DEPRECATED compatibility input: accepted but ignored because current Codex no longer accepts --full-auto."),
3186
3196
  dangerouslyBypassApprovalsAndSandbox: z
3187
3197
  .boolean()
3188
3198
  .default(false)
@@ -3231,10 +3241,13 @@ export function createGatewayServer(deps = {}) {
3231
3241
  .describe("Codex output format. `json` emits --json (JSONL events) so token usage and cost are parsed and reported in the flight recorder. `text` is the default."),
3232
3242
  // U26: high-impact feature flags. All optional.
3233
3243
  outputSchema: z
3234
- .union([z.string(), z.record(z.unknown())])
3244
+ .union([z.string(), z.record(z.string(), z.unknown())])
3235
3245
  .optional()
3236
3246
  .describe("Codex --output-schema. Pass a path (string) or an inline JSON Schema object; object is materialised to a 0o600 temp file under os.tmpdir() and deleted after the run."),
3237
- search: z.boolean().optional().describe("Emit Codex --search to enable web search."),
3247
+ search: z
3248
+ .boolean()
3249
+ .optional()
3250
+ .describe("DEPRECATED compatibility input: accepted but ignored because current Codex exec no longer accepts --search."),
3238
3251
  profile: z
3239
3252
  .string()
3240
3253
  .optional()
@@ -3445,7 +3458,7 @@ export function createGatewayServer(deps = {}) {
3445
3458
  askForApproval: z
3446
3459
  .enum(CODEX_ASK_FOR_APPROVAL_MODES)
3447
3460
  .optional()
3448
- .describe("Codex --ask-for-approval: untrusted|on-request|never."),
3461
+ .describe("DEPRECATED compatibility input: accepted but ignored because current Codex no longer accepts --ask-for-approval."),
3449
3462
  correlationId: z.string().optional().describe("Request trace ID (auto if omitted)"),
3450
3463
  idleTimeoutMs: z
3451
3464
  .number()
@@ -3922,7 +3935,7 @@ export function createGatewayServer(deps = {}) {
3922
3935
  .optional()
3923
3936
  .describe("Claude --agent: dispatch to a named single sub-agent."),
3924
3937
  agents: z
3925
- .record(z.record(z.unknown()))
3938
+ .record(z.string(), z.record(z.string(), z.unknown()))
3926
3939
  .optional()
3927
3940
  .describe("Claude --agents: inline JSON map of agent name → { description, prompt, tools?, model? }."),
3928
3941
  forkSession: z
@@ -3963,7 +3976,7 @@ export function createGatewayServer(deps = {}) {
3963
3976
  .optional()
3964
3977
  .describe("Claude --fallback-model: model name to auto-fallback to when the default model is overloaded (effective only with --print, which the gateway always uses)."),
3965
3978
  jsonSchema: z
3966
- .union([z.string(), z.record(z.unknown())])
3979
+ .union([z.string(), z.record(z.string(), z.unknown())])
3967
3980
  .optional()
3968
3981
  .describe("Claude --json-schema: JSON Schema literal (NOT a path) constraining structured output. Object values are JSON.stringify-d; string values are passed verbatim. Use with outputFormat='json'."),
3969
3982
  // Phase 4 slice ζ — Claude additional-workspace-dirs parity
@@ -4137,7 +4150,7 @@ export function createGatewayServer(deps = {}) {
4137
4150
  fullAuto: z
4138
4151
  .boolean()
4139
4152
  .default(false)
4140
- .describe("DEPRECATED: prefer `sandboxMode` + `askForApproval`. Expands to `--sandbox workspace-write --ask-for-approval never`."),
4153
+ .describe("DEPRECATED: prefer `sandboxMode`. Expands to `--sandbox workspace-write`; current Codex no longer accepts approval-policy flags."),
4141
4154
  sandboxMode: z
4142
4155
  .enum(CODEX_SANDBOX_MODES)
4143
4156
  .optional()
@@ -4145,11 +4158,11 @@ export function createGatewayServer(deps = {}) {
4145
4158
  askForApproval: z
4146
4159
  .enum(CODEX_ASK_FOR_APPROVAL_MODES)
4147
4160
  .optional()
4148
- .describe("Codex --ask-for-approval: untrusted|on-request|never."),
4161
+ .describe("DEPRECATED compatibility input: accepted but ignored because current Codex no longer accepts --ask-for-approval."),
4149
4162
  useLegacyFullAutoFlag: z
4150
4163
  .boolean()
4151
4164
  .default(false)
4152
- .describe("Escape hatch: emit `--full-auto` directly (deprecated)."),
4165
+ .describe("DEPRECATED compatibility input: accepted but ignored because current Codex no longer accepts --full-auto."),
4153
4166
  dangerouslyBypassApprovalsAndSandbox: z
4154
4167
  .boolean()
4155
4168
  .default(false)
@@ -4195,10 +4208,13 @@ export function createGatewayServer(deps = {}) {
4195
4208
  .describe("Codex output format. `json` emits --json (JSONL events) for token usage extraction."),
4196
4209
  // U26: high-impact feature flags. All optional.
4197
4210
  outputSchema: z
4198
- .union([z.string(), z.record(z.unknown())])
4211
+ .union([z.string(), z.record(z.string(), z.unknown())])
4199
4212
  .optional()
4200
4213
  .describe("Codex --output-schema. Pass a path (string) or an inline JSON Schema object."),
4201
- search: z.boolean().optional().describe("Emit Codex --search to enable web search."),
4214
+ search: z
4215
+ .boolean()
4216
+ .optional()
4217
+ .describe("DEPRECATED compatibility input: accepted but ignored because current Codex exec no longer accepts --search."),
4202
4218
  profile: z.string().optional().describe("Codex --profile <name>."),
4203
4219
  configOverrides: CODEX_CONFIG_OVERRIDES_SCHEMA.describe("Codex -c key=value overrides. Keys: /^[a-zA-Z0-9._]+$/. Values: no CR/LF."),
4204
4220
  ephemeral: z.boolean().optional().describe("Codex --ephemeral."),
@@ -4712,6 +4728,59 @@ export function createGatewayServer(deps = {}) {
4712
4728
  };
4713
4729
  });
4714
4730
  } // end if (asyncJobsEnabled)
4731
+ // Read back any persisted request (sync OR async) by its correlation id.
4732
+ // Registered unconditionally — it reads the flight recorder, which is
4733
+ // independent of async-job persistence. Every sync/async response echoes
4734
+ // its id in `structuredContent.correlationId`; pass that id here to recover
4735
+ // the persisted prompt/response after the inline result is gone. With flight
4736
+ // recording disabled (LLM_GATEWAY_LOGS_DB=none → NoopFlightRecorder) the
4737
+ // query yields no rows and this returns the "not found" shape.
4738
+ server.tool("llm_request_result", {
4739
+ correlationId: z
4740
+ .string()
4741
+ .min(1)
4742
+ .describe("Correlation id from a prior request's structuredContent.correlationId (sync or async)"),
4743
+ maxChars: z
4744
+ .number()
4745
+ .int()
4746
+ .min(1000)
4747
+ .max(2000000)
4748
+ .default(PERSISTED_REQUEST_DEFAULT_MAX_CHARS)
4749
+ .describe("Max chars of the persisted response to return"),
4750
+ includePrompt: z
4751
+ .boolean()
4752
+ .default(false)
4753
+ .describe("Include the full persisted prompt text in the result"),
4754
+ }, async ({ correlationId, maxChars, includePrompt }) => {
4755
+ const record = readPersistedRequest(flightRecorder, correlationId, {
4756
+ maxChars,
4757
+ includePrompt,
4758
+ });
4759
+ if (!record) {
4760
+ return {
4761
+ content: [
4762
+ {
4763
+ type: "text",
4764
+ text: JSON.stringify({
4765
+ success: false,
4766
+ error: "No persisted request found for this correlation id",
4767
+ correlationId,
4768
+ hint: "The id may be wrong, the row may have aged out of the flight recorder, or flight recording is disabled (LLM_GATEWAY_LOGS_DB=none).",
4769
+ }, null, 2),
4770
+ },
4771
+ ],
4772
+ isError: true,
4773
+ };
4774
+ }
4775
+ return {
4776
+ content: [
4777
+ {
4778
+ type: "text",
4779
+ text: JSON.stringify({ success: true, request: record }, null, 2),
4780
+ },
4781
+ ],
4782
+ };
4783
+ });
4715
4784
  server.tool("llm_process_health", {}, async () => {
4716
4785
  const health = asyncJobManager.getJobHealth();
4717
4786
  const persistenceBlock = {
@@ -4793,7 +4862,7 @@ export function createGatewayServer(deps = {}) {
4793
4862
  probeInstalled: z
4794
4863
  .boolean()
4795
4864
  .default(false)
4796
- .describe("When true, run local --help probes and compare advertised flags"),
4865
+ .describe("When true, run local --help probes and compare advertised flags against the declared contract. Strongly recommended after any provider CLI upgrade to detect drift."),
4797
4866
  }, async ({ cli, probeInstalled }) => {
4798
4867
  const report = buildUpstreamContractReport({ cli, probeInstalled });
4799
4868
  return { content: [{ type: "text", text: JSON.stringify(report, null, 2) }] };
@@ -5224,12 +5293,21 @@ async function main() {
5224
5293
  "Usage:",
5225
5294
  " llm-cli-gateway [doctor --json|contracts --json|--transport=http|--version]",
5226
5295
  "",
5296
+ "Doctor:",
5297
+ " doctor --json # environment, providers, declared contracts",
5298
+ " doctor --json --probe-upstream # + expensive installed --help probe for drift",
5299
+ "",
5300
+ "After upgrading provider CLIs (grok/claude/etc), use --probe-upstream or",
5301
+ " llm-cli-gateway contracts --json --probe-installed",
5302
+ "to detect when installed binaries have drifted from the gateway contracts.",
5303
+ "",
5227
5304
  ].join("\n"));
5228
5305
  return;
5229
5306
  }
5230
5307
  if (args[0] === "doctor") {
5231
5308
  if (args.includes("--json")) {
5232
- printDoctorJson();
5309
+ const probeUpstream = args.includes("--probe-upstream") || args.includes("--probe-installed");
5310
+ printDoctorJson({ probeUpstream });
5233
5311
  return;
5234
5312
  }
5235
5313
  process.stderr.write("Only doctor --json is supported in this layer.\n");
@@ -5249,7 +5327,13 @@ async function main() {
5249
5327
  process.stdout.write(JSON.stringify(buildUpstreamContractReport({ cli, probeInstalled }), null, 2) + "\n");
5250
5328
  return;
5251
5329
  }
5252
- process.stderr.write("Usage: llm-cli-gateway contracts --json [--cli=claude|codex|gemini|grok|mistral] [--probe-installed]\n");
5330
+ process.stderr.write([
5331
+ "Usage: llm-cli-gateway contracts --json [--cli=claude|codex|gemini|grok|mistral] [--probe-installed]",
5332
+ "",
5333
+ "After upgrading any provider CLI, use --probe-installed to detect drift between",
5334
+ "the installed binary's advertised flags and the gateway's declared contract.",
5335
+ "Example: llm-cli-gateway contracts --json --probe-installed --cli=grok",
5336
+ ].join("\n") + "\n");
5253
5337
  process.exit(2);
5254
5338
  }
5255
5339
  const transportArg = args.find(arg => arg.startsWith("--transport="));
@@ -36,9 +36,8 @@ export declare function parseProcStat(content: string): {
36
36
  */
37
37
  export declare function parseVmRss(content: string): number | null;
38
38
  export declare class ProcessMonitor {
39
- private logger;
40
39
  private prevSamples;
41
- constructor(logger?: Logger);
40
+ constructor(_logger?: Logger);
42
41
  /** Clear all cached CPU samples */
43
42
  reset(): void;
44
43
  sampleProcess(pid: number): ProcessHealth;
@@ -3,7 +3,6 @@
3
3
  * Gracefully degrades on non-Linux platforms.
4
4
  */
5
5
  import { readFileSync } from "fs";
6
- import { noopLogger } from "./logger.js";
7
6
  /**
8
7
  * Parse /proc/[pid]/stat safely.
9
8
  * The `comm` field (field 2) is in parentheses and may contain spaces,
@@ -18,10 +17,14 @@ export function parseProcStat(content) {
18
17
  // fields[0] = state, fields[11] = utime (14-3), fields[12] = stime (15-3)
19
18
  if (fields.length < 13)
20
19
  return null;
20
+ const utime = parseInt(fields[11], 10);
21
+ const stime = parseInt(fields[12], 10);
22
+ if (!Number.isFinite(utime) || !Number.isFinite(stime))
23
+ return null;
21
24
  return {
22
25
  state: fields[0],
23
- utime: parseInt(fields[11], 10),
24
- stime: parseInt(fields[12], 10),
26
+ utime,
27
+ stime,
25
28
  };
26
29
  }
27
30
  /**
@@ -48,12 +51,9 @@ function getTotalCpuJiffies() {
48
51
  }
49
52
  }
50
53
  export class ProcessMonitor {
51
- logger;
52
54
  // Previous samples for CPU delta calculation
53
55
  prevSamples = new Map();
54
- constructor(logger = noopLogger) {
55
- this.logger = logger;
56
- }
56
+ constructor(_logger) { }
57
57
  /** Clear all cached CPU samples */
58
58
  reset() {
59
59
  this.prevSamples.clear();
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { z } from "zod/v3";
2
2
  export interface PromptPartsCacheControl {
3
3
  system?: boolean;
4
4
  tools?: boolean;
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "crypto";
2
- import { z } from "zod";
2
+ import { z } from "zod/v3";
3
3
  const CacheControlSchema = z
4
4
  .object({
5
5
  system: z.boolean().optional(),
@@ -55,16 +55,16 @@ const GUIDANCE = {
55
55
  },
56
56
  grok: {
57
57
  provider: "grok",
58
- displayName: "Grok CLI",
58
+ displayName: "Grok Build",
59
59
  install: {
60
- summary: "Install Grok CLI using xAI's current official installer or managed update flow.",
61
- commands: ["npm install -g grok-build"],
62
- documentationUrl: "https://docs.x.ai/build/cli",
60
+ summary: "Install Grok Build using xAI's current official installer or managed update flow.",
61
+ commands: ["curl -fsSL https://x.ai/cli/install.sh | bash"],
62
+ documentationUrl: "https://docs.x.ai/build/overview",
63
63
  },
64
64
  login: {
65
65
  summary: "Sign in through Grok's official OAuth or device-code flow.",
66
66
  commands: ["grok login --oauth", "grok login --device-auth"],
67
- credentialHandling: "Do not paste xAI API keys, OAuth tokens, or Grok auth files into the gateway or a remote chat.",
67
+ credentialHandling: "For headless environments, set XAI_API_KEY in the local shell. Do not paste xAI API keys, OAuth tokens, or Grok auth files into the gateway or a remote chat.",
68
68
  },
69
69
  verification: {
70
70
  command: "grok inspect --json",
@@ -182,10 +182,6 @@ export function geminiAuthStatus(env = process.env, home = homedir()) {
182
182
  const status = oauth || geminiApiKey || googleApiKey || vertexAi ? "present" : "not_found";
183
183
  return { status, methods };
184
184
  }
185
- /** Back-compat shim retained for callers that only need the binary store status. */
186
- function geminiCredentialStoreStatus() {
187
- return geminiAuthStatus().status;
188
- }
189
185
  function grokCredentialStoreStatus() {
190
186
  const home = homedir();
191
187
  const candidates = [join(home, ".grok", "auth.json"), join(home, ".config", "grok", "auth.json")];
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import { z } from "zod/v3";
2
2
  /** Prefix for gateway-generated session IDs. Enforces provenance structurally. */
3
3
  export declare const GATEWAY_SESSION_PREFIX = "gw-";
4
4
  export interface SessionResumeResult {
@@ -38,8 +38,8 @@ export declare function resolveSessionResumeArgs(opts: {
38
38
  * - "resume-by-id" → `codex exec resume [...resume-safe flags] <SESSION_ID> PROMPT`
39
39
  * - "resume-latest" → `codex exec resume --last [...resume-safe flags] PROMPT`
40
40
  *
41
- * `codex exec resume` rejects `--full-auto`; the original session's approval
42
- * policy is inherited. Callers MUST filter `--full-auto` out of the flag set
41
+ * `codex exec resume` rejects sandbox/working-directory policy flags; the original session's approval
42
+ * policy is inherited. Callers MUST filter those flags out of the flag set
43
43
  * when mode is one of the resume forms (see `prepareCodexRequest`).
44
44
  *
45
45
  * `sessionId` MUST be a real Codex session UUID (as recorded under
@@ -198,41 +198,40 @@ export type GeminiApprovalMode = (typeof GEMINI_APPROVAL_MODES)[number];
198
198
  export declare const CODEX_SANDBOX_MODES: readonly ["read-only", "workspace-write", "danger-full-access"];
199
199
  export type CodexSandboxMode = (typeof CODEX_SANDBOX_MODES)[number];
200
200
  /**
201
- * Codex approval modes (for `--ask-for-approval <mode>`).
201
+ * Deprecated Codex approval modes. Current Codex no longer exposes an
202
+ * `--ask-for-approval` flag; the MCP input is temporarily retained so older
203
+ * callers do not fail schema validation, but it emits no CLI argv.
202
204
  */
203
205
  export declare const CODEX_ASK_FOR_APPROVAL_MODES: readonly ["untrusted", "on-request", "never"];
204
206
  export type CodexAskForApproval = (typeof CODEX_ASK_FOR_APPROVAL_MODES)[number];
205
207
  export interface CodexSandboxFlagsInput {
206
208
  /** Modern: explicit sandbox mode. */
207
209
  sandboxMode?: CodexSandboxMode;
208
- /** Modern: explicit approval mode. */
210
+ /** Deprecated compatibility input; current Codex exposes no approval-policy flag. */
209
211
  askForApproval?: CodexAskForApproval;
210
- /** Legacy: shorthand for sandbox=workspace-write + askForApproval=never. */
212
+ /** Legacy: shorthand for sandbox=workspace-write. */
211
213
  fullAuto?: boolean;
212
214
  /**
213
- * Escape hatch: when true + `fullAuto: true`, emit `--full-auto` directly
214
- * instead of expanding. Off by default. Deprecated and removed after
215
- * Mistral GA.
215
+ * Deprecated compatibility input. Current Codex rejects `--full-auto`, so
216
+ * this no longer changes argv emission.
216
217
  */
217
218
  useLegacyFullAutoFlag?: boolean;
218
219
  }
219
220
  export interface CodexSandboxFlagsResult {
220
221
  args: string[];
221
- /** Set when fullAuto + explicit sandbox/approval are both supplied. */
222
+ /** Set when deprecated/no-op compatibility inputs are supplied. */
222
223
  warning?: string;
223
224
  }
224
225
  /**
225
- * Resolve Codex `--sandbox` / `--ask-for-approval` args from the modern
226
- * params + legacy `fullAuto` shorthand.
226
+ * Resolve current Codex sandbox args from the modern params + legacy
227
+ * `fullAuto` shorthand. Current Codex exposes `--sandbox`, but no longer
228
+ * exposes `--ask-for-approval` or `--full-auto`.
227
229
  *
228
230
  * Precedence:
229
- * 1. If `useLegacyFullAutoFlag && fullAuto`, emit `--full-auto` directly
230
- * (escape hatch; deprecated).
231
- * 2. Else explicit `sandboxMode` / `askForApproval` always emit their
232
- * flags. If `fullAuto: true` is set alongside, a warning is attached
233
- * and the explicit values win.
234
- * 3. Else if `fullAuto: true`, expand to
235
- * `--sandbox workspace-write --ask-for-approval never`.
231
+ * 1. Explicit `sandboxMode` emits `--sandbox <mode>`.
232
+ * 2. Else if `fullAuto: true`, expand to `--sandbox workspace-write`.
233
+ * 3. Deprecated `askForApproval` and `useLegacyFullAutoFlag` emit no argv
234
+ * and return warnings for callers to surface/log.
236
235
  * 4. Else emit nothing.
237
236
  */
238
237
  export declare function resolveCodexSandboxFlags(input: CodexSandboxFlagsInput): CodexSandboxFlagsResult;
@@ -240,19 +239,20 @@ export declare function resolveCodexSandboxFlags(input: CodexSandboxFlagsInput):
240
239
  * Flags that `codex exec resume` rejects (the original session's policy is
241
240
  * inherited). Callers must drop these when building resume argv.
242
241
  *
243
- * Verified against `codex exec resume --help` (codex-cli 0.133.0):
244
- * `--full-auto`, `--sandbox`, `--ask-for-approval`, `--add-dir`, `-C`, and
245
- * `--search` are rejected. `--output-schema` and `-c key=value` ARE accepted
246
- * on resume and therefore are NOT in this filter (Phase 4 slice α restored
247
- * the previously-silent drop of those two).
242
+ * Verified against `codex exec resume --help` (codex-cli 0.135.0):
243
+ * `--sandbox`, `--add-dir`, `-C`, `--cd`, `--profile`, and `--search` are rejected.
244
+ * Deprecated `--full-auto` / `--ask-for-approval` are kept here defensively so
245
+ * legacy pre-filtered segments are stripped instead of reaching spawn.
246
+ * `--output-schema` and `-c key=value` ARE accepted on resume and therefore are
247
+ * NOT in this filter (Phase 4 slice α restored the previously-silent drop of those two).
248
248
  */
249
249
  export declare const CODEX_RESUME_FILTERED_FLAGS: ReadonlySet<string>;
250
250
  /**
251
251
  * Strip resume-incompatible flag/value pairs from a Codex argv segment.
252
252
  *
253
253
  * Bare flags (`--full-auto`, `--search`) drop without consuming a value.
254
- * Value-taking flags (`--sandbox`, `--ask-for-approval`, `--add-dir`, `-C`,
255
- * `--output-schema`) drop together with their immediately-following value.
254
+ * Value-taking flags (`--sandbox`, `--ask-for-approval`, `--add-dir`, `-C`, `--cd`,
255
+ * `--profile`) drop together with their immediately-following value.
256
256
  */
257
257
  export declare function filterCodexResumeFlags(args: string[]): string[];
258
258
  /**
@@ -492,6 +492,8 @@ export interface CodexHighImpactFlagsResult {
492
492
  cleanup: () => void;
493
493
  /** First missing image path, if any. When set, the caller should bail before spawning. */
494
494
  missingImagePath: string | null;
495
+ /** Set when deprecated/no-op compatibility inputs are supplied. */
496
+ warning?: string;
495
497
  }
496
498
  /**
497
499
  * Build the U26 argv segment AND any required side-effect handles.