better-opencode-async-agents 0.2.1 → 0.4.1

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 +676 -35
  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 +71 -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) {
@@ -1014,7 +1116,9 @@ function showProgressToast(allTasks, animationFrame, client, getTasksArray) {
1014
1116
  const prevTools = tools.slice(0, -1);
1015
1117
  toolsStr = prevTools.length > 0 ? ` - ${prevTools.join(" > ")} > 「${lastTool}」` : ` - 「${lastTool}」`;
1016
1118
  }
1017
- taskLines.push(`${spinner} [${shortId(task.sessionID)}] ${task.agent}: ${task.description} (${duration})${toolsStr}`);
1119
+ const callCount = task.progress?.toolCalls ?? 0;
1120
+ const callsStr = callCount > 0 ? ` [${callCount} calls]` : "";
1121
+ taskLines.push(`${spinner} [${shortId(task.sessionID)}] ${task.agent}: ${task.description} (${duration})${toolsStr}${callsStr}`);
1018
1122
  }
1019
1123
  const batchCompleted = batchTasks.filter((t) => t.status === "completed" || t.status === "error" || t.status === "cancelled").sort((a, b) => {
1020
1124
  const aTime = a.completedAt ? new Date(a.completedAt).getTime() : 0;
@@ -1026,7 +1130,9 @@ function showProgressToast(allTasks, animationFrame, client, getTasksArray) {
1026
1130
  for (const task of visibleCompleted) {
1027
1131
  const duration = formatDuration2(new Date(task.startedAt), task.completedAt ? new Date(task.completedAt) : undefined);
1028
1132
  const icon = task.status === "completed" ? "✓" : task.status === "error" ? "✗" : "⊘";
1029
- taskLines.push(`${icon} [${shortId(task.sessionID)}] ${task.agent}: ${task.description} (${duration})`);
1133
+ const callCount = task.progress?.toolCalls ?? 0;
1134
+ const callsStr = callCount > 0 ? ` [${callCount} calls]` : "";
1135
+ taskLines.push(`${icon} [${shortId(task.sessionID)}] ${task.agent}: ${task.description} (${duration})${callsStr}`);
1030
1136
  }
1031
1137
  const hiddenCount = batchCompleted.length - visibleCompleted.length;
1032
1138
  if (hiddenCount > 0) {
@@ -1036,7 +1142,14 @@ function showProgressToast(allTasks, animationFrame, client, getTasksArray) {
1036
1142
  const barLength = 10;
1037
1143
  const filledLength = Math.round(finishedCount / Math.max(totalTasks, 1) * barLength);
1038
1144
  const progressBar = "█".repeat(filledLength) + "░".repeat(barLength - filledLength);
1039
- const summary = `[${progressBar}] ${finishedCount}/${totalTasks} agents (${progressPercent}%) | ${totalToolCalls} tool calls`;
1145
+ const sortedTools = Object.entries(aggregatedToolCalls).sort(([, a], [, b]) => b - a);
1146
+ let toolBreakdown = "";
1147
+ if (sortedTools.length > 0) {
1148
+ const top3 = sortedTools.slice(0, 3).map(([name, count]) => `${name}:${count}`).join(" ");
1149
+ const remaining = sortedTools.length - 3;
1150
+ toolBreakdown = remaining > 0 ? ` (${top3} +${remaining} more)` : ` (${top3})`;
1151
+ }
1152
+ const summary = `[${progressBar}] ${finishedCount}/${totalTasks} agents (${progressPercent}%) | ${totalToolCalls} calls${toolBreakdown}`;
1040
1153
  const tuiClient = client;
1041
1154
  if (!tuiClient.tui?.showToast)
1042
1155
  return;
@@ -1201,7 +1314,7 @@ function stopPolling(pollingInterval) {
1201
1314
  clearInterval(pollingInterval);
1202
1315
  }
1203
1316
  }
1204
- async function pollRunningTasks(client, tasks, originalParentSessionID, updateTaskProgress, notifyParentSession2, clearAllTasks, stopPolling2, showProgressToast2, getTasksArray, getTaskMessages, persistTask) {
1317
+ async function pollRunningTasks(client, tasks, originalParentSessionID, updateTaskProgress, notifyParentSession2, clearAllTasks, stopPolling2, showProgressToast2, getTasksArray, getTaskMessages, persistTask, emitTaskEvent) {
1205
1318
  try {
1206
1319
  const statusResult = await client.session.status();
1207
1320
  const allStatuses = statusResult.data ?? {};
@@ -1240,7 +1353,7 @@ async function pollRunningTasks(client, tasks, originalParentSessionID, updateTa
1240
1353
  const messages = await getTaskMessages(task.sessionID);
1241
1354
  const hasAssistantResponse = messages.some((m) => m.info?.role === "assistant" && m.parts?.some((p) => p.type === "text" && p.text && p.text.length > 0));
1242
1355
  if (hasAssistantResponse) {
1243
- setTaskStatus(task, "completed", { persistFn: persistTask });
1356
+ setTaskStatus(task, "completed", { persistFn: persistTask, emitFn: emitTaskEvent });
1244
1357
  notifyParentSession2(task);
1245
1358
  continue;
1246
1359
  }
@@ -1250,7 +1363,7 @@ async function pollRunningTasks(client, tasks, originalParentSessionID, updateTa
1250
1363
  continue;
1251
1364
  }
1252
1365
  if (sessionStatus.type === "idle") {
1253
- setTaskStatus(task, "completed", { persistFn: persistTask });
1366
+ setTaskStatus(task, "completed", { persistFn: persistTask, emitFn: emitTaskEvent });
1254
1367
  notifyParentSession2(task);
1255
1368
  continue;
1256
1369
  }
@@ -1291,6 +1404,7 @@ async function updateTaskProgress(task, client) {
1291
1404
  return;
1292
1405
  const messages = messagesResult.data;
1293
1406
  let toolCalls = 0;
1407
+ const toolCallsByName = {};
1294
1408
  const allTools = [];
1295
1409
  for (const msg of messages) {
1296
1410
  const parts = msg.parts ?? msg.content ?? [];
@@ -1300,6 +1414,7 @@ async function updateTaskProgress(task, client) {
1300
1414
  for (const part of parts) {
1301
1415
  if (part.type === "tool" && part.tool) {
1302
1416
  toolCalls++;
1417
+ toolCallsByName[part.tool] = (toolCallsByName[part.tool] ?? 0) + 1;
1303
1418
  allTools.push(part.tool);
1304
1419
  }
1305
1420
  }
@@ -1307,11 +1422,13 @@ async function updateTaskProgress(task, client) {
1307
1422
  if (!task.progress) {
1308
1423
  task.progress = {
1309
1424
  toolCalls: 0,
1425
+ toolCallsByName: {},
1310
1426
  lastTools: [],
1311
1427
  lastUpdate: new Date().toISOString()
1312
1428
  };
1313
1429
  }
1314
1430
  task.progress.toolCalls = toolCalls;
1431
+ task.progress.toolCallsByName = toolCallsByName;
1315
1432
  task.progress.lastTools = allTools.slice(-3);
1316
1433
  task.progress.lastUpdate = new Date().toISOString();
1317
1434
  } catch {}
@@ -1436,7 +1553,7 @@ ${part.text}`);
1436
1553
 
1437
1554
  // src/manager/task-lifecycle.ts
1438
1555
  var FORK_METHOD = "inject";
1439
- async function launchTask(input, tasks, client, getOrCreateBatchId, setOriginalParentSessionID, startPolling2, notifyParentSession2) {
1556
+ async function launchTask(input, tasks, client, getOrCreateBatchId, setOriginalParentSessionID, startPolling2, notifyParentSession2, emitTaskEvent, persistTask) {
1440
1557
  if (!input.agent || input.agent.trim() === "") {
1441
1558
  throw new Error("Agent parameter is required");
1442
1559
  }
@@ -1511,12 +1628,16 @@ ${contextText}`;
1511
1628
  isForked: input.fork ?? false,
1512
1629
  progress: {
1513
1630
  toolCalls: 0,
1631
+ toolCallsByName: {},
1514
1632
  lastTools: [],
1515
1633
  lastUpdate: new Date().toISOString()
1516
1634
  }
1517
1635
  };
1518
1636
  setOriginalParentSessionID(input.parentSessionID);
1519
1637
  tasks.set(task.sessionID, task);
1638
+ if (persistTask) {
1639
+ await persistTask(task);
1640
+ }
1520
1641
  startPolling2();
1521
1642
  client.session.promptAsync({
1522
1643
  path: { id: sessionID },
@@ -1542,6 +1663,7 @@ ${contextText}`;
1542
1663
  existingTask.error = errorMessage;
1543
1664
  }
1544
1665
  existingTask.completedAt = new Date().toISOString();
1666
+ emitTaskEvent?.("task.error", existingTask);
1545
1667
  notifyParentSession2(existingTask);
1546
1668
  }
1547
1669
  });
@@ -1568,7 +1690,7 @@ async function getTaskMessages(sessionID, client) {
1568
1690
  }
1569
1691
  return messagesResult.data ?? messagesResult;
1570
1692
  }
1571
- async function checkAndUpdateTaskStatus(task, client, notifyParentSession2, skipNotification = false, getTaskMessages2) {
1693
+ async function checkAndUpdateTaskStatus(task, client, notifyParentSession2, skipNotification = false, getTaskMessages2, emitTaskEvent) {
1572
1694
  if (task.status !== "running") {
1573
1695
  return task;
1574
1696
  }
@@ -1579,6 +1701,7 @@ async function checkAndUpdateTaskStatus(task, client, notifyParentSession2, skip
1579
1701
  if (sessionStatus?.type === "idle") {
1580
1702
  task.status = "completed";
1581
1703
  task.completedAt = new Date().toISOString();
1704
+ emitTaskEvent?.("task.completed", task);
1582
1705
  if (!skipNotification) {
1583
1706
  notifyParentSession2(task);
1584
1707
  }
@@ -1591,6 +1714,7 @@ async function checkAndUpdateTaskStatus(task, client, notifyParentSession2, skip
1591
1714
  if (hasAssistantResponse) {
1592
1715
  task.status = "completed";
1593
1716
  task.completedAt = new Date().toISOString();
1717
+ emitTaskEvent?.("task.completed", task);
1594
1718
  if (!skipNotification) {
1595
1719
  notifyParentSession2(task);
1596
1720
  }
@@ -1627,12 +1751,29 @@ class BackgroundManager {
1627
1751
  currentBatchId = null;
1628
1752
  tasks = new Map;
1629
1753
  originalParentSessionID = null;
1754
+ taskEventListeners = [];
1630
1755
  constructor(ctx) {
1631
1756
  this.client = ctx.client;
1632
1757
  this.directory = ctx.directory;
1633
1758
  this.startEventSubscription();
1634
1759
  this.loadPersistedTasks();
1635
1760
  }
1761
+ onTaskEvent(callback) {
1762
+ this.taskEventListeners.push(callback);
1763
+ return () => {
1764
+ const index = this.taskEventListeners.indexOf(callback);
1765
+ if (index > -1) {
1766
+ this.taskEventListeners.splice(index, 1);
1767
+ }
1768
+ };
1769
+ }
1770
+ emitTaskEvent(eventType, task) {
1771
+ for (const listener of this.taskEventListeners) {
1772
+ try {
1773
+ listener(eventType, task);
1774
+ } catch {}
1775
+ }
1776
+ }
1636
1777
  async loadPersistedTasks() {
1637
1778
  try {
1638
1779
  const persisted = await loadTasks();
@@ -1642,8 +1783,8 @@ class BackgroundManager {
1642
1783
  async launch(input) {
1643
1784
  const task = await launchTask(input, this.tasks, this.client, () => this.getOrCreateBatchId(), (sessionID) => {
1644
1785
  this.originalParentSessionID = sessionID;
1645
- }, () => this.startPolling(), (t) => this.notifyParentSession(t));
1646
- await this.persistTask(task);
1786
+ }, () => this.startPolling(), (t) => this.notifyParentSession(t), (eventType, t) => this.emitTaskEvent(eventType, t), (t) => this.persistTask(t));
1787
+ this.emitTaskEvent("task.created", task);
1647
1788
  return task;
1648
1789
  }
1649
1790
  async persistTask(task) {
@@ -1654,7 +1795,13 @@ class BackgroundManager {
1654
1795
  createdAt: task.startedAt,
1655
1796
  status: task.status,
1656
1797
  resumeCount: task.resumeCount,
1657
- isForked: task.isForked
1798
+ isForked: task.isForked,
1799
+ completedAt: task.completedAt,
1800
+ error: task.error,
1801
+ result: task.result,
1802
+ progress: task.progress,
1803
+ startedAt: task.startedAt,
1804
+ batchId: task.batchId
1658
1805
  };
1659
1806
  try {
1660
1807
  await saveTask(task.sessionID, persisted);
@@ -1663,7 +1810,11 @@ class BackgroundManager {
1663
1810
  }
1664
1811
  }
1665
1812
  async cancelTask(taskId) {
1666
- return cancelTask(taskId, this.tasks, this.client);
1813
+ const task = this.tasks.get(taskId);
1814
+ await cancelTask(taskId, this.tasks, this.client);
1815
+ if (task) {
1816
+ this.emitTaskEvent("task.cancelled", task);
1817
+ }
1667
1818
  }
1668
1819
  clearAllTasks() {
1669
1820
  clearAllTasks(this.tasks, this.client, () => this.stopPolling());
@@ -1751,12 +1902,15 @@ class BackgroundManager {
1751
1902
  getAllTasks() {
1752
1903
  return Array.from(this.tasks.values());
1753
1904
  }
1905
+ getTaskSessionIds() {
1906
+ return Array.from(this.tasks.keys());
1907
+ }
1754
1908
  async getTaskMessages(sessionID) {
1755
1909
  return getTaskMessages(sessionID, this.client);
1756
1910
  }
1757
1911
  async checkAndUpdateTaskStatus(task, skipNotification = false) {
1758
1912
  const previousStatus = task.status;
1759
- const updatedTask = await checkAndUpdateTaskStatus(task, this.client, (t) => this.notifyParentSession(t), skipNotification, (sessionID) => this.getTaskMessages(sessionID));
1913
+ const updatedTask = await checkAndUpdateTaskStatus(task, this.client, (t) => this.notifyParentSession(t), skipNotification, (sessionID) => this.getTaskMessages(sessionID), (eventType, t) => this.emitTaskEvent(eventType, t));
1760
1914
  if (updatedTask.status !== previousStatus) {
1761
1915
  await this.persistTask(updatedTask);
1762
1916
  }
@@ -1903,12 +2057,20 @@ class BackgroundManager {
1903
2057
  this.pollingInterval = undefined;
1904
2058
  }
1905
2059
  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));
2060
+ 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
2061
  }
1908
2062
  showProgressToast(tasks) {
1909
2063
  showProgressToast(tasks, this.animationFrame, this.client, () => this.getAllTasks());
1910
2064
  this.animationFrame = (this.animationFrame + 1) % 10;
1911
2065
  }
2066
+ async updateTaskProgressWithEvent(task) {
2067
+ const previousToolCalls = task.progress?.toolCalls ?? 0;
2068
+ await updateTaskProgress(task, this.client);
2069
+ const currentToolCalls = task.progress?.toolCalls ?? 0;
2070
+ if (currentToolCalls > previousToolCalls) {
2071
+ this.emitTaskEvent("task.updated", task);
2072
+ }
2073
+ }
1912
2074
  notifyParentSession(task) {
1913
2075
  notifyParentSession(task, this.client, this.directory, () => this.getAllTasks());
1914
2076
  }
@@ -1917,11 +2079,475 @@ class BackgroundManager {
1917
2079
  clearAllTasks: () => this.clearAllTasks(),
1918
2080
  getTasksArray: () => this.getAllTasks(),
1919
2081
  notifyParentSession: (task) => this.notifyParentSession(task),
1920
- persistTask: (task) => void this.persistTask(task)
2082
+ persistTask: (task) => void this.persistTask(task),
2083
+ emitTaskEvent: (eventType, task) => this.emitTaskEvent(eventType, task)
1921
2084
  }));
1922
2085
  }
1923
2086
  }
1924
2087
 
2088
+ // src/server/index.ts
2089
+ init_constants();
2090
+ init_storage();
2091
+
2092
+ // src/server/cors.ts
2093
+ var CORS_HEADERS = {
2094
+ "Access-Control-Allow-Origin": "*",
2095
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
2096
+ "Access-Control-Allow-Headers": "Content-Type"
2097
+ };
2098
+ function withCors(response) {
2099
+ for (const [key, value] of Object.entries(CORS_HEADERS)) {
2100
+ response.headers.set(key, value);
2101
+ }
2102
+ return response;
2103
+ }
2104
+ function jsonResponse(data, status = 200) {
2105
+ return withCors(new Response(JSON.stringify(data), {
2106
+ status,
2107
+ headers: { "Content-Type": "application/json" }
2108
+ }));
2109
+ }
2110
+ function errorResponse(error, status) {
2111
+ return jsonResponse({ error, status }, status);
2112
+ }
2113
+ function preflightResponse() {
2114
+ return withCors(new Response(null, { status: 204 }));
2115
+ }
2116
+
2117
+ // src/server/routes.ts
2118
+ init_constants();
2119
+ var startTime = Date.now();
2120
+ function handleHealth(_req, manager) {
2121
+ const tasks = manager.getAllTasks();
2122
+ const body = {
2123
+ status: "ok",
2124
+ uptime: Math.floor((Date.now() - startTime) / 1000),
2125
+ version: "1.0.0",
2126
+ taskCount: tasks.length
2127
+ };
2128
+ return jsonResponse(body);
2129
+ }
2130
+ function handleStats(_req, manager) {
2131
+ const tasks = manager.getAllTasks();
2132
+ const byStatus = {};
2133
+ const byAgent = {};
2134
+ const toolCallsByName = {};
2135
+ const toolCallsByAgent = {};
2136
+ const durations = [];
2137
+ let activeTasks = 0;
2138
+ for (const task of tasks) {
2139
+ byStatus[task.status] = (byStatus[task.status] ?? 0) + 1;
2140
+ byAgent[task.agent] = (byAgent[task.agent] ?? 0) + 1;
2141
+ if (task.progress?.toolCallsByName) {
2142
+ for (const [toolName, count] of Object.entries(task.progress.toolCallsByName)) {
2143
+ toolCallsByName[toolName] = (toolCallsByName[toolName] ?? 0) + count;
2144
+ }
2145
+ }
2146
+ const agentToolCalls = task.progress?.toolCalls ?? 0;
2147
+ if (agentToolCalls > 0) {
2148
+ toolCallsByAgent[task.agent] = (toolCallsByAgent[task.agent] ?? 0) + agentToolCalls;
2149
+ }
2150
+ if (task.status === "running" || task.status === "resumed") {
2151
+ activeTasks++;
2152
+ }
2153
+ if (task.startedAt && task.completedAt) {
2154
+ const duration = new Date(task.completedAt).getTime() - new Date(task.startedAt).getTime();
2155
+ if (duration > 0)
2156
+ durations.push(duration);
2157
+ }
2158
+ }
2159
+ const body = {
2160
+ byStatus,
2161
+ byAgent,
2162
+ toolCallsByName,
2163
+ toolCallsByAgent,
2164
+ duration: {
2165
+ avg: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
2166
+ max: durations.length > 0 ? Math.max(...durations) : 0,
2167
+ min: durations.length > 0 ? Math.min(...durations) : 0
2168
+ },
2169
+ totalTasks: tasks.length,
2170
+ activeTasks
2171
+ };
2172
+ return jsonResponse(body);
2173
+ }
2174
+ function handleTaskList(req, manager) {
2175
+ const url = new URL(req.url);
2176
+ const status = url.searchParams.get("status");
2177
+ const agent = url.searchParams.get("agent");
2178
+ const search = url.searchParams.get("search");
2179
+ const sortParam = url.searchParams.get("sort") ?? "startedAt:desc";
2180
+ let limit = Number.parseInt(url.searchParams.get("limit") ?? String(DEFAULT_TASK_LIMIT), 10);
2181
+ let offset = Number.parseInt(url.searchParams.get("offset") ?? "0", 10);
2182
+ if (Number.isNaN(limit) || limit < 1)
2183
+ limit = DEFAULT_TASK_LIMIT;
2184
+ if (limit > MAX_TASK_LIMIT)
2185
+ limit = MAX_TASK_LIMIT;
2186
+ if (Number.isNaN(offset) || offset < 0)
2187
+ offset = 0;
2188
+ let tasks = manager.getAllTasks();
2189
+ if (status) {
2190
+ tasks = tasks.filter((t) => t.status === status);
2191
+ }
2192
+ if (agent) {
2193
+ tasks = tasks.filter((t) => t.agent === agent);
2194
+ }
2195
+ if (search) {
2196
+ const searchLower = search.toLowerCase();
2197
+ tasks = tasks.filter((t) => t.description.toLowerCase().includes(searchLower) || t.prompt?.toLowerCase().includes(searchLower));
2198
+ }
2199
+ const [sortField, sortDir] = sortParam.split(":");
2200
+ const direction = sortDir === "asc" ? 1 : -1;
2201
+ const safeSortField = sortField ?? "startedAt";
2202
+ tasks.sort((a, b) => {
2203
+ const aVal = a[safeSortField] ?? "";
2204
+ const bVal = b[safeSortField] ?? "";
2205
+ if (aVal < bVal)
2206
+ return -1 * direction;
2207
+ if (aVal > bVal)
2208
+ return 1 * direction;
2209
+ return 0;
2210
+ });
2211
+ const total = tasks.length;
2212
+ tasks = tasks.slice(offset, offset + limit);
2213
+ const body = { tasks, total, limit, offset };
2214
+ return jsonResponse(body);
2215
+ }
2216
+ function handleTaskDetail(req, manager, id) {
2217
+ const task = manager.getTask(id);
2218
+ if (!task) {
2219
+ return errorResponse("Task not found", 404);
2220
+ }
2221
+ return jsonResponse(task);
2222
+ }
2223
+ async function handleTaskLogs(req, manager, id) {
2224
+ const task = manager.getTask(id);
2225
+ if (!task) {
2226
+ return errorResponse("Task not found", 404);
2227
+ }
2228
+ const messages = await manager.getTaskMessages(id);
2229
+ const body = {
2230
+ messages,
2231
+ taskId: id,
2232
+ retrievedAt: new Date().toISOString()
2233
+ };
2234
+ return jsonResponse(body);
2235
+ }
2236
+ function handleTaskGroup(req, manager, groupId) {
2237
+ const allTasks = manager.getAllTasks();
2238
+ const groupTasks = allTasks.filter((t) => t.batchId === groupId);
2239
+ if (groupTasks.length === 0) {
2240
+ return errorResponse("Task group not found", 404);
2241
+ }
2242
+ let completed = 0;
2243
+ let running = 0;
2244
+ let error = 0;
2245
+ let cancelled = 0;
2246
+ let totalToolCalls = 0;
2247
+ const toolCallsByName = {};
2248
+ const toolCallsByAgent = {};
2249
+ let minStart = Number.POSITIVE_INFINITY;
2250
+ let maxEnd = 0;
2251
+ for (const t of groupTasks) {
2252
+ if (t.status === "completed")
2253
+ completed++;
2254
+ else if (t.status === "running" || t.status === "resumed")
2255
+ running++;
2256
+ else if (t.status === "error")
2257
+ error++;
2258
+ else if (t.status === "cancelled")
2259
+ cancelled++;
2260
+ totalToolCalls += t.progress?.toolCalls || 0;
2261
+ if (t.progress?.toolCallsByName) {
2262
+ for (const [toolName, count] of Object.entries(t.progress.toolCallsByName)) {
2263
+ toolCallsByName[toolName] = (toolCallsByName[toolName] ?? 0) + count;
2264
+ }
2265
+ }
2266
+ const agentToolCalls = t.progress?.toolCalls ?? 0;
2267
+ if (agentToolCalls > 0) {
2268
+ toolCallsByAgent[t.agent] = (toolCallsByAgent[t.agent] ?? 0) + agentToolCalls;
2269
+ }
2270
+ if (t.startedAt) {
2271
+ const s = new Date(t.startedAt).getTime();
2272
+ if (s < minStart)
2273
+ minStart = s;
2274
+ }
2275
+ if (t.completedAt) {
2276
+ const e = new Date(t.completedAt).getTime();
2277
+ if (e > maxEnd)
2278
+ maxEnd = e;
2279
+ }
2280
+ }
2281
+ const total = groupTasks.length;
2282
+ const duration = minStart < Number.POSITIVE_INFINITY && maxEnd > 0 ? maxEnd - minStart : 0;
2283
+ const body = {
2284
+ groupId,
2285
+ tasks: groupTasks,
2286
+ stats: {
2287
+ completed,
2288
+ running,
2289
+ error,
2290
+ cancelled,
2291
+ total,
2292
+ completionRate: total > 0 ? completed / total : 0,
2293
+ totalToolCalls,
2294
+ toolCallsByName,
2295
+ toolCallsByAgent,
2296
+ duration
2297
+ }
2298
+ };
2299
+ return jsonResponse(body);
2300
+ }
2301
+
2302
+ // src/server/sse.ts
2303
+ init_constants();
2304
+ class SSEBroadcaster {
2305
+ subscribers = new Set;
2306
+ encoder = new TextEncoder;
2307
+ addSubscriber(controller) {
2308
+ if (this.subscribers.size >= MAX_SSE_SUBSCRIBERS) {
2309
+ return false;
2310
+ }
2311
+ this.subscribers.add(controller);
2312
+ return true;
2313
+ }
2314
+ removeSubscriber(controller) {
2315
+ this.subscribers.delete(controller);
2316
+ }
2317
+ broadcast(eventType, data) {
2318
+ const message = this.formatSSE(eventType, data);
2319
+ const encoded = this.encoder.encode(message);
2320
+ for (const controller of this.subscribers) {
2321
+ try {
2322
+ controller.enqueue(encoded);
2323
+ } catch {
2324
+ this.subscribers.delete(controller);
2325
+ }
2326
+ }
2327
+ }
2328
+ sendTo(controller, eventType, data) {
2329
+ const message = this.formatSSE(eventType, data);
2330
+ try {
2331
+ controller.enqueue(this.encoder.encode(message));
2332
+ } catch {
2333
+ this.subscribers.delete(controller);
2334
+ }
2335
+ }
2336
+ getSubscriberCount() {
2337
+ return this.subscribers.size;
2338
+ }
2339
+ formatSSE(eventType, data) {
2340
+ return `event: ${eventType}
2341
+ data: ${JSON.stringify(data)}
2342
+
2343
+ `;
2344
+ }
2345
+ }
2346
+ function handleSSERequest(req, broadcaster, dataProvider) {
2347
+ let controller;
2348
+ let heartbeatTimer;
2349
+ const stream = new ReadableStream({
2350
+ start(ctrl) {
2351
+ controller = ctrl;
2352
+ if (!broadcaster.addSubscriber(controller)) {
2353
+ controller.enqueue(new TextEncoder().encode(`event: error
2354
+ data: ${JSON.stringify({ error: "Too many connections" })}
2355
+
2356
+ `));
2357
+ controller.close();
2358
+ return;
2359
+ }
2360
+ const snapshot = {
2361
+ tasks: dataProvider.getAllTasks(),
2362
+ stats: dataProvider.buildStats()
2363
+ };
2364
+ broadcaster.sendTo(controller, "snapshot", snapshot);
2365
+ heartbeatTimer = setInterval(() => {
2366
+ try {
2367
+ broadcaster.sendTo(controller, "heartbeat", { ts: new Date().toISOString() });
2368
+ } catch {
2369
+ clearInterval(heartbeatTimer);
2370
+ broadcaster.removeSubscriber(controller);
2371
+ }
2372
+ }, HEARTBEAT_INTERVAL_MS);
2373
+ },
2374
+ cancel() {
2375
+ clearInterval(heartbeatTimer);
2376
+ broadcaster.removeSubscriber(controller);
2377
+ }
2378
+ });
2379
+ return new Response(stream, {
2380
+ status: 200,
2381
+ headers: {
2382
+ "Content-Type": "text/event-stream",
2383
+ "Cache-Control": "no-cache",
2384
+ Connection: "keep-alive",
2385
+ ...CORS_HEADERS
2386
+ }
2387
+ });
2388
+ }
2389
+
2390
+ // src/server/index.ts
2391
+ class StatusApiServer {
2392
+ manager;
2393
+ server = null;
2394
+ broadcaster;
2395
+ unsubscribe = null;
2396
+ startedAt;
2397
+ constructor(manager) {
2398
+ this.manager = manager;
2399
+ this.broadcaster = new SSEBroadcaster;
2400
+ this.startedAt = new Date().toISOString();
2401
+ }
2402
+ static async start(manager) {
2403
+ if (process.env.ASYNCAGENTS_API_ENABLED === "false") {
2404
+ return null;
2405
+ }
2406
+ const instance = new StatusApiServer(manager);
2407
+ await instance.bind();
2408
+ return instance;
2409
+ }
2410
+ async bind() {
2411
+ const desiredPort = Number.parseInt(process.env.ASYNCAGENTS_API_PORT ?? String(DEFAULT_API_PORT), 10);
2412
+ const host = process.env.ASYNCAGENTS_API_HOST ?? DEFAULT_API_HOST;
2413
+ const dataProvider = {
2414
+ getAllTasks: () => this.manager.getAllTasks(),
2415
+ buildStats: () => this.buildStatsData()
2416
+ };
2417
+ const broadcaster = this.broadcaster;
2418
+ const manager = this.manager;
2419
+ let lastError = null;
2420
+ for (let attempt = 0;attempt <= MAX_PORT_RETRY; attempt++) {
2421
+ const port2 = attempt < MAX_PORT_RETRY ? desiredPort + attempt : 0;
2422
+ try {
2423
+ this.server = Bun.serve({
2424
+ port: port2,
2425
+ hostname: host,
2426
+ fetch(req) {
2427
+ if (req.method === "OPTIONS") {
2428
+ return preflightResponse();
2429
+ }
2430
+ const url2 = new URL(req.url);
2431
+ const path = url2.pathname;
2432
+ if (path === "/v1/health")
2433
+ return handleHealth(req, manager);
2434
+ if (path === "/v1/stats")
2435
+ return handleStats(req, manager);
2436
+ if (path === "/v1/tasks")
2437
+ return handleTaskList(req, manager);
2438
+ if (path === "/v1/events")
2439
+ return handleSSERequest(req, broadcaster, dataProvider);
2440
+ const logsMatch = path.match(/^\/v1\/tasks\/([^/]+)\/logs$/);
2441
+ const logsId = logsMatch?.[1];
2442
+ if (logsId) {
2443
+ return handleTaskLogs(req, manager, logsId);
2444
+ }
2445
+ const taskMatch = path.match(/^\/v1\/tasks\/([^/]+)$/);
2446
+ const taskId = taskMatch?.[1];
2447
+ if (taskId) {
2448
+ return handleTaskDetail(req, manager, taskId);
2449
+ }
2450
+ const groupMatch = path.match(/^\/v1\/task-groups\/([^/]+)$/);
2451
+ const groupId = groupMatch?.[1];
2452
+ if (groupId) {
2453
+ return handleTaskGroup(req, manager, groupId);
2454
+ }
2455
+ return errorResponse("Not Found", 404);
2456
+ }
2457
+ });
2458
+ lastError = null;
2459
+ break;
2460
+ } catch (err) {
2461
+ lastError = err;
2462
+ }
2463
+ }
2464
+ if (lastError || !this.server) {
2465
+ throw new Error(`Failed to bind API server after ${MAX_PORT_RETRY + 1} attempts: ${lastError?.message}`);
2466
+ }
2467
+ if (this.manager.onTaskEvent) {
2468
+ this.unsubscribe = this.manager.onTaskEvent((eventType, task) => {
2469
+ this.broadcaster.broadcast(eventType, { task });
2470
+ });
2471
+ }
2472
+ const port = this.server.port;
2473
+ const url = `http://${host}:${port}`;
2474
+ try {
2475
+ await writeServerInfo({
2476
+ port,
2477
+ pid: process.pid,
2478
+ startedAt: this.startedAt,
2479
+ url,
2480
+ version: "1.0.0"
2481
+ });
2482
+ } catch {}
2483
+ }
2484
+ buildStatsData() {
2485
+ const tasks = this.manager.getAllTasks();
2486
+ const byStatus = {};
2487
+ const byAgent = {};
2488
+ const toolCallsByName = {};
2489
+ const toolCallsByAgent = {};
2490
+ const durations = [];
2491
+ let activeTasks = 0;
2492
+ for (const task of tasks) {
2493
+ byStatus[task.status] = (byStatus[task.status] ?? 0) + 1;
2494
+ byAgent[task.agent] = (byAgent[task.agent] ?? 0) + 1;
2495
+ if (task.progress?.toolCallsByName) {
2496
+ for (const [toolName, count] of Object.entries(task.progress.toolCallsByName)) {
2497
+ toolCallsByName[toolName] = (toolCallsByName[toolName] ?? 0) + count;
2498
+ }
2499
+ }
2500
+ const agentToolCalls = task.progress?.toolCalls ?? 0;
2501
+ if (agentToolCalls > 0) {
2502
+ toolCallsByAgent[task.agent] = (toolCallsByAgent[task.agent] ?? 0) + agentToolCalls;
2503
+ }
2504
+ if (task.status === "running" || task.status === "resumed")
2505
+ activeTasks++;
2506
+ if (task.startedAt && task.completedAt) {
2507
+ const d = new Date(task.completedAt).getTime() - new Date(task.startedAt).getTime();
2508
+ if (d > 0)
2509
+ durations.push(d);
2510
+ }
2511
+ }
2512
+ return {
2513
+ byStatus,
2514
+ byAgent,
2515
+ toolCallsByName,
2516
+ toolCallsByAgent,
2517
+ duration: {
2518
+ avg: durations.length ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
2519
+ max: durations.length ? Math.max(...durations) : 0,
2520
+ min: durations.length ? Math.min(...durations) : 0
2521
+ },
2522
+ totalTasks: tasks.length,
2523
+ activeTasks
2524
+ };
2525
+ }
2526
+ getPort() {
2527
+ return this.server?.port ?? 0;
2528
+ }
2529
+ getUrl() {
2530
+ const host = process.env.ASYNCAGENTS_API_HOST ?? DEFAULT_API_HOST;
2531
+ return `http://${host}:${this.getPort()}`;
2532
+ }
2533
+ getBroadcaster() {
2534
+ return this.broadcaster;
2535
+ }
2536
+ async stop() {
2537
+ if (this.unsubscribe) {
2538
+ this.unsubscribe();
2539
+ this.unsubscribe = null;
2540
+ }
2541
+ if (this.server) {
2542
+ this.server.stop(true);
2543
+ this.server = null;
2544
+ }
2545
+ try {
2546
+ await deleteServerInfo();
2547
+ } catch {}
2548
+ }
2549
+ }
2550
+
1925
2551
  // node_modules/.pnpm/@opencode-ai+plugin@1.1.25/node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
1926
2552
  var exports_external = {};
1927
2553
  __export(exports_external, {
@@ -14364,7 +14990,9 @@ async function handleLaunchMode(manager, args, toolContext) {
14364
14990
  parentMessageID: toolContext.messageID,
14365
14991
  parentAgent: toolContext.agent
14366
14992
  });
14367
- return SUCCESS_MESSAGES.taskLaunched(shortId(task.sessionID));
14993
+ const siblingIds = manager.getTaskSessionIds?.() ?? [];
14994
+ const displayId = uniqueShortId(task.sessionID, siblingIds);
14995
+ return SUCCESS_MESSAGES.taskLaunched(displayId);
14368
14996
  } catch (error45) {
14369
14997
  const message = error45 instanceof Error ? error45.message : String(error45);
14370
14998
  return ERROR_MESSAGES.launchFailed(message);
@@ -14383,11 +15011,11 @@ function createBackgroundOutput(manager) {
14383
15011
  },
14384
15012
  async execute(args) {
14385
15013
  try {
14386
- const resolvedId = manager.resolveTaskId(args.task_id);
15014
+ const resolvedId = await manager.resolveTaskIdWithFallback(args.task_id);
14387
15015
  if (!resolvedId) {
14388
15016
  return ERROR_MESSAGES.taskNotFound(args.task_id);
14389
15017
  }
14390
- let task = manager.getTask(resolvedId);
15018
+ let task = manager.getTask(resolvedId) ?? await manager.getTaskWithFallback(resolvedId);
14391
15019
  if (!task) {
14392
15020
  return ERROR_MESSAGES.taskNotFound(args.task_id);
14393
15021
  }
@@ -14431,11 +15059,11 @@ function createBackgroundCancel(manager) {
14431
15059
  },
14432
15060
  async execute(args) {
14433
15061
  try {
14434
- const resolvedId = manager.resolveTaskId(args.task_id);
15062
+ const resolvedId = await manager.resolveTaskIdWithFallback(args.task_id);
14435
15063
  if (!resolvedId) {
14436
15064
  return ERROR_MESSAGES.taskNotFound(args.task_id);
14437
15065
  }
14438
- const task = manager.getTask(resolvedId);
15066
+ const task = await manager.getTaskWithFallback(resolvedId);
14439
15067
  if (!task) {
14440
15068
  return ERROR_MESSAGES.taskNotFound(args.task_id);
14441
15069
  }
@@ -14469,6 +15097,7 @@ function createBackgroundList(manager) {
14469
15097
  }
14470
15098
  tasks.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
14471
15099
  const header = FORMAT_TEMPLATES.listHeader;
15100
+ const allSessionIds = tasks.map((t) => t.sessionID);
14472
15101
  const rows = tasks.map((task) => {
14473
15102
  const duration3 = formatDuration(task.startedAt, task.completedAt);
14474
15103
  const desc = task.description.length > 30 ? `${task.description.slice(0, 27)}...` : task.description;
@@ -14477,19 +15106,22 @@ function createBackgroundList(manager) {
14477
15106
  task.isForked ? "(forked)" : "",
14478
15107
  task.resumeCount > 0 ? "(resumed)" : ""
14479
15108
  ].filter(Boolean).join(" ");
14480
- const idWithIndicators = indicators ? `${shortId(task.sessionID)} ${indicators}` : shortId(task.sessionID);
14481
- return `| \`${idWithIndicators}\` | ${desc} | ${task.agent} | ${icon} ${task.status} | ${duration3} |`;
15109
+ const shortId2 = uniqueShortId(task.sessionID, allSessionIds);
15110
+ const idWithIndicators = indicators ? `${shortId2} ${indicators}` : shortId2;
15111
+ 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` : "-";
15112
+ return `| \`${idWithIndicators}\` | ${desc} | ${task.agent} | ${icon} ${task.status} | ${duration3} | ${toolsInfo} |`;
14482
15113
  }).join(`
14483
15114
  `);
14484
15115
  const running = tasks.filter((t) => t.status === "running").length;
14485
15116
  const completed = tasks.filter((t) => t.status === "completed").length;
14486
15117
  const errored = tasks.filter((t) => t.status === "error").length;
14487
15118
  const cancelled = tasks.filter((t) => t.status === "cancelled").length;
15119
+ const totalToolCalls = tasks.reduce((sum, t) => sum + (t.progress?.toolCalls ?? 0), 0);
14488
15120
  return `${header}
14489
15121
  ${rows}
14490
15122
 
14491
15123
  ---
14492
- ${FORMAT_TEMPLATES.listSummary(tasks.length, running, completed, errored, cancelled)}`;
15124
+ ${FORMAT_TEMPLATES.listSummary(tasks.length, running, completed, errored, cancelled, totalToolCalls)}`;
14493
15125
  } catch (error45) {
14494
15126
  return ERROR_MESSAGES.listFailed(error45 instanceof Error ? error45.message : String(error45));
14495
15127
  }
@@ -14520,6 +15152,15 @@ function createBackgroundClear(manager) {
14520
15152
  // src/index.ts
14521
15153
  async function plugin(ctx) {
14522
15154
  const manager = new BackgroundManager(ctx);
15155
+ const server = await StatusApiServer.start(manager);
15156
+ if (server) {
15157
+ const cleanup = () => {
15158
+ server.stop();
15159
+ };
15160
+ process.on("SIGINT", cleanup);
15161
+ process.on("SIGTERM", cleanup);
15162
+ process.on("exit", cleanup);
15163
+ }
14523
15164
  return {
14524
15165
  tool: {
14525
15166
  asyncagents_task: createBackgroundTask(manager),
@@ -14535,5 +15176,5 @@ export {
14535
15176
  plugin as default
14536
15177
  };
14537
15178
 
14538
- //# debugId=E81274522F3E3FAA64756E2164756E21
15179
+ //# debugId=A899C06A316CFC2964756E2164756E21
14539
15180
  //# sourceMappingURL=index.js.map