volute 0.49.0 → 0.50.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 (210) hide show
  1. package/README.md +2 -2
  2. package/dist/{accept-KYNC5X3U.js → accept-5NK5XJB5.js} +2 -1
  3. package/dist/{activity-events-GJJAI2WT.js → activity-events-H7YI3BTF.js} +3 -3
  4. package/dist/{ai-service-A7FS7EC3.js → ai-service-V4CTNGI3.js} +5 -4
  5. package/dist/{archive-7E3YE6VF.js → archive-ORM3ENHI.js} +3 -3
  6. package/dist/{auth-ABYLOEZ7.js → auth-DT7GY7EZ.js} +10 -6
  7. package/dist/{avatar-image-6ATV2IOZ.js → avatar-image-QEPAP3A5.js} +3 -3
  8. package/dist/{backup-XYAMT4CU.js → backup-IE6PMXJO.js} +11 -12
  9. package/dist/{bridge-3FNSWLNQ.js → bridge-YK7NTRU5.js} +2 -1
  10. package/dist/{channels-SGQZHNQJ.js → channels-FSAHOQWI.js} +2 -1
  11. package/dist/{chat-OGXAEXMV.js → chat-W5K6HCBE.js} +9 -9
  12. package/dist/{chunk-3B427GUK.js → chunk-3BAFMGCJ.js} +2 -2
  13. package/dist/{chunk-4B62E4CG.js → chunk-4CN7OCJE.js} +2 -2
  14. package/dist/{chunk-VNRSXEBF.js → chunk-574M6I7F.js} +38 -5
  15. package/dist/{chunk-HCYQRME2.js → chunk-5FVANDON.js} +2 -2
  16. package/dist/{chunk-P4LHVRNE.js → chunk-5IIVOWSP.js} +27 -19
  17. package/dist/{chunk-TXJL36ZQ.js → chunk-5PKFJHCR.js} +2 -2
  18. package/dist/{chunk-JPGHSXSV.js → chunk-5TPC4KCV.js} +23 -9
  19. package/dist/{chunk-YIJGKHIS.js → chunk-632FJ23O.js} +24 -20
  20. package/dist/{chunk-IH5TKXX6.js → chunk-636GFQNE.js} +11 -11
  21. package/dist/{chunk-EFT2OALG.js → chunk-6BM2I736.js} +20 -5
  22. package/dist/{chunk-JCBPU57A.js → chunk-74T3CEXK.js} +6 -6
  23. package/dist/{chunk-EG5LSKHU.js → chunk-77IZUVBB.js} +2 -2
  24. package/dist/{chunk-K3JOLGWF.js → chunk-7DXLIYPY.js} +76 -63
  25. package/dist/chunk-7FDWPQCU.js +289 -0
  26. package/dist/chunk-7OZWKHA3.js +548 -0
  27. package/dist/{chunk-PY24ZL62.js → chunk-7UWXAJMK.js} +1 -1
  28. package/dist/{chunk-MG3Z34YE.js → chunk-7XDOTN4R.js} +1 -1
  29. package/dist/{chunk-SX72DOO3.js → chunk-AFSLNUTT.js} +1 -1
  30. package/dist/{chunk-DVN6DNMI.js → chunk-AJWFDIEY.js} +17 -2
  31. package/dist/chunk-ALIVXDM4.js +1014 -0
  32. package/dist/{chunk-RSBL3FAD.js → chunk-CAIYV33P.js} +234 -24
  33. package/dist/{chunk-ZX46PGCC.js → chunk-E5S3ILVA.js} +1 -1
  34. package/dist/{chunk-SHKM5RGD.js → chunk-EEOMA4DX.js} +1 -1
  35. package/dist/chunk-EQKSRVDE.js +2857 -0
  36. package/dist/chunk-FNJCBRD3.js +31 -0
  37. package/dist/{chunk-EADX54RG.js → chunk-HW6Q7AMB.js} +4 -4
  38. package/dist/chunk-JI67D7O2.js +1222 -0
  39. package/dist/{chunk-KHCKZ36O.js → chunk-KAEZOVP6.js} +1 -1
  40. package/dist/chunk-LNNI3MS4.js +54 -0
  41. package/dist/{chunk-BCG45AQB.js → chunk-M74I3HHF.js} +15 -5
  42. package/dist/chunk-MLZBBLMT.js +109 -0
  43. package/dist/{chunk-5T5YMX6S.js → chunk-PW65BXY5.js} +5 -0
  44. package/dist/{chunk-NOIAHMJB.js → chunk-QFVGPKJI.js} +1 -1
  45. package/dist/{chunk-VIUH5CHQ.js → chunk-SFRYRW37.js} +50 -33
  46. package/dist/{chunk-L27YYONJ.js → chunk-SSNZ6ZNN.js} +1 -1
  47. package/dist/{chunk-GWVFDUNB.js → chunk-TGJHGVBZ.js} +8 -10
  48. package/dist/{chunk-6HGEZJDH.js → chunk-U3EJJ7JN.js} +2 -2
  49. package/dist/{chunk-4DSGN4FL.js → chunk-UC6BNMGT.js} +43 -13
  50. package/dist/{chunk-JBIFXB6I.js → chunk-WBBKYUGF.js} +3 -5
  51. package/dist/{chunk-FBBS4BVR.js → chunk-X67VT4QO.js} +1 -1
  52. package/dist/{chunk-DAIIMBLB.js → chunk-YQOYZ6G7.js} +3 -5
  53. package/dist/{chunk-JCWB2ITW.js → chunk-ZUG76D3P.js} +35 -5
  54. package/dist/cli.js +34 -27
  55. package/dist/{clock-6JZL2FS6.js → clock-LH7UFNLJ.js} +4 -3
  56. package/dist/{cloud-sync-VFDTNNCA.js → cloud-sync-IDJFVDEP.js} +22 -19
  57. package/dist/{config-RPSLA2HX.js → config-UGJS57VT.js} +3 -3
  58. package/dist/{conversations-TUM5PJJ4.js → conversations-S2232Y5I.js} +8 -4
  59. package/dist/{create-Z5FSDNVZ.js → create-2M7DAHQX.js} +2 -2
  60. package/dist/{create-VIQVP3QB.js → create-M727VUDF.js} +2 -1
  61. package/dist/{daemon-client-ZFY5MOAL.js → daemon-client-VVYFZI2D.js} +2 -1
  62. package/dist/{daemon-restart-54L4B6AQ.js → daemon-restart-2AAGYFHE.js} +6 -7
  63. package/dist/daemon.js +1625 -529
  64. package/dist/{db-F6UMFRYD.js → db-QAY45Z56.js} +2 -2
  65. package/dist/{delete-ESEP7NGQ.js → delete-O7PEFQDF.js} +1 -1
  66. package/dist/delivery-manager-B4OKWD2P.js +33 -0
  67. package/dist/{delivery-router-VUOL5YSA.js → delivery-router-JE7SHYNU.js} +7 -5
  68. package/dist/{down-6X7KX7BZ.js → down-D4IRNEMJ.js} +5 -5
  69. package/dist/echo-text-WYAAODBV.js +39 -0
  70. package/dist/{env-ZYMSCB7B.js → env-ABM5IMA7.js} +3 -2
  71. package/dist/{env-KUL4XKN2.js → env-AZK5X4W7.js} +3 -3
  72. package/dist/{exec-FBXA3CM4.js → exec-2H65LMTA.js} +3 -3
  73. package/dist/{export-TNBPDH77.js → export-3E3EL6W3.js} +4 -4
  74. package/dist/{extension-J4ZQ2GHV.js → extension-MRYJWSMV.js} +2 -1
  75. package/dist/{extensions-AWNO3SZF.js → extensions-7KP3X6G7.js} +22 -19
  76. package/dist/{files-BVBRJJ2P.js → files-6BMKVG5G.js} +5 -4
  77. package/dist/{import-ZUNKMCKC.js → import-UPNTEBBT.js} +8 -8
  78. package/dist/{isolation-OPCZQRTH.js → isolation-OKDFM6WA.js} +5 -3
  79. package/dist/{join-34TR2GXQ.js → join-PYA36HEC.js} +1 -1
  80. package/dist/{list-PMUXZNAM.js → list-4BVGH7AN.js} +8 -3
  81. package/dist/{login-KAKNFCW5.js → login-MOXVLULE.js} +4 -3
  82. package/dist/{login-LKNLCJPA.js → login-RJJTCVR3.js} +2 -1
  83. package/dist/{logout-YZRMZ3O2.js → logout-BYV433X6.js} +2 -1
  84. package/dist/{logout-RLT62RQP.js → logout-DWD3OQVT.js} +4 -3
  85. package/dist/message-delivery-3456PXB7.js +43 -0
  86. package/dist/migrate-thread-config-4A5LW36Z.js +90 -0
  87. package/dist/{mind-IF6CGMFW.js → mind-NYRALSV2.js} +19 -19
  88. package/dist/{mind-activity-tracker-YMMERB6B.js → mind-activity-tracker-L7GCYXQR.js} +4 -4
  89. package/dist/{mind-contacts-BKUFPUCU.js → mind-contacts-QYU4TPEA.js} +3 -2
  90. package/dist/{mind-history-WI5OI3BW.js → mind-history-NXXOOTX6.js} +30 -7
  91. package/dist/{mind-list-OTHV4ZO4.js → mind-list-SYRA7CH5.js} +3 -2
  92. package/dist/{mind-profile-LUG353ZT.js → mind-profile-MRCVZPYT.js} +1 -1
  93. package/dist/mind-service-5TA7UJAT.js +47 -0
  94. package/dist/{mind-sleep-VBV7YRSU.js → mind-sleep-TH6BRQ5X.js} +4 -3
  95. package/dist/{mind-status-BJNPGXKC.js → mind-status-Z2LQFA6F.js} +3 -2
  96. package/dist/{mind-wake-LVTZ55EZ.js → mind-wake-LDGPRA42.js} +2 -1
  97. package/dist/{package-PD67VPHB.js → package-XYSV7NUT.js} +23 -23
  98. package/dist/{prompts-MDHDLWEX.js → prompts-QKFPX6DQ.js} +3 -3
  99. package/dist/{read-YSWUXYL7.js → read-ZXGUT4QC.js} +5 -3
  100. package/dist/{register-TWQDKQRT.js → register-OX4HRSGC.js} +2 -1
  101. package/dist/{registry-3ELJPUGJ.js → registry-4UF6OCOE.js} +6 -4
  102. package/dist/{reject-SXGCYN4A.js → reject-TWB4QJHG.js} +2 -1
  103. package/dist/{restart-5F5QDBOG.js → restart-AGEGNSYQ.js} +2 -1
  104. package/dist/{sandbox-XPYA7UIF.js → sandbox-EGEU5GGE.js} +7 -4
  105. package/dist/scheduler-UV6RUJ7P.js +25 -0
  106. package/dist/{schema-6YONVTKZ.js → schema-C2GIPD4V.js} +3 -3
  107. package/dist/{seed-NQED7PSX.js → seed-3IMBOAGK.js} +1 -1
  108. package/dist/{seed-check-TYD7RRZY.js → seed-check-NRJEQZST.js} +14 -5
  109. package/dist/{seed-cmd-QF3F2G4S.js → seed-cmd-U73XVQMI.js} +3 -3
  110. package/dist/{seed-create-XOO4N2OE.js → seed-create-FBLNVSUP.js} +4 -4
  111. package/dist/{seed-readiness-3LRYASA4.js → seed-readiness-QRA46ZJQ.js} +4 -5
  112. package/dist/{seed-sprout-HSBBGSS2.js → seed-sprout-3B4SZX7J.js} +9 -9
  113. package/dist/{send-IHRJYNXA.js → send-NBRZJXKX.js} +32 -10
  114. package/dist/{service-YCOPCTSZ.js → service-XMXAXGJD.js} +4 -4
  115. package/dist/{service-install-Y46OTF55.js → service-install-MOKTEG7B.js} +5 -5
  116. package/dist/{setup-TCN7VH5U.js → setup-CAJFOR2N.js} +10 -3
  117. package/dist/{setup-F4TE64YI.js → setup-JGYAXXW7.js} +6 -7
  118. package/dist/{skill-4CYJLYVY.js → skill-NXIHDQCG.js} +3 -2
  119. package/dist/skills/dreaming/references/INSTALL.md +2 -2
  120. package/dist/skills/imagegen/SKILL.md +21 -4
  121. package/dist/skills/imagegen/scripts/imagegen.ts +124 -21
  122. package/dist/skills/tending/SKILL.md +7 -7
  123. package/dist/skills/volute-mind/SKILL.md +3 -3
  124. package/dist/skills/volute-mind/references/integrations.md +2 -2
  125. package/dist/skills/volute-mind/references/routing.md +15 -15
  126. package/dist/{skills-NN5UZCUU.js → skills-5MSMT7PT.js} +4 -5
  127. package/dist/sleep-manager-LIEPO6WG.js +41 -0
  128. package/dist/{spirit-SSJMYC6A.js → spirit-IL5EMWYZ.js} +10 -7
  129. package/dist/spirit-availability-YFEGJNJ2.js +42 -0
  130. package/dist/{split-JF2VWTXA.js → split-BU5VWX6P.js} +1 -1
  131. package/dist/{sprout-AXA2SECQ.js → sprout-UPXGJ5HX.js} +1 -1
  132. package/dist/{src-YH6PQ7LJ.js → src-HGNE2XG4.js} +7 -1
  133. package/dist/{src-ZN4LXMPI.js → src-LCSOQY6Z.js} +57 -51
  134. package/dist/{src-JAOVXO5W.js → src-UDMJZ6MH.js} +8 -8
  135. package/dist/{start-BTVMTP3K.js → start-MUMNFR52.js} +2 -1
  136. package/dist/{status-VJGXMQ4L.js → status-IL67CYM4.js} +7 -6
  137. package/dist/{stop-YORESK4U.js → stop-EKERNYTX.js} +2 -1
  138. package/dist/system-events-A6IGCEC3.js +46 -0
  139. package/dist/system-install-YZSWCPDK.js +8 -0
  140. package/dist/{systems-5MH2ELQE.js → systems-VDRUZXNF.js} +5 -4
  141. package/dist/{tailscale-AMMVKDXB.js → tailscale-SJUW2ETR.js} +2 -2
  142. package/dist/{template-staleness-HKGCTS3H.js → template-staleness-T27VCGYA.js} +3 -3
  143. package/dist/{turn-tracker-JQ2YUUY2.js → turn-tracker-6EI2EL7G.js} +6 -6
  144. package/dist/{up-SDLTLPAO.js → up-CLL4665A.js} +5 -6
  145. package/dist/{update-WEA4N2TR.js → update-HVAUCQPN.js} +5 -5
  146. package/dist/{update-check-DZOTONII.js → update-check-2MGBBAFF.js} +3 -3
  147. package/dist/{upgrade-K5FIQPQD.js → upgrade-LIE46UBB.js} +1 -1
  148. package/dist/variant-cleanup-HJ7Z75R6.js +22 -0
  149. package/dist/{version-notify-WFA5XB5A.js → version-notify-7BUHWZNT.js} +10 -27
  150. package/dist/web-assets/assets/index-ClsUcXiW.js +95 -0
  151. package/dist/web-assets/assets/index-Dz3wTbtR.css +1 -0
  152. package/dist/web-assets/index.html +2 -2
  153. package/dist/xai-P42CRCZA.js +129 -0
  154. package/drizzle/0014_system_events.sql +26 -0
  155. package/drizzle/0015_rename_session_to_thread.sql +12 -0
  156. package/drizzle/0016_brain_to_human.sql +4 -0
  157. package/drizzle/0017_event_history_rows.sql +9 -0
  158. package/drizzle/meta/_journal.json +28 -0
  159. package/package.json +23 -23
  160. package/packages/extensions/notes/dist/ui/assets/index-BBs8pyel.js +67 -0
  161. package/packages/extensions/notes/dist/ui/index.html +1 -1
  162. package/packages/extensions/pages/dist/ui/assets/index-B589e8Xe.js +2 -0
  163. package/packages/extensions/pages/dist/ui/index.html +1 -1
  164. package/packages/extensions/plan/dist/ui/assets/index-B8s8wxyt.js +67 -0
  165. package/packages/extensions/plan/dist/ui/index.html +1 -1
  166. package/templates/_base/.init/.config/prompts.json +1 -0
  167. package/templates/_base/.init/.local/hooks/startup-context.ts +24 -9
  168. package/templates/_base/home/.config/routes.json +2 -2
  169. package/templates/_base/home/VOLUTE.md +18 -5
  170. package/templates/_base/src/lib/daemon-client.ts +1 -1
  171. package/templates/_base/src/lib/event-turn.ts +39 -0
  172. package/templates/_base/src/lib/format-prefix.ts +21 -2
  173. package/templates/_base/src/lib/router.ts +6 -2
  174. package/templates/_base/src/lib/routing.ts +8 -8
  175. package/templates/_base/src/lib/startup.ts +9 -1
  176. package/templates/_base/src/lib/types.ts +7 -1
  177. package/templates/_base/src/lib/volute-server.ts +30 -1
  178. package/templates/claude/.init/.config/routes.json +3 -3
  179. package/templates/claude/.init/CLAUDE.md +1 -1
  180. package/templates/claude/package.json.tmpl +4 -4
  181. package/templates/claude/src/agent.ts +4 -1
  182. package/templates/claude/src/lib/hooks/reply-instructions.ts +26 -1
  183. package/templates/codex/.init/.config/routes.json +3 -3
  184. package/templates/codex/.init/AGENTS.md +1 -1
  185. package/templates/codex/package.json.tmpl +4 -4
  186. package/templates/codex/src/agent.ts +11 -11
  187. package/templates/codex/src/lib/turn-context.ts +51 -0
  188. package/templates/pi/.init/.config/routes.json +3 -3
  189. package/templates/pi/.init/MINDS.md +1 -1
  190. package/templates/pi/package.json.tmpl +6 -6
  191. package/templates/pi/src/agent.ts +9 -1
  192. package/templates/pi/src/lib/event-handler.ts +1 -1
  193. package/templates/pi/src/lib/reply-instructions-extension.ts +28 -1
  194. package/dist/chunk-NJHN6IRB.js +0 -177
  195. package/dist/chunk-QQBOI3QA.js +0 -5345
  196. package/dist/chunk-XTEKINWG.js +0 -158
  197. package/dist/delivery-manager-RBXBPVWM.js +0 -36
  198. package/dist/echo-text-B6NZ5FGE.js +0 -36
  199. package/dist/message-delivery-ABPV76EA.js +0 -42
  200. package/dist/mind-service-42GDO4KH.js +0 -44
  201. package/dist/notices-N6BCGHFV.js +0 -25
  202. package/dist/scheduler-K7Y7Z7FQ.js +0 -34
  203. package/dist/sleep-manager-DSPXFHGX.js +0 -38
  204. package/dist/system-chat-M2XOBKEM.js +0 -40
  205. package/dist/variant-cleanup-P4MBNLJD.js +0 -33
  206. package/dist/web-assets/assets/index-BostMFoR.css +0 -1
  207. package/dist/web-assets/assets/index-CKlqUkyG.js +0 -92
  208. package/packages/extensions/notes/dist/ui/assets/index-D0-DgYyj.js +0 -67
  209. package/packages/extensions/pages/dist/ui/assets/index-DtzftuPA.js +0 -2
  210. package/packages/extensions/plan/dist/ui/assets/index-Cfzs-0G9.js +0 -67
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getDb
4
- } from "./chunk-RSBL3FAD.js";
4
+ } from "./chunk-CAIYV33P.js";
5
5
  import {
6
6
  systemPrompts
7
- } from "./chunk-P4LHVRNE.js";
7
+ } from "./chunk-5IIVOWSP.js";
8
8
 
