dsh-github-copilot 0.4.0-alpha.30 → 0.4.0-alpha.32

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/lib/index.js CHANGED
@@ -1,29 +1,29 @@
1
- import { C as currentSearchInitiator, D as GITHUB_COPILOT_CREDENTIAL_KEY, E as readCopilotCatalog, O as GITHUB_COPILOT_PREVIEW_PROVIDER_ID, S as currentChatRoute, T as isPluginPreviewProvider, _ as RESPONSES_WEB_SEARCH_TOOL_TYPE, a as COPILOT_HOSTED_SEARCH_PROVIDER_ID, b as candidatesForRoute, c as abortable$1, d as readBounded, f as applyRequestAuth, h as ANTHROPIC_WEB_SEARCH_TOOL_TYPE, i as describeSearchBackend, k as GITHUB_COPILOT_PROVIDER_ID, l as isAbortError, m as providerRequestHeaders, n as routeSessionSearch, o as GITHUB_COPILOT_HOSTED_SEARCH_PROVIDER_ID, p as normalizeRequestAuth, r as DescribedSearchFallbackError, s as createTraditionalSearchProvider, t as isCopilotSearchSelection, u as providerErrorMessage, v as SearchPlan, w as currentSearchSelection, x as sameCandidates, y as WEB_SEARCH_TOOL_TYPE } from "./search-routing-pFLux0W7.js";
1
+ import { C as currentSearchInitiator, D as GITHUB_COPILOT_CREDENTIAL_KEY, E as readCopilotCatalog, O as GITHUB_COPILOT_PREVIEW_PROVIDER_ID, S as currentChatRoute, T as isPluginPreviewProvider, _ as RESPONSES_WEB_SEARCH_TOOL_TYPE, a as COPILOT_HOSTED_SEARCH_PROVIDER_ID, b as candidatesForRoute, c as abortable$1, d as readBounded, f as applyRequestAuth, h as ANTHROPIC_WEB_SEARCH_TOOL_TYPE, i as describeSearchBackend, k as GITHUB_COPILOT_PROVIDER_ID, l as isAbortError, m as providerRequestHeaders, n as routeSessionSearch, o as GITHUB_COPILOT_HOSTED_SEARCH_PROVIDER_ID, p as normalizeRequestAuth, r as DescribedSearchFallbackError, s as createTraditionalSearchProvider, t as isCopilotSearchSelection, u as providerErrorMessage, v as SearchPlan, w as currentSearchSelection, x as sameCandidates, y as WEB_SEARCH_TOOL_TYPE } from "./search-routing-DqLKem1c.js";
2
2
  import AuthorizationService from "@deepseek-ai/dsh-authorization";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as dshScope from "@deepseek-ai/dsh-scope";
5
5
  import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
6
6
  import z from "@deepseek-ai/schemastery";
7
7
  import { z as z$1 } from "zod";
8
+ import { createModels, getSupportedThinkingLevels, hasApi, lazyStream } from "@earendil-works/pi-ai";
9
+ import { githubCopilotProvider } from "@earendil-works/pi-ai/providers/github-copilot";
10
+ import { getBuiltinModels } from "@earendil-works/pi-ai/providers/all";
8
11
  import * as dshLlm from "@deepseek-ai/dsh-llm";
