@azumag/opencode-rate-limit-fallback 1.0.22 → 1.1.4

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
@@ -13,6 +13,8 @@ OpenCode plugin that automatically switches to fallback models when rate limited
13
13
  - Session model tracking for sequential fallback across multiple rate limits
14
14
  - Cooldown period to prevent immediate retry on rate-limited models
15
15
  - Toast notifications for user feedback
16
+ - Subagent session support with automatic fallback propagation to parent sessions
17
+ - Configurable maximum subagent nesting depth
16
18
 
17
19
  ## Installation
18
20
 
@@ -54,6 +56,8 @@ Create a configuration file at one of these locations:
54
56
  "enabled": true,
55
57
  "cooldownMs": 60000,
56
58
  "fallbackMode": "cycle",
59
+ "maxSubagentDepth": 10,
60
+ "enableSubagentFallback": true,
57
61
  "fallbackModels": [
58
62
  { "providerID": "anthropic", "modelID": "claude-sonnet-4-20250514" },
59
63
  { "providerID": "google", "modelID": "gemini-2.5-pro" },
@@ -70,6 +74,8 @@ Create a configuration file at one of these locations:
70
74
  | `cooldownMs` | number | `60000` | Cooldown period (ms) before retrying a rate-limited model |
71
75
  | `fallbackMode` | string | `"cycle"` | Behavior when all models are exhausted (see below) |
72
76
  | `fallbackModels` | array | See below | List of fallback models in priority order |
77
+ | `maxSubagentDepth` | number | `10` | Maximum nesting depth for subagent hierarchies |
78
+ | `enableSubagentFallback` | boolean | `true` | Enable/disable fallback for subagent sessions |
73
79
 
74
80
  ### Fallback Modes
75
81
 
@@ -89,14 +95,32 @@ If no configuration is provided, the following models are used:
89
95
 
90
96
  ## How It Works
91
97
 
92
- 1. **Detection**: The plugin listens for rate limit errors via:
98
+ 1. **Detection**: The plugin listens for rate limit errors via:
93
99
  - `session.error` events
94
100
  - `message.updated` events with errors
95
101
  - `session.status` events with `type: "retry"`
96
102
 
97
- 2. **Fallback**: The plugin selects the next available model from the fallback list and resends the last user message using the `promptAsync` API.
103
+ 2. **Abort**: When a rate limit is detected, the current session is aborted to stop OpenCode's internal retry mechanism.
98
104
 
99
- 3. **Cooldown**: Rate-limited models are tracked and skipped for the configured cooldown period.
105
+ 3. **Fallback**: The plugin selects the next available model from the fallback list and resends the last user message.
106
+
107
+ 4. **Cooldown**: Rate-limited models are tracked and skipped for the configured cooldown period.
108
+
109
+ ## Subagent Support
110
+
111
+ When OpenCode uses subagents (e.g., for complex tasks requiring specialized agents):
112
+
113
+ - **Automatic Detection**: The plugin detects `subagent.session.created` events
114
+ - **Hierarchy Tracking**: Maintains parent-child relationships between sessions
115
+ - **Fallback Propagation**: When a subagent hits a rate limit, the fallback is triggered at the root session level
116
+ - **Model Sharing**: All subagents in a hierarchy share the same fallback model
117
+
118
+ ### Subagent Configuration
119
+
120
+ | Option | Type | Default | Description |
121
+ |--------|------|---------|-------------|
122
+ | `maxSubagentDepth` | number | `10` | Maximum nesting depth for subagent hierarchies |
123
+ | `enableSubagentFallback` | boolean | `true` | Enable/disable fallback for subagent sessions |
100
124
 
101
125
  ## License
102
126
 
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;AA2NlD,eAAO,MAAM,iBAAiB,EAAE,MAic/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,15 @@ 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
+ // Silently ignore config load errors
40
78
  }
41
79
  }
42
80
  }
@@ -46,126 +84,116 @@ function getModelKey(providerID, modelID) {
46
84
  return `${providerID}/${modelID}`;
47
85
  }
