4.9 KiB
Codex lanes in the Workspace — design
Issue: Smartgift-AI/Claude-Code-Monitor#10 — "Workspace | Thêm Codex vào trang Workspace".
Goal
A lane can be driven by the codex CLI instead of claude, started and watched
from the Workspace page exactly like today: same lane strip, same pipeline map,
same tmux-backed terminal console, same run history.
Decisions (from brainstorming)
- Scope: run Codex in the lane's tmux pane. No ingestion of
~/.codexhistory. - The tool belongs to the lane, not to the run: one lane = one tool, picked
in the "Add lane" modal, not changeable afterwards. One tmux session per lane
stays
ccam-lane-<id>. - Codex run controls: model, sandbox, ask-for-approval, reasoning effort.
- Codex runs persist in
dashboard_runs(newtoolcolumn) so Active runs and Run history show them next to Claude runs. - The rest of the Workspace UI is unchanged.
Data model
server/db.js, following the existing ALTER TABLE lanes ADD COLUMN pattern
(idempotent, try/catch on "duplicate column"):
lanes.tool TEXT NOT NULL DEFAULT 'claude'—'claude' | 'codex'.dashboard_runs.tool TEXT NOT NULL DEFAULT 'claude'.
Both default to 'claude', so every existing row keeps today's behavior and no
response shape changes for existing consumers — tool is an added field.
server/lib/lanes.js accepts tool on create (ensureLane, worktree create),
validates it against {claude, codex}, and exposes it on the lane payload.
updateLane never writes tool — a lane's tool is immutable, same spirit as
"adopted lanes are not destroyable".
Spawn
server/lib/pty-run.js — buildArgv({tool, ...}) branches:
claude (unchanged):
claude --permission-mode <mode> --allow-dangerously-skip-permissions
[--model M] [--effort E] [--resume SID] [prompt]
codex:
codex [resume <uuid> | resume --last]
[-m MODEL] [-s SANDBOX] [-a APPROVAL]
[-c model_reasoning_effort=EFFORT] [prompt]
Notes:
resumeis a subcommand for Codex, so it comes first, before the flags.codex resume <uuid> [prompt]; with no recorded id,codex resume --last.- Allowlists (reject with
EBAD*like today, never pass through):- sandbox:
read-only | workspace-write | danger-full-access - approval:
on-request | never - effort:
low | medium | high | xhigh(Codex has no--effortflag; it is passed as-c model_reasoning_effort=<value>)
- sandbox:
- The
--dangerously-bypass-approvals-and-sandboxand--dangerously-bypass-hook-trustflags are never emitted. spawnRuntakestooland records it on the run row. tmux session naming, create-or-adopt, and the shell-prompt send path are untouched.
API
POST /api/lanes/ensure,POST /api/lanes/worktree: accept optionaltool.POST /api/lanes/:id/start: acceptssandboxandapprovalin addition to today'smodel/effort/permissionMode/resume. The server reads the tool from the lane row, never from the request body, and ignores the fields that do not belong to that tool.GET /api/run/binary?tool=codex: probescodexon PATH (which), same{found, path}shape. Default (no query) staysclaude.
UI
AddLaneModal: a second segmented toggle "Claude Code | Codex" using the existingSegcomponent, next to the Repo/Worktree toggle.LaneCard/LaneStripCard: a small tool badge, shown only forcodexlanes so Claude lanes look exactly as before.RunSetup: fields switch on the selected lane'stool.- claude: model, permission mode, effort (unchanged)
- codex: model, sandbox, approval, reasoning effort
- Codex lanes hide the Claude session picker (the sessions table holds Claude
sessions only) and offer a single "Resume last" action mapping to
codex resume --last. - The Start button's disabled state uses the per-tool binary probe.
- Console, pipeline map, split view, run history: unchanged.
- All new strings go through i18n, no literals.
Known limitations (documented, not fixed here)
Codex does not emit this dashboard's Claude Code hooks, so for a Codex lane:
- Sessions, Analytics, and cost stay empty — nothing ingests Codex usage.
server/lib/stage-detect.jsnever paints the amber detected badge.- Pipeline stages still work when the agent calls
ccam stageitself (the repo already shipsAGENTS.mdand.codex/). - Lane liveness for Codex lanes comes from the tmux pane plus
dashboard_runs.
Testing
npm run test:server: Codex argv shape (fresh, resume-by-id, resume-last, effort mapping), allowlist rejections,toolpersisted on lane and run, start route ignoring foreign fields.npm run test:client:RunSetupfield set per tool,AddLaneModaltool choice reaching the API call, screen snapshots reviewed and regenerated.- Docs to update:
docs/LANES.md(tool per lane, Codex limitations),docs/API.md(new fields),READMElane section if it lists lane fields.