opencode-auto-resume 1.0.19 → 1.0.20

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 +81 -32
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -12583,9 +12583,9 @@ var AutoResumePlugin = async (ctx, options) => {
12583
12583
  return;
12584
12584
  }
12585
12585
  w.continuing = true;
12586
+ let agent;
12587
+ let model;
12586
12588
  try {
12587
- let agent2;
12588
- let model2;
12589
12589
  const msgResp = await ctx.client.session.messages({ path: { id: sid } });
12590
12590
  const msgs = extractMessages(msgResp);
12591
12591
  for (let i = msgs.length - 1;i >= 0; i--) {
@@ -12594,11 +12594,11 @@ var AutoResumePlugin = async (ctx, options) => {
12594
12594
  if (role === "user") {
12595
12595
  const rawAgent = msg.agent;
12596
12596
  if (typeof rawAgent === "string") {
12597
- agent2 = rawAgent;
12597
+ agent = rawAgent;
12598
12598
  } else {
12599
12599
  const fallbackAgent = msg.info?.agent;
12600
12600
  if (typeof fallbackAgent === "string") {
12601
- agent2 = fallbackAgent;
12601
+ agent = fallbackAgent;
12602
12602
  }
12603
12603
  }
12604
12604
  let rawModel = msg.model;
@@ -12606,7 +12606,7 @@ var AutoResumePlugin = async (ctx, options) => {
12606
12606
  rawModel = msg.info?.model;
12607
12607
  }
12608
12608
  if (rawModel && typeof rawModel.providerID === "string" && typeof rawModel.modelID === "string") {
12609
- model2 = {
12609
+ model = {
12610
12610
  providerID: rawModel.providerID,
12611
12611
  modelID: rawModel.modelID
12612
12612
  };
@@ -12618,11 +12618,11 @@ var AutoResumePlugin = async (ctx, options) => {
12618
12618
  path: { id: sid },
12619
12619
  body: {
12620
12620
  parts: [{ type: "text", text }],
12621
- agent: agent2,
12622
- model: model2
12621
+ agent,
12622
+ model
12623
12623
  }
12624
12624
  });
12625
- await log("debug", `${short(sid)} - prompt sent with agent: ${agent2 ?? "(default)"}, model: ${model2 ? `${model2.providerID}/${model2.modelID}` : "(default)"}`);
12625
+ await log("debug", `${short(sid)} - prompt sent with agent: ${agent ?? "(default)"}, model: ${model ? `${model.providerID}/${model.modelID}` : "(default)"}`);
12626
12626
  recordContinue(sid);
12627
12627
  w.lastRetryAt = Date.now();
12628
12628
  } catch (err) {
@@ -12679,6 +12679,46 @@ var AutoResumePlugin = async (ctx, options) => {
12679
12679
  return false;
12680
12680
  }
12681
12681
  }
12682
+ async function hasBusySubagents(parentSid) {
12683
+ try {
12684
+ const response = await ctx.client.session.list();
12685
+ const allSessions = extractMessages(response);
12686
+ for (const s of allSessions) {
12687
+ const sId = s.id;
12688
+ if (!sId || sId === parentSid)
12689
+ continue;
12690
+ const status = s.status;
12691
+ if (status === "busy")
12692
+ return true;
12693
+ }
12694
+ return false;
12695
+ } catch (err) {
12696
+ const errMsg = err instanceof Error ? err.message : String(err);
12697
+ await log("debug", `hasBusySubagents failed for ${short(parentSid)}: ${errMsg}`);
12698
+ return false;
12699
+ }
12700
+ }
12701
+ async function checkSessionHasActiveTool(sid) {
12702
+ try {
12703
+ const response = await ctx.client.session.messages({ path: { id: sid } });
12704
+ const messages = extractMessages(response);
12705
+ const lastMsg = messages[messages.length - 1];
12706
+ if (!lastMsg)
12707
+ return false;
12708
+ const rawRole = lastMsg?.role ?? lastMsg?.info?.role;
12709
+ if (rawRole !== "assistant")
12710
+ return false;
12711
+ const toolCall = lastMsg.toolCall;
12712
+ const toolCalls = lastMsg.tool_calls;
12713
+ const parts = lastMsg.parts;
12714
+ const hasToolCall = toolCall !== undefined || (toolCalls?.length ?? 0) > 0 || (parts?.some((p) => p.type === "tool-call" || p.type === "tool_use") ?? false);
12715
+ return hasToolCall;
12716
+ } catch (err) {
12717
+ const errMsg = err instanceof Error ? err.message : String(err);
12718
+ await log("debug", `checkSessionHasActiveTool failed for ${short(sid)}: ${errMsg}`);
12719
+ return false;
12720
+ }
12721
+ }
12682
12722
  async function checkSubagentStatus(parentSid) {
12683
12723
  try {
12684
12724
  const response = await ctx.client.session.list();
@@ -12698,10 +12738,15 @@ var AutoResumePlugin = async (ctx, options) => {
12698
12738
  const rawRole = lastMsg?.role ?? lastMsg?.info?.role;
12699
12739
  if (lastMsg && rawRole === "assistant" && (("error" in lastMsg) || lastMsg.info && ("error" in lastMsg.info))) {
12700
12740
  await log("debug", `Subagent ${short(sId)} appears crashed`);
12701
- return "crashed";
12741
+ return { status: "crashed" };
12702
12742
  }
12703
12743
  const msgTime = lastMsg?.time?.created ?? lastMsg?.time;
12704
- const hasToolCall = lastMsg?.toolCall !== undefined || lastMsg?.tool_calls !== undefined || lastMsg?.parts?.some((p) => p.type === "tool-call") !== undefined;
12744
+ if (!msgTime)
12745
+ continue;
12746
+ const toolCall = lastMsg.toolCall;
12747
+ const toolCalls = lastMsg.tool_calls;
12748
+ const parts = lastMsg.parts;
12749
+ const hasToolCall = toolCall !== undefined || (toolCalls?.length ?? 0) > 0 || (parts?.some((p) => p.type === "tool-call") ?? false);
12705
12750
  const isStuck = hasToolCall ? now - msgTime > SUBAGENT_STUCK_MS * 3 : now - msgTime > SUBAGENT_STUCK_MS;
12706
12751
  if (isStuck) {
12707
12752
  await log("debug", `Subagent ${short(sId)} stuck - no new text in >${hasToolCall ? 3 : 1}min`);
@@ -12770,10 +12815,10 @@ var AutoResumePlugin = async (ctx, options) => {
12770
12815
  }
12771
12816
  return false;
12772
12817
  }
12773
- function trackToolCall(w, toolName2) {
12818
+ function trackToolCall(w, toolName) {
12774
12819
  const now = Date.now();
12775
12820
  w.recentToolCalls = w.recentToolCalls.filter((call) => now - call.at < 120000);
12776
- w.recentToolCalls.push({ toolName: toolName2, at: now });
12821
+ w.recentToolCalls.push({ toolName, at: now });
12777
12822
  const recentTools = w.recentToolCalls.slice(-15).map((call) => call.toolName);
12778
12823
  if (recentTools.length < 6)
12779
12824
  return false;
@@ -12816,18 +12861,18 @@ var AutoResumePlugin = async (ctx, options) => {
12816
12861
  continue;
12817
12862
  const toolCall = msg.toolCall;
12818
12863
  if (toolCall && typeof toolCall === "object" && "name" in toolCall) {
12819
- const toolName2 = toolCall.name;
12820
- if (toolName2) {
12821
- trackToolCall(w, toolName2);
12864
+ const toolName = toolCall.name;
12865
+ if (toolName) {
12866
+ trackToolCall(w, toolName);
12822
12867
  }
12823
12868
  }
12824
12869
  const toolCalls = msg.tool_calls;
12825
12870
  if (toolCalls) {
12826
12871
  for (const tc of toolCalls) {
12827
12872
  if (typeof tc === "object" && "name" in tc) {
12828
- const toolName2 = tc.name;
12829
- if (toolName2) {
12830
- trackToolCall(w, toolName2);
12873
+ const toolName = tc.name;
12874
+ if (toolName) {
12875
+ trackToolCall(w, toolName);
12831
12876
  }
12832
12877
  }
12833
12878
  }
@@ -12847,14 +12892,15 @@ var AutoResumePlugin = async (ctx, options) => {
12847
12892
  isReasoning = true;
12848
12893
  } else if (partType === "tool_use") {
12849
12894
  isToolUse = true;
12850
- const toolName2 = part.name ?? "unknown";
12851
- text = `tool_use: ${toolName2}`;
12895
+ const toolName = part.name ?? "unknown";
12896
+ text = `tool_use: ${toolName}`;
12852
12897
  } else {
12853
12898
  continue;
12854
12899
  }
12855
12900
  allAssistantText += text + `
12856
12901
  `;
12857
12902
  if (isToolUse) {
12903
+ const toolName = part.name ?? "unknown";
12858
12904
  const isLoop = trackToolCall(w, toolName);
12859
12905
  if (isLoop && w.toolLoopAttempts < 2) {
12860
12906
  w.toolLoopAttempts++;
@@ -12880,8 +12926,8 @@ var AutoResumePlugin = async (ctx, options) => {
12880
12926
  if (containsToolCallAsText(text)) {
12881
12927
  const toolMatch = text.match(/<function=([a-zA-Z_]+)/) || text.match(/<invoke\s+name=([a-zA-Z_]+)/) || text.match(/"name":\s*"([a-zA-Z_]+)/);
12882
12928
  if (toolMatch) {
12883
- const toolName2 = toolMatch[1] || "unknown";
12884
- const isLoop = trackToolCall(w, toolName2);
12929
+ const toolName = toolMatch[1] || "unknown";
12930
+ const isLoop = trackToolCall(w, toolName);
12885
12931
  if (isLoop && w.toolLoopAttempts < 2) {
12886
12932
  w.toolLoopAttempts++;
12887
12933
  const loopCandidate = {
@@ -13117,8 +13163,13 @@ var AutoResumePlugin = async (ctx, options) => {
13117
13163
  tryAbortAndResume(sid, w);
13118
13164
  }
13119
13165
  } else if (subStatus.status === "idle") {
13120
- await log("info", `All subagents idle, parent ${short(sid)} stuck. Triggering abort+resume.`);
13121
- tryAbortAndResume(sid, w);
13166
+ const hasBusySub = await hasBusySubagents(sid);
13167
+ if (hasBusySub) {
13168
+ await log("debug", `Subagents exist but not busy yet, waiting for startup...`);
13169
+ } else {
13170
+ await log("info", `Parent ${short(sid)} stuck with no active subagents. Triggering abort+resume.`);
13171
+ tryAbortAndResume(sid, w);
13172
+ }
13122
13173
  } else {
13123
13174
  await log("debug", `Subagent still running, waiting...`);
13124
13175
  }
@@ -13137,6 +13188,12 @@ var AutoResumePlugin = async (ctx, options) => {
13137
13188
  continue;
13138
13189
  w.lastSubagentCheckAt = now;
13139
13190
  if (w.lastActivityAt > 0 && now - w.lastActivityAt > subagentWaitMs) {
13191
+ const hasActiveTool = await checkSessionHasActiveTool(sid);
13192
+ if (hasActiveTool) {
13193
+ await log("debug", `Parent ${short(sid)} has active tool call, skipping abort check`);
13194
+ w.lastSubagentCheckAt = now;
13195
+ continue;
13196
+ }
13140
13197
  const subStatus = await checkSubagentStatus(sid);
13141
13198
  if (subStatus.status === "idle" || subStatus.status === "unknown") {
13142
13199
  await log("info", `Parent ${short(sid)} stuck with no active subagents. Triggering abort+resume.`);
@@ -13365,14 +13422,6 @@ var AutoResumePlugin = async (ctx, options) => {
13365
13422
  },
13366
13423
  config: async () => {
13367
13424
  log("info", `opencode-auto-resume config OK`);
13368
- if (ctx.ui && typeof ctx.ui.toast === "function") {
13369
- ctx.ui.toast({
13370
- title: "Auto-Resume Plugin",
13371
- message: `Loaded with ${chunkTimeoutMs}ms timeout, ${loopMaxContinues} loop attempts`,
13372
- variant: "success",
13373
- duration: 5000
13374
- });
13375
- }
13376
13425
  },
13377
13426
  tool: {
13378
13427
  task_complete: taskCompleteTool
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-auto-resume",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "OpenCode plugin that automatically resumes stalled LLM sessions when thinking/streaming freezes mid-generation.",
5
5
  "keywords": [
6
6
  "opencode",