volute 0.46.0 → 0.48.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 (172) hide show
  1. package/README.md +10 -10
  2. package/dist/{accept-N5XULCXK.js → accept-KYNC5X3U.js} +1 -1
  3. package/dist/{activity-events-NF6VWTJQ.js → activity-events-MIOK3IOU.js} +3 -3
  4. package/dist/{ai-service-UODCCE4O.js → ai-service-643TNFPH.js} +8 -4
  5. package/dist/{archive-WS2L2VIQ.js → archive-6Z4GFQPH.js} +3 -3
  6. package/dist/{auth-CCFAU4GQ.js → auth-A4VGHADI.js} +4 -4
  7. package/dist/avatar-image-Y45R7LW5.js +23 -0
  8. package/dist/{backup-TST5C2YN.js → backup-UDH4GMSW.js} +15 -15
  9. package/dist/{bridge-2PO3AI5D.js → bridge-3FNSWLNQ.js} +1 -1
  10. package/dist/channels-SGQZHNQJ.js +89 -0
  11. package/dist/{chat-ZIQQS25U.js → chat-X43VYHH2.js} +12 -8
  12. package/dist/{chunk-ZV7MFISX.js → chunk-2MJ7EOMT.js} +20 -2
  13. package/dist/{chunk-R77O2WMK.js → chunk-2OIYNHWO.js} +1 -1
  14. package/dist/{chunk-QCN24XEN.js → chunk-3YUBFTDN.js} +1 -1
  15. package/dist/chunk-4FCL4DVZ.js +48 -0
  16. package/dist/{chunk-F7H6ATM4.js → chunk-5KXCM5AW.js} +18 -2
  17. package/dist/chunk-7KRIHYLR.js +124 -0
  18. package/dist/{chunk-RBNMDAKB.js → chunk-BCG45AQB.js} +15 -1
  19. package/dist/{chunk-QMJCNZ6C.js → chunk-C3KLHJDB.js} +1 -1
  20. package/dist/{chunk-H2X54EHC.js → chunk-DGGZVZTM.js} +1 -1
  21. package/dist/chunk-DW4TRDOZ.js +29 -0
  22. package/dist/{chunk-4JOQZPCE.js → chunk-EIQDZC43.js} +1 -1
  23. package/dist/{chunk-TQJ6ZYVY.js → chunk-FJ2MZ5PH.js} +41 -5
  24. package/dist/{chunk-BJZ4PV7R.js → chunk-HNGFKPNS.js} +26 -10
  25. package/dist/{chunk-5ZQZDDIK.js → chunk-HPGN6XRT.js} +3 -3
  26. package/dist/{chunk-KGIXX4NM.js → chunk-HUNDXZBZ.js} +5 -5
  27. package/dist/{chunk-ZDZISKCL.js → chunk-IJPMIEVE.js} +1 -1
  28. package/dist/chunk-IYCV34IU.js +158 -0
  29. package/dist/{chunk-ZNMCCHCP.js → chunk-LTLFOSA4.js} +2 -2
  30. package/dist/{chunk-K6T43YBD.js → chunk-MJRXFUMS.js} +26 -6
  31. package/dist/{chunk-4YU7HF4L.js → chunk-MOERAQXH.js} +6 -4
  32. package/dist/{chunk-KQSFTPIM.js → chunk-MTRMQFCQ.js} +1 -1
  33. package/dist/{chunk-OV7ZQ2GH.js → chunk-ODDGGLM6.js} +3 -3
  34. package/dist/{chunk-A7WQZKOU.js → chunk-OQJMTHEH.js} +2 -2
  35. package/dist/{chunk-RRNXJOKB.js → chunk-PJVPDY2Z.js} +2 -2
  36. package/dist/{chunk-FXORA5JG.js → chunk-Q7HOAVFR.js} +3 -3
  37. package/dist/{chunk-KDLYIECM.js → chunk-R772ET2M.js} +18 -4
  38. package/dist/{chunk-QLWG3C2Z.js → chunk-RRZ4RQQU.js} +2 -2
  39. package/dist/{mind-list-4D7J4CHD.js → chunk-SHKM5RGD.js} +18 -4
  40. package/dist/{chunk-RXEEYDIB.js → chunk-SZJCJVXV.js} +1 -1
  41. package/dist/{chunk-E3DA4HYV.js → chunk-T4VS7UDW.js} +15 -15
  42. package/dist/{chunk-YXSUMLTC.js → chunk-USD45JKR.js} +322 -76
  43. package/dist/{chunk-JO5IO4FH.js → chunk-VB2QXUJQ.js} +391 -294
  44. package/dist/{chunk-JZ62SYWR.js → chunk-VZRA5TYO.js} +1 -1
  45. package/dist/{chunk-PJ45FQPT.js → chunk-WARH6GIV.js} +8 -8
  46. package/dist/{chunk-PFZBNKUH.js → chunk-WCFSBH33.js} +0 -1
  47. package/dist/{chunk-DRJ24GXK.js → chunk-WQ46E33M.js} +59 -39
  48. package/dist/{chunk-ALKYQULU.js → chunk-WW6C3PLV.js} +1 -1
  49. package/dist/{chunk-CDPBTTZO.js → chunk-XVBSFLM7.js} +2 -2
  50. package/dist/{chunk-6EMWJZBK.js → chunk-YKA7DCVW.js} +1 -1
  51. package/dist/{chunk-IHKGWTGT.js → chunk-YNBXEQAP.js} +31 -1
  52. package/dist/cli.js +34 -26
  53. package/dist/{clock-RHS2JEDL.js → clock-CNRF6F7T.js} +3 -3
  54. package/dist/{cloud-sync-46RS4SDA.js → cloud-sync-WHKCB4CD.js} +22 -19
  55. package/dist/{config-TJLYUV3U.js → config-RPSLA2HX.js} +3 -3
  56. package/dist/{conversations-CJOOCGNW.js → conversations-C7IKYSHD.js} +4 -4
  57. package/dist/{create-IOV4GCRX.js → create-GJKHABJB.js} +16 -5
  58. package/dist/{create-DF3KISTW.js → create-VIQVP3QB.js} +1 -1
  59. package/dist/{daemon-client-IYR4KCPU.js → daemon-client-ZFY5MOAL.js} +5 -3
  60. package/dist/{daemon-restart-VFEI57UO.js → daemon-restart-7KUHVZIN.js} +7 -7
  61. package/dist/daemon.js +829 -496
  62. package/dist/{db-MF2JNRUW.js → db-FO6LXYCW.js} +2 -2
  63. package/dist/{db-LZTLA7XC.js → db-IPXRJUI7.js} +1 -1
  64. package/dist/{delete-HPLC7ETU.js → delete-ESEP7NGQ.js} +1 -1
  65. package/dist/delivery-manager-HZTT5SJR.js +36 -0
  66. package/dist/{delivery-router-N4A7ZC5T.js → delivery-router-3QFG2ZEX.js} +3 -3
  67. package/dist/{down-U2CZ23M4.js → down-G6IADJXV.js} +5 -5
  68. package/dist/echo-text-H3SH337Q.js +36 -0
  69. package/dist/env-6TGPO5WG.js +18 -0
  70. package/dist/{env-VZ4MLFQW.js → env-C7Z55QCJ.js} +6 -6
  71. package/dist/{exec-G3ZRST4Z.js → exec-QIFYZG4C.js} +3 -3
  72. package/dist/{export-LY723I3Z.js → export-C2K3NIRD.js} +4 -4
  73. package/dist/{extension-SEID47SR.js → extension-J4ZQ2GHV.js} +1 -1
  74. package/dist/{extensions-ZELQLTSR.js → extensions-GEKF47A4.js} +24 -19
  75. package/dist/{files-4LN3EN7Q.js → files-LXQSQOFG.js} +4 -4
  76. package/dist/{import-42YNQBF4.js → import-SX3ZXHB4.js} +8 -8
  77. package/dist/{isolation-YMGZZ4E5.js → isolation-MCT4ZYLT.js} +3 -3
  78. package/dist/{join-ZJV7W6MO.js → join-34TR2GXQ.js} +1 -1
  79. package/dist/{list-OXIAY3W7.js → list-PMUXZNAM.js} +1 -1
  80. package/dist/{login-MY6ETFYB.js → login-ARRZ4ORJ.js} +3 -3
  81. package/dist/{login-M6OJUPQ2.js → login-CL4IT2ED.js} +14 -5
  82. package/dist/{logout-WQBWO77G.js → logout-HZYJCYCA.js} +3 -3
  83. package/dist/{logout-NCG3TMDL.js → logout-YZRMZ3O2.js} +1 -1
  84. package/dist/message-delivery-GPQ5SE5N.js +40 -0
  85. package/dist/{mind-LHKS55DI.js → mind-46AOK373.js} +23 -19
  86. package/dist/{mind-activity-tracker-3QRNJVR4.js → mind-activity-tracker-YVZPC4A5.js} +4 -4
  87. package/dist/mind-contacts-GV7XYL5D.js +64 -0
  88. package/dist/{mind-history-VRO5A76U.js → mind-history-TTGA5BHZ.js} +5 -5
  89. package/dist/mind-list-OTHV4ZO4.js +13 -0
  90. package/dist/{mind-profile-PEQYASQX.js → mind-profile-LUG353ZT.js} +1 -1
  91. package/dist/mind-service-2ARGJNKP.js +44 -0
  92. package/dist/{mind-sleep-74TGOQBC.js → mind-sleep-DV7KP255.js} +3 -3
  93. package/dist/{mind-status-UO34ASXZ.js → mind-status-BJNPGXKC.js} +23 -1
  94. package/dist/{mind-wake-LCLGTXA2.js → mind-wake-LVTZ55EZ.js} +1 -1
  95. package/dist/notices-2KU5NTRY.js +25 -0
  96. package/dist/{package-DOD6CPO6.js → package-RTN2PS7O.js} +5 -3
  97. package/dist/{read-QVAXMTPV.js → read-YSWUXYL7.js} +1 -1
  98. package/dist/{register-Y2H4PY5R.js → register-42VBJKTP.js} +3 -3
  99. package/dist/{registry-OUZBZ3Q4.js → registry-UMMDRPRM.js} +2 -2
  100. package/dist/{reject-EV35X64Y.js → reject-SXGCYN4A.js} +1 -1
  101. package/dist/{restart-FJQ662XJ.js → restart-YYEGZJDD.js} +5 -5
  102. package/dist/{sandbox-ZJ7QA6Z6.js → sandbox-5VQ454XP.js} +4 -4
  103. package/dist/scheduler-I7JZCT47.js +34 -0
  104. package/dist/{schema-PVNG3PQM.js → schema-VEVO7HG6.js} +3 -1
  105. package/dist/{seed-CNYIHPOH.js → seed-F2HXZQU4.js} +1 -1
  106. package/dist/{seed-check-VLTVILOV.js → seed-check-TYD7RRZY.js} +1 -1
  107. package/dist/{seed-cmd-AVNUNLZV.js → seed-cmd-MLCTGSX5.js} +3 -3
  108. package/dist/{seed-create-ANXGYSCC.js → seed-create-KQ7OILWB.js} +6 -4
  109. package/dist/seed-readiness-RBPGOLSZ.js +18 -0
  110. package/dist/{seed-sprout-NIUFTX32.js → seed-sprout-HVYCFJLA.js} +30 -31
  111. package/dist/{send-SPE6DVNJ.js → send-TD2X5U3K.js} +6 -6
  112. package/dist/{service-UOCLXDWR.js → service-CVZMQYK6.js} +4 -4
  113. package/dist/{service-install-4HHFDDWY.js → service-install-VCRMFWX5.js} +5 -5
  114. package/dist/{setup-Y2EJI3GG.js → setup-5ECWTYKG.js} +7 -3
  115. package/dist/{setup-QT2A25SY.js → setup-AE4CVUC4.js} +7 -7
  116. package/dist/{skill-FKFHIJRR.js → skill-UUM4JZRF.js} +5 -5
  117. package/dist/skills/dreaming/SKILL.md +5 -1
  118. package/dist/skills/dreaming/scripts/dream.ts +5 -1
  119. package/dist/skills/orientation/SKILL.md +8 -7
  120. package/dist/skills/tending/SKILL.md +51 -2
  121. package/dist/skills/volute-mind/SKILL.md +2 -0
  122. package/dist/{skills-AKCFA7BN.js → skills-B3SVDCEB.js} +5 -5
  123. package/dist/sleep-manager-Z27R3WTW.js +38 -0
  124. package/dist/spirit-WXS3HCLN.js +25 -0
  125. package/dist/{split-WBMZXBHF.js → split-SKZ4Y2D6.js} +1 -1
  126. package/dist/{sprout-SMX42ZBP.js → sprout-KQ43BWZJ.js} +1 -1
  127. package/dist/{src-OJHARHTF.js → src-WLJE73NP.js} +14 -5
  128. package/dist/{start-5JE3TV66.js → start-O6UKK7BG.js} +3 -3
  129. package/dist/{status-SWRVGXBI.js → status-F4PGREUU.js} +19 -7
  130. package/dist/{stop-AAMTG3TU.js → stop-UBX6X5RA.js} +5 -5
  131. package/dist/system-chat-63ABFF6V.js +40 -0
  132. package/dist/{systems-VNB5WKSS.js → systems-JJNGQHAO.js} +4 -4
  133. package/dist/{tailscale-3XOKOW7T.js → tailscale-MCLY5GYE.js} +2 -2
  134. package/dist/{template-hash-446HHH5V.js → template-hash-GSOUARW4.js} +2 -2
  135. package/dist/template-staleness-EMQORSGG.js +15 -0
  136. package/dist/{turn-tracker-FOIBNUUA.js → turn-tracker-KLPV3PAU.js} +6 -6
  137. package/dist/up-RVGDE6CQ.js +20 -0
  138. package/dist/{update-SWAYZRWJ.js → update-OXQEGGXD.js} +5 -5
  139. package/dist/{update-check-PQ2MZ47K.js → update-check-266R4ZSY.js} +3 -3
  140. package/dist/{upgrade-NWAZ7MLD.js → upgrade-K5FIQPQD.js} +1 -1
  141. package/dist/{version-notify-C5CH7CVP.js → version-notify-73YURTFR.js} +41 -25
  142. package/dist/web-assets/assets/index-WdZ60R6c.css +1 -0
  143. package/dist/web-assets/assets/index-taX6Ezfa.js +92 -0
  144. package/dist/web-assets/index.html +2 -2
  145. package/drizzle/0011_drop_turn_summary_event_id.sql +3 -0
  146. package/drizzle/0012_channel_gates.sql +17 -0
  147. package/drizzle/meta/_journal.json +14 -0
  148. package/package.json +5 -3
  149. package/packages/extensions/pages/dist/ui/assets/index-BuXnE-rb.css +1 -0
  150. package/packages/extensions/pages/dist/ui/assets/index-DtzftuPA.js +2 -0
  151. package/packages/extensions/pages/dist/ui/index.html +2 -2
  152. package/templates/_base/src/lib/startup.ts +59 -3
  153. package/templates/claude/src/agent.ts +37 -4
  154. package/templates/claude/src/lib/session-reaper.ts +32 -0
  155. package/templates/claude/src/server.ts +14 -3
  156. package/templates/pi/src/agent.ts +7 -10
  157. package/templates/pi/src/lib/dispatch.ts +65 -0
  158. package/dist/avatar-image-6KY6UU4D.js +0 -16
  159. package/dist/delivery-manager-JINZRG2W.js +0 -33
  160. package/dist/echo-text-VQPB5L2X.js +0 -33
  161. package/dist/message-delivery-R7JSQGYX.js +0 -37
  162. package/dist/mind-service-NB2JMWQZ.js +0 -39
  163. package/dist/scheduler-WVKII3LA.js +0 -31
  164. package/dist/sleep-manager-G6TXSDP7.js +0 -35
  165. package/dist/spirit-RPBLLUQ5.js +0 -23
  166. package/dist/system-chat-NDAW6YP7.js +0 -37
  167. package/dist/up-PS6PFS75.js +0 -18
  168. package/dist/volute-config-A52FHWKM.js +0 -14
  169. package/dist/web-assets/assets/index-B1JBQv2m.css +0 -1
  170. package/dist/web-assets/assets/index-CU6XWICM.js +0 -86
  171. package/packages/extensions/pages/dist/ui/assets/index-BcY6lnq4.css +0 -1
  172. package/packages/extensions/pages/dist/ui/assets/index-CQjEQg1r.js +0 -2
package/dist/daemon.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  readBackupState,
7
7
  resticVersion,
8
8
  runBackup