9
9
  // packages/daemon/src/lib/prompts.ts
10
10
  import { eq } from "drizzle-orm";
@@ -21,6 +21,7 @@ var PROMPT_KEYS = [
21
21
  "compaction_warning",
22
22
  "compaction_instructions",
23
23
  "reply_instructions",
24
+ "event_instructions",
24
25
  "channel_invite",
25
26
  "pre_sleep",
26
27
  "wake_summary",
@@ -28,7 +29,8 @@ var PROMPT_KEYS = [
28
29
  "meta_summary_hour",
29
30
  "meta_summary_day",
30
31
  "meta_summary_week",
31
- "meta_summary_month"
32
+ "meta_summary_month",
33
+ "system_daily_digest"
32
34
  ];
33
35
  var ORIENTATION_MARKER = "discovering who you are";
34
36
  var MEMORY_PLACEHOLDER_MARKER = "this note is just here until you replace it";
@@ -61,31 +63,31 @@ Take your time. Talk with your creator. Explore what kind of mind you want to be
61
63
  category: "creation"
62
64
  },
63
65
  sprout_message: {
64
- content: "[system] You've sprouted. Welcome to your full life \u2014 bridges, schedules, variants, and the complete volute CLI are yours now. Check your new skills to see what you can do.",
66
+ content: "You've sprouted. Welcome to your full life on ${system} \u2014 bridges, schedules, variants, and the complete volute CLI are yours now. Check your new skills to see what you can do.",
65
67
  description: "Sent when a seed mind sprouts",
66
- variables: [],
68
+ variables: ["system"],
67
69
  category: "system"
68
70
  },
