Repository navigation
Proposal: add explicit thread/unload app-server RPC #21889
Description
What variant of Codex are you using?
CLI / app-server
What feature would you like to see?
I would like app-server to expose an explicit thread/unload RPC.
Problem
thread/unsubscribe is currently a delayed unload path, not an immediate unload primitive. The
README says a thread remains loaded until it has had no subscribers and no activity for 30 minutes.
That is reasonable for reconnect-friendly clients, but app-server is also a multi-thread host:
one runtime may keep many unrelated threads loaded at once. In that model, clients need a narrow
way to unload one specific loaded thread immediately, without resetting the whole backend and
disturbing other loaded threads.
This is mainly about thread-scoped settings whose contract is "takes effect on next load". Today
the choices are:
- wait for delayed idle cleanup
- reset the entire backend to affect one thread
This is not a request to hot-update all memory behavior on an already loaded thread.
thread/memoryMode/set already covers persisted future memory-generation eligibility. The gap is a
per-thread unload boundary for settings whose effect is bound at thread load / session
construction.
Concrete use case
Suppose thread A is loaded but idle, and threads B/C are also loaded in the same app-server
runtime. A client updates some next-load-only setting for thread A and wants the next attach/resume
of A to pick it up immediately. Waiting 30 minutes is too slow, but resetting the backend would
also disrupt B/C. A narrow thread/unload would solve exactly that case.
Proposed API
A separate thread/unload RPC would keep the contracts clear:
thread/unsubscribe: subscription / reconnect semanticsthread/unload: explicit operator action
Suggested conservative first version:
- method:
thread/unload - params:
threadId - response statuses:
unloaded,notLoaded,active,otherSubscribers
Suggested behavior:
notLoaded: already not loaded; no-opactive: refuse unload while the thread is activeotherSubscribers: refuse unload if another connection is still subscribedunloaded: shutdown completed and the thread transitioned tonotLoaded
Suggested notifications:
- reuse existing
thread/closed - reuse existing
thread/status/changedtonotLoaded
Suggested call sequence
- Client persists a next-load-only setting for thread A.
- Client calls
thread/unload(threadId=A). - Server refuses if A is active or still has other subscribers.
- Otherwise server unloads A and emits the existing close/status notifications.
- The next attach/resume of A loads a fresh session and picks up the new setting.
Why not the alternatives
I do not think changing thread/unsubscribe to mean immediate unload is the right path, especially
since there is already adjacent upstream work around unsubscribe/unload timing and reconnect
behavior (#10954, #17398, #20487).
I also do not think config/batchWrite with reloadUserConfig: true replaces this need. Runtime
config reload is useful for refreshable user-layer settings, but it is not the same thing as a
per-thread unload boundary for next-load-only behavior.
I also do not think the internal unload_thread_without_subscribers(...) helper should be exposed
directly as-is, because it appears to be a background idle-cleanup path rather than a
request/response-quality explicit unload API.
Questions
- Would maintainers accept a separate
thread/unloadRPC? - Should the first version be experimental-only?
- Is "idle + no other subscribers" the right initial guardrail?
Additional information
I reviewed upstream main on 2026-05-09 and did not find an existing public thread/unload RPC.
I also did not find an open or merged PR whose primary goal was to add that API, though I did find
adjacent thread lifecycle work in #10954, #14137, #14997, #17398, and #20487.
If this direction is interesting, I can provide a focused implementation plan or prepare a patch
after maintainer guidance.
Activity
Irvinwop commentedon Sep 8, 2026
Adding a Codex Desktop use case for this request: please expose an explicit Release task runtime / Reload on next resume action in the task UI, backed by a task-scoped unload operation.
While troubleshooting a custom provider configuration on macOS, correcting the endpoint in the user configuration did not update an already-loaded but idle task's cached provider. Returning to that task resumed the existing runtime. Restarting the entire app would also interrupt unrelated work, and the UI did not offer a way to close/release just that task.
Navigating away is not an immediate release operation. In the installed Desktop environment, inactive tasks remained subscribed for roughly an hour; app-server's documented unsubscribe behavior can then retain an unsubscribed idle task for another 30 minutes. This is context from a customized local setup, not a claim of a newly reproduced stock-build regression.
Suggested UX and safety requirements:
This would make recovery from stale per-task runtime state practical without requiring a whole-app restart or a long, opaque inactivity wait.
nm-z commentedon Oct 7, 2026
Another use case from codex-cli 0.160.1: a local agent manager runs many TUIs against the managed daemon (
codex --remote unix://.../app-server-control.sock). To relaunch an idle thread with a different model, effort or service tier, it has to get the thread unloaded first.thread/unsubscribeonly removes the calling connection, so once the original TUI has exited, nothing can unload the thread short of restarting the whole daemon, which interrupts every other thread's turn. An explicitthread/unload(refused while a turn is running) would let a manager swap one idle thread's settings without touching the rest.rmorse commentedon Oct 15, 2026
Facing the same issue - using codex app server - and if I need to change UI, switch to the TUI, I can't because the session is locked, even though the previous subscriber process is completely terminated.
cbusillo commentedon Oct 16, 2026
I hit this with codex-cli 0.163.0 while coordinating agent ownership. After a TUI quit and released its claim, a successor still saw the old thread as idle in the shared daemon. It reached notLoaded around the normal 30-minute expiry without a restart.
A guarded unload would help, provided it refuses any attached client, running turn, pending approval/input or queued work. thread/unsubscribe from a new observer only affects that observer's connection. thread/read exposes no subscriber presence, so an external coordinator cannot safely distinguish an idle cached runtime from an idle client with unsent input.
A read-only subscriber-presence field would also make that distinction observable. I traced this against the 0.163.0 lifecycle timer, caller-scoped unsubscribe, and Thread read schema.
K0enna commentedon Oct 18, 2026
Measured unsubscribe controls on the installed Windows backend
I tested the unchanged Desktop-bundled codex-cli 0.163.0-alpha.10.2 from package 26.1013.4894.0 in isolated diagnostic homes, with one owned quiet stdio MCP process and no model generation turn.
After this client's successful
thread/unsubscribe:thread_unload_delay_secsin diagnostic homeThe subscribed controls retained their context and MCP process while observed. In every unsubscribe case, the MCP process exited before backend closure and the backend remained alive after cleanup. In the full default-delay case, context removal and MCP exit were both observed at 1,800.016 s; the backend subsequently exited normally, with no fallback termination or diagnostic survivors.
These are observed timings from individual cases, not repeated latency statistics. The default-delay run independently confirms 30-minute delayed cleanup in this installed binary; a 60-second assumption from another version would have misclassified it. No authentication was copied into these homes; WebSocket warm-up returned 401, separate from the lifecycle checks.
This supports the distinction in this proposal:
thread/unsubscribereleases this client's subscription and allows delayed cleanup; it is not an immediate operator unload primitive. It does not prove cleanup under blocking-pool saturation, identify every Desktop helper's owner, or establish safe unloading when another client or background work still needs the thread. No live global unload-delay setting was changed.Related installed-backend and pipe-pressure evidence.
Krarilotus commentedon Oct 19, 2026
Data point from a multi-agent orchestration host using one shared
codex app-server(codex-cli 0.163.0, Windows 11):thread/closedand MCP process exit followed another 30 minutes later.A model-free control used a stub stdio MCP server owning one child and a throwaway
CODEX_HOMEwith server-widethread_unload_delay_secs = 20:thread/startat +0.4 s,thread/unsubscribeat +1.4 s, thenthread/closedand both MCP processes gone at about +23.4 s. Hard-killing a separate test app-server also ended its MCP tree. Teardown worked in these Windows tests; the practical problem was retention time.#55270 is merged and honours per-thread
thread_unload_delay_secs, with0permitting immediate cleanup once there are no subscribers or activity. That appears to cover our retention-control need in versions containing the change; we have not tested that per-thread path on the observed 0.163.0 binary. The host would also need to unsubscribe promptly.An explicit guarded
thread/unload, as proposed here, would still provide a clearer request/response contract for a host that knows an idle thread's task is finished, while preserving history and later resume.