opencode-immune 1.0.2 → 1.0.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.
Files changed (2) hide show
  1. package/dist/plugin.js +49 -3
  2. package/package.json +1 -1
package/dist/plugin.js CHANGED
@@ -197,9 +197,7 @@ function isRateLimitApiError(error) {
197
197
  const maybeError = error;
198
198
  const message = `${maybeError.message ?? ""} ${maybeError.data?.message ?? ""}`.toLowerCase();
199
199
  const type = `${maybeError.data?.type ?? ""}`.toLowerCase();
200
- return (maybeError.name === "APIError" &&
201
- maybeError.data?.isRetryable === true &&
202
- (type.includes("rate_limit") || message.includes("too many requests") || message.includes("rate limit")));
200
+ return ((type.includes("rate_limit") || message.includes("too many requests") || message.includes("rate limit")));
203
201
  }
204
202
  async function setSessionFallbackModel(state, sessionID, model) {
205
203
  const existing = state.managedUltraworkSessions.get(sessionID);
@@ -212,6 +210,33 @@ async function setSessionFallbackModel(state, sessionID, model) {
212
210
  });
213
211
  await writeManagedSessionsCache(state);
214
212
  }
213
+ async function forceRetryManagedSession(state, sessionID, reason) {
214
+ const managedSession = state.managedUltraworkSessions.get(sessionID);
215
+ if (!managedSession)
216
+ return;
217
+ const retryAgent = managedSession.agent || ULTRAWORK_AGENT;
218
+ const fallbackModel = managedSession.fallbackModel;
219
+ const retryText = retryAgent === ULTRAWORK_AGENT
220
+ ? "[SYSTEM: Previous API call failed with a transient error. Re-read memory-bank/tasks.md, check the Phase Status block, and continue the pipeline. Use the exact neutral prompt from your Step 5 table for the next router call. Do NOT analyze or evaluate file contents.]"
221
+ : `[SYSTEM: Previous API call failed with a transient error. Resume the current task in your current role as ${retryAgent}. Continue from the existing session state. Do not restart from scratch unless the current session state is missing.]`;
222
+ await state.input.client.session.promptAsync({
223
+ body: {
224
+ ...(fallbackModel ? { model: fallbackModel } : {}),
225
+ agent: retryAgent,
226
+ parts: [
227
+ {
228
+ type: "text",
229
+ text: retryText,
230
+ },
231
+ ],
232
+ },
233
+ path: { id: sessionID },
234
+ });
235
+ console.log(`[opencode-immune] Forced retry sent to session ${sessionID} (${reason})` +
236
+ (fallbackModel
237
+ ? ` using fallback model ${fallbackModel.providerID}/${fallbackModel.modelID}`
238
+ : ""));
239
+ }
215
240
  // ═══════════════════════════════════════════════════════════════════════════════
216
241
  // UTILITY: ERROR BOUNDARY
217
242
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -749,6 +774,7 @@ const MAX_CYCLES = 10;
749
774
  const PRE_COMMIT_MARKER = "0-ULTRAWORK: PRE_COMMIT";
750
775
  const CYCLE_COMPLETE_MARKER = "0-ULTRAWORK: CYCLE_COMPLETE";
751
776
  const NEXT_TASK_PATTERN = /Next task:\s*(.+)/;
777
+ const RATE_LIMIT_MESSAGE_PATTERN = /too many requests|rate_limit|rate limit/i;
752
778
  /**
753
779
  * chat.message part: scans assistant messages for PRE_COMMIT and CYCLE_COMPLETE markers.
754
780
  *
@@ -771,6 +797,26 @@ function createMultiCycleHandler(state) {
771
797
  messageContent = messageContent.trim();
772
798
  if (!messageContent)
773
799
  return;
800
+ if (sessionID && RATE_LIMIT_MESSAGE_PATTERN.test(messageContent)) {
801
+ const managedSession = getManagedSession(state, sessionID);
802
+ if (managedSession && !managedSession.fallbackModel) {
803
+ await setSessionFallbackModel(state, sessionID, RATE_LIMIT_FALLBACK_MODEL);
804
+ console.log(`[opencode-immune] Rate limit message detected in chat output for session ${sessionID}. ` +
805
+ `Fallback model pinned to ${RATE_LIMIT_FALLBACK_MODEL.providerID}/${RATE_LIMIT_FALLBACK_MODEL.modelID}.`);
806
+ }
807
+ if (managedSession && !state.retryTimers.has(sessionID)) {
808
+ const timer = setTimeout(async () => {
809
+ state.retryTimers.delete(sessionID);
810
+ try {
811
+ await forceRetryManagedSession(state, sessionID, "rate-limit message fallback");
812
+ }
813
+ catch (err) {
814
+ console.error(`[opencode-immune] Forced retry after rate-limit message failed for session ${sessionID}:`, err);
815
+ }
816
+ }, 1_000);
817
+ state.retryTimers.set(sessionID, timer);
818
+ }
819
+ }
774
820
  // ── PRE_COMMIT: execute /commit ──
775
821
  if (messageContent.includes(PRE_COMMIT_MARKER) && !state.commitPending) {
776
822
  state.commitPending = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-immune",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
5
5
  "exports": {
6
6
  "./server": "./dist/plugin.js"