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
This commit is contained in:
2026-06-08 03:49:26 +00:00
parent 50de80ee75
commit 6fde7002aa
29 changed files with 3624 additions and 138 deletions

View File

@@ -3,9 +3,9 @@
> **Stack:** fastify, go-net-http | none | react | typescript > **Stack:** fastify, go-net-http | none | react | typescript
> **Microservices:** @boocode/contracts, @boocode/ion, @boocode/booterm, @boocode/coder, @boocode/server, @boocode/web, codecontext, @boocode/conductor > **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.** > **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 ## CRUD Resources
- **`/api/battles`** GET | POST | GET/:id → Battle - **`/api/battles`** GET | POST | GET/:id → Battle
- **`/api/plans`** GET | POST | GET/:id | PATCH/:id → Plan
- **`/api/runs`** GET | POST | GET/:id → Run - **`/api/runs`** GET | POST | GET/:id → Run
- **`/api/tasks`** GET | POST | GET/:id → Task - **`/api/tasks`** GET | POST | GET/:id → Task
- **`/api/chats/:id/messages`** GET | POST | GET/:id | DELETE/:id → Message - **`/api/chats/:id/messages`** GET | POST | GET/:id | DELETE/:id → Message
@@ -25,11 +26,16 @@
### fastify ### fastify
- `GET` `/api/term/health` params() - `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/start` params(sid, pid) [auth]
- `POST` `/api/term/sessions/:sid/panes/:pid/kill` 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` `/ws/term/sessions/:sid/panes/:pid` params(sid, pid) [auth]
- `GET` `/api/health` params() [auth, db, queue, ai] - `GET` `/api/health` params() [auth, db, queue, ai]
- `GET` `/api/sessions/:sessionId/agent-sessions` params(sessionId) [auth, db] - `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/generate-prompt` params() [auth, db]
- `POST` `/api/battles/:id/stop` params(id) [auth, db] - `POST` `/api/battles/:id/stop` params(id) [auth, db]
- `GET` `/api/battles/:id/analysis` 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/apply` params(id) [auth, db, queue]
- `POST` `/api/pending/:id/reject` 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] - `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/snapshot` params() [db, cache]
- `GET` `/api/providers/config` params() [db, cache] - `GET` `/api/providers/config` params() [db, cache]
- `PATCH` `/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/sessions/:sessionId` params(sessionId) [auth, db]
- `GET` `/api/ws/user` params() [auth, db] - `GET` `/api/ws/user` params() [auth, db]
- `GET` `/api/projects/:id/agents` params(id) [db, cache] - `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] - `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/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/projects/:project_id/artifacts/:filename` params(project_id, filename) [auth, db]
- `GET` `/api/sessions/:id/chats` params(id) [auth, db] - `GET` `/api/sessions/:id/chats` params(id) [auth, db, queue]
- `POST` `/api/sessions/:id/chats` params(id) [auth, db] - `POST` `/api/sessions/:id/chats` params(id) [auth, db, queue]
- `PATCH` `/api/chats/:id` params(id) [auth, db] - `PATCH` `/api/chats/:id` params(id) [auth, db, queue]
- `POST` `/api/sessions/:id/chats/archive-all` params(id) [auth, db] - `POST` `/api/sessions/:id/chats/archive-all` params(id) [auth, db, queue]
- `GET` `/api/sessions/:id/chats/open-count` params(id) [auth, db] - `GET` `/api/sessions/:id/chats/open-count` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/archive` params(id) [auth, db] - `POST` `/api/chats/:id/archive` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/unarchive` params(id) [auth, db] - `POST` `/api/chats/:id/unarchive` params(id) [auth, db, queue]
- `DELETE` `/api/chats/:id` params(id) [auth, db] - `DELETE` `/api/chats/:id` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/fork` params(id) [auth, db] - `POST` `/api/chats/:id/fork` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/discard_stale` params(id) [auth, db] - `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] - `GET` `/api/coder/ws/sessions/:sessionId` params(sessionId) [auth]
- `ALL` `/api/coder/*` params() [auth] - `ALL` `/api/coder/*` params() [auth]
- `GET` `/api/settings/inference` params() [cache] - `GET` `/api/settings/inference` params() [cache]
@@ -94,7 +104,9 @@
- `POST` `/api/chats/:id/continue` params(id) [auth, db, queue] - `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/force_send` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/grant_read_access` 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/create` params() [auth, db]
- `POST` `/api/projects/:id/archive` params(id) [auth, db] - `POST` `/api/projects/:id/archive` params(id) [auth, db]
- `POST` `/api/projects/:id/unarchive` params(id) [auth, db] - `POST` `/api/projects/:id/unarchive` params(id) [auth, db]
@@ -122,6 +134,7 @@
- `GET` `/api/skills` params() [auth, db, queue] - `GET` `/api/skills` params() [auth, db, queue]
- `POST` `/api/chats/:id/skill_invoke` params(id) [auth, db, queue] - `POST` `/api/chats/:id/skill_invoke` params(id) [auth, db, queue]
- `GET` `/api/tools/cost_stats` params() [auth, db] - `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] - `GET` `/api/ws/sessions/:id` params(id) [auth, db]
### go-net-http ### go-net-http
@@ -273,6 +286,25 @@
- model: text (required) - model: text (required)
- verdict: text - 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 ### projects
- id: uuid (pk) - id: uuid (pk)
- name: text (required) - name: text (required)
@@ -294,6 +326,8 @@
- content: text (required) - content: text (required)
- status: text (required) - status: text (required)
- last_seq: integer (required) - last_seq: integer (required)
- cache_tokens: integer
- reasoning_tokens: integer
### message_parts ### message_parts
- id: uuid (pk) - id: uuid (pk)
@@ -311,6 +345,45 @@
- name: text - name: text
- status: text (required) - 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 # Components
@@ -325,23 +398,34 @@
- **AttachmentChip** — props: attachment, onRemove, onPreview — `apps/web/src/components/AttachmentChip.tsx` - **AttachmentChip** — props: attachment, onRemove, onPreview — `apps/web/src/components/AttachmentChip.tsx`
- **AttachmentPreviewModal** — props: attachment, onClose — `apps/web/src/components/AttachmentPreviewModal.tsx` - **AttachmentPreviewModal** — props: attachment, onClose — `apps/web/src/components/AttachmentPreviewModal.tsx`
- **BottomSheet** — props: open, onClose, title — `apps/web/src/components/BottomSheet.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` - **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` - **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` - **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` - **ChatThroughput** — props: chatId, className — `apps/web/src/components/ChatThroughput.tsx`
- **CodeBlock** — props: code, lang — `apps/web/src/components/CodeBlock.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` - **ContextMeter** — props: messages, modelContextLimit, sessionCostUsd — `apps/web/src/components/ContextMeter.tsx`
- **CreateProjectModal** — props: open, onOpenChange — `apps/web/src/components/CreateProjectModal.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` - **DoomLoopSentinel** — props: message — `apps/web/src/components/DoomLoopSentinel.tsx`
- **DropOverlay** — props: visible — `apps/web/src/components/DropOverlay.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` - **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` - **FileViewerOverlay** — props: path, content, lang, onClose — `apps/web/src/components/FileViewerOverlay.tsx`
- **FlowLauncherDialog** — `apps/web/src/components/FlowLauncherDialog.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` - **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` - **HtmlArtifactPane** — props: chatId, state, onClose — `apps/web/src/components/HtmlArtifactPane.tsx`
- **InferenceSettings** — `apps/web/src/components/InferenceSettings.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` - **MarkdownArtifactPane** — props: chatId, state, onClose — `apps/web/src/components/MarkdownArtifactPane.tsx`
- **MarkdownRenderer** — props: content — `apps/web/src/components/MarkdownRenderer.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` - **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` - **MessageList** — props: messages, sessionChats — `apps/web/src/components/MessageList.tsx`
- **MobileTabSwitcher** — props: panes, activePaneIdx, chats, onSwitchPane, onRemovePane, onRenameChat — `apps/web/src/components/MobileTabSwitcher.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` - **RequestReadAccessCard** — props: toolCall, toolResult, chatId — `apps/web/src/components/RequestReadAccessCard.tsx`
- **RightRail** — props: projectId, sessionId — `apps/web/src/components/RightRail.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` - **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` - **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` - **StaleStreamBanner** — props: onRetry, onDiscard — `apps/web/src/components/StaleStreamBanner.tsx`
- **StatusDot** — props: chatId, className — `apps/web/src/components/StatusDot.tsx` - **StatusDot** — props: chatId, className — `apps/web/src/components/StatusDot.tsx`
- **ThemePicker** — `apps/web/src/components/ThemePicker.tsx` - **ThemePicker** — `apps/web/src/components/ThemePicker.tsx`
- **ToolCallGroup** — props: runs — `apps/web/src/components/ToolCallGroup.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` - **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` - **AddProviderModal** — props: open, onOpenChange, onAdded — `apps/web/src/components/coder/AddProviderModal.tsx`
- **ProvidersSettings** — `apps/web/src/components/coder/ProvidersSettings.tsx` - **ProvidersSettings** — `apps/web/src/components/coder/ProvidersSettings.tsx`
@@ -367,21 +453,31 @@
- **ThemeFx** — `apps/web/src/components/fx/ThemeFx.tsx` - **ThemeFx** — `apps/web/src/components/fx/ThemeFx.tsx`
- **ClaudeIcon** — props: size, className — `apps/web/src/components/icons/ProviderIcons.tsx` - **ClaudeIcon** — props: size, className — `apps/web/src/components/icons/ProviderIcons.tsx`
- **OpenCodeIcon** — 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` - **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` - **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` - **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` - **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` - **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` - **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` - **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` - **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` - **TerminalHotkeyBar** — props: ctrlArmed, onSendBytes, onArmCtrl, onFit — `apps/web/src/components/panes/terminal/TerminalHotkeyBar.tsx`
- **RightRailDrawerProvider** — `apps/web/src/hooks/useRightRailDrawer.tsx` - **RightRailDrawerProvider** — `apps/web/src/hooks/useRightRailDrawer.tsx`
- **SidebarDrawerProvider** — `apps/web/src/hooks/useSidebarDrawer.tsx` - **SidebarDrawerProvider** — `apps/web/src/hooks/useSidebarDrawer.tsx`
- **PATH_REGEX** — `apps/web/src/lib/linkify-paths.tsx` - **PATH_REGEX** — `apps/web/src/lib/linkify-paths.tsx`
- **Analytics** — `apps/web/src/pages/Analytics.tsx`
- **Home** — `apps/web/src/pages/Home.tsx` - **Home** — `apps/web/src/pages/Home.tsx`
- **Memory** — `apps/web/src/pages/Memory.tsx`
- **Project** — `apps/web/src/pages/Project.tsx` - **Project** — `apps/web/src/pages/Project.tsx`
- **Results** — `apps/web/src/pages/Results.tsx`
- **Session** — `apps/web/src/pages/Session.tsx` - **Session** — `apps/web/src/pages/Session.tsx`
- **Settings** — `apps/web/src/pages/Settings.tsx` - **Settings** — `apps/web/src/pages/Settings.tsx`
@@ -403,8 +499,17 @@
- function ensureSession: (tmuxConfPath, sessionName, projectRoot, log, cols?, rows?) => Promise<void> - function ensureSession: (tmuxConfPath, sessionName, projectRoot, log, cols?, rows?) => Promise<void>
- function killSession: (tmuxConfPath, sessionName) => Promise<boolean> - function killSession: (tmuxConfPath, sessionName) => Promise<boolean>
- function capturePane: (tmuxConfPath, sessionName, lines) => Promise<string> - function capturePane: (tmuxConfPath, sessionName, lines) => Promise<string>
- _...1 more_
- `apps/booterm/src/pty/pty.ts` — function attachPty: (opts) => IPty - `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` - `apps/coder/src/conductor/contracts.ts`
- function produceContract: (contracts) => string - function produceContract: (contracts) => string
- function reviewContract: (contracts) => string - function reviewContract: (contracts) => string
@@ -491,7 +596,7 @@
- function classifyLane: (battleType, _identity, model, localModels) => ContestantLane - function classifyLane: (battleType, _identity, model, localModels) => ContestantLane
- function nextLocalContestant: (contestants) => string | null - function nextLocalContestant: (contestants) => string | null
- function isBattleComplete: (contestants) => boolean - function isBattleComplete: (contestants) => boolean
- function computeBenchmark: (startedAt, endedAt, costTokens, lane) => Benchmark - function computeBenchmark: (startedAt, endedAt, costTokens, lane, tokenBreakdown) => Benchmark
- function sanitizeSlug: (s) => string - function sanitizeSlug: (s) => string
- function buildBattleSlug: (battleId, battleType, createdAt) => string - function buildBattleSlug: (battleId, battleType, createdAt) => string
- _...7 more_ - _...7 more_
@@ -555,6 +660,7 @@
- function stepEndedToUsage: (props) => StepUsage - function stepEndedToUsage: (props) => StepUsage
- interface StepEndedProps - interface StepEndedProps
- interface StepUsage - interface StepUsage
- `apps/coder/src/services/backends/paseo.ts` — class PaseoBackend, interface PaseoBackendDeps
- `apps/coder/src/services/backends/pushable-iterable.ts` — function createPushable: () => Pushable<T>, interface Pushable - `apps/coder/src/services/backends/pushable-iterable.ts` — function createPushable: () => Pushable<T>, interface Pushable
- `apps/coder/src/services/backends/turn-guard.ts` - `apps/coder/src/services/backends/turn-guard.ts`
- function armAbortGuard: (g) => void - function armAbortGuard: (g) => void
@@ -563,6 +669,30 @@
- interface AbortTerminalGuard - 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-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/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<GuidelineMatchingResult>
- 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/cancel-registry.ts` — function createCancelRegistry: () => CancelRegistry, interface CancelRegistry
- `apps/coder/src/services/checkpoints.ts` - `apps/coder/src/services/checkpoints.ts`
- function buildShadowCommitCommand: (worktreePath, id) => string - function buildShadowCommitCommand: (worktreePath, id) => string
@@ -573,7 +703,15 @@
- interface RestoreCheckpointResult - interface RestoreCheckpointResult
- _...1 more_ - _...1 more_
- `apps/coder/src/services/claude-command-discovery.ts` — function discoverClaudeCommands: () => AgentCommand[] - `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<boolean> - `apps/coder/src/services/command-availability.ts` — function isCommandAvailable: (binary) => Promise<boolean>
- `apps/coder/src/services/conflict-index.ts` — class ConflictIndex, const conflictIndex
- `apps/coder/src/services/correction-service.ts` - `apps/coder/src/services/correction-service.ts`
- function recordCorrection: (originalClaim, correction, principleExtracted, persistedTo, basePath?) => Promise<UserCorrectionRecord> - function recordCorrection: (originalClaim, correction, principleExtracted, persistedTo, basePath?) => Promise<UserCorrectionRecord>
- function scanForCorrections: (auditPath) => Promise<UserCorrectionRecord[]> - function scanForCorrections: (auditPath) => Promise<UserCorrectionRecord[]>
@@ -603,10 +741,11 @@
- function partitionReady: (ready, ctx) => void - function partitionReady: (ready, ctx) => void
- function isRunComplete: (flow, state) => boolean - function isRunComplete: (flow, state) => boolean
- function isStuck: (flow, state) => boolean - function isStuck: (flow, state) => boolean
- function reconcileResumeStep: (status, taskId, taskState) => ResumeAction - function buildBatchState: (flow, inFlight) => Map<string,
- _...5 more_ - _...12 more_
- `apps/coder/src/services/flow-runner.ts` - `apps/coder/src/services/flow-runner.ts`
- function createFlowRunner: (deps) => FlowRunner - function createFlowRunner: (deps) => FlowRunner
- function resolveVariables: (prompt, results, string>) => string
- interface LaunchOpts - interface LaunchOpts
- interface FlowRunner - interface FlowRunner
- `apps/coder/src/services/frame-emitter.ts` - `apps/coder/src/services/frame-emitter.ts`
@@ -626,6 +765,19 @@
- function deleteGuideline: (id, basePath?) => Promise<boolean> - function deleteGuideline: (id, basePath?) => Promise<boolean>
- function findGuideline: (content, basePath?) => Promise<Guideline | null> - function findGuideline: (content, basePath?) => Promise<Guideline | null>
- _...14 more_ - _...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<HostExecResult>, interface HostExecResult - `apps/coder/src/services/host-exec.ts` — function hostExec: (command, opts?) => Promise<HostExecResult>, interface HostExecResult
- `apps/coder/src/services/lsp/client.ts` — class LspClient - `apps/coder/src/services/lsp/client.ts` — class LspClient
- `apps/coder/src/services/lsp/config.ts` — function getServerConfig: (filePath) => LspServerConfig | null, interface LspServerConfig - `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<Location[]> - function findReferences: (client, filePath, content, line, character) => Promise<Location[]>
- `apps/coder/src/services/lsp/server-manager.ts` — class LspServerManager, const lspManager - `apps/coder/src/services/lsp/server-manager.ts` — class LspServerManager, const lspManager
- `apps/coder/src/services/mcp-server.ts` — function startMcpServer: (sql) => Promise<void> - `apps/coder/src/services/mcp-server.ts` — function startMcpServer: (sql) => Promise<void>
- `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` - `apps/coder/src/services/net/port-utils.ts`
- function reclaimPort: (port) => void - function reclaimPort: (port) => void
- function waitForPortRelease: (port, timeoutMs) => Promise<boolean> - function waitForPortRelease: (port, timeoutMs) => Promise<boolean>
@@ -646,6 +836,13 @@
- function createOrphanWorktreeReaper: (deps) => void - function createOrphanWorktreeReaper: (deps) => void
- interface OrphanWorktreeReaperDeps - interface OrphanWorktreeReaperDeps
- interface OrphanReaperResult - 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` - `apps/coder/src/services/pending_changes.ts`
- function planEdit: (content, oldStr, newStr) => EditPlan - function planEdit: (content, oldStr, newStr) => EditPlan
- function queueEdit: (sql, sessionId, taskId, filePath, oldString, newString, projectRoot, // v2.6 Phase 1-UX) => void - 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<CreateElicitationResponse> - function waitForElicitationResponse: (taskId, sessionId, provider, modeId, params, timeoutMs) => Promise<CreateElicitationResponse>
- function cancelPendingPermission: (taskId) => void - function cancelPendingPermission: (taskId) => void
- _...3 more_ - _...3 more_
- `apps/coder/src/services/plan-store.ts`
- function createPlan: (sql, opts) => Promise<Plan>
- function getPlan: (sql, planId) => Promise<Plan | null>
- function listPlans: (sql, projectId) => Promise<Plan[]>
- function listActivePlans: (sql, projectId) => Promise<Plan[]>
- function updatePlan: (sql, planId, opts) => Promise<Plan | null>
- function updatePlanFromRun: (sql, runId, runStatus) => Promise<boolean>
- _...5 more_
- `apps/coder/src/services/provider-commands.ts` - `apps/coder/src/services/provider-commands.ts`
- function getManifestCommands: (provider) => AgentCommand[] - function getManifestCommands: (provider) => AgentCommand[]
- function mergeCommands: (...lists) => AgentCommand[] - function mergeCommands: (...lists) => AgentCommand[]
@@ -684,13 +889,13 @@
- interface ProviderManifestEntry - interface ProviderManifestEntry
- const PROVIDER_MANIFEST: Record<string, ProviderManifestEntry> - const PROVIDER_MANIFEST: Record<string, ProviderManifestEntry>
- `apps/coder/src/services/provider-snapshot.ts` - `apps/coder/src/services/provider-snapshot.ts`
- function fetchDeepSeekModels: (config) => Promise<ProviderModel[]>
- function fetchLlamaSwapModels: (config) => Promise<ProviderModel[]> - function fetchLlamaSwapModels: (config) => Promise<ProviderModel[]>
- function prefixLlamaSwapModels: (models) => ProviderModel[] - function prefixLlamaSwapModels: (models) => ProviderModel[]
- function mergeModels: (...lists) => ProviderModel[] - function mergeModels: (...lists) => ProviderModel[]
- function getProviderSnapshot: (sql, config, cwd?, force) => Promise<ProviderSnapshotEntry[]> - function getProviderSnapshot: (sql, config, cwd?, force) => Promise<ProviderSnapshotEntry[]>
- function clearProviderSnapshotCache: () => void - function clearProviderSnapshotCache: () => void
- function peekSnapshotEntry: (name, cwd?) => ProviderSnapshotEntry | undefined - _...2 more_
- _...1 more_
- `apps/coder/src/services/pty-dispatch.ts` - `apps/coder/src/services/pty-dispatch.ts`
- function dispatchViaPty: (opts) => Promise<DispatchResult> - function dispatchViaPty: (opts) => Promise<DispatchResult>
- interface DispatchResult - interface DispatchResult
@@ -800,6 +1005,17 @@
- function readSession: (sessionId, projectRoot?) => SessionJson | null - function readSession: (sessionId, projectRoot?) => SessionJson | null
- _...9 more_ - _...9 more_
- `apps/server/src/services/auto_name.ts` — function maybeAutoNameChat: (ctx, chatId, sessionId) => Promise<void> - `apps/server/src/services/auto_name.ts` — function maybeAutoNameChat: (ctx, chatId, sessionId) => Promise<void>
- `apps/server/src/services/background-task.ts`
- function setBackgroundInferenceEnqueuer: (enqueue, chatId, assistantMessageId, user) => void
- function spawnBackgroundTask: (sql, log, projectId, input, model, agent?, label?) => Promise<BackgroundTask>
- function getBackgroundTaskStatus: (sql, taskId) => Promise<BackgroundTask | null>
- function getBackgroundTaskResult: (sql, taskId, chatId) => Promise<
- function cancelBackgroundTask: (sql, taskId) => Promise<boolean>
- 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` - `apps/server/src/services/broker.ts`
- function createBroker: (log?) => Broker - function createBroker: (log?) => Broker
- interface Broker - interface Broker
@@ -818,6 +1034,7 @@
- function select: (messages, contextLimit, tailTurns) => SelectResult - function select: (messages, contextLimit, tailTurns) => SelectResult
- function deriveFilesRead: (head) => string[] - function deriveFilesRead: (head) => string[]
- _...8 more_ - _...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<string[]> - `apps/server/src/services/file_index.ts` — function getProjectFiles: (projectId, projectRoot) => Promise<string[]>
- `apps/server/src/services/file_ops.ts` - `apps/server/src/services/file_ops.ts`
- function listDir: (projectRoot, relPath, opts?) => Promise<ListDirResult> - function listDir: (projectRoot, relPath, opts?) => Promise<ListDirResult>
@@ -842,7 +1059,20 @@
- interface GiteaConfig - interface GiteaConfig
- interface GiteaRepo - interface GiteaRepo
- `apps/server/src/services/grant_resolver.ts` — function resolveGrantRoot: (sql, requestedPath, projectRoot, whitelistRoot) => Promise<GrantResolution>, type GrantResolution - `apps/server/src/services/grant_resolver.ts` — function resolveGrantRoot: (sql, requestedPath, projectRoot, whitelistRoot) => Promise<GrantResolution>, 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/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/content-flusher.ts` — function createContentFlusher: (sql, messageId, getContent) => void, interface ContentFlusher
- `apps/server/src/services/inference/dcp/messages.ts` - `apps/server/src/services/inference/dcp/messages.ts`
- function toDcpMessages: (parts) => DcpMessage[] - function toDcpMessages: (parts) => DcpMessage[]
@@ -882,6 +1112,10 @@
- type FailureKind - type FailureKind
- const MISTAKE_THRESHOLD - const MISTAKE_THRESHOLD
- _...1 more_ - _...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` - `apps/server/src/services/inference/parts.ts`
- function insertParts: (sql, parts) => Promise<void> - function insertParts: (sql, parts) => Promise<void>
- function partsFromAssistantMessage: (args) => void - function partsFromAssistantMessage: (args) => void
@@ -894,10 +1128,13 @@
- function maybeFlagForCompaction: (ctx, chatId, updated) => Promise<void> - function maybeFlagForCompaction: (ctx, chatId, updated) => Promise<void>
- interface OpenAiMessage - interface OpenAiMessage
- `apps/server/src/services/inference/provider.ts` - `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 upstreamModel: (config, modelId, agent?) => LanguageModel
- function resolveModelEndpoint: (config, modelId) => void
- function resetDeepSeekProvider: () => void
- interface RoutingInfo - interface RoutingInfo
- type InferenceRoute - _...1 more_
- `apps/server/src/services/inference/prune.ts` - `apps/server/src/services/inference/prune.ts`
- function selectPruneTargets: (partsNewestFirst, tailStartCreatedAt) => void - function selectPruneTargets: (partsNewestFirst, tailStartCreatedAt) => void
- function prune: (args) => Promise<PruneResult> - function prune: (args) => Promise<PruneResult>
@@ -918,6 +1155,12 @@
- function isAnySentinel: (m) => boolean - function isAnySentinel: (m) => boolean
- const DOOM_LOOP_THRESHOLD - const DOOM_LOOP_THRESHOLD
- _...1 more_ - _...1 more_
- `apps/server/src/services/inference/state-graph.ts`
- function createDefaultGraph: () => GraphNode[]
- function runGraph: (ctx, args, extra) => Promise<GraphResult>
- interface GraphState
- interface GraphResult
- type GraphNodeType
- `apps/server/src/services/inference/step-decision.ts` - `apps/server/src/services/inference/step-decision.ts`
- function decideStep: (input) => PreStepDecision - function decideStep: (input) => PreStepDecision
- function decidePostToolAction: (action, mistakeTracker) => PostToolDecision - 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 - `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. // tool list sent to the LLM, so the model can't even attempt them.
webToolsEnabled) => Promise<StreamResult> webToolsEnabled) => Promise<StreamResult>
- `apps/server/src/services/inference/supervisor.ts` — function resolveSupervisorTurn: (latestUserMessage, agents, fallbackModel?) => Promise<SupervisorRoute | null>, interface SupervisorRoute
- `apps/server/src/services/inference/tool-call-parser.ts` - `apps/server/src/services/inference/tool-call-parser.ts`
- function stripToolMarkup: (text, opts?) => string - function stripToolMarkup: (text, opts?) => string
- function extractToolCallBlocks: (buffer, log?) => ToolCallExtraction - function extractToolCallBlocks: (buffer, log?) => ToolCallExtraction
- interface ParsedCall - interface ParsedCall
- interface ToolCallExtraction - interface ToolCallExtraction
- `apps/server/src/services/inference/tool-phase.ts` — function executeToolPhase: (ctx, args, result, startedAt, session, projectRoot, agent?) => Promise<ToolPhaseResult>, 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<ToolPhaseResult>, interface ToolPhaseResult
- `apps/server/src/services/inference/tool-shim.ts` - `apps/server/src/services/inference/tool-shim.ts`
- function extractToolCalls: (text) => ParsedToolCall[] - function extractToolCalls: (text) => ParsedToolCall[]
- function hasToolCallMarkup: (text) => boolean - function hasToolCallMarkup: (text) => boolean
@@ -955,20 +1200,26 @@
- `apps/server/src/services/inference/turn.ts` - `apps/server/src/services/inference/turn.ts`
- function runAssistantTurn: (ctx, args) => Promise<void> - function runAssistantTurn: (ctx, args) => Promise<void>
- function runInference: (ctx, sessionId, chatId, assistantMessageId, signal?) => Promise<void> - function runInference: (ctx, sessionId, chatId, assistantMessageId, signal?) => Promise<void>
- function runInferenceWithModel: (ctx, sessionId, chatId, assistantMessageId, modelOverride, compareGroupId, signal?) => Promise<void>
- function createInferenceRunner: (ctx, 'publishUser'>, publishUserFn, frame) => void - function createInferenceRunner: (ctx, 'publishUser'>, publishUserFn, frame) => void
- `apps/server/src/services/mcp-client.ts` - `apps/server/src/services/mcp-client.ts`
- function initialize: (entries, logger) => Promise<void> - function initialize: (entries, logger) => Promise<void>
- function callTool: (prefixedName, args, unknown>) => Promise<unknown> - function callTool: (prefixedName, args, unknown>) => Promise<unknown>
- function getServerPermission: (prefixedToolName) => McpPermission
- function setServerPermission: (serverName, permission) => void
- function getServerName: (prefixedToolName) => string | null
- function getTools: () => ToolDef<Record<string, unknown>>[] - function getTools: () => ToolDef<Record<string, unknown>>[]
- function getMcpServers: () => Array< - _...6 more_
- function shutdown: () => Promise<void>
- function wrapMcpTool: (serverName, mcpTool) => ToolDef<Record<string, unknown>>
- _...2 more_
- `apps/server/src/services/mcp-config.ts` - `apps/server/src/services/mcp-config.ts`
- function substituteEnvVars: (value, log, unsetVars?) => unknown - function substituteEnvVars: (value, log, unsetVars?) => unknown
- function loadMcpConfig: (configPath, log) => McpServerEntry[] - function loadMcpConfig: (configPath, log) => McpServerEntry[]
- interface McpServerEntry - interface McpServerEntry
- type McpServerConfig - 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<boolean>
- function embed: (texts) => Promise<number[][] | null>
- `apps/server/src/services/memory/entries.ts` — function parseMemoryEntries: (fileName, markdown) => MemoryEntry[], interface MemoryEntry - `apps/server/src/services/memory/entries.ts` — function parseMemoryEntries: (fileName, markdown) => MemoryEntry[], interface MemoryEntry
- `apps/server/src/services/memory/paths.ts` - `apps/server/src/services/memory/paths.ts`
- function getMemoryRoot: (projectRoot) => string - function getMemoryRoot: (projectRoot) => string
@@ -976,7 +1227,10 @@
- function ensureMemoryScaffold: (root) => Promise<void> - function ensureMemoryScaffold: (root) => Promise<void>
- type MemoryTopic - type MemoryTopic
- `apps/server/src/services/memory/prompt.ts` — function formatMemoryBlock: (entries) => string - `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<string[]> - `apps/server/src/services/memory/recall.ts`
- function rankByRelevance: (query, entries) => MemoryEntry[]
- function rankByHybrid: (query, entries) => Promise<MemoryEntry[]>
- function loadMemoryForSession: (projectRoot, _sessionId?, query?) => Promise<string[]>
- `apps/server/src/services/memory/scan.ts` - `apps/server/src/services/memory/scan.ts`
- function scanMemoryScopes: (scope) => Promise<MemoryEntry[]> - function scanMemoryScopes: (scope) => Promise<MemoryEntry[]>
- function scanProjectMemory: (projectRoot) => Promise<MemoryEntry[]> - function scanProjectMemory: (projectRoot) => Promise<MemoryEntry[]>
@@ -1007,6 +1261,11 @@
- function filterSecretEntries: (entries, pathOf) => void - function filterSecretEntries: (entries, pathOf) => void
- class SecretBlockedError - class SecretBlockedError
- const DEFAULT_SECURITY_IGNORE_FILETYPES: ReadonlyArray<string> - const DEFAULT_SECURITY_IGNORE_FILETYPES: ReadonlyArray<string>
- `apps/server/src/services/session-snapshots.ts`
- function saveAgentSnapshot: (sql, chatId, data) => Promise<void>
- function loadAgentSnapshot: (sql, chatId) => Promise<AgentSnapshot | null>
- function deleteAgentSnapshot: (sql, chatId) => Promise<void>
- interface AgentSnapshot
- `apps/server/src/services/skill-invoke.ts` - `apps/server/src/services/skill-invoke.ts`
- function runSkillInvokeTransaction: (sql, args) => Promise< - function runSkillInvokeTransaction: (sql, args) => Promise<
- function buildSkillInvokeSyntheticFrames: (chatId, result, toolCall, skillBody) => SkillInvokeSessionFrame[] - function buildSkillInvokeSyntheticFrames: (chatId, result, toolCall, skillBody) => SkillInvokeSessionFrame[]
@@ -1037,8 +1296,53 @@
- _...2 more_ - _...2 more_
- `apps/server/src/services/task-model.ts` — function taskModelCompletion: (opts) => Promise<string> - `apps/server/src/services/task-model.ts` — function taskModelCompletion: (opts) => Promise<string>
- `apps/server/src/services/task-search-rewrite.ts` — function rewriteSearchQuery: (userMessage) => Promise<string> - `apps/server/src/services/task-search-rewrite.ts` — function rewriteSearchQuery: (userMessage) => Promise<string>
- `apps/server/src/services/tool-traces.ts`
- function insertToolTrace: (sql, insert) => Promise<ToolTrace>
- function updateToolTrace: (sql, id, updates) => Promise<ToolTrace | null>
- interface ToolTrace
- interface ToolTraceInsert
- interface ToolTraceUpdate
- `apps/server/src/services/tools/background-subagent-tools.ts`
- function executeSpawnSubagent: (input, sql, sessionId) => Promise<Record<string, unknown>>
- function executeSubagentStatus: (input, sql) => Promise<Record<string, unknown>>
- function executeSubagentResult: (input, sql) => Promise<Record<string, unknown>>
- type SpawnSubagentInputT
- type SubagentStatusInputT
- type SubagentResultInputT
- _...6 more_
- `apps/server/src/services/tools/codecontext/factory.ts` — function makeCodecontextTool: (opts, unknown>; - `apps/server/src/services/tools/codecontext/factory.ts` — function makeCodecontextTool: (opts, unknown>;
mapArgs) => void mapArgs) => void
- `apps/server/src/services/tools/codecontext/get_code_health.ts`
- function executeGetCodeHealth: (input, projectPath) => Promise<string>
- type GetCodeHealthInputT
- const GetCodeHealthInput
- const getCodeHealth: ToolDef<GetCodeHealthInputT>
- `apps/server/src/services/tools/codecontext/get_code_impact.ts`
- function executeGetCodeImpact: (input, projectPath) => Promise<CodecontextResponse>
- type GetCodeImpactInputT
- const GetCodeImpactInput
- const getCodeImpact: ToolDef<GetCodeImpactInputT>
- `apps/server/src/services/tools/codecontext/get_code_map.ts`
- function executeGetCodeMap: (input, projectRoot) => Promise<CodeMapResponse>
- interface CodeMapResponse
- type GetCodeMapInputT
- const GetCodeMapInput
- const getCodeMap: ToolDef<GetCodeMapInputT>
- `apps/server/src/services/tools/codecontext/get_type_info.ts`
- function executeGetTypeInfo: (input, _projectPath?) => Promise<CodecontextResponse>
- type GetTypeInfoInputT
- const GetTypeInfoInput
- const getTypeInfo: ToolDef<GetTypeInfoInputT>
- `apps/server/src/services/tools/codecontext/get_wiki_article.ts`
- function executeGetWikiArticle: (input, projectPath) => Promise<string>
- type GetWikiArticleInputT
- const GetWikiArticleInput
- const getWikiArticle: ToolDef<GetWikiArticleInputT>
- `apps/server/src/services/tools/execute-command.ts`
- function executeRunCommand: (input, projectRoot) => Promise<RunCommandOutput>
- type RunCommandInputT
- type RunCommandOutput
- const runCommand: ToolDef<RunCommandInputT>
- `apps/server/src/services/tools/registry.ts` — function appendMcpTools: (mcpTools) => void, function toolJsonSchemas: () => ToolJsonSchema[] - `apps/server/src/services/tools/registry.ts` — function appendMcpTools: (mcpTools) => void, function toolJsonSchemas: () => ToolJsonSchema[]
- `apps/server/src/services/tools/tiers.ts` - `apps/server/src/services/tools/tiers.ts`
- function resolveToolTier: (tier) => readonly string[] - function resolveToolTier: (tier) => readonly string[]
@@ -1064,6 +1368,39 @@
- interface WebSearchOutput - interface WebSearchOutput
- type WebSearchInputT - type WebSearchInputT
- const webSearch: ToolDef<WebSearchInputT> - const webSearch: ToolDef<WebSearchInputT>
- `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<string, unknown>
- function loadWorkflowScript: (sourceFile, context) => (...args: unknown[]) => Promise<unknown>
- function loadWorkflowScriptFromCode: (code, context, filename?) => (...args: unknown[]) => Promise<unknown>
- _...3 more_
- `apps/server/src/utils/string-utils.ts` — function stripQuotes: (s) => string - `apps/server/src/utils/string-utils.ts` — function stripQuotes: (s) => string
- `apps/web/src/api/client.ts` - `apps/web/src/api/client.ts`
- class ApiError - class ApiError
@@ -1084,7 +1421,7 @@
- interface TerminalSelectionActions - interface TerminalSelectionActions
- interface TerminalSelection - interface TerminalSelection
- `apps/web/src/hooks/terminal/useTerminalSocket.ts` - `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 - interface TerminalSocket
- type ConnState - type ConnState
- `apps/web/src/hooks/useActivePane.ts` - `apps/web/src/hooks/useActivePane.ts`
@@ -1108,7 +1445,8 @@
- interface ThroughputSample - interface ThroughputSample
- `apps/web/src/hooks/useCoderUserEvents.ts` — function useCoderUserEvents: () => void - `apps/web/src/hooks/useCoderUserEvents.ts` — function useCoderUserEvents: () => void
- `apps/web/src/hooks/useDiffPreferences.ts` — function useDiffPreferences: () => void, interface DiffPreferences - `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/useLongPress.ts` — function useLongPress: (callback) => void
- `apps/web/src/hooks/useProjectGit.ts` — function useProjectGit: (projectId) => GitMeta | null - `apps/web/src/hooks/useProjectGit.ts` — function useProjectGit: (projectId) => GitMeta | null
- `apps/web/src/hooks/useProviderSnapshot.ts` — function refreshProviderSnapshot: (cwd?) => Promise<ProviderSnapshotEntry[]>, function useProviderSnapshot: (cwd?) => ProviderSnapshotEntry[] | null - `apps/web/src/hooks/useProviderSnapshot.ts` — function refreshProviderSnapshot: (cwd?) => Promise<ProviderSnapshotEntry[]>, function useProviderSnapshot: (cwd?) => ProviderSnapshotEntry[] | null
@@ -1121,6 +1459,7 @@
- `apps/web/src/hooks/useSessions.ts` — function useSessions: (projectId) => void - `apps/web/src/hooks/useSessions.ts` — function useSessions: (projectId) => void
- `apps/web/src/hooks/useSidebar.ts` — function useSidebar: () => void - `apps/web/src/hooks/useSidebar.ts` — function useSidebar: () => void
- `apps/web/src/hooks/useSkills.ts` — function useSkills: () => 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/useUserEvents.ts` — function useUserEvents: () => void
- `apps/web/src/hooks/useViewport.ts` — function useViewport: () => ViewportSnapshot, interface ViewportSnapshot - `apps/web/src/hooks/useViewport.ts` — function useViewport: () => ViewportSnapshot, interface ViewportSnapshot
- `apps/web/src/hooks/useWorkspacePanes.ts` - `apps/web/src/hooks/useWorkspacePanes.ts`
@@ -1183,7 +1522,16 @@
- interface ThemeMeta - interface ThemeMeta
- type ThemeId - type ThemeId
- _...5 more_ - _...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/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` - `apps/web/src/utils/diff-layout.ts`
- function parseDiff: (diffBody) => ParsedDiffFile[] - function parseDiff: (diffBody) => ParsedDiffFile[]
- function buildSplitRows: (file) => SplitRow[] - function buildSplitRows: (file) => SplitRow[]
@@ -1344,8 +1692,11 @@
- `CONTAINER_GUIDANCE_FILE` **required** — apps/server/src/services/__tests__/system-prompt.test.ts - `CONTAINER_GUIDANCE_FILE` **required** — apps/server/src/services/__tests__/system-prompt.test.ts
- `CONTEXT7_API_KEY` (has default) — .env - `CONTEXT7_API_KEY` (has default) — .env
- `DATABASE_URL` (has default) — .env.example - `DATABASE_URL` (has default) — .env.example
- `DEEPSEEK_API_KEY` (has default) — .env
- `DEEPSEEK_BASE_URL` (has default) — .env
- `DEFAULT_MODEL` (has default) — .env.example - `DEFAULT_MODEL` (has default) — .env.example
- `DEV_REMOTE_USER` **required** — apps/web/vite.config.ts - `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_BASE_URL` (has default) — .env
- `GITEA_SSH_HOST` (has default) — .env - `GITEA_SSH_HOST` (has default) — .env
- `GITEA_TOKEN` (has default) — .env - `GITEA_TOKEN` (has default) — .env
@@ -1353,6 +1704,7 @@
- `LLAMA_SWAP_URL` (has default) — .env.example - `LLAMA_SWAP_URL` (has default) — .env.example
- `MCP_TEST_MISSING` **required** — apps/server/src/services/__tests__/mcp-config.test.ts - `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 - `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 - `NODE_ENV` (has default) — .env.example
- `PORT` (has default) — .env.example - `PORT` (has default) — .env.example
- `POSTGRES_PASSWORD` (has default) — .env.example - `POSTGRES_PASSWORD` (has default) — .env.example
@@ -1368,6 +1720,10 @@
- `apps/web/vite.config.ts` - `apps/web/vite.config.ts`
- `docker-compose.yml` - `docker-compose.yml`
## Key Dependencies
- better-sqlite3: ^11.10.0
--- ---
# Middleware # Middleware
@@ -1379,6 +1735,7 @@
- turn-guard — `apps/coder/src/services/backends/turn-guard.ts` - turn-guard — `apps/coder/src/services/backends/turn-guard.ts`
- get_middleware — `apps/server/src/services/tools/codecontext/get_middleware.ts` - get_middleware — `apps/server/src/services/tools/codecontext/get_middleware.ts`
- authoring — `conductor/src/flows/authoring.ts` - authoring — `conductor/src/flows/authoring.ts`
- spec — `openspec/changes/add-behavioral-engine/specs/audit-middleware/spec.md`
## custom ## custom
- write_guard.test — `apps/coder/src/services/__tests__/write_guard.test.ts` - write_guard.test — `apps/coder/src/services/__tests__/write_guard.test.ts`
@@ -1400,39 +1757,39 @@
## Most Imported Files (change these carefully) ## Most Imported Files (change these carefully)
- `apps/coder/src/db.ts` — imported by **40** files - `apps/coder/src/db.ts` — imported by **44** files
- `apps/server/src/types/api.ts` — imported by **28** files - `apps/server/src/types/api.ts` — imported by **34** files
- `apps/server/src/db.ts` — imported by **25** files - `apps/server/src/db.ts` — imported by **32** files
- `packages/ion/src/cli/utils.ts` — imported by **24** 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/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/agent-backend.ts` — imported by **14** files
- `apps/coder/src/services/acp-tool-snapshot.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/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 - `conductor/src/types.ts` — imported by **13** files
- `apps/coder/src/services/provider-config-registry.ts` — imported by **12** 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/config.ts` — imported by **11** files
- `apps/coder/src/services/provider-types.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/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/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) ## 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/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` +23 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/artifacts.ts`, `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts` +20 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 - `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/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/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/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/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
- `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
--- ---

View File

@@ -10,23 +10,34 @@
- **AttachmentChip** — props: attachment, onRemove, onPreview — `apps/web/src/components/AttachmentChip.tsx` - **AttachmentChip** — props: attachment, onRemove, onPreview — `apps/web/src/components/AttachmentChip.tsx`
- **AttachmentPreviewModal** — props: attachment, onClose — `apps/web/src/components/AttachmentPreviewModal.tsx` - **AttachmentPreviewModal** — props: attachment, onClose — `apps/web/src/components/AttachmentPreviewModal.tsx`
- **BottomSheet** — props: open, onClose, title — `apps/web/src/components/BottomSheet.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` - **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` - **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` - **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` - **ChatThroughput** — props: chatId, className — `apps/web/src/components/ChatThroughput.tsx`
- **CodeBlock** — props: code, lang — `apps/web/src/components/CodeBlock.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` - **ContextMeter** — props: messages, modelContextLimit, sessionCostUsd — `apps/web/src/components/ContextMeter.tsx`
- **CreateProjectModal** — props: open, onOpenChange — `apps/web/src/components/CreateProjectModal.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` - **DoomLoopSentinel** — props: message — `apps/web/src/components/DoomLoopSentinel.tsx`
- **DropOverlay** — props: visible — `apps/web/src/components/DropOverlay.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` - **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` - **FileViewerOverlay** — props: path, content, lang, onClose — `apps/web/src/components/FileViewerOverlay.tsx`
- **FlowLauncherDialog** — `apps/web/src/components/FlowLauncherDialog.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` - **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` - **HtmlArtifactPane** — props: chatId, state, onClose — `apps/web/src/components/HtmlArtifactPane.tsx`
- **InferenceSettings** — `apps/web/src/components/InferenceSettings.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` - **MarkdownArtifactPane** — props: chatId, state, onClose — `apps/web/src/components/MarkdownArtifactPane.tsx`
- **MarkdownRenderer** — props: content — `apps/web/src/components/MarkdownRenderer.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` - **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` - **MessageList** — props: messages, sessionChats — `apps/web/src/components/MessageList.tsx`
- **MobileTabSwitcher** — props: panes, activePaneIdx, chats, onSwitchPane, onRemovePane, onRenameChat — `apps/web/src/components/MobileTabSwitcher.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` - **RequestReadAccessCard** — props: toolCall, toolResult, chatId — `apps/web/src/components/RequestReadAccessCard.tsx`
- **RightRail** — props: projectId, sessionId — `apps/web/src/components/RightRail.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` - **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` - **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` - **StaleStreamBanner** — props: onRetry, onDiscard — `apps/web/src/components/StaleStreamBanner.tsx`
- **StatusDot** — props: chatId, className — `apps/web/src/components/StatusDot.tsx` - **StatusDot** — props: chatId, className — `apps/web/src/components/StatusDot.tsx`
- **ThemePicker** — `apps/web/src/components/ThemePicker.tsx` - **ThemePicker** — `apps/web/src/components/ThemePicker.tsx`
- **ToolCallGroup** — props: runs — `apps/web/src/components/ToolCallGroup.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` - **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` - **AddProviderModal** — props: open, onOpenChange, onAdded — `apps/web/src/components/coder/AddProviderModal.tsx`
- **ProvidersSettings** — `apps/web/src/components/coder/ProvidersSettings.tsx` - **ProvidersSettings** — `apps/web/src/components/coder/ProvidersSettings.tsx`
@@ -52,20 +65,30 @@
- **ThemeFx** — `apps/web/src/components/fx/ThemeFx.tsx` - **ThemeFx** — `apps/web/src/components/fx/ThemeFx.tsx`
- **ClaudeIcon** — props: size, className — `apps/web/src/components/icons/ProviderIcons.tsx` - **ClaudeIcon** — props: size, className — `apps/web/src/components/icons/ProviderIcons.tsx`
- **OpenCodeIcon** — 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` - **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` - **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` - **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` - **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` - **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` - **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` - **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` - **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` - **TerminalHotkeyBar** — props: ctrlArmed, onSendBytes, onArmCtrl, onFit — `apps/web/src/components/panes/terminal/TerminalHotkeyBar.tsx`
- **RightRailDrawerProvider** — `apps/web/src/hooks/useRightRailDrawer.tsx` - **RightRailDrawerProvider** — `apps/web/src/hooks/useRightRailDrawer.tsx`
- **SidebarDrawerProvider** — `apps/web/src/hooks/useSidebarDrawer.tsx` - **SidebarDrawerProvider** — `apps/web/src/hooks/useSidebarDrawer.tsx`
- **PATH_REGEX** — `apps/web/src/lib/linkify-paths.tsx` - **PATH_REGEX** — `apps/web/src/lib/linkify-paths.tsx`
- **Analytics** — `apps/web/src/pages/Analytics.tsx`
- **Home** — `apps/web/src/pages/Home.tsx` - **Home** — `apps/web/src/pages/Home.tsx`
- **Memory** — `apps/web/src/pages/Memory.tsx`
- **Project** — `apps/web/src/pages/Project.tsx` - **Project** — `apps/web/src/pages/Project.tsx`
- **Results** — `apps/web/src/pages/Results.tsx`
- **Session** — `apps/web/src/pages/Session.tsx` - **Session** — `apps/web/src/pages/Session.tsx`
- **Settings** — `apps/web/src/pages/Settings.tsx` - **Settings** — `apps/web/src/pages/Settings.tsx`

View File

@@ -25,8 +25,11 @@
- `CONTAINER_GUIDANCE_FILE` **required** — apps/server/src/services/__tests__/system-prompt.test.ts - `CONTAINER_GUIDANCE_FILE` **required** — apps/server/src/services/__tests__/system-prompt.test.ts
- `CONTEXT7_API_KEY` (has default) — .env - `CONTEXT7_API_KEY` (has default) — .env
- `DATABASE_URL` (has default) — .env.example - `DATABASE_URL` (has default) — .env.example
- `DEEPSEEK_API_KEY` (has default) — .env
- `DEEPSEEK_BASE_URL` (has default) — .env
- `DEFAULT_MODEL` (has default) — .env.example - `DEFAULT_MODEL` (has default) — .env.example
- `DEV_REMOTE_USER` **required** — apps/web/vite.config.ts - `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_BASE_URL` (has default) — .env
- `GITEA_SSH_HOST` (has default) — .env - `GITEA_SSH_HOST` (has default) — .env
- `GITEA_TOKEN` (has default) — .env - `GITEA_TOKEN` (has default) — .env
@@ -34,6 +37,7 @@
- `LLAMA_SWAP_URL` (has default) — .env.example - `LLAMA_SWAP_URL` (has default) — .env.example
- `MCP_TEST_MISSING` **required** — apps/server/src/services/__tests__/mcp-config.test.ts - `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 - `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 - `NODE_ENV` (has default) — .env.example
- `PORT` (has default) — .env.example - `PORT` (has default) — .env.example
- `POSTGRES_PASSWORD` (has default) — .env.example - `POSTGRES_PASSWORD` (has default) — .env.example
@@ -48,3 +52,7 @@
- `Dockerfile` - `Dockerfile`
- `apps/web/vite.config.ts` - `apps/web/vite.config.ts`
- `docker-compose.yml` - `docker-compose.yml`
## Key Dependencies
- better-sqlite3: ^11.10.0

View File

@@ -2,36 +2,36 @@
## Most Imported Files (change these carefully) ## Most Imported Files (change these carefully)
- `apps/coder/src/db.ts` — imported by **40** files - `apps/coder/src/db.ts` — imported by **44** files
- `apps/server/src/types/api.ts` — imported by **28** files - `apps/server/src/types/api.ts` — imported by **34** files
- `apps/server/src/db.ts` — imported by **25** files - `apps/server/src/db.ts` — imported by **32** files
- `packages/ion/src/cli/utils.ts` — imported by **24** 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/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/agent-backend.ts` — imported by **14** files
- `apps/coder/src/services/acp-tool-snapshot.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/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 - `conductor/src/types.ts` — imported by **13** files
- `apps/coder/src/services/provider-config-registry.ts` — imported by **12** 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/config.ts` — imported by **11** files
- `apps/coder/src/services/provider-types.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/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/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) ## 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/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` +23 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/artifacts.ts`, `apps/server/src/routes/chats.ts`, `apps/server/src/routes/messages.ts` +20 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 - `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/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/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/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/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
- `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

View File

@@ -14,8 +14,17 @@
- function ensureSession: (tmuxConfPath, sessionName, projectRoot, log, cols?, rows?) => Promise<void> - function ensureSession: (tmuxConfPath, sessionName, projectRoot, log, cols?, rows?) => Promise<void>
- function killSession: (tmuxConfPath, sessionName) => Promise<boolean> - function killSession: (tmuxConfPath, sessionName) => Promise<boolean>
- function capturePane: (tmuxConfPath, sessionName, lines) => Promise<string> - function capturePane: (tmuxConfPath, sessionName, lines) => Promise<string>
- _...1 more_
- `apps/booterm/src/pty/pty.ts` — function attachPty: (opts) => IPty - `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` - `apps/coder/src/conductor/contracts.ts`
- function produceContract: (contracts) => string - function produceContract: (contracts) => string
- function reviewContract: (contracts) => string - function reviewContract: (contracts) => string
@@ -102,7 +111,7 @@
- function classifyLane: (battleType, _identity, model, localModels) => ContestantLane - function classifyLane: (battleType, _identity, model, localModels) => ContestantLane
- function nextLocalContestant: (contestants) => string | null - function nextLocalContestant: (contestants) => string | null
- function isBattleComplete: (contestants) => boolean - function isBattleComplete: (contestants) => boolean
- function computeBenchmark: (startedAt, endedAt, costTokens, lane) => Benchmark - function computeBenchmark: (startedAt, endedAt, costTokens, lane, tokenBreakdown) => Benchmark
- function sanitizeSlug: (s) => string - function sanitizeSlug: (s) => string
- function buildBattleSlug: (battleId, battleType, createdAt) => string - function buildBattleSlug: (battleId, battleType, createdAt) => string
- _...7 more_ - _...7 more_
@@ -166,6 +175,7 @@
- function stepEndedToUsage: (props) => StepUsage - function stepEndedToUsage: (props) => StepUsage
- interface StepEndedProps - interface StepEndedProps
- interface StepUsage - interface StepUsage
- `apps/coder/src/services/backends/paseo.ts` — class PaseoBackend, interface PaseoBackendDeps
- `apps/coder/src/services/backends/pushable-iterable.ts` — function createPushable: () => Pushable<T>, interface Pushable - `apps/coder/src/services/backends/pushable-iterable.ts` — function createPushable: () => Pushable<T>, interface Pushable
- `apps/coder/src/services/backends/turn-guard.ts` - `apps/coder/src/services/backends/turn-guard.ts`
- function armAbortGuard: (g) => void - function armAbortGuard: (g) => void
@@ -174,6 +184,30 @@
- interface AbortTerminalGuard - 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-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/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<GuidelineMatchingResult>
- 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/cancel-registry.ts` — function createCancelRegistry: () => CancelRegistry, interface CancelRegistry
- `apps/coder/src/services/checkpoints.ts` - `apps/coder/src/services/checkpoints.ts`
- function buildShadowCommitCommand: (worktreePath, id) => string - function buildShadowCommitCommand: (worktreePath, id) => string
@@ -184,7 +218,15 @@
- interface RestoreCheckpointResult - interface RestoreCheckpointResult
- _...1 more_ - _...1 more_
- `apps/coder/src/services/claude-command-discovery.ts` — function discoverClaudeCommands: () => AgentCommand[] - `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<boolean> - `apps/coder/src/services/command-availability.ts` — function isCommandAvailable: (binary) => Promise<boolean>
- `apps/coder/src/services/conflict-index.ts` — class ConflictIndex, const conflictIndex
- `apps/coder/src/services/correction-service.ts` - `apps/coder/src/services/correction-service.ts`
- function recordCorrection: (originalClaim, correction, principleExtracted, persistedTo, basePath?) => Promise<UserCorrectionRecord> - function recordCorrection: (originalClaim, correction, principleExtracted, persistedTo, basePath?) => Promise<UserCorrectionRecord>
- function scanForCorrections: (auditPath) => Promise<UserCorrectionRecord[]> - function scanForCorrections: (auditPath) => Promise<UserCorrectionRecord[]>
@@ -214,10 +256,11 @@
- function partitionReady: (ready, ctx) => void - function partitionReady: (ready, ctx) => void
- function isRunComplete: (flow, state) => boolean - function isRunComplete: (flow, state) => boolean
- function isStuck: (flow, state) => boolean - function isStuck: (flow, state) => boolean
- function reconcileResumeStep: (status, taskId, taskState) => ResumeAction - function buildBatchState: (flow, inFlight) => Map<string,
- _...5 more_ - _...12 more_
- `apps/coder/src/services/flow-runner.ts` - `apps/coder/src/services/flow-runner.ts`
- function createFlowRunner: (deps) => FlowRunner - function createFlowRunner: (deps) => FlowRunner
- function resolveVariables: (prompt, results, string>) => string
- interface LaunchOpts - interface LaunchOpts
- interface FlowRunner - interface FlowRunner
- `apps/coder/src/services/frame-emitter.ts` - `apps/coder/src/services/frame-emitter.ts`
@@ -237,6 +280,19 @@
- function deleteGuideline: (id, basePath?) => Promise<boolean> - function deleteGuideline: (id, basePath?) => Promise<boolean>
- function findGuideline: (content, basePath?) => Promise<Guideline | null> - function findGuideline: (content, basePath?) => Promise<Guideline | null>
- _...14 more_ - _...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<HostExecResult>, interface HostExecResult - `apps/coder/src/services/host-exec.ts` — function hostExec: (command, opts?) => Promise<HostExecResult>, interface HostExecResult
- `apps/coder/src/services/lsp/client.ts` — class LspClient - `apps/coder/src/services/lsp/client.ts` — class LspClient
- `apps/coder/src/services/lsp/config.ts` — function getServerConfig: (filePath) => LspServerConfig | null, interface LspServerConfig - `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<Location[]> - function findReferences: (client, filePath, content, line, character) => Promise<Location[]>
- `apps/coder/src/services/lsp/server-manager.ts` — class LspServerManager, const lspManager - `apps/coder/src/services/lsp/server-manager.ts` — class LspServerManager, const lspManager
- `apps/coder/src/services/mcp-server.ts` — function startMcpServer: (sql) => Promise<void> - `apps/coder/src/services/mcp-server.ts` — function startMcpServer: (sql) => Promise<void>
- `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` - `apps/coder/src/services/net/port-utils.ts`
- function reclaimPort: (port) => void - function reclaimPort: (port) => void
- function waitForPortRelease: (port, timeoutMs) => Promise<boolean> - function waitForPortRelease: (port, timeoutMs) => Promise<boolean>
@@ -257,6 +351,13 @@
- function createOrphanWorktreeReaper: (deps) => void - function createOrphanWorktreeReaper: (deps) => void
- interface OrphanWorktreeReaperDeps - interface OrphanWorktreeReaperDeps
- interface OrphanReaperResult - 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` - `apps/coder/src/services/pending_changes.ts`
- function planEdit: (content, oldStr, newStr) => EditPlan - function planEdit: (content, oldStr, newStr) => EditPlan
- function queueEdit: (sql, sessionId, taskId, filePath, oldString, newString, projectRoot, // v2.6 Phase 1-UX) => void - 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<CreateElicitationResponse> - function waitForElicitationResponse: (taskId, sessionId, provider, modeId, params, timeoutMs) => Promise<CreateElicitationResponse>
- function cancelPendingPermission: (taskId) => void - function cancelPendingPermission: (taskId) => void
- _...3 more_ - _...3 more_
- `apps/coder/src/services/plan-store.ts`
- function createPlan: (sql, opts) => Promise<Plan>
- function getPlan: (sql, planId) => Promise<Plan | null>
- function listPlans: (sql, projectId) => Promise<Plan[]>
- function listActivePlans: (sql, projectId) => Promise<Plan[]>
- function updatePlan: (sql, planId, opts) => Promise<Plan | null>
- function updatePlanFromRun: (sql, runId, runStatus) => Promise<boolean>
- _...5 more_
- `apps/coder/src/services/provider-commands.ts` - `apps/coder/src/services/provider-commands.ts`
- function getManifestCommands: (provider) => AgentCommand[] - function getManifestCommands: (provider) => AgentCommand[]
- function mergeCommands: (...lists) => AgentCommand[] - function mergeCommands: (...lists) => AgentCommand[]
@@ -295,13 +404,13 @@
- interface ProviderManifestEntry - interface ProviderManifestEntry
- const PROVIDER_MANIFEST: Record<string, ProviderManifestEntry> - const PROVIDER_MANIFEST: Record<string, ProviderManifestEntry>
- `apps/coder/src/services/provider-snapshot.ts` - `apps/coder/src/services/provider-snapshot.ts`
- function fetchDeepSeekModels: (config) => Promise<ProviderModel[]>
- function fetchLlamaSwapModels: (config) => Promise<ProviderModel[]> - function fetchLlamaSwapModels: (config) => Promise<ProviderModel[]>
- function prefixLlamaSwapModels: (models) => ProviderModel[] - function prefixLlamaSwapModels: (models) => ProviderModel[]
- function mergeModels: (...lists) => ProviderModel[] - function mergeModels: (...lists) => ProviderModel[]
- function getProviderSnapshot: (sql, config, cwd?, force) => Promise<ProviderSnapshotEntry[]> - function getProviderSnapshot: (sql, config, cwd?, force) => Promise<ProviderSnapshotEntry[]>
- function clearProviderSnapshotCache: () => void - function clearProviderSnapshotCache: () => void
- function peekSnapshotEntry: (name, cwd?) => ProviderSnapshotEntry | undefined - _...2 more_
- _...1 more_
- `apps/coder/src/services/pty-dispatch.ts` - `apps/coder/src/services/pty-dispatch.ts`
- function dispatchViaPty: (opts) => Promise<DispatchResult> - function dispatchViaPty: (opts) => Promise<DispatchResult>
- interface DispatchResult - interface DispatchResult
@@ -411,6 +520,17 @@
- function readSession: (sessionId, projectRoot?) => SessionJson | null - function readSession: (sessionId, projectRoot?) => SessionJson | null
- _...9 more_ - _...9 more_
- `apps/server/src/services/auto_name.ts` — function maybeAutoNameChat: (ctx, chatId, sessionId) => Promise<void> - `apps/server/src/services/auto_name.ts` — function maybeAutoNameChat: (ctx, chatId, sessionId) => Promise<void>
- `apps/server/src/services/background-task.ts`
- function setBackgroundInferenceEnqueuer: (enqueue, chatId, assistantMessageId, user) => void
- function spawnBackgroundTask: (sql, log, projectId, input, model, agent?, label?) => Promise<BackgroundTask>
- function getBackgroundTaskStatus: (sql, taskId) => Promise<BackgroundTask | null>
- function getBackgroundTaskResult: (sql, taskId, chatId) => Promise<
- function cancelBackgroundTask: (sql, taskId) => Promise<boolean>
- 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` - `apps/server/src/services/broker.ts`
- function createBroker: (log?) => Broker - function createBroker: (log?) => Broker
- interface Broker - interface Broker
@@ -429,6 +549,7 @@
- function select: (messages, contextLimit, tailTurns) => SelectResult - function select: (messages, contextLimit, tailTurns) => SelectResult
- function deriveFilesRead: (head) => string[] - function deriveFilesRead: (head) => string[]
- _...8 more_ - _...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<string[]> - `apps/server/src/services/file_index.ts` — function getProjectFiles: (projectId, projectRoot) => Promise<string[]>
- `apps/server/src/services/file_ops.ts` - `apps/server/src/services/file_ops.ts`
- function listDir: (projectRoot, relPath, opts?) => Promise<ListDirResult> - function listDir: (projectRoot, relPath, opts?) => Promise<ListDirResult>
@@ -453,7 +574,20 @@
- interface GiteaConfig - interface GiteaConfig
- interface GiteaRepo - interface GiteaRepo
- `apps/server/src/services/grant_resolver.ts` — function resolveGrantRoot: (sql, requestedPath, projectRoot, whitelistRoot) => Promise<GrantResolution>, type GrantResolution - `apps/server/src/services/grant_resolver.ts` — function resolveGrantRoot: (sql, requestedPath, projectRoot, whitelistRoot) => Promise<GrantResolution>, 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/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/content-flusher.ts` — function createContentFlusher: (sql, messageId, getContent) => void, interface ContentFlusher
- `apps/server/src/services/inference/dcp/messages.ts` - `apps/server/src/services/inference/dcp/messages.ts`
- function toDcpMessages: (parts) => DcpMessage[] - function toDcpMessages: (parts) => DcpMessage[]
@@ -493,6 +627,10 @@
- type FailureKind - type FailureKind
- const MISTAKE_THRESHOLD - const MISTAKE_THRESHOLD
- _...1 more_ - _...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` - `apps/server/src/services/inference/parts.ts`
- function insertParts: (sql, parts) => Promise<void> - function insertParts: (sql, parts) => Promise<void>
- function partsFromAssistantMessage: (args) => void - function partsFromAssistantMessage: (args) => void
@@ -505,10 +643,13 @@
- function maybeFlagForCompaction: (ctx, chatId, updated) => Promise<void> - function maybeFlagForCompaction: (ctx, chatId, updated) => Promise<void>
- interface OpenAiMessage - interface OpenAiMessage
- `apps/server/src/services/inference/provider.ts` - `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 upstreamModel: (config, modelId, agent?) => LanguageModel
- function resolveModelEndpoint: (config, modelId) => void
- function resetDeepSeekProvider: () => void
- interface RoutingInfo - interface RoutingInfo
- type InferenceRoute - _...1 more_
- `apps/server/src/services/inference/prune.ts` - `apps/server/src/services/inference/prune.ts`
- function selectPruneTargets: (partsNewestFirst, tailStartCreatedAt) => void - function selectPruneTargets: (partsNewestFirst, tailStartCreatedAt) => void
- function prune: (args) => Promise<PruneResult> - function prune: (args) => Promise<PruneResult>
@@ -529,6 +670,12 @@
- function isAnySentinel: (m) => boolean - function isAnySentinel: (m) => boolean
- const DOOM_LOOP_THRESHOLD - const DOOM_LOOP_THRESHOLD
- _...1 more_ - _...1 more_
- `apps/server/src/services/inference/state-graph.ts`
- function createDefaultGraph: () => GraphNode[]
- function runGraph: (ctx, args, extra) => Promise<GraphResult>
- interface GraphState
- interface GraphResult
- type GraphNodeType
- `apps/server/src/services/inference/step-decision.ts` - `apps/server/src/services/inference/step-decision.ts`
- function decideStep: (input) => PreStepDecision - function decideStep: (input) => PreStepDecision
- function decidePostToolAction: (action, mistakeTracker) => PostToolDecision - 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 - `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. // tool list sent to the LLM, so the model can't even attempt them.
webToolsEnabled) => Promise<StreamResult> webToolsEnabled) => Promise<StreamResult>
- `apps/server/src/services/inference/supervisor.ts` — function resolveSupervisorTurn: (latestUserMessage, agents, fallbackModel?) => Promise<SupervisorRoute | null>, interface SupervisorRoute
- `apps/server/src/services/inference/tool-call-parser.ts` - `apps/server/src/services/inference/tool-call-parser.ts`
- function stripToolMarkup: (text, opts?) => string - function stripToolMarkup: (text, opts?) => string
- function extractToolCallBlocks: (buffer, log?) => ToolCallExtraction - function extractToolCallBlocks: (buffer, log?) => ToolCallExtraction
- interface ParsedCall - interface ParsedCall
- interface ToolCallExtraction - interface ToolCallExtraction
- `apps/server/src/services/inference/tool-phase.ts` — function executeToolPhase: (ctx, args, result, startedAt, session, projectRoot, agent?) => Promise<ToolPhaseResult>, 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<ToolPhaseResult>, interface ToolPhaseResult
- `apps/server/src/services/inference/tool-shim.ts` - `apps/server/src/services/inference/tool-shim.ts`
- function extractToolCalls: (text) => ParsedToolCall[] - function extractToolCalls: (text) => ParsedToolCall[]
- function hasToolCallMarkup: (text) => boolean - function hasToolCallMarkup: (text) => boolean
@@ -566,20 +715,26 @@
- `apps/server/src/services/inference/turn.ts` - `apps/server/src/services/inference/turn.ts`
- function runAssistantTurn: (ctx, args) => Promise<void> - function runAssistantTurn: (ctx, args) => Promise<void>
- function runInference: (ctx, sessionId, chatId, assistantMessageId, signal?) => Promise<void> - function runInference: (ctx, sessionId, chatId, assistantMessageId, signal?) => Promise<void>
- function runInferenceWithModel: (ctx, sessionId, chatId, assistantMessageId, modelOverride, compareGroupId, signal?) => Promise<void>
- function createInferenceRunner: (ctx, 'publishUser'>, publishUserFn, frame) => void - function createInferenceRunner: (ctx, 'publishUser'>, publishUserFn, frame) => void
- `apps/server/src/services/mcp-client.ts` - `apps/server/src/services/mcp-client.ts`
- function initialize: (entries, logger) => Promise<void> - function initialize: (entries, logger) => Promise<void>
- function callTool: (prefixedName, args, unknown>) => Promise<unknown> - function callTool: (prefixedName, args, unknown>) => Promise<unknown>
- function getServerPermission: (prefixedToolName) => McpPermission
- function setServerPermission: (serverName, permission) => void
- function getServerName: (prefixedToolName) => string | null
- function getTools: () => ToolDef<Record<string, unknown>>[] - function getTools: () => ToolDef<Record<string, unknown>>[]
- function getMcpServers: () => Array< - _...6 more_
- function shutdown: () => Promise<void>
- function wrapMcpTool: (serverName, mcpTool) => ToolDef<Record<string, unknown>>
- _...2 more_
- `apps/server/src/services/mcp-config.ts` - `apps/server/src/services/mcp-config.ts`
- function substituteEnvVars: (value, log, unsetVars?) => unknown - function substituteEnvVars: (value, log, unsetVars?) => unknown
- function loadMcpConfig: (configPath, log) => McpServerEntry[] - function loadMcpConfig: (configPath, log) => McpServerEntry[]
- interface McpServerEntry - interface McpServerEntry
- type McpServerConfig - 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<boolean>
- function embed: (texts) => Promise<number[][] | null>
- `apps/server/src/services/memory/entries.ts` — function parseMemoryEntries: (fileName, markdown) => MemoryEntry[], interface MemoryEntry - `apps/server/src/services/memory/entries.ts` — function parseMemoryEntries: (fileName, markdown) => MemoryEntry[], interface MemoryEntry
- `apps/server/src/services/memory/paths.ts` - `apps/server/src/services/memory/paths.ts`
- function getMemoryRoot: (projectRoot) => string - function getMemoryRoot: (projectRoot) => string
@@ -587,7 +742,10 @@
- function ensureMemoryScaffold: (root) => Promise<void> - function ensureMemoryScaffold: (root) => Promise<void>
- type MemoryTopic - type MemoryTopic
- `apps/server/src/services/memory/prompt.ts` — function formatMemoryBlock: (entries) => string - `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<string[]> - `apps/server/src/services/memory/recall.ts`
- function rankByRelevance: (query, entries) => MemoryEntry[]
- function rankByHybrid: (query, entries) => Promise<MemoryEntry[]>
- function loadMemoryForSession: (projectRoot, _sessionId?, query?) => Promise<string[]>
- `apps/server/src/services/memory/scan.ts` - `apps/server/src/services/memory/scan.ts`
- function scanMemoryScopes: (scope) => Promise<MemoryEntry[]> - function scanMemoryScopes: (scope) => Promise<MemoryEntry[]>
- function scanProjectMemory: (projectRoot) => Promise<MemoryEntry[]> - function scanProjectMemory: (projectRoot) => Promise<MemoryEntry[]>
@@ -618,6 +776,11 @@
- function filterSecretEntries: (entries, pathOf) => void - function filterSecretEntries: (entries, pathOf) => void
- class SecretBlockedError - class SecretBlockedError
- const DEFAULT_SECURITY_IGNORE_FILETYPES: ReadonlyArray<string> - const DEFAULT_SECURITY_IGNORE_FILETYPES: ReadonlyArray<string>
- `apps/server/src/services/session-snapshots.ts`
- function saveAgentSnapshot: (sql, chatId, data) => Promise<void>
- function loadAgentSnapshot: (sql, chatId) => Promise<AgentSnapshot | null>
- function deleteAgentSnapshot: (sql, chatId) => Promise<void>
- interface AgentSnapshot
- `apps/server/src/services/skill-invoke.ts` - `apps/server/src/services/skill-invoke.ts`
- function runSkillInvokeTransaction: (sql, args) => Promise< - function runSkillInvokeTransaction: (sql, args) => Promise<
- function buildSkillInvokeSyntheticFrames: (chatId, result, toolCall, skillBody) => SkillInvokeSessionFrame[] - function buildSkillInvokeSyntheticFrames: (chatId, result, toolCall, skillBody) => SkillInvokeSessionFrame[]
@@ -648,8 +811,53 @@
- _...2 more_ - _...2 more_
- `apps/server/src/services/task-model.ts` — function taskModelCompletion: (opts) => Promise<string> - `apps/server/src/services/task-model.ts` — function taskModelCompletion: (opts) => Promise<string>
- `apps/server/src/services/task-search-rewrite.ts` — function rewriteSearchQuery: (userMessage) => Promise<string> - `apps/server/src/services/task-search-rewrite.ts` — function rewriteSearchQuery: (userMessage) => Promise<string>
- `apps/server/src/services/tool-traces.ts`
- function insertToolTrace: (sql, insert) => Promise<ToolTrace>
- function updateToolTrace: (sql, id, updates) => Promise<ToolTrace | null>
- interface ToolTrace
- interface ToolTraceInsert
- interface ToolTraceUpdate
- `apps/server/src/services/tools/background-subagent-tools.ts`
- function executeSpawnSubagent: (input, sql, sessionId) => Promise<Record<string, unknown>>
- function executeSubagentStatus: (input, sql) => Promise<Record<string, unknown>>
- function executeSubagentResult: (input, sql) => Promise<Record<string, unknown>>
- type SpawnSubagentInputT
- type SubagentStatusInputT
- type SubagentResultInputT
- _...6 more_
- `apps/server/src/services/tools/codecontext/factory.ts` — function makeCodecontextTool: (opts, unknown>; - `apps/server/src/services/tools/codecontext/factory.ts` — function makeCodecontextTool: (opts, unknown>;
mapArgs) => void mapArgs) => void
- `apps/server/src/services/tools/codecontext/get_code_health.ts`
- function executeGetCodeHealth: (input, projectPath) => Promise<string>
- type GetCodeHealthInputT
- const GetCodeHealthInput
- const getCodeHealth: ToolDef<GetCodeHealthInputT>
- `apps/server/src/services/tools/codecontext/get_code_impact.ts`
- function executeGetCodeImpact: (input, projectPath) => Promise<CodecontextResponse>
- type GetCodeImpactInputT
- const GetCodeImpactInput
- const getCodeImpact: ToolDef<GetCodeImpactInputT>
- `apps/server/src/services/tools/codecontext/get_code_map.ts`
- function executeGetCodeMap: (input, projectRoot) => Promise<CodeMapResponse>
- interface CodeMapResponse
- type GetCodeMapInputT
- const GetCodeMapInput
- const getCodeMap: ToolDef<GetCodeMapInputT>
- `apps/server/src/services/tools/codecontext/get_type_info.ts`
- function executeGetTypeInfo: (input, _projectPath?) => Promise<CodecontextResponse>
- type GetTypeInfoInputT
- const GetTypeInfoInput
- const getTypeInfo: ToolDef<GetTypeInfoInputT>
- `apps/server/src/services/tools/codecontext/get_wiki_article.ts`
- function executeGetWikiArticle: (input, projectPath) => Promise<string>
- type GetWikiArticleInputT
- const GetWikiArticleInput
- const getWikiArticle: ToolDef<GetWikiArticleInputT>
- `apps/server/src/services/tools/execute-command.ts`
- function executeRunCommand: (input, projectRoot) => Promise<RunCommandOutput>
- type RunCommandInputT
- type RunCommandOutput
- const runCommand: ToolDef<RunCommandInputT>
- `apps/server/src/services/tools/registry.ts` — function appendMcpTools: (mcpTools) => void, function toolJsonSchemas: () => ToolJsonSchema[] - `apps/server/src/services/tools/registry.ts` — function appendMcpTools: (mcpTools) => void, function toolJsonSchemas: () => ToolJsonSchema[]
- `apps/server/src/services/tools/tiers.ts` - `apps/server/src/services/tools/tiers.ts`
- function resolveToolTier: (tier) => readonly string[] - function resolveToolTier: (tier) => readonly string[]
@@ -675,6 +883,39 @@
- interface WebSearchOutput - interface WebSearchOutput
- type WebSearchInputT - type WebSearchInputT
- const webSearch: ToolDef<WebSearchInputT> - const webSearch: ToolDef<WebSearchInputT>
- `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<string, unknown>
- function loadWorkflowScript: (sourceFile, context) => (...args: unknown[]) => Promise<unknown>
- function loadWorkflowScriptFromCode: (code, context, filename?) => (...args: unknown[]) => Promise<unknown>
- _...3 more_
- `apps/server/src/utils/string-utils.ts` — function stripQuotes: (s) => string - `apps/server/src/utils/string-utils.ts` — function stripQuotes: (s) => string
- `apps/web/src/api/client.ts` - `apps/web/src/api/client.ts`
- class ApiError - class ApiError
@@ -695,7 +936,7 @@
- interface TerminalSelectionActions - interface TerminalSelectionActions
- interface TerminalSelection - interface TerminalSelection
- `apps/web/src/hooks/terminal/useTerminalSocket.ts` - `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 - interface TerminalSocket
- type ConnState - type ConnState
- `apps/web/src/hooks/useActivePane.ts` - `apps/web/src/hooks/useActivePane.ts`
@@ -719,7 +960,8 @@
- interface ThroughputSample - interface ThroughputSample
- `apps/web/src/hooks/useCoderUserEvents.ts` — function useCoderUserEvents: () => void - `apps/web/src/hooks/useCoderUserEvents.ts` — function useCoderUserEvents: () => void
- `apps/web/src/hooks/useDiffPreferences.ts` — function useDiffPreferences: () => void, interface DiffPreferences - `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/useLongPress.ts` — function useLongPress: (callback) => void
- `apps/web/src/hooks/useProjectGit.ts` — function useProjectGit: (projectId) => GitMeta | null - `apps/web/src/hooks/useProjectGit.ts` — function useProjectGit: (projectId) => GitMeta | null
- `apps/web/src/hooks/useProviderSnapshot.ts` — function refreshProviderSnapshot: (cwd?) => Promise<ProviderSnapshotEntry[]>, function useProviderSnapshot: (cwd?) => ProviderSnapshotEntry[] | null - `apps/web/src/hooks/useProviderSnapshot.ts` — function refreshProviderSnapshot: (cwd?) => Promise<ProviderSnapshotEntry[]>, function useProviderSnapshot: (cwd?) => ProviderSnapshotEntry[] | null
@@ -732,6 +974,7 @@
- `apps/web/src/hooks/useSessions.ts` — function useSessions: (projectId) => void - `apps/web/src/hooks/useSessions.ts` — function useSessions: (projectId) => void
- `apps/web/src/hooks/useSidebar.ts` — function useSidebar: () => void - `apps/web/src/hooks/useSidebar.ts` — function useSidebar: () => void
- `apps/web/src/hooks/useSkills.ts` — function useSkills: () => 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/useUserEvents.ts` — function useUserEvents: () => void
- `apps/web/src/hooks/useViewport.ts` — function useViewport: () => ViewportSnapshot, interface ViewportSnapshot - `apps/web/src/hooks/useViewport.ts` — function useViewport: () => ViewportSnapshot, interface ViewportSnapshot
- `apps/web/src/hooks/useWorkspacePanes.ts` - `apps/web/src/hooks/useWorkspacePanes.ts`
@@ -794,7 +1037,16 @@
- interface ThemeMeta - interface ThemeMeta
- type ThemeId - type ThemeId
- _...5 more_ - _...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/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` - `apps/web/src/utils/diff-layout.ts`
- function parseDiff: (diffBody) => ParsedDiffFile[] - function parseDiff: (diffBody) => ParsedDiffFile[]
- function buildSplitRows: (file) => SplitRow[] - function buildSplitRows: (file) => SplitRow[]

View File

@@ -7,6 +7,7 @@
- turn-guard — `apps/coder/src/services/backends/turn-guard.ts` - turn-guard — `apps/coder/src/services/backends/turn-guard.ts`
- get_middleware — `apps/server/src/services/tools/codecontext/get_middleware.ts` - get_middleware — `apps/server/src/services/tools/codecontext/get_middleware.ts`
- authoring — `conductor/src/flows/authoring.ts` - authoring — `conductor/src/flows/authoring.ts`
- spec — `openspec/changes/add-behavioral-engine/specs/audit-middleware/spec.md`
## custom ## custom
- write_guard.test — `apps/coder/src/services/__tests__/write_guard.test.ts` - write_guard.test — `apps/coder/src/services/__tests__/write_guard.test.ts`

View File

@@ -3,6 +3,7 @@
## CRUD Resources ## CRUD Resources
- **`/api/battles`** GET | POST | GET/:id → Battle - **`/api/battles`** GET | POST | GET/:id → Battle
- **`/api/plans`** GET | POST | GET/:id | PATCH/:id → Plan
- **`/api/runs`** GET | POST | GET/:id → Run - **`/api/runs`** GET | POST | GET/:id → Run
- **`/api/tasks`** GET | POST | GET/:id → Task - **`/api/tasks`** GET | POST | GET/:id → Task
- **`/api/chats/:id/messages`** GET | POST | GET/:id | DELETE/:id → Message - **`/api/chats/:id/messages`** GET | POST | GET/:id | DELETE/:id → Message
@@ -14,11 +15,16 @@
### fastify ### fastify
- `GET` `/api/term/health` params() - `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/start` params(sid, pid) [auth]
- `POST` `/api/term/sessions/:sid/panes/:pid/kill` 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` `/ws/term/sessions/:sid/panes/:pid` params(sid, pid) [auth]
- `GET` `/api/health` params() [auth, db, queue, ai] - `GET` `/api/health` params() [auth, db, queue, ai]
- `GET` `/api/sessions/:sessionId/agent-sessions` params(sessionId) [auth, db] - `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/generate-prompt` params() [auth, db]
- `POST` `/api/battles/:id/stop` params(id) [auth, db] - `POST` `/api/battles/:id/stop` params(id) [auth, db]
- `GET` `/api/battles/:id/analysis` 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/apply` params(id) [auth, db, queue]
- `POST` `/api/pending/:id/reject` 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] - `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/snapshot` params() [db, cache]
- `GET` `/api/providers/config` params() [db, cache] - `GET` `/api/providers/config` params() [db, cache]
- `PATCH` `/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/sessions/:sessionId` params(sessionId) [auth, db]
- `GET` `/api/ws/user` params() [auth, db] - `GET` `/api/ws/user` params() [auth, db]
- `GET` `/api/projects/:id/agents` params(id) [db, cache] - `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] - `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/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/projects/:project_id/artifacts/:filename` params(project_id, filename) [auth, db]
- `GET` `/api/sessions/:id/chats` params(id) [auth, db] - `GET` `/api/sessions/:id/chats` params(id) [auth, db, queue]
- `POST` `/api/sessions/:id/chats` params(id) [auth, db] - `POST` `/api/sessions/:id/chats` params(id) [auth, db, queue]
- `PATCH` `/api/chats/:id` params(id) [auth, db] - `PATCH` `/api/chats/:id` params(id) [auth, db, queue]
- `POST` `/api/sessions/:id/chats/archive-all` params(id) [auth, db] - `POST` `/api/sessions/:id/chats/archive-all` params(id) [auth, db, queue]
- `GET` `/api/sessions/:id/chats/open-count` params(id) [auth, db] - `GET` `/api/sessions/:id/chats/open-count` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/archive` params(id) [auth, db] - `POST` `/api/chats/:id/archive` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/unarchive` params(id) [auth, db] - `POST` `/api/chats/:id/unarchive` params(id) [auth, db, queue]
- `DELETE` `/api/chats/:id` params(id) [auth, db] - `DELETE` `/api/chats/:id` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/fork` params(id) [auth, db] - `POST` `/api/chats/:id/fork` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/discard_stale` params(id) [auth, db] - `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] - `GET` `/api/coder/ws/sessions/:sessionId` params(sessionId) [auth]
- `ALL` `/api/coder/*` params() [auth] - `ALL` `/api/coder/*` params() [auth]
- `GET` `/api/settings/inference` params() [cache] - `GET` `/api/settings/inference` params() [cache]
@@ -83,7 +93,9 @@
- `POST` `/api/chats/:id/continue` params(id) [auth, db, queue] - `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/force_send` params(id) [auth, db, queue]
- `POST` `/api/chats/:id/grant_read_access` 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/create` params() [auth, db]
- `POST` `/api/projects/:id/archive` params(id) [auth, db] - `POST` `/api/projects/:id/archive` params(id) [auth, db]
- `POST` `/api/projects/:id/unarchive` params(id) [auth, db] - `POST` `/api/projects/:id/unarchive` params(id) [auth, db]
@@ -111,6 +123,7 @@
- `GET` `/api/skills` params() [auth, db, queue] - `GET` `/api/skills` params() [auth, db, queue]
- `POST` `/api/chats/:id/skill_invoke` params(id) [auth, db, queue] - `POST` `/api/chats/:id/skill_invoke` params(id) [auth, db, queue]
- `GET` `/api/tools/cost_stats` params() [auth, db] - `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] - `GET` `/api/ws/sessions/:id` params(id) [auth, db]
### go-net-http ### go-net-http

View File

@@ -118,6 +118,25 @@
- model: text (required) - model: text (required)
- verdict: text - 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 ### projects
- id: uuid (pk) - id: uuid (pk)
- name: text (required) - name: text (required)
@@ -139,6 +158,8 @@
- content: text (required) - content: text (required)
- status: text (required) - status: text (required)
- last_seq: integer (required) - last_seq: integer (required)
- cache_tokens: integer
- reasoning_tokens: integer
### message_parts ### message_parts
- id: uuid (pk) - id: uuid (pk)
@@ -155,3 +176,42 @@
- session_id: uuid (required, fk) - session_id: uuid (required, fk)
- name: text - name: text
- status: text (required) - 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)

10
.gitignore vendored
View File

@@ -21,3 +21,13 @@ data/*
!data/coder-providers.example.json !data/coder-providers.example.json
codecontext/fork.tar.gz codecontext/fork.tar.gz
/Arena /Arena
# Auto-generated & scratch artifacts
.impeccable/
.omo/
bun.lock
DESIGN.md
PRODUCT.md
# codesight auto-generated analysis cache
apps/web/.codesight/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-08

View File

@@ -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

View File

@@ -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)

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-08

View File

@@ -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

View File

@@ -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[].

View File

@@ -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
<motion.div
layout
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95, transition: { duration: 0.1 } }}
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
>
<PaneComponent />
</motion.div>
```
`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
<Routes>
<Route path="/" element={<Suspense fallback={<FullPageLoader />}><Home /></Suspense>} />
...
</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 (
<div className="flex flex-col items-center justify-center gap-3 py-16">
<div className="text-muted-foreground/40">{icon}</div>
<p className="text-sm font-medium text-foreground">{title}</p>
{description && (
<p className="text-xs text-muted-foreground text-center max-w-[240px]">{description}</p>
)}
{action && <Button variant="outline" size="sm" onClick={action.onClick}>{action.label}</Button>}
</div>
);
}
```
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
<span className="absolute top-0 left-1/2 -translate-x-1/2 size-0.5 rounded-full bg-amber-500" />
```
### Target
```tsx
// Explicit transform-origin + motion-safe guard
<span className="absolute top-0 left-1/2 -translate-x-1/2 size-0.5 rounded-full bg-amber-500 motion-safe:animate-spin-slow motion-reduce:hidden" style={{ transformOrigin: 'center 6px' }} />
```
The `size-0.5` dots orbit at 12px diameter. `transform-origin: center 6px` centers rotation at the orbit midpoint.

View File

@@ -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**: 2030 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.

View File

@@ -0,0 +1,340 @@
# Implementation Tasks — Impeccable Wave
**Dependency map:** Tasks within a phase are sequential (foundation before use). Phases 25 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.12.4
├──► Phase 3 (Chat) ──────── 3.13.4
├──► Phase 4 (Identity) ──── 4.14.3
└──► Phase 5 (UX Depth) ──── 5.15.5
└──► Phase 6 (Polish) ─── 6.16.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 `<motion.div layout>` 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 `<motion.div variants={staggerContainer}>`. Each message `<motion.div variants={fadeSlideIn}>` 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
<motion.div
animate={message.status === 'streaming' ? { borderColor: ['rgba(255,122,24,0)', 'rgba(255,122,24,0.3)', 'rgba(255,122,24,0)'] } : {}}
transition={message.status === 'streaming' ? { duration: 1.5, repeat: Infinity } : {}}
/>
```
**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
<AnimatePresence initial={false}>
{expanded && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
/>
)}
</AnimatePresence>
```
**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 `<EmptyState>` 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 `<kbd>` 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 `<Suspense>` 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 `<span>tap</span>` 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.

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-08

View File

@@ -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 `<MessageBoundary>` wrapper that catches render errors and shows a "Rendering failed" fallback + retry button. A top-level `<MessageListErrorBoundary>` 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<code+theme+lang, html>` LRU cache (max 50 entries) avoids recomputation without adding a dependency.

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,55 @@
## ADDED Requirements
### Requirement: Error boundary on MarkdownRenderer
`MarkdownRenderer` SHALL be wrapped in a `<MessageBoundary>` 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 `<MessageBoundary>` that catches errors from Shiki's `codeToHtml` and renders a plain-text `<pre>` 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 `<pre>` 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

View File

@@ -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=<messageId>`). 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=<messageId>`
- **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

View File

@@ -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<code+theme+lang, html>`, max 50 entries) to avoid redundant Shiki calls
- [x] 3.8 Test: each feature toggles independently, Shiki fallback to plain `<pre>` 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=<messageId>`, 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 `<MessageBoundary>` component that catches render errors and shows "Rendering failed" + Retry button
- [x] 5.2 Wrap `MarkdownRenderer` in `<MessageBoundary>`
- [x] 5.3 Wrap `CodeBlock` in `<MessageBoundary>` with plain `<pre>` 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 `<MessageListErrorBoundary>` 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

View File

@@ -11,5 +11,8 @@
"devDependencies": { "devDependencies": {
"typescript": "^5.5.0" "typescript": "^5.5.0"
}, },
"license": "MIT" "license": "MIT",
"dependencies": {
"better-sqlite3": "^11.10.0"
}
} }

454
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff