@desplega.ai/agent-swarm 1.157.0 → 1.158.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 (256) hide show
  1. package/dist/{acp-adapter-qhqrk4q0.js → acp-adapter-xaq4n6pc.js} +1 -1
  2. package/dist/{actions-9rrg0zxm.js → actions-p9hz5yat.js} +15 -15
  3. package/dist/{app-enysy1zd.js → app-g26wdcz6.js} +4 -4
  4. package/dist/{artifact-td4q4dch.js → artifact-3v5t9jtg.js} +1 -1
  5. package/dist/{assistant-zegamy48.js → assistant-wydfqkb3.js} +16 -16
  6. package/dist/{boot-reembed-r6shx5qx.js → boot-reembed-g5ckg9yb.js} +6 -6
  7. package/dist/{boot-reembed-zfxb937k.js → boot-reembed-se8zmdcp.js} +5 -5
  8. package/dist/{boot-scrub-logs-k5y0se6r.js → boot-scrub-logs-wfxqan61.js} +3 -3
  9. package/dist/{seed-pricing-vqxa06qf.js → boot-seeds-s3q01xj3.js} +10 -10
  10. package/dist/{claude-adapter-5yc7hxwv.js → claude-adapter-tv2x4f36.js} +7 -7
  11. package/dist/{claude-managed-adapter-ea2aga6w.js → claude-managed-adapter-41b2s7fd.js} +2 -2
  12. package/dist/{claude-sdk-session-j4ywn3xf.js → claude-sdk-session-t442qz6f.js} +8 -8
  13. package/dist/{cli-e781c6f9.js → cli-0xjkj1j7.js} +1 -0
  14. package/dist/{cli-58rgvkt4.js → cli-1hkrch3k.js} +2 -2
  15. package/dist/{cli-yscm6cv8.js → cli-20cj37vt.js} +1866 -9
  16. package/dist/{cli-h0wdadsx.js → cli-4198qc9v.js} +7 -3
  17. package/dist/{cli-awwphhcs.js → cli-47m6dkd3.js} +362 -126
  18. package/dist/{cli-wb2zvbfg.js → cli-53sz8gkz.js} +1 -1
  19. package/dist/{cli-005pze6s.js → cli-5d10vd2m.js} +1 -1
  20. package/dist/{cli-v53rbqp3.js → cli-65hjmev5.js} +57 -17
  21. package/dist/{cli-scwq51gn.js → cli-68w09sjw.js} +7 -1
  22. package/dist/{cli-s98skd5r.js → cli-7gtvvpbh.js} +4 -4
  23. package/dist/{cli-y3nxwx0t.js → cli-7s3syk22.js} +18 -11
  24. package/dist/{cli-74h6f2bp.js → cli-8qmme6vd.js} +2 -2
  25. package/dist/{cli-wsed24mk.js → cli-987hg0aa.js} +2 -2
  26. package/dist/{cli-9n76jszt.js → cli-9httdpyq.js} +1 -1
  27. package/dist/{cli-f3va018d.js → cli-aky8tctm.js} +26 -93
  28. package/dist/{cli-sgyqyyxe.js → cli-c07r9a2m.js} +2 -1
  29. package/dist/{cli-c41wjx0r.js → cli-fgqxwrpv.js} +291 -12
  30. package/dist/{cli-01vfmq8c.js → cli-ftfvggzx.js} +607 -164
  31. package/dist/{cli-ytxs7mn1.js → cli-g8djp6qe.js} +17 -0
  32. package/dist/{cli-avkd6mqv.js → cli-h967nb6r.js} +551 -0
  33. package/dist/{cli-r9ee9c4w.js → cli-j55n64b6.js} +23 -22
  34. package/dist/{cli-9p33cz9h.js → cli-j57qkrpq.js} +86 -28
  35. package/dist/{cli-tjmghmm5.js → cli-q67y4wzm.js} +6 -3
  36. package/dist/{cli-n6zq3kbd.js → cli-rj19hc34.js} +1 -1
  37. package/dist/{cli-jytrw2na.js → cli-rjehvrts.js} +4 -4
  38. package/dist/{cli-tpv97m71.js → cli-rqz8qbw7.js} +245 -75
  39. package/dist/{cli-p37sg0a9.js → cli-s8eet254.js} +1 -1
  40. package/dist/cli-t1ze64h3.js +811 -0
  41. package/dist/{cli-6jjzxv3b.js → cli-ws6rqycm.js} +3 -2
  42. package/dist/{cli-xf5b7y1f.js → cli-wscdvcqz.js} +13 -5
  43. package/dist/{cli-5j7bm4tb.js → cli-x24pshtq.js} +5 -5
  44. package/dist/{cli-nbp6m6gp.js → cli-xberxa1r.js} +51 -26
  45. package/dist/{cli-vx9p3qdt.js → cli-xhaeg2nc.js} +1 -1
  46. package/dist/cli.js +29 -15
  47. package/dist/{codex-adapter-gns3s6c3.js → codex-adapter-cnm8wk11.js} +5 -5
  48. package/dist/{codex-session-runner-gqnxys5w.js → codex-session-runner-gfg547zj.js} +5 -5
  49. package/dist/{commands-hxy2y2sp.js → commands-e3bn0p75.js} +3 -3
  50. package/dist/{db-w8ghhsgf.js → db-03v9kzr2.js} +17 -3
  51. package/dist/{dispatcher-cp1rknjz.js → dispatcher-nrmg8yht.js} +14 -14
  52. package/dist/{dsh-adapter-1dwg97zv.js → dsh-adapter-k03nfqa6.js} +2 -2
  53. package/dist/{handlers-egfhr351.js → handlers-aqcedbyb.js} +17 -17
  54. package/dist/{hook-x4kftwzt.js → hook-7vkskkre.js} +19 -11
  55. package/dist/{hook-fbxjqfjz.js → hook-ybvmrmhq.js} +4 -4
  56. package/dist/{http-c7jfx990.js → http-2eqa8t24.js} +432 -195
  57. package/dist/{index-4k9hct21.js → index-1yks3qcr.js} +14 -14
  58. package/dist/{index-jtvpgb8f.js → index-a3vn96n8.js} +30 -27
  59. package/dist/{index-dc2ek6bw.js → index-c6zsv1bd.js} +5 -5
  60. package/dist/{index-yy07ef2s.js → index-chgdkq36.js} +14 -14
  61. package/dist/{index-r6bty6tb.js → index-x71bvq0p.js} +15 -15
  62. package/dist/{keepalive-f7ekefyc.js → keepalive-vtkf5n99.js} +5 -5
  63. package/dist/{lead-h053syts.js → lead-h3sp357x.js} +26 -26
  64. package/dist/{maintenance-a3w6zs54.js → maintenance-jzeqtk3q.js} +6 -6
  65. package/dist/{oauth-refresh-sweep-vra1dhkd.js → oauth-refresh-sweep-qp3dzmdp.js} +4 -4
  66. package/dist/{onboard-mfgzeqxs.js → onboard-tmsp0qbz.js} +278 -81
  67. package/dist/{opencode-adapter-z64xjzec.js → opencode-adapter-x6x2da1z.js} +3 -3
  68. package/dist/{otel-impl-2ez6xk57.js → otel-impl-9mz73q65.js} +416 -301
  69. package/dist/{pi-mono-adapter-2b7mw16r.js → pi-mono-adapter-m6dvv0dz.js} +2 -2
  70. package/dist/{pricing-refresh-6sw65tck.js → pricing-refresh-v1cbr9te.js} +5 -5
  71. package/dist/{rbac-roles-nm6924sy.js → rbac-roles-c9wqz83x.js} +4 -4
  72. package/dist/{render-v2-f026j6c4.js → render-v2-gryrh9h8.js} +4 -4
  73. package/dist/{setup-w606fr1q.js → setup-q9720bt6.js} +2 -2
  74. package/dist/{virtual-modules-8qheft18.js → virtual-modules-3htpgn8x.js} +1 -1
  75. package/dist/{work-objects-ezmggdne.js → work-objects-b76x7deb.js} +17 -17
  76. package/dist/{worker-1mbyv29k.js → worker-6vrxbv41.js} +26 -26
  77. package/openapi.json +1626 -629
  78. package/package.json +2 -1
  79. package/src/apps/catalog.generated.json +229 -0
  80. package/src/apps/page-validator.ts +71 -16
  81. package/src/apps/row-store.ts +66 -11
  82. package/src/apps/sync.ts +59 -17
  83. package/src/artifact-sdk/browser-sdk.ts +436 -2
  84. package/src/be/approval-sweeps.ts +109 -0
  85. package/src/be/boot-seeds.ts +40 -0
  86. package/src/be/chunking.ts +1 -1
  87. package/src/be/db/agents.ts +20 -0
  88. package/src/be/db/approvals.ts +78 -0
  89. package/src/be/db/tasks/read.ts +197 -39
  90. package/src/be/db/tasks/write.ts +41 -9
  91. package/src/be/db-client.ts +107 -48
  92. package/src/be/db.ts +219 -76
  93. package/src/be/events.ts +33 -1
  94. package/src/be/memory/graph-expansion.ts +10 -3
  95. package/src/be/memory/providers/sqlite-store.ts +55 -13
  96. package/src/be/memory/raters/retrieval.ts +5 -2
  97. package/src/be/memory/reranker.ts +26 -2
  98. package/src/be/memory/types.ts +2 -0
  99. package/src/be/migrations/166_approval_request_auto_cancellation.sql +9 -0
  100. package/src/be/migrations/171_memory_retrieval_relevance.sql +8 -0
  101. package/src/be/migrations/175_workflow_runs_list_index.sql +11 -0
  102. package/src/be/migrations/177_events_event_agent_created_index.sql +11 -0
  103. package/src/be/models-catalog.ts +2 -0
  104. package/src/be/modelsdev-cache.json +1857 -0
  105. package/src/be/modelsdev-cache.ts +14 -1
  106. package/src/be/pricing-normalize.ts +2 -0
  107. package/src/be/scripts/typecheck.ts +1 -0
  108. package/src/be/seed-pricing.ts +3 -1
  109. package/src/be/seed-skills/bundled-files.generated.json +1 -1
  110. package/src/be/steering.ts +53 -3
  111. package/src/be/swarm-config-guard.ts +3 -0
  112. package/src/be/task-citations.ts +29 -0
  113. package/src/cli.tsx +14 -1
  114. package/src/commands/onboard/compose-generator.ts +8 -3
  115. package/src/commands/onboard/container-engine.ts +220 -0
  116. package/src/commands/onboard/manifest.ts +1 -0
  117. package/src/commands/onboard/steps/health-check.tsx +4 -1
  118. package/src/commands/onboard/steps/prereq-check.tsx +50 -54
  119. package/src/commands/onboard/steps/start.tsx +31 -16
  120. package/src/commands/onboard/types.ts +7 -2
  121. package/src/commands/onboard.tsx +39 -4
  122. package/src/commands/profile-sync.ts +79 -26
  123. package/src/commands/runner.ts +8 -1
  124. package/src/extensions/catalog.generated.json +17 -0
  125. package/src/extensions/contract-types.generated.ts +1 -1
  126. package/src/heartbeat/heartbeat.ts +263 -103
  127. package/src/hooks/hook.ts +21 -12
  128. package/src/http/agents.ts +35 -0
  129. package/src/http/approval-requests.ts +136 -64
  130. package/src/http/apps.ts +14 -3
  131. package/src/http/config.ts +15 -1
  132. package/src/http/events.ts +62 -2
  133. package/src/http/index.ts +6 -4
  134. package/src/http/memory.ts +21 -7
  135. package/src/http/schedules.ts +1 -1
  136. package/src/http/session-data.ts +9 -4
  137. package/src/http/sessions.ts +25 -18
  138. package/src/http/stats.ts +19 -7
  139. package/src/http/status.ts +11 -1
  140. package/src/http/tasks.ts +40 -8
  141. package/src/http/webhooks.ts +67 -46
  142. package/src/http/workflows.ts +15 -20
  143. package/src/linear/outbound.ts +37 -15
  144. package/src/otel-impl.ts +151 -1
  145. package/src/otel.ts +14 -3
  146. package/src/prompts/memories.ts +19 -2
  147. package/src/providers/claude-managed-models.ts +8 -0
  148. package/src/providers/modelsdev-reasoning.json +551 -0
  149. package/src/rbac/legacy-policy.ts +16 -0
  150. package/src/rbac/permissions.ts +4 -0
  151. package/src/scheduler/schedule-task.ts +3 -0
  152. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  153. package/src/scripts-runtime/swarm-sdk.ts +9 -0
  154. package/src/scripts-runtime/types/stdlib.d.ts +1 -0
  155. package/src/scripts-runtime/types/swarm-extension.d.ts +1 -0
  156. package/src/scripts-runtime/types/swarm-sdk.d.ts +1 -0
  157. package/src/server.ts +12 -7
  158. package/src/slack/app.ts +35 -4
  159. package/src/slack/connection-state.ts +17 -0
  160. package/src/tasks/context-key.ts +1 -0
  161. package/src/tasks/worker-follow-up.ts +6 -2
  162. package/src/tests/agent-task-activity.test.ts +74 -0
  163. package/src/tests/agents-list-model-display.test.ts +6 -1
  164. package/src/tests/approval-auto-cancellation-migration.test.ts +104 -0
  165. package/src/tests/approval-requests.test.ts +395 -0
  166. package/src/tests/approval-sweeps-config.test.ts +47 -0
  167. package/src/tests/approval-sweeps.test.ts +508 -0
  168. package/src/tests/apps-kibo-blocks.test.ts +77 -0
  169. package/src/tests/apps-spike.test.ts +56 -1
  170. package/src/tests/apps-sync-engine.test.ts +20 -9
  171. package/src/tests/boot-seeds.test.ts +170 -0
  172. package/src/tests/cancel-approval-request-tool.test.ts +115 -0
  173. package/src/tests/claude-managed-adapter.test.ts +6 -0
  174. package/src/tests/config-key-filter.test.ts +80 -0
  175. package/src/tests/context-window.test.ts +1 -0
  176. package/src/tests/db-client-busy-retry.test.ts +96 -0
  177. package/src/tests/db-client.test.ts +60 -1
  178. package/src/tests/db-task-read-contract.test.ts +10 -0
  179. package/src/tests/db-task-write-contract.test.ts +9 -0
  180. package/src/tests/docs-index-footprint.test.js +3 -3
  181. package/src/tests/events-http.test.ts +103 -0
  182. package/src/tests/extensions-catalog.test.ts +22 -17
  183. package/src/tests/extensions-pre-task.test.ts +107 -0
  184. package/src/tests/fixtures/kibo-blocks-definition.json.txt +105 -0
  185. package/src/tests/follow-up-config-inheritance.test.ts +260 -0
  186. package/src/tests/heartbeat-orphan-resume.test.ts +84 -0
  187. package/src/tests/heartbeat-reboot-live.test.ts +95 -0
  188. package/src/tests/heartbeat-stall-cas.test.ts +80 -0
  189. package/src/tests/heartbeat.test.ts +99 -3
  190. package/src/tests/hook-mcp-config.test.ts +29 -0
  191. package/src/tests/http-api-integration.test.ts +16 -2
  192. package/src/tests/kv-storage.test.ts +62 -0
  193. package/src/tests/linear-outbound-sync.test.ts +120 -5
  194. package/src/tests/list-endpoint-slimming.test.ts +91 -0
  195. package/src/tests/memory-graph-organic-route.test.ts +155 -0
  196. package/src/tests/memory-http-recall-gating.test.ts +14 -4
  197. package/src/tests/memory-hybrid.test.ts +161 -1
  198. package/src/tests/memory-prompt-recall-crowdout.test.ts +297 -0
  199. package/src/tests/memory.test.ts +22 -0
  200. package/src/tests/onboard-container-engine.test.ts +240 -0
  201. package/src/tests/otel-event-loop-and-logs.test.ts +99 -0
  202. package/src/tests/pricing-routes.test.ts +14 -0
  203. package/src/tests/profile-sync-warning.test.ts +8 -4
  204. package/src/tests/promote-draft-task-route.test.ts +19 -0
  205. package/src/tests/provider-registration.test.ts +296 -42
  206. package/src/tests/rbac-engine.test.ts +30 -0
  207. package/src/tests/reasoning-effort.test.ts +8 -0
  208. package/src/tests/script-connections.test.ts +7 -0
  209. package/src/tests/sdk-allowlist.test.ts +39 -0
  210. package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
  211. package/src/tests/sessions-detail-batch.test.ts +250 -0
  212. package/src/tests/sessions.test.ts +118 -0
  213. package/src/tests/slack-config.test.ts +22 -0
  214. package/src/tests/sonnet-5-5-registry.test.ts +44 -0
  215. package/src/tests/status.test.ts +36 -1
  216. package/src/tests/swarm-diff.test.ts +316 -10
  217. package/src/tests/task-completion-idempotency.test.ts +2 -1
  218. package/src/tests/tasks-requested-by-filter.test.ts +3 -1
  219. package/src/tests/tool-annotations.test.ts +1 -1
  220. package/src/tests/ui-list-slim-projections.test.ts +158 -0
  221. package/src/tests/webhooks-task-blocked.test.ts +127 -0
  222. package/src/tests/workflow-approval-recovery.test.ts +203 -0
  223. package/src/tests/workflow-hitl-routing.test.ts +22 -1
  224. package/src/tests/workflow-hitl-slack-card.test.ts +52 -0
  225. package/src/tests/workflow-http-v2.test.ts +99 -8
  226. package/src/tests/workflow-retry-validation.test.ts +56 -0
  227. package/src/tests/workflow-tla-races.test.ts +389 -0
  228. package/src/tests/workflow-validation-port-routing.test.ts +113 -1
  229. package/src/tools/cancel-approval-request.ts +57 -0
  230. package/src/tools/memory-search.ts +1 -0
  231. package/src/tools/request-human-input.ts +4 -2
  232. package/src/tools/send-task.ts +4 -0
  233. package/src/tools/templates.ts +19 -0
  234. package/src/tools/tool-config.ts +2 -1
  235. package/src/tools/workflows/list-workflow-runs.ts +1 -0
  236. package/src/types.ts +38 -0
  237. package/src/utils/context-window.ts +1 -0
  238. package/src/workflows/approval-cancel.ts +152 -0
  239. package/src/workflows/approval-notifications.ts +84 -0
  240. package/src/workflows/checkpoint.ts +10 -5
  241. package/src/workflows/definition.ts +21 -0
  242. package/src/workflows/engine.ts +152 -21
  243. package/src/workflows/executors/human-in-the-loop.ts +1 -1
  244. package/src/workflows/recovery.ts +100 -45
  245. package/src/workflows/resume.ts +49 -18
  246. package/src/workflows/retry-poller.ts +71 -21
  247. package/templates/extensions/README.md +1 -0
  248. package/templates/extensions/github-sender-allowlist/README.md +18 -0
  249. package/templates/extensions/github-sender-allowlist/hooks.ts +55 -0
  250. package/templates/extensions/github-sender-allowlist/manifest.json +8 -0
  251. package/templates/skills/apps/content.md +20 -1
  252. package/templates/skills/code-quality/content.md +26 -1
  253. package/templates/skills/pages/content.md +16 -4
  254. package/templates/skills/pages/files/examples/multiplayer-board.html +3 -12
  255. package/dist/cli-2daksqps.js +0 -378
  256. package/dist/rbac-roles-3gxmnb20.js +0 -37
