@azumag/opencode-rate-limit-fallback 1.0.22 → 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/README.md CHANGED
@@ -89,14 +89,16 @@ If no configuration is provided, the following models are used:
89
89
 
90
90
  ## How It Works
91
91
 
92
- 1. **Detection**: The plugin listens for rate limit errors via:
93
- - `session.error` events
94
- - `message.updated` events with errors
95
- - `session.status` events with `type: "retry"`
92
+ 1. **Detection**: The plugin listens for rate limit errors via:
93
+ - `session.error` events
94
+ - `message.updated` events with errors
95
+ - `session.status` events with `type: "retry"`
96
96
 
97
- 2. **Fallback**: The plugin selects the next available model from the fallback list and resends the last user message using the `promptAsync` API.
97
+ 2. **Abort**: When a rate limit is detected, the current session is aborted to stop OpenCode's internal retry mechanism.
98
98
 
99
- 3. **Cooldown**: Rate-limited models are tracked and skipped for the configured cooldown period.
99
+ 3. **Fallback**: The plugin selects the next available model from the fallback list and resends the last user message.
100
+
101
+ 4. **Cooldown**: Rate-limited models are tracked and skipped for the configured cooldown period.
100
102
 
101
103
  ## License
102
104
 
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,221 +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
- // promptAsync: HTTP POST /session/{id}/prompt_async 204 (SDK sdk.gen.js).
320
- //
321
- // For session.error and retry status events, we skip abort (skipAbort = true)
322
- // because the request is already in an error or delayed state. Calling abort
323
- // in these cases can trigger interrupted events that cancel the new fallback
324
- // prompt.
325
- const t0 = Date.now();
326
- if (!skipAbort) {
327
- try {
328
- await client.session.abort({ path: { id: sessionID } });
329
- logToFile(`abort succeeded for session ${sessionID} (${Date.now() - t0}ms)`);
330
- }
331
- catch (abortErr) {
332
- 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();
333
421
  }
334
422
  }
335
- const t1 = Date.now();
336
- await client.session.promptAsync({
337
- path: { id: sessionID },
338
- 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
+ },
339
450
  });
340
- logToFile(`promptAsync completed for session ${sessionID} (${Date.now() - t1}ms, total ${Date.now() - t0}ms) with model ${nextModel.providerID}/${nextModel.modelID}`);
341
- // Toast is best-effort notification. The toast() function (line ~185) has
342
- // built-in fallback: showToast failure → app.log. After promptAsync the
343
- // server may already be disposing, so both showToast and app.log could fail.
344
- // The outer .catch() ensures even total toast() failure never blocks or throws.
345
- toast("Fallback Active", `Now using ${nextModel.modelID}`, "success").catch(() => { });
346
451
  retryState.delete(stateKey);
347
- // Clear fallback flag to allow next fallback if needed
348
- 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);
349
457
  }
350
458
  catch (err) {
351
- logToFile(`handleRateLimitFallback error: ${err}`);
352
- // Fallback failed, clear the flag
353
- fallbackInProgress.delete(sessionID);
459
+ fallbackInProgress.delete(targetSessionID);
460
+ if (process.env.NODE_ENV === "development") {
461
+ console.error("Fallback failed:", err);
462
+ }
354
463
  }
355
464
  }
