feat(sagi): spawn a real claude session for Sagi's Ask handoff

Unmatched Ask questions now adopt a dedicated "Sagi" lane (POST
/api/lanes/ensure, cwd defaults to $HOME) and spawn a tmux-backed claude
run there instead of just prefilling the Run page composer.
This commit is contained in:
2026-08-24 09:39:28 +07:00
parent 510f015fdf
commit b1bca07c47
6 changed files with 99 additions and 26 deletions
+11 -6
View File
@@ -138,18 +138,23 @@ POST /api/lanes/ensure
{ "cwd": "/absolute/path/to/work", "title": "App package", "pipeline": "ship-feature" }
```
`pipeline` is optional and, like `title`, applies **only when a lane is
actually created** — an existing lane is returned untouched. Omitted, the lane
gets `default`. `POST /api/lanes/worktree` and `POST /api/lanes` take the same
optional field. An id no template defines returns `400 EBADPIPELINE`.
`cwd` is optional — when missing or empty the server falls back to
`os.homedir()` (the user's home directory). This is how the Sagi mascot
adopts its dedicated lane without the client needing to know an absolute
path. Existing cwd values still pass through unchanged. `pipeline` is
optional and, like `title`, applies **only when a lane is actually created**
— an existing lane is returned untouched. Omitted, the lane gets `default`.
`POST /api/lanes/worktree` and `POST /api/lanes` take the same optional
field. An id no template defines returns `400 EBADPIPELINE`.
Idempotent lookup by working directory — the Workspace page opens on a `cwd`,
not on a lane id. Returns the lane whose own `cwd` is that path, or the longest
path-boundary parent of it, as `{ "lane": {...}, "created": false }` with `200`.
When no lane owns the path, an `adopted` lane is created and returned as
`{ "lane": {...}, "created": true }` with `201`; `title` is used only in that
case. A relative or missing `cwd` returns `400 EBADCWD`. Path boundaries are
respected: a lane at `/tmp/wt` owns `/tmp/wt/pkg` but never `/tmp/wt-sibling`.
case. A *relative* `cwd` returns `400 EBADCWD`; a missing or empty `cwd`
falls back to the user's home directory instead of erroring. Path boundaries
are respected: a lane at `/tmp/wt` owns `/tmp/wt/pkg` but never `/tmp/wt-sibling`.
Two concurrent calls for the same path always yield **one** lane: the
`lanes.cwd` UNIQUE constraint decides the winner, and the loser re-reads and