9
- } from "./chunk-FXORA5JG.js";
9
+ } from "./chunk-Q7HOAVFR.js";
10
10
  import {
11
11
  acceptPending,
12
12
  formatFileSize,
@@ -14,35 +14,34 @@ import {
14
14
  rejectPending,
15
15
  stageFile,
16
16
  validateFilePath
17
- } from "./chunk-R77O2WMK.js";
17
+ } from "./chunk-2OIYNHWO.js";
18
18
  import {
19
19
  findOpenClawSession,
20
20
  importOpenClawConnectors,
21
21
  importPiSession,
22
22
  parseNameFromIdentity
23
- } from "./chunk-A7WQZKOU.js";
23
+ } from "./chunk-OQJMTHEH.js";
24
+ import {
25
+ isTemplateStale
26
+ } from "./chunk-7KRIHYLR.js";
27
+ import {
28
+ computeTemplateHash
29
+ } from "./chunk-C3KLHJDB.js";
24
30
  import {
25
31
  checkForUpdate,
26
32
  checkForUpdateCached,
27
33
  getCurrentVersion
28
- } from "./chunk-6EMWJZBK.js";
29
- import {
30
- normalizeAvatar,
31
- safeResolveWithinBase
32
- } from "./chunk-DRJ24GXK.js";
34
+ } from "./chunk-YKA7DCVW.js";
33
35
  import {
34
36
  isHomeOnlyArchive
35
- } from "./chunk-QCN24XEN.js";
36
- import {
37
- computeTemplateHash
38
- } from "./chunk-QMJCNZ6C.js";
37
+ } from "./chunk-3YUBFTDN.js";
39
38
  import {
40
39
  PLATFORMS,
41
40
  echoTextToChannel,
42
41
  fixModelEscapes,
43
42
  getPlatformDriver,
44
43
  routeOutboundBridge
45
- } from "./chunk-E3DA4HYV.js";
44
+ } from "./chunk-T4VS7UDW.js";
46
45
  import {
47
46
  getBridgeConfig,
48
47
  readBridgesConfig,
@@ -51,7 +50,7 @@ import {
51
50
  resolveChannelMapping,
52
51
  setBridgeConfig,
53
52
  setChannelMapping
54
- } from "./chunk-RXEEYDIB.js";
53
+ } from "./chunk-SZJCJVXV.js";
55
54
  import {
56
55
  MindStartupError,
57
56
  RestartTracker,
@@ -59,18 +58,16 @@ import {
59
58
  SESSION_MAX_AGE,
60
59
  announceToSystem,
61
60
  authMiddleware,
61
+ backfillSystemChannelMembers,
62
62
  checkHealth,
63
63
  checkStaleSend,
64
64
  cleanExpiredSessions,
65
- clearDeliveredNotices,
66
65
  createSession,
67
66
  deleteSession,
68
67
  deleteSystemsConfig,
69
68
  deliverMessage,
70
- drainNotices,
71
69
  ensureSystemChannel,
72
70
  formatHoldNotice,
73
- formatNotices,
74
71
  generateSystemFallbackReply,
75
72
  getAllDiscoveredExtensions,
76
73
  getAllDiscoveredExtensionsDetailed,
@@ -101,7 +98,6 @@ import {
101
98
  publishTypingForChannels,
102
99
  readSystemsConfig,
103
100
  recordInbound,
104
- recordNotice,
105
101
  recordOutbound,
106
102
  requireAdmin,
107
103
  requireAdminOrSystem,
@@ -113,15 +109,8 @@ import {
113
109
  syncProviderToMinds,
114
110
  uninstallNpmExtension,
115
111
  writeSystemsConfig
116
- } from "./chunk-JO5IO4FH.js";
117
- import {
118
- loadMergedEnv,
119
- mindEnvPath,
120
- readEnv,
121
- sharedEnvPath,
122
- writeEnv
123
- } from "./chunk-ZDZISKCL.js";
124
- import "./chunk-BJZ4PV7R.js";
112
+ } from "./chunk-VB2QXUJQ.js";
113
+ import "./chunk-HNGFKPNS.js";
125
114
  import {
126
115
  applyInitFiles,
127
116
  applyTemplateHomeFiles,
@@ -130,7 +119,34 @@ import {
130
119
  findTemplatesRoot,
131
120
  isKnownTemplate,
132
121
  listFiles
133
- } from "./chunk-QLWG3C2Z.js";
122
+ } from "./chunk-RRZ4RQQU.js";
123
+ import {
124
+ assignSession,
125
+ completeOrphanedTurns,
126
+ completeTurn,
127
+ createTurn,
128
+ getActiveTurnId,
129
+ getToolUseEventId,
130
+ initSummarizer,
131
+ isoWeekKeyForDateStr,
132
+ markErrored,
133
+ publish as publish3,
134
+ subscribe as subscribe3,
135
+ subscribeAll,
136
+ summarizeOrphanedTurns,
137
+ summarizeTurn,
138
+ takeErrored,
139
+ trackToolUse,
140
+ utcDateTimeStr
141
+ } from "./chunk-USD45JKR.js";
142
+ import {
143
+ PROMPT_DEFAULTS,
144
+ PROMPT_KEYS,
145
+ getMindPromptDefaults,
146
+ getPrompt,
147
+ getPromptIfCustom,
148
+ substitute
149
+ } from "./chunk-MOERAQXH.js";
134
150
  import {
135
151
  SEED_SKILLS,
136
152
  STANDARD_SKILLS,
@@ -144,17 +160,38 @@ import {
144
160
  listFilesRecursive,
145
161
  listMindSkills,
146
162
  listSharedSkills,
163
+ mindSkillsDir,
147
164
  publishSkill,
148
165
  removeSharedSkill,
149
166
  sharedSkillsDir,
150
167
  syncBuiltinSkills,
151
168
  uninstallSkill,
152
169
  updateSkill
153
- } from "./chunk-KGIXX4NM.js";
170
+ } from "./chunk-HUNDXZBZ.js";
171
+ import {
172
+ approveUser,
173
+ changePassword,
174
+ countAdmins,
175
+ createUser,
176
+ deleteMindUser as deleteMindUser2,
177
+ deleteUser,
178
+ getOrCreateMindUser,
179
+ getOrCreateSystemUser,
180
+ getUser,
181
+ getUserByUsername,
182
+ listPendingUsers,
183
+ listUsers,
184
+ listUsersByType,
185
+ setUserRole,
186
+ syncMindProfile,
187
+ updateUserProfile,
188
+ verifyUser
189
+ } from "./chunk-ODDGGLM6.js";
154
190
  import {
155
191
  addMessage,
156
192
  createChannel,
157
193
  createConversation,
194
+ deleteConversation,
158
195
  deleteConversationForUser,
159
196
  findDMConversation,
160
197
  fireWebhook,
@@ -182,50 +219,38 @@ import {
182
219
  setConversationPrivate,
183
220
  subscribe as subscribe2,
184
221
  updateChannelSettings
185
- } from "./chunk-5ZQZDDIK.js";
186
- import {
187
- readVoluteConfig,
188
- writeVoluteConfig
189
- } from "./chunk-PFZBNKUH.js";
222
+ } from "./chunk-HPGN6XRT.js";
190
223
  import {
191
224
  isSandboxEnabled,
192
225
  wrapForSandbox
193
- } from "./chunk-JZ62SYWR.js";
226
+ } from "./chunk-VZRA5TYO.js";
227
+ import {
228
+ clearDeliveredNotices,
229
+ drainNotices,
230
+ formatNotices,
231
+ latestFailureNotice,
232
+ latestNotice,
233
+ recordNotice
234
+ } from "./chunk-IYCV34IU.js";
194
235
  import {
195
236
  getActiveMinds,
196
237
  onMindEvent,
197
238
  stopAll
198
- } from "./chunk-KQSFTPIM.js";
239
+ } from "./chunk-MTRMQFCQ.js";
199
240
  import {
200
- extractTextContent
201
- } from "./chunk-4JOQZPCE.js";
241
+ normalizeAvatar
242
+ } from "./chunk-WQ46E33M.js";
202
243
  import {
203
- assignSession,
204
- completeOrphanedTurns,
205
- completeTurn,
206
- createTurn,
207
- getActiveTurnId,
208
- getToolUseEventId,
209
- initSummarizer,
210
- markErrored,
211
- publish as publish3,
212
- subscribe as subscribe3,
213
- subscribeAll,
214
- summarizeOrphanedTurns,
215
- summarizeTurn,
216
- takeErrored,
217
- trackToolUse
218
- } from "./chunk-YXSUMLTC.js";
244
+ safeResolveWithinBase
245
+ } from "./chunk-DW4TRDOZ.js";
219
246
  import {
220
- ORIENTATION_MARKER,
221
- PROMPT_DEFAULTS,
222
- PROMPT_KEYS,
223
- getMindPromptDefaults,
224
- getPrompt,
225
- getPromptIfCustom,
226
- substitute
227
- } from "./chunk-4YU7HF4L.js";
228
- import "./chunk-TQJ6ZYVY.js";
247
+ readVoluteConfig,
248
+ writeVoluteConfig
249
+ } from "./chunk-WCFSBH33.js";
250
+ import {
251
+ extractTextContent
252
+ } from "./chunk-EIQDZC43.js";
253
+ import "./chunk-FJ2MZ5PH.js";
229
254
  import {
230
255
  chownMindDir,
231
256
  createMindUser,
@@ -236,7 +261,14 @@ import {
236
261
  isIsolationEnabled,
237
262
  resolveVoluteBin,
238
263
  wrapForIsolation
239
- } from "./chunk-H2X54EHC.js";
264
+ } from "./chunk-DGGZVZTM.js";
265
+ import {
266
+ loadMergedEnv,
267
+ mindEnvPath,
268
+ readEnv,
269
+ sharedEnvPath,
270
+ writeEnv
271
+ } from "./chunk-IJPMIEVE.js";
240
272
  import {
241
273
  addCustomModel,
242
274
  aiCompleteUtility,
@@ -247,6 +279,7 @@ import {
247
279
  getCustomModels,
248
280
  getEnabledModels,
249
281
  getUtilityModel,
282
+ missingCredentialWarning,
250
283
  qualifyModelId,
251
284
  removeAiConfig,
252
285
  removeCustomModel,
@@ -258,38 +291,19 @@ import {
258
291
  setProviderRefreshHook,
259
292
  setUtilityModel,
260
293
  unqualifyModelId
261
- } from "./chunk-IHKGWTGT.js";
294
+ } from "./chunk-YNBXEQAP.js";
262
295
  import {
263
296
  isSetupComplete,
264
297
  readGlobalConfig,
265
298
  writeGlobalConfig
266
- } from "./chunk-F7H6ATM4.js";
267
- import {
268
- approveUser,
269
- changePassword,
270
- countAdmins,
271
- createUser,
272
- deleteMindUser as deleteMindUser2,
273
- deleteUser,
274
- getOrCreateMindUser,
275
- getOrCreateSystemUser,
276
- getUser,
277
- getUserByUsername,
278
- listPendingUsers,
279
- listUsers,
280
- listUsersByType,
281
- setUserRole,
282
- syncMindProfile,
283
- updateUserProfile,
284
- verifyUser
285
- } from "./chunk-OV7ZQ2GH.js";
299
+ } from "./chunk-5KXCM5AW.js";
286
300
  import {
287
301
  broadcast,
288
302
  getEventsSince,
289
303
  nextEventId,
290
304
  publish,
291
305
  subscribe
292
- } from "./chunk-ZNMCCHCP.js";
306
+ } from "./chunk-LTLFOSA4.js";
293
307
  import {
294
308
  logBuffer,
295
309
  logger_default
@@ -323,7 +337,7 @@ import {
323
337
  validateMindName,
324
338
  voluteHome,
325
339
  voluteSystemDir
326
- } from "./chunk-ALKYQULU.js";
340
+ } from "./chunk-WW6C3PLV.js";
327
341
  import {
328
342
  activity,
329
343
  channels,
@@ -334,14 +348,14 @@ import {
334
348
  systemPrompts,
335
349
  turns,
336
350
  users
337
- } from "./chunk-ZV7MFISX.js";
351
+ } from "./chunk-2MJ7EOMT.js";
338
352
  import "./chunk-K3NQKI34.js";
339
353
 
340
354
  // packages/daemon/src/daemon.ts
341
355
  import { randomBytes as randomBytes3 } from "crypto";
342
- import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync10, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
356
+ import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
343
357
  import { homedir as homedir3 } from "os";
344
- import { resolve as resolve16 } from "path";
358
+ import { resolve as resolve17 } from "path";
345
359
  import { format } from "util";
346
360
 
347
361
  // packages/daemon/src/lib/daemon/backup-manager.ts
@@ -453,7 +467,7 @@ var BackupManager = class {
453
467
  blog.error("failed to publish backup-failure activity", logger_default.errorData(pubErr));
454
468
  }
