opencode-immune 1.0.40 → 1.0.41

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 +41 -1
  2. package/package.json +1 -1
package/dist/plugin.js CHANGED
@@ -11,7 +11,7 @@ const child_process_1 = require("child_process");
11
11
  // ═══════════════════════════════════════════════════════════════════════════════
12
12
  // PLUGIN VERSION CHECK
13
13
  // ═══════════════════════════════════════════════════════════════════════════════
14
- const PLUGIN_VERSION = "1.0.40";
14
+ const PLUGIN_VERSION = "1.0.41";
15
15
  const PLUGIN_PACKAGE_NAME = "opencode-immune";
16
16
  /**
17
17
  * Read plugin version from package.json at runtime.
@@ -75,6 +75,7 @@ function createState(input) {
75
75
  recoveryContext: null,
76
76
  managedUltraworkSessions: new Map(),
77
77
  sessionRetryTimers: new Map(),
78
+ providerRetryWatchdogs: new Map(),
78
79
  sessionErrorRetryCount: new Map(),
79
80
  ultraworkMarkerPath: (0, path_1.join)(input.directory, ".opencode", "state", "ultrawork-active.json"),
80
81
  diagnosticsLogPath: (0, path_1.join)(input.directory, ".opencode", "state", "opencode-immune-debug.log"),
@@ -91,6 +92,7 @@ function createState(input) {
91
92
  }
92
93
  const ULTRAWORK_AGENT = "0-ultrawork";
93
94
  const MANAGED_SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000;
95
+ const PROVIDER_RETRY_WATCHDOG_MS = 30_000;
94
96
  const RATE_LIMIT_FALLBACK_MODEL = {
95
97
  providerID: "externcash",
96
98
  modelID: "gpt-5.5",
@@ -206,8 +208,17 @@ function cancelPendingSessionRetry(state, sessionID, reason) {
206
208
  state.sessionRetryTimers.delete(sessionID);
207
209
  console.log(`[opencode-immune] Cancelled pending retry for session ${sessionID}: ${reason}`);
208
210
  }
211
+ function cancelProviderRetryWatchdog(state, sessionID, reason) {
212
+ const timer = state.providerRetryWatchdogs.get(sessionID);
213
+ if (!timer)
214
+ return;
215
+ clearTimeout(timer);
216
+ state.providerRetryWatchdogs.delete(sessionID);
217
+ console.log(`[opencode-immune] Cancelled provider retry watchdog for session ${sessionID}: ${reason}`);
218
+ }
209
219
  async function removeManagedUltraworkSession(state, sessionID, reason) {
210
220
  cancelPendingSessionRetry(state, sessionID, reason);
221
+ cancelProviderRetryWatchdog(state, sessionID, reason);
211
222
  state.sessionErrorRetryCount.delete(sessionID);
212
223
  const existed = state.managedUltraworkSessions.delete(sessionID);
213
224
  if (!existed)
@@ -353,6 +364,31 @@ function isCertificateApiError(error) {
353
364
  function isProviderRetryBanner(text) {
354
365
  return /(?:<none>\s*)?retrying in \d+s\s*-\s*attempt #\d+/i.test(text);
355
366
  }
367
+ function scheduleProviderRetryWatchdog(state, sessionID, model) {
368
+ if (!isManagedUltraworkSession(state, sessionID))
369
+ return;
370
+ if (state.providerRetryWatchdogs.has(sessionID))
371
+ return;
372
+ const timer = setTimeout(async () => {
373
+ state.providerRetryWatchdogs.delete(sessionID);
374
+ if (!isManagedUltraworkSession(state, sessionID))
375
+ return;
376
+ if (state.sessionRetryTimers.has(sessionID))
377
+ return;
378
+ await setSessionFallbackModel(state, sessionID, model);
379
+ await writeDiagnosticLog(state, "provider-retry-watchdog:fired", {
380
+ sessionID,
381
+ fallbackModel: model,
382
+ });
383
+ scheduleManagedSessionRetry(state, sessionID, {
384
+ delayMs: 1_000,
385
+ reason: "provider retry watchdog",
386
+ countAgainstBudget: false,
387
+ abortBeforePrompt: true,
388
+ });
389
+ }, PROVIDER_RETRY_WATCHDOG_MS);
390
+ state.providerRetryWatchdogs.set(sessionID, timer);
391
+ }
356
392
  async function setSessionFallbackModel(state, sessionID, model) {
357
393
  const existing = state.managedUltraworkSessions.get(sessionID);
358
394
  if (!existing)
@@ -1175,6 +1211,7 @@ function createFallbackModels(state) {
1175
1211
  const wasAlreadyManaged = isManagedUltraworkSession(state, input.sessionID);
1176
1212
  await addManagedUltraworkSession(state, input.sessionID);
1177
1213
  await writeUltraworkMarker(state);
1214
+ scheduleProviderRetryWatchdog(state, input.sessionID, RATE_LIMIT_FALLBACK_MODEL);
1178
1215
  // First contact with 0-ultrawork after plugin restart:
1179
1216
  // if marker is active and tasks.md has incomplete work, send AUTO-RESUME prompt.
1180
1217
  if (!wasAlreadyManaged && !state.autoResumeAttempted) {
@@ -1214,6 +1251,7 @@ function createFallbackModels(state) {
1214
1251
  }
1215
1252
  else if (getManagedSession(state, input.sessionID)?.kind === "child") {
1216
1253
  await updateManagedSessionAgent(state, input.sessionID, input.agent);
1254
+ scheduleProviderRetryWatchdog(state, input.sessionID, CHILD_SESSION_FALLBACK_MODEL);
1217
1255
  }
1218
1256
  // NOTE: Do NOT remove managed root sessions when agent changes in chat.params.
1219
1257
  // Subagent calls from 0-ultrawork (1-van, 7-backlog, etc.) use the same session.
@@ -1324,6 +1362,7 @@ function createEventHandler(state) {
1324
1362
  // Reset retry counter on successful activity
1325
1363
  if (eventType === "session.updated" && sessionID) {
1326
1364
  cancelPendingSessionRetry(state, sessionID, "session updated");
1365
+ cancelProviderRetryWatchdog(state, sessionID, "session updated");
1327
1366
  state.sessionErrorRetryCount.delete(sessionID);
1328
1367
  if (markUltraworkSessionActive(state, sessionID)) {
1329
1368
  // session activity tracked in-memory only
@@ -1473,6 +1512,7 @@ function createTextCompleteHandler(state) {
1473
1512
  const text = output.text ?? "";
1474
1513
  if (!text)
1475
1514
  return;
1515
+ cancelProviderRetryWatchdog(state, sessionID, "assistant text completed");
1476
1516
  // Some provider/SDK failures render as a retry banner instead of a
1477
1517
  // session.error event, leaving the UI waiting for long internal backoff.
1478
1518
  if (isProviderRetryBanner(text) && isManagedUltraworkSession(state, sessionID)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-immune",
3
- "version": "1.0.40",
3
+ "version": "1.0.41",
4
4
  "description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
5
5
  "exports": {
6
6
  "./server": "./dist/plugin.js"