48
86
  function isRateLimitError(error) {
49
- if (!error)
87
+ if (!error || typeof error !== "object")
50
88
  return false;
51
- if (error.name === "APIError" && error.data?.statusCode === 429) {
89
+ // More type-safe error object structure
90
+ const err = error;
91
+ // Check for 429 status code in APIError
92
+ if (err.name === "APIError" && err.data?.statusCode === 429) {
52
93
  return true;
53
94
  }
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) ||
95
+ // Type-safe access to error fields
96
+ const responseBody = String(err.data?.responseBody || "").toLowerCase();
97
+ const message = String(err.data?.message || err.message || "").toLowerCase();
98
+ const errorName = String(err.name || "").toLowerCase();
99
+ return RATE_LIMIT_INDICATORS.some((indicator) => responseBody.includes(indicator) ||
71
100
  message.includes(indicator) ||
72
101
  errorName.includes(indicator));
73
102
  }
103
+ // Constants for deduplication and state management
104
+ const DEDUP_WINDOW_MS = 5000;
105
+ const STATE_TIMEOUT_MS = 30000;
106
+ const CLEANUP_INTERVAL_MS = 300000; // 5 minutes
107
+ const SESSION_ENTRY_TTL_MS = 3600000; // 1 hour
108
+ // Track cleanup intervals globally to prevent TypeScript warnings
109
+ const activeCleanupIntervals = [];
74
110
  export const RateLimitFallback = async ({ client, directory }) => {
75
111
  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
112
  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
113
  return {};
120
114
  }
121
115
  const rateLimitedModels = new Map();
122
116
  const retryState = new Map();
123
117
  const currentSessionModel = new Map();
124
118
  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",
119
+ // Subagent session tracking
120
+ const sessionHierarchies = new Map(); // rootSessionID -> SessionHierarchy
121
+ const sessionToRootMap = new Map(); // sessionID -> rootSessionID
122
+ const maxSubagentDepth = config.maxSubagentDepth ?? 10;
123
+ // Helper functions for session hierarchy management
124
+ function getOrCreateHierarchy(rootSessionID) {
125
+ let hierarchy = sessionHierarchies.get(rootSessionID);
126
+ if (!hierarchy) {
127
+ hierarchy = {
128
+ rootSessionID,
129
+ subagents: new Map(),
130
+ sharedFallbackState: "none",
131
+ sharedConfig: config,
132
+ createdAt: Date.now(),
133
+ lastActivity: Date.now(),
137
134
  };
138
- await client.app.log({
139
- body: {
140
- service: "rate-limit-fallback",
141
- level: variantMap[variant],
142
- message,
143
- },
144
- });
135
+ sessionHierarchies.set(rootSessionID, hierarchy);
136
+ sessionToRootMap.set(rootSessionID, rootSessionID);
145
137
  }
138
+ return hierarchy;
146
139
  }
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
- });
140
+ function registerSubagent(sessionID, parentSessionID) {
141
+ // Validate parent session exists
142
+ // Parent session must either be registered in sessionToRootMap or be a new root session
143
+ const parentRootSessionID = sessionToRootMap.get(parentSessionID);
144
+ // Determine root session - if parent doesn't exist, treat it as a new root
145
+ const rootSessionID = parentRootSessionID || parentSessionID;
146
+ // If parent is not a subagent but we're treating it as a root, create a hierarchy for it
147
+ // This allows sessions to become roots when their first subagent is registered
148
+ const hierarchy = getOrCreateHierarchy(rootSessionID);
149
+ const parentSubagent = hierarchy.subagents.get(parentSessionID);
150
+ const depth = parentSubagent ? parentSubagent.depth + 1 : 1;
151
+ // Enforce max depth
152
+ if (depth > maxSubagentDepth) {
153
+ return false;
167
154
  }
155
+ const subagent = {
156
+ sessionID,
157
+ parentSessionID,
158
+ depth,
159
+ fallbackState: "none",
160
+ createdAt: Date.now(),
161
+ lastActivity: Date.now(),
162
+ };
163
+ hierarchy.subagents.set(sessionID, subagent);
164
+ sessionToRootMap.set(sessionID, rootSessionID);
165
+ hierarchy.lastActivity = Date.now();
166
+ return true;
167
+ }
168
+ function getRootSession(sessionID) {
169
+ return sessionToRootMap.get(sessionID) || null;
168
170
  }
