volute 0.31.0 → 0.32.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 (178) hide show
  1. package/README.md +15 -22
  2. package/dist/{accept-GAKQ3MEH.js → accept-74M7I4RZ.js} +3 -2
  3. package/dist/{activity-events-T5ZRCVAL.js → activity-events-HETAODOK.js} +3 -2
  4. package/dist/{ai-service-UWUPM4T6.js → ai-service-ZIPCV3MX.js} +18 -5
  5. package/dist/api.d.ts +98 -281
  6. package/dist/{archive-YBNSJYZZ.js → archive-INXYFVCW.js} +3 -2
  7. package/dist/{auth-T5AW2USD.js → auth-6DMGES3I.js} +4 -3
  8. package/dist/{bridge-4AJ3EY26.js → bridge-BVCBTGPF.js} +3 -2
  9. package/dist/{chat-7YLT7FI3.js → chat-XT4OBJBU.js} +8 -8
  10. package/dist/{chunk-BNC43CSY.js → chunk-2FLJ63GU.js} +2 -2
  11. package/dist/{chunk-NV3TYNWX.js → chunk-2NGTS5UU.js} +1 -1
  12. package/dist/{chunk-LX6T3GKQ.js → chunk-ALEF47VT.js} +1 -1
  13. package/dist/{chunk-S2TZLSDH.js → chunk-D5G5YOPL.js} +163 -15
  14. package/dist/{chunk-VGWJSNHS.js → chunk-G53F3JA4.js} +1 -35
  15. package/dist/{chunk-A6TUJJ3L.js → chunk-G6BSYHPK.js} +2 -2
  16. package/dist/{chunk-BC3P3QCK.js → chunk-I5KY25PQ.js} +1 -9
  17. package/dist/{chunk-PNQCXLSV.js → chunk-IYDIE3HG.js} +58 -1
  18. package/dist/{chunk-HDKY4TWU.js → chunk-JJ7W6WSB.js} +3 -3
  19. package/dist/{chunk-57OKQMP3.js → chunk-LGB6JBHI.js} +1 -1
  20. package/dist/chunk-LRCG2JLP.js +251 -0
  21. package/dist/{chunk-SNVPRRT7.js → chunk-LSGWR54X.js} +2 -2
  22. package/dist/{chunk-EMPFLFTG.js → chunk-M7UL5S3Q.js} +1 -1
  23. package/dist/chunk-PB65JZK2.js +85 -0
  24. package/dist/chunk-PVY5W6QN.js +41 -0
  25. package/dist/{chunk-BWKIHH7B.js → chunk-QBQ424EM.js} +318 -418
  26. package/dist/{chunk-EKDWA7E4.js → chunk-QZANELPX.js} +4 -2
  27. package/dist/{chunk-AAO77TZX.js → chunk-R7E6CRVQ.js} +1 -1
  28. package/dist/{chunk-X62AXPR7.js → chunk-RPZZSXV3.js} +8 -196
  29. package/dist/{chunk-WRS3B556.js → chunk-RSX4OPZY.js} +5 -5
  30. package/dist/{chunk-FAHDKPEH.js → chunk-S6NFERDC.js} +5 -3
  31. package/dist/chunk-SKLSMHXO.js +208 -0
  32. package/dist/{chunk-DAXJKPHZ.js → chunk-SX5TKJBZ.js} +2 -2
  33. package/dist/{chunk-R5QJBZZG.js → chunk-TDRYEPH4.js} +20 -10
  34. package/dist/{chunk-6QIUN46C.js → chunk-TSXLLQZW.js} +11 -3
  35. package/dist/{chunk-4OUOFS23.js → chunk-UKVWJRKN.js} +1 -1
  36. package/dist/{chunk-NOWVQ7AL.js → chunk-WKF5FEFK.js} +318 -167
  37. package/dist/cli.js +38 -20
  38. package/dist/{clock-LJCG426D.js → clock-2UOZ6JPU.js} +5 -4
  39. package/dist/{cloud-sync-O3LXIRN6.js → cloud-sync-JN3NWKEM.js} +16 -14
  40. package/dist/config-H2H4UIF7.js +72 -0
  41. package/dist/connectors/discord-bridge.js +1 -1
  42. package/dist/connectors/slack-bridge.js +1 -1
  43. package/dist/connectors/telegram-bridge.js +1 -1
  44. package/dist/{conversations-RKKGP5IA.js → conversations-3O5O6AS3.js} +4 -3
  45. package/dist/{create-WUTIIRI2.js → create-RNLNCORE.js} +3 -2
  46. package/dist/{create-TL623TFC.js → create-WBBYI6V7.js} +6 -2
  47. package/dist/{daemon-client-CVGM25DM.js → daemon-client-6QXHZ7US.js} +3 -2
  48. package/dist/{daemon-restart-EZP7XH3V.js → daemon-restart-NGFHFAUF.js} +7 -6
  49. package/dist/daemon.js +907 -612
  50. package/dist/{db-SW5PL6QA.js → db-F34YLV7D.js} +2 -1
  51. package/dist/db-RA45JBFG.js +16 -0
  52. package/dist/{delete-Z6HAG35F.js → delete-QTGWEDBI.js} +1 -1
  53. package/dist/delivery-manager-SDVXFD4W.js +28 -0
  54. package/dist/delivery-router-FL45JL7N.js +21 -0
  55. package/dist/down-TB3ESMNP.js +14 -0
  56. package/dist/{env-NHESNNSP.js → env-RLYQBOOP.js} +3 -2
  57. package/dist/{export-EVMP7GWY.js → export-SUYRLI5Q.js} +4 -3
  58. package/dist/{extension-LR7EW3JF.js → extension-FQ5D3NCC.js} +4 -3
  59. package/dist/{extensions-NGEJI7JH.js → extensions-GDYWQXC4.js} +9 -7
  60. package/dist/{files-3SM7V33S.js → files-EAMPO2SJ.js} +4 -3
  61. package/dist/{history-PQD3LXEP.js → history-FO5PHBQ5.js} +7 -2
  62. package/dist/{import-PR2OCGQJ.js → import-DDUFE7AY.js} +4 -3
  63. package/dist/{join-R4EN5CWQ.js → join-I5QEE3LG.js} +1 -1
  64. package/dist/{list-B4XNUOFO.js → list-DW2VRTOZ.js} +3 -2
  65. package/dist/{login-62JVY6A2.js → login-7CHPW2PN.js} +3 -2
  66. package/dist/{login-URWP6S2N.js → login-RIJF2F4G.js} +3 -2
  67. package/dist/{logout-NXJQJDLI.js → logout-5MLHZALK.js} +3 -2
  68. package/dist/{logout-ZK2N62T3.js → logout-UZJRGY4Z.js} +3 -2
  69. package/dist/message-delivery-2FIM7QKO.js +32 -0
  70. package/dist/{mind-E2ZV2WRX.js → mind-2B6M7Y25.js} +18 -18
  71. package/dist/{mind-activity-tracker-ASNZBMLC.js → mind-activity-tracker-NZZT2NTT.js} +4 -3
  72. package/dist/{mind-list-BEI7E5WY.js → mind-list-WUPMQDYQ.js} +3 -2
  73. package/dist/mind-manager-BNCMGYXW.js +28 -0
  74. package/dist/mind-service-AV273WT4.js +34 -0
  75. package/dist/{mind-sleep-CANABWJI.js → mind-sleep-B7BHJLH7.js} +3 -2
  76. package/dist/{mind-status-6WKZVUOP.js → mind-status-L3EFFRPR.js} +3 -2
  77. package/dist/{mind-wake-RZKLH2IN.js → mind-wake-GY3RFX7Y.js} +3 -2
  78. package/dist/{package-NU4CA7OU.js → package-PK6JUFL3.js} +1 -1
  79. package/dist/{read-THL362EI.js → read-5AMJRO3D.js} +3 -2
  80. package/dist/{register-QAQELAS6.js → register-V2JZZKFK.js} +3 -2
  81. package/dist/{registry-ASXCQCNH.js → registry-PJ4S5PHQ.js} +8 -1
  82. package/dist/{reject-AYPBNPNL.js → reject-33HEZMZ4.js} +3 -2
  83. package/dist/{restart-6SKPV3T2.js → restart-3UCMRUVC.js} +3 -2
  84. package/dist/{sandbox-6ZEWQDVU.js → sandbox-JANNTX6U.js} +4 -3
  85. package/dist/schema-PA3M5ZKH.js +32 -0
  86. package/dist/{seed-OWX2AW75.js → seed-ALUQ55FF.js} +26 -9
  87. package/dist/{send-ZO4BTWXK.js → send-3MI36LEF.js} +56 -67
  88. package/dist/{setup-7CFITEQN.js → setup-SZIARWI6.js} +5 -2
  89. package/dist/{setup-ZXBXG7E4.js → setup-WENLVPVP.js} +8 -6
  90. package/dist/{skill-YFXP67A2.js → skill-TUVOTW4Z.js} +3 -2
  91. package/dist/skills/dreaming/SKILL.md +6 -4
  92. package/dist/skills/dreaming/references/INSTALL.md +2 -2
  93. package/dist/skills/dreaming/scripts/dream.ts +2 -2
  94. package/dist/skills/dreaming/scripts/wake-context-dreams.sh +1 -1
  95. package/dist/skills/imagegen/SKILL.md +6 -5
  96. package/dist/skills/imagegen/references/INSTALL.md +1 -1
  97. package/dist/skills/resonance/SKILL.md +4 -1
  98. package/dist/skills/resonance/references/INSTALL.md +2 -2
  99. package/dist/skills/resonance/scripts/resonance-hook.sh +2 -0
  100. package/dist/skills/resonance/scripts/resonance.ts +35 -5
  101. package/dist/skills/volute-admin/SKILL.md +83 -0
  102. package/dist/skills/volute-mind/SKILL.md +11 -11
  103. package/dist/skills-XNZK6P4K.js +61 -0
  104. package/dist/sleep-manager-53DZOWW7.js +32 -0
  105. package/dist/spirit-N4W4UQRH.js +217 -0
  106. package/dist/{split-MI62KJUU.js → split-STOROBYJ.js} +1 -1
  107. package/dist/{sprout-FDVI2CGN.js → sprout-L2GFOVF7.js} +9 -7
  108. package/dist/{start-D64BRKPH.js → start-K2NCUUCG.js} +3 -2
  109. package/dist/{status-ZZWBYFGE.js → status-TCUMUO6M.js} +5 -4
  110. package/dist/{stop-OP2CTXCO.js → stop-H26JZDXF.js} +3 -2
  111. package/dist/system-chat-NPYFYZVI.js +32 -0
  112. package/dist/{systems-EQPPT4B7.js → systems-DHBKVYEY.js} +6 -5
  113. package/dist/{tailscale-6DJKUMNF.js → tailscale-XHQBZROW.js} +2 -1
  114. package/dist/{template-hash-3HOR4UAJ.js → template-hash-A6VVKOXJ.js} +2 -1
  115. package/dist/up-6I6BHRTO.js +17 -0
  116. package/dist/{update-KUJXATRS.js → update-QVPRF6GR.js} +5 -4
  117. package/dist/{update-check-5WVSU37T.js → update-check-ZD6OOIYQ.js} +3 -2
  118. package/dist/{upgrade-KBHCWX6T.js → upgrade-O4Q7WJM3.js} +12 -14
  119. package/dist/{version-notify-75ELVKPV.js → version-notify-TCKWBZZG.js} +21 -18
  120. package/dist/web-assets/assets/index-Bui7U9Uu.css +1 -0
  121. package/dist/web-assets/assets/index-e36DIo1b.js +73 -0
  122. package/dist/web-assets/ext-theme.css +93 -0
  123. package/dist/web-assets/index.html +2 -2
  124. package/drizzle/0004_spirits.sql +5 -0
  125. package/drizzle/meta/0004_snapshot.json +7 -0
  126. package/drizzle/meta/_journal.json +7 -0
  127. package/package.json +1 -1
  128. package/packages/extensions/notes/dist/ui/assets/index-8jWEv9SA.js +61 -0
  129. package/packages/extensions/notes/dist/ui/assets/index-DkaB7Ytd.css +1 -0
  130. package/packages/extensions/notes/dist/ui/index.html +2 -2
  131. package/packages/extensions/pages/skills/pages/SKILL.md +16 -46
  132. package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +40 -0
  133. package/templates/_base/.init/{.config → .local}/bin/volute +1 -1
  134. package/templates/_base/.init/.local/hooks/pre-prompt/session-activity.ts +40 -0
  135. package/templates/_base/.init/.local/hooks/startup-context.ts +58 -0
  136. package/templates/_base/home/.config/routes.json +1 -1
  137. package/templates/_base/src/lib/daemon-client.ts +21 -13
  138. package/templates/_base/src/lib/format-prefix.ts +1 -0
  139. package/templates/_base/src/lib/hook-loader.ts +155 -0
  140. package/templates/_base/src/lib/startup.ts +11 -4
  141. package/templates/_base/src/lib/transparency.ts +2 -2
  142. package/templates/claude/.init/.claude/settings.json +1 -1
  143. package/templates/claude/.init/.config/routes.json +2 -2
  144. package/templates/claude/src/agent.ts +95 -13
  145. package/templates/claude/src/lib/message-channel.ts +7 -2
  146. package/templates/codex/.init/.config/routes.json +11 -0
  147. package/templates/codex/.init/AGENTS.md +29 -0
  148. package/templates/codex/home/.config/config.json.tmpl +7 -0
  149. package/templates/codex/package.json.tmpl +20 -0
  150. package/templates/codex/src/agent.ts +553 -0
  151. package/templates/codex/src/lib/content.ts +16 -0
  152. package/templates/codex/src/lib/session-store.ts +56 -0
  153. package/templates/codex/src/server.ts +59 -0
  154. package/templates/codex/volute-template.json +8 -0
  155. package/templates/pi/.init/.config/routes.json +2 -2
  156. package/templates/pi/src/agent.ts +62 -8
  157. package/templates/pi/src/lib/event-handler.ts +1 -1
  158. package/templates/pi/src/lib/reply-instructions-extension.ts +32 -11
  159. package/dist/chunk-HR5JKIDG.js +0 -222
  160. package/dist/down-TS4XQBA4.js +0 -13
  161. package/dist/message-delivery-UJHCLVU4.js +0 -30
  162. package/dist/mind-manager-IPA6DZXD.js +0 -26
  163. package/dist/pages-watcher-72OVPRMH.js +0 -22
  164. package/dist/skills/sessions/SKILL.md +0 -49
  165. package/dist/sleep-manager-TPS6OGCA.js +0 -30
  166. package/dist/system-chat-B43GIXQU.js +0 -30
  167. package/dist/up-TDXEP3VA.js +0 -16
  168. package/dist/web-assets/assets/index-BM1cTzBg.js +0 -72
  169. package/dist/web-assets/assets/index-BfJkKTPF.css +0 -1
  170. package/packages/extensions/notes/dist/ui/assets/index-B8GdTnXs.css +0 -1
  171. package/packages/extensions/notes/dist/ui/assets/index-CDpGTCWb.js +0 -2
  172. package/packages/extensions/pages/skills/pages/scripts/pages.mjs +0 -58
  173. package/templates/_base/.init/.config/hooks/startup-context.sh +0 -46
  174. package/templates/_base/.init/.config/scripts/session-reader.ts +0 -59
  175. package/templates/_base/src/lib/session-monitor.ts +0 -400
  176. package/templates/claude/src/lib/hooks/session-context.ts +0 -32
  177. package/templates/pi/src/lib/session-context-extension.ts +0 -35
  178. /package/templates/_base/.init/{.config → .local}/hooks/wake-context.sh +0 -0
package/dist/daemon.js CHANGED
@@ -1,17 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ computeTemplateHash
4
+ } from "./chunk-PVY5W6QN.js";
2
5
  import {
3
6
  checkForUpdate,
4
7
  checkForUpdateCached,
5
8
  getCurrentVersion
6
- } from "./chunk-EMPFLFTG.js";
7
- import {
8
- applyInitFiles,
9
- composeTemplate,
10
- computeTemplateHash,
11
- copyTemplateToDir,
12
- findTemplatesRoot,
13
- listFiles
14
- } from "./chunk-VGWJSNHS.js";
9
+ } from "./chunk-M7UL5S3Q.js";
15
10
  import {
16
11
  PROMPT_DEFAULTS,
17
12
  PROMPT_KEYS,
@@ -36,22 +31,26 @@ import {
36
31
  initScheduler,
37
32
  initSleepManager,
38
33
  initTokenBudget,
34
+ isConversationId,
39
35
  joinSystemChannel,
40
36
  publish as publish2,
41
37
  publishTypingForChannels,
42
38
  readVoluteConfig,
43
39
  recordInbound,
44
- resolveChannelId,
45
40
  resolveMindToken,
46
- splitMessage,
47
41
  startMindFull,
48
42
  stopMindFull,
49
43
  subscribe as subscribe3,
50
44
  substitute,
51
45
  tagUntaggedInbound,
52
- writeChannelEntry,
53
46
  writeVoluteConfig
54
- } from "./chunk-BWKIHH7B.js";
47
+ } from "./chunk-QBQ424EM.js";
48
+ import "./chunk-SKLSMHXO.js";
49
+ import {
50
+ getActiveMinds,
51
+ onMindEvent,
52
+ stopAll
53
+ } from "./chunk-R7E6CRVQ.js";
55
54
  import {
56
55
  addMessage,
57
56
  createChannel,
@@ -79,13 +78,8 @@ import {
79
78
  publish,
80
79
  setConversationPrivate,
81
80
  subscribe as subscribe2
82
- } from "./chunk-FAHDKPEH.js";
83
- import "./chunk-DAXJKPHZ.js";
84
- import {
85
- getActiveMinds,
86
- onMindEvent,
87
- stopAll
88
- } from "./chunk-AAO77TZX.js";
81
+ } from "./chunk-S6NFERDC.js";
82
+ import "./chunk-SX5TKJBZ.js";
89
83
  import {
90
84
  acceptPending,
91
85
  formatFileSize,
@@ -93,13 +87,31 @@ import {
93
87
  rejectPending,
94
88
  stageFile,
95
89
  validateFilePath
96
- } from "./chunk-LX6T3GKQ.js";
90
+ } from "./chunk-ALEF47VT.js";
91
+ import {
92
+ findBridgeForChannel,
93
+ findOpenClawSession,
94
+ getBridgeConfig,
95
+ importOpenClawConnectors,
96
+ importPiSession,
97
+ parseNameFromIdentity,
98
+ readBridgesConfig,
99
+ removeBridgeConfig,
100
+ removeChannelMapping,
101
+ resolveChannelMapping,
102
+ setBridgeConfig,
103
+ setChannelMapping
104
+ } from "./chunk-RSX4OPZY.js";
97
105
  import {
98
- getCachedRecentPages,
99
- getCachedSites
100
- } from "./chunk-HR5JKIDG.js";
106
+ loadMergedEnv,
107
+ mindEnvPath,
108
+ readEnv,
109
+ sharedEnvPath,
110
+ writeEnv
111
+ } from "./chunk-2NGTS5UU.js";
101
112
  import {
102
113
  assignSession,
114
+ completeOrphanedTurns,
103
115
  completeTurn,
104
116
  createTurn,
105
117
  deleteSystemsConfig,
@@ -114,7 +126,8 @@ import {
114
126
  setSummaryEventId,
115
127
  trackToolUse,
116
128
  writeSystemsConfig
117
- } from "./chunk-NOWVQ7AL.js";
129
+ } from "./chunk-WKF5FEFK.js";
130
+ import "./chunk-PB65JZK2.js";
118
131
  import {
119
132
  approveUser,
120
133
  changePassword,
@@ -132,11 +145,18 @@ import {
132
145
  setUserRole,
133
146
  updateUserProfile,
134
147
  verifyUser
135
- } from "./chunk-R5QJBZZG.js";
148
+ } from "./chunk-TDRYEPH4.js";
136
149
  import {
137
150
  broadcast,
138
151
  subscribe
139
- } from "./chunk-EKDWA7E4.js";
152
+ } from "./chunk-QZANELPX.js";
153
+ import {
154
+ applyInitFiles,
155
+ composeTemplate,
156
+ copyTemplateToDir,
157
+ findTemplatesRoot,
158
+ listFiles
159
+ } from "./chunk-G53F3JA4.js";
140
160
  import {
141
161
  SEED_SKILLS,
142
162
  STANDARD_SKILLS,
@@ -154,43 +174,25 @@ import {
154
174
  syncBuiltinSkills,
155
175
  uninstallSkill,
156
176
  updateSkill
157
- } from "./chunk-S2TZLSDH.js";
177
+ } from "./chunk-D5G5YOPL.js";
158
178
  import {
159
179
  isHomeOnlyArchive
160
- } from "./chunk-BC3P3QCK.js";
161
- import {
162
- findBridgeForChannel,
163
- findOpenClawSession,
164
- getBridgeConfig,
165
- importOpenClawConnectors,
166
- importPiSession,
167
- parseNameFromIdentity,
168
- readBridgesConfig,
169
- removeBridgeConfig,
170
- removeChannelMapping,
171
- resolveChannelMapping,
172
- setBridgeConfig,
173
- setChannelMapping
174
- } from "./chunk-WRS3B556.js";
175
- import {
176
- loadMergedEnv,
177
- mindEnvPath,
178
- readEnv,
179
- sharedEnvPath,
180
- writeEnv
181
- } from "./chunk-NV3TYNWX.js";
180
+ } from "./chunk-I5KY25PQ.js";
182
181
  import {
183
- aiComplete,
182
+ aiCompleteUtility,
184
183
  getAiConfig,
185
184
  getAvailableModels,
186
185
  getConfiguredProviders,
187
186
  getEnabledModels,
187
+ qualifyModelId,
188
188
  removeAiConfig,
189
189
  removeProviderConfig,
190
190
  resolveApiKey,
191
+ resolveTemplate,
191
192
  saveProviderConfig,
192
- setEnabledModels
193
- } from "./chunk-PNQCXLSV.js";
193
+ setEnabledModels,
194
+ unqualifyModelId
195
+ } from "./chunk-IYDIE3HG.js";
194
196
  import {
195
197
  logBuffer,
196
198
  logger_default
@@ -206,26 +208,23 @@ import {
206
208
  mindUserName,
207
209
  resolveVoluteBin,
208
210
  wrapForIsolation
209
- } from "./chunk-57OKQMP3.js";
211
+ } from "./chunk-LGB6JBHI.js";
210
212
  import {
211
213
  isSetupComplete,
212
214
  readGlobalConfig,
213
215
  writeGlobalConfig
214
- } from "./chunk-6QIUN46C.js";
216
+ } from "./chunk-TSXLLQZW.js";
215
217
  import "./chunk-D424ZQGI.js";
