From 6fde7002aa782caea910f05e264b0741404cca5b Mon Sep 17 00:00:00 2001 From: indifferentketchup Date: Mon, 8 Jun 2026 03:49:26 +0000 Subject: [PATCH] docs: boocode-lift-analysis, openspec change docs, codesight cache, deps - Add boocode-lift-analysis.md: comprehensive 30-repo lift matrix across 25 domains - Add openspec/ change docs: domain2-code-intelligence, domain3-multi-agent, impeccable-wave, streaming-codeblocks - Update .gitignore: .impeccable/, .omo/, bun.lock, DESIGN.md, PRODUCT.md - Update dependencies in package.json + pnpm-lock.yaml - Update .codesight/ analysis cache --- .codesight/CODESIGHT.md | 449 ++++++- .codesight/components.md | 27 +- .codesight/config.md | 8 + .codesight/graph.md | 30 +- .codesight/libs.md | 284 ++++- .codesight/middleware.md | 1 + .codesight/routes.md | 35 +- .codesight/schema.md | 60 + .gitignore | 10 + docs/boocode-lift-analysis.md | 1063 +++++++++++++++++ .../domain2-code-intelligence/.openspec.yaml | 2 + .../domain2-code-intelligence/proposal.md | 72 ++ .../domain2-code-intelligence/tasks.md | 100 ++ .../domain3-multi-agent/.openspec.yaml | 2 + .../changes/domain3-multi-agent/proposal.md | 51 + openspec/changes/domain3-multi-agent/tasks.md | 57 + openspec/changes/impeccable-wave/design.md | 300 +++++ openspec/changes/impeccable-wave/proposal.md | 78 ++ openspec/changes/impeccable-wave/tasks.md | 340 ++++++ .../.openspec.yaml | 2 + .../design.md | 45 + .../proposal.md | 32 + .../specs/channel-streaming/spec.md | 39 + .../specs/code-block-pro/spec.md | 59 + .../specs/component-hardening/spec.md | 55 + .../specs/message-list-v2/spec.md | 49 + .../tasks.md | 53 + package.json | 5 +- pnpm-lock.yaml | 454 ++++++- 29 files changed, 3624 insertions(+), 138 deletions(-) create mode 100644 docs/boocode-lift-analysis.md create mode 100644 openspec/changes/domain2-code-intelligence/.openspec.yaml create mode 100644 openspec/changes/domain2-code-intelligence/proposal.md create mode 100644 openspec/changes/domain2-code-intelligence/tasks.md create mode 100644 openspec/changes/domain3-multi-agent/.openspec.yaml create mode 100644 openspec/changes/domain3-multi-agent/proposal.md create mode 100644 openspec/changes/domain3-multi-agent/tasks.md create mode 100644 openspec/changes/impeccable-wave/design.md create mode 100644 openspec/changes/impeccable-wave/proposal.md create mode 100644 openspec/changes/impeccable-wave/tasks.md create mode 100644 openspec/changes/streaming-codeblocks-messages-components-v2/.openspec.yaml create mode 100644 openspec/changes/streaming-codeblocks-messages-components-v2/design.md create mode 100644 openspec/changes/streaming-codeblocks-messages-components-v2/proposal.md create mode 100644 openspec/changes/streaming-codeblocks-messages-components-v2/specs/channel-streaming/spec.md create mode 100644 openspec/changes/streaming-codeblocks-messages-components-v2/specs/code-block-pro/spec.md create mode 100644 openspec/changes/streaming-codeblocks-messages-components-v2/specs/component-hardening/spec.md create mode 100644 openspec/changes/streaming-codeblocks-messages-components-v2/specs/message-list-v2/spec.md create mode 100644 openspec/changes/streaming-codeblocks-messages-components-v2/tasks.md diff --git a/.codesight/CODESIGHT.md b/.codesight/CODESIGHT.md index b806795..c86d93b 100644 --- a/.codesight/CODESIGHT.md +++ b/.codesight/CODESIGHT.md @@ -3,9 +3,9 @@ > **Stack:** fastify, go-net-http | none | react | typescript > **Microservices:** @boocode/contracts, @boocode/ion, @boocode/booterm, @boocode/coder, @boocode/server, @boocode/web, codecontext, @boocode/conductor -> 131 routes (9 inferred) + 9 ws | 18 models | 69 components | 247 lib files | 39 env vars | 16 middleware +> 147 routes (9 inferred) + 9 ws | 23 models | 92 components | 296 lib files | 43 env vars | 17 middleware > **Token savings:** this file is ~0 tokens. Without it, AI exploration would cost ~0 tokens. **Saves ~0 tokens per conversation.** -> **Last scanned:** 2026-06-07 21:09 — re-run after significant changes +> **Last scanned:** 2026-06-08 03:49 — re-run after significant changes --- @@ -14,6 +14,7 @@ ## CRUD Resources - **`/api/battles`** GET | POST | GET/:id → Battle +- **`/api/plans`** GET | POST | GET/:id | PATCH/:id → Plan - **`/api/runs`** GET | POST | GET/:id → Run - **`/api/tasks`** GET | POST | GET/:id → Task - **`/api/chats/:id/messages`** GET | POST | GET/:id | DELETE/:id → Message @@ -25,11 +26,16 @@ ### fastify - `GET` `/api/term/health` params() +- `GET` `/api/term/sessions/:sid/panes/:pid/search` params(sid, pid) [auth] +- `GET` `/api/term/sessions` params() [auth] - `POST` `/api/term/sessions/:sid/panes/:pid/start` params(sid, pid) [auth] - `POST` `/api/term/sessions/:sid/panes/:pid/kill` params(sid, pid) [auth] - `GET` `/ws/term/sessions/:sid/panes/:pid` params(sid, pid) [auth] - `GET` `/api/health` params() [auth, db, queue, ai] - `GET` `/api/sessions/:sessionId/agent-sessions` params(sessionId) [auth, db] +- `GET` `/api/analytics/summary` params() [auth, db] +- `GET` `/api/analytics/sessions` params() [auth, db] +- `GET` `/api/analytics/token-breakdown` params() [auth, db] - `POST` `/api/battles/generate-prompt` params() [auth, db] - `POST` `/api/battles/:id/stop` params(id) [auth, db] - `GET` `/api/battles/:id/analysis` params(id) [auth, db] @@ -53,6 +59,7 @@ - `POST` `/api/pending/:id/apply` params(id) [auth, db, queue] - `POST` `/api/pending/:id/reject` params(id) [auth, db, queue] - `POST` `/api/pending/:id/rewind` params(id) [auth, db, queue] +- `GET` `/api/plans/active` params() [db] - `GET` `/api/providers/snapshot` params() [db, cache] - `GET` `/api/providers/config` params() [db, cache] - `PATCH` `/api/providers/config` params() [db, cache] @@ -70,19 +77,22 @@ - `GET` `/api/ws/sessions/:sessionId` params(sessionId) [auth, db] - `GET` `/api/ws/user` params() [auth, db] - `GET` `/api/projects/:id/agents` params(id) [db, cache] +- `GET` `/api/analytics/context` params() [auth, db] - `POST` `/api/chats/:id/messages/:msg_id/artifacts/download` params(id, msg_id) [auth, db] - `GET` `/api/chats/:id/messages/:msg_id/html_artifact` params(id, msg_id) [auth, db] - `GET` `/api/projects/:project_id/artifacts/:filename` params(project_id, filename) [auth, db] -- `GET` `/api/sessions/:id/chats` params(id) [auth, db] -- `POST` `/api/sessions/:id/chats` params(id) [auth, db] -- `PATCH` `/api/chats/:id` params(id) [auth, db] -- `POST` `/api/sessions/:id/chats/archive-all` params(id) [auth, db] -- `GET` `/api/sessions/:id/chats/open-count` params(id) [auth, db] -- `POST` `/api/chats/:id/archive` params(id) [auth, db] -- `POST` `/api/chats/:id/unarchive` params(id) [auth, db] -- `DELETE` `/api/chats/:id` params(id) [auth, db] -- `POST` `/api/chats/:id/fork` params(id) [auth, db] -- `POST` `/api/chats/:id/discard_stale` params(id) [auth, db] +- `GET` `/api/sessions/:id/chats` params(id) [auth, db, queue] +- `POST` `/api/sessions/:id/chats` params(id) [auth, db, queue] +- `PATCH` `/api/chats/:id` params(id) [auth, db, queue] +- `POST` `/api/sessions/:id/chats/archive-all` params(id) [auth, db, queue] +- `GET` `/api/sessions/:id/chats/open-count` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/archive` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/unarchive` params(id) [auth, db, queue] +- `DELETE` `/api/chats/:id` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/fork` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/discard_stale` params(id) [auth, db, queue] +- `GET` `/api/chats/:id/export` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/compare` params(id) [auth, db, queue] - `GET` `/api/coder/ws/sessions/:sessionId` params(sessionId) [auth] - `ALL` `/api/coder/*` params() [auth] - `GET` `/api/settings/inference` params() [cache] @@ -94,7 +104,9 @@ - `POST` `/api/chats/:id/continue` params(id) [auth, db, queue] - `POST` `/api/chats/:id/force_send` params(id) [auth, db, queue] - `POST` `/api/chats/:id/grant_read_access` params(id) [auth, db, queue] -- `GET` `/api/models` params() +- `POST` `/api/chats/:id/mcp-approve` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/messages/:message_id/feedback` params(id, message_id) [auth, db, queue] +- `GET` `/api/models` params() [auth] - `POST` `/api/projects/create` params() [auth, db] - `POST` `/api/projects/:id/archive` params(id) [auth, db] - `POST` `/api/projects/:id/unarchive` params(id) [auth, db] @@ -122,6 +134,7 @@ - `GET` `/api/skills` params() [auth, db, queue] - `POST` `/api/chats/:id/skill_invoke` params(id) [auth, db, queue] - `GET` `/api/tools/cost_stats` params() [auth, db] +- `GET` `/api/chats/:id/traces` params(id) [db] - `GET` `/api/ws/sessions/:id` params(id) [auth, db] ### go-net-http @@ -273,6 +286,25 @@ - model: text (required) - verdict: text +### flow_step_events +- id: uuid (pk) +- run_id: uuid (required, fk) +- step_id: varchar (required, fk) +- event: varchar (required) +- payload: jsonb + +### plans +- id: uuid (pk) +- project_id: uuid (required, fk) +- title: text (required) +- description: text +- status: text (required) +- flow_run_id: uuid (fk) +- progress_pct: integer (required) +- items_total: integer (required) +- items_completed: integer (required) +- metadata: jsonb + ### projects - id: uuid (pk) - name: text (required) @@ -294,6 +326,8 @@ - content: text (required) - status: text (required) - last_seq: integer (required) +- cache_tokens: integer +- reasoning_tokens: integer ### message_parts - id: uuid (pk) @@ -311,6 +345,45 @@ - name: text - status: text (required) +### tool_traces +- id: uuid (pk) +- session_id: uuid (required, fk) +- chat_id: uuid (required, fk) +- message_id: uuid (fk) +- turn_number: integer (required) +- tool_name: text (required) +- tool_input: jsonb (required) +- tool_output: text +- started_at: timestamp(tz) (required) +- finished_at: timestamp(tz) +- latency_ms: integer +- tokens_used: integer +- cache_tokens: integer +- reasoning_tokens: integer +- error: text +- outcome: text + +### tool_trace_states +- id: uuid (pk) +- session_id: uuid (required, fk) +- chat_id: uuid (required, fk) +- message_id: uuid (fk) +- turn_number: integer (required) +- tool_name: text (required) +- tool_input: jsonb (required) +- started_at: timestamp(tz) (required) + +### agent_snapshots +- id: uuid (pk) +- session_id: uuid (required, fk) +- chat_id: uuid (required, fk) +- model: text (required) +- agent: text +- mode: text +- turn_number: integer (required) +- messages: jsonb (required) +- tool_states: jsonb (required) + --- # Components @@ -325,23 +398,34 @@ - **AttachmentChip** — props: attachment, onRemove, onPreview — `apps/web/src/components/AttachmentChip.tsx` - **AttachmentPreviewModal** — props: attachment, onClose — `apps/web/src/components/AttachmentPreviewModal.tsx` - **BottomSheet** — props: open, onClose, title — `apps/web/src/components/BottomSheet.tsx` +- **CacheShapeBadge** — props: cacheTokens, totalTokens — `apps/web/src/components/CacheShapeBadge.tsx` - **CapHitSentinel** — props: message, capHitPosition, isLatest — `apps/web/src/components/CapHitSentinel.tsx` - **ChatInput** — props: disabled, projectId, agentId, onAgentChange, sessionId, webSearchEnabled, onSend, onForceSend, generating, onStop — `apps/web/src/components/ChatInput.tsx` - **ChatTabBar** — props: pane, tabs, tabNumbers, onSwitchTab, onRemoveTab, onCloseOthers, onCloseToRight, onCloseAll, onNewTab, onSplitPane — `apps/web/src/components/ChatTabBar.tsx` - **ChatThroughput** — props: chatId, className — `apps/web/src/components/ChatThroughput.tsx` - **CodeBlock** — props: code, lang — `apps/web/src/components/CodeBlock.tsx` +- **ComparePane** — props: models, responses, onClose — `apps/web/src/components/ComparePane.tsx` - **ContextMeter** — props: messages, modelContextLimit, sessionCostUsd — `apps/web/src/components/ContextMeter.tsx` - **CreateProjectModal** — props: open, onOpenChange — `apps/web/src/components/CreateProjectModal.tsx` +- **DiffSnippet** — props: diff — `apps/web/src/components/DiffSnippet.tsx` +- **DiffSplitView** — props: file, wrapLines — `apps/web/src/components/DiffSplitView.tsx` - **DoomLoopSentinel** — props: message — `apps/web/src/components/DoomLoopSentinel.tsx` - **DropOverlay** — props: visible — `apps/web/src/components/DropOverlay.tsx` +- **EmptyState** — props: icon, title, description, action, className — `apps/web/src/components/EmptyState.tsx` - **FileMentionPopover** — props: query, files, anchorRect, onSelect, onClose — `apps/web/src/components/FileMentionPopover.tsx` - **FileViewerOverlay** — props: path, content, lang, onClose — `apps/web/src/components/FileViewerOverlay.tsx` - **FlowLauncherDialog** — `apps/web/src/components/FlowLauncherDialog.tsx` - **GitDiffView** — props: result, loading, error, mode, onSelectMode, onRefresh, mutating, mutateError, onStage, onUnstage — `apps/web/src/components/GitDiffView.tsx` - **HtmlArtifactPane** — props: chatId, state, onClose — `apps/web/src/components/HtmlArtifactPane.tsx` - **InferenceSettings** — `apps/web/src/components/InferenceSettings.tsx` +- **InlineReviewEditor** — props: initialBody, onSave, onCancel — `apps/web/src/components/InlineReviewEditor.tsx` +- **InlineReviewGutterCell** — props: lineNumber, type, hasComments, canComment, onClick — `apps/web/src/components/InlineReviewGutterCell.tsx` +- **InlineReviewThread** — props: comments, onEditComment, onDeleteComment — `apps/web/src/components/InlineReviewThread.tsx` +- **KeyboardShortcutsDialog** — props: open, onOpenChange — `apps/web/src/components/KeyboardShortcutsDialog.tsx` - **MarkdownArtifactPane** — props: chatId, state, onClose — `apps/web/src/components/MarkdownArtifactPane.tsx` - **MarkdownRenderer** — props: content — `apps/web/src/components/MarkdownRenderer.tsx` +- **McpPermissionDialog** — props: toolCallId, toolName, toolArgs, chatId, open, onClose — `apps/web/src/components/McpPermissionDialog.tsx` +- **McpResponseDisplay** — props: toolCall, toolResult — `apps/web/src/components/McpResponseDisplay.tsx` - **MessageBubble** — props: message, sessionChats, capHitInfo, actions, hideActions, hasCheckpoint, restoreDisabled — `apps/web/src/components/MessageBubble.tsx` - **MessageList** — props: messages, sessionChats — `apps/web/src/components/MessageList.tsx` - **MobileTabSwitcher** — props: panes, activePaneIdx, chats, onSwitchPane, onRemovePane, onRenameChat — `apps/web/src/components/MobileTabSwitcher.tsx` @@ -353,12 +437,14 @@ - **RequestReadAccessCard** — props: toolCall, toolResult, chatId — `apps/web/src/components/RequestReadAccessCard.tsx` - **RightRail** — props: projectId, sessionId — `apps/web/src/components/RightRail.tsx` - **SessionLandingPage** — props: projectId, sessionId, agentId, onAgentChange, onSend, onSkillInvoke, createChat, chats, onOpenChat, onUnarchiveChat — `apps/web/src/components/SessionLandingPage.tsx` +- **SessionTimeline** — props: messages, onClose, onScrollToMessage — `apps/web/src/components/SessionTimeline.tsx` - **SlashCommandPicker** — props: query, items, groups, inputRef, onSelect, onClose, emptyLabel — `apps/web/src/components/SlashCommandPicker.tsx` - **StaleStreamBanner** — props: onRetry, onDiscard — `apps/web/src/components/StaleStreamBanner.tsx` - **StatusDot** — props: chatId, className — `apps/web/src/components/StatusDot.tsx` - **ThemePicker** — `apps/web/src/components/ThemePicker.tsx` - **ToolCallGroup** — props: runs — `apps/web/src/components/ToolCallGroup.tsx` -- **ToolCallLine** — props: run, insideGroup — `apps/web/src/components/ToolCallLine.tsx` +- **ToolCallLine** — props: run, insideGroup, chatId — `apps/web/src/components/ToolCallLine.tsx` +- **TraceViewer** — props: chatId — `apps/web/src/components/TraceViewer.tsx` - **Workspace** — props: sessionId, projectId, agentId, onAgentChange, panesHook, chatsHook, session, project, onAddPane — `apps/web/src/components/Workspace.tsx` - **AddProviderModal** — props: open, onOpenChange, onAdded — `apps/web/src/components/coder/AddProviderModal.tsx` - **ProvidersSettings** — `apps/web/src/components/coder/ProvidersSettings.tsx` @@ -367,21 +453,31 @@ - **ThemeFx** — `apps/web/src/components/fx/ThemeFx.tsx` - **ClaudeIcon** — props: size, className — `apps/web/src/components/icons/ProviderIcons.tsx` - **OpenCodeIcon** — props: size, className — `apps/web/src/components/icons/ProviderIcons.tsx` +- **ActionRow** — props: message, actions, hiddenSet, hasCheckpoint, restoreDisabled — `apps/web/src/components/message-parts/ActionRow.tsx` +- **CompactCard** — props: message, sessionChats — `apps/web/src/components/message-parts/CompactCard.tsx` +- **MistakeRecoverySentinel** — props: message — `apps/web/src/components/message-parts/MistakeRecoverySentinel.tsx` +- **ReasoningBlock** — props: text, streaming — `apps/web/src/components/message-parts/ReasoningBlock.tsx` +- **SendToTerminalMenu** — `apps/web/src/components/message-parts/SendToTerminalMenu.tsx` +- **StatsLine** — props: message — `apps/web/src/components/message-parts/StatsLine.tsx` +- **SummaryCard** — props: message — `apps/web/src/components/message-parts/SummaryCard.tsx` - **ArenaPane** — props: state, onClose — `apps/web/src/components/panes/ArenaPane.tsx` - **ChatPane** — props: sessionId, chatId, projectId, agentId, onAgentChange, sessionChats, webSearchEnabled — `apps/web/src/components/panes/ChatPane.tsx` - **CoderMessageList** — props: messages, chatId, footer, actions, checkpointMessageIds, restoreDisabled — `apps/web/src/components/panes/CoderMessageList.tsx` - **CoderPane** — props: sessionId, paneId, chatId, chatPending, projectPath, onConnectedChange, onAgentLabelChange — `apps/web/src/components/panes/CoderPane.tsx` - **OrchestratorPane** — props: state, onClose — `apps/web/src/components/panes/OrchestratorPane.tsx` - **SettingsPane** — props: session, project, maximized, onToggleMaximize, onClose, isMobile — `apps/web/src/components/panes/SettingsPane.tsx` -- **TerminalPane** — props: sessionId, paneId, label, active — `apps/web/src/components/panes/TerminalPane.tsx` +- **TerminalPane** — props: sessionId, paneId, label, description, parentAgent, active — `apps/web/src/components/panes/TerminalPane.tsx` - **FloatingMenu** — props: x, y, hasSelection, chatInputs, onCopy, onPaste, onSelectAll, onSearch, onSendToChat, onDismiss — `apps/web/src/components/panes/terminal/FloatingMenu.tsx` - **SearchBar** — props: searchRef, theme, onClose — `apps/web/src/components/panes/terminal/SearchBar.tsx` - **TerminalHotkeyBar** — props: ctrlArmed, onSendBytes, onArmCtrl, onFit — `apps/web/src/components/panes/terminal/TerminalHotkeyBar.tsx` - **RightRailDrawerProvider** — `apps/web/src/hooks/useRightRailDrawer.tsx` - **SidebarDrawerProvider** — `apps/web/src/hooks/useSidebarDrawer.tsx` - **PATH_REGEX** — `apps/web/src/lib/linkify-paths.tsx` +- **Analytics** — `apps/web/src/pages/Analytics.tsx` - **Home** — `apps/web/src/pages/Home.tsx` +- **Memory** — `apps/web/src/pages/Memory.tsx` - **Project** — `apps/web/src/pages/Project.tsx` +- **Results** — `apps/web/src/pages/Results.tsx` - **Session** — `apps/web/src/pages/Session.tsx` - **Settings** — `apps/web/src/pages/Settings.tsx` @@ -403,8 +499,17 @@ - function ensureSession: (tmuxConfPath, sessionName, projectRoot, log, cols?, rows?) => Promise - function killSession: (tmuxConfPath, sessionName) => Promise - function capturePane: (tmuxConfPath, sessionName, lines) => Promise + - _...1 more_ - `apps/booterm/src/pty/pty.ts` — function attachPty: (opts) => IPty -- `apps/booterm/src/ws/attach.ts` — function registerWsAttachRoute: (app, tmuxConfPath) => void +- `apps/booterm/src/pty/registry.ts` + - function register: (sessionId, paneId, projectPath, title?, opts?) => void + - function unregister: (paneId) => void + - function touchActivity: (paneId) => void + - function list: () => SessionMeta[] + - function get: (paneId) => SessionMeta | undefined + - function setPendingMetadata: (paneId, meta) => void + - _...8 more_ +- `apps/booterm/src/ws/attach.ts` — function registerWsAttachRoute: (app, tmuxConfPath, idleTimeoutSeconds?, absoluteTimeoutSeconds?) => void - `apps/coder/src/conductor/contracts.ts` - function produceContract: (contracts) => string - function reviewContract: (contracts) => string @@ -491,7 +596,7 @@ - function classifyLane: (battleType, _identity, model, localModels) => ContestantLane - function nextLocalContestant: (contestants) => string | null - function isBattleComplete: (contestants) => boolean - - function computeBenchmark: (startedAt, endedAt, costTokens, lane) => Benchmark + - function computeBenchmark: (startedAt, endedAt, costTokens, lane, tokenBreakdown) => Benchmark - function sanitizeSlug: (s) => string - function buildBattleSlug: (battleId, battleType, createdAt) => string - _...7 more_ @@ -555,6 +660,7 @@ - function stepEndedToUsage: (props) => StepUsage - interface StepEndedProps - interface StepUsage +- `apps/coder/src/services/backends/paseo.ts` — class PaseoBackend, interface PaseoBackendDeps - `apps/coder/src/services/backends/pushable-iterable.ts` — function createPushable: () => Pushable, interface Pushable - `apps/coder/src/services/backends/turn-guard.ts` - function armAbortGuard: (g) => void @@ -563,6 +669,30 @@ - interface AbortTerminalGuard - `apps/coder/src/services/backends/warm-acp-routing.ts` — function shouldUseWarmBackend: (task) => boolean, function isTurnOkForStopReason: (stopReason) => boolean - `apps/coder/src/services/backends/warm-acp.ts` — class WarmAcpBackend, interface WarmAcpBackendDeps +- `apps/coder/src/services/behavioral/generation.ts` + - function createExecutionPlan: (observational, actionable, previouslyApplied, disambiguationGroups, lowCriticality) => BatchExecutionPlan[] + - function getRetryTemperatures: (baseTemp, maxAttempts) => number[] + - class SchematicGenerator + - class DefaultSchematicGenerator + - interface ObservationalOutput + - interface ActionableOutput + - _...7 more_ +- `apps/coder/src/services/behavioral/matching.ts` + - function matchWithRetry: (fn) => void + - function executeBatchesParallel: (batches, _generationInfo) => Promise + - function createScoredMatch: (guidelineId, score, rationale) => ScoredMatch + - class GuidelineMatchingBatchError + - class ObservationalGuidelineMatchingBatch + - class ActionableGuidelineMatchingBatch + - _...25 more_ +- `apps/coder/src/services/behavioral/resolver.ts` + - class RelationalResolver + - interface RelationshipEntity + - interface Relationship + - interface RelationshipStore + - interface ResolvedEntity + - interface Resolution + - _...8 more_ - `apps/coder/src/services/cancel-registry.ts` — function createCancelRegistry: () => CancelRegistry, interface CancelRegistry - `apps/coder/src/services/checkpoints.ts` - function buildShadowCommitCommand: (worktreePath, id) => string @@ -573,7 +703,15 @@ - interface RestoreCheckpointResult - _...1 more_ - `apps/coder/src/services/claude-command-discovery.ts` — function discoverClaudeCommands: () => AgentCommand[] +- `apps/coder/src/services/collision-detector.ts` + - function findConflicts: (changedFiles, worktreeId, /** Approximate line range for the proposed changes, keyed by file path */ + changedRanges, {...}, conflictIndex) => ConflictVerdict[] + - interface ConflictVerdict + - interface ConflictEntry + - type ConflictSeverity + - type ConflictIndexData - `apps/coder/src/services/command-availability.ts` — function isCommandAvailable: (binary) => Promise +- `apps/coder/src/services/conflict-index.ts` — class ConflictIndex, const conflictIndex - `apps/coder/src/services/correction-service.ts` - function recordCorrection: (originalClaim, correction, principleExtracted, persistedTo, basePath?) => Promise - function scanForCorrections: (auditPath) => Promise @@ -603,10 +741,11 @@ - function partitionReady: (ready, ctx) => void - function isRunComplete: (flow, state) => boolean - function isStuck: (flow, state) => boolean - - function reconcileResumeStep: (status, taskId, taskState) => ResumeAction - - _...5 more_ + - function buildBatchState: (flow, inFlight) => Map FlowRunner + - function resolveVariables: (prompt, results, string>) => string - interface LaunchOpts - interface FlowRunner - `apps/coder/src/services/frame-emitter.ts` @@ -626,6 +765,19 @@ - function deleteGuideline: (id, basePath?) => Promise - function findGuideline: (content, basePath?) => Promise - _...14 more_ +- `apps/coder/src/services/hashline/hash-computation.ts` + - function computeLineHash: (lineNumber, content) => string + - function computeLegacyLineHash: (lineNumber, content) => string + - function formatHashLine: (lineNumber, content) => string + - function formatHashLines: (content) => string +- `apps/coder/src/services/hashline/validation.ts` + - function normalizeLineRef: (ref) => string + - function parseLineRef: (ref) => LineRef + - function validateLineRef: (lines, ref) => void + - function validateLineRefs: (lines, refs) => void + - class HashlineMismatchError + - interface LineRef +- `apps/coder/src/services/hashline/xxhash32.ts` — function hashXxh32: (input, seed) => number - `apps/coder/src/services/host-exec.ts` — function hostExec: (command, opts?) => Promise, interface HostExecResult - `apps/coder/src/services/lsp/client.ts` — class LspClient - `apps/coder/src/services/lsp/config.ts` — function getServerConfig: (filePath) => LspServerConfig | null, interface LspServerConfig @@ -637,6 +789,44 @@ - function findReferences: (client, filePath, content, line, character) => Promise - `apps/coder/src/services/lsp/server-manager.ts` — class LspServerManager, const lspManager - `apps/coder/src/services/mcp-server.ts` — function startMcpServer: (sql) => Promise +- `apps/coder/src/services/model-resolution/connected-providers-cache.ts` + - function readConnectedProvidersCache: () => string[] | null + - function findProviderModelMetadata: (_providerID, _modelID) => ModelMetadata | undefined + - function readProviderModelsCache: () => ProviderModelsCache | null + - interface ProviderModelsCache + - interface ConnectedProvidersAdapter + - const connectedProvidersAdapter: ConnectedProvidersAdapter +- `apps/coder/src/services/model-resolution/fallback-chain-from-models.ts` + - function parseFallbackModelEntry: (model, contextProviderID, defaultProviderID) => FallbackEntry | undefined + - function parseFallbackModelObjectEntry: (obj, contextProviderID, defaultProviderID) => FallbackEntry | undefined + - function findMostSpecificFallbackEntry: (providerID, modelID, chain) => FallbackEntry | undefined + - function buildFallbackChainFromModels: (fallbackModels) => void +- `apps/coder/src/services/model-resolution/model-availability.ts` — function fuzzyMatchModel: (target, available, providers?) => string | null, function isModelAvailable: (targetModel, availableModels) => boolean +- `apps/coder/src/services/model-resolution/model-error-classifier.ts` + - function isRetryableModelError: (error) => boolean + - function shouldRetryError: (error) => boolean + - function getNextFallback: (fallbackChain, attemptCount) => FallbackEntry | undefined + - function hasMoreFallbacks: (fallbackChain, attemptCount) => boolean + - function selectFallbackProvider: (providers, preferredProviderID?) => string + - function selectFallbackProviderWithCache: (providers, providerCache, preferredProviderID?) => string + - _...1 more_ +- `apps/coder/src/services/model-resolution/model-normalization.ts` — function normalizeModel: (model?) => string | undefined, function normalizeModelID: (modelID) => string +- `apps/coder/src/services/model-resolution/model-resolution-pipeline.ts` + - function _setModelResolutionLogImplementationForTesting: (logImplementation) => void + - function resolveModelPipeline: (request, providerCache) => void + - type ModelResolutionRequest + - type ModelResolutionProvenance + - type ModelResolutionResult + - type ModelResolutionDeps +- `apps/coder/src/services/model-resolution/model-resolver.ts` + - function resolveModel: (input) => string | undefined + - function resolveModelWithFallback: (input, connectedProvidersAdapter) => ModelResolutionResult | undefined + - function normalizeFallbackModels: (models) => void + - function flattenToFallbackModelStrings: (models) => void + - type ModelResolutionInput + - type ModelSource + - _...2 more_ +- `apps/coder/src/services/model-resolution/provider-model-id-transform.ts` — function transformModelForProvider: (provider, model) => string, function transformModelForProviderDisplay: (provider, model) => string - `apps/coder/src/services/net/port-utils.ts` - function reclaimPort: (port) => void - function waitForPortRelease: (port, timeoutMs) => Promise @@ -646,6 +836,13 @@ - function createOrphanWorktreeReaper: (deps) => void - interface OrphanWorktreeReaperDeps - interface OrphanReaperResult +- `apps/coder/src/services/paseo-client.ts` + - class PaseoClientError + - class PaseoClient + - interface PaseoAgentListItem + - interface PaseoAgentDetail + - interface PaseoSendResult + - interface PaseoClientConfig - `apps/coder/src/services/pending_changes.ts` - function planEdit: (content, oldStr, newStr) => EditPlan - function queueEdit: (sql, sessionId, taskId, filePath, oldString, newString, projectRoot, // v2.6 Phase 1-UX) => void @@ -662,6 +859,14 @@ - function waitForElicitationResponse: (taskId, sessionId, provider, modeId, params, timeoutMs) => Promise - function cancelPendingPermission: (taskId) => void - _...3 more_ +- `apps/coder/src/services/plan-store.ts` + - function createPlan: (sql, opts) => Promise + - function getPlan: (sql, planId) => Promise + - function listPlans: (sql, projectId) => Promise + - function listActivePlans: (sql, projectId) => Promise + - function updatePlan: (sql, planId, opts) => Promise + - function updatePlanFromRun: (sql, runId, runStatus) => Promise + - _...5 more_ - `apps/coder/src/services/provider-commands.ts` - function getManifestCommands: (provider) => AgentCommand[] - function mergeCommands: (...lists) => AgentCommand[] @@ -684,13 +889,13 @@ - interface ProviderManifestEntry - const PROVIDER_MANIFEST: Record - `apps/coder/src/services/provider-snapshot.ts` + - function fetchDeepSeekModels: (config) => Promise - function fetchLlamaSwapModels: (config) => Promise - function prefixLlamaSwapModels: (models) => ProviderModel[] - function mergeModels: (...lists) => ProviderModel[] - function getProviderSnapshot: (sql, config, cwd?, force) => Promise - function clearProviderSnapshotCache: () => void - - function peekSnapshotEntry: (name, cwd?) => ProviderSnapshotEntry | undefined - - _...1 more_ + - _...2 more_ - `apps/coder/src/services/pty-dispatch.ts` - function dispatchViaPty: (opts) => Promise - interface DispatchResult @@ -800,6 +1005,17 @@ - function readSession: (sessionId, projectRoot?) => SessionJson | null - _...9 more_ - `apps/server/src/services/auto_name.ts` — function maybeAutoNameChat: (ctx, chatId, sessionId) => Promise +- `apps/server/src/services/background-task.ts` + - function setBackgroundInferenceEnqueuer: (enqueue, chatId, assistantMessageId, user) => void + - function spawnBackgroundTask: (sql, log, projectId, input, model, agent?, label?) => Promise + - function getBackgroundTaskStatus: (sql, taskId) => Promise + - function getBackgroundTaskResult: (sql, taskId, chatId) => Promise< + - function cancelBackgroundTask: (sql, taskId) => Promise + - interface BackgroundTask +- `apps/server/src/services/boocontext_client.ts` + - function callBoocontext: (req, log?, msg) => void + - interface BoocontextRequest + - interface BoocontextResponse - `apps/server/src/services/broker.ts` - function createBroker: (log?) => Broker - interface Broker @@ -818,6 +1034,7 @@ - function select: (messages, contextLimit, tailTurns) => SelectResult - function deriveFilesRead: (head) => string[] - _...8 more_ +- `apps/server/src/services/export-formatter.ts` — function formatJson: (chat, messages, model) => string, function formatMarkdown: (chat, messages, model) => string - `apps/server/src/services/file_index.ts` — function getProjectFiles: (projectId, projectRoot) => Promise - `apps/server/src/services/file_ops.ts` - function listDir: (projectRoot, relPath, opts?) => Promise @@ -842,7 +1059,20 @@ - interface GiteaConfig - interface GiteaRepo - `apps/server/src/services/grant_resolver.ts` — function resolveGrantRoot: (sql, requestedPath, projectRoot, whitelistRoot) => Promise, type GrantResolution +- `apps/server/src/services/hooks.ts` + - function loadHooksConfig: (path) => HooksConfig + - function reloadHooksConfig: () => HooksConfig + - function createHookRunner: () => HookRunner + - interface HookConfig + - interface HooksConfig + - interface PreToolUsePayload + - _...10 more_ - `apps/server/src/services/inference/budget.ts` — function resolveToolBudget: (agent) => number +- `apps/server/src/services/inference/compute-diff.ts` + - function computeDiff: (oldStr, newStr, filePath) => string + - function isWriteTool: (name) => boolean + - function diffFromToolArgs: (name, args, unknown>, filePath?) => string + - const WRITE_TOOL_NAMES - `apps/server/src/services/inference/content-flusher.ts` — function createContentFlusher: (sql, messageId, getContent) => void, interface ContentFlusher - `apps/server/src/services/inference/dcp/messages.ts` - function toDcpMessages: (parts) => DcpMessage[] @@ -882,6 +1112,10 @@ - type FailureKind - const MISTAKE_THRESHOLD - _...1 more_ +- `apps/server/src/services/inference/multi-modal.ts` + - function hasImageAttachments: (_message) => boolean + - function imageAttachmentsToParts: (attachments) => Array< + - interface ImageAttachment - `apps/server/src/services/inference/parts.ts` - function insertParts: (sql, parts) => Promise - function partsFromAssistantMessage: (args) => void @@ -894,10 +1128,13 @@ - function maybeFlagForCompaction: (ctx, chatId, updated) => Promise - interface OpenAiMessage - `apps/server/src/services/inference/provider.ts` - - function resolveRoute: (agent, config?) => RoutingInfo + - function isDeepSeekModel: (modelId) => boolean + - function resolveRoute: (agent, config?, modelId?) => RoutingInfo - function upstreamModel: (config, modelId, agent?) => LanguageModel + - function resolveModelEndpoint: (config, modelId) => void + - function resetDeepSeekProvider: () => void - interface RoutingInfo - - type InferenceRoute + - _...1 more_ - `apps/server/src/services/inference/prune.ts` - function selectPruneTargets: (partsNewestFirst, tailStartCreatedAt) => void - function prune: (args) => Promise @@ -918,6 +1155,12 @@ - function isAnySentinel: (m) => boolean - const DOOM_LOOP_THRESHOLD - _...1 more_ +- `apps/server/src/services/inference/state-graph.ts` + - function createDefaultGraph: () => GraphNode[] + - function runGraph: (ctx, args, extra) => Promise + - interface GraphState + - interface GraphResult + - type GraphNodeType - `apps/server/src/services/inference/step-decision.ts` - function decideStep: (input) => PreStepDecision - function decidePostToolAction: (action, mistakeTracker) => PostToolDecision @@ -934,12 +1177,14 @@ - `apps/server/src/services/inference/stream-phase.ts` — function executeStreamPhase: (ctx, args, session, messages, state, agent, // v1.11.8, web_search and web_fetch are stripped from the // tool list sent to the LLM, so the model can't even attempt them. webToolsEnabled) => Promise +- `apps/server/src/services/inference/supervisor.ts` — function resolveSupervisorTurn: (latestUserMessage, agents, fallbackModel?) => Promise, interface SupervisorRoute - `apps/server/src/services/inference/tool-call-parser.ts` - function stripToolMarkup: (text, opts?) => string - function extractToolCallBlocks: (buffer, log?) => ToolCallExtraction - interface ParsedCall - interface ToolCallExtraction -- `apps/server/src/services/inference/tool-phase.ts` — function executeToolPhase: (ctx, args, result, startedAt, session, projectRoot, agent?) => Promise, interface ToolPhaseResult +- `apps/server/src/services/inference/tool-input-repair.ts` — function repairToolInput: (schema, unknown> | undefined, args, unknown>) => void, interface ToolInputRepair +- `apps/server/src/services/inference/tool-phase.ts` — function executeToolPhase: (ctx, args, result, startedAt, session, projectRoot, agent?, turnNumber?) => Promise, interface ToolPhaseResult - `apps/server/src/services/inference/tool-shim.ts` - function extractToolCalls: (text) => ParsedToolCall[] - function hasToolCallMarkup: (text) => boolean @@ -955,20 +1200,26 @@ - `apps/server/src/services/inference/turn.ts` - function runAssistantTurn: (ctx, args) => Promise - function runInference: (ctx, sessionId, chatId, assistantMessageId, signal?) => Promise + - function runInferenceWithModel: (ctx, sessionId, chatId, assistantMessageId, modelOverride, compareGroupId, signal?) => Promise - function createInferenceRunner: (ctx, 'publishUser'>, publishUserFn, frame) => void - `apps/server/src/services/mcp-client.ts` - function initialize: (entries, logger) => Promise - function callTool: (prefixedName, args, unknown>) => Promise + - function getServerPermission: (prefixedToolName) => McpPermission + - function setServerPermission: (serverName, permission) => void + - function getServerName: (prefixedToolName) => string | null - function getTools: () => ToolDef>[] - - function getMcpServers: () => Array< - - function shutdown: () => Promise - - function wrapMcpTool: (serverName, mcpTool) => ToolDef> - - _...2 more_ + - _...6 more_ - `apps/server/src/services/mcp-config.ts` - function substituteEnvVars: (value, log, unsetVars?) => unknown - function loadMcpConfig: (configPath, log) => McpServerEntry[] - interface McpServerEntry - type McpServerConfig +- `apps/server/src/services/memory/bm25.ts` — class Bm25Ranker +- `apps/server/src/services/memory/embeddings.ts` + - function isEmbeddingAvailable: () => boolean + - function initEmbeddings: (modelPath?) => Promise + - function embed: (texts) => Promise - `apps/server/src/services/memory/entries.ts` — function parseMemoryEntries: (fileName, markdown) => MemoryEntry[], interface MemoryEntry - `apps/server/src/services/memory/paths.ts` - function getMemoryRoot: (projectRoot) => string @@ -976,7 +1227,10 @@ - function ensureMemoryScaffold: (root) => Promise - type MemoryTopic - `apps/server/src/services/memory/prompt.ts` — function formatMemoryBlock: (entries) => string -- `apps/server/src/services/memory/recall.ts` — function rankByRelevance: (query, entries) => MemoryEntry[], function loadMemoryForSession: (projectRoot, _sessionId?, query?) => Promise +- `apps/server/src/services/memory/recall.ts` + - function rankByRelevance: (query, entries) => MemoryEntry[] + - function rankByHybrid: (query, entries) => Promise + - function loadMemoryForSession: (projectRoot, _sessionId?, query?) => Promise - `apps/server/src/services/memory/scan.ts` - function scanMemoryScopes: (scope) => Promise - function scanProjectMemory: (projectRoot) => Promise @@ -1007,6 +1261,11 @@ - function filterSecretEntries: (entries, pathOf) => void - class SecretBlockedError - const DEFAULT_SECURITY_IGNORE_FILETYPES: ReadonlyArray +- `apps/server/src/services/session-snapshots.ts` + - function saveAgentSnapshot: (sql, chatId, data) => Promise + - function loadAgentSnapshot: (sql, chatId) => Promise + - function deleteAgentSnapshot: (sql, chatId) => Promise + - interface AgentSnapshot - `apps/server/src/services/skill-invoke.ts` - function runSkillInvokeTransaction: (sql, args) => Promise< - function buildSkillInvokeSyntheticFrames: (chatId, result, toolCall, skillBody) => SkillInvokeSessionFrame[] @@ -1037,8 +1296,53 @@ - _...2 more_ - `apps/server/src/services/task-model.ts` — function taskModelCompletion: (opts) => Promise - `apps/server/src/services/task-search-rewrite.ts` — function rewriteSearchQuery: (userMessage) => Promise +- `apps/server/src/services/tool-traces.ts` + - function insertToolTrace: (sql, insert) => Promise + - function updateToolTrace: (sql, id, updates) => Promise + - interface ToolTrace + - interface ToolTraceInsert + - interface ToolTraceUpdate +- `apps/server/src/services/tools/background-subagent-tools.ts` + - function executeSpawnSubagent: (input, sql, sessionId) => Promise> + - function executeSubagentStatus: (input, sql) => Promise> + - function executeSubagentResult: (input, sql) => Promise> + - type SpawnSubagentInputT + - type SubagentStatusInputT + - type SubagentResultInputT + - _...6 more_ - `apps/server/src/services/tools/codecontext/factory.ts` — function makeCodecontextTool: (opts, unknown>; mapArgs) => void +- `apps/server/src/services/tools/codecontext/get_code_health.ts` + - function executeGetCodeHealth: (input, projectPath) => Promise + - type GetCodeHealthInputT + - const GetCodeHealthInput + - const getCodeHealth: ToolDef +- `apps/server/src/services/tools/codecontext/get_code_impact.ts` + - function executeGetCodeImpact: (input, projectPath) => Promise + - type GetCodeImpactInputT + - const GetCodeImpactInput + - const getCodeImpact: ToolDef +- `apps/server/src/services/tools/codecontext/get_code_map.ts` + - function executeGetCodeMap: (input, projectRoot) => Promise + - interface CodeMapResponse + - type GetCodeMapInputT + - const GetCodeMapInput + - const getCodeMap: ToolDef +- `apps/server/src/services/tools/codecontext/get_type_info.ts` + - function executeGetTypeInfo: (input, _projectPath?) => Promise + - type GetTypeInfoInputT + - const GetTypeInfoInput + - const getTypeInfo: ToolDef +- `apps/server/src/services/tools/codecontext/get_wiki_article.ts` + - function executeGetWikiArticle: (input, projectPath) => Promise + - type GetWikiArticleInputT + - const GetWikiArticleInput + - const getWikiArticle: ToolDef +- `apps/server/src/services/tools/execute-command.ts` + - function executeRunCommand: (input, projectRoot) => Promise + - type RunCommandInputT + - type RunCommandOutput + - const runCommand: ToolDef - `apps/server/src/services/tools/registry.ts` — function appendMcpTools: (mcpTools) => void, function toolJsonSchemas: () => ToolJsonSchema[] - `apps/server/src/services/tools/tiers.ts` - function resolveToolTier: (tier) => readonly string[] @@ -1064,6 +1368,39 @@ - interface WebSearchOutput - type WebSearchInputT - const webSearch: ToolDef +- `apps/server/src/services/workflow/catalog.ts` + - function fingerprintAgentTask: (prompt, spec, unknown>, args) => string + - function getBuiltinWorkflows: () => BuiltinWorkflow[] + - function getBuiltinWorkflow: (name) => BuiltinWorkflow | undefined + - function mergeBuiltinWorkflows: (fileWorkflows) => Array< + - interface BuiltinWorkflow + - const meta +- `apps/server/src/services/workflow/discovery.ts` + - function isBuiltinWorkflow: (meta) => boolean + - function discoverWorkflows: (projectRoot) => WorkflowMeta[] + - function findWorkflow: (name, projectRoot) => WorkflowMeta | undefined + - function isValidWorkflowPath: (filePath) => boolean + - interface WorkflowMeta +- `apps/server/src/services/workflow/manager.ts` + - class WorkflowManager + - interface WorkflowMetaInfo + - type WorkflowEventHandler +- `apps/server/src/services/workflow/resumability.ts` + - function cacheKey: (spec, args) => string + - function getCachedResult: (key) => CachedResult | null + - function setCachedResult: (key, result) => void + - function invalidateRun: (runKey) => void + - function clearCache: () => void + - function cacheSize: () => number + - _...1 more_ +- `apps/server/src/services/workflow/sandbox.ts` + - function transformEsmToCjs: (code) => string + - function name: (...) => void + - function isEsmSyntax: (code) => boolean + - function buildSandbox: (context) => Record + - function loadWorkflowScript: (sourceFile, context) => (...args: unknown[]) => Promise + - function loadWorkflowScriptFromCode: (code, context, filename?) => (...args: unknown[]) => Promise + - _...3 more_ - `apps/server/src/utils/string-utils.ts` — function stripQuotes: (s) => string - `apps/web/src/api/client.ts` - class ApiError @@ -1084,7 +1421,7 @@ - interface TerminalSelectionActions - interface TerminalSelection - `apps/web/src/hooks/terminal/useTerminalSocket.ts` - - function useTerminalSocket: ({...}, sessionId, paneId, fit, getSize, setSize, }) => TerminalSocket + - function useTerminalSocket: ({...}, sessionId, paneId, description, parentAgent, fit, getSize, setSize, }) => TerminalSocket - interface TerminalSocket - type ConnState - `apps/web/src/hooks/useActivePane.ts` @@ -1108,7 +1445,8 @@ - interface ThroughputSample - `apps/web/src/hooks/useCoderUserEvents.ts` — function useCoderUserEvents: () => void - `apps/web/src/hooks/useDiffPreferences.ts` — function useDiffPreferences: () => void, interface DiffPreferences -- `apps/web/src/hooks/useGitDiff.ts` — function useGitDiff: (projectId) => void +- `apps/web/src/hooks/useDraftPersistence.ts` — function useDraftPersistence: (chatId) => DraftPersistenceResult, interface DraftPersistenceResult +- `apps/web/src/hooks/useGitDiff.ts` — function useGitDiff: (projectId, hideWhitespace) => void - `apps/web/src/hooks/useLongPress.ts` — function useLongPress: (callback) => void - `apps/web/src/hooks/useProjectGit.ts` — function useProjectGit: (projectId) => GitMeta | null - `apps/web/src/hooks/useProviderSnapshot.ts` — function refreshProviderSnapshot: (cwd?) => Promise, function useProviderSnapshot: (cwd?) => ProviderSnapshotEntry[] | null @@ -1121,6 +1459,7 @@ - `apps/web/src/hooks/useSessions.ts` — function useSessions: (projectId) => void - `apps/web/src/hooks/useSidebar.ts` — function useSidebar: () => void - `apps/web/src/hooks/useSkills.ts` — function useSkills: () => void +- `apps/web/src/hooks/useTerminals.ts` — function useTerminals: () => TerminalRegistration[] - `apps/web/src/hooks/useUserEvents.ts` — function useUserEvents: () => void - `apps/web/src/hooks/useViewport.ts` — function useViewport: () => ViewportSnapshot, interface ViewportSnapshot - `apps/web/src/hooks/useWorkspacePanes.ts` @@ -1183,7 +1522,16 @@ - interface ThemeMeta - type ThemeId - _...5 more_ +- `apps/web/src/lib/tool-utils.ts` + - function isMcpTool: (name) => boolean + - function extractServerName: (name) => string | null + - function extractToolName: (name) => string | null + - const BUILT_IN_TOOLS - `apps/web/src/lib/utils.ts` — function cn: (...inputs) => void +- `apps/web/src/stores/useDiffCommentStore.ts` + - function useDiffComments: (sessionId, mode) => void + - interface DiffComment + - interface DiffCommentTarget - `apps/web/src/utils/diff-layout.ts` - function parseDiff: (diffBody) => ParsedDiffFile[] - function buildSplitRows: (file) => SplitRow[] @@ -1344,8 +1692,11 @@ - `CONTAINER_GUIDANCE_FILE` **required** — apps/server/src/services/__tests__/system-prompt.test.ts - `CONTEXT7_API_KEY` (has default) — .env - `DATABASE_URL` (has default) — .env.example +- `DEEPSEEK_API_KEY` (has default) — .env +- `DEEPSEEK_BASE_URL` (has default) — .env - `DEFAULT_MODEL` (has default) — .env.example - `DEV_REMOTE_USER` **required** — apps/web/vite.config.ts +- `EMBEDDING_MODEL_PATH` **required** — apps/server/src/services/memory/embeddings.ts - `GITEA_BASE_URL` (has default) — .env - `GITEA_SSH_HOST` (has default) — .env - `GITEA_TOKEN` (has default) — .env @@ -1353,6 +1704,7 @@ - `LLAMA_SWAP_URL` (has default) — .env.example - `MCP_TEST_MISSING` **required** — apps/server/src/services/__tests__/mcp-config.test.ts - `MCP_TEST_SECRET` **required** — apps/server/src/services/__tests__/mcp-config.test.ts +- `MEMORY_SEARCH` **required** — apps/server/src/services/memory/recall.ts - `NODE_ENV` (has default) — .env.example - `PORT` (has default) — .env.example - `POSTGRES_PASSWORD` (has default) — .env.example @@ -1368,6 +1720,10 @@ - `apps/web/vite.config.ts` - `docker-compose.yml` +## Key Dependencies + +- better-sqlite3: ^11.10.0 + --- # Middleware @@ -1379,6 +1735,7 @@ - turn-guard — `apps/coder/src/services/backends/turn-guard.ts` - get_middleware — `apps/server/src/services/tools/codecontext/get_middleware.ts` - authoring — `conductor/src/flows/authoring.ts` +- spec — `openspec/changes/add-behavioral-engine/specs/audit-middleware/spec.md` ## custom - write_guard.test — `apps/coder/src/services/__tests__/write_guard.test.ts` @@ -1400,39 +1757,39 @@ ## Most Imported Files (change these carefully) -- `apps/coder/src/db.ts` — imported by **40** files -- `apps/server/src/types/api.ts` — imported by **28** files -- `apps/server/src/db.ts` — imported by **25** files +- `apps/coder/src/db.ts` — imported by **44** files +- `apps/server/src/types/api.ts` — imported by **34** files +- `apps/server/src/db.ts` — imported by **32** files - `packages/ion/src/cli/utils.ts` — imported by **24** files - `apps/coder/src/services/tools/types.ts` — imported by **18** files -- `apps/coder/src/conductor/types.ts` — imported by **14** files +- `apps/coder/src/conductor/types.ts` — imported by **16** files +- `apps/server/src/services/tools.ts` — imported by **15** files - `apps/coder/src/services/agent-backend.ts` — imported by **14** files - `apps/coder/src/services/acp-tool-snapshot.ts` — imported by **14** files +- `apps/server/src/config.ts` — imported by **14** files - `apps/server/src/services/tools/codecontext/factory.ts` — imported by **14** files -- `apps/server/src/services/tools.ts` — imported by **13** files +- `apps/server/src/services/tools/types.ts` — imported by **13** files - `conductor/src/types.ts` — imported by **13** files - `apps/coder/src/services/provider-config-registry.ts` — imported by **12** files -- `apps/server/src/config.ts` — imported by **12** files - `apps/coder/src/config.ts` — imported by **11** files - `apps/coder/src/services/provider-types.ts` — imported by **11** files +- `apps/server/src/services/broker.ts` — imported by **10** files - `apps/server/src/services/agents.ts` — imported by **10** files +- `apps/server/src/services/path_guard.ts` — imported by **10** files - `apps/coder/src/services/pending_changes.ts` — imported by **9** files -- `apps/server/src/services/broker.ts` — imported by **9** files -- `apps/server/src/services/path_guard.ts` — imported by **9** files -- `apps/server/src/services/inference/payload.ts` — imported by **9** files ## Import Map (who imports what) -- `apps/coder/src/db.ts` ← `apps/coder/src/index.ts`, `apps/coder/src/routes/__tests__/agent-sessions.routes.test.ts`, `apps/coder/src/routes/__tests__/chat-resolve.test.ts`, `apps/coder/src/routes/__tests__/providers.routes.test.ts`, `apps/coder/src/routes/agent-sessions.ts` +35 more -- `apps/server/src/types/api.ts` ← `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts`, `apps/server/src/routes/models.ts`, `apps/server/src/routes/projects.ts`, `apps/server/src/routes/sessions.ts` +23 more -- `apps/server/src/db.ts` ← `apps/server/src/index.ts`, `apps/server/src/routes/agents.ts`, `apps/server/src/routes/artifacts.ts`, `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts` +20 more +- `apps/coder/src/db.ts` ← `apps/coder/src/index.ts`, `apps/coder/src/routes/__tests__/agent-sessions.routes.test.ts`, `apps/coder/src/routes/__tests__/chat-resolve.test.ts`, `apps/coder/src/routes/__tests__/providers.routes.test.ts`, `apps/coder/src/routes/agent-sessions.ts` +39 more +- `apps/server/src/types/api.ts` ← `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts`, `apps/server/src/routes/models.ts`, `apps/server/src/routes/projects.ts`, `apps/server/src/routes/sessions.ts` +29 more +- `apps/server/src/db.ts` ← `apps/server/src/index.ts`, `apps/server/src/routes/agents.ts`, `apps/server/src/routes/analytics.ts`, `apps/server/src/routes/artifacts.ts`, `apps/server/src/routes/chats.ts` +27 more - `packages/ion/src/cli/utils.ts` ← `packages/ion/src/cli/commands/abandon.ts`, `packages/ion/src/cli/commands/abandon.ts`, `packages/ion/src/cli/commands/approve.ts`, `packages/ion/src/cli/commands/approve.ts`, `packages/ion/src/cli/commands/cleanup.ts` +19 more - `apps/coder/src/services/tools/types.ts` ← `apps/coder/src/routes/messages.ts`, `apps/coder/src/services/dispatcher.ts`, `apps/coder/src/services/tools/adapter.ts`, `apps/coder/src/services/tools/apply_pending.ts`, `apps/coder/src/services/tools/check_task_status.ts` +13 more -- `apps/coder/src/conductor/types.ts` ← `apps/coder/src/conductor/flows/_util.ts`, `apps/coder/src/conductor/flows/architectural-analysis.ts`, `apps/coder/src/conductor/flows/authoring.ts`, `apps/coder/src/conductor/flows/code-review.ts`, `apps/coder/src/conductor/flows/discovery.ts` +9 more +- `apps/coder/src/conductor/types.ts` ← `apps/coder/src/conductor/flows/_util.ts`, `apps/coder/src/conductor/flows/architectural-analysis.ts`, `apps/coder/src/conductor/flows/authoring.ts`, `apps/coder/src/conductor/flows/code-review.ts`, `apps/coder/src/conductor/flows/discovery.ts` +11 more +- `apps/server/src/services/tools.ts` ← `apps/server/src/index.ts`, `apps/server/src/services/__tests__/agent-allowlist.test.ts`, `apps/server/src/services/agents.ts`, `apps/server/src/services/inference/stream-phase-adapter.ts`, `apps/server/src/services/inference/stream-phase.ts` +10 more - `apps/coder/src/services/agent-backend.ts` ← `apps/coder/src/routes/lifecycle.ts`, `apps/coder/src/services/__tests__/stream-json-parser.test.ts`, `apps/coder/src/services/acp-event-map.ts`, `apps/coder/src/services/agent-pool.ts`, `apps/coder/src/services/backends/__tests__/claude-sdk-map.test.ts` +9 more - `apps/coder/src/services/acp-tool-snapshot.ts` ← `apps/coder/src/services/__tests__/acp-event-map.test.ts`, `apps/coder/src/services/__tests__/frame-emitter.test.ts`, `apps/coder/src/services/__tests__/stream-json-parser.test.ts`, `apps/coder/src/services/acp-dispatch.ts`, `apps/coder/src/services/acp-event-map.ts` +9 more -- `apps/server/src/services/tools/codecontext/factory.ts` ← `apps/server/src/services/tools/codecontext/get_blast_radius.ts`, `apps/server/src/services/tools/codecontext/get_call_graph.ts`, `apps/server/src/services/tools/codecontext/get_codebase_overview.ts`, `apps/server/src/services/tools/codecontext/get_dependencies.ts`, `apps/server/src/services/tools/codecontext/get_file_analysis.ts` +9 more -- `apps/server/src/services/tools.ts` ← `apps/server/src/index.ts`, `apps/server/src/services/__tests__/agent-allowlist.test.ts`, `apps/server/src/services/agents.ts`, `apps/server/src/services/inference/stream-phase-adapter.ts`, `apps/server/src/services/inference/stream-phase.ts` +8 more +- `apps/server/src/config.ts` ← `apps/server/src/db.ts`, `apps/server/src/index.ts`, `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts`, `apps/server/src/routes/models.ts` +9 more --- diff --git a/.codesight/components.md b/.codesight/components.md index 1906d05..d9e5c57 100644 --- a/.codesight/components.md +++ b/.codesight/components.md @@ -10,23 +10,34 @@ - **AttachmentChip** — props: attachment, onRemove, onPreview — `apps/web/src/components/AttachmentChip.tsx` - **AttachmentPreviewModal** — props: attachment, onClose — `apps/web/src/components/AttachmentPreviewModal.tsx` - **BottomSheet** — props: open, onClose, title — `apps/web/src/components/BottomSheet.tsx` +- **CacheShapeBadge** — props: cacheTokens, totalTokens — `apps/web/src/components/CacheShapeBadge.tsx` - **CapHitSentinel** — props: message, capHitPosition, isLatest — `apps/web/src/components/CapHitSentinel.tsx` - **ChatInput** — props: disabled, projectId, agentId, onAgentChange, sessionId, webSearchEnabled, onSend, onForceSend, generating, onStop — `apps/web/src/components/ChatInput.tsx` - **ChatTabBar** — props: pane, tabs, tabNumbers, onSwitchTab, onRemoveTab, onCloseOthers, onCloseToRight, onCloseAll, onNewTab, onSplitPane — `apps/web/src/components/ChatTabBar.tsx` - **ChatThroughput** — props: chatId, className — `apps/web/src/components/ChatThroughput.tsx` - **CodeBlock** — props: code, lang — `apps/web/src/components/CodeBlock.tsx` +- **ComparePane** — props: models, responses, onClose — `apps/web/src/components/ComparePane.tsx` - **ContextMeter** — props: messages, modelContextLimit, sessionCostUsd — `apps/web/src/components/ContextMeter.tsx` - **CreateProjectModal** — props: open, onOpenChange — `apps/web/src/components/CreateProjectModal.tsx` +- **DiffSnippet** — props: diff — `apps/web/src/components/DiffSnippet.tsx` +- **DiffSplitView** — props: file, wrapLines — `apps/web/src/components/DiffSplitView.tsx` - **DoomLoopSentinel** — props: message — `apps/web/src/components/DoomLoopSentinel.tsx` - **DropOverlay** — props: visible — `apps/web/src/components/DropOverlay.tsx` +- **EmptyState** — props: icon, title, description, action, className — `apps/web/src/components/EmptyState.tsx` - **FileMentionPopover** — props: query, files, anchorRect, onSelect, onClose — `apps/web/src/components/FileMentionPopover.tsx` - **FileViewerOverlay** — props: path, content, lang, onClose — `apps/web/src/components/FileViewerOverlay.tsx` - **FlowLauncherDialog** — `apps/web/src/components/FlowLauncherDialog.tsx` - **GitDiffView** — props: result, loading, error, mode, onSelectMode, onRefresh, mutating, mutateError, onStage, onUnstage — `apps/web/src/components/GitDiffView.tsx` - **HtmlArtifactPane** — props: chatId, state, onClose — `apps/web/src/components/HtmlArtifactPane.tsx` - **InferenceSettings** — `apps/web/src/components/InferenceSettings.tsx` +- **InlineReviewEditor** — props: initialBody, onSave, onCancel — `apps/web/src/components/InlineReviewEditor.tsx` +- **InlineReviewGutterCell** — props: lineNumber, type, hasComments, canComment, onClick — `apps/web/src/components/InlineReviewGutterCell.tsx` +- **InlineReviewThread** — props: comments, onEditComment, onDeleteComment — `apps/web/src/components/InlineReviewThread.tsx` +- **KeyboardShortcutsDialog** — props: open, onOpenChange — `apps/web/src/components/KeyboardShortcutsDialog.tsx` - **MarkdownArtifactPane** — props: chatId, state, onClose — `apps/web/src/components/MarkdownArtifactPane.tsx` - **MarkdownRenderer** — props: content — `apps/web/src/components/MarkdownRenderer.tsx` +- **McpPermissionDialog** — props: toolCallId, toolName, toolArgs, chatId, open, onClose — `apps/web/src/components/McpPermissionDialog.tsx` +- **McpResponseDisplay** — props: toolCall, toolResult — `apps/web/src/components/McpResponseDisplay.tsx` - **MessageBubble** — props: message, sessionChats, capHitInfo, actions, hideActions, hasCheckpoint, restoreDisabled — `apps/web/src/components/MessageBubble.tsx` - **MessageList** — props: messages, sessionChats — `apps/web/src/components/MessageList.tsx` - **MobileTabSwitcher** — props: panes, activePaneIdx, chats, onSwitchPane, onRemovePane, onRenameChat — `apps/web/src/components/MobileTabSwitcher.tsx` @@ -38,12 +49,14 @@ - **RequestReadAccessCard** — props: toolCall, toolResult, chatId — `apps/web/src/components/RequestReadAccessCard.tsx` - **RightRail** — props: projectId, sessionId — `apps/web/src/components/RightRail.tsx` - **SessionLandingPage** — props: projectId, sessionId, agentId, onAgentChange, onSend, onSkillInvoke, createChat, chats, onOpenChat, onUnarchiveChat — `apps/web/src/components/SessionLandingPage.tsx` +- **SessionTimeline** — props: messages, onClose, onScrollToMessage — `apps/web/src/components/SessionTimeline.tsx` - **SlashCommandPicker** — props: query, items, groups, inputRef, onSelect, onClose, emptyLabel — `apps/web/src/components/SlashCommandPicker.tsx` - **StaleStreamBanner** — props: onRetry, onDiscard — `apps/web/src/components/StaleStreamBanner.tsx` - **StatusDot** — props: chatId, className — `apps/web/src/components/StatusDot.tsx` - **ThemePicker** — `apps/web/src/components/ThemePicker.tsx` - **ToolCallGroup** — props: runs — `apps/web/src/components/ToolCallGroup.tsx` -- **ToolCallLine** — props: run, insideGroup — `apps/web/src/components/ToolCallLine.tsx` +- **ToolCallLine** — props: run, insideGroup, chatId — `apps/web/src/components/ToolCallLine.tsx` +- **TraceViewer** — props: chatId — `apps/web/src/components/TraceViewer.tsx` - **Workspace** — props: sessionId, projectId, agentId, onAgentChange, panesHook, chatsHook, session, project, onAddPane — `apps/web/src/components/Workspace.tsx` - **AddProviderModal** — props: open, onOpenChange, onAdded — `apps/web/src/components/coder/AddProviderModal.tsx` - **ProvidersSettings** — `apps/web/src/components/coder/ProvidersSettings.tsx` @@ -52,20 +65,30 @@ - **ThemeFx** — `apps/web/src/components/fx/ThemeFx.tsx` - **ClaudeIcon** — props: size, className — `apps/web/src/components/icons/ProviderIcons.tsx` - **OpenCodeIcon** — props: size, className — `apps/web/src/components/icons/ProviderIcons.tsx` +- **ActionRow** — props: message, actions, hiddenSet, hasCheckpoint, restoreDisabled — `apps/web/src/components/message-parts/ActionRow.tsx` +- **CompactCard** — props: message, sessionChats — `apps/web/src/components/message-parts/CompactCard.tsx` +- **MistakeRecoverySentinel** — props: message — `apps/web/src/components/message-parts/MistakeRecoverySentinel.tsx` +- **ReasoningBlock** — props: text, streaming — `apps/web/src/components/message-parts/ReasoningBlock.tsx` +- **SendToTerminalMenu** — `apps/web/src/components/message-parts/SendToTerminalMenu.tsx` +- **StatsLine** — props: message — `apps/web/src/components/message-parts/StatsLine.tsx` +- **SummaryCard** — props: message — `apps/web/src/components/message-parts/SummaryCard.tsx` - **ArenaPane** — props: state, onClose — `apps/web/src/components/panes/ArenaPane.tsx` - **ChatPane** — props: sessionId, chatId, projectId, agentId, onAgentChange, sessionChats, webSearchEnabled — `apps/web/src/components/panes/ChatPane.tsx` - **CoderMessageList** — props: messages, chatId, footer, actions, checkpointMessageIds, restoreDisabled — `apps/web/src/components/panes/CoderMessageList.tsx` - **CoderPane** — props: sessionId, paneId, chatId, chatPending, projectPath, onConnectedChange, onAgentLabelChange — `apps/web/src/components/panes/CoderPane.tsx` - **OrchestratorPane** — props: state, onClose — `apps/web/src/components/panes/OrchestratorPane.tsx` - **SettingsPane** — props: session, project, maximized, onToggleMaximize, onClose, isMobile — `apps/web/src/components/panes/SettingsPane.tsx` -- **TerminalPane** — props: sessionId, paneId, label, active — `apps/web/src/components/panes/TerminalPane.tsx` +- **TerminalPane** — props: sessionId, paneId, label, description, parentAgent, active — `apps/web/src/components/panes/TerminalPane.tsx` - **FloatingMenu** — props: x, y, hasSelection, chatInputs, onCopy, onPaste, onSelectAll, onSearch, onSendToChat, onDismiss — `apps/web/src/components/panes/terminal/FloatingMenu.tsx` - **SearchBar** — props: searchRef, theme, onClose — `apps/web/src/components/panes/terminal/SearchBar.tsx` - **TerminalHotkeyBar** — props: ctrlArmed, onSendBytes, onArmCtrl, onFit — `apps/web/src/components/panes/terminal/TerminalHotkeyBar.tsx` - **RightRailDrawerProvider** — `apps/web/src/hooks/useRightRailDrawer.tsx` - **SidebarDrawerProvider** — `apps/web/src/hooks/useSidebarDrawer.tsx` - **PATH_REGEX** — `apps/web/src/lib/linkify-paths.tsx` +- **Analytics** — `apps/web/src/pages/Analytics.tsx` - **Home** — `apps/web/src/pages/Home.tsx` +- **Memory** — `apps/web/src/pages/Memory.tsx` - **Project** — `apps/web/src/pages/Project.tsx` +- **Results** — `apps/web/src/pages/Results.tsx` - **Session** — `apps/web/src/pages/Session.tsx` - **Settings** — `apps/web/src/pages/Settings.tsx` diff --git a/.codesight/config.md b/.codesight/config.md index 1c33524..016cbc4 100644 --- a/.codesight/config.md +++ b/.codesight/config.md @@ -25,8 +25,11 @@ - `CONTAINER_GUIDANCE_FILE` **required** — apps/server/src/services/__tests__/system-prompt.test.ts - `CONTEXT7_API_KEY` (has default) — .env - `DATABASE_URL` (has default) — .env.example +- `DEEPSEEK_API_KEY` (has default) — .env +- `DEEPSEEK_BASE_URL` (has default) — .env - `DEFAULT_MODEL` (has default) — .env.example - `DEV_REMOTE_USER` **required** — apps/web/vite.config.ts +- `EMBEDDING_MODEL_PATH` **required** — apps/server/src/services/memory/embeddings.ts - `GITEA_BASE_URL` (has default) — .env - `GITEA_SSH_HOST` (has default) — .env - `GITEA_TOKEN` (has default) — .env @@ -34,6 +37,7 @@ - `LLAMA_SWAP_URL` (has default) — .env.example - `MCP_TEST_MISSING` **required** — apps/server/src/services/__tests__/mcp-config.test.ts - `MCP_TEST_SECRET` **required** — apps/server/src/services/__tests__/mcp-config.test.ts +- `MEMORY_SEARCH` **required** — apps/server/src/services/memory/recall.ts - `NODE_ENV` (has default) — .env.example - `PORT` (has default) — .env.example - `POSTGRES_PASSWORD` (has default) — .env.example @@ -48,3 +52,7 @@ - `Dockerfile` - `apps/web/vite.config.ts` - `docker-compose.yml` + +## Key Dependencies + +- better-sqlite3: ^11.10.0 diff --git a/.codesight/graph.md b/.codesight/graph.md index 27323ec..df5e394 100644 --- a/.codesight/graph.md +++ b/.codesight/graph.md @@ -2,36 +2,36 @@ ## Most Imported Files (change these carefully) -- `apps/coder/src/db.ts` — imported by **40** files -- `apps/server/src/types/api.ts` — imported by **28** files -- `apps/server/src/db.ts` — imported by **25** files +- `apps/coder/src/db.ts` — imported by **44** files +- `apps/server/src/types/api.ts` — imported by **34** files +- `apps/server/src/db.ts` — imported by **32** files - `packages/ion/src/cli/utils.ts` — imported by **24** files - `apps/coder/src/services/tools/types.ts` — imported by **18** files -- `apps/coder/src/conductor/types.ts` — imported by **14** files +- `apps/coder/src/conductor/types.ts` — imported by **16** files +- `apps/server/src/services/tools.ts` — imported by **15** files - `apps/coder/src/services/agent-backend.ts` — imported by **14** files - `apps/coder/src/services/acp-tool-snapshot.ts` — imported by **14** files +- `apps/server/src/config.ts` — imported by **14** files - `apps/server/src/services/tools/codecontext/factory.ts` — imported by **14** files -- `apps/server/src/services/tools.ts` — imported by **13** files +- `apps/server/src/services/tools/types.ts` — imported by **13** files - `conductor/src/types.ts` — imported by **13** files - `apps/coder/src/services/provider-config-registry.ts` — imported by **12** files -- `apps/server/src/config.ts` — imported by **12** files - `apps/coder/src/config.ts` — imported by **11** files - `apps/coder/src/services/provider-types.ts` — imported by **11** files +- `apps/server/src/services/broker.ts` — imported by **10** files - `apps/server/src/services/agents.ts` — imported by **10** files +- `apps/server/src/services/path_guard.ts` — imported by **10** files - `apps/coder/src/services/pending_changes.ts` — imported by **9** files -- `apps/server/src/services/broker.ts` — imported by **9** files -- `apps/server/src/services/path_guard.ts` — imported by **9** files -- `apps/server/src/services/inference/payload.ts` — imported by **9** files ## Import Map (who imports what) -- `apps/coder/src/db.ts` ← `apps/coder/src/index.ts`, `apps/coder/src/routes/__tests__/agent-sessions.routes.test.ts`, `apps/coder/src/routes/__tests__/chat-resolve.test.ts`, `apps/coder/src/routes/__tests__/providers.routes.test.ts`, `apps/coder/src/routes/agent-sessions.ts` +35 more -- `apps/server/src/types/api.ts` ← `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts`, `apps/server/src/routes/models.ts`, `apps/server/src/routes/projects.ts`, `apps/server/src/routes/sessions.ts` +23 more -- `apps/server/src/db.ts` ← `apps/server/src/index.ts`, `apps/server/src/routes/agents.ts`, `apps/server/src/routes/artifacts.ts`, `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts` +20 more +- `apps/coder/src/db.ts` ← `apps/coder/src/index.ts`, `apps/coder/src/routes/__tests__/agent-sessions.routes.test.ts`, `apps/coder/src/routes/__tests__/chat-resolve.test.ts`, `apps/coder/src/routes/__tests__/providers.routes.test.ts`, `apps/coder/src/routes/agent-sessions.ts` +39 more +- `apps/server/src/types/api.ts` ← `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts`, `apps/server/src/routes/models.ts`, `apps/server/src/routes/projects.ts`, `apps/server/src/routes/sessions.ts` +29 more +- `apps/server/src/db.ts` ← `apps/server/src/index.ts`, `apps/server/src/routes/agents.ts`, `apps/server/src/routes/analytics.ts`, `apps/server/src/routes/artifacts.ts`, `apps/server/src/routes/chats.ts` +27 more - `packages/ion/src/cli/utils.ts` ← `packages/ion/src/cli/commands/abandon.ts`, `packages/ion/src/cli/commands/abandon.ts`, `packages/ion/src/cli/commands/approve.ts`, `packages/ion/src/cli/commands/approve.ts`, `packages/ion/src/cli/commands/cleanup.ts` +19 more - `apps/coder/src/services/tools/types.ts` ← `apps/coder/src/routes/messages.ts`, `apps/coder/src/services/dispatcher.ts`, `apps/coder/src/services/tools/adapter.ts`, `apps/coder/src/services/tools/apply_pending.ts`, `apps/coder/src/services/tools/check_task_status.ts` +13 more -- `apps/coder/src/conductor/types.ts` ← `apps/coder/src/conductor/flows/_util.ts`, `apps/coder/src/conductor/flows/architectural-analysis.ts`, `apps/coder/src/conductor/flows/authoring.ts`, `apps/coder/src/conductor/flows/code-review.ts`, `apps/coder/src/conductor/flows/discovery.ts` +9 more +- `apps/coder/src/conductor/types.ts` ← `apps/coder/src/conductor/flows/_util.ts`, `apps/coder/src/conductor/flows/architectural-analysis.ts`, `apps/coder/src/conductor/flows/authoring.ts`, `apps/coder/src/conductor/flows/code-review.ts`, `apps/coder/src/conductor/flows/discovery.ts` +11 more +- `apps/server/src/services/tools.ts` ← `apps/server/src/index.ts`, `apps/server/src/services/__tests__/agent-allowlist.test.ts`, `apps/server/src/services/agents.ts`, `apps/server/src/services/inference/stream-phase-adapter.ts`, `apps/server/src/services/inference/stream-phase.ts` +10 more - `apps/coder/src/services/agent-backend.ts` ← `apps/coder/src/routes/lifecycle.ts`, `apps/coder/src/services/__tests__/stream-json-parser.test.ts`, `apps/coder/src/services/acp-event-map.ts`, `apps/coder/src/services/agent-pool.ts`, `apps/coder/src/services/backends/__tests__/claude-sdk-map.test.ts` +9 more - `apps/coder/src/services/acp-tool-snapshot.ts` ← `apps/coder/src/services/__tests__/acp-event-map.test.ts`, `apps/coder/src/services/__tests__/frame-emitter.test.ts`, `apps/coder/src/services/__tests__/stream-json-parser.test.ts`, `apps/coder/src/services/acp-dispatch.ts`, `apps/coder/src/services/acp-event-map.ts` +9 more -- `apps/server/src/services/tools/codecontext/factory.ts` ← `apps/server/src/services/tools/codecontext/get_blast_radius.ts`, `apps/server/src/services/tools/codecontext/get_call_graph.ts`, `apps/server/src/services/tools/codecontext/get_codebase_overview.ts`, `apps/server/src/services/tools/codecontext/get_dependencies.ts`, `apps/server/src/services/tools/codecontext/get_file_analysis.ts` +9 more -- `apps/server/src/services/tools.ts` ← `apps/server/src/index.ts`, `apps/server/src/services/__tests__/agent-allowlist.test.ts`, `apps/server/src/services/agents.ts`, `apps/server/src/services/inference/stream-phase-adapter.ts`, `apps/server/src/services/inference/stream-phase.ts` +8 more +- `apps/server/src/config.ts` ← `apps/server/src/db.ts`, `apps/server/src/index.ts`, `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts`, `apps/server/src/routes/models.ts` +9 more diff --git a/.codesight/libs.md b/.codesight/libs.md index 0cb2c83..7c372ef 100644 --- a/.codesight/libs.md +++ b/.codesight/libs.md @@ -14,8 +14,17 @@ - function ensureSession: (tmuxConfPath, sessionName, projectRoot, log, cols?, rows?) => Promise - function killSession: (tmuxConfPath, sessionName) => Promise - function capturePane: (tmuxConfPath, sessionName, lines) => Promise + - _...1 more_ - `apps/booterm/src/pty/pty.ts` — function attachPty: (opts) => IPty -- `apps/booterm/src/ws/attach.ts` — function registerWsAttachRoute: (app, tmuxConfPath) => void +- `apps/booterm/src/pty/registry.ts` + - function register: (sessionId, paneId, projectPath, title?, opts?) => void + - function unregister: (paneId) => void + - function touchActivity: (paneId) => void + - function list: () => SessionMeta[] + - function get: (paneId) => SessionMeta | undefined + - function setPendingMetadata: (paneId, meta) => void + - _...8 more_ +- `apps/booterm/src/ws/attach.ts` — function registerWsAttachRoute: (app, tmuxConfPath, idleTimeoutSeconds?, absoluteTimeoutSeconds?) => void - `apps/coder/src/conductor/contracts.ts` - function produceContract: (contracts) => string - function reviewContract: (contracts) => string @@ -102,7 +111,7 @@ - function classifyLane: (battleType, _identity, model, localModels) => ContestantLane - function nextLocalContestant: (contestants) => string | null - function isBattleComplete: (contestants) => boolean - - function computeBenchmark: (startedAt, endedAt, costTokens, lane) => Benchmark + - function computeBenchmark: (startedAt, endedAt, costTokens, lane, tokenBreakdown) => Benchmark - function sanitizeSlug: (s) => string - function buildBattleSlug: (battleId, battleType, createdAt) => string - _...7 more_ @@ -166,6 +175,7 @@ - function stepEndedToUsage: (props) => StepUsage - interface StepEndedProps - interface StepUsage +- `apps/coder/src/services/backends/paseo.ts` — class PaseoBackend, interface PaseoBackendDeps - `apps/coder/src/services/backends/pushable-iterable.ts` — function createPushable: () => Pushable, interface Pushable - `apps/coder/src/services/backends/turn-guard.ts` - function armAbortGuard: (g) => void @@ -174,6 +184,30 @@ - interface AbortTerminalGuard - `apps/coder/src/services/backends/warm-acp-routing.ts` — function shouldUseWarmBackend: (task) => boolean, function isTurnOkForStopReason: (stopReason) => boolean - `apps/coder/src/services/backends/warm-acp.ts` — class WarmAcpBackend, interface WarmAcpBackendDeps +- `apps/coder/src/services/behavioral/generation.ts` + - function createExecutionPlan: (observational, actionable, previouslyApplied, disambiguationGroups, lowCriticality) => BatchExecutionPlan[] + - function getRetryTemperatures: (baseTemp, maxAttempts) => number[] + - class SchematicGenerator + - class DefaultSchematicGenerator + - interface ObservationalOutput + - interface ActionableOutput + - _...7 more_ +- `apps/coder/src/services/behavioral/matching.ts` + - function matchWithRetry: (fn) => void + - function executeBatchesParallel: (batches, _generationInfo) => Promise + - function createScoredMatch: (guidelineId, score, rationale) => ScoredMatch + - class GuidelineMatchingBatchError + - class ObservationalGuidelineMatchingBatch + - class ActionableGuidelineMatchingBatch + - _...25 more_ +- `apps/coder/src/services/behavioral/resolver.ts` + - class RelationalResolver + - interface RelationshipEntity + - interface Relationship + - interface RelationshipStore + - interface ResolvedEntity + - interface Resolution + - _...8 more_ - `apps/coder/src/services/cancel-registry.ts` — function createCancelRegistry: () => CancelRegistry, interface CancelRegistry - `apps/coder/src/services/checkpoints.ts` - function buildShadowCommitCommand: (worktreePath, id) => string @@ -184,7 +218,15 @@ - interface RestoreCheckpointResult - _...1 more_ - `apps/coder/src/services/claude-command-discovery.ts` — function discoverClaudeCommands: () => AgentCommand[] +- `apps/coder/src/services/collision-detector.ts` + - function findConflicts: (changedFiles, worktreeId, /** Approximate line range for the proposed changes, keyed by file path */ + changedRanges, {...}, conflictIndex) => ConflictVerdict[] + - interface ConflictVerdict + - interface ConflictEntry + - type ConflictSeverity + - type ConflictIndexData - `apps/coder/src/services/command-availability.ts` — function isCommandAvailable: (binary) => Promise +- `apps/coder/src/services/conflict-index.ts` — class ConflictIndex, const conflictIndex - `apps/coder/src/services/correction-service.ts` - function recordCorrection: (originalClaim, correction, principleExtracted, persistedTo, basePath?) => Promise - function scanForCorrections: (auditPath) => Promise @@ -214,10 +256,11 @@ - function partitionReady: (ready, ctx) => void - function isRunComplete: (flow, state) => boolean - function isStuck: (flow, state) => boolean - - function reconcileResumeStep: (status, taskId, taskState) => ResumeAction - - _...5 more_ + - function buildBatchState: (flow, inFlight) => Map FlowRunner + - function resolveVariables: (prompt, results, string>) => string - interface LaunchOpts - interface FlowRunner - `apps/coder/src/services/frame-emitter.ts` @@ -237,6 +280,19 @@ - function deleteGuideline: (id, basePath?) => Promise - function findGuideline: (content, basePath?) => Promise - _...14 more_ +- `apps/coder/src/services/hashline/hash-computation.ts` + - function computeLineHash: (lineNumber, content) => string + - function computeLegacyLineHash: (lineNumber, content) => string + - function formatHashLine: (lineNumber, content) => string + - function formatHashLines: (content) => string +- `apps/coder/src/services/hashline/validation.ts` + - function normalizeLineRef: (ref) => string + - function parseLineRef: (ref) => LineRef + - function validateLineRef: (lines, ref) => void + - function validateLineRefs: (lines, refs) => void + - class HashlineMismatchError + - interface LineRef +- `apps/coder/src/services/hashline/xxhash32.ts` — function hashXxh32: (input, seed) => number - `apps/coder/src/services/host-exec.ts` — function hostExec: (command, opts?) => Promise, interface HostExecResult - `apps/coder/src/services/lsp/client.ts` — class LspClient - `apps/coder/src/services/lsp/config.ts` — function getServerConfig: (filePath) => LspServerConfig | null, interface LspServerConfig @@ -248,6 +304,44 @@ - function findReferences: (client, filePath, content, line, character) => Promise - `apps/coder/src/services/lsp/server-manager.ts` — class LspServerManager, const lspManager - `apps/coder/src/services/mcp-server.ts` — function startMcpServer: (sql) => Promise +- `apps/coder/src/services/model-resolution/connected-providers-cache.ts` + - function readConnectedProvidersCache: () => string[] | null + - function findProviderModelMetadata: (_providerID, _modelID) => ModelMetadata | undefined + - function readProviderModelsCache: () => ProviderModelsCache | null + - interface ProviderModelsCache + - interface ConnectedProvidersAdapter + - const connectedProvidersAdapter: ConnectedProvidersAdapter +- `apps/coder/src/services/model-resolution/fallback-chain-from-models.ts` + - function parseFallbackModelEntry: (model, contextProviderID, defaultProviderID) => FallbackEntry | undefined + - function parseFallbackModelObjectEntry: (obj, contextProviderID, defaultProviderID) => FallbackEntry | undefined + - function findMostSpecificFallbackEntry: (providerID, modelID, chain) => FallbackEntry | undefined + - function buildFallbackChainFromModels: (fallbackModels) => void +- `apps/coder/src/services/model-resolution/model-availability.ts` — function fuzzyMatchModel: (target, available, providers?) => string | null, function isModelAvailable: (targetModel, availableModels) => boolean +- `apps/coder/src/services/model-resolution/model-error-classifier.ts` + - function isRetryableModelError: (error) => boolean + - function shouldRetryError: (error) => boolean + - function getNextFallback: (fallbackChain, attemptCount) => FallbackEntry | undefined + - function hasMoreFallbacks: (fallbackChain, attemptCount) => boolean + - function selectFallbackProvider: (providers, preferredProviderID?) => string + - function selectFallbackProviderWithCache: (providers, providerCache, preferredProviderID?) => string + - _...1 more_ +- `apps/coder/src/services/model-resolution/model-normalization.ts` — function normalizeModel: (model?) => string | undefined, function normalizeModelID: (modelID) => string +- `apps/coder/src/services/model-resolution/model-resolution-pipeline.ts` + - function _setModelResolutionLogImplementationForTesting: (logImplementation) => void + - function resolveModelPipeline: (request, providerCache) => void + - type ModelResolutionRequest + - type ModelResolutionProvenance + - type ModelResolutionResult + - type ModelResolutionDeps +- `apps/coder/src/services/model-resolution/model-resolver.ts` + - function resolveModel: (input) => string | undefined + - function resolveModelWithFallback: (input, connectedProvidersAdapter) => ModelResolutionResult | undefined + - function normalizeFallbackModels: (models) => void + - function flattenToFallbackModelStrings: (models) => void + - type ModelResolutionInput + - type ModelSource + - _...2 more_ +- `apps/coder/src/services/model-resolution/provider-model-id-transform.ts` — function transformModelForProvider: (provider, model) => string, function transformModelForProviderDisplay: (provider, model) => string - `apps/coder/src/services/net/port-utils.ts` - function reclaimPort: (port) => void - function waitForPortRelease: (port, timeoutMs) => Promise @@ -257,6 +351,13 @@ - function createOrphanWorktreeReaper: (deps) => void - interface OrphanWorktreeReaperDeps - interface OrphanReaperResult +- `apps/coder/src/services/paseo-client.ts` + - class PaseoClientError + - class PaseoClient + - interface PaseoAgentListItem + - interface PaseoAgentDetail + - interface PaseoSendResult + - interface PaseoClientConfig - `apps/coder/src/services/pending_changes.ts` - function planEdit: (content, oldStr, newStr) => EditPlan - function queueEdit: (sql, sessionId, taskId, filePath, oldString, newString, projectRoot, // v2.6 Phase 1-UX) => void @@ -273,6 +374,14 @@ - function waitForElicitationResponse: (taskId, sessionId, provider, modeId, params, timeoutMs) => Promise - function cancelPendingPermission: (taskId) => void - _...3 more_ +- `apps/coder/src/services/plan-store.ts` + - function createPlan: (sql, opts) => Promise + - function getPlan: (sql, planId) => Promise + - function listPlans: (sql, projectId) => Promise + - function listActivePlans: (sql, projectId) => Promise + - function updatePlan: (sql, planId, opts) => Promise + - function updatePlanFromRun: (sql, runId, runStatus) => Promise + - _...5 more_ - `apps/coder/src/services/provider-commands.ts` - function getManifestCommands: (provider) => AgentCommand[] - function mergeCommands: (...lists) => AgentCommand[] @@ -295,13 +404,13 @@ - interface ProviderManifestEntry - const PROVIDER_MANIFEST: Record - `apps/coder/src/services/provider-snapshot.ts` + - function fetchDeepSeekModels: (config) => Promise - function fetchLlamaSwapModels: (config) => Promise - function prefixLlamaSwapModels: (models) => ProviderModel[] - function mergeModels: (...lists) => ProviderModel[] - function getProviderSnapshot: (sql, config, cwd?, force) => Promise - function clearProviderSnapshotCache: () => void - - function peekSnapshotEntry: (name, cwd?) => ProviderSnapshotEntry | undefined - - _...1 more_ + - _...2 more_ - `apps/coder/src/services/pty-dispatch.ts` - function dispatchViaPty: (opts) => Promise - interface DispatchResult @@ -411,6 +520,17 @@ - function readSession: (sessionId, projectRoot?) => SessionJson | null - _...9 more_ - `apps/server/src/services/auto_name.ts` — function maybeAutoNameChat: (ctx, chatId, sessionId) => Promise +- `apps/server/src/services/background-task.ts` + - function setBackgroundInferenceEnqueuer: (enqueue, chatId, assistantMessageId, user) => void + - function spawnBackgroundTask: (sql, log, projectId, input, model, agent?, label?) => Promise + - function getBackgroundTaskStatus: (sql, taskId) => Promise + - function getBackgroundTaskResult: (sql, taskId, chatId) => Promise< + - function cancelBackgroundTask: (sql, taskId) => Promise + - interface BackgroundTask +- `apps/server/src/services/boocontext_client.ts` + - function callBoocontext: (req, log?, msg) => void + - interface BoocontextRequest + - interface BoocontextResponse - `apps/server/src/services/broker.ts` - function createBroker: (log?) => Broker - interface Broker @@ -429,6 +549,7 @@ - function select: (messages, contextLimit, tailTurns) => SelectResult - function deriveFilesRead: (head) => string[] - _...8 more_ +- `apps/server/src/services/export-formatter.ts` — function formatJson: (chat, messages, model) => string, function formatMarkdown: (chat, messages, model) => string - `apps/server/src/services/file_index.ts` — function getProjectFiles: (projectId, projectRoot) => Promise - `apps/server/src/services/file_ops.ts` - function listDir: (projectRoot, relPath, opts?) => Promise @@ -453,7 +574,20 @@ - interface GiteaConfig - interface GiteaRepo - `apps/server/src/services/grant_resolver.ts` — function resolveGrantRoot: (sql, requestedPath, projectRoot, whitelistRoot) => Promise, type GrantResolution +- `apps/server/src/services/hooks.ts` + - function loadHooksConfig: (path) => HooksConfig + - function reloadHooksConfig: () => HooksConfig + - function createHookRunner: () => HookRunner + - interface HookConfig + - interface HooksConfig + - interface PreToolUsePayload + - _...10 more_ - `apps/server/src/services/inference/budget.ts` — function resolveToolBudget: (agent) => number +- `apps/server/src/services/inference/compute-diff.ts` + - function computeDiff: (oldStr, newStr, filePath) => string + - function isWriteTool: (name) => boolean + - function diffFromToolArgs: (name, args, unknown>, filePath?) => string + - const WRITE_TOOL_NAMES - `apps/server/src/services/inference/content-flusher.ts` — function createContentFlusher: (sql, messageId, getContent) => void, interface ContentFlusher - `apps/server/src/services/inference/dcp/messages.ts` - function toDcpMessages: (parts) => DcpMessage[] @@ -493,6 +627,10 @@ - type FailureKind - const MISTAKE_THRESHOLD - _...1 more_ +- `apps/server/src/services/inference/multi-modal.ts` + - function hasImageAttachments: (_message) => boolean + - function imageAttachmentsToParts: (attachments) => Array< + - interface ImageAttachment - `apps/server/src/services/inference/parts.ts` - function insertParts: (sql, parts) => Promise - function partsFromAssistantMessage: (args) => void @@ -505,10 +643,13 @@ - function maybeFlagForCompaction: (ctx, chatId, updated) => Promise - interface OpenAiMessage - `apps/server/src/services/inference/provider.ts` - - function resolveRoute: (agent, config?) => RoutingInfo + - function isDeepSeekModel: (modelId) => boolean + - function resolveRoute: (agent, config?, modelId?) => RoutingInfo - function upstreamModel: (config, modelId, agent?) => LanguageModel + - function resolveModelEndpoint: (config, modelId) => void + - function resetDeepSeekProvider: () => void - interface RoutingInfo - - type InferenceRoute + - _...1 more_ - `apps/server/src/services/inference/prune.ts` - function selectPruneTargets: (partsNewestFirst, tailStartCreatedAt) => void - function prune: (args) => Promise @@ -529,6 +670,12 @@ - function isAnySentinel: (m) => boolean - const DOOM_LOOP_THRESHOLD - _...1 more_ +- `apps/server/src/services/inference/state-graph.ts` + - function createDefaultGraph: () => GraphNode[] + - function runGraph: (ctx, args, extra) => Promise + - interface GraphState + - interface GraphResult + - type GraphNodeType - `apps/server/src/services/inference/step-decision.ts` - function decideStep: (input) => PreStepDecision - function decidePostToolAction: (action, mistakeTracker) => PostToolDecision @@ -545,12 +692,14 @@ - `apps/server/src/services/inference/stream-phase.ts` — function executeStreamPhase: (ctx, args, session, messages, state, agent, // v1.11.8, web_search and web_fetch are stripped from the // tool list sent to the LLM, so the model can't even attempt them. webToolsEnabled) => Promise +- `apps/server/src/services/inference/supervisor.ts` — function resolveSupervisorTurn: (latestUserMessage, agents, fallbackModel?) => Promise, interface SupervisorRoute - `apps/server/src/services/inference/tool-call-parser.ts` - function stripToolMarkup: (text, opts?) => string - function extractToolCallBlocks: (buffer, log?) => ToolCallExtraction - interface ParsedCall - interface ToolCallExtraction -- `apps/server/src/services/inference/tool-phase.ts` — function executeToolPhase: (ctx, args, result, startedAt, session, projectRoot, agent?) => Promise, interface ToolPhaseResult +- `apps/server/src/services/inference/tool-input-repair.ts` — function repairToolInput: (schema, unknown> | undefined, args, unknown>) => void, interface ToolInputRepair +- `apps/server/src/services/inference/tool-phase.ts` — function executeToolPhase: (ctx, args, result, startedAt, session, projectRoot, agent?, turnNumber?) => Promise, interface ToolPhaseResult - `apps/server/src/services/inference/tool-shim.ts` - function extractToolCalls: (text) => ParsedToolCall[] - function hasToolCallMarkup: (text) => boolean @@ -566,20 +715,26 @@ - `apps/server/src/services/inference/turn.ts` - function runAssistantTurn: (ctx, args) => Promise - function runInference: (ctx, sessionId, chatId, assistantMessageId, signal?) => Promise + - function runInferenceWithModel: (ctx, sessionId, chatId, assistantMessageId, modelOverride, compareGroupId, signal?) => Promise - function createInferenceRunner: (ctx, 'publishUser'>, publishUserFn, frame) => void - `apps/server/src/services/mcp-client.ts` - function initialize: (entries, logger) => Promise - function callTool: (prefixedName, args, unknown>) => Promise + - function getServerPermission: (prefixedToolName) => McpPermission + - function setServerPermission: (serverName, permission) => void + - function getServerName: (prefixedToolName) => string | null - function getTools: () => ToolDef>[] - - function getMcpServers: () => Array< - - function shutdown: () => Promise - - function wrapMcpTool: (serverName, mcpTool) => ToolDef> - - _...2 more_ + - _...6 more_ - `apps/server/src/services/mcp-config.ts` - function substituteEnvVars: (value, log, unsetVars?) => unknown - function loadMcpConfig: (configPath, log) => McpServerEntry[] - interface McpServerEntry - type McpServerConfig +- `apps/server/src/services/memory/bm25.ts` — class Bm25Ranker +- `apps/server/src/services/memory/embeddings.ts` + - function isEmbeddingAvailable: () => boolean + - function initEmbeddings: (modelPath?) => Promise + - function embed: (texts) => Promise - `apps/server/src/services/memory/entries.ts` — function parseMemoryEntries: (fileName, markdown) => MemoryEntry[], interface MemoryEntry - `apps/server/src/services/memory/paths.ts` - function getMemoryRoot: (projectRoot) => string @@ -587,7 +742,10 @@ - function ensureMemoryScaffold: (root) => Promise - type MemoryTopic - `apps/server/src/services/memory/prompt.ts` — function formatMemoryBlock: (entries) => string -- `apps/server/src/services/memory/recall.ts` — function rankByRelevance: (query, entries) => MemoryEntry[], function loadMemoryForSession: (projectRoot, _sessionId?, query?) => Promise +- `apps/server/src/services/memory/recall.ts` + - function rankByRelevance: (query, entries) => MemoryEntry[] + - function rankByHybrid: (query, entries) => Promise + - function loadMemoryForSession: (projectRoot, _sessionId?, query?) => Promise - `apps/server/src/services/memory/scan.ts` - function scanMemoryScopes: (scope) => Promise - function scanProjectMemory: (projectRoot) => Promise @@ -618,6 +776,11 @@ - function filterSecretEntries: (entries, pathOf) => void - class SecretBlockedError - const DEFAULT_SECURITY_IGNORE_FILETYPES: ReadonlyArray +- `apps/server/src/services/session-snapshots.ts` + - function saveAgentSnapshot: (sql, chatId, data) => Promise + - function loadAgentSnapshot: (sql, chatId) => Promise + - function deleteAgentSnapshot: (sql, chatId) => Promise + - interface AgentSnapshot - `apps/server/src/services/skill-invoke.ts` - function runSkillInvokeTransaction: (sql, args) => Promise< - function buildSkillInvokeSyntheticFrames: (chatId, result, toolCall, skillBody) => SkillInvokeSessionFrame[] @@ -648,8 +811,53 @@ - _...2 more_ - `apps/server/src/services/task-model.ts` — function taskModelCompletion: (opts) => Promise - `apps/server/src/services/task-search-rewrite.ts` — function rewriteSearchQuery: (userMessage) => Promise +- `apps/server/src/services/tool-traces.ts` + - function insertToolTrace: (sql, insert) => Promise + - function updateToolTrace: (sql, id, updates) => Promise + - interface ToolTrace + - interface ToolTraceInsert + - interface ToolTraceUpdate +- `apps/server/src/services/tools/background-subagent-tools.ts` + - function executeSpawnSubagent: (input, sql, sessionId) => Promise> + - function executeSubagentStatus: (input, sql) => Promise> + - function executeSubagentResult: (input, sql) => Promise> + - type SpawnSubagentInputT + - type SubagentStatusInputT + - type SubagentResultInputT + - _...6 more_ - `apps/server/src/services/tools/codecontext/factory.ts` — function makeCodecontextTool: (opts, unknown>; mapArgs) => void +- `apps/server/src/services/tools/codecontext/get_code_health.ts` + - function executeGetCodeHealth: (input, projectPath) => Promise + - type GetCodeHealthInputT + - const GetCodeHealthInput + - const getCodeHealth: ToolDef +- `apps/server/src/services/tools/codecontext/get_code_impact.ts` + - function executeGetCodeImpact: (input, projectPath) => Promise + - type GetCodeImpactInputT + - const GetCodeImpactInput + - const getCodeImpact: ToolDef +- `apps/server/src/services/tools/codecontext/get_code_map.ts` + - function executeGetCodeMap: (input, projectRoot) => Promise + - interface CodeMapResponse + - type GetCodeMapInputT + - const GetCodeMapInput + - const getCodeMap: ToolDef +- `apps/server/src/services/tools/codecontext/get_type_info.ts` + - function executeGetTypeInfo: (input, _projectPath?) => Promise + - type GetTypeInfoInputT + - const GetTypeInfoInput + - const getTypeInfo: ToolDef +- `apps/server/src/services/tools/codecontext/get_wiki_article.ts` + - function executeGetWikiArticle: (input, projectPath) => Promise + - type GetWikiArticleInputT + - const GetWikiArticleInput + - const getWikiArticle: ToolDef +- `apps/server/src/services/tools/execute-command.ts` + - function executeRunCommand: (input, projectRoot) => Promise + - type RunCommandInputT + - type RunCommandOutput + - const runCommand: ToolDef - `apps/server/src/services/tools/registry.ts` — function appendMcpTools: (mcpTools) => void, function toolJsonSchemas: () => ToolJsonSchema[] - `apps/server/src/services/tools/tiers.ts` - function resolveToolTier: (tier) => readonly string[] @@ -675,6 +883,39 @@ - interface WebSearchOutput - type WebSearchInputT - const webSearch: ToolDef +- `apps/server/src/services/workflow/catalog.ts` + - function fingerprintAgentTask: (prompt, spec, unknown>, args) => string + - function getBuiltinWorkflows: () => BuiltinWorkflow[] + - function getBuiltinWorkflow: (name) => BuiltinWorkflow | undefined + - function mergeBuiltinWorkflows: (fileWorkflows) => Array< + - interface BuiltinWorkflow + - const meta +- `apps/server/src/services/workflow/discovery.ts` + - function isBuiltinWorkflow: (meta) => boolean + - function discoverWorkflows: (projectRoot) => WorkflowMeta[] + - function findWorkflow: (name, projectRoot) => WorkflowMeta | undefined + - function isValidWorkflowPath: (filePath) => boolean + - interface WorkflowMeta +- `apps/server/src/services/workflow/manager.ts` + - class WorkflowManager + - interface WorkflowMetaInfo + - type WorkflowEventHandler +- `apps/server/src/services/workflow/resumability.ts` + - function cacheKey: (spec, args) => string + - function getCachedResult: (key) => CachedResult | null + - function setCachedResult: (key, result) => void + - function invalidateRun: (runKey) => void + - function clearCache: () => void + - function cacheSize: () => number + - _...1 more_ +- `apps/server/src/services/workflow/sandbox.ts` + - function transformEsmToCjs: (code) => string + - function name: (...) => void + - function isEsmSyntax: (code) => boolean + - function buildSandbox: (context) => Record + - function loadWorkflowScript: (sourceFile, context) => (...args: unknown[]) => Promise + - function loadWorkflowScriptFromCode: (code, context, filename?) => (...args: unknown[]) => Promise + - _...3 more_ - `apps/server/src/utils/string-utils.ts` — function stripQuotes: (s) => string - `apps/web/src/api/client.ts` - class ApiError @@ -695,7 +936,7 @@ - interface TerminalSelectionActions - interface TerminalSelection - `apps/web/src/hooks/terminal/useTerminalSocket.ts` - - function useTerminalSocket: ({...}, sessionId, paneId, fit, getSize, setSize, }) => TerminalSocket + - function useTerminalSocket: ({...}, sessionId, paneId, description, parentAgent, fit, getSize, setSize, }) => TerminalSocket - interface TerminalSocket - type ConnState - `apps/web/src/hooks/useActivePane.ts` @@ -719,7 +960,8 @@ - interface ThroughputSample - `apps/web/src/hooks/useCoderUserEvents.ts` — function useCoderUserEvents: () => void - `apps/web/src/hooks/useDiffPreferences.ts` — function useDiffPreferences: () => void, interface DiffPreferences -- `apps/web/src/hooks/useGitDiff.ts` — function useGitDiff: (projectId) => void +- `apps/web/src/hooks/useDraftPersistence.ts` — function useDraftPersistence: (chatId) => DraftPersistenceResult, interface DraftPersistenceResult +- `apps/web/src/hooks/useGitDiff.ts` — function useGitDiff: (projectId, hideWhitespace) => void - `apps/web/src/hooks/useLongPress.ts` — function useLongPress: (callback) => void - `apps/web/src/hooks/useProjectGit.ts` — function useProjectGit: (projectId) => GitMeta | null - `apps/web/src/hooks/useProviderSnapshot.ts` — function refreshProviderSnapshot: (cwd?) => Promise, function useProviderSnapshot: (cwd?) => ProviderSnapshotEntry[] | null @@ -732,6 +974,7 @@ - `apps/web/src/hooks/useSessions.ts` — function useSessions: (projectId) => void - `apps/web/src/hooks/useSidebar.ts` — function useSidebar: () => void - `apps/web/src/hooks/useSkills.ts` — function useSkills: () => void +- `apps/web/src/hooks/useTerminals.ts` — function useTerminals: () => TerminalRegistration[] - `apps/web/src/hooks/useUserEvents.ts` — function useUserEvents: () => void - `apps/web/src/hooks/useViewport.ts` — function useViewport: () => ViewportSnapshot, interface ViewportSnapshot - `apps/web/src/hooks/useWorkspacePanes.ts` @@ -794,7 +1037,16 @@ - interface ThemeMeta - type ThemeId - _...5 more_ +- `apps/web/src/lib/tool-utils.ts` + - function isMcpTool: (name) => boolean + - function extractServerName: (name) => string | null + - function extractToolName: (name) => string | null + - const BUILT_IN_TOOLS - `apps/web/src/lib/utils.ts` — function cn: (...inputs) => void +- `apps/web/src/stores/useDiffCommentStore.ts` + - function useDiffComments: (sessionId, mode) => void + - interface DiffComment + - interface DiffCommentTarget - `apps/web/src/utils/diff-layout.ts` - function parseDiff: (diffBody) => ParsedDiffFile[] - function buildSplitRows: (file) => SplitRow[] diff --git a/.codesight/middleware.md b/.codesight/middleware.md index 176f1e9..5ba07cf 100644 --- a/.codesight/middleware.md +++ b/.codesight/middleware.md @@ -7,6 +7,7 @@ - turn-guard — `apps/coder/src/services/backends/turn-guard.ts` - get_middleware — `apps/server/src/services/tools/codecontext/get_middleware.ts` - authoring — `conductor/src/flows/authoring.ts` +- spec — `openspec/changes/add-behavioral-engine/specs/audit-middleware/spec.md` ## custom - write_guard.test — `apps/coder/src/services/__tests__/write_guard.test.ts` diff --git a/.codesight/routes.md b/.codesight/routes.md index 941e1fe..e1f1814 100644 --- a/.codesight/routes.md +++ b/.codesight/routes.md @@ -3,6 +3,7 @@ ## CRUD Resources - **`/api/battles`** GET | POST | GET/:id → Battle +- **`/api/plans`** GET | POST | GET/:id | PATCH/:id → Plan - **`/api/runs`** GET | POST | GET/:id → Run - **`/api/tasks`** GET | POST | GET/:id → Task - **`/api/chats/:id/messages`** GET | POST | GET/:id | DELETE/:id → Message @@ -14,11 +15,16 @@ ### fastify - `GET` `/api/term/health` params() +- `GET` `/api/term/sessions/:sid/panes/:pid/search` params(sid, pid) [auth] +- `GET` `/api/term/sessions` params() [auth] - `POST` `/api/term/sessions/:sid/panes/:pid/start` params(sid, pid) [auth] - `POST` `/api/term/sessions/:sid/panes/:pid/kill` params(sid, pid) [auth] - `GET` `/ws/term/sessions/:sid/panes/:pid` params(sid, pid) [auth] - `GET` `/api/health` params() [auth, db, queue, ai] - `GET` `/api/sessions/:sessionId/agent-sessions` params(sessionId) [auth, db] +- `GET` `/api/analytics/summary` params() [auth, db] +- `GET` `/api/analytics/sessions` params() [auth, db] +- `GET` `/api/analytics/token-breakdown` params() [auth, db] - `POST` `/api/battles/generate-prompt` params() [auth, db] - `POST` `/api/battles/:id/stop` params(id) [auth, db] - `GET` `/api/battles/:id/analysis` params(id) [auth, db] @@ -42,6 +48,7 @@ - `POST` `/api/pending/:id/apply` params(id) [auth, db, queue] - `POST` `/api/pending/:id/reject` params(id) [auth, db, queue] - `POST` `/api/pending/:id/rewind` params(id) [auth, db, queue] +- `GET` `/api/plans/active` params() [db] - `GET` `/api/providers/snapshot` params() [db, cache] - `GET` `/api/providers/config` params() [db, cache] - `PATCH` `/api/providers/config` params() [db, cache] @@ -59,19 +66,22 @@ - `GET` `/api/ws/sessions/:sessionId` params(sessionId) [auth, db] - `GET` `/api/ws/user` params() [auth, db] - `GET` `/api/projects/:id/agents` params(id) [db, cache] +- `GET` `/api/analytics/context` params() [auth, db] - `POST` `/api/chats/:id/messages/:msg_id/artifacts/download` params(id, msg_id) [auth, db] - `GET` `/api/chats/:id/messages/:msg_id/html_artifact` params(id, msg_id) [auth, db] - `GET` `/api/projects/:project_id/artifacts/:filename` params(project_id, filename) [auth, db] -- `GET` `/api/sessions/:id/chats` params(id) [auth, db] -- `POST` `/api/sessions/:id/chats` params(id) [auth, db] -- `PATCH` `/api/chats/:id` params(id) [auth, db] -- `POST` `/api/sessions/:id/chats/archive-all` params(id) [auth, db] -- `GET` `/api/sessions/:id/chats/open-count` params(id) [auth, db] -- `POST` `/api/chats/:id/archive` params(id) [auth, db] -- `POST` `/api/chats/:id/unarchive` params(id) [auth, db] -- `DELETE` `/api/chats/:id` params(id) [auth, db] -- `POST` `/api/chats/:id/fork` params(id) [auth, db] -- `POST` `/api/chats/:id/discard_stale` params(id) [auth, db] +- `GET` `/api/sessions/:id/chats` params(id) [auth, db, queue] +- `POST` `/api/sessions/:id/chats` params(id) [auth, db, queue] +- `PATCH` `/api/chats/:id` params(id) [auth, db, queue] +- `POST` `/api/sessions/:id/chats/archive-all` params(id) [auth, db, queue] +- `GET` `/api/sessions/:id/chats/open-count` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/archive` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/unarchive` params(id) [auth, db, queue] +- `DELETE` `/api/chats/:id` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/fork` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/discard_stale` params(id) [auth, db, queue] +- `GET` `/api/chats/:id/export` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/compare` params(id) [auth, db, queue] - `GET` `/api/coder/ws/sessions/:sessionId` params(sessionId) [auth] - `ALL` `/api/coder/*` params() [auth] - `GET` `/api/settings/inference` params() [cache] @@ -83,7 +93,9 @@ - `POST` `/api/chats/:id/continue` params(id) [auth, db, queue] - `POST` `/api/chats/:id/force_send` params(id) [auth, db, queue] - `POST` `/api/chats/:id/grant_read_access` params(id) [auth, db, queue] -- `GET` `/api/models` params() +- `POST` `/api/chats/:id/mcp-approve` params(id) [auth, db, queue] +- `POST` `/api/chats/:id/messages/:message_id/feedback` params(id, message_id) [auth, db, queue] +- `GET` `/api/models` params() [auth] - `POST` `/api/projects/create` params() [auth, db] - `POST` `/api/projects/:id/archive` params(id) [auth, db] - `POST` `/api/projects/:id/unarchive` params(id) [auth, db] @@ -111,6 +123,7 @@ - `GET` `/api/skills` params() [auth, db, queue] - `POST` `/api/chats/:id/skill_invoke` params(id) [auth, db, queue] - `GET` `/api/tools/cost_stats` params() [auth, db] +- `GET` `/api/chats/:id/traces` params(id) [db] - `GET` `/api/ws/sessions/:id` params(id) [auth, db] ### go-net-http diff --git a/.codesight/schema.md b/.codesight/schema.md index 5048564..452c49e 100644 --- a/.codesight/schema.md +++ b/.codesight/schema.md @@ -118,6 +118,25 @@ - model: text (required) - verdict: text +### flow_step_events +- id: uuid (pk) +- run_id: uuid (required, fk) +- step_id: varchar (required, fk) +- event: varchar (required) +- payload: jsonb + +### plans +- id: uuid (pk) +- project_id: uuid (required, fk) +- title: text (required) +- description: text +- status: text (required) +- flow_run_id: uuid (fk) +- progress_pct: integer (required) +- items_total: integer (required) +- items_completed: integer (required) +- metadata: jsonb + ### projects - id: uuid (pk) - name: text (required) @@ -139,6 +158,8 @@ - content: text (required) - status: text (required) - last_seq: integer (required) +- cache_tokens: integer +- reasoning_tokens: integer ### message_parts - id: uuid (pk) @@ -155,3 +176,42 @@ - session_id: uuid (required, fk) - name: text - status: text (required) + +### tool_traces +- id: uuid (pk) +- session_id: uuid (required, fk) +- chat_id: uuid (required, fk) +- message_id: uuid (fk) +- turn_number: integer (required) +- tool_name: text (required) +- tool_input: jsonb (required) +- tool_output: text +- started_at: timestamp(tz) (required) +- finished_at: timestamp(tz) +- latency_ms: integer +- tokens_used: integer +- cache_tokens: integer +- reasoning_tokens: integer +- error: text +- outcome: text + +### tool_trace_states +- id: uuid (pk) +- session_id: uuid (required, fk) +- chat_id: uuid (required, fk) +- message_id: uuid (fk) +- turn_number: integer (required) +- tool_name: text (required) +- tool_input: jsonb (required) +- started_at: timestamp(tz) (required) + +### agent_snapshots +- id: uuid (pk) +- session_id: uuid (required, fk) +- chat_id: uuid (required, fk) +- model: text (required) +- agent: text +- mode: text +- turn_number: integer (required) +- messages: jsonb (required) +- tool_states: jsonb (required) diff --git a/.gitignore b/.gitignore index 9d45b0a..e529694 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,13 @@ data/* !data/coder-providers.example.json codecontext/fork.tar.gz /Arena + +# Auto-generated & scratch artifacts +.impeccable/ +.omo/ +bun.lock +DESIGN.md +PRODUCT.md + +# codesight auto-generated analysis cache +apps/web/.codesight/ diff --git a/docs/boocode-lift-analysis.md b/docs/boocode-lift-analysis.md new file mode 100644 index 0000000..2293e30 --- /dev/null +++ b/docs/boocode-lift-analysis.md @@ -0,0 +1,1063 @@ +# BooCode Lift Analysis — What Can We Lift Into BooCode? + +Date: 2026-06-07 +Scope: 30+ repos scanned across 2 passes. + Pass 1 (14 repos): opencode, agentic, hcom, oh-my-openagent, oh-my-pi, OpenAgentsControl, + Codex-CLI-Compact, conductor, measuring-ai-proficiency, boocontext, opencode-pty, + pskoett-skills, paseo, additional-forks (openspec, codesight, tree-sitter-analyzer, etc.) + Pass 2 (16 repos): bun, claudish, cline, goose, han, happy, openchamber, llama.cpp, unsloth, + SkillOpt, subtask2, superset, pi-acp, qwen-code, opencode-background-agents, + opencode-sentry-monitor, opencode-skillful, opencode-wakatime, opencode-worktree, + opencode-zellij-namer + +--- + +## BooCode Architecture (for context mapping) + +``` +apps/server — Fastify API + inference loop + file-read tools + MCP client + codecontext client +apps/web — React 18 + Vite + Tailwind v4 + shadcn/ui +apps/booterm — Fastify + node-pty + tmux (browser terminal panes) +apps/coder — BooCoder: write tools + ACP/PTY dispatcher + provider snapshot + Arena + MCP server +packages/contracts — Cross-app wire contracts (WsFrame, provider-snapshot, message-metadata) +codecontext/ — Go sidecar (codegraph MCP server, v3.2.1) +conductor/ — 22 read-only Han flows via local Qwen +data/AGENTS.md — 8 agent personas (Code Reviewer, Debugger, Refactorer, etc.) +data/skills/ — Skills system (SKILL.md + optional eval.yaml) +orchestrator — Multi-agent conductor (Paseo-style run pane, evidence-disciplined reports) +``` + +--- + +## LIFT MATRIX — Capability Domain × Source Repo × BooCode Target + +### Domain 1: Multi-Provider Model Routing & LLM Abstraction + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| Route/protocol abstraction layer (4-axis: route=protocol+endpoint+auth+framing) | **opencode** `packages/llm/` → `Route.make({provider, protocol, endpoint, auth, framing})` | apps/server inference loop → replace hardcoded provider dispatch | Large | High | +| 40+ provider plugins with model discovery + fallback chains | **oh-my-pi** `packages/ai/` | apps/coder provider-registry.ts (currently 5 providers) | Med | High | +| Provider priority chains, capability detection, availability checking, model normalization | **oh-my-openagent** `packages/model-core/` → pure TS, no harness deps | apps/coder provider-registry.ts → abstract model resolution | Small | High | +| Config-backed provider lifecycle with overrides | **opencode** `packages/core/src/plugin/provider/` | apps/coder provider-registry.ts + data/coder-providers.json | Med | Med | + +### Domain 2: Codebase Context & Code Intelligence + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| Replace Go codecontext sidecar with TS boocontext (7 tools, session cache, wiki) | **boocontext** (this fork `boocontext/`) | codecontext/ sidecar → replace HTTP shim with MCP client | Med | Critical | +| Call graph analysis (callers/callees with depth) | **tree-sitter-analyzer** via boocontext | apps/server codecontext_client.ts → new tool | Med | High | +| Health grades (A-F per file, 7 dimensions) | **tree-sitter-analyzer** via boocontext | apps/server → new tool for Refactorer/Architect agents | Med | High | +| BM25-ranked symbol search | **tree-sitter-analyzer** via boocontext | codecontext_client.ts → replace search_symbols | Med | High | +| Impact analysis (symbol trace + file blast radius merged) | **boocontext** `src/tools/impact.ts` | apps/server → new tool for change planning | Med | High | +| Wiki mode (targeted articles: auth.md, database.md, etc.) | **codesight** `--wiki` mode | apps/server → generate persistent codebase wiki | Med | Med | +| Semantic codebase graph (info_graph + chat_action_graph) | **Codex-CLI-Compact** `bin/dual_graph_launch.sh` (proprietary core) | apps/server → build own dual-graph system | Large | High | +| Token-efficient scanning (7-12x reduction vs manual exploration) | **codesight** | codecontext/ → adopt codesight's scanner | Small | High | + +### Domain 3: Multi-Agent Orchestration + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| Declarative workflow definitions (JSON DAGs) | **conductor** `schemas/WorkflowDef.json` | conductor/ orchestrator → flow format | Med | High | +| Task state machine (SCHEDULED → IN_PROGRESS → COMPLETED/FAILED/TIMED_OUT with retriable flag) | **conductor** `TaskModel.java` | conductor/ flows → step state model | Small | High | +| DO_WHILE loop for agent iteration | **conductor** `DoWhile.java` | conductor/ → loop support | Med | High | +| FORK_JOIN parallel execution with Join synchronization | **conductor** `Fork.java` + `Join.java` | conductor/ → parallel step execution | Med | High | +| SWITCH task for conditional branching | **conductor** `Switch.java` | conductor/ → conditional flow routes | Small | Med | +| Parallel execution with batch batching (independent tasks run concurrently) | **OpenAgentsControl** `.opencode/agent/subagents/core/task-manager.md` | conductor/ → parallel batch execution | Small | High | +| Subagent tool restriction schema (YAML frontmatter: read/grep/glob/bash bools) | **agentic** `agent/*.md` frontmatter | data/AGENTS.md + conductor/agents → tool permissions per agent | Small | High | +| Structured phased workflow lifecycle (ticket → research → plan → execute → commit → review) | **agentic** `command/ticket.md`, `command/research.md`, etc. | conductor/ or new workflow | Med | Med | +| Multi-agent orchestration hub daemon | **paseo** (this fork `paseo/`) | apps/coder → multi-agent lifecycle management | Large | Med | + +### Domain 4: Inter-Agent Communication & Coordination + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| Inter-agent messaging via shared DB + delivery | **hcom** `src/delivery.rs` + `src/db.rs` | apps/server → message queue between Orchestrator + BooCoder | Large | High | +| Per-tool hook normalization (10 tools: Claude, Codex, Gemini, etc.) | **hcom** `src/hooks/` | apps/coder → normalized hook payload format | Med | Med | +| Event subscription system (agents subscribe to status/file-edit events) | **hcom** `src/commands/` (`hcom events --wait --sql`) | apps/server → reactive event triggers | Med | High | +| Collision detection (two agents editing same file within 30s) | **hcom** `src/` (codex_file_edits.rs) | apps/coder → concurrent edit warnings | Small | Med | +| Multi-agent topologies (worker-reviewer, ensemble, cascade pipeline) | **hcom** `docs/` 6 documented patterns | conductor/ → expand beyond read-only Qwen flows | Large | High | +| Cross-device relay (MQTT with E2E encryption) | **hcom** `src/relay/` | net-new network layer | Large | Low | + +### Domain 5: PTY / Terminal + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| Exit notifications (`` with exit code, last line) | **opencode-pty** (this fork) | apps/booterm → notify AI on process completion | Small | High | +| Pattern-based log search with offset/limit pagination | **opencode-pty** `pty_read` with regex pattern | apps/booterm → grep within PTY buffer | Small | High | +| Per-session timeouts | **opencode-pty** `timeoutSeconds` parameter | apps/booterm → runaway process protection | Small | Med | +| Rich session metadata (title, description, parent agent) | **opencode-pty** PTYSession metadata | apps/booterm → AI context about background tasks | Small | Med | +| In-process native PTY (Rust bun-pty vs node-pty+tmux) | **oh-my-pi** `crates/pi-shell/` (embedded bash) | apps/booterm → alternative PTY backend | Large | Med | + +### Domain 6: Code Editing & Execution + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| Hashline editing (content hash anchors, hash mismatch = reject) | **oh-my-openagent** `packages/hashline-core/` | apps/coder edit_file → line anchors with content hashes | Small | High | +| AST-based structural editing (ast_edit with preview) | **oh-my-pi** `packages/coding-agent/` tools | apps/coder → structural rewrite tool | Med | High | +| ast-grep pattern matching across 50+ language grammars | **oh-my-pi** `crates/pi-ast/` | apps/coder → ast_grep_search/replace tools | Med | High | +| Built-in eval kernels (persistent Python + JS cells) | **oh-my-pi** `packages/coding-agent/` eval tool | apps/coder → code execution sandbox | Large | Med | +| Fuzzy edit matching (exact → whitespace → unicode → Levenshtein ladder) | Already in BooCode (`apps/coder/src/fuzzy-match.ts`) | — (already shipped) | — | — | + +### Domain 7: AI Proficiency & Metrics + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| MCP server with 13 proficiency scanning tools | **measuring-ai-proficiency** `measure-ai-proficiency-mcp` CLI | apps/server MCP client → register as MCP server | Small | Med | +| Maturity level (1-8) scoring + recommendations | **measuring-ai-proficiency** `scanner.py` | apps/server → agent "skill level" awareness | Small | Med | +| 2026 context-engineering signals (verification, hooks, primitive discipline, eval loops) | **measuring-ai-proficiency** `signals.py` | apps/server → track AI readiness | Small | Med | +| Validation warnings (STALE, TEMPLATE, BLOAT, MISSING REF) | **measuring-ai-proficiency** `scanner.py` validation | apps/server → alert on broken context files | Small | Low | +| Cross-session context store (decisions, tasks, blockers persist between sessions) | **Codex-CLI-Compact** `context-store.json` pattern | apps/server → project memory | Med | High | +| Token tracking dashboard (per-model, per-project costs) | **Codex-CLI-Compact** `dashboard/server.py` | apps/web → token usage UI | Med | Med | + +### Domain 8: Context Engineering & Skills + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| Two-loop continuous improvement (inner: detect→verify→recover, outer: inspect→encode→regress-test) | **pskoett-skills** — 19 skills | data/skills/ → architecture pattern | Med | High | +| Self-healing with mandatory verify-before-persist | **pskoett-skills** `skills/self-healing/` | data/skills/ → add healing skill | Small | High | +| Verify-gate (compile → test → lint, fix loop on failure) | **pskoett-skills** `skills/verify-gate/` | apps/server/conductor → add verify step | Small | High | +| Pre-flight check (session-start learning visibility) | **pskoett-skills** `skills/pre-flight-check/` | apps/server → session initiation context | Small | Med | +| Context system with project-specific patterns (markdown files agents load proactively) | **OpenAgentsControl** `.opencode/context/` | data/skills/ or new context/ dir | Small | High | +| ContextScout (discover relevant context files) + ExternalScout (live docs via Context7) | **OpenAgentsControl** `.opencode/agent/subagents/core/` | apps/server → dual discovery | Med | High | +| Plugin hook system (chat params, tool execution, auth, provider, shell env) | **opencode** `packages/plugin/` | apps/server → extensible hook architecture | Large | Med | +| Skills with embedded MCPs (per-session lifecycle, spun up on demand) | **oh-my-openagent** `src/features/skill-mcp-manager/` | data/skills/ → MCP-per-skill | Med | High | +| Skill pipeline (task classifier: Trivial/Small/Medium/Large/Long-running/Batch) | **pskoett-skills** `skills/skill-pipeline/` | conductor → depth-calibrated routing | Med | Med | + +### Domain 9: Memory & Session Continuity + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| System Context / Context Epoch model (typed context sources, immutable baselines, snapshots) | **opencode** `packages/core/src/system-context/` | apps/server inference → context lifecycle | Large | High | +| Persistent cross-session memory (Supermemory: project habits, conventions) | **opencode-supermemory** (this fork) | apps/server → long-term agent memory | Med | Med | +| Hindsight memory (agent-curated between sessions) | **oh-my-pi** `packages/agent/` recall/retain tools | apps/server → memory persistence | Med | Med | +| Boulder state (work tracking across sessions with automatic resumption) | **oh-my-openagent** `packages/boulder-state/` | conductor → plan execution persistence | Small | Med | +| Context compression (range/message dedup, purge errors, turn protection) | **opencode-dynamic-context-pruning** (this fork) | apps/server inference → compaction | Med | High | +| Morph Fast Apply (10,500+ tok/s with lazy markers) | **opencode-morph-plugin**, **opencode-morph-fast-apply** | apps/coder edit_file → fast apply backend | Med | Med | + +### Domain 10: Development Workflow + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| Spec-first development (spec.md → tasks → implementation) | **openspec** (this fork `openspec/`) | Already partially in BooCode (`openspec/` directory) | — | — | +| Context-driven dev lifecycle (Context → Spec → Plan → Implement → Revert tracks) | **opencode-conductor** (this fork) | conductor → structured dev tracks | Med | Med | +| Undo Shield (PreToolUse hook blocking destructive ops on high-attention files) | **Codex-CLI-Compact** `bin/undo_shield.py` | apps/coder → edit safety guard | Small | Med | +| Vibe Code Auditor (7 health checks: dead exports, coverage, circular deps, etc.) | **Codex-CLI-Compact** `audit.py` | apps/server → technical debt awareness | Med | Med | + +--- + +## RANKED INTEGRATION RECOMMENDATIONS + +Ordered by **value/cost ratio** for BooCode. + +### Tier 1: High Value, Low Effort (Do First) + +| # | What | Source | BooCode Target | Why | +|---|------|--------|----------------|-----| +| 1 | **Replace Go codecontext with boocontext** | `boocontext/` | codecontext/ sidecar | Direct drop-in. 7 tools instead of 5. Session caching. Call graph, health grades, impact analysis. Written in TS (BooCode's stack). Fixes 0% TypeScript recovery. | +| 2 | **PTY exit notifications** | `opencode-pty/` | apps/booterm | `` structured messages on process completion. Low-effort, enables AI to react to long-running tasks. | +| 3 | **Self-healing skill** | `pskoett-skills/` | data/skills/ | Verify-before-persist discipline. HEAL entries to `.learnings/`. Pattern-Key dedup. Brings the two-loop inner loop to BooCode. | +| 4 | **Verify-gate skill** | `pskoett-skills/` | conductor/ | Compile → test → lint gate with auto-discovery. Fix loop on failure. Universal value for a dev chat app. | +| 5 | **Subagent tool restriction schema** | `agentic/` | data/AGENTS.md + conductor/agents | YAML frontmatter defining tool permissions per agent. Currently all BooCode agents have same tool set. | +| 6 | **Hashline editing** | `oh-my-openagent/` | apps/coder | Content hash anchors for edit targets. Rejects stale patches. Pure TS, no harness deps. Proven 10× success rate improvement. | +| 7 | **Model resolution core** | `oh-my-openagent/` `packages/model-core/` | apps/coder | Provider priority chains, fallback chains, model normalization. Pure TS, no deps. | +| 8 | **Fuzzy edit matching** | Already in BooCode | apps/coder | Already shipped in v2.7.1. Listed for completeness. | + +### Tier 2: High Value, Medium Effort (Next) + +| # | What | Source | BooCode Target | Why | +|---|------|--------|----------------|-----| +| 9 | **Context system (project patterns)** | `OpenAgentsControl/` | data/ or new context/ | Learn project patterns proactively instead of relying on agent discovery. MVI principle (<200 line files, 80% token reduction). | +| 10 | **ContextScout + ExternalScout** | `OpenAgentsControl/` | apps/server | Discover relevant context files + fetch live library docs via Context7. | +| 11 | **Task state machine + FORK_JOIN** | `conductor/` | conductor/ | SCHEDULED → IN_PROGRESS → COMPLETED/FAILED state model. Parallel step execution with Join sync. | +| 12 | **Session metadata for PTY panes** | `opencode-pty/` | apps/booterm | Track title, description, parent agent per terminal pane. Enables AI context awareness. | +| 13 | **Context compression (DCP)** | `opencode-dynamic-context-pruning/` | apps/server inference | Range/message dedup, purge errors. More surgical than current compaction. | +| 14 | **Boulder state (work tracking)** | `oh-my-openagent/` `packages/boulder-state/` | conductor/ | Cross-session plan execution with automatic resumption. Framework-agnostic. | +| 15 | **AST-based editing (ast_grep + ast_edit)** | `oh-my-pi/` `crates/pi-ast/` | apps/coder | Structural code editing with AST patterns. 50+ language grammars. | +| 16 | **Event subscription system** | `hcom/` `src/commands/` | apps/server | Reactive event triggers ("when file X changes, re-run flow Y"). | + +### Tier 3: High Value, Large Effort (Architecture Projects) + +| # | What | Source | BooCode Target | Why | +|---|------|--------|----------------|-----| +| 17 | **Declarative workflow DAGs** | `conductor/` | conductor/ | Replace procedural flow definitions with JSON DAGs. LLM-friendly. Versioned. | +| 18 | **Inter-agent messaging** | `hcom/` | apps/server + apps/coder | DB-backed message queue for Orchestrator → BooCoder messaging. | +| 19 | **System Context model** | `opencode/` | apps/server | Typed context sources, immutable epoch baselines, reconciliation. Foundation for long-session quality. | +| 20 | **Multi-provider routing abstraction** | `opencode/` `packages/llm/` | apps/server | 4-axis route composition. Decouples provider selection from inference loop. | +| 21 | **Plugin hook system** | `opencode/` `packages/plugin/` | apps/server | Extensible lifecycle hooks (chat params, tool execution, auth, provider). | +| 22 | **Multi-agent topologies** | `hcom/` | conductor/ | Expand beyond read-only Qwen flows to general multi-agent patterns. | +| 23 | **Skills with embedded MCPs** | `oh-my-openagent/` | data/skills/ | Per-skill MCP servers, spun up on demand, scoped per session. | + +--- + +## BOOCODE'S CURRENT STATE — What's Already Strong + +Before lifting, acknowledge what BooCode already does well that the forks don't: + +| BooCode Strength | Not matched by | +|-----------------|----------------| +| Postgres-backed session storage (queryable, indexed) | hcom (SQLite), opencode (JSONL) | +| Multi-pane workspace (chat/coder/terminal/orchestrator) | All forks — single-pane or terminal-only | +| Provider snapshot system (probe status, UI toggle, cold refresh) | opencode (no status UI), oh-my-pi (no toggle) | +| Pending-changes queue + diff panel (approval gating) | None of the forks have this UX | +| Arena (same task → N models → analysis) | Unique to BooCode | +| Fuzzy edit matching (Levenshtein ladder) | opencode (exact only), oh-my-openagent (hashline only) | +| Persistent agent sessions (warm ACP resumable) | hcom (per-session only), opencode (cold per turn) | +| Codecontext sidecar (Go, containerized) | boocontext (TS, but no container) | + +--- + +## DEPENDENCY GRAPH — What Depends On What + +``` +Replace codecontext with boocontext (Tier 1) + ├── Enables: Call graph → Impact analysis → Health grades + ├── Enables: Wiki generation → Cross-session codebase knowledge + └── Needs: tree-sitter-analyzer as child MCP server + +PTY exit notifications (Tier 1) + └── Enables: AI reaction to long-running task completion + +Self-healing + Verify-gate (Tier 1) + ├── Prerequisite: .learnings/ directory convention + └── Enables: Full two-loop CI (with learning-aggregator → eval-creator) + +Context system + ContextScout (Tier 2) + ├── Prerequisite: MVI context file format + └── Enables: ExternalScout → live doc fetching + +Conductor flow enhancements (Tier 2) + ├── Task state machine → Prerequisite for: DO_WHILE, FORK_JOIN, SWITCH + └── Enables: Declarative DAGs (Tier 3) + +System Context model (Tier 3) + └── Prerequisite for: Plugin hook system → MCP-per-skill +``` + +--- + +## IMMEDIATE NEXT ACTIONS (If we start today) + +### Week 1: Quick wins +1. **Swap codecontext** — Replace Go sidecar HTTP client with boocontext MCP client. Adds 2 new tools (health, callgraph) with zero architectural change. +2. **Add PTY exit notifications** — Extend booterm's WS protocol to send structured exit payloads. +3. **Add self-healing + verify-gate skills** — Copy skill directories from pskoett-skills, adapt prompts to BooCode's agent format. + +### Week 2: Context system +4. **Add ContextScout** — New agent that discovers relevant context files (from new `context/` directory). +5. **Add model-core** — Extract provider priority/fallback logic from oh-my-openagent's model-core package. + +### Week 2-3: Conductor upgrades +6. **Task state machine** — Add SCHEDULED/IN_PROGRESS/FAILED/COMPLETED states to flow steps. +7. **FORK_JOIN support** — Allow parallel branch execution in conductor flows. + +### Week 3-4: Memory & context +8. **Boulder state** — Add cross-session work tracking/auto-resumption. +9. **Context compression** — Integrate DCP patterns into inference loop compaction. + +--- + +## COMPLETE REPO REFERENCE + +| Repo | Path | Key Capability | Lift Priority | +|------|------|---------------|---------------| +| boocontext | `/opt/forks/boocontext/` | Code intelligence MCP (7 tools, health, callgraph, impact) | **Tier 1** | +| opencode-pty | `/opt/forks/opencode-pty/` | PTY exit notifications, log search, session metadata | **Tier 1** | +| pskoett-skills | `/opt/forks/pskoett-skills/` | Self-healing, verify-gate, two-loop skills | **Tier 1** | +| agentic | `/opt/forks/agentic/` | Subagent tool permissions, structured workflow lifecycle | **Tier 1** | +| oh-my-openagent | `/opt/forks/oh-my-openagent/` | Hashline editing, model-core, boulder state, skills+MCP | **Tier 1-2** | +| oh-my-pi | `/opt/forks/oh-my-pi/` | 40+ providers, AST editing, eval kernels, hindsight memory | **Tier 2** | +| OpenAgentsControl | `/opt/forks/OpenAgentsControl/` | Context system, ContextScout, ExternalScout, task manager | **Tier 2** | +| conductor | `/opt/forks/conductor/` | Workflow DAGs, task state machine, FORK_JOIN, DO_WHILE | **Tier 2** | +| opencode | `/opt/forks/opencode/` | System Context model, plugin hooks, LLM routing, MCP | **Tier 3** | +| hcom | `/opt/forks/hcom/` | Inter-agent messaging, event system, multi-agent topologies | **Tier 3** | +| opencode-dynamic-context-pruning | `/opt/forks/opencode-dynamic-context-pruning/` | Context compression | Tier 2 | +| opencode-supermemory | `/opt/forks/opencode-supermemory/` | Persistent memory | Tier 2 | +| Codex-CLI-Compact | `/opt/forks/Codex-CLI-Compact/` | Context store, undo shield, token dashboard, vibe audit | Tier 2 | +| measuring-ai-proficiency | `/opt/forks/measuring-ai-proficiency/` | AI proficiency MCP, maturity scoring | Tier 2 | +| opencode-morph-plugin | `/opt/forks/opencode-morph-plugin/` | Fast code editing, WarpGrep | Tier 2 | +| opencode-conductor | `/opt/forks/opencode-conductor/` | Context-driven dev lifecycle | Tier 3 | +| paseo | `/opt/forks/paseo/` | Multi-agent orchestration hub | Tier 3 | +| opencode-tokenscope | `/opt/forks/opencode-tokenscope/` | Token usage analysis | Low | +| opencode-skillful | `/opt/forks/opencode-skillful/` | Skill system (SKILL.md, lazy load, model-aware formatting) | **Tier 1** | +| llama.cpp | `/opt/forks/llama.cpp/` | LLM inference engine (BooCode's inference backend) | **Tier 1** | +| claudish | `/opt/forks/claudish/` | Claude Code multi-provider proxy (580+ models) | Tier 2 | +| cline | `/opt/forks/cline/` | Full agent platform (SDK, hub, connectors, cron) | Tier 2 | +| goose | `/opt/forks/goose/` | Block's Rust agent (MCP, 15+ providers, desktop+CLI+API) | Tier 2 | +| han | `/opt/forks/han/` | 18 skills + 23 agents (evidence-based planning, review, TDD) | Tier 2 | +| openchamber | `/opt/forks/openchamber/` | OpenCode UI runtime (Electron, web, branching timeline) | Tier 2 | +| qwen-code | `/opt/forks/qwen-code/` | Full coding agent (daemon mode, skills, hooks, subagents, cron) | Tier 2 | +| subtask2 | `/opt/forks/subtask2/` | Enhanced subtask control (return, loop, parallel, $RESULT) | Tier 2 | +| opencode-background-agents | `/opt/forks/opencode-background-agents/` | Async background delegation with persistence | Tier 2 | +| pi-acp | `/opt/forks/pi-acp/` | ACP adapter for pi (structured diffs, terminal auth) | Tier 2 | +| opencode-worktree | `/opt/forks/opencode-worktree/` | Git worktree + 15-terminal spawning + lifecycle hooks | Tier 2 | +| bun | `/opt/forks/bun/` | JS/TS runtime, bundler, test runner, bun:sqlite, Bun.serve | Tier 3 | +| happy | `/opt/forks/happy/` | Mobile remote control (E2EE, QR auth, voice) | Tier 3 | +| superset | `/opt/forks/superset/` | "Code Editor for AI Agents" - Electron desktop app | Tier 3 | +| unsloth | `/opt/forks/unsloth/` | Efficient LLM fine-tuning (2-5x faster, 70% less VRAM) | Tier 3 | +| SkillOpt | `/opt/forks/SkillOpt/` | Skill document optimization training loop | Tier 3 | +| opencode-sentry-monitor | `/opt/forks/opencode-sentry-monitor/` | Sentry observability (token usage, spans, metrics) | Tier 3 | +| opencode-wakatime | `/opt/forks/opencode-wakatime/` | WakaTime coding activity tracking | Tier 3 | +| opencode-zellij-namer | `/opt/forks/opencode-zellij-namer/` | AI-powered terminal session naming | Tier 3 | + +--- + +## DEEP DIVE FINDINGS — 5 Audit Agents (2026-06-07) + +This section captures the results of deep-dive audits across 5 focus areas: llama.cpp server features, boocontext architecture, conductor/agents/workflows, hooks/skills/prompt library, and inference configuration. + +--- + +### 1. llama.cpp Server: 14 Feature Categories Not Used + +BooCode uses llama-server via llama-swap (default) or llama-sidecar (per-agent flags). The sidecar base args are: +`-ngl 999 -c 32768 --flash-attn on --no-mmap`. Here's what's available but not enabled: + +**High Impact (config only, no code changes):** + +| Feature | Flag | Impact | Activation | +|---------|------|--------|------------| +| KV cache quantization | `--cache-type-k q4_0` | ~4× VRAM savings for KV cache | Sidecar `baseArgs` or unshadow in validator | +| Ngram speculative decoding | `--spec-type ngram-mod` | 2-3× tokens/sec, ~16MB memory, no draft model | Sidecar `baseArgs` or unshadow in validator | +| Context checkpointing | `--ctx-checkpoints 32` | Prevents overflow on long conversations | Via `llama_extra_args` in AGENTS.md | +| Context shift | `--context-shift` | Infinite text generation | Via `llama_extra_args` | +| Slot save/restore | `POST /slots/{id}?action=save\|restore` | Persist KV cache across restarts | New API call in compaction.ts | +| Prometheus metrics | `--metrics` | `/metrics` for token rates, latency, decode stats | Add to sidecar base args | +| Sleep mode | `--sleep-idle-seconds 600` | Auto-shutdown when idle, saves GPU memory | Add to sidecar base args | + +**Key Finding — Shadowing Architecture** (both `llama-args-validator.ts` and sidecar `validator.go`): +- `denyFlags`: model, listen, credentials, webui (hard blocked) +- `shadowFlags`: context size, cache types, spec flags, template flags (auto-stripped from agent args) +- `acceptFlags`: everything else passes through + +To enable cache/spec features: remove from `shadowFlags` in BOTH files, or override via `BASE_ARGS` env var on the sidecar. + +**Medium Impact (via `llama_extra_args`):** +- LoRA adapters (`--lora `) +- Custom chat templates (`--chat-template-file`) +- Reasoning budget (`--reasoning-budget`) +- Multi-GPU tensor split (`--tensor-split`) +- NUMA awareness (`--numa`) + +**Full API surface available at the inference endpoint:** +`/v1/chat/completions` (main), `/v1/completions`, `/v1/embeddings`, `/v1/rerank`, `/v1/messages` (Anthropic-compatible), `/infill`, `/tokenize`, `/detokenize`, `/v1/audio/transcriptions` + +--- + +### 2. Boocontext Architecture: 20 Tools in 2 Layers + +**Integration target** (`codecontext_client.ts` does not yet exist — must be created). + +**Option A (Recommended): MCP subprocess — zero code change:** +```json +{ "mcpServers": { "boocontext": { "command": "npx", "args": ["boocontext-mcp"] } } } +``` + +**Architecture:** +- **13 codesight tools**: scan, get_summary, get_routes, get_schema, get_blast_radius, get_env, get_hot_files, refresh, get_wiki_index, get_wiki_article, lint_wiki, get_events, get_coverage, get_knowledge +- **7 boocontext tools**: overview, map, health (A-F grades), symbols (BM25 search), callgraph (callers/callees), impact (trace + blast radius merged), types (TS type recovery) +- **VerdictEnvelope**: SAFE/CAUTION/UNSAFE/INFO wrapper → `{ verdict, summary, details, metadata }` +- **Child MCP servers**: tree-sitter-analyzer (via `uvx`) + type-inject (via `npx`) +- **Session cache**: in-memory, per-directory, `clearCache()` on project switch + +**Fixes needed before production integration:** +1. Hardcoded type-inject path (`/opt/forks/type-inject/...`) → switch to `npx @nick-vi/type-inject-mcp` +2. Missing `uvx` requirement for tree-sitter-analyzer → document or bundle +3. No request concurrency (serial `processing` flag) → enable parallel for independent tools +4. No graceful degradation on child server failure → retry + fallback to codesight-only +5. No tool result caching per-call → add memoization for expensive operations (health, callgraph) + +--- + +### 3. Conductor/Workflows/Agents: Static Wave Scheduler + +**BooCode has TWO layers:** +- `conductor/` — standalone CLI, deterministic wave scheduler, 22 flows, 23 agents +- `apps/coder/` — in-app orchestrator, DB-persisted flow runs, dispatcher with 4 execution paths +- `data/AGENTS.md` — 9 personas (Code Reviewer, Debugger, Refactorer, etc.) +- `conductor/agents/` — 23 Han personas with frontmatter + tool permissions + +**Execution model** (`flow.ts`): +``` +while (done.size + skipped.size < total): + ready = steps whose deps all satisfied + await Promise.all(ready.map(s => s.run())) +``` + +**Gaps to Claude Code's Dynamic Workflows/Sub-agents/Teams:** + +| Capability | Current | Target | Structural Change Needed | +|------------|---------|--------|------------------------| +| Step graph | Static `Step[]` array | Mutable `FlowGraph` with add/remove/loop | `flow.ts` — Step[] → FlowGraph with WhileStep/UntilStep | +| Result streaming | `Promise` | `AsyncIterable` with partial results | Step.run() signature change | +| Agent spawning | Conductor calls dispatchAgent | Steps call `ctx.spawn(agent, prompt)` | New spawn protocol | +| Shared state | `Record` | Structured SharedState with message-passing channels | ctx.results → SharedState | +| Inter-agent comms | None | Mailbox pattern (send/receive by agent ID) | New message transport | +| Human-in-loop | None | Pause-for-input mid-flow | Flow runner → user prompt gate | +| Workflow composition | None | `flow.invoke(subflow)` | New compose mechanism | + +**Dispatcher** (`dispatcher.ts`, 1726 lines): 4 execution paths (Native, PTY, ACP, OpenCode Server). Polls `tasks` table, LISTEN `tasks_new`. Per-session in-flight tracking prevents concurrent turns. + +**Worktree isolation**: Per-task (`/tmp/booworktrees/`) and session-persistent (`/tmp/booworktrees/sess-`). Diff baseline from `base_commit`. + +**Agent pool** (`agent-pool.ts`): Idle-TTL (30min default) + LRU cap (10 default). Busy backends never evicted. Periodic sweep. + +--- + +### 4. Hooks System: 5 Events Defined, 0 Wired + +**Location**: `apps/coder/src/plugins/host.ts` + +**State**: Shipped dead code. The hook infrastructure exists (handler registry, stdin/stdout protocol, exit codes for allow/deny) but NO `emitHook()` calls exist in `turn.ts`, `tool-phase.ts`, or `stream-phase.ts`. + +**The 5 dead events**: `tool.execute.before`, `tool.execute.after`, `turn.start`, `turn.end`, `task.terminal` + +**What BooCode DOES have (that's active):** +- `broker.ts` — in-memory pub/sub for WS frames (session + user channels) +- `lifecycle.ts` — chat/session close/archive/delete handlers +- `flow-runner.ts` — `handleTaskTerminal` callback (the one wired hook) + +**Claude Code's hook architecture (target — 32+ events):** + +| Category | Events | +|----------|--------| +| Session | SessionStart, SessionEnd, Setup | +| Turn | UserPromptSubmit, Stop, StopFailure, PreCompact, PostCompact | +| Tool | PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch | +| Permission | PermissionRequest, PermissionDenied | +| Agent | SubagentStart, SubagentStop, TeammateIdle | +| File | FileChanged, CwdChanged, WorktreeCreate, WorktreeRemove | +| Config | ConfigChange, InstructionsLoaded | +| MCP | Elicitation, ElicitationResult | +| Notify | Notification, MessageDisplay, TaskCreated, TaskCompleted | + +**BooCode inference lifecycle points** (from `turn.ts` `runAssistantTurn`): +1. loadContext — session/project loaded +2. Doom-loop check +3. Budget check +4. Compaction check +5. Message payload build +6. executeStreamPhase — LLM streaming +7. executeToolPhase — tool execution +8. MistakeTracker integration +9. Step cap summary on exit + +These are natural hook insertion points. + +--- + +### 5. Prompt Library: None + +**Current state**: No central prompt library. Skills contain prompts (41 files in `data/skills/`) but not organized as a user-facing library. `openspec/` has per-batch docs. `data/AGENTS.md` has system prompts. + +**Target**: 20-30 prompts organized by SDLC phase (discover, design, build, ship, operate), filterable by role, with fillable slots and "Why this works" explanations. Markdown files with YAML frontmatter in `data/prompts/`. + +--- + +### 6. Inference Configuration: Two Paths, Shadowing Architecture + +**Default path**: BooCode → `LLAMA_SWAP_URL` → llama-swap → llama-server +**Per-agent path**: BooCode → `LLAMA_SIDECAR_URL` → llama-sidecar (proxy) → per-model llama-server process + +**Sidecar** spawns per-agent llama-server processes via LRU pool (max 2, ports 8500-8599). Configuration env vars: +``` +LLAMA_SERVER_BIN, MODEL_DIR_MAP_FILE, LLAMA_SIDECAR_BIND (127.0.0.1:8402), +MAX_SIDECARS (2), PORT_RANGE (8500-8599), +BASE_ARGS (["-ngl","999","-c","32768","--flash-attn","on","--no-mmap"]), +HEALTH_TIMEOUT_SECONDS (60), HEALTH_INTERVAL_SECONDS (30) +``` + +**AI SDK integration**: `@ai-sdk/openai-compatible` wrapping `createOpenAICompatible({ baseURL: LLAMA_SWAP_URL/v1 })`. Sampler options passed via `providerOptions.openaiCompatible` extraBody (top_k, min_p, top_n_sigma, dry_* family). + +**Model context discovery**: `GET /upstream//props` → `n_ctx` from `default_generation_settings`. Cached forever on success, 60s on failure. + +**Task model**: Non-streaming `POST ${TASK_MODEL_URL}/v1/chat/completions` with `FAST_MODEL` model name. Currently hardcoded to `gemma-3-270m-it`. + +**Key env vars**: `LLAMA_SWAP_URL`, `LLAMA_SIDECAR_URL`, `TASK_MODEL_URL`, `FAST_MODEL`, `DEFAULT_MODEL` + +--- +--- + +## PASS 3 — EXTENSION: Repos Missed in Initial Analysis (2026-06-07, evening) + +After the original lift analysis was written, a second pass identified additional repos in `/forks` that were not covered. This section documents the findings from analyzing those repos for patterns liftable into BooCode. + +--- + +### Domain 11: Claude Code Plugin Architecture (Official) + +**Source**: `claude-code/plugins/` — 14 official Anthropic plugins + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Plugin packaging structure** (`.claude-plugin/plugin.json` + commands/agents/skills/hooks/.mcp.json — standard layout) | `claude-code/plugins/` — `plugin-dev/` | `data/plugins/` or enhance `data/skills/` | Small | High | +| **Parallel PR review agents** (5 agents: bug-finder, comment-analyzer, test-analyzer, type-designer, code-simplifier — parallel execution) | `claude-code/plugins/pr-review-toolkit/` | `conductor/` — PR review workflow | Med | High | +| **Feature dev workflow agents** (3 agents: code-explorer, code-architect, code-reviewer — 7-phase structured process) | `claude-code/plugins/feature-dev/` | `conductor/` — feature development flows | Med | High | +| **Self-referential agent loop** (Ralph Loop: agent runs same task repeatedly until completion, Stop hook intercepts exit) | `claude-code/plugins/ralph-wiggum/` | `apps/coder` → `/ralph-loop` command | Small | High | +| **Security guidance hook** (PreToolUse scanning 9 security patterns: command injection, XSS, eval, pickle deserialization, os.system, etc.) | `claude-code/plugins/security-guidance/` | `apps/coder` → PreToolUse security scan | Small | High | +| **Hookify rule system** (analyze conversations → auto-create hook rules to prevent unwanted behaviors) | `claude-code/plugins/hookify/` | `apps/coder` → behavior prevention rules | Med | High | +| **Agent SDK verifier plugins** (Python + TS SDK validation agents) | `claude-code/plugins/agent-sdk-dev/` | `apps/coder` → SDK compliance checking | Med | Med | +| **Frontend design skill** (production-grade UI design guidance, auto-invoked) | `claude-code/plugins/frontend-design/` | `data/skills/` → frontend design skill | Small | Med | +| **PR toolkit type design analyzer** (specialized agent for API design review) | `claude-code/plugins/pr-review-toolkit/agents/` | `conductor/agents/` → type review agent | Small | Med | +| **Agent tool permission schemas** (YAML frontmatter in agent `.md` files with `tools:` restrict list) | `claude-code/plugins/feature-dev/agents/*.md` frontmatter | `data/AGENTS.md` → tool restriction per agent | Small | High | + +**Key insight**: Claude Code plugin architecture is the most mature reference for how BooCode's skill/plugin system should work. The `plugin.json` → commands/agents/skills/hooks/ `.mcp.json` layout is the de facto standard. The `code-architect.md` agent frontmatter (name, description, tools, model, color) shows exactly how agent metadata should be structured. + +--- + +### Domain 12: Behavioral Compliance Engine (boocontext-audit) + +**Source**: `boocontext-audit/` — TypeScript audit + behavioral rules engine (Way more than just "audit extraction") + +**What it actually is**: A full **Parlant-inspired behavioral compliance engine** ported to TypeScript, wrapping: +- **Guideline system**: `condition`/`action` natural language rules with criticality (low/medium/high/critical) +- **Multi-batch matcher**: observational, actionable, previously-applied, disambiguation, response analysis, low-criticality batches +- **Relational resolver**: DEPENDS_ON, PRIORITIZES, ENTAILS, TAG_ALL, TAG_PRIORITIZES relationship types with iterative convergence loop +- **Schematic generator**: abstract LLM batch caller for structured output per match type +- **Full audit trail**: PostToolUse/Stop/UserPromptSubmit middleware, JSONL buffer, session lifecycle, graded context recovery (L0-L4) + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Guideline condition/action model** (natural language rules with criticality, priority, labels, tags) | `boocontext-audit/src/guideline.ts` | `apps/coder` → behavioral rules engine | Small | Critical | +| **Multi-batch matcher** (6 batch types: Observational, Actionable, PreviouslyApplied, Disambiguation, ResponseAnalysis, LowCriticality — each with LLM structured output) | `boocontext-audit/src/matching.ts` (371 lines) | `apps/coder` → context-relevant rule matching | Med | High | +| **Relational resolver** (Dependency/Priority/Entailment resolution with iterative convergence, max 100 iterations) | `boocontext-audit/src/resolver.ts` (299 lines) | `apps/coder` → rule conflict resolution | Med | High | +| **Relationship store** (5 relationship kinds: DEPENDS_ON, PRIORITIZES, ENTAILS, TAG_ALL, TAG_PRIORITIZES) | `boocontext-audit/src/relationship.ts` (190 lines) | `apps/coder` → rule relationships | Small | High | +| **Schematic generator** (abstract LLM batch caller with temperature retry, structured output per batch type) | `boocontext-audit/src/generation.ts` (183 lines) | `apps/coder` → structured LLM calls | Med | High | +| **Full audit middleware** (PostToolUse, Stop, UserPromptSubmit hooks with JSONL buffer, session handoff, graded recovery) | `boocontext-audit/src/middleware.ts`, `buffer.ts`, `session.ts` | `apps/coder` → audit trail system | Med | High | +| **User correction tracking** (detect corrections in conversations, persist as priority records, load first on recovery) | `boocontext-audit/src/correction.ts` | `apps/coder` → learn from user corrections | Small | High | +| **Anomaly detection** (configurable alert rules with lambdas, CRITICAL/WARNING levels) | `boocontext-audit/src/anomaly.ts` | `apps/coder` → runtime monitoring | Med | Med | +| **Ambient context via AsyncLocalStorage** | `boocontext-audit/src/ambient-context.ts` | `apps/server` → parameter pollution reduction | Small | High | +| **Graded context recovery** (L0 index → L1 session → L2 corrections → L3 full → L4 cross-day) | `boocontext-audit/src/recovery.ts` | `apps/server` → context restoration | Med | High | + +**Key insight**: `boocontext-audit` is the single most undervalued repo in `/forks`. It's a complete behavioral compliance system already ported to TypeScript with zero external dependencies (only `@modelcontextprotocol/sdk`). The Guideline + MultiBatch + RelationalResolver stack is directly usable by BooCode's agent system to enforce agent behavior rules with structured LLM evaluation. The audit middleware is production-ready and can be wired into BooCode's inference lifecycle in days. + +--- + +### Domain 13: TypeScript Type Intelligence (type-inject) + +**Source**: `type-inject/` — TypeScript type context for AI coding assistants + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Type extraction pipeline** (ts-morph-based: parse tsconfig, load files, extract types/signatures/JSDoc/import depth) | `type-inject/packages/core/lib/extractor.ts` (1070 lines) | `apps/coder` → type-aware editing | Med | High | +| **Type lookup MCP tools** (`lookup_type` + `list_types` — query types by name regex, source, kind) | `type-inject/packages/mcp/src/` | `apps/server` → MCP type server | Small | High | +| **Auto-type injection** (PostToolUse hook on Read injects type signatures into file reads) | `type-inject/packages/claude-hook/` | `apps/coder` → type context on file reads | Small | High | +| **Type checking on write** (PostToolUse hook on Write runs type checker, reports errors) | `type-inject/packages/claude-hook/` | `apps/coder` → type error feedback | Small | High | +| **Type prioritizer** (rank types by relevance: used types first, direct imports over transitive, exported over private) | `type-inject/packages/core/lib/prioritizer.ts` | `apps/coder` → relevant type ordering | Small | Med | +| **Token-budgeted type injection** (configurable maxTokens, skipBarrelFiles, onlyUsed filtering) | `type-inject/packages/core/lib/config.ts` | `apps/coder` → context budget management | Small | Med | +| **Svelte support** (extract types from `.svelte` files via optional svelte compiler) | `type-inject/packages/core/lib/svelte-utils.ts` | `apps/coder` → Svelte type awareness | Small | Low | +| **OpenCode plugin** (full plugin with auto-injection on reads and MCP tools) | `type-inject/packages/opencode/` | `apps/coder` → direct integration point | Small | High | + +**Key insight**: type-inject directly solves the "TypeScript type information is missing from context" problem that the lift analysis identified as codecontext's biggest gap (0% TS recovery). Instead of building it, BooCode can use type-inject as an MCP server or direct plugin. The `@nick-vi/type-inject-mcp` package is published and installable via `npx`. This is a **direct port** — add to BooCode's MCP servers config. + +--- + +### Domain 14: Claude Agent SDK (Official Anthropic) + +**Source**: `claude-agent-sdk-typescript/` — Anthropic's official Agent SDK + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Agent lifecycle as SDK** (construct → generate → stream → saveSession — standard agent loop) | `claude-agent-sdk-typescript/` | `apps/server` → standardized agent API | Large | Med | +| **Session store abstraction** (pluggable session persistence backends) | `claude-agent-sdk-typescript/examples/session-stores/` | `apps/server` → session store interface | Small | High | +| **Streaming agent results** (structured stream events for tool calls, text deltas, results) | `claude-agent-sdk-typescript/` | `apps/coder` → streaming agent pattern | Med | Med | +| **Tool use with structured output** (JSON schema enforcement for tool results) | `claude-agent-sdk-typescript/` | `apps/coder` → structured tool results | Small | High | + +**Key insight**: The Claude Agent SDK is less valuable for direct code lift (it's a high-level API wrapper) than for its **design patterns** — the session store abstraction and streaming event protocol are clean references for BooCode's existing agent loop. + +--- + +### Domain 15: LLM Inference Sidecar Architecture (llama-sidecar) + +**Source**: `llama-sidecar/` — BooCode's own per-agent llama-server process pool (Go) + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Process pool with LRU eviction** (max 2 sidecars, port range 8500-8599, LRU eviction of idle) | `llama-sidecar/internal/pool/` (pool.go, sidecar.go) | (already BooCode's) | — | — | +| **Flag validator** (deny groups: model identity, network, auth/TLS, server UI — blocks security-critical flags) | `llama-sidecar/internal/validator/validator.go` (156 lines) | `apps/coder` → llama-args-validator.ts (already ported) | — | — | +| **Arg hash for sidecar dedup** (hash(modelID, flags) → reuse existing sidecar for same config) | `llama-sidecar/internal/pool/hash.go` | (already BooCode's) | — | — | +| **Windows service support** (NSSM integration for Windows daemon) | `llama-sidecar/internal/winsvc/` | (already BooCode's) | — | — | +| **Health check system** (background health checks every 30s, 60s timeout) | `llama-sidecar/internal/pool/` | (already BooCode's) | — | — | +| **X-Agent-Flags header** (per-request flag overrides via HTTP header) | `llama-sidecar/README.md` → `X-Agent-Flags: --top-k 20` | `apps/server` → per-agent flag routing | Small | High | +| **Slot management (coming soon?)** (save/restore KV cache slots — not implemented yet) | (potential) | `apps/server` → cross-turn KV persistence | Large | High | + +**Key insight**: llama-sidecar is already BooCode's own code. The main lift opportunity is the **`X-Agent-Flags` header pattern** — enabling per-agent llama-server parameter overrides (top-k, temperature, cache types) without spawning new sidecar processes. Currently, all agent flags are compiled into the BASE_ARGS. Adding header-based per-request override would enable agent-specific inference tuning. + +--- + +### Domain 16: LangGraph Python Ecosystem (Supervisor + Swarm Patterns) + +**Source**: `langchain/langgraph-supervisor-py/` + `langchain/langgraph-swarm-py/` (Python — patterns only) + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Supervisor agent pattern** (central coordinator delegates to specialized agents via handoff tools) | `langgraph-supervisor-py/langgraph_supervisor/supervisor.py` (469 lines) | `conductor/` → supervisor orchestration | Med | High | +| **Swarm agent pattern** (agents dynamically hand off control based on specialization, no central coordinator) | `langgraph-swarm-py/langgraph_swarm/swarm.py` (289 lines) | `conductor/` → swarm orchestration | Med | High | +| **Handoff tool mechanism** (agents transfer control + context via `create_handoff_tool(agent_name, description)`) | `langgraph-supervisor-py/langgraph_supervisor/handoff.py` | `conductor/` → agent-to-agent handoff | Small | High | +| **Agent name normalization + routing** (Literal-based active_agent enum for type-safe routing) | `langgraph-swarm-py/langgraph_swarm/swarm.py` | `conductor/` → typed agent routing | Small | Med | + +**Key insight**: The supervisor and swarm patterns from LangGraph are the two canonical multi-agent architectures. LangGraph supervisor = central coordinator pattern (like BooCode's conductor but more structured). LangGraph swarm = distributed handoff pattern (agents choose who to delegate to). Both are well-specified <500-line Python modules. The `create_handoff_tool()` mechanism is the critical pattern — a tool that transfers context between agents with a description the source agent can reason about. + +--- + +### Domain 17: Sim Studio — Full AI Agent Workspace + +**Source**: `sim/` — SimStudio open-source AI agent workspace (Next.js + monorepo) + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Visual workflow builder** (drag-and-drop block-based agent design with canvas) | `sim/apps/sim/blocks/` | `apps/web` → visual agent builder | Large | High | +| **Block execution engine** (executor orchestrating block DAGs with variable resolution) | `sim/apps/sim/executor/` | `conductor/` → visual flow execution | Large | High | +| **1,000+ tool integrations** via standardized tool config pattern | `sim/apps/sim/tools/` + `tools/registry.ts` | `data/skills/` → integration framework | Large | High | +| **Knowledge base** (document upload, vector RAG, query from agents) | `sim/apps/sim/` (knowledge base feature) | `apps/server` → RAG system | Large | High | +| **Realtime collaborative canvas** (Socket.IO, room-based multi-user) | `sim/apps/realtime/` | `apps/web` → collaborative workspace | Large | Med | +| **Workflow persistence layer** (load/save/subflow, raw block state) | `sim/packages/workflow-persistence/` | `conductor/` → flow persistence | Med | High | +| **Audit action recording** (`recordAudit + AuditAction + AuditResourceType`) | `sim/packages/audit/` | `apps/server` → audit framework | Small | Med | +| **Better Auth shared session** (cross-service auth via shared DB + secret) | `sim/packages/auth/` | `apps/server` → multi-service auth | Small | High | + +**Key insight**: Sim is a full production AI agent workspace — orders of magnitude more complex than anything else in `/forks`. Its value for BooCode is primarily as a **reference architecture** for the visual workflow builder and block execution engine. The `workflow-types/`, `workflow-persistence/`, and `workflow-authz/` packages are well-modularized and could provide patterns. The audit system and auth patterns are directly portable. + +--- + +### Domain 18: Vercel Ecosystem (Missed Repos) + +**Source**: Various Vercel repos. See DEEP RESEARCH C for Vercel Workflow and DEEP RESEARCH E for Vercel Sandbox. + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Turborepo task scheduler** (dependency graph, parallel execution, local+remote caching, task profiles) | `vercel/turborepo/` | `conductor/` → task dependency scheduling | Med | High | +| **Vercel AI SDK provider pattern** (model-provider abstraction with streaming, tool calling, structured output) | `vercel/ai/` | `apps/server` → provider abstraction (already partially done) | Large | High | +| **SWR stale-while-revalidate** (data fetching cache: serve stale→revalidate→update, dedup, focus revalidation) | `vercel/swr/` | `apps/server` → data caching layer | Small | High | +| **Next.js server actions** (RPC-style server functions called directly from client components) | `vercel/next.js/` | `apps/web` → server action pattern | Small | Med | +| **Vercel CLI platform API** (deployment, environment variables, domain management) | `vercel/vercel/` | `apps/server` → deployment integration | Large | Low | +| **Styled-JSX** (scoped CSS with zero runtime, compile-time transformation) | `vercel/styled-jsx/` | `apps/web` → CSS-in-JS reference | Small | Low | + +**Key insight**: The most valuable Vercel patterns for BooCode are **Turborepo's task scheduler** (parallel dependency-graph execution maps to conductor workflow scheduling) and **SWR's stale-while-revalidate** (maps to BooCode's data caching for codecontext/boocontext results). Vercel AI SDK's provider pattern is already partially replicated in BooCode. + +--- + +### Domain 19: LangChain Example Apps (Not Individual Libraries) + +**Source**: Various LangChain example repos (chat-langchain, open-canvas, deep-agents-ui, etc.) + +These repos are **example applications** of LangChain/LangGraph, not libraries with independent patterns. They demonstrate usage of the core libraries already analyzed (langchain-core, langgraph, langmem). + +| Repo | Assessment | Value to BooCode | +|------|-----------|-----------------| +| `chat-langchain/` | LangSmith-powered RAG chatbot example | **Low** — LangChain-specific patterns | +| `open-canvas/` | Collaborative AI writing canvas (Next.js) | **Low** — product concept, not liftable code | +| `deep-agents-ui/` | TypeScript UI for Deep Agents | **Low** — React UI for langgraphjs | +| `deepagents/` | Python sub-agent framework | **Med** — sub-agent spawning pattern (covered by harness-sdk & DeerFlow analyses) | +| `deepagentsjs/` | JS port of deepagents | **Low** — same as Python version | +| `local-deep-researcher/` | Local research agent (Ollama) | **Low** — simple agent example | +| `open_deep_research/` | LangChain deep research | **Low** — report generation example | +| `open-swe/` | SWE-bench agent framework | **Low** — benchmark-specific | +| `opengpts/` | GPTs clone with LangGraph | **Low** — product concept | +| `openwork/` | LangGraph workflow platform | **Med** — visual workflow patterns | +| `langsmith-starter-kit/` | LangSmith evaluation starter | **Low** — LangSmith-specific | +| `claude-workflow-v2/` | CloudAI-X workflow definitions | **Med** — workflow agent patterns | +| `opencode-workflow/` | CloudAI-X OpenCode workflows | **Med** — OpenCode workflow configurations | + +**Key insight**: These are primarily **example applications** for LangChain's commercial products. They don't contain independently liftable patterns beyond what's already captured in the core LangGraph/LangMem analyses. Skip deep analysis — focus on the library-level repos already covered. + +--- + +### Domain 20: Strands Tools Library + +**Source**: `tools/` — Strands Agents tool library + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Tool library pattern** (file ops, shell, memory, web, image, swarm tools as independent modules) | `tools/` | `data/skills/` → tool organization pattern | Small | Med | +| **Tool module format** (each tool = single directory with standardized interface) | `tools/` | `data/skills/` → tool packaging standard | Small | Med | + +**Key insight**: The strands tools library is a reference for how BooCode could organize its growing tool/skill collection. Each tool as an independent module with a standard interface is the right pattern. + +--- + +### Domain 21: Other Missed Repos — Quick Assessment + +| Repo | Summary | Worth Lift? | Reason | +|------|---------|-------------|--------| +| `amp-acp/` | ACP (Agent Communication Protocol) adapter | **Med** | ACP is a standard, any implementation is useful. Reference for BooCode's ACP layer | +| `qgn.app/` | Electron desktop app (QuickGen — AI code generation UI) | **Low** | Features duplicated: screenshot→code, code editing UI. BooCode's web UI covers the same ground | +| `agent-runtime/` | Generic agent runtime | **Low** | Overlaps with harness-sdk analysis | +| `ai-sdk-provider-opencode-sdk/` | OpenCode provider for Vercel AI SDK | **Low** | Integration adapter, not independently valuable | +| `memory_engine/` | (Not analyzed — see langmem for LangChain memory) | **Low** | Covered by langmem + deer-flow analyses | +| `parlant/` | Already deeply analyzed in DEEP RESEARCH A | — | Covered | +| `audit-harness/` | Already deeply analyzed in DEEP RESEARCH A | — | Covered | +| `Archon/` | Already deeply analyzed in DEEP RESEARCH C | — | Covered | +| `harness-sdk/` | Already deeply analyzed in DEEP RESEARCH B | — | Covered | +| `hive/` | Already deeply analyzed in DEEP RESEARCH B | — | Covered | +| `CowAgent/` | Already deeply analyzed in DEEP RESEARCH D | — | Covered | +| `deer-flow/` | Already deeply analyzed in DEEP RESEARCH D+F | — | Covered | +| `ruflo/` | Already deeply analyzed in DEEP RESEARCH F | — | Covered | +| `opencode-extras/*/` | Already covered in original lift analysis | — | Covered | + +--- + +## UPDATED RANKED INTEGRATION RECOMMENDATIONS (Post-Extension) + +New findings sorted into the existing Tier structure: + +### Tier 0: Immediate (New — higher value than existing Tier 1) +| # | What | Source | BooCode Target | Why | +|---|------|--------|----------------|-----| +| 0a | **Add boocontext-audit as behavioral rules engine** | `boocontext-audit/` | `apps/coder` + `apps/server` | Complete TypeScript Parlant port: guideline model, 6-batch matcher, relational resolver, audit trail, ambient context, graded recovery. Zero external deps. Single most undervalued repo. | +| 0b | **Add type-inject MCP server** | `type-inject/packages/mcp/` | `apps/server` MCP client | Fixes 0% TypeScript recovery. Published as `@nick-vi/type-inject-mcp`. One-line config addition. | +| 0c | **Add type-inject auto-injection hook** | `type-inject/packages/claude-hook/` | `apps/coder` | Type injection on file reads + type checking on writes. Direct improvement to edit quality. | + +### Tier 0.5: Plugin Architecture from claude-code + +| # | What | Source | BooCode Target | Why | +|---|------|--------|----------------|-----| +| 0d | **Adopt claude-code plugin format** | `claude-code/plugins/*/` | `data/plugins/` + `apps/coder` | Standard plugin structure (`.claude-plugin/plugin.json` → commands/agents/skills/hooks/MCP). Proven 14-plugin ecosystem. | +| 0e | **Security guidance hooks** | `claude-code/plugins/security-guidance/` | `apps/coder` PreToolUse | 9 security pattern detectors. Stops command injection, XSS, unsafe deserialization before they happen. | +| 0f | **Self-referential agent loop** | `claude-code/plugins/ralph-wiggum/` | `apps/coder` → `/ralph-loop` | Agent runs same task until completion. Stop hook intercepts exit. | + +### Updated Tier 1 additions: +| # | What | Source | BooCode Target | Why | +|---|------|--------|----------------|-----| +| 1a | **Supervisor agent pattern** | `langgraph-supervisor-py/langgraph_supervisor/` | `conductor/` | Central coordinator delegates to specialized agents. Pattern-ported from 469-line Python module. | +| 1b | **Handoff tool mechanism** | `langgraph-supervisor-py + langgraph-swarm-py` | `conductor/` | Agent-to-agent context transfer via `create_handoff_tool(agent_name, description)`. | +| 1c | **X-Agent-Flags header** | `llama-sidecar/` | `apps/server` | Per-agent LLM parameter overrides without spawning new processes. | + +### Updated Tier 2 additions: +| # | What | Source | BooCode Target | Why | +|---|------|--------|----------------|-----| +| 2a | **Turborepo task scheduler** | `turborepo/` | `conductor/` | Dependency graph scheduling, parallel execution, caching. Maps directly to workflow execution needs. | +| 2b | **SWR stale-while-revalidate cache** | `swr/` | `apps/server` | Data caching layer for codecontext/boocontext results. Serve cached → revalidate → update. | +| 2c | **Swarm agent pattern** | `langgraph-swarm-py/langgraph_swarm/` | `conductor/` | Dynamic agent handoff without central coordinator. | +| 2d | **Sim workflow-types package** | `sim/packages/workflow-types/` | `conductor/` | Pure BlockState/Loop/Parallel types — reference for BooCode's own flow types. | + +### Updated Tier 3 additions: +| # | What | Source | BooCode Target | Why | +|---|------|--------|----------------|-----| +| 3a | **Visual workflow builder** | `sim/apps/sim/blocks/` + `executor/` | `apps/web` | Drag-and-drop agent design. Large effort but high strategic value. | +| 3b | **PR review agent team** | `claude-code/plugins/pr-review-toolkit/` | `conductor/` → PR review flow | 5 parallel agents for code review, bug finding, test analysis, type design, simplification. | + +--- + +## REVISED DEPENDENCY GRAPH + +``` +boocontext-audit behavioral engine (NEW — Tier 0) + ├── Guideline model → Multi-batch matcher → Relational resolver + ├── Audit middleware → Graded context recovery + └── Depends on: LLM backend for SchematicGenerator + +type-inject MCP (NEW — Tier 0) + ├── Type extraction → Type prioritization → Token-budgeted injection + └── Independent — add as MCP server + +claude-code plugin format (NEW — Tier 0.5) + ├── Enables: Security hooks → Self-referential loops → PR review teams + └── Prerequisite: plugin.json convention + +LangGraph supervisor pattern (NEW — Tier 1) + └── Enables: Handoff tool → Swarm pattern (Tier 2) + +Turborepo scheduler (NEW — Tier 2) + └── Enables: Task dependency graph → Parallel execution → Caching +``` + +--- + +## REVISED COMPLETE REPO REFERENCE (Additions) + +| Repo | Path | Key Capability | Lift Priority | +|------|------|---------------|---------------| +| boocontext-audit | `/opt/forks/boocontext-audit/` | Behavioral compliance engine (guidelines, matching, resolution, audit, recovery) | **Tier 0** | +| type-inject | `/opt/forks/type-inject/` | TypeScript type extraction, lookup MCP, auto-injection hooks | **Tier 0** | +| claude-code plugins | `/opt/forks/claude-code/plugins/` | 14 official plugins: plugin format, security hooks, loop, PR review, feature dev | **Tier 0.5** | +| langgraph-supervisor | `/opt/forks/langchain/langgraph-supervisor-py/` | Supervisor agent pattern (central coordinator → specialized agents) | **Tier 1** | +| langgraph-swarm | `/opt/forks/langchain/langgraph-swarm-py/` | Swarm agent pattern (dynamic handoff, no central coordinator) | **Tier 2** | +| turborepo | `/opt/forks/vercel/turborepo/` | Task dependency graph scheduling, parallel execution, caching | **Tier 2** | +| swr | `/opt/forks/vercel/swr/` | Stale-while-revalidate data fetching cache | **Tier 2** | +| sim | `/opt/forks/sim/` | Full AI agent workspace: visual workflows, block engine, knowledge base | **Tier 3** | +| claude-agent-sdk | `/opt/forks/claude-agent-sdk-typescript/` | Session store abstraction, streaming agent pattern | **Tier 2** | +| claude-agent-sdk-python | `/opt/forks/claude-agent-sdk-python/` | Python version (same patterns as TS) | Low | +| strands tools | `/opt/forks/tools/` | Tool organization pattern | Tier 3 | +| amp-acp | `/opt/forks/amp-acp/` | ACP protocol adapter | Tier 2 | +| llama-sidecar | `/opt/forks/llama-sidecar/` | X-Agent-Flags header pattern | **Tier 1** | + +--- + +## KEY FINDINGS SUMMARY + +### Most Undervalued Repo +**`boocontext-audit/`** — Not a simple audit extraction. A full Parlant-inspired behavioral compliance engine with: +- Guideline condition/action model (Parlant port) +- 6-batch multi-matcher (Observational, Actionable, PreviouslyApplied, Disambiguation, ResponseAnalysis, LowCriticality) +- Relational resolver (DEPENDS_ON, PRIORITIZES, ENTAILS, TAG_ALL, TAG_PRIORITIZES with iterative convergence) +- SchematicGenerator for structured LLM batch calls +- Full audit middleware with graded context recovery (L0-L4) +- Zero external deps (only `@modelcontextprotocol/sdk`) + +The behavioral engine could enforce agent behavior rules (security policies, code style, tool restrictions) via structured LLM evaluation — far beyond simple CLAUDE.md guidelines. + +### Easiest Wins +1. **type-inject MCP server** — One-line config add. Fixes 0% TypeScript recovery. +2. **Security guidance hooks** — Copy claude-code/plugins/security-guidance/ pattern. Days of effort. +3. **X-Agent-Flags header** — Small change to inference routing for per-agent LLM parameters. +4. **SWR caching for codecontext** — Stale-while-revalidate for MCP tool results. Days of effort. + +### Architecture Patterns Worth Studying +1. **claude-code plugin format** — `.claude-plugin/plugin.json` → commands/agents/skills/hooks/ `.mcp.json` is the de facto standard. BooCode should adopt a compatible format. +2. **LangGraph supervisor pattern** — Clean central-coordinator architecture with handoff tools. +3. **Sim's workflow-types package** — Pure type definitions for BlockState, Loop, Parallel — reference for conductor flow types. +4. **Turborepo's task scheduling** — Dependency graph with remote caching — maps to conductor's parallel execution needs. + +--- + +## PASS 4 — CORRECTED ASSESSMENTS (Deep Analysis Findings) + +Several repos were initially assessed as "Low Value" in the extension. Deep analysis reveals they are much more significant. + +--- + +### Domain 22: Agent Runtime — Combined Eval + Sandbox + Pregel in TypeScript + +**Source**: `agent-runtime/` — `/home/samkintop/opt/forks/agent-runtime/` + +**What it is**: A **pre-compiled TypeScript monorepo** that combines three independent subsystems into one clean package: +- `@agent-runtime/eval` — OpenEvals-style LLM evaluation framework +- `@agent-runtime/sandbox` — Vercel Sandbox-style remote execution +- `@agent-runtime/graph` — langgraphjs-style Pregel state graph engine + +**Why this matters**: This is the exact combined system from DEEP RESEARCH E (OpenEvals + Vercel Sandbox + langgraphjs), already ported to TypeScript, already compiled to `dist/`, with zero LangChain dependencies. It's a direct-lift candidate. + +**Key Architecture** (`packages/graph/src/`): + +``` +pregel/ +├── index.ts — Pregel class (compiled graph engine) +├── algo.ts — _applyWrites, _prepareNextTasks +├── loop.ts — PregelLoop (execution loop) +├── runner.ts — PregelRunner (parallel task execution) +├── read.ts — PregelNode (channel reads) +├── write.ts — ChannelWrite +├── io.ts — mapInput, readChannels +└── types.ts — Pregel types +channels/ +├── base.ts — BaseChannel (abstract) +├── last_value.ts — LastValue, LastValueAfterFinish +├── binop.ts — BinaryOperatorAggregate +├── topic.ts — Topic (publish/subscribe) +├── ephemeral.ts — EphemeralValue +└── named_barrier.ts — NamedBarrierValue +graph/ +├── graph.ts — Graph class (untyped DAG) +├── state.ts — StateGraph class (typed state machine) +└── annotation.ts — Annotation.Root helpers +stream/ +├── index.ts — StreamEvent protocol, GraphRunStream +state/ +├── schema.ts — State schema + channel builder +└── adapter.ts — State adapter +interrupt.ts — interrupt(), interruptMultiple() — HITL +constants.ts — START, END, INTERRUPT, Command, Send +errors.ts — NodeSpecError, GraphCycleError, ChannelError +``` + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Pre-compiled Pregel engine** (StateGraph, superstep execution, channels, streaming — zero LangChain deps) | `agent-runtime/packages/graph/dist/` | `conductor/` → workflow graph engine | **Small** (import/adapt) | **Critical** | +| **LLM-as-judge evaluator** (structured scoring, prompt templates, multi-turn simulation, trajectory evaluation) | `agent-runtime/packages/eval/dist/` | `apps/server` → agent quality evaluation | **Small** | **High** | +| **Remote sandbox abstraction** (command execution, POSIX filesystem, snapshots, network policy) | `agent-runtime/packages/sandbox/dist/` | `apps/booterm` → sandboxed code execution | **Small** | **High** | +| **Human-in-the-loop interrupts** (interrupt()/interruptMultiple() pattern — resumable execution) | `agent-runtime/packages/graph/src/interrupt.ts` | `apps/coder` → pause-for-input mid-workflow | **Small** | **High** | +| **Channel types** (LastValue, Topic, BinaryOperatorAggregate, Ephemeral, NamedBarrierValue) | `agent-runtime/packages/graph/src/channels/` | `conductor/` → typed state channels | **Small** | **High** | +| **StreamEvent protocol** (STREAM_MODES, GraphRunStream, customEvent) | `agent-runtime/packages/graph/src/stream/` | `apps/server` → streaming workflow events | **Small** | **High** | +| **Execution loop with checkpoint** (PregelLoop → PregelRunner → _applyWrites → checkpoint cycle) | `agent-runtime/packages/graph/src/pregel/loop.ts` + `runner.ts` | `conductor/` → structured execution lifecycle | **Small** | **High** | + +**Key insight**: `agent-runtime` is the #1 most undervalued repo. It's a pre-built, compiled TypeScript implementation of the most powerful patterns from DEEP RESEARCH E, ready for direct import. The `@agent-runtime/graph` package provides a complete Pregel state machine that could replace or augment BooCode's conductor flow runner. Zero LangChain deps, clean TypeScript, already built. + +--- + +### Domain 23: Memory Engine — Complete 3-Tier Memory System + +**Source**: `memory_engine/` — `/home/samkintop/opt/forks/memory_engine/` + +**What it is**: A standalone Python memory system combining LangMem's token-budget summarization, CowAgent's 3-tier architecture (Context→Daily→Core), and DeerFlow's memory update flow. Complete with SQLite FTS5 + vector search, Deep Dream consolidation, and management tools. + +**Why this matters**: Unlike the other memory repos (LangMem = framework-coupled, CowAgent = complex, DeerFlow = LangChain-coupled), `memory_engine` is a **clean, standalone Python package** with a clear architecture that can be ported to TypeScript for boocode directly. + +**Key Architecture:** + +``` +config.py — MemoryConfig (Pydantic, all tunable params) +core/ +├── store.py — MemoryStore (SQLite + FTS5 + vector BLOB, 262 lines) +├── hybrid_search.py — HybridSearchEngine (vector 0.7 + keyword 0.3) +├── schemas.py — MemoryChunk, SearchResult, Fact (Pydantic + dataclass) +tiers/ +├── context.py — ContextTier (short-term, token-budget summarization, 205 lines) +├── core.py — CoreTier (long-term SQLite + hybrid search, 132 lines) +├── daily.py — DailyTier (Markdown files + MEMORY.md, 107 lines) +tools/ +├── manage.py — Memory management (create/update/delete) +├── search.py — Search tools +background/ +├── deep_dream.py — Deep Dream LLM consolidation (like CowAgent) +├── queue.py — Debounced background queue (like DeerFlow) +embedding/ +extraction/ +utils/ +``` + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **SQLite FTS5 store** with vector BLOB storage, WAL mode, thread safety | `memory_engine/core/store.py` (262 lines) | `apps/coder` → memory persistence | Med | High | +| **3-tier memory** (Context: token-budget summary → Daily: Markdown files → Core: SQLite+vectors) | `memory_engine/tiers/` (context.py, daily.py, core.py) | `apps/coder` → structured memory hierarchy | Med | **Critical** | +| **Hybrid search** (vector 0.7 + keyword 0.3 with FTS5, temporal decay) | `memory_engine/core/hybrid_search.py` | `apps/coder` → memory retrieval | Med | High | +| **MemoryConfig** (typed, Pydantic, 21 tunable parameters with defaults) | `memory_engine/config.py` | `apps/server` → configuration pattern | Small | Med | +| **Deep Dream consolidation** (overnight LLM rewrite of MEMORY.md) | `memory_engine/background/deep_dream.py` | `apps/coder` → long-term memory consolidation | Med | High | +| **Management tools** (manage.py create/update/delete + search.py) | `memory_engine/tools/` | `apps/coder` → memory CRUD tools | Small | High | +| **Debounced background queue** | `memory_engine/background/queue.py` | `apps/server` → async memory processing | Small | Med | + +**Key insight**: `memory_engine` is the cleanest reference implementation of the memory patterns identified in DEEP RESEARCH D. Where LangMem is framework-coupled (LangGraph) and CowAgent is feature-rich but complex (1500+ line files), memory_engine has clean <300-line files with clear separation of concerns. Best model for porting 3-tier memory to boocode. + +--- + +## UPDATED RANKED INTEGRATION RECOMMENDATIONS (Post-Correction) + +### New Tier 0 additions: +| # | What | Source | BooCode Target | Why | +|---|------|--------|----------------|-----| +| 0g | **Import @agent-runtime/graph Pregel engine** | `agent-runtime/packages/graph/dist/` | `conductor/` → replace flow runner | Pre-compiled TS StateGraph with Pregel execution, channels, interrupts, streaming. Zero deps. Direct lift. | +| 0h | **Import @agent-runtime/eval evaluator** | `agent-runtime/packages/eval/dist/` | `apps/server` → agent quality eval | LLM-as-judge, trajectory, code correctness, multi-turn simulation. Ready to use. | +| 0i | **Port memory_engine 3-tier memory to TypeScript** | `memory_engine/` (Python → TS port) | `apps/coder` → structured memory | Cleanest reference for Context→Daily→Core with SQLite FTS5 + vectors + Deep Dream. | + +### Updated REVISED COMPLETE REPO REFERENCE + +| Repo | Path | Key Capability | Lift Priority | +|------|------|---------------|---------------| +| agent-runtime | `/opt/forks/agent-runtime/` | Pre-compiled Pregel engine + evaluator + sandbox (TypeScript, zero LangChain deps) | **Tier 0** | +| memory_engine | `/opt/forks/memory_engine/` | 3-tier memory: SQLite FTS5+vectors, Deep Dream, token-budget summarization | **Tier 0** | +| deepagents | `/opt/forks/deepagents/` | Sub-agent framework with filesystem, memory, skills, summarization middleware | Tier 2 | +| open-swe | `/opt/forks/langchain/open-swe/` | Production coding agent: 11 middleware components, sandbox lifecycle, GitHub/Linear/Slack | Tier 2 | + +--- + +### Domain 24: Deep Agents — Production Sub-Agent Harness + +**Source**: `deepagents/` — Python agent harness by LangChain + +**Corrected assessment**: Initially assessed as "Med" due to being a LangChain-specific implementation. However, it contains a **clean middleware architecture** for sub-agent orchestration that is the reference implementation for boocode's sub-agent system. + +**Key patterns** (`deepagents/libs/deepagents/deepagents/`): + +``` +graph.py — create_deep_agent(): full agent assembly (866 lines) +middleware/ +├── subagents.py — SubAgent middleware (788 lines): spawn isolated agents with task() tool +├── async_subagents.py — AsyncSubAgent middleware: parallel sub-agent execution +├── filesystem.py — Filesystem middleware: read/write/edit/ls with permission model +├── memory.py — MemoryMiddleware: persistent cross-session memory +├── skills.py — SkillsMiddleware: loadable skill behaviors +├── summarization.py — Token-budget context management +└── _tool_exclusion.py — Tool exclusion per middleware layer +backends/ — StateBackend, SandboxBackend, LangSmithSandbox +profiles/ — ProviderProfile, HarnessProfile (model-specific config) +``` + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **SubAgent middleware** (task() tool spawns isolated agent with own model, tools, middleware, permissions) | `deepagents/middleware/subagents.py` (788 lines) | `conductor/` → sub-agent spawning | Med | High | +| **Filesystem middleware** (permission model: allow/deny paths by pattern) | `deepagents/middleware/filesystem.py` | `apps/coder` → file access control | Small | High | +| **Memory middleware** (cross-session persistent state via BaseStore) | `deepagents/middleware/memory.py` | `apps/coder` → agent memory abstraction | Med | High | +| **Skills middleware** (loadable skills from directories) | `deepagents/middleware/skills.py` | `data/skills/` → skill loading architecture | Med | Med | +| **Summarization middleware** (token-budget context management) | `deepagents/middleware/summarization.py` | `apps/server` → context window management | Med | High | + +**Key insight**: deepagents is the reference implementation for boocode's sub-agent middleware pattern. The SubAgent spec (name, description, system_prompt, tools, model, middleware, permissions) maps directly to boocode's agent configuration. The middleware chain architecture is the right abstraction for composable agent behavior. + +--- + +### Domain 25: Open SWE — Production Coding Agent Framework + +**Source**: `open-swe/` (at `/opt/forks/langchain/open-swe/`) — LangChain's open-source internal coding agent framework + +**Corrected assessment**: Initially assessed as "Low" because it was grouped with example apps. In reality, it's a **production coding agent framework** used by companies like Stripe, Ramp, Coinbase. Has 10+ middleware components, sandbox lifecycle management, and multi-channel integration (Slack, Linear, GitHub) that directly relate to boocode's domain. + +**Key architecture** (`agent/`): + +``` +├── server.py — Main agent factory (607 lines): create_deep_agent with curated tool list + middleware stack +├── webapp.py — FastAPI routes: webhooks for GitHub, Linear, Slack +├── middleware/ — 11 middleware components +│ ├── check_message_queue.py — Mid-run message injection (Linear/Slack comments arrive mid-stream) +│ ├── model_fallback.py — Transient failure → fallback model (Anthropic↔OpenAI) +│ ├── sandbox_circuit_breaker.py — Circuit breaker on repeated sandbox failures +│ ├── sanitize_thinking_blocks.py — Strip malformed Anthropic thinking blocks +│ ├── sanitize_tool_inputs.py — Normalize tool inputs before execution +│ ├── tool_error_handler.py — Catch tool exceptions → tool messages +│ ├── notify_step_limit.py — Post Slack reply when agent hits step cap +│ ├── refresh_slack_status.py — Keep Slack "typing" indicator alive +│ ├── ensure_no_empty_msg.py — Guard against empty assistant messages +│ └── exclude_tools.py — Tool exclusion per context +├── dashboard/ — Model profiles, team settings, OAuth, usage tracking +├── integrations/ — LangSmith sandbox with GitHub proxy +├── tools/ — Curated tool set (http_request, fetch_url, web_search, Linear, Slack, GitHub) +├── reviewer.py — Read-only PR reviewer with findings model +├── analyzer.py — Per-repo review style learner +├── utils/ — Auth, model resolution, sandbox state management +└── skills/ — Deep agents skills for bootstrap/continual learning +``` + +| Capability | Source | BooCode Target | Effort | Value | +|-----------|--------|----------------|--------|-------| +| **Mid-run message injection** (queue new messages mid-stream → inject before next LLM call) | `open-swe/agent/middleware/check_message_queue.py` | `apps/coder` → mid-conversation message injection | Med | High | +| **Model fallback middleware** (primary fails → retry with fallback provider) | `open-swe/agent/middleware/model_fallback.py` (93 lines) | `apps/coder` → provider failover | Small | High | +| **Sandbox circuit breaker** (consecutive sandbox failures → abort thread) | `open-swe/agent/middleware/sandbox_circuit_breaker.py` (261 lines) | `apps/booterm` → sandbox resilience | Small | High | +| **Sanitize tool inputs middleware** (strip/normalize before tool execution) | `open-swe/agent/middleware/sanitize_tool_inputs.py` | `apps/coder` → tool input safety | Small | Med | +| **Middleware stack pattern** (ordered chain: sanitize → limit → error → queue → status → breaker → fallback) | `open-swe/agent/middleware/` + `agent/server.py` | `conductor/` → middleware architecture | Med | High | +| **Deterministic thread-id derivation** (same issue/PR → same thread) | `open-swe/agent/utils/github_comments.py` + `utils/linear.py` + `utils/slack.py` | `apps/server` → session continuity | Small | High | +| **Sandbox lifecycle** (4 cases: cached→reconnect, creating→reset, missing→create, metadata→reconnect) | `open-swe/agent/utils/sandbox_state.py` | `apps/booterm` → sandbox lifecycle management | Med | High | +| **GitHub proxy in sandbox** (inject GitHub App token → sandbox uses `gh dummy` without real tokens) | `open-swe/agent/utils/github_comments.py`, `integrations/langsmith.py` | `apps/booterm` → authenticated git in sandbox | Med | High | + +**Key insight**: open-swe is the most production-relevant coding agent framework in the entire forks directory. Its middleware stack pattern (10 ordered middleware components) is a direct reference for boocode's inference lifecycle. The model fallback pattern (Anthropic↔OpenAI on transient errors) and sandbox circuit breaker are directly liftable. The mid-run message injection pattern solves a real boocode problem: "how to inject user messages that arrive while the agent is processing." + +--- + +### Corrected Assessment: LangChain Python Library + +**Source**: `langchain/langchain/libs/` + `langchain/langchain/libs/core/langchain_core/` + +This is the LangChain Python framework itself. Not an example app. The core abstractions live in `libs/core/langchain_core/`: + +| Module | Key Patterns | Liftable? | +|--------|-------------|-----------| +| `agents.py` | Agent types, AgentExecutor protocol | **No** — deeply coupled to LangChain's runnable framework | +| `tools.py` | BaseTool, StructuredTool abstraction | **Concept only** — boocode already has its own tool model | +| `callbacks/` | CallbackHandler, AsyncCallbackHandler (17 events) | **Inspiration** — boocode's hook system needs this event set | +| `caches.py` | LLM response caching | **Yes** — pattern for boocode's LLM cache | + +**Verdict**: The LangChain library itself is too coupled to its own framework to lift individual patterns. The callback system (17 lifecycle events for tool start/end, LLM start/end, chain start/end) is a useful reference for boocode's hooks system but not directly liftable. + +--- + +### Corrected Assessment: LangGraph Python Core + +**Source**: `langchain/langgraph/libs/` — Python langgraph + +Key differences from langgraphjs (already analyzed in DEEP RESEARCH E): + +| Feature | Python | JS | BooCode Relevance | +|---------|--------|----|-------------------| +| Checkpointer implementations | SQLite, Postgres, Memory, Conformance tests | Checkpoint interface only | **Med** — checkpointer diversity is useful | +| Prebuilt agents | `create_react_agent` | `createReactAgent` | **Low** — same concept | +| SDK | `sdk-py`, `sdk-js` | Platform clients | **Low** — deployment-specific | +| CLI | `langgraph-cli` (dev, deploy, test) | None | **Low** — dev tooling | + +**Verdict**: No significant liftable patterns beyond what was already covered in the langgraphjs Pregel analysis (DEEP RESEARCH E). The checkpointer implementations are the most useful reference. + +--- + +### Corrected Assessment: langchainjs + +**Source**: `langchain/langchainjs/` + +The JS port of LangChain. Same architecture, different language. Contains no independently liftable patterns beyond what the Python LangChain library provides. + +**Verdict**: No liftable patterns. Focus on `agent-runtime` instead (which is the clean TS rewrite). + +--- diff --git a/openspec/changes/domain2-code-intelligence/.openspec.yaml b/openspec/changes/domain2-code-intelligence/.openspec.yaml new file mode 100644 index 0000000..e8d4ccf --- /dev/null +++ b/openspec/changes/domain2-code-intelligence/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-08 diff --git a/openspec/changes/domain2-code-intelligence/proposal.md b/openspec/changes/domain2-code-intelligence/proposal.md new file mode 100644 index 0000000..80ba5d1 --- /dev/null +++ b/openspec/changes/domain2-code-intelligence/proposal.md @@ -0,0 +1,72 @@ +# Domain 2 — Codebase Context & Code Intelligence + +**Status:** Proposed +**Epic:** domain2-code-intelligence +**Depends on:** v2.8.8-type-inject-mcp, v2.8.12-hashline-audit-hooks + +## Why + +BooCode has two parallel code intelligence systems — and neither is fully wired: + +1. Old Go codecontext sidecar (http://codecontext:8080) — 16 tool wrappers in apps/server/src/services/tools/codecontext/ — called via codecontext_client.ts HTTP client. Limited to codesight-level analysis (routes, schema, middleware, hot files). No type recovery, no health grades, no impact analysis. + +2. New boocontext MCP server (registered in data/mcp.json as node /opt/forks/boocontext/dist/standalone.js) — 7 aggregated tools (overview, map, health, symbols, callgraph, impact, types) backed by child MCP servers. Already running but only available to agents through MCP discovery as boocontext_* tools — not integrated as first-class BooCode agent tools. + +The lift analysis (boocode-lift-analysis.md) rates the boocontext swap as Tier 1 / Critical: 7 tools instead of 5, session caching, call graph, health grades, impact analysis, TS type recovery. Written in TS (BooCode's stack). Fixes 0% TS type recovery. + +## What Changes + +### Phase 1: Go sidecar → boocontext migration (high confidence) + +1. Update codecontext_client.ts to optionally route through boocontext MCP instead of the Go HTTP sidecar. Add a CODECONTEXT_MCP env toggle. When boocontext MCP is active, call the MCP tool directly instead of HTTP POST to http://codecontext:8080/v1/{toolName}. + +2. Add 4 new first-class tool wrappers for boocontext-only capabilities: + - get_code_health — wraps boocontext_health (A-F grades per file, 7 dimensions) + - get_code_impact — wraps boocontext_impact (symbol trace + blast radius merged) + - get_type_info — wraps boocontext_types (TypeScript type signatures, cross-file resolution) + - get_code_map — wraps boocontext_map (compressible context map with DCP) + +3. Register new tools in ALL_TOOLS in tools/registry.ts. Add to agent whitelists in data/AGENTS.md. + +### Phase 2: Child MCP server hardening (high confidence) + +4. Switch type-inject child path from hardcoded /opt/forks/type-inject/packages/mcp/dist/index.js to npx @nick-vi/type-inject-mcp. Set BOOCONTEXT_TYPE_INJECT_NPX=@nick-vi/type-inject-mcp env var on the boocontext MCP process. + +5. Add graceful degradation on child server failure. If tree-sitter-analyzer or type-inject is unavailable, fall back to codesight-only mode instead of failing hard. + +### Phase 3: Wiki mode + scanning (medium confidence) + +6. Add get_wiki_article tool — wraps codesight --wiki output. Generates targeted persistent articles per project, cached on disk. + +7. Add token-efficient scanning — adopt codesight's scanner pattern for get_codebase_overview. Uses DCP compression for large payloads. + +### Phase 4: Teardown (future) + +8. Deprecate Go sidecar — once all tool wrappers verify against boocontext MCP, mark the Go codecontext sidecar as deprecated. Remove from docker-compose.yml and codecontext/Dockerfile when no remaining consumers. + +## Non-Goals +- No changes to the boocontext fork itself (consumed as-is via dist/standalone.js) +- No removal of the Go sidecar in this batch (parallel running OK) +- No changes to boocontext's child-server architecture +- No new framework detectors (boocontext already has 20+ language extractors) + +## Capabilities + +### New Capabilities +- get_code_health — A-F code health grades per file, project health summary, hotspot identification, refactoring candidate ranking +- get_code_impact — Symbol-level trace merged with file-level blast radius. Single call replaces two-step get_symbol_info → get_blast_radius +- get_type_info — TypeScript type recovery: type signatures, interface definitions, generic constraints, cross-file type resolution +- get_code_map — DCP-compressed context map with compress toggle +- get_wiki_article — Persistent codebase wiki article by name, cached on disk + +### Modified Capabilities +- Existing 12 codecontext tools transparently upgraded to boocontext backend (same agent interface, better data) +- codecontext_client.ts — gains MCP routing layer with Go sidecar fallback + +## Metrics +- Go sidecar HTTP calls → 0 (all migrated to boocontext MCP) +- New tool surface area: 16 → 20 first-class tools +- Agent-visible tools: 16 unchanged names + 4 new names +- TypeScript type recovery: 0% → full TS-visible types on file context +- Code health awareness: 0 tools → 1 tool +- Impact analysis: 2-step → 1-step diff --git a/openspec/changes/domain2-code-intelligence/tasks.md b/openspec/changes/domain2-code-intelligence/tasks.md new file mode 100644 index 0000000..7425dfe --- /dev/null +++ b/openspec/changes/domain2-code-intelligence/tasks.md @@ -0,0 +1,100 @@ +# Tasks — Domain 2: Codebase Context & Code Intelligence + +## Phase 1: Migration (5 tasks) + +### 1. Route codecontext_client through boocontext MCP +Update apps/server/src/services/codecontext_client.ts to support dual routing: +- Check CODECONTEXT_MCP env var — when set, route tool calls through the boocontext MCP client instead of HTTP to the Go sidecar +- Map old tool names to boocontext equivalents: + - get_codebase_overview → boocontext_overview + - get_file_analysis → boocontext_map + - get_symbol_info + get_symbol_details + search_symbols → boocontext_symbols + - get_semantic_neighborhoods → boocontext_map + - get_hot_files → boocontext_overview + - get_framework_analysis → boocontext_overview + - get_blast_radius → boocontext_impact + - get_routes → boocontext_overview + - get_middleware → boocontext_overview + - get_dependencies → boocontext_callgraph + - watch_changes → keep as-is (no boocontext equivalent) +- Fall back to Go sidecar HTTP when boocontext unavailable +- Preserve .codecontextignore handling, path validation, and truncation logic +**Verification**: tsc --noEmit passes. Existing codecontext tests pass with both routing modes. + +### 2. Add get_code_health tool +Create apps/server/src/services/tools/codecontext/get_code_health.ts +- Wrap boocontext_health MCP tool +- Schema: project path, optional file path for single-file health +- Output: A-F grade per dimension (cohesion, coupling, complexity, documentation, duplication, unit size, test coverage) + project health summary + hotspots list + refactoring candidates +- Register in tools/registry.ts ALL_TOOLS +- Add to Architect, Refactorer, and Code Reviewer agent tool whitelists in data/AGENTS.md +**Verification**: tsc --noEmit passes. Tool appears as get_code_health in /api/tools response. + +### 3. Add get_code_impact tool +Create apps/server/src/services/tools/codecontext/get_code_impact.ts +- Wrap boocontext_impact MCP tool +- Schema: project path, symbol name, optional file path, depth (default 1) +- Merges tree-sitter-analyzer symbol trace with codesight blast radius — single call +- Register in ALL_TOOLS +- Add to Architect and Planner agent whitelists +**Verification**: tsc --noEmit passes. curl /api/tools shows get_code_impact. + +### 4. Add get_type_info tool +Create apps/server/src/services/tools/codecontext/get_type_info.ts +- Wrap boocontext_types MCP tool (backed by @nick-vi/type-inject-mcp) +- Schema: project path, symbol name (regex), optional file path, kind filter (interface/type/class/function) +- Output: type signatures, interface definitions, generic constraints, JSDoc, import paths +- Register in ALL_TOOLS +- Add to Code Reviewer, Debugger, Architect agent whitelists +**Verification**: tsc --noEmit passes. Tool returns correct TypeScript types for known symbols. + +### 5. Add get_code_map tool +Create apps/server/src/services/tools/codecontext/get_code_map.ts +- Wrap boocontext_map MCP tool +- Schema: project path, compress (boolean, default true) +- DCP-compressed context map with filenames, sizes, import relationships +- Register in ALL_TOOLS +- Add to Recon agent whitelist +**Verification**: tsc --noEmit passes. Map output is valid JSON with compress toggle working. + +## Phase 2: Hardening (2 tasks) + +### 6. Fix type-inject child path +Update boocontext MCP environment: +- Set BOOCONTEXT_TYPE_INJECT_NPX=@nick-vi/type-inject-mcp in docker-compose.yml or the boocontext MCP process env +- Verify the hardcoded path /opt/forks/type-inject/packages/mcp/dist/index.js is no longer used +**Verification**: boocontext_types tool works without /opt/forks/type-inject existing. + +### 7. Add graceful degradation +Update boocontext child-server.ts or the tool wrappers: +- On child MCP server failure (tree-sitter-analyzer or type-inject), retry once then fall back to codesight-only result +- Return partial result with a warning message instead of hard error +- Log the child failure for observability +**Verification**: Unplug tree-sitter-analyzer → boocontext tools still return codesight-level results with a warning. + +## Phase 3: Wiki + Scanning (2 tasks) + +### 8. Add get_wiki_article tool +Create apps/server/src/services/tools/codecontext/get_wiki_article.ts +- Wraps codesight --wiki output via boocontext MCP +- Schema: project path, article name (string) +- Cache generated wiki articles to .codesight/ directory +- First request generates the wiki; subsequent requests read cache +- Register in ALL_TOOLS +**Verification**: First call generates wiki; second call reads cache. Article content matches project structure. + +### 9. Enable DCP compression on get_codebase_overview +Update get_codebase_overview.ts to pass compress: true for large projects +- Adds configurable compress toggle (default: auto — compress when >50 files) +- Matches boocontext_map's DCP pattern +**Verification**: Large projects return compressed output under truncation limit. + +## Phase 4: Teardown (future, 1 task) + +### 10. Deprecate Go sidecar +After all tools verified on boocontext MCP: +- Mark codecontext Go sidecar as deprecated in docker-compose.yml comment +- Add deprecation notice to codecontext/README.md +- Remove from docker-compose.yml services block in a future batch +- Remove codecontext/Dockerfile +- Remove codecontext_client.ts Go HTTP path (keep MCP-only path) diff --git a/openspec/changes/domain3-multi-agent/.openspec.yaml b/openspec/changes/domain3-multi-agent/.openspec.yaml new file mode 100644 index 0000000..e8d4ccf --- /dev/null +++ b/openspec/changes/domain3-multi-agent/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-08 diff --git a/openspec/changes/domain3-multi-agent/proposal.md b/openspec/changes/domain3-multi-agent/proposal.md new file mode 100644 index 0000000..315c481 --- /dev/null +++ b/openspec/changes/domain3-multi-agent/proposal.md @@ -0,0 +1,51 @@ +# Domain 3 — Multi-Agent Orchestration + +**Status:** Proposed +**Depends on:** v2.7.17-orchestrator, v2.8.13-model-resolution-matcher, v2.8.17-boulder-state + +## Why + +BooCode's conductor runs sequential analysis flows on local Qwen with a static wave scheduler. Each step is a linear dependency chain. This works for simple analysis flows but misses 8 concrete improvements identified in the lift analysis (boocode-lift-analysis.md Domain 3). + +DO_WHILE loops, SWITCH branching, FORK_JOIN parallel execution, a formal task state machine, declarative DAGs, parallel batch execution, and a structured phased lifecycle would make the conductor a true multi-agent workflow engine. + +## What Changes + +### Phase 1: Flow state machine + branching (high confidence) + +1. Task state machine — Add formal states: SCHEDULED → IN_PROGRESS → COMPLETED/FAILED/TIMED_OUT with retriable flag. Currently steps just have running/completed/failed/cancelled. + +2. DO_WHILE loop step — Re-runs a sub-step until a condition is met. while(condition) guard on step definition. + +3. SWITCH branch step — cases: [{ when: condition, steps: [...] }]. First matching case executes. + +4. FORK_JOIN step — Fans out to N parallel sub-steps and synchronizes at a Join point. All branches must complete before the flow advances. + +### Phase 2: Declarative DAGs (medium confidence) + +5. JSON DAG format — Optional JSON-based flow definition alongside existing TypeScript Step[] arrays. LLM-friendly, versioned, loadable from disk. + +### Phase 3: Batch execution + lifecycle (lower confidence) + +6. Parallel batch execution — Independent tasks within a flow run concurrently. Uses existing dispatcher's task queue — launch multiple tasks at once. + +7. Structured phased lifecycle — ticket → research → plan → execute → commit → review as a template flow definition. + +## Non-Goals +- No replacement of existing flow engine — additive changes only +- No Paseo daemon integration (handled by omo-paseo-bridge) +- No inter-agent messaging (Domain 4) + +## Capabilities + +### New Capabilities +- DO_WHILE step type — repeat sub-steps until condition met +- SWITCH step type — conditional branching by case +- FORK_JOIN step type — parallel fan-out with Join sync +- Retriable task flag — auto-retry failed steps up to N times +- JSON DAG format — portable flow definitions +- Phased lifecycle template — ticket-to-review reference flow + +### Modified Capabilities +- Step kind: gains 'do_while', 'switch', 'fork_join' +- Flow runner: wave scheduler handles branching + parallel branches diff --git a/openspec/changes/domain3-multi-agent/tasks.md b/openspec/changes/domain3-multi-agent/tasks.md new file mode 100644 index 0000000..8e9ba2f --- /dev/null +++ b/openspec/changes/domain3-multi-agent/tasks.md @@ -0,0 +1,57 @@ +# Tasks — Domain 3: Multi-Agent Orchestration + +## Phase 1 (4 tasks) + +### 1. Task state machine +Add formal states to Step: +- Read ./apps/coder/src/services/flow-runner.ts and ./apps/coder/src/services/flow-runner-decisions.ts +- Extend Step.type to add TIMED_OUT state +- Add retriable flag: retry_count, max_retries to Step +- Wire into flow-runner's advance/delegate path +- On failure with retriable=true: re-queue instead of failing +**Verification**: tsc --noEmit passes + +### 2. DO_WHILE loop step +- Add 'do_while' to StepKind +- Step definition: { kind: 'do_while', condition: (ctx) => boolean, steps: Step[] } +- Flow runner executes steps, re-evaluates condition, loops or advances +- Guard against infinite loops (max 100 iterations default) +**Verification**: tsc --noEmit passes + +### 3. SWITCH branch step +- Add 'switch' to StepKind +- Step definition: { kind: 'switch', cases: [{ when: (ctx) => boolean, steps: Step[] }] } +- Flow runner evaluates cases in order, executes first matching case's steps +- Default/else case support +**Verification**: tsc --noEmit passes + +### 4. FORK_JOIN step +- Add 'fork_join' to StepKind +- Step definition: { kind: 'fork_join', branches: Step[][], joinStrategy: 'all' | 'any' } +- Flow runner dispatches all branches in parallel via existing task queue +- On completion, gathers results and passes to join handler +**Verification**: tsc --noEmit passes + +## Phase 2 (1 task) + +### 5. JSON DAG format +- Define JSON schema for flow DAG: nodes, edges, conditions +- Create DAG loader that produces Step[] from JSON +- Add validation for cycles, unreachable nodes, missing deps +- Parallel to existing TypeScript Step[] arrays — not a replacement +**Verification**: tsc --noEmit passes. Load a sample JSON DAG and verify it produces correct Step[]. + +## Phase 3 (2 tasks) + +### 6. Parallel batch execution +- Modify flow runner to dispatch independent steps concurrently +- Use Promise.all for independent steps in the same wave +- Respect dependency ordering +**Verification**: tsc --noEmit passes. Two independent steps with different durations finish concurrently. + +### 7. Phased lifecycle template +- Create conductor/flows/lifecycle.ts with 6-phase template: + ticket → research → plan → execute → commit → review +- Each phase is a step with agent dispatch + output validation +- Users can fork and customize +**Verification**: tsc --noEmit passes. Flow definition renders correct Step[]. diff --git a/openspec/changes/impeccable-wave/design.md b/openspec/changes/impeccable-wave/design.md new file mode 100644 index 0000000..f8d8c49 --- /dev/null +++ b/openspec/changes/impeccable-wave/design.md @@ -0,0 +1,300 @@ +# Design Decisions — Impeccable Wave + +## 1. Motion Token System + +### Approach + +Define CSS custom properties for motion in `apps/web/src/styles/globals.css`, then reference from components. This is the standard pattern (CSS custom properties) that integrates natively with Tailwind v4's `@theme` directive. + +### Token Schema + +```css +@theme { + /* Durations */ + --duration-instant: 0ms; + --duration-fast: 100ms; + --duration-normal: 150ms; + --duration-slow: 200ms; + --duration-deliberate: 300ms; + + /* Easing curves */ + --ease-enter: cubic-bezier(0.22, 1, 0.36, 1); /* entrances, button press */ + --ease-move: cubic-bezier(0.25, 1, 0.5, 1); /* slides, drawers, panels */ + --ease-hover: ease; /* hover states, color transitions */ + --ease-emerge: cubic-bezier(0.165, 0.84, 0.44, 1); /* modals, popovers scaling in */ + --ease-exit: cubic-bezier(0.4, 0, 0.2, 1); /* quick dismissals */ + + /* Reduced motion override */ + --motion-reduce-transform: none; + --motion-reduce-opacity: none; +} +``` + +The `.dark` block or `@media (prefers-reduced-motion: reduce)` overrides `--motion-reduce-*` to `none` and sets duration overrides to `0ms`. + +### Usage pattern + +```tsx +// Before +className="transition-colors duration-150" + +// After +className="transition-colors duration-[var(--duration-fast)] ease-[var(--ease-hover)]" +``` + +For framer-motion, reference same tokens via JS constants in `lib/motion.ts`. + +## 2. Animation Library Choice: framer-motion + +### Why framer-motion over alternatives + +| Feature | framer-motion | motion (standalone) | CSS-only | +|---------|--------------|---------------------|----------| +| Layout animations | `layout` prop | `layout` prop | ❌ Requires JS | +| AnimatePresence | ✅ | ✅ | ❌ | +| Spring physics | ✅ | ✅ | ❌ (CSS steps approximation) | +| Gesture handling | `drag`, `whileHover`, `whileTap` | Same API | Partial | +| Stagger children | `staggerChildren` variant | Same | ❌ | +| Bundle size | ~30KB gzipped | ~10KB gzipped | 0KB | +| React 18 compat | ✅ 12.x | ✅ 12.x | N/A | + +**Decision:** Use `framer-motion` (full-featured). The `motion` standalone package (same API, smaller bundle) is a future migration if bundle size becomes a concern. For this wave, framer-motion's broader ecosystem and established React 18 support are the safe choice. + +### Bundle impact + +`framer-motion` v12 adds ~30KB gzipped. Route-level code splitting in Phase 5 will offset this by reducing the initial bundle by an estimated 100-200KB (the current bundle includes all 6 page components). Net bundle impact: **~70KB reduction** after code splitting. + +## 3. Workspace Pane Animation Strategy + +### Current architecture + +The workspace grid (`Workspace.tsx`) renders panes from `panes[]` array in a CSS grid. Pane open/close is a state mutation — panes appear/disappear instantly. Reorder is handled by drag (HTML5 native). + +### Animated approach + +``` +┌─────────────────────────────────┐ +│ Workspace Grid │ +│ ┌──────────┐ ┌──────────┐ │ +│ │ Chat │ │ Terminal │ │ +│ │ (enter: │ │ (enter: │ │ +│ │ slideL) │ │ slideR) │ │ +│ └──────────┘ └──────────┘ │ +│ ┌──────────────────────────┐ │ +│ │ Coder (enter: slideUp) │ │ +│ └──────────────────────────┘ │ +└─────────────────────────────────┘ +``` + +**Key decisions:** +1. **Pane open**: Slide in from the nearest edge (left-pane opens from left, right-pane from right, bottom-pane from bottom) with fade. 200ms `ease-out`. +2. **Pane close**: Quick fade (100ms) — exit should be faster than enter per animation best practices. +3. **Pane reorder**: `layout` prop on each pane wrapper. framer-motion handles the FLIP calculation automatically. +4. **Tab switch**: Content crossfade (150ms). Not a slide — tabs are content replacement, not navigation. +5. **Split pane**: New pane emerges from the split point (scale + fade, `transform-origin` at the split edge). + +### Implementation + +```tsx +// Workspace.tsx — pane wrapper + + + +``` + +`AnimatePresence` wraps the pane grid so exiting panes animate out before DOM removal. + +## 4. Message Stagger Entrance + +### Approach + +Use framer-motion's `staggerChildren` variant on the message list container, with individual item variants for the entrance. + +```tsx +const containerVariants = { + initial: {}, + animate: { + transition: { + staggerChildren: 0.03, // 30ms between messages + }, + }, +}; + +const messageVariants = { + initial: { opacity: 0, y: 8 }, + animate: { opacity: 1, y: 0, transition: { duration: 0.2, ease: 'easeOut' } }, +}; +``` + +**Edge cases:** +- **New messages during streaming**: Only animate the first appearance of a message. Once visible, subsequent content updates (streaming deltas) should not re-animate. Track via `data-animated` attribute. +- **Scroll-anchored messages**: When the user is scrolled to bottom and a new message arrives, animate it in. When the user has scrolled up, suppress animation to avoid disorientation. +- **Initial load**: All existing (already-loaded) messages appear without animation. Only NEW messages animate in. +- **Reasoning block**: The collapsible reasoning block uses a spring-based height transition instead of the grid-rows CSS trick. + +## 5. Keyboard Shortcuts Dialog + +### Invocation + +`Cmd+/` or `?` in the workspace. A modal overlay triggered via a new keyboard handler in `Session.tsx`. + +### Content + +Grouped into sections: + +``` +Navigation │ Cmd+` Terminal │ Cmd+T New Terminal + │ Cmd+C New Chat │ Cmd+W Close Pane + │ Tab / Shift+Tab │ Cycle panes + +Chat │ Enter Send │ Shift+Enter Newline + │ Cmd+[ 1-9 ] │ Jump to tab + │ @ │ Mention file + │ / │ Slash commands + +Terminal │ Cmd+Shift+C │ Copy + │ Cmd+F │ Search + │ Esc │ Close search + +General │ Cmd+/ │ This panel + │ ? │ This panel +``` + +### Data source + +Derived from existing keyboard handlers in `Session.tsx` and component files. Centralized into a single `KEYBOARD_SHORTCUTS` constant in `lib/keyboard-shortcuts.ts`. + +## 6. Undo Toast System + +### Approach + +Extend the existing Sonner toast system. Destructive actions call `toast.success(message, { action: { label: 'Undo', onClick: rollback } })`. Each action needs a rollback function. + +### Implementation pattern + +```typescript +// In component with destructive action +async function deleteChat(chatId: string) { + const previousMessages = await api.chats.messages.list(chatId); // snapshot + await api.chats.remove(chatId); // optimistic delete + + toast.success('Chat deleted', { + action: { + label: 'Undo', + onClick: async () => { + await api.chats.restore(chatId, previousMessages); // rollback + }, + }, + duration: 5000, // 5s to undo + }); +} +``` + +### Scope + +Start with 3 high-impact undo actions: +1. **Delete chat** — snapshot messages, restore on undo +2. **Archive project** — toggle unarchive +3. **Close pane** — reopen via `closedPaneStack` (already exists) + +## 7. Font Pairing Strategy + +### Goals +- Distinctive but still technical/developer-appropriate +- Must pair well with JetBrains Mono (code font stays) +- Must support variable weights for the Tailwind v4 font-weight system +- Must be available on Google Fonts (self-hosted via `@fontsource`) + +### Candidate pairs + +| Pair | Vibe | Google Fonts | Variable | +|------|------|-------------|----------| +| **Instrument Sans** + JetBrains Mono | Modern, geometric, technical | ✅ | ✅ | +| **Public Sans** + JetBrains Mono | Clean, neutral, authoritative | ✅ | ✅ | +| **Satoshi** + JetBrains Mono | Bold, editorial, confident | ❌ (paid) | ✅ | +| **Onest** + JetBrains Mono | Rounded, approachable but tech | ✅ | ✅ | +| **Plus Jakarta Sans** + JetBrains Mono | Refined, slightly warmer | ✅ | ✅ | + +**Decision:** Defer final choice to implementation. Replace Inter with chosen font via `@fontsource` import in `main.tsx` + update the `--font-sans` token in `globals.css`. + +## 8. Code Splitting Architecture + +### Current +```tsx +// App.tsx — static imports, all loaded upfront +import Home from '@/pages/Home' +import Project from '@/pages/Project' +import Session from '@/pages/Session' +import Settings from '@/pages/Settings' +import Analytics from '@/pages/Analytics' +import Results from '@/pages/Results' +``` + +### Target +```tsx +// App.tsx — dynamic imports with named exports +const Home = lazy(() => import('@/pages/Home')) +const Project = lazy(() => import('@/pages/Project')) +const Session = lazy(() => import('@/pages/Session')) +const Settings = lazy(() => import('@/pages/Settings')) +const Analytics = lazy(() => import('@/pages/Analytics')) +const Results = lazy(() => import('@/pages/Results')) + +// Single Suspense boundary wrapping all routes + + }>} /> + ... + +``` + +### Chunk strategy + +- **Vendor chunk**: `react`, `react-dom`, `react-router-dom` → stable, infrequently changed +- **UI chunk**: `framer-motion`, `lucide-react`, `react-markdown`, `shiki` → large UI libs +- **Per-page chunks**: Each page component loads on demand + +Configured via `vite.config.ts` `build.rollupOptions.output.manualChunks`. + +## 9. Empty State Pattern + +### Standard template + +```tsx +// New reusable component +function EmptyState({ icon, title, description, action }: EmptyStateProps) { + return ( +
+
{icon}
+

