opencode-auto-resume 1.0.16 → 1.0.17
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 +85 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12351,6 +12351,7 @@ var IDLE_CLEANUP_MS = 10 * 60000;
|
|
|
12351
12351
|
var SESSION_DISCOVERY_INTERVAL_MS = 60000;
|
|
12352
12352
|
var TOOL_TEXT_RECOVERY_PROMPT = "Your last message contained a raw tool call printed as text instead of being executed. " + "Please use the proper tool calling mechanism to execute it.";
|
|
12353
12353
|
var THINKING_TOOL_RECOVERY_PROMPT = "I noticed you have a tool call generated in your thinking/reasoning. " + "Please execute it using the proper tool calling mechanism instead of keeping it in reasoning.";
|
|
12354
|
+
var TOOL_LOOP_RECOVERY_PROMPT = "I notice you've been calling the same tool multiple times in a row without making progress. " + "Please step back and reassess your approach. Consider: " + "1) Are you stuck in a loop? 2) Do you need different information first? " + "3) Should you try a different tool or break the task into smaller steps? " + "Take a moment to think about what's blocking you and propose a different strategy.";
|
|
12354
12355
|
var TOOL_TEXT_PATTERNS = [
|
|
12355
12356
|
/<function\s*=/i,
|
|
12356
12357
|
/<function>/i,
|
|
@@ -12461,9 +12462,7 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12461
12462
|
}
|
|
12462
12463
|
async function log(level, msg) {
|
|
12463
12464
|
try {
|
|
12464
|
-
await ctx.client.app.log({
|
|
12465
|
-
body: { service: "auto-resume", level, message: msg }
|
|
12466
|
-
});
|
|
12465
|
+
await ctx.client.app.log({ body: { service: "auto-resume", level, message: msg } });
|
|
12467
12466
|
} catch {}
|
|
12468
12467
|
}
|
|
12469
12468
|
function ensureWatch(sid) {
|
|
@@ -12488,7 +12487,9 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12488
12487
|
toolTextTimer: null,
|
|
12489
12488
|
checkingToolText: false,
|
|
12490
12489
|
lastSubagentCheckAt: 0,
|
|
12491
|
-
interruptedContinueCount: 0
|
|
12490
|
+
interruptedContinueCount: 0,
|
|
12491
|
+
recentToolCalls: [],
|
|
12492
|
+
toolLoopAttempts: 0
|
|
12492
12493
|
};
|
|
12493
12494
|
sessions.set(sid, w);
|
|
12494
12495
|
}
|
|
@@ -12731,6 +12732,8 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12731
12732
|
w.todoCheckAttempts = 0;
|
|
12732
12733
|
w.checkingToolText = false;
|
|
12733
12734
|
w.interruptedContinueCount = 0;
|
|
12735
|
+
w.recentToolCalls = [];
|
|
12736
|
+
w.toolLoopAttempts = 0;
|
|
12734
12737
|
if (w.toolTextTimer) {
|
|
12735
12738
|
clearTimeout(w.toolTextTimer);
|
|
12736
12739
|
w.toolTextTimer = null;
|
|
@@ -12742,6 +12745,41 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12742
12745
|
w.orphanWatchStartAt = null;
|
|
12743
12746
|
w.idleSince = Date.now();
|
|
12744
12747
|
}
|
|
12748
|
+
function detectPatternLoop(recentTools) {
|
|
12749
|
+
if (recentTools.length < 6)
|
|
12750
|
+
return false;
|
|
12751
|
+
for (const patternLen of [2, 3, 4, 5]) {
|
|
12752
|
+
if (recentTools.length < patternLen * 3)
|
|
12753
|
+
continue;
|
|
12754
|
+
const pattern = recentTools.slice(-patternLen);
|
|
12755
|
+
let matches = 0;
|
|
12756
|
+
for (let i = recentTools.length - patternLen * 2;i >= 0; i -= patternLen) {
|
|
12757
|
+
const slice = recentTools.slice(i, i + patternLen);
|
|
12758
|
+
if (slice.length !== patternLen)
|
|
12759
|
+
break;
|
|
12760
|
+
const isMatch = slice.every((tool3, idx) => tool3 === pattern[idx]);
|
|
12761
|
+
if (!isMatch)
|
|
12762
|
+
break;
|
|
12763
|
+
matches++;
|
|
12764
|
+
}
|
|
12765
|
+
if (matches >= 2)
|
|
12766
|
+
return true;
|
|
12767
|
+
}
|
|
12768
|
+
return false;
|
|
12769
|
+
}
|
|
12770
|
+
function trackToolCall(w, toolName2) {
|
|
12771
|
+
const now = Date.now();
|
|
12772
|
+
w.recentToolCalls = w.recentToolCalls.filter((call) => now - call.at < 120000);
|
|
12773
|
+
w.recentToolCalls.push({ toolName: toolName2, at: now });
|
|
12774
|
+
const recentTools = w.recentToolCalls.slice(-15).map((call) => call.toolName);
|
|
12775
|
+
if (recentTools.length < 6)
|
|
12776
|
+
return false;
|
|
12777
|
+
const lastTool = recentTools[recentTools.length - 1];
|
|
12778
|
+
const consecutiveSame = recentTools.slice(-5).filter((tool3) => tool3 === lastTool).length;
|
|
12779
|
+
if (consecutiveSame >= 3)
|
|
12780
|
+
return true;
|
|
12781
|
+
return detectPatternLoop(recentTools);
|
|
12782
|
+
}
|
|
12745
12783
|
async function checkForToolCallAsText(sid, w) {
|
|
12746
12784
|
if (typeof sid !== "string" || !sid)
|
|
12747
12785
|
return;
|
|
@@ -12773,6 +12811,24 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12773
12811
|
const rawRole = msg.role ?? msg.info?.role;
|
|
12774
12812
|
if (rawRole !== "assistant")
|
|
12775
12813
|
continue;
|
|
12814
|
+
const toolCall = msg.toolCall;
|
|
12815
|
+
if (toolCall && typeof toolCall === "object" && "name" in toolCall) {
|
|
12816
|
+
const toolName2 = toolCall.name;
|
|
12817
|
+
if (toolName2) {
|
|
12818
|
+
trackToolCall(w, toolName2);
|
|
12819
|
+
}
|
|
12820
|
+
}
|
|
12821
|
+
const toolCalls = msg.tool_calls;
|
|
12822
|
+
if (toolCalls) {
|
|
12823
|
+
for (const tc of toolCalls) {
|
|
12824
|
+
if (typeof tc === "object" && "name" in tc) {
|
|
12825
|
+
const toolName2 = tc.name;
|
|
12826
|
+
if (toolName2) {
|
|
12827
|
+
trackToolCall(w, toolName2);
|
|
12828
|
+
}
|
|
12829
|
+
}
|
|
12830
|
+
}
|
|
12831
|
+
}
|
|
12776
12832
|
const parts = msg.parts;
|
|
12777
12833
|
if (!parts)
|
|
12778
12834
|
continue;
|
|
@@ -12788,21 +12844,34 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12788
12844
|
isReasoning = true;
|
|
12789
12845
|
} else if (partType === "tool_use") {
|
|
12790
12846
|
isToolUse = true;
|
|
12791
|
-
const
|
|
12792
|
-
text = `tool_use: ${
|
|
12847
|
+
const toolName2 = part.name ?? "unknown";
|
|
12848
|
+
text = `tool_use: ${toolName2}`;
|
|
12793
12849
|
} else {
|
|
12794
12850
|
continue;
|
|
12795
12851
|
}
|
|
12796
12852
|
allAssistantText += text + `
|
|
12797
12853
|
`;
|
|
12798
12854
|
if (isToolUse) {
|
|
12799
|
-
const
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12855
|
+
const isLoop = trackToolCall(w, toolName);
|
|
12856
|
+
if (isLoop && w.toolLoopAttempts < 2) {
|
|
12857
|
+
w.toolLoopAttempts++;
|
|
12858
|
+
const candidate = {
|
|
12859
|
+
prompt: TOOL_LOOP_RECOVERY_PROMPT,
|
|
12860
|
+
source: "tool-loop",
|
|
12861
|
+
priority: 0
|
|
12862
|
+
};
|
|
12863
|
+
if (!bestCandidate || candidate.priority < bestCandidate.priority) {
|
|
12864
|
+
bestCandidate = candidate;
|
|
12865
|
+
}
|
|
12866
|
+
} else {
|
|
12867
|
+
const candidate = {
|
|
12868
|
+
prompt: "continue",
|
|
12869
|
+
source: "tool-use",
|
|
12870
|
+
priority: 1
|
|
12871
|
+
};
|
|
12872
|
+
if (!bestCandidate || candidate.priority < bestCandidate.priority) {
|
|
12873
|
+
bestCandidate = candidate;
|
|
12874
|
+
}
|
|
12806
12875
|
}
|
|
12807
12876
|
}
|
|
12808
12877
|
if (containsToolCallAsText(text)) {
|
|
@@ -12872,13 +12941,15 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
12872
12941
|
if (!bestCandidate) {
|
|
12873
12942
|
const todos = w.todos || [];
|
|
12874
12943
|
const hasOpenTodos = todos.some((t) => t.status === "pending" || t.status === "in_progress");
|
|
12875
|
-
if (hasOpenTodos) {
|
|
12944
|
+
if (hasOpenTodos && busyCount() === 1) {
|
|
12876
12945
|
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...`);
|
|
12877
12946
|
bestCandidate = {
|
|
12878
12947
|
prompt: "continue",
|
|
12879
12948
|
source: "idle-with-open-todos",
|
|
12880
12949
|
priority: 2
|
|
12881
12950
|
};
|
|
12951
|
+
} else if (hasOpenTodos && busyCount() > 1) {
|
|
12952
|
+
await log("debug", `${short(sid)} - todos remain open but ${busyCount()} sessions busy (subagents running), skipping continue`);
|
|
12882
12953
|
}
|
|
12883
12954
|
}
|
|
12884
12955
|
if (!bestCandidate)
|
package/package.json
CHANGED