Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
06ec553
Merge pull request #3678 from lidge-jun/codex/promote-main-243-01a07240
lidge-jun Sep 5, 2026
116c2ac
Merge commit '44ea9576e27c6be8be7f13a86e32bb349368c54d' into codex/re…
invalid-email-address Sep 6, 2026
07b48da
Merge pull request #3785 from lidge-jun/codex/release-244-main-07c0
lidge-jun Sep 6, 2026
bcdf559
chore(release): promote validated 2.45.0 to main [skip ci]
invalid-email-address Sep 6, 2026
b0900e5
chore(release): promote 2.45.0 to main (#3813)
lidge-jun Sep 6, 2026
3970601
chore(release): prepare 2.46.0 stable promotion
invalid-email-address Sep 7, 2026
bba6322
Merge pull request #3851 from lidge-jun/codex/release-246-main
lidge-jun Sep 7, 2026
3d53e5f
release: prepare 2.47.0 from audited regression candidate
invalid-email-address Sep 7, 2026
eda8754
Merge commit '48ab3e1e66cfa6e0c873de2fafa4540ac61d6c7d' into codex/re…
invalid-email-address Sep 7, 2026
f9e3515
Merge commit '57252193b' into codex/release-247-main
invalid-email-address Sep 7, 2026
6f71931
release: promote 2.47.0 to main (#3929)
lidge-jun Sep 7, 2026
9a60256
Merge commit 'd0737cff3' into codex/release-247-main-final
invalid-email-address Sep 7, 2026
9e9b1d3
Merge commit 'f48c322c0' into codex/release-247-main-final
invalid-email-address Sep 7, 2026
947bae9
Merge commit '0d7652ad1' into codex/release-247-main-final
invalid-email-address Sep 7, 2026
f7f890f
release: apply final roster correction to main (#3933)
lidge-jun Sep 7, 2026
544ebee
release: promote 2.48.0 to main
invalid-email-address Sep 8, 2026
d24ff57
release: set main channel version 2.48.0
invalid-email-address Sep 8, 2026
9a27e86
Merge pull request #4011 from lidge-jun/codex/release-248-main
lidge-jun Sep 8, 2026
62849df
release: promote verified 2.49.0 product tree to main
lidge-jun Sep 9, 2026
2f3f736
Merge pull request #4117 from lidge-jun/codex/release-249-main-01a08498
lidge-jun Sep 9, 2026
3a3de88
release: promote verified 2.50.0 product tree to main
lidge-jun Sep 10, 2026
2d4d7a2
Merge pull request #4195 from lidge-jun/codex/release-250-main-01a08a81
lidge-jun Sep 10, 2026
cf456e8
release: promote verified 2.51.0 product tree to main
lidge-jun Sep 11, 2026
c155cc7
Merge pull request #4271 from lidge-jun/codex/release-251-main
lidge-jun Sep 11, 2026
95c4875
release: promote verified 2.52.0 product tree to main
lidge-jun Sep 12, 2026
4d37c35
Merge pull request #4407 from lidge-jun/codex/release-2520-main
lidge-jun Sep 12, 2026
641b05a
release: promote verified 2.53.0 product tree to main
lidge-jun Sep 13, 2026
aa05b3e
Merge pull request #4507 from lidge-jun/codex/release-2530-main
lidge-jun Sep 13, 2026
8e532c5
release: promote verified 2.54.0 product tree to main
lidge-jun Sep 13, 2026
9f7397e
Merge pull request #4540 from lidge-jun/codex/release-2540-main
lidge-jun Sep 13, 2026
de7b4a1
fix(web-search): release unused OpenAI probe leases
luvs01 Sep 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/server/responses/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6300,6 +6300,10 @@ async function handleResponsesInner(
const result = checkOutboundBodySize(continuationBody, config.maxUpstreamBodyBytes);
return result.admitted ? undefined : describeOutboundBodyRefusal(result);
},
// Resolution can acquire the account's sole cooldown-recovery probe before the routed
// provider reveals whether it will request search. Hand an unused lease back on every
// terminal path; after an executed search, the outcome recorder has already settled it.
onFinalize: () => releaseCodexAuthContextProbeLease(openAiSidecar?.authContext),
signal: upstream.signal,
})
: upstreamResponse.body;
Expand Down
12 changes: 11 additions & 1 deletion src/web-search/passthrough-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ export interface PassthroughWebSearchBridgeStreamOptions {
* message when the extended body may not be sent, or undefined when it is admitted.
*/
checkOutboundBody?: (body: string) => string | undefined;
/** Releases request-scoped resources when the stream completes, fails, or is cancelled. */
onFinalize?: () => void;
signal?: AbortSignal;
}

Expand Down Expand Up @@ -964,22 +966,30 @@ export function createPassthroughWebSearchBridgeStream(
const aborted = (): boolean => cancelled || options.signal?.aborted === true;
const iterator = bridgeStreamBlocks(options, aborted)[Symbol.asyncIterator]();
const encoder = new TextEncoder();
let finalized = false;
const finalize = (): void => {
if (finalized) return;
finalized = true;
options.onFinalize?.();
};
return new ReadableStream<Uint8Array>({
async pull(controller) {
try {
const next = await iterator.next();
if (next.done) {
finalize();
controller.close();
return;
}
controller.enqueue(encoder.encode(next.value));
} catch (error) {
finalize();
controller.error(error);
}
},
cancel(reason) {
cancelled = true;
void iterator.return?.(reason);
void iterator.return?.(reason).finally(finalize);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Finalize before waiting for iterator shutdown

When the client cancels while pull() is awaiting an upstream SSE chunk, an async generator's return() waits for the pending reader.read() before entering its finally; chaining finalize to that promise therefore leaves the sole recovery-probe lease held until the upstream produces data or is separately aborted. This is especially visible on the tee relay path, whose inspection branch may continue draining after client disconnection, so subsequent recovery probes remain blocked despite this cancellation fix. Invoke finalize() synchronously in cancel() and then request iterator/reader shutdown independently.

AGENTS.md reference: src/AGENTS.md:L17-L17

Useful? React with 👍 / 👎.

},
});
}
4 changes: 3 additions & 1 deletion structure/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ on the planned search endpoint. `openai`, `anthropic`, `xai`, `gemini`, and `exa
sidecar executor and that executor's own credential; a missing credential leaves the bridge
disarmed rather than falling through to another paid search. A leg that mixes an intercepted
`web_search` call with another client-executed tool still fails closed. Assistant text is not
treated as a search instruction.
treated as a search instruction. The bridge finalizes request-scoped OpenAI sidecar authority on
completion, failure, and client cancellation, returning any recovery probe lease when no search
request consumed it.

## Remote Hub hardening ownership

Expand Down
11 changes: 7 additions & 4 deletions tests/web-search/web-search-passthrough-bridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ describe("the bridged client stream", () => {

test("a turn with no search is relayed untouched and never re-sends", async () => {
let sends = 0;
let finalizations = 0;
const stream = createPassthroughWebSearchBridgeStream({
plan,
firstLeg: streamFromText(answerLeg()),
Expand All @@ -411,6 +412,7 @@ describe("the bridged client stream", () => {
execute: async () => {
throw new Error("must not execute a search for a turn that did not ask for one");
},
onFinalize: () => { finalizations += 1; },
});

const body = await new Response(stream).text();
Expand All @@ -419,6 +421,7 @@ describe("the bridged client stream", () => {
expect(body).toContain("response.completed");
expect(body).toContain("The current release is 2.50.0.");
expect(body.trimEnd().endsWith("data: [DONE]")).toBe(true);
expect(finalizations).toBe(1);
});

test("a search mixed with another client tool call fails closed instead of dropping it", async () => {
Expand Down Expand Up @@ -657,7 +660,7 @@ describe("the bridged client stream", () => {
test("a cancelled client stream bills no further search and sends no continuation", async () => {
let sends = 0;
let executes = 0;
const controller = new AbortController();
let finalizations = 0;
const stream = createPassthroughWebSearchBridgeStream({
plan,
firstLeg: streamFromText(searchLeg()),
Expand All @@ -672,13 +675,13 @@ describe("the bridged client stream", () => {
executes += 1;
return { text: "a result", sources: [] };
},
signal: controller.signal,
onFinalize: () => { finalizations += 1; },
});

controller.abort();
await new Response(stream).text();
await stream.getReader().cancel("client disconnected");
expect(executes).toBe(0);
expect(sends).toBe(0);
expect(finalizations).toBe(1);
});


Expand Down
Loading