Issue:
Each browser_javascript execution wrapped console methods, but captured
the current (already wrapped) console as "original". This created a chain
of wrappers that accumulated across executions:
- Execution 1: 1x console.log (wrapper1 → real console)
- Execution 2: 2x console.log (wrapper2 → wrapper1 → real console)
- Execution 3: 3x console.log (wrapper3 → wrapper2 → wrapper1 → real console)
- Execution 4: 4x console.log (and so on...)
Fix:
Store the truly original console methods in window.__originalConsole on
first wrap only. All subsequent executions use these stored original methods
instead of capturing the current console. This prevents wrapper accumulation.
Changes:
- Check if window.__originalConsole exists before wrapping
- Store original console methods with .bind() to preserve context
- Always use window.__originalConsole for local logging
- Now each execution logs exactly 1x regardless of execution count
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.
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
- 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
- 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.