opencode-auto-resume 1.1.1 → 1.1.2

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/index.js +45 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12429,6 +12429,18 @@ function containsDoneClaimPattern(text) {
12429
12429
  `);
12430
12430
  return DONE_CLAIM_PATTERNS.some((pat) => pat.test(lastLines));
12431
12431
  }
12432
+ function buildOpenTodosReminder(todos) {
12433
+ const open = todos.filter((t) => t.status === "pending" || t.status === "in_progress");
12434
+ if (open.length === 0)
12435
+ return "continue";
12436
+ const list = open.map((t, i) => `${i + 1}. [${t.status}] ${t.content}`).join(`
12437
+ `);
12438
+ const plural = open.length > 1 ? "s" : "";
12439
+ return `You have ${open.length} unfinished task${plural}:
12440
+ ${list}
12441
+
12442
+ Please continue working on these task${plural}.`;
12443
+ }
12432
12444
  var AutoResumePlugin = async (ctx, options) => {
12433
12445
  const chunkTimeoutMs = options?.chunkTimeoutMs ?? DEFAULT_CHUNK_TIMEOUT_MS;
12434
12446
  const checkIntervalMs = options?.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;
@@ -12491,7 +12503,9 @@ var AutoResumePlugin = async (ctx, options) => {
12491
12503
  interruptedContinueCount: 0,
12492
12504
  recentToolCalls: [],
12493
12505
  toolLoopAttempts: 0,
12494
- isSubagent: false
12506
+ isSubagent: false,
12507
+ completionSignaled: false,
12508
+ todoNudgeAttempts: 0
12495
12509
  };
12496
12510
  sessions.set(sid, w);
12497
12511
  }
@@ -12827,10 +12841,10 @@ var AutoResumePlugin = async (ctx, options) => {
12827
12841
  w.gaveUp = false;
12828
12842
  w.orphanWatchStartAt = null;
12829
12843
  w.aborting = false;
12830
- if (w.isSubagent) {
12831
- w.toolTextRecovered = false;
12832
- }
12844
+ w.toolTextRecovered = false;
12833
12845
  w.toolTextAttempts = 0;
12846
+ w.completionSignaled = false;
12847
+ w.todoNudgeAttempts = 0;
12834
12848
  w.continueTimestamps = [];
12835
12849
  w.idleSince = null;
12836
12850
  w.continuing = false;
@@ -13051,6 +13065,7 @@ var AutoResumePlugin = async (ctx, options) => {
13051
13065
  if (normalized.endsWith("\uD83C\uDF89") && (!bestCandidate || bestCandidate.priority > 0)) {
13052
13066
  await log("info", `${short(sid)} - \uD83C\uDF89 completion detected, skipping continue`);
13053
13067
  w.toolTextRecovered = true;
13068
+ w.completionSignaled = true;
13054
13069
  if (w.toolTextTimer) {
13055
13070
  clearTimeout(w.toolTextTimer);
13056
13071
  w.toolTextTimer = null;
@@ -13061,10 +13076,11 @@ var AutoResumePlugin = async (ctx, options) => {
13061
13076
  const todos = w.todos || [];
13062
13077
  const hasOpenTodos = todos.some((t) => t.status === "pending" || t.status === "in_progress");
13063
13078
  if (hasOpenTodos && busyCount() === 0) {
13064
- await log("info", `${short(sid)} - no activity detected but todos remain open (${todos.filter((t) => t.status === "pending" || t.status === "in_progress").length} tasks). Sending continue...`);
13079
+ const reminder = buildOpenTodosReminder(todos);
13080
+ await log("info", `${short(sid)} - no activity detected but todos remain open (${todos.filter((t) => t.status === "pending" || t.status === "in_progress").length} tasks). Sending reminder...`);
13065
13081
  bestCandidate = {
13066
- prompt: "continue",
13067
- source: "idle-with-open-todos",
13082
+ prompt: reminder,
13083
+ source: "idle-with-open-todos-reminder",
13068
13084
  priority: 2
13069
13085
  };
13070
13086
  } else if (hasOpenTodos && busyCount() > 0) {
@@ -13073,9 +13089,18 @@ var AutoResumePlugin = async (ctx, options) => {
13073
13089
  }
13074
13090
  if (!bestCandidate)
13075
13091
  return;
13076
- w.toolTextRecovered = true;
13077
- w.toolTextAttempts++;
13078
- await log("info", `${bestCandidate.source} detected on ${short(sid)}! ` + `Attempt ${w.toolTextAttempts}/${maxRetries}. Sending recovery prompt...`);
13092
+ const isOpenTodosReminder = bestCandidate.source === "idle-with-open-todos-reminder";
13093
+ if (isOpenTodosReminder) {
13094
+ if (w.todoNudgeAttempts >= maxRetries) {
13095
+ await log("info", `${short(sid)} - max open-todos nudges (${maxRetries}) reached for this idle cycle, waiting for activity`);
13096
+ return;
13097
+ }
13098
+ w.todoNudgeAttempts++;
13099
+ } else {
13100
+ w.toolTextRecovered = true;
13101
+ w.toolTextAttempts++;
13102
+ }
13103
+ await log("info", `${bestCandidate.source} detected on ${short(sid)}! ` + `Attempt ${isOpenTodosReminder ? w.todoNudgeAttempts : w.toolTextAttempts}/${maxRetries}. Sending recovery prompt...`);
13079
13104
  const timeSinceActivity = Date.now() - w.lastActivityAt;
13080
13105
  if (timeSinceActivity < MIN_ACTIVITY_GAP_MS) {
13081
13106
  await log("info", `${short(sid)} - skipping ${bestCandidate.source}, session was active ${Math.round(timeSinceActivity / 1000)}s ago`);
@@ -13141,7 +13166,7 @@ var AutoResumePlugin = async (ctx, options) => {
13141
13166
  return false;
13142
13167
  }
13143
13168
  }
13144
- async function tryResume(sid, w, reason) {
13169
+ async function tryResume(sid, w, reason, prompt) {
13145
13170
  if (typeof sid !== "string" || !sid) {
13146
13171
  await log("warn", `tryResume called with invalid sid: ${sid}`);
13147
13172
  return false;
@@ -13165,7 +13190,7 @@ var AutoResumePlugin = async (ctx, options) => {
13165
13190
  const idleSec = Math.round((now - w.lastActivityAt) / 1000);
13166
13191
  await log("info", `${reason} on ${short(sid)} (${idleSec}s, retry ${w.resumeAttempts}/${maxRetries})`);
13167
13192
  try {
13168
- await sendContinuePrompt(sid, "continue", w);
13193
+ await sendContinuePrompt(sid, prompt ?? "continue", w);
13169
13194
  await log("info", `${short(sid)} - retry sent`);
13170
13195
  return true;
13171
13196
  } catch (err) {
@@ -13354,22 +13379,24 @@ var AutoResumePlugin = async (ctx, options) => {
13354
13379
  if (!w.isSubagent) {
13355
13380
  const todos = w.todos || [];
13356
13381
  const hasOpenTodos = todos.some((t) => t.status === "pending" || t.status === "in_progress");
13357
- if (hasOpenTodos && currentBusy === 0 && !w.toolTextRecovered) {
13382
+ if (hasOpenTodos && currentBusy === 0 && !w.completionSignaled) {
13358
13383
  const isCelebration = await lastAssistantEndsWithCelebration(sid);
13359
13384
  if (isCelebration) {
13360
13385
  await log("info", `${short(sid)} - \uD83C\uDF89 detected in idle handler, skipping continue`);
13361
13386
  w.toolTextRecovered = true;
13387
+ w.completionSignaled = true;
13362
13388
  if (w.toolTextTimer) {
13363
13389
  clearTimeout(w.toolTextTimer);
13364
13390
  w.toolTextTimer = null;
13365
13391
  }
13366
13392
  } else {
13367
- await log("info", `${short(sid)} - idle with ${todos.filter((t) => t.status === "pending" || t.status === "in_progress").length} open todos. Sending continue...`);
13368
- tryResume(sid, w, "Idle with open todos");
13393
+ const reminder = buildOpenTodosReminder(todos);
13394
+ await log("info", `${short(sid)} - idle with ${todos.filter((t) => t.status === "pending" || t.status === "in_progress").length} open todos. Sending reminder...`);
13395
+ tryResume(sid, w, "Idle with open todos", reminder);
13369
13396
  }
13370
13397
  }
13371
13398
  }
13372
- if (!w.toolTextRecovered && w.toolTextAttempts < maxRetries) {
13399
+ if (!w.completionSignaled && w.toolTextAttempts < maxRetries) {
13373
13400
  if (w.toolTextTimer)
13374
13401
  clearTimeout(w.toolTextTimer);
13375
13402
  const checkDelay = statusType === "interrupted" ? 500 : TOOL_TEXT_CHECK_DELAY_MS;
@@ -13497,6 +13524,7 @@ var AutoResumePlugin = async (ctx, options) => {
13497
13524
  if (w) {
13498
13525
  if (!w.isSubagent) {
13499
13526
  w.toolTextRecovered = true;
13527
+ w.completionSignaled = true;
13500
13528
  if (w.toolTextTimer) {
13501
13529
  clearTimeout(w.toolTextTimer);
13502
13530
  w.toolTextTimer = null;
@@ -13526,5 +13554,6 @@ var AutoResumePlugin = async (ctx, options) => {
13526
13554
  var src_default = AutoResumePlugin;
13527
13555
  export {
13528
13556
  src_default as default,
13557
+ buildOpenTodosReminder,
13529
13558
  AutoResumePlugin
13530
13559
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-auto-resume",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "OpenCode plugin that automatically resumes stalled LLM sessions when thinking/streaming freezes mid-generation.",
5
5
  "keywords": [
6
6
  "opencode",