Show Codex CLI activity in tab status#673
Conversation
Codex (OpenAI) does not emit OSC 21337; it conveys working state by prefixing the terminal title (OSC 0/1/2) with a braille-spinner glyph while crunching and removing the prefix when idle or blocked on the user. Sessions running Codex therefore got no tab indicator dot, no subtitle, and no signal to any other consumer of iTermSessionTabStatus. Add a compatibility shim that recognizes the title pattern, gates on the foreground job ancestry containing codex (last-path-component match so /opt/homebrew/bin/codex counts), and synthesizes the equivalent Working/Idle status into iTermSessionTabStatus. The existing tab indicator, tab subtitle, and notification paths render the same as for OSC 21337-aware agents. iTermSessionTabStatus gains a synthesizedStatusSource tag so the shim only clears state it owns. A real OSC 21337 emitter that changes any visible field claims authoritative ownership; the shim refuses to overwrite a real emitter's status (text or dot-only). Synthesized state is excluded from arrangementDictionary so it does not survive window restoration as fake-real state; the shim re-derives status on the next title or foreground-job update post-restore. Pure decoding lives in CodexTitleStatusDecoder.swift and is covered by unit tests for the spinner prefix, near misses, non-codex word matches, and out-of-block scalars. The hook sites in PTYSession assert main thread, matching the threading contract of screenSetTabStatus:.
# Conflicts: # iTerm2.xcodeproj/project.pbxproj
Empirically observed two install layouts: - Brew: /opt/homebrew/bin/codex, single native binary, foreground argv0 ends in codex. Adaptor matches. - npm/npx: node ./node_modules/.bin/codex spawns the platform-specific Rust binary at .../codex-darwin-arm64/.../bin/codex with stdio inherited, so the Rust child is the deepest foreground job on the TTY. Its argv0 ends in codex; foregroundJobAncestorNames lists it before node. Adaptor matches. Tests cover both layouts plus the rejection cases that prove the gate is doing real work: substring-only matches (codex-ish, not-codex), no codex in ancestors, nil ancestors. Plus title-driven transitions and the coexistence invariant: a real OSC 21337 emitter that wrote a visible status before the shim runs is left alone.
# Conflicts: # iTerm2.xcodeproj/project.pbxproj
|
@gnachman friendly ping, did you get a chance to look at this? 🙇🏽 (I have been using this myself for a while now) |
|
This could be done but it's probably not a great idea because it is so tightly bound to the current behavior of Codex. The best way is for codex itself to send OSC 21337. If that is off the table, the second-best way is to add a new category of triggers that run on title change and make it part of a formal Codex integration. That way they can be updated if Codex changes its behavior in the future. |
Per review, move Codex working/idle detection off the hardcoded screenSetWindowTitle hook and into the trigger system. Adds a new event-trigger category, Title Changed (iTermTriggerMatchTypeEventTitleChanged), fired on OSC 0/1/2 title changes with an optional titleRegex filter, selectable and configurable like the other event triggers. Codex now rides that event via an always-on built-in trigger seeded by EventTriggerEvaluator. Its action reuses the existing decoder/adaptor through a new triggerSession side-effect that applies synthesized tab status, so source-ownership and arrangement-exclusion guarantees are preserved. Foreground-job changes re-evaluate the shim directly so working clears when codex exits without a fresh title. Covered by new EventTriggerEvaluatorTitleChangedTests plus the existing Codex decoder/adaptor tests.
# Conflicts: # iTerm2.xcodeproj/project.pbxproj # sources/Settings/Profiles/ITAddressBookMgr.h # sources/Triggers/iTermEventTriggerParameterView.swift
|
thanks @gnachman! makes sense. Reworked it the way you suggested: added a Title Changed event-trigger category, and Codex rides it as a built-in trigger now instead of the hardcoded hook in screenSetWindowTitle. the detection itself (spinner decode + Working/Idle mapping) stays in CodexTitleStatusDecoder/Adaptor, just reused from the trigger action, so same behavior but title handling is part of the trigger system now and updatable if Codex changes it. Synced with master and resolved the conflicts. |
|
The PRs I sent OpenAI has been making some progress and that is a much cleaner solution. Let's hold off on this and see if gets landed. You can find them from the issue: openai/codex#25879 |
Bring in upstream "Show Codex CLI activity in tab status": Codex (OpenAI) does not emit OSC 21337; it signals working/idle by prefixing the OSC 0/1/2 terminal title with a braille-spinner glyph. A pure decoder (CodexTitleStatusDecoder) plus a foreground-job-gated adaptor (CodexTitleStatusAdaptor) synthesize the equivalent iTermSessionTabStatus, driven by a new always-on built-in "Codex Status" trigger riding a new "Title Changed" event-trigger category. Gated behind a new per-user spawnTerm feature flag `spawnterm.codex_status` (default OFF), toggled in Settings > General > AI > spawnTerm. The gate is enforced at the single choke point -[PTYSession screenApplyCodexTitleStatusWithTitle:]: when OFF, no synthesized Codex tab status is applied and behavior is stock. Flag added in all three synced places (spawnterm-flag, spawnterm_flag.py, iTermSpawnTermCapabilities.m) plus feature-flags.md; shell/python parity tests stay green. PTYSession.m merge preserves our prior #51 (user-var sidecar) and #49 (writeTask:completion: threading) changes; the gnachman#673 hooks are purely additive. New Trigger.h delegate method, VT100Screen side-effect plumbing, and the Title Changed event UI are included. Adapted from upstream gnachman#673 Co-authored-by: carvalhoviniciusluiz <carvalhoviniciusluiz@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…an#670/gnachman#673) + 3 new AI flags + agent guide Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codex doesnt emit OSC 21337, so iTerm2 currently cant show the usual AI-agent tab status for it. It does update the terminal title with a braille spinner while its working, so this PR uses that as a compatibility path for Codex only.
The shim watches title changes, verifies the foreground process is actually
codex, and synthesizes the same Working/Idle status the existing OSC 21337 path produces. Real OSC 21337 status still wins, so this wont overwrite agents that already report status directly.The synthesized status is tagged as shim-owned, so it only clears state it created, and its excluded from saved arrangements so restored windows dont come back with stale fake status. It re-derives from the next title or foreground-process update.
Matching is on the foreground process whose last path component is
codex, so it works for both install layouts (brew native binary and the npm/npx Rust binary) and ignores lookalikes likecodex-ish. Prerequisite for #670 (menu bar item counting working AI agents), which picks up Codex support for free once this lands.Test plan
codexshows Working while the title spinner is active, Idle when it stops