@azumag/opencode-rate-limit-fallback 1.0.21 → 1.1.2

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/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
2
  export declare const RateLimitFallback: Plugin;
3
3
  export default RateLimitFallback;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA6NlD,eAAO,MAAM,iBAAiB,EAAE,MA4c/B,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,12 +1,51 @@
1
- import { existsSync, readFileSync, appendFileSync, mkdirSync } from "fs";
2
- import { join, dirname } from "path";
3
- // Debug log at module level
4
- console.log("[rate-limit-fallback] Module loaded, version:", process.env.npm_package_version || "unknown");
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { join } from "path";
3
+ // Event type guards
4
+ function isSessionErrorEvent(event) {
5
+ return event.type === "session.error" &&
6
+ typeof event.properties === "object" &&
7
+ event.properties !== null &&
8
+ "sessionID" in event.properties &&
9
+ "error" in event.properties;
10
+ }
11
+ function isMessageUpdatedEvent(event) {
12
+ return event.type === "message.updated" &&
13
+ typeof event.properties === "object" &&
14
+ event.properties !== null &&
15
+ "info" in event.properties;
16
+ }
17
+ function isSessionStatusEvent(event) {
18
+ return event.type === "session.status" &&
19
+ typeof event.properties === "object" &&
20
+ event.properties !== null;
21
+ }
22
+ // Subagent event type guards
23
+ function isSubagentSessionCreatedEvent(event) {
24
+ return event.type === "subagent.session.created" &&
25
+ typeof event.properties === "object" &&
26
+ event.properties !== null &&
27
+ "sessionID" in event.properties &&
28
+ "parentSessionID" in event.properties;
29
+ }
5
30
  const DEFAULT_FALLBACK_MODELS = [
6
31
  { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
7
32
  { providerID: "google", modelID: "gemini-2.5-pro" },
8
33
  { providerID: "google", modelID: "gemini-2.5-flash" },
9
34
  ];
35
+ const VALID_FALLBACK_MODES = ["cycle", "stop", "retry-last"];
36
+ const RATE_LIMIT_INDICATORS = [
37
+ "rate limit",
38
+ "rate_limit",
39
+ "ratelimit",
40
+ "too many requests",
41
+ "quota exceeded",
42
+ "resource exhausted",
43
+ "usage limit",
44
+ "high concurrency usage of this api",
45
+ "high concurrency",
46
+ "reduce concurrency",
47
+ "429",
48
+ ];
10
49
  const DEFAULT_CONFIG = {
11
50
  fallbackModels: DEFAULT_FALLBACK_MODELS,
12
51
  cooldownMs: 60 * 1000,
@@ -27,16 +66,17 @@ function loadConfig(directory) {
27
66
  const content = readFileSync(configPath, "utf-8");
28
67
  const userConfig = JSON.parse(content);
29
68
  const mode = userConfig.fallbackMode;
30
- const validModes = ["cycle", "stop", "retry-last"];
31
69
  return {
32
70
  ...DEFAULT_CONFIG,
33
71
  ...userConfig,
34
72
  fallbackModels: userConfig.fallbackModels || DEFAULT_CONFIG.fallbackModels,
35
- fallbackMode: validModes.includes(mode) ? mode : DEFAULT_CONFIG.fallbackMode,
73
+ fallbackMode: VALID_FALLBACK_MODES.includes(mode) ? mode : DEFAULT_CONFIG.fallbackMode,
36
74
  };
37
75
  }
38
76
  catch (error) {
39
- // Config load failed, continue to next path
77
+ if (process.env.NODE_ENV === "development") {
78
+ console.warn(`Failed to load config from ${configPath}:`, error);
79
+ }
40
80
  }
41
81
  }
42
82
  }
@@ -46,126 +86,119 @@ function getModelKey(providerID, modelID) {
46
86
  return `${providerID}/${modelID}`;
47
87
  }
48
88
  function isRateLimitError(error) {
49
- if (!error)
89
+ if (!error || typeof error !== "object")
50
90
  return false;
51
- if (error.name === "APIError" && error.data?.statusCode === 429) {
91
+ // More type-safe error object structure
92
+ const err = error;
93
+ // Check for 429 status code in APIError
94
+ if (err.name === "APIError" && err.data?.statusCode === 429) {
52
95
  return true;
53
96
  }
54
- const responseBody = (error.data?.responseBody || "").toLowerCase();
55
- const message = (error.data?.message || error.message || "").toLowerCase();
56
- const errorName = (error.name || "").toLowerCase();
57
- const rateLimitIndicators = [
58
- "rate limit",
59
- "rate_limit",
60
- "ratelimit",
61
- "too many requests",
62
- "quota exceeded",
63
- "resource exhausted",
64
- "usage limit",
65
- "high concurrency usage of this api",
66
- "high concurrency",
67
- "reduce concurrency",
68
- "429",
69
- ];
70
- return rateLimitIndicators.some((indicator) => responseBody.includes(indicator) ||
97
+ // Type-safe access to error fields
98
+ const responseBody = String(err.data?.responseBody || "").toLowerCase();
99
+ const message = String(err.data?.message || err.message || "").toLowerCase();
100
+ const errorName = String(err.name || "").toLowerCase();
101
+ return RATE_LIMIT_INDICATORS.some((indicator) => responseBody.includes(indicator) ||
71
102
  message.includes(indicator) ||
72
103
  errorName.includes(indicator));
73
104
  }
105
+ // Constants for deduplication and state management
106
+ const DEDUP_WINDOW_MS = 5000;
107
+ const STATE_TIMEOUT_MS = 30000;
108
+ const CLEANUP_INTERVAL_MS = 300000; // 5 minutes
109
+ const SESSION_ENTRY_TTL_MS = 3600000; // 1 hour
110
+ // Track cleanup intervals globally to prevent TypeScript warnings
111
+ const activeCleanupIntervals = [];
74
112
  export const RateLimitFallback = async ({ client, directory }) => {
75
113
  const config = loadConfig(directory);
76
- const logFilePath = join(process.env.HOME || "", ".opencode", "rate-limit-fallback-debug.log");
77
- // Ensure log directory exists
78
- try {
79
- mkdirSync(dirname(logFilePath), { recursive: true });
80
- }
81
- catch { }
82
- // Write to file for debugging
83
- const logToFile = (message) => {
84
- try {
85
- appendFileSync(logFilePath, `[${new Date().toISOString()}] ${message}\n`);
86
- }
87
- catch { }
88
- };
89
- logToFile(`Plugin loaded, config: ${JSON.stringify(config)}`);
90
- console.log("[rate-limit-fallback] Plugin loaded, config:", config);
91
- // Use client.app.log for better logging in both run and TUI modes
92
- try {
93
- await client.app.log({
94
- body: {
95
- service: "rate-limit-fallback",
96
- level: "info",
97
- message: `Plugin loaded, config: ${JSON.stringify(config)}`,
98
- },
99
- });
100
- }
101
- catch (e) {
102
- logToFile(`client.app.log failed: ${e}`);
103
- console.log("[rate-limit-fallback] Plugin loaded, config:", config);
104
- }
105
114
  if (!config.enabled) {
106
- logToFile("Plugin disabled, returning empty object");
107
- try {
108
- await client.app.log({
109
- body: {
110
- service: "rate-limit-fallback",
111
- level: "info",
112
- message: "Plugin disabled, returning empty object",
113
- },
114
- });
115
- }
116
- catch {
117
- console.log("[rate-limit-fallback] Plugin disabled, returning empty object");
118
- }
119
115
  return {};
120
116
  }
121
117
  const rateLimitedModels = new Map();
122
118
  const retryState = new Map();
123
119
  const currentSessionModel = new Map();
124
120
  const fallbackInProgress = new Map(); // sessionID -> timestamp
125
- async function logOrToast(message, variant = "info") {
126
- try {
127
- await client.tui.showToast({
128
- body: { message, variant },
129
- });
130
- }
131
- catch {
132
- const variantMap = {
133
- info: "info",
134
- success: "info",
135
- warning: "warn",
136
- error: "error",
121
+ // Subagent session tracking
122
+ const sessionHierarchies = new Map(); // rootSessionID -> SessionHierarchy
123
+ const sessionToRootMap = new Map(); // sessionID -> rootSessionID
124
+ const maxSubagentDepth = config.maxSubagentDepth ?? 10;
125
+ // Helper functions for session hierarchy management
126
+ function getOrCreateHierarchy(rootSessionID) {
127
+ let hierarchy = sessionHierarchies.get(rootSessionID);
128
+ if (!hierarchy) {
129
+ hierarchy = {
130
+ rootSessionID,
131
+ subagents: new Map(),
132
+ sharedFallbackState: "none",
133
+ sharedConfig: config,
134
+ createdAt: Date.now(),
135
+ lastActivity: Date.now(),
137
136
  };
138
- await client.app.log({
139
- body: {
140
- service: "rate-limit-fallback",
141
- level: variantMap[variant],
142
- message,
143
- },
144
- });
137
+ sessionHierarchies.set(rootSessionID, hierarchy);
138
+ sessionToRootMap.set(rootSessionID, rootSessionID);
145
139
  }
140
+ return hierarchy;
146
141
  }
147
- async function toast(title, message, variant = "info") {
148
- try {
149
- await client.tui.showToast({
150
- body: { title, message, variant },
151
- });
152
- }
153
- catch {
154
- const variantMap = {
155
- info: "info",
156
- success: "info",
157
- warning: "warn",
158
- error: "error",
159
- };
160
- await client.app.log({
161
- body: {
162
- service: "rate-limit-fallback",
163
- level: variantMap[variant],
164
- message: `${title}: ${message}`,
165
- },
166
- });
142
+ function registerSubagent(sessionID, parentSessionID) {
143
+ // Validate parent session exists
144
+ // Parent session must either be registered in sessionToRootMap or be a new root session
145
+ const parentRootSessionID = sessionToRootMap.get(parentSessionID);
146
+ // Determine root session - if parent doesn't exist, treat it as a new root
147
+ const rootSessionID = parentRootSessionID || parentSessionID;
148
+ // If parent is not a subagent but we're treating it as a root, create a hierarchy for it
149
+ // This allows sessions to become roots when their first subagent is registered
150
+ const hierarchy = getOrCreateHierarchy(rootSessionID);
151
+ const parentSubagent = hierarchy.subagents.get(parentSessionID);
152
+ const depth = parentSubagent ? parentSubagent.depth + 1 : 1;
153
+ // Enforce max depth
154
+ if (depth > maxSubagentDepth) {
155
+ if (process.env.NODE_ENV === "development") {
156
+ console.warn(`Subagent depth ${depth} exceeds max ${maxSubagentDepth}`);
157
+ }
158
+ return false;
167
159
  }
160
+ const subagent = {
161
+ sessionID,
162
+ parentSessionID,
163
+ depth,
164
+ fallbackState: "none",
165
+ createdAt: Date.now(),
166
+ lastActivity: Date.now(),
167
+ };
168
+ hierarchy.subagents.set(sessionID, subagent);
169
+ sessionToRootMap.set(sessionID, rootSessionID);
170
+ hierarchy.lastActivity = Date.now();
171
+ return true;
172
+ }
173
+ function getRootSession(sessionID) {
174
+ return sessionToRootMap.get(sessionID) || null;
168
175
  }
176
+ function getHierarchy(sessionID) {
177
+ const rootSessionID = getRootSession(sessionID);
178
+ return rootSessionID ? sessionHierarchies.get(rootSessionID) || null : null;
179
+ }
180
+ // Cleanup stale session model entries (every 5 minutes)
181
+ const cleanupInterval = setInterval(() => {
182
+ const now = Date.now();
183
+ for (const [sessionID, entry] of currentSessionModel.entries()) {
184
+ // Remove entries older than 1 hour
185
+ if (now - entry.lastUpdated > SESSION_ENTRY_TTL_MS) {
186
+ currentSessionModel.delete(sessionID);
187
+ }
188
+ }
189
+ // Clean up stale session hierarchies
190
+ for (const [rootSessionID, hierarchy] of sessionHierarchies.entries()) {
191
+ if (now - hierarchy.lastActivity > SESSION_ENTRY_TTL_MS) {
192
+ // Clean up all subagents in this hierarchy
193
+ for (const subagentID of hierarchy.subagents.keys()) {
194
+ sessionToRootMap.delete(subagentID);
195
+ }
196
+ sessionHierarchies.delete(rootSessionID);
197
+ sessionToRootMap.delete(rootSessionID);
198
+ }
199
+ }
200
+ }, CLEANUP_INTERVAL_MS);
201
+ activeCleanupIntervals.push(cleanupInterval);
169
202
  function isModelRateLimited(providerID, modelID) {
170
203
  const key = getModelKey(providerID, modelID);
171
204
  const limitedAt = rateLimitedModels.get(key);
@@ -183,29 +216,17 @@ export const RateLimitFallback = async ({ client, directory }) => {
183
216
  }
184
217
  function findNextAvailableModel(currentProviderID, currentModelID, attemptedModels) {
185
218
  const currentKey = getModelKey(currentProviderID, currentModelID);
186
- let startIndex = config.fallbackModels.findIndex(m => getModelKey(m.providerID, m.modelID) === currentKey);
187
- // If current model is not in the fallback list, search from the beginning
188
- if (startIndex === -1) {
189
- // Only search through all models once (first loop handles this)
190
- for (let i = 0; i < config.fallbackModels.length; i++) {
191
- const model = config.fallbackModels[i];
192
- const key = getModelKey(model.providerID, model.modelID);
193
- if (!attemptedModels.has(key) && !isModelRateLimited(model.providerID, model.modelID)) {
194
- return model;
195
- }
196
- }
197
- return null;
198
- }
199
- // Search for the next model after current position
200
- for (let i = startIndex + 1; i < config.fallbackModels.length; i++) {
219
+ const startIndex = config.fallbackModels.findIndex(m => getModelKey(m.providerID, m.modelID) === currentKey);
220
+ // If current model is not in the fallback list (startIndex is -1), start from 0
221
+ const searchStartIndex = Math.max(0, startIndex);
222
+ for (let i = searchStartIndex + 1; i < config.fallbackModels.length; i++) {
201
223
  const model = config.fallbackModels[i];
202
224
  const key = getModelKey(model.providerID, model.modelID);
203
225
  if (!attemptedModels.has(key) && !isModelRateLimited(model.providerID, model.modelID)) {
204
226
  return model;
205
227
  }
206
228
  }
207
- // Search from the beginning to current position (wrap around)
208
- for (let i = 0; i <= startIndex && i < config.fallbackModels.length; i++) {
229
+ for (let i = 0; i <= searchStartIndex && i < config.fallbackModels.length; i++) {
209
230
  const model = config.fallbackModels[i];
210
231
  const key = getModelKey(model.providerID, model.modelID);
211
232
  if (!attemptedModels.has(key) && !isModelRateLimited(model.providerID, model.modelID)) {
@@ -214,38 +235,80 @@ export const RateLimitFallback = async ({ client, directory }) => {
214
235
  }
215
236
  return null;
216
237
  }
217
- async function handleRateLimitFallback(sessionID, currentProviderID, currentModelID, skipAbort = false) {
238
+ async function handleRateLimitFallback(sessionID, currentProviderID, currentModelID) {
239
+ // Determine the target session ID for fallback processing
240
+ // For subagent sessions, trigger fallback at the root level (parent-centered approach)
241
+ let targetSessionID = sessionID;
218
242
  try {
219
- // Prevent duplicate fallback processing within 5 seconds
220
- const lastFallback = fallbackInProgress.get(sessionID);
221
- if (lastFallback && Date.now() - lastFallback < 5000) {
222
- return;
243
+ // Check if this is a subagent session
244
+ const hierarchy = getHierarchy(sessionID);
245
+ const rootSessionID = getRootSession(sessionID);
246
+ if (rootSessionID && hierarchy) {
247
+ targetSessionID = rootSessionID;
248
+ // If already processing fallback for this hierarchy, skip
249
+ const lastFallback = fallbackInProgress.get(targetSessionID);
250
+ if (lastFallback && Date.now() - lastFallback < DEDUP_WINDOW_MS) {
251
+ return;
252
+ }
253
+ fallbackInProgress.set(targetSessionID, Date.now());
254
+ // Update the shared fallback state
255
+ hierarchy.sharedFallbackState = "in_progress";
256
+ hierarchy.lastActivity = Date.now();
257
+ // Update the subagent's state
258
+ const subagent = hierarchy.subagents.get(sessionID);
259
+ if (subagent) {
260
+ subagent.fallbackState = "in_progress";
261
+ subagent.lastActivity = Date.now();
262
+ }
263
+ }
264
+ else {
265
+ // Prevent duplicate fallback processing within DEDUP_WINDOW_MS for non-subagent sessions
266
+ const lastFallback = fallbackInProgress.get(targetSessionID);
267
+ if (lastFallback && Date.now() - lastFallback < DEDUP_WINDOW_MS) {
268
+ return;
269
+ }
270
+ fallbackInProgress.set(targetSessionID, Date.now());
223
271
  }
224
- fallbackInProgress.set(sessionID, Date.now());
225
272
  // If no model info provided, try to get from tracked session model
226
273
  if (!currentProviderID || !currentModelID) {
227
- const tracked = currentSessionModel.get(sessionID);
274
+ const tracked = currentSessionModel.get(targetSessionID);
228
275
  if (tracked) {
229
276
  currentProviderID = tracked.providerID;
230
277
  currentModelID = tracked.modelID;
231
278
  }
232
279
  }
233
- // Fetch messages BEFORE abort — session must still be alive
234
- const messagesResult = await client.session.messages({ path: { id: sessionID } });
280
+ // Abort current session with error handling
281
+ try {
282
+ await client.session.abort({ path: { id: targetSessionID } });
283
+ }
284
+ catch (abortError) {
285
+ // Log abort error but continue with fallback
286
+ if (process.env.NODE_ENV === "development") {
287
+ console.warn("Failed to abort session:", abortError);
288
+ }
289
+ }
290
+ await client.tui.showToast({
291
+ body: {
292
+ title: "Rate Limit Detected",
293
+ message: `Switching from ${currentModelID || 'current model'}...`,
294
+ variant: "warning",
295
+ duration: 3000,
296
+ },
297
+ });
298
+ const messagesResult = await client.session.messages({ path: { id: targetSessionID } });
235
299
  if (!messagesResult.data) {
236
- fallbackInProgress.delete(sessionID);
300
+ fallbackInProgress.delete(targetSessionID);
237
301
  return;
238
302
  }
239
303
  const messages = messagesResult.data;
240
304
  const lastUserMessage = [...messages].reverse().find(m => m.info.role === "user");
241
305
  if (!lastUserMessage) {
242
- fallbackInProgress.delete(sessionID);
306
+ fallbackInProgress.delete(targetSessionID);
243
307
  return;
244
308
  }
245
- toast("Rate Limit Detected", `Switching from ${currentModelID || 'current model'}...`, "warning").catch(() => { });
246
309
  const stateKey = `${sessionID}:${lastUserMessage.info.id}`;
247
310
  let state = retryState.get(stateKey);
248
- if (!state || Date.now() - state.lastAttemptTime > 30000) {
311
+ if (!state || Date.now() - state.lastAttemptTime > STATE_TIMEOUT_MS) {
249
312
  state = { attemptedModels: new Set(), lastAttemptTime: Date.now() };
250
313
  retryState.set(stateKey, state);
251
314
  }
@@ -272,7 +335,14 @@ export const RateLimitFallback = async ({ client, directory }) => {
272
335
  if (!isLastModelCurrent && !isModelRateLimited(lastModel.providerID, lastModel.modelID)) {
273
336
  // Use the last model for one more try
274
337
  nextModel = lastModel;
275
- toast("Last Resort", `Trying ${lastModel.modelID} one more time...`, "warning").catch(() => { });
338
+ await client.tui.showToast({
339
+ body: {
340
+ title: "Last Resort",
341
+ message: `Trying ${lastModel.modelID} one more time...`,
342
+ variant: "warning",
343
+ duration: 3000,
344
+ },
345
+ });
276
346
  }
277
347
  else {
278
348
  // Last model also failed, reset for next prompt
@@ -287,218 +357,165 @@ export const RateLimitFallback = async ({ client, directory }) => {
287
357
  // "stop" mode: nextModel remains null, will show error below
288
358
  }
289
359
  if (!nextModel) {
290
- toast("No Fallback Available", config.fallbackMode === "stop"
291
- ? "All fallback models exhausted"
292
- : "All models are rate limited", "error").catch(() => { });
360
+ await client.tui.showToast({
361
+ body: {
362
+ title: "No Fallback Available",
363
+ message: config.fallbackMode === "stop"
364
+ ? "All fallback models exhausted"
365
+ : "All models are rate limited",
366
+ variant: "error",
367
+ duration: 5000,
368
+ },
369
+ });
293
370
  retryState.delete(stateKey);
294
- fallbackInProgress.delete(sessionID);
371
+ fallbackInProgress.delete(targetSessionID);
295
372
  return;
296
373
  }
297
374
  state.attemptedModels.add(getModelKey(nextModel.providerID, nextModel.modelID));
298
375
  state.lastAttemptTime = Date.now();
299
376
  const parts = lastUserMessage.parts
300
- .filter((p) => p.type === "text" || p.type === "file")
377
+ .filter((p) => {
378
+ const part = p;
379
+ return part.type === "text" || part.type === "file";
380
+ })
301
381
  .map((p) => {
302
- if (p.type === "text")
303
- return { type: "text", text: p.text };
304
- if (p.type === "file")
305
- return { type: "file", path: p.path, mediaType: p.mediaType };
382
+ const part = p;
383
+ if (part.type === "text")
384
+ return { type: "text", text: String(part.text) };
385
+ if (part.type === "file")
386
+ return { type: "file", path: String(part.path), mediaType: String(part.mediaType) };
306
387
  return null;
307
388
  })
308
- .filter(Boolean);
389
+ .filter((p) => p !== null);
309
390
  if (parts.length === 0) {
310
- fallbackInProgress.delete(sessionID);
391
+ fallbackInProgress.delete(targetSessionID);
311
392
  return;
312
393
  }
394
+ await client.tui.showToast({
395
+ body: {
396
+ title: "Retrying",
397
+ message: `Using ${nextModel.providerID}/${nextModel.modelID}`,
398
+ variant: "info",
399
+ duration: 3000,
400
+ },
401
+ });
313
402
  // Track the new model for this session
314
- currentSessionModel.set(sessionID, { providerID: nextModel.providerID, modelID: nextModel.modelID });
315
- const promptBody = {
316
- parts: parts,
317
- model: { providerID: nextModel.providerID, modelID: nextModel.modelID },
318
- };
319
- // abort promptAsync: abort the old request first, then send fallback prompt
320
- // This prevents the abort from killing the new fallback prompt.
321
- //
322
- // For session.error events, the request is already in error state, so we
323
- // only send promptAsync (skipAbort = true). For retry status, we send both.
324
- //
325
- // promptAsync: HTTP POST /session/{id}/prompt_async → 204 (SDK sdk.gen.js).
326
- const t0 = Date.now();
327
- if (!skipAbort) {
328
- try {
329
- await client.session.abort({ path: { id: sessionID } });
330
- logToFile(`abort succeeded for session ${sessionID} (${Date.now() - t0}ms)`);
331
- }
332
- catch (abortErr) {
333
- logToFile(`abort failed (${Date.now() - t0}ms): ${abortErr}`);
403
+ currentSessionModel.set(targetSessionID, {
404
+ providerID: nextModel.providerID,
405
+ modelID: nextModel.modelID,
406
+ lastUpdated: Date.now(),
407
+ });
408
+ // If this is a root session with subagents, propagate the model to all subagents
409
+ if (hierarchy && hierarchy.rootSessionID === targetSessionID) {
410
+ hierarchy.sharedFallbackState = "completed";
411
+ hierarchy.lastActivity = Date.now();
412
+ // Update model tracking for all subagents
413
+ for (const [subagentID, subagent] of hierarchy.subagents.entries()) {
414
+ currentSessionModel.set(subagentID, {
415
+ providerID: nextModel.providerID,
416
+ modelID: nextModel.modelID,
417
+ lastUpdated: Date.now(),
418
+ });
419
+ subagent.fallbackState = "completed";
420
+ subagent.lastActivity = Date.now();
334
421
  }
335
422
  }
336
- const t1 = Date.now();
337
- await client.session.promptAsync({
338
- path: { id: sessionID },
339
- body: promptBody,
423
+ // Convert internal MessagePart to SDK-compatible format
424
+ const sdkParts = parts.map((part) => {
425
+ if (part.type === "text") {
426
+ return { type: "text", text: part.text };
427
+ }
428
+ // For file parts, we need to match the FilePartInput format
429
+ // Using path as url since we're dealing with local files
430
+ return {
431
+ type: "file",
432
+ url: part.path,
433
+ mime: part.mediaType || "application/octet-stream",
434
+ };
435
+ });
436
+ await client.session.prompt({
437
+ path: { id: targetSessionID },
438
+ body: {
439
+ parts: sdkParts,
440
+ model: { providerID: nextModel.providerID, modelID: nextModel.modelID },
441
+ },
442
+ });
443
+ await client.tui.showToast({
444
+ body: {
445
+ title: "Fallback Successful",
446
+ message: `Now using ${nextModel.modelID}`,
447
+ variant: "success",
448
+ duration: 3000,
449
+ },
340
450
  });
341
- logToFile(`promptAsync completed for session ${sessionID} (${Date.now() - t1}ms, total ${Date.now() - t0}ms) with model ${nextModel.providerID}/${nextModel.modelID}`);
342
- // Toast is best-effort notification. The toast() function (line ~185) has
343
- // built-in fallback: showToast failure → app.log. After promptAsync the
344
- // server may already be disposing, so both showToast and app.log could fail.
345
- // The outer .catch() ensures even total toast() failure never blocks or throws.
346
- toast("Fallback Active", `Now using ${nextModel.modelID}`, "success").catch(() => { });
347
451
  retryState.delete(stateKey);
348
- // Clear fallback flag to allow next fallback if needed
349
- fallbackInProgress.delete(sessionID);
452
+ // Explicitly clean up fallbackInProgress after cooldown period
453
+ // This prevents memory leaks while maintaining the deduplication window
454
+ setTimeout(() => {
455
+ fallbackInProgress.delete(targetSessionID);
456
+ }, DEDUP_WINDOW_MS);
350
457
  }
351
458
  catch (err) {
352
- logToFile(`handleRateLimitFallback error: ${err}`);
353
- // Fallback failed, clear the flag
354
- fallbackInProgress.delete(sessionID);
459
+ fallbackInProgress.delete(targetSessionID);
460
+ if (process.env.NODE_ENV === "development") {
461
+ console.error("Fallback failed:", err);
462
+ }
355
463
  }
356
464
  }
357
465
  return {
358
466
  event: async ({ event }) => {
359
- // Log events to debug plugin execution
360
- logToFile(`Event received: ${event.type}`);
361
- try {
362
- await client.app.log({
363
- body: {
364
- service: "rate-limit-fallback",
365
- level: "debug",
366
- message: `Event received: ${event.type}`,
367
- },
368
- });
369
- }
370
- catch (e) {
371
- logToFile(`client.app.log failed for event: ${e}`);
372
- console.log("[rate-limit-fallback] Event received:", event.type);
373
- }
374
- if (event.type === "session.error") {
467
+ if (isSessionErrorEvent(event)) {
375
468
  const { sessionID, error } = event.properties;
376
- logToFile(`session.error: sessionID=${sessionID}, error=${JSON.stringify(error)}`);
377
- try {
378
- await client.app.log({
379
- body: {
380
- service: "rate-limit-fallback",
381
- level: "debug",
382
- message: `session.error: sessionID=${sessionID}, error=${JSON.stringify(error)}`,
383
- },
384
- });
385
- }
386
- catch {
387
- console.log("[rate-limit-fallback] session.error:", sessionID, error);
388
- }
389
469
  if (sessionID && error && isRateLimitError(error)) {
390
- logToFile("Rate limit error detected, attempting fallback");
391
- try {
392
- await client.app.log({
393
- body: {
394
- service: "rate-limit-fallback",
395
- level: "info",
396
- message: "Rate limit error detected, attempting fallback",
397
- },
398
- });
399
- }
400
- catch {
401
- console.log("[rate-limit-fallback] Rate limit error detected, attempting fallback");
402
- }
403
- await handleRateLimitFallback(sessionID, "", "", true); // skipAbort = true (already in error state)
470
+ await handleRateLimitFallback(sessionID, "", "");
404
471
  }
405
472
  }
406
- if (event.type === "message.updated") {
407
- const info = event.properties?.info;
408
- logToFile(`message.updated: ${JSON.stringify(info)}`);
409
- try {
410
- await client.app.log({
411
- body: {
412
- service: "rate-limit-fallback",
413
- level: "debug",
414
- message: `message.updated: ${JSON.stringify(info)}`,
415
- },
416
- });
417
- }
418
- catch {
419
- console.log("[rate-limit-fallback] message.updated:", info);
420
- }
421
- // Track assistant message model info for later use in fallback
422
- if (info?.role === "assistant" && info?.sessionID && info?.providerID && info?.modelID) {
423
- currentSessionModel.set(info.sessionID, {
424
- providerID: info.providerID,
425
- modelID: info.modelID,
426
- });
427
- }
473
+ if (isMessageUpdatedEvent(event)) {
474
+ const info = event.properties.info;
428
475
  if (info?.error && isRateLimitError(info.error)) {
429
- logToFile("Rate limit error in message, attempting fallback");
430
- try {
431
- await client.app.log({
432
- body: {
433
- service: "rate-limit-fallback",
434
- level: "info",
435
- message: "Rate limit error in message, attempting fallback",
436
- },
437
- });
438
- }
439
- catch {
440
- console.log("[rate-limit-fallback] Rate limit error in message, attempting fallback");
441
- }
442
- await handleRateLimitFallback(info.sessionID, info.providerID || "", info.modelID || "", true); // skipAbort = true (already in error state)
476
+ await handleRateLimitFallback(info.sessionID, info.providerID || "", info.modelID || "");
443
477
  }
444
478
  }
445
- if (event.type === "session.status") {
479
+ if (isSessionStatusEvent(event)) {
446
480
  const props = event.properties;
447
481
  const status = props?.status;
448
- logToFile(`session.status: ${JSON.stringify(status)}`);
449
- try {
450
- await client.app.log({
451
- body: {
452
- service: "rate-limit-fallback",
453
- level: "debug",
454
- message: `session.status: ${JSON.stringify(status)}`,
455
- },
456
- });
457
- }
458
- catch {
459
- console.log("[rate-limit-fallback] session.status:", status);
460
- }
461
- // Note: interrupted status is handled by the fallback sequence (abort → promptAsync).
462
- // We don't need to handle it separately here as it would cause duplicate prompt sends.
463
482
  if (status?.type === "retry" && status?.message) {
464
483
  const message = status.message.toLowerCase();
465
484
  const isRateLimitRetry = message.includes("usage limit") ||
466
485
  message.includes("rate limit") ||
467
486
  message.includes("high concurrency") ||
468
487
  message.includes("reduce concurrency");
469
- logToFile(`Is rate limit retry: ${isRateLimitRetry}, message: ${message}`);
470
- try {
471
- await client.app.log({
472
- body: {
473
- service: "rate-limit-fallback",
474
- level: "debug",
475
- message: `Is rate limit retry: ${isRateLimitRetry}, message: ${message}`,
476
- },
477
- });
478
- }
479
- catch {
480
- console.log("[rate-limit-fallback] Is rate limit retry:", isRateLimitRetry, "message:", message);
481
- }
482
488
  if (isRateLimitRetry) {
483
489
  // Try fallback on any attempt, handleRateLimitFallback will manage state
484
- logToFile("Attempting fallback for rate limit retry");
485
- try {
486
- await client.app.log({
487
- body: {
488
- service: "rate-limit-fallback",
489
- level: "info",
490
- message: "Attempting fallback for rate limit retry",
491
- },
492
- });
493
- }
494
- catch {
495
- console.log("[rate-limit-fallback] Attempting fallback for rate limit retry");
496
- }
497
490
  await handleRateLimitFallback(props.sessionID, "", "");
498
491
  }
499
492
  }
500
493
  }
494
+ // Handle subagent session creation events
495
+ // Note: Using type assertion for subagent events since they may not be in the official Event union yet
496
+ const rawEvent = event;
497
+ if (isSubagentSessionCreatedEvent(rawEvent)) {
498
+ const { sessionID, parentSessionID } = rawEvent.properties;
499
+ if (config.enableSubagentFallback !== false) {
500
+ registerSubagent(sessionID, parentSessionID);
501
+ if (process.env.NODE_ENV === "development") {
502
+ console.log(`Registered subagent ${sessionID} under parent ${parentSessionID}`);
503
+ }
504
+ }
505
+ }
506
+ },
507
+ // Cleanup function to prevent memory leaks
508
+ cleanup: () => {
509
+ clearInterval(cleanupInterval);
510
+ const index = activeCleanupIntervals.indexOf(cleanupInterval);
511
+ if (index > -1) {
512
+ activeCleanupIntervals.splice(index, 1);
513
+ }
514
+ // Clean up all session hierarchies
515
+ sessionHierarchies.clear();
516
+ sessionToRootMap.clear();
501
517
  },
502
518
  };
503
519
  };
504
520
  export default RateLimitFallback;
521
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,10 @@
1
1
  {
2
2
  "name": "@azumag/opencode-rate-limit-fallback",
3
- "version": "1.0.21",
3
+ "version": "1.1.2",
4
4
  "description": "OpenCode plugin that automatically switches to fallback models when rate limited",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
5
7
  "type": "module",
6
- "main": "./dist/index.js",
7
- "scripts": {
8
- "build": "tsc",
9
- "test": "vitest",
10
- "prepublishOnly": "npm run build"
11
- },
12
8
  "keywords": [
13
9
  "opencode",
14
10
  "plugin",
@@ -26,22 +22,27 @@
26
22
  "url": "https://github.com/azumag/opencode-rate-limit-fallback/issues"
27
23
  },
28
24
  "homepage": "https://github.com/azumag/opencode-rate-limit-fallback#readme",
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "typecheck": "tsc --noEmit",
28
+ "clean": "rm -rf dist",
29
+ "prepublishOnly": "npm run clean && npm run build",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "test:coverage": "vitest run --coverage"
33
+ },
29
34
  "files": [
30
- "dist"
35
+ "dist",
36
+ "README.md",
37
+ "LICENSE"
31
38
  ],
32
- "exports": {
33
- ".": {
34
- "import": "./dist/index.js",
35
- "types": "./dist/index.d.ts"
36
- }
39
+ "devDependencies": {
40
+ "@types/node": "^25.2.2",
41
+ "@vitest/coverage-v8": "^4.0.18",
42
+ "typescript": "^5.3.0",
43
+ "vitest": "^4.0.18"
37
44
  },
38
45
  "dependencies": {
39
46
  "@opencode-ai/plugin": "latest"
40
- },
41
- "devDependencies": {
42
- "@tsconfig/node22": "^22.0.5",
43
- "@types/node": "^25.2.2",
44
- "typescript": "^5.9.3",
45
- "vitest": "^3.2.4"
46
47
  }
47
48
  }