2026 03 22 Chat Plugin Core And Native Vscode Chat Design

Documentation for Plans

Chat Plugin Core & Native VSCode Chat — Architecture Spec

Session: S258 Date: 2026-03-22 Status: Spec Complete — Ready for Implementation Owner: libs/libs-ai/chat-plugin-core + apps/vitto-vscode-extension Predecessor: S133 (Plugin Architecture), S149 (Advanced Plugins), S168 (Canonical Presets), S240 (Native Agent Mode Spec) Calibrated Confidence: 0.84


Executive Summary

The Vitto chat plugin system (17 plugins, built since S133) is locked to Angular. This spec extracts a framework-agnostic plugin core (@libs-ai/chat-plugin-core) that enables the same plugins to work across Angular web, Lit VSCode extension, React Native mobile, Flutter, and any future surface.

Two deliverables:

  1. @libs-ai/chat-plugin-core — New NX library. Zero framework imports. Contains all plugin interfaces, types, factories, categories, platform capability negotiation, and the standard preset factory.
  2. Native "Chat" tab in the VSCode extension — Third top-level tab (alongside MCP and Vitto) rendering a Lit-based agent chat that consumes plugins from chat-plugin-core.

Table of Contents

  1. Architecture Overview
  2. chat-plugin-core Library
  3. Core Interfaces & Types
  4. Plugin Categories
  5. Platform Capability System
  6. Plugin Resolution
  7. Platform-Aware Preset Factory
  8. Migration: vitto-chat → chat-plugin-core
  9. VSCode Extension: Chat Tab
  10. IDE Context Adapter
  11. Extension Host Services
  12. Message Protocol Extensions
  13. Infrastructure Details
  14. Risks & Mitigations
  15. Phasing
  16. Key Files Reference

1. Architecture Overview

┌─────────────────────────────────────────────────────────────────┐
│                  @libs-ai/chat-plugin-core                      │
│              (Pure TypeScript — Zero Framework Deps)             │
│                                                                 │
│  Interfaces:  ChatPlugin, PluginDropPayload, ReactiveValue<T>   │
│  Types:       PluginCategory, PlatformCapability, PluginPlatform│
│  Factories:   createRichContextPlugin, createModelPlugin, ...   │
│  Composition: createStandardPluginSet(config)                   │
│  Resolution:  isPluginCompatible(), resolvePluginsForPlatform() │
│  Providers:   AwarenessStateProvider, ContextSuggestionProvider │
│                                                                 │
│  Imports from: @libs-foundational/core-models (pure TS types)   │
│  NO imports from Angular barrels (embeddable-bridge, api-config) │
└─────────┬──────────────────────┬──────────────────┬─────────────┘
          │                      │                  │
    ┌─────▼──────┐     ┌────────▼───────┐   ┌─────▼──────────┐
    │  Angular   │     │  Lit (VSCode)  │   │  Mobile (RN)   │
    │  Adapter   │     │   Adapter      │   │   Adapter      │
    │            │     │                │   │                │
    │ Signal → ✓ │     │ () => prop → ✓ │   │ () => ref → ✓  │
    │ Type<> → ✓ │     │ tagName → ✓    │   │ Component → ✓  │
    │            │     │                │   │                │
    │ vitto-chat │     │ vscode-ext     │   │ (future)       │
    └────────────┘     └────────────────┘   └────────────────┘

Key design decisions:

  1. ReactiveValue<T> = () => T — Angular Signal, Lit getter, React ref — all are callable. Zero wrapper needed for Angular (Signal IS a ReactiveValue).
  2. ComponentRef = unknown — Opaque token. Each framework resolves it (Angular: Type<unknown>, Lit: tag string, RN: component class).
  3. Plugin factories are pure functions — They return plain objects with closures. No DI, no decorators, no framework.
  4. Capability negotiation over platform lists — Plugins declare what they NEED, platforms declare what they PROVIDE. Adding a platform never touches plugin code.

2. chat-plugin-core Library

Directory Structure

