@desplega.ai/agent-swarm 1.146.0 → 1.147.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 (214) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-jnq4612m.js → acp-adapter-9n319wqc.js} +42 -4
  3. package/dist/{actions-s41xcn3f.js → actions-2vxqvpr9.js} +9 -8
  4. package/dist/{app-j8bfw3f6.js → app-w066xfy0.js} +6 -5
  5. package/dist/assistant-kt31dj2h.js +236 -0
  6. package/dist/{boot-reembed-jxdz46jp.js → boot-reembed-az5rassp.js} +7 -6
  7. package/dist/{boot-reembed-x1476agm.js → boot-reembed-j3mm3rfz.js} +8 -7
  8. package/dist/{boot-scrub-logs-rab9h5bt.js → boot-scrub-logs-b6h54817.js} +4 -4
  9. package/dist/{claude-adapter-ep6qekbm.js → claude-adapter-w1z04ann.js} +8 -6
  10. package/dist/{claude-managed-setup-w619jncn.js → claude-managed-setup-f5pmsyrp.js} +24 -3
  11. package/dist/{claude-sdk-session-0wp3qrcy.js → claude-sdk-session-f0cc1kec.js} +8 -6
  12. package/dist/{cli-bafd6xnf.js → cli-0b6y7kxm.js} +1 -1
  13. package/dist/{cli-jyy3e193.js → cli-12fz972k.js} +4 -4
  14. package/dist/{cli-pzfc7j7g.js → cli-1th1728g.js} +2 -2
  15. package/dist/{cli-68h2mfrn.js → cli-3tgwnf43.js} +3 -3
  16. package/dist/{cli-q3qr220d.js → cli-4nr988h6.js} +1 -1
  17. package/dist/{cli-m4xsy03s.js → cli-53s590z8.js} +1 -1
  18. package/dist/{cli-c8q7292f.js → cli-54wwve05.js} +5 -4
  19. package/dist/{cli-rymrjneb.js → cli-719k9j2c.js} +2 -2
  20. package/dist/{cli-zn65x0w8.js → cli-89xfd7f9.js} +21 -47
  21. package/dist/{cli-506z6jqa.js → cli-8v0g1yc8.js} +1 -1
  22. package/dist/cli-9d8ntetj.js +42 -0
  23. package/dist/{cli-dhg6j8h3.js → cli-atcve9y8.js} +826 -737
  24. package/dist/{cli-46yck6nj.js → cli-b5bq9crk.js} +14 -31
  25. package/dist/cli-d8brzxjb.js +190 -0
  26. package/dist/{cli-a960nkfb.js → cli-de14znh8.js} +2 -2
  27. package/dist/{cli-s8sbe32s.js → cli-dttph1bw.js} +2 -2
  28. package/dist/cli-ep19k3mb.js +19 -0
  29. package/dist/{cli-d9pnxgth.js → cli-f146mn5s.js} +7 -50
  30. package/dist/{cli-qar1f940.js → cli-f7dsy61y.js} +5 -3
  31. package/dist/{cli-d4xqde2b.js → cli-ftht3zzj.js} +2780 -2587
  32. package/dist/{cli-pmwbdw36.js → cli-hbv0kq6w.js} +2 -2
  33. package/dist/{cli-fhsz3twr.js → cli-jsp14b6c.js} +171 -1
  34. package/dist/{cli-2zvtzevh.js → cli-kpv03zhj.js} +13 -11
  35. package/dist/{cli-a9bn9svs.js → cli-m8yrsg97.js} +2 -2
  36. package/dist/cli-m9sxbkhm.js +779 -0
  37. package/dist/cli-mmemxxdc.js +823 -0
  38. package/dist/{cli-6xwhtc64.js → cli-n8508nre.js} +1 -1
  39. package/dist/{cli-q0qq1sxw.js → cli-p1d5b073.js} +37 -2
  40. package/dist/{cli-yqeb27t1.js → cli-rw51bq3j.js} +13 -10
  41. package/dist/{cli-zww3vcnm.js → cli-smby5m17.js} +7 -6
  42. package/dist/{cli-ah5hjk49.js → cli-tzvk9haz.js} +94 -372
  43. package/dist/{cli-yxwe913z.js → cli-w9kb3bk1.js} +1 -1
  44. package/dist/{cli-skd674mt.js → cli-y6b6fb76.js} +25 -3
  45. package/dist/{cli-3k2g3kg2.js → cli-yz3djrm0.js} +9 -9
  46. package/dist/{cli-4secrq32.js → cli-zhjmqxzh.js} +1 -1
  47. package/dist/cli.js +13 -13
  48. package/dist/{codex-adapter-d6qajxj3.js → codex-adapter-zw4dwsx7.js} +2 -2
  49. package/dist/{codex-hook-n7m5k7cr.js → codex-hook-bw8p5nh4.js} +2 -2
  50. package/dist/{codex-session-runner-v9krqe4x.js → codex-session-runner-k981s65v.js} +2 -2
  51. package/dist/{commands-923wsas7.js → commands-sykrxx7e.js} +4 -4
  52. package/dist/{db-m1hy4zh5.js → db-sfbsc8wt.js} +6 -4
  53. package/dist/handlers-0aspcejm.js +603 -0
  54. package/dist/{hook-ee4hff56.js → hook-4nvqj2sb.js} +7 -5
  55. package/dist/{hook-bxe3bkdy.js → hook-hxccs7m5.js} +24 -49
  56. package/dist/{http-a8rx91me.js → http-y06z517d.js} +223 -92
  57. package/dist/{index-697zfzv1.js → index-hssww7kj.js} +15 -14
  58. package/dist/{index-3x9g3nbg.js → index-k8znfx0z.js} +13 -12
  59. package/dist/{index-1j9va48r.js → index-tce1rss4.js} +371 -110
  60. package/dist/{index-jj4q3x59.js → index-x5j894zf.js} +14 -13
  61. package/dist/{keepalive-sds4e4ce.js → keepalive-w733ax66.js} +6 -6
  62. package/dist/{lead-ggq6szk8.js → lead-75bjqa9d.js} +29 -27
  63. package/dist/{maintenance-093p41d4.js → maintenance-w4f1zjck.js} +8 -7
  64. package/dist/{oauth-refresh-sweep-dvzq4ckj.js → oauth-refresh-sweep-wdetky26.js} +5 -5
  65. package/dist/{onboard-ydr9tzw5.js → onboard-bb5net8s.js} +2 -2
  66. package/dist/{otel-impl-zx3tdf3j.js → otel-impl-0w7c14ap.js} +1 -1
  67. package/dist/{pi-mono-adapter-dtkz4e7j.js → pi-mono-adapter-2has4gnp.js} +12 -2
  68. package/dist/{pricing-refresh-7e6pqnzp.js → pricing-refresh-9nzsrgcq.js} +6 -6
  69. package/dist/{rbac-roles-p2rh6d0r.js → rbac-roles-r3hqjp09.js} +5 -5
  70. package/dist/{rbac-roles-0dv1wt0w.js → rbac-roles-z1jrffbt.js} +4 -4
  71. package/dist/{registry-9kdck5ye.js → registry-v5wytg0s.js} +1 -2
  72. package/dist/{render-v2-4k3jt4r2.js → render-v2-vbr89fnb.js} +6 -5
  73. package/dist/{seed-pricing-2cc1qmcd.js → seed-pricing-g62hy1pk.js} +7 -5
  74. package/dist/{setup-c5yss30k.js → setup-gnfqnptp.js} +2 -2
  75. package/dist/{worker-7srgeqcr.js → worker-kznxp4qm.js} +29 -27
  76. package/openapi.json +263 -12
  77. package/package.json +1 -1
  78. package/plugin/commands/start-worker.md +4 -9
  79. package/plugin/pi-skills/start-worker/SKILL.md +4 -9
  80. package/src/be/db/agents.ts +556 -0
  81. package/src/be/db/context-versions.ts +117 -0
  82. package/src/be/db/runtime.ts +406 -0
  83. package/src/be/db/tasks/read.ts +1167 -0
  84. package/src/be/db/tasks/write.ts +953 -0
  85. package/src/be/db.ts +583 -3428
  86. package/src/be/migrations/147_scheduled_tasks_parent_task.sql +7 -0
  87. package/src/be/migrations/148_pages_svg.sql +123 -0
  88. package/src/be/migrations/149_session_tokens.sql +24 -0
  89. package/src/be/scripts/typecheck.ts +1 -0
  90. package/src/be/seed-pricing.ts +13 -8
  91. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  92. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  93. package/src/be/seed-scripts/catalog/star-history-refresh.ts +101 -0
  94. package/src/be/seed-scripts/catalog/star-history-renderer.ts +119 -0
  95. package/src/be/seed-scripts/index.ts +17 -0
  96. package/src/be/seed-skills/index.ts +14 -0
  97. package/src/be/swarm-config-guard.ts +1 -1
  98. package/src/be/task-attachment-store.ts +70 -0
  99. package/src/be/users.ts +77 -0
  100. package/src/commands/attachments-section.ts +37 -0
  101. package/src/commands/claude-managed-setup.ts +45 -3
  102. package/src/commands/claude-md-session.ts +408 -0
  103. package/src/commands/context-preamble.ts +15 -3
  104. package/src/commands/profile-sync.ts +205 -5
  105. package/src/commands/provider-credentials.ts +60 -27
  106. package/src/commands/runner.ts +63 -58
  107. package/src/commands/templates.ts +25 -2
  108. package/src/hooks/hook.ts +26 -67
  109. package/src/http/agents.ts +40 -1
  110. package/src/http/auth.ts +13 -1
  111. package/src/http/fs.ts +21 -51
  112. package/src/http/mcp.ts +32 -0
  113. package/src/http/pages-public.ts +20 -4
  114. package/src/http/poll.ts +4 -0
  115. package/src/http/repos.ts +3 -3
  116. package/src/http/sessions.ts +82 -0
  117. package/src/http/tasks.ts +10 -2
  118. package/src/prompts/session-templates.ts +4 -2
  119. package/src/providers/acp-adapter.ts +42 -6
  120. package/src/providers/pi-mono-adapter.ts +23 -6
  121. package/src/scheduler/scheduler.test.ts +49 -2
  122. package/src/scheduler/scheduler.ts +4 -0
  123. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  124. package/src/scripts-runtime/types/stdlib.d.ts +8 -0
  125. package/src/scripts-runtime/types/swarm-sdk.d.ts +8 -0
  126. package/src/server.ts +2 -0
  127. package/src/slack/assistant.ts +87 -46
  128. package/src/slack/handlers.test.ts +1 -3
  129. package/src/slack/handlers.ts +102 -87
  130. package/src/slack/inbound-files.ts +466 -0
  131. package/src/slack/templates.ts +14 -0
  132. package/src/tasks/task-terminal-effects.ts +158 -0
  133. package/src/tests/acp-adapter.test.ts +327 -192
  134. package/src/tests/budget-claim-gate.test.ts +1 -0
  135. package/src/tests/build-pi-skills.test.ts +5 -7
  136. package/src/tests/claude-managed-setup.test.ts +52 -4
  137. package/src/tests/claude-md-session.test.ts +603 -0
  138. package/src/tests/codex-skill-resolver.test.ts +19 -0
  139. package/src/tests/context-versioning.test.ts +107 -0
  140. package/src/tests/credential-check.test.ts +157 -14
  141. package/src/tests/db-agents.test.ts +91 -0
  142. package/src/tests/db-context-versions.test.ts +37 -0
  143. package/src/tests/db-extraction-contract.test.ts +45 -0
  144. package/src/tests/db-runtime.test.ts +70 -0
  145. package/src/tests/db-task-read-contract.test.ts +624 -0
  146. package/src/tests/db-task-write-contract.test.ts +653 -0
  147. package/src/tests/db-tasks-read.test.ts +183 -0
  148. package/src/tests/db-tasks-write.test.ts +230 -0
  149. package/src/tests/defer-task.test.ts +389 -0
  150. package/src/tests/docs-index-footprint.test.js +2 -2
  151. package/src/tests/e2e-visuals-stage.test.ts +151 -0
  152. package/src/tests/file-lock.test.ts +144 -0
  153. package/src/tests/fixtures/file-lock-holder.ts +13 -0
  154. package/src/tests/fixtures/hold-file-lock.ts +25 -0
  155. package/src/tests/heartbeat-expiry.test.ts +188 -0
  156. package/src/tests/http-api-integration.test.ts +78 -1
  157. package/src/tests/pages-public-html.test.ts +43 -0
  158. package/src/tests/pages-svg-migration.test.ts +42 -0
  159. package/src/tests/preload.ts +4 -1
  160. package/src/tests/profile-sync-warning.test.ts +16 -0
  161. package/src/tests/profile-sync.test.ts +126 -0
  162. package/src/tests/prompt-template-session.test.ts +20 -4
  163. package/src/tests/provider-registration.test.ts +93 -0
  164. package/src/tests/rbac-e2e-helpers.test.ts +53 -0
  165. package/src/tests/rbac-e2e-helpers.ts +19 -2
  166. package/src/tests/routing-affinity-inventory.test.ts +1 -1
  167. package/src/tests/runner-attachments-section.test.ts +22 -2
  168. package/src/tests/runner-context-preamble.test.ts +30 -0
  169. package/src/tests/runner-deployment-resume.test.ts +107 -0
  170. package/src/tests/runner-repo-autostash.test.ts +12 -0
  171. package/src/tests/script-connections.test.ts +4 -0
  172. package/src/tests/seed-scripts.test.ts +2 -2
  173. package/src/tests/session-tokens.test.ts +279 -0
  174. package/src/tests/slack-file-tools-attachments.test.ts +408 -0
  175. package/src/tests/slack-inbound-files.test.ts +847 -0
  176. package/src/tests/star-history-refresh.test.ts +102 -0
  177. package/src/tests/swarm-repos.test.ts +20 -0
  178. package/src/tests/tool-annotations.test.ts +4 -3
  179. package/src/tests/ui-logs-parser.test.ts +176 -0
  180. package/src/tools/create-page.ts +4 -2
  181. package/src/tools/defer-task.ts +239 -0
  182. package/src/tools/repos/update-repo.ts +2 -2
  183. package/src/tools/schedules/create-schedule.ts +1 -0
  184. package/src/tools/schedules/list-schedules.ts +1 -0
  185. package/src/tools/schedules/patch-schedule.ts +1 -0
  186. package/src/tools/schedules/update-schedule.ts +1 -0
  187. package/src/tools/slack-download-file.ts +60 -29
  188. package/src/tools/slack-read.ts +73 -32
  189. package/src/tools/store-progress.ts +16 -146
  190. package/src/tools/tool-config.ts +1 -0
  191. package/src/tools/utils.ts +21 -0
  192. package/src/types.ts +37 -4
  193. package/src/utils/acp-session-token.ts +56 -0
  194. package/src/utils/file-lock.ts +111 -0
  195. package/src/utils/heartbeat-expiry.ts +78 -0
  196. package/src/utils/request-auth-context.ts +2 -0
  197. package/src/utils/task-attachment-links.ts +22 -0
  198. package/templates/skills/code-quality/content.md +8 -5
  199. package/templates/skills/download-task-attachment/content.md +2 -1
  200. package/{plugin/pi-skills → templates/skills}/review-offered-task/SKILL.md +5 -5
  201. package/templates/skills/review-offered-task/config.json +14 -0
  202. package/{plugin/commands/review-offered-task.md → templates/skills/review-offered-task/content.md} +3 -8
  203. package/templates/skills/scheduling/content.md +4 -0
  204. package/templates/skills/slack-interaction/content.md +2 -2
  205. package/templates/skills/work-on-task/SKILL.md +16 -0
  206. package/templates/skills/work-on-task/config.json +14 -0
  207. package/templates/skills/work-on-task/content.md +11 -0
  208. package/dist/assistant-dx3ew4d8.js +0 -212
  209. package/dist/cli-00kyrp1a.js +0 -317
  210. package/dist/cli-n1q8q14d.js +0 -177
  211. package/dist/cli-ss6vybmt.js +0 -269
  212. package/dist/handlers-e9vp2fr8.js +0 -592
  213. package/plugin/commands/work-on-task.md +0 -39
  214. package/plugin/pi-skills/work-on-task/SKILL.md +0 -39
@@ -11,17 +11,17 @@ import {
11
11
  import {
12
12
  calculateNextRun,
13
13
  runScheduleNow
14
- } from "./cli-zww3vcnm.js";
14
+ } from "./cli-smby5m17.js";
15
15
  import {
16
16
  startPricingRefreshLoop
17
- } from "./cli-a9bn9svs.js";
17
+ } from "./cli-m8yrsg97.js";
18
18
  import {
19
19
  seedPricingFromModelsDev
20
- } from "./cli-yqeb27t1.js";
20
+ } from "./cli-rw51bq3j.js";
21
21
  import {
22
22
  ensureRbacSeedsSynced,
23
23
  getUserGrant
24
- } from "./cli-506z6jqa.js";
24
+ } from "./cli-8v0g1yc8.js";
25
25
  import {
26
26
  can,
27
27
  decideAdmission,
@@ -48,7 +48,7 @@ import {
48
48
  runSavedScriptAsAgent,
49
49
  updateApp,
50
50
  upgradeAppDefinition
51
- } from "./cli-a960nkfb.js";
51
+ } from "./cli-de14znh8.js";
52
52
  import {
53
53
  require_dist as require_dist2
54
54
  } from "./cli-qgm2pfpq.js";
@@ -72,7 +72,7 @@ import {
72
72
  updateTrackerSync,
73
73
  updateTrackerSyncSwarmId,
74
74
  upsertSwarmConfigWithPolicyMirror
75
- } from "./cli-qar1f940.js";
75
+ } from "./cli-f7dsy61y.js";
76
76
  import {
77
77
  isMultiRuntimeEnabled
78
78
  } from "./cli-07jd68a1.js";
@@ -86,7 +86,7 @@ import {
86
86
  snapshotWorkflow,
87
87
  startWorkflowExecution,
88
88
  validateJsonSchema
89
- } from "./cli-3k2g3kg2.js";
89
+ } from "./cli-yz3djrm0.js";
90
90
  import {
91
91
  ConnectionAuthInputSchema,
92
92
  SCRIPT_LONG_TIMEOUT_HINT_MS,
@@ -109,7 +109,7 @@ import {
109
109
  upsertCredentialBinding,
110
110
  upsertScriptConnection,
111
111
  validateDefinition
112
- } from "./cli-68h2mfrn.js";
112
+ } from "./cli-3tgwnf43.js";
113
113
  import {
114
114
  CANDIDATE_SET_MULTIPLIER,
115
115
  computeScore,
@@ -121,7 +121,7 @@ import {
121
121
  isGraphExpansionEnabled,
122
122
  rerank,
123
123
  rowToCandidate
124
- } from "./cli-pzfc7j7g.js";
124
+ } from "./cli-1th1728g.js";
125
125
  import {
126
126
  getApiKey
127
127
  } from "./cli-f14fvzag.js";
@@ -139,22 +139,29 @@ import {
139
139
  listAuthorizationsForApp,
140
140
  onAuthorizationRefreshed,
141
141
  upsertOAuthApp
142
- } from "./cli-4secrq32.js";
142
+ } from "./cli-zhjmqxzh.js";
143
143
  import {
144
144
  ZodFirstPartyTypeKind,
145
145
  objectType
146
146
  } from "./cli-fad8m16k.js";
147
- import {
148
- AgentFsProvider
149
- } from "./cli-fhsz3twr.js";
150
147
  import {
151
148
  require_dist
152
149
  } from "./cli-51tf7ac3.js";
153
150
  import {
151
+ DEFAULT_DOWNLOAD_DIR,
152
+ MAX_FILE_SIZE,
153
+ attachSlackFilesForAgent,
154
+ attachableTask,
154
155
  createAdditiveBuffer,
156
+ downloadFile,
155
157
  extractSlackMessageText,
156
- parseSlackTs
157
- } from "./cli-ss6vybmt.js";
158
+ getFileInfo,
159
+ parseSlackTs,
160
+ uploadFile
161
+ } from "./cli-mmemxxdc.js";
162
+ import {
163
+ AgentFsProvider
164
+ } from "./cli-jsp14b6c.js";
158
165
  import {
159
166
  findOrCreateUserByEmail,
160
167
  findUserByExternalId,
@@ -166,14 +173,15 @@ import {
166
173
  recordIdentityEvent,
167
174
  recordUnmappedIdentity,
168
175
  renderIdentity,
176
+ resolveBySessionToken,
169
177
  resolveIdentity,
170
178
  resolveIdentityByEmail,
171
179
  resolveUserByToken,
172
180
  unlinkIdentity
173
- } from "./cli-q0qq1sxw.js";
181
+ } from "./cli-p1d5b073.js";
174
182
  import {
175
183
  createTaskWithSiblingAwareness
176
- } from "./cli-m4xsy03s.js";
184
+ } from "./cli-53s590z8.js";
177
185
  import {
178
186
  ensure,
179
187
  getAgentDisplayName,
@@ -184,7 +192,7 @@ import {
184
192
  startSlackApp,
185
193
  stopSlackApp,
186
194
  withSpan
187
- } from "./cli-46yck6nj.js";
195
+ } from "./cli-b5bq9crk.js";
188
196
  import {
189
197
  $OpenAPIRegistry,
190
198
  $OpenApiGeneratorV31,
@@ -215,7 +223,7 @@ import {
215
223
  PromptTemplateStateSchema,
216
224
  ProviderNameSchema,
217
225
  ReasoningEffortSchema,
218
- RepoGuidelinesSchema,
226
+ RepoGuidelinesInputSchema,
219
227
  RepoHooksSchema,
220
228
  ScheduledTaskTargetTypeSchema,
221
229
  ScriptRunStatusSchema,
@@ -431,13 +439,13 @@ import {
431
439
  upsertPromptTemplate,
432
440
  upsertService,
433
441
  validateConfigValue
434
- } from "./cli-d4xqde2b.js";
442
+ } from "./cli-ftht3zzj.js";
435
443
  import {
436
444
  generateDefaultClaudeMd,
437
445
  generateDefaultIdentityMd,
438
446
  generateDefaultSoulMd,
439
447
  init_defaults
440
- } from "./cli-skd674mt.js";
448
+ } from "./cli-y6b6fb76.js";
441
449
  import {
442
450
  getTemplateDefinition,
443
451
  init_registry,
@@ -446,7 +454,7 @@ import {
446
454
  interpolate,
447
455
  registerTemplate,
448
456
  resolveTemplate
449
- } from "./cli-2zvtzevh.js";
457
+ } from "./cli-kpv03zhj.js";
450
458
  import {
451
459
  MAX_PROFILE_FILE_LENGTH,
452
460
  getAppUrl,
@@ -507,7 +515,7 @@ import {
507
515
  import {
508
516
  init_package,
509
517
  package_default
510
- } from "./cli-yxwe913z.js";
518
+ } from "./cli-w9kb3bk1.js";
511
519
  import {
512
520
  init_secret_scrubber,
513
521
  refreshSecretScrubberCache,
@@ -22295,7 +22303,7 @@ async function notifySlack(text) {
22295
22303
  return;
22296
22304
  }
22297
22305
  try {
22298
- const { getSlackApp: getSlackApp2 } = await import("./app-j8bfw3f6.js");
22306
+ const { getSlackApp: getSlackApp2 } = await import("./app-w066xfy0.js");
22299
22307
  const app = getSlackApp2();
22300
22308
  if (!app) {
22301
22309
  console.warn("[Jira webhook keepalive] Slack not available, cannot send notification");
@@ -22366,7 +22374,7 @@ async function deleteJiraWebhook(webhookId) {
22366
22374
  console.log(`[Jira webhook keepalive] Deleted webhook id=${webhookId}`);
22367
22375
  }
22368
22376
  async function overwriteWebhookIds(next) {
22369
- const { getDbClient: getDbClient2 } = await import("./db-m1hy4zh5.js");
22377
+ const { getDbClient: getDbClient2 } = await import("./db-sfbsc8wt.js");
22370
22378
  await getDbClient2().transaction(async (tx) => {
22371
22379
  const row = await tx.get("SELECT metadata FROM oauth_apps WHERE provider = 'jira'");
22372
22380
  if (!row) {
@@ -95048,156 +95056,6 @@ function warnIfMcpBaseUrlLooksLikeAppUrl2() {
95048
95056
  console.warn(`[Linear] WARNING: MCP_BASE_URL (${mcp}) equals APP_URL — surfaced webhook URL points at the dashboard host, not the API. Configure Linear with this URL only if the dashboard host also serves /api/*.`);
95049
95057
  }
95050
95058
  }
95051
- // src/slack/files.ts
95052
- import { createWriteStream } from "node:fs";
95053
- import { mkdir } from "node:fs/promises";
95054
- import { dirname } from "node:path";
95055
- var MAX_FILE_SIZE = 1073741824;
95056
- var DEFAULT_DOWNLOAD_DIR = `/workspace/shared/downloads/${process.env.AGENT_ID || "default"}/slack`;
95057
- async function uploadFile(client, options) {
95058
- const { file, filename, channelId, threadTs, initialComment, title } = options;
95059
- try {
95060
- if (Buffer.isBuffer(file) && file.length > MAX_FILE_SIZE) {
95061
- return {
95062
- success: false,
95063
- error: `File size ${file.length} bytes exceeds maximum of ${MAX_FILE_SIZE} bytes (1 GB)`
95064
- };
95065
- }
95066
- if (typeof file === "string") {
95067
- const bunFile = Bun.file(file);
95068
- const size = bunFile.size;
95069
- if (size > MAX_FILE_SIZE) {
95070
- return {
95071
- success: false,
95072
- error: `File size ${size} bytes exceeds maximum of ${MAX_FILE_SIZE} bytes (1 GB)`
95073
- };
95074
- }
95075
- }
95076
- const uploadArgs = {
95077
- file,
95078
- filename,
95079
- title: title || filename
95080
- };
95081
- if (channelId) {
95082
- uploadArgs.channel_id = channelId;
95083
- if (threadTs) {
95084
- uploadArgs.thread_ts = threadTs;
95085
- }
95086
- if (initialComment) {
95087
- uploadArgs.initial_comment = initialComment;
95088
- }
95089
- }
95090
- const result = await client.filesUploadV2(uploadArgs);
95091
- const uploadedFile = result.files?.[0];
95092
- const fileId = uploadedFile?.files?.[0]?.id;
95093
- if (!fileId) {
95094
- return {
95095
- success: false,
95096
- error: "Upload succeeded but no file ID returned"
95097
- };
95098
- }
95099
- return {
95100
- success: true,
95101
- fileId
95102
- };
95103
- } catch (error) {
95104
- const errorMsg = error instanceof Error ? error.message : String(error);
95105
- return {
95106
- success: false,
95107
- error: errorMsg
95108
- };
95109
- }
95110
- }
95111
- async function downloadFile(options) {
95112
- const { file, savePath, token } = options;
95113
- try {
95114
- const downloadUrl = typeof file === "string" ? file : file.url_private_download;
95115
- if (!downloadUrl) {
95116
- return {
95117
- success: false,
95118
- error: "No download URL available for this file"
95119
- };
95120
- }
95121
- let finalPath = savePath;
95122
- if (typeof file !== "string" && (savePath.endsWith("/") || !savePath.includes("."))) {
95123
- finalPath = savePath.endsWith("/") ? `${savePath}${file.name}` : `${savePath}/${file.name}`;
95124
- }
95125
- await mkdir(dirname(finalPath), { recursive: true });
95126
- const response = await fetch(downloadUrl, {
95127
- headers: {
95128
- Authorization: `Bearer ${token}`
95129
- }
95130
- });
95131
- if (!response.ok) {
95132
- return {
95133
- success: false,
95134
- error: `HTTP error ${response.status}: ${response.statusText}`
95135
- };
95136
- }
95137
- const body = response.body;
95138
- if (!body) {
95139
- return {
95140
- success: false,
95141
- error: "No response body"
95142
- };
95143
- }
95144
- const writer = createWriteStream(finalPath);
95145
- const reader = body.getReader();
95146
- try {
95147
- while (true) {
95148
- const { done, value } = await reader.read();
95149
- if (done)
95150
- break;
95151
- writer.write(value);
95152
- }
95153
- writer.end();
95154
- } finally {
95155
- reader.releaseLock();
95156
- }
95157
- await new Promise((resolve, reject) => {
95158
- writer.on("finish", resolve);
95159
- writer.on("error", reject);
95160
- });
95161
- return {
95162
- success: true,
95163
- savedPath: finalPath
95164
- };
95165
- } catch (error) {
95166
- const errorMsg = error instanceof Error ? error.message : String(error);
95167
- return {
95168
- success: false,
95169
- error: errorMsg
95170
- };
95171
- }
95172
- }
95173
- async function getFileInfo(client, fileId) {
95174
- try {
95175
- const result = await client.files.info({ file: fileId });
95176
- if (!result.file) {
95177
- return null;
95178
- }
95179
- const f = result.file;
95180
- return {
95181
- id: f.id,
95182
- name: f.name,
95183
- title: f.title,
95184
- mimetype: f.mimetype,
95185
- filetype: f.filetype,
95186
- size: f.size,
95187
- url_private: f.url_private,
95188
- url_private_download: f.url_private_download,
95189
- thumb_64: f.thumb_64,
95190
- thumb_80: f.thumb_80,
95191
- thumb_160: f.thumb_160,
95192
- thumb_360: f.thumb_360,
95193
- thumb_480: f.thumb_480,
95194
- user: f.user,
95195
- created: f.created
95196
- };
95197
- } catch {
95198
- return null;
95199
- }
95200
- }
95201
95059
  // src/http/core.ts
95202
95060
  init_request_auth_context();
95203
95061
  init_secret_scrubber();
@@ -95421,6 +95279,12 @@ async function resolveHttpRequestAuth(req, apiKey) {
95421
95279
  return { kind: "user", userId: user.id, user };
95422
95280
  }
95423
95281
  }
95282
+ if (bearer.startsWith("aseph_")) {
95283
+ const principal = await resolveBySessionToken(bearer);
95284
+ if (principal) {
95285
+ return { kind: "agent", agentId: principal.agentId, taskId: principal.taskId };
95286
+ }
95287
+ }
95424
95288
  return null;
95425
95289
  }
95426
95290
  function isActiveUser(user) {
@@ -96016,7 +95880,7 @@ async function reloadGlobalConfigsAndIntegrations() {
96016
95880
  }
96017
95881
  async function reloadGlobalConfigsAndIntegrationsInner() {
96018
95882
  const updated = await loadGlobalConfigsIntoEnv(true);
96019
- const { resetFileStorageProvider } = await import("./registry-9kdck5ye.js");
95883
+ const { resetFileStorageProvider } = await import("./registry-v5wytg0s.js");
96020
95884
  resetFileStorageProvider();
96021
95885
  const integrations = [];
96022
95886
  resetAgentMail();
@@ -96472,6 +96336,14 @@ var scriptRunNudge = (r) => {
96472
96336
  const body = r.data?.data;
96473
96337
  return !r.ok && body?.error === "timeout" ? SCRIPT_RUN_TIMEOUT_NUDGE : scriptAuthoringNudge(r);
96474
96338
  };
96339
+ var SLACK_API_DISK_NUDGE = "That path is on the API server's disk, not in your container — call this from a task you own (or pass its taskId) to get the file as a task attachment with a fetchCommand.";
96340
+ var slackDownloadFileNudge = (r) => r.ok && r.data?.savedPath ? SLACK_API_DISK_NUDGE : undefined;
96341
+ var slackReadNudge = (r) => {
96342
+ if (!r.ok)
96343
+ return;
96344
+ const messages = r.data?.messages;
96345
+ return messages?.some((m) => m.files?.some((f) => Boolean(f.localPath))) ? SLACK_API_DISK_NUDGE : undefined;
96346
+ };
96475
96347
  var workflowLongScriptTimeoutNudge = (r) => {
96476
96348
  if (!r.ok)
96477
96349
  return;
@@ -96479,6 +96351,7 @@ var workflowLongScriptTimeoutNudge = (r) => {
96479
96351
  return hint ? WORKFLOW_LONG_SCRIPT_TIMEOUT_NUDGE : undefined;
96480
96352
  };
96481
96353
  var NUDGES = {
96354
+ "defer-task": (r) => r.ok ? "Stop working on this task now; the wake-up task will carry your note." : undefined,
96482
96355
  "script-run": scriptRunNudge,
96483
96356
  "script-upsert": scriptAuthoringNudge,
96484
96357
  "launch-script-run": scriptAuthoringNudge,
@@ -96487,6 +96360,8 @@ var NUDGES = {
96487
96360
  "update-workflow": workflowLongScriptTimeoutNudge,
96488
96361
  "patch-workflow": workflowLongScriptTimeoutNudge,
96489
96362
  "patch-workflow-node": workflowLongScriptTimeoutNudge,
96363
+ "slack-download-file": slackDownloadFileNudge,
96364
+ "slack-read": slackReadNudge,
96490
96365
  "script-search": (r) => {
96491
96366
  if (!r.ok)
96492
96367
  return;
@@ -102028,8 +101903,8 @@ var registerCreatePageTool = (server) => {
102028
101903
  key: AssetKeySchema.optional().describe("Logical namespace. Defaults to a shared/page:<id>/ resource key."),
102029
101904
  title: string().min(1).describe("Human-readable title shown in listings."),
102030
101905
  slug: string().min(1).optional().describe("URL slug. Defaults to the kebab-cased title. Same slug → updates the existing row."),
102031
- body: string().min(1).describe("Full page body (HTML document or JSON-render spec, per contentType)."),
102032
- contentType: PageContentTypeSchema.describe("'text/html' renders directly at /p/:id; 'application/json' is rendered by the SPA."),
101906
+ body: string().min(1).describe("Full page body (HTML document, SVG image, or JSON-render spec, per contentType)."),
101907
+ contentType: PageContentTypeSchema.describe("'text/html' and 'image/svg+xml' render directly at /p/:id; 'application/json' is rendered by the SPA."),
102033
101908
  authMode: PageAuthModeSchema.default("authed").describe("'authed' — requires page-session cookie (default); 'public' — no gate and must be explicit; 'password' — requires key."),
102034
101909
  password: string().min(1).optional().describe("Plaintext password, hashed before storage. Only meaningful for authMode='password'."),
102035
101910
  description: string().optional().describe("Optional short description, used in listings + OG-tag unfurl."),
@@ -102728,6 +102603,688 @@ ${result.total} rows in ${result.elapsed}ms`;
102728
102603
  });
102729
102604
  };
102730
102605
 
102606
+ // src/tools/defer-task.ts
102607
+ init_zod();
102608
+ init_db();
102609
+
102610
+ // src/tasks/task-terminal-effects.ts
102611
+ init_db();
102612
+
102613
+ // src/be/memory/raters/retrieval.ts
102614
+ init_db();
102615
+ function dedupeMemoryDocumentIds(memories) {
102616
+ const documents = new Map;
102617
+ for (const memory of memories) {
102618
+ const identity = memory.sourcePath ?? memory.key;
102619
+ if (identity == null)
102620
+ continue;
102621
+ const documentKey = JSON.stringify([memory.agentId, memory.scope, identity]);
102622
+ if (!documents.has(documentKey))
102623
+ documents.set(documentKey, memory.id);
102624
+ }
102625
+ return [...documents.values()];
102626
+ }
102627
+ async function recordMemoryAccesses(memoryIds) {
102628
+ const ids = [...new Set(memoryIds)].filter(Boolean);
102629
+ if (ids.length === 0)
102630
+ return;
102631
+ const now = new Date().toISOString();
102632
+ await getDbClient().transaction(async (tx) => {
102633
+ for (const id of ids) {
102634
+ await tx.run("UPDATE agent_memory SET accessedAt = ?, accessCount = accessCount + 1 WHERE id = ?", [now, id]);
102635
+ }
102636
+ });
102637
+ }
102638
+ async function recordRetrievals(taskId, agentId, results, sessionId, extras) {
102639
+ if (!taskId || results.length === 0)
102640
+ return;
102641
+ const insertSql = `INSERT INTO memory_retrieval
102642
+ (id, taskId, agentId, sessionId, memoryId, similarity, retrievedAt, contextKey, intent, eventType, retrievalId, rank, retrievalSource)
102643
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
102644
+ const now = new Date().toISOString();
102645
+ const retrievalId = crypto.randomUUID();
102646
+ const contextKey = extras?.contextKey ?? null;
102647
+ const intent = extras?.intent ?? null;
102648
+ const eventType = extras?.eventType ?? "search";
102649
+ await getDbClient().transaction(async (tx) => {
102650
+ for (const [rank, r] of results.entries()) {
102651
+ await tx.run(insertSql, [
102652
+ crypto.randomUUID(),
102653
+ taskId,
102654
+ agentId,
102655
+ sessionId ?? null,
102656
+ r.memoryId,
102657
+ r.similarity,
102658
+ now,
102659
+ contextKey,
102660
+ intent,
102661
+ eventType,
102662
+ retrievalId,
102663
+ rank,
102664
+ r.retrievalSource ?? null
102665
+ ]);
102666
+ }
102667
+ });
102668
+ ensure({
102669
+ id: "memory_retrieved",
102670
+ flow: "task",
102671
+ runId: taskId,
102672
+ data: {
102673
+ count: results.length,
102674
+ taskId,
102675
+ agentId
102676
+ },
102677
+ validator: (data) => typeof data.count === "number" && data.count > 0 && typeof data.taskId === "string" && data.taskId.length > 0 && typeof data.agentId === "string" && data.agentId.length > 0
102678
+ });
102679
+ }
102680
+ async function getRetrievalsForTask(taskId) {
102681
+ return getDbClient().query("SELECT memoryId, similarity FROM memory_retrieval WHERE taskId = ?", [taskId]);
102682
+ }
102683
+
102684
+ // src/be/memory/raters/explicit-self.ts
102685
+ class ExplicitSelfRatingRater {
102686
+ name = "explicit-self";
102687
+ async rate() {
102688
+ return [];
102689
+ }
102690
+ }
102691
+
102692
+ // src/be/memory/raters/implicit-citation.ts
102693
+ class ImplicitCitationRater {
102694
+ name = "implicit-citation";
102695
+ async rate(ctx) {
102696
+ if (ctx.retrievedMemoryIds.length === 0)
102697
+ return [];
102698
+ const evidence = ctx.evidence ?? "";
102699
+ const events = [];
102700
+ for (const memoryId of ctx.retrievedMemoryIds) {
102701
+ if (evidence.length > 0 && evidence.includes(memoryId)) {
102702
+ events.push({ memoryId, signal: 1, weight: 0.5, source: "" });
102703
+ } else {
102704
+ events.push({ memoryId, signal: -1, weight: 0.25, source: "" });
102705
+ }
102706
+ }
102707
+ return events;
102708
+ }
102709
+ }
102710
+
102711
+ // src/be/memory/raters/noop.ts
102712
+ class NoopRater {
102713
+ name = "noop";
102714
+ async rate() {
102715
+ return [];
102716
+ }
102717
+ }
102718
+
102719
+ // src/be/memory/raters/registry.ts
102720
+ var FACTORIES = {
102721
+ noop: () => new NoopRater,
102722
+ "implicit-citation": () => new ImplicitCitationRater,
102723
+ "explicit-self": () => new ExplicitSelfRatingRater,
102724
+ llm: () => new LlmRater
102725
+ };
102726
+ var SERVER_RATERS = new Set(["implicit-citation"]);
102727
+ function getRegisteredRaters() {
102728
+ const raw = process.env.MEMORY_RATERS;
102729
+ if (!raw || raw.trim() === "") {
102730
+ return [new NoopRater];
102731
+ }
102732
+ const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
102733
+ const raters = [];
102734
+ for (const name of names) {
102735
+ const factory = FACTORIES[name];
102736
+ if (!factory) {
102737
+ console.warn(`[memory-rater] Unknown rater "${name}" in MEMORY_RATERS — skipping`);
102738
+ continue;
102739
+ }
102740
+ raters.push(factory());
102741
+ }
102742
+ if (raters.length === 0) {
102743
+ return [new NoopRater];
102744
+ }
102745
+ return raters;
102746
+ }
102747
+ function getRaterWeightMultiplier(name) {
102748
+ const raw = process.env.MEMORY_RATER_WEIGHTS;
102749
+ if (!raw || raw.trim() === "")
102750
+ return 1;
102751
+ for (const pair of raw.split(",")) {
102752
+ const trimmed = pair.trim();
102753
+ if (trimmed === "")
102754
+ continue;
102755
+ const [rawName, rawMult] = trimmed.split(":");
102756
+ if (!rawName || !rawMult)
102757
+ continue;
102758
+ if (rawName.trim() !== name)
102759
+ continue;
102760
+ const mult = Number(rawMult);
102761
+ if (Number.isFinite(mult) && mult >= 0)
102762
+ return mult;
102763
+ }
102764
+ return 1;
102765
+ }
102766
+
102767
+ // src/be/memory/raters/store.ts
102768
+ init_db();
102769
+ class ExplicitSelfDuplicateError extends Error {
102770
+ event;
102771
+ constructor(message, event) {
102772
+ super(message);
102773
+ this.event = event;
102774
+ this.name = "ExplicitSelfDuplicateError";
102775
+ }
102776
+ }
102777
+ async function applyRating(events, ctx = {}) {
102778
+ if (events.length === 0) {
102779
+ return { applied: 0, rejected: [] };
102780
+ }
102781
+ const accepted = [];
102782
+ const rejected = [];
102783
+ for (const event of events) {
102784
+ const reason = validate(event);
102785
+ if (reason) {
102786
+ rejected.push({ event, reason });
102787
+ continue;
102788
+ }
102789
+ let sanitizedReferencesSource = null;
102790
+ if (event.referencesSource !== undefined) {
102791
+ if (event.referencesSource.length === 0) {
102792
+ rejected.push({ event, reason: "referencesSource must be non-empty" });
102793
+ continue;
102794
+ }
102795
+ if (event.referencesSource.length > REFERENCES_SOURCE_MAX_LENGTH) {
102796
+ rejected.push({
102797
+ event,
102798
+ reason: `referencesSource exceeds ${REFERENCES_SOURCE_MAX_LENGTH} chars`
102799
+ });
102800
+ continue;
102801
+ }
102802
+ sanitizedReferencesSource = sanitizeReferencesSource(event.referencesSource);
102803
+ if (sanitizedReferencesSource === null) {
102804
+ rejected.push({
102805
+ event,
102806
+ reason: "referencesSource contains a NUL byte or strips to empty"
102807
+ });
102808
+ continue;
102809
+ }
102810
+ }
102811
+ accepted.push({ event, sanitizedReferencesSource });
102812
+ }
102813
+ if (accepted.length === 0) {
102814
+ return { applied: 0, rejected };
102815
+ }
102816
+ const UPDATE_MEMORY_SQL = "UPDATE agent_memory SET alpha = alpha + ?, beta = beta + ? WHERE id = ?";
102817
+ const CHECK_EXISTS_SQL = "SELECT id FROM agent_memory WHERE id = ?";
102818
+ const INSERT_RATING_SQL = `INSERT INTO memory_rating
102819
+ (id, memoryId, taskId, source, signal, weight, reasoning, createdAt, contextKey)
102820
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
102821
+ const UPSERT_EDGE_SQL = `INSERT INTO agent_memory_edge (from_id, to_id, type, alpha, beta, createdAt)
102822
+ VALUES (?, ?, 'references-source', ?, ?, ?)
102823
+ ON CONFLICT(from_id, to_id, type) DO UPDATE SET
102824
+ alpha = alpha + excluded.alpha - 1.0,
102825
+ beta = beta + excluded.beta - 1.0`;
102826
+ const { applied, lateRejects, appliedEvents } = await getDbClient().transaction(async (tx) => {
102827
+ let applied2 = 0;
102828
+ const lateRejects2 = [];
102829
+ const appliedEvents2 = [];
102830
+ for (const { event, sanitizedReferencesSource } of accepted) {
102831
+ const exists = await tx.get(CHECK_EXISTS_SQL, [event.memoryId]);
102832
+ if (!exists) {
102833
+ lateRejects2.push({ event, reason: "memoryId not found in agent_memory" });
102834
+ continue;
102835
+ }
102836
+ const alphaDelta = Math.max(0, event.signal) * event.weight;
102837
+ const betaDelta = Math.max(0, -event.signal) * event.weight;
102838
+ await tx.run(UPDATE_MEMORY_SQL, [alphaDelta, betaDelta, event.memoryId]);
102839
+ try {
102840
+ await tx.run(INSERT_RATING_SQL, [
102841
+ crypto.randomUUID(),
102842
+ event.memoryId,
102843
+ ctx.taskId ?? null,
102844
+ event.source,
102845
+ event.signal,
102846
+ event.weight,
102847
+ event.reasoning ?? null,
102848
+ new Date().toISOString(),
102849
+ ctx.contextKey ?? null
102850
+ ]);
102851
+ } catch (err) {
102852
+ if (isUniqueConstraintError(err)) {
102853
+ throw new ExplicitSelfDuplicateError(`duplicate explicit-self rating for memoryId=${event.memoryId} taskId=${ctx.taskId}`, event);
102854
+ }
102855
+ throw err;
102856
+ }
102857
+ if (sanitizedReferencesSource !== null) {
102858
+ await tx.run(UPSERT_EDGE_SQL, [
102859
+ event.memoryId,
102860
+ sanitizedReferencesSource,
102861
+ 1 + alphaDelta,
102862
+ 1 + betaDelta,
102863
+ new Date().toISOString()
102864
+ ]);
102865
+ }
102866
+ appliedEvents2.push({ event, sanitizedReferencesSource });
102867
+ applied2 += 1;
102868
+ }
102869
+ return { applied: applied2, lateRejects: lateRejects2, appliedEvents: appliedEvents2 };
102870
+ });
102871
+ if (ctx.taskId && appliedEvents.length > 0) {
102872
+ for (const { event, sanitizedReferencesSource } of appliedEvents) {
102873
+ ensure({
102874
+ id: "memory_rated",
102875
+ flow: "task",
102876
+ runId: ctx.taskId,
102877
+ data: {
102878
+ memoryId: event.memoryId,
102879
+ source: event.source,
102880
+ signal: event.signal,
102881
+ weight: event.weight,
102882
+ hasReferencesSource: sanitizedReferencesSource !== null
102883
+ },
102884
+ validator: (data) => typeof data.memoryId === "string" && data.memoryId.length > 0 && typeof data.source === "string" && data.source.length > 0 && typeof data.signal === "number" && data.signal >= -1 && data.signal <= 1 && typeof data.weight === "number" && data.weight >= 0 && data.weight <= 1
102885
+ });
102886
+ }
102887
+ }
102888
+ return { applied, rejected: [...rejected, ...lateRejects] };
102889
+ }
102890
+ function validate(event) {
102891
+ if (!event.source || event.source.trim() === "") {
102892
+ return "source is required";
102893
+ }
102894
+ if (!Number.isFinite(event.signal) || event.signal < -1 || event.signal > 1) {
102895
+ return "signal must be in [-1, +1]";
102896
+ }
102897
+ if (!Number.isFinite(event.weight) || event.weight < 0 || event.weight > 1) {
102898
+ return "weight must be in [0, 1]";
102899
+ }
102900
+ if (!event.memoryId) {
102901
+ return "memoryId is required";
102902
+ }
102903
+ return null;
102904
+ }
102905
+ function isUniqueConstraintError(err) {
102906
+ if (!(err instanceof Error))
102907
+ return false;
102908
+ return /UNIQUE constraint failed|SQLITE_CONSTRAINT/i.test(err.message);
102909
+ }
102910
+
102911
+ // src/be/memory/raters/run-server-raters.ts
102912
+ async function runServerRaters(input, deps = {}) {
102913
+ const result = { ratersFired: 0, outcomes: [] };
102914
+ if (input.retrievedMemoryIds.length === 0)
102915
+ return result;
102916
+ const allRaters = deps.raters ?? getRegisteredRaters();
102917
+ const allowed = deps.serverRaterNames ?? SERVER_RATERS;
102918
+ const weightFor = deps.weightMultiplierFor ?? getRaterWeightMultiplier;
102919
+ const applyFn = deps.applyRating ?? applyRating;
102920
+ const serverRaters = allRaters.filter((r) => allowed.has(r.name));
102921
+ for (const rater of serverRaters) {
102922
+ const events = await rater.rate({
102923
+ taskId: input.taskId,
102924
+ agentId: input.agentId,
102925
+ retrievedMemoryIds: input.retrievedMemoryIds,
102926
+ evidence: input.evidence
102927
+ });
102928
+ if (events.length === 0)
102929
+ continue;
102930
+ const multiplier = weightFor(rater.name);
102931
+ const stamped = events.map((e) => ({
102932
+ ...e,
102933
+ source: rater.name,
102934
+ weight: Math.max(0, Math.min(1, e.weight * multiplier))
102935
+ }));
102936
+ const applied = await applyFn(stamped, { taskId: input.taskId });
102937
+ result.ratersFired += 1;
102938
+ result.outcomes.push({ rater: rater.name, events: stamped, result: applied });
102939
+ }
102940
+ return result;
102941
+ }
102942
+
102943
+ // src/memory/automatic-task-gate.ts
102944
+ var SCHEDULE_TAG_PREFIX = "schedule:";
102945
+ var AUTOMATIC_TASK_TYPES = new Set([
102946
+ "boot-triage",
102947
+ "heartbeat",
102948
+ "heartbeat-checklist",
102949
+ "health-check",
102950
+ "health-probe",
102951
+ "monitor",
102952
+ "monitoring"
102953
+ ]);
102954
+ function isAutomaticOrRecurringTaskCompletion(task) {
102955
+ const tags = task.tags ?? [];
102956
+ const taskType = task.taskType?.toLowerCase();
102957
+ return task.source === "schedule" || task.source === "system" || tags.includes("scheduled") || tags.includes("auto-generated") || tags.some((tag) => tag.startsWith(SCHEDULE_TAG_PREFIX)) || taskType !== undefined && (AUTOMATIC_TASK_TYPES.has(taskType) || taskType.endsWith("-monitor") || taskType.endsWith("-digest"));
102958
+ }
102959
+ function shouldPersistAutomaticTaskMemory(task, persistMemory) {
102960
+ if (persistMemory)
102961
+ return true;
102962
+ return !isAutomaticOrRecurringTaskCompletion(task);
102963
+ }
102964
+ var shouldPersistTaskCompletionMemory = shouldPersistAutomaticTaskMemory;
102965
+
102966
+ // src/tasks/task-terminal-effects.ts
102967
+ init_secret_scrubber();
102968
+ async function runTaskTerminalEffects(args) {
102969
+ const { task, status, output, failureReason, agentId, persistMemory } = args;
102970
+ const taskId = task.id;
102971
+ if (shouldPersistTaskCompletionMemory(task, persistMemory)) {
102972
+ (async () => {
102973
+ try {
102974
+ const taskContent = status === "completed" ? `Task: ${task.task}
102975
+
102976
+ Output:
102977
+ ${output || "(no output)"}` : `Task: ${task.task}
102978
+
102979
+ Failure reason:
102980
+ ${failureReason || "No reason provided"}
102981
+
102982
+ This task failed. Learn from this to avoid repeating the mistake.`;
102983
+ if (taskContent.length < 30)
102984
+ return;
102985
+ const store = getMemoryStore();
102986
+ const provider = getEmbeddingProvider();
102987
+ const memory = await store.store({
102988
+ agentId: agentId ?? null,
102989
+ content: taskContent,
102990
+ name: `Task: ${task.task.slice(0, 80)}`,
102991
+ scope: "agent",
102992
+ source: "task_completion",
102993
+ sourceTaskId: taskId
102994
+ });
102995
+ const embedding = await provider.embed(taskContent);
102996
+ if (embedding) {
102997
+ await store.updateEmbedding(memory.id, embedding, provider.name);
102998
+ }
102999
+ const shouldShareWithSwarm = status === "completed" && (task.taskType === "research" || task.tags?.includes("knowledge") || task.tags?.includes("shared"));
103000
+ if (shouldShareWithSwarm) {
103001
+ try {
103002
+ const swarmMemory = await store.store({
103003
+ agentId: agentId ?? null,
103004
+ scope: "swarm",
103005
+ name: `Shared: ${task.task.slice(0, 80)}`,
103006
+ content: `Task completed by agent ${agentId}:
103007
+
103008
+ ${taskContent}`,
103009
+ source: "task_completion",
103010
+ sourceTaskId: taskId
103011
+ });
103012
+ const swarmEmbedding = await provider.embed(taskContent);
103013
+ if (swarmEmbedding) {
103014
+ await store.updateEmbedding(swarmMemory.id, swarmEmbedding, provider.name);
103015
+ }
103016
+ } catch {}
103017
+ }
103018
+ } catch {}
103019
+ })().catch((err) => console.error("[task-terminal-effects] task completion memory write failed:", scrubSecrets(err instanceof Error ? err.message : String(err))));
103020
+ }
103021
+ (async () => {
103022
+ try {
103023
+ const retrievals = await getRetrievalsForTask(taskId);
103024
+ if (retrievals.length === 0)
103025
+ return;
103026
+ const retrievedMemoryIds = retrievals.map((r) => r.memoryId);
103027
+ const logs = await getSessionLogsByTaskId(taskId);
103028
+ const evidence = logs.map((l) => l.content).join(`
103029
+ `);
103030
+ await runServerRaters({
103031
+ taskId,
103032
+ agentId: agentId ?? "",
103033
+ retrievedMemoryIds,
103034
+ evidence
103035
+ });
103036
+ } catch (err) {
103037
+ console.error("[task-terminal-effects] server-rater fire failed:", err instanceof Error ? err.message : String(err));
103038
+ }
103039
+ })().catch((err) => console.error("[task-terminal-effects] server rater run failed:", scrubSecrets(err instanceof Error ? err.message : String(err))));
103040
+ if (!task.workflowRunId) {
103041
+ try {
103042
+ const followUp = await createWorkerTaskFollowUp({ task, status, output, failureReason });
103043
+ if (followUp) {
103044
+ console.log(`[task-terminal-effects] Created follow-up task ${followUp.id.slice(0, 8)} for ${status} task ${taskId.slice(0, 8)}`);
103045
+ }
103046
+ } catch (err) {
103047
+ console.warn(`[task-terminal-effects] Failed to create follow-up task: ${err}`);
103048
+ }
103049
+ }
103050
+ }
103051
+
103052
+ // src/tasks/terminal-result-guard.ts
103053
+ init_db();
103054
+ init_types();
103055
+ init_secret_scrubber();
103056
+ function getTaskOutputValidationError(outputSchema, output) {
103057
+ if (!outputSchema || typeof outputSchema !== "object")
103058
+ return;
103059
+ const schema = outputSchema;
103060
+ if (!output) {
103061
+ return `Task has an outputSchema but no output was provided. You must call store-progress with a valid JSON output matching this schema:
103062
+ ${JSON.stringify(schema, null, 2)}`;
103063
+ }
103064
+ let parsed;
103065
+ try {
103066
+ parsed = JSON.parse(output);
103067
+ } catch {
103068
+ return `Task output must be valid JSON matching the outputSchema. Got invalid JSON. Schema:
103069
+ ${JSON.stringify(schema, null, 2)}`;
103070
+ }
103071
+ let validationErrors;
103072
+ try {
103073
+ validationErrors = validateJsonSchema(schema, parsed);
103074
+ } catch (error) {
103075
+ const detail = error instanceof Error ? error.message : String(error);
103076
+ return `Task outputSchema is malformed and cannot be validated (${detail}). Ask an operator to correct the task's outputSchema, or force-complete with store-progress force: true.`;
103077
+ }
103078
+ if (validationErrors.length > 0) {
103079
+ return `Task output does not match the outputSchema. Errors:
103080
+ ${validationErrors.join(`
103081
+ `)}
103082
+
103083
+ Expected schema:
103084
+ ${JSON.stringify(schema, null, 2)}
103085
+
103086
+ Please fix your output and retry.`;
103087
+ }
103088
+ return;
103089
+ }
103090
+ async function guardTerminalTaskResultWrite(task, write) {
103091
+ if (!isTerminalTaskStatus(task.status) || !write.status && !write.force) {
103092
+ return { handled: false };
103093
+ }
103094
+ const scrubbedOutput = write.output !== undefined ? scrubSecrets(write.output) : undefined;
103095
+ const scrubbedFailureReason = write.failureReason !== undefined ? scrubSecrets(write.failureReason) : undefined;
103096
+ const hasDifferingOutput = scrubbedOutput !== undefined && scrubbedOutput !== task.output;
103097
+ const hasDifferingFailureReason = scrubbedFailureReason !== undefined && scrubbedFailureReason !== task.failureReason;
103098
+ const hasDifferingResultText = hasDifferingOutput || hasDifferingFailureReason;
103099
+ if (hasDifferingResultText && write.force) {
103100
+ const outputValidationError = hasDifferingOutput ? getTaskOutputValidationError(task.outputSchema, write.output) : undefined;
103101
+ if (outputValidationError) {
103102
+ return { handled: true, success: false, message: outputValidationError, task };
103103
+ }
103104
+ const overwrittenTask = await overwriteTerminalTaskResultText(task.id, {
103105
+ ...write.output !== undefined ? { output: write.output } : {},
103106
+ ...write.failureReason !== undefined ? { failureReason: write.failureReason } : {}
103107
+ });
103108
+ if (!overwrittenTask) {
103109
+ return {
103110
+ handled: true,
103111
+ success: false,
103112
+ message: `Task "${task.id}" terminal result text could not be overwritten.`,
103113
+ task
103114
+ };
103115
+ }
103116
+ return {
103117
+ handled: true,
103118
+ success: true,
103119
+ message: `Task "${task.id}" is already ${task.status}; force-overwrote terminal result text ` + "without replaying completion side effects.",
103120
+ task: overwrittenTask,
103121
+ wasForcedOverwrite: true
103122
+ };
103123
+ }
103124
+ if (hasDifferingResultText) {
103125
+ return {
103126
+ handled: true,
103127
+ success: false,
103128
+ message: `Discarded write for already-${task.status} task "${task.id}"; ` + "existing output/failureReason and finishedAt were preserved. Retry with force: true " + "to overwrite terminal result text without replaying completion side effects.",
103129
+ task
103130
+ };
103131
+ }
103132
+ return {
103133
+ handled: true,
103134
+ success: true,
103135
+ message: `Task "${task.id}" is already ${task.status}; treating as no-op. ` + "Existing output preserved (first-call-wins).",
103136
+ task,
103137
+ wasNoOp: true
103138
+ };
103139
+ }
103140
+
103141
+ // src/tools/defer-task.ts
103142
+ init_types();
103143
+
103144
+ class DeferAbortedError extends Error {
103145
+ }
103146
+ function renderChecks(checks2) {
103147
+ if (!checks2 || checks2.length === 0)
103148
+ return "";
103149
+ return `
103150
+
103151
+ Checks:
103152
+ ${checks2.map((c) => `- ${c}`).join(`
103153
+ `)}`;
103154
+ }
103155
+ var registerDeferTaskTool = (server) => {
103156
+ createToolRegistrar(server)("defer-task", {
103157
+ title: "Defer Task",
103158
+ annotations: { destructiveHint: false, idempotentHint: false },
103159
+ description: "Completes this task now with status `completed` and books a wake-up for you. Use when the result needs time: a build, a deploy, a reply. The task reaches its final state on this call; the lead sees your summary as its output. A one-off schedule wakes you up later with a child task that carries this task as its parent. Provide delayMs or runAt, a summary of what you did, and a note that says what is pending and what to check.",
103160
+ inputSchema: object({
103161
+ taskId: string().describe("The ID of the task you are working on."),
103162
+ delayMs: number().int().positive().optional().describe("Wake up after this many milliseconds (e.g. 1800000 for 30 min)."),
103163
+ runAt: string().datetime().optional().describe("Wake up at this ISO datetime (e.g. '2026-03-06T15:00:00Z'). Must be future."),
103164
+ summary: string().min(1).max(4000).describe("What you did so far and where things stand. This becomes the task's output; the lead and your wake-up run both read it."),
103165
+ note: string().min(1).max(2000).describe("What is pending, and what to check on wake-up."),
103166
+ checks: array(string().min(1)).max(20).optional().describe("Concrete things to verify on wake-up, one per entry.")
103167
+ }),
103168
+ outputSchema: swarmToolOutputSchema({
103169
+ yourAgentId: string().optional(),
103170
+ taskId: string().optional(),
103171
+ scheduleId: string().optional(),
103172
+ nextRunAt: string().optional()
103173
+ })
103174
+ }, async ({ taskId, delayMs, runAt, summary, note, checks: checks2 }, requestInfo, _meta) => {
103175
+ if (!requestInfo.agentId) {
103176
+ return toolErr('Agent ID not found. Set the "X-Agent-ID" header.');
103177
+ }
103178
+ const agent = await getAgentById(requestInfo.agentId);
103179
+ if (!agent) {
103180
+ return toolErr(`Agent not found: ${requestInfo.agentId}`);
103181
+ }
103182
+ const task = await getTaskById(taskId);
103183
+ if (!task) {
103184
+ return toolErr(`Task with ID "${taskId}" not found.`);
103185
+ }
103186
+ const forbidden = assertOwnsTask(ownerCtx(requestInfo), task);
103187
+ if (forbidden)
103188
+ return forbidden;
103189
+ if (task.agentId !== requestInfo.agentId) {
103190
+ return toolErr(`Task "${taskId}" is not assigned to you.`);
103191
+ }
103192
+ if (isTerminalTaskStatus(task.status)) {
103193
+ return toolErr(`Task ${taskId} is already ${task.status}; nothing to defer.`);
103194
+ }
103195
+ if (task.workflowRunId) {
103196
+ return toolErr(`Task ${taskId} is owned by workflow run ${task.workflowRunId}; workflow-owned tasks cannot be deferred.`);
103197
+ }
103198
+ if (!delayMs && !runAt) {
103199
+ return toolErr("Provide either delayMs or runAt.");
103200
+ }
103201
+ if (delayMs && runAt) {
103202
+ return toolErr("Provide either delayMs or runAt, not both.");
103203
+ }
103204
+ if (runAt && new Date(runAt).getTime() <= Date.now()) {
103205
+ return toolErr("runAt must be in the future.");
103206
+ }
103207
+ const nextRunAt = delayMs ? new Date(Date.now() + delayMs).toISOString() : runAt;
103208
+ const checksBlock = renderChecks(checks2);
103209
+ const taskTemplate = `Resume task ${taskId}: ${note}${checksBlock}`;
103210
+ const createdBy = await resolveTaskAuditUserId(requestInfo.sourceTaskId, requestInfo.agentId) ?? undefined;
103211
+ const previewOutput = `${summary}
103212
+
103213
+ Deferred until ${nextRunAt} (schedule pending). Pending: ${note}${checksBlock}`;
103214
+ const outputValidationError = getTaskOutputValidationError(task.outputSchema, previewOutput);
103215
+ if (outputValidationError) {
103216
+ return toolErr(`Task ${taskId} has an outputSchema; its terminal output must satisfy it, but a deferral note cannot. ${outputValidationError}`);
103217
+ }
103218
+ try {
103219
+ const committed = await getDbClient().transaction(async () => {
103220
+ const schedule = await createScheduledTask({
103221
+ name: `deferred-${taskId.slice(0, 8)}-${Date.now()}-${crypto.randomUUID()}`,
103222
+ description: note,
103223
+ taskTemplate,
103224
+ targetType: "agent-task",
103225
+ scheduleType: "one_time",
103226
+ nextRunAt,
103227
+ targetAgentId: requestInfo.agentId,
103228
+ createdByAgentId: requestInfo.agentId,
103229
+ taskType: "deferred",
103230
+ tags: ["deferred"],
103231
+ priority: task.priority,
103232
+ modelTier: task.modelTier,
103233
+ parentTaskId: taskId,
103234
+ createdBy
103235
+ });
103236
+ const output = `${summary}
103237
+
103238
+ Deferred until ${nextRunAt} (schedule ${schedule.id}). Pending: ${note}${checksBlock}`;
103239
+ const completed = await completeTask(taskId, output);
103240
+ if (!completed) {
103241
+ throw new DeferAbortedError(`Task ${taskId} reached a terminal state before this deferral committed.`);
103242
+ }
103243
+ getDbClient().afterCommit(() => {
103244
+ ensure({
103245
+ id: "completed",
103246
+ flow: "task",
103247
+ runId: taskId,
103248
+ depIds: task.wasPaused ? ["started", "resumed"] : ["started"],
103249
+ data: {
103250
+ taskId,
103251
+ agentId: task.agentId,
103252
+ previousStatus: task.status,
103253
+ hasOutput: true
103254
+ },
103255
+ validator: (data) => data.previousStatus === "in_progress",
103256
+ filter: ({}, ctx) => ctx.deps.length > 0,
103257
+ conditions: [{ timeout_ms: 3600000 }]
103258
+ });
103259
+ });
103260
+ if (task.agentId) {
103261
+ await updateAgentStatusFromCapacity(task.agentId);
103262
+ }
103263
+ return { scheduleId: schedule.id, output, completed };
103264
+ });
103265
+ await runTaskTerminalEffects({
103266
+ task: committed.completed,
103267
+ status: "completed",
103268
+ output: committed.output,
103269
+ agentId: requestInfo.agentId
103270
+ });
103271
+ return toolOk(`Task ${taskId} completed and deferred. Wake-up at ${nextRunAt} (schedule ${committed.scheduleId}). This task is final; the wake-up task continues the work.`, {
103272
+ data: {
103273
+ yourAgentId: requestInfo.agentId,
103274
+ taskId,
103275
+ scheduleId: committed.scheduleId,
103276
+ nextRunAt
103277
+ }
103278
+ });
103279
+ } catch (error) {
103280
+ const message = error instanceof Error ? error.message : "Unknown error";
103281
+ return toolErr(`Failed to defer task: ${message}`, {
103282
+ data: { yourAgentId: requestInfo.agentId, taskId }
103283
+ });
103284
+ }
103285
+ });
103286
+ };
103287
+
102731
103288
  // src/tools/delete-channel.ts
102732
103289
  init_zod();
102733
103290
  init_db();
@@ -104891,77 +105448,6 @@ async function getLinksForMemory(memoryId, options = {}) {
104891
105448
  return { links, backlinks };
104892
105449
  }
104893
105450
 
104894
- // src/be/memory/raters/retrieval.ts
104895
- init_db();
104896
- function dedupeMemoryDocumentIds(memories) {
104897
- const documents = new Map;
104898
- for (const memory of memories) {
104899
- const identity = memory.sourcePath ?? memory.key;
104900
- if (identity == null)
104901
- continue;
104902
- const documentKey = JSON.stringify([memory.agentId, memory.scope, identity]);
104903
- if (!documents.has(documentKey))
104904
- documents.set(documentKey, memory.id);
104905
- }
104906
- return [...documents.values()];
104907
- }
104908
- async function recordMemoryAccesses(memoryIds) {
104909
- const ids = [...new Set(memoryIds)].filter(Boolean);
104910
- if (ids.length === 0)
104911
- return;
104912
- const now = new Date().toISOString();
104913
- await getDbClient().transaction(async (tx) => {
104914
- for (const id of ids) {
104915
- await tx.run("UPDATE agent_memory SET accessedAt = ?, accessCount = accessCount + 1 WHERE id = ?", [now, id]);
104916
- }
104917
- });
104918
- }
104919
- async function recordRetrievals(taskId, agentId, results, sessionId, extras) {
104920
- if (!taskId || results.length === 0)
104921
- return;
104922
- const insertSql = `INSERT INTO memory_retrieval
104923
- (id, taskId, agentId, sessionId, memoryId, similarity, retrievedAt, contextKey, intent, eventType, retrievalId, rank, retrievalSource)
104924
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
104925
- const now = new Date().toISOString();
104926
- const retrievalId = crypto.randomUUID();
104927
- const contextKey = extras?.contextKey ?? null;
104928
- const intent = extras?.intent ?? null;
104929
- const eventType = extras?.eventType ?? "search";
104930
- await getDbClient().transaction(async (tx) => {
104931
- for (const [rank, r] of results.entries()) {
104932
- await tx.run(insertSql, [
104933
- crypto.randomUUID(),
104934
- taskId,
104935
- agentId,
104936
- sessionId ?? null,
104937
- r.memoryId,
104938
- r.similarity,
104939
- now,
104940
- contextKey,
104941
- intent,
104942
- eventType,
104943
- retrievalId,
104944
- rank,
104945
- r.retrievalSource ?? null
104946
- ]);
104947
- }
104948
- });
104949
- ensure({
104950
- id: "memory_retrieved",
104951
- flow: "task",
104952
- runId: taskId,
104953
- data: {
104954
- count: results.length,
104955
- taskId,
104956
- agentId
104957
- },
104958
- validator: (data) => typeof data.count === "number" && data.count > 0 && typeof data.taskId === "string" && data.taskId.length > 0 && typeof data.agentId === "string" && data.agentId.length > 0
104959
- });
104960
- }
104961
- async function getRetrievalsForTask(taskId) {
104962
- return getDbClient().query("SELECT memoryId, similarity FROM memory_retrieval WHERE taskId = ?", [taskId]);
104963
- }
104964
-
104965
105451
  // src/tools/memory-get.ts
104966
105452
  init_types();
104967
105453
  var NUDGE_ELIGIBLE_SOURCES = new Set(["manual", "file_index"]);
@@ -107116,7 +107602,7 @@ var registerUpdateRepoTool = (server) => {
107116
107602
  defaultBranch: string().optional().describe("New default branch."),
107117
107603
  autoClone: boolean().optional().describe("Whether to auto-clone."),
107118
107604
  hooks: RepoHooksSchema.nullable().optional().describe("Repository hook install config. Set { enabled: true } to opt into best-effort worker hook installation, or null to disable."),
107119
- guidelines: RepoGuidelinesSchema.nullable().optional().describe("Repository guidelines: prChecks (commands before PR), mergeChecks (conditions before merge), allowMerge (default false), review (guidance for reviewers). Pass null to clear.")
107605
+ guidelines: RepoGuidelinesInputSchema.nullable().optional().describe("Repository guidelines: prChecks (commands before PR), mergeChecks (conditions before merge), allowMerge (default false), review (guidance for reviewers). Pass null to clear.")
107120
107606
  }),
107121
107607
  outputSchema: swarmToolOutputSchema({
107122
107608
  repo: swarmRepoOutputShape2.nullable().optional()
@@ -113708,6 +114194,7 @@ var scheduleDataShape = {
113708
114194
  lastRunAt: string().optional(),
113709
114195
  nextRunAt: string().optional(),
113710
114196
  createdByAgentId: string().optional(),
114197
+ parentTaskId: string().optional(),
113711
114198
  timezone: string().optional(),
113712
114199
  model: string().optional(),
113713
114200
  modelTier: ModelTierSchema.optional(),
@@ -114120,6 +114607,7 @@ var scheduleRowShape = {
114120
114607
  lastRunAt: string().optional(),
114121
114608
  nextRunAt: string().optional(),
114122
114609
  createdByAgentId: string().optional(),
114610
+ parentTaskId: string().optional(),
114123
114611
  timezone: string().optional(),
114124
114612
  consecutiveErrors: number().optional(),
114125
114613
  lastErrorAt: string().optional(),
@@ -114258,6 +114746,7 @@ var scheduleDataShape2 = {
114258
114746
  lastRunAt: string().optional(),
114259
114747
  nextRunAt: string().optional(),
114260
114748
  createdByAgentId: string().optional(),
114749
+ parentTaskId: string().optional(),
114261
114750
  timezone: string().optional(),
114262
114751
  model: string().optional(),
114263
114752
  modelTier: ModelTierSchema.optional(),
@@ -114544,6 +115033,7 @@ var scheduleDataShape3 = {
114544
115033
  lastRunAt: string().optional(),
114545
115034
  nextRunAt: string().optional(),
114546
115035
  createdByAgentId: string().optional(),
115036
+ parentTaskId: string().optional(),
114547
115037
  timezone: string().optional(),
114548
115038
  model: string().optional(),
114549
115039
  modelTier: ModelTierSchema.optional(),
@@ -117100,19 +117590,26 @@ var registerSlackDeleteTool = (server) => {
117100
117590
  // src/tools/slack-download-file.ts
117101
117591
  init_zod();
117102
117592
  init_db();
117593
+ function fileIdFromUrl(url) {
117594
+ return /\/files-pri\/[A-Z0-9]+-([A-Z0-9]+)\//.exec(url)?.[1];
117595
+ }
117103
117596
  var registerSlackDownloadFileTool = (server) => {
117104
117597
  createToolRegistrar(server)("slack-download-file", {
117105
117598
  title: "Download file from Slack",
117106
- description: "Download a file from Slack by file ID or URL. Files are saved to the agent's download directory on the shared disk by default.",
117107
- annotations: { readOnlyHint: true, openWorldHint: true },
117599
+ description: "Download a file from Slack by file ID or URL. From a task, the file is stored as an attachment of that task and the result carries a ready-to-run `fetchCommand` to get the bytes into your container. Without a task, the file is saved on the API server's disk, which your container usually can't read.",
117600
+ annotations: { readOnlyHint: false, openWorldHint: true },
117108
117601
  inputSchema: object({
117109
117602
  fileId: string().optional().describe("The Slack file ID to download (e.g., 'F0RDC39U1')."),
117110
117603
  url: string().url().optional().describe("Direct URL to download (url_private_download from a file object)."),
117111
- savePath: string().optional().describe("Where to save the file. Can be a directory or full path. Defaults to /workspace/shared/downloads/{agentId}/slack/"),
117604
+ taskId: uuid().optional().describe("Task to attach the file to. Defaults to the task you are working on; must be a task you own or created."),
117605
+ savePath: string().optional().describe("Only without a task: where to save the file on the API server (directory or full path). Defaults to /workspace/shared/downloads/{agentId}/slack/."),
117112
117606
  filename: string().optional().describe("Filename to use when saving. Only used if savePath is a directory.")
117113
117607
  }),
117114
117608
  outputSchema: swarmToolOutputSchema({
117115
- savedPath: string().optional(),
117609
+ taskId: string().optional(),
117610
+ attachmentId: string().optional(),
117611
+ fetchCommand: string().optional(),
117612
+ savedPath: string().optional().describe("Only without a task: the path on the API server's disk, not in your container."),
117116
117613
  fileInfo: looseObject({
117117
117614
  id: string().optional(),
117118
117615
  name: string().optional(),
@@ -117120,7 +117617,7 @@ var registerSlackDownloadFileTool = (server) => {
117120
117617
  size: number().optional()
117121
117618
  }).optional()
117122
117619
  })
117123
- }, async ({ fileId, url, savePath, filename }, requestInfo, _meta) => {
117620
+ }, async ({ fileId, url, taskId, savePath, filename }, requestInfo, _meta) => {
117124
117621
  if (!requestInfo.agentId) {
117125
117622
  return toolErr("Agent ID not found.");
117126
117623
  }
@@ -117139,22 +117636,33 @@ var registerSlackDownloadFileTool = (server) => {
117139
117636
  if (!token) {
117140
117637
  return toolErr("Slack bot token not configured.");
117141
117638
  }
117639
+ const task = await attachableTask(agent.id, taskId ?? requestInfo.sourceTaskId);
117640
+ if (taskId && !task) {
117641
+ return toolErr("You don't have context for this task.");
117642
+ }
117142
117643
  try {
117143
- let downloadUrl = url;
117144
- let fileInfo;
117145
- if (fileId) {
117146
- const info = await getFileInfo(app.client, fileId);
117644
+ const id2 = fileId ?? (url ? fileIdFromUrl(url) : undefined);
117645
+ const info = id2 ? await getFileInfo(app.client, id2) : null;
117646
+ if (fileId && !info) {
117647
+ return toolErr(`File not found: ${fileId}`);
117648
+ }
117649
+ const fileInfo = info ? { id: info.id, name: info.name, mimetype: info.mimetype, size: info.size } : undefined;
117650
+ if (task) {
117147
117651
  if (!info) {
117148
- return toolErr(`File not found: ${fileId}`);
117652
+ return toolErr("Could not identify the Slack file in that URL; pass fileId instead.");
117149
117653
  }
117150
- downloadUrl = info.url_private_download;
117151
- fileInfo = {
117152
- id: info.id,
117153
- name: info.name,
117154
- mimetype: info.mimetype,
117155
- size: info.size
117156
- };
117654
+ const outcome = (await attachSlackFilesForAgent(app.client, task, [info], agent.id)).get(info.id);
117655
+ if (!outcome || "reason" in outcome) {
117656
+ return toolErr(`Failed to attach file: ${outcome?.reason ?? "unknown error"}`);
117657
+ }
117658
+ const { attachment, fetchCommand } = outcome;
117659
+ return toolOk(`Attached ${attachment.name} to task ${task.id} (attachment ${attachment.id}).`, {
117660
+ details: `Get it into your container with:
117661
+ ${fetchCommand}`,
117662
+ data: { taskId: task.id, attachmentId: attachment.id, fetchCommand, fileInfo }
117663
+ });
117157
117664
  }
117665
+ const downloadUrl = info?.url_private_download ?? url;
117158
117666
  if (!downloadUrl) {
117159
117667
  return toolErr("No download URL available.");
117160
117668
  }
@@ -117171,8 +117679,9 @@ var registerSlackDownloadFileTool = (server) => {
117171
117679
  if (!result.success) {
117172
117680
  return toolErr(`Failed to download file: ${result.error}`);
117173
117681
  }
117174
- const successMsg = `File downloaded successfully to ${result.savedPath}`;
117175
- return toolOk(successMsg, { data: { savedPath: result.savedPath, fileInfo } });
117682
+ return toolOk(`File saved on the API server at ${result.savedPath}.`, {
117683
+ data: { savedPath: result.savedPath, fileInfo }
117684
+ });
117176
117685
  } catch (error) {
117177
117686
  const errorMsg = error instanceof Error ? error.message : String(error);
117178
117687
  return toolErr(`Failed to download file: ${errorMsg}`);
@@ -117443,7 +117952,10 @@ var SlackFileSchema = looseObject({
117443
117952
  filetype: string().optional(),
117444
117953
  size: number().optional(),
117445
117954
  url_private_download: string().optional(),
117446
- localPath: string().optional()
117955
+ attachmentId: string().optional(),
117956
+ fetchCommand: string().optional(),
117957
+ notAttached: string().optional(),
117958
+ localPath: string().optional().describe("Only without a task: the path on the API server's disk, not in your container.")
117447
117959
  });
117448
117960
  var SlackMessageSchema = looseObject({
117449
117961
  user: string().optional(),
@@ -117456,15 +117968,15 @@ var SlackMessageSchema = looseObject({
117456
117968
  var registerSlackReadTool = (server) => {
117457
117969
  createToolRegistrar(server)("slack-read", {
117458
117970
  title: "Read Slack thread/channel history",
117459
- description: "Read messages from a Slack thread or channel. Use inboxMessageId or taskId to read from a thread you have context for, or provide channelId directly for channel history (leads only).",
117460
- annotations: { readOnlyHint: true, openWorldHint: true },
117971
+ description: "Read messages from a Slack thread or channel. Use inboxMessageId or taskId to read from a thread you have context for, or provide channelId directly for channel history (leads only). From a task, files in the messages are stored as attachments of that task, each with a ready-to-run `fetchCommand`.",
117972
+ annotations: { readOnlyHint: false, openWorldHint: true },
117461
117973
  inputSchema: object({
117462
117974
  inboxMessageId: uuid().optional().describe("Read thread history for an inbox message."),
117463
117975
  taskId: uuid().optional().describe("Read thread history for a task."),
117464
117976
  channelId: string().optional().describe("Slack channel ID to read from (requires lead privileges)."),
117465
117977
  threadTs: string().optional().describe("Thread timestamp (required with channelId for thread history)."),
117466
117978
  limit: number().int().min(1).max(100).default(20).describe("Maximum number of messages to retrieve (default: 20, max: 100)."),
117467
- includeFiles: boolean().default(true).describe("Include file attachments in the response (default: true).")
117979
+ includeFiles: boolean().default(true).describe("Include file attachments in the response (default: true). From a task, they are also stored as attachments of that task.")
117468
117980
  }),
117469
117981
  outputSchema: swarmToolOutputSchema({
117470
117982
  channelId: string().optional(),
@@ -117481,6 +117993,7 @@ var registerSlackReadTool = (server) => {
117481
117993
  }
117482
117994
  let slackChannelId = channelId;
117483
117995
  let slackThreadTs = threadTs;
117996
+ let contextTask = null;
117484
117997
  if (inboxMessageId) {
117485
117998
  const inboxMsg = await getInboxMessageById(inboxMessageId);
117486
117999
  if (!inboxMsg) {
@@ -117501,6 +118014,7 @@ var registerSlackReadTool = (server) => {
117501
118014
  }
117502
118015
  slackChannelId = task.slackChannelId;
117503
118016
  slackThreadTs = task.slackThreadTs;
118017
+ contextTask = task;
117504
118018
  } else if (channelId) {
117505
118019
  const decision = can({
117506
118020
  principal: { kind: "agent", agentId: agent.id, isLead: agent.isLead },
@@ -117554,6 +118068,11 @@ var registerSlackReadTool = (server) => {
117554
118068
  }
117555
118069
  }
117556
118070
  const token = process.env.SLACK_BOT_TOKEN;
118071
+ const attachTo = contextTask ?? await attachableTask(agent.id, taskId ?? requestInfo.sourceTaskId);
118072
+ if (taskId && !attachTo) {
118073
+ return toolErr("You don't have context for this task.", { data: { messages: [] } });
118074
+ }
118075
+ const toAttach = [];
117557
118076
  const messages = [];
117558
118077
  for (const m of rawMessages) {
117559
118078
  const extractedText = extractSlackMessageText(m);
@@ -117578,7 +118097,9 @@ var registerSlackReadTool = (server) => {
117578
118097
  size: f.size,
117579
118098
  url_private_download: f.url_private_download
117580
118099
  };
117581
- if (token && f.url_private_download) {
118100
+ if (attachTo) {
118101
+ toAttach.push({ raw: f, info: fileInfo });
118102
+ } else if (token && f.url_private_download) {
117582
118103
  const savePath = `${AUTO_DOWNLOAD_DIR}/${f.id}_${f.name}`;
117583
118104
  try {
117584
118105
  const downloadResult = await downloadFile({
@@ -117603,13 +118124,29 @@ var registerSlackReadTool = (server) => {
117603
118124
  files
117604
118125
  });
117605
118126
  }
118127
+ if (attachTo && toAttach.length > 0) {
118128
+ const outcomes = await attachSlackFilesForAgent(client, attachTo, toAttach.map(({ raw }) => raw), agent.id);
118129
+ for (const { info } of toAttach) {
118130
+ const outcome = outcomes.get(info.id);
118131
+ if (outcome && "attachment" in outcome) {
118132
+ info.attachmentId = outcome.attachment.id;
118133
+ info.fetchCommand = outcome.fetchCommand;
118134
+ } else {
118135
+ info.notAttached = outcome?.reason ?? "not downloaded";
118136
+ }
118137
+ }
118138
+ }
117606
118139
  const textOutput = messages.map((m) => {
117607
118140
  let text = `[${m.username || m.user || "Unknown"}]: ${m.text}`;
117608
118141
  if (m.files && m.files.length > 0) {
117609
118142
  const fileList = m.files.map((f) => {
117610
118143
  let line = ` - ${f.name} (${f.mimetype}, ${Math.round(f.size / 1024)} KB)`;
117611
- if (f.localPath) {
117612
- line += ` [Downloaded: ${f.localPath}]`;
118144
+ if (f.fetchCommand) {
118145
+ line += ` [Task attachment ${f.attachmentId}: ${f.fetchCommand}]`;
118146
+ } else if (f.notAttached) {
118147
+ line += ` [Not attached: ${f.notAttached}]`;
118148
+ } else if (f.localPath) {
118149
+ line += ` [Downloaded on the API server: ${f.localPath}]`;
117613
118150
  }
117614
118151
  return line;
117615
118152
  }).join(`
@@ -118222,379 +118759,6 @@ var registerSteerTaskTool = (server) => {
118222
118759
  // src/tools/store-progress.ts
118223
118760
  init_zod();
118224
118761
  init_db();
118225
-
118226
- // src/be/memory/raters/explicit-self.ts
118227
- class ExplicitSelfRatingRater {
118228
- name = "explicit-self";
118229
- async rate() {
118230
- return [];
118231
- }
118232
- }
118233
-
118234
- // src/be/memory/raters/implicit-citation.ts
118235
- class ImplicitCitationRater {
118236
- name = "implicit-citation";
118237
- async rate(ctx) {
118238
- if (ctx.retrievedMemoryIds.length === 0)
118239
- return [];
118240
- const evidence = ctx.evidence ?? "";
118241
- const events = [];
118242
- for (const memoryId of ctx.retrievedMemoryIds) {
118243
- if (evidence.length > 0 && evidence.includes(memoryId)) {
118244
- events.push({ memoryId, signal: 1, weight: 0.5, source: "" });
118245
- } else {
118246
- events.push({ memoryId, signal: -1, weight: 0.25, source: "" });
118247
- }
118248
- }
118249
- return events;
118250
- }
118251
- }
118252
-
118253
- // src/be/memory/raters/noop.ts
118254
- class NoopRater {
118255
- name = "noop";
118256
- async rate() {
118257
- return [];
118258
- }
118259
- }
118260
-
118261
- // src/be/memory/raters/registry.ts
118262
- var FACTORIES = {
118263
- noop: () => new NoopRater,
118264
- "implicit-citation": () => new ImplicitCitationRater,
118265
- "explicit-self": () => new ExplicitSelfRatingRater,
118266
- llm: () => new LlmRater
118267
- };
118268
- var SERVER_RATERS = new Set(["implicit-citation"]);
118269
- function getRegisteredRaters() {
118270
- const raw = process.env.MEMORY_RATERS;
118271
- if (!raw || raw.trim() === "") {
118272
- return [new NoopRater];
118273
- }
118274
- const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
118275
- const raters = [];
118276
- for (const name of names) {
118277
- const factory = FACTORIES[name];
118278
- if (!factory) {
118279
- console.warn(`[memory-rater] Unknown rater "${name}" in MEMORY_RATERS — skipping`);
118280
- continue;
118281
- }
118282
- raters.push(factory());
118283
- }
118284
- if (raters.length === 0) {
118285
- return [new NoopRater];
118286
- }
118287
- return raters;
118288
- }
118289
- function getRaterWeightMultiplier(name) {
118290
- const raw = process.env.MEMORY_RATER_WEIGHTS;
118291
- if (!raw || raw.trim() === "")
118292
- return 1;
118293
- for (const pair of raw.split(",")) {
118294
- const trimmed = pair.trim();
118295
- if (trimmed === "")
118296
- continue;
118297
- const [rawName, rawMult] = trimmed.split(":");
118298
- if (!rawName || !rawMult)
118299
- continue;
118300
- if (rawName.trim() !== name)
118301
- continue;
118302
- const mult = Number(rawMult);
118303
- if (Number.isFinite(mult) && mult >= 0)
118304
- return mult;
118305
- }
118306
- return 1;
118307
- }
118308
-
118309
- // src/be/memory/raters/store.ts
118310
- init_db();
118311
- class ExplicitSelfDuplicateError extends Error {
118312
- event;
118313
- constructor(message, event) {
118314
- super(message);
118315
- this.event = event;
118316
- this.name = "ExplicitSelfDuplicateError";
118317
- }
118318
- }
118319
- async function applyRating(events, ctx = {}) {
118320
- if (events.length === 0) {
118321
- return { applied: 0, rejected: [] };
118322
- }
118323
- const accepted = [];
118324
- const rejected = [];
118325
- for (const event of events) {
118326
- const reason = validate(event);
118327
- if (reason) {
118328
- rejected.push({ event, reason });
118329
- continue;
118330
- }
118331
- let sanitizedReferencesSource = null;
118332
- if (event.referencesSource !== undefined) {
118333
- if (event.referencesSource.length === 0) {
118334
- rejected.push({ event, reason: "referencesSource must be non-empty" });
118335
- continue;
118336
- }
118337
- if (event.referencesSource.length > REFERENCES_SOURCE_MAX_LENGTH) {
118338
- rejected.push({
118339
- event,
118340
- reason: `referencesSource exceeds ${REFERENCES_SOURCE_MAX_LENGTH} chars`
118341
- });
118342
- continue;
118343
- }
118344
- sanitizedReferencesSource = sanitizeReferencesSource(event.referencesSource);
118345
- if (sanitizedReferencesSource === null) {
118346
- rejected.push({
118347
- event,
118348
- reason: "referencesSource contains a NUL byte or strips to empty"
118349
- });
118350
- continue;
118351
- }
118352
- }
118353
- accepted.push({ event, sanitizedReferencesSource });
118354
- }
118355
- if (accepted.length === 0) {
118356
- return { applied: 0, rejected };
118357
- }
118358
- const UPDATE_MEMORY_SQL = "UPDATE agent_memory SET alpha = alpha + ?, beta = beta + ? WHERE id = ?";
118359
- const CHECK_EXISTS_SQL = "SELECT id FROM agent_memory WHERE id = ?";
118360
- const INSERT_RATING_SQL = `INSERT INTO memory_rating
118361
- (id, memoryId, taskId, source, signal, weight, reasoning, createdAt, contextKey)
118362
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
118363
- const UPSERT_EDGE_SQL = `INSERT INTO agent_memory_edge (from_id, to_id, type, alpha, beta, createdAt)
118364
- VALUES (?, ?, 'references-source', ?, ?, ?)
118365
- ON CONFLICT(from_id, to_id, type) DO UPDATE SET
118366
- alpha = alpha + excluded.alpha - 1.0,
118367
- beta = beta + excluded.beta - 1.0`;
118368
- const { applied, lateRejects, appliedEvents } = await getDbClient().transaction(async (tx) => {
118369
- let applied2 = 0;
118370
- const lateRejects2 = [];
118371
- const appliedEvents2 = [];
118372
- for (const { event, sanitizedReferencesSource } of accepted) {
118373
- const exists = await tx.get(CHECK_EXISTS_SQL, [event.memoryId]);
118374
- if (!exists) {
118375
- lateRejects2.push({ event, reason: "memoryId not found in agent_memory" });
118376
- continue;
118377
- }
118378
- const alphaDelta = Math.max(0, event.signal) * event.weight;
118379
- const betaDelta = Math.max(0, -event.signal) * event.weight;
118380
- await tx.run(UPDATE_MEMORY_SQL, [alphaDelta, betaDelta, event.memoryId]);
118381
- try {
118382
- await tx.run(INSERT_RATING_SQL, [
118383
- crypto.randomUUID(),
118384
- event.memoryId,
118385
- ctx.taskId ?? null,
118386
- event.source,
118387
- event.signal,
118388
- event.weight,
118389
- event.reasoning ?? null,
118390
- new Date().toISOString(),
118391
- ctx.contextKey ?? null
118392
- ]);
118393
- } catch (err) {
118394
- if (isUniqueConstraintError(err)) {
118395
- throw new ExplicitSelfDuplicateError(`duplicate explicit-self rating for memoryId=${event.memoryId} taskId=${ctx.taskId}`, event);
118396
- }
118397
- throw err;
118398
- }
118399
- if (sanitizedReferencesSource !== null) {
118400
- await tx.run(UPSERT_EDGE_SQL, [
118401
- event.memoryId,
118402
- sanitizedReferencesSource,
118403
- 1 + alphaDelta,
118404
- 1 + betaDelta,
118405
- new Date().toISOString()
118406
- ]);
118407
- }
118408
- appliedEvents2.push({ event, sanitizedReferencesSource });
118409
- applied2 += 1;
118410
- }
118411
- return { applied: applied2, lateRejects: lateRejects2, appliedEvents: appliedEvents2 };
118412
- });
118413
- if (ctx.taskId && appliedEvents.length > 0) {
118414
- for (const { event, sanitizedReferencesSource } of appliedEvents) {
118415
- ensure({
118416
- id: "memory_rated",
118417
- flow: "task",
118418
- runId: ctx.taskId,
118419
- data: {
118420
- memoryId: event.memoryId,
118421
- source: event.source,
118422
- signal: event.signal,
118423
- weight: event.weight,
118424
- hasReferencesSource: sanitizedReferencesSource !== null
118425
- },
118426
- validator: (data) => typeof data.memoryId === "string" && data.memoryId.length > 0 && typeof data.source === "string" && data.source.length > 0 && typeof data.signal === "number" && data.signal >= -1 && data.signal <= 1 && typeof data.weight === "number" && data.weight >= 0 && data.weight <= 1
118427
- });
118428
- }
118429
- }
118430
- return { applied, rejected: [...rejected, ...lateRejects] };
118431
- }
118432
- function validate(event) {
118433
- if (!event.source || event.source.trim() === "") {
118434
- return "source is required";
118435
- }
118436
- if (!Number.isFinite(event.signal) || event.signal < -1 || event.signal > 1) {
118437
- return "signal must be in [-1, +1]";
118438
- }
118439
- if (!Number.isFinite(event.weight) || event.weight < 0 || event.weight > 1) {
118440
- return "weight must be in [0, 1]";
118441
- }
118442
- if (!event.memoryId) {
118443
- return "memoryId is required";
118444
- }
118445
- return null;
118446
- }
118447
- function isUniqueConstraintError(err) {
118448
- if (!(err instanceof Error))
118449
- return false;
118450
- return /UNIQUE constraint failed|SQLITE_CONSTRAINT/i.test(err.message);
118451
- }
118452
-
118453
- // src/be/memory/raters/run-server-raters.ts
118454
- async function runServerRaters(input, deps = {}) {
118455
- const result = { ratersFired: 0, outcomes: [] };
118456
- if (input.retrievedMemoryIds.length === 0)
118457
- return result;
118458
- const allRaters = deps.raters ?? getRegisteredRaters();
118459
- const allowed = deps.serverRaterNames ?? SERVER_RATERS;
118460
- const weightFor = deps.weightMultiplierFor ?? getRaterWeightMultiplier;
118461
- const applyFn = deps.applyRating ?? applyRating;
118462
- const serverRaters = allRaters.filter((r) => allowed.has(r.name));
118463
- for (const rater of serverRaters) {
118464
- const events = await rater.rate({
118465
- taskId: input.taskId,
118466
- agentId: input.agentId,
118467
- retrievedMemoryIds: input.retrievedMemoryIds,
118468
- evidence: input.evidence
118469
- });
118470
- if (events.length === 0)
118471
- continue;
118472
- const multiplier = weightFor(rater.name);
118473
- const stamped = events.map((e) => ({
118474
- ...e,
118475
- source: rater.name,
118476
- weight: Math.max(0, Math.min(1, e.weight * multiplier))
118477
- }));
118478
- const applied = await applyFn(stamped, { taskId: input.taskId });
118479
- result.ratersFired += 1;
118480
- result.outcomes.push({ rater: rater.name, events: stamped, result: applied });
118481
- }
118482
- return result;
118483
- }
118484
-
118485
- // src/memory/automatic-task-gate.ts
118486
- var SCHEDULE_TAG_PREFIX = "schedule:";
118487
- var AUTOMATIC_TASK_TYPES = new Set([
118488
- "boot-triage",
118489
- "heartbeat",
118490
- "heartbeat-checklist",
118491
- "health-check",
118492
- "health-probe",
118493
- "monitor",
118494
- "monitoring"
118495
- ]);
118496
- function isAutomaticOrRecurringTaskCompletion(task) {
118497
- const tags = task.tags ?? [];
118498
- const taskType = task.taskType?.toLowerCase();
118499
- return task.source === "schedule" || task.source === "system" || tags.includes("scheduled") || tags.includes("auto-generated") || tags.some((tag) => tag.startsWith(SCHEDULE_TAG_PREFIX)) || taskType !== undefined && (AUTOMATIC_TASK_TYPES.has(taskType) || taskType.endsWith("-monitor") || taskType.endsWith("-digest"));
118500
- }
118501
- function shouldPersistAutomaticTaskMemory(task, persistMemory) {
118502
- if (persistMemory)
118503
- return true;
118504
- return !isAutomaticOrRecurringTaskCompletion(task);
118505
- }
118506
- var shouldPersistTaskCompletionMemory = shouldPersistAutomaticTaskMemory;
118507
-
118508
- // src/tasks/terminal-result-guard.ts
118509
- init_db();
118510
- init_types();
118511
- init_secret_scrubber();
118512
- function getTaskOutputValidationError(outputSchema, output) {
118513
- if (!outputSchema || typeof outputSchema !== "object")
118514
- return;
118515
- const schema = outputSchema;
118516
- if (!output) {
118517
- return `Task has an outputSchema but no output was provided. You must call store-progress with a valid JSON output matching this schema:
118518
- ${JSON.stringify(schema, null, 2)}`;
118519
- }
118520
- let parsed;
118521
- try {
118522
- parsed = JSON.parse(output);
118523
- } catch {
118524
- return `Task output must be valid JSON matching the outputSchema. Got invalid JSON. Schema:
118525
- ${JSON.stringify(schema, null, 2)}`;
118526
- }
118527
- let validationErrors;
118528
- try {
118529
- validationErrors = validateJsonSchema(schema, parsed);
118530
- } catch (error) {
118531
- const detail = error instanceof Error ? error.message : String(error);
118532
- return `Task outputSchema is malformed and cannot be validated (${detail}). Ask an operator to correct the task's outputSchema, or force-complete with store-progress force: true.`;
118533
- }
118534
- if (validationErrors.length > 0) {
118535
- return `Task output does not match the outputSchema. Errors:
118536
- ${validationErrors.join(`
118537
- `)}
118538
-
118539
- Expected schema:
118540
- ${JSON.stringify(schema, null, 2)}
118541
-
118542
- Please fix your output and retry.`;
118543
- }
118544
- return;
118545
- }
118546
- async function guardTerminalTaskResultWrite(task, write2) {
118547
- if (!isTerminalTaskStatus(task.status) || !write2.status && !write2.force) {
118548
- return { handled: false };
118549
- }
118550
- const scrubbedOutput = write2.output !== undefined ? scrubSecrets(write2.output) : undefined;
118551
- const scrubbedFailureReason = write2.failureReason !== undefined ? scrubSecrets(write2.failureReason) : undefined;
118552
- const hasDifferingOutput = scrubbedOutput !== undefined && scrubbedOutput !== task.output;
118553
- const hasDifferingFailureReason = scrubbedFailureReason !== undefined && scrubbedFailureReason !== task.failureReason;
118554
- const hasDifferingResultText = hasDifferingOutput || hasDifferingFailureReason;
118555
- if (hasDifferingResultText && write2.force) {
118556
- const outputValidationError = hasDifferingOutput ? getTaskOutputValidationError(task.outputSchema, write2.output) : undefined;
118557
- if (outputValidationError) {
118558
- return { handled: true, success: false, message: outputValidationError, task };
118559
- }
118560
- const overwrittenTask = await overwriteTerminalTaskResultText(task.id, {
118561
- ...write2.output !== undefined ? { output: write2.output } : {},
118562
- ...write2.failureReason !== undefined ? { failureReason: write2.failureReason } : {}
118563
- });
118564
- if (!overwrittenTask) {
118565
- return {
118566
- handled: true,
118567
- success: false,
118568
- message: `Task "${task.id}" terminal result text could not be overwritten.`,
118569
- task
118570
- };
118571
- }
118572
- return {
118573
- handled: true,
118574
- success: true,
118575
- message: `Task "${task.id}" is already ${task.status}; force-overwrote terminal result text ` + "without replaying completion side effects.",
118576
- task: overwrittenTask,
118577
- wasForcedOverwrite: true
118578
- };
118579
- }
118580
- if (hasDifferingResultText) {
118581
- return {
118582
- handled: true,
118583
- success: false,
118584
- message: `Discarded write for already-${task.status} task "${task.id}"; ` + "existing output/failureReason and finishedAt were preserved. Retry with force: true " + "to overwrite terminal result text without replaying completion side effects.",
118585
- task
118586
- };
118587
- }
118588
- return {
118589
- handled: true,
118590
- success: true,
118591
- message: `Task "${task.id}" is already ${task.status}; treating as no-op. ` + "Existing output preserved (first-call-wins).",
118592
- task,
118593
- wasNoOp: true
118594
- };
118595
- }
118596
-
118597
- // src/tools/store-progress.ts
118598
118762
  init_types();
118599
118763
  init_secret_scrubber();
118600
118764
  var storeProgressOutputSchema = swarmToolOutputSchema({
@@ -118814,91 +118978,15 @@ var registerStoreProgressTool = (server) => {
118814
118978
  };
118815
118979
  });
118816
118980
  const shouldRunTerminalSideEffects = (status === "completed" || status === "failed") && result.success && result.task && !(("wasNoOp" in result) && result.wasNoOp) && !(("wasForcedOverwrite" in result) && result.wasForcedOverwrite);
118817
- if (shouldRunTerminalSideEffects && shouldPersistTaskCompletionMemory(result.task, persistMemory)) {
118818
- (async () => {
118819
- try {
118820
- const taskContent = status === "completed" ? `Task: ${result.task.task}
118821
-
118822
- Output:
118823
- ${output || "(no output)"}` : `Task: ${result.task.task}
118824
-
118825
- Failure reason:
118826
- ${failureReason || "No reason provided"}
118827
-
118828
- This task failed. Learn from this to avoid repeating the mistake.`;
118829
- if (taskContent.length < 30)
118830
- return;
118831
- const store = getMemoryStore();
118832
- const provider = getEmbeddingProvider();
118833
- const memory = await store.store({
118834
- agentId: requestInfo.agentId ?? null,
118835
- content: taskContent,
118836
- name: `Task: ${result.task.task.slice(0, 80)}`,
118837
- scope: "agent",
118838
- source: "task_completion",
118839
- sourceTaskId: taskId
118840
- });
118841
- const embedding = await provider.embed(taskContent);
118842
- if (embedding) {
118843
- await store.updateEmbedding(memory.id, embedding, provider.name);
118844
- }
118845
- const shouldShareWithSwarm = status === "completed" && (result.task.taskType === "research" || result.task.tags?.includes("knowledge") || result.task.tags?.includes("shared"));
118846
- if (shouldShareWithSwarm) {
118847
- try {
118848
- const swarmMemory = await store.store({
118849
- agentId: requestInfo.agentId ?? null,
118850
- scope: "swarm",
118851
- name: `Shared: ${result.task.task.slice(0, 80)}`,
118852
- content: `Task completed by agent ${requestInfo.agentId}:
118853
-
118854
- ${taskContent}`,
118855
- source: "task_completion",
118856
- sourceTaskId: taskId
118857
- });
118858
- const swarmEmbedding = await provider.embed(taskContent);
118859
- if (swarmEmbedding) {
118860
- await store.updateEmbedding(swarmMemory.id, swarmEmbedding, provider.name);
118861
- }
118862
- } catch {}
118863
- }
118864
- } catch {}
118865
- })().catch((err) => console.error("[store-progress] task completion memory write failed:", scrubSecrets(err instanceof Error ? err.message : String(err))));
118866
- }
118867
- if (shouldRunTerminalSideEffects) {
118868
- (async () => {
118869
- try {
118870
- const retrievals = await getRetrievalsForTask(taskId);
118871
- if (retrievals.length === 0)
118872
- return;
118873
- const retrievedMemoryIds = retrievals.map((r) => r.memoryId);
118874
- const logs = await getSessionLogsByTaskId(taskId);
118875
- const evidence = logs.map((l) => l.content).join(`
118876
- `);
118877
- await runServerRaters({
118878
- taskId,
118879
- agentId: requestInfo.agentId ?? "",
118880
- retrievedMemoryIds,
118881
- evidence
118882
- });
118883
- } catch (err) {
118884
- console.error("[store-progress] server-rater fire failed:", err instanceof Error ? err.message : String(err));
118885
- }
118886
- })().catch((err) => console.error("[store-progress] server rater run failed:", scrubSecrets(err instanceof Error ? err.message : String(err))));
118887
- }
118888
- if (status && result.success && result.task && !result.task.workflowRunId && !(("wasNoOp" in result) && result.wasNoOp) && !(("wasForcedOverwrite" in result) && result.wasForcedOverwrite)) {
118889
- try {
118890
- const followUp = await createWorkerTaskFollowUp({
118891
- task: result.task,
118892
- status,
118893
- output,
118894
- failureReason
118895
- });
118896
- if (followUp) {
118897
- console.log(`[store-progress] Created follow-up task ${followUp.id.slice(0, 8)} for ${status} task ${taskId.slice(0, 8)}`);
118898
- }
118899
- } catch (err) {
118900
- console.warn(`[store-progress] Failed to create follow-up task: ${err}`);
118901
- }
118981
+ if (shouldRunTerminalSideEffects && status) {
118982
+ await runTaskTerminalEffects({
118983
+ task: result.task,
118984
+ status,
118985
+ output,
118986
+ failureReason,
118987
+ agentId: requestInfo.agentId,
118988
+ persistMemory
118989
+ });
118902
118990
  }
118903
118991
  const { success, message } = result;
118904
118992
  const task = result.task ? {
@@ -121350,6 +121438,7 @@ async function createServer(opts = {}) {
121350
121438
  if (hasCapability2("scheduling")) {
121351
121439
  registerListSchedulesTool(server);
121352
121440
  registerCreateScheduleTool(server);
121441
+ registerDeferTaskTool(server);
121353
121442
  registerUpdateScheduleTool(server);
121354
121443
  registerPatchScheduleTool(server);
121355
121444
  registerDeleteScheduleTool(server);
@@ -121510,7 +121599,7 @@ function isExplicitSelfRaterEnabled() {
121510
121599
 
121511
121600
  // src/utils/skill-fs-writer.ts
121512
121601
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
121513
- import { dirname as dirname2, join as join3 } from "node:path";
121602
+ import { dirname, join as join3 } from "node:path";
121514
121603
  var SWARM_MARKER_FILE = ".swarm-managed";
121515
121604
  function reconcileManagedSkillFiles(skillDir, currentRelativeFiles) {
121516
121605
  if (!existsSync2(join3(skillDir, SWARM_MARKER_FILE)))
@@ -121614,7 +121703,7 @@ function writeSkillsToFilesystem(entries, harnessType = "all", home) {
121614
121703
  }
121615
121704
  const targetPath = join3(skillDir, file.path);
121616
121705
  try {
121617
- mkdirSync2(dirname2(targetPath), { recursive: true });
121706
+ mkdirSync2(dirname(targetPath), { recursive: true });
121618
121707
  writeFileSync2(targetPath, file.content, "utf-8");
121619
121708
  } catch (err) {
121620
121709
  const msg = err instanceof Error ? err.message : "Unknown error";
@@ -121647,4 +121736,4 @@ function writeSkillsToFilesystem(entries, harnessType = "all", home) {
121647
121736
  return { synced, removed, errors };
121648
121737
  }
121649
121738
 
121650
- export { safeParseAsync2 as safeParseAsync, normalizeObjectSchema, getParseErrorMessage, DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, isInboxAllowed, isSenderAllowed, handleMessageReceived, enqueueAuditRow, enqueueAdmissionRow, flushAuditBuffer, startAuditWriter, stopAuditWriter, startAuditGc, stopAuditGc, handlePullRequest, handleIssue, handleComment, handlePullRequestReview, handleCheckRun, handleCheckSuite, handleWorkflowRun, isGitLabEnabled, initGitLab, verifyGitLabWebhook, handleMergeRequest, handleIssue2 as handleIssue1, handleNote, handlePipeline, getJiraMetadata, updateJiraMetadata, clearJiraMetadata, registerJiraWebhook, deleteJiraWebhook, isJiraEnabled, initJira, handleJiraWebhook, handleAgentSessionEvent, handleIssueUpdate, handleIssueDelete, handleAgentSessionPrompted, isLinearEnabled, initLinear, parseCookieHeader, extractAndVerifyCookie, issuePageSessionCookie, resolveHttpRequestAuth, warnIfCorsAllowsAnyOrigin, isOriginAllowedForCredentials, setCorsHeaders, parseQueryParams, getPathSegments, wireHttpSpanLifecycle, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, runtimeInstanceHeader, describeRequestRoute, route, loadGlobalConfigsIntoEnv, reloadGlobalConfigsAndIntegrations, scheduleIntegrationsReload, handleCore, mcpOverflowAuthError, markScriptSdkRequestOrigin, toolErr, createToolRegistrar, userCtx, resolveTaskAuditUserId, resolveHttpAuditUserId, resolveHttpFavoriteOwner, handleApps, getUserCommsPrefs, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, executeReadOnlyQuery, assertSelectOnlyQuery, handleDbQuery, snapshotMetric, AssetKeyAuthorizationError, authorizeAssetKeyWrite, snapshotPage, assertOAuthAppUrlsSafe, assertOAuthEgressUrlSafe, listOAuthPresets, listOAuthPresetIds, getOAuthPreset, hydrateOAuthAppFromPreset, scopedResourceScopeIdSchema, resolveScopedResourceId, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, reservedNamespaceError, reservedRoomKeyError, refreshLinks, canReadMemory, getLinksForMemory, dedupeMemoryDocumentIds, recordMemoryAccesses, recordRetrievals, expandCandidatesWithGraph, indexMemoryContent, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, resolveRoomNamespace, roomRequestInfo, authorizeRoomNamespace, RoomOperationSchema, removeAwarenessStates, roomTopic, decodeRoomSnapshot, roomView, getRoom, changeRoom, applyRoomUpdate, resetRoom, sweepRooms, closeRooms, removeNamespaceRooms, createEvent, createEventsBatch, getEventCountsFiltered, getEventsFiltered, mergeScheduleTiming, validateRecurringTiming, checkOutputSchemaShape, sendTaskOutputSchema, sendTaskHandler, parseSkillContent, steerTaskUserInputSchema, steerTaskOutputSchema, steerTaskHandler, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, guardTerminalTaskResultWrite, canClaim, emitBudgetRefusalSideEffects, taskActionInputSchema, taskActionOutputSchema, taskActionHandler, patchWorkflowDefinition, resolveScriptsOnlyMode, ALL_CAPABILITIES, hasCapability, getEnabledCapabilities, createServer, SIMILARITY_THRESHOLD, renderMemoriesPrompt, writeSkillsToFilesystem };
121739
+ export { safeParseAsync2 as safeParseAsync, normalizeObjectSchema, getParseErrorMessage, DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, isInboxAllowed, isSenderAllowed, handleMessageReceived, enqueueAuditRow, enqueueAdmissionRow, flushAuditBuffer, startAuditWriter, stopAuditWriter, startAuditGc, stopAuditGc, handlePullRequest, handleIssue, handleComment, handlePullRequestReview, handleCheckRun, handleCheckSuite, handleWorkflowRun, isGitLabEnabled, initGitLab, verifyGitLabWebhook, handleMergeRequest, handleIssue2 as handleIssue1, handleNote, handlePipeline, getJiraMetadata, updateJiraMetadata, clearJiraMetadata, registerJiraWebhook, deleteJiraWebhook, isJiraEnabled, initJira, handleJiraWebhook, handleAgentSessionEvent, handleIssueUpdate, handleIssueDelete, handleAgentSessionPrompted, isLinearEnabled, initLinear, parseCookieHeader, extractAndVerifyCookie, issuePageSessionCookie, resolveHttpRequestAuth, warnIfCorsAllowsAnyOrigin, isOriginAllowedForCredentials, setCorsHeaders, parseQueryParams, getPathSegments, wireHttpSpanLifecycle, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, runtimeInstanceHeader, describeRequestRoute, route, loadGlobalConfigsIntoEnv, reloadGlobalConfigsAndIntegrations, scheduleIntegrationsReload, handleCore, mcpOverflowAuthError, markScriptSdkRequestOrigin, toolErr, createToolRegistrar, userCtx, resolveTaskAuditUserId, resolveHttpAuditUserId, resolveHttpFavoriteOwner, handleApps, getUserCommsPrefs, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, executeReadOnlyQuery, assertSelectOnlyQuery, handleDbQuery, snapshotMetric, AssetKeyAuthorizationError, authorizeAssetKeyWrite, snapshotPage, assertOAuthAppUrlsSafe, assertOAuthEgressUrlSafe, listOAuthPresets, listOAuthPresetIds, getOAuthPreset, hydrateOAuthAppFromPreset, scopedResourceScopeIdSchema, resolveScopedResourceId, dedupeMemoryDocumentIds, recordMemoryAccesses, recordRetrievals, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, guardTerminalTaskResultWrite, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, reservedNamespaceError, reservedRoomKeyError, refreshLinks, canReadMemory, getLinksForMemory, expandCandidatesWithGraph, indexMemoryContent, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, resolveRoomNamespace, roomRequestInfo, authorizeRoomNamespace, RoomOperationSchema, removeAwarenessStates, roomTopic, decodeRoomSnapshot, roomView, getRoom, changeRoom, applyRoomUpdate, resetRoom, sweepRooms, closeRooms, removeNamespaceRooms, createEvent, createEventsBatch, getEventCountsFiltered, getEventsFiltered, mergeScheduleTiming, validateRecurringTiming, checkOutputSchemaShape, sendTaskOutputSchema, sendTaskHandler, parseSkillContent, steerTaskUserInputSchema, steerTaskOutputSchema, steerTaskHandler, canClaim, emitBudgetRefusalSideEffects, taskActionInputSchema, taskActionOutputSchema, taskActionHandler, patchWorkflowDefinition, resolveScriptsOnlyMode, ALL_CAPABILITIES, hasCapability, getEnabledCapabilities, createServer, SIMILARITY_THRESHOLD, renderMemoriesPrompt, writeSkillsToFilesystem };