{title}

+ {description && ( +

{description}

+ )} + {action && } +
+ ); +} +``` + +Replace current text-only empty states in `Home.tsx` (no projects), `Project.tsx` (no sessions), and `SessionLandingPage.tsx` (no chats). + +## 10. StatusDot Animation Fix + +### Current +```tsx +// No transform-origin set — rotation around center creates blur + +``` + +### Target +```tsx +// Explicit transform-origin + motion-safe guard + +``` + +The `size-0.5` dots orbit at 12px diameter. `transform-origin: center 6px` centers rotation at the orbit midpoint. diff --git a/openspec/changes/impeccable-wave/proposal.md b/openspec/changes/impeccable-wave/proposal.md new file mode 100644 index 0000000..c8840b4 --- /dev/null +++ b/openspec/changes/impeccable-wave/proposal.md @@ -0,0 +1,78 @@ +# The Data Terminal — Animation, Polish & Perceptual Design Wave + +## Why + +BooCode's frontend is functionally complete but perceptually unfinished. The gap analysis against `impeccable`-level quality identified 8 critical gaps holding back the "cutting edge, modern, bold" identity you want: + +1. **No layout animations** — The workspace pane grid (core UX) has zero transitions. Panes snap open/closed/reordered. The interface feels static where it should feel alive. +2. **No JS animation library** — Pure CSS only. No springs, no stagger entrances, no shared-element transitions, no interruptible motion. The "Data Terminal" should hum and respond. +3. **Messages teleport in** — No stagger entrance on the message list. Every message appears instantly, robbing the conversation of rhythm. +4. **Motion timing is ad-hoc** — 22 files hardcode durations independently. No shared easing curves, no timing tokens. +5. **No keyboard shortcut reference** — Comprehensive shortcuts exist (C, T, W, Tab, 1-9, backtick) but are invisible. Help/Docs scored 2/40 in the critique. +6. **Font choice is safe** — Inter is the shadcn-default AI slop tell. A bolder pairing would elevate the entire identity. +7. **No route-level code splitting** — Entire app loads in one bundle. Per Vite best practices, this is CRITICAL. +8. **No undo system** — Destructive actions confirm but don't offer recovery. Undo toast is standard UX. +9. **Empty states are text-only** — "No projects" / "No sessions" with no illustration or guidance. + +This batch addresses all of these in a structured, layered approach — from foundational motion tokens through to perceptual polish. + +## Principles + +1. **Motion is telemetry, not decoration.** Every animation communicates state. If it doesn't serve understanding, it doesn't exist. +2. **The Data Terminal should feel alive.** The workspace, messages, and tool calls should have presence — appearing, responding, transitioning with purpose. +3. **Cutting edge does not mean gratuitous.** Animations serve the power user. Fast, interruptible, under 300ms. No bounce, no elastic, no choreographed page-load sequences. +4. **Accessibility is not optional.** Every animation has a `prefers-reduced-motion` alternative. Every interactive element is keyboard-accessible. Empty states teach. +5. **Consistency over surprise.** Motion tokens, easing curves, and timing are shared across every component. Same feel, everywhere. + +## What Changes + +### Phase 1 — Foundation (motion tokens + animation library) + +Install `framer-motion`, define motion design tokens (`--duration-*`, `--ease-*`, `--motion-*`) in `globals.css`, replace all hardcoded timing values across components with the token system. This is the prerequisite for everything else. + +### Phase 2 — Workspace (pane grid animation) + +Animate the multi-pane workspace: pane open/close transitions (slide + fade), pane reorder via layout animation, tab switch transitions. This is the core UX — the single most visible animation gap. + +### Phase 3 — Chat (message animation + stagger) + +Add stagger entrance to the message list. Messages appear in sequence (30ms apart, max 300ms total). New messages scroll-anchor with a subtle entrance. Tool calls expand with spring physics. Reasoning block opens with a smooth height transition. + +### Phase 4 — Visual identity (typography + empty states) + +Swap Inter for a bolder, more distinctive font pairing (e.g. Public Sans + JetBrains Mono, or Instrument Sans + JetBrains Mono — something unexpected but technical). Replace text-only empty states with illustrated CTAs. Add noise/grain texture layer to the background for atmosphere. + +### Phase 5 — UX depth (undo, keyboard reference, code splitting) + +Add undo toasts for destructive actions. Build a keyboard shortcut reference dialog (`Cmd+/` or `?`). Add route-level `React.lazy()` code splitting with `Suspense` boundaries. + +### Phase 6 — Polish pass (critique close-out) + +Fix the P0-P3 issues from the impeccable critique: rounding consistency (done), "tap" label removal, bubble side-stripe, StatusDot transform-origin, ChatInput toolbar density, CompactCard popover overflow. Run a follow-up critique to verify score improvement. + +## New Capabilities + +- `motion-token-system`: CSS custom properties for durations, easing curves, and motion presets in `globals.css`. Every component references tokens, not hardcoded values. +- `pane-layout-animations`: Workspace pans animate open/close/reorder via framer-motion `AnimatePresence` + `layout` prop. +- `message-stagger-entrance`: Message list items stagger in at 30ms intervals on first render and on new messages. +- `keyboard-shortcuts-dialog`: `Cmd+/` panel listing all keyboard shortcuts, grouped by category (navigation, chat, terminal, general). +- `undo-toast-system`: Sonner toast with "Undo" action for destructive operations (delete chat, archive project, close pane). +- `route-code-splitting`: All 6 page components wrapped in `React.lazy(() => import(...))` with Suspense fallbacks. +- `bold-font-pairing`: Replace Inter with a distinctive sans (e.g. Instrument Sans, Public Sans, or Satoshi) while keeping JetBrains Mono for code. +- `illustrated-empty-states`: New user/project/session empty states with inline SVG illustrations + CTA buttons. + +## Modified Capabilities + +- `ChatInput-toolbar`: Secondary controls (Flows, SlashCommands, WebSearch) moved to overflow menu. Toolbar density reduced from 7→4 items. +- `ToolCallGroup`: "tap" label removed — chevron rotation is sufficient. +- `MessageBubble-actions`: Share popover uses portal-based positioning to prevent viewport overflow. +- `StatusDot`: `transform-origin: center` added to streaming spinner + `prefers-reduced-motion` guard. +- `ember-user-bubble`: Side-stripe accent replaced with solid top-border accent for design system compliance. +- `Dynamic-imports`: App.tsx routes switch from static imports to `React.lazy()` with Suspense boundaries. + +## Impact + +- **apps/web**: 20–30 files modified across components, styles, and pages. New files: `lib/motion.ts` (token constants + helpers), `components/KeyboardShortcutsDialog.tsx`, `components/UndoToast.tsx`, empty state SVG illustrations. +- **dependencies**: `framer-motion` added to `apps/web/package.json`. +- **No server/coder/schema changes**: Purely a frontend batch. No API changes, no new endpoints, no DB migrations. +- **Risk areas**: framer-motion bundle size (~30KB gzipped), animation performance on low-end devices, `prefers-reduced-motion` coverage completeness. diff --git a/openspec/changes/impeccable-wave/tasks.md b/openspec/changes/impeccable-wave/tasks.md new file mode 100644 index 0000000..fef6575 --- /dev/null +++ b/openspec/changes/impeccable-wave/tasks.md @@ -0,0 +1,340 @@ +# Implementation Tasks — Impeccable Wave + +**Dependency map:** Tasks within a phase are sequential (foundation before use). Phases 2–5 can run in parallel after Phase 1 completes. Phase 6 (polish) is last. + +``` +Phase 1 (Foundation) + ├── 1.1 Motion tokens + ├── 1.2 Install framer-motion + └── 1.3 Create lib/motion.ts + │ + ├──► Phase 2 (Workspace) ─── 2.1–2.4 + ├──► Phase 3 (Chat) ──────── 3.1–3.4 + ├──► Phase 4 (Identity) ──── 4.1–4.3 + └──► Phase 5 (UX Depth) ──── 5.1–5.5 + │ + └──► Phase 6 (Polish) ─── 6.1–6.5 +``` + +--- + +## Phase 1 — Foundation (prerequisite for all visual animation) + +### 1.1 Define motion design tokens in globals.css + +**File:** `apps/web/src/styles/globals.css` + +Add `@theme` block with duration, easing, and motion-preset custom properties. Include `motion-safe:` and `motion-reduce:` variants. + +**Checklist:** +- [ ] `--duration-instant: 0ms`, `--duration-fast: 100ms`, `--duration-normal: 150ms`, `--duration-slow: 200ms`, `--duration-deliberate: 300ms` +- [ ] `--ease-enter`, `--ease-move`, `--ease-hover`, `--ease-emerge`, `--ease-exit` with cubic-bezier values +- [ ] `--motion-reduce-transform: none` with `prefers-reduced-motion` override +- [ ] Verify no existing hardcoded values conflict + +**Verification:** `lsp_diagnostics` clean, `pnpm -C apps/web build` passes. + +### 1.2 Install framer-motion + +```bash +pnpm -C apps/web add framer-motion +``` + +**Verification:** `pnpm -C apps/web build` exits 0. Bundle size: check `pnpm -C apps/web build --report` for framer-motion contribution. + +### 1.3 Create `lib/motion.ts` — JS motion constants + +**File:** `apps/web/src/lib/motion.ts` + +Export framer-motion `Transition` config objects referencing the CSS tokens as a single source of truth: + +```typescript +export const transitions = { + fast: { type: 'spring' as const, stiffness: 400, damping: 30 }, + normal: { type: 'spring' as const, stiffness: 300, damping: 25 }, + slow: { type: 'spring' as const, stiffness: 200, damping: 20 }, + enter: { duration: 0.2, ease: [0.22, 1, 0.36, 1] }, + exit: { duration: 0.1, ease: [0.4, 0, 0.2, 1] }, +}; + +export const variants = { + staggerContainer: { animate: { transition: { staggerChildren: 0.03 } } }, + fadeSlideIn: { initial: { opacity: 0, y: 8 }, animate: { opacity: 1, y: 0 } }, + scaleIn: { initial: { opacity: 0, scale: 0.95 }, animate: { opacity: 1, scale: 1 } }, +}; +``` + +**Verification:** Importable in any component. `lsp_diagnostics` clean. + +--- + +## Phase 2 — Workspace Pane Animation (core UX) + +### 2.1 Add layout animation to pane wrapper + +**File:** `apps/web/src/components/Workspace.tsx` + +Wrap each pane in `` with enter/exit animations. Use `AnimatePresence` mode="popLayout" around the pane grid. Panes enter with scale+fade from their edge, exit with quick fade. + +**Edge cases:** +- First render: no animation for existing panes (use `initial={false}`) +- Single pane with no siblings: no layout shift, skip animation +- Mobile single-column: simplified animation (no edge-direction, just fade) + +**Verification:** Open tab, close tab, split pane. Observe smooth transitions. Verify `prefers-reduced-motion` disables all animation. + +### 2.2 Animate tab switch in ChatTabBar + +**File:** `apps/web/src/components/ChatTabBar.tsx` + +Add `layout` prop to tab strip items for reorder animation. New tabs appear with `scaleIn` variant. + +**Verification:** Drag-reorder tabs, close tabs, observe smooth reflow. + +### 2.3 Animate pane split + +**File:** `apps/web/src/components/Workspace.tsx` (split handler) + +When splitting a pane, the new pane emerges from the split point with `transform-origin` at the split edge. Uses `scaleIn` variant. + +**Verification:** Split any pane. New pane scales in from correct origin. + +### 2.4 Add pane drag-reorder animation + +**File:** `apps/web/src/components/Workspace.tsx` + +Enable framer-motion's `reorder` group on the pane grid. Replace HTML5 native drag with framer-motion `Reorder.Group` for spring-animated reordering. + +**Verification:** Drag panes to reorder. Motion is spring-based, not hard snap. Reorder is committed on drop. + +--- + +## Phase 3 — Chat Animation (messages + tool calls) + +### 3.1 Add stagger entrance to MessageList + +**File:** `apps/web/src/components/MessageList.tsx` + +Wrap the message list in ``. Each message `` staggers in at 30ms intervals. + +**Edge cases:** +- Initial load: All existing messages should NOT animate (use `initial={false}` on the container) +- Streaming messages: Only the latest assistant message should animate in when it starts streaming +- Scroll-anchored: When user is at bottom, animate. When scrolled up, suppress animation +- Tool call groups: Animate as a single unit (not individual tool calls) + +**Verification:** Open a chat with previous messages — no animation on load. Send a new message — it animates in. Scroll up and send — no animation. + +### 3.2 Animate streaming messages (delta updates) + +**File:** `apps/web/src/components/MessageBubble.tsx` + +Add a subtle pulse/glow animation on the message border while `status === 'streaming'`. The glow transitions from ember orange pulse to steady state on completion. + +```tsx + +``` + +**Edge cases:** Must stop immediately on completion. Must respect reduced motion (no pulse, just steady border). + +**Verification:** Send a message, observe ember pulse. Message completes, pulse stops. Reduce motion enabled — no pulse. + +### 3.3 Add spring animation to tool call expansion + +**File:** `apps/web/src/components/ToolCallLine.tsx` + +Replace the CSS grid-rows collapse with framer-motion `AnimatePresence` + spring height transition. The expanded content slides down from the collapsed header. + +```tsx + + {expanded && ( + + )} + +``` + +**Verification:** Click tool call to expand — smooth spring transition. Click to collapse — quick exit. Multiple rapid clicks — interruptible. + +### 3.4 Animate reasoning block open + +**File:** `apps/web/src/components/MessageBubble.tsx` (ReasoningBlock) + +Same pattern as tool call expansion — spring-based height transition instead of the current grid-rows CSS trick. Reasoning icon rotates (ChevronRight → ChevronDown) with a spring twist. + +**Verification:** Click reasoning block header — smooth expand. Content appears without delay. Collapse — spring exit. + +--- + +## Phase 4 — Visual Identity (fonts + empty states + atmosphere) + +### 4.1 Swap font pairing + +**Files:** `apps/web/src/main.tsx`, `apps/web/src/styles/globals.css` + +- Choose new sans font (e.g. Instrument Sans or Public Sans from Google Fonts) +- Add `@fontsource-variable/instrument-sans` import in `main.tsx` +- Update `--font-sans` in `globals.css` to `"Instrument Sans Variable", ...` +- Verify no layout shift during font swap (font-display: swap) + +**Verification:** All UI text renders in new font. Code renders in JetBrains Mono (unchanged). No flash of invisible text (FOIT). No CLS. + +### 4.2 Create reusable EmptyState component + +**File:** `apps/web/src/components/EmptyState.tsx` + +A reusable component as designed in `design.md` §9. Accepts `icon` (React node), `title`, `description?`, `action?`. + +**Edge cases:** +- No icon provided — use a default muted circle +- Very long description — truncate with `line-clamp-2` +- Icon is text node — wrap in decorative container + +### 4.3 Replace text-only empty states with illustrated versions + +**Files:** `apps/web/src/pages/Home.tsx`, `apps/web/src/pages/Project.tsx`, `apps/web/src/components/SessionLandingPage.tsx` + +Replace "No projects" / "No sessions" / "No chats" plain text with `` components. Use inline SVGs for illustrations (not external assets). + +**Illustrations needed:** +- Home empty: Terminal cursor + code bracket icon +- Project empty: Folder + glow dot +- Session empty: Chat bubble + ember spark + +**Verification:** Navigate to each page with no data. See illustrated empty state. Action buttons work. + +--- + +## Phase 5 — UX Depth (undo, keyboard reference, code splitting) + +### 5.1 Create keyboard shortcut constant + dialog + +- **File (constants):** `apps/web/src/lib/keyboard-shortcuts.ts` +- **File (dialog):** `apps/web/src/components/KeyboardShortcutsDialog.tsx` + +Define all shortcuts as typed constants. Dialog renders a modal with grouped sections. Triggered by `Cmd+/` or `?` in `Session.tsx`. Shortcut keys render with `` styling. + +**Edge cases:** Mobile — shortcuts dialog shows touch-equivalent actions. macOS vs Windows — show `Cmd` vs `Ctrl` labels. + +**Verification:** Press `Cmd+/` — dialog opens. Esc closes. Keys are accurate. `kbd` elements are keyboard-focusable. + +### 5.2 Add undo for delete chat + +**File:** `apps/web/src/components/MessageBubble.tsx` (delete handler) + +Before calling `api.messages.remove`, snapshot the messages. Wrap the delete call in an undo toast. On undo, restore via existing API. Timeout: 5 seconds. + +### 5.3 Add undo for archive project + +**File:** `apps/web/src/components/ProjectSidebar.tsx` (archive handler) + +Same pattern as delete chat. Archive → toast with Undo → unarchive on click. + +### 5.4 Add route-level code splitting + +**File:** `apps/web/src/App.tsx` + +Replace static `import Home from '@/pages/Home'` with `const Home = lazy(() => import('@/pages/Home'))`. Add `Suspense` boundary per route with a `FullPageLoader` fallback. Configure `manualChunks` in `vite.config.ts`. + +**Checklist:** +- [ ] All 6 page components switched to `lazy()` +- [ ] `Suspense` with `FullPageLoader` (existing skeleton pattern) +- [ ] `vite.config.ts` `build.rollupOptions.output.manualChunks` for vendor + UI chunk +- [ ] Verify named exports work (if pages use `export default`) +- [ ] Build and check chunk output + +**Verification:** `pnpm -C apps/web build` produces separate chunk files. Initial page load faster (measure via DevTools network tab). + +### 5.5 Add optional suspense for heavy components + +**File:** `apps/web/src/components/MarkdownRenderer.tsx`, `apps/web/src/components/CodeBlock.tsx` + +Wrap heavy rendering (react-markdown, Shiki highlight) in `` with inline skeleton fallback. The Shiki highlighting in `CodeBlock.tsx` is async — add `lazy()` at component level. + +--- + +## Phase 6 — Polish Pass (critique close-out) + +### 6.1 Remove "tap" label from ToolCallGroup + +**File:** `apps/web/src/components/ToolCallGroup.tsx` + +Delete or comment out the `tap` label. The chevron rotation already communicates expandability. + +**Verification:** ToolCallGroup no longer shows "tap" text. Chevron still rotates. + +### 6.2 Fix StatusDot spinner origin + reduced-motion guard + +**File:** `apps/web/src/components/StatusDot.tsx` + +- Add `transform-origin: center` to the spinning dots container +- Add `motion-reduce:hidden` class to the animated dots +- Verify the static fallback is visible (a single amber dot when motion reduced) + +**Verification:** StatusDot spins smoothly (no blur). Enable reduced motion — static dot appears. + +### 6.3 Fix CompactCard share popover overflow + +**File:** `apps/web/src/components/MessageBubble.tsx` (CompactCard share handler) + +Replace `absolute right-0 top-full` with a portal-based dropdown using the existing `Dialog` component or a boundary-aware positioning utility. + +**Verification:** Share popover never renders past viewport edge, even when trigger is at right edge of message list. + +### 6.4 Fix ChatInput toolbar density + +**File:** `apps/web/src/components/ChatInput.tsx` + +Move Flows, SlashCommands, and WebSearch toggle into an overflow menu (three-dot `MoreHorizontal` button). Keep AgentPicker, Attach, ContextMeter, and Send/Stop always visible. + +**Decision:** The overflow menu uses `DropdownMenu` from shadcn (already exists). Desktop shows 4 items always visible + 3 in overflow. Mobile shows same pattern but with larger touch targets. + +**Verification:** ChatInput toolbar shows 4 items. "..." button reveals 3 more. All actions work from both positions. + +### 6.5 Fix ember user bubble side-stripe + +**File:** `apps/web/src/styles/themes/ember.css` + +Replace `border-right: 2px solid var(--primary)` with `border-top: 2px solid var(--primary)` for design system compliance. This preserves the accent distinction for user messages without violating the side-stripe ban. + +**Verification:** User messages show a top accent border instead of right accent stripe. Looks intentional, not like a leftover. + +--- + +## Final Verification + +### Build + typecheck +```bash +pnpm -C apps/server build +pnpm -C apps/web build +pnpm -C apps/web typecheck +``` + +### LSP diagnostics +```bash +lsp_diagnostics(filePath="apps/web/src", extension=".ts") +lsp_diagnostics(filePath="apps/web/src", extension=".tsx") +``` + +### Visual QA +1. Open workspace — panes animate in +2. Open chat — messages stagger in +3. Send message — streaming pulse, new message enters +4. Expand tool call — spring animation +5. Press Cmd+/ — shortcuts dialog opens +6. Delete chat — undo toast appears, restore works +7. Reduce motion — all animations stop +8. Switch to mobile viewport — no layout breakage + +### Run critique +```bash +$impeccable critique Session +``` +Expect score improvement from 35/40 to 37+/40. diff --git a/openspec/changes/streaming-codeblocks-messages-components-v2/.openspec.yaml b/openspec/changes/streaming-codeblocks-messages-components-v2/.openspec.yaml new file mode 100644 index 0000000..e8d4ccf --- /dev/null +++ b/openspec/changes/streaming-codeblocks-messages-components-v2/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-06-08 diff --git a/openspec/changes/streaming-codeblocks-messages-components-v2/design.md b/openspec/changes/streaming-codeblocks-messages-components-v2/design.md new file mode 100644 index 0000000..a74d700 --- /dev/null +++ b/openspec/changes/streaming-codeblocks-messages-components-v2/design.md @@ -0,0 +1,45 @@ +## Context + +BooCode's chat UI streams LLM responses over a per-session WebSocket. The current `useSessionStream` hook uses a flat `applyFrame` reducer that maps WS frames to a `Message[]` array. This works for the basic case but has limitations: reconnection during streaming replaces the entire message list (snapshot-refetch), frame ordering depends on implicit WS ordering, and there's no concept of channel isolation between text, tool calls, and status. + +`CodeBlock.tsx` wraps Shiki's async `codeToHtml` with basic copy and a language label. No line numbers, no diff markers, no theme switching. + +`MessageList.tsx` does three sequential passes (flatten → group → stampCapHits) on every message array. It uses framer-motion `fadeSlideIn` for all items with no virtualization — beyond ~200 messages the DOM cost is noticeable on mobile. + +The component suite (`MarkdownRenderer`, `MessageBubble`, `ToolCallLine`, `ToolCallGroup`) evolved organically with inconsistent error handling and accessibility. + +LangGraph (referenced conceptually, not as a dependency) uses typed channels with Pregel-style superstep execution, where each channel is a typed slot that receives deltas in deterministic order with checkpoints. We port the channel pattern — not the library. + +## Goals / Non-Goals + +**Goals:** +- Channel-based streaming reducer with typed deltas and ordered frame processing +- CodeBlock with line numbers, diff gutter, theme toggle, word-wrap, collapsible, inline copy progress +- MessageList with virtualized rendering for 500+ messages and smoother animations +- Error boundaries on all render-heavy components so one failure doesn't crash the list +- Keyboard-navigable tool calls in ToolCallLine/ToolCallGroup +- Consistent `aria-label` protocol across all action buttons + +**Non-Goals:** +- Replacing Shiki with another highlighter (stays) +- Porting langgraphjs as a runtime dependency (pattern only) +- Server-side streaming changes outside the WS frame protocol +- Adding undo/redo for message mutations +- Full i18n of the UI + +## Decisions + +1. **Channel-based reducer over flat pattern matching.** Each WS frame type maps to a channel (`text`, `tool_call`, `tool_result`, `status`, `error`). Channels process independently but merge into a single `StreamState` via deterministic ordering rules. This allows mid-stream reconnection without full snapshot — missed deltas can be replayed per-channel. + +2. **react-virtuoso over react-window.** `react-virtuoso` handles variable-height items natively (markdown renders at unpredictable heights), supports sticky headers, and has a built-in `followOutput` mode for streaming chat. `react-window` requires fixed heights or complex measurement. + +3. **Diff mode via CSS gutter classes, not Shiki transformers.** Shiki's `codeToHtml` with `transformers` adds diff line metadata, but the current async-only pipeline makes it hard to cache both diff and plain renders. Instead, `CodeBlock` will detect a `diff-` language prefix, parse `+`/`-` line markers, and render gutter decorations via CSS classes — simpler and works with any Shiki theme. + +4. **Error boundaries at two granularities.** `MarkdownRenderer` and `CodeBlock` each get a `` wrapper that catches render errors and shows a "Rendering failed" fallback + retry button. A top-level `` catches everything else and shows a compact "Something went wrong" bar. This prevents Shiki failures from taking down the entire transcript. + +## Risks / Trade-offs + +- **[Risk] Virtualized list breaks streaming cursor.** When a new message arrives mid-stream, react-virtuoso's `followOutput` may skip ahead if the user is scrolled up reading history. **Mitigation**: `followOutput="auto"` only scrolls when the user is within a threshold of the bottom; manual `scrollToIndex` on new assistant turns when `isNearBottomRef` is true. +- **[Risk] Channel ordering complexity.** Multiple channels processing frames out of order could produce inconsistent state (e.g., `tool_result` channel fires before the `tool_call` channel's frame arrives). **Mitigation**: Each channel frame carries a monotonic `seq` counter from the server; the reducer buffers out-of-order frames per-channel and flushes only when `seq` is contiguous. +- **[Risk] Shiki async loading delay.** First paint for a code block waits for Shiki's WASM + theme load. **Mitigation**: Keep the existing `Suspense` + skeleton fallback pattern; preload Shiki in the app shell via `import('shiki')` at bootstrap. +- **[Trade-off] No code-block caching.** Every re-render re-runs `codeToHtml`. For large files this is wasteful. A `Map` LRU cache (max 50 entries) avoids recomputation without adding a dependency. diff --git a/openspec/changes/streaming-codeblocks-messages-components-v2/proposal.md b/openspec/changes/streaming-codeblocks-messages-components-v2/proposal.md new file mode 100644 index 0000000..6d26af0 --- /dev/null +++ b/openspec/changes/streaming-codeblocks-messages-components-v2/proposal.md @@ -0,0 +1,32 @@ +## Why + +The chat UI is the core surface of BooCode, but its streaming layer, code blocks, message list, and shared components have accumulated organic complexity without a unified overhaul. Streaming uses a basic `useSessionStream` + `applyFrame` reducer with manual exponential backoff; `CodeBlock.tsx` lacks line numbers, diff highlighting, and multi-theme support; `MessageList.tsx`'s three-pass pre-render (flatten → group → stampCapHits) works but doesn't virtualize or animate smoothly at scale; and shared components (`MarkdownRenderer`, `MessageBubble`, `ChatInput`, `ToolCallLine`) lack consistent error boundaries, loading skeletons, and accessibility. These together create the highest-impact surface for UX improvement. + +## What Changes + +- **Streaming layer v2**: Port LangGraph-inspired channel-based state management patterns into the `useSessionStream` reducer architecture — typed channel deltas, predictable frame ordering, interrupt/resume-aware message grafting, and reconnection that preserves mid-stream state instead of a full snapshot-refetch. +- **CodeBlock overhaul**: Add line numbers, diff-highlight mode (gutter markers for `+`/`-` lines), multi-theme toggle (light/dark), word-wrap toggle, collapsible long blocks, and inline copy with progress feedback. Shiki stays as the highlighter. +- **MessageList v2**: Virtualized rendering via `react-virtuoso` for long transcripts, smoother streaming entrance animations (staggered fade-slide per message), smarter tool-call grouping with collapse/expand per group, and message pin/bookmark support (local-only, persisted in URL hash). +- **Component consistency pass**: Error boundaries on `MarkdownRenderer` and `CodeBlock` so Shiki failures don't crash the entire message list; loading skeletons for streaming messages; accessible keyboard navigation in `ToolCallLine` and `ToolCallGroup`; consistent `aria-label` protocol across all `ActionRow` buttons. + +## Capabilities + +### New Capabilities +- `channel-streaming`: Channel-based streaming reducer inspired by LangGraph Pregel — typed channel deltas (`text`, `tool_call`, `tool_result`, `status`) map to structured states, interrupt/resume-aware mid-stream reconnection, and predictable frame ordering via monotonic seq counters. +- `code-block-pro`: Line numbers, diff gutter markers, multi-theme toggle, word-wrap toggle, collapsible blocks (≥30 lines auto-collapse), inline copy with progress. +- `message-list-v2`: Virtualized rendering for 500+ message transcripts, smarter tool-call grouping with per-group expand/collapse, stagger entrance animations, message pin/bookmark (local via URL hash). +- `component-hardening`: Error boundaries on `MarkdownRenderer`/`CodeBlock`, `Suspense` loading skeletons for streaming content, keyboard-navigable tool calls (`Tab`/`Enter`/`Arrow`), consistent `aria-label` convention. + +### Modified Capabilities +*(None — no existing spec files in `openspec/specs/` are changing)* + +## Impact + +- **`apps/web/src/hooks/useSessionStream.ts`**: Core rewrite from flat `applyFrame` reducer to channel-based dispatch with typed channel deltas. +- **`apps/web/src/components/CodeBlock.tsx`**: Full rewrite — line numbering, diff mode, theme toggle, collapsible. +- **`apps/web/src/components/MessageList.tsx`**: Virtualized rendering, new entrance animation system, smarter grouping. +- **`apps/web/src/components/MessageBubble.tsx`**: Error boundaries, skeleton loading for streaming messages. +- **`apps/web/src/components/MarkdownRenderer.tsx`**: Wrap in error boundary, add `Suspense` fallback. +- **`apps/web/src/components/ToolCallLine.tsx`**, **`ToolCallGroup.tsx`**: Keyboard navigation, aria-labels. +- **`apps/web/package.json`**: New dep `@virtuoso-dev/react-virtuoso` for virtualized list. +- **`@boocode/contracts`**: New channel-delta frame types in `ws-frames.ts` schema for the streaming v2 protocol. diff --git a/openspec/changes/streaming-codeblocks-messages-components-v2/specs/channel-streaming/spec.md b/openspec/changes/streaming-codeblocks-messages-components-v2/specs/channel-streaming/spec.md new file mode 100644 index 0000000..679cf6e --- /dev/null +++ b/openspec/changes/streaming-codeblocks-messages-components-v2/specs/channel-streaming/spec.md @@ -0,0 +1,39 @@ +## ADDED Requirements + +### Requirement: Channel-based streaming reducer + +The streaming layer SHALL dispatch WS frames into typed channels (`text`, `tool_call`, `tool_result`, `status`, `error`) instead of a flat `applyFrame` pattern match. Each channel SHALL produce deltas independently but merge into a single `StreamState` via a deterministic ordering function. + +#### Scenario: Text delta dispatched to text channel +- **WHEN** a `delta` frame with `content` arrives +- **THEN** the text channel accumulates the chunk and the merged state appends it to the target message's content + +#### Scenario: Tool call dispatched to tool_call channel +- **WHEN** a `tool_call` frame with a `tool_call` object arrives +- **THEN** the tool_call channel pushes it to `message.tool_calls` on the target message id + +### Requirement: Mid-stream reconnection without snapshot + +The hook SHALL support mid-stream reconnection by replaying missed deltas per-channel, using a monotonic `seq` counter on each frame. If the server cannot replay, a full `snapshot` frame SHALL be used as fallback. + +#### Scenario: Reconnection with missed deltas +- **WHEN** the WebSocket reconnects during an active stream +- **THEN** the client sends the last processed `seq` per-channel and the server replays missed deltas +- **AND** if replay is unavailable, the server sends a `snapshot` frame and the reducer resets to that state + +### Requirement: Out-of-order frame buffering + +The reducer SHALL buffer out-of-order frames per-channel and flush them only when the sequence number is contiguous from the last known value. + +#### Scenario: Frame arrives out of order +- **WHEN** a frame with `seq=5` arrives before `seq=4` for the same channel +- **THEN** the reducer holds `seq=5` in a per-channel buffer +- **AND** when `seq=4` arrives, both frames SHALL be applied in order + +### Requirement: Channel frame schema in ws-frames + +The `@boocode/contracts` ws-frames schema SHALL define typed channel delta frames with a `seq` field, a `channel` discriminator, and channel-specific payloads. + +#### Scenario: Channel frame validates correctly +- **WHEN** a channel delta frame matches the schema +- **THEN** `WsFrameSchema.safeParse` returns success diff --git a/openspec/changes/streaming-codeblocks-messages-components-v2/specs/code-block-pro/spec.md b/openspec/changes/streaming-codeblocks-messages-components-v2/specs/code-block-pro/spec.md new file mode 100644 index 0000000..58a8833 --- /dev/null +++ b/openspec/changes/streaming-codeblocks-messages-components-v2/specs/code-block-pro/spec.md @@ -0,0 +1,59 @@ +## ADDED Requirements + +### Requirement: Line numbers + +`CodeBlock` SHALL render line numbers in a left gutter for blocks with >1 line. Line numbers SHALL be 1-indexed, right-aligned, and muted in color (`text-muted-foreground`). Long files (≥1000 lines) SHALL not show line numbers by default (toggle override). + +#### Scenario: Multi-line block shows line numbers +- **WHEN** a code block with 5 lines renders +- **THEN** a left gutter shows numbers 1 through 5 +- **AND** each number is right-aligned and muted + +### Requirement: Diff gutter markers + +When the language prefix starts with `diff-` (e.g. `diff-typescript`), `CodeBlock` SHALL parse leading `+` / `-` / (space) characters on each line. Lines starting with `+` SHALL get a green left border and background tint. Lines starting with `-` SHALL get a red left border and background tint. Common lines SHALL render normally. The `+`/`-` prefix characters SHALL be stripped from the rendered text. + +#### Scenario: Diff block renders with gutter markers +- **WHEN** a code block with language `diff-typescript` contains lines `+ const x = 1` and `- const y = 2` +- **THEN** the first line has a green gutter marker and shows `const x = 1` +- **AND** the second line has a red gutter marker and shows `const y = 2` + +### Requirement: Multi-theme toggle + +`CodeBlock` SHALL display a theme toggle button that switches between `github-dark` and `github-light`. The initial theme SHALL match the app's current color scheme. The user's choice SHALL persist for the session (no localStorage). + +#### Scenario: User toggles theme +- **WHEN** the user clicks the theme toggle button +- **THEN** the code block re-renders with the alternate Shiki theme +- **AND** the button icon reflects the current theme + +### Requirement: Word-wrap toggle + +`CodeBlock` SHALL provide a word-wrap toggle button. When active, long lines wrap; when inactive, they overflow with horizontal scrolling. The default state SHALL be no-wrap (scroll). The toggle SHALL persist per-code-block for the session. + +#### Scenario: User toggles word wrap +- **WHEN** the user clicks the word-wrap toggle +- **THEN** long lines wrap to the container width +- **AND** clicking again restores horizontal scroll + +### Requirement: Collapsible long blocks + +`CodeBlock` SHALL auto-collapse blocks with ≥30 lines, showing the first 15 lines and a "Show {N} more lines" button. Clicking expands the full block. The collapsed state SHALL be indicated by a gradient fade at the cutoff. + +#### Scenario: Long block collapses +- **WHEN** a code block has 50 lines +- **THEN** the first 15 lines render with a fade gradient at the bottom +- **AND** a "Show 35 more lines" button appears below +- **AND** clicking the button expands all 50 lines + +### Requirement: Inline copy with progress + +The copy button SHALL show an animated check icon on success and a brief "Copied" label that resolves after 1200ms. On copy failure, the button SHALL briefly show a red X before reverting. + +#### Scenario: Copy succeeds +- **WHEN** the user clicks Copy and the clipboard write succeeds +- **THEN** the button shows a check icon and "Copied" label for 1200ms + +#### Scenario: Copy fails +- **WHEN** the user clicks Copy and the clipboard write fails +- **THEN** the button shows a red X for 1200ms before reverting diff --git a/openspec/changes/streaming-codeblocks-messages-components-v2/specs/component-hardening/spec.md b/openspec/changes/streaming-codeblocks-messages-components-v2/specs/component-hardening/spec.md new file mode 100644 index 0000000..7fb3aa1 --- /dev/null +++ b/openspec/changes/streaming-codeblocks-messages-components-v2/specs/component-hardening/spec.md @@ -0,0 +1,55 @@ +## ADDED Requirements + +### Requirement: Error boundary on MarkdownRenderer + +`MarkdownRenderer` SHALL be wrapped in a `` that catches render errors from remark-gfm or react-markdown and shows a compact "Content rendering failed" fallback with a retry button. + +#### Scenario: Markdown render throws +- **WHEN** `react-markdown` or `remark-gfm` throws during rendering +- **THEN** the error boundary catches the exception and renders a fallback UI +- **AND** the fallback shows "Content rendering failed" with a Retry button +- **AND** clicking Retry re-mounts `MarkdownRenderer` with the same content + +### Requirement: Error boundary on CodeBlock + +`CodeBlock` SHALL be wrapped in a `` that catches errors from Shiki's `codeToHtml` and renders a plain-text `
` fallback with the source code.
+
+#### Scenario: Shiki highlight fails
+- **WHEN** `codeToHtml` throws (e.g., unknown language, WASM load failure)
+- **THEN** the error boundary catches the exception and renders a plain `
` block with the original code
+
+### Requirement: Loading skeleton for streaming messages
+
+Messages with `status === 'streaming'` and no content yet SHALL render a pulse-animated skeleton placeholder instead of an empty bubble.
+
+#### Scenario: Streaming message starts with no content
+- **WHEN** a `message_started` frame arrives with `role: 'assistant'`
+- **THEN** a skeleton placeholder renders (animated pulse bar) until the first `delta` frame arrives with content
+
+### Requirement: Keyboard-navigable ToolCallLine
+
+`ToolCallLine` SHALL support full keyboard navigation: `Tab` to focus, `Enter`/`Space` to toggle expand/collapse, `Escape` to collapse if expanded.
+
+#### Scenario: User navigates tool call via keyboard
+- **WHEN** the user presses `Tab` to focus a `ToolCallLine`
+- **THEN** a visible focus ring appears
+- **AND** pressing `Enter` toggles expand/collapse
+- **AND** pressing `Escape` collapses it if expanded
+
+### Requirement: Keyboard-navigable ToolCallGroup
+
+`ToolCallGroup` SHALL support the same keyboard navigation as `ToolCallLine`. The group header SHALL be the focusable element.
+
+#### Scenario: User navigates tool group via keyboard
+- **WHEN** the user presses `Tab` to focus a `ToolCallGroup` header
+- **THEN** a visible focus ring appears
+- **AND** pressing `Enter` toggles expand/collapse of the group
+
+### Requirement: Consistent aria-label protocol
+
+Every interactive element in `ActionRow` (Copy, Resend, Regenerate, Fork, Delete, Restore, Pin) SHALL have an `aria-label` attribute that matches its `title` text.
+
+#### Scenario: Action row button has aria-label
+- **WHEN** an ActionRow button renders
+- **THEN** it SHALL have an `aria-label` matching its `title`
+- **AND** the label SHALL be unique within the message bubble
diff --git a/openspec/changes/streaming-codeblocks-messages-components-v2/specs/message-list-v2/spec.md b/openspec/changes/streaming-codeblocks-messages-components-v2/specs/message-list-v2/spec.md
new file mode 100644
index 0000000..a6a8d5b
--- /dev/null
+++ b/openspec/changes/streaming-codeblocks-messages-components-v2/specs/message-list-v2/spec.md
@@ -0,0 +1,49 @@
+## ADDED Requirements
+
+### Requirement: Virtualized rendering
+
+`MessageList` SHALL use `react-virtuoso` to virtualize rendering of messages beyond the viewport. Only visible messages and a configurable overscan buffer (default 5 items above/below) SHALL be in the DOM. The virtualizer SHALL support variable-height items (Markdown renders at unpredictable heights).
+
+#### Scenario: Long transcript renders with virtualization
+- **WHEN** a chat has 500 messages
+- **THEN** only ~20 messages are in the DOM at any time (viewport ± overscan)
+- **AND** scrolling maintains correct scroll position
+
+#### Scenario: Streaming follows bottom by default
+- **WHEN** a new streaming message arrives and the user is near the bottom
+- **THEN** the list auto-scrolls to follow the streaming content
+- **AND** if the user has scrolled up reading history, auto-scroll is suppressed
+
+### Requirement: Smarter tool-call grouping
+
+Consecutive tool runs of the same name SHALL collapse into a grouped control with a count header (same as current behavior for ≥3). Additionally, each group SHALL support independent expand/collapse state (not just the current all-or-nothing). User can expand a group to see individual calls, or collapse them back.
+
+#### Scenario: Grouped tool calls are individually expandable
+- **WHEN** a tool group with 5 `view_file` calls renders
+- **THEN** the collapsed group shows "5 view_file calls" with an expand chevron
+- **AND** clicking expand shows each call as a separate item
+- **AND** clicking collapse returns to the grouped header
+
+### Requirement: Stagger entrance animations
+
+New messages SHALL enter with a staggered fade-slide animation. Each new message SHALL animate with `opacity: 0 → 1, y: 8 → 0`. Consecutive new messages (from a batch delta or snapshot) SHALL stagger by 40ms between items, up to a max of 500ms total stagger.
+
+#### Scenario: Batch of messages arrives
+- **WHEN** 10 new messages arrive in a `snapshot` frame
+- **THEN** each message fades in sequentially with a 40ms stagger between them
+- **AND** animation is skipped (zero-duration) when `prefers-reduced-motion` is set
+
+### Requirement: Message pin/bookmark
+
+Users SHALL be able to pin a message by clicking a pin icon in the message's ActionRow. Pinned messages SHALL be tracked locally via URL hash (`#pin=`). Only one message can be pinned at a time. A pinned indicator SHALL show at the top of the message list with a "Jump to pinned" link.
+
+#### Scenario: User pins a message
+- **WHEN** the user clicks the pin icon on a message
+- **THEN** the URL hash updates to `#pin=`
+- **AND** a pin indicator appears at the top of the list with "Jump to pinned" link
+- **AND** clicking the pin icon again unpins and clears the hash
+
+#### Scenario: Pinned message navigates on click
+- **WHEN** the user clicks "Jump to pinned"
+- **THEN** the list scrolls to the pinned message
+- **AND** the pinned message briefly highlights
diff --git a/openspec/changes/streaming-codeblocks-messages-components-v2/tasks.md b/openspec/changes/streaming-codeblocks-messages-components-v2/tasks.md
new file mode 100644
index 0000000..3b9f11d
--- /dev/null
+++ b/openspec/changes/streaming-codeblocks-messages-components-v2/tasks.md
@@ -0,0 +1,53 @@
+## 1. Contracts: Channel delta frame schema
+
+- [x] 1.1 Add channel-delta frame types to `packages/contracts/src/ws-frames.ts`: `seq` field, `channel` discriminator (`text | tool_call | tool_result | status | error`), and per-channel payload types
+- [x] 1.2 Update `WsFrameSchema` Zod schema with the new channel frame variants
+- [x] 1.3 Regenerate downstream types and verify `ws-frames.test.ts` passes
+
+## 2. Streaming layer: Channel-based reducer
+
+- [x] 2.1 Implement `ChannelBuffer` — per-channel out-of-order frame buffer with contiguous-seq flush logic
+- [x] 2.2 Rewrite `applyFrame` in `useSessionStream.ts` as a channel-dispatch reducer that fans out to channel buffers then merges into `StreamState`
+- [x] 2.3 Add mid-stream reconnection protocol: client sends last `seq` per-channel on reconnect; server sends replay deltas or fallback snapshot
+- [x] 2.4 Handle edge cases: empty delta, duplicate seq, channel stall timeout (5s force-snapshot fallback)
+- [x] 2.5 Test: manual WS frame injection with out-of-order deltas, reconnect mid-stream, verify state consistency — 29 tests, all pass
+
+## 3. CodeBlock Pro
+
+- [x] 3.1 Add line-number gutter rendering (1-indexed, right-aligned, muted color, hidden for ≥1000 lines)
+- [x] 3.2 Add diff-gutter mode: detect `diff-` language prefix, parse `+`/`-` markers, render green/red gutter classes
+- [x] 3.3 Add theme toggle button (`github-dark` / `github-light`) with session-persisted choice
+- [x] 3.4 Add word-wrap toggle button (default no-wrap, toggle to wrap with CSS `white-space: pre-wrap`)
+- [x] 3.5 Add collapsible mode: auto-collapse ≥30 lines, show first 15 + "Show N more" button with gradient fade
+- [x] 3.6 Add inline copy with progress states (check/red-X revert after 1200ms)
+- [x] 3.7 Add LRU cache (`Map`, max 50 entries) to avoid redundant Shiki calls
+- [x] 3.8 Test: each feature toggles independently, Shiki fallback to plain `
` on error
+
+## 4. MessageList v2
+
+- [x] 4.1 Replace flat `div` with `react-virtuoso` `Virtuoso` component, configure `followOutput="auto"` and overscan=5
+- [x] 4.2 Preserve `isNearBottomRef` scroll-tracking logic for followOutput gating
+- [x] 4.3 Add stagger entrance animation (40ms stagger between new items, max 500ms, 0-duration with `prefers-reduced-motion`)
+- [x] 4.4 Refactor tool-call grouping: per-group independent expand/collapse state (not all-or-nothing)
+- [x] 4.5 Implement message pin/bookmark: URL hash `#pin=`, pin indicator bar at top with "Jump to pinned"
+- [x] 4.6 Add `react-virtuoso` to `package.json` dependencies
+- [x] 4.7 Test: 500-message transcript renders with ~20 DOM nodes, scroll-to-bottom on new stream, pin/unpin works
+
+## 5. Component hardening
+
+- [x] 5.1 Create `` component that catches render errors and shows "Rendering failed" + Retry button
+- [x] 5.2 Wrap `MarkdownRenderer` in ``
+- [x] 5.3 Wrap `CodeBlock` in `` with plain `
` fallback on Shiki failure
+- [x] 5.4 Add streaming skeleton placeholder: `status === 'streaming'` with empty content renders pulse bar
+- [x] 5.5 Add keyboard navigation to `ToolCallLine`: `Tab` focus, `Enter`/`Space` toggle, `Escape` collapse, visible focus ring — `tabIndex={0}`, `onKeyDown`, `focus-visible:ring-2`
+- [x] 5.6 Add keyboard navigation to `ToolCallGroup`: focusable header, same keybindings
+- [x] 5.7 Audit `ActionRow` buttons: every interactive element has matching `aria-label` and `title`
+- [x] 5.8 Create `` top-level wrapper for catastrophic failures
+
+## 6. Build and verify
+
+- [x] 6.1 Run `pnpm build` and fix any type/compile errors — PASSES (tsc -b + vite build)
+- [x] 6.2 Run `npx tsc -p apps/web/tsconfig.app.json --noEmit` and fix any type errors — PASSES (no errors)
+- [ ] 6.3 Smoke-test streaming with a real LLM turn (send message, verify streaming renders, tool calls, complete) — requires running LLM environment
+- [ ] 6.4 Smoke-test code blocks (diff highlight, line numbers, collapse, copy, theme toggle) — requires running environment
+- [ ] 6.5 Smoke-test message list (scrolling, pin, error boundary injection) — requires running environment
diff --git a/package.json b/package.json
index cccbe85..06ca7e4 100644
--- a/package.json
+++ b/package.json
@@ -11,5 +11,8 @@
   "devDependencies": {
     "typescript": "^5.5.0"
   },
