@desplega.ai/agent-swarm 1.152.2 → 1.154.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 (284) hide show
  1. package/README.md +2 -2
  2. package/dist/{acp-adapter-pfeje2v5.js → acp-adapter-eb2aj8xy.js} +4 -4
  3. package/dist/{actions-0sttc7f5.js → actions-kpnwp1kz.js} +31 -27
  4. package/dist/{anthropic-messages-2y8nktar.js → anthropic-messages-9vqsbznt.js} +112 -84
  5. package/dist/{app-p8teva95.js → app-zk1apkf6.js} +10 -7
  6. package/dist/{assistant-r45jnsrt.js → assistant-kwq4y50q.js} +32 -28
  7. package/dist/{azure-openai-responses-f38x2avq.js → azure-openai-responses-04qwdwfq.js} +27 -13
  8. package/dist/{boot-reembed-je26h5y7.js → boot-reembed-22hd3jjn.js} +9 -7
  9. package/dist/{boot-reembed-1qmybsd4.js → boot-reembed-qe168424.js} +10 -8
  10. package/dist/{boot-scrub-logs-gte3h1mm.js → boot-scrub-logs-pkdpsrjd.js} +7 -5
  11. package/dist/{claude-adapter-csc1mzka.js → claude-adapter-3fx90nww.js} +18 -16
  12. package/dist/{claude-managed-adapter-dsmkcrjy.js → claude-managed-adapter-ea2aga6w.js} +3 -3
  13. package/dist/{claude-sdk-session-fcd6f078.js → claude-sdk-session-z7fahf1k.js} +18 -16
  14. package/dist/{cli-r2fpe7cx.js → cli-00w2c66p.js} +61 -55
  15. package/dist/{cli-wbyk465z.js → cli-09y3tjxz.js} +1 -1
  16. package/dist/{cli-ht5rs63n.js → cli-0e31nm4n.js} +16 -7
  17. package/dist/cli-0x5qzvkg.js +4279 -0
  18. package/dist/{cli-d3p82erb.js → cli-18amys3y.js} +9 -9
  19. package/dist/{cli-4ahpr4sw.js → cli-1jpjafw4.js} +5 -5
  20. package/dist/{cli-jbmc0r7r.js → cli-243tzws8.js} +1 -1
  21. package/dist/{cli-ch38q5z4.js → cli-24k806sc.js} +6789 -2542
  22. package/dist/cli-3bkz2p7j.js +2828 -0
  23. package/dist/cli-3er6gcsn.js +1849 -0
  24. package/dist/cli-4k7jazkq.js +3339 -0
  25. package/dist/{cli-c4eeb5q2.js → cli-5b6b62tz.js} +1 -1
  26. package/dist/{cli-qd65dvr6.js → cli-5vs0nh43.js} +65 -44
  27. package/dist/{cli-z3wkng35.js → cli-6r3cgpk2.js} +224 -156
  28. package/dist/{cli-pgj4a5fb.js → cli-71cj99hx.js} +4 -3
  29. package/dist/cli-8sea27q4.js +14075 -0
  30. package/dist/{cli-jvy9qgjs.js → cli-913yakhv.js} +22 -7
  31. package/dist/{cli-pf9q3696.js → cli-927erqqt.js} +5 -2
  32. package/dist/cli-95jnpnqg.js +230 -0
  33. package/dist/{cli-wpv5rgg2.js → cli-abp3g4ax.js} +15 -2
  34. package/dist/{cli-ckz5t2hb.js → cli-acy6bfhr.js} +10 -7
  35. package/dist/{cli-4yc5d8fk.js → cli-avkd6mqv.js} +50 -0
  36. package/dist/{cli-8wrympd7.js → cli-bzr0ftht.js} +26 -7
  37. package/dist/{cli-qn042eh7.js → cli-c1v10xb2.js} +194 -1913
  38. package/dist/cli-cgj4hc12.js +18902 -0
  39. package/dist/{cli-2nnd56zt.js → cli-e0pg4zeh.js} +3 -2
  40. package/dist/{cli-r84ctdxn.js → cli-e31ehqwy.js} +2 -2
  41. package/dist/{cli-gphgnvpf.js → cli-e3229na9.js} +3 -3
  42. package/dist/{cli-gpqcs935.js → cli-e3cyf59d.js} +2 -2
  43. package/dist/{cli-s25avqg9.js → cli-e781c6f9.js} +1 -0
  44. package/dist/{cli-s2qjdd6z.js → cli-eetrvv91.js} +1 -1
  45. package/dist/{cli-j4nv2wqy.js → cli-fdzg6jhf.js} +10 -4
  46. package/dist/{cli-fm4ytsdk.js → cli-g2h29qar.js} +2 -2
  47. package/dist/{cli-zvgj5zg8.js → cli-ge67p080.js} +302 -3497
  48. package/dist/cli-h38x0qhg.js +6912 -0
  49. package/dist/{cli-9xvdm3bx.js → cli-h60qfasa.js} +742 -278
  50. package/dist/{cli-jew3jda5.js → cli-hws0vn86.js} +2 -2
  51. package/dist/{cli-j8dggbcr.js → cli-k0g1sjzm.js} +23 -31
  52. package/dist/{cli-xw7vak96.js → cli-kp3qs53m.js} +5 -5
  53. package/dist/{cli-rgvp6r2x.js → cli-mcnhgnn1.js} +193 -4
  54. package/dist/{cli-zvkq5csn.js → cli-myaxtgh8.js} +7 -6
  55. package/dist/{cli-56h18ng2.js → cli-n62atwh9.js} +1 -1
  56. package/dist/cli-n7h1kqpf.js +29 -0
  57. package/dist/{cli-fmy65jdn.js → cli-pf1v8xhb.js} +7 -5
  58. package/dist/{cli-xy1cchaf.js → cli-q85qhckh.js} +238 -11
  59. package/dist/{cli-3bpzj8me.js → cli-r1d3dzt6.js} +2 -2
  60. package/dist/{pi-mono-adapter-jcqt71cg.js → cli-rxkqvngf.js} +67343 -101083
  61. package/dist/{cli-kj76p1ez.js → cli-scwq51gn.js} +13 -7
  62. package/dist/{cli-b9dd0h3k.js → cli-stwsmd6k.js} +3 -2
  63. package/dist/{cli-c2mk1kgf.js → cli-v4en75c5.js} +8 -1
  64. package/dist/{cli-z61ykvam.js → cli-wg04s9kr.js} +4 -3
  65. package/dist/cli-yn0a417a.js +4069 -0
  66. package/dist/{cli-zfzznn7c.js → cli-ytxs7mn1.js} +26 -2
  67. package/dist/{cli-4j8k1m4w.js → cli-zt1227k1.js} +1162 -192
  68. package/dist/cli.js +28 -35
  69. package/dist/{codex-adapter-fxmpsabw.js → codex-adapter-ttdm995v.js} +12 -11
  70. package/dist/{codex-hook-8jexc34x.js → codex-hook-8gx7bcz0.js} +3 -3
  71. package/dist/{codex-session-runner-rr1e47ea.js → codex-session-runner-3v8nh4c8.js} +12 -11
  72. package/dist/{commands-0f5rxz51.js → commands-1cxwbhhq.js} +7 -5
  73. package/dist/{db-r3jxf3wy.js → db-y9zwsr79.js} +25 -5
  74. package/dist/{dispatcher-06vph6q4.js → dispatcher-9m9zss36.js} +30 -26
  75. package/dist/dsh-adapter-gsb2qqf9.js +19 -0
  76. package/dist/{e2b-413b8t4v.js → e2b-93x1k0j5.js} +6 -7
  77. package/dist/{google-generative-ai-ndjmj5fa.js → google-generative-ai-gwwrnzqn.js} +33 -73
  78. package/dist/{google-vertex-fr665tqs.js → google-vertex-5wfawpwz.js} +38 -68
  79. package/dist/{handlers-0hgtc910.js → handlers-36fm1bwn.js} +33 -29
  80. package/dist/{hook-9kvrv1er.js → hook-pntw4r8h.js} +12 -10
  81. package/dist/{hook-fbfhkat3.js → hook-vwq8y26p.js} +13 -11
  82. package/dist/{http-xbyx8k5j.js → http-20jesakk.js} +357 -261
  83. package/dist/{index-jsw3aynb.js → index-6artrehg.js} +30 -26
  84. package/dist/{cli-pdz9qde4.js → index-9ch1n761.js} +10 -11
  85. package/dist/{index-rnyahfn5.js → index-9n5z2492.js} +30 -26
  86. package/dist/{index-66xz5e9j.js → index-h5gs261z.js} +521 -9
  87. package/dist/{index-tfsdxyv6.js → index-j5wrf1yw.js} +40 -25
  88. package/dist/{index-2sp0384z.js → index-kkeftv5h.js} +738 -21
  89. package/dist/{index-sgk50mbg.js → index-m2xqpz5r.js} +31 -27
  90. package/dist/{index-gpfn1741.js → index-m9ks8a7t.js} +9 -7
  91. package/dist/index-w097eg0x.js +5 -0
  92. package/dist/jiti-loader-b4837mtm.js +34 -0
  93. package/dist/jiti-static-loader-q2g5ds0y.js +31267 -0
  94. package/dist/{keepalive-s3xpr7sw.js → keepalive-pwxvxd2c.js} +9 -7
  95. package/dist/{lead-rq4n5r16.js → lead-m789wqa3.js} +46 -41
  96. package/dist/{maintenance-f0atj1ab.js → maintenance-bvg6w7e2.js} +10 -8
  97. package/dist/{mistral-conversations-qhr84ph3.js → mistral-conversations-zx0kn554.js} +24 -18
  98. package/dist/{node-x1g4v0pq.js → node-5qtycf6a.js} +16 -11
  99. package/dist/{oauth-refresh-sweep-q0t3bxre.js → oauth-refresh-sweep-6v4bd737.js} +8 -6
  100. package/dist/{onboard-br5j1kch.js → onboard-snckpcj1.js} +115 -35
  101. package/dist/{openai-codex-responses-wy6a43wd.js → openai-codex-responses-4vjqzvjp.js} +33 -27
  102. package/dist/{openai-completions-32rdwmze.js → openai-completions-pxsmym7g.js} +52 -64
  103. package/dist/{openai-responses-6xwf7bzg.js → openai-responses-tmn0rqp0.js} +23 -24
  104. package/dist/{opencode-adapter-28axqa09.js → opencode-adapter-qbmv4tpy.js} +6 -6
  105. package/dist/{otel-impl-vdqge2nt.js → otel-impl-e0ytens5.js} +4 -4
  106. package/dist/{pi-messages-b1mbr6gp.js → pi-messages-txpjj39q.js} +3 -3
  107. package/dist/pi-mono-adapter-c4ggemhz.js +1316 -0
  108. package/dist/{pricing-refresh-hb3691fd.js → pricing-refresh-52g2ywrw.js} +9 -7
  109. package/dist/{rbac-roles-t78xt39b.js → rbac-roles-5455kfdn.js} +8 -6
  110. package/dist/{rbac-roles-jh280k9g.js → rbac-roles-hbf0xa52.js} +9 -7
  111. package/dist/{render-v2-7p8r2ymg.js → render-v2-npm8xdzh.js} +16 -9
  112. package/dist/{seed-pricing-hf889enh.js → seed-pricing-650wbr1n.js} +8 -6
  113. package/dist/{setup-w6mb03jv.js → setup-k1mew5rf.js} +4 -5
  114. package/dist/virtual-modules-2q5b2pk4.js +54 -0
  115. package/dist/{work-objects-nt8gy1kj.js → work-objects-2ea3kvwg.js} +33 -29
  116. package/dist/{worker-dwy617t0.js → worker-ec6hvc4m.js} +46 -41
  117. package/openapi.json +109 -16
  118. package/package.json +7 -6
  119. package/plugin/opencode-plugins/lib/memory-raters.ts +7 -0
  120. package/src/be/automation-preflight.ts +6 -4
  121. package/src/be/db/agents.ts +75 -2
  122. package/src/be/db/tasks/read.ts +2 -0
  123. package/src/be/db/tasks/write.ts +7 -1
  124. package/src/be/db.ts +140 -18
  125. package/src/be/extensions/db.ts +35 -4
  126. package/src/be/memory/raters/llm.ts +2 -6
  127. package/src/be/memory/raters/noop.ts +2 -3
  128. package/src/be/memory/raters/registry.ts +4 -10
  129. package/src/be/migrations/157_defer_provenance_and_marker.sql +27 -0
  130. package/src/be/models-catalog.ts +2 -0
  131. package/src/be/modelsdev-cache.json +213 -0
  132. package/src/be/rbac-audit.ts +2 -0
  133. package/src/be/seed/agent-fs-provision.ts +23 -0
  134. package/src/be/seed-pricing.ts +3 -1
  135. package/src/be/swarm-config-guard.ts +1 -0
  136. package/src/commands/onboard/compose-generator.ts +16 -1
  137. package/src/commands/onboard/env-generator.ts +14 -1
  138. package/src/commands/onboard/manifest.ts +1 -0
  139. package/src/commands/onboard/steps/integration-slack.tsx +56 -16
  140. package/src/commands/onboard/steps/review.tsx +5 -0
  141. package/src/commands/onboard/types.ts +6 -0
  142. package/src/commands/onboard.tsx +5 -1
  143. package/src/commands/provider-credentials.ts +14 -2
  144. package/src/commands/runner.ts +18 -2
  145. package/src/extensions/identity.ts +15 -7
  146. package/src/extensions/lifecycle.ts +15 -21
  147. package/src/hooks/codex-hook.ts +7 -1
  148. package/src/http/agents.ts +12 -4
  149. package/src/http/config.ts +1 -1
  150. package/src/http/core.ts +5 -3
  151. package/src/http/extensions.ts +108 -34
  152. package/src/http/poll.ts +7 -0
  153. package/src/http/schedules.ts +10 -0
  154. package/src/http/script-runs.ts +35 -26
  155. package/src/http/status.ts +24 -11
  156. package/src/http/tasks.ts +6 -1
  157. package/src/prompts/base-prompt.ts +15 -7
  158. package/src/prompts/memories.ts +5 -7
  159. package/src/prompts/session-templates.ts +3 -2
  160. package/src/prompts/steering-delivery.ts +2 -0
  161. package/src/providers/claude-managed-models.ts +8 -0
  162. package/src/providers/codex-adapter.ts +3 -6
  163. package/src/providers/codex-models.ts +15 -1
  164. package/src/providers/dsh-adapter.ts +248 -0
  165. package/src/providers/index.ts +5 -1
  166. package/src/providers/modelsdev-reasoning.json +50 -0
  167. package/src/providers/pi-mono-extension.ts +2 -5
  168. package/src/rbac/legacy-policy.ts +15 -1
  169. package/src/rbac/types.ts +2 -0
  170. package/src/server.ts +7 -1
  171. package/src/slack/ack.ts +43 -8
  172. package/src/slack/app.ts +32 -10
  173. package/src/slack/config.ts +29 -0
  174. package/src/slack/handlers.ts +7 -1
  175. package/src/slack/render-v2.ts +186 -17
  176. package/src/slack/responses.ts +1 -3
  177. package/src/slack/task-output.ts +66 -8
  178. package/src/slack/thread-buffer.ts +27 -3
  179. package/src/telemetry.ts +2 -3
  180. package/src/tests/agent-fs-provision-seeder.test.ts +56 -2
  181. package/src/tests/agents-harness-provider.test.ts +19 -0
  182. package/src/tests/agents-list-model-display.test.ts +60 -1
  183. package/src/tests/base-prompt.test.ts +25 -0
  184. package/src/tests/claude-managed-adapter.test.ts +6 -0
  185. package/src/tests/codex-gpt6-release.test.ts +97 -0
  186. package/src/tests/codex-steering-hook.test.ts +2 -2
  187. package/src/tests/config-api-only-keys.test.ts +9 -0
  188. package/src/tests/context-window.test.ts +1 -0
  189. package/src/tests/db-task-read-contract.test.ts +9 -0
  190. package/src/tests/db-task-write-contract.test.ts +9 -0
  191. package/src/tests/defer-task.test.ts +115 -16
  192. package/src/tests/dsh-adapter.test.ts +276 -0
  193. package/src/tests/e2e-nightly-report.test.ts +3 -3
  194. package/src/tests/e2e-redact.test.ts +2 -0
  195. package/src/tests/env-flag.test.ts +6 -0
  196. package/src/tests/extension-agent-visibility.test.ts +474 -0
  197. package/src/tests/extensions-db.test.ts +41 -0
  198. package/src/tests/extensions-http.test.ts +148 -4
  199. package/src/tests/extensions-lifecycle.test.ts +21 -1
  200. package/src/tests/extensions-mcp-tools.test.ts +9 -5
  201. package/src/tests/fetch-resolved-env.test.ts +43 -0
  202. package/src/tests/internal-ai/complete-structured.test.ts +32 -9
  203. package/src/tests/internal-ai/credentials.test.ts +4 -4
  204. package/src/tests/internal-ai/models.test.ts +20 -0
  205. package/src/tests/memory-rate-tool.test.ts +7 -10
  206. package/src/tests/memory-rater-defaults.test.ts +23 -0
  207. package/src/tests/memory-rater-e2e.test.ts +2 -3
  208. package/src/tests/migration-046-budgets.test.ts +2 -0
  209. package/src/tests/model-control.test.ts +2 -2
  210. package/src/tests/onboard-compose.test.ts +14 -0
  211. package/src/tests/onboard-env.test.ts +17 -0
  212. package/src/tests/onboard-manifest.test.ts +12 -0
  213. package/src/tests/opus-5-5-registry.test.ts +48 -0
  214. package/src/tests/pricing-refresh.test.ts +1 -1
  215. package/src/tests/pricing-routes.test.ts +15 -3
  216. package/src/tests/provider-registration.test.ts +5 -2
  217. package/src/tests/rbac-admission-e2e.test.ts +54 -0
  218. package/src/tests/rbac-e2e-helpers.ts +2 -0
  219. package/src/tests/rbac-engine.test.ts +32 -0
  220. package/src/tests/rbac-wire-e2e.test.ts +33 -0
  221. package/src/tests/reasoning-effort.test.ts +8 -0
  222. package/src/tests/reload-config.test.ts +33 -0
  223. package/src/tests/script-connections.test.ts +3 -0
  224. package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
  225. package/src/tests/slack-config.test.ts +136 -0
  226. package/src/tests/slack-mode-lifecycle.test.ts +69 -0
  227. package/src/tests/slack-render-v2.test.ts +446 -16
  228. package/src/tests/slack-steering.test.ts +69 -1
  229. package/src/tests/slack-task-output.test.ts +33 -12
  230. package/src/tests/status.test.ts +22 -2
  231. package/src/tests/steering-core.test.ts +35 -0
  232. package/src/tests/steering-delivery.test.ts +39 -0
  233. package/src/tests/steering-transport.test.ts +2 -2
  234. package/src/tests/task-tool-preload-mcp.test.ts +3 -1
  235. package/src/tests/telemetry-init.test.ts +18 -0
  236. package/src/tests/workflow-live-recovery.test.ts +126 -0
  237. package/src/tests/workflow-llm.test.ts +3 -3
  238. package/src/tools/defer-task.ts +64 -26
  239. package/src/tools/extension-common.ts +4 -0
  240. package/src/tools/extension-install.ts +1 -1
  241. package/src/tools/extension-list.ts +4 -3
  242. package/src/tools/join-swarm.ts +1 -1
  243. package/src/tools/schedules/create-schedule.ts +11 -1
  244. package/src/tools/schedules/patch-schedule.ts +5 -0
  245. package/src/tools/schedules/update-schedule.ts +5 -0
  246. package/src/tools/send-task.ts +6 -0
  247. package/src/tools/utils.ts +1 -1
  248. package/src/types.ts +30 -8
  249. package/src/utils/context-window.ts +1 -0
  250. package/src/utils/credentials.ts +8 -0
  251. package/src/utils/internal-ai/complete-structured.ts +8 -0
  252. package/src/utils/internal-ai/models.ts +5 -5
  253. package/src/utils/memory-raters.ts +3 -0
  254. package/src/workflows/engine.ts +28 -0
  255. package/src/workflows/recovery.ts +7 -4
  256. package/templates/schedules/daily-status-report/content.md +25 -12
  257. package/templates/schedules/daily-workflow-health-audit/content.md +1 -1
  258. package/templates/skills/swarm-extensions/content.md +9 -5
  259. package/dist/cli-4wppfkcm.js +0 -16384
  260. package/dist/cli-55qrx3q8.js +0 -6041
  261. package/dist/cli-cg4vnyh4.js +0 -305
  262. package/dist/cli-jyetr64j.js +0 -52
  263. package/dist/cli-pp9ax9pa.js +0 -520
  264. package/dist/cli-qa66qaf0.js +0 -6076
  265. package/dist/cli-qghpvca2.js +0 -590
  266. package/dist/cli-rembwgt8.js +0 -861
  267. package/dist/cli-tcvj7j2r.js +0 -1971
  268. package/dist/cli-vfcth11k.js +0 -36
  269. package/dist/cli-w1d1bgkk.js +0 -1422
  270. package/dist/cli-y6x0gqtg.js +0 -5977
  271. package/dist/es5-tj3tn4jj.js +0 -844
  272. package/dist/index-1kq5hw2p.js +0 -467
  273. package/dist/index-1p8wdstt.js +0 -7
  274. package/dist/index-8fdjzb2f.js +0 -100
  275. package/dist/index-bxmwzbgw.js +0 -365
  276. package/dist/index-nzerxd5z.js +0 -7
  277. package/dist/index-pyymnzat.js +0 -1168
  278. package/dist/index-qv6atbab.js +0 -619
  279. package/dist/index-rja32qa7.js +0 -801
  280. package/dist/index-tzg5srfp.js +0 -211
  281. package/dist/index-vhncw983.js +0 -90
  282. package/dist/index-xk036zsk.js +0 -859
  283. package/dist/index-zyazbzcb.js +0 -6
  284. package/dist/{claude-managed-setup-48dwg9bv.js → claude-managed-setup-f5pmsyrp.js} +3 -3
