Skip to content

feat: launch Copilot in remote mode with QR code sharing - #197

Draft
catatafishen wants to merge 11 commits into
masterfrom
feat/copilot-remote-mode
Draft

feat: launch Copilot in remote mode with QR code sharing#197
catatafishen wants to merge 11 commits into
masterfrom
feat/copilot-remote-mode

Conversation

@catatafishen

@catatafishen catatafishen commented Apr 13, 2026

Copy link
Copy Markdown
Owner

Summary

Adds support for GitHub Copilot CLI remote control mode (--remote flag), enabling users to share their Copilot session via a QR code or link that can be opened on GitHub web or mobile.

See the GitHub changelog for the underlying feature.

Changes

CopilotClient

  • setRemoteMode(boolean) and setRemoteUrlListener(Consumer<String>) methods
  • buildCommand() inserts --remote at argv[1] when enabled
  • registerHandlers() override scans stderr for GitHub URLs (ANSI-stripped, fire-once semantics)

ActiveAgentManager

  • setRemoteMode(), isRemoteMode(), and setRemoteUrlListener() public API
  • Threads remote mode and listener into CopilotClient inside start()

AcpConnectPanel

  • Adds a "Remote control (share session)" checkbox — only visible when the Copilot profile is selected
  • Wires checkbox state to agentManager.setRemoteMode()

ChatToolWindowContent

  • Registers the remote URL listener before loadModelsAsync() so it is attached before acpClient.start() fires
  • showRemoteSessionUrl() opens the dialog on the EDT

RemoteSessionPanel (new)

  • Non-modal DialogWrapper showing:
    • 200×200 QR code (rendered via existing QrCodePanel)
    • URL label
    • "Copy link" button
    • "Open in browser" button (disabled if Desktop not supported)

Testing

  • Enable Copilot profile in connect panel
  • Check "Remote control (share session)" checkbox
  • Click Connect — QR code dialog should appear once the CLI prints the session URL

⚠️ Known Limitation: Not Working in ACP Mode

The --remote flag is currently not functional when the Copilot CLI runs in ACP (Agent-Computer Protocol) mode. In interactive terminal mode the remote session URL is printed to stderr and can be captured by the plugin. In ACP mode the URL is not emitted, so the QR dialog is never triggered.


📋 Draft Support Ticket (not posted yet)

Title: [Feature Request] ACP mode: emit remote session URL for plugin integration

To: GitHub Copilot CLI team

The Copilot CLI --remote flag (public preview, Apr 2026) enables session sharing via a QR code/link. This works in interactive terminal mode, where the session URL is printed to stderr and can be captured by IDE plugins.

However, when running in ACP mode (--acp flag), the remote session URL is not emitted. This prevents IDE plugins (such as AgentBridge) from intercepting the URL and showing the QR code dialog to the user — even though the plugin is fully implemented to support this workflow (see this PR).

Request: Please emit the remote session URL in a machine-readable way when the CLI is started in ACP mode — for example, as a structured stderr event, a dedicated ACP protocol message, or a JSON line on stdout. This would allow ACP client plugins to surface the QR code and share-link dialog to the user.

@codecov

codecov Bot commented Apr 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.41096% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...tafishen/agentbridge/acp/client/CopilotClient.java 91.66% 4 Missing ⚠️
...ishen/agentbridge/services/ActiveAgentManager.java 87.50% 2 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@catatafishen
catatafishen force-pushed the feat/copilot-remote-mode branch from 5bd0d9d to d60cfa1 Compare April 14, 2026 07:56
@catatafishen
catatafishen force-pushed the feat/copilot-remote-mode branch from 355f187 to 3319d13 Compare April 14, 2026 19:59
@catatafishen
catatafishen force-pushed the feat/copilot-remote-mode branch from 9c11643 to 0f6446d Compare April 15, 2026 09:59
@catatafishen
catatafishen force-pushed the feat/copilot-remote-mode branch from 0f6446d to 29a1b48 Compare April 15, 2026 12:25
catatafishen and others added 10 commits April 17, 2026 08:01
Adds support for GitHub Copilot CLI remote control mode (`--remote` flag),
which lets users share their Copilot session via a QR code or link that
opens on GitHub web or mobile.

Changes:
- CopilotClient: adds `setRemoteMode(boolean)` and `setRemoteUrlListener()`
  methods; `buildCommand()` inserts `--remote` at position 1 when enabled;
  `registerHandlers()` override scans stderr for GitHub URLs (ANSI-stripped)
  and fires the listener once (fire-once semantics)
- ActiveAgentManager: adds `setRemoteMode()`, `isRemoteMode()`, and
  `setRemoteUrlListener()` public API; threads remote mode and listener into
  `CopilotClient` in `start()` via instanceof pattern match
- AcpConnectPanel: adds "Remote control (share session)" checkbox visible
  only when the Copilot profile is selected; wires checkbox to
  `agentManager.setRemoteMode()`
- ChatToolWindowContent: registers the remote URL listener before
  `loadModelsAsync()` so it is attached before `acpClient.start()` fires;
  `showRemoteSessionUrl()` shows the dialog on the EDT
- RemoteSessionPanel (new): non-modal `DialogWrapper` showing a 200×200 QR
  code, the URL label, a Copy link button, and an Open in browser button

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…active

When the "Remote control" checkbox is checked and the user clicks Connect,
register a URL listener before starting the agent so the GitHub remote
session URL emitted to stderr by the Copilot CLI is captured and displayed.

Adds StatusBanner.showRemoteSessionUrl(url) — an info banner with
"Copy URL" and "Open in Browser" actions, following the same pattern
as showDeviceCode.

Wires the listener in AcpConnectPanel.doConnect() when remote mode
is enabled, so the banner appears as soon as the CLI prints the URL.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The previous implementation had two bugs:

1. AcpConnectPanel registered a listener using its own statusBanner,
   which is hidden once the chat panel is shown after connecting.
   ChatToolWindowContent then overwrote that listener with a dialog
   (RemoteSessionPanel), so neither the banner nor the dialog appeared.

2. The Copilot CLI with --remote --acp --stdio emits no URL to stderr
   (confirmed by testing), so the URL-based approach can never fire.

Fix:
- Show "Remote mode active" info banner immediately in the onSuccess
  callback of loadModelsAsync when isRemoteMode is true. This uses the
  chat panel's statusBanner, which is the correct visible banner.
- Keep the URL listener in ChatToolWindowContent pointing at the
  statusBanner (not a dialog) so it works if the CLI ever provides a URL.
- Remove the listener from AcpConnectPanel.doConnect() — it used the
  wrong banner and was always overwritten anyway.
- Delete RemoteSessionPanel.kt — no longer referenced anywhere.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the CLI is launched with --remote and remote sessions are
disabled for the repository, the CLI prints an error to stderr
(in interactive mode). This change wires up detection and display:

- CopilotClient: add REMOTE_NOT_ENABLED_PATTERN and
  remoteErrorListener field + setRemoteErrorListener(). The
  stderr handler (already active when remoteMode=true) now also
  checks for the "not enabled" pattern via extractRemoteNotEnabledError()
  and fires the listener fire-once. Strips ANSI codes and leading
  CLI decoration chars (!, ►, ●) before matching.

- ActiveAgentManager: add pendingRemoteErrorListener field +
  setRemoteErrorListener(). configureCopilotClientForStart() now
  passes it to CopilotClient alongside the URL listener.

- ChatToolWindowContent: register the error listener in
  connectToAgent() when remote mode is active. On fire, shows
  statusBanner.showError(message) — replaces the "Remote mode
  active" info banner with a visible error.

- Tests: extractRemoteNotEnabledError unit tests (plain, bang
  prefix, ANSI, unrelated lines), stderr handler tests (fires
  once, URL does not fire error listener), ActiveAgentManager
  delegation tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ning

The "Remote control (share session)" checkbox is now in the Copilot
settings page (Settings → Agents → GitHub Copilot → Configuration)
instead of the connect panel. Key changes:

- CopilotClientConfigurable: accept Project (previously ignored),
  add remoteSessionCheckbox and a red warning label explaining that
  Copilot in ACP mode does not expose the session URL needed to
  connect remotely, making the feature non-functional until Copilot
  adds ACP support. apply()/reset()/isModified() wired to
  ActiveAgentManager.setRemoteMode()/isRemoteMode().

- ActiveAgentManager: setRemoteMode() now persists the value via
  PropertiesComponent (key copilot.remoteMode) so the setting
  survives IDE restarts. KEY_REMOTE_MODE added alongside KEY_AUTO_CONNECT.
  Constructor already loaded from PropertiesComponent.

- AcpConnectPanel: checkbox and all its visibility/reset logic was
  already removed in the previous session.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tionFactory

- Remove `final` from `CopilotClient` so tests can subclass it
- Add `propertiesProvider` Supplier to `ActiveAgentManager` for constructor-free
  injection of `PropertiesComponent` in tests (avoids IntelliJ platform dependency)
- Rewrite `ActiveAgentManagerRemoteModeTest` with `InMemoryPropertiesComponent` stub
  and `ReflectionFactory.newConstructorForSerialization` allocation
- Rewrite `CopilotClientRemoteModeTest.allocateClient()` to use `ReflectionFactory`
  instead of Mockito inline mock maker, which fails on JetBrains JVM 25 when multiple
  test classes load the same class before Mockito can instrument it
- Add `TestCopilotClient` subclass that overrides `getResumeSessionId()` so
  buildCommand tests work without platform services
- Fix `UsageStatisticsLoaderTest` missing imports (`LinkedHashSet`, `Set`)
- Add `mockito-extensions/org.mockito.plugins.MockMaker` resource for compatibility
- All 46 remote mode tests now pass

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…atisticsService

- Fix UsageStatisticsLoaderTest: invokeCollectTurnStats was reflectively looking for
  a 7-param collectTurnStats(Path, String, LocalDate, LocalDate, Map, Set, Map) that
  doesn't exist; updated helper to use the real 5-param signature. Also corrected
  turnStatsWithoutTimestamp_usesLastSeenTimestampFallback assertions: the test was
  asserting entry-level agent overrides the session agent, which contradicts
  usesSessionAgentEvenWhenEntriesHaveDifferentAgentFields; corrected to expect session-
  level agent and added the date assertion that actually tests the timestamp fallback.

- Fix ToolCallStatisticsService.initialize(): throwing IllegalStateException when
  project.getBasePath() returns null causes initialize_nullBasePath_doesNotThrowAndLeavesQueriesEmpty
  to fail; changed to log a warning and return early so callers see empty statistics
  rather than a crash on default/null-path projects.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ticsService

Align with master's behavior — null basePath must fail loudly so misconfigured
projects surface visibly rather than silently dropping tool call statistics.
Updates the companion test to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@catatafishen

Copy link
Copy Markdown
Owner Author

This doesn't seem to be supported in ACP mode. I'll leave the change here and test it a bit more before deciding if it should be scrapped.

@catatafishen
catatafishen marked this pull request as draft May 3, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants