33 KiB
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:
- Multiple transport options: Direct API calls OR proxy-based calls
- Full message rendering: Text, thinking blocks, tool calls, attachments, images
- Streaming support: Real-time message streaming with proper batching
- Tool execution and rendering: Extensible tool system with custom renderers
- Session management: State management with persistence
- 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
-
AgentInterface.ts (325 lines)
- Main chat interface container
- Manages scrolling, auto-scroll behavior
- Coordinates MessageList, StreamingMessageContainer, MessageEditor
- Displays usage stats
- Handles session lifecycle
-
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
-
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
-
StreamingMessageContainer.ts (95 lines)
- Manages streaming message updates
- Batches updates using
requestAnimationFramefor performance - Shows loading indicator during streaming
- Handles immediate updates for clearing
-
ConsoleBlock.ts (62 lines)
- Console-style output display
- Auto-scrolling to bottom
- Copy button functionality
- Used by tool renderers
State Management
-
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 messagesetModel(),setSystemPrompt(),setThinkingLevel(),setTools()appendMessage(),replaceMessages(),clearMessages()abort()- Cancel ongoing requestsubscribe(fn)- Listen to state changes
-
state/session-store.ts (needs investigation)
- Session persistence to IndexedDB
- Load/save conversation history
- Multiple session management
Transport Layer
-
state/transports/types.ts (17 lines)
AgentTransportinterfaceAgentRunConfiginterface- Defines contract for transport implementations
-
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()
-
NEW: state/transports/direct-transport.ts (needs creation)
- DirectTransport class
- Calls provider APIs directly using API keys from KeyStore
- Uses
@mariozechner/pi-ai'sagentLoop()directly - No auth token needed
-
utils/proxy-client.ts (285 lines)
streamSimpleProxy()function- Fetches from
/api/streamendpoint - 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
-
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
-
tools/index.ts (40 lines)
- Exports tool functions from
@mariozechner/pi-ai - Registers default tool renderers
- Exports
renderToolParams()andrenderToolResult() - Re-exports tool implementations
- Exports tool functions from
-
tools/types.ts (needs investigation)
ToolRendererinterface- Contracts for custom tool renderers
-
tools/renderer-registry.ts (19 lines)
- Global registry:
Map<string, ToolRenderer> registerToolRenderer(name, renderer)functiongetToolRenderer(name)function
- Global registry:
-
tools/renderers/DefaultRenderer.ts (1162 chars)
- Fallback renderer for unknown tools
- Renders params as JSON
- Renders results as JSON or text
-
tools/renderers/CalculateRenderer.ts (1677 chars)
- Custom renderer for calculate tool
- Shows expression and result
-
tools/renderers/GetCurrentTimeRenderer.ts (1328 chars)
- Custom renderer for time tool
- Shows timezone and formatted time
-
tools/renderers/BashRenderer.ts (1500 chars)
- Custom renderer for bash tool
- Uses ConsoleBlock for output
-
tools/javascript-repl.ts (needs investigation)
- JavaScript REPL tool implementation
- May need adaptation for browser environment
-
tools/web-search.ts (needs investigation)
- Web search tool implementation
- Check if compatible with browser extension
-
tools/sleep.ts (needs investigation)
- Simple sleep/delay tool
Utilities
-
utils/format.ts (needs investigation)
formatUsage()- Format token usage and costs- Other formatting utilities
-
utils/auth-token.ts (21 lines)
getAuthToken()- Prompt for proxy auth tokenclearAuthToken()- Remove from storage- Uses PromptDialog for input
-
dialogs/PromptDialog.ts (needs investigation)
- Simple text input dialog
- Used for auth token entry
Debug/Development
- 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:
- Copy
ConsoleBlock.tsto browser extension - Update imports to use
@mariozechner/mini-lit - Replace icon imports with lucide icons:
iconCheckLine→CheckiconFileCopy2Line→Copy
- 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:
- Copy
Messages.tsto browser extension - Update imports:
Buttonfrom@mariozechner/mini-litformatUsagefrom utils- Icons from lucide (ToolsLine, Loader4Line, BugLine)
- Add new type:
AppMessage(already have partial in extension) - Components to register:
user-messageassistant-messagetool-messagetool-message-debugaborted-message
- Update i18n strings:
- "Error:", "Request aborted", "Call", "Result", "(no result)", "Waiting for tool result…", "Call was aborted; no result."
- 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:
- Copy
MessageList.tsto browser extension - Update imports
- Uses
repeat()directive from lit - ensure it's available - Register
message-listelement 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:
- Copy
StreamingMessageContainer.tsto browser extension - Update imports
- Register
streaming-message-containerelement with guard - 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:
- Read
tools/types.tsto understandToolRendererinterface - Copy both files to
src/tools/ - Create registry as singleton
Verification: Can register and retrieve renderers
Task 2.2: Port Tool Renderers
Files: All src/tools/renderers/*.ts
Actions:
- Copy
DefaultRenderer.ts - Copy
CalculateRenderer.ts - Copy
GetCurrentTimeRenderer.ts - Copy
BashRenderer.ts - Update all to use
@mariozechner/mini-litand lucide icons - 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:
- Read each file to assess browser compatibility
- Port
sleep.ts(should be trivial) - Port
javascript-repl.ts- may neednew Function()or eval - Port
web-search.ts- check if it uses fetch or needs adaptation - Update
tools/index.tsto 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:
- Copy file to
src/state/transports/ - 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:
- Copy
proxy-client.tstosrc/utils/ - Update
streamSimpleProxy()to use configurable proxy URL - Read proxy URL from config (default: user-configurable)
- Update error messages for i18n
- 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:
- Copy file to
src/state/transports/ - Rename
LocalTransporttoProxyTransportfor clarity - Update to use
streamSimpleProxyfrom proxy-client - 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:
- Create new
DirectTransportclass implementingAgentTransport - Use
agentLoop()from@mariozechner/pi-aidirectly - Integrate with KeyStore to get API keys per provider
- Pass API key in options to
agentLoop() - Handle
no-api-keyerrors by triggering ApiKeysDialog
Example Implementation:
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:
- Create transport mode storage: "direct" | "proxy"
- Create proxy URL storage with default
- Create getters/setters:
getTransportMode()/setTransportMode()getProxyUrl()/setProxyUrl()
- Store in chrome.storage.local
Example:
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:
- Read file to identify all formatting functions
- Copy
formatUsage()function - Copy any other utilities needed by AgentSession
- 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:
- Copy file to
src/utils/ - Update to use chrome.storage.local
- 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:
- Read genai-workshop-new version
- Adapt to use
@mariozechner/mini-litDialog - Create simple input dialog similar to ApiKeysDialog
- 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:
- Copy
agent-session.tstosrc/state/ - Update imports:
- ProxyTransport from
./transports/proxy-transport.js - DirectTransport from
./transports/direct-transport.js - Types from pi-ai
- KeyStore, auth-token utils
- ProxyTransport from
- Modify constructor to accept transport mode
- Create transport based on mode:
- "proxy" → ProxyTransport
- "direct" → DirectTransport
- Update to use chrome.storage for persistence
- Add
ThinkingLeveltype - Add
AppMessagetype extension for attachments
Key modifications:
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:
- Copy
AgentInterface.tstosrc/ - Update all imports to use ported components
- Register
agent-interfacecustom element with guard - Update icons to lucide
- Add i18n strings:
- "No session available", "No session set", "Hide debug view", "Show debug view"
- Properties:
session(external AgentSession)enableAttachmentsenableModelSelectorenableThinkingshowThemeToggleshowDebugToggle
- 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:
- Remove current chat implementation
- Create AgentSession instance
- Render
<agent-interface>with session - Configure:
enableAttachments={true}enableModelSelector={true}enableThinking={true}showThemeToggle={false}(already in header)showDebugToggle={false}(optional)
- Remove old MessageEditor integration (now inside AgentInterface)
- Set system prompt (optional)
- Set default tools (optional - calculateTool, getCurrentTimeTool)
Example:
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:
- Create dialog extending DialogBase
- Sections:
- Transport Mode: Radio buttons for "Direct" | "Proxy"
- Proxy URL: Input field (only shown if proxy mode)
- API Keys: Button to open ApiKeysDialog
- 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:
- Change settings button to open SettingsDialog (not ApiKeysDialog directly)
- 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:
- Read full file to understand functionality
- Copy to
src/ - Update imports
- Format ChatML, SSE events, timing info
- 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:
- Read file to understand IndexedDB usage
- Create IndexedDB schema for sessions
- Implement save/load/list/delete operations
- Add to AgentInterface or ChatPanel
- Add UI for switching sessions
Verification: Can save and load conversation history
Task 6.3: Add System Prompt Editor (Optional)
Actions:
- Create dialog or expandable textarea
- Allow editing session.state.systemPrompt
- 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:
// 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:
import { Button } from "./mini/Button.js";
import { iconLoader4Line } from "./mini/icons.js";
To browser extension:
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.localfetch("/api/...")→fetch(proxyUrl + "/api/...")- No direct filesystem access
5. Transport Mode Configuration
Ensure AgentSession can be created with either transport:
// 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:
// 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:
- Run
npm run check- TypeScript compilation - Run
npm run build:chrome- Chrome extension build - Run
npm run build:firefox- Firefox extension build - Load extension in browser and test functionality
- Check console for errors
9. Proxy URL Configuration
Default proxy URL should be configurable but default to:
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
# 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:
- ✅ User can send messages with text and attachments
- ✅ Messages stream in real-time with proper rendering
- ✅ Tool calls execute and display results
- ✅ Both direct and proxy transports work
- ✅ Settings can be configured and persisted
- ✅ Usage stats are tracked and displayed
- ✅ Extension works in both Chrome and Firefox
- ✅ All TypeScript types compile without errors
- ✅ No console errors in normal operation
- ✅ UI is responsive and performs well
Notes for New Session
If starting a new session, key context:
- Extension structure: Browser extension in
packages/browser-extension/ - Source codebase:
genai-workshop-new/src/app/ - UI framework: LitElement with
@mariozechner/mini-litpackage - AI package:
@mariozechner/pi-aifor LLM interactions - Icons: Using lucide instead of custom icon set
- i18n: All UI strings must be in i18n.ts (English + German)
- Storage: chrome.storage.local for all persistence
- TypeScript:
useDefineForClassFields: falserequired - Custom elements: Must use registration guards
- Build:
npm run build:chromeandnpm run build:firefox
Critical files to reference:
packages/browser-extension/tsconfig.json- TS configpackages/browser-extension/src/utils/i18n.ts- i18n stringspackages/browser-extension/src/state/KeyStore.ts- API key storagepackages/browser-extension/src/dialogs/ApiKeysDialog.ts- API key UIgenai-workshop-new/src/app/AgentInterface.ts- Reference implementationgenai-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; })