nexus-agents 8.76.2 → 8.78.0

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 (133) hide show
  1. package/dist/{ast-rule-runner-BXBBL5G7.js → ast-rule-runner-U5N43D4U.js} +3 -3
  2. package/dist/{child-mcp-config-3IELNFHI.js → child-mcp-config-IRZHH2TU.js} +2 -2
  3. package/dist/{chunk-UF3BNMOV.js → chunk-33HHRRKZ.js} +34 -31
  4. package/dist/chunk-33HHRRKZ.js.map +1 -0
  5. package/dist/{chunk-PE4QQAAY.js → chunk-3K7OHJLM.js} +53 -38
  6. package/dist/chunk-3K7OHJLM.js.map +1 -0
  7. package/dist/{chunk-A6QRKIGL.js → chunk-4FDESAVV.js} +3 -3
  8. package/dist/{chunk-WFHUSSKZ.js → chunk-5CIP4EWI.js} +21 -5
  9. package/dist/chunk-5CIP4EWI.js.map +1 -0
  10. package/dist/{chunk-J527NA52.js → chunk-5EXZHBAQ.js} +3 -3
  11. package/dist/{chunk-T52Y554H.js → chunk-7RCC7WGS.js} +2 -2
  12. package/dist/{chunk-BAGUYKZL.js → chunk-7ZMNE7U2.js} +34 -12
  13. package/dist/chunk-7ZMNE7U2.js.map +1 -0
  14. package/dist/{chunk-CAIJB3CO.js → chunk-ACXBBUW3.js} +72 -5
  15. package/dist/chunk-ACXBBUW3.js.map +1 -0
  16. package/dist/{chunk-NBTG2OQC.js → chunk-AE2U6NUZ.js} +2 -2
  17. package/dist/{chunk-ZVQ2Q7OM.js → chunk-AVEDKF3J.js} +2 -2
  18. package/dist/{chunk-IPI4I2UW.js → chunk-CMZPHXDH.js} +2 -2
  19. package/dist/{chunk-MSPYAOAU.js → chunk-DWQ7TG5L.js} +2 -2
  20. package/dist/{chunk-6E3DFUYA.js → chunk-EMWRUQQS.js} +5 -5
  21. package/dist/{chunk-F76Y5QXV.js → chunk-ER76ZRBU.js} +9 -9
  22. package/dist/{chunk-T6W3N3N3.js → chunk-F7R2E74V.js} +2 -2
  23. package/dist/{chunk-PJZQQYLJ.js → chunk-GHNHZSDW.js} +7 -7
  24. package/dist/{chunk-VFTP7WSH.js → chunk-J76ZM6QF.js} +2 -2
  25. package/dist/{chunk-AONC4VYB.js → chunk-LIVTFARG.js} +2 -2
  26. package/dist/{chunk-EVBLCOEN.js → chunk-MYF73MI3.js} +2 -2
  27. package/dist/{chunk-MRGBENPM.js → chunk-NEIPHVTA.js} +2 -2
  28. package/dist/{chunk-IDPA4X6J.js → chunk-NW5MCYOE.js} +3 -3
  29. package/dist/{chunk-G744HQMZ.js → chunk-OZFXXGGZ.js} +75 -12
  30. package/dist/chunk-OZFXXGGZ.js.map +1 -0
  31. package/dist/{chunk-CMG3P6OI.js → chunk-PSCY6MSD.js} +4 -4
  32. package/dist/{chunk-RC4ZQ4ZE.js → chunk-Q6JZQWQB.js} +28 -31
  33. package/dist/chunk-Q6JZQWQB.js.map +1 -0
  34. package/dist/{chunk-AWCSYKUR.js → chunk-QNRB5U7W.js} +2 -2
  35. package/dist/{chunk-FRHYZJGC.js → chunk-SKAPTWVH.js} +3 -3
  36. package/dist/{chunk-2P4HP2A6.js → chunk-T63EIQYI.js} +2 -2
  37. package/dist/{chunk-FMAJIIXZ.js → chunk-UNPDTD6N.js} +2 -2
  38. package/dist/{chunk-GOXBEH6M.js → chunk-VFFG3HIY.js} +2 -2
  39. package/dist/{chunk-MCRMY7K7.js → chunk-WABZQ477.js} +4 -4
  40. package/dist/{chunk-VKLUJZQV.js → chunk-WUN3IB3T.js} +2 -2
  41. package/dist/{chunk-MSSRXYMZ.js → chunk-XQ34WJOM.js} +2 -2
  42. package/dist/{chunk-NOSQXAW3.js → chunk-YUDJV6JF.js} +4 -4
  43. package/dist/{cli-circuit-breaker-H5CESPLC.js → cli-circuit-breaker-DJU6WTWT.js} +3 -3
  44. package/dist/cli.js +65 -51
  45. package/dist/cli.js.map +1 -1
  46. package/dist/{composite-router-DSYVXQ7C.js → composite-router-F3D2QRHI.js} +2 -2
  47. package/dist/{consensus-vote-OKA2PL74.js → consensus-vote-3ZA6SHMI.js} +16 -16
  48. package/dist/{context-retriever-C5KY23EG.js → context-retriever-J24ZMIZG.js} +8 -8
  49. package/dist/{doctor-deep-LBPZXYN4.js → doctor-deep-WWOPRJJS.js} +3 -3
  50. package/dist/{doctor-live-QAT3FLUJ.js → doctor-live-55CKYQ6V.js} +10 -10
  51. package/dist/{expert-bridge-F7V2AXUD.js → expert-bridge-5MFBN4ZW.js} +4 -4
  52. package/dist/{factory-MZTDIMCP.js → factory-3ZA63QDH.js} +9 -9
  53. package/dist/{factory-ECJNRE2W.js → factory-TXEYHBQV.js} +5 -5
  54. package/dist/{improvement-review-UEVIATKC.js → improvement-review-VC46AY77.js} +5 -5
  55. package/dist/index.d.ts +75 -3
  56. package/dist/index.js +30 -30
  57. package/dist/{init-opencode-WXEXOV6X.js → init-opencode-R5JTQZG5.js} +5 -5
  58. package/dist/issue-triage-M7XEMQZ7.js +16 -0
  59. package/dist/{pr-reviewer-helpers-VRPD3CNI.js → pr-reviewer-helpers-HRM74BLX.js} +4 -4
  60. package/dist/{registry-command-JZLWVBLG.js → registry-command-ZPKPR6WV.js} +2 -2
  61. package/dist/{repo-security-plan-IS65FSZF.js → repo-security-plan-LXKTNEGB.js} +3 -3
  62. package/dist/{research-helpers-synthesize-TLZPEOAU.js → research-helpers-synthesize-FZLLZSJM.js} +4 -4
  63. package/dist/{routing-memory-YHEH7ULH.js → routing-memory-35Z7UVYC.js} +2 -2
  64. package/dist/{session-memory-5MD3ZFJX.js → session-memory-G7VGV2SC.js} +3 -3
  65. package/dist/{setup-config-RFOBGXUH.js → setup-config-KLKXB2LM.js} +3 -3
  66. package/dist/{setup-custom-api-DKXRZY4C.js → setup-custom-api-TJMRUQFK.js} +14 -11
  67. package/dist/setup-custom-api-TJMRUQFK.js.map +1 -0
  68. package/dist/setup-data-dir-NQITBZYA.js +30 -0
  69. package/dist/{tool-memory-HCCMGPVI.js → tool-memory-65MWYDEP.js} +5 -5
  70. package/dist/{unified-registry-3TWHRO5L.js → unified-registry-2HCLBGWM.js} +10 -10
  71. package/dist/{weather-report-MGOA7OOC.js → weather-report-RMRBC6LH.js} +2 -2
  72. package/package.json +1 -1
  73. package/dist/chunk-BAGUYKZL.js.map +0 -1
  74. package/dist/chunk-CAIJB3CO.js.map +0 -1
  75. package/dist/chunk-G744HQMZ.js.map +0 -1
  76. package/dist/chunk-PE4QQAAY.js.map +0 -1
  77. package/dist/chunk-RC4ZQ4ZE.js.map +0 -1
  78. package/dist/chunk-UF3BNMOV.js.map +0 -1
  79. package/dist/chunk-WFHUSSKZ.js.map +0 -1
  80. package/dist/issue-triage-OJZ275JY.js +0 -16
  81. package/dist/setup-custom-api-DKXRZY4C.js.map +0 -1
  82. package/dist/setup-data-dir-JXSO4FDY.js +0 -30
  83. /package/dist/{ast-rule-runner-BXBBL5G7.js.map → ast-rule-runner-U5N43D4U.js.map} +0 -0
  84. /package/dist/{child-mcp-config-3IELNFHI.js.map → child-mcp-config-IRZHH2TU.js.map} +0 -0
  85. /package/dist/{chunk-A6QRKIGL.js.map → chunk-4FDESAVV.js.map} +0 -0
  86. /package/dist/{chunk-J527NA52.js.map → chunk-5EXZHBAQ.js.map} +0 -0
  87. /package/dist/{chunk-T52Y554H.js.map → chunk-7RCC7WGS.js.map} +0 -0
  88. /package/dist/{chunk-NBTG2OQC.js.map → chunk-AE2U6NUZ.js.map} +0 -0
  89. /package/dist/{chunk-ZVQ2Q7OM.js.map → chunk-AVEDKF3J.js.map} +0 -0
  90. /package/dist/{chunk-IPI4I2UW.js.map → chunk-CMZPHXDH.js.map} +0 -0
  91. /package/dist/{chunk-MSPYAOAU.js.map → chunk-DWQ7TG5L.js.map} +0 -0
  92. /package/dist/{chunk-6E3DFUYA.js.map → chunk-EMWRUQQS.js.map} +0 -0
  93. /package/dist/{chunk-F76Y5QXV.js.map → chunk-ER76ZRBU.js.map} +0 -0
  94. /package/dist/{chunk-T6W3N3N3.js.map → chunk-F7R2E74V.js.map} +0 -0
  95. /package/dist/{chunk-PJZQQYLJ.js.map → chunk-GHNHZSDW.js.map} +0 -0
  96. /package/dist/{chunk-VFTP7WSH.js.map → chunk-J76ZM6QF.js.map} +0 -0
  97. /package/dist/{chunk-AONC4VYB.js.map → chunk-LIVTFARG.js.map} +0 -0
  98. /package/dist/{chunk-EVBLCOEN.js.map → chunk-MYF73MI3.js.map} +0 -0
  99. /package/dist/{chunk-MRGBENPM.js.map → chunk-NEIPHVTA.js.map} +0 -0
  100. /package/dist/{chunk-IDPA4X6J.js.map → chunk-NW5MCYOE.js.map} +0 -0
  101. /package/dist/{chunk-CMG3P6OI.js.map → chunk-PSCY6MSD.js.map} +0 -0
  102. /package/dist/{chunk-AWCSYKUR.js.map → chunk-QNRB5U7W.js.map} +0 -0
  103. /package/dist/{chunk-FRHYZJGC.js.map → chunk-SKAPTWVH.js.map} +0 -0
  104. /package/dist/{chunk-2P4HP2A6.js.map → chunk-T63EIQYI.js.map} +0 -0
  105. /package/dist/{chunk-FMAJIIXZ.js.map → chunk-UNPDTD6N.js.map} +0 -0
  106. /package/dist/{chunk-GOXBEH6M.js.map → chunk-VFFG3HIY.js.map} +0 -0
  107. /package/dist/{chunk-MCRMY7K7.js.map → chunk-WABZQ477.js.map} +0 -0
  108. /package/dist/{chunk-VKLUJZQV.js.map → chunk-WUN3IB3T.js.map} +0 -0
  109. /package/dist/{chunk-MSSRXYMZ.js.map → chunk-XQ34WJOM.js.map} +0 -0
  110. /package/dist/{chunk-NOSQXAW3.js.map → chunk-YUDJV6JF.js.map} +0 -0
  111. /package/dist/{cli-circuit-breaker-H5CESPLC.js.map → cli-circuit-breaker-DJU6WTWT.js.map} +0 -0
  112. /package/dist/{composite-router-DSYVXQ7C.js.map → composite-router-F3D2QRHI.js.map} +0 -0
  113. /package/dist/{consensus-vote-OKA2PL74.js.map → consensus-vote-3ZA6SHMI.js.map} +0 -0
  114. /package/dist/{context-retriever-C5KY23EG.js.map → context-retriever-J24ZMIZG.js.map} +0 -0
  115. /package/dist/{doctor-deep-LBPZXYN4.js.map → doctor-deep-WWOPRJJS.js.map} +0 -0
  116. /package/dist/{doctor-live-QAT3FLUJ.js.map → doctor-live-55CKYQ6V.js.map} +0 -0
  117. /package/dist/{expert-bridge-F7V2AXUD.js.map → expert-bridge-5MFBN4ZW.js.map} +0 -0
  118. /package/dist/{factory-ECJNRE2W.js.map → factory-3ZA63QDH.js.map} +0 -0
  119. /package/dist/{factory-MZTDIMCP.js.map → factory-TXEYHBQV.js.map} +0 -0
  120. /package/dist/{improvement-review-UEVIATKC.js.map → improvement-review-VC46AY77.js.map} +0 -0
  121. /package/dist/{init-opencode-WXEXOV6X.js.map → init-opencode-R5JTQZG5.js.map} +0 -0
  122. /package/dist/{issue-triage-OJZ275JY.js.map → issue-triage-M7XEMQZ7.js.map} +0 -0
  123. /package/dist/{pr-reviewer-helpers-VRPD3CNI.js.map → pr-reviewer-helpers-HRM74BLX.js.map} +0 -0
  124. /package/dist/{registry-command-JZLWVBLG.js.map → registry-command-ZPKPR6WV.js.map} +0 -0
  125. /package/dist/{repo-security-plan-IS65FSZF.js.map → repo-security-plan-LXKTNEGB.js.map} +0 -0
  126. /package/dist/{research-helpers-synthesize-TLZPEOAU.js.map → research-helpers-synthesize-FZLLZSJM.js.map} +0 -0
  127. /package/dist/{routing-memory-YHEH7ULH.js.map → routing-memory-35Z7UVYC.js.map} +0 -0
  128. /package/dist/{session-memory-5MD3ZFJX.js.map → session-memory-G7VGV2SC.js.map} +0 -0
  129. /package/dist/{setup-config-RFOBGXUH.js.map → setup-config-KLKXB2LM.js.map} +0 -0
  130. /package/dist/{setup-data-dir-JXSO4FDY.js.map → setup-data-dir-NQITBZYA.js.map} +0 -0
  131. /package/dist/{tool-memory-HCCMGPVI.js.map → tool-memory-65MWYDEP.js.map} +0 -0
  132. /package/dist/{unified-registry-3TWHRO5L.js.map → unified-registry-2HCLBGWM.js.map} +0 -0
  133. /package/dist/{weather-report-MGOA7OOC.js.map → weather-report-RMRBC6LH.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/sdk/gateway-env.ts","../src/adapters/sdk/custom-api-validation.ts"],"sourcesContent":["/**\n * Env resolver for the single-model `custom-openai` gateway path (#4392\n * increment 3).\n *\n * Two mechanisms read an OpenAI-compatible gateway from the environment:\n *\n * A. the single-model SDK path — `SdkAdapter({ providerId: 'custom-openai' })`\n * via `auto-adapter.ts`, which mints the `api:custom-openai` routing arm\n * under `NEXUS_BILLING_MODE=api`;\n * B. the discovery path — `openai-compat-adapter.ts`, which lists the\n * gateway's models, serves voters in-process and registers ONE\n * `api:<endpoint>` arm.\n *\n * Both take a base URL and a key. Mechanism A historically read\n * `NEXUS_CUSTOM_API_BASE_URL` / `NEXUS_CUSTOM_API_KEY`; mechanism B reads\n * `NEXUS_OPENAI_COMPAT_URL` / `NEXUS_OPENAI_COMPAT_KEY`. This module makes the\n * old pair a deprecated ALIAS of the new pair for mechanism A only — a pure\n * rename shim, resolution `new ?? old` per variable (panel\n * vote-1789558437481-yup142p runoff, option C). Mechanism B is deliberately\n * NOT fed from the alias: renaming to the new names is what opts an operator\n * into the gateway path, and the deprecation warn says so. The aliases are\n * dropped in the next major (#6291).\n *\n * @module adapters/sdk/gateway-env\n */\n\nimport type { ILogger } from '../../core/index.js';\nimport { createLogger } from '../../core/index.js';\nimport {\n DEPRECATED_GATEWAY_ENV_ALIASES,\n OPENAI_COMPAT_KEY_ENV,\n OPENAI_COMPAT_URL_ENV,\n} from './types.js';\n\n/** One deprecated gateway env name found set in the environment. */\nexport interface DeprecatedGatewayEnvUse {\n readonly name: string;\n readonly replacement: string;\n /** True when the replacement is also set, so this alias is ignored. */\n readonly shadowed: boolean;\n}\n\n/** The resolved mechanism-A gateway pair plus every deprecated name in use. */\nexport interface GatewayEnv {\n /** `NEXUS_OPENAI_COMPAT_URL ?? NEXUS_CUSTOM_API_BASE_URL`, trimmed; empty is unset. */\n readonly baseUrl: string | undefined;\n /** `NEXUS_OPENAI_COMPAT_KEY ?? NEXUS_CUSTOM_API_KEY`, trimmed; empty is unset. */\n readonly apiKey: string | undefined;\n /** Deprecated names that are set, in table order — empty when none is. */\n readonly deprecated: readonly DeprecatedGatewayEnvUse[];\n}\n\n/** A set env value, trimmed; `undefined` for unset, empty, or whitespace-only. */\nfunction readTrimmed(env: NodeJS.ProcessEnv, name: string): string | undefined {\n const value = env[name]?.trim();\n return value === undefined || value === '' ? undefined : value;\n}\n\n/**\n * Resolve the mechanism-A gateway pair from `env` (default `process.env`).\n * Pure: no logging, no once-guard. `readGatewayEnv` is the production entry;\n * this one serves the report surfaces (env-schema, doctor) that must not warn.\n */\nexport function resolveGatewayEnv(env: NodeJS.ProcessEnv = process.env): GatewayEnv {\n const deprecated: DeprecatedGatewayEnvUse[] = [];\n for (const alias of DEPRECATED_GATEWAY_ENV_ALIASES) {\n if (readTrimmed(env, alias.deprecated) === undefined) continue;\n deprecated.push({\n name: alias.deprecated,\n replacement: alias.replacement,\n shadowed: readTrimmed(env, alias.replacement) !== undefined,\n });\n }\n return {\n baseUrl: readWithAlias(env, OPENAI_COMPAT_URL_ENV),\n apiKey: readWithAlias(env, OPENAI_COMPAT_KEY_ENV),\n deprecated,\n };\n}\n\n/** `new ?? old` for one variable, the old spelling taken from the alias table. */\nfunction readWithAlias(env: NodeJS.ProcessEnv, replacement: string): string | undefined {\n const current = readTrimmed(env, replacement);\n if (current !== undefined) return current;\n const alias = DEPRECATED_GATEWAY_ENV_ALIASES.find((a) => a.replacement === replacement);\n return alias === undefined ? undefined : readTrimmed(env, alias.deprecated);\n}\n\nconst defaultLogger = createLogger({ component: 'gateway-env' });\n\n/**\n * Once per process: a deprecated name is set, so the operator hears about it\n * exactly once, from whichever reader gets there first (or from server\n * startup). Names only — never a value.\n */\nlet deprecationWarned = false;\n\n/** Test-only: re-arm the once-guard. */\nexport function _resetDeprecatedGatewayEnvWarning(): void {\n deprecationWarned = false;\n}\n\n/**\n * Emit the one deprecation warn if any deprecated gateway name is set and it\n * has not been emitted yet in this process. Safe to call from every reader;\n * the gateway bootstrap (`cli-server-gateway.ts` `tryWireGatewayAdapters`)\n * calls it at server startup so the line appears even when no reader of the\n * alias is reached (plan billing with a CLI available never consults\n * mechanism A).\n */\nexport function warnDeprecatedGatewayEnvOnce(\n env: NodeJS.ProcessEnv = process.env,\n logger: ILogger = defaultLogger\n): void {\n if (deprecationWarned) return;\n const { deprecated } = resolveGatewayEnv(env);\n if (deprecated.length === 0) return;\n deprecationWarned = true;\n logger.warn(formatDeprecationWarning(deprecated));\n}\n\n/**\n * The warn line. Each name says whether it is honoured or ignored (shadowed\n * by its replacement), then the option-C consequence: these names configure\n * only the single-model `custom-openai` path, and the rename is the opt-in\n * to the gateway path. No value is ever interpolated — a URL can carry\n * userinfo and the key is the key.\n */\nfunction formatDeprecationWarning(deprecated: readonly DeprecatedGatewayEnvUse[]): string {\n const uses = deprecated\n .map((d) =>\n d.shadowed\n ? `${d.name} (ignored: ${d.replacement} is set)`\n : `${d.name} (honoured; rename to ${d.replacement})`\n )\n .join('; ');\n return (\n `Deprecated gateway environment variable(s) set (#4392): ${uses}. ` +\n 'These names configure only the single-model custom-openai path and stay aliases ' +\n 'until the next major (#6291). Rename to NEXUS_OPENAI_COMPAT_* to opt into the ' +\n 'gateway path (model discovery, in-process voter transport, api:<endpoint> arm).'\n );\n}\n\n/**\n * The production reader for mechanism A: resolve the pair and emit the\n * once-per-process deprecation warn when a deprecated name is set.\n */\nexport function readGatewayEnv(\n env: NodeJS.ProcessEnv = process.env,\n logger: ILogger = defaultLogger\n): GatewayEnv {\n const resolved = resolveGatewayEnv(env);\n if (resolved.deprecated.length > 0) warnDeprecatedGatewayEnvOnce(env, logger);\n return resolved;\n}\n\n/**\n * The host of a gateway base URL, for log lines and error messages. A base\n * URL can carry userinfo (`https://user:key@host/v1`), so the full string\n * must never reach a log. Two failure shapes, both replaced rather than\n * echoed: a string that does not parse at all, and a scheme-less paste such\n * as `key@host/v1`, which WHATWG parses with `key:` as the SCHEME and an\n * empty hostname (measured: `new URL('u:pw@host/v1').hostname === ''`).\n */\nexport function hostnameOf(baseUrl: string): string {\n try {\n const host = new URL(baseUrl).hostname;\n return host === '' ? '<no host>' : host;\n } catch {\n return '<unparseable url>';\n }\n}\n\n/**\n * Every exact occurrence of `apiKey` in `message` replaced with `<redacted>`.\n * The pattern-based sanitizer only knows vendor key shapes; a gateway key can\n * be any string, and a 401 body may echo the one it rejected. An empty key\n * matches nothing (`replaceAll('')` would interleave the marker).\n */\nexport function redactApiKey(message: string, apiKey: string | undefined): string {\n return apiKey === undefined || apiKey === '' ? message : message.replaceAll(apiKey, '<redacted>');\n}\n","/**\n * Validation for the `custom-openai` SDK adapter's gateway URL.\n *\n * The base URL is user-provided, so without validation the adapter becomes\n * an SSRF vector: a malicious prompt that reshaped env vars, or a typo in\n * the config, could point nexus-agents at `http://169.254.169.254/` (AWS\n * metadata) or `http://localhost:5432/` (internal services). This module\n * rejects such URLs unless the user explicitly opts in via\n * `NEXUS_CUSTOM_API_ALLOW_PRIVATE=1`.\n *\n * Called out in the #2119 consensus vote by the Security Engineer role.\n *\n * @module adapters/sdk/custom-api-validation\n */\n\nimport { isIPv4, isIPv6 } from 'node:net';\nimport { lookup as dnsLookup } from 'node:dns/promises';\nimport { ConfigError, ok, err, createLogger, type Result } from '../../core/index.js';\nimport { CUSTOM_API_ALLOW_PRIVATE_ENV } from './types.js';\n\nconst logger = createLogger({ module: 'custom-api-validation' });\n\n/**\n * Why a given URL was rejected. Machine-readable so error messages can\n * distinguish root causes in downstream tooling.\n */\nexport type BaseUrlRejectionReason =\n | 'empty'\n | 'not_a_url'\n | 'not_http_https'\n | 'loopback'\n | 'link_local'\n | 'private_range'\n | 'reserved';\n\ninterface RejectionDetail {\n readonly reason: BaseUrlRejectionReason;\n readonly message: string;\n}\n\n/**\n * Validates a user-provided custom-gateway base URL. Returns the URL as an\n * `ok` Result if it passes, or a `ConfigError` with a machine-readable\n * reason if it fails.\n *\n * Pass `{ allowPrivate: true }` (or set the env var) to bypass the SSRF\n * checks — use this only when the gateway is on a trusted internal host\n * and you accept the risk.\n */\nexport function validateCustomApiBaseUrl(\n raw: string | undefined,\n opts: { readonly allowPrivate?: boolean } = {}\n): Result<URL, ConfigError> {\n if (raw === undefined || raw.trim() === '') {\n return err(\n new ConfigError(\n 'Custom API base URL is required but missing. Set NEXUS_OPENAI_COMPAT_URL or pass `baseUrl` in config.'\n )\n );\n }\n\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n // Not echoed: these messages reach the resilient-adapter warn line, and\n // the raw string can carry userinfo (#4392 inc 3).\n return err(new ConfigError('Custom API base URL is not a valid URL'));\n }\n\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n return err(\n new ConfigError(`Custom API base URL must use http or https, got \"${url.protocol}\"`)\n );\n }\n\n const allowPrivate = opts.allowPrivate === true || resolveAllowPrivateFromEnv();\n if (!allowPrivate) {\n const rejection = classifyPrivateHost(url.hostname);\n if (rejection !== null) {\n return err(\n new ConfigError(\n `Custom API base URL rejected (SSRF guard, reason=\"${rejection.reason}\"): ${rejection.message}. ` +\n `Set ${CUSTOM_API_ALLOW_PRIVATE_ENV}=1 to bypass if the gateway runs on a trusted internal host.`\n )\n );\n }\n }\n\n return ok(url);\n}\n\nfunction resolveAllowPrivateFromEnv(): boolean {\n // Deliberately strict (case-sensitive, not parseBoolEnv): this disables an\n // SSRF guard, so we don't want extra case variants (e.g. `TRUE`) to loosen\n // the control. Fail-closed on anything but exact `1`/`true` (#3297).\n const v = process.env[CUSTOM_API_ALLOW_PRIVATE_ENV];\n return v === '1' || v === 'true';\n}\n\n/**\n * Returns a rejection reason if the hostname resolves (by string form) to\n * a loopback, link-local, or RFC 1918 private address; `null` otherwise.\n *\n * Note: this is a string-level check. It does NOT perform DNS resolution,\n * so a public DNS name that secretly resolves to a private IP will pass.\n * For that level of defense, callers should additionally run\n * {@link assertCustomApiHostResolvesPublic}, which performs a DNS lookup\n * and classifies each resolved address with the same range tables below.\n */\nfunction classifyPrivateHost(hostname: string): RejectionDetail | null {\n const normalized = normalizeHost(hostname);\n\n // Literal IP addresses — classify directly against the range tables.\n const literal = classifyIpAddress(normalized);\n if (literal !== 'not_an_ip') {\n return literal;\n }\n\n // Hostname literals that resolve to loopback without needing DNS\n if (\n normalized === 'localhost' ||\n normalized.endsWith('.localhost') ||\n normalized.endsWith('.local') // mDNS\n ) {\n return {\n reason: 'loopback',\n message: `hostname \"${hostname}\" resolves to loopback/mDNS`,\n };\n }\n\n return null;\n}\n\n/**\n * Strips IPv6 bracket wrapping (URL.hostname yields \"[::1]\") and lowercases,\n * giving the bare form the `node:net` checks expect. Shared by the sync and\n * async guards so the normalization rule lives in one place.\n */\nfunction normalizeHost(hostname: string): string {\n const stripped =\n hostname.startsWith('[') && hostname.endsWith(']') ? hostname.slice(1, -1) : hostname;\n return stripped.toLowerCase();\n}\n\n/**\n * Classifies a single (already normalized, bracket-stripped) address string.\n * Returns:\n * - a {@link RejectionDetail} if it is a private/loopback/link-local/reserved IP,\n * - `null` if it is a public IP literal (safe),\n * - the sentinel `'not_an_ip'` if the string is not an IP literal at all.\n *\n * This is the shared per-address classifier reused by both the sync\n * string-level guard ({@link classifyPrivateHost}) and the async\n * DNS-resolve-time guard ({@link assertCustomApiHostResolvesPublic}), so the\n * IPv4/IPv6 range tables live in exactly one place.\n */\nfunction classifyIpAddress(normalized: string): RejectionDetail | null | 'not_an_ip' {\n if (isIPv4(normalized)) {\n return classifyIPv4(normalized);\n }\n if (isIPv6(normalized)) {\n return classifyIPv6(normalized);\n }\n return 'not_an_ip';\n}\n\n/** Verdict for a host that is already an IP literal — no DNS required. */\nfunction classifyLiteral(hostname: string, normalized: string): Result<void, ConfigError> {\n const literal = classifyIpAddress(normalized);\n if (literal === null || literal === 'not_an_ip') return ok(undefined);\n return err(\n new ConfigError(\n `Custom API host '${hostname}' is a ${literal.reason} address: ${literal.message}`\n )\n );\n}\n\n/**\n * DNS-resolve-time SSRF guard for the custom-openai gateway.\n *\n * The sync {@link validateCustomApiBaseUrl} only inspects the hostname as a\n * string, so a PUBLIC DNS name that resolves to a PRIVATE/loopback/link-local\n * IP (e.g. an attacker-controlled `gateway.evil.test` pointing at\n * `169.254.169.254`) slips past it. This function closes that gap: it resolves\n * the hostname and runs the EXISTING IP classification against every returned\n * address, failing closed if ANY of them is private.\n *\n * Behaviour:\n * - `allowPrivate` (opt or env) → bypass, returns `ok` (matches the sync guard).\n * - hostname is already an IP literal → no DNS needed; the sync guard already\n * classified literals at construction, so return `ok`.\n * - otherwise `dns.lookup(hostname, { all: true })` and classify each address.\n * If ANY resolves private → `err(ConfigError)`.\n * - on a `dns.lookup` THROW (transient/NXDOMAIN/network error) → log debug and\n * return `ok`. Failing closed here is too aggressive: it would break legit\n * gateways on a flaky resolver, and this is additive defense-in-depth layered\n * on top of the sync string guard that already ran at construction. We only\n * REJECT on a SUCCESSFUL resolution to a private address.\n *\n * Residual risk (TOCTOU): this is RESOLVE-time, not socket-connect-time. A name\n * that passes here could rebind to a private IP before the actual connect\n * (DNS rebinding). A full fix needs a custom `fetch` `lookup` hook validating\n * the peer address at the socket layer — out of scope for this LOW\n * defense-in-depth pass (#3426).\n */\nexport async function assertCustomApiHostResolvesPublic(\n hostname: string,\n opts: { readonly allowPrivate?: boolean } = {}\n): Promise<Result<void, ConfigError>> {\n const allowPrivate = opts.allowPrivate === true || resolveAllowPrivateFromEnv();\n if (allowPrivate) {\n return ok(undefined);\n }\n\n const normalized = normalizeHost(hostname);\n\n // A literal needs no DNS, but it is still classified HERE rather than\n // assumed handled elsewhere. This used to return ok for any literal on the\n // grounds that the sync guard had classified it at construction — but\n // `discoverModels` calls only this guard, never the sync one, so on that\n // path each deferred to the other and neither ran. `http://169.254.169.254/`\n // reached IMDS, from a base URL that can come from a file rather than from\n // operator intent.\n if (isIPv4(normalized) || isIPv6(normalized)) {\n return classifyLiteral(hostname, normalized);\n }\n\n // Only `address` is read; the resolver's `family` is intentionally NOT\n // trusted — `classifyIpAddress` re-derives v4/v6 from the address string, so\n // a mislabeled family in the lookup result cannot let a private IP slip past.\n let addresses: ReadonlyArray<{ readonly address: string }>;\n try {\n addresses = await dnsLookup(hostname, { all: true });\n } catch (error) {\n // Fail OPEN: transient/NXDOMAIN/network errors must not break legit\n // gateways. The sync string guard already ran; this is additive only.\n logger.debug('custom-api SSRF resolve check: DNS lookup failed, skipping (fail-open)', {\n hostname,\n error: error instanceof Error ? error.message : String(error),\n });\n return ok(undefined);\n }\n\n const rejection = firstPrivateAddress(addresses);\n if (rejection !== null) {\n return err(\n new ConfigError(\n `Custom API base URL rejected (SSRF resolve guard, reason=\"${rejection.reason}\"): ` +\n `hostname \"${hostname}\" resolved to ${rejection.message}. ` +\n `Set ${CUSTOM_API_ALLOW_PRIVATE_ENV}=1 to bypass if the gateway runs on a trusted internal host.`\n )\n );\n }\n\n return ok(undefined);\n}\n\n/**\n * Returns the {@link RejectionDetail} for the first resolved address that\n * classifies as private/loopback/link-local/reserved, or `null` if every\n * address is public. Fail-closed: any single private hit trips the guard.\n */\nfunction firstPrivateAddress(\n addresses: ReadonlyArray<{ readonly address: string }>\n): RejectionDetail | null {\n for (const { address } of addresses) {\n const rejection = classifyIpAddress(address.toLowerCase());\n if (rejection !== null && rejection !== 'not_an_ip') {\n return rejection;\n }\n }\n return null;\n}\n\n/**\n * IPv4 ranges the SSRF guard rejects, in order of specificity.\n *\n * \"Not RFC1918\" is not the same as \"public\". The blocks below RFC1918 are\n * here because each is reachable from a host and serves something: 100.64/10\n * carries Alibaba Cloud's metadata endpoint, 192.0.0/24 carries a legacy\n * Oracle Cloud one, and multicast/reserved/broadcast reach the local segment\n * rather than the internet (second pass over #4884).\n */\nconst IPV4_RULES: ReadonlyArray<{\n readonly match: (a: number, b: number, c: number) => boolean;\n readonly reason: BaseUrlRejectionReason;\n readonly label: string;\n}> = [\n { match: (a) => a === 127, reason: 'loopback', label: 'IPv4 loopback' },\n { match: (a) => a === 10, reason: 'private_range', label: 'IPv4 private (10/8)' },\n {\n match: (a, b) => a === 172 && b >= 16 && b <= 31,\n reason: 'private_range',\n label: 'IPv4 private (172.16/12)',\n },\n {\n match: (a, b) => a === 192 && b === 168,\n reason: 'private_range',\n label: 'IPv4 private (192.168/16)',\n },\n {\n match: (a, b) => a === 169 && b === 254,\n reason: 'link_local',\n label: 'IPv4 link-local (169.254/16 — AWS IMDS)',\n },\n { match: (a) => a === 0, reason: 'reserved', label: 'IPv4 reserved (0/8)' },\n {\n match: (a, b) => a === 100 && b >= 64 && b <= 127,\n reason: 'private_range',\n label: 'IPv4 shared address space (100.64/10 — CGNAT, Alibaba metadata)',\n },\n {\n match: (a, b, c) => a === 192 && b === 0 && c === 0,\n reason: 'reserved',\n label: 'IPv4 IETF protocol assignments (192.0.0/24 — legacy Oracle metadata)',\n },\n {\n match: (a, b) => a === 198 && (b === 18 || b === 19),\n reason: 'reserved',\n label: 'IPv4 benchmarking (198.18/15)',\n },\n { match: (a) => a >= 224 && a <= 239, reason: 'reserved', label: 'IPv4 multicast (224/4)' },\n {\n match: (a) => a >= 240,\n reason: 'reserved',\n label: 'IPv4 reserved (240/4, includes 255.255.255.255 broadcast)',\n },\n];\n\nfunction classifyIPv4(ip: string): RejectionDetail | null {\n const parts = ip.split('.').map((p) => Number.parseInt(p, 10));\n if (parts.length !== 4 || parts.some((n) => Number.isNaN(n))) return null;\n const [a, b, c] = parts as [number, number, number, number];\n for (const rule of IPV4_RULES) {\n if (rule.match(a, b, c)) {\n return { reason: rule.reason, message: `${rule.label} (${ip})` };\n }\n }\n return null;\n}\n\n/**\n * Expand an IPv6 literal to its eight 16-bit groups, or null if unparseable.\n *\n * A trailing dotted quad (`::ffff:1.2.3.4`) is folded into two groups first,\n * so callers see one representation regardless of spelling.\n */\nfunction foldTrailingQuad(s: string): string | null {\n const dotted = /(?:^|:)((?:\\d{1,3}\\.){3}\\d{1,3})$/.exec(s);\n if (dotted?.[1] === undefined) return s;\n const q = dotted[1].split('.').map((n) => Number.parseInt(n, 10));\n if (q.length !== 4 || q.some((n) => Number.isNaN(n) || n < 0 || n > 255)) return null;\n const hi = (((q[0] ?? 0) << 8) | (q[1] ?? 0)).toString(16);\n const lo = (((q[2] ?? 0) << 8) | (q[3] ?? 0)).toString(16);\n return `${s.slice(0, s.length - dotted[1].length)}${hi}:${lo}`;\n}\n\nfunction toHextets(ip: string): number[] | null {\n const s = foldTrailingQuad(ip.toLowerCase());\n if (s === null) return null;\n const halves = s.split('::');\n if (halves.length > 2) return null;\n const head = halves[0] === '' || halves[0] === undefined ? [] : halves[0].split(':');\n const tail = halves[1] === '' || halves[1] === undefined ? [] : halves[1].split(':');\n const groups =\n halves.length === 1\n ? head\n : [...head, ...Array<string>(8 - head.length - tail.length).fill('0'), ...tail];\n if (groups.length !== 8) return null;\n const out = groups.map((g) => Number.parseInt(g === '' ? '0' : g, 16));\n return out.some((n) => Number.isNaN(n) || n < 0 || n > 0xffff) ? null : out;\n}\n\n/**\n * The IPv4 address embedded in an IPv6 literal, in dotted form, or null.\n *\n * Covers every prefix that carries a routable IPv4 payload: IPv4-mapped\n * (`::ffff:0:0/96`), IPv4-translated (`::ffff:0:0:0/96`), the NAT64\n * well-known prefix (`64:ff9b::/96`), 6to4 (`2002::/16`), and the deprecated\n * IPv4-compatible form (`::a.b.c.d`). Each is the same destination under a\n * different spelling, and the IPv4 rules must apply to all of them.\n */\nfunction embeddedIPv4(g: readonly number[]): string | null {\n // 6to4 is the odd one out: the payload sits in hextets 1-2, not 6-7, so it\n // cannot share the trailing-quad extraction below.\n if (g[0] === 0x2002) return hextetPairToDotted(g[1] ?? 0, g[2] ?? 0);\n const top = g.slice(0, 6);\n const prefixes: ReadonlyArray<readonly number[]> = [\n [0, 0, 0, 0, 0, 0xffff], // ::ffff:0:0/96 — IPv4-mapped\n [0, 0, 0, 0, 0xffff, 0], // ::ffff:0:0:0/96 — IPv4-translated\n [0x64, 0xff9b, 0, 0, 0, 0], // 64:ff9b::/96 — NAT64 well-known\n [0, 0, 0, 0, 0, 0], // ::a.b.c.d — deprecated IPv4-compatible\n ];\n const matched = prefixes.some((p) => p.every((v, i) => top[i] === v));\n if (!matched) return null;\n const g6 = g[6] ?? 0;\n const g7 = g[7] ?? 0;\n // `::` and `::1` are their own addresses, not embedded IPv4.\n if (top.every((v) => v === 0) && g6 === 0 && g7 <= 1) return null;\n return hextetPairToDotted(g6, g7);\n}\n\n/** The dotted IPv4 address carried by two consecutive 16-bit hextets. */\nfunction hextetPairToDotted(hi: number, lo: number): string {\n return [(hi >> 8) & 255, hi & 255, (lo >> 8) & 255, lo & 255].join('.');\n}\n\n/**\n * Classify an IPv6 address by its first hextet, or null if it is not in a\n * non-global prefix.\n *\n * Numeric rather than a string prefix test. The previous `startsWith('fe80:')`\n * covered 1/64th of fe80::/10 — `febf::1` is equally link-local and was\n * allowed — and site-local fec0::/10 had no check at all (second pass over #4884).\n */\nfunction classifyIPv6Prefix(first: number, ip: string): RejectionDetail | null {\n if (first >= 0xfc00 && first <= 0xfdff) {\n return { reason: 'private_range', message: `IPv6 unique-local (${ip}, fc00::/7)` };\n }\n if (first >= 0xfe80 && first <= 0xfebf) {\n return { reason: 'link_local', message: `IPv6 link-local (${ip}, fe80::/10)` };\n }\n if (first >= 0xfec0 && first <= 0xfeff) {\n return { reason: 'private_range', message: `IPv6 site-local (${ip}, fec0::/10)` };\n }\n return null;\n}\n\nfunction classifyIPv6(ip: string): RejectionDetail | null {\n const lower = ip.toLowerCase();\n if (lower === '::1') return { reason: 'loopback', message: `IPv6 loopback (${ip})` };\n\n const groups = toHextets(lower);\n if (groups === null) return null;\n\n const prefixDetail = classifyIPv6Prefix(groups[0] ?? 0, ip);\n if (prefixDetail !== null) return prefixDetail;\n\n if (groups.every((n) => n === 0)) {\n return { reason: 'reserved', message: `IPv6 unspecified (${ip}) — routes to localhost` };\n }\n\n // An IPv4 address written in IPv6 form is still that IPv4 address, and\n // `isIPv6` is true for it so it never reached the IPv4 rules — IMDS,\n // loopback and RFC1918 were all reachable this way. Note the URL parser\n // normalises the dotted spelling to hex, so the hex form is the one that\n // actually arrives.\n const v4 = embeddedIPv4(groups);\n if (v4 === null) return null;\n const detail = classifyIPv4(v4);\n return detail === null\n ? null\n : { reason: detail.reason, message: `${detail.message} via IPv6 (${ip})` };\n}\n"],"mappings":"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","names":["logger"]}
@@ -4,7 +4,7 @@ import {
4
4
  getErrorMessage,
5
5
  getTimeProvider,
6
6
  ok
7
- } from "./chunk-WFHUSSKZ.js";
7
+ } from "./chunk-5CIP4EWI.js";
8
8
 
9
9
  // src/context/session-memory.ts
10
10
  import * as fs from "node:fs";
@@ -370,4 +370,4 @@ export {
370
370
  SessionMemory,
371
371
  createSessionMemory
372
372
  };
373
- //# sourceMappingURL=chunk-NBTG2OQC.js.map
373
+ //# sourceMappingURL=chunk-AE2U6NUZ.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  createLogger,
6
6
  getTimeProvider
7
- } from "./chunk-WFHUSSKZ.js";
7
+ } from "./chunk-5CIP4EWI.js";
8
8
 