libs/libs-ai/chat-plugin-core/
├── src/
│   ├── index.ts                              # Public API barrel
│   └── lib/
│       ├── interfaces/
│       │   ├── chat-plugin.interface.ts       # Core ChatPlugin interface
│       │   ├── plugin-providers.interface.ts  # Provider interfaces (AwarenessStateProvider, etc.)
│       │   └── plugin-resolution.ts           # Platform capability matching
│       ├── types/
│       │   ├── plugin-categories.ts           # PluginCategory enum + metadata
│       │   ├── plugin-capabilities.ts         # PlatformCapability + platform presets
│       │   ├── reactive.types.ts              # ReactiveValue<T>, ComponentRef
│       │   └── chat-core.types.ts             # ChatMessage, QuickAction, SlashCommand, etc.
│       ├── plugins/
│       │   ├── index.ts                       # All plugin factory exports
│       │   ├── context/                       # Category: context
│       │   │   ├── rich-context.plugin.ts
│       │   │   ├── capabilities.plugin.ts
│       │   │   └── suggestions.plugin.ts
│       │   ├── intelligence/                  # Category: intelligence
│       │   │   ├── model.plugin.ts
│       │   │   ├── agent.plugin.ts
│       │   │   ├── mastery.plugin.ts
│       │   │   └── learning.plugin.ts
│       │   ├── generation/                    # Category: generation
│       │   │   ├── generation.plugin.ts
│       │   │   ├── module-builder.plugin.ts
│       │   │   ├── teach.plugin.ts
│       │   │   └── prompt-library.plugin.ts
│       │   ├── bridge/                        # Category: bridge
│       │   │   ├── computer-use-bridge.plugin.ts
│       │   │   ├── observatory.plugin.ts
│       │   │   └── awareness.plugin.ts
│       │   └── input/                         # Category: input
│       │       ├── voice.plugin.ts
│       │       ├── action.plugin.ts
│       │       └── context-legacy.plugin.ts
│       ├── composition/
│       │   ├── plugin-preset.factory.ts       # createStandardPluginSet() — platform-aware
│       │   └── plugin-resolver.ts             # Capability matching + filtering
│       └── utils/
│           └── plugin-guards.ts               # isChatPlugin(), hasCapability(), etc.
├── project.json
├── tsconfig.json
├── tsconfig.lib.json
└── jest.config.ts

NX Configuration

// project.json
{
  "name": "chat-plugin-core",
  "$schema": "../../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "libs/libs-ai/chat-plugin-core/src",
  "prefix": "lib",
  "projectType": "library",
  "tags": ["scope:ai", "type:core"],
  "targets": {
    "lint": { "executor": "@nx/eslint:lint" }
  }
}

Tag type:core (not type:feature) — this is a foundational library that other feature libs depend on.

tsconfig.json (NO angularCompilerOptions)

{
  "extends": "../../../tsconfig.base.json",
  "compilerOptions": {
    "moduleResolution": "bundler",
    "strict": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "module": "preserve"
  },
  "files": [],
  "include": [],
  "references": [{ "path": "./tsconfig.lib.json" }]
}

No angularCompilerOptions — this library has zero Angular dependency.

Dependencies

Dependency Import Status
@libs-foundational/core-models PlatformRole, RouteContext, CapabilityContext, DerivedFeatures Safe — pure TS types, no Angular
@libs-ux/embeddable-bridge DragPayload DO NOT import — barrel re-exports Angular services/directives. Define PluginDropPayload locally in chat-plugin-core instead (see below)
@libs-foundational/api-config AI_MODELS_CONFIG DO NOT import — barrel re-exports InjectionToken from @angular/core. Extract model constants into chat-plugin-core/types/model-constants.ts
@libs-ai/vitto-core VittoPageContextRegistry DO NOT import — Angular service. Replace with ReactiveValue<string[]> via activeSuggestions config option

Critical rule: chat-plugin-core must NEVER import from any barrel that re-exports Angular code. Even if the specific type you need is pure TS, the barrel import pulls the entire module graph. Always verify the barrel file before adding a dependency.

PluginDropPayload (replaces DragPayload)

// types/chat-core.types.ts

/**
 * Lightweight drop payload for plugin onDrop handlers.
 * Framework-agnostic subset of @libs-ux/embeddable-bridge DragPayload.
 * Angular adapter maps DragPayload → PluginDropPayload.
 */
export interface PluginDropPayload {
  id: string;
  type: string;
  title: string;
  data: unknown;
  sourceApp?: string;
}

AI Model Constants (replaces AI_MODELS_CONFIG import)

// types/model-constants.ts

/**
 * LLM model identifiers used by the model plugin.
 * Extracted from @libs-foundational/api-config to avoid Angular barrel.
 * Keep in sync with api-config/ai-models.config.ts.
 */
export const CHAT_MODEL_IDS = {
  fast: 'claude-sonnet-4-6',
  reasoning: 'claude-opus-4-6',
  haiku: 'claude-haiku-4-5-20251001',
} as const;

3. Core Interfaces & Types

ReactiveValue — The Framework Bridge

// types/reactive.types.ts

/**
 * Framework-agnostic reactive value getter.
 *
 * Call it to read the current value — identical call signature to:
 *   - Angular Signal:  signal() → T
 *   - Lit reactive:    () => this.property
 *   - React:           () => stateRef.current
 *   - Plain JS:        () => someVariable
 *
 * AI AGENT GUIDE:
 * When creating a plugin config, wrap your framework's reactive primitive:
 *   Angular:  { contextSignal: mySignal }            // Signal IS a ReactiveValue
 *   Lit:      { contextSignal: () => this.context }   // Arrow wrapping @state
 *   React:    { contextSignal: () => contextRef.current }
 *   Plain:    { contextSignal: () => currentContext }  // Any getter function
 */
