@bitkyc08/opencodex 2.58.0 → 2.60.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 (303) hide show
  1. package/README.md +28 -10
  2. package/gui/dist/assets/index-BTuCbqQd.css +1 -0
  3. package/gui/dist/assets/index-DoBVdPHP.js +134 -0
  4. package/gui/dist/index.html +2 -2
  5. package/gui/dist/provider-icons/crusoe.svg +1 -0
  6. package/gui/dist/provider-icons/opper.svg +3 -0
  7. package/package.json +4 -1
  8. package/src/adapters/anthropic-image-codec.ts +16 -2
  9. package/src/adapters/anthropic-image-normalize.ts +49 -2
  10. package/src/adapters/anthropic.ts +4 -1
  11. package/src/adapters/base.ts +34 -1
  12. package/src/adapters/coding-agent/turn.ts +22 -2
  13. package/src/adapters/command-code.ts +50 -3
  14. package/src/adapters/cursor/catalog.ts +11 -0
  15. package/src/adapters/cursor/checkpoint-store.ts +3 -0
  16. package/src/adapters/cursor/discovery.ts +11 -8
  17. package/src/adapters/cursor/effort-map.ts +16 -2
  18. package/src/adapters/cursor/envelope-echo.ts +55 -2
  19. package/src/adapters/cursor/live-transport.ts +26 -9
  20. package/src/adapters/cursor/message-mapper.ts +3 -2
  21. package/src/adapters/cursor/protobuf-request.ts +8 -5
  22. package/src/adapters/cursor/request-builder.ts +21 -4
  23. package/src/adapters/cursor/thread-continuity.ts +105 -31
  24. package/src/adapters/cursor/tool-guidance.ts +5 -4
  25. package/src/adapters/cursor/transport.ts +19 -0
  26. package/src/adapters/cursor.ts +45 -2
  27. package/src/adapters/devin/cloud-direct/chat.ts +14 -3
  28. package/src/adapters/devin/cloud-direct/index.ts +7 -0
  29. package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +140 -0
  30. package/src/adapters/devin.ts +125 -26
  31. package/src/adapters/google-antigravity-replay.ts +1 -1
  32. package/src/adapters/google-antigravity-wire.ts +55 -4
  33. package/src/adapters/google-http.ts +57 -11
  34. package/src/adapters/google-tool-schema.ts +595 -31
  35. package/src/adapters/google-wire-compiler.ts +93 -10
  36. package/src/adapters/google-wire-shape.ts +461 -0
  37. package/src/adapters/google.ts +60 -10
  38. package/src/adapters/openai-chat/response-events.ts +61 -0
  39. package/src/adapters/openai-chat-images.ts +3 -1
  40. package/src/adapters/openai-chat.ts +10 -11
  41. package/src/adapters/openai-responses/image-gen.ts +8 -6
  42. package/src/adapters/openai-responses/passthrough.ts +25 -4
  43. package/src/adapters/openai-responses/reasoning.ts +7 -0
  44. package/src/adapters/openai-responses/tool-output-recovery.ts +75 -0
  45. package/src/adapters/openai-responses/tool-schema.ts +19 -7
  46. package/src/adapters/opencode-go-additional-tools.ts +12 -2
  47. package/src/adapters/responses-tool-schema.ts +76 -46
  48. package/src/adapters/run-turn-queue.ts +17 -4
  49. package/src/bridge/response-json.ts +1 -1
  50. package/src/bridge/sse.ts +28 -35
  51. package/src/claude/context-windows.ts +22 -0
  52. package/src/claude/outbound.ts +35 -4
  53. package/src/cli/account-api.ts +4 -3
  54. package/src/cli/account-extended.ts +26 -6
  55. package/src/cli/account-orca-import.ts +63 -0
  56. package/src/cli/account.ts +32 -4
  57. package/src/cli/capabilities.ts +40 -0
  58. package/src/cli/claude.ts +29 -1
  59. package/src/cli/codex-cli-update.ts +97 -2
  60. package/src/cli/dispatch.ts +57 -3
  61. package/src/cli/doctor.ts +218 -4
  62. package/src/cli/help.ts +4 -1
  63. package/src/cli/hub.ts +3 -2
  64. package/src/cli/index.ts +95 -22
  65. package/src/cli/models-runtime.ts +33 -4
  66. package/src/cli/opencode.ts +2 -2
  67. package/src/cli/provider.ts +13 -1
  68. package/src/cli/registry.ts +11 -1
  69. package/src/cli/runtime-api.ts +44 -0
  70. package/src/cli/start-args.ts +94 -0
  71. package/src/cli/system-command.ts +2 -0
  72. package/src/client/machine-api.ts +6 -5
  73. package/src/client/machine-listener.ts +16 -3
  74. package/src/client/runtime.ts +26 -2
  75. package/src/clients/config-export/constants.ts +2 -3
  76. package/src/clients/config-export.ts +5 -5
  77. package/src/codex/account-store.ts +146 -5
  78. package/src/codex/auth-api/account-list.ts +19 -11
  79. package/src/codex/auth-api/pool-quota-probe.ts +44 -10
  80. package/src/codex/auth-api/routes.ts +17 -2
  81. package/src/codex/auth-context.ts +16 -12
  82. package/src/codex/catalog/build-entries.ts +25 -4
  83. package/src/codex/catalog/derive-entry.ts +8 -1
  84. package/src/codex/catalog/effort.ts +10 -6
  85. package/src/codex/catalog/gather-capture.ts +22 -2
  86. package/src/codex/catalog/model-hints.ts +66 -33
  87. package/src/codex/catalog/parsing.ts +90 -5
  88. package/src/codex/catalog/provider-models.ts +19 -2
  89. package/src/codex/catalog/reserve-warn.ts +96 -0
  90. package/src/codex/catalog/retained-sync.ts +41 -26
  91. package/src/codex/catalog/routed-gather.ts +61 -3
  92. package/src/codex/cli-installation-identity.ts +210 -0
  93. package/src/codex/cli-installation-targets.ts +158 -0
  94. package/src/codex/context-compat.ts +5 -2
  95. package/src/codex/convergence.ts +5 -0
  96. package/src/codex/desired-state.ts +4 -1
  97. package/src/codex/history-job.ts +6 -6
  98. package/src/codex/history-provider.ts +24 -167
  99. package/src/codex/history-rollout-read.ts +174 -0
  100. package/src/codex/history-state-open.ts +105 -0
  101. package/src/codex/inject/config-toml.ts +44 -2
  102. package/src/codex/inject.ts +3 -2
  103. package/src/codex/internal/catalog-writer.ts +33 -1
  104. package/src/codex/lineage.ts +83 -32
  105. package/src/codex/loopback-target.ts +31 -0
  106. package/src/codex/main-account-hard-lock.ts +2 -1
  107. package/src/codex/main-account.ts +10 -3
  108. package/src/codex/main-device-reauth.ts +17 -9
  109. package/src/codex/model-cache.ts +47 -0
  110. package/src/codex/model-entitlements.ts +80 -2
  111. package/src/codex/observed-model-denials.ts +230 -0
  112. package/src/codex/orca-auth-source.ts +94 -0
  113. package/src/codex/orca-import.ts +219 -0
  114. package/src/codex/prompt-text-probe.ts +289 -16
  115. package/src/codex/quota-401-recovery.ts +12 -0
  116. package/src/codex/quota-types.ts +65 -0
  117. package/src/codex/quota.ts +24 -19
  118. package/src/codex/routing/cooldown-math.ts +8 -47
  119. package/src/codex/routing/pin-drain.ts +57 -0
  120. package/src/codex/routing.ts +20 -16
  121. package/src/codex/shim.ts +1 -1
  122. package/src/codex/subagent-model-fallback.ts +114 -2
  123. package/src/codex/windows-installation-files.ts +224 -0
  124. package/src/combos/failover.ts +125 -5
  125. package/src/config/admitted-identity.ts +222 -0
  126. package/src/config/diagnostics.ts +43 -1
  127. package/src/config/feature-flags.ts +5 -0
  128. package/src/config/load-degrade.ts +33 -0
  129. package/src/config/pending-teardown.ts +8 -0
  130. package/src/config/process-state.ts +36 -3
  131. package/src/config/provider-relative-send-path.ts +16 -0
  132. package/src/config/proxy-env.ts +31 -7
  133. package/src/config/schema/compaction-triggers.ts +11 -0
  134. package/src/config/schema/config-schema.ts +25 -0
  135. package/src/config/schema/leaf-validators.ts +76 -17
  136. package/src/config.ts +2 -2
  137. package/src/generated/compatibility-version.json +410 -258
  138. package/src/generated/model-metadata.ts +1 -1
  139. package/src/grok/reset-coupons.ts +38 -19
  140. package/src/images/loop.ts +6 -1
  141. package/src/integrations/aside-profile-context.ts +37 -3
  142. package/src/integrations/aside-profile-journal.ts +68 -3
  143. package/src/integrations/aside-profiles.ts +128 -3
  144. package/src/integrations/mutation-plan.ts +815 -0
  145. package/src/integrations/writer.ts +85 -99
  146. package/src/lab/live/transport.ts +4 -0
  147. package/src/lab/live/types.ts +5 -0
  148. package/src/lab/subject/behavior-fingerprint.ts +1 -1
  149. package/src/lib/admin-secrets.ts +9 -1
  150. package/src/lib/bounded-body.ts +4 -2
  151. package/src/lib/debug-log-buffer.ts +6 -1
  152. package/src/lib/debug.ts +23 -0
  153. package/src/lib/destination-policy.ts +48 -6
  154. package/src/lib/errors.ts +82 -15
  155. package/src/lib/http-response-semantics.ts +57 -0
  156. package/src/lib/lab-live-pinned-sender.ts +26 -12
  157. package/src/lib/local-destinations.ts +32 -5
  158. package/src/lib/pinned-http.ts +142 -2
  159. package/src/lib/plain-data.ts +103 -0
  160. package/src/lib/process-control.ts +13 -5
  161. package/src/lib/provider-outbound.ts +53 -5
  162. package/src/lib/proxy-env.ts +70 -3
  163. package/src/lib/request-execution-budget.ts +11 -3
  164. package/src/lib/response-body-inactivity.ts +193 -0
  165. package/src/lib/retry-delay.ts +69 -0
  166. package/src/lib/socks5-fetch.ts +741 -0
  167. package/src/lib/spend-ledger-owner.ts +364 -0
  168. package/src/lib/spend-reservation-ledger.ts +332 -35
  169. package/src/lib/windows-system-proxy.ts +16 -11
  170. package/src/lib/workflow-budget.ts +145 -8
  171. package/src/oauth/account-quota-rank.ts +72 -15
  172. package/src/oauth/callback-server.ts +4 -3
  173. package/src/oauth/generic-account-failover.ts +41 -27
  174. package/src/oauth/health.ts +12 -1
  175. package/src/oauth/index.ts +3 -107
  176. package/src/oauth/login-flow-state.ts +127 -0
  177. package/src/oauth/orcarouter.ts +15 -2
  178. package/src/oauth/store.ts +8 -0
  179. package/src/providers/codex-capacity.ts +9 -0
  180. package/src/providers/derive.ts +34 -17
  181. package/src/providers/devin-cli-authmode-migration.ts +14 -10
  182. package/src/providers/devin-provider-merge-migration.ts +33 -12
  183. package/src/providers/free-directory.ts +20 -2
  184. package/src/providers/key-failover.ts +327 -25
  185. package/src/providers/model-rename-migration.ts +56 -1
  186. package/src/providers/model-rename-startup.ts +7 -5
  187. package/src/providers/openai-sidecar.ts +4 -0
  188. package/src/providers/openai-virtual-models.ts +42 -2
  189. package/src/providers/opencode-go-transport.ts +14 -5
  190. package/src/providers/quota/antigravity.ts +22 -2
  191. package/src/providers/quota/report-cache.ts +3 -0
  192. package/src/providers/quota/vendor-probes-key.ts +1 -1
  193. package/src/providers/registry/entries-core.ts +39 -17
  194. package/src/providers/registry/entries-extended.ts +131 -1
  195. package/src/providers/registry/model-ids.ts +168 -0
  196. package/src/providers/registry/model-seeds.ts +87 -21
  197. package/src/providers/registry/types.ts +2 -0
  198. package/src/providers/resolved-model-policy-merge.ts +167 -0
  199. package/src/providers/resolved-model-policy.ts +406 -0
  200. package/src/providers/stale-vision-classification-migration.ts +137 -0
  201. package/src/responses/apply-patch-envelope.ts +32 -11
  202. package/src/responses/bridge-search-replay-cache.ts +152 -0
  203. package/src/responses/code-mode-helper-compat.ts +26 -16
  204. package/src/responses/custom-tool-compat.ts +1 -1
  205. package/src/responses/freeform-wrapper-scan.ts +279 -0
  206. package/src/responses/hosted-tool-policy.ts +85 -2
  207. package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
  208. package/src/responses/progressive-freeform-input.ts +130 -0
  209. package/src/responses/reasoning-envelope.ts +30 -0
  210. package/src/responses/schema.ts +9 -2
  211. package/src/responses/state.ts +5 -12
  212. package/src/responses/tool-name-aliases.ts +15 -1
  213. package/src/router.ts +91 -115
  214. package/src/routing/compatibility/behavior.ts +9 -0
  215. package/src/routing/compatibility/subject.ts +16 -1
  216. package/src/server/adapter-resolve.ts +9 -0
  217. package/src/server/auth-cors.ts +29 -0
  218. package/src/server/chat-completions.ts +13 -5
  219. package/src/server/chat-native-sse.ts +26 -9
  220. package/src/server/chat-native.ts +10 -4
  221. package/src/server/claude-messages.ts +30 -5
  222. package/src/server/effort-row.ts +11 -3
  223. package/src/server/grok-responses-control-frame.ts +160 -1
  224. package/src/server/gui-static.ts +36 -2
  225. package/src/server/inbound-body-admission.ts +187 -0
  226. package/src/server/index/serve-options.ts +86 -29
  227. package/src/server/index/spend-ledger-lifecycle.ts +66 -0
  228. package/src/server/index/websocket-handler.ts +6 -1
  229. package/src/server/index.ts +24 -25
  230. package/src/server/management/api-access.ts +3 -4
  231. package/src/server/management/aside-profile-routes.ts +266 -7
  232. package/src/server/management/config-routes.ts +48 -7
  233. package/src/server/management/context.ts +3 -0
  234. package/src/server/management/integration-routes.ts +287 -5
  235. package/src/server/management/metrics-routes.ts +20 -0
  236. package/src/server/management/model-rows.ts +224 -12
  237. package/src/server/management/provider-capability-config.ts +35 -7
  238. package/src/server/management/provider-routes.ts +70 -18
  239. package/src/server/management/route-registry.ts +14 -0
  240. package/src/server/management/shared.ts +10 -3
  241. package/src/server/management/system-restart.ts +7 -2
  242. package/src/server/management/system-routes.ts +2 -0
  243. package/src/server/management/usage-aggregate-cache.ts +4 -0
  244. package/src/server/management-api.ts +2 -0
  245. package/src/server/management-auth.ts +15 -1
  246. package/src/server/proxy-liveness.ts +97 -2
  247. package/src/server/readiness.ts +29 -10
  248. package/src/server/relay-eager.ts +24 -2
  249. package/src/server/relay.ts +136 -34
  250. package/src/server/request-log.ts +80 -3
  251. package/src/server/request-metrics.ts +236 -0
  252. package/src/server/responses/adapter-continuation.ts +74 -30
  253. package/src/server/responses/adapter-delivery.ts +39 -8
  254. package/src/server/responses/adapter-dispatch.ts +63 -30
  255. package/src/server/responses/compact.ts +89 -18
  256. package/src/server/responses/compaction-routing.ts +111 -0
  257. package/src/server/responses/core-codex-account.ts +90 -24
  258. package/src/server/responses/core-combo.ts +7 -7
  259. package/src/server/responses/core-normalize.ts +16 -15
  260. package/src/server/responses/core-opaque-recovery.ts +1 -0
  261. package/src/server/responses/core-options.ts +4 -0
  262. package/src/server/responses/encrypted-payload.ts +20 -2
  263. package/src/server/responses/fetch-helpers.ts +68 -2
  264. package/src/server/responses/passthrough-delivery.ts +41 -15
  265. package/src/server/responses/passthrough-dispatch.ts +145 -53
  266. package/src/server/responses/passthrough-execution.ts +11 -1
  267. package/src/server/responses/policy-fallback.ts +5 -13
  268. package/src/server/responses/request-prepare.ts +96 -17
  269. package/src/server/responses/request-send-budget.ts +89 -8
  270. package/src/server/responses/request-sidecar-auth.ts +17 -9
  271. package/src/server/responses/request-spend.ts +38 -9
  272. package/src/server/responses/request-transport.ts +15 -12
  273. package/src/server/responses/run-turn-execution.ts +45 -8
  274. package/src/server/responses/sidecar-execution.ts +19 -2
  275. package/src/server/responses/ws-upstream.ts +16 -28
  276. package/src/server/responses-custom-tool-repair.ts +29 -56
  277. package/src/server/responses-undeclared-tool-guard.ts +31 -1
  278. package/src/server/sse-frame-buffer.ts +12 -10
  279. package/src/server/sse-payload-rewrite.ts +37 -10
  280. package/src/server/system-env-shell.ts +5 -1
  281. package/src/server/system-env.ts +7 -1
  282. package/src/server/workflow-refusal.ts +56 -2
  283. package/src/service/cli.ts +16 -6
  284. package/src/service/guards.ts +10 -0
  285. package/src/service/health.ts +43 -0
  286. package/src/service/state.ts +7 -2
  287. package/src/tray/windows-tray.ps1 +155 -3
  288. package/src/types/accounts.ts +4 -0
  289. package/src/types/config.ts +111 -6
  290. package/src/types/provider.ts +37 -0
  291. package/src/types/request.ts +9 -1
  292. package/src/types/tools.ts +14 -0
  293. package/src/types/wire.ts +9 -1
  294. package/src/types.ts +1 -0
  295. package/src/usage/expected-prices.ts +28 -0
  296. package/src/usage/log.ts +87 -4
  297. package/src/vision/eligibility.ts +88 -9
  298. package/src/vision/plan.ts +34 -10
  299. package/src/web-search/executor.ts +41 -2
  300. package/src/web-search/loop.ts +6 -1
  301. package/src/web-search/passthrough-bridge.ts +39 -5
  302. package/gui/dist/assets/index-BbrHOIY0.js +0 -128
  303. package/gui/dist/assets/index-C5-RdDmD.css +0 -1
