Skip to content

feat(cli-registry): GitHub Copilot CLI as a run mode - #592

Open
opticon454 wants to merge 9 commits into
Ark0N:masterfrom
opticon454:feat/cli-copilot
Open

opticon454 wants to merge 9 commits into
Ark0N:masterfrom
opticon454:feat/cli-copilot

Conversation

@opticon454

@opticon454 opticon454 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

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).

  • Launch options: a copilotConfig on the session create and quick-start bodies, the same shape as grokConfig/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 --yolo is dropped), continueSession (--continue, which Past Sessions sends: the most recent conversation in that directory) and allowAll (--yolo).
  • Permissions: the Run button sends allowAll: true, like grok's alwaysApprove and antigravity's dangerouslySkipPermissions. allowAll is a privilegedParam, so a non-granted multi-user owner has it forced back to false; an absent config is not materialized, because a bare copilot is already in Manual Approval. COPILOT_HOME and COPILOT_ALLOW_ALL are privileged env keys.
  • Working/idle: from the footer. A running turn reads ◉ 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.
  • Model: read from the right-hand field of the footer's last row.
  • Custom endpoints: Copilot's BYOK variables (COPILOT_PROVIDER_BASE_URL, _API_KEY, COPILOT_MODEL) are injected for a Custom Model Endpoint, with the /v1 suffix, so the Run menu's custom endpoint rows work for it; the three are privileged env keys.
  • Naming: a tab name you set yourself is passed as --name (an automatic w1-… name is not pinned), except next to --resume/--continue, which Copilot refuses to combine with it (a continued session keeps its name).
  • Selection and wheel: Copilot turns mouse tracking on itself, so it uses 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 agent verbs: the session exports CODEMAN_MUX, CODEMAN_SESSION_ID and CODEMAN_API_URL, so ls, spawn, send, wait, read, interrupt and rm work. composerReadyMark is the composer's footer hint (/ commands · ? help), not ❯, which the folder-trust dialog also draws. agent read returns 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 stop is refused for Copilot like other hook-less modes; idle, exit or --match markers work.
  • Plan usage: the header chip gets a Copilot row with the calendar-month premium-request quota (mo, with request counts and the reset in the tooltip), read from GET api.github.com/copilot_internal/user with 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 answers entitlement: 0, has_quota: false) just shows no row.
  • Probe: GET /api/copilot/status (available, path, version), backed by copilot-cli-resolver.ts, which only accepts a binary whose --version says GitHub Copilot CLI.
  • Env: the COPILOT_ prefix only (a headless host sets COPILOT_GITHUB_TOKEN); GH_TOKEN/GITHUB_TOKEN stay out of the global allowlist.
  • MCP sync: the entry declares capabilities.mcpConfig (~/.copilot/mcp-config.json, COPILOT_HOME relocation), so Settings no longer lists Copilot as unsupported. The sync-only row in mcp-sync-targets.ts is skipped while the run mode is enabled.
  • Docker: no credStore; Copilot's token is plain text in config.json on a host without a keyring, so it is not seeded. Sign in inside the container (documented in Docker-Cases.md).
  • Also: the agent image's npm package list, Run-menu logo (a drawn mark, not GitHub's), phone-picker entry, skill docs and the plugin mirror.

Measured on 1.0.94 inside tmux

Alternate-screen TUI with mouse tracking on. Keystrokes sent with tmux send-keys and through POST /api/sessions/:id/input reach 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_KEY and COPILOT_MODEL set, copilot -p posts to /v1/chat/completions with the key and the model name, no GitHub sign-in. --resume <id> --model <m> and --continue --model <m> resume the earlier session; --name with 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;…M reports. 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. The codeman agent lifecycle ran against two Copilot panes: spawn warns instead of claiming ready while the trust dialog is up, --wait resolves on idle, read returns the answer, the WORKDONE_4711 marker wait matches, interrupt and rm work, and rm of self is refused.

