better-opencode-async-agents 0.2.1 → 0.4.0

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 (40) hide show
  1. package/dist/constants.d.ts +8 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/helpers.d.ts +17 -2
  4. package/dist/helpers.d.ts.map +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +652 -33
  7. package/dist/index.js.map +21 -17
  8. package/dist/manager/events.d.ts +1 -0
  9. package/dist/manager/events.d.ts.map +1 -1
  10. package/dist/manager/index.d.ts +20 -0
  11. package/dist/manager/index.d.ts.map +1 -1
  12. package/dist/manager/notifications.d.ts.map +1 -1
  13. package/dist/manager/polling.d.ts +1 -1
  14. package/dist/manager/polling.d.ts.map +1 -1
  15. package/dist/manager/task-lifecycle.d.ts +2 -2
  16. package/dist/manager/task-lifecycle.d.ts.map +1 -1
  17. package/dist/prompts.d.ts +1 -1
  18. package/dist/prompts.d.ts.map +1 -1
  19. package/dist/server/cors.d.ts +22 -0
  20. package/dist/server/cors.d.ts.map +1 -0
  21. package/dist/server/index.d.ts +20 -0
  22. package/dist/server/index.d.ts.map +1 -0
  23. package/dist/server/routes.d.ts +14 -0
  24. package/dist/server/routes.d.ts.map +1 -0
  25. package/dist/server/sse.d.ts +44 -0
  26. package/dist/server/sse.d.ts.map +1 -0
  27. package/dist/server/types.d.ts +69 -0
  28. package/dist/server/types.d.ts.map +1 -0
  29. package/dist/storage.d.ts +34 -0
  30. package/dist/storage.d.ts.map +1 -1
  31. package/dist/tools/cancel.d.ts +3 -3
  32. package/dist/tools/cancel.d.ts.map +1 -1
  33. package/dist/tools/list.d.ts.map +1 -1
  34. package/dist/tools/output.d.ts +3 -2
  35. package/dist/tools/output.d.ts.map +1 -1
  36. package/dist/tools/task.d.ts +1 -0
  37. package/dist/tools/task.d.ts.map +1 -1
  38. package/dist/types.d.ts +7 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -125,7 +125,7 @@ function getHomeDir() {
125
125
  console.warn("[constants] Could not determine home directory, using /tmp");
126
126
  return "/tmp";
127
127
  }
128
- var COMPLETION_DISPLAY_DURATION = 1e4, SPINNER_FRAMES, HOME_DIR, STORAGE_DIR, TASKS_FILE, FORK_MAX_TOKENS = 1e5, FORK_TOOL_RESULT_LIMIT = 1500, FORK_TOOL_PARAMS_LIMIT = 200;
128
+ var COMPLETION_DISPLAY_DURATION = 1e4, SPINNER_FRAMES, HOME_DIR, STORAGE_DIR, TASKS_FILE, FORK_MAX_TOKENS = 1e5, FORK_TOOL_RESULT_LIMIT = 1500, FORK_TOOL_PARAMS_LIMIT = 200, DEFAULT_API_PORT = 5165, DEFAULT_API_HOST = "127.0.0.1", SERVER_INFO_FILENAME = "server.json", MAX_PORT_RETRY = 10, HEARTBEAT_INTERVAL_MS = 30000, MAX_SSE_SUBSCRIBERS = 50, DEFAULT_TASK_LIMIT = 50, MAX_TASK_LIMIT = 200;
129
129
  var init_constants = __esm(() => {
130
130
  SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
131
131
  HOME_DIR = getHomeDir();
@@ -136,11 +136,15 @@ var init_constants = __esm(() => {
136
136
  // src/storage.ts
137
137
  var exports_storage = {};
138
138
  __export(exports_storage, {
139
+ writeServerInfo: () => writeServerInfo,
139
140
  saveTasks: () => saveTasks,
140
141
  saveTask: () => saveTask,
142
+ readServerInfo: () => readServerInfo,
141
143
  loadTasks: () => loadTasks,
144
+ loadAllTasks: () => loadAllTasks,
142
145
  getPersistedTask: () => getPersistedTask,
143
146
  ensureStorageDir: () => ensureStorageDir,
147
+ deleteServerInfo: () => deleteServerInfo,
144
148
  deletePersistedTask: () => deletePersistedTask
145
149
  });
146
150
  async function getFs() {
@@ -229,9 +233,75 @@ async function deletePersistedTask(sessionID) {
229
233
  await saveTasks(tasks);
230
234
  }
231
235
  }
232
- var fsPromises = null;
236
+ async function loadAllTasks() {
237
+ const tasks = await loadTasks();
238
+ return Object.entries(tasks).map(([sessionID, task]) => ({ sessionID, ...task }));
239
+ }
240
+ async function writeServerInfo(info) {
241
+ try {
242
+ await ensureStorageDir();
243
+ const content = JSON.stringify(info, null, 2);
244
+ if (hasBun()) {
245
+ await Bun.write(SERVER_INFO_FILE, content);
246
+ return;
247
+ }
248
+ const fs = await getFs();
249
+ if (fs) {
250
+ await fs.writeFile(SERVER_INFO_FILE, content, "utf-8");
251
+ return;
252
+ }
253
+ console.warn("[storage] No file system API available for writing server info");
254
+ } catch (error) {
255
+ console.warn(`[storage] Failed to write server info: ${error}`);
256
+ throw error;
257
+ }
258
+ }
259
+ async function readServerInfo() {
260
+ try {
261
+ if (hasBun()) {
262
+ const file = Bun.file(SERVER_INFO_FILE);
263
+ const exists = await file.exists();
264
+ if (!exists) {
265
+ return null;
266
+ }
267
+ const content = await file.text();
268
+ return JSON.parse(content);
269
+ }
270
+ const fs = await getFs();
271
+ if (fs) {
272
+ const content = await fs.readFile(SERVER_INFO_FILE, "utf-8");
273
+ return JSON.parse(content);
274
+ }
275
+ return null;
276
+ } catch (error) {
277
+ const code = error.code;
278
+ if (code !== "ENOENT") {
279
+ console.warn(`[storage] Failed to read server info: ${error}`);
280
+ }
281
+ return null;
282
+ }
283
+ }
284
+ async function deleteServerInfo() {
285
+ try {
286
+ if (hasBun()) {
287
+ const file = Bun.file(SERVER_INFO_FILE);
288
+ const exists = await file.exists();
289
+ if (exists) {
290
+ await file.delete();
291
+ }
292
+ return;
293
+ }
294
+ const fs = await getFs();
295
+ if (fs) {
296
+ await fs.unlink(SERVER_INFO_FILE).catch(() => {});
297
+ return;
298
+ }
299
+ } catch {}
300
+ }
301
+ var fsPromises = null, SERVER_INFO_FILE;
233
302
  var init_storage = __esm(() => {
234
303
  init_constants();
304
+ SERVER_INFO_FILE = `${STORAGE_DIR}/${SERVER_INFO_FILENAME}`;
235
305
  });
236
306
 
237
307
  // node_modules/tiktoken/lite/tiktoken_bg.wasm
@@ -801,9 +871,9 @@ Duration: ${duration}
801
871
  Error fetching messages: ${errMsg}`,
802
872
  listHeader: `# Background Tasks
803
873
 
804
- | Task ID | Description | Agent | Status | Duration |
805
- |---------|-------------|-------|--------|----------|`,
806
- listSummary: (total, running, completed, errored, cancelled) => `**Total: ${total}** | ⏳ ${running} running | ✓ ${completed} completed | ✗ ${errored} error | ⊘ ${cancelled} cancelled`,
874
+ | Task ID | Description | Agent | Status | Duration | Tools |
875
+ |---------|-------------|-------|--------|----------|-------|`,
876
+ listSummary: (total, running, completed, errored, cancelled, totalToolCalls) => `**Total: ${total}** | ⏳ ${running} running | ✓ ${completed} completed | ✗ ${errored} error | ⊘ ${cancelled} cancelled | \uD83D\uDD27 ${totalToolCalls} tool calls`,
807
877
  progressSection: (tools) => `
808
878
  | Last tools | ${tools.join(" → ")} |`
809
879
  };
@@ -818,6 +888,7 @@ var PLACEHOLDER_TEXT = {
818
888
 
819
889
  // src/helpers.ts
820
890
  function setTaskStatus(task, status, options) {
891
+ const previousStatus = task.status;
821
892
  task.status = status;
822
893
  if (status === "completed" || status === "error" || status === "cancelled") {
823
894
  task.completedAt = new Date().toISOString();
@@ -826,13 +897,35 @@ function setTaskStatus(task, status, options) {
826
897
  task.error = options.error;
827
898
  }
828
899
  options?.persistFn?.(task);
900
+ if (options?.emitFn && previousStatus !== status) {
901
+ if (status === "completed") {
902
+ options.emitFn("task.completed", task);
903
+ } else if (status === "error") {
904
+ options.emitFn("task.error", task);
905
+ } else if (status === "cancelled") {
906
+ options.emitFn("task.cancelled", task);
907
+ }
908
+ }
909
+ }
910
+ function shortId(sessionId, minLen = 8) {
911
+ if (!sessionId.startsWith("ses_")) {
912
+ return sessionId.slice(0, minLen + 4);
913
+ }
914
+ const suffix = sessionId.slice(4);
915
+ return `ses_${suffix.slice(0, minLen)}`;
829
916
  }
830
- function shortId(sessionId) {
917
+ function uniqueShortId(sessionId, siblingIds, minLen = 8) {
831
918
  if (!sessionId.startsWith("ses_")) {
832
- return sessionId.slice(0, 12);
919
+ return sessionId.slice(0, minLen + 4);
833
920
  }
834
921
  const suffix = sessionId.slice(4);
835
- return `ses_${suffix.slice(0, 8)}`;
922
+ for (let len = minLen;len <= suffix.length; len++) {
923
+ const candidate = `ses_${suffix.slice(0, len)}`;
924
+ const hasClash = siblingIds.some((otherId) => otherId !== sessionId && otherId.startsWith(candidate));
925
+ if (!hasClash)
926
+ return candidate;
927
+ }
928
+ return sessionId;
836
929
  }
837
930
  function formatDuration(startStr, endStr) {
838
931
  const start = new Date(startStr);
@@ -926,7 +1019,7 @@ async function startEventSubscription(client, handleEvent) {
926
1019
  }
927
1020
  }
928
1021
  function handleEvent(event, callbacks) {
929
- const { clearAllTasks, getTasksArray, notifyParentSession, persistTask } = callbacks;
1022
+ const { clearAllTasks, getTasksArray, notifyParentSession, persistTask, emitTaskEvent } = callbacks;
930
1023
  const props = event.properties;
931
1024
  if (event.type === "tui.command.execute") {
932
1025
  const command = props?.command;
@@ -954,7 +1047,8 @@ function handleEvent(event, callbacks) {
954
1047
  if (task.status === "running") {
955
1048
  setTaskStatus(task, "cancelled", {
956
1049
  error: "Session deleted",
957
- persistFn: persistTask
1050
+ persistFn: persistTask,
1051
+ emitFn: emitTaskEvent
958
1052
  });
959
1053
  }
960
1054
  return;
@@ -971,7 +1065,7 @@ function handleEvent(event, callbacks) {
971
1065
  }
972
1066
  if (task.status !== "running")
973
1067
  return;
974
- setTaskStatus(task, "completed", { persistFn: persistTask });
1068
+ setTaskStatus(task, "completed", { persistFn: persistTask, emitFn: emitTaskEvent });
975
1069
  notifyParentSession(task);
976
1070
  }
977
1071
  }
@@ -1003,6 +1097,14 @@ function showProgressToast(allTasks, animationFrame, client, getTasksArray) {
1003
1097
  const nextAnimationFrame = (animationFrame + 1) % SPINNER_FRAMES.length;
1004
1098
  const spinner = SPINNER_FRAMES[nextAnimationFrame];
1005
1099
  const totalToolCalls = batchTasks.reduce((sum, t) => sum + (t.progress?.toolCalls ?? 0), 0);
1100
+ const aggregatedToolCalls = {};
1101
+ for (const t of batchTasks) {
1102
+ if (t.progress?.toolCallsByName) {
1103
+ for (const [toolName, count] of Object.entries(t.progress.toolCallsByName)) {
1104
+ aggregatedToolCalls[toolName] = (aggregatedToolCalls[toolName] ?? 0) + count;
1105
+ }
1106
+ }
1107
+ }
1006
1108
  const taskLines = [];
1007
1109
  const batchRunning = runningTasks.filter((t) => t.batchId === activeBatchId);
1008
1110
  for (const task of batchRunning) {
@@ -1036,7 +1138,14 @@ function showProgressToast(allTasks, animationFrame, client, getTasksArray) {
1036
1138
  const barLength = 10;
1037
1139
  const filledLength = Math.round(finishedCount / Math.max(totalTasks, 1) * barLength);
1038
1140
  const progressBar = "█".repeat(filledLength) + "░".repeat(barLength - filledLength);
1039
- const summary = `[${progressBar}] ${finishedCount}/${totalTasks} agents (${progressPercent}%) | ${totalToolCalls} tool calls`;
1141
+ const sortedTools = Object.entries(aggregatedToolCalls).sort(([, a], [, b]) => b - a);
1142
+ let toolBreakdown = "";
1143
+ if (sortedTools.length > 0) {
1144
+ const top3 = sortedTools.slice(0, 3).map(([name, count]) => `${name}:${count}`).join(" ");
1145
+ const remaining = sortedTools.length - 3;
1146
+ toolBreakdown = remaining > 0 ? ` (${top3} +${remaining} more)` : ` (${top3})`;
1147
+ }
1148
+ const summary = `[${progressBar}] ${finishedCount}/${totalTasks} agents (${progressPercent}%) | ${totalToolCalls} calls${toolBreakdown}`;
1040
1149
  const tuiClient = client;
1041
1150
  if (!tuiClient.tui?.showToast)
1042
1151
  return;
@@ -1201,7 +1310,7 @@ function stopPolling(pollingInterval) {
1201
1310
  clearInterval(pollingInterval);
1202
1311
  }
1203
1312
  }
1204
- async function pollRunningTasks(client, tasks, originalParentSessionID, updateTaskProgress, notifyParentSession2, clearAllTasks, stopPolling2, showProgressToast2, getTasksArray, getTaskMessages, persistTask) {
1313
+ async function pollRunningTasks(client, tasks, originalParentSessionID, updateTaskProgress, notifyParentSession2, clearAllTasks, stopPolling2, showProgressToast2, getTasksArray, getTaskMessages, persistTask, emitTaskEvent) {
1205
1314
  try {
1206
1315
  const statusResult = await client.session.status();
1207
1316
  const allStatuses = statusResult.data ?? {};
@@ -1240,7 +1349,7 @@ async function pollRunningTasks(client, tasks, originalParentSessionID, updateTa
1240
1349
  const messages = await getTaskMessages(task.sessionID);
1241
1350
  const hasAssistantResponse = messages.some((m) => m.info?.role === "assistant" && m.parts?.some((p) => p.type === "text" && p.text && p.text.length > 0));
1242
1351
  if (hasAssistantResponse) {
1243
- setTaskStatus(task, "completed", { persistFn: persistTask });
1352
+ setTaskStatus(task, "completed", { persistFn: persistTask, emitFn: emitTaskEvent });
1244
1353
  notifyParentSession2(task);
1245
1354
  continue;
1246
1355
  }
@@ -1250,7 +1359,7 @@ async function pollRunningTasks(client, tasks, originalParentSessionID, updateTa
1250
1359
  continue;
1251
1360
  }
1252
1361
  if (sessionStatus.type === "idle") {
1253
- setTaskStatus(task, "completed", { persistFn: persistTask });
1362
+ setTaskStatus(task, "completed", { persistFn: persistTask, emitFn: emitTaskEvent });
1254
1363
  notifyParentSession2(task);
1255
1364
  continue;
1256
1365
  }
@@ -1291,6 +1400,7 @@ async function updateTaskProgress(task, client) {
1291
1400
  return;
1292
1401
  const messages = messagesResult.data;
1293
1402
  let toolCalls = 0;
1403
+ const toolCallsByName = {};
1294
1404
  const allTools = [];
1295
1405
  for (const msg of messages) {
1296
1406
  const parts = msg.parts ?? msg.content ?? [];
@@ -1300,6 +1410,7 @@ async function updateTaskProgress(task, client) {
1300
1410
  for (const part of parts) {
1301
1411
  if (part.type === "tool" && part.tool) {
1302
1412
  toolCalls++;
1413
+ toolCallsByName[part.tool] = (toolCallsByName[part.tool] ?? 0) + 1;
1303
1414
  allTools.push(part.tool);
1304
1415
  }
1305
1416
  }
@@ -1307,11 +1418,13 @@ async function updateTaskProgress(task, client) {
1307
1418
  if (!task.progress) {
1308
1419
  task.progress = {
1309
1420
  toolCalls: 0,
1421
+ toolCallsByName: {},
1310
1422
  lastTools: [],
1311
1423
  lastUpdate: new Date().toISOString()
1312
1424
  };
1313
1425
  }
1314
1426
  task.progress.toolCalls = toolCalls;
1427
+ task.progress.toolCallsByName = toolCallsByName;
1315
1428
  task.progress.lastTools = allTools.slice(-3);
1316
1429
  task.progress.lastUpdate = new Date().toISOString();
1317
1430
  } catch {}
@@ -1436,7 +1549,7 @@ ${part.text}`);
1436
1549
 
1437
1550
  // src/manager/task-lifecycle.ts
1438
1551
  var FORK_METHOD = "inject";
1439
- async function launchTask(input, tasks, client, getOrCreateBatchId, setOriginalParentSessionID, startPolling2, notifyParentSession2) {
1552
+ async function launchTask(input, tasks, client, getOrCreateBatchId, setOriginalParentSessionID, startPolling2, notifyParentSession2, emitTaskEvent, persistTask) {
1440
1553
  if (!input.agent || input.agent.trim() === "") {
1441
1554
  throw new Error("Agent parameter is required");
1442
1555
  }
@@ -1511,12 +1624,16 @@ ${contextText}`;
1511
1624
  isForked: input.fork ?? false,
1512
1625
  progress: {
1513
1626
  toolCalls: 0,
1627
+ toolCallsByName: {},
1514
1628
  lastTools: [],
1515
1629
  lastUpdate: new Date().toISOString()
1516
1630
  }
1517
1631
  };
1518
1632
  setOriginalParentSessionID(input.parentSessionID);
1519
1633
  tasks.set(task.sessionID, task);
1634
+ if (persistTask) {
1635
+ await persistTask(task);
1636
+ }
1520
1637
  startPolling2();
1521
1638
  client.session.promptAsync({
1522
1639
  path: { id: sessionID },
@@ -1542,6 +1659,7 @@ ${contextText}`;
1542
1659
  existingTask.error = errorMessage;
1543
1660
  }
1544
1661
  existingTask.completedAt = new Date().toISOString();
1662
+ emitTaskEvent?.("task.error", existingTask);
1545
1663
  notifyParentSession2(existingTask);
1546
1664
  }
1547
1665
  });
@@ -1568,7 +1686,7 @@ async function getTaskMessages(sessionID, client) {
1568
1686
  }
1569
1687
  return messagesResult.data ?? messagesResult;
1570
1688
  }
1571
- async function checkAndUpdateTaskStatus(task, client, notifyParentSession2, skipNotification = false, getTaskMessages2) {
1689
+ async function checkAndUpdateTaskStatus(task, client, notifyParentSession2, skipNotification = false, getTaskMessages2, emitTaskEvent) {
1572
1690
  if (task.status !== "running") {
1573
1691
  return task;
1574
1692
  }
@@ -1579,6 +1697,7 @@ async function checkAndUpdateTaskStatus(task, client, notifyParentSession2, skip
1579
1697
  if (sessionStatus?.type === "idle") {
1580
1698
  task.status = "completed";
1581
1699
  task.completedAt = new Date().toISOString();
1700
+ emitTaskEvent?.("task.completed", task);
1582
1701
  if (!skipNotification) {
1583
1702
  notifyParentSession2(task);
1584
1703
  }
@@ -1591,6 +1710,7 @@ async function checkAndUpdateTaskStatus(task, client, notifyParentSession2, skip
1591
1710
  if (hasAssistantResponse) {
1592
1711
  task.status = "completed";
1593
1712
  task.completedAt = new Date().toISOString();
1713
+ emitTaskEvent?.("task.completed", task);
1594
1714
  if (!skipNotification) {
1595
1715
  notifyParentSession2(task);
1596
1716
  }
@@ -1627,12 +1747,29 @@ class BackgroundManager {
1627
1747
  currentBatchId = null;
1628
1748
  tasks = new Map;
1629
1749
  originalParentSessionID = null;
1750
+ taskEventListeners = [];
1630
1751
  constructor(ctx) {
1631
1752
  this.client = ctx.client;
1632
1753
  this.directory = ctx.directory;
1633
1754
  this.startEventSubscription();
1634
1755
  this.loadPersistedTasks();
1635
1756
  }
1757
+ onTaskEvent(callback) {
1758
+ this.taskEventListeners.push(callback);
1759
+ return () => {
1760
+ const index = this.taskEventListeners.indexOf(callback);
1761
+ if (index > -1) {
1762
+ this.taskEventListeners.splice(index, 1);
1763
+ }
1764
+ };
1765
+ }
1766
+ emitTaskEvent(eventType, task) {
1767
+ for (const listener of this.taskEventListeners) {
1768
+ try {
1769
+ listener(eventType, task);
1770
+ } catch {}
1771
+ }
1772
+ }
1636
1773
  async loadPersistedTasks() {
1637
1774
  try {
1638
1775
  const persisted = await loadTasks();
@@ -1642,8 +1779,8 @@ class BackgroundManager {
1642
1779
  async launch(input) {
1643
1780
  const task = await launchTask(input, this.tasks, this.client, () => this.getOrCreateBatchId(), (sessionID) => {
1644
1781
  this.originalParentSessionID = sessionID;
1645
- }, () => this.startPolling(), (t) => this.notifyParentSession(t));
1646
- await this.persistTask(task);
1782
+ }, () => this.startPolling(), (t) => this.notifyParentSession(t), (eventType, t) => this.emitTaskEvent(eventType, t), (t) => this.persistTask(t));
1783
+ this.emitTaskEvent("task.created", task);
1647
1784
  return task;
1648
1785
  }
1649
1786
  async persistTask(task) {
@@ -1654,7 +1791,13 @@ class BackgroundManager {
1654
1791
  createdAt: task.startedAt,
1655
1792
  status: task.status,
1656
1793
  resumeCount: task.resumeCount,
1657
- isForked: task.isForked
1794
+ isForked: task.isForked,
1795
+ completedAt: task.completedAt,
1796
+ error: task.error,
1797
+ result: task.result,
1798
+ progress: task.progress,
1799
+ startedAt: task.startedAt,
1800
+ batchId: task.batchId
1658
1801
  };
1659
1802
  try {
1660
1803
  await saveTask(task.sessionID, persisted);
@@ -1663,7 +1806,11 @@ class BackgroundManager {
1663
1806
  }
1664
1807
  }
1665
1808
  async cancelTask(taskId) {
1666
- return cancelTask(taskId, this.tasks, this.client);
1809
+ const task = this.tasks.get(taskId);
1810
+ await cancelTask(taskId, this.tasks, this.client);
1811
+ if (task) {
1812
+ this.emitTaskEvent("task.cancelled", task);
1813
+ }
1667
1814
  }
1668
1815
  clearAllTasks() {
1669
1816
  clearAllTasks(this.tasks, this.client, () => this.stopPolling());
@@ -1751,12 +1898,15 @@ class BackgroundManager {
1751
1898
  getAllTasks() {
1752
1899
  return Array.from(this.tasks.values());
1753
1900
  }
1901
+ getTaskSessionIds() {
1902
+ return Array.from(this.tasks.keys());
1903
+ }
1754
1904
  async getTaskMessages(sessionID) {
1755
1905
  return getTaskMessages(sessionID, this.client);
1756
1906
  }
1757
1907
  async checkAndUpdateTaskStatus(task, skipNotification = false) {
1758
1908
  const previousStatus = task.status;
1759
- const updatedTask = await checkAndUpdateTaskStatus(task, this.client, (t) => this.notifyParentSession(t), skipNotification, (sessionID) => this.getTaskMessages(sessionID));
1909
+ const updatedTask = await checkAndUpdateTaskStatus(task, this.client, (t) => this.notifyParentSession(t), skipNotification, (sessionID) => this.getTaskMessages(sessionID), (eventType, t) => this.emitTaskEvent(eventType, t));
1760
1910
  if (updatedTask.status !== previousStatus) {
1761
1911
  await this.persistTask(updatedTask);
1762
1912
  }
@@ -1903,12 +2053,20 @@ class BackgroundManager {
1903
2053
  this.pollingInterval = undefined;
1904
2054
  }
1905
2055
  async pollRunningTasks() {
1906
- await pollRunningTasks(this.client, this.tasks, this.originalParentSessionID, (task) => updateTaskProgress(task, this.client), (task) => this.notifyParentSession(task), () => this.clearAllTasks(), () => this.stopPolling(), (tasks) => this.showProgressToast(tasks), () => this.getAllTasks(), (sessionID) => this.getTaskMessages(sessionID), (task) => void this.persistTask(task));
2056
+ await pollRunningTasks(this.client, this.tasks, this.originalParentSessionID, (task) => this.updateTaskProgressWithEvent(task), (task) => this.notifyParentSession(task), () => this.clearAllTasks(), () => this.stopPolling(), (tasks) => this.showProgressToast(tasks), () => this.getAllTasks(), (sessionID) => this.getTaskMessages(sessionID), (task) => void this.persistTask(task), (eventType, task) => this.emitTaskEvent(eventType, task));
1907
2057
  }
1908
2058
  showProgressToast(tasks) {
1909
2059
  showProgressToast(tasks, this.animationFrame, this.client, () => this.getAllTasks());
1910
2060
  this.animationFrame = (this.animationFrame + 1) % 10;
1911
2061
  }
2062
+ async updateTaskProgressWithEvent(task) {
2063
+ const previousToolCalls = task.progress?.toolCalls ?? 0;
2064
+ await updateTaskProgress(task, this.client);
2065
+ const currentToolCalls = task.progress?.toolCalls ?? 0;
2066
+ if (currentToolCalls > previousToolCalls) {
2067
+ this.emitTaskEvent("task.updated", task);
2068
+ }
2069
+ }
1912
2070
  notifyParentSession(task) {
1913
2071
  notifyParentSession(task, this.client, this.directory, () => this.getAllTasks());
1914
2072
  }
@@ -1917,11 +2075,457 @@ class BackgroundManager {
1917
2075
  clearAllTasks: () => this.clearAllTasks(),
1918
2076
  getTasksArray: () => this.getAllTasks(),
1919
2077
  notifyParentSession: (task) => this.notifyParentSession(task),
1920
- persistTask: (task) => void this.persistTask(task)
2078
+ persistTask: (task) => void this.persistTask(task),
2079
+ emitTaskEvent: (eventType, task) => this.emitTaskEvent(eventType, task)
1921
2080
  }));
1922
2081
  }
1923
2082
  }
1924
2083
 
2084
+ // src/server/index.ts
2085
+ init_constants();
2086
+ init_storage();
2087
+
2088
+ // src/server/cors.ts
2089
+ var CORS_HEADERS = {
2090
+ "Access-Control-Allow-Origin": "*",
2091
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
2092
+ "Access-Control-Allow-Headers": "Content-Type"
2093
+ };
2094
+ function withCors(response) {
2095
+ for (const [key, value] of Object.entries(CORS_HEADERS)) {
2096
+ response.headers.set(key, value);
2097
+ }
2098
+ return response;
2099
+ }
2100
+ function jsonResponse(data, status = 200) {
2101
+ return withCors(new Response(JSON.stringify(data), {
2102
+ status,
2103
+ headers: { "Content-Type": "application/json" }
2104
+ }));
2105
+ }
2106
+ function errorResponse(error, status) {
2107
+ return jsonResponse({ error, status }, status);
2108
+ }
2109
+ function preflightResponse() {
2110
+ return withCors(new Response(null, { status: 204 }));
2111
+ }
2112
+
2113
+ // src/server/routes.ts
2114
+ init_constants();
2115
+ var startTime = Date.now();
2116
+ function handleHealth(_req, manager) {
2117
+ const tasks = manager.getAllTasks();
2118
+ const body = {
2119
+ status: "ok",
2120
+ uptime: Math.floor((Date.now() - startTime) / 1000),
2121
+ version: "1.0.0",
2122
+ taskCount: tasks.length
2123
+ };
2124
+ return jsonResponse(body);
2125
+ }
2126
+ function handleStats(_req, manager) {
2127
+ const tasks = manager.getAllTasks();
2128
+ const byStatus = {};
2129
+ const byAgent = {};
2130
+ const toolCallsByName = {};
2131
+ const durations = [];
2132
+ let activeTasks = 0;
2133
+ for (const task of tasks) {
2134
+ byStatus[task.status] = (byStatus[task.status] ?? 0) + 1;
2135
+ byAgent[task.agent] = (byAgent[task.agent] ?? 0) + 1;
2136
+ if (task.progress?.toolCallsByName) {
2137
+ for (const [toolName, count] of Object.entries(task.progress.toolCallsByName)) {
2138
+ toolCallsByName[toolName] = (toolCallsByName[toolName] ?? 0) + count;
2139
+ }
2140
+ }
2141
+ if (task.status === "running" || task.status === "resumed") {
2142
+ activeTasks++;
2143
+ }
2144
+ if (task.startedAt && task.completedAt) {
2145
+ const duration = new Date(task.completedAt).getTime() - new Date(task.startedAt).getTime();
2146
+ if (duration > 0)
2147
+ durations.push(duration);
2148
+ }
2149
+ }
2150
+ const body = {
2151
+ byStatus,
2152
+ byAgent,
2153
+ toolCallsByName,
2154
+ duration: {
2155
+ avg: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
2156
+ max: durations.length > 0 ? Math.max(...durations) : 0,
2157
+ min: durations.length > 0 ? Math.min(...durations) : 0
2158
+ },
2159
+ totalTasks: tasks.length,
2160
+ activeTasks
2161
+ };
2162
+ return jsonResponse(body);
2163
+ }
2164
+ function handleTaskList(req, manager) {
2165
+ const url = new URL(req.url);
2166
+ const status = url.searchParams.get("status");
2167
+ const agent = url.searchParams.get("agent");
2168
+ const search = url.searchParams.get("search");
2169
+ const sortParam = url.searchParams.get("sort") ?? "startedAt:desc";
2170
+ let limit = Number.parseInt(url.searchParams.get("limit") ?? String(DEFAULT_TASK_LIMIT), 10);
2171
+ let offset = Number.parseInt(url.searchParams.get("offset") ?? "0", 10);
2172
+ if (Number.isNaN(limit) || limit < 1)
2173
+ limit = DEFAULT_TASK_LIMIT;
2174
+ if (limit > MAX_TASK_LIMIT)
2175
+ limit = MAX_TASK_LIMIT;
2176
+ if (Number.isNaN(offset) || offset < 0)
2177
+ offset = 0;
2178
+ let tasks = manager.getAllTasks();
2179
+ if (status) {
2180
+ tasks = tasks.filter((t) => t.status === status);
2181
+ }
2182
+ if (agent) {
2183
+ tasks = tasks.filter((t) => t.agent === agent);
2184
+ }
2185
+ if (search) {
2186
+ const searchLower = search.toLowerCase();
2187
+ tasks = tasks.filter((t) => t.description.toLowerCase().includes(searchLower) || t.prompt?.toLowerCase().includes(searchLower));
2188
+ }
2189
+ const [sortField, sortDir] = sortParam.split(":");
2190
+ const direction = sortDir === "asc" ? 1 : -1;
2191
+ const safeSortField = sortField ?? "startedAt";
2192
+ tasks.sort((a, b) => {
2193
+ const aVal = a[safeSortField] ?? "";
2194
+ const bVal = b[safeSortField] ?? "";
2195
+ if (aVal < bVal)
2196
+ return -1 * direction;
2197
+ if (aVal > bVal)
2198
+ return 1 * direction;
2199
+ return 0;
2200
+ });
2201
+ const total = tasks.length;
2202
+ tasks = tasks.slice(offset, offset + limit);
2203
+ const body = { tasks, total, limit, offset };
2204
+ return jsonResponse(body);
2205
+ }
2206
+ function handleTaskDetail(req, manager, id) {
2207
+ const task = manager.getTask(id);
2208
+ if (!task) {
2209
+ return errorResponse("Task not found", 404);
2210
+ }
2211
+ return jsonResponse(task);
2212
+ }
2213
+ async function handleTaskLogs(req, manager, id) {
2214
+ const task = manager.getTask(id);
2215
+ if (!task) {
2216
+ return errorResponse("Task not found", 404);
2217
+ }
2218
+ const messages = await manager.getTaskMessages(id);
2219
+ const body = {
2220
+ messages,
2221
+ taskId: id,
2222
+ retrievedAt: new Date().toISOString()
2223
+ };
2224
+ return jsonResponse(body);
2225
+ }
2226
+ function handleTaskGroup(req, manager, groupId) {
2227
+ const allTasks = manager.getAllTasks();
2228
+ const groupTasks = allTasks.filter((t) => t.batchId === groupId);
2229
+ if (groupTasks.length === 0) {
2230
+ return errorResponse("Task group not found", 404);
2231
+ }
2232
+ let completed = 0;
2233
+ let running = 0;
2234
+ let error = 0;
2235
+ let cancelled = 0;
2236
+ let totalToolCalls = 0;
2237
+ const toolCallsByName = {};
2238
+ let minStart = Number.POSITIVE_INFINITY;
2239
+ let maxEnd = 0;
2240
+ for (const t of groupTasks) {
2241
+ if (t.status === "completed")
2242
+ completed++;
2243
+ else if (t.status === "running" || t.status === "resumed")
2244
+ running++;
2245
+ else if (t.status === "error")
2246
+ error++;
2247
+ else if (t.status === "cancelled")
2248
+ cancelled++;
2249
+ totalToolCalls += t.progress?.toolCalls || 0;
2250
+ if (t.progress?.toolCallsByName) {
2251
+ for (const [toolName, count] of Object.entries(t.progress.toolCallsByName)) {
2252
+ toolCallsByName[toolName] = (toolCallsByName[toolName] ?? 0) + count;
2253
+ }
2254
+ }
2255
+ if (t.startedAt) {
2256
+ const s = new Date(t.startedAt).getTime();
2257
+ if (s < minStart)
2258
+ minStart = s;
2259
+ }
2260
+ if (t.completedAt) {
2261
+ const e = new Date(t.completedAt).getTime();
2262
+ if (e > maxEnd)
2263
+ maxEnd = e;
2264
+ }
2265
+ }
2266
+ const total = groupTasks.length;
2267
+ const duration = minStart < Number.POSITIVE_INFINITY && maxEnd > 0 ? maxEnd - minStart : 0;
2268
+ const body = {
2269
+ groupId,
2270
+ tasks: groupTasks,
2271
+ stats: {
2272
+ completed,
2273
+ running,
2274
+ error,
2275
+ cancelled,
2276
+ total,
2277
+ completionRate: total > 0 ? completed / total : 0,
2278
+ totalToolCalls,
2279
+ toolCallsByName,
2280
+ duration
2281
+ }
2282
+ };
2283
+ return jsonResponse(body);
2284
+ }
2285
+
2286
+ // src/server/sse.ts
2287
+ init_constants();
2288
+ class SSEBroadcaster {
2289
+ subscribers = new Set;
2290
+ encoder = new TextEncoder;
2291
+ addSubscriber(controller) {
2292
+ if (this.subscribers.size >= MAX_SSE_SUBSCRIBERS) {
2293
+ return false;
2294
+ }
2295
+ this.subscribers.add(controller);
2296
+ return true;
2297
+ }
2298
+ removeSubscriber(controller) {
2299
+ this.subscribers.delete(controller);
2300
+ }
2301
+ broadcast(eventType, data) {
2302
+ const message = this.formatSSE(eventType, data);
2303
+ const encoded = this.encoder.encode(message);
2304
+ for (const controller of this.subscribers) {
2305
+ try {
2306
+ controller.enqueue(encoded);
2307
+ } catch {
2308
+ this.subscribers.delete(controller);
2309
+ }
2310
+ }
2311
+ }
2312
+ sendTo(controller, eventType, data) {
2313
+ const message = this.formatSSE(eventType, data);
2314
+ try {
2315
+ controller.enqueue(this.encoder.encode(message));
2316
+ } catch {
2317
+ this.subscribers.delete(controller);
2318
+ }
2319
+ }
2320
+ getSubscriberCount() {
2321
+ return this.subscribers.size;
2322
+ }
2323
+ formatSSE(eventType, data) {
2324
+ return `event: ${eventType}
2325
+ data: ${JSON.stringify(data)}
2326
+
2327
+ `;
2328
+ }
2329
+ }
2330
+ function handleSSERequest(req, broadcaster, dataProvider) {
2331
+ let controller;
2332
+ let heartbeatTimer;
2333
+ const stream = new ReadableStream({
2334
+ start(ctrl) {
2335
+ controller = ctrl;
2336
+ if (!broadcaster.addSubscriber(controller)) {
2337
+ controller.enqueue(new TextEncoder().encode(`event: error
2338
+ data: ${JSON.stringify({ error: "Too many connections" })}
2339
+
2340
+ `));
2341
+ controller.close();
2342
+ return;
2343
+ }
2344
+ const snapshot = {
2345
+ tasks: dataProvider.getAllTasks(),
2346
+ stats: dataProvider.buildStats()
2347
+ };
2348
+ broadcaster.sendTo(controller, "snapshot", snapshot);
2349
+ heartbeatTimer = setInterval(() => {
2350
+ try {
2351
+ broadcaster.sendTo(controller, "heartbeat", { ts: new Date().toISOString() });
2352
+ } catch {
2353
+ clearInterval(heartbeatTimer);
2354
+ broadcaster.removeSubscriber(controller);
2355
+ }
2356
+ }, HEARTBEAT_INTERVAL_MS);
2357
+ },
2358
+ cancel() {
2359
+ clearInterval(heartbeatTimer);
2360
+ broadcaster.removeSubscriber(controller);
2361
+ }
2362
+ });
2363
+ return new Response(stream, {
2364
+ status: 200,
2365
+ headers: {
2366
+ "Content-Type": "text/event-stream",
2367
+ "Cache-Control": "no-cache",
2368
+ Connection: "keep-alive",
2369
+ ...CORS_HEADERS
2370
+ }
2371
+ });
2372
+ }
2373
+
2374
+ // src/server/index.ts
2375
+ class StatusApiServer {
2376
+ manager;
2377
+ server = null;
2378
+ broadcaster;
2379
+ unsubscribe = null;
2380
+ startedAt;
2381
+ constructor(manager) {
2382
+ this.manager = manager;
2383
+ this.broadcaster = new SSEBroadcaster;
2384
+ this.startedAt = new Date().toISOString();
2385
+ }
2386
+ static async start(manager) {
2387
+ if (process.env.ASYNCAGENTS_API_ENABLED === "false") {
2388
+ return null;
2389
+ }
2390
+ const instance = new StatusApiServer(manager);
2391
+ await instance.bind();
2392
+ return instance;
2393
+ }
2394
+ async bind() {
2395
+ const desiredPort = Number.parseInt(process.env.ASYNCAGENTS_API_PORT ?? String(DEFAULT_API_PORT), 10);
2396
+ const host = process.env.ASYNCAGENTS_API_HOST ?? DEFAULT_API_HOST;
2397
+ const dataProvider = {
2398
+ getAllTasks: () => this.manager.getAllTasks(),
2399
+ buildStats: () => this.buildStatsData()
2400
+ };
2401
+ const broadcaster = this.broadcaster;
2402
+ const manager = this.manager;
2403
+ let lastError = null;
2404
+ for (let attempt = 0;attempt <= MAX_PORT_RETRY; attempt++) {
2405
+ const port2 = attempt < MAX_PORT_RETRY ? desiredPort + attempt : 0;
2406
+ try {
2407
+ this.server = Bun.serve({
2408
+ port: port2,
2409
+ hostname: host,
2410
+ fetch(req) {
2411
+ if (req.method === "OPTIONS") {
2412
+ return preflightResponse();
2413
+ }
2414
+ const url2 = new URL(req.url);
2415
+ const path = url2.pathname;
2416
+ if (path === "/v1/health")
2417
+ return handleHealth(req, manager);
2418
+ if (path === "/v1/stats")
2419
+ return handleStats(req, manager);
2420
+ if (path === "/v1/tasks")
2421
+ return handleTaskList(req, manager);
2422
+ if (path === "/v1/events")
2423
+ return handleSSERequest(req, broadcaster, dataProvider);
2424
+ const logsMatch = path.match(/^\/v1\/tasks\/([^/]+)\/logs$/);
2425
+ const logsId = logsMatch?.[1];
2426
+ if (logsId) {
2427
+ return handleTaskLogs(req, manager, logsId);
2428
+ }
2429
+ const taskMatch = path.match(/^\/v1\/tasks\/([^/]+)$/);
2430
+ const taskId = taskMatch?.[1];
2431
+ if (taskId) {
2432
+ return handleTaskDetail(req, manager, taskId);
2433
+ }
2434
+ const groupMatch = path.match(/^\/v1\/task-groups\/([^/]+)$/);
2435
+ const groupId = groupMatch?.[1];
2436
+ if (groupId) {
2437
+ return handleTaskGroup(req, manager, groupId);
2438
+ }
2439
+ return errorResponse("Not Found", 404);
2440
+ }
2441
+ });
2442
+ lastError = null;
2443
+ break;
2444
+ } catch (err) {
2445
+ lastError = err;
2446
+ }
2447
+ }
2448
+ if (lastError || !this.server) {
2449
+ throw new Error(`Failed to bind API server after ${MAX_PORT_RETRY + 1} attempts: ${lastError?.message}`);
2450
+ }
2451
+ if (this.manager.onTaskEvent) {
2452
+ this.unsubscribe = this.manager.onTaskEvent((eventType, task) => {
2453
+ this.broadcaster.broadcast(eventType, { task });
2454
+ });
2455
+ }
2456
+ const port = this.server.port;
2457
+ const url = `http://${host}:${port}`;
2458
+ try {
2459
+ await writeServerInfo({
2460
+ port,
2461
+ pid: process.pid,
2462
+ startedAt: this.startedAt,
2463
+ url,
2464
+ version: "1.0.0"
2465
+ });
2466
+ } catch {}
2467
+ }
2468
+ buildStatsData() {
2469
+ const tasks = this.manager.getAllTasks();
2470
+ const byStatus = {};
2471
+ const byAgent = {};
2472
+ const toolCallsByName = {};
2473
+ const durations = [];
2474
+ let activeTasks = 0;
2475
+ for (const task of tasks) {
2476
+ byStatus[task.status] = (byStatus[task.status] ?? 0) + 1;
2477
+ byAgent[task.agent] = (byAgent[task.agent] ?? 0) + 1;
2478
+ if (task.progress?.toolCallsByName) {
2479
+ for (const [toolName, count] of Object.entries(task.progress.toolCallsByName)) {
2480
+ toolCallsByName[toolName] = (toolCallsByName[toolName] ?? 0) + count;
2481
+ }
2482
+ }
2483
+ if (task.status === "running" || task.status === "resumed")
2484
+ activeTasks++;
2485
+ if (task.startedAt && task.completedAt) {
2486
+ const d = new Date(task.completedAt).getTime() - new Date(task.startedAt).getTime();
2487
+ if (d > 0)
2488
+ durations.push(d);
2489
+ }
2490
+ }
2491
+ return {
2492
+ byStatus,
2493
+ byAgent,
2494
+ toolCallsByName,
2495
+ duration: {
2496
+ avg: durations.length ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
2497
+ max: durations.length ? Math.max(...durations) : 0,
2498
+ min: durations.length ? Math.min(...durations) : 0
2499
+ },
2500
+ totalTasks: tasks.length,
2501
+ activeTasks
2502
+ };
2503
+ }
2504
+ getPort() {
2505
+ return this.server?.port ?? 0;
2506
+ }
2507
+ getUrl() {
2508
+ const host = process.env.ASYNCAGENTS_API_HOST ?? DEFAULT_API_HOST;
2509
+ return `http://${host}:${this.getPort()}`;
2510
+ }
2511
+ getBroadcaster() {
2512
+ return this.broadcaster;
2513
+ }
2514
+ async stop() {
2515
+ if (this.unsubscribe) {
2516
+ this.unsubscribe();
2517
+ this.unsubscribe = null;
2518
+ }
2519
+ if (this.server) {
2520
+ this.server.stop(true);
2521
+ this.server = null;
2522
+ }
2523
+ try {
2524
+ await deleteServerInfo();
2525
+ } catch {}
2526
+ }
2527
+ }
2528
+
1925
2529
  // node_modules/.pnpm/@opencode-ai+plugin@1.1.25/node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
1926
2530
  var exports_external = {};
1927
2531
  __export(exports_external, {
@@ -14364,7 +14968,9 @@ async function handleLaunchMode(manager, args, toolContext) {
14364
14968
  parentMessageID: toolContext.messageID,
14365
14969
  parentAgent: toolContext.agent
14366
14970
  });
14367
- return SUCCESS_MESSAGES.taskLaunched(shortId(task.sessionID));
14971
+ const siblingIds = manager.getTaskSessionIds?.() ?? [];
14972
+ const displayId = uniqueShortId(task.sessionID, siblingIds);
14973
+ return SUCCESS_MESSAGES.taskLaunched(displayId);
14368
14974
  } catch (error45) {
14369
14975
  const message = error45 instanceof Error ? error45.message : String(error45);
14370
14976
  return ERROR_MESSAGES.launchFailed(message);
@@ -14383,11 +14989,11 @@ function createBackgroundOutput(manager) {
14383
14989
  },
14384
14990
  async execute(args) {
14385
14991
  try {
14386
- const resolvedId = manager.resolveTaskId(args.task_id);
14992
+ const resolvedId = await manager.resolveTaskIdWithFallback(args.task_id);
14387
14993
  if (!resolvedId) {
14388
14994
  return ERROR_MESSAGES.taskNotFound(args.task_id);
14389
14995
  }
14390
- let task = manager.getTask(resolvedId);
14996
+ let task = manager.getTask(resolvedId) ?? await manager.getTaskWithFallback(resolvedId);
14391
14997
  if (!task) {
14392
14998
  return ERROR_MESSAGES.taskNotFound(args.task_id);
14393
14999
  }
@@ -14431,11 +15037,11 @@ function createBackgroundCancel(manager) {
14431
15037
  },
14432
15038
  async execute(args) {
14433
15039
  try {
14434
- const resolvedId = manager.resolveTaskId(args.task_id);
15040
+ const resolvedId = await manager.resolveTaskIdWithFallback(args.task_id);
14435
15041
  if (!resolvedId) {
14436
15042
  return ERROR_MESSAGES.taskNotFound(args.task_id);
14437
15043
  }
14438
- const task = manager.getTask(resolvedId);
15044
+ const task = await manager.getTaskWithFallback(resolvedId);
14439
15045
  if (!task) {
14440
15046
  return ERROR_MESSAGES.taskNotFound(args.task_id);
14441
15047
  }
@@ -14469,6 +15075,7 @@ function createBackgroundList(manager) {
14469
15075
  }
14470
15076
  tasks.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
14471
15077
  const header = FORMAT_TEMPLATES.listHeader;
15078
+ const allSessionIds = tasks.map((t) => t.sessionID);
14472
15079
  const rows = tasks.map((task) => {
14473
15080
  const duration3 = formatDuration(task.startedAt, task.completedAt);
14474
15081
  const desc = task.description.length > 30 ? `${task.description.slice(0, 27)}...` : task.description;
@@ -14477,19 +15084,22 @@ function createBackgroundList(manager) {
14477
15084
  task.isForked ? "(forked)" : "",
14478
15085
  task.resumeCount > 0 ? "(resumed)" : ""
14479
15086
  ].filter(Boolean).join(" ");
14480
- const idWithIndicators = indicators ? `${shortId(task.sessionID)} ${indicators}` : shortId(task.sessionID);
14481
- return `| \`${idWithIndicators}\` | ${desc} | ${task.agent} | ${icon} ${task.status} | ${duration3} |`;
15087
+ const shortId2 = uniqueShortId(task.sessionID, allSessionIds);
15088
+ const idWithIndicators = indicators ? `${shortId2} ${indicators}` : shortId2;
15089
+ const toolsInfo = task.progress?.toolCallsByName && Object.keys(task.progress.toolCallsByName).length > 0 ? Object.entries(task.progress.toolCallsByName).sort(([, a], [, b]) => b - a).map(([name, count]) => `${name}:${count}`).join(" ") : task.progress?.toolCalls ? `${task.progress.toolCalls} calls` : "-";
15090
+ return `| \`${idWithIndicators}\` | ${desc} | ${task.agent} | ${icon} ${task.status} | ${duration3} | ${toolsInfo} |`;
14482
15091
  }).join(`
14483
15092
  `);
14484
15093
  const running = tasks.filter((t) => t.status === "running").length;
14485
15094
  const completed = tasks.filter((t) => t.status === "completed").length;
14486
15095
  const errored = tasks.filter((t) => t.status === "error").length;
14487
15096
  const cancelled = tasks.filter((t) => t.status === "cancelled").length;
15097
+ const totalToolCalls = tasks.reduce((sum, t) => sum + (t.progress?.toolCalls ?? 0), 0);
14488
15098
  return `${header}
14489
15099
  ${rows}
14490
15100
 
14491
15101
  ---
14492
- ${FORMAT_TEMPLATES.listSummary(tasks.length, running, completed, errored, cancelled)}`;
15102
+ ${FORMAT_TEMPLATES.listSummary(tasks.length, running, completed, errored, cancelled, totalToolCalls)}`;
14493
15103
  } catch (error45) {
14494
15104
  return ERROR_MESSAGES.listFailed(error45 instanceof Error ? error45.message : String(error45));
14495
15105
  }
@@ -14520,6 +15130,15 @@ function createBackgroundClear(manager) {
14520
15130
  // src/index.ts
14521
15131
  async function plugin(ctx) {
14522
15132
  const manager = new BackgroundManager(ctx);
15133
+ const server = await StatusApiServer.start(manager);
15134
+ if (server) {
15135
+ const cleanup = () => {
15136
+ server.stop();
15137
+ };
15138
+ process.on("SIGINT", cleanup);
15139
+ process.on("SIGTERM", cleanup);
15140
+ process.on("exit", cleanup);
15141
+ }
14523
15142
  return {
14524
15143
  tool: {
14525
15144
  asyncagents_task: createBackgroundTask(manager),
@@ -14535,5 +15154,5 @@ export {
14535
15154
  plugin as default
14536
15155
  };
14537
15156
 
14538
- //# debugId=E81274522F3E3FAA64756E2164756E21
15157
+ //# debugId=C3296E4B18C7938B64756E2164756E21
14539
15158
  //# sourceMappingURL=index.js.map