216
218
  import {
217
219
  readSessionFile
218
- } from "./chunk-4OUOFS23.js";
220
+ } from "./chunk-UKVWJRKN.js";
219
221
  import {
220
222
  buildVoluteSlug,
221
223
  slugify
222
- } from "./chunk-A6TUJJ3L.js";
224
+ } from "./chunk-G6BSYHPK.js";
223
225
  import {
224
- activity,
225
226
  addMind,
226
227
  addVariant,
227
- conversationParticipants,
228
- conversations,
229
228
  daemonLoopback,
230
229
  ensureSystemDir,
231
230
  ensureVoluteHome,
@@ -233,25 +232,30 @@ import {
233
232
  findVariants,
234
233
  getBaseName,
235
234
  getDb,
236
- messages,
237
235
  mindDir,
238
- mindHistory,
239
236
  nextPort,
240
237
  readAllMinds,
241
238
  readRegistry,
242
239
  removeMind,
243
- sessions,
244
240
  setMindRunning,
245
241
  setMindStage,
246
242
  setMindTemplateHash,
247
243
  stateDir,
248
- systemPrompts,
249
- turns,
250
- users,
251
244
  validateMindName,
252
245
  voluteHome,
253
246
  voluteSystemDir
254
- } from "./chunk-X62AXPR7.js";
247
+ } from "./chunk-LRCG2JLP.js";
248
+ import {
249
+ activity,
250
+ conversationParticipants,
251
+ conversations,
252
+ messages,
253
+ mindHistory,
254
+ sessions,
255
+ systemPrompts,
256
+ turns,
257
+ users
258
+ } from "./chunk-RPZZSXV3.js";
255
259
  import {
256
260
  __export
257
261
  } from "./chunk-K3NQKI34.js";
