fix(openai-codex): complete prompts over the streaming transport (#1243)
* fix(openai-codex): complete prompts over the streaming transport The Codex subscription endpoint only accepts streaming requests, so `completePrompt` sending `stream: false` was rejected outright with HTTP 400 `Stream must be set to true`. That made commit-message generation, prompt enhancement and condensing unusable on Codex. Rather than issue its own request, `completePrompt` now runs the existing streaming path and joins the text chunks. That inherits the OAuth refresh-and-retry, the SDK-then-SSE fallback and the Luna body instead of duplicating a second, subtly different request builder. Reasoning chunks are deliberately dropped: a commit message is written straight into the Source Control box. A caller's abort signal also never reached the wire, since both transports abort through the handler's own controller. It is now linked to that controller, so stopping a generation actually cancels it. The spec asserted `stream: false`, pinning the bug in place; it now asserts the opposite and covers chunk joining, reasoning exclusion, auth retry and signal propagation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(openai-codex): stop replaying the stream and treat an abort as one Two problems in the streaming completePrompt path. The SDK stream and its consumption loop sat inside the same try, so an error raised part way through the stream was handled as "the SDK could not be used at all" and the request was replayed over SSE. Whatever the SDK had already yielded stayed with the caller, so the replay appended a second generation to the first. The fallback now closes after the first SDK event, which is the point where the request has been accepted and its output is already out. It is set before processEvent runs, since that mutates response state too, so a throw from it must not replay either. This covers the chat path as well, since both go through executeRequest. Both transports also end quietly on abort, breaking out of their loops rather than throwing, so completePrompt returned whatever partial text had arrived and callers read a cancelled generation as a finished one. It now rejects with an AbortError instead, and a cancellation is passed through rather than reported to telemetry or relabelled as a completion error, since stopping is the caller's own doing. The two abort specs asserted that a cancelled call resolves, which held the bug in place. They now assert the rejection and cover the pre aborted and mid stream cases, and a new spec covers the mid stream SDK failure and checks that SSE is never reached. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(openai-codex): close the last two replay routes into a live request Follow-ups to the previous commit, both raised by CodeRabbit. Closing the SSE fallback after the first SDK event left the OAuth retry loop as a second way back into a request the service had already accepted. A mid stream error that reads as an auth failure would refresh the token and send the whole thing again, so completePrompt could concatenate two generations and the chat path could repeat streamed effects. The flag moved onto the handler, reset per request alongside the other response state, and the retry is now skipped once the SDK has emitted. A refresh before any event still retries as before, which is the case that loop exists for. An abort also still reached the fallback, since the SDK rejects when the caller cancels and that read as a transport failure. It spent a second request on an already aborted signal and reported the cancellation as a connection error. The fallback now rethrows instead. Specs for both, plus a check that the pre event refresh path is untouched. * fix(openai-codex): report one abort result however the request ended Chasing a review comment about the cancellation tests turned up a real inconsistency behind it. A stream that ended quietly threw an AbortError, but a transport that rejected on abort had its own error passed straight through, so what a cancelled completePrompt rejected with depended on how far the request had got. Callers cannot key off that. An abort is now restated as an AbortError unless it already is one. The in flight case, where the caller cancels after the request is away but before any event, had no coverage. It only works if the caller signal is genuinely linked to the internal controller, so it is the case that would catch that link breaking. Added, and it waits for the request to be in flight before aborting, since the token lookup and the listener are both async and a synchronous abort fires before anything is listening. Also tightened the existing abort assertion from a bare rejects.toThrow, which passed on any error at all, to the same AbortError check the other cancellation tests use. * fix(openai-codex): keep refusals out of the completed prompt completePrompt joined every text chunk, and a refusal is streamed as text so the chat can show it. The non-streaming request this replaced read output_text, which never carries refusals, so it returned "". The streaming version instead handed the prompt enhancer "[Refusal] ..." to paste into the input box as if the model had answered. The prefix both refusal branches emit is now a shared constant, and completePrompt skips the chunks carrying it - any real output_text alongside a refusal still comes back, matching what output_text held. Also corrects the comment naming commit-message generation as the caller. The prompt enhancer is the only one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> @ --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
R
Rafael Oliveira committed
bd399fa77046727fff853f4a41936984753c9eda
Parent: c747c02
Committed by GitHub <noreply@github.com>
on 8/22/2026, 2:45:22 PM