@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
@@ -71,6 +71,107 @@ export async function sleepWithAbort(ms: number, signal?: AbortSignal): Promise<
71
71
  });
72
72
  }
73
73
 
74
+ /**
75
+ * Best-effort, bounded cancellation of a response body before a retry backoff.
76
+ *
77
+ * The 429 paths release the unread body before waiting so sockets do not accumulate under a
78
+ * rate-limit storm, but a never-settling `cancel()` promise must not be able to block the
79
+ * abort-aware backoff (client cancel, `maxIntervalMs`, or the cumulative header deadline).
80
+ * Cancellation is started and its rejection observed; the await is bounded by `timeoutMs`
81
+ * and the abort signal. This mirrors the rotation-path guarantee (release is initiated, not
82
+ * awaited forever) while preserving the resource-release intent of the same-target paths.
83
+ */
84
+ export async function releaseResponseBodyBestEffort(
85
+ body: ReadableStream<Uint8Array> | null,
86
+ signal: AbortSignal | undefined,
87
+ timeoutMs = 1_000,
88
+ ): Promise<void> {
89
+ if (!body) return;
90
+ if (signal?.aborted) {
91
+ void body.cancel().catch(() => {});
92
+ return;
93
+ }
94
+ const cancel = body.cancel().catch(() => {});
95
+ if (!signal) {
96
+ await Promise.race([cancel, new Promise<void>(resolve => setTimeout(resolve, timeoutMs))]);
97
+ return;
98
+ }
99
+ await new Promise<void>(resolve => {
100
+ let timer: ReturnType<typeof setTimeout>;
101
+ /**
102
+ * Abort hook: clear the bounded-body release timer and settle the promise so a
103
+ * never-settling cancel() can never block the abort-aware backoff.
104
+ */
105
+ const onAbort = () => {
106
+ clearTimeout(timer);
107
+ resolve();
108
+ };
109
+ timer = setTimeout(() => {
110
+ signal.removeEventListener("abort", onAbort);
111
+ resolve();
112
+ }, timeoutMs);
113
+ signal.addEventListener("abort", onAbort, { once: true });
114
+ void cancel.then(() => {
115
+ clearTimeout(timer);
116
+ signal.removeEventListener("abort", onAbort);
117
+ resolve();
118
+ });
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Abort-aware sleep that yields an adapter `heartbeat` at least every `heartbeatIntervalMs`.
124
+ * The Responses bridge treats a returned iterator event as upstream liveness and aborts turns
125
+ * that stay silent past the stall budget (default 300s), while a retryOn429 wait may legally
126
+ * reach 600s — so deliberate waits must keep the watchdog fed or a long backoff is killed
127
+ * mid-turn. The final chunk always yields once, which doubles as the post-wait liveness beat.
128
+ */
129
+ export async function* sleepWithHeartbeats(
130
+ ms: number,
131
+ signal?: AbortSignal,
132
+ heartbeatIntervalMs = 10_000,
133
+ ): AsyncGenerator<{ type: "heartbeat" }> {
134
+ if (ms <= 0) return;
135
+ // Guard against a non-positive interval: a zero/negative step would spin the loop forever
136
+ // while sleepWithAbort early-returns without ever observing the abort signal. NaN must be
137
+ // normalized too: Math.max(1, NaN) is NaN, which would abort the wait after one beat.
138
+ const stepMs = Number.isNaN(heartbeatIntervalMs) ? 1 : Math.max(1, heartbeatIntervalMs);
139
+ let remaining = ms;
140
+ while (remaining > 0) {
141
+ const chunk = Math.min(remaining, stepMs);
142
+ await sleepWithAbort(chunk, signal);
143
+ remaining -= chunk;
144
+ yield { type: "heartbeat" };
145
+ }
146
+ }
147
+
148
+ export interface SameTarget429WaitOptions {
149
+ body: ReadableStream<Uint8Array> | null;
150
+ signal?: AbortSignal;
151
+ delayMs: number;
152
+ /**
153
+ * When set, the wait yields adapter heartbeats so bridge stall watchdogs stay fed.
154
+ * Omit for pre-stream recovery paths that have no stall watchdog.
155
+ */
156
+ heartbeatIntervalMs?: number;
157
+ }
158
+
159
+ /**
160
+ * Shared pre-replay prep for opt-in same-target 429 waits:
161
+ * release the unread 429 body, then sleep (optionally with heartbeats).
162
+ * Callers still own attempt budgeting, abort re-checks, and the replay itself.
163
+ */
164
+ export async function* prepareSameTarget429Wait(
165
+ options: SameTarget429WaitOptions,
166
+ ): AsyncGenerator<{ type: "heartbeat" }> {
167
+ await releaseResponseBodyBestEffort(options.body, options.signal);
168
+ if (options.heartbeatIntervalMs === undefined) {
169
+ await sleepWithAbort(options.delayMs, options.signal);
170
+ return;
171
+ }
172
+ yield* sleepWithHeartbeats(options.delayMs, options.signal, options.heartbeatIntervalMs);
173
+ }
174
+
74
175
  export function isConnectionResetError(err: unknown): boolean {
75
176
  if (!(err instanceof Error)) return false;
76
177
  // Aborts and timeouts are caller decisions / honest failures — never retryable.
@@ -114,6 +215,7 @@ export async function fetchWithAttemptDeadline(
114
215
  timeoutMs: number,
115
216
  abortSignal?: AbortSignal,
116
217
  preferIdentityEncoding = false,
218
+ executor: typeof globalThis.fetch = globalThis.fetch,
117
219
  ): Promise<Response> {
118
220
  const attemptTimeout = clearableDeadline(timeoutMs, abortSignal);
119
221
  const headers = new Headers(init.headers);
@@ -121,7 +223,7 @@ export async function fetchWithAttemptDeadline(
121
223
  headers.set("accept-encoding", "identity");
122
224
  }
123
225
  try {
124
- return await fetch(url, {
226
+ return await executor(url, {
125
227
  ...init,
126
228
  headers,
127
229
  signal: attemptTimeout.signal,
@@ -148,6 +250,39 @@ export interface TransientRetryOptions extends ResetRetryOptions {
148
250
  export type UpstreamSendRecovery = "connection-reset" | "transient-5xx";
149
251
  type ReplayableFetch = (recovery?: UpstreamSendRecovery) => Promise<Response>;
150
252
 
253
+ /**
254
+ * Rejection thrown by the upstream retry helpers when the terminal attempt
255
+ * rejects after earlier attempts already produced credential-visible evidence:
256
+ * transient 5xx responses, or a connection reset after the request was read.
257
+ *
258
+ * That evidence proves the host and credential path were reached, so the
259
+ * failure must stay account-attributed even though the terminal promise looks
260
+ * like a transport rejection (issue #914 review: mixed 5xx/reset -> rejection
261
+ * must not be downgraded to the account-neutral pre-connection class). The
262
+ * original rejection is preserved as `cause` so its code and message stay
263
+ * inspectable. Extracted from PR #966 (Yuxin-Qiao) with attribution.
264
+ */
265
+ export class UpstreamRetryEvidenceError extends Error {
266
+ constructor(
267
+ public readonly transientStatuses: readonly number[],
268
+ cause: unknown,
269
+ /** True when a connection-reset retry already reached the origin. */
270
+ public readonly resetSeen = false,
271
+ ) {
272
+ const detail = cause instanceof Error ? cause.message : String(cause);
273
+ const kinds: string[] = [];
274
+ if (transientStatuses.length > 0) kinds.push("transient 5xx response(s)");
275
+ if (resetSeen) kinds.push("a credential-visible connection reset");
276
+ super(
277
+ kinds.length > 0
278
+ ? `upstream fetch failed after ${kinds.join(" and ")}: ${detail}`
279
+ : `upstream fetch failed: ${detail}`,
280
+ { cause },
281
+ );
282
+ this.name = "UpstreamRetryEvidenceError";
283
+ }
284
+ }
285
+
151
286
  /**
152
287
  * Opt out of Bun's keep-alive pool after a connection-reset retry.
153
288
  *
@@ -181,12 +316,22 @@ export async function fetchWithResetRetry(
181
316
  ): Promise<Response> {
182
317
  const attempts = Math.max(1, opts.attempts ?? RESET_RETRY_MAX_ATTEMPTS);
183
318
  let lastError: unknown;
319
+ let sawReset = false;
184
320
  for (let attempt = 0; attempt < attempts; attempt++) {
185
321
  if (opts.abortSignal?.aborted) throw abortError(opts.abortSignal);
186
322
  try {
187
323
  return await doFetch(attempt === 0 ? firstRecovery : "connection-reset");
188
324
  } catch (err) {
189
- if (opts.abortSignal?.aborted || !isConnectionResetError(err) || attempt === attempts - 1) throw err;
325
+ if (opts.abortSignal?.aborted) throw err;
326
+ if (!isConnectionResetError(err)) {
327
+ // A reset that already reached the origin is credential-visible
328
+ // evidence: keep it attached so the terminal rejection cannot be
329
+ // downgraded to the pre-connection neutral class (#914 review).
330
+ if (sawReset) throw new UpstreamRetryEvidenceError([], err, true);
331
+ throw err;
332
+ }
333
+ if (attempt === attempts - 1) throw err;
334
+ sawReset = true;
190
335
  lastError = err;
191
336
  console.warn(
192
337
  `[upstream-retry] connection reset${opts.label ? ` (${opts.label})` : ""} — retrying (${attempt + 2}/${attempts})`,
@@ -216,6 +361,7 @@ export async function fetchWithTransientRetry(
216
361
  ): Promise<Response> {
217
362
  const attempts = Math.max(1, opts.attempts ?? TRANSIENT_RETRY_MAX_ATTEMPTS);
218
363
  const slowAttemptMs = opts.slowAttemptMs ?? TRANSIENT_RETRY_SLOW_ATTEMPT_MS;
364
+ const transientStatuses: number[] = [];
219
365
  let attemptStart = Date.now();
220
366
  let res = await fetchWithResetRetry(doFetch, opts);
221
367
  for (let attempt = 0; attempt < attempts - 1; attempt++) {
@@ -233,7 +379,14 @@ export async function fetchWithTransientRetry(
233
379
  cancelResponseBodyBestEffort(res);
234
380
  await sleepWithAbort(delay, opts.abortSignal);
235
381
  attemptStart = Date.now();
236
- res = await fetchWithResetRetry(doFetch, opts, "transient-5xx");
382
+ transientStatuses.push(res.status);
383
+ try {
384
+ res = await fetchWithResetRetry(doFetch, opts, "transient-5xx");
385
+ } catch (err) {
386
+ // Keep the prior 5xx evidence attached: the origin already responded, so
387
+ // this rejection is not pre-connection and must not classify as neutral.
388
+ throw new UpstreamRetryEvidenceError(transientStatuses, err);
389
+ }
237
390
  }
238
391
  return res;
239
392
  }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Windows-tolerant atomic replace.
3
+ *
4
+ * POSIX `rename()` replaces the destination entry unconditionally. Windows can
5
+ * refuse the same call with EBUSY, EPERM or EACCES while another process holds
6
+ * the target open — a real-time scanner that just indexed the file, a sync
7
+ * client, a backup agent. The hold is usually momentary, so a bounded retry
8
+ * turns an operational failure back into a successful publish.
9
+ *
10
+ * The envelope is deliberately small: two retries, 25ms then 50ms, about 75ms
11
+ * total. It is sized for a scanner blinking, not for a file someone actually
12
+ * has open. Widening it without evidence would trade a rare failure for a
13
+ * routine stall.
14
+ *
15
+ * This lives in its own module rather than in config.ts because
16
+ * config-ownership.ts is one of its callers and config.ts already imports
17
+ * config-ownership.ts — exporting it from there would close an import cycle.
18
+ */
19
+
20
+ import { renameSync } from "node:fs";
21
+
22
+ /**
23
+ * Which durable publisher retried. A closed union on purpose: the value is a
24
+ * label in a diagnostic surface, and a path-derived string could carry a
25
+ * username. The type is the enforcement — a path cannot be passed here without
26
+ * failing typecheck. (`privacy:scan` reads file text and cannot see a runtime
27
+ * value, so it is a backstop for the response body, not the guard.)
28
+ */
29
+ export type ReplacePublisher =
30
+ | "config"
31
+ | "prompt-journal"
32
+ | "config-ownership"
33
+ | "claude-agents"
34
+ | "lab-automation"
35
+ | "lab-ledger"
36
+ | "storage-cleanup"
37
+ | "tray";
38
+
39
+ /** The Windows error codes this module treats as a momentary hold. */
40
+ export type ReplaceRetryCode = "EBUSY" | "EPERM" | "EACCES";
41
+
42
+ export interface ReplaceRetryCounts {
43
+ /** Replaces that hit this code and were retried. */
44
+ retried: number;
45
+ /** Replaces that exhausted every retry and threw. */
46
+ exhausted: number;
47
+ }
48
+
49
+ /**
50
+ * Keyed by publisher AND code. The code is the diagnostic half: EBUSY from a
51
+ * scanner, EACCES from a permissions problem and EPERM from a lock are three
52
+ * different stories, and collapsing them would leave the counters unable to
53
+ * answer the question they exist for.
54
+ */
55
+ const counters = new Map<string, ReplaceRetryCounts>();
56
+
57
+ function counterKey(publisher: ReplacePublisher, code: ReplaceRetryCode): string {
58
+ return `${publisher}:${code}`;
59
+ }
60
+
61
+ function bump(
62
+ publisher: ReplacePublisher,
63
+ code: ReplaceRetryCode,
64
+ field: keyof ReplaceRetryCounts,
65
+ ): void {
66
+ const key = counterKey(publisher, code);
67
+ const current = counters.get(key) ?? { retried: 0, exhausted: 0 };
68
+ current[field] += 1;
69
+ counters.set(key, current);
70
+ }
71
+
72
+ /**
73
+ * Process-lifetime snapshot, keyed `publisher:CODE`. These reset on restart,
74
+ * which is fine: the question they answer is "does this ever fire at all", not
75
+ * "how often per hour".
76
+ */
77
+ export function readWindowsReplaceRetryCounters(): Record<string, ReplaceRetryCounts> {
78
+ const out: Record<string, ReplaceRetryCounts> = {};
79
+ for (const [key, counts] of counters) out[key] = { ...counts };
80
+ return out;
81
+ }
82
+
83
+ /** Test-only: the counters are module state and cases must not leak into each other. */
84
+ export function resetWindowsReplaceRetryCountersForTests(): void {
85
+ counters.clear();
86
+ }
87
+ export interface AtomicRenameIO {
88
+ platform: NodeJS.Platform;
89
+ rename: (source: string, destination: string) => void;
90
+ sleep: (milliseconds: number) => void;
91
+ }
92
+
93
+ const MAX_RETRIES = 2;
94
+
95
+ /**
96
+ * Windows sharing violations only, returning the code so the caller can record
97
+ * which one. Any other error is the caller's to see, immediately.
98
+ */
99
+ function transientWindowsReplaceCode(
100
+ platform: NodeJS.Platform,
101
+ error: unknown,
102
+ ): ReplaceRetryCode | null {
103
+ if (platform !== "win32") return null;
104
+ const code = (error as NodeJS.ErrnoException).code;
105
+ return code === "EBUSY" || code === "EPERM" || code === "EACCES" ? code : null;
106
+ }
107
+
108
+ export function renameAtomicFile(
109
+ source: string,
110
+ destination: string,
111
+ io: AtomicRenameIO = {
112
+ platform: process.platform,
113
+ rename: renameSync,
114
+ sleep: Bun.sleepSync,
115
+ },
116
+ publisher: ReplacePublisher = "config",
117
+ ): void {
118
+ for (let attempt = 0; ; attempt += 1) {
119
+ try {
120
+ io.rename(source, destination);
121
+ return;
122
+ } catch (error) {
123
+ const code = transientWindowsReplaceCode(io.platform, error);
124
+ if (!code) throw error;
125
+ if (attempt >= MAX_RETRIES) {
126
+ bump(publisher, code, "exhausted");
127
+ throw error;
128
+ }
129
+ bump(publisher, code, "retried");
130
+ io.sleep(25 * (attempt + 1));
131
+ }
132
+ }
133
+ }
134
+
135
+ export async function renameAtomicFileAsync(
136
+ source: string,
137
+ destination: string,
138
+ publisher: ReplacePublisher = "config",
139
+ ): Promise<void> {
140
+ for (let attempt = 0; ; attempt += 1) {
141
+ try {
142
+ renameSync(source, destination);
143
+ return;
144
+ } catch (error) {
145
+ const code = transientWindowsReplaceCode(process.platform, error);
146
+ if (!code) throw error;
147
+ if (attempt >= MAX_RETRIES) {
148
+ bump(publisher, code, "exhausted");
149
+ throw error;
150
+ }
151
+ bump(publisher, code, "retried");
152
+ await Bun.sleep(25 * (attempt + 1));
153
+ }
154
+ }
155
+ }
@@ -21,6 +21,13 @@ type TrustedSystemDirectoryResolver = () => string;
21
21
  type IsUserAnAdmin = () => number;
22
22
  type WindowsElevationProbe = () => boolean | null;
23
23
 
24
+ export class WindowsSystemDirectoryFfiUnavailableError extends Error {
25
+ constructor() {
26
+ super("Failed to load GetSystemDirectoryW from kernel32.dll.");
27
+ this.name = "WindowsSystemDirectoryFfiUnavailableError";
28
+ }
29
+ }
30
+
24
31
  let getSystemDirectoryWFn: GetSystemDirectoryW | null | undefined;
25
32
  let trustedSystemDirectoryResolverForTests: TrustedSystemDirectoryResolver | null = null;
26
33
  let isUserAnAdminFn: (() => boolean) | null | undefined;
@@ -114,7 +121,7 @@ export function resolveTrustedWindowsSystemDirectory(): string {
114
121
  }
115
122
  const getSystemDirectoryW = loadGetSystemDirectoryW();
116
123
  if (!getSystemDirectoryW) {
117
- throw new Error("Failed to load GetSystemDirectoryW from kernel32.dll.");
124
+ throw new WindowsSystemDirectoryFfiUnavailableError();
118
125
  }
119
126
 
120
127
  let size = 260;
@@ -168,7 +175,7 @@ export function assertTrustedSystemExecutableForTests(candidate: string, label:
168
175
  return assertTrustedSystemExecutable(candidate, label);
169
176
  }
170
177
 
171
- type ElevationExeOverrides = { powershell?: string; schtasks?: string };
178
+ type ElevationExeOverrides = { powershell?: string; schtasks?: string; taskkill?: string; icacls?: string };
172
179
  let elevationExeOverridesForTests: ElevationExeOverrides | null = null;
173
180
 
174
181
  /**
@@ -204,6 +211,24 @@ export function resolveTrustedWindowsSchtasksExe(): string {
204
211
  return assertTrustedSystemExecutable(candidate, "schtasks.exe");
205
212
  }
206
213
 
214
+ /** Absolute path to System32\\taskkill.exe from a trusted system directory. */
215
+ export function resolveTrustedWindowsTaskkillExe(): string {
216
+ if (elevationExeOverridesForTests?.taskkill) {
217
+ return elevationExeOverridesForTests.taskkill;
218
+ }
219
+ const candidate = join(resolveTrustedWindowsSystemDirectory(), "taskkill.exe");
220
+ return assertTrustedSystemExecutable(candidate, "taskkill.exe");
221
+ }
222
+
223
+ /** Absolute path to System32\\icacls.exe from a trusted system directory. */
224
+ export function resolveTrustedWindowsIcaclsExe(): string {
225
+ if (elevationExeOverridesForTests?.icacls) {
226
+ return elevationExeOverridesForTests.icacls;
227
+ }
228
+ const candidate = join(resolveTrustedWindowsSystemDirectory(), "icacls.exe");
229
+ return assertTrustedSystemExecutable(candidate, "icacls.exe");
230
+ }
231
+
207
232
  /** Stable machine-readable marker for a denied `schtasks /create`. Crosses the CLI→proxy boundary. */
208
233
  export const WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED_MARKER =
209
234
  "OCX_ERROR_CODE=WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED";
@@ -605,6 +630,49 @@ export function runWindowsElevated(file: string, args: string[]): Promise<number
605
630
  return startPowerShellCommand(script).completion.then(result => result.exitCode);
606
631
  }
607
632
 
633
+ /**
634
+ * Register one scheduled-task definition without exposing a mutable XML pathname to
635
+ * the elevated process. The XML bytes are fixed in the encoded PowerShell command
636
+ * before UAC; Register-ScheduledTask receives that string directly after elevation.
637
+ */
638
+ export function runWindowsElevatedScheduledTaskRegistration(
639
+ taskName: string,
640
+ xml: string,
641
+ ): Promise<number> {
642
+ const xmlBase64 = Buffer.from(xml, "utf16le").toString("base64");
643
+ const powerShellPath = windowsPowerShell();
644
+ const powerShellDirectory = powerShellPath.replace(/[\\/][^\\/]+$/, "");
645
+ const scheduledTasksModule = `${powerShellDirectory}\\Modules\\ScheduledTasks\\ScheduledTasks.psd1`;
646
+ const inner = [
647
+ `$taskName = ${psSingleQuote(taskName)}`,
648
+ `$xmlBase64 = ${psSingleQuote(xmlBase64)}`,
649
+ "$xml = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($xmlBase64))",
650
+ `$module = Microsoft.PowerShell.Core\\Import-Module -Name ${psSingleQuote(scheduledTasksModule)} -PassThru -Force -ErrorAction Stop`,
651
+ "$registerTask = $module.ExportedCommands['Register-ScheduledTask']",
652
+ "if ($null -eq $registerTask) { throw 'Trusted ScheduledTasks module does not export Register-ScheduledTask.' }",
653
+ "& $registerTask -TaskName $taskName -Xml $xml -Force -ErrorAction Stop | Out-Null",
654
+ ].join("; ");
655
+ const encodedCommand = Buffer.from(inner, "utf16le").toString("base64");
656
+ const script = [
657
+ `$p = Start-Process -FilePath ${psSingleQuote(powerShellPath)}`,
658
+ ` -ArgumentList ${psSingleQuote(buildWindowsElevatedArgumentList([
659
+ "-NoProfile",
660
+ "-NonInteractive",
661
+ "-ExecutionPolicy",
662
+ "Bypass",
663
+ "-EncodedCommand",
664
+ encodedCommand,
665
+ ]))}`,
666
+ " -Verb RunAs -WindowStyle Hidden -PassThru -Wait;",
667
+ `if ($null -eq $p) { exit ${OCX_ELEVATED_UAC_CANCELLED} }`,
668
+ "$null = $p.Handle;",
669
+ `if ($null -eq $p.ExitCode) { exit ${OCX_ELEVATED_PROTOCOL_FAILED} }`,
670
+ "exit $p.ExitCode",
671
+ ].join("");
672
+
673
+ return startPowerShellCommand(script).completion.then(result => result.exitCode);
674
+ }
675
+
608
676
  /**
609
677
  * Build the elevated (post-UAC) script: create → run → optional delete rollback.
610
678
  * Returns only OpenCodex protocol exit codes (never raw schtasks codes, never 1223).