Repository navigation
feat(cli-registry): GitHub Copilot CLI as a run mode - #592
Conversation
|
Switched to draft as I'll implement the missing features so GH copilot goes in complete |
ccead2e to
7407bbc
Compare
|
I've been using this manually from my desktop and mobile and I can't find any faults with it |
|
Thanks @opticon454, this is a thorough piece of work: GitHub Copilot CLI becomes a full run mode through a registry entry, with launch options, the multi-user clamp, BYOK custom endpoints, footer-based working/idle and model detection, a status probe and tests to match. The measurements in the entry comments are exactly what makes a new CLI reviewable. Typecheck, lint, format and the full test gate all pass here. A few things to change before merge:
Smaller things I can pick up at merge time if you prefer: the Prettier re-padding of the unrelated tables in Once 1 to 4 are in, this is ready to merge. Thanks again for driving it all the way to a complete mode. |
1d107ec to
25ee641
Compare
|
Thanks for the careful review. All four are in as new commits on top of the original, so the delta is easy to see (
From the smaller list I did the zh-CN One more fix, found while testing: copy on selection. Copilot's TUI turns mouse tracking on itself. Under Two more, from the
How I checked it: a throwaway Codeman instance (own data dir and tmux socket) against a local OpenAI-compatible endpoint, with a real Chromium. A plain drag in a Copilot pane gave a selection that Auto Copy put on the clipboard and the wheel left as Full suite ( |
|
One more addition since the review fixes: the header plan-usage chip now has a Copilot row ( What it shows. One Where it comes from. The Copilot CLI has no scriptable usage command (
Tests. The parser (including overage clamping and the unlimited case), the token lookup (env precedence, the commented Also confirmed since the last comment: touch swipes on a phone reach Copilot as wheel reports in both directions (the same wheel forwarding that fixed drag selection), checked with real touch events in an emulated phone. If you'd rather not ship the network read in this PR, it is one self-contained commit and easy to drop; say so and I'll split it out. |
|
Thanks @opticon454, this round closes all four points from the first review exactly as asked: Two of the newer pieces need a fix before merge:
Smaller things I will pick up at merge time unless you get to them first:
Once 1 to 3 are in, this is ready to merge. Thanks again for taking Copilot all the way to a complete mode, with measurements to back every choice. |
|
Thanks, all three are fixed in 1. Free shows 100% used. 2. 3. PageUp/PageDown fallback. I measured it rather than drop the claim: on 1.0.95 in tmux, three PageUp moved a 140-line answer from lines 109-140 to 010-042 and three PageDown returned, so Copilot does page on those keys (SGR wheel reports also scroll it, 3 lines per report). Smaller things, done so you do not have to:
|
0557971 to
9ebce8b
Compare
|
Rebased onto 1.41.1 (docs-only conflicts in CLAUDE.md and README.md, resolved in favour of upstream's text with the Copilot wording re-applied). Copilot now also declares the |
|
Thanks @opticon454, and sorry this one is still waiting. Here is where it stands after 1.42.0. The run mode is in good shape. I re-checked the two upstream blockers that parked #198 on a current Copilot CLI (1.0.98): tmux The quota row is what holds it. Before shipping a request from the server I had a proper security review done of
There is also a documented route that avoids all of this: What I'd suggest:
With the quota row split out and those two done, the run mode can go into the next release. Thanks again for the depth here: the measurements in the entry comments and the golden tests made it very reviewable. |
9ebce8b to
aae6093
Compare
|
Rebased onto Two docs-only conflicts, both resolved by keeping upstream's new text and adding the Copilot entry:
Typecheck is clean and the full Your two local merge-time commits should apply on top of this head as they are. |
|
Thanks @opticon454 for the rebase onto 1.42.0. To recap, this PR adds GitHub Copilot CLI as a full run mode (registry entry, launch options, the multi-user clamp, BYOK endpoints, footer-based working/idle and model detection, the The run mode is ready apart from one small hardening. The head you pushed still has the quota row exactly as before, so the two asks from my last comment stand:
Smaller things, welcome in the same round, otherwise I will take them at merge time:
On my side I will add the two commits I mentioned (the wider |
aae6093 to
1ed3d8d
Compare
|
Thanks for the review. All five points are in; the head is now
Typecheck, lint, |
Copilot (`copilot`, npm @github/copilot) as a stock CLI entry with the same launch options as the other agent CLIs: model (--model), resume (--resume), continue (--continue) and allow-all (--yolo, clamped off for non-granted multi-user owners). Adds a resolver and GET /api/copilot/status probe, the agent-image package, Run-menu/mobile/logo entries, skill docs and tests.
- Drop GH_TOKEN / GITHUB_TOKEN from env.allowedKeys. The allowlist is one global list with no mode context, so they were settable on every session. COPILOT_GITHUB_TOKEN is already admitted by the COPILOT_ prefix. - Declare capabilities.mcpConfig (copilot-json, COPILOT_HOME relocation) so Settings does not list Copilot as unsupported while the sync-only target also lists it. The sync-only row is skipped while the run mode is enabled. - Require a leading alphanumeric on the resume id (path-segment in the registry, matching regex in the schema): --resume's value is optional, so `--resume --yolo` made --yolo its own flag and bypassed the allowAll clamp. Golden cases added; "ids only" wording corrected. - Remove the unread overlays.credStore line and document signing in inside a Docker container. - Add the zh-CN "Run GitHub Copilot" string, pin the new declarations in tests and update the MCP registry expectations.
Copilot's TUI turns mouse tracking on itself. Under `strip-mux-only` those DECSETs reached xterm, which then reported a plain drag to the TUI instead of selecting (only Shift+drag selected), so Auto Copy and copy-on-select did nothing. This is the failure opencode had and `strip-mux-and-mouse` was added for: the DECSETs are stripped, a drag is a local selection and clicks still reach the CLI through the browser's hand-encoded tap (`cliMouseTracking`). Stripping them also stops xterm from encoding the wheel for the CLI, so `_shouldForwardWheelToApp` now forwards it as SGR reports while Copilot is tracking the mouse (the same bytes xterm sent natively, which is what scrolling was verified against). Shift+wheel stays on local scrollback and a stale flag falls back to PageUp/PageDown. Tests: the strip flavor for copilot (stripped DECSETs, 3J kept, cliMouseTracking, direct-PTY untouched) and the wheel gate (forwards while tracking, no other non-claude mode changes).
…its for it Copilot had no composerReadyMark, so `codeman agent spawn --mode copilot` returned as soon as the session existed, before the composer could take a prompt (and before folder trust was answered). The mark is the composer's footer hint, not the glyph: the trust dialog draws `❯ 1. Yes`, so `❯` would call a worker ready while it still asks for trust. Measured on 1.0.95: the dialog footer reads `↑/↓ to navigate`, the composer's reads `/ commands · ? help`.
`/api/sessions/:id/last-response` fell through to the Claude transcript scan for copilot (not in the external-CLI list), so `codeman agent read` answered empty even with an answer on screen. Copilot is a repainting full-screen TUI whose stream is absolute cursor moves, so the buffer cannot be cut into lines either; the route now reads the pane's visible screen (tmux capture-pane) for such modes and falls back to the buffer where there is no pane. The segmenter gets a small per-CLI "pane dialect" row instead of another branch in the Codex-oriented loop: `❯ <prompt> HH:MM`, `● <answer>` with indented continuation, the cwd / `Session: N AIC used` row, composer, footer and the `┃` scrollbar gutter are chrome, and everything before the first prompt (banner, trust dialog) is dropped so a session that has not been asked anything answers nothing rather than serving its logo. Fixture is a real Copilot 1.0.95 pane. Tool-call rows are not modelled and read as answer text.
- New docs/copilot-integration.md in the shape of the other CLI guides: install and identity probe, authentication (keychain vs plain-text config.json, COPILOT_GITHUB_TOKEN), what Codeman wires up (Run button --yolo and its clamp, model, resume, continue, --name rule), BYOK custom endpoints, terminal behaviour (drag selection, wheel and touch), the codeman agent verbs (ready mark, read), plan usage, Docker, Remote SSH, what is deliberately not wired, known gaps. - Agent-CLIs wiki: the eleven-modes table, a GitHub Copilot CLI section, the env prefix row, the feature-comparison rows and the status endpoint list. - README, Home, FAQ, Core Concepts, Docker Cases, Remote SSH, Contributing, The Dashboard and Custom Model Endpoints: mode lists and counts; CLAUDE.md: stack line, SessionMode, prefixes, resolver list, external-CLI notes and the plan-usage source; api-reference MCP target wording; the env allowlist error message names COPILOT_*.
…allback, review nits Review of 8659ba2..be987fb: - Usage: a Free account answers premium_interactions {entitlement: 0, remaining: 0, percent_remaining: 0, unlimited: false, has_quota: false}, which read as 100% used. The parser now returns null when has_quota is false or the entitlement is not positive, with that payload as a fixture. Free meters chat (200) and completions (2000) too, so the comment no longer says they are unlimited on every plan. - agent read: rows were skipped until a `❯ <prompt>` row, so an answer taller than the pane (prompt scrolled off the top) read as "". With no prompt row AND no banner / trust dialog on screen, the rows above the composer are now the tail of the last answer. Text typed in the composer (a prompt-shaped row between the two rules) is not a prompt row. - Page keys: measured on 1.0.95 in tmux that Copilot pages on PageUp/PageDown (three PageUp moved lines 109-140 to 010-042, PageDown returned) as well as on SGR wheel reports, so copilot joins claude and opencode in _localScrollbackIsHollow(); the stale-tracking-flag fallback the comment and docs describe is now true, for the main pane and a TerminalTile (same function), with tests. - X-GitHub-Api-Version is 2022-11-28, a version GitHub supports (verified against the live endpoint); readPlanUsageTelemetryEnabled() is checked before isCopilotAvailable() so a switched-off chip skips the resolver probe too. - Palette label for Copilot in panels-ui.js; stale comments in types/session.ts and stock.ts. - Docs: Past Sessions continues with --continue (resumeSessionId is for API callers), nothing sets continueSession on a respawn, and only a tab name the user set is passed as --name; the usage wording for Free; the wheel and mouse-strip paragraphs in CLAUDE.md and architecture-invariants; the changeset describes the whole feature.
The Copilot quota chip row moves to its own PR (branch feat/copilot-plan-usage on the fork). This removes its docs, changeset line and CLAUDE.md note.
Like resumeSessionId, model follows a flag, so --model --yolo made --yolo its own flag and bypassed the allowAll clamp. New model-plain token pattern, the same rule in the create schema, and golden and schema tests.
…ide an answer A submitted prompt is only a prompt at column 0 or 1, so an indented shell prompt in a code block stays answer text. Only the last rule, composer row, rule triple is the composer, so a paragraph between two rules in an answer is no longer dropped. A test for each.
…PI or COPILOT_MODEL only The Remote SSH section claimed the same flags and clamp as a local launch, but defaultRemoteCommandForMode starts a bare copilot (pinned in test/location-overlay-commands.test.ts). Nothing in the UI sets copilotConfig.model, so the model rows now say API callers, or COPILOT_MODEL for a custom endpoint.
The entry inherited wheelForward never from agentDefaults() while _shouldForwardWheelToApp forwards Copilot's wheel whenever the CLI tracks the mouse, at any version. New always mode, set on copilot, pinned by a registry test.
1ed3d8d to
ebfa4a0
Compare
|
Rebased onto the current
No code changes beyond that. Re-checked on the rebased head: |
|
Thanks @opticon454 for taking this all the way. This PR adds GitHub Copilot CLI as a full run mode: registry entry, launch options with the multi-user clamp, BYOK endpoints, footer-based working/idle and model detection, the selection and wheel strip, and the A few small things remain. I will take them at merge time unless you would rather push them yourself:
On my side I will add the two commits I mentioned earlier, the wider |
…de or move the token The COPILOT_ prefix admits every variable Copilot reads, and the 1.0.98 bundle reads far more than `copilot help environment` lists. Only COPILOT_HOME, COPILOT_ALLOW_ALL and the BYOK trio were privileged, so a non-granted multi-user owner could still hand back what the allowAll clamp removed: - approval: COPILOT_PLAN_THEN_AUTOPILOT, COPILOT_ASSISTED_APPROVAL - code with no approval row: COPILOT_CLI_DIST_DIR runs <dir>/index.js in place of the CLI, the package caches choose the CLI build, COPILOT_EDITOR, COPILOT_PROVIDER_API_KEY_COMMAND and COPILOT_AHP_HOST_BIN are executed, skills, provider configs and hooks are loaded from the named places (the DSH_HOME class) - token redirects: COPILOT_API_URL and the other API bases receive the CLI's sign-in token, which in multi-user mode is the operator's (all users share the OS account and ~/.copilot), and the rest of the BYOK provider family re-aims a Codeman custom endpoint (the DEEPSEEK_BASE_URL / OMP_AUTH_BROKER_URL class) All are now privilegedEnvKeys. The registry schema capped that list at 8; the cap is now 48, since a longer deny list only takes more away from non-granted owners. The test sends one of each class through the real schema (they all pass it) and the real clamp. The docs say the list is read from the 1.0.98 bundle and that a variable a later release adds is not covered until it is listed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…PILOT_* in the allowlist line The PR names GitHub Copilot CLI throughout README.md (ten CLIs, the install list, the run-mode table, the multi-CLI prefixes, the agent-read note and the architecture diagram), and README.zh-CN.md, which is kept in parity with it since 1a692f5, still said nine CLIs and never mentioned Copilot. The same spots are translated here (not the plan-usage line: the quota row was split out of the PR, so neither README names Copilot there). Both READMEs' "Schema-validated inputs" bullet lists every admitted env prefix and missed the new COPILOT_* one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… by token Both Docker sections said "or set COPILOT_GITHUB_TOKEN for the session", which cannot work: a docker quick-start refuses envOverrides (session-routes.ts), and Copilot's dockerExecEnvNames is empty, so a token in the server environment is not forwarded into the container either. The sentence now says to sign in inside the container and why the token route does not exist. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The pane segmenter ended the response block at every full-width rule, so an answer that draws one (a markdown `---`, a passage quoted between two rules) read as only its last section: `Summary / ──── / Details` came back as "Details", and the existing test pinned that by expecting the quoted passage without its "Here it is:" lead-in. Inside an open response a rule is now held and written into the text as `---` once more of the same answer follows, so a rule that only separates the answer from the footer never trails it. The composer's own two rules (the last rule/❯/rule triple) are still boundaries. The old test now expects both parts, and a new one pins a Summary/rule/Details answer as one block; both fail without the change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
GET /api/sessions/:id/last-response reads a copilot pane through capturePaneText, since a repainting full-screen TUI streams cursor moves rather than lines, and nothing pinned that at the route: had the branch stopped capturing, copilot would quietly have been segmented off the fused stream again. Three app.inject cases with a stubbed mux: the captured screen is what gets segmented (the stream text never shows), a null capture falls back to the buffer, and a line-streaming mode (opencode) never asks for the screen. The first two fail with the capture switched off. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…v prefixes - docs/cli-registry.md said Session history resume goes through copilotConfig.resumeSessionId / continueSession. Past Sessions sends only `continueSession: true` (terminal-ui.js, the modeConfigKey table); resumeSessionId is for API callers. - The wheelForward JSDoc in cli-registry/types.ts read as if the value had effect. Nothing reads it (it is in the DECLARED-FOR-LATER list); the browser gate is _shouldForwardWheelToApp in terminal-ui.js with its own per-CLI rules, which the comment now says. - The env-prefix lists in the CLAUDE.md Security table and the allowedEnvPrefixes() comment in schemas.ts stopped at DEEPSEEK_, missing both OMP_ and the new COPILOT_; the Multi-CLI prefix rule missed OMP_. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…run mode GitHub Copilot CLI (`copilot`) becomes a run mode: Run menu, welcome screen, cron and CLI management once it is installed, tmux-backed like the other agent CLIs, working/idle and the model read off its footer, launch options through `copilotConfig` (model, resume, continue, the multi-user-clamped allowAll), Custom Model Endpoints via Copilot's BYOK variables, MCP sync of its mcp-config.json, a status probe, and the `codeman agent` verbs (spawn waits for its composer, read returns its last answer). The plan-usage quota row that earlier rounds carried was split out on review and is not part of this merge. Merge-time commits on top: the wider COPILOT_* clamp and the zh-CN README rows (maintainer), the Docker sign-in docs, divider rows inside an answer kept by `agent read`, route tests for the screen-read last-response branch, and doc drift (Past Sessions, wheelForward JSDoc, COPILOT_/OMP_ in the env prefix lists). Verified before merge on Copilot CLI 1.0.98: injected keystrokes, bracketed paste and Enter reach the TUI in tmux (github/copilot-cli#4180 does not reproduce), a shell tool call completes inside tmux (#4223 does not reproduce), and a session driven through this branch answered over send-and-wait. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Merged and shipped in 1.43.0. Thank you @opticon454, this one took five review rounds and you turned every one around fast, with measurements instead of guesses each time. Before merging I kept the promise from #198 and checked a current Copilot CLI (1.0.98) myself: injected keystrokes, a bracketed paste and Enter all reach the TUI in tmux, and a shell tool call completes inside tmux, so neither github/copilot-cli#4180 nor #4223 reproduces. A Copilot session driven through this branch answered over send-and-wait end to end. What I added at merge time, as separate commits so you can see them: the wider One follow-up worth a small PR if you feel like it: Copilot's folder-trust dialog is not answered automatically, so a worker started through the API waits on it until someone presses Enter. |
Summary
GitHub Copilot CLI (
copilot, npm@github/copilot) as a stock run mode, with the same launch options as the other agent CLIs. Covers #198.It shows up in the Run menu, welcome screen, phone picker and cron agent list once installed, runs in tmux like the other agent CLIs, and CLI management can install it (
npm install -g @github/copilot).copilotConfigon the session create and quick-start bodies, the same shape asgrokConfig/ompConfig:model(--model),resumeSessionId(--resume <value>for API callers: one plain word starting with a letter or digit, so a flag-shaped value such as--yolois dropped),continueSession(--continue, which Past Sessions sends: the most recent conversation in that directory) andallowAll(--yolo).allowAll: true, like grok'salwaysApproveand antigravity'sdangerouslySkipPermissions.allowAllis aprivilegedParam, so a non-granted multi-user owner has it forced back tofalse; an absent config is not materialized, because a barecopilotis already in Manual Approval.COPILOT_HOMEandCOPILOT_ALLOW_ALLare privileged env keys.◉ Working esc edit prompt(the dot alternates◉/◎); the composer is the❯between two rules. A tool waiting for approval replaces the composer and the tab reads idle.COPILOT_PROVIDER_BASE_URL,_API_KEY,COPILOT_MODEL) are injected for a Custom Model Endpoint, with the/v1suffix, so the Run menu's custom endpoint rows work for it; the three are privileged env keys.--name(an automaticw1-…name is not pinned), except next to--resume/--continue, which Copilot refuses to combine with it (a continued session keeps its name).altScreen: 'strip-mux-and-mouse'like opencode: a plain drag is a local selection (Auto Copy and copy-on-select work, not just Shift+drag), clicks still reach the CLI through the hand-encoded tap, and the wheel is forwarded as SGR reports while the CLI is tracking the mouse.codeman agentverbs: the session exportsCODEMAN_MUX,CODEMAN_SESSION_IDandCODEMAN_API_URL, sols,spawn,send,wait,read,interruptandrmwork.composerReadyMarkis the composer's footer hint (/ commands · ? help), not❯, which the folder-trust dialog also draws.agent readreturns the last answer: the route reads the pane's visible screen for this repainting TUI and segments it with a per-CLI pane dialect (prompt, answer bullet, chrome, scrollbar gutter). Tool-call rows are not modelled and read as answer text. A prompt row scrolled off by a tall answer reads as the answer's tail (unless the banner or trust dialog is on screen).--until stopis refused for Copilot like other hook-less modes;idle,exitor--matchmarkers work.Copilotrow with the calendar-month premium-request quota (mo, with request counts and the reset in the tooltip), read fromGET api.github.com/copilot_internal/userwith the CLI's own sign-in token. The endpoint is not part of GitHub's documented API (the editors use it), so the read is best-effort and silent: 10-minute host poll, only when the CLI is installed, a token resolves and plan-usage display is on; any failure, an unlimited quota or no premium quota at all (a Free account answersentitlement: 0, has_quota: false) just shows no row.GET /api/copilot/status(available,path,version), backed bycopilot-cli-resolver.ts, which only accepts a binary whose--versionsaysGitHub Copilot CLI.COPILOT_prefix only (a headless host setsCOPILOT_GITHUB_TOKEN);GH_TOKEN/GITHUB_TOKENstay out of the global allowlist.capabilities.mcpConfig(~/.copilot/mcp-config.json,COPILOT_HOMErelocation), so Settings no longer lists Copilot as unsupported. The sync-only row inmcp-sync-targets.tsis skipped while the run mode is enabled.credStore; Copilot's token is plain text inconfig.jsonon a host without a keyring, so it is not seeded. Sign in inside the container (documented inDocker-Cases.md).Measured on 1.0.94 inside tmux
Alternate-screen TUI with mouse tracking on. Keystrokes sent with
tmux send-keysand throughPOST /api/sessions/:id/inputreach the composer, Shift+Enter inserts a newline, mouse-wheel scrolling works, and shell tool calls complete (an approval prompt, a 6 s command, 3000 lines of output). github/copilot-cli#4180 (a driven PTY ignores input) and #4223 (a shell command never completes under tmux) were filed against 1.0.70 to 1.0.74; neither reproduces on 1.0.94, so older installs may still hit them.Also checked against a mock OpenAI endpoint (1.0.95)
With
COPILOT_PROVIDER_BASE_URL=http://127.0.0.1:<port>/v1,COPILOT_PROVIDER_API_KEYandCOPILOT_MODELset,copilot -pposts to/v1/chat/completionswith the key and the model name, no GitHub sign-in.--resume <id> --model <m>and--continue --model <m>resume the earlier session;--namewith either is rejected, hence the rule above.Checked end to end
A throwaway Codeman instance (own data dir and tmux socket) against a local OpenAI-compatible endpoint, with a real Chromium: a plain drag in a Copilot pane produced a 38-character selection that Auto Copy put on the clipboard, and the wheel went out as
ESC[<64;…Mreports. A shell pane that turns mouse tracking on itself (the pre-fix situation) produced no selection and left the clipboard alone, so the check does detect the failure. Thecodeman agentlifecycle ran against two Copilot panes:spawnwarns instead of claiming ready while the trust dialog is up,--waitresolves onidle,readreturns the answer, theWORKDONE_4711marker wait matches,interruptandrmwork, andrmof self is refused.Tests
Spawn golden and
copilot-cli-resolvertests, the multi-user clamp, history-resume fidelity, the capability-predicate row, remote/docker overlay commands, install.sh detection parity, docker-agent-image coverage, the id-branching guard (legacy<Mode>Configplumbing allowlisted as for grok), and the registry load/schema tests.tsc --noEmitand lint clean.