package/src/be/db.ts CHANGED
@@ -141,10 +141,15 @@ import { normalizeDate, normalizeDateRequired } from "./date-utils";
141
141
  import {
142
142
  type AgentRow,
143
143
  configureAgentDependencies,
144
+ ExtensionAgentAssignmentError,
144
145
  getActiveTaskCount,
145
146
  getAgentById,
146
147
  getAllAgents,
147
148
  isAgentEligibleForTask,
149
+ isExtensionAgent,
150
+ NOT_EXTENSION_AGENT_SQL,
151
+ ReservedAgentRoleError,
152
+ reservedRoleViolation,
148
153
  rowToAgent,
149
154
  } from "./db/agents";
150
155
  import {
@@ -172,6 +177,9 @@ export {
172
177
  buildRoutingAffinityFromAgent,
173
178
  createAgent,
174
179
  deleteAgent,
180
+ EXTENSION_AGENT_ROLE,
181
+ ExtensionAgentAssignmentError,
182
+ extensionAgentAssignmentError,
175
183
  getActiveTaskCount,
176
184
  getAgentById,
177
185
  getAgentHarnessProviders,
@@ -181,9 +189,13 @@ export {
181
189
  hasCapacity,
182
190
  incrementEmptyPollCount,
183
191
  isAgentEligibleForTask,
192
+ isExtensionAgent,
184
193
  isPoolAffinityEnforcementEnabled,
185
194
  listAgentsWithCredStatusByProvider,
186
195
  MAX_EMPTY_POLLS,
196
+ NOT_EXTENSION_AGENT_SQL,
197
+ ReservedAgentRoleError,
198
+ reservedRoleViolation,
187
199
  resetEmptyPollCount,
188
200
  setAgentHarnessProvider,
189
201
  shouldBlockPolling,
@@ -427,6 +439,10 @@ export interface SlackMessageRecord {
427
439
  finalizedAt?: string;
428
440
  streamChunksAppended: number;
429
441
  conclusionKind?: SlackConclusionKind;
442
+ /** Set once a deferral card has been rewritten into its resolved state. */
443
+ deferralResolvedAt?: string;
444
+ /** Set with `deferralResolvedAt` when the rewrite was given up on. */
445
+ deferralAbandonedAt?: string;
430
446
  createdAt: string;
431
447
  updatedAt: string;
432
448
  }
@@ -449,6 +465,8 @@ type SlackMessageRow = {
449
465
  finalized_at: string | null;
450
466
  stream_chunks_appended: number;
451
467
  conclusion_kind: SlackConclusionKind | null;
468
+ deferral_resolved_at: string | null;
469
+ deferral_abandoned_at: string | null;
452
470
  created_at: string;
453
471
  updated_at: string;
454
472
  };
@@ -466,6 +484,8 @@ function rowToSlackMessage(row: SlackMessageRow): SlackMessageRecord {
466
484
  finalizedAt: row.finalized_at ?? undefined,
467
485
  streamChunksAppended: row.stream_chunks_appended,
468
486
  conclusionKind: row.conclusion_kind ?? undefined,
487
+ deferralResolvedAt: row.deferral_resolved_at ?? undefined,
488
+ deferralAbandonedAt: row.deferral_abandoned_at ?? undefined,
469
489
  createdAt: row.created_at,
470
490
  updatedAt: row.updated_at,
471
491
  };
@@ -675,6 +695,57 @@ export async function getSlackOutcomeMessage(taskId: string): Promise<SlackMessa
675
695
  return row ? rowToSlackMessage(row) : null;
676
696
  }
677
697
 
698
+ /**
699
+ * Deferral outcome cards whose wake-up task has since reached a terminal
700
+ * state, and which have not been rewritten into that resolved state yet.
701
+ *
702
+ * The join is `agent_tasks.parentTaskId` — `defer-task` sets it on the
703
+ * schedule and `createStandaloneScheduleTask` passes it through, so the
704
+ * wake-up task points back at the task whose card announced the wait.
705
+ * `deferredAt` (not the `deferred` tag) identifies the deferring task,
706
+ * because the wake-up task inherits the tag.
707
+ */
708
+ export async function getResolvableDeferralOutcomes(
709
+ limit = 50,
710
+ ): Promise<{ card: SlackMessageRecord; deferredTaskId: string; wakeTaskId: string }[]> {
711
+ const rows = await getDbClient().query<SlackMessageRow & { wake_task_id: string }>(
712
+ `SELECT card.*, wake.id AS wake_task_id
713
+ FROM slack_messages card
714
+ JOIN agent_tasks deferred ON deferred.id = card.task_id
715
+ JOIN agent_tasks wake ON wake.parentTaskId = deferred.id AND wake.taskType = 'deferred'
716
+ WHERE card.kind = 'outcome'
717
+ AND card.finalized_at IS NOT NULL
718
+ AND card.deferral_resolved_at IS NULL
719
+ AND card.ts NOT LIKE 'pending:%'
720
+ AND deferred.deferredAt IS NOT NULL
721
+ AND wake.status IN ('completed', 'failed', 'cancelled')
722
+ ORDER BY wake.finishedAt ASC
723
+ LIMIT ?`,
724
+ [limit],
725
+ );
726
+ return rows.map((row) => ({
727
+ card: rowToSlackMessage(row),
728
+ deferredTaskId: row.task_id!,
729
+ wakeTaskId: row.wake_task_id,
730
+ }));
731
+ }
732
+
733
+ /**
734
+ * Mark a deferral card as resolved, so the resolution pass skips it after.
735
+ * `abandoned` records that the rewrite was given up on, which hands the
736
+ * answer back to the wake-up's own outcome card.
737
+ */
738
+ export async function markSlackDeferralResolved(
739
+ id: string,
740
+ options: { abandoned?: boolean } = {},
741
+ ): Promise<void> {
742
+ const now = new Date().toISOString();
743
+ await getDbClient().run(
744
+ "UPDATE slack_messages SET deferral_resolved_at = ?, deferral_abandoned_at = ? WHERE id = ?",
745
+ [now, options.abandoned ? now : null, id],
746
+ );
747
+ }
748
+
678
749
  export async function getSlackTreeMessages(): Promise<SlackMessageRecord[]> {
679
750
  const rows = await getDbClient().query<SlackMessageRow>(
680
751
  `SELECT tree.*
@@ -1662,6 +1733,7 @@ type TaskSteeringMessageRow = {
1662
1733
  status: string;
1663
1734
  delivered_mode: string | null;
1664
1735
  source: string;
1736
+ sender_name?: string | null;
1665
1737
  created_by_kind: string;
1666
1738
  created_by_user_id: string | null;
1667
1739
  created_by_agent_id: string | null;
@@ -1672,7 +1744,20 @@ type TaskSteeringMessageRow = {
1672
1744
  handled_note: string | null;
1673
1745
  };
1674
1746
 
1747
+ // Resolve names in the API so workers need no database access or identity lookups.
1748
+ const STEERING_MESSAGE_SELECT = `SELECT m.*,
1749
+ CASE m.created_by_kind WHEN 'user' THEN u.name WHEN 'agent' THEN a.name END AS sender_name
1750
+ FROM task_steering_messages m
1751
+ LEFT JOIN users u ON m.created_by_kind = 'user' AND u.id = m.created_by_user_id
1752
+ LEFT JOIN agents a ON m.created_by_kind = 'agent' AND a.id = m.created_by_agent_id`;
1753
+
1675
1754
  function rowToSteeringMessage(row: TaskSteeringMessageRow): SteeringMessage {
1755
+ const senderId =
1756
+ row.created_by_kind === "user" ? row.created_by_user_id : row.created_by_agent_id;
1757
+ const senderLabel =
1758
+ row.created_by_kind === "system"
1759
+ ? "system"
1760
+ : `${row.sender_name?.trim() || senderId || "Unknown"} (${row.created_by_kind})`;
1676
1761
  return {
1677
1762
  id: row.id,
1678
1763
  taskId: row.task_id,
@@ -1681,6 +1766,7 @@ function rowToSteeringMessage(row: TaskSteeringMessageRow): SteeringMessage {
1681
1766
  status: row.status as SteeringStatus,
1682
1767
  deliveredMode: (row.delivered_mode as SteerMode | null) ?? undefined,
1683
1768
  source: row.source as SteeringSource,
1769
+ senderLabel,
1684
1770
  createdByKind: row.created_by_kind as SteeringMessage["createdByKind"],
1685
1771
  createdByUserId: row.created_by_user_id ?? undefined,
1686
1772
  createdByAgentId: row.created_by_agent_id ?? undefined,
@@ -1739,7 +1825,7 @@ export async function createSteeringMessage(
1739
1825
  });
1740
1826
  } catch {}
1741
1827
 
1742
- return rowToSteeringMessage(row);
1828
+ return (await getSteeringMessageById(id))!;
1743
1829
  }
1744
1830
 
1745
1831
  export async function getSteeringMessagesForTask(
@@ -1748,15 +1834,15 @@ export async function getSteeringMessagesForTask(
1748
1834
  ): Promise<SteeringMessage[]> {
1749
1835
  const rows = opts?.status
1750
1836
  ? await getDbClient().query<TaskSteeringMessageRow>(
1751
- `SELECT * FROM task_steering_messages
1752
- WHERE task_id = ? AND status = ?
1753
- ORDER BY created_at ASC, rowid ASC`,
1837
+ `${STEERING_MESSAGE_SELECT}
1838
+ WHERE m.task_id = ? AND m.status = ?
1839
+ ORDER BY m.created_at ASC, m.rowid ASC`,
1754
1840
  [taskId, opts.status],
1755
1841
  )
1756
1842
  : await getDbClient().query<TaskSteeringMessageRow>(
1757
- `SELECT * FROM task_steering_messages
1758
- WHERE task_id = ?
1759
- ORDER BY created_at ASC, rowid ASC`,
1843
+ `${STEERING_MESSAGE_SELECT}
1844
+ WHERE m.task_id = ?
1845
+ ORDER BY m.created_at ASC, m.rowid ASC`,
1760
1846
  [taskId],
1761
1847
  );
1762
1848
  return rows.map(rowToSteeringMessage);
@@ -1764,8 +1850,8 @@ export async function getSteeringMessagesForTask(
1764
1850
 
1765
1851
  export async function getSteeringMessageById(id: string): Promise<SteeringMessage | null> {
1766
1852
  const row = await getDbClient().get<TaskSteeringMessageRow>(
1767
- `SELECT * FROM task_steering_messages
1768
- WHERE id = ?`,
1853
+ `${STEERING_MESSAGE_SELECT}
1854
+ WHERE m.id = ?`,
1769
1855
  [id],
1770
1856
  );
1771
1857
  return row ? rowToSteeringMessage(row) : null;
@@ -1777,8 +1863,7 @@ export async function getPendingSteeringForTask(taskId: string): Promise<Steerin
1777
1863
 
1778
1864
  export async function getPendingSteeringForAgent(agentId: string): Promise<SteeringMessage[]> {
1779
1865
  const rows = await getDbClient().query<TaskSteeringMessageRow>(
1780
- `SELECT m.*
1781
- FROM task_steering_messages m
1866
+ `${STEERING_MESSAGE_SELECT}
1782
1867
  JOIN agent_tasks t ON t.id = m.task_id
1783
1868
  WHERE t.agentId = ? AND m.status = 'pending'
1784
1869
  ORDER BY m.created_at ASC, m.rowid ASC`,
@@ -1816,7 +1901,7 @@ export async function markSteeringDelivered(
1816
1901
  } catch {}
1817
1902
  }
1818
1903
 
1819
- return row ? rowToSteeringMessage(row) : null;
1904
+ return row ? await getSteeringMessageById(id) : null;
1820
1905
  }
1821
1906
 
1822
1907
  export async function markSteeringHandled(
@@ -1832,7 +1917,7 @@ export async function markSteeringHandled(
1832
1917
  RETURNING *`,
1833
1918
  [note ?? null, id],
1834
1919
  );
1835
- return row ? rowToSteeringMessage(row) : null;
1920
+ return row ? await getSteeringMessageById(id) : null;
1836
1921
  }
1837
1922
 
1838
1923
  export async function markSteeringPromoted(
@@ -1847,7 +1932,7 @@ export async function markSteeringPromoted(
1847
1932
  RETURNING *`,
1848
1933
  [promotedTaskId, id],
1849
1934
  );
1850
- return row ? rowToSteeringMessage(row) : null;
1935
+ return row ? await getSteeringMessageById(id) : null;
1851
1936
  }
1852
1937
 
1853
1938
  export async function cancelPendingSteeringForTask(taskId: string): Promise<number> {
@@ -2475,6 +2560,17 @@ export async function createTaskExtended(
2475
2560
  // (explicit `leadOnly`/`capabilities`, or a lead-only ratchet) still gates
2476
2561
  // as before.
2477
2562
  const targetAgentId = options.agentId ?? options.offeredTo;
2563
+ // Extension identities (`ext:<name>`, role "extension") are API principals,
2564
+ // never executors. This is the shared chokepoint for every creation path
2565
+ // (send-task, POST /api/tasks, schedules, workflow agent-task nodes, script
2566
+ // runs, apps, Slack fan-out, pre.task.create hook rewrites).
2567
+ for (const id of new Set([options.agentId, options.offeredTo])) {
2568
+ if (!id) continue;
2569
+ const target = await getAgentById(id);
2570
+ if (target && isExtensionAgent(target)) {
2571
+ throw new ExtensionAgentAssignmentError(target);
2572
+ }
2573
+ }
2478
2574
  if (options.routingAffinity && targetAgentId && !routingAffinityIsInheritedProvenance) {
2479
2575
  const target = await getAgentById(targetAgentId);
2480
2576
  if (!target || !isAgentEligibleForTask(target, { routingAffinity: options.routingAffinity })) {
@@ -2763,6 +2859,9 @@ export async function acceptTask(taskId: string, agentId: string): Promise<Agent
2763
2859
  const task = await getTaskById(taskId);
2764
2860
  if (!task) return null;
2765
2861
  const agent = await getAgentById(agentId);
2862
+ // Extension identities never take work, including offers left over from
2863
+ // before creation-time rejection existed.
2864
+ if (isExtensionAgent(agent)) return null;
2766
2865
  if (
2767
2866
  (task.routingAffinity?.leadOnly || task.routingAffinityInvalid) &&
2768
2867
  (!agent || !isAgentEligibleForTask(agent, task))
@@ -3053,6 +3152,7 @@ export async function claimOfferedTask(taskId: string, agentId: string): Promise
3053
3152
  if (!task) return null;
3054
3153
  if (task.status !== "offered" || task.offeredTo !== agentId) return null;
3055
3154
  const agent = await getAgentById(agentId);
3155
+ if (isExtensionAgent(agent)) return null;
3056
3156
  if (
3057
3157
  (task.routingAffinity?.leadOnly || task.routingAffinityInvalid) &&
3058
3158
  (!agent || !isAgentEligibleForTask(agent, task))
@@ -3291,6 +3391,8 @@ export async function updateAgentProfile(
3291
3391
  expectedHashes?: ProfileExpectedHashes;
3292
3392
  /** Called once per field dropped because its expected hash was stale. */
3293
3393
  onConflict?: (conflict: ProfileSyncConflict) => void;
3394
+ /** Only the extension identity lifecycle may grant the reserved extension role. */
3395
+ allowExtensionRole?: boolean;
3294
3396
  },
3295
3397
  ): Promise<Agent | null> {
3296
3398
  return await getDbClient().transaction(async (tx) => {
@@ -3298,6 +3400,11 @@ export async function updateAgentProfile(
3298
3400
  const current = await tx.get<AgentRow>("SELECT * FROM agents WHERE id = ?", [id]);
3299
3401
  if (!current) return null;
3300
3402
 
3403
+ const roleViolation = reservedRoleViolation(current.role, updates.role, {
3404
+ allowExtensionRole: guard?.allowExtensionRole,
3405
+ });
3406
+ if (roleViolation) throw new ReservedAgentRoleError(roleViolation);
3407
+
3301
3408
  // Compare with the old metadata before replacing it. Persist refreshed defaults
3302
3409
  // atomically so both running workers and restarted workers see matching blobs.
3303
3410
  const previous = rowToAgent(current);
@@ -5474,6 +5581,8 @@ type ScheduledTaskRow = {
5474
5581
  nextRunAt: string | null;
5475
5582
  createdByAgentId: string | null;
5476
5583
  parentTaskId: string | null;
5584
+ requestedDelayMs: number | null;
5585
+ requestedRunAt: string | null;
5477
5586
  timezone: string;
5478
5587
  consecutiveErrors: number | null;
5479
5588
  lastErrorAt: string | null;
@@ -5524,6 +5633,8 @@ function rowToScheduledTask(row: ScheduledTaskRow): ScheduledTask {
5524
5633
  nextRunAt: normalizeDate(row.nextRunAt) ?? undefined,
5525
5634
  createdByAgentId: row.createdByAgentId ?? undefined,
5526
5635
  parentTaskId: row.parentTaskId ?? undefined,
5636
+ requestedDelayMs: row.requestedDelayMs ?? undefined,
5637
+ requestedRunAt: normalizeDate(row.requestedRunAt) ?? undefined,
5527
5638
  timezone: row.timezone,
5528
5639
  consecutiveErrors: row.consecutiveErrors ?? 0,
5529
5640
  lastErrorAt: normalizeDate(row.lastErrorAt) ?? undefined,
@@ -5675,6 +5786,13 @@ export interface CreateScheduledTaskData {
5675
5786
  createdByAgentId?: string;
5676
5787
  /** Only `defer-task` sets this — the task the schedule's run continues. */
5677
5788
  parentTaskId?: string;
5789
+ /**
5790
+ * Only `defer-task` sets these — the delay the caller actually asked for,
5791
+ * kept because the scheduler clears `nextRunAt` once a one_time schedule
5792
+ * fires. Write-once provenance; nothing reads them to decide when to run.
5793
+ */
5794
+ requestedDelayMs?: number;
5795
+ requestedRunAt?: string;
5678
5796
  timezone?: string;
5679
5797
  model?: string;
5680
5798
  modelTier?: ModelTier;
@@ -5697,11 +5815,12 @@ export async function createScheduledTask(data: CreateScheduledTaskData): Promis
5697
5815
  `INSERT INTO scheduled_tasks (
5698
5816
  id, "key", name, description, cronExpression, intervalMs, taskTemplate,
5699
5817
  taskType, tags, priority, targetAgentId, enabled, nextRunAt,
5700
- createdByAgentId, parentTaskId, timezone, model, modelTier, scheduleType,
5818
+ createdByAgentId, parentTaskId, requestedDelayMs, requestedRunAt,
5819
+ timezone, model, modelTier, scheduleType,
5701
5820
  targetType, workflowId, scriptName, scriptArgs, params, requiredParams, requires,
5702
5821
  createdAt, lastUpdatedAt,
5703
5822
  created_by, updated_by
5704
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`,
5823
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING *`,
5705
5824
  [
5706
5825
  id,
5707
5826
  normalizeAssetKey(data.key ?? defaultAssetKey("schedule", id)),
@@ -5718,6 +5837,8 @@ export async function createScheduledTask(data: CreateScheduledTaskData): Promis
5718
5837
  data.nextRunAt ?? null,
5719
5838
  data.createdByAgentId ?? null,
5720
5839
  data.parentTaskId ?? null,
5840
+ data.requestedDelayMs ?? null,
5841
+ data.requestedRunAt ?? null,
5721
5842
  data.timezone ?? "UTC",
5722
5843
  data.model ?? null,
5723
5844
  data.modelTier ?? null,
@@ -12565,9 +12686,10 @@ export async function getSwarmMetrics(): Promise<SwarmMetrics> {
12565
12686
 
12566
12687
  const groupCounts = async (
12567
12688
  table: string,
12689
+ where?: string,
12568
12690
  ): Promise<{ total: number; by_status: Record<string, number> }> => {
12569
12691
  const rows = await client.query<{ status: string; count: number }>(
12570
- `SELECT status, COUNT(*) AS count FROM ${table} GROUP BY status`,
12692
+ `SELECT status, COUNT(*) AS count FROM ${table}${where ? ` WHERE ${where}` : ""} GROUP BY status`,
12571
12693
  );
12572
12694
  const by_status: Record<string, number> = {};
12573
12695
  let total = 0;
@@ -12589,7 +12711,7 @@ export async function getSwarmMetrics(): Promise<SwarmMetrics> {
12589
12711
 
12590
12712
  return {
12591
12713
  tasks: await groupCounts("agent_tasks"),
12592
- agents: await groupCounts("agents"),
12714
+ agents: await groupCounts("agents", NOT_EXTENSION_AGENT_SQL),
12593
12715
  workflows: { total: workflowRow?.total ?? 0, enabled: workflowRow?.enabled ?? 0 },
12594
12716
  pages: { total: pagesRow?.count ?? 0 },
12595
12717
  sessions: { active: sessionsRow?.count ?? 0 },
@@ -20,8 +20,12 @@ export type InstallExtensionArgs = {
20
20
  createdBy?: string | null;
21
21
  activate?: boolean;
22
22
  changeReason?: string | null;
23
+ /** HTTP worker upserts must not claim another creator's name. */
24
+ ownerOnly?: string;
23
25
  };
24
26
 
27
+ export class ExtensionOwnershipError extends Error {}
28
+
25
29
  export type InstallExtensionResult = {
26
30
  extension: Extension;
27
31
  isNew: boolean;
@@ -60,6 +64,19 @@ export async function installExtension(
60
64
  args.manifest.name,
61
65
  ]);
62
66
  const existing = existingRow ? rowToExtension(existingRow) : null;
67
+ if (existing && args.ownerOnly && existing.createdByAgentId !== args.ownerOnly) {
68
+ throw new ExtensionOwnershipError("Forbidden: extension belongs to another owner");
69
+ }
70
+ if (
71
+ existing?.enabled &&
72
+ args.ownerOnly &&
73
+ ((args.config !== undefined && JSON.stringify(args.config) !== existing.configJson) ||
74
+ (args.priority !== undefined && args.priority !== existing.priority))
75
+ ) {
76
+ throw new ExtensionOwnershipError(
77
+ "Forbidden: only a lead or operator may change live extension configuration",
78
+ );
79
+ }
63
80
  const now = new Date().toISOString();
64
81
 
65
82
  if (!existing) {
@@ -313,6 +330,7 @@ export async function updateExtensionMeta(
313
330
  config?: Record<string, unknown>;
314
331
  description?: string;
315
332
  updatedBy?: string | null;
333
+ ownerOnly?: string;
316
334
  },
317
335
  ): Promise<Extension | null> {
318
336
  const sets: string[] = [];
@@ -329,14 +347,21 @@ export async function updateExtensionMeta(
329
347
  sets.push("description = ?");
330
348
  values.push(args.description);
331
349
  }
332
- if (sets.length === 0) return getExtensionById(id);
350
+ if (sets.length === 0 && !args.ownerOnly) return getExtensionById(id);
333
351
 
334
352
  sets.push("updated_by = ?", "updatedAt = ?");
335
353
  values.push(args.updatedBy ?? null, new Date().toISOString(), id);
354
+ // The enabled check must be part of the write: a lead may enable the draft
355
+ // after the HTTP handler has authorized it but before this UPDATE executes.
356
+ const ownerCondition = args.ownerOnly ? " AND createdByAgentId = ? AND enabled = 0" : "";
357
+ if (args.ownerOnly) values.push(args.ownerOnly);
336
358
  const row = await getDbClient().get<ExtensionRow>(
337
- `UPDATE extensions SET ${sets.join(", ")} WHERE id = ? RETURNING *`,
359
+ `UPDATE extensions SET ${sets.join(", ")} WHERE id = ?${ownerCondition} RETURNING *`,
338
360
  values,
339
361
  );
362
+ if (!row && args.ownerOnly) {
363
+ throw new ExtensionOwnershipError("Forbidden: extension must be your own disabled draft");
364
+ }
340
365
  return row ? rowToExtension(row) : null;
341
366
  }
342
367
 
@@ -401,8 +426,14 @@ export async function recordExtensionFailure(
401
426
  return row ? rowToExtension(row) : null;
402
427
  }
403
428
 
404
- export async function deleteExtension(id: string): Promise<boolean> {
405
- const result = await getDbClient().run("DELETE FROM extensions WHERE id = ?", [id]);
429
+ export async function deleteExtension(id: string, ownerOnly?: string): Promise<boolean> {
430
+ const result = await getDbClient().run(
431
+ `DELETE FROM extensions WHERE id = ?${ownerOnly ? " AND createdByAgentId = ? AND enabled = 0" : ""}`,
432
+ ownerOnly ? [id, ownerOnly] : [id],
433
+ );
434
+ if (result.changes === 0 && ownerOnly) {
435
+ throw new ExtensionOwnershipError("Forbidden: extension must be your own disabled draft");
436
+ }
406
437
  return result.changes > 0;
407
438
  }
408
439
 
@@ -17,6 +17,7 @@
17
17
  * touch `bun:sqlite` or `src/be/db`. The boundary check enforces it.
18
18
  */
19
19
  import { z } from "zod";
20
+ import { getMemoryRaterNames } from "../../../utils/memory-raters";
20
21
  import { ClaudeCliLlmRaterClient, type LlmRaterClient, type LlmRaterResult } from "./llm-client";
21
22
  import {
22
23
  type MemoryRater,
@@ -246,12 +247,7 @@ ${memoryBlock}`;
246
247
  * path — strict opt-in so existing deployments are byte-identical when unset.
247
248
  */
248
249
  export function isLlmRaterEnabled(): boolean {
249
- const raw = process.env.MEMORY_RATERS;
250
- if (!raw || raw.trim() === "") return false;
251
- return raw
252
- .split(",")
253
- .map((s) => s.trim())
254
- .includes("llm");
250
+ return getMemoryRaterNames().includes("llm");
255
251
  }
256
252
 
257
253
  /** Memory snapshot returned by `GET /api/memory/retrievals`. */
@@ -1,9 +1,8 @@
1
1
  import type { MemoryRater, RatingEvent } from "./types";
2
2
 
3
3
  /**
4
- * Default rater. Emits no events, makes no DB calls. Selected when
5
- * MEMORY_RATERS is unset or empty so the framework defaults to behaving
6
- * byte-identically to pre-rater builds.
4
+ * Disabled rater. Emits no events, makes no DB calls. Selected when
5
+ * MEMORY_RATERS is explicitly empty or no configured names are recognized.
7
6
  */
8
7
  export class NoopRater implements MemoryRater {
9
8
  readonly name = "noop";
@@ -1,3 +1,4 @@
1
+ import { getMemoryRaterNames } from "../../../utils/memory-raters";
1
2
  import { ExplicitSelfRatingRater } from "./explicit-self";
2
3
  import { ImplicitCitationRater } from "./implicit-citation";
3
4
  import { LlmRater } from "./llm";
@@ -8,7 +9,8 @@ import type { MemoryRater } from "./types";
8
9
  * Plan: thoughts/taras/plans/2026-05-05-memory-rater-v1.5/step-1.md §4
9
10
  *
10
11
  * `MEMORY_RATERS` env — comma-separated list of rater names. Defaults to
11
- * `[NoopRater]` when unset/empty so existing deployments stay byte-identical.
12
+ * `implicit-citation,explicit-self` when unset. An explicit empty value selects
13
+ * `[NoopRater]`.
12
14
  *
13
15
  * `MEMORY_RATER_WEIGHTS` env — optional `name:multiplier,...` overrides.
14
16
  * Multiplier is applied to every emitted RatingEvent.weight before
@@ -43,15 +45,7 @@ const FACTORIES: Record<string, RaterFactory> = {
43
45
  export const SERVER_RATERS = new Set<string>(["implicit-citation"]);
44
46
 
45
47
  export function getRegisteredRaters(): MemoryRater[] {
46
- const raw = process.env.MEMORY_RATERS;
47
- if (!raw || raw.trim() === "") {
48
- return [new NoopRater()];
49
- }
50
-
51
- const names = raw
52
- .split(",")
53
- .map((s) => s.trim())
54
- .filter((s) => s.length > 0);
48
+ const names = getMemoryRaterNames();
55
49
 
56
50
  const raters: MemoryRater[] = [];
57
51
  for (const name of names) {
@@ -0,0 +1,27 @@
1
+ -- Persist what `defer-task` was actually ASKED for, alongside the derived
2
+ -- `nextRunAt`. The scheduler nulls `nextRunAt` once a one_time schedule fires,
3
+ -- so without these columns the requested-delay distribution is unrecoverable
4
+ -- after the wake-up runs.
5
+ --
6
+ -- Exactly one of the two is ever set (defer-task rejects both/neither), and
7
+ -- neither is read by the scheduler — they are write-once provenance.
8
+ ALTER TABLE scheduled_tasks ADD COLUMN requestedDelayMs INTEGER;
9
+ ALTER TABLE scheduled_tasks ADD COLUMN requestedRunAt TEXT;
10
+
11
+ -- Marks a task that ENDED in a deferral, so renderers can tell "parked" from
12
+ -- "done" on a row whose status is `completed` either way.
13
+ --
14
+ -- The `deferred` tag cannot answer this: `defer-task` adds it to the deferring
15
+ -- task, and the wake-up task it schedules inherits the same tag — so a wake-up
16
+ -- child that simply finished is tag-identical to one that deferred again.
17
+ ALTER TABLE agent_tasks ADD COLUMN deferredAt TEXT;
18
+
19
+ -- Set once a deferral's ⏳ outcome card has been rewritten into its resolved
20
+ -- state, so the resolution pass rewrites each card exactly once instead of on
21
+ -- every render tick.
22
+ ALTER TABLE slack_messages ADD COLUMN deferral_resolved_at TEXT;
23
+
24
+ -- Set alongside `deferral_resolved_at` when the rewrite was given up on
25
+ -- (Slack refused it, or it kept failing). The wake-up's answer then never
26
+ -- reached the ⏳ card, so the wake-up posts its own outcome card instead.
27
+ ALTER TABLE slack_messages ADD COLUMN deferral_abandoned_at TEXT;
@@ -34,6 +34,8 @@ export type CatalogProviderId = (typeof CATALOG_PROVIDER_IDS)[number];
34
34
  * of the picker between models.dev listing gaps.
35
35
  */
36
36
  export const PINNED_MODELSDEV_ENTRIES = [
37
+ // Official launch metadata; retain until models.dev catches up. Verified 2026-09-22.
38
+ "anthropic/claude-opus-5-5",
37
39
  "anthropic/claude-mythos-5",
38
40
  "anthropic/claude-sonnet-5",
39
41
  "amazon-bedrock/anthropic.claude-sonnet-5",