cartethyia 2.7.5 → 2.7.6
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/.env.example +4 -1
- package/README.md +1 -1
- package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-D5rol2j2.js → ConsoleLogPage-KRYjHZsX.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-B5nZvmf0.js → InjectSystemPromptPanel-d5v3K_Yc.js} +245 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-DnQPSIA8.js → ModelPicker-C6WlpU5E.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-5SnUSy3j.js → ProxyPage-DxjvUzSD.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-BJtRZ4qD.js → SettingsPage-BW4AKj2H.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-BfAb5-o_.js → StudioPage-DRPavCdU.js} +4 -4
- package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-BNzeZ2Vk.js → UsagePage-B7M7LvMM.js} +13 -13
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C0K-c4eo.js → assert-CidQVoNo.js} +1 -1
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-DfbhQU4k.js → badge-BaX2GTrn.js} +1 -1
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/{banner-BllT4LcF.js → banner-DFH4Bkte.js} +1 -1
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/{card-CtkX66Wd.js → card-CxDw0lK6.js} +1 -1
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
- package/dist/dashboard/assets/{cli-tools-CQXVMn4P.js → cli-tools-Bwv5-zGZ.js} +1 -1
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-BQqBQd2n.js → clipboard-button-DfS1ntiq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-BrTEZ8zu.js → drawer-QqPul6sC.js} +1 -1
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-BAACA727.js → formatters-u7JPB5BX.js} +1 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
- package/dist/dashboard/assets/{index-FQlj0HUT.js → index-Ko4dLRnV.js} +2 -2
- package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-C4gU5iOL.js → inline-CHJQwxLa.js} +1 -1
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/{input-BHHX_Tmm.js → input-BlmlizVV.js} +1 -1
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/{layout-CajrYUnD.js → layout-Bz93I-yE.js} +1 -1
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-Bx2I7XUe.js → list-row-Ml16KCfu.js} +1 -1
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/{live-DNNqRbj2.js → live-7_QpnYGu.js} +1 -1
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/{network-C5UBACrJ.js → network-CuMDaTzT.js} +1 -1
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/{pagination-Xsi9Qx04.js → pagination-B5aOoweZ.js} +1 -1
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai.webp +0 -0
- package/dist/dashboard/assets/providers/zcode.webp +0 -0
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/{routing-in0f_IfK.js → routing-fnCsxgNO.js} +1 -1
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/{select-BLULNqTp.js → select-BHqpd-bg.js} +2 -2
- package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
- package/dist/dashboard/assets/{settings-CcGczj6I.js → settings-BxslsErn.js} +1 -1
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/{stack-D2lIR0tg.js → stack-j7cXjgYX.js} +1 -1
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/{state-DTARjfiW.js → state-DHmEnLa2.js} +1 -1
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/{switch-c0oPSnqW.js → switch-DmZcyTIC.js} +1 -1
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-m8WGNPrz.js → useMutation-CbCP4w1o.js} +1 -1
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +57 -57
- package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
- package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
- package/package.json +1 -1
- package/src/boot/dependencies.ts +10 -2
- package/src/config/providers.ts +13 -7
- package/src/config/spec.ts +18 -11
- package/src/config/usage.ts +27 -0
- package/src/console/api-keys/routes.ts +19 -23
- package/src/console/cli-tools/contracts.ts +39 -295
- package/src/console/cli-tools/fs-ops.ts +29 -0
- package/src/console/cli-tools/injectors/claude.ts +3 -3
- package/src/console/cli-tools/injectors/codex.ts +6 -6
- package/src/console/cli-tools/injectors/driver.ts +8 -105
- package/src/console/cli-tools/injectors/kilo.ts +146 -21
- package/src/console/cli-tools/injectors/opencode.ts +28 -5
- package/src/console/cli-tools/routes.ts +6 -0
- package/src/console/cli-tools/service.ts +7 -6
- package/src/console/observability/contracts.ts +6 -0
- package/src/console/observability/store.ts +3 -0
- package/src/console/share/share-stats.ts +7 -9
- package/src/observability/event-log.ts +7 -0
- package/src/observability/event-query.ts +2 -0
- package/src/observability/telemetry-buffer.ts +7 -0
- package/src/persistence/schema.ts +8 -0
- package/src/persistence/stores/api-key-store.ts +19 -1
- package/src/protocol/dsml.ts +5 -76
- package/src/providers/adapters/compatible-adapter.ts +29 -1
- package/src/providers/default-registry.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -14
- package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
- package/src/providers/integrations/antigravity/antigravity.ts +20 -11
- package/src/providers/integrations/cursor/cursor.ts +2 -3
- package/src/providers/integrations/github/github-discovery.ts +15 -2
- package/src/providers/integrations/github/github-oauth.ts +178 -9
- package/src/providers/integrations/github/github.ts +95 -14
- package/src/providers/integrations/kiro/kiro.ts +1 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +9 -3
- package/src/providers/integrations/qoder/qoder.ts +147 -88
- package/src/providers/integrations/trae/trae.ts +2 -2
- package/src/providers/operations/account-error-classification.ts +67 -17
- package/src/providers/operations/byok-wire-profile.ts +19 -15
- package/src/providers/provider-usage.ts +15 -49
- package/src/security/admission/service.ts +11 -4
- package/src/security/api-key-auth.ts +5 -3
- package/src/transport/dispatch/attempt-finalize.ts +69 -21
- package/src/transport/dispatch/attempt-loop.ts +22 -0
- package/src/transport/failure-policy.ts +15 -1
- package/src/transport/guards/error-lifecycle.ts +4 -0
- package/src/transport/request/state.ts +6 -0
- package/src/transport/routing/router.ts +7 -0
- package/src/transport/surface/reasoning-summary.ts +9 -14
- package/src/transport/vision-handoff.ts +83 -28
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js +0 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +0 -1
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +0 -1
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js +0 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +0 -1
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +0 -1
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js +0 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +0 -1
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +0 -2
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/app-Bgd3zANQ.js +0 -9
- package/dist/dashboard/assets/app-Bgd3zANQ.js.gz +0 -0
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
- package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BEK7KP0H.js +0 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +0 -1
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/entry-EBHJ3DrE.js +0 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +0 -4
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js +0 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +0 -2
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +0 -1
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
- package/src/console/cli-tools/injectors/cline.ts +0 -110
- package/src/console/cli-tools/injectors/copilot.ts +0 -108
- package/src/console/cli-tools/injectors/cowork.ts +0 -64
- package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
- package/src/console/cli-tools/injectors/droid.ts +0 -96
- package/src/console/cli-tools/injectors/grok-build.ts +0 -70
- package/src/console/cli-tools/injectors/hermes.ts +0 -101
- package/src/console/cli-tools/injectors/jcode.ts +0 -115
- package/src/console/cli-tools/injectors/openclaw.ts +0 -161
|
@@ -29,13 +29,31 @@ import type { OAuthTokenRefreshResult } from "../../authentication/oauth-refresh
|
|
|
29
29
|
import { OAuthDeviceFlow } from "../../authentication/oauth-device-flow";
|
|
30
30
|
import type { FetchLike } from "../../authentication/oauth-client";
|
|
31
31
|
import { resolveGithubEnterpriseDomain } from "../../../config/providers";
|
|
32
|
+
import { randomUUID, createHash } from "node:crypto";
|
|
33
|
+
import { getOrCreateInstallId } from "../shared/install-id";
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
/** Public GitHub OAuth app id the Copilot clients use; not a secret. */
|
|
35
37
|
export const GITHUB_CLIENT_ID = "Iv1.b507a08c87ecfe98" as const;
|
|
38
|
+
/** The OpenCode desktop client's own GitHub OAuth app id (used by its Copilot integration). */
|
|
39
|
+
export const GITHUB_OPENCODE_CLIENT_ID = "Ov23li8tweQw6odWQebz" as const;
|
|
36
40
|
export const GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token" as const;
|
|
37
41
|
/** Default API host when a token carries no `proxy-ep` claim. */
|
|
38
42
|
export const GITHUB_DEFAULT_API_HOST = "api.individual.githubcopilot.com" as const;
|
|
43
|
+
/**
|
|
44
|
+
* The Copilot API host OpenCode's integration uses. The reference plugin always
|
|
45
|
+
* dials `api.githubcopilot.com` for GitHub.com (never the per-claim
|
|
46
|
+
* `api.individual.*` host the VS Code minted tokens carry), so an OpenCode
|
|
47
|
+
* login pins this host rather than falling back to the VS Code default.
|
|
48
|
+
*/
|
|
49
|
+
export const GITHUB_OPENCODE_API_HOST = "api.githubcopilot.com" as const;
|
|
50
|
+
/**
|
|
51
|
+
* How long an OpenCode login's credential is treated as valid before the
|
|
52
|
+
* refresh sweep revisits it. The raw GitHub OAuth token has no Copilot token
|
|
53
|
+
* expiry (there is no minted token), so this is a far-future safeguard, not a
|
|
54
|
+
* real lifetime: a re-login or upstream expiry is what actually rotates it.
|
|
55
|
+
*/
|
|
56
|
+
const GITHUB_OPENCODE_TOKEN_LIFETIME_MS = 365 * 24 * 60 * 60 * 1000;
|
|
39
57
|
|
|
40
58
|
/**
|
|
41
59
|
* Headers every Copilot inference and policy request must carry.
|
|
@@ -55,6 +73,52 @@ export const GITHUB_REQUEST_HEADERS: Readonly<Record<string, string>> = Object.f
|
|
|
55
73
|
"x-github-api-version": "2026-08-01",
|
|
56
74
|
});
|
|
57
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Stable per-process Copilot session id.
|
|
78
|
+
*
|
|
79
|
+
* The reference extension (`_mixinHeaders`) stamps `VScode-SessionId` with the
|
|
80
|
+
* VS Code window session and `VScode-MachineId` with the machine id. There is
|
|
81
|
+
* no VS Code window here, so the closest 1:1 shapes are: a session id minted
|
|
82
|
+
* once per gateway process (a new window = a new process in the reference), and
|
|
83
|
+
* a stable per-installation identity for the machine id.
|
|
84
|
+
*/
|
|
85
|
+
export const GITHUB_COPILOT_SESSION_ID = randomUUID();
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolves the Copilot client identity headers shared by chat/auto/discovery.
|
|
89
|
+
*
|
|
90
|
+
* The machine id is derived deterministically from the persisted install id and
|
|
91
|
+
* an account fingerprint when one is provided. The reference extension uses one
|
|
92
|
+
* machine id per VS Code installation for every account, but this gateway pools
|
|
93
|
+
* many accounts on one host — sharing one id across all of them makes every
|
|
94
|
+
* account look like a single device (an easy clustering/abuse signal). A
|
|
95
|
+
* per-account machine id keeps each account looking like its own device while
|
|
96
|
+
* staying stable across restarts for the same account. Without an account key
|
|
97
|
+
* (e.g. credential could not be parsed) the raw install id is used.
|
|
98
|
+
*/
|
|
99
|
+
export async function githubCopilotClientIds(
|
|
100
|
+
accountKey?: string,
|
|
101
|
+
): Promise<{
|
|
102
|
+
readonly sessionId: string;
|
|
103
|
+
readonly machineId: string;
|
|
104
|
+
}> {
|
|
105
|
+
const installId = await getOrCreateInstallId("github");
|
|
106
|
+
const machineId =
|
|
107
|
+
accountKey === undefined || accountKey.length === 0
|
|
108
|
+
? installId
|
|
109
|
+
: deterministicMachineId(installId, accountKey);
|
|
110
|
+
return {
|
|
111
|
+
sessionId: GITHUB_COPILOT_SESSION_ID,
|
|
112
|
+
machineId,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Stable UUID-shaped machine id derived from the install id and an account key. */
|
|
117
|
+
export function deterministicMachineId(installId: string, accountKey: string): string {
|
|
118
|
+
const digest = createHash("sha256").update(`${installId}:${accountKey}`).digest("hex");
|
|
119
|
+
return `${digest.slice(0, 8)}-${digest.slice(8, 12)}-${digest.slice(12, 16)}-${digest.slice(16, 20)}-${digest.slice(20, 32)}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
58
122
|
const DEVICE_INTERVAL_SECONDS = 5;
|
|
59
123
|
const DEVICE_EXPIRY_SECONDS = 900;
|
|
60
124
|
/** Copilot tokens are short-lived; a missing `expires_at` falls back to 25 minutes. */
|
|
@@ -196,17 +260,80 @@ async function fetchGitHubLogin(
|
|
|
196
260
|
}
|
|
197
261
|
}
|
|
198
262
|
|
|
263
|
+
interface CopilotUserProfile {
|
|
264
|
+
readonly chat_enabled?: boolean;
|
|
265
|
+
readonly can_signup_for_limited?: boolean;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Reads the Copilot entitlement profile for a freshly approved GitHub token.
|
|
270
|
+
*
|
|
271
|
+
* OAuth itself succeeds for any GitHub account; entitlement is the only signal
|
|
272
|
+
* that the account can chat. Mirrors the reference OpenCode provider: an
|
|
273
|
+
* explicit `chat_enabled: false` is a denial (the account is not signed up or
|
|
274
|
+
* has no seat), and a failed/malformed lookup must not turn a GitHub hiccup
|
|
275
|
+
* into a denial — only an explicit answer blocks login.
|
|
276
|
+
*/
|
|
277
|
+
async function fetchGitHubCopilotEntitlement(
|
|
278
|
+
githubAccessToken: string,
|
|
279
|
+
domain: string,
|
|
280
|
+
fetchFn: FetchLike,
|
|
281
|
+
signal: AbortSignal,
|
|
282
|
+
): Promise<{ denied: string } | { denied?: undefined }> {
|
|
283
|
+
try {
|
|
284
|
+
const endpoint =
|
|
285
|
+
domain === "github.com"
|
|
286
|
+
? "https://api.github.com/copilot_internal/user"
|
|
287
|
+
: `https://api.${domain}/copilot_internal/user`;
|
|
288
|
+
const response = await fetchFn(endpoint, {
|
|
289
|
+
headers: {
|
|
290
|
+
accept: "application/json",
|
|
291
|
+
authorization: `Bearer ${githubAccessToken}`,
|
|
292
|
+
"user-agent": GITHUB_REQUEST_HEADERS["user-agent"]!,
|
|
293
|
+
"x-github-api-version": "2025-04-01",
|
|
294
|
+
},
|
|
295
|
+
signal,
|
|
296
|
+
});
|
|
297
|
+
if (!response.ok) return { denied: undefined };
|
|
298
|
+
const profile = record(await response.json()) as CopilotUserProfile | undefined;
|
|
299
|
+
if (profile?.chat_enabled === false) {
|
|
300
|
+
if (profile.can_signup_for_limited) {
|
|
301
|
+
return {
|
|
302
|
+
denied:
|
|
303
|
+
"This GitHub account is not signed up for GitHub Copilot. Sign up for Copilot Free at https://github.com/features/copilot/plans and connect again.",
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
denied:
|
|
308
|
+
"This GitHub account does not have GitHub Copilot access. It needs an active Copilot subscription or a seat assigned by an organization.",
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return { denied: undefined };
|
|
312
|
+
} catch {
|
|
313
|
+
// Entitlement is a gate, not a reliability dependency: a network glitch
|
|
314
|
+
// here must not lock out an account that did approve the login.
|
|
315
|
+
return { denied: undefined };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
199
319
|
export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
200
320
|
override readonly supportsDeviceCode = true;
|
|
201
321
|
override readonly supportsBrowserCode = false;
|
|
202
322
|
|
|
203
323
|
protected override readonly providerLabel = "GitHub Copilot";
|
|
204
|
-
protected override readonly clientId = GITHUB_CLIENT_ID;
|
|
205
324
|
protected override readonly tokenUrl = GITHUB_ACCESS_TOKEN_URL;
|
|
206
325
|
protected override readonly scopes = "read:user";
|
|
207
326
|
|
|
208
|
-
|
|
327
|
+
/** Which GitHub OAuth app this flow presents. Defaults to the Copilot Chat
|
|
328
|
+
* extension's app; OpenCode's own app differs (`GITHUB_OPENCODE_CLIENT_ID`). */
|
|
329
|
+
protected override readonly clientId: string;
|
|
330
|
+
|
|
331
|
+
constructor(
|
|
332
|
+
fetchFn: FetchLike = globalThis.fetch,
|
|
333
|
+
clientId: string = GITHUB_CLIENT_ID,
|
|
334
|
+
) {
|
|
209
335
|
super(fetchFn);
|
|
336
|
+
this.clientId = clientId;
|
|
210
337
|
}
|
|
211
338
|
|
|
212
339
|
override async startDeviceAuth(_context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
|
|
@@ -218,7 +345,7 @@ export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
|
218
345
|
"content-type": "application/x-www-form-urlencoded",
|
|
219
346
|
"user-agent": GITHUB_REQUEST_HEADERS["user-agent"]!,
|
|
220
347
|
},
|
|
221
|
-
body: new URLSearchParams({ client_id:
|
|
348
|
+
body: new URLSearchParams({ client_id: this.clientId, scope: "read:user" }),
|
|
222
349
|
});
|
|
223
350
|
const body = record(await readJsonResponse(response, "GitHub device authorization"));
|
|
224
351
|
const deviceCode = nonEmptyTrimmedString(body?.device_code);
|
|
@@ -272,7 +399,7 @@ export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
|
272
399
|
"user-agent": GITHUB_REQUEST_HEADERS["user-agent"]!,
|
|
273
400
|
},
|
|
274
401
|
body: new URLSearchParams({
|
|
275
|
-
client_id:
|
|
402
|
+
client_id: this.clientId,
|
|
276
403
|
device_code: deviceAuthId,
|
|
277
404
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
278
405
|
}),
|
|
@@ -315,21 +442,48 @@ export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
|
315
442
|
: `GitHub device polling failed (${response.status})`;
|
|
316
443
|
return { status: "failed", reason };
|
|
317
444
|
}
|
|
318
|
-
let minted;
|
|
445
|
+
let minted: Awaited<ReturnType<typeof mintGithubToken>> | undefined;
|
|
319
446
|
try {
|
|
320
|
-
|
|
447
|
+
// OAuth succeeds for any GitHub account; the entitlement profile is the
|
|
448
|
+
// only signal that the account can actually chat. An explicit denial is
|
|
449
|
+
// surfaced as a failed verdict before any token is minted or stored,
|
|
450
|
+
// mirroring the reference OpenCode provider.
|
|
451
|
+
const entitlement = await fetchGitHubCopilotEntitlement(
|
|
452
|
+
githubToken,
|
|
453
|
+
domain,
|
|
454
|
+
this.fetchFn,
|
|
455
|
+
AbortSignal.timeout(10_000),
|
|
456
|
+
);
|
|
457
|
+
if (entitlement.denied !== undefined) {
|
|
458
|
+
return { status: "failed", reason: entitlement.denied };
|
|
459
|
+
}
|
|
460
|
+
// OpenCode's OAuth app is the client the `opencode-copilot` wire mode
|
|
461
|
+
// presents upstream, and that mode uses the raw GitHub token directly —
|
|
462
|
+
// no minted Copilot token. The VS Code mint endpoint
|
|
463
|
+
// (`/copilot_internal/v2/token`) rejects non-VS-Code client ids with 403
|
|
464
|
+
// (GitHub's anti-scraping gate), so an OpenCode login must not mint.
|
|
465
|
+
if (this.clientId !== GITHUB_OPENCODE_CLIENT_ID) {
|
|
466
|
+
minted = await mintGithubToken(githubToken, domain, this.fetchFn);
|
|
467
|
+
}
|
|
321
468
|
} catch (error) {
|
|
322
469
|
// The mint runs after approval: surface its reason as a failed verdict,
|
|
323
470
|
// not an exception that escapes the dashboard's poll request.
|
|
324
471
|
return { status: "failed", reason: error instanceof Error ? error.message : "GitHub Copilot token mint failed" };
|
|
325
472
|
}
|
|
326
473
|
const label = await fetchGitHubLogin(githubToken, domain, this.fetchFn, AbortSignal.timeout(10_000));
|
|
474
|
+
const access = minted === undefined ? githubToken : minted.access;
|
|
475
|
+
const apiHost = minted === undefined ? GITHUB_OPENCODE_API_HOST : minted.apiHost;
|
|
476
|
+
const expiresAt =
|
|
477
|
+
minted === undefined
|
|
478
|
+
? new Date(Date.now() + GITHUB_OPENCODE_TOKEN_LIFETIME_MS)
|
|
479
|
+
: minted.expiresAt;
|
|
327
480
|
const result: OAuthExchangeResult = {
|
|
328
|
-
access: encodeGithubCredential(
|
|
481
|
+
access: encodeGithubCredential(access, apiHost, githubToken),
|
|
329
482
|
// The GitHub token is the refresh value: it is what re-mints the Copilot
|
|
330
|
-
// token
|
|
483
|
+
// token (VS Code app) or is the raw credential itself (OpenCode app),
|
|
484
|
+
// depending on which app the flow presented.
|
|
331
485
|
refresh: githubToken,
|
|
332
|
-
expiresAt
|
|
486
|
+
expiresAt,
|
|
333
487
|
...(label === undefined ? {} : { accountLabel: label }),
|
|
334
488
|
};
|
|
335
489
|
return { status: "complete", result };
|
|
@@ -337,6 +491,21 @@ export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
|
337
491
|
|
|
338
492
|
|
|
339
493
|
override async refresh(refreshToken: string, signal?: AbortSignal): Promise<OAuthTokenRefreshResult> {
|
|
494
|
+
// OpenCode's app uses the raw GitHub token directly — there is no minted
|
|
495
|
+
// Copilot token to re-mint, and the VS Code mint endpoint rejects the
|
|
496
|
+
// OpenCode client id. Refresh is then a no-op that keeps the credential
|
|
497
|
+
// valid on its far-future lifetime.
|
|
498
|
+
if (this.clientId === GITHUB_OPENCODE_CLIENT_ID) {
|
|
499
|
+
return {
|
|
500
|
+
access: encodeGithubCredential(
|
|
501
|
+
refreshToken,
|
|
502
|
+
GITHUB_OPENCODE_API_HOST,
|
|
503
|
+
refreshToken,
|
|
504
|
+
),
|
|
505
|
+
refresh: refreshToken,
|
|
506
|
+
expiresAt: new Date(Date.now() + GITHUB_OPENCODE_TOKEN_LIFETIME_MS),
|
|
507
|
+
};
|
|
508
|
+
}
|
|
340
509
|
const minted = await mintGithubToken(
|
|
341
510
|
refreshToken,
|
|
342
511
|
copilotGitHubDomain(),
|
|
@@ -23,14 +23,16 @@ import { allParts, type CanonicalEvent, type CanonicalRequest, type ContentPart
|
|
|
23
23
|
import { OpenAICompatibleAdapter, withBearerAuthentication } from "../../adapters/compatible-adapter";
|
|
24
24
|
import { postUpstreamJson } from "../../../protocol/upstream/post-json";
|
|
25
25
|
import { providerBaseUrl } from "../../provider-metadata";
|
|
26
|
-
import { getOrCreateInstallId } from "../shared/install-id";
|
|
27
26
|
import { resolveRedisClient } from "../../../persistence/redis";
|
|
28
27
|
import { applyParamQuirks } from "../../../protocol/translation/quirks";
|
|
29
28
|
import { resolveInboundSessionId } from "../../operations/session-resolution";
|
|
30
29
|
import {
|
|
31
30
|
GITHUB_REQUEST_HEADERS,
|
|
31
|
+
githubCopilotClientIds,
|
|
32
32
|
parseGithubCredential,
|
|
33
|
+
type GithubCredential,
|
|
33
34
|
} from "./github-oauth";
|
|
35
|
+
import { resolveGithubCopilotMode } from "../../../config/providers";
|
|
34
36
|
|
|
35
37
|
export const GITHUB_PROVIDER_ID = "github" as const;
|
|
36
38
|
|
|
@@ -502,6 +504,36 @@ class GithubAdapter extends OpenAICompatibleAdapter {
|
|
|
502
504
|
);
|
|
503
505
|
}
|
|
504
506
|
|
|
507
|
+
/**
|
|
508
|
+
* Minimal OpenCode-style Copilot headers using the long-lived GitHub OAuth
|
|
509
|
+
* token directly (no minted Copilot token, no VS Code identity). This is
|
|
510
|
+
* the shape the official OpenCode plugin sends; proven live (2026-11-03):
|
|
511
|
+
*
|
|
512
|
+
* - POST /auto with these headers + the raw GitHub token -> 200, returns the
|
|
513
|
+
* session token and selects gpt-6-luna;
|
|
514
|
+
* - POST /responses with `Copilot-Session-Token` + GitHub token -> 200 stream.
|
|
515
|
+
*/
|
|
516
|
+
async #opencodeStyleHeaders(
|
|
517
|
+
context: ProviderDispatchContext,
|
|
518
|
+
request: CanonicalRequest,
|
|
519
|
+
credential: GithubCredential,
|
|
520
|
+
): Promise<Record<string, string>> {
|
|
521
|
+
const headers: Record<string, string> = {
|
|
522
|
+
"content-type": "application/json",
|
|
523
|
+
"user-agent": "opencode/0.1.0",
|
|
524
|
+
"openai-intent": "conversation-edits",
|
|
525
|
+
"x-github-api-version": "2026-08-01",
|
|
526
|
+
"x-interaction-type": "conversation-agent",
|
|
527
|
+
"x-interaction-id": randomUUID(),
|
|
528
|
+
"x-initiator": isAgentInitiated(request) ? "agent" : "user",
|
|
529
|
+
"x-request-id": context.request_id ?? randomUUID(),
|
|
530
|
+
};
|
|
531
|
+
if (credential.github !== undefined && credential.github.length > 0) {
|
|
532
|
+
headers.authorization = `Bearer ${credential.github}`;
|
|
533
|
+
}
|
|
534
|
+
return headers;
|
|
535
|
+
}
|
|
536
|
+
|
|
505
537
|
async #selectAutoModel(
|
|
506
538
|
prompt: string,
|
|
507
539
|
tier: "efficiency" | "balance" | "intelligence",
|
|
@@ -510,13 +542,24 @@ class GithubAdapter extends OpenAICompatibleAdapter {
|
|
|
510
542
|
candidate: ProviderDispatchTarget,
|
|
511
543
|
context: ProviderDispatchContext,
|
|
512
544
|
): Promise<GithubAutoDecision> {
|
|
513
|
-
const headers = await this.prepareHeaders(context, request, candidate);
|
|
514
545
|
const credential = context.credential.secret === undefined
|
|
515
546
|
? undefined
|
|
516
547
|
: parseGithubCredential(new TextDecoder().decode(context.credential.secret));
|
|
517
548
|
if (credential === undefined) {
|
|
518
549
|
throw new GatewayError("authentication_failed", 401, "GitHub Copilot credential is missing (request {requestId}). Wrong key, right door — check the credential 🔑");
|
|
519
550
|
}
|
|
551
|
+
// `opencode-copilot` mode (default): raw GitHub token + minimal headers,
|
|
552
|
+
// proven the most stable (single token, no mint expiry). The reference
|
|
553
|
+
// mode (`vscode-client-real`) uses the minted Copilot token + full VS Code
|
|
554
|
+
// headers — but a stale/expired minted token fails with 403 and the
|
|
555
|
+
// reference headers on /auto can answer 400 "invalid apiVersion" unless the
|
|
556
|
+
// minted token is fresh, so the default stays opencode.
|
|
557
|
+
const headers =
|
|
558
|
+
resolveGithubCopilotMode() === "opencode-copilot" &&
|
|
559
|
+
credential.github !== undefined &&
|
|
560
|
+
credential.github.length > 0
|
|
561
|
+
? await this.#opencodeStyleHeaders(context, request, credential)
|
|
562
|
+
: await this.prepareHeaders(context, request, candidate);
|
|
520
563
|
const payload: Record<string, unknown> = { prompt, tier };
|
|
521
564
|
if (hasImage) payload.has_image = true;
|
|
522
565
|
const outboundFetch = context.outbound_fetch ?? this.#fetch ?? globalThis.fetch;
|
|
@@ -648,20 +691,64 @@ class GithubAdapter extends OpenAICompatibleAdapter {
|
|
|
648
691
|
// it and wins — the route-level User-Agent is for generic OpenAI-compatible
|
|
649
692
|
// hosts and would be rejected here.
|
|
650
693
|
Object.assign(headers, GITHUB_REQUEST_HEADERS);
|
|
694
|
+
const secret = context.credential.secret;
|
|
695
|
+
let parsedCredential: ReturnType<typeof parseGithubCredential> | undefined;
|
|
696
|
+
if (secret !== undefined && secret.length > 0) {
|
|
697
|
+
try {
|
|
698
|
+
parsedCredential = parseGithubCredential(new TextDecoder().decode(secret));
|
|
699
|
+
} catch {
|
|
700
|
+
// Unparseable credential; fall back to the reference identity below.
|
|
701
|
+
}
|
|
702
|
+
}
|
|
651
703
|
const autoSessionToken = this.#autoSessionTokens.get(request);
|
|
652
|
-
|
|
704
|
+
// Auto-selected requests stream behind the session token. In the default
|
|
705
|
+
// `opencode-copilot` mode (raw GitHub token + minimal headers, proven 200
|
|
706
|
+
// on /auto and /responses), the whole request uses the OpenCode shape. The
|
|
707
|
+
// reference mode (`vscode-client-real`) keeps the reference identity and
|
|
708
|
+
// the minted Copilot token.
|
|
709
|
+
if (autoSessionToken !== undefined && parsedCredential?.github !== undefined) {
|
|
710
|
+
if (resolveGithubCopilotMode() === "opencode-copilot") {
|
|
711
|
+
const opencode = await this.#opencodeStyleHeaders(context, request, parsedCredential);
|
|
712
|
+
for (const key of Object.keys(headers)) delete headers[key];
|
|
713
|
+
Object.assign(headers, opencode);
|
|
714
|
+
headers["Copilot-Session-Token"] = autoSessionToken;
|
|
715
|
+
return headers;
|
|
716
|
+
}
|
|
717
|
+
headers["Copilot-Session-Token"] = autoSessionToken;
|
|
718
|
+
} else if (autoSessionToken !== undefined) {
|
|
653
719
|
headers["Copilot-Session-Token"] = autoSessionToken;
|
|
654
720
|
}
|
|
655
721
|
const requestId = context.request_id ?? randomUUID();
|
|
722
|
+
// Reference (`_mixinHeaders` + request path): the interaction id is a
|
|
723
|
+
// separate value from the request id (`X-Request-Id`), and the Copilot
|
|
724
|
+
// client identity is stamped as `VScode-SessionId`/`VScode-MachineId`.
|
|
725
|
+
// The library version header carries the fetcher library id (`node-http`
|
|
726
|
+
// etc.), not the extension version — we previously sent the extension
|
|
727
|
+
// version there, which the upstream reads as a different client fingerprint.
|
|
728
|
+
const interactionId = randomUUID();
|
|
729
|
+
// Machine id is per-account (stable derive from install id + account
|
|
730
|
+
// fingerprint), so a pooled gateway does not make every account look like
|
|
731
|
+
// one device.
|
|
732
|
+
const accountKey =
|
|
733
|
+
parsedCredential === undefined
|
|
734
|
+
? undefined
|
|
735
|
+
: createHash("sha256")
|
|
736
|
+
.update(`${parsedCredential.apiHost}:${parsedCredential.access}`)
|
|
737
|
+
.digest("hex");
|
|
738
|
+
const { sessionId, machineId } = await githubCopilotClientIds(accountKey);
|
|
656
739
|
Object.assign(headers, {
|
|
657
|
-
"editor-device-id":
|
|
740
|
+
"editor-device-id": machineId,
|
|
741
|
+
"VScode-MachineId": machineId,
|
|
742
|
+
"VScode-SessionId": sessionId,
|
|
658
743
|
"openai-intent": "conversation-agent",
|
|
744
|
+
// Reference stamps `Openai-Organization` on HTTP chat requests.
|
|
745
|
+
...(candidate?.wire_family === "chat" ? { "Openai-Organization": "github-copilot" } : {}),
|
|
659
746
|
"x-agent-task-id": requestId,
|
|
660
|
-
"x-interaction-id":
|
|
747
|
+
"x-interaction-id": interactionId,
|
|
661
748
|
"x-initiator": isAgentInitiated(request) ? "agent" : "user",
|
|
662
749
|
"x-interaction-type": "conversation-agent",
|
|
663
750
|
"x-request-id": requestId,
|
|
664
|
-
"x-vscode-user-agent-library-version": "
|
|
751
|
+
"x-vscode-user-agent-library-version": "node-http",
|
|
665
752
|
});
|
|
666
753
|
// The copy Claude family is served on the native Anthropic Messages wire,
|
|
667
754
|
// which gates feature betas behind `anthropic-beta` (mirroring the
|
|
@@ -676,14 +763,8 @@ class GithubAdapter extends OpenAICompatibleAdapter {
|
|
|
676
763
|
} else {
|
|
677
764
|
delete headers["copilot-vision-request"];
|
|
678
765
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
try {
|
|
682
|
-
const { access } = parseGithubCredential(new TextDecoder().decode(secret));
|
|
683
|
-
headers.authorization = `Bearer ${access}`;
|
|
684
|
-
} catch {
|
|
685
|
-
// Left as the factory sent it: the request fails closed at the upstream
|
|
686
|
-
// auth boundary, and the parse error belongs to the credential boundary.
|
|
766
|
+
if (parsedCredential !== undefined) {
|
|
767
|
+
headers.authorization = `Bearer ${parsedCredential.access}`;
|
|
687
768
|
}
|
|
688
769
|
return headers;
|
|
689
770
|
}
|
|
@@ -52,7 +52,6 @@ import {
|
|
|
52
52
|
} from "./kiro-request";
|
|
53
53
|
import { machineIdForAuthState } from "./kiro-machine-id";
|
|
54
54
|
import { estimateKiroSessionCache } from "./kiro-cache";
|
|
55
|
-
import { applyCacheMode } from "../../provider-usage";
|
|
56
55
|
import { resolveKiroProfileArn } from "./kiro-profile";
|
|
57
56
|
import {
|
|
58
57
|
AWS_REGION_PATTERN,
|
|
@@ -284,7 +283,7 @@ function kiroUsage(
|
|
|
284
283
|
// The runtime never reports cache hits; close the gap with our own history.
|
|
285
284
|
// A genuine ( >0) report conserves the complete record. Then the operator's
|
|
286
285
|
// cache mode decides whether a hit makes the whole input count as cached.
|
|
287
|
-
return
|
|
286
|
+
return estimateKiroSessionCache(record, sessionId, messages);
|
|
288
287
|
}
|
|
289
288
|
|
|
290
289
|
/**
|
|
@@ -126,7 +126,13 @@ export const QODER_CN_MODEL_CONFIGS: Readonly<Record<string, QoderModelConfig>>
|
|
|
126
126
|
* `qmodel` carries a 1M window. That is why the rosters are not merged.
|
|
127
127
|
*/
|
|
128
128
|
export const QODER_INTL_MODEL_CONFIGS: Readonly<Record<string, QoderModelConfig>> = {
|
|
129
|
-
|
|
129
|
+
// Every row declares its *maximum selectable* window (1M), not the currently
|
|
130
|
+
// active one: Qoder publishes a `context_config` of selectable windows per
|
|
131
|
+
// row (1M/400K/200K/…), and the adapter dispatches the widest
|
|
132
|
+
// (`qoderMaxContextFor`). The live list's `max_input_tokens` only names the
|
|
133
|
+
// currently-selected window (often 180K/200K), which is not a ceiling —
|
|
134
|
+
// declaring that would cap a conversation a 1M-capable account could run.
|
|
135
|
+
auto: { id: "auto", display_name: "Auto", max_input_tokens: 1000000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
|
|
130
136
|
ultimate: { id: "ultimate", display_name: "Ultimate", max_input_tokens: 1000000, max_output_tokens: 128_000, is_vl: true, is_free: false, is_reasoning: true },
|
|
131
137
|
// `fast` is what the live roster reports today; `performance` is the older
|
|
132
138
|
// key for the same slot. Both are kept so a hand-added model id written
|
|
@@ -134,7 +140,7 @@ export const QODER_INTL_MODEL_CONFIGS: Readonly<Record<string, QoderModelConfig>
|
|
|
134
140
|
// discovery supersedes it on the first successful fetch.
|
|
135
141
|
fast: { id: "fast", display_name: "Fast", max_input_tokens: 1000000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
|
|
136
142
|
performance: { id: "performance", display_name: "Performance", max_input_tokens: 1000000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
|
|
137
|
-
efficient: { id: "efficient", display_name: "Efficient", max_input_tokens:
|
|
143
|
+
efficient: { id: "efficient", display_name: "Efficient", max_input_tokens: 1000000, max_output_tokens: 64_000, is_vl: true, is_reasoning: false },
|
|
138
144
|
// Sonus/Cantus need an explicit effort on the IDE surface; without it the
|
|
139
145
|
// upstream streams text only, no reasoning (verified live). DeepSeek-family
|
|
140
146
|
// models think by default and are left untouched.
|
|
@@ -152,7 +158,7 @@ export const QODER_INTL_MODEL_CONFIGS: Readonly<Record<string, QoderModelConfig>
|
|
|
152
158
|
// Live roster (2026-11-01) reports dfmodel as billed (is_free false); it was
|
|
153
159
|
// previously marked free. Display names are corrected from the same capture.
|
|
154
160
|
dfmodel: { id: "dfmodel", display_name: "DeepSeek-Flash", max_input_tokens: 1000000, max_output_tokens: 384_000, is_vl: true, is_free: false, is_reasoning: true },
|
|
155
|
-
mmodel: { id: "mmodel", display_name: "MiniMax-M3.1-Flash", max_input_tokens: 1000000, max_output_tokens: 512_000, is_vl: true, is_free: false, is_reasoning:
|
|
161
|
+
mmodel: { id: "mmodel", display_name: "MiniMax-M3.1-Flash", max_input_tokens: 1000000, max_output_tokens: 512_000, is_vl: true, is_free: false, is_reasoning: true },
|
|
156
162
|
// `lite` is the free-tier plan package: same 1M window and multimodal input
|
|
157
163
|
// as `qmodel` (Qwen3.7-Plus), capped at 64k output, free tier.
|
|
158
164
|
lite: { id: "lite", display_name: "Lite-Qwen-3.7-Plus", max_input_tokens: 1000000, max_output_tokens: 65_536, is_vl: true, is_free: true, is_reasoning: false },
|