@iislee/opencodex 2.11.0 → 2.31.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 (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -1,8 +1,12 @@
1
+ import { sseFieldValue } from "../lib/sse-decoder";
2
+ import {
3
+ appendSafeWebSearchSource,
4
+ safeWebSearchSources,
5
+ type SafeWebSearchSource,
6
+ } from "./sources";
7
+
1
8
  /** A single web source backing the sidecar's answer. */
2
- export interface WebSearchSource {
3
- url: string;
4
- title?: string;
5
- }
9
+ export type WebSearchSource = SafeWebSearchSource;
6
10
 
7
11
  /** The sidecar's synthesized answer plus its sources (empty `sources` is fine). */
8
12
  export interface WebSearchResult {
@@ -27,11 +31,17 @@ interface OutputItem {
27
31
  content?: OutputTextBlock[];
28
32
  }
29
33
 
34
+ // ChatGPT's Codex backend does not accept `max_output_tokens` on sidecar requests. Bound the raw
35
+ // streamed response here, before decoded text and authoritative/delta copies can accumulate.
36
+ export const MAX_SIDECAR_RESPONSE_BYTES = 64 * 1024;
37
+
30
38
  /** Push a `url_citation` annotation as a source, de-duplicated by URL. */
31
39
  function collectAnnotation(ann: AnnotationLike | undefined, sources: WebSearchSource[], seen: Set<string>): void {
32
40
  if (!ann || ann.type !== "url_citation" || typeof ann.url !== "string" || seen.has(ann.url)) return;
33
- seen.add(ann.url);
34
- sources.push({ url: ann.url, ...(ann.title ? { title: ann.title } : {}) });
41
+ if (appendSafeWebSearchSource(sources, {
42
+ url: ann.url,
43
+ ...(ann.title !== undefined ? { title: ann.title } : {}),
44
+ })) seen.add(ann.url);
35
45
  }
36
46
 
37
47
  /**
@@ -45,9 +55,58 @@ function collectAnnotation(ann: AnnotationLike | undefined, sources: WebSearchSo
45
55
  * (`### Sources:`, `**Sources**`), a title line whose URL sits on the FOLLOWING line, and trailing
46
56
  * URL punctuation (`;`, `,`, `)`, `]`, `.`). Prose that follows the source list is preserved.
47
57
  */
48
- const URL_RE = /https?:\/\/[^\s<>()\[\]]+/;
58
+ const URL_RE = /https?:\/\/[^\s<>()\[\]]+/i;
59
+ // Recognize URI-like candidates separately from the HTTP(S)-only acceptance boundary. A rejected
60
+ // citation (for example `javascript:`) still belongs to the trailing Sources block and must not be
61
+ // left behind as ordinary assistant text.
62
+ const URI_LIKE_RE = /[a-z][a-z0-9+.-]*:[^\s<>()\[\]]+/i;
49
63
  // A "Sources:" / "Source:" header, allowing markdown prefixes (#, *, -, >) and bold/italic wrappers.
50
- const SOURCES_HEADER_RE = /^\s*(?:#{1,6}\s*)?[-*>\s]*\**\s*sources?\s*\**\s*:?\s*\**\s*$/i;
64
+ const SOURCES_WORD_RE = /^sources?/i;
65
+
66
+ /** Match the legacy Sources-header grammar without overlapping regex quantifiers. */
67
+ function isSourcesHeader(line: string): boolean {
68
+ let cursor = 0;
69
+ /** Match one code unit using JavaScript's existing `\s` semantics. */
70
+ const isWhitespace = (char: string | undefined): boolean => char !== undefined && /\s/u.test(char);
71
+ /** Advance over the current contiguous whitespace run. */
72
+ const skipWhitespace = (): void => {
73
+ while (isWhitespace(line[cursor])) cursor += 1;
74
+ };
75
+ /** Advance over the current contiguous Markdown-star run. */
76
+ const skipStars = (): void => {
77
+ while (line[cursor] === "*") cursor += 1;
78
+ };
79
+
80
+ skipWhitespace();
81
+ if (line[cursor] === "#") {
82
+ const start = cursor;
83
+ while (line[cursor] === "#") cursor += 1;
84
+ if (cursor - start > 6) return false;
85
+ skipWhitespace();
86
+ }
87
+
88
+ while (isWhitespace(line[cursor]) || line[cursor] === "-" || line[cursor] === "*" || line[cursor] === ">") {
89
+ cursor += 1;
90
+ }
91
+
92
+ const word = SOURCES_WORD_RE.exec(line.slice(cursor, cursor + 7));
93
+ if (!word) return false;
94
+ cursor += word[0].length;
95
+
96
+ // Preserve `\s*\**\s*:?\s*\**\s*$`: at most two star runs, with the optional
97
+ // colon between them. Either run may be empty, so `Sources:*` is one trailing run; three
98
+ // separated runs and a colon after the second run remain invalid.
99
+ skipWhitespace();
100
+ skipStars();
101
+ skipWhitespace();
102
+ if (line[cursor] === ":") {
103
+ cursor += 1;
104
+ skipWhitespace();
105
+ }
106
+ skipStars();
107
+ skipWhitespace();
108
+ return cursor === line.length;
109
+ }
51
110
 
52
111
  /** Trim wrapping/trailing noise from a captured URL: angle brackets, then trailing punctuation. */
53
112
  function cleanUrl(url: string): string {
@@ -62,14 +121,14 @@ function cleanTitle(prefix: string): string {
62
121
  return title;
63
122
  }
64
123
 
65
- function extractTrailingSources(text: string): { text: string; sources: WebSearchSource[] } {
124
+ function extractTrailingSources(text: string): { text: string; sources: WebSearchSource[]; stripped: boolean } {
66
125
  const lines = text.split("\n");
67
126
  // Find the LAST line that is a "Sources:" header (markdown prefixes allowed).
68
127
  let headerIdx = -1;
69
128
  for (let i = lines.length - 1; i >= 0; i--) {
70
- if (SOURCES_HEADER_RE.test(lines[i])) { headerIdx = i; break; }
129
+ if (isSourcesHeader(lines[i])) { headerIdx = i; break; }
71
130
  }
72
- if (headerIdx === -1) return { text, sources: [] };
131
+ if (headerIdx === -1) return { text, sources: [], stripped: false };
73
132
  const sources: WebSearchSource[] = [];
74
133
  const seen = new Set<string>();
75
134
  // Track the last line index actually consumed as part of the source list so trailing prose after
@@ -77,14 +136,16 @@ function extractTrailingSources(text: string): { text: string; sources: WebSearc
77
136
  let lastConsumed = headerIdx;
78
137
  // A title line whose URL is expected on a following line (multiline entry).
79
138
  let pendingTitle: string | null = null;
139
+ let consumedSourceLine = false;
80
140
  for (let i = headerIdx + 1; i < lines.length; i++) {
81
141
  const raw = lines[i].trim();
82
142
  if (raw === "") {
83
143
  // Blank line between header and first entry is fine; a blank AFTER entries ends the list.
84
- if (sources.length > 0 || pendingTitle !== null) break;
144
+ if (consumedSourceLine || pendingTitle !== null) break;
85
145
  continue;
86
146
  }
87
- const m = raw.match(URL_RE);
147
+ const httpMatch = raw.match(URL_RE);
148
+ const m = httpMatch ?? raw.match(URI_LIKE_RE);
88
149
  if (!m) {
89
150
  // A list-ish line with no URL may be a title whose URL is on the next line. Only treat it as a
90
151
  // pending title when it looks like a list item; otherwise it's prose → stop.
@@ -93,23 +154,27 @@ function extractTrailingSources(text: string): { text: string; sources: WebSearc
93
154
  }
94
155
  break;
95
156
  }
157
+ // A non-HTTP URI embedded in prose is not sufficient to classify the line as a citation.
158
+ // Accept it as a consumed source line only when it is a list item or the whole line starts with
159
+ // the URI candidate, matching the existing bare-URL grammar.
160
+ if (!httpMatch && !/^[-*>\d.)]/.test(raw) && m.index !== 0) break;
96
161
  const url = cleanUrl(m[0]);
97
162
  if (!url) { break; }
163
+ consumedSourceLine = true;
98
164
  lastConsumed = i;
99
165
  // Title: text before the URL on this line, else a buffered title from a preceding line.
100
166
  const inlinePrefix = raw.slice(0, m.index);
101
167
  const title = cleanTitle(inlinePrefix) || (pendingTitle ? cleanTitle(pendingTitle) : "");
102
168
  pendingTitle = null;
103
169
  if (seen.has(url)) continue;
104
- seen.add(url);
105
- sources.push(title ? { url, title } : { url });
170
+ if (appendSafeWebSearchSource(sources, title ? { url, title } : { url })) seen.add(url);
106
171
  }
107
- if (sources.length === 0) return { text, sources: [] };
172
+ if (!consumedSourceLine) return { text, sources: [], stripped: false };
108
173
  // Keep text before the header AND any prose after the consumed source lines.
109
174
  const before = lines.slice(0, headerIdx).join("\n").replace(/\s+$/, "");
110
175
  const after = lines.slice(lastConsumed + 1).join("\n").replace(/^\s+/, "");
111
176
  const body = after ? (before ? `${before}\n\n${after}` : after) : before;
112
- return { text: body, sources };
177
+ return { text: body, sources, stripped: true };
113
178
  }
114
179
 
115
180
  /** Pull final text + url_citation sources from a completed Responses `output[]` array. */
@@ -128,6 +193,15 @@ function fromOutputArray(output: OutputItem[], seen: Set<string>): WebSearchResu
128
193
  return { text, sources };
129
194
  }
130
195
 
196
+ function cancelReaderWithoutWaiting(
197
+ reader: ReadableStreamDefaultReader<Uint8Array>,
198
+ reason: string,
199
+ ): void {
200
+ try {
201
+ void reader.cancel(reason).catch(() => undefined);
202
+ } catch { /* best-effort body teardown */ }
203
+ }
204
+
131
205
  /**
132
206
  * Parse the sidecar's streamed Responses SSE into a final answer + sources. Tolerant of the full set of
133
207
  * Responses streaming events: prefers the authoritative `response.completed` output[], then the
@@ -141,6 +215,7 @@ export async function parseSidecarSSE(response: Response): Promise<WebSearchResu
141
215
  const reader = response.body.getReader();
142
216
  const decoder = new TextDecoder();
143
217
  let buffer = "";
218
+ let responseBytes = 0;
144
219
  const seen = new Set<string>();
145
220
  // Holder object — fields are mutated inside the closure, so they can't live as narrowed locals.
146
221
  const acc: {
@@ -153,11 +228,23 @@ export async function parseSidecarSSE(response: Response): Promise<WebSearchResu
153
228
 
154
229
  const handle = (payload: string): void => {
155
230
  if (!payload || payload === "[DONE]") return;
156
- let data: Record<string, unknown>;
157
- try { data = JSON.parse(payload) as Record<string, unknown>; } catch {
158
- console.warn(`[web-search-parse] malformed SSE JSON (${payload.length} chars): ${payload.slice(0, 120)}`);
231
+ // Neither warning below copies the frame's content. An upstream SSE payload can carry model
232
+ // output or credential material, and a malformed frame is exactly the case where the content
233
+ // is least trustworthy. Length plus a classification separates the two failure modes in a log
234
+ // without reproducing anything from the wire.
235
+ let parsed: unknown;
236
+ try { parsed = JSON.parse(payload); } catch {
237
+ console.warn(`[web-search-parse] malformed SSE JSON (${payload.length} chars)`);
159
238
  return;
160
239
  }
240
+ // `JSON.parse("null")` returns null rather than throwing, so the catch above cannot cover it
241
+ // and the `data.type` read below threw out of parseSidecarSSE.
242
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
243
+ const shape = Array.isArray(parsed) ? "array" : parsed === null ? "null" : typeof parsed;
244
+ console.warn(`[web-search-parse] non-record SSE JSON frame (${payload.length} chars, ${shape})`);
245
+ return;
246
+ }
247
+ const data = parsed as Record<string, unknown>;
161
248
  const type = data.type as string | undefined;
162
249
  if (type === "response.output_text.delta" && typeof data.delta === "string") {
163
250
  acc.deltaText += data.delta;
@@ -183,11 +270,22 @@ export async function parseSidecarSSE(response: Response): Promise<WebSearchResu
183
270
  while (true) {
184
271
  const { done, value } = await reader.read();
185
272
  if (done) break;
186
- buffer += decoder.decode(value, { stream: true });
273
+ const remaining = MAX_SIDECAR_RESPONSE_BYTES - responseBytes;
274
+ const accepted = value.byteLength <= remaining ? value : value.subarray(0, remaining);
275
+ responseBytes += accepted.byteLength;
276
+ buffer += decoder.decode(accepted, { stream: true });
187
277
  const lines = buffer.split("\n");
188
278
  buffer = lines.pop() ?? "";
189
279
  for (const line of lines) {
190
- if (line.startsWith("data: ")) handle(line.slice(6).trim());
280
+ const data = sseFieldValue(line, "data");
281
+ if (data !== null) handle(data.trim());
282
+ }
283
+ if (responseBytes >= MAX_SIDECAR_RESPONSE_BYTES) {
284
+ // Preserve complete events accepted up to the cap, but discard any unterminated line and
285
+ // TextDecoder carry. Do not let a rejecting/hung cancel turn bounded partial output into
286
+ // an error or keep this parser waiting on upstream teardown.
287
+ cancelReaderWithoutWaiting(reader, "sidecar response byte limit reached");
288
+ break;
191
289
  }
192
290
  }
193
291
  } finally {
@@ -199,22 +297,19 @@ export async function parseSidecarSSE(response: Response): Promise<WebSearchResu
199
297
  || acc.doneText.trim() && acc.doneText
200
298
  || acc.deltaText;
201
299
  // Merge sources from the final output[] and the streaming annotation events.
202
- const sources = [...(acc.final?.sources ?? [])];
203
- const seenMerge = new Set(sources.map(s => s.url));
300
+ const sources = safeWebSearchSources(acc.final?.sources ?? []);
204
301
  for (const s of acc.streamSources) {
205
- if (!seenMerge.has(s.url)) { seenMerge.add(s.url); sources.push(s); }
302
+ appendSafeWebSearchSource(sources, s);
206
303
  }
207
304
  // Hosted web_search usually omits url_citation annotations and lists sources in a trailing
208
305
  // `Sources:` markdown block instead. Pull those out (and strip the block from the answer so the
209
306
  // tool_result renderer doesn't print sources twice). Annotation titles win; text-block titles
210
307
  // only fill a gap. URL-deduped against annotation sources.
211
- const { text: body, sources: textSources } = extractTrailingSources(typeof text === "string" ? text : "");
308
+ const { text: body, sources: textSources, stripped } = extractTrailingSources(typeof text === "string" ? text : "");
212
309
  for (const s of textSources) {
213
- if (seenMerge.has(s.url)) continue;
214
- seenMerge.add(s.url);
215
- sources.push(s);
310
+ appendSafeWebSearchSource(sources, s);
216
311
  }
217
- const finalText = textSources.length > 0 ? body : (typeof text === "string" ? text : "");
312
+ const finalText = stripped ? body : (typeof text === "string" ? text : "");
218
313
  if (!finalText.trim() && acc.error) return { text: "", sources, error: acc.error };
219
314
  return { text: finalText, sources };
220
315
  }
@@ -0,0 +1,60 @@
1
+ export interface SafeWebSearchSource {
2
+ url: string;
3
+ title?: string;
4
+ }
5
+
6
+ export const MAX_WEB_SEARCH_SOURCES = 20;
7
+ export const MAX_WEB_SEARCH_URL_BYTES = 2_048;
8
+ export const MAX_WEB_SEARCH_TITLE_BYTES = 256;
9
+ export const MAX_WEB_SEARCH_SOURCE_BYTES = 16_384;
10
+
11
+ const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f-\u009f]/u;
12
+ const encoder = new TextEncoder();
13
+
14
+ function byteLength(value: string): number {
15
+ return encoder.encode(value).byteLength;
16
+ }
17
+
18
+ function serializedSourceBytes(source: SafeWebSearchSource): number {
19
+ return byteLength(JSON.stringify(source));
20
+ }
21
+
22
+ function safeTitle(value: unknown): string | undefined {
23
+ if (typeof value !== "string"
24
+ || value.trim().length === 0
25
+ || CONTROL_CHARACTERS.test(value)
26
+ || byteLength(value) > MAX_WEB_SEARCH_TITLE_BYTES) return undefined;
27
+ return value;
28
+ }
29
+
30
+ /** Add one client-safe citation while enforcing per-message count and byte budgets. */
31
+ export function appendSafeWebSearchSource(target: SafeWebSearchSource[], source: unknown): boolean {
32
+ if (target.length >= MAX_WEB_SEARCH_SOURCES || !source || typeof source !== "object") return false;
33
+ const candidate = source as { url?: unknown; title?: unknown };
34
+ if (typeof candidate.url !== "string"
35
+ || candidate.url.length === 0
36
+ || candidate.url.trim() !== candidate.url
37
+ || CONTROL_CHARACTERS.test(candidate.url)
38
+ || byteLength(candidate.url) > MAX_WEB_SEARCH_URL_BYTES
39
+ || target.some(existing => existing.url === candidate.url)) return false;
40
+
41
+ let parsed: URL;
42
+ try { parsed = new URL(candidate.url); } catch { return false; }
43
+ if ((parsed.protocol !== "http:" && parsed.protocol !== "https:")
44
+ || parsed.username.length > 0
45
+ || parsed.password.length > 0) return false;
46
+
47
+ const title = safeTitle(candidate.title);
48
+ const next = title === undefined ? { url: candidate.url } : { url: candidate.url, title };
49
+ const usedBytes = target.reduce((sum, item) => sum + serializedSourceBytes(item), 0);
50
+ if (usedBytes + serializedSourceBytes(next) > MAX_WEB_SEARCH_SOURCE_BYTES) return false;
51
+ target.push(next);
52
+ return true;
53
+ }
54
+
55
+ export function safeWebSearchSources(sources: unknown): SafeWebSearchSource[] {
56
+ if (!Array.isArray(sources)) return [];
57
+ const safe: SafeWebSearchSource[] = [];
58
+ for (const source of sources) appendSafeWebSearchSource(safe, source);
59
+ return safe;
60
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Execute ONE web search via a Grok sidecar through the STORED xai OAuth credential (#2188 L7).
3
+ *
4
+ * POSTs the canonical api.x.ai Responses endpoint with the hosted web_search tool (plus the
5
+ * opt-in x_search tool) and reduces the SSE stream to a SidecarOutcome. Wire shapes are
6
+ * probe-verified (devlog 003 / 070): reasoning.effort is accepted alongside tools; both hosted
7
+ * tools may share one request; annotations arrive as url_citation events on message items;
8
+ * x_search activity surfaces as custom_tool_call items (ctc_) rather than the documented
9
+ * x_search_call — both are tolerated, neither is required. `action` may be absent on
10
+ * output_item.added (skeleton-first) and fills in later. Never throws — returns `{error}` so
11
+ * the caller injects a graceful tool result.
12
+ */
13
+ import type { OcxProviderConfig } from "../types";
14
+ import { getValidAccessToken, publicOAuthAuthenticationErrorMessage } from "../oauth";
15
+ import { fetchWithResetRetry } from "../lib/upstream-retry";
16
+ import { cancelBodyOnAbort, signalWithTimeout } from "../lib/abort";
17
+ import { sidecarEnter } from "../lib/sidecar-tracker";
18
+ import { redactSecretString } from "../lib/redact";
19
+ import { MAX_SIDECAR_RESPONSE_BYTES, type WebSearchSource } from "./parse";
20
+ import { BASE_INSTRUCTION, IMAGE_INSTRUCTION, type SidecarOutcome, type SidecarSettings } from "./executor";
21
+
22
+ /** The one destination the OAuth credential may be sent to; provider.baseUrl is honored only on the same origin. */
23
+ const XAI_RESPONSES_ORIGIN = "https://api.x.ai";
24
+
25
+ export interface XaiSearchOptions {
26
+ /** Opt-in: add the hosted x_search tool next to web_search. */
27
+ xSearch?: boolean;
28
+ allowedXHandles?: string[];
29
+ excludedXHandles?: string[];
30
+ fromDate?: string;
31
+ toDate?: string;
32
+ }
33
+
34
+ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
35
+
36
+ /** Doc-validated limits (docs.x.ai x-search): <=20 handles, allow XOR exclude, ISO dates. */
37
+ export function validateXaiSearchOptions(options: XaiSearchOptions): string | undefined {
38
+ const allowed = options.allowedXHandles ?? [];
39
+ const excluded = options.excludedXHandles ?? [];
40
+ if (allowed.length > 0 && excluded.length > 0) return "allowedXHandles and excludedXHandles are mutually exclusive";
41
+ if (allowed.length > 20) return "allowedXHandles admits at most 20 handles";
42
+ if (excluded.length > 20) return "excludedXHandles admits at most 20 handles";
43
+ for (const [field, value] of [["fromDate", options.fromDate], ["toDate", options.toDate]] as const) {
44
+ if (value !== undefined && !ISO_DATE.test(value)) return `${field} must be an ISO-8601 date (YYYY-MM-DD)`;
45
+ }
46
+ return undefined;
47
+ }
48
+
49
+ function buildXSearchTool(options: XaiSearchOptions): Record<string, unknown> {
50
+ return {
51
+ type: "x_search",
52
+ ...(options.allowedXHandles?.length ? { allowed_x_handles: options.allowedXHandles } : {}),
53
+ ...(options.excludedXHandles?.length ? { excluded_x_handles: options.excludedXHandles } : {}),
54
+ ...(options.fromDate ? { from_date: options.fromDate } : {}),
55
+ ...(options.toDate ? { to_date: options.toDate } : {}),
56
+ };
57
+ }
58
+
59
+ function isRec(v: unknown): v is Record<string, unknown> {
60
+ return !!v && typeof v === "object" && !Array.isArray(v);
61
+ }
62
+
63
+ export async function runXaiWebSearch(
64
+ query: string,
65
+ providerName: string,
66
+ provider: OcxProviderConfig,
67
+ settings: SidecarSettings,
68
+ options: XaiSearchOptions = {},
69
+ abortSignal?: AbortSignal,
70
+ ): Promise<SidecarOutcome> {
71
+ const invalid = validateXaiSearchOptions(options);
72
+ if (invalid) return { text: "", sources: [], error: `xai sidecar options invalid: ${invalid}` };
73
+ let token: string;
74
+ try {
75
+ token = await getValidAccessToken(providerName);
76
+ } catch (e) {
77
+ return { text: "", sources: [], error: `xai sidecar auth failed: ${publicOAuthAuthenticationErrorMessage(e)}` };
78
+ }
79
+ // Credential pinning: only the EXACT api.x.ai origin may carry the OAuth bearer.
80
+ // A prefix check would admit https://api.x.ai.evil/ (review Critical); parse and
81
+ // compare origins, falling back to the canonical endpoint on any mismatch.
82
+ let base = `${XAI_RESPONSES_ORIGIN}/v1`;
83
+ if (provider.baseUrl) {
84
+ try {
85
+ const parsed = new URL(provider.baseUrl);
86
+ if (parsed.origin === XAI_RESPONSES_ORIGIN) base = provider.baseUrl.replace(/\/+$/, "");
87
+ } catch { /* malformed baseUrl: keep the canonical endpoint */ }
88
+ }
89
+ const url = `${base}/responses`;
90
+ const instruction = settings.describeImages ? BASE_INSTRUCTION + IMAGE_INSTRUCTION : BASE_INSTRUCTION;
91
+ const body = {
92
+ model: settings.model,
93
+ instructions: instruction,
94
+ input: [{ role: "user", content: query }],
95
+ tools: [{ type: "web_search" }, ...(options.xSearch ? [buildXSearchTool(options)] : [])],
96
+ include: ["web_search_call.action.sources"],
97
+ reasoning: { effort: settings.reasoning },
98
+ stream: true,
99
+ };
100
+ const linkedSignal = signalWithTimeout(settings.timeoutMs, abortSignal);
101
+ const sidecarExit = sidecarEnter("web-search");
102
+ const t0 = Date.now();
103
+ try {
104
+ const res = await fetchWithResetRetry(
105
+ () => fetch(url, {
106
+ method: "POST",
107
+ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` },
108
+ body: JSON.stringify(body),
109
+ signal: linkedSignal.signal,
110
+ // Credential-bearing: never follow a redirect off the pinned origin.
111
+ redirect: "manual",
112
+ }),
113
+ { abortSignal: linkedSignal.signal, label: "xai-web-search-sidecar" },
114
+ );
115
+ const detachBodyGuard = cancelBodyOnAbort(res.body, linkedSignal.signal);
116
+ if (!res.ok) {
117
+ const t = await res.text().catch(() => "");
118
+ detachBodyGuard();
119
+ const entitlement = res.status === 401 || res.status === 403 ? " (Grok OAuth entitlement — re-run ocx login xai?)" : "";
120
+ return { text: "", sources: [], error: `xai sidecar HTTP ${res.status}${entitlement}: ${redactSecretString(t.slice(0, 200))}` };
121
+ }
122
+ try {
123
+ return await parseXaiResponsesSSE(res);
124
+ } finally {
125
+ detachBodyGuard();
126
+ }
127
+ } catch (e) {
128
+ const kind = e instanceof Error && e.name === "TimeoutError" ? "timeout" : "connect_error";
129
+ console.warn(`[web-search] xai sidecar ${kind} (${Date.now() - t0}ms)`);
130
+ return { text: "", sources: [], error: redactSecretString(e instanceof Error ? e.message : String(e)) };
131
+ } finally {
132
+ sidecarExit();
133
+ linkedSignal.cleanup();
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Reduce a Grok Responses SSE stream to a SidecarOutcome (probe-shapes in devlog 003):
139
+ * text from output_text deltas on message items; sources from url_citation annotations
140
+ * unioned with web_search_call action.sources (deduped by url). Tolerates custom_tool_call
141
+ * and x_search_call items, absent `action`, and unknown event names. Bounds raw bytes.
142
+ */
143
+ export async function parseXaiResponsesSSE(response: Response): Promise<SidecarOutcome> {
144
+ if (!response.body) return { text: "", sources: [] };
145
+ const reader = response.body.getReader();
146
+ const decoder = new TextDecoder();
147
+ let buffer = "";
148
+ let responseBytes = 0;
149
+ let text = "";
150
+ let doneText = "";
151
+ const sourceUrls = new Map<string, WebSearchSource>();
152
+ let error: string | null = null;
153
+ const addSource = (url: unknown, title?: unknown) => {
154
+ if (typeof url !== "string" || url.length === 0) return;
155
+ if (!sourceUrls.has(url)) sourceUrls.set(url, { url, ...(typeof title === "string" && title.length > 0 && title !== url ? { title } : {}) });
156
+ };
157
+ try {
158
+ while (true) {
159
+ const { done, value } = await reader.read();
160
+ if (done) break;
161
+ responseBytes += value.byteLength;
162
+ if (responseBytes > MAX_SIDECAR_RESPONSE_BYTES * 8) {
163
+ error = "xai sidecar stream exceeded byte bound";
164
+ await reader.cancel(error).catch(() => {});
165
+ break;
166
+ }
167
+ buffer += decoder.decode(value, { stream: true });
168
+ let idx;
169
+ while ((idx = buffer.indexOf("\n\n")) >= 0) {
170
+ const frame = buffer.slice(0, idx);
171
+ buffer = buffer.slice(idx + 2);
172
+ const dataLine = frame.match(/^data: (.+)$/m)?.[1];
173
+ if (!dataLine || dataLine === "[DONE]") continue;
174
+ let payload: unknown;
175
+ try { payload = JSON.parse(dataLine); } catch { continue; }
176
+ if (!isRec(payload) || typeof payload.type !== "string") continue;
177
+ switch (payload.type) {
178
+ case "response.output_text.delta": {
179
+ if (typeof payload.delta === "string") text += payload.delta;
180
+ break;
181
+ }
182
+ case "response.output_text.done": {
183
+ if (typeof payload.text === "string") doneText = payload.text;
184
+ break;
185
+ }
186
+ case "response.output_text.annotation.added": {
187
+ const annotation = payload.annotation;
188
+ if (isRec(annotation) && annotation.type === "url_citation") addSource(annotation.url, annotation.title);
189
+ break;
190
+ }
191
+ case "response.output_item.done": {
192
+ const item = payload.item;
193
+ if (isRec(item) && item.type === "web_search_call" && isRec(item.action) && Array.isArray(item.action.sources)) {
194
+ for (const s of item.action.sources) if (isRec(s)) addSource(s.url);
195
+ }
196
+ break;
197
+ }
198
+ case "response.failed":
199
+ case "error": {
200
+ const message = isRec(payload.response) && isRec(payload.response.error) && typeof payload.response.error.message === "string"
201
+ ? payload.response.error.message
202
+ : typeof payload.message === "string" ? payload.message : "upstream reported failure";
203
+ error = redactSecretString(String(message).slice(0, 200));
204
+ break;
205
+ }
206
+ default: break;
207
+ }
208
+ }
209
+ }
210
+ } catch (e) {
211
+ error = redactSecretString(e instanceof Error ? e.message : String(e));
212
+ } finally {
213
+ reader.releaseLock();
214
+ }
215
+ const finalText = doneText.length >= text.length ? doneText : text;
216
+ const outcome: SidecarOutcome = { text: finalText, sources: [...sourceUrls.values()] };
217
+ if (error && finalText.length === 0) outcome.error = error;
218
+ return outcome;
219
+ }