Skip to content

moshi-hook codex-hook: all Codex sessions attributed to one tmux pane (Codex shared app-server env) #5

Description

@andrew-housebets

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 TMUXTMUX_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

  1. 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).
  2. In tmux pane B (different tmux session, different cwd), start another Codex TUI and submit a prompt.
  3. ~/.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:

  1. 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.
  2. 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.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions