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.
- package/CHANGELOG.md +20 -0
- package/LICENSE +21 -0
- package/README.md +336 -0
- package/dist/aggregator.d.ts +40 -0
- package/dist/aggregator.d.ts.map +1 -0
- package/dist/aggregator.js +254 -0
- package/dist/aggregator.js.map +1 -0
- package/dist/classifier.d.ts +50 -0
- package/dist/classifier.d.ts.map +1 -0
- package/dist/classifier.js +304 -0
- package/dist/classifier.js.map +1 -0
- package/dist/copilot-budget.d.ts +57 -0
- package/dist/copilot-budget.d.ts.map +1 -0
- package/dist/copilot-budget.js +500 -0
- package/dist/copilot-budget.js.map +1 -0
- package/dist/debug.d.ts +7 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +49 -0
- package/dist/debug.js.map +1 -0
- package/dist/estimator.d.ts +92 -0
- package/dist/estimator.d.ts.map +1 -0
- package/dist/estimator.js +523 -0
- package/dist/estimator.js.map +1 -0
- package/dist/github-api.d.ts +11 -0
- package/dist/github-api.d.ts.map +1 -0
- package/dist/github-api.js +273 -0
- package/dist/github-api.js.map +1 -0
- package/dist/persistence.d.ts +28 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +246 -0
- package/dist/persistence.js.map +1 -0
- package/dist/tools.d.ts +20 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +300 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +184 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
|
@@ -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"}
|