opencode-immune 1.0.60 → 1.0.61

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 +38 -15
  2. package/package.json +1 -1
package/dist/plugin.js CHANGED
@@ -107,6 +107,7 @@ function createState(input) {
107
107
  approximateTokens: 0,
108
108
  sessionActive: false,
109
109
  autoResumeAttempted: false,
110
+ autoResumeInFlight: false,
110
111
  cycleCount: 0,
111
112
  commitPending: false,
112
113
  pluginUpdateMessage: null,
@@ -178,16 +179,21 @@ async function createManagedUltraworkSession(state, title) {
178
179
  async function applyUltraworkSessionPermissions(state, sessionID) {
179
180
  if (state.ultraworkPermissionSessions.has(sessionID))
180
181
  return;
181
- const result = await state.client.session.update({
182
- directory: state.input.directory,
183
- sessionID,
184
- permission: ULTRAWORK_SESSION_PERMISSION,
185
- });
186
- if (result.error || !result.response.ok) {
187
- pluginLog.warn(`[opencode-immune] Failed to apply ultrawork permissions to session ${sessionID}:`, result.error ?? result.response.status);
188
- return;
182
+ try {
183
+ const result = await state.client.session.update({
184
+ directory: state.input.directory,
185
+ sessionID,
186
+ permission: ULTRAWORK_SESSION_PERMISSION,
187
+ });
188
+ if (result.error || !result.response.ok) {
189
+ pluginLog.warn(`[opencode-immune] Failed to apply ultrawork permissions to session ${sessionID}:`, result.error ?? result.response.status);
190
+ return;
191
+ }
192
+ state.ultraworkPermissionSessions.add(sessionID);
193
+ }
194
+ catch (err) {
195
+ pluginLog.warn(`[opencode-immune] Failed to apply ultrawork permissions to session ${sessionID}:`, err);
189
196
  }
190
- state.ultraworkPermissionSessions.add(sessionID);
191
197
  }
192
198
  async function promptManagedSession(state, sessionID, text, options = {}) {
193
199
  const result = await state.client.session.promptAsync({
@@ -1394,18 +1400,23 @@ function createSessionRecoveryEvent(state) {
1394
1400
  // Register this root session as managed so retry/recovery works
1395
1401
  await addManagedUltraworkSession(state, sessionID);
1396
1402
  // Skip sending AUTO-RESUME if already sent from plugin init
1397
- if (state.autoResumeAttempted) {
1398
- pluginLog.info(`[opencode-immune] Auto-resume already sent from plugin init, skipping duplicate for session ${sessionID}.`);
1403
+ if (state.autoResumeAttempted || state.autoResumeInFlight) {
1404
+ pluginLog.info(`[opencode-immune] Auto-resume already sent or in flight, skipping duplicate for session ${sessionID}.`);
1399
1405
  return;
1400
1406
  }
1407
+ state.autoResumeInFlight = true;
1401
1408
  setTimeout(async () => {
1402
1409
  try {
1403
1410
  await promptManagedSession(state, sessionID, `[AUTO-RESUME] Previous session was interrupted. Read memory-bank/tasks.md, check the Phase Status block, and continue the pipeline. Use ONLY the Phase Status block to determine the next phase. Do NOT analyze or evaluate the content of tasks.md. Call the appropriate router with the exact neutral prompt from your Step 5 table.`);
1411
+ state.autoResumeAttempted = true;
1404
1412
  pluginLog.info(`[opencode-immune] Auto-resume prompt sent to managed ultrawork session ${sessionID}`);
1405
1413
  }
1406
1414
  catch (err) {
1407
1415
  pluginLog.info(`[opencode-immune] Auto-resume failed (session may have been taken over):`, err);
1408
1416
  }
1417
+ finally {
1418
+ state.autoResumeInFlight = false;
1419
+ }
1409
1420
  }, 3_000);
1410
1421
  }
1411
1422
  }
@@ -1631,8 +1642,7 @@ function createFallbackModels(state) {
1631
1642
  scheduleProviderRetryWatchdog(state, input.sessionID);
1632
1643
  // First contact with 0-ultrawork after plugin restart:
1633
1644
  // if marker is active and tasks.md has incomplete work, send AUTO-RESUME prompt.
1634
- if (!wasAlreadyManaged && !state.autoResumeAttempted) {
1635
- state.autoResumeAttempted = true;
1645
+ if (!wasAlreadyManaged && !state.autoResumeAttempted && !state.autoResumeInFlight) {
1636
1646
  const markerActive = await isUltraworkMarkerActive(state);
1637
1647
  if (markerActive) {
1638
1648
  const recovery = await parseTasksFile(state.input.directory);
@@ -1642,14 +1652,19 @@ function createFallbackModels(state) {
1642
1652
  `task="${recovery.task}", level=${recovery.level}, phase=${recovery.phase}. ` +
1643
1653
  `Sending AUTO-RESUME prompt in 3s...`);
1644
1654
  const sid = input.sessionID;
1655
+ state.autoResumeInFlight = true;
1645
1656
  setTimeout(async () => {
1646
1657
  try {
1647
1658
  await promptManagedSession(state, sid, `[AUTO-RESUME] Previous session was interrupted. Read memory-bank/tasks.md, check the Phase Status block, and continue the pipeline. Use ONLY the Phase Status block to determine the next phase. Do NOT analyze or evaluate the content of tasks.md. Call the appropriate router with the exact neutral prompt from your Step 5 table.`);
1659
+ state.autoResumeAttempted = true;
1648
1660
  pluginLog.info(`[opencode-immune] Auto-resume prompt sent to session ${sid}`);
1649
1661
  }
1650
1662
  catch (err) {
1651
1663
  pluginLog.info(`[opencode-immune] Auto-resume prompt failed:`, err);
1652
1664
  }
1665
+ finally {
1666
+ state.autoResumeInFlight = false;
1667
+ }
1653
1668
  }, 3_000);
1654
1669
  }
1655
1670
  }
@@ -2123,12 +2138,12 @@ async function server(input) {
2123
2138
  if (recovery && recovery.phase !== "ARCHIVE: DONE") {
2124
2139
  // Active task exists with incomplete phases — resume it
2125
2140
  state.recoveryContext = recovery;
2126
- state.autoResumeAttempted = true;
2127
2141
  pluginLog.info(`[opencode-immune] Plugin init: ultrawork marker active, recovery context loaded: ` +
2128
2142
  `task="${recovery.task}", level=${recovery.level}, phase=${recovery.phase}. ` +
2129
2143
  `Will create new session and send AUTO-RESUME.`);
2130
2144
  // Create a new session and send AUTO-RESUME prompt (same pattern as CYCLE_COMPLETE).
2131
2145
  // Delay to let opencode fully initialize.
2146
+ state.autoResumeInFlight = true;
2132
2147
  setTimeout(async () => {
2133
2148
  try {
2134
2149
  const newSessionID = await createManagedUltraworkSession(state, `AUTO-RESUME: ${recovery.task}`);
@@ -2138,11 +2153,15 @@ async function server(input) {
2138
2153
  }
2139
2154
  pluginLog.info(`[opencode-immune] Auto-resume: New session created: ${newSessionID}`);
2140
2155
  await promptManagedSession(state, newSessionID, `[AUTO-RESUME] Previous session was interrupted. Read memory-bank/tasks.md, check the Phase Status block, and continue the pipeline. Use ONLY the Phase Status block to determine the next phase. Do NOT analyze or evaluate the content of tasks.md. Call the appropriate router with the exact neutral prompt from your Step 5 table.`);
2156
+ state.autoResumeAttempted = true;
2141
2157
  pluginLog.info(`[opencode-immune] Auto-resume prompt sent to new session ${newSessionID}`);
2142
2158
  }
2143
2159
  catch (err) {
2144
2160
  pluginLog.error("[opencode-immune] Auto-resume: Failed to create session or send prompt:", err);
2145
2161
  }
2162
+ finally {
2163
+ state.autoResumeInFlight = false;
2164
+ }
2146
2165
  }, 5_000);
2147
2166
  }
2148
2167
  else {
@@ -2152,9 +2171,9 @@ async function server(input) {
2152
2171
  const backlogContent = await readFile(backlogPath, "utf-8");
2153
2172
  const hasPendingTasks = /- \[ \]/.test(backlogContent);
2154
2173
  if (hasPendingTasks) {
2155
- state.autoResumeAttempted = true;
2156
2174
  pluginLog.info(`[opencode-immune] Plugin init: no active task but backlog has pending items. ` +
2157
2175
  `Will create new session to start next cycle.`);
2176
+ state.autoResumeInFlight = true;
2158
2177
  setTimeout(async () => {
2159
2178
  try {
2160
2179
  const newSessionID = await createManagedUltraworkSession(state, `AUTO-CYCLE: next backlog task`);
@@ -2164,11 +2183,15 @@ async function server(input) {
2164
2183
  }
2165
2184
  pluginLog.info(`[opencode-immune] Auto-cycle: New session created: ${newSessionID}`);
2166
2185
  await promptManagedSession(state, newSessionID, `[AUTO-CYCLE] Continue processing task backlog. Read memory-bank/tasks.md and memory-bank/backlog.md, pick the next pending task, and run the full pipeline.`);
2186
+ state.autoResumeAttempted = true;
2167
2187
  pluginLog.info(`[opencode-immune] Auto-cycle prompt sent to new session ${newSessionID}`);
2168
2188
  }
2169
2189
  catch (err) {
2170
2190
  pluginLog.error("[opencode-immune] Auto-cycle: Failed to create session or send prompt:", err);
2171
2191
  }
2192
+ finally {
2193
+ state.autoResumeInFlight = false;
2194
+ }
2172
2195
  }, 5_000);
2173
2196
  }
2174
2197
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-immune",
3
- "version": "1.0.60",
3
+ "version": "1.0.61",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
6
6
  "exports": {