codex - 💡(How to fix) Fix `/stop <id>` to terminate a single background terminal [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openai/codex#17821Fetched 2026-04-15 06:27:34
View on GitHub
Comments
1
Participants
2
Timeline
5
Reactions
0
Author
Timeline (top)
labeled ×3commented ×1unlabeled ×1

Error Message

  • /stop <unknown-id> — friendly error, no side effects.
  • /stop <ambiguous-prefix> — ambiguity error listing how many match, no side effects.
  1. codex-core — add UnifiedExecProcessManager::terminate_process(i32) -> bool, a session wrapper, a submission-loop handler, and a dispatch arm that emits EventMsg::Error when the id is unknown or cannot be parsed.
  • Con: aliases drift as processes start/stop, so /stop 1 depends on the most recent /ps output, which is subtle and error-prone.
  1. Zero matches → error ("No background terminal matching {arg}. Run /ps to see ids.").
  2. Multiple matches → error ("Ambiguous id {arg}: matches N terminals, be more specific.").
  3. Ambiguity handling. Should a 2-match prefix error out (my suggestion), or deterministically stop the first one and warn? I chose the strict option because silent "pick one" behavior is surprising.

Root Cause

  1. Id format. Option A vs B above, or something else (e.g. a short hex slug instead of the raw decimal prefix). Is there a house style here?
  2. Ambiguity handling. Should a 2-match prefix error out (my suggestion), or deterministically stop the first one and warn? I chose the strict option because silent "pick one" behavior is surprising.
  3. app-server JSON-RPC surface. The TUI routes ops through thread/backgroundTerminals/clean, so adding /stop <id> end-to-end needs either (a) a new thread/backgroundTerminals/stop { threadId, processId } method, or (b) extending clean with an optional processId. Separate method feels cleaner and keeps clean wire-stable, but I'd like the team's call on this.
  4. /clean <id> alias. /clean is currently a silent no-op on inline args. If Stop opts into inline args, /clean a1b2c3 would start working automatically. This is almost certainly fine but worth flagging so it's not a surprise.
  5. No JSON-RPC surface, TUI-only? If the team would rather scope this to the TUI only (no new app-server method), I'd need to know because TUI ops round-trip through the app-server in production, so "TUI-only" would require a different plumbing strategy.

Fix Action

Fix / Workaround

  1. codex-protocol — add an Op::StopBackgroundTerminal { process_id: String } variant next to CleanBackgroundTerminals, keeping the existing stop-all wire format byte-stable.
  2. codex-core — add UnifiedExecProcessManager::terminate_process(i32) -> bool, a session wrapper, a submission-loop handler, and a dispatch arm that emits EventMsg::Error when the id is unknown or cannot be parsed.
  3. codex-tui (+ codex-app-server wiring) — opt Stop into supports_inline_args, render /ps bullets with a short id, add a stop_one_background_terminal helper with prefix matching, route it through the existing AppCommandView / thread/backgroundTerminals/* surface.

Code Example

Background terminals

[a1b2c3] bash -lc "npm run dev"
...
[d4e5f6] tail -f /var/log/app.log
RAW_BUFFERClick to expand / collapse

What variant of Codex are you using?

CLI

What feature would you like to see?

Today the TUI exposes two slash commands for background terminals:

  • /ps — list running background terminals
  • /stop (alias /clean) — terminate all running background terminals

There is no way to stop just one. If I have three long-lived shells running (e.g. a dev server, a log tail, a file watcher) and one of them is misbehaving, my only option is /stop, which kills everything, and then re-launching the ones I still wanted. This is friction in an otherwise pleasant workflow.

I'd like /stop to accept an optional id argument, e.g. /stop a1b2c3, that terminates exactly one background terminal. Bare /stop (and /clean) should keep working exactly as they do today — nothing regresses for existing users.

Proposed user-facing behavior:

  • /ps — same output as today, but each bullet gains a short id prefix:

    Background terminals
    
      • [a1b2c3] bash -lc "npm run dev"
          ↳ ...
      • [d4e5f6] tail -f /var/log/app.log
  • /stop — unchanged. Stops everything.

  • /stop <id> — looks up the process by id (or prefix) and stops just that one.

  • /stop <unknown-id> — friendly error, no side effects.

  • /stop <ambiguous-prefix> — ambiguity error listing how many match, no side effects.


Additional information

Why this is probably low-cost for Codex

Most of the plumbing already exists. The backend already assigns a random i32 process id per unified-exec session; that id is already propagated to the TUI through ExecCommandBeginEvent.process_id and stored on each UnifiedExecProcessSummary as key; the id is simply not shown in /ps and not accepted by /stop. The ProcessStore even already has a remove(i32) method — the only missing piece on the core side is a public async wrapper that also terminates the process handle and unregisters its network approval (one function, ~12 lines, mirroring terminate_all_processes).

So the scope splits cleanly along three surfaces:

  1. codex-protocol — add an Op::StopBackgroundTerminal { process_id: String } variant next to CleanBackgroundTerminals, keeping the existing stop-all wire format byte-stable.
  2. codex-core — add UnifiedExecProcessManager::terminate_process(i32) -> bool, a session wrapper, a submission-loop handler, and a dispatch arm that emits EventMsg::Error when the id is unknown or cannot be parsed.
  3. codex-tui (+ codex-app-server wiring) — opt Stop into supports_inline_args, render /ps bullets with a short id, add a stop_one_background_terminal helper with prefix matching, route it through the existing AppCommandView / thread/backgroundTerminals/* surface.

Design discussion — the one real judgment call

The backend id is a random i32, currently generated in [1000, 100000). Two options for what to expose to the user:

Option A — raw id as a string, rendered as a 6-char prefix in /ps, prefix-matched on /stop <arg>.

  • Pro: stateless; what /ps shows is what /stop accepts; matches ExecCommandBeginEvent.process_id so logs/rollouts line up.
  • Pro: users can paste the full id from logs, or type just enough of a prefix to disambiguate.
  • Con: the visible id is not "pretty" — it's just a prefix of a decimal number.

Option B — stable short aliases (1, 2, …) rendered at /ps time and remembered on ChatWidget until the next /ps invocation.

  • Pro: very friendly to type.
  • Con: aliases drift as processes start/stop, so /stop 1 depends on the most recent /ps output, which is subtle and error-prone.
  • Con: new mutable TUI state; invalidated by each subsequent /ps.

I'd pick A. Short aliases feel ergonomic in isolation but the "last /ps wins" semantics are a trap once the user has two terminal sessions open or scrolls back in the transcript.

Matching rules for /stop <arg> that I'd suggest:

  1. Trim the arg; empty → existing stop-all path.
  2. Collect candidates where process.key.starts_with(arg).
  3. Exactly one match → stop that one.
  4. Zero matches → error ("No background terminal matching {arg}. Run /ps to see ids.").
  5. Multiple matches → error ("Ambiguous id {arg}: matches N terminals, be more specific.").

Open questions I'd like input on before any code lands

  1. Id format. Option A vs B above, or something else (e.g. a short hex slug instead of the raw decimal prefix). Is there a house style here?
  2. Ambiguity handling. Should a 2-match prefix error out (my suggestion), or deterministically stop the first one and warn? I chose the strict option because silent "pick one" behavior is surprising.
  3. app-server JSON-RPC surface. The TUI routes ops through thread/backgroundTerminals/clean, so adding /stop <id> end-to-end needs either (a) a new thread/backgroundTerminals/stop { threadId, processId } method, or (b) extending clean with an optional processId. Separate method feels cleaner and keeps clean wire-stable, but I'd like the team's call on this.
  4. /clean <id> alias. /clean is currently a silent no-op on inline args. If Stop opts into inline args, /clean a1b2c3 would start working automatically. This is almost certainly fine but worth flagging so it's not a surprise.
  5. No JSON-RPC surface, TUI-only? If the team would rather scope this to the TUI only (no new app-server method), I'd need to know because TUI ops round-trip through the app-server in production, so "TUI-only" would require a different plumbing strategy.

Reference implementation

I have a working implementation on my fork, split across three atomic commits (core, app-server, tui) with unit tests, an integration test that spawns two real processes and verifies OS-level termination of one and survival of the other, and regenerated insta snapshots for /ps. It's available here if it would be useful as a starting point for design discussion or as a reference once the direction is agreed: https://github.com/no-on3/codex/pull/1

I put this together primarily to validate that the design holds up end-to-end, and I'm happy to iterate on the design in this thread before any of that gets looked at. Thanks

extent analysis

TL;DR

To implement the requested feature, add an optional id argument to the /stop command, allowing users to terminate a specific background terminal by its ID.

Guidance

  1. Update the codex-protocol: Add an Op::StopBackgroundTerminal { process_id: String } variant to handle the new /stop <id> command.
  2. Modify codex-core: Implement UnifiedExecProcessManager::terminate_process(i32) -> bool to handle process termination and add necessary error handling for unknown or ambiguous IDs.
  3. Update codex-tui and codex-app-server: Render IDs in /ps output, add a stop_one_background_terminal helper, and route it through the existing AppCommandView surface.
  4. Decide on ID format and ambiguity handling: Choose between Option A (raw ID as a string) or Option B (stable short aliases) and determine how to handle ambiguous IDs (e.g., error out or stop the first match).

Example

No code snippet is provided as the issue does not contain sufficient code context.

Notes

The implementation should consider the trade-offs between the two proposed ID formats and ambiguity handling strategies. The choice of ID format and ambiguity handling will impact the user experience and the complexity of the implementation.

Recommendation

Apply the workaround by implementing the requested feature, as it provides a more user-friendly experience and does not introduce significant complexity. The proposed implementation seems well-structured, and the reference implementation is available for review and iteration.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

codex - 💡(How to fix) Fix `/stop <id>` to terminate a single background terminal [1 comments, 2 participants]