Tests

Spawn golden and copilot-cli-resolver tests, 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>Config plumbing allowlisted as for grok), and the registry load/schema tests. tsc --noEmit and lint clean.

@opticon454

Copy link
Copy Markdown
Contributor Author

Switched to draft as I'll implement the missing features so GH copilot goes in complete

@opticon454
opticon454 force-pushed the feat/cli-copilot branch 4 times, most recently from 232994c to ccead2e Compare October 10, 2026 06:29
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.
@opticon454
opticon454 marked this pull request as ready for review October 10, 2026 07:06
@opticon454

Copy link
Copy Markdown
Contributor Author

I've been using this manually from my desktop and mobile and I can't find any faults with it

@Ark0N

Ark0N commented Oct 10, 2026

Copy link
Copy Markdown
Owner

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:

  1. GH_TOKEN / GITHUB_TOKEN in env.allowedKeys (src/config/cli-registry/stock.ts:1660). The env allowlist is one global list with no mode context, so these two keys become settable on every session (I checked: a claude, shell or codex create with them now validates). CLAUDE.md and the comment above allowedEnvPrefixes() in src/web/schemas.ts keep each CLI to its own vendor namespace for that reason, which is why pi and DeepSeek leave their foreign provider keys out. Copilot reads COPILOT_GITHUB_TOKEN first and the COPILOT_ prefix already admits it, so please set allowedKeys: [] and point headless users at COPILOT_GITHUB_TOKEN in the comment and docs.

  2. MCP sync shows Copilot as unsupported (src/web/routes/mcp-sync-routes.ts:62). Now that Copilot is an agent entry without capabilities.mcpConfig, mcpUnsupportedLabels() lists "GitHub Copilot" while the sync-only target still syncs "GitHub Copilot CLI", so Settings says both. Declaring mcpConfig: { path: '.copilot/mcp-config.json', format: 'copilot-json', relocation: { envVar: 'COPILOT_HOME', path: 'mcp-config.json' } } on the entry fixes it (the dialect and the disabledMcpServers read are keyed by format, and the sync-only row is then skipped). The src/mcp-sync-targets.ts @fileoverview and the paragraph in docs/cli-registry.md need the matching update.

  3. Resume ids that start with - (src/web/schemas.ts:436 and the resumeId param at stock.ts:1609). resumeSessionId: '--yolo' passes both patterns and renders copilot --resume --yolo; since --resume takes an optional value, --yolo is then its own flag, which gets around the allowAll clamp for a non-granted owner. Please require a leading alphanumeric (/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/ in the schema, pattern: 'path-segment' on the param), add a golden case for it, and reword the "ids only, never names" comments here and in docs/cli-registry.md:284, since a name like my-feature also matches. Grok has the same shape; that one is ours to fix separately.

  4. Docker credentials (stock.ts:1713). overlays.credStore is one of the registry fields no code reads yet; Docker seeding comes from CRED_STORES in src/docker-hosts.ts:854, which has no .copilot row, so Copilot in a Docker case starts signed out in each new container. Either add a row there (the token sits in config.json on hosts without a keyring, so an enabledByEnv opt-in like the gh row is worth considering) or drop the credStore line and say in docs/wiki/Docker-Cases.md that you sign in inside the container.

Smaller things I can pick up at merge time if you prefer: the Prettier re-padding of the unrelated tables in docs/cli-registry.md (docs are outside our Prettier scope), a 'Run GitHub Copilot' zh-CN entry in src/web/public/i18n.js, short: 'Copilot' in mobile-overview.js:61, and the CLAUDE.md / docs/wiki/Agent-CLIs.md / allowlist error message updates for the new mode.

Once 1 to 4 are in, this is ready to merge. Thanks again for driving it all the way to a complete mode.

- 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.
@opticon454

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review. All four are in as new commits on top of the original, so the delta is easy to see (f4d68f47 for the review, then three follow-ups for bugs found while testing).

  1. GH_TOKEN / GITHUB_TOKEN: allowedKeys is now [], and the comment points headless hosts at COPILOT_GITHUB_TOKEN (already admitted by the COPILOT_ prefix). A test pins that both names are rejected for claude, shell, codex and copilot creates.
  2. MCP sync: the entry declares mcpConfig (.copilot/mcp-config.json, copilot-json, COPILOT_HOME relocation). I kept the sync-only row: it is skipped while the run mode is enabled and still applies if someone disables the mode with the tool installed. The mcp-sync-targets.ts @fileoverview, the route comments and docs/cli-registry.md are updated, and test/mcp-sync-registry.test.ts now expects copilot.
  3. Resume ids starting with -: you were right, --resume --yolo rendered --yolo as its own flag. The param is now path-segment and the schema regex needs a leading alphanumeric. The golden test covers --yolo, -n, --allow-all-tools, ., .. and .hidden (all dropped) and that my-feature still passes. The "ids only" wording is corrected in the entry, schema and docs.
  4. Docker credentials: I dropped the credStore line rather than add a CRED_STORES row, because the token is plain text in config.json on a host without a keyring. Docker-Cases.md now says to sign in inside the container.

From the smaller list I did the zh-CN Run GitHub Copilot entry and put the unrelated Prettier re-padding in docs/cli-registry.md back to upstream's. I left short as 'GitHub Copilot' in mobile-overview.js: test/mobile-overview.test.ts pins the static table to the registry label, so 'Copilot' there fails that parity check. The CLAUDE.md / Agent-CLIs.md / allowlist error-message updates I've left for merge time, as you offered.

One more fix, found while testing: copy on selection. 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, so Auto Copy and copy-on-select did nothing (only Shift+drag selected). It is the failure opencode had and strip-mux-and-mouse exists for, so Copilot now uses it: a drag is a local selection and clicks still reach the CLI through the hand-encoded tap. Stripping the DECSETs also stops xterm encoding the wheel, so _shouldForwardWheelToApp forwards it as SGR reports while Copilot is tracking the mouse (the same bytes xterm sent natively, which is what scrolling was verified against). Tests cover the strip and the wheel gate.

Two more, from the codeman agent verbs (README: session-to-session verbs in every CLI mode):

  • spawn readiness. Copilot declared no composerReadyMark, so agent spawn --mode copilot returned before the composer could take a prompt. The mark is the footer hint / commands · ? help, not ❯: the folder-trust dialog draws ❯ 1. Yes, which is the same trap the claude entry's comment describes.
  • agent read. Copilot was missing from the external-CLI list that routes last-response to the pane segmenter, so it fell through to the Claude transcript scan and answered empty. Copilot's stream is absolute cursor moves rather than lines, so the route reads the pane's visible screen (tmux capture-pane) for it and segments with a small per-CLI "pane dialect" row (❯ <prompt> HH:MM, ● <answer>, status/footer/scrollbar chrome; everything before the first prompt is dropped, so a fresh session answers nothing instead of serving its banner). Tool-call rows are not modelled and read as answer text.

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 ESC[<64;…M reports; a shell pane that enables mouse tracking itself (the old situation) produced no selection, so the check does detect the failure. Over the verbs: ls, spawn (warns instead of claiming ready while the trust dialog is up), send --wait (resolves on idle), read, wait --match, interrupt, rm and the refusal to rm self. --until stop is refused for Copilot like the other hook-less modes.

Full suite (npm test), typecheck, lint and format are clean locally.

The Copilot CLI has no scriptable usage command (`copilot billing` exists only inside the TUI), but
the quota the editors show is `GET https://api.github.com/copilot_internal/user`, readable with the
CLI's own sign-in token. The header chip gets a `Copilot` row with one `mo` window (the
calendar-month premium-request quota) beside Claude's and Codex's, with request counts and the reset
in the tooltip.

