@azumag/opencode-rate-limit-fallback 1.64.0 → 1.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -240,6 +240,10 @@ export const RateLimitFallback = async ({ client, directory, worktree }) => {
240
240
  // Handle message.updated events
241
241
  if (isMessageUpdatedEvent(event)) {
242
242
  const info = event.properties.info;
243
+ // Track model info for all assistant messages (needed to identify current model on session.error)
244
+ if (info?.providerID && info?.modelID && info?.sessionID) {
245
+ fallbackHandler.setSessionModel(info.sessionID, info.providerID, info.modelID);
246
+ }
243
247
  if (info?.error && errorPatternRegistry.isRateLimitError(info.error)) {
244
248
  // Learn from this error if pattern learning is enabled
245
249
  const patternLearner = errorPatternRegistry.getPatternLearner();
@@ -143,7 +143,7 @@ export class ConfigValidator {
143
143
  }
144
144
  }
145
145
  if (config.retryPolicy.strategy !== undefined) {
146
- const validStrategies = ['immediate', 'exponential', 'linear', 'custom'];
146
+ const validStrategies = ['immediate', 'exponential', 'linear', 'polynomial', 'custom'];
147
147
  if (!validStrategies.includes(config.retryPolicy.strategy)) {
148
148
  errors.push({
149
149
  path: 'retryPolicy.strategy',
@@ -43,13 +43,9 @@ export declare class FallbackHandler {
43
43
  modelID: string;
44
44
  } | null;
45
45
  /**
46
- * Abort current session with error handling
47
- */
48
- private abortSession;
49
- /**
50
- * Queue prompt asynchronously (non-blocking), then abort retry loop.
51
- * promptAsync FIRST queues pending work so that server doesn't dispose on idle.
52
- * abort SECOND cancels the retry loop; the server sees the queued prompt and processes it.
46
+ * Queue prompt asynchronously (non-blocking) to schedule fallback.
47
+ * The server's retry loop finishes naturally; it then picks up the queued prompt.
48
+ * We do NOT call abort — its AbortController signal persists and kills the new stream.
53
49
  */
54
50
  retryWithModel(targetSessionID: string, model: FallbackModel, parts: MessagePart[], hierarchy: SessionHierarchy | null): Promise<void>;
55
51
  /**
@@ -92,21 +92,9 @@ export class FallbackHandler {
92
92
  return tracked ? { providerID: tracked.providerID, modelID: tracked.modelID } : null;
93
93
  }
94
94
  /**
95
- * Abort current session with error handling
96
- */
97
- async abortSession(sessionID) {
98
- try {
99
- await this.client.session.abort({ path: { id: sessionID } });
100
- }
101
- catch (abortError) {
102
- // Silently ignore abort errors and continue with fallback
103
- this.logger.debug(`Failed to abort session ${sessionID}`, { error: abortError });
104
- }
105
- }
106
- /**
107
- * Queue prompt asynchronously (non-blocking), then abort retry loop.
108
- * promptAsync FIRST queues pending work so that server doesn't dispose on idle.
109
- * abort SECOND cancels the retry loop; the server sees the queued prompt and processes it.
95
+ * Queue prompt asynchronously (non-blocking) to schedule fallback.
96
+ * The server's retry loop finishes naturally; it then picks up the queued prompt.
97
+ * We do NOT call abort — its AbortController signal persists and kills the new stream.
110
98
  */
111
99
  async retryWithModel(targetSessionID, model, parts, hierarchy) {
112
100
  // Record model usage for dynamic prioritization
@@ -152,11 +140,13 @@ export class FallbackHandler {
152
140
  model: { providerID: model.providerID, modelID: model.modelID },
153
141
  },
154
142
  });
155
- // 2. abort: cancel the retry loop; server sees queued prompt and processes it
156
- await this.abortSession(targetSessionID);
143
+ // Do NOT call abort after promptAsync.
144
+ // The AbortController signal persists and kills the newly queued stream too,
145
+ // causing "interrupted" in TUI mode and server disposal in headless mode.
146
+ // Let the server's retry loop finish naturally; it will pick up the queued prompt.
157
147
  await safeShowToast(this.client, {
158
148
  body: {
159
- title: "Fallback Successful",
149
+ title: "Fallback Queued",
160
150
  message: `Now using ${model.modelID}`,
161
151
  variant: "success",
162
152
  duration: 3000,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azumag/opencode-rate-limit-fallback",
3
- "version": "1.64.0",
3
+ "version": "1.67.0",
4
4
  "description": "OpenCode plugin that automatically switches to fallback models when rate limited",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",