1import * as Schema from "effect/Schema";
2
3import {
4 ForwardCompatibleArray,
5 IsoDateTime,
6 NonNegativeInt,
7 TrimmedNonEmptyString,
8} from "./baseSchemas.ts";
9import { ProviderDriverKind, ProviderInstanceId } from "./providerInstance.ts";
10import { UsageLimitSourceId } from "./usageLimitSourceId.ts";
11
12/** Stable per-provider ids let sparse updates merge with full probes. */
13export const ServerProviderUsageWindow = Schema.Struct({
14 id: TrimmedNonEmptyString,
15 kind: Schema.Literals(["session", "weekly", "monthly", "other"]),
16 label: TrimmedNonEmptyString,
17 /** When present, this limit applies only to these provider model IDs. */
18 modelIds: Schema.optional(Schema.Array(TrimmedNonEmptyString)),
19 providerIds: Schema.optional(Schema.Array(TrimmedNonEmptyString)),
20 usedPercent: Schema.Number.check(Schema.isBetween({ minimum: 0, maximum: 100 })),
21 resetsAt: Schema.optional(IsoDateTime),
22 windowDurationMins: Schema.optional(NonNegativeInt),
23});
24export type ServerProviderUsageWindow = typeof ServerProviderUsageWindow.Type;
25
26/** One kind of banked reset, such as Claude's weekly reset or its Fable-only reset. */
27export const ServerProviderResetCreditOption = Schema.Struct({
28 creditId: TrimmedNonEmptyString,
29 label: TrimmedNonEmptyString,
30 description: TrimmedNonEmptyString,
31 availableCount: NonNegativeInt,
32 expiresAt: Schema.optional(IsoDateTime),
33});
34export type ServerProviderResetCreditOption = typeof ServerProviderResetCreditOption.Type;
35
36export const ServerProviderResetCredits = Schema.Struct({
37 availableCount: NonNegativeInt,
38 nextExpiresAt: Schema.optional(IsoDateTime),
39 /** Pins hub redemption to the displayed credit, including retries from another client. */
40 nextCreditId: Schema.optional(TrimmedNonEmptyString),
41 /** Present when resets differ in what they refill; redeem one by its `creditId`. */
42 options: Schema.optional(ForwardCompatibleArray(ServerProviderResetCreditOption)),
43});
44export type ServerProviderResetCredits = typeof ServerProviderResetCredits.Type;
45
46/** Clear bars for unavailable accounts; retain last good bars after a failed probe. */
47export const ServerProviderUsageLimits = Schema.Struct({
48 checkedAt: IsoDateTime,
49 windows: ForwardCompatibleArray(ServerProviderUsageWindow),
50 credentialFingerprint: Schema.optional(TrimmedNonEmptyString),
51 resetCredits: Schema.optional(ServerProviderResetCredits),
52 externalUsage: Schema.optional(
53 Schema.Struct({
54 label: TrimmedNonEmptyString,
55 url: TrimmedNonEmptyString,
56 }),
57 ),
58 unavailable: Schema.optional(
59 Schema.Struct({
60 reason: Schema.Literals(["unsupported", "probeFailed"]),
61 message: Schema.optional(TrimmedNonEmptyString),
62 }),
63 ),
64});
65export type ServerProviderUsageLimits = typeof ServerProviderUsageLimits.Type;
66
67/** Partial updates merge windows by id; omitted windows remain unchanged. */
68export const ProviderUsageLimitsUpdate = Schema.Struct({
69 windows: Schema.Array(ServerProviderUsageWindow),
70});
71export type ProviderUsageLimitsUpdate = typeof ProviderUsageLimitsUpdate.Type;
72
73export const UsageLimitSourceAccount = Schema.Struct({
74 id: TrimmedNonEmptyString,
75 driver: ProviderDriverKind,
76 email: Schema.optional(TrimmedNonEmptyString),
77 plan: Schema.optional(TrimmedNonEmptyString),
78 workspaceId: Schema.optional(TrimmedNonEmptyString),
79 usageLimits: ServerProviderUsageLimits,
80});
81export type UsageLimitSourceAccount = typeof UsageLimitSourceAccount.Type;
82
83export const UsageLimitSourceSnapshot = Schema.Struct({
84 id: UsageLimitSourceId,
85 kind: Schema.Literal("cliproxy"),
86 label: TrimmedNonEmptyString,
87 checkedAt: IsoDateTime,
88 accounts: ForwardCompatibleArray(UsageLimitSourceAccount),
89 error: Schema.optional(TrimmedNonEmptyString),
90});
91export type UsageLimitSourceSnapshot = typeof UsageLimitSourceSnapshot.Type;
92
93export const UsageLimitSourceSnapshots = ForwardCompatibleArray(UsageLimitSourceSnapshot);
94export type UsageLimitSourceSnapshots = typeof UsageLimitSourceSnapshots.Type;
95
96export const UsageLimitSourceConsumeResetCreditInput = Schema.Struct({
97 sourceId: UsageLimitSourceId,
98 accountId: TrimmedNonEmptyString,
99 creditId: TrimmedNonEmptyString,
100});
101export type UsageLimitSourceConsumeResetCreditInput =
102 typeof UsageLimitSourceConsumeResetCreditInput.Type;
103
104export const ProviderConsumeResetCreditInput = Schema.Union([
105 Schema.Struct({
106 instanceId: ProviderInstanceId,
107 creditId: Schema.optional(TrimmedNonEmptyString),
108 }),
109 UsageLimitSourceConsumeResetCreditInput,
110]);
111export type ProviderConsumeResetCreditInput = typeof ProviderConsumeResetCreditInput.Type;
112
113export class UsageLimitSourceError extends Schema.TaggedError<UsageLimitSourceError>()(
114 "UsageLimitSourceError",
115 { detail: Schema.String },
116) {
117 override get message(): string {
118 return this.detail;
119 }
120}
121
122export const ProviderConsumeResetCreditOutcome = Schema.Literals([
123 "reset",
124 "nothingToReset",
125 "noCredit",
126 "alreadyRedeemed",
127]);
128export type ProviderConsumeResetCreditOutcome = typeof ProviderConsumeResetCreditOutcome.Type;
129
130export const ProviderConsumeResetCreditResult = Schema.Struct({
131 outcome: ProviderConsumeResetCreditOutcome,
132 warning: Schema.optional(TrimmedNonEmptyString),
133});
134export type ProviderConsumeResetCreditResult = typeof ProviderConsumeResetCreditResult.Type;
135
136export const UsageLimitsReport = Schema.Struct({
137 createdAt: IsoDateTime,
138 accounts: Schema.Array(
139 Schema.Struct({
140 id: TrimmedNonEmptyString,
141 driver: ProviderDriverKind,
142 label: TrimmedNonEmptyString,
143 plan: Schema.optional(TrimmedNonEmptyString),
144 email: Schema.optional(TrimmedNonEmptyString),
145 sourceLabel: Schema.optional(TrimmedNonEmptyString),
146 instanceId: Schema.optional(ProviderInstanceId),
147 resetCreditInput: Schema.optional(ProviderConsumeResetCreditInput),
148 displayName: Schema.optional(Schema.String),
149 accentColor: Schema.optional(Schema.String),
150 limits: ServerProviderUsageLimits,
151 }),
152 ),
153 notices: Schema.Array(Schema.String),
154});
155export type UsageLimitsReport = typeof UsageLimitsReport.Type;