The host polls it every 10 minutes, read-only like the Codex poll, and only when the CLI is
installed, a token resolves (COPILOT_GITHUB_TOKEN, GH_TOKEN, GITHUB_TOKEN, then authTokens in
<COPILOT_HOME or ~/.copilot>/config.json) and plan-usage display is on, so turning the chip off also
stops the request. The endpoint is undocumented, so every failure (offline, 401, a changed shape, an
unlimited quota) yields no row. The token is never logged and is sent only to api.github.com with
redirects refused.
@opticon454

Copy link
Copy Markdown
Contributor Author

One more addition since the review fixes: the header plan-usage chip now has a Copilot row (6f89c70b).

What it shows. One mo window with the calendar-month premium-request quota, and the request counts and reset date in the tooltip, beside Claude's and Codex's rows.

Where it comes from. The Copilot CLI has no scriptable usage command (copilot billing exists only inside the TUI), so this reads the quota snapshot the editors use, GET https://api.github.com/copilot_internal/user, with the CLI's own sign-in token. That endpoint is not part of GitHub's documented API, so I kept it narrow and fail-silent. It is the one part of this PR you may want to weigh:

  • It is the first thing in Codeman that makes a request to GitHub on the user's behalf from the server.
  • It polls every 10 minutes (same host-scoped, read-only shape as the Codex poll) and only when the CLI is installed, a token resolves (COPILOT_GITHUB_TOKEN, GH_TOKEN, GITHUB_TOKEN, then authTokens in <COPILOT_HOME or ~/.copilot>/config.json) and plan-usage display is on, so turning the chip off also stops the request.
  • Any failure (offline, 401, a changed shape) or an unlimited quota yields no row. The token is never logged or put in an error, and it is sent only to api.github.com over HTTPS with redirects refused.

premium_interactions is the only metered bucket; chat and completions are unlimited: true on the plan I could check (a Business seat), which I can't speak for beyond that.

Tests. The parser (including overage clamping and the unlimited case), the token lookup (env precedence, the commented config.json, COPILOT_HOME, a non-github.com host), the reader with an injected fetch, and the chip rendering. The Claude/Codex change-detection signatures are byte-identical to before.

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.

- 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_*.
FAQ, Installation and Security said the only network traffic is browser to server. The plan-usage
Copilot row asks api.github.com for the user's own quota, so each now states that one optional
exception, when it happens (CLI installed and signed in, chip on) and how to stop it.
@Ark0N

Ark0N commented Oct 11, 2026

Copy link
Copy Markdown
Owner

Thanks @opticon454, this round closes all four points from the first review exactly as asked: allowedKeys is empty, Copilot declares its own mcpConfig, a resume value has to start with a letter or digit (the golden test covering --yolo, -n and friends is a nice touch), and the Docker docs say to sign in inside the container. The selection fix, the readiness mark and the agent read reader are all good catches too. Typecheck, lint, format and the full gate pass here, and CI is green.

Two of the newer pieces need a fix before merge:

  1. Copilot Free shows 100% used (src/usage-telemetry.ts:165). A Free account answers premium_interactions: { entitlement: 0, remaining: 0, percent_remaining: 0, unlimited: false, has_quota: false }, so parseCopilotQuotaResponse returns usedPercentage: 100 and the chip shows Copilot mo 100% for a plan with no premium quota. Please return null when entitlement is not positive or has_quota === false, and add that payload as a fixture in test/copilot-plan-usage.test.ts. On that plan chat (200) and completions (2000) are metered too, so the comment about them being unlimited on every plan needs rewording.

  2. agent read is empty when the answer is taller than the pane (src/web/response-viewer-transcript.ts:354). Rows are skipped until a ❯ <prompt> row is seen, and with only the visible screen to read, a long answer pushes that row off the top: a 40-row screen holding a 35-line answer returns "", which a caller cannot tell apart from "no answer yet". When no prompt row is on screen but the session has already been sent a prompt (or there is no banner on screen), please treat the non-chrome rows above the composer as the tail of the last answer, and add a test with the prompt row scrolled off. docs/copilot-integration.md:171 already describes the tail behaviour, so the code just needs to match it.

  3. The PageUp/PageDown fallback (src/web/public/terminal-ui.js:5653). The comment and docs/copilot-integration.md:95 say a stale tracking flag falls back to page keys, but _localScrollbackIsHollow() (line 5751) only covers claude and opencode, so for Copilot the wheel scrolls an empty local buffer in that window. Either measure that Copilot pages on PgUp/PgDn and add it there (and in the TerminalTile twin) with a test that asserts the keys, or drop the claim from the comment, the docs and the test name.

