docs: add v2 architecture planning and research findings
Architecture decision: Tauri 2.x + Svelte 5 + Claude Agent SDK (Node.js sidecar). Includes research on Agent SDK streaming, xterm.js performance, WebKit2GTK limitations, and adversarial review corrections (two-tier observation, Svelte 5 over Solid.js, SDK abstraction layer). Six implementation phases defined, MVP = Phases 1-4.
This commit is contained in:
parent
70f8030ae4
commit
2723fbf4be
4 changed files with 581 additions and 0 deletions
209
docs/findings.md
Normal file
209
docs/findings.md
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
# BTerminal v2 — Research Findings
|
||||
|
||||
## 1. Claude Agent SDK — The Foundation
|
||||
|
||||
**Source:** https://platform.claude.com/docs/en/agent-sdk/overview
|
||||
|
||||
The Claude Agent SDK (formerly Claude Code SDK, renamed Sept 2025) provides everything we need:
|
||||
|
||||
### Streaming API
|
||||
```typescript
|
||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
||||
|
||||
for await (const message of query({
|
||||
prompt: "Fix the bug",
|
||||
options: { allowedTools: ["Read", "Edit", "Bash"] }
|
||||
})) {
|
||||
// Each message is structured, typed, parseable
|
||||
console.log(message);
|
||||
}
|
||||
```
|
||||
|
||||
### Subagent Detection
|
||||
Messages from subagents include `parent_tool_use_id`:
|
||||
```typescript
|
||||
// Check for subagent invocation
|
||||
for (const block of msg.message?.content ?? []) {
|
||||
if (block.type === "tool_use" && block.name === "Task") {
|
||||
console.log(`Subagent invoked: ${block.input.subagent_type}`);
|
||||
}
|
||||
}
|
||||
// Check if message is from within a subagent
|
||||
if (msg.parent_tool_use_id) {
|
||||
console.log("Running inside subagent");
|
||||
}
|
||||
```
|
||||
|
||||
### Session Management
|
||||
- `session_id` captured from init message
|
||||
- Resume with `options: { resume: sessionId }`
|
||||
- Subagent transcripts persist independently
|
||||
|
||||
### Hooks
|
||||
`PreToolUse`, `PostToolUse`, `Stop`, `SessionStart`, `SessionEnd`, `UserPromptSubmit`
|
||||
|
||||
### Telemetry
|
||||
Every `SDKResultMessage` contains: `total_cost_usd`, `duration_ms`, per-model `modelUsage` breakdowns.
|
||||
|
||||
### Key Insight
|
||||
**We don't need terminal emulation for SDK agents.** The SDK gives us structured data — we can render it as rich UI (markdown, diff views, file cards, agent trees) instead of raw terminal text. Terminal emulation (xterm.js) is only needed for SSH, local shell, and legacy Claude CLI sessions.
|
||||
|
||||
---
|
||||
|
||||
## 2. Tauri + xterm.js — Proven Stack
|
||||
|
||||
### Existing Projects
|
||||
- **tauri-terminal** (github.com/marc2332/tauri-terminal) — basic Tauri + xterm.js + portable-pty
|
||||
- **Terminon** (github.com/Shabari-K-S/terminon) — Tauri v2 + React + xterm.js, SSH profiles, split panes
|
||||
- **terraphim-liquid-glass-terminal** — Tauri + xterm.js with design effects
|
||||
- **tauri-plugin-pty** (github.com/Tnze/tauri-plugin-pty) — PTY plugin for Tauri 2, xterm.js bridge
|
||||
|
||||
### Integration Pattern
|
||||
```
|
||||
Frontend (xterm.js) ←→ Tauri IPC ←→ Rust PTY (portable-pty) ←→ Shell/SSH/Claude
|
||||
```
|
||||
- `pty.onData()` → `term.write()` (output)
|
||||
- `term.onData()` → `pty.write()` (input)
|
||||
|
||||
### Tauri IPC Latency
|
||||
- Linux: ~5ms for typical payloads (serialization-free IPC in v2)
|
||||
- For terminal output: irrelevant. Claude outputs text at human-readable speed.
|
||||
- For keystroke echo: 5ms + xterm.js render = ~10-15ms total. Acceptable.
|
||||
|
||||
---
|
||||
|
||||
## 3. Terminal Performance Context
|
||||
|
||||
### Native Terminal Latency (for reference)
|
||||
| Terminal | Latency | Notes |
|
||||
|---|---|---|
|
||||
| xterm (native) | ~10ms | Gold standard |
|
||||
| Alacritty | ~12ms | GPU-rendered Rust |
|
||||
| Kitty | ~13ms | GPU-rendered |
|
||||
| VTE (GNOME Terminal) | ~50ms | GTK3/4, spikes above |
|
||||
| Hyper (Electron+xterm.js) | ~40ms | Web-based worst case |
|
||||
|
||||
### Throughput (find /usr benchmark)
|
||||
All within 0.5s of each other: xterm 2.2s, alacritty 2.2s, wezterm 2.8s. "Not meaningfully different to a human."
|
||||
|
||||
### Memory
|
||||
- Alacritty: ~30MB
|
||||
- WezTerm: ~45MB
|
||||
- xterm (native): ~5MB
|
||||
|
||||
### Verdict for BTerminal v2
|
||||
xterm.js in Tauri will be ~20-30ms latency, ~40MB per terminal instance. For Claude sessions (AI output, not vim), this is perfectly fine. The VTE we currently use in GTK3 is actually *slower* at ~50ms.
|
||||
|
||||
---
|
||||
|
||||
## 4. Zellij Architecture (Inspiration)
|
||||
|
||||
**Source:** Research agent findings
|
||||
|
||||
Zellij uses WASM plugins for extensibility:
|
||||
- Plugins communicate via message passing at WASM boundary
|
||||
- Permission model controls what plugins can access
|
||||
- Event types for rendering, input, lifecycle
|
||||
- Layout defined in KDL files
|
||||
|
||||
**Relevance:** We don't need WASM plugins. Our "plugins" are just different pane types (terminal, agent, markdown). But the layout concept (KDL or JSON layout definitions) is worth borrowing for saved layouts.
|
||||
|
||||
---
|
||||
|
||||
## 5. 32:9 Ultrawide Design Patterns
|
||||
|
||||
**Key Insight:** 5120px width ÷ ~600px per useful pane = ~8 panes max, ~4-5 comfortable.
|
||||
|
||||
**Layout Philosophy:**
|
||||
- Center of screen = primary attention (1-2 main agent panes)
|
||||
- Left edge = navigation (session sidebar, 250-300px)
|
||||
- Right edge = context (agent tree, file viewer, 350-450px)
|
||||
- Never use tabs for primary content — everything visible
|
||||
- Tabs only for switching between saved layouts
|
||||
|
||||
**Interaction Model:**
|
||||
- Click sidebar session → opens in next available pane slot
|
||||
- Agent spawns subagent → new pane auto-appears (or tree node if panes full)
|
||||
- File reference in agent output → click to open markdown viewer pane
|
||||
- Drag pane borders to resize
|
||||
- Keyboard: Ctrl+1-8 to focus pane, Ctrl+Shift+Arrow to move pane
|
||||
|
||||
---
|
||||
|
||||
## 6. Frontend Framework Choice
|
||||
|
||||
### Why Solid.js
|
||||
- **Fine-grained reactivity** — updates only the DOM nodes that changed, not the component tree
|
||||
- **No VDOM** — critical when we have 4-8 panes each streaming data
|
||||
- **Small bundle** — ~7KB vs React's ~40KB
|
||||
- **JSX familiar** — easy for anyone who knows React
|
||||
- **Signals** — perfect for streaming agent state
|
||||
|
||||
### Alternative: Svelte
|
||||
- Also no VDOM, also reactive, slightly larger community
|
||||
- Slightly more ceremony for stores/state management
|
||||
- Would also work, personal preference
|
||||
|
||||
### NOT React
|
||||
- VDOM reconciliation across 4-8 simultaneously updating panes = CPU waste
|
||||
- Larger bundle
|
||||
- State management complexity (need Redux/Zustand for cross-pane state)
|
||||
|
||||
---
|
||||
|
||||
## 7. Key Technical Risks
|
||||
|
||||
| Risk | Mitigation |
|
||||
|---|---|
|
||||
| **WebKit2GTK has NO WebGL** — xterm.js falls back to Canvas on Linux | Use xterm.js Canvas addon explicitly. For AI output (not vim), Canvas at 60fps is fine. |
|
||||
| xterm.js performance with 4+ instances (Canvas mode) | Lazy init (create xterm only when pane visible), limit to 4-6 active terminals |
|
||||
| Agent SDK TS package may not run in Tauri's webview | Run SDK in Rust sidecar process, stream to frontend via Tauri events |
|
||||
| Tauri IPC bottleneck with high-throughput agent output | Batch messages, use Tauri events (push) not commands (pull) |
|
||||
| File watcher flooding on rapid saves | Debounce 200ms in Rust before sending to frontend |
|
||||
| Layout state persistence across restarts | SQLite for sessions + layout, atomic writes |
|
||||
| Tauri multi-webview behind `unstable` flag | Single webview with CSS Grid panes, not multiple webviews |
|
||||
|
||||
---
|
||||
|
||||
## 8. Claude Code CLI Observation (Alternative to SDK)
|
||||
|
||||
**Critical discovery:** We can observe ANY running Claude Code session (even interactive CLI ones) via two mechanisms:
|
||||
|
||||
### A. `stream-json` output mode
|
||||
```bash
|
||||
claude -p "fix the bug" --output-format stream-json
|
||||
```
|
||||
Emits typed events: `stream_event`, `assistant`, `user`, `system` (init carries session_id), `result`.
|
||||
|
||||
### B. JSONL session file tailing
|
||||
Session files live at `~/.claude/projects/<encoded-dir-path>/<session-uuid>.jsonl`. Append-only, written immediately. Can be `tail -f`'d for external observation.
|
||||
|
||||
Path encoding: `/home/user/project` → `-home-user-project`
|
||||
|
||||
### C. Hooks (SDK only)
|
||||
`SubagentStart`, `SubagentStop` (gives `agent_transcript_path`), `PreToolUse`, `PostToolUse`, `Stop`, `Notification`, `TeammateIdle`
|
||||
|
||||
### Implication for BTerminal v2
|
||||
**Three observation tiers:**
|
||||
1. **SDK sessions** (best): Full structured streaming, subagent detection, hooks, cost tracking
|
||||
2. **CLI sessions with stream-json** (good): Structured output, but requires spawning claude with `-p` flag (non-interactive)
|
||||
3. **Interactive CLI sessions** (fallback): Tail JSONL session files + show terminal via xterm.js
|
||||
|
||||
---
|
||||
|
||||
## 9. Agent Teams (Experimental)
|
||||
|
||||
`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` enables full independent Claude Code instances sharing a task list and mailbox.
|
||||
|
||||
- 3-5 teammates is the practical sweet spot (linear token cost)
|
||||
- Display modes: in-process (Shift+Down cycles), tmux (own pane each), auto
|
||||
- Session resumption is broken for in-process teammates
|
||||
- BTerminal v2 could become the ideal frontend for Agent Teams — each teammate gets its own pane
|
||||
|
||||
---
|
||||
|
||||
## 10. Competing Approaches
|
||||
|
||||
- **claude-squad** (Go+tmux): Most adopted multi-agent manager. BTerminal v2 would replace this.
|
||||
- **agent-deck**: MCP socket pooling (~85-90% memory savings). Could integrate as backend.
|
||||
- **Git worktrees**: Dominant isolation strategy for parallel Claude sessions. BTerminal should support spawning agents in worktrees.
|
||||
177
docs/phases.md
Normal file
177
docs/phases.md
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
# BTerminal v2 — Implementation Phases
|
||||
|
||||
See [task_plan.md](task_plan.md) for architecture decisions, error handling, and testing strategy.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Project Scaffolding [status: not_started] — MVP
|
||||
|
||||
- [ ] Create feature branch `v2-mission-control`
|
||||
- [ ] Initialize Tauri 2.x project with Svelte 5 frontend
|
||||
- [ ] Project structure (see below)
|
||||
- [ ] Basic Tauri window with Catppuccin Mocha CSS variables
|
||||
- [ ] Verify Tauri builds and launches on target system
|
||||
- [ ] Set up dev scripts (dev, build, lint)
|
||||
|
||||
### File Structure
|
||||
```
|
||||
bterminal-v2/
|
||||
src-tauri/
|
||||
src/
|
||||
main.rs # Tauri app entry
|
||||
pty.rs # PTY management (portable-pty, not plugin)
|
||||
sidecar.rs # Node.js sidecar lifecycle (spawn, restart, health)
|
||||
watcher.rs # File watcher for markdown viewer
|
||||
session.rs # Session persistence (SQLite via rusqlite)
|
||||
Cargo.toml
|
||||
src/
|
||||
App.svelte # Root layout
|
||||
lib/
|
||||
components/
|
||||
Layout/
|
||||
TilingGrid.svelte # Dynamic tiling manager
|
||||
PaneContainer.svelte # Individual pane wrapper
|
||||
PaneHeader.svelte # Pane title bar with controls
|
||||
Terminal/
|
||||
TerminalPane.svelte # xterm.js terminal pane
|
||||
Agent/
|
||||
AgentPane.svelte # SDK agent structured output
|
||||
AgentTree.svelte # Subagent tree visualization
|
||||
ToolCallCard.svelte # Individual tool call display
|
||||
Markdown/
|
||||
MarkdownPane.svelte # Live markdown file viewer
|
||||
Sidebar/
|
||||
SessionList.svelte # Session browser
|
||||
stores/
|
||||
sessions.ts # Session state ($state runes)
|
||||
agents.ts # Active agent tracking
|
||||
layout.ts # Pane layout state
|
||||
adapters/
|
||||
sdk-messages.ts # SDK message abstraction layer
|
||||
pty-bridge.ts # PTY IPC wrapper
|
||||
styles/
|
||||
catppuccin.css # Theme CSS variables
|
||||
app.css
|
||||
sidecar/
|
||||
agent-runner.ts # Node.js sidecar entry point
|
||||
package.json # Agent SDK dependency
|
||||
esbuild.config.ts # Bundle to single file
|
||||
package.json
|
||||
svelte.config.js
|
||||
vite.config.ts
|
||||
tauri.conf.json
|
||||
```
|
||||
|
||||
**Key change from v1:** Using portable-pty directly from Rust instead of tauri-plugin-pty (38-star community plugin). portable-pty is well-maintained (used by WezTerm). More work upfront, more reliable long-term.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Terminal Pane + Layout [status: not_started] — MVP
|
||||
|
||||
### Layout (responsive)
|
||||
|
||||
**32:9 (5120px) — full density:**
|
||||
```
|
||||
+--------+------------------------------------+--------+
|
||||
|Sidebar | 2-4 panes, CSS Grid, resizable | Right |
|
||||
| 260px | | 380px |
|
||||
+--------+------------------------------------+--------+
|
||||
```
|
||||
|
||||
**16:9 (1920px) — degraded but functional:**
|
||||
```
|
||||
+--------+-------------------------+
|
||||
|Sidebar | 1-2 panes | (right panel collapses to overlay)
|
||||
| 240px | |
|
||||
+--------+-------------------------+
|
||||
```
|
||||
|
||||
- [ ] CSS Grid layout with sidebar + main area + optional right panel
|
||||
- [ ] Responsive breakpoints (ultrawide / standard / narrow)
|
||||
- [ ] Pane resize via drag handles (use CSS `resize` or lightweight lib)
|
||||
- [ ] Layout presets: 1-col, 2-col, 3-col, 2x2, master+stack
|
||||
- [ ] Save/restore layout to SQLite
|
||||
- [ ] Keyboard: Ctrl+1-4 focus pane, Ctrl+Shift+arrows move
|
||||
|
||||
### Terminal
|
||||
- [ ] xterm.js with Canvas addon (explicit — no WebGL dependency)
|
||||
- [ ] Catppuccin Mocha theme for xterm.js
|
||||
- [ ] PTY spawn from Rust (portable-pty), stream to frontend via Tauri events
|
||||
- [ ] Terminal resize -> PTY resize
|
||||
- [ ] Copy/paste (Ctrl+Shift+C/V)
|
||||
- [ ] SSH session: spawn `ssh` command in PTY
|
||||
- [ ] Local shell: spawn user's $SHELL
|
||||
- [ ] Claude Code CLI: spawn `claude` in PTY (fallback mode)
|
||||
|
||||
**Milestone: After Phase 2, we have a working multi-pane terminal.** Usable as a daily driver even without agent features.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Agent SDK Integration [status: not_started] — MVP
|
||||
|
||||
- [ ] Node.js sidecar: thin wrapper around Agent SDK `query()`
|
||||
- [ ] Sidecar communication: Rust spawns Node.js, stdio NDJSON
|
||||
- [ ] Sidecar lifecycle: spawn on demand, detect crash, restart
|
||||
- [ ] SDK message adapter (abstraction layer)
|
||||
- [ ] Agent pane: renders structured messages
|
||||
- Text -> markdown rendered
|
||||
- Tool calls -> collapsible cards (tool name + input + output)
|
||||
- Subagent spawn -> tree node + optional new pane
|
||||
- Errors -> highlighted error card
|
||||
- Cost/tokens -> pane header metrics
|
||||
- [ ] Auto-scroll with scroll-lock on user scroll-up
|
||||
- [ ] Agent status indicator (running/thinking/waiting/done/error)
|
||||
- [ ] Start/stop/cancel agent from UI
|
||||
- [ ] Session resume (SDK `resume: sessionId`)
|
||||
|
||||
**Milestone: After Phase 3, we have the core differentiator.** SDK agents run in structured panes alongside raw terminals.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Session Management + Markdown Viewer [status: not_started] — MVP
|
||||
|
||||
### Sessions
|
||||
- [ ] SQLite persistence for sessions (rusqlite)
|
||||
- [ ] Session types: SSH, Claude CLI, Agent SDK, Local Shell
|
||||
- [ ] Session CRUD in sidebar
|
||||
- [ ] Session groups/folders
|
||||
- [ ] Remember last layout on restart
|
||||
|
||||
### Markdown Viewer
|
||||
- [ ] File watcher (notify crate) -> Tauri events -> frontend
|
||||
- [ ] Markdown rendering (marked.js or remark)
|
||||
- [ ] Syntax highlighting (Shiki)
|
||||
- [ ] Open from sidebar or from agent output file references
|
||||
- [ ] Debounce file watcher (200ms)
|
||||
|
||||
**Milestone: After Phase 4 = MVP ship.** Full session management, structured agent panes, terminal panes, markdown viewer.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Agent Tree + Polish [status: not_started] — Post-MVP
|
||||
|
||||
- [ ] Agent tree visualization (SVG, compact horizontal layout)
|
||||
- [ ] Click tree node -> focus agent pane
|
||||
- [ ] Aggregate cost per subtree
|
||||
- [ ] Global status bar (total cost, active agents, uptime)
|
||||
- [ ] Notification system (agent done, error)
|
||||
- [ ] Global keyboard shortcuts
|
||||
- [ ] Settings dialog
|
||||
- [ ] ctx integration (port from v1)
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Packaging + Distribution [status: not_started] — Post-MVP
|
||||
|
||||
- [ ] install.sh v2 (check Node.js, install Tauri runtime deps)
|
||||
- [ ] AppImage build (single file, works everywhere)
|
||||
- [ ] .deb package (Debian/Ubuntu)
|
||||
- [ ] GitHub Actions CI for building releases
|
||||
- [ ] Auto-update mechanism (Tauri updater)
|
||||
- [ ] Migrate bterminal.svg icon
|
||||
- [ ] README update
|
||||
|
||||
### System Requirements
|
||||
- Node.js 20+ (for Agent SDK sidecar)
|
||||
- WebKit2GTK 4.1+ (Tauri runtime)
|
||||
- Linux x86_64 (primary target)
|
||||
37
docs/progress.md
Normal file
37
docs/progress.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# BTerminal v2 — Progress Log
|
||||
|
||||
## Session: 2026-03-05
|
||||
|
||||
### Research Phase (complete)
|
||||
- [x] Analyzed current BTerminal v1 codebase (2092 lines Python, GTK3+VTE)
|
||||
- [x] Queried Memora — no existing BTerminal memories
|
||||
- [x] Researched Claude Agent SDK — found structured streaming, subagent tracking, hooks
|
||||
- [x] Researched Tauri + xterm.js ecosystem — found 4+ working projects
|
||||
- [x] Researched terminal latency benchmarks — xterm.js acceptable for AI output
|
||||
- [x] Researched 32:9 ultrawide layout patterns
|
||||
- [x] Evaluated GTK4 vs Tauri vs pure Rust — Tauri wins for this use case
|
||||
- [x] Created task_plan.md with 8 phases
|
||||
- [x] Created findings.md with 7 research areas
|
||||
|
||||
### Technology Decision (complete)
|
||||
- Decision: **Tauri 2.x + Solid.js + Claude Agent SDK + xterm.js**
|
||||
- Rationale documented in task_plan.md Phase 0
|
||||
|
||||
### Adversarial Review (complete)
|
||||
- [x] Spawned devil's advocate agent to attack the plan
|
||||
- [x] Identified 5 fatal/critical issues:
|
||||
1. Node.js sidecar requirement unacknowledged
|
||||
2. SDK 0.2.x instability — need abstraction layer
|
||||
3. Three-tier observation overengineered → simplified to two-tier
|
||||
4. Solid.js ecosystem too small → switched to Svelte 5
|
||||
5. Missing: packaging, error handling, testing, responsive design
|
||||
- [x] Revised plan (Rev 2) incorporating all corrections
|
||||
- [x] Added error handling strategy table
|
||||
- [x] Added testing strategy table
|
||||
- [x] Defined MVP boundary (Phases 1-4)
|
||||
- [x] Added responsive layout requirement (1920px degraded mode)
|
||||
|
||||
### Next Steps
|
||||
- [ ] Present plan to user for review and decision
|
||||
- [ ] Create feature branch
|
||||
- [ ] Begin Phase 1: Project scaffolding
|
||||
158
docs/task_plan.md
Normal file
158
docs/task_plan.md
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
# BTerminal v2 — Claude Agent Mission Control
|
||||
|
||||
## Goal
|
||||
Redesign BTerminal from a GTK3 terminal emulator into a **multi-session Claude agent dashboard** optimized for 32:9 ultrawide (5120x1440). Simultaneous visibility of all active sessions, agent tree visualization, inline markdown rendering, maximum information density.
|
||||
|
||||
## Status: PLANNING (Rev 2 — post-adversarial review)
|
||||
|
||||
---
|
||||
|
||||
## Adversarial Review Corrections
|
||||
|
||||
The initial plan had critical gaps surfaced by a devil's advocate review. Key corrections:
|
||||
|
||||
1. **Node.js sidecar is required** — Claude Agent SDK is TS/Python, not Rust. Cannot run in Tauri's webview or Rust. Must spawn a Node.js sidecar process. This has real packaging/complexity implications.
|
||||
2. **SDK is 0.2.x (pre-1.0)** — 127 versions in 5 months. We MUST have an abstraction layer (message adapter) between SDK wire format and UI renderers.
|
||||
3. **Three-tier observation → Two-tier** — Drop JSONL tailing of interactive CLI sessions. Too fragile (undocumented internal format). Just two tiers: SDK (structured) and Terminal (raw).
|
||||
4. **Scope reduction** — Phases 1-4 are the MVP. Phases 5-8 are post-MVP. Ship a usable tool after Phase 4.
|
||||
5. **Svelte 5 over Solid.js** — Adversarial review is right: Solid's ecosystem is too small, Svelte 5 runes match its reactivity model with much larger ecosystem.
|
||||
6. **Responsive layout required** — Cannot design only for 32:9. Must work on 1920x1080 with degraded but functional layout.
|
||||
7. **Packaging story must be planned upfront** — Not a Phase 8 afterthought.
|
||||
8. **Error handling and testing strategy required** — Not optional.
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Technology Decision [status: complete]
|
||||
|
||||
### Decision: **Tauri 2.x + Svelte 5 + Claude Agent SDK (via Node.js sidecar)**
|
||||
|
||||
**Why Tauri over Electron:**
|
||||
- Rust backend is genuinely useful for PTY management and file watching
|
||||
- Memory overhead matters when running 4+ agent sidecars
|
||||
- Better security model (no Node.js in renderer)
|
||||
- **Acknowledged limitation:** WebKit2GTK has no WebGL. xterm.js uses Canvas fallback. Acceptable for 2-4 AI output panes. NOT for 8+ high-throughput terminals.
|
||||
- If Canvas proves unacceptable: escape hatch is switching to Electron (frontend code is framework-agnostic web tech, mostly portable)
|
||||
|
||||
**Why Svelte 5 (revised from Solid.js):**
|
||||
- Fine-grained reactivity via `$state`/`$derived` runes — comparable to Solid signals
|
||||
- No VDOM — same performance characteristic
|
||||
- Much larger ecosystem (xterm.js wrappers, layout libraries, component libs)
|
||||
- Better TypeScript support and devtools
|
||||
- Svelte 5 runes eliminated the ceremony that older Svelte versions had
|
||||
|
||||
**Why NOT React:**
|
||||
- VDOM reconciliation across 4+ simultaneously streaming panes = CPU waste
|
||||
- Larger bundle (40KB vs ~5KB Svelte runtime)
|
||||
|
||||
### Architecture: Two-Tier Observation
|
||||
|
||||
| Session Type | Backend | Frontend | Observation |
|
||||
|---|---|---|---|
|
||||
| **SDK Agent** | Node.js sidecar → Rust bridge → Tauri events | Structured rich panels | Full: streaming, subagents, hooks, cost |
|
||||
| **Terminal** (SSH/CLI/Shell) | PTY via portable-pty (Rust) | xterm.js terminal | Raw terminal only |
|
||||
| **File viewer** | Rust file watcher (notify) | Markdown renderer | N/A |
|
||||
|
||||
**Dropped:** Interactive CLI JSONL tailing (undocumented internal format, fragile).
|
||||
**Dropped:** CLI stream-json tier (SDK handles this better for non-interactive use).
|
||||
|
||||
### Node.js Sidecar Architecture (critical detail)
|
||||
|
||||
The Agent SDK cannot run in Rust or the webview. Solution:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Tauri App │
|
||||
│ │
|
||||
│ ┌──────────┐ Tauri IPC ┌──────────────────┐ │
|
||||
│ │ WebView │ ←────────────→ │ Rust Backend │ │
|
||||
│ │ (Svelte) │ │ │ │
|
||||
│ └──────────┘ │ ├── PTY manager │ │
|
||||
│ │ ├── File watcher│ │
|
||||
│ │ └── Sidecar mgr │──┼──→ Node.js process
|
||||
│ └──────────────────┘ │ (Agent SDK)
|
||||
│ │ stdio JSON-RPC
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- Rust spawns Node.js child process on demand (when user starts an SDK agent session)
|
||||
- Communication: stdio with newline-delimited JSON (simple, no socket server)
|
||||
- Node.js process runs a thin wrapper that calls `query()` and forwards messages
|
||||
- If sidecar crashes: detect via process exit, show error in UI, offer restart
|
||||
- **Packaging:** Bundle the sidecar JS as a single file (esbuild bundle). Require Node.js 20+ as system dependency. Document in install.sh.
|
||||
- **Future:** Could replace Node.js with Deno (single binary, no npm) for better packaging.
|
||||
|
||||
### SDK Abstraction Layer
|
||||
|
||||
```typescript
|
||||
// adapters/sdk-messages.ts — insulates UI from SDK wire format changes
|
||||
interface AgentMessage {
|
||||
id: string;
|
||||
type: 'text' | 'tool_call' | 'tool_result' | 'subagent_spawn' | 'status' | 'cost';
|
||||
parentId?: string; // for subagent tracking
|
||||
content: unknown; // type-specific payload
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
// Adapter function — this is the ONLY place that knows SDK internals
|
||||
function adaptSDKMessage(raw: SDKMessage): AgentMessage { ... }
|
||||
```
|
||||
|
||||
When SDK changes its message format, only the adapter needs updating.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
See [phases.md](phases.md) for the full phased implementation plan (Phases 1-6).
|
||||
|
||||
- **MVP:** Phases 1-4 (scaffolding, terminal+layout, agent SDK, session mgmt+markdown)
|
||||
- **Post-MVP:** Phases 5-6 (agent tree, polish, packaging)
|
||||
|
||||
---
|
||||
|
||||
## Decisions Log
|
||||
|
||||
| Decision | Rationale | Date |
|
||||
|---|---|---|
|
||||
| Tauri 2.x over GTK4 | Web frontend for markdown, tiling, agent viz; Rust backend for PTY/SDK | 2026-03-05 |
|
||||
| Tauri over Electron | Memory efficiency, Rust backend value, security model. Escape hatch: port to Electron if Canvas perf unacceptable | 2026-03-05 |
|
||||
| Svelte 5 over Solid.js | Larger ecosystem, Svelte 5 runes match Solid's reactivity, better tooling | 2026-03-05 |
|
||||
| Two-tier over three-tier | Drop JSONL tailing (undocumented internal format). SDK or raw terminal, nothing in between | 2026-03-05 |
|
||||
| portable-pty over tauri-plugin-pty | Direct Rust crate (used by WezTerm) vs 38-star community plugin | 2026-03-05 |
|
||||
| Node.js sidecar for SDK | SDK is TS/Python only. Sidecar with stdio NDJSON. Future: replace with Deno | 2026-03-05 |
|
||||
| SDK abstraction layer | SDK is 0.2.x, 127 versions in 5 months. Must insulate UI from wire format changes | 2026-03-05 |
|
||||
| MVP = Phases 1-4 | Ship usable tool before tackling tree viz, packaging, polish | 2026-03-05 |
|
||||
| Canvas addon (not WebGL) | WebKit2GTK has no WebGL. Explicit Canvas addon avoids silent fallback | 2026-03-05 |
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **Node.js or Deno for sidecar?** Node.js has the SDK package. Deno would be a single binary (better packaging) but needs SDK compatibility testing. → Start Node.js, evaluate Deno later.
|
||||
2. **Multi-machine support?** Remote agents via WebSocket. Phase 7+ feature.
|
||||
3. **Agent Teams integration?** Experimental Anthropic feature. Natural fit but adds complexity. Phase 7+.
|
||||
4. **Electron escape hatch threshold?** If Canvas xterm.js proves >50ms latency on target system with 4 panes, switch to Electron. Benchmark in Phase 2.
|
||||
|
||||
## Error Handling Strategy
|
||||
|
||||
| Failure | Response |
|
||||
|---|---|
|
||||
| Node.js sidecar crash | Detect via process exit code, show error banner, offer restart button |
|
||||
| Claude API 529 (overloaded) | Exponential backoff in sidecar, show "rate limited" status in pane |
|
||||
| API key expired | Sidecar reports auth error, prompt user to update key in settings |
|
||||
| PTY process exit | Show exit code in terminal, offer reconnect for SSH |
|
||||
| WebKit2GTK OOM | Limit to 4 active xterm.js instances, lazy-init others |
|
||||
| Simultaneous resize of N terminals | Debounce resize events (100ms), batch PTY resize calls |
|
||||
| SDK message format change | Adapter layer catches unknown types, logs warning, renders as raw JSON fallback |
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
| Layer | Tool | What |
|
||||
|---|---|---|
|
||||
| SDK adapter | Vitest | Message parsing, type discrimination, unknown message fallback |
|
||||
| Svelte components | Svelte testing library | Pane rendering, layout responsive breakpoints |
|
||||
| Rust backend | cargo test | PTY lifecycle, sidecar spawn/kill, file watcher debounce |
|
||||
| Integration | Playwright | Full app: open terminal, run command, verify output |
|
||||
| Manual | Developer testing | xterm.js Canvas performance with 4 panes on target hardware |
|
||||
|
||||
## Errors Encountered
|
||||
|
||||
(none yet)
|
||||
Loading…
Add table
Add a link
Reference in a new issue