export type ReactiveValue<T> = () => T;

/**
 * Framework-agnostic component reference.
 *
 * Each framework resolves this to its own component type:
 *   - Angular:      Type<unknown>     (class reference)
 *   - Lit:          string            (custom element tag name)
 *   - React Native: React.ComponentType<unknown>
 *   - Flutter:      Type              (Dart widget class)
 *
 * Plugins pass these opaquely — only the framework adapter interprets them.
 * The plugin system NEVER inspects or instantiates ComponentRef directly.
 */
export type ComponentRef = unknown;

Provider Interfaces

// interfaces/plugin-providers.interface.ts

/**
 * Abstraction over AwarenessDataService.
 * Angular service implements this. Other platforms provide their own.
 */
export interface AwarenessStateProvider {
  cuBridgeState(): CUBridgeState;
  routineSuggestions(): RoutineSuggestion[];
  connect(userId: string): void;
  disconnect(): void;
}

export interface CUBridgeState {
  sessions: Array<{ sessionId: string; status: string }>;
  blockers: Array<{
    id: string;
    sessionId: string;
    type: '2fa' | 'captcha' | 'confirmation' | string;
  }>;
}

export interface RoutineSuggestion {
  id: string;
  title: string;
  dismissed: boolean;
  actions: Array<{ prompt?: string }>;
}

/**
 * Abstraction over VittoChatContextService.
 * Provides capability and suggestion data for a given context.
 */
export interface ContextSuggestionProvider {
  getCapabilitiesForContext(contextType: string): CapabilityEntry[];
  getSuggestionsForContext(contextType: string): VittoSuggestion[];
}

export interface CapabilityEntry {
  id: string;
  name: string;
  description: string;
  icon: string;
  handler: string;
  enabled: boolean;
}

ChatPlugin Interface

// interfaces/chat-plugin.interface.ts

/**
 * Core ChatPlugin interface — framework-agnostic.
 *
 * Plugins are plain configuration objects returned by factory functions.
 * NOT framework services. NOT classes. NOT singletons.
 *
 * ═══════════════════════════════════════════════════════════════
 * AI AGENT GUIDE — Creating a New Plugin
 * ═══════════════════════════════════════════════════════════════
 *
 * 1. Choose a category (context | intelligence | generation | bridge | input)
 *    See PluginCategory docs for definitions.
 *
 * 2. Create a factory: createMyPlugin(config) => ChatPlugin
 *    File location: plugins/{category}/my-plugin.plugin.ts
 *
 * 3. Set `requires` to minimum capabilities your plugin needs:
 *    - Empty array [] = works everywhere (prefer this)
 *    - Only add capabilities that would BREAK the plugin if missing
 *
 * 4. Set `platforms` ONLY for inherently platform-specific plugins:
 *    - Example: 'ide-diagnostics' only makes sense in VSCode
 *    - Omit platforms to let capability matching decide (preferred)
 *    - When both `platforms` and `requires` are set, `platforms`
 *      takes precedence (explicit override)
 *
 * 5. Export from plugins/index.ts
 *
 * 6. Add to composition/plugin-preset.factory.ts if standard
 *
 * Resolution order:
 *   platforms (if set) → capability match → role gating → included
 *
 * quickActions may be a getter that reads ReactiveValue —
 * always access fresh, never cache the returned array.
 * ═══════════════════════════════════════════════════════════════
 */
export interface ChatPlugin {
  /** Unique plugin identifier (kebab-case) */
  id: string;

  /** Display label */
  label?: string;

  /** Icon identifier (codicon name or emoji) */
  icon?: string;

  // ─── Classification ─────────────────────────────────────────

  /** Plugin category. See PluginCategory docs. */
  category: PluginCategory;

  /**
   * Capabilities this plugin REQUIRES from the host platform.
   * Empty [] = universal (works everywhere).
   * Only list what would BREAK the plugin if missing.
   */
  requires: PlatformCapability[];

  /**
   * Explicit platform override — OPTIONAL.
   * When set, takes precedence over capability matching.
   * Plugin ONLY included on listed platforms.
   * Use sparingly — prefer `requires` for future-proofing.
   */
  platforms?: PluginPlatform[];

  // ─── UI Composition ─────────────────────────────────────────

  tabs?: ChatPluginTab[];
  slashCommands?: SlashCommandDefinition[];
  quickActions?: QuickAction[];
  outputComponents?: ComponentRef[];
  inputAddons?: ComponentRef[];
  headerAddons?: ComponentRef[];