-  "license": "MIT"
+  "license": "MIT",
+  "dependencies": {
+    "better-sqlite3": "^11.10.0"
+  }
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ec22e22..7e2a4bf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7,6 +7,10 @@ settings:
 importers:
 
   .:
+    dependencies:
+      better-sqlite3:
+        specifier: ^11.10.0
+        version: 11.10.0
     devDependencies:
       typescript:
         specifier: ^5.5.0
@@ -93,7 +97,7 @@ importers:
         version: 5.9.3
       vitest:
         specifier: ^3.0.0
-        version: 3.2.4(@types/debug@4.1.13)(@types/node@20.19.41)(lightningcss@1.32.0)(msw@2.14.6(@types/node@20.19.41)(typescript@5.9.3))
+        version: 3.2.4(@types/debug@4.1.13)(@types/node@20.19.41)(jsdom@29.1.1(@noble/hashes@1.8.0))(lightningcss@1.32.0)(msw@2.14.6(@types/node@20.19.41)(typescript@5.9.3))
 
   apps/server:
     dependencies:
@@ -148,7 +152,7 @@ importers:
         version: 5.9.3
       vitest:
         specifier: ^3.2.4
-        version: 3.2.4(@types/debug@4.1.13)(@types/node@20.19.41)(lightningcss@1.32.0)(msw@2.14.6(@types/node@20.19.41)(typescript@5.9.3))
+        version: 3.2.4(@types/debug@4.1.13)(@types/node@20.19.41)(jsdom@29.1.1(@noble/hashes@1.8.0))(lightningcss@1.32.0)(msw@2.14.6(@types/node@20.19.41)(typescript@5.9.3))
 
   apps/web:
     dependencies:
@@ -182,6 +186,9 @@ importers:
       clsx:
         specifier: ^2.1.1
         version: 2.1.1
+      framer-motion:
+        specifier: ^12.40.0
+        version: 12.40.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       lucide-react:
         specifier: ^1.16.0
         version: 1.16.0(react@18.3.1)
@@ -200,6 +207,9 @@ importers:
       react-router-dom:
         specifier: ^6.26.0
         version: 6.30.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+      react-virtuoso:
+        specifier: ^4.18.7
+        version: 4.18.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       remark-gfm:
         specifier: ^4.0.1
         version: 4.0.1
@@ -234,6 +244,9 @@ importers:
       '@vitejs/plugin-react':
         specifier: ^4.3.1
         version: 4.7.0(vite@5.4.21(@types/node@20.19.41)(lightningcss@1.32.0))
+      jsdom:
+        specifier: ^29.1.1
+        version: 29.1.1(@noble/hashes@1.8.0)
       shadcn:
         specifier: ^4.7.0
         version: 4.7.0(@types/node@20.19.41)(typescript@5.9.3)
@@ -246,6 +259,9 @@ importers:
       vite:
         specifier: ^5.3.4
         version: 5.4.21(@types/node@20.19.41)(lightningcss@1.32.0)
+      vitest:
+        specifier: ^3.2.4
+        version: 3.2.4(@types/debug@4.1.13)(@types/node@20.19.41)(jsdom@29.1.1(@noble/hashes@1.8.0))(lightningcss@1.32.0)(msw@2.14.6(@types/node@20.19.41)(typescript@5.9.3))
 
   packages/contracts:
     dependencies:
