opencode-immune 1.0.64 → 1.0.66
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 +83 -5
- 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.66";
|
|
3781
3781
|
var PLUGIN_PACKAGE_NAME = "opencode-immune";
|
|
3782
3782
|
var PLUGIN_DIRNAME = dirname(fileURLToPath(import.meta.url));
|
|
3783
3783
|
function getServerAuthHeaders() {
|
|
@@ -3873,6 +3873,8 @@ function createState(input) {
|
|
|
3873
3873
|
sessionActive: false,
|
|
3874
3874
|
autoResumeAttempted: false,
|
|
3875
3875
|
autoResumeInFlight: false,
|
|
3876
|
+
autoCycleInFlight: false,
|
|
3877
|
+
autoCycleSourceSessions: /* @__PURE__ */ new Set(),
|
|
3876
3878
|
cycleCount: 0,
|
|
3877
3879
|
commitPending: false,
|
|
3878
3880
|
pluginUpdateMessage: null
|
|
@@ -3966,6 +3968,7 @@ async function applyUltraworkSessionPermissions(state, sessionID) {
|
|
|
3966
3968
|
}
|
|
3967
3969
|
}
|
|
3968
3970
|
async function promptManagedSession(state, sessionID, text, options = {}) {
|
|
3971
|
+
await applyUltraworkSessionPermissions(state, sessionID);
|
|
3969
3972
|
const result = await state.client.session.promptAsync({
|
|
3970
3973
|
directory: state.input.directory,
|
|
3971
3974
|
sessionID,
|
|
@@ -4726,6 +4729,15 @@ async function parseTasksFile(directory) {
|
|
|
4726
4729
|
return null;
|
|
4727
4730
|
}
|
|
4728
4731
|
}
|
|
4732
|
+
async function hasPendingBacklogTasks(directory) {
|
|
4733
|
+
try {
|
|
4734
|
+
const backlogPath = join(directory, "memory-bank", "backlog.md");
|
|
4735
|
+
const backlogContent = await readFile(backlogPath, "utf-8");
|
|
4736
|
+
return /- \[ \]/.test(backlogContent);
|
|
4737
|
+
} catch {
|
|
4738
|
+
return false;
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4729
4741
|
var HARNESS_VERSION_FILE = ".harness-version";
|
|
4730
4742
|
var HARNESS_TOKEN_ENV = "OPENCODE_IMMUNE_TOKEN";
|
|
4731
4743
|
var DEFAULT_HARNESS_REPO = "gendoor/opencode-immune-harness";
|
|
@@ -4826,6 +4838,24 @@ async function readLocalHarnessVersion(directory) {
|
|
|
4826
4838
|
return null;
|
|
4827
4839
|
}
|
|
4828
4840
|
}
|
|
4841
|
+
function parseHarnessVersion(tag) {
|
|
4842
|
+
const match = tag.match(/^v(\d{4}\.\d{2}\.\d{2})(?:-(.+))?$/);
|
|
4843
|
+
if (!match?.[1]) return null;
|
|
4844
|
+
return {
|
|
4845
|
+
dateKey: match[1],
|
|
4846
|
+
suffix: match[2] ?? ""
|
|
4847
|
+
};
|
|
4848
|
+
}
|
|
4849
|
+
function compareHarnessVersions(localVersion, releaseVersion) {
|
|
4850
|
+
if (localVersion === releaseVersion) return 0;
|
|
4851
|
+
const local = parseHarnessVersion(localVersion);
|
|
4852
|
+
const release = parseHarnessVersion(releaseVersion);
|
|
4853
|
+
if (!local || !release) return null;
|
|
4854
|
+
if (local.dateKey < release.dateKey) return -1;
|
|
4855
|
+
if (local.dateKey > release.dateKey) return 1;
|
|
4856
|
+
if (local.suffix !== release.suffix) return null;
|
|
4857
|
+
return 0;
|
|
4858
|
+
}
|
|
4829
4859
|
async function downloadHarnessAsset(assetUrl, token) {
|
|
4830
4860
|
const resp = await fetch(assetUrl, {
|
|
4831
4861
|
headers: {
|
|
@@ -4894,6 +4924,21 @@ async function syncHarness(state) {
|
|
|
4894
4924
|
);
|
|
4895
4925
|
return;
|
|
4896
4926
|
}
|
|
4927
|
+
if (localVersion) {
|
|
4928
|
+
const versionOrder = compareHarnessVersions(localVersion, release.tagName);
|
|
4929
|
+
if (versionOrder === null) {
|
|
4930
|
+
pluginLog.warn(
|
|
4931
|
+
`[opencode-immune] Harness sync: local ${localVersion} and release ${release.tagName} have ambiguous ordering. Skipping sync to avoid overwriting project harness.`
|
|
4932
|
+
);
|
|
4933
|
+
return;
|
|
4934
|
+
}
|
|
4935
|
+
if (versionOrder > 0) {
|
|
4936
|
+
pluginLog.info(
|
|
4937
|
+
`[opencode-immune] Harness sync: local ${localVersion} is newer than release ${release.tagName}. Skipping sync.`
|
|
4938
|
+
);
|
|
4939
|
+
return;
|
|
4940
|
+
}
|
|
4941
|
+
}
|
|
4897
4942
|
pluginLog.info(
|
|
4898
4943
|
`[opencode-immune] Harness sync: updating ${localVersion ?? "(none)"} \u2192 ${release.tagName}`
|
|
4899
4944
|
);
|
|
@@ -5479,8 +5524,9 @@ function createTextCompleteHandler(state) {
|
|
|
5479
5524
|
const sessionID = input.sessionID;
|
|
5480
5525
|
const text = output.text ?? "";
|
|
5481
5526
|
if (!text) return;
|
|
5527
|
+
const isManagedRootSession = isManagedRootUltraworkSession(state, sessionID);
|
|
5482
5528
|
cancelProviderRetryWatchdog(state, sessionID, "assistant text completed");
|
|
5483
|
-
if (isProviderRetryBanner(text) &&
|
|
5529
|
+
if (isProviderRetryBanner(text) && isManagedRootSession) {
|
|
5484
5530
|
const fallbackModel = getSessionFallbackModel(state, sessionID);
|
|
5485
5531
|
await setSessionFallbackModel(state, sessionID, fallbackModel);
|
|
5486
5532
|
scheduleManagedSessionRetry(state, sessionID, {
|
|
@@ -5562,6 +5608,40 @@ function createTextCompleteHandler(state) {
|
|
|
5562
5608
|
} catch (err) {
|
|
5563
5609
|
pluginLog.error("[opencode-immune] Multi-Cycle: Failed to create session or send prompt:", err);
|
|
5564
5610
|
}
|
|
5611
|
+
return;
|
|
5612
|
+
}
|
|
5613
|
+
if (!isManagedRootSession || state.autoCycleInFlight) return;
|
|
5614
|
+
const recovery = await parseTasksFile(state.input.directory);
|
|
5615
|
+
if (recovery) return;
|
|
5616
|
+
const hasPendingTasks = await hasPendingBacklogTasks(state.input.directory);
|
|
5617
|
+
if (!hasPendingTasks) return;
|
|
5618
|
+
if (state.autoCycleSourceSessions.has(sessionID)) return;
|
|
5619
|
+
state.autoCycleSourceSessions.add(sessionID);
|
|
5620
|
+
state.autoCycleInFlight = true;
|
|
5621
|
+
pluginLog.warn(
|
|
5622
|
+
`[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.`
|
|
5623
|
+
);
|
|
5624
|
+
try {
|
|
5625
|
+
const newSessionID = await createManagedUltraworkSession(
|
|
5626
|
+
state,
|
|
5627
|
+
`AUTO-CYCLE: next backlog task`
|
|
5628
|
+
);
|
|
5629
|
+
if (!newSessionID) {
|
|
5630
|
+
pluginLog.error("[opencode-immune] Multi-Cycle fallback: Failed to create new session \u2014 no ID returned.");
|
|
5631
|
+
return;
|
|
5632
|
+
}
|
|
5633
|
+
pluginLog.info(`[opencode-immune] Multi-Cycle fallback: New session created: ${newSessionID}`);
|
|
5634
|
+
await promptManagedSession(
|
|
5635
|
+
state,
|
|
5636
|
+
newSessionID,
|
|
5637
|
+
`[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.`
|
|
5638
|
+
);
|
|
5639
|
+
pluginLog.info(`[opencode-immune] Multi-Cycle fallback: Bootstrap prompt sent to ${newSessionID}`);
|
|
5640
|
+
} catch (err) {
|
|
5641
|
+
state.autoCycleSourceSessions.delete(sessionID);
|
|
5642
|
+
pluginLog.error("[opencode-immune] Multi-Cycle fallback: Failed to create session or send prompt:", err);
|
|
5643
|
+
} finally {
|
|
5644
|
+
state.autoCycleInFlight = false;
|
|
5565
5645
|
}
|
|
5566
5646
|
};
|
|
5567
5647
|
}
|
|
@@ -5675,9 +5755,7 @@ async function server(input) {
|
|
|
5675
5755
|
}, 5e3);
|
|
5676
5756
|
} else {
|
|
5677
5757
|
try {
|
|
5678
|
-
const
|
|
5679
|
-
const backlogContent = await readFile(backlogPath, "utf-8");
|
|
5680
|
-
const hasPendingTasks = /- \[ \]/.test(backlogContent);
|
|
5758
|
+
const hasPendingTasks = await hasPendingBacklogTasks(state.input.directory);
|
|
5681
5759
|
if (hasPendingTasks) {
|
|
5682
5760
|
pluginLog.info(
|
|
5683
5761
|
`[opencode-immune] Plugin init: no active task but backlog has pending items. Will create new session to start next cycle.`
|