opencode-auto-resume 1.1.2 → 1.1.3
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/index.js +102 -43
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12505,7 +12505,9 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12505
12505
|
toolLoopAttempts: 0,
|
|
12506
12506
|
isSubagent: false,
|
|
12507
12507
|
completionSignaled: false,
|
|
12508
|
-
todoNudgeAttempts: 0
|
|
12508
|
+
todoNudgeAttempts: 0,
|
|
12509
|
+
pendingTools: 0,
|
|
12510
|
+
pendingCommands: 0
|
|
12509
12511
|
};
|
|
12510
12512
|
sessions.set(sid, w);
|
|
12511
12513
|
}
|
|
@@ -12517,6 +12519,9 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12517
12519
|
w.lastActivityAt = Date.now();
|
|
12518
12520
|
}
|
|
12519
12521
|
}
|
|
12522
|
+
function hasInflightTools(w) {
|
|
12523
|
+
return w.pendingTools > 0 || w.pendingCommands > 0;
|
|
12524
|
+
}
|
|
12520
12525
|
function busyCount() {
|
|
12521
12526
|
let count = 0;
|
|
12522
12527
|
for (const [, w] of sessions) {
|
|
@@ -12838,6 +12843,8 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12838
12843
|
function resetSessionFlags(w) {
|
|
12839
12844
|
w.userCancelled = false;
|
|
12840
12845
|
w.resumeAttempts = 0;
|
|
12846
|
+
w.pendingTools = 0;
|
|
12847
|
+
w.pendingCommands = 0;
|
|
12841
12848
|
w.gaveUp = false;
|
|
12842
12849
|
w.orphanWatchStartAt = null;
|
|
12843
12850
|
w.aborting = false;
|
|
@@ -12859,10 +12866,11 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12859
12866
|
}
|
|
12860
12867
|
}
|
|
12861
12868
|
function resetIdleFlags(w) {
|
|
12862
|
-
w.userCancelled = false;
|
|
12863
12869
|
w.aborting = false;
|
|
12864
12870
|
w.orphanWatchStartAt = null;
|
|
12865
12871
|
w.idleSince = Date.now();
|
|
12872
|
+
w.pendingTools = 0;
|
|
12873
|
+
w.pendingCommands = 0;
|
|
12866
12874
|
}
|
|
12867
12875
|
function detectPatternLoop(recentTools) {
|
|
12868
12876
|
if (recentTools.length < 6)
|
|
@@ -13107,6 +13115,10 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13107
13115
|
return;
|
|
13108
13116
|
}
|
|
13109
13117
|
if (isHallucinationLoop(sid)) {
|
|
13118
|
+
if (hasInflightTools(w)) {
|
|
13119
|
+
await log("debug", `Session ${short(sid)} has ${w.pendingTools} tool(s) in-flight, skipping hallucination abort`);
|
|
13120
|
+
return;
|
|
13121
|
+
}
|
|
13110
13122
|
const hasActiveTool = await checkSessionHasActiveTool(sid);
|
|
13111
13123
|
if (hasActiveTool) {
|
|
13112
13124
|
await log("debug", `Session ${short(sid)} has active tool, skipping hallucination abort`);
|
|
@@ -13177,6 +13189,11 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13177
13189
|
if (w.lastRetryAt > 0 && elapsedSinceRetry < requiredBackoff)
|
|
13178
13190
|
return false;
|
|
13179
13191
|
if (isHallucinationLoop(sid)) {
|
|
13192
|
+
if (hasInflightTools(w)) {
|
|
13193
|
+
await log("debug", `Session ${short(sid)} has ${w.pendingTools} tool(s) in-flight, skipping hallucination abort`);
|
|
13194
|
+
w.lastRetryAt = now;
|
|
13195
|
+
return false;
|
|
13196
|
+
}
|
|
13180
13197
|
const hasActiveTool = await checkSessionHasActiveTool(sid);
|
|
13181
13198
|
if (hasActiveTool) {
|
|
13182
13199
|
await log("debug", `Session ${short(sid)} has active tool, skipping hallucination abort`);
|
|
@@ -13250,6 +13267,11 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13250
13267
|
const orphanIdle = now - w.orphanWatchStartAt;
|
|
13251
13268
|
if (orphanIdle >= subagentWaitMs + gracePeriodMs) {
|
|
13252
13269
|
if (w.resumeAttempts < maxRetries) {
|
|
13270
|
+
if (hasInflightTools(w)) {
|
|
13271
|
+
await log("debug", `Parent ${short(sid)} has ${w.pendingTools} tool(s) in-flight, skipping orphan-watch abort`);
|
|
13272
|
+
w.orphanWatchStartAt = now;
|
|
13273
|
+
continue;
|
|
13274
|
+
}
|
|
13253
13275
|
const hasActiveTool = await checkSessionHasActiveTool(sid);
|
|
13254
13276
|
if (hasActiveTool) {
|
|
13255
13277
|
await log("debug", `Parent ${short(sid)} has active tool call, skipping orphan-watch abort`);
|
|
@@ -13296,6 +13318,11 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13296
13318
|
w.lastSubagentCheckAt = now;
|
|
13297
13319
|
continue;
|
|
13298
13320
|
}
|
|
13321
|
+
if (hasInflightTools(w)) {
|
|
13322
|
+
await log("debug", `Session ${short(sid)} has ${w.pendingTools} tool(s) in-flight, skipping abort check`);
|
|
13323
|
+
w.lastSubagentCheckAt = now;
|
|
13324
|
+
continue;
|
|
13325
|
+
}
|
|
13299
13326
|
const hasActiveTool = await checkSessionHasActiveTool(sid);
|
|
13300
13327
|
if (hasActiveTool) {
|
|
13301
13328
|
await log("debug", `Session ${short(sid)} has active tool call, skipping abort check`);
|
|
@@ -13320,15 +13347,20 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13320
13347
|
}
|
|
13321
13348
|
const idle = now - w.lastActivityAt;
|
|
13322
13349
|
if (idle >= chunkTimeoutMs + gracePeriodMs) {
|
|
13323
|
-
|
|
13324
|
-
|
|
13325
|
-
await log("debug", `Session ${short(sid)} has active tool call, skipping stall recovery`);
|
|
13350
|
+
if (hasInflightTools(w)) {
|
|
13351
|
+
await log("debug", `Session ${short(sid)} has ${w.pendingTools} tool(s) in-flight, skipping stall recovery`);
|
|
13326
13352
|
w.lastSubagentCheckAt = now;
|
|
13327
|
-
} else
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13353
|
+
} else {
|
|
13354
|
+
const hasActiveTool = await checkSessionHasActiveTool(sid);
|
|
13355
|
+
if (hasActiveTool) {
|
|
13356
|
+
await log("debug", `Session ${short(sid)} has active tool call, skipping stall recovery`);
|
|
13357
|
+
w.lastSubagentCheckAt = now;
|
|
13358
|
+
} else if (w.resumeAttempts < maxRetries) {
|
|
13359
|
+
tryResume(sid, w, "Stream stall");
|
|
13360
|
+
} else if (!w.gaveUp) {
|
|
13361
|
+
w.gaveUp = true;
|
|
13362
|
+
log("warn", `${short(sid)} - all ${maxRetries} retries exhausted.`);
|
|
13363
|
+
}
|
|
13332
13364
|
}
|
|
13333
13365
|
}
|
|
13334
13366
|
}
|
|
@@ -13362,7 +13394,17 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13362
13394
|
resetSessionFlags(w);
|
|
13363
13395
|
prevBusyCount = busyCount();
|
|
13364
13396
|
log("debug", `${short(sid)} -> busy (${prevBusyCount})`);
|
|
13365
|
-
} else if (statusType === "
|
|
13397
|
+
} else if (statusType === "interrupted") {
|
|
13398
|
+
w.status = "idle";
|
|
13399
|
+
resetIdleFlags(w);
|
|
13400
|
+
w.userCancelled = true;
|
|
13401
|
+
if (w.toolTextTimer) {
|
|
13402
|
+
clearTimeout(w.toolTextTimer);
|
|
13403
|
+
w.toolTextTimer = null;
|
|
13404
|
+
}
|
|
13405
|
+
prevBusyCount = busyCount();
|
|
13406
|
+
log("info", `${short(sid)} -> interrupted by user, backing off`);
|
|
13407
|
+
} else if (statusType === "idle") {
|
|
13366
13408
|
w.status = "idle";
|
|
13367
13409
|
resetIdleFlags(w);
|
|
13368
13410
|
const currentBusy = busyCount();
|
|
@@ -13375,11 +13417,11 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13375
13417
|
}
|
|
13376
13418
|
}
|
|
13377
13419
|
prevBusyCount = currentBusy;
|
|
13378
|
-
log("debug", `${short(sid)} -> idle (${currentBusy})
|
|
13420
|
+
log("debug", `${short(sid)} -> idle (${currentBusy})`);
|
|
13379
13421
|
if (!w.isSubagent) {
|
|
13380
13422
|
const todos = w.todos || [];
|
|
13381
13423
|
const hasOpenTodos = todos.some((t) => t.status === "pending" || t.status === "in_progress");
|
|
13382
|
-
if (hasOpenTodos && currentBusy === 0 && !w.completionSignaled) {
|
|
13424
|
+
if (hasOpenTodos && currentBusy === 0 && !w.completionSignaled && !w.userCancelled) {
|
|
13383
13425
|
const isCelebration = await lastAssistantEndsWithCelebration(sid);
|
|
13384
13426
|
if (isCelebration) {
|
|
13385
13427
|
await log("info", `${short(sid)} - \uD83C\uDF89 detected in idle handler, skipping continue`);
|
|
@@ -13396,13 +13438,12 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13396
13438
|
}
|
|
13397
13439
|
}
|
|
13398
13440
|
}
|
|
13399
|
-
if (!w.completionSignaled && w.toolTextAttempts < maxRetries) {
|
|
13441
|
+
if (!w.completionSignaled && !w.userCancelled && w.toolTextAttempts < maxRetries) {
|
|
13400
13442
|
if (w.toolTextTimer)
|
|
13401
13443
|
clearTimeout(w.toolTextTimer);
|
|
13402
|
-
const checkDelay = statusType === "interrupted" ? 500 : TOOL_TEXT_CHECK_DELAY_MS;
|
|
13403
13444
|
w.toolTextTimer = setTimeout(() => {
|
|
13404
13445
|
checkForToolCallAsText(sid, w);
|
|
13405
|
-
},
|
|
13446
|
+
}, TOOL_TEXT_CHECK_DELAY_MS);
|
|
13406
13447
|
}
|
|
13407
13448
|
} else if (statusType === "retry") {
|
|
13408
13449
|
touchSession(sid);
|
|
@@ -13413,7 +13454,9 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13413
13454
|
case "session.created": {
|
|
13414
13455
|
if (!sid)
|
|
13415
13456
|
break;
|
|
13416
|
-
ensureWatch(sid);
|
|
13457
|
+
const w = ensureWatch(sid);
|
|
13458
|
+
w.pendingTools = 0;
|
|
13459
|
+
w.pendingCommands = 0;
|
|
13417
13460
|
log("debug", `New session: ${short(sid)} (${sessions.size})`);
|
|
13418
13461
|
break;
|
|
13419
13462
|
}
|
|
@@ -13444,33 +13487,14 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13444
13487
|
break;
|
|
13445
13488
|
const w = sessions.get(sid);
|
|
13446
13489
|
if (w) {
|
|
13447
|
-
const wasJustContinued = w.continuing || Date.now() - w.lastRetryAt < 2000;
|
|
13448
13490
|
w.status = "idle";
|
|
13449
13491
|
resetIdleFlags(w);
|
|
13450
|
-
|
|
13451
|
-
if (
|
|
13452
|
-
w.
|
|
13453
|
-
|
|
13454
|
-
w.continuing = false;
|
|
13455
|
-
try {
|
|
13456
|
-
await sendContinuePrompt(sid, "continue", w);
|
|
13457
|
-
await log("info", `${short(sid)} - interrupted continue retried`);
|
|
13458
|
-
} catch (err) {
|
|
13459
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
13460
|
-
await log("warn", `${short(sid)} - interrupted continue retry failed: ${errMsg}`);
|
|
13461
|
-
}
|
|
13462
|
-
return;
|
|
13463
|
-
} else if (wasJustContinued && w.interruptedContinueCount >= 3) {
|
|
13464
|
-
await log("warn", `${short(sid)} - too many interrupted continues (${w.interruptedContinueCount}), stopping retries`);
|
|
13465
|
-
w.interruptedContinueCount = 0;
|
|
13466
|
-
}
|
|
13467
|
-
if (!w.toolTextRecovered && w.toolTextAttempts < maxRetries) {
|
|
13468
|
-
if (w.toolTextTimer)
|
|
13469
|
-
clearTimeout(w.toolTextTimer);
|
|
13470
|
-
w.toolTextTimer = setTimeout(() => {
|
|
13471
|
-
checkForToolCallAsText(sid, w);
|
|
13472
|
-
}, 500);
|
|
13492
|
+
w.userCancelled = true;
|
|
13493
|
+
if (w.toolTextTimer) {
|
|
13494
|
+
clearTimeout(w.toolTextTimer);
|
|
13495
|
+
w.toolTextTimer = null;
|
|
13473
13496
|
}
|
|
13497
|
+
log("info", `${short(sid)} -> interrupted by user, backing off`);
|
|
13474
13498
|
}
|
|
13475
13499
|
break;
|
|
13476
13500
|
}
|
|
@@ -13506,11 +13530,25 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13506
13530
|
break;
|
|
13507
13531
|
const errorMessage = errorObj?.data?.message ?? String(errorObj?.data ?? "");
|
|
13508
13532
|
log("debug", `Session error: ${errorName} - ${errorMessage}`);
|
|
13533
|
+
if (sid) {
|
|
13534
|
+
const w = sessions.get(sid);
|
|
13535
|
+
if (w) {
|
|
13536
|
+
w.pendingTools = 0;
|
|
13537
|
+
w.pendingCommands = 0;
|
|
13538
|
+
}
|
|
13539
|
+
}
|
|
13509
13540
|
break;
|
|
13510
13541
|
}
|
|
13511
13542
|
case "command.executed": {
|
|
13512
|
-
for (const [,
|
|
13513
|
-
resetSessionFlags(
|
|
13543
|
+
for (const [, w2] of sessions) {
|
|
13544
|
+
resetSessionFlags(w2);
|
|
13545
|
+
}
|
|
13546
|
+
if (!sid)
|
|
13547
|
+
break;
|
|
13548
|
+
const w = sessions.get(sid);
|
|
13549
|
+
if (w) {
|
|
13550
|
+
w.pendingCommands = Math.max(0, w.pendingCommands - 1);
|
|
13551
|
+
w.lastActivityAt = Date.now();
|
|
13514
13552
|
}
|
|
13515
13553
|
break;
|
|
13516
13554
|
}
|
|
@@ -13548,6 +13586,27 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13548
13586
|
},
|
|
13549
13587
|
tool: {
|
|
13550
13588
|
task_complete: taskCompleteTool
|
|
13589
|
+
},
|
|
13590
|
+
"tool.execute.before": async (input) => {
|
|
13591
|
+
if (!input?.sessionID)
|
|
13592
|
+
return;
|
|
13593
|
+
const w = ensureWatch(input.sessionID);
|
|
13594
|
+
w.pendingTools++;
|
|
13595
|
+
w.lastActivityAt = Date.now();
|
|
13596
|
+
},
|
|
13597
|
+
"command.execute.before": async (input) => {
|
|
13598
|
+
if (!input?.sessionID)
|
|
13599
|
+
return;
|
|
13600
|
+
const w = ensureWatch(input.sessionID);
|
|
13601
|
+
w.pendingCommands++;
|
|
13602
|
+
w.lastActivityAt = Date.now();
|
|
13603
|
+
},
|
|
13604
|
+
"tool.execute.after": async (input) => {
|
|
13605
|
+
if (!input?.sessionID)
|
|
13606
|
+
return;
|
|
13607
|
+
const w = ensureWatch(input.sessionID);
|
|
13608
|
+
w.pendingTools = Math.max(0, w.pendingTools - 1);
|
|
13609
|
+
w.lastActivityAt = Date.now();
|
|
13551
13610
|
}
|
|
13552
13611
|
};
|
|
13553
13612
|
};
|
package/package.json
CHANGED