@@ -258,7 +274,7 @@ importers:
         version: 5.9.3
       vitest:
         specifier: ^3.2.4
-        version: 3.2.4(@types/debug@4.1.13)(@types/node@25.9.2)(lightningcss@1.32.0)(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))
+        version: 3.2.4(@types/debug@4.1.13)(@types/node@25.9.2)(jsdom@29.1.1(@noble/hashes@1.8.0))(lightningcss@1.32.0)(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))
 
   packages/ion:
     dependencies:
@@ -289,7 +305,7 @@ importers:
         version: 5.9.3
       vitest:
         specifier: ^3.2.4
-        version: 3.2.4(@types/debug@4.1.13)(@types/node@25.9.2)(lightningcss@1.32.0)(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))
+        version: 3.2.4(@types/debug@4.1.13)(@types/node@25.9.2)(jsdom@29.1.1(@noble/hashes@1.8.0))(lightningcss@1.32.0)(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3))
     optionalDependencies:
       better-sqlite3:
         specifier: ^11.0.0
@@ -351,21 +367,25 @@ packages:
     resolution: {integrity: sha512-WvwiQBWt3tdu5EwqjpDZszI6p2uetYsw4Cxc6ptO/SmLIYXcDienP8nmirZdsZrS+Gzk6imgY0IY5mmNaRhelQ==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.159':
     resolution: {integrity: sha512-FlsS5M4GCpzsQVaNDFF8dRgFGR3QwyAHZFl/xM/2Y2BqVBH+NH17RpKQSJxr1qr41QnsNkinMnu2iSKoc33hKg==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.159':
     resolution: {integrity: sha512-kFH6RC2YJbPc8XWRNy/wL4YU7LzdJjSwAdH488sVzIif3q+TrvVrV5y/IW0+MLmta+CKIqtFYpGaucsJYvj7Eg==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@anthropic-ai/claude-agent-sdk-linux-x64@0.3.159':
     resolution: {integrity: sha512-uNPEC/iRzVb4bEdzs0KAz1zV7i1PVGEZZnJTQyi1OtgVa81sAoH/H0CbbzDiTsquKdaESf+1DSSEkUlfZmMUEw==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.159':
     resolution: {integrity: sha512-WN1QEZGgWXz9GMl61QU6j9E+LEF5plki87bL2xsGwuCPzK+OeVPQU55pabuP8P+vFBFHUo3Y9OlTVyZHnUzmAQ==}
@@ -394,6 +414,21 @@ packages:
       zod:
         optional: true
 
+  '@asamuzakjp/css-color@5.1.11':
+    resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==}
+    engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+  '@asamuzakjp/dom-selector@7.1.1':
+    resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==}
+    engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+  '@asamuzakjp/generational-cache@1.0.1':
+    resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==}
+    engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+  '@asamuzakjp/nwsapi@2.3.9':
+    resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
   '@babel/code-frame@7.29.0':
     resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
     engines: {node: '>=6.9.0'}
@@ -539,6 +574,46 @@ packages:
     resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
     engines: {node: '>=6.9.0'}
 
+  '@bramus/specificity@2.4.2':
+    resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
+    hasBin: true
+
+  '@csstools/color-helpers@6.0.2':
+    resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==}
+    engines: {node: '>=20.19.0'}
+
+  '@csstools/css-calc@3.2.1':
+    resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==}
+    engines: {node: '>=20.19.0'}
+    peerDependencies:
+      '@csstools/css-parser-algorithms': ^4.0.0
+      '@csstools/css-tokenizer': ^4.0.0
+
+  '@csstools/css-color-parser@4.1.1':
+    resolution: {integrity: sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==}
+    engines: {node: '>=20.19.0'}
+    peerDependencies:
+      '@csstools/css-parser-algorithms': ^4.0.0
+      '@csstools/css-tokenizer': ^4.0.0
+
+  '@csstools/css-parser-algorithms@4.0.0':
+    resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
+    engines: {node: '>=20.19.0'}
+    peerDependencies:
+      '@csstools/css-tokenizer': ^4.0.0
+
+  '@csstools/css-syntax-patches-for-csstree@1.1.5':
+    resolution: {integrity: sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==}
+    peerDependencies:
+      css-tree: ^3.2.1
+    peerDependenciesMeta:
+      css-tree:
+        optional: true
+
+  '@csstools/css-tokenizer@4.0.0':
+    resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
+    engines: {node: '>=20.19.0'}
+
   '@dotenvx/dotenvx@1.66.0':
     resolution: {integrity: sha512-qlQFhHUjhRDybrinqLAD0MClVZDOrsq80O8eD5iSjz3Qa/4f3Jg7SQrOaSobrRyP1QaWIYLGtGpj2c7H0D8NUw==}
     hasBin: true