Smaller things I will pick up at merge time unless you get to them first:

  • X-GitHub-Api-Version: 2025-04-01 (src/utils/copilot-plan-usage.ts:86) is not a version GitHub supports on its documented endpoints (it answers 400 there); copilot_internal/user ignores it today. I will switch it to 2022-11-28.
  • Checking readPlanUsageTelemetryEnabled() before isCopilotAvailable() in src/web/server.ts:2871, so a switched-off chip skips the resolver probe as well.
  • Doc wording: Past Sessions resumes Copilot with --continue, not --resume <id>; Copilot has no respawn; only a manually chosen tab name goes out as --name; plus the stale comments at src/types/session.ts:549 and src/config/cli-registry/stock.ts:1574.
  • The wheel and mouse-strip paragraphs in CLAUDE.md and docs/architecture-invariants.md, a missing palette label in panels-ui.js, two small typos in the README and docs/cli-registry.md, and the changeset text.

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.

…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.
@opticon454

Copy link
Copy Markdown
Contributor Author

Thanks, all three are fixed in 0ec702fa, each with a test. Full suite, typecheck, lint and format are clean locally.

1. Free shows 100% used. parseCopilotQuotaResponse now returns null when has_quota === false or the entitlement is not positive, so a Free account gets no Copilot row. Your payload is a fixture in test/copilot-plan-usage.test.ts (Free: chat 200 and completions 2000 metered, premium_interactions with no quota). The comment, docs/copilot-integration.md, the wiki and CLAUDE.md no longer say chat and completions are unlimited on every plan: they are unlimited on the paid plans, metered on Free, and the chip shows neither.

2. agent read on a tall answer. With no prompt row on screen and no banner or trust dialog either, the rows above the composer are now the tail of the last answer. Tests: prompt scrolled off, a leading ● row still on screen, the banner / trust dialog still reading as nothing, and the prompt row preferred when it is visible. One case I hit while writing them: text typed in the composer is a ❯ <text> row between the two rules and would have counted as "the first prompt", so typing under a tall answer made its tail read as banner. A prompt-shaped row flanked by the rules is now treated as the composer, with a test.

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). _localScrollbackIsHollow() now covers copilot, which is the single function the main pane and the TerminalTile twin both call. Tests assert \x1b[5~ / \x1b[6~ for the main pane and for a tile, and that a Copilot pane with real local scrollback is left alone. The comment, the docs and architecture-invariants.md are updated to match.

Smaller things, done so you do not have to:

  • X-GitHub-Api-Version is 2022-11-28 (checked against the live endpoint, still 200).
  • readPlanUsageTelemetryEnabled() is checked before isCopilotAvailable(), so a switched-off chip skips the resolver probe too.
  • 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 goes out as --name. Corrected in copilot-integration.md, the Agent CLIs wiki, cli-registry.md and the PR description.
  • The stale comments in src/types/session.ts and stock.ts, the missing palette label in panels-ui.js, the wheel / mouse-strip paragraphs in CLAUDE.md and architecture-invariants.md, and the changeset text.
  • I read through the README and docs/cli-registry.md additions for the typos you mentioned and did not find any, so if you spotted specific ones I have not fixed them.

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