copilot-reverse 0.7.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/changes.js +15 -15
- package/dist/core/model-canonical.js +35 -0
- package/dist/tui/setup/clients.js +8 -4
- package/dist/version.js +1 -1
- package/dist/worker/anthropic-server.js +5 -2
- package/dist/worker/router.js +4 -3
- package/package.json +1 -1
package/dist/changes.js
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
export const APP_CHANGES = [
|
|
2
|
+
{
|
|
3
|
+
"version": "0.8.1",
|
|
4
|
+
"date": "2026-06-29",
|
|
5
|
+
"summary": "fix(release): update CHANGELOG before the build so the just-released version appears in `/changes`. gen-changes.mjs runs in prebuild and reads CHANGELOG, but the workflow appended the new entry only after publish — so each release's own notes lagged one version behind. CHANGELOG is now written before build; changesets are still consumed after publish."
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"version": "0.8.0",
|
|
9
|
+
"date": "2026-06-29",
|
|
10
|
+
"summary": "Map Copilot model ids to the canonical ids Claude Code's native /model picker recognises, so models show friendly names and the 1M-context badge instead of bare ids. Outbound, `/anthropic/v1/models` dashes claude ids (`claude-opus-4.8` → `claude-opus-4-8[1m]`) and tags opus 4.6/4.7/4.8 + sonnet 4.6 as 1M; setup's default ANTHROPIC_MODEL is dashed the same way so the picker matches it; inbound, requests resolve back to the real Copilot model. GPT/o3 pass through unchanged."
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.7.0",
|
|
14
|
+
"date": "2026-06-29",
|
|
15
|
+
"summary": "feat(tui): `/metrics` now reports token usage (in/out) and an estimated cost per model and overall — the worker records prompt/completion tokens for every request (persisted in SQLite), and cost is a list-price estimate (Copilot is flat-fee). User messages also get a highlighted bar in the transcript so they stand out from muted system notes and assistant output."
|
|
16
|
+
},
|
|
2
17
|
{
|
|
3
18
|
"version": "0.6.0",
|
|
4
19
|
"date": "2026-06-29",
|
|
@@ -33,20 +48,5 @@ export const APP_CHANGES = [
|
|
|
33
48
|
"version": "0.5.0",
|
|
34
49
|
"date": "2026-06-28",
|
|
35
50
|
"summary": "Add a GitHub-token heartbeat: the supervisor now re-checks every ~60s whether the stored GitHub login still works, and the TUI footer shows a live `github ✓` / `✗ /login` badge — so an expired or revoked login surfaces within ~60s instead of only on the next failed request or a manual `/status`. A transient network/rate-limit hiccup is distinguished from a real auth failure, so the badge never flips on a single blip."
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"version": "0.4.0",
|
|
39
|
-
"date": "2026-06-26",
|
|
40
|
-
"summary": "Codex `/responses` support, web search via Microsoft Web IQ, and a tool-call recovery fix:"
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
"version": "0.3.0",
|
|
44
|
-
"date": "2026-06-26",
|
|
45
|
-
"summary": "Restore `web_search` and `web_fetch` for Claude Code through the gateway: the worker now runs these tools internally against Microsoft Web IQ in a transparent agentic loop, and a new `/web-search-support` command stores the WebIQ API key."
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"version": "0.2.1",
|
|
49
|
-
"date": "2026-06-25",
|
|
50
|
-
"summary": "Fix `/login` hanging with no output: the device-code prompt is now shown immediately while authorization is pending, instead of being buffered behind the blocking token poll."
|
|
51
51
|
}
|
|
52
52
|
];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Maps Copilot's model ids to the canonical ids Claude Code recognises, so its native /model picker
|
|
2
|
+
// lights up with friendly names, tier grouping, and the 1M-context badge instead of bare ids.
|
|
3
|
+
//
|
|
4
|
+
// Copilot advertises DOTTED ids (claude-opus-4.8); Claude Code's built-in table keys on DASHED ids
|
|
5
|
+
// (claude-opus-4-8) and shows the 1M badge only when the id ends with the [1m] suffix. So OUTBOUND
|
|
6
|
+
// (/v1/models, ANTHROPIC_MODEL) we dash every claude id + add [1m] for the families Claude Code knows
|
|
7
|
+
// to be 1M; INBOUND the proxy strips [1m] and fuzzy-maps the dashed id back to Copilot's dotted id
|
|
8
|
+
// (see bestModelMatch). Non-claude ids (gpt*, o3*) have no canonical form → pass through untouched.
|
|
9
|
+
export const ONE_M_SUFFIX = "[1m]";
|
|
10
|
+
// Dashed canonical ids whose Claude Code table carries a 1M window — only these get the [1m] badge.
|
|
11
|
+
// Everything else stays at its default window. Anchored on the probed v2.1.195 binary table.
|
|
12
|
+
const ONE_M_MODELS = new Set(["claude-opus-4-6", "claude-opus-4-7", "claude-opus-4-8", "claude-sonnet-4-6"]);
|
|
13
|
+
// claude-<family>-<major>-<minor> -> "Family Major.Minor" (e.g. claude-opus-4-8 -> "Opus 4.8").
|
|
14
|
+
function displayName(dashed) {
|
|
15
|
+
const m = /^claude-(opus|sonnet|haiku)-(\d+)-(\d+)$/.exec(dashed);
|
|
16
|
+
if (!m)
|
|
17
|
+
return dashed;
|
|
18
|
+
const [, fam, maj, min] = m;
|
|
19
|
+
return `${fam[0].toUpperCase()}${fam.slice(1)} ${maj}.${min}`;
|
|
20
|
+
}
|
|
21
|
+
// Outbound: Copilot id -> the id+display Claude Code's picker understands. Claude ids are dashed to the
|
|
22
|
+
// canonical form; the 1M families get the [1m] suffix so the picker shows the badge and sizes context
|
|
23
|
+
// to 1M. Non-claude ids echo back as-is so they still appear, just without native metadata.
|
|
24
|
+
export function toCanonical(copilotId) {
|
|
25
|
+
if (!copilotId.startsWith("claude-"))
|
|
26
|
+
return { id: copilotId, display_name: copilotId };
|
|
27
|
+
const dashed = copilotId.replace(/\./g, "-");
|
|
28
|
+
const id = ONE_M_MODELS.has(dashed) ? `${dashed}${ONE_M_SUFFIX}` : dashed;
|
|
29
|
+
return { id, display_name: displayName(dashed) };
|
|
30
|
+
}
|
|
31
|
+
// Inbound: drop the [1m] picker suffix. The dashed canonical id then resolves back to Copilot's
|
|
32
|
+
// dotted id via the router's exact map + fuzzy fallback; nothing else to do here.
|
|
33
|
+
export function stripOneM(model) {
|
|
34
|
+
return model.endsWith(ONE_M_SUFFIX) ? model.slice(0, -ONE_M_SUFFIX.length) : model;
|
|
35
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toCanonical } from "../../core/model-canonical.js";
|
|
1
2
|
export function claudeCodeConfig(e) {
|
|
2
3
|
const base = `http://${e.host}:${e.port}/anthropic`;
|
|
3
4
|
return {
|
|
@@ -6,11 +7,14 @@ export function claudeCodeConfig(e) {
|
|
|
6
7
|
};
|
|
7
8
|
}
|
|
8
9
|
export const ONE_M_SUFFIX = "[1m]";
|
|
9
|
-
// Claude Code switches to its 1M
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
10
|
+
// Claude Code switches to its 1M window only when ANTHROPIC_MODEL ends with `[1m]`, and only matches
|
|
11
|
+
// the model to its native picker entry when the id is the DASHED canonical form it knows
|
|
12
|
+
// (claude-opus-4-8, not Copilot's dotted claude-opus-4.8). Route the default model through toCanonical
|
|
13
|
+
// so it's both dashed and 1M-badged for the known families; for non-claude ids keep the legacy
|
|
14
|
+
// context-window suffix. The proxy strips [1m] + fuzzy-maps back to Copilot before forwarding.
|
|
13
15
|
export function withClaude1mSuffix(model, contextWindow) {
|
|
16
|
+
if (model.startsWith("claude-"))
|
|
17
|
+
return toCanonical(model).id;
|
|
14
18
|
return contextWindow && contextWindow > 800_000 && contextWindow < 1_500_000 && !model.endsWith(ONE_M_SUFFIX)
|
|
15
19
|
? `${model}${ONE_M_SUFFIX}`
|
|
16
20
|
: model;
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// AUTO-GENERATED by scripts/gen-version.mjs from package.json — do not edit.
|
|
2
|
-
export const APP_VERSION = "0.
|
|
2
|
+
export const APP_VERSION = "0.8.1";
|
|
@@ -5,6 +5,7 @@ import { errorHint } from "./errors.js";
|
|
|
5
5
|
import { CopilotAuthError } from "../providers/copilot/token.js";
|
|
6
6
|
import { isGatewayTool } from "../core/server-tools.js";
|
|
7
7
|
import { RunawayGuard } from "../core/stream-guard.js";
|
|
8
|
+
import { toCanonical } from "../core/model-canonical.js";
|
|
8
9
|
const frame = (event, data) => `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
9
10
|
const safeJson = (s) => { try {
|
|
10
11
|
return JSON.parse(s);
|
|
@@ -22,9 +23,11 @@ const MAX_TOOL_ITERS = 5;
|
|
|
22
23
|
const STREAM_DEADLINE_MS = 120_000;
|
|
23
24
|
export function mountAnthropic(app, router, onMetric, runner) {
|
|
24
25
|
// Model discovery — Anthropic list shape. Claude Desktop / Anthropic-protocol clients GET this
|
|
25
|
-
// before chatting; without it they 404 on the connection test.
|
|
26
|
+
// before chatting; without it they 404 on the connection test. Claude families are mapped to the
|
|
27
|
+
// canonical id + display Claude Code recognises (with [1m] for 1M models) so its native picker shows
|
|
28
|
+
// friendly names + the 1M badge; non-claude ids pass through. resolveModel maps them back inbound.
|
|
26
29
|
app.get("/anthropic/v1/models", (_req, res) => {
|
|
27
|
-
res.json({ data: router.listModels().map((id) => ({ type: "model", id
|
|
30
|
+
res.json({ data: router.listModels().map((id) => ({ type: "model", ...toCanonical(id) })), has_more: false });
|
|
28
31
|
});
|
|
29
32
|
// Anthropic clients (Claude Code) call this to size the prompt and decide when to auto-compact.
|
|
30
33
|
app.post("/anthropic/v1/messages/count_tokens", (req, res) => {
|
package/dist/worker/router.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { bestModelMatch } from "../core/fuzzy.js";
|
|
2
2
|
import { FALLBACK_MODELS } from "../providers/copilot/models.js";
|
|
3
|
+
import { stripOneM } from "../core/model-canonical.js";
|
|
3
4
|
// M1: single provider. Model name is remapped to the provider's actual id.
|
|
4
5
|
export class Router {
|
|
5
6
|
providers;
|
|
@@ -16,12 +17,12 @@ export class Router {
|
|
|
16
17
|
listModels() { return this.available.length ? this.available : FALLBACK_MODELS; }
|
|
17
18
|
resolveModel(requested) {
|
|
18
19
|
// Claude Code appends [1m] to signal its 1M context window; Copilot doesn't know that id, so
|
|
19
|
-
// strip it back to the
|
|
20
|
-
requested =
|
|
20
|
+
// strip it back to the canonical model before mapping/forwarding.
|
|
21
|
+
requested = stripOneM(requested);
|
|
21
22
|
const mapped = this.modelMap[requested];
|
|
22
23
|
if (mapped)
|
|
23
24
|
return mapped;
|
|
24
|
-
// Fuzzy-match a near-miss id (e.g. claude-opus-4-8
|
|
25
|
+
// Fuzzy-match a near-miss id (e.g. canonical claude-opus-4-8 -> Copilot claude-opus-4.8) to a real model.
|
|
25
26
|
if (this.available.length) {
|
|
26
27
|
const match = bestModelMatch(requested, this.available);
|
|
27
28
|
if (match)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "copilot-reverse",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Interactive terminal app that exposes your GitHub Copilot subscription as local OpenAI- and Anthropic-compatible endpoints, with a self-healing daemon and a built-in assistant.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|