171
+ function getHierarchy(sessionID) {
172
+ const rootSessionID = getRootSession(sessionID);
173
+ return rootSessionID ? sessionHierarchies.get(rootSessionID) || null : null;
174
+ }
175
+ // Cleanup stale session model entries (every 5 minutes)
176
+ const cleanupInterval = setInterval(() => {
177
+ const now = Date.now();
178
+ for (const [sessionID, entry] of currentSessionModel.entries()) {
179
+ // Remove entries older than 1 hour
180
+ if (now - entry.lastUpdated > SESSION_ENTRY_TTL_MS) {
181
+ currentSessionModel.delete(sessionID);
182
+ }
183
+ }
184
+ // Clean up stale session hierarchies
185
+ for (const [rootSessionID, hierarchy] of sessionHierarchies.entries()) {
186
+ if (now - hierarchy.lastActivity > SESSION_ENTRY_TTL_MS) {
187
+ // Clean up all subagents in this hierarchy
188
+ for (const subagentID of hierarchy.subagents.keys()) {
189
+ sessionToRootMap.delete(subagentID);
190
+ }
191
+ sessionHierarchies.delete(rootSessionID);
192
+ sessionToRootMap.delete(rootSessionID);
193
+ }
194
+ }
195
+ }, CLEANUP_INTERVAL_MS);
196
+ activeCleanupIntervals.push(cleanupInterval);
169
197
  function isModelRateLimited(providerID, modelID) {
170
198
  const key = getModelKey(providerID, modelID);
171
199
  const limitedAt = rateLimitedModels.get(key);
@@ -183,29 +211,17 @@ export const RateLimitFallback = async ({ client, directory }) => {
183
211
  }
184
212
  function findNextAvailableModel(currentProviderID, currentModelID, attemptedModels) {
185
213
  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++) {
214
+ const startIndex = config.fallbackModels.findIndex(m => getModelKey(m.providerID, m.modelID) === currentKey);
215
+ // If current model is not in the fallback list (startIndex is -1), start from 0
216
+ const searchStartIndex = Math.max(0, startIndex);
217
+ for (let i = searchStartIndex + 1; i < config.fallbackModels.length; i++) {
201
218
  const model = config.fallbackModels[i];
202
219
  const key = getModelKey(model.providerID, model.modelID);
203
220
  if (!attemptedModels.has(key) && !isModelRateLimited(model.providerID, model.modelID)) {
204
221
  return model;
205
222
  }
206
223
  }
207
- // Search from the beginning to current position (wrap around)
208
- for (let i = 0; i <= startIndex && i < config.fallbackModels.length; i++) {
224
+ for (let i = 0; i <= searchStartIndex && i < config.fallbackModels.length; i++) {
209
225
  const model = config.fallbackModels[i];
210
226
  const key = getModelKey(model.providerID, model.modelID);
211
227
  if (!attemptedModels.has(key) && !isModelRateLimited(model.providerID, model.modelID)) {
@@ -214,38 +230,77 @@ export const RateLimitFallback = async ({ client, directory }) => {
214
230
  }
215
231
  return null;
216
232
  }
217
- async function handleRateLimitFallback(sessionID, currentProviderID, currentModelID, skipAbort = false) {
233
+ async function handleRateLimitFallback(sessionID, currentProviderID, currentModelID) {
234
+ // Determine the target session ID for fallback processing
235
+ // For subagent sessions, trigger fallback at the root level (parent-centered approach)
236
+ let targetSessionID = sessionID;
218
237
  try {
219
- // Prevent duplicate fallback processing within 5 seconds
220
- const lastFallback = fallbackInProgress.get(sessionID);
221
- if (lastFallback && Date.now() - lastFallback < 5000) {
222
- return;
238
+ // Check if this is a subagent session
239
+ const hierarchy = getHierarchy(sessionID);
240
+ const rootSessionID = getRootSession(sessionID);
241
+ if (rootSessionID && hierarchy) {
242
+ targetSessionID = rootSessionID;
243
+ // If already processing fallback for this hierarchy, skip
244
+ const lastFallback = fallbackInProgress.get(targetSessionID);
245
+ if (lastFallback && Date.now() - lastFallback < DEDUP_WINDOW_MS) {
246
+ return;
247
+ }
248
+ fallbackInProgress.set(targetSessionID, Date.now());
249
+ // Update the shared fallback state
250
+ hierarchy.sharedFallbackState = "in_progress";
251
+ hierarchy.lastActivity = Date.now();
252
+ // Update the subagent's state
253
+ const subagent = hierarchy.subagents.get(sessionID);
254
+ if (subagent) {
255
+ subagent.fallbackState = "in_progress";
256
+ subagent.lastActivity = Date.now();
257
+ }
258
+ }
259
+ else {
260
+ // Prevent duplicate fallback processing within DEDUP_WINDOW_MS for non-subagent sessions
261
+ const lastFallback = fallbackInProgress.get(targetSessionID);
262
+ if (lastFallback && Date.now() - lastFallback < DEDUP_WINDOW_MS) {
263
+ return;
264
+ }
265
+ fallbackInProgress.set(targetSessionID, Date.now());
223
266
  }
224
- fallbackInProgress.set(sessionID, Date.now());
225
267
  // If no model info provided, try to get from tracked session model
226
268
  if (!currentProviderID || !currentModelID) {
227
- const tracked = currentSessionModel.get(sessionID);
269
+ const tracked = currentSessionModel.get(targetSessionID);
228
270
  if (tracked) {
229
271
  currentProviderID = tracked.providerID;
230
272
  currentModelID = tracked.modelID;
231
273
  }
232
274
  }
233
- // Fetch messages BEFORE abort — session must still be alive
234
- const messagesResult = await client.session.messages({ path: { id: sessionID } });
275
+ // Abort current session with error handling
276
+ try {
277
+ await client.session.abort({ path: { id: targetSessionID } });
278
+ }
279
+ catch (abortError) {
280
+ // Silently ignore abort errors and continue with fallback
281
+ }
282
+ await client.tui.showToast({
283
+ body: {
284
+ title: "Rate Limit Detected",
285
+ message: `Switching from ${currentModelID || 'current model'}...`,
286
+ variant: "warning",
287
+ duration: 3000,
288
+ },
289
+ });
290
+ const messagesResult = await client.session.messages({ path: { id: targetSessionID } });
235
291
  if (!messagesResult.data) {
236
- fallbackInProgress.delete(sessionID);
292
+ fallbackInProgress.delete(targetSessionID);
237
293
  return;
238
294
  }
239
295
  const messages = messagesResult.data;
240
296
  const lastUserMessage = [...messages].reverse().find(m => m.info.role === "user");
241
297
  if (!lastUserMessage) {
242
- fallbackInProgress.delete(sessionID);
298
+ fallbackInProgress.delete(targetSessionID);
243
299
  return;
244
300
  }
245
- toast("Rate Limit Detected", `Switching from ${currentModelID || 'current model'}...`, "warning").catch(() => { });
246
301
  const stateKey = `${sessionID}:${lastUserMessage.info.id}`;
247
302
  let state = retryState.get(stateKey);
248
- if (!state || Date.now() - state.lastAttemptTime > 30000) {
303
+ if (!state || Date.now() - state.lastAttemptTime > STATE_TIMEOUT_MS) {
249
304
  state = { attemptedModels: new Set(), lastAttemptTime: Date.now() };
250
305
  retryState.set(stateKey, state);
251
306
  }
@@ -272,7 +327,14 @@ export const RateLimitFallback = async ({ client, directory }) => {
272
327
  if (!isLastModelCurrent && !isModelRateLimited(lastModel.providerID, lastModel.modelID)) {
273
328
  // Use the last model for one more try
274
329
  nextModel = lastModel;
275
- toast("Last Resort", `Trying ${lastModel.modelID} one more time...`, "warning").catch(() => { });
330
+ await client.tui.showToast({
331
+ body: {
332
+ title: "Last Resort",
333
+ message: `Trying ${lastModel.modelID} one more time...`,
334
+ variant: "warning",
335
+ duration: 3000,
336
+ },
337
+ });
276
338
  }
277
339
  else {
278
340
  // Last model also failed, reset for next prompt
@@ -287,221 +349,160 @@ export const RateLimitFallback = async ({ client, directory }) => {
287
349
  // "stop" mode: nextModel remains null, will show error below
288
350
  }
289
351
  if (!nextModel) {
290
- toast("No Fallback Available", config.fallbackMode === "stop"
291
- ? "All fallback models exhausted"
292
- : "All models are rate limited", "error").catch(() => { });
352
+ await client.tui.showToast({
353
+ body: {
354
+ title: "No Fallback Available",
355
+ message: config.fallbackMode === "stop"
356
+ ? "All fallback models exhausted"
357
+ : "All models are rate limited",
358
+ variant: "error",
359
+ duration: 5000,
360
+ },
361
+ });
293
362
  retryState.delete(stateKey);
294
- fallbackInProgress.delete(sessionID);
363
+ fallbackInProgress.delete(targetSessionID);
295
364
  return;
296
365
  }
297
366
  state.attemptedModels.add(getModelKey(nextModel.providerID, nextModel.modelID));
298
367
  state.lastAttemptTime = Date.now();
299
368
  const parts = lastUserMessage.parts
300
- .filter((p) => p.type === "text" || p.type === "file")
369
+ .filter((p) => {
370
+ const part = p;
371
+ return part.type === "text" || part.type === "file";
372
+ })
301
373
  .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 };
374
+ const part = p;
375
+ if (part.type === "text")
376
+ return { type: "text", text: String(part.text) };
377
+ if (part.type === "file")
378
+ return { type: "file", path: String(part.path), mediaType: String(part.mediaType) };
306
379
  return null;
307
380
  })
308
- .filter(Boolean);
381
+ .filter((p) => p !== null);
309
382
  if (parts.length === 0) {
310
- fallbackInProgress.delete(sessionID);
383
+ fallbackInProgress.delete(targetSessionID);
311
384
  return;
312
385
  }
386
+ await client.tui.showToast({
387
+ body: {
388
+ title: "Retrying",
389
+ message: `Using ${nextModel.providerID}/${nextModel.modelID}`,
390
+ variant: "info",
391
+ duration: 3000,
392
+ },
393
+ });
313
394
  // 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}`);
395
+ currentSessionModel.set(targetSessionID, {
396
+ providerID: nextModel.providerID,
397
+ modelID: nextModel.modelID,
398
+ lastUpdated: Date.now(),
399
+ });
400
+ // If this is a root session with subagents, propagate the model to all subagents
401
+ if (hierarchy && hierarchy.rootSessionID === targetSessionID) {
402
+ hierarchy.sharedFallbackState = "completed";
403
+ hierarchy.lastActivity = Date.now();
404
+ // Update model tracking for all subagents
405
+ for (const [subagentID, subagent] of hierarchy.subagents.entries()) {
406
+ currentSessionModel.set(subagentID, {
407
+ providerID: nextModel.providerID,
408
+ modelID: nextModel.modelID,
409
+ lastUpdated: Date.now(),
410
+ });
411
+ subagent.fallbackState = "completed";
412
+ subagent.lastActivity = Date.now();
333
413
  }
334
414
  }
335
- const t1 = Date.now();
336
- await client.session.promptAsync({
337
- path: { id: sessionID },
338
- body: promptBody,
415
+ // Convert internal MessagePart to SDK-compatible format
416
+ const sdkParts = parts.map((part) => {
417
+ if (part.type === "text") {
418
+ return { type: "text", text: part.text };
419
+ }
420
+ // For file parts, we need to match the FilePartInput format
421
+ // Using path as url since we're dealing with local files
422
+ return {
423
+ type: "file",
424
+ url: part.path,
425
+ mime: part.mediaType || "application/octet-stream",
426
+ };
427
+ });
428
+ await client.session.prompt({
429
+ path: { id: targetSessionID },
430
+ body: {
431
+ parts: sdkParts,
432
+ model: { providerID: nextModel.providerID, modelID: nextModel.modelID },
433
+ },
434
+ });
435
+ await client.tui.showToast({
436
+ body: {
437
+ title: "Fallback Successful",
438
+ message: `Now using ${nextModel.modelID}`,
439
+ variant: "success",
440
+ duration: 3000,
441
+ },
339
442
  });
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
443
  retryState.delete(stateKey);
347
- // Clear fallback flag to allow next fallback if needed
348
- fallbackInProgress.delete(sessionID);
444
+ // Explicitly clean up fallbackInProgress after cooldown period
445
+ // This prevents memory leaks while maintaining the deduplication window
446
+ setTimeout(() => {
447
+ fallbackInProgress.delete(targetSessionID);
448
+ }, DEDUP_WINDOW_MS);
349
449
  }
350
450
  catch (err) {
351
- logToFile(`handleRateLimitFallback error: ${err}`);
352
- // Fallback failed, clear the flag
353
- fallbackInProgress.delete(sessionID);
451
+ fallbackInProgress.delete(targetSessionID);
452
+ // Silently ignore fallback errors
354
453
  }
355
454
  }
356
455
  return {
357
456
  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") {
457
+ if (isSessionErrorEvent(event)) {
374
458
  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
459
  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)
460
+ await handleRateLimitFallback(sessionID, "", "");
403
461
  }
404
462
  }
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
- }
463
+ if (isMessageUpdatedEvent(event)) {
464
+ const info = event.properties.info;
427
465
  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)
466
+ await handleRateLimitFallback(info.sessionID, info.providerID || "", info.modelID || "");
442
467
  }
443
468
  }
444
- if (event.type === "session.status") {
469
+ if (isSessionStatusEvent(event)) {
445
470
  const props = event.properties;
446
471
  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
472
  if (status?.type === "retry" && status?.message) {
465
473
  const message = status.message.toLowerCase();
466
474
  const isRateLimitRetry = message.includes("usage limit") ||
467
475
  message.includes("rate limit") ||
468
476
  message.includes("high concurrency") ||
469
477
  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
478
  if (isRateLimitRetry) {
484
479
  // 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);
480
+ await handleRateLimitFallback(props.sessionID, "", "");
501
481
  }
502
482
  }
503
483
  }
484
+ // Handle subagent session creation events
485
+ // Note: Using type assertion for subagent events since they may not be in the official Event union yet
486
+ const rawEvent = event;
487
+ if (isSubagentSessionCreatedEvent(rawEvent)) {
488
+ const { sessionID, parentSessionID } = rawEvent.properties;
489
+ if (config.enableSubagentFallback !== false) {
490
+ registerSubagent(sessionID, parentSessionID);
491
+ }
492
+ }
493
+ },
494
+ // Cleanup function to prevent memory leaks
495
+ cleanup: () => {
496
+ clearInterval(cleanupInterval);
497
+ const index = activeCleanupIntervals.indexOf(cleanupInterval);
498
+ if (index > -1) {
499
+ activeCleanupIntervals.splice(index, 1);
500
+ }
501
+ // Clean up all session hierarchies
502
+ sessionHierarchies.clear();
503
+ sessionToRootMap.clear();
504
504
  },
505
505
  };
506
506
  };
507
507
  export default RateLimitFallback;
508
+ //# 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.4",
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
  }