opencode-immune 1.0.4 → 1.0.6
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 +40 -2
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -13,6 +13,7 @@ function createState(input) {
|
|
|
13
13
|
retryTimers: new Map(),
|
|
14
14
|
retryCount: new Map(),
|
|
15
15
|
managedSessionsCachePath: (0, path_1.join)(input.directory, ".opencode", "state", "opencode-immune-managed-sessions.json"),
|
|
16
|
+
diagnosticsLogPath: (0, path_1.join)(input.directory, ".opencode", "state", "opencode-immune-debug.log"),
|
|
16
17
|
lastEditAttempt: null,
|
|
17
18
|
toolCallCount: 0,
|
|
18
19
|
todoWriteUsed: false,
|
|
@@ -66,6 +67,17 @@ async function writeManagedSessionsCache(state) {
|
|
|
66
67
|
console.log(`[opencode-immune] Pruned ${removed} expired managed ultrawork session(s) while writing cache.`);
|
|
67
68
|
}
|
|
68
69
|
}
|
|
70
|
+
async function writeDiagnosticLog(state, event, data = {}) {
|
|
71
|
+
try {
|
|
72
|
+
const cacheDir = (0, path_1.join)(state.input.directory, ".opencode", "state");
|
|
73
|
+
await (0, promises_1.mkdir)(cacheDir, { recursive: true });
|
|
74
|
+
const line = JSON.stringify({ ts: new Date().toISOString(), event, ...data });
|
|
75
|
+
await (0, promises_1.appendFile)(state.diagnosticsLogPath, `${line}\n`, "utf-8");
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// diagnostics must never affect runtime behavior
|
|
79
|
+
}
|
|
80
|
+
}
|
|
69
81
|
async function loadManagedSessionsCache(state) {
|
|
70
82
|
try {
|
|
71
83
|
const raw = await (0, promises_1.readFile)(state.managedSessionsCachePath, "utf-8");
|
|
@@ -219,6 +231,26 @@ async function forceRetryManagedSession(state, sessionID, reason) {
|
|
|
219
231
|
const retryText = retryAgent === ULTRAWORK_AGENT
|
|
220
232
|
? "[SYSTEM: Previous API call failed with a transient error. Re-read memory-bank/tasks.md, check the Phase Status block, and continue the pipeline. Use the exact neutral prompt from your Step 5 table for the next router call. Do NOT analyze or evaluate file contents.]"
|
|
221
233
|
: `[SYSTEM: Previous API call failed with a transient error. Resume the current task in your current role as ${retryAgent}. Continue from the existing session state. Do not restart from scratch unless the current session state is missing.]`;
|
|
234
|
+
await writeDiagnosticLog(state, "force-retry:start", {
|
|
235
|
+
sessionID,
|
|
236
|
+
reason,
|
|
237
|
+
retryAgent,
|
|
238
|
+
managedKind: managedSession.kind,
|
|
239
|
+
rootSessionID: managedSession.rootSessionID,
|
|
240
|
+
fallbackModel,
|
|
241
|
+
});
|
|
242
|
+
try {
|
|
243
|
+
await state.input.client.session.abort({
|
|
244
|
+
path: { id: sessionID },
|
|
245
|
+
});
|
|
246
|
+
await writeDiagnosticLog(state, "force-retry:abort-success", { sessionID });
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
await writeDiagnosticLog(state, "force-retry:abort-failed", {
|
|
250
|
+
sessionID,
|
|
251
|
+
error: err instanceof Error ? err.message : String(err),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
222
254
|
await state.input.client.session.promptAsync({
|
|
223
255
|
body: {
|
|
224
256
|
...(fallbackModel ? { model: fallbackModel } : {}),
|
|
@@ -236,6 +268,12 @@ async function forceRetryManagedSession(state, sessionID, reason) {
|
|
|
236
268
|
(fallbackModel
|
|
237
269
|
? ` using fallback model ${fallbackModel.providerID}/${fallbackModel.modelID}`
|
|
238
270
|
: ""));
|
|
271
|
+
await writeDiagnosticLog(state, "force-retry:prompt-sent", {
|
|
272
|
+
sessionID,
|
|
273
|
+
reason,
|
|
274
|
+
retryAgent,
|
|
275
|
+
fallbackModel,
|
|
276
|
+
});
|
|
239
277
|
}
|
|
240
278
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
241
279
|
// UTILITY: ERROR BOUNDARY
|
|
@@ -784,8 +822,6 @@ const RATE_LIMIT_MESSAGE_PATTERN = /too many requests|rate_limit|rate limit/i;
|
|
|
784
822
|
function createMultiCycleHandler(state) {
|
|
785
823
|
return async (input, output) => {
|
|
786
824
|
const sessionID = input.sessionID;
|
|
787
|
-
if (!isManagedRootUltraworkSession(state, sessionID))
|
|
788
|
-
return;
|
|
789
825
|
// Extract text content from parts
|
|
790
826
|
const parts = output.parts ?? [];
|
|
791
827
|
let messageContent = "";
|
|
@@ -817,6 +853,8 @@ function createMultiCycleHandler(state) {
|
|
|
817
853
|
state.retryTimers.set(sessionID, timer);
|
|
818
854
|
}
|
|
819
855
|
}
|
|
856
|
+
if (!isManagedRootUltraworkSession(state, sessionID))
|
|
857
|
+
return;
|
|
820
858
|
// ── PRE_COMMIT: execute /commit ──
|
|
821
859
|
if (messageContent.includes(PRE_COMMIT_MARKER) && !state.commitPending) {
|
|
822
860
|
state.commitPending = true;
|