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.
Files changed (2) hide show
  1. package/dist/plugin.js +68 -18
  2. 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 /commit first, then creates a new session.
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 && sessionID) {
1267
+ if (!state.commitPending) {
1211
1268
  state.commitPending = true;
1212
- console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected (standalone), executing /commit...");
1269
+ console.log("[opencode-immune] Multi-Cycle: PRE_COMMIT detected (standalone), running git commit...");
1213
1270
  try {
1214
- await state.input.client.session.command({
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: /commit failed (standalone):", err);
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 (sessionID && !state.commitPending) {
1285
+ if (!state.commitPending) {
1233
1286
  state.commitPending = true;
1234
- console.log("[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected, running /commit first...");
1287
+ console.log("[opencode-immune] Multi-Cycle: CYCLE_COMPLETE detected, running git commit first...");
1235
1288
  try {
1236
- await state.input.client.session.command({
1237
- body: { command: "/commit", arguments: "" },
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: /commit failed (continuing anyway):", err);
1293
+ console.error("[opencode-immune] Multi-Cycle: git commit failed (continuing anyway):", err);
1244
1294
  }
1245
1295
  finally {
1246
1296
  state.commitPending = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-immune",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "OpenCode plugin: session recovery, auto-retry, multi-cycle automation, context monitoring",
5
5
  "exports": {
6
6
  "./server": "./dist/plugin.js"