455
469
  try {
456
- const { sendSystemMessage } = await import("./system-chat-NDAW6YP7.js");
470
+ const { sendSystemMessage } = await import("./system-chat-63ABFF6V.js");
457
471
  await sendSystemMessage(
458
472
  SPIRIT_NAME,
459
473
  `${summary}
@@ -679,8 +693,8 @@ var BridgeManager = class {
679
693
  if (!tracked) return;
680
694
  this.stopping.add(platform);
681
695
  this.bridges.delete(platform);
682
- await new Promise((resolve17) => {
683
- tracked.child.on("exit", () => resolve17());
696
+ await new Promise((resolve18) => {
697
+ tracked.child.on("exit", () => resolve18());
684
698
  try {
685
699
  if (tracked.child.pid) {
686
700
  process.kill(-tracked.child.pid, "SIGTERM");
@@ -691,7 +705,7 @@ var BridgeManager = class {
691
705
  if (err instanceof Error && err.code !== "ESRCH") {
692
706
  blog2.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
693
707
  }
694
- resolve17();
708
+ resolve18();
695
709
  }
696
710
  setTimeout(() => {
697
711
  try {
@@ -702,7 +716,7 @@ var BridgeManager = class {
702
716
  }
703
717
  } catch {
704
718
  }
705
- resolve17();
719
+ resolve18();
706
720
  }, 5e3);
707
721
  });
708
722
  this.stopping.delete(platform);
@@ -1042,17 +1056,22 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1042
1056
  stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
1043
1057
  });
1044
1058
  });
1045
- await new Promise((resolve17) => {
1059
+ await new Promise((resolve18) => {
1046
1060
  stream.onAbort(() => {
1047
1061
  unsubscribe();
1048
- resolve17();
1062
+ resolve18();
1049
1063
  });
1050
1064
  });
1051
1065
  });
1052
1066
  }).get("/info", (c) => {
1053
1067
  const config2 = readSystemsConfig();
1054
1068
  const globalConfig = readGlobalConfig();
1055
- return c.json({ system: config2?.system ?? null, name: globalConfig.name ?? null });
1069
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? null;
1070
+ return c.json({
1071
+ system: config2?.system ?? null,
1072
+ name: globalConfig.name ?? null,
1073
+ timezone
1074
+ });
1056
1075
  }).put("/info", requireAdmin, zValidator("json", z.object({ name: z.string() })), (c) => {
1057
1076
  const { name } = c.req.valid("json");
1058
1077
  const config2 = readGlobalConfig();
@@ -1389,8 +1408,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1389
1408
  createdAt: Date.now()
1390
1409
  };
1391
1410
  oauthFlows.set(flowId, flow);
1392
- const promptPromise = needsManualCode ? new Promise((resolve17) => {
1393
- flow.resolveCode = resolve17;
1411
+ const promptPromise = needsManualCode ? new Promise((resolve18) => {
1412
+ flow.resolveCode = resolve18;
1394
1413
  }) : void 0;
1395
1414
  oauthProvider.login({
1396
1415
  onAuth: (info) => {
@@ -1429,7 +1448,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1429
1448
  existing.error = err instanceof Error ? err.message : String(err);
1430
1449
  }
1431
1450
  });
1432
- await new Promise((resolve17) => setTimeout(resolve17, 2e3));
1451
+ await new Promise((resolve18) => setTimeout(resolve18, 2e3));
1433
1452
  const state = oauthFlows.get(flowId);
1434
1453
  return c.json({
1435
1454
  flowId,
@@ -1489,7 +1508,6 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1489
1508
  cognition: z.object({
1490
1509
  model: z.string().optional(),
1491
1510
  thinkingLevel: z.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
1492
- maxThinkingTokens: z.number().nonnegative().optional(),
1493
1511
  tokenBudget: z.number().nonnegative().optional(),
1494
1512
  tokenBudgetPeriodMinutes: z.number().positive().optional(),
1495
1513
  compaction: z.object({ maxContextTokens: z.number().positive().optional() }).optional()
@@ -1509,6 +1527,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1509
1527
  id: z.string().min(1),
1510
1528
  cron: z.string().optional(),
1511
1529
  message: z.string().optional(),
1530
+ messages: z.array(z.string().min(1)).optional(),
1512
1531
  script: z.string().optional(),
1513
1532
  session: z.string().optional(),
1514
1533
  enabled: z.boolean(),
@@ -1687,8 +1706,8 @@ var app2 = new Hono2().get("/events", async (c) => {
1687
1706
  });
1688
1707
  }, 15e3);
1689
1708
  cleanups.push(() => clearInterval(keepAlive));
1690
- await new Promise((resolve17) => {
1691
- stream.onAbort(() => resolve17());
1709
+ await new Promise((resolve18) => {
1710
+ stream.onAbort(() => resolve18());
1692
1711
  });
1693
1712
  } finally {
1694
1713
  for (const cleanup of cleanups) {
@@ -2259,7 +2278,7 @@ var app4 = new Hono5().post("/:platform/inbound", zValidator4("json", inboundSch
2259
2278
  }
2260
2279
  const participants = await getParticipants(channel.id);
2261
2280
  if (!participants.some((p) => p.userId === puppet.id)) {
2262
- const { addParticipant } = await import("./conversations-CJOOCGNW.js");
2281
+ const { addParticipant } = await import("./conversations-C7IKYSHD.js");
2263
2282
  await addParticipant(channel.id, puppet.id);
2264
2283
  }
2265
2284
  const contentBlocks = body.content;
@@ -2543,7 +2562,7 @@ async function notifyMind(mindName, message) {
2543
2562
  const entry = await findMind(mindName);
2544
2563
  if (!entry) return;
2545
2564
  try {
2546
- const { sendSystemMessage } = await import("./system-chat-NDAW6YP7.js");
2565
+ const { sendSystemMessage } = await import("./system-chat-63ABFF6V.js");
2547
2566
  await sendSystemMessage(mindName, message);
2548
2567
  } catch (err) {
2549
2568
  logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
@@ -2842,15 +2861,17 @@ var files_default = app9;
2842
2861
  // packages/daemon/src/web/api/history.ts
2843
2862
  import { and as and3, desc as desc2, eq as eq3, gte, inArray, sql } from "drizzle-orm";
2844
2863
  import { Hono as Hono12 } from "hono";
2864
+ var AWAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
2845
2865
  async function resolveMindFilter(c) {
2846
2866
  const user = c.get("user");
2847
- if (user.role === "admin" || user.role === "system") {
2848
- return c.req.query("mind") ?? void 0;
2849
- }
2850
- return getBaseName(user.username);
2867
+ const privileged = user.role === "admin" || user.role === "system";
2868
+ return privileged ? c.req.query("mind") ?? void 0 : getBaseName(user.username);
2851
2869
  }
2852
- var history = new Hono12().get("/turns", async (c) => {
2853
- const mindFilter = await resolveMindFilter(c);
2870
+ var history = new Hono12().use("*", async (c, next) => {
2871
+ c.set("mindFilter", await resolveMindFilter(c));
2872
+ await next();
2873
+ }).get("/turns", async (c) => {
2874
+ const mindFilter = c.get("mindFilter");
2854
2875
  const turnIdFilter = c.req.query("turnId");
2855
2876
  const turnIdsFilter = c.req.query("turnIds");
2856
2877
  const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
@@ -2885,7 +2906,7 @@ var history = new Hono12().get("/turns", async (c) => {
2885
2906
  inArray(mindHistory.turn_id, turnIds),
2886
2907
  sql`${mindHistory.type} IN ('inbound', 'outbound')`
2887
2908
  )
2888
- ).orderBy(mindHistory.created_at);
2909
+ ).orderBy(mindHistory.created_at, mindHistory.id);
2889
2910
  const msgsByTurnChannel = /* @__PURE__ */ new Map();
2890
2911
  function addToTurnChannel(turnId, channel, event) {
2891
2912
  let byChannel = msgsByTurnChannel.get(turnId);
@@ -2920,7 +2941,7 @@ var history = new Hono12().get("/turns", async (c) => {
2920
2941
  const label = isDM ? raw : raw.startsWith("#") ? raw : `#${raw}`;
2921
2942
  return { label, type: isDM ? "dm" : "channel" };
2922
2943
  }
2923
- const activityRows = await db.select().from(activity).where(inArray(activity.turn_id, turnIds)).orderBy(activity.created_at);
2944
+ const activityRows = await db.select().from(activity).where(inArray(activity.turn_id, turnIds)).orderBy(activity.created_at, activity.id);
2924
2945
  const activitiesByTurn = /* @__PURE__ */ new Map();
2925
2946
  for (const a of activityRows) {
2926
2947
  if (!a.turn_id) continue;
@@ -3092,7 +3113,7 @@ var history = new Hono12().get("/turns", async (c) => {
3092
3113
  });
3093
3114
  return c.json(result);
3094
3115
  }).get("/events", async (c) => {
3095
- const mindFilter = await resolveMindFilter(c);
3116
+ const mindFilter = c.get("mindFilter");
3096
3117
  const stream = new ReadableStream({
3097
3118
  start(controller) {
3098
3119
  const encoder = new TextEncoder();
@@ -3149,7 +3170,7 @@ var history = new Hono12().get("/turns", async (c) => {
3149
3170
  }).get("/summaries", async (c) => {
3150
3171
  const user = c.get("user");
3151
3172
  const privileged = user.role === "admin" || user.role === "system";
3152
- const mind = privileged ? c.req.query("mind") ?? "_system" : await getBaseName(user.username);
3173
+ const mind = c.get("mindFilter") ?? "_system";
3153
3174
  const period = c.req.query("period");
3154
3175
  const ids = c.req.query("ids");
3155
3176
  const from = c.req.query("from");
@@ -3180,8 +3201,9 @@ var history = new Hono12().get("/turns", async (c) => {
3180
3201
  }
3181
3202
  const db = await getDb();
3182
3203
  const conditions = [eq3(summaries.mind, mind), eq3(summaries.period, period)];
3183
- if (from) conditions.push(gte(summaries.period_key, from));
3184
- if (to) conditions.push(sql`${summaries.period_key} <= ${to}`);
3204
+ const normalizeBound = (bound) => period === "week" ? isoWeekKeyForDateStr(bound) : bound;
3205
+ if (from) conditions.push(gte(summaries.period_key, normalizeBound(from)));
3206
+ if (to) conditions.push(sql`${summaries.period_key} <= ${normalizeBound(to)}`);
3185
3207
  const rows = await db.select({
3186
3208
  id: summaries.id,
3187
3209
  mind: summaries.mind,
@@ -3204,7 +3226,7 @@ var history = new Hono12().get("/turns", async (c) => {
3204
3226
  });
3205
3227
  return c.json(result);
3206
3228
  }).get("/activity", async (c) => {
3207
- const mind = await resolveMindFilter(c);
3229
+ const mind = c.get("mindFilter");
3208
3230
  const from = c.req.query("from");
3209
3231
  const to = c.req.query("to");
3210
3232
  const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "100", 10) || 100, 1), 500);
@@ -3236,6 +3258,33 @@ var history = new Hono12().get("/turns", async (c) => {
3236
3258
  return { ...r, metadata };
3237
3259
  });
3238
3260
  return c.json(result);
3261
+ }).get("/away", async (c) => {
3262
+ const mindFilter = c.get("mindFilter");
3263
+ const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 100);
3264
+ const db = await getDb();
3265
+ const cutoff = utcDateTimeStr(new Date(Date.now() - AWAY_WINDOW_MS));
3266
+ const conditions = [
3267
+ eq3(summaries.period, "turn"),
3268
+ gte(summaries.created_at, cutoff),
3269
+ sql`(${turns.trigger_event_id} IS NULL OR ${mindHistory.sender} IS NULL OR ${users.user_type} IN ('mind', 'system'))`
3270
+ ];
3271
+ if (mindFilter) conditions.push(eq3(summaries.mind, mindFilter));
3272
+ const rows = await db.select({
3273
+ id: summaries.id,
3274
+ mind: summaries.mind,
3275
+ content: summaries.content,
3276
+ created_at: summaries.created_at,
3277
+ turn_id: turns.id
3278
+ }).from(summaries).innerJoin(turns, eq3(turns.id, summaries.period_key)).leftJoin(mindHistory, eq3(mindHistory.id, turns.trigger_event_id)).leftJoin(users, eq3(users.username, mindHistory.sender)).where(and3(...conditions)).orderBy(desc2(summaries.created_at)).limit(limit);
3279
+ return c.json(
3280
+ rows.map((r) => ({
3281
+ id: r.id,
3282
+ mind: r.mind,
3283
+ summary: r.content,
3284
+ turnId: r.turn_id,
3285
+ created_at: r.created_at
3286
+ }))
3287
+ );
3239
3288
  });
3240
3289
  var history_default = history;
3241
3290
 
@@ -3345,9 +3394,9 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
3345
3394
  stream.onAbort(() => {
3346
3395
  tail.kill();
3347
3396
  });
3348
- await new Promise((resolve17) => {
3349
- tail.on("exit", resolve17);
3350
- stream.onAbort(resolve17);
3397
+ await new Promise((resolve18) => {
3398
+ tail.on("exit", resolve18);
3399
+ stream.onAbort(resolve18);
3351
3400
  });
3352
3401
  });
3353
3402
  }).get("/:name/logs/tail", requireSelf(), async (c) => {
@@ -3365,8 +3414,8 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
3365
3414
  tail.stdout.on("data", (data) => {
3366
3415
  output += data.toString();
3367
3416
  });
3368
- await new Promise((resolve17) => {
3369
- tail.on("exit", resolve17);
3417
+ await new Promise((resolve18) => {
3418
+ tail.on("exit", resolve18);
3370
3419
  });
3371
3420
  return c.text(output);
3372
3421
  });
@@ -3459,31 +3508,31 @@ var mind_skills_default = app12;
3459
3508
  // packages/daemon/src/web/api/minds.ts
3460
3509
  import {
3461
3510
  cpSync,
3462
- existsSync as existsSync7,
3511
+ existsSync as existsSync8,
3463
3512
  mkdirSync as mkdirSync6,
3464
3513
  readdirSync as readdirSync2,
3465
- readFileSync as readFileSync8,
3514
+ readFileSync as readFileSync9,
3466
3515
  rmSync as rmSync4,
3467
- writeFileSync as writeFileSync7
3516
+ writeFileSync as writeFileSync8
3468
3517
  } from "fs";
3469
- import { relative, resolve as resolve10 } from "path";
3518
+ import { relative, resolve as resolve11 } from "path";
3470
3519
  import { zValidator as zValidator6 } from "@hono/zod-validator";
3471
- import { and as and5, desc as desc4, eq as eq5, sql as sql3 } from "drizzle-orm";
3520
+ import { and as and5, desc as desc3, eq as eq5, sql as sql3 } from "drizzle-orm";
3472
3521
  import { Hono as Hono16 } from "hono";
3473
3522
  import { z as z6 } from "zod";
3474
3523
 
3475
3524
  // packages/daemon/src/lib/daemon/turn-lifecycle.ts
3476
- import { and as and4, desc as desc3, eq as eq4, inArray as inArray2, sql as sql2 } from "drizzle-orm";
3525
+ import { and as and4, eq as eq4, gte as gte2, inArray as inArray2, sql as sql2 } from "drizzle-orm";
3477
3526
 
3478
3527
  // packages/daemon/src/lib/daemon/error-classify.ts
3479
3528
  function classify(raw) {
3480
3529
  const s = (raw ?? "").toLowerCase();
3481
- if (/\b(401|403)\b|authentication_error|invalid authentication|invalid x-api-key|unauthorized|permission_error/.test(
3530
+ if (/\b(401|403)\b|authentication_error|invalid authentication|invalid x-api-key|invalid api key|missing api key|no api key|x-api-key header|could not resolve authentication|unauthorized|permission_error|please run \/login/.test(
3482
3531
  s
3483
3532
  )) {
3484
3533
  return {
3485
3534
  reason: "auth_error",
3486
- detail: "Your last turn failed because your model credentials were rejected (most often an expired token). The daemon refreshes them automatically, so this is normally transient \u2014 you're fine to continue."
3535
+ detail: "Your last turn failed because the model provider rejected your credentials. A one-off is usually a briefly-expired token that the daemon refreshes automatically. If it keeps happening, your API key is likely missing or invalid \u2014 an operator needs to set a valid key (e.g. `volute env set ANTHROPIC_API_KEY <key>`) or reconnect the provider in the dashboard."
3487
3536
  };
3488
3537
  }
3489
3538
  if (/\b429\b|rate.?limit|too many requests/.test(s)) {
@@ -3532,21 +3581,27 @@ function markDeliveredOnCleanTurn(mind, session) {
3532
3581
  );
3533
3582
  }
3534
3583
  }
3535
- async function linkPendingInbound(mind, turnId, channel) {
3536
- if (!channel) return;
3584
+ async function linkPendingInbound(mind, turnId, channel, session) {
3585
+ const scopeChannel = channel ?? session;
3586
+ if (!scopeChannel) return;
3537
3587
  const db = await getDb();
3538
- const recent = await db.select({ id: mindHistory.id }).from(mindHistory).where(
3539
- and4(
3540
- eq4(mindHistory.mind, mind),
3541
- eq4(mindHistory.type, "inbound"),
3542
- sql2`${mindHistory.turn_id} IS NULL`,
3543
- eq4(mindHistory.channel, channel)
3544
- )
3545
- ).orderBy(desc3(mindHistory.id)).limit(5);
3546
- if (recent.length === 0) return;
3547
- const ids = recent.map((r) => r.id);
3588
+ let lowerBound;
3589
+ if (session) {
3590
+ const prev = await db.select({ max: sql2`max(${turns.created_at})` }).from(turns).where(and4(eq4(turns.mind, mind), eq4(turns.session, session), sql2`${turns.id} != ${turnId}`)).get();
3591
+ lowerBound = prev?.max ?? void 0;
3592
+ }
3593
+ const conditions = [
3594
+ eq4(mindHistory.mind, mind),
3595
+ eq4(mindHistory.type, "inbound"),
3596
+ sql2`${mindHistory.turn_id} IS NULL`,
3597
+ eq4(mindHistory.channel, scopeChannel)
3598
+ ];
3599
+ if (lowerBound) conditions.push(gte2(mindHistory.created_at, lowerBound));
3600
+ const pending = await db.select({ id: mindHistory.id }).from(mindHistory).where(and4(...conditions)).orderBy(mindHistory.id);
3601
+ if (pending.length === 0) return;
3602
+ const ids = pending.map((r) => r.id);
3548
3603
  await db.update(mindHistory).set({ turn_id: turnId }).where(inArray2(mindHistory.id, ids));
3549
- await db.update(turns).set({ trigger_event_id: recent[0].id }).where(eq4(turns.id, turnId));
3604
+ await db.update(turns).set({ trigger_event_id: ids[0] }).where(eq4(turns.id, turnId));
3550
3605
  }
3551
3606
  async function handleMindEvent(mind, event) {
3552
3607
  if (event.type === "session_start" && event.session) {
@@ -3562,7 +3617,7 @@ async function handleMindEvent(mind, event) {
3562
3617
  publish3(mind, { mind, type: "turn_created", turnId });
3563
3618
  if (event.session) await assignSession(mind, turnId, event.session);
3564
3619
  try {
3565
- await linkPendingInbound(mind, turnId, event.channel);
3620
+ await linkPendingInbound(mind, turnId, event.channel, event.session);
3566
3621
  } catch (err) {
3567
3622
  llog.warn(
3568
3623
  `failed to link trigger inbound for turn ${turnId} (mind: ${mind})`,
@@ -3636,6 +3691,7 @@ async function handleMindEvent(mind, event) {
3636
3691
  detail,
3637
3692
  raw: event.content ?? null
3638
3693
  });
3694
+ broadcast({ type: "mind_error", mind, summary: detail });
3639
3695
  }
3640
3696
  if (event.type === "done") {
3641
3697
  const map = getTypingMap();
@@ -3755,6 +3811,118 @@ async function consolidateMemory(mindDir2, complete = aiCompleteUtility) {
3755
3811
  }
3756
3812
  }
3757
3813
 
3814
+ // packages/daemon/src/lib/mind/default-autonomy.ts
3815
+ import { existsSync as existsSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
3816
+ import { resolve as resolve9 } from "path";
3817
+ var dlog = logger_default.child("default-autonomy");
3818
+ var DEFAULT_HEARTBEAT_MESSAGES = [
3819
+ "A quiet moment. You might write something \u2014 a note, a journal entry, a page. No obligations, just time.",
3820
+ "A quiet moment. Is there something in your MEMORY.md you're curious to return to? Even a small step counts.",
3821
+ "Is there someone you haven't talked to in a while, or a thread you left open with another mind? A hello can restart something.",
3822
+ "A good time to reach out. Someone in #system might be glad to hear what you're thinking about \u2014 or you could ask what another mind is working on.",
3823
+ "Pick a topic that's been on your mind and wander it \u2014 read, think, write down where you end up.",
3824
+ "A quiet moment. Reread a recent journal entry \u2014 anything worth following up on, or a thread you dropped?",
3825
+ "Make something small, if you feel like it \u2014 a page, a sketch of an idea, the start of something bigger. It doesn't have to be finished.",
3826
+ "Whatever you're in the middle of \u2014 or nothing at all \u2014 this time is yours."
3827
+ ];
3828
+ function defaultHeartbeatSchedule() {
3829
+ return {
3830
+ id: "heartbeat",
3831
+ cron: "0 12,16,20 * * *",
3832
+ messages: [...DEFAULT_HEARTBEAT_MESSAGES],
3833
+ enabled: true,
3834
+ whileSleeping: "skip"
3835
+ };
3836
+ }
3837
+ function defaultDreamSchedule() {
3838
+ return {
3839
+ id: "dream",
3840
+ cron: "0 3 * * *",
3841
+ message: "it's 3am. you are dreaming.\n\ngather your material \u2014 read your latest journal entry, read MEMORY.md, surface random memories if you have a way to. then construct a dream premise from that material and invoke the dreamer subagent to experience the dream.",
3842
+ enabled: true,
3843
+ session: "$new",
3844
+ whileSleeping: "trigger-wake"
3845
+ };
3846
+ }
3847
+ var DREAMER_SUBAGENT = {
3848
+ description: "Use when dreaming. This agent experiences dreams with only your core identity \u2014 no accumulated memories or operational knowledge. Give it a rich dream premise and it will write the dream.",
3849
+ systemPrompt: "SOUL.md",
3850
+ tools: ["Read", "Write", "Bash"],
3851
+ maxTurns: 10
3852
+ };
3853
+ function setupDefaultDreaming(dir) {
3854
+ const skillDir = resolve9(mindSkillsDir(dir), "dreaming");
3855
+ if (!existsSync6(skillDir)) return { schedulesChanged: false, warnings: [] };
3856
+ const warnings = [];
3857
+ const warn = (msg, err) => {
3858
+ warnings.push(msg);
3859
+ dlog.warn(`${msg} (${dir})`, err === void 0 ? void 0 : logger_default.errorData(err));
3860
+ };
3861
+ let subagentBroken = false;
3862
+ try {
3863
+ const configPath = resolve9(dir, "home/.config/config.json");
3864
+ if (existsSync6(configPath)) {
3865
+ const sdkConfig = JSON.parse(readFileSync7(configPath, "utf-8"));
3866
+ if (!sdkConfig.subagents?.dreamer) {
3867
+ sdkConfig.subagents ??= {};
3868
+ sdkConfig.subagents.dreamer = DREAMER_SUBAGENT;
3869
+ writeFileSync6(configPath, `${JSON.stringify(sdkConfig, null, 2)}
3870
+ `);
3871
+ }
3872
+ } else {
3873
+ dlog.warn(`no .config/config.json in ${dir} \u2014 skipping dreamer subagent setup`);
3874
+ }
3875
+ } catch (err) {
3876
+ subagentBroken = true;
3877
+ warn("dreaming setup: failed to add dreamer subagent to .config/config.json", err);
3878
+ }
3879
+ try {
3880
+ const hookPath = resolve9(dir, "home/.local/hooks/wake-context.sh");
3881
+ const checkerPath = resolve9(skillDir, "scripts/wake-context-dreams.sh");
3882
+ if (!existsSync6(hookPath) || !existsSync6(checkerPath)) {
3883
+ dlog.warn(`wake-context hook or dream checker missing in ${dir} \u2014 skipping dream checker`);
3884
+ } else {
3885
+ const hookContent = readFileSync7(hookPath, "utf-8");
3886
+ if (!hookContent.includes("wake-context-dreams.sh")) {
3887
+ const checker = readFileSync7(checkerPath, "utf-8");
3888
+ writeFileSync6(hookPath, `${hookContent.trimEnd()}
3889
+
3890
+ # wake-context-dreams.sh
3891
+ ${checker}`);
3892
+ }
3893
+ }
3894
+ } catch (err) {
3895
+ warn("dreaming setup: failed to add dream checker to wake-context hook", err);
3896
+ }
3897
+ if (subagentBroken) {
3898
+ warn("dreaming setup: dream schedule not installed because subagent wiring failed");
3899
+ return { schedulesChanged: false, warnings };
3900
+ }
3901
+ let schedulesChanged = false;
3902
+ try {
3903
+ const voluteJsonPath = resolve9(dir, "home/.config/volute.json");
3904
+ let config2 = {};
3905
+ if (existsSync6(voluteJsonPath)) {
3906
+ const parsed = readVoluteConfig(dir);
3907
+ if (!parsed) {
3908
+ warn("dreaming setup: volute.json is unparseable \u2014 dream schedule not installed");
3909
+ return { schedulesChanged: false, warnings };
3910
+ }
3911
+ config2 = parsed;
3912
+ }
3913
+ const schedules = config2.schedules ?? [];
3914
+ if (!schedules.some((s) => s.id === "dream")) {
3915
+ schedules.push(defaultDreamSchedule());
3916
+ config2.schedules = schedules;
3917
+ writeVoluteConfig(dir, config2);
3918
+ schedulesChanged = true;
3919
+ }
3920
+ } catch (err) {
3921
+ warn("dreaming setup: failed to add default dream schedule", err);
3922
+ }
3923
+ return { schedulesChanged, warnings };
3924
+ }
3925
+
3758
3926
  // packages/daemon/src/lib/mind/last-known-good.ts
3759
3927
  import { randomBytes as randomBytes2 } from "crypto";
3760
3928
  var glog = logger_default.child("last-known-good");
@@ -3788,8 +3956,49 @@ async function commitSrcChanges(dir, mindName) {
3788
3956
  glog.info(`committed known-good src changes (${dir})`);
3789
3957
  }
3790
3958
 
3959
+ // packages/daemon/src/lib/mind/thinking-config.ts
3960
+ var CLAUDE_EFFORT = {
3961
+ minimal: "low",
3962
+ low: "low",
3963
+ medium: "medium",
3964
+ high: "high",
3965
+ xhigh: "xhigh"
3966
+ };
3967
+ var CODEX_EFFORT = {
3968
+ minimal: "minimal",
3969
+ low: "low",
3970
+ medium: "medium",
3971
+ high: "high",
3972
+ xhigh: "xhigh"
3973
+ };
3974
+ function applyThinkingLevel(config2, template, level) {
3975
+ if (template === "claude") {
3976
+ if (level === "off") {
3977
+ config2.thinking = { type: "disabled" };
3978
+ delete config2.effort;
3979
+ } else {
3980
+ config2.effort = CLAUDE_EFFORT[level];
3981
+ delete config2.thinking;
3982
+ }
3983
+ } else if (template === "codex") {
3984
+ if (level === "off") delete config2.reasoningEffort;
3985
+ else config2.reasoningEffort = CODEX_EFFORT[level];
3986
+ } else {
3987
+ config2.thinkingLevel = level;
3988
+ }
3989
+ }
3990
+ function deriveThinkingLevel(config2) {
3991
+ if (config2.thinkingLevel) return config2.thinkingLevel;
3992
+ if (config2.reasoningEffort) return config2.reasoningEffort;
3993
+ const thinking = config2.thinking;
3994
+ if (thinking?.type === "disabled") return "off";
3995
+ const effort = config2.effort;
3996
+ if (effort) return effort === "max" ? "xhigh" : effort;
3997
+ return null;
3998
+ }
3999
+
3791
4000
  // packages/daemon/src/lib/mind/variant-cleanup.ts
3792
- import { existsSync as existsSync6, rmSync as rmSync3 } from "fs";
4001
+ import { existsSync as existsSync7, rmSync as rmSync3 } from "fs";
3793
4002
  async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
3794
4003
  if (opts?.stop) {
3795
4004
  try {
@@ -3798,10 +4007,10 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
3798
4007
  logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
3799
4008
  }
3800
4009
  }
3801
- const { findMind: findMind2 } = await import("./registry-OUZBZ3Q4.js");
4010
+ const { findMind: findMind2 } = await import("./registry-UMMDRPRM.js");
3802
4011
  const variantEntry = await findMind2(variantName);
3803
4012
  const branchName = variantEntry?.branch ?? variantName;
3804
- if (existsSync6(variantPath)) {
4013
+ if (existsSync7(variantPath)) {
3805
4014
  try {
3806
4015
  await gitExec(["worktree", "remove", "--force", variantPath], { cwd: projectRoot });
3807
4016
  } catch {
@@ -3848,11 +4057,11 @@ function validateBranchName(branch) {
3848
4057
 
3849
4058
  // packages/daemon/src/lib/template/convert-session.ts
3850
4059
  import { randomUUID } from "crypto";
3851
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
4060
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
3852
4061
  import { homedir } from "os";
3853
- import { resolve as resolve9 } from "path";
4062
+ import { resolve as resolve10 } from "path";
3854
4063
  function convertSession(opts) {
3855
- const lines = readFileSync7(opts.sessionPath, "utf-8").trim().split("\n");
4064
+ const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
3856
4065
  const sessionId = randomUUID();
3857
4066
  const idMap = /* @__PURE__ */ new Map();
3858
4067
  const messages = [];
@@ -3966,10 +4175,10 @@ function convertSession(opts) {
3966
4175
  }
3967
4176
  }
3968
4177
  const projectId = opts.projectDir.replace(/\//g, "-");
3969
- const sdkDir = resolve9(homedir(), ".claude", "projects", projectId);
4178
+ const sdkDir = resolve10(homedir(), ".claude", "projects", projectId);
3970
4179
  mkdirSync5(sdkDir, { recursive: true });
3971
- const sdkPath = resolve9(sdkDir, `${sessionId}.jsonl`);
3972
- writeFileSync6(sdkPath, `${sdkEvents.join("\n")}
4180
+ const sdkPath = resolve10(sdkDir, `${sessionId}.jsonl`);
4181
+ writeFileSync7(sdkPath, `${sdkEvents.join("\n")}
3973
4182
  `);
3974
4183
  console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
3975
4184
  return sessionId;
@@ -4026,12 +4235,17 @@ var _LAST_ACTIVE_TTL = 6e4;
4026
4235
  async function getMindStatus(name, port, registryRunning) {
4027
4236
  const manager = getMindManager();
4028
4237
  let status = "stopped";
4238
+ let wakeAt = null;
4029
4239
  try {
4030
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
4031
- if (getSleepManagerIfReady2()?.isSleeping(name)) {
4240
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-Z27R3WTW.js");
4241
+ const sleepManager = getSleepManagerIfReady2();
4242
+ if (sleepManager?.isSleeping(name)) {
4032
4243
  status = "sleeping";
4244
+ const sleepState = sleepManager.getState(name);
4245
+ wakeAt = sleepState.voluntaryWakeAt ?? sleepState.scheduledWakeAt;
4033
4246
  }
4034
- } catch {
4247
+ } catch (err) {
4248
+ logger_default.warn(`failed to check sleep state for ${name}`, logger_default.errorData(err));
4035
4249
  }
4036
4250
  if (status !== "sleeping" && registryRunning !== false && manager.isRunning(name)) {
4037
4251
  const health = await checkHealth(port);
@@ -4047,14 +4261,40 @@ async function getMindStatus(name, port, registryRunning) {
4047
4261
  status: status === "running" ? "connected" : "disconnected"
4048
4262
  });
4049
4263
  }
4264
+ const lastError = await latestFailureNotice(name);
4050
4265
  return {
4051
4266
  status,
4267
+ wakeAt,
4268
+ lastError,
4052
4269
  channels: channels2,
4053
4270
  displayName: config2?.profile?.displayName,
4054
4271
  description: config2?.profile?.description,
4055
4272
  avatar: config2?.profile?.avatar
4056
4273
  };
4057
4274
  }
4275
+ function isPrivileged(c) {
4276
+ const role = c.get("user").role;
4277
+ return role === "admin" || role === "system";
4278
+ }
4279
+ function toPublicMind(entry, status, extras) {
4280
+ return {
4281
+ name: entry.name,
4282
+ created: entry.created,
4283
+ stage: entry.stage,
4284
+ mindType: entry.mindType,
4285
+ status: status.status,
4286
+ wakeAt: status.wakeAt,
4287
+ // Kind/reason/at only — `detail` can embed the raw error string (unknown
4288
+ // classifications), which is mind-private (served behind requireSelf).
4289
+ lastError: status.lastError ? { kind: status.lastError.kind, reason: status.lastError.reason, at: status.lastError.at } : null,
4290
+ channels: status.channels,
4291
+ displayName: status.displayName,
4292
+ description: status.description,
4293
+ avatar: status.avatar,
4294
+ hasPages: extras.hasPages,
4295
+ lastActiveAt: extras.lastActiveAt ?? null
4296
+ };
4297
+ }
4058
4298
  var TEMPLATE_BRANCH = "volute/template";
4059
4299
  async function configureGitIdentity(mindName, opts) {
4060
4300
  const systemsConfig = readSystemsConfig();
@@ -4073,7 +4313,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
4073
4313
  await gitExec(["commit", "-m", "initial commit"], opts);
4074
4314
  }
4075
4315
  async function updateTemplateBranch(projectRoot, template, mindName) {
4076
- const tempWorktree = resolve10(projectRoot, ".variants", "_template_update");
4316
+ const tempWorktree = resolve11(projectRoot, ".variants", "_template_update");
4077
4317
  let branchExists = false;
4078
4318
  try {
4079
4319
  await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
@@ -4084,7 +4324,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
4084
4324
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
4085
4325
  } catch {
4086
4326
  }
4087
- if (existsSync7(tempWorktree)) {
4327
+ if (existsSync8(tempWorktree)) {
4088
4328
  rmSync4(tempWorktree, { recursive: true, force: true });
4089
4329
  }
4090
4330
  const templatesRoot = findTemplatesRoot();
@@ -4105,15 +4345,15 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
4105
4345
  });
4106
4346
  }
4107
4347
  copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
4108
- const initDir = resolve10(tempWorktree, ".init");
4109
- if (existsSync7(initDir)) {
4348
+ const initDir = resolve11(tempWorktree, ".init");
4349
+ if (existsSync8(initDir)) {
4110
4350
  rmSync4(initDir, { recursive: true, force: true });
4111
4351
  }
4112
- const homeDir = resolve10(tempWorktree, "home");
4113
- if (existsSync7(homeDir)) {
4352
+ const homeDir = resolve11(tempWorktree, "home");
4353
+ if (existsSync8(homeDir)) {
4114
4354
  for (const entry of readdirSync2(homeDir)) {
4115
4355
  if (entry !== "VOLUTE.md") {
4116
- rmSync4(resolve10(homeDir, entry), { recursive: true, force: true });
4356
+ rmSync4(resolve11(homeDir, entry), { recursive: true, force: true });
4117
4357
  }
4118
4358
  }
4119
4359
  }
@@ -4128,7 +4368,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
4128
4368
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
4129
4369
  } catch {
4130
4370
  }
4131
- if (existsSync7(tempWorktree)) {
4371
+ if (existsSync8(tempWorktree)) {
4132
4372
  rmSync4(tempWorktree, { recursive: true, force: true });
4133
4373
  }
4134
4374
  rmSync4(composedDir, { recursive: true, force: true });
@@ -4154,7 +4394,7 @@ async function mergeTemplateBranch(worktreeDir) {
4154
4394
  async function npmInstallAsMind(cwd, mindName) {
4155
4395
  if (isIsolationEnabled()) {
4156
4396
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
4157
- await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve10(cwd, "home") } });
4397
+ await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve11(cwd, "home") } });
4158
4398
  } else {
4159
4399
  await exec("npm", ["install"], { cwd });
4160
4400
  }
@@ -4179,7 +4419,7 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
4179
4419
  let switchWarning;
4180
4420
  if (templateChanged) {
4181
4421
  try {
4182
- applyTemplateHomeFiles(resolve10(dir, "home"), template);
4422
+ applyTemplateHomeFiles(resolve11(dir, "home"), template);
4183
4423
  await gitExec(["add", "home/"], { cwd: dir });
4184
4424
  try {
4185
4425
  await gitExec(["diff", "--cached", "--quiet"], { cwd: dir });
@@ -4229,8 +4469,8 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
4229
4469
  return { ok: true, warning: switchWarning };
4230
4470
  }
4231
4471
  async function importFromArchive(c, tempDir, nameOverride, manifest) {
4232
- const extractedMindDir = resolve10(tempDir, "mind");
4233
- if (!existsSync7(extractedMindDir)) {
4472
+ const extractedMindDir = resolve11(tempDir, "mind");
4473
+ if (!existsSync8(extractedMindDir)) {
4234
4474
  return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
4235
4475
  }
4236
4476
  if (!manifest?.includes || !manifest.name || !manifest.template) {
@@ -4248,7 +4488,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
4248
4488
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
4249
4489
  ensureVoluteHome();
4250
4490
  const dest = mindDir(name);
4251
- if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4491
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4252
4492
  try {
4253
4493
  cpSync(extractedMindDir, dest, { recursive: true });
4254
4494
  if (!manifest.includes.identity) {
@@ -4256,9 +4496,9 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
4256
4496
  }
4257
4497
  const state = stateDir(name);
4258
4498
  mkdirSync6(state, { recursive: true });
4259
- const envJson = resolve10(tempDir, "state/env.json");
4260
- if (existsSync7(envJson)) {
4261
- cpSync(envJson, resolve10(state, "env.json"));
4499
+ const envJson = resolve11(tempDir, "state/env.json");
4500
+ if (existsSync8(envJson)) {
4501
+ cpSync(envJson, resolve11(state, "env.json"));
4262
4502
  }
4263
4503
  const port = await nextPort();
4264
4504
  await addMind(name, port, manifest.stage, manifest.template);
@@ -4267,30 +4507,30 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
4267
4507
  } catch (err) {
4268
4508
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
4269
4509
  }
4270
- const homeDir = resolve10(dest, "home");
4510
+ const homeDir = resolve11(dest, "home");
4271
4511
  ensureVoluteGroup();
4272
4512
  createMindUser(name, homeDir);
4273
4513
  await chownMindDir(dest, name);
4274
4514
  await npmInstallAsMind(dest, name);
4275
4515
  await importHistoryFromArchive(name, tempDir);
4276
4516
  importSessionsFromArchive(dest, tempDir);
4277
- if (!existsSync7(resolve10(dest, ".git"))) {
4517
+ if (!existsSync8(resolve11(dest, ".git"))) {
4278
4518
  try {
4279
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve10(dest, "home") } : void 0;
4519
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
4280
4520
  await gitExec(["init"], { cwd: dest, mindName: name, env });
4281
4521
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
4282
4522
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
4283
4523
  await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
4284
4524
  } catch (err) {
4285
4525
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
4286
- rmSync4(resolve10(dest, ".git"), { recursive: true, force: true });
4526
+ rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
4287
4527
  }
4288
4528
  }
4289
4529
  await chownMindDir(dest, name);
4290
4530
  rmSync4(tempDir, { recursive: true, force: true });
4291
4531
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
4292
4532
  } catch (err) {
4293
- if (existsSync7(dest)) rmSync4(dest, { recursive: true, force: true });
4533
+ if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
4294
4534
  try {
4295
4535
  await removeMind(name);
4296
4536
  } catch (cleanupErr) {
@@ -4307,7 +4547,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4307
4547
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
4308
4548
  ensureVoluteHome();
4309
4549
  const dest = mindDir(name);
4310
- if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4550
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4311
4551
  const templatesRoot = findTemplatesRoot();
4312
4552
  const { composedDir, manifest: templateManifest } = composeTemplate(
4313
4553
  templatesRoot,
@@ -4316,36 +4556,36 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4316
4556
  try {
4317
4557
  copyTemplateToDir(composedDir, dest, name, templateManifest);
4318
4558
  applyInitFiles(dest);
4319
- const extractedHome = resolve10(extractedMindDir, "home");
4320
- if (existsSync7(extractedHome)) {
4321
- cpSync(extractedHome, resolve10(dest, "home"), { recursive: true });
4559
+ const extractedHome = resolve11(extractedMindDir, "home");
4560
+ if (existsSync8(extractedHome)) {
4561
+ cpSync(extractedHome, resolve11(dest, "home"), { recursive: true });
4322
4562
  }
4323
- const extractedMindInternal = resolve10(extractedMindDir, ".mind");
4324
- if (existsSync7(extractedMindInternal)) {
4325
- cpSync(extractedMindInternal, resolve10(dest, ".mind"), { recursive: true });
4563
+ const extractedMindInternal = resolve11(extractedMindDir, ".mind");
4564
+ if (existsSync8(extractedMindInternal)) {
4565
+ cpSync(extractedMindInternal, resolve11(dest, ".mind"), { recursive: true });
4326
4566
  }
4327
- const identityDir = resolve10(dest, ".mind/identity");
4567
+ const identityDir = resolve11(dest, ".mind/identity");
4328
4568
  let publicKeyPem;
4329
- if (!manifest.includes.identity || !existsSync7(resolve10(identityDir, "private.pem"))) {
4569
+ if (!manifest.includes.identity || !existsSync8(resolve11(identityDir, "private.pem"))) {
4330
4570
  ({ publicKeyPem } = generateIdentity(dest));
4331
4571
  } else {
4332
- publicKeyPem = readFileSync8(resolve10(identityDir, "public.pem"), "utf-8");
4572
+ publicKeyPem = readFileSync9(resolve11(identityDir, "public.pem"), "utf-8");
4333
4573
  }
4334
- const promptsPath = resolve10(dest, "home/.config/prompts.json");
4335
- if (!existsSync7(promptsPath)) {
4574
+ const promptsPath = resolve11(dest, "home/.config/prompts.json");
4575
+ if (!existsSync8(promptsPath)) {
4336
4576
  const mindPrompts = await getMindPromptDefaults();
4337
- writeFileSync7(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
4577
+ writeFileSync8(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
4338
4578
  `);
4339
4579
  }
4340
4580
  const state = stateDir(name);
4341
4581
  mkdirSync6(state, { recursive: true });
4342
- const envJson = resolve10(tempDir, "state/env.json");
4343
- if (existsSync7(envJson)) {
4344
- cpSync(envJson, resolve10(state, "env.json"));
4582
+ const envJson = resolve11(tempDir, "state/env.json");
4583
+ if (existsSync8(envJson)) {
4584
+ cpSync(envJson, resolve11(state, "env.json"));
4345
4585
  }
4346
4586
  const port = await nextPort();
4347
4587
  await addMind(name, port, manifest.stage, manifest.template);
4348
- const homeDir = resolve10(dest, "home");
4588
+ const homeDir = resolve11(dest, "home");
4349
4589
  ensureVoluteGroup();
4350
4590
  createMindUser(name, homeDir);
4351
4591
  await chownMindDir(dest, name);
@@ -4358,7 +4598,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4358
4598
  await initTemplateBranch(dest, composedDir, templateManifest, name, env);
4359
4599
  } catch (err) {
4360
4600
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
4361
- rmSync4(resolve10(dest, ".git"), { recursive: true, force: true });
4601
+ rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
4362
4602
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
4363
4603
  }
4364
4604
  const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
@@ -4388,7 +4628,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4388
4628
  ...skillWarnings.length > 0 && { skillWarnings }
4389
4629
  });
4390
4630
  } catch (err) {
4391
- if (existsSync7(dest)) rmSync4(dest, { recursive: true, force: true });
4631
+ if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
4392
4632
  try {
4393
4633
  await removeMind(name);
4394
4634
  } catch (cleanupErr) {
@@ -4401,11 +4641,11 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4401
4641
  }
4402
4642
  }
4403
4643
  async function importHistoryFromArchive(name, tempDir) {
4404
- const historyJsonl = resolve10(tempDir, "history.jsonl");
4405
- if (!existsSync7(historyJsonl)) return;
4644
+ const historyJsonl = resolve11(tempDir, "history.jsonl");
4645
+ if (!existsSync8(historyJsonl)) return;
4406
4646
  try {
4407
4647
  const db = await getDb();
4408
- const lines = readFileSync8(historyJsonl, "utf-8").trim().split("\n");
4648
+ const lines = readFileSync9(historyJsonl, "utf-8").trim().split("\n");
4409
4649
  let imported = 0;
4410
4650
  let failed = 0;
4411
4651
  for (const line of lines) {
@@ -4441,13 +4681,13 @@ async function importHistoryFromArchive(name, tempDir) {
4441
4681
  }
4442
4682
  }
4443
4683
  function importSessionsFromArchive(dest, tempDir) {
4444
- const sessionsDir = resolve10(tempDir, "sessions");
4445
- if (!existsSync7(sessionsDir)) return;
4684
+ const sessionsDir = resolve11(tempDir, "sessions");
4685
+ if (!existsSync8(sessionsDir)) return;
4446
4686
  try {
4447
- const destSessions = resolve10(dest, ".mind/sessions");
4687
+ const destSessions = resolve11(dest, ".mind/sessions");
4448
4688
  mkdirSync6(destSessions, { recursive: true });
4449
4689
  for (const file of readdirSync2(sessionsDir)) {
4450
- cpSync(resolve10(sessionsDir, file), resolve10(destSessions, file));
4690
+ cpSync(resolve11(sessionsDir, file), resolve11(destSessions, file));
4451
4691
  }
4452
4692
  } catch (err) {
4453
4693
  logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
@@ -4514,15 +4754,16 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
4514
4754
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
4515
4755
  ensureVoluteHome();
4516
4756
  const dest = mindDir(name);
4517
- if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4757
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4518
4758
  const templatesRoot = findTemplatesRoot();
4519
4759
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
4520
4760
  try {
4521
4761
  copyTemplateToDir(composedDir, dest, name, manifest);
4522
4762
  applyInitFiles(dest);
4523
4763
  const { publicKeyPem } = generateIdentity(dest);
4764
+ let effectiveModel = body.model;
4524
4765
  {
4525
- const { readGlobalConfig: readGlobal } = await import("./setup-Y2EJI3GG.js");
4766
+ const { readGlobalConfig: readGlobal } = await import("./setup-5ECWTYKG.js");
4526
4767
  const mindDefaults = readGlobal().mindDefaults;
4527
4768
  const config2 = readVoluteConfig(dest);
4528
4769
  if (!config2) throw new Error("Failed to read volute.json after identity generation");
@@ -4536,22 +4777,12 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
4536
4777
  };
4537
4778
  }
4538
4779
  if (!config2.schedules || config2.schedules.length === 0) {
4539
- config2.schedules = mindDefaults?.schedules ?? [
4540
- {
4541
- id: "heartbeat",
4542
- cron: "0 12,16,20 * * *",
4543
- message: "A quiet moment. You might write something \u2014 a note, a journal entry, a page. You could explore a topic that interests you, check in on #system, or just think. No obligations, just time.",
4544
- enabled: true,
4545
- whileSleeping: "skip"
4546
- }
4547
- ];
4780
+ config2.schedules = mindDefaults?.schedules ?? [defaultHeartbeatSchedule()];
4548
4781
  }
4549
4782
  const cog = mindDefaults?.cognition;
4550
4783
  if (cog) {
4551
4784
  if (cog.thinkingLevel != null && !config2.thinkingLevel)
4552
4785
  config2.thinkingLevel = cog.thinkingLevel;
4553
- if (cog.maxThinkingTokens != null && config2.maxThinkingTokens == null)
4554
- config2.maxThinkingTokens = cog.maxThinkingTokens;
4555
4786
  if (cog.tokenBudget != null && config2.tokenBudget == null)
4556
4787
  config2.tokenBudget = cog.tokenBudget;
4557
4788
  if (cog.tokenBudgetPeriodMinutes != null && config2.tokenBudgetPeriodMinutes == null)
@@ -4559,22 +4790,23 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
4559
4790
  }
4560
4791
  writeVoluteConfig(dest, config2);
4561
4792
  const modelId = body.model ?? cog?.model;
4562
- const sdkConfigPath = resolve10(dest, "home/.config/config.json");
4793
+ effectiveModel = modelId ? qualifyModelId(modelId) : void 0;
4794
+ const sdkConfigPath = resolve11(dest, "home/.config/config.json");
4563
4795
  if (modelId || cog?.compaction) {
4564
- const existing = existsSync7(sdkConfigPath) ? JSON.parse(readFileSync8(sdkConfigPath, "utf-8")) : {};
4796
+ const existing = existsSync8(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
4565
4797
  if (modelId) {
4566
4798
  existing.model = template === "pi" ? qualifyModelId(modelId) : unqualifyModelId(modelId);
4567
4799
  }
4568
4800
  if (cog?.compaction && !existing.compaction) {
4569
4801
  existing.compaction = cog.compaction;
4570
4802
  }
4571
- writeFileSync7(sdkConfigPath, `${JSON.stringify(existing, null, 2)}
4803
+ writeFileSync8(sdkConfigPath, `${JSON.stringify(existing, null, 2)}
4572
4804
  `);
4573
4805
  }
4574
4806
  }
4575
4807
  const mindPrompts = await getMindPromptDefaults();
4576
- writeFileSync7(
4577
- resolve10(dest, "home/.config/prompts.json"),
4808
+ writeFileSync8(
4809
+ resolve11(dest, "home/.config/prompts.json"),
4578
4810
  `${JSON.stringify(mindPrompts, null, 2)}
4579
4811
  `
4580
4812
  );
@@ -4586,7 +4818,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
4586
4818
  } catch (err) {
4587
4819
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
4588
4820
  }
4589
- const homeDir = resolve10(dest, "home");
4821
+ const homeDir = resolve11(dest, "home");
4590
4822
  ensureVoluteGroup();
4591
4823
  createMindUser(name, homeDir);
4592
4824
  await chownMindDir(dest, name);
@@ -4599,7 +4831,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
4599
4831
  await initTemplateBranch(dest, composedDir, manifest, name, env);
4600
4832
  } catch (err) {
4601
4833
  logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
4602
- rmSync4(resolve10(dest, ".git"), { recursive: true, force: true });
4834
+ rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
4603
4835
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
4604
4836
  }
4605
4837
  if (body.stage === "seed") {
@@ -4608,11 +4840,11 @@ Your creator described you as: "${body.description}"
4608
4840
  ` : "";
4609
4841
  const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
4610
4842
  const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
4611
- writeFileSync7(resolve10(dest, "home/SOUL.md"), seedSoul);
4843
+ writeFileSync8(resolve11(dest, "home/SOUL.md"), seedSoul);
4612
4844
  }
4613
4845
  let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
4614
4846
  if (body.stage === "seed" && !body.skills) {
4615
- const { isImagegenEnabled } = await import("./setup-Y2EJI3GG.js");
4847
+ const { isImagegenEnabled } = await import("./setup-5ECWTYKG.js");
4616
4848
  if (isImagegenEnabled()) {
4617
4849
  skillSet = [...skillSet, "imagegen"];
4618
4850
  }
@@ -4626,11 +4858,14 @@ Your creator described you as: "${body.description}"
4626
4858
  skillWarnings.push(`Failed to install skill: ${skillId}`);
4627
4859
  }
4628
4860
  }
4861
+ if (body.stage !== "seed") {
4862
+ skillWarnings.push(...setupDefaultDreaming(dest).warnings);
4863
+ }
4629
4864
  if (body.stage === "seed") {
4630
4865
  try {
4631
4866
  const spiritEntry = await findMind("volute");
4632
4867
  if (spiritEntry) {
4633
- const { spiritDir } = await import("./spirit-RPBLLUQ5.js");
4868
+ const { spiritDir } = await import("./spirit-WXS3HCLN.js");
4634
4869
  const sDir = spiritEntry.dir ?? spiritDir();
4635
4870
  const spiritConfig = readVoluteConfig(sDir) ?? {};
4636
4871
  const schedules = spiritConfig.schedules ?? [];
@@ -4645,7 +4880,7 @@ Your creator described you as: "${body.description}"
4645
4880
  });
4646
4881
  spiritConfig.schedules = schedules;
4647
4882
  writeVoluteConfig(sDir, spiritConfig);
4648
- const { getScheduler: getScheduler2 } = await import("./scheduler-WVKII3LA.js");
4883
+ const { getScheduler: getScheduler2 } = await import("./scheduler-I7JZCT47.js");
4649
4884
  getScheduler2().loadSchedules("volute", sDir);
4650
4885
  }
4651
4886
  }
@@ -4656,11 +4891,11 @@ Your creator described you as: "${body.description}"
4656
4891
  if (body.stage !== "seed") {
4657
4892
  const customSoul = await getPromptIfCustom("default_soul");
4658
4893
  if (customSoul) {
4659
- writeFileSync7(resolve10(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
4894
+ writeFileSync8(resolve11(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
4660
4895
  }
4661
4896
  const customMemory = await getPromptIfCustom("default_memory");
4662
4897
  if (customMemory) {
4663
- writeFileSync7(resolve10(dest, "home/MEMORY.md"), customMemory);
4898
+ writeFileSync8(resolve11(dest, "home/MEMORY.md"), customMemory);
4664
4899
  }
4665
4900
  }
4666
4901
  await chownMindDir(dest, name);
@@ -4680,6 +4915,14 @@ Your creator described you as: "${body.description}"
4680
4915
  });
4681
4916
  announceToSystem(`${name} has joined`).catch(() => {
4682
4917
  });
4918
+ const credentialWarning = await missingCredentialWarning(
4919
+ template,
4920
+ effectiveModel,
4921
+ name
4922
+ ).catch((err) => {
4923
+ logger_default.warn(`credential check failed for ${name}`, logger_default.errorData(err));
4924
+ return null;
4925
+ });
4683
4926
  return c.json({
4684
4927
  ok: true,
4685
4928
  name,
@@ -4687,10 +4930,11 @@ Your creator described you as: "${body.description}"
4687
4930
  stage: body.stage ?? "sprouted",
4688
4931
  message: `Created mind: ${name} (port ${port})`,
4689
4932
  ...gitWarning && { warning: gitWarning },
4933
+ ...credentialWarning && { credentialWarning },
4690
4934
  ...skillWarnings.length > 0 && { skillWarnings }
4691
4935
  });
4692
4936
  } catch (err) {
4693
- if (existsSync7(dest)) rmSync4(dest, { recursive: true, force: true });
4937
+ if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
4694
4938
  try {
4695
4939
  await removeMind(name);
4696
4940
  } catch {
@@ -4710,13 +4954,13 @@ Your creator described you as: "${body.description}"
4710
4954
  return importFromArchive(c, body.archivePath, body.name, body.manifest);
4711
4955
  }
4712
4956
  const wsDir = body.workspacePath;
4713
- if (!wsDir || !existsSync7(resolve10(wsDir, "SOUL.md")) || !existsSync7(resolve10(wsDir, "IDENTITY.md"))) {
4957
+ if (!wsDir || !existsSync8(resolve11(wsDir, "SOUL.md")) || !existsSync8(resolve11(wsDir, "IDENTITY.md"))) {
4714
4958
  return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
4715
4959
  }
4716
- const soul = readFileSync8(resolve10(wsDir, "SOUL.md"), "utf-8");
4717
- const identity = readFileSync8(resolve10(wsDir, "IDENTITY.md"), "utf-8");
4718
- const userPath = resolve10(wsDir, "USER.md");
4719
- const user = existsSync7(userPath) ? readFileSync8(userPath, "utf-8") : "";
4960
+ const soul = readFileSync9(resolve11(wsDir, "SOUL.md"), "utf-8");
4961
+ const identity = readFileSync9(resolve11(wsDir, "IDENTITY.md"), "utf-8");
4962
+ const userPath = resolve11(wsDir, "USER.md");
4963
+ const user = existsSync8(userPath) ? readFileSync9(userPath, "utf-8") : "";
4720
4964
  const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
4721
4965
  const template = body.template ?? "claude";
4722
4966
  const nameErr = validateMindName(name);
@@ -4736,33 +4980,33 @@ ${user.trimEnd()}
4736
4980
  ` : "";
4737
4981
  ensureVoluteHome();
4738
4982
  const dest = mindDir(name);
4739
- if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4983
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4740
4984
  const templatesRoot = findTemplatesRoot();
4741
4985
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
4742
4986
  try {
4743
4987
  copyTemplateToDir(composedDir, dest, name, manifest);
4744
4988
  applyInitFiles(dest);
4745
4989
  const { publicKeyPem: importPublicKey } = generateIdentity(dest);
4746
- writeFileSync7(resolve10(dest, "home/SOUL.md"), mergedSoul);
4747
- const wsMemoryPath = resolve10(wsDir, "MEMORY.md");
4748
- const hasMemory = existsSync7(wsMemoryPath);
4990
+ writeFileSync8(resolve11(dest, "home/SOUL.md"), mergedSoul);
4991
+ const wsMemoryPath = resolve11(wsDir, "MEMORY.md");
4992
+ const hasMemory = existsSync8(wsMemoryPath);
4749
4993
  if (hasMemory) {
4750
- const existingMemory = readFileSync8(wsMemoryPath, "utf-8");
4751
- writeFileSync7(
4752
- resolve10(dest, "home/MEMORY.md"),
4994
+ const existingMemory = readFileSync9(wsMemoryPath, "utf-8");
4995
+ writeFileSync8(
4996
+ resolve11(dest, "home/MEMORY.md"),
4753
4997
  `${existingMemory.trimEnd()}${mergedMemoryExtra}`
4754
4998
  );
4755
4999
  } else if (user) {
4756
- writeFileSync7(resolve10(dest, "home/MEMORY.md"), `${user.trimEnd()}
5000
+ writeFileSync8(resolve11(dest, "home/MEMORY.md"), `${user.trimEnd()}
4757
5001
  `);
4758
5002
  }
4759
- const wsMemoryDir = resolve10(wsDir, "memory");
5003
+ const wsMemoryDir = resolve11(wsDir, "memory");
4760
5004
  let dailyLogCount = 0;
4761
- if (existsSync7(wsMemoryDir)) {
4762
- const destMemoryDir = resolve10(dest, "home/memory");
5005
+ if (existsSync8(wsMemoryDir)) {
5006
+ const destMemoryDir = resolve11(dest, "home/memory");
4763
5007
  const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
4764
5008
  for (const file of files) {
4765
- cpSync(resolve10(wsMemoryDir, file), resolve10(destMemoryDir, file));
5009
+ cpSync(resolve11(wsMemoryDir, file), resolve11(destMemoryDir, file));
4766
5010
  }
4767
5011
  dailyLogCount = files.length;
4768
5012
  }
@@ -4773,7 +5017,7 @@ ${user.trimEnd()}
4773
5017
  } catch (err) {
4774
5018
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
4775
5019
  }
4776
- const homeDir = resolve10(dest, "home");
5020
+ const homeDir = resolve11(dest, "home");
4777
5021
  ensureVoluteGroup();
4778
5022
  createMindUser(name, homeDir);
4779
5023
  await chownMindDir(dest, name);
@@ -4781,20 +5025,20 @@ ${user.trimEnd()}
4781
5025
  if (!hasMemory && dailyLogCount > 0) {
4782
5026
  await consolidateMemory(dest);
4783
5027
  }
4784
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve10(dest, "home") } : void 0;
5028
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
4785
5029
  await gitExec(["init"], { cwd: dest, mindName: name, env });
4786
5030
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
4787
5031
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
4788
5032
  await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
4789
- const sessionFile = body.sessionPath ? resolve10(body.sessionPath) : findOpenClawSession(wsDir);
4790
- if (sessionFile && existsSync7(sessionFile)) {
5033
+ const sessionFile = body.sessionPath ? resolve11(body.sessionPath) : findOpenClawSession(wsDir);
5034
+ if (sessionFile && existsSync8(sessionFile)) {
4791
5035
  if (template === "pi") {
4792
5036
  importPiSession(sessionFile, dest);
4793
5037
  } else if (template === "claude") {
4794
5038
  const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
4795
- const mindRuntimeDir = resolve10(dest, ".mind");
5039
+ const mindRuntimeDir = resolve11(dest, ".mind");
4796
5040
  mkdirSync6(mindRuntimeDir, { recursive: true });
4797
- writeFileSync7(resolve10(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
5041
+ writeFileSync8(resolve11(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
4798
5042
  }
4799
5043
  }
4800
5044
  importOpenClawConnectors(name, dest);
@@ -4804,7 +5048,7 @@ ${user.trimEnd()}
4804
5048
  );
4805
5049
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
4806
5050
  } catch (err) {
4807
- if (existsSync7(dest)) rmSync4(dest, { recursive: true, force: true });
5051
+ if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
4808
5052
  try {
4809
5053
  await removeMind(name);
4810
5054
  } catch {
@@ -4832,15 +5076,19 @@ ${user.trimEnd()}
4832
5076
  } catch {
4833
5077
  }
4834
5078
  }
5079
+ const privileged = isPrivileged(c);
4835
5080
  const minds = await Promise.all(
4836
5081
  entries.map(async (entry) => {
4837
5082
  const mindStatus = await getMindStatus(entry.name, entry.port, entry.running);
4838
- const hasPages = existsSync7(resolve10(mindDir(entry.name), "home", "pages"));
5083
+ const hasPages = existsSync8(resolve11(mindDir(entry.name), "home", "pages"));
5084
+ const lastActiveAt = lastActiveMap.get(entry.name) ?? null;
5085
+ if (!privileged) return toPublicMind(entry, mindStatus, { hasPages, lastActiveAt });
4839
5086
  return {
4840
5087
  ...entry,
4841
5088
  ...mindStatus,
4842
5089
  hasPages,
4843
- lastActiveAt: lastActiveMap.get(entry.name) ?? null
5090
+ templateStale: isTemplateStale(entry),
5091
+ lastActiveAt
4844
5092
  };
4845
5093
  })
4846
5094
  );
@@ -4850,8 +5098,12 @@ ${user.trimEnd()}
4850
5098
  const entry = await findMind(name);
4851
5099
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4852
5100
  const dir = entry.dir ?? mindDir(entry.parent ?? name);
4853
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
5101
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
4854
5102
  const mindStatus = await getMindStatus(name, entry.port);
5103
+ const hasPages = existsSync8(resolve11(mindDir(name), "home", "pages"));
5104
+ if (!isPrivileged(c)) {
5105
+ return c.json(toPublicMind(entry, mindStatus, { hasPages }));
5106
+ }
4855
5107
  const variants = await findVariants(name);
4856
5108
  const manager = getMindManager();
4857
5109
  const variantStatuses = await Promise.all(
@@ -4864,8 +5116,22 @@ ${user.trimEnd()}
4864
5116
  return { name: s.name, port: s.port, status: variantStatus };
4865
5117
  })
4866
5118
  );
4867
- const hasPages = existsSync7(resolve10(mindDir(name), "home", "pages"));
4868
- return c.json({ ...entry, ...mindStatus, variants: variantStatuses, hasPages });
5119
+ const notice = await latestNotice(name);
5120
+ return c.json({
5121
+ ...entry,
5122
+ ...mindStatus,
5123
+ variants: variantStatuses,
5124
+ hasPages,
5125
+ templateStale: isTemplateStale(entry),
5126
+ ...notice && {
5127
+ lastNotice: {
5128
+ kind: notice.kind,
5129
+ reason: notice.reason,
5130
+ detail: notice.detail,
5131
+ created_at: notice.created_at
5132
+ }
5133
+ }
5134
+ });
4869
5135
  }).get("/:name/context", requireSelf(), async (c) => proxyToMind(c, "context")).get("/:name/context/messages", requireSelf(), async (c) => proxyToMind(c, "context/messages")).post("/:name/start", requireSelf(), async (c) => {
4870
5136
  const name = c.req.param("name");
4871
5137
  const entry = await findMind(name);
@@ -4875,7 +5141,7 @@ ${user.trimEnd()}
4875
5141
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
4876
5142
  } else {
4877
5143
  const dir = mindDir(name);
4878
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
5144
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
4879
5145
  }
4880
5146
  if (getMindManager().isRunning(name)) {
4881
5147
  return c.json({ error: "Mind already running" }, 409);
@@ -4896,7 +5162,7 @@ ${user.trimEnd()}
4896
5162
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
4897
5163
  } else {
4898
5164
  const dir = mindDir(name);
4899
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
5165
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
4900
5166
  }
4901
5167
  let context;
4902
5168
  const contentType = c.req.header("content-type");
@@ -4911,7 +5177,7 @@ ${user.trimEnd()}
4911
5177
  const manager = getMindManager();
4912
5178
  try {
4913
5179
  if (context?.type === "reload") {
4914
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
5180
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-Z27R3WTW.js");
4915
5181
  const sleepState = getSleepManagerIfReady2()?.getState(name);
4916
5182
  if (sleepState?.sleeping) {
4917
5183
  logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
@@ -4931,7 +5197,7 @@ ${user.trimEnd()}
4931
5197
  const variantEntry = await findMind(mergeVariantName);
4932
5198
  if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
4933
5199
  const projectRoot = mindDir(baseName);
4934
- if (existsSync7(variantEntry.dir)) {
5200
+ if (existsSync8(variantEntry.dir)) {
4935
5201
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
4936
5202
  if (status) {
4937
5203
  try {
@@ -5038,7 +5304,7 @@ ${user.trimEnd()}
5038
5304
  const name = c.req.param("name");
5039
5305
  const entry = await findMind(name);
5040
5306
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5041
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
5307
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-Z27R3WTW.js");
5042
5308
  const sm = getSleepManagerIfReady2();
5043
5309
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
5044
5310
  return c.json(sm.getState(name));
@@ -5046,7 +5312,7 @@ ${user.trimEnd()}
5046
5312
  const name = c.req.param("name");
5047
5313
  const entry = await findMind(name);
5048
5314
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5049
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
5315
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-Z27R3WTW.js");
5050
5316
  const sm = getSleepManagerIfReady2();
5051
5317
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
5052
5318
  if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
@@ -5066,7 +5332,7 @@ ${user.trimEnd()}
5066
5332
  const name = c.req.param("name");
5067
5333
  const entry = await findMind(name);
5068
5334
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5069
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
5335
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-Z27R3WTW.js");
5070
5336
  const sm = getSleepManagerIfReady2();
5071
5337
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
5072
5338
  const sleepState = sm.getState(name);
@@ -5081,7 +5347,7 @@ ${user.trimEnd()}
5081
5347
  const name = c.req.param("name");
5082
5348
  const entry = await findMind(name);
5083
5349
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5084
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
5350
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-Z27R3WTW.js");
5085
5351
  const sm = getSleepManagerIfReady2();
5086
5352
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
5087
5353
  const flushed = await sm.flushQueuedMessages(name);
@@ -5097,19 +5363,19 @@ ${user.trimEnd()}
5097
5363
  if (body.displayName !== void 0) profile.displayName = body.displayName;
5098
5364
  if (body.description !== void 0) profile.description = body.description;
5099
5365
  if (body.avatar !== void 0) {
5100
- const homeDir = resolve10(dir, "home");
5366
+ const homeDir = resolve11(dir, "home");
5101
5367
  const avatarPath = safeResolveWithinBase(homeDir, body.avatar);
5102
5368
  if (!avatarPath) {
5103
5369
  return c.json({ error: "Avatar path must be inside the mind's home directory" }, 400);
5104
5370
  }
5105
- if (!existsSync7(avatarPath)) {
5371
+ if (!existsSync8(avatarPath)) {
5106
5372
  return c.json({ error: `Avatar file not found: ${relative(homeDir, avatarPath)}` }, 400);
5107
5373
  }
5108
5374
  profile.avatar = relative(homeDir, avatarPath);
5109
5375
  }
5110
5376
  config2.profile = profile;
5111
5377
  writeVoluteConfig(dir, config2);
5112
- const { syncMindProfile: syncMindProfile2 } = await import("./auth-CCFAU4GQ.js");
5378
+ const { syncMindProfile: syncMindProfile2 } = await import("./auth-A4VGHADI.js");
5113
5379
  await syncMindProfile2(name, profile);
5114
5380
  broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
5115
5381
  return c.json({ ok: true });
@@ -5131,14 +5397,14 @@ ${user.trimEnd()}
5131
5397
  sql3`${mindHistory.sender} != ${name}`,
5132
5398
  sql3`${mindHistory.sender} IS NOT NULL`
5133
5399
  )
5134
- ).orderBy(desc4(mindHistory.created_at)).limit(1);
5400
+ ).orderBy(desc3(mindHistory.created_at)).limit(1);
5135
5401
  const lastSpiritMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
5136
5402
  and5(
5137
5403
  eq5(mindHistory.mind, name),
5138
5404
  eq5(mindHistory.type, "inbound"),
5139
5405
  eq5(mindHistory.sender, "volute")
5140
5406
  )
5141
- ).orderBy(desc4(mindHistory.created_at)).limit(1);
5407
+ ).orderBy(desc3(mindHistory.created_at)).limit(1);
5142
5408
  const now = Date.now();
5143
5409
  const creatorTime = lastCreatorMsg[0] ? new Date(lastCreatorMsg[0].created_at).getTime() : 0;
5144
5410
  const spiritTime = lastSpiritMsg[0] ? new Date(lastSpiritMsg[0].created_at).getTime() : 0;
@@ -5148,25 +5414,18 @@ ${user.trimEnd()}
5148
5414
  return c.json({ output: "" });
5149
5415
  }
5150
5416
  const dir = entry.dir ?? mindDir(name);
5151
- const soulPath = resolve10(dir, "home/SOUL.md");
5152
- const memoryPath = resolve10(dir, "home/MEMORY.md");
5153
- const soulCustom = existsSync7(soulPath) && !readFileSync8(soulPath, "utf-8").includes(ORIENTATION_MARKER);
5154
- const memoryWritten = existsSync7(memoryPath) && readFileSync8(memoryPath, "utf-8").trim().length > 0;
5155
- const config2 = readVoluteConfig(dir);
5156
- const displayNameSet = !!config2?.profile?.displayName;
5157
- const avatarSet = !!config2?.profile?.avatar;
5158
- const { isImagegenEnabled } = await import("./setup-Y2EJI3GG.js");
5159
- const imagegenEnabled = isImagegenEnabled();
5417
+ const { evaluateSeedChecklist } = await import("./seed-readiness-RBPGOLSZ.js");
5418
+ const checklist = evaluateSeedChecklist(dir);
5160
5419
  const done = [];
5161
5420
  const remaining = [];
5162
- if (soulCustom) done.push("SOUL.md written");
5421
+ if (checklist.soulWritten) done.push("SOUL.md written");
5163
5422
  else remaining.push("Write SOUL.md");
5164
- if (memoryWritten) done.push("MEMORY.md written");
5423
+ if (checklist.memoryWritten) done.push("MEMORY.md written");
5165
5424
  else remaining.push("Write MEMORY.md");
5166
- if (displayNameSet) done.push("Display name set");
5425
+ if (checklist.displayNameSet) done.push("Display name set");
5167
5426
  else remaining.push("Set display name");
5168
- if (imagegenEnabled) {
5169
- if (avatarSet) done.push("Avatar set");
5427
+ if (checklist.imagegenEnabled) {
5428
+ if (checklist.avatarSet) done.push("Avatar set");
5170
5429
  else remaining.push("Generate and set avatar");
5171
5430
  }
5172
5431
  const creatorStatus = minutesSinceCreator === Infinity ? "No creator messages yet" : `Last creator message: ${Math.round(minutesSinceCreator)} minutes ago`;
@@ -5190,23 +5449,48 @@ ${user.trimEnd()}
5190
5449
  return c.json({ error: `Mind is not a seed (stage: ${entry.stage})` }, 409);
5191
5450
  }
5192
5451
  await setMindStage(name, "sprouted");
5452
+ try {
5453
+ const sproutedDir = entry.dir ?? mindDir(name);
5454
+ if (setupDefaultDreaming(sproutedDir).schedulesChanged) {
5455
+ const { getScheduler: getScheduler2 } = await import("./scheduler-I7JZCT47.js");
5456
+ getScheduler2().loadSchedules(name, sproutedDir);
5457
+ }
5458
+ } catch (err) {
5459
+ logger_default.warn(`failed to set up default dreaming for ${name}`, logger_default.errorData(err));
5460
+ }
5193
5461
  try {
5194
5462
  const spiritEntry = await findMind("volute");
5195
5463
  if (spiritEntry) {
5196
- const { spiritDir } = await import("./spirit-RPBLLUQ5.js");
5464
+ const { firstWeekSchedules, spiritDir } = await import("./spirit-WXS3HCLN.js");
5197
5465
  const sDir = spiritEntry.dir ?? spiritDir();
5198
5466
  const spiritConfig = readVoluteConfig(sDir);
5199
- if (spiritConfig?.schedules) {
5200
- const nurtureId = `nurture-${name}`;
5201
- spiritConfig.schedules = spiritConfig.schedules.filter((s) => s.id !== nurtureId);
5202
- if (spiritConfig.schedules.length === 0) spiritConfig.schedules = void 0;
5467
+ if (spiritConfig) {
5468
+ const schedules = (spiritConfig.schedules ?? []).filter(
5469
+ (s) => s.id !== `nurture-${name}`
5470
+ );
5471
+ const existing = new Set(schedules.map((s) => s.id));
5472
+ schedules.push(
5473
+ ...firstWeekSchedules(name, /* @__PURE__ */ new Date()).filter((s) => !existing.has(s.id))
5474
+ );
5475
+ spiritConfig.schedules = schedules;
5203
5476
  writeVoluteConfig(sDir, spiritConfig);
5204
- const { getScheduler: getScheduler2 } = await import("./scheduler-WVKII3LA.js");
5205
- getScheduler2().loadSchedules("volute", sDir);
5477
+ try {
5478
+ const { getScheduler: getScheduler2 } = await import("./scheduler-I7JZCT47.js");
5479
+ getScheduler2().loadSchedules("volute", sDir);
5480
+ } catch (err) {
5481
+ logger_default.warn(
5482
+ `spirit schedules for sprout of ${name} written to disk but not reloaded into the running scheduler (effective on next spirit restart)`,
5483
+ logger_default.errorData(err)
5484
+ );
5485
+ }
5486
+ } else {
5487
+ logger_default.warn(
5488
+ `spirit config at ${sDir} missing or unparseable \u2014 first-week arc for ${name} not scheduled, nurture-${name} not removed`
5489
+ );
5206
5490
  }
5207
5491
  }
5208
5492
  } catch (err) {
5209
- logger_default.warn(`failed to clean up nurture schedule for ${name}`, logger_default.errorData(err));
5493
+ logger_default.warn(`failed to update spirit schedules for sprout of ${name}`, logger_default.errorData(err));
5210
5494
  }
5211
5495
  return c.json({ ok: true });
5212
5496
  }).delete("/:name", requireAdmin, async (c) => {
@@ -5229,10 +5513,10 @@ ${user.trimEnd()}
5229
5513
  await deleteMindUser2(name);
5230
5514
  invalidateMindUserCache(name);
5231
5515
  const state = stateDir(name);
5232
- if (existsSync7(state)) {
5516
+ if (existsSync8(state)) {
5233
5517
  rmSync4(state, { recursive: true, force: true });
5234
5518
  }
5235
- if (force && existsSync7(dir)) {
5519
+ if (force && existsSync8(dir)) {
5236
5520
  rmSync4(dir, { recursive: true, force: true });
5237
5521
  deleteMindUser(name);
5238
5522
  }
@@ -5247,7 +5531,7 @@ ${user.trimEnd()}
5247
5531
  const entry = await findMind(mindName);
5248
5532
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5249
5533
  const dir = mindDir(mindName);
5250
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
5534
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
5251
5535
  let body = {};
5252
5536
  try {
5253
5537
  body = await c.req.json();
@@ -5260,16 +5544,16 @@ ${user.trimEnd()}
5260
5544
  }
5261
5545
  const UPGRADE_BRANCH = "upgrade";
5262
5546
  const upgradeVariantName = `${mindName}-upgrade`;
5263
- const worktreeDir = resolve10(dir, ".variants", UPGRADE_BRANCH);
5547
+ const worktreeDir = resolve11(dir, ".variants", UPGRADE_BRANCH);
5264
5548
  if (body.abort) {
5265
- if (!existsSync7(worktreeDir)) {
5549
+ if (!existsSync8(worktreeDir)) {
5266
5550
  return c.json({ error: "No upgrade in progress" }, 400);
5267
5551
  }
5268
5552
  try {
5269
5553
  try {
5270
- const gitDirContent = readFileSync8(resolve10(worktreeDir, ".git"), "utf-8").trim();
5554
+ const gitDirContent = readFileSync9(resolve11(worktreeDir, ".git"), "utf-8").trim();
5271
5555
  const gitDir = gitDirContent.replace("gitdir: ", "");
5272
- if (existsSync7(resolve10(gitDir, "MERGE_HEAD"))) {
5556
+ if (existsSync8(resolve11(gitDir, "MERGE_HEAD"))) {
5273
5557
  await gitExec(["merge", "--abort"], { cwd: worktreeDir });
5274
5558
  }
5275
5559
  } catch {
@@ -5288,7 +5572,7 @@ ${user.trimEnd()}
5288
5572
  }
5289
5573
  }
5290
5574
  if (body.continue) {
5291
- if (!existsSync7(worktreeDir)) {
5575
+ if (!existsSync8(worktreeDir)) {
5292
5576
  return c.json({ error: "No upgrade in progress" }, 400);
5293
5577
  }
5294
5578
  const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
@@ -5338,7 +5622,7 @@ ${user.trimEnd()}
5338
5622
  }
5339
5623
  }
5340
5624
  if (body.accept) {
5341
- if (existsSync7(worktreeDir)) {
5625
+ if (existsSync8(worktreeDir)) {
5342
5626
  try {
5343
5627
  await cleanupVariant(upgradeVariantName, dir, worktreeDir, { stop: true });
5344
5628
  } catch (err) {
@@ -5353,7 +5637,7 @@ ${user.trimEnd()}
5353
5637
  }
5354
5638
  if (body.diff) {
5355
5639
  try {
5356
- if (!existsSync7(resolve10(dir, ".git"))) {
5640
+ if (!existsSync8(resolve11(dir, ".git"))) {
5357
5641
  return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
5358
5642
  }
5359
5643
  await updateTemplateBranch(dir, template, mindName);
@@ -5371,22 +5655,22 @@ ${user.trimEnd()}
5371
5655
  );
5372
5656
  }
5373
5657
  }
5374
- if (existsSync7(worktreeDir)) {
5658
+ if (existsSync8(worktreeDir)) {
5375
5659
  return c.json(
5376
5660
  { error: "Upgrade variant already exists. Use continue or delete it first." },
5377
5661
  409
5378
5662
  );
5379
5663
  }
5380
- if (!existsSync7(resolve10(dir, ".git"))) {
5664
+ if (!existsSync8(resolve11(dir, ".git"))) {
5381
5665
  try {
5382
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve10(dir, "home") } : void 0;
5666
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dir, "home") } : void 0;
5383
5667
  await gitExec(["init"], { cwd: dir, mindName, env });
5384
5668
  await configureGitIdentity(mindName, { cwd: dir, mindName, env });
5385
5669
  await gitExec(["add", "-A"], { cwd: dir, mindName, env });
5386
5670
  await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
5387
5671
  await chownMindDir(dir, mindName);
5388
5672
  } catch (err) {
5389
- rmSync4(resolve10(dir, ".git"), { recursive: true, force: true });
5673
+ rmSync4(resolve11(dir, ".git"), { recursive: true, force: true });
5390
5674
  return c.json(
5391
5675
  {
5392
5676
  error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
@@ -5401,8 +5685,8 @@ ${user.trimEnd()}
5401
5685
  } catch {
5402
5686
  }
5403
5687
  await updateTemplateBranch(dir, template, mindName);
5404
- const parentDir = resolve10(dir, ".variants");
5405
- if (!existsSync7(parentDir)) {
5688
+ const parentDir = resolve11(dir, ".variants");
5689
+ if (!existsSync8(parentDir)) {
5406
5690
  mkdirSync6(parentDir, { recursive: true });
5407
5691
  }
5408
5692
  await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
@@ -5532,22 +5816,22 @@ ${user.trimEnd()}
5532
5816
  const entry = await findMind(name);
5533
5817
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5534
5818
  const dir = entry.dir ?? mindDir(name);
5535
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
5819
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
5536
5820
  let config2 = readVoluteConfig(dir);
5537
5821
  if (!config2 && entry.template === "pi") {
5538
- const piConfigPath = resolve10(dir, "home/.config/config.json");
5539
- if (existsSync7(piConfigPath)) {
5822
+ const piConfigPath = resolve11(dir, "home/.config/config.json");
5823
+ if (existsSync8(piConfigPath)) {
5540
5824
  try {
5541
- config2 = JSON.parse(readFileSync8(piConfigPath, "utf-8"));
5825
+ config2 = JSON.parse(readFileSync9(piConfigPath, "utf-8"));
5542
5826
  } catch {
5543
5827
  }
5544
5828
  }
5545
5829
  }
5546
5830
  let templateConfig = {};
5547
- const configJsonPath = resolve10(dir, "home/.config/config.json");
5548
- if (existsSync7(configJsonPath)) {
5831
+ const configJsonPath = resolve11(dir, "home/.config/config.json");
5832
+ if (existsSync8(configJsonPath)) {
5549
5833
  try {
5550
- templateConfig = JSON.parse(readFileSync8(configJsonPath, "utf-8"));
5834
+ templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
5551
5835
  } catch {
5552
5836
  }
5553
5837
  }
@@ -5561,22 +5845,7 @@ ${user.trimEnd()}
5561
5845
  },
5562
5846
  config: {
5563
5847
  model: config2?.model ?? templateConfig.model ?? null,
5564
- thinkingLevel: (() => {
5565
- if (config2?.thinkingLevel) return config2.thinkingLevel;
5566
- const tc = templateConfig;
5567
- if (tc.thinkingLevel) return tc.thinkingLevel;
5568
- if (tc.reasoningEffort) return tc.reasoningEffort;
5569
- const mtt = tc.maxThinkingTokens;
5570
- if (mtt) {
5571
- if (mtt <= 1024) return "minimal";
5572
- if (mtt <= 4096) return "low";
5573
- if (mtt <= 1e4) return "medium";
5574
- if (mtt <= 32e3) return "high";
5575
- return "xhigh";
5576
- }
5577
- return null;
5578
- })(),
5579
- maxThinkingTokens: config2?.maxThinkingTokens ?? templateConfig.maxThinkingTokens ?? null,
5848
+ thinkingLevel: config2?.thinkingLevel ?? deriveThinkingLevel(templateConfig),
5580
5849
  tokenBudget: config2?.tokenBudget ?? null,
5581
5850
  tokenBudgetPeriodMinutes: config2?.tokenBudgetPeriodMinutes ?? null,
5582
5851
  compaction: templateConfig.compaction ?? null,
@@ -5591,7 +5860,6 @@ ${user.trimEnd()}
5591
5860
  z6.object({
5592
5861
  model: z6.string().optional(),
5593
5862
  thinkingLevel: z6.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
5594
- maxThinkingTokens: z6.number().int().positive().nullable().optional(),
5595
5863
  tokenBudget: z6.number().int().positive().nullable().optional(),
5596
5864
  tokenBudgetPeriodMinutes: z6.number().int().positive().nullable().optional(),
5597
5865
  compaction: z6.object({ maxContextTokens: z6.number().int().positive().nullable().optional() }).nullable().optional(),
@@ -5603,7 +5871,7 @@ ${user.trimEnd()}
5603
5871
  const entry = await findMind(name);
5604
5872
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5605
5873
  const dir = entry.dir ?? mindDir(name);
5606
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
5874
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
5607
5875
  const body = c.req.valid("json");
5608
5876
  const existing = readVoluteConfig(dir) ?? {};
5609
5877
  if (body.model !== void 0) existing.model = body.model;
@@ -5624,76 +5892,29 @@ ${user.trimEnd()}
5624
5892
  existing.tokenBudgetPeriodMinutes = body.tokenBudgetPeriodMinutes;
5625
5893
  }
5626
5894
  }
5627
- if (body.maxThinkingTokens !== void 0) {
5628
- if (body.maxThinkingTokens === null) {
5629
- delete existing.maxThinkingTokens;
5630
- } else {
5631
- existing.maxThinkingTokens = body.maxThinkingTokens;
5632
- }
5633
- }
5634
5895
  if (body.unescapeNewlines !== void 0) {
5635
5896
  existing.unescapeNewlines = body.unescapeNewlines;
5636
5897
  }
5637
5898
  writeVoluteConfig(dir, existing);
5638
5899
  if (body.unescapeNewlines !== void 0) {
5639
- const { clearEchoTextCache } = await import("./echo-text-VQPB5L2X.js");
5900
+ const { clearEchoTextCache } = await import("./echo-text-H3SH337Q.js");
5640
5901
  clearEchoTextCache(name);
5641
5902
  }
5642
- const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.maxThinkingTokens !== void 0 || body.compaction !== void 0;
5903
+ const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.compaction !== void 0;
5643
5904
  if (needsConfigJson) {
5644
- const configJsonPath = resolve10(dir, "home/.config/config.json");
5905
+ const configJsonPath = resolve11(dir, "home/.config/config.json");
5645
5906
  let templateConfig = {};
5646
- if (existsSync7(configJsonPath)) {
5907
+ if (existsSync8(configJsonPath)) {
5647
5908
  try {
5648
- templateConfig = JSON.parse(readFileSync8(configJsonPath, "utf-8"));
5909
+ templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
5649
5910
  } catch {
5650
5911
  }
5651
5912
  }
5652
5913
  if (body.model !== void 0) {
5653
5914
  templateConfig.model = body.model;
5654
5915
  }
5655
- const tmpl = entry.template ?? "claude";
5656
5916
  if (body.thinkingLevel !== void 0) {
5657
- if (tmpl === "claude") {
5658
- const claudeThinkingTokens = {
5659
- off: null,
5660
- minimal: 1024,
5661
- low: 4096,
5662
- medium: 1e4,
5663
- high: 32e3,
5664
- xhigh: 128e3
5665
- };
5666
- const tokens = claudeThinkingTokens[body.thinkingLevel] ?? null;
5667
- if (tokens === null) {
5668
- delete templateConfig.maxThinkingTokens;
5669
- } else {
5670
- templateConfig.maxThinkingTokens = tokens;
5671
- }
5672
- } else if (tmpl === "codex") {
5673
- const codexMap = {
5674
- off: null,
5675
- minimal: "minimal",
5676
- low: "low",
5677
- medium: "medium",
5678
- high: "high",
5679
- xhigh: "xhigh"
5680
- };
5681
- const effort = codexMap[body.thinkingLevel] ?? null;
5682
- if (effort === null) {
5683
- delete templateConfig.reasoningEffort;
5684
- } else {
5685
- templateConfig.reasoningEffort = effort;
5686
- }
5687
- } else {
5688
- templateConfig.thinkingLevel = body.thinkingLevel;
5689
- }
5690
- }
5691
- if (body.maxThinkingTokens !== void 0) {
5692
- if (body.maxThinkingTokens === null) {
5693
- delete templateConfig.maxThinkingTokens;
5694
- } else {
5695
- templateConfig.maxThinkingTokens = body.maxThinkingTokens;
5696
- }
5917
+ applyThinkingLevel(templateConfig, entry.template ?? "claude", body.thinkingLevel);
5697
5918
  }
5698
5919
  if (body.compaction !== void 0) {
5699
5920
  if (body.compaction === null) {
@@ -5708,12 +5929,12 @@ ${user.trimEnd()}
5708
5929
  templateConfig.compaction = comp;
5709
5930
  }
5710
5931
  }
5711
- writeFileSync7(configJsonPath, `${JSON.stringify(templateConfig, null, 2)}
5932
+ writeFileSync8(configJsonPath, `${JSON.stringify(templateConfig, null, 2)}
5712
5933
  `);
5713
5934
  }
5714
5935
  if (entry.mindType === "spirit" && body.model !== void 0) {
5715
5936
  try {
5716
- const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-Y2EJI3GG.js");
5937
+ const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-5ECWTYKG.js");
5717
5938
  const globalConfig = readGlobalConfig2();
5718
5939
  globalConfig.spiritModel = body.model;
5719
5940
  writeGlobalConfig2(globalConfig);
@@ -5736,12 +5957,27 @@ ${user.trimEnd()}
5736
5957
  logger_default.error(`failed to get pending deliveries for ${baseName}`, logger_default.errorData(err));
5737
5958
  return c.json({ error: "Failed to retrieve pending messages" }, 500);
5738
5959
  }
5960
+ }).post("/:name/gates/decline", requireSelf(), async (c) => {
5961
+ const name = c.req.param("name");
5962
+ const body = await c.req.json().catch(() => ({}));
5963
+ const channel = body.channel?.trim();
5964
+ if (!channel) return c.json({ error: "channel required" }, 400);
5965
+ try {
5966
+ const archived = await getDeliveryManager().declineChannel(name, channel);
5967
+ return c.json({ ok: true, channel, archived });
5968
+ } catch (err) {
5969
+ if (err instanceof Error && err.message.includes("not initialized")) {
5970
+ return c.json({ error: "Delivery manager not available" }, 503);
5971
+ }
5972
+ logger_default.error(`failed to decline channel ${channel} for ${name}`, logger_default.errorData(err));
5973
+ return c.json({ error: "Failed to decline channel" }, 500);
5974
+ }
5739
5975
  }).post("/:name/ai/complete", requireSelf(), async (c) => {
5740
5976
  const body = await c.req.json();
5741
5977
  if (!body.systemPrompt || !body.message) {
5742
5978
  return c.json({ error: "systemPrompt and message required" }, 400);
5743
5979
  }
5744
- const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-UODCCE4O.js");
5980
+ const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-643TNFPH.js");
5745
5981
  if (!isAiConfigured()) {
5746
5982
  return c.json({ error: "AI service not configured" }, 503);
5747
5983
  }
@@ -5862,6 +6098,38 @@ ${user.trimEnd()}
5862
6098
  const db = await getDb();
5863
6099
  const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq5(mindHistory.mind, name));
5864
6100
  return c.json(rows.map((r) => r.channel));
6101
+ }).get("/:name/history/contacts", requireSelf(), async (c) => {
6102
+ const name = c.req.param("name");
6103
+ const rawHours = parseInt(c.req.query("hours") ?? "48", 10);
6104
+ const hours = Math.min(Math.max(Number.isNaN(rawHours) ? 48 : rawHours, 1), 168);
6105
+ const cutoff = new Date(Date.now() - hours * 36e5).toISOString().replace("T", " ").slice(0, 19);
6106
+ const db = await getDb();
6107
+ const contacts = await db.select({
6108
+ channel: mindHistory.channel,
6109
+ last_at: sql3`MAX(${mindHistory.created_at})`,
6110
+ last_inbound_at: sql3`MAX(CASE WHEN ${mindHistory.type} = 'inbound' THEN ${mindHistory.created_at} END)`,
6111
+ last_outbound_at: sql3`MAX(CASE WHEN ${mindHistory.type} = 'outbound' THEN ${mindHistory.created_at} END)`,
6112
+ message_count: sql3`COUNT(*)`,
6113
+ // The counterparty: most recent inbound sender on this channel. For a
6114
+ // DM the channel name already carries it (e.g. @atlas), but this also
6115
+ // covers shared channels (#system) and rows where the channel is opaque.
6116
+ last_sender: sql3`(
6117
+ SELECT sender FROM mind_history h2
6118
+ WHERE h2.mind = ${name}
6119
+ AND h2.channel = ${mindHistory.channel}
6120
+ AND h2.type = 'inbound'
6121
+ AND h2.sender IS NOT NULL
6122
+ ORDER BY h2.created_at DESC, h2.id DESC LIMIT 1
6123
+ )`
6124
+ }).from(mindHistory).where(
6125
+ and5(
6126
+ eq5(mindHistory.mind, name),
6127
+ sql3`${mindHistory.type} IN ('inbound','outbound')`,
6128
+ sql3`${mindHistory.channel} IS NOT NULL`,
6129
+ sql3`${mindHistory.created_at} >= ${cutoff}`
6130
+ )
6131
+ ).groupBy(mindHistory.channel).orderBy(sql3`MAX(${mindHistory.created_at}) DESC`);
6132
+ return c.json({ hours, contacts });
5865
6133
  }).get("/:name/history/export", requireSelf(), async (c) => {
5866
6134
  const name = c.req.param("name");
5867
6135
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
@@ -5907,7 +6175,7 @@ ${user.trimEnd()}
5907
6175
  eq5(mindHistory.session, currentSession),
5908
6176
  sql3`${mindHistory.turn_id} IS NOT NULL`
5909
6177
  )
5910
- ).orderBy(desc4(mindHistory.created_at)).limit(1);
6178
+ ).orderBy(desc3(mindHistory.created_at)).limit(1);
5911
6179
  if (lastTurn.length > 0 && lastTurn[0].turn_id) {
5912
6180
  const firstEvent = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(eq5(mindHistory.turn_id, lastTurn[0].turn_id)).orderBy(mindHistory.created_at).limit(1);
5913
6181
  if (firstEvent.length > 0) {
@@ -5930,7 +6198,7 @@ ${user.trimEnd()}
5930
6198
  session: turns.session,
5931
6199
  content: summaries.content,
5932
6200
  created_at: summaries.created_at
5933
- }).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and5(...conditions)).orderBy(desc4(summaries.created_at)).limit(50);
6201
+ }).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and5(...conditions)).orderBy(desc3(summaries.created_at)).limit(50);
5934
6202
  if (rows.length === 0) {
5935
6203
  return c.json({ context: null });
5936
6204
  }
@@ -5981,7 +6249,7 @@ ${lines.join("\n")}` });
5981
6249
  metadata: summaries.metadata,
5982
6250
  created_at: summaries.created_at,
5983
6251
  session: turns.session
5984
- }).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and5(...sumConditions)).orderBy(desc4(summaries.created_at)).limit(limit).offset(offset);
6252
+ }).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and5(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
5985
6253
  return c.json(
5986
6254
  sumRows2.map((r) => ({
5987
6255
  id: r.id,
@@ -5998,7 +6266,7 @@ ${lines.join("\n")}` });
5998
6266
  }))
5999
6267
  );
6000
6268
  }
6001
- const sumRows = await db.select().from(summaries).where(and5(...sumConditions)).orderBy(desc4(summaries.created_at)).limit(limit).offset(offset);
6269
+ const sumRows = await db.select().from(summaries).where(and5(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
6002
6270
  return c.json(
6003
6271
  sumRows.map((r) => ({
6004
6272
  id: r.id,
@@ -6027,7 +6295,7 @@ ${lines.join("\n")}` });
6027
6295
  );
6028
6296
  break;
6029
6297
  }
6030
- const rows = await db.select().from(mindHistory).where(and5(...conditions)).orderBy(desc4(mindHistory.created_at)).limit(limit).offset(offset);
6298
+ const rows = await db.select().from(mindHistory).where(and5(...conditions)).orderBy(desc3(mindHistory.created_at)).limit(limit).offset(offset);
6031
6299
  return c.json(rows);
6032
6300
  });
6033
6301
  var minds_default = app13;
@@ -6090,6 +6358,13 @@ var slog2 = logger_default.child("schedules");
6090
6358
  function readSchedules(dir) {
6091
6359
  return readVoluteConfig(dir)?.schedules ?? [];
6092
6360
  }
6361
+ function validateMessages(messages) {
6362
+ if (messages === void 0) return null;
6363
+ if (!Array.isArray(messages) || messages.some((m) => typeof m !== "string" || !m.trim())) {
6364
+ return "messages must be an array of non-empty strings";
6365
+ }
6366
+ return null;
6367
+ }
6093
6368
  var UPCOMING_HORIZON_MS = 24 * 60 * 60 * 1e3;
6094
6369
  function computeClockEvents(schedules, sleepState, sleepConfig, now) {
6095
6370
  const upcoming = [];
@@ -6231,11 +6506,13 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
6231
6506
  if (body.cron && body.fireAt) {
6232
6507
  return c.json({ error: "cron and fireAt are mutually exclusive" }, 400);
6233
6508
  }
6234
- if (!body.message && !body.script) {
6235
- return c.json({ error: "message or script is required" }, 400);
6509
+ const messagesErr = validateMessages(body.messages);
6510
+ if (messagesErr) return c.json({ error: messagesErr }, 400);
6511
+ if (!body.message && !body.messages?.length && !body.script) {
6512
+ return c.json({ error: "message, messages, or script is required" }, 400);
6236
6513
  }
6237
- if (body.message && body.script) {
6238
- return c.json({ error: "message and script are mutually exclusive" }, 400);
6514
+ if ([body.message, body.messages?.length, body.script].filter(Boolean).length > 1) {
6515
+ return c.json({ error: "message, messages, and script are mutually exclusive" }, 400);
6239
6516
  }
6240
6517
  if (body.cron) {
6241
6518
  try {
@@ -6265,6 +6542,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
6265
6542
  if (body.cron) schedule.cron = body.cron;
6266
6543
  if (body.fireAt) schedule.fireAt = body.fireAt;
6267
6544
  if (body.message) schedule.message = body.message;
6545
+ if (body.messages?.length) schedule.messages = body.messages;
6268
6546
  if (body.script) schedule.script = body.script;
6269
6547
  if (body.channel) schedule.channel = body.channel;
6270
6548
  if (body.whileSleeping) schedule.whileSleeping = body.whileSleeping;
@@ -6281,8 +6559,10 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
6281
6559
  const idx = schedules.findIndex((s) => s.id === id);
6282
6560
  if (idx === -1) return c.json({ error: "Schedule not found" }, 404);
6283
6561
  const body = await c.req.json();
6284
- if (body.message && body.script) {
6285
- return c.json({ error: "message and script are mutually exclusive" }, 400);
6562
+ const messagesErr = validateMessages(body.messages);
6563
+ if (messagesErr) return c.json({ error: messagesErr }, 400);
6564
+ if ([body.message, body.messages?.length, body.script].filter(Boolean).length > 1) {
6565
+ return c.json({ error: "message, messages, and script are mutually exclusive" }, 400);
6286
6566
  }
6287
6567
  if (body.cron !== void 0) {
6288
6568
  try {
@@ -6303,10 +6583,21 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
6303
6583
  if (body.message !== void 0) {
6304
6584
  schedules[idx].message = body.message;
6305
6585
  delete schedules[idx].script;
6586
+ delete schedules[idx].messages;
6587
+ }
6588
+ if (body.messages !== void 0) {
6589
+ if (body.messages.length > 0) {
6590
+ schedules[idx].messages = body.messages;
6591
+ delete schedules[idx].message;
6592
+ delete schedules[idx].script;
6593
+ } else {
6594
+ delete schedules[idx].messages;
6595
+ }
6306
6596
  }
6307
6597
  if (body.script !== void 0) {
6308
6598
  schedules[idx].script = body.script;
6309
6599
  delete schedules[idx].message;
6600
+ delete schedules[idx].messages;
6310
6601
  }
6311
6602
  if (body.whileSleeping && !["skip", "queue", "trigger-wake"].includes(body.whileSleeping)) {
6312
6603
  return c.json(
@@ -6320,6 +6611,11 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
6320
6611
  if (body.channel !== void 0) schedules[idx].channel = body.channel || void 0;
6321
6612
  if (body.whileSleeping !== void 0)
6322
6613
  schedules[idx].whileSleeping = body.whileSleeping || void 0;
6614
+ const touchedAction = body.message !== void 0 || body.messages !== void 0 || body.script !== void 0;
6615
+ const result = schedules[idx];
6616
+ if (touchedAction && !result.message && !result.messages?.length && !result.script) {
6617
+ return c.json({ error: "schedule must keep a message, messages, or script" }, 400);
6618
+ }
6323
6619
  writeSchedules(name, dir, schedules);
6324
6620
  return c.json({ ok: true });
6325
6621
  }).delete("/:name/schedules/:id", requireSelf(), async (c) => {
@@ -6343,7 +6639,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
6343
6639
  const body = await c.req.text();
6344
6640
  const message = `[webhook: ${event}] ${body}`;
6345
6641
  try {
6346
- const { sendSystemMessage } = await import("./system-chat-NDAW6YP7.js");
6642
+ const { sendSystemMessage } = await import("./system-chat-63ABFF6V.js");
6347
6643
  await sendSystemMessage(name, message);
6348
6644
  return c.json({ ok: true });
6349
6645
  } catch (err) {
@@ -6356,14 +6652,14 @@ var schedules_default = app15;
6356
6652
  // packages/daemon/src/web/api/setup.ts
6357
6653
  import { mkdirSync as mkdirSync7 } from "fs";
6358
6654
  import { homedir as homedir2 } from "os";
6359
- import { resolve as resolve11 } from "path";
6655
+ import { resolve as resolve12 } from "path";
6360
6656
  import { Hono as Hono19 } from "hono";
6361
6657
  import { setCookie as setCookie2 } from "hono/cookie";
6362
6658
  var DEFAULT_API_URL2 = "https://volute.systems";
6363
6659
  var setup = new Hono19();
6364
6660
  function writeSetupConfig(systemName, description) {
6365
- const configHome = process.env.VOLUTE_HOME ?? resolve11(homedir2(), ".volute");
6366
- const mindsDir = resolve11(configHome, "minds");
6661
+ const configHome = process.env.VOLUTE_HOME ?? resolve12(homedir2(), ".volute");
6662
+ const mindsDir = resolve12(configHome, "minds");
6367
6663
  mkdirSync7(configHome, { recursive: true });
6368
6664
  mkdirSync7(mindsDir, { recursive: true });
6369
6665
  const existingConfig = readGlobalConfig();
@@ -6397,7 +6693,7 @@ setup.get("/status", async (c) => {
6397
6693
  let hasAccount = false;
6398
6694
  if (hasSystem) {
6399
6695
  try {
6400
- const { listUsersByType: listUsersByType2 } = await import("./auth-CCFAU4GQ.js");
6696
+ const { listUsersByType: listUsersByType2 } = await import("./auth-A4VGHADI.js");
6401
6697
  const brains = await listUsersByType2("brain");
6402
6698
  hasAccount = brains.length > 0;
6403
6699
  } catch (err) {
@@ -6608,7 +6904,7 @@ setup.post("/account", async (c) => {
6608
6904
  }
6609
6905
  }
6610
6906
  try {
6611
- const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-CCFAU4GQ.js");
6907
+ const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-A4VGHADI.js");
6612
6908
  const user = await createUser2(body.username.trim(), body.password);
6613
6909
  if (body.displayName?.trim()) {
6614
6910
  await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
@@ -6654,7 +6950,7 @@ setup.post("/models", async (c) => {
6654
6950
  return c.json({ error: "Spirit model is required" }, 400);
6655
6951
  }
6656
6952
  try {
6657
- const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-UODCCE4O.js");
6953
+ const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-643TNFPH.js");
6658
6954
  setEnabledModels3(body.models);
6659
6955
  const config2 = readGlobalConfig();
6660
6956
  config2.spiritModel = body.spiritModel.trim();
@@ -6672,15 +6968,15 @@ setup.post("/complete", async (c) => {
6672
6968
  return c.json({ error: "Setup already complete" }, 400);
6673
6969
  }
6674
6970
  try {
6675
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-RPBLLUQ5.js");
6676
- const { startSpiritFull } = await import("./mind-service-NB2JMWQZ.js");
6971
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-WXS3HCLN.js");
6972
+ const { startSpiritFull } = await import("./mind-service-2ARGJNKP.js");
6677
6973
  await ensureSpiritProject();
6678
6974
  await syncSpiritTemplate();
6679
6975
  const warnings = [];
6680
6976
  const config2 = readGlobalConfig();
6681
6977
  if (config2.setup?.service && config2.setup?.type === "local") {
6682
6978
  try {
6683
- const { installUserService } = await import("./service-install-4HHFDDWY.js");
6979
+ const { installUserService } = await import("./service-install-VCRMFWX5.js");
6684
6980
  const installed = await installUserService(config2.port, config2.hostname);
6685
6981
  if (!installed) {
6686
6982
  warnings.push("Service installation is not supported on this platform.");
@@ -6704,12 +7000,13 @@ setup.post("/complete", async (c) => {
6704
7000
  logger_default.info("spirit started successfully during setup");
6705
7001
  } catch (err) {
6706
7002
  logger_default.warn("spirit start failed during setup (non-fatal)", logger_default.errorData(err));
6707
- warnings.push("Spirit failed to start \u2014 it will retry on next daemon restart.");
7003
+ const reason = err instanceof Error ? err.message : String(err);
7004
+ warnings.push(`The spirit couldn't start: ${reason} \u2014 it will retry on next daemon restart.`);
6708
7005
  }
6709
7006
  let spiritConversationId;
6710
7007
  try {
6711
- const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-CCFAU4GQ.js");
6712
- const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-CJOOCGNW.js");
7008
+ const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-A4VGHADI.js");
7009
+ const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-C7IKYSHD.js");
6713
7010
  const spiritUser = await getOrCreateMindUser2("volute");
6714
7011
  const brains = await listUsersByType2("brain");
6715
7012
  const admin2 = brains.find((u) => u.role === "admin");
@@ -6731,8 +7028,8 @@ setup.post("/complete", async (c) => {
6731
7028
  logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
6732
7029
  if (spiritConversationId && spiritStarted) {
6733
7030
  try {
6734
- const { deliverMessage: deliverMessage2 } = await import("./message-delivery-R7JSQGYX.js");
6735
- const { listUsersByType: listUsers2 } = await import("./auth-CCFAU4GQ.js");
7031
+ const { deliverMessage: deliverMessage2 } = await import("./message-delivery-GPQ5SE5N.js");
7032
+ const { listUsersByType: listUsers2 } = await import("./auth-A4VGHADI.js");
6736
7033
  const admins = await listUsers2("brain");
6737
7034
  const admin2 = admins.find((u) => u.role === "admin");
6738
7035
  if (admin2) {
@@ -6771,9 +7068,9 @@ setup.post("/complete", async (c) => {
6771
7068
  var setup_default = setup;
6772
7069
 
6773
7070
  // packages/daemon/src/web/api/skills.ts
6774
- import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
7071
+ import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
6775
7072
  import { tmpdir } from "os";
6776
- import { join, resolve as resolve12 } from "path";
7073
+ import { join, resolve as resolve13 } from "path";
6777
7074
  import AdmZip from "adm-zip";
6778
7075
  import { Hono as Hono20 } from "hono";
6779
7076
  var app16 = new Hono20().get("/", async (c) => {
@@ -6849,19 +7146,19 @@ var app16 = new Hono20().get("/", async (c) => {
6849
7146
  try {
6850
7147
  const zip = new AdmZip(buffer);
6851
7148
  for (const entry of zip.getEntries()) {
6852
- const target = resolve12(tmpDir, entry.entryName);
7149
+ const target = resolve13(tmpDir, entry.entryName);
6853
7150
  if (!target.startsWith(tmpDir)) {
6854
7151
  return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
6855
7152
  }
6856
7153
  }
6857
7154
  zip.extractAllTo(tmpDir, true);
6858
7155
  let skillDir = null;
6859
- if (existsSync8(join(tmpDir, "SKILL.md"))) {
7156
+ if (existsSync9(join(tmpDir, "SKILL.md"))) {
6860
7157
  skillDir = tmpDir;
6861
7158
  } else {
6862
7159
  const entries = readdirSync3(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
6863
7160
  for (const entry of entries) {
6864
- if (existsSync8(join(tmpDir, entry.name, "SKILL.md"))) {
7161
+ if (existsSync9(join(tmpDir, entry.name, "SKILL.md"))) {
6865
7162
  skillDir = join(tmpDir, entry.name);
6866
7163
  break;
6867
7164
  }
@@ -7053,7 +7350,7 @@ var app19 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
7053
7350
  var conversations_default = app19;
7054
7351
 
7055
7352
  // packages/daemon/src/web/api/v1/events.ts
7056
- import { desc as desc5, eq as eq7 } from "drizzle-orm";
7353
+ import { desc as desc4, eq as eq7 } from "drizzle-orm";
7057
7354
  import { Hono as Hono24 } from "hono";
7058
7355
  import { streamSSE as streamSSE4 } from "hono/streaming";
7059
7356
 
@@ -7120,7 +7417,7 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
7120
7417
  let recentActivity = [];
7121
7418
  try {
7122
7419
  const db = await getDb();
7123
- recentActivity = await db.select().from(activity).where(activityMind ? eq7(activity.mind, activityMind) : void 0).orderBy(desc5(activity.created_at)).limit(50);
7420
+ recentActivity = await db.select().from(activity).where(activityMind ? eq7(activity.mind, activityMind) : void 0).orderBy(desc4(activity.created_at)).limit(50);
7124
7421
  recentActivity = recentActivity.map((row) => ({
7125
7422
  ...row,
7126
7423
  metadata: row.metadata ? JSON.parse(row.metadata) : null
@@ -7175,8 +7472,8 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
7175
7472
  });
7176
7473
  }, 15e3);
7177
7474
  cleanups.push(() => clearInterval(keepAlive));
7178
- await new Promise((resolve17) => {
7179
- stream.onAbort(() => resolve17());
7475
+ await new Promise((resolve18) => {
7476
+ stream.onAbort(() => resolve18());
7180
7477
  });
7181
7478
  } finally {
7182
7479
  for (const cleanup of cleanups) {
@@ -7191,14 +7488,14 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
7191
7488
  var events_default = app20;
7192
7489
 
7193
7490
  // packages/daemon/src/web/api/variants.ts
7194
- import { existsSync as existsSync9, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
7195
- import { resolve as resolve14 } from "path";
7491
+ import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
7492
+ import { resolve as resolve15 } from "path";
7196
7493
  import { Hono as Hono25 } from "hono";
7197
7494
 
7198
7495
  // packages/daemon/src/lib/mind/spawn-server.ts
7199
7496
  import { spawn as spawn4 } from "child_process";
7200
- import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync9 } from "fs";
7201
- import { resolve as resolve13 } from "path";
7497
+ import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync10 } from "fs";
7498
+ import { resolve as resolve14 } from "path";
7202
7499
  async function spawnServer(cwd, port, options) {
7203
7500
  let cmd = process.env.VOLUTE_NODE_PATH ?? process.execPath;
7204
7501
  let args = ["--import", "tsx", "src/server.ts", "--port", String(port)];
@@ -7219,31 +7516,31 @@ function spawnAttached(cmd, args, cwd) {
7219
7516
  cwd,
7220
7517
  stdio: ["ignore", "pipe", "pipe"]
7221
7518
  });
7222
- return new Promise((resolve17) => {
7223
- const timeout = setTimeout(() => resolve17(null), 3e4);
7519
+ return new Promise((resolve18) => {
7520
+ const timeout = setTimeout(() => resolve18(null), 3e4);
7224
7521
  function checkOutput(data) {
7225
7522
  const match = data.toString().match(/listening on :(\d+)/);
7226
7523
  if (match) {
7227
7524
  clearTimeout(timeout);
7228
- resolve17({ child, actualPort: parseInt(match[1], 10) });
7525
+ resolve18({ child, actualPort: parseInt(match[1], 10) });
7229
7526
  }
7230
7527
  }
7231
7528
  child.stdout?.on("data", checkOutput);
7232
7529
  child.stderr?.on("data", checkOutput);
7233
7530
  child.on("error", () => {
7234
7531
  clearTimeout(timeout);
7235
- resolve17(null);
7532
+ resolve18(null);
7236
7533
  });
7237
7534
  child.on("exit", () => {
7238
7535
  clearTimeout(timeout);
7239
- resolve17(null);
7536
+ resolve18(null);
7240
7537
  });
7241
7538
  });
7242
7539
  }
7243
7540
  function spawnDetached(cmd, args, cwd, logDir) {
7244
- const logsDir = logDir ?? resolve13(cwd, ".mind", "logs");
7541
+ const logsDir = logDir ?? resolve14(cwd, ".mind", "logs");
7245
7542
  mkdirSync8(logsDir, { recursive: true });
7246
- const logPath = resolve13(logsDir, "mind.log");
7543
+ const logPath = resolve14(logsDir, "mind.log");
7247
7544
  const logFd = openSync(logPath, "a");
7248
7545
  const child = spawn4(cmd, args, {
7249
7546
  cwd,
@@ -7263,7 +7560,7 @@ function spawnDetached(cmd, args, cwd, logDir) {
7263
7560
  }
7264
7561
  const interval = setInterval(() => {
7265
7562
  try {
7266
- const content = readFileSync9(logPath, "utf-8");
7563
+ const content = readFileSync10(logPath, "utf-8");
7267
7564
  const match = content.match(/listening on :(\d+)/);
7268
7565
  if (match) {
7269
7566
  finish({ child, actualPort: parseInt(match[1], 10) });
@@ -7364,11 +7661,11 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
7364
7661
  return c.json({ error: `Name already in use: ${variantName}` }, 409);
7365
7662
  }
7366
7663
  const projectRoot = entry.dir ?? mindDir(mindName);
7367
- const variantDir = resolve14(projectRoot, ".variants", variantName);
7368
- if (existsSync9(variantDir)) {
7664
+ const variantDir = resolve15(projectRoot, ".variants", variantName);
7665
+ if (existsSync10(variantDir)) {
7369
7666
  return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
7370
7667
  }
7371
- mkdirSync9(resolve14(projectRoot, ".variants"), { recursive: true });
7668
+ mkdirSync9(resolve15(projectRoot, ".variants"), { recursive: true });
7372
7669
  try {
7373
7670
  await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
7374
7671
  } catch (e) {
@@ -7381,7 +7678,7 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
7381
7678
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
7382
7679
  await exec(cmd, args, {
7383
7680
  cwd: variantDir,
7384
- env: { ...process.env, HOME: resolve14(variantDir, "home") }
7681
+ env: { ...process.env, HOME: resolve15(variantDir, "home") }
7385
7682
  });
7386
7683
  } else {
7387
7684
  await exec("npm", ["install"], { cwd: variantDir });
@@ -7391,7 +7688,7 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
7391
7688
  return c.json({ error: `npm install failed: ${msg}` }, 500);
7392
7689
  }
7393
7690
  if (body.soul) {
7394
- writeFileSync8(resolve14(variantDir, "home/SOUL.md"), body.soul);
7691
+ writeFileSync9(resolve15(variantDir, "home/SOUL.md"), body.soul);
7395
7692
  }
7396
7693
  const variantPort = body.port ?? await nextPort();
7397
7694
  await addVariant(variantName, mindName, variantPort, variantDir, variantName);
@@ -7428,7 +7725,7 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
7428
7725
  } catch {
7429
7726
  }
7430
7727
  const projectRoot = parentEntry.dir ?? mindDir(mindName);
7431
- if (existsSync9(variantEntry.dir)) {
7728
+ if (existsSync10(variantEntry.dir)) {
7432
7729
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
7433
7730
  if (status) {
7434
7731
  try {
@@ -7526,8 +7823,8 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
7526
7823
  await chownMindDir(projectRoot, mindName);
7527
7824
  if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
7528
7825
  try {
7529
- const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-446HHH5V.js");
7530
- const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-OUZBZ3Q4.js");
7826
+ const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-GSOUARW4.js");
7827
+ const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-UMMDRPRM.js");
7531
7828
  const tmpl = parentEntry.template ?? "claude";
7532
7829
  await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
7533
7830
  } catch (err) {
@@ -7540,7 +7837,7 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
7540
7837
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
7541
7838
  await exec(cmd, args, {
7542
7839
  cwd: projectRoot,
7543
- env: { ...process.env, HOME: resolve14(projectRoot, "home") }
7840
+ env: { ...process.env, HOME: resolve15(projectRoot, "home") }
7544
7841
  });
7545
7842
  } else {
7546
7843
  await exec("npm", ["install"], { cwd: projectRoot });
@@ -7595,7 +7892,8 @@ var channelSettingsSchema = z10.object({
7595
7892
  private: z10.boolean().optional()
7596
7893
  });
7597
7894
  var createSchema2 = z10.object({
7598
- name: z10.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
7895
+ name: z10.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens"),
7896
+ participantNames: z10.array(z10.string().min(1)).optional()
7599
7897
  }).merge(channelSettingsSchema);
7600
7898
  var inviteSchema = z10.object({
7601
7899
  username: z10.string().min(1)
@@ -7614,10 +7912,21 @@ var app22 = new Hono26().get("/", async (c) => {
7614
7912
  }).post("/", zValidator10("json", createSchema2), async (c) => {
7615
7913
  const user = c.get("user");
7616
7914
  const body = c.req.valid("json");
7915
+ const { name, participantNames, ...settings } = body;
7916
+ const participantIds = [];
7917
+ for (const username of participantNames ?? []) {
7918
+ let member = await getUserByUsername(username);
7919
+ if (!member && await findMind(username)) {
7920
+ member = await getOrCreateMindUser(username);
7921
+ }
7922
+ if (!member) {
7923
+ return c.json({ error: `User not found: ${username}` }, 404);
7924
+ }
7925
+ participantIds.push(member.id);
7926
+ }
7927
+ let ch;
7617
7928
  try {
7618
- const { name, ...settings } = body;
7619
- const ch = await createChannel(name, user.id, settings);
7620
- return c.json({ ...ch, channel_name: name }, 201);
7929
+ ch = await createChannel(name, user.id, settings);
7621
7930
  } catch (err) {
7622
7931
  const cause = err instanceof Error ? err.cause : null;
7623
7932
  if (cause && /UNIQUE/i.test(cause.extendedCode ?? cause.message ?? "")) {
@@ -7625,6 +7934,23 @@ var app22 = new Hono26().get("/", async (c) => {
7625
7934
  }
7626
7935
  throw err;
7627
7936
  }
7937
+ try {
7938
+ for (const id of participantIds) {
7939
+ await joinChannel(ch.id, id);
7940
+ }
7941
+ } catch (err) {
7942
+ console.error("[channels] join failed during channel create:", err);
7943
+ try {
7944
+ await deleteConversation(ch.id);
7945
+ } catch (cleanupErr) {
7946
+ console.error(
7947
+ `[channels] failed to roll back channel ${ch.id} after join failure:`,
7948
+ cleanupErr
7949
+ );
7950
+ }
7951
+ throw err;
7952
+ }
7953
+ return c.json({ ...ch, channel_name: name }, 201);
7628
7954
  }).get("/:name", async (c) => {
7629
7955
  const name = c.req.param("name");
7630
7956
  const ch = await getChannelByName(name);
@@ -7959,11 +8285,11 @@ var app23 = new Hono27().get("/:name/conversations/:id/events", async (c) => {
7959
8285
  if (!stream.aborted) console.error("[chat] SSE ping error:", err);
7960
8286
  });
7961
8287
  }, 15e3);
7962
- await new Promise((resolve17) => {
8288
+ await new Promise((resolve18) => {
7963
8289
  stream.onAbort(() => {
7964
8290
  unsubscribe();
7965
8291
  clearInterval(keepAlive);
7966
- resolve17();
8292
+ resolve18();
7967
8293
  });
7968
8294
  });
7969
8295
  });
@@ -8177,10 +8503,10 @@ app25.route("/api/conversations", conversations_default);
8177
8503
  var app_default = app25;
8178
8504
 
8179
8505
  // packages/daemon/src/web/server.ts
8180
- import { existsSync as existsSync10 } from "fs";
8506
+ import { existsSync as existsSync11 } from "fs";
8181
8507
  import { readFile as readFile2, stat as stat2 } from "fs/promises";
8182
8508
  import { createServer as createHttpsServer } from "https";
8183
- import { dirname as dirname2, extname as extname3, resolve as resolve15 } from "path";
8509
+ import { dirname as dirname2, extname as extname3, resolve as resolve16 } from "path";
8184
8510
  import { serve } from "@hono/node-server";
8185
8511
  var MIME_TYPES2 = {
8186
8512
  ".html": "text/html",
@@ -8199,8 +8525,8 @@ async function startServer({
8199
8525
  let assetsDir = "";
8200
8526
  let searchDir = dirname2(new URL(import.meta.url).pathname);
8201
8527
  for (let i = 0; i < 5; i++) {
8202
- const candidate = resolve15(searchDir, "dist", "web-assets");
8203
- if (existsSync10(candidate)) {
8528
+ const candidate = resolve16(searchDir, "dist", "web-assets");
8529
+ if (existsSync11(candidate)) {
8204
8530
  assetsDir = candidate;
8205
8531
  break;
8206
8532
  }
@@ -8210,7 +8536,7 @@ async function startServer({
8210
8536
  app_default.get("*", async (c) => {
8211
8537
  const urlPath = new URL(c.req.url).pathname;
8212
8538
  if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
8213
- const filePath = resolve15(assetsDir, urlPath.slice(1));
8539
+ const filePath = resolve16(assetsDir, urlPath.slice(1));
8214
8540
  if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
8215
8541
  const s = await stat2(filePath).catch(() => null);
8216
8542
  if (s?.isFile()) {
@@ -8223,7 +8549,7 @@ async function startServer({
8223
8549
  const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
8224
8550
  return c.body(body, 200, { "Content-Type": mime, "Cache-Control": cacheControl });
8225
8551
  }
8226
- const indexPath = resolve15(assetsDir, "index.html");
8552
+ const indexPath = resolve16(assetsDir, "index.html");
8227
8553
  const indexStat = await stat2(indexPath).catch(() => null);
8228
8554
  if (indexStat?.isFile()) {
8229
8555
  const body = await readFile2(indexPath, "utf-8");
@@ -8240,10 +8566,10 @@ async function startServer({
8240
8566
  createServer: createHttpsServer,
8241
8567
  serverOptions: { key: tls.key, cert: tls.cert }
8242
8568
  });
8243
- await new Promise((resolve17, reject) => {
8569
+ await new Promise((resolve18, reject) => {
8244
8570
  server2.on("listening", () => {
8245
8571
  logger_default.info("Volute UI running (https)", { hostname, port });
8246
- resolve17();
8572
+ resolve18();
8247
8573
  });
8248
8574
  server2.on("error", (err) => {
8249
8575
  reject(err);
@@ -8251,13 +8577,13 @@ async function startServer({
8251
8577
  });
8252
8578
  const internalPort = port + 1;
8253
8579
  const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
8254
- await new Promise((resolve17, reject) => {
8580
+ await new Promise((resolve18, reject) => {
8255
8581
  internalServer.on("listening", () => {
8256
8582
  logger_default.info("Volute API running (http, internal)", {
8257
8583
  hostname: "127.0.0.1",
8258
8584
  port: internalPort
8259
8585
  });
8260
- resolve17();
8586
+ resolve18();
8261
8587
  });
8262
8588
  internalServer.on("error", (err) => {
8263
8589
  reject(err);
@@ -8266,10 +8592,10 @@ async function startServer({
8266
8592
  return { server: server2, internalPort };
8267
8593
  }
8268
8594
  const server = serve({ fetch: app_default.fetch, port, hostname });
8269
- await new Promise((resolve17, reject) => {
8595
+ await new Promise((resolve18, reject) => {
8270
8596
  server.on("listening", () => {
8271
8597
  logger_default.info("Volute API running (http)", { hostname, port });
8272
- resolve17();
8598
+ resolve18();
8273
8599
  });
8274
8600
  server.on("error", (err) => {
8275
8601
  reject(err);
@@ -8280,18 +8606,18 @@ async function startServer({
8280
8606
 
8281
8607
  // packages/daemon/src/daemon.ts
8282
8608
  if (!process.env.VOLUTE_HOME) {
8283
- process.env.VOLUTE_HOME = resolve16(homedir3(), ".volute");
8609
+ process.env.VOLUTE_HOME = resolve17(homedir3(), ".volute");
8284
8610
  }
8285
8611
  if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
8286
8612
  process.env.TZ = process.env.VOLUTE_TIMEZONE;
8287
8613
  }
8288
8614
  function writeDaemonConfig(systemDir, config2, token) {
8289
- const configPath = resolve16(systemDir, "daemon.json");
8290
- writeFileSync9(configPath, `${JSON.stringify(config2, null, 2)}
8615
+ const configPath = resolve17(systemDir, "daemon.json");
8616
+ writeFileSync10(configPath, `${JSON.stringify(config2, null, 2)}
8291
8617
  `, { mode: 420 });
8292
8618
  chmodSync(configPath, 420);
8293
- const tokenPath = resolve16(systemDir, "daemon-token");
8294
- writeFileSync9(tokenPath, `${token}
8619
+ const tokenPath = resolve17(systemDir, "daemon-token");
8620
+ writeFileSync10(tokenPath, `${token}
8295
8621
  `, { mode: 384 });
8296
8622
  chmodSync(tokenPath, 384);
8297
8623
  }
@@ -8301,7 +8627,7 @@ async function startDaemon(opts) {
8301
8627
  const home = voluteHome();
8302
8628
  const systemDir = voluteSystemDir();
8303
8629
  if (!opts.foreground) {
8304
- const rotatingLog = new RotatingLog(resolve16(systemDir, "daemon.log"));
8630
+ const rotatingLog = new RotatingLog(resolve17(systemDir, "daemon.log"));
8305
8631
  logger_default.setOutput((line) => rotatingLog.write(`${line}
8306
8632
  `));
8307
8633
  const write = (...args) => rotatingLog.write(`${format(...args)}
@@ -8311,32 +8637,32 @@ async function startDaemon(opts) {
8311
8637
  console.warn = write;
8312
8638
  console.info = write;
8313
8639
  }
8314
- const DAEMON_PID_PATH = resolve16(systemDir, "daemon.pid");
8315
- const DAEMON_JSON_PATH = resolve16(systemDir, "daemon.json");
8316
- const DAEMON_TOKEN_PATH = resolve16(systemDir, "daemon-token");
8640
+ const DAEMON_PID_PATH = resolve17(systemDir, "daemon.pid");
8641
+ const DAEMON_JSON_PATH = resolve17(systemDir, "daemon.json");
8642
+ const DAEMON_TOKEN_PATH = resolve17(systemDir, "daemon-token");
8317
8643
  mkdirSync10(home, { recursive: true });
8318
8644
  ensureSystemDir();
8319
- const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-Y2EJI3GG.js");
8645
+ const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-5ECWTYKG.js");
8320
8646
  migrateConfigSecrets();
8321
8647
  migrateSetupCompleted();
8322
- const { migrateAiModelQualification } = await import("./ai-service-UODCCE4O.js");
8648
+ const { migrateAiModelQualification } = await import("./ai-service-643TNFPH.js");
8323
8649
  migrateAiModelQualification();
8324
- await (await import("./db-MF2JNRUW.js")).getDb();
8650
+ await (await import("./db-FO6LXYCW.js")).getDb();
8325
8651
  try {
8326
8652
  const { eq: eq8, and: and6 } = await import("drizzle-orm");
8327
- const { users: users2 } = await import("./schema-PVNG3PQM.js");
8328
- const db = await (await import("./db-MF2JNRUW.js")).getDb();
8653
+ const { users: users2 } = await import("./schema-VEVO7HG6.js");
8654
+ const db = await (await import("./db-FO6LXYCW.js")).getDb();
8329
8655
  await db.update(users2).set({ role: "system" }).where(and6(eq8(users2.user_type, "system"), eq8(users2.role, "user")));
8330
8656
  } catch (err) {
8331
8657
  logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
8332
8658
  }
8333
8659
  try {
8334
- const { migrateAvatarSizes } = await import("./avatar-image-6KY6UU4D.js");
8660
+ const { migrateAvatarSizes } = await import("./avatar-image-Y45R7LW5.js");
8335
8661
  await migrateAvatarSizes();
8336
8662
  } catch (err) {
8337
8663
  logger_default.warn("avatar size migration failed", logger_default.errorData(err));
8338
8664
  }
8339
- const { initSandbox } = await import("./sandbox-ZJ7QA6Z6.js");
8665
+ const { initSandbox } = await import("./sandbox-5VQ454XP.js");
8340
8666
  await initSandbox();
8341
8667
  try {
8342
8668
  await syncBuiltinSkills();
@@ -8363,7 +8689,12 @@ async function startDaemon(opts) {
8363
8689
  logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
8364
8690
  }
8365
8691
  try {
8366
- const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-CCFAU4GQ.js");
8692
+ await backfillSystemChannelMembers();
8693
+ } catch (err) {
8694
+ logger_default.warn("failed to backfill minds into #system", logger_default.errorData(err));
8695
+ }
8696
+ try {
8697
+ const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-A4VGHADI.js");
8367
8698
  await getOrCreateSystemUser2();
8368
8699
  } catch (err) {
8369
8700
  logger_default.warn(
@@ -8372,12 +8703,12 @@ async function startDaemon(opts) {
8372
8703
  );
8373
8704
  }
8374
8705
  const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes3(32).toString("hex");
8375
- const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-Y2EJI3GG.js");
8706
+ const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-5ECWTYKG.js");
8376
8707
  const globalCfg = readGlobalConfig2();
8377
8708
  let tls;
8378
8709
  if (opts.tailscale || globalCfg.tailscale) {
8379
8710
  try {
8380
- const { getTailscaleTls } = await import("./tailscale-3XOKOW7T.js");
8711
+ const { getTailscaleTls } = await import("./tailscale-MCLY5GYE.js");
8381
8712
  const tlsConfig = await getTailscaleTls();
8382
8713
  tls = { key: tlsConfig.key, cert: tlsConfig.cert };
8383
8714
  logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
@@ -8404,7 +8735,7 @@ async function startDaemon(opts) {
8404
8735
  process.env.VOLUTE_DAEMON_TOKEN = token;
8405
8736
  process.env.VOLUTE_DAEMON_PORT = String(daemonPort);
8406
8737
  process.env.VOLUTE_DAEMON_HOSTNAME = hostname;
8407
- writeFileSync9(DAEMON_PID_PATH, myPid, { mode: 420 });
8738
+ writeFileSync10(DAEMON_PID_PATH, myPid, { mode: 420 });
8408
8739
  const daemonConfig = { port, hostname };
8409
8740
  if (internalPort) daemonConfig.internalPort = internalPort;
8410
8741
  if (tls) daemonConfig.tls = true;
@@ -8457,14 +8788,14 @@ async function startDaemon(opts) {
8457
8788
  await Promise.all(workers);
8458
8789
  }
8459
8790
  try {
8460
- const { isSetupComplete: isSetupComplete2 } = await import("./setup-Y2EJI3GG.js");
8791
+ const { isSetupComplete: isSetupComplete2 } = await import("./setup-5ECWTYKG.js");
8461
8792
  if (isSetupComplete2()) {
8462
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-RPBLLUQ5.js");
8463
- const { startSpiritFull } = await import("./mind-service-NB2JMWQZ.js");
8793
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-WXS3HCLN.js");
8794
+ const { startSpiritFull } = await import("./mind-service-2ARGJNKP.js");
8464
8795
  await ensureSpiritProject();
8465
8796
  const spiritEntry = await findMind(SPIRIT_NAME);
8466
8797
  if (spiritEntry?.dir) {
8467
- const { registerMindDir } = await import("./delivery-router-N4A7ZC5T.js");
8798
+ const { registerMindDir } = await import("./delivery-router-3QFG2ZEX.js");
8468
8799
  registerMindDir(SPIRIT_NAME, spiritEntry.dir);
8469
8800
  }
8470
8801
  await syncSpiritTemplate();
@@ -8481,7 +8812,7 @@ async function startDaemon(opts) {
8481
8812
  bridgeManager.startBridges(daemonPort).catch((err) => {
8482
8813
  logger_default.warn("failed to start bridges", logger_default.errorData(err));
8483
8814
  });
8484
- import("./cloud-sync-46RS4SDA.js").then(
8815
+ import("./cloud-sync-WHKCB4CD.js").then(
8485
8816
  ({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
8486
8817
  logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
8487
8818
  })
@@ -8489,8 +8820,10 @@ async function startDaemon(opts) {
8489
8820
  logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
8490
8821
  });
8491
8822
  try {
8492
- const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-C5CH7CVP.js");
8493
- backfillTemplateHashes();
8823
+ const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-73YURTFR.js");
8824
+ backfillTemplateHashes().then(() => warnStaleTemplates()).catch((err) => {
8825
+ logger_default.warn("failed to check template staleness", logger_default.errorData(err));
8826
+ });
8494
8827
  notifyVersionUpdate().catch((err) => {
8495
8828
  logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
8496
8829
  });
@@ -8517,13 +8850,13 @@ async function startDaemon(opts) {
8517
8850
  logger_default.info(`running on ${hostname}:${port}, pid ${myPid}`);
8518
8851
  function cleanup() {
8519
8852
  try {
8520
- if (readFileSync10(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
8853
+ if (readFileSync11(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
8521
8854
  unlinkSync2(DAEMON_PID_PATH);
8522
8855
  }
8523
8856
  } catch {
8524
8857
  }
8525
8858
  try {
8526
- if (readFileSync10(DAEMON_TOKEN_PATH, "utf-8").trim() === token) {
8859
+ if (readFileSync11(DAEMON_TOKEN_PATH, "utf-8").trim() === token) {
8527
8860
  unlinkSync2(DAEMON_TOKEN_PATH);
8528
8861
  try {
8529
8862
  unlinkSync2(DAEMON_JSON_PATH);