feat(workspace): AI tool per session, one place to start a session, ended sessions open like Claude Desktop

- A session picks its own AI tool: `tool` on POST /api/lanes/:id/start
  (a catalog id, known + enabled, else 400 EBADTOOL/EDISABLED); a resume
  keeps the tool its session ran on (`sessions.tool`). The workspace's
  `lanes.tool` is now only the default.
- The workspace home's prompt box is the one place a session starts. Its
  fields run in order: AI → AI Router (Native or a profile) → Model (the
  tool's own list, or the router's models) → Permission (Claude mode /
  Codex sandbox+approval / catalog bypass) → Effort.
- The pane's run form (RunSetup) is gone: New run, a single pane's exit and
  an empty split pane's New session all go to the home. RunSetup.tsx keeps
  PromptEditor, CwdAutocomplete and ModelPicker (new `choices` prop).
- The tool icon moved from the sidebar's workspace row to each session row
  (right edge, `session.tool`); the split-pane header shows the run's tool.
- An ended session opens in its workspace (`&session=`, SessionReplay.tsx):
  its conversation read-only plus a prompt box; only a SENT message resumes
  it (resumeSessionId + prompt, first free slot). The console is only
  hidden behind it.
- Analyst button beside Files/Changes in the right panel opens
  /sessions/:id for the opened session.
- Gemini CLI ships its own icon (client/public/ai-tools/gemini.svg, from
  zonalogo.com).
- RouterPicker always shows (Native disabled for tools without router
  support); useAiRouters() exported.
- Tests: server start-with-tool cases; SessionReplay, home field order/tool/
  router models, empty pane, Analyst; dropped the cwd-field suites
  (Run.defaultCwd, Workspace.laneCwd). Docs: CLAUDE.md rules, API.md,
  LANES.md, README, ARCHITECTURE, INSTALL.
This commit is contained in:
2026-10-02 15:29:38 +07:00
parent c507d37baf
commit 4802684d2f
34 changed files with 1137 additions and 2504 deletions
+9 -5
View File
@@ -150,8 +150,8 @@ it a `cwd` nested inside another workspace returns that parent workspace (`creat
false`); with it the nested directory gets a workspace of its own. The Add-workspace
modal sends it, and says so when the directory already has a workspace. An id no template defines returns `400 EBADPIPELINE`.
`tool` (`"claude"` or `"codex"`, default `"claude"`) is the CLI this workspace's
runs will spawn. Immutable once the workspace is created; an attempt to update it on
`tool` (an AI Tools catalog id, default `"claude"`) is the CLI this workspace's
sessions start on unless a start names another (`POST /api/lanes/:id/start` `tool`). Immutable once the workspace is created; an attempt to update it on
an existing workspace returns `400 EBADTOOL`. The tool choice appears as an immutable
badge on the workspace card (Codex workspaces only).
@@ -228,8 +228,8 @@ existing `lane_update` message when the workspace becomes `idle` or `failed`.
}
```
`tool` (`"claude"` or `"codex"`, default `"claude"`) is the CLI this workspace's
runs will spawn. Immutable once the workspace is created; an attempt to update it on
`tool` (an AI Tools catalog id, default `"claude"`) is the CLI this workspace's
sessions start on unless a start names another (`POST /api/lanes/:id/start` `tool`). Immutable once the workspace is created; an attempt to update it on
an existing workspace returns `400 EBADTOOL`.
`sourceRepo` must exist, be absolute, and be a git repository with at least
@@ -266,7 +266,11 @@ Starts a new run in the workspace's working directory, or attaches an existing i
}
```
All fields are optional. The tool (CLI invoked) comes from the workspace itself, never the request — **a client cannot override a workspace's tool**. If the workspace is a Codex workspace, three fields control Codex-specific behavior:
All fields are optional.
- **`tool`** — the AI tool THIS session runs on, an AI Tools catalog id (only an id; the command, args and env still come from the catalog file). Omitted = the workspace's own tool. It must be known (`400 EBADTOOL`) and enabled (`400 EDISABLED`); the workspace's own tool is never re-checked, so a workspace on a tool disabled since keeps starting when `tool` is omitted. A resume (`resumeSessionId`, or `resume`) ignores `tool` and keeps the tool its session ran on (`sessions.tool`). The workspace's own `tool` column is unchanged. The run is recorded with its tool (`dashboard_runs.tool`, the run handle's `tool`).
When the session's tool is Codex, three fields control Codex-specific behavior:
- **`sandbox`** — one of `read-only`, `workspace-write`, or `danger-full-access`, or omitted/empty for no sandbox. Passed to `codex -s <value>`.
- **`approval`** — one of `on-request` or `never`, or omitted/empty for no approval prompt. Passed to `codex -a <value>`.