@@ -843,6 +918,15 @@ packages:
     cpu: [x64]
     os: [win32]
 
+  '@exodus/bytes@1.15.1':
+    resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==}
+    engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+    peerDependencies:
+      '@noble/hashes': ^1.8.0 || ^2.0.0
+    peerDependenciesMeta:
+      '@noble/hashes':
+        optional: true
+
   '@fastify/accept-negotiator@1.1.0':
     resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==}
     engines: {node: '>=14'}
@@ -1752,66 +1836,79 @@ packages:
     resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==}
     cpu: [arm]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm-musleabihf@4.60.3':
     resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==}
     cpu: [arm]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-arm64-gnu@4.60.3':
     resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-arm64-musl@4.60.3':
     resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-loong64-gnu@4.60.3':
     resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==}
     cpu: [loong64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-loong64-musl@4.60.3':
     resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==}
     cpu: [loong64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-ppc64-gnu@4.60.3':
     resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==}
     cpu: [ppc64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-ppc64-musl@4.60.3':
     resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==}
     cpu: [ppc64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-riscv64-gnu@4.60.3':
     resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==}
     cpu: [riscv64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-riscv64-musl@4.60.3':
     resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==}
     cpu: [riscv64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-linux-s390x-gnu@4.60.3':
     resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==}
     cpu: [s390x]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-gnu@4.60.3':
     resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@rollup/rollup-linux-x64-musl@4.60.3':
     resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@rollup/rollup-openbsd-x64@4.60.3':
     resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==}
