opencode-immune 1.0.0 → 1.0.1
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 +37 -1
- package/package.json +2 -2
package/dist/plugin.js
CHANGED
|
@@ -24,6 +24,10 @@ function createState(input) {
|
|
|
24
24
|
}
|
|
25
25
|
const ULTRAWORK_AGENT = "0-ultrawork";
|
|
26
26
|
const MANAGED_SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
27
|
+
const RATE_LIMIT_FALLBACK_MODEL = {
|
|
28
|
+
providerID: "externcash",
|
|
29
|
+
modelID: "gpt-5.4",
|
|
30
|
+
};
|
|
27
31
|
function isManagedUltraworkSession(state, sessionID) {
|
|
28
32
|
return !!sessionID && state.managedUltraworkSessions.has(sessionID);
|
|
29
33
|
}
|
|
@@ -92,6 +96,7 @@ async function addManagedUltraworkSession(state, sessionID, timestamp = Date.now
|
|
|
92
96
|
agent: ULTRAWORK_AGENT,
|
|
93
97
|
createdAt: existing?.createdAt ?? timestamp,
|
|
94
98
|
updatedAt: timestamp,
|
|
99
|
+
fallbackModel: existing?.fallbackModel,
|
|
95
100
|
};
|
|
96
101
|
if (existing &&
|
|
97
102
|
existing.agent === nextRecord.agent &&
|
|
@@ -140,6 +145,27 @@ function isRetryableApiError(error) {
|
|
|
140
145
|
return (maybeError.name === "APIError" &&
|
|
141
146
|
maybeError.data?.isRetryable === true);
|
|
142
147
|
}
|
|
148
|
+
function isRateLimitApiError(error) {
|
|
149
|
+
if (!error || typeof error !== "object")
|
|
150
|
+
return false;
|
|
151
|
+
const maybeError = error;
|
|
152
|
+
const message = `${maybeError.message ?? ""} ${maybeError.data?.message ?? ""}`.toLowerCase();
|
|
153
|
+
const type = `${maybeError.data?.type ?? ""}`.toLowerCase();
|
|
154
|
+
return (maybeError.name === "APIError" &&
|
|
155
|
+
maybeError.data?.isRetryable === true &&
|
|
156
|
+
(type.includes("rate_limit") || message.includes("too many requests") || message.includes("rate limit")));
|
|
157
|
+
}
|
|
158
|
+
async function setSessionFallbackModel(state, sessionID, model) {
|
|
159
|
+
const existing = state.managedUltraworkSessions.get(sessionID);
|
|
160
|
+
if (!existing)
|
|
161
|
+
return;
|
|
162
|
+
state.managedUltraworkSessions.set(sessionID, {
|
|
163
|
+
...existing,
|
|
164
|
+
updatedAt: Date.now(),
|
|
165
|
+
fallbackModel: model,
|
|
166
|
+
});
|
|
167
|
+
await writeManagedSessionsCache(state);
|
|
168
|
+
}
|
|
143
169
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
144
170
|
// UTILITY: ERROR BOUNDARY
|
|
145
171
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -586,6 +612,12 @@ function createEventHandler(state) {
|
|
|
586
612
|
if (count < MAX_RETRIES) {
|
|
587
613
|
const delay = Math.min(BASE_DELAY_MS * Math.pow(2, count), MAX_DELAY_MS);
|
|
588
614
|
state.retryCount.set(sessionID, count + 1);
|
|
615
|
+
if (isRateLimitApiError(error)) {
|
|
616
|
+
await setSessionFallbackModel(state, sessionID, RATE_LIMIT_FALLBACK_MODEL);
|
|
617
|
+
console.log(`[opencode-immune] Rate limit detected for session ${sessionID}. ` +
|
|
618
|
+
`Retry will use fallback model ${RATE_LIMIT_FALLBACK_MODEL.providerID}/${RATE_LIMIT_FALLBACK_MODEL.modelID}.`);
|
|
619
|
+
}
|
|
620
|
+
const fallbackModel = state.managedUltraworkSessions.get(sessionID)?.fallbackModel;
|
|
589
621
|
console.log(`[opencode-immune] Session error detected (attempt ${count + 1}/${MAX_RETRIES}). ` +
|
|
590
622
|
`Waiting ${delay / 1000}s before retry...`);
|
|
591
623
|
const timer = setTimeout(async () => {
|
|
@@ -596,6 +628,7 @@ function createEventHandler(state) {
|
|
|
596
628
|
try {
|
|
597
629
|
await state.input.client.session.promptAsync({
|
|
598
630
|
body: {
|
|
631
|
+
...(fallbackModel ? { model: fallbackModel } : {}),
|
|
599
632
|
agent: ULTRAWORK_AGENT,
|
|
600
633
|
parts: [
|
|
601
634
|
{
|
|
@@ -606,7 +639,10 @@ function createEventHandler(state) {
|
|
|
606
639
|
},
|
|
607
640
|
path: { id: sessionID },
|
|
608
641
|
});
|
|
609
|
-
console.log(`[opencode-immune] Auto-retry message sent to session ${sessionID}`
|
|
642
|
+
console.log(`[opencode-immune] Auto-retry message sent to session ${sessionID}` +
|
|
643
|
+
(fallbackModel
|
|
644
|
+
? ` using fallback model ${fallbackModel.providerID}/${fallbackModel.modelID}`
|
|
645
|
+
: ""));
|
|
610
646
|
}
|
|
611
647
|
catch (err) {
|
|
612
648
|
state.retryCount.set(sessionID, Math.max((state.retryCount.get(sessionID) ?? 1) - 1, 0));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-immune",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./server": "./dist/plugin.js"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"prepublishOnly": "npm run build"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@opencode-ai/plugin": "
|
|
17
|
+
"@opencode-ai/plugin": "1.4.1"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/node": "^25.5.2",
|