hive-am 1.10.0 → 1.11.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 (107) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/bin/hive-am.mjs +15 -3
  3. package/package.json +1 -1
  4. package/server/dist/db.js +13 -4
  5. package/server/dist/dispatches.js +50 -2
  6. package/server/dist/http/routes/orchestration.js +16 -1
  7. package/server/dist/instructions.js +1 -1
  8. package/server/dist/runtime.js +25 -1
  9. package/server/mcp/dispatch.mjs +42 -2
  10. package/web/.next/BUILD_ID +1 -1
  11. package/web/.next/app-build-manifest.json +58 -58
  12. package/web/.next/app-path-routes-manifest.json +4 -4
  13. package/web/.next/build-manifest.json +2 -2
  14. package/web/.next/prerender-manifest.json +52 -52
  15. package/web/.next/required-server-files.json +1 -1
  16. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/web/.next/server/app/_not-found.html +1 -1
  18. package/web/.next/server/app/_not-found.rsc +3 -3
  19. package/web/.next/server/app/agents/[id]/page.js +2 -2
  20. package/web/.next/server/app/agents/[id]/page_client-reference-manifest.js +1 -1
  21. package/web/.next/server/app/agents/page.js +2 -2
  22. package/web/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  23. package/web/.next/server/app/agents.html +1 -1
  24. package/web/.next/server/app/agents.rsc +5 -5
  25. package/web/.next/server/app/chat/page.js +1 -1
  26. package/web/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  27. package/web/.next/server/app/chat.html +1 -1
  28. package/web/.next/server/app/chat.rsc +4 -4
  29. package/web/.next/server/app/colony/page.js +2 -2
  30. package/web/.next/server/app/colony/page_client-reference-manifest.js +1 -1
  31. package/web/.next/server/app/colony.html +1 -1
  32. package/web/.next/server/app/colony.rsc +4 -4
  33. package/web/.next/server/app/connections/page.js +1 -1
  34. package/web/.next/server/app/connections/page_client-reference-manifest.js +1 -1
  35. package/web/.next/server/app/connections.html +1 -1
  36. package/web/.next/server/app/connections.rsc +4 -4
  37. package/web/.next/server/app/index.html +1 -1
  38. package/web/.next/server/app/index.rsc +4 -4
  39. package/web/.next/server/app/page.js +2 -2
  40. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  41. package/web/.next/server/app/relations/page.js +1 -1
  42. package/web/.next/server/app/relations/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/relations.html +1 -1
  44. package/web/.next/server/app/relations.rsc +4 -4
  45. package/web/.next/server/app/resources/[id]/page_client-reference-manifest.js +1 -1
  46. package/web/.next/server/app/resources/page_client-reference-manifest.js +1 -1
  47. package/web/.next/server/app/resources.html +1 -1
  48. package/web/.next/server/app/resources.rsc +4 -4
  49. package/web/.next/server/app/schedules/page.js +1 -1
  50. package/web/.next/server/app/schedules/page_client-reference-manifest.js +1 -1
  51. package/web/.next/server/app/schedules.html +1 -1
  52. package/web/.next/server/app/schedules.rsc +4 -4
  53. package/web/.next/server/app/sessions/page.js +1 -1
  54. package/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  55. package/web/.next/server/app/sessions.html +1 -1
  56. package/web/.next/server/app/sessions.rsc +4 -4
  57. package/web/.next/server/app/skills/page.js +1 -1
  58. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  59. package/web/.next/server/app/skills.html +1 -1
  60. package/web/.next/server/app/skills.rsc +4 -4
  61. package/web/.next/server/app/types/page.js +1 -1
  62. package/web/.next/server/app/types/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/types.html +1 -1
  64. package/web/.next/server/app/types.rsc +4 -4
  65. package/web/.next/server/app-paths-manifest.json +4 -4
  66. package/web/.next/server/chunks/143.js +1 -1
  67. package/web/.next/server/chunks/345.js +1 -1
  68. package/web/.next/server/chunks/368.js +1 -1
  69. package/web/.next/server/chunks/403.js +1 -1
  70. package/web/.next/server/chunks/505.js +3 -3
  71. package/web/.next/server/chunks/944.js +1 -1
  72. package/web/.next/server/chunks/953.js +1 -1
  73. package/web/.next/server/pages/404.html +1 -1
  74. package/web/.next/server/pages/500.html +1 -1
  75. package/web/.next/server/server-reference-manifest.json +1 -1
  76. package/web/.next/static/chunks/{1517-5db81b0d2ac8f7f5.js → 1517-9b82b61ee9921bb1.js} +1 -1
  77. package/web/.next/static/chunks/{1606-2aa8ee5a6751f920.js → 1606-6d25d725156f5127.js} +1 -1
  78. package/web/.next/static/chunks/{2547-821b5a01cff1915f.js → 2547-8b6bdae3c622a158.js} +1 -1
  79. package/web/.next/static/chunks/4491-886c18aee1e9c737.js +1 -0
  80. package/web/.next/static/chunks/4740-0581e5cb9334010b.js +1 -0
  81. package/web/.next/static/chunks/{5587-7e580517c4105e63.js → 5587-cdccc5cf4056b739.js} +1 -1
  82. package/web/.next/static/chunks/app/agents/[id]/page-eeb93069b69e7f7e.js +1 -0
  83. package/web/.next/static/chunks/app/agents/layout-db700c86124f32de.js +1 -0
  84. package/web/.next/static/chunks/app/agents/{page-0d8769f28c624f9c.js → page-bf40d654c5fe5c23.js} +1 -1
  85. package/web/.next/static/chunks/app/chat/{page-b1569cf72e958d8b.js → page-157685bdaad86c26.js} +1 -1
  86. package/web/.next/static/chunks/app/colony/{page-ffdc123dac47ad69.js → page-600b663a716d08c4.js} +1 -1
  87. package/web/.next/static/chunks/app/connections/{page-0e4f9f272da95c83.js → page-cbfcb1547d45c833.js} +1 -1
  88. package/web/.next/static/chunks/app/{layout-068c1695366993c1.js → layout-b327408bcfb13a70.js} +1 -1
  89. package/web/.next/static/chunks/app/page-78c38105a034b0b3.js +1 -0
  90. package/web/.next/static/chunks/app/relations/{page-a9d991b174372f37.js → page-622a4c5723c7c5cc.js} +1 -1
  91. package/web/.next/static/chunks/app/resources/[id]/{page-48131f750e983a07.js → page-b9af9eecf693b695.js} +1 -1
  92. package/web/.next/static/chunks/app/resources/{page-706d012a4c0d57ce.js → page-a2e17f0dc205a2a4.js} +1 -1
  93. package/web/.next/static/chunks/app/schedules/{page-5821c0d26afde69f.js → page-d534462088c64b59.js} +1 -1
  94. package/web/.next/static/chunks/app/sessions/{page-6145eb19002dd390.js → page-6fef5b73bfbf262e.js} +1 -1
  95. package/web/.next/static/chunks/app/skills/page-ab25a3a6d348a049.js +1 -0
  96. package/web/.next/static/chunks/app/types/{page-a991fe9afa51bdb7.js → page-7e08ca1bcbee605f.js} +1 -1
  97. package/web/.next/static/css/f9099a8765eed6f0.css +1 -0
  98. package/web/.next/trace +31 -31
  99. package/web/.next/static/chunks/4491-49c700f509985c9e.js +0 -1
  100. package/web/.next/static/chunks/9977-78b955d85f795a21.js +0 -1
  101. package/web/.next/static/chunks/app/agents/[id]/page-da4050632f63874d.js +0 -1
  102. package/web/.next/static/chunks/app/agents/layout-2536c0c15d09ddcf.js +0 -1
  103. package/web/.next/static/chunks/app/page-68ae166b2f8dbf94.js +0 -1
  104. package/web/.next/static/chunks/app/skills/page-b94287c3a333870d.js +0 -1
  105. package/web/.next/static/css/1afa1d0e0fdc435f.css +0 -1
  106. /package/web/.next/static/{qv9RAZqD8Of6bH1cw6RzA → HzL5-xIKbr0VsaWuLWHNl}/_buildManifest.js +0 -0
  107. /package/web/.next/static/{qv9RAZqD8Of6bH1cw6RzA → HzL5-xIKbr0VsaWuLWHNl}/_ssgManifest.js +0 -0
package/CHANGELOG.md CHANGED
@@ -6,6 +6,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.11.0] - 2026-10-23
10
+
11
+ ### Added
12
+
13
+ - **Cancel a delegated task.** A task sent by mistake or no longer needed can be taken back, queued or running. In **Recent delegations** (Colony and Home) each unfinished task has a **×** button: a queued one is cancelled at once and never starts, a running one asks first and is stopped. The orchestrator is told once, in the place where it delegated. Orchestrators get two new tools, `dispatch_cancel` (only for their own tasks; they get no extra message for a task they cancel) and `dispatch_list` (their tasks that have not finished). A cancelled task shows as **cancelled** with a grey dot, frees its place in the limit of 10, and what the worker already changed stays in the folder. Unfinished tasks now come first in the list and are never hidden by its limit. Stop on a worker still stops only its current turn and marks that delegation failed.
14
+
15
+ ### Fixed
16
+
17
+ - **Lists follow the language at once.** Changing the language with the app open left some names in the old language until a reload: the **No colony** group in the chat's agent list and in **Resources**, and the colony hints in the agent form and the resource manager field. They now change right away.
18
+ - **Collapsing the agent list clears its search.** A search typed in the chat's agent list kept filtering the collapsed list, which has no search box, so agents and groups seemed to vanish and the group headings stayed disabled. Collapsing now clears the search, and expanding shows the full list with an empty box.
19
+ - **More of the interface follows the language.** The **Entrypoint**, **Gateway** and **Aliases** labels of a service's details, and the names screen readers hear for the method picker of a new HTTP request and for the sideways scroll bar of the diff view, were always in English; a request that failed in an HTTP file showed `error` in English too. They now come from the dictionaries. Empty cells and values show a plain hyphen instead of a long dash, and a service's empty lists (health log, variables, ports, mounts, networks) say **none**.
20
+
21
+ ## [1.10.1] - 2026-10-23
22
+
23
+ ### Fixed
24
+
25
+ - **Long prompts no longer cover the editor's footer.** In **Preview** and **Split**, a long system prompt was drawn over the word count and tips line under the editor. Each pane now scrolls on its own inside the editor and the footer stays visible below it.
26
+ - **`hive-am update` no longer cuts the install off after 2 minutes.** The install now has its own limit of 15 minutes (`HIVE_AM_INSTALL_TIMEOUT`, in seconds, `0` for none) and says beforehand that it can take a few minutes. When npm runs past the limit or is stopped by a signal, the message says so, warns the install may be incomplete and gives the command to run by hand, instead of `exit null` and a permissions hint; that hint now only shows when npm reports `EACCES` or `EPERM`.
27
+
9
28
  ## [1.10.0] - 2026-10-22
10
29
 
11
30
  ### Added
@@ -283,7 +302,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
283
302
  - Complete documentation in English and Spanish.
284
303
  - npm package with the `hive-am` command.
285
304
 
286
- [Unreleased]: https://github.com/hughcbscrt/hive-am/compare/v1.10.0...HEAD
305
+ [Unreleased]: https://github.com/hughcbscrt/hive-am/compare/v1.11.0...HEAD
306
+ [1.11.0]: https://github.com/hughcbscrt/hive-am/compare/v1.10.1...v1.11.0
307
+ [1.10.1]: https://github.com/hughcbscrt/hive-am/compare/v1.10.0...v1.10.1
287
308
  [1.10.0]: https://github.com/hughcbscrt/hive-am/compare/v1.9.1...v1.10.0
288
309
  [1.9.1]: https://github.com/hughcbscrt/hive-am/compare/v1.9.0...v1.9.1
289
310
  [1.9.0]: https://github.com/hughcbscrt/hive-am/compare/v1.8.0...v1.9.0
