@bitkyc08/opencodex 2.84.0 → 2.87.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 (518) hide show
  1. package/README.md +0 -4
  2. package/bin/ocx.mjs +61 -3
  3. package/gui/dist/assets/App-ZfYbLHkB.js +58 -0
  4. package/gui/dist/assets/App-wT3mXqFz.css +1 -0
  5. package/gui/dist/assets/{Tray-C9TGtwad.js → Tray-DCd-A1az.js} +1 -1
  6. package/gui/dist/assets/index-CfYDMj34.js +94 -0
  7. package/gui/dist/assets/index-DPeB9HpV.css +1 -0
  8. package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
  9. package/gui/dist/index.html +2 -2
  10. package/package.json +1 -2
  11. package/src/adapters/anthropic/beta-allowlist.ts +91 -11
  12. package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
  13. package/src/adapters/anthropic/client-identity.ts +1 -1
  14. package/src/adapters/anthropic/passthrough.ts +16 -3
  15. package/src/adapters/anthropic-progress.ts +84 -0
  16. package/src/adapters/anthropic.ts +108 -21
  17. package/src/adapters/azure.ts +19 -1
  18. package/src/adapters/base.ts +2 -0
  19. package/src/adapters/claude-cli/adapter.ts +111 -23
  20. package/src/adapters/claude-cli/profiles.ts +2 -1
  21. package/src/adapters/claude-cli/stable-replay.ts +108 -0
  22. package/src/adapters/claude-cli/usage-admission.ts +230 -0
  23. package/src/adapters/client-fingerprint.ts +24 -0
  24. package/src/adapters/coding-agent/protocol.ts +1 -1
  25. package/src/adapters/coding-agent/turn.ts +40 -11
  26. package/src/adapters/cursor/arg-codec.ts +17 -5
  27. package/src/adapters/cursor/catalog.ts +8 -0
  28. package/src/adapters/cursor/effort-map.ts +3 -0
  29. package/src/adapters/cursor/freeform-args.ts +75 -0
  30. package/src/adapters/cursor/protobuf-events.ts +36 -23
  31. package/src/adapters/dsh-account.ts +97 -0
  32. package/src/adapters/google-antigravity-wire.ts +22 -1
  33. package/src/adapters/google.ts +37 -10
  34. package/src/adapters/openai-chat/messages.ts +4 -3
  35. package/src/adapters/openai-chat/passthrough.ts +8 -2
  36. package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
  37. package/src/adapters/openai-chat/response-events.ts +59 -1
  38. package/src/adapters/openai-chat.ts +8 -5
  39. package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
  40. package/src/adapters/openai-responses/passthrough.ts +57 -125
  41. package/src/adapters/openai-responses/request-strips.ts +25 -3
  42. package/src/adapters/openai-responses/transport.ts +81 -0
  43. package/src/adapters/qoder/adapter.ts +44 -3
  44. package/src/adapters/registry.ts +5 -0
  45. package/src/adapters/zed.ts +26 -1
  46. package/src/advisor/activity.ts +55 -0
  47. package/src/advisor/budget.ts +62 -0
  48. package/src/advisor/consult.ts +223 -0
  49. package/src/advisor/context.ts +238 -0
  50. package/src/advisor/disclosure.ts +20 -0
  51. package/src/advisor/evidence-tool.ts +21 -0
  52. package/src/advisor/evidence.ts +117 -0
  53. package/src/advisor/runtime.ts +353 -0
  54. package/src/advisor/settings.ts +144 -0
  55. package/src/advisor/state.ts +369 -0
  56. package/src/advisor/synthetic-tool.ts +33 -0
  57. package/src/bridge/errors.ts +24 -1
  58. package/src/bridge/internal.ts +3 -1
  59. package/src/bridge/response-json.ts +18 -3
  60. package/src/bridge/sse.ts +58 -8
  61. package/src/bridge/terminal-refusal.ts +59 -0
  62. package/src/chat/inbound.ts +30 -6
  63. package/src/chat/outbound.ts +5 -0
  64. package/src/claude/desktop-3p-library.ts +72 -7
  65. package/src/claude/desktop-3p.ts +75 -19
  66. package/src/claude/desktop-first-party.ts +1 -1
  67. package/src/claude/desktop-picker-platform.ts +4 -0
  68. package/src/claude/desktop-picker-profile.ts +11 -4
  69. package/src/claude/desktop-picker.ts +15 -8
  70. package/src/claude/desktop-profile.ts +10 -1
  71. package/src/claude/desktop-remote-store-io.ts +5 -3
  72. package/src/claude/desktop-system-proxy.ts +50 -4
  73. package/src/claude/inbound-content-options.ts +11 -0
  74. package/src/claude/inbound.ts +154 -50
  75. package/src/claude/intercept/local-ca.ts +8 -1
  76. package/src/claude/intercept/picker-ca-startup.ts +2 -1
  77. package/src/claude/intercept/picker-runtime.ts +6 -5
  78. package/src/claude/intercept/picker-trust-windows.ts +338 -0
  79. package/src/claude/intercept/picker-trust.ts +19 -0
  80. package/src/claude/intercept/port-policy.ts +30 -0
  81. package/src/claude/intercept/runtime.ts +97 -94
  82. package/src/claude/model-default-settings.ts +154 -0
  83. package/src/claude/model-info.ts +7 -7
  84. package/src/claude/outbound.ts +51 -19
  85. package/src/cli/account-api.ts +1 -0
  86. package/src/cli/account-policy-dto.ts +6 -0
  87. package/src/cli/account-policy.ts +34 -5
  88. package/src/cli/account-preservation.ts +44 -0
  89. package/src/cli/account.ts +8 -2
  90. package/src/cli/advisor.ts +165 -0
  91. package/src/cli/auxiliary-listener-preflight.ts +37 -0
  92. package/src/cli/capabilities-accounts.ts +20 -1
  93. package/src/cli/capabilities-agents-routing.ts +23 -2
  94. package/src/cli/capabilities-base.ts +39 -11
  95. package/src/cli/capabilities-observe-system.ts +63 -3
  96. package/src/cli/capabilities-provider-models.ts +11 -0
  97. package/src/cli/chatgpt-command.ts +35 -14
  98. package/src/cli/claude.ts +46 -6
  99. package/src/cli/codex-cli-update.ts +151 -41
  100. package/src/cli/codex-provider.ts +44 -0
  101. package/src/cli/combo-input.ts +10 -1
  102. package/src/cli/combo-result.ts +1 -1
  103. package/src/cli/combo.ts +2 -1
  104. package/src/cli/cross-home-owner.ts +17 -14
  105. package/src/cli/dispatch.ts +30 -5
  106. package/src/cli/doctor.ts +15 -1
  107. package/src/cli/ensure-desktop-cold-start.ts +382 -0
  108. package/src/cli/export-command.ts +1 -1
  109. package/src/cli/help.ts +4 -2
  110. package/src/cli/index.ts +14 -5
  111. package/src/cli/integration-plan-dto.ts +3 -0
  112. package/src/cli/log-history.ts +172 -0
  113. package/src/cli/models-runtime-subcommands.ts +1 -0
  114. package/src/cli/models-runtime.ts +72 -8
  115. package/src/cli/observe-stream.ts +2 -0
  116. package/src/cli/observe.ts +16 -4
  117. package/src/cli/provider.ts +27 -5
  118. package/src/cli/registry.ts +41 -9
  119. package/src/cli/restart-scope.ts +3 -1
  120. package/src/cli/runtime-api.ts +5 -3
  121. package/src/cli/system-command.ts +40 -8
  122. package/src/cli/uninstall-client-state.ts +32 -0
  123. package/src/cli/usage-compare.ts +309 -0
  124. package/src/cli/usage-model-search.ts +4 -4
  125. package/src/cli/usage-report.ts +2 -2
  126. package/src/cli/v2.ts +1 -1
  127. package/src/client/machine-listener.ts +1 -3
  128. package/src/client/runtime.ts +1 -0
  129. package/src/clients/client-path.ts +51 -0
  130. package/src/clients/config-export/contracts.ts +3 -1
  131. package/src/clients/config-export/model-metadata.ts +3 -2
  132. package/src/clients/config-export/qoder.ts +93 -0
  133. package/src/clients/config-export.ts +35 -48
  134. package/src/clients/gentle-shell-path.ts +87 -0
  135. package/src/codex/account-auto-switch.ts +2 -0
  136. package/src/codex/account-identity.ts +29 -0
  137. package/src/codex/account-label.ts +1 -1
  138. package/src/codex/account-lifecycle.ts +21 -5
  139. package/src/codex/account-preservation.ts +28 -0
  140. package/src/codex/account-use-remaining.ts +23 -0
  141. package/src/codex/app-server-processes.ts +97 -7
  142. package/src/codex/app-server-restart-service.ts +55 -4
  143. package/src/codex/auth-api/account-list.ts +4 -0
  144. package/src/codex/auth-api/account-pause-group.ts +6 -50
  145. package/src/codex/auth-api/account-preservation.ts +57 -0
  146. package/src/codex/auth-api/pool-mode-gate.ts +5 -2
  147. package/src/codex/auth-api/routes.ts +8 -0
  148. package/src/codex/auth-api/use-remaining-route.ts +36 -0
  149. package/src/codex/auth-api.ts +0 -1
  150. package/src/codex/auth-context.ts +81 -7
  151. package/src/codex/autostart-health.ts +4 -0
  152. package/src/codex/catalog/auto-review.ts +20 -3
  153. package/src/codex/catalog/build-entries.ts +19 -2
  154. package/src/codex/catalog/combo-member.ts +14 -5
  155. package/src/codex/catalog/configured-tiers.ts +50 -0
  156. package/src/codex/catalog/effort.ts +4 -1
  157. package/src/codex/catalog/gather-capture.ts +2 -0
  158. package/src/codex/catalog/metadata.ts +11 -9
  159. package/src/codex/catalog/model-hints.ts +42 -19
  160. package/src/codex/catalog/model-visibility.ts +3 -1
  161. package/src/codex/catalog/native-ultrafast.ts +106 -0
  162. package/src/codex/catalog/parsing.ts +35 -86
  163. package/src/codex/catalog/provider-models.ts +40 -34
  164. package/src/codex/catalog/retained-sync.ts +2 -0
  165. package/src/codex/catalog/routed-gather.ts +33 -16
  166. package/src/codex/catalog/vendor-context.ts +82 -0
  167. package/src/codex/cli-install-provenance.ts +105 -17
  168. package/src/codex/cli-update-plan.ts +545 -0
  169. package/src/codex/config-write-lock.ts +63 -4
  170. package/src/codex/convergence.ts +2 -0
  171. package/src/codex/desktop-app/picker-freshness.ts +96 -0
  172. package/src/codex/desktop-app/types.ts +2 -0
  173. package/src/codex/desktop-app/windows.ts +29 -16
  174. package/src/codex/desktop-app-restart.ts +25 -2
  175. package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
  176. package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
  177. package/src/codex/desktop-compatibility/connection-store.ts +87 -0
  178. package/src/codex/desktop-compatibility/installed-build.ts +73 -0
  179. package/src/codex/desktop-compatibility/json-body.ts +29 -0
  180. package/src/codex/desktop-compatibility/native-identity.ts +62 -0
  181. package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
  182. package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
  183. package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
  184. package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
  185. package/src/codex/desktop-compatibility/runtime.ts +182 -0
  186. package/src/codex/desktop-compatibility/service.ts +30 -0
  187. package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
  188. package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
  189. package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
  190. package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
  191. package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
  192. package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
  193. package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
  194. package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
  195. package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
  196. package/src/codex/desktop-compatibility/windows-key-protection.ts +165 -0
  197. package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
  198. package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
  199. package/src/codex/features.ts +85 -45
  200. package/src/codex/history-provider.ts +1 -1
  201. package/src/codex/inject/config-write-section.ts +4 -1
  202. package/src/codex/inject/history-failure.ts +27 -0
  203. package/src/codex/inject/remove.ts +18 -6
  204. package/src/codex/inject/restore.ts +24 -2
  205. package/src/codex/inject/routing-classify.ts +4 -3
  206. package/src/codex/inject.ts +18 -27
  207. package/src/codex/journal.ts +16 -1
  208. package/src/codex/main-account-hard-lock.ts +30 -1
  209. package/src/codex/main-account.ts +99 -15
  210. package/src/codex/native-main-refresh-mode.ts +217 -0
  211. package/src/codex/native-profile-startup.ts +1 -1
  212. package/src/codex/ocx-compaction-history.ts +12 -10
  213. package/src/codex/paths.ts +86 -1
  214. package/src/codex/provider-routing.ts +174 -0
  215. package/src/codex/refresh.ts +2 -2
  216. package/src/codex/routing/health-store.ts +2 -2
  217. package/src/codex/routing/pool-recovery.ts +79 -0
  218. package/src/codex/routing/selection.ts +4 -3
  219. package/src/codex/routing/thread-affinity.ts +1 -1
  220. package/src/codex/shim-diagnostics.ts +6 -0
  221. package/src/codex/shim-overlay.ts +7 -2
  222. package/src/codex/signed-in-identity.ts +43 -0
  223. package/src/codex/subagent-model-fallback.ts +1 -1
  224. package/src/codex/sync.ts +6 -1
  225. package/src/codex/user-identity.ts +16 -5
  226. package/src/codex/windows-local-appdata.ts +84 -0
  227. package/src/combos/failover.ts +6 -1
  228. package/src/combos/index.ts +1 -0
  229. package/src/combos/resolve.ts +46 -10
  230. package/src/combos/types.ts +15 -0
  231. package/src/config/diagnostics.ts +35 -7
  232. package/src/config/live-reconcile.ts +51 -2
  233. package/src/config/load-degrade.ts +23 -1
  234. package/src/config/opencode-wire-upgrade.ts +54 -0
  235. package/src/config/opencode-zen-upgrade.ts +52 -0
  236. package/src/config/owner-registry.ts +22 -17
  237. package/src/config/paths.ts +10 -3
  238. package/src/config/provider-validation.ts +45 -2
  239. package/src/config/schema/config-schema.ts +39 -0
  240. package/src/config/schema/desktop-compatibility.ts +9 -0
  241. package/src/config/schema/leaf-validators.ts +39 -9
  242. package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
  243. package/src/config/schema/native-reasoning-retention.ts +26 -0
  244. package/src/config.ts +2 -0
  245. package/src/generated/compatibility-version.json +962 -350
  246. package/src/generated/model-metadata.ts +1 -1
  247. package/src/grok/sync.ts +1 -1
  248. package/src/images/loop.ts +46 -8
  249. package/src/integrations/catalog-refresh.ts +1 -1
  250. package/src/integrations/config-io.ts +20 -2
  251. package/src/integrations/journal.ts +15 -1
  252. package/src/integrations/mutation-plan.ts +2 -0
  253. package/src/integrations/registry.ts +15 -0
  254. package/src/integrations/store.ts +3 -0
  255. package/src/integrations/writer.ts +10 -1
  256. package/src/lib/advisor-activation.ts +16 -0
  257. package/src/lib/background-work.ts +65 -0
  258. package/src/lib/bounded-file-read.ts +58 -0
  259. package/src/lib/bun-path-runtime.d.mts +10 -1
  260. package/src/lib/bun-path-runtime.mjs +62 -3
  261. package/src/lib/codex-restart-contract.ts +15 -0
  262. package/src/lib/desktop-proxy-route.ts +30 -0
  263. package/src/lib/desktop-upstream-tunnel.ts +72 -0
  264. package/src/lib/empty-tool-catalog.ts +62 -0
  265. package/src/lib/errors.ts +26 -3
  266. package/src/lib/gcp-adc.ts +96 -9
  267. package/src/lib/gcp-credential-input.ts +12 -0
  268. package/src/lib/lab-live-route-production.ts +5 -0
  269. package/src/lib/local-internal-call-capability.ts +55 -0
  270. package/src/lib/opaque-proxy-tunnel.ts +217 -0
  271. package/src/lib/package-tree-integrity.ts +2 -2
  272. package/src/lib/redact.ts +2 -2
  273. package/src/lib/socks5-fetch.ts +48 -71
  274. package/src/lib/socks5-handshake.ts +112 -0
  275. package/src/lib/translator-budget.ts +7 -1
  276. package/src/oauth/account-quota-rank.ts +69 -2
  277. package/src/oauth/anthropic-account-refusal.ts +20 -2
  278. package/src/oauth/anthropic-oauth-definitions.ts +3 -1
  279. package/src/oauth/anthropic-routing.ts +1 -21
  280. package/src/oauth/anthropic.ts +38 -7
  281. package/src/oauth/callback-server.ts +27 -1
  282. package/src/oauth/chatgpt-device.ts +1 -1
  283. package/src/oauth/chatgpt-shared.ts +83 -0
  284. package/src/oauth/chatgpt.ts +8 -72
  285. package/src/oauth/dsh.ts +203 -0
  286. package/src/oauth/generic-account-failover.ts +62 -12
  287. package/src/oauth/generic-session-affinity.ts +125 -0
  288. package/src/oauth/index.ts +188 -111
  289. package/src/oauth/kiro-device-login.ts +2 -1
  290. package/src/oauth/kiro.ts +7 -1
  291. package/src/oauth/local-token-detect.ts +21 -1
  292. package/src/oauth/login-cli-errors.ts +44 -0
  293. package/src/oauth/login-cli.ts +69 -7
  294. package/src/oauth/login-flow-state.ts +371 -23
  295. package/src/oauth/meta-muse-errors.ts +41 -0
  296. package/src/oauth/meta-muse-keychain.ts +52 -0
  297. package/src/oauth/meta-muse.ts +51 -84
  298. package/src/oauth/model-discovery-auth.ts +3 -0
  299. package/src/oauth/pool-settings-capability.ts +4 -2
  300. package/src/oauth/refresh-flight-retirement.ts +48 -0
  301. package/src/oauth/session-affinity-key.ts +23 -0
  302. package/src/oauth/store-anthropic-instance.ts +9 -2
  303. package/src/oauth/store.ts +12 -5
  304. package/src/protocols/codecs/chat-ir.ts +467 -0
  305. package/src/protocols/codecs/chat.ts +10 -1
  306. package/src/protocols/codecs/messages-ir.ts +500 -0
  307. package/src/protocols/encoders/adapter-events.ts +5 -4
  308. package/src/protocols/encoders/messages.ts +9 -6
  309. package/src/protocols/features.ts +10 -2
  310. package/src/protocols/path.ts +11 -1
  311. package/src/protocols/plan-snapshot.ts +39 -16
  312. package/src/protocols/plan.ts +9 -1
  313. package/src/protocols/provider-summary.ts +2 -1
  314. package/src/protocols/settings.ts +8 -0
  315. package/src/providers/antigravity-account-roster.ts +102 -0
  316. package/src/providers/antigravity-models.ts +85 -7
  317. package/src/providers/api-key-cooldowns.ts +49 -0
  318. package/src/providers/api-key-resolve.ts +11 -6
  319. package/src/providers/api-key-response-ownership.ts +57 -0
  320. package/src/providers/api-key-selection.ts +31 -6
  321. package/src/providers/api-keys.ts +214 -3
  322. package/src/providers/claude-cli-usage.ts +96 -0
  323. package/src/providers/dsh-account-destination.ts +31 -0
  324. package/src/providers/dsh-account-models.ts +41 -0
  325. package/src/providers/gcp-credential-cleanup.ts +59 -0
  326. package/src/providers/key-failover.ts +54 -50
  327. package/src/providers/key-store.ts +7 -2
  328. package/src/providers/model-rename-fields.ts +5 -0
  329. package/src/providers/model-rename-migration.ts +7 -5
  330. package/src/providers/openai-tiers-destination.ts +11 -2
  331. package/src/providers/openai-tiers.ts +5 -12
  332. package/src/providers/opencode-go-transport.ts +3 -1
  333. package/src/providers/quota/account-cache.ts +6 -5
  334. package/src/providers/quota/account-row.ts +34 -0
  335. package/src/providers/quota/antigravity.ts +28 -7
  336. package/src/providers/quota/dsh-account.ts +70 -0
  337. package/src/providers/quota/vendor-probes-oauth.ts +1 -93
  338. package/src/providers/quota-types.ts +2 -0
  339. package/src/providers/quota.ts +14 -20
  340. package/src/providers/registry/entries-core.ts +44 -14
  341. package/src/providers/registry/entries-extended.ts +19 -0
  342. package/src/providers/registry/types.ts +1 -1
  343. package/src/providers/request-pacing.ts +77 -19
  344. package/src/providers/routed-config.ts +339 -0
  345. package/src/providers/service-tier.ts +2 -1
  346. package/src/quota/window-mapping.ts +5 -2
  347. package/src/responses/anthropic-progress.ts +34 -0
  348. package/src/responses/assistant-replay.ts +35 -0
  349. package/src/responses/compaction-history-framing.ts +65 -0
  350. package/src/responses/compaction.ts +6 -1
  351. package/src/responses/incomplete-reason.ts +31 -0
  352. package/src/responses/parser.ts +78 -22
  353. package/src/responses/reasoning-envelope.ts +4 -0
  354. package/src/responses/reasoning-replay-cache.ts +24 -2
  355. package/src/responses/reasoning-retention.ts +173 -0
  356. package/src/responses/retained-compaction.ts +46 -0
  357. package/src/responses/schema.ts +23 -0
  358. package/src/responses/state.ts +32 -29
  359. package/src/router.ts +8 -321
  360. package/src/routing/health.ts +37 -22
  361. package/src/routing/history/build-file.ts +92 -0
  362. package/src/routing/history/indexer.ts +254 -221
  363. package/src/routing/history/ingest.ts +242 -0
  364. package/src/routing/history/rebuild-job.ts +111 -0
  365. package/src/routing/history/rebuild-protocol.ts +15 -0
  366. package/src/routing/history/rebuild-worker.ts +34 -0
  367. package/src/routing/profile.ts +1 -1
  368. package/src/server/admission-model-scope.ts +21 -1
  369. package/src/server/antigravity-direct-eligibility.ts +208 -0
  370. package/src/server/auth-cors.ts +15 -0
  371. package/src/server/chat-completions.ts +82 -10
  372. package/src/server/chat-native-reasoning.ts +123 -0
  373. package/src/server/chat-native-sse.ts +110 -7
  374. package/src/server/chat-native.ts +76 -17
  375. package/src/server/claude-messages.ts +164 -433
  376. package/src/server/effort-policy.ts +1 -1
  377. package/src/server/images.ts +15 -2
  378. package/src/server/index/desktop-compatibility-startup.ts +56 -0
  379. package/src/server/index/model-alias-id.ts +36 -0
  380. package/src/server/index/optional-listeners.ts +2 -0
  381. package/src/server/index/serve-options.ts +11 -3
  382. package/src/server/index/startup-warnings.ts +11 -0
  383. package/src/server/index/websocket-handler.ts +9 -0
  384. package/src/server/index.ts +14 -11
  385. package/src/server/inference/advisor-diagnostics.ts +31 -0
  386. package/src/server/inference/client-encoder-delivery.ts +6 -5
  387. package/src/server/login-shell-path.ts +146 -0
  388. package/src/server/management/advisor-routes.ts +202 -0
  389. package/src/server/management/agent-settings-routes.ts +22 -4
  390. package/src/server/management/claude-desktop-picker-routes.ts +1 -1
  391. package/src/server/management/combo-routes.ts +176 -50
  392. package/src/server/management/companion-routes.ts +4 -0
  393. package/src/server/management/config-routes.ts +77 -6
  394. package/src/server/management/context.ts +4 -0
  395. package/src/server/management/cursor-integration-routes.ts +1 -1
  396. package/src/server/management/desktop-compatibility-routes.ts +46 -0
  397. package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
  398. package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
  399. package/src/server/management/model-routes.ts +13 -8
  400. package/src/server/management/model-rows.ts +14 -0
  401. package/src/server/management/native-integration-routes.ts +2 -7
  402. package/src/server/management/native-reasoning-retention-routes.ts +41 -0
  403. package/src/server/management/oauth-account-routes.ts +87 -14
  404. package/src/server/management/protocol-settings-patch.ts +1 -0
  405. package/src/server/management/provider-gcp-credential-routes.ts +29 -0
  406. package/src/server/management/provider-key-pacing-routes.ts +31 -0
  407. package/src/server/management/provider-overwrite-carry.ts +7 -0
  408. package/src/server/management/provider-routes.ts +25 -30
  409. package/src/server/management/route-registry.ts +21 -1
  410. package/src/server/management/sibling-guard.ts +1 -0
  411. package/src/server/management-api.ts +9 -1
  412. package/src/server/messages-antigravity-direct.ts +195 -0
  413. package/src/server/messages-native-tool-guard.ts +185 -0
  414. package/src/server/messages-native.ts +106 -22
  415. package/src/server/messages-shared.ts +431 -0
  416. package/src/server/relay.ts +1 -1
  417. package/src/server/request-log-terminal-status.ts +1 -1
  418. package/src/server/request-log.ts +13 -1
  419. package/src/server/responses/account-change-state.ts +8 -2
  420. package/src/server/responses/adapter-continuation.ts +48 -19
  421. package/src/server/responses/adapter-delivery.ts +43 -8
  422. package/src/server/responses/adapter-dispatch.ts +48 -4
  423. package/src/server/responses/advisor-plan-slot.ts +27 -0
  424. package/src/server/responses/advisor-retention.ts +117 -0
  425. package/src/server/responses/advisor-slot.ts +368 -0
  426. package/src/server/responses/code-mode-collab-surface.ts +116 -8
  427. package/src/server/responses/codex-ws-wire.ts +15 -8
  428. package/src/server/responses/compact.ts +303 -125
  429. package/src/server/responses/compaction-recovery.ts +18 -10
  430. package/src/server/responses/compaction-retention.ts +61 -0
  431. package/src/server/responses/context-overflow.ts +11 -6
  432. package/src/server/responses/core-auth.ts +6 -0
  433. package/src/server/responses/core-codex-account.ts +11 -1
  434. package/src/server/responses/core-combo-failure.ts +12 -1
  435. package/src/server/responses/core-combo.ts +141 -36
  436. package/src/server/responses/core-errors.ts +3 -3
  437. package/src/server/responses/core-lifetime.ts +7 -2
  438. package/src/server/responses/core-normalize.ts +11 -0
  439. package/src/server/responses/core-opaque-recovery.ts +35 -0
  440. package/src/server/responses/core-options.ts +51 -0
  441. package/src/server/responses/core-pipeline.ts +141 -0
  442. package/src/server/responses/core-replay.ts +25 -6
  443. package/src/server/responses/core.ts +3 -116
  444. package/src/server/responses/fetch-helpers.ts +73 -19
  445. package/src/server/responses/main-hard-lock-routing.ts +109 -0
  446. package/src/server/responses/native-response-control.ts +4 -2
  447. package/src/server/responses/passthrough-delivery.ts +19 -11
  448. package/src/server/responses/passthrough-dispatch.ts +29 -16
  449. package/src/server/responses/request-prepare.ts +144 -17
  450. package/src/server/responses/request-sidecar-auth.ts +2 -2
  451. package/src/server/responses/request-transport.ts +120 -27
  452. package/src/server/responses/response-effects.ts +1 -0
  453. package/src/server/responses/run-turn-execution.ts +166 -38
  454. package/src/server/responses/sidecar-execution.ts +98 -5
  455. package/src/server/responses/terminal-guard.ts +4 -17
  456. package/src/server/responses-undeclared-tool-guard.ts +30 -9
  457. package/src/server/startup-action-control.ts +16 -2
  458. package/src/server/startup-health-cache.ts +1 -0
  459. package/src/service/auth-preflight.ts +71 -0
  460. package/src/service/cli.ts +4 -2
  461. package/src/service/definition.ts +97 -0
  462. package/src/service/desktop-startup.ts +14 -0
  463. package/src/service/guards.ts +1 -61
  464. package/src/service/health-sentinel-linux.ts +112 -0
  465. package/src/service/health-sentinel-main.ts +99 -61
  466. package/src/service/health-sentinel-probe.ts +24 -7
  467. package/src/service/health.ts +5 -89
  468. package/src/service/launchd.ts +27 -7
  469. package/src/service/logging.ts +135 -0
  470. package/src/service/repair.ts +2 -1
  471. package/src/service/state.ts +7 -8
  472. package/src/service/systemd.ts +1 -1
  473. package/src/service/windows-guarded-manager.ts +2 -1
  474. package/src/service/windows-ops.ts +1 -1
  475. package/src/service/windows-scheduler.ts +2 -7
  476. package/src/service/windows-taskxml.ts +2 -2
  477. package/src/service/windows-wscript.ts +14 -0
  478. package/src/service.ts +4 -2
  479. package/src/sidecar/auth.ts +2 -0
  480. package/src/tray/windows-tray.ps1 +43 -24
  481. package/src/types/config.ts +124 -6
  482. package/src/types/opencode-go.ts +61 -0
  483. package/src/types/opencode-zen.ts +29 -0
  484. package/src/types/provider.ts +39 -6
  485. package/src/types/request.ts +38 -2
  486. package/src/types/tools.ts +2 -0
  487. package/src/types/wire.ts +29 -37
  488. package/src/types.ts +1 -0
  489. package/src/update/async-check.ts +1 -1
  490. package/src/update/badge.ts +2 -2
  491. package/src/update/index.ts +88 -15
  492. package/src/update/job-supervised-restart.ts +107 -0
  493. package/src/update/job.ts +32 -32
  494. package/src/update/notify.ts +1 -1
  495. package/src/update/npm-invocation.mjs +12 -2
  496. package/src/update/refresh-scheduler.ts +2 -2
  497. package/src/update/supervised-recovery.d.mts +27 -0
  498. package/src/update/supervised-recovery.mjs +157 -0
  499. package/src/usage/cache-diagnostic.ts +2 -0
  500. package/src/usage/cost.ts +48 -12
  501. package/src/usage/expected-prices.ts +15 -0
  502. package/src/usage/ledger-retention.ts +2 -1
  503. package/src/usage/ledger-scanner.ts +6 -5
  504. package/src/usage/log.ts +17 -8
  505. package/src/usage/summary.ts +22 -9
  506. package/src/usage/user-cost-overlays.ts +14 -19
  507. package/src/usage/user-cost-validation.ts +38 -0
  508. package/src/vision/eligibility.ts +13 -0
  509. package/src/web-search/gemini-executor.ts +2 -2
  510. package/src/web-search/loop.ts +55 -5
  511. package/src/web-search/progress-stream.ts +4 -1
  512. package/src/web-search/run-turn-loop.ts +9 -1
  513. package/assets/sponsors/packycode.png +0 -0
  514. package/gui/dist/assets/App-CSJvmjNs.css +0 -1
  515. package/gui/dist/assets/App-CZtqiGcw.js +0 -56
  516. package/gui/dist/assets/index-BB6uK6tU.css +0 -1
  517. package/gui/dist/assets/index-CTm4l-HQ.js +0 -94
  518. package/gui/dist/assets/quota-summary-BbEeRfPY.js +0 -1