  // ─── Message Processing ─────────────────────────────────────

  enrichMessage?: (msg: ChatMessage) => ChatMessage;

  // ─── Lifecycle ──────────────────────────────────────────────

  onActivate?: () => void;
  onDeactivate?: () => void;
  onContextChange?: (context: VittoRichContext) => void;
  onDrop?: (data: PluginDropPayload) => ChatMessage | null;
  onDestroy?: () => void;
}

4. Plugin Categories

// types/plugin-categories.ts

/**
 * AI AGENT GUIDE — Choose the category matching PRIMARY responsibility:
 *
 *   context       — Observe/enrich messages with user awareness.
 *                   No UI rendering. Examples: rich-context, capabilities, suggestions.
 *
 *   intelligence  — Control AI behavior, orchestration, learning.
 *                   Affects HOW Vitto responds. Examples: model, agent, mastery, learning.
 *
 *   generation    — Content creation UIs via tabs/panels.
 *                   Dedicated interfaces for artifacts. Examples: generation, module-builder.
 *
 *   bridge        — Connect external systems into chat.
 *                   Proxy state/events from outside. Examples: computer-use-bridge, observatory.
 *
 *   input         — Enhance input modalities and interactions.
 *                   Add ways to interact. Examples: voice, action (drag-drop).
 */
export type PluginCategory = 'context' | 'intelligence' | 'generation' | 'bridge' | 'input';

export interface PluginCategoryMetadata {
  id: PluginCategory;
  label: string;
  description: string;
  /** Composition priority — lower = earlier in plugin array */
  priority: number;
}

export const PLUGIN_CATEGORIES: Record<PluginCategory, PluginCategoryMetadata> = {
  context:      { id: 'context',      label: 'Context',      description: 'Observe and enrich with user awareness',    priority: 0 },
  intelligence: { id: 'intelligence', label: 'Intelligence', description: 'Control AI behavior, agents, learning',      priority: 1 },
  generation:   { id: 'generation',   label: 'Generation',   description: 'Content creation UIs and artifact builders',  priority: 2 },
  bridge:       { id: 'bridge',       label: 'Bridge',       description: 'Connect external systems into chat',          priority: 3 },
  input:        { id: 'input',        label: 'Input',        description: 'Enhance input modalities and interactions',   priority: 4 },
};

5. Platform Capability System

// types/plugin-capabilities.ts

/**
 * Capabilities a platform provides to plugins.
 *
 * AI AGENT GUIDE — Creating a new plugin:
 * 1. List capabilities your plugin REQUIRES to function
 * 2. Only list truly required — not "nice to have"
 * 3. requires: [] = works on ALL platforms
 * 4. Preset factory auto-excludes from incompatible platforms
 *
 * Adding a new platform:
 * 1. Add to PluginPlatform type
 * 2. Add capability list to PLATFORM_CAPABILITIES
 * 3. All compatible plugins automatically available
 */
export type PlatformCapability =
  // Rendering
  | 'component-rendering'    // Render plugin tab components
  // Input
  | 'drag-drop'              // Drag-drop interactions
  | 'speech-api'             // Web Speech API or equivalent STT/TTS
  | 'file-upload'            // Upload files/images from device
  // Context sources
  | 'route-context'          // URL-based routing with params
  | 'ide-context'            // IDE file/editor/terminal context
  | 'git-context'            // Git status, branch, modified files
  // System access
  | 'file-system'            // Read/write local files
  | 'terminal'               // Execute shell commands
  | 'clipboard'              // Read/write clipboard
  | 'diagnostics'            // Language diagnostics (errors, warnings)
  // Mobile/native
  | 'camera'                 // Camera access
  | 'haptics'                // Haptic feedback
  | 'push-notifications'     // Push notifications
  | 'biometrics'             // Biometric auth
  // Network
  | 'sse-streaming'          // Consume SSE event streams
  | 'websocket'              // Maintain WebSocket connections
  ;

/**
 * Known platform identifiers.
 * Used for explicit plugin platform overrides.
 */
export type PluginPlatform =
  | 'web'                // Angular web apps
  | 'vscode'             // VSCode/Cursor extension (Lit)
  | 'chrome-extension'   // Chrome extension sidepanel
  | 'mobile'             // React Native / Flutter
  | 'desktop'            // Electron / Tauri
  ;

/**
 * Platform capability presets.
 *
 * AI AGENT GUIDE — Adding a new platform:
 * 1. Add platform to PluginPlatform
 * 2. Add capabilities here
 * 3. Run preset factory tests to verify resolution
 */