9
9
  // src/mcp/tools/scanner-registry-fetcher.ts
10
10
  import { z } from "zod";
@@ -740,4 +740,4 @@ export {
740
740
  generateSecurityPlan,
741
741
  buildPlanFromAnalysis
742
742
  };
743
- //# sourceMappingURL=chunk-ZVQ2Q7OM.js.map
743
+ //# sourceMappingURL=chunk-AVEDKF3J.js.map
@@ -2,7 +2,7 @@ import {
2
2
  createLogger,
3
3
  err,
4
4
  ok
5
- } from "./chunk-WFHUSSKZ.js";
5
+ } from "./chunk-5CIP4EWI.js";
6
6
 
7
7
  // src/scm/token-resolver.ts
8
8
  import { execFile } from "node:child_process";
@@ -87,4 +87,4 @@ export {
87
87
  hasToken,
88
88
  getTokenEnvVars
89
89
  };
90
- //# sourceMappingURL=chunk-IPI4I2UW.js.map
90
+ //# sourceMappingURL=chunk-CMZPHXDH.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveClassGuardMs
3
- } from "./chunk-WFHUSSKZ.js";
3
+ } from "./chunk-5CIP4EWI.js";
4
4
 
5
5
  // src/cli/cli-readiness.ts
6
6
  var READINESS_LEVELS = ["installed", "authenticated", "serves"];
@@ -90,4 +90,4 @@ export {
90
90
  SERVES_PROBE_PROMPT,
91
91
  probeServes
92
92
  };
93
- //# sourceMappingURL=chunk-MSPYAOAU.js.map
93
+ //# sourceMappingURL=chunk-DWQ7TG5L.js.map
@@ -8,13 +8,13 @@ import {
8
8
  loadTechniquesRegistry,
9
9
  resolveInsideRoot,
10
10
  savePapersRegistry
11
- } from "./chunk-MRGBENPM.js";
11
+ } from "./chunk-NEIPHVTA.js";
12
12
  import {
13
13
  resolveToken
14
- } from "./chunk-IPI4I2UW.js";
14
+ } from "./chunk-CMZPHXDH.js";
15
15
  import {
16
16
  getToolMemory
17
- } from "./chunk-NOSQXAW3.js";
17
+ } from "./chunk-YUDJV6JF.js";
18
18
  import {
19
19
  capitalizeWords,
20
20
  tokenizeFiltered,
@@ -43,7 +43,7 @@ import {
43
43
  ok,
44
44
  registerPersistentDistillerFactory,
45
45
  sigmoidConfidence
46
- } from "./chunk-WFHUSSKZ.js";
46
+ } from "./chunk-5CIP4EWI.js";
47
47
  import {
48
48
  ensureLearningDir,
49
49
  getRulesFile
@@ -4241,4 +4241,4 @@ export {
4241
4241
  DEFAULT_CONTEXT_BUDGET_TOKENS,
4242
4242
  getContextPromptPrefix
4243
4243
  };
4244
- //# sourceMappingURL=chunk-6E3DFUYA.js.map
4244
+ //# sourceMappingURL=chunk-EMWRUQQS.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isGatewayModelAdapter
3
- } from "./chunk-RC4ZQ4ZE.js";
3
+ } from "./chunk-Q6JZQWQB.js";
4
4
  import {
5
5
  allOf
6
6
  } from "./chunk-OQB3XC5O.js";
