113 lines
4.9 KiB
Markdown
113 lines
4.9 KiB
Markdown
# Codex lanes in the Workspace — design
|
|
|
|
Issue: [Smartgift-AI/Claude-Code-Monitor#10](https://git.smartgift.io.vn/Smartgift-AI/Claude-Code-Monitor/issues/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)
|
|
|
|
1. Scope: run Codex in the lane's tmux pane. No ingestion of `~/.codex` history.
|
|
2. 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>`.
|
|
3. Codex run controls: model, sandbox, ask-for-approval, reasoning effort.
|
|
4. Codex runs persist in `dashboard_runs` (new `tool` column) so Active runs and
|
|
Run history show them next to Claude runs.
|
|
5. 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:
|
|
|
|
- `resume` is 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 `--effort` flag; it is
|
|
passed as `-c model_reasoning_effort=<value>`)
|
|
- The `--dangerously-bypass-approvals-and-sandbox` and
|
|
`--dangerously-bypass-hook-trust` flags are never emitted.
|
|
- `spawnRun` takes `tool` and 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 optional `tool`.
|
|
- `POST /api/lanes/:id/start`: accepts `sandbox` and `approval` in addition to
|
|
today's `model`/`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`: probes `codex` on PATH (`which`), same
|
|
`{found, path}` shape. Default (no query) stays `claude`.
|
|
|
|
## UI
|
|
|
|
- `AddLaneModal`: a second segmented toggle "Claude Code | Codex" using the
|
|
existing `Seg` component, next to the Repo/Worktree toggle.
|
|
- `LaneCard` / `LaneStripCard`: a small tool badge, shown only for `codex` lanes
|
|
so Claude lanes look exactly as before.
|
|
- `RunSetup`: fields switch on the selected lane's `tool`.
|
|
- 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.js` never paints the amber detected badge.
|
|
- Pipeline stages still work when the agent calls `ccam stage` itself (the repo
|
|
already ships `AGENTS.md` and `.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, `tool` persisted on lane and run,
|
|
start route ignoring foreign fields.
|
|
- `npm run test:client`: `RunSetup` field set per tool, `AddLaneModal` tool
|
|
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), `README` lane section if it lists lane fields.
|