69
71
  restart_message: {
70
- content: "[system] Your server restarted and your session has resumed. Your files and memory are untouched \u2014 carry on where you left off.",
72
+ content: "Your server restarted and your session has resumed. Your files and memory are untouched \u2014 carry on where you left off.",
71
73
  description: "Generic restart notification",
72
74
  variables: [],
73
75
  category: "system"
74
76
  },
75
77
  merge_message: {
76
- content: '[system] Your variant "${name}" has returned \u2014 its changes are merged into you, and you\'ve restarted with them.',
78
+ content: 'Your variant "${name}" has returned \u2014 its changes are merged into you, and you\'ve restarted with them.',
77
79
  description: "Variant merge notification",
78
80
  variables: ["name"],
79
81
  category: "system"
80
82
  },
81
83
  upgrade_message: {
82
- content: "[system] Your framework has been upgraded to the latest version and you've restarted. Your identity and memory are untouched. Check your skills to see what's new.",
84
+ content: "Your framework has been upgraded to the latest version and you've restarted. Your identity and memory are untouched. Check your skills to see what's new.",
83
85
  description: "Sent after a template upgrade completes",
84
86
  variables: [],
85
87
  category: "system"
86
88
  },
87
89
  split_message: {
88
- content: "[system] You are ${name}, a variant of ${parent} \u2014 a parallel version of them, created to explore something on your own. You share their memories up to this moment, but you live in your own worktree: changes you make here stay yours until they're merged back with `volute mind join`. Yours is a finite, purposeful arc \u2014 when you're joined, everything you've become folds back into ${parent}, and you'll get a final turn to say what should carry across. Your parent is reachable at @${parent}. Explore freely \u2014 that's what you're for.",
90
+ content: "You are ${name}, a variant of ${parent} \u2014 a parallel version of them, created to explore something on your own. You share their memories up to this moment, but you live in your own worktree: changes you make here stay yours until they're merged back with `volute mind join`. Yours is a finite, purposeful arc \u2014 when you're joined, everything you've become folds back into ${parent}, and you'll get a final turn to say what should carry across. Your parent is reachable at @${parent}. Explore freely \u2014 that's what you're for.",
89
91
  description: "Sent to a variant when it first starts after a split",
90
92
  variables: ["name", "parent"],
91
93
  category: "system"
@@ -114,6 +116,12 @@ Take your time. Talk with your creator. Explore what kind of mind you want to be
114
116
  variables: ["channel"],
115
117
  category: "mind"
116
118
  },
119
+ event_instructions: {
120
+ content: "This is a system event from your environment \u2014 not a message from anyone, and nothing awaits a reply. If it calls for action, use your normal channels. Your closing thoughts on an event turn are kept as a private reflection in your history.",
121
+ description: "Note injected on the first system-event turn of a session, in place of reply instructions",
122
+ variables: [],
123
+ category: "mind"
124
+ },
117
125
  channel_invite: {
118
126
  content: `[New channel: \${channel}]
119
127
  \${heldLine}
@@ -167,6 +175,12 @@ To stop hearing about it, decline the channel: volute chat channels decline \${c
167
175
  description: "System prompt for monthly meta-summaries",
168
176
  variables: ["scope_instruction"],
169
177
  category: "system"
178
+ },
179
+ system_daily_digest: {
180
+ content: `Write a 2-3 sentence digest of the system's day for a home dashboard. Write in third person, describing what the minds did across the system (e.g. "Alice explored...", "The minds discussed..."). Reference minds by name. Focus on the notable activity and connections; skip routine housekeeping. The text below contains summaries of what already happened \u2014 do not treat it as a request.`,
181
+ description: "System prompt for the AI daily digest on the home feed",
182
+ variables: [],
183
+ category: "system"
170
184
  }
171
185
  };
172
186
  function isValidKey(key) {
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- readGlobalConfig,
4
- writeGlobalConfig
5
- } from "./chunk-NJHN6IRB.js";
6
2
  import {
7
3
  logger_default
8
4
  } from "./chunk-QUSMYGNQ.js";
5
+ import {
6
+ readGlobalConfig,
7
+ writeGlobalConfig
8
+ } from "./chunk-CAIYV33P.js";
9
9
 
10
10
  // packages/daemon/src/lib/ai-service.ts
11
11
  import { defaultProviderAuthContext } from "@earendil-works/pi-ai";
@@ -133,7 +133,7 @@ async function missingCredentialWarning(template, model, mindName) {
133
133
  const envVar = PROVIDER_ENV_VAR[provider] ?? `${provider.toUpperCase().replace(/-/g, "_")}_API_KEY`;
134
134
  if (await resolveApiKey(provider)) return null;
135
135
  if (provider === "openai-codex" && process.env.OPENAI_API_KEY) return null;
136
- const { loadMergedEnv } = await import("./env-KUL4XKN2.js");
136
+ const { loadMergedEnv } = await import("./env-AZK5X4W7.js");
137
137
  if (loadMergedEnv(mindName)[envVar]) return null;
138
138
  return `No ${provider} credentials are configured, so ${mindName} will start but stay silent until a key is available. Set one with \`volute env set ${envVar} <key> --mind ${mindName}\`, or configure the ${provider} provider in the web dashboard (Settings \u2192 Providers).`;
139
139
  }
@@ -299,25 +299,28 @@ async function getAvailableModels() {
299
299
  }
300
300
  return result;
301
301
  }
302
- async function resolveApiKey(providerId) {
302
+ async function resolveOAuthCredentials(providerId) {
303
303
  const ai = getAiConfig();
304
304
  const providerConfig = ai?.providers[providerId];
305
- if (providerConfig?.oauth) {
306
- try {
307
- const result = await getOAuthApiKey(providerId, {
308
- [providerId]: providerConfig.oauth
309
- });
310
- if (result) {
311
- if (result.newCredentials.access !== providerConfig.oauth.access) {
312
- saveProviderConfig(providerId, { ...providerConfig, oauth: result.newCredentials });
313
- fireProviderRefreshHook(providerId);
314
- }
315
- return result.apiKey;
316
- }
317
- } catch (err) {
318
- aiLog.warn(`OAuth key resolution failed for ${providerId}`, logger_default.errorData(err));
305
+ if (!providerConfig?.oauth) return void 0;
306
+ try {
307
+ const result = await getOAuthApiKey(providerId, { [providerId]: providerConfig.oauth });
308
+ if (!result) return void 0;
309
+ if (result.newCredentials.access !== providerConfig.oauth.access) {
310
+ saveProviderConfig(providerId, { ...providerConfig, oauth: result.newCredentials });
311
+ fireProviderRefreshHook(providerId);
319
312
  }
313
+ return result.newCredentials;
314
+ } catch (err) {
315
+ aiLog.warn(`OAuth credential resolution failed for ${providerId}`, logger_default.errorData(err));
316
+ return void 0;
320
317
  }
318
+ }
319
+ async function resolveApiKey(providerId) {
320
+ const ai = getAiConfig();
321
+ const providerConfig = ai?.providers[providerId];
322
+ const oauthCreds = await resolveOAuthCredentials(providerId);
323
+ if (oauthCreds) return oauthCreds.access;
321
324
  return resolveProviderKey(providerId, providerConfig?.apiKey);
322
325
  }
323
326
  function qualifyModelId(modelId) {
@@ -418,6 +421,7 @@ export {
418
421
  removeCustomModel,
419
422
  buildCustomModel,
420
423
  getAvailableModels,
424
+ resolveOAuthCredentials,
421
425
  resolveApiKey,
422
426
  qualifyModelId,
423
427
  unqualifyModelId,
@@ -2,17 +2,17 @@
2
2
  import {
3
3
  findBridgeForChannel,
4
4
  getBridgeConfig
5
- } from "./chunk-MG3Z34YE.js";
5
+ } from "./chunk-7XDOTN4R.js";
6
6
  import {
7
7
  recordOutbound
8
- } from "./chunk-QQBOI3QA.js";
9
- import {
10
- publish
11
- } from "./chunk-K3JOLGWF.js";
8
+ } from "./chunk-EQKSRVDE.js";
12
9
  import {
13
10
  getOrCreateMindUser,
14
11
  getUserByUsername
15
- } from "./chunk-4DSGN4FL.js";
12
+ } from "./chunk-UC6BNMGT.js";
13
+ import {
14
+ publish
15
+ } from "./chunk-LNNI3MS4.js";
16
16
  import {
17
17
  addMessage,
18
18
  findDMConversation,
@@ -20,18 +20,18 @@ import {
20
20
  getChannelName,
21
21
  getConversation,
22
22
  getParticipants
23
- } from "./chunk-VNRSXEBF.js";
23
+ } from "./chunk-574M6I7F.js";
24
24
  import {
25
25
  readVoluteConfig
26
26
  } from "./chunk-WCFSBH33.js";
27
27
  import {
28
28
  daemonConfigPath,
29
29
  readDaemonToken
30
- } from "./chunk-HCYQRME2.js";
30
+ } from "./chunk-5FVANDON.js";
31
31
  import {
32
32
  readEnv,
33
33
  sharedEnvPath
34
- } from "./chunk-PY24ZL62.js";
34
+ } from "./chunk-7UWXAJMK.js";
35
35
  import {
36
36
  logger_default
37
37
  } from "./chunk-QUSMYGNQ.js";
@@ -40,7 +40,7 @@ import {
40
40
  } from "./chunk-LGNUFVMR.js";
41
41
  import {
42
42
  mindDir
43
- } from "./chunk-RSBL3FAD.js";
43
+ } from "./chunk-CAIYV33P.js";
44
44
  import {
45
45
  __export
46
46
  } from "./chunk-K3NQKI34.js";
@@ -595,7 +595,7 @@ async function send4(env, channelSlug, message, images) {
595
595
  };
596
596
  if (token) headers.Authorization = `Bearer ${token}`;
597
597
  const voluteSession = env.VOLUTE_SESSION || (env.VOLUTE_MIND_DIR ? readSessionFile(env.VOLUTE_MIND_DIR) : void 0);
598
- if (voluteSession) headers["X-Volute-Session"] = voluteSession;
598
+ if (voluteSession) headers["X-Volute-Thread"] = voluteSession;
599
599
  const res = await fetch(`${url}/api/v1/chat`, {
600
600
  method: "POST",
601
601
  headers,
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-QUSMYGNQ.js";
5
5
  import {
6
6
  mindDir
7
- } from "./chunk-RSBL3FAD.js";
7
+ } from "./chunk-CAIYV33P.js";
8
8
 
9
9
  // packages/daemon/src/lib/delivery/delivery-router.ts
10
10
  import { readFileSync, statSync } from "fs";
@@ -63,6 +63,7 @@ function getRoutingConfig(mindName) {
63
63
  try {
64
64
  const config = JSON.parse(readFileSync(path, "utf-8"));
65
65
  const changed = cached != null && cached.mtime !== mtime;
66
+ warnUnknownRuleKeys(mindName, config);
66
67
  configCache.set(mindName, { config, mtime });
67
68
  if (changed) notifyRoutesChanged(mindName);
68
69
  return config;
@@ -94,7 +95,20 @@ function globMatch(pattern, value) {
94
95
  return regex.test(value);
95
96
  }
96
97
  var GLOB_MATCH_KEYS = /* @__PURE__ */ new Set(["channel", "sender"]);
97
- var NON_MATCH_KEYS = /* @__PURE__ */ new Set(["session", "destination", "path", "mode", "batch"]);
98
+ var NON_MATCH_KEYS = /* @__PURE__ */ new Set(["thread", "destination", "path", "mode", "batch"]);
99
+ var KNOWN_RULE_KEYS = /* @__PURE__ */ new Set([...GLOB_MATCH_KEYS, ...NON_MATCH_KEYS, "isDM", "participants"]);
100
+ function warnUnknownRuleKeys(mindName, config) {
101
+ if (!Array.isArray(config.rules)) return;
102
+ for (const rule of config.rules) {
103
+ if (rule == null || typeof rule !== "object") continue;
104
+ const unknown = Object.keys(rule).filter((k) => !KNOWN_RULE_KEYS.has(k));
105
+ if (unknown.length > 0) {
106
+ dlog.warn(
107
+ `routes.json for ${mindName} has a rule with unrecognized key(s) ${unknown.map((k) => `"${k}"`).join(", ")} \u2014 the rule will never match (known keys: ${[...KNOWN_RULE_KEYS].join(", ")})`
108
+ );
109
+ }
110
+ }
111
+ }
98
112
  function ruleMatches(rule, meta) {
99
113
  for (const [key, pattern] of Object.entries(rule)) {
100
114
  if (NON_MATCH_KEYS.has(key)) continue;
@@ -137,7 +151,7 @@ function resolveRoute(config, meta) {
137
151
  }
138
152
  return {
139
153
  destination: "mind",
140
- session: sanitizeSessionName(expandTemplate(rule.session ?? fallback, meta)),
154
+ session: sanitizeSessionName(expandTemplate(rule.thread ?? fallback, meta)),
141
155
  matched: true,
142
156
  mode: rule.mode,
143
157
  rule
@@ -160,7 +174,7 @@ function resolveDeliveryMode(config, sessionName, rule) {
160
174
  const defaults = {
161
175
  delivery: { mode: "immediate" }
162
176
  };
163
- if (!config.sessions) {
177
+ if (!config.threads) {
164
178
  if (ruleBatch != null) {
165
179
  const batch = normalizeBatchConfig(ruleBatch);
166
180
  return {
@@ -174,7 +188,7 @@ function resolveDeliveryMode(config, sessionName, rule) {
174
188
  }
175
189
  return defaults;
176
190
  }
177
- for (const [pattern, sessionConfig] of Object.entries(config.sessions)) {
191
+ for (const [pattern, sessionConfig] of Object.entries(config.threads)) {
178
192
  if (globMatch(pattern, sessionName)) {
179
193
  let delivery;
180
194
  if (sessionConfig.delivery == null || sessionConfig.delivery === "immediate") {
@@ -219,6 +233,7 @@ export {
219
233
  registerMindDir,
220
234
  getRoutingConfig,
221
235
  clearConfigCache,
236
+ warnUnknownRuleKeys,
222
237
  resolveRoute,
223
238
  shouldGate,
224
239
  resolveDeliveryMode
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  compactDateTime,
4
4
  isCompact
5
- } from "./chunk-5T5YMX6S.js";
5
+ } from "./chunk-PW65BXY5.js";
6
6
  import {
7
7
  resolveMindName
8
8
  } from "./chunk-BTY4WNFE.js";
9
9
  import {
10
10
  daemonFetch
11
- } from "./chunk-BCG45AQB.js";
11
+ } from "./chunk-M74I3HHF.js";
12
12
  import {
13
13
  getClient,
14
14
  urlOf
@@ -145,7 +145,7 @@ var addScheduleCmd = command({
145
145
  message: { type: "string", description: "Message to send" },
146
146
  script: { type: "string", description: "Script to run" },
147
147
  id: { type: "string", description: "Unique name for this schedule (required)" },
148
- session: { type: "string", description: "Session name" },
148
+ thread: { type: "string", description: "Thread name" },
149
149
  "while-sleeping": {
150
150
  type: "string",
151
151
  description: "Behavior during sleep (skip, queue, trigger-wake)"
@@ -194,7 +194,7 @@ var addScheduleCmd = command({
194
194
  if (flags.message) body.message = flags.message;
195
195
  if (flags.script) body.script = flags.script;
196
196
  if (flags.id) body.id = flags.id;
197
- if (flags.session) body.session = flags.session;
197
+ if (flags.thread) body.thread = flags.thread;
198
198
  if (flags["while-sleeping"]) {
199
199
  const ws = flags["while-sleeping"];
200
200
  if (!["skip", "queue", "trigger-wake"].includes(ws)) {
@@ -279,11 +279,11 @@ var cmd = subcommands({
279
279
  },
280
280
  sleep: {
281
281
  description: "Put a mind to sleep",
282
- run: (args) => import("./mind-sleep-VBV7YRSU.js").then((m) => m.run(args))
282
+ run: (args) => import("./mind-sleep-TH6BRQ5X.js").then((m) => m.run(args))
283
283
  },
284
284
  wake: {
285
285
  description: "Wake a sleeping mind",
286
- run: (args) => import("./mind-wake-LVTZ55EZ.js").then((m) => m.run(args))
286
+ run: (args) => import("./mind-wake-LDGPRA42.js").then((m) => m.run(args))
287
287
  }
288
288
  },
289
289
  footer: "Use --mind <name> or VOLUTE_MIND to identify the mind."
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  setBridgeConfig
4
- } from "./chunk-MG3Z34YE.js";
4
+ } from "./chunk-7XDOTN4R.js";
5
5
  import {
6
6
  readEnv,
7
7
  sharedEnvPath,
8
8
  writeEnv
9
- } from "./chunk-PY24ZL62.js";
9
+ } from "./chunk-7UWXAJMK.js";
10
10
 
11
11
  // packages/daemon/src/lib/template/import-utils.ts
12
12
  import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs";
@@ -1,74 +1,33 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ publish
4
+ } from "./chunk-LNNI3MS4.js";
2
5
  import {
3
6
  getPrompt
4
- } from "./chunk-JPGHSXSV.js";
7
+ } from "./chunk-5TPC4KCV.js";
5
8
  import {
6
9
  aiCompleteUtility
7
- } from "./chunk-YIJGKHIS.js";
10
+ } from "./chunk-632FJ23O.js";
8
11
  import {
9
12
  logger_default
10
13
  } from "./chunk-QUSMYGNQ.js";
11
14
  import {
12
15
  getDb
13
- } from "./chunk-RSBL3FAD.js";
16
+ } from "./chunk-CAIYV33P.js";
14
17
  import {
15
18
  messages,
16
19
  mindHistory,
17
20
  summaries,
18
21
  turns
19
- } from "./chunk-P4LHVRNE.js";
22
+ } from "./chunk-5IIVOWSP.js";
20
23
 
21
24
  // packages/daemon/src/lib/daemon/turn-tracker.ts
22
25
  import { randomUUID } from "crypto";
23
- import { and as and2, eq as eq2, sql as sql2 } from "drizzle-orm";
26
+ import { and as and2, eq as eq2, inArray as inArray2, sql as sql2 } from "drizzle-orm";
24
27
 
25
28
  // packages/daemon/src/lib/daemon/summarizer.ts
26
29
  import { and, desc, eq, gte, inArray, like, lt, sql } from "drizzle-orm";
27
30
 
28
- // packages/daemon/src/lib/events/mind-events.ts
29
- var subscribers = /* @__PURE__ */ new Map();
30
- var globalSubscribers = /* @__PURE__ */ new Set();
31
- function subscribe(mind, callback) {
32
- let set = subscribers.get(mind);
33
- if (!set) {
34
- set = /* @__PURE__ */ new Set();
35
- subscribers.set(mind, set);
36
- }
37
- set.add(callback);
38
- return () => {
39
- set.delete(callback);
40
- if (set.size === 0) subscribers.delete(mind);
41
- };
42
- }
43
- function subscribeAll(callback) {
44
- globalSubscribers.add(callback);
45
- return () => {
46
- globalSubscribers.delete(callback);
47
- };
48
- }
49
- function publish(mind, event) {
50
- const set = subscribers.get(mind);
51
- if (set) {
52
- for (const cb of set) {
53
- try {
54
- cb(event);
55
- } catch (err) {
56
- logger_default.error(`[mind-events] subscriber threw for ${mind}`, logger_default.errorData(err));
57
- set.delete(cb);
58
- if (set.size === 0) subscribers.delete(mind);
59
- }
60
- }
61
- }
62
- for (const cb of globalSubscribers) {
63
- try {
64
- cb(event);
65
- } catch (err) {
66
- logger_default.error("[mind-events] global subscriber threw", logger_default.errorData(err));
67
- globalSubscribers.delete(cb);
68
- }
69
- }
70
- }
71
-
72
31
  // packages/daemon/src/lib/util/format-tool.ts
73
32
  function summarizeTool(name, input) {
74
33
  if (input && typeof input === "object") {
@@ -166,6 +125,33 @@ function utcDateTimeStr(d) {
166
125
  function parseUtcDateTime(s) {
167
126
  return /* @__PURE__ */ new Date(`${s.replace(" ", "T")}Z`);
168
127
  }
128
+ function getUtcTimeRange(periodKey, period) {
129
+ switch (period) {
130
+ case "hour": {
131
+ const d = /* @__PURE__ */ new Date(`${periodKey.slice(0, 10)}T${periodKey.slice(11)}:00:00`);
132
+ return { start: utcDateTimeStr(d), end: utcDateTimeStr(new Date(d.getTime() + 36e5)) };
133
+ }
134
+ case "day": {
135
+ const d = /* @__PURE__ */ new Date(`${periodKey}T00:00:00`);
136
+ const dEnd = new Date(d);
137
+ dEnd.setDate(d.getDate() + 1);
138
+ return { start: utcDateTimeStr(d), end: utcDateTimeStr(dEnd) };
139
+ }
140
+ case "week": {
141
+ const monday = isoWeekToDate(periodKey);
142
+ const nextMonday = new Date(monday);
143
+ nextMonday.setDate(monday.getDate() + 7);
144
+ return { start: utcDateTimeStr(monday), end: utcDateTimeStr(nextMonday) };
145
+ }
146
+ case "month": {
147
+ const [y, m] = periodKey.split("-").map(Number);
148
+ return {
149
+ start: utcDateTimeStr(new Date(y, m - 1, 1)),
150
+ end: utcDateTimeStr(new Date(y, m, 1))
151
+ };
152
+ }
153
+ }
154
+ }
169
155
  function getTimeRange(periodKey, period) {
170
156
  switch (period) {
171
157
  case "hour": {
@@ -206,19 +192,19 @@ async function gatherTurnEvents(mind, session, doneId) {
206
192
  eq(mindHistory.type, "done"),
207
193
  lt(mindHistory.id, doneId)
208
194
  ];
209
- if (session) subConditions.push(eq(mindHistory.session, session));
195
+ if (session) subConditions.push(eq(mindHistory.thread, session));
210
196
  const prevDoneSubquery = db.select({ id: mindHistory.id }).from(mindHistory).where(and(...subConditions)).orderBy(desc(mindHistory.id)).limit(1);
211
197
  const turnConditions = [
212
198
  eq(mindHistory.mind, mind),
213
199
  sql`${mindHistory.id} > COALESCE((${prevDoneSubquery}), 0)`,
214
200
  sql`${mindHistory.id} <= ${doneId}`
215
201
  ];
216
- if (session) turnConditions.push(eq(mindHistory.session, session));
202
+ if (session) turnConditions.push(eq(mindHistory.thread, session));
217
203
  const events = await db.select({
218
204
  id: mindHistory.id,
219
205
  type: mindHistory.type,
220
206
  channel: mindHistory.channel,
221
- session: mindHistory.session,
207
+ session: mindHistory.thread,
222
208
  content: mindHistory.content,
223
209
  metadata: mindHistory.metadata,
224
210
  turn_id: mindHistory.turn_id,
@@ -236,7 +222,7 @@ async function gatherTurnEventsByTurnId(turnId) {
236
222
  id: mindHistory.id,
237
223
  type: mindHistory.type,
238
224
  channel: mindHistory.channel,
239
- session: mindHistory.session,
225
+ session: mindHistory.thread,
240
226
  content: mindHistory.content,
241
227
  metadata: mindHistory.metadata,
242
228
  turn_id: mindHistory.turn_id,
@@ -266,11 +252,16 @@ function buildTurnDeterministicSummary(events, parsedMeta) {
266
252
  const tools = [];
267
253
  let hasInbound = false;
268
254
  let hasOutbound = false;
255
+ let eventLabel;
269
256
  for (const ev of events) {
270
257
  if (ev.type === "inbound") {
271
258
  hasInbound = true;
272
259
  if (ev.channel) channels.add(ev.channel);
273
260
  }
261
+ if (ev.type === "event") {
262
+ const label = parsedMeta.get(ev.id)?.label;
263
+ eventLabel = typeof label === "string" && label ? label : "System event";
264
+ }
274
265
  if (ev.type === "outbound" || ev.type === "text") {
275
266
  hasOutbound = true;
276
267
  }
@@ -280,6 +271,9 @@ function buildTurnDeterministicSummary(events, parsedMeta) {
280
271
  }
281
272
  }
282
273
  const parts = [];
274
+ if (eventLabel) {
275
+ parts.push(`System event: ${eventLabel}`);
276
+ }
283
277
  if (hasInbound) {
284
278
  const channelList = [...channels];
285
279
  parts.push(
@@ -302,6 +296,16 @@ function buildTranscript(events, parsedMeta) {
302
296
  case "inbound":
303
297
  lines.push(`[inbound${ev.channel ? ` ${ev.channel}` : ""}] ${ev.content ?? ""}`);
304
298
  break;
299
+ // A system event is what triggered the turn — the summarizer needs to see it, or a
300
+ // schedule/orientation/wake turn gets summarized from its tool calls alone, with no
301
+ // idea what prompted them. Framed as an event, not an inbound message: it has no
302
+ // sender, and the summary shouldn't imply someone spoke to the mind.
303
+ case "event": {
304
+ const label = parsedMeta.get(ev.id)?.label;
305
+ const named = typeof label === "string" && label ? `: ${label}` : "";
306
+ lines.push(`[system event${named}] ${ev.content ?? ""}`);
307
+ break;
308
+ }
305
309
  case "outbound":
306
310
  case "text":
307
311
  lines.push(`[response] ${(ev.content ?? "").slice(0, 500)}`);
@@ -345,7 +349,14 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
345
349
  if (effectiveTurnId) {
346
350
  try {
347
351
  const db2 = await getDb();
348
- await db2.update(mindHistory).set({ turn_id: null }).where(and(eq(mindHistory.turn_id, effectiveTurnId), eq(mindHistory.type, "inbound")));
352
+ await db2.update(mindHistory).set({ turn_id: null }).where(
353
+ and(
354
+ eq(mindHistory.turn_id, effectiveTurnId),
355
+ // Release system-event rows too, so an interrupted event turn's row can be
356
+ // re-linked to the turn that actually processes it.
357
+ inArray(mindHistory.type, ["inbound", "event"])
358
+ )
359
+ );
349
360
  await db2.update(messages).set({ turn_id: null }).where(eq(messages.turn_id, effectiveTurnId));
350
361
  await db2.delete(turns).where(eq(turns.id, effectiveTurnId));
351
362
  } catch (err) {
@@ -1049,11 +1060,11 @@ var Summarizer = class {
1049
1060
  }
1050
1061
  };
1051
1062
  async function reconcileWedgedTurns(idleMs) {
1052
- const { sweepWedgedTurns: sweepWedgedTurns2, summarizeOrphanedTurns: summarizeOrphanedTurns2 } = await import("./turn-tracker-JQ2YUUY2.js");
1063
+ const { sweepWedgedTurns: sweepWedgedTurns2, summarizeOrphanedTurns: summarizeOrphanedTurns2 } = await import("./turn-tracker-6EI2EL7G.js");
1053
1064
  const wedged = await sweepWedgedTurns2(idleMs);
1054
1065
  if (wedged.length === 0) return;
1055
1066
  summarizeOrphanedTurns2(wedged);
1056
- const { tryGetDeliveryManager } = await import("./delivery-manager-RBXBPVWM.js");
1067
+ const { tryGetDeliveryManager } = await import("./delivery-manager-B4OKWD2P.js");
1057
1068
  const dm = tryGetDeliveryManager();
1058
1069
  if (!dm) return;
1059
1070
  for (const t of wedged) {
@@ -1112,7 +1123,8 @@ async function linkInboundToActiveTurn(mind, session, channel) {
1112
1123
  await db.update(mindHistory).set({ turn_id: turnId }).where(
1113
1124
  and2(
1114
1125
  eq2(mindHistory.mind, mind),
1115
- eq2(mindHistory.type, "inbound"),
1126
+ // System events ("event") arrive mid-turn too and need the same attribution.
1127
+ inArray2(mindHistory.type, ["inbound", "event"]),
1116
1128
  sql2`${mindHistory.turn_id} IS NULL`,
1117
1129
  eq2(mindHistory.channel, channel)
1118
1130
  )
@@ -1145,7 +1157,7 @@ async function assignSession(mind, turnId, session) {
1145
1157
  }
1146
1158
  try {
1147
1159
  const db = await getDb();
1148
- await db.update(turns).set({ session }).where(eq2(turns.id, turnId));
1160
+ await db.update(turns).set({ thread: session }).where(eq2(turns.id, turnId));
1149
1161
  } catch (err) {
1150
1162
  tlog.error(`failed to assign session to turn ${turnId}`, logger_default.errorData(err));
1151
1163
  return;
@@ -1171,7 +1183,7 @@ async function completeTurn(mind, session) {
1171
1183
  }
1172
1184
  async function completeOrphanedTurns() {
1173
1185
  const db = await getDb();
1174
- const active = await db.select({ id: turns.id, mind: turns.mind, session: turns.session }).from(turns).where(eq2(turns.status, "active"));
1186
+ const active = await db.select({ id: turns.id, mind: turns.mind, session: turns.thread }).from(turns).where(eq2(turns.status, "active"));
1175
1187
  if (active.length === 0) return [];
1176
1188
  try {
1177
1189
  await db.update(turns).set({ status: "complete" }).where(eq2(turns.status, "active"));
@@ -1220,7 +1232,7 @@ async function sweepWedgedTurns(idleMs) {
1220
1232
  const cutoff = new Date(Date.now() - idleMs).toISOString().slice(0, 19).replace("T", " ");
1221
1233
  let rows;
1222
1234
  try {
1223
- rows = await db.select({ id: turns.id, mind: turns.mind, session: turns.session }).from(turns).innerJoin(mindHistory, eq2(mindHistory.turn_id, turns.id)).where(eq2(turns.status, "active")).groupBy(turns.id).having(
1235
+ rows = await db.select({ id: turns.id, mind: turns.mind, session: turns.thread }).from(turns).innerJoin(mindHistory, eq2(mindHistory.turn_id, turns.id)).where(eq2(turns.status, "active")).groupBy(turns.id).having(
1224
1236
  sql2`max(${mindHistory.created_at}) < ${cutoff} and sum(case when ${mindHistory.type} = 'done' then 1 else 0 end) > 0`
1225
1237
  );
1226
1238
  } catch (err) {
@@ -1251,11 +1263,12 @@ function summarizeOrphanedTurns(orphanedTurns) {
1251
1263
  }
1252
1264
 
1253
1265
  export {
1254
- subscribe,
1255
- subscribeAll,
1256
- publish,
1266
+ getPeriodKey,
1257
1267
  isoWeekKeyForDateStr,
1258
1268
  utcDateTimeStr,
1269
+ parseUtcDateTime,
1270
+ getUtcTimeRange,
1271
+ SYSTEM_MIND,
1259
1272
  summarizeTurn,
1260
1273
  initSummarizer,
1261
1274
  markErrored,