package/src/server.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import pkg from "../package.json";
3
+ import { ensurePricingSeeded, ensureRbacSeeded } from "./be/boot-seeds";
3
4
  import { initDb } from "./be/db";
4
5
  import { startPricingRefreshLoop } from "./be/pricing-refresh";
5
- import { ensureRbacSeedsSynced } from "./be/rbac-roles";
6
- import { seedPricingFromModelsDev } from "./be/seed-pricing";
7
6
  import { isSteeringEnabled } from "./be/steering";
8
7
  import { registerGithubTaskReactions } from "./github/task-reactions";
9
8
  import { loadGlobalConfigsIntoEnv } from "./http/core";
@@ -20,6 +19,7 @@ import { registerAppQueryTool } from "./tools/app-query";
20
19
  import { registerAppRollbackTool } from "./tools/app-rollback";
21
20
  import { registerAppSyncTool } from "./tools/app-sync";
22
21
  import { registerAppUpsertTool } from "./tools/app-upsert";
22
+ import { registerCancelApprovalRequestTool } from "./tools/cancel-approval-request";
23
23
  import { registerCancelTaskTool } from "./tools/cancel-task";
24
24
  import { registerContextDiffTool } from "./tools/context-diff";
25
25
  import { registerContextHistoryTool } from "./tools/context-history";
@@ -307,14 +307,18 @@ export async function createServer(
307
307
  initDb(process.env.DATABASE_PATH);
308
308
 
309
309
  // Phase 2: project the vendored models.dev snapshot into the pricing table.
310
- // Idempotent (INSERT OR IGNORE keyed on PK with effective_from=0); safe to
311
- // call on every boot. See src/be/seed-pricing.ts for the projection logic
312
- // and the manual-override constants for runtime-fee / ACU pricing.
313
- seedPricingFromModelsDev();
310
+ // Idempotent (INSERT OR IGNORE keyed on PK with effective_from=0). This runs
311
+ // once per database handle, not once per MCP session: the seed is a
312
+ // synchronous 8.6 MB parse plus a write transaction, and createServer() runs
313
+ // on every `POST /mcp` session init. The HTTP boot path seeds first, so
314
+ // sessions normally skip it; standalone callers (stdio, tests) seed here.
315
+ // See src/be/seed-pricing.ts for the projection logic and the
316
+ // manual-override constants for runtime-fee / ACU pricing.
317
+ ensurePricingSeeded();
314
318
  startPricingRefreshLoop();
315
319
 
316
320
  try {
317
- ensureRbacSeedsSynced();
321
+ ensureRbacSeeded();
318
322
  } catch (err) {
319
323
  console.error("[startup] Failed to sync RBAC seed rows:", err);
320
324
  // RBAC flag-on must fail closed; flag-off deployments should not be bricked
@@ -500,6 +504,7 @@ export async function createServer(
500
504
  registerRetryWorkflowRunTool(server);
501
505
  registerCancelWorkflowRunTool(server);
502
506
  registerRequestHumanInputTool(server);
507
+ registerCancelApprovalRequestTool(server);
503
508
  }
504
509
 
505
510
  // Skills capability - installable skill packages (create, search, install, publish)
package/src/slack/app.ts CHANGED
@@ -1,12 +1,31 @@
1
- import { App, LogLevel } from "@slack/bolt";
1
+ import { App, LogLevel, SocketModeReceiver } from "@slack/bolt";
2
2
  import { emitBuiltInIntegrationConnectedOnce, ensureSlackRenderV2Activation } from "../be/db";
3
3
  import { getSlackConfiguration } from "./config";
4
+ import { type SlackConnectionState, setSlackConnectionState } from "./connection-state";
4
5
  import { getSlackSocketModeBlockReason, SLACK_DEV_SOCKET_MODE_OPT_IN } from "./socket-mode-guard";
5
6
  import { startTaskWatcher, stopTaskWatcher } from "./watcher";
6
7
 
7
8
  let app: App | null = null;
9
+ let receiver: SocketModeReceiver | null = null;
8
10
  let initialized = false;
9
11
 
12
+ const SOCKET_EVENT_STATES: Record<string, SlackConnectionState> = {
13
+ connecting: "connecting",
14
+ reconnecting: "connecting",
15
+ connected: "connected",
16
+ disconnecting: "disconnected",
17
+ disconnected: "disconnected",
18
+ };
19
+
20
+ function trackConnectionState(owner: SocketModeReceiver): void {
21
+ for (const [event, state] of Object.entries(SOCKET_EVENT_STATES)) {
22
+ owner.client.on(event, () => {
23
+ // Ignore late events from a receiver that stopSlackApp() already replaced.
24
+ if (receiver === owner) setSlackConnectionState(state);
25
+ });
26
+ }
27
+ }
28
+
10
29
  export function getSlackApp(): App | null {
11
30
  return app;
12
31
  }
@@ -59,12 +78,22 @@ export async function initSlackApp(): Promise<App | null> {
59
78
 
60
79
  // SLACK_API_URL points Bolt (Web API and apps.connections.open) at a mock Slack server for e2e tests.
61
80
  const slackApiUrl = process.env.SLACK_API_URL;
81
+ const clientOptions = slackApiUrl ? { slackApiUrl } : undefined;
82
+ const logLevel = process.env.NODE_ENV === "development" ? LogLevel.DEBUG : LogLevel.INFO;
83
+ // Build the receiver ourselves so its SocketModeClient lifecycle events can
84
+ // feed /status. Bolt would otherwise forward clientOptions via installerOptions.
85
+ receiver = new SocketModeReceiver({
86
+ appToken,
87
+ logLevel,
88
+ installerOptions: clientOptions ? { clientOptions } : {},
89
+ });
90
+ trackConnectionState(receiver);
62
91
  app = new App({
63
92
  token: botToken,
64
- appToken: appToken,
93
+ receiver,
65
94
  socketMode: true,
66
- logLevel: process.env.NODE_ENV === "development" ? LogLevel.DEBUG : LogLevel.INFO,
67
- ...(slackApiUrl ? { clientOptions: { slackApiUrl } } : {}),
95
+ logLevel,
96
+ ...(clientOptions ? { clientOptions } : {}),
68
97
  });
69
98
 
70
99
  // Failed validation must remain retryable without requiring stopSlackApp().
@@ -117,5 +146,7 @@ export async function stopSlackApp(): Promise<void> {
117
146
  app = null;
118
147
  console.log("[Slack] Bot disconnected");
119
148
  }
149
+ receiver = null;
150
+ setSlackConnectionState("disconnected");
120
151
  initialized = false;
121
152
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Live Socket Mode connection state, fed by the SocketModeClient lifecycle
3
+ * events in `app.ts` and read by `/status` to decide whether Slack is
4
+ * `verified`. Kept in its own module so status code can read it without
5
+ * importing the Bolt app (several tests mock `./app`).
6
+ */
7
+ export type SlackConnectionState = "disconnected" | "connecting" | "connected";
8
+
9
+ let state: SlackConnectionState = "disconnected";
10
+
11
+ export function getSlackConnectionState(): SlackConnectionState {
12
+ return state;
13
+ }
14
+
15
+ export function setSlackConnectionState(next: SlackConnectionState): void {
16
+ state = next;
17
+ }
@@ -22,6 +22,7 @@
22
22
  * task:workflow:{workflowRunId}
23
23
  * task:agent:{agentId} (KV-only: per-agent scratchpad)
24
24
  * task:page:{pageId} (KV-only: per-page state, proxy-enforced)
25
+ * task:ui:{kind}:{ref}:{sessionNonce} (UI-built by the contextual session panel; no server parser)
25
26
  *
26
27
  * Rules:
27
28
  * - Fixed prefix tokens (`task`, family, sub-family, kind) are always lowercase.
@@ -524,8 +524,9 @@ export async function createResumeFollowUp(args: {
524
524
 
525
525
  // Identity-shaped fields (dir, VCS provider/repo/number/url/etc.,
526
526
  // outputSchema, slack channel/thread/user, agentmail, mention, contextKey,
527
- // requestedByUserId, followUpConfig) are auto-inherited from the parent by
528
- // `createTaskExtended`'s parentTaskId block (see src/be/db.ts). `model` is
527
+ // requestedByUserId) are auto-inherited from the parent by
528
+ // `createTaskExtended`'s parentTaskId block (see src/be/db.ts);
529
+ // followUpConfig is opt-in and set below. `model` is
529
530
  // deliberately excluded there so the resume task resolves to the claiming
530
531
  // agent's own provider/model — never the parent's concrete model string.
531
532
  // We only override what's SPECIFIC to the resume task here.
@@ -564,6 +565,9 @@ export async function createResumeFollowUp(args: {
564
565
  tags,
565
566
  priority,
566
567
  parentTaskId: parent.id,
568
+ // Same work, new session: the creator's onCompleted/onFailed must still
569
+ // fire when this resume finishes.
570
+ inheritParentFollowUpConfig: true,
567
571
  routingAffinity,
568
572
  });
569
573
 
@@ -0,0 +1,74 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
+ import { unlink } from "node:fs/promises";
3
+ import { createServer as createHttpServer, type Server } from "node:http";
4
+ import { closeDb, createAgent, createTaskExtended, getDbClient, initDb } from "../be/db";
5
+ import { handleAgentsRest } from "../http/agents";
6
+ import { listenOnFreePort } from "./test-net";
7
+
8
+ const TEST_DB_PATH = "./test-agent-task-activity.sqlite";
9
+
10
+ async function removeDbFiles(path: string): Promise<void> {
11
+ for (const suffix of ["", "-wal", "-shm"]) {
12
+ try {
13
+ await unlink(path + suffix);
14
+ } catch (error) {
15
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
16
+ }
17
+ }
18
+ }
19
+
20
+ let server: Server;
21
+ let baseUrl = "";
22
+
23
+ beforeAll(async () => {
24
+ await removeDbFiles(TEST_DB_PATH);
25
+ initDb(TEST_DB_PATH);
26
+ server = createHttpServer(async (req, res) => {
27
+ const url = new URL(req.url ?? "/", "http://localhost");
28
+ const segments = url.pathname.split("/").filter(Boolean);
29
+ if (await handleAgentsRest(req, res, segments, url.searchParams, undefined)) return;
30
+ res.writeHead(404).end();
31
+ });
32
+ baseUrl = `http://localhost:${await listenOnFreePort(server)}`;
33
+ });
34
+
35
+ afterAll(async () => {
36
+ await new Promise<void>((resolve) => server.close(() => resolve()));
37
+ closeDb();
38
+ await removeDbFiles(TEST_DB_PATH);
39
+ });
40
+
41
+ describe("GET /api/agents/:id/task-activity", () => {
42
+ test("counts tasks per UTC day inside the window, for that agent only", async () => {
43
+ const agent = await createAgent({ name: "a", isLead: false, status: "idle", capabilities: [] });
44
+ const other = await createAgent({ name: "b", isLead: false, status: "idle", capabilities: [] });
45
+ const day = (offset: number) =>
46
+ new Date(Date.now() - offset * 86_400_000).toISOString().slice(0, 10);
47
+ const stamp = async (agentId: string, date: string) => {
48
+ const task = await createTaskExtended("t", { agentId });
49
+ await getDbClient().run("UPDATE agent_tasks SET createdAt = ? WHERE id = ?", [
50
+ `${date}T12:00:00.000Z`,
51
+ task.id,
52
+ ]);
53
+ };
54
+ await stamp(agent.id, day(1));
55
+ await stamp(agent.id, day(1));
56
+ await stamp(agent.id, day(3));
57
+ await stamp(agent.id, day(40));
58
+ await stamp(other.id, day(1));
59
+
60
+ const res = await fetch(`${baseUrl}/api/agents/${agent.id}/task-activity?days=30`);
61
+ expect(res.status).toBe(200);
62
+ expect(await res.json()).toEqual({
63
+ days: [
64
+ { date: day(3), count: 1 },
65
+ { date: day(1), count: 2 },
66
+ ],
67
+ });
68
+ });
69
+
70
+ test("404s for an unknown agent", async () => {
71
+ const res = await fetch(`${baseUrl}/api/agents/nope/task-activity`);
72
+ expect(res.status).toBe(404);
73
+ });
74
+ });
@@ -113,6 +113,11 @@ describe("agents list model display", () => {
113
113
  provider: "Anthropic",
114
114
  providerId: "anthropic",
115
115
  });
116
+ expect(getAgentModelPresentation("claude-sonnet-5-5")).toMatchObject({
117
+ label: "Claude Sonnet 5.5",
118
+ provider: "Anthropic",
119
+ providerId: "anthropic",
120
+ });
116
121
  expect(getAgentModelPresentation("claude-opus-5")).toMatchObject({
117
122
  label: "Claude Opus 5",
118
123
  provider: "Anthropic",
@@ -129,7 +134,7 @@ describe("agents list model display", () => {
129
134
  providerId: "anthropic",
130
135
  });
131
136
  expect(getAgentModelPresentation("sonnet")).toMatchObject({
132
- label: "Claude Sonnet 5",
137
+ label: "Claude Sonnet 5.5",
133
138
  provider: "Anthropic",
134
139
  providerId: "anthropic",
135
140
  });
@@ -0,0 +1,104 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { afterEach, describe, expect, test } from "bun:test";
3
+ import { runMigrations } from "../be/migrations/runner";
4
+
5
+ const DB_PATH = "./test-approval-auto-cancellation-migration.sqlite";
6
+
7
+ async function removeDb(): Promise<void> {
8
+ for (const suffix of ["", "-wal", "-shm"]) {
9
+ try {
10
+ await Bun.file(DB_PATH + suffix).delete();
11
+ } catch (error) {
12
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
13
+ }
14
+ }
15
+ }
16
+
17
+ afterEach(removeDb);
18
+
19
+ type Row = {
20
+ id: string;
21
+ status: string;
22
+ resolvedBy: string | null;
23
+ resolvedAt: string | null;
24
+ resolutionReason: string | null;
25
+ };
26
+
27
+ function readRows(db: Database): Row[] {
28
+ return db
29
+ .query<Row, []>(
30
+ "SELECT id, status, resolvedBy, resolvedAt, resolutionReason FROM approval_requests ORDER BY id",
31
+ )
32
+ .all();
33
+ }
34
+
35
+ describe("migration 166 approval auto-cancellation index", () => {
36
+ test("adds the pending-created index and leaves the schema and rows intact", async () => {
37
+ await removeDb();
38
+ const db = new Database(DB_PATH, { create: true });
39
+ try {
40
+ runMigrations(db);
41
+ // Roll the database back to the state before migration 166.
42
+ db.run("DROP INDEX idx_approval_requests_pending_created");
43
+ db.run("DELETE FROM _migrations WHERE version = 166");
44
+ db.run(`INSERT INTO approval_requests
45
+ (id, title, questions, approvers, status, resolvedBy, resolvedAt, resolutionReason,
46
+ expiresAt, createdAt, updatedAt)
47
+ VALUES
48
+ ('a-pending', 'Pending', '[]', '{}', 'pending', NULL, NULL, NULL,
49
+ NULL, '2026-01-01', '2026-01-01'),
50
+ ('b-cancelled', 'Cancelled', '[]', '{}', 'cancelled', NULL, '2026-01-03', 'run cancelled',
51
+ NULL, '2026-01-01', '2026-01-03'),
52
+ ('c-timeout', 'Timeout', '[]', '{}', 'timeout', NULL, '2026-01-04', NULL,
53
+ '2026-01-02', '2026-01-01', '2026-01-04')`);
54
+ const before = readRows(db);
55
+
56
+ runMigrations(db);
57
+
58
+ expect(readRows(db)).toEqual(before);
59
+ const indexes = db
60
+ .query<{ name: string }, []>(
61
+ "SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = 'approval_requests'",
62
+ )
63
+ .all()
64
+ .map((row) => row.name);
65
+ expect(indexes).toEqual(
66
+ expect.arrayContaining([
67
+ "idx_approval_requests_pending_created",
68
+ "idx_approval_requests_status",
69
+ "idx_approval_requests_created",
70
+ "idx_approval_requests_workflow",
71
+ "idx_approval_requests_task",
72
+ "idx_approval_requests_expires",
73
+ ]),
74
+ );
75
+
76
+ const tableSql = db
77
+ .query<{ sql: string }, []>(
78
+ "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'approval_requests'",
79
+ )
80
+ .get()?.sql;
81
+ expect(tableSql).toContain("'cancelled'");
82
+ expect(tableSql).toContain("'timeout'");
83
+
84
+ const insert = (id: string, status: string) =>
85
+ db.run(
86
+ `INSERT INTO approval_requests (id, title, questions, approvers, status)
87
+ VALUES (?, 't', '[]', '{}', ?)`,
88
+ [id, status],
89
+ );
90
+ expect(() => insert("d-cancelled", "cancelled")).not.toThrow();
91
+ expect(() => insert("e-timeout", "timeout")).not.toThrow();
92
+ expect(() => insert("f-expired", "expired")).toThrow(/CHECK/);
93
+
94
+ // Assert 166 was re-recorded, not that it is the tail: later migrations
95
+ // land on top of it.
96
+ const reapplied = db
97
+ .query<{ version: number }, []>("SELECT version FROM _migrations WHERE version = 166")
98
+ .get();
99
+ expect(reapplied?.version).toBe(166);
100
+ } finally {
101
+ db.close();
102
+ }
103
+ });
104
+ });