package/bin/hive-am.mjs CHANGED
@@ -245,9 +245,21 @@ async function update(list) {
245
245
  if (kind === 'source') fail('This is a source checkout: update it with `git pull` and `npm install`, then rebuild.');
246
246
  if (kind !== 'global') fail(`hive-am was not installed globally with npm (${root}), so it cannot update itself. Install the new version the way you installed this one: npm install -g ${pkg.name}@${latest}`);
247
247
 
248
- console.log(`Installing ${pkg.name}@${latest} ...`);
249
- const r = npm(['install', '-g', `${pkg.name}@${latest}`], { stdio: 'inherit', encoding: undefined });
250
- if (r.status !== 0) fail(`npm could not install ${pkg.name}@${latest} (exit ${r.status}). If it is a permissions error, see https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally`);
248
+ // the install gets its own limit: a slow registry or a big download easily takes longer than the 2 minutes of the quick queries
249
+ const raw = process.env.HIVE_AM_INSTALL_TIMEOUT;
250
+ const secs = raw === undefined || raw === '' ? 900 : Number(raw);
251
+ if (!Number.isFinite(secs) || secs < 0) fail(`HIVE_AM_INSTALL_TIMEOUT must be a number of seconds (0 for no limit), not "${raw}".`);
252
+ console.log(`Installing ${pkg.name}@${latest} ... this can take a few minutes (Ctrl+C stops it).`);
253
+ // stderr is piped so a permissions error can be told apart, and written back so npm's own text is not lost
254
+ const r = npm(['install', '-g', `${pkg.name}@${latest}`], { stdio: ['inherit', 'inherit', 'pipe'], timeout: secs * 1000 });
255
+ if (r.stderr) process.stderr.write(r.stderr);
256
+ const manual = `The install may be incomplete: run it yourself with npm install -g ${pkg.name}@${latest}`;
257
+ if (r.error?.code === 'ETIMEDOUT') fail(`npm was stopped after ${secs} seconds without finishing the install of ${pkg.name}@${latest} (raise the limit with HIVE_AM_INSTALL_TIMEOUT, in seconds, 0 for none). ${manual}`);
258
+ if (r.signal) fail(`npm was stopped by ${r.signal} while installing ${pkg.name}@${latest}. ${manual}`);
259
+ if (r.status !== 0) {
260
+ const perms = /\bE(ACCES|PERM)\b/.test(r.stderr ?? '') ? ' It is a permissions error: see https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally' : '';
261
+ fail(`npm could not install ${pkg.name}@${latest} (exit ${r.status ?? r.error?.message}).${perms}`);
262
+ }
251
263
  let now = latest;
252
264
  try { now = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version; } catch { /* the folder was replaced: keep what npm installed */ }
253
265
  console.log(`hive-am updated: ${pkg.version} -> ${now}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hive-am",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "A colony of coding agents: orchestrators and subagents over Claude Code, OpenCode, Kiro and Cursor, with real session resume, a browser UI and Telegram.",
5
5
  "keywords": [
6
6
  "ai-agents",
package/server/dist/db.js CHANGED
@@ -245,8 +245,9 @@ export const agents = {
245
245
  allSessions: () => q(`SELECT s.*, a.name agent_name, a.role agent_role, f.name from_name FROM agent_sessions s JOIN agents a ON a.id=s.agent_id LEFT JOIN agents f ON f.id=s.from_id ORDER BY s.last_seen DESC`).all(),
246
246
  };
247
247
  // ---- dispatch log ----
248
- // A delegation is `queued` until the worker's turn starts (it waits behind the worker's other turns), then `running`, then `done`, `failed`
249
- // or `interrupted` (the server stopped while it was queued or running).
248
+ // A delegation is `queued` until the worker's turn starts (it waits behind the worker's other turns), then `running`, then `done`, `failed`,
249
+ // `interrupted` (the server stopped while it was queued or running) or `cancelled` (its orchestrator or the person took it back). Only a
250
+ // row still queued or running is closed, so the first of the end of the turn and a cancel sets the one final state.
250
251
  export const dispatches = {
251
252
  start(from, to, task, mode = 'sync', origin = null) {
252
253
  const id = uid();
@@ -256,16 +257,24 @@ export const dispatches = {
256
257
  get: (id) => q('SELECT * FROM dispatches WHERE id=?').get(id),
257
258
  setRunning: (id) => q("UPDATE dispatches SET status='running' WHERE id=? AND status='queued'").run(id),
258
259
  setSession: (id, sessionId) => q('UPDATE dispatches SET session_id=? WHERE id=?').run(sessionId, id),
259
- finish: (id, status, result) => q('UPDATE dispatches SET status=?, result=?, finished_at=? WHERE id=?').run(status, result.slice(0, 20000), now(), id),
260
+ finish: (id, status, result) => q("UPDATE dispatches SET status=?, result=?, finished_at=? WHERE id=? AND status IN ('queued','running')").run(status, result.slice(0, 20000), now(), id).changes > 0,
261
+ /** Cancels a task that has not finished; false if it already had. Cancelled by its orchestrator, it is never announced (the tool told it). */
262
+ cancel: (id, result, byOrchestrator) => {
263
+ const at = now();
264
+ return q("UPDATE dispatches SET status='cancelled', result=?, finished_at=?, notified_at=CASE WHEN ? THEN ? ELSE notified_at END WHERE id=? AND status IN ('queued','running')").run(result, at, byOrchestrator ? 1 : 0, at, id).changes > 0;
265
+ },
260
266
  markNotified: (id) => q('UPDATE dispatches SET notified_at=? WHERE id=? AND notified_at IS NULL').run(now(), id),
261
267
  markRead: (id) => q('UPDATE dispatches SET read_at=? WHERE id=? AND read_at IS NULL').run(now(), id),
262
268
  /** Background tasks of an orchestrator that have not finished yet. */
263
269
  unfinished: (from) => q("SELECT COUNT(*) AS n FROM dispatches WHERE from_id=? AND mode='background' AND status IN ('queued','running')").get(from).n,
270
+ /** Every task of an orchestrator that has not finished, oldest first. */
271
+ open: (from) => q("SELECT * FROM dispatches WHERE from_id=? AND status IN ('queued','running') ORDER BY created_at, id").all(from),
264
272
  /** Finished background tasks whose result was not handed to the orchestrator yet. */
265
273
  undelivered: () => q("SELECT * FROM dispatches WHERE mode='background' AND status NOT IN ('queued','running') AND notified_at IS NULL ORDER BY created_at").all(),
266
274
  /** At startup nothing is running any more: what was queued or running is interrupted (a task is never resumed). */
267
275
  interruptLeftovers: () => q("UPDATE dispatches SET status='interrupted', finished_at=? WHERE status IN ('queued','running')").run(now()).changes,
268
- recent: (limit = 50) => q('SELECT * FROM dispatches ORDER BY created_at DESC LIMIT ?').all(limit),
276
+ /** The newest rows, plus any older one that has not finished (so the interface can always show and cancel it). */
277
+ recent: (limit = 50) => q("SELECT * FROM (SELECT * FROM dispatches ORDER BY created_at DESC LIMIT ?) UNION SELECT * FROM dispatches WHERE status IN ('queued','running') ORDER BY created_at DESC").all(limit),
269
278
  };
270
279
  // Agents left 'running' by a crash/blackout are simply idle again: their session is intact on disk.
271
280
  q("UPDATE agents SET status='idle' WHERE status='running'").run();
@@ -1,6 +1,6 @@
1
1
  import { agents, dispatches } from './db.js';
2
2
  import { threads } from './connections/store.js';
3
- import { liveOrigin, notifyAgentsChanged, startDispatch } from './runtime.js';
3
+ import { abortDispatch, cancelQueued, liveOrigin, notifyAgentsChanged, startDispatch } from './runtime.js';
4
4
  import { deliverTo } from './wake.js';
5
5
  import { DomainError } from './errors.js';
6
6
  /**
@@ -15,7 +15,7 @@ export const MAX_BACKGROUND = 10; // unfinished background tasks per orchestrato
15
15
  export const MAX_WAIT_S = 60; // the longest one `dispatch_status` call waits
16
16
  const POLL_MS = 250;
17
17
  const RETRY_MS = Number(process.env.HIVE_AM_WAKE_RETRY_MS) || 5000; // wait before trying a delivery again (as wake-ups do)
18
- const FINISHED = new Set(['done', 'failed', 'interrupted']);
18
+ const FINISHED = new Set(['done', 'failed', 'interrupted', 'cancelled']);
19
19
  const NO_TEXT = '(the subagent finished without a text answer)';
20
20
  const nameOf = (id) => agents.get(id)?.name ?? 'a removed agent';
21
21
  /** Starts a delegation without waiting for it. In the background, its end is announced to the orchestrator; a synchronous one is polled. */
@@ -74,6 +74,40 @@ export async function taskStatus(fromId, id, waitSeconds) {
74
74
  out.note = `wait_seconds is capped at ${MAX_WAIT_S}: waited ${MAX_WAIT_S} s at most.`;
75
75
  return out;
76
76
  }
77
+ const CANCELLED_BY = { orchestrator: 'Cancelled by the orchestrator before it finished.', person: 'Cancelled by the person before it finished.' };
78
+ /**
79
+ * Takes a delegated task back. A queued one leaves the worker's queue and never starts; a running one has its turn aborted (only that turn).
80
+ * One already finished is left as it is and its state is answered, so cancelling twice is harmless. The row is closed by the same conditional
81
+ * update as the end of the turn, so whichever comes first sets the one final state. `by` is the orchestrator that started it, or null for the
82
+ * person (from the web), who may cancel any task; cancelled by the person, the orchestrator is told once.
83
+ */
84
+ export function cancelTask(by, id) {
85
+ if (!id)
86
+ throw new DispatchError('id is required');
87
+ const row = dispatches.get(id);
88
+ if (!row)
89
+ throw new DispatchError(`There is no delegated task with id "${id}".`, 'not_found');
90
+ if (by !== null && row.from_id !== by)
91
+ throw new DispatchError(`Task "${id}" was not started by you.`, 'forbidden');
92
+ const was = row.status;
93
+ const cancelled = !FINISHED.has(was) && dispatches.cancel(id, by === null ? CANCELLED_BY.person : CANCELLED_BY.orchestrator, by !== null);
94
+ if (cancelled) {
95
+ if (was === 'queued')
96
+ cancelQueued(row.to_id, id);
97
+ else
98
+ abortDispatch(row.to_id, id);
99
+ notifyAgentsChanged();
100
+ // At once, not when the turn ends: a queued task only settles when the worker's queue reaches it.
101
+ if (by === null)
102
+ void announce(id);
103
+ }
104
+ const now = dispatches.get(id) ?? row;
105
+ return { id, agent: nameOf(now.to_id), status: now.status, mode: now.mode, was, cancelled, ...(FINISHED.has(now.status) ? { result: resultText(now) } : {}) };
106
+ }
107
+ /** The tasks of an orchestrator that have not finished, oldest first, so it can tell which one to cancel. */
108
+ export function openTasks(fromId) {
109
+ return dispatches.open(fromId).map((r) => ({ id: r.id, agent: nameOf(r.to_id), status: r.status, mode: r.mode, task: r.task.length > 200 ? `${r.task.slice(0, 200)}…` : r.task, age_s: Math.max(0, Math.round((Date.now() - r.created_at) / 1000)) }));
110
+ }
77
111
  /** What a finished task says: its answer, its error, or why it stopped. */
78
112
  function resultText(row) {
79
113
  if (row.status === 'interrupted')
@@ -85,12 +119,26 @@ function resultText(row) {
85
119
  const noticeText = (row) => `Delegated task ${row.id} to ${nameOf(row.to_id)} finished (${row.status}). Result:\n${resultText(row)}`;
86
120
  const NEXT = 'Carry on with the work this task was part of and tell the person what came of it';
87
121
  const timers = new Map();
122
+ const delivering = new Set();
88
123
  /** Hands the result of a finished background task to the orchestrator, once. Tried again while the platform is not connected yet. */
89
124
  async function announce(id, tries = 0) {
125
+ // A second call while the first is still delivering (a cancel, then the end of the aborted turn) or waiting to try again does nothing.
126
+ if (delivering.has(id) || (!tries && timers.has(id)))
127
+ return;
90
128
  timers.delete(id);
91
129
  const row = dispatches.get(id);
92
130
  if (!row || row.mode !== 'background' || row.notified_at || !FINISHED.has(row.status))
93
131
  return;
132
+ delivering.add(id);
133
+ try {
134
+ await deliver(row, tries);
135
+ }
136
+ finally {
137
+ delivering.delete(id);
138
+ }
139
+ }
140
+ async function deliver(row, tries) {
141
+ const id = row.id;
94
142
  const o = row.origin ? JSON.parse(row.origin) : null;
95
143
  const to = { agent_id: row.from_id, connection_id: o?.connection_id ?? null, thread_id: o?.thread_id ?? null, req_id: o?.req_id ?? null, req_name: o?.req_name ?? null };
96
144
  const again = () => {
@@ -1,5 +1,5 @@
1
1
  import { agents, dispatches } from '../../db.js';
2
- import { startTask, taskStatus } from '../../dispatches.js';
2
+ import { cancelTask, openTasks, startTask, taskStatus } from '../../dispatches.js';
3
3
  import { dispatch, liveTurn, notifyAgentsChanged } from '../../runtime.js';
4
4
  import { agentOrFail } from '../../services/agents.js';
5
5
  import { body } from '../body.js';
@@ -40,4 +40,19 @@ route('POST', '/api/dispatch/status', async ({ req }) => {
40
40
  throw bad('from is required');
41
41
  return taskStatus(text(from), text(id), wait_seconds);
42
42
  });
43
+ // Cancelling answers the final state: `cancelled`, or the one the task already had (it is not an error).
44
+ route('POST', '/api/dispatch/cancel', async ({ req }) => {
45
+ const { from, id } = await body(req);
46
+ if (!from)
47
+ throw bad('from is required');
48
+ return cancelTask(text(from), text(id));
49
+ });
50
+ route('POST', '/api/dispatch/list', async ({ req }) => {
51
+ const { from } = await body(req);
52
+ if (!from)
53
+ throw bad('from is required');
54
+ return openTasks(text(from));
55
+ });
43
56
  route('GET', '/api/dispatches', () => dispatches.recent());
57
+ // From the web: the person can cancel any task, as they can stop any agent.
58
+ route('POST', '/api/dispatches/:id/cancel', ({ params }) => cancelTask(null, params[0]));
@@ -78,7 +78,7 @@ ${lazy.map((s) => `- **${s.name}**${s.description ? `: ${s.description}` : ''}`)
78
78
  const team = a.worker_ids.map((wid) => agents.get(wid)).filter(Boolean);
79
79
  if (team.length) {
80
80
  const roster = team.map((w) => `- **${w.name}**${w.description ? `: ${w.description}` : ''}`).join('\n');
81
- parts.push(`## Your team\nThese are your subagents — the ONLY agents you can delegate to:\n${roster}\n\nDelegate with the \`dispatch\` tool (${tn('dispatch')}) instead of doing their work yourself, then synthesize their answers. Always use it: never play a subagent's role yourself, and never use a generic built-in subagent in its place — a subagent is a separate real agent with its own session. Each delegation starts a fresh conversation for that subagent, so put all the context it needs in the task. \`dispatch\` returns at once with a task id, so you can answer the person or delegate more meanwhile, and the result reaches you as a message when the subagent finishes (you can also ask with the \`dispatch_status\` tool (${tn('dispatch_status')})). Use \`background: false\` only for a very short task whose answer you need right now: the call then waits for it. Your team can change at any time: when asked which agents you have or can delegate to, call the \`list_agents\` tool (${tn('list_agents')}) and report exactly what it returns, never an older list from memory. Never mention or try to use agents outside that list.`);
81
+ parts.push(`## Your team\nThese are your subagents — the ONLY agents you can delegate to:\n${roster}\n\nDelegate with the \`dispatch\` tool (${tn('dispatch')}) instead of doing their work yourself, then synthesize their answers. Always use it: never play a subagent's role yourself, and never use a generic built-in subagent in its place — a subagent is a separate real agent with its own session. Each delegation starts a fresh conversation for that subagent, so put all the context it needs in the task. \`dispatch\` returns at once with a task id, so you can answer the person or delegate more meanwhile, and the result reaches you as a message when the subagent finishes (you can also ask with the \`dispatch_status\` tool (${tn('dispatch_status')})). Use \`background: false\` only for a very short task whose answer you need right now: the call then waits for it. To take back a task that is no longer needed (sent by mistake, with the wrong context, or the plan changed), call the \`dispatch_cancel\` tool (${tn('dispatch_cancel')}) with its id; the \`dispatch_list\` tool (${tn('dispatch_list')}) lists your tasks that have not finished. What a cancelled subagent already changed stays in the folder, so check it before delegating again. Your team can change at any time: when asked which agents you have or can delegate to, call the \`list_agents\` tool (${tn('list_agents')}) and report exactly what it returns, never an older list from memory. Never mention or try to use agents outside that list.`);
82
82
  }
83
83
  else {
84
84
  parts.push('## Your team\nYou currently have no subagents connected to you, so you cannot delegate. If asked, say so; do not claim to know other agents.');
@@ -13,7 +13,7 @@ const states = new Map();
13
13
  const st = (id) => {
14
14
  let s = states.get(id);
15
15
  if (!s)
16
- states.set(id, (s = { chain: Promise.resolve(), controller: null, queued: 0, live: null }));
16
+ states.set(id, (s = { chain: Promise.resolve(), controller: null, dispatchId: null, queued: 0, live: null, cancelled: new Set() }));
17
17
  return s;
18
18
  };
19
19
  export const liveTurn = (id) => states.get(id)?.live ?? null;
@@ -47,6 +47,9 @@ export function sendTurn(agentId, prompt, source = 'user', from, dispatchId, ori
47
47
  }
48
48
  async function execute(agentId, prompt, source, from, dispatchId, origin) {
49
49
  const s = st(agentId);
50
+ // A delegation cancelled while it waited never reaches the provider; it already left the queue count when it was cancelled.
51
+ if (dispatchId && s.cancelled.delete(dispatchId))
52
+ return { ok: false, text: '', error: 'Cancelled' };
50
53
  s.queued--;
51
54
  let raw;
52
55
  let agent;
@@ -84,6 +87,7 @@ async function execute(agentId, prompt, source, from, dispatchId, origin) {
84
87
  const push = (event) => { s.live?.events.push(event); emit({ kind: 'event', agentId, turnId, event }); };
85
88
  try {
86
89
  s.controller = controller;
90
+ s.dispatchId = dispatchId ?? null;
87
91
  s.live = { turnId, prompt, events: [], source, origin };
88
92
  if (dispatchId)
89
93
  dispatches.setRunning(dispatchId);
@@ -143,6 +147,7 @@ async function execute(agentId, prompt, source, from, dispatchId, origin) {
143
147
  if (controller.signal.aborted && !error)
144
148
  push({ t: 'done', ok: false, summary: 'Stopped' });
145
149
  s.controller = null;
150
+ s.dispatchId = null;
146
151
  s.live = null;
147
152
  if (!error && !delegated && !controller.signal.aborted) {
148
153
  agents.setInstrHash(agentId, hash);
@@ -159,6 +164,24 @@ export function stopAgent(agentId, clearQueue = true) {
159
164
  s.controller.abort();
160
165
  return true;
161
166
  }
167
+ /** Takes a queued delegation out of the worker's queue: its turn is skipped when the chain reaches it, and the count drops now. */
168
+ export function cancelQueued(agentId, dispatchId) {
169
+ const s = states.get(agentId);
170
+ if (!s || s.queued <= 0 || s.cancelled.has(dispatchId))
171
+ return false;
172
+ s.cancelled.add(dispatchId);
173
+ s.queued--;
174
+ emit({ kind: 'status', agentId, status: s.live ? 'running' : s.queued ? 'queued' : agents.get(agentId)?.status === 'error' ? 'error' : 'idle', queued: s.queued });
175
+ return true;
176
+ }
177
+ /** Aborts the worker's turn only if it is serving this delegation, so cancelling one task never stops another. */
178
+ export function abortDispatch(agentId, dispatchId) {
179
+ const s = states.get(agentId);
180
+ if (!s?.controller || s.dispatchId !== dispatchId)
181
+ return false;
182
+ s.controller.abort();
183
+ return true;
184
+ }
162
185
  /**
163
186
  * Orchestrator → worker delegation. Enforces the explicit assignment rule. The task is stored and queued on the worker at once; `done`
164
187
  * settles when the worker's turn ends, after the row holds the result, so nothing depends on the caller still waiting for it.
@@ -174,6 +197,7 @@ export function startDispatch(fromId, workerName, task, mode = 'sync', origin =
174
197
  throw new Error(`"${worker.name}" is not assigned to ${from.name}`);
175
198
  const id = dispatches.start(from.id, worker.id, task, mode, origin);
176
199
  const done = sendTurn(worker.id, task, 'dispatch', from.id, id).then((res) => {
200
+ // A no-op when the task was cancelled meanwhile: the aborted or skipped turn does not turn `cancelled` into `failed`.
177
201
  dispatches.finish(id, res.ok ? 'done' : 'failed', res.text || res.error || '');
178
202
  return res;
179
203
  });
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Minimal MCP stdio server for hive-am agents. Tool groups come from HIVE_CAPS:
3
- // dispatch → list_agents, dispatch, dispatch_status (orchestrators with a team)
3
+ // dispatch → list_agents, dispatch, dispatch_status, dispatch_list, dispatch_cancel (orchestrators with a team)
4
4
  // channel → channel_reply, channel_send_file, channel_react, channel_mute (agents linked to Telegram/Slack)
5
5
  // wake → wake_me, wake_when_done, schedule_create, schedule_list, schedule_cancel (agents with the Wake-ups skill: to be woken later in the same place)
6
6
  // objects → object_list, object_logs, object_action (agents with the Colony objects skill: the servers and containers of their colony)
@@ -39,7 +39,7 @@ const allTools = [
39
39
  {
40
40
  cap: 'dispatch',
41
41
  name: 'dispatch_status',
42
- description: 'State of a task you delegated: queued, running, done, failed or interrupted, with the result once it has finished. With wait_seconds it waits up to that long for the end before answering.',
42
+ description: 'State of a task you delegated: queued, running, done, failed, interrupted or cancelled, with the result once it has finished. With wait_seconds it waits up to that long for the end before answering.',
43
43
  inputSchema: {
44
44
  type: 'object',
45
45
  properties: {
@@ -50,6 +50,23 @@ const allTools = [
50
50
  additionalProperties: false,
51
51
  },
52
52
  },
53
+ {
54
+ cap: 'dispatch',
55
+ name: 'dispatch_list',
56
+ description: 'List the tasks you delegated that have not finished yet (queued or running), with their id, subagent, state and age, oldest first.',
57
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
58
+ },
59
+ {
60
+ cap: 'dispatch',
61
+ name: 'dispatch_cancel',
62
+ description: 'Cancel a task you delegated that is no longer needed (sent by mistake, wrong context, or the plan changed). A queued task never starts; a running one is stopped at once, and what the subagent already changed stays in the folder, so check it before delegating again. A task that already finished is left as it is and its state is answered. You are not sent a message about a task you cancel.',
63
+ inputSchema: {
64
+ type: 'object',
65
+ properties: { id: { type: 'string', description: 'The task id that dispatch returned' } },
66
+ required: ['id'],
67
+ additionalProperties: false,
68
+ },
69
+ },
53
70
  {
54
71
  cap: 'channel',
55
72
  name: 'channel_reply',
@@ -333,12 +350,35 @@ async function call(name, args) {
333
350
  }
334
351
  if (s.status === 'done') return s.result;
335
352
  if (s.status === 'failed' || s.status === 'interrupted') throw new Error(`${j.agent} did not finish the task (${s.status}): ${s.result}`);
353
+ if (s.status === 'cancelled') throw new Error(`${j.agent} task was cancelled: ${s.result}`);
336
354
  }
337
355
  }
338
356
  if (name === 'dispatch_status') {
339
357
  const s = await taskStatus(args.id, args.wait_seconds);
340
358
  return `Task ${s.id} for ${s.agent}: ${s.status}.${s.note ? ` ${s.note}` : ''}${s.result !== undefined ? ` Result:\n${s.result}` : ' Not finished yet.'}`;
341
359
  }
360
+ if (name === 'dispatch_list') {
361
+ const r = await fetch(`${API}/api/dispatch/list`, {
362
+ method: 'POST', headers: { 'content-type': 'application/json' },
363
+ body: JSON.stringify({ from: FROM }),
364
+ });
365
+ const j = await r.json();
366
+ if (!r.ok) throw new Error(j.error ?? `dispatch_list failed (${r.status})`);
367
+ const age = (s) => (s < 60 ? `${s} s` : s < 3600 ? `${Math.round(s / 60)} min` : `${Math.round(s / 3600)} h`);
368
+ return j.length ? j.map((t) => `${t.id} · ${t.agent} · ${t.status} · ${t.mode} · ${age(t.age_s)} ago\n ${t.task.replace(/\s+/g, ' ')}`).join('\n') : 'You have no delegated tasks that have not finished.';
369
+ }
370
+ if (name === 'dispatch_cancel') {
371
+ const r = await fetch(`${API}/api/dispatch/cancel`, {
372
+ method: 'POST', headers: { 'content-type': 'application/json' },
373
+ body: JSON.stringify({ from: FROM, id: args.id }),
374
+ });
375
+ const j = await r.json();
376
+ if (!r.ok) throw new Error(j.error ?? `dispatch_cancel failed (${r.status})`);
377
+ if (!j.cancelled) return `Task ${j.id} for ${j.agent} had already finished (${j.status}); nothing changed.${j.result !== undefined ? ` Result:\n${j.result}` : ''}`;
378
+ return j.was === 'running'
379
+ ? `Task ${j.id} for ${j.agent} cancelled: ${j.agent} was working on it and was stopped. What it already changed stays in the folder; check it before delegating again.`
380
+ : `Task ${j.id} for ${j.agent} cancelled before it started.`;
381
+ }
342
382
  if (name === 'channel_reply') {
343
383
  const r = await fetch(`${API}/api/channel/reply`, {
344
384
  method: 'POST', headers: { 'content-type': 'application/json' },
@@ -1 +1 @@
1
- qv9RAZqD8Of6bH1cw6RzA
1
+ HzL5-xIKbr0VsaWuLWHNl
@@ -13,40 +13,40 @@
13
13
  "static/chunks/89-b194fcffe5120c44.js",
14
14
  "static/chunks/main-app-27cc7ffdb888b879.js",
15
15
  "static/css/945795da867f5250.css",
16
- "static/css/1afa1d0e0fdc435f.css",
16
+ "static/css/f9099a8765eed6f0.css",
17
17
  "static/chunks/586-4f67b823df060b46.js",
18
18
  "static/chunks/8523-82e1ee6239699d8a.js",
19
- "static/chunks/9977-78b955d85f795a21.js",
19
+ "static/chunks/4740-0581e5cb9334010b.js",
20
20
  "static/chunks/969-07e498da015b149a.js",
21
21
  "static/chunks/3790-3d3f11a264e8ede8.js",
22
- "static/chunks/app/layout-068c1695366993c1.js"
22
+ "static/chunks/app/layout-b327408bcfb13a70.js"
23
23
  ],
24
- "/chat/page": [
24
+ "/colony/page": [
25
25
  "static/chunks/webpack-ba68c1813987a630.js",
26
26
  "static/chunks/0937d497-daa99a23a4431317.js",
27
27
  "static/chunks/89-b194fcffe5120c44.js",
28
28
  "static/chunks/main-app-27cc7ffdb888b879.js",
29
29
  "static/chunks/586-4f67b823df060b46.js",
30
30
  "static/chunks/7706-bd0d2bda0e0c8d1e.js",
31
- "static/chunks/9977-78b955d85f795a21.js",
32
- "static/chunks/1517-5db81b0d2ac8f7f5.js",
33
- "static/chunks/app/chat/page-b1569cf72e958d8b.js"
31
+ "static/chunks/5642-09dd4161ddddaf94.js",
32
+ "static/chunks/4162-c2361cb017547c37.js",
33
+ "static/chunks/4740-0581e5cb9334010b.js",
34
+ "static/chunks/1517-9b82b61ee9921bb1.js",
35
+ "static/chunks/969-07e498da015b149a.js",
36
+ "static/chunks/1606-6d25d725156f5127.js",
37
+ "static/chunks/5587-cdccc5cf4056b739.js",
38
+ "static/chunks/app/colony/page-600b663a716d08c4.js"
34
39
  ],
35
- "/colony/page": [
40
+ "/chat/page": [
36
41
  "static/chunks/webpack-ba68c1813987a630.js",
37
42
  "static/chunks/0937d497-daa99a23a4431317.js",
38
43
  "static/chunks/89-b194fcffe5120c44.js",
39
44
  "static/chunks/main-app-27cc7ffdb888b879.js",
40
45
  "static/chunks/586-4f67b823df060b46.js",
41
46
  "static/chunks/7706-bd0d2bda0e0c8d1e.js",
42
- "static/chunks/5642-09dd4161ddddaf94.js",
43
- "static/chunks/4162-c2361cb017547c37.js",
44
- "static/chunks/9977-78b955d85f795a21.js",
45
- "static/chunks/1517-5db81b0d2ac8f7f5.js",
46
- "static/chunks/969-07e498da015b149a.js",
47
- "static/chunks/1606-2aa8ee5a6751f920.js",
48
- "static/chunks/5587-7e580517c4105e63.js",
49
- "static/chunks/app/colony/page-ffdc123dac47ad69.js"
47
+ "static/chunks/4740-0581e5cb9334010b.js",
48
+ "static/chunks/1517-9b82b61ee9921bb1.js",
49
+ "static/chunks/app/chat/page-157685bdaad86c26.js"
50
50
  ],
51
51
  "/connections/page": [
52
52
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -54,8 +54,8 @@
54
54
  "static/chunks/89-b194fcffe5120c44.js",
55
55
  "static/chunks/main-app-27cc7ffdb888b879.js",
56
56
  "static/chunks/586-4f67b823df060b46.js",
57
- "static/chunks/9977-78b955d85f795a21.js",
58
- "static/chunks/app/connections/page-0e4f9f272da95c83.js"
57
+ "static/chunks/4740-0581e5cb9334010b.js",
58
+ "static/chunks/app/connections/page-cbfcb1547d45c833.js"
59
59
  ],
60
60
  "/page": [
61
61
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -64,10 +64,10 @@
64
64
  "static/chunks/main-app-27cc7ffdb888b879.js",
65
65
  "static/chunks/586-4f67b823df060b46.js",
66
66
  "static/chunks/7706-bd0d2bda0e0c8d1e.js",
67
- "static/chunks/9977-78b955d85f795a21.js",
68
- "static/chunks/1517-5db81b0d2ac8f7f5.js",
67
+ "static/chunks/4740-0581e5cb9334010b.js",
68
+ "static/chunks/1517-9b82b61ee9921bb1.js",
69
69
  "static/chunks/3790-3d3f11a264e8ede8.js",
70
- "static/chunks/app/page-68ae166b2f8dbf94.js"
70
+ "static/chunks/app/page-78c38105a034b0b3.js"
71
71
  ],
72
72
  "/relations/page": [
73
73
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -78,10 +78,10 @@
78
78
  "static/chunks/2b9ff8ee-0851c7e556e3a39b.js",
79
79
  "static/chunks/586-4f67b823df060b46.js",
80
80
  "static/chunks/3757-45ea801718ec19e4.js",
81
- "static/chunks/9977-78b955d85f795a21.js",
82
- "static/chunks/app/relations/page-a9d991b174372f37.js"
81
+ "static/chunks/4740-0581e5cb9334010b.js",
82
+ "static/chunks/app/relations/page-622a4c5723c7c5cc.js"
83
83
  ],
84
- "/resources/[id]/page": [
84
+ "/resources/page": [
85
85
  "static/chunks/webpack-ba68c1813987a630.js",
86
86
  "static/chunks/0937d497-daa99a23a4431317.js",
87
87
  "static/chunks/89-b194fcffe5120c44.js",
@@ -91,16 +91,16 @@
91
91
  "static/chunks/5642-09dd4161ddddaf94.js",
92
92
  "static/chunks/4837-8dc05d75f258f9e1.js",
93
93
  "static/chunks/1771-4688ad65e21bb512.js",
94
- "static/chunks/9977-78b955d85f795a21.js",
95
- "static/chunks/1517-5db81b0d2ac8f7f5.js",
94
+ "static/chunks/4740-0581e5cb9334010b.js",
95
+ "static/chunks/1517-9b82b61ee9921bb1.js",
96
96
  "static/chunks/969-07e498da015b149a.js",
97
- "static/chunks/1606-2aa8ee5a6751f920.js",
98
- "static/chunks/5587-7e580517c4105e63.js",
97
+ "static/chunks/1606-6d25d725156f5127.js",
98
+ "static/chunks/5587-cdccc5cf4056b739.js",
99
99
  "static/chunks/4030-bc2e0aa4da0d9ad8.js",
100
- "static/chunks/4491-49c700f509985c9e.js",
101
- "static/chunks/app/resources/[id]/page-48131f750e983a07.js"
100
+ "static/chunks/4491-886c18aee1e9c737.js",
101
+ "static/chunks/app/resources/page-a2e17f0dc205a2a4.js"
102
102
  ],
103
- "/resources/page": [
103
+ "/resources/[id]/page": [
104
104
  "static/chunks/webpack-ba68c1813987a630.js",
105
105
  "static/chunks/0937d497-daa99a23a4431317.js",
106
106
  "static/chunks/89-b194fcffe5120c44.js",
@@ -110,14 +110,14 @@
110
110
  "static/chunks/5642-09dd4161ddddaf94.js",
111
111
  "static/chunks/4837-8dc05d75f258f9e1.js",
112
112
  "static/chunks/1771-4688ad65e21bb512.js",
113
- "static/chunks/9977-78b955d85f795a21.js",
114
- "static/chunks/1517-5db81b0d2ac8f7f5.js",
113
+ "static/chunks/4740-0581e5cb9334010b.js",
114
+ "static/chunks/1517-9b82b61ee9921bb1.js",
115
115
  "static/chunks/969-07e498da015b149a.js",
116
- "static/chunks/1606-2aa8ee5a6751f920.js",
117
- "static/chunks/5587-7e580517c4105e63.js",
116
+ "static/chunks/1606-6d25d725156f5127.js",
117
+ "static/chunks/5587-cdccc5cf4056b739.js",
118
118
  "static/chunks/4030-bc2e0aa4da0d9ad8.js",
119
- "static/chunks/4491-49c700f509985c9e.js",
120
- "static/chunks/app/resources/page-706d012a4c0d57ce.js"
119
+ "static/chunks/4491-886c18aee1e9c737.js",
120
+ "static/chunks/app/resources/[id]/page-b9af9eecf693b695.js"
121
121
  ],
122
122
  "/schedules/page": [
123
123
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -125,8 +125,8 @@
125
125
  "static/chunks/89-b194fcffe5120c44.js",
126
126
  "static/chunks/main-app-27cc7ffdb888b879.js",
127
127
  "static/chunks/586-4f67b823df060b46.js",
128
- "static/chunks/9977-78b955d85f795a21.js",
129
- "static/chunks/app/schedules/page-5821c0d26afde69f.js"
128
+ "static/chunks/4740-0581e5cb9334010b.js",
129
+ "static/chunks/app/schedules/page-d534462088c64b59.js"
130
130
  ],
131
131
  "/sessions/page": [
132
132
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -136,10 +136,10 @@
136
136
  "static/chunks/586-4f67b823df060b46.js",
137
137
  "static/chunks/7706-bd0d2bda0e0c8d1e.js",
138
138
  "static/chunks/2654-4aeef09c0d6bf56a.js",
139
- "static/chunks/9977-78b955d85f795a21.js",
139
+ "static/chunks/4740-0581e5cb9334010b.js",
140
140
  "static/chunks/969-07e498da015b149a.js",
141
- "static/chunks/2547-821b5a01cff1915f.js",
142
- "static/chunks/app/sessions/page-6145eb19002dd390.js"
141
+ "static/chunks/2547-8b6bdae3c622a158.js",
142
+ "static/chunks/app/sessions/page-6fef5b73bfbf262e.js"
143
143
  ],
144
144
  "/skills/page": [
145
145
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -148,8 +148,8 @@
148
148
  "static/chunks/main-app-27cc7ffdb888b879.js",
149
149
  "static/chunks/586-4f67b823df060b46.js",
150
150
  "static/chunks/7706-bd0d2bda0e0c8d1e.js",
151
- "static/chunks/9977-78b955d85f795a21.js",
152
- "static/chunks/app/skills/page-b94287c3a333870d.js"
151
+ "static/chunks/4740-0581e5cb9334010b.js",
152
+ "static/chunks/app/skills/page-ab25a3a6d348a049.js"
153
153
  ],
154
154
  "/types/page": [
155
155
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -158,9 +158,9 @@
158
158
  "static/chunks/main-app-27cc7ffdb888b879.js",
159
159
  "static/chunks/586-4f67b823df060b46.js",
160
160
  "static/chunks/7706-bd0d2bda0e0c8d1e.js",
161
- "static/chunks/9977-78b955d85f795a21.js",
162
- "static/chunks/1517-5db81b0d2ac8f7f5.js",
163
- "static/chunks/app/types/page-a991fe9afa51bdb7.js"
161
+ "static/chunks/4740-0581e5cb9334010b.js",
162
+ "static/chunks/1517-9b82b61ee9921bb1.js",
163
+ "static/chunks/app/types/page-7e08ca1bcbee605f.js"
164
164
  ],
165
165
  "/agents/layout": [
166
166
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -170,10 +170,10 @@
170
170
  "static/chunks/586-4f67b823df060b46.js",
171
171
  "static/chunks/7706-bd0d2bda0e0c8d1e.js",
172
172
  "static/chunks/5642-09dd4161ddddaf94.js",
173
- "static/chunks/9977-78b955d85f795a21.js",
174
- "static/chunks/1517-5db81b0d2ac8f7f5.js",
173
+ "static/chunks/4740-0581e5cb9334010b.js",
174
+ "static/chunks/1517-9b82b61ee9921bb1.js",
175
175
  "static/chunks/969-07e498da015b149a.js",
176
- "static/chunks/app/agents/layout-2536c0c15d09ddcf.js"
176
+ "static/chunks/app/agents/layout-db700c86124f32de.js"
177
177
  ],
178
178
  "/agents/[id]/page": [
179
179
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -185,13 +185,13 @@
185
185
  "static/chunks/5642-09dd4161ddddaf94.js",
186
186
  "static/chunks/4837-8dc05d75f258f9e1.js",
187
187
  "static/chunks/3214-c9d0c933db5b0f44.js",
188
- "static/chunks/9977-78b955d85f795a21.js",
189
- "static/chunks/1517-5db81b0d2ac8f7f5.js",
188
+ "static/chunks/4740-0581e5cb9334010b.js",
189
+ "static/chunks/1517-9b82b61ee9921bb1.js",
190
190
  "static/chunks/969-07e498da015b149a.js",
191
- "static/chunks/1606-2aa8ee5a6751f920.js",
191
+ "static/chunks/1606-6d25d725156f5127.js",
192
192
  "static/chunks/4030-bc2e0aa4da0d9ad8.js",
193
- "static/chunks/2547-821b5a01cff1915f.js",
194
- "static/chunks/app/agents/[id]/page-da4050632f63874d.js"
193
+ "static/chunks/2547-8b6bdae3c622a158.js",
194
+ "static/chunks/app/agents/[id]/page-eeb93069b69e7f7e.js"
195
195
  ],
196
196
  "/agents/page": [
197
197
  "static/chunks/webpack-ba68c1813987a630.js",
@@ -200,9 +200,9 @@
200
200
  "static/chunks/main-app-27cc7ffdb888b879.js",
201
201
  "static/chunks/586-4f67b823df060b46.js",
202
202
  "static/chunks/7706-bd0d2bda0e0c8d1e.js",
203
- "static/chunks/9977-78b955d85f795a21.js",
204
- "static/chunks/1517-5db81b0d2ac8f7f5.js",
205
- "static/chunks/app/agents/page-0d8769f28c624f9c.js"
203
+ "static/chunks/4740-0581e5cb9334010b.js",
204
+ "static/chunks/1517-9b82b61ee9921bb1.js",
205
+ "static/chunks/app/agents/page-bf40d654c5fe5c23.js"
206
206
  ]
207
207
  }
208
208
  }