@@ -1915,24 +2012,28 @@ packages:
     engines: {node: '>= 20'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   '@tailwindcss/oxide-linux-arm64-musl@4.3.0':
     resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==}
     engines: {node: '>= 20'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   '@tailwindcss/oxide-linux-x64-gnu@4.3.0':
     resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==}
     engines: {node: '>= 20'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   '@tailwindcss/oxide-linux-x64-musl@4.3.0':
     resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==}
     engines: {node: '>= 20'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   '@tailwindcss/oxide-wasm32-wasi@4.3.0':
     resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==}
@@ -2203,6 +2304,9 @@ packages:
   better-sqlite3@11.10.0:
     resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==}
 
+  bidi-js@1.0.3:
+    resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
   bindings@1.5.0:
     resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
 
@@ -2387,6 +2491,10 @@ packages:
   css-select@5.2.2:
     resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
 
+  css-tree@3.2.1:
+    resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
+    engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
   css-what@6.2.2:
     resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
     engines: {node: '>= 6'}
@@ -2403,6 +2511,10 @@ packages:
     resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
     engines: {node: '>= 12'}
 
+  data-urls@7.0.0:
+    resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
+    engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
   debug@4.4.3:
     resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
     engines: {node: '>=6.0'}
@@ -2412,6 +2524,9 @@ packages:
       supports-color:
         optional: true
 