export const PLATFORM_CAPABILITIES: Record<PluginPlatform, PlatformCapability[]> = {
  web: [
    'component-rendering', 'drag-drop', 'speech-api', 'file-upload',
    'route-context', 'clipboard', 'sse-streaming', 'websocket',
  ],
  vscode: [
    'component-rendering', 'file-system', 'terminal', 'diagnostics',
    'git-context', 'ide-context', 'clipboard', 'sse-streaming', 'websocket',
  ],
  'chrome-extension': [
    'component-rendering', 'clipboard', 'sse-streaming', 'websocket',
  ],
  mobile: [
    'component-rendering', 'speech-api', 'file-upload', 'camera',
    'haptics', 'push-notifications', 'biometrics', 'clipboard',
    'sse-streaming', 'websocket',
  ],
  desktop: [
    'component-rendering', 'file-system', 'terminal', 'git-context',
    'clipboard', 'drag-drop', 'sse-streaming', 'websocket',
  ],
};

6. Plugin Resolution

// interfaces/plugin-resolution.ts

/**
 * Resolution order:
 * 1. plugin.platforms set → only those platforms (explicit override)
 * 2. plugin.requires empty → compatible with ALL platforms
 * 3. Otherwise → platform must provide ALL required capabilities
 */
export function isPluginCompatible(
  plugin: ChatPlugin,
  platform: PluginPlatform,
): boolean {
  if (plugin.platforms && plugin.platforms.length > 0) {
    return plugin.platforms.includes(platform);
  }
  if (plugin.requires.length === 0) {
    return true;
  }
  const available = PLATFORM_CAPABILITIES[platform] ?? [];
  return plugin.requires.every(cap => available.includes(cap));
}

export function resolvePluginsForPlatform(
  plugins: ChatPlugin[],
  platform: PluginPlatform,
): ChatPlugin[] {
  return plugins.filter(p => isPluginCompatible(p, platform));
}

7. Platform-Aware Preset Factory

// composition/plugin-preset.factory.ts

export interface StandardPluginSetConfig {
  /** Target platform */
  platform: PluginPlatform;
  /** User role — controls feature gating */
  platformRole: PlatformRole;
  /** Reactive rich context */
  contextSignal: ReactiveValue<VittoRichContext | null>;

  // Callbacks (universal)
  onModelChange?: (id: string) => void;
  onAgentStatusRequested?: () => void;

  // Optional providers (interface-based)
  contextProvider?: ContextSuggestionProvider;
  awarenessProvider?: AwarenessStateProvider;

  // Component refs (platform-specific)
  generationComponents?: { image?: ComponentRef; module?: ComponentRef; video?: ComponentRef; widget?: ComponentRef };
  teachComponent?: ComponentRef;
  promptComponent?: ComponentRef;
  observatoryComponent?: ComponentRef;
  awarenessComponents?: { cuProgressIndicator?: ComponentRef };

  // Data
  userId?: string;
  spaceId?: string;
  roleLevel?: number;
  includeVoice?: boolean;

  // Module builder (S248)
  moduleContextSignal?: ReactiveValue<ModuleContextState | null>;
  onEntityExtract?: (entities: string[]) => void;
  onModuleGenerate?: (description: string) => void;
  onModuleValidate?: () => void;
  onModuleExport?: () => void;

  // Suggestions
  activeSuggestions?: ReactiveValue<string[]>;
}

/**
 * Canonical plugin composition — platform-aware.
 *
 * Creates ALL plugins in standard order (category priority),
 * then filters by:
 * 1. Platform capability matching (automatic)
 * 2. Role gating (consumer vs T0+)
 * 3. Config availability (no component ref = no tab plugin)
 */
export function createStandardPluginSet(config: StandardPluginSetConfig): ChatPlugin[] {
  // ... builds all plugins in canonical order, then:
  return resolvePluginsForPlatform(allPlugins, config.platform);
}

Canonical order: context → intelligence → generation → bridge → input → learning (silent). Same order as current createStandardPluginSet in S168 plugin-presets.ts.


8. Migration: vitto-chat → chat-plugin-core

What Moves

