@remodex/rmx 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (588) hide show
  1. package/AGENTS_INSTALL.md +91 -0
  2. package/LICENSE +21 -0
  3. package/README.md +242 -0
  4. package/assets/architecture.png +0 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/claude-code-models.gif +0 -0
  7. package/assets/codex-app-picker.png +0 -0
  8. package/bin/ocx.mjs +584 -0
  9. package/bin/package-main.mjs +9 -0
  10. package/gui/dist/assets/index-CZqebSPQ.css +1 -0
  11. package/gui/dist/assets/index-CkETtt7P.js +71 -0
  12. package/gui/dist/favicon.png +0 -0
  13. package/gui/dist/fonts/google-sans-cyrillic.woff2 +0 -0
  14. package/gui/dist/fonts/google-sans-latin.woff2 +0 -0
  15. package/gui/dist/icons.svg +24 -0
  16. package/gui/dist/index.html +25 -0
  17. package/gui/dist/logo.png +0 -0
  18. package/gui/dist/provider-icons/alibaba-color.svg +1 -0
  19. package/gui/dist/provider-icons/antigravity-color.svg +1 -0
  20. package/gui/dist/provider-icons/claude-color.svg +1 -0
  21. package/gui/dist/provider-icons/cline-color.svg +16 -0
  22. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
  23. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  24. package/gui/dist/provider-icons/copilot-color.svg +1 -0
  25. package/gui/dist/provider-icons/cursor-color.svg +2 -0
  26. package/gui/dist/provider-icons/deepseek-color.svg +1 -0
  27. package/gui/dist/provider-icons/discord.svg +1 -0
  28. package/gui/dist/provider-icons/firepass-color.svg +1 -0
  29. package/gui/dist/provider-icons/fireworks-color.svg +1 -0
  30. package/gui/dist/provider-icons/gemini-color.svg +1 -0
  31. package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
  32. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
  33. package/gui/dist/provider-icons/grok.svg +1 -0
  34. package/gui/dist/provider-icons/groq-color.svg +1 -0
  35. package/gui/dist/provider-icons/huggingface-color.svg +1 -0
  36. package/gui/dist/provider-icons/kimi-color.svg +1 -0
  37. package/gui/dist/provider-icons/kiro-color.svg +15 -0
  38. package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
  39. package/gui/dist/provider-icons/mistral-color.svg +1 -0
  40. package/gui/dist/provider-icons/moonshot-color.svg +1 -0
  41. package/gui/dist/provider-icons/nvidia-color.svg +1 -0
  42. package/gui/dist/provider-icons/ollama-color.svg +1 -0
  43. package/gui/dist/provider-icons/openai.svg +1 -0
  44. package/gui/dist/provider-icons/opencode.svg +2 -0
  45. package/gui/dist/provider-icons/openrouter-color.svg +1 -0
  46. package/gui/dist/provider-icons/pi.svg +21 -0
  47. package/gui/dist/provider-icons/qianfan-color.svg +1 -0
  48. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
  49. package/gui/dist/provider-icons/telegram.svg +1 -0
  50. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
  51. package/gui/dist/provider-icons/vllm-color.svg +1 -0
  52. package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
  53. package/package.json +118 -0
  54. package/src/AGENTS.md +28 -0
  55. package/src/adapters/anthropic-image-guard.ts +251 -0
  56. package/src/adapters/anthropic-image-normalize.ts +518 -0
  57. package/src/adapters/anthropic.ts +1205 -0
  58. package/src/adapters/azure.ts +36 -0
  59. package/src/adapters/base.ts +83 -0
  60. package/src/adapters/client-fingerprint.ts +59 -0
  61. package/src/adapters/command-code.ts +453 -0
  62. package/src/adapters/cursor/arg-codec.ts +38 -0
  63. package/src/adapters/cursor/arg-normalize.ts +104 -0
  64. package/src/adapters/cursor/cursor-errors.ts +165 -0
  65. package/src/adapters/cursor/discovery.ts +276 -0
  66. package/src/adapters/cursor/effort-map.ts +139 -0
  67. package/src/adapters/cursor/exec-policy.ts +88 -0
  68. package/src/adapters/cursor/framing.ts +250 -0
  69. package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
  70. package/src/adapters/cursor/kv-store.ts +52 -0
  71. package/src/adapters/cursor/live-models.ts +153 -0
  72. package/src/adapters/cursor/live-smoke-gate.ts +41 -0
  73. package/src/adapters/cursor/live-transport.ts +1235 -0
  74. package/src/adapters/cursor/mcp-config.ts +42 -0
  75. package/src/adapters/cursor/mcp-manager.ts +333 -0
  76. package/src/adapters/cursor/message-mapper.ts +49 -0
  77. package/src/adapters/cursor/native-exec-common.ts +59 -0
  78. package/src/adapters/cursor/native-exec-desktop.ts +184 -0
  79. package/src/adapters/cursor/native-exec-fs.ts +332 -0
  80. package/src/adapters/cursor/native-exec-mcp.ts +153 -0
  81. package/src/adapters/cursor/native-exec-network.ts +43 -0
  82. package/src/adapters/cursor/native-exec-shell.ts +548 -0
  83. package/src/adapters/cursor/native-exec-tools.ts +118 -0
  84. package/src/adapters/cursor/native-exec.ts +604 -0
  85. package/src/adapters/cursor/protobuf-events.ts +735 -0
  86. package/src/adapters/cursor/protobuf-request.ts +719 -0
  87. package/src/adapters/cursor/request-builder.ts +280 -0
  88. package/src/adapters/cursor/thread-continuity.ts +67 -0
  89. package/src/adapters/cursor/tool-definitions.ts +621 -0
  90. package/src/adapters/cursor/transport-retry.ts +132 -0
  91. package/src/adapters/cursor/transport.ts +57 -0
  92. package/src/adapters/cursor/types.ts +59 -0
  93. package/src/adapters/cursor.ts +196 -0
  94. package/src/adapters/google-antigravity-replay.ts +520 -0
  95. package/src/adapters/google-antigravity-wire.ts +140 -0
  96. package/src/adapters/google-errors.ts +85 -0
  97. package/src/adapters/google-http.ts +100 -0
  98. package/src/adapters/google-tool-schema.ts +173 -0
  99. package/src/adapters/google-truncation.ts +24 -0
  100. package/src/adapters/google-wire-compiler.ts +232 -0
  101. package/src/adapters/google.ts +859 -0
  102. package/src/adapters/identity.ts +77 -0
  103. package/src/adapters/image.ts +23 -0
  104. package/src/adapters/kiro-constants.ts +16 -0
  105. package/src/adapters/kiro-errors.ts +208 -0
  106. package/src/adapters/kiro-events.ts +197 -0
  107. package/src/adapters/kiro-images.ts +129 -0
  108. package/src/adapters/kiro-retry.ts +312 -0
  109. package/src/adapters/kiro-thinking.ts +104 -0
  110. package/src/adapters/kiro-tool-fallback.ts +36 -0
  111. package/src/adapters/kiro-tools.ts +224 -0
  112. package/src/adapters/kiro-truncation.ts +33 -0
  113. package/src/adapters/kiro-wire.ts +129 -0
  114. package/src/adapters/kiro.ts +1924 -0
  115. package/src/adapters/mimo-free.ts +263 -0
  116. package/src/adapters/openai-chat.ts +1265 -0
  117. package/src/adapters/openai-responses.ts +1309 -0
  118. package/src/adapters/run-turn-queue.ts +114 -0
  119. package/src/adapters/tool-catalog-nudge.ts +71 -0
  120. package/src/adapters/upstream-http-error.ts +48 -0
  121. package/src/android-remote/assets.ts +218 -0
  122. package/src/android-remote/attachments.ts +168 -0
  123. package/src/android-remote/auth.ts +237 -0
  124. package/src/android-remote/cloudflare-provisioning.ts +409 -0
  125. package/src/android-remote/cloudflare-secret.ts +106 -0
  126. package/src/android-remote/cloudflare-tunnel.ts +488 -0
  127. package/src/android-remote/cloudflared.ts +286 -0
  128. package/src/android-remote/codex-app-server.ts +565 -0
  129. package/src/android-remote/desktop-history-page.ts +936 -0
  130. package/src/android-remote/desktop-ipc.ts +3643 -0
  131. package/src/android-remote/desktop-ownership-store.ts +98 -0
  132. package/src/android-remote/desktop-project-registration.ts +129 -0
  133. package/src/android-remote/desktop-session-stream.ts +1110 -0
  134. package/src/android-remote/desktop-workspace-state.ts +443 -0
  135. package/src/android-remote/file-change-parser.ts +112 -0
  136. package/src/android-remote/gateway.ts +9108 -0
  137. package/src/android-remote/mutation-store.ts +299 -0
  138. package/src/android-remote/projection.ts +1780 -0
  139. package/src/android-remote/queued-turn-store.ts +248 -0
  140. package/src/android-remote/session-command-recovery.ts +1384 -0
  141. package/src/android-remote/store.ts +466 -0
  142. package/src/android-remote/thread-reconciliation.ts +133 -0
  143. package/src/android-remote/thread-source-paths.ts +307 -0
  144. package/src/android-remote/thread-stream.ts +546 -0
  145. package/src/android-remote/turn-activity.ts +235 -0
  146. package/src/android-remote/user-message-identity.ts +94 -0
  147. package/src/bridge.ts +1793 -0
  148. package/src/chat/inbound.ts +295 -0
  149. package/src/chat/outbound.ts +821 -0
  150. package/src/claude/agents-inject.ts +267 -0
  151. package/src/claude/alias.ts +149 -0
  152. package/src/claude/auth-detect.ts +229 -0
  153. package/src/claude/auth-mode-migration.ts +32 -0
  154. package/src/claude/auth-mode.ts +62 -0
  155. package/src/claude/context-windows.ts +189 -0
  156. package/src/claude/desktop-3p-guard.ts +35 -0
  157. package/src/claude/desktop-3p-paths.ts +84 -0
  158. package/src/claude/desktop-3p.ts +601 -0
  159. package/src/claude/desktop-health.ts +26 -0
  160. package/src/claude/desktop-profile.ts +263 -0
  161. package/src/claude/gateway-cache.ts +70 -0
  162. package/src/claude/inbound-debug.ts +163 -0
  163. package/src/claude/inbound.ts +519 -0
  164. package/src/claude/model-info.ts +154 -0
  165. package/src/claude/outbound.ts +898 -0
  166. package/src/cli/access.ts +108 -0
  167. package/src/cli/account-api.ts +296 -0
  168. package/src/cli/account-auth.ts +250 -0
  169. package/src/cli/account-catalog-refresh.ts +14 -0
  170. package/src/cli/account-extended.ts +476 -0
  171. package/src/cli/account-main.ts +317 -0
  172. package/src/cli/account.ts +297 -0
  173. package/src/cli/agent-driven.ts +70 -0
  174. package/src/cli/agent.ts +184 -0
  175. package/src/cli/catalog-prewarm.ts +27 -0
  176. package/src/cli/claude-desktop.ts +211 -0
  177. package/src/cli/claude.ts +302 -0
  178. package/src/cli/codex-shim-autorestore.ts +45 -0
  179. package/src/cli/codex-shim-readiness.ts +69 -0
  180. package/src/cli/combo.ts +124 -0
  181. package/src/cli/config-command.ts +183 -0
  182. package/src/cli/debug.ts +228 -0
  183. package/src/cli/desktop-first-run.ts +25 -0
  184. package/src/cli/doctor.ts +1022 -0
  185. package/src/cli/export-command.ts +201 -0
  186. package/src/cli/help.ts +370 -0
  187. package/src/cli/index.ts +1565 -0
  188. package/src/cli/init.ts +224 -0
  189. package/src/cli/integrations.ts +225 -0
  190. package/src/cli/interactive-confirm.ts +133 -0
  191. package/src/cli/internal-dispatch.ts +35 -0
  192. package/src/cli/launcher-context.ts +77 -0
  193. package/src/cli/models-runtime.ts +224 -0
  194. package/src/cli/models.ts +340 -0
  195. package/src/cli/observe.ts +170 -0
  196. package/src/cli/opencode.ts +587 -0
  197. package/src/cli/provider-runtime.ts +179 -0
  198. package/src/cli/provider.ts +476 -0
  199. package/src/cli/ready.ts +301 -0
  200. package/src/cli/route-policy.ts +92 -0
  201. package/src/cli/runtime-api.ts +328 -0
  202. package/src/cli/star-prompt.ts +211 -0
  203. package/src/cli/status-oauth.ts +78 -0
  204. package/src/cli/status.ts +321 -0
  205. package/src/cli/system-command.ts +196 -0
  206. package/src/cli/system-restart-client.ts +146 -0
  207. package/src/cli/tray-proxy.ts +205 -0
  208. package/src/cli/v2.ts +200 -0
  209. package/src/cli.ts +10 -0
  210. package/src/clients/config-export.ts +1109 -0
  211. package/src/codex/account-id.ts +34 -0
  212. package/src/codex/account-label.ts +34 -0
  213. package/src/codex/account-lifecycle.ts +172 -0
  214. package/src/codex/account-namespace-match.ts +63 -0
  215. package/src/codex/account-namespaces.ts +195 -0
  216. package/src/codex/account-pause.ts +20 -0
  217. package/src/codex/account-priority.ts +83 -0
  218. package/src/codex/account-runtime-state.ts +31 -0
  219. package/src/codex/account-store.ts +517 -0
  220. package/src/codex/account-usability.ts +40 -0
  221. package/src/codex/admission.ts +263 -0
  222. package/src/codex/app-server-processes.ts +799 -0
  223. package/src/codex/auth-api.ts +2098 -0
  224. package/src/codex/auth-collision.ts +107 -0
  225. package/src/codex/auth-context.ts +480 -0
  226. package/src/codex/autostart-health.ts +156 -0
  227. package/src/codex/catalog/account-models.ts +67 -0
  228. package/src/codex/catalog/aggregation.ts +471 -0
  229. package/src/codex/catalog/bundled.ts +533 -0
  230. package/src/codex/catalog/effort.ts +432 -0
  231. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  232. package/src/codex/catalog/kinds.ts +2 -0
  233. package/src/codex/catalog/metadata.ts +287 -0
  234. package/src/codex/catalog/native-models.ts +7 -0
  235. package/src/codex/catalog/parsing.ts +503 -0
  236. package/src/codex/catalog/provider-fetch.ts +2267 -0
  237. package/src/codex/catalog/sync.ts +1606 -0
  238. package/src/codex/catalog-admission.ts +197 -0
  239. package/src/codex/catalog-refresh-status.ts +87 -0
  240. package/src/codex/catalog-write-serialization.ts +242 -0
  241. package/src/codex/catalog.ts +15 -0
  242. package/src/codex/codex-write-lock.ts +384 -0
  243. package/src/codex/convergence-types.ts +593 -0
  244. package/src/codex/convergence.ts +580 -0
  245. package/src/codex/custom-model-catalog-migration.ts +176 -0
  246. package/src/codex/data/upstream-models.json +830 -0
  247. package/src/codex/desired-state.ts +230 -0
  248. package/src/codex/desktop-client-processes.ts +521 -0
  249. package/src/codex/exec-invocation.ts +22 -0
  250. package/src/codex/features.ts +1091 -0
  251. package/src/codex/generation.ts +202 -0
  252. package/src/codex/history-job.ts +347 -0
  253. package/src/codex/history-lock.ts +242 -0
  254. package/src/codex/history-migration-guardian.ts +115 -0
  255. package/src/codex/history-provider.ts +1075 -0
  256. package/src/codex/history-transition.ts +105 -0
  257. package/src/codex/history-worker.ts +204 -0
  258. package/src/codex/home.ts +206 -0
  259. package/src/codex/inject-coordination.ts +257 -0
  260. package/src/codex/inject.ts +1857 -0
  261. package/src/codex/injected-marker.ts +79 -0
  262. package/src/codex/integration-record.ts +266 -0
  263. package/src/codex/internal/catalog-writer.ts +203 -0
  264. package/src/codex/internal/history-writer.ts +105 -0
  265. package/src/codex/journal.ts +172 -0
  266. package/src/codex/main-account-cache.ts +56 -0
  267. package/src/codex/main-account.ts +40 -0
  268. package/src/codex/management-convergence.ts +114 -0
  269. package/src/codex/model-cache.ts +267 -0
  270. package/src/codex/native-main-admission.ts +47 -0
  271. package/src/codex/native-main-auth-temp.ts +187 -0
  272. package/src/codex/native-main-claim.ts +167 -0
  273. package/src/codex/native-main-lock-file.ts +162 -0
  274. package/src/codex/native-main-owner.ts +329 -0
  275. package/src/codex/native-profile-api.ts +247 -0
  276. package/src/codex/native-profile-manager.ts +1531 -0
  277. package/src/codex/native-profile-processes.ts +121 -0
  278. package/src/codex/native-profile-recovery.ts +99 -0
  279. package/src/codex/native-profile-stage-store.ts +387 -0
  280. package/src/codex/native-profile-startup.ts +348 -0
  281. package/src/codex/native-profile-store.ts +855 -0
  282. package/src/codex/native-profile-types.ts +120 -0
  283. package/src/codex/native-residue.ts +691 -0
  284. package/src/codex/paths.ts +78 -0
  285. package/src/codex/plugins-doctor.ts +242 -0
  286. package/src/codex/pool-rotation.ts +295 -0
  287. package/src/codex/project-config-warnings.ts +426 -0
  288. package/src/codex/prompt-journal.ts +311 -0
  289. package/src/codex/prompt-layers.ts +967 -0
  290. package/src/codex/prompt-lock.ts +143 -0
  291. package/src/codex/provider-adoption.ts +242 -0
  292. package/src/codex/quota-rejection.ts +224 -0
  293. package/src/codex/quota.ts +494 -0
  294. package/src/codex/refresh.ts +60 -0
  295. package/src/codex/routing.ts +1855 -0
  296. package/src/codex/runtime.ts +659 -0
  297. package/src/codex/shim.ts +1215 -0
  298. package/src/codex/subagent-defaults.ts +557 -0
  299. package/src/codex/subagent-model-fallback.ts +560 -0
  300. package/src/codex/sync.ts +238 -0
  301. package/src/codex/transition-state.ts +612 -0
  302. package/src/codex/upstream-host-health.ts +368 -0
  303. package/src/codex/user-identity.ts +374 -0
  304. package/src/codex/warmup.ts +192 -0
  305. package/src/codex/websocket-registry.ts +100 -0
  306. package/src/codex/write-coordination.ts +114 -0
  307. package/src/combos/failover.ts +140 -0
  308. package/src/combos/index.ts +44 -0
  309. package/src/combos/request.ts +64 -0
  310. package/src/combos/resolve.ts +232 -0
  311. package/src/combos/types.ts +392 -0
  312. package/src/config.ts +3270 -0
  313. package/src/generated/model-metadata.ts +144 -0
  314. package/src/github/star-state.ts +203 -0
  315. package/src/grok/inject.ts +540 -0
  316. package/src/grok/inspect.ts +45 -0
  317. package/src/grok/status.ts +127 -0
  318. package/src/grok/sync.ts +66 -0
  319. package/src/images/artifacts.ts +516 -0
  320. package/src/images/fulfill-video.ts +163 -0
  321. package/src/images/fulfill.ts +149 -0
  322. package/src/images/index.ts +4 -0
  323. package/src/images/loop.ts +922 -0
  324. package/src/images/plan.ts +133 -0
  325. package/src/images/synthetic-tool.ts +133 -0
  326. package/src/images/types.ts +41 -0
  327. package/src/images/xai-client.ts +141 -0
  328. package/src/images/xai-video-client.ts +163 -0
  329. package/src/index.ts +22 -0
  330. package/src/integrations/config-io.ts +151 -0
  331. package/src/integrations/journal.ts +315 -0
  332. package/src/integrations/merge.ts +135 -0
  333. package/src/integrations/native/ownership-preflight.ts +202 -0
  334. package/src/integrations/ownership.ts +111 -0
  335. package/src/integrations/registry.ts +108 -0
  336. package/src/integrations/serialize.ts +235 -0
  337. package/src/integrations/state.ts +290 -0
  338. package/src/integrations/store.ts +103 -0
  339. package/src/integrations/writer.ts +492 -0
  340. package/src/lib/abort.ts +146 -0
  341. package/src/lib/admin-secrets.ts +25 -0
  342. package/src/lib/admission.ts +83 -0
  343. package/src/lib/app-owned-memory-stores.ts +173 -0
  344. package/src/lib/app-owned-memory.ts +265 -0
  345. package/src/lib/bounded-body.ts +242 -0
  346. package/src/lib/bun-binary-validator.d.mts +3 -0
  347. package/src/lib/bun-binary-validator.mjs +18 -0
  348. package/src/lib/bun-runtime.ts +184 -0
  349. package/src/lib/bun-stream-caps.ts +127 -0
  350. package/src/lib/config-ownership.ts +438 -0
  351. package/src/lib/crash-guard.ts +344 -0
  352. package/src/lib/debug-log-buffer.ts +83 -0
  353. package/src/lib/debug-settings.ts +108 -0
  354. package/src/lib/debug.ts +31 -0
  355. package/src/lib/destination-policy.ts +316 -0
  356. package/src/lib/errors.ts +364 -0
  357. package/src/lib/eventstream-decoder.ts +253 -0
  358. package/src/lib/gcp-adc.ts +341 -0
  359. package/src/lib/injection-debug-log.ts +58 -0
  360. package/src/lib/local-management-attestation.ts +51 -0
  361. package/src/lib/open-url.ts +25 -0
  362. package/src/lib/pinned-http.ts +182 -0
  363. package/src/lib/privacy.ts +20 -0
  364. package/src/lib/process-control.ts +168 -0
  365. package/src/lib/provider-environment.ts +470 -0
  366. package/src/lib/provider-outbound.ts +203 -0
  367. package/src/lib/provider-url.ts +14 -0
  368. package/src/lib/proxy-env.ts +18 -0
  369. package/src/lib/redact.ts +510 -0
  370. package/src/lib/remodex-home.ts +616 -0
  371. package/src/lib/retry-after.ts +55 -0
  372. package/src/lib/service-secrets.ts +178 -0
  373. package/src/lib/shadow-call.ts +54 -0
  374. package/src/lib/sidecar-tracker.ts +52 -0
  375. package/src/lib/sse-decoder.ts +364 -0
  376. package/src/lib/state-store-registrations.ts +109 -0
  377. package/src/lib/state-store-sweeper.ts +184 -0
  378. package/src/lib/system-restart-contract.ts +73 -0
  379. package/src/lib/test-home-guard.ts +98 -0
  380. package/src/lib/token-estimate.ts +69 -0
  381. package/src/lib/translator-budget.ts +366 -0
  382. package/src/lib/upstream-reachability.ts +91 -0
  383. package/src/lib/upstream-retry.ts +508 -0
  384. package/src/lib/win-exec.ts +115 -0
  385. package/src/lib/win-paths.ts +68 -0
  386. package/src/lib/windows-elevation.ts +705 -0
  387. package/src/lib/windows-secret-acl.ts +817 -0
  388. package/src/lib/windows-user-principal.ts +283 -0
  389. package/src/lib/winsw.ts +402 -0
  390. package/src/model-sources.ts +73 -0
  391. package/src/oauth/anthropic-routing.ts +594 -0
  392. package/src/oauth/anthropic.ts +177 -0
  393. package/src/oauth/callback-server.ts +294 -0
  394. package/src/oauth/chatgpt.ts +150 -0
  395. package/src/oauth/command-code.ts +239 -0
  396. package/src/oauth/cursor.ts +231 -0
  397. package/src/oauth/github-copilot.ts +428 -0
  398. package/src/oauth/google-antigravity.ts +230 -0
  399. package/src/oauth/health.ts +443 -0
  400. package/src/oauth/index.ts +1280 -0
  401. package/src/oauth/key-providers.ts +128 -0
  402. package/src/oauth/kimi.ts +213 -0
  403. package/src/oauth/kiro-credentials.ts +726 -0
  404. package/src/oauth/kiro.ts +621 -0
  405. package/src/oauth/local-token-detect.ts +121 -0
  406. package/src/oauth/log.ts +48 -0
  407. package/src/oauth/login-cli.ts +163 -0
  408. package/src/oauth/pkce.ts +15 -0
  409. package/src/oauth/store.ts +655 -0
  410. package/src/oauth/token-guardian.ts +309 -0
  411. package/src/oauth/types.ts +62 -0
  412. package/src/oauth/xai.ts +241 -0
  413. package/src/providers/alibaba-region-backup.ts +75 -0
  414. package/src/providers/alibaba-region-migration.ts +156 -0
  415. package/src/providers/alibaba-region-startup.ts +36 -0
  416. package/src/providers/antigravity-models.ts +317 -0
  417. package/src/providers/api-keys.ts +140 -0
  418. package/src/providers/base-url-choices.ts +64 -0
  419. package/src/providers/codex-capacity.ts +288 -0
  420. package/src/providers/command-code-efforts.ts +85 -0
  421. package/src/providers/context-cap.ts +73 -0
  422. package/src/providers/derive.ts +451 -0
  423. package/src/providers/free-directory.ts +187 -0
  424. package/src/providers/github-copilot-transport.ts +56 -0
  425. package/src/providers/google-vertex-location.ts +14 -0
  426. package/src/providers/key-failover.ts +271 -0
  427. package/src/providers/kiro-models.ts +67 -0
  428. package/src/providers/label.ts +19 -0
  429. package/src/providers/model-discovery-limits.ts +16 -0
  430. package/src/providers/model-discovery.ts +361 -0
  431. package/src/providers/openai-sidecar.ts +235 -0
  432. package/src/providers/openai-tier-startup.ts +27 -0
  433. package/src/providers/openai-tiers.ts +301 -0
  434. package/src/providers/openai-virtual-models.ts +83 -0
  435. package/src/providers/openrouter-routing.ts +102 -0
  436. package/src/providers/provider-id-rewrite.ts +179 -0
  437. package/src/providers/quota.ts +1942 -0
  438. package/src/providers/reasoning-capabilities.ts +336 -0
  439. package/src/providers/registry.ts +2375 -0
  440. package/src/providers/slug-codec.ts +74 -0
  441. package/src/providers/xai-transport.ts +149 -0
  442. package/src/reasoning-effort.ts +243 -0
  443. package/src/responses/compaction.ts +124 -0
  444. package/src/responses/hosted-tool-policy.ts +9 -0
  445. package/src/responses/parser.ts +714 -0
  446. package/src/responses/reasoning-envelope.ts +60 -0
  447. package/src/responses/reasoning-replay-cache.ts +106 -0
  448. package/src/responses/schema.ts +159 -0
  449. package/src/responses/spill-store.ts +431 -0
  450. package/src/responses/state.ts +1039 -0
  451. package/src/responses/tool-groups.ts +19 -0
  452. package/src/router.ts +802 -0
  453. package/src/routing/analytics.ts +377 -0
  454. package/src/routing/capability.ts +205 -0
  455. package/src/routing/cost.ts +77 -0
  456. package/src/routing/evaluator.ts +444 -0
  457. package/src/routing/health.ts +401 -0
  458. package/src/routing/history/cursor.ts +43 -0
  459. package/src/routing/history/indexer.ts +605 -0
  460. package/src/routing/history/schema.ts +72 -0
  461. package/src/routing/profile-namespace.ts +15 -0
  462. package/src/routing/profile.ts +424 -0
  463. package/src/routing/quota.ts +145 -0
  464. package/src/routing/request-evidence.ts +45 -0
  465. package/src/routing/trace.ts +686 -0
  466. package/src/server/adapter-resolve.ts +83 -0
  467. package/src/server/auth-cors.ts +606 -0
  468. package/src/server/chat-completions.ts +379 -0
  469. package/src/server/claude-messages.ts +980 -0
  470. package/src/server/effort-policy.ts +251 -0
  471. package/src/server/github-copilot-responses-repair.ts +338 -0
  472. package/src/server/gui-static.ts +152 -0
  473. package/src/server/image-retry.ts +42 -0
  474. package/src/server/images.ts +485 -0
  475. package/src/server/index.ts +1633 -0
  476. package/src/server/lifecycle.ts +482 -0
  477. package/src/server/live.ts +609 -0
  478. package/src/server/management/agent-settings-routes.ts +1180 -0
  479. package/src/server/management/android-remote-routes.ts +390 -0
  480. package/src/server/management/api-access.ts +141 -0
  481. package/src/server/management/api-key-usage.ts +167 -0
  482. package/src/server/management/body.ts +35 -0
  483. package/src/server/management/combo-routes.ts +244 -0
  484. package/src/server/management/config-routes.ts +602 -0
  485. package/src/server/management/context.ts +88 -0
  486. package/src/server/management/integration-routes.ts +538 -0
  487. package/src/server/management/logs-usage-routes.ts +516 -0
  488. package/src/server/management/model-routes.ts +519 -0
  489. package/src/server/management/model-rows.ts +143 -0
  490. package/src/server/management/native-integration-routes.ts +781 -0
  491. package/src/server/management/oauth-account-routes.ts +573 -0
  492. package/src/server/management/provider-routes.ts +781 -0
  493. package/src/server/management/request-history-routes.ts +191 -0
  494. package/src/server/management/routing-analytics-routes.ts +74 -0
  495. package/src/server/management/routing-profile-routes.ts +384 -0
  496. package/src/server/management/shared.ts +277 -0
  497. package/src/server/management/sidebar-routes.ts +106 -0
  498. package/src/server/management/sync-response.ts +69 -0
  499. package/src/server/management/system-restart.ts +433 -0
  500. package/src/server/management/system-routes.ts +141 -0
  501. package/src/server/management/usage-summary-cache.ts +86 -0
  502. package/src/server/management-api.ts +269 -0
  503. package/src/server/management-auth.ts +353 -0
  504. package/src/server/memory-watchdog.ts +156 -0
  505. package/src/server/port-reclaim.ts +307 -0
  506. package/src/server/ports.ts +156 -0
  507. package/src/server/proxy-liveness.ts +326 -0
  508. package/src/server/proxy-stop.ts +92 -0
  509. package/src/server/readiness.ts +99 -0
  510. package/src/server/relay-eager.ts +353 -0
  511. package/src/server/relay.ts +1179 -0
  512. package/src/server/request-decompress.ts +132 -0
  513. package/src/server/request-log-conversation.ts +168 -0
  514. package/src/server/request-log.ts +1072 -0
  515. package/src/server/responses/collaboration.ts +409 -0
  516. package/src/server/responses/compact.ts +710 -0
  517. package/src/server/responses/core.ts +3561 -0
  518. package/src/server/responses/encrypted-payload.ts +308 -0
  519. package/src/server/responses/fetch-helpers.ts +171 -0
  520. package/src/server/responses/passthrough-error.ts +78 -0
  521. package/src/server/responses/policy-fallback.ts +152 -0
  522. package/src/server/responses/terminal-guard.ts +230 -0
  523. package/src/server/responses/upstream-error.ts +48 -0
  524. package/src/server/responses-image-gen-repair.ts +132 -0
  525. package/src/server/responses-item-id-repair.ts +272 -0
  526. package/src/server/responses-json-events.ts +52 -0
  527. package/src/server/responses-model-rewrite.ts +29 -0
  528. package/src/server/responses-snapshot-repair.ts +621 -0
  529. package/src/server/responses.ts +10 -0
  530. package/src/server/search.ts +181 -0
  531. package/src/server/sse-frame-buffer.ts +292 -0
  532. package/src/server/sse-payload-rewrite.ts +263 -0
  533. package/src/server/startup-action-control.ts +308 -0
  534. package/src/server/startup-health-cache.ts +119 -0
  535. package/src/server/system-env.ts +418 -0
  536. package/src/server/windows-tcp-drop.ts +184 -0
  537. package/src/server/windows-tray-control.ts +41 -0
  538. package/src/server/ws-bridge.ts +470 -0
  539. package/src/service-manager-probe.ts +824 -0
  540. package/src/service.ts +3011 -0
  541. package/src/stall-timeout.ts +20 -0
  542. package/src/storage/cleanup-job.ts +57 -0
  543. package/src/storage/cleanup.ts +3085 -0
  544. package/src/storage/policy-job.ts +457 -0
  545. package/src/storage/policy-scheduler.ts +40 -0
  546. package/src/storage/policy-worker.ts +59 -0
  547. package/src/storage/policy.ts +527 -0
  548. package/src/storage/restore-job.ts +299 -0
  549. package/src/storage/restore-worker.ts +58 -0
  550. package/src/storage/scanner.ts +238 -0
  551. package/src/storage/storage-mutation-coordinator.ts +139 -0
  552. package/src/storage/worker-lifecycle.ts +215 -0
  553. package/src/tray/assets/opencodex-tray-offline.ico +0 -0
  554. package/src/tray/assets/opencodex-tray-online.ico +0 -0
  555. package/src/tray/assets/opencodex-tray-warning.ico +0 -0
  556. package/src/tray/assets/opencodex-tray.png +0 -0
  557. package/src/tray/windows-tray.ps1 +364 -0
  558. package/src/tray/windows.ts +738 -0
  559. package/src/types.ts +1531 -0
  560. package/src/update/badge.ts +72 -0
  561. package/src/update/desktop-release.ts +1620 -0
  562. package/src/update/index.ts +402 -0
  563. package/src/update/job.ts +1906 -0
  564. package/src/update/notify.ts +261 -0
  565. package/src/update/npm-cache-preflight.d.mts +47 -0
  566. package/src/update/npm-cache-preflight.mjs +201 -0
  567. package/src/update/npm-invocation.d.mts +23 -0
  568. package/src/update/npm-invocation.mjs +94 -0
  569. package/src/update/tray-update-plan.d.mts +18 -0
  570. package/src/update/tray-update-plan.mjs +38 -0
  571. package/src/usage/cost.ts +0 -0
  572. package/src/usage/debug.ts +97 -0
  573. package/src/usage/expected-prices.ts +283 -0
  574. package/src/usage/log.ts +695 -0
  575. package/src/usage/summary.ts +585 -0
  576. package/src/usage/totals.ts +14 -0
  577. package/src/vision/anthropic-describe.ts +185 -0
  578. package/src/vision/describe.ts +127 -0
  579. package/src/vision/index.ts +558 -0
  580. package/src/vision/reasoning.ts +55 -0
  581. package/src/web-search/anthropic-executor.ts +189 -0
  582. package/src/web-search/executor.ts +105 -0
  583. package/src/web-search/format-result.ts +89 -0
  584. package/src/web-search/index.ts +196 -0
  585. package/src/web-search/loop.ts +791 -0
  586. package/src/web-search/parse.ts +235 -0
  587. package/src/web-search/progress-stream.ts +342 -0
  588. package/src/web-search/synthetic-tool.ts +47 -0
