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
@@ -0,0 +1,548 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ publish
4
+ } from "./chunk-LNNI3MS4.js";
5
+ import {
6
+ logger_default
7
+ } from "./chunk-QUSMYGNQ.js";
8
+ import {
9
+ findMind,
10
+ getBaseName,
11
+ getDb
12
+ } from "./chunk-CAIYV33P.js";
13
+ import {
14
+ mindHistory,
15
+ systemEvents,
16
+ turns
17
+ } from "./chunk-5IIVOWSP.js";
18
+
19
+ // packages/daemon/src/lib/chat/system-events.ts
20
+ import { and, asc, desc, eq, gt, isNull, lte, or, sql } from "drizzle-orm";
21
+ var elog = logger_default.child("system-events");
22
+ var MIND_LEVEL_THREAD = "";
23
+ var MAX_NEXT_TURN_EVENTS = 100;
24
+ var FLUSH_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
25
+ function parseMeta(meta, context) {
26
+ if (!meta) return {};
27
+ try {
28
+ return JSON.parse(meta);
29
+ } catch (err) {
30
+ elog.error(`corrupt event meta${context ? ` (${context})` : ""}: ${meta}`, logger_default.errorData(err));
31
+ return {};
32
+ }
33
+ }
34
+ function eventLabel(type, meta) {
35
+ const m = meta ?? {};
36
+ const s = (k) => typeof m[k] === "string" ? m[k] : void 0;
37
+ switch (type) {
38
+ case "schedule":
39
+ return s("scheduleId") ? `Schedule: ${s("scheduleId")}` : "Schedule";
40
+ case "sleep":
41
+ return "Time to rest";
42
+ case "wake":
43
+ return "Woke from sleep";
44
+ case "lifecycle":
45
+ switch (s("subtype")) {
46
+ case "merge":
47
+ return "Variant merged in";
48
+ case "split":
49
+ return "Split into a variant";
50
+ case "variant-created":
51
+ return "Variant split off";
52
+ case "sprout":
53
+ return "Sprouted";
54
+ case "upgrade":
55
+ return "Framework upgraded";
56
+ case "restart":
57
+ return "Server restarted";
58
+ case "farewell":
59
+ return "Farewell before merge";
60
+ case "sprout-welcome":
61
+ return "A mind sprouted";
62
+ default:
63
+ return "Lifecycle";
64
+ }
65
+ case "orientation":
66
+ return "Orientation";
67
+ case "budget":
68
+ return "Token budget";
69
+ case "version":
70
+ return "New version available";
71
+ case "channel":
72
+ return "Channel invite";
73
+ case "file-share":
74
+ return "File offered";
75
+ case "webhook":
76
+ return s("source") ? `Webhook: ${s("source")}` : "Webhook";
77
+ case "notice":
78
+ switch (s("subtype")) {
79
+ case "crash":
80
+ return "Crash notice";
81
+ case "turn_error":
82
+ return "Turn error";
83
+ case "startup":
84
+ return "Startup notice";
85
+ case "extension":
86
+ return s("reason") ? `Notice: ${s("reason")}` : "Notice";
87
+ default:
88
+ return "Notice";
89
+ }
90
+ default:
91
+ return type.charAt(0).toUpperCase() + type.slice(1);
92
+ }
93
+ }
94
+ async function recordNotice(input) {
95
+ const type = input.kind === "budget" ? "budget" : "notice";
96
+ await deliverEvent(input.mind, {
97
+ type,
98
+ body: input.detail,
99
+ thread: input.thread,
100
+ delivery: "next-turn",
101
+ meta: {
102
+ subtype: input.kind,
103
+ reason: input.reason,
104
+ ...input.raw ? { raw: input.raw } : {}
105
+ }
106
+ });
107
+ }
108
+ function eventChannel(type, id) {
109
+ return `event:${type}:${id}`;
110
+ }
111
+ async function recordEventRow(mind, event) {
112
+ const channel = eventChannel(event.type, event.id);
113
+ const label = eventLabel(event.type, parseMeta(event.meta, `event ${event.id}`));
114
+ const metadata = { systemEventId: event.id, label };
115
+ try {
116
+ const db = await getDb();
117
+ const baseName = await getBaseName(mind);
118
+ await db.insert(mindHistory).values({
119
+ mind: baseName,
120
+ type: "event",
121
+ channel,
122
+ sender: null,
123
+ content: event.body,
124
+ metadata: JSON.stringify(metadata)
125
+ });
126
+ publish(baseName, {
127
+ mind: baseName,
128
+ type: "event",
129
+ channel,
130
+ content: event.body,
131
+ metadata
132
+ });
133
+ } catch (err) {
134
+ elog.warn(`failed to persist event row for ${mind}`, logger_default.errorData(err));
135
+ }
136
+ }
137
+ var staleTemplateWarned = /* @__PURE__ */ new Set();
138
+ async function postEventEnvelope(mind, event) {
139
+ const entry = await findMind(mind);
140
+ if (!entry) {
141
+ elog.warn(`cannot deliver event ${event.id} to ${mind}: mind not found`);
142
+ return false;
143
+ }
144
+ const meta = parseMeta(event.meta, `event ${event.id}`);
145
+ try {
146
+ const res = await fetch(`http://127.0.0.1:${entry.port}/message`, {
147
+ method: "POST",
148
+ headers: { "Content-Type": "application/json" },
149
+ body: JSON.stringify({
150
+ kind: "event",
151
+ event: {
152
+ id: event.id,
153
+ type: event.type,
154
+ label: eventLabel(event.type, meta),
155
+ body: event.body,
156
+ at: event.created_at
157
+ },
158
+ session: event.thread
159
+ }),
160
+ signal: AbortSignal.timeout(1e4)
161
+ });
162
+ if (!res.ok) {
163
+ elog.warn(`mind ${mind} rejected event ${event.id}: HTTP ${res.status}`);
164
+ return false;
165
+ }
166
+ try {
167
+ const ack = await res.json();
168
+ if (ack.event !== true && !staleTemplateWarned.has(mind)) {
169
+ staleTemplateWarned.add(mind);
170
+ elog.warn(
171
+ `mind ${mind} ack'd an event without event support \u2014 its template predates system events; run \`volute mind upgrade ${mind}\``
172
+ );
173
+ }
174
+ } catch {
175
+ if (!staleTemplateWarned.has(mind)) {
176
+ staleTemplateWarned.add(mind);
177
+ elog.warn(
178
+ `mind ${mind} ack'd an event without event support \u2014 its template predates system events; run \`volute mind upgrade ${mind}\``
179
+ );
180
+ }
181
+ }
182
+ return true;
183
+ } catch (err) {
184
+ elog.warn(`failed to POST event ${event.id} to ${mind}`, logger_default.errorData(err));
185
+ return false;
186
+ }
187
+ }
188
+ async function markDelivered(id, extraMeta) {
189
+ try {
190
+ const db = await getDb();
191
+ if (extraMeta) {
192
+ const row = await db.select({ meta: systemEvents.meta }).from(systemEvents).where(eq(systemEvents.id, id)).get();
193
+ const merged = { ...parseMeta(row?.meta, `event ${id}`), ...extraMeta };
194
+ await db.update(systemEvents).set({ delivered_at: sql`(datetime('now'))`, meta: JSON.stringify(merged) }).where(eq(systemEvents.id, id));
195
+ } else {
196
+ await db.update(systemEvents).set({ delivered_at: sql`(datetime('now'))` }).where(eq(systemEvents.id, id));
197
+ }
198
+ } catch (err) {
199
+ elog.error(
200
+ `failed to stamp event ${id} delivered \u2014 it will be redelivered on the next wake/start`,
201
+ logger_default.errorData(err)
202
+ );
203
+ }
204
+ }
205
+ async function deliverEvent(mind, input) {
206
+ const thread = input.thread ?? "main";
207
+ const delivery = input.delivery ?? "immediate";
208
+ let eventId;
209
+ try {
210
+ const db = await getDb();
211
+ const rows = await db.insert(systemEvents).values({
212
+ mind,
213
+ type: input.type,
214
+ body: input.body,
215
+ meta: input.meta ? JSON.stringify(input.meta) : null,
216
+ delivery,
217
+ thread
218
+ }).returning({ id: systemEvents.id });
219
+ eventId = rows[0]?.id;
220
+ if (eventId == null) {
221
+ elog.error(`event insert for ${mind} (${input.type}) returned no id \u2014 event dropped`);
222
+ return { delivered: false };
223
+ }
224
+ if (delivery === "next-turn") {
225
+ await db.run(
226
+ sql`UPDATE system_events SET delivered_at = datetime('now'), meta = json_set(COALESCE(meta, '{}'), '$.dropped', 1) WHERE mind = ${mind} AND thread = ${thread} AND delivery = 'next-turn' AND delivered_at IS NULL AND id NOT IN (SELECT id FROM system_events WHERE mind = ${mind} AND thread = ${thread} AND delivery = 'next-turn' AND delivered_at IS NULL ORDER BY id DESC LIMIT ${MAX_NEXT_TURN_EVENTS})`
227
+ );
228
+ return { id: eventId, delivered: false };
229
+ }
230
+ const baseName = await getBaseName(mind);
231
+ const { getSleepManagerIfReady } = await import("./sleep-manager-LIEPO6WG.js");
232
+ const sleepManager = getSleepManagerIfReady();
233
+ const sleeping = !input.force && sleepManager?.isSleeping(baseName);
234
+ const behavior = input.whileSleeping ?? "queue";
235
+ if (sleeping && behavior === "skip") {
236
+ await markDelivered(eventId, { skipped: true });
237
+ return { id: eventId, delivered: false };
238
+ }
239
+ if (!sleeping) {
240
+ const event = await db.select().from(systemEvents).where(eq(systemEvents.id, eventId)).get();
241
+ if (event && await postEventEnvelope(mind, event)) {
242
+ await markDelivered(eventId);
243
+ await recordEventRow(mind, event);
244
+ return { id: eventId, delivered: true };
245
+ }
246
+ elog.warn(
247
+ `event ${eventId} (${input.type}) for ${mind} was not delivered; left pending for the next wake or mind start`
248
+ );
249
+ return { id: eventId, delivered: false };
250
+ }
251
+ if (behavior === "trigger-wake") {
252
+ sleepManager?.initiateWake(baseName, { trigger: { channel: `event:${input.type}` } }).catch(
253
+ (err) => elog.warn(`failed to trigger-wake ${baseName} for event`, logger_default.errorData(err))
254
+ );
255
+ }
256
+ return { id: eventId, delivered: false };
257
+ } catch (err) {
258
+ elog.error(`failed to deliver event to ${mind}`, logger_default.errorData(err));
259
+ return { id: eventId, delivered: false };
260
+ }
261
+ }
262
+ var NO_REPLAY_TYPES = /* @__PURE__ */ new Set(["sleep"]);
263
+ async function flushQueuedEvents(mind) {
264
+ try {
265
+ const db = await getDb();
266
+ const rows = await db.select().from(systemEvents).where(
267
+ and(
268
+ eq(systemEvents.mind, mind),
269
+ eq(systemEvents.delivery, "immediate"),
270
+ isNull(systemEvents.delivered_at)
271
+ )
272
+ ).orderBy(asc(systemEvents.id)).all();
273
+ let delivered = 0;
274
+ for (const event of rows) {
275
+ if (NO_REPLAY_TYPES.has(event.type) || eventAgeMs(event.created_at) > FLUSH_MAX_AGE_MS) {
276
+ elog.info(`expiring stale pending event ${event.id} (${event.type}) for ${mind}`);
277
+ await markDelivered(event.id, { expired: true });
278
+ continue;
279
+ }
280
+ if (await postEventEnvelope(mind, event)) {
281
+ await markDelivered(event.id);
282
+ await recordEventRow(mind, event);
283
+ delivered++;
284
+ } else {
285
+ elog.warn(`flush could not deliver event ${event.id} (${event.type}) to ${mind}`);
286
+ }
287
+ }
288
+ return delivered;
289
+ } catch (err) {
290
+ elog.warn(`failed to flush queued events for ${mind}`, logger_default.errorData(err));
291
+ return 0;
292
+ }
293
+ }
294
+ function eventAgeMs(createdAt) {
295
+ const iso = createdAt.endsWith("Z") ? createdAt : `${createdAt.replace(" ", "T")}Z`;
296
+ const t = new Date(iso).getTime();
297
+ return Number.isNaN(t) ? 0 : Date.now() - t;
298
+ }
299
+ async function pendingEventCount(mind) {
300
+ try {
301
+ const db = await getDb();
302
+ const row = await db.select({ n: sql`count(*)` }).from(systemEvents).where(
303
+ and(
304
+ eq(systemEvents.mind, mind),
305
+ eq(systemEvents.delivery, "immediate"),
306
+ isNull(systemEvents.delivered_at)
307
+ )
308
+ ).get();
309
+ return row?.n ?? 0;
310
+ } catch (err) {
311
+ elog.warn(`failed to count pending events for ${mind}`, logger_default.errorData(err));
312
+ return 0;
313
+ }
314
+ }
315
+ function pendingEventsLine(count) {
316
+ if (count <= 0) return "";
317
+ return `${count} system event${count === 1 ? "" : "s"} arrived while you slept and will follow shortly.`;
318
+ }
319
+ async function drainEvents(mind, thread, limit = MAX_NEXT_TURN_EVENTS) {
320
+ const db = await getDb();
321
+ return db.select().from(systemEvents).where(
322
+ and(
323
+ eq(systemEvents.mind, mind),
324
+ eq(systemEvents.delivery, "next-turn"),
325
+ isNull(systemEvents.delivered_at),
326
+ or(eq(systemEvents.thread, thread), eq(systemEvents.thread, MIND_LEVEL_THREAD))
327
+ )
328
+ ).orderBy(asc(systemEvents.id)).limit(limit);
329
+ }
330
+ function metaOf(e) {
331
+ return parseMeta(e.meta, `event ${e.id}`);
332
+ }
333
+ var FAILURE_SUBTYPES = /* @__PURE__ */ new Set(["turn_error", "crash", "startup"]);
334
+ var NOTICE_HEADER = "[Notices] While you were unavailable, one or more turns failed. You're back now:";
335
+ function formatEvents(events) {
336
+ if (events.length === 0) return null;
337
+ const failures = [];
338
+ const budgets = [];
339
+ const versions = [];
340
+ const extensions = [];
341
+ const others = [];
342
+ for (const e of events) {
343
+ if (e.type === "budget") budgets.push(e);
344
+ else if (e.type === "version") versions.push(e);
345
+ else if (e.type === "notice") {
346
+ const subtype = String(metaOf(e).subtype);
347
+ if (FAILURE_SUBTYPES.has(subtype)) failures.push(e);
348
+ else if (subtype === "extension") extensions.push(e);
349
+ else others.push(e);
350
+ } else others.push(e);
351
+ }
352
+ const blocks = [];
353
+ if (failures.length > 0) {
354
+ const groups = /* @__PURE__ */ new Map();
355
+ for (const e of failures) {
356
+ const reason = String(metaOf(e).reason ?? "unknown");
357
+ const time = localHM(e.created_at);
358
+ const g = groups.get(reason);
359
+ if (g) {
360
+ g.count += 1;
361
+ g.detail = e.body;
362
+ g.last = time;
363
+ } else {
364
+ groups.set(reason, { count: 1, detail: e.body, first: time, last: time });
365
+ }
366
+ }
367
+ const lines = [...groups.values()].map((g) => {
368
+ const span = g.first === g.last ? g.first : `${g.first}\u2013${g.last}`;
369
+ const plural = g.count === 1 ? "turn" : "turns";
370
+ return `- ${g.count} ${plural} failed (${span}): ${g.detail}`;
371
+ });
372
+ blocks.push(`${NOTICE_HEADER}
373
+ ${lines.join("\n")}`);
374
+ }
375
+ if (budgets.length > 0) {
376
+ const lines = budgets.map((e) => `- ${localHM(e.created_at)} ${e.body}`);
377
+ blocks.push(`[Budget]
378
+ ${lines.join("\n")}`);
379
+ }
380
+ if (versions.length > 0) {
381
+ const lines = versions.map((e) => `- ${localHM(e.created_at)} ${e.body}`);
382
+ blocks.push(`[Update]
383
+ ${lines.join("\n")}`);
384
+ }
385
+ if (extensions.length > 0) {
386
+ const byExt = /* @__PURE__ */ new Map();
387
+ for (const e of extensions) {
388
+ const ext = String(metaOf(e).reason ?? "extension");
389
+ const arr = byExt.get(ext);
390
+ if (arr) arr.push(e);
391
+ else byExt.set(ext, [e]);
392
+ }
393
+ for (const [ext, items] of byExt) {
394
+ const header = `[${ext.charAt(0).toUpperCase()}${ext.slice(1)}]`;
395
+ const lines = items.map((e) => `- ${localHM(e.created_at)} ${e.body}`);
396
+ blocks.push(`${header}
397
+ ${lines.join("\n")}`);
398
+ }
399
+ }
400
+ if (others.length > 0) {
401
+ const lines = others.map(
402
+ (e) => `- ${localHM(e.created_at)} (${eventLabel(e.type, metaOf(e))}) ${e.body}`
403
+ );
404
+ blocks.push(`[Events]
405
+ ${lines.join("\n")}`);
406
+ }
407
+ return blocks.join("\n\n");
408
+ }
409
+ function localHM(createdAt) {
410
+ const iso = createdAt.endsWith("Z") ? createdAt : `${createdAt.replace(" ", "T")}Z`;
411
+ return new Date(iso).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
412
+ }
413
+ async function clearDeliveredEvents(mind, thread, uptoId) {
414
+ try {
415
+ const db = await getDb();
416
+ await db.update(systemEvents).set({ delivered_at: sql`(datetime('now'))` }).where(
417
+ and(
418
+ eq(systemEvents.mind, mind),
419
+ eq(systemEvents.delivery, "next-turn"),
420
+ isNull(systemEvents.delivered_at),
421
+ or(eq(systemEvents.thread, thread), eq(systemEvents.thread, MIND_LEVEL_THREAD)),
422
+ lte(systemEvents.id, uptoId)
423
+ )
424
+ );
425
+ } catch (err) {
426
+ elog.warn(`failed to clear delivered events for ${mind}:${thread}`, logger_default.errorData(err));
427
+ }
428
+ }
429
+ var failureReadErrorLogged = false;
430
+ async function latestFailureEvent(mind) {
431
+ try {
432
+ const db = await getDb();
433
+ const e = await db.select().from(systemEvents).where(
434
+ and(
435
+ eq(systemEvents.mind, mind),
436
+ eq(systemEvents.type, "notice"),
437
+ isNull(systemEvents.delivered_at),
438
+ // Closed set of literal subtypes (not user input).
439
+ sql`json_extract(${systemEvents.meta}, '$.subtype') IN ('turn_error', 'crash', 'startup')`
440
+ )
441
+ ).orderBy(desc(systemEvents.id)).limit(1).get();
442
+ if (!e) return null;
443
+ const recovered = await db.select({ id: turns.id }).from(turns).where(
444
+ and(eq(turns.mind, mind), eq(turns.status, "complete"), gt(turns.created_at, e.created_at))
445
+ ).limit(1);
446
+ if (recovered.length > 0) return null;
447
+ const m = metaOf(e);
448
+ return {
449
+ kind: String(m.subtype ?? "notice"),
450
+ reason: String(m.reason ?? "unknown"),
451
+ detail: e.body,
452
+ at: e.created_at
453
+ };
454
+ } catch (err) {
455
+ if (!failureReadErrorLogged) {
456
+ failureReadErrorLogged = true;
457
+ elog.error(`failed to read latest failure event for ${mind}`, logger_default.errorData(err));
458
+ }
459
+ return null;
460
+ }
461
+ }
462
+ async function latestEvent(mind) {
463
+ const db = await getDb();
464
+ const row = await db.select().from(systemEvents).where(
465
+ and(
466
+ eq(systemEvents.mind, mind),
467
+ eq(systemEvents.delivery, "next-turn"),
468
+ isNull(systemEvents.delivered_at)
469
+ )
470
+ ).orderBy(desc(systemEvents.id)).limit(1).get();
471
+ if (!row) return null;
472
+ const m = metaOf(row);
473
+ return {
474
+ kind: String(m.subtype ?? row.type),
475
+ reason: String(m.reason ?? row.type),
476
+ detail: row.body,
477
+ created_at: row.created_at
478
+ };
479
+ }
480
+ async function hasUndeliveredEvent(mind, reason) {
481
+ const db = await getDb();
482
+ const row = await db.select({ id: systemEvents.id }).from(systemEvents).where(
483
+ and(
484
+ eq(systemEvents.mind, mind),
485
+ eq(systemEvents.delivery, "next-turn"),
486
+ isNull(systemEvents.delivered_at),
487
+ sql`json_extract(${systemEvents.meta}, '$.reason') = ${reason}`
488
+ )
489
+ ).limit(1).get();
490
+ return row != null;
491
+ }
492
+ async function listEvents(mind, limit = 100, before) {
493
+ const db = await getDb();
494
+ const conditions = [eq(systemEvents.mind, mind)];
495
+ if (before != null) conditions.push(sql`${systemEvents.id} < ${before}`);
496
+ return db.select().from(systemEvents).where(and(...conditions)).orderBy(desc(systemEvents.id)).limit(limit);
497
+ }
498
+ async function recordReflection(mind, eventId, text) {
499
+ try {
500
+ const db = await getDb();
501
+ await db.update(systemEvents).set({ reflection: text }).where(and(eq(systemEvents.id, eventId), eq(systemEvents.mind, mind)));
502
+ } catch (err) {
503
+ elog.warn(`failed to record reflection for event ${eventId}`, logger_default.errorData(err));
504
+ }
505
+ }
506
+ async function captureReflection(mind, turnId) {
507
+ if (!turnId) return;
508
+ try {
509
+ const db = await getDb();
510
+ const turn = await db.select({ trigger: turns.trigger_event_id }).from(turns).where(eq(turns.id, turnId)).get();
511
+ if (turn?.trigger == null) return;
512
+ const trigger = await db.select({ metadata: mindHistory.metadata }).from(mindHistory).where(eq(mindHistory.id, turn.trigger)).get();
513
+ const eventId = trigger?.metadata ? parseMeta(trigger.metadata, `event row ${turn.trigger}`).systemEventId : void 0;
514
+ if (typeof eventId !== "number") return;
515
+ const row = await db.select({ content: mindHistory.content }).from(mindHistory).where(
516
+ and(
517
+ eq(mindHistory.mind, mind),
518
+ eq(mindHistory.turn_id, turnId),
519
+ eq(mindHistory.type, "text")
520
+ )
521
+ ).orderBy(desc(mindHistory.id)).limit(1).get();
522
+ const text = row?.content?.trim();
523
+ if (text) await recordReflection(mind, eventId, text);
524
+ } catch (err) {
525
+ elog.warn(`failed to capture reflection for turn ${turnId}`, logger_default.errorData(err));
526
+ }
527
+ }
528
+
529
+ export {
530
+ MIND_LEVEL_THREAD,
531
+ parseMeta,
532
+ eventLabel,
533
+ recordNotice,
534
+ eventChannel,
535
+ deliverEvent,
536
+ flushQueuedEvents,
537
+ pendingEventCount,
538
+ pendingEventsLine,
539
+ drainEvents,
540
+ formatEvents,
541
+ clearDeliveredEvents,
542
+ latestFailureEvent,
543
+ latestEvent,
544
+ hasUndeliveredEvent,
545
+ listEvents,
546
+ recordReflection,
547
+ captureReflection
548
+ };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  stateDir,
4
4
  voluteSystemDir
