@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
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  REBOOT_RETRY_PIN_TAG,
3
3
  agentsWithLiveRuntime,
4
+ cancelApprovalRequestAndRun,
4
5
  countActiveRuntimeInstancesForAgent,
6
+ createApprovalFollowUpTask,
5
7
  createPoolStarvationDecisionTask,
6
8
  createRerouteDecisionTask,
7
9
  createResumeFollowUp,
@@ -15,7 +17,7 @@ import {
15
17
  recoverIncompleteRuns,
16
18
  repointTrackerSyncBySwarmId,
17
19
  resolveLeadOnlyRecoveryAssignment
18
- } from "./cli-01vfmq8c.js";
20
+ } from "./cli-ftfvggzx.js";
19
21
  import {
20
22
  isMultiRuntimeEnabled
21
23
  } from "./cli-re8z5p8p.js";
@@ -24,6 +26,7 @@ import {
24
26
  assignUnassignedTaskPending,
25
27
  backfillSupersedeTaskResumeTaskId,
26
28
  buildRoutingAffinityFromAgent,
29
+ cascadeFailDependents,
27
30
  cleanupStaleSessions,
28
31
  createLogEntry,
29
32
  createTaskExtended,
@@ -34,15 +37,19 @@ import {
34
37
  getActiveTaskCount,
35
38
  getAllAgents,
36
39
  getDbClient,
40
+ getDependentTasks,
41
+ getExpiredPendingApprovals,
37
42
  getIdleWorkersWithCapacity,
38
43
  getLeadAgent,
39
44
  getPendingSteeringForTask,
40
45
  getRecentCompletedCount,
41
46
  getRecentFailedCount,
42
47
  getRecentFailedTasks,
48
+ getStaleApprovalRequests,
43
49
  getStalePinnedResumes,
44
50
  getStaleUnassignedAffinityTasks,
45
51
  getStalledInProgressTasks,
52
+ getSupersededTasksWithoutResume,
46
53
  getTaskById,
47
54
  getTaskStats,
48
55
  getTasksByStatus,
@@ -59,9 +66,10 @@ import {
59
66
  releaseStaleOfferedTasksForOfflineAgents,
60
67
  releaseStaleProcessingInbox,
61
68
  releaseStaleReviewingTasks,
69
+ resolveApprovalRequest,
62
70
  supersedeTask,
63
71
  updateAgentStatus
64
- } from "./cli-awwphhcs.js";
72
+ } from "./cli-47m6dkd3.js";
65
73
  import {
66
74
  init_registry,
67
75
  init_resolver,
@@ -73,6 +81,73 @@ import {
73
81
  scrubSecrets
74
82
  } from "./cli-r2ap2czm.js";
75
83
 
84
+ // src/be/approval-sweeps.ts
85
+ init_db();
86
+ var DEFAULT_APPROVAL_REQUEST_AUTO_CANCELLATION_DAYS = 7;
87
+ function approvalRequestAutoCancellationDays() {
88
+ const raw = process.env.APPROVAL_REQUEST_AUTO_CANCELLATION_DAYS;
89
+ if (raw != null && /^\d+$/.test(raw.trim())) {
90
+ return Number.parseInt(raw.trim(), 10);
91
+ }
92
+ return DEFAULT_APPROVAL_REQUEST_AUTO_CANCELLATION_DAYS;
93
+ }
94
+ async function autoCancelStaleApprovalRequests(now = new Date) {
95
+ const days = approvalRequestAutoCancellationDays();
96
+ if (days === 0)
97
+ return { cancelled: [], runsCancelled: [] };
98
+ const cutoff = new Date(now.getTime() - days * 86400000).toISOString();
99
+ const stale = await getStaleApprovalRequests({ cutoff });
100
+ const reason = `Auto-cancelled by the approval sweep after ${days} days with no response`;
101
+ const cancelled = [];
102
+ const runsCancelled = [];
103
+ for (const row of stale) {
104
+ try {
105
+ const result = await cancelApprovalRequestAndRun(row.id, {
106
+ reason,
107
+ resolvedBy: null,
108
+ slackReason: "auto-cancelled with no response"
109
+ });
110
+ if (!result || "timedOut" in result)
111
+ continue;
112
+ cancelled.push(result.request);
113
+ if (result.runCancelled && result.request.workflowRunId) {
114
+ runsCancelled.push(result.request.workflowRunId);
115
+ }
116
+ } catch (err) {
117
+ console.error(`[approval-sweep] could not cancel approval request ${row.id}: ${err instanceof Error ? err.message : String(err)}`);
118
+ }
119
+ }
120
+ if (cancelled.length > 0) {
121
+ console.log(`[approval-sweep] auto-cancelled ${cancelled.length} stale approval requests and ${runsCancelled.length} workflow runs`);
122
+ }
123
+ return { cancelled, runsCancelled };
124
+ }
125
+ async function timeoutExpiredApprovalRequests(now = new Date) {
126
+ const expired = await getExpiredPendingApprovals({ now: now.toISOString() });
127
+ const timedOut = [];
128
+ for (const row of expired) {
129
+ try {
130
+ const updated = await getDbClient().transaction(async () => {
131
+ const request = await resolveApprovalRequest(row.id, {
132
+ status: "timeout",
133
+ resolutionReason: `Timed out by the approval sweep: no answer before ${row.expiresAt}`
134
+ });
135
+ if (request)
136
+ await createApprovalFollowUpTask(request, "hitl.timeout");
137
+ return request;
138
+ });
139
+ if (updated)
140
+ timedOut.push(updated);
141
+ } catch (err) {
142
+ console.error(`[approval-sweep] could not time out approval request ${row.id}: ${err instanceof Error ? err.message : String(err)}`);
143
+ }
144
+ }
145
+ if (timedOut.length > 0) {
146
+ console.log(`[approval-sweep] timed out ${timedOut.length} expired approval requests`);
147
+ }
148
+ return { timedOut };
149
+ }
150
+
76
151
  // src/heartbeat/heartbeat.ts
77
152
  init_db();
78
153
  init_steering();
@@ -260,12 +335,15 @@ async function codeLevelTriage() {
260
335
  workflowRuns: 0,
261
336
  staleRuntimes: 0,
262
337
  staleOfferedTasks: 0,
263
- abandonedDraftTasks: 0
338
+ abandonedDraftTasks: 0,
339
+ approvalAutoCancelled: 0,
340
+ approvalTimedOut: 0
264
341
  }
265
342
  };
266
343
  findings.staleCleanup.staleRuntimes = (await expireStaleRuntimeInstances()).expired;
267
344
  findings.staleCleanup.staleOfferedTasks = await releaseStaleOfferedTasksForOfflineAgents();
268
345
  await detectAndRemediateStalledTasks(findings);
346
+ await repairSupersededWithoutResume(findings);
269
347
  await checkWorkerHealth(findings);
270
348
  await autoAssignPoolTasks(findings);
271
349
  await cleanupStaleResources(findings);
@@ -361,6 +439,29 @@ async function detectAndRemediateStalledTasks(findings) {
361
439
  await remediateCrashedWorkerTask(findings, task, remediationOpts, proposed);
362
440
  }
363
441
  }
442
+ var ORPHAN_SUPERSEDE_MIN_AGE_MS = 60 * 1000;
443
+ var ORPHAN_SUPERSEDE_MAX_AGE_MS = 24 * 60 * 60 * 1000;
444
+ async function repairSupersededWithoutResume(findings) {
445
+ const now = Date.now();
446
+ const orphans = await getSupersededTasksWithoutResume(new Date(now - ORPHAN_SUPERSEDE_MIN_AGE_MS).toISOString(), new Date(now - ORPHAN_SUPERSEDE_MAX_AGE_MS).toISOString());
447
+ for (const task of orphans) {
448
+ if (!task.agentId)
449
+ continue;
450
+ if (getNextResumeGeneration(task) > maxResumeGenerations())
451
+ continue;
452
+ const resume = await createResumeFollowUp({ parentId: task.id, reason: "crash_recovery" });
453
+ if (resume.kind !== "created")
454
+ continue;
455
+ await backfillSupersedeTaskResumeTaskId(task.id, resume.task.id);
456
+ findings.autoResumedTasks.push({
457
+ taskId: task.id,
458
+ resumeTaskId: resume.task.id,
459
+ agentId: task.agentId,
460
+ reason: "repaired superseded task without resume"
461
+ });
462
+ console.log(`[Heartbeat] Created missing resume ${resume.task.id.slice(0, 8)} for superseded task ${task.id.slice(0, 8)}`);
463
+ }
464
+ }
364
465
  function isRemediationAction(value) {
365
466
  return typeof value === "string" && REMEDIATION_ACTIONS.has(value);
366
467
  }
@@ -393,7 +494,9 @@ async function remediateCrashedWorkerTask(findings, task, opts, decision) {
393
494
  if (!task.agentId)
394
495
  return;
395
496
  if (decision.action === "fail") {
396
- const failed = await failTask(task.id, decision.reason);
497
+ const failed = await failTask(task.id, decision.reason, {
498
+ expectedLastUpdatedAt: task.lastUpdatedAt
499
+ });
397
500
  if (failed) {
398
501
  findings.autoFailedTasks.push({
399
502
  taskId: task.id,
@@ -416,7 +519,8 @@ async function remediateCrashedWorkerTask(findings, task, opts, decision) {
416
519
  await beforeHeartbeatSupersedeForTests?.(task);
417
520
  const superseded = await supersedeTask(task.id, {
418
521
  reason: decision.reason,
419
- resumeTaskId: null
522
+ resumeTaskId: null,
523
+ expectedLastUpdatedAt: task.lastUpdatedAt
420
524
  });
421
525
  if (!superseded) {
422
526
  return;
@@ -510,94 +614,144 @@ async function runRebootSweep() {
510
614
  if (sessionLastSeen >= bootEpoch - BOOT_EPOCH_SKEW_MS) {
511
615
  continue;
512
616
  }
617
+ if (Date.now() - sessionLastSeen < stallThresholdStaleHeartbeatMin() * 60 * 1000) {
618
+ continue;
619
+ }
513
620
  }
514
621
  if (session)
515
622
  await deleteActiveSession(task.id);
516
- const failed = await failTask(task.id, reason);
623
+ const failed = await failTask(task.id, reason, { cascadeDependents: false });
517
624
  if (!failed)
518
625
  continue;
519
- if (await getActiveTaskCount(task.agentId) === 0) {
520
- await restoreAgentIdleAfterRemediation(task.agentId);
521
- }
522
- if (SKIP_AUTO_RESUME_TYPES.has(task.taskType ?? "")) {
523
- rebootAffectedTasks.push({ original: failed, retryTaskId: null });
524
- continue;
525
- }
526
626
  let retryTaskId = null;
527
- if (task.routingAffinityInvalid) {
528
- console.error(`[Heartbeat] Reboot retry suppressed for ${task.id.slice(0, 8)}: invalid routing affinity`);
529
- rebootAffectedTasks.push({ original: failed, retryTaskId: null });
530
- continue;
531
- }
532
- const existingRetry = await getDbClient().get(`SELECT id FROM agent_tasks
627
+ try {
628
+ if (await getActiveTaskCount(task.agentId) === 0) {
629
+ await restoreAgentIdleAfterRemediation(task.agentId);
630
+ }
631
+ if (SKIP_AUTO_RESUME_TYPES.has(task.taskType ?? "")) {
632
+ rebootAffectedTasks.push({ original: failed, retryTaskId: null });
633
+ continue;
634
+ }
635
+ if (task.routingAffinityInvalid) {
636
+ console.error(`[Heartbeat] Reboot retry suppressed for ${task.id.slice(0, 8)}: invalid routing affinity`);
637
+ rebootAffectedTasks.push({ original: failed, retryTaskId: null });
638
+ continue;
639
+ }
640
+ const existingRetry = await getDbClient().get(`SELECT id FROM agent_tasks
533
641
  WHERE parentTaskId = ?
534
642
  AND status NOT IN ('completed', 'failed', 'cancelled')
535
643
  LIMIT 1`, [task.id]);
536
- if (!existingRetry) {
537
- try {
538
- let preferredAgentId;
539
- let sourceCandidate = await getPinCandidateAgent(task.agentId);
540
- if (sourceCandidate) {
541
- const activeCount = await getActiveTaskCount(sourceCandidate.id);
542
- const maxTasks = sourceCandidate.maxTasks ?? 1;
543
- const hasCap = activeCount < maxTasks;
544
- if (hasCap) {
545
- preferredAgentId = sourceCandidate.id;
546
- } else {
547
- sourceCandidate = null;
548
- console.warn(`[Heartbeat] Reboot retry for task ${task.id.slice(0, 8)} NOT pinned: agent ${task.agentId.slice(0, 8)} at capacity (${activeCount}/${maxTasks}); falling back to affinity-gated pool`);
644
+ if (!existingRetry) {
645
+ try {
646
+ let preferredAgentId;
647
+ let sourceCandidate = await getPinCandidateAgent(task.agentId);
648
+ if (sourceCandidate) {
649
+ const activeCount = await getActiveTaskCount(sourceCandidate.id);
650
+ const maxTasks = sourceCandidate.maxTasks ?? 1;
651
+ const hasCap = activeCount < maxTasks;
652
+ if (hasCap) {
653
+ preferredAgentId = sourceCandidate.id;
654
+ } else {
655
+ sourceCandidate = null;
656
+ console.warn(`[Heartbeat] Reboot retry for task ${task.id.slice(0, 8)} NOT pinned: agent ${task.agentId.slice(0, 8)} at capacity (${activeCount}/${maxTasks}); falling back to affinity-gated pool`);
657
+ }
549
658
  }
550
- }
551
- const leadOnly = task.routingAffinity?.leadOnly === true;
552
- const authorization = await resolveLeadOnlyRecoveryAssignment(task, sourceCandidate);
553
- if (leadOnly)
554
- preferredAgentId = authorization.agentId;
555
- const tags = ["reboot-retry", "auto-generated"];
556
- if (preferredAgentId !== undefined)
557
- tags.push(REBOOT_RETRY_PIN_TAG);
558
- const sourceAffinity = await buildRoutingAffinityFromAgent(task.agentId) ?? undefined;
559
- const routingAffinity = leadOnly ? {
560
- ...sourceAffinity ?? task.routingAffinity,
561
- capabilities: task.routingAffinity?.capabilities ?? sourceAffinity?.capabilities ?? [],
562
- leadOnly: true
563
- } : sourceAffinity;
564
- const retryTask = await createTaskExtended(task.task, {
565
- parentTaskId: task.id,
566
- agentId: preferredAgentId,
567
- routingReason: preferredAgentId === undefined ? undefined : preferredAgentId === task.agentId ? "continuity" : "reroute_fault",
568
- routingSource: preferredAgentId === undefined ? undefined : "engine_default",
569
- tags,
570
- priority: task.priority,
571
- source: task.source,
572
- taskType: task.taskType ?? undefined,
573
- routingAffinity
574
- });
575
- if (authorization.decision) {
576
- await createLogEntry({
577
- eventType: "task_recovery_authorization",
578
- taskId: retryTask.id,
659
+ const leadOnly = task.routingAffinity?.leadOnly === true;
660
+ const authorization = await resolveLeadOnlyRecoveryAssignment(task, sourceCandidate);
661
+ if (leadOnly)
662
+ preferredAgentId = authorization.agentId;
663
+ const tags = ["reboot-retry", "auto-generated"];
664
+ if (preferredAgentId !== undefined)
665
+ tags.push(REBOOT_RETRY_PIN_TAG);
666
+ const sourceAffinity = await buildRoutingAffinityFromAgent(task.agentId) ?? undefined;
667
+ const routingAffinity = leadOnly ? {
668
+ ...sourceAffinity ?? task.routingAffinity,
669
+ capabilities: task.routingAffinity?.capabilities ?? sourceAffinity?.capabilities ?? [],
670
+ leadOnly: true
671
+ } : sourceAffinity;
672
+ const retryTask = await createTaskExtended(task.task, {
673
+ parentTaskId: task.id,
579
674
  agentId: preferredAgentId,
580
- metadata: {
581
- leadOnly: true,
582
- parentTaskId: task.id,
583
- sourceAgentId: task.agentId,
584
- decision: authorization.decision
585
- }
675
+ routingReason: preferredAgentId === undefined ? undefined : preferredAgentId === task.agentId ? "continuity" : "reroute_fault",
676
+ routingSource: preferredAgentId === undefined ? undefined : "engine_default",
677
+ tags,
678
+ priority: task.priority,
679
+ source: task.source,
680
+ taskType: task.taskType ?? undefined,
681
+ inheritParentFollowUpConfig: true,
682
+ routingAffinity
586
683
  });
684
+ if (authorization.decision) {
685
+ await createLogEntry({
686
+ eventType: "task_recovery_authorization",
687
+ taskId: retryTask.id,
688
+ agentId: preferredAgentId,
689
+ metadata: {
690
+ leadOnly: true,
691
+ parentTaskId: task.id,
692
+ sourceAgentId: task.agentId,
693
+ decision: authorization.decision
694
+ }
695
+ });
696
+ }
697
+ retryTaskId = retryTask.id;
698
+ console.log(`[Heartbeat] Reboot retry created: ${retryTaskId} (parent: ${task.id})`);
699
+ } catch (err) {
700
+ console.error(`[Heartbeat] Failed to create retry task for ${task.id}:`, err);
587
701
  }
588
- retryTaskId = retryTask.id;
589
- console.log(`[Heartbeat] Reboot retry created: ${retryTaskId} (parent: ${task.id})`);
590
- } catch (err) {
591
- console.error(`[Heartbeat] Failed to create retry task for ${task.id}:`, err);
592
702
  }
703
+ rebootAffectedTasks.push({ original: failed, retryTaskId });
704
+ } finally {
705
+ await settleRebootSweptDependents(task.id, retryTaskId);
593
706
  }
594
- rebootAffectedTasks.push({ original: failed, retryTaskId });
595
707
  }
596
708
  console.log(`[Heartbeat] Reboot sweep complete: ${rebootAffectedTasks.length} task(s) auto-failed and retried`);
597
709
  } finally {
598
710
  isSweeping = false;
599
711
  }
600
712
  }
713
+ var NEVER_STARTED_STATUSES = new Set([
714
+ "draft",
715
+ "backlog",
716
+ "unassigned",
717
+ "offered",
718
+ "reviewing",
719
+ "pending"
720
+ ]);
721
+ async function settleRebootSweptDependents(sweptId, retryTaskId) {
722
+ try {
723
+ if (retryTaskId) {
724
+ for (const dep of await getDependentTasks(sweptId)) {
725
+ if (!NEVER_STARTED_STATUSES.has(dep.status))
726
+ continue;
727
+ const dependsOn = [
728
+ ...new Set(dep.dependsOn.map((id) => id === sweptId ? retryTaskId : id))
729
+ ];
730
+ const row = await getDbClient().get(`UPDATE agent_tasks
731
+ SET dependsOn = ?, lastUpdatedAt = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
732
+ WHERE id = ? AND status = ?
733
+ AND EXISTS (SELECT 1 FROM json_each(agent_tasks.dependsOn) WHERE value = ?)
734
+ RETURNING id`, [JSON.stringify(dependsOn), dep.id, dep.status, sweptId]);
735
+ if (!row)
736
+ continue;
737
+ try {
738
+ await createLogEntry({
739
+ eventType: "task_dependency_repointed",
740
+ taskId: dep.id,
741
+ agentId: dep.agentId ?? undefined,
742
+ oldValue: sweptId,
743
+ newValue: retryTaskId,
744
+ metadata: { reason: "reboot_retry" }
745
+ });
746
+ } catch {}
747
+ console.log(`[Heartbeat] Reboot sweep: dependent ${dep.id.slice(0, 8)} re-pointed from ${sweptId.slice(0, 8)} to retry ${retryTaskId.slice(0, 8)}`);
748
+ }
749
+ }
750
+ await cascadeFailDependents(sweptId, "failed");
751
+ } catch (err) {
752
+ console.error(`[Heartbeat] Reboot sweep: settling dependents of ${sweptId} failed:`, err);
753
+ }
754
+ }
601
755
  function getRebootAffectedTasks() {
602
756
  return rebootAffectedTasks;
603
757
  }
@@ -764,6 +918,18 @@ async function cleanupStaleResources(findings) {
764
918
  } catch {
765
919
  findings.staleCleanup.workflowRuns = 0;
766
920
  }
921
+ try {
922
+ findings.staleCleanup.approvalTimedOut = (await timeoutExpiredApprovalRequests()).timedOut.length;
923
+ } catch (err) {
924
+ console.error("[heartbeat] approval timeout sweep failed:", err);
925
+ findings.staleCleanup.approvalTimedOut = 0;
926
+ }
927
+ try {
928
+ findings.staleCleanup.approvalAutoCancelled = (await autoCancelStaleApprovalRequests()).cancelled.length;
929
+ } catch (err) {
930
+ console.error("[heartbeat] approval auto-cancel sweep failed:", err);
931
+ findings.staleCleanup.approvalAutoCancelled = 0;
932
+ }
767
933
  }
768
934
  function isEffectivelyEmpty(content) {
769
935
  const lines = content.split(`
@@ -970,7 +1136,9 @@ async function runHeartbeatSweep() {
970
1136
  workflowRuns: 0,
971
1137
  staleRuntimes: 0,
972
1138
  staleOfferedTasks: 0,
973
- abandonedDraftTasks: 0
1139
+ abandonedDraftTasks: 0,
1140
+ approvalAutoCancelled: 0,
1141
+ approvalTimedOut: 0
974
1142
  }
975
1143
  };
976
1144
  cleanupOnlyFindings.staleCleanup.staleRuntimes = (await expireStaleRuntimeInstances()).expired;
@@ -1018,9 +1186,11 @@ function logFindings(findings) {
1018
1186
  inboxProcessing,
1019
1187
  workflowRuns,
1020
1188
  staleOfferedTasks,
1021
- abandonedDraftTasks
1189
+ abandonedDraftTasks,
1190
+ approvalAutoCancelled,
1191
+ approvalTimedOut
1022
1192
  } = findings.staleCleanup;
1023
- const totalCleanup = sessions + reviewingTasks + mentionProcessing + inboxProcessing + workflowRuns;
1193
+ const totalCleanup = sessions + reviewingTasks + mentionProcessing + inboxProcessing + workflowRuns + approvalAutoCancelled + approvalTimedOut;
1024
1194
  if (totalCleanup > 0) {
1025
1195
  parts.push(`stale_cleanup=${totalCleanup}`);
1026
1196
  }
@@ -63160,7 +63160,7 @@ function getCreateJiti() {
63160
63160
  }
63161
63161
  var virtualModulesPromise;
63162
63162
  function getVirtualModules() {
63163
- virtualModulesPromise ??= import("./virtual-modules-8qheft18.js").then((module) => module.VIRTUAL_MODULES);
63163
+ virtualModulesPromise ??= import("./virtual-modules-3htpgn8x.js").then((module) => module.VIRTUAL_MODULES);
63164
63164
  return virtualModulesPromise;
63165
63165
  }
63166
63166
  var _aliases = null;