docs: update meta files for provider adapter implementation

This commit is contained in:
Hibryda 2026-03-11 02:08:45 +01:00
parent 11b00f18f8
commit 8f4faaafa3
6 changed files with 93 additions and 43 deletions

View file

@ -5,7 +5,7 @@
- v1 is a single-file Python app (`bterminal.py`). Changes are localized.
- v2 docs are in `docs/`. Architecture decisions are in `docs/task_plan.md`.
- v2 Phases 1-7 + multi-machine (A-D) + profiles/skills complete. Extras: SSH, ctx, themes, detached mode, auto-updater, shiki, copy/paste, session resume, drag-resize, session groups, Deno sidecar, Claude profiles, skill discovery.
- v3 Mission Control (All Phases 1-10 Complete + S-1 Phase 1/1.5/2 Conflict Detection): project groups, workspace store, 12 Workspace components, session continuity, workspace teardown, file overlap conflict detection (Bash writes, dismiss/acknowledge, worktree suppression), inotify-based external write detection (S-1 Phase 2), dead v2 component cleanup. 202 vitest + 39 cargo tests.
- v3 Mission Control (All Phases 1-10 Complete + S-1 Phase 1/1.5/2 Conflict Detection + Provider Adapter Pattern): project groups, workspace store, 12 Workspace components, session continuity, workspace teardown, file overlap conflict detection, inotify-based external write detection, multi-provider adapter pattern (3 phases: core abstraction, settings UI, sidecar routing), dead v2 component cleanup. 202 vitest + 42 cargo tests.
- v3 docs: `docs/v3-task_plan.md`, `docs/v3-findings.md`, `docs/v3-progress.md`.
- Consult Memora (tag: `bterminal`) before making architectural changes.
@ -31,17 +31,18 @@
## Key Technical Constraints
- WebKit2GTK has no WebGL — xterm.js must use Canvas addon explicitly.
- Agent sessions use `@anthropic-ai/claude-agent-sdk` query() function (migrated from raw CLI spawning due to piped stdio hang bug). SDK handles subprocess management internally. All output goes through the adapter layer (`src/lib/adapters/sdk-messages.ts`) — SDK message format matches CLI stream-json.
- Sidecar uses a single pre-built bundle (`sidecar/dist/agent-runner.mjs`) that runs on both Deno and Node.js. SidecarCommand struct in sidecar.rs abstracts runtime (Deno preferred for faster startup, Node.js fallback). Communicates with Rust via stdio NDJSON. Claude CLI auto-detected at startup via `findClaudeCli()` — checks ~/.local/bin/claude, ~/.claude/local/claude, /usr/local/bin/claude, /usr/bin/claude, then `which claude`. Path passed to SDK via `pathToClaudeCodeExecutable` option. Agents error immediately if CLI not found. CLAUDE* env var stripping is dual-layer: (1) Rust SidecarManager uses env_clear() + envs(clean_env) to strip all CLAUDE* vars before spawning the sidecar process, (2) JS runner also strips via SDK's `env` option (defense-in-depth). Without this, nesting detection triggers when BTerminal is launched from a Claude Code terminal. Session stop uses AbortController.abort() (not process.kill()). `agent-runner-deno.ts` exists as standalone alternative runner but is NOT used by SidecarManager.
- Agent sessions use `@anthropic-ai/claude-agent-sdk` query() function (migrated from raw CLI spawning due to piped stdio hang bug). SDK handles subprocess management internally. All output goes through the adapter layer (`src/lib/adapters/claude-messages.ts` via `message-adapters.ts` registry) — SDK message format matches CLI stream-json. Multi-provider support: message-adapters.ts routes by ProviderId to provider-specific parsers (currently only Claude registered).
- Sidecar uses per-provider runner bundles (`sidecar/dist/{provider}-runner.mjs`). Currently only `claude-runner.mjs` exists. SidecarManager.resolve_sidecar_for_provider(provider) finds the right runner file. Deno preferred (faster startup), Node.js fallback. Communicates with Rust via stdio NDJSON. Claude CLI auto-detected at startup via `findClaudeCli()` — checks ~/.local/bin/claude, ~/.claude/local/claude, /usr/local/bin/claude, /usr/bin/claude, then `which claude`. Path passed to SDK via `pathToClaudeCodeExecutable` option. Agents error immediately if CLI not found. Provider env var stripping: strip_provider_env_var() strips CLAUDE*/CODEX*/OLLAMA* vars (whitelists CLAUDE_CODE_EXPERIMENTAL_*). Dual-layer: (1) Rust env_clear() + clean_env, (2) JS runner SDK `env` option. Session stop uses AbortController.abort(). `agent-runner-deno.ts` exists as standalone alternative runner but is NOT used by SidecarManager.
- AgentPane does NOT stop agents in onDestroy — onDestroy fires on layout remounts, not just explicit close. Stop-on-close is handled externally (was TilingGrid in v2, now workspace teardown in v3).
- Agent dispatcher (`src/lib/agent-dispatcher.ts`) is a singleton that routes sidecar events to the agent store. Handles subagent routing (project-scoped sessions skip layout pane, render in TeamAgentsPanel; detached mode creates layout pane). Session persistence via registerSessionProject() + persistSessionForProject() (saves state + messages to SQLite on complete).
- AgentQueryOptions supports `permission_mode` field (flows Rust -> sidecar -> SDK). Defaults to 'bypassPermissions', supports 'default' mode. allowDangerouslySkipPermissions is conditionally set. Also supports: `setting_sources` (defaults to ['user', 'project']), `system_prompt`, `model`, `claude_config_dir` (for multi-account), `additional_directories`.
- Claude profiles: claude_list_profiles() reads ~/.config/switcher/profiles/ with profile.toml metadata. Profile set per-project in Settings (project.profile field), passed through ClaudeSession -> AgentPane `profile` prop -> resolved to config_dir for SDK. Profile name shown as info-only in ProjectHeader.
- ProjectBox has project-level tab bar: Model | Docs | Context | Files | SSH | Memory. Three mount strategies: PERSISTED-EAGER (Model, Docs, Context — always mounted, display:flex/none), PERSISTED-LAZY (Files, SSH, Memory — mount on first activation via {#if everActivated} + display:flex/none). Tab type: `'model' | 'docs' | 'context' | 'files' | 'ssh' | 'memories'`. Model tab = ClaudeSession+TeamAgentsPanel. Docs tab = ProjectFiles (markdown viewer). Context tab = ContextTab.svelte (LLM context window visualization: stats bar, segmented token meter, file references, turn breakdown; reads from agent store via sessionId prop; replaced old ContextPane ctx database viewer). Files tab = FilesTab.svelte (VSCode-style directory tree + CodeMirror 6 editor with 15 language modes, dirty tracking, Ctrl+S save, save-on-blur setting, image display via convertFileSrc, 10MB gate; CodeEditor.svelte wrapper; PdfViewer.svelte for PDF files via pdfjs-dist with canvas multi-page rendering + zoom 0.5x3x; CsvTable.svelte for CSV with RFC 4180 parser, delimiter auto-detect, sortable columns). SSH tab = SshTab.svelte (CRUD for SSH connections, launch spawns terminal tab in Model tab). Memory tab = MemoriesTab.svelte (pluggable via MemoryAdapter interface in memory-adapter.ts). Rust backend: list_directory_children + read_file_content + write_file_content (FileContent tagged union: Text/Binary/TooLarge). Frontend bridge: files-bridge.ts.
- Agent dispatcher (`src/lib/agent-dispatcher.ts`) is a singleton that routes sidecar events to the agent store. Provider-aware: sessionProviderMap routes messages through adaptMessage(provider, event) from message-adapters.ts. Handles subagent routing (project-scoped sessions skip layout pane, render in TeamAgentsPanel; detached mode creates layout pane). Session persistence via registerSessionProject(sessionId, projectId, provider) + persistSessionForProject() (saves state + messages to SQLite on complete).
- AgentQueryOptions supports `provider` field (defaults to 'claude', flows Rust -> sidecar), `provider_config` blob (Rust passes through as serde_json::Value), `permission_mode` (defaults to 'bypassPermissions'), `setting_sources` (defaults to ['user', 'project']), `system_prompt`, `model`, `claude_config_dir` (for multi-account), `additional_directories`.
- Claude profiles: claude_list_profiles() reads ~/.config/switcher/profiles/ with profile.toml metadata. Profile set per-project in Settings (project.profile field), passed through AgentSession -> AgentPane `profile` prop -> resolved to config_dir for SDK. Profile name shown as info-only in ProjectHeader.
- ProjectBox has project-level tab bar: Model | Docs | Context | Files | SSH | Memory. Three mount strategies: PERSISTED-EAGER (Model, Docs, Context — always mounted, display:flex/none), PERSISTED-LAZY (Files, SSH, Memory — mount on first activation via {#if everActivated} + display:flex/none). Tab type: `'model' | 'docs' | 'context' | 'files' | 'ssh' | 'memories'`. Model tab = AgentSession+TeamAgentsPanel. Docs tab = ProjectFiles (markdown viewer). Context tab = ContextTab.svelte (LLM context window visualization: stats bar, segmented token meter, file references, turn breakdown; reads from agent store via sessionId prop; replaced old ContextPane ctx database viewer). Files tab = FilesTab.svelte (VSCode-style directory tree + CodeMirror 6 editor with 15 language modes, dirty tracking, Ctrl+S save, save-on-blur setting, image display via convertFileSrc, 10MB gate; CodeEditor.svelte wrapper; PdfViewer.svelte for PDF files via pdfjs-dist with canvas multi-page rendering + zoom 0.5x3x; CsvTable.svelte for CSV with RFC 4180 parser, delimiter auto-detect, sortable columns). SSH tab = SshTab.svelte (CRUD for SSH connections, launch spawns terminal tab in Model tab). Memory tab = MemoriesTab.svelte (pluggable via MemoryAdapter interface in memory-adapter.ts). Rust backend: list_directory_children + read_file_content + write_file_content (FileContent tagged union: Text/Binary/TooLarge). Frontend bridge: files-bridge.ts.
- ProjectHeader shows CWD (ellipsized from START via `direction: rtl`) + profile name as info-only text on right side. AgentPane no longer has DIR/ACC toolbar — CWD and profile are props from parent.
- Skill discovery: claude_list_skills() reads ~/.claude/skills/ (dirs with SKILL.md or .md files). claude_read_skill() reads content. AgentPane `/` prefix triggers autocomplete menu. Skill content injected as prompt via expandSkillPrompt().
- claude-bridge.ts adapter wraps profile/skill Tauri commands (ClaudeProfile, ClaudeSkill interfaces).
- Sidecar build: `npm run build:sidecar` bundles SDK into agent-runner.mjs via esbuild (no --external, SDK included in bundle).
- claude-bridge.ts adapter wraps profile/skill Tauri commands (ClaudeProfile, ClaudeSkill interfaces). provider-bridge.ts wraps claude-bridge as generic provider bridge (delegates by ProviderId).
- Provider adapter pattern: ProviderId = 'claude' | 'codex' | 'ollama'. ProviderCapabilities flags gate UI (hasProfiles, hasSkills, hasModelSelection, hasSandbox, supportsSubagents, supportsCost, supportsResume). ProviderMeta registered via registerProvider() in App.svelte onMount. AgentPane receives provider + capabilities props. SettingsTab has Providers section with collapsible per-provider config panels. ProjectConfig.provider field for per-project selection. Settings persisted as `provider_settings` JSON blob.
- Sidecar build: `npm run build:sidecar` bundles SDK into claude-runner.mjs via esbuild (no --external, SDK included in bundle). Future providers will have separate runner bundles.
- Agent preview terminal: `AgentPreviewPane.svelte` is a read-only xterm.js terminal (disableStdin:true) that subscribes to an agent session's messages via `$derived(getAgentSession(sessionId))` and renders tool calls/results in real-time. Bash commands shown as cyan ` cmd`, file ops as yellow `[Read] path`, results as plain text (80-line truncation), errors in red. Spawned via 👁 button in TerminalTabs (appears when agentSessionId prop is set). TerminalTab type: `'agent-preview'` with `agentSessionId` field. Deduplicates — won't create two previews for the same session. ProjectBox passes mainSessionId to TerminalTabs.
- Maximum 4 active xterm.js instances to avoid WebKit2GTK memory issues. Agent preview uses disableStdin and no PTY so is lighter, but still counts.
- Store files using Svelte 5 runes (`$state`, `$derived`) MUST have `.svelte.ts` extension (not `.ts`). Import with `.svelte` suffix. Plain `.ts` compiles but fails at runtime with "rune_outside_svelte".
@ -78,7 +79,7 @@
- v3 component tree: App -> GlobalTabBar (settings icon) + sidebar-panel? (SettingsTab) + workspace (ProjectGrid) + StatusBar. See `docs/v3-task_plan.md` for full tree.
- MarkdownPane reactively watches filePath changes via $effect (not onMount-only). Uses sans-serif font (Inter, system-ui), all --ctp-* theme vars. Styled blockquotes with translucent backgrounds, table row hover, link hover underlines. Inner `.markdown-pane-scroll` wrapper with `container-type: inline-size` for responsive padding via `--bterminal-pane-padding-inline`.
- AgentPane UI (redesigned 2026-03-09): sans-serif root font (`system-ui, -apple-system, sans-serif`), monospace only on code/tool names. Tool calls paired with results in collapsible `<details>` groups via `$derived.by` toolResultMap (cache-guarded by tool_result count). Hook messages collapsed into compact `<details>` with gear icon. Context window meter inline in status strip. Cost bar minimal (no background, subtle border-top). Session summary with translucent surface background. Two-phase scroll anchoring (`$effect.pre` + `$effect`). Tool-aware output truncation (Bash 500 lines, Read/Write 50, Glob/Grep 20, default 30). Colors softened via `color-mix()`. Inner `.agent-pane-scroll` wrapper with `container-type: inline-size` for responsive padding via shared `--bterminal-pane-padding-inline` variable.
- ProjectBox uses CSS `style:display` (flex/none) instead of `{#if}` for tab content panes — keeps ClaudeSession mounted across tab switches (prevents session ID reset and message loss). Terminal section also uses `style:display`. Grid rows: auto auto 1fr auto.
- ProjectBox uses CSS `style:display` (flex/none) instead of `{#if}` for tab content panes — keeps AgentSession mounted across tab switches (prevents session ID reset and message loss). Terminal section also uses `style:display`. Grid rows: auto auto 1fr auto.
- Svelte 5 event syntax: use `onclick` not `on:click`. Svelte 5 requires lowercase event handler attributes (no colon syntax).
## Memora Tags

View file

@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `claude_read_skill` path traversal: added `canonicalize()` + `starts_with()` validation to prevent reading arbitrary files via crafted skill paths (lib.rs)
### Added
- **Agent provider adapter pattern**architecture plan: 3-phase implementation (core abstraction, settings UI, sidecar routing), 6 architecture decisions (PA-1PA-6), capability-driven UI rendering, per-provider sidecar binaries. Planning docs at `docs/provider-adapter/` (task_plan.md, findings.md, progress.md)
- **Agent provider adapter pattern**full implementation (3 phases complete): core abstraction layer (provider types/registry/capabilities, message adapter registry, 4 file renames), Settings UI (collapsible per-provider config panels, per-project provider dropdown, settings persistence), sidecar routing (provider-based runner selection, env var stripping for CLAUDE*/CODEX*/OLLAMA*). 5 new files, 4 renames, 20+ modified. 6 architecture decisions (PA-1PA-6). Docs at `docs/provider-adapter/`
- **PDF viewer** in Files tab: `PdfViewer.svelte` using pdfjs-dist (v5.5.207). Canvas-based multi-page rendering, zoom controls (0.5x3x, 25% steps), HiDPI-aware via devicePixelRatio. Reads PDF via `convertFileSrc()` — no new Rust commands needed
- **CSV table view** in Files tab: `CsvTable.svelte` with RFC 4180 CSV parser (no external dependency). Auto-detects delimiter (comma, semicolon, tab). Sortable columns (numeric-aware), sticky header, row numbers, text truncation at 20rem
- FilesTab routing update: Binary+pdf → PdfViewer, Text+csv → CsvTable. Updated file icons (📕 PDF, 📊 CSV)
@ -62,6 +62,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `useWorktrees` optional boolean field on ProjectConfig for future per-project worktree spawning setting (groups.ts)
### Changed
- Renamed `sdk-messages.ts``claude-messages.ts`, `agent-runner.ts``claude-runner.ts`, `ClaudeSession.svelte``AgentSession.svelte` — provider-neutral naming for multi-provider support
- `agent-dispatcher.ts` now uses `adaptMessage(provider, event)` from message-adapters.ts registry instead of directly calling `adaptSDKMessage` — enables per-provider message parsing
- Rust `AgentQueryOptions` gained `provider` (String, defaults "claude") and `provider_config` (serde_json::Value) fields with serde defaults for backward compatibility
- Rust `SidecarManager.resolve_sidecar_for_provider(provider)` looks for `{provider}-runner.mjs` instead of hardcoded `claude-runner.mjs`
- Rust `strip_provider_env_var()` strips CLAUDE*/CODEX*/OLLAMA* env vars (whitelists CLAUDE_CODE_EXPERIMENTAL_*)
- SettingsTab: added Providers section with collapsible per-provider config panels (enabled toggle, default model, capabilities display) and per-project provider dropdown
- AgentPane: capability-driven rendering via ProviderCapabilities props (hasProfiles, hasSkills, supportsResume gates)
- AgentPane UI redesign: sans-serif root font (system-ui), tool calls paired with results in collapsible `<details>` groups, hook messages collapsed into compact labels, context window usage meter in status strip, cost bar made minimal (no background), session summary with translucent background, two-phase scroll anchoring, tool-aware output truncation (Bash 500/Read 50/Glob 20 lines), colors softened via `color-mix()`, responsive margins via container queries (AgentPane.svelte)
- MarkdownPane: added inner scroll wrapper with `container-type: inline-size`, responsive padding via shared `--bterminal-pane-padding-inline` variable (MarkdownPane.svelte)
- Added `--bterminal-pane-padding-inline: clamp(0.75rem, 3.5cqi, 2rem)` shared CSS variable for responsive pane padding (catppuccin.css)

View file

@ -52,7 +52,12 @@ Terminal emulator with SSH and Claude Code session management. v1 (GTK3+VTE Pyth
| `v2/src/lib/components/Agent/AgentPane.svelte` | Agent session pane (sans-serif font, tool call/result pairing, hook collapsing, context meter, prompt, cost, profile selector, skill autocomplete) |
| `v2/src/lib/adapters/pty-bridge.ts` | PTY IPC wrapper (Tauri invoke/listen) |
| `v2/src/lib/adapters/agent-bridge.ts` | Agent IPC wrapper (Tauri invoke/listen) |
| `v2/src/lib/adapters/sdk-messages.ts` | SDK message adapter (stream-json parser) |
| `v2/src/lib/adapters/claude-messages.ts` | Claude message adapter (stream-json parser, renamed from sdk-messages.ts) |
| `v2/src/lib/adapters/message-adapters.ts` | Provider message adapter registry (per-provider routing to common AgentMessage) |
| `v2/src/lib/adapters/provider-bridge.ts` | Generic provider bridge (delegates to provider-specific bridges) |
| `v2/src/lib/providers/types.ts` | Provider abstraction types (ProviderId, ProviderCapabilities, ProviderMeta, ProviderSettings) |
| `v2/src/lib/providers/registry.svelte.ts` | Svelte 5 rune-based provider registry (registerProvider, getProviders) |
| `v2/src/lib/providers/claude.ts` | Claude provider metadata constant (CLAUDE_PROVIDER) |
| `v2/src/lib/agent-dispatcher.ts` | Routes sidecar events to agent store + subagent routing + session persistence + toast notifications |
| `v2/src/lib/adapters/file-bridge.ts` | File watcher IPC wrapper |
| `v2/src/lib/adapters/settings-bridge.ts` | Settings IPC wrapper (get/set/list) |
@ -86,14 +91,14 @@ Terminal emulator with SSH and Claude Code session management. v1 (GTK3+VTE Pyth
| `v2/src/lib/utils/tool-files.ts` | Shared file path extraction from tool_call inputs (extractFilePaths, extractWritePaths, extractWorktreePath) |
| `v2/src/lib/components/StatusBar/StatusBar.svelte` | Mission Control bar (agent states, $/hr burn rate, attention queue, cost) |
| `v2/src/lib/components/Notifications/ToastContainer.svelte` | Toast notification display |
| `v2/src/lib/components/Workspace/` | v3 components: GlobalTabBar, ProjectGrid, ProjectBox, ProjectHeader, ClaudeSession, TeamAgentsPanel, AgentCard, TerminalTabs, ProjectFiles, FilesTab, SshTab, MemoriesTab, CommandPalette, DocsTab, SettingsTab |
| `v2/src/lib/components/Workspace/` | v3 components: GlobalTabBar, ProjectGrid, ProjectBox, ProjectHeader, AgentSession, TeamAgentsPanel, AgentCard, TerminalTabs, ProjectFiles, FilesTab, SshTab, MemoriesTab, CommandPalette, DocsTab, SettingsTab |
| `v2/src/lib/types/groups.ts` | TypeScript interfaces (ProjectConfig, GroupConfig, GroupsFile) |
| `v2/src/lib/adapters/session-bridge.ts` | Session/layout/group persistence IPC wrapper |
| `v2/src/lib/components/Markdown/MarkdownPane.svelte` | Markdown file viewer (marked.js + shiki, live reload) |
| `v2/sidecar/agent-runner.ts` | Sidecar source (compiled to .mjs by esbuild, includes findClaudeCli()) |
| `v2/sidecar/claude-runner.ts` | Claude sidecar source (compiled to .mjs by esbuild, includes findClaudeCli()) |
| `v2/sidecar/agent-runner-deno.ts` | Standalone Deno sidecar runner (not used by SidecarManager, alternative) |
| `v2/sidecar/dist/agent-runner.mjs` | Bundled sidecar (runs on both Deno and Node.js) |
| `v2/src/lib/adapters/sdk-messages.test.ts` | Vitest tests for SDK message adapter (25 tests) |
| `v2/sidecar/dist/claude-runner.mjs` | Bundled Claude sidecar (runs on both Deno and Node.js) |
| `v2/src/lib/adapters/claude-messages.test.ts` | Vitest tests for Claude message adapter (25 tests) |
| `v2/src/lib/adapters/agent-bridge.test.ts` | Vitest tests for agent IPC bridge (11 tests) |
| `v2/src/lib/agent-dispatcher.test.ts` | Vitest tests for agent dispatcher (29 tests) |
| `v2/src/lib/stores/conflicts.test.ts` | Vitest tests for conflict detection (28 tests) |

View file

@ -9,12 +9,13 @@
- [ ] **Multi-machine TLS/certificate pinning** -- TLS support for bterminal-relay + certificate pinning in RemoteManager.
- [ ] **Agent Teams real-world testing** -- Env var whitelist fix done. 3 test sessions ran ($1.10, $0.69, $1.70) but model didn't spawn subagents — needs complex multi-part prompts to trigger delegation. Test with CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1.
- [ ] **Configurable stall threshold** -- health.yaml per-project config for stall threshold (currently hardcoded 15 min). Adaptive suggestions after 50 sessions from session_metrics data.
- [ ] **Agent provider adapter pattern** -- Abstract model interface (ProviderConfig, ProviderCapabilities) for multi-model support (Claude, Codex, Ollama). Rename agent-runner.mjs → claude-runner.mjs, capability gates in UI. **Planning complete** (docs/provider-adapter/task_plan.md): 3-phase plan, 6 architecture decisions (PA-1PA-6), 24 tasks. Phase 1 (core abstraction) ready to implement.
- [ ] **Add Codex/Ollama provider runners** -- Provider adapter pattern implemented (all 3 phases). Need to create codex-runner.ts and ollama-runner.ts sidecar runners + corresponding message adapters (codex-messages.ts, ollama-messages.ts) + register providers in App.svelte.
- [ ] **S-2 — session anchors** -- Preserve first 5 turns / 2K tokens verbatim through checkpoint chains. Pin additional turns via AST conversation tree action.
- [ ] **Worktree isolation per project (S-1 Phase 3)** -- Three parts: (1) **UI toggle**: useWorktrees checkbox in SettingsTab per-project settings, persisted in groups.json ProjectConfig. (2) **Spawn with --worktree**: when useWorktrees=true, pass `--worktree <session-slug>` flag to Claude Code CLI via sidecar AgentQueryOptions. Sidecar creates worktree at `<cwd>/.claude/worktrees/<name>/`, agent CWD is set there. (3) **Worktree detection improvement**: current extractWorktreePath checks tool_call params, but agents don't signal worktrees in tool calls — the real signal is CWD-based (path contains `.claude/worktrees/`) or from sidecar notification events (`worktreePath`/`worktreeBranch` fields, Claude Code v2.1.72+). Update agent-dispatcher to parse init event CWD or notification fields. For Codex/Cursor agents: detect `$CODEX_HOME/worktrees/` or `~/.cursor/worktrees/` CWD patterns. Conflict suppression already works once worktree is registered via setSessionWorktree().
## Completed
- [x] **Agent provider adapter pattern** -- Multi-provider support (Claude, Codex, Ollama) via 3-phase adapter pattern. Core abstraction (provider types/registry/capabilities, message adapter registry, file renames), Settings UI (collapsible provider panels, per-project dropdown), Sidecar routing (provider-based runner selection, env var stripping). 5 new files, 4 renames, 20+ modified. 202 vitest + 42 cargo tests. | Done: 2026-03-11
- [x] **Files tab PDF viewer + CSV table** -- PdfViewer.svelte (pdfjs-dist 5.5.207, canvas multi-page, zoom 0.5x3x, HiDPI). CsvTable.svelte (RFC 4180 parser, delimiter auto-detect, sortable columns, sticky header). FilesTab routing: Binary+pdf→PdfViewer, Text+csv→CsvTable. | Done: 2026-03-11
- [x] **Filesystem Write Detection (S-1 Phase 2)** -- inotify-based file change detection via notify crate (fs_watcher.rs). Timing heuristic (2s grace) for PID attribution — suppresses agent's own writes, surfaces external edits. EXTERNAL_SESSION_ID sentinel, recordExternalWrite(), externalConflictCount. Orange "ext write" badge + red "agent conflict" badge in ProjectHeader. ProjectBox $effect starts/stops per-project. Inotify watch limit sensing: reads /proc/sys max_user_watches, counts watched dirs, warns at >75% usage with fix command. 202/202 vitest + 42/42 cargo tests. | Done: 2026-03-11
- [x] **Conflict Detection Enhancements (S-1 Phase 1.5)** -- Bash write detection (>, >>, sed -i, tee, cp, mv). Acknowledge/dismiss conflicts (clickable badge). Worktree-aware conflict suppression. useWorktrees ProjectConfig field. 194/194 tests. | Done: 2026-03-11
@ -24,5 +25,3 @@
- [x] **CodeMirror 6 editor** -- Replaced shiki viewer in FilesTab with CodeMirror 6. CodeEditor.svelte wrapper: 15 lazy-loaded language modes, Catppuccin theme, auto-close brackets, bracket matching, code folding, Ctrl+S save, dirty tracking, save-on-blur setting. write_file_content Rust command. | Done: 2026-03-10
- [x] **FilesTab reactivity fixes** -- Fixed HTML nesting (<button> inside <button>), Svelte 5 $state proxy reactivity for file content loading. | Done: 2026-03-10
- [x] **Tab system overhaul** -- Renamed Claude→Model, Files→Docs. Added Files (VSCode-style tree+viewer), SSH (CRUD+launch), Memory (pluggable adapter) tabs. PERSISTED-EAGER/LAZY mount strategies. Rust list_directory_children + read_file_content commands. | Done: 2026-03-10
- [x] **AgentPane collapsibles + aspect ratio** -- Text messages collapsible (open by default), cost summary collapsed by default. Project max aspect ratio setting (0.33.0, default 1.0) with CSS var + SettingsTab stepper. No-implicit-push rule 52. Desktop StartupWMClass. | Done: 2026-03-09
- [x] **AgentPane + MarkdownPane UI redesign** -- AgentPane: sans-serif font, tool call/result pairing via $derived.by, hook message collapsing, context meter, muted colors via color-mix(), responsive margins via container queries. MarkdownPane: container query wrapper, shared responsive padding variable. Tribunal-elected design (S-3-R4, 88% confidence). 139/139 tests pass. | Done: 2026-03-09

View file

@ -23,3 +23,41 @@
- PA-6: Providers section in SettingsTab scroll (not inner tabs)
**Status:** Planning complete. Ready for Phase 1 implementation.
### 2026-03-11 — Implementation (All 3 Phases)
**Duration:** ~60 min
**What happened:**
**Phase 1 — Core Abstraction Layer (16 tasks):**
1. Created provider types (ProviderId, ProviderCapabilities, ProviderMeta, ProviderSettings)
2. Created Svelte 5 rune-based provider registry (registry.svelte.ts)
3. Created Claude provider meta constant (claude.ts)
4. Renamed sdk-messages.ts → claude-messages.ts, updated 13+ import references
5. Created message adapter registry (message-adapters.ts) with per-provider routing
6. Updated Rust AgentQueryOptions with `provider` and `provider_config` fields (serde defaults for backward compat)
7. Updated agent-bridge.ts TypeScript options
8. Renamed agent-runner.ts → claude-runner.ts, rebuilt dist bundle
9. Added provider field to ProjectConfig (groups.ts)
10. Renamed ClaudeSession.svelte → AgentSession.svelte with provider awareness
11. Updated agent-dispatcher.ts with sessionProviderMap for provider-based message routing
12. Updated AgentPane.svelte with capability-driven rendering (hasProfiles, hasSkills, supportsResume gates)
13. Created provider-bridge.ts (generic adapter delegating to provider-specific bridges)
14. Registered CLAUDE_PROVIDER in App.svelte onMount
15. Updated all test mocks (dispatcher test: adaptMessage mock with provider param)
16. Verified: 202 vitest + 42 cargo tests pass
**Phase 2 — Settings UI (5 tasks):**
1. Added "Providers" section to SettingsTab with collapsible per-provider config panels
2. Each panel: enabled toggle, default model input, capabilities badge display
3. Added per-project provider dropdown in project cards (conditional on >1 provider)
4. Provider settings persisted as JSON blob via `provider_settings` settings key
5. AgentPane already capability-aware from Phase 1
**Phase 3 — Sidecar Routing (3 tasks):**
1. Refactored resolve_sidecar_command() → resolve_sidecar_for_provider(provider) — looks for `{provider}-runner.mjs`
2. query() validates provider runner exists before sending message
3. Extracted strip_provider_env_var() — strips CLAUDE*/CODEX*/OLLAMA* env vars (whitelists CLAUDE_CODE_EXPERIMENTAL_*)
**Status:** All 3 phases complete. 202 vitest + 42 cargo tests pass. Zero regression.

View file

@ -23,40 +23,40 @@ Multi-provider agent support (Claude Code, Codex CLI, Ollama) via adapter patter
| # | Task | Files | Status |
|---|------|-------|--------|
| 1.1 | Create provider types | NEW: `src/lib/providers/types.ts` | pending |
| 1.2 | Create provider registry | NEW: `src/lib/providers/registry.svelte.ts` | pending |
| 1.3 | Create Claude provider meta | NEW: `src/lib/providers/claude.ts` | pending |
| 1.4 | Rename sdk-messages.ts → claude-messages.ts | RENAME + update imports | pending |
| 1.5 | Create message adapter registry | NEW: `src/lib/adapters/message-adapters.ts` | pending |
| 1.6 | Update Rust AgentQueryOptions | MOD: `bterminal-core/src/sidecar.rs` | pending |
| 1.7 | Update agent-bridge.ts options shape | MOD: `src/lib/adapters/agent-bridge.ts` | pending |
| 1.8 | Rename agent-runner.ts → claude-runner.ts | RENAME + update build script | pending |
| 1.9 | Add provider field to ProjectConfig | MOD: `src/lib/types/groups.ts` | pending |
| 1.10 | Rename ClaudeSession.svelte → AgentSession.svelte | RENAME + update imports | pending |
| 1.11 | Update agent-dispatcher provider routing | MOD: `src/lib/agent-dispatcher.ts` | pending |
| 1.12 | Update AgentPane for capability-driven rendering | MOD: `src/lib/components/Agent/AgentPane.svelte` | pending |
| 1.13 | Rename claude-bridge.ts → provider-bridge.ts | RENAME + genericize | pending |
| 1.14 | Update Rust lib.rs commands | MOD: `src-tauri/src/lib.rs` | pending |
| 1.15 | Update all tests | MOD: test files | pending |
| 1.16 | Verify: 202 vitest + 42 cargo tests pass | — | pending |
| 1.1 | Create provider types | NEW: `src/lib/providers/types.ts` | done |
| 1.2 | Create provider registry | NEW: `src/lib/providers/registry.svelte.ts` | done |
| 1.3 | Create Claude provider meta | NEW: `src/lib/providers/claude.ts` | done |
| 1.4 | Rename sdk-messages.ts → claude-messages.ts | RENAME + update imports | done |
| 1.5 | Create message adapter registry | NEW: `src/lib/adapters/message-adapters.ts` | done |
| 1.6 | Update Rust AgentQueryOptions | MOD: `bterminal-core/src/sidecar.rs` | done |
| 1.7 | Update agent-bridge.ts options shape | MOD: `src/lib/adapters/agent-bridge.ts` | done |
| 1.8 | Rename agent-runner.ts → claude-runner.ts | RENAME + update build script | done |
| 1.9 | Add provider field to ProjectConfig | MOD: `src/lib/types/groups.ts` | done |
| 1.10 | Rename ClaudeSession.svelte → AgentSession.svelte | RENAME + update imports | done |
| 1.11 | Update agent-dispatcher provider routing | MOD: `src/lib/agent-dispatcher.ts` | done |
| 1.12 | Update AgentPane for capability-driven rendering | MOD: `src/lib/components/Agent/AgentPane.svelte` | done |
| 1.13 | Rename claude-bridge.ts → provider-bridge.ts | RENAME + genericize | done |
| 1.14 | Update Rust lib.rs commands | MOD: `src-tauri/src/lib.rs` | done |
| 1.15 | Update all tests | MOD: test files | done |
| 1.16 | Verify: 202 vitest + 42 cargo tests pass | — | done |
### Phase 2: Settings UI
| # | Task | Files | Status |
|---|------|-------|--------|
| 2.1 | Add Providers section to SettingsTab | MOD: `SettingsTab.svelte` | pending |
| 2.2 | Per-provider collapsible config panels | MOD: `SettingsTab.svelte` | pending |
| 2.3 | Per-project provider dropdown | MOD: `SettingsTab.svelte` | pending |
| 2.4 | Persist provider settings | MOD: `settings-bridge.ts` | pending |
| 2.5 | Provider-aware AgentPane | MOD: `AgentPane.svelte` | pending |
| 2.1 | Add Providers section to SettingsTab | MOD: `SettingsTab.svelte` | done |
| 2.2 | Per-provider collapsible config panels | MOD: `SettingsTab.svelte` | done |
| 2.3 | Per-project provider dropdown | MOD: `SettingsTab.svelte` | done |
| 2.4 | Persist provider settings | MOD: `settings-bridge.ts` | done |
| 2.5 | Provider-aware AgentPane | MOD: `AgentPane.svelte` | done |
### Phase 3: Sidecar Routing
| # | Task | Files | Status |
|---|------|-------|--------|
| 3.1 | SidecarManager provider-based runner selection | MOD: `bterminal-core/src/sidecar.rs` | pending |
| 3.2 | Per-provider runner discovery | MOD: `bterminal-core/src/sidecar.rs` | pending |
| 3.3 | Provider-specific env var stripping | MOD: `bterminal-core/src/sidecar.rs` | pending |
| 3.1 | SidecarManager provider-based runner selection | MOD: `bterminal-core/src/sidecar.rs` | done |
| 3.2 | Per-provider runner discovery | MOD: `bterminal-core/src/sidecar.rs` | done |
| 3.3 | Provider-specific env var stripping | MOD: `bterminal-core/src/sidecar.rs` | done |
## Type System