From @libs-ai/vitto-chat To @libs-ai/chat-plugin-core
plugins/chat-plugin.interface.ts interfaces/chat-plugin.interface.ts
plugins/*.plugin.ts (all 17) plugins/{category}/*.plugin.ts
plugins/plugin-presets.ts composition/plugin-preset.factory.ts
types/chat.types.ts (types below) types/chat-core.types.ts

Types that must migrate to chat-core.types.ts:

ChatMessage, ChatMessageRole, ChatMessageMetadata, ToolCallInfo, VittoMood, TaskProgressState, TaskDelegation, TaskCheckpoint, VittoPhase, SlashCommandDefinition, SlashCommand, ActionableOutput, ActionableOutputType, ChatPluginTab, QuickAction, VittoRichContext, VittoAppType, VittoCapability, CapabilityActionType, VittoSuggestion, SuggestionType, VittoChatFeatures, chatFeaturesForPlatformRole

Types that stay in vitto-chat (Angular-specific or service-coupled):

VittoChatConfig (uses Angular service patterns), component input types, SpaceContext re-export (stays as local alias, consumers import RouteContext from core-models)

What Stays in vitto-chat

Zero-Breaking-Change Re-exports

// @libs-ai/vitto-chat/src/lib/plugins/index.ts
// Re-exports for backward compatibility. Import from @libs-ai/chat-plugin-core directly.
export {
  type ChatPlugin,
  isChatPlugin,
  createRichContextPlugin,
  createCapabilitiesPlugin,
  // ... all 17 plugin factories (see plugins/index.ts for full list)
  createStandardPluginSet,
  type StandardPluginSetConfig,
} from '@libs-ai/chat-plugin-core';

Angular Adapter

// @libs-ai/vitto-chat/src/lib/adapters/angular-plugin.adapter.ts

import { createStandardPluginSet, StandardPluginSetConfig } from '@libs-ai/chat-plugin-core';

/**
 * Angular wrapper. Angular Signals ARE ReactiveValues (same call
 * signature). Angular Type<T> IS ComponentRef (unknown). This exists
 * for type-narrowing convenience and backward compatibility.
 *
 * PluginPresetConfig is the EXISTING config type from S168.
 * This adapter maps it to StandardPluginSetConfig, injecting platform: 'web'.
 */
export function createAngularPluginSet(config: PluginPresetConfig): ChatPlugin[] {
  return createStandardPluginSet({
    platform: 'web',
    platformRole: config.platformRole,
    contextSignal: config.contextSignal,
    onModelChange: config.onModelChange,
    onAgentStatusRequested: config.onAgentStatusRequested,
    generationComponents: config.generationComponents,
    teachComponent: config.teachComponent,
    promptComponent: config.promptComponent,
    observatoryComponent: config.observatoryComponent,
    userId: config.userId,
    spaceId: config.spaceId,
    roleLevel: config.roleLevel,
    includeVoice: config.includeVoice,
    moduleContextSignal: config.moduleContextSignal,
    onEntityExtract: config.onEntityExtract,
    onModuleGenerate: config.onModuleGenerate,
    onModuleValidate: config.onModuleValidate,
    onModuleExport: config.onModuleExport,
    // Map Angular services to provider interfaces
    contextProvider: config.contextService,
    awarenessProvider: config.awarenessService,
    awarenessComponents: config.awarenessComponents,
  });
}

9. VSCode Extension: Chat Tab

Shell Tab Addition

Current shell in main-panel.ts:1752-1775 has two tabs (MCP, Vitto). Add a third:

<nav class="shell-tabs">
  <button class="shell-tab active" data-pane="mcp">
    <span class="tab-icon">&#x2699;</span><span>MCP</span>
  </button>
  <button class="shell-tab" data-pane="chat">
    <span class="tab-icon">&#x1F4AC;</span><span>Vitto</span>
  </button>
  <button class="shell-tab" data-pane="agent-chat">
    <span class="tab-icon">&#x26A1;</span><span>Chat</span>
  </button>
</nav>

<div class="shell-content">
  <div id="pane-mcp" class="shell-pane lit-pane active"><!-- Lit MCP dashboard --></div>
  <div id="pane-chat" class="shell-pane"><!-- Angular iframe --></div>
  <div id="pane-agent-chat" class="shell-pane lit-pane"><!-- Native agent chat --></div>
</div>

Agent Chat Lit Component

New Lit component: agent-chat-view.ts mounted inside #pane-agent-chat.

Consumes plugins from chat-plugin-core:

import { createStandardPluginSet } from '@libs-ai/chat-plugin-core';

const plugins = createStandardPluginSet({
  platform: 'vscode',
  platformRole: 'developer',
  contextSignal: () => this.ideContext,
  onModelChange: (id) => send({ type: 'setModel', modelId: id }),
  onAgentStatusRequested: () => send({ type: 'getAgentStatus' }),
  userId: this.userId,
});

Auto-excluded on VSCode (via capability matching):

Auto-excluded because no component ref provided:

Works out of the box:

Chat UI Features


10. IDE Context Adapter

RouteContext in @libs-foundational/core-models must be extended with IDE contexts.

Addition to context.types.ts:

export type RouteContext =
  // Web contexts (existing)
  | 'page-builder' | 'modular-universe' | 'universe-explorer'
  | 'theme-designer' | 'flow-builder' | 'widget-editor'
  | 'dashboard' | 'settings' | 'analytics' | 'user-management'
  | 'vitto' | 'vitto-studio' | 'computer-use'
  // IDE contexts (new)
  | 'ide-editor' | 'ide-terminal' | 'ide-debug' | 'ide-diff'
  // Mobile contexts (future)
  | 'mobile-home' | 'mobile-notifications'
  | 'unknown';