9
12
  import { CONTEXT_WINDOW_EXCEEDED_CODE, LlmError, ReasoningEffortId, attributionHeaders, callConfigEquals, contentHasImage, isAgentLoopRequest, resolveImageAttachmentAccess, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
10
13
  import { WebError } from "@deepseek-ai/dsh-web";
11
14
  import * as dshSettings from "@deepseek-ai/dsh-settings";
12
- import { getBuiltinModels } from "@earendil-works/pi-ai/providers/all";
13
- import { createModels, getSupportedThinkingLevels, hasApi, lazyStream } from "@earendil-works/pi-ai";
14
15
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
15
- import { githubCopilotProvider } from "@earendil-works/pi-ai/providers/github-copilot";
16
16
  import { Config as Config$1, PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
17
17
  import { estimateContextTokens, estimateMessageTokens } from "@earendil-works/pi-ai/utils/estimate";
18
18
  import { buildCopilotDynamicHeaders, hasCopilotVisionInput } from "@earendil-works/pi-ai/api/github-copilot-headers";
19
19
  //#region lib/types/dual-model-host.js
20
20
  /** Optional plugin-owned planner/executor sessions; no Core/default-model mutation. */
21
- var __runInitializers$2 = function(thisArg, initializers, value) {
21
+ var __runInitializers$3 = function(thisArg, initializers, value) {
22
22
  var useValue = arguments.length > 2;
23
23
  for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
24
24
  return useValue ? value : void 0;
25
25
  };
26
- var __esDecorate$2 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
26
+ var __esDecorate$3 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
27
27
  function accept(f) {
28
28
  if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
29
29
  return f;
@@ -305,7 +305,7 @@ let GitHubCopilotDualModel = (() => {
305
305
  _view_decorators = [Remote];
306
306
  _save_decorators = [Remote];
307
307
  _create_decorators = [Remote];
308
- __esDecorate$2(this, null, _view_decorators, {
308
+ __esDecorate$3(this, null, _view_decorators, {
309
309
  kind: "method",
310
310
  name: "view",
311
311
  static: false,
@@ -316,7 +316,7 @@ let GitHubCopilotDualModel = (() => {
316
316
  },
317
317
  metadata: _metadata
318
318
  }, null, _instanceExtraInitializers);
319
- __esDecorate$2(this, null, _save_decorators, {
319
+ __esDecorate$3(this, null, _save_decorators, {
320
320
  kind: "method",
321
321
  name: "save",
322
322
  static: false,
@@ -327,7 +327,7 @@ let GitHubCopilotDualModel = (() => {
327
327
  },
328
328
  metadata: _metadata
329
329
  }, null, _instanceExtraInitializers);
330
- __esDecorate$2(this, null, _create_decorators, {
330
+ __esDecorate$3(this, null, _create_decorators, {
331
331
  kind: "method",
332
332
  name: "create",
333
333
  static: false,
@@ -345,7 +345,7 @@ let GitHubCopilotDualModel = (() => {
345
345
  value: _metadata
346
346
  });
347
347
  }
348
- owner = __runInitializers$2(this, _instanceExtraInitializers);
348
+ owner = __runInitializers$3(this, _instanceExtraInitializers);
349
349
  lifetime = new AbortController();
350
350
  creates = /* @__PURE__ */ new Map();
351
351
  overlays = /* @__PURE__ */ new Map();
@@ -857,72 +857,847 @@ let GitHubCopilotDualModel = (() => {
857
857
  throw error;
858
858
  }
859
859
  }
860
- };
861
- })();
862
- //#endregion
863
- //#region lib/types/search-routing-remote.js
864
- const SearchProviderCatalogSchema = z$1.object({
865
- supported: z$1.boolean(),
866
- providers: z$1.array(z$1.object({ id: z$1.string().min(1).max(512) }).strict()).max(512)
867
- }).strict();
868
- //#endregion
869
- //#region lib/types/search-routing-host.js
870
- var __runInitializers$1 = function(thisArg, initializers, value) {
871
- var useValue = arguments.length > 2;
872
- for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
873
- return useValue ? value : void 0;
874
- };
875
- var __esDecorate$1 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
876
- function accept(f) {
877
- if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
878
- return f;
860
+ };
861
+ })();
862
+ //#endregion
863
+ //#region lib/types/search-routing-remote.js
864
+ const SearchProviderCatalogSchema = z$1.object({
865
+ supported: z$1.boolean(),
866
+ providers: z$1.array(z$1.object({ id: z$1.string().min(1).max(512) }).strict()).max(512)
867
+ }).strict();
868
+ //#endregion
869
+ //#region lib/types/search-routing-host.js
870
+ var __runInitializers$2 = function(thisArg, initializers, value) {
871
+ var useValue = arguments.length > 2;
872
+ for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
873
+ return useValue ? value : void 0;
874
+ };
875
+ var __esDecorate$2 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
876
+ function accept(f) {
877
+ if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
878
+ return f;
879
+ }
880
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
881
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
882
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
883
+ var _, done = false;
884
+ for (var i = decorators.length - 1; i >= 0; i--) {
885
+ var context = {};
886
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
887
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
888
+ context.addInitializer = function(f) {
889
+ if (done) throw new TypeError("Cannot add initializers after decoration has completed");
890
+ extraInitializers.push(accept(f || null));
891
+ };
892
+ var result = (0, decorators[i])(kind === "accessor" ? {
893
+ get: descriptor.get,
894
+ set: descriptor.set
895
+ } : descriptor[key], context);
896
+ if (kind === "accessor") {
897
+ if (result === void 0) continue;
898
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
899
+ if (_ = accept(result.get)) descriptor.get = _;
900
+ if (_ = accept(result.set)) descriptor.set = _;
901
+ if (_ = accept(result.init)) initializers.unshift(_);
902
+ } else if (_ = accept(result)) {
903
+ if (kind === "field") initializers.unshift(_);
904
+ else descriptor[key] = _;
905
+ }
906
+ }
907
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
908
+ done = true;
909
+ };
910
+ let SearchRoutingController = (() => {
911
+ let _classSuper = TypertRemoteService;
912
+ let _instanceExtraInitializers = [];
913
+ let _providers_decorators;
914
+ return class SearchRoutingController extends _classSuper {
915
+ static {
916
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
917
+ _providers_decorators = [Remote];
918
+ __esDecorate$2(this, null, _providers_decorators, {
919
+ kind: "method",
920
+ name: "providers",
921
+ static: false,
922
+ private: false,
923
+ access: {
924
+ has: (obj) => "providers" in obj,
925
+ get: (obj) => obj.providers
926
+ },
927
+ metadata: _metadata
928
+ }, null, _instanceExtraInitializers);
929
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, {
930
+ enumerable: true,
931
+ configurable: true,
932
+ writable: true,
933
+ value: _metadata
934
+ });
935
+ }
936
+ constructor(ctx) {
937
+ super(ctx, "githubCopilotSearchRouting");
938
+ __runInitializers$2(this, _instanceExtraInitializers);
939
+ }
940
+ providers() {
941
+ const unavailable = {
942
+ supported: false,
943
+ providers: []
944
+ };
945
+ const catalog = this.ctx.get("githubCopilotSearchCatalog");
946
+ if (typeof catalog?.list !== "function") return unavailable;
947
+ try {
948
+ const ids = catalog.list();
949
+ const parsed = SearchProviderCatalogSchema.safeParse({
950
+ supported: true,
951
+ providers: ids.map((id) => ({ id }))
952
+ });
953
+ if (!parsed.success || new Set(ids).size !== ids.length) return unavailable;
954
+ parsed.data.providers.sort((a, b) => a.id.localeCompare(b.id));
955
+ return parsed.data;
956
+ } catch {
957
+ return unavailable;
958
+ }
959
+ }
960
+ };
961
+ })();
962
+ //#endregion
963
+ //#region lib/types/copilot-grant.js
964
+ function nonEmptyOAuthString(value, field) {
965
+ if (typeof value !== "string" || value.trim().length === 0) throw new Error(`github-copilot: OAuth credential field "${field}" must be a non-empty string`);
966
+ return value;
967
+ }
968
+ /**
969
+ * Rebuild pi-ai's GitHub Copilot grant as the exact JSON-safe provider shape.
970
+ * Prototype and unrelated extension members are intentionally not preserved.
971
+ */
972
+ function normalizeGitHubCopilotOAuthCredential(credential) {
973
+ if (typeof credential !== "object" || credential === null) throw new Error("github-copilot: OAuth credential must be an object");
974
+ if (Reflect.get(credential, "type") !== "oauth") throw new Error("github-copilot: OAuth credential type must be \"oauth\"");
975
+ const refresh = nonEmptyOAuthString(Reflect.get(credential, "refresh"), "refresh");
976
+ const access = nonEmptyOAuthString(Reflect.get(credential, "access"), "access");
977
+ const expires = Reflect.get(credential, "expires");
978
+ if (typeof expires !== "number" || !Number.isFinite(expires)) throw new Error("github-copilot: OAuth credential field \"expires\" must be a finite number");
979
+ const enterpriseUrl = Reflect.get(credential, "enterpriseUrl");
980
+ if (enterpriseUrl !== void 0 && (typeof enterpriseUrl !== "string" || enterpriseUrl.trim().length === 0)) throw new Error("github-copilot: OAuth credential field \"enterpriseUrl\" must be a non-empty string");
981
+ const availableModelIds = Reflect.get(credential, "availableModelIds");
982
+ if (availableModelIds !== void 0 && (!Array.isArray(availableModelIds) || !availableModelIds.every((modelId) => typeof modelId === "string" && modelId.trim().length > 0))) throw new Error("github-copilot: OAuth credential field \"availableModelIds\" must be an array of non-empty strings");
983
+ return {
984
+ type: "oauth",
985
+ refresh,
986
+ access,
987
+ expires,
988
+ ...enterpriseUrl === void 0 ? {} : { enterpriseUrl },
989
+ ...availableModelIds === void 0 ? {} : { availableModelIds: [...new Set(availableModelIds)] }
990
+ };
991
+ }
992
+ //#endregion
993
+ //#region lib/types/copilot-auth.js
994
+ /**
995
+ * Provider-side GitHub Copilot auth resolution over the same credential record
996
+ * that DSH's built-in llm-pi-ai adapter owns.
997
+ */
998
+ function credentialService(ctx) {
999
+ const candidate = ctx.get("credentials");
1000
+ if (typeof candidate !== "object" || candidate === null) throw new Error("github-copilot: DSH credentials service is unavailable");
1001
+ for (const method of [
1002
+ "readRecord",
1003
+ "listRecords",
1004
+ "modifyRecord",
1005
+ "deleteRecord"
1006
+ ]) if (typeof Reflect.get(candidate, method) !== "function") throw new Error(`github-copilot: required DSH credentials API "credentials.${method}" is unavailable`);
1007
+ return candidate;
1008
+ }
1009
+ function toCredential(record) {
1010
+ if (record === void 0) return void 0;
1011
+ if (record.kind === "api-key") return {
1012
+ type: "api_key",
1013
+ ...record.key === void 0 ? {} : { key: record.key },
1014
+ ...record.env === void 0 ? {} : { env: { ...record.env } }
1015
+ };
1016
+ if (typeof record.payload !== "object" || record.payload === null) throw new Error("github-copilot: stored llm-pi-ai GitHub Copilot grant is not an object");
1017
+ return normalizeGitHubCopilotOAuthCredential(record.payload);
1018
+ }
1019
+ function toRecord(credential) {
1020
+ if (credential.type === "api_key") return {
1021
+ kind: "api-key",
1022
+ ...credential.key === void 0 ? {} : { key: credential.key },
1023
+ ...credential.env === void 0 ? {} : { env: { ...credential.env } }
1024
+ };
1025
+ return {
1026
+ kind: "grant",
1027
+ payload: normalizeGitHubCopilotOAuthCredential(credential)
1028
+ };
1029
+ }
1030
+ /** Bind exactly one logical route to the canonical Host-owned credential record. */
1031
+ function createGitHubCopilotCredentialStore(ctx, logicalProviderId = GITHUB_COPILOT_PROVIDER_ID) {
1032
+ const assertProvider = (providerId) => {
1033
+ if (providerId !== logicalProviderId) throw new Error("COPILOT_CREDENTIAL_PROVIDER_MISMATCH");
1034
+ };
1035
+ return {
1036
+ read: async (providerId) => {
1037
+ assertProvider(providerId);
1038
+ return toCredential(await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY));
1039
+ },
1040
+ list: async () => {
1041
+ const record = await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
1042
+ return record === void 0 ? [] : [{
1043
+ providerId: logicalProviderId,
1044
+ type: record.kind === "api-key" ? "api_key" : "oauth"
1045
+ }];
1046
+ },
1047
+ modify: async (providerId, mutate) => {
1048
+ assertProvider(providerId);
1049
+ return toCredential(await credentialService(ctx).modifyRecord(GITHUB_COPILOT_CREDENTIAL_KEY, async (current) => {
1050
+ const next = await mutate(toCredential(current));
1051
+ return next === void 0 ? void 0 : toRecord(next);
1052
+ }));
1053
+ },
1054
+ delete: async (providerId) => {
1055
+ assertProvider(providerId);
1056
+ await credentialService(ctx).deleteRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
1057
+ }
1058
+ };
1059
+ }
1060
+ /**
1061
+ * Build one resolver whose pi-ai collection refreshes OAuth credentials inside
1062
+ * the DSH record's serialized modify operation before exposing request auth.
1063
+ */
1064
+ function createGitHubCopilotTokenResolver(ctx, onCredentialChanged) {
1065
+ const models = createModels({ credentials: createGitHubCopilotCredentialStore(ctx) });
1066
+ const provider = githubCopilotProvider();
1067
+ if (provider.auth.oauth === void 0) throw new Error("github-copilot: native OAuth method is unavailable");
1068
+ models.setProvider({
1069
+ ...provider,
1070
+ auth: { oauth: provider.auth.oauth }
1071
+ });
1072
+ return async (modelId) => {
1073
+ const snapshot = readCopilotCatalog(ctx);
1074
+ const native = new Map(snapshot.models.map((model) => [model.id, model])).get(modelId);
1075
+ if (native === void 0) throw new Error(`github-copilot: pi-ai catalog has no GitHub Copilot model "${modelId}"`);
1076
+ const resolved = await models.getAuth("github-copilot");
1077
+ if (resolved === void 0) return void 0;
1078
+ if (onCredentialChanged !== void 0) try {
1079
+ await onCredentialChanged();
1080
+ } catch (error) {
1081
+ ctx.logger.warn("github-copilot: provider route reconciliation will retry after a later auth resolution");
1082
+ ctx.logger.warn(error);
1083
+ }
1084
+ const stored = await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
1085
+ if (!(stored?.kind === "grant" && (normalizeGitHubCopilotOAuthCredential(stored.payload).availableModelIds ?? []).includes(modelId))) throw new Error(`github-copilot: model "${modelId}" is not available for the signed-in Copilot account`);
1086
+ const apiKey = resolved.auth.apiKey;
1087
+ if (apiKey === void 0) return void 0;
1088
+ return {
1089
+ apiKey,
1090
+ baseURL: trustedCopilotBaseUrl(resolved.auth.baseUrl ?? native.baseUrl, enterpriseDomainOf(stored)),
1091
+ ...resolved.auth.headers === void 0 ? {} : { headers: resolved.auth.headers }
1092
+ };
1093
+ };
1094
+ }
1095
+ function enterpriseDomainOf(record) {
1096
+ if (record?.kind !== "grant" || typeof record.payload !== "object" || record.payload === null) return void 0;
1097
+ const value = Reflect.get(record.payload, "enterpriseUrl");
1098
+ if (typeof value !== "string" || value.trim().length === 0) return void 0;
1099
+ try {
1100
+ return (value.includes("://") ? new URL(value) : new URL(`https://${value}`)).hostname.toLowerCase();
1101
+ } catch {
1102
+ throw new Error("github-copilot: stored enterpriseUrl is invalid");
1103
+ }
1104
+ }
1105
+ /** Validate an OAuth-derived endpoint without disclosing its value or credential payload. */
1106
+ function trustedGitHubCopilotBaseUrl(value, grant) {
1107
+ return trustedCopilotBaseUrl(value, enterpriseDomainOf({
1108
+ kind: "grant",
1109
+ payload: normalizeGitHubCopilotOAuthCredential(grant)
1110
+ }));
1111
+ }
1112
+ function trustedCopilotBaseUrl(value, enterpriseDomain) {
1113
+ if (value === void 0) throw new Error("github-copilot: pi-ai resolved no Copilot API base URL");
1114
+ const url = new URL(value);
1115
+ const hostname = url.hostname.toLowerCase();
1116
+ const githubHosted = /^api(?:\.[a-z0-9-]+)+\.githubcopilot\.com$/u.test(hostname);
1117
+ const enterpriseHosted = enterpriseDomain !== void 0 && hostname === `copilot-api.${enterpriseDomain}`;
1118
+ if (!(url.protocol === "https:" && (githubHosted || enterpriseHosted)) || url.username.length > 0 || url.password.length > 0) throw new Error("github-copilot: pi-ai resolved an untrusted Copilot API base URL");
1119
+ return url.origin;
1120
+ }
1121
+ //#endregion
1122
+ //#region lib/types/account-model-auth.js
1123
+ /** Native getAuth renews within five minutes; reserve another thirty seconds for metadata preparation. */
1124
+ const ACCOUNT_MODEL_AUTH_MIN_VALIDITY_MS = 33e4;
1125
+ /** An opaque, process-local account key. Never a substitute for request credentials. */
1126
+ function copilotAccountKey(grant) {
1127
+ return createHash("sha256").update(`${grant.refresh.length}:`).update(grant.refresh).update(grant.enterpriseUrl ?? "").digest("hex");
1128
+ }
1129
+ /** Compare normalized permission snapshots without retaining model credentials. */
1130
+ function copilotEntitlementKey(grant) {
1131
+ const ids = grant.availableModelIds;
1132
+ const normalized = ids === void 0 ? "missing" : JSON.stringify([...new Set(ids)].sort());
1133
+ return createHash("sha256").update(normalized).digest("hex");
1134
+ }
1135
+ function authEntitlementKey(auth) {
1136
+ return copilotEntitlementKey({ ...auth.availableModelIds === void 0 ? {} : { availableModelIds: [...auth.availableModelIds] } });
1137
+ }
1138
+ function active(signal) {
1139
+ if (signal.aborted) throw new Error("COPILOT_ACCOUNT_AUTH_ABORTED");
1140
+ }
1141
+ /**
1142
+ * Bind model discovery to the existing native OAuth lifecycle. No model catalog,
1143
+ * login, environment fallback or separate credential record is introduced here.
1144
+ */
1145
+ function createAccountModelAuth(credentials, renewRejectedCredential) {
1146
+ const native = githubCopilotProvider();
1147
+ const oauth = native.auth.oauth;
1148
+ if (oauth === void 0) throw new Error("COPILOT_ACCOUNT_OAUTH_UNAVAILABLE");
1149
+ const readGrant = async (signal) => {
1150
+ active(signal);
1151
+ const stored = await credentials.read(GITHUB_COPILOT_PROVIDER_ID);
1152
+ active(signal);
1153
+ if (stored?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
1154
+ return normalizeGitHubCopilotOAuthCredential(stored);
1155
+ };
1156
+ const matches = (grant, key) => {
1157
+ if (copilotAccountKey(grant) !== key) throw new Error("COPILOT_ACCOUNT_CHANGED");
1158
+ };
1159
+ const current = async (auth, signal) => {
1160
+ active(signal);
1161
+ const grant = await readGrant(signal);
1162
+ matches(grant, auth.accountKey);
1163
+ if (grant.access !== auth.apiKey || grant.expires <= Date.now()) throw new Error("COPILOT_ACCOUNT_TOKEN_CHANGED");
1164
+ if (copilotEntitlementKey(grant) !== authEntitlementKey(auth)) throw new Error("COPILOT_ACCOUNT_ENTITLEMENT_CHANGED");
1165
+ const derived = await oauth.toAuth(grant);
1166
+ active(signal);
1167
+ if (derived.apiKey !== auth.apiKey || trustedGitHubCopilotBaseUrl(derived.baseUrl, grant) !== auth.baseURL) throw new Error("COPILOT_ACCOUNT_AUTH_CHANGED");
1168
+ const latest = await readGrant(signal);
1169
+ matches(latest, auth.accountKey);
1170
+ if (latest.access !== auth.apiKey || latest.expires <= Date.now()) throw new Error("COPILOT_ACCOUNT_TOKEN_CHANGED");
1171
+ if (copilotEntitlementKey(latest) !== authEntitlementKey(auth)) throw new Error("COPILOT_ACCOUNT_ENTITLEMENT_CHANGED");
1172
+ };
1173
+ return {
1174
+ async resolveAuth(signal) {
1175
+ try {
1176
+ const before = await readGrant(signal);
1177
+ const key = copilotAccountKey(before);
1178
+ const rejectedAccess = renewRejectedCredential?.(before) === true ? before.access : void 0;
1179
+ const forResolution = (grant) => rejectedAccess !== void 0 && grant.access === rejectedAccess ? {
1180
+ ...grant,
1181
+ expires: 0
1182
+ } : grant;
1183
+ const guardedOAuth = {
1184
+ ...oauth,
1185
+ login: async () => {
1186
+ throw new Error("COPILOT_ACCOUNT_USE_CANONICAL_SIGN_IN");
1187
+ },
1188
+ async refresh(credential, requestSignal) {
1189
+ active(signal);
1190
+ matches(normalizeGitHubCopilotOAuthCredential(credential), key);
1191
+ const result = normalizeGitHubCopilotOAuthCredential(await oauth.refresh(credential, requestSignal === void 0 ? signal : AbortSignal.any([signal, requestSignal])));
1192
+ active(signal);
1193
+ matches(result, key);
1194
+ return result;
1195
+ },
1196
+ async toAuth(credential) {
1197
+ active(signal);
1198
+ const grant = normalizeGitHubCopilotOAuthCredential(credential);
1199
+ matches(grant, key);
1200
+ const result = await oauth.toAuth(grant);
1201
+ active(signal);
1202
+ return {
1203
+ ...result,
1204
+ baseUrl: trustedGitHubCopilotBaseUrl(result.baseUrl, grant)
1205
+ };
1206
+ }
1207
+ };
1208
+ const models = createModels({ credentials: {
1209
+ read: async (provider) => {
1210
+ const value = await credentials.read(provider);
1211
+ active(signal);
1212
+ if (value?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
1213
+ const grant = normalizeGitHubCopilotOAuthCredential(value);
1214
+ matches(grant, key);
1215
+ return forResolution(grant);
1216
+ },
1217
+ list: () => credentials.list(),
1218
+ modify: (provider, mutate) => credentials.modify(provider, async (value) => {
1219
+ active(signal);
1220
+ if (value?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
1221
+ matches(normalizeGitHubCopilotOAuthCredential(value), key);
1222
+ const next = await mutate(forResolution(normalizeGitHubCopilotOAuthCredential(value)));
1223
+ active(signal);
1224
+ if (next !== void 0) matches(normalizeGitHubCopilotOAuthCredential(next), key);
1225
+ return next;
1226
+ }),
1227
+ delete: async () => {
1228
+ throw new Error("COPILOT_ACCOUNT_DISCOVERY_CANNOT_SIGN_OUT");
1229
+ }
1230
+ } });
1231
+ models.setProvider({
1232
+ ...native,
1233
+ auth: { oauth: guardedOAuth }
1234
+ });
1235
+ const resolved = await models.getAuth(GITHUB_COPILOT_PROVIDER_ID, {
1236
+ signal,
1237
+ minOAuthValidityMs: ACCOUNT_MODEL_AUTH_MIN_VALIDITY_MS
1238
+ });
1239
+ active(signal);
1240
+ if (resolved?.auth.apiKey === void 0) throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
1241
+ const grant = await readGrant(signal);
1242
+ matches(grant, key);
1243
+ if (rejectedAccess !== void 0 && grant.access === rejectedAccess) throw new Error("COPILOT_ACCOUNT_TOKEN_REJECTED");
1244
+ const auth = {
1245
+ apiKey: resolved.auth.apiKey,
1246
+ baseURL: trustedGitHubCopilotBaseUrl(resolved.auth.baseUrl, grant),
1247
+ accountKey: key,
1248
+ ...grant.availableModelIds === void 0 ? {} : { availableModelIds: Object.freeze([...grant.availableModelIds]) }
1249
+ };
1250
+ await current(auth, signal);
1251
+ return Object.freeze(auth);
1252
+ } catch {
1253
+ throw new Error(signal.aborted ? "COPILOT_ACCOUNT_AUTH_ABORTED" : "COPILOT_ACCOUNT_AUTH_FAILED");
1254
+ }
1255
+ },
1256
+ async assertAuthCurrent(auth, signal) {
1257
+ try {
1258
+ await current(auth, signal);
1259
+ } catch {
1260
+ throw new Error(signal.aborted ? "COPILOT_ACCOUNT_AUTH_ABORTED" : "COPILOT_ACCOUNT_AUTH_CHANGED");
1261
+ }
1262
+ }
1263
+ };
1264
+ }
1265
+ //#endregion
1266
+ //#region lib/types/copilot-usage-types.js
1267
+ const COPILOT_USAGE_MAX_AMOUNT = 0xe8d4a51000;
1268
+ const COPILOT_USAGE_MAX_TIMESTAMP = 0xe677d21fdbff;
1269
+ //#endregion
1270
+ //#region lib/types/copilot-usage-normalize.js
1271
+ const amount = z$1.number().finite().min(0).max(COPILOT_USAGE_MAX_AMOUNT);
1272
+ const entitlement = z$1.union([
1273
+ amount,
1274
+ z$1.literal(-1),
1275
+ z$1.string().max(32).regex(/^(?:-1|(?:0|[1-9]\d*)(?:\.\d+)?)$/u)
1276
+ ]).transform(Number).refine((value) => Number.isFinite(value) && value <= 0xe8d4a51000);
1277
+ const snapshot = z$1.object({
1278
+ unlimited: z$1.boolean(),
1279
+ entitlement: entitlement.optional(),
1280
+ percent_remaining: z$1.number().finite().min(0).max(100).optional(),
1281
+ quota_remaining: amount.optional(),
1282
+ credits_used: amount.optional(),
1283
+ has_quota: z$1.boolean().optional(),
1284
+ token_based_billing: z$1.boolean().optional(),
1285
+ quota_reset_at: z$1.number().int().min(0).max(Math.floor(COPILOT_USAGE_MAX_TIMESTAMP / 1e3)).optional()
1286
+ });
1287
+ const response = z$1.object({
1288
+ token_based_billing: z$1.boolean().optional(),
1289
+ access_type_sku: z$1.string().max(128).optional(),
1290
+ quota_reset_date: z$1.string().max(64).optional(),
1291
+ quota_reset_date_utc: z$1.string().max(64).optional(),
1292
+ limited_user_reset_date: z$1.string().max(64).optional(),
1293
+ quota_snapshots: z$1.object({
1294
+ chat: snapshot.optional(),
1295
+ premium_interactions: snapshot.optional()
1296
+ }).optional(),
1297
+ monthly_quotas: z$1.object({ chat: amount }).optional(),
1298
+ limited_user_quotas: z$1.object({ chat: amount }).optional()
1299
+ });
1300
+ function unavailableCopilotUsage(diagnostic, billing = "unknown", budget = "unknown") {
1301
+ return {
1302
+ state: diagnostic === "COPILOT_USAGE_SIGNED_OUT" ? "signed-out" : "unavailable",
1303
+ billing,
1304
+ budget,
1305
+ diagnostic
1306
+ };
1307
+ }
1308
+ function resetDate(value) {
1309
+ if (!/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2}))?$/u.test(value)) return void 0;
1310
+ const date = value.slice(0, 10);
1311
+ const day = Date.parse(`${date}T00:00:00Z`);
1312
+ if (!Number.isFinite(day) || new Date(day).toISOString().slice(0, 10) !== date) return void 0;
1313
+ const parsed = Date.parse(value);
1314
+ return Number.isFinite(parsed) && parsed >= 0 && parsed <= 0xe677d21fdbff ? parsed : void 0;
1315
+ }
1316
+ /**
1317
+ * Pinned VS Code 44825207: parseQuotas/getQuotaUsage and chatStatusDashboard
1318
+ * display account amounts directly. Only per-turn nano-AIU uses /1e9; it is
1319
+ * intentionally absent here. Pooled credits_used never shares an entitlement.
1320
+ */
1321
+ function normalizeCopilotUsage(input, observedAt) {
1322
+ const parsed = response.safeParse(input);
1323
+ if (!parsed.success) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE");
1324
+ const data = parsed.data;
1325
+ if (data.token_based_billing === void 0) return unavailableCopilotUsage("COPILOT_USAGE_BILLING_UNKNOWN");
1326
+ const billing = data.token_based_billing ? "credits" : "requests";
1327
+ const quota = data.access_type_sku === "free_limited_copilot" ? data.quota_snapshots?.chat : data.quota_snapshots?.premium_interactions;
1328
+ const reset = data.quota_reset_date_utc ?? data.quota_reset_date ?? data.limited_user_reset_date;
1329
+ const accountReset = reset === void 0 ? void 0 : resetDate(reset);
1330
+ if (reset !== void 0 && accountReset === void 0) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE", billing);
1331
+ const resetAt = quota?.quota_reset_at === void 0 ? accountReset : quota.quota_reset_at * 1e3;
1332
+ const dates = {
1333
+ observedAt,
1334
+ ...resetAt === void 0 ? {} : { resetAt }
1335
+ };
1336
+ if (!quota) {
1337
+ const limit = data.monthly_quotas?.chat, remaining = data.limited_user_quotas?.chat;
1338
+ if (billing === "requests" && limit !== void 0 && limit > 0 && remaining !== void 0 && remaining <= limit) return {
1339
+ state: "ready",
1340
+ billing,
1341
+ budget: "individual",
1342
+ limit,
1343
+ remaining,
1344
+ used: limit - remaining,
1345
+ percentUsed: (limit - remaining) / limit * 100,
1346
+ ...dates
1347
+ };
1348
+ return unavailableCopilotUsage("COPILOT_USAGE_SNAPSHOT_MISSING", billing);
1349
+ }
1350
+ if (quota.token_based_billing !== void 0 && quota.token_based_billing !== data.token_based_billing) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE", billing);
1351
+ if (quota.unlimited) {
1352
+ if (quota.has_quota === false) return unavailableCopilotUsage("COPILOT_USAGE_POOLED_EXHAUSTED", billing, "pooled");
1353
+ if (billing !== "credits" || quota.credits_used === void 0) return unavailableCopilotUsage("COPILOT_USAGE_POOLED_UNAVAILABLE", billing, "pooled");
1354
+ return {
1355
+ state: "ready",
1356
+ billing,
1357
+ budget: "pooled",
1358
+ used: quota.credits_used,
1359
+ ...dates
1360
+ };
1361
+ }
1362
+ const limit = quota.entitlement, percent = quota.percent_remaining;
1363
+ if (limit === 0) return unavailableCopilotUsage("COPILOT_USAGE_NO_ALLOCATION", billing, "individual");
1364
+ if (limit !== void 0 && limit < 0 || percent === void 0 || limit !== void 0 && quota.quota_remaining !== void 0 && quota.quota_remaining > limit) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE", billing);
1365
+ if (limit !== void 0 && quota.quota_remaining !== void 0 && Math.abs(quota.quota_remaining / limit * 100 - percent) > 1) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE", billing);
1366
+ const remaining = quota.quota_remaining;
1367
+ return {
1368
+ state: "ready",
1369
+ billing,
1370
+ budget: "individual",
1371
+ percentUsed: 100 - percent,
1372
+ ...dates,
1373
+ ...limit === void 0 ? {} : { limit },
1374
+ ...remaining === void 0 ? {} : { remaining },
1375
+ ...limit === void 0 || remaining === void 0 ? {} : { used: limit - remaining }
1376
+ };
1377
+ }
1378
+ //#endregion
1379
+ //#region lib/types/copilot-usage-host.js
1380
+ var __runInitializers$1 = function(thisArg, initializers, value) {
1381
+ var useValue = arguments.length > 2;
1382
+ for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
1383
+ return useValue ? value : void 0;
1384
+ };
1385
+ var __esDecorate$1 = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
1386
+ function accept(f) {
1387
+ if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
1388
+ return f;
1389
+ }
1390
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
1391
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
1392
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
1393
+ var _, done = false;
1394
+ for (var i = decorators.length - 1; i >= 0; i--) {
1395
+ var context = {};
1396
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
1397
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
1398
+ context.addInitializer = function(f) {
1399
+ if (done) throw new TypeError("Cannot add initializers after decoration has completed");
1400
+ extraInitializers.push(accept(f || null));
1401
+ };
1402
+ var result = (0, decorators[i])(kind === "accessor" ? {
1403
+ get: descriptor.get,
1404
+ set: descriptor.set
1405
+ } : descriptor[key], context);
1406
+ if (kind === "accessor") {
1407
+ if (result === void 0) continue;
1408
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
1409
+ if (_ = accept(result.get)) descriptor.get = _;
1410
+ if (_ = accept(result.set)) descriptor.set = _;
1411
+ if (_ = accept(result.init)) initializers.unshift(_);
1412
+ } else if (_ = accept(result)) {
1413
+ if (kind === "field") initializers.unshift(_);
1414
+ else descriptor[key] = _;
1415
+ }
1416
+ }
1417
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
1418
+ done = true;
1419
+ };
1420
+ const COPILOT_USAGE_ENDPOINT = "https://api.github.com/copilot_internal/user";
1421
+ const TTL_MS = 6e4;
1422
+ const REFRESH_INTERVAL_MS = 1e4;
1423
+ const FAILURE_COOLDOWN_MS = 3e4;
1424
+ const TIMEOUT_MS = 1e4;
1425
+ const MAX_BODY_BYTES = 262144;
1426
+ var UsageFailure = class extends Error {
1427
+ diagnostic;
1428
+ constructor(diagnostic) {
1429
+ super(diagnostic);
1430
+ this.diagnostic = diagnostic;
1431
+ }
1432
+ };
1433
+ function failure$2(diagnostic) {
1434
+ throw new UsageFailure(diagnostic);
1435
+ }
1436
+ async function readBody(response, signal) {
1437
+ const length = response.headers.get("content-length");
1438
+ if (length !== null && (!/^\d+$/u.test(length) || Number(length) > MAX_BODY_BYTES)) {
1439
+ response.body?.cancel().catch(() => void 0);
1440
+ failure$2("COPILOT_USAGE_BODY_TOO_LARGE");
1441
+ }
1442
+ if (!response.body) failure$2("COPILOT_USAGE_INVALID_RESPONSE");
1443
+ const reader = response.body.getReader(), chunks = [];
1444
+ const cancel = () => {
1445
+ reader.cancel().catch(() => void 0);
1446
+ };
1447
+ signal.addEventListener("abort", cancel, { once: true });
1448
+ if (signal.aborted) cancel();
1449
+ let size = 0;
1450
+ try {
1451
+ while (true) {
1452
+ const next = await reader.read();
1453
+ if (next.done) break;
1454
+ size += next.value.byteLength;
1455
+ if (size > MAX_BODY_BYTES) {
1456
+ reader.cancel().catch(() => void 0);
1457
+ failure$2("COPILOT_USAGE_BODY_TOO_LARGE");
1458
+ }
1459
+ chunks.push(next.value);
1460
+ }
1461
+ } finally {
1462
+ signal.removeEventListener("abort", cancel);
1463
+ reader.releaseLock();
1464
+ }
1465
+ const bytes = new Uint8Array(size);
1466
+ let offset = 0;
1467
+ for (const chunk of chunks) {
1468
+ bytes.set(chunk, offset);
1469
+ offset += chunk.byteLength;
1470
+ }
1471
+ try {
1472
+ return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
1473
+ } catch {
1474
+ failure$2("COPILOT_USAGE_INVALID_RESPONSE");
1475
+ }
1476
+ }
1477
+ /** One Host-owned, memory-only account cache. No auth renewal, persistence or model transport. */
1478
+ var CopilotUsageSource = class {
1479
+ dependencies;
1480
+ fetcher;
1481
+ now;
1482
+ generation = 0;
1483
+ disposed = false;
1484
+ key;
1485
+ cached;
1486
+ last;
1487
+ nextRefreshAt = 0;
1488
+ failureUntil = 0;
1489
+ flight;
1490
+ constructor(dependencies) {
1491
+ this.dependencies = dependencies;
1492
+ this.fetcher = dependencies.fetch ?? globalThis.fetch;
1493
+ this.now = dependencies.now ?? Date.now;
1494
+ }
1495
+ invalidate() {
1496
+ this.generation++;
1497
+ this.flight?.abort.abort();
1498
+ this.flight = void 0;
1499
+ this.key = void 0;
1500
+ this.cached = void 0;
1501
+ this.last = void 0;
1502
+ this.nextRefreshAt = 0;
1503
+ this.failureUntil = 0;
1504
+ }
1505
+ dispose() {
1506
+ this.disposed = true;
1507
+ this.invalidate();
1508
+ }
1509
+ get() {
1510
+ return this.load(false);
1511
+ }
1512
+ refresh() {
1513
+ return this.load(true);
1514
+ }
1515
+ active(generation) {
1516
+ if (this.disposed) failure$2("COPILOT_USAGE_DISPOSED");
1517
+ if (generation !== this.generation) failure$2("COPILOT_USAGE_ACCOUNT_CHANGED");
1518
+ }
1519
+ async readAuth(generation) {
1520
+ this.active(generation);
1521
+ let stored;
1522
+ try {
1523
+ stored = await this.dependencies.readCredential();
1524
+ } catch {
1525
+ this.active(generation);
1526
+ failure$2("COPILOT_USAGE_CREDENTIALS_UNAVAILABLE");
1527
+ }
1528
+ this.active(generation);
1529
+ if (stored === void 0) failure$2("COPILOT_USAGE_SIGNED_OUT");
1530
+ let grant;
1531
+ try {
1532
+ grant = normalizeGitHubCopilotOAuthCredential(stored);
1533
+ } catch {
1534
+ failure$2("COPILOT_USAGE_INVALID_GRANT");
1535
+ }
1536
+ if (grant.enterpriseUrl !== void 0) failure$2("COPILOT_USAGE_ENTERPRISE_UNSUPPORTED");
1537
+ return {
1538
+ grant,
1539
+ key: copilotAccountKey(grant)
1540
+ };
1541
+ }
1542
+ async assertCurrent(key, generation) {
1543
+ if ((await this.readAuth(generation)).key !== key) failure$2("COPILOT_USAGE_ACCOUNT_CHANGED");
1544
+ }
1545
+ diagnostic(error) {
1546
+ return error instanceof UsageFailure ? error.diagnostic : "COPILOT_USAGE_NETWORK";
1547
+ }
1548
+ async load(force) {
1549
+ let generation = this.generation;
1550
+ let auth;
1551
+ try {
1552
+ auth = await this.readAuth(generation);
1553
+ this.active(generation);
1554
+ } catch (error) {
1555
+ if (generation === this.generation) this.invalidate();
1556
+ return unavailableCopilotUsage(this.diagnostic(error));
1557
+ }
1558
+ if (this.key !== void 0 && this.key !== auth.key) {
1559
+ this.invalidate();
1560
+ generation = this.generation;
1561
+ }
1562
+ this.key = auth.key;
1563
+ if (this.flight) {
1564
+ const result = await this.flight.promise;
1565
+ if (this.disposed) return unavailableCopilotUsage("COPILOT_USAGE_DISPOSED");
1566
+ if (generation !== this.generation) return unavailableCopilotUsage("COPILOT_USAGE_ACCOUNT_CHANGED");
1567
+ return { ...result };
1568
+ }
1569
+ const now = this.now();
1570
+ if (this.last && now < this.failureUntil) return { ...this.last };
1571
+ if (this.cached && (!force && now - this.cached.observedAt < TTL_MS || now < this.nextRefreshAt)) return { ...this.cached };
1572
+ const abort = new AbortController();
1573
+ const promise = this.request(auth, generation, abort);
1574
+ this.flight = {
1575
+ generation,
1576
+ promise,
1577
+ abort
1578
+ };
1579
+ try {
1580
+ return { ...await promise };
1581
+ } finally {
1582
+ if (this.flight?.promise === promise) this.flight = void 0;
1583
+ }
1584
+ }
1585
+ async fetchQuota(auth, abort) {
1586
+ const response = await this.fetcher(COPILOT_USAGE_ENDPOINT, {
1587
+ method: "GET",
1588
+ redirect: "error",
1589
+ signal: abort.signal,
1590
+ cache: "no-store",
1591
+ headers: {
1592
+ Authorization: `token ${auth.grant.refresh}`,
1593
+ Accept: "application/json",
1594
+ "X-GitHub-Api-Version": "2025-04-01"
1595
+ }
1596
+ });
1597
+ if (response.redirected || response.status >= 300 && response.status < 400) {
1598
+ response.body?.cancel().catch(() => void 0);
1599
+ failure$2("COPILOT_USAGE_REDIRECT");
1600
+ }
1601
+ if (!response.ok) {
1602
+ response.body?.cancel().catch(() => void 0);
1603
+ if (response.status === 401 || response.status === 403) failure$2("COPILOT_USAGE_AUTH_REJECTED");
1604
+ if (response.status === 429) failure$2("COPILOT_USAGE_RATE_LIMITED");
1605
+ failure$2("COPILOT_USAGE_HTTP_ERROR");
1606
+ }
1607
+ return readBody(response, abort.signal);
879
1608
  }
880
- var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
881
- var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
882
- var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
883
- var _, done = false;
884
- for (var i = decorators.length - 1; i >= 0; i--) {
885
- var context = {};
886
- for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
887
- for (var p in contextIn.access) context.access[p] = contextIn.access[p];
888
- context.addInitializer = function(f) {
889
- if (done) throw new TypeError("Cannot add initializers after decoration has completed");
890
- extraInitializers.push(accept(f || null));
891
- };
892
- var result = (0, decorators[i])(kind === "accessor" ? {
893
- get: descriptor.get,
894
- set: descriptor.set
895
- } : descriptor[key], context);
896
- if (kind === "accessor") {
897
- if (result === void 0) continue;
898
- if (result === null || typeof result !== "object") throw new TypeError("Object expected");
899
- if (_ = accept(result.get)) descriptor.get = _;
900
- if (_ = accept(result.set)) descriptor.set = _;
901
- if (_ = accept(result.init)) initializers.unshift(_);
902
- } else if (_ = accept(result)) {
903
- if (kind === "field") initializers.unshift(_);
904
- else descriptor[key] = _;
1609
+ async request(auth, generation, abort) {
1610
+ let timer;
1611
+ let removeAbort;
1612
+ let value;
1613
+ let error;
1614
+ try {
1615
+ await this.assertCurrent(auth.key, generation);
1616
+ this.active(generation);
1617
+ this.nextRefreshAt = this.now() + REFRESH_INTERVAL_MS;
1618
+ const cancellation = new Promise((_resolve, reject) => {
1619
+ const cancel = () => reject(new UsageFailure(this.disposed ? "COPILOT_USAGE_DISPOSED" : "COPILOT_USAGE_ACCOUNT_CHANGED"));
1620
+ abort.signal.addEventListener("abort", cancel, { once: true });
1621
+ removeAbort = () => abort.signal.removeEventListener("abort", cancel);
1622
+ timer = setTimeout(() => {
1623
+ reject(new UsageFailure("COPILOT_USAGE_TIMEOUT"));
1624
+ abort.abort();
1625
+ }, TIMEOUT_MS);
1626
+ });
1627
+ value = normalizeCopilotUsage(await Promise.race([this.fetchQuota(auth, abort), cancellation]), this.now());
1628
+ } catch (caught) {
1629
+ error = caught;
1630
+ } finally {
1631
+ if (timer !== void 0) clearTimeout(timer);
1632
+ removeAbort?.();
1633
+ }
1634
+ try {
1635
+ await this.assertCurrent(auth.key, generation);
1636
+ this.active(generation);
1637
+ } catch (caught) {
1638
+ if (generation === this.generation) this.invalidate();
1639
+ return unavailableCopilotUsage(this.diagnostic(caught));
905
1640
  }
1641
+ if (error !== void 0) {
1642
+ const diagnostic = this.diagnostic(error);
1643
+ if (diagnostic === "COPILOT_USAGE_ACCOUNT_CHANGED" || diagnostic === "COPILOT_USAGE_SIGNED_OUT" || diagnostic === "COPILOT_USAGE_INVALID_GRANT" || diagnostic === "COPILOT_USAGE_ENTERPRISE_UNSUPPORTED" || diagnostic === "COPILOT_USAGE_CREDENTIALS_UNAVAILABLE") {
1644
+ this.invalidate();
1645
+ return unavailableCopilotUsage(diagnostic);
1646
+ }
1647
+ const staleAllowed = [
1648
+ "COPILOT_USAGE_NETWORK",
1649
+ "COPILOT_USAGE_TIMEOUT",
1650
+ "COPILOT_USAGE_HTTP_ERROR",
1651
+ "COPILOT_USAGE_RATE_LIMITED"
1652
+ ].includes(diagnostic);
1653
+ if (!staleAllowed) this.cached = void 0;
1654
+ value = staleAllowed && this.cached ? {
1655
+ ...this.cached,
1656
+ state: "stale",
1657
+ diagnostic
1658
+ } : unavailableCopilotUsage(diagnostic);
1659
+ }
1660
+ if (value === void 0) return unavailableCopilotUsage("COPILOT_USAGE_INVALID_RESPONSE");
1661
+ this.last = value;
1662
+ if (value.state === "ready") {
1663
+ this.cached = value;
1664
+ this.failureUntil = 0;
1665
+ } else {
1666
+ if (value.state !== "stale") this.cached = void 0;
1667
+ this.failureUntil = this.now() + FAILURE_COOLDOWN_MS;
1668
+ }
1669
+ return value;
906
1670
  }
907
- if (target) Object.defineProperty(target, contextIn.name, descriptor);
908
- done = true;
909
1671
  };
910
- let SearchRoutingController = (() => {
1672
+ let GitHubCopilotUsageController = (() => {
911
1673
  let _classSuper = TypertRemoteService;
912
1674
  let _instanceExtraInitializers = [];
913
- let _providers_decorators;
914
- return class SearchRoutingController extends _classSuper {
1675
+ let _get_decorators;
1676
+ let _refresh_decorators;
1677
+ return class GitHubCopilotUsageController extends _classSuper {
915
1678
  static {
916
1679
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
917
- _providers_decorators = [Remote];
918
- __esDecorate$1(this, null, _providers_decorators, {
1680
+ _get_decorators = [Remote];
1681
+ _refresh_decorators = [Remote];
1682
+ __esDecorate$1(this, null, _get_decorators, {
919
1683
  kind: "method",
920
- name: "providers",
1684
+ name: "get",
921
1685
  static: false,
922
1686
  private: false,
923
1687
  access: {
924
- has: (obj) => "providers" in obj,
925
- get: (obj) => obj.providers
1688
+ has: (obj) => "get" in obj,
1689
+ get: (obj) => obj.get
1690
+ },
1691
+ metadata: _metadata
1692
+ }, null, _instanceExtraInitializers);
1693
+ __esDecorate$1(this, null, _refresh_decorators, {
1694
+ kind: "method",
1695
+ name: "refresh",
1696
+ static: false,
1697
+ private: false,
1698
+ access: {
1699
+ has: (obj) => "refresh" in obj,
1700
+ get: (obj) => obj.refresh
926
1701
  },
927
1702
  metadata: _metadata
928
1703
  }, null, _instanceExtraInitializers);
@@ -933,36 +1708,31 @@ let SearchRoutingController = (() => {
933
1708
  value: _metadata
934
1709
  });
935
1710
  }
1711
+ source = __runInitializers$1(this, _instanceExtraInitializers);
936
1712
  constructor(ctx) {
937
- super(ctx, "githubCopilotSearchRouting");
938
- __runInitializers$1(this, _instanceExtraInitializers);
1713
+ super(ctx, "githubCopilotUsage");
1714
+ const credentials = createGitHubCopilotCredentialStore(ctx);
1715
+ this.source = new CopilotUsageSource({ readCredential: () => credentials.read(GITHUB_COPILOT_PROVIDER_ID) });
1716
+ ctx.on("credentials/record-updated", (key) => {
1717
+ if (key === "llm-pi-ai/github-copilot") this.source.invalidate();
1718
+ });
1719
+ ctx.on("settings/updated", (namespace) => {
1720
+ if (namespace === "github-copilot" || namespace === "llm-pi-ai") this.source.invalidate();
1721
+ });
1722
+ ctx.effect(() => () => this.source.dispose());
939
1723
  }
940
- providers() {
941
- const unavailable = {
942
- supported: false,
943
- providers: []
944
- };
945
- const catalog = this.ctx.get("githubCopilotSearchCatalog");
946
- if (typeof catalog?.list !== "function") return unavailable;
947
- try {
948
- const ids = catalog.list();
949
- const parsed = SearchProviderCatalogSchema.safeParse({
950
- supported: true,
951
- providers: ids.map((id) => ({ id }))
952
- });
953
- if (!parsed.success || new Set(ids).size !== ids.length) return unavailable;
954
- parsed.data.providers.sort((a, b) => a.id.localeCompare(b.id));
955
- return parsed.data;
956
- } catch {
957
- return unavailable;
958
- }
1724
+ async get() {
1725
+ return this.source.get();
1726
+ }
1727
+ async refresh() {
1728
+ return this.source.refresh();
959
1729
  }
960
1730
  };
961
1731
  })();
962
1732
  //#endregion
963
1733
  //#region package.json
964
1734
  var name$1 = "dsh-github-copilot";
965
- var version = "0.4.0-alpha.30";
1735
+ var version = "0.4.0-alpha.32";
966
1736
  //#endregion
967
1737
  //#region lib/types/probe.js
968
1738
  /**
@@ -3127,36 +3897,6 @@ function assertDshCompatibility(ctx) {
3127
3897
  if (typeof ctx["plugin"] !== "function") incompatible("context.plugin");
3128
3898
  }
3129
3899
  //#endregion
3130
- //#region lib/types/copilot-grant.js
3131
- function nonEmptyOAuthString(value, field) {
3132
- if (typeof value !== "string" || value.trim().length === 0) throw new Error(`github-copilot: OAuth credential field "${field}" must be a non-empty string`);
3133
- return value;
3134
- }
3135
- /**
3136
- * Rebuild pi-ai's GitHub Copilot grant as the exact JSON-safe provider shape.
3137
- * Prototype and unrelated extension members are intentionally not preserved.
3138
- */
3139
- function normalizeGitHubCopilotOAuthCredential(credential) {
3140
- if (typeof credential !== "object" || credential === null) throw new Error("github-copilot: OAuth credential must be an object");
3141
- if (Reflect.get(credential, "type") !== "oauth") throw new Error("github-copilot: OAuth credential type must be \"oauth\"");
3142
- const refresh = nonEmptyOAuthString(Reflect.get(credential, "refresh"), "refresh");
3143
- const access = nonEmptyOAuthString(Reflect.get(credential, "access"), "access");
3144
- const expires = Reflect.get(credential, "expires");
3145
- if (typeof expires !== "number" || !Number.isFinite(expires)) throw new Error("github-copilot: OAuth credential field \"expires\" must be a finite number");
3146
- const enterpriseUrl = Reflect.get(credential, "enterpriseUrl");
3147
- if (enterpriseUrl !== void 0 && (typeof enterpriseUrl !== "string" || enterpriseUrl.trim().length === 0)) throw new Error("github-copilot: OAuth credential field \"enterpriseUrl\" must be a non-empty string");
3148
- const availableModelIds = Reflect.get(credential, "availableModelIds");
3149
- if (availableModelIds !== void 0 && (!Array.isArray(availableModelIds) || !availableModelIds.every((modelId) => typeof modelId === "string" && modelId.trim().length > 0))) throw new Error("github-copilot: OAuth credential field \"availableModelIds\" must be an array of non-empty strings");
3150
- return {
3151
- type: "oauth",
3152
- refresh,
3153
- access,
3154
- expires,
3155
- ...enterpriseUrl === void 0 ? {} : { enterpriseUrl },
3156
- ...availableModelIds === void 0 ? {} : { availableModelIds: [...new Set(availableModelIds)] }
3157
- };
3158
- }
3159
- //#endregion
3160
3900
  //#region lib/types/temporary-models.js
3161
3901
  const GPT_6_ASTRA = {
3162
3902
  id: "gpt-6-astra",
@@ -4061,189 +4801,60 @@ let GitHubCopilotAuthorizationController = (() => {
4061
4801
  },
4062
4802
  prompt: (prompt) => {
4063
4803
  if (prompt.kind === "text" && /GitHub Enterprise URL\/domain/i.test(prompt.message)) return Promise.resolve("");
4064
- return Promise.reject(/* @__PURE__ */ new Error(`github-copilot: this browser bridge cannot answer authorization prompt "${prompt.message}"`));
4065
- }
4066
- }
4067
- }).then(async (outcome) => {
4068
- if (outcome.status === "authorized") {
4069
- this.notices = [];
4070
- await this.ensureProviderProfile();
4071
- return;
4072
- }
4073
- this.notices = [];
4074
- }).catch((error) => {
4075
- this.notices = [];
4076
- this.failure = messageOf(error);
4077
- this.ctx.logger.error("github-copilot: GitHub Copilot authorization failed");
4078
- this.ctx.logger.error(error);
4079
- }).finally(() => {
4080
- this.attempt = void 0;
4081
- });
4082
- this.attempt = running;
4083
- return this.status();
4084
- }
4085
- async cancel() {
4086
- const authorization = service(this.ctx, "authorization", [
4087
- "describe",
4088
- "begin",
4089
- "cancel"
4090
- ]);
4091
- this.notices = [];
4092
- authorization.cancel(GITHUB_COPILOT_CREDENTIAL_KEY);
4093
- return this.status();
4094
- }
4095
- async signOut() {
4096
- if (service(this.ctx, "authorization", [
4097
- "describe",
4098
- "begin",
4099
- "cancel"
4100
- ]).describe("llm-pi-ai/github-copilot")?.inFlight === true) throw new Error("github-copilot: cancel the active sign-in attempt before signing out");
4101
- await service(this.ctx, "credentials", ["describeRecord", "deleteRecord"]).deleteRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
4102
- this.notices = [];
4103
- this.failure = void 0;
4104
- this.reconciliationFailed = false;
4105
- return this.status();
4106
- }
4107
- async ensureProviderProfile() {
4108
- try {
4109
- await ensureGitHubCopilotProviderProfile(this.ctx);
4110
- this.reconciliationFailed = false;
4111
- } catch {
4112
- this.reconciliationFailed = true;
4113
- this.ctx.logger.warn("github-copilot: route reconciliation failed; review route status before retrying");
4114
- }
4115
- }
4116
- };
4117
- })();
4118
- //#endregion
4119
- //#region lib/types/copilot-auth.js
4120
- /**
4121
- * Provider-side GitHub Copilot auth resolution over the same credential record
4122
- * that DSH's built-in llm-pi-ai adapter owns.
4123
- */
4124
- function credentialService(ctx) {
4125
- const candidate = ctx.get("credentials");
4126
- if (typeof candidate !== "object" || candidate === null) throw new Error("github-copilot: DSH credentials service is unavailable");
4127
- for (const method of [
4128
- "readRecord",
4129
- "listRecords",
4130
- "modifyRecord",
4131
- "deleteRecord"
4132
- ]) if (typeof Reflect.get(candidate, method) !== "function") throw new Error(`github-copilot: required DSH credentials API "credentials.${method}" is unavailable`);
4133
- return candidate;
4134
- }
4135
- function toCredential(record) {
4136
- if (record === void 0) return void 0;
4137
- if (record.kind === "api-key") return {
4138
- type: "api_key",
4139
- ...record.key === void 0 ? {} : { key: record.key },
4140
- ...record.env === void 0 ? {} : { env: { ...record.env } }
4141
- };
4142
- if (typeof record.payload !== "object" || record.payload === null) throw new Error("github-copilot: stored llm-pi-ai GitHub Copilot grant is not an object");
4143
- return normalizeGitHubCopilotOAuthCredential(record.payload);
4144
- }
4145
- function toRecord(credential) {
4146
- if (credential.type === "api_key") return {
4147
- kind: "api-key",
4148
- ...credential.key === void 0 ? {} : { key: credential.key },
4149
- ...credential.env === void 0 ? {} : { env: { ...credential.env } }
4150
- };
4151
- return {
4152
- kind: "grant",
4153
- payload: normalizeGitHubCopilotOAuthCredential(credential)
4154
- };
4155
- }
4156
- /** Bind exactly one logical route to the canonical Host-owned credential record. */
4157
- function createGitHubCopilotCredentialStore(ctx, logicalProviderId = GITHUB_COPILOT_PROVIDER_ID) {
4158
- const assertProvider = (providerId) => {
4159
- if (providerId !== logicalProviderId) throw new Error("COPILOT_CREDENTIAL_PROVIDER_MISMATCH");
4160
- };
4161
- return {
4162
- read: async (providerId) => {
4163
- assertProvider(providerId);
4164
- return toCredential(await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY));
4165
- },
4166
- list: async () => {
4167
- const record = await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
4168
- return record === void 0 ? [] : [{
4169
- providerId: logicalProviderId,
4170
- type: record.kind === "api-key" ? "api_key" : "oauth"
4171
- }];
4172
- },
4173
- modify: async (providerId, mutate) => {
4174
- assertProvider(providerId);
4175
- return toCredential(await credentialService(ctx).modifyRecord(GITHUB_COPILOT_CREDENTIAL_KEY, async (current) => {
4176
- const next = await mutate(toCredential(current));
4177
- return next === void 0 ? void 0 : toRecord(next);
4178
- }));
4179
- },
4180
- delete: async (providerId) => {
4181
- assertProvider(providerId);
4182
- await credentialService(ctx).deleteRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
4804
+ return Promise.reject(/* @__PURE__ */ new Error(`github-copilot: this browser bridge cannot answer authorization prompt "${prompt.message}"`));
4805
+ }
4806
+ }
4807
+ }).then(async (outcome) => {
4808
+ if (outcome.status === "authorized") {
4809
+ this.notices = [];
4810
+ await this.ensureProviderProfile();
4811
+ return;
4812
+ }
4813
+ this.notices = [];
4814
+ }).catch((error) => {
4815
+ this.notices = [];
4816
+ this.failure = messageOf(error);
4817
+ this.ctx.logger.error("github-copilot: GitHub Copilot authorization failed");
4818
+ this.ctx.logger.error(error);
4819
+ }).finally(() => {
4820
+ this.attempt = void 0;
4821
+ });
4822
+ this.attempt = running;
4823
+ return this.status();
4183
4824
  }
4184
- };
4185
- }
4186
- /**
4187
- * Build one resolver whose pi-ai collection refreshes OAuth credentials inside
4188
- * the DSH record's serialized modify operation before exposing request auth.
4189
- */
4190
- function createGitHubCopilotTokenResolver(ctx, onCredentialChanged) {
4191
- const models = createModels({ credentials: createGitHubCopilotCredentialStore(ctx) });
4192
- const provider = githubCopilotProvider();
4193
- if (provider.auth.oauth === void 0) throw new Error("github-copilot: native OAuth method is unavailable");
4194
- models.setProvider({
4195
- ...provider,
4196
- auth: { oauth: provider.auth.oauth }
4197
- });
4198
- return async (modelId) => {
4199
- const snapshot = readCopilotCatalog(ctx);
4200
- const native = new Map(snapshot.models.map((model) => [model.id, model])).get(modelId);
4201
- if (native === void 0) throw new Error(`github-copilot: pi-ai catalog has no GitHub Copilot model "${modelId}"`);
4202
- const resolved = await models.getAuth("github-copilot");
4203
- if (resolved === void 0) return void 0;
4204
- if (onCredentialChanged !== void 0) try {
4205
- await onCredentialChanged();
4206
- } catch (error) {
4207
- ctx.logger.warn("github-copilot: provider route reconciliation will retry after a later auth resolution");
4208
- ctx.logger.warn(error);
4825
+ async cancel() {
4826
+ const authorization = service(this.ctx, "authorization", [
4827
+ "describe",
4828
+ "begin",
4829
+ "cancel"
4830
+ ]);
4831
+ this.notices = [];
4832
+ authorization.cancel(GITHUB_COPILOT_CREDENTIAL_KEY);
4833
+ return this.status();
4834
+ }
4835
+ async signOut() {
4836
+ if (service(this.ctx, "authorization", [
4837
+ "describe",
4838
+ "begin",
4839
+ "cancel"
4840
+ ]).describe("llm-pi-ai/github-copilot")?.inFlight === true) throw new Error("github-copilot: cancel the active sign-in attempt before signing out");
4841
+ await service(this.ctx, "credentials", ["describeRecord", "deleteRecord"]).deleteRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
4842
+ this.notices = [];
4843
+ this.failure = void 0;
4844
+ this.reconciliationFailed = false;
4845
+ return this.status();
4846
+ }
4847
+ async ensureProviderProfile() {
4848
+ try {
4849
+ await ensureGitHubCopilotProviderProfile(this.ctx);
4850
+ this.reconciliationFailed = false;
4851
+ } catch {
4852
+ this.reconciliationFailed = true;
4853
+ this.ctx.logger.warn("github-copilot: route reconciliation failed; review route status before retrying");
4854
+ }
4209
4855
  }
4210
- const stored = await credentialService(ctx).readRecord(GITHUB_COPILOT_CREDENTIAL_KEY);
4211
- if (!(stored?.kind === "grant" && (normalizeGitHubCopilotOAuthCredential(stored.payload).availableModelIds ?? []).includes(modelId))) throw new Error(`github-copilot: model "${modelId}" is not available for the signed-in Copilot account`);
4212
- const apiKey = resolved.auth.apiKey;
4213
- if (apiKey === void 0) return void 0;
4214
- return {
4215
- apiKey,
4216
- baseURL: trustedCopilotBaseUrl(resolved.auth.baseUrl ?? native.baseUrl, enterpriseDomainOf(stored)),
4217
- ...resolved.auth.headers === void 0 ? {} : { headers: resolved.auth.headers }
4218
- };
4219
4856
  };
4220
- }
4221
- function enterpriseDomainOf(record) {
4222
- if (record?.kind !== "grant" || typeof record.payload !== "object" || record.payload === null) return void 0;
4223
- const value = Reflect.get(record.payload, "enterpriseUrl");
4224
- if (typeof value !== "string" || value.trim().length === 0) return void 0;
4225
- try {
4226
- return (value.includes("://") ? new URL(value) : new URL(`https://${value}`)).hostname.toLowerCase();
4227
- } catch {
4228
- throw new Error("github-copilot: stored enterpriseUrl is invalid");
4229
- }
4230
- }
4231
- /** Validate an OAuth-derived endpoint without disclosing its value or credential payload. */
4232
- function trustedGitHubCopilotBaseUrl(value, grant) {
4233
- return trustedCopilotBaseUrl(value, enterpriseDomainOf({
4234
- kind: "grant",
4235
- payload: normalizeGitHubCopilotOAuthCredential(grant)
4236
- }));
4237
- }
4238
- function trustedCopilotBaseUrl(value, enterpriseDomain) {
4239
- if (value === void 0) throw new Error("github-copilot: pi-ai resolved no Copilot API base URL");
4240
- const url = new URL(value);
4241
- const hostname = url.hostname.toLowerCase();
4242
- const githubHosted = /^api(?:\.[a-z0-9-]+)+\.githubcopilot\.com$/u.test(hostname);
4243
- const enterpriseHosted = enterpriseDomain !== void 0 && hostname === `copilot-api.${enterpriseDomain}`;
4244
- if (!(url.protocol === "https:" && (githubHosted || enterpriseHosted)) || url.username.length > 0 || url.password.length > 0) throw new Error("github-copilot: pi-ai resolved an untrusted Copilot API base URL");
4245
- return url.origin;
4246
- }
4857
+ })();
4247
4858
  //#endregion
4248
4859
  //#region lib/types/tool-schema-compat.js
4249
4860
  const ESCALATION_FIELDS = /* @__PURE__ */ new Set(["sandbox_permissions", "justification"]);
@@ -4603,15 +5214,24 @@ function createAccountProvider(descriptors, guard, baseURL) {
4603
5214
  }));
4604
5215
  headers.authorization = `Bearer ${options.apiKey}`;
4605
5216
  headers["x-api-key"] = null;
5217
+ let unauthorized = false;
5218
+ const fetch = options.fetch ?? globalThis.fetch;
5219
+ const observeResponse = async (input, init) => {
5220
+ const response = await fetch(input, init);
5221
+ if (response.status === 401) unauthorized = true;
5222
+ return response;
5223
+ };
4606
5224
  const wireOptions = model.api === "anthropic-messages" ? {
4607
5225
  ...options,
4608
5226
  signal: lease.signal,
4609
5227
  apiKey: void 0,
4610
- headers
5228
+ headers,
5229
+ fetch: observeResponse
4611
5230
  } : {
4612
5231
  ...options,
4613
5232
  signal: lease.signal,
4614
- headers
5233
+ headers,
5234
+ fetch: observeResponse
4615
5235
  };
4616
5236
  if (!hasApi(model, "openai-responses") && !hasApi(model, "openai-completions") && !hasApi(model, "anthropic-messages")) {
4617
5237
  lease.release();
@@ -4622,6 +5242,9 @@ function createAccountProvider(descriptors, guard, baseURL) {
4622
5242
  yield* native.streamSimple(model, context, wireOptions);
4623
5243
  } finally {
4624
5244
  lease.release();
5245
+ if (unauthorized && !guard.signal.aborted && !options.signal?.aborted) try {
5246
+ guard.onUnauthorized?.();
5247
+ } catch {}
4625
5248
  }
4626
5249
  })();
4627
5250
  })
@@ -4632,142 +5255,6 @@ function createAccountProvider(descriptors, guard, baseURL) {
4632
5255
  };
4633
5256
  }
4634
5257
  //#endregion
4635
- //#region lib/types/account-model-auth.js
4636
- /** Native getAuth renews within five minutes; reserve another thirty seconds for metadata preparation. */
4637
- const ACCOUNT_MODEL_AUTH_MIN_VALIDITY_MS = 33e4;
4638
- /** An opaque, process-local account key. Never a substitute for request credentials. */
4639
- function copilotAccountKey(grant) {
4640
- return createHash("sha256").update(`${grant.refresh.length}:`).update(grant.refresh).update(grant.enterpriseUrl ?? "").digest("hex");
4641
- }
4642
- /** Compare normalized permission snapshots without retaining model credentials. */
4643
- function copilotEntitlementKey(grant) {
4644
- const ids = grant.availableModelIds;
4645
- const normalized = ids === void 0 ? "missing" : JSON.stringify([...new Set(ids)].sort());
4646
- return createHash("sha256").update(normalized).digest("hex");
4647
- }
4648
- function authEntitlementKey(auth) {
4649
- return copilotEntitlementKey({ ...auth.availableModelIds === void 0 ? {} : { availableModelIds: [...auth.availableModelIds] } });
4650
- }
4651
- function active(signal) {
4652
- if (signal.aborted) throw new Error("COPILOT_ACCOUNT_AUTH_ABORTED");
4653
- }
4654
- /**
4655
- * Bind model discovery to the existing native OAuth lifecycle. No model catalog,
4656
- * login, environment fallback or separate credential record is introduced here.
4657
- */
4658
- function createAccountModelAuth(credentials) {
4659
- const native = githubCopilotProvider();
4660
- const oauth = native.auth.oauth;
4661
- if (oauth === void 0) throw new Error("COPILOT_ACCOUNT_OAUTH_UNAVAILABLE");
4662
- const readGrant = async (signal) => {
4663
- active(signal);
4664
- const stored = await credentials.read(GITHUB_COPILOT_PROVIDER_ID);
4665
- active(signal);
4666
- if (stored?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
4667
- return normalizeGitHubCopilotOAuthCredential(stored);
4668
- };
4669
- const matches = (grant, key) => {
4670
- if (copilotAccountKey(grant) !== key) throw new Error("COPILOT_ACCOUNT_CHANGED");
4671
- };
4672
- const current = async (auth, signal) => {
4673
- active(signal);
4674
- const grant = await readGrant(signal);
4675
- matches(grant, auth.accountKey);
4676
- if (grant.access !== auth.apiKey || grant.expires <= Date.now()) throw new Error("COPILOT_ACCOUNT_TOKEN_CHANGED");
4677
- if (copilotEntitlementKey(grant) !== authEntitlementKey(auth)) throw new Error("COPILOT_ACCOUNT_ENTITLEMENT_CHANGED");
4678
- const derived = await oauth.toAuth(grant);
4679
- active(signal);
4680
- if (derived.apiKey !== auth.apiKey || trustedGitHubCopilotBaseUrl(derived.baseUrl, grant) !== auth.baseURL) throw new Error("COPILOT_ACCOUNT_AUTH_CHANGED");
4681
- const latest = await readGrant(signal);
4682
- matches(latest, auth.accountKey);
4683
- if (latest.access !== auth.apiKey || latest.expires <= Date.now()) throw new Error("COPILOT_ACCOUNT_TOKEN_CHANGED");
4684
- if (copilotEntitlementKey(latest) !== authEntitlementKey(auth)) throw new Error("COPILOT_ACCOUNT_ENTITLEMENT_CHANGED");
4685
- };
4686
- return {
4687
- async resolveAuth(signal) {
4688
- try {
4689
- const key = copilotAccountKey(await readGrant(signal));
4690
- const guardedOAuth = {
4691
- ...oauth,
4692
- login: async () => {
4693
- throw new Error("COPILOT_ACCOUNT_USE_CANONICAL_SIGN_IN");
4694
- },
4695
- async refresh(credential, requestSignal) {
4696
- active(signal);
4697
- matches(normalizeGitHubCopilotOAuthCredential(credential), key);
4698
- const result = normalizeGitHubCopilotOAuthCredential(await oauth.refresh(credential, requestSignal === void 0 ? signal : AbortSignal.any([signal, requestSignal])));
4699
- active(signal);
4700
- matches(result, key);
4701
- return result;
4702
- },
4703
- async toAuth(credential) {
4704
- active(signal);
4705
- const grant = normalizeGitHubCopilotOAuthCredential(credential);
4706
- matches(grant, key);
4707
- const result = await oauth.toAuth(grant);
4708
- active(signal);
4709
- return {
4710
- ...result,
4711
- baseUrl: trustedGitHubCopilotBaseUrl(result.baseUrl, grant)
4712
- };
4713
- }
4714
- };
4715
- const models = createModels({ credentials: {
4716
- read: async (provider) => {
4717
- const value = await credentials.read(provider);
4718
- active(signal);
4719
- if (value?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
4720
- matches(normalizeGitHubCopilotOAuthCredential(value), key);
4721
- return value;
4722
- },
4723
- list: () => credentials.list(),
4724
- modify: (provider, mutate) => credentials.modify(provider, async (value) => {
4725
- active(signal);
4726
- if (value?.type !== "oauth") throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
4727
- matches(normalizeGitHubCopilotOAuthCredential(value), key);
4728
- const next = await mutate(value);
4729
- active(signal);
4730
- if (next !== void 0) matches(normalizeGitHubCopilotOAuthCredential(next), key);
4731
- return next;
4732
- }),
4733
- delete: async () => {
4734
- throw new Error("COPILOT_ACCOUNT_DISCOVERY_CANNOT_SIGN_OUT");
4735
- }
4736
- } });
4737
- models.setProvider({
4738
- ...native,
4739
- auth: { oauth: guardedOAuth }
4740
- });
4741
- const resolved = await models.getAuth(GITHUB_COPILOT_PROVIDER_ID, {
4742
- signal,
4743
- minOAuthValidityMs: ACCOUNT_MODEL_AUTH_MIN_VALIDITY_MS
4744
- });
4745
- active(signal);
4746
- if (resolved?.auth.apiKey === void 0) throw new Error("COPILOT_ACCOUNT_OAUTH_REQUIRED");
4747
- const grant = await readGrant(signal);
4748
- matches(grant, key);
4749
- const auth = {
4750
- apiKey: resolved.auth.apiKey,
4751
- baseURL: trustedGitHubCopilotBaseUrl(resolved.auth.baseUrl, grant),
4752
- accountKey: key,
4753
- ...grant.availableModelIds === void 0 ? {} : { availableModelIds: Object.freeze([...grant.availableModelIds]) }
4754
- };
4755
- await current(auth, signal);
4756
- return Object.freeze(auth);
4757
- } catch {
4758
- throw new Error(signal.aborted ? "COPILOT_ACCOUNT_AUTH_ABORTED" : "COPILOT_ACCOUNT_AUTH_FAILED");
4759
- }
4760
- },
4761
- async assertAuthCurrent(auth, signal) {
4762
- try {
4763
- await current(auth, signal);
4764
- } catch {
4765
- throw new Error(signal.aborted ? "COPILOT_ACCOUNT_AUTH_ABORTED" : "COPILOT_ACCOUNT_AUTH_CHANGED");
4766
- }
4767
- }
4768
- };
4769
- }
4770
- //#endregion
4771
5258
  //#region lib/types/account-model-catalog.js
4772
5259
  /** Pure normalization of account /models metadata; no transport, credentials, or model-name rules. */
4773
5260
  const ACCOUNT_MODEL_CATALOG_LIMITS = Object.freeze({
@@ -5890,7 +6377,21 @@ function apply$1(ctx, config = {}) {
5890
6377
  const budgetSettings = requestBudgetSettings ?? (() => requestBudget ?? {});
5891
6378
  resolveRequestBudgetPolicy(budgetSettings());
5892
6379
  const store = createGitHubCopilotCredentialStore(ctx, GITHUB_COPILOT_PREVIEW_PROVIDER_ID);
5893
- const nativeAuth = createAccountModelAuth(createGitHubCopilotCredentialStore(ctx));
6380
+ let rejectedAuth;
6381
+ let authRecovery;
6382
+ const nativeAuth = createAccountModelAuth(createGitHubCopilotCredentialStore(ctx), (grant) => {
6383
+ const accountKey = copilotAccountKey(grant);
6384
+ if (rejectedAuth?.accountKey !== accountKey || rejectedAuth.tokenFingerprint !== tokenFingerprint(grant.access)) return false;
6385
+ const now = Date.now();
6386
+ const cooldown = Math.max(1e3, cacheSettings().accountModelFailureCooldownMs ?? 3e5);
6387
+ const age = authRecovery === void 0 ? void 0 : now - authRecovery.at;
6388
+ if (!Number.isFinite(now) || authRecovery?.accountKey === accountKey && (age === void 0 || !Number.isFinite(age) || age < cooldown)) throw failure("COPILOT_PREVIEW_AUTH_RECOVERY_COOLDOWN");
6389
+ authRecovery = {
6390
+ accountKey,
6391
+ at: now
6392
+ };
6393
+ return true;
6394
+ });
5894
6395
  const nativeModels = getBuiltinModels("github-copilot");
5895
6396
  const nativeApis = new Map(nativeModels.map((model) => [model.id, model.api]));
5896
6397
  let lastValidatedProof;
@@ -6086,7 +6587,15 @@ function apply$1(ctx, config = {}) {
6086
6587
  const optionsFor = (lease, inspectRequest) => {
6087
6588
  const guard = {
6088
6589
  ...lifetime.guard(lease),
6089
- ...inspectRequest === void 0 ? {} : { inspectRequest }
6590
+ ...inspectRequest === void 0 ? {} : { inspectRequest },
6591
+ onUnauthorized() {
6592
+ if (lease === void 0 || !lifetime.isCurrent(lease.revision) || source.readDisplaySnapshot() !== lease.snapshot || provenSnapshot !== lease.snapshot || snapshotProof !== lease.proof) return;
6593
+ rejectedAuth = lease.proof;
6594
+ lifetime.change();
6595
+ provenSnapshot = void 0;
6596
+ snapshotProof = void 0;
6597
+ lastValidatedProof = void 0;
6598
+ }
6090
6599
  };
6091
6600
  const { provider } = createAccountProvider(lease?.snapshot.models ?? [], guard, lease?.proof.baseURL ?? "https://api.individual.githubcopilot.com");
6092
6601
  const profile = Object.freeze({
@@ -6303,6 +6812,7 @@ function activate(ctx, config) {
6303
6812
  ctx.plugin(GitHubCopilotAuthorizationController);
6304
6813
  ctx.plugin(GitHubCopilotDualModel);
6305
6814
  ctx.plugin(SearchRoutingController);
6815
+ ctx.plugin(GitHubCopilotUsageController);
6306
6816
  const resolveGitHubCopilotToken = createGitHubCopilotTokenResolver(ctx, async () => {
6307
6817
  await ensureGitHubCopilotProviderProfile(ctx);
6308
6818
  });