opencode-immune 1.0.79 → 1.0.80
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/server.js +92 -33
- package/package.json +1 -1
package/dist/plugin/server.js
CHANGED
|
@@ -3777,7 +3777,7 @@ import { fileURLToPath } from "url";
|
|
|
3777
3777
|
import { createHash } from "crypto";
|
|
3778
3778
|
import { tmpdir } from "os";
|
|
3779
3779
|
import { execFile } from "child_process";
|
|
3780
|
-
var PLUGIN_VERSION = "1.0.
|
|
3780
|
+
var PLUGIN_VERSION = "1.0.80";
|
|
3781
3781
|
var PLUGIN_PACKAGE_NAME = "opencode-immune";
|
|
3782
3782
|
var PLUGIN_DIRNAME = dirname(fileURLToPath(import.meta.url));
|
|
3783
3783
|
function getServerAuthHeaders() {
|
|
@@ -3876,6 +3876,7 @@ function createState(input) {
|
|
|
3876
3876
|
autoResumeInFlight: false,
|
|
3877
3877
|
autoCycleInFlight: false,
|
|
3878
3878
|
autoCycleSourceSessions: /* @__PURE__ */ new Set(),
|
|
3879
|
+
autoCycleFallbackTimers: /* @__PURE__ */ new Map(),
|
|
3879
3880
|
autoCycleLockPath: join(
|
|
3880
3881
|
input.directory,
|
|
3881
3882
|
".opencode",
|
|
@@ -3895,6 +3896,7 @@ var PROVIDER_RETRY_WATCHDOG_MS = 3e4;
|
|
|
3895
3896
|
var RETRY_PROMPT_DELIVERY_ATTEMPTS = 3;
|
|
3896
3897
|
var CHILD_FALLBACK_REQUEST_TTL_MS = 10 * 60 * 1e3;
|
|
3897
3898
|
var AUTO_CYCLE_LOCK_TTL_MS = 30 * 60 * 1e3;
|
|
3899
|
+
var FALLBACK_AUTO_CYCLE_DELAY_MS = 6e4;
|
|
3898
3900
|
var MODEL_NAME_CAPABILITY_SCORE = {
|
|
3899
3901
|
"claude-opus-4-7": 100,
|
|
3900
3902
|
"gpt-5.5": 100,
|
|
@@ -3914,6 +3916,13 @@ function isManagedRootUltraworkSession(state, sessionID) {
|
|
|
3914
3916
|
const record = getManagedSession(state, sessionID);
|
|
3915
3917
|
return !!record && record.kind === "root";
|
|
3916
3918
|
}
|
|
3919
|
+
function hasRecentManagedChildSessionForRoot(state, rootSessionID, sinceMs) {
|
|
3920
|
+
const cutoff = Date.now() - sinceMs;
|
|
3921
|
+
for (const record of state.managedUltraworkSessions.values()) {
|
|
3922
|
+
if (record.kind === "child" && record.rootSessionID === rootSessionID && record.updatedAt >= cutoff) return true;
|
|
3923
|
+
}
|
|
3924
|
+
return false;
|
|
3925
|
+
}
|
|
3917
3926
|
async function createManagedUltraworkSession(state, title) {
|
|
3918
3927
|
const result = await state.client.session.create({
|
|
3919
3928
|
directory: state.input.directory,
|
|
@@ -4110,6 +4119,11 @@ async function addManagedUltraworkSession(state, sessionID, timestamp = Date.now
|
|
|
4110
4119
|
async function addManagedChildSession(state, sessionID, parentSessionID, timestamp = Date.now()) {
|
|
4111
4120
|
const parent = state.managedUltraworkSessions.get(parentSessionID);
|
|
4112
4121
|
if (!parent) return;
|
|
4122
|
+
cancelFallbackAutoCycle(
|
|
4123
|
+
state,
|
|
4124
|
+
parent.rootSessionID,
|
|
4125
|
+
`child session ${sessionID} created`
|
|
4126
|
+
);
|
|
4113
4127
|
cancelProviderRetryWatchdog(
|
|
4114
4128
|
state,
|
|
4115
4129
|
parent.rootSessionID,
|
|
@@ -4155,8 +4169,13 @@ function cancelProviderRetryWatchdog(state, sessionID, reason) {
|
|
|
4155
4169
|
);
|
|
4156
4170
|
}
|
|
4157
4171
|
async function removeManagedUltraworkSession(state, sessionID, reason) {
|
|
4172
|
+
const existing = state.managedUltraworkSessions.get(sessionID);
|
|
4158
4173
|
cancelPendingSessionRetry(state, sessionID, reason);
|
|
4159
4174
|
cancelProviderRetryWatchdog(state, sessionID, reason);
|
|
4175
|
+
cancelFallbackAutoCycle(state, sessionID, reason);
|
|
4176
|
+
if (existing?.kind === "child") {
|
|
4177
|
+
cancelFallbackAutoCycle(state, existing.rootSessionID, `child session ${sessionID} ${reason}`);
|
|
4178
|
+
}
|
|
4160
4179
|
state.sessionErrorRetryCount.delete(sessionID);
|
|
4161
4180
|
const existed = state.managedUltraworkSessions.delete(sessionID);
|
|
4162
4181
|
if (!existed) return;
|
|
@@ -5760,7 +5779,15 @@ function createEventHandler(state) {
|
|
|
5760
5779
|
state.sessionErrorRetryCount.delete(sessionID);
|
|
5761
5780
|
if (markUltraworkSessionActive(state, sessionID)) {
|
|
5762
5781
|
}
|
|
5782
|
+
if (managedSession?.kind === "root") {
|
|
5783
|
+
cancelFallbackAutoCycle(state, sessionID, "root session updated");
|
|
5784
|
+
}
|
|
5763
5785
|
if (managedSession?.kind === "child") {
|
|
5786
|
+
cancelFallbackAutoCycle(
|
|
5787
|
+
state,
|
|
5788
|
+
managedSession.rootSessionID,
|
|
5789
|
+
`child session ${sessionID} updated`
|
|
5790
|
+
);
|
|
5764
5791
|
cancelProviderRetryWatchdog(
|
|
5765
5792
|
state,
|
|
5766
5793
|
managedSession.rootSessionID,
|
|
@@ -5802,6 +5829,64 @@ async function commitCycleChanges(state, reason) {
|
|
|
5802
5829
|
state.commitPending = false;
|
|
5803
5830
|
}
|
|
5804
5831
|
}
|
|
5832
|
+
function cancelFallbackAutoCycle(state, sessionID, reason) {
|
|
5833
|
+
const timer = state.autoCycleFallbackTimers.get(sessionID);
|
|
5834
|
+
if (!timer) return;
|
|
5835
|
+
clearTimeout(timer);
|
|
5836
|
+
state.autoCycleFallbackTimers.delete(sessionID);
|
|
5837
|
+
pluginLog.info(
|
|
5838
|
+
`[opencode-immune] Cancelled fallback AUTO-CYCLE for session ${sessionID}: ${reason}`
|
|
5839
|
+
);
|
|
5840
|
+
}
|
|
5841
|
+
function scheduleFallbackAutoCycle(state, sessionID) {
|
|
5842
|
+
if (state.autoCycleFallbackTimers.has(sessionID)) return;
|
|
5843
|
+
const timer = setTimeout(async () => {
|
|
5844
|
+
state.autoCycleFallbackTimers.delete(sessionID);
|
|
5845
|
+
if (!isManagedRootUltraworkSession(state, sessionID)) return;
|
|
5846
|
+
if (state.autoCycleInFlight || state.autoCycleSourceSessions.has(sessionID)) return;
|
|
5847
|
+
if (hasRecentManagedChildSessionForRoot(state, sessionID, FALLBACK_AUTO_CYCLE_DELAY_MS)) {
|
|
5848
|
+
scheduleFallbackAutoCycle(state, sessionID);
|
|
5849
|
+
return;
|
|
5850
|
+
}
|
|
5851
|
+
const recovery = await parseTasksFile(state.input.directory);
|
|
5852
|
+
if (recovery) return;
|
|
5853
|
+
const hasPendingTasks = await hasPendingBacklogTasks(state.input.directory);
|
|
5854
|
+
if (!hasPendingTasks) return;
|
|
5855
|
+
const lockAcquired = await acquireAutoCycleLock(
|
|
5856
|
+
state,
|
|
5857
|
+
"multi-cycle-fallback",
|
|
5858
|
+
sessionID
|
|
5859
|
+
);
|
|
5860
|
+
if (!lockAcquired) return;
|
|
5861
|
+
state.autoCycleSourceSessions.add(sessionID);
|
|
5862
|
+
state.autoCycleInFlight = true;
|
|
5863
|
+
pluginLog.warn(
|
|
5864
|
+
`[opencode-immune] Multi-Cycle fallback: no CYCLE_COMPLETE marker detected for ${sessionID} after ${FALLBACK_AUTO_CYCLE_DELAY_MS}ms grace period. Starting AUTO-CYCLE.`
|
|
5865
|
+
);
|
|
5866
|
+
try {
|
|
5867
|
+
await commitCycleChanges(state, "fallback AUTO-CYCLE before new session");
|
|
5868
|
+
await refreshAutoCycleLock(state, sessionID);
|
|
5869
|
+
await startAutoCycleInNewSession(
|
|
5870
|
+
state,
|
|
5871
|
+
{
|
|
5872
|
+
sourceSessionID: sessionID,
|
|
5873
|
+
logContext: "Multi-Cycle fallback",
|
|
5874
|
+
clearLockOnFailureReason: "fallback bootstrap failed",
|
|
5875
|
+
retireSourceSession: true
|
|
5876
|
+
}
|
|
5877
|
+
);
|
|
5878
|
+
} catch (err) {
|
|
5879
|
+
state.autoCycleSourceSessions.delete(sessionID);
|
|
5880
|
+
pluginLog.error("[opencode-immune] Multi-Cycle fallback: Failed to send prompt:", err);
|
|
5881
|
+
} finally {
|
|
5882
|
+
state.autoCycleInFlight = false;
|
|
5883
|
+
}
|
|
5884
|
+
}, FALLBACK_AUTO_CYCLE_DELAY_MS);
|
|
5885
|
+
state.autoCycleFallbackTimers.set(sessionID, timer);
|
|
5886
|
+
pluginLog.info(
|
|
5887
|
+
`[opencode-immune] Multi-Cycle fallback: scheduled delayed AUTO-CYCLE check for ${sessionID}.`
|
|
5888
|
+
);
|
|
5889
|
+
}
|
|
5805
5890
|
async function archiveProgress(directory) {
|
|
5806
5891
|
const progressPath = join(directory, "memory-bank", "progress.md");
|
|
5807
5892
|
try {
|
|
@@ -5903,23 +5988,26 @@ function createTextCompleteHandler(state) {
|
|
|
5903
5988
|
);
|
|
5904
5989
|
}
|
|
5905
5990
|
if (text.includes(ALL_CYCLES_COMPLETE_MARKER)) {
|
|
5991
|
+
cancelFallbackAutoCycle(state, sessionID, "ALL_CYCLES_COMPLETE detected");
|
|
5906
5992
|
await clearUltraworkMarker(state);
|
|
5907
5993
|
await clearAutoCycleLock(state, "all cycles complete");
|
|
5908
5994
|
pluginLog.info("[opencode-immune] Multi-Cycle: ALL_CYCLES_COMPLETE detected, marker cleared.");
|
|
5909
5995
|
return;
|
|
5910
5996
|
}
|
|
5911
5997
|
if (text.includes(PRE_COMMIT_MARKER) && !text.includes(CYCLE_COMPLETE_MARKER)) {
|
|
5998
|
+
cancelFallbackAutoCycle(state, sessionID, "PRE_COMMIT detected");
|
|
5912
5999
|
await commitCycleChanges(state, "PRE_COMMIT detected (standalone)");
|
|
5913
6000
|
return;
|
|
5914
6001
|
}
|
|
5915
6002
|
if (text.includes(CYCLE_COMPLETE_MARKER)) {
|
|
6003
|
+
cancelFallbackAutoCycle(state, sessionID, "CYCLE_COMPLETE detected");
|
|
5916
6004
|
if (state.autoCycleInFlight || state.autoCycleSourceSessions.has(sessionID)) return;
|
|
5917
|
-
const
|
|
6005
|
+
const lockAcquired = await acquireAutoCycleLock(
|
|
5918
6006
|
state,
|
|
5919
6007
|
"cycle-complete",
|
|
5920
6008
|
sessionID
|
|
5921
6009
|
);
|
|
5922
|
-
if (!
|
|
6010
|
+
if (!lockAcquired) return;
|
|
5923
6011
|
state.autoCycleSourceSessions.add(sessionID);
|
|
5924
6012
|
state.autoCycleInFlight = true;
|
|
5925
6013
|
try {
|
|
@@ -5966,36 +6054,7 @@ function createTextCompleteHandler(state) {
|
|
|
5966
6054
|
if (recovery) return;
|
|
5967
6055
|
const hasPendingTasks = await hasPendingBacklogTasks(state.input.directory);
|
|
5968
6056
|
if (!hasPendingTasks) return;
|
|
5969
|
-
|
|
5970
|
-
const lockAcquired = await acquireAutoCycleLock(
|
|
5971
|
-
state,
|
|
5972
|
-
"multi-cycle-fallback",
|
|
5973
|
-
sessionID
|
|
5974
|
-
);
|
|
5975
|
-
if (!lockAcquired) return;
|
|
5976
|
-
state.autoCycleSourceSessions.add(sessionID);
|
|
5977
|
-
state.autoCycleInFlight = true;
|
|
5978
|
-
pluginLog.warn(
|
|
5979
|
-
`[opencode-immune] Multi-Cycle fallback: no CYCLE_COMPLETE marker detected for ${sessionID}, but tasks.md has no active task and backlog has pending items. Starting AUTO-CYCLE.`
|
|
5980
|
-
);
|
|
5981
|
-
try {
|
|
5982
|
-
await commitCycleChanges(state, "fallback AUTO-CYCLE before new session");
|
|
5983
|
-
await refreshAutoCycleLock(state, sessionID);
|
|
5984
|
-
await startAutoCycleInNewSession(
|
|
5985
|
-
state,
|
|
5986
|
-
{
|
|
5987
|
-
sourceSessionID: sessionID,
|
|
5988
|
-
logContext: "Multi-Cycle fallback",
|
|
5989
|
-
clearLockOnFailureReason: "fallback bootstrap failed",
|
|
5990
|
-
retireSourceSession: true
|
|
5991
|
-
}
|
|
5992
|
-
);
|
|
5993
|
-
} catch (err) {
|
|
5994
|
-
state.autoCycleSourceSessions.delete(sessionID);
|
|
5995
|
-
pluginLog.error("[opencode-immune] Multi-Cycle fallback: Failed to send prompt:", err);
|
|
5996
|
-
} finally {
|
|
5997
|
-
state.autoCycleInFlight = false;
|
|
5998
|
-
}
|
|
6057
|
+
scheduleFallbackAutoCycle(state, sessionID);
|
|
5999
6058
|
};
|
|
6000
6059
|
}
|
|
6001
6060
|
function createMultiCycleHandler(state) {
|