// MUST also add to CapabilityContext:
export type CapabilityContext =
  // ... existing values ...
  | 'ide-assistant'        // New: maps from all ide-* routes
  | 'mobile-assistant'     // New: maps from all mobile-* routes
  ;

// MUST also add to ROUTE_TO_CAPABILITY (Record<RouteContext, CapabilityContext>
// requires ALL union members mapped):
export const ROUTE_TO_CAPABILITY: Record<RouteContext, CapabilityContext> = {
  // ... existing mappings ...
  'ide-editor': 'ide-assistant',
  'ide-terminal': 'ide-assistant',
  'ide-debug': 'ide-assistant',
  'ide-diff': 'ide-assistant',
  'mobile-home': 'mobile-assistant',
  'mobile-notifications': 'mobile-assistant',
};

In the extension, ContextProviderService maps IDE state to VittoRichContext:

function mapIdeContextToRichContext(ide: IdeContext): VittoRichContext {
  return {
    contextType: 'ide-editor',
    filePath: ide.activeFile?.path,
    fileLanguage: ide.activeFile?.language,
    fileContent: ide.activeFile?.content,
    metadata: {
      openFiles: ide.openFiles,
      diagnosticCount: ide.diagnostics.reduce((sum, d) => sum + d.errors + d.warnings, 0),
      gitBranch: ide.gitStatus?.branch,
      gitModified: ide.gitStatus?.modified,
      projectType: ide.projectType,
    },
  };
}

The capabilities.plugin.ts needs IDE-specific capability data added to CAPABILITIES_BY_CONTEXT for ide-editor, ide-terminal, etc.


11. Extension Host Services

Three new services in the extension host (Node.js side), as specified in the S240 Native Agent Mode spec:

Service Purpose Key Methods
IdeToolService VSCode API wrappers readFile, writeFile, runCommand, searchCode, listFiles, getDiagnostics, showDiff
AgentSessionService SSE client + tool execution loop chat(options), cancel(), handleStreamEvent(), executeTool()
ContextProviderService Gathers IDE context gatherContext(), getGitStatus(), detectProjectType()

These follow the S240 spec exactly. See docs/superpowers/specs/2026-03-22-vitto-native-agent-mode-design.md sections 2, 3, 6.


12. Message Protocol Extensions

New message types added to the webview ↔ extension host bridge:

// MessageToExtension additions:
| { type: 'agentMessage'; text: string; sessionId?: string }
| { type: 'agentCancel' }
| { type: 'agentApproveEdit'; path: string }
| { type: 'agentRejectEdit'; path: string }
| { type: 'getIdeContext' }

// MessageFromExtension additions:
| { type: 'agentText'; content: string }
| { type: 'agentThinking'; content: string }
| { type: 'agentToolStart'; tool: string; input: Record<string, unknown> }
| { type: 'agentToolResult'; tool: string; result: IdeToolResult }
| { type: 'agentDone'; summary: string }
| { type: 'agentError'; message: string }
| { type: 'agentCancelled' }
| { type: 'ideContext'; context: IdeContext }

13. Infrastructure Details

Path Mapping