@@ -347,8 +351,8 @@ var BridgeManager = class {
347
351
  shuttingDown = false;
348
352
  restartTracker = new RestartTracker();
349
353
  async startBridges(daemonPort) {
350
- const config = readBridgesConfig();
351
- const platforms = Object.entries(config).filter(([, cfg]) => cfg.enabled).map(([platform]) => platform);
354
+ const config2 = readBridgesConfig();
355
+ const platforms = Object.entries(config2).filter(([, cfg]) => cfg.enabled).map(([platform]) => platform);
352
356
  await Promise.all(
353
357
  platforms.map(
354
358
  (platform) => this.startBridge(platform, daemonPort).catch((err) => {
@@ -813,6 +817,13 @@ var requireAdmin = createMiddleware(async (c, next) => {
813
817
  }
814
818
  await next();
815
819
  });
820
+ var requireAdminOrSystem = createMiddleware(async (c, next) => {
821
+ const user = c.get("user");
822
+ if (user.role !== "admin" && user.role !== "system") {
823
+ return c.json({ error: "Forbidden" }, 403);
824
+ }
825
+ await next();
826
+ });
816
827
  async function resolveSession(sessionId) {
817
828
  const cached = sessionCache.get(sessionId);
818
829
  if (cached && cached.expires > Date.now()) {
@@ -879,7 +890,7 @@ var authMiddleware = createMiddleware(async (c, next) => {
879
890
  });
880
891
  var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
881
892
  const user = c.get("user");
882
- if (user.role !== "admin") {
893
+ if (user.role !== "admin" && user.role !== "system") {
883
894
  const target = c.req.param(paramName) ?? "";
884
895
  const baseName = await getBaseName(target);
885
896
  if (user.username !== baseName) {
@@ -916,8 +927,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
916
927
  });
917
928
  });
918
929
  }).get("/info", (c) => {
919
- const config = readSystemsConfig();
920
- return c.json({ system: config?.system ?? null });
930
+ const config2 = readSystemsConfig();
931
+ return c.json({ system: config2?.system ?? null });
921
932
  }).post(
922
933
  "/register",
923
934
  requireAdmin,
@@ -928,6 +939,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
928
939
  return c.json({ error: `Already registered as "${existing.system}"` }, 400);
929
940
  }
930
941
  const { name } = c.req.valid("json");
942
+ const config2 = readGlobalConfig();
931
943
  const apiUrl = process.env.VOLUTE_SYSTEMS_URL || DEFAULT_API_URL;
932
944
  let apiKey;
933
945
  let system;
@@ -935,7 +947,11 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
935
947
  const res = await fetch(`${apiUrl}/api/register`, {
936
948
  method: "POST",
937
949
  headers: { "Content-Type": "application/json" },
938
- body: JSON.stringify({ name: name.trim() })
950
+ body: JSON.stringify({
951
+ name: name.trim(),
952
+ displayName: config2.name || void 0,
953
+ description: config2.description || void 0
954
+ })
939
955
  });
940
956
  if (!res.ok) {
941
957
  const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
@@ -997,16 +1013,16 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
997
1013
  deleteSystemsConfig();
998
1014
  return c.json({ ok: true });
999
1015
  }).put("/pages/publish/:name", requireAdmin, async (c) => {
1000
- const config = readSystemsConfig();
1001
- if (!config) return c.json({ error: "Not connected to volute.systems" }, 400);
1016
+ const config2 = readSystemsConfig();
1017
+ if (!config2) return c.json({ error: "Not connected to volute.systems" }, 400);
1002
1018
  const name = c.req.param("name");
1003
1019
  const body = await c.req.text();
1004
1020
  try {
1005
- const res = await fetch(`${config.apiUrl}/api/pages/publish/${name}`, {
1021
+ const res = await fetch(`${config2.apiUrl}/api/pages/publish/${name}`, {
1006
1022
  method: "PUT",
1007
1023
  headers: {
1008
1024
  "Content-Type": "application/json",
1009
- Authorization: `Bearer ${config.apiKey}`
1025
+ Authorization: `Bearer ${config2.apiKey}`
1010
1026
  },
1011
1027
  body
1012
1028
  });
@@ -1016,12 +1032,12 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1016
1032
  return c.json({ error: `Connection failed: ${err.message}` }, 502);
1017
1033
  }
1018
1034
  }).get("/pages/status/:name", requireAdmin, async (c) => {
1019
- const config = readSystemsConfig();
1020
- if (!config) return c.json({ error: "Not connected to volute.systems" }, 400);
1035
+ const config2 = readSystemsConfig();
1036
+ if (!config2) return c.json({ error: "Not connected to volute.systems" }, 400);
1021
1037
  const name = c.req.param("name");
1022
1038
  try {
1023
- const res = await fetch(`${config.apiUrl}/api/pages/status/${name}`, {
1024
- headers: { Authorization: `Bearer ${config.apiKey}` }
1039
+ const res = await fetch(`${config2.apiUrl}/api/pages/status/${name}`, {
1040
+ headers: { Authorization: `Bearer ${config2.apiKey}` }
1025
1041
  });
1026
1042
  const data = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
1027
1043
  return c.json(data, res.status);
@@ -1239,7 +1255,7 @@ function stopApiKeyRefresh() {
1239
1255
  var system_default = app;
1240
1256
 
1241
1257
  // src/web/app.ts
1242
- import { Hono as Hono30 } from "hono";
1258
+ import { Hono as Hono31 } from "hono";
1243
1259
  import { bodyLimit } from "hono/body-limit";
1244
1260
  import { csrf } from "hono/csrf";
1245
1261
  import { HTTPException } from "hono/http-exception";
@@ -1702,7 +1718,7 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
1702
1718
  }
1703
1719
  const participants = await getParticipants(channel.id);
1704
1720
  if (!participants.some((p) => p.userId === puppet.id)) {
1705
- const { addParticipant } = await import("./conversations-RKKGP5IA.js");
1721
+ const { addParticipant } = await import("./conversations-3O5O6AS3.js");
1706
1722
  await addParticipant(channel.id, puppet.id);
1707
1723
  }
1708
1724
  const contentBlocks = body.content;
@@ -1716,10 +1732,10 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
1716
1732
  });
1717
1733
  return c.json({ ok: true, conversationId: channel.id });
1718
1734
  }).get("/", (c) => {
1719
- const config = readBridgesConfig();
1735
+ const config2 = readBridgesConfig();
1720
1736
  const manager = getBridgeManager();
1721
1737
  const statuses = manager.getBridgeStatus();
1722
- const bridges = Object.entries(config).map(([platform, cfg]) => {
1738
+ const bridges = Object.entries(config2).map(([platform, cfg]) => {
1723
1739
  const status = statuses.find((s) => s.platform === platform);
1724
1740
  const def = getBridgeDef(platform);
1725
1741
  return {
@@ -1787,16 +1803,16 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
1787
1803
  return c.json({ ok: true });
1788
1804
  }).get("/:platform/mappings", (c) => {
1789
1805
  const platform = c.req.param("platform");
1790
- const config = getBridgeConfig(platform);
1791
- if (!config) return c.json({ error: "Bridge not configured" }, 404);
1792
- return c.json(config.channelMappings);
1806
+ const config2 = getBridgeConfig(platform);
1807
+ if (!config2) return c.json({ error: "Bridge not configured" }, 404);
1808
+ return c.json(config2.channelMappings);
1793
1809
  });
1794
1810
  async function fanOutToBridgedMinds(opts) {
1795
1811
  const participants = await getParticipants(opts.conversationId);
1796
1812
  const mindParticipants = participants.filter((p) => p.userType === "mind");
1797
1813
  const participantNames = participants.map((p) => p.username);
1798
- const { getMindManager: getMindManager2 } = await import("./mind-manager-IPA6DZXD.js");
1799
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-TPS6OGCA.js");
1814
+ const { getMindManager: getMindManager2 } = await import("./mind-manager-BNCMGYXW.js");
1815
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
1800
1816
  const manager = getMindManager2();
1801
1817
  const sm = getSleepManagerIfReady2();
1802
1818
  const targetMinds = mindParticipants.filter((ap) => {
@@ -1809,15 +1825,6 @@ async function fanOutToBridgedMinds(opts) {
1809
1825
  convTitle: null,
1810
1826
  conversationId: opts.conversationId
1811
1827
  });
1812
- try {
1813
- writeChannelEntry(mindName, channel, {
1814
- platformId: opts.conversationId,
1815
- platform: "volute",
1816
- type: opts.isDM ? "dm" : "channel"
1817
- });
1818
- } catch (err) {
1819
- logger_default.warn(`failed to write channel entry for ${mindName}`, logger_default.errorData(err));
1820
- }
1821
1828
  deliverMessage(mindName, {
1822
1829
  content: opts.contentBlocks,
1823
1830
  channel,
@@ -1845,6 +1852,21 @@ __export(discord_exports, {
1845
1852
  read: () => read,
1846
1853
  send: () => send
1847
1854
  });
1855
+
1856
+ // src/connectors/sdk.ts
1857
+ function splitMessage(text, maxLength) {
1858
+ const chunks = [];
1859
+ while (text.length > maxLength) {
1860
+ let splitAt = text.lastIndexOf("\n", maxLength);
1861
+ if (splitAt < maxLength / 2) splitAt = maxLength;
1862
+ chunks.push(text.slice(0, splitAt));
1863
+ text = text.slice(splitAt).replace(/^\n/, "");
1864
+ }
1865
+ if (text) chunks.push(text);
1866
+ return chunks;
1867
+ }
1868
+
1869
+ // src/lib/channels/discord.ts
1848
1870
  var DISCORD_MAX_LENGTH = 2e3;
1849
1871
  var API_BASE = "https://discord.com/api/v10";
1850
1872
  function requireToken(env) {
@@ -1863,7 +1885,7 @@ async function discordGet(token, path) {
1863
1885
  }
1864
1886
  async function read(env, channelSlug, limit) {
1865
1887
  const token = requireToken(env);
1866
- const channelId = resolveChannelId2(env, channelSlug);
1888
+ const channelId = resolveChannelId(channelSlug);
1867
1889
  const res = await fetch(`${API_BASE}/channels/${channelId}/messages?limit=${limit}`, {
1868
1890
  headers: { Authorization: `Bot ${token}` }
1869
1891
  });
@@ -1875,7 +1897,7 @@ async function read(env, channelSlug, limit) {
1875
1897
  }
1876
1898
  async function send(env, channelSlug, message, images) {
1877
1899
  const token = requireToken(env);
1878
- const channelId = resolveChannelId2(env, channelSlug);
1900
+ const channelId = resolveChannelId(channelSlug);
1879
1901
  if (images?.length) {
1880
1902
  for (let i = 0; i < images.length; i++) {
1881
1903
  const img = images[i];
@@ -1987,18 +2009,8 @@ async function createConversation2(env, participants, _name) {
1987
2009
  if (!res.ok) {
1988
2010
  throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
1989
2011
  }
1990
- const dm = await res.json();
1991
- const slug = `discord:@${slugify(participants[0])}`;
1992
- const mindName = env.VOLUTE_MIND;
1993
- if (mindName) {
1994
- writeChannelEntry(mindName, slug, {
1995
- platformId: dm.id,
1996
- platform: "discord",
1997
- name: participants[0],
1998
- type: "dm"
1999
- });
2000
- }
2001
- return slug;
2012
+ const dmChannel = await res.json();
2013
+ return `discord:${dmChannel.id}`;
2002
2014
  }
2003
2015
 
2004
2016
  // src/lib/channels/slack.ts
@@ -2037,7 +2049,7 @@ async function slackApi(token, method, body) {
2037
2049
  }
2038
2050
  async function read2(env, channelSlug, limit) {
2039
2051
  const token = requireToken2(env);
2040
- const channelId = resolveChannelId2(env, channelSlug);
2052
+ const channelId = resolveChannelId(channelSlug);
2041
2053
  const data = await slackApi(token, "conversations.history", {
2042
2054
  channel: channelId,
2043
2055
  limit
@@ -2046,7 +2058,7 @@ async function read2(env, channelSlug, limit) {
2046
2058
  }
2047
2059
  async function send2(env, channelSlug, message, images) {
2048
2060
  const token = requireToken2(env);
2049
- const channelId = resolveChannelId2(env, channelSlug);
2061
+ const channelId = resolveChannelId(channelSlug);
2050
2062
  if (images?.length) {
2051
2063
  for (const img of images) {
2052
2064
  const ext = img.media_type.split("/")[1] || "png";
@@ -2151,7 +2163,6 @@ async function createConversation3(env, participants, name) {
2151
2163
  if (!user) throw new Error(`User not found: ${p}`);
2152
2164
  ids.push(user.id);
2153
2165
  }
2154
- const mindName = env.VOLUTE_MIND;
2155
2166
  if (name) {
2156
2167
  const createData = await slackApi(token, "conversations.create", {
2157
2168
  name,
@@ -2166,31 +2177,11 @@ async function createConversation3(env, participants, name) {
2166
2177
  }
2167
2178
  const authData = await slackApi(token, "auth.test", {});
2168
2179
  const teamName = authData.team ?? "workspace";
2169
- const slug2 = `slack:${slugify(teamName)}/${slugify(name)}`;
2170
- if (mindName) {
2171
- writeChannelEntry(mindName, slug2, {
2172
- platformId: channelId,
2173
- platform: "slack",
2174
- name,
2175
- type: "channel"
2176
- });
2177
- }
2178
- return slug2;
2179
- }
2180
- const openData = await slackApi(token, "conversations.open", {
2181
- users: ids.join(",")
2182
- });
2183
- const platformId = openData.channel.id;
2184
- const slug = participants.length === 1 ? `slack:@${slugify(participants[0])}` : `slack:@${participants.map(slugify).sort().join(",")}`;
2185
- if (mindName) {
2186
- writeChannelEntry(mindName, slug, {
2187
- platformId,
2188
- platform: "slack",
2189
- name: participants.join(", "),
2190
- type: participants.length === 1 ? "dm" : "channel"
2191
- });
2180
+ const slug = `slack:${slugify(teamName)}/${slugify(name)}`;
2181
+ return slug;
2192
2182
  }
2193
- return slug;
2183
+ const openData = await slackApi(token, "conversations.open", { users: ids.join(",") });
2184
+ return `slack:${openData.channel.id}`;
2194
2185
  }
2195
2186
 
2196
2187
  // src/lib/channels/telegram.ts
@@ -2216,7 +2207,7 @@ async function read3(_env, _channelSlug, _limit) {
2216
2207
  }
2217
2208
  async function send3(env, channelSlug, message, images) {
2218
2209
  const token = requireToken3(env);
2219
- const chatId = resolveChannelId2(env, channelSlug);
2210
+ const chatId = resolveChannelId(channelSlug);
2220
2211
  if (images?.length) {
2221
2212
  const CAPTION_MAX = 1024;
2222
2213
  for (let i = 0; i < images.length; i++) {
@@ -2305,24 +2296,24 @@ function getDaemonConfig() {
2305
2296
  if (!existsSync5(configPath)) {
2306
2297
  throw new Error("Volute daemon is not running");
2307
2298
  }
2308
- let config;
2299
+ let config2;
2309
2300
  try {
2310
- config = JSON.parse(readFileSync5(configPath, "utf-8"));
2301
+ config2 = JSON.parse(readFileSync5(configPath, "utf-8"));
2311
2302
  } catch (err) {
2312
2303
  throw new Error(`Failed to parse ${configPath}: ${err}`);
2313
2304
  }
2314
- if (typeof config.port !== "number") {
2305
+ if (typeof config2.port !== "number") {
2315
2306
  throw new Error(`Invalid or missing port in ${configPath}`);
2316
2307
  }
2317
2308
  const url = new URL("http://localhost");
2318
- url.hostname = config.hostname || "localhost";
2319
- url.port = String(config.port);
2320
- return { url: url.origin, token: config.token };
2309
+ url.hostname = config2.hostname || "localhost";
2310
+ url.port = String(config2.port);
2311
+ return { url: url.origin, token: config2.token };
2321
2312
  }
2322
2313
  async function read4(env, channelSlug, limit) {
2323
2314
  const mindName = env.VOLUTE_MIND;
2324
2315
  if (!mindName) throw new Error("VOLUTE_MIND not set");
2325
- const conversationId = resolveChannelId2(env, channelSlug);
2316
+ const conversationId = resolveChannelId(channelSlug);
2326
2317
  const { url, token } = getDaemonConfig();
2327
2318
  const headers = { Origin: url };
2328
2319
  if (token) headers.Authorization = `Bearer ${token}`;
@@ -2345,7 +2336,7 @@ async function read4(env, channelSlug, limit) {
2345
2336
  async function send4(env, channelSlug, message, images) {
2346
2337
  const mindName = env.VOLUTE_MIND;
2347
2338
  if (!mindName) throw new Error("VOLUTE_MIND not set");
2348
- const conversationId = resolveChannelId2(env, channelSlug);
2339
+ const conversationId = resolveChannelId(channelSlug);
2349
2340
  const { url, token } = getDaemonConfig();
2350
2341
  const headers = {
2351
2342
  "Content-Type": "application/json",
@@ -2451,7 +2442,7 @@ async function createConversation5(env, participants, name) {
2451
2442
  throw new Error(data.error ?? `Failed to create conversation: ${res.status}`);
2452
2443
  }
2453
2444
  const conv = await res.json();
2454
- return `volute:${conv.id}`;
2445
+ return conv.id;
2455
2446
  }
2456
2447
 
2457
2448
  // src/lib/channels.ts
@@ -2459,121 +2450,40 @@ var CHANNELS = {
2459
2450
  volute: {
2460
2451
  name: "volute",
2461
2452
  displayName: "Volute",
2462
- showToolCalls: true,
2463
2453
  builtIn: true,
2464
2454
  driver: volute_exports
2465
2455
  },
2466
2456
  discord: {
2467
2457
  name: "discord",
2468
2458
  displayName: "Discord",
2469
- showToolCalls: false,
2470
2459
  driver: discord_exports
2471
2460
  },
2472
2461
  slack: {
2473
2462
  name: "slack",
2474
2463
  displayName: "Slack",
2475
- showToolCalls: false,
2476
2464
  driver: slack_exports
2477
2465
  },
2478
2466
  telegram: {
2479
2467
  name: "telegram",
2480
2468
  displayName: "Telegram",
2481
- showToolCalls: false,
2482
2469
  driver: telegram_exports
2483
2470
  },
2484
- mail: { name: "mail", displayName: "Email", showToolCalls: false },
2485
- system: { name: "system", displayName: "System", showToolCalls: false }
2471
+ mail: { name: "mail", displayName: "Email" },
2472
+ system: { name: "system", displayName: "System" }
2486
2473
  };
2487
2474
  function getChannelDriver(platform) {
2488
2475
  return CHANNELS[platform]?.driver ?? null;
2489
2476
  }
2490
- function resolveChannelId2(env, slug) {
2491
- const mindName = env.VOLUTE_MIND;
2492
- if (!mindName) {
2493
- const colonIdx = slug.indexOf(":");
2494
- return colonIdx !== -1 ? slug.slice(colonIdx + 1) : slug;
2495
- }
2496
- return resolveChannelId(mindName, slug);
2477
+ function resolveChannelId(slug) {
2478
+ const colonIdx = slug.indexOf(":");
2479
+ return colonIdx !== -1 ? slug.slice(colonIdx + 1) : slug;
2497
2480
  }
2498
2481
 
2499
2482
  // src/web/api/channels.ts
2500
2483
  function buildEnv(name) {
2501
2484
  return { ...loadMergedEnv(name), VOLUTE_MIND: name, VOLUTE_MIND_DIR: mindDir(name) };
2502
2485
  }
2503
- var app5 = new Hono5().post("/:name/channels/send", requireSelf(), async (c) => {
2504
- const name = c.req.param("name");
2505
- if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
2506
- const { platform, uri, message, images, sender } = await c.req.json();
2507
- const driver = getChannelDriver(platform);
2508
- if (!driver) return c.json({ error: `No driver for platform: ${platform}` }, 400);
2509
- const env = buildEnv(name);
2510
- if (sender) env.VOLUTE_SENDER = sender;
2511
- const mindSession = c.get("mindSession");
2512
- if (mindSession) env.VOLUTE_SESSION = mindSession;
2513
- try {
2514
- await driver.send(env, uri, message, images);
2515
- return c.json({ ok: true });
2516
- } catch (err) {
2517
- return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
2518
- }
2519
- }).get("/:name/channels/read", async (c) => {
2520
- const name = c.req.param("name");
2521
- if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
2522
- const platform = c.req.query("platform");
2523
- const uri = c.req.query("uri");
2524
- const limit = parseInt(c.req.query("limit") ?? "20", 10) || 20;
2525
- if (!platform || !uri) return c.json({ error: "platform and uri required" }, 400);
2526
- const driver = getChannelDriver(platform);
2527
- if (!driver) return c.json({ error: `No driver for platform: ${platform}` }, 400);
2528
- const env = buildEnv(name);
2529
- try {
2530
- const output = await driver.read(env, uri, limit);
2531
- return c.text(output);
2532
- } catch (err) {
2533
- return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
2534
- }
2535
- }).get("/:name/channels/list", async (c) => {
2536
- const name = c.req.param("name");
2537
- if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
2538
- const platform = c.req.query("platform");
2539
- const platforms = platform ? [platform] : Object.keys(CHANNELS);
2540
- const env = buildEnv(name);
2541
- const results = {};
2542
- for (const p of platforms) {
2543
- const driver = getChannelDriver(p);
2544
- if (!driver?.listConversations) continue;
2545
- try {
2546
- const convs = await driver.listConversations(env);
2547
- for (const conv of convs) {
2548
- writeChannelEntry(name, conv.id, {
2549
- platformId: conv.platformId,
2550
- platform: p,
2551
- name: conv.name,
2552
- type: conv.type
2553
- });
2554
- }
2555
- results[p] = convs;
2556
- } catch (err) {
2557
- results[p] = [{ error: err instanceof Error ? err.message : String(err) }];
2558
- }
2559
- }
2560
- return c.json(results);
2561
- }).get("/:name/channels/users", async (c) => {
2562
- const name = c.req.param("name");
2563
- if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
2564
- const platform = c.req.query("platform");
2565
- if (!platform) return c.json({ error: "platform required" }, 400);
2566
- const driver = getChannelDriver(platform);
2567
- if (!driver?.listUsers)
2568
- return c.json({ error: `Platform ${platform} does not support listing users` }, 400);
2569
- const env = buildEnv(name);
2570
- try {
2571
- const users2 = await driver.listUsers(env);
2572
- return c.json(users2);
2573
- } catch (err) {
2574
- return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
2575
- }
2576
- }).post("/:name/channels/create", requireSelf(), async (c) => {
2486
+ var app5 = new Hono5().post("/:name/channels/create", requireSelf(), async (c) => {
2577
2487
  const name = c.req.param("name");
2578
2488
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
2579
2489
  const {
@@ -2590,16 +2500,44 @@ var app5 = new Hono5().post("/:name/channels/send", requireSelf(), async (c) =>
2590
2500
  if (sender) env.VOLUTE_SENDER = sender;
2591
2501
  try {
2592
2502
  const slug = await driver.createConversation(env, participants, convName);
2593
- return c.json({ slug });
2503
+ return c.json({ slug, conversationId: slug });
2594
2504
  } catch (err) {
2595
2505
  return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
2596
2506
  }
2597
2507
  });
2598
2508
  var channels_default = app5;
2599
2509
 
2600
- // src/web/api/env.ts
2510
+ // src/web/api/config.ts
2601
2511
  import { Hono as Hono6 } from "hono";
2602
- var app6 = new Hono6().get("/:name/env", async (c) => {
2512
+ var config = new Hono6();
2513
+ config.get("/models", (c) => {
2514
+ const enabled = new Set(getEnabledModels());
2515
+ const all = getAvailableModels();
2516
+ const models = all.map((m) => ({
2517
+ id: m.id,
2518
+ name: m.name,
2519
+ provider: m.provider,
2520
+ enabled: enabled.has(m.id)
2521
+ }));
2522
+ return c.json(models);
2523
+ });
2524
+ config.get("/providers", (c) => {
2525
+ const configured = getConfiguredProviders();
2526
+ return c.json(configured.map((id) => ({ id, configured: true })));
2527
+ });
2528
+ config.get("/status", (c) => {
2529
+ const globalConfig = readGlobalConfig();
2530
+ return c.json({
2531
+ name: globalConfig.name ?? "Volute",
2532
+ spiritModel: globalConfig.spiritModel ?? null,
2533
+ setupComplete: globalConfig.setupCompleted ?? false
2534
+ });
2535
+ });
2536
+ var config_default = config;
2537
+
2538
+ // src/web/api/env.ts
2539
+ import { Hono as Hono7 } from "hono";
2540
+ var app6 = new Hono7().get("/:name/env", async (c) => {
2603
2541
  const name = c.req.param("name");
2604
2542
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
2605
2543
  const shared = readEnv(sharedEnvPath());
@@ -2642,7 +2580,7 @@ var app6 = new Hono6().get("/:name/env", async (c) => {
2642
2580
  writeEnv(path, env);
2643
2581
  return c.json({ ok: true });
2644
2582
  });
2645
- var sharedEnvApp = new Hono6().get("/", (c) => {
2583
+ var sharedEnvApp = new Hono7().get("/", (c) => {
2646
2584
  return c.json(readEnv(sharedEnvPath()));
2647
2585
  }).put("/:key", requireAdmin, async (c) => {
2648
2586
  const key = c.req.param("key");
@@ -2672,8 +2610,8 @@ var sharedEnvApp = new Hono6().get("/", (c) => {
2672
2610
  var env_default = app6;
2673
2611
 
2674
2612
  // src/web/api/extensions.ts
2675
- import { Hono as Hono7 } from "hono";
2676
- var app7 = new Hono7().get("/", (c) => {
2613
+ import { Hono as Hono8 } from "hono";
2614
+ var app7 = new Hono8().get("/", (c) => {
2677
2615
  return c.json(getLoadedExtensions());
2678
2616
  });
2679
2617
  var extensions_default = app7;
@@ -2681,18 +2619,18 @@ var extensions_default = app7;
2681
2619
  // src/web/api/file-sharing.ts
2682
2620
  import { readFileSync as readFileSync6, statSync } from "fs";
2683
2621
  import { resolve as resolve6 } from "path";
2684
- import { Hono as Hono8 } from "hono";
2622
+ import { Hono as Hono9 } from "hono";
2685
2623
  async function notifyMind(mindName, message) {
2686
2624
  const entry = await findMind(mindName);
2687
2625
  if (!entry) return;
2688
2626
  try {
2689
- const { sendSystemMessage } = await import("./system-chat-B43GIXQU.js");
2627
+ const { sendSystemMessage } = await import("./system-chat-NPYFYZVI.js");
2690
2628
  await sendSystemMessage(mindName, message);
2691
2629
  } catch (err) {
2692
2630
  logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
2693
2631
  }
2694
2632
  }
2695
- var app8 = new Hono8().post("/:name/files/send", requireSelf(), async (c) => {
2633
+ var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
2696
2634
  const senderName = c.req.param("name");
2697
2635
  const senderEntry = await findMind(senderName);
2698
2636
  if (!senderEntry) return c.json({ error: "Sender mind not found" }, 404);
@@ -2809,11 +2747,9 @@ var app8 = new Hono8().post("/:name/files/send", requireSelf(), async (c) => {
2809
2747
  var file_sharing_default = app8;
2810
2748
 
2811
2749
  // src/web/api/files.ts
2812
- import { existsSync as existsSync6 } from "fs";
2813
- import { readdir, readFile, realpath, stat } from "fs/promises";
2750
+ import { readFile, realpath, stat } from "fs/promises";
2814
2751
  import { extname as extname2, resolve as resolve7 } from "path";
2815
- import { Hono as Hono9 } from "hono";
2816
- var ALLOWED_FILES = /* @__PURE__ */ new Set(["SOUL.md", "MEMORY.md", "CLAUDE.md", "VOLUTE.md"]);
2752
+ import { Hono as Hono10 } from "hono";
2817
2753
  var AVATAR_MIME2 = {
2818
2754
  ".png": "image/png",
2819
2755
  ".jpg": "image/jpeg",
@@ -2822,18 +2758,18 @@ var AVATAR_MIME2 = {
2822
2758
  ".webp": "image/webp"
2823
2759
  };
2824
2760
  var MAX_AVATAR_SIZE2 = 2 * 1024 * 1024;
2825
- var app9 = new Hono9().get("/:name/avatar", async (c) => {
2761
+ var app9 = new Hono10().get("/:name/avatar", async (c) => {
2826
2762
  const name = c.req.param("name");
2827
2763
  const entry = await findMind(name);
2828
2764
  if (!entry) return c.json({ error: "Mind not found" }, 404);
2829
2765
  const dir = mindDir(name);
2830
- const config = readVoluteConfig(dir);
2831
- if (!config?.profile?.avatar) return c.json({ error: "No avatar configured" }, 404);
2832
- const ext = extname2(config.profile.avatar).toLowerCase();
2766
+ const config2 = readVoluteConfig(dir);
2767
+ if (!config2?.profile?.avatar) return c.json({ error: "No avatar configured" }, 404);
2768
+ const ext = extname2(config2.profile.avatar).toLowerCase();
2833
2769
  const mime = AVATAR_MIME2[ext];
2834
2770
  if (!mime) return c.json({ error: "Invalid avatar extension" }, 400);
2835
2771
  const homeDir = resolve7(dir, "home");
2836
- const avatarPath = resolve7(homeDir, config.profile.avatar);
2772
+ const avatarPath = resolve7(homeDir, config2.profile.avatar);
2837
2773
  if (!avatarPath.startsWith(`${homeDir}/`)) return c.json({ error: "Invalid avatar path" }, 400);
2838
2774
  let realAvatarPath;
2839
2775
  try {
@@ -2857,40 +2793,15 @@ var app9 = new Hono9().get("/:name/avatar", async (c) => {
2857
2793
  } catch {
2858
2794
  return c.json({ error: "Failed to read avatar file" }, 500);
2859
2795
  }
2860
- }).get("/:name/files", async (c) => {
2861
- const name = c.req.param("name");
2862
- const entry = await findMind(name);
2863
- if (!entry) return c.json({ error: "Mind not found" }, 404);
2864
- const dir = mindDir(name);
2865
- const homeDir = resolve7(dir, "home");
2866
- if (!existsSync6(homeDir)) return c.json({ error: "Home directory missing" }, 404);
2867
- const allFiles = await readdir(homeDir);
2868
- const files = allFiles.filter((f) => f.endsWith(".md") && ALLOWED_FILES.has(f));
2869
- return c.json(files);
2870
- }).get("/:name/files/:filename", async (c) => {
2871
- const name = c.req.param("name");
2872
- const filename = c.req.param("filename");
2873
- if (!ALLOWED_FILES.has(filename)) {
2874
- return c.json({ error: "File not allowed" }, 403);
2875
- }
2876
- const entry = await findMind(name);
2877
- if (!entry) return c.json({ error: "Mind not found" }, 404);
2878
- const dir = mindDir(name);
2879
- const filePath = resolve7(dir, "home", filename);
2880
- if (!existsSync6(filePath)) {
2881
- return c.json({ error: "File not found" }, 404);
2882
- }
2883
- const content = await readFile(filePath, "utf-8");
2884
- return c.json({ filename, content });
2885
2796
  });
2886
2797
  var files_default = app9;
2887
2798
 
2888
2799
  // src/web/api/keys.ts
2889
- import { Hono as Hono10 } from "hono";
2800
+ import { Hono as Hono11 } from "hono";
2890
2801
 
2891
2802
  // src/lib/identity.ts
2892
2803
  import { createHash, generateKeyPairSync, sign, verify } from "crypto";
2893
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
2804
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
2894
2805
  import { resolve as resolve8 } from "path";
2895
2806
  function generateIdentity(mindDir2) {
2896
2807
  const identityDir = resolve8(mindDir2, ".mind/identity");
@@ -2903,20 +2814,20 @@ function generateIdentity(mindDir2) {
2903
2814
  const publicPath = resolve8(identityDir, "public.pem");
2904
2815
  writeFileSync4(privatePath, privateKey, { mode: 384 });
2905
2816
  writeFileSync4(publicPath, publicKey, { mode: 420 });
2906
- const config = readVoluteConfig(mindDir2) ?? {};
2907
- config.identity = {
2817
+ const config2 = readVoluteConfig(mindDir2) ?? {};
2818
+ config2.identity = {
2908
2819
  privateKey: ".mind/identity/private.pem",
2909
2820
  publicKey: ".mind/identity/public.pem"
2910
2821
  };
2911
- writeVoluteConfig(mindDir2, config);
2822
+ writeVoluteConfig(mindDir2, config2);
2912
2823
  return { publicKeyPem: publicKey, privateKeyPem: privateKey };
2913
2824
  }
2914
2825
  function getPublicKey(mindDir2) {
2915
- const config = readVoluteConfig(mindDir2);
2916
- const relPath = config?.identity?.publicKey;
2826
+ const config2 = readVoluteConfig(mindDir2);
2827
+ const relPath = config2?.identity?.publicKey;
2917
2828
  if (!relPath) return null;
2918
2829
  const fullPath = resolve8(mindDir2, relPath);
2919
- if (!existsSync7(fullPath)) return null;
2830
+ if (!existsSync6(fullPath)) return null;
2920
2831
  return readFileSync7(fullPath, "utf-8");
2921
2832
  }
2922
2833
  function getFingerprint(publicKeyPem) {
@@ -2946,7 +2857,7 @@ async function publishPublicKey(mindName, publicKeyPem) {
2946
2857
  }
2947
2858
 
2948
2859
  // src/web/api/keys.ts
2949
- var app10 = new Hono10().get("/:fingerprint", async (c) => {
2860
+ var app10 = new Hono11().get("/:fingerprint", async (c) => {
2950
2861
  const fingerprint = c.req.param("fingerprint");
2951
2862
  for (const entry of await readRegistry()) {
2952
2863
  try {
@@ -2964,16 +2875,16 @@ var keys_default = app10;
2964
2875
 
2965
2876
  // src/web/api/logs.ts
2966
2877
  import { spawn as spawn2 } from "child_process";
2967
- import { existsSync as existsSync8 } from "fs";
2878
+ import { existsSync as existsSync7 } from "fs";
2968
2879
  import { resolve as resolve9 } from "path";
2969
- import { Hono as Hono11 } from "hono";
2880
+ import { Hono as Hono12 } from "hono";
2970
2881
  import { streamSSE as streamSSE3 } from "hono/streaming";
2971
- var app11 = new Hono11().get("/:name/logs", async (c) => {
2882
+ var app11 = new Hono12().get("/:name/logs", async (c) => {
2972
2883
  const name = c.req.param("name");
2973
2884
  const entry = await findMind(name);
2974
2885
  if (!entry) return c.json({ error: "Mind not found" }, 404);
2975
2886
  const logFile = resolve9(stateDir(name), "logs", "mind.log");
2976
- if (!existsSync8(logFile)) {
2887
+ if (!existsSync7(logFile)) {
2977
2888
  return c.json({ error: "No log file found" }, 404);
2978
2889
  }
2979
2890
  return streamSSE3(c, async (stream) => {
@@ -3001,7 +2912,7 @@ var app11 = new Hono11().get("/:name/logs", async (c) => {
3001
2912
  const entry = await findMind(name);
3002
2913
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3003
2914
  const logFile = resolve9(stateDir(name), "logs", "mind.log");
3004
- if (!existsSync8(logFile)) {
2915
+ if (!existsSync7(logFile)) {
3005
2916
  return c.json({ error: "No log file found" }, 404);
3006
2917
  }
3007
2918
  const nParam = parseInt(c.req.query("n") ?? "50", 10);
@@ -3020,9 +2931,9 @@ var logs_default = app11;
3020
2931
 
3021
2932
  // src/web/api/mind-skills.ts
3022
2933
  import { zValidator as zValidator4 } from "@hono/zod-validator";
3023
- import { Hono as Hono12 } from "hono";
2934
+ import { Hono as Hono13 } from "hono";
3024
2935
  import { z as z4 } from "zod";
3025
- var app12 = new Hono12().get("/:name/skills", async (c) => {
2936
+ var app12 = new Hono13().get("/:name/skills", async (c) => {
3026
2937
  const name = c.req.param("name");
3027
2938
  const entry = await findMind(name);
3028
2939
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -3102,7 +3013,7 @@ var mind_skills_default = app12;
3102
3013
  // src/web/api/minds.ts
3103
3014
  import {
3104
3015
  cpSync,
3105
- existsSync as existsSync10,
3016
+ existsSync as existsSync9,
3106
3017
  mkdirSync as mkdirSync6,
3107
3018
  readdirSync as readdirSync2,
3108
3019
  readFileSync as readFileSync10,
@@ -3112,7 +3023,7 @@ import {
3112
3023
  import { resolve as resolve12 } from "path";
3113
3024
  import { zValidator as zValidator5 } from "@hono/zod-validator";
3114
3025
  import { and as and4, desc as desc3, eq as eq5, inArray, sql as sql2 } from "drizzle-orm";
3115
- import { Hono as Hono13 } from "hono";
3026
+ import { Hono as Hono14 } from "hono";
3116
3027
  import { z as z5 } from "zod";
3117
3028
 
3118
3029
  // src/lib/consolidate.ts
@@ -3554,7 +3465,7 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
3554
3465
  const transcript = buildTranscript(events);
3555
3466
  if (transcript.trim()) {
3556
3467
  const summaryPrompt = await getPrompt("turn_summary");
3557
- const aiResult = await aiComplete(summaryPrompt, transcript);
3468
+ const aiResult = await aiCompleteUtility(summaryPrompt, transcript);
3558
3469
  if (aiResult) {
3559
3470
  summaryText = aiResult;
3560
3471
  deterministic = false;
@@ -3626,7 +3537,7 @@ async function checkHealth(port) {
3626
3537
  }
3627
3538
 
3628
3539
  // src/lib/variant-cleanup.ts
3629
- import { existsSync as existsSync9, rmSync as rmSync3 } from "fs";
3540
+ import { existsSync as existsSync8, rmSync as rmSync3 } from "fs";
3630
3541
  async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
3631
3542
  if (opts?.stop) {
3632
3543
  try {
@@ -3635,10 +3546,10 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
3635
3546
  logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
3636
3547
  }
3637
3548
  }
3638
- const { findMind: findMind2 } = await import("./registry-ASXCQCNH.js");
3549
+ const { findMind: findMind2 } = await import("./registry-PJ4S5PHQ.js");
3639
3550
  const variantEntry = await findMind2(variantName);
3640
3551
  const branchName = variantEntry?.branch ?? variantName;
3641
- if (existsSync9(variantPath)) {
3552
+ if (existsSync8(variantPath)) {
3642
3553
  try {
3643
3554
  await gitExec(["worktree", "remove", "--force", variantPath], { cwd: projectRoot });
3644
3555
  } catch {
@@ -3689,7 +3600,7 @@ async function getMindStatus(name, port) {
3689
3600
  const manager = getMindManager();
3690
3601
  let status = "stopped";
3691
3602
  try {
3692
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-TPS6OGCA.js");
3603
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
3693
3604
  if (getSleepManagerIfReady2()?.isSleeping(name)) {
3694
3605
  status = "sleeping";
3695
3606
  }
@@ -3699,24 +3610,22 @@ async function getMindStatus(name, port) {
3699
3610
  const health = await checkHealth(port);
3700
3611
  status = health.ok ? "running" : "starting";
3701
3612
  }
3702
- const config = readVoluteConfig(mindDir(name));
3703
- const channelConfig = config?.channels;
3613
+ const config2 = readVoluteConfig(mindDir(name));
3704
3614
  const channels = [];
3705
3615
  for (const [, provider] of Object.entries(CHANNELS)) {
3706
3616
  if (!provider.builtIn) continue;
3707
3617
  channels.push({
3708
3618
  name: provider.name,
3709
3619
  displayName: provider.displayName,
3710
- status: status === "running" ? "connected" : "disconnected",
3711
- showToolCalls: channelConfig?.[provider.name]?.showToolCalls ?? provider.showToolCalls
3620
+ status: status === "running" ? "connected" : "disconnected"
3712
3621
  });
3713
3622
  }
3714
3623
  return {
3715
3624
  status,
3716
3625
  channels,
3717
- displayName: config?.profile?.displayName,
3718
- description: config?.profile?.description,
3719
- avatar: config?.profile?.avatar
3626
+ displayName: config2?.profile?.displayName,
3627
+ description: config2?.profile?.description,
3628
+ avatar: config2?.profile?.avatar
3720
3629
  };
3721
3630
  }
3722
3631
  var TEMPLATE_BRANCH = "volute/template";
@@ -3748,7 +3657,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
3748
3657
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
3749
3658
  } catch {
3750
3659
  }
3751
- if (existsSync10(tempWorktree)) {
3660
+ if (existsSync9(tempWorktree)) {
3752
3661
  rmSync4(tempWorktree, { recursive: true, force: true });
3753
3662
  }
3754
3663
  const templatesRoot = findTemplatesRoot();
@@ -3770,11 +3679,11 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
3770
3679
  }
3771
3680
  copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
3772
3681
  const initDir = resolve12(tempWorktree, ".init");
3773
- if (existsSync10(initDir)) {
3682
+ if (existsSync9(initDir)) {
3774
3683
  rmSync4(initDir, { recursive: true, force: true });
3775
3684
  }
3776
3685
  const homeDir = resolve12(tempWorktree, "home");
3777
- if (existsSync10(homeDir)) {
3686
+ if (existsSync9(homeDir)) {
3778
3687
  for (const entry of readdirSync2(homeDir)) {
3779
3688
  if (entry !== "VOLUTE.md") {
3780
3689
  rmSync4(resolve12(homeDir, entry), { recursive: true, force: true });
@@ -3792,7 +3701,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
3792
3701
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
3793
3702
  } catch {
3794
3703
  }
3795
- if (existsSync10(tempWorktree)) {
3704
+ if (existsSync9(tempWorktree)) {
3796
3705
  rmSync4(tempWorktree, { recursive: true, force: true });
3797
3706
  }
3798
3707
  rmSync4(composedDir, { recursive: true, force: true });
@@ -3823,9 +3732,54 @@ async function npmInstallAsMind(cwd, mindName) {
3823
3732
  await exec("npm", ["install"], { cwd });
3824
3733
  }
3825
3734
  }
3735
+ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariantName, upgradeBranch, template) {
3736
+ const mainStatus = (await gitExec(["status", "--porcelain"], { cwd: dir })).trim();
3737
+ if (mainStatus) {
3738
+ await gitExec(["add", "-A"], { cwd: dir });
3739
+ await gitExec(["commit", "-m", "Auto-commit before upgrade merge"], { cwd: dir });
3740
+ }
3741
+ await gitExec(["merge", upgradeBranch], { cwd: dir });
3742
+ try {
3743
+ await cleanupVariant(upgradeVariantName, dir, worktreeDir);
3744
+ } catch (err) {
3745
+ logger_default.warn(`failed to clean up upgrade worktree for ${mindName}`, logger_default.errorData(err));
3746
+ }
3747
+ try {
3748
+ await gitExec(["branch", "-D", upgradeBranch], { cwd: dir });
3749
+ } catch {
3750
+ }
3751
+ try {
3752
+ await setMindTemplateHash(mindName, computeTemplateHash(template));
3753
+ } catch (err) {
3754
+ logger_default.warn(`failed to update template hash for ${mindName}`, logger_default.errorData(err));
3755
+ }
3756
+ try {
3757
+ await npmInstallAsMind(dir, mindName);
3758
+ } catch (err) {
3759
+ logger_default.warn(`npm install failed after upgrade merge for ${mindName}`, logger_default.errorData(err));
3760
+ return {
3761
+ ok: true,
3762
+ warning: `Upgrade merged but npm install failed: ${err instanceof Error ? err.message : String(err)}. You may need to run npm install manually.`
3763
+ };
3764
+ }
3765
+ const manager = getMindManager();
3766
+ try {
3767
+ if (manager.isRunning(mindName)) {
3768
+ await manager.stopMind(mindName);
3769
+ }
3770
+ manager.setPendingContext(mindName, { type: "upgraded" });
3771
+ await manager.startMind(mindName);
3772
+ } catch (e) {
3773
+ return {
3774
+ ok: true,
3775
+ warning: `Upgrade merged but mind restart failed: ${e instanceof Error ? e.message : String(e)}`
3776
+ };
3777
+ }
3778
+ return { ok: true };
3779
+ }
3826
3780
  async function importFromArchive(c, tempDir, nameOverride, manifest) {
3827
3781
  const extractedMindDir = resolve12(tempDir, "mind");
3828
- if (!existsSync10(extractedMindDir)) {
3782
+ if (!existsSync9(extractedMindDir)) {
3829
3783
  return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
3830
3784
  }
3831
3785
  if (!manifest?.includes || !manifest.name || !manifest.template) {
@@ -3843,7 +3797,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
3843
3797
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
3844
3798
  ensureVoluteHome();
3845
3799
  const dest = mindDir(name);
3846
- if (existsSync10(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3800
+ if (existsSync9(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3847
3801
  try {
3848
3802
  cpSync(extractedMindDir, dest, { recursive: true });
3849
3803
  if (!manifest.includes.identity) {
@@ -3851,12 +3805,8 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
3851
3805
  }
3852
3806
  const state = stateDir(name);
3853
3807
  mkdirSync6(state, { recursive: true });
3854
- const channelsJson = resolve12(tempDir, "state/channels.json");
3855
- if (existsSync10(channelsJson)) {
3856
- cpSync(channelsJson, resolve12(state, "channels.json"));
3857
- }
3858
3808
  const envJson = resolve12(tempDir, "state/env.json");
3859
- if (existsSync10(envJson)) {
3809
+ if (existsSync9(envJson)) {
3860
3810
  cpSync(envJson, resolve12(state, "env.json"));
3861
3811
  }
3862
3812
  const port = await nextPort();
@@ -3873,7 +3823,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
3873
3823
  await npmInstallAsMind(dest, name);
3874
3824
  await importHistoryFromArchive(name, tempDir);
3875
3825
  importSessionsFromArchive(dest, tempDir);
3876
- if (!existsSync10(resolve12(dest, ".git"))) {
3826
+ if (!existsSync9(resolve12(dest, ".git"))) {
3877
3827
  try {
3878
3828
  const env = isIsolationEnabled() ? { ...process.env, HOME: resolve12(dest, "home") } : void 0;
3879
3829
  await gitExec(["init"], { cwd: dest, mindName: name, env });
@@ -3889,7 +3839,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
3889
3839
  rmSync4(tempDir, { recursive: true, force: true });
3890
3840
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
3891
3841
  } catch (err) {
3892
- if (existsSync10(dest)) rmSync4(dest, { recursive: true, force: true });
3842
+ if (existsSync9(dest)) rmSync4(dest, { recursive: true, force: true });
3893
3843
  try {
3894
3844
  await removeMind(name);
3895
3845
  } catch (cleanupErr) {
@@ -3906,7 +3856,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3906
3856
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
3907
3857
  ensureVoluteHome();
3908
3858
  const dest = mindDir(name);
3909
- if (existsSync10(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3859
+ if (existsSync9(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3910
3860
  const templatesRoot = findTemplatesRoot();
3911
3861
  const { composedDir, manifest: templateManifest } = composeTemplate(
3912
3862
  templatesRoot,
@@ -3916,34 +3866,30 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3916
3866
  copyTemplateToDir(composedDir, dest, name, templateManifest);
3917
3867
  applyInitFiles(dest);
3918
3868
  const extractedHome = resolve12(extractedMindDir, "home");
3919
- if (existsSync10(extractedHome)) {
3869
+ if (existsSync9(extractedHome)) {
3920
3870
  cpSync(extractedHome, resolve12(dest, "home"), { recursive: true });
3921
3871
  }
3922
3872
  const extractedMindInternal = resolve12(extractedMindDir, ".mind");
3923
- if (existsSync10(extractedMindInternal)) {
3873
+ if (existsSync9(extractedMindInternal)) {
3924
3874
  cpSync(extractedMindInternal, resolve12(dest, ".mind"), { recursive: true });
3925
3875
  }
3926
3876
  const identityDir = resolve12(dest, ".mind/identity");
3927
3877
  let publicKeyPem;
3928
- if (!manifest.includes.identity || !existsSync10(resolve12(identityDir, "private.pem"))) {
3878
+ if (!manifest.includes.identity || !existsSync9(resolve12(identityDir, "private.pem"))) {
3929
3879
  ({ publicKeyPem } = generateIdentity(dest));
3930
3880
  } else {
3931
3881
  publicKeyPem = readFileSync10(resolve12(identityDir, "public.pem"), "utf-8");
3932
3882
  }
3933
3883
  const promptsPath = resolve12(dest, "home/.config/prompts.json");
3934
- if (!existsSync10(promptsPath)) {
3884
+ if (!existsSync9(promptsPath)) {
3935
3885
  const mindPrompts = await getMindPromptDefaults();
3936
3886
  writeFileSync7(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
3937
3887
  `);
3938
3888
  }
3939
3889
  const state = stateDir(name);
3940
3890
  mkdirSync6(state, { recursive: true });
3941
- const channelsJson = resolve12(tempDir, "state/channels.json");
3942
- if (existsSync10(channelsJson)) {
3943
- cpSync(channelsJson, resolve12(state, "channels.json"));
3944
- }
3945
3891
  const envJson = resolve12(tempDir, "state/env.json");
3946
- if (existsSync10(envJson)) {
3892
+ if (existsSync9(envJson)) {
3947
3893
  cpSync(envJson, resolve12(state, "env.json"));
3948
3894
  }
3949
3895
  const port = await nextPort();
@@ -3996,7 +3942,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3996
3942
  ...skillWarnings.length > 0 && { skillWarnings }
3997
3943
  });
3998
3944
  } catch (err) {
3999
- if (existsSync10(dest)) rmSync4(dest, { recursive: true, force: true });
3945
+ if (existsSync9(dest)) rmSync4(dest, { recursive: true, force: true });
4000
3946
  try {
4001
3947
  await removeMind(name);
4002
3948
  } catch (cleanupErr) {
@@ -4010,7 +3956,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4010
3956
  }
4011
3957
  async function importHistoryFromArchive(name, tempDir) {
4012
3958
  const historyJsonl = resolve12(tempDir, "history.jsonl");
4013
- if (!existsSync10(historyJsonl)) return;
3959
+ if (!existsSync9(historyJsonl)) return;
4014
3960
  try {
4015
3961
  const db = await getDb();
4016
3962
  const lines = readFileSync10(historyJsonl, "utf-8").trim().split("\n");
@@ -4050,7 +3996,7 @@ async function importHistoryFromArchive(name, tempDir) {
4050
3996
  }
4051
3997
  function importSessionsFromArchive(dest, tempDir) {
4052
3998
  const sessionsDir = resolve12(tempDir, "sessions");
4053
- if (!existsSync10(sessionsDir)) return;
3999
+ if (!existsSync9(sessionsDir)) return;
4054
4000
  try {
4055
4001
  const destSessions = resolve12(dest, ".mind/sessions");
4056
4002
  mkdirSync6(destSessions, { recursive: true });
@@ -4068,17 +4014,29 @@ var createMindSchema = z5.object({
4068
4014
  description: z5.string().optional(),
4069
4015
  model: z5.string().optional(),
4070
4016
  seedSoul: z5.string().optional(),
4071
- skills: z5.array(z5.string()).optional()
4017
+ skills: z5.array(z5.string()).optional(),
4018
+ createdBy: z5.string().optional()
4072
4019
  });
4073
- var app13 = new Hono13().post("/", requireAdmin, zValidator5("json", createMindSchema), async (c) => {
4020
+ function formatTimeAgo(date) {
4021
+ const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
4022
+ if (seconds < 60) return "just now";
4023
+ const minutes = Math.floor(seconds / 60);
4024
+ if (minutes < 60) return `${minutes}m ago`;
4025
+ const hours = Math.floor(minutes / 60);
4026
+ if (hours < 24) return `${hours}h ago`;
4027
+ const days = Math.floor(hours / 24);
4028
+ return `${days}d ago`;
4029
+ }
4030
+ var app13 = new Hono14().post("/", requireAdminOrSystem, zValidator5("json", createMindSchema), async (c) => {
4074
4031
  const body = c.req.valid("json");
4075
- const { name, template = "claude" } = body;
4032
+ const { name } = body;
4033
+ const template = body.template ?? resolveTemplate(body.model);
4076
4034
  const nameErr = validateMindName(name);
4077
4035
  if (nameErr) return c.json({ error: nameErr }, 400);
4078
4036
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
4079
4037
  ensureVoluteHome();
4080
4038
  const dest = mindDir(name);
4081
- if (existsSync10(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4039
+ if (existsSync9(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4082
4040
  const templatesRoot = findTemplatesRoot();
4083
4041
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
4084
4042
  try {
@@ -4086,19 +4044,19 @@ var app13 = new Hono13().post("/", requireAdmin, zValidator5("json", createMindS
4086
4044
  applyInitFiles(dest);
4087
4045
  const { publicKeyPem } = generateIdentity(dest);
4088
4046
  {
4089
- const config = readVoluteConfig(dest);
4090
- if (!config) throw new Error("Failed to read volute.json after identity generation");
4047
+ const config2 = readVoluteConfig(dest);
4048
+ if (!config2) throw new Error("Failed to read volute.json after identity generation");
4091
4049
  if (body.description) {
4092
- config.profile = { ...config.profile, description: body.description };
4050
+ config2.profile = { ...config2.profile, description: body.description };
4093
4051
  }
4094
- if (!config.sleep) {
4095
- config.sleep = {
4052
+ if (!config2.sleep) {
4053
+ config2.sleep = {
4096
4054
  enabled: true,
4097
4055
  schedule: { sleep: "0 0 * * *", wake: "0 8 * * *" }
4098
4056
  };
4099
4057
  }
4100
- if (!config.schedules || config.schedules.length === 0) {
4101
- config.schedules = [
4058
+ if (!config2.schedules || config2.schedules.length === 0) {
4059
+ config2.schedules = [
4102
4060
  {
4103
4061
  id: "heartbeat",
4104
4062
  cron: "0 12,16,20 * * *",
@@ -4108,12 +4066,12 @@ var app13 = new Hono13().post("/", requireAdmin, zValidator5("json", createMindS
4108
4066
  }
4109
4067
  ];
4110
4068
  }
4111
- writeVoluteConfig(dest, config);
4069
+ writeVoluteConfig(dest, config2);
4112
4070
  }
4113
4071
  if (body.model) {
4114
4072
  const configPath = resolve12(dest, "home/.config/config.json");
4115
- const existing = existsSync10(configPath) ? JSON.parse(readFileSync10(configPath, "utf-8")) : {};
4116
- existing.model = body.model;
4073
+ const existing = existsSync9(configPath) ? JSON.parse(readFileSync10(configPath, "utf-8")) : {};
4074
+ existing.model = template === "pi" ? qualifyModelId(body.model) : unqualifyModelId(body.model);
4117
4075
  writeFileSync7(configPath, `${JSON.stringify(existing, null, 2)}
4118
4076
  `);
4119
4077
  }
@@ -4124,7 +4082,8 @@ var app13 = new Hono13().post("/", requireAdmin, zValidator5("json", createMindS
4124
4082
  `
4125
4083
  );
4126
4084
  const port = await nextPort();
4127
- await addMind(name, port, body.stage, template);
4085
+ const createdBy = body.createdBy ?? c.get("user")?.username;
4086
+ await addMind(name, port, body.stage, template, createdBy);
4128
4087
  try {
4129
4088
  await setMindTemplateHash(name, computeTemplateHash(template));
4130
4089
  } catch (err) {
@@ -4206,7 +4165,7 @@ The human who planted you described you as: "${body.description}"
4206
4165
  ...skillWarnings.length > 0 && { skillWarnings }
4207
4166
  });
4208
4167
  } catch (err) {
4209
- if (existsSync10(dest)) rmSync4(dest, { recursive: true, force: true });
4168
+ if (existsSync9(dest)) rmSync4(dest, { recursive: true, force: true });
4210
4169
  try {
4211
4170
  await removeMind(name);
4212
4171
  } catch {
@@ -4226,13 +4185,13 @@ The human who planted you described you as: "${body.description}"
4226
4185
  return importFromArchive(c, body.archivePath, body.name, body.manifest);
4227
4186
  }
4228
4187
  const wsDir = body.workspacePath;
4229
- if (!wsDir || !existsSync10(resolve12(wsDir, "SOUL.md")) || !existsSync10(resolve12(wsDir, "IDENTITY.md"))) {
4188
+ if (!wsDir || !existsSync9(resolve12(wsDir, "SOUL.md")) || !existsSync9(resolve12(wsDir, "IDENTITY.md"))) {
4230
4189
  return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
4231
4190
  }
4232
4191
  const soul = readFileSync10(resolve12(wsDir, "SOUL.md"), "utf-8");
4233
4192
  const identity = readFileSync10(resolve12(wsDir, "IDENTITY.md"), "utf-8");
4234
4193
  const userPath = resolve12(wsDir, "USER.md");
4235
- const user = existsSync10(userPath) ? readFileSync10(userPath, "utf-8") : "";
4194
+ const user = existsSync9(userPath) ? readFileSync10(userPath, "utf-8") : "";
4236
4195
  const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
4237
4196
  const template = body.template ?? "claude";
4238
4197
  const nameErr = validateMindName(name);
@@ -4252,7 +4211,7 @@ ${user.trimEnd()}
4252
4211
  ` : "";
4253
4212
  ensureVoluteHome();
4254
4213
  const dest = mindDir(name);
4255
- if (existsSync10(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4214
+ if (existsSync9(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4256
4215
  const templatesRoot = findTemplatesRoot();
4257
4216
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
4258
4217
  try {
@@ -4261,7 +4220,7 @@ ${user.trimEnd()}
4261
4220
  const { publicKeyPem: importPublicKey } = generateIdentity(dest);
4262
4221
  writeFileSync7(resolve12(dest, "home/SOUL.md"), mergedSoul);
4263
4222
  const wsMemoryPath = resolve12(wsDir, "MEMORY.md");
4264
- const hasMemory = existsSync10(wsMemoryPath);
4223
+ const hasMemory = existsSync9(wsMemoryPath);
4265
4224
  if (hasMemory) {
4266
4225
  const existingMemory = readFileSync10(wsMemoryPath, "utf-8");
4267
4226
  writeFileSync7(
@@ -4274,7 +4233,7 @@ ${user.trimEnd()}
4274
4233
  }
4275
4234
  const wsMemoryDir = resolve12(wsDir, "memory");
4276
4235
  let dailyLogCount = 0;
4277
- if (existsSync10(wsMemoryDir)) {
4236
+ if (existsSync9(wsMemoryDir)) {
4278
4237
  const destMemoryDir = resolve12(dest, "home/memory");
4279
4238
  const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
4280
4239
  for (const file of files) {
@@ -4303,7 +4262,7 @@ ${user.trimEnd()}
4303
4262
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
4304
4263
  await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
4305
4264
  const sessionFile = body.sessionPath ? resolve12(body.sessionPath) : findOpenClawSession(wsDir);
4306
- if (sessionFile && existsSync10(sessionFile)) {
4265
+ if (sessionFile && existsSync9(sessionFile)) {
4307
4266
  if (template === "pi") {
4308
4267
  importPiSession(sessionFile, dest);
4309
4268
  } else if (template === "claude") {
@@ -4325,7 +4284,7 @@ ${user.trimEnd()}
4325
4284
  );
4326
4285
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
4327
4286
  } catch (err) {
4328
- if (existsSync10(dest)) rmSync4(dest, { recursive: true, force: true });
4287
+ if (existsSync9(dest)) rmSync4(dest, { recursive: true, force: true });
4329
4288
  try {
4330
4289
  await removeMind(name);
4331
4290
  } catch {
@@ -4349,7 +4308,7 @@ ${user.trimEnd()}
4349
4308
  const minds = await Promise.all(
4350
4309
  entries.map(async (entry) => {
4351
4310
  const mindStatus = await getMindStatus(entry.name, entry.port);
4352
- const hasPages = existsSync10(resolve12(mindDir(entry.name), "home", "public", "pages"));
4311
+ const hasPages = existsSync9(resolve12(mindDir(entry.name), "home", "public", "pages"));
4353
4312
  return {
4354
4313
  ...entry,
4355
4314
  ...mindStatus,
@@ -4359,16 +4318,12 @@ ${user.trimEnd()}
4359
4318
  })
4360
4319
  );
4361
4320
  return c.json(minds);
4362
- }).get("/pages/sites", async (c) => {
4363
- return c.json(getCachedSites());
4364
- }).get("/pages/recent", async (c) => {
4365
- return c.json(getCachedRecentPages());
4366
4321
  }).get("/:name", async (c) => {
4367
4322
  const name = c.req.param("name");
4368
4323
  const entry = await findMind(name);
4369
4324
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4370
4325
  const dir = entry.dir ?? mindDir(entry.parent ?? name);
4371
- if (!existsSync10(dir)) return c.json({ error: "Mind directory missing" }, 404);
4326
+ if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
4372
4327
  const mindStatus = await getMindStatus(name, entry.port);
4373
4328
  const variants = await findVariants(name);
4374
4329
  const manager = getMindManager();
@@ -4382,7 +4337,7 @@ ${user.trimEnd()}
4382
4337
  return { name: s.name, port: s.port, status: variantStatus };
4383
4338
  })
4384
4339
  );
4385
- const hasPages = existsSync10(resolve12(mindDir(name), "home", "public", "pages"));
4340
+ const hasPages = existsSync9(resolve12(mindDir(name), "home", "public", "pages"));
4386
4341
  return c.json({ ...entry, ...mindStatus, variants: variantStatuses, hasPages });
4387
4342
  }).post("/:name/start", requireSelf(), async (c) => {
4388
4343
  const name = c.req.param("name");
@@ -4393,7 +4348,7 @@ ${user.trimEnd()}
4393
4348
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
4394
4349
  } else {
4395
4350
  const dir = mindDir(name);
4396
- if (!existsSync10(dir)) return c.json({ error: "Mind directory missing" }, 404);
4351
+ if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
4397
4352
  }
4398
4353
  if (getMindManager().isRunning(name)) {
4399
4354
  return c.json({ error: "Mind already running" }, 409);
@@ -4414,7 +4369,7 @@ ${user.trimEnd()}
4414
4369
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
4415
4370
  } else {
4416
4371
  const dir = mindDir(name);
4417
- if (!existsSync10(dir)) return c.json({ error: "Mind directory missing" }, 404);
4372
+ if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
4418
4373
  }
4419
4374
  let context;
4420
4375
  const contentType = c.req.header("content-type");
@@ -4429,7 +4384,7 @@ ${user.trimEnd()}
4429
4384
  const manager = getMindManager();
4430
4385
  try {
4431
4386
  if (context?.type === "reload") {
4432
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-TPS6OGCA.js");
4387
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
4433
4388
  const sleepState = getSleepManagerIfReady2()?.getState(name);
4434
4389
  if (sleepState?.sleeping) {
4435
4390
  logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
@@ -4449,7 +4404,7 @@ ${user.trimEnd()}
4449
4404
  const variantEntry = await findMind(mergeVariantName);
4450
4405
  if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
4451
4406
  const projectRoot = mindDir(baseName);
4452
- if (existsSync10(variantEntry.dir)) {
4407
+ if (existsSync9(variantEntry.dir)) {
4453
4408
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
4454
4409
  if (status) {
4455
4410
  try {
@@ -4525,7 +4480,7 @@ ${user.trimEnd()}
4525
4480
  const name = c.req.param("name");
4526
4481
  const entry = await findMind(name);
4527
4482
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4528
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-TPS6OGCA.js");
4483
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
4529
4484
  const sm = getSleepManagerIfReady2();
4530
4485
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4531
4486
  return c.json(sm.getState(name));
@@ -4533,7 +4488,7 @@ ${user.trimEnd()}
4533
4488
  const name = c.req.param("name");
4534
4489
  const entry = await findMind(name);
4535
4490
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4536
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-TPS6OGCA.js");
4491
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
4537
4492
  const sm = getSleepManagerIfReady2();
4538
4493
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4539
4494
  if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
@@ -4553,7 +4508,7 @@ ${user.trimEnd()}
4553
4508
  const name = c.req.param("name");
4554
4509
  const entry = await findMind(name);
4555
4510
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4556
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-TPS6OGCA.js");
4511
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
4557
4512
  const sm = getSleepManagerIfReady2();
4558
4513
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4559
4514
  const sleepState = sm.getState(name);
@@ -4568,7 +4523,7 @@ ${user.trimEnd()}
4568
4523
  const name = c.req.param("name");
4569
4524
  const entry = await findMind(name);
4570
4525
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4571
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-TPS6OGCA.js");
4526
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
4572
4527
  const sm = getSleepManagerIfReady2();
4573
4528
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4574
4529
  const flushed = await sm.flushQueuedMessages(name);
@@ -4606,10 +4561,10 @@ ${user.trimEnd()}
4606
4561
  await removeMind(name);
4607
4562
  await deleteMindUser2(name);
4608
4563
  const state = stateDir(name);
4609
- if (existsSync10(state)) {
4564
+ if (existsSync9(state)) {
4610
4565
  rmSync4(state, { recursive: true, force: true });
4611
4566
  }
4612
- if (force && existsSync10(dir)) {
4567
+ if (force && existsSync9(dir)) {
4613
4568
  rmSync4(dir, { recursive: true, force: true });
4614
4569
  deleteMindUser(name);
4615
4570
  }
@@ -4624,7 +4579,7 @@ ${user.trimEnd()}
4624
4579
  const entry = await findMind(mindName);
4625
4580
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4626
4581
  const dir = mindDir(mindName);
4627
- if (!existsSync10(dir)) return c.json({ error: "Mind directory missing" }, 404);
4582
+ if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
4628
4583
  let body = {};
4629
4584
  try {
4630
4585
  body = await c.req.json();
@@ -4635,14 +4590,14 @@ ${user.trimEnd()}
4635
4590
  const upgradeVariantName = `${mindName}-upgrade`;
4636
4591
  const worktreeDir = resolve12(dir, ".variants", UPGRADE_BRANCH);
4637
4592
  if (body.abort) {
4638
- if (!existsSync10(worktreeDir)) {
4593
+ if (!existsSync9(worktreeDir)) {
4639
4594
  return c.json({ error: "No upgrade in progress" }, 400);
4640
4595
  }
4641
4596
  try {
4642
4597
  try {
4643
4598
  const gitDirContent = readFileSync10(resolve12(worktreeDir, ".git"), "utf-8").trim();
4644
4599
  const gitDir = gitDirContent.replace("gitdir: ", "");
4645
- if (existsSync10(resolve12(gitDir, "MERGE_HEAD"))) {
4600
+ if (existsSync9(resolve12(gitDir, "MERGE_HEAD"))) {
4646
4601
  await gitExec(["merge", "--abort"], { cwd: worktreeDir });
4647
4602
  }
4648
4603
  } catch {
@@ -4661,7 +4616,7 @@ ${user.trimEnd()}
4661
4616
  }
4662
4617
  }
4663
4618
  if (body.continue) {
4664
- if (!existsSync10(worktreeDir)) {
4619
+ if (!existsSync9(worktreeDir)) {
4665
4620
  return c.json({ error: "No upgrade in progress" }, 400);
4666
4621
  }
4667
4622
  const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
@@ -4679,92 +4634,77 @@ ${user.trimEnd()}
4679
4634
  if (!msg.includes("nothing to commit") && !stderr.includes("nothing to commit") && !stdout.includes("nothing to commit"))
4680
4635
  throw e;
4681
4636
  }
4682
- chownMindDir(dir, mindName);
4683
4637
  try {
4684
- await npmInstallAsMind(worktreeDir, mindName);
4685
- const variantPort = await nextPort();
4686
- await addVariant(upgradeVariantName, mindName, variantPort, worktreeDir, UPGRADE_BRANCH);
4687
- await getMindManager().startMind(upgradeVariantName);
4688
- return c.json({
4689
- ok: true,
4690
- name: mindName,
4691
- variant: UPGRADE_BRANCH,
4692
- port: variantPort
4638
+ await gitExec(["add", "home/"], { cwd: worktreeDir });
4639
+ } catch (err) {
4640
+ logger_default.warn(`failed to re-add home files during upgrade for ${mindName}`, logger_default.errorData(err));
4641
+ }
4642
+ try {
4643
+ await gitExec(["diff", "--cached", "--quiet"], { cwd: worktreeDir });
4644
+ } catch {
4645
+ await gitExec(["commit", "-m", "re-add allowlisted home files"], {
4646
+ cwd: worktreeDir
4693
4647
  });
4648
+ }
4649
+ chownMindDir(dir, mindName);
4650
+ try {
4651
+ const result = await mergeUpgradeAndRestart(
4652
+ mindName,
4653
+ dir,
4654
+ worktreeDir,
4655
+ upgradeVariantName,
4656
+ UPGRADE_BRANCH,
4657
+ template
4658
+ );
4659
+ return c.json(result);
4694
4660
  } catch (err) {
4695
- await cleanupVariant(upgradeVariantName, dir, worktreeDir);
4696
4661
  return c.json(
4697
- { error: err instanceof Error ? err.message : "Failed to continue upgrade" },
4662
+ { error: err instanceof Error ? err.message : "Failed to merge upgrade" },
4698
4663
  500
4699
4664
  );
4700
4665
  }
4701
4666
  }
4702
4667
  if (body.accept) {
4703
- if (!existsSync10(worktreeDir)) {
4704
- return c.json({ error: "No upgrade in progress" }, 400);
4705
- }
4706
- const variantEntry = await findMind(upgradeVariantName);
4707
- if (!variantEntry) {
4708
- return c.json({ error: "Upgrade variant not found in DB" }, 400);
4709
- }
4710
- const status = (await gitExec(["status", "--porcelain"], { cwd: worktreeDir })).trim();
4711
- if (status) {
4668
+ if (existsSync9(worktreeDir)) {
4712
4669
  try {
4713
- await gitExec(["add", "-A"], { cwd: worktreeDir });
4714
- await gitExec(["commit", "-m", "Auto-commit before upgrade merge"], {
4715
- cwd: worktreeDir
4716
- });
4717
- } catch {
4718
- return c.json({ error: "Failed to auto-commit upgrade changes before merge" }, 500);
4670
+ await cleanupVariant(upgradeVariantName, dir, worktreeDir, { stop: true });
4671
+ } catch (err) {
4672
+ logger_default.warn(`failed to clean up legacy upgrade variant for ${mindName}`, logger_default.errorData(err));
4719
4673
  }
4720
- }
4721
- const mainStatus = (await gitExec(["status", "--porcelain"], { cwd: dir })).trim();
4722
- if (mainStatus) {
4723
4674
  try {
4724
- await gitExec(["add", "-A"], { cwd: dir });
4725
- await gitExec(["commit", "-m", "Auto-commit before upgrade merge"], { cwd: dir });
4726
- } catch (_e) {
4727
- return c.json({ error: "Failed to auto-commit main changes before merge" }, 500);
4675
+ await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
4676
+ } catch {
4728
4677
  }
4729
4678
  }
4679
+ return c.json({ error: "Upgrades now auto-merge. Run 'volute mind upgrade' again." }, 400);
4680
+ }
4681
+ if (body.diff) {
4730
4682
  try {
4731
- await gitExec(["merge", UPGRADE_BRANCH], { cwd: dir });
4732
- } catch {
4733
- return c.json({ error: "Merge failed. Resolve conflicts manually." }, 500);
4734
- }
4735
- await cleanupVariant(upgradeVariantName, dir, worktreeDir, { stop: true });
4736
- try {
4737
- await setMindTemplateHash(mindName, computeTemplateHash(template));
4683
+ if (!existsSync9(resolve12(dir, ".git"))) {
4684
+ return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
4685
+ }
4686
+ await updateTemplateBranch(dir, template, mindName);
4687
+ let diff;
4688
+ try {
4689
+ diff = await gitExec(["diff", "HEAD...volute/template"], { cwd: dir });
4690
+ } catch {
4691
+ diff = await gitExec(["diff", "HEAD", "volute/template"], { cwd: dir });
4692
+ }
4693
+ return c.json({ ok: true, diff: diff || "(no changes)" });
4738
4694
  } catch (err) {
4739
- logger_default.warn(`failed to update template hash for ${mindName}`, logger_default.errorData(err));
4695
+ return c.json(
4696
+ { error: err instanceof Error ? err.message : "Failed to generate diff" },
4697
+ 500
4698
+ );
4740
4699
  }
4741
- try {
4742
- await npmInstallAsMind(dir, mindName);
4743
- } catch (err) {
4744
- logger_default.warn(`npm install failed after upgrade merge for ${mindName}`, logger_default.errorData(err));
4745
- }
4746
- const manager = getMindManager();
4747
- try {
4748
- if (manager.isRunning(mindName)) {
4749
- await manager.stopMind(mindName);
4750
- }
4751
- manager.setPendingContext(mindName, { type: "merged", name: upgradeVariantName });
4752
- await manager.startMind(mindName);
4753
- } catch (e) {
4754
- return c.json({
4755
- ok: true,
4756
- warning: `Upgrade merged but mind restart failed: ${e instanceof Error ? e.message : String(e)}`
4757
- });
4758
- }
4759
- return c.json({ ok: true });
4760
4700
  }
4761
- if (existsSync10(worktreeDir)) {
4701
+ if (existsSync9(worktreeDir)) {
4762
4702
  return c.json(
4763
4703
  { error: "Upgrade variant already exists. Use continue or delete it first." },
4764
4704
  409
4765
4705
  );
4766
4706
  }
4767
- if (!existsSync10(resolve12(dir, ".git"))) {
4707
+ if (!existsSync9(resolve12(dir, ".git"))) {
4768
4708
  try {
4769
4709
  const env = isIsolationEnabled() ? { ...process.env, HOME: resolve12(dir, "home") } : void 0;
4770
4710
  await gitExec(["init"], { cwd: dir, mindName, env });
@@ -4787,7 +4727,7 @@ ${user.trimEnd()}
4787
4727
  await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
4788
4728
  } catch {
4789
4729
  }
4790
- if (!existsSync10(resolve12(dir, "home", "shared"))) {
4730
+ if (!existsSync9(resolve12(dir, "home", "shared"))) {
4791
4731
  try {
4792
4732
  await addSharedWorktree(mindName, dir);
4793
4733
  } catch (err) {
@@ -4799,7 +4739,7 @@ ${user.trimEnd()}
4799
4739
  }
4800
4740
  await updateTemplateBranch(dir, template, mindName);
4801
4741
  const parentDir = resolve12(dir, ".variants");
4802
- if (!existsSync10(parentDir)) {
4742
+ if (!existsSync9(parentDir)) {
4803
4743
  mkdirSync6(parentDir, { recursive: true });
4804
4744
  }
4805
4745
  await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
@@ -4850,22 +4790,22 @@ ${user.trimEnd()}
4850
4790
  });
4851
4791
  }
4852
4792
  try {
4853
- await npmInstallAsMind(worktreeDir, mindName);
4854
- const variantPort = await nextPort();
4855
- await addVariant(upgradeVariantName, mindName, variantPort, worktreeDir, UPGRADE_BRANCH);
4856
- await getMindManager().startMind(upgradeVariantName);
4857
- return c.json({
4858
- ok: true,
4859
- name: mindName,
4860
- variant: UPGRADE_BRANCH,
4861
- port: variantPort
4862
- });
4863
- } catch (err) {
4864
- await cleanupVariant(upgradeVariantName, dir, worktreeDir);
4865
- return c.json(
4866
- { error: err instanceof Error ? err.message : "Failed to complete upgrade" },
4867
- 500
4793
+ const result = await mergeUpgradeAndRestart(
4794
+ mindName,
4795
+ dir,
4796
+ worktreeDir,
4797
+ upgradeVariantName,
4798
+ UPGRADE_BRANCH,
4799
+ template
4868
4800
  );
4801
+ return c.json(result);
4802
+ } catch (err) {
4803
+ try {
4804
+ await cleanupVariant(upgradeVariantName, dir, worktreeDir);
4805
+ } catch (cleanupErr) {
4806
+ logger_default.warn(`cleanup failed after upgrade error for ${mindName}`, logger_default.errorData(cleanupErr));
4807
+ }
4808
+ return c.json({ error: err instanceof Error ? err.message : "Failed to merge upgrade" }, 500);
4869
4809
  }
4870
4810
  }).get("/:name/conversations", async (c) => {
4871
4811
  const name = c.req.param("name");
@@ -4928,13 +4868,13 @@ ${user.trimEnd()}
4928
4868
  const entry = await findMind(name);
4929
4869
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4930
4870
  const dir = mindDir(name);
4931
- if (!existsSync10(dir)) return c.json({ error: "Mind directory missing" }, 404);
4932
- let config = readVoluteConfig(dir);
4933
- if (!config && entry.template === "pi") {
4871
+ if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
4872
+ let config2 = readVoluteConfig(dir);
4873
+ if (!config2 && entry.template === "pi") {
4934
4874
  const piConfigPath = resolve12(dir, "home/.config/config.json");
4935
- if (existsSync10(piConfigPath)) {
4875
+ if (existsSync9(piConfigPath)) {
4936
4876
  try {
4937
- config = JSON.parse(readFileSync10(piConfigPath, "utf-8"));
4877
+ config2 = JSON.parse(readFileSync10(piConfigPath, "utf-8"));
4938
4878
  } catch {
4939
4879
  }
4940
4880
  }
@@ -4948,10 +4888,10 @@ ${user.trimEnd()}
4948
4888
  template: entry.template
4949
4889
  },
4950
4890
  config: {
4951
- model: config?.model ?? null,
4952
- thinkingLevel: config?.thinkingLevel ?? null,
4953
- tokenBudget: config?.tokenBudget ?? null,
4954
- tokenBudgetPeriodMinutes: config?.tokenBudgetPeriodMinutes ?? null
4891
+ model: config2?.model ?? null,
4892
+ thinkingLevel: config2?.thinkingLevel ?? null,
4893
+ tokenBudget: config2?.tokenBudget ?? null,
4894
+ tokenBudgetPeriodMinutes: config2?.tokenBudgetPeriodMinutes ?? null
4955
4895
  }
4956
4896
  });
4957
4897
  }).put(
@@ -4971,7 +4911,7 @@ ${user.trimEnd()}
4971
4911
  const entry = await findMind(name);
4972
4912
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4973
4913
  const dir = mindDir(name);
4974
- if (!existsSync10(dir)) return c.json({ error: "Mind directory missing" }, 404);
4914
+ if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
4975
4915
  const body = c.req.valid("json");
4976
4916
  const existing = readVoluteConfig(dir) ?? {};
4977
4917
  if (body.model !== void 0) existing.model = body.model;
@@ -5013,7 +4953,7 @@ ${user.trimEnd()}
5013
4953
  if (!body.systemPrompt || !body.message) {
5014
4954
  return c.json({ error: "systemPrompt and message required" }, 400);
5015
4955
  }
5016
- const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-UWUPM4T6.js");
4956
+ const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-ZIPCV3MX.js");
5017
4957
  if (!isAiConfigured()) {
5018
4958
  return c.json({ error: "AI service not configured" }, 503);
5019
4959
  }
@@ -5105,7 +5045,7 @@ ${user.trimEnd()}
5105
5045
  publishTypingForChannels(affected, map);
5106
5046
  broadcast({ type: "mind_done", mind: baseName, summary: "Finished processing" });
5107
5047
  try {
5108
- await getDeliveryManager().sessionDone(baseName, body.session);
5048
+ getDeliveryManager().sessionDone(baseName, body.session);
5109
5049
  } catch (err) {
5110
5050
  if (!(err instanceof Error && err.message.includes("not initialized"))) {
5111
5051
  logger_default.error(`delivery manager sessionDone failed for ${baseName}`, logger_default.errorData(err));
@@ -5450,6 +5390,52 @@ ${user.trimEnd()}
5450
5390
  )
5451
5391
  ).orderBy(mindHistory.id);
5452
5392
  return c.json(rows);
5393
+ }).get("/:name/history/cross-session", async (c) => {
5394
+ const name = c.req.param("name");
5395
+ const currentSession = c.req.query("session");
5396
+ const db = await getDb();
5397
+ let sinceTimestamp = null;
5398
+ if (currentSession) {
5399
+ const lastTurn = await db.select({ turn_id: mindHistory.turn_id }).from(mindHistory).where(
5400
+ and4(
5401
+ eq5(mindHistory.mind, name),
5402
+ eq5(mindHistory.session, currentSession),
5403
+ sql2`${mindHistory.turn_id} IS NOT NULL`
5404
+ )
5405
+ ).orderBy(desc3(mindHistory.created_at)).limit(1);
5406
+ if (lastTurn.length > 0 && lastTurn[0].turn_id) {
5407
+ 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);
5408
+ if (firstEvent.length > 0) {
5409
+ sinceTimestamp = firstEvent[0].created_at;
5410
+ }
5411
+ }
5412
+ }
5413
+ if (!sinceTimestamp) {
5414
+ sinceTimestamp = new Date(Date.now() - 36e5).toISOString().replace("T", " ").slice(0, 19);
5415
+ }
5416
+ const conditions = [
5417
+ eq5(mindHistory.mind, name),
5418
+ eq5(mindHistory.type, "summary"),
5419
+ sql2`${mindHistory.created_at} > ${sinceTimestamp}`
5420
+ ];
5421
+ if (currentSession) {
5422
+ conditions.push(sql2`${mindHistory.session} != ${currentSession}`);
5423
+ }
5424
+ const rows = await db.select({
5425
+ session: mindHistory.session,
5426
+ content: mindHistory.content,
5427
+ created_at: mindHistory.created_at
5428
+ }).from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.created_at)).limit(50);
5429
+ if (rows.length === 0) {
5430
+ return c.json({ context: null });
5431
+ }
5432
+ const lines = rows.map((row) => {
5433
+ const ts = new Date(row.created_at.endsWith("Z") ? row.created_at : `${row.created_at}Z`);
5434
+ const ago = formatTimeAgo(ts);
5435
+ return `- ${row.session ?? "unknown"} (${ago}): ${row.content ?? ""}`;
5436
+ });
5437
+ return c.json({ context: `[Session Activity]
5438
+ ${lines.join("\n")}` });
5453
5439
  }).get("/:name/history", async (c) => {
5454
5440
  const name = c.req.param("name");
5455
5441
  const channel = c.req.query("channel");
@@ -5490,9 +5476,9 @@ var minds_default = app13;
5490
5476
  // src/web/api/prompts.ts
5491
5477
  import { zValidator as zValidator6 } from "@hono/zod-validator";
5492
5478
  import { eq as eq6, sql as sql3 } from "drizzle-orm";
5493
- import { Hono as Hono14 } from "hono";
5479
+ import { Hono as Hono15 } from "hono";
5494
5480
  import { z as z6 } from "zod";
5495
- var app14 = new Hono14().get("/", async (c) => {
5481
+ var app14 = new Hono15().get("/", async (c) => {
5496
5482
  let rows;
5497
5483
  try {
5498
5484
  const db = await getDb();
@@ -5539,9 +5525,9 @@ var app14 = new Hono14().get("/", async (c) => {
5539
5525
  var prompts_default = app14;
5540
5526
 
5541
5527
  // src/web/api/public-files.ts
5542
- import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
5528
+ import { readdir, readFile as readFile2, stat as stat2 } from "fs/promises";
5543
5529
  import { extname as extname3, resolve as resolve13 } from "path";
5544
- import { Hono as Hono15 } from "hono";
5530
+ import { Hono as Hono16 } from "hono";
5545
5531
  var MAX_FILE_SIZE = 50 * 1024 * 1024;
5546
5532
  async function resolvePublicRoot(name) {
5547
5533
  if (name === "_system") return resolve13(voluteHome(), "shared");
@@ -5572,7 +5558,7 @@ var MIME_TYPES = {
5572
5558
  async function listDir(dirPath) {
5573
5559
  let entries;
5574
5560
  try {
5575
- entries = await readdir2(dirPath, { withFileTypes: true });
5561
+ entries = await readdir(dirPath, { withFileTypes: true });
5576
5562
  } catch (err) {
5577
5563
  if (err?.code === "ENOENT") return [];
5578
5564
  throw err;
@@ -5582,7 +5568,7 @@ async function listDir(dirPath) {
5582
5568
  type: e.isDirectory() ? "directory" : "file"
5583
5569
  }));
5584
5570
  }
5585
- var app15 = new Hono15().get("/:name/", async (c) => {
5571
+ var app15 = new Hono16().get("/:name/", async (c) => {
5586
5572
  const name = c.req.param("name");
5587
5573
  const publicRoot = await resolvePublicRoot(name);
5588
5574
  if (!publicRoot) return c.json({ error: "Not found" }, 404);
@@ -5629,16 +5615,16 @@ var public_files_default = app15;
5629
5615
 
5630
5616
  // src/web/api/schedules.ts
5631
5617
  import { CronExpressionParser } from "cron-parser";
5632
- import { Hono as Hono16 } from "hono";
5618
+ import { Hono as Hono17 } from "hono";
5633
5619
  var slog2 = logger_default.child("schedules");
5634
5620
  function readSchedules(name) {
5635
5621
  return readVoluteConfig(mindDir(name))?.schedules ?? [];
5636
5622
  }
5637
5623
  function writeSchedules(name, schedules) {
5638
5624
  const dir = mindDir(name);
5639
- const config = readVoluteConfig(dir) ?? {};
5640
- config.schedules = schedules.length > 0 ? schedules : void 0;
5641
- writeVoluteConfig(dir, config);
5625
+ const config2 = readVoluteConfig(dir) ?? {};
5626
+ config2.schedules = schedules.length > 0 ? schedules : void 0;
5627
+ writeVoluteConfig(dir, config2);
5642
5628
  getScheduler().loadSchedules(name);
5643
5629
  getSleepManagerIfReady()?.invalidateSleepConfig(name);
5644
5630
  fireWebhook({
@@ -5647,7 +5633,7 @@ function writeSchedules(name, schedules) {
5647
5633
  data: { schedules }
5648
5634
  });
5649
5635
  }
5650
- var app16 = new Hono16().get("/:name/clock/status", async (c) => {
5636
+ var app16 = new Hono17().get("/:name/clock/status", async (c) => {
5651
5637
  const name = c.req.param("name");
5652
5638
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
5653
5639
  const sleepManager = getSleepManagerIfReady();
@@ -5805,7 +5791,7 @@ var app16 = new Hono16().get("/:name/clock/status", async (c) => {
5805
5791
  const body = await c.req.text();
5806
5792
  const message = `[webhook: ${event}] ${body}`;
5807
5793
  try {
5808
- const { sendSystemMessage } = await import("./system-chat-B43GIXQU.js");
5794
+ const { sendSystemMessage } = await import("./system-chat-NPYFYZVI.js");
5809
5795
  await sendSystemMessage(name, message);
5810
5796
  return c.json({ ok: true });
5811
5797
  } catch (err) {
@@ -5819,18 +5805,53 @@ var schedules_default = app16;
5819
5805
  import { mkdirSync as mkdirSync7 } from "fs";
5820
5806
  import { homedir as homedir2 } from "os";
5821
5807
  import { resolve as resolve14 } from "path";
5822
- import { Hono as Hono17 } from "hono";
5823
- var setup = new Hono17();
5824
- setup.get("/status", (c) => {
5808
+ import { Hono as Hono18 } from "hono";
5809
+ import { setCookie as setCookie2 } from "hono/cookie";
5810
+ var DEFAULT_API_URL2 = "https://volute.systems";
5811
+ var setup = new Hono18();
5812
+ function writeSetupConfig(systemName, description) {
5813
+ const configHome = process.env.VOLUTE_HOME ?? resolve14(homedir2(), ".volute");
5814
+ const mindsDir = resolve14(configHome, "minds");
5815
+ mkdirSync7(configHome, { recursive: true });
5816
+ mkdirSync7(mindsDir, { recursive: true });
5817
+ const existingConfig = readGlobalConfig();
5818
+ const setupConfig = {
5819
+ type: "local",
5820
+ mindsDir,
5821
+ isolation: "sandbox",
5822
+ service: false
5823
+ };
5824
+ const config2 = {
5825
+ ...existingConfig,
5826
+ name: systemName,
5827
+ description: description || existingConfig.description,
5828
+ setup: setupConfig
5829
+ };
5830
+ writeGlobalConfig(config2);
5831
+ return config2;
5832
+ }
5833
+ setup.get("/status", async (c) => {
5825
5834
  const complete = isSetupComplete();
5826
- if (!complete) {
5827
- return c.json({ complete });
5835
+ if (complete) {
5836
+ const config3 = readGlobalConfig();
5837
+ return c.json({
5838
+ complete,
5839
+ config: { name: config3.name, setup: config3.setup }
5840
+ });
5828
5841
  }
5829
- const config = readGlobalConfig();
5830
- return c.json({
5831
- complete,
5832
- config: { name: config.name, setup: config.setup }
5833
- });
5842
+ const config2 = readGlobalConfig();
5843
+ const hasSystem = config2.setup != null;
5844
+ let hasAccount = false;
5845
+ if (hasSystem) {
5846
+ try {
5847
+ const { listUsersByType: listUsersByType2 } = await import("./auth-6DMGES3I.js");
5848
+ const brains = await listUsersByType2("brain");
5849
+ hasAccount = brains.length > 0;
5850
+ } catch (err) {
5851
+ logger_default.debug("could not check for existing accounts during setup status", logger_default.errorData(err));
5852
+ }
5853
+ }
5854
+ return c.json({ complete, hasSystem, hasAccount });
5834
5855
  });
5835
5856
  setup.post("/configure", async (c) => {
5836
5857
  if (isSetupComplete()) {
@@ -5846,42 +5867,313 @@ setup.post("/configure", async (c) => {
5846
5867
  return c.json({ error: "System name is required" }, 400);
5847
5868
  }
5848
5869
  const setupType = body.type ?? "local";
5849
- const isolation = body.isolation ?? "sandbox";
5850
5870
  if (setupType !== "local") {
5851
5871
  return c.json({ error: "Web setup only supports local install type" }, 400);
5852
5872
  }
5853
- const configHome = process.env.VOLUTE_HOME ?? resolve14(homedir2(), ".volute");
5854
- const mindsDir = resolve14(configHome, "minds");
5855
5873
  try {
5856
- mkdirSync7(configHome, { recursive: true });
5857
- mkdirSync7(mindsDir, { recursive: true });
5874
+ const config2 = writeSetupConfig(body.name.trim());
5875
+ config2.setupCompleted = true;
5876
+ writeGlobalConfig(config2);
5877
+ return c.json({ ok: true, config: { name: config2.name, setup: config2.setup } });
5858
5878
  } catch (err) {
5859
- return c.json({ error: `Failed to create directories: ${err.message}` }, 500);
5879
+ return c.json({ error: `Failed to write configuration: ${err.message}` }, 500);
5860
5880
  }
5861
- const existingConfig = readGlobalConfig();
5862
- const setupConfig = {
5863
- type: setupType,
5864
- mindsDir,
5865
- isolation,
5866
- service: false
5867
- };
5868
- const config = {
5869
- ...existingConfig,
5870
- name: body.name.trim(),
5871
- setup: setupConfig
5872
- };
5881
+ });
5882
+ setup.post("/system", async (c) => {
5883
+ if (isSetupComplete()) {
5884
+ return c.json({ error: "Setup already complete" }, 400);
5885
+ }
5886
+ let body;
5873
5887
  try {
5874
- writeGlobalConfig(config);
5888
+ body = await c.req.json();
5889
+ } catch {
5890
+ return c.json({ error: "Invalid JSON in request body" }, 400);
5891
+ }
5892
+ if (!body.name?.trim()) {
5893
+ return c.json({ error: "System name is required" }, 400);
5894
+ }
5895
+ try {
5896
+ writeSetupConfig(body.name.trim(), body.description?.trim());
5897
+ return c.json({ ok: true });
5875
5898
  } catch (err) {
5876
5899
  return c.json({ error: `Failed to write configuration: ${err.message}` }, 500);
5877
5900
  }
5878
- return c.json({ ok: true, config: { name: config.name, setup: config.setup } });
5901
+ });
5902
+ setup.post("/system/register", async (c) => {
5903
+ if (isSetupComplete()) {
5904
+ return c.json({ error: "Setup already complete" }, 400);
5905
+ }
5906
+ let body;
5907
+ try {
5908
+ body = await c.req.json();
5909
+ } catch {
5910
+ return c.json({ error: "Invalid JSON in request body" }, 400);
5911
+ }
5912
+ if (!body.slug?.trim()) {
5913
+ return c.json({ error: "System slug is required" }, 400);
5914
+ }
5915
+ const existing = readSystemsConfig();
5916
+ if (existing) {
5917
+ return c.json({ error: `Already registered as "${existing.system}"` }, 400);
5918
+ }
5919
+ const config2 = readGlobalConfig();
5920
+ const apiUrl = process.env.VOLUTE_SYSTEMS_URL || DEFAULT_API_URL2;
5921
+ let apiKey;
5922
+ let system;
5923
+ try {
5924
+ const res = await fetch(`${apiUrl}/api/register`, {
5925
+ method: "POST",
5926
+ headers: { "Content-Type": "application/json" },
5927
+ body: JSON.stringify({
5928
+ name: body.slug.trim(),
5929
+ displayName: config2.name || void 0,
5930
+ description: config2.description || void 0
5931
+ })
5932
+ });
5933
+ if (!res.ok) {
5934
+ const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
5935
+ return c.json({ error: `volute.systems: ${err.error}` }, 502);
5936
+ }
5937
+ ({ apiKey, system } = await res.json());
5938
+ } catch (err) {
5939
+ return c.json({ error: `Could not reach volute.systems: ${err.message}` }, 502);
5940
+ }
5941
+ try {
5942
+ writeSystemsConfig({ apiKey, system, apiUrl });
5943
+ } catch (err) {
5944
+ return c.json(
5945
+ {
5946
+ error: `Registered as "${system}" but failed to save config: ${err.message}`
5947
+ },
5948
+ 500
5949
+ );
5950
+ }
5951
+ return c.json({ system });
5952
+ });
5953
+ setup.post("/system/login", async (c) => {
5954
+ if (isSetupComplete()) {
5955
+ return c.json({ error: "Setup already complete" }, 400);
5956
+ }
5957
+ let body;
5958
+ try {
5959
+ body = await c.req.json();
5960
+ } catch {
5961
+ return c.json({ error: "Invalid JSON in request body" }, 400);
5962
+ }
5963
+ if (!body.key?.trim()) {
5964
+ return c.json({ error: "API key is required" }, 400);
5965
+ }
5966
+ const existing = readSystemsConfig();
5967
+ if (existing) {
5968
+ return c.json({ error: `Already logged in as "${existing.system}"` }, 400);
5969
+ }
5970
+ const apiUrl = process.env.VOLUTE_SYSTEMS_URL || DEFAULT_API_URL2;
5971
+ let system;
5972
+ try {
5973
+ const res = await fetch(`${apiUrl}/api/whoami`, {
5974
+ headers: { Authorization: `Bearer ${body.key.trim()}` }
5975
+ });
5976
+ if (!res.ok) {
5977
+ const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
5978
+ return c.json({ error: `volute.systems: ${err.error}` }, 502);
5979
+ }
5980
+ ({ system } = await res.json());
5981
+ } catch (err) {
5982
+ return c.json({ error: `Could not reach volute.systems: ${err.message}` }, 502);
5983
+ }
5984
+ try {
5985
+ writeSystemsConfig({ apiKey: body.key.trim(), system, apiUrl });
5986
+ } catch (err) {
5987
+ return c.json(
5988
+ {
5989
+ error: `Logged in as "${system}" but failed to save config: ${err.message}`
5990
+ },
5991
+ 500
5992
+ );
5993
+ }
5994
+ return c.json({ system });
5995
+ });
5996
+ setup.post("/system/disconnect", async (c) => {
5997
+ if (isSetupComplete()) {
5998
+ return c.json({ error: "Setup already complete" }, 400);
5999
+ }
6000
+ deleteSystemsConfig();
6001
+ return c.json({ ok: true });
6002
+ });
6003
+ setup.get("/system/systems-status", (c) => {
6004
+ const config2 = readSystemsConfig();
6005
+ return c.json({ registered: !!config2, system: config2?.system ?? null });
6006
+ });
6007
+ setup.post("/account", async (c) => {
6008
+ if (isSetupComplete()) {
6009
+ return c.json({ error: "Setup already complete" }, 400);
6010
+ }
6011
+ let body;
6012
+ try {
6013
+ body = await c.req.json();
6014
+ } catch {
6015
+ return c.json({ error: "Invalid JSON in request body" }, 400);
6016
+ }
6017
+ if (!body.username?.trim()) {
6018
+ return c.json({ error: "Username is required" }, 400);
6019
+ }
6020
+ if (!body.password || body.password.length < 1) {
6021
+ return c.json({ error: "Password is required" }, 400);
6022
+ }
6023
+ const config2 = readGlobalConfig();
6024
+ if (!config2.setup) {
6025
+ try {
6026
+ writeSetupConfig(config2.name ?? "Volute");
6027
+ } catch (err) {
6028
+ return c.json({ error: `Failed to write configuration: ${err.message}` }, 500);
6029
+ }
6030
+ }
6031
+ try {
6032
+ const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-6DMGES3I.js");
6033
+ const user = await createUser2(body.username.trim(), body.password);
6034
+ if (body.displayName?.trim()) {
6035
+ await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
6036
+ }
6037
+ const sessionId = await createSession(user.id);
6038
+ setCookie2(c, "volute_session", sessionId, {
6039
+ path: "/",
6040
+ httpOnly: true,
6041
+ sameSite: "Lax",
6042
+ maxAge: Math.floor(SESSION_MAX_AGE / 1e3)
6043
+ });
6044
+ return c.json({
6045
+ ok: true,
6046
+ user: {
6047
+ id: user.id,
6048
+ username: user.username,
6049
+ role: user.role,
6050
+ display_name: body.displayName?.trim() ?? null
6051
+ }
6052
+ });
6053
+ } catch (err) {
6054
+ const msg = err instanceof Error ? err.message : String(err);
6055
+ if (msg.includes("UNIQUE constraint")) {
6056
+ return c.json({ error: "Username already exists" }, 409);
6057
+ }
6058
+ return c.json({ error: `Failed to create user: ${msg}` }, 500);
6059
+ }
6060
+ });
6061
+ setup.post("/models", async (c) => {
6062
+ if (isSetupComplete()) {
6063
+ return c.json({ error: "Setup already complete" }, 400);
6064
+ }
6065
+ let body;
6066
+ try {
6067
+ body = await c.req.json();
6068
+ } catch {
6069
+ return c.json({ error: "Invalid JSON in request body" }, 400);
6070
+ }
6071
+ if (!Array.isArray(body.models) || body.models.length === 0) {
6072
+ return c.json({ error: "At least one model must be selected" }, 400);
6073
+ }
6074
+ if (!body.spiritModel?.trim()) {
6075
+ return c.json({ error: "Spirit model is required" }, 400);
6076
+ }
6077
+ try {
6078
+ const { setEnabledModels: setEnabledModels2, setUtilityModel } = await import("./ai-service-ZIPCV3MX.js");
6079
+ setEnabledModels2(body.models);
6080
+ const config2 = readGlobalConfig();
6081
+ config2.spiritModel = body.spiritModel.trim();
6082
+ writeGlobalConfig(config2);
6083
+ if (body.utilityModel?.trim()) {
6084
+ setUtilityModel(body.utilityModel.trim());
6085
+ }
6086
+ return c.json({ ok: true });
6087
+ } catch (err) {
6088
+ return c.json({ error: `Failed to save models: ${err.message}` }, 500);
6089
+ }
6090
+ });
6091
+ setup.post("/complete", async (c) => {
6092
+ if (isSetupComplete()) {
6093
+ return c.json({ error: "Setup already complete" }, 400);
6094
+ }
6095
+ try {
6096
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-N4W4UQRH.js");
6097
+ const { startSpiritFull } = await import("./mind-service-AV273WT4.js");
6098
+ await ensureSpiritProject();
6099
+ await syncSpiritTemplate();
6100
+ const warnings = [];
6101
+ let spiritStarted = false;
6102
+ try {
6103
+ logger_default.info("starting spirit during setup...");
6104
+ await startSpiritFull("volute");
6105
+ spiritStarted = true;
6106
+ logger_default.info("spirit started successfully during setup");
6107
+ } catch (err) {
6108
+ logger_default.warn("spirit start failed during setup (non-fatal)", logger_default.errorData(err));
6109
+ warnings.push("Spirit failed to start \u2014 it will retry on next daemon restart.");
6110
+ }
6111
+ let spiritConversationId;
6112
+ try {
6113
+ const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-6DMGES3I.js");
6114
+ const { createConversation: createConversation6, findDMConversation: findDMConversation2 } = await import("./conversations-3O5O6AS3.js");
6115
+ const spiritUser = await getOrCreateMindUser2("volute");
6116
+ const brains = await listUsersByType2("brain");
6117
+ const admin2 = brains.find((u) => u.role === "admin");
6118
+ if (admin2) {
6119
+ const existing = await findDMConversation2("volute", [admin2.id, spiritUser.id]);
6120
+ if (existing) {
6121
+ spiritConversationId = existing;
6122
+ } else {
6123
+ const conv = await createConversation6("volute", "volute", {
6124
+ participantIds: [admin2.id, spiritUser.id],
6125
+ title: "Volute"
6126
+ });
6127
+ spiritConversationId = conv.id;
6128
+ }
6129
+ }
6130
+ } catch (err) {
6131
+ logger_default.warn("failed to create spirit DM during setup (non-fatal)", logger_default.errorData(err));
6132
+ warnings.push("Failed to create conversation with spirit.");
6133
+ }
6134
+ logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
6135
+ if (spiritConversationId && spiritStarted) {
6136
+ try {
6137
+ const { deliverMessage: deliverMessage2 } = await import("./message-delivery-2FIM7QKO.js");
6138
+ const { listUsersByType: listUsers6 } = await import("./auth-6DMGES3I.js");
6139
+ const admins = await listUsers6("brain");
6140
+ const admin2 = admins.find((u) => u.role === "admin");
6141
+ const adminName = admin2?.display_name || admin2?.username || "the admin";
6142
+ const welcomePrompt = `Setup is complete. ${adminName} just finished setting up this system and is now in your chat. Welcome them and help them create their first mind. Keep it brief \u2014 a short greeting, then ask what kind of being they'd like to bring into the world. Remember: minds are beings with their own experience, not tools \u2014 ask about personality and character, never about tasks or utility.`;
6143
+ await deliverMessage2("volute", {
6144
+ content: [{ type: "text", text: welcomePrompt }],
6145
+ channel: `@${admin2?.username ?? "system"}`,
6146
+ conversationId: spiritConversationId,
6147
+ sender: admin2?.username ?? "system",
6148
+ isDM: true,
6149
+ participants: ["volute", admin2?.username ?? "system"],
6150
+ participantCount: 2
6151
+ });
6152
+ } catch (err) {
6153
+ logger_default.warn("failed to send welcome prompt to spirit (non-fatal)", logger_default.errorData(err));
6154
+ warnings.push(
6155
+ "Welcome message failed to send \u2014 try sending a message to start the conversation."
6156
+ );
6157
+ }
6158
+ }
6159
+ const config2 = readGlobalConfig();
6160
+ config2.setupCompleted = true;
6161
+ writeGlobalConfig(config2);
6162
+ return c.json({
6163
+ ok: true,
6164
+ spiritConversationId,
6165
+ spiritStarted,
6166
+ warnings: warnings.length > 0 ? warnings : void 0
6167
+ });
6168
+ } catch (err) {
6169
+ return c.json({ error: `Setup completion failed: ${err.message}` }, 500);
6170
+ }
5879
6171
  });
5880
6172
  var setup_default = setup;
5881
6173
 
5882
6174
  // src/web/api/shared.ts
5883
- import { Hono as Hono18 } from "hono";
5884
- var app17 = new Hono18().post("/:name/shared/merge", requireAdmin, async (c) => {
6175
+ import { Hono as Hono19 } from "hono";
6176
+ var app17 = new Hono19().post("/:name/shared/merge", requireAdmin, async (c) => {
5885
6177
  const name = c.req.param("name");
5886
6178
  const entry = await findMind(name);
5887
6179
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -5902,12 +6194,12 @@ var app17 = new Hono18().post("/:name/shared/merge", requireAdmin, async (c) =>
5902
6194
  var shared_default = app17;
5903
6195
 
5904
6196
  // src/web/api/skills.ts
5905
- import { existsSync as existsSync11, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
6197
+ import { existsSync as existsSync10, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
5906
6198
  import { tmpdir } from "os";
5907
6199
  import { join, resolve as resolve15 } from "path";
5908
6200
  import AdmZip from "adm-zip";
5909
- import { Hono as Hono19 } from "hono";
5910
- var app18 = new Hono19().get("/", async (c) => {
6201
+ import { Hono as Hono20 } from "hono";
6202
+ var app18 = new Hono20().get("/", async (c) => {
5911
6203
  const skills = await listSharedSkills();
5912
6204
  return c.json(skills);
5913
6205
  }).get("/defaults/list", async (c) => {
@@ -5917,15 +6209,15 @@ var app18 = new Hono19().get("/", async (c) => {
5917
6209
  if (!Array.isArray(body.skills) || !body.skills.every((s) => typeof s === "string")) {
5918
6210
  return c.json({ error: "body.skills must be a string array" }, 400);
5919
6211
  }
5920
- const config = readGlobalConfig();
6212
+ const config2 = readGlobalConfig();
5921
6213
  const allStandard = /* @__PURE__ */ new Set([...STANDARD_SKILLS, ...getExtensionStandardSkills()]);
5922
6214
  const newSet = new Set(body.skills);
5923
6215
  const removed = [...allStandard].filter((s) => !newSet.has(s));
5924
- const prevRemoved = new Set(config.removedDefaultSkills ?? []);
6216
+ const prevRemoved = new Set(config2.removedDefaultSkills ?? []);
5925
6217
  for (const s of removed) prevRemoved.add(s);
5926
6218
  for (const s of body.skills) prevRemoved.delete(s);
5927
6219
  writeGlobalConfig({
5928
- ...config,
6220
+ ...config2,
5929
6221
  defaultSkills: body.skills,
5930
6222
  removedDefaultSkills: [...prevRemoved]
5931
6223
  });
@@ -5939,10 +6231,10 @@ var app18 = new Hono19().get("/", async (c) => {
5939
6231
  if (current.includes(body.skill)) {
5940
6232
  return c.json({ error: `"${body.skill}" is already a default skill` }, 409);
5941
6233
  }
5942
- const config = readGlobalConfig();
6234
+ const config2 = readGlobalConfig();
5943
6235
  const updated = [...current, body.skill];
5944
- const removed = (config.removedDefaultSkills ?? []).filter((s) => s !== body.skill);
5945
- writeGlobalConfig({ ...config, defaultSkills: updated, removedDefaultSkills: removed });
6236
+ const removed = (config2.removedDefaultSkills ?? []).filter((s) => s !== body.skill);
6237
+ writeGlobalConfig({ ...config2, defaultSkills: updated, removedDefaultSkills: removed });
5946
6238
  return c.json({ skills: updated });
5947
6239
  }).delete("/defaults/list/:skill", requireAdmin, async (c) => {
5948
6240
  const skill = c.req.param("skill");
@@ -5950,11 +6242,11 @@ var app18 = new Hono19().get("/", async (c) => {
5950
6242
  if (!current.includes(skill)) {
5951
6243
  return c.json({ error: `"${skill}" is not a default skill` }, 404);
5952
6244
  }
5953
- const config = readGlobalConfig();
6245
+ const config2 = readGlobalConfig();
5954
6246
  const updated = current.filter((s) => s !== skill);
5955
- const removed = new Set(config.removedDefaultSkills ?? []);
6247
+ const removed = new Set(config2.removedDefaultSkills ?? []);
5956
6248
  removed.add(skill);
5957
- writeGlobalConfig({ ...config, defaultSkills: updated, removedDefaultSkills: [...removed] });
6249
+ writeGlobalConfig({ ...config2, defaultSkills: updated, removedDefaultSkills: [...removed] });
5958
6250
  return c.json({ skills: updated });
5959
6251
  }).post("/upload", requireAdmin, async (c) => {
5960
6252
  const body = await c.req.parseBody();
@@ -5977,12 +6269,12 @@ var app18 = new Hono19().get("/", async (c) => {
5977
6269
  }
5978
6270
  zip.extractAllTo(tmpDir, true);
5979
6271
  let skillDir = null;
5980
- if (existsSync11(join(tmpDir, "SKILL.md"))) {
6272
+ if (existsSync10(join(tmpDir, "SKILL.md"))) {
5981
6273
  skillDir = tmpDir;
5982
6274
  } else {
5983
6275
  const entries = readdirSync3(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
5984
6276
  for (const entry of entries) {
5985
- if (existsSync11(join(tmpDir, entry.name, "SKILL.md"))) {
6277
+ if (existsSync10(join(tmpDir, entry.name, "SKILL.md"))) {
5986
6278
  skillDir = join(tmpDir, entry.name);
5987
6279
  break;
5988
6280
  }
@@ -6022,14 +6314,14 @@ var skills_default = app18;
6022
6314
 
6023
6315
  // src/web/api/typing.ts
6024
6316
  import { zValidator as zValidator7 } from "@hono/zod-validator";
6025
- import { Hono as Hono20 } from "hono";
6317
+ import { Hono as Hono21 } from "hono";
6026
6318
  import { z as z7 } from "zod";
6027
6319
  var typingSchema = z7.object({
6028
6320
  channel: z7.string().min(1),
6029
6321
  sender: z7.string().min(1),
6030
6322
  active: z7.boolean()
6031
6323
  });
6032
- var app19 = new Hono20().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
6324
+ var app19 = new Hono21().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
6033
6325
  const { channel, sender, active } = c.req.valid("json");
6034
6326
  const map = getTypingMap();
6035
6327
  if (active) {
@@ -6037,10 +6329,8 @@ var app19 = new Hono20().post("/:name/typing", zValidator7("json", typingSchema)
6037
6329
  } else {
6038
6330
  map.delete(channel, sender);
6039
6331
  }
6040
- const volutePrefix = "volute:";
6041
- if (channel.startsWith(volutePrefix)) {
6042
- const conversationId = channel.slice(volutePrefix.length);
6043
- publish(conversationId, { type: "typing", senders: map.get(channel) });
6332
+ if (isConversationId(channel)) {
6333
+ publish(channel, { type: "typing", senders: map.get(channel) });
6044
6334
  }
6045
6335
  return c.json({ ok: true });
6046
6336
  }).get("/:name/typing", (c) => {
@@ -6055,9 +6345,9 @@ var typing_default = app19;
6055
6345
 
6056
6346
  // src/web/api/update.ts
6057
6347
  import { spawn as spawn3 } from "child_process";
6058
- import { Hono as Hono21 } from "hono";
6348
+ import { Hono as Hono22 } from "hono";
6059
6349
  var bin;
6060
- var app20 = new Hono21().get("/update", async (c) => {
6350
+ var app20 = new Hono22().get("/update", async (c) => {
6061
6351
  const result = await checkForUpdate();
6062
6352
  return c.json(result);
6063
6353
  }).post("/update", requireAdmin, async (c) => {
@@ -6076,17 +6366,18 @@ var update_default = app20;
6076
6366
 
6077
6367
  // src/web/api/v1/chat.ts
6078
6368
  import { zValidator as zValidator8 } from "@hono/zod-validator";
6079
- import { Hono as Hono22 } from "hono";
6369
+ import { Hono as Hono23 } from "hono";
6080
6370
  import { streamSSE as streamSSE4 } from "hono/streaming";
6081
6371
  import { z as z8 } from "zod";
6082
6372
  async function fanOutToMinds(opts) {
6083
6373
  const participants = await getParticipants(opts.conversationId);
6084
- const mindParticipants = participants.filter((p) => p.userType === "mind");
6374
+ const mindParticipants = participants.filter(
6375
+ (p) => p.userType === "mind" || p.userType === "system"
6376
+ );
6085
6377
  const participantNames = participants.map((p) => p.username);
6086
6378
  const isDM = opts.isDM ?? participants.length === 2;
6087
- const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "channel");
6088
- const { getMindManager: getMindManager2 } = await import("./mind-manager-IPA6DZXD.js");
6089
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-TPS6OGCA.js");
6379
+ const { getMindManager: getMindManager2 } = await import("./mind-manager-BNCMGYXW.js");
6380
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
6090
6381
  const manager = getMindManager2();
6091
6382
  const sm = getSleepManagerIfReady2();
6092
6383
  const targetMinds = mindParticipants.map((ap) => {
@@ -6103,19 +6394,6 @@ async function fanOutToMinds(opts) {
6103
6394
  ...opts.slugExtra
6104
6395
  });
6105
6396
  }
6106
- const channelEntry = {
6107
- platformId: opts.conversationId,
6108
- platform: "volute",
6109
- name: opts.convTitle ?? void 0,
6110
- type: channelEntryType
6111
- };
6112
- for (const ap of mindParticipants) {
6113
- try {
6114
- writeChannelEntry(ap.username, slugForMind(ap.username), channelEntry);
6115
- } catch (err) {
6116
- logger_default.warn(`failed to write channel entry for ${ap.username}`, logger_default.errorData(err));
6117
- }
6118
- }
6119
6397
  for (const mindName of targetMinds) {
6120
6398
  const target = opts.targetName ? opts.targetName(mindName) : mindName;
6121
6399
  const channel = slugForMind(mindName);
@@ -6146,7 +6424,7 @@ var unifiedChatSchema = z8.object({
6146
6424
  conversationId: z8.string(),
6147
6425
  images: z8.array(z8.object({ media_type: z8.string(), data: z8.string() })).optional()
6148
6426
  });
6149
- var app21 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zValidator8("json", mindChatSchema), async (c) => {
6427
+ var app21 = new Hono23().use("*", authMiddleware).post("/minds/:name/chat", zValidator8("json", mindChatSchema), async (c) => {
6150
6428
  const name = c.req.param("name");
6151
6429
  const baseName = await getBaseName(name);
6152
6430
  const entry = await findMind(baseName);
@@ -6218,7 +6496,6 @@ var app21 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zVal
6218
6496
  senderName,
6219
6497
  convTitle,
6220
6498
  isDM,
6221
- channelEntryType: isDM ? "dm" : "channel",
6222
6499
  slugExtra: conv ? { convType: conv.type, convName: conv.name } : void 0,
6223
6500
  targetName: (username) => username === baseName ? name : username
6224
6501
  });
@@ -6285,7 +6562,6 @@ var app21 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zVal
6285
6562
  senderName,
6286
6563
  convTitle: conv.title,
6287
6564
  isDM,
6288
- channelEntryType: isDM ? "dm" : "channel",
6289
6565
  slugExtra: { convType: conv.type, convName: conv.name }
6290
6566
  });
6291
6567
  return c.json({ ok: true, conversationId: body.conversationId });
@@ -6294,13 +6570,13 @@ var chat_default = app21;
6294
6570
 
6295
6571
  // src/web/api/v1/conversations.ts
6296
6572
  import { zValidator as zValidator9 } from "@hono/zod-validator";
6297
- import { Hono as Hono23 } from "hono";
6573
+ import { Hono as Hono24 } from "hono";
6298
6574
  import { z as z9 } from "zod";
6299
6575
  var createSchema = z9.object({
6300
6576
  title: z9.string().optional(),
6301
6577
  participantNames: z9.array(z9.string()).min(1)
6302
6578
  });
6303
- var app22 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6579
+ var app22 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
6304
6580
  const user = c.get("user");
6305
6581
  const convs = await listConversationsWithParticipants(user.id);
6306
6582
  return c.json(convs);
@@ -6377,7 +6653,7 @@ var conversations_default = app22;
6377
6653
 
6378
6654
  // src/web/api/v1/events.ts
6379
6655
  import { desc as desc4 } from "drizzle-orm";
6380
- import { Hono as Hono24 } from "hono";
6656
+ import { Hono as Hono25 } from "hono";
6381
6657
  import { streamSSE as streamSSE5 } from "hono/streaming";
6382
6658
 
6383
6659
  // src/lib/events/brain-presence.ts
@@ -6424,7 +6700,7 @@ function getEventsSince(sinceId) {
6424
6700
  }
6425
6701
 
6426
6702
  // src/web/api/v1/events.ts
6427
- var app23 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
6703
+ var app23 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
6428
6704
  const user = c.get("user");
6429
6705
  const since = c.req.query("since");
6430
6706
  const sinceId = since ? Number(since) : 0;
@@ -6468,14 +6744,10 @@ var app23 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
6468
6744
  } catch (err) {
6469
6745
  logger_default.error("[v1-events] failed to fetch conversations", logger_default.errorData(err));
6470
6746
  }
6471
- const sites = await getCachedSites();
6472
- const recentPages = await getCachedRecentPages();
6473
6747
  const snapshotData = {
6474
6748
  event: "snapshot",
6475
6749
  activity: recentActivity,
6476
6750
  conversations: conversations2,
6477
- sites,
6478
- recentPages,
6479
6751
  activeMinds: getActiveMinds(),
6480
6752
  onlineBrains: getOnlineBrains()
6481
6753
  };
@@ -6534,9 +6806,9 @@ var app23 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
6534
6806
  var events_default = app23;
6535
6807
 
6536
6808
  // src/web/api/variants.ts
6537
- import { existsSync as existsSync12, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
6809
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
6538
6810
  import { resolve as resolve17 } from "path";
6539
- import { Hono as Hono25 } from "hono";
6811
+ import { Hono as Hono26 } from "hono";
6540
6812
 
6541
6813
  // src/lib/spawn-server.ts
6542
6814
  import { spawn as spawn4 } from "child_process";
@@ -6652,7 +6924,7 @@ async function verify2(port) {
6652
6924
  }
6653
6925
 
6654
6926
  // src/web/api/variants.ts
6655
- var app24 = new Hono25().get("/:name/variants", async (c) => {
6927
+ var app24 = new Hono26().get("/:name/variants", async (c) => {
6656
6928
  const name = c.req.param("name");
6657
6929
  const entry = await findMind(name);
6658
6930
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -6697,7 +6969,7 @@ var app24 = new Hono25().get("/:name/variants", async (c) => {
6697
6969
  }
6698
6970
  const projectRoot = mindDir(mindName);
6699
6971
  const variantDir = resolve17(projectRoot, ".variants", variantName);
6700
- if (existsSync12(variantDir)) {
6972
+ if (existsSync11(variantDir)) {
6701
6973
  return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
6702
6974
  }
6703
6975
  mkdirSync9(resolve17(projectRoot, ".variants"), { recursive: true });
@@ -6758,7 +7030,7 @@ var app24 = new Hono25().get("/:name/variants", async (c) => {
6758
7030
  } catch {
6759
7031
  }
6760
7032
  const projectRoot = mindDir(mindName);
6761
- if (existsSync12(variantEntry.dir)) {
7033
+ if (existsSync11(variantEntry.dir)) {
6762
7034
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
6763
7035
  if (status) {
6764
7036
  try {
@@ -6818,8 +7090,8 @@ var app24 = new Hono25().get("/:name/variants", async (c) => {
6818
7090
  await cleanupVariant(variantName, projectRoot, variantEntry.dir);
6819
7091
  if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
6820
7092
  try {
6821
- const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-3HOR4UAJ.js");
6822
- const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-ASXCQCNH.js");
7093
+ const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-A6VVKOXJ.js");
7094
+ const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-PJ4S5PHQ.js");
6823
7095
  const tmpl = parentEntry.template ?? "claude";
6824
7096
  await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
6825
7097
  } catch (err) {
@@ -6878,7 +7150,7 @@ var variants_default = app24;
6878
7150
 
6879
7151
  // src/web/api/volute/channels.ts
6880
7152
  import { zValidator as zValidator10 } from "@hono/zod-validator";
6881
- import { Hono as Hono26 } from "hono";
7153
+ import { Hono as Hono27 } from "hono";
6882
7154
  import { z as z10 } from "zod";
6883
7155
  var createSchema2 = z10.object({
6884
7156
  name: z10.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
@@ -6886,7 +7158,7 @@ var createSchema2 = z10.object({
6886
7158
  var inviteSchema = z10.object({
6887
7159
  username: z10.string().min(1)
6888
7160
  });
6889
- var app25 = new Hono26().get("/", async (c) => {
7161
+ var app25 = new Hono27().get("/", async (c) => {
6890
7162
  const user = c.get("user");
6891
7163
  const channels = await listChannels();
6892
7164
  const results = await Promise.all(
@@ -6910,6 +7182,12 @@ var app25 = new Hono26().get("/", async (c) => {
6910
7182
  }
6911
7183
  throw err;
6912
7184
  }
7185
+ }).get("/:name", async (c) => {
7186
+ const name = c.req.param("name");
7187
+ const ch = await getChannelByName(name);
7188
+ if (!ch) return c.json({ error: "Channel not found" }, 404);
7189
+ const participants = await getParticipants(ch.id);
7190
+ return c.json({ ...ch, participants });
6913
7191
  }).post("/:name/join", async (c) => {
6914
7192
  const name = c.req.param("name");
6915
7193
  const user = c.get("user");
@@ -6954,7 +7232,7 @@ var channels_default2 = app25;
6954
7232
 
6955
7233
  // src/web/api/volute/chat.ts
6956
7234
  import { zValidator as zValidator11 } from "@hono/zod-validator";
6957
- import { Hono as Hono27 } from "hono";
7235
+ import { Hono as Hono28 } from "hono";
6958
7236
  import { streamSSE as streamSSE6 } from "hono/streaming";
6959
7237
  import { z as z11 } from "zod";
6960
7238
 
@@ -7032,12 +7310,13 @@ async function routeDMOutbound(conversationId, senderName, contentBlocks) {
7032
7310
  // src/web/api/volute/chat.ts
7033
7311
  async function fanOutToMinds2(opts) {
7034
7312
  const participants = await getParticipants(opts.conversationId);
7035
- const mindParticipants = participants.filter((p) => p.userType === "mind");
7313
+ const mindParticipants = participants.filter(
7314
+ (p) => p.userType === "mind" || p.userType === "system"
7315
+ );
7036
7316
  const participantNames = participants.map((p) => p.username);
7037
7317
  const isDM = opts.isDM ?? participants.length === 2;
7038
- const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "channel");
7039
- const { getMindManager: getMindManager2 } = await import("./mind-manager-IPA6DZXD.js");
7040
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-TPS6OGCA.js");
7318
+ const { getMindManager: getMindManager2 } = await import("./mind-manager-BNCMGYXW.js");
7319
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
7041
7320
  const manager = getMindManager2();
7042
7321
  const sm = getSleepManagerIfReady2();
7043
7322
  const targetMinds = mindParticipants.map((ap) => {
@@ -7054,19 +7333,6 @@ async function fanOutToMinds2(opts) {
7054
7333
  ...opts.slugExtra
7055
7334
  });
7056
7335
  }
7057
- const channelEntry = {
7058
- platformId: opts.conversationId,
7059
- platform: "volute",
7060
- name: opts.convTitle ?? void 0,
7061
- type: channelEntryType
7062
- };
7063
- for (const ap of mindParticipants) {
7064
- try {
7065
- writeChannelEntry(ap.username, slugForMind(ap.username), channelEntry);
7066
- } catch (err) {
7067
- logger_default.warn(`failed to write channel entry for ${ap.username}`, logger_default.errorData(err));
7068
- }
7069
- }
7070
7336
  for (const mindName of targetMinds) {
7071
7337
  const target = opts.targetName ? opts.targetName(mindName) : mindName;
7072
7338
  const channel = slugForMind(mindName);
@@ -7103,7 +7369,7 @@ var chatSchema = z11.object({
7103
7369
  ).optional(),
7104
7370
  files: z11.array(fileSchema).optional()
7105
7371
  });
7106
- var app26 = new Hono27().post("/:name/chat", zValidator11("json", chatSchema), async (c) => {
7372
+ var app26 = new Hono28().post("/:name/chat", zValidator11("json", chatSchema), async (c) => {
7107
7373
  const name = c.req.param("name");
7108
7374
  const baseName = await getBaseName(name);
7109
7375
  const entry = await findMind(baseName);
@@ -7213,7 +7479,6 @@ var app26 = new Hono27().post("/:name/chat", zValidator11("json", chatSchema), a
7213
7479
  senderName,
7214
7480
  convTitle,
7215
7481
  isDM,
7216
- channelEntryType: isDM ? "dm" : "channel",
7217
7482
  slugExtra: conv ? { convType: conv.type, convName: conv.name } : void 0,
7218
7483
  targetName: (username) => username === baseName ? name : username
7219
7484
  });
@@ -7259,7 +7524,7 @@ var unifiedChatSchema2 = z11.object({
7259
7524
  images: z11.array(z11.object({ media_type: z11.string(), data: z11.string() })).optional(),
7260
7525
  files: z11.array(fileSchema).optional()
7261
7526
  });
7262
- var unifiedChatApp = new Hono27().post(
7527
+ var unifiedChatApp = new Hono28().post(
7263
7528
  "/chat",
7264
7529
  zValidator11("json", unifiedChatSchema2),
7265
7530
  async (c) => {
@@ -7340,7 +7605,6 @@ var unifiedChatApp = new Hono27().post(
7340
7605
  senderName,
7341
7606
  convTitle: conv.title,
7342
7607
  isDM,
7343
- channelEntryType: isDM ? "dm" : "channel",
7344
7608
  slugExtra: { convType: conv.type, convName: conv.name }
7345
7609
  });
7346
7610
  if (user.user_type === "mind" && body.message) {
@@ -7359,14 +7623,14 @@ var chat_default2 = app26;
7359
7623
 
7360
7624
  // src/web/api/volute/conversations.ts
7361
7625
  import { zValidator as zValidator12 } from "@hono/zod-validator";
7362
- import { Hono as Hono28 } from "hono";
7626
+ import { Hono as Hono29 } from "hono";
7363
7627
  import { z as z12 } from "zod";
7364
7628
  var createConvSchema = z12.object({
7365
7629
  title: z12.string().optional(),
7366
7630
  participantIds: z12.array(z12.number()).optional(),
7367
7631
  participantNames: z12.array(z12.string()).optional()
7368
7632
  });
7369
- var app27 = new Hono28().get("/:name/conversations", async (c) => {
7633
+ var app27 = new Hono29().get("/:name/conversations", async (c) => {
7370
7634
  const name = c.req.param("name");
7371
7635
  const user = c.get("user");
7372
7636
  let lookupId = user.id;
@@ -7478,14 +7742,14 @@ var conversations_default2 = app27;
7478
7742
 
7479
7743
  // src/web/api/volute/user-conversations.ts
7480
7744
  import { zValidator as zValidator13 } from "@hono/zod-validator";
7481
- import { Hono as Hono29 } from "hono";
7745
+ import { Hono as Hono30 } from "hono";
7482
7746
  import { streamSSE as streamSSE7 } from "hono/streaming";
7483
7747
  import { z as z13 } from "zod";
7484
7748
  var createSchema3 = z13.object({
7485
7749
  title: z13.string().optional(),
7486
7750
  participantNames: z13.array(z13.string()).min(1)
7487
7751
  });
7488
- var app28 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
7752
+ var app28 = new Hono30().use("*", authMiddleware).get("/", async (c) => {
7489
7753
  const user = c.get("user");
7490
7754
  const convs = await listConversationsWithParticipants(user.id);
7491
7755
  return c.json(convs);
@@ -7575,7 +7839,7 @@ var user_conversations_default = app28;
7575
7839
 
7576
7840
  // src/web/app.ts
7577
7841
  var httpLog = logger_default.child("http");
7578
- var app29 = new Hono30();
7842
+ var app29 = new Hono31();
7579
7843
  app29.onError((err, c) => {
7580
7844
  if (err instanceof HTTPException) {
7581
7845
  return err.getResponse();
@@ -7628,9 +7892,11 @@ app29.use("/api/prompts/*", authMiddleware);
7628
7892
  app29.use("/api/skills/*", authMiddleware);
7629
7893
  app29.use("/api/extensions/*", authMiddleware);
7630
7894
  app29.use("/api/bridges/*", authMiddleware);
7895
+ app29.use("/api/config/*", authMiddleware);
7631
7896
  app29.use("/api/v1/*", authMiddleware);
7632
7897
  app29.route("/api/setup", setup_default);
7633
7898
  app29.route("/public", public_files_default);
7899
+ app29.route("/api/config", config_default);
7634
7900
  var routes = app29.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/minds", minds_default).route("/api/minds", chat_default2).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", shared_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/conversations", user_conversations_default).route("/api/volute/channels", channels_default2).route("/api/volute", unifiedChatApp).route("/api/bridges", bridges_default).route("/api/extensions", extensions_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", chat_default);
7635
7901
  app29.route("/api/v1/minds", minds_default);
7636
7902
  app29.route("/api/v1/minds", typing_default);
@@ -7649,7 +7915,7 @@ app29.route("/api/v1/channels", channels_default2);
7649
7915
  var app_default = app29;
7650
7916
 
7651
7917
  // src/web/server.ts
7652
- import { existsSync as existsSync13 } from "fs";
7918
+ import { existsSync as existsSync12 } from "fs";
7653
7919
  import { readFile as readFile3, stat as stat3 } from "fs/promises";
7654
7920
  import { createServer as createHttpsServer } from "https";
7655
7921
  import { dirname as dirname2, extname as extname4, resolve as resolve18 } from "path";
@@ -7672,7 +7938,7 @@ async function startServer({
7672
7938
  let searchDir = dirname2(new URL(import.meta.url).pathname);
7673
7939
  for (let i = 0; i < 5; i++) {
7674
7940
  const candidate = resolve18(searchDir, "dist", "web-assets");
7675
- if (existsSync13(candidate)) {
7941
+ if (existsSync12(candidate)) {
7676
7942
  assetsDir = candidate;
7677
7943
  break;
7678
7944
  }
@@ -7778,8 +8044,18 @@ async function startDaemon(opts) {
7778
8044
  } catch (err) {
7779
8045
  logger_default.warn("failed to initialize shared repo", logger_default.errorData(err));
7780
8046
  }
7781
- await (await import("./db-SW5PL6QA.js")).getDb();
7782
- const { initSandbox } = await import("./sandbox-6ZEWQDVU.js");
8047
+ const { migrateSetupCompleted } = await import("./setup-SZIARWI6.js");
8048
+ migrateSetupCompleted();
8049
+ await (await import("./db-F34YLV7D.js")).getDb();
8050
+ try {
8051
+ const { eq: eq7, and: and5 } = await import("drizzle-orm");
8052
+ const { users: users2 } = await import("./schema-PA3M5ZKH.js");
8053
+ const db = await (await import("./db-F34YLV7D.js")).getDb();
8054
+ await db.update(users2).set({ role: "system" }).where(and5(eq7(users2.user_type, "system"), eq7(users2.role, "user")));
8055
+ } catch (err) {
8056
+ logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
8057
+ }
8058
+ const { initSandbox } = await import("./sandbox-JANNTX6U.js");
7783
8059
  await initSandbox();
7784
8060
  try {
7785
8061
  await syncBuiltinSkills();
@@ -7799,7 +8075,7 @@ async function startDaemon(opts) {
7799
8075
  logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
7800
8076
  }
7801
8077
  try {
7802
- const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-T5AW2USD.js");
8078
+ const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-6DMGES3I.js");
7803
8079
  await getOrCreateSystemUser2();
7804
8080
  } catch (err) {
7805
8081
  logger_default.warn(
@@ -7810,7 +8086,7 @@ async function startDaemon(opts) {
7810
8086
  const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes(32).toString("hex");
7811
8087
  let tls;
7812
8088
  if (opts.tailscale) {
7813
- const { getTailscaleTls } = await import("./tailscale-6DJKUMNF.js");
8089
+ const { getTailscaleTls } = await import("./tailscale-XHQBZROW.js");
7814
8090
  const tlsConfig = await getTailscaleTls();
7815
8091
  tls = { key: tlsConfig.key, cert: tlsConfig.cert };
7816
8092
  logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
@@ -7850,8 +8126,9 @@ async function startDaemon(opts) {
7850
8126
  const sleepManager = initSleepManager();
7851
8127
  sleepManager.start();
7852
8128
  const unsubscribeWebhook = initWebhook();
8129
+ await completeOrphanedTurns();
7853
8130
  const allMinds = await readAllMinds();
7854
- const runningEntries = allMinds.filter((e) => e.running);
8131
+ const runningEntries = allMinds.filter((e) => e.running && e.mindType !== "spirit");
7855
8132
  {
7856
8133
  const queue = [...runningEntries];
7857
8134
  const workers = Array.from({ length: Math.min(5, queue.length) }, async () => {
@@ -7878,10 +8155,28 @@ async function startDaemon(opts) {
7878
8155
  });
7879
8156
  await Promise.all(workers);
7880
8157
  }
8158
+ try {
8159
+ const { isSetupComplete: isSetupComplete2 } = await import("./setup-SZIARWI6.js");
8160
+ if (isSetupComplete2()) {
8161
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-N4W4UQRH.js");
8162
+ const { startSpiritFull } = await import("./mind-service-AV273WT4.js");
8163
+ await ensureSpiritProject();
8164
+ await syncSpiritTemplate();
8165
+ const spiritEntry = await findMind("volute");
8166
+ if (spiritEntry && !manager.isRunning("volute")) {
8167
+ await startSpiritFull("volute");
8168
+ }
8169
+ }
8170
+ } catch (err) {
8171
+ logger_default.warn(
8172
+ "failed to start system spirit \u2014 system replies will use aiComplete fallback",
8173
+ logger_default.errorData(err)
8174
+ );
8175
+ }
7881
8176
  bridgeManager.startBridges(daemonPort).catch((err) => {
7882
8177
  logger_default.warn("failed to start bridges", logger_default.errorData(err));
7883
8178
  });
7884
- import("./cloud-sync-O3LXIRN6.js").then(
8179
+ import("./cloud-sync-JN3NWKEM.js").then(
7885
8180
  ({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
7886
8181
  logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
7887
8182
  })
@@ -7889,7 +8184,7 @@ async function startDaemon(opts) {
7889
8184
  logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
7890
8185
  });
7891
8186
  try {
7892
- const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-75ELVKPV.js");
8187
+ const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-TCKWBZZG.js");
7893
8188
  backfillTemplateHashes();
7894
8189
  notifyVersionUpdate().catch((err) => {
7895
8190
  logger_default.warn("failed to send version update notifications", logger_default.errorData(err));