Vitto Vscode Extension Native Chat Spec
Documentation for Vitto
Vitto VS Code Extension — Native Chat Tab: Spec & Enhancement Plan
Status: Living document
Last updated: 2026-03-28
Session: S284 (spec review, gap analysis, implementation — reattributed from S282)
Scope: apps/vitto-vscode-extension + related backend-vitto-api agent-session path + @libs-ai/chat-plugin-core
Audience: Engineers extending Vitto in VS Code / Cursor
1. Purpose
This document records what was implemented to align the native Lit chat tab with the web vitto-chat experience (power, robustness, ease of use), provides a comprehensive gap analysis against the Angular web app, and proposes a phased plan to reach full feature parity. It is meant to stay verifiable: file paths point to the source of truth in the repo.
2. Goals and non-goals
Goals
- Remove duplicate "two chat tabs" confusion by defaulting to native chat while keeping the embedded Angular iframe recoverable via settings.
- Make model preference (
/model, quick actions) actually affect the backend agent loop when valid. - Improve discoverability and operations: session/context awareness, links to MCP and full web Vitto.
- Add workspace-scoped transcript persistence so the sidebar chat survives reloads (parity with "chat history" expectations).
- Keep backend session id stable so Mongo history + IDE tool delegation stay aligned with the UI.
- Reach full feature parity with web vitto-chat: all plugins, generative UI, tool renderers, activity cards, and session management.
Non-goals
- Re-implementing the full Angular
@libs-ai/vitto-chatapp (Observatory, generative UI blocks, Dexie schema, cost UI, etc.) inside the Lit webview using Angular. We will rebuild these features natively in Lit. - Cross-device cloud sync of native-chat transcripts (local
workspaceStateonly). - Full mobile/desktop parity (voice input via Web Speech API is unavailable in VS Code webviews).
3. Current state: what's implemented (S270-S281)
3.1 Extension package & settings
| Item | Detail |
|---|---|
| Version | apps/vitto-vscode-extension/package.json — 0.15.4. |
vitto.showEmbeddedWebChat |
Boolean setting, default false. Hides iframe tab. |
| Tab labels | Web on: Web + Chat. Web off: native tab labeled Vitto. |
3.2 Shell & webview host (main-panel.ts)
- Injects
showEmbeddedWebChatfrom config. - Conditional top nav (optional Web button).
handleGetConfig: includesagentPreferredModel.setAgentPreferredModel— persists via config service.loadNativeChatTranscript— readsworkspaceStatekeyvitto.nativeChat.transcript.v1, ensuresAgentSessionServiceexists, callssetSessionId, postsnativeChatTranscriptto webview.saveNativeChatTranscript— writes transcript, syncssetSessionId.- Storage key:
NATIVE_CHAT_STORAGE_KEY = 'vitto.nativeChat.transcript.v1'.
3.3 Agent session service (agent-session.service.ts)
- POST body includes
preferredModel. setSessionId(sessionId)— alignscurrentSessionIdwith restored transcript.- 7 IDE tools:
read_file,write_file,run_command,search_code,list_files,get_diagnostics,show_diff.
3.4 Native chat UI (agent-chat-view.ts)
Header: VITTO branding, connection status, session badge, preferred model display. Actions: New (new session), Copy (Markdown export), MCP (switch pane), Web (open external). Chat behavior:
chatSessionIdpersisted, sent assessionIdon everyagentMessage.loadNativeChatTranscripton connect;nativeChatTranscriptrestores messages + id.- Debounced save (~500ms) on message changes; skips while
isHydrating. - Limits: max 50 stored messages; tool output truncated (~20k chars).
/modeland quick actions callsetAgentPreferredModel.
Message rendering (agent-message.ts):
- User bubble (right-aligned, gradient), attachment chips.
- Assistant text via
<markdown-view>(simple inline parser). - Thinking blocks (collapsible
<details>). - Tool start (card with icon + input summary + spinner).
- Tool result (collapsible, success/failure indicator).
- Error card (red border).
- Done card (muted summary).
Markdown view (markdown-view.ts):
- Simple regex-based parser: headers, bold, italic, code blocks, inline code, links, blockquotes, lists, horizontal rules.
- Uses
unsafeHTML— XSS mitigated by HTML entity escaping before parsing.
3.5 Backend (backend-vitto-api)
| File | Change |
|---|---|
vitto.controller.ts |
POST /engine/agent-session accepts optional preferredModel; SSE response with heartbeat (15s). |
vitto-engine.service.ts |
startAgentSessionMessage / runAgentSessionLoop accept preferredModel. If isValidModel(preferredModel), resolves provider + model. Falls back to SmartRouter. |
| Event mapper | Maps internal events to extension format (see Section 5). |
3.6 Types (panels/webview/types.ts)
ConfigInfo.agentPreferredModel— optional string.NativeChatPersistedMessage— JSON-safe message shape.NativeChatTranscriptV1—{ version: 1, sessionId, messages[], updatedAt }.- Message types:
loadNativeChatTranscript,saveNativeChatTranscript,nativeChatTranscript.
4. Critical review of uncommitted work (S270-S281)
4.1 What's solid
Transcript persistence — well-structured V1 schema, proper hydration guard (
isHydrating), sensible limits (50 messages, 20K tool output). The debounce-on-update approach is correct. Confidence: Certain.Session ID continuity —
chatSessionIdshared between webview, extension hostAgentSessionService, and backend Mongo. ThesetSessionIdescape hatch on restore is necessary and correctly placed. Confidence: Certain.New chat flow — correctly cancels streaming, generates new UUID, clears state, immediately persists empty transcript. The
requestAnimationFrametrick to deferisHydrating = falseis a reasonable micro-task boundary. Confidence: Certain.Copy as Markdown — good format, handles all message types including thinking in
<details>tags. Tool result output capped at 120K chars (reasonable for clipboard). Confidence: Certain.
4.2 Critical issues
| # | Issue | Severity | Confidence |
|---|---|---|---|
| C1 | unsafeHTML in markdown-view without DOMPurify |
HIGH | Certain |
The markdown parser escapes < and > before processing, which provides basic XSS protection. However, the regex replacement chain is fragile — a crafted input could potentially produce valid HTML through interaction between transformations (e.g., a link href with javascript: protocol passes through since only <> are escaped, not attribute values). While this is in a VS Code webview sandbox with CSP, the target="_blank" links could still be attack vectors if content comes from untrusted backends. Recommendation: add DOMPurify (3KB gzipped) or at minimum sanitize href attributes. |
|||
| C2 | preferredModel silently ignored for complexity: 'complex' |
MEDIUM | Certain |
runAgentSessionLoop always passes complexity: 'complex' to agentLoop.run. The backend code shows that for complex tasks, SmartRouter may override the user's model preference. The user sees their model displayed in the header but may actually get a different model. No feedback mechanism exists. This is documented in the original spec's "Known limitations" but remains a real UX problem. |
|||
| C3 | No agent.iteration / task.stuck handling in extension |
MEDIUM | Certain |
The backend emits agent.iteration (with token counts) and task.stuck (loop/stall detection) events. The event mapper currently suppresses these or maps tool.starting to thinking. The extension has no concept of iteration progress, token usage, or stuck detection. Users see streaming text but have no visibility into cost or loop detection. |
4.3 Non-critical improvements
| # | Improvement | Impact | Confidence |
|---|---|---|---|
| N1 | Transcript doesn't flush on dispose | Messages since last debounce are lost if VS Code closes. The disconnectedCallback clears the timer but doesn't do a final save. Simple fix: call this.persistNow() before clearing timer. |
Certain |
| N2 | Single-session transcript limit | Only one conversation persisted in workspaceState. Users who switch between topics lose context. The web app has a session sidebar with multiple threads. |
Certain |
| N3 | No message editing or regeneration | Web chat supports edit-and-resend and regenerate. Native chat only supports copy and new chat. | Certain |
| N4 | tool.starting → thinking mapping loses information |
The event mapper converts tool.starting to a generic "thinking" event. The extension could display richer tool-start context (tool category, reasoning hypothesis from S238.5) if the raw event were forwarded. |
Likely |
4.4 Nitpicks
| # | Nitpick |
|---|---|
| P1 | ensureChatSessionId duplicates the UUID generation logic (also in newChat). Extract to a shared generateSessionId() utility. |
| P2 | cloneMessageForStorage uses JSON.parse(JSON.stringify()) for deep clone of input — works but structuredClone is available in all VS Code webview runtimes. |
| P3 | The "New" and "Copy" header buttons are text-only. Using codicons (e.g., $(add), $(copy)) would match VS Code conventions and save horizontal space in the narrow sidebar. |
5. SSE protocol reference (complete)
5.1 Backend event types emitted by agent-session
| Backend Event | Extension Mapped Type | Payload (extension format) | Currently Handled? |
|---|---|---|---|
agent.text |
text |
{ type: 'text', content } |
Yes |
tool_use |
tool_use |
{ type: 'tool_use', id, name, input } |
Yes |
tool.starting |
thinking |
{ type: 'thinking', content: 'Using [tool]...' } |
Yes (lossy) |
tool.called |
(suppressed) | — | N/A |
task.completed |
done |
{ type: 'done', summary } |
Yes |
task.failed |
error |
{ type: 'error', message } |
Yes |
task.stuck |
(suppressed) | — | No |
agent.iteration |
(suppressed) | — | No |
heartbeat |
heartbeat |
{ type: 'heartbeat' } |
Partial (no UI) |
5.2 Missing event handling (recommended additions)
| Event | Recommended Extension Behavior |
|---|---|
agent.iteration |
Update token counter in header. Show iteration N/max. Enable cost badge. |
task.stuck (loop) |
Show warning banner: "Vitto appears stuck in a loop with [tool]". Offer cancel button. |
task.stuck (stall) |
Show warning: "No progress for Xs". Offer cancel. |
task.completed (tokens) |
Display final token count + estimated cost in done card or header. |
5.3 Tool result cycle
- Extension receives
tool_useSSE event with server-generatedid. - Extension executes tool locally via
IdeToolService. - Extension POSTs result to
POST /engine/agent-session/:sessionId/tool-resultwith{ toolUseId, result, error? }. - Backend resolves pending Promise, agent loop continues.
- Timeout: 120 seconds — backend auto-resolves with error message.
6. Feature gap analysis: native chat vs. web vitto-chat
6.1 Plugin system (17 plugins in chat-plugin-core)
| Plugin | Category | Web Status | Native Status | Port Difficulty | Notes |
|---|---|---|---|---|---|
| Rich Context | Context | Full | Partial (IDE context only) | LOW | Needs VittoRichContext signal wiring |
| Capabilities | Context | Full (90+ capabilities) | Missing | LOW | Pure config; needs capability grid UI |
| Suggestions | Context | Full | Partial (basic quick actions) | LOW | Needs backend suggestion provider |
| Model | Intelligence | Full | Partial (/model works) |
LOW | Needs model picker dropdown, cost badge |
| Agent | Intelligence | Full | Missing | LOW | Slash commands only, callback-driven |
| Mastery | Intelligence | Full | Missing | LOW | Slash commands + message enrichment |
| Learning | Intelligence | Full (silent) | Missing | LOW | Silent plugin, pure enrichment |
| Generation | Generation | Full (tabs) | Missing | MEDIUM | Needs Lit tab system for components |
| Module Builder | Generation | Full | Missing | MEDIUM | Active only in modular-universe context |
| Teach | Generation | Full | Missing | MEDIUM | Requires component injection |
| Prompt Library | Generation | Full | Missing | MEDIUM | Requires component injection |
| Platform Elements | Generation | Full | Missing | LOW | Chat-only integration |
| Computer Use Bridge | Bridge | Full | Missing | LOW | Quick actions + enrichment |
| Observatory | Bridge | Full | Not applicable | N/A | Use "Web" button to open in browser |
| Awareness | Bridge | Full | Missing | LOW | Service-based, needs awareness provider |
| Voice | Input | Full | Auto-disabled | N/A | Web Speech API unavailable in webview |
| Action | Input | Full | Missing | LOW | Drag-drop actionable outputs |
| Context Legacy | Input | Full | Missing | LOW | Superseded by Rich Context |
Summary: 4 of 17 plugins have partial support. 11 are completely missing. 2 are N/A for VSCode.
6.2 Message rendering features
| Feature | Web | Native | Gap |
|---|---|---|---|
| Markdown (full) | UxMarkdownComponent (mature) |
Simple regex parser | Major — no tables, no strikethrough, no task lists, no nested lists, no KaTeX |
| Syntax highlighting | highlight.js integration |
Language tag only, no highlighting | Major |
| Generative UI blocks | GenerativeUIComponentCatalog (7 block types) |
Not implemented | Major |
| Tool renderers | 14 specialized renderers | Generic card (all tools same) | Major |
| Activity cards | 10+ SSE event card types | Not implemented | Significant |
| Message actions | Copy, rate, regenerate, edit, escalate | None per-message | Significant |
| Cost badge | Per-message token/cost display | Not available | Moderate |
| Avatar + mood | Mood-based aura, snapshot | Static "V" text | Moderate |
| Typing indicator | Animated dots + "thinking about..." | "Vitto is thinking..." text | Minor |
6.3 Input features
| Feature | Web | Native | Gap |
|---|---|---|---|
| Slash commands | Full palette with autocomplete | Basic palette (functional) | Minor |
| Quick actions | Dynamic from all plugins | From chat-plugin-core (basic) | Moderate |
| File attachments | Drag-drop zone + preview | Drag-drop + chips (functional) | Minor |
| Emoji picker | Full picker | Not implemented | Low priority |
| Input toolbar | Bold/italic/code formatting | Plain text only | Low priority |
6.4 Session management
| Feature | Web | Native | Gap |
|---|---|---|---|
| Multiple sessions | Full sidebar + switcher | Single session only | Major |
| Session rename | Inline editing | Not available | Moderate |
| Session search | Text search across sessions | Not available | Moderate |
| Reverse-load (pagination) | Last 30 messages + scroll-load | All 50 messages at once | Minor |
6.5 System features
| Feature | Web | Native | Gap |
|---|---|---|---|
| Token/cost tracking | Per-message + cumulative | Not available | Significant |
| Iteration progress | Visible in UI | Not available | Significant |
| Stuck/loop detection | Warning + cancel | Not available | Significant |
| Connection retry | Exponential backoff | Basic (functional) | Minor |
7. Architecture decision: how to port
7.1 Key insight: @libs-ai/chat-plugin-core is already framework-agnostic
The core plugin library has zero Angular imports. It uses:
ReactiveValue<T>=() => T(not Angular Signal)ComponentRef=unknown(not Angular Type)- Pure TypeScript interfaces
This means all 15 applicable plugins can be instantiated directly in the Lit webview without any Angular bridging. The main work is:
- Implementing platform capability providers for the VSCode context.
- Building Lit components for plugin tabs, quick actions, and output addons.
- Wiring the plugin lifecycle (activate, deactivate, context change, message enrichment).
7.2 Component injection strategy for Lit
Web vitto-chat uses Angular's ViewContainerRef.createComponent() for dynamic plugin components. In Lit:
// Plugin declares a Lit component tag name instead of Angular Type
interface LitPluginTab {
label: string;
icon: string;
tagName: string; // e.g., 'vitto-generation-tab'
}
// Host renders dynamically
render() {
const tag = this.activeTab.tagName;
return html`${staticHtml`<${unsafeStatic(tag)}></${unsafeStatic(tag)}>`}`;
}
This is a proven Lit pattern and avoids the complexity of a full DI system.
7.3 Recommended: Lit component registry
Create a LitPluginRegistry that maps plugin IDs to Lit custom elements. Plugins register their components at import time (same self-registration pattern as web activity cards). This keeps the plugin system decoupled from the chat shell.
7.4 Markdown rendering upgrade path
The current regex-based parser is fragile and missing major features. Options:
| Option | Size (gzip) | Features | Recommendation |
|---|---|---|---|
| Keep current regex | 0KB | Basic only | No — too limited |
markdown-it |
~28KB | Full CommonMark, extensible | Yes — battle-tested, plugin ecosystem |
marked |
~12KB | CommonMark, fast | Viable alternative |
| VS Code's own markdown renderer | 0KB (built-in) | Full | Not accessible from webview |
Recommendation: Use markdown-it with plugins for syntax highlighting (markdown-it-highlightjs, ~5KB) and task lists. Add DOMPurify (~3KB) for sanitization. Total added weight: ~36KB gzip. Acceptable for a VS Code extension.
8. Enhancement plan (phased)
Phase 0 — Fixes for current work (before merge)
Rationale: These should ship with the current S270-281 uncommitted changes.
| ID | Task | Effort | Why |
|---|---|---|---|
| P0.1 | Flush transcript on dispose — call persistNow() in disconnectedCallback before clearing timer |
15 min | Prevents data loss on VS Code close |
| P0.2 | Add DOMPurify to markdown-view or at minimum sanitize href attributes in link regex |
1 hr | XSS risk with unsafeHTML |
| P0.3 | Extract generateSessionId() utility to deduplicate UUID generation |
15 min | Code hygiene |
| P0.4 | Forward task.completed tokens to webview for display in done card |
2 hr | Users deserve cost visibility |
Phase 1 — Foundation & plugin system (HIGH priority)
Goal: Wire the framework-agnostic plugin system and upgrade core rendering. This unblocks all subsequent phases.
| ID | Task | Effort | Dependencies |
|---|---|---|---|
| P1.1 | Upgrade markdown to markdown-it + DOMPurify — replace regex parser, add syntax highlighting via highlight.js, support tables/task lists/strikethrough |
2-3 days | None |
| P1.2 | Integrate @libs-ai/chat-plugin-core — import in esbuild config, create LitPluginHost that manages plugin lifecycle (activate/deactivate/enrichMessage/onContextChange) |
2-3 days | None |
| P1.3 | Implement VSCode platform capability set — declare component-rendering, file-system, terminal, diagnostics, git-context, ide-context, clipboard, sse-streaming, websocket, drag-drop |
1 day | P1.2 |
| P1.4 | Wire plugin quick actions — replace static quick actions with dynamic ones from all active plugins | 1 day | P1.2 |
| P1.5 | Wire plugin slash commands — merge plugin-provided commands with existing commands, use unified palette | 1 day | P1.2 |
| P1.6 | Wire plugin message enrichment — call enrichMessage() on all plugins before sending |
0.5 day | P1.2 |
| P1.7 | Handle agent.iteration events — extend event mapper to forward, display iteration/token count in header or footer bar |
1-2 days | Backend mapper change |
| P1.8 | Handle task.stuck events — display warning banner with cancel option |
1 day | Backend mapper change |
Estimated total: 2-3 weeks
Phase 2 — Rich message rendering (MEDIUM-HIGH priority)
Goal: Messages look and feel like the web app. Users can see what tools did, interact with generative UI, and track costs.
| ID | Task | Effort | Dependencies |
|---|---|---|---|
| P2.1 | Tool renderer system — create <tool-renderer-host> Lit component with dispatch table. Port the 6 most-used renderers: browser, shell, search, delegation, http-request, default. Others can use default renderer with progressive enhancement. |
3-4 days | P1.1 |
| P2.2 | Message body parser — port the 3-layer detection from web: tool-output segments, generative-ui blocks, markdown segments. Requires understanding TOOLS_WITH_RENDERERS whitelist. |
2 days | P2.1 |
| P2.3 | Generative UI renderer — create <vitto-render> Lit component + component catalog. Port 4 priority blocks: rich-card, rich-button, rich-quick-reply, alert-badge. |
2-3 days | P1.1 |
| P2.4 | Per-message actions — add copy, rate (thumbs up/down) to each message. Regenerate can wait for Phase 3. | 1-2 days | None |
| P2.5 | Cost badge — display tokens + estimated cost on task.completed. Show per-message if agent.iteration data is available. |
1 day | P1.7 |
| P2.6 | Activity cards — create <activity-card-host> + port 4 priority card types: reasoning-trace, background-task, proactive-trigger, trust-update. Others render as generic cards. |
2-3 days | P1.7 |
| P2.7 | Improve tool.starting rendering — forward raw event instead of mapping to "thinking". Show tool category, reasoning hypothesis (S238.5). |
1 day | Backend mapper change |
Estimated total: 2-3 weeks
Phase 3 — Session management & interaction (MEDIUM priority)
Goal: Multiple sessions, edit/regenerate, session sidebar.
| ID | Task | Effort | Dependencies |
|---|---|---|---|
| P3.1 | Multi-session storage — change workspaceState from single transcript to session map: { sessions: { [id]: TranscriptV2 }, activeSessionId, order[] }. Migrate V1 on first load. |
2 days | None |
| P3.2 | Session sidebar — collapsible list in the chat header area (or a slide-out panel). Shows session titles, timestamps, switch/delete actions. | 2-3 days | P3.1 |
| P3.3 | Session rename — inline edit on session title. | 0.5 day | P3.2 |
| P3.4 | Regenerate last response — resend last user message with same context to get a new response. Needs clear interaction with sessionId and server history. |
1-2 days | None |
| P3.5 | Edit user message — replace message content and resend from that point. Backend implications: may need to truncate server-side history or start new session from that point. | 2-3 days | P3.4 |
| P3.6 | Model picker dropdown — visual model selector instead of /model command. Show current model, available models with provider icons. |
1-2 days | P1.2 |
Estimated total: 2-3 weeks
Phase 4 — Plugin feature completeness (MEDIUM priority)
Goal: All applicable plugins fully functional in native chat.
| ID | Task | Effort | Dependencies |
|---|---|---|---|
| P4.1 | Rich Context plugin — wire VittoRichContext from IDE context (map active file → context type). Generate context-aware quick actions (90+ capabilities). |
2 days | P1.2 |
| P4.2 | Capabilities plugin — render capability grid in chat header area. | 1-2 days | P4.1 |
| P4.3 | Agent plugin — wire /status, /agents, /briefing, /pause, /resume slash commands. Role-based filtering from JWT. |
1-2 days | P1.5 |
| P4.4 | Mastery plugin — wire /mastery, /dossier, /teach commands. Message enrichment with active job type. |
1 day | P1.6 |
| P4.5 | Learning plugin — wire silent reinforcement (no UI, just enrichment). | 0.5 day | P1.6 |
| P4.6 | Computer Use Bridge — wire CU session awareness, blocker quick actions (2FA, CAPTCHA, confirmation). Show CU progress indicator. | 1-2 days | P1.2 |
| P4.7 | Awareness plugin — wire routine suggestion quick actions from Awareness Hub. | 1 day | P1.2 |
| P4.8 | Module Builder plugin — wire /extract, /generate, /validate, /export commands. Active only in modular-universe context. |
1-2 days | P4.1, P1.5 |
| P4.9 | Platform Elements plugin — wire /platform add entity, /platform edit columns commands. Element context enrichment. |
1 day | P4.1, P1.5 |
Estimated total: 2-3 weeks
Phase 5 — Polish & advanced features (LOWER priority)
| ID | Task | Effort | Dependencies |
|---|---|---|---|
| P5.1 | Remaining tool renderers — port module-generation, task-complete, computer-use, mcp-context, sub-vitto, oauth, domain-registry renderers. | 3-4 days | P2.1 |
| P5.2 | Remaining generative UI blocks — rich-list-picker, rich-carousel, skill-preview-card. | 1-2 days | P2.3 |
| P5.3 | Remaining activity cards — sub-agent-approval, oauth-connect, vm-execution, evolution-milestone, persona-adapted, skill-usage, proactive-summary. | 2-3 days | P2.6 |
| P5.4 | Drag-drop actionable outputs — Action plugin integration. Outputs (modules, widgets, code) draggable to editor. | 2 days | P4.1 |
| P5.5 | Import/export JSON — full conversation backup/restore for debugging. | 1 day | P3.1 |
| P5.6 | Keyboard shortcuts — Cmd/Ctrl+N (new chat), Cmd/Ctrl+L (clear), Cmd/Ctrl+K (command palette). | 0.5 day | None |
| P5.7 | Accessibility — ARIA labels, focus management, screen reader support. | 2 days | All UI work |
| P5.8 | Generation & Teach tabs — Lit-native tab components for generation workflows. Complex; may not be needed if users prefer web for these. | 3-5 days | P1.2 |
| P5.9 | Prompt Library tab — browse/search/apply prompt templates. | 2-3 days | P1.2 |
Estimated total: 3-4 weeks
9. Dependency graph (critical path)
P0 (fixes) ──┐
├── P1.1 (markdown-it) ──── P2.1 (tool renderers) ──── P2.2 (body parser)
│ └── P2.3 (generative UI)
├── P1.2 (plugin core) ──── P1.3 (capabilities) ──── P4.1 (rich context)
│ ├── P1.4 (quick actions) └── P4.2-P4.9
│ ├── P1.5 (slash commands)
│ └── P1.6 (enrichment)
├── P1.7 (iteration events) ── P2.5 (cost badge)
│ └── P2.6 (activity cards)
├── P1.8 (stuck events)
│
└── P3.1 (multi-session) ── P3.2 (sidebar) ── P3.3-P3.5
Critical path for "looks like web chat": P0 → P1.1 → P2.1 → P2.2 → P2.3 (markdown, tool renderers, body parser, generative UI).
Critical path for "acts like web chat": P0 → P1.2 → P1.3-P1.6 → P4.1-P4.9 (plugin system, all plugins).
These two paths are independent and can be parallelized across developers.
10. Backend changes required
| Change | Location | Phase | Description |
|---|---|---|---|
Forward agent.iteration to extension |
agent-session-event-mapper.ts |
P1.7 | Map to { type: 'iteration', iteration, maxIterations, inputTokens, outputTokens } |
Forward task.stuck to extension |
agent-session-event-mapper.ts |
P1.8 | Map to { type: 'stuck', reason, stuckType, tool? } |
Include tokens in done event |
agent-session-event-mapper.ts |
P0.4 | Add tokens: { input, output, total } to done payload |
Forward raw tool.starting |
agent-session-event-mapper.ts |
P2.7 | Pass { type: 'tool_starting', tool, category, reasoning? } instead of converting to thinking |
| Complexity classification | vitto-engine.service.ts |
P1 | Allow extension to pass complexity hint or classify based on message length/context |
| Contract tests | New test file | P1 | (modelId) → (provider, model) for every model in isValidModel |
11. esbuild considerations
The extension uses esbuild for the webview bundle. Adding @libs-ai/chat-plugin-core requires:
- Add to esbuild externals or bundle directly — since
chat-plugin-coreis pure TS with zero Angular deps, it bundles cleanly. Verify no barrel import issues (same pattern as S258/S259). markdown-it+highlight.js— CommonJS modules; esbuild handles these natively. May need--platform=browserflag (already set for webview build).DOMPurify— UMD/ESM dual. Use ESM import.- Bundle size budget — Current webview bundle is ~180KB. Adding markdown-it (~28KB), highlight.js (~28KB with common languages), DOMPurify (~3KB) = ~60KB added. Total ~240KB. Acceptable for VS Code extension.
12. Configuration reference
| Setting | Default | Scope | Role |
|---|---|---|---|
vitto.showEmbeddedWebChat |
false |
Workspace | Show embedded iframe Web tab |
vitto.agentPreferredModel |
(globalState) | Global | Last model id; sent as preferredModel |
Transcript lives in WorkspaceState under vitto.nativeChat.transcript.v1 (V1) or vitto.nativeChat.sessions.v2 (future V2 multi-session).
13. Known limitations and risks
- Provider mapping — Backend resolves
preferredModelwithisValidModel+getProviderFromModel. New providers may not map cleanly. Mitigation: Contract tests (Phase 1). - IDE loop complexity — Always
complex. Mitigation: Allow extension to classify (Phase 1, backend change). - Partial saves — Debounce during streaming. Mitigation: Flush on dispose (P0.1).
- WorkspaceState size — VS Code limits (~5MB). Mitigation: Message count limits + tool output truncation. Multi-session (Phase 3) will need careful storage management.
- Plugin component injection — Lit's
unsafeStaticfor dynamic tag names requires trust in plugin registrations. Mitigation: Whitelist-only component registry (same security model as web generative UI catalog). - Webview CSP — VS Code webviews have strict CSP. External resources (images, fonts) need
nonceorcspSource. Mitigation: Inline all assets; use VS Code's webview URI API for extension resources. - Voice input — Web Speech API unavailable in VS Code webviews. Mitigation: Auto-disabled by capability system. No workaround needed.
- highlight.js bundle size — Full language set is 400KB+. Mitigation: Import only common languages (JS, TS, Python, HTML, CSS, JSON, YAML, Bash, SQL, Go, Rust, Java, C++, C#, Ruby, PHP, Shell) = ~28KB.
14. Verification checklist (for releases)
-
npm run buildinapps/vitto-vscode-extensionsucceeds. - With
vitto.showEmbeddedWebChat: false, only MCP | Vitto | Repos (no iframe tab). - Reload window: transcript restores; new messages continue the same
sessionId. -
/model <valid-id>then send message: backend receivespreferredModel. - New clears UI and starts a new
sessionId. - Markdown renders: headers, bold, italic, code blocks with highlighting, links, tables.
- Tool results show in specialized renderers (browser, shell, search at minimum).
- Generative UI blocks render when backend returns
generative-uicode blocks. - Plugin quick actions appear based on IDE context.
- Token count visible during and after streaming.
- Stuck/loop warning appears and cancel works.
- Multi-session: can create, switch, rename, delete sessions.
- DOMPurify blocks XSS payloads in markdown content.
15. Key file index
| Area | Path |
|---|---|
| Extension entry | apps/vitto-vscode-extension/src/extension.ts |
| Webview host | apps/vitto-vscode-extension/src/panels/main-panel.ts |
| Agent session | apps/vitto-vscode-extension/src/services/agent-session.service.ts |
| Config | apps/vitto-vscode-extension/src/services/config.service.ts |
| Native chat UI | apps/vitto-vscode-extension/src/panels/webview/components/agent/agent-chat-view.ts |
| Message bubble | apps/vitto-vscode-extension/src/panels/webview/components/agent/agent-message.ts |
| Markdown view | apps/vitto-vscode-extension/src/panels/webview/components/shared/markdown-view.ts |
| Types / messages | apps/vitto-vscode-extension/src/panels/webview/types.ts |
| Webview bundle entry | apps/vitto-vscode-extension/src/panels/webview/index.ts |
| Package / settings | apps/vitto-vscode-extension/package.json |
| Webview esbuild | apps/vitto-vscode-extension/esbuild.mjs |
| Tool renderers | apps/vitto-vscode-extension/src/panels/webview/components/agent/tool-renderers.ts |
| Message body parser | apps/vitto-vscode-extension/src/panels/webview/components/agent/message-body-parser.ts |
| Generative UI | apps/vitto-vscode-extension/src/panels/webview/components/agent/generative-ui.ts |
| Session sidebar | apps/vitto-vscode-extension/src/panels/webview/components/agent/session-sidebar.ts |
| Plugin bridge | apps/vitto-vscode-extension/src/panels/webview/components/agent/plugin-bridge.ts |
| Plugin core (shared) | libs/libs-ai/chat-plugin-core/src/ |
| Plugin interfaces | libs/libs-ai/chat-plugin-core/src/lib/interfaces/chat-plugin.interface.ts |
| Plugin capabilities | libs/libs-ai/chat-plugin-core/src/lib/types/plugin-capabilities.ts |
| Plugin presets | libs/libs-ai/chat-plugin-core/src/lib/plugins/ (17 plugin factories) |
| Web chat (reference) | libs/libs-ai/vitto-chat/src/lib/ |
| Web tool renderers | libs/libs-ai/vitto-chat/src/lib/components/tool-renderers/ |
| Web generative UI | libs/libs-ai/vitto-chat/src/lib/generative-ui/ |
| Web activity cards | libs/libs-ai/vitto-chat/src/lib/components/activity-cards/ |
| API route | APIs/backend-vitto-api/src/app/engine/vitto.controller.ts |
| Engine loop | APIs/backend-vitto-api/src/app/engine/vitto-engine.service.ts |
| Event mapper | APIs/backend-vitto-api/src/app/engine/utils/agent-session-event-mapper.ts |
| IDE tool strategy | APIs/backend-vitto-api/src/app/engine/strategies/ide-tool.strategy.ts |
| Model config | libs/libs-api/backend-core/src/lib/config/ai-models.config.ts |
16. Document history
| Date | Session | Note |
|---|---|---|
| 2026-03-28 | S270-S281 | Initial spec: implemented features + enhancement roadmap. |
| 2026-03-28 | S284.1 | Expanded: full gap analysis (17 plugins, SSE protocol, 5-phase plan), critical review of S270-S281 uncommitted work, architecture decisions. Reattributed from S282.2. |
| 2026-03-28 | S284.1 | P0: flush-on-dispose, DOMPurify/markdown-it/highlight.js, deduplicated session ID. Backend event mapper forwards iteration/stuck/tokens. Reattributed from S282.7. |
| 2026-03-28 | S284.1 | P2+P3: tool renderer system (6 renderers), 3-layer message body parser, generative UI (3 components), multi-session V2 storage, session sidebar. Production bundle: 683KB. Reattributed from S282.9. |