Skip to content

[WRONG BRANCH] release: promote dev to main for 2.58.0 - #4915

Merged
lidge-jun merged 53 commits into
mainfrom
release/2.58.0
Sep 17, 2026
Merged

lidge-jun merged 53 commits into
mainfrom
release/2.58.0

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Summary

Promotes dev to main for the 2.58.0 release. The head is the frozen candidate 5061f2c956, with package.json already at 2.58.0.

What the line contains: send-budget admission for adapter-owned retries and the MiMo 401 body leak found while closing it, a stop to forwarding Codex's private access_programs to third-party Responses upstreams, clean-terminal classification so a normal end_turn no longer drops final_answer, Z.AI model discovery, confirmed first-frame usage reporting for Claude, safe teardown that restores routing without waiting on paginated history plus Desktop switch application and effective-state reporting, Cursor textual tool-marker quarantine and per-account observed overflow ceilings, CodeBuddy bare tool-name refusal, the Alibaba catalog refresh, top-level capacity mirroring on /v1/models, custom-model context validation, the dependency-audit bump, a docs-site build gate on pull requests, and the five-layer native control stack behind default-off flags.

Verification

Full-platform regression on the candidate: all nine Windows shards, all four Linux shards, both macOS shards, gates, docs site build, keyring and npm-global on all three platforms, storage policy, api usage, docker smoke — all green. macOS capacity cancellations are the only non-green entries and produced no result rather than a failure.

enforce-target is red by design on a promotion pull request, as it was for the 2.57.0 and 2.56.0 promotions: the gate accepts dev as a base and this one targets main.

Checklist

  • Scope stays focused and avoids unrelated cleanup — this is a promotion, with no commits of its own.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults — the native control stack ships default-off and the dependency bump carried a recorded audit.

Summary by CodeRabbit

  • New Features

    • Added experimental native WebSocket steering and function-result injection for supported Responses integrations.
    • Added clearer desktop switch status, including stored, effective, applied, and failure details.
    • Improved Codex restore and uninstall flows for paginated-history setups, with safer partial restoration and explicit provider-table removal.
    • Expanded model discovery and provider metadata, including updated Alibaba and Z.AI model support.
  • Bug Fixes

    • Improved Cursor tool-call handling, context limits, and incomplete-tool recovery.
    • Custom model context-window errors are now reported instead of silently ignored.
    • Improved retry accounting, response classification, and Windows cleanup reliability.
  • Documentation

    • Expanded integration, configuration, provider, and native-control documentation.

github-actions Bot and others added 30 commits September 17, 2026 03:14
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…state (#4832)

* docs(devlog): record the 2.57.0 release and its registry propagation state

Evidence for every step of the train, the two judgment calls (red dev, CodeQL
new-alert count), and the pending registry read that must not become a republish.

* docs(devlog): the registry confirmed 2.57.0 eight minutes after publish

---------

Co-authored-by: lidge-jun <lidge-jun@users.noreply.github.com>
…4825)