@@ -3,7 +3,14 @@ import { FORWARD_HEADERS } from "../adapters/openai-responses";
3
3
  import { signalWithTimeout, cancelBodyOnAbort } from "../lib/abort";
4
4
  import { redactSecretString } from "../lib/redact";
5
5
  import { sidecarEnter } from "../lib/sidecar-tracker";
6
- import { applyUpstreamRecoveryInit, fetchWithResetRetry } from "../lib/upstream-retry";
6
+ import {
7
+ applyUpstreamRecoveryInit,
8
+ fetchWithResetRetry,
9
+ releaseResponseBodyBestEffort,
10
+ retryBackoffDelayMs,
11
+ sleepWithAbort,
12
+ RETRY_AFTER_CEILING_MS,
13
+ } from "../lib/upstream-retry";
7
14
  import { withUpstreamHttpVersion } from "../lib/upstream-http-version";
8
15
  import { parseSidecarSSE, type WebSearchResult } from "./parse";
9
16
  import type { CodexUpstreamOutcome } from "../codex/routing";
@@ -35,6 +42,22 @@ export const IMAGE_INSTRUCTION =
35
42
 
36
43
  /** A search result, or an `error` string when the search couldn't run (surfaced as a tool result). */
37
44
  export type SidecarOutcome = WebSearchResult & { error?: string };
45
+
46
+ /**
47
+ * Bounded same-search 429 replays for the sidecar POST.
48
+ *
49
+ * The forward backend throttles burst sidecar traffic, and without a replay the 429 becomes a
50
+ * failed tool result that poisons the query for the whole turn (see failedQueries in loop.ts).
51
+ * 1 initial send + 2 replays; Retry-After is honored as a lower bound and capped by
52
+ * RETRY_AFTER_CEILING_MS (an instruction past the ceiling ends with the 429 instead of
53
+ * parking the search). Each wait releases the unread 429 body first so sockets do not
54
+ * accumulate under a rate-limit storm. Abort or timeout ends the wait through the existing
55
+ * catch, exactly like an abort during the SSE parse.
56
+ */
57
+ const SIDECAR_429_MAX_ATTEMPTS = 3;
58
+ const SIDECAR_429_BASE_DELAY_MS = 1_000;
59
+ const SIDECAR_429_MAX_DELAY_MS = 10_000;
60
+
38
61
  export type SidecarOutcomeRecorder = (outcome: CodexUpstreamOutcome) => void;
39
62
 
40
63
  /**
@@ -79,7 +102,7 @@ export async function runWebSearch(
79
102
  const sidecarExit = sidecarEnter("web-search");
80
103
  const t0 = Date.now();
81
104
  try {
82
- const res = await fetchWithResetRetry(
105
+ const sendOnce = () => fetchWithResetRetry(
83
106
  // Recovery nests INSIDE the version helper: applyUpstreamRecoveryInit then always receives a
84
107
  // defined init, and withUpstreamHttpVersion spreads the result, so `protocol` and the
85
108
  // recovery fields (`connection: close` + Bun's transport-level `keepalive: false`) survive
@@ -96,6 +119,22 @@ export async function runWebSearch(
96
119
  }, recovery), forwardProvider)),
97
120
  { replaySafe: true, abortSignal: linkedSignal.signal, label: "web-search-sidecar" },
98
121
  );
122
+ let res = await sendOnce();
123
+ for (let attempt = 0; res.status === 429 && attempt + 1 < SIDECAR_429_MAX_ATTEMPTS; attempt++) {
124
+ const delay = retryBackoffDelayMs(attempt, {
125
+ baseDelayMs: SIDECAR_429_BASE_DELAY_MS,
126
+ maxDelayMs: SIDECAR_429_MAX_DELAY_MS,
127
+ headers: res.headers,
128
+ retryAfterIsLowerBound: true,
129
+ });
130
+ // A deadline, not a clamp: an instruction past the ceiling ends the search with the
131
+ // 429 instead of parking it at a provider that already said it would refuse.
132
+ if (delay > RETRY_AFTER_CEILING_MS) break;
133
+ console.warn(`[web-search] sidecar HTTP 429 — retrying (${attempt + 2}/${SIDECAR_429_MAX_ATTEMPTS}) after ${delay}ms`);
134
+ await releaseResponseBodyBestEffort(res.body, linkedSignal.signal);
135
+ await sleepWithAbort(delay, linkedSignal.signal);
136
+ res = await sendOnce();
137
+ }
99
138
  // Attach the body guard before ANY branch reads it. The success path guarded itself below,
100
139
  // but the failure branch's `res.text()` runs first, so a cancel landing between fetch
101
140
  // resolution and reader attach orphaned the internal rejection (found investigating #1419).
@@ -333,10 +333,15 @@ export interface WebSearchLoopDeps {
333
333
  * 429 states the window's reset epoch even when it omits Retry-After -- and a rotation that
334
334
  * cannot see it cools the drained account for the short default instead of until the window
335
335
  * actually reopens. Optional so existing callers keep compiling.
336
+ *
337
+ * `retryParsed` is the exact iteration-local request the retry will be built from. The loop
338
+ * sends a shallow copy of the outer parsed request, so a rotation that rebinds only the outer
339
+ * object never reaches the wire. Optional so existing callers keep compiling.
336
340
  */
