Component: moshi-hook 0.2.41 (Linux x86_64) · Codex CLI 0.144.1 · tmux 3.x
(Filing here since the moshi source repo is private and this is the active public repo — happy to move this wherever you prefer.)
Symptom
Every Codex session on a host is attributed to the same tmux session/pane in the Moshi app, regardless of which pane the Codex TUI actually runs in. All files in ~/.local/state/moshi/codex-sessions/*.json carry identical tmuxSession/tmuxPane values (in my case a pane from a session none of them ran in), while Claude Code sessions attribute correctly.
Root cause
Recent Codex CLI versions (observed on 0.144.1) host all sessions inside a single shared codex app-server daemon (control socket at ~/.codex/app-server-control/). The daemon is auto-spawned by the first Codex client that needs it, then detaches (ppid 1) and outlives its parent.
Hook subprocesses — including moshi-hook codex-hook — are forked by the app-server, not by the per-pane TUI. So every hook invocation inherits the app-server's frozen environment: the TMUX/TMUX_PANE of whichever pane the daemon happened to be auto-started from, forever.
moshi-hook codex-hook derives terminal context from its own $TMUX_PANE, which is correct for the Claude Code hook model (hooks fork from the in-pane CLI) but now always wrong for Codex: it resolves the app-server's birth pane for every session on the host.
Evidence from my host:
tr '\0' '\n' < /proc/<app-server-pid>/environ | grep TMUX → TMUX_PANE=%164 (a pane created hours earlier, in a session unrelated to most Codex work).
- Every
codex-sessions/*.json written since that daemon spawned says "tmuxPane": "%164", including sessions whose rollout cwd is a different worktree and whose TUI provably runs in another pane (/proc/<tui-pid>/environ shows the correct, distinct TMUX_PANE).
moshi-hook context run manually with each TUI's env resolves the correct pane — the detection logic is fine; it's just executing inside the wrong process.
Repro
- In tmux pane A, start a Codex TUI (this auto-spawns the shared app-server as a child of pane A's shell; it daemonizes).
- In tmux pane B (different tmux session, different cwd), start another Codex TUI and submit a prompt.
~/.local/state/moshi/codex-sessions/<session-B>.json reports pane A's session/pane.
Suggested fix
Don't trust the hook process env for Codex. Options, roughly in order of preference:
- Resolve the pane from the session, not the env: moshi-hook already ships pane-process detection (
tmux:process in moshi-hook context). For codex-hook events, scan tmux list-panes -a for the pane whose process tree contains the Codex client for that session_id (or whose pane_current_path is the longest prefix of the hook payload's cwd), and only fall back to env when that fails.
- Sanity-check the env: if
$TMUX_PANE's pane_current_path is unrelated to the hook payload's cwd, discard the tmux context instead of recording it (falling back to the existing project-from-cwd naming). This is a two-line guard that stops the wrong-pane stamping even if 1 is too invasive.
- Detect the app-server ancestry (
/proc/self/status ppid chain reaching a codex app-server process) and skip env-based tmux context in that case.
Note the same applies to any future agent CLI that moves session hosting into a shared daemon — env-based attribution only works while hooks fork from a process rooted in the pane.
Workaround I'm running
A transparent wrapper installed at ~/.local/bin/moshi-hook (real binary moved aside) that, for codex-hook invocations only: trusts the inherited TMUX_PANE iff that pane's root process is an ancestor of the hook process (true for in-pane forks, false for app-server forks); otherwise picks the pane whose pane_current_path is the longest prefix of $PWD (Codex spawns hooks with cwd = session cwd) among panes actually running a codex process; otherwise strips TMUX/TMUX_PANE so headless sessions fall back to project-from-cwd naming. Verified end-to-end: a TUI in a fresh tmux session now attributes correctly even though its hooks fork from an app-server carrying another pane's env. Happy to share the script if useful.
Also worth knowing for anyone hitting this: pre-starting the app-server from a clean environment (e.g. a systemd user unit, so no TMUX* vars at all) stops the false attribution — sessions then group by project cwd — but only pane-aware resolution in moshi-hook can restore true per-pane attribution.
Component: moshi-hook 0.2.41 (Linux x86_64) · Codex CLI 0.144.1 · tmux 3.x
(Filing here since the moshi source repo is private and this is the active public repo — happy to move this wherever you prefer.)
Symptom
Every Codex session on a host is attributed to the same tmux session/pane in the Moshi app, regardless of which pane the Codex TUI actually runs in. All files in
~/.local/state/moshi/codex-sessions/*.jsoncarry identicaltmuxSession/tmuxPanevalues (in my case a pane from a session none of them ran in), while Claude Code sessions attribute correctly.Root cause
Recent Codex CLI versions (observed on 0.144.1) host all sessions inside a single shared
codex app-serverdaemon (control socket at~/.codex/app-server-control/). The daemon is auto-spawned by the first Codex client that needs it, then detaches (ppid 1) and outlives its parent.Hook subprocesses — including
moshi-hook codex-hook— are forked by the app-server, not by the per-pane TUI. So every hook invocation inherits the app-server's frozen environment: theTMUX/TMUX_PANEof whichever pane the daemon happened to be auto-started from, forever.moshi-hook codex-hookderives terminal context from its own$TMUX_PANE, which is correct for the Claude Code hook model (hooks fork from the in-pane CLI) but now always wrong for Codex: it resolves the app-server's birth pane for every session on the host.Evidence from my host:
tr '\0' '\n' < /proc/<app-server-pid>/environ | grep TMUX→TMUX_PANE=%164(a pane created hours earlier, in a session unrelated to most Codex work).codex-sessions/*.jsonwritten since that daemon spawned says"tmuxPane": "%164", including sessions whose rolloutcwdis a different worktree and whose TUI provably runs in another pane (/proc/<tui-pid>/environshows the correct, distinctTMUX_PANE).moshi-hook contextrun manually with each TUI's env resolves the correct pane — the detection logic is fine; it's just executing inside the wrong process.Repro
~/.local/state/moshi/codex-sessions/<session-B>.jsonreports pane A's session/pane.Suggested fix
Don't trust the hook process env for Codex. Options, roughly in order of preference:
tmux:processinmoshi-hook context). For codex-hook events, scantmux list-panes -afor the pane whose process tree contains the Codex client for thatsession_id(or whosepane_current_pathis the longest prefix of the hook payload'scwd), and only fall back to env when that fails.$TMUX_PANE'spane_current_pathis unrelated to the hook payload'scwd, discard the tmux context instead of recording it (falling back to the existing project-from-cwd naming). This is a two-line guard that stops the wrong-pane stamping even if 1 is too invasive./proc/self/statusppid chain reaching acodex app-serverprocess) and skip env-based tmux context in that case.Note the same applies to any future agent CLI that moves session hosting into a shared daemon — env-based attribution only works while hooks fork from a process rooted in the pane.
Workaround I'm running
A transparent wrapper installed at
~/.local/bin/moshi-hook(real binary moved aside) that, forcodex-hookinvocations only: trusts the inheritedTMUX_PANEiff that pane's root process is an ancestor of the hook process (true for in-pane forks, false for app-server forks); otherwise picks the pane whosepane_current_pathis the longest prefix of$PWD(Codex spawns hooks with cwd = session cwd) among panes actually running acodexprocess; otherwise stripsTMUX/TMUX_PANEso headless sessions fall back to project-from-cwd naming. Verified end-to-end: a TUI in a fresh tmux session now attributes correctly even though its hooks fork from an app-server carrying another pane's env. Happy to share the script if useful.Also worth knowing for anyone hitting this: pre-starting the app-server from a clean environment (e.g. a systemd user unit, so no
TMUX*vars at all) stops the false attribution — sessions then group by project cwd — but only pane-aware resolution in moshi-hook can restore true per-pane attribution.