@khalilgharbaoui/opencode-claude-code-plugin 0.53.3 → 0.53.4

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 CHANGED
@@ -3860,8 +3860,8 @@ function buildProxyTimeoutError(toolName, ms) {
3860
3860
  return new Error(base);
3861
3861
  }
3862
3862
  var TASK_PROXY_NOTE = "This and task_batch are the ONLY tools that dispatch opencode subagents (including user @-mentions). Claude Code's built-in TaskCreate/TaskUpdate manage a local todo list and cannot dispatch subagents. Do not search config files to verify a subagent type exists: invalid types fail fast with a clear error. Calls block until the subagent finishes. For two or more independent subagents in one response use task_batch, not several task calls: those run one after another. Task calls have no proxy deadline by default: the call waits for the subagent to finish (a positive proxyToolTimeoutMs override adds a deadline).";
3863
- var TASK_BACKGROUND_NOTE = 'Fire-and-collect: set `background: true` and the call returns at once with `<task id="ses_..." state="running">` instead of the subagent\'s answer. Keep working on something that does not overlap it, then end your turn: when the subagent finishes, opencode delivers its result to this conversation on its own as a new message. Do NOT poll, sleep or loop waiting for it. The `id` in that envelope is the task_id: pass it to task_status to read a result the notification did not deliver, or to task_cancel to stop a background subagent you no longer want.';
3864
- var TASK_BACKGROUND_NOTE_V2 = 'Fire-and-collect: set `background: true` and the call returns at once with `The subagent is working in the background (sessionID: ses_...)` instead of the subagent\'s answer. Keep working on something that does not overlap it, then end your turn: when the subagent finishes, opencode delivers its result to this conversation on its own, as a `<subagent sessionID="..." state="completed">` message. Do NOT poll, sleep or loop waiting for it. That sessionID is the task_id: pass it to task_status to read a result the notification did not deliver, or to task_cancel to stop a background subagent you no longer want.';
3863
+ var TASK_BACKGROUND_NOTE = 'Fire-and-collect: set `background: true` and the call returns at once with `<task id="ses_..." state="running">` instead of the subagent\'s answer. Keep working on something that does not overlap it, then end your turn: when the subagent finishes, opencode delivers its result to this conversation on its own as a new message. Do NOT poll, sleep or loop waiting for it. The `id` in that envelope is the task_id: pass it to task_status to read a result the notification did not deliver, or to task_cancel to stop a background subagent you no longer want. The dispatch result may end with how many background subagents are running now: that is for information, never a reason to poll or wait.';
3864
+ var TASK_BACKGROUND_NOTE_V2 = 'Fire-and-collect: set `background: true` and the call returns at once with `The subagent is working in the background (sessionID: ses_...)` instead of the subagent\'s answer. Keep working on something that does not overlap it, then end your turn: when the subagent finishes, opencode delivers its result to this conversation on its own, as a `<subagent sessionID="..." state="completed">` message. Do NOT poll, sleep or loop waiting for it. That sessionID is the task_id: pass it to task_status to read a result the notification did not deliver, or to task_cancel to stop a background subagent you no longer want. The dispatch result may end with how many background subagents are running now: that is for information, never a reason to poll or wait.';
3865
3865
  var TASK_BATCH_TOOL_NAME = "task_batch";
3866
3866
  var TASK_BATCH_PROXY_NOTE = "Use this instead of several task calls in one response: Claude Code runs MCP tool calls one at a time, so separate task calls run serially even when emitted together, while one task_batch call fans them out as parallel opencode task calls. Each task takes the same fields as the task tool. Results come back in task order, each labelled. Like task it has no proxy deadline by default (a positive proxyToolTimeoutMs override adds one).";
3867
3867
  var TASK_INPUT_REQUIRED = ["description", "prompt", "subagent_type"];
@@ -5941,23 +5941,9 @@ function noteBackgroundDispatchResult(sessionKey2, result, now = Date.now()) {
5941
5941
  }
