Replace window.sendRuntimeMessage existence check with URL-based detection.
The sendRuntimeMessage function exists in both extension and non-extension
contexts (e.g., downloaded HTML artifacts), causing incorrect behavior.
Changes:
- Add window.__isExtensionContext() helper to RuntimeMessageBridge that checks:
- chrome-extension:// URLs (Chrome)
- moz-extension:// URLs (Firefox)
- about:srcdoc (sandbox iframes)
- Update all runtime providers to use __isExtensionContext() instead:
- FileDownloadRuntimeProvider: Correctly falls back to browser download
- ConsoleRuntimeProvider: Only sends messages in extension context
- ArtifactsRuntimeProvider: Properly detects offline/read-only mode
This fixes the issue where downloaded HTML artifacts incorrectly try to
communicate with the extension when opened from disk.
- Add getAllFromIndex to StorageBackend interface for index-based queries
- Implement getAllFromIndex in IndexedDBStorageBackend using cursor API
- Update SessionsStore.getAllMetadata to use lastModified index
- Enables database-native sorting instead of fetching all keys and sorting in JS
Benefits:
- Much faster for large datasets (uses IndexedDB cursor with direction)
- Reduces memory usage (no need to load all keys first)
- Leverages existing indices for optimal performance
- Changed ToolRenderer return type from TemplateResult to ToolRenderResult
- ToolRenderResult = { content: TemplateResult, isCustom: boolean }
- isCustom: true = no card wrapper, false = wrap in card
- Updated all existing tool renderers to return new format
- Updated Messages.ts to handle custom rendering
This enables tools to render without default card chrome when needed.
Make these properties public so they can be accessed externally for
test automation and other programmatic control.
Changes:
- Change agent, agentInterface, artifactsPanel from private to public
Clean up and restructure JavaScript REPL description following consistent pattern:
- Purpose section
- When to Use section
- Environment section
- Common Libraries section
- Important Notes section
- Example section
Removed buildJavaScriptReplDescription() function - no longer dynamically
injecting runtime provider docs into tool description (will move to system prompt).
Changes:
- Replace JAVASCRIPT_REPL_BASE_DESCRIPTION with JAVASCRIPT_REPL_DESCRIPTION
- Remove JAVASCRIPT_REPL_CHART_EXAMPLE and JAVASCRIPT_REPL_FOOTER
- Remove buildJavaScriptReplDescription() function
- Update javascript-repl tool to use static description
- Simpler, more scannable structure with clear hierarchy
Add clear decision tree and anti-patterns to prevent LLMs from using
get + rewrite when they should use update for targeted edits.
Changes:
- Add CRITICAL workflow section at top with decision tree
- Emphasize 'update' as PREFERRED for edits (token efficient)
- Mark 'rewrite' as LAST RESORT only
- Add ANTI-PATTERNS section showing wrong approaches
- Clarify use cases for each command
- Add examples emphasizing surgical modifications
Make it clear that runtime functions are ONLY for programmatically generated
content (by code), not for content the LLM authors directly.
Changes:
- Add WHEN TO USE and DO NOT USE sections to runtime provider description
- Emphasize that createOrUpdateArtifact is for code-generated content
- Point users to artifacts tool for LLM-authored content like summaries
LLMs don't need to check existence - they can just list all artifacts.
Simpler API that returns all filenames at once.
Changes:
- Replace hasArtifact(filename) with listArtifacts() returning string[]
- Add 'list' action handler that returns all artifact keys
- Update examples in prompt to use listArtifacts()
LLMs get confused about when to use create vs update. The single function
automatically detects if the artifact exists and chooses the right operation.
Changes:
- Replace createArtifact/updateArtifact with createOrUpdateArtifact in runtime
- Update handler to check existence and use appropriate command (create/rewrite)
- Simplify prompt documentation and examples
When abort happens during tool call streaming, the tool result should show as aborted.
Previously used global isStreaming state which would flip when new messages streamed in,
causing spinner to reappear incorrectly.
Changes:
- Use message.stopReason === "aborted" to detect aborted tool calls
- Create synthetic error result for aborted tool calls in ToolMessage component
- Fix Ollama provider key test to return true (can't know which model to test)
- Add newline before HTML execution logs in artifacts update
- Inject <script>if (window.complete) window.complete();</script> before </html>
- HTML artifacts are long-running and don't time out
- Console logs are sent immediately, completion signals when page is ready
- Removes need for artificial timeout in HTML artifacts
- Take artifactsPanel and agent directly instead of 5 separate function parameters
- Define minimal ArtifactsPanelLike and AgentLike interfaces to avoid circular deps
- Update all call sites (ChatPanel, browser-javascript) to use simplified constructor
- Much cleaner and easier to use
- Throw error instead of returning output when result.success is false
- Ensures tool call is marked as failed when code execution fails or is aborted
- Matches browser-javascript error handling pattern
Console Logging Improvements:
- Changed ConsoleRuntimeProvider to send logs immediately instead of batching
- Track pending send promises and await them in onCompleted callback
- Ensures REPL gets all logs before execution-complete
- Enables real-time console logging for HTML artifacts
Message Routing Fixes:
- Remove "handled" concept from message routing - broadcast all messages to all providers/consumers
- Change handleMessage return type from Promise<boolean> to Promise<void>
- Add debug logging to RuntimeMessageRouter to trace message flow
- Fix duplicate error logging (window error handler now only tracks errors, doesn't log them)
Output Formatting Consistency:
- Remove [LOG], [ERROR] prefixes from console output in both tools
- Show console logs before error messages
- Use "=> value" format for return values in both javascript-repl and browser-javascript
- Remove duplicate "Error:" prefix and extra formatting
Bug Fixes:
- Fix race condition where execution-complete arrived before console logs
- Fix ConsoleRuntimeProvider blocking execution-complete from reaching consumers
- Remove duplicate console log collection from SandboxedIframe
- Fix return value capture by wrapping user code in async function
- console.log('Reporting execution error:', finalError) was logging the error
- This caused duplicate error message in output
- Removed all debug console.log statements from window.complete()
- Error is only shown via execution-error message now
- javascript-repl now displays return values with => prefix
- Objects are JSON.stringify'd with formatting
- Removed console.error from wrapper to prevent duplicate error messages
- Error is already captured and sent via execution-error message
- Fixes missing return value display and duplicate error output
- Return value now passed to window.complete(error, returnValue)
- execution-complete message includes returnValue field
- SandboxResult interface updated to include returnValue
- executionConsumer passes returnValue in resolved promise
- Return values properly captured and available to callers
- User code with return statement was exiting the async IIFE early
- Completion callbacks and window.complete() were never reached
- Now wrap user code in userCodeFunc to capture return value
- Return statement returns from userCodeFunc, not outer IIFE
- Completion callbacks and window.complete() always execute
- Return value is logged to console output
- Fixes 30-second timeout when using return statements in REPL
- Router was stopping propagation after first handler returned true
- This prevented consumers from seeing messages that providers handled
- executionConsumer never received execution-complete because ConsoleRuntimeProvider handled it first
- Now all providers and consumers receive all messages
- Fixes javascript_repl never completing
- ConsoleRuntimeProvider was handling execution-complete and returning true
- This stopped message propagation before executionConsumer could handle it
- ExecutionConsumer never got execution-complete, so promise never resolved
- Now ConsoleRuntimeProvider responds but returns false to allow propagation
- Fixes javascript_repl never completing (30s timeout)
- window.complete() was fire-and-forget, causing execution-complete to arrive before console messages
- This caused sandbox to unregister before console message responses arrived
- Made complete() async and await sendRuntimeMessage()
- SandboxedIframe wrapper now awaits window.complete()
- Ensures all messages are processed before cleanup/unregister
- Fixes 30-second timeout on javascript_repl
- ConsoleRuntimeProvider.handleMessage() was not calling respond()
- This caused sendRuntimeMessage() to hang waiting for response (30s timeout)
- Now properly acknowledges console, execution-complete, and execution-error messages
- Fixes javascript_repl hanging on simple console.log() calls
- Remove fallback timeout from ConsoleRuntimeProvider (was causing 2s delays)
- Add completion callback support to SandboxedIframe REPL wrapper
- Call completion callbacks before window.complete() in both success/error paths
- Both browser-javascript and javascript-repl now use identical completion pattern
- Ensures console logs are batched and sent before execution completes
Major refactoring to unify runtime providers across sandbox and user script contexts:
1. Runtime Bridge & Router
- Add RuntimeMessageBridge for unified messaging abstraction
- Rename SandboxMessageRouter → RuntimeMessageRouter
- Router now handles both iframe and user script messages
- Guard for non-extension environments
2. Provider Refactoring
- ArtifactsRuntimeProvider: Add offline mode with snapshot fallback
- AttachmentsRuntimeProvider: Remove returnDownloadableFile (moved to dedicated provider)
- ConsoleRuntimeProvider: Add message collection, remove lifecycle logic
- FileDownloadRuntimeProvider: New provider for file downloads
3. HTML Escaping Fix
- Escape </script> in JSON.stringify output to prevent premature tag closure
- Applies when injecting provider data into <script> tags
- JavaScript engine automatically unescapes, no runtime changes needed
4. Function Renaming
- listFiles → listAttachments
- readTextFile → readTextAttachment
- readBinaryFile → readBinaryAttachment
- returnFile → returnDownloadableFile
5. Updated Exports
- Export new RuntimeMessageBridge and RuntimeMessageRouter
- Export FileDownloadRuntimeProvider
- Update all cross-references
This sets the foundation for reusing providers in browser-javascript tool.
- Check if store has keyPath before calling put()
- If keyPath exists (in-line keys), only pass value: store.put(value)
- If no keyPath (out-of-line keys), pass both: store.put(value, key)
- Apply fix to both set() and transaction.set()
- Fixes DataError when saving sessions with keyPath: 'id'
- Create base Store class with private backend and protected getBackend()
- Add SettingsStore, ProviderKeysStore, SessionsStore
- Each store defines its own schema via getConfig()
- AppStorage now takes stores + backend in constructor
- Remove SessionsRepository (logic moved to SessionsStore)
- Update all consumers to use store API (storage.settings.get/set, storage.providerKeys.get/set)
- Update example app to follow new pattern: create stores, gather configs, create backend, wire
- Benefits: stores own their schema, no circular deps, cleaner separation
- Replace fragmented storage backends with single IndexedDBStorageBackend
- Create multi-store StorageBackend interface (storeName parameter)
- Remove old backends: IndexedDBBackend, LocalStorageBackend, SessionIndexedDBBackend, WebExtensionStorageBackend
- Remove old repositories: ProviderKeysRepository, SessionRepository, SettingsRepository
- Simplify AppStorage to directly expose storage methods (getSetting/setSetting, getProviderKey/setProviderKey)
- Create SessionsRepository for session-specific operations
- Update all consumers to use new simplified API
- Update example app to use new storage architecture
- Benefits: 10GB+ quota (vs 10MB chrome.storage), single database, consistent API
- Update renderHeader and renderCollapsibleHeader in renderer-registry.ts to accept `text: string | TemplateResult`
- Remove duplicated renderCollapsibleHeaderWithPill helper in artifacts-tool-renderer.ts
- Update all artifact renderer calls to use renderHeaderWithPill() inline
- Remove all separate pill rendering below headers
This allows artifact pills to be rendered inline with header text without code duplication.
- Create ArtifactPill component (similar to SkillPill)
- Renders filename as clickable pill with FileCode2 icon
- Clicking pill opens artifacts panel and selects that artifact
- Update ArtifactsToolRenderer to accept artifactsPanel reference
- Pass artifactsPanel from ChatPanel to renderer on initialization
- Display artifact pill below header for all commands
- Pill only clickable when artifactsPanel reference is available
- All actions except DELETE now use collapsible headers
- CREATE/UPDATE/REWRITE/GET/LOGS: code/output collapsed by default
- DELETE: keeps simple non-collapsible header
- Fix isStreaming parameter usage for proper spinner state
- Add smooth 300ms animation on expand/collapse
- Full header is clickable to toggle collapse state
- Add renderCollapsibleHeader() to renderer-registry
- Places chevron on right, spinner on left
- Toggles between ChevronRight (collapsed) and ChevronDown (expanded)
- Uses max-h-0/max-h-[2000px] with transition-all for smooth animation
- Dynamically adds/removes mt-3 to avoid margin when collapsed
- Update javascript-repl renderer to use collapsible sections
- Code and console output hidden by default
- Only file attachments remain visible
- 300ms smooth animation on expand/collapse
- Export renderCollapsibleHeader from web-ui index
- Extract ArtifactsToolRenderer from ArtifactsPanel into standalone renderer
- Fix ChatPanel to register ArtifactsToolRenderer instead of panel
- Implement command-specific rendering logic (create/update/rewrite/get/logs/delete)
- Create reusable Console component with copy button and autoscroll toggle
- Replace custom console implementation with ExpandableSection and Console
- Fix Lit reactivity for HtmlArtifact logs using spread operator
- Add Lucide icons (FileCode2, ChevronsDown, Lock) for UI consistency
- Follow skill.ts patterns with renderHeader and state handling
- Add i18n strings for all artifact actions and console features
- Rename chrome-storage-backend.ts to web-extension-storage-backend.ts
- Update to use globalThis.browser || globalThis.chrome for Firefox/Chrome compatibility
- Export both names for backward compatibility
- Fix tsc --preserveWatchOutput in web-ui dev script to prevent console clearing
This fixes the "browser is not defined" error in Chrome extensions.
- Move mini-lit from dependencies to peerDependencies
- Keep in devDependencies for development
- Prevents bundlers from including mini-lit when consuming pi-web-ui
- Consumer (sitegeist) provides mini-lit, esbuild bundles it once
- Fixes duplicate mini-lit bundling issue permanently