@@ -24,20 +24,20 @@ import {
24
24
  toolSuccessStructured,
25
25
  withProgressHeartbeat,
26
26
  wrapToolWithTimeout
27
- } from "./chunk-J527NA52.js";
27
+ } from "./chunk-5EXZHBAQ.js";
28
28
  import {
29
29
  VERSION
30
- } from "./chunk-EVBLCOEN.js";
30
+ } from "./chunk-MYF73MI3.js";
31
31
  import {
32
32
  UNRESOLVED_MODEL_ID,
33
33
  assessPanelIndependence,
34
34
  countDistinctModels,
35
35
  createEvent,
36
36
  getGlobalRegistry
37
- } from "./chunk-CMG3P6OI.js";
37
+ } from "./chunk-PSCY6MSD.js";
38
38
  import {
39
39
  getToolMemory
40
- } from "./chunk-NOSQXAW3.js";
40
+ } from "./chunk-YUDJV6JF.js";
41
41
  import {
42
42
  authRemediation,
43
43
  classifyExtractedError,
@@ -48,7 +48,7 @@ import {
48
48
  sanitizeOutput,
49
49
  sleep,
50
50
  withTimeout
51
- } from "./chunk-UF3BNMOV.js";
51
+ } from "./chunk-33HHRRKZ.js";
52
52
  import {
53
53
  AgentError,
54
54
  CLI_NAMES,
@@ -81,7 +81,7 @@ import {
81
81
  registerPersistentOutcomeStoreFactory,
82
82
  resolveClassGuardMs,
83
83
  resolveVoteTimeout
84
- } from "./chunk-WFHUSSKZ.js";
84
+ } from "./chunk-5CIP4EWI.js";
85
85
  import {
86
86
  ensureLearningDir,
87
87
  getOutcomesFile
@@ -6410,7 +6410,7 @@ function logPartialProposal(bounded, log) {
6410
6410
  }
6411
6411
  async function runContrarianCheck(proposal, log, opts) {
6412
6412
  try {
6413
- const { executeExpert } = await import("./expert-bridge-F7V2AXUD.js");
6413
+ const { executeExpert } = await import("./expert-bridge-5MFBN4ZW.js");
6414
6414
  const bounded = boundArtifactForReview(proposal, CONTRARIAN_PROPOSAL_BUDGET, "proposal");
6415
6415
  logPartialProposal(bounded, log);
6416
6416
  const result = await executeExpert("architecture", buildContrarianPrompt(bounded, opts), {
@@ -9188,4 +9188,4 @@ export {
9188
9188
  CONSENSUS_VOTE_TOOL_SCHEMA,
9189
9189
  registerConsensusVoteTool
9190
9190
  };
9191
- //# sourceMappingURL=chunk-F76Y5QXV.js.map
9191
+ //# sourceMappingURL=chunk-ER76ZRBU.js.map
@@ -2,7 +2,7 @@ import {
2
2
  CACHE_TIMEOUTS,
3
3
  createLogger,
4
4
  getTimeProvider
5
- } from "./chunk-WFHUSSKZ.js";
5
+ } from "./chunk-5CIP4EWI.js";
6
6
 
7
7
  // src/security/trust-types.ts
8
8
  import { z } from "zod";
@@ -610,4 +610,4 @@ export {
610
610
  resolveReputationGatingMode,
611
611
  gateWithReputation
612
612
  };
613
- //# sourceMappingURL=chunk-T6W3N3N3.js.map
613
+ //# sourceMappingURL=chunk-F7R2E74V.js.map
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  resolveCliSlot
3
- } from "./chunk-VFTP7WSH.js";
3
+ } from "./chunk-J76ZM6QF.js";
4
4
  import {
5
5
  createLogger,
6
6
  getTimeProvider,
7
7
  isRateLimitText
8
- } from "./chunk-WFHUSSKZ.js";
8
+ } from "./chunk-5CIP4EWI.js";
9
9
 
10
10
  // src/pipeline/expert-bridge.ts
11
11
  function resolveCliFromModelString(model) {
@@ -21,7 +21,7 @@ async function getMcpConfigPath() {
21
21
  if (cachedMcpConfigPath !== null) return cachedMcpConfigPath;
22
22
  mcpConfigInitPromise ??= (async () => {
23
23
  try {
24
- const { generateMcpConfig } = await import("./child-mcp-config-3IELNFHI.js");
24
+ const { generateMcpConfig } = await import("./child-mcp-config-IRZHH2TU.js");
25
25
  const config = await generateMcpConfig();
26
26
  cachedMcpConfigPath = config.configPath;
27
27
  cachedMcpConfigCleanup = config.cleanup;
@@ -93,8 +93,8 @@ var routerInitPromise = null;
93
93
  async function getRouter() {
94
94
  if (cachedRouter !== null) return cachedRouter;
95
95
  routerInitPromise ??= (async () => {
96
- const { createAllAdapters } = await import("./factory-MZTDIMCP.js");
97
- const { createCompositeRouter } = await import("./composite-router-DSYVXQ7C.js");
96
+ const { createAllAdapters } = await import("./factory-3ZA63QDH.js");
97
+ const { createCompositeRouter } = await import("./composite-router-F3D2QRHI.js");
98
98
  const adapters = createAllAdapters();
99
99
  if (adapters.size === 0) {
100
100
  routerInitPromise = null;
@@ -102,7 +102,7 @@ async function getRouter() {
102
102
  }
103
103
  cachedRouter = adaptCompositeRouter(createCompositeRouter(adapters));
104
104
  try {
105
- const { createCliCircuitBreakerIntegration } = await import("./cli-circuit-breaker-H5CESPLC.js");
105
+ const { createCliCircuitBreakerIntegration } = await import("./cli-circuit-breaker-DJU6WTWT.js");
106
106
  cachedCircuitBreaker = createCliCircuitBreakerIntegration([...adapters.values()]);
107
107
  } catch (error) {
108
108
  const msg = error instanceof Error ? error.message : String(error);
@@ -216,4 +216,4 @@ export {
216
216
  tokenSplitFromUsage,
217
217
  executeExpert
218
218
  };
219
- //# sourceMappingURL=chunk-PJZQQYLJ.js.map
219
+ //# sourceMappingURL=chunk-GHNHZSDW.js.map
@@ -3,7 +3,7 @@ import {
3
3
  MODEL_IDS,
4
4
  getTimeProvider,
5
5
  resolveModelIdentitySync
6
- } from "./chunk-WFHUSSKZ.js";
6
+ } from "./chunk-5CIP4EWI.js";
7
7
 
8
8
  // src/config/model-availability.ts
9
9
  var DEFAULT_TTL_MS = 6e4;
@@ -150,4 +150,4 @@ export {
150
150
  resetAvailabilityCache,
151
151
  filterAvailableModels
152
152
  };
153
- //# sourceMappingURL=chunk-VFTP7WSH.js.map
153
+ //# sourceMappingURL=chunk-J76ZM6QF.js.map
@@ -3,7 +3,7 @@ import {
3
3
  ValidationError,
4
4
  createLogger,
5
5
  formatZodError
6
- } from "./chunk-WFHUSSKZ.js";
6
+ } from "./chunk-5CIP4EWI.js";
7
7
 
8
8
  // src/security/ast-rule-runner.ts
9
9
  import { existsSync } from "node:fs";
@@ -241,4 +241,4 @@ export {
241
241
  collectAstQaFindings,
242
242
  runAstQaRules
243
243
  };
244
- //# sourceMappingURL=chunk-AONC4VYB.js.map
244
+ //# sourceMappingURL=chunk-LIVTFARG.js.map
@@ -1,6 +1,6 @@
1
1
  // src/version.ts
2
2
  import semver from "semver";
3
- var VERSION = true ? "8.76.2" : "dev";
3
+ var VERSION = true ? "8.78.0" : "dev";
4
4
  var NODE_ENGINE_RANGE = ">=22.5.0";
5
5
  function isNodeVersionSupported(version) {
6
6
  return semver.satisfies(version, NODE_ENGINE_RANGE);
@@ -11,4 +11,4 @@ export {
11
11
  NODE_ENGINE_RANGE,
12
12
  isNodeVersionSupported
13
13
  };
14
- //# sourceMappingURL=chunk-EVBLCOEN.js.map
14
+ //# sourceMappingURL=chunk-MYF73MI3.js.map
@@ -5,7 +5,7 @@ import {
5
5
  err,
6
6
  getErrorMessage,
7
7
  ok
8
- } from "./chunk-WFHUSSKZ.js";
8
+ } from "./chunk-5CIP4EWI.js";
9
9
  import {
10
10
  findRepoRoot,
11
11
  getActiveWorkspaceRoot,
@@ -223,4 +223,4 @@ export {
223
223
  loadPapersRegistry,
224
224
  savePapersRegistry
225
225
  };
226
- //# sourceMappingURL=chunk-MRGBENPM.js.map
226
+ //# sourceMappingURL=chunk-NEIPHVTA.js.map
@@ -7,7 +7,7 @@ import {
7
7
  toolStructuredError,
8
8
  toolSuccessStructured,
9
9
  wrapToolWithTimeout
10
- } from "./chunk-J527NA52.js";
10
+ } from "./chunk-5EXZHBAQ.js";
11
11
  import {
12
12
  JsonlStore,
13
13
  SECURITY_KEYWORDS,
@@ -20,7 +20,7 @@ import {
20
20
  getTimeProvider,
21
21
  isDeclaredNeverDeprecate,
22
22
  isRegisteredToolName
23
- } from "./chunk-WFHUSSKZ.js";
23
+ } from "./chunk-5CIP4EWI.js";
24
24
  import {
25
25
  getNexusDataDir,
26
26
  nexusDataPath
@@ -2282,4 +2282,4 @@ export {
2282
2282
  runImprovementReview,
2283
2283
  registerImprovementReviewTool
2284
2284
  };
2285
- //# sourceMappingURL=chunk-IDPA4X6J.js.map
2285
+ //# sourceMappingURL=chunk-NW5MCYOE.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  GitHubProvider,
3
3
  ScmError
4
- } from "./chunk-GOXBEH6M.js";
4
+ } from "./chunk-VFFG3HIY.js";
5
5
  import {
6
6
  ROLE_DEFAULT_TRUST,
7
7
  ReputationCache,
@@ -12,10 +12,10 @@ import {
12
12
  resolveEnvMode,
13
13
  resolveReputationGatingMode,
14
14
  sanitizeInput
15
- } from "./chunk-T6W3N3N3.js";
15
+ } from "./chunk-F7R2E74V.js";
16
16
  import {
17
17
  hasToken
18
- } from "./chunk-IPI4I2UW.js";
18
+ } from "./chunk-CMZPHXDH.js";
19
19
  import {
20
20
  CLI_SUBPROCESS_TIMEOUTS,
21
21
  createLogger,
@@ -23,7 +23,7 @@ import {
23
23
  getErrorMessage,
24
24
  getTimeProvider,
25
25
  ok
26
- } from "./chunk-WFHUSSKZ.js";
26
+ } from "./chunk-5CIP4EWI.js";
27
27
 
28
28
  // src/security/firewall/firewall-policy-mode.ts
29
29
  import { z } from "zod";
@@ -1336,7 +1336,8 @@ var HostileInputFirewall = class {
1336
1336
  return err({
1337
1337
  code: "POLICY_REFUSED",
1338
1338
  message: `Refused by firewall policy: ${corroboration.actionType} lacks required corroboration \u2014 ${corroboration.missing.join("; ")}`,
1339
- stage: "corroboration"
1339
+ stage: "corroboration",
1340
+ missing: corroboration.missing
1340
1341
  });
1341
1342
  }
1342
1343
  return ok({
@@ -1344,6 +1345,7 @@ var HostileInputFirewall = class {
1344
1345
  satisfied: corroboration.satisfied,
1345
1346
  missing: corroboration.missing,
1346
1347
  corroboratingSources: corroboration.corroboratingSources,
1348
+ clearedOnlyByUnverifiedSources: corroboration.clearedOnlyByUnverifiedSources,
1347
1349
  policyMode: this.policyMode,
1348
1350
  wouldRefuse: !corroboration.satisfied && this.policyMode === "audit"
1349
1351
  });
@@ -1565,6 +1567,7 @@ function getUntrustedInputFirewall() {
1565
1567
  singleton ??= new HostileInputFirewall({
1566
1568
  adapter: createGitHubAdapter(),
1567
1569
  contentDowngrade: false,
1570
+ stages: { corroboration: true },
1568
1571
  ...configuredAuditLogger !== void 0 ? { auditLogger: configuredAuditLogger } : {}
1569
1572
  });
1570
1573
  return singleton;
@@ -1609,6 +1612,44 @@ function evaluateActionThroughFirewall(input, options) {
1609
1612
  wouldRefuse
1610
1613
  });
1611
1614
  }
1615
+ function validateActionCorroboration(action) {
1616
+ const result = getUntrustedInputFirewall().validateAction(action);
1617
+ if (!result.ok) {
1618
+ const { code, stage, missing } = result.error;
1619
+ if (code !== "POLICY_REFUSED" || stage !== "corroboration") return err(asError(result.error));
1620
+ if (missing === void 0 || missing.length === 0) return err(asError(result.error));
1621
+ logger4.warn("Untrusted-input firewall refused an uncorroborated action (enforce mode)", {
1622
+ actionType: action.type,
1623
+ stage,
1624
+ missing
1625
+ });
1626
+ return ok({ refused: true, satisfied: false, stage, missing, policyMode: "enforce" });
1627
+ }
1628
+ const validation = result.value;
1629
+ if (!validation.evaluated) {
1630
+ return err(
1631
+ new Error(
1632
+ `Untrusted-input firewall did not evaluate corroboration for action ${action.type} (${validation.reason})`
1633
+ )
1634
+ );
1635
+ }
1636
+ if (validation.wouldRefuse) {
1637
+ logger4.warn("Untrusted-input firewall would refuse under enforce (audit mode)", {
1638
+ actionType: action.type,
1639
+ stage: "corroboration",
1640
+ missing: validation.missing
1641
+ });
1642
+ }
1643
+ return ok({
1644
+ refused: false,
1645
+ satisfied: validation.satisfied,
1646
+ missing: validation.missing,
1647
+ corroboratingSources: validation.corroboratingSources,
1648
+ clearedOnlyByUnverifiedSources: validation.clearedOnlyByUnverifiedSources,
1649
+ policyMode: validation.policyMode,
1650
+ wouldRefuse: validation.wouldRefuse
1651
+ });
1652
+ }
1612
1653
  function processAndLog(input, options) {
1613
1654
  const result = getUntrustedInputFirewall().process(input, options);
1614
1655
  if (!result.ok) {
@@ -2038,9 +2079,17 @@ function describeAction(action) {
2038
2079
  }
2039
2080
  }
2040
2081
  function buildActionDetails(action, policy, corrob) {
2082
+ const policyRules = policy.violations.map((violation) => violation.rule);
2041
2083
  return {
2042
- policyViolations: policy.violations.map((violation) => violation.rule),
2043
- missingCorroboration: corrob.missing,
2084
+ ...corrob.refused ? {
2085
+ policyViolations: [...policyRules, "INSUFFICIENT_CORROBORATION"],
2086
+ missingCorroboration: corrob.missing,
2087
+ refusedAtStage: corrob.stage
2088
+ } : {
2089
+ policyViolations: policyRules,
2090
+ missingCorroboration: corrob.missing,
2091
+ corroborationWouldRefuse: corrob.wouldRefuse
2092
+ },
2044
2093
  ...action.type === "ClassifyIssue" && { category: action.category },
2045
2094
  ...action.type === "ProposeLabels" && { labels: action.labels }
2046
2095
  };
@@ -2051,17 +2100,29 @@ function validateActionsThroughFirewall(input, gate, actions, existingLabels) {
2051
2100
  for (const action of actions) {
2052
2101
  const decision = evaluateActionThroughFirewall(input, { ...gate, ...labels, action });
2053
2102
  if (!decision.ok) return decision;
2054
- const corrobResult = validateCorroboration(action);
2103
+ const corroboration = validateActionCorroboration(action);
2104
+ if (!corroboration.ok) return corroboration;
2055
2105
  proposed.push({
2056
2106
  type: action.type,
2057
2107
  description: describeAction(action),
2058
- policyApproved: decision.value.allowed,
2059
- corroborated: corrobResult.satisfied,
2060
- details: buildActionDetails(action, decision.value, corrobResult)
2108
+ policyApproved: decision.value.allowed && !corroboration.value.refused,
2109
+ corroborated: corroboration.value.satisfied,
2110
+ details: buildActionDetails(action, decision.value, corroboration.value)
2061
2111
  });
2062
2112
  }
2063
2113
  return ok(proposed);
2064
2114
  }
2115
+ function isStringArray(value) {
2116
+ return Array.isArray(value) && value.every((v) => typeof v === "string");
2117
+ }
2118
+ function readActionRefusal(action) {
2119
+ const { policyViolations, missingCorroboration, refusedAtStage } = action.details;
2120
+ return {
2121
+ policyViolations: isStringArray(policyViolations) ? policyViolations : [],
2122
+ missingCorroboration: isStringArray(missingCorroboration) ? missingCorroboration : [],
2123
+ ...refusedAtStage === "corroboration" ? { refusedAtStage } : {}
2124
+ };
2125
+ }
2065
2126
 
2066
2127
  // src/dogfooding/issue-triage.ts
2067
2128
  var logger6 = createLogger({ component: "IssueTriage" });
@@ -2440,12 +2501,14 @@ export {
2440
2501
  configureUntrustedInputFirewall,
2441
2502
  runUntrustedInputFirewall,
2442
2503
  evaluateActionThroughFirewall,
2504
+ validateActionCorroboration,
2443
2505
  firewallInputFor,
2444
2506
  parsePRUrl,
2445
2507
  GitHubReviewer,
2446
2508
  GitHubUserInfo,
2447
2509
  createFullGitHubProvider,
2510
+ readActionRefusal,
2448
2511
  IssueTriage,
2449
2512
  createIssueTriage
2450
2513
  };
2451
- //# sourceMappingURL=chunk-G744HQMZ.js.map
2514
+ //# sourceMappingURL=chunk-OZFXXGGZ.js.map