opencode-immune 1.0.70 → 1.0.72
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 +95 -24
- 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.72";
|
|
3781
3781
|
var PLUGIN_PACKAGE_NAME = "opencode-immune";
|
|
3782
3782
|
var PLUGIN_DIRNAME = dirname(fileURLToPath(import.meta.url));
|
|
3783
3783
|
function getServerAuthHeaders() {
|
|
@@ -3848,6 +3848,7 @@ function createState(input) {
|
|
|
3848
3848
|
recoveryContext: null,
|
|
3849
3849
|
managedUltraworkSessions: /* @__PURE__ */ new Map(),
|
|
3850
3850
|
sessionRetryTimers: /* @__PURE__ */ new Map(),
|
|
3851
|
+
abortedMessageRetries: /* @__PURE__ */ new Set(),
|
|
3851
3852
|
providerRetryWatchdogs: /* @__PURE__ */ new Map(),
|
|
3852
3853
|
childFallbackRequests: /* @__PURE__ */ new Map(),
|
|
3853
3854
|
sessionErrorRetryCount: /* @__PURE__ */ new Map(),
|
|
@@ -3918,11 +3919,15 @@ var CHILD_SESSION_FALLBACK_MODEL = {
|
|
|
3918
3919
|
providerID: "claudehub",
|
|
3919
3920
|
modelID: "claude-opus-4-7"
|
|
3920
3921
|
};
|
|
3922
|
+
var HIGH_CAPABILITY_FALLBACK_MODELS = [
|
|
3923
|
+
{ providerID: "codexsale", modelID: "gpt-5.5" },
|
|
3924
|
+
{ providerID: "codexsale", modelID: "gpt-5.4" }
|
|
3925
|
+
];
|
|
3921
3926
|
var AUTO_CYCLE_LOCK_TTL_MS = 30 * 60 * 1e3;
|
|
3922
3927
|
var FALLBACK_MODEL_CANDIDATES = [
|
|
3923
3928
|
CHILD_SESSION_FALLBACK_MODEL,
|
|
3924
|
-
|
|
3925
|
-
|
|
3929
|
+
...HIGH_CAPABILITY_FALLBACK_MODELS,
|
|
3930
|
+
RATE_LIMIT_FALLBACK_MODEL
|
|
3926
3931
|
];
|
|
3927
3932
|
var FALLBACK_AGENT_SUFFIX = "-provider-fallback";
|
|
3928
3933
|
function isManagedUltraworkSession(state, sessionID) {
|
|
@@ -4346,6 +4351,46 @@ function getRetryableErrorType(error) {
|
|
|
4346
4351
|
if (action === "retry") return `transient provider error${statusSuffix}`;
|
|
4347
4352
|
return "non-retryable provider error";
|
|
4348
4353
|
}
|
|
4354
|
+
function compactUnknown(value, maxLength = 8e3) {
|
|
4355
|
+
try {
|
|
4356
|
+
const serialized = typeof value === "string" ? value : JSON.stringify(value);
|
|
4357
|
+
return (serialized ?? "").slice(0, maxLength).toLowerCase();
|
|
4358
|
+
} catch {
|
|
4359
|
+
return String(value).slice(0, maxLength).toLowerCase();
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
function isAbortedMessageEvent(eventType, properties) {
|
|
4363
|
+
if (eventType !== "message.updated" && eventType !== "message.part.updated") return false;
|
|
4364
|
+
const text = compactUnknown(properties ?? {});
|
|
4365
|
+
return text.includes("messageabortederror") || text.includes("tool execution aborted") || text.includes('"interrupted":true') || text.includes('"status":"error"') && text.includes('"aborted"');
|
|
4366
|
+
}
|
|
4367
|
+
function getMessageIDFromEvent(properties) {
|
|
4368
|
+
const info = isRecord(properties?.info) ? properties.info : void 0;
|
|
4369
|
+
const part = isRecord(properties?.part) ? properties.part : void 0;
|
|
4370
|
+
return stringifyErrorField(properties?.messageID) ?? stringifyErrorField(info?.id) ?? stringifyErrorField(part?.messageID) ?? stringifyErrorField(properties?.id);
|
|
4371
|
+
}
|
|
4372
|
+
async function recoverUntrackedRootSessionForActiveTask(state, sessionID, reason) {
|
|
4373
|
+
const markerActive = await isUltraworkMarkerActive(state);
|
|
4374
|
+
if (!markerActive) return void 0;
|
|
4375
|
+
const recovery = await parseTasksFile(state.input.directory);
|
|
4376
|
+
if (!recovery || recovery.phase === "ARCHIVE: DONE") return void 0;
|
|
4377
|
+
state.recoveryContext = recovery;
|
|
4378
|
+
await addManagedUltraworkSession(state, sessionID);
|
|
4379
|
+
const recovered = getManagedSession(state, sessionID);
|
|
4380
|
+
await writeDiagnosticLog(state, "session-retry:recovered-untracked-root", {
|
|
4381
|
+
sessionID,
|
|
4382
|
+
task: recovery.task,
|
|
4383
|
+
level: recovery.level,
|
|
4384
|
+
phase: recovery.phase,
|
|
4385
|
+
reason
|
|
4386
|
+
});
|
|
4387
|
+
writePluginLog(
|
|
4388
|
+
state,
|
|
4389
|
+
"warn",
|
|
4390
|
+
`[opencode-immune] Recovered untracked root session ${sessionID} for ${reason}.`
|
|
4391
|
+
);
|
|
4392
|
+
return recovered;
|
|
4393
|
+
}
|
|
4349
4394
|
function recordChildFallbackRequest(state, managedSession, childSessionID, error) {
|
|
4350
4395
|
const fallbackModel = selectFallbackModel(
|
|
4351
4396
|
getFailedModelFromError(error) ?? managedSession.currentModel
|
|
@@ -4405,26 +4450,11 @@ async function setSessionFallbackModel(state, sessionID, model) {
|
|
|
4405
4450
|
}
|
|
4406
4451
|
async function recoverUntrackedRootSessionForRetry(state, sessionID, error) {
|
|
4407
4452
|
if (!isRetryableApiError(error)) return void 0;
|
|
4408
|
-
|
|
4409
|
-
if (!markerActive) return void 0;
|
|
4410
|
-
const recovery = await parseTasksFile(state.input.directory);
|
|
4411
|
-
if (!recovery || recovery.phase === "ARCHIVE: DONE") return void 0;
|
|
4412
|
-
state.recoveryContext = recovery;
|
|
4413
|
-
await addManagedUltraworkSession(state, sessionID);
|
|
4414
|
-
const recovered = getManagedSession(state, sessionID);
|
|
4415
|
-
await writeDiagnosticLog(state, "session-retry:recovered-untracked-root", {
|
|
4416
|
-
sessionID,
|
|
4417
|
-
task: recovery.task,
|
|
4418
|
-
level: recovery.level,
|
|
4419
|
-
phase: recovery.phase,
|
|
4420
|
-
errorType: getRetryableErrorType(error)
|
|
4421
|
-
});
|
|
4422
|
-
writePluginLog(
|
|
4453
|
+
return recoverUntrackedRootSessionForActiveTask(
|
|
4423
4454
|
state,
|
|
4424
|
-
|
|
4425
|
-
`
|
|
4455
|
+
sessionID,
|
|
4456
|
+
`retry after ${getRetryableErrorType(error)}`
|
|
4426
4457
|
);
|
|
4427
|
-
return recovered;
|
|
4428
4458
|
}
|
|
4429
4459
|
function getManagedSessionRetryContext(state, sessionID) {
|
|
4430
4460
|
const managedSession = state.managedUltraworkSessions.get(sessionID);
|
|
@@ -5412,9 +5442,50 @@ function createEventHandler(state) {
|
|
|
5412
5442
|
await sessionRecovery(input);
|
|
5413
5443
|
const event = input.event;
|
|
5414
5444
|
const eventType = event.type ?? "unknown";
|
|
5415
|
-
const
|
|
5416
|
-
const
|
|
5417
|
-
const
|
|
5445
|
+
const properties = event.properties;
|
|
5446
|
+
const info = properties?.info;
|
|
5447
|
+
const sessionID = properties?.sessionID ?? info?.sessionID ?? info?.id;
|
|
5448
|
+
const error = properties?.error ?? info?.error;
|
|
5449
|
+
if (sessionID && isAbortedMessageEvent(eventType, properties)) {
|
|
5450
|
+
const messageID = getMessageIDFromEvent(properties) ?? `${sessionID}:unknown`;
|
|
5451
|
+
const retryKey = `${sessionID}:${messageID}`;
|
|
5452
|
+
if (state.abortedMessageRetries.has(retryKey)) {
|
|
5453
|
+
return;
|
|
5454
|
+
}
|
|
5455
|
+
const managedSession = getManagedSession(state, sessionID) ?? await recoverUntrackedRootSessionForActiveTask(
|
|
5456
|
+
state,
|
|
5457
|
+
sessionID,
|
|
5458
|
+
"aborted message recovery"
|
|
5459
|
+
);
|
|
5460
|
+
if (managedSession?.kind === "root") {
|
|
5461
|
+
state.abortedMessageRetries.add(retryKey);
|
|
5462
|
+
await writeDiagnosticLog(state, "session-retry:aborted-message-observed", {
|
|
5463
|
+
sessionID,
|
|
5464
|
+
messageID,
|
|
5465
|
+
eventType
|
|
5466
|
+
});
|
|
5467
|
+
const count = state.sessionErrorRetryCount.get(sessionID) ?? 0;
|
|
5468
|
+
if (count < MAX_RETRIES) {
|
|
5469
|
+
state.sessionErrorRetryCount.set(sessionID, count + 1);
|
|
5470
|
+
const scheduled = scheduleManagedSessionRetry(state, sessionID, {
|
|
5471
|
+
delayMs: Math.min(BASE_DELAY_MS * Math.pow(2, count), MAX_DELAY_MS),
|
|
5472
|
+
reason: "aborted message",
|
|
5473
|
+
attemptLabel: `aborted attempt ${count + 1}/${MAX_RETRIES}`,
|
|
5474
|
+
countAgainstBudget: true
|
|
5475
|
+
});
|
|
5476
|
+
if (!scheduled) {
|
|
5477
|
+
state.sessionErrorRetryCount.set(sessionID, count);
|
|
5478
|
+
}
|
|
5479
|
+
}
|
|
5480
|
+
} else if (managedSession?.kind === "child") {
|
|
5481
|
+
await writeDiagnosticLog(state, "session-retry:aborted-child-observed", {
|
|
5482
|
+
sessionID,
|
|
5483
|
+
messageID,
|
|
5484
|
+
rootSessionID: managedSession.rootSessionID,
|
|
5485
|
+
eventType
|
|
5486
|
+
});
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5418
5489
|
if (eventType === "session.error") {
|
|
5419
5490
|
await writeDiagnosticLog(state, "session-error:observed", {
|
|
5420
5491
|
sessionID,
|