5
- } from "./chunk-RSBL3FAD.js";
5
+ } from "./chunk-CAIYV33P.js";
6
6
 
7
7
  // packages/daemon/src/lib/config/env.ts
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-QUSMYGNQ.js";
5
5
  import {
6
6
  voluteSystemDir
7
- } from "./chunk-RSBL3FAD.js";
7
+ } from "./chunk-CAIYV33P.js";
8
8
 
9
9
  // packages/daemon/src/lib/bridges/bridges.ts
10
10
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteSystemDir
4
- } from "./chunk-RSBL3FAD.js";
4
+ } from "./chunk-CAIYV33P.js";
5
5
 
6
6
  // packages/daemon/src/lib/update-check.ts
7
7
  import { execFile as execFileCb, execFileSync } from "child_process";
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getBaseName,
4
+ isSpiritName,
4
5
  validateMindName
5
- } from "./chunk-RSBL3FAD.js";
6
+ } from "./chunk-CAIYV33P.js";
6
7
 
7
8
  // packages/daemon/src/lib/mind/isolation.ts
8
9
  import { execFileSync as execFileSync2 } from "child_process";
@@ -63,7 +64,7 @@ function isIsolationEnabled() {
63
64
  }
64
65
  function mindUserName(mindName) {
65
66
  const err = validateMindName(mindName);
66
- if (err && mindName !== "volute") {
67
+ if (err && !isSpiritName(mindName)) {
67
68
  throw new Error(`Invalid mind name for isolation: ${err}`);
68
69
  }
69
70
  const prefix = process.env.VOLUTE_USER_PREFIX ?? "mind-";
@@ -255,6 +256,19 @@ async function chownMindDir(dir, name) {
255
256
  throw new Error(`Failed to chmod ${dir}${stderr ? `: ${stderr}` : ""}`);
256
257
  }
257
258
  }
259
+ async function chownMindFile(filePath, name) {
260
+ if (!isIsolationEnabled()) return;
261
+ const user = mindUserName(name);
262
+ const group = process.platform === "darwin" ? "volute" : user;
263
+ try {
264
+ await exec("chown", [`${user}:${group}`, filePath]);
265
+ } catch (err) {
266
+ const stderr = String(err?.stderr ?? "").trim();
267
+ throw new Error(
268
+ `Failed to chown ${filePath} to ${user}:${group}${stderr ? `: ${stderr}` : ""}`
269
+ );
270
+ }
271
+ }
258
272
 
259
273
  export {
260
274
  isIsolationEnabled,
@@ -265,6 +279,7 @@ export {
265
279
  wrapForIsolation,
266
280
  chownTargets,
267
281
  chownMindDir,
282
+ chownMindFile,
268
283
  exec,
269
284
  gitExec,
270
285
  resolveVoluteBin,