@@ -18,8 +18,10 @@
18
18
  *
19
19
  * Display-time estimation only — these rows never affect billing.
20
20
  */
21
- import type { OcxConfig, OcxProviderConfig, ProviderCostOverlay } from "../types";
22
- import { MAX_COST4_RATE, type ExpectedPriceOverlay } from "./expected-prices";
21
+ import type { OcxConfig, OcxProviderConfig, ProviderCostOverlay, PromptLengthPricing } from "../types";
22
+ import type { ExpectedPriceOverlay } from "./expected-prices";
23
+ import { validCost4 } from "./user-cost-validation";
24
+ export { COST4_RATE_KEYS, isValidCost4Rate, validCost4, isValidPromptLengthPricing } from "./user-cost-validation";
23
25
  import { redactSecretString } from "../lib/redact";
24
26
  import { isSelectableCodexPoolAccount, MAIN_CODEX_ACCOUNT_ID } from "../codex/account-id";
25
27
  import { codexAccountLogLabel } from "../codex/account-label";
@@ -236,22 +238,13 @@ export function resetPreservedDiskOnlyProvidersForTests(): void {
236
238
  preservationOwnerStates.clear();
237
239
  }
238
240
 
239
- /** Exact four fields accepted for a user-configured cost tuple. */
240
- export const COST4_RATE_KEYS = ["input", "output", "cacheRead", "cacheWrite"] as const;
241
-
242
- /** Shared per-rate predicate used by config validation, display sanitization, and runtime lifting. */
243
- export function isValidCost4Rate(rate: unknown): rate is number {
244
- return typeof rate === "number"
245
- && Number.isFinite(rate)
246
- && rate >= 0
247
- && rate <= MAX_COST4_RATE;
248
- }
249
-
250
- /** True when `value` is a complete cost entry: all four rates are non-negative finite numbers. */
251
- function validCost4(value: unknown): value is ProviderCostOverlay {
252
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
253
- const entry = value as Record<string, unknown>;
254
- return COST4_RATE_KEYS.every(key => isValidCost4Rate(entry[key]));
241
+ /** Copy a validated override; automatic is represented by absence. */
242
+ export function copyPromptLengthPricing(policy: PromptLengthPricing): PromptLengthPricing | undefined {
243
+ if (policy.mode === "automatic") return undefined;
244
+ if (policy.mode !== "custom") return { mode: policy.mode };
245
+ const { input, output, cacheRead, cacheWrite } = policy.rates;
246
+ return { mode: "custom", thresholdTokens: policy.thresholdTokens, comparison: policy.comparison,
247
+ rates: { input, output, cacheRead, cacheWrite } };
255
248
  }
256
249
 
257
250
  /**
@@ -273,10 +266,11 @@ export function refreshUserCostOverlays(config: OcxConfig): void {
273
266
  if (!costs || typeof costs !== "object" || Array.isArray(costs)) continue;
274
267
  for (const [modelId, cost4] of Object.entries(costs)) {
275
268
  if (!modelId.trim() || !validCost4(cost4)) continue;
269
+ const policy = cost4.promptLengthPricing && copyPromptLengthPricing(cost4.promptLengthPricing);
276
270
  rows.push({
277
271
  provider: providerName,
278
272
  modelId,
279
- // Copy ONLY the four validated rate fields: a hand-edited row may
273
+ // Copy only validated rates and policy: a hand-edited row may
280
274
  // carry extra properties (e.g. a misplaced apiKey) that must never
281
275
  // reach display estimates or /api/logs through the registry.
282
276
  cost4: {
@@ -285,6 +279,7 @@ export function refreshUserCostOverlays(config: OcxConfig): void {
285
279
  cacheRead: cost4.cacheRead,
286
280
  cacheWrite: cost4.cacheWrite,
287
281
  },
282
+ ...(policy ? { promptLengthPricing: policy } : {}),
288
283
  // Display provenance only: redact token-shaped provider/model ids so
289
284
  // the source string can never echo a pasted key. Matching still uses
290
285
  // the raw fields, and the change-detection signature below MUST keep
@@ -0,0 +1,38 @@
1
+ /** Pure model-cost validation shared by config, migration, and display lifting. */
2
+ import type { ProviderCostOverlay, PromptLengthPricing } from "../types";
3
+ import { MAX_COST4_RATE } from "./expected-prices";
4
+
5
+ /** Exact four fields accepted for a user-configured cost tuple. */
6
+ export const COST4_RATE_KEYS = ["input", "output", "cacheRead", "cacheWrite"] as const;
7
+
8
+ /** Shared per-rate predicate used by config validation, display sanitization, and runtime lifting. */
9
+ export function isValidCost4Rate(rate: unknown): rate is number {
10
+ return typeof rate === "number"
11
+ && Number.isFinite(rate)
12
+ && rate >= 0
13
+ && rate <= MAX_COST4_RATE;
14
+ }
15
+
16
+ /** Complete base rates with a valid optional policy; extra top-level fields are never copied. */
17
+ export function validCost4(value: unknown): value is ProviderCostOverlay {
18
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
19
+ const entry = value as Record<string, unknown>;
20
+ return COST4_RATE_KEYS.every(key => Object.hasOwn(entry, key) && isValidCost4Rate(entry[key]))
21
+ && (!Object.hasOwn(entry, "promptLengthPricing") || isValidPromptLengthPricing(entry.promptLengthPricing));
22
+ }
23
+
24
+ /** Strict nested policy boundary; diagnostics never echo untrusted policy fields. */
25
+ export function isValidPromptLengthPricing(value: unknown): value is PromptLengthPricing {
26
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
27
+ const policy = value as Record<string, unknown>;
28
+ if (!Object.hasOwn(policy, "mode")) return false;
29
+ if (policy.mode === "automatic" || policy.mode === "flat") return Object.keys(policy).length === 1;
30
+ if (policy.mode !== "custom" || Object.keys(policy).length !== 4
31
+ || !["mode", "thresholdTokens", "comparison", "rates"].every(key => Object.hasOwn(policy, key))
32
+ || typeof policy.thresholdTokens !== "number" || !Number.isSafeInteger(policy.thresholdTokens)
33
+ || policy.thresholdTokens <= 0 || (policy.comparison !== "gt" && policy.comparison !== "gte")) return false;
34
+ const rates = policy.rates;
35
+ return !!rates && typeof rates === "object" && !Array.isArray(rates)
36
+ && Object.keys(rates).length === COST4_RATE_KEYS.length
37
+ && COST4_RATE_KEYS.every(key => Object.hasOwn(rates, key) && isValidCost4Rate((rates as Record<string, unknown>)[key]));
38
+ }
@@ -226,6 +226,19 @@ export function isVisionSidecarConsumer(config: VisionCapabilityConfig, provider
226
226
  return isVisionSidecarConsumerWithCache(config, providerName, modelId, new Map());
227
227
  }
228
228
 
229
+ /**
230
+ * `isVisionSidecarConsumer` for many rows of one config. Rows of the same provider share one
231
+ * registry enrichment instead of rebuilding it per row, which matters for `/api/models`
232
+ * listings with thousands of rows. Bind it to a single config snapshot; it does not observe
233
+ * provider changes made after the first lookup.
234
+ */
235
+ export function visionSidecarConsumerClassifier(
236
+ config: VisionCapabilityConfig,
237
+ ): (providerName: string, modelId: string) => boolean {
238
+ const cache: EnrichedProviderCache = new Map();
239
+ return (providerName, modelId) => isVisionSidecarConsumerWithCache(config, providerName, modelId, cache);
240
+ }
241
+
229
242
  /**
230
243
  * Can this model accept an image on the wire? Sources are consulted in descending
231
244
  * trustworthiness; the first that speaks wins. `undefined` means nothing knows,
@@ -15,7 +15,7 @@ import { cancelBodyOnAbort, signalWithTimeout } from "../lib/abort";
15
15
  import { readBoundedResponseBytes } from "../lib/bounded-body";
16
16
  import { sidecarEnter } from "../lib/sidecar-tracker";
17
17
  import { redactSecretString } from "../lib/redact";
18
- import { ANTIGRAVITY_REQUEST_UA } from "../adapters/google-antigravity-wire";
18
+ import { antigravityUserAgentForWireModel } from "../adapters/google-antigravity-wire";
19
19
  import { resolveAntigravityEffortWireModel } from "../providers/antigravity-models";
20
20
  import { getProviderRegistryEntry } from "../providers/registry";
21
21
  import { MAX_SIDECAR_RESPONSE_BYTES, type WebSearchSource } from "./parse";
@@ -74,7 +74,7 @@ export async function runGeminiWebSearch(
74
74
  headers: {
75
75
  "Content-Type": "application/json",
76
76
  "Authorization": `Bearer ${token}`,
77
- "User-Agent": ANTIGRAVITY_REQUEST_UA,
77
+ "User-Agent": antigravityUserAgentForWireModel(wireModelId),
78
78
  },
79
79
  body: JSON.stringify(envelope),
80
80
  signal: linkedSignal.signal,
@@ -6,6 +6,9 @@ import { cloneProviderOpaqueToolCallMetadata } from "../responses/provider-opaqu
6
6
  import type { AttemptRecoveryKind } from "../usage/log";
7
7
  import { isTruncatedStopReason } from "../responses/truncated-stop-reason";
8
8
  import { bridgeToResponsesSSE } from "../bridge";
9
+ import { isTerminalRefusalIncomplete } from "../bridge/terminal-refusal";
10
+ import { guardEmptyToolCatalogStream, type ClientToolCatalogRestriction } from "../lib/empty-tool-catalog";
11
+ import { mergeUsage } from "../server/responses/empty-completion-guard";
9
12
  import { runWebSearch, type SidecarOutcome, type SidecarOutcomeRecorder, type SidecarSettings } from "./executor";
10
13
  import { runAnthropicWebSearch } from "./anthropic-executor";
11
14
  import { runXaiWebSearch, type XaiSearchOptions } from "./xai-executor";
@@ -143,6 +146,18 @@ async function* replay(events: AdapterEvent[]): AsyncGenerator<AdapterEvent> {
143
146
  for (const e of events) yield e;
144
147
  }
145
148
 
149
+ function terminalUsage(events: AdapterEvent[]): OcxUsage | undefined {
150
+ for (const e of events) if (e.type === "done" || e.type === "incomplete") return e.usage;
151
+ return undefined;
152
+ }
153
+
154
+ /** Hidden iterations add counters only: their checkpoint and raw wire usage are not the final terminal's. */
155
+ function countersOnly(usage: OcxUsage | undefined): OcxUsage | undefined {
156
+ if (!usage) return undefined;
157
+ const { contextTotalTokens: _context, rawUsage: _raw, ...counters } = usage;
158
+ return counters;
159
+ }
160
+
146
161
  /**
147
162
  * Collect the thinking block that preceded a web_search call in this iteration's events, so the
148
163
  * replayed assistant turn can carry it. Anthropic extended thinking REQUIRES the assistant
@@ -189,7 +204,11 @@ function extractIterationThinking(events: AdapterEvent[]): OcxThinkingContent[]
189
204
  };
190
205
 
191
206
  for (const e of events) {
192
- if (e.type === "thinking_delta") {
207
+ if (e.type === "text_delta" && e.anthropicThinkingSignature) {
208
+ flushVisible();
209
+ flushRaw();
210
+ parts.push({ type: "thinking", thinking: e.text, signature: e.anthropicThinkingSignature });
211
+ } else if (e.type === "thinking_delta") {
193
212
  flushRaw();
194
213
  thinking += e.thinking;
195
214
  } else if (e.type === "reasoning_raw_delta") {
@@ -272,6 +291,7 @@ class LoopError extends Error {
272
291
  */
273
292
  export interface WebSearchLoopDeps {
274
293
  parsed: OcxParsedRequest;
294
+ clientToolCatalogRestriction?: ClientToolCatalogRestriction;
275
295
  adapter: ProviderAdapter;
276
296
  incomingMeta: IncomingMeta;
277
297
  /**
@@ -297,6 +317,8 @@ export interface WebSearchLoopDeps {
297
317
  settings: SidecarSettings;
298
318
  maxSearches: number;
299
319
  forceEmptyResponseId?: boolean;
320
+ /** Responses inbound only: deliver a non-retryable upstream refusal as `response.failed`. */
321
+ terminalRefusalAsFailed?: boolean;
300
322
  abortSignal?: AbortSignal;
301
323
  recordSidecarOutcome?: SidecarOutcomeRecorder;
302
324
  beforeSidecarDispatch?: () => void;
@@ -320,6 +342,12 @@ export interface WebSearchLoopDeps {
320
342
  onFirstOutput?: () => void;
321
343
  /** Raw adapter usage at the terminal event, pre wire-normalization (see bridgeToResponsesSSE onUsage). */
322
344
  onUsage?: (usage: OcxUsage | undefined) => void;
345
+ /**
346
+ * After an `on429` rotation, offered the usage of the hidden iterations the left credential
347
+ * served. Returning true means it was billed to that credential's attempt, so `onUsage` will not
348
+ * report it again.
349
+ */
350
+ onRotatedUsage?: (usage: OcxUsage) => boolean;
323
351
  /** Observe the exact adapter request selected for each routed-model iteration. */
324
352
  onRequestBuilt?: (request: AdapterRequest) => void;
325
353
  /** Request-scoped executor retains the core's selection binding across loop retries. */
@@ -407,6 +435,12 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
407
435
  // One iteration beyond the forced answer is reserved for its empty-answer recovery below.
408
436
  const HARD_CAP = maxSearches + 3;
409
437
  let emptyAnswerRetries = 0;
438
+ // Every iteration re-sends the full context, but only the last one's terminal reaches the bridge.
439
+ // Search-calling passes and the discarded empty forced answer are summed here and added to what
440
+ // onUsage reports, so the request log meters what was actually sent. The client-facing terminal
441
+ // keeps the last iteration's usage: Codex and Claude Code size the context window from it, and
442
+ // a sum would read as a context several times larger and trigger early auto-compaction.
443
+ let hiddenUsage: OcxUsage | undefined;
410
444
  let accountRefusalOutputStarted = false;
411
445
  const connectTimeoutMs = deps.connectTimeoutMs ?? 200_000;
412
446
  const routedModelStallTimeoutMs = deps.routedModelStallTimeoutMs ?? 200_000;
@@ -586,6 +620,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
586
620
  const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers,
587
621
  iterParsed, prepared.response);
588
622
  if (!rotated) break;
623
+ if (hiddenUsage && deps.onRotatedUsage?.(hiddenUsage)) hiddenUsage = undefined;
589
624
  const retainRefusal = prepared.response.status === 403
590
625
  && deps.incomingMeta?.providerName === "google-antigravity" && rotated.recoveryKind === "oauth-account-403";
591
626
  // Keep the bounded refusal readable until the sibling actually returns headers.
@@ -681,6 +716,8 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
681
716
  signal,
682
717
  inactivityTimeoutMs: routedModelStallTimeoutMs,
683
718
  translatorBudget,
719
+ // A pass that fails was still billed; the error path reports it with the hidden passes.
720
+ onErrorUsage: usage => { hiddenUsage = mergeUsage(hiddenUsage, usage); },
684
721
  })) {
685
722
  if (event.type === "heartbeat") yield event;
686
723
  // Kiro's explicit-completion protocol marks ordinary assistant text as commentary while
@@ -689,7 +726,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
689
726
  // Codex show only `Working` until both Kiro attempts had finished (often 30-40 seconds).
690
727
  // Tool events remain buffered below, so the decision to invoke the hosted sidecar is still
691
728
  // atomic and no search call can escape before its stream has validated successfully.
692
- else if (event.type === "text_delta" && event.phase === "commentary") { accountRefusalOutputStarted = true; yield event; }
729
+ else if (event.type === "text_delta" && event.phase === "commentary" && !event.anthropicThinkingSignature) { accountRefusalOutputStarted = true; yield event; }
693
730
  else if (liveWindowOpen && LIVE_STREAMABLE.has(event.type)) {
694
731
  // Live events are ALSO buffered: the scanner still needs them for thinking extraction
695
732
  // and the forced-answer output check; only the terminal replay skips them (by count).
@@ -914,7 +951,10 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
914
951
  // limit-reached result instead of failing the turn (#6464: stripping
915
952
  // the declaration makes some models emit the raw call as visible
916
953
  // text).
917
- const shouldLoop = split.calls.length > 0 && !split.hasRealToolCall;
954
+ // A refused iteration ends the turn even when it carried a search call: running the
955
+ // search would send the refused context back to the model (#7148).
956
+ const shouldLoop = split.calls.length > 0 && !split.hasRealToolCall
957
+ && !split.passthrough.some(isTerminalRefusalIncomplete);
918
958
  if (!shouldLoop) {
919
959
  // #1001: a forced-answer pass that ends `done` must have produced
920
960
  // usable output — never a malformed tool call, and never silence.
@@ -944,6 +984,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
944
984
  stopReason: terminalEvent.stopReason,
945
985
  eventTypes: [...new Set(split.passthrough.map(event => event.type))],
946
986
  }));
