nexus-agents 8.112.0 → 8.112.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.
Files changed (140) hide show
  1. package/dist/{ast-rule-runner-Y7JYRYIJ.js → ast-rule-runner-UEQO7V4Y.js} +3 -3
  2. package/dist/{child-mcp-config-T5ACQOUC.js → child-mcp-config-BYNNNVZF.js} +3 -12
  3. package/dist/child-mcp-config-BYNNNVZF.js.map +1 -0
  4. package/dist/{chunk-7MDXMFP7.js → chunk-27E5O3GY.js} +2 -2
  5. package/dist/{chunk-WO2QMQUG.js → chunk-3E4J3EE6.js} +121 -84
  6. package/dist/chunk-3E4J3EE6.js.map +1 -0
  7. package/dist/{chunk-HWFC5ASC.js → chunk-3SY2GPU3.js} +2 -2
  8. package/dist/{chunk-2IIJQIMM.js → chunk-3TB6L3TE.js} +109 -39
  9. package/dist/chunk-3TB6L3TE.js.map +1 -0
  10. package/dist/{chunk-5U5MRUIO.js → chunk-5GMUGQGF.js} +4 -4
  11. package/dist/{chunk-THB3IYJX.js → chunk-5VS577SD.js} +2 -2
  12. package/dist/{chunk-H4NYL266.js → chunk-6FC5VVDA.js} +2 -2
  13. package/dist/{chunk-JSHF2KFP.js → chunk-6RYPMOR6.js} +8 -8
  14. package/dist/{chunk-EBWQVNA2.js → chunk-76XX7NPA.js} +2 -2
  15. package/dist/{chunk-U5K4FVMI.js → chunk-7JKUA5X6.js} +4 -4
  16. package/dist/{chunk-PQLW4ZXT.js → chunk-CD2O5QRG.js} +11 -11
  17. package/dist/{chunk-GG3UPYQC.js → chunk-CZ7EPWIJ.js} +2 -2
  18. package/dist/{chunk-YFNDV7FJ.js → chunk-DARVXIKJ.js} +2 -2
  19. package/dist/{chunk-XIUCWV2L.js → chunk-DAWGZNLK.js} +2 -2
  20. package/dist/{chunk-CPZ6OYZV.js → chunk-DAYX7FLQ.js} +3 -3
  21. package/dist/{chunk-OYV7SZGV.js → chunk-EIDH64P4.js} +5 -5
  22. package/dist/{chunk-K42UETAL.js → chunk-GFNUYOCY.js} +2 -2
  23. package/dist/{chunk-7OID2D3X.js → chunk-HEBQA4E4.js} +2 -2
  24. package/dist/{chunk-QIRFVULH.js → chunk-I2FZPV7P.js} +2 -2
  25. package/dist/{chunk-ZNBVTWQX.js → chunk-JLOW2KAD.js} +5 -5
  26. package/dist/{chunk-6UI734G5.js → chunk-KUB3MGWF.js} +27 -7
  27. package/dist/chunk-KUB3MGWF.js.map +1 -0
  28. package/dist/{chunk-AXTUSOO2.js → chunk-LLG4XR6P.js} +5 -5
  29. package/dist/{chunk-A6MJ5OT5.js → chunk-LYZ2OXI7.js} +2 -2
  30. package/dist/{chunk-4YGPA6RY.js → chunk-M72V2I3F.js} +2 -2
  31. package/dist/{chunk-22ORLI5U.js → chunk-N67DGBPZ.js} +2 -2
  32. package/dist/{chunk-DVGHZMUL.js → chunk-NL2IL32T.js} +43 -43
  33. package/dist/{chunk-DVGHZMUL.js.map → chunk-NL2IL32T.js.map} +1 -1
  34. package/dist/{chunk-LOUQYL7L.js → chunk-NXCO6Z7O.js} +3 -3
  35. package/dist/{chunk-56BNVEYR.js → chunk-P7ZL3KD6.js} +2 -2
  36. package/dist/{chunk-BD7DK7WH.js → chunk-QDDTFVW7.js} +2 -2
  37. package/dist/{chunk-O7JOVLLY.js → chunk-RLSKNDO5.js} +4 -4
  38. package/dist/{chunk-JEAVQI3X.js → chunk-TTPAWUHT.js} +2 -2
  39. package/dist/{chunk-2T6Q3JTZ.js → chunk-X3VNURAA.js} +3 -3
  40. package/dist/{chunk-FDASKOC7.js → chunk-Y4ZLHASZ.js} +11 -3
  41. package/dist/chunk-Y4ZLHASZ.js.map +1 -0
  42. package/dist/{chunk-JKAJK3GA.js → chunk-YBRWJBHN.js} +2 -2
  43. package/dist/{chunk-TSDOTP53.js → chunk-YZMSYPAU.js} +5 -5
  44. package/dist/{chunk-VOKK274H.js → chunk-ZWXZY3Z7.js} +2 -2
  45. package/dist/{cli-circuit-breaker-WIMKRL7H.js → cli-circuit-breaker-KR72NLMQ.js} +3 -3
  46. package/dist/cli.js +53 -49
  47. package/dist/cli.js.map +1 -1
  48. package/dist/{composite-router-JDU5RLVX.js → composite-router-OEN6DCPK.js} +2 -2
  49. package/dist/{consensus-vote-X5QSYJRA.js → consensus-vote-VD74QW5N.js} +18 -18
  50. package/dist/{context-retriever-UZKQCWZY.js → context-retriever-7VBZZG3I.js} +8 -8
  51. package/dist/{doctor-deep-TMRXDBKM.js → doctor-deep-JPICT7QB.js} +3 -3
  52. package/dist/doctor-gateway-report-MZY47ZZV.js +17 -0
  53. package/dist/{doctor-live-6HWHQ2WN.js → doctor-live-AF4XP5WY.js} +11 -11
  54. package/dist/{expert-bridge-HPMKPJFI.js → expert-bridge-MPMDMGCC.js} +5 -5
  55. package/dist/{factory-PYFGNN5R.js → factory-2Z7GFPKY.js} +5 -5
  56. package/dist/{factory-QHGZKNBZ.js → factory-ZGYUWL2O.js} +10 -10
  57. package/dist/{improvement-review-2RVAMHCY.js → improvement-review-UA6OJTZ6.js} +6 -6
  58. package/dist/index.d.ts +7 -7
  59. package/dist/index.js +33 -33
  60. package/dist/{init-opencode-DO36MEWH.js → init-opencode-FYYFCXHB.js} +6 -6
  61. package/dist/{issue-triage-I2FL44CG.js → issue-triage-FOOCSNNK.js} +6 -6
  62. package/dist/{openai-compat-adapter-3Q7FLYBM.js → openai-compat-adapter-YAWAN7MX.js} +6 -6
  63. package/dist/{pr-reviewer-helpers-EHQ4Z7L6.js → pr-reviewer-helpers-J737NDAT.js} +4 -4
  64. package/dist/{registry-command-MP57K2IU.js → registry-command-OQ4SLR6K.js} +2 -2
  65. package/dist/{repo-security-plan-EW2Z4UYX.js → repo-security-plan-ZNKV7LPR.js} +3 -3
  66. package/dist/{research-helpers-synthesize-2ORWR7IO.js → research-helpers-synthesize-7RBYSNPH.js} +4 -4
  67. package/dist/{routing-memory-7YDZMCBP.js → routing-memory-AFLU42CD.js} +2 -2
  68. package/dist/{session-memory-YWUVRCF7.js → session-memory-22AVU7MG.js} +3 -3
  69. package/dist/{setup-config-56WKB7QE.js → setup-config-KVH4TQN7.js} +3 -3
  70. package/dist/{setup-custom-api-TO2GWNSV.js → setup-custom-api-2ZHJT7BL.js} +4 -4
  71. package/dist/setup-data-dir-YZ3VVOT2.js +34 -0
  72. package/dist/{tool-memory-2JWYUQPL.js → tool-memory-WSCOQQS2.js} +5 -5
  73. package/dist/{unified-registry-QCVJ5M2A.js → unified-registry-RBENDRS4.js} +12 -12
  74. package/dist/{weather-report-ERRSDDVB.js → weather-report-EGG35WEB.js} +2 -2
  75. package/package.json +1 -1
  76. package/dist/child-mcp-config-T5ACQOUC.js.map +0 -1
  77. package/dist/chunk-2IIJQIMM.js.map +0 -1
  78. package/dist/chunk-6UI734G5.js.map +0 -1
  79. package/dist/chunk-FDASKOC7.js.map +0 -1
  80. package/dist/chunk-WO2QMQUG.js.map +0 -1
  81. package/dist/doctor-gateway-report-IH5SXTJS.js +0 -17
  82. package/dist/setup-data-dir-4ONA7QX7.js +0 -34
  83. /package/dist/{ast-rule-runner-Y7JYRYIJ.js.map → ast-rule-runner-UEQO7V4Y.js.map} +0 -0
  84. /package/dist/{chunk-7MDXMFP7.js.map → chunk-27E5O3GY.js.map} +0 -0
  85. /package/dist/{chunk-HWFC5ASC.js.map → chunk-3SY2GPU3.js.map} +0 -0
  86. /package/dist/{chunk-5U5MRUIO.js.map → chunk-5GMUGQGF.js.map} +0 -0
  87. /package/dist/{chunk-THB3IYJX.js.map → chunk-5VS577SD.js.map} +0 -0
  88. /package/dist/{chunk-H4NYL266.js.map → chunk-6FC5VVDA.js.map} +0 -0
  89. /package/dist/{chunk-JSHF2KFP.js.map → chunk-6RYPMOR6.js.map} +0 -0
  90. /package/dist/{chunk-EBWQVNA2.js.map → chunk-76XX7NPA.js.map} +0 -0
  91. /package/dist/{chunk-U5K4FVMI.js.map → chunk-7JKUA5X6.js.map} +0 -0
  92. /package/dist/{chunk-PQLW4ZXT.js.map → chunk-CD2O5QRG.js.map} +0 -0
  93. /package/dist/{chunk-GG3UPYQC.js.map → chunk-CZ7EPWIJ.js.map} +0 -0
  94. /package/dist/{chunk-YFNDV7FJ.js.map → chunk-DARVXIKJ.js.map} +0 -0
  95. /package/dist/{chunk-XIUCWV2L.js.map → chunk-DAWGZNLK.js.map} +0 -0
  96. /package/dist/{chunk-CPZ6OYZV.js.map → chunk-DAYX7FLQ.js.map} +0 -0
  97. /package/dist/{chunk-OYV7SZGV.js.map → chunk-EIDH64P4.js.map} +0 -0
  98. /package/dist/{chunk-K42UETAL.js.map → chunk-GFNUYOCY.js.map} +0 -0
  99. /package/dist/{chunk-7OID2D3X.js.map → chunk-HEBQA4E4.js.map} +0 -0
  100. /package/dist/{chunk-QIRFVULH.js.map → chunk-I2FZPV7P.js.map} +0 -0
  101. /package/dist/{chunk-ZNBVTWQX.js.map → chunk-JLOW2KAD.js.map} +0 -0
  102. /package/dist/{chunk-AXTUSOO2.js.map → chunk-LLG4XR6P.js.map} +0 -0
  103. /package/dist/{chunk-A6MJ5OT5.js.map → chunk-LYZ2OXI7.js.map} +0 -0
  104. /package/dist/{chunk-4YGPA6RY.js.map → chunk-M72V2I3F.js.map} +0 -0
  105. /package/dist/{chunk-22ORLI5U.js.map → chunk-N67DGBPZ.js.map} +0 -0
  106. /package/dist/{chunk-LOUQYL7L.js.map → chunk-NXCO6Z7O.js.map} +0 -0
  107. /package/dist/{chunk-56BNVEYR.js.map → chunk-P7ZL3KD6.js.map} +0 -0
  108. /package/dist/{chunk-BD7DK7WH.js.map → chunk-QDDTFVW7.js.map} +0 -0
  109. /package/dist/{chunk-O7JOVLLY.js.map → chunk-RLSKNDO5.js.map} +0 -0
  110. /package/dist/{chunk-JEAVQI3X.js.map → chunk-TTPAWUHT.js.map} +0 -0
  111. /package/dist/{chunk-2T6Q3JTZ.js.map → chunk-X3VNURAA.js.map} +0 -0
  112. /package/dist/{chunk-JKAJK3GA.js.map → chunk-YBRWJBHN.js.map} +0 -0
  113. /package/dist/{chunk-TSDOTP53.js.map → chunk-YZMSYPAU.js.map} +0 -0
  114. /package/dist/{chunk-VOKK274H.js.map → chunk-ZWXZY3Z7.js.map} +0 -0
  115. /package/dist/{cli-circuit-breaker-WIMKRL7H.js.map → cli-circuit-breaker-KR72NLMQ.js.map} +0 -0
  116. /package/dist/{composite-router-JDU5RLVX.js.map → composite-router-OEN6DCPK.js.map} +0 -0
  117. /package/dist/{consensus-vote-X5QSYJRA.js.map → consensus-vote-VD74QW5N.js.map} +0 -0
  118. /package/dist/{context-retriever-UZKQCWZY.js.map → context-retriever-7VBZZG3I.js.map} +0 -0
  119. /package/dist/{doctor-deep-TMRXDBKM.js.map → doctor-deep-JPICT7QB.js.map} +0 -0
  120. /package/dist/{doctor-gateway-report-IH5SXTJS.js.map → doctor-gateway-report-MZY47ZZV.js.map} +0 -0
  121. /package/dist/{doctor-live-6HWHQ2WN.js.map → doctor-live-AF4XP5WY.js.map} +0 -0
  122. /package/dist/{expert-bridge-HPMKPJFI.js.map → expert-bridge-MPMDMGCC.js.map} +0 -0
  123. /package/dist/{factory-PYFGNN5R.js.map → factory-2Z7GFPKY.js.map} +0 -0
  124. /package/dist/{factory-QHGZKNBZ.js.map → factory-ZGYUWL2O.js.map} +0 -0
  125. /package/dist/{improvement-review-2RVAMHCY.js.map → improvement-review-UA6OJTZ6.js.map} +0 -0
  126. /package/dist/{init-opencode-DO36MEWH.js.map → init-opencode-FYYFCXHB.js.map} +0 -0
  127. /package/dist/{issue-triage-I2FL44CG.js.map → issue-triage-FOOCSNNK.js.map} +0 -0
  128. /package/dist/{openai-compat-adapter-3Q7FLYBM.js.map → openai-compat-adapter-YAWAN7MX.js.map} +0 -0
  129. /package/dist/{pr-reviewer-helpers-EHQ4Z7L6.js.map → pr-reviewer-helpers-J737NDAT.js.map} +0 -0
  130. /package/dist/{registry-command-MP57K2IU.js.map → registry-command-OQ4SLR6K.js.map} +0 -0
  131. /package/dist/{repo-security-plan-EW2Z4UYX.js.map → repo-security-plan-ZNKV7LPR.js.map} +0 -0
  132. /package/dist/{research-helpers-synthesize-2ORWR7IO.js.map → research-helpers-synthesize-7RBYSNPH.js.map} +0 -0
  133. /package/dist/{routing-memory-7YDZMCBP.js.map → routing-memory-AFLU42CD.js.map} +0 -0
  134. /package/dist/{session-memory-YWUVRCF7.js.map → session-memory-22AVU7MG.js.map} +0 -0
  135. /package/dist/{setup-config-56WKB7QE.js.map → setup-config-KVH4TQN7.js.map} +0 -0
  136. /package/dist/{setup-custom-api-TO2GWNSV.js.map → setup-custom-api-2ZHJT7BL.js.map} +0 -0
  137. /package/dist/{setup-data-dir-4ONA7QX7.js.map → setup-data-dir-YZ3VVOT2.js.map} +0 -0
  138. /package/dist/{tool-memory-2JWYUQPL.js.map → tool-memory-WSCOQQS2.js.map} +0 -0
  139. /package/dist/{unified-registry-QCVJ5M2A.js.map → unified-registry-RBENDRS4.js.map} +0 -0
  140. /package/dist/{weather-report-ERRSDDVB.js.map → weather-report-EGG35WEB.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli-adapters/cli-to-model-adapter.ts","../src/adapters/claude-adapter.ts","../src/adapters/claude-adapter-types.ts","../src/adapters/claude-adapter-helpers.ts","../src/adapters/prompt-utils.ts","../src/adapters/sdk/sdk-adapter.ts","../src/cli-adapters/cli-detection-cache.ts","../src/adapters/auto-adapter.ts","../src/cli-adapters/subprocess-adapter.ts","../src/cli-adapters/base-adapter.ts","../src/cli-adapters/cli-timeout-helpers.ts","../src/cli-adapters/cli-timeout-profiles.ts","../src/utils/async-utils.ts","../src/cli-adapters/cli-retry-loop.ts","../src/cli-adapters/subprocess-env.ts","../src/cli-adapters/process-tree-kill.ts","../src/cli-adapters/proc-start-time.ts","../src/utils/type-coercion.ts","../src/cli-adapters/parsers/claude-parser.ts","../src/config/models-dev-by-vendor.ts","../src/cli-adapters/adapters/claude-adapter.ts","../src/cli-adapters/parsers/agy-parser.ts","../src/config/agy-model-map.ts","../src/cli-adapters/adapters/gemini-adapter-helpers.ts","../src/cli-adapters/adapters/gemini-adapter.ts","../src/cli-adapters/adapters/codex-adapter.ts","../src/cli-adapters/parsers/codex-parser.ts","../src/cli-adapters/adapters/codex-adapter-helpers.ts","../src/cli-adapters/adapters/codex-mcp-adapter.ts","../src/cli-adapters/adapters/codex-mcp-adapter-helpers.ts","../src/cli-adapters/adapters/opencode-adapter.ts","../src/cli-adapters/parsers/opencode-parser.ts","../src/cli/cli-auth-probe.ts","../src/cli-adapters/cli-admission.ts","../src/cli-adapters/codex-mcp-server-probe.ts","../src/cli-adapters/factory.ts"],"sourcesContent":["/**\n * CLI-to-Model Adapter Bridge\n *\n * Wraps an ICliAdapter to implement IModelAdapter, enabling CLI tools\n * (claude, gemini, codex) to be used where model adapters are expected.\n *\n * @module cli-adapters/cli-to-model-adapter\n */\n\nimport type {\n Result,\n IModelAdapter,\n CompletionRequest,\n CompletionResponse,\n ModelCapability,\n} from '../core/index.js';\nimport {\n ModelCapability as MC,\n ok,\n err,\n ModelError,\n ConfigError,\n ErrorCode,\n createLogger,\n} from '../core/index.js';\nimport { estimateTokens } from '../core/token-estimator.js';\nimport type { ICliAdapter, CliTask, CliResponse, CliError, ExecutionOptions } from './types.js';\nimport { readOnlyAnalysisRefusal } from './read-only-analysis.js';\nimport type { StreamChunk } from '../core/types/model.js';\nimport { toModelTokenUsage } from './token-usage-bridge.js';\nimport { isCallerInputCliError } from './cli-error-helpers.js';\nimport { findCanonicalModel } from '../config/model-config-helpers.js';\nimport { CLI_NAMES } from '../config/model-capabilities-types.js';\n\nconst logger = createLogger({ component: 'cli-to-model-adapter' });\n\n/** Configuration for CliToModelAdapter. */\nexport interface CliToModelAdapterConfig {\n /** Default timeout for CLI calls (ms). Overrides auto-detection. */\n readonly defaultTimeoutMs?: number;\n}\n\n/**\n * Bridge adapter that wraps ICliAdapter to implement IModelAdapter.\n *\n * This enables using CLI tools (claude, gemini, codex) in contexts\n * that expect IModelAdapter.\n *\n * @example\n * ```typescript\n * const cliAdapter = createCliAdapter({ cli: 'claude' });\n * const modelAdapter = new CliToModelAdapter(cliAdapter);\n *\n * // Now use modelAdapter where IModelAdapter is expected\n * const result = await modelAdapter.complete({\n * messages: [{ role: 'user', content: 'Hello!' }],\n * });\n * ```\n */\nexport class CliToModelAdapter implements IModelAdapter {\n readonly providerId: string;\n readonly modelId: string;\n readonly capabilities: readonly ModelCapability[];\n\n private readonly cliAdapter: ICliAdapter;\n private readonly defaultTimeoutMs: number | undefined;\n\n /**\n * Creates a bridge from CLI adapter to model adapter.\n *\n * @param cliAdapter - The CLI adapter to wrap\n * @param config - Optional configuration (e.g. timeout override)\n */\n constructor(cliAdapter: ICliAdapter, config?: CliToModelAdapterConfig) {\n this.cliAdapter = cliAdapter;\n this.defaultTimeoutMs = config?.defaultTimeoutMs;\n this.providerId = `cli-${cliAdapter.name}`;\n this.modelId = cliAdapter.getModelInfo().id;\n this.capabilities = this.deriveCapabilities();\n }\n\n /**\n * Derives ModelCapability from CLI capabilities.\n */\n private deriveCapabilities(): readonly ModelCapability[] {\n const caps: ModelCapability[] = [MC.COMPLETION, MC.TOOL_USE];\n\n // Claude CLI has extended thinking capability\n if (this.cliAdapter.name === 'claude') {\n caps.push(MC.EXTENDED_THINKING);\n }\n\n return caps;\n }\n\n /**\n * Converts CompletionRequest to CliTask.\n */\n private toCliTask(request: CompletionRequest): CliTask {\n // Build content from messages\n const content = request.messages\n .map((msg) => {\n const text =\n typeof msg.content === 'string'\n ? msg.content\n : msg.content\n .filter((b): b is { type: 'text'; text: string } => b.type === 'text')\n .map((b) => b.text)\n .join('\\n');\n return `[${msg.role}]: ${text}`;\n })\n .join('\\n\\n');\n\n // Build task with conditional optional properties\n const task: CliTask = {\n content,\n ...(request.workDir !== undefined ? { options: { workDir: request.workDir } } : {}),\n ...(request.accessMode !== undefined ? { accessMode: request.accessMode } : {}),\n };\n\n if (request.systemPrompt !== undefined) {\n (task as { systemPrompt: string }).systemPrompt = request.systemPrompt;\n }\n if (request.maxTokens !== undefined) {\n (task as { maxTokens: number }).maxTokens = request.maxTokens;\n }\n const model = this.forwardableModel(request.model);\n if (model !== undefined) {\n (task as { model: string }).model = model;\n }\n\n return task;\n }\n\n /**\n * The requested model, when this CLI may be handed it (#6599). A registry\n * model that belongs ONLY to other CLIs is withheld: a failover can land a\n * model-bound request on a different CLI, which cannot run it and then runs\n * its own default — logged, and the response reports the model that ran.\n * A name the registry does not know is forwarded; the CLI adapter resolves\n * it or returns an error.\n */\n private forwardableModel(model: string | undefined): string | undefined {\n if (model === undefined) return undefined;\n const owners = CLI_NAMES.filter((cli) => findCanonicalModel(cli, model) !== undefined);\n if (owners.length === 0 || (owners as readonly string[]).includes(this.cliAdapter.name)) {\n return model;\n }\n logger.warn('Requested model belongs to another CLI; this CLI runs its default', {\n model,\n cli: this.cliAdapter.name,\n owners,\n });\n return undefined;\n }\n\n /**\n * Converts CliResponse to CompletionResponse.\n */\n private toCompletionResponse(response: CliResponse, forwarded?: string): CompletionResponse {\n const u = response.usage;\n return {\n content: [{ type: 'text', text: response.text }],\n // #4439: this used to be `response.usage?.x ?? 0`, which turned \"the CLI\n // reported nothing\" into a present 0/0/0 — indistinguishable downstream\n // from a real zero-token call. That single coercion defeated the\n // measured-voter gate (#4436) on every live vote and dropped the cache\n // fields (#4438) that #4435 needs. Absence stays absent; a present\n // usage crosses the type boundary through the one conversion (#4440).\n ...(u !== undefined ? { usage: toModelTokenUsage(u) } : {}),\n stopReason: 'end_turn',\n // #6599: no CLI parser reports the model, so a forwarded model is the\n // one that ran — report its canonical id, or cost and outcomes are\n // attributed to the CLI default.\n model: response.model ?? this.reportedForwardedModel(forwarded) ?? this.modelId,\n // #6094: carry the transport's captured stderr up to the model boundary\n // so the voter path can read the structured \"could not read\" signal.\n // Absent stays absent; an empty string is not a signal.\n ...(response.stderr !== undefined && response.stderr !== ''\n ? { cliStderr: response.stderr }\n : {}),\n // #6120/#6115: an in-family model substitution rides up the same way, so\n // the seat can say which alias it asked for and which one answered.\n ...(response.fallbackFrom !== undefined ? { fallbackFrom: response.fallbackFrom } : {}),\n };\n }\n\n /**\n * Converts CliError to ModelError.\n */\n private toModelError(cliError: CliError): ModelError {\n const options = cliError.cause !== undefined ? { cause: cliError.cause } : {};\n // #6599: caller input (e.g. an unresolvable requested model) keeps its\n // identity across the bridge, so the breaker can decline to count it.\n const code = isCallerInputCliError(cliError) ? { code: ErrorCode.INVALID_INPUT } : {};\n return new ModelError(cliError.message, { ...options, ...code });\n }\n\n /**\n * The canonical registry id of a model forwarded to this CLI, when the\n * registry lists it under this CLI. A name the registry does not know is not\n * claimed: the CLI may have substituted its default (agy does), so the\n * adapter default stays the honest report.\n */\n private reportedForwardedModel(forwarded: string | undefined): string | undefined {\n if (forwarded === undefined) return undefined;\n return findCanonicalModel(this.cliAdapter.name, forwarded)?.id;\n }\n\n /**\n * Send a completion request via CLI.\n */\n async complete(request: CompletionRequest): Promise<Result<CompletionResponse, ModelError>> {\n const task = this.toCliTask(request);\n // #6754: fail closed for an adapter that does not declare read-only\n // enforcement, whatever its own execute would have done with the field.\n const refusal = readOnlyAnalysisRefusal(this.cliAdapter, task);\n if (refusal !== undefined) return err(this.toModelError(refusal));\n // Per-request timeout (#3304) takes precedence over the construction-time\n // default, so a long-budget caller (e.g. a consensus vote) isn't cut off by\n // the adapter's shorter standard CLI timeout.\n const effectiveTimeoutMs = request.timeoutMs ?? this.defaultTimeoutMs;\n // #6680: the caller's signal reaches the CLI adapter, which kills its\n // subprocess on abort. It used to stop here, so neither a watchdog timeout\n // nor `cancel_job` could end a running CLI call.\n const opts: ExecutionOptions | undefined =\n effectiveTimeoutMs !== undefined || request.signal !== undefined\n ? {\n ...(effectiveTimeoutMs !== undefined ? { timeoutMs: effectiveTimeoutMs } : {}),\n ...(request.signal !== undefined ? { signal: request.signal } : {}),\n }\n : undefined;\n const result = await this.cliAdapter.execute(task, opts);\n\n if (!result.ok) {\n return err(this.toModelError(result.error));\n }\n\n return ok(this.toCompletionResponse(result.value, task.model));\n }\n\n /**\n * Streaming is not supported via CLI adapters.\n * Falls back to non-streaming and yields single chunk.\n */\n async *stream(request: CompletionRequest): AsyncIterable<StreamChunk> {\n const result = await this.complete(request);\n\n if (!result.ok) {\n throw result.error;\n }\n\n const response = result.value;\n\n yield { type: 'message_start', message: { model: response.model } };\n\n yield {\n type: 'content_block_start',\n index: 0,\n contentBlock: response.content[0] ?? { type: 'text', text: '' },\n };\n\n const firstBlock = response.content[0];\n const text = firstBlock?.type === 'text' ? firstBlock.text : '';\n yield { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text } };\n\n yield { type: 'content_block_stop', index: 0 };\n\n yield {\n type: 'message_delta',\n delta: { stop_reason: response.stopReason },\n // exactOptionalPropertyTypes: omit the key entirely when unknown rather\n // than passing an explicit undefined (#4439).\n ...(response.usage !== undefined ? { usage: response.usage } : {}),\n };\n\n yield { type: 'message_stop' };\n }\n\n /**\n * Token count via canonical estimator (DRY consolidation Issue #1596).\n */\n countTokens(text: string): Promise<number> {\n return Promise.resolve(estimateTokens(text));\n }\n\n /**\n * Validate configuration by running health check.\n */\n validateConfig(): Result<void, ConfigError> {\n // CLI adapters validate via health check\n return ok(undefined);\n }\n\n /**\n * Initialize the underlying CLI adapter.\n */\n async initialize(): Promise<void> {\n await this.cliAdapter.initialize();\n }\n\n /**\n * Dispose the underlying CLI adapter.\n */\n async dispose(): Promise<void> {\n await this.cliAdapter.dispose();\n }\n}\n\n/**\n * Creates a model adapter from a CLI adapter.\n *\n * @param cliAdapter - The CLI adapter to wrap\n * @returns IModelAdapter implementation\n */\nexport function createCliToModelAdapter(\n cliAdapter: ICliAdapter,\n config?: CliToModelAdapterConfig\n): CliToModelAdapter {\n return new CliToModelAdapter(cliAdapter, config);\n}\n","/**\n * nexus-agents/adapters - Claude/Anthropic Model Adapter\n *\n * Adapter for Anthropic's Claude models (claude-opus-4, claude-sonnet-4, claude-haiku-3).\n * Implements the IModelAdapter interface with streaming support, rate limiting,\n * and proper error handling.\n *\n * Verified 2026-01-03: @anthropic-ai/sdk@0.71.2 is current stable\n * (Source: npm registry)\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\nimport type { MessageStreamEvent } from '@anthropic-ai/sdk/resources/messages';\nimport type {\n Result,\n CompletionRequest,\n CompletionResponse,\n ModelMetadata,\n StreamChunk,\n ContentBlock,\n TokenUsage,\n} from '../core/index.js';\nimport { ok, err, ModelError, ConfigError, getTokenEstimator } from '../core/index.js';\nimport {\n BaseAdapter,\n type BaseAdapterConfig,\n requireApiKey,\n validateApiKeyPresence,\n} from './base-adapter.js';\nimport { createStream } from './streaming.js';\nimport type { ClaudeAdapterConfig } from './claude-adapter-types.js';\nimport { DEFAULT_MAX_TOKENS } from './claude-adapter-types.js';\nimport {\n mapStopReason,\n mapContentBlock,\n mapMessage,\n mapTool,\n resolveModelId,\n getModelCapabilities,\n forcesResponseTool,\n buildRespondTool,\n mapResponseWithRespondTool,\n RESPOND_TOOL_NAME,\n} from './claude-adapter-helpers.js';\nimport { extractRequestSystemPrompt } from './prompt-utils.js';\nimport { planOptionalParams, type DroppedParam } from './optional-params.js';\n\n// Re-export types and constants for backward compatibility\nexport type { ClaudeAdapterConfig } from './claude-adapter-types.js';\nexport { CLAUDE_MODELS, CLAUDE_MODEL_ALIASES } from './claude-adapter-types.js';\n\n/**\n * Claude/Anthropic model adapter.\n *\n * Provides a unified interface for interacting with Anthropic's Claude models.\n * Supports completion, streaming, tool use, and vision capabilities.\n *\n * @example\n * ```typescript\n * const adapter = new ClaudeAdapter({\n * modelId: 'claude-sonnet-4',\n * apiKey: process.env.ANTHROPIC_API_KEY,\n * });\n *\n * const result = await adapter.complete({\n * messages: [{ role: 'user', content: 'Hello!' }],\n * maxTokens: 1024,\n * });\n *\n * if (result.ok) {\n * console.log(result.value.content);\n * }\n * ```\n */\nexport class ClaudeAdapter extends BaseAdapter {\n private readonly client: Anthropic;\n private readonly resolvedModelId: string;\n\n /**\n * Creates a new ClaudeAdapter instance.\n *\n * @param config - Claude adapter configuration\n * @throws {ConfigError} If API key is missing\n */\n constructor(config: ClaudeAdapterConfig) {\n const resolvedModelId = resolveModelId(config.modelId);\n\n // Build baseConfig conditionally to satisfy exactOptionalPropertyTypes\n const baseConfig: BaseAdapterConfig = {\n providerId: 'anthropic',\n modelId: resolvedModelId,\n capabilities: getModelCapabilities(config.modelId),\n apiKey: config.apiKey,\n };\n\n // Only set optional properties if defined\n if (config.baseUrl !== undefined) {\n baseConfig.baseUrl = config.baseUrl;\n }\n if (config.timeout !== undefined) {\n baseConfig.timeout = config.timeout;\n }\n if (config.maxRetries !== undefined) {\n baseConfig.maxRetries = config.maxRetries;\n }\n\n super(baseConfig);\n\n this.resolvedModelId = resolvedModelId;\n\n // Validate API key presence\n requireApiKey(config.apiKey, 'Anthropic', config.modelId);\n\n // Create Anthropic client\n this.client = new Anthropic({\n apiKey: config.apiKey,\n baseURL: config.baseUrl,\n timeout: config.timeout,\n maxRetries: config.maxRetries ?? 2,\n });\n }\n\n /**\n * Validates adapter configuration.\n * Extends base validation with Claude-specific checks.\n */\n override validateConfig(): Result<void, ConfigError> {\n const baseResult = super.validateConfig();\n if (!baseResult.ok) {\n return baseResult;\n }\n\n // Validate API key is present\n const keyResult = validateApiKeyPresence(this.config.apiKey, this.providerId, this.modelId);\n if (!keyResult.ok) return keyResult;\n\n return ok(undefined);\n }\n\n /**\n * Send a completion request to Claude.\n *\n * @param request - The completion request\n * @returns Result with response or ModelError\n */\n async complete(request: CompletionRequest): Promise<Result<CompletionResponse, ModelError>> {\n this.logRequest(request);\n\n try {\n const response = await this.executeCompletion(request);\n this.logResponse(response);\n return ok(response);\n } catch (error) {\n return err(this.transformError(error));\n }\n }\n\n /**\n * Stream a completion request from Claude.\n *\n * @param request - The completion request\n * @yields StreamChunk objects as they arrive\n */\n async *stream(request: CompletionRequest): AsyncIterable<StreamChunk> {\n this.logRequest(request);\n\n const [controller, iterable] = createStream<StreamChunk>();\n\n // Start streaming in the background\n this.executeStream(request, controller).catch((error: unknown) => {\n const modelError = this.transformError(error);\n controller.error(modelError);\n });\n\n yield* iterable;\n }\n\n /**\n * Count tokens in text using Claude-specific estimation.\n *\n * Claude uses a custom tokenizer. This provides a more accurate estimate\n * than the base adapter's generic calculation.\n *\n * @param text - Text to count tokens for\n * @returns Approximate token count\n */\n override countTokens(text: string): Promise<number> {\n // Use unified TokenEstimator with Claude-specific ratio (~3.5 chars/token)\n return Promise.resolve(getTokenEstimator().estimateText(text, 'claude'));\n }\n\n /**\n * Executes the completion request against the Anthropic API.\n */\n private async executeCompletion(request: CompletionRequest): Promise<CompletionResponse> {\n const { params, dropped } = this.buildRequestParams(request);\n // #3036: forward AbortSignal into the SDK so withWatchdog timeouts\n // actually cancel the in-flight HTTP request instead of leaking it\n // past the Promise.race boundary. Branch on signal presence — vitest\n // 4 `toHaveBeenCalledWith(params)` treats `(params, undefined)` as\n // a distinct call shape from `(params)`, so passing the second arg\n // unconditionally would break every callsite-shape assertion.\n const response =\n request.signal !== undefined\n ? await this.client.messages.create(params, { signal: request.signal })\n : await this.client.messages.create(params);\n\n // #3433: when we forced a `respond` tool to satisfy a non-text\n // responseFormat, surface its structured input as a text block so the\n // caller's JSON parser keeps working unchanged.\n // #4069: surface params dropped by the seam (e.g. temperature) as warnings.\n return this.mapResponse(response, forcesResponseTool(request), dropped);\n }\n\n /**\n * Executes streaming completion and pushes chunks to the controller.\n */\n private async executeStream(\n request: CompletionRequest,\n controller: {\n push: (chunk: StreamChunk) => Result<void, Error>;\n complete: () => void;\n error: (error: Error) => void;\n }\n ): Promise<void> {\n try {\n const { params } = this.buildRequestParams(request);\n const stream = this.client.messages.stream(params);\n\n for await (const event of stream) {\n const chunk = this.mapStreamEvent(event);\n if (chunk) {\n controller.push(chunk);\n }\n }\n\n controller.complete();\n } catch (error) {\n const modelError = this.transformError(error);\n controller.error(modelError);\n }\n }\n\n /**\n * Builds Anthropic API request parameters from our CompletionRequest.\n */\n private buildRequestParams(request: CompletionRequest): {\n params: Anthropic.MessageCreateParamsNonStreaming;\n dropped: readonly DroppedParam[];\n } {\n // Filter out system messages and map the rest\n const messages = request.messages.filter((m) => m.role !== 'system').map(mapMessage);\n\n const params: Anthropic.MessageCreateParamsNonStreaming = {\n model: this.resolvedModelId,\n messages,\n max_tokens: request.maxTokens ?? DEFAULT_MAX_TOKENS,\n };\n\n // Add system prompt if provided\n const systemPrompt = extractRequestSystemPrompt(request);\n if (systemPrompt !== undefined) {\n params.system = systemPrompt;\n }\n\n // Apply optional parameters; the seam reports any params it dropped (#4069).\n const dropped = this.applyOptionalParams(params, request);\n\n return { params, dropped };\n }\n\n /**\n * Applies optional parameters to the request params.\n *\n * @returns the params the seam dropped (#4069) — surfaced as response warnings.\n */\n private applyOptionalParams(\n params: Anthropic.MessageCreateParamsNonStreaming,\n request: CompletionRequest\n ): readonly DroppedParam[] {\n // #4068: the temperature drop-decision (#4061: Claude after Opus 4.6 rejects\n // any non-1.0 temperature with a 400; omit it — 1.0 is the API default, so\n // omitting is equivalent) is centralized in the shared planOptionalParams seam.\n const plan = planOptionalParams(request, this.resolvedModelId);\n if (plan.temperature !== undefined) {\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- still valid for older Anthropic models (≤ Opus 4.6) and value 1.0; the seam gates on temperatureUnsupportedForModel\n params.temperature = plan.temperature;\n }\n\n if (request.stop !== undefined && request.stop.length > 0) {\n params.stop_sequences = request.stop;\n }\n\n if (request.tools !== undefined && request.tools.length > 0) {\n params.tools = request.tools.map(mapTool);\n }\n\n // #3433: honor responseFormat via a forced `respond` tool_use. Anthropic\n // has no native JSON mode, so we register a synthetic tool whose\n // input_schema is the requested schema and force the model to call it.\n // The caller-supplied tools (above) are preserved — `respond` is merged\n // in, never clobbered. We only set tool_choice when the caller didn't\n // already specify their own tool usage intent.\n if (forcesResponseTool(request)) {\n const respondTool = buildRespondTool(request.responseFormat);\n params.tools = [...(params.tools ?? []), respondTool];\n params.tool_choice = { type: 'tool', name: RESPOND_TOOL_NAME };\n }\n\n return plan.dropped;\n }\n\n /**\n * Maps Anthropic API response to our CompletionResponse format.\n */\n private mapResponse(\n response: Anthropic.Message,\n surfaceRespondTool = false,\n dropped: readonly DroppedParam[] = []\n ): CompletionResponse {\n const content: ContentBlock[] = surfaceRespondTool\n ? mapResponseWithRespondTool(response.content)\n : response.content.map(mapContentBlock);\n\n // Cache figures come through in full (#4440). The CLI-side\n // `parsers/claude-parser.ts` has extracted these since #4435 — a panel's\n // FIRST call is the one that writes the cache, so dropping them loses its\n // largest input measurement — but this direct-API path was never updated,\n // the same one-arm-fixed shape as #4602.\n //\n // The SDK types both as `number | null` — never undefined, which is why a\n // null check alone suffices here. Null stays absent rather than\n // becoming 0: a fabricated 0 reads as \"no cache write happened\", which is\n // a claim the API did not make. `totalTokens` deliberately remains\n // uncached input + output, matching the CLI parser — folding the cache\n // figures in would change semantics for every existing consumer.\n const cachedInputTokens = response.usage.cache_read_input_tokens;\n const cacheCreationInputTokens = response.usage.cache_creation_input_tokens;\n const usage: TokenUsage = {\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n totalTokens: response.usage.input_tokens + response.usage.output_tokens,\n ...(cachedInputTokens !== null && { cachedInputTokens }),\n ...(cacheCreationInputTokens !== null && { cacheCreationInputTokens }),\n };\n\n return {\n content,\n usage,\n stopReason: mapStopReason(response.stop_reason),\n model: response.model,\n // #4069: surface dropped params (e.g. an omitted temperature) so the caller\n // can see a behavioral param had no effect. Omitted entirely when nothing\n // was dropped (exactOptionalPropertyTypes).\n ...(dropped.length > 0 ? { warnings: dropped } : {}),\n };\n }\n\n /** Usage for a `message_start` chunk, omitted when the vendor sent none (#4835). */\n private static usageFrom(raw: { input_tokens?: number; output_tokens?: number } | undefined): {\n usage?: TokenUsage;\n } {\n const inputTokens = raw?.input_tokens;\n if (typeof inputTokens !== 'number') return {};\n const outputTokens = typeof raw?.output_tokens === 'number' ? raw.output_tokens : 0;\n return {\n usage: { inputTokens, outputTokens, totalTokens: inputTokens + outputTokens },\n };\n }\n\n /**\n * Maps Anthropic stream events to our StreamChunk format.\n */\n private mapStreamEvent(event: MessageStreamEvent): StreamChunk | null {\n switch (event.type) {\n case 'message_start':\n // #4835: the prompt count arrives HERE, not on message_delta — the\n // Anthropic protocol puts it on `message.usage`. The delta comment\n // (\"Not available in delta\") was correct about the wrong event, and\n // the value was being dropped rather than being unavailable.\n //\n // Read defensively and OMIT when absent rather than zero-fill: the\n // SDK types it as required, but an Anthropic-compatible proxy may not\n // send it, and crashing a stream over a telemetry field would be a\n // worse failure than the one being fixed.\n return {\n type: 'message_start',\n message: { model: event.message.model },\n ...ClaudeAdapter.usageFrom(event.message.usage),\n };\n\n case 'content_block_start':\n return {\n type: 'content_block_start',\n index: event.index,\n contentBlock: mapContentBlock(event.content_block),\n };\n\n case 'content_block_delta':\n if (event.delta.type === 'text_delta') {\n return {\n type: 'content_block_delta',\n index: event.index,\n delta: { type: 'text_delta', text: event.delta.text },\n };\n }\n return null;\n\n case 'content_block_stop':\n return {\n type: 'content_block_stop',\n index: event.index,\n };\n\n case 'message_delta':\n return {\n type: 'message_delta',\n delta: { stop_reason: mapStopReason(event.delta.stop_reason ?? null) },\n usage: {\n // Genuinely absent on this event; the real count was emitted on\n // message_start above. Flagged so `totalTokens` reads as the\n // lower bound it is rather than as a total (#4835).\n inputTokens: 0,\n inputTokensMeasured: false,\n outputTokens: event.usage.output_tokens,\n totalTokens: event.usage.output_tokens,\n },\n };\n\n case 'message_stop':\n return { type: 'message_stop' };\n\n default:\n return null;\n }\n }\n\n /**\n * (#2540) List models the Anthropic API currently exposes.\n * Wraps `client.models.list()`. Cached for 5 min, in-flight promise\n * shared across concurrent callers, throws on non-2xx so the\n * harness-side identity resolver knows to fall back.\n */\n async listModels(): Promise<readonly ModelMetadata[]> {\n const now = Date.now();\n if (this.modelsCache !== null && now - this.modelsCache.fetchedAt < CLAUDE_LIST_MODELS_TTL_MS) {\n return this.modelsCache.value;\n }\n if (this.modelsInFlight !== null) return this.modelsInFlight;\n const inFlight = this.fetchModels();\n this.modelsInFlight = inFlight;\n try {\n const value = await inFlight;\n this.modelsCache = { value, fetchedAt: Date.now() };\n return value;\n } finally {\n this.modelsInFlight = null;\n }\n }\n\n private modelsCache: { value: readonly ModelMetadata[]; fetchedAt: number } | null = null;\n private modelsInFlight: Promise<readonly ModelMetadata[]> | null = null;\n\n private async fetchModels(): Promise<readonly ModelMetadata[]> {\n const list = await this.client.models.list();\n const out: ModelMetadata[] = [];\n for (const m of list.data) {\n const entry: ModelMetadata = { id: m.id };\n if (typeof m.created_at === 'string') {\n const ts = Date.parse(m.created_at);\n if (!Number.isNaN(ts)) {\n out.push({ ...entry, ownedBy: 'anthropic', createdAt: Math.floor(ts / 1000) });\n continue;\n }\n }\n out.push({ ...entry, ownedBy: 'anthropic' });\n }\n return out;\n }\n}\n\nconst CLAUDE_LIST_MODELS_TTL_MS = 5 * 60 * 1000;\n\n/**\n * Creates a ClaudeAdapter with the specified configuration.\n * Factory function for cleaner API.\n *\n * @param config - Claude adapter configuration\n * @returns A configured ClaudeAdapter instance\n *\n * @example\n * ```typescript\n * const adapter = createClaudeAdapter({\n * modelId: 'claude-sonnet-4',\n * apiKey: process.env.ANTHROPIC_API_KEY!,\n * });\n * ```\n */\nexport function createClaudeAdapter(config: ClaudeAdapterConfig): ClaudeAdapter {\n return new ClaudeAdapter(config);\n}\n","/**\n * nexus-agents/adapters - Claude Adapter Types\n *\n * Type definitions and constants for the Claude/Anthropic adapter.\n *\n * @module adapters/claude-adapter-types\n */\n\nimport { getCliModelName } from '../config/model-config-helpers.js';\n\n/**\n * Supported Claude model identifiers.\n *\n * Derived from `config/in-tree-data.ts` via `getCliModelName()` (which reads\n * the ModelRegistry — see `config/model-registry.ts`). Do not hardcode\n * model-version strings here; update the registry.\n */\nexport const CLAUDE_MODELS = {\n OPUS_4: getCliModelName('claude-opus'),\n SONNET_4: getCliModelName('claude-sonnet'),\n HAIKU_4: getCliModelName('claude-haiku'),\n} as const;\n\n/**\n * Legacy version-suffix aliases mapped to the current registry cliModelName.\n *\n * Values come from `CLAUDE_MODELS` so they stay in sync with the canonical\n * registry. Add legacy entries here, never the version strings themselves.\n */\nexport const CLAUDE_MODEL_ALIASES: Record<string, string> = {\n 'claude-opus-4': CLAUDE_MODELS.OPUS_4,\n 'claude-sonnet-4': CLAUDE_MODELS.SONNET_4,\n 'claude-haiku-4': CLAUDE_MODELS.HAIKU_4,\n // Legacy alias — pre-4.x users routed to the current haiku.\n 'claude-haiku-3': CLAUDE_MODELS.HAIKU_4,\n} as const;\n\n/**\n * Configuration specific to ClaudeAdapter.\n */\nexport interface ClaudeAdapterConfig {\n /** Model ID (e.g., 'claude-sonnet-4' or full model identifier) */\n modelId: string;\n /** API key for Anthropic API (required) */\n apiKey: string;\n /** Base URL for API (optional, defaults to Anthropic's API) */\n baseUrl?: string;\n /** Request timeout in milliseconds (optional) */\n timeout?: number;\n /** Maximum retries for failed requests (optional) */\n maxRetries?: number;\n}\n\n// Note: Token estimation moved to core/token-estimator.ts (unified TokenEstimator)\n\n/**\n * Default maximum tokens for Claude models.\n */\nexport const DEFAULT_MAX_TOKENS = 4096;\n","/**\n * nexus-agents/adapters - Claude Adapter Helpers\n *\n * Helper functions for mapping between nexus-agents types and Anthropic SDK types.\n *\n * @module adapters/claude-adapter-helpers\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\nimport type {\n MessageParam,\n ContentBlock as AnthropicContentBlock,\n} from '@anthropic-ai/sdk/resources/messages';\nimport type {\n ContentBlock,\n Message,\n ToolDefinition,\n StopReason,\n CompletionRequest,\n ResponseFormat,\n} from '../core/index.js';\nimport { ModelCapability } from '../core/index.js';\nimport { getCliModelName, resolveCliAlias } from '../config/model-config-helpers.js';\n\n/**\n * Name of the synthetic tool the adapter forces when a caller requests a\n * structured `responseFormat` (#3433). Anthropic has no native JSON mode, so\n * we model structured output as a forced `tool_use`.\n */\nexport const RESPOND_TOOL_NAME = 'respond';\n\n/**\n * Maps Anthropic stop reasons to our StopReason type.\n */\nexport function mapStopReason(anthropicReason: string | null): StopReason {\n switch (anthropicReason) {\n case 'end_turn':\n return 'end_turn';\n case 'max_tokens':\n return 'max_tokens';\n case 'stop_sequence':\n return 'stop_sequence';\n case 'tool_use':\n return 'tool_use';\n default:\n return 'end_turn';\n }\n}\n\n/**\n * Maps Anthropic content blocks to our ContentBlock type.\n */\nexport function mapContentBlock(block: AnthropicContentBlock): ContentBlock {\n if (block.type === 'text') {\n return { type: 'text', text: block.text };\n }\n if (block.type === 'tool_use') {\n const toolBlock = block;\n return {\n type: 'tool_use',\n id: toolBlock.id,\n name: toolBlock.name,\n input: toolBlock.input,\n };\n }\n // Handle unexpected block types gracefully\n return { type: 'text', text: '' };\n}\n\n/**\n * Maps our Message format to Anthropic's MessageParam format.\n */\nexport function mapMessage(message: Message): MessageParam {\n const role = message.role === 'user' ? 'user' : 'assistant';\n\n if (typeof message.content === 'string') {\n return { role, content: message.content };\n }\n\n // Map content blocks\n const content = message.content.map((block) => {\n if (block.type === 'text') {\n return { type: 'text' as const, text: block.text };\n }\n if (block.type === 'tool_use') {\n return {\n type: 'tool_use' as const,\n id: block.id,\n name: block.name,\n input: block.input,\n };\n }\n if (block.type === 'tool_result') {\n const toolResult: {\n type: 'tool_result';\n tool_use_id: string;\n content: string;\n is_error?: boolean;\n } = {\n type: 'tool_result' as const,\n tool_use_id: block.tool_use_id,\n content: block.content,\n };\n // Only set is_error if explicitly defined (exactOptionalPropertyTypes)\n if (block.is_error !== undefined) {\n toolResult.is_error = block.is_error;\n }\n return toolResult;\n }\n // Image type is the remaining possibility\n // Cast source to match Anthropic's expected type\n return {\n type: 'image' as const,\n source: block.source as Anthropic.ImageBlockParam['source'],\n };\n });\n\n return { role, content };\n}\n\n/**\n * Maps our ToolDefinition to Anthropic's tool format.\n */\nexport function mapTool(tool: ToolDefinition): Anthropic.Tool {\n return {\n name: tool.name,\n description: tool.description,\n input_schema: tool.inputSchema as Anthropic.Tool.InputSchema,\n };\n}\n\n/**\n * Whether this request asks the Claude adapter to produce structured output\n * via a forced `respond` tool_use (#3433). True only when `responseFormat` is\n * present and not the plain `text` form.\n */\nexport function forcesResponseTool(request: CompletionRequest): boolean {\n return request.responseFormat !== undefined && request.responseFormat.type !== 'text';\n}\n\n/**\n * Builds the synthetic `respond` tool for a non-text responseFormat (#3433).\n * For `json_schema` the caller's schema becomes the tool `input_schema`; for\n * `json_object` we use a permissive `{ type: 'object' }` schema.\n */\nexport function buildRespondTool(format: ResponseFormat | undefined): Anthropic.Tool {\n const inputSchema: Record<string, unknown> =\n format?.type === 'json_schema' ? format.schema : { type: 'object' };\n return mapTool({\n name: RESPOND_TOOL_NAME,\n description:\n 'Return the structured response for this request. Call this tool exactly once with the full answer as its arguments.',\n inputSchema,\n });\n}\n\n/**\n * Maps an Anthropic response body when a forced `respond` tool was requested\n * (#3433). If the model emitted a `respond` tool_use block, its `.input` is\n * surfaced as a single JSON `text` block so existing text/JSON parsers keep\n * working unchanged. Otherwise falls back to the standard block mapping.\n */\nexport function mapResponseWithRespondTool(\n blocks: readonly AnthropicContentBlock[]\n): ContentBlock[] {\n const respondBlock = blocks.find(\n (block) => block.type === 'tool_use' && block.name === RESPOND_TOOL_NAME\n );\n if (respondBlock?.type === 'tool_use') {\n return [{ type: 'text', text: JSON.stringify(respondBlock.input) }];\n }\n return blocks.map(mapContentBlock);\n}\n\n/**\n * Resolves a Claude model alias to the full identifier the SDK expects.\n *\n * `resolveCliAlias` consults the canonical registry — both the cliAlias /\n * id columns AND the `aliases` array (#2199 Child 5 migration). Unknown\n * ids pass through (e.g., custom Bedrock identifiers).\n *\n * The canonical model strings live in `config/in-tree-data.ts` and are\n * resolved through the ModelRegistry; this function never holds them\n * directly (issue #2186 Child 1).\n */\nexport function resolveModelId(modelId: string): string {\n const registryId = resolveCliAlias(modelId);\n if (registryId !== undefined) return getCliModelName(registryId);\n return modelId;\n}\n\n/**\n * Determines capabilities based on model ID.\n */\nexport function getModelCapabilities(modelId: string): readonly ModelCapability[] {\n const capabilities: ModelCapability[] = [\n ModelCapability.COMPLETION,\n ModelCapability.STREAMING,\n ModelCapability.TOOL_USE,\n ModelCapability.VISION,\n ];\n\n // Extended thinking is available on Opus and Sonnet 4\n const resolvedId = resolveModelId(modelId);\n if (resolvedId.includes('opus') || resolvedId.includes('sonnet-4')) {\n capabilities.push(ModelCapability.EXTENDED_THINKING);\n }\n\n return capabilities;\n}\n","/**\n * Shared prompt extraction utilities for model adapters.\n *\n * Consolidates the duplicated extractSystemPrompt logic from\n * claude-adapter, gemini-adapter, and ollama-adapter into a single\n * implementation.\n *\n * @module adapters/prompt-utils\n * (Source: Issue #1596 — DRY adapter standardization)\n */\n\nimport type { CompletionRequest } from '../core/index.js';\nimport { extractSystemPrompt } from '../context/token-counter-helpers.js';\n\n/**\n * Extracts the system prompt from a CompletionRequest.\n *\n * Checks the explicit `systemPrompt` field first, then falls back to\n * searching the messages array for a system-role message.\n */\nexport function extractRequestSystemPrompt(request: CompletionRequest): string | undefined {\n if (request.systemPrompt !== undefined && request.systemPrompt !== '') {\n return request.systemPrompt;\n }\n return extractSystemPrompt(request.messages);\n}\n","/* eslint max-lines: [\"error\", { \"max\": 500, \"skipBlankLines\": true, \"skipComments\": true }] */\n// ~475 lines as eslint counts them (blanks and comments skipped), inside the\n// 400-600 band .rules/governance.md preserves for a cohesive file — AI SDK model adapter lifecycle, response mapping, and error fidelity (#6618).\n/**\n * nexus-agents/adapters/sdk - Base SDK Adapter\n *\n * Implements IModelAdapter using the Vercel AI SDK's generateText/streamText\n * APIs. Provides a unified adapter for any AI SDK-supported provider.\n *\n * @module adapters/sdk/sdk-adapter\n * (Source: Issue #1123 — AI SDK provider layer)\n */\n\nimport type {\n CompletionRequest,\n CompletionResponse,\n ContentBlock,\n ResponseFormat,\n StreamChunk,\n Result,\n ILogger,\n TokenUsage,\n} from '../../core/index.js';\nimport {\n ok,\n ModelError,\n ModelCapability,\n createLogger,\n getErrorMessage,\n} from '../../core/index.js';\nimport { BaseAdapter, AdapterModelError } from '../base-adapter.js';\nimport { ErrorCode } from '../../core/index.js';\nimport {\n isRateLimitLikeError,\n resolveRetryAfterMs,\n RETRY_AFTER_CONTEXT_KEY,\n} from '../rate-limit-detector.js';\nimport { sanitizeOutput } from '../../security/output-sanitizer.js';\nimport type { SdkAdapterConfig, SdkProviderId } from './types.js';\nimport { PROVIDER_ENV_KEYS } from './types.js';\nimport {\n readCustomApiSurface,\n readDirectOpenAiSurface,\n readGatewayEnv,\n redactApiKey,\n} from './gateway-env.js';\nimport { gatewayAiSdkOptions, readGatewayTransport } from '../gateway-http.js';\nimport { planOptionalParams, type DroppedParam } from '../optional-params.js';\nimport {\n validateCustomApiBaseUrl,\n assertCustomApiHostResolvesPublic,\n} from './custom-api-validation.js';\n\n/** Minimal AI SDK model interface (duck-typed for optional dependency). */\ninterface AiSdkModel {\n readonly modelId: string;\n}\n\n/** AI SDK generateText result shape (duck-typed). */\ninterface GenerateTextResult {\n text: string;\n finishReason: string;\n usage: {\n inputTokens: number | undefined;\n outputTokens: number | undefined;\n totalTokens: number | undefined;\n };\n response: { modelId: string };\n}\n\n/** AI SDK streamText result shape (duck-typed). */\ninterface StreamTextResult {\n textStream: AsyncIterable<string>;\n finishReason?: Promise<string> | string | undefined;\n}\n\n/** AI SDK generateObject result shape (duck-typed). */\ninterface GenerateObjectResult {\n object: unknown;\n finishReason: string;\n usage: {\n inputTokens: number | undefined;\n outputTokens: number | undefined;\n totalTokens: number | undefined;\n };\n response: { modelId: string };\n}\n\n/** Maps SDK counters without presenting a partial component sum as a total. */\nfunction mapSdkUsage(usage: GenerateTextResult['usage']): TokenUsage | undefined {\n const { inputTokens, outputTokens, totalTokens } = usage;\n if (totalTokens !== undefined) {\n return { inputTokens: inputTokens ?? 0, outputTokens: outputTokens ?? 0, totalTokens };\n }\n if (inputTokens === undefined || outputTokens === undefined) return undefined;\n return { inputTokens, outputTokens, totalTokens: inputTokens + outputTokens };\n}\n\n/** Opaque schema handle returned by the AI SDK `jsonSchema` helper. */\ntype AiSdkSchema = unknown;\n\n/** Function signatures for AI SDK entry points (loaded dynamically). */\ninterface AiSdkFunctions {\n generateText: (options: Record<string, unknown>) => Promise<GenerateTextResult>;\n streamText: (options: Record<string, unknown>) => StreamTextResult;\n generateObject: (options: Record<string, unknown>) => Promise<GenerateObjectResult>;\n jsonSchema: (schema: Record<string, unknown>) => AiSdkSchema;\n}\n\n/** AI SDK provider factory: creates a provider instance that is callable as a model factory. */\ntype ProviderFactory = (opts: Record<string, unknown>) => ProviderInstance;\n\n/**\n * AI SDK provider instance: callable to create a model on the provider's\n * default surface. `@ai-sdk/openai` also exposes `.chat` and `.responses`\n * (#6645); they are optional here because other providers lack them.\n */\ntype ProviderInstance = ((id: string) => AiSdkModel) & {\n readonly chat?: (id: string) => AiSdkModel;\n readonly responses?: (id: string) => AiSdkModel;\n};\n\n/** Build an OpenAI-provider model on the chosen API surface (#6645, #6654). */\nfunction modelOnSurface(\n provider: ProviderInstance,\n surface: ReturnType<typeof readCustomApiSurface>,\n modelId: string\n): AiSdkModel {\n const build = surface === 'chat' ? provider.chat : provider.responses;\n if (typeof build !== 'function') {\n throw new Error(`AI SDK OpenAI provider has no '${surface}' model factory`);\n }\n return build(modelId);\n}\n\n/** The API surface an adapter's provider is pinned to, read once at construction. */\nfunction apiSurfaceFor(\n providerId: SdkProviderId\n): ReturnType<typeof readCustomApiSurface> | undefined {\n if (providerId === 'custom-openai') return readCustomApiSurface();\n if (providerId === 'openai') return readDirectOpenAiSurface();\n return undefined;\n}\n\n/**\n * Extracts a named provider factory from a dynamically-imported AI SDK module.\n *\n * AI SDK provider modules export factory functions (e.g., createAnthropic, createOpenAI)\n * that return callable provider instances. Since these are optional peer dependencies\n * loaded via dynamic import, we validate the shape at runtime rather than relying on\n * compile-time types.\n */\nfunction extractProviderFactory(\n mod: Record<string, unknown>,\n factoryName: string\n): ProviderFactory {\n const factory = mod[factoryName];\n if (typeof factory !== 'function') {\n throw new Error(`AI SDK module missing expected export: ${factoryName}`);\n }\n return factory as ProviderFactory;\n}\n\n/**\n * Validates a dynamically-imported AI SDK module has the expected generateText/streamText exports.\n *\n * The 'ai' package is an optional peer dependency loaded via dynamic import.\n * We validate the shape at runtime to avoid unsafe casts.\n */\n/**\n * Validate that a dynamically-imported `ai` module exposes the functions this\n * adapter needs; throw a clear, per-export error otherwise. Exported for direct\n * unit testing (#3449) so the \"missing export\" cases don't need a global module\n * mock (`vi.doMock`/`resetModules`), whose state leaked across parallel tests.\n */\nexport function extractAiSdkFunctions(mod: Record<string, unknown>): AiSdkFunctions {\n const generateText = mod['generateText'];\n const streamText = mod['streamText'];\n const generateObject = mod['generateObject'];\n const jsonSchema = mod['jsonSchema'];\n if (typeof generateText !== 'function') {\n throw new Error(\"AI SDK module missing expected export: 'generateText'\");\n }\n if (typeof streamText !== 'function') {\n throw new Error(\"AI SDK module missing expected export: 'streamText'\");\n }\n // #3433: structured output routes through generateObject + jsonSchema.\n if (typeof generateObject !== 'function') {\n throw new Error(\"AI SDK module missing expected export: 'generateObject'\");\n }\n if (typeof jsonSchema !== 'function') {\n throw new Error(\"AI SDK module missing expected export: 'jsonSchema'\");\n }\n return {\n generateText: generateText as AiSdkFunctions['generateText'],\n streamText: streamText as AiSdkFunctions['streamText'],\n generateObject: generateObject as AiSdkFunctions['generateObject'],\n jsonSchema: jsonSchema as AiSdkFunctions['jsonSchema'],\n };\n}\n\n/**\n * Runtime-validates the duck-typed `generateObject` result shape (#3433).\n *\n * `generateObject` comes from the optional `ai` peer dependency, so its\n * result is `unknown` to us. We narrow it here rather than casting, so a\n * shape change in the SDK surfaces as a clear error instead of a silent\n * `undefined` downstream.\n */\nfunction isGenerateObjectResult(value: unknown): value is GenerateObjectResult {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Record<string, unknown>;\n if (!('object' in record)) return false;\n if (typeof record['finishReason'] !== 'string') return false;\n const usage = record['usage'];\n if (typeof usage !== 'object' || usage === null) return false;\n const response = record['response'];\n if (typeof response !== 'object' || response === null) return false;\n if (typeof (response as Record<string, unknown>)['modelId'] !== 'string') return false;\n return true;\n}\n\n/**\n * Resolves the API key for a given provider.\n * Priority: explicit config > environment variable. The `custom-openai`\n * key goes through the gateway-env resolver, which honours the deprecated\n * `NEXUS_CUSTOM_API_KEY` alias (#4392 increment 3).\n */\nfunction resolveApiKey(providerId: SdkProviderId, configKey?: string): string | undefined {\n if (configKey !== undefined) return configKey;\n if (providerId === 'custom-openai') return readGatewayEnv().apiKey;\n return process.env[PROVIDER_ENV_KEYS[providerId]];\n}\n\n/**\n * For the `custom-openai` provider only: resolve the base URL (config >\n * env, the env side via the gateway-env resolver) and run it through the\n * SSRF guard. Returns `undefined` for every other provider (the AI SDK's\n * built-in factories handle their own endpoints). Throws `ConfigError` at\n * construction time for invalid custom-openai setups — catching\n * misconfiguration immediately rather than on the first request.\n */\nfunction resolveAndValidateCustomBaseUrl(config: SdkAdapterConfig): string | undefined {\n if (config.providerId !== 'custom-openai') return undefined;\n const raw = config.baseUrl ?? readGatewayEnv().baseUrl;\n const validated = validateCustomApiBaseUrl(raw);\n if (!validated.ok) throw validated.error;\n return validated.value.toString();\n}\n\n/**\n * Maps AI SDK finish reasons to our StopReason type.\n */\nfunction mapFinishReason(reason: string): CompletionResponse['stopReason'] {\n switch (reason) {\n case 'stop':\n case 'end-turn':\n return 'end_turn';\n case 'length':\n return 'max_tokens';\n case 'tool-calls':\n return 'tool_use';\n // #6618: 'content-filter' is never mapped as a finish: the adapter turns it into\n // an error before a response is built (see assertValidCompletion), because\n // every consumer reads a mapped response as an answer.\n default:\n return 'end_turn';\n }\n}\n\n/** A completion that must surface as an error, never as an empty success (#6607, #6618). */\ninterface NonAnswer {\n /** Why the completion carries no answer. */\n readonly reason: 'content_filter' | 'reasoning_truncated';\n readonly detail: string;\n readonly reasoningTokens?: number;\n}\n\nconst CONTENT_FILTERED: NonAnswer = {\n reason: 'content_filter',\n detail: 'the reply was blocked by a content filter',\n};\n\nconst REASONING_TRUNCATED: NonAnswer = {\n reason: 'reasoning_truncated',\n detail:\n 'the completion budget was spent on reasoning before any output (empty reply, finish length)',\n};\n\n/**\n * A non-answer as a non-retryable `MODEL_ERROR` (#6607, #6618). `context.reason`\n * names which kind it was, so telemetry can tell a refusal from a\n * reasoning-exhausted budget. `source` is `provider/model`.\n */\nfunction nonAnswerError(source: string, nonAnswer: NonAnswer, servedModel?: string): ModelError {\n return new ModelError(`${source}: ${nonAnswer.detail}`, {\n code: ErrorCode.MODEL_ERROR,\n context: {\n reason: nonAnswer.reason,\n ...(servedModel !== undefined ? { servedModel } : {}),\n ...(nonAnswer.reasoningTokens !== undefined\n ? { reasoningTokens: nonAnswer.reasoningTokens }\n : {}),\n },\n });\n}\n\n/**\n * Classifies whether a completion result is a non-answer and throws a `ModelError` if so (#6618).\n */\nfunction assertValidCompletion(\n source: string,\n finishReason: string,\n hasContent: boolean,\n servedModel?: string\n): void {\n if (finishReason === 'content-filter') {\n throw nonAnswerError(source, CONTENT_FILTERED, servedModel);\n }\n if (finishReason === 'length' && !hasContent) {\n throw nonAnswerError(source, REASONING_TRUNCATED, servedModel);\n }\n}\n\n/**\n * Awaits and validates the stream finish reason (#6618).\n */\nasync function assertValidStreamFinish(\n source: string,\n result: StreamTextResult,\n totalText: string\n): Promise<void> {\n if (result.finishReason === undefined) return;\n const finishReason = await result.finishReason;\n assertValidCompletion(source, finishReason, totalText !== '');\n}\n\n/**\n * Categorizes an error into an ErrorCode for the circuit breaker.\n */\nfunction categorizeError(error: unknown): ErrorCode {\n if (isRateLimitLikeError(error)) {\n return ErrorCode.MODEL_RATE_LIMITED;\n }\n const message = getErrorMessage(error).toLowerCase();\n if (message.includes('timeout') || message.includes('timed out')) {\n return ErrorCode.MODEL_TIMEOUT;\n }\n if (message.includes('401') || message.includes('unauthorized') || message.includes('api key')) {\n return ErrorCode.CONFIG_INVALID;\n }\n return ErrorCode.MODEL_ERROR;\n}\n\n/**\n * AI SDK adapter implementing IModelAdapter.\n *\n * Uses Vercel AI SDK (npm: ai) for model interaction instead of\n * CLI subprocess spawning. Supports any provider that has an\n * `@ai-sdk/*` package.\n */\nexport class SdkAdapter extends BaseAdapter {\n private readonly sdkProviderId: SdkProviderId;\n private model: AiSdkModel | undefined;\n private sdkFunctions: AiSdkFunctions | undefined;\n private readonly sdkConfig: SdkAdapterConfig;\n /** Validated base URL for custom-openai provider; undefined for built-ins. */\n private readonly customBaseUrl: string | undefined;\n /**\n * OpenAI API surface: always set for custom-openai (#6645); for the direct\n * `openai` provider only when `OPENAI_BASE_URL` names a non-OpenAI host\n * (#6654). `undefined` keeps the provider's default surface.\n */\n private readonly apiSurface: ReturnType<typeof readCustomApiSurface> | undefined;\n /** Inflight init promise for coalescing concurrent calls (Issue #1438). */\n private initPromise: Promise<void> | undefined;\n /**\n * Cached result of the DNS-resolve-time SSRF check for custom-openai\n * (#3426). Resolved once on first init so we don't re-resolve the gateway\n * hostname on every request. `undefined` until the check has run.\n */\n private resolveSsrfChecked = false;\n\n constructor(config: SdkAdapterConfig, logger?: ILogger) {\n const apiKey = resolveApiKey(config.providerId, config.apiKey);\n super({\n providerId: `sdk-${config.providerId}`,\n modelId: config.modelId,\n capabilities: [ModelCapability.COMPLETION, ModelCapability.STREAMING],\n logger: logger ?? createLogger({ adapter: `sdk-${config.providerId}` }),\n ...(apiKey !== undefined ? { apiKey } : {}),\n ...(config.timeout !== undefined ? { timeout: config.timeout } : {}),\n ...(config.maxRetries !== undefined ? { maxRetries: config.maxRetries } : {}),\n });\n this.sdkProviderId = config.providerId;\n this.sdkConfig = config;\n this.customBaseUrl = resolveAndValidateCustomBaseUrl(config);\n this.apiSurface = apiSurfaceFor(config.providerId);\n }\n\n /**\n * Lazily initialize the AI SDK model and functions.\n * This allows the adapter to be created without the AI SDK installed,\n * failing only when actually used.\n */\n private async ensureInitialized(): Promise<void> {\n if (this.model !== undefined) return;\n // Coalesce concurrent init calls into a single load (Issue #1438)\n if (this.initPromise !== undefined) {\n await this.initPromise;\n return;\n }\n this.initPromise = this.doInitialize();\n try {\n await this.initPromise;\n } finally {\n this.initPromise = undefined;\n }\n }\n\n private async doInitialize(): Promise<void> {\n const apiKey = resolveApiKey(this.sdkProviderId, this.sdkConfig.apiKey);\n if (apiKey === undefined) {\n throw new AdapterModelError(`No API key for ${this.sdkProviderId}`, {\n code: ErrorCode.CONFIG_INVALID,\n });\n }\n\n // DNS-resolve-time SSRF guard for custom-openai gateways (#3426). The\n // construction-time guard is string-level only; this resolves the gateway\n // hostname and rejects if it points at a private/loopback/link-local IP.\n // Run BEFORE any model state is set so a rejection leaves the adapter\n // uninitialized — a retry re-runs the guard rather than skipping it via the\n // `this.model !== undefined` short-circuit in ensureInitialized().\n await this.ensureCustomHostResolvesPublic();\n\n // Dynamic import — AI SDK is an optional peer dependency\n const providerModule = await this.loadProvider(apiKey);\n this.model = providerModule.model;\n\n // AI SDK is an optional peer dependency — validate shape at runtime\n const aiModule = await import('ai');\n this.sdkFunctions = extractAiSdkFunctions(aiModule);\n }\n\n /**\n * For custom-openai only: run the DNS-resolve-time SSRF check exactly once\n * and throw if the gateway hostname resolves to a private address (#3426).\n * Cached via `resolveSsrfChecked` so the hostname is not re-resolved on\n * every request. No-op for non-custom providers (built-in endpoints are\n * trusted) and when no custom base URL is configured.\n */\n private async ensureCustomHostResolvesPublic(): Promise<void> {\n if (this.resolveSsrfChecked) return;\n if (this.sdkProviderId !== 'custom-openai' || this.customBaseUrl === undefined) {\n this.resolveSsrfChecked = true;\n return;\n }\n const hostname = new URL(this.customBaseUrl).hostname;\n const result = await assertCustomApiHostResolvesPublic(hostname);\n if (!result.ok) {\n // Do NOT cache a rejection (#3426 QA): leaving the flag false means a\n // retry re-runs the guard rather than silently skipping it via the\n // early-return above. The guard itself fails OPEN on transient resolver\n // errors, so a flaky-DNS host still proceeds; only a confirmed private\n // resolution throws here.\n throw result.error;\n }\n this.resolveSsrfChecked = true;\n }\n\n /**\n * Loads the provider-specific AI SDK module.\n *\n * Each @ai-sdk/* package exports a factory function (e.g., createAnthropic)\n * that returns a callable provider instance. We use extractProviderFactory()\n * to validate the export exists at runtime, since these are optional peer deps.\n */\n private async loadProvider(apiKey: string): Promise<{ model: AiSdkModel }> {\n switch (this.sdkProviderId) {\n case 'anthropic': {\n const mod = await import('@ai-sdk/anthropic');\n const factory = extractProviderFactory(mod, 'createAnthropic');\n const provider = factory({ apiKey });\n return { model: provider(this.modelId) };\n }\n case 'openai': {\n const mod = await import('@ai-sdk/openai');\n const factory = extractProviderFactory(mod, 'createOpenAI');\n const provider = factory({ apiKey });\n // The provider reads OPENAI_BASE_URL itself; a gateway there gets chat\n // completions unless NEXUS_CUSTOM_API_SURFACE=responses (#6654).\n if (this.apiSurface === undefined) return { model: provider(this.modelId) };\n return { model: modelOnSurface(provider, this.apiSurface, this.modelId) };\n }\n case 'google': {\n const mod = await import('@ai-sdk/google');\n const factory = extractProviderFactory(mod, 'createGoogleGenerativeAI');\n const provider = factory({ apiKey });\n return { model: provider(this.modelId) };\n }\n case 'custom-openai': {\n // OpenAI-compatible gateway (multi-vendor proxies, self-hosted servers,\n // corporate LLM gateways). Reuses @ai-sdk/openai with a configurable\n // baseURL. See custom-api-validation.ts for the SSRF guard; the\n // adapter constructor validates before this method is reached.\n const mod = await import('@ai-sdk/openai');\n const factory = extractProviderFactory(mod, 'createOpenAI');\n const opts: Record<string, unknown> = { apiKey };\n if (this.customBaseUrl !== undefined) {\n opts['baseURL'] = this.customBaseUrl;\n // Auth header, extra headers and proxy: the gateway's transport (#6629).\n const transport = readGatewayTransport(this.customBaseUrl, process.env, this.logger);\n Object.assign(opts, gatewayAiSdkOptions({ ...transport, apiKey }));\n }\n const provider = factory(opts);\n // Chat completions unless NEXUS_CUSTOM_API_SURFACE=responses (#6645):\n // the provider's default is the Responses API, which many gateways lack.\n return {\n model: modelOnSurface(provider, this.apiSurface ?? 'chat', this.modelId),\n };\n }\n }\n }\n\n /**\n * Maps our CompletionRequest to AI SDK generateText options.\n */\n private buildSdkOptions(request: CompletionRequest): {\n options: Record<string, unknown>;\n dropped: readonly DroppedParam[];\n } {\n const options: Record<string, unknown> = {\n model: this.model,\n messages: request.messages.map((m) => ({\n role: m.role === 'system' ? 'system' : m.role,\n content:\n typeof m.content === 'string'\n ? m.content\n : m.content.map((c: ContentBlock) => {\n if (c.type === 'text') return { type: 'text' as const, text: c.text };\n return c;\n }),\n })),\n };\n\n if (request.systemPrompt !== undefined) {\n options['system'] = request.systemPrompt;\n }\n // #4068: the temperature drop-decision (#4061/#4062: Claude after Opus 4.6 and\n // OpenAI reasoning models reject a non-1.0 temperature with a 400; the AI-SDK\n // path routes to both, so omit the param for those models — 1.0 is the API\n // default, equivalent) is centralized in the shared planOptionalParams seam.\n const plan = planOptionalParams(request, this.modelId);\n if (plan.temperature !== undefined) {\n options['temperature'] = plan.temperature;\n }\n if (request.maxTokens !== undefined) {\n options['maxTokens'] = request.maxTokens;\n }\n if (request.stop !== undefined) {\n options['stopSequences'] = request.stop;\n }\n\n // #4069: report dropped params so complete() can surface them as warnings.\n return { options, dropped: plan.dropped };\n }\n\n /**\n * generateText path (text / absent responseFormat) — unchanged behavior.\n */\n private async completeText(\n sdk: AiSdkFunctions,\n options: Record<string, unknown>\n ): Promise<CompletionResponse> {\n const result = await sdk.generateText(options);\n assertValidCompletion(\n `${this.providerId}/${this.modelId}`,\n result.finishReason,\n result.text !== '' && result.text.trim() !== '',\n result.response.modelId\n );\n const usage = mapSdkUsage(result.usage);\n return {\n content: [{ type: 'text', text: result.text }],\n ...(usage !== undefined ? { usage } : {}),\n stopReason: mapFinishReason(result.finishReason),\n model: result.response.modelId,\n };\n }\n\n /**\n * generateObject path (#3433) — json_object / json_schema responseFormat.\n *\n * Uses the AI SDK `jsonSchema` helper to build the schema handle\n * (permissive `{ type: 'object' }` for json_object), then stringifies the\n * returned object into a text content block so downstream parsers /\n * extractTextFromResponse keep working unchanged.\n */\n private async completeStructured(\n sdk: AiSdkFunctions,\n options: Record<string, unknown>,\n responseFormat: Exclude<ResponseFormat, { type: 'text' }>\n ): Promise<CompletionResponse> {\n const rawSchema: Record<string, unknown> =\n responseFormat.type === 'json_schema' ? responseFormat.schema : { type: 'object' };\n const schema = sdk.jsonSchema(rawSchema);\n const result: unknown = await sdk.generateObject({ ...options, schema });\n if (!isGenerateObjectResult(result)) {\n throw new Error(\n 'AI SDK generateObject returned an unexpected result shape ' +\n '(missing object/usage/finishReason/response.modelId)'\n );\n }\n assertValidCompletion(\n `${this.providerId}/${this.modelId}`,\n result.finishReason,\n result.object !== null && result.object !== undefined,\n result.response.modelId\n );\n const usage = mapSdkUsage(result.usage);\n return {\n content: [{ type: 'text', text: JSON.stringify(result.object) }],\n ...(usage !== undefined ? { usage } : {}),\n stopReason: mapFinishReason(result.finishReason),\n model: result.response.modelId,\n };\n }\n\n async complete(request: CompletionRequest): Promise<Result<CompletionResponse, ModelError>> {\n try {\n await this.ensureInitialized();\n this.logRequest(request);\n\n const sdk = this.sdkFunctions;\n if (sdk === undefined) {\n throw new Error(\n `SDK not initialized for model '${this.sdkConfig.modelId}' (provider: ${this.sdkProviderId}). ` +\n 'Ensure ensureInitialized() completes before calling complete().'\n );\n }\n const { options, dropped } = this.buildSdkOptions(request);\n\n // #3433: native structured output. json_object/json_schema route\n // through generateObject; everything else keeps the generateText path\n // unchanged.\n const responseFormat = request.responseFormat;\n const base =\n responseFormat !== undefined && responseFormat.type !== 'text'\n ? await this.completeStructured(sdk, options, responseFormat)\n : await this.completeText(sdk, options);\n\n // #4069: surface params dropped by the seam (e.g. temperature) as warnings.\n // Omitted entirely when nothing was dropped (exactOptionalPropertyTypes).\n const response: CompletionResponse =\n dropped.length > 0 ? { ...base, warnings: dropped } : base;\n\n this.logResponse(response);\n return ok(response);\n } catch (error: unknown) {\n const code = categorizeError(error);\n return this.toErrorResult(error, code);\n }\n }\n\n async *stream(request: CompletionRequest): AsyncIterable<StreamChunk> {\n // Ensure initialization and SDK readiness before entering the generator body.\n // Errors thrown before the first yield in an async generator bypass for-await-of\n // try/catch in callers, so we validate eagerly and wrap the body in try/catch.\n await this.ensureInitialized();\n this.logRequest(request);\n\n const sdk = this.sdkFunctions;\n if (sdk === undefined) {\n throw new AdapterModelError('SDK not initialized after ensureInitialized()', {\n code: ErrorCode.CONFIG_INVALID,\n });\n }\n\n const { options } = this.buildSdkOptions(request);\n\n // First yield establishes the generator — errors after this point are\n // properly caught by callers using for-await-of with try/catch.\n yield { type: 'message_start', message: { model: this.modelId } };\n\n const result = sdk.streamText(options);\n let index = 0;\n yield { type: 'content_block_start', index, contentBlock: { type: 'text', text: '' } };\n\n let totalText = '';\n for await (const text of result.textStream) {\n // #3317 finding #8: skip empty-string deltas — the SDK can emit zero-length\n // chunks (keepalives/segment boundaries); a `text_delta` with `text: ''` is\n // noise that downstream re-assemblers must otherwise special-case.\n if (text === '') continue;\n totalText += text;\n yield {\n type: 'content_block_delta',\n index,\n delta: { type: 'text_delta', text },\n };\n }\n\n await assertValidStreamFinish(`${this.providerId}/${this.modelId}`, result, totalText);\n\n yield { type: 'content_block_stop', index };\n index++;\n yield {\n type: 'message_delta',\n delta: { stop_reason: 'end_turn' },\n // #4835: no usage is reported on this path, so none is emitted —\n // zero-filling all three made an unmeasured stream indistinguishable\n // from one that consumed nothing. `usage` is optional on the chunk.\n };\n yield { type: 'message_stop' };\n }\n\n /**\n * Converts a caught error into a Result error with categorized ErrorCode.\n */\n private toErrorResult(error: unknown, code: ErrorCode): Result<CompletionResponse, ModelError> {\n if (error instanceof ModelError) {\n this.logger.error(`SDK adapter error (${this.sdkProviderId})`, error);\n return { ok: false, error };\n }\n\n // Scrub API keys + bearer tokens out of upstream SDK error messages\n // before they hit logs or the surfaced ModelError. Parity with the\n // subprocess-adapter path. Audit #2824. The RESOLVED key is redacted by\n // exact match first (#4392 inc 3): a gateway key has no vendor shape the\n // pattern sanitizer knows, and a 401 body may echo the key it rejected.\n const apiKey = resolveApiKey(this.sdkProviderId, this.sdkConfig.apiKey);\n const safeMessage = sanitizeOutput(redactApiKey(getErrorMessage(error), apiKey));\n // Never the original object: its message AND its stack's first line carry\n // the raw text. The name is kept so the log still says what was thrown.\n const errorObj = new Error(safeMessage);\n if (error instanceof Error) errorObj.name = error.name;\n this.logger.error(`SDK adapter error (${this.sdkProviderId})`, errorObj);\n // #4606: this path builds the ModelError itself rather than going through\n // `BaseAdapter.transformError`, so it has to capture the horizon too. The\n // AI SDK's `APICallError` carries `responseHeaders` as a plain record;\n // `resolveRetryAfterMs` reads only `retry-after` out of it and returns a\n // number, so no header bag reaches the error or the logs. Parsed off the\n // SANITIZED message, so a scrubbed credential can't be re-read from it.\n const retryAfterMs =\n code === ErrorCode.MODEL_RATE_LIMITED ? resolveRetryAfterMs(error, safeMessage) : undefined;\n // AdapterModelError extends ModelError — no cast needed\n const modelError = new AdapterModelError(`${this.sdkProviderId} SDK error: ${safeMessage}`, {\n code,\n ...(retryAfterMs !== undefined\n ? { context: { [RETRY_AFTER_CONTEXT_KEY]: retryAfterMs } }\n : {}),\n });\n return { ok: false, error: modelError };\n }\n}\n","/**\n * nexus-agents/cli-adapters - CLI Detection Cache\n *\n * Caches CLI health check results to avoid repeated subprocess calls.\n * Invalidates on circuit breaker trips or manual invalidation.\n *\n * @module cli-adapters/cli-detection-cache\n * (Source: Issue #165, Proposal adapter-architecture-review.md)\n */\n\nimport { z } from 'zod';\nimport { createLogger } from '../core/logger.js';\nimport type { ILogger } from '../core/index.js';\nimport { getTimeProvider } from '../core/index.js';\nimport type { CliName, HealthStatus, VersionStatus } from './types.js';\n\n/**\n * Cached health result for a CLI.\n */\nexport interface CliHealthResult {\n /** Whether the CLI is healthy and available */\n readonly healthy: boolean;\n /** CLI version detected */\n readonly version: string;\n /** Version compatibility status */\n readonly versionStatus: VersionStatus;\n /** When this result was captured */\n readonly checkedAt: Date;\n /** Optional status message */\n readonly message?: string | undefined;\n}\n\n/**\n * Configuration for the CLI detection cache.\n */\nexport interface CliDetectionCacheConfig {\n /** Base time-to-live in milliseconds (default: 5 minutes) */\n readonly ttlMs: number;\n /** Enable adaptive TTL based on health history (default: true) */\n readonly adaptiveTtl?: boolean | undefined;\n /** Logger instance */\n readonly logger?: ILogger | undefined;\n}\n\n/**\n * Zod schema for cache configuration validation.\n */\nexport const CliDetectionCacheConfigSchema = z.object({\n ttlMs: z.number().min(1000).max(3600_000).default(300_000),\n});\n\n/**\n * Default cache configuration.\n */\nexport const DEFAULT_CACHE_CONFIG: CliDetectionCacheConfig = {\n ttlMs: 300_000, // 5 minutes\n adaptiveTtl: true,\n};\n\n/** Adaptive TTL multipliers. Healthy CLIs are polled less often; unhealthy more often. */\nconst ADAPTIVE_MULTIPLIER_HEALTHY = 2.0;\nconst ADAPTIVE_MULTIPLIER_UNHEALTHY = 0.25;\n/** Number of consecutive same-status results before the multiplier kicks in. */\nconst ADAPTIVE_STREAK_THRESHOLD = 2;\n\n/**\n * Interface for CLI detection cache.\n * Allows dependency injection for testing.\n */\nexport interface ICliDetectionCache {\n /** Get cached health result for a CLI */\n get(cli: CliName): CliHealthResult | undefined;\n\n /** Set health result for a CLI */\n set(cli: CliName, result: CliHealthResult): void;\n\n /** Check if cache entry is stale */\n isStale(cli: CliName): boolean;\n\n /** Invalidate cache for a specific CLI or all CLIs */\n invalidate(cli?: CliName): void;\n\n /** Get all cached results */\n getAll(): ReadonlyMap<CliName, CliHealthResult>;\n\n /** Get cache statistics */\n getStats(): CacheStats;\n\n /** Get effective TTL for a CLI (accounts for adaptive adjustments) */\n getEffectiveTtl(cli: CliName): number;\n}\n\n/**\n * Cache statistics for observability.\n */\nexport interface CacheStats {\n /** Number of cached entries */\n readonly size: number;\n /** Cache hits since last reset */\n readonly hits: number;\n /** Cache misses since last reset */\n readonly misses: number;\n /** Hit rate (0-1) */\n readonly hitRate: number;\n /** When stats were last reset */\n readonly lastReset: Date;\n}\n\n/**\n * CLI detection cache implementation.\n * Thread-safe for Node.js single-threaded execution.\n */\nexport class CliDetectionCache implements ICliDetectionCache {\n private readonly config: CliDetectionCacheConfig;\n private readonly logger: ILogger;\n private readonly cache: Map<CliName, CliHealthResult> = new Map();\n /** Consecutive same-health-status count per CLI (for adaptive TTL). */\n private readonly streaks: Map<CliName, { healthy: boolean; count: number }> = new Map();\n private hits = 0;\n private misses = 0;\n private lastReset: Date = new Date(getTimeProvider().now());\n\n constructor(config?: Partial<CliDetectionCacheConfig>) {\n const validated = CliDetectionCacheConfigSchema.parse({\n ttlMs: config?.ttlMs ?? DEFAULT_CACHE_CONFIG.ttlMs,\n });\n this.config = {\n ...validated,\n adaptiveTtl: config?.adaptiveTtl ?? DEFAULT_CACHE_CONFIG.adaptiveTtl,\n logger: config?.logger,\n };\n this.logger = config?.logger ?? createLogger({ component: 'CliDetectionCache' });\n\n this.logger.debug('CliDetectionCache initialized', { ttlMs: this.config.ttlMs });\n }\n\n get(cli: CliName): CliHealthResult | undefined {\n const result = this.cache.get(cli);\n\n if (result === undefined) {\n this.misses++;\n this.logger.debug('Cache miss', { cli });\n return undefined;\n }\n\n if (this.isStale(cli)) {\n this.misses++;\n this.logger.debug('Cache stale', { cli, checkedAt: result.checkedAt });\n return undefined;\n }\n\n this.hits++;\n this.logger.debug('Cache hit', { cli, healthy: result.healthy });\n return result;\n }\n\n set(cli: CliName, result: CliHealthResult): void {\n this.cache.set(cli, result);\n this.updateStreak(cli, result.healthy);\n this.logger.debug('Cache updated', {\n cli,\n healthy: result.healthy,\n version: result.version,\n });\n }\n\n isStale(cli: CliName): boolean {\n const result = this.cache.get(cli);\n if (result === undefined) return true;\n\n const age = getTimeProvider().now() - result.checkedAt.getTime();\n return age > this.getEffectiveTtl(cli);\n }\n\n /** Returns the effective TTL for a CLI, applying adaptive multiplier if enabled. */\n getEffectiveTtl(cli: CliName): number {\n if (this.config.adaptiveTtl === false) return this.config.ttlMs;\n const streak = this.streaks.get(cli);\n if (streak === undefined || streak.count < ADAPTIVE_STREAK_THRESHOLD) {\n return this.config.ttlMs;\n }\n const multiplier = streak.healthy ? ADAPTIVE_MULTIPLIER_HEALTHY : ADAPTIVE_MULTIPLIER_UNHEALTHY;\n return this.config.ttlMs * multiplier;\n }\n\n private updateStreak(cli: CliName, healthy: boolean): void {\n const prev = this.streaks.get(cli);\n if (prev?.healthy === healthy) {\n this.streaks.set(cli, { healthy, count: prev.count + 1 });\n } else {\n this.streaks.set(cli, { healthy, count: 1 });\n }\n }\n\n invalidate(cli?: CliName): void {\n if (cli !== undefined) {\n this.cache.delete(cli);\n this.streaks.delete(cli);\n this.logger.info('Cache invalidated', { cli });\n } else {\n this.cache.clear();\n this.streaks.clear();\n this.logger.info('Cache cleared');\n }\n }\n\n getAll(): ReadonlyMap<CliName, CliHealthResult> {\n return this.cache;\n }\n\n getStats(): CacheStats {\n const total = this.hits + this.misses;\n return {\n size: this.cache.size,\n hits: this.hits,\n misses: this.misses,\n hitRate: total > 0 ? this.hits / total : 0,\n lastReset: this.lastReset,\n };\n }\n\n /**\n * Resets cache statistics.\n */\n resetStats(): void {\n this.hits = 0;\n this.misses = 0;\n this.lastReset = new Date(getTimeProvider().now());\n this.logger.debug('Cache stats reset');\n }\n\n /**\n * Converts HealthStatus to CliHealthResult for caching.\n */\n static fromHealthStatus(status: HealthStatus): CliHealthResult {\n return {\n healthy: status.healthy,\n version: status.version,\n versionStatus: status.versionStatus,\n checkedAt: status.lastChecked,\n message: status.message,\n };\n }\n}\n\n/**\n * Creates a CLI detection cache instance.\n *\n * @param config - Optional cache configuration\n * @returns CLI detection cache\n *\n * @example\n * ```typescript\n * const cache = createCliDetectionCache({ ttlMs: 60_000 }); // 1 minute TTL\n * const result = cache.get('claude');\n * if (!result) {\n * const health = await adapter.healthCheck();\n * cache.set('claude', CliDetectionCache.fromHealthStatus(health));\n * }\n * ```\n */\nexport function createCliDetectionCache(\n config?: Partial<CliDetectionCacheConfig>\n): ICliDetectionCache {\n return new CliDetectionCache(config);\n}\n","/**\n * Auto-Selecting Model Adapter Factory\n *\n * Automatically selects the best available adapter:\n * 1. CLI adapters (claude/gemini/codex) - OAuth, no API keys needed\n * The CLIs handle model selection internally.\n * 2. API adapters (Anthropic/OpenAI) - requires API keys (fallback)\n *\n * Supports optional caching to avoid repeated CLI health checks.\n *\n * @module adapters/auto-adapter\n * (Source: Issue #165 - CLI detection cache)\n */\n\nimport type { IModelAdapter, ILogger } from '../core/index.js';\nimport { createLogger } from '../core/index.js';\nimport { createCliAdapter, isCliAvailable, getAvailableClis } from '../cli-adapters/factory.js';\nimport { isCliDisabled } from '../cli-adapters/disabled-clis.js';\nimport {\n createGatewaySlotAdapter,\n hasGatewaySlotCatalog,\n resolveGatewayDefault,\n resolveGatewaySlot,\n} from './gateway-family-slots.js';\nimport { createCliToModelAdapter } from '../cli-adapters/cli-to-model-adapter.js';\nimport { createModelToCliAdapter } from '../cli-adapters/model-to-cli-adapter.js';\nimport { createClaudeAdapter } from './claude-adapter.js';\nimport { SdkAdapter } from './sdk/index.js';\nimport { warnIfGatewayCostUndeclared } from './sdk/gateway-cost.js';\nimport { hostnameOf, readGatewayEnv } from './sdk/gateway-env.js';\nimport type { CliName, ICliAdapter, ApiVendor, ApiArmId } from '../cli-adapters/types.js';\nimport { apiArmId } from '../cli-adapters/types.js';\nimport { buildCliCapabilityProfiles } from '../config/model-config-helpers.js';\nimport type { ICliDetectionCache } from '../cli-adapters/cli-detection-cache.js';\nimport { createCliDetectionCache } from '../cli-adapters/cli-detection-cache.js';\nimport { CUSTOM_API_DEFAULT_MODEL } from '../config/defaults.js';\nimport { getCliModelName, getDefaultModelForCli } from '../config/model-config-helpers.js';\n\n/**\n * Adapter selection priority.\n */\nexport type AdapterPriority = 'cli-first' | 'api-first' | 'cli-only' | 'api-only';\n\n/**\n * Configuration for auto-selecting adapters.\n */\nexport interface AutoAdapterConfig {\n /** Selection priority (default: 'cli-first') */\n readonly priority?: AdapterPriority;\n /** Preferred CLI if multiple available (optional) */\n readonly preferredCli?: CliName;\n /** API key for Anthropic (optional, for fallback) */\n readonly anthropicApiKey?: string;\n /** API key for OpenAI (optional, for fallback via AI SDK) */\n readonly openaiApiKey?: string;\n /** API key for Google AI (optional, for fallback via AI SDK) */\n readonly googleApiKey?: string;\n /** Logger instance */\n readonly logger?: ILogger;\n /** CLI detection cache (optional, creates new if not provided) */\n readonly cache?: ICliDetectionCache;\n /** Whether to create and use cache if not provided (default: true) */\n readonly enableCache?: boolean;\n /** Default timeout for CLI subprocess calls (ms). Overrides auto-detection. */\n readonly defaultCliTimeoutMs?: number;\n}\n\n/**\n * Result of adapter selection.\n */\nexport interface AdapterSelection {\n /** The selected adapter */\n readonly adapter: IModelAdapter;\n /** Source of the adapter */\n readonly source: 'cli' | 'api';\n /** Which CLI or API was selected */\n readonly name: string;\n /** Why this adapter was selected */\n readonly reason: string;\n /** The cache used for CLI detection (for reuse) */\n readonly cache?: ICliDetectionCache | undefined;\n}\n\nconst defaultLogger = createLogger({ component: 'auto-adapter' });\n\n/**\n * Resolves the cache to use based on configuration.\n */\nfunction resolveCache(config: AutoAdapterConfig, logger: ILogger): ICliDetectionCache | undefined {\n if (config.cache !== undefined) {\n return config.cache;\n }\n const enableCache = config.enableCache ?? true;\n return enableCache ? createCliDetectionCache({ logger }) : undefined;\n}\n\n/** The CLI bridge's timeout config, when the caller set one. */\nfunction cliBridgeConfig(config: AutoAdapterConfig): { defaultTimeoutMs: number } | undefined {\n return config.defaultCliTimeoutMs !== undefined\n ? { defaultTimeoutMs: config.defaultCliTimeoutMs }\n : undefined;\n}\n\n/**\n * Attempts to create a CLI-based model adapter.\n * The CLI tools (claude, gemini, codex) handle their own model selection.\n * Uses cache to avoid repeated health checks.\n */\nasync function tryCliAdapter(\n config: AutoAdapterConfig,\n logger: ILogger,\n cache?: ICliDetectionCache\n): Promise<AdapterSelection | null> {\n const preferredCli = config.preferredCli;\n const bridgeConfig = cliBridgeConfig(config);\n\n // #6590: a preferred CLI disabled by NEXUS_DISABLED_CLIS is skipped, not\n // probed. The registry pins every per-CLI adapter through this field.\n if (preferredCli !== undefined && isCliDisabled(preferredCli)) {\n logger.info('Preferred CLI is disabled by NEXUS_DISABLED_CLIS', { cli: preferredCli });\n }\n\n // If preferred CLI specified, try that first\n const preferred = await tryPreferredCli(config, logger, cache);\n if (preferred !== null) return preferred;\n\n // #6604: in gateway mode a pinned slot with no CLI is served by its\n // family's gateway model, or is unavailable — never another CLI's family.\n const gatewaySlot = tryGatewaySlot(config, logger);\n if (gatewaySlot !== undefined) return gatewaySlot;\n\n // Otherwise, get all available CLIs and use the first one found\n const availableClis = await getAvailableClis(cache);\n\n if (availableClis.length === 0) {\n logger.info('No CLI adapters available');\n return null;\n }\n\n // Use first available CLI - each CLI handles its own model selection\n const selectedCli = availableClis[0];\n\n if (selectedCli === undefined) {\n return null;\n }\n\n logger.info('Auto-selected CLI', { cli: selectedCli, available: availableClis });\n const cliAdapter = createCliAdapter({ cli: selectedCli, logger });\n await cliAdapter.initialize();\n\n return {\n adapter: createCliToModelAdapter(cliAdapter, bridgeConfig),\n source: 'cli',\n name: selectedCli,\n reason: `Using '${selectedCli}' CLI (model selection handled by CLI)`,\n cache,\n };\n}\n\n/** The pinned CLI's own adapter when it is enabled and available, else null. */\nasync function tryPreferredCli(\n config: AutoAdapterConfig,\n logger: ILogger,\n cache?: ICliDetectionCache\n): Promise<AdapterSelection | null> {\n const preferredCli = config.preferredCli;\n if (\n preferredCli === undefined ||\n isCliDisabled(preferredCli) ||\n !(await isCliAvailable(preferredCli, cache))\n ) {\n return null;\n }\n logger.info('Using preferred CLI', { cli: preferredCli });\n const cliAdapter = createCliAdapter({ cli: preferredCli, logger });\n await cliAdapter.initialize();\n return {\n adapter: createCliToModelAdapter(cliAdapter, cliBridgeConfig(config)),\n source: 'cli',\n name: preferredCli,\n reason: `Preferred CLI '${preferredCli}' is available (model selection handled by CLI)`,\n cache,\n };\n}\n\n/**\n * The gateway selection for a pinned slot whose CLI is not available (#6604).\n * A CLI disabled by `NEXUS_DISABLED_CLIS` counts as not available (#6720:\n * the variable is transport-scoped), so its family's gateway model serves\n * the slot. `undefined` keeps the pre-#6604 path: no pinned slot, or no\n * gateway catalogue. A slot whose family the gateway does not serve is served\n * by a direct API key of the SAME family when one is set (#6604 review, item\n * 4: before the gateway existed that key served it) — unless its CLI is\n * disabled, which never falls back to an API key (#6720); otherwise it THROWS, so\n * the resilient adapter reports it unavailable instead of substituting the\n * first installed CLI, another family's key or `NEXUS_CUSTOM_MODEL`.\n */\nfunction tryGatewaySlot(config: AutoAdapterConfig, logger: ILogger): AdapterSelection | undefined {\n const preferredCli = config.preferredCli;\n if (preferredCli === undefined) return undefined;\n if (preferredCli === 'opencode' && hasGatewaySlotCatalog()) {\n // #6626: opencode is multi-vendor, not a family. Serving it with the\n // gateway default would record the default's outcomes under the opencode\n // slot, and its registry ids (`opencode-custom-opus`) are not gateway ids.\n throw new Error(\n \"The 'opencode' slot is unavailable: its CLI is not available, and in gateway mode it is not substituted (opencode has no model family; the unpinned default serves any-model requests)\"\n );\n }\n const slot = resolveGatewaySlot(preferredCli, process.env, logger);\n if (slot.kind === 'inactive') return undefined;\n if (slot.kind === 'unavailable') {\n // #6720 review: a DISABLED CLI's slot is served by the gateway family model\n // only. A same-family API key would be new API spend the operator did not\n // ask for, and doctor and the fallback chain both call this slot unavailable.\n if (isCliDisabled(preferredCli)) {\n throw new Error(\n `The '${preferredCli}' slot is unavailable: its CLI is disabled by NEXUS_DISABLED_CLIS and the gateway serves no ${slot.family} model`\n );\n }\n const sameFamily = buildApiSelectionForVendor(slot.family, logger, config);\n if (sameFamily !== null) {\n return sameFamily;\n }\n throw new Error(\n `The '${preferredCli}' slot is unavailable: its CLI is not available, the gateway serves no ${slot.family} model, and no ${slot.family} API key is set`\n );\n }\n const modelId = slot.adapter.modelId;\n logger.info('Using gateway family model for CLI slot', { cli: preferredCli, model: modelId });\n return {\n adapter: createGatewaySlotAdapter(preferredCli, slot.adapter),\n source: 'api',\n name: preferredCli,\n reason: `CLI '${preferredCli}' is not available; gateway ${slot.family} model '${modelId}' serves the slot (${slot.via})`,\n };\n}\n\n/**\n * Resolves an API key from config or environment variable.\n */\nfunction resolveApiKeyFromEnv(configKey: string | undefined, envVar: string): string | undefined {\n const key = configKey ?? process.env[envVar];\n return key !== undefined && key.length > 0 ? key : undefined;\n}\n\n/**\n * Attempts to create an API-based model adapter.\n * Tries providers in order: Anthropic (native), OpenAI (SDK), Google (SDK).\n * This is a fallback when no CLIs are available.\n */\nfunction tryApiAdapter(config: AutoAdapterConfig, logger: ILogger): AdapterSelection | null {\n // Derive default model IDs from canonical registry instead of hardcoding\n const claudeModelId = getCliModelName(getDefaultModelForCli('claude'));\n const codexModelId = getCliModelName(getDefaultModelForCli('codex'));\n const geminiModelId = getCliModelName(getDefaultModelForCli('gemini'));\n\n // 1. Anthropic — use native ClaudeAdapter (battle-tested)\n const anthropicKey = resolveApiKeyFromEnv(config.anthropicApiKey, 'ANTHROPIC_API_KEY');\n if (anthropicKey !== undefined) {\n logger.info('Using Anthropic API adapter', { model: claudeModelId });\n return {\n adapter: createClaudeAdapter({ modelId: claudeModelId, apiKey: anthropicKey }),\n source: 'api',\n name: 'anthropic',\n reason: `Using Anthropic API (native adapter, model: ${claudeModelId})`,\n };\n }\n\n // 2. OpenAI — use AI SDK adapter\n const openaiKey = resolveApiKeyFromEnv(config.openaiApiKey, 'OPENAI_API_KEY');\n if (openaiKey !== undefined) {\n logger.info('Using OpenAI API adapter (AI SDK)', { model: codexModelId });\n return {\n adapter: new SdkAdapter({ providerId: 'openai', modelId: codexModelId, apiKey: openaiKey }),\n source: 'api',\n name: 'openai',\n reason: `Using OpenAI API via AI SDK (model: ${codexModelId})`,\n };\n }\n\n // 3. Google — use AI SDK adapter\n const googleKey = resolveApiKeyFromEnv(config.googleApiKey, 'GOOGLE_AI_API_KEY');\n if (googleKey !== undefined) {\n logger.info('Using Google AI API adapter (AI SDK)', { model: geminiModelId });\n return {\n adapter: new SdkAdapter({\n providerId: 'google',\n modelId: geminiModelId,\n apiKey: googleKey,\n }),\n source: 'api',\n name: 'google',\n reason: `Using Google AI API via AI SDK (model: ${geminiModelId})`,\n };\n }\n\n // 4. Custom OpenAI-compatible gateway (multi-vendor proxies, self-hosted\n // LLM servers, corporate gateways). Extracted for line limit.\n const custom = tryCustomOpenAiAdapter(logger);\n if (custom !== null) return custom;\n\n logger.info('No API keys available for any provider');\n return null;\n}\n\n/**\n * Tries the custom-openai SDK adapter if the gateway URL and key are both\n * set: `NEXUS_OPENAI_COMPAT_URL` / `NEXUS_OPENAI_COMPAT_KEY`, or their\n * deprecated aliases `NEXUS_CUSTOM_API_BASE_URL` / `NEXUS_CUSTOM_API_KEY`\n * (#4392 increment 3; the resolver warns once when an alias is in use). The\n * adapter constructor runs the base URL through an SSRF guard (see\n * adapters/sdk/custom-api-validation.ts). Epic #2119.\n *\n * Only the hostname reaches the log and the reason string: a base URL can\n * carry userinfo.\n */\nfunction tryCustomOpenAiAdapter(logger: ILogger): AdapterSelection | null {\n const { baseUrl: customBaseUrl, apiKey: customKey } = readGatewayEnv(process.env, logger);\n if (customKey === undefined || customBaseUrl === undefined) return null;\n const choice = customModelChoice(logger);\n if (choice === null) return null;\n const { modelId: customModelId, note } = choice;\n const host = hostnameOf(customBaseUrl);\n logger.info('Using custom-openai SDK adapter', { model: customModelId, host });\n return {\n // The caller's logger reaches the adapter, so what it logs on a failed\n // call is observable where the selection was made (#4392 inc 3 review).\n adapter: new SdkAdapter(\n {\n providerId: 'custom-openai',\n modelId: customModelId,\n apiKey: customKey,\n baseUrl: customBaseUrl,\n },\n logger\n ),\n source: 'api',\n name: 'custom-openai',\n reason: `Using custom OpenAI-compatible gateway at ${host} (model: ${customModelId}${note})`,\n };\n}\n\n/**\n * The model the custom-openai adapter sends. With no gateway catalogue it is\n * `NEXUS_CUSTOM_MODEL` or the built-in default, exactly as before #6626. In\n * gateway mode it is a catalogue model (`resolveGatewayDefault`), never an\n * unvalidated id; null when the catalogue holds no chat model.\n */\nfunction customModelChoice(logger: ILogger): { modelId: string; note: string } | null {\n const d = resolveGatewayDefault(process.env, logger);\n if (d.kind === 'inactive') {\n return { modelId: process.env['NEXUS_CUSTOM_MODEL'] ?? CUSTOM_API_DEFAULT_MODEL, note: '' };\n }\n if (d.kind === 'resolved')\n return { modelId: d.adapter.modelId, note: `; gateway default by ${d.via}` };\n logger.warn('Gateway catalogue has no chat model; the custom-openai default is unavailable');\n return null;\n}\n\n/**\n * Resolve an API-vendor name to its `{vendor, slot}` pair (#3422). `slot` is the\n * *attribution* CLI slot (`getModelInfo`, capability profile) — NOT the routing\n * arm id, which stays distinct (`api:<vendor>`) so CLI and API telemetry never\n * merge. Exhaustive switch (concrete literals, no index-access undefined).\n */\nfunction resolveApiVendor(name: string): { vendor: ApiVendor; slot: CliName } | undefined {\n switch (name) {\n case 'anthropic':\n return { vendor: 'anthropic', slot: 'claude' };\n case 'openai':\n return { vendor: 'openai', slot: 'codex' };\n case 'google':\n return { vendor: 'google', slot: 'gemini' };\n case 'custom-openai':\n return { vendor: 'custom-openai', slot: 'opencode' };\n default:\n return undefined;\n }\n}\n\n/**\n * Wrap an `AdapterSelection{source:'api'}` for insertion into a CompositeRouter's\n * `Map<RoutingArmId, ICliAdapter>` (#3317 step 1 / #3422). Returns the distinct\n * routing arm id (`api:<vendor>`) and an `ICliAdapter` view of the IModelAdapter\n * (via {@link createModelToCliAdapter}). Returns null for CLI selections (the\n * router already gets those from `createAllAdapters` under their slot key) or an\n * unrecognized vendor.\n */\nexport function wrapApiSelectionForRouter(\n selection: AdapterSelection\n): { armId: ApiArmId; adapter: ICliAdapter } | null {\n if (selection.source !== 'api') return null;\n const resolved = resolveApiVendor(selection.name);\n if (resolved === undefined) return null;\n const adapter = createModelToCliAdapter(selection.adapter, {\n name: resolved.slot,\n capabilities: buildCliCapabilityProfiles()[resolved.slot],\n });\n return { armId: apiArmId(resolved.vendor), adapter };\n}\n\n/**\n * Build an `AdapterSelection{source:'api'}` for a single vendor when its key(s)\n * are present, else null. Reuses the same adapter constructors as\n * {@link tryApiAdapter} but is key-presence-only and never calls out (#3422).\n */\nfunction buildApiSelectionForVendor(\n vendor: ApiVendor,\n logger: ILogger,\n config: AutoAdapterConfig = {}\n): AdapterSelection | null {\n switch (vendor) {\n case 'anthropic': {\n const key = resolveApiKeyFromEnv(config.anthropicApiKey, 'ANTHROPIC_API_KEY');\n if (key === undefined) return null;\n const modelId = getCliModelName(getDefaultModelForCli('claude'));\n return {\n adapter: createClaudeAdapter({ modelId, apiKey: key }),\n source: 'api',\n name: 'anthropic',\n reason: `Using Anthropic API (native adapter, model: ${modelId})`,\n };\n }\n case 'openai': {\n const key = resolveApiKeyFromEnv(config.openaiApiKey, 'OPENAI_API_KEY');\n if (key === undefined) return null;\n const modelId = getCliModelName(getDefaultModelForCli('codex'));\n return {\n adapter: new SdkAdapter({ providerId: 'openai', modelId, apiKey: key }),\n source: 'api',\n name: 'openai',\n reason: `Using OpenAI API via AI SDK (model: ${modelId})`,\n };\n }\n case 'google': {\n const key = resolveApiKeyFromEnv(config.googleApiKey, 'GOOGLE_AI_API_KEY');\n if (key === undefined) return null;\n const modelId = getCliModelName(getDefaultModelForCli('gemini'));\n return {\n adapter: new SdkAdapter({ providerId: 'google', modelId, apiKey: key }),\n source: 'api',\n name: 'google',\n reason: `Using Google AI API via AI SDK (model: ${modelId})`,\n };\n }\n case 'custom-openai':\n return tryCustomOpenAiAdapter(logger);\n default: {\n const exhaustive: never = vendor;\n throw new Error(`Unknown API vendor: ${String(exhaustive)}`);\n }\n }\n}\n\n/** API vendors enumerated in routing-arm order (#3422). */\nconst API_ROUTING_VENDORS: readonly ApiVendor[] = [\n 'anthropic',\n 'openai',\n 'google',\n 'custom-openai',\n];\n\n/**\n * Enumerate the direct-API routing arms whose keys are present in the\n * environment, each wrapped as an `ICliAdapter` keyed by its distinct\n * `api:<vendor>` arm id (#3422). Key-presence-only and deterministic: a vendor\n * is included iff its required env var(s) are set; keys are never validated by\n * calling out. Used by `createAllAdapters` under `NEXUS_BILLING_MODE=api`.\n */\nexport function collectApiRoutingArms(\n logger: ILogger = defaultLogger\n): Array<{ armId: ApiArmId; adapter: ICliAdapter }> {\n const arms: Array<{ armId: ApiArmId; adapter: ICliAdapter }> = [];\n for (const vendor of API_ROUTING_VENDORS) {\n const selection = buildApiSelectionForVendor(vendor, logger);\n if (selection === null) continue;\n const wrapped = wrapApiSelectionForRouter(selection);\n if (wrapped === null) continue;\n arms.push(wrapped);\n // Only the custom-openai (gateway) arm can be undeclared; the helper is\n // silent for the three vendor arms (#4392 increment 2).\n warnIfGatewayCostUndeclared(wrapped.armId, logger);\n }\n return arms;\n}\n\n/** Try CLI first, then API as fallback. */\nasync function selectCliFirst(\n config: AutoAdapterConfig,\n logger: ILogger,\n cache?: ICliDetectionCache\n): Promise<AdapterSelection> {\n const cliResult = await tryCliAdapter(config, logger, cache);\n if (cliResult !== null) return cliResult;\n const apiResult = tryApiAdapter(config, logger);\n if (apiResult !== null) return apiResult;\n throw new Error(\n 'No adapters available. Install a CLI (claude/gemini/codex) or set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_AI_API_KEY.'\n );\n}\n\n/** Try API first, then CLI as fallback. */\nasync function selectApiFirst(\n config: AutoAdapterConfig,\n logger: ILogger,\n cache?: ICliDetectionCache\n): Promise<AdapterSelection> {\n const apiResult = tryApiAdapter(config, logger);\n if (apiResult !== null) return apiResult;\n const cliResult = await tryCliAdapter(config, logger, cache);\n if (cliResult !== null) return cliResult;\n throw new Error(\n 'No adapters available. Set ANTHROPIC_API_KEY/OPENAI_API_KEY/GOOGLE_AI_API_KEY or install a CLI (claude/gemini/codex).'\n );\n}\n\n/** CLI only - no API fallback. */\nasync function selectCliOnly(\n config: AutoAdapterConfig,\n logger: ILogger,\n cache?: ICliDetectionCache\n): Promise<AdapterSelection> {\n const cliResult = await tryCliAdapter(config, logger, cache);\n if (cliResult !== null) return cliResult;\n throw new Error(\n 'No CLI adapters available. Install and authenticate claude, gemini, or codex CLI.'\n );\n}\n\n/** API only - no CLI fallback. */\nfunction selectApiOnly(config: AutoAdapterConfig, logger: ILogger): AdapterSelection {\n const apiResult = tryApiAdapter(config, logger);\n if (apiResult !== null) return apiResult;\n throw new Error(\n 'No API key available. Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_AI_API_KEY.'\n );\n}\n\n/**\n * Auto-selects the best available model adapter.\n * Uses caching to avoid repeated CLI health checks.\n *\n * @param config - Configuration options\n * @returns Selected adapter or throws if none available\n *\n * @example\n * ```typescript\n * // Use CLI if available, fall back to API\n * const { adapter, source, reason, cache } = await createAutoAdapter();\n * console.log(`Using ${source} adapter: ${reason}`);\n *\n * // Reuse cache for subsequent selections\n * const { adapter: adapter2 } = await createAutoAdapter({ cache });\n *\n * // Force CLI only\n * const { adapter } = await createAutoAdapter({ priority: 'cli-only' });\n * ```\n */\nexport async function createAutoAdapter(config: AutoAdapterConfig = {}): Promise<AdapterSelection> {\n const logger = config.logger ?? defaultLogger;\n const priority = config.priority ?? 'cli-first';\n const cache = resolveCache(config, logger);\n\n logger.info('Auto-selecting adapter', { priority, cacheEnabled: cache !== undefined });\n\n switch (priority) {\n case 'cli-first':\n return selectCliFirst(config, logger, cache);\n case 'api-first':\n return selectApiFirst(config, logger, cache);\n case 'cli-only':\n return selectCliOnly(config, logger, cache);\n case 'api-only':\n return selectApiOnly(config, logger);\n default: {\n const exhaustive: never = priority;\n throw new Error(`Unknown priority: ${String(exhaustive)}`);\n }\n }\n}\n","/* eslint-disable max-lines -- Cohesive subprocess adapter base class; governance 400-600 OK if cohesive */\n/**\n * nexus-agents/cli-adapters - Subprocess Adapter\n *\n * Base class for subprocess-based CLI adapters.\n * Used by ClaudeCliAdapter and GeminiCliAdapter.\n *\n * Extracted from base-adapter.ts per Issue #272 (file size limits).\n */\n\nimport { spawn } from 'node:child_process';\nimport type { ChildProcess, ChildProcessWithoutNullStreams } from 'node:child_process';\n\nimport type { Result } from '../core/index.js';\nimport { ok, err, getTimeProvider, createLogger, getErrorMessage } from '../core/index.js';\n\nimport type {\n CliName,\n CliTransport,\n CliTask,\n CliResponse,\n CliError,\n CliErrorCode,\n ResolvedExecutionOptions,\n ICliResponseParser,\n} from './types.js';\nimport { BaseCliAdapter } from './base-adapter.js';\nimport { buildChildEnv } from './subprocess-env.js';\nimport { SIGKILL_GRACE_MS, terminateProcessTree, trackProcessTree } from './process-tree-kill.js';\nimport { sanitizeOutput } from '../security/output-sanitizer.js';\nimport { isRateLimitText, isDurableCapacityText } from '../adapters/rate-limit-detector.js';\nimport {\n parseCliErrorEnvelope,\n classifyExtractedError,\n isAuthFailureText,\n} from './cli-error-envelope.js';\nimport { createCallerAbortCliError, isTimeoutText } from './cli-error-helpers.js';\nimport { generateHyphenId } from '../utils/id-utils.js';\n\n/** Minimum length for plaintext fallback to kick in.\n * Lowered from 100→30 to recover short but valid CLI responses (#1401). */\nconst PLAINTEXT_FALLBACK_MIN_LENGTH = 30;\n\n/**\n * Attempts to extract a usable response from raw stdout when the structured\n * parser fails. Returns the trimmed text if it looks like natural language\n * (not JSON/NDJSON) and exceeds the minimum length threshold.\n *\n * Recovers responses from CLIs that output plaintext instead of their\n * expected structured format. (#1401)\n */\nfunction tryPlaintextFallback(stdout: string): string | null {\n const trimmed = stdout.trim();\n if (trimmed.length < PLAINTEXT_FALLBACK_MIN_LENGTH) return null;\n // Skip if it looks like structured output the parser should handle\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) return null;\n return trimmed;\n}\n\n/** Error patterns in stderr that indicate a real failure, not debug output (#1402). */\nconst STDERR_ERROR_PATTERNS = [\n 'error:',\n 'fatal:',\n 'panic:',\n 'unhandled',\n 'not found',\n 'invalid model',\n 'authentication',\n 'unauthorized',\n 'permission denied',\n 'connection refused',\n 'econnrefused',\n 'enotfound',\n 'timeout',\n 'failed to connect',\n 'invalid api key',\n 'rate limit',\n 'quota exceeded',\n 'service unavailable',\n];\n\n/** Checks if stderr looks like a real error (not just debug/progress output). */\nfunction looksLikeErrorStderr(stderr: string): boolean {\n const lower = stderr.toLowerCase();\n return STDERR_ERROR_PATTERNS.some((pattern) => lower.includes(pattern));\n}\n\n/** Stderr patterns indicating transient connection failures (retryable). */\nconst STDERR_CONNECTION_PATTERNS = [\n 'connection refused',\n 'econnrefused',\n 'enotfound',\n 'econnreset',\n 'failed to connect',\n 'service unavailable',\n 'eaddrinuse',\n 'address already in use',\n];\n\n/**\n * Classifies a stderr error message into the most specific CliErrorCode.\n * Checks for transient patterns (connection, rate-limit, timeout) before\n * falling back to EXECUTION_ERROR. This ensures transient failures in\n * stderr are retried instead of treated as terminal. (#1401)\n *\n * Rate-limit detection uses shared patterns from rate-limit-detector. (#1596)\n */\nfunction classifyStderrError(stderr: string): CliErrorCode {\n const lower = stderr.toLowerCase();\n if (STDERR_CONNECTION_PATTERNS.some((p) => lower.includes(p))) return 'CONNECTION_ERROR';\n if (isRateLimitText(stderr)) return 'RATE_LIMITED';\n if (isTimeoutText(stderr)) return 'TIMEOUT';\n // #6269: a credential failure is terminal for this CLI (not in\n // RETRYABLE_ERROR_CODES) and counts as `authentication` on the breaker.\n if (isAuthFailureText(stderr)) return 'NOT_AUTHENTICATED';\n return 'EXECUTION_ERROR';\n}\n\n/**\n * The first non-blank stderr line, for an error message that names the cause\n * without the stack frames and tier tables that follow it (#6269). `stderr`\n * is already sanitized by the caller.\n */\nfunction firstStderrLine(stderr: string): string {\n return (\n stderr\n .split('\\n')\n .find((line) => line.trim() !== '')\n ?.trim() ?? ''\n );\n}\n\nconst subprocessLogger = createLogger({ component: 'subprocess-adapter' });\n\n/**\n * Longest stderr carried on a SUCCESSFUL response (#6094). The buffer above\n * caps capture at 10 MB; the response field exists so a classifier can read a\n * failure line, not to ship a log. Kept from the head: the sandbox failure is\n * written when the CLI's tool loop first tries the shell.\n */\nexport const MAX_RESPONSE_STDERR_CHARS = 8_000;\n\n/**\n * The `stderr` field of a SUCCESSFUL response (#6094). A run's stderr was\n * captured and dropped here: a sandboxed shell failure inside the CLI's tool\n * loop is written to it while stdout still carries a parsed answer, and that\n * is the voter classifier's structured signal. Present only when non-empty.\n */\nfunction successStderrField(stderr: string): { stderr?: string } {\n return stderr === '' ? {} : { stderr: stderr.slice(0, MAX_RESPONSE_STDERR_CHARS) };\n}\n\n/** Maximum buffer size for stdout/stderr (10 MB). */\nconst MAX_BUFFER_BYTES = 10 * 1024 * 1024;\n\n/** CliErrorCodes that represent transient failures safe to retry. */\nconst TRANSIENT_ERROR_CODES: ReadonlySet<CliErrorCode> = new Set([\n 'TIMEOUT',\n 'RATE_LIMITED',\n 'CONNECTION_ERROR',\n 'PARSE_ERROR',\n]);\n\n/** Delay schedule for transient-error retries (ms per attempt index). */\nconst TRANSIENT_RETRY_DELAYS_MS = [500, 1000] as const;\n\n/** Maximum number of transient-error retries. */\nconst MAX_TRANSIENT_RETRIES = TRANSIENT_RETRY_DELAYS_MS.length;\n\n/**\n * Maximum retries for PARSE_ERROR specifically (#1533).\n * Parse errors are less likely to self-heal than timeouts/connection errors,\n * so we cap at 1 retry instead of 2.\n */\nexport const MAX_PARSE_RETRIES = 1;\n\n/** Timeout extension multiplier when retrying a TIMEOUT error. (#1401) */\nexport const TIMEOUT_RETRY_MULTIPLIER = 1.5;\n\n/**\n * Grace period before escalating SIGTERM to SIGKILL on timeout (#3026\n * finding 1). Defined beside the tree kill, which every subprocess runner\n * shares (#6747); re-exported so tests can assert against the threshold.\n */\nexport { SIGKILL_GRACE_MS };\n\n/**\n * Checks whether a CliErrorCode represents a transient failure.\n * Timeout, rate_limit, connection, and parse errors are transient.\n * Parse errors get fewer retries (MAX_PARSE_RETRIES) than others (#1533).\n */\nexport function isTransientError(code: CliErrorCode): boolean {\n return TRANSIENT_ERROR_CODES.has(code);\n}\n\n/**\n * Spawn the CLI and track it so server shutdown ends it (#6680). It stays in\n * the server's process group, so a signal to that group reaches it too.\n */\nfunction spawnCliChild(\n cliName: CliName,\n cmdConfig: CommandConfig,\n workDir: unknown\n): ChildProcessWithoutNullStreams {\n // Curated child env: base infrastructure vars + only this CLI's\n // own vendor credentials, so cross-vendor API keys don't leak\n // into the spawned CLI (#2865). Also drops CLAUDECODE — a nested\n // CLI must not believe it's already inside Claude Code.\n // #6754: a command's own variables (e.g. a read-only permission config) are\n // applied last, so an inherited value of the same name cannot override them.\n const childEnv = { ...buildChildEnv(cliName), ...cmdConfig.env };\n return trackProcessTree(\n spawn(cmdConfig.command, cmdConfig.args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: childEnv,\n ...(typeof workDir === 'string' && workDir.trim().length > 0 ? { cwd: workDir } : {}),\n })\n );\n}\n\n/**\n * Whether a failed spawn is worth spawning again (#6120).\n *\n * A transient CODE is not the whole answer: a durable capacity cap arrives as\n * RATE_LIMITED too, and re-running the same model against a spend ceiling is\n * the futile work #5359 named. The claude out-of-credits envelope cost two\n * extra spawns and 1.5 s per call this way, then still failed.\n */\nfunction shouldRetryInPlace(error: CliError, signal: AbortSignal | undefined): boolean {\n // #6680: an aborted call is not transient — retrying it only sleeps through\n // the backoff to fast-fail again.\n if (signal?.aborted === true) return false;\n return isTransientError(error.code) && !isDurableCapacityText(error.message);\n}\n\n/**\n * Exit codes that indicate transient process termination (retryable).\n * - 137 = 128 + SIGKILL (9): OOM killer, system resource limits\n * - 143 = 128 + SIGTERM (15): external termination (our timeout SIGTERM\n * is already caught by the timeout handler before close fires)\n */\nconst TRANSIENT_EXIT_CODES = new Set([137, 143]);\n\n/** Human-readable signal names for exit codes (128 + signal number). */\nconst EXIT_CODE_SIGNAL_NAMES: Record<number, string> = {\n 137: 'SIGKILL (OOM or killed)',\n 143: 'SIGTERM (terminated)',\n};\n\n/** Builds an error message for signal exit codes that includes identifiable keywords. */\nfunction buildSignalExitMessage(code: number): string {\n const signalName = EXIT_CODE_SIGNAL_NAMES[code];\n if (signalName !== undefined) return `Process killed by ${signalName}, exit code ${String(code)}`;\n return `Process exited with code ${String(code)}`;\n}\n\n/**\n * Checks whether a process exit code indicates a transient failure\n * that is safe to retry (e.g., OOM kill, external SIGTERM).\n */\nexport function isTransientExitCode(code: number | null): boolean {\n return code !== null && TRANSIENT_EXIT_CODES.has(code);\n}\n\n/** Internal state for buffered stream collection. */\ninterface BufferState {\n stdout: string;\n stderr: string;\n stdoutBytes: number;\n stderrBytes: number;\n stdoutTruncated: boolean;\n stderrTruncated: boolean;\n resolved: boolean;\n /**\n * Wall-clock at first stdout byte (or null if none yet). Used by #2472 to\n * split total subprocess time into spawn-latency (spawn→first byte) and\n * streaming (first byte→close), which is the cheapest way to identify\n * which stage causes stochastic timeouts.\n */\n firstByteTime: number | null;\n}\n\n/** Append data to a buffered stream, respecting the 10 MB cap. */\nfunction appendBuffered(state: BufferState, stream: 'stdout' | 'stderr', data: Buffer): void {\n const bytesKey = stream === 'stdout' ? 'stdoutBytes' : 'stderrBytes';\n const truncKey = stream === 'stdout' ? 'stdoutTruncated' : 'stderrTruncated';\n if (state[bytesKey] < MAX_BUFFER_BYTES) {\n state[stream] += data.toString();\n state[bytesKey] += data.length;\n } else if (!state[truncKey]) {\n state[truncKey] = true;\n subprocessLogger.warn(`${stream} buffer exceeded 10 MB, truncating`);\n }\n}\n\n/**\n * Command configuration returned by getCommand.\n */\nexport interface CommandConfig {\n command: string;\n args: string[];\n /** Optional stdin content (prompt passed via stdin instead of args) */\n stdin?: string;\n /**\n * Variables set on the child's environment on top of the curated base\n * (#6754). They win over any inherited value of the same name.\n */\n env?: Readonly<Record<string, string>>;\n /**\n * Optional cleanup callback invoked after the subprocess resolves\n * (success, error, or timeout). Used by adapters that materialize\n * temp files for the subprocess (e.g. codex `model_instructions_file`).\n * Errors thrown by cleanup are logged but do not affect the request result.\n */\n cleanup?: () => void | Promise<void>;\n}\n\n/**\n * Configuration for transient-error retry behaviour.\n */\nexport interface TransientRetryConfig {\n /** Whether transient-error retry is enabled (default: false). */\n enabled: boolean;\n}\n\n/**\n * Base class for subprocess-based CLI adapters.\n * Used by ClaudeCliAdapter and GeminiCliAdapter.\n */\nexport abstract class SubprocessCliAdapter extends BaseCliAdapter {\n readonly transport: CliTransport = 'subprocess';\n\n protected abstract readonly parser: ICliResponseParser;\n\n /** Transient-error retry config. Override in subclass to enable. */\n protected readonly transientRetry: TransientRetryConfig = { enabled: true };\n\n /**\n * The inner {@link retryTransient} layer is the single retry authority\n * for subprocess CLIs. When it is enabled (the default), the shared\n * outer retry loop must not also retry: nesting both meant up to 6\n * subprocess spawns and ~10-minute hangs on a persistent TIMEOUT, since\n * the inner layer's timeout extension compounds on every outer attempt\n * (#2824). The outer loop still runs once, so circuit-breaker failure\n * recording is unaffected.\n */\n protected override shouldOuterRetry(opts: ResolvedExecutionOptions): boolean {\n return opts.allowRetry && !this.transientRetry.enabled;\n }\n\n /**\n * Gets CLI command and arguments for execution.\n * If stdin is provided, it will be written to the process stdin.\n */\n protected abstract getCommand(task: CliTask): CommandConfig;\n\n /**\n * Invokes the optional cleanup hook supplied by getCommand(). Sync\n * throws are swallowed (warn-only) and async rejections are caught\n * so cleanup failures never bubble up and mask the real subprocess\n * result.\n */\n private runSubprocessCleanup(cleanup: CommandConfig['cleanup']): void {\n if (cleanup === undefined) return;\n try {\n const r = cleanup();\n if (r instanceof Promise) {\n r.catch((e: unknown) => {\n this.logger.warn('Subprocess cleanup failed', { error: String(e) });\n });\n }\n } catch (e: unknown) {\n this.logger.warn('Subprocess cleanup threw', { error: String(e) });\n }\n }\n\n /**\n * #3026 finding 2: SIGTERM the child when the caller's AbortSignal\n * aborts mid-execution. Listener auto-detaches on `'close'` so we\n * don't leak across child lifetimes.\n */\n private attachAbortSignal(\n child: ChildProcess,\n signal: AbortSignal,\n resolve: (r: Result<CliResponse, CliError>) => void\n ): void {\n const onAbort = (): void => {\n if (child.exitCode === null && child.signalCode === null) {\n // #6680: the child and its descendants, so a relaunched CLI worker is\n // not orphaned; a tree that ignores SIGTERM is force-reaped rather than\n // left running after the cancel.\n void terminateProcessTree(child, SIGKILL_GRACE_MS, () => {\n this.logger.warn('Child ignored SIGTERM after abort, escalating to SIGKILL', {\n cli: this.name,\n sigkillGraceMs: SIGKILL_GRACE_MS,\n });\n });\n }\n // #6691: the adapter's own watchdog is the timer in `spawnSubprocess`,\n // not this listener, so a caller abort is a cancel unless its reason\n // names a deadline.\n resolve(err(createCallerAbortCliError(signal.reason, 'Aborted by caller signal', this.name)));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n child.once('close', () => {\n signal.removeEventListener('abort', onAbort);\n });\n }\n\n /**\n * Executes a task via subprocess, with optional transient-error retry.\n * When `transientRetry.enabled` is true, transient errors (timeout,\n * rate_limit, connection, parse) are retried with exponential backoff\n * (500ms, 1000ms). Parse errors get max 1 retry (#1533); others get 2.\n */\n async executeTask(\n task: CliTask,\n options: ResolvedExecutionOptions\n ): Promise<Result<CliResponse, CliError>> {\n // #2963 site 3: per-execute request ID for log correlation. CliTask\n // doesn't carry a task-id field; generate one here so every\n // log-line for this invocation (including the per-retry timing\n // breakdown emitted by `logTimingBreakdown`) can be grouped by a\n // single ID. Doesn't propagate up to MCP — it's purely an\n // adapter-internal correlation key.\n const requestId = generateHyphenId('cli-req', 8);\n const result = await this.spawnSubprocess(task, options, requestId);\n if (result.ok || !this.transientRetry.enabled) return result;\n if (!shouldRetryInPlace(result.error, options.signal)) return result;\n\n return this.retryTransient(task, options, result, 0, requestId);\n }\n\n /**\n * Retries a transient error with bounded exponential backoff.\n */\n private async retryTransient(\n task: CliTask,\n options: ResolvedExecutionOptions,\n lastResult: Result<CliResponse, CliError>,\n attempt: number,\n requestId: string\n ): Promise<Result<CliResponse, CliError>> {\n const isParseError = !lastResult.ok && lastResult.error.code === 'PARSE_ERROR';\n const maxRetries = isParseError ? MAX_PARSE_RETRIES : MAX_TRANSIENT_RETRIES;\n if (attempt >= maxRetries) return lastResult;\n\n // Guard above ensures attempt < maxRetries ≤ TRANSIENT_RETRY_DELAYS_MS.length\n const delayMs = TRANSIENT_RETRY_DELAYS_MS[attempt] as number;\n const isTimeout = !lastResult.ok && lastResult.error.code === 'TIMEOUT';\n subprocessLogger.debug('Retrying transient error', {\n cli: this.name,\n requestId,\n attempt: attempt + 1,\n delayMs,\n errorCode: !lastResult.ok ? lastResult.error.code : undefined,\n });\n\n await this.delay(delayMs);\n\n // Extend timeout on TIMEOUT retries so near-miss tasks can complete (#1401)\n const retryOptions = isTimeout\n ? { ...options, timeoutMs: Math.round(options.timeoutMs * TIMEOUT_RETRY_MULTIPLIER) }\n : options;\n const result = await this.spawnSubprocess(task, retryOptions, requestId);\n if (result.ok) return result;\n if (!shouldRetryInPlace(result.error, options.signal)) return result;\n\n return this.retryTransient(task, retryOptions, result, attempt + 1, requestId);\n }\n\n /**\n * Spawns a single subprocess execution (no retry).\n *\n * `requestId` (#2963 site 3) correlates the timing-breakdown log emitted\n * on subprocess close back to the parent `executeTask` invocation —\n * essential when multiple subprocesses for the same CLI run concurrently\n * (pipelines, votes) and the JSDoc's stated goal (\"group by cli + provider\n * + model and surface tail-latency outliers\") requires a way to\n * disambiguate which timing row belongs to which call.\n */\n /**\n * Wraps a command's cleanup in a once-guard (#4488).\n *\n * `CommandConfig.cleanup` removes a tempdir the command builder created (e.g.\n * codex's `nexus-codex-sysprompt-*` holding the system prompt). It must run on\n * EVERY exit path — a path that skips it leaks a directory per invocation, the\n * leak codex-adapter's own comment records as having exhausted inodes on\n * long-running MCP daemons — and exactly ONCE, since a second rm could hit a\n * path the OS has already handed to someone else.\n */\n /**\n * Result for a synchronous spawn failure (#4488).\n *\n * `spawn()` throws synchronously on some failures (EACCES, and ENOENT on\n * certain platforms), and handler setup can throw too. Those paths never\n * reach `resolve`, so without catching them the tempdir leaks AND the promise\n * rejects instead of returning a Result — breaking the never-throws contract\n * this adapter is supposed to honour.\n */\n private spawnFailure(command: string, error: unknown): Result<CliResponse, CliError> {\n return err(\n this.createError('EXECUTION_ERROR', `Failed to spawn ${command}: ${getErrorMessage(error)}`)\n );\n }\n\n private onceCleanup(cleanup: CommandConfig['cleanup']): () => void {\n let done = false;\n return (): void => {\n if (done) return;\n done = true;\n this.runSubprocessCleanup(cleanup);\n };\n }\n\n private spawnSubprocess(\n task: CliTask,\n options: ResolvedExecutionOptions,\n requestId: string\n ): Promise<Result<CliResponse, CliError>> {\n // #3026 finding 2: fast-fail if the caller already aborted before we\n // bothered to spawn. Saves a child process start when an upstream\n // wave/loop has already moved on. Checked before getCommand (#6680): a\n // command builder can create a tempdir that only the spawn path cleans up.\n const signal = options.signal;\n if (signal?.aborted === true) {\n return Promise.resolve(\n err(createCallerAbortCliError(signal.reason, 'Aborted before spawn', this.name))\n );\n }\n // #6277: the resolved guard reaches getCommand on the task, so an adapter\n // whose CLI has its own wait (agy --print-timeout) can size it to the\n // budget instead of a default that races the guard.\n const cmdConfig = this.getCommand(\n task.timeoutMs === undefined ? { ...task, timeoutMs: options.timeoutMs } : task\n );\n const startTime = getTimeProvider().now();\n\n return new Promise((resolveOuter) => {\n const runCleanupOnce = this.onceCleanup(cmdConfig.cleanup);\n const resolve = (r: Result<CliResponse, CliError>): void => {\n runCleanupOnce();\n resolveOuter(r);\n };\n try {\n const child = spawnCliChild(this.name, cmdConfig, task.options?.['workDir']);\n\n const onProgress = options.onProgress;\n // Called for its side effects (handlers are attached to `child`);\n // the returned state is not needed here, so it is not bound.\n this.setupChildProcessHandlers({\n child,\n startTime,\n timeoutMs: options.timeoutMs,\n resolve,\n requestId,\n ...(onProgress !== undefined ? { onProgress } : {}),\n });\n\n if (signal !== undefined) {\n this.attachAbortSignal(child, signal, resolve);\n }\n\n // Write stdin content if provided and close stdin\n if (cmdConfig.stdin !== undefined) {\n child.stdin.write(cmdConfig.stdin);\n }\n child.stdin.end();\n } catch (error: unknown) {\n runCleanupOnce();\n resolveOuter(this.spawnFailure(cmdConfig.command, error));\n }\n });\n }\n\n /**\n * Sets up child process event handlers for output collection and error handling.\n */\n private setupChildProcessHandlers(args: {\n child: ReturnType<typeof spawn>;\n startTime: number;\n timeoutMs: number;\n resolve: (result: Result<CliResponse, CliError>) => void;\n requestId: string;\n onProgress?: () => void;\n }): BufferState {\n const { child, startTime, timeoutMs, resolve, requestId, onProgress } = args;\n const state: BufferState = {\n stdout: '',\n stderr: '',\n resolved: false,\n stdoutBytes: 0,\n stderrBytes: 0,\n stdoutTruncated: false,\n stderrTruncated: false,\n firstByteTime: null,\n };\n\n const resolveOnce = (result: Result<CliResponse, CliError>): void => {\n if (!state.resolved) {\n state.resolved = true;\n resolve(result);\n }\n };\n\n this.attachStdoutHandlers(child, state, onProgress);\n\n child.on('error', (error: Error) => {\n resolveOnce(this.handleSubprocessError(error));\n });\n\n const timeoutId = this.scheduleTimeoutWithSigkillEscalation({\n child,\n timeoutMs,\n requestId,\n resolveOnce,\n });\n\n child.on('close', (code: number | null) => {\n // #6680: the SIGKILL escalation is terminateProcessTree's; a descendant\n // can outlive the child's close, so only an empty tree cancels it there.\n clearTimeout(timeoutId);\n this.logTimingBreakdown(state, startTime, code, requestId);\n resolveOnce(this.classifyCloseResult(code, state, startTime));\n });\n\n return state;\n }\n\n /**\n * Schedules the SIGTERM-on-timeout + SIGKILL-on-grace escalation\n * (#3026 finding 1).\n *\n * The timer SIGTERMs the tree and resolves the caller's promise\n * immediately so it doesn't wait on a hung child. The escalation\n * (`SIGKILL_GRACE_MS` later, in {@link terminateProcessTree}) checks\n * whether the tree actually exited and force-reaps it with SIGKILL if\n * not — preventing zombie accumulation when a child ignores SIGTERM\n * (Node CLIs that install graceful-shutdown handlers can hang on a broken\n * stream). Off Linux the descendants are collected by an async `ps`\n * first (#6718). Returns the timeout timer, for the `'close'` handler.\n */\n private scheduleTimeoutWithSigkillEscalation(opts: {\n child: ReturnType<typeof spawn>;\n timeoutMs: number;\n requestId: string;\n resolveOnce: (result: Result<CliResponse, CliError>) => void;\n }): NodeJS.Timeout {\n const { child, timeoutMs, requestId, resolveOnce } = opts;\n return setTimeout(() => {\n void terminateProcessTree(child, SIGKILL_GRACE_MS, () => {\n this.logger.warn('Child ignored SIGTERM, escalating to SIGKILL', {\n cli: this.name,\n requestId,\n sigkillGraceMs: SIGKILL_GRACE_MS,\n });\n });\n resolveOnce(err(this.createError('TIMEOUT', 'Execution timed out')));\n }, timeoutMs);\n }\n\n /** Attach stdout/stderr data handlers + capture first-byte time (#2472). */\n private attachStdoutHandlers(\n child: ReturnType<typeof spawn>,\n state: BufferState,\n onProgress?: () => void\n ): void {\n // stdio: ['pipe', 'pipe', 'pipe'] guarantees non-null streams\n if (child.stdout !== null) {\n child.stdout.on('data', (data: Buffer) => {\n if (state.firstByteTime === null && data.length > 0) {\n state.firstByteTime = getTimeProvider().now();\n }\n appendBuffered(state, 'stdout', data);\n onProgress?.();\n });\n }\n if (child.stderr !== null) {\n child.stderr.on('data', (data: Buffer) => {\n appendBuffered(state, 'stderr', data);\n });\n }\n }\n\n /**\n * Log spawn-latency vs streaming breakdown at info level (#2472). Emits\n * one structured event per subprocess invocation, queryable via the\n * existing trace JSONL infrastructure. The breakdown lets operators\n * identify whether a slow run was caused by:\n * - High spawn-latency: model gateway took its time before producing\n * the first token (cold-start, queueing, network jitter).\n * - High streaming-time: response body was large or generation slow.\n * - Total approaches the timeout cap with no first-byte: hung process.\n *\n * Structured fields chosen so existing query_trace tooling can group by\n * cli + provider + model and surface tail-latency outliers.\n */\n private logTimingBreakdown(\n state: BufferState,\n startTime: number,\n code: number | null,\n requestId: string\n ): void {\n const now = getTimeProvider().now();\n const totalMs = now - startTime;\n const spawnLatencyMs = state.firstByteTime === null ? null : state.firstByteTime - startTime;\n const streamingMs = state.firstByteTime === null ? null : now - state.firstByteTime;\n this.logger.info('Subprocess timing', {\n cli: this.name,\n requestId,\n totalMs,\n spawnLatencyMs,\n streamingMs,\n sawFirstByte: state.firstByteTime !== null,\n exitCode: code,\n stdoutBytes: state.stdoutBytes,\n stderrBytes: state.stderrBytes,\n });\n }\n\n /** Classify a subprocess close event into a Result. */\n private classifyCloseResult(\n code: number | null,\n state: BufferState,\n startTime: number\n ): Result<CliResponse, CliError> {\n // Redact API keys from output before any processing, logging, or tracing (#1597)\n state.stdout = sanitizeOutput(state.stdout);\n state.stderr = sanitizeOutput(state.stderr);\n\n if (code !== 0 && state.stdout === '') {\n // Stderr classification takes priority over exit code (#1401)\n if (state.stderr !== '') {\n const msg = state.stderr;\n return err(this.createError(classifyStderrError(state.stderr), msg));\n }\n // Signal-killed processes (137=SIGKILL/OOM, 143=SIGTERM) are transient.\n // Message includes signal name for downstream outcome classification.\n const msg = code !== null ? buildSignalExitMessage(code) : 'Process exited with unknown code';\n if (isTransientExitCode(code)) {\n return err(this.createError('CONNECTION_ERROR', msg));\n }\n return err(this.createError('EXECUTION_ERROR', msg));\n }\n // Non-zero exit with stderr errors: classify specifically to enable\n // transient retry for connection/rate-limit/timeout errors (#1401, #1402)\n if (code !== 0 && state.stderr !== '' && looksLikeErrorStderr(state.stderr)) {\n const msg = `Exit code ${String(code)}: ${state.stderr.slice(0, 500).trim()}`;\n return err(this.createError(classifyStderrError(state.stderr), msg));\n }\n return this.handleSubprocessOutput(state.stdout, state.stderr, startTime);\n }\n\n /**\n * Handles successful subprocess output.\n */\n protected handleSubprocessOutput(\n stdout: string,\n stderr: string,\n startTime: number\n ): Result<CliResponse, CliError> {\n if (stderr !== '' && stdout === '') {\n return err(this.createError(classifyStderrError(stderr), stderr));\n }\n\n const text = this.parser.extractResponse(stdout);\n if (text === null) return this.handleNoAnswer(stdout, stderr, startTime);\n const authFailure = this.classifyEmptyAnswer(text, stderr);\n if (authFailure !== null) return authFailure;\n\n const usage = this.parser.extractUsage(stdout);\n const sessionId = this.parser.extractSessionId(stdout);\n // #5241: a vendor-reported cost is a MEASUREMENT and was being dropped, so\n // `CliResponse.costUsd` had no producer at all — leaving budget-router's\n // `result.value.costUsd ?? estimatedCostUsd` unable to take its left branch\n // and debiting an estimate under a field named `actual`. Parsers whose CLI\n // reports no cost do not implement the method, so absence here means \"this\n // vendor does not report cost\", not \"cost unknown this call\".\n const costUsd = this.parser.extractCostUsd?.(stdout) ?? null;\n\n return ok(\n this.normalizeResponse(text, usage ?? undefined, {\n durationMs: getTimeProvider().now() - startTime,\n raw: stdout,\n ...(sessionId !== null && { sessionId }),\n ...(costUsd !== null && { costUsd }),\n ...successStderrField(stderr),\n })\n );\n }\n\n /**\n * The parser found no usable content. An error-only stream (e.g. OpenCode\n * NDJSON `{\"type\":\"error\"}`) surfaced an `errorMessage`: classify it before\n * the generic PARSE_ERROR path, which would mask the real cause.\n */\n private handleNoAnswer(\n stdout: string,\n stderr: string,\n startTime: number\n ): Result<CliResponse, CliError> {\n const errorOnly = this.classifyErrorOnlyStream(stdout);\n if (errorOnly !== null) return errorOnly;\n return this.handleUnparseableOutput(stdout, stderr, startTime);\n }\n\n /**\n * #6269: a well-formed envelope carrying NO answer while stderr names a\n * credential failure is that failure, not a completion. `agy` exits 0 with\n * `{\"status\":\"SUCCESS\",\"response\":\"\"}` and \"Error authenticating: …\" on\n * stderr; handing \"\" downstream had the vote path parse it (and retry the\n * parse) for the whole panel budget. Returns `null` — no reclassification —\n * for a non-empty answer, and for an empty answer with stderr that names no\n * auth failure: empty stderr is NOT evidence, so that case still flows\n * through as the empty answer it is.\n */\n private classifyEmptyAnswer(text: string, stderr: string): Result<CliResponse, CliError> | null {\n if (text.trim() !== '' || !isAuthFailureText(stderr)) return null;\n const msg = `${this.name}: ${firstStderrLine(stderr)}`;\n return err(this.createError('NOT_AUTHENTICATED', msg));\n }\n\n /**\n * Classify an error-only stream — one where the parser surfaced an\n * `errorMessage` but no usable content (so `extractResponse` returned null).\n * Returns a typed error (NOT_AUTHENTICATED / RATE_LIMITED with a remediation\n * hint, or EXECUTION_ERROR) so an upstream 401 / 429 isn't masked as\n * PARSE_ERROR. Returns `null` when the parser exposes no error message (no\n * `extractErrorMessage`, or empty) — the caller then falls back to the\n * generic unparseable-output recovery.\n */\n private classifyErrorOnlyStream(stdout: string): Result<CliResponse, CliError> | null {\n const errorMessage = this.parser.extractErrorMessage?.(stdout) ?? null;\n if (errorMessage === null || errorMessage === '') return null;\n const classified = classifyExtractedError(errorMessage, this.name);\n const msg =\n classified.hint !== undefined\n ? `${classified.message}\\n → ${classified.hint}`\n : classified.message;\n return err(this.createError(classified.code, msg));\n }\n\n /**\n * Handles the parse-failure branch: when the CLI's structured response\n * parser returned null. Order of recovery attempts (most-specific first):\n * 1. Rate-limit text in raw stdout (#1320)\n * 2. Structured CLI error envelope (#2440)\n * 3. Plaintext fallback for natural-language output (#1401)\n * 4. Generic PARSE_ERROR with truncated snippet\n */\n private handleUnparseableOutput(\n stdout: string,\n stderr: string,\n startTime: number\n ): Result<CliResponse, CliError> {\n if (isRateLimitText(stdout)) {\n const snippet = stdout.slice(0, 500).trim();\n return err(this.createError('RATE_LIMITED', snippet));\n }\n const envelope = parseCliErrorEnvelope(stdout, this.name);\n if (envelope !== null) {\n const msg =\n envelope.hint !== undefined\n ? `${envelope.message}\\n → ${envelope.hint}`\n : envelope.message;\n // #2455 ask 3: at debug level, dump the full sanitized envelope so\n // operators can recover context without re-running. Sanitized via\n // output-sanitizer.ts so leaked tokens (some upstreams echo back the\n // bad token in error responses) don't reach logs.\n subprocessLogger.debug('CLI error envelope unwrapped', {\n cli: this.name,\n code: envelope.code,\n rawSanitized: sanitizeOutput(stdout),\n });\n return err(this.createError(envelope.code, msg));\n }\n const plaintext = tryPlaintextFallback(stdout);\n if (plaintext !== null) {\n subprocessLogger.debug('Using plaintext fallback for unparseable output', {\n rawSanitized: sanitizeOutput(stdout),\n });\n return ok(\n this.normalizeResponse(plaintext, undefined, {\n durationMs: getTimeProvider().now() - startTime,\n raw: stdout,\n })\n );\n }\n const snippet = stdout.slice(0, 500).trim();\n const stderrHint = stderr !== '' ? ` [stderr: ${stderr.slice(0, 300).trim()}]` : '';\n return err(\n this.createError('PARSE_ERROR', `Failed to parse response: ${snippet}${stderrHint}`)\n );\n }\n\n /**\n * Handles subprocess execution errors.\n */\n private handleSubprocessError(error: unknown): Result<CliResponse, CliError> {\n if (!(error instanceof Error)) {\n return err(this.createError('EXECUTION_ERROR', 'Unknown error'));\n }\n\n if (error.message.includes('ETIMEDOUT') || error.message.includes('timeout')) {\n return err(this.createError('TIMEOUT', 'Execution timed out', error));\n }\n\n if (error.message.includes('ENOENT')) {\n return err(this.createError('NOT_FOUND', `${this.binaryName} CLI not found`, error));\n }\n\n return err(this.createError('EXECUTION_ERROR', sanitizeOutput(error.message), error));\n }\n\n /**\n * Initializes the adapter and capacity tracker.\n */\n initialize(): Promise<void> {\n this.initCapacityTracker();\n this.initialized = true;\n return Promise.resolve();\n }\n\n /**\n * Disposes the adapter (no-op for subprocess).\n */\n dispose(): Promise<void> {\n this.initialized = false;\n return Promise.resolve();\n }\n}\n","/**\n * nexus-agents/cli-adapters - Base Adapter\n *\n * Abstract base class for CLI adapters with common functionality.\n * Provides version checking, health checks, and error handling.\n *\n * SubprocessCliAdapter extracted to subprocess-adapter.ts per Issue #272.\n *\n * (Source: cli-project_plan.md v2.1.0)\n */\n\nimport { exec } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport semver from 'semver';\nimport { CLI_SUBPROCESS_TIMEOUTS } from '../config/timeouts.js';\n\nimport type { Result } from '../core/index.js';\nimport { err, getTimeProvider } from '../core/index.js';\nimport type { ILogger } from '../core/index.js';\nimport { createLogger } from '../core/index.js';\n\nimport type {\n ICliAdapter,\n CliName,\n CliTransport,\n CliTask,\n CliResponse,\n CliError,\n CliErrorCode,\n HealthStatus,\n CapacityStatus,\n ModelInfo,\n CapabilityProfile,\n ExecutionOptions,\n ResolvedExecutionOptions,\n VersionStatus,\n TokenUsage,\n} from './types.js';\nimport { CLI_VERSION_REQUIREMENTS, DEFAULT_CAPABILITIES } from './types.js';\nimport { getTimeoutForTaskAuto } from './cli-timeout-profiles.js';\nimport { CapacityTracker, createCapacityTracker } from './capacity-tracker.js';\nimport { executeCliRetryLoop } from './cli-retry-loop.js';\nimport { getDefaultCliCircuitBreakerRegistry } from './cli-circuit-breaker.js';\nimport { createCliError } from './cli-error-helpers.js';\nimport { readOnlyAnalysisRefusal } from './read-only-analysis.js';\n\nconst execAsync = promisify(exec);\n\n/**\n * Default execution options.\n *\n * Timeout reduced from 120s to 60s per Issue #280 to prevent\n * cascading timeouts in multi-agent voting scenarios.\n */\nconst DEFAULT_OPTIONS: ResolvedExecutionOptions = {\n timeoutMs: 60_000, // 1 minute (reduced from 2 minutes per Issue #280)\n allowRetry: true,\n maxRetries: 1, // Reduced from 2 to prevent 3+ minute total wait\n trackUsage: true,\n onProgress: undefined,\n};\n\n/**\n * Abstract base class for CLI adapters.\n * Provides common functionality for version checking, health, and error handling.\n */\nexport abstract class BaseCliAdapter implements ICliAdapter {\n abstract readonly name: CliName;\n abstract readonly transport: CliTransport;\n\n /**\n * The executable this adapter actually runs.\n *\n * Defaults to {@link name} because for most arms the routing identity and the\n * binary are the same word. They are NOT always the same: the `gemini` arm\n * runs `agy` (Antigravity) after Google retired the standalone gemini CLI\n * (#4346). Conflating the two is how that arm ended up executing `agy` for\n * work while shelling `gemini --version` for its health check — reporting the\n * dead binary's version against the live one's floor, and failing its own\n * availability gate while working perfectly.\n */\n get binaryName(): string {\n return this.name;\n }\n\n /**\n * Whether this adapter maps `accessMode: 'read-only-analysis'` to its CLI's\n * own enforcement (#6754). False here so a new adapter fails closed until it\n * implements the mode and says so.\n */\n readonly enforcesReadOnlyAnalysis: boolean = false;\n\n protected readonly logger: ILogger;\n protected capacityTracker: CapacityTracker | null = null;\n protected initialized = false;\n protected cachedVersion?: string;\n /**\n * Epoch ms at which {@link cachedVersion} was read off the binary.\n *\n * The cache never expires, so this is what lets `healthCheck` say whether\n * its `reachable` rests on a probe that just ran or on one from minutes ago\n * (#5864).\n */\n protected cachedVersionAt?: number;\n\n constructor(logger?: ILogger) {\n this.logger = logger ?? createLogger({ component: 'cli-adapter' });\n }\n\n /**\n * Initializes the capacity tracker.\n * Called by subclasses after name is set.\n */\n protected initCapacityTracker(): void {\n this.capacityTracker = createCapacityTracker(this.name);\n }\n\n /**\n * Gets the capability profile for this CLI.\n */\n get capabilities(): CapabilityProfile {\n return DEFAULT_CAPABILITIES[this.name];\n }\n\n /**\n * Abstract method for executing a task.\n * Implemented by concrete adapters.\n */\n abstract executeTask(\n task: CliTask,\n options: ResolvedExecutionOptions\n ): Promise<Result<CliResponse, CliError>>;\n\n /**\n * Abstract method for getting model info.\n * Implemented by concrete adapters.\n */\n abstract getModelInfo(): ModelInfo;\n\n /**\n * Abstract method for initialization.\n * Implemented by concrete adapters.\n */\n abstract initialize(): Promise<void>;\n\n /**\n * Abstract method for cleanup.\n * Implemented by concrete adapters.\n */\n abstract dispose(): Promise<void>;\n\n /**\n * Executes a task with error handling and retries.\n *\n * Timeout priority (highest to lowest):\n * 1. options.timeoutMs - explicit execution option\n * 2. task.timeoutMs - task-level setting\n * 3. getTimeoutForTaskAuto() - computed from task complexity and CLI\n */\n async execute(task: CliTask, options?: ExecutionOptions): Promise<Result<CliResponse, CliError>> {\n // #6754: checked before anything else runs, so a refused read-only task\n // never initializes, spawns or counts against the breaker.\n const refusal = this.accessModeRefusal(task);\n if (refusal !== undefined) return err(refusal);\n\n const effectiveTimeout = this.computeTimeout(task, options);\n const opts = { ...DEFAULT_OPTIONS, ...options, timeoutMs: effectiveTimeout };\n\n if (!this.initialized) {\n await this.initialize();\n }\n\n this.logger.debug('Executing task', {\n cli: this.name,\n contentLength: task.content.length,\n model: task.model,\n timeoutMs: effectiveTimeout,\n });\n\n return this.executeWithRetry(task, opts);\n }\n\n /**\n * The refusal for a task this adapter cannot run in its requested access\n * mode, or `undefined` (#6754). Adapters override it to add conflicts their\n * own options can create, and call `super` first.\n */\n protected accessModeRefusal(task: CliTask): CliError | undefined {\n return readOnlyAnalysisRefusal(this, task);\n }\n\n /**\n * Computes effective timeout for a task.\n */\n private computeTimeout(task: CliTask, options?: ExecutionOptions): number {\n if (options?.timeoutMs !== undefined) return options.timeoutMs;\n if (task.timeoutMs !== undefined) return task.timeoutMs;\n return getTimeoutForTaskAuto(this.name, task.content);\n }\n\n /**\n * Whether the shared outer retry loop ({@link executeCliRetryLoop}) is\n * allowed to retry this adapter's failures. The base adapter honors the\n * caller's `allowRetry`. Subprocess adapters override this to suppress\n * the outer loop when their own transient-retry layer is active, so the\n * two layers do not nest into multiplied spawns (#2824).\n */\n protected shouldOuterRetry(opts: ResolvedExecutionOptions): boolean {\n return opts.allowRetry;\n }\n\n /**\n * Executes task with retry logic via shared retry loop.\n */\n private async executeWithRetry(\n task: CliTask,\n opts: ResolvedExecutionOptions\n ): Promise<Result<CliResponse, CliError>> {\n // #4330: this used to pass no breaker, which made the `recordFailure` in\n // `executeCliRetryLoop` unreachable for every subprocess CLI. The voter\n // serving-gate reads exactly this registry, so a quota-dead CLI's snapshot\n // stayed `undefined` and the gate fail-opened on every panel — #4325's\n // exclusion could never fire because nothing produced the signal.\n const circuitBreaker = getDefaultCliCircuitBreakerRegistry().getBreaker(this.name);\n const result = await executeCliRetryLoop(() => this.executeTask(task, opts), {\n maxRetries: opts.maxRetries,\n allowRetry: this.shouldOuterRetry(opts),\n baseDelayMs: 1_000,\n maxDelayMs: 16_000,\n circuitBreaker,\n cli: this.name,\n logger: this.logger,\n });\n\n if (result.ok) {\n // Recording the success is the caller's job (`executeCliRetryLoop` only\n // records failures — see its test), and it matters: in the closed state\n // `recordSuccess` zeroes the failure count, which is what makes the\n // threshold mean \"consecutive failures\". Without it a long-lived process\n // accumulates scattered blips and eventually evicts a healthy CLI.\n circuitBreaker.recordSuccess();\n this.recordUsage(result.value.response);\n this.logger.info('Task executed successfully', {\n cli: this.name,\n attempt: result.value.retryCount + 1,\n durationMs: result.value.response.durationMs,\n tokensUsed: result.value.response.usage?.totalTokens,\n });\n return { ok: true, value: result.value.response };\n }\n\n this.recordQuotaSignal(result.error);\n this.logger.warn('Task execution failed', {\n cli: this.name,\n error: result.error.message,\n retryable: result.error.retryable,\n });\n return result;\n }\n\n /**\n * Feed a provider's own rate-limit assertion into capacity tracking (#4456).\n *\n * A `retry-after` the provider stated is far stronger evidence than local\n * counting: the tracker otherwise sees only this process's spend and cannot\n * observe a plan quota burned gradually or burned elsewhere, which is the\n * incident #4351 reported. Only RATE_LIMITED carries that assertion; the\n * tracker itself decides whether the stated wait is long enough to mean\n * durable quota rather than a per-minute throttle.\n */\n protected recordQuotaSignal(error: CliError): void {\n if (error.code !== 'RATE_LIMITED') return;\n if (this.capacityTracker === null) return;\n\n const recorded = this.capacityTracker.recordProviderQuotaExhaustion(error.retryAfterMs);\n if (recorded) {\n this.logger.warn('Provider asserted durable quota exhaustion', {\n cli: this.name,\n retryAfterMs: error.retryAfterMs,\n });\n }\n }\n\n /**\n * Performs a health check.\n */\n async healthCheck(): Promise<HealthStatus> {\n try {\n const version = await this.getVersion();\n const versionStatus = this.checkVersionCompatibility(version);\n\n const message = this.getVersionMessage(versionStatus, version);\n const status: HealthStatus = {\n healthy: versionStatus !== 'unsupported' && versionStatus !== 'breaking',\n version,\n versionStatus,\n // The binary answered `--version`, whatever it said (#5060) — though\n // not necessarily on THIS call, which is what versionProbedAt says.\n reachable: true,\n lastChecked: new Date(getTimeProvider().now()),\n ...(this.cachedVersionAt !== undefined && {\n versionProbedAt: new Date(this.cachedVersionAt),\n }),\n ...(message !== undefined && { message }),\n };\n\n return status;\n } catch (error) {\n return {\n healthy: false,\n version: 'unknown',\n versionStatus: 'unsupported',\n // `getVersion` could not run the binary — absent, not merely outdated.\n reachable: false,\n message: error instanceof Error ? error.message : 'Health check failed',\n lastChecked: new Date(getTimeProvider().now()),\n };\n }\n }\n\n /**\n * Gets CLI version.\n */\n async getVersion(): Promise<string> {\n if (this.cachedVersion !== undefined && this.cachedVersion !== '') {\n return this.cachedVersion;\n }\n\n try {\n const { stdout } = await execAsync(`${this.binaryName} --version`, {\n timeout: CLI_SUBPROCESS_TIMEOUTS.spawnMs,\n });\n\n // Extract version number from output\n const version = this.parseVersion(stdout.trim());\n this.cachedVersion = version;\n this.cachedVersionAt = getTimeProvider().now();\n return version;\n } catch (cause: unknown) {\n throw new Error(`Failed to get ${this.binaryName} version`, { cause });\n }\n }\n\n /**\n * Gets current capacity status based on tracked usage.\n * Uses usage-based tracking since CLI subprocess execution\n * doesn't expose HTTP rate limit headers.\n *\n * @see Issue #456 - Real API rate limit tracking\n */\n getCapacity(): Promise<CapacityStatus> {\n // Lazy-init the tracker if no caller has run initialize() yet (#2714).\n // Pre-fix every doctor invocation tripped this path: doctor calls\n // adapter.getCapacity() WITHOUT first calling adapter.initialize(),\n // so each of the four adapters logged a \"Capacity tracker uninitialized\"\n // WARN and returned a hardcoded 100k-token fallback. The fallback\n // surfaced in doctor's output as \"Capacity: 100% remaining\" — a\n // fictional reading, not a real one. The tracker is per-process and\n // idempotent under createCapacityTracker, so initializing on first\n // read is safe.\n if (this.capacityTracker === null) {\n this.initCapacityTracker();\n }\n const tracker = this.capacityTracker;\n if (tracker === null) {\n // Unreachable in practice — initCapacityTracker assigns the field —\n // but keep the type-safe path for the impossible case.\n throw new Error(`Capacity tracker initialization failed for ${this.name}`);\n }\n return Promise.resolve(tracker.getCapacity());\n }\n\n /**\n * Records usage from a response for capacity tracking.\n */\n protected recordUsage(response: CliResponse): void {\n if (this.capacityTracker !== null) {\n this.capacityTracker.recordUsage(response.usage);\n }\n }\n\n /**\n * Parses version from CLI output.\n */\n protected parseVersion(output: string): string {\n // Handle common version formats:\n // \"2.0.76 (Claude Code)\"\n // \"0.22.5\"\n // \"codex-cli 0.77.0\"\n const match = /(\\d+\\.\\d+\\.\\d+)/.exec(output);\n return match?.[1] ?? '0.0.0';\n }\n\n /**\n * Checks version compatibility.\n */\n protected checkVersionCompatibility(version: string): VersionStatus {\n const requirements = CLI_VERSION_REQUIREMENTS[this.name];\n\n const validVersion = semver.valid(version);\n if (validVersion === null) {\n return 'unsupported';\n }\n\n const isLtMinimum = semver.lt(validVersion, requirements.minimum);\n if (isLtMinimum) {\n return 'unsupported';\n }\n\n const hasBreaking = requirements.breaking.some((v) => semver.gte(validVersion, v));\n if (hasBreaking) {\n return 'breaking';\n }\n\n const isLtRecommended = semver.lt(validVersion, requirements.recommended);\n if (isLtRecommended) {\n return 'outdated';\n }\n\n return 'supported';\n }\n\n /**\n * Gets version status message.\n */\n protected getVersionMessage(status: VersionStatus, version: string): string | undefined {\n const requirements = CLI_VERSION_REQUIREMENTS[this.name];\n\n switch (status) {\n case 'unsupported':\n return `Version ${version} is not supported. Minimum: ${requirements.minimum}`;\n case 'breaking':\n return `Version ${version} has known compatibility issues`;\n case 'outdated':\n return `Consider upgrading to ${requirements.recommended}`;\n case 'supported':\n return undefined;\n }\n }\n\n /**\n * Creates a CLI error.\n */\n protected createError(code: CliErrorCode, message: string, cause?: Error): CliError {\n // One construction path (#6120): this carried its own copy of the\n // retryable-code list and its own `parseRetryAfterMs` call (#4373), so the\n // durable-capacity rule added to `createCliError` would otherwise have\n // applied only to adapters that happened to use the helper.\n return createCliError(code, message, this.name, cause);\n }\n\n /**\n * Normalizes CLI response to common format.\n */\n protected normalizeResponse(\n text: string,\n usage?: TokenUsage,\n extra?: Partial<CliResponse>\n ): CliResponse {\n return {\n text,\n ...(usage !== undefined && { usage }),\n ...extra,\n };\n }\n\n /**\n * Delays for the specified milliseconds.\n */\n protected delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/**\n * CLI Timeout Helpers\n *\n * Task complexity estimation for timeout selection.\n * Separated from timeout values (which live in config/timeouts.ts).\n *\n * @module cli-adapters/cli-timeout-helpers\n * (Source: Issue #984 — Centralize timeout configuration)\n */\n\nimport type { TaskComplexity } from '../config/timeouts.js';\nimport { getCliTimeout } from '../config/timeouts.js';\nimport { detectTaskCategory } from '../config/task-specialization.js';\nimport { getOutcomeStore } from '../orchestration/outcomes/outcome-store.js';\nimport type { CliNameLiteral } from '../config/model-capabilities-types.js';\n\n/**\n * Estimate task complexity from task description.\n *\n * Heuristics based on testing:\n * - Simple: Single function, quick query, < 5 files\n * - Standard: Multi-file changes, moderate analysis\n * - Complex: Codebase-wide, deep analysis, architecture\n *\n * @param taskDescription - Description of the task\n * @returns Estimated complexity\n */\nexport function estimateTaskComplexity(taskDescription: string): TaskComplexity {\n const lower = taskDescription.toLowerCase();\n\n // Complex indicators\n const complexIndicators = [\n 'codebase',\n 'architecture',\n 'refactor',\n 'all files',\n 'entire',\n 'comprehensive',\n 'deep analysis',\n 'system-wide',\n 'security',\n 'audit',\n 'vulnerability',\n ];\n if (complexIndicators.some((indicator) => lower.includes(indicator))) {\n return 'complex';\n }\n\n // Simple indicators\n // Testing and exploration demoted to simple per weather data (99%+ success) (#1401)\n const simpleIndicators = [\n 'single',\n 'quick',\n 'one function',\n 'simple',\n 'small',\n 'brief',\n 'short',\n 'run tests',\n 'test suite',\n 'exploration',\n 'explore',\n ];\n if (simpleIndicators.some((indicator) => lower.includes(indicator))) {\n return 'simple';\n }\n\n // Default to standard\n return 'standard';\n}\n\n// ============================================================================\n// Adaptive Timeout (#1534)\n// ============================================================================\n\n/** Minimum successful outcomes required before adaptive timeout kicks in. */\nexport const ADAPTIVE_TIMEOUT_MIN_SAMPLES = 10;\n\n/** Safety margin applied to p95 duration (p95 * margin = adaptive timeout). */\nexport const ADAPTIVE_TIMEOUT_MARGIN = 1.2;\n\n/**\n * Compute p95 of a sorted array of numbers.\n * Returns the value at the 95th percentile index.\n */\nfunction computeP95(sorted: readonly number[]): number {\n if (sorted.length === 0) return 0;\n const idx = Math.ceil(sorted.length * 0.95) - 1;\n return sorted[Math.min(idx, sorted.length - 1)] as number;\n}\n\n/** Options for adaptive timeout computation. */\nexport interface AdaptiveTimeoutOptions {\n /** Override the outcome store (for testing). */\n readonly store?: import('../orchestration/outcomes/outcome-store.js').OutcomeStore;\n}\n\n/**\n * Get adaptive timeout for a CLI + task description pair.\n *\n * Uses historical outcome data to compute p95 execution duration.\n * When sufficient samples exist, returns max(static_timeout, p95 * 1.2).\n * Falls back to static keyword-based timeout when data is sparse.\n *\n * @param cli - CLI name\n * @param taskDescription - Task description for complexity + category detection\n * @param options - Optional overrides (e.g., custom store for testing)\n * @returns Timeout in milliseconds (never less than static timeout)\n */\nexport function getAdaptiveTimeout(\n cli: string,\n taskDescription: string,\n options?: AdaptiveTimeoutOptions\n): number {\n const complexity = estimateTaskComplexity(taskDescription);\n const staticTimeout = getCliTimeout(cli, complexity);\n\n const match = detectTaskCategory(taskDescription);\n if (match === null) return staticTimeout;\n\n const store = options?.store ?? getOutcomeStore();\n const outcomes = store.query({\n cli: cli as CliNameLiteral,\n category: match.category,\n success: true,\n });\n\n if (outcomes.length < ADAPTIVE_TIMEOUT_MIN_SAMPLES) return staticTimeout;\n\n const durations = outcomes.map((o) => o.durationMs).sort((a, b) => a - b);\n const p95 = computeP95(durations);\n const adaptiveTimeout = Math.round(p95 * ADAPTIVE_TIMEOUT_MARGIN);\n\n return Math.max(staticTimeout, adaptiveTimeout);\n}\n","/**\n * CLI Timeout Profiles - Configurable timeouts per CLI tool.\n *\n * Delegates to `config/timeouts.ts` (canonical source, Issue #984).\n * This file provides backward-compatible re-exports.\n *\n * @module cli-adapters/cli-timeout-profiles\n * (Source: Issue #357, CLI delegation testing 2026-01-18)\n */\n\nimport {\n CLI_TIMEOUTS,\n getCliTimeout,\n type TaskComplexity,\n type TimeoutProfile,\n} from '../config/timeouts.js';\nimport {\n estimateTaskComplexity as _estimateTaskComplexity,\n getAdaptiveTimeout,\n} from './cli-timeout-helpers.js';\n\n// Re-export types for backward compatibility\nexport type { TimeoutProfile, TaskComplexity };\n\n/** Per-CLI timeout profiles. Canonical source: `config/timeouts.ts`. */\nexport const CLI_TIMEOUT_PROFILES: Record<string, TimeoutProfile> = {\n claude: CLI_TIMEOUTS.claude,\n gemini: CLI_TIMEOUTS.gemini,\n codex: CLI_TIMEOUTS.codex,\n};\n\n/** Default timeout profile. Canonical source: `config/timeouts.ts`. */\nexport const DEFAULT_TIMEOUT_PROFILE: TimeoutProfile = CLI_TIMEOUTS.default;\n\n/** Get timeout for a task. Canonical source: `config/timeouts.ts`. */\nexport function getTimeoutForTask(cli: string, complexity: TaskComplexity): number {\n return getCliTimeout(cli, complexity);\n}\n\n/** Estimate task complexity from description. Canonical: `cli-timeout-helpers.ts`. */\nexport function estimateTaskComplexity(taskDescription: string): TaskComplexity {\n return _estimateTaskComplexity(taskDescription);\n}\n\n/**\n * Get timeout with automatic complexity estimation.\n * Uses adaptive timeout from outcome history when sufficient data exists (#1534).\n */\nexport function getTimeoutForTaskAuto(cli: string, taskDescription: string): number {\n return getAdaptiveTimeout(cli, taskDescription);\n}\n","import { getErrorMessage } from '../core/index.js';\n\n/**\n * Async Utilities\n *\n * Centralized async helper functions for delay, timeout, and promise utilities.\n * Consolidates 9+ duplicate sleep/delay implementations across the codebase.\n *\n * @module utils/async-utils\n * (Source: LOOP H-K consolidation)\n */\n\n// ============================================================================\n// Delay / Sleep\n// ============================================================================\n\n/**\n * Creates a promise that resolves after the specified delay.\n * Alias: `delay` (both names are exported for compatibility)\n *\n * @param ms - Delay in milliseconds\n * @returns Promise that resolves after the delay\n *\n * @example\n * ```typescript\n * await sleep(1000); // Wait 1 second\n * await delay(500); // Wait 500ms (alias)\n * ```\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Alias for `sleep()` - provided for compatibility with existing code.\n */\nexport const delay = sleep;\n\n// ============================================================================\n// Timeout Wrapper\n// ============================================================================\n\n/**\n * Result type for withTimeout operations.\n */\nexport type TimeoutResult<T> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly error: string };\n\n/**\n * Wraps a promise with a timeout.\n * Returns an error result if the timeout is exceeded.\n *\n * @param promise - The promise to wrap\n * @param timeoutMs - Timeout in milliseconds\n * @param errorMessage - Error message if timeout is exceeded\n * @returns A result object with either the value or an error\n *\n * @example\n * ```typescript\n * const result = await withTimeout(\n * fetchData(),\n * 5000,\n * 'Request timed out after 5s'\n * );\n *\n * if (result.ok) {\n * console.log(result.value);\n * } else {\n * console.error(result.error);\n * }\n * ```\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n errorMessage: string\n): Promise<TimeoutResult<T>> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(errorMessage));\n }, timeoutMs);\n });\n\n try {\n const result = await Promise.race([promise, timeoutPromise]);\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n return { ok: true, value: result };\n } catch (error) {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n return { ok: false, error: getErrorMessage(error) };\n }\n}\n\n// ============================================================================\n// Promise Utilities\n// ============================================================================\n\n/**\n * Executes promises in sequence, one at a time.\n *\n * @param tasks - Array of functions that return promises\n * @returns Array of results in order\n *\n * @example\n * ```typescript\n * const results = await sequence([\n * () => fetch('/api/1'),\n * () => fetch('/api/2'),\n * ]);\n * ```\n */\nexport async function sequence<T>(tasks: ReadonlyArray<() => Promise<T>>): Promise<T[]> {\n const results: T[] = [];\n for (const task of tasks) {\n results.push(await task());\n }\n return results;\n}\n","/**\n * nexus-agents/cli-adapters - Unified CLI Retry Loop\n *\n * CLI-specific retry loop used by all CLI adapters (base + Gemini).\n * Supports optional circuit-breaker integration, returns CliResponse\n * with retryCount, and maps to FailureCategory for breaker tracking.\n *\n * Sibling implementation (see #2230): adapters/retry.ts holds the\n * generic, type-parameterized `withRetry<T>` for non-CLI use. Don't\n * reach for that one when you need circuit-breaker coupling; don't\n * reach for this one from non-CLI code. Math primitives differ\n * deliberately:\n * - this file: 1-indexed attempt, +0..30% jitter, cap-after\n * - adapters/retry.ts: 0-indexed attempt, ±jitterFactor, cap-before-jitter\n *\n * If you find yourself writing a third retry loop: stop, run\n * `consensus_vote` with scope_steward in the panel, and pick whichever\n * of these two fits — don't add a third.\n *\n * (Source: Issue #1596 — Extract shared prompt utils and rate-limit patterns)\n */\n\nimport type { Result } from '../core/index.js';\nimport { err, ok, getRandomProvider } from '../core/index.js';\nimport type { ILogger } from '../core/index.js';\nimport type { CliResponse, CliError, CliErrorCode, CliName } from './types.js';\nimport type { ICircuitBreaker, FailureCategory } from './circuit-breaker-types.js';\nimport { delay } from '../utils/async-utils.js';\nimport { RETRYABLE_ERROR_CODES } from './cli-error-helpers.js';\nimport { isCallerCancelled } from '../adapters/abort-utils.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CliRetryLoopConfig {\n readonly maxRetries: number;\n readonly allowRetry: boolean;\n readonly baseDelayMs: number;\n readonly maxDelayMs: number;\n readonly circuitBreaker?: ICircuitBreaker | null;\n readonly cli: CliName;\n readonly logger: ILogger;\n}\n\nexport interface CliRetryResult {\n readonly response: CliResponse;\n readonly retryCount: number;\n}\n\n// ============================================================================\n// Retry Logic (moved from gemini-adapter-helpers.ts)\n// ============================================================================\n\n/**\n * Calculates exponential backoff delay with jitter.\n *\n * @param attempt - Current attempt number (1-indexed)\n * @param baseDelayMs - Base delay in milliseconds\n * @param maxDelayMs - Maximum delay cap in milliseconds\n * @returns Delay in milliseconds with jitter applied\n */\nexport function calculateBackoffDelay(\n attempt: number,\n baseDelayMs: number,\n maxDelayMs: number\n): number {\n const exponentialDelay = baseDelayMs * Math.pow(2, attempt - 1);\n const jitter = getRandomProvider().random() * 0.3 * exponentialDelay;\n const delayMs = exponentialDelay + jitter;\n return Math.min(delayMs, maxDelayMs);\n}\n\n/**\n * Resolve how long to wait before the next attempt (#4373).\n *\n * Prefers a provider-supplied `retryAfterMs` over the computed exponential\n * backoff — a provider that names its window knows better than our guess — but\n * still clamps to the caller's ceiling so a CLI claiming a multi-hour window\n * cannot wedge the retry loop. A zero or negative hint is ignored rather than\n * retried instantly.\n */\nexport function resolveRetryDelayMs(\n error: CliError,\n attempt: number,\n baseDelayMs: number,\n maxDelayMs: number\n): number {\n const hint = error.retryAfterMs;\n if (hint !== undefined && hint > 0) {\n return Math.min(hint, maxDelayMs);\n }\n return calculateBackoffDelay(attempt, baseDelayMs, maxDelayMs);\n}\n\n/** Determines if an error code is retryable. */\nexport function isRetryableError(code: CliErrorCode): boolean {\n return RETRYABLE_ERROR_CODES.has(code);\n}\n\n/**\n * Categorizes a CLI error for circuit breaker tracking.\n * Returns a FailureCategory compatible with the circuit breaker.\n */\nexport function categorizeError(error: CliError): FailureCategory {\n switch (error.code) {\n case 'TIMEOUT':\n return 'timeout';\n case 'RATE_LIMITED':\n return 'rate_limit';\n case 'NOT_AUTHENTICATED':\n return 'authentication';\n case 'CONNECTION_ERROR':\n return 'connection';\n default:\n return 'unknown';\n }\n}\n\n// ============================================================================\n// Unified Retry Loop\n// ============================================================================\n\n/**\n * Executes a CLI operation with retry logic and optional circuit breaker.\n *\n * Used by both BaseCliAdapter (no circuit breaker) and GeminiCliAdapter\n * (with circuit breaker) to eliminate duplicate retry implementations.\n */\nexport async function executeCliRetryLoop(\n executeFn: () => Promise<Result<CliResponse, CliError>>,\n config: CliRetryLoopConfig\n): Promise<Result<CliRetryResult, CliError>> {\n const maxAttempts = config.allowRetry ? config.maxRetries + 1 : 1;\n let lastError: CliError | undefined;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const result = await executeFn();\n\n if (result.ok) {\n return ok({ response: result.value, retryCount: attempt - 1 });\n }\n\n lastError = result.error;\n\n // #6691: a caller cancel is neither a health signal nor worth retrying.\n if (isCallerCancelled(lastError)) return err(lastError);\n\n // Record failure with circuit breaker if present\n if (config.circuitBreaker !== undefined && config.circuitBreaker !== null) {\n config.circuitBreaker.recordFailure(categorizeError(lastError));\n }\n\n // Check if we should retry\n if (!shouldRetry(lastError, attempt, maxAttempts, config.circuitBreaker)) {\n return err(lastError);\n }\n\n const delayMs = resolveRetryDelayMs(lastError, attempt, config.baseDelayMs, config.maxDelayMs);\n config.logger.debug('Retrying CLI execution', {\n cli: config.cli,\n attempt,\n nextAttempt: attempt + 1,\n delayMs: Math.round(delayMs),\n });\n\n await delay(delayMs);\n }\n\n return err(\n lastError ?? {\n code: 'UNKNOWN',\n message: 'Max retries exceeded',\n cli: config.cli,\n retryable: false,\n }\n );\n}\n\nfunction shouldRetry(\n error: CliError,\n attempt: number,\n maxAttempts: number,\n circuitBreaker?: ICircuitBreaker | null\n): boolean {\n if (attempt >= maxAttempts) return false;\n if (!error.retryable) return false;\n if (!isRetryableError(error.code)) return false;\n if (circuitBreaker?.getState() === 'open') return false;\n return true;\n}\n","/**\n * Environment-variable allowlist for spawned CLI subprocesses (#2865).\n *\n * `spawnSubprocess` previously passed the entire `process.env` to every\n * spawned CLI (claude / gemini / codex / opencode) — only `CLAUDECODE`\n * was stripped. That leaked cross-vendor API keys: the gemini CLI\n * received `ANTHROPIC_API_KEY` and `OPENAI_API_KEY`, the codex CLI\n * received `GOOGLE_AI_API_KEY`, and so on. A buggy or malicious CLI\n * could exfiltrate keys it has no business seeing.\n *\n * `buildChildEnv()` constructs a curated child environment instead:\n * base infrastructure vars that every CLI needs, plus ONLY the spawned\n * CLI's own vendor credential(s). Sensitive credentials and secrets\n * (including `NEXUS_*` keys, tokens, and secrets) are stripped by\n * default unless explicitly allowed via {@link NEXUS_SUBPROCESS_EXTRA_ENV}.\n *\n * @module cli-adapters/subprocess-env\n */\n\nimport type { CliName } from './types.js';\nimport { parseBoolValue } from '../config/defaults-env.js';\n\n/**\n * Marker stamped on EVERY spawned-CLI child env (#4033): how deep we are in the\n * nexus → CLI subprocess nesting. A child CLI (e.g. `opencode run`) that is\n * itself configured to auto-start a `nexus-agents --mode=server` MCP server\n * would otherwise deadlock the voter — the nested server attaches to the child's\n * stdio and blocks the child's MCP handshake, so the voter never returns its\n * JSON. The server bootstrap reads this marker and refuses to start when nested\n * (see `cli-server.ts`). Distinct from the codex-only `NEXUS_MCP_DEPTH` guard so\n * the two cannot interfere. `NEXUS_`-prefixed, so it survives the allowlist (and\n * the `=0` full-passthrough hatch) and reaches the grandchild MCP server.\n */\nexport const NEXUS_SUBPROCESS_DEPTH_ENV = 'NEXUS_SUBPROCESS_DEPTH';\n\n/** Read the subprocess nesting depth from an env bag; clamps missing/junk to 0. */\nexport function readSubprocessDepth(env: NodeJS.ProcessEnv = process.env): number {\n const raw = Number.parseInt(env[NEXUS_SUBPROCESS_DEPTH_ENV] ?? '0', 10);\n return Number.isFinite(raw) && raw > 0 ? raw : 0;\n}\n\n/** Exact-match infrastructure vars every spawned CLI legitimately needs. */\nconst BASE_ENV_EXACT: readonly string[] = [\n 'PATH',\n 'HOME',\n 'USER',\n 'LOGNAME',\n 'SHELL',\n 'LANG',\n 'TERM',\n 'TZ',\n 'TMPDIR',\n 'TEMP',\n 'TMP',\n 'HTTP_PROXY',\n 'HTTPS_PROXY',\n 'NO_PROXY',\n 'ALL_PROXY',\n 'http_proxy',\n 'https_proxy',\n 'no_proxy',\n 'all_proxy',\n 'NODE_OPTIONS',\n 'NODE_PATH',\n 'NODE_ENV',\n 'NODE_EXTRA_CA_CERTS',\n 'SSL_CERT_FILE',\n 'SSL_CERT_DIR',\n];\n\n/** Prefix-match infrastructure var families. */\nconst BASE_ENV_PREFIXES: readonly string[] = [\n 'LC_', // locale: LC_ALL, LC_CTYPE, …\n 'NEXUS_', // nexus-agents config + nested-run credentials (a child may be a nested nexus-agents)\n 'npm_config_', // npm/node resolution config: registry, proxy, …\n];\n\n/**\n * Per-CLI vendor credentials. The spawned CLI gets ONLY its own\n * vendor's key(s) — not every vendor's. `opencode` can route to any\n * provider via its config, so it gets the full set.\n */\nconst CLI_VENDOR_KEYS: Record<CliName, readonly string[]> = {\n claude: ['ANTHROPIC_API_KEY'],\n gemini: ['GOOGLE_AI_API_KEY', 'GEMINI_API_KEY', 'GOOGLE_API_KEY'],\n codex: ['OPENAI_API_KEY'],\n opencode: [\n 'ANTHROPIC_API_KEY',\n 'OPENAI_API_KEY',\n 'GOOGLE_AI_API_KEY',\n 'GEMINI_API_KEY',\n 'GOOGLE_API_KEY',\n 'OPENROUTER_API_KEY',\n ],\n};\n\n/**\n * Env var naming ADDITIONAL var names to forward to spawned CLIs\n * (comma/whitespace-separated). The granular alternative to the\n * `NEXUS_SUBPROCESS_ENV_ALLOWLIST=false` hammer: for a custom gateway whose auth key\n * is neither `NEXUS_`-prefixed nor a known vendor key (#4037), name it here to\n * forward ONLY that var while keeping full cross-vendor isolation for everything\n * else. `NEXUS_`-prefixed itself, so it also reaches nested runs.\n */\nexport const NEXUS_SUBPROCESS_EXTRA_ENV = 'NEXUS_SUBPROCESS_EXTRA_ENV';\n\n/** Parse the operator-configured extra-forward var names (empty when unset). */\nfunction readExtraEnvNames(env: NodeJS.ProcessEnv): readonly string[] {\n const raw = env[NEXUS_SUBPROCESS_EXTRA_ENV];\n if (raw === undefined || raw.trim() === '') return [];\n return raw\n .split(/[,\\s]+/)\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n}\n\n/**\n * Pattern matching secret or credential environment variable names that must\n * NEVER be implicitly forwarded to spawned CLI subprocesses by prefix match,\n * even when matching `NEXUS_` or another infrastructure prefix.\n *\n * Specific vendor keys in `CLI_VENDOR_KEYS` or operator-explicit extra variables\n * configured via {@link NEXUS_SUBPROCESS_EXTRA_ENV} are still honored.\n */\nconst BLOCKED_SECRET_PATTERN =\n /(?:^NEXUS_SECRET_)|(?:(?:^|_|[a-z])(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|CREDENTIALS)$)/i;\n\n/** True if `key` matches patterns designated for secrets/credentials. */\nfunction isBlockedSecretVar(key: string): boolean {\n return BLOCKED_SECRET_PATTERN.test(key);\n}\n\n/**\n * True if `key` is permitted for a CLI whose vendor keys are `vendorKeys`, plus\n * any operator-allowlisted `extraEnv` names (#4037).\n */\nfunction isAllowed(\n key: string,\n vendorKeys: readonly string[],\n extraEnv: readonly string[]\n): boolean {\n if (BASE_ENV_EXACT.includes(key)) return true;\n if (vendorKeys.includes(key)) return true;\n if (extraEnv.includes(key)) return true;\n if (isBlockedSecretVar(key)) return false;\n return BASE_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));\n}\n\n/**\n * Builds the environment for a spawned CLI subprocess: base\n * infrastructure vars plus only `cliName`'s own vendor credentials.\n * Cross-vendor API keys are dropped (#2865).\n *\n * `CLAUDECODE` is never forwarded — a nested CLI seeing it would\n * believe it's already inside Claude Code, breaking nested sessions\n * (the pre-#2865 behavior also stripped it explicitly).\n *\n * Granular extension: {@link NEXUS_SUBPROCESS_EXTRA_ENV} forwards named extra\n * vars (e.g. a custom gateway key) while keeping cross-vendor isolation — prefer\n * it over the blunt `=false` hatch (#4037).\n *\n * Escape hatch: the boolean `NEXUS_SUBPROCESS_ENV_ALLOWLIST` (default on)\n * set to `false`/`0` restores the pre-#2865 full-passthrough behavior (minus\n * `CLAUDECODE`) — a field un-break if the allowlist ever drops a var a CLI\n * needs. Parsed with the shared `parseBoolValue` accept-set (#5155).\n */\nexport function buildChildEnv(cliName: CliName): NodeJS.ProcessEnv {\n const source = process.env;\n const childEnv: NodeJS.ProcessEnv = {};\n // Stamp the incremented nesting depth (#4033) LAST in both branches so it\n // overrides any inherited value rather than copying the parent's depth.\n const nextDepth = String(readSubprocessDepth(source) + 1);\n\n if (!parseBoolValue(source['NEXUS_SUBPROCESS_ENV_ALLOWLIST'], true)) {\n for (const [key, value] of Object.entries(source)) {\n if (value !== undefined && key !== 'CLAUDECODE') childEnv[key] = value;\n }\n childEnv[NEXUS_SUBPROCESS_DEPTH_ENV] = nextDepth;\n stampNoRelaunch(cliName, childEnv);\n return childEnv;\n }\n\n const vendorKeys = CLI_VENDOR_KEYS[cliName];\n const extraEnv = readExtraEnvNames(source);\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) continue;\n if (key === 'CLAUDECODE') continue;\n if (isAllowed(key, vendorKeys, extraEnv)) childEnv[key] = value;\n }\n childEnv[NEXUS_SUBPROCESS_DEPTH_ENV] = nextDepth;\n stampNoRelaunch(cliName, childEnv);\n return childEnv;\n}\n\n/**\n * gemini-cli relaunches itself as a child process unless told not to, and the\n * relaunching parent does not stop its worker on SIGTERM (#6680). The adapter\n * kills the child's tracked descendant tree anyway (#6703); this keeps the\n * worker the process the adapter spawned. Harmless for a binary that does not\n * read it.\n */\nfunction stampNoRelaunch(cliName: CliName, childEnv: NodeJS.ProcessEnv): void {\n if (cliName === 'gemini') childEnv['GEMINI_CLI_NO_RELAUNCH'] = 'true';\n}\n\n/** Test/introspection accessor for the per-CLI vendor-key map. */\nexport function getCliVendorKeys(): Readonly<Record<CliName, readonly string[]>> {\n return CLI_VENDOR_KEYS;\n}\n","/**\n * Signal a CLI subprocess together with the processes it spawned (#6680).\n *\n * A CLI can relaunch itself as a child (gemini-cli does unless\n * `GEMINI_CLI_NO_RELAUNCH` is set), so signalling only the spawned PID left\n * the real worker running as an orphan after a cancel or a timeout. These\n * helpers collect the child's descendant PIDs first, then signal the child\n * and exactly those PIDs, never a process group. The CLI stays in the\n * server's process group, so a harness that kills that group, even with\n * SIGKILL, still reaches every CLI process as it did before.\n *\n * Descendants are read from `/proc/<pid>/task/<tid>/children` on Linux and\n * from `ps -A -o pid=,ppid=` on other POSIX systems. Where neither works\n * (Windows) only the direct child is signalled.\n *\n * Off Linux, `ps` runs asynchronously (#6718), so a signal pass does not block\n * the event loop for up to the `ps` timeout. The descendants are still\n * collected before anything is signalled: the async paths await the `ps`\n * result, then signal. The one synchronous `ps` left is the `exit` hook's,\n * because a `process.on('exit')` handler cannot await. On Linux the `/proc`\n * walk is synchronous everywhere; it reads small files and spawns nothing.\n *\n * PID reuse (#6714): a PID is only a name, and the kernel hands it out again\n * once its process is reaped. So the fresh walk is skipped once the child has\n * exited (Node reaps it at once, freeing its PID), and on Linux each\n * descendant is recorded with its start time (`/proc/<pid>/stat` field 22)\n * and re-verified before every later signal or liveness check; a mismatch or\n * an unreadable stat means the process is gone and the PID is skipped. Other\n * platforms expose no start time cheaply, so there a descendant is recorded\n * without one and signalled by PID alone, as before.\n *\n * Known gap (#6718): a descendant that double-forked and was reparented to\n * init, or to another subreaper, before the signal is no longer in the tree,\n * so it is neither signalled nor escalated. The alternatives were weighed and\n * not adopted. A process-group or session kill cannot be used: the CLIs share\n * the server's group (#6703), and a daemon that double-forks usually calls\n * `setsid` as well. `PR_SET_CHILD_SUBREAPER` needs a `prctl` call Node does\n * not expose, so it would take a native addon or a wrapper binary. A cgroup\n * needs a delegated, writable cgroup v2 subtree, which a plain server does not\n * have. And a daemon that detached itself on purpose may be meant to outlive\n * the CLI. If a real CLI is seen leaking one, the cheapest dependency-free\n * option on Linux is to tag each spawn's environment with a unique marker and\n * match it in `/proc/<pid>/environ`, which a double fork inherits.\n *\n * @module cli-adapters/process-tree-kill\n */\n\nimport { execFile, execFileSync } from 'node:child_process';\nimport { readdirSync, readFileSync } from 'node:fs';\nimport type { ChildProcess } from 'node:child_process';\nimport { readProcStartTime } from './proc-start-time.js';\n\n/** Upper bound on descendants collected, so a fork bomb cannot stall the walk. */\nconst MAX_DESCENDANTS = 1_000;\n\n/** `ps` arguments listing every process with its parent. */\nconst PS_ARGS = ['-A', '-o', 'pid=,ppid='];\n\n/** Bound on one `ps` run, synchronous or not. */\nconst PS_TIMEOUT_MS = 2_000;\n\n/**\n * True for a child `spawn` actually started. Duck-typed on the fields only a\n * real spawn sets: a test double lacks them, so its made-up PID is never\n * walked or signalled.\n */\nfunction isSpawnedProcess(child: ChildProcess): boolean {\n return (\n typeof child.pid === 'number' &&\n typeof child.spawnfile === 'string' &&\n Array.isArray(child.spawnargs)\n );\n}\n\n/** Direct children of `pid` from `/proc` (Linux), or undefined when unreadable. */\nfunction procChildren(pid: number): number[] | undefined {\n try {\n const tasks = readdirSync(`/proc/${String(pid)}/task`);\n const kids: number[] = [];\n for (const tid of tasks) {\n const raw = readFileSync(`/proc/${String(pid)}/task/${tid}/children`, 'utf8');\n for (const token of raw.split(/\\s+/)) {\n const kid = Number(token);\n if (token !== '' && Number.isInteger(kid)) kids.push(kid);\n }\n }\n return kids;\n } catch {\n return undefined;\n }\n}\n\n/** Parse `ps -o pid=,ppid=` output into a `ppid → children` map. */\nfunction parsePsChildMap(out: string): Map<number, number[]> {\n const map = new Map<number, number[]>();\n for (const line of out.split('\\n')) {\n const [pidText, ppidText] = line.trim().split(/\\s+/);\n const pid = Number(pidText);\n const ppid = Number(ppidText);\n if (!Number.isInteger(pid) || !Number.isInteger(ppid)) continue;\n map.set(ppid, [...(map.get(ppid) ?? []), pid]);\n }\n return map;\n}\n\n/**\n * A `ppid → children` map from a SYNCHRONOUS `ps` (non-Linux POSIX), or\n * undefined when unavailable. It blocks the event loop for up to\n * {@link PS_TIMEOUT_MS}, so only the synchronous paths use it (#6718).\n */\nfunction psChildMapSync(): Map<number, number[]> | undefined {\n if (process.platform === 'win32') return undefined;\n try {\n return parsePsChildMap(\n execFileSync('ps', PS_ARGS, { encoding: 'utf8', timeout: PS_TIMEOUT_MS })\n );\n } catch {\n return undefined;\n }\n}\n\n/** The same map from an asynchronous `ps`; resolves undefined when unavailable. */\nfunction psChildMapAsync(): Promise<Map<number, number[]> | undefined> {\n if (process.platform === 'win32') return Promise.resolve(undefined);\n return new Promise((resolve) => {\n execFile('ps', PS_ARGS, { encoding: 'utf8', timeout: PS_TIMEOUT_MS }, (error, stdout) => {\n resolve(error === null ? parsePsChildMap(stdout) : undefined);\n });\n });\n}\n\n/** Every descendant PID of `root`, breadth-first, capped at {@link MAX_DESCENDANTS}. */\nfunction walkDescendants(root: number, childrenOf: (pid: number) => number[]): number[] {\n const seen = new Set<number>([root]);\n const out: number[] = [];\n const queue = [root];\n while (queue.length > 0 && out.length < MAX_DESCENDANTS) {\n const pid = queue.shift() as number;\n for (const kid of childrenOf(pid)) {\n if (seen.has(kid)) continue;\n seen.add(kid);\n out.push(kid);\n queue.push(kid);\n }\n }\n return out;\n}\n\n/**\n * Every descendant PID of `root`, synchronously. Empty when the platform\n * offers no way to list them.\n */\nfunction collectDescendants(root: number): number[] {\n const useProc = process.platform === 'linux' && procChildren(root) !== undefined;\n const psMap = useProc ? undefined : psChildMapSync();\n return walkDescendants(root, (pid) => (useProc ? procChildren(pid) : psMap?.get(pid)) ?? []);\n}\n\n/** Every descendant PID of `root` from an asynchronous `ps` (#6718). */\nasync function collectDescendantsAsync(root: number): Promise<number[]> {\n const psMap = await psChildMapAsync();\n return walkDescendants(root, (pid) => psMap?.get(pid) ?? []);\n}\n\n/** Only ESRCH proves a process is gone; EPERM means it exists but is not ours. */\nfunction isPidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error: unknown) {\n return (error as NodeJS.ErrnoException).code !== 'ESRCH';\n }\n}\n\n/**\n * A descendant as it was when collected. `startTime` is REQUIRED so every\n * producer has to decide it: the `/proc/<pid>/stat` start time on Linux, or\n * `undefined` where the platform offers none, which means \"signal by PID\n * alone\" (#6714).\n */\ninterface KnownProcess {\n readonly pid: number;\n readonly startTime: string | undefined;\n}\n\n/** The OS operations the tree kill performs; a seam so tests can stage PID reuse. */\ninterface ProcessTreeOps {\n /** Every descendant PID of `root`, or empty when they cannot be listed. Synchronous. */\n readonly collectDescendants: (root: number) => number[];\n /**\n * The same without blocking the event loop, or undefined where the\n * synchronous walk does not block (Linux `/proc`). REQUIRED so every\n * producer decides; every path but the `exit` hook uses it when set (#6718).\n */\n readonly collectDescendantsAsync: ((root: number) => Promise<number[]>) | undefined;\n /** True when start times are available at all (Linux). */\n readonly hasStartTimes: boolean;\n /** The start time of `pid` now, or undefined when it cannot be read (gone). */\n readonly readStartTime: (pid: number) => string | undefined;\n readonly isPidAlive: (pid: number) => boolean;\n readonly kill: (pid: number, signal: NodeJS.Signals) => void;\n}\n\nconst defaultOps: ProcessTreeOps = {\n collectDescendants,\n collectDescendantsAsync: process.platform === 'linux' ? undefined : collectDescendantsAsync,\n hasStartTimes: process.platform === 'linux',\n readStartTime: readProcStartTime,\n isPidAlive,\n kill: (pid, signal) => {\n process.kill(pid, signal);\n },\n};\n\n/**\n * Record each PID with its start time. On Linux a PID whose stat cannot be\n * read has already exited and is dropped; elsewhere none has a start time.\n */\nfunction identify(pids: readonly number[], ops: ProcessTreeOps): KnownProcess[] {\n if (!ops.hasStartTimes) return pids.map((pid) => ({ pid, startTime: undefined }));\n const out: KnownProcess[] = [];\n for (const pid of pids) {\n const startTime = ops.readStartTime(pid);\n if (startTime !== undefined) out.push({ pid, startTime });\n }\n return out;\n}\n\n/**\n * True while `known` is still the process that was collected. A recorded\n * start time must read back identically; without one (non-Linux) only\n * whether the PID exists can be checked.\n */\nfunction isSameProcess(known: KnownProcess, ops: ProcessTreeOps): boolean {\n if (known.startTime === undefined) return ops.isPidAlive(known.pid);\n return ops.readStartTime(known.pid) === known.startTime;\n}\n\n/** The first entry recorded for each PID, in order. */\nfunction uniqueByPid(entries: readonly KnownProcess[]): KnownProcess[] {\n const byPid = new Map<number, KnownProcess>();\n for (const entry of entries) {\n if (!byPid.has(entry.pid)) byPid.set(entry.pid, entry);\n }\n return [...byPid.values()];\n}\n\nfunction hasExited(child: ChildProcess): boolean {\n return child.exitCode !== null || child.signalCode !== null;\n}\n\n/**\n * CLI process trees still running, so server shutdown can end them (#6680).\n *\n * The CLIs share the server's process group, so a signal to the whole group\n * reaches them directly. A signal to the server's PID alone does not: the\n * shutdown path SIGTERMs these trees instead, and a synchronous `exit` hook\n * SIGKILLs whatever is left.\n */\nconst liveTrees = new Map<ChildProcess, KnownProcess[]>();\nlet exitHookInstalled = false;\n\n/**\n * The PID to walk for fresh descendants, or undefined when no walk may be\n * made: a test double, or a child that has exited and whose PID may already\n * name another process (#6714).\n */\nfunction walkablePid(child: ChildProcess): number | undefined {\n return isSpawnedProcess(child) && !hasExited(child) ? child.pid : undefined;\n}\n\n/**\n * Signal the child, then `fresh` merged with `known` and the tracked set, each\n * re-verified first and skipped if its PID now names a different process\n * (#6714). ESRCH (already gone) is ignored. Returns the descendants signalled.\n */\nfunction signalCollected(\n child: ChildProcess,\n signal: NodeJS.Signals,\n known: readonly KnownProcess[],\n fresh: readonly KnownProcess[],\n ops: ProcessTreeOps\n): KnownProcess[] {\n // A tracked tree may hold descendants an earlier signal collected (the shutdown path).\n const candidates = uniqueByPid([...known, ...(liveTrees.get(child) ?? []), ...fresh]);\n child.kill(signal);\n const tree = candidates.filter((entry) => isSameProcess(entry, ops));\n for (const entry of tree) {\n try {\n ops.kill(entry.pid, signal);\n } catch {\n // ESRCH: already gone.\n }\n }\n if (liveTrees.has(child)) liveTrees.set(child, tree);\n return tree;\n}\n\n/**\n * Signal the child and its descendants, synchronously. The descendants are\n * collected before anything is signalled — once a parent dies its children\n * are reparented and can no longer be found through it — and merged with\n * `known`, the set an earlier call collected, so a SIGKILL escalation still\n * reaches a grandchild whose parent exited on the SIGTERM.\n *\n * Once the child has exited no fresh walk is made and only `known` is\n * signalled (#6714). Returns the descendants still tracked: those signalled,\n * besides the child.\n *\n * Off Linux the walk is a synchronous `ps` that blocks the event loop, so in\n * production only the `exit` hook reaches this; every other path goes through\n * {@link terminateProcessTree} or {@link signalTrackedProcessTreesAsync} (#6718).\n */\nexport function signalProcessTree(\n child: ChildProcess,\n signal: NodeJS.Signals,\n known: readonly KnownProcess[],\n ops: ProcessTreeOps = defaultOps\n): KnownProcess[] {\n const pid = walkablePid(child);\n const fresh = pid === undefined ? [] : identify(ops.collectDescendants(pid), ops);\n return signalCollected(child, signal, known, fresh, ops);\n}\n\n/**\n * Signal without blocking the event loop (#6718). When no asynchronous walk is\n * needed (`ops.collectDescendantsAsync` unset, or no walk at all) the signals\n * are sent before this returns and the tree comes back as an array, exactly as\n * {@link signalProcessTree}. Otherwise the returned promise collects FIRST and\n * signals once the collection resolves.\n */\nfunction signalProcessTreeSoon(\n child: ChildProcess,\n signal: NodeJS.Signals,\n known: readonly KnownProcess[],\n ops: ProcessTreeOps\n): KnownProcess[] | Promise<KnownProcess[]> {\n const pid = walkablePid(child);\n const collectAsync = ops.collectDescendantsAsync;\n if (pid === undefined || collectAsync === undefined) {\n return signalProcessTree(child, signal, known, ops);\n }\n return collectAsync(pid).then(\n (pids) => {\n // The child may have exited, and its PID been reused, while `ps` ran (#6714).\n const fresh = hasExited(child) ? [] : identify(pids, ops);\n return signalCollected(child, signal, known, fresh, ops);\n },\n // A failed walk must not leave the child unsignalled or reject into a `void`.\n () => signalCollected(child, signal, known, [], ops)\n );\n}\n\n/**\n * True while the child, or any collected descendant that is still the same\n * process (#6714), is running.\n */\nexport function isProcessTreeAlive(\n child: ChildProcess,\n tree: readonly KnownProcess[],\n ops: ProcessTreeOps = defaultOps\n): boolean {\n if (!hasExited(child)) return true;\n return tree.some((entry) => isSameProcess(entry, ops));\n}\n\n/**\n * True once a tracked tree can be forgotten: its child has exited and no known\n * descendant is still the process that was collected. Without start times\n * (non-Linux) a live PID cannot be told from a reused one, so there a tree is\n * finished as soon as its child exits, as before #6714; retaining it could\n * leave a reused PID for the exit hook to SIGKILL.\n */\nfunction isFinishedTree(\n child: ChildProcess,\n known: readonly KnownProcess[],\n ops: ProcessTreeOps\n): boolean {\n if (!hasExited(child)) return false;\n return !ops.hasStartTimes || !isProcessTreeAlive(child, known, ops);\n}\n\n/** Forget every tracked tree that is finished. */\nfunction pruneFinishedTrees(ops: ProcessTreeOps): void {\n for (const [child, known] of liveTrees) {\n if (isFinishedTree(child, known, ops)) liveTrees.delete(child);\n }\n}\n\n/**\n * Track a spawned CLI, returning it. Only real spawns are tracked. On Linux\n * the tree stays tracked past the child's `close` while a descendant a signal\n * already collected is still the same running process, so a shutdown inside\n * the SIGKILL grace window still reaches a grandchild that ignored the\n * SIGTERM (#6714). Elsewhere it is forgotten on `close`, as before.\n */\nexport function trackProcessTree<T extends ChildProcess>(\n child: T,\n ops: ProcessTreeOps = defaultOps\n): T {\n if (!isSpawnedProcess(child)) return child;\n pruneFinishedTrees(ops);\n liveTrees.set(child, []);\n child.once('close', () => {\n if (isFinishedTree(child, liveTrees.get(child) ?? [], ops)) liveTrees.delete(child);\n });\n if (!exitHookInstalled) {\n exitHookInstalled = true;\n process.on('exit', () => {\n signalTrackedProcessTrees('SIGKILL');\n });\n }\n return child;\n}\n\n/**\n * Signal every tracked CLI tree; returns how many were signalled. SYNCHRONOUS\n * because the `exit` hook cannot await, so off Linux this is the one path that\n * still runs a blocking `ps` (#6718). Each tree's collected descendants are\n * remembered, so the exit hook's SIGKILL still reaches a grandchild whose\n * parent died on the shutdown SIGTERM.\n */\nexport function signalTrackedProcessTrees(\n signal: NodeJS.Signals,\n ops: ProcessTreeOps = defaultOps\n): number {\n pruneFinishedTrees(ops);\n let count = 0;\n for (const [child, known] of [...liveTrees]) {\n signalProcessTree(child, signal, known, ops);\n count++;\n }\n return count;\n}\n\n/**\n * {@link signalTrackedProcessTrees} for a caller that can await (the graceful\n * shutdown): off Linux each tree is collected by an asynchronous `ps` before\n * it is signalled (#6718). Resolves to how many trees were signalled.\n */\nexport async function signalTrackedProcessTreesAsync(\n signal: NodeJS.Signals,\n ops: ProcessTreeOps = defaultOps\n): Promise<number> {\n pruneFinishedTrees(ops);\n const trees = [...liveTrees];\n await Promise.all(\n trees.map(([child, known]) => Promise.resolve(signalProcessTreeSoon(child, signal, known, ops)))\n );\n return trees.length;\n}\n\n/**\n * Grace period before a SIGTERMed tree is SIGKILLed (#3026 finding 1). 5 s\n * gives a well-behaved child time to flush state and exit, and bounds how long\n * one that ignores SIGTERM keeps running.\n */\nexport const SIGKILL_GRACE_MS = 5_000;\n\n/**\n * SIGTERM the child and its descendants, then SIGKILL whatever of that tree is\n * still the same running process after `graceMs` (#6680, #6747). `onEscalate`\n * runs just before the SIGKILL, for the caller's log line. The escalation timer\n * is unref'd: a server exiting inside the grace window still reaches a tracked\n * tree through the exit hook.\n *\n * Where no asynchronous walk is needed (Linux) the SIGTERM is sent and the\n * timer armed before this returns. Off Linux the descendants are first\n * collected by an asynchronous `ps`, so the SIGTERM goes out, and the grace\n * period starts, once it resolves (#6718). Resolves to the descendants\n * signalled.\n */\nexport function terminateProcessTree(\n child: ChildProcess,\n graceMs: number = SIGKILL_GRACE_MS,\n onEscalate?: () => void,\n ops: ProcessTreeOps = defaultOps\n): Promise<KnownProcess[]> {\n const armEscalation = (tree: KnownProcess[]): KnownProcess[] => {\n const timer = setTimeout(() => {\n if (!isProcessTreeAlive(child, tree, ops)) return;\n onEscalate?.();\n void signalProcessTreeSoon(child, 'SIGKILL', tree, ops);\n }, graceMs);\n timer.unref();\n // A descendant can outlive the child's close, so only an empty tree cancels the check.\n if (tree.length === 0) {\n child.once('close', () => {\n clearTimeout(timer);\n });\n }\n return tree;\n };\n const signalled = signalProcessTreeSoon(child, 'SIGTERM', [], ops);\n if (Array.isArray(signalled)) return Promise.resolve(armEscalation(signalled));\n return signalled.then(armEscalation);\n}\n","/**\n * A process's start time from `/proc/<pid>/stat` (Linux), used to tell a\n * process from a later one that reused its PID (#6714). Together, a PID and\n * its start time name one process for as long as the kernel is up.\n *\n * @module cli-adapters/proc-start-time\n */\n\nimport { readFileSync } from 'node:fs';\n\n/** `/proc/<pid>/stat` field 22 is starttime; the split fields begin at field 3. */\nconst START_TIME_INDEX = 22 - 3;\n\n/**\n * Field 22 (starttime) of a `/proc/<pid>/stat` line, or undefined when it\n * does not parse. Field 2 (comm) is parenthesised and may itself contain\n * spaces and `)`, so the fields are split after the LAST `)`. A zombie\n * (state `Z`) has already died and only awaits its reaper, so it reads as\n * gone too: signalling it does nothing, and waiting on it would keep an\n * escalation armed where PID 1 never reaps orphans.\n */\nexport function parseProcStatStartTime(stat: string): string | undefined {\n const close = stat.lastIndexOf(')');\n if (close === -1) return undefined;\n const fields = stat\n .slice(close + 1)\n .trim()\n .split(/\\s+/);\n if (fields[0] === 'Z') return undefined;\n const startTime = fields[START_TIME_INDEX];\n return startTime !== undefined && /^\\d+$/.test(startTime) ? startTime : undefined;\n}\n\n/** The start time of `pid`, or undefined when its stat cannot be read (it is gone). */\nexport function readProcStartTime(pid: number): string | undefined {\n try {\n return parseProcStatStartTime(readFileSync(`/proc/${String(pid)}/stat`, 'utf8'));\n } catch {\n // ENOENT/ESRCH: the process is gone.\n return undefined;\n }\n}\n","/**\n * nexus-agents/utils - Type Coercion Utilities\n *\n * Safe type coercion helpers for parsing and validating unknown values.\n * Consolidates patterns used across parsers, validators, and adapters.\n *\n * @module utils/type-coercion\n */\n\n/**\n * Safely casts value to Record if it's a non-null, non-array object.\n *\n * @param value - Value to check\n * @returns Record if valid object, null otherwise\n *\n * @example\n * const data = asRecord(parsed);\n * if (data !== null) {\n * const name = asString(data.name);\n * }\n */\nexport function asRecord(value: unknown): Record<string, unknown> | null {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return null;\n}\n\n/**\n * Type guard version of asRecord for use in conditionals.\n *\n * @param value - Value to check\n * @returns True if value is a non-null, non-array object\n *\n * @example\n * if (isRecord(data)) {\n * console.log(data.field);\n * }\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Safely extracts a string value.\n *\n * @param value - Value to check\n * @returns String if valid, null otherwise\n */\nexport function asString(value: unknown): string | null {\n return typeof value === 'string' ? value : null;\n}\n\n/**\n * Safely extracts a number value.\n *\n * @param value - Value to check\n * @returns Number if valid (not NaN, not Infinity), null otherwise\n */\nexport function asNumber(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value;\n }\n return null;\n}\n\n/**\n * Safely extracts a boolean value.\n *\n * @param value - Value to check\n * @returns Boolean if valid, null otherwise\n */\nexport function asBoolean(value: unknown): boolean | null {\n return typeof value === 'boolean' ? value : null;\n}\n\n/**\n * Safely extracts an array value.\n *\n * @param value - Value to check\n * @returns Array if valid, null otherwise\n */\nexport function asArray(value: unknown): unknown[] | null {\n return Array.isArray(value) ? value : null;\n}\n\n/**\n * Extracts a string field from a record.\n *\n * @param record - Record to extract from\n * @param key - Field key\n * @returns String value or undefined if not found/invalid\n */\nexport function extractStringField(\n record: Record<string, unknown>,\n key: string\n): string | undefined {\n const value = record[key];\n return typeof value === 'string' ? value : undefined;\n}\n\n/**\n * Extracts a number field from a record.\n *\n * @param record - Record to extract from\n * @param key - Field key\n * @returns Number value or null if not found/invalid\n */\nexport function extractNumberField(record: Record<string, unknown>, key: string): number | null {\n const value = record[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : null;\n}\n\n/**\n * Extracts a boolean field from a record.\n *\n * @param record - Record to extract from\n * @param key - Field key\n * @returns Boolean value or undefined if not found/invalid\n */\nexport function extractBooleanField(\n record: Record<string, unknown>,\n key: string\n): boolean | undefined {\n const value = record[key];\n return typeof value === 'boolean' ? value : undefined;\n}\n\n/**\n * Extracts a nested record field from a record.\n *\n * @param record - Record to extract from\n * @param key - Field key\n * @returns Nested record or null if not found/invalid\n */\nexport function extractRecordField(\n record: Record<string, unknown>,\n key: string\n): Record<string, unknown> | null {\n return asRecord(record[key]);\n}\n\n/**\n * Safely parses JSON with fallback to null.\n *\n * @param raw - Raw JSON string\n * @returns Parsed value or null if invalid\n */\nexport function safeJsonParse(raw: string): unknown {\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n return null;\n }\n}\n\n/**\n * Safely parses JSON and returns as Record.\n *\n * @param raw - Raw JSON string\n * @returns Parsed record or null if invalid\n */\nexport function safeJsonParseRecord(raw: string): Record<string, unknown> | null {\n const parsed = safeJsonParse(raw);\n return asRecord(parsed);\n}\n","/**\n * nexus-agents/cli-adapters - Claude CLI Response Parser\n *\n * Defensive parser for Claude CLI JSON output.\n * Handles version 2.0.x output format.\n *\n * (Source: cli-project_plan.md v2.1.0)\n * (Source: docs/research/cli-integration-architecture.md)\n */\n\nimport type { ICliResponseParser, TokenUsage } from '../types.js';\nimport { asRecord, extractNumberField } from '../../utils/type-coercion.js';\nimport { createLogger } from '../../core/index.js';\n\nconst logger = createLogger({ component: 'claude-parser' });\n\n/**\n * Claude CLI response structure.\n * (Source: CLI testing 2026-01-04)\n */\nexport interface ClaudeCliResponse {\n readonly type: 'result';\n readonly subtype?: 'success' | 'error';\n readonly is_error: boolean;\n /** Why generation stopped — `end_turn` on an answer, `stop_sequence` on the measured error envelope (#6120). */\n readonly stop_reason?: string;\n readonly duration_ms?: number;\n readonly result: string;\n readonly session_id?: string;\n readonly total_cost_usd?: number;\n readonly usage?: {\n readonly input_tokens: number;\n readonly output_tokens: number;\n readonly cache_creation_input_tokens?: number;\n readonly cache_read_input_tokens?: number;\n };\n readonly modelUsage?: Record<\n string,\n {\n readonly inputTokens: number;\n readonly outputTokens: number;\n readonly cacheReadInputTokens?: number;\n readonly cacheCreationInputTokens?: number;\n readonly costUSD?: number;\n readonly contextWindow?: number;\n }\n >;\n}\n\n/** A cost figure usable as a measurement: finite and not negative. */\nfunction isUsableCost(value: number): boolean {\n return Number.isFinite(value) && value >= 0;\n}\n\n/**\n * Sum the per-model `costUSD` entries, or `null` if none carried one.\n *\n * `null` rather than `0` is the point: \"the vendor reported nothing\" is not\n * \"the vendor reported free\", and returning 0 would tell the budget router the\n * call cost nothing.\n */\nfunction sumModelUsageCost(raw: unknown): number | null {\n const modelUsage = asRecord(raw);\n if (modelUsage === null) return null;\n\n let sum = 0;\n let sawCost = false;\n for (const entry of Object.values(modelUsage)) {\n const perModel = asRecord(entry);\n if (perModel === null) continue;\n const cost = perModel.costUSD;\n if (typeof cost !== 'number' || !isUsableCost(cost)) continue;\n sum += cost;\n sawCost = true;\n }\n return sawCost ? sum : null;\n}\n\n/**\n * Parser for Claude CLI JSON output.\n * Implements defensive parsing - only requires essential fields.\n */\nexport class ClaudeResponseParser implements ICliResponseParser<ClaudeCliResponse> {\n readonly name = 'claude-parser';\n readonly supportedVersionRange = '>=2.0.0 <3.0.0';\n\n /**\n * Parses complete Claude CLI response.\n */\n parse(raw: string): ClaudeCliResponse | null {\n try {\n const data: unknown = JSON.parse(raw);\n\n if (!this.isValidResponse(data)) {\n return null;\n }\n\n return data;\n } catch {\n logger.debug('Skipped malformed output line', { snippet: raw.slice(0, 100) });\n return null;\n }\n }\n\n /**\n * Extracts just the response text (most stable field).\n * Returns null if the response contains an error.\n */\n extractResponse(raw: string): string | null {\n try {\n const data: unknown = JSON.parse(raw);\n const record = asRecord(data);\n if (record === null) return null;\n\n // Check for API errors (is_error: true indicates an error occurred)\n if (record.is_error === true) {\n return null;\n }\n\n const result = record.result;\n if (typeof result === 'string') {\n return result;\n }\n\n return null;\n } catch {\n logger.debug('Skipped malformed output line', { snippet: raw.slice(0, 100) });\n return null;\n }\n }\n\n /**\n * The error text of an `is_error: true` envelope, with its `stop_reason`\n * (#6120).\n *\n * Before this the envelope reached the adapter only through the generic\n * unparseable-output path, whose first step scans the WHOLE stdout for\n * rate-limit text. The out-of-credits envelope carries `api_error_status:\n * 429`, so that scan matched and the error message became the first 500\n * characters of the envelope — `{\"duration_api_ms\":0,…` — while the one\n * field that names the cause, `result`, never reached anyone. Surfacing it\n * here routes the envelope through `classifyErrorOnlyStream`, which\n * classifies the message text rather than the envelope bytes.\n *\n * `null` when the envelope is not an error, or when `result` is empty: an\n * empty error text is not a message, and the caller's recovery order handles\n * it as before.\n */\n extractErrorMessage(raw: string): string | null {\n try {\n const record = asRecord(JSON.parse(raw));\n if (record?.is_error !== true) return null;\n\n const result = record.result;\n if (typeof result !== 'string' || result === '') return null;\n\n const stopReason = record.stop_reason;\n return typeof stopReason === 'string' && stopReason !== ''\n ? `${result} (stop_reason: ${stopReason})`\n : result;\n } catch {\n logger.debug('Skipped malformed output line', { snippet: raw.slice(0, 100) });\n return null;\n }\n }\n\n /**\n * Extracts token usage from response.\n */\n extractUsage(raw: string): TokenUsage | null {\n try {\n const data: unknown = JSON.parse(raw);\n const record = asRecord(data);\n if (record === null) return null;\n\n const usageRecord = asRecord(record.usage);\n if (usageRecord === null) return null;\n\n const inputTokens = extractNumberField(usageRecord, 'input_tokens');\n const outputTokens = extractNumberField(usageRecord, 'output_tokens');\n\n if (inputTokens === null || outputTokens === null) {\n return null;\n }\n\n const cachedInputTokens = extractNumberField(usageRecord, 'cache_read_input_tokens');\n // Declared on ClaudeResult.usage since forever but never read (#4435) —\n // so a panel's FIRST call, which writes the cache, lost its largest\n // input measurement entirely. Absent stays absent: a fabricated 0 would\n // read as \"no cache write happened\".\n const cacheCreationInputTokens = extractNumberField(\n usageRecord,\n 'cache_creation_input_tokens'\n );\n\n return {\n inputTokens,\n outputTokens,\n // NOTE: still uncached input + output. Folding the cache figures in\n // here is a semantics change for every consumer of totalTokens and\n // belongs with the threading increment on #4435, not this extraction.\n totalTokens: inputTokens + outputTokens,\n ...(cachedInputTokens !== null && { cachedInputTokens }),\n ...(cacheCreationInputTokens !== null && { cacheCreationInputTokens }),\n };\n } catch {\n logger.debug('Skipped malformed output line', { snippet: raw.slice(0, 100) });\n return null;\n }\n }\n\n /**\n * Extracts the cost the Claude CLI reported for this call.\n *\n * Prefers `total_cost_usd` — the vendor's own total — over summing\n * `modelUsage[*].costUSD`, because a per-model breakdown can omit a component\n * the total includes. Both are declared on {@link ClaudeCliResponse} and\n * neither reached `CliResponse` before #5241.\n *\n * Rejects a negative or non-finite figure: a cost is a measurement, and\n * letting a corrupt one through would debit the budget router with garbage.\n */\n extractCostUsd(raw: string): number | null {\n try {\n const record = asRecord(JSON.parse(raw));\n if (record === null) return null;\n\n const total = record.total_cost_usd;\n if (typeof total === 'number') return isUsableCost(total) ? total : null;\n\n return sumModelUsageCost(record.modelUsage);\n } catch {\n logger.debug('Skipped malformed output line', { snippet: raw.slice(0, 100) });\n return null;\n }\n }\n\n /**\n * Extracts session ID for resumption.\n */\n extractSessionId(raw: string): string | null {\n try {\n const data: unknown = JSON.parse(raw);\n const record = asRecord(data);\n if (record === null) return null;\n\n const sessionId = record.session_id;\n if (typeof sessionId === 'string') {\n return sessionId;\n }\n\n return null;\n } catch {\n logger.debug('Skipped malformed output line', { snippet: raw.slice(0, 100) });\n return null;\n }\n }\n\n /**\n * Type guard for valid response structure.\n */\n private isValidResponse(data: unknown): data is ClaudeCliResponse {\n const record = asRecord(data);\n if (record === null) return false;\n\n // Only require the essential field\n return typeof record.result === 'string';\n }\n}\n","/**\n * Key-free model enumeration for CLI-subprocess adapters via models.dev (#3405).\n *\n * claude/codex/gemini CLIs have no `list-models` command, and their OAuth tokens\n * can't call the vendor `/v1/models` REST endpoints (inference-only). So we\n * enumerate from the committed, CI-refreshed **models.dev snapshot** filtered by\n * vendor — a current, comprehensive, *key-free* catalog. This is EXISTENCE only\n * (liveness is handled reactively by the 404 fallback); the in-tree registry\n * stays authoritative for pricing/capability.\n *\n * Fail-OPEN: a missing/malformed snapshot yields `[]`, never throws.\n *\n * @module config/models-dev-by-vendor\n */\nimport { createLogger } from '../core/index.js';\n\nimport { loadModelsDevSnapshot } from './models-dev-snapshot-loader.js';\n\nconst logger = createLogger({ component: 'models-dev-by-vendor' });\n\n/**\n * CLI name → models.dev vendor key. opencode is intentionally absent — it has a\n * native `opencode models` probe (wired separately).\n */\nexport const CLI_TO_MODELSDEV_VENDOR: Readonly<Record<string, string>> = {\n claude: 'anthropic',\n codex: 'openai',\n gemini: 'google',\n};\n\ninterface VendoredId {\n readonly id: string;\n readonly vendor: string;\n}\n\nlet cached: readonly VendoredId[] | null = null;\n\nfunction loadEntries(): readonly VendoredId[] {\n if (cached !== null) return cached;\n try {\n const { entries } = loadModelsDevSnapshot();\n // `vendor` is a required ModelEntry field; a non-matching/absent value simply\n // fails the equality filter below, so no defensive fallback is needed.\n cached = entries.map((e) => ({ id: e.id, vendor: e.vendor }));\n } catch (error: unknown) {\n logger.debug('models.dev snapshot load failed; enumeration empty', {\n error: error instanceof Error ? error.message : String(error),\n });\n cached = [];\n }\n return cached;\n}\n\n/** Reset the in-process snapshot cache (tests). */\nexport function resetModelsDevByVendorCache(): void {\n cached = null;\n}\n\n/** All model ids the models.dev snapshot lists for `vendor`. */\nexport function listModelsByVendor(vendor: string): readonly { id: string }[] {\n return loadEntries()\n .filter((e) => e.vendor === vendor)\n .map((e) => ({ id: e.id }));\n}\n\n/**\n * Model ids for a CLI, mapped via {@link CLI_TO_MODELSDEV_VENDOR}. Returns `[]`\n * for CLIs without a vendor mapping (e.g. opencode, which enumerates natively).\n */\nexport function listModelsForCli(cliName: string): readonly { id: string; provider: string }[] {\n const vendor = CLI_TO_MODELSDEV_VENDOR[cliName];\n if (vendor === undefined) return [];\n return listModelsByVendor(vendor).map((m) => ({ id: m.id, provider: vendor }));\n}\n","/**\n * nexus-agents/cli-adapters - Claude CLI Adapter\n *\n * Subprocess-based adapter for Claude CLI.\n * Uses JSON output format for stable parsing.\n *\n * (Source: cli-project_plan.md v2.1.0)\n * (Source: docs/research/cli-integration-architecture.md)\n */\n\nimport type {\n ICliResponseParser,\n CliTask,\n CliResponse,\n CliError,\n ModelInfo,\n CliName,\n BaseAdapterOptions,\n ResolvedExecutionOptions,\n} from '../types.js';\nimport type { Result } from '../../core/index.js';\nimport { ok } from '../../core/index.js';\nimport { SubprocessCliAdapter, type CommandConfig } from '../subprocess-adapter.js';\nimport { isReadOnlyAnalysis, readOnlyAnalysisConflict } from '../read-only-analysis.js';\nimport { ClaudeResponseParser } from '../parsers/claude-parser.js';\nimport type { CliModelInfo } from '../types-capability.js';\nimport { listModelsForCli } from '../../config/models-dev-by-vendor.js';\nimport { getDefaultRegistry } from '../../config/model-registry.js';\nimport { isDurableCapacityText } from '../../adapters/rate-limit-detector.js';\nimport {\n getDefaultModelForCli,\n getCliModelName,\n buildModelInfo,\n findInTreeByCli,\n FALLBACK_CONTEXT_WINDOW,\n FALLBACK_MAX_OUTPUT,\n} from '../../config/model-config-helpers.js';\n\n/**\n * Maps internal model names → Claude CLI aliases. Derived entirely from the\n * canonical registry: every claude entry contributes its `cliAlias` (passthrough),\n * its registry `id`, its `cliModelName`, and every legacy-name in `aliases[]`. Migration of these\n * legacy strings into the registry happened in #2200 Child 1.\n */\nconst MODEL_TO_CLI_ALIAS: Record<string, string> = buildClaudeAliasMap();\n\nfunction buildClaudeAliasMap(): Record<string, string> {\n const map: Record<string, string> = {};\n for (const model of findInTreeByCli('claude')) {\n if (model.cliAlias === undefined) continue;\n const alias = model.cliAlias;\n map[alias] = alias;\n // #6599: the canonical registry id is what a model-bound request carries\n // (`getAdapterForModel`, `resolveModelForTier`); the claude binary rejects\n // it as `unrecognized_model`, so it must map to the alias too.\n map[model.id] = alias;\n if (model.cliModelName !== undefined) map[model.cliModelName] = alias;\n for (const legacyName of model.aliases ?? []) {\n map[legacyName] = alias;\n }\n }\n return map;\n}\n\n/**\n * Default cost when an unrecognized model id is passed (pricing matches\n * current Opus, the strongest tier — conservative over-estimate). Per-model\n * legacy cost overrides were removed in #2200 Child 1; they're reachable\n * via the registry now.\n */\nconst UNKNOWN_MODEL_DEFAULT_INPUT_COST = 5.0;\nconst UNKNOWN_MODEL_DEFAULT_OUTPUT_COST = 25.0;\n\n/**\n * Task option that disables the in-family fallback (#6120). A probe that\n * wants to know whether ONE model answers — doctor's pinned-model line —\n * must not be answered by its sibling.\n */\nexport const IN_FAMILY_FALLBACK_OPTION = 'inFamilyFallback';\n\n/** Resolve an internal model name, legacy name or alias to the CLI alias. */\nfunction toCliAlias(internalModel: string): string {\n return MODEL_TO_CLI_ALIAS[internalModel] ?? internalModel;\n}\n\n/**\n * The claude alias the registry lists after `alias`, or undefined when\n * `alias` is the last one (or not a registry alias at all — no basis for a\n * \"next\"). Read from the registry at call time rather than a module-load\n * constant so an operator overlay is honoured; the order is the registry's\n * (in-tree: fable → opus → sonnet → haiku), not a hard-coded list.\n */\nfunction nextClaudeAlias(alias: string): string | undefined {\n const aliases = getDefaultRegistry()\n .allEntries()\n .flatMap((entry) =>\n entry.cliName === 'claude' && entry.cliAlias !== undefined ? [entry.cliAlias] : []\n );\n const at = aliases.indexOf(alias);\n return at === -1 ? undefined : aliases[at + 1];\n}\n\n/**\n * The ONLY built-in claude tools a read-only analysis seat may use (#6754),\n * passed as `--tools`. An allow list fails closed: a tool claude adds later,\n * a search tool, or a subagent tool is absent unless named here. Verified on\n * claude 2.1.281: with `--tools Read,Grep,Glob` the model reported having\n * exactly those three and made no search or fetch request.\n */\nconst CLAUDE_READ_ONLY_TOOLS = ['Read', 'Grep', 'Glob'] as const;\n\n/**\n * Tools also denied outright under read-only analysis (#6754). Redundant with\n * {@link CLAUDE_READ_ONLY_TOOLS}; kept so a settings-file allow rule cannot\n * re-admit any of them if the allow list is ever widened by mistake.\n */\nconst CLAUDE_READ_ONLY_DISALLOWED_TOOLS = [\n 'Bash',\n 'Edit',\n 'Write',\n 'NotebookEdit',\n 'WebFetch',\n 'WebSearch',\n] as const;\n\n/**\n * Claude CLI adapter using subprocess transport.\n * Executes: claude -p --output-format json \"<task>\"\n */\nexport class ClaudeCliAdapter extends SubprocessCliAdapter {\n readonly name: CliName = 'claude';\n override readonly enforcesReadOnlyAnalysis = true;\n protected readonly parser: ICliResponseParser = new ClaudeResponseParser();\n\n private readonly model: string;\n\n constructor(options?: BaseAdapterOptions) {\n super(options?.logger);\n this.model = options?.model ?? getCliModelName(getDefaultModelForCli('claude'));\n }\n\n /**\n * Key-free model enumeration (#3405): the claude CLI has no list-models\n * command and its OAuth token can't call /v1/models, so we enumerate the\n * vendor's models from the models.dev snapshot. Existence only.\n */\n listModels(): Promise<readonly CliModelInfo[]> {\n return Promise.resolve(listModelsForCli(this.name));\n }\n\n /**\n * Gets Claude model information.\n * `buildModelInfo` matches `cliModelName`, `cliAlias`, and `aliases[]` —\n * a single call handles 'opus', 'sonnet', 'haiku', current model names,\n * and the legacy `claude-opus-4` / `claude-haiku-3` / etc. entries that\n * live in the registry's aliases since #2200 Child 1.\n *\n * Truly unrecognized models fall through to conservative defaults\n * (current Opus pricing).\n */\n getModelInfo(): ModelInfo {\n const fromRegistry = buildModelInfo('claude', this.model);\n if (fromRegistry !== undefined) return fromRegistry;\n\n return {\n id: this.model,\n name: this.model,\n contextWindow: FALLBACK_CONTEXT_WINDOW,\n maxOutput: FALLBACK_MAX_OUTPUT,\n costPerMillionInput: UNKNOWN_MODEL_DEFAULT_INPUT_COST,\n costPerMillionOutput: UNKNOWN_MODEL_DEFAULT_OUTPUT_COST,\n };\n }\n\n /**\n * Run the task, and on an out-of-credits envelope for the requested model\n * retry ONCE with the next claude alias the registry lists (#6120).\n *\n * The credit exhaustion the claude CLI reports is per MODEL — `fable`\n * answered \"You're out of usage credits\" while `sonnet` answered the same\n * prompt — so it is not evidence against the CLI, and it must not reach the\n * per-CLI circuit breaker as one. The breaker records what leaves this\n * method: a substituted success records nothing, and a second capacity\n * error propagates as the typed error and counts once, because by then the\n * family, not one model, has failed. A non-capacity `is_error` (auth, a\n * server error) is returned as-is; another model would not fix it.\n *\n * The substitution is stamped on the response as `fallbackFrom` so a vote\n * record can say which model actually answered (#6115).\n */\n override async executeTask(\n task: CliTask,\n options: ResolvedExecutionOptions\n ): Promise<Result<CliResponse, CliError>> {\n const first = await super.executeTask(task, options);\n if (first.ok || !isDurableCapacityText(first.error.message)) return first;\n if (task.options?.[IN_FAMILY_FALLBACK_OPTION] === false) return first;\n\n const requested = toCliAlias(task.model ?? this.model);\n const next = nextClaudeAlias(requested);\n if (next === undefined) return first;\n\n this.logger.warn('Claude model out of usage credits; retrying once with the next alias', {\n requested,\n next,\n });\n const second = await super.executeTask({ ...task, model: next }, options);\n if (!second.ok) return second;\n return ok({ ...second.value, model: next, fallbackFrom: requested });\n }\n\n /**\n * #6754: a permission bypass would defeat read-only analysis mode, so the\n * combination is refused rather than resolved one way silently.\n */\n protected override accessModeRefusal(task: CliTask): CliError | undefined {\n const base = super.accessModeRefusal(task);\n if (base !== undefined || !isReadOnlyAnalysis(task)) return base;\n if (task.options?.['skipPermissions'] === true) {\n return readOnlyAnalysisConflict(this.name, 'the task also asks to skip permissions');\n }\n // `--strict-mcp-config` still loads the servers `--mcp-config` names, and\n // an MCP server's tools are outside the built-in allow list.\n if (typeof task.options?.['mcpConfigPath'] === 'string') {\n return readOnlyAnalysisConflict(this.name, 'the task also names an MCP config');\n }\n return undefined;\n }\n\n /** Appends optional string-type task options to CLI args. */\n private appendTaskOptions(args: string[], task: CliTask): void {\n const workDir = task.options?.['workDir'];\n if (typeof workDir === 'string' && workDir.length > 0) {\n args.push('--add-dir', workDir);\n }\n const mcpConfigPath = task.options?.['mcpConfigPath'];\n if (typeof mcpConfigPath === 'string' && mcpConfigPath.length > 0) {\n args.push('--mcp-config', mcpConfigPath);\n }\n if (isReadOnlyAnalysis(task)) {\n // #6754: offer only the read tools, load no MCP server, deny the\n // command/write/network tools again, and pin the permission mode so a\n // settings-file default cannot widen it. `manual` is the documented\n // mode; in print mode nothing that would prompt can be approved.\n args.push('--permission-mode', 'manual');\n args.push('--tools', CLAUDE_READ_ONLY_TOOLS.join(','));\n args.push('--strict-mcp-config');\n args.push('--disallowedTools', CLAUDE_READ_ONLY_DISALLOWED_TOOLS.join(','));\n return;\n }\n // Allow full tool access in non-interactive mode (needed for SWE-bench)\n if (task.options?.['skipPermissions'] === true) {\n args.push('--dangerously-skip-permissions');\n }\n }\n\n /**\n * Gets CLI command and arguments for execution.\n * Uses stdin for the prompt to avoid argument escaping issues,\n * especially important when using --add-dir.\n */\n protected getCommand(task: CliTask): CommandConfig {\n const args: string[] = ['-p', '--output-format', 'json'];\n\n // Add model - convert internal names to CLI aliases\n const internalModel = task.model ?? this.model;\n const cliModel = toCliAlias(internalModel);\n args.push('--model', cliModel);\n\n // Add system prompt if provided\n if (task.systemPrompt !== undefined && task.systemPrompt !== '') {\n args.push('--system-prompt', task.systemPrompt);\n }\n\n // Add session for continuation\n if (task.sessionId !== undefined && task.sessionId !== '') {\n args.push('--resume', task.sessionId);\n }\n\n this.appendTaskOptions(args, task);\n\n // Note: maxTokens is intentionally not passed to Claude CLI.\n // The Claude CLI does not support --max-tokens. Use --max-budget-usd instead.\n // The CLI handles token limits internally based on model configuration.\n\n // Pass prompt via stdin to avoid argument escaping issues\n return { command: 'claude', args, stdin: task.content };\n }\n}\n","/**\n * nexus-agents/cli-adapters - Antigravity (`agy`) CLI Response Parser\n *\n * Parses `agy --output-format json`, the replacement for the retired standalone\n * `gemini` CLI (#4346).\n *\n * FAIL-CLOSED BY CONSTRUCTION. `agy` exits 0 even when the run failed — a bad\n * model returns `{\"status\":\"ERROR\",\"response\":\"\",\"error\":\"…\"}` with exit code 0.\n * The verdict lives in the `status` field, so this parser treats anything that\n * is not an explicit `SUCCESS` envelope with a string `response` as a failure:\n * unparseable output, truncated JSON, a missing `status`, an unrecognized\n * `status`, and a non-string `response` all yield `null`. Callers must not\n * infer success from the process exit code.\n *\n * Verified against `agy` v1.1.9 (2026-08-09).\n *\n * @module cli-adapters/parsers/agy-parser\n */\n\nimport { z } from 'zod';\nimport type { ICliResponseParser, TokenUsage } from '../types.js';\n\n/**\n * The usage block agy reports. `thinking_tokens` and `cache_read_tokens` have no\n * home in {@link TokenUsage}; see {@link AgyResponseParser.extractUsage} for how\n * they are handled rather than dropped.\n */\nconst AgyUsageSchema = z.object({\n input_tokens: z.number(),\n output_tokens: z.number(),\n thinking_tokens: z.number().optional(),\n cache_read_tokens: z.number().optional(),\n total_tokens: z.number(),\n});\n\n/**\n * A successful agy envelope. `status` is pinned to the literal `SUCCESS` so an\n * unrecognized future status (e.g. a `PARTIAL`) fails the parse instead of being\n * waved through as a success.\n */\nconst AgySuccessSchema = z.object({\n status: z.literal('SUCCESS'),\n response: z.string(),\n conversation_id: z.string().optional(),\n duration_seconds: z.number().optional(),\n num_turns: z.number().optional(),\n usage: AgyUsageSchema.optional(),\n});\n\n/** A failed agy envelope — still delivered with exit code 0. */\nconst AgyErrorSchema = z.object({\n status: z.string(),\n error: z.string().optional(),\n response: z.string().optional(),\n conversation_id: z.string().optional(),\n});\n\n/** A parsed, validated agy success envelope. */\nexport type AgyCliResponse = z.infer<typeof AgySuccessSchema>;\n\n/** Parse raw stdout as JSON, or undefined when it is not JSON at all. */\nfunction asJson(raw: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Parser for `agy --output-format json`.\n *\n * `parse` returns a value ONLY for a well-formed success envelope. Failure\n * detail is available separately via {@link AgyResponseParser.extractErrorMessage}.\n */\nexport class AgyResponseParser implements ICliResponseParser<AgyCliResponse> {\n readonly name = 'agy-parser';\n readonly supportedVersionRange = '>=1.0.0 <2.0.0';\n\n parse(raw: string): AgyCliResponse | null {\n const json = asJson(raw);\n if (json === undefined) return null;\n const parsed = AgySuccessSchema.safeParse(json);\n return parsed.success ? parsed.data : null;\n }\n\n extractResponse(raw: string): string | null {\n return this.parse(raw)?.response ?? null;\n }\n\n /**\n * The error text from a non-SUCCESS envelope, or null when the run succeeded\n * or the output was not a recognizable agy envelope at all.\n *\n * Returning the message lets the adapter classify it (rate limit, auth,\n * bad model) through the shared taxonomy rather than reporting a bare\n * PARSE_ERROR — the same reason `opencode-parser` grew this method.\n */\n extractErrorMessage(raw: string): string | null {\n const json = asJson(raw);\n if (json === undefined) return null;\n // A valid success envelope has no error to report.\n if (AgySuccessSchema.safeParse(json).success) return null;\n const parsed = AgyErrorSchema.safeParse(json);\n if (!parsed.success) return null;\n return parsed.data.error ?? `agy reported status '${parsed.data.status}'`;\n }\n\n /**\n * Maps agy's usage block onto {@link TokenUsage}.\n *\n * `thinking_tokens` are **folded into `outputTokens`** rather than dropped:\n * they are generated, billable tokens, and discarding them would understate\n * cost in exactly the outcome data the routing loop learns from.\n * `cache_read_tokens` are deliberately NOT added — they are a subset of input\n * already counted in `input_tokens`, so adding them would double-count.\n */\n /**\n * agy's `conversation_id` is the session handle — it is what `--conversation`\n * accepts to resume a run, so it maps directly onto the session-id contract.\n */\n extractSessionId(raw: string): string | null {\n const id = this.parse(raw)?.conversation_id;\n return id === undefined || id === '' ? null : id;\n }\n\n extractUsage(raw: string): TokenUsage | null {\n const parsed = this.parse(raw);\n if (parsed?.usage === undefined) return null;\n const { input_tokens, output_tokens, thinking_tokens, total_tokens } = parsed.usage;\n return {\n inputTokens: input_tokens,\n outputTokens: output_tokens + (thinking_tokens ?? 0),\n totalTokens: total_tokens,\n };\n }\n}\n","/**\n * nexus-agents/config - Antigravity (`agy`) model-slug mapping\n *\n * Maps canonical registry model ids onto the model slugs the `agy` CLI accepts\n * (#4346).\n *\n * WHY THIS IS SEPARATE FROM `cliModelName`. The obvious move — repointing the\n * gemini entries' `cliModelName` at agy slugs — is wrong: that field is ALSO\n * read by the API-based `GeminiAdapter` via `GEMINI_MODELS`\n * (`adapters/gemini-types.ts:21-23`), which calls the Google API directly and\n * needs real API ids like `gemini-2.5-flash`. agy's slugs (`gemini-3.6-flash-low`)\n * are meaningless to that API. One field cannot serve both surfaces, so the\n * CLI-transport naming lives here.\n *\n * Lives under `config/` because `scripts/check-model-string-drift.ts` blocks\n * version-bearing model-id literals outside this directory, and enforces it in\n * CI and pre-push.\n *\n * Verified against `agy models` on v1.1.21 (2026-08-26). `agy models` is once\n * again enumerable non-interactively — it completed piped in ~1s, exit 0, over\n * three runs. #4393 recorded it hanging for 90s without a TTY on v1.1.11; that\n * was an upstream defect and it is fixed. `scripts/check-agy-model-drift.ts`\n * now compares this list against the live CLI so it cannot silently rot again.\n *\n * @module config/agy-model-map\n */\n\nimport { findCanonicalModel } from './model-config-helpers.js';\n\n/**\n * The model slugs `agy` accepts, as reported by `agy models`.\n *\n * agy bakes reasoning effort into the slug (`-high`/`-medium`/`-low`) rather\n * than taking it as a separate axis. A `--effort` flag also exists; the slug\n * suffix is used here because it is what `agy models` enumerates, making it the\n * machine-readable contract.\n *\n * agy is multi-model — it also fronts `claude-sonnet-4-6`,\n * `claude-opus-4-6-thinking` and `gpt-oss-120b-medium`. Those are deliberately\n * NOT mapped: the `gemini` routing arm means \"Gemini-family models\", and this\n * repo already routes Claude through the claude adapter and GPT-OSS through\n * openrouter arms. Adding duplicate routes here would give the router redundant\n * arms to disambiguate with no consumer asking for them (#4346, 7/0 vote).\n */\nexport const AGY_MODEL_SLUGS = [\n 'gemini-3.7-flash-high',\n 'gemini-3.7-flash-medium',\n 'gemini-3.7-flash-low',\n 'gemini-3.6-flash-high',\n 'gemini-3.6-flash-medium',\n 'gemini-3.6-flash-low',\n 'gemini-3.5-flash-high',\n 'gemini-3.5-flash-medium',\n 'gemini-3.5-flash-low',\n 'gemini-3.1-pro-high',\n 'gemini-3.1-pro-low',\n] as const;\n\nexport type AgyModelSlug = (typeof AGY_MODEL_SLUGS)[number];\n\n/**\n * Canonical registry model id → agy slug.\n *\n * Mapped by TIER SEMANTICS, not by version-string similarity, because agy's\n * generations do not line up with the registry's. The registry's quality\n * profile is the guide: the two `pro` entries take the pro slugs (higher\n * reasoning gets `-high`), and the three flash entries take flash slugs\n * descending by the cost/quality trade-off each entry records.\n *\n * `gemini-pro` and `gemini-flash` are registry entries for the 2.5 generation,\n * which agy does not serve at all; they map to the nearest surviving tier rather\n * than being dropped, so routing that already selects them keeps working.\n */\nconst CANONICAL_TO_AGY: Readonly<Record<string, AgyModelSlug>> = {\n // reasoning 10 — the strongest entry takes the strongest pro tier\n 'gemini-3-pro': 'gemini-3.1-pro-high',\n // reasoning 9, 2.5-generation — nearest surviving pro tier\n 'gemini-pro': 'gemini-3.1-pro-low',\n // speed 10 / quality 8\n 'gemini-3.5-flash': 'gemini-3.5-flash-medium',\n // speed 10 / quality 8, cheaper than the above\n 'gemini-3-flash': 'gemini-3.5-flash-low',\n // cheapest entry, quality 7, 2.5-generation\n 'gemini-flash': 'gemini-3.6-flash-low',\n};\n\n/**\n * agy slug → canonical registry id. Derived from {@link CANONICAL_TO_AGY} so the\n * two directions cannot drift (#4395).\n *\n * Needed because the forward map alone left the arm unable to describe what it\n * actually ran: `getModelInfo()` reported the registry's `cliModelName` (a\n * Google API id agy does not accept) while `getCommand()` spawned the slug. That\n * mismatch reached `TaskOutcome.model`, so the routing loop was told the wrong\n * model produced each outcome, and pricing could not resolve the slug at all.\n */\nconst AGY_TO_CANONICAL: ReadonlyMap<string, string> = new Map(\n Object.entries(CANONICAL_TO_AGY).map(([canonical, slug]) => [slug, canonical])\n);\n\n/**\n * Resolve an agy slug back to the canonical registry id, or null when the slug\n * is one agy serves but the registry has no entry for.\n *\n * Null rather than a guess: several registry entries map onto the same slug\n * where agy's generations do not line up with ours, so a reverse lookup is\n * lossy by construction. Callers should fall back to the raw slug rather than\n * inventing an entry.\n */\nexport function fromAgyModelSlug(slug: string): string | null {\n return AGY_TO_CANONICAL.get(slug) ?? null;\n}\n\n/** The slug used when a caller names a model agy cannot serve. */\nexport const DEFAULT_AGY_MODEL: AgyModelSlug = 'gemini-3.1-pro-high';\n\n/** Whether a string is a slug `agy` will accept. */\nexport function isAgyModelSlug(value: string): value is AgyModelSlug {\n return (AGY_MODEL_SLUGS as readonly string[]).includes(value);\n}\n\n/**\n * Resolve a model identifier to a slug `agy` accepts.\n *\n * Accepts an already-valid agy slug unchanged (so callers can pin one\n * directly), then a canonical registry id, and otherwise falls back to\n * {@link DEFAULT_AGY_MODEL}.\n *\n * The fallback is deliberate rather than an error: an unmapped id reaching agy\n * produces `{\"status\":\"ERROR\",\"error\":\"invalid model selection ...\"}` **with\n * exit code 0**, which is a far worse failure than quietly running a valid\n * model. The substitution is logged by the caller.\n */\nexport function toAgyModelSlug(model: string): AgyModelSlug {\n if (isAgyModelSlug(model)) return model;\n const direct = CANONICAL_TO_AGY[model];\n if (direct !== undefined) return direct;\n // #4395: callers legitimately pin a registry `cliModelName` (a Google API id\n // like `gemini-2.5-pro`) rather than the canonical entry id. Resolve it to the\n // canonical entry before mapping, so a pinned model is honoured instead of\n // silently falling through to the default.\n const canonical = findCanonicalModel('gemini', model)?.id;\n if (canonical !== undefined) {\n const viaCanonical = CANONICAL_TO_AGY[canonical];\n if (viaCanonical !== undefined) return viaCanonical;\n }\n return DEFAULT_AGY_MODEL;\n}\n","/**\n * nexus-agents/cli-adapters - Gemini CLI Adapter Helpers\n *\n * CLI-specific helper functions for retry logic and error handling.\n * Model info lookups consolidated into config/model-config-helpers.ts (#886).\n */\n\nimport type { CliError, CliName } from '../types.js';\n\n// -----------------------------------------------------------------------------\n// Fallback Defaults (for unknown models not in canonical registry)\n// -----------------------------------------------------------------------------\n\n/**\n * Fallback defaults for Gemini models not in the canonical registry.\n * All current models are in config/in-tree-data.ts and served by\n * buildModelInfo('gemini', model). This provides sensible defaults\n * when an unknown model name is encountered at runtime.\n *\n * @see config/in-tree-data.ts — single source of truth for current models\n */\nexport const GEMINI_LEGACY_DEFAULTS = {\n displayNames: {} as Readonly<Record<string, string>>,\n contextWindows: {} as Readonly<Record<string, number>>,\n inputCosts: {} as Readonly<Record<string, number>>,\n outputCosts: {} as Readonly<Record<string, number>>,\n contextWindow: 1_000_000,\n inputCost: 0.15,\n outputCost: 0.6,\n} as const;\n\n// -----------------------------------------------------------------------------\n// Re-exports from canonical cli-retry-loop.ts (Issue #1596)\n// -----------------------------------------------------------------------------\n\nexport { calculateBackoffDelay, isRetryableError, categorizeError } from '../cli-retry-loop.js';\n\n// -----------------------------------------------------------------------------\n// Error Factory\n// -----------------------------------------------------------------------------\n\n/** Creates a circuit breaker open error. */\nexport function createCircuitOpenError(cli: CliName): CliError {\n return {\n code: 'EXECUTION_ERROR',\n message: `Circuit breaker is open - ${cli} CLI temporarily unavailable`,\n cli,\n retryable: false,\n };\n}\n\n// -----------------------------------------------------------------------------\n// Utility Functions\n// -----------------------------------------------------------------------------\n\n// Re-export from canonical source for backward compatibility\nexport { delay } from '../../utils/async-utils.js';\n","/**\n * nexus-agents/cli-adapters - Gemini CLI Adapter\n *\n * Subprocess-based adapter for Gemini CLI with:\n * - Tiered timeout profiles based on task complexity\n * - Resilient JSON parsing with fallback strategies\n * - Exponential backoff retry logic\n * - Circuit breaker integration for sustained failures\n *\n * (Source: cli-project_plan.md v2.1.0)\n * (Source: Issue #366 - Gemini CLI timeout and parser improvements)\n * (Source: Issue #389 - Merged enhanced adapter back to canonical)\n */\n\nimport type { Result, ILogger } from '../../core/index.js';\nimport { ok, err, createLogger, getTimeProvider } from '../../core/index.js';\nimport type {\n ICliResponseParser,\n CliTask,\n CliResponse,\n CliError,\n ModelInfo,\n CliName,\n ExecutionOptions,\n ResolvedExecutionOptions,\n BaseAdapterOptions,\n} from '../types.js';\nimport { SubprocessCliAdapter, type CommandConfig } from '../subprocess-adapter.js';\nimport { isReadOnlyAnalysis } from '../read-only-analysis.js';\nimport { AgyResponseParser } from '../parsers/agy-parser.js';\nimport { toAgyModelSlug, AGY_MODEL_SLUGS } from '../../config/agy-model-map.js';\nimport type { CliModelInfo } from '../types-capability.js';\nimport {\n getTimeoutForTask,\n estimateTaskComplexity,\n type TaskComplexity,\n} from '../cli-timeout-profiles.js';\nimport {\n CliCircuitBreaker,\n DEFAULT_CIRCUIT_BREAKER_CONFIG,\n type CircuitBreakerConfig,\n type CircuitBreakerSnapshot,\n} from '../circuit-breaker.js';\nimport { GEMINI_LEGACY_DEFAULTS, createCircuitOpenError } from './gemini-adapter-helpers.js';\nimport { executeCliRetryLoop } from '../cli-retry-loop.js';\nimport { GEMINI_CLI_COMMAND } from '../cli-error-envelope.js';\nimport {\n buildModelInfo,\n getCliModelName,\n getDefaultModelForCli,\n} from '../../config/model-config-helpers.js';\n\n/** Derive the CLI model name for the default Gemini model from the canonical registry. */\nconst DEFAULT_GEMINI_CLI_MODEL: string = getCliModelName(getDefaultModelForCli('gemini'));\n\n/** Configuration for Gemini adapter. Extends BaseAdapterOptions with retry/circuit breaker. */\nexport interface GeminiConfig extends BaseAdapterOptions {\n /** Maximum retry attempts (default: 3) */\n readonly maxRetries?: number;\n /** Base delay for exponential backoff in ms (default: 1000) */\n readonly baseDelayMs?: number;\n /** Maximum delay for backoff in ms (default: 30000) */\n readonly maxDelayMs?: number;\n /** Circuit breaker configuration */\n readonly circuitBreakerConfig?: Partial<CircuitBreakerConfig>;\n /** Enable circuit breaker (default: true) */\n readonly enableCircuitBreaker?: boolean;\n}\n\n/** Execution result with metadata. */\nexport interface GeminiExecutionResult {\n readonly response: CliResponse;\n readonly retryCount: number;\n readonly totalDurationMs: number;\n readonly complexity: TaskComplexity;\n readonly circuitState: 'closed' | 'open' | 'half-open';\n}\n\nconst DEFAULT_CONFIG: Required<Omit<GeminiConfig, 'logger' | 'circuitBreakerConfig'>> = {\n model: DEFAULT_GEMINI_CLI_MODEL,\n maxRetries: 3,\n baseDelayMs: 1000,\n maxDelayMs: 30_000,\n enableCircuitBreaker: true,\n};\n\n/**\n * The executable name spawned by the Gemini CLI adapter (#4346, #6278).\n * The standalone gemini CLI is EOL — it exits 55 with IneligibleTierError on\n * every invocation.\n */\nexport { GEMINI_CLI_COMMAND };\n\n/**\n * Gemini CLI adapter with reliability features.\n *\n * Includes tiered timeouts, resilient parsing, retry logic, and circuit breaker.\n */\n/**\n * agy flags for a read-only analysis task (#6754), verified in `agy --help`:\n * `--mode plan` is the agent execution mode without edits, and `--sandbox`\n * runs with terminal restrictions. What plan mode permits was NOT observed on\n * a live run (quota-limited when this was written); the flags were.\n */\nconst AGY_READ_ONLY_ARGS = ['--mode', 'plan', '--sandbox'] as const;\n\nexport class GeminiCliAdapter extends SubprocessCliAdapter {\n readonly name: CliName = 'gemini';\n override readonly enforcesReadOnlyAnalysis = true;\n\n /**\n * #4346: the arm is still called `gemini` (it serves Google's Gemini models\n * and keeps the routing/LinUCB identity), but the executable is `agy`. The\n * standalone gemini CLI is EOL — it exits 55 with IneligibleTierError on\n * every invocation.\n */\n override get binaryName(): string {\n return GEMINI_CLI_COMMAND;\n }\n protected readonly parser: ICliResponseParser;\n\n private readonly model: string;\n private readonly maxRetries: number;\n private readonly baseDelayMs: number;\n private readonly maxDelayMs: number;\n private readonly circuitBreaker: CliCircuitBreaker | null;\n private readonly adapterLogger: ILogger;\n\n constructor(options?: GeminiConfig) {\n const mergedConfig = { ...DEFAULT_CONFIG, ...options };\n super(options?.logger);\n\n this.model = mergedConfig.model;\n this.maxRetries = mergedConfig.maxRetries;\n this.baseDelayMs = mergedConfig.baseDelayMs;\n this.maxDelayMs = mergedConfig.maxDelayMs;\n this.parser = new AgyResponseParser();\n this.adapterLogger = options?.logger ?? createLogger({ component: 'gemini-adapter' });\n\n // Initialize circuit breaker if enabled\n if (mergedConfig.enableCircuitBreaker) {\n const cbConfig: CircuitBreakerConfig = {\n ...DEFAULT_CIRCUIT_BREAKER_CONFIG,\n ...options?.circuitBreakerConfig,\n };\n this.circuitBreaker = new CliCircuitBreaker('gemini', cbConfig);\n } else {\n this.circuitBreaker = null;\n }\n }\n\n /** Key-free model enumeration via the models.dev snapshot (#3405). */\n /**\n * The slugs this arm can actually run (#5085).\n *\n * NOT `listModelsForCli('gemini')`, which resolves the models.dev `google`\n * vendor — 82 Google **API** ids like `gemini-2.5-flash`. This arm spawns\n * `agy`, which accepts none of them; the same reasoning already documented\n * for `cliModelName` in `config/agy-model-map.ts` applies to enumeration.\n * Reporting the API list made every consumer confidently wrong rather than\n * empty, which is worse.\n */\n listModels(): Promise<readonly CliModelInfo[]> {\n return Promise.resolve(AGY_MODEL_SLUGS.map((id) => ({ id, provider: 'antigravity' })));\n }\n\n /**\n * Gets Gemini model information.\n * Resolves from canonical registry when possible, falls back to legacy lookup.\n * Note: maxOutput is capped at 8_192 (Gemini CLI constraint).\n */\n getModelInfo(): ModelInfo {\n const fromRegistry = buildModelInfo('gemini', this.model);\n if (fromRegistry !== undefined) {\n return { ...fromRegistry, maxOutput: 8_192 };\n }\n return {\n id: this.model,\n name: GEMINI_LEGACY_DEFAULTS.displayNames[this.model] ?? this.model,\n contextWindow:\n GEMINI_LEGACY_DEFAULTS.contextWindows[this.model] ?? GEMINI_LEGACY_DEFAULTS.contextWindow,\n maxOutput: 8_192,\n costPerMillionInput:\n GEMINI_LEGACY_DEFAULTS.inputCosts[this.model] ?? GEMINI_LEGACY_DEFAULTS.inputCost,\n costPerMillionOutput:\n GEMINI_LEGACY_DEFAULTS.outputCosts[this.model] ?? GEMINI_LEGACY_DEFAULTS.outputCost,\n };\n }\n\n /**\n * Executes a task with reliability features.\n */\n override async execute(\n task: CliTask,\n options?: ExecutionOptions\n ): Promise<Result<CliResponse, CliError>> {\n const result = await this.executeWithMetadata(task, options);\n\n if (result.ok) {\n return ok(result.value.response);\n }\n\n return err(result.error);\n }\n\n /**\n * Executes with full metadata about retry attempts and circuit state.\n */\n async executeWithMetadata(\n task: CliTask,\n options?: ExecutionOptions\n ): Promise<Result<GeminiExecutionResult, CliError>> {\n // #6754: this path bypasses the base execute, so it applies the same\n // fail-closed access-mode check first.\n const refusal = this.accessModeRefusal(task);\n if (refusal !== undefined) return err(refusal);\n const circuitCheckResult = this.checkCircuitBreaker();\n if (circuitCheckResult !== null) {\n return err(circuitCheckResult);\n }\n\n const startTime = getTimeProvider().now();\n const complexity = estimateTaskComplexity(task.content);\n const effectiveOptions = this.buildExecutionOptions(task.content, options);\n\n const result = await this.executeWithRetryTracking(task, effectiveOptions);\n\n return this.buildExecutionResult(result, startTime, complexity);\n }\n\n /**\n * Gets current circuit breaker snapshot.\n */\n getCircuitBreakerSnapshot(): CircuitBreakerSnapshot | null {\n return this.circuitBreaker?.getSnapshot() ?? null;\n }\n\n /**\n * Resets the circuit breaker to closed state.\n */\n resetCircuitBreaker(): void {\n this.circuitBreaker?.reset();\n }\n\n /**\n * Gets CLI command and arguments for execution.\n */\n protected override getCommand(task: CliTask): CommandConfig {\n const args: string[] = [];\n\n // #4346: the standalone `gemini` CLI is retired — it fails every invocation\n // with IneligibleTierError (exit 55). `agy` (Antigravity) is Google's own\n // replacement. Flag spellings differ entirely from the old CLI:\n // -o json -> --output-format json\n // -m <model> -> --model <slug>\n // --resume -> --conversation\n // --policy -> (no equivalent; see systemPrompt handling below)\n args.push('--output-format', 'json');\n\n // Registry model ids and agy slugs are different namespaces — see\n // config/agy-model-map.ts for why `cliModelName` cannot carry the agy slug.\n // An unmapped id would make agy return status:ERROR with exit code 0, so the\n // resolver substitutes a valid slug rather than letting that happen.\n const requested = task.model ?? this.model;\n const model = toAgyModelSlug(requested);\n if (model !== requested) {\n this.adapterLogger.debug('Substituted an agy model slug', { requested, model });\n }\n args.push('--model', model);\n\n if (task.sessionId !== undefined && task.sessionId !== '') {\n args.push('--conversation', task.sessionId);\n }\n\n // #6254: agy's workspace is its STORED project, not the directory it is\n // spawned in — the other arms take the cwd as their tree by default; agy\n // does not. Measured: spawned from this repository with no --add-dir, a\n // voter seat read `packages/nexus-agents/package.json` out of an unrelated\n // checkout on the same machine and reported that file's version, and on\n // governor panels it abstained \"no repository or accessible sandbox was\n // provided\". The task's `workDir` (the same option the claude adapter\n // honours) wins; otherwise the process cwd, which is what the subprocess\n // inherits and what the voter prompt names.\n const workDir = task.options?.['workDir'];\n args.push('--add-dir', typeof workDir === 'string' && workDir !== '' ? workDir : process.cwd());\n\n // #6277: agy's own print-mode wait defaults to 5m0s regardless of the\n // budget the caller gave us. Measured on the #6260 governor panels: a\n // contrarian seat with a 600 s budget hit agy's 5-minute default, agy\n // exited 0 with {\"status\":\"SUCCESS\",\"response\":\"\"}, and the vote path\n // read the empty answer as a parse failure for the rest of the budget.\n // Derive the CLI's wait from the task timeout, a little below the guard\n // so agy's own timeout fires first and its stderr is readable.\n args.push(...agyPrintTimeoutArgs(task.timeoutMs));\n\n args.push(...agyAccessModeArgs(task));\n\n // agy has no system-prompt flag. The old CLI's `--policy <file>` preserved\n // system-role framing (#1886); agy offers only `--agent`, which selects a\n // preconfigured agent rather than accepting inline instructions. So the\n // system prompt is prepended to the user content — a deliberate downgrade\n // in framing fidelity, recorded here so it is not mistaken for an\n // oversight. No temp file is written, which also removes the tempdir-leak\n // surface the old path had.\n const content =\n task.systemPrompt !== undefined && task.systemPrompt !== ''\n ? `${task.systemPrompt}\\n\\n${task.content}`\n : task.content;\n\n // --print LAST with an explicit value. agy accepts flags in any order, but\n // a valueless --print consumes whatever token follows it, so the prompt is\n // always passed as its argument rather than positionally.\n args.push('--print', content);\n\n return { command: this.binaryName, args };\n }\n\n private checkCircuitBreaker(): CliError | null {\n if (this.circuitBreaker === null) {\n return null;\n }\n if (this.circuitBreaker.getState() === 'open') {\n return createCircuitOpenError('gemini');\n }\n return null;\n }\n\n private buildExecutionOptions(\n taskContent: string,\n options?: ExecutionOptions\n ): ResolvedExecutionOptions {\n const complexity = estimateTaskComplexity(taskContent);\n const timeoutMs = options?.timeoutMs ?? getTimeoutForTask(this.name, complexity);\n\n return {\n timeoutMs,\n allowRetry: options?.allowRetry ?? true,\n maxRetries: options?.maxRetries ?? this.maxRetries,\n trackUsage: options?.trackUsage ?? true,\n onProgress: options?.onProgress,\n };\n }\n\n private buildExecutionResult(\n result: Result<{ response: CliResponse; retryCount: number }, CliError>,\n startTime: number,\n complexity: TaskComplexity\n ): Result<GeminiExecutionResult, CliError> {\n const totalDurationMs = getTimeProvider().now() - startTime;\n const circuitState = this.circuitBreaker?.getState() ?? 'closed';\n\n if (result.ok) {\n this.circuitBreaker?.recordSuccess();\n return ok({\n response: result.value.response,\n retryCount: result.value.retryCount,\n totalDurationMs,\n complexity,\n circuitState,\n });\n }\n\n return err(result.error);\n }\n\n private async executeWithRetryTracking(\n task: CliTask,\n options: ResolvedExecutionOptions\n ): Promise<Result<{ response: CliResponse; retryCount: number }, CliError>> {\n return executeCliRetryLoop(() => this.executeTask(task, options), {\n maxRetries: options.maxRetries,\n allowRetry: this.shouldOuterRetry(options),\n baseDelayMs: this.baseDelayMs,\n maxDelayMs: this.maxDelayMs,\n circuitBreaker: this.circuitBreaker,\n cli: this.name,\n logger: this.adapterLogger,\n });\n }\n}\n\n/** Creates a Gemini CLI adapter with reliability features. */\nexport function createGeminiAdapter(options?: GeminiConfig): GeminiCliAdapter {\n return new GeminiCliAdapter(options);\n}\n\n/** agy's argv for the task's access mode (#6754); empty in the default mode. */\nfunction agyAccessModeArgs(task: CliTask): readonly string[] {\n return isReadOnlyAnalysis(task) ? AGY_READ_ONLY_ARGS : [];\n}\n\n/** Headroom between agy's print-mode wait and the subprocess guard (#6277). */\nconst AGY_PRINT_TIMEOUT_HEADROOM_MS = 5_000;\n/** Shortest wait handed to agy; below this a seat cannot answer at all. */\nconst AGY_PRINT_TIMEOUT_FLOOR_S = 30;\n\n/**\n * agy's `--print-timeout` argv (Go duration, whole seconds) for a task\n * budget: the guard minus a fixed headroom, never below the floor. No\n * budget → no flag, so agy's own default applies.\n */\nfunction agyPrintTimeoutArgs(timeoutMs: number | undefined): readonly string[] {\n if (timeoutMs === undefined) return [];\n const seconds = Math.floor((timeoutMs - AGY_PRINT_TIMEOUT_HEADROOM_MS) / 1000);\n return ['--print-timeout', `${String(Math.max(AGY_PRINT_TIMEOUT_FLOOR_S, seconds))}s`];\n}\n","/**\n * nexus-agents/cli-adapters - Codex CLI Adapter\n *\n * Subprocess-based adapter for Codex CLI.\n * Extends SubprocessCliAdapter to reuse retry logic, health checks,\n * version detection, and capacity tracking.\n *\n * (Source: cli-project_plan.md v2.1.0)\n * (Source: Issue #1140 — Migrated to SubprocessCliAdapter base class)\n *\n * SECURITY: All spawn() calls use array-based args without shell: true.\n * User task content is passed as a single argv element (no shell interpolation).\n */\n\nimport { writeFileSync, rmSync } from 'node:fs';\nimport { nexusMkdtempSync } from '../../config/nexus-tmp-dir.js';\nimport { join } from 'node:path';\nimport type { ICliResponseParser, CliTask, ModelInfo, CliName } from '../types.js';\nimport { SubprocessCliAdapter, type CommandConfig } from '../subprocess-adapter.js';\nimport { CodexResponseParser } from '../parsers/codex-parser.js';\nimport type { CliModelInfo } from '../types-capability.js';\nimport { listModelsForCli } from '../../config/models-dev-by-vendor.js';\nimport {\n CODEX_LEGACY_DEFAULTS,\n type CodexAdapterOptions,\n codexPlatformSandboxArgs,\n toCodexModelSlug,\n} from './codex-adapter-helpers.js';\nimport {\n getDefaultModelForCli,\n getCliModelName,\n buildModelInfo,\n} from '../../config/model-config-helpers.js';\n\n// Re-export CLI-specific helpers for backward compatibility\nexport { createCodexError, normalizeCodexResponse, delay } from './codex-adapter-helpers.js';\n\n/**\n * Codex CLI adapter using subprocess transport.\n *\n * Extends SubprocessCliAdapter which provides:\n * - Retry logic with exponential backoff\n * - Health checks with version compatibility\n * - Capacity tracking\n * - Subprocess spawn with timeout handling\n */\n/**\n * The codex sandbox every `codex exec` runs under (#6754). Read-only analysis\n * mode needs nothing beyond it: `-s read-only` already refuses writes and\n * network, so this adapter enforces the mode on every task, not only on the\n * ones that ask. Named so a change to the default is a visible change to the\n * mode's guarantee.\n */\nconst CODEX_EXEC_SANDBOX = 'read-only';\n\nexport class CodexCliAdapter extends SubprocessCliAdapter {\n readonly name: CliName = 'codex';\n override readonly enforcesReadOnlyAnalysis = true;\n protected readonly parser: ICliResponseParser = new CodexResponseParser();\n\n private readonly model: string;\n private readonly platform: NodeJS.Platform;\n\n constructor(options?: CodexAdapterOptions) {\n super(options?.logger);\n this.model = options?.model ?? getCliModelName(getDefaultModelForCli('codex'));\n this.platform = options?.platform ?? process.platform;\n }\n\n /** Key-free model enumeration via the models.dev snapshot (#3405). */\n listModels(): Promise<readonly CliModelInfo[]> {\n return Promise.resolve(listModelsForCli(this.name));\n }\n\n /**\n * Gets Codex model information.\n * Resolves from canonical registry when possible, falls back to legacy lookup.\n */\n getModelInfo(): ModelInfo {\n const fromRegistry = buildModelInfo('codex', this.model);\n if (fromRegistry !== undefined) return fromRegistry;\n return {\n id: this.model,\n name: CODEX_LEGACY_DEFAULTS.displayNames[this.model] ?? this.model,\n contextWindow: CODEX_LEGACY_DEFAULTS.contextWindow,\n maxOutput: CODEX_LEGACY_DEFAULTS.maxOutput,\n costPerMillionInput:\n CODEX_LEGACY_DEFAULTS.inputCosts[this.model] ?? CODEX_LEGACY_DEFAULTS.inputCost,\n costPerMillionOutput:\n CODEX_LEGACY_DEFAULTS.outputCosts[this.model] ?? CODEX_LEGACY_DEFAULTS.outputCost,\n };\n }\n\n /**\n * Gets CLI command and arguments for execution.\n * Task content is passed as a positional argument (not via stdin).\n */\n protected getCommand(task: CliTask): CommandConfig {\n const args: string[] = ['exec'];\n\n // Add JSON output\n args.push('--json');\n\n // Add model only if specified (use CLI default otherwise). task.model is\n // the canonical registry id, which codex rejects — translate it (#5091).\n const model = task.model ?? this.model;\n if (model !== '') {\n args.push('-m', toCodexModelSlug(model, this.logger));\n }\n\n // Add sandbox mode for safety (read-only by default). On Linux, back it\n // with legacy landlock so the read-only sandbox actually starts under\n // AppArmor's userns restriction (#6093, see codexPlatformSandboxArgs).\n args.push('-s', CODEX_EXEC_SANDBOX, ...codexPlatformSandboxArgs(this.platform));\n\n // Skip git repo check for standalone prompts\n args.push('--skip-git-repo-check');\n\n // Honor systemPrompt via codex's `model_instructions_file` config key\n // (per openai/codex#11588 — closed with this workaround). We materialize\n // the prompt into a tempfile, pass it via `-c`, and clean up on exit.\n let cleanup: (() => void) | undefined;\n if (task.systemPrompt !== undefined && task.systemPrompt !== '') {\n const dir = nexusMkdtempSync('nexus-codex-sysprompt-');\n const file = join(dir, 'instructions.md');\n writeFileSync(file, task.systemPrompt, { encoding: 'utf8', mode: 0o600 });\n args.push('-c', `model_instructions_file=${file}`);\n cleanup = (): void => {\n // Recursive rm so we drop the parent tempdir, not just the file\n // inside it. Pre-fix every codex call with a systemPrompt leaked\n // one empty `/tmp/nexus-codex-sysprompt-XXXXXX` dir until the OS\n // reaper ran — exhausting inodes on long-running MCP daemons.\n try {\n rmSync(dir, { recursive: true, force: true });\n } catch {\n // best-effort; tempdir auto-cleanup will eventually reap it\n }\n };\n }\n\n // Add the task content (no JSON.stringify needed without shell: true)\n args.push(task.content);\n\n return cleanup === undefined ? { command: 'codex', args } : { command: 'codex', args, cleanup };\n }\n}\n","/**\n * nexus-agents/cli-adapters - Codex CLI Response Parser\n *\n * Defensive parser for Codex CLI NDJSON output.\n * Handles version 0.7x.x output format.\n *\n * (Source: cli-project_plan.md v2.1.0)\n * (Source: docs/research/cli-integration-architecture.md)\n */\n\nimport type { ICliResponseParser, TokenUsage } from '../types.js';\nimport { asRecord, extractNumberField } from '../../utils/type-coercion.js';\nimport { createLogger } from '../../core/index.js';\n\nconst logger = createLogger({ component: 'codex-parser' });\n\n/**\n * Codex CLI NDJSON event types.\n */\nexport type CodexEventType =\n | 'thread.started'\n | 'turn.started'\n | 'item.completed'\n | 'turn.completed';\n\n/**\n * Base Codex event structure.\n */\nexport interface CodexEvent {\n readonly type: CodexEventType;\n}\n\n/**\n * Thread started event.\n */\nexport interface CodexThreadStarted extends CodexEvent {\n readonly type: 'thread.started';\n readonly thread_id: string;\n}\n\n/**\n * Item completed event (contains response or reasoning).\n */\nexport interface CodexItemCompleted extends CodexEvent {\n readonly type: 'item.completed';\n readonly item: {\n readonly id: string;\n readonly type: string;\n readonly text: string;\n };\n}\n\n/**\n * Turn completed event (contains usage).\n */\nexport interface CodexTurnCompleted extends CodexEvent {\n readonly type: 'turn.completed';\n readonly usage?: {\n readonly input_tokens: number;\n readonly cached_input_tokens?: number;\n readonly output_tokens: number;\n };\n}\n\n/**\n * Aggregated Codex response from NDJSON stream.\n */\nexport interface CodexCliResponse {\n readonly threadId?: string;\n readonly messages: readonly string[];\n readonly reasoning: readonly string[];\n readonly usage?: TokenUsage;\n}\n\n/**\n * Parser for Codex CLI NDJSON output.\n * Implements defensive parsing - processes stream of events.\n */\nexport class CodexResponseParser implements ICliResponseParser<CodexCliResponse> {\n readonly name = 'codex-parser';\n readonly supportedVersionRange = '>=0.70.0 <1.0.0';\n\n /**\n * Parses complete Codex CLI NDJSON stream.\n */\n parse(raw: string): CodexCliResponse | null {\n const lines = raw.trim().split('\\n');\n let threadId: string | undefined;\n const messages: string[] = [];\n const reasoning: string[] = [];\n let usage: TokenUsage | undefined;\n\n for (const line of lines) {\n if (line.trim() === '') continue;\n this.processLine(\n line,\n messages,\n reasoning,\n (id) => (threadId = id),\n (u) => (usage = u)\n );\n }\n\n if (messages.length === 0 && reasoning.length === 0) {\n return null;\n }\n\n return {\n messages,\n reasoning,\n ...(threadId !== undefined && { threadId }),\n ...(usage !== undefined && { usage }),\n };\n }\n\n /**\n * Processes a single NDJSON line.\n */\n private processLine(\n line: string,\n messages: string[],\n reasoning: string[],\n setThreadId: (id: string) => void,\n setUsage: (usage: TokenUsage) => void\n ): void {\n try {\n const event: unknown = JSON.parse(line);\n const record = asRecord(event);\n if (record === null) return;\n\n const eventType = record.type;\n\n if (eventType === 'thread.started') {\n const tid = record.thread_id;\n if (typeof tid === 'string') setThreadId(tid);\n } else if (eventType === 'item.completed') {\n this.processItemCompleted(record, messages, reasoning);\n } else if (eventType === 'turn.completed') {\n const usage = this.extractUsageFromEvent(record);\n if (usage !== null) setUsage(usage);\n }\n } catch {\n // Skip malformed NDJSON lines\n logger.debug('Skipped malformed NDJSON line', { snippet: line.slice(0, 100) });\n }\n }\n\n /**\n * Extracts just the response text (most stable field).\n * Concatenates all agent_message items.\n */\n extractResponse(raw: string): string | null {\n const parsed = this.parse(raw);\n if (parsed === null || parsed.messages.length === 0) {\n return null;\n }\n\n return parsed.messages.join('\\n');\n }\n\n /**\n * Extracts token usage from NDJSON stream.\n */\n extractUsage(raw: string): TokenUsage | null {\n const lines = raw.trim().split('\\n');\n\n for (const line of lines) {\n if (line.trim() === '') continue;\n\n try {\n const event: unknown = JSON.parse(line);\n const record = asRecord(event);\n if (record === null) continue;\n\n if (record.type === 'turn.completed') {\n return this.extractUsageFromEvent(record);\n }\n } catch {\n logger.debug('Skipped malformed NDJSON line', { snippet: line.slice(0, 100) });\n continue;\n }\n }\n\n return null;\n }\n\n /**\n * Extracts session ID (thread_id) for resumption.\n */\n extractSessionId(raw: string): string | null {\n const lines = raw.trim().split('\\n');\n\n for (const line of lines) {\n if (line.trim() === '') continue;\n\n try {\n const event: unknown = JSON.parse(line);\n const record = asRecord(event);\n if (record === null) continue;\n\n if (record.type === 'thread.started') {\n const threadId = record.thread_id;\n if (typeof threadId === 'string') {\n return threadId;\n }\n }\n } catch {\n logger.debug('Skipped malformed NDJSON line', { snippet: line.slice(0, 100) });\n continue;\n }\n }\n\n return null;\n }\n\n /**\n * Processes an item.completed event.\n */\n private processItemCompleted(\n record: Record<string, unknown>,\n messages: string[],\n reasoning: string[]\n ): void {\n const item = asRecord(record.item);\n if (item === null) return;\n\n const itemType = item.type;\n const text = item.text;\n\n if (typeof text !== 'string') return;\n\n if (itemType === 'agent_message') {\n messages.push(text);\n } else if (itemType === 'reasoning') {\n reasoning.push(text);\n }\n }\n\n /**\n * Extracts usage from a turn.completed event.\n */\n private extractUsageFromEvent(record: Record<string, unknown>): TokenUsage | null {\n const usage = asRecord(record.usage);\n if (usage === null) return null;\n\n const inputTokens = extractNumberField(usage, 'input_tokens');\n const outputTokens = extractNumberField(usage, 'output_tokens');\n\n if (inputTokens === null || outputTokens === null) {\n return null;\n }\n\n const cachedInputTokens = extractNumberField(usage, 'cached_input_tokens');\n\n return {\n inputTokens,\n outputTokens,\n totalTokens: inputTokens + outputTokens,\n ...(cachedInputTokens !== null && { cachedInputTokens }),\n };\n }\n}\n","/**\n * nexus-agents/cli-adapters - Codex CLI Adapter Helpers\n *\n * CLI-specific helper functions for Codex subprocess adapter.\n * Model info lookups consolidated into config/model-config-helpers.ts (#886).\n */\n\nimport type { BaseAdapterOptions, CliError, CliName, TokenUsage, CliResponse } from '../types.js';\nimport { createCliError as sharedCreateCliError } from '../cli-error-helpers.js';\nimport type { ILogger } from '../../core/logger.js';\nimport { resolveCliModelName } from '../../config/model-config-helpers.js';\n\n// -----------------------------------------------------------------------------\n// Legacy Fallback Defaults (for non-canonical models)\n// -----------------------------------------------------------------------------\n\n/** Legacy fallback values for Codex models not in the canonical registry. */\nexport const CODEX_LEGACY_DEFAULTS = {\n displayNames: {\n o3: 'O3',\n 'o3-mini': 'O3 Mini',\n 'o4-mini': 'O4 Mini',\n } as Readonly<Record<string, string>>,\n inputCosts: {\n o3: 10.0,\n 'o3-mini': 1.1,\n 'o4-mini': 1.1,\n } as Readonly<Record<string, number>>,\n outputCosts: {\n o3: 40.0,\n 'o3-mini': 4.4,\n 'o4-mini': 4.4,\n } as Readonly<Record<string, number>>,\n contextWindow: 400_000,\n maxOutput: 100_000,\n inputCost: 1.1,\n outputCost: 4.4,\n} as const;\n\n// -----------------------------------------------------------------------------\n// Sandbox backend selection (#6093)\n// -----------------------------------------------------------------------------\n\n/** Options accepted by both codex transports (subprocess and MCP). */\nexport interface CodexAdapterOptions extends BaseAdapterOptions {\n /**\n * Host platform the sandbox arguments are chosen for. Defaults to\n * `process.platform`; injectable so a test can exercise the Linux and\n * non-Linux branches without mocking a global (#6093).\n */\n readonly platform?: NodeJS.Platform;\n}\n\n/**\n * The `-c key=value` override that switches codex's Linux sandbox from bwrap\n * to the legacy landlock backend. Spelled exactly as `codex features list`\n * names it (`use_legacy_landlock`, flagged deprecated on codex-cli 0.153.4):\n * `-c` accepts any key, so a misspelling would be silently ignored.\n */\nconst CODEX_LEGACY_LANDLOCK_CONFIG = 'features.use_legacy_landlock=true';\n\n/**\n * Extra codex argv for the host platform: `['-c', CODEX_LEGACY_LANDLOCK_CONFIG]`\n * on Linux, nothing elsewhere.\n *\n * Why (#6093, measured on codex-cli 0.153.4 / bwrap 0.9.0): on hosts with\n * `kernel.apparmor_restrict_unprivileged_userns=1` (the Ubuntu 24.04+ default)\n * every bwrap-backed sandbox mode — `read-only`, `workspace-write`, network\n * off — dies with `bwrap: loopback: Failed RTM_NEWADDR: Operation not\n * permitted` before codex reads a single file, so the two codex voter seats\n * abstained with \"repository inspection failed\" or voted on the proposal\n * text alone. With the legacy landlock backend reads work and writes are\n * still refused (`Permission denied`). The read scope is read-only-all-disk,\n * the same scope the read-only bwrap profile already granted, so this widens\n * nothing: a seat could always read dotfiles and echo them into its\n * reasoning. The host-level alternative is\n * `kernel.apparmor_restrict_unprivileged_userns=0` or an AppArmor profile for\n * bwrap; this flag makes the seats work without host changes.\n *\n * Shared by `CodexCliAdapter` (`codex exec`) and `CodexMcpAdapter`\n * (`codex mcp-server`) so the platform gate exists once. Both subcommands\n * accept `-c` after the subcommand name (verified with `--help`, which rejects\n * an unknown flag in that position with exit 2).\n */\nexport function codexPlatformSandboxArgs(\n platform: NodeJS.Platform = process.platform\n): readonly string[] {\n return platform === 'linux' ? ['-c', CODEX_LEGACY_LANDLOCK_CONFIG] : [];\n}\n\n// -----------------------------------------------------------------------------\n// Error Handling\n// -----------------------------------------------------------------------------\n\n/**\n * Creates a CLI error with the canonical retryable-flag logic.\n * Kept as an alias under this name for backward compatibility with callers\n * that imported `createCodexError` before the helper was consolidated in\n * `cli-error-helpers.ts` (#2181). Prefer `createCliError` from the shared\n * helper in new code.\n */\nexport function createCodexError(\n code: CliError['code'],\n message: string,\n cli: CliName,\n cause?: Error\n): CliError {\n return sharedCreateCliError(code, message, cli, cause);\n}\n\n// -----------------------------------------------------------------------------\n// Response Normalization\n// -----------------------------------------------------------------------------\n\n/** Normalizes CLI response to common format. */\nexport function normalizeCodexResponse(\n text: string,\n usage?: TokenUsage,\n extra?: Partial<CliResponse>\n): CliResponse {\n return {\n text,\n ...(usage !== undefined && { usage }),\n ...extra,\n };\n}\n\n// Re-export from canonical source for backward compatibility\nexport { delay } from '../../utils/async-utils.js';\n\n/**\n * Translate a model identifier to the slug the `codex` binary accepts (#5091).\n *\n * `task.model` arrives as the canonical registry id (`codex-5.3`), which codex\n * rejects; the registry's `cliModelName` (`gpt-5.4`) is what `-m` and the MCP\n * `model` argument want. Shared by the subprocess and MCP transports so the\n * translation exists once.\n *\n * Unknown model: passed through verbatim with a warning, the same fail-open\n * choice `claude-adapter.ts` (`MODEL_TO_CLI_ALIAS[m] ?? m`) and\n * `opencode-adapter.ts` (`resolveOpenCodeModel`) make. A caller pinning a slug\n * the registry has not caught up with (codex ships new ones between releases)\n * must still be able to run it, and codex rejects a bad `-m` with a non-zero\n * exit, so the failure stays visible — unlike agy, whose exit-0 error is why\n * `toAgyModelSlug` substitutes a default instead.\n */\nexport function toCodexModelSlug(model: string, logger: ILogger): string {\n const slug = resolveCliModelName('codex', model);\n if (slug === undefined) {\n logger.warn('Model is not in the model registry for codex; passing it to codex unchanged', {\n model,\n });\n return model;\n }\n return slug;\n}\n","/**\n * nexus-agents/cli-adapters - Codex MCP Adapter\n *\n * MCP-based adapter for Codex CLI. Preferred transport for Codex integration.\n * Extends BaseCliAdapter to reuse retry logic, health checks, version\n * detection, and capacity tracking.\n *\n * (Source: Issue #1140 — Migrated to BaseCliAdapter base class)\n *\n * SECURITY: All spawn() calls use array-based args without shell interpolation.\n */\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';\nimport type {\n CliName,\n CliTransport,\n CliTask,\n CliResponse,\n CliError,\n ModelInfo,\n ResolvedExecutionOptions,\n} from '../types.js';\nimport type { Result } from '../../core/index.js';\nimport { getErrorMessage, ok, err, getTimeProvider, createLogger } from '../../core/index.js';\nimport type { CliModelInfo } from '../types-capability.js';\nimport { listModelsForCli } from '../../config/models-dev-by-vendor.js';\nimport { BaseCliAdapter } from '../base-adapter.js';\nimport { isReadOnlyAnalysis, readOnlyAnalysisConflict } from '../read-only-analysis.js';\nimport { MAX_RESPONSE_STDERR_CHARS } from '../subprocess-adapter.js';\n\nimport {\n type CodexAdapterOptions,\n codexPlatformSandboxArgs,\n toCodexModelSlug,\n} from './codex-adapter-helpers.js';\nimport {\n CODEX_LEGACY_DEFAULTS,\n type McpToolResult,\n extractTextFromContent,\n createTimeout,\n determineErrorCode,\n NEXUS_MCP_DEPTH_ENV,\n nextCodexMcpDepthOrThrow,\n} from './codex-mcp-adapter-helpers.js';\nimport {\n getDefaultModelForCli,\n getCliModelName,\n buildModelInfo,\n} from '../../config/model-config-helpers.js';\n\n/**\n * Codex CLI adapter using MCP transport.\n *\n * Extends BaseCliAdapter which provides:\n * - Retry logic with exponential backoff\n * - Health checks with version compatibility\n * - Capacity tracking\n * - Error creation helpers\n */\nexport class CodexMcpAdapter extends BaseCliAdapter {\n readonly name: CliName = 'codex';\n /**\n * #6754: a new thread is started with `sandbox: 'read-only'` on every call.\n * A `codex-reply` carries no sandbox argument, so a read-only task that\n * continues a session is refused in {@link accessModeRefusal}.\n */\n override readonly enforcesReadOnlyAnalysis = true;\n readonly transport: CliTransport = 'mcp';\n\n private readonly model: string;\n private readonly platform: NodeJS.Platform;\n private client: Client | undefined;\n private mcpTransport: StdioClientTransport | undefined;\n private connected = false;\n /**\n * Stderr attribution state (#6094). The registry caches one adapter per CLI\n * and voter roles run under `Promise.all`, so the mcp-server's stderr pipe is\n * shared by every in-flight call. `inFlight` is the current overlap;\n * `overlapEpoch` advances whenever a call starts while another is in flight,\n * so a capture that began alone can still see that it was later overlapped.\n */\n private inFlight = 0;\n private overlapEpoch = 0;\n\n constructor(options?: CodexAdapterOptions) {\n super(options?.logger ?? createLogger({ component: 'codex-mcp-adapter' }));\n this.model = options?.model ?? getCliModelName(getDefaultModelForCli('codex'));\n this.platform = options?.platform ?? process.platform;\n }\n\n /**\n * Key-free model enumeration via the models.dev snapshot (#3405), matching\n * `CodexCliAdapter`.\n *\n * #4318: this was missing, and `buildDefaultModelSources` includes an adapter\n * only when `hasListModels(adapter)` is true. Since `createAllAdapters`\n * defaults codex to the mcp transport, codex was silently filtered out of\n * `list_available_models` — the probe reported one fewer transport than it\n * had, with no error anywhere. Model enumeration is transport-independent, so\n * the two adapters must answer identically.\n */\n listModels(): Promise<readonly CliModelInfo[]> {\n return Promise.resolve(listModelsForCli(this.name));\n }\n\n /**\n * Gets Codex model information.\n * Resolves from canonical registry when possible, falls back to legacy lookup.\n */\n getModelInfo(): ModelInfo {\n const fromRegistry = buildModelInfo('codex', this.model);\n if (fromRegistry !== undefined) return fromRegistry;\n return {\n id: this.model,\n name: CODEX_LEGACY_DEFAULTS.displayNames[this.model] ?? this.model,\n contextWindow: CODEX_LEGACY_DEFAULTS.contextWindow,\n maxOutput: CODEX_LEGACY_DEFAULTS.maxOutput,\n costPerMillionInput:\n CODEX_LEGACY_DEFAULTS.inputCosts[this.model] ?? CODEX_LEGACY_DEFAULTS.inputCost,\n costPerMillionOutput:\n CODEX_LEGACY_DEFAULTS.outputCosts[this.model] ?? CODEX_LEGACY_DEFAULTS.outputCost,\n };\n }\n\n /**\n * Initializes the MCP connection to Codex.\n */\n async initialize(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n this.initCapacityTracker();\n this.logger.debug('Initializing Codex MCP connection');\n\n // Recursion guard (#3350): refuse to spawn `codex mcp-server` if we're\n // already nested inside a nexus-spawned codex MCP context, and stamp the\n // child with the incremented depth so it (and anything it spawns) inherits\n // the marker. Throws before any spawn when nested — breaking the loop.\n const childDepth = nextCodexMcpDepthOrThrow();\n\n try {\n // The `sandbox: 'read-only'` tool call below runs inside codex's Linux\n // sandbox; on Linux, select the legacy landlock backend so it starts\n // under AppArmor's userns restriction (#6093, codexPlatformSandboxArgs).\n this.mcpTransport = new StdioClientTransport({\n command: 'codex',\n args: ['mcp-server', ...codexPlatformSandboxArgs(this.platform)],\n stderr: 'pipe',\n env: { [NEXUS_MCP_DEPTH_ENV]: childDepth },\n });\n\n this.client = new Client({ name: 'nexus-agents', version: '2.0.0' }, { capabilities: {} });\n await this.client.connect(this.mcpTransport);\n this.connected = true;\n this.initialized = true;\n this.logger.info('Codex MCP connection established');\n } catch (error) {\n this.connected = false;\n const connectionError = error instanceof Error ? error : new Error('Connection failed');\n this.logger.error('Failed to initialize Codex MCP connection', connectionError);\n throw error;\n }\n }\n\n /**\n * Executes a task via MCP client.\n * Called by BaseCliAdapter.execute() with retry handling.\n */\n async executeTask(\n _task: CliTask,\n options: ResolvedExecutionOptions\n ): Promise<Result<CliResponse, CliError>> {\n const startTime = getTimeProvider().now();\n\n if (this.client === undefined) {\n return err(this.createError('CONNECTION_ERROR', 'MCP client not initialized'));\n }\n\n // #6094: read the mcp-server's piped stderr for the duration of THIS call.\n // A sandbox failure inside the codex tool loop (bwrap on a userns-\n // restricted host) is written there while the tool result still carries\n // the model's parsed answer; without this the pipe was never read.\n const stderrCapture = this.captureTransportStderr();\n try {\n const result = await Promise.race([\n this.callCodexTool(_task),\n createTimeout(options.timeoutMs),\n ]);\n\n if (result === null) {\n return err(this.createError('TIMEOUT', 'Execution timed out'));\n }\n\n return this.parseToolResult(result, startTime, stderrCapture.stop());\n } catch (error) {\n return this.handleExecutionError(error);\n } finally {\n stderrCapture.stop();\n }\n }\n\n /**\n * Attach a listener to the transport's stderr pipe; `stop()` detaches it\n * and returns what was written meanwhile (capped like the subprocess path).\n * A transport without a readable stderr (tests, or `stderr: 'inherit'`)\n * yields an empty capture, which the caller records as absent.\n *\n * ATTRIBUTION (#6094 review): the pipe is process-wide, so a line written\n * while two calls overlap cannot be assigned to either. If another call was\n * in flight at ANY point during this capture, the capture is discarded\n * (debug-logged) and the seat falls through to the reasoning fallback.\n * Attributing it to both would classify a seat that read the artifact as\n * unverifiable.\n */\n private captureTransportStderr(): { stop: () => string } {\n this.inFlight++;\n if (this.inFlight > 1) this.overlapEpoch++;\n const epochAtStart = this.overlapEpoch;\n const overlappedAtStart = this.inFlight > 1;\n const stream = (this.mcpTransport as { stderr?: NodeJS.ReadableStream | null } | undefined)\n ?.stderr;\n let captured = '';\n const onData = (chunk: Buffer | string): void => {\n if (captured.length < MAX_RESPONSE_STDERR_CHARS) captured += chunk.toString();\n };\n stream?.on('data', onData);\n let stopped = false;\n return {\n stop: (): string => {\n if (stopped) return '';\n stopped = true;\n stream?.off('data', onData);\n this.inFlight--;\n const overlapped = overlappedAtStart || this.overlapEpoch !== epochAtStart;\n if (overlapped && captured !== '') {\n this.logger.debug('Codex MCP stderr not attributed: another call was in flight', {\n capturedChars: captured.length,\n });\n return '';\n }\n return captured.slice(0, MAX_RESPONSE_STDERR_CHARS);\n },\n };\n }\n\n /** #6754: a continued session cannot be pinned to the read-only sandbox. */\n protected override accessModeRefusal(task: CliTask): CliError | undefined {\n const base = super.accessModeRefusal(task);\n if (base !== undefined || !isReadOnlyAnalysis(task)) return base;\n if (task.sessionId !== undefined && task.sessionId !== '') {\n return readOnlyAnalysisConflict(\n this.name,\n 'a continued codex session carries no sandbox setting'\n );\n }\n return undefined;\n }\n\n /**\n * Calls the codex or codex-reply tool on Codex MCP server.\n * @see https://developers.openai.com/codex/mcp/\n */\n private async callCodexTool(task: CliTask): Promise<McpToolResult> {\n if (this.client === undefined) {\n throw new Error('Client not initialized');\n }\n\n const isReply = task.sessionId !== undefined && task.sessionId !== '';\n const toolName = isReply ? 'codex-reply' : 'codex';\n\n // task.model is the canonical registry id, which codex rejects — translate\n // it to the slug the MCP server's `model` argument accepts (#5091).\n const baseArgs = {\n prompt: task.content,\n ...(task.model !== undefined && { model: toCodexModelSlug(task.model, this.logger) }),\n };\n\n const args = isReply\n ? { ...baseArgs, threadId: task.sessionId }\n : {\n ...baseArgs,\n sandbox: 'read-only' as const,\n 'approval-policy': 'never' as const,\n };\n\n const result = await this.client.callTool({\n name: toolName,\n arguments: args,\n });\n\n return result as McpToolResult;\n }\n\n /**\n * Parses MCP tool result to CLI response.\n */\n private parseToolResult(\n result: McpToolResult,\n startTime: number,\n stderr: string\n ): Result<CliResponse, CliError> {\n if (result.isError === true) {\n const errorText = extractTextFromContent(result.content);\n // #4373: this hardcoded EXECUTION_ERROR and never looked at the message,\n // so a quota-dead codex-mcp was indistinguishable from any other failure\n // and never counted as a serving failure against the circuit breaker the\n // voter gate reads (#4330). Classify the text the same way the error path\n // below does.\n const code = errorText === null ? 'EXECUTION_ERROR' : determineErrorCode(errorText);\n return err(this.createError(code, errorText ?? 'Tool execution failed'));\n }\n\n const text = extractTextFromContent(result.content);\n if (text === null) {\n return err(this.createError('PARSE_ERROR', 'No text content in response'));\n }\n\n return ok({\n text,\n durationMs: getTimeProvider().now() - startTime,\n raw: result,\n ...(stderr !== '' ? { stderr } : {}),\n });\n }\n\n /**\n * Handles execution errors.\n */\n private handleExecutionError(error: unknown): Result<CliResponse, CliError> {\n const message = getErrorMessage(error);\n const errorCode = determineErrorCode(message);\n\n if (errorCode === 'CONNECTION_ERROR') {\n this.connected = false;\n }\n\n return err(this.createError(errorCode, message, error as Error));\n }\n\n /**\n * Disposes the adapter and closes MCP connection.\n */\n async dispose(): Promise<void> {\n if (this.mcpTransport !== undefined) {\n this.logger.debug('Closing Codex MCP connection');\n await this.mcpTransport.close();\n this.mcpTransport = undefined;\n }\n this.client = undefined;\n this.connected = false;\n this.initialized = false;\n }\n}\n","/**\n * nexus-agents/cli-adapters - Codex MCP Adapter Helpers\n *\n * CLI-specific helper functions for Codex MCP adapter.\n * Model info lookups consolidated into config/model-config-helpers.ts (#886).\n *\n * (Source: cli-project_plan.md v2.1.0, Issue #90)\n */\n\nimport type { ResolvedExecutionOptions, CliError, CliName } from '../types.js';\nimport { CODEX_MCP_TIMEOUTS } from '../../config/timeouts.js';\nimport { isRateLimitText } from '../../adapters/rate-limit-detector.js';\nimport {\n createCliError as sharedCreateCliError,\n isRetryableErrorCode as sharedIsRetryableErrorCode,\n} from '../cli-error-helpers.js';\n\n// Re-export legacy defaults from the subprocess helpers (DRY)\nexport { CODEX_LEGACY_DEFAULTS } from './codex-adapter-helpers.js';\n\n/**\n * Default execution options for Codex MCP.\n * Timeout and retry values derived from config/timeouts.ts (#1220).\n */\nexport const DEFAULT_CODEX_MCP_OPTIONS: ResolvedExecutionOptions = {\n timeoutMs: CODEX_MCP_TIMEOUTS.defaultMs,\n allowRetry: true,\n maxRetries: CODEX_MCP_TIMEOUTS.maxRetries,\n trackUsage: true,\n onProgress: undefined,\n};\n\n// ---------------------------------------------------------------------------\n// Recursion guard for `codex mcp-server` spawning (#3350)\n// ---------------------------------------------------------------------------\n\n/**\n * Env var stamped on the `codex mcp-server` child nexus spawns, marking how\n * deep we are in the nexus→codex-mcp spawn nesting. Inherited by anything the\n * codex MCP server itself spawns (including a nested `nexus-agents\n * --mode=server` if codex is misconfigured to launch one).\n */\nexport const NEXUS_MCP_DEPTH_ENV = 'NEXUS_MCP_DEPTH';\n\n/**\n * Highest nesting depth at which nexus will spawn `codex mcp-server`. `0`\n * means only the top-level nexus process spawns it; any deeper attempt is the\n * recursive codex↔nexus loop and is refused.\n */\nconst MAX_CODEX_MCP_SPAWN_DEPTH = 0;\n\n/** Read the current nesting depth from an env bag; clamps missing/junk to 0. */\nexport function readMcpDepth(env: NodeJS.ProcessEnv = process.env): number {\n const raw = Number.parseInt(env[NEXUS_MCP_DEPTH_ENV] ?? '0', 10);\n return Number.isFinite(raw) && raw > 0 ? raw : 0;\n}\n\n/**\n * Recursion guard (#3350). Returns the depth string to stamp on the spawned\n * `codex mcp-server` child, or throws if we are already nested — which would\n * re-enter the codex↔nexus MCP spawn loop that corrupted the codex OAuth\n * token (dozens of leaked servers racing the shared refresh-token rotation).\n */\nexport function nextCodexMcpDepthOrThrow(env: NodeJS.ProcessEnv = process.env): string {\n const depth = readMcpDepth(env);\n if (depth > MAX_CODEX_MCP_SPAWN_DEPTH) {\n throw new Error(\n `Refusing to spawn 'codex mcp-server': already nested inside a ` +\n `nexus-spawned codex MCP context (${NEXUS_MCP_DEPTH_ENV}=${String(depth)}). ` +\n `This breaks the recursive codex↔nexus MCP spawn loop (#3350). If you ` +\n `intentionally configured codex to launch nexus-agents as an MCP ` +\n `server, remove that '[mcp_servers.nexus-agents]' entry from ~/.codex/config.toml.`\n );\n }\n return String(depth + 1);\n}\n\n/**\n * MCP tool call result structure.\n */\nexport interface McpToolResult {\n content?: Array<{ type: string; text?: string }>;\n isError?: boolean;\n}\n\n// -----------------------------------------------------------------------------\n// Error Handling\n// -----------------------------------------------------------------------------\n\n/**\n * Checks if an error code is retryable. Kept exported for backward\n * compatibility with callers that imported from this module; delegates\n * to the canonical helper (#2181).\n */\nexport function isRetryableErrorCode(code: CliError['code']): boolean {\n return sharedIsRetryableErrorCode(code);\n}\n\n/**\n * Creates a CLI error object with the canonical retryable-flag logic.\n * Kept exported for backward compatibility (#2181).\n */\nexport function createCliError(\n code: CliError['code'],\n message: string,\n cli: CliName,\n cause?: Error\n): CliError {\n return sharedCreateCliError(code, message, cli, cause);\n}\n\n// -----------------------------------------------------------------------------\n// Content Extraction\n// -----------------------------------------------------------------------------\n\n/** Extracts text from MCP content array. */\nexport function extractTextFromContent(\n content?: Array<{ type: string; text?: string }>\n): string | null {\n if (content === undefined || content.length === 0) {\n return null;\n }\n\n const textContents = content\n .filter((c): c is { type: string; text: string } => c.type === 'text' && c.text !== undefined)\n .map((c) => c.text);\n\n return textContents.length > 0 ? textContents.join('\\n') : null;\n}\n\n// Re-export from canonical source for backward compatibility\nexport { delay } from '../../utils/async-utils.js';\n\n/** Creates a timeout promise that resolves to null. */\nexport function createTimeout(ms: number): Promise<null> {\n return new Promise((resolve) => {\n setTimeout(() => {\n resolve(null);\n }, ms);\n });\n}\n\n/**\n * Determines error code from error message.\n *\n * The terminal/actionable readings are checked first: a missing binary or a\n * timeout is a better answer than \"rate limited\" even when the text mentions a\n * rate limit, because a rate-limit reading routes the caller into a retry that\n * can never succeed.\n */\nexport function determineErrorCode(message: string): CliError['code'] {\n if (message.includes('ENOENT') || message.includes('not found')) {\n return 'NOT_FOUND';\n }\n\n if (message.includes('timeout') || message.includes('ETIMEDOUT')) {\n return 'TIMEOUT';\n }\n\n if (message.includes('connection') || message.includes('disconnect')) {\n return 'CONNECTION_ERROR';\n }\n\n // #4373: this check was missing entirely. Every other adapter extends\n // SubprocessCliAdapter and inherits it; CodexMcpAdapter extends\n // BaseCliAdapter and classified on its own, so a quota-dead codex-mcp came\n // back as a generic EXECUTION_ERROR and never registered as a serving failure\n // with the circuit breaker the voter serving-gate reads (#4330).\n // Reuses the shared pattern set rather than adding a second taxonomy.\n if (isRateLimitText(message)) {\n return 'RATE_LIMITED';\n }\n\n return 'EXECUTION_ERROR';\n}\n\n/** Parses version string from codex --version output. */\nexport function parseVersionFromOutput(output: string): string {\n const match = /(\\d+\\.\\d+\\.\\d+)/.exec(output.trim());\n return match?.[1] ?? '0.0.0';\n}\n","/**\n * nexus-agents/cli-adapters - OpenCode CLI Adapter\n *\n * Subprocess-based adapter for OpenCode CLI.\n * Uses `opencode run --format json` for stable parsing.\n *\n * (Source: Issue #1124, opencode.ai/docs/cli/)\n */\n\nimport { execFile } from 'node:child_process';\n\nimport type {\n ICliResponseParser,\n CliTask,\n CliModelInfo,\n ModelInfo,\n CliName,\n BaseAdapterOptions,\n} from '../types.js';\nimport {\n SubprocessCliAdapter,\n type CommandConfig,\n type TransientRetryConfig,\n} from '../subprocess-adapter.js';\nimport { OpenCodeResponseParser } from '../parsers/opencode-parser.js';\nimport { createCallerInputCliError } from '../cli-error-helpers.js';\nimport { isReadOnlyAnalysis } from '../read-only-analysis.js';\nimport { isDynamicModelsEnabled } from '../../config/register-model-sources.js';\nimport { getAvailabilityCache } from '../../config/model-availability.js';\nimport type { ModelId } from '../../config/model-capabilities-types.js';\nimport type { Result } from '../../core/index.js';\nimport { ok, err } from '../../core/index.js';\nimport type { CliResponse, CliError } from '../types-core.js';\nimport type { ExecutionOptions, ResolvedExecutionOptions } from '../types-capability.js';\nimport {\n getDefaultModelForCli,\n getCliModelName,\n buildModelInfo,\n findInTreeByCli,\n findCanonicalModel,\n FALLBACK_CONTEXT_WINDOW,\n FALLBACK_MAX_OUTPUT,\n} from '../../config/model-config-helpers.js';\nimport { createLogger } from '../../core/index.js';\n\nconst logger = createLogger({ component: 'opencode-adapter' });\n\n/** Strict allowlist for OpenCode --variant flag values. */\nconst ALLOWED_VARIANTS = ['high', 'max', 'minimal'];\n\n/**\n * Maps internal model names to OpenCode CLI --model values.\n * OpenCode uses `provider/model-name` format (e.g., `anthropic/claude-sonnet-4-6`).\n * Built from canonical registry + common alias fallbacks (#1402).\n */\nconst MODEL_TO_CLI_NAME: Record<string, string> = buildOpenCodeAliasMap();\n\nfunction buildOpenCodeAliasMap(): Record<string, string> {\n const map: Record<string, string> = {};\n for (const model of findInTreeByCli('opencode')) {\n if (model.cliModelName === undefined) continue;\n // Map internal ID → CLI model name\n map[model.id] = model.cliModelName;\n // Map CLI alias → CLI model name\n if (model.cliAlias !== undefined) {\n map[model.cliAlias] = model.cliModelName;\n }\n // Pass through cliModelName itself\n map[model.cliModelName] = model.cliModelName;\n }\n return map;\n}\n\n/** Resolves an internal model name to OpenCode CLI format. */\nfunction resolveOpenCodeModel(model: string): string {\n return MODEL_TO_CLI_NAME[model] ?? model;\n}\n\n/**\n * Provider prefix opencode puts on gateway models: the registry and callers\n * say `qwen/qwen3-coder`, `opencode models` lists `openrouter/qwen/qwen3-coder`.\n */\nconst OPENROUTER_PREFIX = 'openrouter/';\n\n/**\n * Candidate opencode ids for a requested model, in preference order (#6599):\n * the name as given, then its registry mapping, each also tried under the\n * `openrouter/` prefix.\n */\nfunction openCodeCandidates(requested: string): string[] {\n const mapped = resolveOpenCodeModel(requested);\n const bases = mapped === requested ? [requested] : [requested, mapped];\n return bases.flatMap((b) => (b.startsWith(OPENROUTER_PREFIX) ? [b] : [b, OPENROUTER_PREFIX + b]));\n}\n\n/**\n * The id a response reports for `cliId`, and so the id cost is priced by: the\n * canonical registry id when one names this model (with or without the\n * `openrouter/` prefix), else the opencode id itself — which an unpriced\n * lookup then reports as unknown rather than at the default's price.\n */\nfunction reportedModelId(requested: string, cliId: string): string {\n const bare = cliId.startsWith(OPENROUTER_PREFIX) ? cliId.slice(OPENROUTER_PREFIX.length) : cliId;\n const entry =\n findCanonicalModel('opencode', requested) ??\n findCanonicalModel('opencode', cliId) ??\n findCanonicalModel('opencode', bare);\n return entry?.id ?? cliId;\n}\n\n/** A requested model resolved against the local opencode install (#6599). */\ninterface ResolvedOpenCodeModel {\n /** The id passed as `--model`. */\n readonly cliId: string;\n /** The id the response reports, which cost is priced by. */\n readonly reportedAs: string;\n}\n\n/** Timeout for `opencode models` probe (ms). */\nconst PROBE_TIMEOUT_MS = 10_000;\n\n/**\n * Probes available models by running `opencode models`.\n * Returns a Set of model IDs (e.g., \"opencode/big-pickle\").\n * Caches result in a module-level variable for the process lifetime.\n */\nlet cachedModels: Set<string> | undefined;\n/** Inflight probe promise for coalescing concurrent calls (Issue #1438). */\nlet probePromise: Promise<Set<string>> | undefined;\n\nfunction probeAvailableModels(): Promise<Set<string>> {\n if (cachedModels !== undefined) return Promise.resolve(cachedModels);\n if (probePromise !== undefined) return probePromise;\n\n probePromise = new Promise<Set<string>>((resolve) => {\n execFile('opencode', ['models'], { timeout: PROBE_TIMEOUT_MS }, (error, stdout) => {\n if (error !== null || stdout.trim() === '') {\n logger.debug('Failed to probe OpenCode models, will omit --model flag', {\n error: error?.message,\n });\n cachedModels = new Set();\n resolve(cachedModels);\n return;\n }\n const models = new Set(\n stdout\n .trim()\n .split('\\n')\n .map((l) => l.trim())\n .filter((l) => l.length > 0)\n );\n logger.debug('Probed OpenCode models', { count: models.size });\n cachedModels = models;\n resolve(cachedModels);\n });\n }).finally(() => {\n probePromise = undefined;\n });\n\n return probePromise;\n}\n\n/** Patterns indicating an Anthropic provider in OpenCode models list. */\nconst ANTHROPIC_MODEL_PATTERNS = ['anthropic/', 'custom/claude'];\n\n/**\n * Logs a warning if OpenCode has an Anthropic provider configured (#1429).\n * Claude Code subscription API keys must NOT be used with third-party tools.\n */\nfunction warnIfAnthropicProvider(models: Set<string>): void {\n const anthropicModels = [...models].filter((m) =>\n ANTHROPIC_MODEL_PATTERNS.some((p) => m.toLowerCase().includes(p))\n );\n if (anthropicModels.length > 0) {\n logger.warn(\n 'OpenCode has Anthropic/Claude models configured. ' +\n 'Ensure these use a SEPARATE API key from console.anthropic.com, ' +\n 'NOT a Claude Code subscription key (which is restricted to Claude Code only).',\n { detectedModels: anthropicModels }\n );\n }\n}\n\n/**\n * OpenCode CLI adapter using subprocess transport.\n * Executes: opencode run --format json \"<task>\"\n *\n * Probes available models on first use. The adapter's own default is passed\n * as --model only when available (#1402); an explicitly requested model is\n * resolved against the probe or returned as an error (#6599).\n */\n/**\n * Child environment for a read-only analysis task (#6754). opencode reads\n * `OPENCODE_PERMISSION` as JSON and merges it into the `permission` config\n * after every config file, managed preferences included (verified in the\n * opencode 1.15.x binary; the key names and `allow`/`ask`/`deny` values are\n * documented at https://opencode.ai/docs/permissions). `bash` covers command\n * execution, `edit` every file write, `webfetch` network fetch.\n */\nconst OPENCODE_READ_ONLY_ENV: Readonly<Record<string, string>> = {\n OPENCODE_PERMISSION: JSON.stringify({ bash: 'deny', edit: 'deny', webfetch: 'deny' }),\n};\n\nexport class OpenCodeCliAdapter extends SubprocessCliAdapter {\n readonly name: CliName = 'opencode';\n override readonly enforcesReadOnlyAnalysis = true;\n protected readonly parser: ICliResponseParser = new OpenCodeResponseParser();\n\n /** Enable transient-error retry for OpenCode (#1456). */\n protected override readonly transientRetry: TransientRetryConfig = { enabled: true };\n\n private readonly model: string;\n private availableModels: Set<string> | undefined;\n\n constructor(options?: BaseAdapterOptions) {\n super(options?.logger);\n this.model = options?.model ?? getCliModelName(getDefaultModelForCli('opencode'));\n }\n\n /**\n * Gets OpenCode model information from canonical registry.\n */\n getModelInfo(): ModelInfo {\n const fromRegistry = buildModelInfo('opencode', this.model);\n if (fromRegistry !== undefined) return fromRegistry;\n\n return {\n id: this.model,\n name: `OpenCode (${this.model})`,\n contextWindow: FALLBACK_CONTEXT_WINDOW,\n maxOutput: FALLBACK_MAX_OUTPUT,\n // OpenCode pricing fallback is adapter-specific (not the Claude 5/25).\n costPerMillionInput: 3.0,\n costPerMillionOutput: 15.0,\n };\n }\n\n /**\n * Initializes the adapter — probes available models.\n * Warns if Anthropic provider is configured (#1429 — API key boundaries).\n */\n override async initialize(): Promise<void> {\n this.availableModels = await probeAvailableModels();\n warnIfAnthropicProvider(this.availableModels);\n await super.initialize();\n }\n\n /** Returns true if the model is available in the OpenCode installation. */\n private isModelAvailable(cliModel: string): boolean {\n if (this.availableModels === undefined || this.availableModels.size === 0) return false;\n return this.availableModels.has(cliModel);\n }\n\n /** #3408: true if the model is in rate-limit cooldown (recent 429). Opt-in. */\n private isCooled(cliModel: string): boolean {\n return (\n isDynamicModelsEnabled() && getAvailabilityCache().isKnownUnavailable(cliModel as ModelId)\n );\n }\n\n /** Usable = offered by the OpenCode install AND not in rate-limit cooldown. */\n private isModelUsable(cliModel: string): boolean {\n return this.isModelAvailable(cliModel) && !this.isCooled(cliModel);\n }\n\n /** The first candidate id for `requested` that this install lists. */\n private matchInventory(requested: string): string | undefined {\n const available = this.availableModels;\n if (available === undefined || available.size === 0) return undefined;\n return openCodeCandidates(requested).find((c) => available.has(c));\n }\n\n /**\n * Resolves an explicitly requested model (#6599). Unlike the adapter's own\n * default, an explicit request is never silently dropped: a model that is\n * not listed, or is in rate-limit cooldown, is an error rather than a run of\n * opencode's default under the requested model's name.\n */\n private resolveRequestedModel(requested: string): Result<ResolvedOpenCodeModel, CliError> {\n if (this.availableModels === undefined || this.availableModels.size === 0) {\n // No inventory to check against (`opencode models` failed): pass the\n // registry mapping through and let opencode reject it if it must.\n const cliId = resolveOpenCodeModel(requested);\n logger.warn('OpenCode model inventory unavailable; passing requested model unverified', {\n requested,\n cliId,\n });\n return ok({ cliId, reportedAs: reportedModelId(requested, cliId) });\n }\n const cliId = this.matchInventory(requested);\n if (cliId === undefined) {\n return err(\n createCallerInputCliError(\n `OpenCode cannot run requested model \"${requested}\": \\`opencode models\\` lists none of ` +\n `${openCodeCandidates(requested).join(', ')}. Refusing to run opencode's default in its place.`,\n this.name\n )\n );\n }\n if (this.isCooled(cliId)) {\n return err(\n createCallerInputCliError(\n `OpenCode requested model \"${requested}\" (${cliId}) is in rate-limit cooldown.`,\n this.name\n )\n );\n }\n return ok({ cliId, reportedAs: reportedModelId(requested, cliId) });\n }\n\n /**\n * #6599: an explicitly requested model is resolved to an opencode id before\n * the run, and the response reports the model that ran so cost and outcome\n * attribution price it rather than the adapter default. With no requested\n * model the path is unchanged.\n */\n override async execute(\n task: CliTask,\n options?: ExecutionOptions\n ): Promise<Result<CliResponse, CliError>> {\n if (task.model === undefined) return super.execute(task, options);\n if (!this.initialized) await this.initialize();\n const resolved = this.resolveRequestedModel(task.model);\n if (!resolved.ok) return resolved;\n const { cliId, reportedAs } = resolved.value;\n const result = await super.execute({ ...task, model: cliId }, options);\n if (!result.ok) return result;\n return ok({ ...result.value, model: reportedAs });\n }\n\n /** Appends --model if the resolved model is usable (#1402, #3407, #3408). */\n private appendModelArg(args: string[], task: CliTask): void {\n if (task.model !== undefined) {\n // Explicit request: already resolved by execute(). Never dropped — an\n // id opencode does not know fails loudly in opencode itself.\n args.push('--model', this.matchInventory(task.model) ?? resolveOpenCodeModel(task.model));\n return;\n }\n const cliModel = resolveOpenCodeModel(this.model);\n\n if (this.isModelUsable(cliModel)) {\n args.push('--model', cliModel);\n } else {\n logger.debug('Model not usable (unavailable or cooled), using OpenCode default', {\n requested: cliModel,\n available: this.availableModels?.size ?? 0,\n });\n }\n }\n\n /**\n * #3408: mark a model in rate-limit cooldown when a call returns RATE_LIMITED,\n * so subsequent selections skip it until the AvailabilityCache TTL recovers.\n * Wraps the base executeTask; opt-in + fail-open (no-op when discovery is off).\n * An explicitly requested model in cooldown is refused by execute() (#6599).\n */\n override async executeTask(\n task: CliTask,\n options: ResolvedExecutionOptions\n ): Promise<Result<CliResponse, CliError>> {\n const result = await super.executeTask(task, options);\n if (isDynamicModelsEnabled() && !result.ok && result.error.code === 'RATE_LIMITED') {\n const cliModel = resolveOpenCodeModel(task.model ?? this.model);\n getAvailabilityCache().markUnavailable(cliModel as ModelId, 'rate-limited (429)');\n logger.debug('Cooldown: marked model rate-limited (#3408)', { model: cliModel });\n }\n return result;\n }\n\n /** Appends optional task flags (workDir, variant, thinking). */\n private appendTaskFlags(args: string[], task: CliTask): void {\n const workDir = task.options?.['workDir'];\n if (typeof workDir === 'string' && workDir.length > 0) {\n args.push('--dir', workDir);\n }\n const variant = task.options?.['variant'];\n if (typeof variant === 'string' && ALLOWED_VARIANTS.includes(variant)) {\n args.push('--variant', variant);\n }\n if (task.options?.['thinking'] === true) {\n args.push('--thinking');\n }\n }\n\n /**\n * Gets CLI command and arguments for execution.\n * Uses `opencode run` with JSON format for stable parsing.\n * Omits --model when the adapter default isn't available (#1402).\n */\n protected getCommand(task: CliTask): CommandConfig {\n const args: string[] = ['run', '--format', 'json'];\n this.appendModelArg(args, task);\n this.appendTaskFlags(args, task);\n\n // Honor systemPrompt by prepending to stdin content (#1886).\n // OpenCode CLI has no --system-prompt or --policy equivalent, so\n // prepend is the only option. This loses the formal system-role\n // distinction, but satisfies the contract that systemPrompt\n // influences the call (far better than silently dropping it).\n const content =\n task.systemPrompt !== undefined && task.systemPrompt !== ''\n ? `${task.systemPrompt}\\n\\n---\\n\\n${task.content}`\n : task.content;\n\n return isReadOnlyAnalysis(task)\n ? { command: 'opencode', args, stdin: content, env: OPENCODE_READ_ONLY_ENV }\n : { command: 'opencode', args, stdin: content };\n }\n\n /**\n * (#2540) Lists models the local OpenCode installation can route to.\n * Wraps the existing `probeAvailableModels()` (cached for the process\n * lifetime — see `cachedModels` at the top of this file) and reshapes\n * the result into the CliModelInfo schema. Splits `provider/model` ids\n * when present.\n */\n async listModels(): Promise<readonly CliModelInfo[]> {\n const ids = await probeAvailableModels();\n const out: CliModelInfo[] = [];\n for (const raw of ids) {\n const slash = raw.indexOf('/');\n if (slash > 0 && slash < raw.length - 1) {\n out.push({ id: raw, provider: raw.slice(0, slash) });\n } else {\n out.push({ id: raw });\n }\n }\n return out;\n }\n}\n\n/** Resets model probe cache (for testing). */\nexport function resetOpenCodeModelCache(): void {\n cachedModels = undefined;\n}\n","/**\n * nexus-agents/cli-adapters - OpenCode CLI Response Parser\n *\n * Defensive parser for OpenCode CLI JSON output.\n * Handles `opencode run --format json` NDJSON event stream.\n *\n * Real opencode v1.2.x NDJSON format (verified via E2E testing):\n * {\"type\":\"step_start\",\"sessionID\":\"ses_...\",\"part\":{\"type\":\"step-start\",...}}\n * {\"type\":\"text\",\"sessionID\":\"ses_...\",\"part\":{\"type\":\"text\",\"text\":\"Hello!\",...}}\n * {\"type\":\"step_finish\",\"sessionID\":\"ses_...\",\"part\":{\"type\":\"step-finish\",\"tokens\":{...},...}}\n *\n * (Source: Issue #1124, #1244, opencode.ai/docs/cli/)\n */\n\nimport type { ICliResponseParser, TokenUsage } from '../types.js';\nimport { asRecord, extractNumberField } from '../../utils/type-coercion.js';\nimport { createLogger } from '../../core/index.js';\n\nconst logger = createLogger({ component: 'opencode-parser' });\n\n/** Minimum raw text length for plaintext fallback (#1402). */\nconst PLAINTEXT_MIN_LENGTH = 10;\n\n/** Returns true if text looks like NDJSON (most non-empty lines start with '{' ). */\nfunction looksLikeNdjson(text: string): boolean {\n const lines = text.split('\\n').filter((l) => l.trim() !== '');\n if (lines.length === 0) return false;\n const jsonLineCount = lines.filter((l) => l.trimStart().startsWith('{')).length;\n return jsonLineCount > lines.length / 2;\n}\n\n/**\n * OpenCode CLI NDJSON event types.\n * Includes both real v1.2.x types and legacy assumed types for compatibility.\n */\nexport type OpenCodeEventType =\n // Real opencode v1.2.x event types\n | 'step_start'\n | 'text'\n | 'tool_use'\n | 'step_finish'\n | 'error'\n // Legacy assumed types (maintained for backward compatibility)\n | 'session.start'\n | 'message.start'\n | 'message.delta'\n | 'message.complete'\n | 'session.complete';\n\n/**\n * Aggregated OpenCode response from NDJSON stream.\n */\nexport interface OpenCodeCliResponse {\n readonly sessionId?: string;\n readonly content: string;\n readonly usage?: TokenUsage;\n /**\n * Concatenated error-event messages emitted during the NDJSON stream.\n * Set whenever `{\"type\":\"error\",...}` events appeared. Independent of\n * whether `content` is empty — if both are populated the model produced\n * text and then errored, callers can decide what to do.\n * #2821: previously error events were folded into `content` (making\n * failures look like successful responses to consensus voters/routers).\n */\n readonly errorMessage?: string;\n}\n\n/** Internal state from processing NDJSON lines. */\ninterface NdjsonParseState {\n readonly sessionId: string | undefined;\n readonly contentParts: string[];\n readonly errorMessages: string[];\n readonly usage: TokenUsage | undefined;\n readonly hasStepEvents: boolean;\n readonly hasAnyRecognizedEvent: boolean;\n}\n\n/**\n * Parser for OpenCode CLI JSON output.\n * Handles NDJSON event stream from `opencode run --format json`.\n *\n * Supports both real opencode v1.2.x format and legacy assumed format.\n */\nexport class OpenCodeResponseParser implements ICliResponseParser<OpenCodeCliResponse> {\n readonly name = 'opencode-parser';\n readonly supportedVersionRange = '>=1.0.0 <2.0.0';\n\n /**\n * Parses complete OpenCode CLI NDJSON stream.\n */\n parse(raw: string): OpenCodeCliResponse | null {\n const lines = raw.trim().split('\\n');\n const state = this.processAllLines(lines);\n\n const errorMessage =\n state.errorMessages.length > 0 ? state.errorMessages.join('; ') : undefined;\n\n if (state.contentParts.length === 0) {\n // #2821: error-only streams must surface as failure. Empty content +\n // errorMessage set causes extractResponse() to return null, which the\n // subprocess-adapter then classifies as EXECUTION_ERROR — instead of\n // wrapping `[OpenCode error: ...]` in ok() and feeding it to voters.\n if (errorMessage !== undefined) {\n return this.buildResponse('', state.sessionId, state.usage, errorMessage);\n }\n return this.handleEmptyContent(raw, lines.length, state);\n }\n\n return this.buildResponse(\n state.contentParts.join(''),\n state.sessionId,\n state.usage,\n errorMessage\n );\n }\n\n /** Processes all NDJSON lines and returns aggregated state. */\n private processAllLines(lines: readonly string[]): NdjsonParseState {\n let sessionId: string | undefined;\n const contentParts: string[] = [];\n const errorMessages: string[] = [];\n let usage: TokenUsage | undefined;\n let hasStepEvents = false;\n let hasAnyRecognizedEvent = false;\n\n for (let idx = 0; idx < lines.length; idx++) {\n const line = lines[idx];\n if (line === undefined || line.trim() === '') continue;\n const hadEvent = this.processLine(\n line,\n { contentParts, errorMessages },\n (id) => (sessionId = id),\n (u) => (usage = u),\n idx\n );\n if (hadEvent) hasStepEvents = true;\n if (hadEvent || this.isRecognizedLegacyEvent(line)) hasAnyRecognizedEvent = true;\n }\n\n return {\n sessionId,\n contentParts,\n errorMessages,\n usage,\n hasStepEvents,\n hasAnyRecognizedEvent,\n };\n }\n\n /** Handles the case where no text content was extracted from NDJSON. */\n private handleEmptyContent(\n raw: string,\n lineCount: number,\n state: NdjsonParseState\n ): OpenCodeCliResponse | null {\n // Tool-only responses have step_start/step_finish but no text events.\n if (state.hasStepEvents) {\n return this.buildResponse(\n '[Tool-only response — no text output]',\n state.sessionId,\n state.usage\n );\n }\n // Fallback: try parsing as plain JSON (non-streaming mode)\n logger.debug('No NDJSON content extracted, trying JSON fallback', {\n rawLength: raw.length,\n lineCount,\n hasStepEvents: state.hasStepEvents,\n hasAnyRecognizedEvent: state.hasAnyRecognizedEvent,\n });\n return this.parsePlainJson(raw, state.hasAnyRecognizedEvent);\n }\n\n /** Builds an OpenCodeCliResponse from parsed components. */\n private buildResponse(\n content: string,\n sessionId: string | undefined,\n usage: TokenUsage | undefined,\n errorMessage?: string\n ): OpenCodeCliResponse {\n return {\n content,\n ...(sessionId !== undefined && { sessionId }),\n ...(usage !== undefined && { usage }),\n ...(errorMessage !== undefined && { errorMessage }),\n };\n }\n\n /**\n * Extracts just the response text.\n */\n extractResponse(raw: string): string | null {\n const parsed = this.parse(raw);\n if (parsed === null || parsed.content === '') {\n logger.debug('extractResponse returned null', {\n rawLength: raw.length,\n snippet: raw.slice(0, 100),\n parsedNull: parsed === null,\n });\n return null;\n }\n return parsed.content;\n }\n\n /**\n * Surfaces the error message from an error-only NDJSON stream (a\n * `{\"type\":\"error\",...}` event with no text content). The subprocess adapter\n * consumes this when {@link extractResponse} returns `null`, so an upstream\n * 401 / rate-limit is classified by its message (NOT_AUTHENTICATED /\n * RATE_LIMITED with a remediation hint) instead of falling through to a\n * generic PARSE_ERROR.\n */\n extractErrorMessage(raw: string): string | null {\n return this.parse(raw)?.errorMessage ?? null;\n }\n\n /**\n * Extracts token usage from response.\n */\n extractUsage(raw: string): TokenUsage | null {\n const lines = raw.trim().split('\\n');\n\n for (const line of lines) {\n if (line.trim() === '') continue;\n try {\n const event: unknown = JSON.parse(line);\n const record = asRecord(event);\n if (record === null) continue;\n\n // Real format: step_finish with nested part.tokens\n if (record.type === 'step_finish') {\n const usage = this.extractUsageFromPart(record);\n if (usage !== null) return usage;\n }\n\n // Legacy format: session.complete or message.complete with usage field\n if (record.type === 'session.complete' || record.type === 'message.complete') {\n const usage = this.extractUsageFromRecord(record);\n if (usage !== null) return usage;\n }\n } catch {\n continue;\n }\n }\n\n return null;\n }\n\n /**\n * Extracts session ID for resumption.\n */\n extractSessionId(raw: string): string | null {\n const lines = raw.trim().split('\\n');\n\n for (const line of lines) {\n if (line.trim() === '') continue;\n const sid = this.extractSessionIdFromLine(line);\n if (sid !== null) return sid;\n }\n\n return null;\n }\n\n /** Extracts session ID from a single NDJSON line. */\n private extractSessionIdFromLine(line: string): string | null {\n try {\n const record = asRecord(JSON.parse(line) as unknown);\n if (record === null) return null;\n\n // Real format: sessionID at top level (step_start, text, step_finish)\n if (typeof record.sessionID === 'string') return record.sessionID;\n\n // Legacy format: session_id/sessionId in session events\n const sid = record.session_id ?? record.sessionId;\n if (typeof sid === 'string') return sid;\n\n return null;\n } catch {\n return null;\n }\n }\n\n /**\n * Processes a single NDJSON line.\n * Handles both real v1.2.x format and legacy assumed format.\n * Returns true if a real v1.2.x step event was processed (step_start/text/tool_use/step_finish).\n * Legacy events return false since they don't indicate tool-only responses.\n */\n private processLine(\n line: string,\n collectors: { contentParts: string[]; errorMessages: string[] },\n setSessionId: (id: string) => void,\n setUsage: (usage: TokenUsage) => void,\n lineIndex: number\n ): boolean {\n try {\n const record = asRecord(JSON.parse(line) as unknown);\n if (record === null) return false;\n\n const isReal = this.processRealEvent(record, collectors, setSessionId, setUsage);\n if (isReal) return true;\n\n this.processLegacyEvent(record, collectors.contentParts, setSessionId, setUsage);\n return false;\n } catch {\n logger.debug('Skipped malformed NDJSON line', {\n lineNumber: lineIndex + 1,\n snippet: line.slice(0, 100),\n });\n return false;\n }\n }\n\n /** Processes real opencode v1.2.x event types. Returns true if handled. */\n private processRealEvent(\n record: Record<string, unknown>,\n collectors: { contentParts: string[]; errorMessages: string[] },\n setSessionId: (id: string) => void,\n setUsage: (usage: TokenUsage) => void\n ): boolean {\n switch (record.type) {\n case 'step_start':\n case 'tool_use':\n this.handleRealSessionId(record, setSessionId);\n return true;\n case 'text':\n this.handleRealSessionId(record, setSessionId);\n this.pushRealTextContent(record, collectors.contentParts);\n return true;\n case 'step_finish':\n this.handleRealSessionId(record, setSessionId);\n this.emitRealUsage(record, setUsage);\n return true;\n case 'error':\n this.handleRealSessionId(record, setSessionId);\n this.captureErrorMessage(record, collectors.errorMessages);\n return true;\n default:\n return false;\n }\n }\n\n /**\n * Captures an error-event message for the response's `errorMessage` field\n * (#2821). Previously this pushed `[OpenCode error: ...]` into `contentParts`,\n * which made error-only streams look like successful responses to consensus\n * voters and the routing learner.\n */\n private captureErrorMessage(record: Record<string, unknown>, errorMessages: string[]): void {\n const errorObj = asRecord(record.error);\n if (errorObj === null) return;\n\n const data = asRecord(errorObj.data);\n const message =\n data !== null && typeof data.message === 'string'\n ? data.message\n : typeof errorObj.name === 'string'\n ? errorObj.name\n : 'Unknown error';\n\n logger.warn('OpenCode returned error event', { message });\n errorMessages.push(message);\n }\n\n /** Processes legacy assumed event types. */\n private processLegacyEvent(\n record: Record<string, unknown>,\n contentParts: string[],\n setSessionId: (id: string) => void,\n setUsage: (usage: TokenUsage) => void\n ): void {\n switch (record.type) {\n case 'session.start':\n this.handleLegacySessionStart(record, setSessionId);\n break;\n case 'message.delta':\n this.pushLegacyTextContent(record, contentParts);\n break;\n case 'message.complete':\n this.pushLegacyTextContent(record, contentParts);\n this.emitLegacyUsage(record, setUsage);\n break;\n case 'session.complete':\n this.emitLegacyUsage(record, setUsage);\n break;\n }\n }\n\n // --- Real v1.2.x format handlers ---\n\n /** Extracts sessionID from top-level field (real format). */\n private handleRealSessionId(\n record: Record<string, unknown>,\n setSessionId: (id: string) => void\n ): void {\n if (typeof record.sessionID === 'string') setSessionId(record.sessionID);\n }\n\n /** Extracts text from nested part.text field (real format). */\n private pushRealTextContent(record: Record<string, unknown>, parts: string[]): void {\n const part = asRecord(record.part);\n if (part === null) return;\n\n if (typeof part.text === 'string') parts.push(part.text);\n }\n\n /** Extracts usage from nested part.tokens field (real format). */\n private emitRealUsage(\n record: Record<string, unknown>,\n setUsage: (usage: TokenUsage) => void\n ): void {\n const usage = this.extractUsageFromPart(record);\n if (usage !== null) setUsage(usage);\n }\n\n /** Extracts usage from part.tokens (real opencode v1.2.x format). */\n private extractUsageFromPart(record: Record<string, unknown>): TokenUsage | null {\n const part = asRecord(record.part);\n if (part === null) return null;\n\n const tokens = asRecord(part.tokens);\n if (tokens === null) return null;\n\n const inputTokens = extractNumberField(tokens, 'input');\n const outputTokens = extractNumberField(tokens, 'output');\n\n if (inputTokens === null || outputTokens === null) return null;\n\n return {\n inputTokens,\n outputTokens,\n totalTokens: inputTokens + outputTokens,\n };\n }\n\n // --- Legacy format handlers ---\n\n /** Extracts session ID from a session event record (legacy format). */\n private handleLegacySessionStart(\n record: Record<string, unknown>,\n setSessionId: (id: string) => void\n ): void {\n const sid = record.session_id ?? record.sessionId;\n if (typeof sid === 'string') setSessionId(sid);\n }\n\n /** Pushes text content from a message event (legacy format). */\n private pushLegacyTextContent(record: Record<string, unknown>, parts: string[]): void {\n const text = record.content ?? record.delta ?? record.text;\n if (typeof text === 'string') parts.push(text);\n }\n\n /** Emits usage from a record (legacy format). */\n private emitLegacyUsage(\n record: Record<string, unknown>,\n setUsage: (usage: TokenUsage) => void\n ): void {\n const usage = this.extractUsageFromRecord(record);\n if (usage !== null) setUsage(usage);\n }\n\n /** Checks if a line contains a recognized legacy event type (without full parsing). */\n private isRecognizedLegacyEvent(line: string): boolean {\n try {\n const record = asRecord(JSON.parse(line) as unknown);\n if (record === null) return false;\n const t = record.type;\n return (\n t === 'session.start' ||\n t === 'message.start' ||\n t === 'message.delta' ||\n t === 'message.complete' ||\n t === 'session.complete'\n );\n } catch {\n return false;\n }\n }\n\n /**\n * Fallback parser for plain JSON output (non-streaming).\n * Falls back to raw plaintext if JSON parsing fails and content is substantial (#1402).\n * When hasAnyRecognizedEvent is true, NDJSON-like plaintext is rejected (it was\n * recognized NDJSON that simply had no content — not malformed output).\n */\n private parsePlainJson(raw: string, hasAnyRecognizedEvent: boolean): OpenCodeCliResponse | null {\n try {\n const data: unknown = JSON.parse(raw);\n const record = asRecord(data);\n if (record === null) return this.parsePlaintext(raw, hasAnyRecognizedEvent);\n\n // Try common response field names\n const content = record.content ?? record.result ?? record.text ?? record.output;\n if (typeof content !== 'string') return null; // Valid JSON but no recognized fields\n\n const usage = this.extractUsageFromRecord(record);\n const sid = record.session_id ?? record.sessionId;\n\n return {\n content,\n ...(typeof sid === 'string' ? { sessionId: sid } : {}),\n ...(usage !== null ? { usage } : {}),\n };\n } catch {\n return this.parsePlaintext(raw, hasAnyRecognizedEvent);\n }\n }\n\n /**\n * Last-resort plaintext fallback for non-JSON CLI output (#1402).\n * Returns raw text as content when it has substantial length.\n * Accepts NDJSON-like content only when no recognized events were found\n * (meaning it's truly unrecognized/malformed output, not valid NDJSON with no text).\n */\n private parsePlaintext(raw: string, hasAnyRecognizedEvent: boolean): OpenCodeCliResponse | null {\n const trimmed = raw.trim();\n if (trimmed.length < PLAINTEXT_MIN_LENGTH) {\n logger.debug('Plaintext fallback rejected: too short', { length: trimmed.length });\n return null;\n }\n if (looksLikeNdjson(trimmed)) {\n if (hasAnyRecognizedEvent) {\n logger.debug('Plaintext fallback rejected: recognized NDJSON with no content', {\n length: trimmed.length,\n });\n return null;\n }\n logger.debug('Plaintext fallback: accepting malformed NDJSON as text', {\n length: trimmed.length,\n });\n }\n return { content: trimmed };\n }\n\n /**\n * Extracts usage from a record with usage/token fields (legacy format).\n */\n private extractUsageFromRecord(record: Record<string, unknown>): TokenUsage | null {\n const usage = asRecord(record.usage);\n if (usage === null) return null;\n\n const inputTokens =\n extractNumberField(usage, 'input_tokens') ?? extractNumberField(usage, 'inputTokens');\n const outputTokens =\n extractNumberField(usage, 'output_tokens') ?? extractNumberField(usage, 'outputTokens');\n\n if (inputTokens === null || outputTokens === null) return null;\n\n return {\n inputTokens,\n outputTokens,\n totalTokens: inputTokens + outputTokens,\n };\n }\n}\n","/**\n * cli-auth-probe — real authentication probes for the four AI CLIs.\n *\n * The existing `adapter.healthCheck()` only confirms the CLI binary exists +\n * the version is supported. It does NOT probe whether the CLI can actually\n * authenticate. Result: doctor reports \"✓ Auth: CLI auth\" for installed-but-\n * unauthed CLIs, then downstream commands fail with unreadable errors.\n *\n * This module fixes that. Per-CLI probes use the cheapest reliable signal:\n *\n * - claude → presence + non-expired `~/.claude/.credentials.json`\n * (or env: ANTHROPIC_API_KEY)\n * - codex → `codex login status` (returns auth state)\n * (or env: OPENAI_API_KEY)\n * - gemini → presence of `agy` only, reported as `unknown` (#4391). The arm\n * runs Antigravity, which offers no non-interactive auth check\n * and does NOT use `~/.gemini/oauth_creds.json`.\n * - opencode → `opencode auth list` (parses credential count)\n *\n * No live API calls. Tokens are never decoded or sent anywhere — only their\n * presence/expiry is read locally. Where a CLI offers no signal we can read, the\n * probe reports `unknown` rather than guessing in either direction (#4391).\n *\n * Source: Issue #2447. Replaces the binary-detection-only path in doctor.ts\n * (Issue #2439 follow-up).\n */\n\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nimport { CLI_SUBPROCESS_TIMEOUTS } from '../config/timeouts.js';\nimport type { CliName } from '../cli-adapters/types.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Result of probing a single CLI's authentication state. Discriminated so\n * callers can render auth | needs-login | not-installed | error states\n * without re-checking flags.\n */\nexport type AuthProbeResult =\n | {\n readonly cli: CliName;\n readonly state: 'authenticated';\n /** How auth was satisfied — informational, not load-bearing. */\n readonly via: 'env-var' | 'cli-credentials';\n /** Optional metadata (e.g. expiry timestamp) if cheap to extract. */\n readonly meta?: Readonly<Record<string, unknown>>;\n }\n | {\n readonly cli: CliName;\n readonly state: 'needs-login';\n /** Short, human-readable reason to surface to the operator. */\n readonly reason: string;\n /** Exact command the operator should run to fix this. */\n readonly fixCommand: string;\n /** Optional canonical URL for credentials/console. */\n readonly fixUrl?: string;\n /** Env-var alternative if applicable. */\n readonly envFallback?: string;\n }\n | {\n readonly cli: CliName;\n readonly state: 'not-installed';\n readonly reason: string;\n }\n | {\n /**\n * The CLI is installed, but it exposes NO way to verify authentication\n * that we can use (#4391). Not a failure — an honest absence of evidence.\n *\n * Callers must admit these optimistically. Asserting `needs-login` here\n * excluded a working `agy` arm from routing for exactly this reason\n * (#4346), and asserting `authenticated` is how the retired gemini CLI\n * stayed selectable while failing every call (#4318). Real invocation\n * failures — the circuit breaker — do the excluding instead.\n */\n readonly cli: CliName;\n readonly state: 'unknown';\n readonly reason: string;\n }\n | {\n readonly cli: CliName;\n readonly state: 'error';\n readonly reason: string;\n };\n\nconst HOME = homedir();\n\n// ============================================================================\n// Per-CLI probes\n// ============================================================================\n\nfunction claudeNeedsLogin(reason: string): AuthProbeResult {\n return {\n cli: 'claude',\n state: 'needs-login',\n reason,\n fixCommand: 'claude /login',\n envFallback: 'ANTHROPIC_API_KEY',\n fixUrl: 'https://console.anthropic.com/account/keys',\n };\n}\n\nfunction probeClaude(): AuthProbeResult {\n if (process.env['ANTHROPIC_API_KEY'] !== undefined && process.env['ANTHROPIC_API_KEY'] !== '') {\n return { cli: 'claude', state: 'authenticated', via: 'env-var' };\n }\n const credPath = join(HOME, '.claude', '.credentials.json');\n if (!existsSync(credPath)) {\n return claudeNeedsLogin(\n 'No credentials at ~/.claude/.credentials.json and ANTHROPIC_API_KEY is not set'\n );\n }\n try {\n const parsed: unknown = JSON.parse(readFileSync(credPath, 'utf-8'));\n if (!isClaudeCredsShape(parsed)) {\n return claudeNeedsLogin(\n 'Credentials file present but not in expected shape (missing claudeAiOauth.accessToken)'\n );\n }\n const expiresAt = parsed.claudeAiOauth.expiresAt;\n if (typeof expiresAt === 'number' && expiresAt < Date.now()) {\n return claudeNeedsLogin(`OAuth token expired ${new Date(expiresAt).toISOString()}`);\n }\n return {\n cli: 'claude',\n state: 'authenticated',\n via: 'cli-credentials',\n ...(typeof expiresAt === 'number' ? { meta: { expiresAt } } : {}),\n };\n } catch (e: unknown) {\n return {\n cli: 'claude',\n state: 'error',\n reason: `Failed to read claude credentials: ${e instanceof Error ? e.message : String(e)}`,\n };\n }\n}\n\nfunction codexNeedsLogin(reason: string): AuthProbeResult {\n return {\n cli: 'codex',\n state: 'needs-login',\n reason,\n fixCommand: 'codex login',\n envFallback: 'OPENAI_API_KEY',\n fixUrl: 'https://platform.openai.com/api-keys',\n };\n}\n\nfunction classifyCodexStdout(stdout: string): AuthProbeResult {\n // Check 'not logged in' BEFORE 'logged in' — substring shadowing.\n if (/not logged/i.test(stdout) || /no.*token/i.test(stdout)) {\n return codexNeedsLogin(stdout.trim().split('\\n')[0] ?? 'Not logged in');\n }\n // Probe succeeded; treat unrecognized output as authed (defensive default).\n return { cli: 'codex', state: 'authenticated', via: 'cli-credentials' };\n}\n\nasync function probeCodex(): Promise<AuthProbeResult> {\n if (process.env['OPENAI_API_KEY'] !== undefined && process.env['OPENAI_API_KEY'] !== '') {\n return { cli: 'codex', state: 'authenticated', via: 'env-var' };\n }\n try {\n const { stdout } = await execFileAsync('codex', ['login', 'status'], {\n timeout: CLI_SUBPROCESS_TIMEOUTS.spawnMs,\n });\n return classifyCodexStdout(stdout);\n } catch (e: unknown) {\n const msg = e instanceof Error ? e.message : String(e);\n if (/ENOENT|not found/i.test(msg)) {\n return { cli: 'codex', state: 'not-installed', reason: 'codex binary not on PATH' };\n }\n return codexNeedsLogin('Not logged in (codex login status returned non-zero)');\n }\n}\n\n/**\n * Probe the gemini arm, which runs `agy` (Antigravity) since Google retired the\n * standalone gemini CLI (#4389).\n *\n * There is no usable auth signal to read, and that is a measured conclusion\n * rather than an assumption:\n *\n * - `agy models` HANGS when stdout is not a TTY — 90s and no output, confirmed\n * from both a shell pipe and `execFile` (#4393). Unusable programmatically.\n * - There is no `auth`, `login` or `whoami` subcommand.\n * - agy does not use `~/.gemini/oauth_creds.json`. Verified experimentally:\n * it served a completion while that file sat untouched with an expiry 5.5\n * hours in the past. The previous probe read exactly that file and so\n * reported `needs-login` for a working gateway.\n *\n * The only thing that proves agy works is a real completion, which costs ~17k\n * input tokens per call — far too expensive for a health check.\n *\n * So this reports `unknown`: the binary is there, and we decline to guess. Under\n * the admission policy that makes the arm selectable, and a genuinely broken\n * gateway is excluded by its own failures rather than by a check that cannot\n * see it.\n */\nasync function probeGemini(): Promise<AuthProbeResult> {\n try {\n // `--version` is local, instant, and TTY-independent — it establishes\n // presence without asserting anything about auth.\n await execFileAsync('agy', ['--version'], {\n timeout: CLI_SUBPROCESS_TIMEOUTS.spawnMs,\n maxBuffer: 64 * 1024,\n });\n return {\n cli: 'gemini',\n state: 'unknown',\n reason: 'agy exposes no non-interactive auth check; admitted unverified',\n };\n } catch (e: unknown) {\n const err = e as { code?: string | number };\n if (err.code === 'ENOENT') {\n return { cli: 'gemini', state: 'not-installed', reason: 'agy binary not found on PATH' };\n }\n // Deliberately does not echo stderr: gateway output can carry request\n // content or credential material.\n return { cli: 'gemini', state: 'error', reason: 'agy --version failed' };\n }\n}\n\nasync function probeOpencode(): Promise<AuthProbeResult> {\n // OpenCode's auth is per-provider (anthropic/openai/etc) configured in\n // ~/.config/opencode/opencode.json or via env. Probe by listing credentials.\n try {\n const { stdout } = await execFileAsync('opencode', ['auth', 'list'], {\n timeout: CLI_SUBPROCESS_TIMEOUTS.spawnMs,\n });\n if (/0 credentials/i.test(stdout)) {\n return {\n cli: 'opencode',\n state: 'needs-login',\n reason: 'No providers configured in opencode',\n fixCommand: 'opencode auth login',\n fixUrl: 'https://opencode.ai/docs/config',\n };\n }\n return { cli: 'opencode', state: 'authenticated', via: 'cli-credentials' };\n } catch (e: unknown) {\n const msg = e instanceof Error ? e.message : String(e);\n if (/ENOENT|not found/i.test(msg)) {\n return { cli: 'opencode', state: 'not-installed', reason: 'opencode binary not on PATH' };\n }\n return {\n cli: 'opencode',\n state: 'error',\n reason: msg.split('\\n')[0] ?? 'opencode auth list failed',\n };\n }\n}\n\n// ============================================================================\n// Type guards (no `any`, no untyped JSON)\n// ============================================================================\n\ninterface ClaudeCreds {\n readonly claudeAiOauth: {\n readonly accessToken: string;\n readonly expiresAt?: number;\n };\n}\n\nfunction isClaudeCredsShape(v: unknown): v is ClaudeCreds {\n if (typeof v !== 'object' || v === null) return false;\n const oauth = (v as { claudeAiOauth?: unknown }).claudeAiOauth;\n if (typeof oauth !== 'object' || oauth === null) return false;\n return typeof (oauth as { accessToken?: unknown }).accessToken === 'string';\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/** Probe a single CLI. */\nexport async function probeCli(cli: CliName): Promise<AuthProbeResult> {\n switch (cli) {\n case 'claude':\n return Promise.resolve(probeClaude());\n case 'codex':\n return probeCodex();\n case 'gemini':\n return probeGemini();\n case 'opencode':\n return probeOpencode();\n }\n}\n\n/** Probe all four CLIs in parallel. Order in returned array matches input. */\nexport async function probeAllClis(): Promise<readonly AuthProbeResult[]> {\n const clis: readonly CliName[] = ['claude', 'gemini', 'codex', 'opencode'];\n return Promise.all(clis.map((c) => probeCli(c)));\n}\n","/**\n * The router's CLI admission predicate (#6720).\n *\n * `isCliAvailable` (factory) and `doctor` both decide from it whether a CLI\n * serves its own slot, so doctor cannot report \"CLI\" for a slot the router\n * sends to the gateway. A module of its own so modules that mock the factory\n * wholesale still reach the real predicate.\n *\n * @module cli-adapters/cli-admission\n */\n\nimport type { HealthStatus } from './types.js';\nimport type { AuthProbeResult } from '../cli/cli-auth-probe.js';\n\n/** The auth probe determined the CLI cannot serve: logged out or not installed. */\nexport function cliAuthBlocks(auth: Pick<AuthProbeResult, 'state'>): boolean {\n return auth.state === 'needs-login' || auth.state === 'not-installed';\n}\n\n/**\n * Whether a CLI with this health and auth probe is available to route to.\n *\n * #4391: `unknown` auth is ADMITTED, not excluded. Some gateways expose no\n * auth signal we can read — agy has no non-interactive auth check at all, and\n * its `models` subcommand hangs without a TTY (#4393). Treating an absence of\n * evidence as a failure is what excluded a working agy arm from routing\n * (#4346); treating it as success is how the retired gemini CLI stayed\n * selectable while failing every call (#4318). We admit it and let real\n * invocation failures do the excluding, via the circuit breaker the adapters\n * now feed (#4330).\n */\nexport function isCliAdmitted(\n health: Pick<HealthStatus, 'healthy'>,\n auth: Pick<AuthProbeResult, 'state'>\n): boolean {\n return health.healthy && !cliAuthBlocks(auth);\n}\n","/**\n * nexus-agents/cli-adapters - `codex mcp-server` transport probe (#6119)\n *\n * codex-cli 0.154.0 removed the `mcp-server` subcommand: `codex --help` lists\n * only `mcp` (manage external MCP servers), and `codex mcp-server` forwards to\n * the interactive CLI, which dies with `stdin is not a terminal` under a pipe.\n * The MCP transport (`CodexMcpAdapter`) spawns exactly that, so every codex\n * voter seat errored while `doctor` still printed \"Ready (Codex mcp-server)\"\n * — a verdict inferred from the install, not measured.\n *\n * This probe measures. It runs `codex mcp-server --help` with stdin CLOSED\n * (`stdio: ['ignore', 'pipe', 'pipe']`) so a codex that forwards to the\n * interactive CLI cannot block on a terminal, and reports the subcommand\n * available only when the process exits 0 AND its stdout names `mcp-server`.\n * On 0.154.0 the forwarded `--help` exits 0 with the TOP-LEVEL help, which\n * lists `mcp` but never `mcp-server` — hence both conditions.\n *\n * The default exec's verdict is cached per process: the binary does not\n * change under a running server, and the factory is synchronous and called on\n * every adapter construction.\n *\n * @module cli-adapters/codex-mcp-server-probe\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { CLI_SUBPROCESS_TIMEOUTS } from '../config/timeouts.js';\n\n/** What the probe needs back from running a command: exit status and stdout. */\nexport interface CodexMcpServerProbeResult {\n /** Exit code, or `null` when the process could not be spawned or was killed. */\n readonly exitCode: number | null;\n readonly stdout: string;\n}\n\n/**\n * Synchronous command runner the probe is written against. Injectable so the\n * verdict can be tested against recorded help texts without a codex binary.\n */\nexport type CodexMcpServerProbeExec = (\n command: string,\n args: readonly string[]\n) => CodexMcpServerProbeResult;\n\n/** One-line cause, shared by the doctor line and the typed error. */\nexport const CODEX_MCP_SERVER_UNAVAILABLE_REASON = 'codex-cli ≥0.154 has no mcp-server subcommand';\n\n/**\n * Thrown when a caller demands the `mcp` codex transport explicitly and the\n * installed codex cannot serve it. Named so the cause is readable at the\n * throw site instead of surfacing later as a `stdin is not a terminal` exit\n * from a doomed `codex mcp-server` spawn.\n */\nexport class CodexMcpServerUnavailableError extends Error {\n constructor() {\n super(\n `Codex transport 'mcp' was requested but ${CODEX_MCP_SERVER_UNAVAILABLE_REASON}; ` +\n \"use transport 'subprocess' (codex exec) or leave the transport unset to auto-select\"\n );\n this.name = 'CodexMcpServerUnavailableError';\n }\n}\n\nconst PROBE_ARGS: readonly string[] = ['mcp-server', '--help'];\n/** Word-bounded so the sibling `mcp` subcommand in the top-level help cannot match. */\nconst SUBCOMMAND_PATTERN = /\\bmcp-server\\b/;\n\n/** Run the probe command synchronously; a spawn failure or a timeout is `exitCode: null`. */\nconst defaultExec: CodexMcpServerProbeExec = (command, args) => {\n try {\n const stdout = execFileSync(command, [...args], {\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'pipe'],\n timeout: CLI_SUBPROCESS_TIMEOUTS.statusProbeMs,\n });\n return { exitCode: 0, stdout };\n } catch (error: unknown) {\n const status = (error as { status?: unknown }).status;\n const stdout = (error as { stdout?: unknown }).stdout;\n return {\n exitCode: typeof status === 'number' ? status : null,\n stdout: typeof stdout === 'string' ? stdout : '',\n };\n }\n};\n\nlet cachedDefaultVerdict: boolean | undefined;\n\n/**\n * Whether the installed codex serves the `mcp-server` subcommand.\n *\n * With no argument the default exec runs once per process and the verdict is\n * cached. An injected exec is run on every call and never cached — the caller\n * owns it.\n */\nexport function codexMcpServerAvailable(exec?: CodexMcpServerProbeExec): boolean {\n if (exec !== undefined) return probe(exec);\n cachedDefaultVerdict ??= probe(defaultExec);\n return cachedDefaultVerdict;\n}\n\nfunction probe(exec: CodexMcpServerProbeExec): boolean {\n const { exitCode, stdout } = exec('codex', PROBE_ARGS);\n return exitCode === 0 && SUBCOMMAND_PATTERN.test(stdout);\n}\n","/**\n * nexus-agents/cli-adapters - Adapter Factory\n *\n * Factory for creating CLI adapters based on configuration.\n * Supports optional caching of CLI health check results.\n *\n * (Source: cli-project_plan.md v2.1.0)\n * (Source: Issue #90 - Codex MCP adapter)\n * (Source: Issue #165 - CLI detection cache)\n */\n\nimport type { ICliAdapter, CliName, RoutingArmId, CliTransport } from './types.js';\nimport { createLogger, getTimeProvider } from '../core/index.js';\nimport { collectApiRoutingArms } from '../adapters/auto-adapter.js';\nimport { ClaudeCliAdapter } from './adapters/claude-adapter.js';\nimport { GeminiCliAdapter } from './adapters/gemini-adapter.js';\nimport { CodexCliAdapter } from './adapters/codex-adapter.js';\nimport { CodexMcpAdapter } from './adapters/codex-mcp-adapter.js';\nimport { OpenCodeCliAdapter } from './adapters/opencode-adapter.js';\nimport type { ILogger } from '../core/index.js';\nimport type { ICliDetectionCache } from './cli-detection-cache.js';\nimport { CliDetectionCache, createCliDetectionCache } from './cli-detection-cache.js';\nimport { probeCli } from '../cli/cli-auth-probe.js';\nimport { cliAuthBlocks, isCliAdmitted } from './cli-admission.js';\nimport { getCliCircuitBreakerSnapshot } from './cli-circuit-breaker.js';\nimport { isCliDisabled } from './disabled-clis.js';\nimport { buildGatewaySlotRouterArm } from './gateway-slot-arm.js';\nimport {\n codexMcpServerAvailable,\n CodexMcpServerUnavailableError,\n CODEX_MCP_SERVER_UNAVAILABLE_REASON,\n} from './codex-mcp-server-probe.js';\n\nconst factoryLogger = createLogger({ component: 'cli-adapter-factory' });\n\n/**\n * Configuration for creating a CLI adapter.\n */\nexport interface CliAdapterConfig {\n /** Which CLI to use */\n readonly cli: CliName;\n /** Optional model override */\n readonly model?: string;\n /** Optional logger */\n readonly logger?: ILogger;\n /**\n * Transport for Codex: `'mcp'` or `'subprocess'`. Unset selects by probe\n * (#6119): `mcp` when the installed codex serves `mcp-server`, otherwise\n * `subprocess` (`codex exec`). An explicit `'mcp'` on a codex without the\n * subcommand throws {@link CodexMcpServerUnavailableError} at construction.\n */\n readonly transport?: CliTransport;\n}\n\n/**\n * Creates a CLI adapter based on configuration.\n *\n * @param config - Adapter configuration\n * @returns The configured CLI adapter\n * @throws Error if CLI name is not supported\n *\n * @example\n * ```typescript\n * const adapter = createCliAdapter({ cli: 'claude', model: 'claude-opus-4' });\n * const result = await adapter.execute({ content: 'Hello!' });\n * ```\n */\nexport function createCliAdapter(config: CliAdapterConfig): ICliAdapter {\n const options = {\n ...(config.model !== undefined && { model: config.model }),\n ...(config.logger !== undefined && { logger: config.logger }),\n };\n\n switch (config.cli) {\n case 'claude':\n return new ClaudeCliAdapter(options);\n\n case 'gemini':\n return new GeminiCliAdapter(options);\n\n case 'codex':\n return createCodexAdapter(config.transport, options);\n\n case 'opencode':\n return new OpenCodeCliAdapter(options);\n\n default: {\n const exhaustiveCheck: never = config.cli;\n throw new Error(`Unsupported CLI: ${String(exhaustiveCheck)}`);\n }\n }\n}\n\n/**\n * Creates a Codex adapter for the requested transport.\n *\n * The MCP transport was the default since #90. codex-cli 0.154 removed the\n * `mcp-server` subcommand it spawns (#6119), so an unset transport now follows\n * the probe: `mcp` only when the installed codex serves it, otherwise the\n * subprocess transport, which already carries the #6093 landlock flag and the\n * #6101 stderr capture. An explicit `'mcp'` that the probe refuses throws a\n * typed error naming the cause rather than spawning a process that dies with\n * `stdin is not a terminal`.\n *\n * @param transport - `'mcp'`, `'subprocess'`, or `undefined` to select by probe\n * @param options - Adapter options\n * @returns Codex CLI adapter\n * @throws CodexMcpServerUnavailableError when `'mcp'` is demanded but unavailable\n */\nfunction createCodexAdapter(\n transport: CliTransport | undefined,\n options: { model?: string; logger?: ILogger }\n): ICliAdapter {\n if (transport === 'subprocess') {\n return new CodexCliAdapter(options);\n }\n const mcpServerAvailable = codexMcpServerAvailable();\n if (transport === 'mcp') {\n if (!mcpServerAvailable) throw new CodexMcpServerUnavailableError();\n return new CodexMcpAdapter(options);\n }\n if (mcpServerAvailable) return new CodexMcpAdapter(options);\n (options.logger ?? factoryLogger).debug('Codex transport selected by probe: subprocess', {\n reason: CODEX_MCP_SERVER_UNAVAILABLE_REASON,\n });\n return new CodexCliAdapter(options);\n}\n\n/**\n * Creates all available routing-arm adapters.\n * Codex transport is selected by probe unless one is passed (#6119).\n *\n * The four CLI slots are registered under their slot key. In gateway mode a\n * vendor slot whose CLI is not available (`isCliAvailable`, the predicate\n * `createAutoAdapter` uses) is served by a gateway model of its family, and a\n * slot with neither a usable binary nor a family model is omitted (#6604,\n * `gateway-slot-arm.ts`). `NEXUS_DISABLED_CLIS` is transport-scoped (#6720):\n * a disabled CLI counts as \"not available\", so its family's gateway model\n * still serves the slot; without a gateway catalogue a disabled CLI's slot is\n * omitted (#6590), and every CLI disabled yields an empty map. When\n * `NEXUS_BILLING_MODE=api`, the direct-API adapters whose keys are present are\n * ALSO appended as distinct `api:<vendor>` routing arms (#3422) so the router /\n * bandit can score them separately from the CLI slots. DEFAULT (plan) mode\n * returns CLIs only — never surprise API spend. Key-presence-only and\n * deterministic; keys are never validated by calling out.\n *\n * @param logger - Optional shared logger\n * @param codexTransport - Transport for Codex; unset selects by probe\n * @returns Map of routing arm id to adapter\n */\nexport function createAllAdapters(\n logger?: ILogger,\n codexTransport?: CliTransport\n): Map<RoutingArmId, ICliAdapter> {\n const adapters = new Map<RoutingArmId, ICliAdapter>();\n const options = logger !== undefined ? { logger } : undefined;\n\n const slots: ReadonlyArray<readonly [CliName, () => ICliAdapter]> = [\n ['claude', () => new ClaudeCliAdapter(options)],\n ['gemini', () => new GeminiCliAdapter(options)],\n ['codex', () => createCodexAdapter(codexTransport, options ?? {})],\n ['opencode', () => new OpenCodeCliAdapter(options)],\n ];\n // #6720: NEXUS_DISABLED_CLIS is transport-scoped. A disabled CLI is \"CLI not\n // available\": its family's gateway model serves the slot when there is one,\n // and otherwise the slot has no arm. `create` is never called for it, so a\n // disabled codex is not even probed for its transport.\n const isAvailable = sharedSlotAvailability();\n for (const [cli, create] of slots) {\n const arm = buildGatewaySlotRouterArm(cli, create, isAvailable, logger);\n if (arm === 'unavailable') continue;\n adapters.set(cli, arm ?? create());\n }\n\n // API arms enter the router only in explicit api billing mode (#3422).\n if (process.env['NEXUS_BILLING_MODE'] === 'api') {\n for (const { armId, adapter } of collectApiRoutingArms(logger)) {\n adapters.set(armId, adapter);\n }\n }\n\n return adapters;\n}\n\n/**\n * The availability predicate the gateway-mode router arms of ONE\n * `createAllAdapters` call share (#6604): `isCliAvailable`, the predicate\n * `createAutoAdapter` uses, over one detection cache created on first use;\n * `fresh` invalidates the cached answer for that CLI first.\n */\nfunction sharedSlotAvailability(): (cli: CliName, fresh: boolean) => Promise<boolean> {\n let cache: ICliDetectionCache | undefined;\n return (cli, fresh) => {\n cache ??= createCliDetectionCache();\n // A re-check after an availability failure must probe again, not read\n // the cached \"available\" that committed the arm to the CLI.\n if (fresh) cache.invalidate(cli);\n return isCliAvailable(cli, cache);\n };\n}\n\n/**\n * Checks if a CLI is available by running a health check.\n * Uses cache if provided to avoid repeated subprocess calls.\n *\n * @param cli - CLI name to check\n * @param cache - Optional cache to use\n * @returns True if CLI is healthy\n */\nexport async function isCliAvailable(cli: CliName, cache?: ICliDetectionCache): Promise<boolean> {\n // Check cache first\n if (cache !== undefined) {\n const cached = cache.get(cli);\n if (cached !== undefined) {\n return cached.healthy;\n }\n }\n\n try {\n const adapter = createCliAdapter({ cli });\n // Pre-#2725 only ran healthCheck() — which confirms the binary exists\n // and runs but does NOT probe authentication. Result: orchestrate listed\n // opencode as \"Available\" when the user wasn't logged in, and the next\n // call failed with an opaque subprocess error. Auth must agree with the\n // probe doctor already uses (cli-auth-probe.ts, #2447).\n const [health, auth] = await Promise.all([adapter.healthCheck(), probeCli(cli)]);\n const authBlocks = cliAuthBlocks(auth);\n const available = isCliAdmitted(health, auth);\n\n // Store in cache if provided. Synthesize a degraded health record when\n // the binary is healthy but auth failed, so downstream consumers see\n // \"unavailable\" without losing the version string.\n if (cache !== undefined) {\n if (available) {\n cache.set(cli, CliDetectionCache.fromHealthStatus(health));\n } else {\n cache.set(cli, {\n healthy: false,\n version: health.version,\n versionStatus: health.versionStatus,\n checkedAt: new Date(),\n message: authBlocks\n ? `auth: ${auth.state}` + ('reason' in auth ? ` (${auth.reason})` : '')\n : health.message,\n });\n }\n }\n\n return available;\n } catch (error: unknown) {\n // Closes #2952 (medium): pre-fix the bare `catch {}` dropped the error\n // entirely — operators saw `<cli>: unavailable` with no way to tell\n // whether the binary was missing, the probe timed out, or some other\n // failure occurred. Now include the message in the cached entry.\n cacheHealthCheckFailure(cache, cli, error);\n return false;\n }\n}\n\n/** Records a health-check exception in the cache with the error message preserved. */\nfunction cacheHealthCheckFailure(\n cache: ICliDetectionCache | undefined,\n cli: CliName,\n error: unknown\n): void {\n if (cache === undefined) return;\n const message = error instanceof Error ? error.message : String(error);\n cache.set(cli, {\n healthy: false,\n version: 'unknown',\n versionStatus: 'unsupported',\n checkedAt: new Date(getTimeProvider().now()),\n message: `Health check failed: ${message}`,\n });\n}\n\n/**\n * Gets all available CLIs by running health checks.\n * Uses cache if provided to avoid repeated subprocess calls. CLIs disabled by\n * `NEXUS_DISABLED_CLIS` are excluded (#6590); disabling every CLI yields `[]`.\n *\n * @param cache - Optional cache to use\n * @returns Array of available CLI names\n */\nexport async function getAvailableClis(cache?: ICliDetectionCache): Promise<CliName[]> {\n // #6590: disabled CLIs are dropped before probing, so none spends a probe.\n const clis = (['claude', 'gemini', 'codex', 'opencode'] as const).filter(\n (cli) => !isCliDisabled(cli)\n );\n\n // Check all CLIs in parallel to avoid sequential timeout penalties\n const results = await Promise.allSettled(\n clis.map(async (cli) => ({ cli, available: await isCliAvailable(cli, cache) }))\n );\n\n return results\n .filter(\n (r): r is PromiseFulfilledResult<{ cli: CliName; available: boolean }> =>\n r.status === 'fulfilled' && r.value.available\n )\n .filter((r) => isCliServingForVoters(r.value.cli))\n .map((r) => r.value.cli);\n}\n\nfunction isCliServingForVoters(cli: CliName): boolean {\n try {\n const snapshot = getCliCircuitBreakerSnapshot(cli);\n if (snapshot?.state !== 'open') {\n return true;\n }\n factoryLogger.warn('CLI excluded from voter availability because circuit is open', {\n cli,\n circuitState: snapshot.state,\n failureCount: snapshot.failureCount,\n });\n return false;\n } catch {\n return true;\n }\n}\n"],"mappings":"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","names":["Anthropic","Anthropic","logger","logger","estimateTaskComplexity","categorizeError","logger","readFileSync","readFileSync","msg","snippet","logger","logger","z","estimateTaskComplexity","logger","logger","execFile","logger","logger","execFile","cliId","execFile","promisify","readFileSync","join","promisify","execFile","join","readFileSync","err","execFileSync","execFileSync","exec","logger","cached"]}