opencode-immune 1.0.59 → 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.
- package/dist/plugin.js +56 -7
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -96,6 +96,7 @@ function createState(input) {
|
|
|
96
96
|
providerRetryWatchdogs: new Map(),
|
|
97
97
|
childFallbackRequests: new Map(),
|
|
98
98
|
sessionErrorRetryCount: new Map(),
|
|
99
|
+
ultraworkPermissionSessions: new Set(),
|
|
99
100
|
fallbackAgentByAgent: new Map(),
|
|
100
101
|
baseAgentByFallbackAgent: new Map(),
|
|
101
102
|
ultraworkMarkerPath: join(input.directory, ".opencode", "state", "ultrawork-active.json"),
|
|
@@ -106,6 +107,7 @@ function createState(input) {
|
|
|
106
107
|
approximateTokens: 0,
|
|
107
108
|
sessionActive: false,
|
|
108
109
|
autoResumeAttempted: false,
|
|
110
|
+
autoResumeInFlight: false,
|
|
109
111
|
cycleCount: 0,
|
|
110
112
|
commitPending: false,
|
|
111
113
|
pluginUpdateMessage: null,
|
|
@@ -174,6 +176,25 @@ async function createManagedUltraworkSession(state, title) {
|
|
|
174
176
|
await addManagedUltraworkSession(state, sessionID);
|
|
175
177
|
return sessionID;
|
|
176
178
|
}
|
|
179
|
+
async function applyUltraworkSessionPermissions(state, sessionID) {
|
|
180
|
+
if (state.ultraworkPermissionSessions.has(sessionID))
|
|
181
|
+
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);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
177
198
|
async function promptManagedSession(state, sessionID, text, options = {}) {
|
|
178
199
|
const result = await state.client.session.promptAsync({
|
|
179
200
|
directory: state.input.directory,
|
|
@@ -336,6 +357,7 @@ async function addManagedUltraworkSession(state, sessionID, timestamp = Date.now
|
|
|
336
357
|
return;
|
|
337
358
|
}
|
|
338
359
|
state.managedUltraworkSessions.set(sessionID, nextRecord);
|
|
360
|
+
await applyUltraworkSessionPermissions(state, sessionID);
|
|
339
361
|
}
|
|
340
362
|
async function addManagedChildSession(state, sessionID, parentSessionID, timestamp = Date.now()) {
|
|
341
363
|
const parent = state.managedUltraworkSessions.get(parentSessionID);
|
|
@@ -379,6 +401,7 @@ async function removeManagedUltraworkSession(state, sessionID, reason) {
|
|
|
379
401
|
cancelPendingSessionRetry(state, sessionID, reason);
|
|
380
402
|
cancelProviderRetryWatchdog(state, sessionID, reason);
|
|
381
403
|
state.sessionErrorRetryCount.delete(sessionID);
|
|
404
|
+
state.ultraworkPermissionSessions.delete(sessionID);
|
|
382
405
|
const existed = state.managedUltraworkSessions.delete(sessionID);
|
|
383
406
|
if (!existed)
|
|
384
407
|
return;
|
|
@@ -1377,18 +1400,23 @@ function createSessionRecoveryEvent(state) {
|
|
|
1377
1400
|
// Register this root session as managed so retry/recovery works
|
|
1378
1401
|
await addManagedUltraworkSession(state, sessionID);
|
|
1379
1402
|
// Skip sending AUTO-RESUME if already sent from plugin init
|
|
1380
|
-
if (state.autoResumeAttempted) {
|
|
1381
|
-
pluginLog.info(`[opencode-immune] Auto-resume already sent
|
|
1403
|
+
if (state.autoResumeAttempted || state.autoResumeInFlight) {
|
|
1404
|
+
pluginLog.info(`[opencode-immune] Auto-resume already sent or in flight, skipping duplicate for session ${sessionID}.`);
|
|
1382
1405
|
return;
|
|
1383
1406
|
}
|
|
1407
|
+
state.autoResumeInFlight = true;
|
|
1384
1408
|
setTimeout(async () => {
|
|
1385
1409
|
try {
|
|
1386
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;
|
|
1387
1412
|
pluginLog.info(`[opencode-immune] Auto-resume prompt sent to managed ultrawork session ${sessionID}`);
|
|
1388
1413
|
}
|
|
1389
1414
|
catch (err) {
|
|
1390
1415
|
pluginLog.info(`[opencode-immune] Auto-resume failed (session may have been taken over):`, err);
|
|
1391
1416
|
}
|
|
1417
|
+
finally {
|
|
1418
|
+
state.autoResumeInFlight = false;
|
|
1419
|
+
}
|
|
1392
1420
|
}, 3_000);
|
|
1393
1421
|
}
|
|
1394
1422
|
}
|
|
@@ -1614,8 +1642,7 @@ function createFallbackModels(state) {
|
|
|
1614
1642
|
scheduleProviderRetryWatchdog(state, input.sessionID);
|
|
1615
1643
|
// First contact with 0-ultrawork after plugin restart:
|
|
1616
1644
|
// if marker is active and tasks.md has incomplete work, send AUTO-RESUME prompt.
|
|
1617
|
-
if (!wasAlreadyManaged && !state.autoResumeAttempted) {
|
|
1618
|
-
state.autoResumeAttempted = true;
|
|
1645
|
+
if (!wasAlreadyManaged && !state.autoResumeAttempted && !state.autoResumeInFlight) {
|
|
1619
1646
|
const markerActive = await isUltraworkMarkerActive(state);
|
|
1620
1647
|
if (markerActive) {
|
|
1621
1648
|
const recovery = await parseTasksFile(state.input.directory);
|
|
@@ -1625,14 +1652,19 @@ function createFallbackModels(state) {
|
|
|
1625
1652
|
`task="${recovery.task}", level=${recovery.level}, phase=${recovery.phase}. ` +
|
|
1626
1653
|
`Sending AUTO-RESUME prompt in 3s...`);
|
|
1627
1654
|
const sid = input.sessionID;
|
|
1655
|
+
state.autoResumeInFlight = true;
|
|
1628
1656
|
setTimeout(async () => {
|
|
1629
1657
|
try {
|
|
1630
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;
|
|
1631
1660
|
pluginLog.info(`[opencode-immune] Auto-resume prompt sent to session ${sid}`);
|
|
1632
1661
|
}
|
|
1633
1662
|
catch (err) {
|
|
1634
1663
|
pluginLog.info(`[opencode-immune] Auto-resume prompt failed:`, err);
|
|
1635
1664
|
}
|
|
1665
|
+
finally {
|
|
1666
|
+
state.autoResumeInFlight = false;
|
|
1667
|
+
}
|
|
1636
1668
|
}, 3_000);
|
|
1637
1669
|
}
|
|
1638
1670
|
}
|
|
@@ -2066,8 +2098,17 @@ function createMultiCycleHandler(state) {
|
|
|
2066
2098
|
function createPermissionAskHandler(state) {
|
|
2067
2099
|
return async (input, output) => {
|
|
2068
2100
|
const sessionID = input.sessionID;
|
|
2069
|
-
if (!
|
|
2101
|
+
if (!sessionID)
|
|
2070
2102
|
return;
|
|
2103
|
+
if (!isManagedUltraworkSession(state, sessionID)) {
|
|
2104
|
+
const markerActive = await isUltraworkMarkerActive(state);
|
|
2105
|
+
const recovery = markerActive ? await parseTasksFile(state.input.directory) : null;
|
|
2106
|
+
if (!recovery || recovery.phase === "ARCHIVE: DONE")
|
|
2107
|
+
return;
|
|
2108
|
+
state.recoveryContext = recovery;
|
|
2109
|
+
await addManagedUltraworkSession(state, sessionID);
|
|
2110
|
+
pluginLog.info(`[opencode-immune] Permission request recovered AUTO-RESUME session ${sessionID}; applying managed ultrawork auto-allow.`);
|
|
2111
|
+
}
|
|
2071
2112
|
output.status = "allow";
|
|
2072
2113
|
await writeDiagnosticLog(state, "permission:auto-allow", {
|
|
2073
2114
|
sessionID,
|
|
@@ -2097,12 +2138,12 @@ async function server(input) {
|
|
|
2097
2138
|
if (recovery && recovery.phase !== "ARCHIVE: DONE") {
|
|
2098
2139
|
// Active task exists with incomplete phases — resume it
|
|
2099
2140
|
state.recoveryContext = recovery;
|
|
2100
|
-
state.autoResumeAttempted = true;
|
|
2101
2141
|
pluginLog.info(`[opencode-immune] Plugin init: ultrawork marker active, recovery context loaded: ` +
|
|
2102
2142
|
`task="${recovery.task}", level=${recovery.level}, phase=${recovery.phase}. ` +
|
|
2103
2143
|
`Will create new session and send AUTO-RESUME.`);
|
|
2104
2144
|
// Create a new session and send AUTO-RESUME prompt (same pattern as CYCLE_COMPLETE).
|
|
2105
2145
|
// Delay to let opencode fully initialize.
|
|
2146
|
+
state.autoResumeInFlight = true;
|
|
2106
2147
|
setTimeout(async () => {
|
|
2107
2148
|
try {
|
|
2108
2149
|
const newSessionID = await createManagedUltraworkSession(state, `AUTO-RESUME: ${recovery.task}`);
|
|
@@ -2112,11 +2153,15 @@ async function server(input) {
|
|
|
2112
2153
|
}
|
|
2113
2154
|
pluginLog.info(`[opencode-immune] Auto-resume: New session created: ${newSessionID}`);
|
|
2114
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;
|
|
2115
2157
|
pluginLog.info(`[opencode-immune] Auto-resume prompt sent to new session ${newSessionID}`);
|
|
2116
2158
|
}
|
|
2117
2159
|
catch (err) {
|
|
2118
2160
|
pluginLog.error("[opencode-immune] Auto-resume: Failed to create session or send prompt:", err);
|
|
2119
2161
|
}
|
|
2162
|
+
finally {
|
|
2163
|
+
state.autoResumeInFlight = false;
|
|
2164
|
+
}
|
|
2120
2165
|
}, 5_000);
|
|
2121
2166
|
}
|
|
2122
2167
|
else {
|
|
@@ -2126,9 +2171,9 @@ async function server(input) {
|
|
|
2126
2171
|
const backlogContent = await readFile(backlogPath, "utf-8");
|
|
2127
2172
|
const hasPendingTasks = /- \[ \]/.test(backlogContent);
|
|
2128
2173
|
if (hasPendingTasks) {
|
|
2129
|
-
state.autoResumeAttempted = true;
|
|
2130
2174
|
pluginLog.info(`[opencode-immune] Plugin init: no active task but backlog has pending items. ` +
|
|
2131
2175
|
`Will create new session to start next cycle.`);
|
|
2176
|
+
state.autoResumeInFlight = true;
|
|
2132
2177
|
setTimeout(async () => {
|
|
2133
2178
|
try {
|
|
2134
2179
|
const newSessionID = await createManagedUltraworkSession(state, `AUTO-CYCLE: next backlog task`);
|
|
@@ -2138,11 +2183,15 @@ async function server(input) {
|
|
|
2138
2183
|
}
|
|
2139
2184
|
pluginLog.info(`[opencode-immune] Auto-cycle: New session created: ${newSessionID}`);
|
|
2140
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;
|
|
2141
2187
|
pluginLog.info(`[opencode-immune] Auto-cycle prompt sent to new session ${newSessionID}`);
|
|
2142
2188
|
}
|
|
2143
2189
|
catch (err) {
|
|
2144
2190
|
pluginLog.error("[opencode-immune] Auto-cycle: Failed to create session or send prompt:", err);
|
|
2145
2191
|
}
|
|
2192
|
+
finally {
|
|
2193
|
+
state.autoResumeInFlight = false;
|
|
2194
|
+
}
|
|
2146
2195
|
}, 5_000);
|
|
2147
2196
|
}
|
|
2148
2197
|
else {
|