@@ -0,0 +1,431 @@
1
+ import {
2
+ chmodSync,
3
+ closeSync,
4
+ constants,
5
+ copyFileSync,
6
+ existsSync,
7
+ fsyncSync,
8
+ linkSync,
9
+ lstatSync,
10
+ mkdirSync,
11
+ openSync,
12
+ opendirSync,
13
+ readFileSync,
14
+ unlinkSync,
15
+ writeSync,
16
+ } from "node:fs";
17
+ import { createHash, randomBytes } from "node:crypto";
18
+ import { join } from "node:path";
19
+ import { getConfigDir } from "../config";
20
+ import { forgetEphemeralSecretPath, forgetHardenedSecretPath, hardenSecretDir, hardenSecretPath } from "../lib/windows-secret-acl";
21
+ import type { OcxProviderContinuationState } from "../types";
22
+
23
+ export const RESPONSE_SPILL_VERSION = 1;
24
+ export const RESPONSE_SPILL_DIR_NAME = "responses-state-spill";
25
+ export const RESPONSE_SPILL_ORPHAN_GRACE_MS = 15 * 60_000;
26
+ export const RESPONSE_SPILL_SCAN_MAX = 4_096;
27
+ export const RESPONSE_SPILL_CLEANUP_MAX = 512;
28
+
29
+ const RESPONSE_SPILL_PUBLISH_RETRIES = 64;
30
+ const OWNED_SPILL_NAME = /^([A-Za-z0-9._-]{1,80})\.([0-9a-f]{12})\.([0-9a-f]{24})\.(\d+)\.(\d+)\.spill\.json$/;
31
+ const OWNED_SPILL_TEMP_NAME = /^\.response-spill\.[0-9]+\.[0-9a-f]{16}\.tmp$/;
32
+
33
+ export interface ResponseSpillPayload {
34
+ version: 1;
35
+ responseId: string;
36
+ createdAt: number;
37
+ items: unknown[];
38
+ providers?: OcxProviderContinuationState;
39
+ }
40
+
41
+ export interface ResponseSpillRef {
42
+ version: 1;
43
+ fileName: string;
44
+ digest: string;
45
+ payloadBytes: number;
46
+ }
47
+
48
+ /**
49
+ * Hard ceiling for one spill payload, enforced BOTH at direct-spill admission
50
+ * (state.ts refuses to durably retain a larger candidate) and at replay read
51
+ * (below). 256 MiB keeps the replay transient under the process-wide
52
+ * APP_OWNED_WORST_CASE_PINNED_BYTES ceiling (512 MiB); without an admission
53
+ * ceiling the read ceiling would strand write-only spills on disk.
54
+ */
55
+ export const MAX_RESPONSE_SPILL_PAYLOAD_BYTES = 256 * 1024 * 1024;
56
+
57
+ let spillPayloadCapOverride: number | null = null;
58
+
59
+ /** Test-only: lower/restore the single-spill payload ceiling (null restores). */
60
+ export function setResponseSpillPayloadCapForTests(bytes: number | null): void {
61
+ spillPayloadCapOverride = bytes;
62
+ }
63
+
64
+ export function responseSpillPayloadCap(): number {
65
+ return spillPayloadCapOverride ?? MAX_RESPONSE_SPILL_PAYLOAD_BYTES;
66
+ }
67
+
68
+ export type ResponseSpillReadResult =
69
+ | { ok: true; payload: ResponseSpillPayload }
70
+ | { ok: false; reason: "missing" | "corrupt" | "too_large" };
71
+
72
+ export interface ResponseSpillCleanupResult {
73
+ scanned: number;
74
+ removed: number;
75
+ failed: number;
76
+ bytesRemoved: number;
77
+ }
78
+
79
+ export interface ResponseSpillIoForTest {
80
+ write?: (fd: number, bytes: Uint8Array) => void;
81
+ fsync?: (fd: number) => void;
82
+ /** Directory-handle fsync seam for `fsyncDirectoryBestEffort` only. */
83
+ fsyncDir?: (fd: number) => void;
84
+ /** Directory-handle open seam for `fsyncDirectoryBestEffort` only. */
85
+ openDir?: (dir: string) => number;
86
+ /** Directory-handle close seam for `fsyncDirectoryBestEffort` only. */
87
+ closeDir?: (fd: number) => void;
88
+ link?: (tempPath: string, destinationPath: string) => void;
89
+ copyFileExcl?: (tempPath: string, destinationPath: string) => void;
90
+ unlink?: (path: string) => void;
91
+ /** Orphan-GC enumeration seam: returns the next directory entry NAME or
92
+ * null for end-of-directory. Lets tests prove the scan cap binds without
93
+ * materializing thousands of real files. */
94
+ readdirEntry?: () => string | null;
95
+ record?: (event: "write" | "fsync" | "close" | "harden" | "publish" | "dir-fsync" | "stub-swap") => void;
96
+ }
97
+
98
+ let spillIoForTest: ResponseSpillIoForTest | null = null;
99
+ let spillGeneration = 0;
100
+
101
+ export function setSpillIoForTest(io: ResponseSpillIoForTest | null): void {
102
+ spillIoForTest = io;
103
+ }
104
+
105
+ function record(event: "write" | "fsync" | "close" | "harden" | "publish" | "dir-fsync" | "stub-swap"): void {
106
+ spillIoForTest?.record?.(event);
107
+ }
108
+
109
+ function fsyncDirectoryBestEffort(dir: string): void {
110
+ let fd: number | null = null;
111
+ // Record the durability attempt before opening the directory: Windows cannot
112
+ // open/fsync directory handles this way, but callers still cross this seam.
113
+ record("dir-fsync");
114
+ try {
115
+ fd = spillIoForTest?.openDir ? spillIoForTest.openDir(dir) : openSync(dir, "r");
116
+ try {
117
+ if (spillIoForTest?.fsyncDir) spillIoForTest.fsyncDir(fd);
118
+ else if (spillIoForTest?.fsync) spillIoForTest.fsync(fd);
119
+ else fsyncSync(fd);
120
+ } catch {
121
+ // Windows and some filesystems do not support fsync on directory handles.
122
+ }
123
+ } catch {
124
+ // Directory missing or unreadable — nothing further to sync.
125
+ } finally {
126
+ if (fd !== null) {
127
+ try {
128
+ if (spillIoForTest?.closeDir) spillIoForTest.closeDir(fd);
129
+ else closeSync(fd);
130
+ } catch { /* best effort */ }
131
+ }
132
+ }
133
+ }
134
+
135
+ export function noteStubSwapForTest(): void {
136
+ record("stub-swap");
137
+ }
138
+
139
+ export function responseSpillDirectory(dir = getConfigDir()): string {
140
+ return join(dir, RESPONSE_SPILL_DIR_NAME);
141
+ }
142
+
143
+ function sha256(value: string | Uint8Array): string {
144
+ return createHash("sha256").update(value).digest("hex");
145
+ }
146
+
147
+ function sanitizeResponseId(responseId: string): string {
148
+ const visible = responseId
149
+ .normalize("NFC")
150
+ .replace(/[^A-Za-z0-9._-]/g, "_")
151
+ .replace(/_+/g, "_")
152
+ .slice(0, 80);
153
+ return visible || "response";
154
+ }
155
+
156
+ function isErrno(error: unknown, code: string): boolean {
157
+ return !!error && typeof error === "object" && (error as NodeJS.ErrnoException).code === code;
158
+ }
159
+
160
+ function canUseExclusiveCopyFallback(error: unknown): boolean {
161
+ return process.platform === "win32" || ["EPERM", "EACCES", "ENOSYS", "ENOTSUP", "EOPNOTSUPP", "EXDEV"]
162
+ .some(code => isErrno(error, code));
163
+ }
164
+
165
+ function harden(path: string, mode: number): void {
166
+ try {
167
+ chmodSync(path, mode);
168
+ } catch {
169
+ if (process.platform !== "win32") throw new Error("Response spill permission hardening failed");
170
+ }
171
+ if (process.platform === "win32") {
172
+ const result = mode === 0o700
173
+ ? hardenSecretDir(path, { required: true })
174
+ : hardenSecretPath(path, { required: true });
175
+ if (!result.ok) throw new Error("Response spill permission hardening failed");
176
+ }
177
+ }
178
+
179
+ function writeAll(fd: number, bytes: Uint8Array): void {
180
+ if (spillIoForTest?.write) spillIoForTest.write(fd, bytes);
181
+ else {
182
+ let offset = 0;
183
+ while (offset < bytes.byteLength) offset += writeSync(fd, bytes, offset, bytes.byteLength - offset);
184
+ }
185
+ record("write");
186
+ }
187
+
188
+ function fsyncFile(fd: number): void {
189
+ if (spillIoForTest?.fsync) spillIoForTest.fsync(fd);
190
+ else fsyncSync(fd);
191
+ record("fsync");
192
+ }
193
+
194
+ function closeFile(fd: number): void {
195
+ closeSync(fd);
196
+ record("close");
197
+ }
198
+
199
+ function unlink(path: string, ephemeral = false): void {
200
+ try {
201
+ if (spillIoForTest?.unlink) spillIoForTest.unlink(path);
202
+ else unlinkSync(path);
203
+ // Ephemeral release only for publish temps; stable spill files keep their
204
+ // destination-keyed timeout memos (anti-restall) and drop just the
205
+ // success memo for the now-deleted file.
206
+ if (ephemeral) forgetEphemeralSecretPath(path);
207
+ else forgetHardenedSecretPath(path);
208
+ } catch (error) {
209
+ if (isErrno(error, "ENOENT")) {
210
+ if (ephemeral) forgetEphemeralSecretPath(path);
211
+ else forgetHardenedSecretPath(path);
212
+ }
213
+ throw error;
214
+ }
215
+ }
216
+
217
+ function unlinkEphemeral(path: string): void {
218
+ unlink(path, true);
219
+ }
220
+
221
+ function publishNoReplace(tempPath: string, destinationPath: string): void {
222
+ try {
223
+ if (spillIoForTest?.link) spillIoForTest.link(tempPath, destinationPath);
224
+ else linkSync(tempPath, destinationPath);
225
+ } catch (error) {
226
+ if (isErrno(error, "EEXIST")) throw error;
227
+ if (!canUseExclusiveCopyFallback(error)) throw error;
228
+ let copied = false;
229
+ try {
230
+ if (spillIoForTest?.copyFileExcl) spillIoForTest.copyFileExcl(tempPath, destinationPath);
231
+ else copyFileSync(tempPath, destinationPath, constants.COPYFILE_EXCL);
232
+ copied = true;
233
+ harden(destinationPath, 0o600);
234
+ const copyFd = openSync(destinationPath, "r");
235
+ try {
236
+ if (spillIoForTest?.fsync) spillIoForTest.fsync(copyFd);
237
+ else fsyncSync(copyFd);
238
+ } finally {
239
+ closeSync(copyFd);
240
+ }
241
+ } catch (copyError) {
242
+ if (copied) {
243
+ try { unlink(destinationPath); } catch { /* startup GC reclaims an incomplete publication */ }
244
+ }
245
+ throw copyError;
246
+ }
247
+ }
248
+ record("publish");
249
+ }
250
+
251
+ function validSpillRef(ref: ResponseSpillRef): boolean {
252
+ return ref.version === 1
253
+ && OWNED_SPILL_NAME.test(ref.fileName)
254
+ && /^[0-9a-f]{64}$/.test(ref.digest)
255
+ && Number.isSafeInteger(ref.payloadBytes)
256
+ && ref.payloadBytes >= 0;
257
+ }
258
+
259
+ function validPayload(value: unknown, responseId: string): value is ResponseSpillPayload {
260
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
261
+ const payload = value as Record<string, unknown>;
262
+ const keys = Object.keys(payload);
263
+ if (keys.some(key => !["version", "responseId", "createdAt", "items", "providers"].includes(key))) return false;
264
+ if (payload.version !== 1 || payload.responseId !== responseId) return false;
265
+ if (typeof payload.createdAt !== "number" || !Number.isFinite(payload.createdAt)) return false;
266
+ if (!Array.isArray(payload.items)) return false;
267
+ if (payload.providers !== undefined) {
268
+ if (!payload.providers || typeof payload.providers !== "object" || Array.isArray(payload.providers)) return false;
269
+ for (const providerState of Object.values(payload.providers)) {
270
+ if (!providerState || typeof providerState !== "object" || Array.isArray(providerState)) return false;
271
+ }
272
+ }
273
+ return true;
274
+ }
275
+
276
+ export function writeResponseSpillDurably(
277
+ responseId: string,
278
+ state: Omit<ResponseSpillPayload, "version" | "responseId">,
279
+ ): ResponseSpillRef {
280
+ let tempPath: string | null = null;
281
+ let fd: number | null = null;
282
+ try {
283
+ const payload: ResponseSpillPayload = {
284
+ version: 1,
285
+ responseId,
286
+ createdAt: state.createdAt,
287
+ items: state.items,
288
+ ...(state.providers ? { providers: state.providers } : {}),
289
+ };
290
+ const serialized = JSON.stringify(payload);
291
+ if (serialized === undefined) throw new Error("Response spill serialization failed");
292
+ const bytes = Buffer.from(serialized, "utf8");
293
+ const digest = sha256(bytes);
294
+ const idDigest = sha256(responseId).slice(0, 12);
295
+ const contentDigest = digest.slice(0, 24);
296
+ const dir = responseSpillDirectory();
297
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
298
+ harden(dir, 0o700);
299
+
300
+ tempPath = join(dir, `.response-spill.${process.pid}.${randomBytes(8).toString("hex")}.tmp`);
301
+ fd = openSync(tempPath, "wx", 0o600);
302
+ writeAll(fd, bytes);
303
+ fsyncFile(fd);
304
+ closeFile(fd);
305
+ fd = null;
306
+ harden(tempPath, 0o600);
307
+ record("harden");
308
+ const publishTempPath = tempPath;
309
+
310
+ for (let attempt = 0; attempt < RESPONSE_SPILL_PUBLISH_RETRIES; attempt++) {
311
+ spillGeneration += 1;
312
+ const fileName = `${sanitizeResponseId(responseId)}.${idDigest}.${contentDigest}.${spillGeneration}.${bytes.byteLength}.spill.json`;
313
+ if (!OWNED_SPILL_NAME.test(fileName)) throw new Error("Response spill name allocation failed");
314
+ const destinationPath = join(dir, fileName);
315
+ try {
316
+ publishNoReplace(publishTempPath, destinationPath);
317
+ fsyncDirectoryBestEffort(dir);
318
+ unlinkEphemeral(publishTempPath);
319
+ tempPath = null;
320
+ return { version: 1, fileName, digest, payloadBytes: bytes.byteLength };
321
+ } catch (error) {
322
+ if (isErrno(error, "EEXIST")) continue;
323
+ throw error;
324
+ }
325
+ }
326
+ throw new Error("Response spill publication retries exhausted");
327
+ } catch {
328
+ if (fd !== null) {
329
+ try { closeSync(fd); } catch { /* best effort */ }
330
+ }
331
+ if (tempPath) {
332
+ try { unlinkEphemeral(tempPath); } catch { /* best effort */ }
333
+ }
334
+ throw new Error("Response spill write failed");
335
+ }
336
+ }
337
+
338
+ export function readResponseSpill(responseId: string, ref: ResponseSpillRef): ResponseSpillReadResult {
339
+ if (!validSpillRef(ref)) return { ok: false, reason: "corrupt" };
340
+ // Refuse before any read/parse: an oversized declared payload would otherwise
341
+ // materialize an unbounded transient (readFileSync + utf8 + JSON.parse).
342
+ if (ref.payloadBytes > responseSpillPayloadCap()) return { ok: false, reason: "too_large" };
343
+ const match = OWNED_SPILL_NAME.exec(ref.fileName);
344
+ if (!match
345
+ || match[2] !== sha256(responseId).slice(0, 12)
346
+ || match[3] !== ref.digest.slice(0, 24)
347
+ || !Number.isSafeInteger(Number(match[4]))
348
+ || Number(match[4]) <= 0
349
+ || Number(match[5]) !== ref.payloadBytes) return { ok: false, reason: "corrupt" };
350
+ const path = join(responseSpillDirectory(), ref.fileName);
351
+ let bytes: Buffer;
352
+ try {
353
+ const stat = lstatSync(path);
354
+ if (!stat.isFile() || stat.isSymbolicLink()) return { ok: false, reason: "corrupt" };
355
+ if (stat.size !== ref.payloadBytes) return { ok: false, reason: "corrupt" };
356
+ bytes = readFileSync(path);
357
+ } catch (error) {
358
+ return { ok: false, reason: isErrno(error, "ENOENT") ? "missing" : "corrupt" };
359
+ }
360
+ if (bytes.byteLength !== ref.payloadBytes || sha256(bytes) !== ref.digest) return { ok: false, reason: "corrupt" };
361
+ try {
362
+ const payload = JSON.parse(bytes.toString("utf8")) as unknown;
363
+ return validPayload(payload, responseId) ? { ok: true, payload } : { ok: false, reason: "corrupt" };
364
+ } catch {
365
+ return { ok: false, reason: "corrupt" };
366
+ }
367
+ }
368
+
369
+ export function deleteResponseSpill(ref: ResponseSpillRef): void {
370
+ if (!validSpillRef(ref)) return;
371
+ const dir = responseSpillDirectory();
372
+ try {
373
+ unlink(join(dir, ref.fileName));
374
+ fsyncDirectoryBestEffort(dir);
375
+ } catch { /* best effort */ }
376
+ }
377
+
378
+ export function recoverOrphanedResponseSpills(
379
+ referencedFileNames: ReadonlySet<string>,
380
+ dir = responseSpillDirectory(),
381
+ opts?: { graceMs?: number },
382
+ ): ResponseSpillCleanupResult {
383
+ const result: ResponseSpillCleanupResult = { scanned: 0, removed: 0, failed: 0, bytesRemoved: 0 };
384
+ const graceMs = opts?.graceMs ?? RESPONSE_SPILL_ORPHAN_GRACE_MS;
385
+ // ONE loop serves both the real directory handle and the injected test seam
386
+ // (review C2-2: two duplicated loops let the test prove only its own copy).
387
+ // The reader is called strictly AFTER the scan-cap check, so entry
388
+ // SCAN_MAX+1 is never requested from either source.
389
+ let handle: ReturnType<typeof opendirSync> | null = null;
390
+ const injected = spillIoForTest?.readdirEntry;
391
+ if (!injected) {
392
+ try { handle = opendirSync(dir); } catch { return result; }
393
+ }
394
+ const nextName = (): string | null => {
395
+ if (injected) return injected();
396
+ const entry = handle!.readSync();
397
+ return entry ? entry.name : null;
398
+ };
399
+ try {
400
+ while (result.scanned < RESPONSE_SPILL_SCAN_MAX) {
401
+ const name = nextName();
402
+ if (name === null) break;
403
+ result.scanned += 1;
404
+ if (result.removed + result.failed >= RESPONSE_SPILL_CLEANUP_MAX) break;
405
+ const spillMatch = OWNED_SPILL_NAME.exec(name);
406
+ const isOwnedTemp = OWNED_SPILL_TEMP_NAME.test(name);
407
+ if ((!spillMatch && !isOwnedTemp) || referencedFileNames.has(name)) continue;
408
+ const path = join(dir, name);
409
+ let stat: ReturnType<typeof lstatSync>;
410
+ try { stat = lstatSync(path); } catch { continue; }
411
+ if (!stat.isFile() || stat.isSymbolicLink() || Date.now() - stat.mtimeMs < graceMs) continue;
412
+ try {
413
+ // Orphaned publish temps get the full ephemeral release; stable
414
+ // orphaned spills keep destination-keyed timeout memos.
415
+ if (isOwnedTemp) unlinkEphemeral(path);
416
+ else unlink(path);
417
+ result.removed += 1;
418
+ result.bytesRemoved += stat.size;
419
+ } catch {
420
+ result.failed += 1;
421
+ }
422
+ }
423
+ } finally {
424
+ try { handle?.closeSync(); } catch { /* best effort */ }
425
+ }
426
+ return result;
427
+ }
428
+
429
+ export function responseSpillExistsForTests(ref: ResponseSpillRef): boolean {
430
+ return validSpillRef(ref) && existsSync(join(responseSpillDirectory(), ref.fileName));
431
+ }