<!-- Markdown mirror. Canonical: https://empryo.com/docs/providers/copilot -->

# GitHub Copilot

> Use your paid GitHub Copilot subscription in Empryo: sign in with a device code, watch the monthly allowance, run Claude, GPT and Gemini at no extra cost.

If you pay for [GitHub Copilot](https://github.com/features/copilot), Empryo can use it. Claude Opus/Sonnet, GPT-4o, GPT-5.4, o-series, Gemini 2.5 Pro - all included in your Copilot subscription, no extra cost.

> **Warning:**
> Unofficial integration using the same internal API as VS Code. Not endorsed by GitHub. Use at your own risk.

## Setup

**1. Sign in**

```
/login copilot
```
Empryo shows a short code, opens `github.com/login/device`, and waits while you approve. The token goes to your system keychain. No IDE required.

**2. Use it**

Press `Ctrl+L`, pick `copilot`, choose a model.
```bash
empryo --headless --model copilot/claude-sonnet-4.6 "explain this file"
```

### Enterprise seats

A Copilot seat can live on a GitHub that is not github.com, GitHub Enterprise Cloud **with data residency** (a `*.ghe.com` tenant) or a self-hosted **Enterprise Server**. Those tenants serve every leg of the flow from their own hostnames, and a token minted on one GitHub means nothing on another, so the host is part of the sign-in rather than a setting applied afterwards:

```
/login copilot octocorp.ghe.com
```

`/login copilot enterprise` asks for the host instead, and `/login` lists **GitHub Copilot (Enterprise)** as its own row on both the TUI picker and the desktop dialog. Headless takes it in the same position:

```bash
empryo --headless --login copilot octocorp.ghe.com
```

Give the **tenant root**. `api.` and `copilot-api.` hosts are derived from it, pasting one folds back to the root rather than being taken literally:

| You are on | Device login | REST + token exchange | Inference |
|---|---|---|---|
| github.com | `github.com` | `api.github.com` | `api.githubcopilot.com` |
| `octocorp.ghe.com` | `octocorp.ghe.com` | `api.octocorp.ghe.com` | `copilot-api.octocorp.ghe.com` |
| Enterprise Server | your host | `<host>/api/v3` | whatever the token exchange names |

The host is saved next to the credential as `copilotHost`, and `COPILOT_GITHUB_DOMAIN` overrides it for one run or a CI image. Signing out returns the lane to public github.com. Business and Enterprise seats **on github.com** need none of this. Their proxy already comes back in the token exchange and has always been followed.

> **Note:**
> The device flow uses the shared editor OAuth app id. If your enterprise registers its own app for editor clients, set `COPILOT_OAUTH_CLIENT_ID` to it. A `*.ghe.com` hostname does not by itself change which integrations your admin permits.

### Reusing an IDE token instead

If you already signed in to Copilot in VS Code or JetBrains, that token works too:

```bash
jq -r '."github.com".oauth_token' ~/.config/github-copilot/apps.json
empryo --set-key copilot <token>
```

On Windows the path is `~\AppData\Local\github-copilot\apps.json`. `/keys` inside the TUI does the same thing.

> **Note:**
> The credential must come from a **classic OAuth App**, which is what both paths above produce. Tokens minted by a GitHub App (client ids starting `Ov23li`) are rejected by Copilot's token exchange with a 404, so a fine-grained PAT will not work here.

## Which models your seat can actually run

Copilot's catalog lists every model GitHub sells, not the ones your seat may use. A plan with **no premium allowance** (Copilot Free, `free_limited_copilot`) routes only the base GPT line, `gpt-4.1` and `gpt-4o`. Every other id answers:

```
The requested model is not supported.
```

Empryo rewrites that refusal with the reason and your seat SKU instead of passing it through, because GitHub's own message names neither. Claude, Gemini and the GPT-5 line need a paid Copilot plan.

Models whose terms you have not accepted yet (`policy.state: disabled`. Most new SKUs) are accepted automatically on first use, the same call the official editor extensions make when you pick the model there.

### Two wire formats

Copilot is the only provider whose endpoint varies per model, and Empryo follows the `supported_endpoints` its `/models` declares:

| Models | Endpoint |
|--------|----------|
| `gpt-5.5`, `gpt-5.6-*`, `mai-code-*` | `/responses` only |
| `gpt-5.4`, `gpt-5-mini` | both, Empryo takes `/responses` |
| Claude, Gemini, Kimi, `gpt-4.1`, `gpt-4o` | `/chat/completions` |

The Responses lane runs with `store: false` and encrypted reasoning replay, so a multi-step agent turn keeps its reasoning across tool calls. The mapping is cached on disk, so a `--headless` run resuming a saved model encodes the right wire before it opens any picker.

Reasoning effort comes from Copilot's own per-model ladder (`/effort`), which is not always the vendor's: Copilot's Claude SKUs take `low|medium|high|max`, `gpt-5.6` adds `none`.

## Usage and quota

Copilot bills a monthly allowance, not tokens, so the number that matters is how much of the month is left. GitHub reports it and Empryo shows it next to the relay subscription meters, in the same gauge:

```
Subscription · copilot
● pro                    octocat
  ▸ premium        [▰▰▰▰▰▱▱▱▱▱]   50%  ↺ 12d
    chat           [▰▱▱▱▱▱▱▱▱▱]   10%  ↺ 12d
    completions    [▱▱▱▱▱▱▱▱▱▱]    0%  ↺ 12d  editor only
```

Three allowances come back, and they are not equivalent:

| Allowance | What spends it |
|-----------|----------------|
| `premium` | Premium model requests, what an Empryo turn costs on a paid seat |
| `chat` | Chat requests, what an Empryo turn costs on a Free seat |
| `completions` | Inline ghost text in an editor. **Empryo never spends this.** |

That last row is why `completions` is drawn dimmed and marked *editor only*: a spent completions cap says nothing about whether your next turn will go through, so it must never read as "your plan is spent".

> **Note:**
> Allowances your plan does not carry are omitted rather than drawn empty. A Free seat has no premium budget, and GitHub reports it as zero-of-zero, rendering that verbatim would paint a full red bar for a limit that does not exist.

Where it shows, on every surface that can carry a plan meter:

| Surface | What appears |
|---------|--------------|
| Terminal status bar | Ambient gauge beside the context bar, on a `copilot/*` model |
| `/usage` → Plan | Every allowance, with the gating one marked |
| Desktop status bar | Same gauge; click opens the Usage drawer |
| Desktop Usage drawer | A Copilot card beside the relay and DevPass meters |
| Headless | A `copilot:` warning on stderr after a turn that leaves an allowance nearly spent |

The status bar carries one plan meter, and it follows the model: a `copilot/*` turn shows the Copilot allowance, a `proxy/*` turn shows the relay window. They are never both true at once, so they never compete for the slot.

Numbers are cached for five minutes, they only move monthly, and GitHub's [Acceptable Use Policies](https://docs.github.com/en/site-policy/acceptable-use-policies/github-acceptable-use-policies) discourage chatty automation against their servers.

## Context windows follow your seat

Copilot resells these models and caps them under their native windows. A plain seat gets Claude Opus at 200k where Anthropic serves 1M. Seats with the extended-context capability get more, and GitHub reports the number per account rather than per model.

Empryo reads both numbers Copilot publishes for each model and keeps them apart:

| Copilot reports | Empryo uses it for |
|---|---|
| `max_context_window_tokens` | the window shown in the model picker |
| `max_prompt_tokens` | how much of that window an input may occupy, which is what compaction budgets against |

Collapsing them made every extended-context model read as its input half. If your seat has the 1M capability, open the picker once so the live listing lands, and the larger window appears. Until then Empryo falls back to a table of plain-seat numbers, which is the safe direction to be wrong in: an overstated window means compaction never fires and Copilot rejects the turn.

### Where the list comes from

Three tiers, in order, and a lower one only fills what the one above did not answer:

| Tier | Source | What it contributes |
|---|---|---|
| 1 | `/models` on your seat | The SKUs your plan was sold, with its real windows, prompt ceilings, output caps, effort ladders and endpoint lanes |
| 2 | models.dev | Models Copilot serves *somebody*, including SKUs a plan without the capability never lists. Windows are clamped to what Copilot resells the model at |
| 3 | The shipped table | Last resort, when both are unreachable |

Signing in replaces the seat: `/login copilot` drops the previous account's cached catalog (memory and `copilot-models.json`) and re-lists immediately, so a Free seat's numbers never survive an upgrade or an account switch.

Which rows a human is offered is GitHub's call, not a guess: the listing carries `model_picker_enabled` per model, and Empryo filters on it exactly as the official client does. Where no listing exists to ask, a cold start, a catalog-only row, a name pattern stands in for it (`copilot-search-*`, `exec-agent-*`, `trajectory-*`, the `-picker` / `-secondary` / `-tertiary` shadow deployments).

### The client Empryo identifies as, and the line it will not cross

GitHub answers differently depending on which client is asking, and the difference is not cosmetic. Its own changelog scopes the 1M-token context windows to Visual Studio Code and Copilot CLI, and a seat entitled to them gets them **on the same model ids**: `claude-opus-5` came back as 264k to one client and 1,000,000 to another, from one account, minutes apart.

So Empryo asks the way a current client asks: a recent `Editor-Version` and `Editor-Plugin-Version`, `Copilot-Integration-Id: vscode-chat`, and `X-GitHub-Api-Version: 2026-08-01`. It also says what each request is for, because GitHub reads that too: a model listing asks as `model-access`, a turn as `conversation-agent`, and a request carrying images declares `Copilot-Vision-Request`.

> **Note:**
> The extended windows arrive on the base ids, so there is nothing to pick. A seat with the capability reads 1M in the picker, and a seat without it reads what it was sold. Empryo never invents a `-1m` row.

The `User-Agent` says **`Empryo/<version>`**. It is not GitHub Copilot Chat and does not pretend to be. The Acceptable Use Policies forbid "fraudulently misrepresenting your identity", and every third-party Copilot client names itself here.

For the same reason Empryo sends **no** `VScode-MachineId`, `VScode-SessionId` or `Editor-Device-Id`. The official client sends real ones. Ours could only be invented, and an invented hardware id is a fake installation, not a way of asking. On a real seat, sending them changed nothing about the catalog anyway.

Every part of the identity is overridable, because it is GitHub's to change and yours to test:

| Variable | Default |
|---|---|
| `COPILOT_EDITOR_VERSION` | `vscode/1.130.0` |
| `COPILOT_PLUGIN_VERSION` | `0.58.0` |
| `COPILOT_INTEGRATION_ID` | `vscode-chat` |
| `COPILOT_API_VERSION` | `2026-08-01` |

`COPILOT_INTEGRATION_ID=copilot-cli empryo --list-models copilot` asks GitHub the same question as a different client, and the `source:` line says which tier answered.

### Request discipline

Nothing in GitHub's terms forbids reaching Copilot from a third-party client. Section J (AI Features) governs individual seats and names no required interface. What the terms do name is volume: the Acceptable Use Policies forbid "excessive automated bulk activity" and "undue burden on our servers through automated means", and Section H lets GitHub suspend API access for request rates it judges excessive.

A turn here is shaped like a turn in Copilot CLI or the VS Code extension: one agent loop, one request per step, and `x-initiator: agent` when a step follows a tool result. The model listing is the one call outside that loop, because the UI can trigger it with nobody asking. So that is the one thing Empryo throttles, harder than the client GitHub ships:

| | VS Code extension | Empryo |
|---|---|---|
| Model listing | refresh every 10 min, skipped while the window is inactive | cached 30 min, never twice inside 1 min |
| Turn requests | one per agent step | one per agent step |
| Quota poll | on demand | at most every 5 min, only on a `copilot/*` model |

Concretely:

- **One listing per minute per process, floor.** Every picker open, cache invalidation and login collapses into one question. A sign-in is the single event allowed to bypass it.
- **A 429 or 403 is a stop, not a retry.** Empryo backs off for `Retry-After` (or 15 minutes), serves the seat's last listing from disk, and says so once.
- **A refused credential is probed once.** The GitHub-App fallback runs a single time per token per process, never in a loop.
- **Quota is read at most every five minutes**, and only for a `copilot/*` model.

## Legal position

GitHub restructured its terms on **27 April 2026**. What governs Copilot now depends on your seat:

| Seat | Governed by |
|------|-------------|
| Business, Enterprise | [GitHub Copilot Product Specific Terms](https://github.com/customer-terms/github-copilot-product-specific-terms) |
| Everything else | **Section J (AI Features)** of the [GitHub Terms of Service](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service) |

Section J covers ownership of input and output, model training and the opt-out, disclaimers and indemnity. It places no restriction on which client you use. The constraints that do apply come from elsewhere in the agreement:

#### What actually constrains you (reviewed 2026-08-02)

- **Section C → Acceptable Use Policies.** [AUP §4](https://docs.github.com/en/site-policy/acceptable-use-policies/github-acceptable-use-policies) prohibits "excessive automated bulk activity" and placing undue burden on GitHub's servers. This is the clause a runaway loop trips, not a clause about which editor you use.
- **Section H → API Terms.** Abuse or excessively frequent requests may result in suspension of API access, at GitHub's sole discretion. Sharing tokens to exceed rate limits is prohibited outright.
- **Section J.3 → training.** Unless you opt out in account settings, your prompts and the model's output may be used to develop and improve GitHub's models. Individual licences only. Customer Agreement seats are excluded.

#### What changed since the last review

The previous review (2026-04-04) cited the Copilot section of *Additional Products and Features* as evidence that alternative clients were not prohibited. That section has since been rewritten, it now delegates to Section J or the Product Specific Terms rather than describing client behaviour, so the old citation no longer supports anything. The conclusion is unchanged, the evidence for it moved.

#### Risks

- The endpoint is internal and can change without notice.
- Sustained high volume may trigger rate limiting or an abuse-detection warning.
- Terms can be revised again. The April rewrite is proof of that.

One [community discussion](https://github.com/orgs/community/discussions/178117) has a GitHub employee stating the API is "intended solely for officially supported clients". That is not in any legal document, but it is the clearest signal of intent on record.

## The official route

Since mid-2026 GitHub ships a supported path that did not exist when this integration was written:

| Package | What it is |
|---------|-----------|
| [`@github/copilot`](https://www.npmjs.com/package/@github/copilot) | The official Copilot CLI |
| [`@github/copilot-sdk`](https://www.npmjs.com/package/@github/copilot-sdk) | MIT-licensed TypeScript SDK that drives that CLI over JSON-RPC |

If being on a sanctioned integration matters more to you than running Copilot models inside Empryo's own agent loop, that SDK is the route GitHub endorses. Empryo's provider talks to the chat endpoint directly instead, which is what lets Copilot models use the same tools, Genome and compaction as every other provider.

**Re-verify if GitHub publishes updated terms after 2026-08-02.**
