v2.2-paseo-providers: Paseo provider stack + v2.2.1 pane-scoped chat fixes
Ship Paseo-equivalent provider snapshot, AgentComposerBar, ACP dispatch rewrite with streaming/persist, permission prompts, and agent commands. Follow-up: pane-scoped chat resolution, CoderMessageList tool timeline, WS user-delta replace, and inference orphan tool_call stripping. Archive openspec v2-2; update CHANGELOG and CURRENT. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,16 +8,16 @@ Last updated: 2026-05-25
|
||||
|
||||
BooCode is a **3-app monorepo** at `/opt/boocode/` (locked 2026-05-22):
|
||||
|
||||
- **BooChat** (`apps/chat`, port `9500`, `code.indifferentketchup.com`) — read-only chat with file-inspection tools. The live thing. Pick a project, chat with a local LLM, get streaming responses over WebSocket. DB renamed `boochat_db` at v2.0.
|
||||
- **BooCoder** (`apps/coder`, port `9502`, `coder.indifferentketchup.com`) — write tools + external-CLI dispatch. **Shipped v2.0.0–v2.0.4.** In-process inference loop (with `pending_changes` table) AND ACP-dispatched external agents (opencode/goose) with PTY fallback (claude/pi/smallcode) — same surface, two execution paths.
|
||||
- **BooTerm** (`apps/booterm`, port `9501`) — PTY/tmux/xterm.js. **Live since May 2026.** Node 20 Alpine + node-pty + tmux + xterm.js. Tmux session per pane (`bc-<uuid>`), SSH-out works (openssh-client + gosu in the image). `/api/term/health` shares the existing `boochat_db`.
|
||||
- **BooChat** (`apps/server` + `apps/web`, port `9500`, `code.indifferentketchup.com`) — read-only chat with file-inspection tools. Backend in `apps/server`, SPA in `apps/web`. Database `boochat` (renamed from `boocode` at v2.0).
|
||||
- **BooCoder** (`apps/coder`, port `9502`, `coder.indifferentketchup.com`) — write tools + external-CLI dispatch. **Shipped v2.0.0–v2.1.0.** Host systemd service (not Docker since v2.1.0). In-process inference (with `pending_changes` table) AND ACP-dispatched external agents (opencode/goose) with PTY fallback (claude/qwen).
|
||||
- **BooTerm** (`apps/booterm`, port `9501`) — PTY/tmux/xterm.js. **Live since May 2026.** bookworm-slim + node-pty + tmux + xterm.js. Tmux session per pane (`bc-<uuid>`), SSH-out works (openssh-client + gosu in the image). Shares Postgres database `boochat`.
|
||||
|
||||
Caddy → Authelia → Tailscale → `100.114.205.53` → 9500/9501/9502. Three apps, **one shared Postgres** (`boocode_db` → `boochat_db`).
|
||||
Caddy → Authelia → Tailscale → `100.114.205.53` → 9500/9501/9502. Three apps, **one shared Postgres** (Docker service `boocode_db`, database name `boochat`).
|
||||
|
||||
**Architectural commitments:**
|
||||
|
||||
- **No embeddings.** Model uses file-view tools (`view_file`, `list_dir`, `grep`, `find_files`) + sidecar analyzers (codecontext, future codesight) + codecontext MCP tools. Walked away from the RAG pipeline May 2026.
|
||||
- **BooChat is read-only** through v1.x. Write tools land in BooCoder at v2.0.
|
||||
- **BooChat is read-only.** Write tools live in BooCoder (shipped v2.0).
|
||||
- **Mount strategy: blanket `/opt:rw`, permission gating at the write-tool layer.** Per-project scoping is policy, not mount. Path-guard correctness is the #1 test target for v2.0.
|
||||
- **External CLI agents (`opencode`/`claude`/`goose`/`pi`) live on the host, not in containers.** BooCoder shells out via local-exec PTY or ACP subprocess. Host install inherits Sam's existing `~/.opencode/`, `~/.claude/`, `~/.config/goose/` configs.
|
||||
- **Protocol roles locked (2026-05-22):** **BooChat = MCP client only** (read-only tool consumer, never enables write-capable MCP servers). **BooCoder = MCP client + MCP server + ACP client (host) + ACP agent (driveable)** — full matrix. BooCoder's ACP-client role replaces raw-PTY dispatch for ACP-capable agents (opencode `opencode acp`, goose `goose acp`); PTY fallback retained for claude/pi/smallcode.
|
||||
@@ -178,7 +178,7 @@ Inspired by Thariq Shihipar's "HTML > Markdown at length" pattern (`claude.com/b
|
||||
- Add HTML-on-request rule to global `AGENTS.md`: "Stay in Markdown by default for all outputs, short or long. Switch to a self-contained `<!DOCTYPE html>...</html>` artifact only when the user explicitly asks (e.g. 'render this as HTML', 'make a dashboard', 'build a diagram')."
|
||||
- Inline the `web-artifacts-builder` "avoid AI slop" design principles for when HTML is requested: no excessive centered layouts, no purple gradients, no uniform rounded corners, no Inter font, no generic AI aesthetics.
|
||||
- Cite Thariq's blog post in the rule comment so future audit passes know where the design conventions came from.
|
||||
1. **Detection at the BooChat backend.** In `apps/chat/services/inference/stream-phase.ts` post-processing: detect any assistant text part starting with `<!DOCTYPE html>` (case-insensitive, whitespace-trimmed) — or wrapped in a fenced ` ```html` block — and tag it as an HTML artifact. Emit a new part kind `html_artifact` into `message_parts` (CHECK constraint update). Payload: `{html_content, char_count, title}`. Title pulled from `<title>` tag or first `<h1>` if available. Detection is opportunistic — when the model produces HTML (because the user asked), the tag fires; otherwise the message stays plain-Markdown and no `html_artifact` part is written.
|
||||
1. **Detection at the BooChat backend.** In `apps/server/src/services/inference/stream-phase.ts` post-processing: detect any assistant text part starting with `<!DOCTYPE html>` (case-insensitive, whitespace-trimmed) — or wrapped in a fenced ` ```html` block — and tag it as an HTML artifact. Emit a new part kind `html_artifact` into `message_parts` (CHECK constraint update). Payload: `{html_content, char_count, title}`. Title pulled from `<title>` tag or first `<h1>` if available. Detection is opportunistic — when the model produces HTML (because the user asked), the tag fires; otherwise the message stays plain-Markdown and no `html_artifact` part is written.
|
||||
1. **Pane-only render surface.** Every assistant message in the chat stream gets an "Open in pane" affordance (icon button in the message footer, alongside the existing copy/regenerate controls). Clicking it opens the message as an artifact pane in BooChat's existing workspace splitter, alongside the file viewer and BooTerm. Pane is dismissible. Pane state persisted via `sessions.workspace_panes jsonb` (the v1.12.1 schema already supports this).
|
||||
- **Markdown pane** — renders via the same Markdown component used inline in `MessageBubble` (so syntax highlighting, fenced code blocks, tables, etc. all work). Header carries **Copy** (writes raw Markdown source to clipboard via `navigator.clipboard.writeText`) and **Download** (`.md`) buttons.
|
||||
- **HTML pane** — renders the artifact in a sandboxed iframe at full pane height. Header carries **Download** (`.html`) only. **No Copy button** — HTML source isn't useful clipboard content; if the user wants the source they can Download and inspect.
|
||||
@@ -261,7 +261,7 @@ Independent batch — ships clean any time after v1.13. Low leverage unless Sam
|
||||
|
||||
## v2.0 — BooCoder: pending changes + dual execution paths + ACP host + MCP server
|
||||
|
||||
**Major version bump.** New app `apps/coder/` inside the existing monorepo (not a separate repo). Lands together with the `boocode_db` → `boochat_db` DB rename and the per-app subdomain split (`code.indifferentketchup.com` → BooChat, `coder.indifferentketchup.com` → BooCoder).
|
||||
**Major version bump.** New app `apps/coder/` inside the existing monorepo (not a separate repo). Shipped with database rename `boocode` → `boochat` and subdomain split (`code.indifferentketchup.com` → BooChat, `coder.indifferentketchup.com` → BooCoder).
|
||||
|
||||
**Shipped v2.0.0–v2.0.4.** All 8 phases complete. See retrospective below.
|
||||
|
||||
@@ -391,8 +391,8 @@ Per-session Docker sandbox spawned by BooCoder on first write. Only project path
|
||||
|`boochat` (was `boocode`) |`100.114.205.53:9500`|`/opt:/opt:ro` |Read-only chat + SPA host + MCP client |Live (renames at v2.0)|
|
||||
|`booterm` |`100.114.205.53:9501`|`/opt:/opt` |PTY/tmux terminal sessions |**Live (May 2026)** |
|
||||
|`boocoder` |`100.114.205.53:9502`|`/opt:/opt:rw` (policy-gated)|Write tools + ACP host + MCP client + MCP server + external-CLI dispatch|**Shipped v2.0.0–v2.0.4** |
|
||||
|`boochat_db` (was `boocode_db`)|`127.0.0.1:5500` |`boocode_pgdata` volume |Postgres 16-alpine (shared by all three) |**Live** (renamed at v2.0)|
|
||||
|`codecontext` |`:8765` (internal) |`/opt/projects:/workspace:ro`|MCP server for architect tools |**Live (v1.12.0)** |
|
||||
|**`boochat`** (Docker service `boocode_db`)|`127.0.0.1:5500` |`boocode_pgdata` volume |Postgres 16-alpine (shared by all three) |**Live** (DB renamed from `boocode` at v2.0)|
|
||||
|`codecontext` |`:8080` (internal, Docker network) |`/opt:/opt:ro`|Go HTTP sidecar for code graph tools |**Live (v1.12.0)** |
|
||||
|
||||
### Caddy routing target (post-v2.0)
|
||||
|
||||
@@ -435,7 +435,7 @@ term.indifferentketchup.com → booterm :9501 (or routed under code.
|
||||
- **v1.14.x-html:** `message_parts.kind` CHECK constraint extended with `'html_artifact'` value
|
||||
- **v1.15:** `permissions` table, `agent_permissions` join, `session_permissions` join, `mcp_servers (name, type, transport, url_or_command, enabled, config_hash, last_probed_at)` registry
|
||||
- **v1.16:** `repo_health_cache (project_id, file_hashes_sig, payload JSONB, created_at)`
|
||||
- **v2.0:** `pending_changes (id, session_id, file_path, diff TEXT, status, created_at)`; `tasks`, `task_templates`, `pipelines`, `pipeline_runs`; `available_agents (name, install_path, version, supports_acp, supports_mcp_client, last_probed_at)`; `human_inbox` view; DB rename `boocode_db` → `boochat_db`
|
||||
- **v2.0 (shipped):** `pending_changes`, `tasks`, `available_agents`, `human_inbox` view; database renamed `boocode` → `boochat`
|
||||
- **v2.2:** none (`boocoder acp` is a new entry point, not a schema change)
|
||||
|
||||
-----
|
||||
@@ -517,8 +517,8 @@ Full inventory and rationale in `boocode_code_review.md`. Headline items below;
|
||||
|
||||
### Monorepo / multi-app structure (2026-05-22, locked)
|
||||
|
||||
- **BooCode is a 3-app monorepo** at `/opt/boocode/`: `apps/chat` (read-only, currently the live thing at 9500), `apps/coder` (write tools + external CLI dispatch, 9502, v2.0 planned), `apps/booterm` (PTY terminal, **live since May 2026 at 9501**). Shared `apps/server` (Fastify backend) and `apps/web` (React shell hosting the three surfaces as tabs).
|
||||
- **Single shared database, rename `boocode_db` → `boochat_db` when BooCoder lands.** All three surfaces in one Postgres. Cross-surface joins are valuable (coder task → originating chat → term debugging session). Separate databases would break this.
|
||||
- **BooCode is a 3-surface monorepo** at `/opt/boocode/`: BooChat (`apps/server` + `apps/web`, :9500), BooCoder (`apps/coder`, :9502, **shipped v2.0–v2.1.0**, host systemd), BooTerm (`apps/booterm`, :9501, live since May 2026). One React SPA hosts chat, coder, and terminal panes.
|
||||
- **Single shared database `boochat`.** Docker service `boocode_db`, all three surfaces connect to the same Postgres. Cross-surface joins are valuable (coder task → originating chat → term debugging session).
|
||||
- **Mount strategy: blanket `/opt:rw`, policy enforcement at the write-tool layer.** Per-project scoping is logic, not mount. Path-guard correctness becomes the highest-priority test target for v2.0 — fuzz it, property-test it, every traversal-attack pattern (including MCP-served filesystem writes).
|
||||
- **External CLI agents on the host, not in containers.** BooCoder shells out via local-exec PTY or ACP subprocess (`node-pty`, host shell, or `child_process.spawn('opencode', ['acp'])`). Host install inherits Sam's existing `~/.opencode/`, `~/.claude/`, `~/.config/goose/` configs without re-mounting. Containerize later only if a concrete reason emerges.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user