opencode-immune 1.0.22 → 1.0.24
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 +68 -18
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -185,6 +185,17 @@ function isRetryableApiError(error) {
|
|
|
185
185
|
maybeError.data?.isRetryable === true) {
|
|
186
186
|
return true;
|
|
187
187
|
}
|
|
188
|
+
// HTTP status code based detection (retryable server/gateway errors + rate limits)
|
|
189
|
+
const status = maybeError.status ?? maybeError.statusCode ?? maybeError.data?.status;
|
|
190
|
+
if (status && (status === 404 || // transient endpoint not found (API gateway issues)
|
|
191
|
+
status === 429 || // rate limit
|
|
192
|
+
status === 500 || // internal server error
|
|
193
|
+
status === 502 || // bad gateway
|
|
194
|
+
status === 503 || // service unavailable
|
|
195
|
+
status === 504 // gateway timeout
|
|
196
|
+
)) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
188
199
|
// Text-based detection for model access errors (not marked as retryable
|
|
189
200
|
// by the API but retryable with a fallback model)
|
|
190
201
|
const message = `${maybeError.message ?? ""} ${maybeError.data?.message ?? ""}`.toLowerCase();
|
|
@@ -192,7 +203,20 @@ function isRetryableApiError(error) {
|
|
|
192
203
|
message.includes("not allowed") ||
|
|
193
204
|
message.includes("model not available") ||
|
|
194
205
|
message.includes("model_not_found") ||
|
|
195
|
-
message.includes("access denied")
|
|
206
|
+
message.includes("access denied") ||
|
|
207
|
+
message.includes("404") ||
|
|
208
|
+
message.includes("not found") ||
|
|
209
|
+
message.includes("page not found") ||
|
|
210
|
+
message.includes("502") ||
|
|
211
|
+
message.includes("bad gateway") ||
|
|
212
|
+
message.includes("503") ||
|
|
213
|
+
message.includes("service unavailable") ||
|
|
214
|
+
message.includes("504") ||
|
|
215
|
+
message.includes("gateway timeout") ||
|
|
216
|
+
message.includes("econnrefused") ||
|
|
217
|
+
message.includes("econnreset") ||
|
|
218
|
+
message.includes("etimedout") ||
|
|
219
|
+
message.includes("fetch failed")) {
|
|
196
220
|
return true;
|
|
197
221
|
}
|
|
198
222
|
return false;
|
|
@@ -1183,13 +1207,46 @@ const PRE_COMMIT_MARKER = "0-ULTRAWORK: PRE_COMMIT";
|
|
|
1183
1207
|
const CYCLE_COMPLETE_MARKER = "0-ULTRAWORK: CYCLE_COMPLETE";
|
|
1184
1208
|
const NEXT_TASK_PATTERN = /Next task:\s*(.+)/;
|
|
1185
1209
|
const ALL_CYCLES_COMPLETE_MARKER = "0-ULTRAWORK: ALL_CYCLES_COMPLETE";
|
|
1210
|
+
/**
|
|
1211
|
+
* Helper: run git commit in the project directory.
|
|
1212
|
+
* Uses execFile for safety (no shell injection).
|
|
1213
|
+
* Returns true if commit succeeded, false otherwise.
|
|
1214
|
+
*/
|
|
1215
|
+
function runGitCommit(directory, message) {
|
|
1216
|
+
return new Promise((resolve) => {
|
|
1217
|
+
// Stage all changes first
|
|
1218
|
+
(0, child_process_1.execFile)("git", ["add", "-A"], { cwd: directory }, (addErr) => {
|
|
1219
|
+
if (addErr) {
|
|
1220
|
+
console.error("[opencode-immune] git add failed:", addErr.message);
|
|
1221
|
+
resolve(false);
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
// Then commit
|
|
1225
|
+
(0, child_process_1.execFile)("git", ["commit", "-m", message], { cwd: directory }, (commitErr, stdout, stderr) => {
|
|
1226
|
+
if (commitErr) {
|
|
1227
|
+
// "nothing to commit" is not a real error
|
|
1228
|
+
if (stderr?.includes("nothing to commit") || stdout?.includes("nothing to commit")) {
|
|
1229
|
+
console.log("[opencode-immune] git commit: nothing to commit (clean tree).");
|
|
1230
|
+
resolve(true);
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
console.error("[opencode-immune] git commit failed:", commitErr.message, stderr);
|
|
1234
|
+
resolve(false);
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
console.log("[opencode-immune] git commit succeeded:", stdout?.trim());
|
|
1238
|
+
resolve(true);
|
|
1239
|
+
});
|
|
1240
|
+
});
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1186
1243
|
/**
|
|
1187
1244
|
* experimental.text.complete: scans completed assistant text for signal markers.
|
|
1188
1245
|
*
|
|
1189
1246
|
* KEY INSIGHT: text.complete fires per text-part, not per message.
|
|
1190
1247
|
* PRE_COMMIT and CYCLE_COMPLETE are in DIFFERENT text parts.
|
|
1191
1248
|
* Therefore CYCLE_COMPLETE handler must be self-contained:
|
|
1192
|
-
* it always runs
|
|
1249
|
+
* it always runs git commit first, then creates a new session.
|
|
1193
1250
|
*
|
|
1194
1251
|
* ALL_CYCLES_COMPLETE → clears ultrawork marker, no new session.
|
|
1195
1252
|
*/
|
|
@@ -1207,18 +1264,14 @@ function createTextCompleteHandler(state) {
|
|
|
1207
1264
|
}
|
|
1208
1265
|
// ── PRE_COMMIT only (without CYCLE_COMPLETE in same part): run commit ──
|
|
1209
1266
|
if (text.includes(PRE_COMMIT_MARKER) && !text.includes(CYCLE_COMPLETE_MARKER)) {
|
|
1210
|
-
if (!state.commitPending
|
|
1267
|
+
if (!state.commitPending) {
|
|
1211
1268
|
state.commitPending = true;
|
|
1212
|
-
console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected (standalone),
|
|
1269
|
+
console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected (standalone), running git commit...");
|
|
1213
1270
|
try {
|
|
1214
|
-
await state.input.
|
|
1215
|
-
body: { command: "/commit", arguments: "" },
|
|
1216
|
-
path: { id: sessionID },
|
|
1217
|
-
});
|
|
1218
|
-
console.log("[opencode-immune] Multi-Cycle: /commit completed (standalone).");
|
|
1271
|
+
await runGitCommit(state.input.directory, "chore: ultrawork cycle auto-commit");
|
|
1219
1272
|
}
|
|
1220
1273
|
catch (err) {
|
|
1221
|
-
console.error("[opencode-immune] Multi-Cycle:
|
|
1274
|
+
console.error("[opencode-immune] Multi-Cycle: git commit failed (standalone):", err);
|
|
1222
1275
|
}
|
|
1223
1276
|
finally {
|
|
1224
1277
|
state.commitPending = false;
|
|
@@ -1229,18 +1282,15 @@ function createTextCompleteHandler(state) {
|
|
|
1229
1282
|
// ── CYCLE_COMPLETE: self-contained sequence: commit → new session ──
|
|
1230
1283
|
if (text.includes(CYCLE_COMPLETE_MARKER)) {
|
|
1231
1284
|
// Step 1: Always commit first (CYCLE_COMPLETE implies end of cycle)
|
|
1232
|
-
if (
|
|
1285
|
+
if (!state.commitPending) {
|
|
1233
1286
|
state.commitPending = true;
|
|
1234
|
-
console.log("[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected, running
|
|
1287
|
+
console.log("[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected, running git commit first...");
|
|
1235
1288
|
try {
|
|
1236
|
-
await state.input.
|
|
1237
|
-
|
|
1238
|
-
path: { id: sessionID },
|
|
1239
|
-
});
|
|
1240
|
-
console.log("[opencode-immune] Multi-Cycle: /commit completed before new cycle.");
|
|
1289
|
+
await runGitCommit(state.input.directory, "chore: ultrawork cycle auto-commit");
|
|
1290
|
+
console.log("[opencode-immune] Multi-Cycle: git commit completed before new cycle.");
|
|
1241
1291
|
}
|
|
1242
1292
|
catch (err) {
|
|
1243
|
-
console.error("[opencode-immune] Multi-Cycle:
|
|
1293
|
+
console.error("[opencode-immune] Multi-Cycle: git commit failed (continuing anyway):", err);
|
|
1244
1294
|
}
|
|
1245
1295
|
finally {
|
|
1246
1296
|
state.commitPending = false;
|