356
465
  return {
357
466
  event: async ({ event }) => {
358
- // Log events to debug plugin execution
359
- logToFile(`Event received: ${event.type}`);
360
- try {
361
- await client.app.log({
362
- body: {
363
- service: "rate-limit-fallback",
364
- level: "debug",
365
- message: `Event received: ${event.type}`,
366
- },
367
- });
368
- }
369
- catch (e) {
370
- logToFile(`client.app.log failed for event: ${e}`);
371
- console.log("[rate-limit-fallback] Event received:", event.type);
372
- }
373
- if (event.type === "session.error") {
467
+ if (isSessionErrorEvent(event)) {
374
468
  const { sessionID, error } = event.properties;
375
- logToFile(`session.error: sessionID=${sessionID}, error=${JSON.stringify(error)}`);
376
- try {
377
- await client.app.log({
378
- body: {
379
- service: "rate-limit-fallback",
380
- level: "debug",
381
- message: `session.error: sessionID=${sessionID}, error=${JSON.stringify(error)}`,
382
- },
383
- });
384
- }
385
- catch {
386
- console.log("[rate-limit-fallback] session.error:", sessionID, error);
387
- }
388
469
  if (sessionID && error && isRateLimitError(error)) {
389
- logToFile("Rate limit error detected, attempting fallback");
390
- try {
391
- await client.app.log({
392
- body: {
393
- service: "rate-limit-fallback",
394
- level: "info",
395
- message: "Rate limit error detected, attempting fallback",
396
- },
397
- });
398
- }
399
- catch {
400
- console.log("[rate-limit-fallback] Rate limit error detected, attempting fallback");
401
- }
402
- await handleRateLimitFallback(sessionID, "", "", true); // skipAbort = true (already in error state)
470
+ await handleRateLimitFallback(sessionID, "", "");
403
471
  }
404
472
  }
405
- if (event.type === "message.updated") {
406
- const info = event.properties?.info;
407
- logToFile(`message.updated: ${JSON.stringify(info)}`);
408
- try {
409
- await client.app.log({
410
- body: {
411
- service: "rate-limit-fallback",
412
- level: "debug",
413
- message: `message.updated: ${JSON.stringify(info)}`,
414
- },
415
- });
416
- }
417
- catch {
418
- console.log("[rate-limit-fallback] message.updated:", info);
419
- }
420
- // Track assistant message model info for later use in fallback
421
- if (info?.role === "assistant" && info?.sessionID && info?.providerID && info?.modelID) {
422
- currentSessionModel.set(info.sessionID, {
423
- providerID: info.providerID,
424
- modelID: info.modelID,
425
- });
426
- }
473
+ if (isMessageUpdatedEvent(event)) {
474
+ const info = event.properties.info;
427
475
  if (info?.error && isRateLimitError(info.error)) {
428
- logToFile("Rate limit error in message, attempting fallback");
429
- try {
430
- await client.app.log({
431
- body: {
432
- service: "rate-limit-fallback",
433
- level: "info",
434
- message: "Rate limit error in message, attempting fallback",
435
- },
436
- });
437
- }
438
- catch {
439
- console.log("[rate-limit-fallback] Rate limit error in message, attempting fallback");
440
- }
441
- await handleRateLimitFallback(info.sessionID, info.providerID || "", info.modelID || "", true); // skipAbort = true (already in error state)
476
+ await handleRateLimitFallback(info.sessionID, info.providerID || "", info.modelID || "");
442
477
  }
443
478
  }
444
- if (event.type === "session.status") {
479
+ if (isSessionStatusEvent(event)) {
445
480
  const props = event.properties;
446
481
  const status = props?.status;
447
- logToFile(`session.status: ${JSON.stringify(status)}`);
448
- try {
449
- await client.app.log({
450
- body: {
451
- service: "rate-limit-fallback",
452
- level: "debug",
453
- message: `session.status: ${JSON.stringify(status)}`,
454
- },
455
- });
456
- }
457
- catch {
458
- console.log("[rate-limit-fallback] session.status:", status);
459
- }
460
- // Note: interrupted status is ignored here. Since we no longer call abort
461
- // for retry status events, interrupted events should not be triggered by
462
- // our fallback logic. If they occur (e.g., from user action), we let them
463
- // be handled by the system.
464
482
  if (status?.type === "retry" && status?.message) {
465
483
  const message = status.message.toLowerCase();
466
484
  const isRateLimitRetry = message.includes("usage limit") ||
467
485
  message.includes("rate limit") ||
468
486
  message.includes("high concurrency") ||
469
487
  message.includes("reduce concurrency");
470
- logToFile(`Is rate limit retry: ${isRateLimitRetry}, message: ${message}`);
471
- try {
472
- await client.app.log({
473
- body: {
474
- service: "rate-limit-fallback",
475
- level: "debug",
476
- message: `Is rate limit retry: ${isRateLimitRetry}, message: ${message}`,
477
- },
478
- });
479
- }
480
- catch {
481
- console.log("[rate-limit-fallback] Is rate limit retry:", isRateLimitRetry, "message:", message);
482
- }
483
488
  if (isRateLimitRetry) {
484
489
  // Try fallback on any attempt, handleRateLimitFallback will manage state
485
- logToFile("Attempting fallback for rate limit retry");
486
- try {
487
- await client.app.log({
488
- body: {
489
- service: "rate-limit-fallback",
490
- level: "info",
491
- message: "Attempting fallback for rate limit retry",
492
- },
493
- });
494
- }
495
- catch {
496
- console.log("[rate-limit-fallback] Attempting fallback for rate limit retry");
497
- }
498
- // skipAbort = true for retry status to avoid triggering interrupted events
499
- // that could cancel the new fallback prompt
500
- await handleRateLimitFallback(props.sessionID, "", "", true);
490
+ await handleRateLimitFallback(props.sessionID, "", "");
501
491
  }
502
492
  }
503
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();
504
517
  },
505
518
  };
506
519
  };
507
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.22",
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
  }