@opengeni/subscriptions 1.4.5-canary.38533006839001 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/adapter.d.ts CHANGED
@@ -273,6 +273,14 @@ export interface SubscriptionCoreAdapter<Credential = unknown> {
273
273
  * it passes (design 2.2).
274
274
  */
275
275
  usageReadEndsQuotaExhaustion?: boolean;
276
+ /**
277
+ * With `usageReadEndsQuotaExhaustion`: whether one `fetchUsage` response
278
+ * confirms the connection may serve again. A response can report every
279
+ * window below the limit and still say the account is refused (or that a
280
+ * feature-specific limit is spent); such a reading never ends a stored
281
+ * quota exhaustion. Absent: every reading below the limit does.
282
+ */
283
+ usageReadConfirmsAvailability?(response: unknown): boolean;
276
284
  /**
277
285
  * The provider's usage-endpoint readings are complete: each reports every
278
286
  * window that limits the connection, so a stored exhausted window that a
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/quota.ts","../src/cache.ts","../src/eligibility.ts","../src/reasoning.ts","../src/spread.ts","../src/placement.ts","../src/adapter.ts","../src/settlement.ts"],"sourcesContent":["import type { ModelId, SubscriptionConnection, SubscriptionQuota } from \"./types\";\n\n/**\n * What the shared quota model says about a connection's capacity now\n * (design 2.2). `unknown` is never treated as available or exhausted\n * (SUB-ELIG-06): it participates in ordinary rotation without a ranking penalty (D-14).\n */\nexport type QuotaCapacity =\n | { kind: \"available\" }\n | { kind: \"unknown\" }\n /** `resetsAt` is null when the provider did not say when it resets. */\n | { kind: \"exhausted\"; resetsAt: number | null };\n\nconst UNKNOWN: QuotaCapacity = Object.freeze({ kind: \"unknown\" });\nconst AVAILABLE: QuotaCapacity = Object.freeze({ kind: \"available\" });\n\n/**\n * Capacity of a quota observation at `now`.\n *\n * - An exhaustion deadline (`exhaustedUntil`) in the future is authoritative.\n * - A quota exhaustion whose deadline has passed is an observed reset, so it\n * counts as known capacity; a passed rate-limit deadline says nothing about\n * quota.\n * - A window reported exhausted stays exhausted until its reset; an exhausted\n * window whose reset has passed counts as reset (known capacity).\n * - With nothing exhausted, any window with a known status is known capacity.\n * - No observation, only windows of unknown status, or an observation older\n * than `staleAfterMs` (or undated, when a bound is given), is unknown.\n */\nexport function quotaCapacity(\n quota: SubscriptionQuota | null,\n now: number,\n staleAfterMs?: number,\n): QuotaCapacity {\n if (!quota) return UNKNOWN;\n // A stale or undated observation no longer says the account has capacity\n // (SUB-ELIG-06); observed exhaustion still stands until its reset.\n const stale =\n staleAfterMs !== undefined &&\n (quota.observedAt === null || now - quota.observedAt > staleAfterMs);\n let exhausted = false;\n let resetsAt: number | null = null;\n let resetUnknown = false;\n const extend = (at: number | null) => {\n exhausted = true;\n if (at === null) resetUnknown = true;\n else resetsAt = resetsAt === null ? at : Math.max(resetsAt, at);\n };\n let known = false;\n if (quota.exhaustedUntil !== null) {\n if (quota.exhaustedUntil > now) extend(quota.exhaustedUntil);\n else if (quota.exhaustedKind === \"quota\" && !stale) known = true;\n }\n for (const window of quota.windows) {\n if (window.status === \"exhausted\") {\n if (window.resetsAt === null || window.resetsAt > now) extend(window.resetsAt);\n else if (!stale) known = true;\n } else if (window.status !== \"unknown\" && !stale) {\n known = true;\n }\n }\n if (exhausted) return { kind: \"exhausted\", resetsAt: resetUnknown ? null : resetsAt };\n return known ? AVAILABLE : UNKNOWN;\n}\n\n/** End of a model-specific cooldown that is still running, else null. */\nexport function modelCooldownUntil(\n quota: SubscriptionQuota | null,\n modelId: ModelId,\n now: number,\n): number | null {\n const until = quota?.modelCooldowns[modelId];\n return until !== undefined && until > now ? until : null;\n}\n\n/**\n * A quota observation applies only to the credential generation it was made\n * with, so a refusal seen before a refresh cannot quarantine the renewed\n * credential (design 2.2).\n */\nexport function quotaObservationApplies(\n connection: Pick<SubscriptionConnection, \"refreshGeneration\">,\n observation: Pick<SubscriptionQuota, \"observedRefreshGeneration\">,\n): boolean {\n return observation.observedRefreshGeneration === connection.refreshGeneration;\n}\n\n/**\n * Apply an observation to the stored quota (design 2.2). It applies only to\n * the current credential generation, never replaces a newer or dated reading\n * with an older or undated one, and never shortens a running exhaustion\n * deadline or model cooldown: a later usage reading without them does not\n * clear them. A stored exhausted window with a known reset that an\n * observation does not report at all stays until that reset (one that\n * reports the window again replaces it), unless the observation is a\n * usage-endpoint reading of an adapter that declares its usage readings\n * complete (`options.usageReadingsComplete`, from the adapter's\n * `usageReadingsComplete`): such a reading replaces the stored windows, so\n * it can show an early reset (also of a refusal's fallback window).\n * Response headers and refusals, and the usage readings of every other\n * adapter, report only some windows (fail closed: absent means partial).\n * So a window that only refusals report (a policy hold the caller records\n * itself, for example) ends at its reset or with a complete usage reading,\n * never with a header reading. A caller's own refusal must be tagged\n * `refusal`, not `usage_endpoint`, or a complete adapter's reading would\n * erase the stored windows.\n * Clearing a cooldown early is an explicit, revision-fenced store operation,\n * not an observation. Revision numbers are assigned by the store.\n */\nexport function applyQuotaObservation(\n connection: Pick<SubscriptionConnection, \"refreshGeneration\" | \"quota\">,\n observation: SubscriptionQuota,\n options: {\n /** The observing adapter declares its usage-endpoint readings complete. */\n usageReadingsComplete?: boolean;\n } = {},\n): SubscriptionQuota | null {\n if (!quotaObservationApplies(connection, observation)) return connection.quota;\n const current = connection.quota;\n if (!current || current.observedRefreshGeneration !== connection.refreshGeneration) {\n return observation;\n }\n if (\n current.observedAt !== null &&\n (observation.observedAt === null || observation.observedAt < current.observedAt)\n ) {\n return current;\n }\n // Keep only what still runs after this observation was made: later values\n // win, and deadlines and cooldowns that ended before it are dropped.\n const observedAt = observation.observedAt ?? -Infinity;\n const modelCooldowns: Record<string, number> = {};\n for (const source of [current.modelCooldowns, observation.modelCooldowns]) {\n for (const [modelId, until] of Object.entries(source)) {\n if (until <= observedAt) continue;\n modelCooldowns[modelId] = Math.max(modelCooldowns[modelId] ?? until, until);\n }\n }\n const keepCurrentDeadline =\n current.exhaustedUntil !== null &&\n current.exhaustedUntil > observedAt &&\n (observation.exhaustedUntil === null || observation.exhaustedUntil < current.exhaustedUntil);\n const reported = new Set(observation.windows.map((window) => window.id));\n const omittedExhausted =\n observation.source === \"usage_endpoint\" && options.usageReadingsComplete === true\n ? []\n : current.windows.filter(\n (window) =>\n !reported.has(window.id) &&\n window.status === \"exhausted\" &&\n window.resetsAt !== null &&\n window.resetsAt > observedAt,\n );\n return {\n ...observation,\n windows: omittedExhausted.length\n ? [...observation.windows, ...omittedExhausted]\n : observation.windows,\n modelCooldowns,\n exhaustedUntil: keepCurrentDeadline ? current.exhaustedUntil : observation.exhaustedUntil,\n exhaustedKind: keepCurrentDeadline ? current.exhaustedKind : observation.exhaustedKind,\n };\n}\n","import type { CacheFacts, SessionBinding } from \"./types\";\n\n/**\n * Idle cut-off used for providers whose cut-off has not been measured yet\n * (design 6.1). Erring towards \"warm\" avoids paying a cache miss for a switch\n * that was not needed.\n */\nexport const DEFAULT_IDLE_CUTOFF_MS = 60 * 60 * 1000;\n\n/** How long the provider prompt cache stays warm after a model call. */\nexport function cacheLifetimeMs(facts: CacheFacts | undefined): number {\n if (!facts) return DEFAULT_IDLE_CUTOFF_MS;\n if (facts.kind === \"exact_ttl\") return Math.max(0, facts.ttlMs);\n return facts.cutoffMs === null ? DEFAULT_IDLE_CUTOFF_MS : Math.max(0, facts.cutoffMs);\n}\n\n/**\n * The cache is warm while the session has been idle no longer than the\n * provider's cache lifetime (SUB-STICK-04). Idle time is measured from the\n * latest completed model call, not the turn start, so a long turn is not\n * mistaken for a cold cache. An exact lifetime sent with the latest request\n * (an exact cache TTL) takes precedence over the provider's facts.\n */\nexport function isCacheWarm(\n binding: Pick<SessionBinding, \"lastModelCallAt\" | \"cacheTtlMs\">,\n facts: CacheFacts | undefined,\n now: number,\n): boolean {\n const lifetime =\n binding.cacheTtlMs !== undefined && binding.cacheTtlMs !== null\n ? Math.max(0, binding.cacheTtlMs)\n : cacheLifetimeMs(facts);\n return now - binding.lastModelCallAt <= lifetime;\n}\n","import { modelCooldownUntil, quotaCapacity, type QuotaCapacity } from \"./quota\";\nimport { inferenceSourceFor, providerSwitchesFor } from \"./settings\";\nimport type {\n ModelDescriptor,\n ModelId,\n PlacementInput,\n PlacementPerson,\n PlacementWorkspace,\n SubscriptionConnection,\n} from \"./types\";\n\n/**\n * Reasons a connection may not serve a session at all. These are authority:\n * the database enforces the same rules, so a connection with any of them must\n * never be selected (SUB-ELIG-01, SUB-ELIG-05, SUB-SET-06, SUB-FAIL-04).\n */\nexport type AuthorizationIneligibility =\n | \"provider_disabled\"\n | \"organization_accounts_off\"\n | \"inference_source_excludes_connection\"\n | \"out_of_scope\"\n | \"personal_connections_disabled\"\n | \"personal_owner_inactive\"\n | \"personal_not_owners_work\"\n | \"personal_authority_missing\";\n\n/** Reasons an authorized connection cannot serve a model now (SUB-ELIG-02..04). */\nexport type ServiceIneligibility =\n | \"unhealthy\"\n | \"allocator_disabled\"\n | \"wrong_provider\"\n | \"model_unknown\"\n | \"model_not_allowed_by_workspace\"\n | \"model_not_entitled\"\n | \"model_not_allowed_by_connection\"\n | \"source_assignment_allocator_mismatch\"\n | \"model_cooling_down\"\n | \"exhausted\";\n\nexport type Ineligibility = AuthorizationIneligibility | ServiceIneligibility;\n\nexport const AUTHORIZATION_INELIGIBILITIES: readonly AuthorizationIneligibility[] = Object.freeze([\n \"provider_disabled\",\n \"organization_accounts_off\",\n \"inference_source_excludes_connection\",\n \"out_of_scope\",\n \"personal_connections_disabled\",\n \"personal_owner_inactive\",\n \"personal_not_owners_work\",\n \"personal_authority_missing\",\n]);\n\nconst AUTHORIZATION_SET = new Set<Ineligibility>(AUTHORIZATION_INELIGIBILITIES);\n\nexport function isAuthorizationIneligibility(\n reason: Ineligibility,\n): reason is AuthorizationIneligibility {\n return AUTHORIZATION_SET.has(reason);\n}\n\nconst PERMANENT_SERVICE = new Set<Ineligibility>([\n \"wrong_provider\",\n \"model_unknown\",\n \"model_not_allowed_by_workspace\",\n \"model_not_entitled\",\n \"model_not_allowed_by_connection\",\n]);\n\n/**\n * Reasons that neither time nor a quota reset clears: the work is not\n * authorized on the account, or the account cannot serve the model at all.\n * Health and allocator eligibility are not permanent: reconnecting or\n * re-enabling allocation restores them.\n */\nexport function isPermanentIneligibility(reason: Ineligibility): boolean {\n return AUTHORIZATION_SET.has(reason) || PERMANENT_SERVICE.has(reason);\n}\n\n/** Capacity of a connection now, with the input's staleness bound for its provider. */\nexport function connectionCapacity(\n input: PlacementInput,\n connection: SubscriptionConnection,\n): QuotaCapacity {\n const capacity = quotaCapacity(\n connection.quota,\n input.now,\n input.quotaStaleAfterMs?.[connection.provider],\n );\n if (connectionUsesExtraCredits(input, connection)) return { kind: \"available\" };\n return capacity;\n}\n\n/** Only included-window exhaustion is eligible for opted-in credit fallback. */\nexport function connectionUsesExtraCredits(\n input: PlacementInput,\n connection: SubscriptionConnection,\n): boolean {\n return (\n connection.extraCreditsEnabled === true &&\n !(connection.quota?.exhaustedUntil != null && connection.quota.exhaustedUntil > input.now) &&\n quotaCapacity(connection.quota, input.now, input.quotaStaleAfterMs?.[connection.provider])\n .kind === \"exhausted\"\n );\n}\n\nfunction findModel(input: PlacementInput, modelId: ModelId): ModelDescriptor | undefined {\n return input.models.find((model) => model.id === modelId);\n}\n\nfunction findPerson(input: PlacementInput, membershipId: string): PlacementPerson | undefined {\n return input.people.find((person) => person.membershipId === membershipId);\n}\n\nfunction sourceAssignments(input: PlacementInput, connection: SubscriptionConnection) {\n const source = inferenceSourceFor(input.settings, connection.provider);\n return connection.assignmentPolicies?.filter(\n (policy) =>\n policy.workspaceId === input.workspace.id &&\n (source === \"automatic\" || policy.inferencePool === source),\n );\n}\n\nfunction sharedConnectionMatchesSource(input: PlacementInput, connection: SubscriptionConnection) {\n const source = inferenceSourceFor(input.settings, connection.provider);\n const assignments = sourceAssignments(input, connection);\n if (assignments !== undefined) return assignments.length > 0;\n\n // M2 worlds do not yet include the M3 per-workspace assignment relation.\n // Keep their compatibility projection readable while the cutover is off.\n const managedHere =\n connection.ownership.kind === \"shared\" &&\n connection.ownership.managedByWorkspaceId === input.workspace.id;\n if (source === \"workspace\") return managedHere;\n if (source === \"organization\") return !managedHere;\n return source === \"automatic\" || managedHere;\n}\n\n/** Workspace model restrictions are a ceiling on every selection (SUB-ELIG-02). */\nexport function modelAllowedInWorkspace(workspace: PlacementWorkspace, modelId: ModelId): boolean {\n return workspace.allowedModelIds === null || workspace.allowedModelIds.includes(modelId);\n}\n\n/** Is the provider switched on for this workspace (SUB-SET-06)? */\nexport function providerEnabled(input: PlacementInput, provider: string): boolean {\n return providerSwitchesFor(input.settings, provider).enabled;\n}\n\n/**\n * Authorization of one connection for the session's work, independent of\n * model and capacity. Personal connections serve only their owner's own work,\n * in the owner's private sessions or Personal workspace, with frozen personal\n * authority for that provider (SUB-ELIG-05, design 3.7, 3.8). Shared\n * connections serve the workspaces and people in their scope, where people are\n * evaluated against the session owner.\n */\nexport function authorizationIneligibility(\n input: PlacementInput,\n connection: SubscriptionConnection,\n): AuthorizationIneligibility[] {\n const reasons: AuthorizationIneligibility[] = [];\n const { session, workspace } = input;\n const switches = providerSwitchesFor(input.settings, connection.provider);\n if (!switches.enabled) reasons.push(\"provider_disabled\");\n const ownership = connection.ownership;\n if (ownership.kind === \"personal\") {\n const owner = ownership.ownerMembershipId;\n if (!input.settings.personalConnectionsAllowed) reasons.push(\"personal_connections_disabled\");\n if (!findPerson(input, owner)?.active) reasons.push(\"personal_owner_inactive\");\n const authority = session.personalAuthority.find(\n (entry) => entry.provider === connection.provider && entry.ownerMembershipId === owner,\n );\n // A new shared-chat entry is a restriction, never a pool-wide grant and\n // never an alias for the chat owner's identity. Historical entries keep\n // their private-work eligibility unchanged.\n const ownersOwnWork = authority?.sharedSessionChoice\n ? session.ownerMembershipId !== null &&\n authority.sharedSessionChoice.sessionId === session.id &&\n authority.sharedSessionChoice.connectionId === connection.id\n : session.ownerMembershipId === owner &&\n (session.visibility === \"private\" ||\n (workspace.kind === \"personal\" && workspace.ownerMembershipId === owner));\n if (!ownersOwnWork) reasons.push(\"personal_not_owners_work\");\n if (!authority) reasons.push(\"personal_authority_missing\");\n return reasons;\n }\n if (!sharedConnectionMatchesSource(input, connection)) {\n reasons.push(\n switches.inferenceSource === undefined && !switches.useOrganizationAccounts\n ? \"organization_accounts_off\"\n : \"inference_source_excludes_connection\",\n );\n }\n const scope = ownership.scope;\n const inScope =\n scope.kind === \"organization\" ||\n (scope.kind === \"workspaces\" &&\n (scope.workspaceIds.includes(workspace.id) ||\n (workspace.kind === \"personal\" && scope.allowPersonalWorkspaces))) ||\n (scope.kind === \"people\" &&\n session.ownerMembershipId !== null &&\n scope.membershipIds.includes(session.ownerMembershipId));\n if (!inScope) reasons.push(\"out_of_scope\");\n return reasons;\n}\n\n/** Reasons the connection cannot serve this model, ignoring capacity and cooldowns. */\nfunction staticServiceIneligibility(\n input: PlacementInput,\n connection: SubscriptionConnection,\n modelId: ModelId,\n): ServiceIneligibility[] {\n const reasons: ServiceIneligibility[] = [];\n if (connection.health !== \"healthy\") reasons.push(\"unhealthy\");\n const assignments = sourceAssignments(input, connection);\n if (\n !connection.allocatorEnabled ||\n (assignments !== undefined && !assignments.some((policy) => policy.allocatorEnabled))\n ) {\n reasons.push(\"allocator_disabled\");\n }\n const model = findModel(input, modelId);\n if (!model) reasons.push(\"model_unknown\");\n else if (model.provider !== connection.provider) reasons.push(\"wrong_provider\");\n if (!modelAllowedInWorkspace(input.workspace, modelId)) {\n reasons.push(\"model_not_allowed_by_workspace\");\n }\n const entitled =\n (connection.entitledModelIds === null || connection.entitledModelIds.includes(modelId)) &&\n !connection.excludedModelIds.includes(modelId);\n if (!entitled) reasons.push(\"model_not_entitled\");\n const connectionAllowsModel =\n connection.allowedModelIds === null || connection.allowedModelIds.includes(modelId);\n const assignmentAllowsModel =\n assignments === undefined ||\n assignments.some(\n (policy) =>\n (policy.allowedModelIds === null || policy.allowedModelIds.includes(modelId)) &&\n !policy.excludedModelIds?.includes(modelId),\n );\n if (!connectionAllowsModel || !assignmentAllowsModel) {\n reasons.push(\"model_not_allowed_by_connection\");\n }\n if (assignments !== undefined) {\n const hasAllocatableAssignment = assignments.some((policy) => policy.allocatorEnabled);\n const hasServingAssignment = assignments.some(\n (policy) =>\n policy.allocatorEnabled &&\n (policy.allowedModelIds === null || policy.allowedModelIds.includes(modelId)) &&\n !policy.excludedModelIds?.includes(modelId),\n );\n if (assignmentAllowsModel && hasAllocatableAssignment && !hasServingAssignment) {\n reasons.push(\"source_assignment_allocator_mismatch\");\n }\n }\n return reasons;\n}\n\n/** Every reason this connection cannot serve this session and model now; empty when it can. */\nexport function connectionIneligibility(\n input: PlacementInput,\n connection: SubscriptionConnection,\n modelId: ModelId,\n): Ineligibility[] {\n const reasons: Ineligibility[] = [\n ...authorizationIneligibility(input, connection),\n ...staticServiceIneligibility(input, connection, modelId),\n ];\n if (modelCooldownUntil(connection.quota, modelId, input.now) !== null) {\n reasons.push(\"model_cooling_down\");\n }\n if (connectionCapacity(input, connection).kind === \"exhausted\") reasons.push(\"exhausted\");\n return reasons;\n}\n\n/** SUB-ELIG-01..06: may this connection serve this session's model now? */\nexport function canServe(\n input: PlacementInput,\n connection: SubscriptionConnection,\n modelId: ModelId,\n): boolean {\n return connectionIneligibility(input, connection, modelId).length === 0;\n}\n\n/**\n * When a connection that is blocked only by capacity or a model cooldown can\n * serve this model again. Returns `undefined` when something other than time\n * blocks it, or when it is not blocked at all, and `null` when the reset time\n * is unknown.\n */\nexport function servableAgainAt(\n input: PlacementInput,\n connection: SubscriptionConnection,\n modelId: ModelId,\n): number | null | undefined {\n if (authorizationIneligibility(input, connection).length > 0) return undefined;\n if (staticServiceIneligibility(input, connection, modelId).length > 0) return undefined;\n const capacity = connectionCapacity(input, connection);\n const cooldown = modelCooldownUntil(connection.quota, modelId, input.now);\n if (capacity.kind !== \"exhausted\" && cooldown === null) return undefined;\n // A refusal can expire before an exhausted allowance resets. At that\n // point credit consent may make the account usable, even without a known\n // allowance reset. Evaluate every observed transition using the same policy.\n const transitions = [\n cooldown,\n connection.quota?.exhaustedUntil,\n ...(connection.quota?.windows.map((window) => window.resetsAt) ?? []),\n ]\n .filter((at): at is number => at != null && at > input.now)\n .sort((a, b) => a - b);\n return transitions.find((now) => canServe({ ...input, now }, connection, modelId)) ?? null;\n}\n\n/** Own Personal workspaces use connected accounts directly; elsewhere fallback requires consent. */\nexport function personalFallbackActive(input: PlacementInput): boolean {\n if (input.session.ownerMembershipId === null) return false;\n if (input.workspace.kind === \"shared\" && input.session.visibility === \"shared\") return false;\n return (\n (input.workspace.kind === \"personal\" &&\n input.workspace.ownerMembershipId === input.session.ownerMembershipId) ||\n (input.settings.personalFallbackAllowed &&\n !!findPerson(input, input.session.ownerMembershipId)?.personalFallbackOptIn)\n );\n}\n","import type { ModelDescriptor } from \"./types\";\n\n/**\n * Map a requested reasoning level onto a target model (SUB-FAIL-03, D-16).\n *\n * A level the target supports by name is kept. Otherwise the level's relative\n * position on the source model's ladder (lowest 0, highest 1) is mapped to the\n * nearest relative position on the target's ladder, an exact tie going to the\n * lower level. A level the source ladder does not list has no position and\n * maps to the middle of the target's ladder (the lower middle of two).\n */\nexport function mapReasoningLevel(\n requested: string,\n source: Pick<ModelDescriptor, \"reasoningLevels\"> | undefined,\n target: Pick<ModelDescriptor, \"reasoningLevels\">,\n): string {\n const levels = target.reasoningLevels;\n if (levels.includes(requested)) return requested;\n if (levels.length <= 1) return levels[0] ?? \"\";\n const sourceLevels = source?.reasoningLevels ?? [];\n const position = sourceLevels.indexOf(requested);\n // The requested position as an exact fraction numerator / denominator.\n const [numerator, denominator] =\n position < 0 || sourceLevels.length <= 1 ? [1, 2] : [position, sourceLevels.length - 1];\n // round(numerator / denominator * (L - 1)) with ties down, in integers.\n const scaled = numerator * (levels.length - 1);\n const index = Math.floor((2 * scaled + denominator - 1) / (2 * denominator));\n return levels[Math.min(index, levels.length - 1)]!;\n}\n","/**\n * Spread hash (D-21, D-23): 32-bit FNV-1a over the UTF-16 code units of\n * `<session id>|<connection id>`, finished with the murmur3 32-bit finalizer.\n * Among otherwise equal connections a session takes the one with the lowest\n * value, so each session has a stable home that does not move when an\n * unrelated connection joins or leaves the pool, and placement needs no lock\n * across sessions (SUB-SEL-05). Without the finalizer, FNV-1a values of keys\n * that differ only in their last characters are not uniformly ordered, which\n * skews the spread (SUB-SEL-03, D-23).\n */\nexport function spreadHash(sessionId: string, connectionId: string): number {\n const key = `${sessionId}|${connectionId}`;\n let hash = 0x811c9dc5;\n for (let index = 0; index < key.length; index += 1) {\n hash ^= key.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n hash ^= hash >>> 16;\n hash = Math.imul(hash, 0x85ebca6b) >>> 0;\n hash ^= hash >>> 13;\n hash = Math.imul(hash, 0xc2b2ae35) >>> 0;\n hash ^= hash >>> 16;\n return hash >>> 0;\n}\n","import { isCacheWarm } from \"./cache\";\nimport {\n canServe,\n connectionUsesExtraCredits,\n connectionIneligibility,\n isPermanentIneligibility,\n modelAllowedInWorkspace,\n personalFallbackActive,\n providerEnabled,\n servableAgainAt,\n type Ineligibility,\n} from \"./eligibility\";\nimport { mapReasoningLevel } from \"./reasoning\";\nimport {\n FALLBACK_ORDER_LIMITS,\n fallbackOrderFor,\n hasFallbackOrderEntry,\n rotationFor,\n} from \"./settings\";\nimport { spreadHash } from \"./spread\";\nimport type {\n ModelDescriptor,\n ModelId,\n ModelTier,\n PlacementDecision,\n PlacementInput,\n PlacementSwitch,\n ReselectionPoint,\n SubscriptionConnection,\n WaitReason,\n} from \"./types\";\n\nfunction findModel(input: PlacementInput, modelId: ModelId): ModelDescriptor | undefined {\n return input.models.find((model) => model.id === modelId);\n}\n\n/** Every model tier, as declared once in the pure package (D-35). */\nexport const MODEL_TIERS: readonly ModelTier[] = Object.freeze([\"frontier\", \"standard\", \"fast\"]);\n\n/** Whether a value is one of the declared model tiers. */\nexport function isModelTier(value: unknown): value is ModelTier {\n return typeof value === \"string\" && (MODEL_TIERS as readonly string[]).includes(value);\n}\n\n/**\n * The default fallback order of a preferred model that has no explicit\n * entry (D-35): the models of other providers that declare the preferred\n * model's tier, never a lower or a higher one, providers in id order and\n * each provider's models in their input (declaration) order, at most\n * `FALLBACK_ORDER_LIMITS.fallbacksPerModel`. Empty while cross-provider\n * failover is off, and for a preferred model that is unknown or declares no\n * tier; a model without a tier is never derived. Same-provider model\n * fallback stays explicit only. Stepping down a tier is not allowed until\n * the owner decides it (Q-04). Provider ids are opaque keys here: which\n * providers' models the input carries is the caller's choice.\n */\nexport function derivedFallbackOrder(input: PlacementInput): ModelId[] {\n if (!input.settings.crossProviderFailover) return [];\n const preferred = findModel(input, input.session.preferredModelId);\n if (!preferred || !isModelTier(preferred.tier)) return [];\n const tier = preferred.tier;\n const derived = input.models\n .map((model, index) => ({ model, index }))\n .filter(\n ({ model }) =>\n model.provider !== preferred.provider && model.tier === tier && model.id !== preferred.id,\n )\n .sort(\n (left, right) =>\n (left.model.provider < right.model.provider\n ? -1\n : left.model.provider > right.model.provider\n ? 1\n : 0) || left.index - right.index,\n )\n .map(({ model }) => model.id);\n return [...new Set(derived)].slice(0, FALLBACK_ORDER_LIMITS.fallbacksPerModel);\n}\n\n/**\n * Candidate models in failover order (design 4 step 3): the preferred model,\n * then its fallbacks: the explicit effective entry when there is one, even\n * an empty one, else the same-tier order `derivedFallbackOrder` derives\n * (D-35). Same-provider fallbacks are always candidates; other providers\n * need cross-provider failover (SUB-FAIL-02, SUB-FAIL-03). A session limited\n * to \"only this model\" or bound by an explicit choice has only the preferred\n * model (SUB-FAIL-05). Restrictions do not remove candidates, so a restricted\n * preferred model keeps its place in the order (D-17); see `modelPermitted`.\n */\nexport function failoverCandidateModels(input: PlacementInput): ModelId[] {\n const { session, settings } = input;\n const preferredId = session.preferredModelId;\n if (session.onlyThisModel || session.binding?.choice === \"explicit\") return [preferredId];\n const preferred = findModel(input, preferredId);\n const candidates: ModelId[] = [preferredId];\n const order = hasFallbackOrderEntry(settings, preferredId)\n ? fallbackOrderFor(settings, preferredId)\n : derivedFallbackOrder(input);\n for (const modelId of order) {\n if (candidates.includes(modelId)) continue;\n const model = findModel(input, modelId);\n if (!model) continue;\n if (model.provider !== preferred?.provider && !settings.crossProviderFailover) continue;\n candidates.push(modelId);\n }\n return candidates;\n}\n\n/**\n * May this session's work use this model at all? The workspace model ceiling\n * (SUB-ELIG-02), the provider switch (SUB-SET-06) and a compaction provider\n * lock (SUB-FAIL-09) restrict models alike: a restricted preferred model is\n * treated like one without capacity and work moves to the next permitted\n * candidate (D-17, D-26).\n */\nexport function modelPermitted(\n input: PlacementInput,\n modelId: ModelId,\n honourCompactionLock = true,\n): boolean {\n const model = findModel(input, modelId);\n if (!model) return false;\n if (!modelAllowedInWorkspace(input.workspace, modelId)) return false;\n if (!providerEnabled(input, model.provider)) return false;\n const lock = input.session.compactionProviderLock;\n return !honourCompactionLock || lock === null || model.provider === lock;\n}\n\n/**\n * Order servable connections for one model (design 4 step 5): the primary\n * first, whether or not its quota is known (D-13, D-15), then the session's\n * spread hash (D-14, D-21, D-23), then id. Missing telemetry is not a penalty.\n */\nexport function rankConnections(\n input: PlacementInput,\n connections: readonly SubscriptionConnection[],\n): SubscriptionConnection[] {\n const keyed = connections.map((connection) => {\n const rotation = rotationFor(input.settings, connection.provider);\n return {\n connection,\n credits: connectionUsesExtraCredits(input, connection),\n primary: rotation.mode === \"primary_first\" && rotation.primaryConnectionId === connection.id,\n hash: spreadHash(input.session.id, connection.id),\n };\n });\n keyed.sort(\n (left, right) =>\n Number(left.credits) - Number(right.credits) ||\n Number(right.primary) - Number(left.primary) ||\n left.hash - right.hash ||\n (left.connection.id < right.connection.id\n ? -1\n : left.connection.id > right.connection.id\n ? 1\n : 0),\n );\n return keyed.map((entry) => entry.connection);\n}\n\n/**\n * Why an automatic binding does not keep its account for this placement\n * (SUB-STICK-03). Every reason except `cache_cold` is forced: the account or\n * its model cannot (or should not) serve this work now, whatever the cache.\n */\nexport type ReselectionReason =\n /** The session has no account yet. */\n | \"unbound\"\n /**\n * The bound account is not in this session's world: removed, or outside\n * the session's scope (a disconnected account stays in the world and is\n * `account_ineligible`).\n */\n | \"account_unavailable\"\n /** The bound model may no longer be used here (workspace, provider switch, compaction lock). */\n | \"model_not_permitted\"\n /**\n * The bound account cannot serve now; `ineligibility` says why (exhausted\n * or rate-limited, unhealthy or quarantined, disconnected, switched off,\n * personal authority or owner gone).\n */\n | \"account_ineligible\"\n /** The bound account would spend paid credits while another has included allowance (SUB-CREDITS-01). */\n | \"extra_credits\"\n /** A compaction or model change replaced the cached prefix (SUB-STICK-05). */\n | \"reselection_point\"\n /** Idle longer than the provider's declared cache lifetime (SUB-STICK-04). */\n | \"cache_cold\";\n\nexport type BindingStickiness =\n | { kind: \"keep\"; connectionId: string; modelId: ModelId }\n | {\n kind: \"reselect\";\n reason: ReselectionReason;\n /** The bound account's ineligibility for its model (empty unless `account_ineligible`). */\n ineligibility: readonly Ineligibility[];\n /** The re-selection points that applied (empty unless `reselection_point`). */\n points: readonly ReselectionPoint[];\n };\n\n/**\n * Does the session's automatic binding keep its account (SUB-STICK-02,\n * SUB-STICK-03)? It does while the account can still serve the bound model,\n * no re-selection point applies and the prompt cache is warm; otherwise\n * placement re-runs normal selection, which may land on the same account.\n * Forced reasons are checked before warmth, so a warm cache never keeps an\n * account the session may not use (SUB-STICK-07). An explicit choice is not\n * evaluated here: placement keeps it or waits for it (SUB-STICK-06).\n */\nexport function bindingStickiness(input: PlacementInput): BindingStickiness {\n const { session, connections } = input;\n const binding = session.binding;\n const reselect = (\n reason: ReselectionReason,\n ineligibility: readonly Ineligibility[] = [],\n points: readonly ReselectionPoint[] = [],\n ): BindingStickiness => ({ kind: \"reselect\", reason, ineligibility, points });\n if (!binding) return reselect(\"unbound\");\n const bound = connections.find((connection) => connection.id === binding.connectionId);\n if (!bound) return reselect(\"account_unavailable\");\n const permitted = failoverCandidateModels(input).filter((modelId) =>\n modelPermitted(input, modelId),\n );\n if (!permitted.includes(binding.modelId))\n // The session moved to another model (no longer a candidate at all) is\n // a re-selection point; a candidate the session may not use is forced.\n return session.reselectionPoints.length > 0 &&\n !failoverCandidateModels(input).includes(binding.modelId)\n ? reselect(\"reselection_point\", [], session.reselectionPoints)\n : reselect(\"model_not_permitted\");\n const ineligibility = connectionIneligibility(input, bound, binding.modelId);\n if (ineligibility.length > 0) return reselect(\"account_ineligible\", ineligibility);\n const fallback = personalFallbackActive(input);\n if (\n connectionUsesExtraCredits(input, bound) &&\n connections.some(\n (candidate) =>\n (candidate.ownership.kind === \"shared\" || fallback) &&\n !connectionUsesExtraCredits(input, candidate) &&\n canServe(input, candidate, binding.modelId),\n )\n ) {\n return reselect(\"extra_credits\");\n }\n if (session.reselectionPoints.length > 0)\n return reselect(\"reselection_point\", [], session.reselectionPoints);\n if (!isCacheWarm(binding, input.cacheFacts[bound.provider], input.now))\n return reselect(\"cache_cold\");\n return { kind: \"keep\", connectionId: bound.id, modelId: binding.modelId };\n}\n\n/**\n * Place one session's turn (design 4): run on an account and model, or wait\n * with an explained reason. The sender is deliberately not an input: the\n * account belongs to the session (SUB-STICK-01).\n */\nexport function decidePlacement(input: PlacementInput): PlacementDecision {\n const { session, connections } = input;\n const preferred = findModel(input, session.preferredModelId);\n const models = failoverCandidateModels(input);\n const permitted = models.filter((modelId) => modelPermitted(input, modelId));\n const fallback = personalFallbackActive(input);\n const wait = (reason: WaitReason, earliestResetAt: number | null): PlacementDecision => ({\n kind: \"wait\",\n reason,\n earliestResetAt,\n });\n const run = (\n connection: SubscriptionConnection,\n modelId: ModelId,\n kind: PlacementSwitch,\n ): PlacementDecision => ({\n kind: \"run\",\n connectionId: connection.id,\n provider: connection.provider,\n modelId,\n reasoningLevel: mapReasoningLevel(\n session.reasoningLevel,\n preferred,\n findModel(input, modelId)!,\n ),\n switch: kind,\n personal: connection.ownership.kind === \"personal\",\n });\n const servableAutomatically = (modelId: ModelId) =>\n connections.some(\n (connection) =>\n (connection.ownership.kind === \"shared\" || fallback) &&\n canServe(input, connection, modelId),\n );\n // Candidates the compaction lock alone keeps from this session (SUB-FAIL-09).\n const lockedOut = models.filter(\n (modelId) => !modelPermitted(input, modelId) && modelPermitted(input, modelId, false),\n );\n\n // No candidate model may be used here: wait on the restriction (D-17).\n if (permitted.length === 0) {\n return wait(lockedOut.length > 0 ? \"compaction_provider_locked\" : \"model_not_allowed\", null);\n }\n\n const binding = session.binding;\n const bound = binding\n ? connections.find((connection) => connection.id === binding.connectionId)\n : undefined;\n\n // An explicit choice is binding: run there or wait for it (SUB-SEL-04,\n // SUB-STICK-06). One that can never serve this work says so instead of\n // waiting silently (D-24, SUB-ACCESS-06).\n if (binding?.choice === \"explicit\") {\n if (bound && canServe(input, bound, session.preferredModelId)) {\n return run(bound, session.preferredModelId, \"pinned\");\n }\n if (\n !bound ||\n connectionIneligibility(input, bound, session.preferredModelId).some(isPermanentIneligibility)\n ) {\n return wait(\"pinned_account_ineligible\", null);\n }\n const at = servableAgainAt(input, bound, session.preferredModelId);\n return wait(\"pinned_account_unavailable\", typeof at === \"number\" ? at : null);\n }\n\n const bindingServable =\n !!binding &&\n !!bound &&\n permitted.includes(binding.modelId) &&\n canServe(input, bound, binding.modelId);\n\n // Keep a warm session where it is unless a re-selection point applies\n // or the account is forced away (SUB-STICK-02, SUB-STICK-03, SUB-STICK-05).\n const stickiness = bindingStickiness(input);\n if (stickiness.kind === \"keep\" && bound) return run(bound, stickiness.modelId, \"sticky\");\n\n // Each model on shared connections, then (opt-in) on the owner's personal\n // connections, before the next model (SUB-SEL-01, SUB-SEL-02, D-12).\n for (const [index, modelId] of models.entries()) {\n if (!modelPermitted(input, modelId)) continue;\n const eligible = connections.filter(\n (connection) =>\n canServe(input, connection, modelId) &&\n (connection.ownership.kind === \"shared\" || fallback),\n );\n const included = eligible.filter(\n (connection) => !connectionUsesExtraCredits(input, connection),\n );\n const servable = included.length ? included : eligible;\n const chosen =\n rankConnections(\n input,\n servable.filter((connection) => connection.ownership.kind === \"shared\"),\n )[0] ??\n (fallback\n ? rankConnections(\n input,\n servable.filter((connection) => connection.ownership.kind === \"personal\"),\n )[0]\n : undefined);\n if (!chosen) continue;\n let kind: PlacementSwitch;\n if (!binding) kind = \"initial\";\n else if (binding.connectionId === chosen.id && binding.modelId === modelId) kind = \"sticky\";\n else if (index === 0) {\n if (binding.modelId !== modelId) kind = \"return_to_preferred\";\n else kind = bindingServable ? \"reselected_cold\" : \"failover_same_provider\";\n } else {\n kind =\n findModel(input, modelId)?.provider === preferred?.provider\n ? \"failover_same_provider\"\n : \"failover_cross_provider\";\n }\n return run(chosen, modelId, kind);\n }\n\n // Nothing servable: wait for the earliest known time a connection this\n // session may use automatically can serve a permitted model (SUB-WAIT-01,\n // SUB-WAIT-02, D-22), and name the compaction lock when it is what keeps a\n // servable model away (SUB-FAIL-09).\n let earliest: number | null = null;\n for (const modelId of permitted) {\n for (const connection of connections) {\n if (connection.ownership.kind === \"personal\" && !fallback) continue;\n const at = servableAgainAt(input, connection, modelId);\n if (typeof at === \"number\") earliest = earliest === null ? at : Math.min(earliest, at);\n }\n }\n return wait(\n lockedOut.some(servableAutomatically) ? \"compaction_provider_locked\" : \"no_eligible_capacity\",\n earliest,\n );\n}\n","import type { CacheFacts, ModelId, ModelTier, ProviderId, SubscriptionQuota } from \"./types\";\n\n/**\n * The prompt-cache lifetime a provider adapter declares (SUB-PROV-01,\n * SUB-STICK-04): either the exact lifetime Opengeni sends with each request,\n * or an idle cut-off taken from the provider's public documentation. Every\n * adapter states a positive number; the shared core never falls back to a\n * default for a provider and never names one. Choose it conservatively:\n * a session switches account only once idle longer than this, so a value\n * that is too short pays for a cache miss the provider would have avoided.\n */\nexport type DeclaredCacheFacts =\n | { kind: \"exact_ttl\"; ttlMs: number }\n | { kind: \"measured_idle_cutoff\"; cutoffMs: number };\n\n/** The declared lifetime in milliseconds, or a thrown error for an unusable declaration. */\nexport function declaredCacheLifetimeMs(facts: DeclaredCacheFacts): number {\n const lifetime = facts.kind === \"exact_ttl\" ? facts.ttlMs : facts.cutoffMs;\n if (!Number.isSafeInteger(lifetime) || lifetime <= 0)\n throw new Error(\"A provider adapter must declare a positive whole-millisecond cache lifetime\");\n return lifetime;\n}\n\n/**\n * Provider differences are capability flags, never provider conditionals in\n * shared code (SUB-PROV-02).\n */\nexport type ProviderCapabilities = {\n /**\n * The credential renews itself through refresh. A provider whose credential\n * formats differ (OAuth renews, a setup token or an API key does not)\n * declares it per format through `capabilitiesFor`.\n */\n autoRenews: boolean;\n resetCredits: boolean;\n /** Paid usage beyond the plan can be enabled per connection (consent-gated). */\n extraCredits: boolean;\n modelEntitlements: boolean;\n realtime: boolean;\n fundsMedia: boolean;\n apps: boolean;\n remoteCompaction: boolean;\n quotaWindows: boolean;\n};\n\n/**\n * Shared error outcomes every adapter classifies into (SUB-PROV-01). A\n * refusal that limits one model only carries its `modelId`, so the core can\n * record it as that model's cooldown on the connection\n * (`modelCooldownFromOutcome`). The shared settlement step\n * (`planSubscriptionCoreRefusal`) records it; the live worker does once it\n * adopts that step.\n */\nexport type ProviderErrorOutcome =\n | { kind: \"exhausted\"; resetAt: number | null; modelId?: ModelId }\n | { kind: \"rate_limited\"; retryAfterMs: number | null; modelId?: ModelId }\n | { kind: \"unauthorized\" }\n | { kind: \"forbidden\" }\n | { kind: \"entitlement_missing\"; modelId: ModelId }\n | { kind: \"overloaded\" }\n | { kind: \"transient\" }\n | { kind: \"fatal\" };\n\n/** An encrypted credential as stored; the core never decrypts it. */\nexport type EncryptedCredential = {\n credentialEncrypted: string;\n credentialFormat: string;\n expiresAt: number | null;\n};\n\n/** Identity a sign-in reports for the connection row. */\nexport type ConnectionIdentity = {\n providerAccountId: string;\n accountEmail: string | null;\n planType: string | null;\n};\n\nexport type SignInResult = { credential: EncryptedCredential; identity: ConnectionIdentity };\n\n/** Items in conversation history that only their own provider accepts. */\nexport type ProviderHistoryItemKind =\n | \"encrypted_reasoning\"\n | \"thinking_signature\"\n | \"provider_tool\"\n | \"remote_compaction\";\n\n/**\n * What a provider can accept from history written while another provider\n * served the session (design 4, SUB-FAIL-08).\n */\nexport type HistoryCompatibility = {\n /** Provider-specific item kinds to drop from a request copy for this provider. */\n dropFromOtherProviders: readonly ProviderHistoryItemKind[];\n};\n\n/**\n * The adapter surface every model connection implements, including API-key\n * connectors (SUB-PROV-04). `Transport` is the provider's request-local\n * authorization handle; `ProviderError` is whatever the transport throws.\n */\nexport interface ModelConnectionAdapter<Transport = unknown, ProviderError = unknown> {\n readonly provider: ProviderId;\n readonly capabilities: ProviderCapabilities;\n /** Request-local authorization and wire normalization for one selected connection. */\n transport(input: { connectionId: string; credential: EncryptedCredential }): Promise<Transport>;\n /** Models the connection's plan can serve, including provider-observed exclusions. */\n entitledModels(input: {\n planType: string | null;\n excludedModelIds: readonly ModelId[];\n }): readonly ModelId[] | null;\n /** Null when the error is not a provider outcome (for example a programming error). */\n classifyError(error: ProviderError): ProviderErrorOutcome | null;\n cacheFacts(input: { modelId: ModelId }): CacheFacts;\n historyCompatibility(): HistoryCompatibility;\n /**\n * Optional out-of-turn quota probe (a provider usage endpoint), decoded by\n * the subscription adapter's `decodeQuota`.\n */\n fetchUsage?(transport: Transport): Promise<unknown>;\n /** Optional live model catalog of the connection, cached per refresh generation. */\n liveModels?(transport: Transport): Promise<readonly ModelId[]>;\n}\n\n/**\n * The full adapter of a signed-in subscription (SUB-PROV-01): sign-in,\n * refresh, quota decoding, plus the shared connection surface.\n */\nexport interface SubscriptionProviderAdapter<\n Transport = unknown,\n ProviderError = unknown,\n SignInInput = unknown,\n UsageResponse = unknown,\n> extends ModelConnectionAdapter<Transport, ProviderError> {\n /** OAuth, device code or setup token; returns an encrypted secret and identity. */\n signIn(input: SignInInput): Promise<SignInResult>;\n /**\n * Token refresh, called only under the core's single per-connection lock.\n * The core increments `refresh_generation` for every successful refresh.\n */\n refresh(input: {\n connectionId: string;\n credential: EncryptedCredential;\n }): Promise<EncryptedCredential>;\n /** Usage responses or headers decoded into the shared quota model. */\n decodeQuota(input: {\n response: UsageResponse;\n observedAt: number;\n refreshGeneration: number;\n }): Omit<SubscriptionQuota, \"revision\">;\n}\n\n/**\n * How a provider's credential is obtained and kept valid. `oauth` renews\n * through refresh; `setup_token` is pasted once and may not renew; `api_key`\n * is a static key with no refresh (API-key connectors).\n */\nexport type CredentialKind = \"oauth\" | \"setup_token\" | \"api_key\";\n\n/** How a provider reports usage limits to the shared quota model. */\nexport type QuotaKind =\n /** Rolling usage windows with reset times (subscriptions). */\n | \"usage_windows\"\n /** A spend budget in currency units (API-key connectors). */\n | \"spend_budget\"\n /** Request or token rate limits only, observed from refusals or headers. */\n | \"rate_limits\";\n\n/**\n * The provider facts the shared subscription runtime needs, beyond the\n * connection surface: everything the core does is identical for every\n * provider, and these members are the only per-provider inputs. Shared core\n * modules receive an adapter as data and never branch on its `provider`.\n *\n * `Credential` is the decoded secret (OAuth tokens, a setup token, an API\n * key). The core stores only its encrypted encoding and never inspects it.\n */\nexport interface SubscriptionCoreAdapter<Credential = unknown> {\n readonly provider: ProviderId;\n /** Product name used in operator-facing error texts (never in routing). */\n readonly displayName: string;\n /** Capabilities of the provider's renewable credential format. */\n readonly capabilities: ProviderCapabilities;\n /**\n * Capabilities of one credential format, as `credential.format` derives it\n * from a decoded secret. Pass that derived value, never the stored\n * `credential_format` column: the column is informational and may hold the\n * shared writers' envelope version (`v1`). The core never calls `refresh`\n * for a format whose `autoRenews` is false.\n */\n capabilitiesFor(credentialFormat: string): ProviderCapabilities;\n readonly credentialKind: CredentialKind;\n readonly quotaKind: QuotaKind;\n /** Provider identity used by workspace model policy for this provider's models. */\n readonly modelPolicyProviderId: string;\n /**\n * Every workspace model policy provider identity one model must be allowed\n * under, for a provider whose models carry more than one (absent, null or\n * empty: `modelPolicyProviderId` alone). A model is allowed only when the\n * policy allows each identity, so a second identity can only narrow access.\n * Read through `subscriptionCoreModelPolicyProviderIds`.\n */\n readonly modelPolicyProviderIdsFor?: (modelId: ModelId) => readonly string[] | null;\n /**\n * The prompt-cache lifetime placement keeps a session's account for\n * (SUB-STICK-04): an exact TTL Opengeni sends, or a documented idle cut-off.\n */\n readonly cacheFacts: DeclaredCacheFacts;\n /** Health policy applied to this provider's connections. */\n readonly health: {\n /** How long a connection the provider refused (forbidden) stays quarantined. */\n readonly forbiddenQuarantineMs: number;\n /** How long a model the plan is not entitled to stays cooled down. */\n readonly entitlementCooldownMs: number;\n /** How long a rate-limited connection rests when the provider gave no retry time. */\n readonly rateLimitFallbackMs: number;\n /**\n * How long an exhausted connection (usage limit or spend budget) rests\n * when the provider gave no reset. Placement retries it then, so a\n * connector without a usage reading still recovers on its own.\n */\n readonly exhaustedFallbackMs: number;\n };\n /** Plaintext codec for the encrypted credential column. */\n readonly credential: {\n /** Parse decrypted plaintext; throw fixed text (never echo the plaintext). */\n decode(plaintext: string): Credential;\n encode(credential: Credential): string;\n /** The expiry embedded in the credential (a JWT exp), used when the store has none. */\n expiry(credential: Credential): Date | null;\n /** The stored `credential_format` of a decoded credential. */\n format(credential: Credential): string;\n };\n /**\n * Credential renewal under the core's single per-connection lock. Null for\n * credentials that never renew (API keys, setup tokens without OAuth).\n */\n readonly refresh: CredentialRefresher<Credential> | null;\n /** Stored needs-relogin text for a provider message (fills an empty one). */\n reloginText(message: string): string;\n /**\n * Optional tier of one of the provider's models (D-35). With no explicit\n * fallback entry, placement derives a cross-provider order from equal\n * tiers. Absent, or null for a model: no tier, so nothing is derived for\n * or to that model and only explicit fallback entries apply.\n */\n modelTier?(modelId: ModelId): ModelTier | null;\n /**\n * Optional out-of-turn quota probe: read the provider's usage endpoint\n * with one connection's bearer. Every physical request goes through\n * `input.fetch` (the core's request custody). The response is opaque to\n * the core and decoded by `decodeQuota`.\n */\n fetchUsage?(input: SubscriptionCoreConnectionRead<Credential>): Promise<unknown>;\n /**\n * Decode a `fetchUsage` response into the shared quota model, fenced on\n * the refresh generation of the bearer that read it. Null when the\n * response carries no quota (an error payload or no windows).\n *\n * `modelIds`, when the caller passes it, lists the product models the\n * connection can serve when the response is decoded (the caller's\n * catalog), for providers whose usage reports limits of model groups\n * that become per-model cooldowns. Absent: no catalog is known, and only\n * models the response itself names can be cooled down.\n */\n decodeQuota?(input: {\n response: unknown;\n observedAt: number;\n refreshGeneration: number;\n modelIds?: readonly ModelId[];\n }): SubscriptionQuota | null;\n /**\n * The provider's usage endpoint is authoritative for its quota: a\n * `decodeQuota` reading below the limit ends a stored quota exhaustion\n * (`exhaustedKind: \"quota\"`) before its deadline, as an explicit,\n * revision-fenced store write. Absent: a running deadline is kept until\n * it passes (design 2.2).\n */\n usageReadEndsQuotaExhaustion?: boolean;\n /**\n * The provider's usage-endpoint readings are complete: each reports every\n * window that limits the connection, so a stored exhausted window that a\n * newer reading omits has ended and is dropped. Absent or false: a\n * reading may omit a window that still runs, and the shared quota merge\n * keeps an omitted exhausted window until its own reset (fail closed,\n * design 2.2). Response headers and refusals are always partial.\n */\n usageReadingsComplete?: boolean;\n /**\n * Optional live model catalog of one connection (provider model ids),\n * read with its bearer through `input.fetch`. Throws when the provider\n * refuses the credential.\n */\n liveModels?(input: SubscriptionCoreConnectionRead<Credential>): Promise<readonly ModelId[]>;\n}\n\n/** One connection's bearer as a connection read sees it. */\nexport type SubscriptionCoreConnectionBearer<Credential> = {\n /** The decoded, current credential. */\n credential: Credential;\n providerAccountId: string | null;\n /** Provider-owned connection facts; only the provider's adapter interprets them. */\n providerState: Record<string, unknown>;\n};\n\n/** What a connection read (usage, live catalog) receives from the core. */\nexport type SubscriptionCoreConnectionRead<Credential> = {\n /** The current bearer (refreshed under the core lock when stale). */\n getToken(): Promise<SubscriptionCoreConnectionBearer<Credential>>;\n /** A forced refresh under the core lock, after the provider refused the bearer. */\n refresh(): Promise<SubscriptionCoreConnectionBearer<Credential>>;\n /** A provider HTTP call under the core's request custody. */\n fetch: (input: string | URL, init?: RequestInit) => Promise<Response>;\n};\n\n/** Renewal of one credential; the core persists the result before anything else. */\nexport type CredentialRefresher<Credential> = {\n /** Refresh this long before the credential expires. */\n readonly windowMs: number;\n /** Refresh after this long when the expiry is unknown. */\n readonly fallbackMs: number;\n /** Call the provider and return the rotated credential. */\n rotate(credential: Credential): Promise<RotatedCredential<Credential>>;\n /**\n * The needs-relogin message when `error` is a permanent refusal of the\n * credential (revoked or expired refresh token), else null.\n */\n reloginMessage(error: unknown): string | null;\n};\n\nexport type RotatedCredential<Credential> = {\n credential: Credential;\n expiresAt: Date | null;\n /** The plan the rotated credential reports, or null when it reports none. */\n planType: string | null;\n};\n\n/**\n * The refresher the core may call for one decoded credential: the adapter's\n * refresher when the credential's format renews, else null (the credential\n * is used until it expires or is refused, then needs a new sign-in).\n */\nexport function subscriptionCoreCredentialRefresher<Credential>(\n adapter: Pick<SubscriptionCoreAdapter<Credential>, \"capabilitiesFor\" | \"credential\" | \"refresh\">,\n credential: Credential,\n): CredentialRefresher<Credential> | null {\n if (adapter.refresh === null) return null;\n return adapter.capabilitiesFor(adapter.credential.format(credential)).autoRenews\n ? adapter.refresh\n : null;\n}\n\n/**\n * The workspace model policy provider identities one of the adapter's models\n * must all be allowed under (never empty).\n */\nexport function subscriptionCoreModelPolicyProviderIds(\n adapter: Pick<SubscriptionCoreAdapter, \"modelPolicyProviderId\" | \"modelPolicyProviderIdsFor\">,\n modelId: ModelId,\n): readonly string[] {\n const ids = adapter.modelPolicyProviderIdsFor?.(modelId);\n return ids && ids.length > 0 ? ids : [adapter.modelPolicyProviderId];\n}\n\n/**\n * The model cooldown a refusal implies, or null when it limits the whole\n * connection (no `modelId`) or carries no time. `rate_limited` without a\n * known delay is not a cooldown: the connection's own rate-limit handling\n * applies.\n */\nexport function modelCooldownFromOutcome(\n outcome: ProviderErrorOutcome,\n now: number,\n): { modelId: ModelId; until: number } | null {\n if (outcome.kind === \"rate_limited\") {\n if (outcome.modelId === undefined || outcome.retryAfterMs === null) return null;\n return { modelId: outcome.modelId, until: now + Math.max(0, outcome.retryAfterMs) };\n }\n if (outcome.kind === \"exhausted\") {\n if (outcome.modelId === undefined || outcome.resetAt === null) return null;\n return outcome.resetAt > now ? { modelId: outcome.modelId, until: outcome.resetAt } : null;\n }\n return null;\n}\n","import {\n modelCooldownFromOutcome,\n type ProviderErrorOutcome,\n type SubscriptionCoreAdapter,\n} from \"./adapter\";\nimport type { ModelId, SubscriptionQuota } from \"./types\";\n\n/**\n * How a reserved model request settles (the request ledger's outcome):\n * `refused` when the provider definitively declined it, `response_received`\n * when a response arrived (an error response included), `unknown` when the\n * outcome cannot be known (the connection dropped mid-call), which blocks\n * every later request of the turn instead of a replay.\n */\nexport type SubscriptionCoreRequestOutcome = \"response_received\" | \"refused\" | \"unknown\";\n\n/** The connection state a definitive refusal leaves, so placement avoids it. */\nexport type SubscriptionCoreRefusalHealth =\n /** A quota observation: exhausted (usage limit, spend budget or rate limit) until `exhaustedUntil`. */\n | { kind: \"quota\"; observation: SubscriptionQuota }\n /** One model rests on the connection until `until`. */\n | { kind: \"model_cooldown\"; modelId: ModelId; until: number }\n /** `sign_in`: needs a new sign-in or key; `forbidden`: a time-bound quarantine. */\n | { kind: \"quarantine\"; reason: \"sign_in\" | \"forbidden\" };\n\n/**\n * The provider-neutral settlement of one classified upstream outcome on the\n * leased connection (design 5.3, \"API-key connectors on the shared core\").\n *\n * - `retry_after_refresh`: a refused renewable credential is refreshed once\n * under the core's lock and the same connection is retried; only a refusal\n * that survives the refresh is settled.\n * - `settle`: the (turn, connection) failure receipt is written first (it\n * counts toward the per-turn failover bound and carries the refresh\n * generation the refused credential held), then `health`; placement then\n * fails over.\n * - `no_failover`: an overloaded, failed or fatal reply is not a refusal of\n * the connection: no receipt, no health change, no rotation (it may have\n * consumed work; the turn's own error handling decides).\n */\nexport type SubscriptionCoreRefusalPlan =\n | { kind: \"retry_after_refresh\"; requestOutcome: SubscriptionCoreRequestOutcome }\n | {\n kind: \"settle\";\n requestOutcome: SubscriptionCoreRequestOutcome;\n receipt: { kind: ProviderErrorOutcome[\"kind\"]; evidence: { refreshGeneration: number } };\n health: SubscriptionCoreRefusalHealth;\n }\n | { kind: \"no_failover\"; requestOutcome: SubscriptionCoreRequestOutcome };\n\n/**\n * Plan the settlement of one classified outcome. A null outcome (the\n * connector could not classify what happened, such as a dropped connection)\n * is an unknown request outcome and never a refusal.\n *\n * - a `rate_limited` or `exhausted` refusal naming a model (`modelId`): that\n * model cools down on the connection until the provider's time, or the\n * matching fallback below; the connection keeps serving other models;\n * - `rate_limited`: exhausted (`rate_limit`) until the retry time, or the\n * adapter's `rateLimitFallbackMs` when the provider gave none;\n * - `exhausted`: exhausted (`quota`) until the provider's reset, which is\n * authoritative. Without a known future reset, an exhausted window that\n * resets after the adapter's `exhaustedFallbackMs` instead: placement\n * retries the connection then even when no usage reading exists, and a\n * later usage reading of an adapter that declares its readings complete\n * (which replaces windows but never shortens an exhaustion deadline) can\n * show recovered capacity sooner; a partial reading keeps the window;\n * - `entitlement_missing`: the model cools down for `entitlementCooldownMs`;\n * - `unauthorized` and `forbidden`: refresh and retry once when the credential\n * renews and has not been refreshed for this refusal; otherwise\n * `unauthorized` needs a new sign-in (a static key: a new key) and\n * `forbidden` is quarantined for `forbiddenQuarantineMs`.\n */\nexport function planSubscriptionCoreRefusal(\n adapter: Pick<SubscriptionCoreAdapter, \"health\">,\n outcome: ProviderErrorOutcome | null,\n input: {\n now: number;\n /** The refresh generation of the credential the refused request carried. */\n refreshGeneration: number;\n /** The credential renews (the core may refresh it). */\n renewable: boolean;\n /** This refusal already follows a refresh of the same connection. */\n refreshed: boolean;\n },\n): SubscriptionCoreRefusalPlan {\n if (outcome === null) return { kind: \"no_failover\", requestOutcome: \"unknown\" };\n const { now, refreshGeneration } = input;\n const quota = (\n fields: Pick<SubscriptionQuota, \"windows\" | \"exhaustedUntil\" | \"exhaustedKind\">,\n ): SubscriptionCoreRefusalHealth => ({\n kind: \"quota\" as const,\n observation: {\n modelCooldowns: {},\n revision: 0,\n observedAt: now,\n observedRefreshGeneration: refreshGeneration,\n source: \"refusal\" as const,\n ...fields,\n },\n });\n const settle = (health: SubscriptionCoreRefusalHealth): SubscriptionCoreRefusalPlan => ({\n kind: \"settle\",\n requestOutcome: \"refused\",\n receipt: { kind: outcome.kind, evidence: { refreshGeneration } },\n health,\n });\n // A limit on one model (a gateway's per-model rate limit or budget) cools\n // that model only, until the provider's time or the adapter's fallback;\n // the connection keeps serving its other models.\n if (\n (outcome.kind === \"rate_limited\" || outcome.kind === \"exhausted\") &&\n outcome.modelId !== undefined\n ) {\n const known = modelCooldownFromOutcome(outcome, now);\n return settle({\n kind: \"model_cooldown\",\n modelId: outcome.modelId,\n until:\n known !== null && known.until > now\n ? known.until\n : now +\n (outcome.kind === \"rate_limited\"\n ? adapter.health.rateLimitFallbackMs\n : adapter.health.exhaustedFallbackMs),\n });\n }\n switch (outcome.kind) {\n case \"rate_limited\":\n return settle(\n quota({\n windows: [],\n exhaustedUntil:\n now +\n (outcome.retryAfterMs !== null && outcome.retryAfterMs > 0\n ? outcome.retryAfterMs\n : adapter.health.rateLimitFallbackMs),\n exhaustedKind: \"rate_limit\",\n }),\n );\n case \"exhausted\":\n return settle(\n outcome.resetAt !== null && outcome.resetAt > now\n ? quota({ windows: [], exhaustedUntil: outcome.resetAt, exhaustedKind: \"quota\" })\n : quota({\n windows: [\n {\n id: \"exhausted_without_reset\",\n usedPercent: 100,\n resetsAt: now + adapter.health.exhaustedFallbackMs,\n status: \"exhausted\",\n },\n ],\n exhaustedUntil: null,\n exhaustedKind: null,\n }),\n );\n case \"entitlement_missing\":\n return settle({\n kind: \"model_cooldown\",\n modelId: outcome.modelId,\n until: now + adapter.health.entitlementCooldownMs,\n });\n case \"unauthorized\":\n case \"forbidden\":\n if (input.renewable && !input.refreshed)\n return { kind: \"retry_after_refresh\", requestOutcome: \"refused\" };\n return settle({\n kind: \"quarantine\",\n reason: outcome.kind === \"unauthorized\" ? \"sign_in\" : \"forbidden\",\n });\n case \"overloaded\":\n return { kind: \"no_failover\", requestOutcome: \"refused\" };\n case \"transient\":\n case \"fatal\":\n return { kind: \"no_failover\", requestOutcome: \"response_received\" };\n }\n}\n"],"mappings":"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","names":["findModel","ineligibility"]}
1
+ {"version":3,"sources":["../src/quota.ts","../src/cache.ts","../src/eligibility.ts","../src/reasoning.ts","../src/spread.ts","../src/placement.ts","../src/adapter.ts","../src/settlement.ts"],"sourcesContent":["import type { ModelId, SubscriptionConnection, SubscriptionQuota } from \"./types\";\n\n/**\n * What the shared quota model says about a connection's capacity now\n * (design 2.2). `unknown` is never treated as available or exhausted\n * (SUB-ELIG-06): it participates in ordinary rotation without a ranking penalty (D-14).\n */\nexport type QuotaCapacity =\n | { kind: \"available\" }\n | { kind: \"unknown\" }\n /** `resetsAt` is null when the provider did not say when it resets. */\n | { kind: \"exhausted\"; resetsAt: number | null };\n\nconst UNKNOWN: QuotaCapacity = Object.freeze({ kind: \"unknown\" });\nconst AVAILABLE: QuotaCapacity = Object.freeze({ kind: \"available\" });\n\n/**\n * Capacity of a quota observation at `now`.\n *\n * - An exhaustion deadline (`exhaustedUntil`) in the future is authoritative.\n * - A quota exhaustion whose deadline has passed is an observed reset, so it\n * counts as known capacity; a passed rate-limit deadline says nothing about\n * quota.\n * - A window reported exhausted stays exhausted until its reset; an exhausted\n * window whose reset has passed counts as reset (known capacity).\n * - With nothing exhausted, any window with a known status is known capacity.\n * - No observation, only windows of unknown status, or an observation older\n * than `staleAfterMs` (or undated, when a bound is given), is unknown.\n */\nexport function quotaCapacity(\n quota: SubscriptionQuota | null,\n now: number,\n staleAfterMs?: number,\n): QuotaCapacity {\n if (!quota) return UNKNOWN;\n // A stale or undated observation no longer says the account has capacity\n // (SUB-ELIG-06); observed exhaustion still stands until its reset.\n const stale =\n staleAfterMs !== undefined &&\n (quota.observedAt === null || now - quota.observedAt > staleAfterMs);\n let exhausted = false;\n let resetsAt: number | null = null;\n let resetUnknown = false;\n const extend = (at: number | null) => {\n exhausted = true;\n if (at === null) resetUnknown = true;\n else resetsAt = resetsAt === null ? at : Math.max(resetsAt, at);\n };\n let known = false;\n if (quota.exhaustedUntil !== null) {\n if (quota.exhaustedUntil > now) extend(quota.exhaustedUntil);\n else if (quota.exhaustedKind === \"quota\" && !stale) known = true;\n }\n for (const window of quota.windows) {\n if (window.status === \"exhausted\") {\n if (window.resetsAt === null || window.resetsAt > now) extend(window.resetsAt);\n else if (!stale) known = true;\n } else if (window.status !== \"unknown\" && !stale) {\n known = true;\n }\n }\n if (exhausted) return { kind: \"exhausted\", resetsAt: resetUnknown ? null : resetsAt };\n return known ? AVAILABLE : UNKNOWN;\n}\n\n/** End of a model-specific cooldown that is still running, else null. */\nexport function modelCooldownUntil(\n quota: SubscriptionQuota | null,\n modelId: ModelId,\n now: number,\n): number | null {\n const until = quota?.modelCooldowns[modelId];\n return until !== undefined && until > now ? until : null;\n}\n\n/**\n * A quota observation applies only to the credential generation it was made\n * with, so a refusal seen before a refresh cannot quarantine the renewed\n * credential (design 2.2).\n */\nexport function quotaObservationApplies(\n connection: Pick<SubscriptionConnection, \"refreshGeneration\">,\n observation: Pick<SubscriptionQuota, \"observedRefreshGeneration\">,\n): boolean {\n return observation.observedRefreshGeneration === connection.refreshGeneration;\n}\n\n/**\n * Apply an observation to the stored quota (design 2.2). It applies only to\n * the current credential generation, never replaces a newer or dated reading\n * with an older or undated one, and never shortens a running exhaustion\n * deadline or model cooldown: a later usage reading without them does not\n * clear them. A stored exhausted window with a known reset that an\n * observation does not report at all stays until that reset (one that\n * reports the window again replaces it), unless the observation is a\n * usage-endpoint reading of an adapter that declares its usage readings\n * complete (`options.usageReadingsComplete`, from the adapter's\n * `usageReadingsComplete`): such a reading replaces the stored windows, so\n * it can show an early reset (also of a refusal's fallback window).\n * Response headers and refusals, and the usage readings of every other\n * adapter, report only some windows (fail closed: absent means partial).\n * So a window that only refusals report (a policy hold the caller records\n * itself, for example) ends at its reset or with a complete usage reading,\n * never with a header reading. A caller's own refusal must be tagged\n * `refusal`, not `usage_endpoint`, or a complete adapter's reading would\n * erase the stored windows.\n * Clearing a cooldown early is an explicit, revision-fenced store operation,\n * not an observation. Revision numbers are assigned by the store.\n */\nexport function applyQuotaObservation(\n connection: Pick<SubscriptionConnection, \"refreshGeneration\" | \"quota\">,\n observation: SubscriptionQuota,\n options: {\n /** The observing adapter declares its usage-endpoint readings complete. */\n usageReadingsComplete?: boolean;\n } = {},\n): SubscriptionQuota | null {\n if (!quotaObservationApplies(connection, observation)) return connection.quota;\n const current = connection.quota;\n if (!current || current.observedRefreshGeneration !== connection.refreshGeneration) {\n return observation;\n }\n if (\n current.observedAt !== null &&\n (observation.observedAt === null || observation.observedAt < current.observedAt)\n ) {\n return current;\n }\n // Keep only what still runs after this observation was made: later values\n // win, and deadlines and cooldowns that ended before it are dropped.\n const observedAt = observation.observedAt ?? -Infinity;\n const modelCooldowns: Record<string, number> = {};\n for (const source of [current.modelCooldowns, observation.modelCooldowns]) {\n for (const [modelId, until] of Object.entries(source)) {\n if (until <= observedAt) continue;\n modelCooldowns[modelId] = Math.max(modelCooldowns[modelId] ?? until, until);\n }\n }\n const keepCurrentDeadline =\n current.exhaustedUntil !== null &&\n current.exhaustedUntil > observedAt &&\n (observation.exhaustedUntil === null || observation.exhaustedUntil < current.exhaustedUntil);\n const reported = new Set(observation.windows.map((window) => window.id));\n const omittedExhausted =\n observation.source === \"usage_endpoint\" && options.usageReadingsComplete === true\n ? []\n : current.windows.filter(\n (window) =>\n !reported.has(window.id) &&\n window.status === \"exhausted\" &&\n window.resetsAt !== null &&\n window.resetsAt > observedAt,\n );\n return {\n ...observation,\n windows: omittedExhausted.length\n ? [...observation.windows, ...omittedExhausted]\n : observation.windows,\n modelCooldowns,\n exhaustedUntil: keepCurrentDeadline ? current.exhaustedUntil : observation.exhaustedUntil,\n exhaustedKind: keepCurrentDeadline ? current.exhaustedKind : observation.exhaustedKind,\n };\n}\n","import type { CacheFacts, SessionBinding } from \"./types\";\n\n/**\n * Idle cut-off used for providers whose cut-off has not been measured yet\n * (design 6.1). Erring towards \"warm\" avoids paying a cache miss for a switch\n * that was not needed.\n */\nexport const DEFAULT_IDLE_CUTOFF_MS = 60 * 60 * 1000;\n\n/** How long the provider prompt cache stays warm after a model call. */\nexport function cacheLifetimeMs(facts: CacheFacts | undefined): number {\n if (!facts) return DEFAULT_IDLE_CUTOFF_MS;\n if (facts.kind === \"exact_ttl\") return Math.max(0, facts.ttlMs);\n return facts.cutoffMs === null ? DEFAULT_IDLE_CUTOFF_MS : Math.max(0, facts.cutoffMs);\n}\n\n/**\n * The cache is warm while the session has been idle no longer than the\n * provider's cache lifetime (SUB-STICK-04). Idle time is measured from the\n * latest completed model call, not the turn start, so a long turn is not\n * mistaken for a cold cache. An exact lifetime sent with the latest request\n * (an exact cache TTL) takes precedence over the provider's facts.\n */\nexport function isCacheWarm(\n binding: Pick<SessionBinding, \"lastModelCallAt\" | \"cacheTtlMs\">,\n facts: CacheFacts | undefined,\n now: number,\n): boolean {\n const lifetime =\n binding.cacheTtlMs !== undefined && binding.cacheTtlMs !== null\n ? Math.max(0, binding.cacheTtlMs)\n : cacheLifetimeMs(facts);\n return now - binding.lastModelCallAt <= lifetime;\n}\n","import { modelCooldownUntil, quotaCapacity, type QuotaCapacity } from \"./quota\";\nimport { inferenceSourceFor, providerSwitchesFor } from \"./settings\";\nimport type {\n ModelDescriptor,\n ModelId,\n PlacementInput,\n PlacementPerson,\n PlacementWorkspace,\n SubscriptionConnection,\n} from \"./types\";\n\n/**\n * Reasons a connection may not serve a session at all. These are authority:\n * the database enforces the same rules, so a connection with any of them must\n * never be selected (SUB-ELIG-01, SUB-ELIG-05, SUB-SET-06, SUB-FAIL-04).\n */\nexport type AuthorizationIneligibility =\n | \"provider_disabled\"\n | \"organization_accounts_off\"\n | \"inference_source_excludes_connection\"\n | \"out_of_scope\"\n | \"personal_connections_disabled\"\n | \"personal_owner_inactive\"\n | \"personal_not_owners_work\"\n | \"personal_authority_missing\";\n\n/** Reasons an authorized connection cannot serve a model now (SUB-ELIG-02..04). */\nexport type ServiceIneligibility =\n | \"unhealthy\"\n | \"allocator_disabled\"\n | \"wrong_provider\"\n | \"model_unknown\"\n | \"model_not_allowed_by_workspace\"\n | \"model_not_entitled\"\n | \"model_not_allowed_by_connection\"\n | \"source_assignment_allocator_mismatch\"\n | \"model_cooling_down\"\n | \"exhausted\";\n\nexport type Ineligibility = AuthorizationIneligibility | ServiceIneligibility;\n\nexport const AUTHORIZATION_INELIGIBILITIES: readonly AuthorizationIneligibility[] = Object.freeze([\n \"provider_disabled\",\n \"organization_accounts_off\",\n \"inference_source_excludes_connection\",\n \"out_of_scope\",\n \"personal_connections_disabled\",\n \"personal_owner_inactive\",\n \"personal_not_owners_work\",\n \"personal_authority_missing\",\n]);\n\nconst AUTHORIZATION_SET = new Set<Ineligibility>(AUTHORIZATION_INELIGIBILITIES);\n\nexport function isAuthorizationIneligibility(\n reason: Ineligibility,\n): reason is AuthorizationIneligibility {\n return AUTHORIZATION_SET.has(reason);\n}\n\nconst PERMANENT_SERVICE = new Set<Ineligibility>([\n \"wrong_provider\",\n \"model_unknown\",\n \"model_not_allowed_by_workspace\",\n \"model_not_entitled\",\n \"model_not_allowed_by_connection\",\n]);\n\n/**\n * Reasons that neither time nor a quota reset clears: the work is not\n * authorized on the account, or the account cannot serve the model at all.\n * Health and allocator eligibility are not permanent: reconnecting or\n * re-enabling allocation restores them.\n */\nexport function isPermanentIneligibility(reason: Ineligibility): boolean {\n return AUTHORIZATION_SET.has(reason) || PERMANENT_SERVICE.has(reason);\n}\n\n/** Capacity of a connection now, with the input's staleness bound for its provider. */\nexport function connectionCapacity(\n input: PlacementInput,\n connection: SubscriptionConnection,\n): QuotaCapacity {\n const capacity = quotaCapacity(\n connection.quota,\n input.now,\n input.quotaStaleAfterMs?.[connection.provider],\n );\n if (connectionUsesExtraCredits(input, connection)) return { kind: \"available\" };\n return capacity;\n}\n\n/** Only included-window exhaustion is eligible for opted-in credit fallback. */\nexport function connectionUsesExtraCredits(\n input: PlacementInput,\n connection: SubscriptionConnection,\n): boolean {\n return (\n connection.extraCreditsEnabled === true &&\n !(connection.quota?.exhaustedUntil != null && connection.quota.exhaustedUntil > input.now) &&\n quotaCapacity(connection.quota, input.now, input.quotaStaleAfterMs?.[connection.provider])\n .kind === \"exhausted\"\n );\n}\n\nfunction findModel(input: PlacementInput, modelId: ModelId): ModelDescriptor | undefined {\n return input.models.find((model) => model.id === modelId);\n}\n\nfunction findPerson(input: PlacementInput, membershipId: string): PlacementPerson | undefined {\n return input.people.find((person) => person.membershipId === membershipId);\n}\n\nfunction sourceAssignments(input: PlacementInput, connection: SubscriptionConnection) {\n const source = inferenceSourceFor(input.settings, connection.provider);\n return connection.assignmentPolicies?.filter(\n (policy) =>\n policy.workspaceId === input.workspace.id &&\n (source === \"automatic\" || policy.inferencePool === source),\n );\n}\n\nfunction sharedConnectionMatchesSource(input: PlacementInput, connection: SubscriptionConnection) {\n const source = inferenceSourceFor(input.settings, connection.provider);\n const assignments = sourceAssignments(input, connection);\n if (assignments !== undefined) return assignments.length > 0;\n\n // M2 worlds do not yet include the M3 per-workspace assignment relation.\n // Keep their compatibility projection readable while the cutover is off.\n const managedHere =\n connection.ownership.kind === \"shared\" &&\n connection.ownership.managedByWorkspaceId === input.workspace.id;\n if (source === \"workspace\") return managedHere;\n if (source === \"organization\") return !managedHere;\n return source === \"automatic\" || managedHere;\n}\n\n/** Workspace model restrictions are a ceiling on every selection (SUB-ELIG-02). */\nexport function modelAllowedInWorkspace(workspace: PlacementWorkspace, modelId: ModelId): boolean {\n return workspace.allowedModelIds === null || workspace.allowedModelIds.includes(modelId);\n}\n\n/** Is the provider switched on for this workspace (SUB-SET-06)? */\nexport function providerEnabled(input: PlacementInput, provider: string): boolean {\n return providerSwitchesFor(input.settings, provider).enabled;\n}\n\n/**\n * Authorization of one connection for the session's work, independent of\n * model and capacity. Personal connections serve only their owner's own work,\n * in the owner's private sessions or Personal workspace, with frozen personal\n * authority for that provider (SUB-ELIG-05, design 3.7, 3.8). Shared\n * connections serve the workspaces and people in their scope, where people are\n * evaluated against the session owner.\n */\nexport function authorizationIneligibility(\n input: PlacementInput,\n connection: SubscriptionConnection,\n): AuthorizationIneligibility[] {\n const reasons: AuthorizationIneligibility[] = [];\n const { session, workspace } = input;\n const switches = providerSwitchesFor(input.settings, connection.provider);\n if (!switches.enabled) reasons.push(\"provider_disabled\");\n const ownership = connection.ownership;\n if (ownership.kind === \"personal\") {\n const owner = ownership.ownerMembershipId;\n if (!input.settings.personalConnectionsAllowed) reasons.push(\"personal_connections_disabled\");\n if (!findPerson(input, owner)?.active) reasons.push(\"personal_owner_inactive\");\n const authority = session.personalAuthority.find(\n (entry) => entry.provider === connection.provider && entry.ownerMembershipId === owner,\n );\n // A new shared-chat entry is a restriction, never a pool-wide grant and\n // never an alias for the chat owner's identity. Historical entries keep\n // their private-work eligibility unchanged.\n const ownersOwnWork = authority?.sharedSessionChoice\n ? session.ownerMembershipId !== null &&\n authority.sharedSessionChoice.sessionId === session.id &&\n authority.sharedSessionChoice.connectionId === connection.id\n : session.ownerMembershipId === owner &&\n (session.visibility === \"private\" ||\n (workspace.kind === \"personal\" && workspace.ownerMembershipId === owner));\n if (!ownersOwnWork) reasons.push(\"personal_not_owners_work\");\n if (!authority) reasons.push(\"personal_authority_missing\");\n return reasons;\n }\n if (!sharedConnectionMatchesSource(input, connection)) {\n reasons.push(\n switches.inferenceSource === undefined && !switches.useOrganizationAccounts\n ? \"organization_accounts_off\"\n : \"inference_source_excludes_connection\",\n );\n }\n const scope = ownership.scope;\n const inScope =\n scope.kind === \"organization\" ||\n (scope.kind === \"workspaces\" &&\n (scope.workspaceIds.includes(workspace.id) ||\n (workspace.kind === \"personal\" && scope.allowPersonalWorkspaces))) ||\n (scope.kind === \"people\" &&\n session.ownerMembershipId !== null &&\n scope.membershipIds.includes(session.ownerMembershipId));\n if (!inScope) reasons.push(\"out_of_scope\");\n return reasons;\n}\n\n/** Reasons the connection cannot serve this model, ignoring capacity and cooldowns. */\nfunction staticServiceIneligibility(\n input: PlacementInput,\n connection: SubscriptionConnection,\n modelId: ModelId,\n): ServiceIneligibility[] {\n const reasons: ServiceIneligibility[] = [];\n if (connection.health !== \"healthy\") reasons.push(\"unhealthy\");\n const assignments = sourceAssignments(input, connection);\n if (\n !connection.allocatorEnabled ||\n (assignments !== undefined && !assignments.some((policy) => policy.allocatorEnabled))\n ) {\n reasons.push(\"allocator_disabled\");\n }\n const model = findModel(input, modelId);\n if (!model) reasons.push(\"model_unknown\");\n else if (model.provider !== connection.provider) reasons.push(\"wrong_provider\");\n if (!modelAllowedInWorkspace(input.workspace, modelId)) {\n reasons.push(\"model_not_allowed_by_workspace\");\n }\n const entitled =\n (connection.entitledModelIds === null || connection.entitledModelIds.includes(modelId)) &&\n !connection.excludedModelIds.includes(modelId);\n if (!entitled) reasons.push(\"model_not_entitled\");\n const connectionAllowsModel =\n connection.allowedModelIds === null || connection.allowedModelIds.includes(modelId);\n const assignmentAllowsModel =\n assignments === undefined ||\n assignments.some(\n (policy) =>\n (policy.allowedModelIds === null || policy.allowedModelIds.includes(modelId)) &&\n !policy.excludedModelIds?.includes(modelId),\n );\n if (!connectionAllowsModel || !assignmentAllowsModel) {\n reasons.push(\"model_not_allowed_by_connection\");\n }\n if (assignments !== undefined) {\n const hasAllocatableAssignment = assignments.some((policy) => policy.allocatorEnabled);\n const hasServingAssignment = assignments.some(\n (policy) =>\n policy.allocatorEnabled &&\n (policy.allowedModelIds === null || policy.allowedModelIds.includes(modelId)) &&\n !policy.excludedModelIds?.includes(modelId),\n );\n if (assignmentAllowsModel && hasAllocatableAssignment && !hasServingAssignment) {\n reasons.push(\"source_assignment_allocator_mismatch\");\n }\n }\n return reasons;\n}\n\n/** Every reason this connection cannot serve this session and model now; empty when it can. */\nexport function connectionIneligibility(\n input: PlacementInput,\n connection: SubscriptionConnection,\n modelId: ModelId,\n): Ineligibility[] {\n const reasons: Ineligibility[] = [\n ...authorizationIneligibility(input, connection),\n ...staticServiceIneligibility(input, connection, modelId),\n ];\n if (modelCooldownUntil(connection.quota, modelId, input.now) !== null) {\n reasons.push(\"model_cooling_down\");\n }\n if (connectionCapacity(input, connection).kind === \"exhausted\") reasons.push(\"exhausted\");\n return reasons;\n}\n\n/** SUB-ELIG-01..06: may this connection serve this session's model now? */\nexport function canServe(\n input: PlacementInput,\n connection: SubscriptionConnection,\n modelId: ModelId,\n): boolean {\n return connectionIneligibility(input, connection, modelId).length === 0;\n}\n\n/**\n * When a connection that is blocked only by capacity or a model cooldown can\n * serve this model again. Returns `undefined` when something other than time\n * blocks it, or when it is not blocked at all, and `null` when the reset time\n * is unknown.\n */\nexport function servableAgainAt(\n input: PlacementInput,\n connection: SubscriptionConnection,\n modelId: ModelId,\n): number | null | undefined {\n if (authorizationIneligibility(input, connection).length > 0) return undefined;\n if (staticServiceIneligibility(input, connection, modelId).length > 0) return undefined;\n const capacity = connectionCapacity(input, connection);\n const cooldown = modelCooldownUntil(connection.quota, modelId, input.now);\n if (capacity.kind !== \"exhausted\" && cooldown === null) return undefined;\n // A refusal can expire before an exhausted allowance resets. At that\n // point credit consent may make the account usable, even without a known\n // allowance reset. Evaluate every observed transition using the same policy.\n const transitions = [\n cooldown,\n connection.quota?.exhaustedUntil,\n ...(connection.quota?.windows.map((window) => window.resetsAt) ?? []),\n ]\n .filter((at): at is number => at != null && at > input.now)\n .sort((a, b) => a - b);\n return transitions.find((now) => canServe({ ...input, now }, connection, modelId)) ?? null;\n}\n\n/** Own Personal workspaces use connected accounts directly; elsewhere fallback requires consent. */\nexport function personalFallbackActive(input: PlacementInput): boolean {\n if (input.session.ownerMembershipId === null) return false;\n if (input.workspace.kind === \"shared\" && input.session.visibility === \"shared\") return false;\n return (\n (input.workspace.kind === \"personal\" &&\n input.workspace.ownerMembershipId === input.session.ownerMembershipId) ||\n (input.settings.personalFallbackAllowed &&\n !!findPerson(input, input.session.ownerMembershipId)?.personalFallbackOptIn)\n );\n}\n","import type { ModelDescriptor } from \"./types\";\n\n/**\n * Map a requested reasoning level onto a target model (SUB-FAIL-03, D-16).\n *\n * A level the target supports by name is kept. Otherwise the level's relative\n * position on the source model's ladder (lowest 0, highest 1) is mapped to the\n * nearest relative position on the target's ladder, an exact tie going to the\n * lower level. A level the source ladder does not list has no position and\n * maps to the middle of the target's ladder (the lower middle of two).\n */\nexport function mapReasoningLevel(\n requested: string,\n source: Pick<ModelDescriptor, \"reasoningLevels\"> | undefined,\n target: Pick<ModelDescriptor, \"reasoningLevels\">,\n): string {\n const levels = target.reasoningLevels;\n if (levels.includes(requested)) return requested;\n if (levels.length <= 1) return levels[0] ?? \"\";\n const sourceLevels = source?.reasoningLevels ?? [];\n const position = sourceLevels.indexOf(requested);\n // The requested position as an exact fraction numerator / denominator.\n const [numerator, denominator] =\n position < 0 || sourceLevels.length <= 1 ? [1, 2] : [position, sourceLevels.length - 1];\n // round(numerator / denominator * (L - 1)) with ties down, in integers.\n const scaled = numerator * (levels.length - 1);\n const index = Math.floor((2 * scaled + denominator - 1) / (2 * denominator));\n return levels[Math.min(index, levels.length - 1)]!;\n}\n","/**\n * Spread hash (D-21, D-23): 32-bit FNV-1a over the UTF-16 code units of\n * `<session id>|<connection id>`, finished with the murmur3 32-bit finalizer.\n * Among otherwise equal connections a session takes the one with the lowest\n * value, so each session has a stable home that does not move when an\n * unrelated connection joins or leaves the pool, and placement needs no lock\n * across sessions (SUB-SEL-05). Without the finalizer, FNV-1a values of keys\n * that differ only in their last characters are not uniformly ordered, which\n * skews the spread (SUB-SEL-03, D-23).\n */\nexport function spreadHash(sessionId: string, connectionId: string): number {\n const key = `${sessionId}|${connectionId}`;\n let hash = 0x811c9dc5;\n for (let index = 0; index < key.length; index += 1) {\n hash ^= key.charCodeAt(index);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n hash ^= hash >>> 16;\n hash = Math.imul(hash, 0x85ebca6b) >>> 0;\n hash ^= hash >>> 13;\n hash = Math.imul(hash, 0xc2b2ae35) >>> 0;\n hash ^= hash >>> 16;\n return hash >>> 0;\n}\n","import { isCacheWarm } from \"./cache\";\nimport {\n canServe,\n connectionUsesExtraCredits,\n connectionIneligibility,\n isPermanentIneligibility,\n modelAllowedInWorkspace,\n personalFallbackActive,\n providerEnabled,\n servableAgainAt,\n type Ineligibility,\n} from \"./eligibility\";\nimport { mapReasoningLevel } from \"./reasoning\";\nimport {\n FALLBACK_ORDER_LIMITS,\n fallbackOrderFor,\n hasFallbackOrderEntry,\n rotationFor,\n} from \"./settings\";\nimport { spreadHash } from \"./spread\";\nimport type {\n ModelDescriptor,\n ModelId,\n ModelTier,\n PlacementDecision,\n PlacementInput,\n PlacementSwitch,\n ReselectionPoint,\n SubscriptionConnection,\n WaitReason,\n} from \"./types\";\n\nfunction findModel(input: PlacementInput, modelId: ModelId): ModelDescriptor | undefined {\n return input.models.find((model) => model.id === modelId);\n}\n\n/** Every model tier, as declared once in the pure package (D-35). */\nexport const MODEL_TIERS: readonly ModelTier[] = Object.freeze([\"frontier\", \"standard\", \"fast\"]);\n\n/** Whether a value is one of the declared model tiers. */\nexport function isModelTier(value: unknown): value is ModelTier {\n return typeof value === \"string\" && (MODEL_TIERS as readonly string[]).includes(value);\n}\n\n/**\n * The default fallback order of a preferred model that has no explicit\n * entry (D-35): the models of other providers that declare the preferred\n * model's tier, never a lower or a higher one, providers in id order and\n * each provider's models in their input (declaration) order, at most\n * `FALLBACK_ORDER_LIMITS.fallbacksPerModel`. Empty while cross-provider\n * failover is off, and for a preferred model that is unknown or declares no\n * tier; a model without a tier is never derived. Same-provider model\n * fallback stays explicit only. Stepping down a tier is not allowed until\n * the owner decides it (Q-04). Provider ids are opaque keys here: which\n * providers' models the input carries is the caller's choice.\n */\nexport function derivedFallbackOrder(input: PlacementInput): ModelId[] {\n if (!input.settings.crossProviderFailover) return [];\n const preferred = findModel(input, input.session.preferredModelId);\n if (!preferred || !isModelTier(preferred.tier)) return [];\n const tier = preferred.tier;\n const derived = input.models\n .map((model, index) => ({ model, index }))\n .filter(\n ({ model }) =>\n model.provider !== preferred.provider && model.tier === tier && model.id !== preferred.id,\n )\n .sort(\n (left, right) =>\n (left.model.provider < right.model.provider\n ? -1\n : left.model.provider > right.model.provider\n ? 1\n : 0) || left.index - right.index,\n )\n .map(({ model }) => model.id);\n return [...new Set(derived)].slice(0, FALLBACK_ORDER_LIMITS.fallbacksPerModel);\n}\n\n/**\n * Candidate models in failover order (design 4 step 3): the preferred model,\n * then its fallbacks: the explicit effective entry when there is one, even\n * an empty one, else the same-tier order `derivedFallbackOrder` derives\n * (D-35). Same-provider fallbacks are always candidates; other providers\n * need cross-provider failover (SUB-FAIL-02, SUB-FAIL-03). A session limited\n * to \"only this model\" or bound by an explicit choice has only the preferred\n * model (SUB-FAIL-05). Restrictions do not remove candidates, so a restricted\n * preferred model keeps its place in the order (D-17); see `modelPermitted`.\n */\nexport function failoverCandidateModels(input: PlacementInput): ModelId[] {\n const { session, settings } = input;\n const preferredId = session.preferredModelId;\n if (session.onlyThisModel || session.binding?.choice === \"explicit\") return [preferredId];\n const preferred = findModel(input, preferredId);\n const candidates: ModelId[] = [preferredId];\n const order = hasFallbackOrderEntry(settings, preferredId)\n ? fallbackOrderFor(settings, preferredId)\n : derivedFallbackOrder(input);\n for (const modelId of order) {\n if (candidates.includes(modelId)) continue;\n const model = findModel(input, modelId);\n if (!model) continue;\n if (model.provider !== preferred?.provider && !settings.crossProviderFailover) continue;\n candidates.push(modelId);\n }\n return candidates;\n}\n\n/**\n * May this session's work use this model at all? The workspace model ceiling\n * (SUB-ELIG-02), the provider switch (SUB-SET-06) and a compaction provider\n * lock (SUB-FAIL-09) restrict models alike: a restricted preferred model is\n * treated like one without capacity and work moves to the next permitted\n * candidate (D-17, D-26).\n */\nexport function modelPermitted(\n input: PlacementInput,\n modelId: ModelId,\n honourCompactionLock = true,\n): boolean {\n const model = findModel(input, modelId);\n if (!model) return false;\n if (!modelAllowedInWorkspace(input.workspace, modelId)) return false;\n if (!providerEnabled(input, model.provider)) return false;\n const lock = input.session.compactionProviderLock;\n return !honourCompactionLock || lock === null || model.provider === lock;\n}\n\n/**\n * Order servable connections for one model (design 4 step 5): the primary\n * first, whether or not its quota is known (D-13, D-15), then the session's\n * spread hash (D-14, D-21, D-23), then id. Missing telemetry is not a penalty.\n */\nexport function rankConnections(\n input: PlacementInput,\n connections: readonly SubscriptionConnection[],\n): SubscriptionConnection[] {\n const keyed = connections.map((connection) => {\n const rotation = rotationFor(input.settings, connection.provider);\n return {\n connection,\n credits: connectionUsesExtraCredits(input, connection),\n primary: rotation.mode === \"primary_first\" && rotation.primaryConnectionId === connection.id,\n hash: spreadHash(input.session.id, connection.id),\n };\n });\n keyed.sort(\n (left, right) =>\n Number(left.credits) - Number(right.credits) ||\n Number(right.primary) - Number(left.primary) ||\n left.hash - right.hash ||\n (left.connection.id < right.connection.id\n ? -1\n : left.connection.id > right.connection.id\n ? 1\n : 0),\n );\n return keyed.map((entry) => entry.connection);\n}\n\n/**\n * Why an automatic binding does not keep its account for this placement\n * (SUB-STICK-03). Every reason except `cache_cold` is forced: the account or\n * its model cannot (or should not) serve this work now, whatever the cache.\n */\nexport type ReselectionReason =\n /** The session has no account yet. */\n | \"unbound\"\n /**\n * The bound account is not in this session's world: removed, or outside\n * the session's scope (a disconnected account stays in the world and is\n * `account_ineligible`).\n */\n | \"account_unavailable\"\n /** The bound model may no longer be used here (workspace, provider switch, compaction lock). */\n | \"model_not_permitted\"\n /**\n * The bound account cannot serve now; `ineligibility` says why (exhausted\n * or rate-limited, unhealthy or quarantined, disconnected, switched off,\n * personal authority or owner gone).\n */\n | \"account_ineligible\"\n /** The bound account would spend paid credits while another has included allowance (SUB-CREDITS-01). */\n | \"extra_credits\"\n /** A compaction or model change replaced the cached prefix (SUB-STICK-05). */\n | \"reselection_point\"\n /** Idle longer than the provider's declared cache lifetime (SUB-STICK-04). */\n | \"cache_cold\";\n\nexport type BindingStickiness =\n | { kind: \"keep\"; connectionId: string; modelId: ModelId }\n | {\n kind: \"reselect\";\n reason: ReselectionReason;\n /** The bound account's ineligibility for its model (empty unless `account_ineligible`). */\n ineligibility: readonly Ineligibility[];\n /** The re-selection points that applied (empty unless `reselection_point`). */\n points: readonly ReselectionPoint[];\n };\n\n/**\n * Does the session's automatic binding keep its account (SUB-STICK-02,\n * SUB-STICK-03)? It does while the account can still serve the bound model,\n * no re-selection point applies and the prompt cache is warm; otherwise\n * placement re-runs normal selection, which may land on the same account.\n * Forced reasons are checked before warmth, so a warm cache never keeps an\n * account the session may not use (SUB-STICK-07). An explicit choice is not\n * evaluated here: placement keeps it or waits for it (SUB-STICK-06).\n */\nexport function bindingStickiness(input: PlacementInput): BindingStickiness {\n const { session, connections } = input;\n const binding = session.binding;\n const reselect = (\n reason: ReselectionReason,\n ineligibility: readonly Ineligibility[] = [],\n points: readonly ReselectionPoint[] = [],\n ): BindingStickiness => ({ kind: \"reselect\", reason, ineligibility, points });\n if (!binding) return reselect(\"unbound\");\n const bound = connections.find((connection) => connection.id === binding.connectionId);\n if (!bound) return reselect(\"account_unavailable\");\n const permitted = failoverCandidateModels(input).filter((modelId) =>\n modelPermitted(input, modelId),\n );\n if (!permitted.includes(binding.modelId))\n // The session moved to another model (no longer a candidate at all) is\n // a re-selection point; a candidate the session may not use is forced.\n return session.reselectionPoints.length > 0 &&\n !failoverCandidateModels(input).includes(binding.modelId)\n ? reselect(\"reselection_point\", [], session.reselectionPoints)\n : reselect(\"model_not_permitted\");\n const ineligibility = connectionIneligibility(input, bound, binding.modelId);\n if (ineligibility.length > 0) return reselect(\"account_ineligible\", ineligibility);\n const fallback = personalFallbackActive(input);\n if (\n connectionUsesExtraCredits(input, bound) &&\n connections.some(\n (candidate) =>\n (candidate.ownership.kind === \"shared\" || fallback) &&\n !connectionUsesExtraCredits(input, candidate) &&\n canServe(input, candidate, binding.modelId),\n )\n ) {\n return reselect(\"extra_credits\");\n }\n if (session.reselectionPoints.length > 0)\n return reselect(\"reselection_point\", [], session.reselectionPoints);\n if (!isCacheWarm(binding, input.cacheFacts[bound.provider], input.now))\n return reselect(\"cache_cold\");\n return { kind: \"keep\", connectionId: bound.id, modelId: binding.modelId };\n}\n\n/**\n * Place one session's turn (design 4): run on an account and model, or wait\n * with an explained reason. The sender is deliberately not an input: the\n * account belongs to the session (SUB-STICK-01).\n */\nexport function decidePlacement(input: PlacementInput): PlacementDecision {\n const { session, connections } = input;\n const preferred = findModel(input, session.preferredModelId);\n const models = failoverCandidateModels(input);\n const permitted = models.filter((modelId) => modelPermitted(input, modelId));\n const fallback = personalFallbackActive(input);\n const wait = (reason: WaitReason, earliestResetAt: number | null): PlacementDecision => ({\n kind: \"wait\",\n reason,\n earliestResetAt,\n });\n const run = (\n connection: SubscriptionConnection,\n modelId: ModelId,\n kind: PlacementSwitch,\n ): PlacementDecision => ({\n kind: \"run\",\n connectionId: connection.id,\n provider: connection.provider,\n modelId,\n reasoningLevel: mapReasoningLevel(\n session.reasoningLevel,\n preferred,\n findModel(input, modelId)!,\n ),\n switch: kind,\n personal: connection.ownership.kind === \"personal\",\n });\n const servableAutomatically = (modelId: ModelId) =>\n connections.some(\n (connection) =>\n (connection.ownership.kind === \"shared\" || fallback) &&\n canServe(input, connection, modelId),\n );\n // Candidates the compaction lock alone keeps from this session (SUB-FAIL-09).\n const lockedOut = models.filter(\n (modelId) => !modelPermitted(input, modelId) && modelPermitted(input, modelId, false),\n );\n\n // No candidate model may be used here: wait on the restriction (D-17).\n if (permitted.length === 0) {\n return wait(lockedOut.length > 0 ? \"compaction_provider_locked\" : \"model_not_allowed\", null);\n }\n\n const binding = session.binding;\n const bound = binding\n ? connections.find((connection) => connection.id === binding.connectionId)\n : undefined;\n\n // An explicit choice is binding: run there or wait for it (SUB-SEL-04,\n // SUB-STICK-06). One that can never serve this work says so instead of\n // waiting silently (D-24, SUB-ACCESS-06).\n if (binding?.choice === \"explicit\") {\n if (bound && canServe(input, bound, session.preferredModelId)) {\n return run(bound, session.preferredModelId, \"pinned\");\n }\n if (\n !bound ||\n connectionIneligibility(input, bound, session.preferredModelId).some(isPermanentIneligibility)\n ) {\n return wait(\"pinned_account_ineligible\", null);\n }\n const at = servableAgainAt(input, bound, session.preferredModelId);\n return wait(\"pinned_account_unavailable\", typeof at === \"number\" ? at : null);\n }\n\n const bindingServable =\n !!binding &&\n !!bound &&\n permitted.includes(binding.modelId) &&\n canServe(input, bound, binding.modelId);\n\n // Keep a warm session where it is unless a re-selection point applies\n // or the account is forced away (SUB-STICK-02, SUB-STICK-03, SUB-STICK-05).\n const stickiness = bindingStickiness(input);\n if (stickiness.kind === \"keep\" && bound) return run(bound, stickiness.modelId, \"sticky\");\n\n // Each model on shared connections, then (opt-in) on the owner's personal\n // connections, before the next model (SUB-SEL-01, SUB-SEL-02, D-12).\n for (const [index, modelId] of models.entries()) {\n if (!modelPermitted(input, modelId)) continue;\n const eligible = connections.filter(\n (connection) =>\n canServe(input, connection, modelId) &&\n (connection.ownership.kind === \"shared\" || fallback),\n );\n const included = eligible.filter(\n (connection) => !connectionUsesExtraCredits(input, connection),\n );\n const servable = included.length ? included : eligible;\n const chosen =\n rankConnections(\n input,\n servable.filter((connection) => connection.ownership.kind === \"shared\"),\n )[0] ??\n (fallback\n ? rankConnections(\n input,\n servable.filter((connection) => connection.ownership.kind === \"personal\"),\n )[0]\n : undefined);\n if (!chosen) continue;\n let kind: PlacementSwitch;\n if (!binding) kind = \"initial\";\n else if (binding.connectionId === chosen.id && binding.modelId === modelId) kind = \"sticky\";\n else if (index === 0) {\n if (binding.modelId !== modelId) kind = \"return_to_preferred\";\n else kind = bindingServable ? \"reselected_cold\" : \"failover_same_provider\";\n } else {\n kind =\n findModel(input, modelId)?.provider === preferred?.provider\n ? \"failover_same_provider\"\n : \"failover_cross_provider\";\n }\n return run(chosen, modelId, kind);\n }\n\n // Nothing servable: wait for the earliest known time a connection this\n // session may use automatically can serve a permitted model (SUB-WAIT-01,\n // SUB-WAIT-02, D-22), and name the compaction lock when it is what keeps a\n // servable model away (SUB-FAIL-09).\n let earliest: number | null = null;\n for (const modelId of permitted) {\n for (const connection of connections) {\n if (connection.ownership.kind === \"personal\" && !fallback) continue;\n const at = servableAgainAt(input, connection, modelId);\n if (typeof at === \"number\") earliest = earliest === null ? at : Math.min(earliest, at);\n }\n }\n return wait(\n lockedOut.some(servableAutomatically) ? \"compaction_provider_locked\" : \"no_eligible_capacity\",\n earliest,\n );\n}\n","import type { CacheFacts, ModelId, ModelTier, ProviderId, SubscriptionQuota } from \"./types\";\n\n/**\n * The prompt-cache lifetime a provider adapter declares (SUB-PROV-01,\n * SUB-STICK-04): either the exact lifetime Opengeni sends with each request,\n * or an idle cut-off taken from the provider's public documentation. Every\n * adapter states a positive number; the shared core never falls back to a\n * default for a provider and never names one. Choose it conservatively:\n * a session switches account only once idle longer than this, so a value\n * that is too short pays for a cache miss the provider would have avoided.\n */\nexport type DeclaredCacheFacts =\n | { kind: \"exact_ttl\"; ttlMs: number }\n | { kind: \"measured_idle_cutoff\"; cutoffMs: number };\n\n/** The declared lifetime in milliseconds, or a thrown error for an unusable declaration. */\nexport function declaredCacheLifetimeMs(facts: DeclaredCacheFacts): number {\n const lifetime = facts.kind === \"exact_ttl\" ? facts.ttlMs : facts.cutoffMs;\n if (!Number.isSafeInteger(lifetime) || lifetime <= 0)\n throw new Error(\"A provider adapter must declare a positive whole-millisecond cache lifetime\");\n return lifetime;\n}\n\n/**\n * Provider differences are capability flags, never provider conditionals in\n * shared code (SUB-PROV-02).\n */\nexport type ProviderCapabilities = {\n /**\n * The credential renews itself through refresh. A provider whose credential\n * formats differ (OAuth renews, a setup token or an API key does not)\n * declares it per format through `capabilitiesFor`.\n */\n autoRenews: boolean;\n resetCredits: boolean;\n /** Paid usage beyond the plan can be enabled per connection (consent-gated). */\n extraCredits: boolean;\n modelEntitlements: boolean;\n realtime: boolean;\n fundsMedia: boolean;\n apps: boolean;\n remoteCompaction: boolean;\n quotaWindows: boolean;\n};\n\n/**\n * Shared error outcomes every adapter classifies into (SUB-PROV-01). A\n * refusal that limits one model only carries its `modelId`, so the core can\n * record it as that model's cooldown on the connection\n * (`modelCooldownFromOutcome`). The shared settlement step\n * (`planSubscriptionCoreRefusal`) records it; the live worker does once it\n * adopts that step.\n */\nexport type ProviderErrorOutcome =\n | { kind: \"exhausted\"; resetAt: number | null; modelId?: ModelId }\n | { kind: \"rate_limited\"; retryAfterMs: number | null; modelId?: ModelId }\n | { kind: \"unauthorized\" }\n | { kind: \"forbidden\" }\n | { kind: \"entitlement_missing\"; modelId: ModelId }\n | { kind: \"overloaded\" }\n | { kind: \"transient\" }\n | { kind: \"fatal\" };\n\n/** An encrypted credential as stored; the core never decrypts it. */\nexport type EncryptedCredential = {\n credentialEncrypted: string;\n credentialFormat: string;\n expiresAt: number | null;\n};\n\n/** Identity a sign-in reports for the connection row. */\nexport type ConnectionIdentity = {\n providerAccountId: string;\n accountEmail: string | null;\n planType: string | null;\n};\n\nexport type SignInResult = { credential: EncryptedCredential; identity: ConnectionIdentity };\n\n/** Items in conversation history that only their own provider accepts. */\nexport type ProviderHistoryItemKind =\n | \"encrypted_reasoning\"\n | \"thinking_signature\"\n | \"provider_tool\"\n | \"remote_compaction\";\n\n/**\n * What a provider can accept from history written while another provider\n * served the session (design 4, SUB-FAIL-08).\n */\nexport type HistoryCompatibility = {\n /** Provider-specific item kinds to drop from a request copy for this provider. */\n dropFromOtherProviders: readonly ProviderHistoryItemKind[];\n};\n\n/**\n * The adapter surface every model connection implements, including API-key\n * connectors (SUB-PROV-04). `Transport` is the provider's request-local\n * authorization handle; `ProviderError` is whatever the transport throws.\n */\nexport interface ModelConnectionAdapter<Transport = unknown, ProviderError = unknown> {\n readonly provider: ProviderId;\n readonly capabilities: ProviderCapabilities;\n /** Request-local authorization and wire normalization for one selected connection. */\n transport(input: { connectionId: string; credential: EncryptedCredential }): Promise<Transport>;\n /** Models the connection's plan can serve, including provider-observed exclusions. */\n entitledModels(input: {\n planType: string | null;\n excludedModelIds: readonly ModelId[];\n }): readonly ModelId[] | null;\n /** Null when the error is not a provider outcome (for example a programming error). */\n classifyError(error: ProviderError): ProviderErrorOutcome | null;\n cacheFacts(input: { modelId: ModelId }): CacheFacts;\n historyCompatibility(): HistoryCompatibility;\n /**\n * Optional out-of-turn quota probe (a provider usage endpoint), decoded by\n * the subscription adapter's `decodeQuota`.\n */\n fetchUsage?(transport: Transport): Promise<unknown>;\n /** Optional live model catalog of the connection, cached per refresh generation. */\n liveModels?(transport: Transport): Promise<readonly ModelId[]>;\n}\n\n/**\n * The full adapter of a signed-in subscription (SUB-PROV-01): sign-in,\n * refresh, quota decoding, plus the shared connection surface.\n */\nexport interface SubscriptionProviderAdapter<\n Transport = unknown,\n ProviderError = unknown,\n SignInInput = unknown,\n UsageResponse = unknown,\n> extends ModelConnectionAdapter<Transport, ProviderError> {\n /** OAuth, device code or setup token; returns an encrypted secret and identity. */\n signIn(input: SignInInput): Promise<SignInResult>;\n /**\n * Token refresh, called only under the core's single per-connection lock.\n * The core increments `refresh_generation` for every successful refresh.\n */\n refresh(input: {\n connectionId: string;\n credential: EncryptedCredential;\n }): Promise<EncryptedCredential>;\n /** Usage responses or headers decoded into the shared quota model. */\n decodeQuota(input: {\n response: UsageResponse;\n observedAt: number;\n refreshGeneration: number;\n }): Omit<SubscriptionQuota, \"revision\">;\n}\n\n/**\n * How a provider's credential is obtained and kept valid. `oauth` renews\n * through refresh; `setup_token` is pasted once and may not renew; `api_key`\n * is a static key with no refresh (API-key connectors).\n */\nexport type CredentialKind = \"oauth\" | \"setup_token\" | \"api_key\";\n\n/** How a provider reports usage limits to the shared quota model. */\nexport type QuotaKind =\n /** Rolling usage windows with reset times (subscriptions). */\n | \"usage_windows\"\n /** A spend budget in currency units (API-key connectors). */\n | \"spend_budget\"\n /** Request or token rate limits only, observed from refusals or headers. */\n | \"rate_limits\";\n\n/**\n * The provider facts the shared subscription runtime needs, beyond the\n * connection surface: everything the core does is identical for every\n * provider, and these members are the only per-provider inputs. Shared core\n * modules receive an adapter as data and never branch on its `provider`.\n *\n * `Credential` is the decoded secret (OAuth tokens, a setup token, an API\n * key). The core stores only its encrypted encoding and never inspects it.\n */\nexport interface SubscriptionCoreAdapter<Credential = unknown> {\n readonly provider: ProviderId;\n /** Product name used in operator-facing error texts (never in routing). */\n readonly displayName: string;\n /** Capabilities of the provider's renewable credential format. */\n readonly capabilities: ProviderCapabilities;\n /**\n * Capabilities of one credential format, as `credential.format` derives it\n * from a decoded secret. Pass that derived value, never the stored\n * `credential_format` column: the column is informational and may hold the\n * shared writers' envelope version (`v1`). The core never calls `refresh`\n * for a format whose `autoRenews` is false.\n */\n capabilitiesFor(credentialFormat: string): ProviderCapabilities;\n readonly credentialKind: CredentialKind;\n readonly quotaKind: QuotaKind;\n /** Provider identity used by workspace model policy for this provider's models. */\n readonly modelPolicyProviderId: string;\n /**\n * Every workspace model policy provider identity one model must be allowed\n * under, for a provider whose models carry more than one (absent, null or\n * empty: `modelPolicyProviderId` alone). A model is allowed only when the\n * policy allows each identity, so a second identity can only narrow access.\n * Read through `subscriptionCoreModelPolicyProviderIds`.\n */\n readonly modelPolicyProviderIdsFor?: (modelId: ModelId) => readonly string[] | null;\n /**\n * The prompt-cache lifetime placement keeps a session's account for\n * (SUB-STICK-04): an exact TTL Opengeni sends, or a documented idle cut-off.\n */\n readonly cacheFacts: DeclaredCacheFacts;\n /** Health policy applied to this provider's connections. */\n readonly health: {\n /** How long a connection the provider refused (forbidden) stays quarantined. */\n readonly forbiddenQuarantineMs: number;\n /** How long a model the plan is not entitled to stays cooled down. */\n readonly entitlementCooldownMs: number;\n /** How long a rate-limited connection rests when the provider gave no retry time. */\n readonly rateLimitFallbackMs: number;\n /**\n * How long an exhausted connection (usage limit or spend budget) rests\n * when the provider gave no reset. Placement retries it then, so a\n * connector without a usage reading still recovers on its own.\n */\n readonly exhaustedFallbackMs: number;\n };\n /** Plaintext codec for the encrypted credential column. */\n readonly credential: {\n /** Parse decrypted plaintext; throw fixed text (never echo the plaintext). */\n decode(plaintext: string): Credential;\n encode(credential: Credential): string;\n /** The expiry embedded in the credential (a JWT exp), used when the store has none. */\n expiry(credential: Credential): Date | null;\n /** The stored `credential_format` of a decoded credential. */\n format(credential: Credential): string;\n };\n /**\n * Credential renewal under the core's single per-connection lock. Null for\n * credentials that never renew (API keys, setup tokens without OAuth).\n */\n readonly refresh: CredentialRefresher<Credential> | null;\n /** Stored needs-relogin text for a provider message (fills an empty one). */\n reloginText(message: string): string;\n /**\n * Optional tier of one of the provider's models (D-35). With no explicit\n * fallback entry, placement derives a cross-provider order from equal\n * tiers. Absent, or null for a model: no tier, so nothing is derived for\n * or to that model and only explicit fallback entries apply.\n */\n modelTier?(modelId: ModelId): ModelTier | null;\n /**\n * Optional out-of-turn quota probe: read the provider's usage endpoint\n * with one connection's bearer. Every physical request goes through\n * `input.fetch` (the core's request custody). The response is opaque to\n * the core and decoded by `decodeQuota`.\n */\n fetchUsage?(input: SubscriptionCoreConnectionRead<Credential>): Promise<unknown>;\n /**\n * Decode a `fetchUsage` response into the shared quota model, fenced on\n * the refresh generation of the bearer that read it. Null when the\n * response carries no quota (an error payload or no windows).\n *\n * `modelIds`, when the caller passes it, lists the product models the\n * connection can serve when the response is decoded (the caller's\n * catalog), for providers whose usage reports limits of model groups\n * that become per-model cooldowns. Absent: no catalog is known, and only\n * models the response itself names can be cooled down.\n */\n decodeQuota?(input: {\n response: unknown;\n observedAt: number;\n refreshGeneration: number;\n modelIds?: readonly ModelId[];\n }): SubscriptionQuota | null;\n /**\n * The provider's usage endpoint is authoritative for its quota: a\n * `decodeQuota` reading below the limit ends a stored quota exhaustion\n * (`exhaustedKind: \"quota\"`) before its deadline, as an explicit,\n * revision-fenced store write. Absent: a running deadline is kept until\n * it passes (design 2.2).\n */\n usageReadEndsQuotaExhaustion?: boolean;\n /**\n * With `usageReadEndsQuotaExhaustion`: whether one `fetchUsage` response\n * confirms the connection may serve again. A response can report every\n * window below the limit and still say the account is refused (or that a\n * feature-specific limit is spent); such a reading never ends a stored\n * quota exhaustion. Absent: every reading below the limit does.\n */\n usageReadConfirmsAvailability?(response: unknown): boolean;\n /**\n * The provider's usage-endpoint readings are complete: each reports every\n * window that limits the connection, so a stored exhausted window that a\n * newer reading omits has ended and is dropped. Absent or false: a\n * reading may omit a window that still runs, and the shared quota merge\n * keeps an omitted exhausted window until its own reset (fail closed,\n * design 2.2). Response headers and refusals are always partial.\n */\n usageReadingsComplete?: boolean;\n /**\n * Optional live model catalog of one connection (provider model ids),\n * read with its bearer through `input.fetch`. Throws when the provider\n * refuses the credential.\n */\n liveModels?(input: SubscriptionCoreConnectionRead<Credential>): Promise<readonly ModelId[]>;\n}\n\n/** One connection's bearer as a connection read sees it. */\nexport type SubscriptionCoreConnectionBearer<Credential> = {\n /** The decoded, current credential. */\n credential: Credential;\n providerAccountId: string | null;\n /** Provider-owned connection facts; only the provider's adapter interprets them. */\n providerState: Record<string, unknown>;\n};\n\n/** What a connection read (usage, live catalog) receives from the core. */\nexport type SubscriptionCoreConnectionRead<Credential> = {\n /** The current bearer (refreshed under the core lock when stale). */\n getToken(): Promise<SubscriptionCoreConnectionBearer<Credential>>;\n /** A forced refresh under the core lock, after the provider refused the bearer. */\n refresh(): Promise<SubscriptionCoreConnectionBearer<Credential>>;\n /** A provider HTTP call under the core's request custody. */\n fetch: (input: string | URL, init?: RequestInit) => Promise<Response>;\n};\n\n/** Renewal of one credential; the core persists the result before anything else. */\nexport type CredentialRefresher<Credential> = {\n /** Refresh this long before the credential expires. */\n readonly windowMs: number;\n /** Refresh after this long when the expiry is unknown. */\n readonly fallbackMs: number;\n /** Call the provider and return the rotated credential. */\n rotate(credential: Credential): Promise<RotatedCredential<Credential>>;\n /**\n * The needs-relogin message when `error` is a permanent refusal of the\n * credential (revoked or expired refresh token), else null.\n */\n reloginMessage(error: unknown): string | null;\n};\n\nexport type RotatedCredential<Credential> = {\n credential: Credential;\n expiresAt: Date | null;\n /** The plan the rotated credential reports, or null when it reports none. */\n planType: string | null;\n};\n\n/**\n * The refresher the core may call for one decoded credential: the adapter's\n * refresher when the credential's format renews, else null (the credential\n * is used until it expires or is refused, then needs a new sign-in).\n */\nexport function subscriptionCoreCredentialRefresher<Credential>(\n adapter: Pick<SubscriptionCoreAdapter<Credential>, \"capabilitiesFor\" | \"credential\" | \"refresh\">,\n credential: Credential,\n): CredentialRefresher<Credential> | null {\n if (adapter.refresh === null) return null;\n return adapter.capabilitiesFor(adapter.credential.format(credential)).autoRenews\n ? adapter.refresh\n : null;\n}\n\n/**\n * The workspace model policy provider identities one of the adapter's models\n * must all be allowed under (never empty).\n */\nexport function subscriptionCoreModelPolicyProviderIds(\n adapter: Pick<SubscriptionCoreAdapter, \"modelPolicyProviderId\" | \"modelPolicyProviderIdsFor\">,\n modelId: ModelId,\n): readonly string[] {\n const ids = adapter.modelPolicyProviderIdsFor?.(modelId);\n return ids && ids.length > 0 ? ids : [adapter.modelPolicyProviderId];\n}\n\n/**\n * The model cooldown a refusal implies, or null when it limits the whole\n * connection (no `modelId`) or carries no time. `rate_limited` without a\n * known delay is not a cooldown: the connection's own rate-limit handling\n * applies.\n */\nexport function modelCooldownFromOutcome(\n outcome: ProviderErrorOutcome,\n now: number,\n): { modelId: ModelId; until: number } | null {\n if (outcome.kind === \"rate_limited\") {\n if (outcome.modelId === undefined || outcome.retryAfterMs === null) return null;\n return { modelId: outcome.modelId, until: now + Math.max(0, outcome.retryAfterMs) };\n }\n if (outcome.kind === \"exhausted\") {\n if (outcome.modelId === undefined || outcome.resetAt === null) return null;\n return outcome.resetAt > now ? { modelId: outcome.modelId, until: outcome.resetAt } : null;\n }\n return null;\n}\n","import {\n modelCooldownFromOutcome,\n type ProviderErrorOutcome,\n type SubscriptionCoreAdapter,\n} from \"./adapter\";\nimport type { ModelId, SubscriptionQuota } from \"./types\";\n\n/**\n * How a reserved model request settles (the request ledger's outcome):\n * `refused` when the provider definitively declined it, `response_received`\n * when a response arrived (an error response included), `unknown` when the\n * outcome cannot be known (the connection dropped mid-call), which blocks\n * every later request of the turn instead of a replay.\n */\nexport type SubscriptionCoreRequestOutcome = \"response_received\" | \"refused\" | \"unknown\";\n\n/** The connection state a definitive refusal leaves, so placement avoids it. */\nexport type SubscriptionCoreRefusalHealth =\n /** A quota observation: exhausted (usage limit, spend budget or rate limit) until `exhaustedUntil`. */\n | { kind: \"quota\"; observation: SubscriptionQuota }\n /** One model rests on the connection until `until`. */\n | { kind: \"model_cooldown\"; modelId: ModelId; until: number }\n /** `sign_in`: needs a new sign-in or key; `forbidden`: a time-bound quarantine. */\n | { kind: \"quarantine\"; reason: \"sign_in\" | \"forbidden\" };\n\n/**\n * The provider-neutral settlement of one classified upstream outcome on the\n * leased connection (design 5.3, \"API-key connectors on the shared core\").\n *\n * - `retry_after_refresh`: a refused renewable credential is refreshed once\n * under the core's lock and the same connection is retried; only a refusal\n * that survives the refresh is settled.\n * - `settle`: the (turn, connection) failure receipt is written first (it\n * counts toward the per-turn failover bound and carries the refresh\n * generation the refused credential held), then `health`; placement then\n * fails over.\n * - `no_failover`: an overloaded, failed or fatal reply is not a refusal of\n * the connection: no receipt, no health change, no rotation (it may have\n * consumed work; the turn's own error handling decides).\n */\nexport type SubscriptionCoreRefusalPlan =\n | { kind: \"retry_after_refresh\"; requestOutcome: SubscriptionCoreRequestOutcome }\n | {\n kind: \"settle\";\n requestOutcome: SubscriptionCoreRequestOutcome;\n receipt: { kind: ProviderErrorOutcome[\"kind\"]; evidence: { refreshGeneration: number } };\n health: SubscriptionCoreRefusalHealth;\n }\n | { kind: \"no_failover\"; requestOutcome: SubscriptionCoreRequestOutcome };\n\n/**\n * Plan the settlement of one classified outcome. A null outcome (the\n * connector could not classify what happened, such as a dropped connection)\n * is an unknown request outcome and never a refusal.\n *\n * - a `rate_limited` or `exhausted` refusal naming a model (`modelId`): that\n * model cools down on the connection until the provider's time, or the\n * matching fallback below; the connection keeps serving other models;\n * - `rate_limited`: exhausted (`rate_limit`) until the retry time, or the\n * adapter's `rateLimitFallbackMs` when the provider gave none;\n * - `exhausted`: exhausted (`quota`) until the provider's reset, which is\n * authoritative. Without a known future reset, an exhausted window that\n * resets after the adapter's `exhaustedFallbackMs` instead: placement\n * retries the connection then even when no usage reading exists, and a\n * later usage reading of an adapter that declares its readings complete\n * (which replaces windows but never shortens an exhaustion deadline) can\n * show recovered capacity sooner; a partial reading keeps the window;\n * - `entitlement_missing`: the model cools down for `entitlementCooldownMs`;\n * - `unauthorized` and `forbidden`: refresh and retry once when the credential\n * renews and has not been refreshed for this refusal; otherwise\n * `unauthorized` needs a new sign-in (a static key: a new key) and\n * `forbidden` is quarantined for `forbiddenQuarantineMs`.\n */\nexport function planSubscriptionCoreRefusal(\n adapter: Pick<SubscriptionCoreAdapter, \"health\">,\n outcome: ProviderErrorOutcome | null,\n input: {\n now: number;\n /** The refresh generation of the credential the refused request carried. */\n refreshGeneration: number;\n /** The credential renews (the core may refresh it). */\n renewable: boolean;\n /** This refusal already follows a refresh of the same connection. */\n refreshed: boolean;\n },\n): SubscriptionCoreRefusalPlan {\n if (outcome === null) return { kind: \"no_failover\", requestOutcome: \"unknown\" };\n const { now, refreshGeneration } = input;\n const quota = (\n fields: Pick<SubscriptionQuota, \"windows\" | \"exhaustedUntil\" | \"exhaustedKind\">,\n ): SubscriptionCoreRefusalHealth => ({\n kind: \"quota\" as const,\n observation: {\n modelCooldowns: {},\n revision: 0,\n observedAt: now,\n observedRefreshGeneration: refreshGeneration,\n source: \"refusal\" as const,\n ...fields,\n },\n });\n const settle = (health: SubscriptionCoreRefusalHealth): SubscriptionCoreRefusalPlan => ({\n kind: \"settle\",\n requestOutcome: \"refused\",\n receipt: { kind: outcome.kind, evidence: { refreshGeneration } },\n health,\n });\n // A limit on one model (a gateway's per-model rate limit or budget) cools\n // that model only, until the provider's time or the adapter's fallback;\n // the connection keeps serving its other models.\n if (\n (outcome.kind === \"rate_limited\" || outcome.kind === \"exhausted\") &&\n outcome.modelId !== undefined\n ) {\n const known = modelCooldownFromOutcome(outcome, now);\n return settle({\n kind: \"model_cooldown\",\n modelId: outcome.modelId,\n until:\n known !== null && known.until > now\n ? known.until\n : now +\n (outcome.kind === \"rate_limited\"\n ? adapter.health.rateLimitFallbackMs\n : adapter.health.exhaustedFallbackMs),\n });\n }\n switch (outcome.kind) {\n case \"rate_limited\":\n return settle(\n quota({\n windows: [],\n exhaustedUntil:\n now +\n (outcome.retryAfterMs !== null && outcome.retryAfterMs > 0\n ? outcome.retryAfterMs\n : adapter.health.rateLimitFallbackMs),\n exhaustedKind: \"rate_limit\",\n }),\n );\n case \"exhausted\":\n return settle(\n outcome.resetAt !== null && outcome.resetAt > now\n ? quota({ windows: [], exhaustedUntil: outcome.resetAt, exhaustedKind: \"quota\" })\n : quota({\n windows: [\n {\n id: \"exhausted_without_reset\",\n usedPercent: 100,\n resetsAt: now + adapter.health.exhaustedFallbackMs,\n status: \"exhausted\",\n },\n ],\n exhaustedUntil: null,\n exhaustedKind: null,\n }),\n );\n case \"entitlement_missing\":\n return settle({\n kind: \"model_cooldown\",\n modelId: outcome.modelId,\n until: now + adapter.health.entitlementCooldownMs,\n });\n case \"unauthorized\":\n case \"forbidden\":\n if (input.renewable && !input.refreshed)\n return { kind: \"retry_after_refresh\", requestOutcome: \"refused\" };\n return settle({\n kind: \"quarantine\",\n reason: outcome.kind === \"unauthorized\" ? \"sign_in\" : \"forbidden\",\n });\n case \"overloaded\":\n return { kind: \"no_failover\", requestOutcome: \"refused\" };\n case \"transient\":\n case \"fatal\":\n return { kind: \"no_failover\", requestOutcome: \"response_received\" };\n }\n}\n"],"mappings":"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","names":["findModel","ineligibility"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/subscriptions",
3
- "version": "1.4.5-canary.38533006839001",
3
+ "version": "2.0.1",
4
4
  "description": "Pure, provider-neutral subscription account policy for Opengeni: contract types, effective settings, eligibility, placement, failover order, cache coldness, the shared quota model and adapter interfaces. No database, provider SDK or network dependency.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/adapter.ts CHANGED
@@ -276,6 +276,14 @@ export interface SubscriptionCoreAdapter<Credential = unknown> {
276
276
  * it passes (design 2.2).
277
277
  */
278
278
  usageReadEndsQuotaExhaustion?: boolean;
279
+ /**
280
+ * With `usageReadEndsQuotaExhaustion`: whether one `fetchUsage` response
281
+ * confirms the connection may serve again. A response can report every
282
+ * window below the limit and still say the account is refused (or that a
283
+ * feature-specific limit is spent); such a reading never ends a stored
284
+ * quota exhaustion. Absent: every reading below the limit does.
285
+ */
286
+ usageReadConfirmsAvailability?(response: unknown): boolean;
279
287
  /**
280
288
  * The provider's usage-endpoint readings are complete: each reports every
281
289
  * window that limits the connection, so a stored exhausted window that a