987
+ hiddenUsage = mergeUsage(hiddenUsage, terminalEvent.usage);
947
988
  if (!split.hasMalformedToolCall && !split.hasRealToolCall && emptyAnswerRetries === 0) {
948
989
  emptyAnswerRetries++;
949
990
  console.warn("[web-search-loop] empty forced answer — retrying once without tools");
@@ -966,13 +1007,19 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
966
1007
  yield* replay(split.passthrough.slice(split.streamedPassthroughCount));
967
1008
  return;
968
1009
  }
1010
+ hiddenUsage = mergeUsage(hiddenUsage, terminalUsage(split.passthrough));
969
1011
  // The thinking that led to the search belongs to the FIRST call's assistant replay turn.
1012
+ for (const event of split.passthrough.slice(split.streamedPassthroughCount)) {
1013
+ if (event.type === "text_delta" && event.anthropicThinkingSignature) yield event;
1014
+ }
970
1015
  const iterationThinking = extractIterationThinking(split.passthrough);
971
1016
  for (const [callIndex, call] of split.calls.entries()) {
972
1017
  accountRefusalOutputStarted = true;
973
1018
  yield* runSearchCall(call, callIndex === 0 ? iterationThinking : []);
974
1019
  }
975
1020
  } catch (e) {
1021
+ // The bridge reports no usage for either error; the iterations already sent still count.
1022
+ if (hiddenUsage) deps.onUsage?.(hiddenUsage);
976
1023
  if (isTranslatorBudgetExceededError(e)) {
977
1024
  yield {
978
1025
  type: "error",
@@ -987,6 +1034,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
987
1034
  return;
988
1035
  }
989
1036
  }
1037
+ if (hiddenUsage) deps.onUsage?.(hiddenUsage);
990
1038
  yield { type: "error", message: "web-search loop exceeded its iteration cap" };
991
1039
  } finally {
992
1040
  if (abortSignal) abortSignal.removeEventListener("abort", linkAbort);
@@ -994,7 +1042,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
994
1042
  }
