opencode-auto-resume 1.0.19 → 1.0.21

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 +89 -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,54 @@ 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 listResponse = await ctx.client.session.list();
12704
+ const sessions2 = extractMessages(listResponse);
12705
+ const currentSession = sessions2.find((s) => s.id === sid);
12706
+ const status = currentSession?.status;
12707
+ if (status === "busy") {
12708
+ await log("debug", `Session ${short(sid)} is busy, likely executing a tool`);
12709
+ return true;
12710
+ }
12711
+ const response = await ctx.client.session.messages({ path: { id: sid } });
12712
+ const messages = extractMessages(response);
12713
+ const lastMsg = messages[messages.length - 1];
12714
+ if (!lastMsg)
12715
+ return false;
12716
+ const rawRole = lastMsg?.role ?? lastMsg?.info?.role;
12717
+ if (rawRole !== "assistant")
12718
+ return false;
12719
+ const toolCall = lastMsg.toolCall;
12720
+ const toolCalls = lastMsg.tool_calls;
12721
+ const parts = lastMsg.parts;
12722
+ const hasToolCall = toolCall !== undefined || (toolCalls?.length ?? 0) > 0 || (parts?.some((p) => p.type === "tool-call" || p.type === "tool_use") ?? false);
12723
+ return hasToolCall;
12724
+ } catch (err) {
12725
+ const errMsg = err instanceof Error ? err.message : String(err);
12726
+ await log("debug", `checkSessionHasActiveTool failed for ${short(sid)}: ${errMsg}`);
12727
+ return false;
12728
+ }
12729
+ }
12682
12730
  async function checkSubagentStatus(parentSid) {
12683
12731
  try {
12684
12732
  const response = await ctx.client.session.list();
@@ -12698,10 +12746,15 @@ var AutoResumePlugin = async (ctx, options) => {
12698
12746
  const rawRole = lastMsg?.role ?? lastMsg?.info?.role;
12699
12747
  if (lastMsg && rawRole === "assistant" && (("error" in lastMsg) || lastMsg.info && ("error" in lastMsg.info))) {
12700
12748
  await log("debug", `Subagent ${short(sId)} appears crashed`);
12701
- return "crashed";
12749
+ return { status: "crashed" };
12702
12750
  }
12703
12751
  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;
12752
+ if (!msgTime)
12753
+ continue;
12754
+ const toolCall = lastMsg.toolCall;
12755
+ const toolCalls = lastMsg.tool_calls;
12756
+ const parts = lastMsg.parts;
12757
+ const hasToolCall = toolCall !== undefined || (toolCalls?.length ?? 0) > 0 || (parts?.some((p) => p.type === "tool-call") ?? false);
12705
12758
  const isStuck = hasToolCall ? now - msgTime > SUBAGENT_STUCK_MS * 3 : now - msgTime > SUBAGENT_STUCK_MS;
12706
12759
  if (isStuck) {
12707
12760
  await log("debug", `Subagent ${short(sId)} stuck - no new text in >${hasToolCall ? 3 : 1}min`);
@@ -12770,10 +12823,10 @@ var AutoResumePlugin = async (ctx, options) => {
12770
12823
  }
12771
12824
  return false;
12772
12825
  }
12773
- function trackToolCall(w, toolName2) {
12826
+ function trackToolCall(w, toolName) {
12774
12827
  const now = Date.now();
12775
12828
  w.recentToolCalls = w.recentToolCalls.filter((call) => now - call.at < 120000);
12776
- w.recentToolCalls.push({ toolName: toolName2, at: now });
12829
+ w.recentToolCalls.push({ toolName, at: now });
12777
12830
  const recentTools = w.recentToolCalls.slice(-15).map((call) => call.toolName);
12778
12831
  if (recentTools.length < 6)
12779
12832
  return false;
@@ -12816,18 +12869,18 @@ var AutoResumePlugin = async (ctx, options) => {
12816
12869
  continue;
12817
12870
  const toolCall = msg.toolCall;
12818
12871
  if (toolCall && typeof toolCall === "object" && "name" in toolCall) {
12819
- const toolName2 = toolCall.name;
12820
- if (toolName2) {
12821
- trackToolCall(w, toolName2);
12872
+ const toolName = toolCall.name;
12873
+ if (toolName) {
12874
+ trackToolCall(w, toolName);
12822
12875
  }
12823
12876
  }
12824
12877
  const toolCalls = msg.tool_calls;
12825
12878
  if (toolCalls) {
12826
12879
  for (const tc of toolCalls) {
12827
12880
  if (typeof tc === "object" && "name" in tc) {
12828
- const toolName2 = tc.name;
12829
- if (toolName2) {
12830
- trackToolCall(w, toolName2);
12881
+ const toolName = tc.name;
12882
+ if (toolName) {
12883
+ trackToolCall(w, toolName);
12831
12884
  }
12832
12885
  }
12833
12886
  }
@@ -12847,14 +12900,15 @@ var AutoResumePlugin = async (ctx, options) => {
12847
12900
  isReasoning = true;
12848
12901
  } else if (partType === "tool_use") {
12849
12902
  isToolUse = true;
12850
- const toolName2 = part.name ?? "unknown";
12851
- text = `tool_use: ${toolName2}`;
12903
+ const toolName = part.name ?? "unknown";
12904
+ text = `tool_use: ${toolName}`;
12852
12905
  } else {
12853
12906
  continue;
12854
12907
  }
12855
12908
  allAssistantText += text + `
12856
12909
  `;
12857
12910
  if (isToolUse) {
12911
+ const toolName = part.name ?? "unknown";
12858
12912
  const isLoop = trackToolCall(w, toolName);
12859
12913
  if (isLoop && w.toolLoopAttempts < 2) {
12860
12914
  w.toolLoopAttempts++;
@@ -12880,8 +12934,8 @@ var AutoResumePlugin = async (ctx, options) => {
12880
12934
  if (containsToolCallAsText(text)) {
12881
12935
  const toolMatch = text.match(/<function=([a-zA-Z_]+)/) || text.match(/<invoke\s+name=([a-zA-Z_]+)/) || text.match(/"name":\s*"([a-zA-Z_]+)/);
12882
12936
  if (toolMatch) {
12883
- const toolName2 = toolMatch[1] || "unknown";
12884
- const isLoop = trackToolCall(w, toolName2);
12937
+ const toolName = toolMatch[1] || "unknown";
12938
+ const isLoop = trackToolCall(w, toolName);
12885
12939
  if (isLoop && w.toolLoopAttempts < 2) {
12886
12940
  w.toolLoopAttempts++;
12887
12941
  const loopCandidate = {
@@ -13117,8 +13171,13 @@ var AutoResumePlugin = async (ctx, options) => {
13117
13171
  tryAbortAndResume(sid, w);
13118
13172
  }
13119
13173
  } else if (subStatus.status === "idle") {
13120
- await log("info", `All subagents idle, parent ${short(sid)} stuck. Triggering abort+resume.`);
13121
- tryAbortAndResume(sid, w);
13174
+ const hasBusySub = await hasBusySubagents(sid);
13175
+ if (hasBusySub) {
13176
+ await log("debug", `Subagents exist but not busy yet, waiting for startup...`);
13177
+ } else {
13178
+ await log("info", `Parent ${short(sid)} stuck with no active subagents. Triggering abort+resume.`);
13179
+ tryAbortAndResume(sid, w);
13180
+ }
13122
13181
  } else {
13123
13182
  await log("debug", `Subagent still running, waiting...`);
13124
13183
  }
@@ -13137,6 +13196,12 @@ var AutoResumePlugin = async (ctx, options) => {
13137
13196
  continue;
13138
13197
  w.lastSubagentCheckAt = now;
13139
13198
  if (w.lastActivityAt > 0 && now - w.lastActivityAt > subagentWaitMs) {
13199
+ const hasActiveTool = await checkSessionHasActiveTool(sid);
13200
+ if (hasActiveTool) {
13201
+ await log("debug", `Parent ${short(sid)} has active tool call, skipping abort check`);
13202
+ w.lastSubagentCheckAt = now;
13203
+ continue;
13204
+ }
13140
13205
  const subStatus = await checkSubagentStatus(sid);
13141
13206
  if (subStatus.status === "idle" || subStatus.status === "unknown") {
13142
13207
  await log("info", `Parent ${short(sid)} stuck with no active subagents. Triggering abort+resume.`);
@@ -13365,14 +13430,6 @@ var AutoResumePlugin = async (ctx, options) => {
13365
13430
  },
13366
13431
  config: async () => {
13367
13432
  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
13433
  },
13377
13434
  tool: {
13378
13435
  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.21",
4
4
  "description": "OpenCode plugin that automatically resumes stalled LLM sessions when thinking/streaming freezes mid-generation.",
5
5
  "keywords": [
6
6
  "opencode",