Add to tsconfig.base.json (not generated.paths.json — that's for @module/*):

"@libs-ai/chat-plugin-core": ["libs/libs-ai/chat-plugin-core/src"],
"@libs-ai/chat-plugin-core/*": ["libs/libs-ai/chat-plugin-core/src/*"]

esbuild Resolution — REQUIRES SETUP

The extension does NOT currently import from any @libs-* library. The webview build pipeline (tsconfig.webview.json → tsc → esbuild) lacks path alias resolution for @libs-*. This must be fixed in Phase 3:

Option A (recommended): Add an esbuild alias plugin to esbuild.mjs that reads tsconfig.base.json paths and maps them for bundling. This is a ~15-line plugin.

Option B: Make tsconfig.webview.json extend tsconfig.base.json. This gives it path mappings but may introduce unwanted compiler options — test carefully.

Option C: Use relative imports from the webview source to chat-plugin-core source files. Fragile but works without config changes.

The Node extension build (tsconfig.json extends tsconfig.base.json) can already resolve @libs-* paths — only the webview build needs setup.

CSS Scoping Convention

Per convention vscode-extension:global-css-scoping: global CSS in the Lit webview must be scoped to #pane-mcp to avoid clobbering shell tabs. The Chat tab's Lit components must scope their styles to #pane-agent-chat or use Lit's shadow DOM (preferred).

CSP Policy

Per convention vscode-extension:csp-nonce: webview uses nonce-based script-src. No inline onclick. Use addEventListener in nonce'd script blocks.


14. Risks & Mitigations

Risk Severity Mitigation
AI_MODELS_CONFIG from @libs-foundational/api-config may have Angular deps Medium Verify. If Angular-dependent, extract model constants to core-models or chat-plugin-core
esbuild/webview cannot resolve @libs-* paths High Webview tsconfig lacks path aliases. Must add esbuild alias plugin or extend tsconfig.base.json. See Infrastructure section. Test in Phase 3 before building Chat UI.
Backend /engine/agent-session SSE endpoint doesn't exist High Must be built on vitto-api. Extension can be tested with mocks. Backend is separate work.
Plugin getter pattern (get quickActions()) caching Low Documented in interface: "always access fresh, never cache"
Migration blast radius (16 plugins moving) Medium Re-export shim in vitto-chat keeps all existing imports working
RouteContext extension with IDE types affects all consumers Low Additive change — new union members don't break existing pattern matches (TypeScript is structural)

15. Phasing

Phase 1: chat-plugin-core Library + Plugin Migration (1-2 sessions)

Interfaces and factories MUST move together — the new ChatPlugin interface requires category and requires fields that only exist when factories are updated.

Phase 3: VSCode Chat Tab — Foundation (1 session)

Phase 4: VSCode Chat Tab — Agent Loop (1 session)

Phase 5: VSCode Chat Tab — Polish (1 session)

Phase 6: Backend Agent Session Endpoint (1 session)


16. Key Files Reference

Files to Create

libs/libs-ai/chat-plugin-core/
├── project.json
├── tsconfig.json (NO angularCompilerOptions)
├── tsconfig.lib.json
├── jest.config.ts
└── src/
    ├── index.ts
    └── lib/
        ├── interfaces/
        │   ├── chat-plugin.interface.ts
        │   ├── plugin-providers.interface.ts
        │   └── plugin-resolution.ts
        ├── types/
        │   ├── plugin-categories.ts
        │   ├── plugin-capabilities.ts
        │   ├── reactive.types.ts
        │   └── chat-core.types.ts
        ├── plugins/
        │   ├── index.ts
        │   ├── context/{rich-context,capabilities,suggestions}.plugin.ts
        │   ├── intelligence/{model,agent,mastery,learning}.plugin.ts
        │   ├── generation/{generation,module-builder,teach,prompt-library}.plugin.ts
        │   ├── bridge/{computer-use-bridge,observatory,awareness}.plugin.ts
        │   └── input/{voice,action,context-legacy}.plugin.ts
        ├── composition/
        │   ├── plugin-preset.factory.ts
        │   └── plugin-resolver.ts
        └── utils/
            └── plugin-guards.ts

apps/vitto-vscode-extension/src/
├── services/
│   ├── ide-tool.service.ts
│   ├── agent-session.service.ts
│   ├── context-provider.service.ts
│   └── diff-content-provider.ts
└── panels/webview/components/agent/
    └── agent-chat-view.ts

Files to Modify

libs/libs-ai/vitto-chat/src/
├── lib/plugins/index.ts            # Re-export shim
├── lib/plugins/plugin-presets.ts    # Angular adapter wrapper

libs/libs-foundational/core-models/src/
├── lib/types/context.types.ts      # Add ide-editor, ide-terminal, etc.

apps/vitto-vscode-extension/src/
├── panels/main-panel.ts            # Add Chat tab + agent message handlers
├── panels/webview/types.ts         # Add 9 new message types
├── panels/webview/app.ts           # (Lit app stays in MCP pane — no change)

tsconfig.base.json                  # Add @libs-ai/chat-plugin-core path

Plugin Compatibility Matrix

Plugin Category requires platforms Web VSCode Mobile Desktop Chrome Ext
rich-context context [] — Y Y Y Y Y
capabilities context [] — Y Y Y Y Y
suggestions context [] — Y Y Y Y Y
model intelligence [] — Y Y Y Y Y
agent intelligence [] — Y Y Y Y Y
mastery intelligence [] — Y Y Y Y Y
learning intelligence [] — Y Y Y Y Y
generation generation [component-rendering] — Y Y Y Y Y
module-builder generation [] — Y Y Y Y Y
teach generation [component-rendering] — Y Y Y Y Y
prompt-library generation [component-rendering] — Y Y Y Y Y
observatory bridge [component-rendering] — Y Y Y Y Y
computer-use-bridge bridge [] — Y Y Y Y Y
awareness bridge [] — Y Y Y Y Y
voice input [speech-api] — Y N Y N N
action input [drag-drop] — Y N N Y N

Notes:


End of spec. This extracts a framework-agnostic plugin core from 16 existing plugins, adds platform capability negotiation for cross-surface reuse, and enables a native Chat tab in the VSCode extension — all without breaking existing Angular consumers.