+  decimal.js@10.6.0:
+    resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
   decode-named-character-reference@1.3.0:
     resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
 
@@ -2537,6 +2652,10 @@ packages:
     resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
     engines: {node: '>=0.12'}
 
+  entities@8.0.0:
+    resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
+    engines: {node: '>=20.19.0'}
+
   env-paths@2.2.1:
     resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
     engines: {node: '>=6'}
@@ -2722,6 +2841,20 @@ packages:
     resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
     engines: {node: '>= 0.6'}
 
+  framer-motion@12.40.0:
+    resolution: {integrity: sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==}
+    peerDependencies:
+      '@emotion/is-prop-valid': '*'
+      react: ^18.0.0 || ^19.0.0
+      react-dom: ^18.0.0 || ^19.0.0
+    peerDependenciesMeta:
+      '@emotion/is-prop-valid':
+        optional: true
+      react:
+        optional: true
+      react-dom:
+        optional: true
+
   fresh@2.0.0:
     resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
     engines: {node: '>= 0.8'}
@@ -2831,6 +2964,10 @@ packages:
     resolution: {integrity: sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==}
     engines: {node: '>=16.9.0'}
 
+  html-encoding-sniffer@6.0.0:
+    resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
+    engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
   html-url-attributes@3.0.1:
     resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
 
@@ -2949,6 +3086,9 @@ packages:
     resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
     engines: {node: '>=12'}
 
+  is-potential-custom-element-name@1.0.1:
+    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
   is-promise@4.0.0:
     resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
 
@@ -3003,6 +3143,15 @@ packages:
     resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
     hasBin: true
 
+  jsdom@29.1.1:
+    resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==}
+    engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
+    peerDependencies:
+      canvas: ^3.0.0
+    peerDependenciesMeta:
+      canvas:
+        optional: true
+
   jsesc@3.1.0:
     resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
     engines: {node: '>=6'}
@@ -3081,24 +3230,28 @@ packages:
     engines: {node: '>= 12.0.0'}
     cpu: [arm64]
     os: [linux]
+    libc: [glibc]
 
   lightningcss-linux-arm64-musl@1.32.0:
     resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
     engines: {node: '>= 12.0.0'}
     cpu: [arm64]
     os: [linux]
+    libc: [musl]
 
   lightningcss-linux-x64-gnu@1.32.0:
     resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
     engines: {node: '>= 12.0.0'}
     cpu: [x64]
     os: [linux]
+    libc: [glibc]
 
   lightningcss-linux-x64-musl@1.32.0:
     resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
     engines: {node: '>= 12.0.0'}
     cpu: [x64]
     os: [linux]
+    libc: [musl]
 
   lightningcss-win32-arm64-msvc@1.32.0:
     resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
@@ -3136,6 +3289,10 @@ packages:
   lru-cache@10.4.3:
     resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
 
+  lru-cache@11.5.1:
+    resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==}
+    engines: {node: 20 || >=22}
+
   lru-cache@5.1.1:
     resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
 
@@ -3199,6 +3356,9 @@ packages:
   mdast-util-to-string@4.0.0:
     resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
 
+  mdn-data@2.27.1:
+    resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+
   media-typer@1.1.0:
     resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
     engines: {node: '>= 0.8'}
@@ -3345,6 +3505,12 @@ packages:
   mkdirp-classic@0.5.3:
     resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
 
+  motion-dom@12.40.0:
+    resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==}
+
+  motion-utils@12.39.0:
+    resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==}
+
   ms@2.1.3:
     resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
 
@@ -3482,6 +3648,9 @@ packages:
     resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
     engines: {node: '>=18'}
 
+  parse5@8.0.1:
+    resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==}
+
   parseurl@1.3.3:
     resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
     engines: {node: '>= 0.8'}
@@ -3635,6 +3804,10 @@ packages:
   pump@3.0.4:
     resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
 
+  punycode@2.3.1:
+    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+    engines: {node: '>=6'}
+
   qs@6.15.1:
     resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==}
     engines: {node: '>=0.6'}
@@ -3728,6 +3901,12 @@ packages:
       '@types/react':
         optional: true
 
+  react-virtuoso@4.18.7:
+    resolution: {integrity: sha512-xNF5zDGEEIMB7cKwcen/pLig0YDf6OnfFrVgKFa7sHPf9fRem0CaLshyObbBcP88jzn0enavL39EgplgdyT21g==}
+    peerDependencies:
+      react: '>=16 || >=17 || >= 18 || >= 19'
+      react-dom: '>=16 || >=17 || >= 18 || >=19'
+
   react@18.3.1:
     resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
     engines: {node: '>=0.10.0'}
@@ -3824,6 +4003,10 @@ packages:
   safer-buffer@2.1.2:
     resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
 
+  saxes@6.0.0:
+    resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+    engines: {node: '>=v12.22.7'}
+
   scheduler@0.23.2:
     resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
 
@@ -4012,6 +4195,9 @@ packages:
   style-to-object@1.0.14:
     resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==}
 
+  symbol-tree@3.2.4:
+    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
   tagged-tag@1.0.0:
     resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
     engines: {node: '>=20'}
@@ -4084,6 +4270,10 @@ packages:
     resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
     engines: {node: '>=16'}
 
+  tr46@6.0.0:
+    resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
+    engines: {node: '>=20'}
+
   trim-lines@3.0.1:
     resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
 
@@ -4137,6 +4327,10 @@ packages:
   undici-types@7.24.6:
     resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
 
+  undici@7.27.2:
+    resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==}
+    engines: {node: '>=20.18.1'}
+
   unicorn-magic@0.3.0:
     resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
     engines: {node: '>=18'}
@@ -4282,10 +4476,26 @@ packages:
       jsdom:
         optional: true
 
+  w3c-xmlserializer@5.0.0:
+    resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+    engines: {node: '>=18'}
+
   web-streams-polyfill@3.3.3:
     resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
     engines: {node: '>= 8'}
 
+  webidl-conversions@8.0.1:
+    resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
+    engines: {node: '>=20'}
+
+  whatwg-mimetype@5.0.0:
+    resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
+    engines: {node: '>=20'}
+
+  whatwg-url@16.0.1:
+    resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
+    engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
   which@2.0.2:
     resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
     engines: {node: '>= 8'}
@@ -4328,6 +4538,13 @@ packages:
     resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==}
     engines: {node: '>=20'}
 
+  xml-name-validator@5.0.0:
+    resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+    engines: {node: '>=18'}
+
+  xmlchars@2.2.0:
+    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
   xtend@4.0.2:
     resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
     engines: {node: '>=0.4'}
@@ -4450,6 +4667,26 @@ snapshots:
     optionalDependencies:
       zod: 3.25.76
 
+  '@asamuzakjp/css-color@5.1.11':
+    dependencies:
+      '@asamuzakjp/generational-cache': 1.0.1
+      '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+      '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+      '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+      '@csstools/css-tokenizer': 4.0.0
+
+  '@asamuzakjp/dom-selector@7.1.1':
+    dependencies:
+      '@asamuzakjp/generational-cache': 1.0.1
+      '@asamuzakjp/nwsapi': 2.3.9
+      bidi-js: 1.0.3
+      css-tree: 3.2.1
+      is-potential-custom-element-name: 1.0.1
+
+  '@asamuzakjp/generational-cache@1.0.1': {}
+
+  '@asamuzakjp/nwsapi@2.3.9': {}
+
   '@babel/code-frame@7.29.0':
     dependencies:
       '@babel/helper-validator-identifier': 7.28.5
@@ -4648,6 +4885,34 @@ snapshots:
       '@babel/helper-string-parser': 7.27.1
       '@babel/helper-validator-identifier': 7.28.5
 
+  '@bramus/specificity@2.4.2':
+    dependencies:
+      css-tree: 3.2.1
+
+  '@csstools/color-helpers@6.0.2': {}
+
+  '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+    dependencies:
+      '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+      '@csstools/css-tokenizer': 4.0.0
+
+  '@csstools/css-color-parser@4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+    dependencies:
+      '@csstools/color-helpers': 6.0.2
+      '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+      '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+      '@csstools/css-tokenizer': 4.0.0
+
+  '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
+    dependencies:
+      '@csstools/css-tokenizer': 4.0.0
+
+  '@csstools/css-syntax-patches-for-csstree@1.1.5(css-tree@3.2.1)':
+    optionalDependencies:
+      css-tree: 3.2.1
+
+  '@csstools/css-tokenizer@4.0.0': {}
+
   '@dotenvx/dotenvx@1.66.0':
     dependencies:
       commander: 11.1.0
@@ -4812,6 +5077,10 @@ snapshots:
   '@esbuild/win32-x64@0.28.0':
     optional: true
 
+  '@exodus/bytes@1.15.1(@noble/hashes@1.8.0)':
+    optionalDependencies:
+      '@noble/hashes': 1.8.0
+
   '@fastify/accept-negotiator@1.1.0': {}
 
   '@fastify/ajv-compiler@3.6.0':
@@ -6227,8 +6496,7 @@ snapshots:
 
   balanced-match@4.0.4: {}
 
-  base64-js@1.5.1:
-    optional: true
+  base64-js@1.5.1: {}
 
   baseline-browser-mapping@2.10.29: {}
 
@@ -6236,19 +6504,20 @@ snapshots:
     dependencies:
       bindings: 1.5.0
       prebuild-install: 7.1.3
-    optional: true
+
+  bidi-js@1.0.3:
+    dependencies:
+      require-from-string: 2.0.2
 
   bindings@1.5.0:
     dependencies:
       file-uri-to-path: 1.0.0
-    optional: true
 
   bl@4.1.0:
     dependencies:
       buffer: 5.7.1
       inherits: 2.0.4
       readable-stream: 3.6.2
-    optional: true
 
   body-parser@2.2.2:
     dependencies:
@@ -6290,7 +6559,6 @@ snapshots:
     dependencies:
       base64-js: 1.5.1
       ieee754: 1.2.1
-    optional: true
 
   bundle-name@4.1.0:
     dependencies:
@@ -6336,8 +6604,7 @@ snapshots:
 
   check-error@2.1.3: {}
 
-  chownr@1.1.4:
-    optional: true
+  chownr@1.1.4: {}
 
   class-variance-authority@0.7.1:
     dependencies:
@@ -6419,6 +6686,11 @@ snapshots:
       domutils: 3.2.2
       nth-check: 2.1.1
 
+  css-tree@3.2.1:
+    dependencies:
+      mdn-data: 2.27.1
+      source-map-js: 1.2.1
+
   css-what@6.2.2: {}
 
   cssesc@3.0.0: {}
@@ -6427,10 +6699,19 @@ snapshots:
 
   data-uri-to-buffer@4.0.1: {}
 
+  data-urls@7.0.0(@noble/hashes@1.8.0):
+    dependencies:
+      whatwg-mimetype: 5.0.0
+      whatwg-url: 16.0.1(@noble/hashes@1.8.0)
+    transitivePeerDependencies:
+      - '@noble/hashes'
+
   debug@4.4.3:
     dependencies:
       ms: 2.1.3
 
+  decimal.js@10.6.0: {}
+
   decode-named-character-reference@1.3.0:
     dependencies:
       character-entities: 2.0.2
@@ -6438,14 +6719,12 @@ snapshots:
   decompress-response@6.0.0:
     dependencies:
       mimic-response: 3.1.0
-    optional: true
 
   dedent@1.7.2: {}
 
   deep-eql@5.0.2: {}
 
-  deep-extend@0.6.0:
-    optional: true
+  deep-extend@0.6.0: {}
 
   deepmerge@4.3.1: {}
 
@@ -6539,6 +6818,8 @@ snapshots:
 
   entities@4.5.0: {}
 
+  entities@8.0.0: {}
+
   env-paths@2.2.1: {}
 
   error-ex@1.3.4:
@@ -6659,8 +6940,7 @@ snapshots:
       strip-final-newline: 4.0.0
       yoctocolors: 2.1.2
 
-  expand-template@2.0.3:
-    optional: true
+  expand-template@2.0.3: {}
 
   expect-type@1.3.0: {}
 
@@ -6786,8 +7066,7 @@ snapshots:
     dependencies:
       is-unicode-supported: 2.1.0
 
-  file-uri-to-path@1.0.0:
-    optional: true
+  file-uri-to-path@1.0.0: {}
 
   fill-range@7.1.1:
     dependencies:
@@ -6821,10 +7100,18 @@ snapshots:
 
   forwarded@0.2.0: {}
 
+  framer-motion@12.40.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+    dependencies:
+      motion-dom: 12.40.0
+      motion-utils: 12.39.0
+      tslib: 2.8.1
+    optionalDependencies:
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+
   fresh@2.0.0: {}
 
-  fs-constants@1.0.0:
-    optional: true
+  fs-constants@1.0.0: {}
 
   fs-extra@11.3.5:
     dependencies:
@@ -6874,8 +7161,7 @@ snapshots:
       '@sec-ant/readable-stream': 0.4.1
       is-stream: 4.0.1
 
-  github-from-package@0.0.0:
-    optional: true
+  github-from-package@0.0.0: {}
 
   glob-parent@5.1.2:
     dependencies:
@@ -6949,6 +7235,12 @@ snapshots:
 
   hono@4.12.18: {}
 
+  html-encoding-sniffer@6.0.0(@noble/hashes@1.8.0):
+    dependencies:
+      '@exodus/bytes': 1.15.1(@noble/hashes@1.8.0)
+    transitivePeerDependencies:
+      - '@noble/hashes'
+
   html-url-attributes@3.0.1: {}
 
   html-void-elements@3.0.0: {}
@@ -6984,8 +7276,7 @@ snapshots:
     dependencies:
       safer-buffer: 2.1.2
 
-  ieee754@1.2.1:
-    optional: true
+  ieee754@1.2.1: {}
 
   ignore@5.3.2: {}
 
@@ -6996,8 +7287,7 @@ snapshots:
 
   inherits@2.0.4: {}
 
-  ini@1.3.8:
-    optional: true
+  ini@1.3.8: {}
 
   inline-style-parser@0.2.7: {}
 
@@ -7044,6 +7334,8 @@ snapshots:
 
   is-plain-obj@4.1.0: {}
 
+  is-potential-custom-element-name@1.0.1: {}
+
   is-promise@4.0.0: {}
 
   is-regexp@3.1.0: {}
@@ -7082,6 +7374,32 @@ snapshots:
     dependencies:
       argparse: 2.0.1
 
+  jsdom@29.1.1(@noble/hashes@1.8.0):
+    dependencies:
+      '@asamuzakjp/css-color': 5.1.11
+      '@asamuzakjp/dom-selector': 7.1.1
+      '@bramus/specificity': 2.4.2
+      '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1)
+      '@exodus/bytes': 1.15.1(@noble/hashes@1.8.0)
+      css-tree: 3.2.1
+      data-urls: 7.0.0(@noble/hashes@1.8.0)
+      decimal.js: 10.6.0
+      html-encoding-sniffer: 6.0.0(@noble/hashes@1.8.0)
+      is-potential-custom-element-name: 1.0.1
+      lru-cache: 11.5.1
+      parse5: 8.0.1
+      saxes: 6.0.0
+      symbol-tree: 3.2.4
+      tough-cookie: 6.0.1
+      undici: 7.27.2
+      w3c-xmlserializer: 5.0.0
+      webidl-conversions: 8.0.1
+      whatwg-mimetype: 5.0.0
+      whatwg-url: 16.0.1(@noble/hashes@1.8.0)
+      xml-name-validator: 5.0.0
+    transitivePeerDependencies:
+      - '@noble/hashes'
+
   jsesc@3.1.0: {}
 
   json-parse-even-better-errors@2.3.1: {}
@@ -7185,6 +7503,8 @@ snapshots:
 
   lru-cache@10.4.3: {}
 
+  lru-cache@11.5.1: {}
+
   lru-cache@5.1.1:
     dependencies:
       yallist: 3.1.1
@@ -7354,6 +7674,8 @@ snapshots:
     dependencies:
       '@types/mdast': 4.0.4
 
+  mdn-data@2.27.1: {}
+
   media-typer@1.1.0: {}
 
   merge-descriptors@2.0.0: {}
@@ -7570,8 +7892,7 @@ snapshots:
 
   mimic-function@5.0.1: {}
 
-  mimic-response@3.1.0:
-    optional: true
+  mimic-response@3.1.0: {}
 
   minimatch@10.2.5:
     dependencies:
@@ -7585,8 +7906,13 @@ snapshots:
 
   minipass@7.1.3: {}
 
-  mkdirp-classic@0.5.3:
-    optional: true
+  mkdirp-classic@0.5.3: {}
+
+  motion-dom@12.40.0:
+    dependencies:
+      motion-utils: 12.39.0
+
+  motion-utils@12.39.0: {}
 
   ms@2.1.3: {}
 
@@ -7647,15 +7973,13 @@ snapshots:
 
   nanoid@5.1.11: {}
 
-  napi-build-utils@2.0.0:
-    optional: true
+  napi-build-utils@2.0.0: {}
 
   negotiator@1.0.0: {}
 
   node-abi@3.92.0:
     dependencies:
       semver: 7.8.0
-    optional: true
 
   node-addon-api@7.1.1: {}
 
@@ -7773,6 +8097,10 @@ snapshots:
 
   parse-ms@4.0.0: {}
 
+  parse5@8.0.1:
+    dependencies:
+      entities: 8.0.0
+
   parseurl@1.3.3: {}
 
   path-browserify@1.0.1: {}
@@ -7896,7 +8224,6 @@ snapshots:
       simple-get: 4.0.1
       tar-fs: 2.1.4
       tunnel-agent: 0.6.0
-    optional: true
 
   pretty-ms@9.3.0:
     dependencies:
@@ -7922,7 +8249,8 @@ snapshots:
     dependencies:
       end-of-stream: 1.4.5
       once: 1.4.0
-    optional: true
+
+  punycode@2.3.1: {}
 
   qs@6.15.1:
     dependencies:
@@ -8010,7 +8338,6 @@ snapshots:
       ini: 1.3.8
       minimist: 1.2.8
       strip-json-comments: 2.0.1
-    optional: true
 
   react-dom@18.3.1(react@18.3.1):
     dependencies:
@@ -8077,6 +8404,11 @@ snapshots:
     optionalDependencies:
       '@types/react': 18.3.28
 
+  react-virtuoso@4.18.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+    dependencies:
+      react: 18.3.1
+      react-dom: 18.3.1(react@18.3.1)
+
   react@18.3.1:
     dependencies:
       loose-envify: 1.4.0
@@ -8218,6 +8550,10 @@ snapshots:
 
   safer-buffer@2.1.2: {}
 
+  saxes@6.0.0:
+    dependencies:
+      xmlchars: 2.2.0
+
   scheduler@0.23.2:
     dependencies:
       loose-envify: 1.4.0
@@ -8353,15 +8689,13 @@ snapshots:
 
   signal-exit@4.1.0: {}
 
-  simple-concat@1.0.1:
-    optional: true
+  simple-concat@1.0.1: {}
 
   simple-get@4.0.1:
     dependencies:
       decompress-response: 6.0.0
       once: 1.4.0
       simple-concat: 1.0.1
-    optional: true
 
   sisteransi@1.0.5: {}
 
@@ -8448,8 +8782,7 @@ snapshots:
 
   strip-final-newline@4.0.0: {}
 
-  strip-json-comments@2.0.1:
-    optional: true
+  strip-json-comments@2.0.1: {}
 
   strip-literal@3.1.0:
     dependencies:
@@ -8463,6 +8796,8 @@ snapshots:
     dependencies:
       inline-style-parser: 0.2.7
 
+  symbol-tree@3.2.4: {}
+
   tagged-tag@1.0.0: {}
 
   tailwind-merge@3.6.0: {}
@@ -8477,7 +8812,6 @@ snapshots:
       mkdirp-classic: 0.5.3
       pump: 3.0.4
       tar-stream: 2.2.0
-    optional: true
 
   tar-stream@2.2.0:
     dependencies:
@@ -8486,7 +8820,6 @@ snapshots:
       fs-constants: 1.0.0
       inherits: 2.0.4
       readable-stream: 3.6.2
-    optional: true
 
   thread-stream@3.1.0:
     dependencies:
@@ -8527,6 +8860,10 @@ snapshots:
     dependencies:
       tldts: 7.0.30
 
+  tr46@6.0.0:
+    dependencies:
+      punycode: 2.3.1
+
   trim-lines@3.0.1: {}
 
   trough@2.2.0: {}
@@ -8555,7 +8892,6 @@ snapshots:
   tunnel-agent@0.6.0:
     dependencies:
       safe-buffer: 5.2.1
-    optional: true
 
   tw-animate-css@1.4.0: {}
 
@@ -8577,6 +8913,8 @@ snapshots:
 
   undici-types@7.24.6: {}
 
+  undici@7.27.2: {}
+
   unicorn-magic@0.3.0: {}
 
   unified@11.0.5:
@@ -8715,7 +9053,7 @@ snapshots:
       fsevents: 2.3.3
       lightningcss: 1.32.0
 
-  vitest@3.2.4(@types/debug@4.1.13)(@types/node@20.19.41)(lightningcss@1.32.0)(msw@2.14.6(@types/node@20.19.41)(typescript@5.9.3)):
+  vitest@3.2.4(@types/debug@4.1.13)(@types/node@20.19.41)(jsdom@29.1.1(@noble/hashes@1.8.0))(lightningcss@1.32.0)(msw@2.14.6(@types/node@20.19.41)(typescript@5.9.3)):
     dependencies:
       '@types/chai': 5.2.3
       '@vitest/expect': 3.2.4
@@ -8743,6 +9081,7 @@ snapshots:
     optionalDependencies:
       '@types/debug': 4.1.13
       '@types/node': 20.19.41
+      jsdom: 29.1.1(@noble/hashes@1.8.0)
     transitivePeerDependencies:
       - less
       - lightningcss
@@ -8754,7 +9093,7 @@ snapshots:
       - supports-color
       - terser
 
-  vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.9.2)(lightningcss@1.32.0)(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3)):
+  vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.9.2)(jsdom@29.1.1(@noble/hashes@1.8.0))(lightningcss@1.32.0)(msw@2.14.6(@types/node@25.9.2)(typescript@5.9.3)):
     dependencies:
       '@types/chai': 5.2.3
       '@vitest/expect': 3.2.4
@@ -8782,6 +9121,7 @@ snapshots:
     optionalDependencies:
       '@types/debug': 4.1.13
       '@types/node': 25.9.2
+      jsdom: 29.1.1(@noble/hashes@1.8.0)
     transitivePeerDependencies:
       - less
       - lightningcss
@@ -8793,8 +9133,24 @@ snapshots:
       - supports-color
       - terser
 
+  w3c-xmlserializer@5.0.0:
+    dependencies:
+      xml-name-validator: 5.0.0
+
   web-streams-polyfill@3.3.3: {}
 
+  webidl-conversions@8.0.1: {}
+
+  whatwg-mimetype@5.0.0: {}
+
+  whatwg-url@16.0.1(@noble/hashes@1.8.0):
+    dependencies:
+      '@exodus/bytes': 1.15.1(@noble/hashes@1.8.0)
+      tr46: 6.0.0
+      webidl-conversions: 8.0.1
+    transitivePeerDependencies:
+      - '@noble/hashes'
+
   which@2.0.2:
     dependencies:
       isexe: 2.0.0
@@ -8829,6 +9185,10 @@ snapshots:
       is-wsl: 3.1.1
       powershell-utils: 0.1.0
 
+  xml-name-validator@5.0.0: {}
+
+  xmlchars@2.2.0: {}
+
   xtend@4.0.2: {}
 
   y18n@5.0.8: {}