import type { FetchImpl, Message } from "../types.js"; /** * Infer whether the current request to Copilot is user-initiated or agent-initiated. * Accepts `unknown[]` because providers may pass pre-converted message shapes. */ export type CopilotInitiator = "user" | "agent"; export type CopilotPremiumRequests = number; export type CopilotDynamicHeaders = { headers: Record; initiator: CopilotInitiator; premiumRequests: CopilotPremiumRequests; }; export declare function resolveGitHubCopilotBaseUrl(baseUrl: string | undefined, apiKey: string | undefined): string | undefined; /** * Opt-in `Copilot-Integration-Id` override for chat and model-policy requests. * Reads `COPILOT_INTEGRATION_ID`; unset/invalid keeps the chat-surface default * (`COPILOT_CHAT_INTEGRATION_ID`). Model discovery keeps the CLI identity: it * unlocks enterprise/experimental models and listing models is not * policy-gated the way chat completions are (#11372). */ export declare function resolveCopilotIntegrationIdOverride(env?: Record): string | undefined; /** * Effective identity before the chat-surface default: explicit value, then * request headers, then `COPILOT_INTEGRATION_ID`. Pure given its inputs, so * tests inject literals instead of mutating process state. */ export declare function resolveCopilotRequestIdentity(headers?: Record, explicit?: unknown, env?: Record): string | undefined; /** * Stable cache key for a raw Copilot API key envelope on one effective host. * Hashes the bearer with `Bun.hash` (repo-approved hashing API; same * credential-scoped pattern as the GitLab Duo and Codex account keys) so token * bytes never sit in the map as keys; enterprise/business routing inputs and * the normalized effective base URL participate so the same token on two hosts * does not share an entry. */ export declare function getCopilotIntegrationCacheKey(apiKeyRaw: string | undefined, baseUrl?: string): string | undefined; /** Cached working identity for a cache key, if one was learned. */ export declare function getCachedCopilotIntegrationId(cacheKey: string | undefined): string | undefined; /** * Remember the identity that cleared the identity gate for a credential. * Every store refreshes recency, so hot credentials survive eviction. */ export declare function rememberCopilotWorkingIntegrationId(cacheKey: string | undefined, integrationId: unknown): void; /** Clear one cached identity, or the whole cache when no key is given. */ export declare function clearCopilotIntegrationCache(cacheKey?: string): void; /** * Reissue Copilot client-identity denials once with the other surface. * * Chat is the default surface (`COPILOT_CHAT_INTEGRATION_ID`) because Business * organizations that gate premium models per client surface commonly allow * chat while blocking CLI/agentic clients (issue #11372). Other Business and * Enterprise orgs do the opposite and reject the chat identity — as an HTTP 403 * or, on `api.business.githubcopilot.com`, an HTTP 400 `model_not_supported` * (issue #11669). Both denials retry once as the CLI. The retry fires only for * requests carrying the chat default and only when the caller resolved no * explicit identity — an explicit choice is never second-guessed. The denied * body is drained before reissuing, and the retry carries the CLI identity so * the guard passes it through: at most two requests, never a loop. * * When `cacheKey` is set, a 2xx retry remembers its identity via * `rememberCopilotWorkingIntegrationId`, so later streams for the same * credential start at the working shape. A cached CLI start that is itself * denied (stale after an org-policy flip) retries once as chat and relearns. * Only a 2xx retry proves its identity — 401s deny every identity equally and * 408/429/5xx are transport-retryable (the transport resends the *original* * headers), so those must never be recorded as working. Any non-2xx retry * clears the entry instead: the next stream rediscovers rather than pinning a * shape that just failed. */ export declare function wrapFetchForCopilotFallback(base: FetchImpl | undefined, enabled: boolean, integrationId?: unknown, cacheKey?: string, /** * Build-time cache provenance: the exact cached value the outgoing headers * were built from. `undefined` rereads the cache at dispatch (direct * callers); `null` pins "cache was empty at build" so a sibling learning * mid-flight cannot change this request's retry decision. */ cacheSnapshot?: string | null): FetchImpl; export declare function inferCopilotInitiator(messages: unknown[]): CopilotInitiator; /** Check whether any message in the conversation contains image content. */ export declare function hasCopilotVisionInput(messages: Message[]): boolean; /** * Resolve an explicitly configured Copilot initiator header, if present. * Handles case-insensitive X-Initiator keys and returns the last valid value. */ export declare function getCopilotInitiatorOverride(headers: Record | undefined): CopilotInitiator | undefined; export type CopilotPlanTier = "free" | "paid"; export declare function getCopilotPremiumMultiplier(premiumMultiplier: number | undefined, planTier?: string): number; export declare function getCopilotPremiumRequests(params: { initiator: CopilotInitiator; premiumMultiplier?: number; planTier?: string; }): CopilotPremiumRequests; /** * Build dynamic Copilot headers that vary per-request. * Static headers (User-Agent, Editor-Version, etc.) come from model.headers. */ export declare function buildCopilotDynamicHeaders(params: { messages: unknown[]; hasImages: boolean; premiumMultiplier?: number; headers?: Record; initiatorOverride?: CopilotInitiator; planTier?: string; /** Enterprise login domain; Enterprise keeps the CLI identity that its private endpoint accepts. */ enterpriseUrl?: string; /** Raw explicit identity; validated here, chat default when absent/invalid. */ integrationId?: unknown; /** Learned working identity for this credential; explicit still wins, then this, then the defaults. */ cachedIntegrationId?: unknown; }): CopilotDynamicHeaders;