Skip to content

codex-restart quits the desktop shell where upstream restarts only the app-server, discarding unsaved client state #4761

Description

@lidge-jun

Client or integration

Codex App

Area

Service lifecycle

Summary

ocx system codex-restart quits and relaunches the whole Codex desktop shell. Upstream's own restart operation does not: codex app-server daemon restart stops and starts only the app-server process it manages, refuses to act when the responding app-server is not daemon-managed, and waits for the replacement to pass a readiness probe. Upstream treats a server restart as something clients survive — SIGTERM means "graceful restart drain", the acceptor stops once running turns reach zero, and connected clients reconnect.

Because we terminate the desktop shell instead, everything that lived only in the client is discarded: an unsaved model-picker selection, an in-progress composer draft, and any approval prompt waiting for the user. The desktop restart path finds root Codex shell processes, asks them to quit, force-stops survivors, and relaunches the app; it never starts or readiness-probes an app-server itself and relies on the relaunched desktop to create replacements.

This is not a regression and not a defect in the current implementation. That code is careful: it revalidates process start times before signalling, refuses uncertain app-server identities, and is gated behind an explicit --yes. The scope is not gratuitous either — model_catalog_json is startup-only upstream, so a catalog change genuinely cannot be picked up by a running desktop without restarting it, and config/batchWrite.reloadUserConfig reapplies only reloadable settings and is documented as neither a process restart nor a catalog reload.

The debt is that the only tool offered for "pick up the new catalog" is the most destructive one available, and the user pays unsaved client state for it every time. Worth considering: detect whether the catalog actually changed and skip the desktop restart when it did not; restart app-servers alone when that suffices; or name the specific unsaved desktop state at risk before quitting rather than describing the scope generically.

Expected: refreshing the catalog should not cost the user their unsaved desktop state unless that is genuinely the only way to apply the change, and the command should say so when it is.

Reproduction

  1. Start the proxy and connect Codex desktop: ocx start --port 10100.
  2. In the Codex desktop app, open a thread and leave unsaved client-side state: type a composer draft without sending it, change the model in the picker without starting a turn, and leave an approval prompt open.
  3. Run ocx system codex-restart --yes.
  4. The desktop app fully quits and relaunches. The draft, the unsent picker selection, and the pending approval are gone.

Compare with upstream codex app-server daemon restart, which restarts only the managed app-server and leaves the desktop client running to reconnect.

Observed against the current restart path in src/codex/app-server-restart-service.ts, src/codex/desktop-app-restart.ts, and src/server/management/system-routes.ts. The upstream contract is in codex-rs/app-server-daemon/src/lib.rs and codex-rs/app-server/src/lib.rs.

Version

dev at 3070d64

Operating system

macOS 27.0 arm64 (the desktop-restart path is macOS and Windows specific)

Provider and model

No response

Logs or error output

Screenshots and supporting files

No response

Redacted configuration

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingserviceService lifecycle (WinSW/launchd/scheduler)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions