@bitkyc08/opencodex 2.59.0 → 2.61.0-preview.20260922

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 (374) hide show
  1. package/AGENTS_INSTALL.md +64 -0
  2. package/README.md +28 -1
  3. package/bin/ocx.mjs +382 -209
  4. package/gui/dist/assets/App-E64Rzjap.js +50 -0
  5. package/gui/dist/assets/Tray-_nfzD8k4.js +1 -0
  6. package/gui/dist/assets/index-DpdfZWMK.js +86 -0
  7. package/gui/dist/assets/index-_bpvxJu0.css +1 -0
  8. package/gui/dist/assets/usage-companion-chart-DtoK7T6h.js +1 -0
  9. package/gui/dist/favicon.png +0 -0
  10. package/gui/dist/index.html +2 -2
  11. package/gui/dist/provider-icons/stepfun-color.svg +1 -0
  12. package/package.json +8 -1
  13. package/src/adapters/anthropic-image-codec.ts +16 -2
  14. package/src/adapters/anthropic-image-normalize.ts +49 -2
  15. package/src/adapters/anthropic.ts +20 -1
  16. package/src/adapters/base.ts +23 -0
  17. package/src/adapters/coding-agent/protocol.ts +36 -6
  18. package/src/adapters/coding-agent/turn.ts +32 -4
  19. package/src/adapters/command-code.ts +52 -4
  20. package/src/adapters/cursor/catalog.ts +51 -7
  21. package/src/adapters/cursor/checkpoint-store.ts +3 -0
  22. package/src/adapters/cursor/discovery.ts +11 -8
  23. package/src/adapters/cursor/live-transport.ts +26 -9
  24. package/src/adapters/cursor/protobuf-request.ts +6 -3
  25. package/src/adapters/cursor/request-builder.ts +20 -4
  26. package/src/adapters/cursor/transport.ts +19 -0
  27. package/src/adapters/cursor.ts +14 -3
  28. package/src/adapters/declaration-carrier.ts +45 -0
  29. package/src/adapters/devin/cloud-direct/chat.ts +3 -1
  30. package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +42 -5
  31. package/src/adapters/devin.ts +125 -36
  32. package/src/adapters/google-antigravity-replay.ts +1 -1
  33. package/src/adapters/google-antigravity-wire.ts +34 -7
  34. package/src/adapters/google-errors.ts +7 -1
  35. package/src/adapters/google-http.ts +49 -10
  36. package/src/adapters/google-tool-schema.ts +595 -31
  37. package/src/adapters/google-wire-compiler.ts +93 -10
  38. package/src/adapters/google-wire-shape.ts +461 -0
  39. package/src/adapters/google.ts +66 -11
  40. package/src/adapters/image.ts +4 -1
  41. package/src/adapters/input-media-guard.ts +21 -9
  42. package/src/adapters/kiro/usage.ts +3 -2
  43. package/src/adapters/kiro-tool-fallback.ts +1 -1
  44. package/src/adapters/ollama-native.ts +6 -0
  45. package/src/adapters/openai-chat/developer-role.ts +61 -0
  46. package/src/adapters/openai-chat/messages.ts +46 -27
  47. package/src/adapters/openai-chat/parallel-tool-calls.ts +32 -0
  48. package/src/adapters/openai-chat/passthrough.ts +33 -9
  49. package/src/adapters/openai-chat/reasoning-wire.ts +89 -0
  50. package/src/adapters/openai-chat-images.ts +3 -1
  51. package/src/adapters/openai-chat.ts +23 -58
  52. package/src/adapters/openai-responses/image-gen.ts +8 -6
  53. package/src/adapters/openai-responses/passthrough.ts +17 -3
  54. package/src/adapters/openai-responses/reasoning.ts +7 -0
  55. package/src/adapters/opencode-go-additional-tools.ts +12 -2
  56. package/src/adapters/registry.ts +3 -2
  57. package/src/adapters/run-turn-queue.ts +178 -29
  58. package/src/adapters/xai-web-search.ts +16 -1
  59. package/src/bridge/errors.ts +8 -2
  60. package/src/bridge/response-json.ts +9 -1
  61. package/src/bridge/sse.ts +13 -151
  62. package/src/chat/inbound.ts +141 -5
  63. package/src/claude/desktop-3p.ts +7 -1
  64. package/src/claude/desktop-first-party.ts +183 -0
  65. package/src/claude/desktop-gateway-state.ts +41 -0
  66. package/src/claude/inbound-content-options.ts +6 -0
  67. package/src/claude/inbound.ts +32 -6
  68. package/src/claude/intercept/connect-proxy.ts +179 -0
  69. package/src/claude/intercept/listener.ts +122 -0
  70. package/src/claude/intercept/local-ca.ts +298 -0
  71. package/src/claude/intercept/runtime.ts +98 -0
  72. package/src/claude/intercept/settings.ts +189 -0
  73. package/src/cli/access.ts +87 -0
  74. package/src/cli/account-auth.ts +19 -0
  75. package/src/cli/account-extended.ts +4 -4
  76. package/src/cli/capabilities.ts +31 -0
  77. package/src/cli/claude-desktop.ts +206 -16
  78. package/src/cli/codex-shim-autorestore.ts +3 -0
  79. package/src/cli/companion.ts +56 -0
  80. package/src/cli/dispatch.ts +46 -7
  81. package/src/cli/doctor.ts +28 -9
  82. package/src/cli/ensure-desired-integrations.ts +43 -5
  83. package/src/cli/help.ts +7 -9
  84. package/src/cli/hub.ts +3 -2
  85. package/src/cli/index.ts +203 -59
  86. package/src/cli/init.ts +8 -0
  87. package/src/cli/integrations.ts +7 -1
  88. package/src/cli/opencode.ts +2 -2
  89. package/src/cli/provider.ts +13 -1
  90. package/src/cli/registry.ts +41 -2
  91. package/src/cli/resolve.ts +230 -0
  92. package/src/cli/root.ts +24 -1
  93. package/src/cli/start-ownership-publication.ts +56 -0
  94. package/src/cli/status-probes.ts +2 -18
  95. package/src/cli/status.ts +62 -0
  96. package/src/cli/stop-report.ts +143 -0
  97. package/src/cli/uninstall-plan.ts +9 -0
  98. package/src/client/machine-api.ts +2 -2
  99. package/src/client/machine-listener.ts +4 -7
  100. package/src/client/runtime.ts +26 -2
  101. package/src/clients/aside-profiles.ts +4 -0
  102. package/src/clients/config-export/zcode-store.ts +157 -0
  103. package/src/clients/config-export.ts +36 -0
  104. package/src/codex/account-store.ts +65 -0
  105. package/src/codex/app-server-processes.ts +72 -40
  106. package/src/codex/auth-api/account-list.ts +19 -11
  107. package/src/codex/auth-api/login-flow.ts +6 -1
  108. package/src/codex/auth-api/pool-quota-probe.ts +30 -7
  109. package/src/codex/autostart-health.ts +28 -0
  110. package/src/codex/catalog/build-entries.ts +2 -2
  111. package/src/codex/catalog/effort.ts +3 -3
  112. package/src/codex/catalog/gather-capture.ts +21 -2
  113. package/src/codex/catalog/model-hints.ts +29 -28
  114. package/src/codex/catalog/parsing.ts +7 -0
  115. package/src/codex/catalog/provider-models.ts +43 -17
  116. package/src/codex/catalog/retained-sync.ts +24 -28
  117. package/src/codex/catalog/routed-gather.ts +19 -0
  118. package/src/codex/context-compat.ts +5 -2
  119. package/src/codex/convergence.ts +2 -2
  120. package/src/codex/desired-state.ts +4 -1
  121. package/src/codex/history-job.ts +6 -6
  122. package/src/codex/history-provider.ts +31 -166
  123. package/src/codex/history-rollout-read.ts +174 -0
  124. package/src/codex/inject/config-toml.ts +41 -6
  125. package/src/codex/inject/paginated-openai-compat.ts +90 -0
  126. package/src/codex/inject.ts +18 -15
  127. package/src/codex/injected-marker.ts +18 -0
  128. package/src/codex/internal/catalog-writer.ts +33 -1
  129. package/src/codex/main-account.ts +6 -0
  130. package/src/codex/model-cache.ts +99 -6
  131. package/src/codex/model-entitlement-admission.ts +59 -0
  132. package/src/codex/model-entitlements.ts +116 -54
  133. package/src/codex/native-main-admission.ts +83 -0
  134. package/src/codex/observed-model-denials.ts +101 -8
  135. package/src/codex/prompt-text-probe.ts +9 -6
  136. package/src/codex/routing/health-store.ts +39 -0
  137. package/src/codex/routing/selection.ts +37 -1
  138. package/src/codex/routing.ts +12 -42
  139. package/src/codex/shim-templates.ts +29 -3
  140. package/src/codex/shim.ts +1 -1
  141. package/src/codex/subagent-model-fallback.ts +22 -4
  142. package/src/combos/failover.ts +3 -0
  143. package/src/companion/settings.ts +132 -0
  144. package/src/config/admitted-identity.ts +222 -0
  145. package/src/config/atomic-write.ts +117 -5
  146. package/src/config/diagnostics.ts +22 -1
  147. package/src/config/feature-flags.ts +5 -0
  148. package/src/config/load-degrade.ts +52 -7
  149. package/src/config/process-state.ts +1 -1
  150. package/src/config/proxy-env.ts +8 -2
  151. package/src/config/schema/compaction-triggers.ts +11 -0
  152. package/src/config/schema/config-schema.ts +31 -1
  153. package/src/config/schema/leaf-validators.ts +59 -0
  154. package/src/config.ts +3 -3
  155. package/src/generated/compatibility-version.json +604 -280
  156. package/src/grok/reset-coupons.ts +38 -19
  157. package/src/images/loop.ts +6 -1
  158. package/src/integrations/aside-profile-context.ts +37 -3
  159. package/src/integrations/aside-profile-journal.ts +68 -3
  160. package/src/integrations/aside-profiles.ts +128 -3
  161. package/src/integrations/config-io.ts +44 -10
  162. package/src/integrations/merge.ts +120 -13
  163. package/src/integrations/mutation-plan.ts +921 -0
  164. package/src/integrations/registry.ts +38 -0
  165. package/src/integrations/state.ts +78 -45
  166. package/src/integrations/target.ts +208 -0
  167. package/src/integrations/writer.ts +134 -110
  168. package/src/lab/conformance/fixture-provider.ts +5 -0
  169. package/src/lab/live/transport.ts +4 -0
  170. package/src/lab/live/types.ts +5 -0
  171. package/src/lab/subject/behavior-fingerprint.ts +1 -1
  172. package/src/lib/admin-secrets.ts +9 -1
  173. package/src/lib/browser-launch-notice.ts +59 -0
  174. package/src/lib/bun-runtime.ts +6 -2
  175. package/src/lib/debug-log-buffer.ts +6 -1
  176. package/src/lib/debug.ts +63 -0
  177. package/src/lib/errors.ts +79 -0
  178. package/src/lib/http-response-semantics.ts +57 -0
  179. package/src/lib/lab-live-pinned-sender.ts +26 -12
  180. package/src/lib/open-url.ts +51 -7
  181. package/src/lib/package-tree-integrity.ts +2 -1
  182. package/src/lib/package-version.ts +8 -0
  183. package/src/lib/pinned-http.ts +142 -2
  184. package/src/lib/plain-data.ts +103 -0
  185. package/src/lib/process-control.ts +13 -5
  186. package/src/lib/provider-egress.ts +310 -0
  187. package/src/lib/provider-outbound.ts +109 -16
  188. package/src/lib/proxy-env.ts +82 -7
  189. package/src/lib/request-execution-budget.ts +72 -0
  190. package/src/lib/request-failure-attribution.ts +183 -0
  191. package/src/lib/request-failure-model.ts +236 -0
  192. package/src/lib/request-resend-gate.ts +138 -0
  193. package/src/lib/socks5-fetch.ts +136 -26
  194. package/src/lib/spend-ledger-owner.ts +364 -0
  195. package/src/lib/spend-reservation-ledger.ts +218 -27
  196. package/src/lib/standalone.ts +16 -0
  197. package/src/lib/upstream-retry.ts +167 -16
  198. package/src/lib/windows-system-proxy.ts +16 -11
  199. package/src/lib/winsw.ts +2 -2
  200. package/src/oauth/callback-server.ts +4 -3
  201. package/src/oauth/generic-account-failover.ts +1 -0
  202. package/src/oauth/health.ts +12 -1
  203. package/src/oauth/index.ts +27 -108
  204. package/src/oauth/login-cli.ts +80 -29
  205. package/src/oauth/login-flow-state.ts +127 -0
  206. package/src/providers/api-key-resolve.ts +133 -0
  207. package/src/providers/api-key-selection.ts +5 -1
  208. package/src/providers/derive.ts +34 -17
  209. package/src/providers/devin-cli-authmode-migration.ts +14 -10
  210. package/src/providers/key-failover.ts +97 -19
  211. package/src/providers/key-store.ts +34 -110
  212. package/src/providers/model-rename-fields.ts +147 -0
  213. package/src/providers/model-rename-migration.ts +179 -38
  214. package/src/providers/model-rename-startup.ts +7 -5
  215. package/src/providers/openai-virtual-models.ts +42 -2
  216. package/src/providers/quota/antigravity.ts +22 -2
  217. package/src/providers/quota/vendor-probes-key.ts +38 -23
  218. package/src/providers/reasoning-metadata.ts +43 -18
  219. package/src/providers/registry/entries-core.ts +47 -20
  220. package/src/providers/registry/entries-extended.ts +63 -4
  221. package/src/providers/registry/model-ids.ts +168 -0
  222. package/src/providers/registry/model-seeds.ts +56 -10
  223. package/src/providers/registry/types.ts +2 -0
  224. package/src/providers/resolved-model-policy-merge.ts +167 -0
  225. package/src/providers/resolved-model-policy.ts +406 -0
  226. package/src/providers/stale-vision-classification-migration.ts +137 -0
  227. package/src/providers/xai-transport.ts +12 -1
  228. package/src/reasoning-effort.ts +8 -0
  229. package/src/responses/apply-patch-envelope.ts +0 -12
  230. package/src/responses/freeform-wrapper-scan.ts +279 -0
  231. package/src/responses/function-call-compat.ts +38 -1
  232. package/src/responses/inline-document.ts +65 -0
  233. package/src/responses/input-media.ts +42 -8
  234. package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
  235. package/src/responses/muse-tool-name-alias.ts +19 -0
  236. package/src/responses/parser-content.ts +8 -2
  237. package/src/responses/parser-tools.ts +3 -0
  238. package/src/responses/parser.ts +3 -1
  239. package/src/responses/progressive-freeform-input.ts +130 -0
  240. package/src/responses/reasoning-envelope.ts +30 -0
  241. package/src/responses/schema.ts +3 -0
  242. package/src/responses/state.ts +5 -12
  243. package/src/responses/tool-name-aliases.ts +15 -1
  244. package/src/router.ts +108 -117
  245. package/src/routing/compatibility/behavior.ts +9 -0
  246. package/src/routing/compatibility/subject.ts +16 -1
  247. package/src/server/adapter-resolve.ts +9 -0
  248. package/src/server/admission-model-scope.ts +219 -0
  249. package/src/server/audio-live.ts +9 -3
  250. package/src/server/audio-upstream.ts +18 -0
  251. package/src/server/auth-cors.ts +29 -0
  252. package/src/server/chat-completions.ts +60 -4
  253. package/src/server/chat-native.ts +19 -4
  254. package/src/server/claude-messages.ts +61 -20
  255. package/src/server/effort-row.ts +11 -3
  256. package/src/server/grok-responses-control-frame.ts +160 -1
  257. package/src/server/grok-responses-snapshot-repair.ts +113 -11
  258. package/src/server/gui-freshness.ts +103 -0
  259. package/src/server/gui-static.ts +7 -9
  260. package/src/server/images.ts +59 -6
  261. package/src/server/index/claude-intercept-lifecycle.ts +49 -0
  262. package/src/server/index/serve-options.ts +142 -39
  263. package/src/server/index/spend-ledger-lifecycle.ts +92 -0
  264. package/src/server/index/startup-warnings.ts +24 -0
  265. package/src/server/index/websocket-handler.ts +6 -1
  266. package/src/server/index.ts +34 -38
  267. package/src/server/lifecycle.ts +4 -4
  268. package/src/server/live-call-bindings.ts +6 -0
  269. package/src/server/live.ts +88 -3
  270. package/src/server/management/agent-settings-routes.ts +121 -36
  271. package/src/server/management/aside-profile-routes.ts +266 -7
  272. package/src/server/management/companion-routes.ts +77 -0
  273. package/src/server/management/config-routes.ts +18 -2
  274. package/src/server/management/context.ts +3 -0
  275. package/src/server/management/integration-routes.ts +287 -5
  276. package/src/server/management/logs-usage-routes.ts +19 -0
  277. package/src/server/management/metrics-routes.ts +20 -0
  278. package/src/server/management/model-rows.ts +224 -12
  279. package/src/server/management/native-integration-routes.ts +103 -6
  280. package/src/server/management/oauth-account-routes.ts +45 -7
  281. package/src/server/management/route-registry.ts +20 -0
  282. package/src/server/management/shared.ts +28 -4
  283. package/src/server/management/system-restart.ts +7 -2
  284. package/src/server/management/system-routes.ts +2 -0
  285. package/src/server/management/usage-aggregate-cache.ts +4 -0
  286. package/src/server/management/usage-timeline-routes.ts +44 -0
  287. package/src/server/management-api.ts +10 -9
  288. package/src/server/management-auth.ts +15 -1
  289. package/src/server/proxy-liveness.ts +75 -0
  290. package/src/server/readiness.ts +29 -10
  291. package/src/server/relay-eager.ts +24 -2
  292. package/src/server/relay.ts +138 -12
  293. package/src/server/request-log-failure-attribution.ts +99 -0
  294. package/src/server/request-log.ts +169 -2
  295. package/src/server/request-metrics.ts +298 -0
  296. package/src/server/responses/adapter-continuation.ts +3 -3
  297. package/src/server/responses/adapter-dispatch.ts +11 -6
  298. package/src/server/responses/codex-ws-wire.ts +34 -8
  299. package/src/server/responses/combo-stream-preflight.ts +168 -6
  300. package/src/server/responses/compact.ts +43 -10
  301. package/src/server/responses/compaction-routing.ts +111 -0
  302. package/src/server/responses/core-codex-account.ts +8 -3
  303. package/src/server/responses/core-combo.ts +7 -7
  304. package/src/server/responses/core-normalize.ts +6 -12
  305. package/src/server/responses/core-opaque-recovery.ts +91 -0
  306. package/src/server/responses/core-options.ts +4 -0
  307. package/src/server/responses/encrypted-payload.ts +20 -2
  308. package/src/server/responses/fetch-helpers.ts +124 -8
  309. package/src/server/responses/input-admission.ts +10 -0
  310. package/src/server/responses/passthrough-delivery.ts +45 -15
  311. package/src/server/responses/passthrough-dispatch.ts +215 -44
  312. package/src/server/responses/passthrough-error.ts +27 -8
  313. package/src/server/responses/policy-fallback.ts +5 -13
  314. package/src/server/responses/request-prepare.ts +109 -18
  315. package/src/server/responses/request-send-budget.ts +17 -1
  316. package/src/server/responses/request-sidecar-auth.ts +1 -1
  317. package/src/server/responses/request-transport.ts +26 -6
  318. package/src/server/responses/reset-replay.ts +108 -0
  319. package/src/server/responses/run-turn-execution.ts +25 -3
  320. package/src/server/responses/sidecar-execution.ts +17 -2
  321. package/src/server/responses/ws-upstream.ts +14 -27
  322. package/src/server/responses-custom-tool-repair.ts +27 -54
  323. package/src/server/responses-request-tool-scope.ts +214 -0
  324. package/src/server/responses-undeclared-tool-guard.ts +35 -2
  325. package/src/server/search.ts +25 -1
  326. package/src/server/sse-payload-rewrite.ts +1 -1
  327. package/src/server/usage-ledger-retention.ts +73 -0
  328. package/src/service/cli.ts +48 -2
  329. package/src/service/health.ts +3 -2
  330. package/src/service/install-state-contract.d.mts +27 -0
  331. package/src/service/install-state-contract.mjs +34 -0
  332. package/src/service/launchd.ts +1 -1
  333. package/src/service/orchestration.ts +2 -4
  334. package/src/service/ownership-compatibility.ts +164 -0
  335. package/src/service/ownership-mutation-lease.d.mts +32 -0
  336. package/src/service/ownership-mutation-lease.mjs +211 -0
  337. package/src/service/repair.ts +45 -1
  338. package/src/service/state-lock.ts +269 -0
  339. package/src/service/state-record.d.mts +36 -0
  340. package/src/service/state-record.mjs +138 -0
  341. package/src/service/state.ts +582 -68
  342. package/src/service/windows-taskxml.ts +11 -10
  343. package/src/service.ts +7 -3
  344. package/src/tray/windows-tray.ps1 +156 -4
  345. package/src/types/config.ts +48 -3
  346. package/src/types/provider.ts +91 -0
  347. package/src/types/request.ts +30 -2
  348. package/src/types/tools.ts +33 -0
  349. package/src/types.ts +4 -0
  350. package/src/update/index.ts +207 -63
  351. package/src/update/job.ts +9 -5
  352. package/src/update/ownership-transaction.ts +47 -0
  353. package/src/update/restart-ownership.ts +54 -0
  354. package/src/update/runtime-ownership.d.mts +40 -0
  355. package/src/update/runtime-ownership.mjs +122 -0
  356. package/src/usage/attempt-delivery.ts +198 -0
  357. package/src/usage/cache-diagnostic.ts +305 -0
  358. package/src/usage/failure-fingerprint.ts +118 -0
  359. package/src/usage/failure-projection-cache.ts +174 -0
  360. package/src/usage/failure-projection.ts +174 -0
  361. package/src/usage/ledger-retention.ts +165 -0
  362. package/src/usage/log.ts +126 -79
  363. package/src/usage/request-outcome.ts +150 -0
  364. package/src/usage/retention-contract.ts +28 -0
  365. package/src/usage/summary.ts +2 -2
  366. package/src/usage/telemetry-contract.ts +237 -0
  367. package/src/usage/timeline.ts +236 -0
  368. package/src/vision/eligibility.ts +88 -9
  369. package/src/vision/plan.ts +34 -10
  370. package/src/web-search/alpha-search.ts +21 -1
  371. package/src/web-search/executor.ts +41 -2
  372. package/src/web-search/loop.ts +6 -1
  373. package/gui/dist/assets/index-C5IebErG.js +0 -136
  374. package/gui/dist/assets/index-OESInAjC.css +0 -1
@@ -15,6 +15,8 @@ import { getCachedCatalog, type CacheEntry } from "./devin/cloud-direct/catalog"
15
15
  import { collapseDevinModelUid } from "./devin/live-models";
16
16
  import { buildNonOpenAIToolCatalogNudgeForTools } from "./tool-catalog-nudge";
17
17
  import { DEVIN_DEFAULT_API_SERVER, resolveDevinApiServer } from "../oauth/devin";
18
+ import { isProviderIssuedThinkingSignature } from "../responses/reasoning-envelope";
19
+ import { SendBudgetExhaustedError } from "../lib/upstream-retry";
18
20
 
19
21
  /**
20
22
  * Combine two usage frames from one turn by keeping the larger count per field.
@@ -190,6 +192,35 @@ async function resolveWireModelUid(
190
192
  */
191
193
  export const resolveWireModelUidForTests = resolveWireModelUid;
192
194
 
195
+ const positiveTokenCount = (value: unknown): number | undefined =>
196
+ typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
197
+
198
+ /**
199
+ * Read a per-model token count for the exact UID selected for this turn.
200
+ *
201
+ * Tries the selected UID and then its collapsed base id, preferring the
202
+ * canonical spelling and accepting dotted or case-folded saved hints — the same
203
+ * normalization the inference request applies to the model id. Where several
204
+ * spellings match one id, the smallest wins: a ceiling stated twice is
205
+ * satisfied by the lower statement.
206
+ */
207
+ function devinModelTokenHint(
208
+ record: Record<string, number> | undefined,
209
+ modelUid: string,
210
+ ): number | undefined {
211
+ if (!record) return undefined;
212
+ for (const id of [modelUid, collapseDevinModelUid(modelUid)]) {
213
+ const exact = Object.hasOwn(record, id) ? positiveTokenCount(record[id]) : undefined;
214
+ if (exact !== undefined) return exact;
215
+ const matches = Object.entries(record)
216
+ .filter(([key]) => normalizeDevinModelId(key).toLowerCase() === id.toLowerCase())
217
+ .map(([, value]) => positiveTokenCount(value))
218
+ .filter((value): value is number => value !== undefined);
219
+ if (matches.length > 0) return Math.min(...matches);
220
+ }
221
+ return undefined;
222
+ }
223
+
193
224
  /**
194
225
  * Resolve the INPUT ceiling for the exact UID selected for this turn. Catalog
195
226
  * ClientModelConfig #18 and CompletionConfiguration #3 both carry input tokens;
@@ -202,33 +233,50 @@ function resolveDevinMaxInputTokens(
202
233
  modelUid: string,
203
234
  liveWindow?: number,
204
235
  ): number | undefined {
205
- const positive = (value: unknown): number | undefined =>
206
- typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
207
- const baseId = collapseDevinModelUid(modelUid);
208
- const configured = (record: Record<string, number> | undefined): number | undefined => {
209
- if (!record) return undefined;
210
- for (const id of [modelUid, baseId]) {
211
- // Prefer the canonical spelling; retain dotted/case-folded saved hints,
212
- // matching the model-id normalization used for the inference request.
213
- const exact = Object.hasOwn(record, id) ? positive(record[id]) : undefined;
214
- if (exact !== undefined) return exact;
215
- const matches = Object.entries(record)
216
- .filter(([key]) => normalizeDevinModelId(key).toLowerCase() === id.toLowerCase())
217
- .map(([, value]) => positive(value))
218
- .filter((value): value is number => value !== undefined);
219
- if (matches.length > 0) return Math.min(...matches);
220
- }
221
- return undefined;
222
- };
223
- const contextHint = configured(provider.modelContextWindows) ?? positive(provider.contextWindow);
224
- const inputHint = configured(provider.modelMaxInputTokens);
225
- const ceilings = [positive(liveWindow), contextHint, inputHint]
236
+ const contextHint = devinModelTokenHint(provider.modelContextWindows, modelUid)
237
+ ?? positiveTokenCount(provider.contextWindow);
238
+ const inputHint = devinModelTokenHint(provider.modelMaxInputTokens, modelUid);
239
+ const ceilings = [positiveTokenCount(liveWindow), contextHint, inputHint]
226
240
  .filter((value): value is number => value !== undefined);
227
241
  return ceilings.length > 0 ? Math.min(...ceilings) : undefined;
228
242
  }
229
243
 
230
- /** Pure test seam; runtime uses the same resolver immediately before dispatch. */
244
+ /**
245
+ * Resolve the OUTPUT ceiling for this turn, highest authority first:
246
+ *
247
+ * 1. the caller's explicit `max_output_tokens`, forwarded unchanged — an
248
+ * explicit cap is a request, so a small one is never widened into a
249
+ * configured larger one;
250
+ * 2. the configured per-model cap (`modelMaxOutputTokens`), read through the
251
+ * same UID-aware hint lookup the input ceiling uses;
252
+ * 3. the provider-wide `defaultMaxOutputTokens`;
253
+ * 4. undefined, which leaves the cloud-direct encoder's own 8192 fallback in
254
+ * place for a provider that configured nothing.
255
+ *
256
+ * This is NOT the history ceiling, and the two must not collapse into one
257
+ * number. CompletionConfiguration #2 is the output cap and #3 is the context
258
+ * window, so feeding a context window into this resolver would ask Cognition to
259
+ * generate a whole window's worth of output. Nothing here reads
260
+ * `contextWindow` or `modelContextWindows` for that reason.
261
+ *
262
+ * Step 1 keeps the caller's raw value rather than `positiveTokenCount`: the
263
+ * inbound parser owns what a caller may send, and re-filtering here would
264
+ * silently promote a rejected value to a configured cap the caller never asked
265
+ * for.
266
+ */
267
+ function resolveDevinMaxOutputTokens(
268
+ provider: OcxProviderConfig,
269
+ modelUid: string,
270
+ requested: number | undefined,
271
+ ): number | undefined {
272
+ if (typeof requested === "number") return requested;
273
+ return devinModelTokenHint(provider.modelMaxOutputTokens, modelUid)
274
+ ?? positiveTokenCount(provider.defaultMaxOutputTokens);
275
+ }
276
+
277
+ /** Pure test seams; runtime uses the same resolvers immediately before dispatch. */
231
278
  export const resolveDevinMaxInputTokensForTests = resolveDevinMaxInputTokens;