5942
5942
  async function countRunningBackgroundTasks() {
5943
5943
  const entries = [...started.entries()];
5944
- const cancelledIds = /* @__PURE__ */ new Set();
5945
- for (const ids of cancelled.values()) for (const id of ids) cancelledIds.add(id);
5944
+ const cancelledIds = cancelledTaskIds();
5946
5945
  const states = await Promise.all(
5947
- entries.map(async ([taskId]) => {
5948
- if (cancelledIds.has(taskId)) return "stopped";
5949
- try {
5950
- const runState = await fetchSessionRunState(taskId);
5951
- if (runState !== "unknown") {
5952
- return isBackgroundTaskRunning(runState, []) ? "running" : "stopped";
5953
- }
5954
- const replies = await fetchSessionReplies(taskId);
5955
- if (replies === void 0) return "unreadable";
5956
- return isBackgroundTaskRunning(runState, replies) ? "running" : "stopped";
5957
- } catch {
5958
- return "unreadable";
5959
- }
5960
- })
5946
+ entries.map(([taskId]) => backgroundTaskState(taskId, cancelledIds))
5961
5947
  );
5962
5948
  return {
5963
5949
  running: states.filter((state) => state === "running").length,
@@ -5965,6 +5951,87 @@ async function countRunningBackgroundTasks() {
5965
5951
  unreadable: states.filter((state) => state === "unreadable").length
5966
5952
  };
5967
5953
  }
5954
+ function cancelledTaskIds() {
5955
+ const ids = /* @__PURE__ */ new Set();
5956
+ for (const set of cancelled.values()) for (const id of set) ids.add(id);
5957
+ return ids;
5958
+ }
5959
+ async function backgroundTaskState(taskId, cancelledIds) {
5960
+ if (cancelledIds.has(taskId)) return "stopped";
5961
+ try {
5962
+ const runState = await fetchSessionRunState(taskId);
5963
+ if (runState !== "unknown") {
5964
+ return isBackgroundTaskRunning(runState, []) ? "running" : "stopped";
5965
+ }
5966
+ const replies = await fetchSessionReplies(taskId);
5967
+ if (replies === void 0) return "unreadable";
5968
+ return isBackgroundTaskRunning(runState, replies) ? "running" : "stopped";
5969
+ } catch {
5970
+ return "unreadable";
5971
+ }
5972
+ }
5973
+ var BACKGROUND_COUNT_BUDGET_MS = 500;
5974
+ var BACKGROUND_COUNT_CONCURRENCY = 4;
5975
+ async function countOtherRunningWithin(exclude, budgetMs) {
5976
+ const taskIds = [...started.keys()].filter((taskId) => !exclude.has(taskId));
5977
+ if (taskIds.length === 0) return 0;
5978
+ const cancelledIds = cancelledTaskIds();
5979
+ let overBudget = false;
5980
+ let unreadable = false;
5981
+ let next = 0;
5982
+ let running = 0;
5983
+ const worker = async () => {
5984
+ while (!overBudget && !unreadable && next < taskIds.length) {
5985
+ const taskId = taskIds[next++];
5986
+ const state = await backgroundTaskState(taskId, cancelledIds);
5987
+ if (state === "running") running++;
5988
+ if (state === "unreadable") unreadable = true;
5989
+ }
5990
+ };
5991
+ const workers = Array.from(
5992
+ { length: Math.min(BACKGROUND_COUNT_CONCURRENCY, taskIds.length) },
5993
+ worker
5994
+ );
5995
+ let timer;
5996
+ const deadline = new Promise((resolve6) => {
5997
+ timer = setTimeout(() => resolve6("over"), budgetMs);
5998
+ timer.unref?.();
5999
+ });
6000
+ try {
6001
+ const outcome = await Promise.race([Promise.all(workers).then(() => "done"), deadline]);
6002
+ if (outcome === "over") {
6003
+ overBudget = true;
6004
+ return void 0;
6005
+ }
6006
+ return unreadable ? void 0 : running;
6007
+ } catch {
6008
+ overBudget = true;
6009
+ return void 0;
6010
+ } finally {
6011
+ clearTimeout(timer);
6012
+ }
6013
+ }
6014
+ async function withBackgroundRunningCount(result, budgetMs = BACKGROUND_COUNT_BUDGET_MS) {
6015
+ if (result.kind !== "text" || result.isError === true) return result;
6016
+ const dispatched = backgroundTaskIdsIn(result.text);
6017
+ if (dispatched.length === 0) return result;
6018
+ const others = await countOtherRunningWithin(new Set(dispatched), budgetMs);
6019
+ if (others === void 0) {
6020
+ log.info("background running count omitted: a lookup was slow or unreadable", {
6021
+ budgetMs,
6022
+ dispatched: dispatched.length
6023
+ });
6024
+ return result;
6025
+ }
6026
+ return { ...result, text: result.text + formatBackgroundRunningLine(others, dispatched.length) };
6027
+ }
6028
+ function formatBackgroundRunningLine(others, dispatched) {
6029
+ const total = others + dispatched;
6030
+ const including = dispatched === 1 ? "this one" : `the ${dispatched} just started`;
6031
+ return `
6032
+
6033
+ Background subagents running now: ${total} (including ${including}).`;
6034
+ }
5968
6035
  var lastGate;
5969
6036
  function recordBackgroundSubagentGate(gate, now = Date.now()) {
5970
6037
  lastGate = { ...gate, at: now };
@@ -12200,6 +12267,7 @@ This opencode host runs background subagents, so \`mcp__opencode_proxy__task\` t
12200
12267
  - A background dispatch returns at once with \`<task id="ses_..." state="running">\` instead of the subagent's answer. That is success, not a truncated result.
12201
12268
  - After starting one, keep working on something that does not overlap it, then end your turn normally. When the subagent finishes, opencode delivers its result into this conversation as a new message on its own. Do not sleep, poll, or ask the operator to wait.
12202
12269
  - The \`id\` in that envelope is the task_id. \`mcp__opencode_proxy__task_status\` reads a result back if the automatic delivery did not arrive (an interrupted or errored turn); \`mcp__opencode_proxy__task_cancel\` stops a background subagent you no longer want. Neither is a progress poll.
12270
+ - A background dispatch's result may end with \`Background subagents running now: N\`. That number is for information, so you can avoid starting more than the work needs; it is never a reason to poll, sleep or wait.
12203
12271
  - Foreground is still the default and still the right choice when you need the answer before you can continue. Use background only for work that genuinely runs alongside yours.`;
12204
12272
  var BACKGROUND_SUBAGENT_HINT_V2 = `## Background subagents
12205
12273
 
@@ -12208,6 +12276,7 @@ This opencode host runs background subagents, so \`mcp__opencode_proxy__task\` t
12208
12276
  - A background dispatch returns at once with \`The subagent is working in the background (sessionID: ses_...)\` instead of the subagent's answer. That is success, not a truncated result.
12209
12277
  - After starting one, keep working on something that does not overlap it, then end your turn normally. When the subagent finishes, opencode delivers its result into this conversation on its own, as a \`<subagent sessionID="..." state="completed">\` message. Do not sleep, poll, or ask the operator to wait.
12210
12278
  - That \`sessionID\` is the task_id. \`mcp__opencode_proxy__task_status\` reads a result back if the automatic delivery did not arrive (an interrupted or errored turn); \`mcp__opencode_proxy__task_cancel\` stops a background subagent you no longer want. Neither is a progress poll.
12279
+ - A background dispatch's result may end with \`Background subagents running now: N\`. That number is for information, so you can avoid starting more than the work needs; it is never a reason to poll, sleep or wait.
12211
12280
  - Foreground is still the default and still the right choice when you need the answer before you can continue. Use background only for work that genuinely runs alongside yours.`;
12212
12281
  function backgroundSubagentHint(dialect) {
12213
12282
  return dialect === "v2" ? BACKGROUND_SUBAGENT_HINT_V2 : BACKGROUND_SUBAGENT_HINT;
@@ -16956,8 +17025,14 @@ var ClaudeCodeLanguageModel = class {
16956
17025
  await new Promise((resolve6) => setTimeout(resolve6, FORWARD_SETTLE_MS));
16957
17026
  if (state.controllerClosed) return;
16958
17027
  }
16959
- for (const { call, result } of previousPendingProxyMatches) {
16960
- if (result) {
17028
+ for (const { call, result: opencodeResult } of previousPendingProxyMatches) {
17029
+ if (opencodeResult) {
17030
+ let result = opencodeResult;
17031
+ if (call.toolName === "task" || call.toolName === TASK_BATCH_TOOL_NAME) {
17032
+ noteBackgroundDispatchResult(sk, result);
17033
+ result = await withBackgroundRunningCount(result);
17034
+ if (state.controllerClosed) return;
17035
+ }
16961
17036
  const channelClosed = isPendingProxyCallChannelClosed(call);
16962
17037
  log.info("resolving pending proxy call from tool result prompt", {
16963
17038
  sessionKey: sk,
@@ -16973,9 +17048,6 @@ var ClaudeCodeLanguageModel = class {
16973
17048
  recoveryRequired: channelClosed || state.unattendedTurnEnded
16974
17049
  });
16975
17050
  }
16976
- if (call.toolName === "task" || call.toolName === TASK_BATCH_TOOL_NAME) {
16977
- noteBackgroundDispatchResult(sk, result);
16978
- }
16979
17051
  resolvePendingProxyCallById(call.toolCallId, result);
16980
17052
  } else {
16981
17053
  log.info(