opencode-immune 1.0.0 → 1.0.1

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 +37 -1
  2. package/package.json +2 -2
package/dist/plugin.js CHANGED
@@ -24,6 +24,10 @@ function createState(input) {
24
24
  }
25
25
  const ULTRAWORK_AGENT = "0-ultrawork";
26
26
  const MANAGED_SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000;
27
+ const RATE_LIMIT_FALLBACK_MODEL = {
28
+ providerID: "externcash",
29
+ modelID: "gpt-5.4",
30
+ };
27
31
  function isManagedUltraworkSession(state, sessionID) {
28
32
  return !!sessionID && state.managedUltraworkSessions.has(sessionID);
29
33
  }
@@ -92,6 +96,7 @@ async function addManagedUltraworkSession(state, sessionID, timestamp = Date.now
92
96
  agent: ULTRAWORK_AGENT,
93
97
  createdAt: existing?.createdAt ?? timestamp,
94
98
  updatedAt: timestamp,
99
+ fallbackModel: existing?.fallbackModel,
95
100
  };
96
101
  if (existing &&
97
102
  existing.agent === nextRecord.agent &&
@@ -140,6 +145,27 @@ function isRetryableApiError(error) {
140
145
  return (maybeError.name === "APIError" &&
141
146
  maybeError.data?.isRetryable === true);
142
147
  }
148
+ function isRateLimitApiError(error) {
149
+ if (!error || typeof error !== "object")
150
+ return false;
151
+ const maybeError = error;
152
+ const message = `${maybeError.message ?? ""} ${maybeError.data?.message ?? ""}`.toLowerCase();
153
+ const type = `${maybeError.data?.type ?? ""}`.toLowerCase();
154
+ return (maybeError.name === "APIError" &&
155
+ maybeError.data?.isRetryable === true &&
156
+ (type.includes("rate_limit") || message.includes("too many requests") || message.includes("rate limit")));
157
+ }
158
+ async function setSessionFallbackModel(state, sessionID, model) {
159
+ const existing = state.managedUltraworkSessions.get(sessionID);
160
+ if (!existing)
161
+ return;
162
+ state.managedUltraworkSessions.set(sessionID, {
163
+ ...existing,
164
+ updatedAt: Date.now(),
165
+ fallbackModel: model,
166
+ });
167
+ await writeManagedSessionsCache(state);
168
+ }
143
169
  // ═══════════════════════════════════════════════════════════════════════════════
144
170
  // UTILITY: ERROR BOUNDARY
145
171
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -586,6 +612,12 @@ function createEventHandler(state) {
586
612
  if (count < MAX_RETRIES) {
587
613
  const delay = Math.min(BASE_DELAY_MS * Math.pow(2, count), MAX_DELAY_MS);
588
614
  state.retryCount.set(sessionID, count + 1);
615
+ if (isRateLimitApiError(error)) {
616
+ await setSessionFallbackModel(state, sessionID, RATE_LIMIT_FALLBACK_MODEL);
617
+ console.log(`[opencode-immune] Rate limit detected for session ${sessionID}. ` +
618
+ `Retry will use fallback model ${RATE_LIMIT_FALLBACK_MODEL.providerID}/${RATE_LIMIT_FALLBACK_MODEL.modelID}.`);
619
+ }
620
+ const fallbackModel = state.managedUltraworkSessions.get(sessionID)?.fallbackModel;
589
621
  console.log(`[opencode-immune] Session error detected (attempt ${count + 1}/${MAX_RETRIES}). ` +
590
622
  `Waiting ${delay / 1000}s before retry...`);
591
623
  const timer = setTimeout(async () => {
@@ -596,6 +628,7 @@ function createEventHandler(state) {
596
628
  try {
597
629
  await state.input.client.session.promptAsync({
598
630
  body: {
631
+ ...(fallbackModel ? { model: fallbackModel } : {}),
599
632
  agent: ULTRAWORK_AGENT,
600
633
  parts: [
601
634
  {
@@ -606,7 +639,10 @@ function createEventHandler(state) {
606
639
  },
607
640
  path: { id: sessionID },
608
641
  });
609
- console.log(`[opencode-immune] Auto-retry message sent to session ${sessionID}`);
642
+ console.log(`[opencode-immune] Auto-retry message sent to session ${sessionID}` +
643
+ (fallbackModel
644
+ ? ` using fallback model ${fallbackModel.providerID}/${fallbackModel.modelID}`
645
+ : ""));
610
646
  }
611
647
  catch (err) {
612
648
  state.retryCount.set(sessionID, Math.max((state.retryCount.get(sessionID) ?? 1) - 1, 0));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-immune",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
5
5
  "exports": {
6
6
  "./server": "./dist/plugin.js"
@@ -14,7 +14,7 @@
14
14
  "prepublishOnly": "npm run build"
15
15
  },
16
16
  "dependencies": {
17
- "@opencode-ai/plugin": "^1.4.1"
17
+ "@opencode-ai/plugin": "1.4.1"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@types/node": "^25.5.2",