337
341
  on429?: (
338
342
  retryAfterHeader: string | null,
339
343
  responseHeaders?: Headers,
344
+ retryParsed?: OcxParsedRequest,
340
345
  ) => ProviderAdapter | null | Promise<ProviderAdapter | null>;
341
346
  /** Opt-in same-target 429 policy (key-auth providers). When present, 429 replays on the SAME key before on429 rotation. */
342
347
  retryOn429Policy?: Required<RateLimitRetryPolicy> | null;
@@ -558,7 +563,7 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
558
563
  // 429 key-failover parity with the normal routed path: rotate pool keys until one responds
559
564
  // or the pool is exhausted (deps.on429 returns null — cooldown map guarantees termination).
560
565
  while (prepared.response.status === 429 && deps.on429) {
561
- const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers);
566
+ const rotated = await deps.on429(prepared.response.headers.get("retry-after"), prepared.response.headers, iterParsed);
562
567
  if (!rotated) break;
563
568
  // Never let a broken body's cancel promise outlive the cumulative header deadline. Observe
564
569
  // it, but proceed immediately to the rotated fetch under the SAME deadline signal.
@@ -75,6 +75,7 @@ import {
75
75
  } from "./sidecar-providers";
76
76
  import { providerDestinationConfigError } from "../lib/destination-policy";
77
77
  import { redactSecretString } from "../lib/redact";
78
+ import { rememberBridgeSearchReplay } from "../responses/bridge-search-replay-cache";
78
79
 
79
80
  /** Canonical Ollama Cloud origin. The only origin the "ollama" backend derives on its own. */
80
81
  export const OLLAMA_CLOUD_ORIGIN = "https://ollama.com";
@@ -377,11 +378,18 @@ export interface PassthroughWebSearchBridgeStreamOptions {
377
378
  /** Sends one continuation leg and resolves with its response. */
378
379
  send: (body: string) => Promise<Response>;
379
380
  execute: PassthroughWebSearchBridgeExecutor;
381
+ /**
382
+ * Destination identity for the executed-search memo (#4587). When absent nothing is recorded,
383
+ * and the next turn replays the hosted cell exactly as it does today.
384
+ */
385
+ destinationScope?: string;
380
386
  /**
381
387
  * Re-applies the caller's outbound body ceiling to a continuation body. Returns a refusal
382
388
  * message when the extended body may not be sent, or undefined when it is admitted.
383
389
  */
384
390
  checkOutboundBody?: (body: string) => string | undefined;
391
+ /** Releases request-scoped resources when the stream completes, fails, or is cancelled. */
392
+ onFinalize?: () => void;
385
393
  signal?: AbortSignal;
386
394
  }
387
395
 
@@ -1094,11 +1102,22 @@ async function* bridgeStreamBlocks(
1094
1102
  outcome = await options.execute(queries, options.signal);
1095
1103
  }
1096
1104
  yield* emit(state.searchEndFrames(call, queries, outcome));
1097
- turns.push({
1098
- call,
1099
- // The model needs a readable result either way; an executor error is reported as the
1100
- // tool result rather than as a turn failure, so it can still answer without the search.
1101
- output: outcome.error ? "Web search failed: " + outcome.error : outcome.text,
1105
+ // The model needs a readable result either way; an executor error is reported as the
1106
+ // tool result rather than as a turn failure, so it can still answer without the search.
1107
+ const output = outcome.error ? "Web search failed: " + outcome.error : outcome.text;
1108
+ turns.push({ call, output });
1109
+ // Record what a continuation leg WOULD put on the wire, whether or not this leg sends one
1110
+ // (#4587). The caller keeps the hosted cell and replays it next turn; the pre-dispatch
1111
+ // rewrite in the Responses adapter uses this to hand the destination back its own call and
1112
+ // result instead of an item type it never produced. Recording the same text that
1113
+ // appendBridgeSearchTurn would append is what keeps a replayed turn and a continued turn
1114
+ // showing the destination one consistent conversation.
1115
+ rememberBridgeSearchReplay(options.destinationScope, call.cellItemId, {
1116
+ callId: call.callId,
1117
+ sourceItemId: call.sourceItemId,
1118
+ name: WEB_SEARCH_TOOL_NAME,
1119
+ argumentsText: call.argumentsText,
1120
+ output,
1102
1121
  });
1103
1122
  }
1104
1123
 
@@ -1170,21 +1189,36 @@ export function createPassthroughWebSearchBridgeStream(
1170
1189
  const aborted = (): boolean => cancelled || options.signal?.aborted === true;
1171
1190
  const iterator = bridgeStreamBlocks(options, aborted)[Symbol.asyncIterator]();
1172
1191
  const encoder = new TextEncoder();
1192
+ let finalized = false;
1193
+ const finalize = (): void => {
1194
+ if (finalized) return;
1195
+ finalized = true;
1196
+ options.onFinalize?.();
1197
+ };
1173
1198
  return new ReadableStream<Uint8Array>({
1174
1199
  async pull(controller) {
1175
1200
  try {
1176
1201
  const next = await iterator.next();
1177
1202
  if (next.done) {
1203
+ finalize();
1178
1204
  controller.close();
1179
1205
  return;
1180
1206
  }
1181
1207
  controller.enqueue(encoder.encode(next.value));
1182
1208
  } catch (error) {
1209
+ finalize();
1183
1210
  controller.error(error);
1184
1211
  }
1185
1212
  },
1186
1213
  cancel(reason) {
1187
1214
  cancelled = true;
1215
+ // Release request-scoped authority immediately. An async generator cannot
1216
+ // process a queued return() while its active next() is blocked on an
1217
+ // upstream read, so deferring finalize until that settles would hold the
1218
+ // sidecar probe lease for as long as the abandoned upstream leg does.
1219
+ // Optional chaining would also skip finalize entirely for an iterator
1220
+ // with no return method.
1221
+ finalize();
1188
1222
  void iterator.return?.(reason);
1189
1223
  },
1190
1224
  });