chore+docs: config, agent registry, codecontext, v2.6 spec, changelog
Working-tree config/doc changes (.gitignore, CLAUDE.md, AGENTS.md removal + data/AGENTS.md, codecontext Dockerfile/shim — pre-existing) plus this session's v2-6 persistent-agent-sessions openspec proposal/design/tasks (planning only; feature unimplemented, reserves the v2.6.0 tag) and the v2.5.2 CHANGELOG entry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
94
openspec/changes/v2-6-persistent-agent-sessions/tasks.md
Normal file
94
openspec/changes/v2-6-persistent-agent-sessions/tasks.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# v2.6 Tasks — Persistent agent sessions
|
||||
|
||||
Phased so each phase is independently shippable and smoke-testable. Phase 1
|
||||
(OpenCode server) delivers the most value on the cleanest API; goose/qwen warm
|
||||
ACP follows; hardening last.
|
||||
|
||||
## Phase 0 — Foundations (no behavior change)
|
||||
|
||||
- [ ] 0.1 Add `session_worktrees` + `agent_sessions` tables (per `(session_id, agent)`)
|
||||
to `apps/coder/src/schema.sql` (idempotent; see design §3).
|
||||
- [ ] 0.2 Define `AgentBackend` / `AgentSessionHandle` interface + normalized `onEvent`
|
||||
event union (reuse shapes from `acp-dispatch.ts`).
|
||||
- [ ] 0.3 Scaffold `agent-pool.ts` with lazy get-or-create keyed by `(chat, agent)`,
|
||||
health, `dispose()`; wire `app.addHook('onClose')` to dispose alongside dispatcher `stop()`.
|
||||
|
||||
## Phase 1 — OpenCode server backend (multi-turn, warm)
|
||||
|
||||
- [ ] 1.1 Add `@opencode-ai/sdk` to `apps/coder/package.json`; pin to installed opencode major.
|
||||
- [ ] 1.2 `backends/opencode-server.ts`: spawn `opencode serve` once (random
|
||||
`OPENCODE_SERVER_PASSWORD`, allocated port), `createOpencodeClient`, wait for ready line.
|
||||
- [ ] 1.3 Single `/event` SSE read loop; demux by `properties.sessionID`; map
|
||||
`message.part.delta`/`updated` (text + reasoning) + tool parts to `onEvent`.
|
||||
- [ ] 1.4 Port Paseo `streamedPartKeys` reasoning dedup (delta vs final part).
|
||||
- [ ] 1.5 `ensureSession`: reuse the `(chat, opencode)` `agent_sessions` row if present
|
||||
(resume on switch-back), else `client.session.create()` → store `agent_session_id`.
|
||||
- [ ] 1.6 `prompt`: send via SDK with `x-opencode-directory` = session worktree + `model`.
|
||||
- [ ] 1.7 Dispatcher: when `agent==='opencode'`, route to pool backend instead of
|
||||
`dispatchViaAcp`; keep broker frames + `persistExternalAgentTurn` identical.
|
||||
- [ ] 1.8 Persistent worktree: chat-keyed `createWorktree` (shared across agents);
|
||||
capture base commit in `session_worktrees`; reuse across turns and agents.
|
||||
- [ ] 1.9 Per-session concurrency: replace global `running` with `Map<sessionId,Promise>`;
|
||||
`poll()` skips sessions with an in-flight turn.
|
||||
- [ ] 1.10 Per-turn diff → supersede prior `pending_changes` row for the session (latest-wins).
|
||||
- [ ] **Smoke 1:** two messages in one opencode chat → same `agent_session_id`, same worktree,
|
||||
no second `createWorktree`; agent references turn-1 edits; reasoning shows once; turn-2 faster.
|
||||
|
||||
## Phase 1 (UX) — Attribution & switch affordances (design §9)
|
||||
|
||||
- [ ] U.1 Stamp `pending_changes.agent` at queue time (worktree path → task agent;
|
||||
native write tools → `'boocode'`; manual RightRail create → NULL).
|
||||
- [ ] U.2 Add `agent` to `listPending` response + frontend `PendingChange` type.
|
||||
- [ ] U.3 Extract `providerIcon()` to a shared helper; DiffPanel renders an agent badge
|
||||
per row + a "Changes from X, Y" note when the pending set spans >1 agent (§9a).
|
||||
- [ ] U.4 `GET /api/sessions/:id/agent-sessions` route + `api.coder.agentSessions` +
|
||||
`useAgentSessions(sessionId)` (refetch on `message_complete`) (§9b).
|
||||
- [ ] U.5 `AgentComposerBar` optional `sessionId` prop → resumed / history / new-session
|
||||
chip beside the Provider picker; hidden on fresh chats and other callers (§9b).
|
||||
- [ ] **Smoke U:** stage edits with opencode then boocode → DiffPanel badges each row to the
|
||||
right agent; composer shows "resumed" when re-selecting opencode, "new session" for goose.
|
||||
|
||||
## Phase 2 — Warm ACP backend (goose, qwen)
|
||||
|
||||
- [ ] 2.1 `backends/warm-acp.ts`: persistent spawn + `ClientSideConnection`; `initialize` +
|
||||
`session/new` once; reuse `acp-dispatch.ts` `handleSessionUpdate`.
|
||||
- [ ] 2.2 `prompt`: `session/prompt` on the warm connection per turn; per-turn abort signal only.
|
||||
- [ ] 2.3 Child supervision: detached lifetime, exit handler marks `status='crashed'`.
|
||||
- [ ] 2.4 Dispatcher routes `goose`/`qwen` to warm backend; keep one-shot fallback for arena/MCP
|
||||
(or opt those into pool too — decide in review).
|
||||
- [ ] **Smoke 2:** two messages in a goose chat reuse the same process + ACP session + worktree;
|
||||
reasoning still renders; no per-turn respawn.
|
||||
- [ ] **Smoke 2b (switch round-trip):** opencode → boocode → opencode in one chat — opencode
|
||||
resumes the SAME `agent_session_id` (memory intact), boocode saw opencode's turns as
|
||||
history, all three shared the one worktree, and no agent was locked to the chat.
|
||||
|
||||
## Phase 3 — Lifecycle hardening
|
||||
|
||||
- [ ] 3.1 Idle TTL eviction keyed per `(chat, agent)`; reattach-on-next-turn from `agent_sessions`.
|
||||
- [ ] 3.2 Crash recovery: opencode server restart recreates sessions; ACP re-`session/new`.
|
||||
- [ ] 3.3 Chat close/archive hook → `closeSession` for every `(chat, agent)` + remove the
|
||||
shared `session_worktrees` row + worktree; mark agent rows `status='closed'`.
|
||||
- [ ] 3.4 Orphan worktree reaper (extend periodic sweeper) + max-live-worktrees LRU cap.
|
||||
- [ ] 3.5 Re-baseline worktree diff after `apply_pending`.
|
||||
- [ ] 3.6 Reconnect test: restart BooCoder mid-session → next turn reattaches/recreates cleanly.
|
||||
- [ ] 3.7 Staging-boundary hint in DiffPanel (§9c): muted one-liner when the selected
|
||||
provider can't see another agent's unapplied worktree edits (derived from per-change
|
||||
`agent` + current provider; no new state).
|
||||
|
||||
## Tests
|
||||
|
||||
- [ ] T.1 `agent-pool` unit: get-or-create, idle evict, dispose drains in-flight (DB-opt-in pattern).
|
||||
- [ ] T.2 opencode SSE demux + reasoning dedup unit (fixture event stream).
|
||||
- [ ] T.3 per-session concurrency: two sessions run concurrently, one session serializes.
|
||||
|
||||
## Docs
|
||||
|
||||
- [ ] D.1 Update `CLAUDE.md` (BooCoder dispatch section) + `BOOCODER.md` health/contract.
|
||||
- [ ] D.2 Note opencode `@opencode-ai/sdk` dep + `OPENCODE_SERVER_PASSWORD` env in env docs.
|
||||
- [ ] D.3 `CHANGELOG.md` entry on tag (`v2.6.0-persistent-agent-sessions`).
|
||||
|
||||
## Build / deploy gate
|
||||
|
||||
- [ ] B.1 `pnpm -C apps/server build && pnpm -C apps/coder build` clean.
|
||||
- [ ] B.2 `pnpm -C apps/server test` (+ DB-opt-in) green.
|
||||
- [ ] B.3 Deploy: `sudo systemctl restart boocoder`; `curl :9502/api/health` reports tool count.
|
||||
Reference in New Issue
Block a user