-
Notifications
You must be signed in to change notification settings - Fork 4
Description
[RFC] Bug fixes + features — WSL support, multi-panel routing, model selector, stop button
👋 Introduction
Hi! I'm @svg153, a software engineer who found this project while looking for a way to use GitHub Copilot directly from the browser side panel without switching context to the terminal.
I've been using the extension in a WSL + Windows setup (WSL runs the native messaging host and Node.js, the browser runs on Windows) and quickly ran into several bugs that prevented it from working at all. I started fixing them one by one and ended up doing a broader pass across the codebase.
All the work has been done on a fork of this repo: svg153/github-copilot-browser.
Note on tooling: The fixes and features described here were implemented using GitHub Copilot CLI under my direct supervision, with manual testing of every change in my WSL+Windows environment. The separation of the full changes into individual focused PRs was also automated by Copilot CLI. I've reviewed every diff and verified that the isolated branches are clean and correct.
🧪 Full integration test
All changes have been tested together in this PR on my fork:
If you want to verify everything works end-to-end before considering individual PRs, that's the place to look.
📦 Individual PRs — one change per PR
I've also split every change into isolated, focused PRs targeting main on my fork, so you can cherry-pick exactly what you want:
🔍 Why each change was needed
🐛 PR-9 fix/tool-permission-approve-all — Critical
Symptom: Every tool call fails silently. The LLM attempts to use a browser tool (e.g. get_page_content) and receives "denied-no-approval-rule-and-could-not-request-from-user" from the SDK.
Root cause: @github/copilot-sdk's createSession() defaults to denying all tool permission requests when no permissionHandler is provided.
Fix: Import approveAll from @github/copilot-sdk and pass it as onPermissionRequest: approveAll to createSession(). One line of code, but without it the extension is completely non-functional.
// Before
session = await client.createSession({ tools: browserTools, ... });
// After
import { approveAll } from '@github/copilot-sdk';
session = await client.createSession({ tools: browserTools, onPermissionRequest: approveAll, ... });🐛 PR-2 fix/active-tab-side-panel — Critical
Symptom: All content-script tools (get_page_content, click_element, capture_screenshot, etc.) fail with "No active tab" when the side panel has keyboard focus.
Root cause: chrome.tabs.query({ active: true, currentWindow: true }) returns the extension's own side-panel window when the panel has focus — not the browser tab the user is looking at.
Fix: Use lastFocusedWindow: true instead of currentWindow: true, then filter out chrome-extension://, chrome://, and edge:// URLs. Add a fallback that searches all windows.
🐛 PR-6 fix/multi-panel-message-routing — High
Symptom: With multiple browser windows open, responses from one conversation appear in the wrong panel.
Root cause: sendToPanels() broadcasts every response to all connected panel ports.
Fix: Track activePort (the port that sent the last SEND_CHAT_MESSAGE) and route all chat/tool messages only to that port via sendToActivePanel(). Also fixes empty CHAT_RESPONSE payloads and silent tool execution failures.
🐛 PR-5 fix/native-messaging-reconnect — High
Symptom: When the native host crashes, the only recovery is reloading the extension.
Root cause: onDisconnect handler only set status to error — no reconnect was attempted.
Fix: Exponential backoff reconnect (5s → 10s → 20s → 30s). _userDisconnected flag prevents auto-reconnect when the user intentionally clicks "Disconnect".
✨ PR-10 feat/host-auto-discover-cli — High
Symptom: The host fails to start on any platform other than macOS Apple Silicon with a cryptic ENOENT error.
Root cause: Two hardcoded paths: the shebang #!/opt/homebrew/bin/node and cliPath: '/opt/homebrew/bin/copilot' in CopilotClient. Both only exist on macOS+Homebrew+Apple Silicon.
Fix:
- Change shebang to
#!/usr/bin/env node - Add
findCopilotCli()that checks common install locations across all platforms (macOS Homebrew, Linux system/user, Windows), then falls back towhich/where
🔧 PR-4 refactor/type-safe-native-protocol — Medium
Problem: NativeMessage had fictional variants (COPILOT_REQUEST, COPILOT_RESPONSE, COPILOT_STREAM) not present in the actual protocol. TypeScript provided zero type safety for the native messaging layer. Also: duplicate HOVER_ELEMENT in ContentScriptMessage.
Fix: Replace with HostOutboundMessage (SW→host) and HostInboundMessage (host→SW). NativeMessage kept as @deprecated alias.
🔧 PR-7 fix/type-isstreaming-chatmessage — Medium
Problem: ChatMessage lacked isStreaming, forcing unsafe casts like (msg as ChatMessage & { isStreaming?: boolean }).isStreaming in multiple places.
Fix: Add isStreaming?: boolean to ChatMessage in types.ts, remove all cast workarounds.
✨ PR-8 feat/stop-generation — Medium
Feature: A red stop button (■) replacing the send button while streaming. Calls session.abort(), finalizes the partial response without losing already-received text, resets loading state. Textarea disabled during loading to prevent double-sends.
✨ PR-3 feat/model-selector — Low
Feature: <select> dropdown in the header showing available models (fetched on connect via GET_MODELS), allowing mid-session model switching via SET_MODEL. Adds ModelInfo type and full message stack support.
🔀 Merge order and conflict map
Files touched by multiple PRs
| File | PRs that touch it |
|---|---|
host.mjs |
PR-9 · PR-10 · PR-8 · PR-3 |
service-worker.ts |
PR-6 · PR-8 · PR-3 |
App.tsx |
PR-7 · PR-8 · PR-3 |
messages.ts |
PR-4 · PR-3 |
types.ts |
PR-7 · PR-3 |
Dependency & merge-order graph
flowchart TD
subgraph W1["🌊 Wave 1 — fully independent, merge in any order"]
PR9("[PR-9]<br/>fix: tool permission<br/>host.mjs")
PR2("[PR-2]<br/>fix: active tab<br/>tab-manager.ts")
PR5("[PR-5]<br/>fix: reconnect<br/>native-messaging.ts")
PR10("[PR-10]<br/>feat: auto-discover CLI<br/>host.mjs")
end
subgraph W2["🌊 Wave 2 — type foundations"]
PR4("[PR-4]<br/>refactor: protocol types<br/>messages.ts")
PR7("[PR-7]<br/>fix: isStreaming type<br/>types.ts · App.tsx")
end
subgraph W3["🌊 Wave 3 — routing fix"]
PR6("[PR-6]<br/>fix: multi-panel routing<br/>service-worker.ts")
end
subgraph W4["🌊 Wave 4 — needs Wave 2"]
PR8("[PR-8]<br/>feat: stop button<br/>host.mjs · service-worker.ts · App.tsx")
end
subgraph W5["🌊 Wave 5 — needs everything"]
PR3("[PR-3]<br/>feat: model selector<br/>7 files")
end
W1 --> W3
PR7 -->|"needs isStreaming"| PR8
W2 --> PR3
W3 --> PR3
PR8 --> PR3
style PR9 fill:#d73a49,color:#fff
style PR2 fill:#d73a49,color:#fff
style PR5 fill:#e36209,color:#fff
style PR10 fill:#e36209,color:#fff
style PR4 fill:#6f42c1,color:#fff
style PR7 fill:#6f42c1,color:#fff
style PR6 fill:#e36209,color:#fff
style PR8 fill:#0366d6,color:#fff
style PR3 fill:#0366d6,color:#fff
Legend: 🔴 Critical bug fix · 🟠 High priority · 🟣 Refactor / type fix · 🔵 Feature
⚠️ PRs with file conflicts (same file, different content)
If applied out of order these will need manual conflict resolution:
PR-9, PR-2, PR-5 and PR-10 each touch a unique file — they can all be merged simultaneously with zero conflicts.
💬 What I'd suggest
If I had to pick the most impactful subset to merge first:
- PR-9 — Without this, no browser tool works at all. One-line fix.
- PR-2 — Without this, all content-script tools fail when the panel has focus.
- PR-10 — Without this, the host only starts on one specific platform.
- PR-5 — The extension recovers automatically from host crashes.
- PR-6 — Multi-window users see wrong responses without this.
The refactors (PR-4, PR-7) and features (PR-8, PR-3) are nice-to-have and can be discussed separately.
If any of this is interesting to you, I'm happy to:
- Open PRs directly on this repo following your contribution guidelines
- Adjust the scope or implementation of any change
- Add tests if you have a test setup I should follow
- Break things down further or combine them differently
Thanks for building this! 🙏