opencode-copilot-usage-detector 0.1.0

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.
@@ -0,0 +1,50 @@
1
+ import type { LimitClass, LimitHitEvent } from "./types.js";
2
+ export declare function classifyErrorImmediate(errorMessage: string, statusCode: number | undefined, responseHeaders: Record<string, string> | undefined): {
3
+ class: LimitClass;
4
+ confidence: number;
5
+ reason: string;
6
+ };
7
+ export interface RateLimitHeaders {
8
+ retryAfter: string | null;
9
+ remaining: string | null;
10
+ limit: string | null;
11
+ reset: string | null;
12
+ used: string | null;
13
+ resource: string | null;
14
+ all: Record<string, string>;
15
+ }
16
+ export declare function extractRateLimitHeaders(headers: Record<string, string> | undefined): RateLimitHeaders | null;
17
+ export interface ReclassificationContext {
18
+ /** Observations from the last N minutes after the original error */
19
+ recentObservations: Array<{
20
+ type: string;
21
+ model?: string;
22
+ ts: string;
23
+ }>;
24
+ /** Current daily token total */
25
+ dailyTokens: number;
26
+ /** Current daily request total */
27
+ dailyRequests: number;
28
+ /** Current global budget estimate (if available) */
29
+ globalEstimate: number | null;
30
+ /** Whether other models had successful requests since the error */
31
+ otherModelsWorking: boolean;
32
+ /** List of models that worked since the error */
33
+ workingModels: string[];
34
+ /** Error rate in the reclassification window */
35
+ errorRate: number;
36
+ /** Minutes since the original error */
37
+ minutesSinceError: number;
38
+ /** Whether we've observed a recovery (successful request after limit) */
39
+ hasRecovered: boolean;
40
+ /** Minutes from error to recovery (if recovered) */
41
+ recoveryMinutes: number | null;
42
+ }
43
+ export declare function reclassify(originalEvent: LimitHitEvent, ctx: ReclassificationContext): {
44
+ newClass: LimitClass;
45
+ confidence: number;
46
+ reason: string;
47
+ };
48
+ export declare function scheduleReclassification(eventTs: string, buildContext: () => ReclassificationContext): void;
49
+ export declare function clearPendingTimers(): void;
50
+ //# sourceMappingURL=classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.d.ts","sourceRoot":"","sources":["../src/classifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAsD3D,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAClD;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA2E3D;AAkBD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC5B;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAC1C,gBAAgB,GAAG,IAAI,CA4BzB;AAMD,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,kBAAkB,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACvE,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,aAAa,EAAE,MAAM,CAAA;IACrB,oDAAoD;IACpD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,mEAAmE;IACnE,kBAAkB,EAAE,OAAO,CAAA;IAC3B,iDAAiD;IACjD,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,iBAAiB,EAAE,MAAM,CAAA;IACzB,yEAAyE;IACzE,YAAY,EAAE,OAAO,CAAA;IACrB,oDAAoD;IACpD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED,wBAAgB,UAAU,CACxB,aAAa,EAAE,aAAa,EAC5B,GAAG,EAAE,uBAAuB,GAC3B;IAAE,QAAQ,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA8F9D;AASD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,uBAAuB,GAC1C,IAAI,CAoBN;AA8CD,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
@@ -0,0 +1,304 @@
1
+ import { readObservations, appendObservation } from "./persistence.js";
2
+ // ============================================================
3
+ // Stage 1: Immediate classification from error message patterns
4
+ // ============================================================
5
+ const PREVIEW_LIMIT_PATTERNS = [
6
+ "model is currently unavailable",
7
+ "model capacity reached",
8
+ "this model is at capacity",
9
+ "model.*unavailable",
10
+ "preview.*limit",
11
+ "model.*capacity",
12
+ ];
13
+ const DAILY_LIMIT_PATTERNS = [
14
+ "exceeded your copilot token usage",
15
+ "exceeded.*token.*usage",
16
+ "rate_limited",
17
+ "daily.*limit",
18
+ "usage.*limit.*reached",
19
+ ];
20
+ const BURST_LIMIT_PATTERNS = [
21
+ "too many requests",
22
+ "requests.*per.*minute",
23
+ "throttl",
24
+ "slow down",
25
+ ];
26
+ const MODEL_BLOCKED_PATTERNS = [
27
+ "not available.*plan",
28
+ "not supported",
29
+ "forbidden",
30
+ "access denied",
31
+ "not authorized.*model",
32
+ "not included.*plan",
33
+ "model not found",
34
+ "not allowed",
35
+ "does not have access",
36
+ "not enabled",
37
+ ];
38
+ function matchesAny(text, patterns) {
39
+ const lower = text.toLowerCase();
40
+ return patterns.some((p) => {
41
+ if (p.includes(".*")) {
42
+ return new RegExp(p, "i").test(text);
43
+ }
44
+ return lower.includes(p);
45
+ });
46
+ }
47
+ export function classifyErrorImmediate(errorMessage, statusCode, responseHeaders) {
48
+ const msg = errorMessage.toLowerCase();
49
+ // Check for blocked model first — 403 is a strong signal
50
+ if (statusCode === 403) {
51
+ return {
52
+ class: "model_blocked",
53
+ confidence: 0.95,
54
+ reason: "status_403_forbidden",
55
+ };
56
+ }
57
+ if (matchesAny(errorMessage, MODEL_BLOCKED_PATTERNS)) {
58
+ return {
59
+ class: "model_blocked",
60
+ confidence: 0.6,
61
+ reason: "error_message_matches_blocked_pattern",
62
+ };
63
+ }
64
+ // Check for rate-limit headers first — these are the most reliable signal
65
+ if (responseHeaders) {
66
+ const retryAfter = findHeader(responseHeaders, "retry-after");
67
+ const rateLimitRemaining = findHeader(responseHeaders, "x-ratelimit-remaining");
68
+ const rateLimitReset = findHeader(responseHeaders, "x-ratelimit-reset");
69
+ if (retryAfter || rateLimitRemaining === "0") {
70
+ // Headers present — likely a real rate limit. Try to determine type from context.
71
+ const retrySeconds = retryAfter ? parseInt(retryAfter, 10) : NaN;
72
+ if (!isNaN(retrySeconds) && retrySeconds < 120) {
73
+ return {
74
+ class: "burst_rpm_limit",
75
+ confidence: 0.8,
76
+ reason: `retry-after=${retrySeconds}s suggests burst/RPM limit`,
77
+ };
78
+ }
79
+ }
80
+ // Log all rate-limit-related headers for future analysis
81
+ if (rateLimitReset || rateLimitRemaining) {
82
+ // We have headers but can't fully classify yet — proceed with message analysis
83
+ }
84
+ }
85
+ // 429 status code without clear message
86
+ if (statusCode === 429 && matchesAny(errorMessage, BURST_LIMIT_PATTERNS)) {
87
+ return {
88
+ class: "burst_rpm_limit",
89
+ confidence: 0.6,
90
+ reason: "status_429_with_burst_pattern",
91
+ };
92
+ }
93
+ // Check known patterns
94
+ if (matchesAny(errorMessage, PREVIEW_LIMIT_PATTERNS)) {
95
+ return {
96
+ class: "preview_limit",
97
+ confidence: 0.7,
98
+ reason: "error_message_matches_preview_pattern",
99
+ };
100
+ }
101
+ if (matchesAny(errorMessage, DAILY_LIMIT_PATTERNS)) {
102
+ return {
103
+ class: "hard_daily_limit",
104
+ confidence: 0.5, // Lower confidence — wait for reclassification to confirm
105
+ reason: "error_message_matches_daily_pattern",
106
+ };
107
+ }
108
+ // Unknown — will be reclassified later
109
+ return {
110
+ class: "unknown",
111
+ confidence: 0,
112
+ reason: "no_pattern_match",
113
+ };
114
+ }
115
+ // ============================================================
116
+ // Rate-limit header extraction
117
+ // ============================================================
118
+ function findHeader(headers, name) {
119
+ // Headers may be case-insensitive
120
+ const lower = name.toLowerCase();
121
+ for (const [key, value] of Object.entries(headers)) {
122
+ if (key.toLowerCase() === lower)
123
+ return value;
124
+ }
125
+ return undefined;
126
+ }
127
+ export function extractRateLimitHeaders(headers) {
128
+ if (!headers)
129
+ return null;
130
+ const relevant = {};
131
+ for (const [key, value] of Object.entries(headers)) {
132
+ const lower = key.toLowerCase();
133
+ if (lower.includes("rate") ||
134
+ lower.includes("retry") ||
135
+ lower.includes("limit") ||
136
+ lower.includes("throttl") ||
137
+ lower.includes("remaining")) {
138
+ relevant[key] = value;
139
+ }
140
+ }
141
+ if (Object.keys(relevant).length === 0)
142
+ return null;
143
+ return {
144
+ retryAfter: findHeader(headers, "retry-after") ?? null,
145
+ remaining: findHeader(headers, "x-ratelimit-remaining") ?? null,
146
+ limit: findHeader(headers, "x-ratelimit-limit") ?? null,
147
+ reset: findHeader(headers, "x-ratelimit-reset") ?? null,
148
+ used: findHeader(headers, "x-ratelimit-used") ?? null,
149
+ resource: findHeader(headers, "x-ratelimit-resource") ?? null,
150
+ all: relevant,
151
+ };
152
+ }
153
+ export function reclassify(originalEvent, ctx) {
154
+ // Check for blocked model: zero usage and no recovery after 30 min
155
+ if (originalEvent.day_cumulative_tokens === 0 &&
156
+ originalEvent.day_cumulative_requests === 0 &&
157
+ !ctx.hasRecovered &&
158
+ ctx.minutesSinceError > 30) {
159
+ return {
160
+ newClass: "model_blocked",
161
+ confidence: 0.7,
162
+ reason: "zero_usage_no_recovery_suggests_blocked",
163
+ };
164
+ }
165
+ // Stage 5: If we have recovery data, use recovery time to classify
166
+ if (ctx.hasRecovered && ctx.recoveryMinutes !== null) {
167
+ if (ctx.recoveryMinutes < 30) {
168
+ return {
169
+ newClass: "burst_rpm_limit",
170
+ confidence: 0.8,
171
+ reason: `recovered_in_${Math.round(ctx.recoveryMinutes)}min_suggests_burst_limit`,
172
+ };
173
+ }
174
+ if (ctx.recoveryMinutes > 120) {
175
+ return {
176
+ newClass: "hard_daily_limit",
177
+ confidence: 0.8,
178
+ reason: `no_recovery_for_${Math.round(ctx.recoveryMinutes)}min_suggests_daily_limit`,
179
+ };
180
+ }
181
+ // Between 30-120 min — ambiguous
182
+ return {
183
+ newClass: "unknown_recovery",
184
+ confidence: 0.4,
185
+ reason: `recovery_in_${Math.round(ctx.recoveryMinutes)}min_ambiguous`,
186
+ };
187
+ }
188
+ // Stage 2: Cross-model correlation
189
+ if (ctx.otherModelsWorking && ctx.workingModels.length > 0) {
190
+ return {
191
+ newClass: "preview_limit",
192
+ confidence: 0.75,
193
+ reason: `only_${originalEvent.model}_affected_other_models_working: ${ctx.workingModels.join(",")}`,
194
+ };
195
+ }
196
+ // Stage 3: Cancel-rate analysis
197
+ if (ctx.errorRate > 0.7) {
198
+ // High error rate — all models seem affected
199
+ return {
200
+ newClass: "hard_daily_limit",
201
+ confidence: 0.7,
202
+ reason: `cancel_rate_${(ctx.errorRate * 100).toFixed(0)}pct_suggests_global_limit`,
203
+ };
204
+ }
205
+ // Stage 4: Budget comparison
206
+ if (ctx.globalEstimate !== null && ctx.globalEstimate > 0) {
207
+ const pct = ctx.dailyTokens / ctx.globalEstimate;
208
+ if (pct < 0.3 && ctx.otherModelsWorking) {
209
+ return {
210
+ newClass: "preview_limit",
211
+ confidence: 0.7,
212
+ reason: `hit_at_${(pct * 100).toFixed(0)}pct_of_global_estimate_suggests_preview`,
213
+ };
214
+ }
215
+ if (pct > 0.7) {
216
+ return {
217
+ newClass: "hard_daily_limit",
218
+ confidence: 0.65,
219
+ reason: `hit_at_${(pct * 100).toFixed(0)}pct_of_global_estimate_suggests_daily`,
220
+ };
221
+ }
222
+ }
223
+ // Not enough info to reclassify — keep as-is
224
+ if (!ctx.hasRecovered && ctx.minutesSinceError > 120) {
225
+ // If we haven't recovered after 2 hours and have no other data, lean toward daily limit
226
+ return {
227
+ newClass: "hard_daily_limit",
228
+ confidence: 0.5,
229
+ reason: "no_recovery_after_2h_no_other_signals",
230
+ };
231
+ }
232
+ return {
233
+ newClass: originalEvent.class,
234
+ confidence: 0,
235
+ reason: "insufficient_data_for_reclassification",
236
+ };
237
+ }
238
+ // ============================================================
239
+ // Schedule reclassification
240
+ // ============================================================
241
+ const RECLASSIFY_DELAY_MS = 10 * 60 * 1000; // 10 minutes
242
+ const pendingTimers = new Map();
243
+ export function scheduleReclassification(eventTs, buildContext) {
244
+ // Clear any existing timer for this event
245
+ const existing = pendingTimers.get(eventTs);
246
+ if (existing)
247
+ clearTimeout(existing);
248
+ const timer = setTimeout(() => {
249
+ pendingTimers.delete(eventTs);
250
+ try {
251
+ performReclassification(eventTs, buildContext);
252
+ }
253
+ catch {
254
+ // Reclassification failure is not critical
255
+ }
256
+ }, RECLASSIFY_DELAY_MS);
257
+ // Don't prevent process exit while waiting for reclassification
258
+ if (typeof timer === "object" && "unref" in timer) {
259
+ timer.unref();
260
+ }
261
+ pendingTimers.set(eventTs, timer);
262
+ }
263
+ function performReclassification(eventTs, buildContext) {
264
+ // Find the original event
265
+ const allEvents = readObservations({ type: "limit_hit" });
266
+ const original = allEvents.find((e) => e.type === "limit_hit" && e.ts === eventTs);
267
+ if (!original)
268
+ return;
269
+ const ctx = buildContext();
270
+ const result = reclassify(original, ctx);
271
+ // Only write reclassify event if we actually changed the class
272
+ if (result.newClass !== original.class && result.confidence > 0) {
273
+ appendObservation({
274
+ ts: new Date().toISOString(),
275
+ type: "reclassify",
276
+ ref_ts: eventTs,
277
+ old_class: original.class,
278
+ new_class: result.newClass,
279
+ reason: result.reason,
280
+ evidence: {
281
+ confidence: result.confidence,
282
+ daily_tokens: ctx.dailyTokens,
283
+ daily_requests: ctx.dailyRequests,
284
+ error_rate: ctx.errorRate,
285
+ other_models_working: ctx.otherModelsWorking,
286
+ working_models: ctx.workingModels,
287
+ minutes_since_error: ctx.minutesSinceError,
288
+ has_recovered: ctx.hasRecovered,
289
+ recovery_minutes: ctx.recoveryMinutes,
290
+ global_estimate: ctx.globalEstimate,
291
+ },
292
+ });
293
+ }
294
+ }
295
+ // ============================================================
296
+ // Cleanup
297
+ // ============================================================
298
+ export function clearPendingTimers() {
299
+ for (const timer of pendingTimers.values()) {
300
+ clearTimeout(timer);
301
+ }
302
+ pendingTimers.clear();
303
+ }
304
+ //# sourceMappingURL=classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.js","sourceRoot":"","sources":["../src/classifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { Event, AssistantMessage, ApiError } from "@opencode-ai/sdk";
2
+ export declare function formatTokensShort(n: number): string;
3
+ export declare function isAssistantMessage(msg: unknown): msg is AssistantMessage;
4
+ export declare function isApiError(error: unknown): error is ApiError;
5
+ export declare function isModelBlockedError(error: ApiError, modelCumulativeTokens: number, modelCumulativeRequests: number): boolean;
6
+ export declare function isRateLimitError(error: ApiError): boolean;
7
+ declare const plugin: (ctx: import("@opencode-ai/plugin").PluginInput) => Promise<{
8
+ config: (opencodeConfig: import("@opencode-ai/sdk").Config) => Promise<void>;
9
+ "command.execute.before": (input: {
10
+ command: string;
11
+ sessionID: string;
12
+ arguments: string;
13
+ }, _output: {
14
+ parts: any[];
15
+ }) => Promise<void>;
16
+ event: ({ event }: {
17
+ event: Event;
18
+ }) => Promise<void>;
19
+ "chat.params": ({ sessionID, model, provider }: {
20
+ sessionID: string;
21
+ agent: string;
22
+ model: import("@opencode-ai/sdk").Model;
23
+ provider: import("@opencode-ai/plugin").ProviderContext;
24
+ message: import("@opencode-ai/sdk").UserMessage;
25
+ }) => Promise<void>;
26
+ "experimental.chat.system.transform": (_input: {
27
+ sessionID?: string;
28
+ model: import("@opencode-ai/sdk").Model;
29
+ }, output: {
30
+ system: string[];
31
+ }) => Promise<void>;
32
+ "experimental.session.compacting": (_input: {
33
+ sessionID: string;
34
+ }, output: {
35
+ context: string[];
36
+ prompt?: string;
37
+ }) => Promise<void>;
38
+ tool: {
39
+ budget: {
40
+ description: string;
41
+ args: {
42
+ action: import("zod").ZodEnum<{
43
+ insights: "insights";
44
+ status: "status";
45
+ history: "history";
46
+ errors: "errors";
47
+ recompute: "recompute";
48
+ }>;
49
+ };
50
+ execute(args: {
51
+ action: "insights" | "status" | "history" | "errors" | "recompute";
52
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
53
+ };
54
+ };
55
+ }>;
56
+ export default plugin;
57
+ //# sourceMappingURL=copilot-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copilot-budget.d.ts","sourceRoot":"","sources":["../src/copilot-budget.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,KAAK,EACL,gBAAgB,EAGhB,QAAQ,EACT,MAAM,kBAAkB,CAAA;AAmCzB,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAInD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,gBAAgB,CAOxE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAM5D;AAkBD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,EACf,qBAAqB,EAAE,MAAM,EAC7B,uBAAuB,EAAE,MAAM,GAC9B,OAAO,CAkBT;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAWzD;AAUD,QAAA,MAAM,MAAM;;sCA4FC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,WACvD;QAAE,KAAK,EAAE,GAAG,EAAE,CAAA;KAAE;uBA+CF;QAAE,KAAK,EAAE,KAAK,CAAA;KAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmV1B,CAAA;AAEnB,eAAe,MAAM,CAAA"}