Last release blocker for 2.57.0. The PR aggregate ci check is green at this exact head, and the lane=all dispatch 35134620067 on the same head passed all 24 real jobs including every one of the six Windows shards individually, with only the optional macos control job outstanding. Windows shard failures had moved between shards across five dispatches while always landing on the same management-auth cleanup, which is what said the shard number was noise and the held handle was the defect. The chain was a killed subprocess abandoned before it died, then two real Windows timeouts being paid on the startup path, then a failed-start native-main release that was not awaitable. All three are fixed at the cause. Host-owned merge decision; no local suite, typecheck, build, or install was run.
…ill clock (#4833)

The shutdown drain measured its deadlines with `Date.now()` while the ACL harden work
those deadlines budget runs on the injectable spill clock. A test could therefore freeze
the clock, believe it had removed wall time from the case, and still lose its reserve to
real elapsed time on a loaded runner.

That is what turned `shutdown fallback prices the job-owned superseded generation before
publishing` red on macOS 2/2 in run 35137850114 at e2304ce: the case freezes both
clocks and sets an 80 ms reserve, and its own comment says the budget must not race a
loaded shard's wall clock, but the freeze never reached `fallbackPendingResponseSpills`.
It failed with `Response spill shutdown fallback budget exhausted` (ETIMEDOUT) instead of
reaching the cap assertion it exists to make.

`responseSpillNow()` is now exported from the spill store and used at all six former
`Date.now()` sites in the drain, so the budget and the work it budgets read one clock.
Production behaviour is unchanged: with no override installed it is `Date.now()`.

The new test pins both directions. Real wall time burned inside a synchronous icacls
runner cannot exhaust a frozen reserve, and advancing only the injected clock past the
reserve still produces the ETIMEDOUT close-out - so the contract is "the budget reads the
injected clock", not "the budget was removed".

No local suite, focused test, typecheck, build, or install was run.
…sable port (#4834)

PR #4830 raised SPAWN_BUDGET_MS on win32 from 45s to 90s to fix one test
case. 31 test files read that constant and nine hand it to
setDefaultTimeout, so the edit reached 339 Windows cases: 315 went 45s to
90s, 22 more that multiply it went 90s to 180s, and one derivation chain
in codex-sync-api reached 265s. The detectors that now report twice as
late are the contention ones -- codex-write-lock, the cross-process
history-lock exclusions, the shim process cases -- and several of the
affected files never spawn anything.

The measurement behind #4830 was also contaminated. The child published
its port through atomicWriteFile, the production SECRET writer, which on
Windows runs hardenSecretPath(..., required: true) twice, each able to
spawn PowerShell for SID resolution and several 30s-budgeted icacls
passes. That ACL ceremony ran inside the window the parent measures as
"time to reach a port" -- on a disposable port number.

- SPAWN_BUDGET_MS returns to 45s on every platform.
- COLD_SPAWN_BUDGET_MS (90s, win32 only) is consumed exactly once, by the
  readiness wait of the first proxy child in native-profile-startup.
- The child publishes its port and settled markers with a plain temp-file
  rename, preserving the #1061 no-partial-read contract without the ACL
  ceremony.
- The child logs child-entry, start-server-begin, start-server-end and
  port-published, so the next slow run names its own phase.
- codex-sync-api owns its bounds instead of deriving them, which returns
  that case to 130s and makes it immune to the next edit of a shared
  constant. It also reports its measured preparation window on green runs.

Co-authored-by: lidge-jun <lidge-jun@users.noreply.github.com>
…them (#4835)

* test(ci): make two fail-open environment skips hard preconditions in CI

model-metadata-sync skipped its entire drift gate when scripts/model-metadata.source.json was absent. That snapshot is tracked in this repository, so an absent input is a broken checkout, not an environment variation, and the skip silently removed the only check that the committed src/generated/model-metadata.ts still matches its source. The precondition is now asserted.

server-startup-reconcile-resilience probed Bun.serve and skipped four startup cases whenever the probe failed. That is right in a sandboxed agent environment that denies Bun.serve outright, and wrong in hosted CI, where a runner that cannot bind loopback is a broken runner and four assertions disappeared with no trace. The probe now suppresses cases only outside CI, and a CI-only guard case asserts the bind capability so a genuinely unbindable runner names itself.

* test(storage): re-enable the isolate worker-teardown cases on Linux and macOS

Four Worker-spawning cases were skipped everywhere except win32. The stated cause was real: Bun 1.3.14 segfaulted at 0xFFFFFFFFFFFFFFF8 mid-file with a balanced workers_spawned/workers_terminated count (exit 133 on macOS Silicon in run 30691129351, exit 132 on ubuntu GHA in run 30700011812), which is a runtime defect our JavaScript teardown cannot close.

Bun 1.4.0, the version this repository pins, contains the upstream fix: worker threads are parent-owned and joined before the parent VM is destroyed, bun:sqlite and other native resources are torn down before JSC, and a termination gate keeps native callbacks out of a stopping worker (oven-sh/bun#37075, #38299). oven-sh/bun#38519 reproduces this exact class and records 3/3 crashes on 1.3.14 against 3 x 400 clean terminate cycles on 1.4.0.

The skip is deleted rather than re-scoped, and the churn count is a single 8 on every platform: the one-cycle macOS and two-cycle Linux caps were crash avoidance, and a one-cycle 'repeated spawn/reset' case does not test what its name claims. The meta-test that pinned those per-platform caps goes with them. The OS-join settle in src/storage/worker-lifecycle.ts is unchanged.

* fix(codex): carry the history busy timeout into the Worker and unskip the restore-busy case

tests/codex-integration/codex-composed-acceptance.test.ts declared the restore-busy envelope a platform-independent contract and then skipped it on win32. The comment was right and the skip was wrong. Run 32344670867 shows what actually happened: 'CLI watchdog: ocx restore --json' at 45197 ms on a shard where neighbouring cases took 54-106 s. No envelope, no SQLite error, no failed assertion - the child was still waiting out production's own busy budget (5 s per attempt, two attempts, 500 ms apart) inside a real CLI process.

That wait is not the assertion, so it is shortened rather than budgeted for. In-process history tests already do this with setHistoryDbBusyTimeoutForTests; a child process could not be reached that way, and neither could the history Worker, which is a separate module realm that starts from the provider's default. The run message now carries the parent realm's busy timeout the same way it already carries the homes, validated and refused when malformed, and the Worker adopts it before its first state_5.sqlite open. Production sends the same codex-rs-matching 5 s the Worker would have used on its own, so the happy path is unchanged.

The test spawns that one child with a --preload that applies the knob only when OCX_TEST_HISTORY_BUSY_TIMEOUT_MS is set on its environment; no production module reads that variable. The lock, the two-attempt retry, the exit code, the exact JSON envelope, the byte-identity check, the release, and the convergence assertion are untouched.
…r-the-fact dispatch (#4840)

The release record claimed the candidate run covered all six Windows shards. It did not:
platform-windows is dispatch-only, so both the candidate push run 35131181996 and the
release-SHA push run 35133242171 skipped it, and the aggregate ci check accepts a skipped
producer as a pass. The green Windows evidence that existed belonged to 1504caa, the
#4825 lane head, not to the commit 2.57.0 was published from.

Dispatch 35139132889 has now run lane=all at 44de45d, the exact published SHA, and all
six shards passed individually. The release is sound; the record was not.

No local suite, typecheck, build, or install was run.
…en (#4837)

Four paths in this workflow turned a real failure into a green check.

scripts/ci/run-bun-test-batches.sh classified a batch timeout separately, re-ran
the batch one file per process, retried each singleton once more, and returned
success when that sweep passed. Singleton isolation removes exactly the
conditions that produce the failure -- batch concurrency, shared process state,
resource pressure -- so the sweep was always going to pass. Run 35087572377 job
104766021341 logged a segfault in batches 11, 15, 18, 19, 22 and 26 and reported
green. The macOS shard, the macOS control and the Windows shard each carried
`for attempt in 1 2`: a second execution that happens not to die does not
un-kill the first.

A timeout or a crash now fails its job on the first occurrence. The one-file-per
-process sweep stays, renamed to attribute_batch_file_by_file, and runs only
after the shard has already failed, so a human still learns which files were in
the batch. The shared classifier still runs in every lane; it now chooses the
error message rather than the outcome.

The aggregate `ci` gate accepted `skipped` from any job unconditionally, so it
could not tell "this event did not ask for the job" from "this event asked and
the job never started". It now derives what the event requested from the same
conditions the jobs carry, and requires success from each requested job and
skipped from the rest. On a lane=all dispatch it also reads the run's own job
list and requires six concrete successful `windows N/6` results, because a
matrix rollup reports success for five successes and one skipped leg -- the
shape of run 35112645195, where job 104850075282 was skipped with zero steps
while aggregate job 104855808104 concluded success.

tests/ci-workflows/ci-bun-crash-classifier.test.ts asserted shell source text
and never executed anything; one of its cases pinned the mask in place. The
disposition contract moves to tests/ci-workflows/ci-crash-disposition.test.ts,
which executes the real classifier against synthesized exit statuses and log
fixtures, and the real batch runner against a fake bun and a fake timeout that
reproduce a crash, a hang and an assertion failure on demand.
Each ACL-hardened write on Windows ran the icacls sequence twice against the
same file. atomicWriteFile hardens the temp while it is still empty, so it is
never readable by another principal, and hardens it again before the rename.
The content written in between moves the success memo's freshness component
(ctimeNs, which libuv reports from the NTFS ChangeTime), so the second call
missed the memo and reapplied the ACL the file already had: /grant:r,
/inheritance:r, /remove:g, plus up to three /findsid probes.

After the content write the writer now re-asserts that the path still resolves
to the object its descriptor holds, then re-attributes the memo to that same
object, so the pre-rename harden resolves through the memo instead of repeating
the mutation. Both calls stay required: true and still fail the write closed;
re-attribution moves only the freshness, and only for an unchanged object.

Windows also stops taking the chmod on that path. There it sets the read-only
attribute rather than the DACL, so it is not the secret boundary, and its
ChangeTime bump is what retired the memo a line later. POSIX keeps it.

A cold ocx start writes ocx.pid and runtime-port.json through this writer, so
those two writes go from four icacls sequences to two.
…it holds (#4849)

* fix(lib): let a killed child actually die before anyone deletes what it holds

Windows has been failing one shard per lane=all dispatch for weeks, and the shard
number kept moving while the case family did not. The repository already wrote the
right conclusion down once - the blame moved, the cause did not - and #4825 was the
attempt at it. It did not land the fix.

#4825 replaced "kill and resolve in the same tick" with "kill, wait up to two seconds,
then abandon". That is the same false ownership contract on a slower clock. When the
grace expired the caller was told its child was gone, cleanup removed the directory,
and Windows returned EPERM because icacls still held it:

  EPERM: operation not permitted, rm '...\Temp\opencodex-test-vwyZZv\tmp\ocx-management-auth-kgvXCT'
  (fail) codex app-server restart routes ride the management gate > the data-plane token
         does not authorize the restart route [15367.90ms]

A handle-bearing caller now has no second deadline after the kill. The child's actual
exit is the only release signal, because it is the only thing that is true. Only the
icacls runner takes that path; windows-user-principal passes 0 and still abandons
immediately, since its PowerShell lookup sits on the startup critical path and holds
no path anyone removes. The icacls runner's own outer watchdog still bounds the caller,
so nothing can wait forever - the reap continues in the background and teardown drains
it through flushRequiredSubprocessReapsForTests.

The regression test no longer sleeps. A manual deadline seam orders kill against exit
directly, so it proves the sequence rather than waiting for it.

The Desktop copy-coherence probes are the same mechanism seen from the other end. Those
scenarios perform different numbers of secure writes, each able to launch PowerShell or
icacls on Windows, and that contention was spending a deadline the probe shares. The
generated child now installs the existing synthetic principal and ACL runner seams before
it loads any client module. Files, SQLite, locking, rotation, recovery and Desktop
projection stay real; only the ACL subprocesses become synthetic, and an ACL descendant
can no longer outlive the parent's kill holding a temp path.

Causation was checked before writing any of this. Across 48 lane=all dispatches that
completed all six Windows shards without PR #4836, the EPERM class appears in 3 and the
probe-timeout class in 1, so both predate that PR and neither is attributable to it.

No local suite, focused test, typecheck, build, or install was run.

* fix(windows): separate "may I stop waiting" from "is it safe to delete"

The first attempt made `waitForSubprocessExit` wait for a killed child to really
exit, and Windows still failed the same way. The abandonment had simply moved up a
layer.

`awaitAsyncIcaclsRunner`'s belt fires at timeoutMs + kill grace + 250ms. That margin
was sized against the old bounded 2-second grace, so once the inner wait became
unbounded the belt was once again the deadline: on a stalled icacls it resolved
timedOut while the child was alive, the caller logged "continuing without it", and
teardown deleted a directory icacls.exe still held.

  [opencodex] ACL hardening timed out (ETIMEDOUT) - transient icacls stall
  EPERM: operation not permitted, rm '...\tmp\ocx-management-auth-Ge98cW'
  (fail) codex app-server restart routes ride the management gate > both routes
         reject an unauthenticated caller and a cross-origin caller [15261.56ms]

The 15.26s there is removeTreeWithRetry's ladder exhausting, not a probe deadline.

These are two different questions and they now have two different answers. The belt
still releases its caller, so a genuinely stuck child cannot hang startup, shutdown
or uninstall - that bound is the whole reason the belt exists. But when it gives up
it registers the outstanding reap against the target path, and code that is about to
REMOVE something must settle that separately: a test tree awaits
flushWindowsSecretAclReapsBeforeRemoval, the async writer leaves a residual temp
rather than racing an unlink against a live handle, and uninstall refuses promptly
rather than waiting, because handing a stuck child the power to hang `ocx uninstall`
would give back the bound we just protected.

The holder was confirmed rather than assumed: the failing proxy is in-process and
auth rejection stops the restart child from spawning, so icacls.exe is the only
child holding the fixture path.

removeTreeWithRetry stays. It is a fair accommodation of a short filesystem release
race; it should just never have been absorbing a live child.

No local suite, focused test, typecheck, build, or install was run.
…4846)

* test: assert these contracts by behaviour instead of by source shape

Five test files asserted what the source LOOKS LIKE — regexes over src/ read through
repoPath()/Bun.file() — rather than what it does. A test like that survives any regression
that keeps the shape and breaks on a harmless rename, so it reports the opposite of what a
reader assumes. The audit that found them rated this family High for the auth, send-budget,
abort and lifecycle cases specifically, because those are the ones whose silent failure a
user pays for.

Each file keeps every contract it previously pinned; the assertions moved to the observable
consequence of that contract.

- responses-preview-main-read-fence: the fence forbids READING the physical main token, so
  the oracle is whether auth.json is opened. Both preview sites and final auth are now driven
  for real - a thread_spawn carrying a forwardable caller bearer, a main-only denial cache,
  and an encrypted-recovery re-preview through a noncanonical route. The file's own comment
  said this fixture was more fragile than the divergence it would catch; it is built here and
  it is not. The deliberate asymmetry is preserved: nativeMainSelectionOnly still derives from
  the drain alone in both files, now proven by routing rather than by regex.
- transient-budget-scope-source and execution-budget-permits: one physical send is one charge,
  and a reservation the budget hands back releases its tokens instead of booking spend. Both
  are now read off the ledger and the observer.
- cancel-body-on-abort: the teardown ORDER is the contract, so a fake records
  fetch.abort -> body.cancel -> reader.cancel -> releaseLock and the sequence is asserted.
  Four dispatch paths additionally prove each original body is consumed exactly once.
- probe-lease-dispatch-wiring: "the production limiter is reachable" is precisely the claim a
  regex answers badly, since an import can exist while the call site is dead. A real dispatch
  now has to move the shared limiter's counter.

No production file changed and no test seam was added - every observable already existed.
Ordering is done with deferred promises, not wall-clock sleeps.

No local suite, focused test, typecheck, build, or install was run.

* test: narrow the two preview cases to the read they actually fence

Hosted CI on the first attempt reported two real auth.json reads where the case expected
none, and a 404 where it expected the encrypted-recovery path. Both were fixture faults,
and chasing them found something worth recording.

The reads are genuine request-path reads, not setup noise:
prepareResponsesRequest's explicit account preview reaches auth.json through
previewCodexAccountForRequest -> pickPriorityPreemption -> getEligiblePoolAccounts ->
isCodexAccountUsable -> isMainAccountCredentialUsable, and applySubagentModelFallback
then runs poolAccountPreview over the same path a second time. So a request owning its
own credential still consults physical-main LIVENESS twice, through pool eligibility.

That is a wider gap than the fence these cases were converted from, which guards the
denial-cache credential validation in model-entitlements.ts. Rather than quietly widen
the assertion to today's number, the two cases now say what they check - main is excluded
from denial-cache credential validation - and assert it by recording a stack at every read
and requiring none to pass through model-entitlements.ts. Removing the preview ownership
exclusion still turns them red, and now the failure names the path.

The 404 was native-model reservation: a bare gpt-5.6-sol needs a provider configured under
the name openai, and defaultProvider routed is never consulted for a bare native model.

The pool-liveness reads are left as a finding for a separate change; widening the fence to
cover them is an auth-boundary change and does not belong in a test conversion.

* test: reach encrypted recovery through the response, and name what it covers

The previous attempt configured a noncanonical baseUrl under the provider named
openai. That provider's endpoint is fixed, so the setting was discarded with a
warning and the recovery path never ran - recoveryCalls stayed 0.

Recovery is driven by the upstream RESPONSE, not by the URL, so the fixture now
provokes it the way the existing combo recovery suite does: canonical OpenAI
receives the encrypted assignment and answers 401, the routed backup is ineligible
while the ciphertext is unreadable, combo failover invokes assignment recovery, and
the decrypted plaintext is replayed to backup.example. The case asserts one recovery
call, one backup request carrying the recovered assignment, and zero physical-main
reads after the recovery boundary.

It is renamed to say what it actually covers. Combo children set comboAttempt, so
this response-driven flow does not execute the direct recovery re-preview at
request-prepare.ts:713. Calling it that would be the same false confidence this
whole change removes, so the name is narrower than the one it replaces.

No local suite, focused test, typecheck, build, or install was run.

* test: drain what the new combo path leaves open

The previous commit made this file provoke real combo failover and encrypted
recovery. That reached a code path the earlier cases never did, and it left
process resources behind: postSpawn never drained the response body, and
handleResponses holds its translator budget until EOF or cancellation, so the
stream lifecycle stayed open past the case. The completed recovery path can also
schedule a response-state debounce, and combo selection state and cooldowns
survived teardown.

macOS shards run every file in one Bun process with bounded parallelism -
--isolate rebuilds globals and module graphs per file but does not reclaim
outstanding process resources - so the cost landed on siblings. Two unrelated
cases in other files timed out at 30.09s and 20.77s where they had taken 8.36s
and 8.77s on this branch's own baseline.

postSpawn now drains every body it returns, and teardown clears combo selection,
combo cooldowns and response-state memory and persistence. The rest of the
cleanup - fetch, Date.now, the filesystem spy, native-main recovery blocking,
account state, recovery-cache flights and timers - was already correct.

Worth recording: those two cases were already marginal. A recent dev run had the
cancellation case at 20.58s against its 30s bound and the memory case at 11.79s
against 20s. This change removes the burden that pushed them over, but the margin
itself is a separate finding.

No local suite, focused test, typecheck, build, or install was run.
…each legible (#4851)

* ci(windows): restore the margin the six-shard leg lost, and make a breach legible

Every Windows dispatch had become a coin flip against the 30-minute job wall.
Measured wall time per shard over the last seven lane=all dispatches, in minutes:

  run 35168946544   30.2 CANCELLED   20.8  20.3  21.3  13.4  21.0
  run 35164979005   23.6  22.5  20.4  23.3  13.8  16.6
  run 35161399172   23.5  24.7  23.7  26.8  17.2  17.3
  run 35152226272   16.5  18.6  20.3  13.7  20.4  24.8
  run 35148850553   18.6  20.0  24.8  14.2  23.9  22.8
  run 35139132889   21.7  18.8  23.8  18.9  24.7  20.5
  run 35134620067   20.5  20.3  19.9  16.8  24.0  28.3

13.4 to 30.2 against a 30-minute ceiling. A shard killed at the wall reports
cancelled - neither a pass nor a fail, and with no indication of which file was
running when it died.

This is the third time this leg has grown into its ceiling; ci.yml already records
the first two. One leg reached 30 minutes and died in cleanup, four shards then ran
17-25 minutes with a green 3/4 cancelled at 25m12s, and six were chosen to put each
leg at two-thirds of that. Six has now done the same, helped by a suite that keeps
growing and by #4835 re-enabling a family that had been skipped.

Nine shards, arithmetic in the workflow: total observed work is about 133 minutes,
so nine legs project to 26.5 minutes including the ~1.43 slowest-shard skew and the
25% run-to-run variance this file already documents; eight projects to 29.8, which
is not margin. The ceiling stays 30 minutes, because raising it is the masking
answer and the number is supposed to mean something. The cost is three more
concurrent Windows runners and their fixed setup.

Cutting work per shard buys time but does not make a wedge readable, so this leg
now runs through the same batch runner Linux uses: at-most-12-file processes with a
120-second bound. A timeout or crash fixes the shard red immediately and names the
batch; the singleton sweep that follows is diagnosis only and cannot turn it green,
exactly as #4837 established. scope=all keeps all 1327 Windows files - Linux alone
excludes the storage-policy and api-usage families because separate jobs own them.

The aggregate gate counts the nine legs by name through the Actions API. A matrix
rolls up to success when a leg never starts, so counting is the only way to know
the dispatch produced the evidence it was run to produce.

No local suite, focused test, typecheck, build, or install was run.

* ci(windows): size the batch bound from Windows data, not Linux's

The first attempt gave this leg Linux's batch settings unchanged - 12 files, 120
seconds - and 7 of 9 shards went red on dispatch 35171877721. The runner reported
it precisely: "batch 5 timeout failure (exit 124)" followed by "every file passed
alone, so the timeout lives in multi-file process state". That second line is the
report you get when a bound is simply too small, not when something is wedged.

Windows is the slowest hardware on the board, which is the whole reason this leg
needed nine shards; a bound copied from the fastest one was never going to hold.
Measured across 58 completed batches in that dispatch: median 39.1s, p90 92.6s,
p95 100.1s, max 105.8s, and seven batches reached the 120s ceiling. The bound sat
at roughly the mean, so about half of all batches were always going to breach it.

Six files per batch with a 480-second bound. The sizing case is one naturally slow
file: codex-inject-integration.test.ts passes in 312.0s and 317.6s in green runs,
so its six-file batch projects to 337.4s, and 421.8s with the 25% run-to-run
variance this workflow already documents. 480 leaves 58.2s over that. Six-file
attribution halves topped out at 148.0s, so every other batch has an enormous
margin.

Linux keeps 12 files and 120 seconds. That number is correctly sized for that
hardware and sharing one constant across two very different machines is what
caused this.

The two numbers are independent. Batch size and bound decide how quickly a wedge
is named; the nine-shard split decides total wall time. Six-file batches add 12
processes per shard at a measured 0.106-0.168s of wrapper overhead each, about 2.1s
per shard, so the margin arithmetic in the shard comment is unchanged.

A real wedge now fails within eight minutes naming at most six files, with
singleton attribution after the shard is already red.

No local suite, focused test, typecheck, build, or install was run.

* test(ci): stop the batch oracle from discarding a one-file primary batch

The new scope=all case failed expecting three batches and seeing two, and the
interesting part is that the runner was right and the test was wrong.

batchCalls() classified every invocation beginning with "1|" as singleton
attribution. Seven fixture files at batch size three is a valid primary sequence of
3, 3, 1 - so the oracle threw away the last real batch and then reported the count
it had just corrupted. A test that miscounts and then asserts its own miscount is
the same false confidence this branch has been removing elsewhere, so the fix is the
oracle, not the number.

It now asserts the exact primary sequence 3, 3, 1, checks the runner's own summary
line for seven files in three processes, and still requires the dedicated file to
appear.

Windows coverage was verified independently rather than assumed, because a scope
that silently dropped the dedicated families would be exactly the silent loss this
round exists to prevent. From dispatch 35174148018: 1327 test files in the
repository, 1320 in general scope, 7 dedicated; the Windows legs ran 148x4 + 147x5
= 1327, and the logs show all seven - tests/server/api-usage.test.ts and the six
storage-policy files - executing across shards 3 through 8.

That dispatch also carried the calibration result: nine Windows shards, all green,
at 10.3 12.2 12.5 13.1 13.6 13.8 15.0 15.1 17.1 minutes against the 30-minute wall,
against a six-shard spread of 13.4 to 30.2.

No local suite, focused test, typecheck, build, or install was run.
…nal (#4859)

`a contender with a deadline waits for the holder instead of failing immediately`
went red on dev (run 35177450461, job 105062310488) with the HOLDER reporting busy,
which should have been impossible: the parent had already seen its hold marker.

Both facts were true. The marker was written from inside the lock callback, so it
proved the child had ENTERED the section, not that it finished holding it. The
child's coordination database lived under the ambient OPENCODEX_HOME, which every
file in the same CI batch shares, so another test reading it could turn the
holder's COMMIT into SQLITE_BUSY. With timeoutMs 0 the child had no retry, the
acquisition rolled back, and it returned busy after having already published the
marker the parent was waiting on.

The 150ms sleep was the second half of the same problem. It was standing in for
"the waiter is now actually waiting", and nothing made that true - on a loaded
runner the parent could release before the contention it exists to measure had
begun, which would also have made the waitedMs > 0 assertion a coin flip.

So: the child's database moves to the per-test temp root, which removes the
cross-file contention entirely; the waiter runs as its own child and publishes a
wait marker only after confirming its lock promise did not settle synchronously;
and the parent releases the holder only once it has seen that marker. waitedMs now
comes back from the child, so the property the case exists to prove is guaranteed
by construction rather than by timing.

No other blind sequencing remains in this file - every other wait is the existing
waitFor, which polls an observable condition.

No local suite, focused test, typecheck, build, or install was run.
…igits "502" (#4860)

Windows shard 4/9 of run 35180376537 went red on a turn that had succeeded
perfectly. The relay stamps every chunk with a random chatcmpl-<hex> id, and this
run drew chatcmpl-05021785ecf5440c96ca31be. expect(text).not.toContain("502")
searched the whole stream, found those three characters inside the id, and failed.

tests/images/loop.test.ts already retired the identical assertion for "504" and
measured it: roughly one id in 137 contains a given three-digit string, which
reddened about one run in 69 for no reason at all.

The assertion could not do its job either. This relay's failure mode carries an
error frame and ends the turn; the number 502 never appears in the body, so a real
failure would have slipped straight past it. It was simultaneously flaky and blind.

Assert the terminal shape instead: the stream reached [DONE] and carried no error
frame. That detects the failure the case was written for and cannot be moved by a
random identifier.

No local suite, focused test, typecheck, build, or install was run.
…one flag (#4862)

`ocx config show` timed out at its 40-second bound on Windows shard 6/9 of run
35182806140. The same case took 879ms and 1673ms in the two preceding dispatches,
so this was a 25-45x outlier rather than a chronic cost, and a bigger budget would
have been the wrong answer twice over.

The obvious suspect was Windows ACL hardening, and it is not that: `config show`
reads through readConfigDiagnostics and never calls loadConfig, the failing job
carries no ACL diagnostic line, and the ACL-free `config get` case beside it also
took 16.5 seconds. What both share is cold module loading.

The command was importing the whole connect, lifecycle and catalog graph for one
thing: deciding whether the `_remoteHub` annotation should say connected. On a cold
Windows process that import is most of the command's cost, and it can drag the
lifecycle recovery path in behind it.

It now derives that annotation from the validated client record and the bounded
service-token reader, without importing ./connect, without catalog readiness work
and without entering lifecycle recovery.

Nothing about the security boundary changes: no writer moved, and no
`required: true` ACL call was touched. If the read-path reasoning is wrong the
worst case is an inaccurate `_remoteHub.connected` display during an unusual
recovery state - secret permissions and persisted bytes are unaffected.

No local suite, focused test, typecheck, build, or install was run.
* test(web-search): drive the connect deadline on a virtual clock

`fast headers plus raw byte progress can outlive connectTimeoutMs` timed out at
1002.37ms against its own 1000ms ceiling on macOS 2/2 of run 35186268515. Its
siblings in the same describe run in 2.58, 3.48, 2.57, 3.06 and 14.65ms, so this
case was three orders of magnitude slower than everything around it and sitting
2.37ms outside a bound it could not reliably clear.

It was spending that second on five real 12ms waits used to walk a real deadline
forward. Real sleeps and a real ceiling race the same scheduler, so runner load
decides the outcome - which is how a case about timeout SEMANTICS became a case
about how busy the machine was.

It now drives the existing clearableDeadline seam with a virtual elapsed counter.
The first byte lands at a virtual 26ms against a 25ms deadline, which is the exact
condition the case exists to describe: headers that arrive fast must clear the
deadline before the body is consumed, and continuing byte progress must not re-arm
it. Body, cancellation, status and completion assertions are unchanged.

The 1000ms ceiling stays exactly where it was. Nothing was widened; the wall-clock
wait was removed instead.

Ablation: leave the deadline armed, or move its clear to first-byte progress, and
the abort lands before "a" is enqueued, so the body and completion assertions fail.

No local suite, focused test, typecheck, build, or install was run.

* test(web-search): fit the virtual-clock rewrite inside the ratchet cap

The rewrite took this file to 2857 lines against a 2823 baseline, so the
repository file-size ratchet failed and took test 1/4 and macos 2/2 with it.

Compacted to exactly 2823. Every assertion, the virtual deadline semantics, the
ablation and the 1000ms ceiling are unchanged; only the expression is tighter.

The baseline itself is untouched. Editing the cap to fit a change is the same move
as widening a timeout to fit a slow test.

No local suite, focused test, typecheck, build, or install was run.
…nst itself (#4876)

dev went red on windows 4/9 of dispatch 35191675127, and the cause is mine.

#4851 replaced the Windows leg's single `bun test` invocation with 25 sequential
batch invocations. tests/preload.ts takes a user-scoped machine-local lock, and
line 101 makes it win32-only, so Linux and macOS have run this same batch runner
unqueued for a long time while Windows had never batched at all. The lock joins
workers that share a run ID and blocks anything with a different one, so each batch
now queued behind the previous batch's stragglers:

  [test] bare Bun worker 2548 is waiting for test run pid 7272 to release the user lock.
  ##[warning]Bun test process timed out after 480s in shard 4/9 batch 6/25.
  ##[error]Shard 4/9 batch 6: every file passed alone

The first file of that batch then ran in 11.25s during attribution. The eight
minutes were queue, not work, which is why a larger bound would only have hidden it
for longer.

scripts/test-run-lock.ts already names this case in its own timeout message: set
OCX_TEST_NO_QUEUE=1 only when overlapping test runners are intentional. A dedicated
CI job running its own batches back to back is one logical test run, so the batch
step now sets it. What the queue protects against - an unrelated second suite
stacking load on a developer's machine - cannot happen in that job, and each batch
still creates its isolated home and arms the live-home and service-manager guards
before it would have reached the lock.

The lock's own unit tests now pin an explicitly queued environment, so the workflow
bypass can never silently turn their acquisitions into no-ops. That was the real
risk in disabling a guard by environment variable.

The 480-second batch bound and the 30-minute job ceiling are unchanged.

No local suite, focused test, typecheck, build, or install was run.
…act (#4882)

Record the parent-relative delta of each stage in the four-deep native\nWebSocket control stack, state the stages as one checkable contract,\ndetermine in code whether the native-main read fence gates them, and set\nthe order in which activation should be decided.
Six narrow contract defects that share no code path, one pull request each.
Records what each unit may not change, and two repository gates checked against
the tree rather than assumed: the file-size ratchet pins gui/src/pages/Models.tsx
at its current 2792 lines, so the carried custom-model validation cannot add a
net line, and enforce-target requires a GUI screenshot this lane cannot produce.
The reference entry opens with "Key-auth openai-chat providers only",
which an operator reasonably reads as "every other adapter gets no
transient retry". That is not what happens.

A provider whose adapter is openai-responses never reaches
transientRetryPolicyFor at all. createResponsesPassthroughAdapter
declares passthrough: true, and core.ts returns into
executePassthroughResponse on that flag before the adapter dispatch
path is built. The passthrough lane then applies its own transient
ladder from a fixed constant, so such a provider gets replay the
setting can neither enable nor tune.

Both halves of that mismatch mislead: the option looks broader than it
is, and the untouched lane looks quieter than it is. One sentence in
each locale states the boundary.

Documentation only. No runtime behaviour changes.
…udget (#4865)

* fix(adapters): charge adapter-owned retry sends to the request send budget

mimo-free's 401 JWT replay, command-code's reasoning-effort repair, and the google-http transient loop each issued bare fetches that never touched ctx.sendBudget, so a request holding only its final recovery permit still dispatched and a refused retry still paid the backoff sleep.

Route each physical send through createAdapterPhysicalSend: admission precedes pacing, backoff and superseded-response cancellation, a credential hop's pending permit pays for the first send exactly once, and a refused retry returns the real upstream response instead of a synthetic error.

Follow-up to #4621.

* test(layout): map physical-send.test.ts to the adapters domain

* fix(mimo-free): drain the 401 body before the JWT refresh can reject

The 401 replay moved its drain behind admission so that a budget-refused
replay can still return that same response with a readable body. Inside
beforeDispatch it ran last, after resetMimoJwtCache and getMimoJwt.

getMimoJwt issues its own bootstrap request and rejects on a failed or
oversized response. When it did, fetchResponse threw and the 401 body was
never released - a leak the pre-change code did not have, because it
cancelled first and refreshed second.

Draining first WITHIN beforeDispatch keeps both properties: it is still
after admission, so a refusal returns the untouched response, and it no
longer depends on the refresh succeeding.

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Co-authored-by: JUN <bitkyc08@gmail.com>
…4881)

* docs(devlog): plan the L3 retry, admission and combo-recovery unit

* docs(devlog): quote the mimo drain line with its comment intact

The hygiene scanner reads added lines in every changed file, markdown
included, and the abbreviated snippet carried a literal empty catch.
Quoting the real source line keeps the point and clears the rule.
…ird-party upstreams (#4884)

Codex 0.155 attaches a top-level access_programs object gated on ChatGPT auth
rather than on the destination URL, and loopback injection keeps the client's
openai provider identity, so the field rode along to every routed destination.
A gateway that validates its top-level schema rejects the request before
inference, which broke every turn on OpenCode-Go's muse-spark models.

Add CANONICAL_ONLY_TOP_LEVEL_FIELDS beside CANONICAL_ONLY_TOOL_FIELDS and apply
it at the existing private-field boundary for destinations OpenCodex does not
operate. Scoped by destination rather than by the canonical surface so the
native /responses/compact path, which spreads the raw body and is offered only
to OpenAI-operated destinations, cannot disagree with /responses for the same
provider.

codex_output_schema is not listed: in codex-rs it is the name of the text.format
JSON schema, not a top-level key. The table stays limited to keys a client is
observed to send so this does not become an unknown-parameter sanitizer.

Closes #4853
The Astro toolchain had no pull-request build gate. ci.yml contained no
docs-site reference and built no docs, and deploy-docs.yml triggers only on
push to main, which is after promotion. The first machine that could discover a
broken docs build was the deploy, so a dependency bump under docs-site/ could
only be backed by an author's local run.

Adds a docs filter to the existing changes job and one job selected by it.

docs-site/** is deliberately not added to the ci filter: a prose edit has no
business starting the cross-platform matrix, it only has to build. A separate
filter output keeps those two questions apart. The workflow file itself is in
the docs filter so an edit to the job verifies itself, without which this
change's own pull request would skip the job it adds.

One Linux leg. The site is static output from a Node/Bun toolchain with no
OS-specific behaviour to promise, so another platform would spend queue time
without buying coverage. --frozen-lockfile carries as much of the value as the
build does, because it fails on a manifest and lockfile that disagree, which is
the shape a hand-edited override introduces.

The aggregate ci gate is event-aware, so the job is declared in the four places
that have to agree: needs, CHANGES_DOCS, GATED_JOBS, and expected_for. No
existing job's sharding, timeout, or runner selection changes, no Windows leg is
added, and permissions stay contents: read.
…view outcomes (#4892)

The native /responses/compact path spreads the caller's raw body without passing
through the Responses adapter, and is offered only to OpenAI-operated
destinations. Scoping the top-level strip on the canonical surface would have
made openai-apikey behave differently on its two endpoints, so the boundary is
the destination instead. Records why, since the document argued the opposite.

Also records what the two reviews found: the adjacency flag was doing double
duty and would have inserted placeholder tool turns into Kimi conversations,
and the ollama-native replay change has no correctness defect.
* docs(devlog): plan safe teardown and honest settings application [skip ci]

Opens unit 260917_l2_safe_teardown for issues #4812 and #4809.

#4812: the Codex history preflight answers "may I rewrite conversation
history?" and four restore call sites use that answer to decide "may I
remove OpenCodex routing from config.toml?". Since the refusal triggers on
the mere presence of a history_mode column, which every current Codex build
has, the config half is permanently unreachable and uninstall can remove the
proxy while leaving routing pointed at it.

#4809: the two Desktop switches persist and report success without rewriting
the injected config.toml, and report the configured value rather than the
effective one on a bind where the flag is dropped.

The plan records the upstream provider-resolution facts that make a degraded
"strip root routing, retain the provider table" restore safe, and that make
the opposite ordering fatal to config load. No product code changes here.

* fix(codex): restore routing without waiting on paginated history

Closes #4812.

preflightCodexHistoryInjection answers one question — may conversation
history be rewritten — and four restore call sites used that answer to decide
a different one: may OpenCodex routing come out of config.toml. The refusal
triggers on the mere presence of a history_mode column, which every current
Codex build has, so the config half was permanently unreachable. ocx
uninstall removed the proxy and left config.toml pointed at it, and every
subsequent codex invocation failed on a dead port with no supported recovery.

The guard is right about history and is unchanged. It was being asked the
wrong question.

On history_paginated_requires_native_writer, and only that reason, restore
now removes every OpenCodex root routing key, retains
[model_providers.opencodex] verbatim including its ownership marker, and
skips the history relabel rather than attempting it. Every other refusal
reason keeps its hard refusal and compensating rollback untouched.

Retention is what makes the split safe. In codex-rs, Config::load resolves
model_provider_id as (thread override) ?? cfg.model_provider ?? "openai" and
returns NotFound on a miss, which fails the entire config load rather than
one thread; a resumed thread supplies its persisted provider as that
override. Keeping the table therefore leaves tagged conversations openable —
their requests fail against the stopped proxy with an ordinary connection
error — while plain codex returns to the built-in provider. Removing the
table while the root selector survived would break every codex invocation,
so the table is captured before the transform and re-appended into the same
buffer, reaching disk as one write.

The capture cannot reuse removeOcxSection's scan. That one opens a section on
any line carrying the ownership marker, which is safe only because the root
openai_base_url marker has already been consumed by the time it runs. Capture
reads the untouched file, so it anchors on the provider header instead and
pulls in an immediately preceding marker as its comment.

A stand-down can also appear during the write, after the pre-write check said
otherwise. The table is therefore captured unconditionally and re-attached
once after the write, and the history decision is re-asked at the same point
so a Worker whose own preflight is now guaranteed to refuse is never spawned.

Reported as artifacts.config.state "partial" with action
routing-restored-provider-retained, carrying the retained lines and the
follow-up command. historyPreflightRefusal stays unset: it still means
nothing was attempted, and a stop obligation that was discharged must release
its receipt. restore, stop, uninstall, the service subcommands, the stop API
and status all name the retained table and the skipped relabel, so a partial
outcome is never printed as a plain success.

ocx restore --remove-codex-provider-table removes the table for a user who
accepts that tagged conversations stop opening. Nothing selects it
implicitly.

Verification: hosted CI only. The local suite, typecheck, build and the ocx
binary were deliberately not run for this change.

* fix(codex): keep the new teardown helpers off the inject facade

The file-size ratchet caught src/codex/inject.ts at 998 lines against a
committed cap of 987: the previous commit had added nine passthrough
re-exports and a comment to it.

That cap is the right answer here rather than an obstacle. The new helpers
are teardown reporting, owned by inject/remove.ts and inject/restore.ts, and
routing them through the facade added nothing except a second name for each.
The three consumers import from the owning modules instead, and the dangling
comment left behind when HISTORY_RELABEL_STANDS_DOWN moved to
history-provider.ts is removed with it.

No behavior change.

* fix(codex): let an explicit provider-table removal past the history preflight

CI caught it: "paginated sync/async restore removes every root route and
honors removeProviderTable" failed with success false.

removeCodexConfig re-derived the history question with its own preflight and
keyed the refusal on whether the table was being retained, so the one path
that legitimately removes the table on a paginated home — the explicit
--remove-codex-provider-table opt-in — was refused by a check its caller had
already answered.

Retention and the refusal are the same decision seen from two sides, so they
are now one option instead of two booleans. The caller passes down what it
resolved: refuse-on-any, stand-down-retain, or stand-down-remove. A direct
caller that passes nothing keeps the original refusal on every reason, and
only the stand-down reason can be resolved this way — every other reason
still means the history state itself is wrong.

* test(stop): pin the restore success branch by shape, not by one line

CI caught it: the source-oracle assertion in grok-lifecycle.test.ts expected
the literal text "if (result.success) console.log", and that branch grew a
body when a degraded restore had to report the provider table it retained.

Pinned as two assertions instead, plus the preflight-refusal conjunction that
decides whether a stop receipt stays owed. A degraded restore discharged its
config obligation and must not be read as a deferral, so that condition is
now part of what this test holds rather than something it happened to cover.
lidge-jun and others added 14 commits September 17, 2026 22:24
… dropping it (#4890)

* fix(models): reject an invalid custom-model context window instead of dropping it

Carries PR #4863 onto current dev. Typing a k-suffixed value such as 350k into
the Custom Model dialog produced NaN, so the field was dropped on add or sent
as null on edit while the dialog closed with a success toast.

Co-authored-by: liangbo <liangbo.yejc@bytedance.com>

* fix(gui): keep custom context validation within size cap

* Keep the rationale for the custom-model context guard inline

The guard rewrite that fit the file-size ratchet dropped the note explaining why
an invalid draft must not read as "field omitted / override cleared". Restore it
as a trailing comment, which costs no line.

---------

Co-authored-by: liangbo <liangbo.yejc@bytedance.com>
…4875)

* fix(cursor): remint conversation after incomplete tool-call streams (#4874)

Cursor fail-closes a truncated client-tool stream but kept the same
conversation id, so the next turn resumed a session left waiting for
mcpResult. Remint after the streamed error, persist the thread
override, and synthesize a missing tool_result on native Composer
replay. Isolated helper turns stay fail-closed.

* fix(cursor): bound incomplete-tool conversation remints

Keep compaction and isolated turns outside incomplete-tool recovery, cap rotations per retained thread scope, and share the producer message prefix with classification. Preserve the blob-test size ratchet by relocating the existing replay regression.

Co-authored-by: MerryEcho <xx59623633@163.com>

---------

Co-authored-by: JUN <bitkyc08@gmail.com>
…threads (#4871)

* fix(xai): seed Responses tool-result adjacency for interrupted Codex threads

Grok 4.6/4.5 OAuth Responses replays Codex tool history. After a mid-stream
502/reset the client can resend a function_call without its output, or with
hook-injected developer context between the pair. Google already synthesizes
a missing tool_result; xAI did not, so later turns in the same thread 400.

Reuse requiresAdjacentResponsesToolResults (Kimi #4726, DeepSeek #1292) and
run the existing orphan-call placeholder for non-forward adjacency providers.
Do not set statelessResponses: xAI stores responses for 30 days.

Closes #4870

* fix(responses): separate tool-result pairing from adjacency

Gating the missing-output synthesis on requiresAdjacentResponsesToolResults
enrolled kimi and kimi-code in it too. Both carry that flag because their parser
rejects a hook-split pair, but the same report (#4726) shows a call left without
any result is accepted, so they would have started receiving placeholder tool
turns for a shape they never rejected.

Adjacency reorders items the upstream would accept in some order. Pairing
synthesizes an item the client never sent, which is a larger claim about what
happened in the conversation, so it gets its own capability:
requiresPairedResponsesToolResults, seeded on xai only. statelessResponses still
implies it, which is how DeepSeek already had the repair.

Stateful behavior is untouched: store and previous_response_id survive, and
forward auth still suppresses synthesis.

* test(xai): pin the dangling custom tool call through the lowering it actually takes

The previous case declared no custom tool, so collectRoutedCustomToolNames found
nothing and the lowering never ran; it proved only that the repair indexes
custom calls. xAI sets supportsResponsesCustomTools: false, so the production
shape is a declared custom tool that gets lowered, and that is what this now
asserts end to end.

* docs(structure): note the xAI-only pairing capability on the provider page

providers/xai-grok.md co-owns src/providers/ and says the surface retains its existing behavior, which stopped being true once a capability was seeded for xAI alone. Points at chat-compat.md rather than restating the contract in two places.

* fix(server): classify the new pairing capability in the provider field policy

PROVIDER_CONFIG_FIELD_POLICY is satisfies Record<keyof OcxProviderConfig, ...>, so adding a field to OcxProviderConfig without a policy row fails the whole typecheck from a different file. That is what broke gates and the production adapter contract test on Linux and Windows.

editor matches the sibling wire capabilities (statelessResponses, requiresAdjacentResponsesToolResults, annotateEmptyToolOutputs) and matches what the policy means: the field is user-authorable through the leaf validator schema and is seeded from the registry only when the user has not set it. It carries no credential and is not a runtime observation.

---------

Co-authored-by: JUN <bitkyc08@gmail.com>
… they now measure (#4843)

The first round added the instrumentation; this one uses it. Run
35141541461 measured the Windows preparation window at 2740ms and the
Linux/macOS one at 423-575ms, against a reserve that had been guessing at
52.7s. Boot drops 40s to 30s and the Windows preparation reserve 80s to
55s, which takes the case from 265s to 95s while still leaving room for
the 52.7s outlier the reserve was written for: that much preparation
still leaves the flip its full boot budget and its reap.

The same run also showed every readiness wait in native-profile-startup
at 2.0-4.9s on all six Windows shards, first child included, confirming
the 50.7s observation behind #4830 was the ACL ceremony rather than a
cold start. COLD_SPAWN_BUDGET_MS is kept as a bound against the part one
run cannot rule out, with a note to delete rather than raise it.

Co-authored-by: lidge-jun <lidge-jun@users.noreply.github.com>
…f refusing the replay (#4848)

Codex writes mid-turn context items inside a single turn: a PostToolUse hook
verdict from hooks.json, a context notice. One of them routinely lands between
the assistant tool_calls message and that call's own tool result, so
buildNativeMessages saw a conversational message while the batch was open,
flushed it, found a call without a result, and threw:

  ollama-native tool call call_x is missing its tool result; refusing interrupted replay

Codex surfaces that local validation failure as 502 Provider unreachable, and
the item order is part of the persisted thread history, so every later turn of
the affected thread failed the same way and the task could not be resumed.

The chat adapter already repairs this shape: src/adapters/openai-chat/messages.ts
defers barrier messages until the tool round completes, reattaches the real
results to their original call occurrence, and answers an unresolvable call with
an explicit "no tool result was recorded" tool message. The native transport now
does both, request-locally.

The strict pair checks are untouched: an orphan result, a duplicate result for
the same call, and a result naming another tool still throw.

structure/providers/chat-compat.md records that the native wire now carries the
chat wire's deferred-barrier contract, so the two cannot drift apart silently.

Reported in #4842.
)

* feat(responses): add opt-in native WebSocket steering

* fix(responses): preserve steering continuations and register bounded owners

* fix(responses): address native steering review findings

Clear unstarted and superseded channel ownership, append bounded replay without argument spreading, and add six red-to-green regressions. Consolidate architecture notes into short links and document native control helpers. Preserve the maintainer-integrated dev branch without rewriting its history.

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Co-authored-by: lidge-jun <lidge-jun@users.noreply.github.com>
…y after cooling (#4824)

* fix(combos): allow single-target combo with waitForCooldownMs to retry after cooling

* fix(combos): bound single-target cooldown retry and document loop termination

* fix(combos): name single-target cooldown retry and document the wait contract

* fix(combos): correct waitForCooldownMs terminal response in docs
Co-authored-by: 87003697 <870036797@qq.ocm>
Co-authored-by: lidge-jun <lidge-jun@users.noreply.github.com>
…4908)

The file-size ratchet fails on dev: tests/server/server-combo-failover-e2e.test.ts
is 4207 lines against a cap of 4166. Neither contributing change was over the cap
on its own branch. #4824 took the file from 4100 to 4153 and #4817 added 54 lines
that computed to 4154 against the pre-#4824 file, so both were honestly green;
the sum only crossed the cap once both were on dev.

updateBaseline() stores Math.min(cap, lines), so the tool lowers a cap and never
raises one. A GREW offence cannot be cleared by regenerating the baseline, and
raising the number by hand is the one move the ratchet exists to prevent. Move
the newest case into a sibling file instead, as d3ca552 did for the same file.

The test body is moved verbatim. The new file carries only the part of the parent
fixture this case uses: loopback upstreams, an isolated home, and the combo and
request-log state that leaks between tests. It mocks no module, because this case
drives the real openai-responses adapter.

tests/server/server-combo-failover-e2e.test.ts returns to 4153 lines and the
repository scan reports no offender.

Co-authored-by: 404Unkown <52745108+87003697@users.noreply.github.com>
Co-authored-by: agentHits <140916359+agentHits@users.noreply.github.com>
…4858)

* feat(responses): relay native multi-agent function-result injection

Follow up on #4782 with a separate default-off injection owner, bounded serial acknowledgements, same-account caller continuations, accepted-result replay and regression coverage.

* refactor(responses): name the shared native control field nativeControl

The field now holds either a steering or an injection owner, so the generic name matches the NativeResponseControl contract. Move the response-ownership markers next to the shared interface. Behavior is unchanged.

* fix(responses): fail the turn on a native-control attach conflict instead of falling back to HTTP

* test(responses): move the attach-conflict regression into ws-failure-stage

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Co-authored-by: luvs01 <luvs01@users.noreply.github.com>
…ed output (#4861)

* feat(responses): support typed native result continuations and preserve hosted output

Extend #4858 with rich/custom results and explicit approval continuations, execution-mode selection, structural replay matching and lossless sparse-terminal reconciliation. Keep unsupported inject and mixed-mode operations fail-closed.

* fix(responses): reject continuations that omit pinned injection settings

* fix(responses): refund exact injection batch bytes and use site-relative docs link

* test(responses): follow the nativeControl field rename in continuation assertions

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
… replay output (#4911)

* fix(responses): bound steering confirmation waits and preserve sparse replay output

Separate monotonic acknowledgement, successor and tool deadlines; reconcile steering replay with completed wire items without weakening ownership or retry guards. Follow up on #4861.

* fix(server): validate native control settings before superseding the active turn

A malformed response.create frame cancelled the live turn before its steering channel was constructed, so a rejected frame could discard active work without recording a replacement. Build the channel first; only cancel after it validates.

* fix(responses): reject malformed output_index in steering replay

A response.output_item.done frame with a non-safe-integer index matched no branch and was silently dropped from retained output. Validate inside the branch and throw, matching the injection replay observer.

* perf(responses): cache the parsed base frame across steering continuations

sendControl re-parsed the full original frameText for every response.create continuation; a full-replay frame runs to megabytes. Hoist the parse and reuse the immutable base.

* docs: name the canonical steering route and drop duplicated policy text

State that native steering requires the canonical ChatGPT forward route, describe control deadlines as fixed rather than inactivity-based, and reduce the server reference paragraphs to a scope summary with the canonical guide links.

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
… and executable probes (#4912)

* feat(responses): complete safe steering overrides, API transport and executable probe

* docs: clarify steering routes and consolidate control guidance

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 17, 2026 17:37
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-17T17:42:07.255528Z 5061f2c Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot changed the title release: promote dev to main for 2.58.0 [WRONG BRANCH] release: promote dev to main for 2.58.0 Sep 17, 2026
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • wrong target branch (main); retarget to dev. UI screenshot required.

What to do

  • Retarget this PR to dev — all contributions go to dev.
  • Add a screenshot of the UI change to the PR description.

Its title has been prefixed with [WRONG BRANCH].
Automatic draft conversion failed (token cannot change draft status). Please convert this pull request to a draft manually. The required enforce-target check will keep failing until every issue above is resolved.

@github-actions
github-actions Bot marked this pull request as draft September 17, 2026 17:38
@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 0601106a-b69e-4704-b0a8-468c46f10e20

📥 Commits

Reviewing files that changed from the base of the PR and between 44de45d and 5061f2c.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • docs-site/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (233)
  • .github/workflows/ci.yml
  • devlog/_plan/260916_cursor_http2_toolcall/000_plan.md
  • devlog/_plan/260916_cursor_http2_toolcall/010_phase1_text_toolcall_quarantine.md
  • devlog/_plan/260916_cursor_http2_toolcall/020_phase2_observed_max_tokens.md
  • devlog/_plan/260917_2570_release_train/040_release.md
  • devlog/_plan/260917_l1_preview_read_fence_and_dep_audit/000_master_plan.md
  • devlog/_plan/260917_l1_preview_read_fence_and_dep_audit/010_issue_4850_preview_pool_eligibility_fence.md
  • devlog/_plan/260917_l1_preview_read_fence_and_dep_audit/020_pr_4873_dependency_audit_review.md
  • devlog/_plan/260917_l1_preview_read_fence_and_dep_audit/030_unit_c_docs_site_build_gate.md
  • devlog/_plan/260917_l2_safe_teardown/000_master_plan.md
  • devlog/_plan/260917_l2_safe_teardown/010_upstream_resolution_facts.md
  • devlog/_plan/260917_l2_safe_teardown/020_issue_4812_contract.md
  • devlog/_plan/260917_l2_safe_teardown/030_issue_4809_contract.md
  • devlog/_plan/260917_l2_safe_teardown/040_verification.md
  • devlog/_plan/260917_l3_retry_budget_admission/000_roadmap.md
  • devlog/_plan/260917_l3_retry_budget_admission/010_admission_audit.md
  • devlog/_plan/260917_l3_retry_budget_admission/020_independent_review.md
  • devlog/_plan/260917_l4_responses_private_fields_and_history/010_roadmap.md
  • devlog/_plan/260917_l5_cursor_stabilization/000_roadmap.md
  • devlog/_plan/260917_l5_cursor_stabilization/010_u1_text_toolcall_contract.md
  • devlog/_plan/260917_l5_cursor_stabilization/020_u2_observed_window_scope.md
  • devlog/_plan/260917_l5_cursor_stabilization/030_u3_remint_isolation_and_budget.md
  • devlog/_plan/260917_l5_cursor_stabilization/040_u4_codebuddy_bare_tool_names.md
  • devlog/_plan/260917_l6_contract_quality_fixes/000_master_plan.md
  • devlog/_plan/260917_l6_contract_quality_fixes/010_4855_terminal_stop_classification.md
  • devlog/_plan/260917_l6_contract_quality_fixes/020_4822_zai_model_discovery.md
  • devlog/_plan/260917_l6_contract_quality_fixes/030_carried_catalog_and_gui_prs.md
  • devlog/_plan/260917_l6_contract_quality_fixes/040_4857_first_frame_usage_contract.md
  • devlog/_plan/260917_l7_native_control_stack_audit/000_plan.md
  • devlog/_plan/260917_l7_native_control_stack_audit/010_stage_diffs.md
  • devlog/_plan/260917_l7_native_control_stack_audit/020_integration_contract.md
  • devlog/_plan/260917_l7_native_control_stack_audit/030_l1_precondition.md
  • devlog/_plan/260917_l7_native_control_stack_audit/040_activation_decision.md
  • devlog/_plan/260918_2580_release_train/000_roadmap.md
  • docs-site/package.json
  • docs-site/src/content/docs/fr/reference/configuration/providers.md
  • docs-site/src/content/docs/guides/codex-integration.md
  • docs-site/src/content/docs/ja/reference/configuration/providers.md
  • docs-site/src/content/docs/ko/reference/configuration/providers.md
  • docs-site/src/content/docs/reference/configuration/providers.md
  • docs-site/src/content/docs/reference/configuration/routing.md
  • docs-site/src/content/docs/reference/configuration/server.md
  • docs-site/src/content/docs/ru/reference/configuration/providers.md
  • docs-site/src/content/docs/tr/reference/configuration/providers.md
  • docs-site/src/content/docs/zh-cn/reference/configuration/providers.md
  • docs-site/src/content/docs/zh-tw/reference/configuration/providers.md
  • gui/src/pages/Models.tsx
  • gui/tests/models-custom-context-invalid.test.tsx
  • package.json
  • scripts/ci/bun-crash-signatures.sh
  • scripts/ci/run-bun-test-batches.sh
  • scripts/steering-probe.ts
  • scripts/steering-smoke.ts
  • scripts/test-layout/layout.json
  • src/adapters/codebuddy/scaffold-guard.ts
  • src/adapters/command-code.ts
  • src/adapters/cursor.ts
  • src/adapters/cursor/cursor-errors.ts
  • src/adapters/cursor/discovery.ts
  • src/adapters/cursor/live-transport.ts
  • src/adapters/cursor/protobuf-events.ts
  • src/adapters/cursor/protobuf-request.ts
  • src/adapters/cursor/text-toolcall.ts
  • src/adapters/cursor/thread-continuity.ts
  • src/adapters/cursor/types.ts
  • src/adapters/google-http.ts
  • src/adapters/mimo-free.ts
  • src/adapters/ollama-native.ts
  • src/adapters/openai-responses/passthrough.ts
  • src/adapters/openai-responses/request-strips.ts
  • src/adapters/physical-send.ts
  • src/bridge/response-json.ts
  • src/bridge/sse.ts
  • src/claude/outbound.ts
  • src/cli/config-command.ts
  • src/cli/dispatch.ts
  • src/cli/index.ts
  • src/cli/system-command.ts
  • src/cli/uninstall-client-state.ts
  • src/codex/auth-context.ts
  • src/codex/desktop-switches.ts
  • src/codex/history-job.ts
  • src/codex/history-provider.ts
  • src/codex/history-worker.ts
  • src/codex/inject.ts
  • src/codex/inject/remove.ts
  • src/codex/inject/restore.ts
  • src/codex/loopback-target.ts
  • src/codex/native-profile-startup.ts
  • src/config/atomic-write.ts
  • src/config/schema/config-schema.ts
  • src/config/schema/leaf-validators.ts
  • src/lib/bounded-subprocess.ts
  • src/lib/windows-secret-acl.ts
  • src/lib/windows-user-principal.ts
  • src/providers/derive.ts
  • src/providers/model-discovery.ts
  • src/providers/registry/entries-core.ts
  • src/providers/registry/entries-extended.ts
  • src/providers/registry/model-seeds.ts
  • src/providers/registry/types.ts
  • src/responses/spill-store.ts
  • src/responses/state.ts
  • src/responses/state/body-policy.ts
  • src/responses/state/spill-queue.ts
  • src/router.ts
  • src/server/auth-cors.ts
  • src/server/index/websocket-handler.ts
  • src/server/management/config-routes.ts
  • src/server/models-capabilities.ts
  • src/server/responses/codex-ws-exchange.ts
  • src/server/responses/combo-stream-preflight.ts
  • src/server/responses/core-combo.ts
  • src/server/responses/core-options.ts
  • src/server/responses/fetch-helpers.ts
  • src/server/responses/native-injection-protocol.ts
  • src/server/responses/native-injection-replay.ts
  • src/server/responses/native-injection.ts
  • src/server/responses/native-response-control.ts
  • src/server/responses/native-response-json.ts
  • src/server/responses/native-response-output.ts
  • src/server/responses/native-steering-log.ts
  • src/server/responses/native-steering-policy.ts
  • src/server/responses/native-steering-replay.ts
  • src/server/responses/native-steering-settings.ts
  • src/server/responses/native-steering.ts
  • src/server/responses/native-tool-results.ts
  • src/server/responses/passthrough-delivery.ts
  • src/server/responses/passthrough-dispatch.ts
  • src/server/responses/request-prepare.ts
  • src/server/responses/ws-upstream.ts
  • src/server/stop-teardown.ts
  • src/server/ws-bridge.ts
  • src/service/cli.ts
  • src/types/config.ts
  • src/types/provider.ts
  • structure/adapters/registry.md
  • structure/catalog.md
  • structure/clients/claude-desktop.md
  • structure/codex-home.md
  • structure/config.md
  • structure/data-planes/images.md
  • structure/data-planes/inbound-compat.md
  • structure/gui-and-management-api.md
  • structure/ops/docs-and-release.md
  • structure/ops/service-and-sidecars.md
  • structure/overview.md
  • structure/providers/chat-compat.md
  • structure/providers/cursor.md
  • structure/providers/kiro.md
  • structure/providers/xai-grok.md
  • structure/runtime.md
  • structure/subagents.md
  • structure/transports/byte-accounting.md
  • structure/transports/inventory.md
  • structure/transports/responses.md
  • structure/transports/streaming-health.md
  • tests/adapters/bridge.test.ts
  • tests/adapters/google/google-vertex-http.test.ts
  • tests/adapters/physical-send.test.ts
  • tests/ci-workflows/ci-bun-crash-classifier.test.ts
  • tests/ci-workflows/ci-crash-disposition.test.ts
  • tests/ci-workflows/ci-workflows.test.ts
  • tests/ci-workflows/macos-serial-lanes.test.ts
  • tests/ci-workflows/test-runner.test.ts
  • tests/claude-integration/claude-desktop-1m.test.ts
  • tests/claude-integration/claude-outbound.test.ts
  • tests/cli/cli-config-show-client.test.ts
  • tests/cli/cli-headless-parity.test.ts
  • tests/cli/cli-status-json.test.ts
  • tests/cli/uninstall.test.ts
  • tests/clients/client-connect.test.ts
  • tests/codex-integration/codex-composed-acceptance.test.ts
  • tests/codex-integration/codex-history-worker.test.ts
  • tests/codex-integration/codex-inject-integration.test.ts
  • tests/codex-integration/codex-inject.test.ts
  • tests/codex-integration/codex-log-guard-maintenance.test.ts
  • tests/codex-integration/codex-restore-app-rewrite.test.ts
  • tests/codex-integration/codex-sync-api.test.ts
  • tests/codex-integration/codex-write-lock.test.ts
  • tests/codex-integration/model-metadata-sync.test.ts
  • tests/codex-integration/native-main-owner-lifetime.test.ts
  • tests/codex-integration/native-profile-startup.test.ts
  • tests/config/settings-desktop-switch-apply.test.ts
  • tests/config/settings-stream-mode.test.ts
  • tests/fixtures/test-layout-expected.json
  • tests/gui/alibaba-intl-token-plan.test.ts
  • tests/helpers/codex-write-lock-child.ts
  • tests/helpers/history-busy-timeout-preload.ts
  • tests/helpers/native-injection-fixture.ts
  • tests/helpers/native-profile-startup-child.ts
  • tests/helpers/responses-core-source.ts
  • tests/helpers/send-budget-owner.ts
  • tests/helpers/test-budget.ts
  • tests/lib/execution-budget-permits.test.ts
  • tests/lib/stall-subprocess-exit.test.ts
  • tests/lib/transient-budget-scope-source.test.ts
  • tests/providers/codebuddy-adapter.test.ts
  • tests/providers/command-code-provider.test.ts
  • tests/providers/cursor/cursor-adapter.test.ts
  • tests/providers/cursor/cursor-discovery.test.ts
  • tests/providers/cursor/cursor-errors.test.ts
  • tests/providers/cursor/cursor-local-models-schema.test.ts
  • tests/providers/cursor/cursor-protobuf-events.test.ts
  • tests/providers/mimo-free-provider.test.ts
  • tests/providers/ollama/ollama-native.test.ts
  • tests/providers/provider-model-discovery-contract.test.ts
  • tests/providers/provider-registry-parity.test.ts
  • tests/providers/xai/grok-lifecycle.test.ts
  • tests/providers/xai/xai-responses-adjacency.test.ts
  • tests/responses/chat-completions-deferred-tools.test.ts
  • tests/responses/responses-canonical-only-top-level-fields.test.ts
  • tests/responses/responses-core-modules.test.ts
  • tests/responses/responses-preview-main-read-fence.test.ts
  • tests/responses/responses-spill-shutdown-clock.test.ts
  • tests/responses/ws-failure-stage.test.ts
  • tests/responses/ws-native-injection.test.ts
  • tests/responses/ws-native-result-continuations.test.ts
  • tests/responses/ws-native-steering.test.ts
  • tests/responses/ws-steering-completion.test.ts
  • tests/responses/ws-steering-smoke.test.ts
  • tests/responses/ws-steering-stability.test.ts
  • tests/routing/combo-stream-preflight.test.ts
  • tests/routing/probe-lease-dispatch-wiring.test.ts
  • tests/server/cancel-body-on-abort.test.ts
  • tests/server/server-combo-failover-e2e.test.ts
  • tests/server/server-combo-zero-output-failover.test.ts
  • tests/server/server-management-auth.test.ts
  • tests/server/server-startup-reconcile-resilience.test.ts
  • tests/service/stop-deferred-teardown.test.ts
  • tests/storage/storage-worker-teardown-isolate.test.ts
  • tests/web-search/web-search.test.ts
  • tests/windows/windows-acl-start-cost.test.ts

📝 Walkthrough

Walkthrough

This PR bundles multiple independent slices: an experimental native WebSocket steering/injection control stack, Cursor adapter stabilization, a degraded Codex restore path with desktop-switch reporting, CI crash-retry removal plus a docs-site build gate, adapter send-budget admission fixes, provider registry corrections, Windows ACL/subprocess safety, stop-reason and Claude usage fixes, combo retry/main-read-fence hardening, a GUI context-window fix, and a large batch of planning documentation.

Changes

CI Workflow, Test Runner & Docs-Site Build Gate

Layer / File(s) Summary
Docs filter and aggregate gate rewrite
.github/workflows/ci.yml
Adds a docs filter, a docs-site-build job, and rewrites the ci gate to require success from event-requested jobs and skipped from all others.
Crash-retry removal and batch runner rewrite
.github/workflows/ci.yml, scripts/ci/run-bun-test-batches.sh, scripts/ci/bun-crash-signatures.sh
macOS/Windows legs now fail on the first crash instead of retrying. Windows expands to nine shards. The batch runner attributes failing files without recovering the batch.
Crash classifier and test-layout test coverage
tests/ci-workflows/*.test.ts, scripts/test-layout/layout.json, tests/fixtures/test-layout-expected.json
Tests pin no-retry semantics and add explicit test-to-domain mappings for new files.
Docs and misc test-infra hardening
structure/ops/docs-and-release.md, structure/overview.md, tests/cli/cli-status-json.test.ts, tests/storage/*, tests/web-search/*, tests/server/server-startup-reconcile-resilience.test.ts
Documents the new gate behavior; hardens several flaky/platform-gated tests.

Estimated code review effort: 5 (Critical) | ~240 minutes

Native WebSocket Steering & Function-Result Injection

Layer / File(s) Summary
Config flags and shared contracts
src/types/config.ts, src/config/schema/config-schema.ts, src/server/responses/native-response-control.ts, native-tool-results.ts, native-response-output.ts, native-response-json.ts
Adds default-off codexNativeSteering/codexNativeInjection flags and the shared control/tool-result/output-reconciliation contracts.
Steering channel implementation
src/server/responses/native-steering*.ts
NativeSteeringChannel owns one private physical connection per turn with absolute per-phase deadlines and a replay journal.
Injection channel implementation
src/server/responses/native-injection*.ts
NativeInjectionChannel queues one in-flight saved-result submission at a time with acknowledgement matching and terminal reconciliation.
Server wiring
src/server/index/websocket-handler.ts, src/server/ws-bridge.ts, src/server/responses/codex-ws-exchange.ts, ws-upstream.ts, fetch-helpers.ts, passthrough-dispatch.ts, passthrough-delivery.ts
Threads the native control channel through the WebSocket handler, exchange, dispatch, and delivery paths, including untilEof SSE draining.
Docs
docs-site/src/content/docs/guides/codex-integration.md, docs-site/.../reference/configuration/server.md, structure/**/*.md
Documents both control paths, their limits, and cross-references the shared contracts across many structure/ ownership pages.
Tests and probe scripts
tests/responses/ws-native-*.test.ts, ws-steering-*.test.ts, scripts/steering-smoke.ts, tests/helpers/native-injection-fixture.ts
Adds an extensive test suite plus offline/live steering-probe scripts.

Cursor Adapter Stabilization

Layer / File(s) Summary
Text tool-call quarantine module
src/adapters/cursor/text-toolcall.ts
Drains [TOOL_CALL]name[ARGS]{json} markers with a byte-capped suppressed scan for oversized/split markers.
Incomplete-tool detection and observed context window
src/adapters/cursor/cursor-errors.ts, discovery.ts
Adds isCursorIncompleteToolCallMessage and an identity-scoped observed maxTokens cache for inferCursorContextWindow.
Protobuf event buffering
src/adapters/cursor/protobuf-events.ts
Buffers advertised text tool calls until finalize, flushing only when no real client tool call was recorded that turn.
Remint budget, live-transport, and orchestration
src/adapters/cursor/thread-continuity.ts, live-transport.ts, cursor.ts, types.ts
Adds an independent bounded remint counter for incomplete-tool streams and threads identity scope through the request pipeline.
Missing tool-result placeholder
src/adapters/cursor/protobuf-request.ts
Synthesizes a marked error result for a pending native Composer tool call with no matching result.
Docs and tests
structure/providers/cursor.md, tests/providers/cursor/*.test.ts
Documents the quarantine/observed-window/remint boundaries with matching regression tests.

Codex Degraded Restore, Desktop Switches & Config CLI

Layer / File(s) Summary
History provider constant and busy-timeout plumbing
src/codex/history-provider.ts, history-job.ts, history-worker.ts
Extracts HISTORY_RELABEL_STANDS_DOWN as a shared constant and threads the DB busy timeout across the process/Worker boundary.
Provider-table capture and retention
src/codex/inject/remove.ts
Captures and re-attaches [model_providers.opencodex] on a stand-down-retain disposition instead of failing removal.
Restore contract: partial state and disposition resolver
src/codex/inject/restore.ts
Adds "partial" artifact state, resolveRestoreHistoryDisposition, and --remove-codex-provider-table across sync/async restore.
Desktop switch reporting
src/codex/desktop-switches.ts, src/server/management/config-routes.ts
Reports stored/effective/applied state and runs real injection after catalog convergence and lock release.
CLI and service surfaces
src/cli/dispatch.ts, index.ts, system-command.ts, uninstall-client-state.ts, service/cli.ts, stop-teardown.ts, codex/auth-context.ts, codex/native-profile-startup.ts, cli/config-command.ts
Surfaces retained-table warnings and the new flag; extracts requestOwnedMainPinState; tracks startup-release promises for drain.
Docs and tests
structure/codex-home.md, structure/config.md, tests/codex-integration/*.test.ts, tests/config/*.test.ts
Documents and tests the degraded-restore and desktop-switch behavior end to end.

Adapter Physical-Send Budget Admission & Retry Fixes

Layer / File(s) Summary
createAdapterPhysicalSend contract
src/adapters/physical-send.ts
Enforces single physical dispatch per invocation with admission before pacing/backoff and permit release on refusal.
Adapter integrations
src/adapters/command-code.ts, google-http.ts, mimo-free.ts, ollama-native.ts
Routes retries through the helper; fixes mimo-free's body-drain-before-refresh ordering; defers/synthesizes ollama-native tool-batch messages.
openai-responses passthrough
src/adapters/openai-responses/passthrough.ts, request-strips.ts
Strips access_programs for non-OpenAI-operated destinations and widens missing-output synthesis via requiresPairedResponsesToolResults.
Docs and tests
docs-site/.../providers.md (8 locales), tests/adapters/*.test.ts, tests/providers/*.test.ts
Clarifies retry-policy scope per locale; adds budget/permit and adapter regression tests.

Provider Registry, Model Discovery & Capability Metadata

Layer / File(s) Summary
Registry entries and discovery envelope
src/providers/registry/*.ts, model-discovery.ts
Adds envelopeKey/idField discovery config, a Z.AI spec, requiresPairedResponsesToolResults on xAI, and refreshed Alibaba Token Plan model seeds.
CodeBuddy bare tool-name guard
src/adapters/codebuddy/scaffold-guard.ts
Drops the functions. prefix requirement so bare tool names are flagged.
Top-level capability mirrors
src/server/models-capabilities.ts
Mirrors context/output-token capacities to the top level of /v1/models rows.
Docs and tests
structure/providers/chat-compat.md, xai-grok.md, tests/providers/*.test.ts, tests/gui/alibaba-intl-token-plan.test.ts
Documents and tests the roster and discovery changes.

Windows ACL Hardening & Subprocess Reap Safety

Layer / File(s) Summary
Deadline scheduler and ACL memoization
src/lib/bounded-subprocess.ts, windows-secret-acl.ts, windows-user-principal.ts
Adds an injectable deadline scheduler, memoizes ACL hardening across a write, and tracks pending async icacls reaps per path.
Atomic-write and cleanup gating
src/config/atomic-write.ts, src/cli/uninstall-client-state.ts, tests/windows/*.test.ts, tests/lib/stall-subprocess-exit.test.ts
Refuses removal while ACL hardening still owns a path; raises AtomicWriteResidualTempError instead of silently retrying.

Response Spill Clock & State Body-Policy

Layer / File(s) Summary
Spill clock unification and body-policy extraction
src/responses/spill-store.ts, state/spill-queue.ts, state.ts, state/body-policy.ts
Shares one injectable clock between shutdown-drain deadlines and ACL-hardening budgets; extracts body-persistence tracking.

Stop-Reason Classification & Claude Usage Reporting

Layer / File(s) Summary
Terminal stop-reason classification
src/bridge/response-json.ts, src/bridge/sse.ts
Uses isTruncatedStopReason instead of truthiness so any non-truncation reason counts as final_answer.
Claude early usage snapshot
src/claude/outbound.ts
Uses confirmed pre-content usage in message_start when a response.in_progress usage frame arrives first.

Combo Retry/Failover & Preview Main-Read Fence

Layer / File(s) Summary
Combo preflight bare-error classification
src/server/responses/combo-stream-preflight.ts
Classifies zero-output bare errors by structured status/retryability instead of exact-message matching.
Single-target cooldown retry
src/server/responses/core-combo.ts
Re-picks the sole target once its cooldown expires.
Preview main-read fence
src/codex/auth-context.ts, src/server/responses/request-prepare.ts
Extracts requestOwnedMainPinState and applies it to preview/recovery selection.

GUI Custom Model Context Window Validation

Layer / File(s) Summary
Context-window parsing fix
gui/src/pages/Models.tsx, gui/tests/models-custom-context-invalid.test.tsx
Routes context-window input through parseContextWindowDraft, blocking invalid values instead of silently dropping them.

Planning & Devlog Documentation

Layer / File(s) Summary
Devlog plan documents
devlog/_plan/**/*.md, package.json, docs-site/package.json
Adds a large batch of planning/audit documents and bumps the package version to 2.58.0 and the astro/sharp/svgo dependencies.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant WebSocketHandler
  participant NativeSteeringChannel
  participant CodexWsExchange
  participant CodexUpstream

  Client->>WebSocketHandler: response.create (steering-eligible)
  WebSocketHandler->>NativeSteeringChannel: construct control after admission
  WebSocketHandler->>CodexWsExchange: attach nativeControl
  CodexWsExchange->>CodexUpstream: physical WebSocket send
  Client->>WebSocketHandler: response.steer
  WebSocketHandler->>NativeSteeringChannel: steer(frame)
  NativeSteeringChannel->>CodexUpstream: send steer frame
  CodexUpstream-->>NativeSteeringChannel: response.steer.accepted
  CodexUpstream-->>CodexWsExchange: response.completed
  CodexWsExchange-->>WebSocketHandler: relay terminal (untilEof)
  WebSocketHandler-->>Client: SSE terminal frame
Loading
sequenceDiagram
  participant CLI
  participant RestoreFlow as inject/restore.ts
  participant HistoryPreflight as history-provider.ts
  participant RemoveConfig as inject/remove.ts

  CLI->>RestoreFlow: restoreNativeCodexAsync()
  RestoreFlow->>HistoryPreflight: preflightCodexHistoryInjection()
  HistoryPreflight-->>RestoreFlow: HISTORY_RELABEL_STANDS_DOWN
  RestoreFlow->>RestoreFlow: resolveRestoreHistoryDisposition() -> stand-down
  RestoreFlow->>RemoveConfig: removeCodexConfig({historyDisposition:"stand-down-retain"})
  RemoveConfig-->>RestoreFlow: retained provider table lines
  RestoreFlow-->>CLI: state="partial", action="routing-restored-provider-retained"
Loading
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lidge-jun
lidge-jun marked this pull request as ready for review September 17, 2026 17:40
@lidge-jun
lidge-jun merged commit 6fe4cd0 into main Sep 17, 2026
100 of 139 checks passed
@lidge-jun
lidge-jun deleted the release/2.58.0 branch September 17, 2026 17:40
@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 77 / 80

이 PR은 기능을 새로 넣는 요청이 아니다. release/2.58.0 브랜치를 main으로 올리는 2.58.0 프로모션이다. 프로모션 head SHA는 5061f2c956145ec5cbf76b259cf35c312afa9301이고, origin/release/2.58.0과 같다. 그 직후 dev에는 #4916이 머지되어 HEAD가 4655d32f88385608769c93c7f7ac79ed0056d68d로 한 칸 앞섰다. 즉 후보 tip은 의도적으로 얼려 두고, 살아 있는 dev만 2.59.0으로 연 상태다. package.json은 release 후보가 2.58.0, 현재 dev가 2.59.0, main은 아직 2.57.0이다.

제목에 [WRONG BRANCH]가 붙어 있고 enforce-target은 FAILURE다. 이건 버그가 아니라 프로모션의 약속된 빨간 표시다. 게이트는 기능 PR이 dev를 base로 쓰게 막는 장치라서, main을 base로 쓰는 프로모션은 매번 같은 빨간 표를 받는다. 본문도 2.57.0·2.56.0 프로모션과 같다고 적었고, 트레인 문서 devlog/_plan/260918_2580_release_train/000_roadmap.md에도 enforce-target red by design이라고 적혀 있다. 그래서 enforce-target 하나만으로 이 PR을 닫거나 rebase하라고 하면 안 된다.

지금 API 기준으로 이 PR은 draft다. mergeable은 true로 보이지만 mergeable_state는 blocked다. 필수 product/test·CodeQL·CodeRabbit 등이 아직 pending인 상태와 맞다. 본문은 후보 SHA에서 전 플랫폼 회귀가 이미 초록이었다고 주장한다. 그건 이 PR 자체의 rollup과 별개일 수 있으니, 머지 직전에는 후보 SHA 5061f2c956의 hosted CI 증거와 이 PR head의 필수 체크가 둘 다 안정적인지 확인하는 편이 안전하다. 트레인 규칙대로 로컬 suite/typecheck/build/ocx는 게이트가 아니다. 취소된 잡은 통과도 실패도 아니다.

mainrelease/2.58.0은 GitHub compare상 diverged(ahead 53 / behind 99)로 보인다. behind 쪽은 예전 프로모션 merge 커밋들이 main에만 쌓인 이력이다. 2.57.0 프로모션 tip d78be30da는 release 조상이고, 이번에 올라가는 53커밋은 dev에 쌓였던 2.58.0 라인이다. 제품 트리가 main의 옛 릴리스 merge 그래프와 선형으로 같아 보이지 않는 것은 정상이다.

트레인 순서의 open-dev 단계는 이미 끝났다. #4916(chore(release): open dev at 2.59.0 before releasing 2.58.0)이 4655d32f8로 머지됐다. 2.57.0 때 #4827을 먼저 처리했던 패턴과 같다. release.yml이 publish 전에 assert-ahead로 dev가 새 태그보다 앞서 있기를 요구하는 전제는 지금 충족된 상태다. 남은 게이트는 draft→ready, 필수 CI 안정, 그다음 이 프로모션 머지다.

트레인 문서가 들고 가는 알려진 잔여물도 그대로다. #4800은 여전히 OPEN이고, Responses passthrough에서 그 재시도 정책이 닿지 않는다는 판정이 있다. 네이티브 컨트롤 스택(src/server/responses/native-steering-settings.ts / native-steering-policy.ts / native-injection.ts 등)은 후보 tip에 있고 기본 off라, 옵션을 켜지 않은 설치에는 행동 변화가 없다. types.ts/config.ts 스플릿 캠페인은 이번 release chore에는 해당 없다. 이미 얼린 트리를 main으로 옮기는 일이다.

라인 단위로 “이 줄이 틀렸다”고 찍을 제품 패치는 없다. 아래는 경로·게이트·전제 위주의 점검이다.

경로 release/2.58.0 → main - 프로모션 base/head가 맞고, head SHA 5061f2c956가 얼린 2.58.0 후보 tip이다.
경로 package.json - 후보=2.58.0, 현재 dev(#4916 후)=2.59.0, main=2.57.0. bump 방향이 트레인과 맞다.
경로 src/server/responses/native-steering-settings.ts · native-steering-policy.ts · native-injection.ts - 후보 tip에 존재. default-off 네이티브 스택 라인과 맞다.
경로 devlog/_plan/260918_2580_release_train/000_roadmap.md - 순서 freeze → 전플랫폼 회귀 → open-dev → promote → publish → preview.
게이트 enforce-target - FAILURE는 예상된 WRONG BRANCH 표시. 기능 PR 기준로 해석하면 오판.
상태 draft - 아직 ready가 아니다. draft를 풀고 필수 체크가 안정되기 전에 머지하지 말 것.
전제 #4916 - 이미 MERGED(4655d32f8). open-dev/assert-ahead 전제는 충족.
잔여 #4800 - OPEN이지만 트레인 문서상 이번 라인에 묶지 않는 알려진 항목. 프로모션 차단 사유로 보지 않는다.
CI rollup - product/test·CodeQL 등이 아직 진행 중. 완료·증거 재확인 전에 머지하지 말 것.

메인테이너의 판단이 필요한 지점

  • draft를 ready로 올린 뒤 후보 5061f2c956 hosted CI 증거를 publish 직전에 한 번 더 볼지.
  • enforce-target 빨간 표를 무시하고 프로모션을 진행할지(이전 2.55/2.56/2.57과 동일 패턴).
  • 프로모션 머지 후 태그/release.yml(expected-sha)·preview 프로모션·registry 확인을 누가 소유할지.
  • fix(retry): extend transient-5xx replay budget to openai-responses passthrough #4800 계열을 2.58.0 밖으로 남길지, 아니면 별도 핫픽스 라인으로 잡을지.

너의 추천
이 PR을 draft에서 ready로 올린 뒤 필수 product CI가 후보 tip SHA에서 안정되면 머지한다. enforce-target FAILURE만으로 닫거나 rebase하지 않는다. #4916은 이미 끝났으니 open-dev를 다시 기다릴 필요는 없다. types/config 스플릿 캠페인 이유로 이 chore를 반려할 필요는 없다. 머지 후에는 v2.58.0 태그·release.yml expected-sha=5061f2c956(또는 main merge commit 규칙에 맞는 SHA)·preview 프로모션·registry 반영을 바로 확인한다.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 76 / 80

이 PR은 기능을 새로 넣는 요청이 아니다. release/2.58.0 브랜치를 main으로 올리는 2.58.0 프로모션이다. 지금 로컬에서 갱신한 dev HEAD는 4655d32f88385608769c93c7f7ac79ed0056d68d(#4916 open-dev 머지 후)이고, 이 PR의 head SHA는 프리즈 후보 5061f2c956145ec5cbf76b259cf35c312afa9301이다. package.json은 후보/release 쪽이 2.58.0, 현재 dev tip은 이미 2.59.0, main은 아직 2.57.0이다.

제목에 [WRONG BRANCH]가 붙어 있고 enforce-target은 FAILURE다. 이건 버그가 아니라 프로모션의 약속된 빨간 표시다. 게이트는 기능 PR이 dev를 base로 쓰게 막는 장치라서, main을 base로 쓰는 프로모션은 매번 같은 빨간 표를 받는다. 본문도 2.57.0·2.56.0 프로모션과 같다고 적었고, 트레인 문서 devlog/_plan/260918_2580_release_train/000_roadmap.md에도 enforce-target red by design이라고 적혀 있다. 그래서 enforce-target 하나만으로 이 PR을 닫거나 rebase하라고 하면 안 된다.

지금 API 기준으로 이 PR은 draft다. 일부 resolve-pr/CodeQL 등은 진행·성공이 섞여 보인다. 본문은 후보 SHA에서 전 플랫폼 회귀가 이미 초록이었다고 주장한다. 그건 이 PR 자체의 rollup과 별개일 수 있으니, 머지 직전에는 후보 SHA 5061f2c956의 hosted CI 증거와 이 PR head의 필수 체크가 둘 다 안정적인지 확인하는 편이 안전하다. 트레인 규칙대로 로컬 suite/typecheck/build/ocx는 게이트가 아니다.

트레인 순서상 프로모션·publish 전에 dev 버전 라인을 다음 minor로 여는 단계가 있다. 그 역할의 #4916(chore(release): open dev at 2.59.0 before releasing 2.58.0)은 이미 머지되어 tip이 4655d32f8·패키지 2.59.0이 되었다. 2.57.0 때 open-dev를 먼저 처리했던 패턴과 같다. 이제 assert-ahead/publish 앞의 open-dev 게이트는 채워진 상태다.

트레인 문서가 들고 가는 알려진 잔여물도 그대로다. #4800은 여전히 OPEN이고, Responses passthrough에서 그 재시도 정책이 닿지 않는다는 판정이 있다. 네이티브 컨트롤 스택(native-steering-settings / native-steering-policy / native-injection 등)은 tip에 있고 기본 off라, 옵션을 켜지 않은 설치에는 행동 변화가 없다. types.ts/config.ts 스플릿 캠페인은 이번 release chore에는 해당 없다. 이미 dev에 쌓인 트리를 main으로 옮기는 일이다.

라인 단위로 “이 줄이 틀렸다”고 찍을 제품 패치는 없다. 아래는 경로·게이트·전제 위주의 점검이다.

경로 release/2.58.0 → main - 프로모션 base/head가 맞고, head SHA가 프리즈 후보 5061f2c956이다.
경로 package.json - 후보/release=2.58.0, 현재 dev=2.59.0(#4916), main=2.57.0. 프로모션이 올리는 버전 방향은 맞다.
경로 src/server/responses/native-steering-settings.ts · native-steering-policy.ts · native-injection.ts - 후보 tip에 존재. 본문이 말한 default-off 네이티브 스택 라인과 맞다.
경로 devlog/_plan/260918_2580_release_train/000_roadmap.md - 트레인 순서가 freeze → 전플랫폼 회귀 → open-dev → promote → publish → preview다. open-dev(#4916)는 완료.
게이트 enforce-target - FAILURE는 예상된 WRONG BRANCH 표시. 기능 PR 기준로 해석하면 오판.
상태 draft - 아직 ready가 아니다. draft를 풀고 필수 체크가 안정되기 전에 머지하지 말 것.
전제 #4916 - MERGED. dev 2.59.0 open 완료. assert-ahead/publish 순서의 open-dev 칸은 채워짐.
잔여 #4800 - OPEN이지만 트레인 문서상 이번 라인에 묶지 않는 알려진 항목. 프로모션 차단 사유로 보지 않는다.
CI rollup - 일부 체크 진행 중. 완료·증거 재확인 전에 머지하지 말 것.

메인테이너의 판단이 필요한 지점

너의 추천
open-dev(#4916)는 이미 끝났으니, 이 PR을 draft에서 ready로 올린 뒤 필수 product CI가 프리즈 SHA에서 안정되면 머지한다. enforce-target FAILURE만으로 닫거나 rebase하지 않는다. types/config 스플릿 캠페인 이유로 이 chore를 반려할 필요는 없다. 머지 후에는 v2.58.0 태그·release.yml expected-sha·preview 프로모션·registry 반영을 바로 확인한다.

이 댓글은 grok-bot이 작성했습니다

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants