opencode-auto-resume 1.1.18 → 1.1.19
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/README.md +2 -0
- package/dist/index.js +84 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -415,6 +415,8 @@ With options:
|
|
|
415
415
|
| `thinkingToolRecoveryPrompt` | `THINKING_TOOL_RECOVERY_PROMPT` | Override the thinking-tool recovery prompt |
|
|
416
416
|
| `doneWithoutWorkPrompt` | `DONE_WITHOUT_WORK_PROMPT` | Override the done-claim-with-open-todos prompt |
|
|
417
417
|
| `doneWithoutDetailsPrompt` | `DONE_WITHOUT_DETAILS_PROMPT` | Override the done-claim-with-no-todos report prompt |
|
|
418
|
+
| `doneClaimPatterns` | `DONE_CLAIM_PATTERNS` | Array of regex strings overriding the default done-claim detection patterns (case-insensitive, multiline). Invalid regexes are skipped. Empty array falls back to defaults. |
|
|
419
|
+
| `readyToContinuePatterns` | `READY_TO_CONTINUE_PATTERNS` | Array of regex strings overriding the default ready-to-continue detection patterns (case-insensitive). Invalid regexes are skipped. Empty array falls back to defaults. |
|
|
418
420
|
| `silentDeadStreamMinTokens` | `200` | Min output tokens to treat a textless `finish:"unknown"` message as a dead stream |
|
|
419
421
|
| `busyStallStrategy` | `"continue"` | Busy-stall response: `"continue"`, `"abort"` (abort-first), or `"off"` (disabled) |
|
|
420
422
|
| `contextSaturationThreshold` | `0.85` | Ratio of used/usable context that routes a saturated parent to magic-context `ctx-wrapup` (only when magic-context is installed) |
|
package/dist/index.js
CHANGED
|
@@ -12428,7 +12428,7 @@ function containsToolCallAsText(text) {
|
|
|
12428
12428
|
}
|
|
12429
12429
|
return false;
|
|
12430
12430
|
}
|
|
12431
|
-
function containsReadyToContinuePattern(text) {
|
|
12431
|
+
function containsReadyToContinuePattern(text, patterns = READY_TO_CONTINUE_PATTERNS) {
|
|
12432
12432
|
const lines = text.split(`
|
|
12433
12433
|
`);
|
|
12434
12434
|
const lastLine = lines[lines.length - 1]?.trim();
|
|
@@ -12436,7 +12436,7 @@ function containsReadyToContinuePattern(text) {
|
|
|
12436
12436
|
return false;
|
|
12437
12437
|
const lastLines = lines.slice(-3).join(`
|
|
12438
12438
|
`);
|
|
12439
|
-
return
|
|
12439
|
+
return patterns.some((pat) => pat.test(lastLines));
|
|
12440
12440
|
}
|
|
12441
12441
|
var DONE_CLAIM_PATTERNS = [
|
|
12442
12442
|
/^task\s+done[.!]*$/im,
|
|
@@ -12456,12 +12456,12 @@ var DONE_CLAIM_PATTERNS = [
|
|
|
12456
12456
|
];
|
|
12457
12457
|
var DONE_WITHOUT_WORK_PROMPT = "I need you to verify more carefully that you have actually completed all the required tasks. " + "Your response indicated you're done, but no work was detected. Please check your todo list " + "and complete any remaining work.";
|
|
12458
12458
|
var DONE_WITHOUT_DETAILS_PROMPT = "Your last response claimed the task is complete but contained no work description. This is not acceptable. " + "You MUST respond now with a full, detailed report of everything you did: " + "for each file you modified, state the full path and the exact changes; " + "list every command you ran to verify and its result; state the final outcome. " + "Do NOT reply with 'done', 'task completed', or any short acknowledgment \u2014 " + "your ONLY acceptable response right now is this detailed report. Write it now.";
|
|
12459
|
-
function containsDoneClaimPattern(text) {
|
|
12459
|
+
function containsDoneClaimPattern(text, patterns = DONE_CLAIM_PATTERNS) {
|
|
12460
12460
|
const lines = text.split(`
|
|
12461
12461
|
`);
|
|
12462
12462
|
const lastLines = lines.slice(-5).join(`
|
|
12463
12463
|
`);
|
|
12464
|
-
return
|
|
12464
|
+
return patterns.some((pat) => pat.test(lastLines));
|
|
12465
12465
|
}
|
|
12466
12466
|
function containsWorkDescription(text) {
|
|
12467
12467
|
if (/`[^`\n]*\.[a-zA-Z0-9]{1,8}[^`\n]*`/.test(text))
|
|
@@ -12675,6 +12675,30 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12675
12675
|
const contextSaturationThreshold = options?.contextSaturationThreshold ?? 0.85;
|
|
12676
12676
|
const subagentNativeCompactionEnabled = options?.subagentNativeCompactionEnabled ?? false;
|
|
12677
12677
|
const activeUserWindowMs = options?.activeUserWindowMs ?? DEFAULT_ACTIVE_USER_WINDOW_MS;
|
|
12678
|
+
const doneClaimPatterns = (() => {
|
|
12679
|
+
const raw = options?.doneClaimPatterns;
|
|
12680
|
+
if (!Array.isArray(raw) || raw.length === 0)
|
|
12681
|
+
return DONE_CLAIM_PATTERNS;
|
|
12682
|
+
return raw.map((s) => {
|
|
12683
|
+
try {
|
|
12684
|
+
return new RegExp(s, "im");
|
|
12685
|
+
} catch {
|
|
12686
|
+
return null;
|
|
12687
|
+
}
|
|
12688
|
+
}).filter((r) => r !== null);
|
|
12689
|
+
})();
|
|
12690
|
+
const readyToContinuePatterns = (() => {
|
|
12691
|
+
const raw = options?.readyToContinuePatterns;
|
|
12692
|
+
if (!Array.isArray(raw) || raw.length === 0)
|
|
12693
|
+
return READY_TO_CONTINUE_PATTERNS;
|
|
12694
|
+
return raw.map((s) => {
|
|
12695
|
+
try {
|
|
12696
|
+
return new RegExp(s, "i");
|
|
12697
|
+
} catch {
|
|
12698
|
+
return null;
|
|
12699
|
+
}
|
|
12700
|
+
}).filter((r) => r !== null);
|
|
12701
|
+
})();
|
|
12678
12702
|
const dbg = (...args) => {
|
|
12679
12703
|
if (debug)
|
|
12680
12704
|
console.log("[debug]", ...args);
|
|
@@ -12749,6 +12773,7 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12749
12773
|
orphanWatchStartAt: null,
|
|
12750
12774
|
aborting: false,
|
|
12751
12775
|
pluginAbortInFlight: false,
|
|
12776
|
+
pluginAbortAt: 0,
|
|
12752
12777
|
toolTextRecovered: false,
|
|
12753
12778
|
toolTextAttempts: 0,
|
|
12754
12779
|
continueTimestamps: [],
|
|
@@ -12917,6 +12942,10 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12917
12942
|
}
|
|
12918
12943
|
if (w.userCancelled || w.completionSignaled)
|
|
12919
12944
|
return;
|
|
12945
|
+
if (w.gaveUp) {
|
|
12946
|
+
await log("debug", `${short(sid)} - gaveUp latched, refusing further continue prompts`);
|
|
12947
|
+
return;
|
|
12948
|
+
}
|
|
12920
12949
|
if (!w.continuing)
|
|
12921
12950
|
dbg(`State transition on ${short(sid)}: continuing=false -> true`);
|
|
12922
12951
|
w.continuing = true;
|
|
@@ -13018,7 +13047,6 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13018
13047
|
} catch (err) {
|
|
13019
13048
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
13020
13049
|
await log("warn", `${short(sid)} - recovery retry failed: ${errMsg}`);
|
|
13021
|
-
w.recoveryAttempts = 0;
|
|
13022
13050
|
}
|
|
13023
13051
|
w.watchdogRetryGuard = false;
|
|
13024
13052
|
} else {
|
|
@@ -13030,8 +13058,11 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13030
13058
|
dbg(`Watchdog check on ${short(sid)}: status=${w.status}, recoveryAttempts=${w.recoveryAttempts}, maxRetries=${maxRecoveryRetries}, watchdogLatencyMs=${Date.now() - w.lastRetryAt} -> ABORT_RESUME`);
|
|
13031
13059
|
const resumed = await tryAbortAndResume(sid, w);
|
|
13032
13060
|
if (!resumed && !w.aborting) {
|
|
13033
|
-
|
|
13034
|
-
|
|
13061
|
+
w.gaveUp = true;
|
|
13062
|
+
w.pendingRecovery = false;
|
|
13063
|
+
w.pendingRecoveryReason = null;
|
|
13064
|
+
await log("warn", `Recovery exhausted on ${short(sid)}: attempts=${w.recoveryAttempts}, lastError=abort+resume failed \u2014 GAVE UP (re-arms on next user message)`);
|
|
13065
|
+
dbg(`Watchdog check on ${short(sid)}: status=${w.status} -> GAVE_UP (latched)`);
|
|
13035
13066
|
if (w.pendingRecoveryAt > 0) {
|
|
13036
13067
|
dbg(`Total recovery cycle on ${short(sid)} (failed): totalCycleMs=${Date.now() - w.pendingRecoveryAt}`);
|
|
13037
13068
|
}
|
|
@@ -13660,7 +13691,7 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13660
13691
|
bestCandidate = candidate;
|
|
13661
13692
|
}
|
|
13662
13693
|
}
|
|
13663
|
-
if (containsReadyToContinuePattern(text)) {
|
|
13694
|
+
if (containsReadyToContinuePattern(text, readyToContinuePatterns)) {
|
|
13664
13695
|
const todos = w.todos || [];
|
|
13665
13696
|
const hasOpenTodos = todos.some(isOpenTodo);
|
|
13666
13697
|
if (!hasOpenTodos && todos.length > 0) {
|
|
@@ -13680,16 +13711,42 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13680
13711
|
await log("info", `${short(sid)} - skipping continue, todos appear completed (attempt ${w.todoCheckAttempts}/2)`);
|
|
13681
13712
|
continue;
|
|
13682
13713
|
}
|
|
13683
|
-
|
|
13684
|
-
|
|
13685
|
-
|
|
13686
|
-
|
|
13687
|
-
|
|
13688
|
-
|
|
13689
|
-
|
|
13714
|
+
if (containsDoneClaimPattern(text, doneClaimPatterns)) {
|
|
13715
|
+
const todos = w.todos || [];
|
|
13716
|
+
const hasOpenTodos = todos.some(isOpenTodo);
|
|
13717
|
+
if (hasOpenTodos) {
|
|
13718
|
+
const candidate = {
|
|
13719
|
+
prompt: doneWithoutWorkPrompt,
|
|
13720
|
+
source: "done-claim",
|
|
13721
|
+
priority: 1
|
|
13722
|
+
};
|
|
13723
|
+
if (!bestCandidate || candidate.priority < bestCandidate.priority) {
|
|
13724
|
+
bestCandidate = candidate;
|
|
13725
|
+
}
|
|
13726
|
+
} else if (!containsWorkDescription(allAssistantText)) {
|
|
13727
|
+
if (w.doneClaimNoTodosAttempts < maxRetries) {
|
|
13728
|
+
const candidate = {
|
|
13729
|
+
prompt: doneWithoutDetailsPrompt,
|
|
13730
|
+
source: "done-claim-no-todos",
|
|
13731
|
+
priority: 1
|
|
13732
|
+
};
|
|
13733
|
+
if (!bestCandidate || candidate.priority < bestCandidate.priority) {
|
|
13734
|
+
bestCandidate = candidate;
|
|
13735
|
+
}
|
|
13736
|
+
}
|
|
13737
|
+
}
|
|
13738
|
+
} else {
|
|
13739
|
+
const candidate = {
|
|
13740
|
+
prompt: continuePrompt,
|
|
13741
|
+
source: "ready-to-continue",
|
|
13742
|
+
priority: 1
|
|
13743
|
+
};
|
|
13744
|
+
if (!bestCandidate || candidate.priority < bestCandidate.priority) {
|
|
13745
|
+
bestCandidate = candidate;
|
|
13746
|
+
}
|
|
13690
13747
|
}
|
|
13691
13748
|
}
|
|
13692
|
-
if (!bestCandidate && containsDoneClaimPattern(text)) {
|
|
13749
|
+
if (!bestCandidate && containsDoneClaimPattern(text, doneClaimPatterns)) {
|
|
13693
13750
|
const todos = w.todos || [];
|
|
13694
13751
|
const hasOpenTodos = todos.some(isOpenTodo);
|
|
13695
13752
|
if (hasOpenTodos) {
|
|
@@ -13774,6 +13831,7 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13774
13831
|
await log("info", `${short(sid)} - max open-todos nudges (${maxRetries}) reached, waiting for activity`);
|
|
13775
13832
|
return;
|
|
13776
13833
|
}
|
|
13834
|
+
w.todoNudgeAttempts++;
|
|
13777
13835
|
} else if (isDoneClaimNoTodos) {
|
|
13778
13836
|
w.doneClaimNoTodosAttempts++;
|
|
13779
13837
|
} else {
|
|
@@ -13829,6 +13887,7 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13829
13887
|
const idleSec = Math.round((Date.now() - (w.orphanWatchStartAt ?? w.lastActivityAt)) / 1000);
|
|
13830
13888
|
await log("info", `Abort+Resume on ${short(sid)} (${idleSec}s idle). Aborting...`);
|
|
13831
13889
|
w.pluginAbortInFlight = true;
|
|
13890
|
+
w.pluginAbortAt = Date.now();
|
|
13832
13891
|
try {
|
|
13833
13892
|
await ctx.client.session.abort({ path: { id: sid } });
|
|
13834
13893
|
await log("info", `${short(sid)} - abort OK`);
|
|
@@ -13838,6 +13897,7 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13838
13897
|
await log("warn", `${short(sid)} - abort failed: ${errMsg}`);
|
|
13839
13898
|
w.aborting = false;
|
|
13840
13899
|
w.pluginAbortInFlight = false;
|
|
13900
|
+
w.pluginAbortAt = 0;
|
|
13841
13901
|
return false;
|
|
13842
13902
|
}
|
|
13843
13903
|
await new Promise((resolve) => setTimeout(resolve, ABORT_CONTINUE_DELAY_MS));
|
|
@@ -13862,6 +13922,7 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
13862
13922
|
return false;
|
|
13863
13923
|
} finally {
|
|
13864
13924
|
w.pluginAbortInFlight = false;
|
|
13925
|
+
w.pluginAbortAt = 0;
|
|
13865
13926
|
}
|
|
13866
13927
|
}
|
|
13867
13928
|
async function tryResume(sid, w, reason, prompt) {
|
|
@@ -14526,10 +14587,16 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
14526
14587
|
const isMessageAborted = errorName === "MessageAbortedError";
|
|
14527
14588
|
if (isMessageAborted) {
|
|
14528
14589
|
for (const [wSid, w] of sessions) {
|
|
14529
|
-
|
|
14590
|
+
const PLUGIN_ABORT_GRACE_MS = 1000;
|
|
14591
|
+
const isOwnAbort = w.pluginAbortInFlight && Date.now() - (w.pluginAbortAt || 0) < PLUGIN_ABORT_GRACE_MS;
|
|
14592
|
+
if (!isOwnAbort) {
|
|
14530
14593
|
w.userCancelled = true;
|
|
14531
14594
|
w.status = "idle";
|
|
14532
14595
|
resetIdleFlags(w);
|
|
14596
|
+
if (w.toolTextTimer) {
|
|
14597
|
+
clearTimeout(w.toolTextTimer);
|
|
14598
|
+
w.toolTextTimer = null;
|
|
14599
|
+
}
|
|
14533
14600
|
}
|
|
14534
14601
|
}
|
|
14535
14602
|
log("info", "User abort (ESC)");
|
package/package.json
CHANGED