opencode-immune 1.0.42 → 1.0.44

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 +52 -4
  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.42";
14
+ const PLUGIN_VERSION = "1.0.44";
15
15
  const PLUGIN_PACKAGE_NAME = "opencode-immune";
16
16
  /**
17
17
  * Read plugin version from package.json at runtime.
@@ -76,6 +76,7 @@ function createState(input) {
76
76
  managedUltraworkSessions: new Map(),
77
77
  sessionRetryTimers: new Map(),
78
78
  providerRetryWatchdogs: new Map(),
79
+ childFallbackRequests: new Map(),
79
80
  sessionErrorRetryCount: new Map(),
80
81
  ultraworkMarkerPath: (0, path_1.join)(input.directory, ".opencode", "state", "ultrawork-active.json"),
81
82
  diagnosticsLogPath: (0, path_1.join)(input.directory, ".opencode", "state", "opencode-immune-debug.log"),
@@ -93,12 +94,13 @@ function createState(input) {
93
94
  const ULTRAWORK_AGENT = "0-ultrawork";
94
95
  const MANAGED_SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000;
95
96
  const PROVIDER_RETRY_WATCHDOG_MS = 30_000;
97
+ const CHILD_FALLBACK_REQUEST_TTL_MS = 10 * 60 * 1000;
96
98
  const RATE_LIMIT_FALLBACK_MODEL = {
97
- providerID: "externcash",
99
+ providerID: "codexsale",
98
100
  modelID: "gpt-5.5",
99
101
  };
100
102
  const CHILD_SESSION_FALLBACK_MODEL = {
101
- providerID: "externcash",
103
+ providerID: "codexsale",
102
104
  modelID: "gpt-5.5",
103
105
  };
104
106
  function isManagedUltraworkSession(state, sessionID) {
@@ -364,6 +366,36 @@ function isCertificateApiError(error) {
364
366
  function isProviderRetryBanner(text) {
365
367
  return /(?:<none>\s*)?retrying in \d+s\s*-\s*attempt #\d+/i.test(text);
366
368
  }
369
+ function getRetryableErrorType(error) {
370
+ if (isModelAccessError(error))
371
+ return "model access error";
372
+ if (isRateLimitApiError(error))
373
+ return "rate limit";
374
+ if (isCertificateApiError(error))
375
+ return "certificate error";
376
+ return "retryable provider error";
377
+ }
378
+ function recordChildFallbackRequest(state, managedSession, childSessionID, error) {
379
+ const request = {
380
+ childSessionID,
381
+ rootSessionID: managedSession.rootSessionID,
382
+ agent: managedSession.agent || "unknown",
383
+ errorType: getRetryableErrorType(error),
384
+ fallbackModel: CHILD_SESSION_FALLBACK_MODEL,
385
+ createdAt: Date.now(),
386
+ };
387
+ state.childFallbackRequests.set(managedSession.rootSessionID, request);
388
+ }
389
+ function getChildFallbackRequest(state, rootSessionID, now = Date.now()) {
390
+ const request = state.childFallbackRequests.get(rootSessionID);
391
+ if (!request)
392
+ return undefined;
393
+ if (now - request.createdAt > CHILD_FALLBACK_REQUEST_TTL_MS) {
394
+ state.childFallbackRequests.delete(rootSessionID);
395
+ return undefined;
396
+ }
397
+ return request;
398
+ }
367
399
  function scheduleProviderRetryWatchdog(state, sessionID, model) {
368
400
  if (!isManagedRootUltraworkSession(state, sessionID))
369
401
  return;
@@ -1041,6 +1073,21 @@ function createSystemTransform(state) {
1041
1073
  `Read memory-bank/tasks.md and memory-bank/activeContext.md to resume work.`);
1042
1074
  }
1043
1075
  }
1076
+ const rootSessionID = input.sessionID;
1077
+ if (rootSessionID && isManagedRootUltraworkSession(state, rootSessionID)) {
1078
+ const childFallbackRequest = getChildFallbackRequest(state, rootSessionID);
1079
+ if (childFallbackRequest) {
1080
+ output.system.push(`[Router-Owned Child Retry Required]\n` +
1081
+ `A child/subagent session failed with a retryable provider/model error. ` +
1082
+ `Do not advance to the next sequential pipeline slot until this failed slot is explicitly resolved.\n` +
1083
+ `- Failed child session: ${childFallbackRequest.childSessionID}\n` +
1084
+ `- Agent: ${childFallbackRequest.agent}\n` +
1085
+ `- Error type: ${childFallbackRequest.errorType}\n` +
1086
+ `- Required fallback model: ${childFallbackRequest.fallbackModel.providerID}/${childFallbackRequest.fallbackModel.modelID}\n` +
1087
+ `Router action: retry the SAME agent/slot once using the fallback model if available; if it still fails or cannot be retried, record DECLINE/failed-provider-retry for that slot before continuing. ` +
1088
+ `Never resume the failed child session directly; create a router-owned replacement attempt instead.`);
1089
+ }
1090
+ }
1044
1091
  // Ralph Loop injection
1045
1092
  if (state.lastEditAttempt) {
1046
1093
  const edit = state.lastEditAttempt;
@@ -1334,8 +1381,9 @@ function createEventHandler(state) {
1334
1381
  // Child/subagent failures are owned by the router. Auto-resuming a failed
1335
1382
  // child after the router advances can create two writers in one pipeline.
1336
1383
  if (isChild) {
1384
+ recordChildFallbackRequest(state, managedSession, sessionID, error);
1337
1385
  console.log(`[opencode-immune] Child session ${sessionID}: retryable error detected. ` +
1338
- `Skipping plugin auto-retry; router owns sequential fallback decisions.`);
1386
+ `Recorded router-owned fallback request and skipped plugin auto-retry.`);
1339
1387
  state.sessionErrorRetryCount.set(sessionID, count);
1340
1388
  return;
1341
1389
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-immune",
3
- "version": "1.0.42",
3
+ "version": "1.0.44",
4
4
  "description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
5
5
  "exports": {
6
6
  "./server": "./dist/plugin.js"