279
+ export const resolveDevinMaxOutputTokensForTests = resolveDevinMaxOutputTokens;
232
280
 
233
281
  export class DevinMissingCredentialError extends Error {
234
282
  constructor() {
@@ -253,6 +301,17 @@ function textFromParts(content: string | OcxContentPart[] | undefined): string {
253
301
  return content.map((part) => (part.type === "text" ? part.text : "")).filter(Boolean).join("\n");
254
302
  }
255
303
 
304
+ const MAX_DEVIN_REMOTE_IMAGE_URL_CHARS = 8_192;
305
+
306
+ function boundedDevinRemoteImageReference(imageUrl: string): string | undefined {
307
+ if (imageUrl.length > MAX_DEVIN_REMOTE_IMAGE_URL_CHARS) return undefined;
308
+ try {
309
+ return new URL(imageUrl).protocol === "https:" ? imageUrl : undefined;
310
+ } catch {
311
+ return undefined;
312
+ }
313
+ }
314
+
256
315
  /**
257
316
  * Convert inbound content parts to the multimodal shape the wire encoder accepts.
258
317
  *
@@ -261,9 +320,10 @@ function textFromParts(content: string | OcxContentPart[] | undefined): string {
261
320
  * text-only string and a message whose only content was an image was dropped
262
321
  * entirely, which is why a pasted screenshot killed the turn and the only
263
322
  * workaround was running OCR before sending. A data: URL carries everything
264
- * field #10 needs; a remote https URL cannot be inlined without a fetch, so it
265
- * stays as an explicit text reference rather than pretending the model can see
266
- * a picture it cannot. Video has no Devin field and is skipped.
323
+ * field #10 needs; a bounded remote https URL cannot be inlined without a fetch,
324
+ * so it stays as an explicit text reference rather than pretending the model can
325
+ * see a picture it cannot. Unsupported and oversized references become a fixed
326
+ * omission marker, never attacker-sized prompt text. Video has no Devin field.
267
327
  */
268
328
  function mapOcxContentToWire(content: string | OcxContentPart[] | undefined): string | ContentPart[] {
269
329
  if (typeof content === "string" || !Array.isArray(content)) return content ?? "";
@@ -271,10 +331,19 @@ function mapOcxContentToWire(content: string | OcxContentPart[] | undefined): st
271
331
  for (const part of content) {
272
332
  if (part.type === "text" && part.text) {
273
333
  out.push({ type: "text", text: part.text });
334
+ } else if (part.type === "document") {
335
+ // No Devin document field; the marker keeps the turn from disappearing entirely.
336
+ out.push({ type: "text", text: part.text });
274
337
  } else if (part.type === "image") {
275
338
  const m = part.imageUrl.match(/^data:([^;]+);base64,(.+)$/);
276
339
  if (m) out.push({ type: "image", mimeType: m[1]!, base64Data: m[2]! });
277
- else out.push({ type: "text", text: `[image url: ${part.imageUrl}]` });
340
+ else {
341
+ const remoteReference = boundedDevinRemoteImageReference(part.imageUrl);
342
+ out.push({
343
+ type: "text",
344
+ text: remoteReference ? `[image url: ${remoteReference}]` : "[image omitted: unsupported or oversized URL]",
345
+ });
346
+ }
278
347
  }
279
348
  }
280
349
  return out;
@@ -310,23 +379,26 @@ function assistantText(message: OcxAssistantMessage): string {
310
379
  * clients of the same service write #11 thinking with #12 signature and #18
311
380
  * signature_type on the assistant prompt.
312
381
  *
313
- * The signature attests the thinking it was produced with, so a block without
314
- * one contributes its text and nothing else rather than borrowing a neighbour's.
382
+ * Field #12 attests the exact text at #11, and the wire has room for one pair.
383
+ * Every block that carries text is replayed, so the chain stays intact; the
384
+ * signature rides along only when the text being replayed IS the text it
385
+ * attests, which is exactly the single-block case. Several independently signed
386
+ * blocks send an unsigned prompt rather than pairing one block's attestation
387
+ * with another block's words. A signature-only block attests encrypted thinking
388
+ * that is not being replayed at all, so it is not one of these blocks and
389
+ * cannot contribute the pair.
315
390
  */
316
391
  function assistantThinking(
317
392
  message: OcxAssistantMessage,
318
393
  ): { thinking?: string; signature?: string } {
319
394
  const blocks = message.content.filter(
320
395
  (part): part is Extract<typeof part, { type: "thinking" }> => part.type === "thinking",
321
- );
396
+ ).filter(part => Boolean(part.thinking));
322
397
  if (blocks.length === 0) return {};
323
- const thinking = blocks.map(b => b.thinking).filter(Boolean).join("\n");
324
- // Only one signature can ride the prompt, so take the last block that has
325
- // one: that is the block the turn actually ended on.
326
- const signature = blocks.filter(b => b.signature).at(-1)?.signature;
398
+ const signature = blocks.length === 1 ? blocks[0]!.signature : undefined;
327
399
  return {
328
- ...(thinking ? { thinking } : {}),
329
- ...(signature ? { signature } : {}),
400
+ thinking: blocks.map(part => part.thinking).join("\n"),
401
+ ...(isProviderIssuedThinkingSignature(signature) ? { signature } : {}),
330
402
  };
331
403
  }
332
404
 
@@ -490,6 +562,10 @@ export function createDevinAdapter(
490
562
 
491
563
  return {
492
564
  name: "devin",
565
+ // Every GetChatMessage send, including the first, is admitted through the shared budget and
566
+ // reported from the executor that dispatches it. The caller therefore leaves the first
567
+ // send's accounting here rather than logging it before admission can refuse it.
568
+ reportsPhysicalSends: true,
493
569
 
494
570
  buildRequest() {
495
571
  return {
@@ -563,6 +639,9 @@ export function createDevinAdapter(
563
639
  const maxInputTokens = resolveDevinMaxInputTokens(
564
640
  provider, modelUid, catalog?.byUid.get(modelUid)?.contextWindow,
565
641
  );
642
+ const maxOutputTokens = resolveDevinMaxOutputTokens(
643
+ provider, modelUid, parsed.options.maxOutputTokens,
644
+ );
566
645
  // The reset-retry wrapper waits out a 429 that states its own recovery
567
646
  // delay ("limit will reset in 35 seconds") and replays the identical
568
647
  // request — but only while zero events have been yielded, so a
@@ -579,11 +658,18 @@ export function createDevinAdapter(
579
658
  // input hint used to force every model through the 128k default.
580
659
  completionOpts: {
581
660
  ...(maxInputTokens !== undefined ? { maxInputTokens } : {}),
582
- ...(typeof parsed.options.maxOutputTokens === "number" ? { maxOutputTokens: parsed.options.maxOutputTokens } : {}),
661
+ ...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
583
662
  ...(typeof parsed.options.temperature === "number" ? { temperature: parsed.options.temperature } : {}),
584
663
  ...(typeof parsed.options.topP === "number" ? { topP: parsed.options.topP } : {}),
585
664
  },
586
665
  signal: incoming.abortSignal,
666
+ }, {
667
+ execution: {
668
+ executor: incoming.providerFetch,
669
+ sendBudget: incoming.sendBudget,
670
+ onPhysicalSend: incoming.onPhysicalSend,
671
+ onRecoveryWithheld: incoming.onRecoveryWithheld,
672
+ },
587
673
  })) {
588
674
  if (incoming.abortSignal?.aborted) {
589
675
  // Emitting nothing here left the bridge to synthesize adapter_eof.
@@ -662,6 +748,9 @@ export function createDevinAdapter(
662
748
  emit({ type: "error", message: DEVIN_CLIENT_CLOSED_MESSAGE, status: 499, retryable: false, ...(usage ? { usage } : {}) });
663
749
  return;
664
750
  }
751
+ // The Responses boundary already maps this local refusal to its structured 429 code.
752
+ // Converting it to an adapter event would make it an ordinary untyped upstream error.
753
+ if (error instanceof SendBudgetExhaustedError) throw error;
665
754
  const message = error instanceof CloudChatError
666
755
  ? ("Devin cloud error" + (error.code ? " " + error.code : "") + ": " + error.message)
667
756
  : error instanceof Error ? error.message : String(error);
@@ -629,7 +629,7 @@ export function antigravityUsesReplayCache(model: string): boolean {
629
629
  * neither the wire metadata nor the replay cache can supply a real one, this is the official
630
630
  * validator-bypass token.
631
631
  */
632
- const THOUGHT_SIGNATURE_BYPASS = "skip_thought_signature_validator";
632
+ export const THOUGHT_SIGNATURE_BYPASS = "skip_thought_signature_validator";
633
633
 
634
634
  /**
635
635
  * True when the model speaks the Gemini wire dialect that requires a thought signature on the
@@ -45,8 +45,11 @@ function firstUserText(parsed: OcxParsedRequest): string | undefined {
45
45
  for (const msg of parsed.context.messages) {
46
46
  if (msg.role !== "user") continue;
47
47
  if (typeof msg.content === "string") return msg.content;
48
- const first = (msg.content as OcxContentPart[]).find(p => p.type === "text" && typeof p.text === "string");
49
- if (first && first.type === "text") return first.text;
48
+ // A document part carries text too: ignoring it left a document-only opening turn with no
49
+ // anchor, which silently downgrades the deterministic session id to a random one.
50
+ const first = (msg.content as OcxContentPart[])
51
+ .find(p => (p.type === "text" || p.type === "document") && typeof p.text === "string");
52
+ if (first && (first.type === "text" || first.type === "document")) return first.text;
50
53
  }
51
54
  return undefined;
52
55
  }
@@ -77,7 +80,7 @@ export function antigravitySessionId(parsed: OcxParsedRequest): string {
77
80
  //
78
81
  // Clients that send no thread header keep the text anchor and its instability; this is a scoped
79
82
  // repair, not a universal one.
80
- const text = clientThreadAnchor(parsed) ?? firstUserText(parsed);
83
+ const text = codexThreadAnchor(parsed).preimage ?? firstUserText(parsed);
81
84
  if (!text) return `-${Math.floor(Math.random() * 9e18).toString()}`;
82
85
  const digest = createHash("sha256").update(text, "utf8").digest();
83
86
  const masked = digest.readBigUInt64BE(0) & 0x7fffffffffffffffn;
@@ -96,7 +99,10 @@ export function antigravitySessionId(parsed: OcxParsedRequest): string {
96
99
  * functionCall identity (name+args), so a shared id does not misattribute them. Instability, not
97
100
  * collision, is the failure mode this function exists to prevent.
98
101
  */
99
- function clientThreadAnchor(parsed: OcxParsedRequest): string | undefined {
102
+ function codexThreadAnchor(parsed: OcxParsedRequest): {
103
+ kind: "parent-and-own" | "parent-only" | "absent";
104
+ preimage?: string;
105
+ } {
100
106
  // `_clientThreadId` carries `x-codex-parent-thread-id`, which every parallel child of one
101
107
  // parent presents identically, so anchoring on it alone collapsed concurrent children onto a
102
108
  // single upstream Cloud Code Assist session (#5033).
@@ -113,9 +119,9 @@ function clientThreadAnchor(parsed: OcxParsedRequest): string | undefined {
113
119
  // survive both compaction and restart.
114
120
  const own = parsed._codexOwnThreadId?.trim();
115
121
  const parent = parsed._clientThreadId?.trim();
116
- if (own && parent) return `codex-thread:${parent}\u0000${own}`;
122
+ if (own && parent) return { kind: "parent-and-own", preimage: `codex-thread:${parent}\u0000${own}` };
117
123
  // Parent-only clients keep the anchor they already had.
118
- if (parent) return `codex-thread:${parent}`;
124
+ if (parent) return { kind: "parent-only", preimage: `codex-thread:${parent}` };
119
125
  // A parentless ROOT deliberately omits the parent header. `src/server/context-history.ts` says
120
126
  // so in as many words: root model requests use (session-id=root, thread-id=root) and do not
121
127
  // fabricate a parent key. It has no pair to key on, and #5054's claim that a root presents
@@ -125,7 +131,28 @@ function clientThreadAnchor(parsed: OcxParsedRequest): string | undefined {
125
131
  // preference: durable Antigravity replay state is keyed by model plus session id, and moving a
126
132
  // root's anchor on upgrade strands every signature stored under the old session — the exact
127
133
  // instability this derivation exists to avoid, introduced while fixing sharing.
128
- return undefined;
134
+ return { kind: "absent" };
135
+ }
136
+
137
+ /**
138
+ * Which class of anchor {@link antigravitySessionId} derives this request's session id from.
139
+ *
140
+ * The id itself identifies a live conversation and is a Google-visible wire value, so it is not
141
+ * something a user can paste into a public bug report. The class is: it distinguishes the four
142
+ * stability regimes above — a Codex parent/child pair, a parent-only client, the content-derived
143
+ * text anchor that compaction can move, and the random fallback that changes every turn — while
144
+ * carrying no identity at all.
145
+ *
146
+ * It reads the SAME decision the id derivation reads, so the two cannot disagree about which
147
+ * regime a request is in. What it does not do is change how the id itself is derived: nothing
148
+ * about a Google-visible wire value moves because a diagnostic wants to describe it.
149
+ */
150
+ export type AntigravitySessionAnchor = "parent-and-own" | "parent-only" | "text" | "none";
151
+
152
+ export function antigravitySessionAnchor(parsed: OcxParsedRequest): AntigravitySessionAnchor {
153
+ const anchor = codexThreadAnchor(parsed);
154
+ if (anchor.kind !== "absent") return anchor.kind;
155
+ return firstUserText(parsed) ? "text" : "none";
129
156
  }
130
157
 
131
158
  /** A Gemini content part as it appears in an Antigravity request body. */
@@ -66,7 +66,13 @@ function classifyGoogle(label: string, status: number | undefined, enumStatus: s
66
66
  if (status === 401 || enumStatus === "UNAUTHENTICATED" || lower.includes("unauthenticated") || lower.includes("invalid authentication") || lower.includes("expired")) {
67
67
  return `${label} authentication failed`;
68
68
  }
69
- if (status === 403 || enumStatus === "PERMISSION_DENIED" || lower.includes("permission_denied") || lower.includes("permission denied") || lower.includes("access denied")) {
69
+ // Keep Google's explicit enum in the normalized text. Responses/combo handling receives
70
+ // only this string, so dropping it would let location wording override the authoritative
71
+ // permission reason during downstream classification.
72
+ if (enumStatus === "PERMISSION_DENIED") {
73
+ return `${label} access denied (PERMISSION_DENIED)`;
74
+ }
75
+ if (status === 403 || lower.includes("permission_denied") || lower.includes("permission denied") || lower.includes("access denied")) {
70
76
  return `${label} access denied`;
71
77
  }
72
78
  // Google rejects unsupported geographic / datacenter locations with HTTP 400
@@ -2,8 +2,10 @@ import type { AdapterFetchContext, AdapterRequest } from "./base";
2
2
  import { createAdapterPhysicalSend } from "./physical-send";
3
3
  import type { SendClass } from "../lib/request-execution-budget";
4
4
  import type { AttemptRecoveryKind } from "../usage/log";
5
+ import { debugProviderDiagnosticLazy } from "../lib/debug";
5
6
  import { isQuotaExhaustedBody, retryableGoogleStatus, safeGoogleHttpErrorMessage } from "./google-errors";
6
- import { repairGoogleInvalidRequestBody } from "./google-wire-compiler";
7
+ import { repairGoogleInvalidRequestBodyWithReport } from "./google-wire-compiler";
8
+ import type { GoogleToolSchemaPolicy, GoogleToolSchemaProfile } from "./google-tool-schema";
7
9
  import { normalizeUpstreamHttpErrorResponse, readDisplaySafeErrorPayloadText } from "./upstream-http-error";
8
10
  import {
9
11
  abortError,
@@ -22,6 +24,8 @@ const GOOGLE_RETRY_MAX_MS = 2_000;
22
24
  export interface GoogleRetryOptions {
23
25
  /** Repair-and-replay structurally invalid 400 bodies (Vertex/Antigravity behavior). */
24
26
  repairInvalid400?: boolean;
27
+ toolSchemaProfile?: GoogleToolSchemaProfile;
28
+ toolSchemaPolicy?: GoogleToolSchemaPolicy;
25
29
  }
26
30
 
27
31
  async function normalizeFinalGoogleError(label: string, res: Response, signal?: AbortSignal): Promise<Response> {
@@ -45,6 +49,8 @@ export async function fetchGoogleWithRetry(
45
49
  opts: GoogleRetryOptions = {},
46
50
  ): Promise<Response> {
47
51
  const repairInvalid400 = opts.repairInvalid400 ?? true;
52
+ const toolSchemaProfile = opts.toolSchemaProfile ?? { endpointClass: "vertex" };
53
+ const toolSchemaPolicy = opts.toolSchemaPolicy ?? "compatible";
48
54
  const timeoutMs = ctx.timeoutMs ?? 200_000;
49
55
  const send = createAdapterPhysicalSend(ctx);
50
56
  let lastError: unknown;
@@ -77,10 +83,22 @@ export async function fetchGoogleWithRetry(
77
83
  } catch (error) {
78
84
  if (ctx.abortSignal?.aborted) throw error;
79
85
  }
80
- const repairedBody = repairGoogleInvalidRequestBody(activeRequest.body, payloadText);
81
- if (repairedBody !== undefined) {
86
+ const repair = repairGoogleInvalidRequestBodyWithReport(activeRequest.body, payloadText, toolSchemaProfile);
87
+ if (repair !== undefined) {
88
+ if (repair.toolSchemaLoss) {
89
+ const changedSendAllowed = toolSchemaPolicy !== "reject-lossy";
90
+ debugProviderDiagnosticLazy("google", "google-tool-schema-repair", () => ({
91
+ ...repair.toolSchemaLoss,
92
+ phase: "repair",
93
+ declarationCount: repair.toolSchemaDeclarationCount ?? 0,
94
+ changedSendAllowed,
95
+ }));
96
+ if (!changedSendAllowed) {
97
+ return ctx.returnRawErrors ? res : normalizeFinalGoogleError(label, res, ctx.abortSignal);
98
+ }
99
+ }
82
100
  compatibilityReplayUsed = true;
83
- activeRequest = { ...activeRequest, body: repairedBody };
101
+ activeRequest = { ...activeRequest, body: repair.body };
84
102
  pendingResponse = res;
85
103
  sendClass = "repair";
86
104
  attempt--; // The changed-request replay is separate from transient retry accounting.
@@ -145,16 +163,37 @@ export async function fetchGoogleWithRetry(
145
163
  * demand" spikes, plus plain rate-limit 429s, both of which previously failed immediately
146
164
  * because the default server fetch path only retries connection resets.
147
165
  */
148
- export function fetchDirectGeminiWithRetry(request: AdapterRequest, ctx: AdapterFetchContext = {}): Promise<Response> {
149
- return fetchGoogleWithRetry("Gemini", request, { ...ctx, returnRawErrors: true }, { repairInvalid400: false });
166
+ export function fetchDirectGeminiWithRetry(
167
+ request: AdapterRequest,
168
+ ctx: AdapterFetchContext = {},
169
+ schemaPolicy: Pick<GoogleRetryOptions, "toolSchemaProfile" | "toolSchemaPolicy"> = {},
170
+ ): Promise<Response> {
171
+ return fetchGoogleWithRetry("Gemini", request, { ...ctx, returnRawErrors: true }, {
172
+ ...schemaPolicy,
173
+ repairInvalid400: false,
174
+ });
150
175
  }
151
176
 
152
177
  /** Vertex AI retry wrapper. */
153
- export function fetchVertexWithRetry(request: AdapterRequest, ctx: AdapterFetchContext = {}): Promise<Response> {
154
- return fetchGoogleWithRetry("Vertex AI", request, ctx);
178
+ export function fetchVertexWithRetry(
179
+ request: AdapterRequest,
180
+ ctx: AdapterFetchContext = {},
181
+ schemaPolicy: Pick<GoogleRetryOptions, "toolSchemaProfile" | "toolSchemaPolicy"> = {},
182
+ ): Promise<Response> {
183
+ return fetchGoogleWithRetry("Vertex AI", request, ctx, {
184
+ toolSchemaProfile: { endpointClass: "vertex" },
185
+ ...schemaPolicy,
186
+ });
155
187
  }
156
188
 
157
189
  /** Antigravity (Cloud Code Assist) retry wrapper. */
158
- export function fetchAntigravityWithRetry(request: AdapterRequest, ctx: AdapterFetchContext = {}): Promise<Response> {
159
- return fetchGoogleWithRetry("Antigravity", request, ctx);
190
+ export function fetchAntigravityWithRetry(
191
+ request: AdapterRequest,
192
+ ctx: AdapterFetchContext = {},
193
+ schemaPolicy: Pick<GoogleRetryOptions, "toolSchemaProfile" | "toolSchemaPolicy"> = {},
194
+ ): Promise<Response> {
195
+ return fetchGoogleWithRetry("Antigravity", request, ctx, {
196
+ toolSchemaProfile: { endpointClass: "cloud-code-assist" },
197
+ ...schemaPolicy,
198
+ });
160
199
  }