995
1043
 
996
1044
  const sse = bridgeToResponsesSSE(
997
- produce(), parsed._responseModelId ?? parsed.modelId, toolNsMap, freeform, toolSearch, () => {
1045
+ guardEmptyToolCatalogStream(produce(), deps.clientToolCatalogRestriction, translatorBudget), parsed._responseModelId ?? parsed.modelId, toolNsMap, freeform, toolSearch, () => {
998
1046
  const elapsed = Date.now() - loopT0;
999
1047
  if (executedSearchCount > 0 || searchesExecuted > 0) {
1000
1048
  console.warn(`[web-search-loop] cancelled — ${executedSearchCount} real searches, ${searchesExecuted - executedSearchCount} placeholders, ${elapsed}ms`);
@@ -1004,12 +1052,14 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
1004
1052
  {
1005
1053
  translatorBudget,
1006
1054
  replayCacheScope: parsed._reasoningReplayScope,
1055
+ ...(deps.clientToolCatalogRestriction ? { ...deps.clientToolCatalogRestriction, enforceDeclaredToolNames: false } : {}),
1007
1056
  ...(deps.forceEmptyResponseId ? { responseId: "" } : {}),
1057
+ ...(deps.terminalRefusalAsFailed ? { terminalRefusalAsFailed: true } : {}),
1008
1058
  hideThinkingSummary: parsed.options.hideThinkingSummary,
1009
1059
  hideRawReasoning: parsed.options.hideRawReasoning,
1010
1060
  ...(deps.stallTimeoutSec !== undefined ? { stallTimeoutSec: deps.stallTimeoutSec } : {}),
1011
1061
  ...(deps.onFirstOutput ? { onFirstOutput: deps.onFirstOutput } : {}),
1012
- ...(deps.onUsage ? { onUsage: deps.onUsage } : {}),
1062
+ ...(deps.onUsage ? { onUsage: (usage: OcxUsage | undefined) => deps.onUsage?.(mergeUsage(countersOnly(hiddenUsage), usage)) } : {}),
1013
1063
  ...(deps.onCompletedResponse ? { onCompletedResponse: deps.onCompletedResponse } : {}),
1014
1064
  },
1015
1065
  );
@@ -1,5 +1,5 @@
1
1
  import type { ProviderAdapter } from "../adapters/base";
2
- import type { AdapterEvent } from "../types";
2
+ import type { AdapterEvent, OcxUsage } from "../types";
3
3
  import {
4
4
  isTranslatorBudgetExceededError,
5
5
  TRANSLATOR_MAX_TURN_BYTES,
@@ -32,6 +32,8 @@ export interface ParseStreamWithProgressOptions {
32
32
  signal?: AbortSignal;
33
33
  /** Kept configurable for focused tests; production callers should use the 5 second default. */
34
34
  postTerminalDrainTimeoutMs?: number;
35
+ /** Receives the usage an adapter `error` event carries before the stream fails on it. */
36
+ onErrorUsage?: (usage: OcxUsage) => void;
35
37
  }
36
38
 
37
39
  type ParseStream = ProviderAdapter["parseStream"];
@@ -296,6 +298,7 @@ export async function* parseStreamWithProgress(
296
298
  );
297
299
  }
298
300
  if (event.type === "error") {
301
+ if (event.usage) options.onErrorUsage?.(event.usage);
299
302
  fail(event.code === "translation_buffer_limit"
300
303
  ? new TranslatorBudgetExceededError("live_transient", TRANSLATOR_MAX_TURN_BYTES)
301
304
  : new Error(event.message));
@@ -124,7 +124,12 @@ function extractIterationContent(events: AdapterEvent[]): OcxAssistantContentPar
124
124
  rawReasoning = "";
125
125
  };
126
126
  for (const e of events) {
127
- if (e.type === "thinking_delta") {
127
+ if (e.type === "text_delta" && e.anthropicThinkingSignature) {
128
+ flushText();
129
+ flushVisible();
130
+ flushRaw();
131
+ parts.push({ type: "thinking", thinking: e.text, signature: e.anthropicThinkingSignature });
132
+ } else if (e.type === "thinking_delta") {
128
133
  flushText();
129
134
  flushRaw();
130
135
  thinking += e.thinking;
@@ -533,6 +538,9 @@ export async function* runTurnWebSearchLoop(
533
538
  return;
534
539
  }
535
540
 
541
+ for (const event of replayEvents) {
542
+ if (event.type === "text_delta" && event.anthropicThinkingSignature) yield event;
543
+ }
536
544
  const iterationContent = extractIterationContent(split.passthrough);
537
545
  for (const [callIndex, call] of split.calls.entries()) {
538
546
  if (abortSignal?.aborted) { yield abortEvent(); return; }
Binary file
@@ -1 +0,0 @@
1
- .codex-account-controls{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:12px;min-width:0;display:flex}.codex-account-auto-switch{flex:none;align-items:center;gap:7px;min-width:0;display:flex}.codex-account-auto-switch-label{font-size:var(--text-label);color:var(--muted);font-weight:var(--weight-medium);white-space:nowrap}.codex-account-auto-switch-input-wrap{align-items:stretch;display:inline-flex;position:relative}.codex-account-auto-switch-input{width:76px;height:30px;font-size:var(--text-label);text-align:right;padding:4px 38px 4px 8px}.codex-account-auto-switch-unit{color:var(--muted);font-size:var(--text-label);pointer-events:none;position:absolute;top:50%;right:25px;transform:translateY(-50%)}.codex-account-auto-switch-input-wrap .ocx-stepper{width:22px}.codex-account-auto-switch-toggle{transform-origin:50%;margin-inline:-3px;transform:scale(.8)}.quota-row--codex-credits .quota-val{white-space:nowrap}.quota-compact:has(>.quota-row--codex-credits){grid-template-columns:minmax(34px,max-content) max-content minmax(34px,max-content) minmax(38px,max-content) minmax(58px,1fr) minmax(30px,max-content);column-gap:8px}.quota-compact:has(>.quota-row--codex-credits)>*{grid-column:1/-1}.quota-compact:has(>.quota-row--codex-credits)>.quota-row:not(.quota-row--credits){grid-template-columns:subgrid}.codex-auth-credits-toggle{align-items:center;gap:var(--space-2);white-space:nowrap;display:inline-flex}.codex-auth-credits-toggle__label{font-size:var(--text-label);color:var(--muted)}.codex-credit-spend{align-items:center;gap:var(--space-2);white-space:nowrap;display:inline-flex}.codex-credit-spend .toggle[aria-pressed=mixed] .toggle-knob{transform:translate(8px,-50%)}.codex-account-credits{align-items:center;gap:7px;display:inline-flex}.codex-account-credits__label{font-size:var(--text-label);color:var(--muted);font-weight:var(--weight-medium);white-space:nowrap}.codex-account-credits__toggle{transform-origin:50%;margin-inline:-3px;transform:scale(.8)}.anthropic-pool-card__native-messages{align-items:flex-start;gap:var(--space-2);margin-top:var(--space-3);cursor:pointer;display:flex}.anthropic-pool-card__native-messages>input{margin:var(--space-0-5) 0 0;accent-color:var(--accent)}.anthropic-pool-card__native-messages>input:focus-visible{outline:var(--space-0-5) solid var(--accent);outline-offset:var(--space-0-5)}.anthropic-pool-card__native-messages-copy{gap:var(--space-1);max-width:var(--prose-measure);display:grid}.anthropic-pool-card__native-messages:has(input:disabled){cursor:default}.anthropic-pool-card__save-warning{margin:var(--space-2) var(--space-4);color:var(--amber);font-size:var(--text-label);line-height:var(--leading-body)}.kiro-device-dialog{max-width:var(--prose-measure);border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--surface);width:100%;color:var(--text);padding:var(--space-6)}.kiro-device-dialog::backdrop{background:var(--glass-rail)}.kiro-login-choices{gap:var(--space-2);margin-block:var(--space-4);display:grid}.kiro-login-choices .btn{min-height:var(--control-touch);justify-content:flex-start}.kiro-device-code{align-items:center;gap:var(--space-3);margin-block:var(--space-4);flex-wrap:wrap;display:flex}.kiro-device-code-value{font-family:var(--font-code);font-size:var(--text-title);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide)}.kiro-device-destination{overflow-wrap:anywhere;-webkit-user-select:all;user-select:all}.kiro-device-actions{justify-content:flex-end;gap:var(--space-2);margin-top:var(--space-4);display:flex}.logs-stack-end.logs-decode-rate{white-space:normal;display:flex}.logs-decode-rate>span:first-child{white-space:nowrap}.logs-decode-rate .text-caption{font-family:var(--font-ui);overflow-wrap:break-word}.log-detail-grid.log-detail-performance-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.log-detail-performance-grid>span{overflow-wrap:break-word;min-width:0}.logs-decode-basis-hint{grid-column:1/-1}.main-inner:has(.claude-page .claudecode-workspace-shell){max-width:980px}.claude-panel-lead{color:var(--muted);font-size:var(--text-label);line-height:var(--leading-body);max-width:var(--prose-measure);margin:0}.claudecode-connection-card{margin-bottom:var(--space-4)}.claudecode-workspace-shell .setting-controls>.custom-select{text-align:right}.claude-page .claude-status-bar{z-index:8;box-shadow:var(--shadow-sm);flex-wrap:wrap;padding:6px 8px 6px 14px;position:sticky;top:12px}.claude-page .claude-status-bar>.claude-status-health{margin-left:0}.claude-status-actions{align-items:center;gap:var(--space-3);margin-left:auto;display:flex}.claude-desktop-roles{margin-bottom:var(--space-4);overflow:hidden}.claude-desktop-section-title{font-size:var(--text-control);font-weight:var(--weight-semibold);line-height:var(--leading-ui);color:var(--text);align-items:baseline;gap:8px;margin:0;display:flex}.claude-desktop-section-title .count{color:var(--muted);font-weight:var(--weight-medium);font-family:var(--font-code);font-size:var(--text-label)}.claude-desktop-roles>.claude-desktop-section-title{padding:12px 16px 4px}.claude-desktop-roles-scope{color:var(--muted);font-size:var(--text-label);margin:0;padding:0 16px 8px}.claude-desktop-roles .setting-controls>.custom-select{text-align:right}.claude-desktop-roles .setting-label{flex:12rem}.claude-desktop-roles .setting-controls{flex:0 auto;min-width:0;max-width:min(60%,28rem)}.claude-desktop-roles .setting-controls>.custom-select,.claude-desktop-roles .select-trigger{max-width:100%}.claude-desktop-roles .select-trigger>span:first-child{text-overflow:ellipsis;min-width:0;overflow:hidden}.claude-desktop-roles .select-trigger>svg{flex:none}.claude-role-option{vertical-align:bottom;min-width:0;max-width:100%;display:inline-flex}.claude-role-option__route{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.claude-role-option__status{white-space:pre;color:var(--muted);flex:none}@media (width<=760px){.claude-desktop-roles .setting-controls{max-width:100%}.claude-desktop-roles .setting-controls>.custom-select,.claude-desktop-roles .select-trigger{width:100%}.claude-desktop-roles .select-trigger{justify-content:space-between}}.claude-desktop-list{gap:var(--space-2);margin-bottom:var(--space-4);flex-direction:column;display:flex}.claude-desktop-list-head{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;display:flex}.claude-desktop-list-hint{color:var(--muted);font-size:var(--text-label);margin:0}.claude-desktop-list-rows{margin:0;padding:0;list-style:none;overflow:hidden}.claude-desktop-list-row{border-top:1px solid var(--border-soft,var(--border));align-items:center;gap:10px;min-width:0;padding:8px 14px;display:flex}.claude-desktop-list-row:first-child{border-top:none}.claude-desktop-list-names{flex:1;align-items:baseline;gap:8px;min-width:0;display:flex}.claude-desktop-list-names strong,.claude-desktop-list-names code{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.claude-desktop-list-names strong{font-weight:var(--weight-medium)}.claude-desktop-list-names code{color:var(--muted);font-size:11px}.claude-row-quick{color:var(--accent)}.claude-desktop-advanced{margin-bottom:var(--space-4);border:1px solid var(--border);border-radius:var(--radius);background:var(--surface)}.claude-desktop-advanced>summary{cursor:pointer;flex-direction:column;gap:8px;padding:12px 14px;list-style:none;display:flex}.claude-desktop-advanced>summary::-webkit-details-marker{display:none}.claude-desktop-advanced-title{font-weight:var(--weight-semibold);color:var(--text);align-items:center;gap:6px;display:flex}.claude-desktop-advanced-chips{flex-wrap:wrap;gap:6px;display:flex}.claude-desktop-advanced-chip{border-radius:var(--radius-pill);background:var(--raised);max-width:100%;color:var(--muted);font-size:var(--text-label);align-items:baseline;gap:6px;padding:2px 10px;display:inline-flex}.claude-desktop-advanced-chip b{color:var(--text);font-weight:var(--weight-medium)}.claude-desktop-advanced-chip code{text-overflow:ellipsis;white-space:nowrap;max-width:220px;font-size:11px;overflow:hidden}.claude-desktop-advanced-count{font-family:var(--font-code);color:var(--faint);font-size:11px}.claude-desktop-advanced-desc{color:var(--muted);font-size:var(--text-label);margin:0;padding:0 14px 10px}.claude-desktop-advanced>.ocx-group-stack{padding:0 10px 10px}.remote-link-page{gap:var(--space-4);display:grid}.remote-link-page .page-head{align-items:flex-start}.remote-link-page .page-sub{max-width:var(--prose-measure);margin:0}.remote-link-panel{gap:var(--space-4);display:grid}.remote-link-off-preview{min-height:260px;position:relative;overflow:hidden}.remote-link-off-preview:after{content:"";pointer-events:none;-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);background:color-mix(in srgb, var(--bg) 24%, transparent);position:absolute;inset:0}.remote-link-preview-content{gap:var(--space-3);filter:grayscale();opacity:.64;-webkit-user-select:none;user-select:none;display:grid}.remote-link-preview-row{justify-content:space-between;align-items:center;gap:var(--space-3);padding:var(--space-3);border:1px solid var(--border-soft);border-radius:var(--radius-sm);background:var(--raised);display:flex}.remote-link-preview-lines{gap:var(--space-2);width:min(55%,300px);display:grid}.remote-link-preview-lines span{border-radius:var(--radius-pill);background:var(--border);height:8px;display:block}.remote-link-switch-row{z-index:1;justify-content:space-between;align-items:center;gap:var(--space-4);display:flex;position:relative}.remote-link-switch{width:var(--control-touch);height:var(--control-md);border:1px solid var(--border);border-radius:var(--radius-pill);background:var(--toggle-off-bg);cursor:pointer;padding:3px}.remote-link-switch:after{content:"";background:var(--toggle-dot-color);width:24px;height:24px;transition:transform var(--motion-fast) ease;border-radius:50%;display:block}.remote-link-switch[aria-checked=true]{background:var(--toggle-on-bg)}.remote-link-switch[aria-checked=true]:after{transform:translate(16px)}.remote-link-switch:focus-visible,.remote-link-role-card:focus-visible,.remote-link-sheet button:focus-visible,.remote-link-sheet a:focus-visible,.remote-link-row button:focus-visible{outline:2px solid var(--accent-ring);outline-offset:2px}.remote-link-role-grid{gap:var(--space-3);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.remote-link-role-card{min-height:132px;padding:var(--space-4);border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--raised);color:var(--text);text-align:left;cursor:pointer}.remote-link-role-card:hover{background:var(--raised-hover)}.remote-link-role-card[aria-disabled=true]{color:var(--muted);cursor:not-allowed;opacity:.72}.remote-link-role-card[aria-disabled=true]:hover{background:var(--raised)}.remote-link-role-card[aria-checked=true]{border-color:var(--text);box-shadow:0 0 0 1px var(--text)}.remote-link-role-card strong,.remote-link-role-card span{display:block}.remote-link-role-card span{margin-top:var(--space-2);color:var(--muted);font-size:var(--text-label)}.remote-link-toolbar{justify-content:space-between;align-items:center;gap:var(--space-3);display:flex}.remote-link-status{align-items:center;gap:var(--space-2);color:var(--muted);font-size:var(--text-label);display:inline-flex}.remote-link-status:before{content:"";background:var(--faint);border-radius:50%;flex:none;width:8px;height:8px}.remote-link-status[data-state=connected]:before{background:var(--green)}.remote-link-status[data-state=reconnecting]:before{background:var(--amber)}.remote-link-status[data-state=failed]:before{background:var(--red)}.remote-link-status[data-state=connecting]:before{background:var(--amber)}.remote-link-children{gap:var(--space-2);display:grid}.remote-link-row{gap:var(--space-3);padding:var(--space-3);border:1px solid var(--border-soft);border-radius:var(--radius-sm);background:var(--raised);grid-template-columns:minmax(0,1fr) auto;align-items:center;display:grid}.remote-link-row-main{gap:var(--space-2);min-width:0;display:grid}.remote-link-row-meta{gap:var(--space-3);color:var(--muted);font-size:var(--text-caption);flex-wrap:wrap;display:flex}.remote-link-row .btn{min-height:var(--control-touch)}.remote-link-error{color:var(--red)}.remote-link-hint{margin-top:var(--space-1);color:var(--muted);font-size:var(--text-label);overflow-wrap:anywhere;display:block}.remote-link-info{color:var(--muted);font-size:var(--text-label)}.remote-link-restart{gap:var(--space-2);padding:var(--space-4);border:1px solid var(--border-soft);border-radius:var(--radius-sm);background:var(--raised);display:grid}.remote-link-restart p{color:var(--muted);margin:0}.remote-link-fingerprint{padding:var(--space-3);overflow-wrap:anywhere;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--raised);font-family:var(--font-code);font-size:var(--text-label);margin:0}.remote-link-workspace-card{justify-content:space-between;align-items:center;gap:var(--space-4);display:flex}.remote-link-workspace-card p{margin:var(--space-1) 0 0;color:var(--muted);font-size:var(--text-label)}.remote-link-sheet{width:min(620px, calc(100vw - var(--space-4)));max-height:min(760px, calc(100dvh - var(--space-4)));padding:var(--space-6);border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--bg);color:var(--text);box-shadow:var(--shadow);margin:auto;overflow:auto}.remote-link-sheet::backdrop{background:color-mix(in srgb, var(--text) 24%, transparent)}.remote-link-sheet-head{justify-content:space-between;align-items:flex-start;gap:var(--space-3);display:flex}.remote-link-sheet-head h3{font-size:var(--text-title);margin:0}.remote-link-sheet-body{gap:var(--space-4);margin-top:var(--space-5);display:grid}.remote-link-sheet pre{white-space:pre-wrap;overflow-wrap:anywhere;font-family:var(--font-code);font-size:var(--text-label)}.remote-link-candidates{gap:var(--space-2);display:grid}.remote-link-candidate{justify-content:space-between;align-items:center;gap:var(--space-3);min-height:var(--control-touch);padding:var(--space-2) var(--space-3);border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--raised);color:var(--text);cursor:pointer;text-align:left;display:flex}.remote-link-candidate:hover{background:var(--raised-hover)}.remote-link-form{gap:var(--space-2);display:grid}.remote-link-form .field-label{margin-bottom:0}.remote-link-sheet-actions{justify-content:flex-end;gap:var(--space-2);flex-wrap:wrap;display:flex}.remote-link-confirm-dialog{width:min(460px, calc(100vw - var(--space-4)));padding:var(--space-5);border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);color:var(--text);box-shadow:var(--shadow);margin:auto}.remote-link-confirm-dialog::backdrop{background:color-mix(in srgb, var(--text) 24%, transparent)}.remote-link-confirm-dialog h3{font-size:var(--text-subtitle);margin:0}.remote-link-confirm-dialog p{color:var(--muted)}@media (width<=620px){.remote-link-role-grid{grid-template-columns:1fr}.remote-link-workspace-card,.remote-link-switch-row{flex-direction:column;align-items:stretch}.remote-link-switch{align-self:flex-start}.remote-link-sheet{width:calc(100vw - var(--space-2));border-radius:var(--radius-lg) var(--radius-lg) 0 0;max-height:88dvh;margin:auto auto 0}}@media (prefers-reduced-motion:reduce){.remote-link-page *,.remote-link-page :before,.remote-link-page :after{scroll-behavior:auto!important;transition:none!important;animation:none!important}}.quota-summary-bar{z-index:var(--z-sticky);border-bottom:1px solid var(--border);background:var(--bg);min-height:34px;font-size:var(--text-label);align-items:center;gap:12px;padding:4px 16px;display:flex;position:sticky;top:0}.quota-summary-scroller{flex:1;align-items:center;gap:2px;min-width:0;display:flex}.quota-summary-list{overscroll-behavior-x:contain;scrollbar-width:none;flex-wrap:nowrap;flex:auto;align-items:center;gap:4px;min-width:0;margin:0;padding:3px;list-style:none;display:flex;overflow:auto hidden}.quota-summary-list::-webkit-scrollbar{display:none}.quota-summary-item{flex:none}.quota-summary-scroll{border:1px solid var(--border);background:var(--surface);width:22px;height:22px;color:var(--text);font:inherit;cursor:pointer;border-radius:999px;flex:none;place-items:center;padding:0;font-weight:600;line-height:1;display:inline-grid}.quota-summary-scroll:hover:not([aria-disabled=true]){background:var(--bg)}.quota-summary-scroll[aria-disabled=true]{color:var(--muted);opacity:.45;cursor:default}.quota-summary-scroll:focus-visible{outline:2px solid var(--text);outline-offset:1px}.quota-summary-chip{color:var(--text);font:inherit;white-space:nowrap;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:999px;align-items:center;gap:6px;padding:3px 8px;text-decoration:none;display:inline-flex}.quota-summary-chip:hover,.quota-summary-chip--open{border-color:var(--border)}.quota-summary-chip:focus-visible{outline:2px solid var(--text);outline-offset:1px}.quota-summary-name{color:var(--muted)}.quota-summary-pct{font-variant-numeric:tabular-nums;font-weight:600}.quota-summary-flag{width:14px;height:14px;color:var(--bg);font-size:var(--text-micro);border-radius:50%;place-items:center;font-weight:700;display:inline-grid}.quota-summary-item--warn .quota-summary-chip{background:var(--amber-soft)}.quota-summary-item--warn .quota-summary-pct{color:var(--amber)}.quota-summary-item--warn .quota-summary-flag{background:var(--amber)}.quota-summary-item--critical .quota-summary-chip{border-color:var(--red);background:var(--red-soft)}.quota-summary-item--critical .quota-summary-pct{color:var(--red)}.quota-summary-item--critical .quota-summary-flag{background:var(--red)}.quota-summary-popover{top:calc(var(--qs-pop-top,0) * 1px);left:calc(var(--qs-pop-left,0) * 1px);z-index:var(--z-popover);border:1px solid var(--border);background:var(--surface);border-radius:8px;min-width:260px;max-width:calc(100% - 16px);padding:10px 12px;position:fixed;box-shadow:0 8px 24px #00000024}.quota-summary-popover:before{content:"";left:calc(var(--qs-bridge-left,0) * 1px - 1px);right:calc(var(--qs-bridge-right,0) * 1px - 1px);height:calc((var(--qs-pop-gap,4) + 1) * 1px);position:absolute;bottom:100%}.quota-summary-popover-head{justify-content:space-between;align-items:center;gap:12px;margin-bottom:6px;display:flex}.quota-summary-badge{font-size:var(--text-caption);border-radius:999px;padding:1px 6px}.quota-summary-badge--warn{color:var(--amber);background:var(--amber-soft)}.quota-summary-badge--critical{color:var(--red);background:var(--red-soft)}.quota-summary-table{border-collapse:collapse;width:100%}.quota-summary-table th,.quota-summary-table td{text-align:left;white-space:nowrap;padding:3px 0;font-weight:400}.quota-summary-table td.quota-summary-row-pct{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap;padding:3px 12px;font-weight:600}.quota-summary-row-reset{color:var(--muted);white-space:nowrap}.quota-summary-table th[scope=row]{white-space:normal;overflow-wrap:anywhere}.quota-summary-row--warn .quota-summary-row-pct{color:var(--amber)}.quota-summary-row--critical .quota-summary-row-pct{color:var(--red)}.quota-summary-popover-foot{color:var(--muted);font-size:var(--text-caption);flex-wrap:wrap;justify-content:space-between;gap:4px 12px;margin-top:6px;display:flex}.quota-summary-updated{color:var(--muted);font-size:var(--text-caption);white-space:nowrap;flex-shrink:0}.quota-summary-popover-link{color:var(--text);text-underline-offset:2px;text-decoration:underline}.quota-summary-popover-link:focus-visible{outline:2px solid var(--text);outline-offset:1px}.quota-summary-updated--stale{color:var(--amber)}.main:has(>.quota-summary-bar):has(>.main-inner--combos .combos-workspace-shell){flex-direction:column;height:100dvh;display:flex}.main:has(>.quota-summary-bar)>.main-inner.main-inner--combos:has(.combos-workspace-shell){flex:auto;height:auto;min-height:0}@media (width<=760px){.quota-summary-bar{padding:4px 10px;position:static}.quota-summary-popover{top:calc(var(--qs-pop-top,0) * 1px);min-width:0;left:8px;right:8px}.main:has(>.quota-summary-bar):has(>.main-inner--combos .combos-workspace-shell){height:100%;min-height:0}}.section-switcher{border:1px solid var(--border);border-radius:var(--radius-pill);background:var(--surface);flex-wrap:wrap;gap:2px;max-width:100%;margin:0 0 16px;padding:2px;display:inline-flex}.section-switcher-btn{appearance:none;color:var(--muted);border-radius:var(--radius-pill);cursor:pointer;font:inherit;font-size:var(--text-control);white-space:nowrap;background:0 0;border:none;align-items:center;padding:5px 14px;display:inline-flex}.section-switcher-btn:hover{color:var(--text)}.section-switcher-btn[aria-current=page]{background:var(--raised);color:var(--text);font-weight:var(--weight-semibold)}.section-switcher-btn:focus-visible{outline:2px solid var(--accent-ring);outline-offset:1px}@media (width<=760px){.section-switcher{display:flex}.section-switcher-btn{min-height:var(--control-touch);flex:auto;justify-content:center}}.app{--titlebar-h:40px;--tl-inset:80px;--chrome-clear:52px}.app--macos{--chrome-clear:124px;--sidebar-column:max(232px, calc(var(--tl-inset) + 52px))}.sidebar-top{z-index:calc(var(--z-overlay,30) + 1);width:232px;height:var(--titlebar-h,40px);border-bottom:1px solid var(--border);background:var(--glass-rail);-webkit-backdrop-filter:var(--glass-blur);-webkit-user-select:none;user-select:none;align-items:center;padding:0 10px 0 14px;display:flex;position:fixed;top:0;left:0}.app--macos .sidebar-top{width:var(--sidebar-column);padding-left:var(--tl-inset,80px)}.sidebar{padding-top:calc(var(--titlebar-h,40px) + 18px)}.sidebar-collapse{border-radius:var(--radius-sm);width:28px;height:28px;color:var(--muted);cursor:pointer;transition:background var(--motion-fast), color var(--motion-fast);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.sidebar-collapse:hover{background:var(--accent-soft);color:var(--text)}.sidebar-collapse:focus-visible{outline:2px solid var(--accent-ring);outline-offset:2px}.sidebar-collapse svg{width:18px;height:18px}.main-top{z-index:var(--z-sticky);height:var(--titlebar-h,40px);background:var(--bg);border-bottom:1px solid var(--border);-webkit-user-select:none;user-select:none;align-items:stretch;display:flex;position:sticky;top:0}.main-top>.quota-summary-bar{border-bottom:none;flex:auto;min-width:0;min-height:0;padding:0 16px;position:static}.main:has(>.main-top):has(>.main-inner--combos .combos-workspace-shell){flex-direction:column;height:100dvh;display:flex}.main:has(>.main-top)>.main-inner.main-inner--combos:has(.combos-workspace-shell){flex:auto;height:auto;min-height:0}@media (width>=761px){.app{transition:grid-template-columns .16s}.app--macos.app--reduced-zoom{transition:none}.app--macos:not(.app--nav-collapsed){grid-template-columns:var(--sidebar-column) minmax(0, 1fr)}.app.app--nav-collapsed{grid-template-columns:0 1fr}.app--nav-collapsed .sidebar{display:none}.app--nav-collapsed .main{grid-column:2}.app--nav-collapsed .sidebar-top{-webkit-backdrop-filter:none;background:0 0;border-bottom:none;width:auto;margin:0;padding:0 12px}.app--macos.app--nav-collapsed .sidebar-top{padding-left:var(--tl-inset,80px)}.app--nav-collapsed .main-top,.app--nav-collapsed .main>.quota-summary-bar{padding-left:var(--chrome-clear,52px)}}@media (width<=760px){.sidebar-top{display:none}.main-top{z-index:auto;position:static}.main:has(>.main-top):has(>.main-inner--combos .combos-workspace-shell){height:100%;min-height:0}.app--macos .mobile-topbar{padding-left:var(--tl-inset,80px)}.app--macos .sidebar.open{padding-top:calc(var(--titlebar-h,40px) + 18px)}}@media (width<=320px){.app--macos .mobile-topbar{flex-wrap:wrap}.app--macos .mobile-topbar-actions{width:calc(100% + var(--tl-inset,80px));margin-left:calc(-1 * var(--tl-inset,80px));justify-content:flex-end}}