mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 19:05:11 +00:00
987 lines
33 KiB
Markdown
987 lines
33 KiB
Markdown
# Porting Plan: genai-workshop-new Chat System to Browser Extension
|
|
|
|
## Executive Summary
|
|
|
|
Port the complete chat interface, message rendering, streaming, tool execution, and transport system from `genai-workshop-new/src/app` to the browser extension. The goal is to provide a full-featured AI chat interface with:
|
|
|
|
1. **Multiple transport options**: Direct API calls OR proxy-based calls
|
|
2. **Full message rendering**: Text, thinking blocks, tool calls, attachments, images
|
|
3. **Streaming support**: Real-time message streaming with proper batching
|
|
4. **Tool execution and rendering**: Extensible tool system with custom renderers
|
|
5. **Session management**: State management with persistence
|
|
6. **Debug capabilities**: Optional debug view for development
|
|
|
|
## Current State Analysis
|
|
|
|
### Already Ported to Browser Extension
|
|
- ✅ `AttachmentTile.ts` - Display attachment thumbnails
|
|
- ✅ `AttachmentOverlay.ts` - Full-screen attachment viewer
|
|
- ✅ `MessageEditor.ts` - Input field with attachment support
|
|
- ✅ `utils/attachment-utils.ts` - PDF, Office, image processing
|
|
- ✅ `utils/i18n.ts` - Internationalization
|
|
- ✅ `dialogs/ApiKeysDialog.ts` - API key management
|
|
- ✅ `dialogs/ModelSelector.ts` - Model selection dialog
|
|
- ✅ `state/KeyStore.ts` - API key storage
|
|
|
|
### Available in @mariozechner/mini-lit Package
|
|
- ✅ `CodeBlock` - Syntax-highlighted code display
|
|
- ✅ `MarkdownBlock` - Markdown rendering
|
|
- ✅ `Button`, `Input`, `Select`, `Textarea`, etc. - UI components
|
|
- ✅ `ThemeToggle` - Dark/light mode
|
|
- ✅ `Dialog` - Base dialog component
|
|
|
|
### Needs to Be Ported
|
|
|
|
#### Core Chat System
|
|
1. **AgentInterface.ts** (325 lines)
|
|
- Main chat interface container
|
|
- Manages scrolling, auto-scroll behavior
|
|
- Coordinates MessageList, StreamingMessageContainer, MessageEditor
|
|
- Displays usage stats
|
|
- Handles session lifecycle
|
|
|
|
2. **MessageList.ts** (78 lines)
|
|
- Renders stable (non-streaming) messages
|
|
- Uses `repeat()` directive for efficient rendering
|
|
- Maps tool results by call ID
|
|
- Renders user and assistant messages
|
|
|
|
3. **Messages.ts** (286 lines)
|
|
- **UserMessage component**: Displays user messages with attachments
|
|
- **AssistantMessage component**: Displays assistant messages with text, thinking, tool calls
|
|
- **ToolMessage component**: Displays individual tool invocations with debug view
|
|
- **ToolMessageDebugView component**: Shows tool call args and results
|
|
- **AbortedMessage component**: Shows aborted requests
|
|
|
|
4. **StreamingMessageContainer.ts** (95 lines)
|
|
- Manages streaming message updates
|
|
- Batches updates using `requestAnimationFrame` for performance
|
|
- Shows loading indicator during streaming
|
|
- Handles immediate updates for clearing
|
|
|
|
5. **ConsoleBlock.ts** (62 lines)
|
|
- Console-style output display
|
|
- Auto-scrolling to bottom
|
|
- Copy button functionality
|
|
- Used by tool renderers
|
|
|
|
#### State Management
|
|
|
|
6. **state/agent-session.ts** (282 lines)
|
|
- **AgentSession class**: Core state management
|
|
- Manages conversation state: messages, model, tools, system prompt, thinking level
|
|
- Implements pub/sub pattern for state updates
|
|
- Handles message preprocessing (e.g., extracting text from documents)
|
|
- Coordinates transport for sending messages
|
|
- Collects debug information
|
|
- Event types: `state-update`, `error-no-model`, `error-no-api-key`
|
|
- Methods:
|
|
- `prompt(input, attachments)` - Send user message
|
|
- `setModel()`, `setSystemPrompt()`, `setThinkingLevel()`, `setTools()`
|
|
- `appendMessage()`, `replaceMessages()`, `clearMessages()`
|
|
- `abort()` - Cancel ongoing request
|
|
- `subscribe(fn)` - Listen to state changes
|
|
|
|
7. **state/session-store.ts** (needs investigation)
|
|
- Session persistence to IndexedDB
|
|
- Load/save conversation history
|
|
- Multiple session management
|
|
|
|
#### Transport Layer
|
|
|
|
8. **state/transports/types.ts** (17 lines)
|
|
- `AgentTransport` interface
|
|
- `AgentRunConfig` interface
|
|
- Defines contract for transport implementations
|
|
|
|
9. **state/transports/proxy-transport.ts** (54 lines)
|
|
- **LocalTransport class** (misleadingly named - actually proxy)
|
|
- Calls proxy server via `streamSimpleProxy`
|
|
- Passes auth token from KeyStore
|
|
- Yields events from `agentLoop()`
|
|
|
|
10. **NEW: state/transports/direct-transport.ts** (needs creation)
|
|
- **DirectTransport class**
|
|
- Calls provider APIs directly using API keys from KeyStore
|
|
- Uses `@mariozechner/pi-ai`'s `agentLoop()` directly
|
|
- No auth token needed
|
|
|
|
11. **utils/proxy-client.ts** (285 lines)
|
|
- `streamSimpleProxy()` function
|
|
- Fetches from `/api/stream` endpoint
|
|
- Parses SSE (Server-Sent Events) stream
|
|
- Reconstructs partial messages from delta events
|
|
- Handles abort signals
|
|
- Maps proxy events to `AssistantMessageEvent`
|
|
- Detects unauthorized and clears auth token
|
|
|
|
12. **NEW: utils/config.ts** (needs creation)
|
|
- Transport configuration
|
|
- Proxy URL configuration
|
|
- Storage key: `transport-mode` ("direct" | "proxy")
|
|
- Storage key: `proxy-url` (default: configurable)
|
|
|
|
#### Tool System
|
|
|
|
13. **tools/index.ts** (40 lines)
|
|
- Exports tool functions from `@mariozechner/pi-ai`
|
|
- Registers default tool renderers
|
|
- Exports `renderToolParams()` and `renderToolResult()`
|
|
- Re-exports tool implementations
|
|
|
|
14. **tools/types.ts** (needs investigation)
|
|
- `ToolRenderer` interface
|
|
- Contracts for custom tool renderers
|
|
|
|
15. **tools/renderer-registry.ts** (19 lines)
|
|
- Global registry: `Map<string, ToolRenderer>`
|
|
- `registerToolRenderer(name, renderer)` function
|
|
- `getToolRenderer(name)` function
|
|
|
|
16. **tools/renderers/DefaultRenderer.ts** (1162 chars)
|
|
- Fallback renderer for unknown tools
|
|
- Renders params as JSON
|
|
- Renders results as JSON or text
|
|
|
|
17. **tools/renderers/CalculateRenderer.ts** (1677 chars)
|
|
- Custom renderer for calculate tool
|
|
- Shows expression and result
|
|
|
|
18. **tools/renderers/GetCurrentTimeRenderer.ts** (1328 chars)
|
|
- Custom renderer for time tool
|
|
- Shows timezone and formatted time
|
|
|
|
19. **tools/renderers/BashRenderer.ts** (1500 chars)
|
|
- Custom renderer for bash tool
|
|
- Uses ConsoleBlock for output
|
|
|
|
20. **tools/javascript-repl.ts** (needs investigation)
|
|
- JavaScript REPL tool implementation
|
|
- May need adaptation for browser environment
|
|
|
|
21. **tools/web-search.ts** (needs investigation)
|
|
- Web search tool implementation
|
|
- Check if compatible with browser extension
|
|
|
|
22. **tools/sleep.ts** (needs investigation)
|
|
- Simple sleep/delay tool
|
|
|
|
#### Utilities
|
|
|
|
23. **utils/format.ts** (needs investigation)
|
|
- `formatUsage()` - Format token usage and costs
|
|
- Other formatting utilities
|
|
|
|
24. **utils/auth-token.ts** (21 lines)
|
|
- `getAuthToken()` - Prompt for proxy auth token
|
|
- `clearAuthToken()` - Remove from storage
|
|
- Uses PromptDialog for input
|
|
|
|
25. **dialogs/PromptDialog.ts** (needs investigation)
|
|
- Simple text input dialog
|
|
- Used for auth token entry
|
|
|
|
#### Debug/Development
|
|
|
|
26. **DebugView.ts** (needs investigation)
|
|
- Debug panel showing request/response details
|
|
- ChatML formatting
|
|
- SSE event stream
|
|
- Timing information (TTFT, total time)
|
|
- Optional feature for development
|
|
|
|
#### NOT Needed
|
|
|
|
- ❌ `demos/` folder - All demo files (ignore)
|
|
- ❌ `mini/` folder - All UI components (use @mariozechner/mini-lit instead)
|
|
- ❌ `admin/ProxyAdmin.ts` - Proxy server admin (not needed in extension)
|
|
- ❌ `CodeBlock.ts` - Available in @mariozechner/mini-lit
|
|
- ❌ `MarkdownBlock.ts` - Available in @mariozechner/mini-lit
|
|
- ❌ `ScatterPlot.ts` - Demo visualization
|
|
- ❌ `tools/artifacts.ts` - Artifact tool (demo feature)
|
|
- ❌ `tools/bash-mcp-server.ts` - MCP integration (not feasible in browser)
|
|
- ❌ `AttachmentTileList.ts` - Likely superseded by MessageEditor integration
|
|
|
|
---
|
|
|
|
## Detailed Porting Tasks
|
|
|
|
### Phase 1: Core Message Rendering (Foundation)
|
|
|
|
#### Task 1.1: Port ConsoleBlock
|
|
**File**: `src/ConsoleBlock.ts`
|
|
**Dependencies**: mini-lit icons
|
|
**Actions**:
|
|
1. Copy `ConsoleBlock.ts` to browser extension
|
|
2. Update imports to use `@mariozechner/mini-lit`
|
|
3. Replace icon imports with lucide icons:
|
|
- `iconCheckLine` → `Check`
|
|
- `iconFileCopy2Line` → `Copy`
|
|
4. Update i18n strings:
|
|
- Add "console", "Copy output", "Copied!" to i18n.ts
|
|
|
|
**Verification**: Render `<console-block content="test output"></console-block>`
|
|
|
|
#### Task 1.2: Port Messages.ts (User, Assistant, Tool Components)
|
|
**File**: `src/Messages.ts`
|
|
**Dependencies**: ConsoleBlock, formatUsage, tool rendering
|
|
**Actions**:
|
|
1. Copy `Messages.ts` to browser extension
|
|
2. Update imports:
|
|
- `Button` from `@mariozechner/mini-lit`
|
|
- `formatUsage` from utils
|
|
- Icons from lucide (ToolsLine, Loader4Line, BugLine)
|
|
3. Add new type: `AppMessage` (already have partial in extension)
|
|
4. Components to register:
|
|
- `user-message`
|
|
- `assistant-message`
|
|
- `tool-message`
|
|
- `tool-message-debug`
|
|
- `aborted-message`
|
|
5. Update i18n strings:
|
|
- "Error:", "Request aborted", "Call", "Result", "(no result)", "Waiting for tool result…", "Call was aborted; no result."
|
|
6. Guard all custom element registrations
|
|
|
|
**Verification**:
|
|
- Render user message with text and attachments
|
|
- Render assistant message with text, thinking, tool calls
|
|
- Render tool message in pending, success, error states
|
|
|
|
#### Task 1.3: Port MessageList
|
|
**File**: `src/MessageList.ts`
|
|
**Dependencies**: Messages.ts
|
|
**Actions**:
|
|
1. Copy `MessageList.ts` to browser extension
|
|
2. Update imports
|
|
3. Uses `repeat()` directive from lit - ensure it's available
|
|
4. Register `message-list` element with guard
|
|
|
|
**Verification**: Render a list of mixed user/assistant/tool messages
|
|
|
|
#### Task 1.4: Port StreamingMessageContainer
|
|
**File**: `src/StreamingMessageContainer.ts`
|
|
**Dependencies**: Messages.ts
|
|
**Actions**:
|
|
1. Copy `StreamingMessageContainer.ts` to browser extension
|
|
2. Update imports
|
|
3. Register `streaming-message-container` element with guard
|
|
4. Test batching behavior with rapid updates
|
|
|
|
**Verification**:
|
|
- Stream messages update smoothly
|
|
- Cursor blinks during streaming
|
|
- Immediate clear works correctly
|
|
|
|
---
|
|
|
|
### Phase 2: Tool System
|
|
|
|
#### Task 2.1: Port Tool Types and Registry
|
|
**Files**: `src/tools/types.ts`, `src/tools/renderer-registry.ts`
|
|
**Actions**:
|
|
1. Read `tools/types.ts` to understand `ToolRenderer` interface
|
|
2. Copy both files to `src/tools/`
|
|
3. Create registry as singleton
|
|
|
|
**Verification**: Can register and retrieve renderers
|
|
|
|
#### Task 2.2: Port Tool Renderers
|
|
**Files**: All `src/tools/renderers/*.ts`
|
|
**Actions**:
|
|
1. Copy `DefaultRenderer.ts`
|
|
2. Copy `CalculateRenderer.ts`
|
|
3. Copy `GetCurrentTimeRenderer.ts`
|
|
4. Copy `BashRenderer.ts`
|
|
5. Update all to use `@mariozechner/mini-lit` and lucide icons
|
|
6. Ensure all use ConsoleBlock where needed
|
|
|
|
**Verification**: Test each renderer with sample tool calls
|
|
|
|
#### Task 2.3: Port Tool Implementations
|
|
**Files**: `src/tools/javascript-repl.ts`, `src/tools/web-search.ts`, `src/tools/sleep.ts`
|
|
**Actions**:
|
|
1. Read each file to assess browser compatibility
|
|
2. Port `sleep.ts` (should be trivial)
|
|
3. Port `javascript-repl.ts` - may need `new Function()` or eval
|
|
4. Port `web-search.ts` - check if it uses fetch or needs adaptation
|
|
5. Update `tools/index.ts` to register all renderers and export tools
|
|
|
|
**Verification**: Test each tool execution in browser context
|
|
|
|
---
|
|
|
|
### Phase 3: Transport Layer
|
|
|
|
#### Task 3.1: Port Transport Types
|
|
**File**: `src/state/transports/types.ts`
|
|
**Actions**:
|
|
1. Copy file to `src/state/transports/`
|
|
2. Verify types align with pi-ai package
|
|
|
|
**Verification**: Types compile correctly
|
|
|
|
#### Task 3.2: Port Proxy Client
|
|
**File**: `src/utils/proxy-client.ts`
|
|
**Dependencies**: auth-token.ts
|
|
**Actions**:
|
|
1. Copy `proxy-client.ts` to `src/utils/`
|
|
2. Update `streamSimpleProxy()` to use configurable proxy URL
|
|
3. Read proxy URL from config (default: user-configurable)
|
|
4. Update error messages for i18n
|
|
5. Add i18n strings: "Proxy error: {status} {statusText}", "Proxy error: {error}", "Auth token is required for proxy transport"
|
|
|
|
**Verification**: Can connect to proxy server with auth token
|
|
|
|
#### Task 3.3: Port Proxy Transport
|
|
**File**: `src/state/transports/proxy-transport.ts`
|
|
**Actions**:
|
|
1. Copy file to `src/state/transports/`
|
|
2. Rename `LocalTransport` to `ProxyTransport` for clarity
|
|
3. Update to use `streamSimpleProxy` from proxy-client
|
|
4. Integrate with KeyStore for auth token
|
|
|
|
**Verification**: Can send message through proxy
|
|
|
|
#### Task 3.4: Create Direct Transport
|
|
**File**: `src/state/transports/direct-transport.ts` (NEW)
|
|
**Actions**:
|
|
1. Create new `DirectTransport` class implementing `AgentTransport`
|
|
2. Use `agentLoop()` from `@mariozechner/pi-ai` directly
|
|
3. Integrate with KeyStore to get API keys per provider
|
|
4. Pass API key in options to `agentLoop()`
|
|
5. Handle `no-api-key` errors by triggering ApiKeysDialog
|
|
|
|
**Example Implementation**:
|
|
```typescript
|
|
import { agentLoop, type AgentContext, type PromptConfig, type UserMessage } from "@mariozechner/pi-ai";
|
|
import { keyStore } from "../../KeyStore.js";
|
|
import type { AgentRunConfig, AgentTransport } from "./types.js";
|
|
|
|
export class DirectTransport implements AgentTransport {
|
|
constructor(
|
|
private readonly getMessages: () => Promise<Message[]>,
|
|
) {}
|
|
|
|
async *run(userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal) {
|
|
// Get API key from KeyStore
|
|
const apiKey = await keyStore.getKey(cfg.model.provider);
|
|
if (!apiKey) {
|
|
throw new Error("no-api-key");
|
|
}
|
|
|
|
const context: AgentContext = {
|
|
systemPrompt: cfg.systemPrompt,
|
|
messages: await this.getMessages(),
|
|
tools: cfg.tools,
|
|
};
|
|
|
|
const pc: PromptConfig = {
|
|
model: cfg.model,
|
|
reasoning: cfg.reasoning,
|
|
apiKey, // Direct API key
|
|
};
|
|
|
|
// Yield events from agentLoop
|
|
for await (const ev of agentLoop(userMessage as unknown as UserMessage, context, pc, signal)) {
|
|
yield ev;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Verification**: Can send message directly to provider APIs
|
|
|
|
#### Task 3.5: Create Transport Configuration
|
|
**File**: `src/utils/config.ts` (NEW)
|
|
**Actions**:
|
|
1. Create transport mode storage: "direct" | "proxy"
|
|
2. Create proxy URL storage with default
|
|
3. Create getters/setters:
|
|
- `getTransportMode()` / `setTransportMode()`
|
|
- `getProxyUrl()` / `setProxyUrl()`
|
|
4. Store in chrome.storage.local
|
|
|
|
**Example**:
|
|
```typescript
|
|
export type TransportMode = "direct" | "proxy";
|
|
|
|
export async function getTransportMode(): Promise<TransportMode> {
|
|
const result = await chrome.storage.local.get("transport-mode");
|
|
return (result["transport-mode"] as TransportMode) || "direct";
|
|
}
|
|
|
|
export async function setTransportMode(mode: TransportMode): Promise<void> {
|
|
await chrome.storage.local.set({ "transport-mode": mode });
|
|
}
|
|
|
|
export async function getProxyUrl(): Promise<string> {
|
|
const result = await chrome.storage.local.get("proxy-url");
|
|
return result["proxy-url"] || "https://genai.mariozechner.at";
|
|
}
|
|
|
|
export async function setProxyUrl(url: string): Promise<void> {
|
|
await chrome.storage.local.set({ "proxy-url": url });
|
|
}
|
|
```
|
|
|
|
**Verification**: Can read/write transport config
|
|
|
|
---
|
|
|
|
### Phase 4: State Management
|
|
|
|
#### Task 4.1: Port Utilities
|
|
**File**: `src/utils/format.ts`
|
|
**Actions**:
|
|
1. Read file to identify all formatting functions
|
|
2. Copy `formatUsage()` function
|
|
3. Copy any other utilities needed by AgentSession
|
|
4. Update imports
|
|
|
|
**Verification**: Test formatUsage with sample usage data
|
|
|
|
#### Task 4.2: Port Auth Token Utils
|
|
**File**: `src/utils/auth-token.ts`
|
|
**Dependencies**: PromptDialog
|
|
**Actions**:
|
|
1. Copy file to `src/utils/`
|
|
2. Update to use chrome.storage.local
|
|
3. Will need PromptDialog (next task)
|
|
|
|
**Verification**: Can prompt for and store auth token
|
|
|
|
#### Task 4.3: Port/Create PromptDialog
|
|
**File**: `src/dialogs/PromptDialog.ts`
|
|
**Actions**:
|
|
1. Read genai-workshop-new version
|
|
2. Adapt to use `@mariozechner/mini-lit` Dialog
|
|
3. Create simple input dialog similar to ApiKeysDialog
|
|
4. Add `PromptDialog.ask(title, message, defaultValue, isPassword)` static method
|
|
|
|
**Verification**: Can prompt for text input
|
|
|
|
#### Task 4.4: Port Agent Session
|
|
**File**: `src/state/agent-session.ts`
|
|
**Dependencies**: Transports, formatUsage, auth-token, DebugView types
|
|
**Actions**:
|
|
1. Copy `agent-session.ts` to `src/state/`
|
|
2. Update imports:
|
|
- ProxyTransport from `./transports/proxy-transport.js`
|
|
- DirectTransport from `./transports/direct-transport.js`
|
|
- Types from pi-ai
|
|
- KeyStore, auth-token utils
|
|
3. Modify constructor to accept transport mode
|
|
4. Create transport based on mode:
|
|
- "proxy" → ProxyTransport
|
|
- "direct" → DirectTransport
|
|
5. Update to use chrome.storage for persistence
|
|
6. Add `ThinkingLevel` type
|
|
7. Add `AppMessage` type extension for attachments
|
|
|
|
**Key modifications**:
|
|
```typescript
|
|
constructor(opts: AgentSessionOptions & { transportMode?: TransportMode } = {
|
|
authTokenProvider: async () => getAuthToken()
|
|
}) {
|
|
// ... existing state init ...
|
|
|
|
const mode = opts.transportMode || await getTransportMode();
|
|
|
|
if (mode === "proxy") {
|
|
this.transport = new ProxyTransport(
|
|
async () => this.preprocessMessages(),
|
|
opts.authTokenProvider
|
|
);
|
|
} else {
|
|
this.transport = new DirectTransport(
|
|
async () => this.preprocessMessages()
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Verification**:
|
|
- Create session with direct transport, send message
|
|
- Create session with proxy transport, send message
|
|
- Test abort functionality
|
|
- Test state subscription
|
|
|
|
---
|
|
|
|
### Phase 5: Main Interface Integration
|
|
|
|
#### Task 5.1: Port AgentInterface
|
|
**File**: `src/AgentInterface.ts`
|
|
**Dependencies**: Everything above
|
|
**Actions**:
|
|
1. Copy `AgentInterface.ts` to `src/`
|
|
2. Update all imports to use ported components
|
|
3. Register `agent-interface` custom element with guard
|
|
4. Update icons to lucide
|
|
5. Add i18n strings:
|
|
- "No session available", "No session set", "Hide debug view", "Show debug view"
|
|
6. Properties:
|
|
- `session` (external AgentSession)
|
|
- `enableAttachments`
|
|
- `enableModelSelector`
|
|
- `enableThinking`
|
|
- `showThemeToggle`
|
|
- `showDebugToggle`
|
|
7. Methods:
|
|
- `setInput(text, attachments)`
|
|
- `sendMessage(input, attachments)`
|
|
|
|
**Verification**: Full chat interface works end-to-end
|
|
|
|
#### Task 5.2: Integrate into ChatPanel
|
|
**File**: `src/ChatPanel.ts`
|
|
**Actions**:
|
|
1. Remove current chat implementation
|
|
2. Create AgentSession instance
|
|
3. Render `<agent-interface>` with session
|
|
4. Configure:
|
|
- `enableAttachments={true}`
|
|
- `enableModelSelector={true}`
|
|
- `enableThinking={true}`
|
|
- `showThemeToggle={false}` (already in header)
|
|
- `showDebugToggle={false}` (optional)
|
|
5. Remove old MessageEditor integration (now inside AgentInterface)
|
|
6. Set system prompt (optional)
|
|
7. Set default tools (optional - calculateTool, getCurrentTimeTool)
|
|
|
|
**Example**:
|
|
```typescript
|
|
import { AgentSession } from "./state/agent-session.js";
|
|
import "./AgentInterface.js";
|
|
import { calculateTool, getCurrentTimeTool } from "./tools/index.js";
|
|
|
|
@customElement("chat-panel")
|
|
export class ChatPanel extends LitElement {
|
|
@state() private session!: AgentSession;
|
|
|
|
override async connectedCallback() {
|
|
super.connectedCallback();
|
|
|
|
// Create session
|
|
this.session = new AgentSession({
|
|
initialState: {
|
|
systemPrompt: "You are a helpful AI assistant.",
|
|
tools: [calculateTool, getCurrentTimeTool],
|
|
},
|
|
authTokenProvider: async () => getAuthToken(),
|
|
transportMode: await getTransportMode(),
|
|
});
|
|
}
|
|
|
|
override render() {
|
|
return html`
|
|
<agent-interface
|
|
.session=${this.session}
|
|
.enableAttachments=${true}
|
|
.enableModelSelector=${true}
|
|
.enableThinking=${true}
|
|
.showThemeToggle=${false}
|
|
.showDebugToggle=${true}
|
|
></agent-interface>
|
|
`;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Verification**: Full extension works with chat interface
|
|
|
|
#### Task 5.3: Create Settings Dialog
|
|
**File**: `src/dialogs/SettingsDialog.ts` (NEW)
|
|
**Actions**:
|
|
1. Create dialog extending DialogBase
|
|
2. Sections:
|
|
- **Transport Mode**: Radio buttons for "Direct" | "Proxy"
|
|
- **Proxy URL**: Input field (only shown if proxy mode)
|
|
- **API Keys**: Button to open ApiKeysDialog
|
|
3. Save settings to config utils
|
|
|
|
**UI Layout**:
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ Settings [x] │
|
|
├─────────────────────────────────────┤
|
|
│ │
|
|
│ Transport Mode │
|
|
│ ○ Direct (use API keys) │
|
|
│ ● Proxy (use auth token) │
|
|
│ │
|
|
│ Proxy URL │
|
|
│ [https://genai.mariozechner.at ] │
|
|
│ │
|
|
│ [Manage API Keys...] │
|
|
│ │
|
|
│ [Cancel] [Save] │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
**Verification**: Can toggle transport mode and set proxy URL
|
|
|
|
#### Task 5.4: Update Header
|
|
**File**: `src/sidepanel.ts`
|
|
**Actions**:
|
|
1. Change settings button to open SettingsDialog (not ApiKeysDialog directly)
|
|
2. SettingsDialog should have button to open ApiKeysDialog
|
|
|
|
**Verification**: Settings accessible from header
|
|
|
|
---
|
|
|
|
### Phase 6: Optional Features
|
|
|
|
#### Task 6.1: Port DebugView (Optional)
|
|
**File**: `src/DebugView.ts`
|
|
**Actions**:
|
|
1. Read full file to understand functionality
|
|
2. Copy to `src/`
|
|
3. Update imports
|
|
4. Format ChatML, SSE events, timing info
|
|
5. Add to AgentInterface when `showDebugToggle={true}`
|
|
|
|
**Verification**: Debug view shows request/response details
|
|
|
|
#### Task 6.2: Port Session Store (Optional)
|
|
**File**: `src/utils/session-db.ts` or `src/state/session-store.ts`
|
|
**Actions**:
|
|
1. Read file to understand IndexedDB usage
|
|
2. Create IndexedDB schema for sessions
|
|
3. Implement save/load/list/delete operations
|
|
4. Add to AgentInterface or ChatPanel
|
|
5. Add UI for switching sessions
|
|
|
|
**Verification**: Can save and load conversation history
|
|
|
|
#### Task 6.3: Add System Prompt Editor (Optional)
|
|
**Actions**:
|
|
1. Create dialog or expandable textarea
|
|
2. Allow editing session.state.systemPrompt
|
|
3. Add to settings or main interface
|
|
|
|
**Verification**: Can customize system prompt
|
|
|
|
---
|
|
|
|
## File Mapping Reference
|
|
|
|
### Source → Destination
|
|
|
|
| Source File | Destination File | Status | Dependencies |
|
|
|------------|------------------|--------|--------------|
|
|
| `app/ConsoleBlock.ts` | `src/ConsoleBlock.ts` | ⭕ New | mini-lit, lucide |
|
|
| `app/Messages.ts` | `src/Messages.ts` | ⭕ New | ConsoleBlock, formatUsage, tools |
|
|
| `app/MessageList.ts` | `src/MessageList.ts` | ⭕ New | Messages.ts |
|
|
| `app/StreamingMessageContainer.ts` | `src/StreamingMessageContainer.ts` | ⭕ New | Messages.ts |
|
|
| `app/AgentInterface.ts` | `src/AgentInterface.ts` | ⭕ New | All message components |
|
|
| `app/state/agent-session.ts` | `src/state/agent-session.ts` | ⭕ New | Transports, formatUsage |
|
|
| `app/state/transports/types.ts` | `src/state/transports/types.ts` | ⭕ New | pi-ai |
|
|
| `app/state/transports/proxy-transport.ts` | `src/state/transports/proxy-transport.ts` | ⭕ New | proxy-client |
|
|
| N/A | `src/state/transports/direct-transport.ts` | ⭕ New | pi-ai, KeyStore |
|
|
| `app/utils/proxy-client.ts` | `src/utils/proxy-client.ts` | ⭕ New | auth-token |
|
|
| N/A | `src/utils/config.ts` | ⭕ New | chrome.storage |
|
|
| `app/utils/format.ts` | `src/utils/format.ts` | ⭕ New | None |
|
|
| `app/utils/auth-token.ts` | `src/utils/auth-token.ts` | ⭕ New | PromptDialog |
|
|
| `app/tools/types.ts` | `src/tools/types.ts` | ⭕ New | None |
|
|
| `app/tools/renderer-registry.ts` | `src/tools/renderer-registry.ts` | ⭕ New | types.ts |
|
|
| `app/tools/renderers/DefaultRenderer.ts` | `src/tools/renderers/DefaultRenderer.ts` | ⭕ New | mini-lit |
|
|
| `app/tools/renderers/CalculateRenderer.ts` | `src/tools/renderers/CalculateRenderer.ts` | ⭕ New | mini-lit |
|
|
| `app/tools/renderers/GetCurrentTimeRenderer.ts` | `src/tools/renderers/GetCurrentTimeRenderer.ts` | ⭕ New | mini-lit |
|
|
| `app/tools/renderers/BashRenderer.ts` | `src/tools/renderers/BashRenderer.ts` | ⭕ New | ConsoleBlock |
|
|
| `app/tools/javascript-repl.ts` | `src/tools/javascript-repl.ts` | ⭕ New | pi-ai |
|
|
| `app/tools/web-search.ts` | `src/tools/web-search.ts` | ⭕ New | pi-ai |
|
|
| `app/tools/sleep.ts` | `src/tools/sleep.ts` | ⭕ New | pi-ai |
|
|
| `app/tools/index.ts` | `src/tools/index.ts` | ⭕ New | All tools |
|
|
| `app/dialogs/PromptDialog.ts` | `src/dialogs/PromptDialog.ts` | ⭕ New | mini-lit |
|
|
| N/A | `src/dialogs/SettingsDialog.ts` | ⭕ New | config, ApiKeysDialog |
|
|
| `app/DebugView.ts` | `src/DebugView.ts` | ⭕ Optional | highlight.js |
|
|
| `app/utils/session-db.ts` | `src/utils/session-db.ts` | ⭕ Optional | IndexedDB |
|
|
|
|
### Already in Extension
|
|
|
|
| File | Status | Notes |
|
|
|------|--------|-------|
|
|
| `src/MessageEditor.ts` | ✅ Exists | May need minor updates |
|
|
| `src/AttachmentTile.ts` | ✅ Exists | Complete |
|
|
| `src/AttachmentOverlay.ts` | ✅ Exists | Complete |
|
|
| `src/utils/attachment-utils.ts` | ✅ Exists | Complete |
|
|
| `src/dialogs/ModelSelector.ts` | ✅ Exists | May need integration check |
|
|
| `src/dialogs/ApiKeysDialog.ts` | ✅ Exists | Complete |
|
|
| `src/state/KeyStore.ts` | ✅ Exists | Complete |
|
|
|
|
---
|
|
|
|
## Critical Implementation Notes
|
|
|
|
### 1. Custom Element Registration Guards
|
|
|
|
ALL custom elements must use registration guards to prevent duplicate registration errors:
|
|
|
|
```typescript
|
|
// Instead of @customElement decorator
|
|
export class MyComponent extends LitElement {
|
|
// ... component code ...
|
|
}
|
|
|
|
// At end of file
|
|
if (!customElements.get("my-component")) {
|
|
customElements.define("my-component", MyComponent);
|
|
}
|
|
```
|
|
|
|
### 2. Import Path Updates
|
|
|
|
When porting, update ALL imports:
|
|
|
|
**From genai-workshop-new**:
|
|
```typescript
|
|
import { Button } from "./mini/Button.js";
|
|
import { iconLoader4Line } from "./mini/icons.js";
|
|
```
|
|
|
|
**To browser extension**:
|
|
```typescript
|
|
import { Button } from "@mariozechner/mini-lit";
|
|
import { Loader2 } from "lucide";
|
|
import { icon } from "@mariozechner/mini-lit";
|
|
// Use: icon(Loader2, "md")
|
|
```
|
|
|
|
### 3. Icon Mapping
|
|
|
|
| genai-workshop | lucide | Usage |
|
|
|----------------|--------|-------|
|
|
| `iconLoader4Line` | `Loader2` | `icon(Loader2, "sm")` |
|
|
| `iconToolsLine` | `Wrench` | `icon(Wrench, "md")` |
|
|
| `iconBugLine` | `Bug` | `icon(Bug, "sm")` |
|
|
| `iconCheckLine` | `Check` | `icon(Check, "sm")` |
|
|
| `iconFileCopy2Line` | `Copy` | `icon(Copy, "sm")` |
|
|
|
|
### 4. Chrome Extension APIs
|
|
|
|
Replace browser APIs where needed:
|
|
- `localStorage` → `chrome.storage.local`
|
|
- `fetch("/api/...")` → `fetch(proxyUrl + "/api/...")`
|
|
- No direct filesystem access
|
|
|
|
### 5. Transport Mode Configuration
|
|
|
|
Ensure AgentSession can be created with either transport:
|
|
|
|
```typescript
|
|
// Direct mode (uses API keys from KeyStore)
|
|
const session = new AgentSession({
|
|
transportMode: "direct",
|
|
authTokenProvider: async () => undefined, // not needed
|
|
});
|
|
|
|
// Proxy mode (uses auth token)
|
|
const session = new AgentSession({
|
|
transportMode: "proxy",
|
|
authTokenProvider: async () => getAuthToken(),
|
|
});
|
|
```
|
|
|
|
### 6. i18n Strings to Add
|
|
|
|
All UI strings must be in i18n.ts with English and German translations:
|
|
|
|
```typescript
|
|
// Messages.ts
|
|
"Error:", "Request aborted", "Call", "Result", "(no result)",
|
|
"Waiting for tool result…", "Call was aborted; no result."
|
|
|
|
// ConsoleBlock.ts
|
|
"console", "Copy output", "Copied!"
|
|
|
|
// AgentInterface.ts
|
|
"No session available", "No session set", "Hide debug view", "Show debug view"
|
|
|
|
// Transport errors
|
|
"Proxy error: {status} {statusText}", "Proxy error: {error}",
|
|
"Auth token is required for proxy transport"
|
|
|
|
// Settings
|
|
"Settings", "Transport Mode", "Direct (use API keys)",
|
|
"Proxy (use auth token)", "Proxy URL", "Manage API Keys"
|
|
```
|
|
|
|
### 7. TypeScript Configuration
|
|
|
|
The extension uses `useDefineForClassFields: false` in tsconfig.base.json. Ensure all ported components are compatible.
|
|
|
|
### 8. Build Verification Steps
|
|
|
|
After each phase:
|
|
1. Run `npm run check` - TypeScript compilation
|
|
2. Run `npm run build:chrome` - Chrome extension build
|
|
3. Run `npm run build:firefox` - Firefox extension build
|
|
4. Load extension in browser and test functionality
|
|
5. Check console for errors
|
|
|
|
### 9. Proxy URL Configuration
|
|
|
|
Default proxy URL should be configurable but default to:
|
|
```typescript
|
|
const DEFAULT_PROXY_URL = "https://genai.mariozechner.at";
|
|
```
|
|
|
|
Users should be able to change this in settings for self-hosted proxies.
|
|
|
|
---
|
|
|
|
## Testing Checklist
|
|
|
|
### Phase 1: Message Rendering
|
|
- [ ] User messages display with text
|
|
- [ ] User messages display with attachments
|
|
- [ ] Assistant messages display with text
|
|
- [ ] Assistant messages display with thinking blocks
|
|
- [ ] Assistant messages display with tool calls
|
|
- [ ] Tool messages show pending state with spinner
|
|
- [ ] Tool messages show completed state with results
|
|
- [ ] Tool messages show error state
|
|
- [ ] Tool messages show aborted state
|
|
- [ ] Console blocks render output
|
|
- [ ] Console blocks auto-scroll
|
|
- [ ] Console blocks copy to clipboard
|
|
|
|
### Phase 2: Tool System
|
|
- [ ] Calculate tool renders expression and result
|
|
- [ ] Time tool renders timezone and formatted time
|
|
- [ ] Bash tool renders output in console block
|
|
- [ ] JavaScript REPL tool executes code
|
|
- [ ] Web search tool fetches results
|
|
- [ ] Sleep tool delays execution
|
|
- [ ] Custom tool renderers can be registered
|
|
- [ ] Unknown tools use default renderer
|
|
- [ ] Tool debug view shows call args and results
|
|
|
|
### Phase 3: Transport Layer
|
|
- [ ] Proxy transport connects to server
|
|
- [ ] Proxy transport handles auth token
|
|
- [ ] Proxy transport streams messages
|
|
- [ ] Proxy transport reconstructs partial messages
|
|
- [ ] Proxy transport handles abort
|
|
- [ ] Proxy transport handles errors
|
|
- [ ] Direct transport uses API keys from KeyStore
|
|
- [ ] Direct transport calls provider APIs directly
|
|
- [ ] Direct transport handles missing API key
|
|
- [ ] Direct transport streams messages
|
|
- [ ] Direct transport handles abort
|
|
- [ ] Transport mode can be switched
|
|
- [ ] Proxy URL can be configured
|
|
|
|
### Phase 4: State Management
|
|
- [ ] AgentSession manages conversation state
|
|
- [ ] AgentSession sends messages
|
|
- [ ] AgentSession receives streaming updates
|
|
- [ ] AgentSession handles tool execution
|
|
- [ ] AgentSession handles errors
|
|
- [ ] AgentSession can be aborted
|
|
- [ ] AgentSession persists state
|
|
- [ ] AgentSession supports multiple sessions
|
|
- [ ] System prompt can be set
|
|
- [ ] Model can be selected
|
|
- [ ] Thinking level can be adjusted
|
|
- [ ] Tools can be configured
|
|
- [ ] Usage stats are tracked
|
|
|
|
### Phase 5: Main Interface
|
|
- [ ] AgentInterface displays messages
|
|
- [ ] AgentInterface handles scrolling
|
|
- [ ] AgentInterface enables auto-scroll
|
|
- [ ] AgentInterface shows usage stats
|
|
- [ ] AgentInterface integrates MessageEditor
|
|
- [ ] AgentInterface integrates ModelSelector
|
|
- [ ] AgentInterface shows thinking toggle
|
|
- [ ] Settings dialog opens
|
|
- [ ] Settings dialog saves transport mode
|
|
- [ ] Settings dialog saves proxy URL
|
|
- [ ] Settings dialog opens API keys dialog
|
|
- [ ] Header settings button works
|
|
|
|
### Phase 6: Optional Features
|
|
- [ ] Debug view shows request details
|
|
- [ ] Debug view shows response details
|
|
- [ ] Debug view shows timing info
|
|
- [ ] Debug view formats ChatML
|
|
- [ ] Sessions can be saved
|
|
- [ ] Sessions can be loaded
|
|
- [ ] Sessions can be listed
|
|
- [ ] Sessions can be deleted
|
|
- [ ] System prompt can be edited
|
|
|
|
---
|
|
|
|
## Dependencies to Install
|
|
|
|
```bash
|
|
# If not already installed
|
|
npm install highlight.js # For DebugView (optional)
|
|
```
|
|
|
|
---
|
|
|
|
## Estimated Complexity
|
|
|
|
| Phase | Files | LOC (approx) | Complexity | Time Estimate |
|
|
|-------|-------|--------------|------------|---------------|
|
|
| Phase 1 | 5 | ~800 | Medium | 4-6 hours |
|
|
| Phase 2 | 10 | ~400 | Low-Medium | 3-4 hours |
|
|
| Phase 3 | 6 | ~600 | High | 6-8 hours |
|
|
| Phase 4 | 5 | ~500 | Medium-High | 5-7 hours |
|
|
| Phase 5 | 4 | ~400 | Medium | 4-5 hours |
|
|
| Phase 6 | 3 | ~400 | Low | 2-3 hours |
|
|
| **TOTAL** | **33** | **~3100** | - | **24-33 hours** |
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
The port is complete when:
|
|
|
|
1. ✅ User can send messages with text and attachments
|
|
2. ✅ Messages stream in real-time with proper rendering
|
|
3. ✅ Tool calls execute and display results
|
|
4. ✅ Both direct and proxy transports work
|
|
5. ✅ Settings can be configured and persisted
|
|
6. ✅ Usage stats are tracked and displayed
|
|
7. ✅ Extension works in both Chrome and Firefox
|
|
8. ✅ All TypeScript types compile without errors
|
|
9. ✅ No console errors in normal operation
|
|
10. ✅ UI is responsive and performs well
|
|
|
|
---
|
|
|
|
## Notes for New Session
|
|
|
|
If starting a new session, key context:
|
|
|
|
1. **Extension structure**: Browser extension in `packages/browser-extension/`
|
|
2. **Source codebase**: `genai-workshop-new/src/app/`
|
|
3. **UI framework**: LitElement with `@mariozechner/mini-lit` package
|
|
4. **AI package**: `@mariozechner/pi-ai` for LLM interactions
|
|
5. **Icons**: Using lucide instead of custom icon set
|
|
6. **i18n**: All UI strings must be in i18n.ts (English + German)
|
|
7. **Storage**: chrome.storage.local for all persistence
|
|
8. **TypeScript**: `useDefineForClassFields: false` required
|
|
9. **Custom elements**: Must use registration guards
|
|
10. **Build**: `npm run build:chrome` and `npm run build:firefox`
|
|
|
|
**Critical files to reference**:
|
|
- `packages/browser-extension/tsconfig.json` - TS config
|
|
- `packages/browser-extension/src/utils/i18n.ts` - i18n strings
|
|
- `packages/browser-extension/src/state/KeyStore.ts` - API key storage
|
|
- `packages/browser-extension/src/dialogs/ApiKeysDialog.ts` - API key UI
|
|
- `genai-workshop-new/src/app/AgentInterface.ts` - Reference implementation
|
|
- `genai-workshop-new/src/app/state/agent-session.ts` - State management reference
|
|
|
|
**Key architectural decisions**:
|
|
- Single AgentSession per chat
|
|
- Transport is pluggable (direct or proxy)
|
|
- Tools are registered in a global registry
|
|
- Message rendering is separated: stable (MessageList) vs streaming (StreamingMessageContainer)
|
|
- All components use light DOM (`createRenderRoot() { return this; }`)
|