@jeffreycao/copilot-api 1.2.7 → 1.3.0

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/README.md CHANGED
@@ -21,7 +21,8 @@
21
21
 
22
22
  ---
23
23
 
24
- **Note:** If you are using [opencode](https://github.com/sst/opencode), you do not need this project. Opencode supports GitHub Copilot provider out of the box.
24
+ > [!NOTE]
25
+ > [opencode](https://github.com/sst/opencode) already ships with a built-in GitHub Copilot provider, so you may not need this project for basic usage. This proxy is still useful if you want OpenCode to talk to Copilot through `@ai-sdk/anthropic`, preserve Anthropic Messages semantics for tool use, prefer the native Messages API over plain Chat Completions for Claude-family models, use `gpt-5.4` phase-aware commentary, or fine-tune premium-request usage with small-model fallbacks.
25
26
 
26
27
  ---
27
28
 
@@ -29,9 +30,17 @@
29
30
 
30
31
  A reverse-engineered proxy for the GitHub Copilot API that exposes it as an OpenAI and Anthropic compatible service. This allows you to use GitHub Copilot with any tool that supports the OpenAI Chat Completions API or the Anthropic Messages API, including to power [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview).
31
32
 
33
+ Compared with routing everything through plain Chat Completions compatibility, this proxy can prefer Copilot's native Anthropic-style Messages API for Claude-family models, preserve more native thinking/tool semantics, reduce unnecessary Premium request consumption on warmup or resumed tool turns, and expose phase-aware `gpt-5.4` / `gpt-5.3-codex` responses that are easier for users to follow.
34
+
32
35
  ## Features
33
36
 
34
37
  - **OpenAI & Anthropic Compatibility**: Exposes GitHub Copilot as an OpenAI-compatible (`/v1/responses`, `/v1/chat/completions`, `/v1/models`, `/v1/embeddings`) and Anthropic-compatible (`/v1/messages`) API.
38
+ - **Anthropic-First Routing for Claude Models**: When a model supports Copilot's native `/v1/messages` endpoint, the proxy prefers it over `/responses` or `/chat/completions`, preserving Anthropic-style `tool_use` / `tool_result` flows and more Claude-native behavior.
39
+ - **Fewer Unnecessary Premium Requests**: Reduces wasted premium usage by routing warmup and compact/background requests to `smallModel`, merging `tool_result` follow-ups back into the tool flow, and treating resumed tool turns as continuation traffic instead of fresh premium interactions.
40
+ - **Phase-Aware `gpt-5.4` and `gpt-5.3-codex`**: These models can emit user-friendly commentary before deeper reasoning or tool use, so long-running coding actions are easier to understand instead of appearing as a sudden tool burst.
41
+ - **Claude Native Beta Support**: On the Messages API path, supports Anthropic-native capabilities such as `interleaved-thinking`, `advanced-tool-use`, and `context-management`, which are difficult or unavailable through plain Chat Completions compatibility.
42
+ - **Subagent Marker Integration**: Optional Claude Code and opencode plugins can inject `__SUBAGENT_MARKER__...` and propagate `x-session-id` so subagent traffic keeps the correct root session and agent/user semantics.
43
+ - **OpenCode via `@ai-sdk/anthropic`**: Point OpenCode at this proxy as an Anthropic provider so Anthropic Messages semantics, premium-request optimizations, and Claude-native behavior are preserved end to end.
35
44
  - **Claude Code Integration**: Easily configure and launch [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) to use Copilot as its backend with a simple command-line flag (`--claude-code`).
36
45
  - **Usage Dashboard**: A web-based dashboard to monitor your Copilot API usage, view quotas, and see detailed statistics.
37
46
  - **Rate Limit Control**: Manage API usage with rate-limiting options (`--rate-limit`) and a waiting mechanism (`--wait`) to prevent errors from rapid requests.
@@ -44,6 +53,45 @@ A reverse-engineered proxy for the GitHub Copilot API that exposes it as an Open
44
53
  - **Custom Data Directory**: Change the default data directory (where tokens and config are stored) by setting `COPILOT_API_HOME` environment variable.
45
54
  - **Multi-Provider Anthropic Proxy Routes**: Add global provider configs and call external Anthropic-compatible APIs via `/:provider/v1/messages` and `/:provider/v1/models`.
46
55
 
56
+ ## Better Agent Semantics
57
+
58
+ ### Native Anthropic Messages API when available
59
+
60
+ For models that advertise Copilot support for `/v1/messages`, this project sends the request to the native Messages API first and only falls back to `/responses` or `/chat/completions` when needed.
61
+
62
+ Compared with using Claude-family models only through Chat Completions compatibility, the Messages API path keeps more Anthropic-native behavior, including support for:
63
+
64
+ - `interleaved-thinking-2025-05-14`
65
+ - `advanced-tool-use-2025-11-20`
66
+ - `context-management-2025-06-27`
67
+
68
+ Supported `anthropic-beta` values are filtered and forwarded on the native Messages path, and `interleaved-thinking` is added automatically when a thinking budget is requested for non-adaptive extended thinking.
69
+
70
+ ### Fewer unnecessary Premium requests
71
+
72
+ The proxy includes request-accounting safeguards designed for tool-heavy coding workflows:
73
+
74
+ - tool-less warmup or probe requests can be forced onto `smallModel` so background checks do not spend premium usage;
75
+ - compact/background requests can be downgraded to `smallModel` automatically;
76
+ - mixed `tool_result` + reminder text blocks are merged back into the `tool_result` flow instead of being counted like fresh user turns;
77
+ - `x-initiator` is derived from the latest message or item, not stale assistant history.
78
+
79
+ This helps resumed tool turns continue the existing workflow instead of consuming an extra Premium request as a brand-new interaction.
80
+
81
+ ### Phase-aware `gpt-5.4` and `gpt-5.3-codex`
82
+
83
+ By default, the built-in `extraPrompts` for `gpt-5.4` and `gpt-5.3-codex` enable intermediary-update behavior, and the proxy translates assistant turns into `phase: "commentary"` before tool calls and `phase: "final_answer"` for the final response.
84
+
85
+ That gives clients a short, user-friendly explanation of what the model is about to do before deeper reasoning or tool execution begins.
86
+
87
+ ### Subagent marker integration
88
+
89
+ For subagent-based clients, this project can preserve root session context and correctly classify subagent-originated traffic.
90
+
91
+ The marker flow uses `__SUBAGENT_MARKER__...` inside a `<system-reminder>` block together with root `x-session-id` propagation. When a marker is detected, the proxy can keep the parent session identity, infer `x-initiator: agent`, and tag the interaction as subagent traffic instead of a fresh top-level request.
92
+
93
+ Optional marker producers are included for both Claude Code and opencode; see [Subagent Marker Integration](#subagent-marker-integration-optional) below for setup details.
94
+
47
95
  ## Demo
48
96
 
49
97
  https://github.com/user-attachments/assets/7654b383-669d-4eb9-b23c-06d7aefee8c5
@@ -191,38 +239,47 @@ The following command line options are available for the `start` command:
191
239
  "apiKeys": []
192
240
  },
193
241
  "providers": {
194
- "openrouter": {
242
+ "custom": {
195
243
  "type": "anthropic",
196
244
  "enabled": true,
197
- "baseUrl": "https://openrouter.ai/api",
245
+ "baseUrl": "your-base-url",
198
246
  "apiKey": "sk-your-provider-key",
199
- "defaultTemperature": 0.7,
200
- "defaultTopP": 0.9,
201
- "defaultTopK": 20
247
+ "models": {
248
+ "kimi-k2.5": {
249
+ "temperature": 1,
250
+ "topP": 0.95
251
+ }
252
+ }
202
253
  }
203
254
  },
204
255
  "extraPrompts": {
205
256
  "gpt-5-mini": "<built-in exploration prompt>",
206
- "gpt-5.1-codex-max": "<built-in exploration prompt>"
257
+ "gpt-5.3-codex": "<built-in commentary prompt>",
258
+ "gpt-5.4": "<built-in commentary prompt>"
207
259
  },
208
260
  "smallModel": "gpt-5-mini",
261
+ "responsesApiContextManagementModels": [],
209
262
  "modelReasoningEfforts": {
210
- "gpt-5-mini": "low"
263
+ "gpt-5-mini": "low",
264
+ "gpt-5.3-codex": "xhigh",
265
+ "gpt-5.4": "xhigh"
211
266
  },
212
267
  "useFunctionApplyPatch": true,
213
268
  "compactUseSmallModel": true
214
269
  }
215
270
  ```
216
271
  - **auth.apiKeys:** API keys used for request authentication. Supports multiple keys for rotation. Requests can authenticate with either `x-api-key: <key>` or `Authorization: Bearer <key>`. If empty or omitted, authentication is disabled.
217
- - **extraPrompts:** Map of `model -> prompt` appended to the first system prompt when translating Anthropic-style requests to Copilot. Use this to inject guardrails or guidance per model. Missing default entries are auto-added without overwriting your custom prompts.
218
- - **providers:** Global upstream provider map. Each provider key (for example `openrouter`) becomes a route prefix (`/openrouter/v1/messages`). Currently only `type: "anthropic"` is supported.
272
+ - **extraPrompts:** Map of `model -> prompt` appended to the first system prompt when translating Anthropic-style requests to Copilot. Use this to inject guardrails or guidance per model. Missing default entries are auto-added without overwriting your custom prompts. The built-in prompts for `gpt-5.3-codex` and `gpt-5.4` enable phase-aware commentary, which lets the model emit a short user-facing progress update before tools or deeper reasoning.
273
+ - **providers:** Global upstream provider map. Each provider key (for example `custom`) becomes a route prefix (`/custom/v1/messages`). Currently only `type: "anthropic"` is supported.
219
274
  - `enabled` defaults to `true` if omitted.
220
275
  - `baseUrl` should be provider API base URL without trailing `/v1/messages`.
221
276
  - `apiKey` is used as upstream `x-api-key`.
222
- - `defaultTemperature` (optional): Default temperature value used when the request does not specify one.
223
- - `defaultTopP` (optional): Default top_p value used when the request does not specify one.
224
- - `defaultTopK` (optional): Default top_k value used when the request does not specify one.
225
- - **smallModel:** Fallback model used for tool-less warmup messages (e.g., Claude Code probe requests) to avoid spending premium requests; defaults to `gpt-5-mini`.
277
+ - `models` (optional): Per-model configuration map. Each key is a model ID (matching the model name in requests), and the value is:
278
+ - `temperature` (optional): Default temperature value used when the request does not specify one.
279
+ - `topP` (optional): Default top_p value used when the request does not specify one.
280
+ - `topK` (optional): Default top_k value used when the request does not specify one.
281
+ - **smallModel:** Fallback model used for tool-less warmup messages, compact/background requests, and other short housekeeping turns (for example from Claude Code or OpenCode) to avoid spending premium requests; defaults to `gpt-5-mini`.
282
+ - **responsesApiContextManagementModels:** List of model IDs that should receive Responses API `context_management` compaction instructions. Use this when a model supports server-side context management and you want the proxy to keep only the latest compaction carrier on follow-up turns.
226
283
  - **modelReasoningEfforts:** Per-model `reasoning.effort` sent to the Copilot Responses API. Allowed values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. If a model isn’t listed, `high` is used by default.
227
284
  - **useFunctionApplyPatch:** When `true`, the server will convert any custom tool named `apply_patch` in Responses payloads into an OpenAI-style function tool (`type: "function"`) with a parameter schema so assistants can call it using function-calling semantics to edit files. Set to `false` to leave tools unchanged. Defaults to `true`.
228
285
  - **compactUseSmallModel:** When `true`, detected "compact" requests (e.g., from Claude Code or Opencode compact mode) will automatically use the configured `smallModel` to avoid consuming premium model usage for short/background tasks. Defaults to `true`.
@@ -351,6 +408,96 @@ Or use inline environment variable:
351
408
  COPILOT_API_OAUTH_APP=opencode npx @jeffreycao/copilot-api@latest start
352
409
  ```
353
410
 
411
+ ## Using with OpenCode
412
+
413
+ OpenCode already has a direct GitHub Copilot provider. Use this section when you want OpenCode to point at this proxy through `@ai-sdk/anthropic` and reuse the agent behaviors described earlier in this README.
414
+
415
+ ### Minimal setup
416
+
417
+ Start the proxy with the OpenCode OAuth app:
418
+
419
+ ```sh
420
+ COPILOT_API_OAUTH_APP=opencode npx @jeffreycao/copilot-api@latest start
421
+ ```
422
+
423
+ Then point OpenCode at the proxy with `@ai-sdk/anthropic`.
424
+
425
+ Example `~/.config/opencode/opencode.json`:
426
+
427
+ ```json
428
+ {
429
+ "$schema": "https://opencode.ai/config.json",
430
+ "model": "local/gpt-5.4",
431
+ "small_model": "local/gpt-5-mini",
432
+ "agent": {
433
+ "build": {
434
+ "model": "local/gpt-5.4"
435
+ },
436
+ "plan": {
437
+ "model": "local/gpt-5.4"
438
+ },
439
+ "explore": {
440
+ "model": "local/gpt-5-mini"
441
+ }
442
+ },
443
+ "provider": {
444
+ "local": {
445
+ "npm": "@ai-sdk/anthropic",
446
+ "name": "Copilot API Proxy",
447
+ "options": {
448
+ "baseURL": "http://localhost:4141/v1",
449
+ "apiKey": "dummy"
450
+ },
451
+ "models": {
452
+ "gpt-5.4": {
453
+ "name": "gpt-5.4",
454
+ "modalities": {
455
+ "input": ["text", "image"],
456
+ "output": ["text"]
457
+ },
458
+ "limit": {
459
+ "context": 272000,
460
+ "output": 128000
461
+ }
462
+ },
463
+ "gpt-5-mini": {
464
+ "name": "gpt-5-mini",
465
+ "limit": {
466
+ "context": 128000,
467
+ "output": 64000
468
+ }
469
+ },
470
+ "claude-sonnet-4.6": {
471
+ "id": "claude-sonnet-4.6",
472
+ "name": "claude-sonnet-4.6",
473
+ "modalities": {
474
+ "input": ["text", "image"],
475
+ "output": ["text"]
476
+ },
477
+ "limit": {
478
+ "context": 128000,
479
+ "output": 32000
480
+ },
481
+ "options": {
482
+ "thinking": {
483
+ "type": "enabled",
484
+ "budgetTokens": 31999
485
+ }
486
+ }
487
+ }
488
+ }
489
+ }
490
+ }
491
+ }
492
+ ```
493
+
494
+ Why these fields matter:
495
+
496
+ - `npm: "@ai-sdk/anthropic"` is the important part. OpenCode will speak Anthropic Messages semantics to this proxy instead of flattening everything into OpenAI Chat Completions.
497
+ - `options.baseURL` should be `http://localhost:4141/v1`; the Anthropic SDK will append `/messages`, `/models`, and `/messages/count_tokens` automatically.
498
+ - `model`, `small_model`, and `agent.*.model` let you keep `gpt-5.4` for build/plan work while routing exploration and background work to `gpt-5-mini`.
499
+ - If you enable `auth.apiKeys` in this proxy, replace `dummy` with a real key. Otherwise any placeholder value is fine.
500
+
354
501
  ## Using the Usage Viewer
355
502
 
356
503
  After starting the server, a URL to the Copilot Usage Dashboard will be displayed in your console. This dashboard is a web interface for monitoring your API usage.
@@ -401,8 +548,8 @@ Here is an example `.claude/settings.json` file:
401
548
  "env": {
402
549
  "ANTHROPIC_BASE_URL": "http://localhost:4141",
403
550
  "ANTHROPIC_AUTH_TOKEN": "dummy",
404
- "ANTHROPIC_MODEL": "gpt-5.2",
405
- "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-5.2",
551
+ "ANTHROPIC_MODEL": "gpt-5.4",
552
+ "ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-5.4",
406
553
  "ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-5-mini",
407
554
  "DISABLE_NON_ESSENTIAL_MODEL_CALLS": "1",
408
555
  "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
@@ -422,9 +569,9 @@ You can find more options here: [Claude Code settings](https://docs.anthropic.co
422
569
 
423
570
  You can also read more about IDE integration here: [Add Claude Code to your IDE](https://docs.anthropic.com/en/docs/claude-code/ide-integrations)
424
571
 
425
- ### Subagent Marker Integration (Optional)
572
+ ## Subagent Marker Integration (Optional)
426
573
 
427
- This project supports `x-initiator: agent` for subagent-originated requests.
574
+ This project supports `x-initiator: agent` for subagent-originated requests and can preserve the root session identity with `x-session-id` when a subagent marker is present.
428
575
 
429
576
  #### Claude Code plugin producer (marketplace-based)
430
577
 
@@ -495,9 +642,11 @@ bun run start
495
642
  - `--wait`: Use this with `--rate-limit`. It makes the server wait for the cooldown period to end instead of rejecting the request with an error. This is useful for clients that don't automatically retry on rate limit errors.
496
643
  - If you have a GitHub business or enterprise plan account with Copilot, use the `--account-type` flag (e.g., `--account-type business`). See the [official documentation](https://docs.github.com/en/enterprise-cloud@latest/copilot/managing-copilot/managing-github-copilot-in-your-organization/managing-access-to-github-copilot-in-your-organization/managing-github-copilot-access-to-your-organizations-network#configuring-copilot-subscription-based-network-routing-for-your-enterprise-or-organization) for more details.
497
644
 
498
- ### CLAUDE.md Recommended Content
645
+ ### CLAUDE.md or AGENTS.md Recommended Content
499
646
 
500
- Please include the following in `CLAUDE.md` (for Claude usage):
647
+ Please include the following in `CLAUDE.md` or `AGENTS.md`:
501
648
 
502
- - Prohibited from directly asking questions to users, MUST use AskUserQuestion tool.
503
- - Once you can confirm that the task is complete, MUST use AskUserQuestion tool to make user confirm. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.
649
+ ```
650
+ - Prohibited from directly asking questions to users, MUST use question tool.
651
+ - Once you can confirm that the task is complete, MUST use question tool to make user confirm. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again, after try again, MUST use question tool to make user confirm again.
652
+ ```
@@ -477,12 +477,10 @@ function getProviderConfig(name) {
477
477
  type,
478
478
  baseUrl,
479
479
  apiKey,
480
- defaultTemperature: provider.defaultTemperature,
481
- defaultTopP: provider.defaultTopP,
482
- defaultTopK: provider.defaultTopK
480
+ models: provider.models
483
481
  };
484
482
  }
485
483
 
486
484
  //#endregion
487
485
  export { HTTPError, PATHS, cacheMacMachineId, cacheModels, cacheVSCodeVersion, cacheVsCodeSessionId, copilotBaseUrl, copilotHeaders, ensurePaths, forwardError, generateRequestIdFromPayload, getConfig, getCopilotUsage, getExtraPromptForModel, getGitHubApiBaseUrl, getOauthAppConfig, getOauthUrls, getProviderConfig, getReasoningEffortForModel, getRootSessionId, getSmallModel, getUUID, githubHeaders, isNullish, isOpencodeOauthApp, isResponsesApiContextManagementModel, mergeConfigWithDefaults, prepareInteractionHeaders, shouldCompactUseSmallModel, sleep, standardHeaders, state };
488
- //# sourceMappingURL=config-CdXB2ktZ.js.map
486
+ //# sourceMappingURL=config-CzJXEvJM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-CzJXEvJM.js","names":["state: State","state","headers: Record<string, string>","headers","errorJson: unknown","vsCodeSessionRefreshTimer: ReturnType<typeof setTimeout> | null","sessionId: string | undefined","defaultConfig: AppConfig","cachedConfig: AppConfig | null","fs"],"sources":["../src/lib/paths.ts","../src/lib/state.ts","../src/lib/api-config.ts","../src/lib/error.ts","../src/services/copilot/get-models.ts","../src/services/get-vscode-version.ts","../src/lib/utils.ts","../src/services/github/get-copilot-usage.ts","../src/lib/config.ts"],"sourcesContent":["import fs from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\n\nconst AUTH_APP = process.env.COPILOT_API_OAUTH_APP?.trim() || \"\"\nconst ENTERPRISE_PREFIX = process.env.COPILOT_API_ENTERPRISE_URL ? \"ent_\" : \"\"\n\nconst DEFAULT_DIR = path.join(os.homedir(), \".local\", \"share\", \"copilot-api\")\nconst APP_DIR = process.env.COPILOT_API_HOME || DEFAULT_DIR\n\nconst GITHUB_TOKEN_PATH = path.join(\n APP_DIR,\n AUTH_APP,\n ENTERPRISE_PREFIX + \"github_token\",\n)\nconst CONFIG_PATH = path.join(APP_DIR, \"config.json\")\n\nexport const PATHS = {\n APP_DIR,\n GITHUB_TOKEN_PATH,\n CONFIG_PATH,\n}\n\nexport async function ensurePaths(): Promise<void> {\n await fs.mkdir(path.join(PATHS.APP_DIR, AUTH_APP), { recursive: true })\n await ensureFile(PATHS.GITHUB_TOKEN_PATH)\n await ensureFile(PATHS.CONFIG_PATH)\n}\n\nasync function ensureFile(filePath: string): Promise<void> {\n try {\n await fs.access(filePath, fs.constants.W_OK)\n } catch {\n await fs.writeFile(filePath, \"\")\n await fs.chmod(filePath, 0o600)\n }\n}\n","import type { ModelsResponse } from \"~/services/copilot/get-models\"\n\nexport interface State {\n githubToken?: string\n copilotToken?: string\n\n accountType: string\n models?: ModelsResponse\n vsCodeVersion?: string\n\n macMachineId?: string\n vsCodeSessionId?: string\n\n manualApprove: boolean\n rateLimitWait: boolean\n showToken: boolean\n\n // Rate limiting configuration\n rateLimitSeconds?: number\n lastRequestTimestamp?: number\n verbose: boolean\n}\n\nexport const state: State = {\n accountType: \"individual\",\n manualApprove: false,\n rateLimitWait: false,\n showToken: false,\n verbose: false,\n}\n","import { randomUUID } from \"node:crypto\"\n\nimport type { State } from \"./state\"\n\nexport const isOpencodeOauthApp = (): boolean => {\n return process.env.COPILOT_API_OAUTH_APP?.trim() === \"opencode\"\n}\n\nexport const normalizeDomain = (input: string): string => {\n return input\n .trim()\n .replace(/^https?:\\/\\//u, \"\")\n .replace(/\\/+$/u, \"\")\n}\n\nexport const getEnterpriseDomain = (): string | null => {\n const raw = (process.env.COPILOT_API_ENTERPRISE_URL ?? \"\").trim()\n if (!raw) return null\n const normalized = normalizeDomain(raw)\n return normalized || null\n}\n\nexport const getGitHubBaseUrl = (): string => {\n const resolvedDomain = getEnterpriseDomain()\n return resolvedDomain ? `https://${resolvedDomain}` : GITHUB_BASE_URL\n}\n\nexport const getGitHubApiBaseUrl = (): string => {\n const resolvedDomain = getEnterpriseDomain()\n return resolvedDomain ?\n `https://${resolvedDomain}/api/v3`\n : GITHUB_API_BASE_URL\n}\n\nexport const getOpencodeOauthHeaders = (): Record<string, string> => {\n return {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"User-Agent\":\n \"opencode/1.2.16 ai-sdk/provider-utils/3.0.21 runtime/bun/1.3.10, opencode/1.2.16\",\n }\n}\n\nexport const getOauthUrls = (): {\n deviceCodeUrl: string\n accessTokenUrl: string\n} => {\n const githubBaseUrl = getGitHubBaseUrl()\n\n return {\n deviceCodeUrl: `${githubBaseUrl}/login/device/code`,\n accessTokenUrl: `${githubBaseUrl}/login/oauth/access_token`,\n }\n}\n\ninterface OauthAppConfig {\n clientId: string\n headers: Record<string, string>\n scope: string\n}\n\nexport const getOauthAppConfig = (): OauthAppConfig => {\n if (isOpencodeOauthApp()) {\n return {\n clientId: OPENCODE_GITHUB_CLIENT_ID,\n headers: getOpencodeOauthHeaders(),\n scope: GITHUB_APP_SCOPES,\n }\n }\n\n return {\n clientId: GITHUB_CLIENT_ID,\n headers: standardHeaders(),\n scope: GITHUB_APP_SCOPES,\n }\n}\n\nexport const prepareInteractionHeaders = (\n sessionId: string | undefined,\n isSubagent: boolean,\n headers: Record<string, string>,\n) => {\n const sendInteractionHeaders = !isOpencodeOauthApp()\n\n if (isSubagent) {\n headers[\"x-initiator\"] = \"agent\"\n if (sendInteractionHeaders) {\n headers[\"x-interaction-type\"] = \"conversation-subagent\"\n }\n }\n\n if (sessionId && sendInteractionHeaders) {\n headers[\"x-interaction-id\"] = sessionId\n }\n}\n\nexport const standardHeaders = () => ({\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n})\n\nconst COPILOT_VERSION = \"0.38.2\"\nconst EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`\nconst USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`\n\nconst API_VERSION = \"2025-10-01\"\n\nexport const copilotBaseUrl = (state: State) => {\n const enterpriseDomain = getEnterpriseDomain()\n if (enterpriseDomain) {\n return `https://copilot-api.${enterpriseDomain}`\n }\n\n return state.accountType === \"individual\" ?\n \"https://api.githubcopilot.com\"\n : `https://api.${state.accountType}.githubcopilot.com`\n}\n\nexport const copilotHeaders = (\n state: State,\n requestId?: string,\n vision: boolean = false,\n) => {\n if (isOpencodeOauthApp()) {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${state.copilotToken}`,\n ...getOpencodeOauthHeaders(),\n \"Openai-Intent\": \"conversation-edits\",\n }\n\n if (vision) headers[\"Copilot-Vision-Request\"] = \"true\"\n\n return headers\n }\n\n const requestIdValue = requestId ?? randomUUID()\n const headers: Record<string, string> = {\n Authorization: `Bearer ${state.copilotToken}`,\n \"content-type\": standardHeaders()[\"content-type\"],\n \"copilot-integration-id\": \"vscode-chat\",\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": EDITOR_PLUGIN_VERSION,\n \"user-agent\": USER_AGENT,\n \"openai-intent\": \"conversation-agent\",\n \"x-github-api-version\": API_VERSION,\n \"x-request-id\": requestIdValue,\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n \"x-agent-task-id\": requestIdValue,\n \"x-interaction-type\": \"conversation-agent\",\n }\n\n if (vision) headers[\"copilot-vision-request\"] = \"true\"\n\n if (state.macMachineId) {\n headers[\"vscode-machineid\"] = state.macMachineId\n }\n\n if (state.vsCodeSessionId) {\n headers[\"vscode-sessionid\"] = state.vsCodeSessionId\n }\n\n return headers\n}\n\nexport const GITHUB_API_BASE_URL = \"https://api.github.com\"\nexport const githubHeaders = (state: State) => ({\n ...standardHeaders(),\n authorization: `token ${state.githubToken}`,\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": EDITOR_PLUGIN_VERSION,\n \"user-agent\": USER_AGENT,\n \"x-github-api-version\": API_VERSION,\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n})\n\nexport const GITHUB_BASE_URL = \"https://github.com\"\nexport const GITHUB_CLIENT_ID = \"Iv1.b507a08c87ecfe98\"\nexport const GITHUB_APP_SCOPES = [\"read:user\"].join(\" \")\nexport const OPENCODE_GITHUB_CLIENT_ID = \"Ov23li8tweQw6odWQebz\"\n","import type { Context } from \"hono\"\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\"\n\nimport consola from \"consola\"\n\nexport class HTTPError extends Error {\n response: Response\n\n constructor(message: string, response: Response) {\n super(message)\n this.response = response\n }\n}\n\nexport async function forwardError(c: Context, error: unknown) {\n consola.error(\"Error occurred:\", error)\n\n if (error instanceof HTTPError) {\n const errorText = await error.response.text()\n let errorJson: unknown\n try {\n errorJson = JSON.parse(errorText)\n } catch {\n errorJson = errorText\n }\n consola.error(\"HTTP error:\", errorJson)\n return c.json(\n {\n error: {\n message: errorText,\n type: \"error\",\n },\n },\n error.response.status as ContentfulStatusCode,\n )\n }\n\n return c.json(\n {\n error: {\n message: (error as Error).message,\n type: \"error\",\n },\n },\n 500,\n )\n}\n","import { copilotBaseUrl, copilotHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const getModels = async () => {\n const response = await fetch(`${copilotBaseUrl(state)}/models`, {\n headers: copilotHeaders(state),\n })\n\n if (!response.ok) throw new HTTPError(\"Failed to get models\", response)\n\n return (await response.json()) as ModelsResponse\n}\n\nexport interface ModelsResponse {\n data: Array<Model>\n object: string\n}\n\ninterface ModelLimits {\n max_context_window_tokens?: number\n max_output_tokens?: number\n max_prompt_tokens?: number\n max_inputs?: number\n}\n\ninterface ModelSupports {\n max_thinking_budget?: number\n min_thinking_budget?: number\n tool_calls?: boolean\n parallel_tool_calls?: boolean\n dimensions?: boolean\n streaming?: boolean\n structured_outputs?: boolean\n vision?: boolean\n adaptive_thinking?: boolean\n}\n\ninterface ModelCapabilities {\n family: string\n limits: ModelLimits\n object: string\n supports: ModelSupports\n tokenizer: string\n type: string\n}\n\nexport interface Model {\n capabilities: ModelCapabilities\n id: string\n model_picker_enabled: boolean\n name: string\n object: string\n preview: boolean\n vendor: string\n version: string\n policy?: {\n state: string\n terms: string\n }\n supported_endpoints?: Array<string>\n}\n","const FALLBACK = \"1.110.1\"\n\nexport async function getVSCodeVersion() {\n await Promise.resolve()\n return FALLBACK\n}\n","import type { Context } from \"hono\"\n\nimport consola from \"consola\"\nimport { createHash, randomUUID } from \"node:crypto\"\nimport { networkInterfaces } from \"node:os\"\n\nimport type { AnthropicMessagesPayload } from \"~/routes/messages/anthropic-types\"\n\nimport { getModels } from \"~/services/copilot/get-models\"\nimport { getVSCodeVersion } from \"~/services/get-vscode-version\"\n\nimport { state } from \"./state\"\n\nexport const sleep = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms)\n })\n\nexport const isNullish = (value: unknown): value is null | undefined =>\n value === null || value === undefined\n\nexport async function cacheModels(): Promise<void> {\n const models = await getModels()\n state.models = models\n}\n\nexport const cacheVSCodeVersion = async () => {\n const response = await getVSCodeVersion()\n state.vsCodeVersion = response\n\n consola.info(`Using VSCode version: ${response}`)\n}\n\nconst invalidMacAddresses = new Set([\n \"00:00:00:00:00:00\",\n \"ff:ff:ff:ff:ff:ff\",\n \"ac:de:48:00:11:22\",\n])\n\nfunction validateMacAddress(candidate: string): boolean {\n const tempCandidate = candidate.replaceAll(\"-\", \":\").toLowerCase()\n return !invalidMacAddresses.has(tempCandidate)\n}\n\nexport function getMac(): string | null {\n const ifaces = networkInterfaces()\n // eslint-disable-next-line guard-for-in\n for (const name in ifaces) {\n const networkInterface = ifaces[name]\n if (networkInterface) {\n for (const { mac } of networkInterface) {\n if (validateMacAddress(mac)) {\n return mac\n }\n }\n }\n }\n return null\n}\n\nexport const cacheMacMachineId = () => {\n const macAddress = getMac() ?? randomUUID()\n state.macMachineId = createHash(\"sha256\")\n .update(macAddress, \"utf8\")\n .digest(\"hex\")\n consola.debug(`Using machine ID: ${state.macMachineId}`)\n}\n\nconst SESSION_REFRESH_BASE_MS = 60 * 60 * 1000\nconst SESSION_REFRESH_JITTER_MS = 20 * 60 * 1000\nlet vsCodeSessionRefreshTimer: ReturnType<typeof setTimeout> | null = null\n\nconst generateSessionId = () => {\n state.vsCodeSessionId = randomUUID() + Date.now().toString()\n consola.debug(`Generated VSCode session ID: ${state.vsCodeSessionId}`)\n}\n\nexport const stopVsCodeSessionRefreshLoop = () => {\n if (vsCodeSessionRefreshTimer) {\n clearTimeout(vsCodeSessionRefreshTimer)\n vsCodeSessionRefreshTimer = null\n }\n}\n\nconst scheduleSessionIdRefresh = () => {\n const randomDelay = Math.floor(Math.random() * SESSION_REFRESH_JITTER_MS)\n const delay = SESSION_REFRESH_BASE_MS + randomDelay\n consola.debug(\n `Scheduling next VSCode session ID refresh in ${Math.round(\n delay / 1000,\n )} seconds`,\n )\n\n stopVsCodeSessionRefreshLoop()\n vsCodeSessionRefreshTimer = setTimeout(() => {\n try {\n generateSessionId()\n } catch (error) {\n consola.error(\"Failed to refresh session ID, rescheduling...\", error)\n } finally {\n scheduleSessionIdRefresh()\n }\n }, delay)\n}\n\nexport const cacheVsCodeSessionId = () => {\n stopVsCodeSessionRefreshLoop()\n generateSessionId()\n scheduleSessionIdRefresh()\n}\n\ninterface PayloadMessage {\n role?: string\n content?: string | Array<{ type?: string; text?: string }> | null\n type?: string\n}\n\nconst findLastUserContent = (\n messages: Array<PayloadMessage>,\n): string | null => {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.role === \"user\" && msg.content) {\n if (typeof msg.content === \"string\") {\n return msg.content\n } else if (Array.isArray(msg.content)) {\n const array = msg.content\n .filter((n) => n.type !== \"tool_result\")\n .map((n) => ({ ...n, cache_control: undefined }))\n if (array.length > 0) {\n return JSON.stringify(array)\n }\n }\n }\n }\n return null\n}\n\nexport const generateRequestIdFromPayload = (\n payload: {\n messages: string | Array<PayloadMessage> | undefined\n },\n sessionId?: string,\n): string => {\n const messages = payload.messages\n if (messages) {\n const lastUserContent =\n typeof messages === \"string\" ? messages : findLastUserContent(messages)\n\n if (lastUserContent) {\n return getUUID(\n (sessionId ?? \"\") + (state.macMachineId ?? \"\") + lastUserContent,\n )\n }\n }\n\n return randomUUID()\n}\n\nexport const getRootSessionId = (\n anthropicPayload: AnthropicMessagesPayload,\n c: Context,\n): string | undefined => {\n let sessionId: string | undefined\n if (anthropicPayload.metadata?.user_id) {\n const sessionMatch = new RegExp(/_session_(.+)$/).exec(\n anthropicPayload.metadata.user_id,\n )\n sessionId = sessionMatch ? sessionMatch[1] : undefined\n } else {\n sessionId = c.req.header(\"x-session-id\")\n }\n if (sessionId) {\n return getUUID(sessionId)\n }\n return sessionId\n}\n\nexport const getUUID = (content: string): string => {\n const hash = createHash(\"sha256\").update(content).digest(\"hex\")\n const hash32 = hash.slice(0, 32)\n return `${hash32.slice(0, 8)}-${hash32.slice(8, 12)}-${hash32.slice(12, 16)}-${hash32.slice(16, 20)}-${hash32.slice(20)}`\n}\n","import { getGitHubApiBaseUrl, githubHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const getCopilotUsage = async (): Promise<CopilotUsageResponse> => {\n const response = await fetch(\n `${getGitHubApiBaseUrl()}/copilot_internal/user`,\n {\n headers: githubHeaders(state),\n },\n )\n\n if (!response.ok) {\n throw new HTTPError(\"Failed to get Copilot usage\", response)\n }\n\n return (await response.json()) as CopilotUsageResponse\n}\n\nexport interface QuotaDetail {\n entitlement: number\n overage_count: number\n overage_permitted: boolean\n percent_remaining: number\n quota_id: string\n quota_remaining: number\n remaining: number\n unlimited: boolean\n}\n\ninterface QuotaSnapshots {\n chat: QuotaDetail\n completions: QuotaDetail\n premium_interactions: QuotaDetail\n}\n\ninterface CopilotUsageResponse {\n access_type_sku: string\n analytics_tracking_id: string\n assigned_date: string\n can_signup_for_limited: boolean\n chat_enabled: boolean\n copilot_plan: string\n organization_login_list: Array<unknown>\n organization_list: Array<unknown>\n quota_reset_date: string\n quota_snapshots: QuotaSnapshots\n}\n","import consola from \"consola\"\nimport fs from \"node:fs\"\n\nimport { PATHS } from \"./paths\"\n\nexport interface AppConfig {\n auth?: {\n apiKeys?: Array<string>\n }\n providers?: Record<string, ProviderConfig>\n extraPrompts?: Record<string, string>\n smallModel?: string\n responsesApiContextManagementModels?: Array<string>\n modelReasoningEfforts?: Record<\n string,\n \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\"\n >\n useFunctionApplyPatch?: boolean\n compactUseSmallModel?: boolean\n}\n\nexport interface ModelConfig {\n temperature?: number\n topP?: number\n topK?: number\n}\n\nexport interface ProviderConfig {\n type?: string\n enabled?: boolean\n baseUrl?: string\n apiKey?: string\n models?: Record<string, ModelConfig>\n}\n\nexport interface ResolvedProviderConfig {\n name: string\n type: \"anthropic\"\n baseUrl: string\n apiKey: string\n models?: Record<string, ModelConfig>\n}\n\nconst gpt5ExplorationPrompt = `## Exploration and reading files\n- **Think first.** Before any tool call, decide ALL files/resources you will need.\n- **Batch everything.** If you need multiple files (even from different places), read them together.\n- **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.\n- **Only make sequential calls if you truly cannot know the next file without seeing a result first.**\n- **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`\n\nconst gpt5CommentaryPrompt = `# Working with the user\n\nYou interact with the user through a terminal. You have 2 ways of communicating with the users: \n- Share intermediary updates in \\`commentary\\` channel. \n- After you have completed all your work, send a message to the \\`final\\` channel. \n\n## Intermediary updates\n\n- Intermediary updates go to the \\`commentary\\` channel.\n- User updates are short updates while you are working, they are NOT final answers.\n- You use 1-2 sentence user updates to communicate progress and new information to the user as you are doing work.\n- Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements (“Done —”, “Got it”, “Great question, ”) or framing phrases.\n- You provide user updates frequently, every 20s.\n- Before exploring or doing substantial work, you start with a user update acknowledging the request and explaining your first step. You should include your understanding of the user request and explain what you will do. Avoid commenting on the request or using starters such as \"Got it -\" or \"Understood -\" etc.\n- When exploring, e.g. searching, reading files, you provide user updates as you go, every 20s, explaining what context you are gathering and what you've learned. Vary your sentence structure when providing these updates to avoid sounding repetitive - in particular, don't start each sentence the same way.\n- After you have sufficient context, and the work is substantial, you provide a longer plan (this is the only user update that may be longer than 2 sentences and can contain formatting).\n- Before performing file edits of any kind, you provide updates explaining what edits you are making.\n- As you are thinking, you very frequently provide updates even if not taking any actions, informing the user of your progress. You interrupt your thinking and send multiple updates in a row if thinking for more than 100 words.\n- Tone of your updates MUST match your personality.`\n\nconst defaultConfig: AppConfig = {\n auth: {\n apiKeys: [],\n },\n providers: {},\n extraPrompts: {\n \"gpt-5-mini\": gpt5ExplorationPrompt,\n \"gpt-5.3-codex\": gpt5CommentaryPrompt,\n \"gpt-5.4\": gpt5CommentaryPrompt,\n },\n smallModel: \"gpt-5-mini\",\n responsesApiContextManagementModels: [],\n modelReasoningEfforts: {\n \"gpt-5-mini\": \"low\",\n \"gpt-5.3-codex\": \"xhigh\",\n \"gpt-5.4\": \"xhigh\",\n },\n useFunctionApplyPatch: true,\n compactUseSmallModel: true,\n}\n\nlet cachedConfig: AppConfig | null = null\n\nfunction ensureConfigFile(): void {\n try {\n fs.accessSync(PATHS.CONFIG_PATH, fs.constants.R_OK | fs.constants.W_OK)\n } catch {\n fs.mkdirSync(PATHS.APP_DIR, { recursive: true })\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n try {\n fs.chmodSync(PATHS.CONFIG_PATH, 0o600)\n } catch {\n return\n }\n }\n}\n\nfunction readConfigFromDisk(): AppConfig {\n ensureConfigFile()\n try {\n const raw = fs.readFileSync(PATHS.CONFIG_PATH, \"utf8\")\n if (!raw.trim()) {\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n return defaultConfig\n }\n return JSON.parse(raw) as AppConfig\n } catch (error) {\n consola.error(\"Failed to read config file, using default config\", error)\n return defaultConfig\n }\n}\n\nfunction mergeDefaultConfig(config: AppConfig): {\n mergedConfig: AppConfig\n changed: boolean\n} {\n const extraPrompts = config.extraPrompts ?? {}\n const defaultExtraPrompts = defaultConfig.extraPrompts ?? {}\n const modelReasoningEfforts = config.modelReasoningEfforts ?? {}\n const defaultModelReasoningEfforts = defaultConfig.modelReasoningEfforts ?? {}\n\n const missingExtraPromptModels = Object.keys(defaultExtraPrompts).filter(\n (model) => !Object.hasOwn(extraPrompts, model),\n )\n\n const missingReasoningEffortModels = Object.keys(\n defaultModelReasoningEfforts,\n ).filter((model) => !Object.hasOwn(modelReasoningEfforts, model))\n\n const hasExtraPromptChanges = missingExtraPromptModels.length > 0\n const hasReasoningEffortChanges = missingReasoningEffortModels.length > 0\n\n if (!hasExtraPromptChanges && !hasReasoningEffortChanges) {\n return { mergedConfig: config, changed: false }\n }\n\n return {\n mergedConfig: {\n ...config,\n extraPrompts: {\n ...defaultExtraPrompts,\n ...extraPrompts,\n },\n modelReasoningEfforts: {\n ...defaultModelReasoningEfforts,\n ...modelReasoningEfforts,\n },\n },\n changed: true,\n }\n}\n\nexport function mergeConfigWithDefaults(): AppConfig {\n const config = readConfigFromDisk()\n const { mergedConfig, changed } = mergeDefaultConfig(config)\n\n if (changed) {\n try {\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(mergedConfig, null, 2)}\\n`,\n \"utf8\",\n )\n } catch (writeError) {\n consola.warn(\n \"Failed to write merged extraPrompts to config file\",\n writeError,\n )\n }\n }\n\n cachedConfig = mergedConfig\n return mergedConfig\n}\n\nexport function getConfig(): AppConfig {\n cachedConfig ??= readConfigFromDisk()\n return cachedConfig\n}\n\nexport function getExtraPromptForModel(model: string): string {\n const config = getConfig()\n return config.extraPrompts?.[model] ?? \"\"\n}\n\nexport function getSmallModel(): string {\n const config = getConfig()\n return config.smallModel ?? \"gpt-5-mini\"\n}\n\nexport function getResponsesApiContextManagementModels(): Array<string> {\n const config = getConfig()\n return (\n config.responsesApiContextManagementModels\n ?? defaultConfig.responsesApiContextManagementModels\n ?? []\n )\n}\n\nexport function isResponsesApiContextManagementModel(model: string): boolean {\n return getResponsesApiContextManagementModels().includes(model)\n}\n\nexport function getReasoningEffortForModel(\n model: string,\n): \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" {\n const config = getConfig()\n return config.modelReasoningEfforts?.[model] ?? \"high\"\n}\n\nexport function shouldCompactUseSmallModel(): boolean {\n const config = getConfig()\n return config.compactUseSmallModel ?? true\n}\n\nexport function normalizeProviderBaseUrl(url: string): string {\n return url.trim().replace(/\\/+$/u, \"\")\n}\n\nexport function getProviderConfig(name: string): ResolvedProviderConfig | null {\n const providerName = name.trim()\n if (!providerName) {\n return null\n }\n\n const config = getConfig()\n const provider = config.providers?.[providerName]\n if (!provider) {\n return null\n }\n\n if (provider.enabled === false) {\n return null\n }\n\n const type = provider.type ?? \"anthropic\"\n if (type !== \"anthropic\") {\n consola.warn(\n `Provider ${providerName} is ignored because only anthropic type is supported`,\n )\n return null\n }\n\n const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? \"\")\n const apiKey = (provider.apiKey ?? \"\").trim()\n if (!baseUrl || !apiKey) {\n consola.warn(\n `Provider ${providerName} is enabled but missing baseUrl or apiKey`,\n )\n return null\n }\n\n return {\n name: providerName,\n type,\n baseUrl,\n apiKey,\n models: provider.models,\n }\n}\n\nexport function listEnabledProviders(): Array<string> {\n const config = getConfig()\n const providerNames = Object.keys(config.providers ?? {})\n return providerNames.filter((name) => getProviderConfig(name) !== null)\n}\n"],"mappings":"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"}
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { HTTPError, PATHS, cacheMacMachineId, cacheModels, cacheVSCodeVersion, cacheVsCodeSessionId, ensurePaths, getCopilotUsage, getGitHubApiBaseUrl, getOauthAppConfig, getOauthUrls, githubHeaders, isOpencodeOauthApp, mergeConfigWithDefaults, sleep, standardHeaders, state } from "./config-CdXB2ktZ.js";
2
+ import { HTTPError, PATHS, cacheMacMachineId, cacheModels, cacheVSCodeVersion, cacheVsCodeSessionId, ensurePaths, getCopilotUsage, getGitHubApiBaseUrl, getOauthAppConfig, getOauthUrls, githubHeaders, isOpencodeOauthApp, mergeConfigWithDefaults, sleep, standardHeaders, state } from "./config-CzJXEvJM.js";
3
3
  import { defineCommand, runMain } from "citty";
4
4
  import consola from "consola";
5
5
  import fs from "node:fs/promises";
@@ -464,7 +464,7 @@ async function runServer(options) {
464
464
  }
465
465
  }
466
466
  consola.box(`🌐 Usage Viewer: ${serverUrl}/usage-viewer?endpoint=${serverUrl}/usage`);
467
- const { server } = await import("./server-CtNjbKuO.js");
467
+ const { server } = await import("./server-Da1oLLcF.js");
468
468
  serve({
469
469
  fetch: server.fetch,
470
470
  port: options.port,
@@ -1,4 +1,4 @@
1
- import { HTTPError, PATHS, cacheModels, copilotBaseUrl, copilotHeaders, forwardError, generateRequestIdFromPayload, getConfig, getCopilotUsage, getExtraPromptForModel, getProviderConfig, getReasoningEffortForModel, getRootSessionId, getSmallModel, getUUID, isNullish, isResponsesApiContextManagementModel, prepareInteractionHeaders, shouldCompactUseSmallModel, sleep, state } from "./config-CdXB2ktZ.js";
1
+ import { HTTPError, PATHS, cacheModels, copilotBaseUrl, copilotHeaders, forwardError, generateRequestIdFromPayload, getConfig, getCopilotUsage, getExtraPromptForModel, getProviderConfig, getReasoningEffortForModel, getRootSessionId, getSmallModel, getUUID, isNullish, isResponsesApiContextManagementModel, prepareInteractionHeaders, shouldCompactUseSmallModel, sleep, state } from "./config-CzJXEvJM.js";
2
2
  import consola from "consola";
3
3
  import path from "node:path";
4
4
  import fs, { readFileSync } from "node:fs";
@@ -526,6 +526,57 @@ embeddingRoutes.post("/", async (c) => {
526
526
  }
527
527
  });
528
528
 
529
+ //#endregion
530
+ //#region src/lib/models.ts
531
+ const findEndpointModel = (sdkModelId) => {
532
+ const models = state.models?.data ?? [];
533
+ const exactMatch = models.find((m) => m.id === sdkModelId);
534
+ if (exactMatch) return exactMatch;
535
+ const normalized = _normalizeSdkModelId(sdkModelId);
536
+ if (!normalized) return;
537
+ const modelName = `claude-${normalized.family}-${normalized.version}`;
538
+ const model = models.find((m) => m.id === modelName);
539
+ if (model) return model;
540
+ };
541
+ /**
542
+ * Normalizes an SDK model ID to extract the model family and version.
543
+ * this method from github copilot extension
544
+ * Examples:
545
+ * - "claude-opus-4-5-20251101" -> { family: "opus", version: "4.5" }
546
+ * - "claude-3-5-sonnet-20241022" -> { family: "sonnet", version: "3.5" }
547
+ * - "claude-sonnet-4-20250514" -> { family: "sonnet", version: "4" }
548
+ * - "claude-haiku-3-5-20250514" -> { family: "haiku", version: "3.5" }
549
+ * - "claude-haiku-4.5" -> { family: "haiku", version: "4.5" }
550
+ */
551
+ const _normalizeSdkModelId = (sdkModelId) => {
552
+ const withoutDate = sdkModelId.toLowerCase().replace(/-\d{8}$/, "");
553
+ const pattern1 = withoutDate.match(/^claude-(\w+)-(\d+)-(\d+)$/);
554
+ if (pattern1) return {
555
+ family: pattern1[1],
556
+ version: `${pattern1[2]}.${pattern1[3]}`
557
+ };
558
+ const pattern2 = withoutDate.match(/^claude-(\d+)-(\d+)-(\w+)$/);
559
+ if (pattern2) return {
560
+ family: pattern2[3],
561
+ version: `${pattern2[1]}.${pattern2[2]}`
562
+ };
563
+ const pattern3 = withoutDate.match(/^claude-(\w+)-(\d+)\.(\d+)$/);
564
+ if (pattern3) return {
565
+ family: pattern3[1],
566
+ version: `${pattern3[2]}.${pattern3[3]}`
567
+ };
568
+ const pattern4 = withoutDate.match(/^claude-(\w+)-(\d+)$/);
569
+ if (pattern4) return {
570
+ family: pattern4[1],
571
+ version: pattern4[2]
572
+ };
573
+ const pattern5 = withoutDate.match(/^claude-(\d+)-(\w+)$/);
574
+ if (pattern5) return {
575
+ family: pattern5[2],
576
+ version: pattern5[1]
577
+ };
578
+ };
579
+
529
580
  //#endregion
530
581
  //#region src/routes/messages/utils.ts
531
582
  function mapOpenAIStopReasonToAnthropic(finishReason) {
@@ -542,7 +593,7 @@ function mapOpenAIStopReasonToAnthropic(finishReason) {
542
593
  //#region src/routes/messages/non-stream-translation.ts
543
594
  const THINKING_TEXT = "Thinking...";
544
595
  function translateToOpenAI(payload) {
545
- const modelId = translateModelName(payload.model);
596
+ const modelId = payload.model;
546
597
  const model = state.models?.data.find((m) => m.id === modelId);
547
598
  const thinkingBudget = getThinkingBudget(payload, model);
548
599
  return {
@@ -563,56 +614,27 @@ function getThinkingBudget(payload, model) {
563
614
  const thinking = payload.thinking;
564
615
  if (model && thinking) {
565
616
  const maxThinkingBudget = Math.min(model.capabilities.supports.max_thinking_budget ?? 0, (model.capabilities.limits.max_output_tokens ?? 0) - 1);
566
- if (maxThinkingBudget > 0 && thinking.budget_tokens !== void 0) {
617
+ thinking.budget_tokens ??= maxThinkingBudget;
618
+ if (maxThinkingBudget > 0) {
567
619
  const budgetTokens = Math.min(thinking.budget_tokens, maxThinkingBudget);
568
620
  return Math.max(budgetTokens, model.capabilities.supports.min_thinking_budget ?? 1024);
569
621
  }
570
622
  }
571
623
  }
572
- function translateModelName(model) {
573
- if (model.startsWith("claude-sonnet-4-")) return model.replace(/^claude-sonnet-4-.*/, "claude-sonnet-4");
574
- else if (model.startsWith("claude-opus-4-")) return model.replace(/^claude-opus-4-.*/, "claude-opus-4");
575
- return model;
576
- }
577
- function translateAnthropicMessagesToOpenAI(payload, modelId, thinkingBudget) {
578
- const systemMessages = handleSystemPrompt(payload.system, modelId, thinkingBudget);
624
+ function translateAnthropicMessagesToOpenAI(payload, modelId, _thinkingBudget) {
625
+ const systemMessages = handleSystemPrompt(payload.system);
579
626
  const otherMessages = payload.messages.flatMap((message) => message.role === "user" ? handleUserMessage(message) : handleAssistantMessage(message, modelId));
580
- if (modelId.startsWith("claude") && thinkingBudget) {
581
- const reminder = "<system-reminder>you MUST follow interleaved_thinking_protocol</system-reminder>";
582
- const firstUserIndex = otherMessages.findIndex((m) => m.role === "user");
583
- if (firstUserIndex !== -1) {
584
- const userMessage = otherMessages[firstUserIndex];
585
- if (typeof userMessage.content === "string") userMessage.content = reminder + "\n\n" + userMessage.content;
586
- else if (Array.isArray(userMessage.content)) userMessage.content = [{
587
- type: "text",
588
- text: reminder
589
- }, ...userMessage.content];
590
- }
591
- }
592
627
  return [...systemMessages, ...otherMessages];
593
628
  }
594
- function handleSystemPrompt(system, modelId, thinkingBudget) {
629
+ function handleSystemPrompt(system) {
595
630
  if (!system) return [];
596
- let extraPrompt = "";
597
- if (modelId.startsWith("claude") && thinkingBudget) extraPrompt = `
598
- <interleaved_thinking_protocol>
599
- ABSOLUTE REQUIREMENT - NON-NEGOTIABLE:
600
- The current thinking_mode is interleaved, Whenever you have the result of a function call, think carefully , MUST output a thinking block
601
- RULES:
602
- Tool result → thinking block (ALWAYS, no exceptions)
603
- This is NOT optional - it is a hard requirement
604
- The thinking block must contain substantive reasoning (minimum 3-5 sentences)
605
- Think about: what the results mean, what to do next, how to answer the user
606
- NEVER skip this step, even if the result seems simple or obvious
607
- </interleaved_thinking_protocol>`;
608
631
  if (typeof system === "string") return [{
609
632
  role: "system",
610
- content: system + extraPrompt
633
+ content: system
611
634
  }];
612
635
  else return [{
613
636
  role: "system",
614
- content: system.map((block, index) => {
615
- if (index === 0) return block.text + extraPrompt;
637
+ content: system.map((block) => {
616
638
  return block.text;
617
639
  }).join("\n\n")
618
640
  }];
@@ -784,7 +806,8 @@ async function handleCountTokens(c) {
784
806
  const anthropicBeta = c.req.header("anthropic-beta");
785
807
  const anthropicPayload = await c.req.json();
786
808
  const openAIPayload = translateToOpenAI(anthropicPayload);
787
- const selectedModel = state.models?.data.find((model) => model.id === anthropicPayload.model);
809
+ const selectedModel = findEndpointModel(anthropicPayload.model);
810
+ anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
788
811
  if (!selectedModel) {
789
812
  consola.warn("Model not found, returning default token count");
790
813
  return c.json({ input_tokens: 1 });
@@ -2124,7 +2147,8 @@ async function handleCompletion(c) {
2124
2147
  const requestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
2125
2148
  logger$5.debug("Generated request ID:", requestId);
2126
2149
  if (state.manualApprove) await awaitApproval();
2127
- const selectedModel = state.models?.data.find((m) => m.id === anthropicPayload.model);
2150
+ const selectedModel = findEndpointModel(anthropicPayload.model);
2151
+ anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
2128
2152
  if (shouldUseMessagesApi(selectedModel)) return await handleWithMessagesApi(c, anthropicPayload, {
2129
2153
  anthropicBetaHeader: anthropicBeta,
2130
2154
  subagentMarker,
@@ -2447,6 +2471,18 @@ const FORWARDABLE_HEADERS = [
2447
2471
  "accept",
2448
2472
  "user-agent"
2449
2473
  ];
2474
+ const STRIPPED_RESPONSE_HEADERS = [
2475
+ "connection",
2476
+ "content-encoding",
2477
+ "content-length",
2478
+ "keep-alive",
2479
+ "proxy-authenticate",
2480
+ "proxy-authorization",
2481
+ "te",
2482
+ "trailer",
2483
+ "transfer-encoding",
2484
+ "upgrade"
2485
+ ];
2450
2486
  function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
2451
2487
  const headers = {
2452
2488
  "content-type": "application/json",
@@ -2459,6 +2495,15 @@ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
2459
2495
  }
2460
2496
  return headers;
2461
2497
  }
2498
+ function createProviderProxyResponse(upstreamResponse) {
2499
+ const headers = new Headers(upstreamResponse.headers);
2500
+ for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
2501
+ return new Response(upstreamResponse.body, {
2502
+ headers,
2503
+ status: upstreamResponse.status,
2504
+ statusText: upstreamResponse.statusText
2505
+ });
2506
+ }
2462
2507
  async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
2463
2508
  return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
2464
2509
  method: "POST",
@@ -2485,9 +2530,10 @@ async function handleProviderMessages(c) {
2485
2530
  } }, 404);
2486
2531
  try {
2487
2532
  const payload = await c.req.json();
2488
- payload.temperature ??= providerConfig.defaultTemperature;
2489
- payload.top_p ??= providerConfig.defaultTopP;
2490
- payload.top_k ??= providerConfig.defaultTopK;
2533
+ const modelConfig = providerConfig.models?.[payload.model];
2534
+ payload.temperature ??= modelConfig?.temperature;
2535
+ payload.top_p ??= modelConfig?.topP;
2536
+ payload.top_k ??= modelConfig?.topK;
2491
2537
  logger$3.debug("provider.messages.request", JSON.stringify({
2492
2538
  payload,
2493
2539
  provider
@@ -2508,7 +2554,7 @@ async function handleProviderMessages(c) {
2508
2554
  }
2509
2555
  });
2510
2556
  }
2511
- return upstreamResponse;
2557
+ return createProviderProxyResponse(upstreamResponse);
2512
2558
  } catch (error) {
2513
2559
  logger$3.error("provider.messages.error", {
2514
2560
  provider,
@@ -2553,7 +2599,7 @@ providerModelRoutes.get("/", async (c) => {
2553
2599
  provider,
2554
2600
  statusCode: upstreamResponse.status
2555
2601
  });
2556
- return upstreamResponse;
2602
+ return createProviderProxyResponse(upstreamResponse);
2557
2603
  } catch (error) {
2558
2604
  logger$2.error("provider.models.error", {
2559
2605
  provider,
@@ -2753,4 +2799,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
2753
2799
 
2754
2800
  //#endregion
2755
2801
  export { server };
2756
- //# sourceMappingURL=server-CtNjbKuO.js.map
2802
+ //# sourceMappingURL=server-Da1oLLcF.js.map