import * as Schema from "effect/Schema"; import { ForwardCompatibleArray, IsoDateTime, NonNegativeInt, TrimmedNonEmptyString, } from "./baseSchemas.ts"; import { ProviderDriverKind, ProviderInstanceId } from "./providerInstance.ts"; import { UsageLimitSourceId } from "./usageLimitSourceId.ts"; /** * One rolling quota window a subscription provider reports for the signed-in * account, e.g. Claude's five-hour session or Codex's weekly allowance. * * `id` is stable per provider (`five_hour`, `seven_day_opus`, `primary`) so a * sparse turn-driven update lands on the same row a full probe produced. * `kind` only orders and labels the bar. */ export const ServerProviderUsageWindow = Schema.Struct({ id: TrimmedNonEmptyString, kind: Schema.Literals(["session", "weekly", "monthly", "other"]), label: TrimmedNonEmptyString, usedPercent: Schema.Number.check(Schema.isBetween({ minimum: 0, maximum: 100 })), resetsAt: Schema.optional(IsoDateTime), windowDurationMins: Schema.optional(NonNegativeInt), }); export type ServerProviderUsageWindow = typeof ServerProviderUsageWindow.Type; /** * Reset credits a provider banks on the account. Codex grants these when it * has rate-limited the user unfairly; redeeming one clears the current * windows. Only present when the provider reports them at all. */ export const ServerProviderResetCredits = Schema.Struct({ availableCount: NonNegativeInt, nextExpiresAt: Schema.optional(IsoDateTime), /** Pins hub redemption to the displayed credit, including retries from another client. */ nextCreditId: Schema.optional(TrimmedNonEmptyString), }); export type ServerProviderResetCredits = typeof ServerProviderResetCredits.Type; /** * Subscription usage the provider knows about the signed-in account. * * `unavailable` distinguishes an account that can never report windows (API * key, Bedrock) from a probe that failed this time, so clients can keep the * last good bars for the latter and clear them for the former. */ export const ServerProviderUsageLimits = Schema.Struct({ checkedAt: IsoDateTime, windows: ForwardCompatibleArray(ServerProviderUsageWindow), resetCredits: Schema.optional(ServerProviderResetCredits), unavailable: Schema.optional( Schema.Struct({ reason: Schema.Literals(["unsupported", "probeFailed"]), message: Schema.optional(TrimmedNonEmptyString), }), ), }); export type ServerProviderUsageLimits = typeof ServerProviderUsageLimits.Type; /** * What an adapter reports when its runtime pushes a rate-limit update during * a turn. Sparse by contract: Claude's `rate_limit_event` names one window at * a time and Codex documents its notification as a partial. Windows merge by * `id` onto the instance's published snapshot; omitted windows are unchanged. */ export const ProviderUsageLimitsUpdate = Schema.Struct({ windows: Schema.Array(ServerProviderUsageWindow), }); export type ProviderUsageLimitsUpdate = typeof ProviderUsageLimitsUpdate.Type; /** * One account a usage-limit source reports on. `driver` is the provider the * account belongs to, for the icon and colour clients already have; the * account itself is not something this environment can run turns on. */ export const UsageLimitSourceAccount = Schema.Struct({ id: TrimmedNonEmptyString, driver: ProviderDriverKind, /** The signed-in address, when the source names one; clients blur it like provider auth. */ email: Schema.optional(TrimmedNonEmptyString), /** Plan as the matching provider would label it (`ChatGPT Pro 20x Subscription`). */ plan: Schema.optional(TrimmedNonEmptyString), usageLimits: ServerProviderUsageLimits, }); export type UsageLimitSourceAccount = typeof UsageLimitSourceAccount.Type; /** * The published state of one configured `usageLimitSources` entry. A source * that could not be read keeps `error` beside an empty account list rather * than vanishing, so the user can see it is configured but failing. */ export const UsageLimitSourceSnapshot = Schema.Struct({ id: UsageLimitSourceId, kind: Schema.Literal("cliproxy"), label: TrimmedNonEmptyString, checkedAt: IsoDateTime, accounts: ForwardCompatibleArray(UsageLimitSourceAccount), error: Schema.optional(TrimmedNonEmptyString), }); export type UsageLimitSourceSnapshot = typeof UsageLimitSourceSnapshot.Type; export const UsageLimitSourceSnapshots = ForwardCompatibleArray(UsageLimitSourceSnapshot); export type UsageLimitSourceSnapshots = typeof UsageLimitSourceSnapshots.Type; export const UsageLimitSourceConsumeResetCreditInput = Schema.Struct({ sourceId: UsageLimitSourceId, accountId: TrimmedNonEmptyString, creditId: TrimmedNonEmptyString, }); export type UsageLimitSourceConsumeResetCreditInput = typeof UsageLimitSourceConsumeResetCreditInput.Type; export const ProviderConsumeResetCreditInput = Schema.Union([ Schema.Struct({ instanceId: ProviderInstanceId }), UsageLimitSourceConsumeResetCreditInput, ]); export type ProviderConsumeResetCreditInput = typeof ProviderConsumeResetCreditInput.Type; export class UsageLimitSourceError extends Schema.TaggedError()( "UsageLimitSourceError", { detail: Schema.String }, ) { override get message(): string { return this.detail; } } /** Mirrors Codex's own outcome set; other providers map onto it. */ export const ProviderConsumeResetCreditOutcome = Schema.Literals([ "reset", "nothingToReset", "noCredit", "alreadyRedeemed", ]); export type ProviderConsumeResetCreditOutcome = typeof ProviderConsumeResetCreditOutcome.Type; export const ProviderConsumeResetCreditResult = Schema.Struct({ outcome: ProviderConsumeResetCreditOutcome, /** Redemption succeeded, but a follow-up such as clearing the hub cooldown failed. */ warning: Schema.optional(TrimmedNonEmptyString), }); export type ProviderConsumeResetCreditResult = typeof ProviderConsumeResetCreditResult.Type; /** A point-in-time view of one provider's limits, built for the /usage-limits panel. */ export const UsageLimitsReport = Schema.Struct({ createdAt: IsoDateTime, accounts: Schema.Array( Schema.Struct({ id: TrimmedNonEmptyString, driver: ProviderDriverKind, label: TrimmedNonEmptyString, plan: Schema.optional(TrimmedNonEmptyString), email: Schema.optional(TrimmedNonEmptyString), sourceLabel: Schema.optional(TrimmedNonEmptyString), instanceId: Schema.optional(ProviderInstanceId), resetCreditInput: Schema.optional(ProviderConsumeResetCreditInput), displayName: Schema.optional(Schema.String), accentColor: Schema.optional(Schema.String), limits: ServerProviderUsageLimits, }), ), notices: Schema.Array(Schema.String), }); export type UsageLimitsReport = typeof UsageLimitsReport.Type;