volute 0.50.0 → 0.52.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 (145) hide show
  1. package/dist/{activity-events-H7YI3BTF.js → activity-events-LQII6PZS.js} +3 -3
  2. package/dist/{ai-service-V4CTNGI3.js → ai-service-YW5SWCSB.js} +7 -3
  3. package/dist/{archive-ORM3ENHI.js → archive-FOHGXHPT.js} +3 -3
  4. package/dist/{auth-DT7GY7EZ.js → auth-KRDSM55Q.js} +6 -4
  5. package/dist/{avatar-image-QEPAP3A5.js → avatar-image-52WGXLHV.js} +3 -3
  6. package/dist/{backup-IE6PMXJO.js → backup-YVGV2ZNM.js} +7 -7
  7. package/dist/{chat-W5K6HCBE.js → chat-VAZ25KVT.js} +4 -4
  8. package/dist/{chunk-5IIVOWSP.js → chunk-2RNMBXD2.js} +16 -0
  9. package/dist/{chunk-U3EJJ7JN.js → chunk-5N32EN4O.js} +2 -2
  10. package/dist/{chunk-ZUG76D3P.js → chunk-5Y54AE3L.js} +1 -1
  11. package/dist/{chunk-5FVANDON.js → chunk-6NLW72HP.js} +2 -2
  12. package/dist/{chunk-ALIVXDM4.js → chunk-724DVYHU.js} +95 -51
  13. package/dist/{chunk-UC6BNMGT.js → chunk-7LT3YZUR.js} +15 -3
  14. package/dist/{chunk-PW65BXY5.js → chunk-7YJY3F2R.js} +11 -1
  15. package/dist/{chunk-7OZWKHA3.js → chunk-AXFLY5GU.js} +28 -5
  16. package/dist/{chunk-SSNZ6ZNN.js → chunk-CV6BNGFK.js} +3 -3
  17. package/dist/{chunk-MLZBBLMT.js → chunk-CW7BQKF6.js} +4 -4
  18. package/dist/{chunk-QFVGPKJI.js → chunk-DMK5ASKV.js} +1 -1
  19. package/dist/{chunk-WBBKYUGF.js → chunk-EDR47UE2.js} +2 -2
  20. package/dist/{chunk-632FJ23O.js → chunk-EGV6R6UF.js} +26 -7
  21. package/dist/{chunk-KAEZOVP6.js → chunk-FLIEXG2T.js} +1 -1
  22. package/dist/{chunk-5TPC4KCV.js → chunk-FXWBFDQR.js} +2 -2
  23. package/dist/{chunk-74T3CEXK.js → chunk-G7A5LEVI.js} +2 -2
  24. package/dist/{chunk-7UWXAJMK.js → chunk-GLM7TVWP.js} +1 -1
  25. package/dist/{chunk-CAIYV33P.js → chunk-H6X6CKDN.js} +2 -1
  26. package/dist/{chunk-C3KLHJDB.js → chunk-HTRSSOHY.js} +1 -1
  27. package/dist/{chunk-77IZUVBB.js → chunk-I7U5EEZC.js} +2 -2
  28. package/dist/{chunk-X67VT4QO.js → chunk-ICXY54ZC.js} +1 -1
  29. package/dist/{chunk-HW6Q7AMB.js → chunk-ITK6HV47.js} +6 -7
  30. package/dist/{chunk-5PKFJHCR.js → chunk-JHKBALS2.js} +2 -2
  31. package/dist/{chunk-SFRYRW37.js → chunk-K2F237KS.js} +9 -9
  32. package/dist/{chunk-7XDOTN4R.js → chunk-L7BKTZ5U.js} +1 -1
  33. package/dist/{chunk-AFSLNUTT.js → chunk-LAUCO2HC.js} +1 -1
  34. package/dist/{chunk-AJWFDIEY.js → chunk-M77B73VV.js} +1 -1
  35. package/dist/{chunk-4CN7OCJE.js → chunk-OXWUEU5V.js} +2 -2
  36. package/dist/{chunk-TGJHGVBZ.js → chunk-RPPTGPYF.js} +4 -4
  37. package/dist/{chunk-JI67D7O2.js → chunk-RU74YG46.js} +120 -25
  38. package/dist/{chunk-7FDWPQCU.js → chunk-SW2U6HRB.js} +6 -6
  39. package/dist/{chunk-574M6I7F.js → chunk-TPX6E6PL.js} +3 -3
  40. package/dist/{chunk-3BAFMGCJ.js → chunk-U54PJPTB.js} +2 -2
  41. package/dist/{chunk-6BM2I736.js → chunk-UDZLPR34.js} +1 -1
  42. package/dist/{chunk-E5S3ILVA.js → chunk-UFTDRTCP.js} +1 -1
  43. package/dist/{chunk-YQOYZ6G7.js → chunk-VPOMJMV6.js} +2 -2
  44. package/dist/{chunk-RRZ4RQQU.js → chunk-X67BDDZR.js} +5 -7
  45. package/dist/{chunk-636GFQNE.js → chunk-XRWUQPCA.js} +8 -7
  46. package/dist/{chunk-7DXLIYPY.js → chunk-YVR6EHWL.js} +6 -6
  47. package/dist/{chunk-EQKSRVDE.js → chunk-ZEZQNP2G.js} +113 -49
  48. package/dist/cli.js +19 -19
  49. package/dist/{clock-LH7UFNLJ.js → clock-ZT5YT6HA.js} +2 -2
  50. package/dist/{cloud-sync-IDJFVDEP.js → cloud-sync-H7EWZASD.js} +22 -22
  51. package/dist/{conversations-S2232Y5I.js → conversations-ZL6PHROD.js} +4 -4
  52. package/dist/{create-2M7DAHQX.js → create-G2RIKQYK.js} +1 -1
  53. package/dist/{daemon-restart-2AAGYFHE.js → daemon-restart-4FWUA5JF.js} +6 -6
  54. package/dist/daemon.js +622 -340
  55. package/dist/{db-QAY45Z56.js → db-BONH4SUG.js} +2 -2
  56. package/dist/{delete-O7PEFQDF.js → delete-CYVZUAV2.js} +4 -0
  57. package/dist/delivery-manager-VYXVKUMP.js +35 -0
  58. package/dist/{delivery-router-JE7SHYNU.js → delivery-router-RHANNTAC.js} +3 -3
  59. package/dist/{down-D4IRNEMJ.js → down-QSNEMTTX.js} +5 -5
  60. package/dist/echo-text-PEEV7FQU.js +39 -0
  61. package/dist/{env-ABM5IMA7.js → env-IBW2QI6K.js} +1 -1
  62. package/dist/{env-AZK5X4W7.js → env-SQFCWW52.js} +3 -3
  63. package/dist/{exec-2H65LMTA.js → exec-ABD3DYA2.js} +3 -3
  64. package/dist/{export-3E3EL6W3.js → export-VBNWM7I4.js} +3 -3
  65. package/dist/{extensions-7KP3X6G7.js → extensions-N6L5NYTK.js} +22 -22
  66. package/dist/{files-6BMKVG5G.js → files-BRY5RRIM.js} +3 -3
  67. package/dist/{import-UPNTEBBT.js → import-DB2KULRO.js} +6 -6
  68. package/dist/{isolation-OKDFM6WA.js → isolation-GVV2IBAU.js} +3 -3
  69. package/dist/{join-PYA36HEC.js → join-KLRU72JJ.js} +10 -2
  70. package/dist/{list-4BVGH7AN.js → list-UQGIW7OF.js} +1 -1
  71. package/dist/{login-MOXVLULE.js → login-SB5OENMV.js} +2 -2
  72. package/dist/{logout-DWD3OQVT.js → logout-YFZAJRPY.js} +2 -2
  73. package/dist/message-delivery-7URC52ME.js +43 -0
  74. package/dist/{migrate-thread-config-4A5LW36Z.js → migrate-thread-config-EQSEP2XG.js} +2 -2
  75. package/dist/{mind-NYRALSV2.js → mind-NEXGT3KE.js} +9 -9
  76. package/dist/{mind-activity-tracker-L7GCYXQR.js → mind-activity-tracker-QF6DCX2X.js} +4 -4
  77. package/dist/{mind-history-NXXOOTX6.js → mind-history-OGNR4L7W.js} +1 -1
  78. package/dist/mind-service-E26ZQXDS.js +47 -0
  79. package/dist/{mind-sleep-TH6BRQ5X.js → mind-sleep-LZAFBDZD.js} +2 -2
  80. package/dist/{package-XYSV7NUT.js → package-2W2XKSIP.js} +2 -2
  81. package/dist/{prompts-QKFPX6DQ.js → prompts-NBO3HRK3.js} +3 -3
  82. package/dist/{read-ZXGUT4QC.js → read-RJ5NAW5J.js} +3 -14
  83. package/dist/{registry-4UF6OCOE.js → registry-TRC2HJXR.js} +2 -2
  84. package/dist/{sandbox-EGEU5GGE.js → sandbox-NNWCTHRI.js} +3 -3
  85. package/dist/scheduler-JTTWZDYU.js +25 -0
  86. package/dist/{schema-C2GIPD4V.js → schema-ZZQVM4GR.js} +3 -1
  87. package/dist/{seed-3IMBOAGK.js → seed-64XEHRUP.js} +1 -1
  88. package/dist/{seed-cmd-U73XVQMI.js → seed-cmd-UEL2QB3Q.js} +2 -2
  89. package/dist/{seed-create-FBLNVSUP.js → seed-create-4JRQDPGA.js} +3 -3
  90. package/dist/{seed-readiness-QRA46ZJQ.js → seed-readiness-DYOVCYHN.js} +4 -4
  91. package/dist/{seed-sprout-3B4SZX7J.js → seed-sprout-NIDIOJXT.js} +7 -7
  92. package/dist/{send-NBRZJXKX.js → send-2YKOZJJ5.js} +81 -49
  93. package/dist/{service-XMXAXGJD.js → service-XQPMZAGI.js} +4 -4
  94. package/dist/{service-install-MOKTEG7B.js → service-install-2EWD7KXD.js} +5 -5
  95. package/dist/{setup-JGYAXXW7.js → setup-4IX6PCD3.js} +6 -6
  96. package/dist/{setup-CAJFOR2N.js → setup-WZSXHHRJ.js} +2 -2
  97. package/dist/{skill-NXIHDQCG.js → skill-B5OJEQUA.js} +1 -1
  98. package/dist/skills/volute-mind/references/variants.md +1 -1
  99. package/dist/{skills-5MSMT7PT.js → skills-MPUALV3S.js} +4 -4
  100. package/dist/sleep-manager-OFP2GFXF.js +41 -0
  101. package/dist/{spirit-IL5EMWYZ.js → spirit-4ECYIVGE.js} +7 -7
  102. package/dist/spirit-availability-Y54Y3675.js +42 -0
  103. package/dist/{sprout-UPXGJ5HX.js → sprout-GJMZFRGZ.js} +1 -1
  104. package/dist/{status-IL67CYM4.js → status-MWLE3RVC.js} +5 -5
  105. package/dist/{system-events-A6IGCEC3.js → system-events-V43HDA3T.js} +5 -3
  106. package/dist/{tailscale-SJUW2ETR.js → tailscale-KHNZFC7L.js} +2 -2
  107. package/dist/{template-hash-GSOUARW4.js → template-hash-YKWMVYMM.js} +2 -2
  108. package/dist/{template-staleness-T27VCGYA.js → template-staleness-PY2CULH6.js} +5 -5
  109. package/dist/{turn-tracker-6EI2EL7G.js → turn-tracker-C5PVIC3B.js} +5 -5
  110. package/dist/{up-CLL4665A.js → up-H6LJZDRK.js} +5 -5
  111. package/dist/{update-HVAUCQPN.js → update-CIWI2JYA.js} +5 -5
  112. package/dist/{update-check-2MGBBAFF.js → update-check-SZ7B5IFI.js} +3 -3
  113. package/dist/variant-cleanup-STC6B5HR.js +22 -0
  114. package/dist/{version-notify-7BUHWZNT.js → version-notify-LBVHH5YV.js} +8 -8
  115. package/dist/web-assets/assets/index-B_eYTa3R.js +97 -0
  116. package/dist/web-assets/assets/index-DhZrzddW.css +1 -0
  117. package/dist/web-assets/index.html +2 -2
  118. package/drizzle/0018_api_tokens.sql +20 -0
  119. package/drizzle/meta/_journal.json +7 -0
  120. package/package.json +2 -2
  121. package/templates/_base/{biome.json.tmpl → biome.json} +2 -1
  122. package/templates/_base/gitignore +44 -0
  123. package/templates/_base/home/public/.gitkeep +0 -0
  124. package/templates/_base/src/lib/auto-commit.ts +49 -11
  125. package/templates/_base/src/lib/format-prefix.ts +15 -8
  126. package/templates/_base/src/lib/router.ts +13 -8
  127. package/templates/claude/volute-template.json +1 -3
  128. package/templates/codex/volute-template.json +1 -3
  129. package/templates/pi/{package.json.tmpl → package.json} +2 -2
  130. package/templates/pi/volute-template.json +1 -3
  131. package/dist/delivery-manager-B4OKWD2P.js +0 -33
  132. package/dist/echo-text-WYAAODBV.js +0 -39
  133. package/dist/message-delivery-3456PXB7.js +0 -43
  134. package/dist/mind-service-5TA7UJAT.js +0 -47
  135. package/dist/scheduler-UV6RUJ7P.js +0 -25
  136. package/dist/sleep-manager-LIEPO6WG.js +0 -41
  137. package/dist/spirit-availability-YFEGJNJ2.js +0 -42
  138. package/dist/variant-cleanup-HJ7Z75R6.js +0 -22
  139. package/dist/web-assets/assets/index-ClsUcXiW.js +0 -95
  140. package/dist/web-assets/assets/index-Dz3wTbtR.css +0 -1
  141. /package/templates/_base/home/.config/{config.json.tmpl → config.json} +0 -0
  142. /package/templates/claude/{package.json.tmpl → package.json} +0 -0
  143. /package/templates/codex/home/.config/{config.json.tmpl → config.json} +0 -0
  144. /package/templates/codex/{package.json.tmpl → package.json} +0 -0
  145. /package/templates/pi/home/.config/{config.json.tmpl → config.json} +0 -0
package/dist/daemon.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  readBackupState,
7
7
  resticVersion,
8
8
  runBackup
9
- } from "./chunk-WBBKYUGF.js";
9
+ } from "./chunk-EDR47UE2.js";
10
10
  import {
11
11
  acceptPending,
12
12
  formatFileSize,
@@ -14,44 +14,44 @@ import {
14
14
  rejectPending,
15
15
  stageFile,
16
16
  validateFilePath
17
- } from "./chunk-QFVGPKJI.js";
17
+ } from "./chunk-DMK5ASKV.js";
18
18
  import {
19
19
  findOpenClawSession,
20
20
  importOpenClawConnectors,
21
21
  importPiSession,
22
22
  parseNameFromIdentity
23
- } from "./chunk-77IZUVBB.js";
23
+ } from "./chunk-I7U5EEZC.js";
24
24
  import {
25
25
  SPIRIT_NOTICE_PREFIX,
26
26
  ensureSpiritAvailable
27
- } from "./chunk-MLZBBLMT.js";
27
+ } from "./chunk-CW7BQKF6.js";
28
28
  import {
29
29
  isHomeOnlyArchive
30
- } from "./chunk-KAEZOVP6.js";
30
+ } from "./chunk-FLIEXG2T.js";
31
31
  import {
32
32
  evaluateSeedChecklist
33
- } from "./chunk-5PKFJHCR.js";
33
+ } from "./chunk-JHKBALS2.js";
34
34
  import {
35
35
  isTemplateStale
36
- } from "./chunk-SSNZ6ZNN.js";
36
+ } from "./chunk-CV6BNGFK.js";
37
37
  import {
38
38
  computeTemplateHash
39
- } from "./chunk-C3KLHJDB.js";
39
+ } from "./chunk-HTRSSOHY.js";
40
40
  import {
41
41
  cleanupVariant
42
- } from "./chunk-HW6Q7AMB.js";
42
+ } from "./chunk-ITK6HV47.js";
43
43
  import {
44
44
  checkForUpdate,
45
45
  checkForUpdateCached,
46
46
  getCurrentVersion
47
- } from "./chunk-AFSLNUTT.js";
47
+ } from "./chunk-LAUCO2HC.js";
48
48
  import {
49
49
  PLATFORMS,
50
50
  echoTextToChannel,
51
51
  fixModelEscapes,
52
52
  getPlatformDriver,
53
53
  routeOutboundBridge
54
- } from "./chunk-636GFQNE.js";
54
+ } from "./chunk-XRWUQPCA.js";
55
55
  import {
56
56
  getBridgeConfig,
57
57
  readBridgesConfig,
@@ -60,7 +60,7 @@ import {
60
60
  resolveChannelMapping,
61
61
  setBridgeConfig,
62
62
  setChannelMapping
63
- } from "./chunk-7XDOTN4R.js";
63
+ } from "./chunk-L7BKTZ5U.js";
64
64
  import {
65
65
  SESSION_MAX_AGE,
66
66
  announceSprout,
@@ -86,9 +86,11 @@ import {
86
86
  installNpmExtension,
87
87
  invalidateMindUserCache,
88
88
  invalidateSessionCache,
89
+ issueApiToken,
89
90
  joinSystemChannel,
90
91
  joinSystemChannelForMind,
91
92
  linkToolResultToTurn,
93
+ listApiTokens,
92
94
  loadAllExtensions,
93
95
  notifyExtensionsDaemonStart,
94
96
  notifyExtensionsDaemonStop,
@@ -97,13 +99,14 @@ import {
97
99
  requireAdmin,
98
100
  requireAdminOrSystem,
99
101
  requireSelf,
102
+ revokeApiToken,
100
103
  setExtensionEnabled,
101
104
  startMindFull,
102
105
  stopMindFull,
103
106
  uninstallNpmExtension,
104
107
  writeSystemsConfig
105
- } from "./chunk-EQKSRVDE.js";
106
- import "./chunk-SFRYRW37.js";
108
+ } from "./chunk-ZEZQNP2G.js";
109
+ import "./chunk-K2F237KS.js";
107
110
  import {
108
111
  applyInitFiles,
109
112
  applyTemplateHomeFiles,
@@ -112,11 +115,11 @@ import {
112
115
  findTemplatesRoot,
113
116
  isKnownTemplate,
114
117
  listFiles
115
- } from "./chunk-RRZ4RQQU.js";
118
+ } from "./chunk-X67BDDZR.js";
116
119
  import {
117
120
  getScheduler,
118
121
  initScheduler
119
- } from "./chunk-7FDWPQCU.js";
122
+ } from "./chunk-SW2U6HRB.js";
120
123
  import {
121
124
  checkStaleSend,
122
125
  formatHoldNotice,
@@ -125,7 +128,7 @@ import {
125
128
  initDeliveryManager,
126
129
  isConversationId,
127
130
  publishTypingForChannels
128
- } from "./chunk-JI67D7O2.js";
131
+ } from "./chunk-RU74YG46.js";
129
132
  import {
130
133
  MindStartupError,
131
134
  RestartTracker,
@@ -135,10 +138,10 @@ import {
135
138
  initMindManager,
136
139
  syncProviderToMinds,
137
140
  tryGetMindManager
138
- } from "./chunk-ALIVXDM4.js";
141
+ } from "./chunk-724DVYHU.js";
139
142
  import {
140
143
  extractTextContent
141
- } from "./chunk-6BM2I736.js";
144
+ } from "./chunk-UDZLPR34.js";
142
145
  import {
143
146
  SYSTEM_MIND,
144
147
  assignSession,
@@ -158,7 +161,7 @@ import {
158
161
  takeErrored,
159
162
  trackToolUse,
160
163
  utcDateTimeStr
161
- } from "./chunk-7DXLIYPY.js";
164
+ } from "./chunk-YVR6EHWL.js";
162
165
  import {
163
166
  SEED_SKILLS,
164
167
  STANDARD_SKILLS,
@@ -180,12 +183,13 @@ import {
180
183
  syncBuiltinSkills,
181
184
  uninstallSkill,
182
185
  updateSkill
183
- } from "./chunk-TGJHGVBZ.js";
186
+ } from "./chunk-RPPTGPYF.js";
184
187
  import {
185
188
  approveUser,
186
189
  changePassword,
187
190
  countAdmins,
188
191
  createUser,
192
+ deleteExternalMindUser,
189
193
  deleteMindUser as deleteMindUser2,
190
194
  deleteUser,
191
195
  getDisplayNames,
@@ -201,9 +205,10 @@ import {
201
205
  updateUserProfile,
202
206
  verifyUser,
203
207
  withSenderDisplayNames
204
- } from "./chunk-UC6BNMGT.js";
208
+ } from "./chunk-7LT3YZUR.js";
205
209
  import {
206
210
  captureReflection,
211
+ cleanExpiredEvents,
207
212
  clearDeliveredEvents,
208
213
  deliverEvent,
209
214
  drainEvents,
@@ -214,7 +219,7 @@ import {
214
219
  listEvents,
215
220
  parseMeta,
216
221
  recordNotice
217
- } from "./chunk-7OZWKHA3.js";
222
+ } from "./chunk-AXFLY5GU.js";
218
223
  import {
219
224
  publish as publish2,
220
225
  subscribe as subscribe2,
@@ -227,11 +232,11 @@ import {
227
232
  getPrompt,
228
233
  getPromptIfCustom,
229
234
  substitute
230
- } from "./chunk-5TPC4KCV.js";
235
+ } from "./chunk-FXWBFDQR.js";
231
236
  import {
232
237
  isSandboxEnabled,
233
238
  wrapForSandbox
234
- } from "./chunk-ZUG76D3P.js";
239
+ } from "./chunk-5Y54AE3L.js";
235
240
  import {
236
241
  addMessage,
237
242
  createChannel,
@@ -267,22 +272,22 @@ import {
267
272
  subscribe as subscribe3,
268
273
  subscribeParticipantAdded,
269
274
  updateChannelSettings
270
- } from "./chunk-574M6I7F.js";
275
+ } from "./chunk-TPX6E6PL.js";
271
276
  import {
272
277
  getActiveMinds,
273
278
  onMindEvent,
274
279
  stopAll
275
- } from "./chunk-E5S3ILVA.js";
280
+ } from "./chunk-UFTDRTCP.js";
276
281
  import {
277
282
  broadcast,
278
283
  getEventsSince,
279
284
  nextEventId,
280
285
  publish,
281
286
  subscribe
282
- } from "./chunk-3BAFMGCJ.js";
287
+ } from "./chunk-U54PJPTB.js";
283
288
  import {
284
289
  normalizeAvatar
285
- } from "./chunk-X67VT4QO.js";
290
+ } from "./chunk-ICXY54ZC.js";
286
291
  import {
287
292
  safeResolveWithinBase
288
293
  } from "./chunk-DW4TRDOZ.js";
@@ -290,7 +295,7 @@ import {
290
295
  readVoluteConfig,
291
296
  writeVoluteConfig
292
297
  } from "./chunk-WCFSBH33.js";
293
- import "./chunk-5FVANDON.js";
298
+ import "./chunk-6NLW72HP.js";
294
299
  import {
295
300
  chownMindDir,
296
301
  chownMindFile,
@@ -302,15 +307,16 @@ import {
302
307
  isIsolationEnabled,
303
308
  resolveVoluteBin,
304
309
  wrapForIsolation
305
- } from "./chunk-AJWFDIEY.js";
310
+ } from "./chunk-M77B73VV.js";
306
311
  import {
307
312
  loadMergedEnv,
308
313
  mindEnvPath,
309
314
  readEnv,
310
315
  sharedEnvPath,
311
316
  writeEnv
312
- } from "./chunk-7UWXAJMK.js";
317
+ } from "./chunk-GLM7TVWP.js";
313
318
  import {
319
+ OAuthRefreshError,
314
320
  addCustomModel,
315
321
  aiCompleteUtility,
316
322
  buildCustomModel,
@@ -334,7 +340,7 @@ import {
334
340
  setProviderRefreshHook,
335
341
  setUtilityModel,
336
342
  unqualifyModelId
337
- } from "./chunk-632FJ23O.js";
343
+ } from "./chunk-EGV6R6UF.js";
338
344
  import {
339
345
  logBuffer,
340
346
  logger_default
@@ -374,7 +380,7 @@ import {
374
380
  voluteHome,
375
381
  voluteSystemDir,
376
382
  writeGlobalConfig
377
- } from "./chunk-CAIYV33P.js";
383
+ } from "./chunk-H6X6CKDN.js";
378
384
  import {
379
385
  activity,
380
386
  channels,
@@ -386,14 +392,14 @@ import {
386
392
  systemPrompts,
387
393
  turns,
388
394
  users
389
- } from "./chunk-5IIVOWSP.js";
395
+ } from "./chunk-2RNMBXD2.js";
390
396
  import "./chunk-K3NQKI34.js";
391
397
 
392
398
  // packages/daemon/src/daemon.ts
393
399
  import { randomBytes as randomBytes3 } from "crypto";
394
400
  import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
395
401
  import { homedir as homedir3 } from "os";
396
- import { resolve as resolve18 } from "path";
402
+ import { resolve as resolve20 } from "path";
397
403
  import { format } from "util";
398
404
 
399
405
  // packages/daemon/src/lib/daemon/backup-manager.ts
@@ -504,7 +510,7 @@ var BackupManager = class {
504
510
  } catch (pubErr) {
505
511
  blog.error("failed to publish backup-failure activity", logger_default.errorData(pubErr));
506
512
  }
507
- const { deliverEvent: deliverEvent2 } = await import("./system-events-A6IGCEC3.js");
513
+ const { deliverEvent: deliverEvent2 } = await import("./system-events-V43HDA3T.js");
508
514
  await deliverEvent2(getSpiritName(), {
509
515
  type: "notice",
510
516
  meta: { subtype: "backup" },
@@ -728,8 +734,8 @@ var BridgeManager = class {
728
734
  if (!tracked) return;
729
735
  this.stopping.add(platform);
730
736
  this.bridges.delete(platform);
731
- await new Promise((resolve19) => {
732
- tracked.child.on("exit", () => resolve19());
737
+ await new Promise((resolve21) => {
738
+ tracked.child.on("exit", () => resolve21());
733
739
  try {
734
740
  if (tracked.child.pid) {
735
741
  process.kill(-tracked.child.pid, "SIGTERM");
@@ -740,7 +746,7 @@ var BridgeManager = class {
740
746
  if (err instanceof Error && err.code !== "ESRCH") {
741
747
  blog2.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
742
748
  }
743
- resolve19();
749
+ resolve21();
744
750
  }
745
751
  setTimeout(() => {
746
752
  try {
@@ -751,7 +757,7 @@ var BridgeManager = class {
751
757
  }
752
758
  } catch {
753
759
  }
754
- resolve19();
760
+ resolve21();
755
761
  }, 5e3);
756
762
  });
757
763
  this.stopping.delete(platform);
@@ -861,6 +867,11 @@ async function runMaintenance() {
861
867
  } catch (err) {
862
868
  logger_default.warn("maintenance: failed to clean expired logs", logger_default.errorData(err));
863
869
  }
870
+ try {
871
+ await cleanExpiredEvents();
872
+ } catch (err) {
873
+ logger_default.warn("maintenance: failed to clean expired events", logger_default.errorData(err));
874
+ }
864
875
  }
865
876
  function startMaintenanceInterval(intervalMs = MAINTENANCE_INTERVAL_MS, task = runMaintenance) {
866
877
  const handle = setInterval(() => void task(), intervalMs);
@@ -1112,6 +1123,7 @@ async function xaiSearch(_query, _cred) {
1112
1123
  }
1113
1124
 
1114
1125
  // packages/daemon/src/lib/services/imagegen.ts
1126
+ var igLog = logger_default.child("imagegen");
1115
1127
  var PROVIDERS = {
1116
1128
  replicate: {
1117
1129
  envVar: "REPLICATE_API_TOKEN",
@@ -1275,12 +1287,19 @@ async function resolveCredential(providerId) {
1275
1287
  const config2 = readGlobalConfig();
1276
1288
  const configKey = config2.imagegen?.providers?.[providerId]?.apiKey;
1277
1289
  if (configKey) return { kind: "api_key", token: configKey };
1278
- const oauth = await resolveOAuthCredentials(aiProviderId);
1279
- if (oauth) return { kind: "oauth", token: oauth.access };
1290
+ let oauthError;
1291
+ try {
1292
+ const oauth = await resolveOAuthCredentials(aiProviderId);
1293
+ if (oauth) return { kind: "oauth", token: oauth.access };
1294
+ } catch (err) {
1295
+ if (!(err instanceof OAuthRefreshError)) throw err;
1296
+ oauthError = err;
1297
+ }
1280
1298
  const aiKey = config2.ai?.providers?.[aiProviderId]?.apiKey;
1281
1299
  if (aiKey) return { kind: "api_key", token: aiKey };
1282
1300
  const envKey = provider.envVar ? process.env[provider.envVar] : void 0;
1283
1301
  if (envKey) return { kind: "api_key", token: envKey };
1302
+ if (oauthError) throw oauthError;
1284
1303
  return void 0;
1285
1304
  }
1286
1305
  function getEnabledModels2() {
@@ -1315,12 +1334,27 @@ async function searchModels(query, provider) {
1315
1334
  return providerDef.search(query || "text to image", cred);
1316
1335
  }
1317
1336
  const searches = [];
1337
+ let transientError;
1318
1338
  for (const [id, def] of Object.entries(PROVIDERS)) {
1319
- const cred = await resolveCredential(id);
1339
+ let cred;
1340
+ try {
1341
+ cred = await resolveCredential(id);
1342
+ } catch (err) {
1343
+ if (!(err instanceof OAuthRefreshError)) throw err;
1344
+ igLog.warn(`skipping ${id} in model search: OAuth temporarily unavailable`);
1345
+ transientError = err;
1346
+ continue;
1347
+ }
1320
1348
  if (!cred) continue;
1321
- searches.push(def.search(query || "text to image", cred).catch(() => []));
1349
+ searches.push(
1350
+ def.search(query || "text to image", cred).catch((err) => {
1351
+ igLog.warn(`model search failed for ${id}`, logger_default.errorData(err));
1352
+ return [];
1353
+ })
1354
+ );
1322
1355
  }
1323
1356
  if (searches.length === 0) {
1357
+ if (transientError) throw transientError;
1324
1358
  throw new Error("No imagegen providers configured");
1325
1359
  }
1326
1360
  return (await Promise.all(searches)).flat();
@@ -1382,7 +1416,7 @@ async function generateImage(model, prompt, signal) {
1382
1416
 
1383
1417
  // packages/daemon/src/web/api/system.ts
1384
1418
  var DEFAULT_API_URL = "https://volute.systems";
1385
- var igLog = logger_default.child("imagegen");
1419
+ var igLog2 = logger_default.child("imagegen");
1386
1420
  var app = new Hono().post("/restart", requireAdmin, (c) => {
1387
1421
  setTimeout(() => process.exit(1), 200);
1388
1422
  return c.json({ ok: true });
@@ -1400,10 +1434,10 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1400
1434
  stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
1401
1435
  });
1402
1436
  });
1403
- await new Promise((resolve19) => {
1437
+ await new Promise((resolve21) => {
1404
1438
  stream.onAbort(() => {
1405
1439
  unsubscribe();
1406
- resolve19();
1440
+ resolve21();
1407
1441
  });
1408
1442
  });
1409
1443
  });
@@ -1428,7 +1462,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1428
1462
  config2.name = name.trim() || void 0;
1429
1463
  writeGlobalConfig(config2);
1430
1464
  if (config2.name !== previousName) {
1431
- const { notifySpiritSystemChange } = await import("./spirit-IL5EMWYZ.js");
1465
+ const { notifySpiritSystemChange } = await import("./spirit-4ECYIVGE.js");
1432
1466
  await notifySpiritSystemChange();
1433
1467
  }
1434
1468
  return c.json({ name: config2.name ?? null });
@@ -1621,7 +1655,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1621
1655
  const results = await searchModels(q || void 0, provider || void 0);
1622
1656
  return c.json(results);
1623
1657
  } catch (err) {
1624
- igLog.error("model search failed", logger_default.errorData(err));
1658
+ igLog2.error("model search failed", logger_default.errorData(err));
1625
1659
  return c.json({ error: err instanceof Error ? err.message : "Search failed" }, 500);
1626
1660
  }
1627
1661
  }).post(
@@ -1638,7 +1672,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1638
1672
  }
1639
1673
  });
1640
1674
  } catch (err) {
1641
- igLog.error("image generation failed", logger_default.errorData(err));
1675
+ igLog2.error("image generation failed", logger_default.errorData(err));
1642
1676
  return c.json({ error: err instanceof Error ? err.message : "Generation failed" }, 500);
1643
1677
  }
1644
1678
  }
@@ -1787,8 +1821,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1787
1821
  createdAt: Date.now()
1788
1822
  };
1789
1823
  oauthFlows.set(flowId, flow);
1790
- const promptPromise = needsManualCode ? new Promise((resolve19) => {
1791
- flow.resolveCode = resolve19;
1824
+ const promptPromise = needsManualCode ? new Promise((resolve21) => {
1825
+ flow.resolveCode = resolve21;
1792
1826
  }) : void 0;
1793
1827
  oauthProvider.login({
1794
1828
  onAuth: (info) => {
@@ -1827,7 +1861,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1827
1861
  existing.error = err instanceof Error ? err.message : String(err);
1828
1862
  }
1829
1863
  });
1830
- await new Promise((resolve19) => setTimeout(resolve19, 2e3));
1864
+ await new Promise((resolve21) => setTimeout(resolve21, 2e3));
1831
1865
  const state = oauthFlows.get(flowId);
1832
1866
  return c.json({
1833
1867
  flowId,
@@ -2088,8 +2122,8 @@ var app2 = new Hono2().get("/events", async (c) => {
2088
2122
  });
2089
2123
  }, 15e3);
2090
2124
  cleanups.push(() => clearInterval(keepAlive));
2091
- await new Promise((resolve19) => {
2092
- stream.onAbort(() => resolve19());
2125
+ await new Promise((resolve21) => {
2126
+ stream.onAbort(() => resolve21());
2093
2127
  });
2094
2128
  } finally {
2095
2129
  for (const cleanup of cleanups) {
@@ -2122,6 +2156,11 @@ function isNotModified(c, etag) {
2122
2156
  }
2123
2157
 
2124
2158
  // packages/daemon/src/web/api/auth.ts
2159
+ async function withExternalFlag(list) {
2160
+ if (!list.some((u) => u.user_type === "mind")) return list;
2161
+ const local = new Set((await readAllMinds()).map((m) => m.name));
2162
+ return list.map((u) => u.user_type === "mind" ? { ...u, external: !local.has(u.username) } : u);
2163
+ }
2125
2164
  function tryJoinSystem(userId) {
2126
2165
  if (!process.env.VOLUTE_DAEMON_TOKEN) return;
2127
2166
  joinSystemChannel(userId).catch(() => {
@@ -2145,6 +2184,12 @@ var profileSchema = z2.object({
2145
2184
  display_name: z2.string().max(100).nullable().optional(),
2146
2185
  description: z2.string().max(500).nullable().optional()
2147
2186
  });
2187
+ var registerMindSchema = z2.object({
2188
+ name: z2.string().min(1),
2189
+ displayName: z2.string().max(100).optional(),
2190
+ description: z2.string().max(500).optional(),
2191
+ tokenLabel: z2.string().max(100).optional()
2192
+ });
2148
2193
  var AVATAR_MIME = {
2149
2194
  ".png": "image/png",
2150
2195
  ".jpg": "image/jpeg",
@@ -2156,7 +2201,39 @@ var MAX_AVATAR_SIZE = 2 * 1024 * 1024;
2156
2201
  function avatarsDir() {
2157
2202
  return resolve3(voluteHome(), "avatars");
2158
2203
  }
2159
- var authenticated = new Hono3().use(authMiddleware).post("/change-password", zValidator2("json", changePasswordSchema), async (c) => {
2204
+ var authenticated = new Hono3().use(authMiddleware).post("/minds", requireAdmin, zValidator2("json", registerMindSchema), async (c) => {
2205
+ const { name, displayName, description, tokenLabel } = c.req.valid("json");
2206
+ const invalid = validateMindName(name);
2207
+ if (invalid) return c.json({ error: invalid }, 400);
2208
+ if (await getUserByUsername(name)) {
2209
+ return c.json({ error: `"${name}" is already taken` }, 409);
2210
+ }
2211
+ const db = await getDb();
2212
+ let user;
2213
+ try {
2214
+ [user] = await db.insert(users).values({
2215
+ username: name,
2216
+ password_hash: "!mind",
2217
+ role: "user",
2218
+ user_type: "mind",
2219
+ display_name: displayName ?? null,
2220
+ description: description ?? null
2221
+ }).returning({
2222
+ id: users.id,
2223
+ username: users.username,
2224
+ user_type: users.user_type,
2225
+ role: users.role,
2226
+ display_name: users.display_name
2227
+ });
2228
+ } catch (err) {
2229
+ if (await getUserByUsername(name)) {
2230
+ return c.json({ error: `"${name}" is already taken` }, 409);
2231
+ }
2232
+ throw err;
2233
+ }
2234
+ const { id: tokenId, token } = await issueApiToken(user.id, tokenLabel);
2235
+ return c.json({ name, user, token, tokenId }, 201);
2236
+ }).post("/change-password", zValidator2("json", changePasswordSchema), async (c) => {
2160
2237
  const user = c.get("user");
2161
2238
  const { currentPassword, newPassword } = c.req.valid("json");
2162
2239
  const ok = await changePassword(user.id, currentPassword, newPassword);
@@ -2237,9 +2314,9 @@ var admin = new Hono3().use(authMiddleware).get("/users", async (c) => {
2237
2314
  }
2238
2315
  const type = c.req.query("type");
2239
2316
  if (type === "human" || type === "mind") {
2240
- return c.json(await listUsersByType(type));
2317
+ return c.json(await withExternalFlag(await listUsersByType(type)));
2241
2318
  }
2242
- return c.json(await listUsers());
2319
+ return c.json(await withExternalFlag(await listUsers()));
2243
2320
  }).get("/users/pending", async (c) => {
2244
2321
  const user = c.get("user");
2245
2322
  if (user.role !== "admin") return c.json({ error: "Forbidden" }, 403);
@@ -2272,7 +2349,42 @@ var admin = new Hono3().use(authMiddleware).get("/users", async (c) => {
2272
2349
  await setUserRole(id, role);
2273
2350
  return c.json({ ok: true });
2274
2351
  }
2275
- ).put("/users/:id/profile", zValidator2("json", profileSchema), async (c) => {
2352
+ ).post(
2353
+ "/users/:id/tokens",
2354
+ requireAdmin,
2355
+ zValidator2("json", z2.object({ label: z2.string().max(200).optional() })),
2356
+ async (c) => {
2357
+ const id = parseInt(c.req.param("id"), 10);
2358
+ if (Number.isNaN(id)) return c.json({ error: "Invalid user ID" }, 400);
2359
+ const target = await getUser(id);
2360
+ if (!target) return c.json({ error: "User not found" }, 404);
2361
+ const { label } = c.req.valid("json");
2362
+ const issued = await issueApiToken(id, label);
2363
+ return c.json(
2364
+ {
2365
+ id: issued.id,
2366
+ token: issued.token,
2367
+ label: issued.label,
2368
+ createdAt: issued.created_at
2369
+ },
2370
+ 201
2371
+ );
2372
+ }
2373
+ ).get("/users/:id/tokens", requireAdmin, async (c) => {
2374
+ const id = parseInt(c.req.param("id"), 10);
2375
+ if (Number.isNaN(id)) return c.json({ error: "Invalid user ID" }, 400);
2376
+ const target = await getUser(id);
2377
+ if (!target) return c.json({ error: "User not found" }, 404);
2378
+ const tokens = await listApiTokens(id);
2379
+ return c.json(tokens.map((t) => ({ id: t.id, label: t.label, createdAt: t.created_at })));
2380
+ }).delete("/users/:id/tokens/:tokenId", requireAdmin, async (c) => {
2381
+ const id = parseInt(c.req.param("id"), 10);
2382
+ const tokenId = parseInt(c.req.param("tokenId"), 10);
2383
+ if (Number.isNaN(id) || Number.isNaN(tokenId)) return c.json({ error: "Invalid ID" }, 400);
2384
+ const revoked = await revokeApiToken(id, tokenId);
2385
+ if (!revoked) return c.json({ error: "Token not found" }, 404);
2386
+ return c.json({ ok: true });
2387
+ }).put("/users/:id/profile", zValidator2("json", profileSchema), async (c) => {
2276
2388
  const user = c.get("user");
2277
2389
  if (user.role !== "admin") return c.json({ error: "Forbidden" }, 403);
2278
2390
  const id = parseInt(c.req.param("id"), 10);
@@ -2301,7 +2413,11 @@ var admin = new Hono3().use(authMiddleware).get("/users", async (c) => {
2301
2413
  if (adminCount <= 1) return c.json({ error: "Cannot delete the last admin" }, 400);
2302
2414
  }
2303
2415
  if (target.user_type === "mind") {
2304
- return c.json({ error: "Use the mind deletion API to delete minds" }, 400);
2416
+ if (await findMind(target.username)) {
2417
+ return c.json({ error: "Use the mind deletion API to delete minds" }, 400);
2418
+ }
2419
+ await deleteExternalMindUser(id);
2420
+ return c.json({ ok: true });
2305
2421
  }
2306
2422
  await deleteUser(id);
2307
2423
  return c.json({ ok: true });
@@ -2557,15 +2673,18 @@ async function fanOutToMinds(opts) {
2557
2673
  const isDM = opts.isDM ?? participants.length === 2;
2558
2674
  const manager = getMindManager();
2559
2675
  const sm = getSleepManagerIfReady();
2676
+ const registeredMinds = new Set((await readAllMinds()).map((m) => m.name));
2560
2677
  const targetMinds = mindParticipants.map((ap) => {
2561
2678
  const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
2562
2679
  if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
2563
2680
  if (ap.username !== opts.senderName) {
2564
- logger_default.warn(
2565
- `fan-out: skipping ${ap.username} (not running) for conversation ${opts.conversationId}`
2566
- );
2681
+ if (registeredMinds.has(ap.username)) {
2682
+ logger_default.warn(
2683
+ `fan-out: skipping ${ap.username} (not running) for conversation ${opts.conversationId}`
2684
+ );
2685
+ }
2567
2686
  if (ap.userType === "system") {
2568
- import("./spirit-availability-YFEGJNJ2.js").then(({ ensureSpiritAvailable: ensureSpiritAvailable2 }) => ensureSpiritAvailable2()).catch((err) => logger_default.warn("fan-out: on-demand spirit start failed", logger_default.errorData(err)));
2687
+ import("./spirit-availability-Y54Y3675.js").then(({ ensureSpiritAvailable: ensureSpiritAvailable2 }) => ensureSpiritAvailable2()).catch((err) => logger_default.warn("fan-out: on-demand spirit start failed", logger_default.errorData(err)));
2569
2688
  }
2570
2689
  }
2571
2690
  return null;
@@ -2668,7 +2787,7 @@ var app4 = new Hono5().post("/:platform/inbound", zValidator4("json", inboundSch
2668
2787
  }
2669
2788
  const participants = await getParticipants(channel.id);
2670
2789
  if (!participants.some((p) => p.userId === puppet.id)) {
2671
- const { addParticipant } = await import("./conversations-S2232Y5I.js");
2790
+ const { addParticipant } = await import("./conversations-ZL6PHROD.js");
2672
2791
  await addParticipant(channel.id, puppet.id);
2673
2792
  }
2674
2793
  const contentBlocks = body.content;
@@ -2952,7 +3071,7 @@ async function notifyMind(mindName, message) {
2952
3071
  const entry = await findMind(mindName);
2953
3072
  if (!entry) return;
2954
3073
  try {
2955
- const { deliverEvent: deliverEvent2 } = await import("./system-events-A6IGCEC3.js");
3074
+ const { deliverEvent: deliverEvent2 } = await import("./system-events-V43HDA3T.js");
2956
3075
  await deliverEvent2(mindName, { type: "file-share", body: message });
2957
3076
  } catch (err) {
2958
3077
  logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
@@ -3923,9 +4042,9 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
3923
4042
  stream.onAbort(() => {
3924
4043
  tail.kill();
3925
4044
  });
3926
- await new Promise((resolve19) => {
3927
- tail.on("exit", resolve19);
3928
- stream.onAbort(resolve19);
4045
+ await new Promise((resolve21) => {
4046
+ tail.on("exit", resolve21);
4047
+ stream.onAbort(resolve21);
3929
4048
  });
3930
4049
  });
3931
4050
  }).get("/:name/logs/tail", requireSelf(), async (c) => {
@@ -3943,8 +4062,8 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
3943
4062
  tail.stdout.on("data", (data) => {
3944
4063
  output += data.toString();
3945
4064
  });
3946
- await new Promise((resolve19) => {
3947
- tail.on("exit", resolve19);
4065
+ await new Promise((resolve21) => {
4066
+ tail.on("exit", resolve21);
3948
4067
  });
3949
4068
  return c.text(output);
3950
4069
  });
@@ -4037,14 +4156,14 @@ var mind_skills_default = app12;
4037
4156
  // packages/daemon/src/web/api/minds.ts
4038
4157
  import {
4039
4158
  cpSync,
4040
- existsSync as existsSync7,
4159
+ existsSync as existsSync8,
4041
4160
  mkdirSync as mkdirSync6,
4042
4161
  readdirSync as readdirSync2,
4043
4162
  readFileSync as readFileSync9,
4044
4163
  rmSync as rmSync3,
4045
4164
  writeFileSync as writeFileSync8
4046
4165
  } from "fs";
4047
- import { relative, resolve as resolve11 } from "path";
4166
+ import { relative, resolve as resolve13 } from "path";
4048
4167
  import { zValidator as zValidator6 } from "@hono/zod-validator";
4049
4168
  import { and as and5, desc as desc3, eq as eq5, sql as sql3 } from "drizzle-orm";
4050
4169
  import { Hono as Hono16 } from "hono";
@@ -4487,6 +4606,49 @@ async function commitSrcChanges(dir, mindName) {
4487
4606
  glog.info(`committed known-good src changes (${dir})`);
4488
4607
  }
4489
4608
 
4609
+ // packages/daemon/src/lib/mind/npm-install.ts
4610
+ import { existsSync as existsSync7 } from "fs";
4611
+ import { resolve as resolve10 } from "path";
4612
+ var NPM_INSTALL_ARGS = [
4613
+ "install",
4614
+ "--no-audit",
4615
+ "--no-fund",
4616
+ "--prefer-offline",
4617
+ "--loglevel=error"
4618
+ ];
4619
+ function lowPriorityArgv(cmd, args, ionice) {
4620
+ return ionice ? ["ionice", ["-c2", "-n7", "nice", "-n19", cmd, ...args]] : ["nice", ["-n19", cmd, ...args]];
4621
+ }
4622
+ function hasIonice() {
4623
+ return process.platform === "linux" && (existsSync7("/usr/bin/ionice") || existsSync7("/bin/ionice"));
4624
+ }
4625
+ async function npmInstallAsMind(cwd, mindName) {
4626
+ const [cmd, args] = lowPriorityArgv("npm", NPM_INSTALL_ARGS, hasIonice());
4627
+ if (isIsolationEnabled()) {
4628
+ const [wrappedCmd, wrappedArgs] = await wrapForIsolation(cmd, args, mindName);
4629
+ await exec(wrappedCmd, wrappedArgs, {
4630
+ cwd,
4631
+ env: { ...process.env, HOME: resolve10(cwd, "home") }
4632
+ });
4633
+ } else {
4634
+ await exec(cmd, args, { cwd });
4635
+ }
4636
+ }
4637
+ async function depsChangedSince(dir, fromRef) {
4638
+ try {
4639
+ await gitExec(["diff", "--quiet", fromRef, "HEAD", "--", "package.json", "package-lock.json"], {
4640
+ cwd: dir
4641
+ });
4642
+ return false;
4643
+ } catch {
4644
+ return true;
4645
+ }
4646
+ }
4647
+ async function npmInstallNeeded(dir, preMergeRef) {
4648
+ if (!existsSync7(resolve10(dir, "node_modules"))) return true;
4649
+ return depsChangedSince(dir, preMergeRef);
4650
+ }
4651
+
4490
4652
  // packages/daemon/src/lib/mind/thinking-config.ts
4491
4653
  var CLAUDE_EFFORT = {
4492
4654
  minimal: "low",
@@ -4529,6 +4691,8 @@ function deriveThinkingLevel(config2) {
4529
4691
  }
4530
4692
 
4531
4693
  // packages/daemon/src/lib/mind/variants.ts
4694
+ import { statSync as statSync3 } from "fs";
4695
+ import { resolve as resolve11 } from "path";
4532
4696
  var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
4533
4697
  function validateBranchName(branch) {
4534
4698
  if (!SAFE_BRANCH_RE.test(branch)) {
@@ -4541,6 +4705,55 @@ function validateBranchName(branch) {
4541
4705
  }
4542
4706
  var MERGE_EXCLUDED_PATHS = ["home/MEMORY.md", "home/memory/journal/"];
4543
4707
  var MAX_DELTA_CHARS = 12e3;
4708
+ var NEVER_REPORT_PREFIXES = ["home/.claude/", "home/.pi/", "home/.agents/"];
4709
+ var MAX_LISTED_UNRESOLVED_FILES = 20;
4710
+ async function findUnresolvedHomeFiles(cwd) {
4711
+ const raw = await gitExec(
4712
+ ["ls-files", "--others", "--ignored", "--exclude-standard", "-z", "--", "home"],
4713
+ { cwd }
4714
+ );
4715
+ const allPaths = raw.split("\0").filter((path) => path && !NEVER_REPORT_PREFIXES.some((prefix) => path.startsWith(prefix)));
4716
+ const files = [];
4717
+ let totalBytes = 0;
4718
+ for (const path of allPaths) {
4719
+ let bytes = -1;
4720
+ try {
4721
+ bytes = statSync3(resolve11(cwd, path)).size;
4722
+ } catch {
4723
+ }
4724
+ if (bytes > 0) totalBytes += bytes;
4725
+ if (files.length < MAX_LISTED_UNRESOLVED_FILES) files.push({ path, bytes });
4726
+ }
4727
+ return { files, totalCount: allPaths.length, totalBytes };
4728
+ }
4729
+ function formatBytes(bytes) {
4730
+ if (bytes < 0) return "size unknown";
4731
+ if (bytes < 1024) return `${bytes} B`;
4732
+ const units = ["KB", "MB", "GB", "TB"];
4733
+ let value = bytes / 1024;
4734
+ for (const unit of units) {
4735
+ if (value < 1024) return `${value.toFixed(1)} ${unit}`;
4736
+ value /= 1024;
4737
+ }
4738
+ return `${value.toFixed(1)} PB`;
4739
+ }
4740
+ function formatUnresolvedHomeFilesMessage(variantName, result) {
4741
+ const lines = result.files.map(
4742
+ (f) => ` - ${f.path.replace(/^home\//, "")} (${formatBytes(f.bytes)})`
4743
+ );
4744
+ const remaining = result.totalCount - result.files.length;
4745
+ if (remaining > 0) lines.push(` ...and ${remaining} more`);
4746
+ return `Cannot join variant "${variantName}": it has ${result.totalCount} untracked file${result.totalCount === 1 ? "" : "s"} under home/ that .gitignore is hiding from git \u2014 never committed, and joining would destroy them when the variant is cleaned up (total: ${formatBytes(result.totalBytes)}):
4747
+
4748
+ ${lines.join("\n")}
4749
+
4750
+ Before retrying the join, resolve each file in the variant \u2014 one of:
4751
+ 1. Track it: git add -f -- <path> (then commit it, or let auto-commit pick it up next turn)
4752
+ 2. Copy it out of home/ to somewhere safe, then delete it
4753
+ 3. Delete it if it's disposable
4754
+
4755
+ Once resolved, retry the join. To join anyway and discard everything listed above, pass discardUnresolved: true.`;
4756
+ }
4544
4757
  async function mergeVariantExcludingMemory(projectRoot, variantBranch) {
4545
4758
  const opts = { cwd: projectRoot };
4546
4759
  let memoryDelta = "";
@@ -4743,7 +4956,7 @@ async function waitForImagegenJob(mind, id, timeoutMs) {
4743
4956
  import { randomUUID } from "crypto";
4744
4957
  import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
4745
4958
  import { homedir } from "os";
4746
- import { resolve as resolve10 } from "path";
4959
+ import { resolve as resolve12 } from "path";
4747
4960
  function convertSession(opts) {
4748
4961
  const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
4749
4962
  const sessionId = randomUUID();
@@ -4859,9 +5072,9 @@ function convertSession(opts) {
4859
5072
  }
4860
5073
  }
4861
5074
  const projectId = opts.projectDir.replace(/\//g, "-");
4862
- const sdkDir = resolve10(homedir(), ".claude", "projects", projectId);
5075
+ const sdkDir = resolve12(homedir(), ".claude", "projects", projectId);
4863
5076
  mkdirSync5(sdkDir, { recursive: true });
4864
- const sdkPath = resolve10(sdkDir, `${sessionId}.jsonl`);
5077
+ const sdkPath = resolve12(sdkDir, `${sessionId}.jsonl`);
4865
5078
  writeFileSync7(sdkPath, `${sdkEvents.join("\n")}
4866
5079
  `);
4867
5080
  console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
@@ -4928,7 +5141,7 @@ async function getMindStatus(name, port, registryRunning, seed) {
4928
5141
  let status = "stopped";
4929
5142
  let wakeAt = null;
4930
5143
  try {
4931
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
5144
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
4932
5145
  const sleepManager = getSleepManagerIfReady2();
4933
5146
  if (sleepManager?.isSleeping(name)) {
4934
5147
  status = "sleeping";
@@ -5007,7 +5220,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
5007
5220
  await gitExec(["commit", "-m", "initial commit"], opts);
5008
5221
  }
5009
5222
  async function updateTemplateBranch(projectRoot, template, mindName) {
5010
- const tempWorktree = resolve11(projectRoot, ".variants", "_template_update");
5223
+ const tempWorktree = resolve13(projectRoot, ".variants", "_template_update");
5011
5224
  let branchExists = false;
5012
5225
  try {
5013
5226
  await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
@@ -5018,7 +5231,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
5018
5231
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
5019
5232
  } catch {
5020
5233
  }
5021
- if (existsSync7(tempWorktree)) {
5234
+ if (existsSync8(tempWorktree)) {
5022
5235
  rmSync3(tempWorktree, { recursive: true, force: true });
5023
5236
  }
5024
5237
  const templatesRoot = findTemplatesRoot();
@@ -5039,15 +5252,15 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
5039
5252
  });
5040
5253
  }
5041
5254
  copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
5042
- const initDir = resolve11(tempWorktree, ".init");
5043
- if (existsSync7(initDir)) {
5255
+ const initDir = resolve13(tempWorktree, ".init");
5256
+ if (existsSync8(initDir)) {
5044
5257
  rmSync3(initDir, { recursive: true, force: true });
5045
5258
  }
5046
- const homeDir = resolve11(tempWorktree, "home");
5047
- if (existsSync7(homeDir)) {
5259
+ const homeDir = resolve13(tempWorktree, "home");
5260
+ if (existsSync8(homeDir)) {
5048
5261
  for (const entry of readdirSync2(homeDir)) {
5049
5262
  if (entry !== "VOLUTE.md") {
5050
- rmSync3(resolve11(homeDir, entry), { recursive: true, force: true });
5263
+ rmSync3(resolve13(homeDir, entry), { recursive: true, force: true });
5051
5264
  }
5052
5265
  }
5053
5266
  }
@@ -5062,7 +5275,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
5062
5275
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
5063
5276
  } catch {
5064
5277
  }
5065
- if (existsSync7(tempWorktree)) {
5278
+ if (existsSync8(tempWorktree)) {
5066
5279
  rmSync3(tempWorktree, { recursive: true, force: true });
5067
5280
  }
5068
5281
  rmSync3(composedDir, { recursive: true, force: true });
@@ -5085,14 +5298,6 @@ async function mergeTemplateBranch(worktreeDir) {
5085
5298
  throw e;
5086
5299
  }
5087
5300
  }
5088
- async function npmInstallAsMind(cwd, mindName) {
5089
- if (isIsolationEnabled()) {
5090
- const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
5091
- await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve11(cwd, "home") } });
5092
- } else {
5093
- await exec("npm", ["install"], { cwd });
5094
- }
5095
- }
5096
5301
  async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariantName, upgradeBranch, template, oldTemplate) {
5097
5302
  const templateChanged = template !== oldTemplate;
5098
5303
  const mainStatus = (await gitExec(["status", "--porcelain"], { cwd: dir })).trim();
@@ -5100,9 +5305,10 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
5100
5305
  await gitExec(["add", "-A"], { cwd: dir });
5101
5306
  await gitExec(["commit", "-m", "Auto-commit before upgrade merge"], { cwd: dir });
5102
5307
  }
5308
+ const preMergeHead = (await gitExec(["rev-parse", "HEAD"], { cwd: dir })).trim();
5103
5309
  await gitExec(["merge", upgradeBranch], { cwd: dir });
5104
5310
  try {
5105
- await cleanupVariant(upgradeVariantName, dir, worktreeDir);
5311
+ await cleanupVariant(upgradeVariantName, mindName, dir, worktreeDir);
5106
5312
  } catch (err) {
5107
5313
  logger_default.warn(`failed to clean up upgrade worktree for ${mindName}`, logger_default.errorData(err));
5108
5314
  }
@@ -5113,7 +5319,7 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
5113
5319
  let switchWarning;
5114
5320
  if (templateChanged) {
5115
5321
  try {
5116
- applyTemplateHomeFiles(resolve11(dir, "home"), template);
5322
+ applyTemplateHomeFiles(resolve13(dir, "home"), template);
5117
5323
  const migratedSkills = migrateSkillsToTemplate(dir, oldTemplate, template);
5118
5324
  await gitExec(["add", "home/"], { cwd: dir });
5119
5325
  try {
@@ -5140,14 +5346,18 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
5140
5346
  } catch (err) {
5141
5347
  logger_default.warn(`failed to update template for ${mindName}`, logger_default.errorData(err));
5142
5348
  }
5143
- try {
5144
- await npmInstallAsMind(dir, mindName);
5145
- } catch (err) {
5146
- logger_default.warn(`npm install failed after upgrade merge for ${mindName}`, logger_default.errorData(err));
5147
- return {
5148
- ok: true,
5149
- warning: `Upgrade merged but npm install failed: ${err instanceof Error ? err.message : String(err)}. You may need to run npm install manually.`
5150
- };
5349
+ if (await npmInstallNeeded(dir, preMergeHead)) {
5350
+ try {
5351
+ await npmInstallAsMind(dir, mindName);
5352
+ } catch (err) {
5353
+ logger_default.warn(`npm install failed after upgrade merge for ${mindName}`, logger_default.errorData(err));
5354
+ return {
5355
+ ok: true,
5356
+ warning: `Upgrade merged but npm install failed: ${err instanceof Error ? err.message : String(err)}. You may need to run npm install manually.`
5357
+ };
5358
+ }
5359
+ } else {
5360
+ logger_default.info(`skipping npm install for ${mindName} \u2014 dependencies unchanged by upgrade`);
5151
5361
  }
5152
5362
  const manager = getMindManager();
5153
5363
  try {
@@ -5155,7 +5365,7 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
5155
5365
  await manager.stopMind(mindName);
5156
5366
  }
5157
5367
  manager.setPendingContext(mindName, { type: "upgraded" });
5158
- await manager.startMind(mindName);
5368
+ await manager.startMind(mindName, { healthTimeoutMs: 12e4 });
5159
5369
  } catch (e) {
5160
5370
  return {
5161
5371
  ok: true,
@@ -5165,8 +5375,8 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
5165
5375
  return { ok: true, warning: switchWarning };
5166
5376
  }
5167
5377
  async function importFromArchive(c, tempDir, nameOverride, manifest) {
5168
- const extractedMindDir = resolve11(tempDir, "mind");
5169
- if (!existsSync7(extractedMindDir)) {
5378
+ const extractedMindDir = resolve13(tempDir, "mind");
5379
+ if (!existsSync8(extractedMindDir)) {
5170
5380
  return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
5171
5381
  }
5172
5382
  if (!manifest?.includes || !manifest.name || !manifest.template) {
@@ -5184,7 +5394,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
5184
5394
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
5185
5395
  ensureVoluteHome();
5186
5396
  const dest = mindDir(name);
5187
- if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5397
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5188
5398
  try {
5189
5399
  cpSync(extractedMindDir, dest, { recursive: true });
5190
5400
  if (!manifest.includes.identity) {
@@ -5192,9 +5402,9 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
5192
5402
  }
5193
5403
  const state = stateDir(name);
5194
5404
  mkdirSync6(state, { recursive: true });
5195
- const envJson = resolve11(tempDir, "state/env.json");
5196
- if (existsSync7(envJson)) {
5197
- cpSync(envJson, resolve11(state, "env.json"));
5405
+ const envJson = resolve13(tempDir, "state/env.json");
5406
+ if (existsSync8(envJson)) {
5407
+ cpSync(envJson, resolve13(state, "env.json"));
5198
5408
  }
5199
5409
  const port = await nextPort();
5200
5410
  await addMind(name, port, manifest.stage, manifest.template);
@@ -5203,30 +5413,30 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
5203
5413
  } catch (err) {
5204
5414
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
5205
5415
  }
5206
- const homeDir = resolve11(dest, "home");
5416
+ const homeDir = resolve13(dest, "home");
5207
5417
  ensureVoluteGroup();
5208
5418
  createMindUser(name, homeDir);
5209
5419
  await chownMindDir(dest, name);
5210
5420
  await npmInstallAsMind(dest, name);
5211
5421
  await importHistoryFromArchive(name, tempDir);
5212
5422
  importSessionsFromArchive(dest, tempDir);
5213
- if (!existsSync7(resolve11(dest, ".git"))) {
5423
+ if (!existsSync8(resolve13(dest, ".git"))) {
5214
5424
  try {
5215
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
5425
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dest, "home") } : void 0;
5216
5426
  await gitExec(["init"], { cwd: dest, mindName: name, env });
5217
5427
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
5218
5428
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
5219
5429
  await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
5220
5430
  } catch (err) {
5221
5431
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
5222
- rmSync3(resolve11(dest, ".git"), { recursive: true, force: true });
5432
+ rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
5223
5433
  }
5224
5434
  }
5225
5435
  await chownMindDir(dest, name);
5226
5436
  rmSync3(tempDir, { recursive: true, force: true });
5227
5437
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
5228
5438
  } catch (err) {
5229
- if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
5439
+ if (existsSync8(dest)) rmSync3(dest, { recursive: true, force: true });
5230
5440
  try {
5231
5441
  await removeMind(name);
5232
5442
  } catch (cleanupErr) {
@@ -5243,7 +5453,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5243
5453
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
5244
5454
  ensureVoluteHome();
5245
5455
  const dest = mindDir(name);
5246
- if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5456
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5247
5457
  const templatesRoot = findTemplatesRoot();
5248
5458
  const { composedDir, manifest: templateManifest } = composeTemplate(
5249
5459
  templatesRoot,
@@ -5252,36 +5462,36 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5252
5462
  try {
5253
5463
  copyTemplateToDir(composedDir, dest, name, templateManifest);
5254
5464
  applyInitFiles(dest);
5255
- const extractedHome = resolve11(extractedMindDir, "home");
5256
- if (existsSync7(extractedHome)) {
5257
- cpSync(extractedHome, resolve11(dest, "home"), { recursive: true });
5465
+ const extractedHome = resolve13(extractedMindDir, "home");
5466
+ if (existsSync8(extractedHome)) {
5467
+ cpSync(extractedHome, resolve13(dest, "home"), { recursive: true });
5258
5468
  }
5259
- const extractedMindInternal = resolve11(extractedMindDir, ".mind");
5260
- if (existsSync7(extractedMindInternal)) {
5261
- cpSync(extractedMindInternal, resolve11(dest, ".mind"), { recursive: true });
5469
+ const extractedMindInternal = resolve13(extractedMindDir, ".mind");
5470
+ if (existsSync8(extractedMindInternal)) {
5471
+ cpSync(extractedMindInternal, resolve13(dest, ".mind"), { recursive: true });
5262
5472
  }
5263
- const identityDir = resolve11(dest, ".mind/identity");
5473
+ const identityDir = resolve13(dest, ".mind/identity");
5264
5474
  let publicKeyPem;
5265
- if (!manifest.includes.identity || !existsSync7(resolve11(identityDir, "private.pem"))) {
5475
+ if (!manifest.includes.identity || !existsSync8(resolve13(identityDir, "private.pem"))) {
5266
5476
  ({ publicKeyPem } = generateIdentity(dest));
5267
5477
  } else {
5268
- publicKeyPem = readFileSync9(resolve11(identityDir, "public.pem"), "utf-8");
5478
+ publicKeyPem = readFileSync9(resolve13(identityDir, "public.pem"), "utf-8");
5269
5479
  }
5270
- const promptsPath = resolve11(dest, "home/.config/prompts.json");
5271
- if (!existsSync7(promptsPath)) {
5480
+ const promptsPath = resolve13(dest, "home/.config/prompts.json");
5481
+ if (!existsSync8(promptsPath)) {
5272
5482
  const mindPrompts = await getMindPromptDefaults();
5273
5483
  writeFileSync8(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
5274
5484
  `);
5275
5485
  }
5276
5486
  const state = stateDir(name);
5277
5487
  mkdirSync6(state, { recursive: true });
5278
- const envJson = resolve11(tempDir, "state/env.json");
5279
- if (existsSync7(envJson)) {
5280
- cpSync(envJson, resolve11(state, "env.json"));
5488
+ const envJson = resolve13(tempDir, "state/env.json");
5489
+ if (existsSync8(envJson)) {
5490
+ cpSync(envJson, resolve13(state, "env.json"));
5281
5491
  }
5282
5492
  const port = await nextPort();
5283
5493
  await addMind(name, port, manifest.stage, manifest.template);
5284
- const homeDir = resolve11(dest, "home");
5494
+ const homeDir = resolve13(dest, "home");
5285
5495
  ensureVoluteGroup();
5286
5496
  createMindUser(name, homeDir);
5287
5497
  await chownMindDir(dest, name);
@@ -5294,7 +5504,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5294
5504
  await initTemplateBranch(dest, composedDir, templateManifest, name, env);
5295
5505
  } catch (err) {
5296
5506
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
5297
- rmSync3(resolve11(dest, ".git"), { recursive: true, force: true });
5507
+ rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
5298
5508
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
5299
5509
  }
5300
5510
  const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
@@ -5324,7 +5534,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5324
5534
  ...skillWarnings.length > 0 && { skillWarnings }
5325
5535
  });
5326
5536
  } catch (err) {
5327
- if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
5537
+ if (existsSync8(dest)) rmSync3(dest, { recursive: true, force: true });
5328
5538
  try {
5329
5539
  await removeMind(name);
5330
5540
  } catch (cleanupErr) {
@@ -5337,8 +5547,8 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5337
5547
  }
5338
5548
  }
5339
5549
  async function importHistoryFromArchive(name, tempDir) {
5340
- const historyJsonl = resolve11(tempDir, "history.jsonl");
5341
- if (!existsSync7(historyJsonl)) return;
5550
+ const historyJsonl = resolve13(tempDir, "history.jsonl");
5551
+ if (!existsSync8(historyJsonl)) return;
5342
5552
  try {
5343
5553
  const db = await getDb();
5344
5554
  const lines = readFileSync9(historyJsonl, "utf-8").trim().split("\n");
@@ -5378,13 +5588,13 @@ async function importHistoryFromArchive(name, tempDir) {
5378
5588
  }
5379
5589
  }
5380
5590
  function importSessionsFromArchive(dest, tempDir) {
5381
- const sessionsDir = resolve11(tempDir, "sessions");
5382
- if (!existsSync7(sessionsDir)) return;
5591
+ const sessionsDir = resolve13(tempDir, "sessions");
5592
+ if (!existsSync8(sessionsDir)) return;
5383
5593
  try {
5384
- const destSessions = resolve11(dest, ".mind/sessions");
5594
+ const destSessions = resolve13(dest, ".mind/sessions");
5385
5595
  mkdirSync6(destSessions, { recursive: true });
5386
5596
  for (const file of readdirSync2(sessionsDir)) {
5387
- cpSync(resolve11(sessionsDir, file), resolve11(destSessions, file));
5597
+ cpSync(resolve13(sessionsDir, file), resolve13(destSessions, file));
5388
5598
  }
5389
5599
  } catch (err) {
5390
5600
  logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
@@ -5458,13 +5668,13 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5458
5668
  );
5459
5669
  }
5460
5670
  {
5461
- const { mindLimitError, readGlobalConfig: readGlobalConfig2 } = await import("./setup-CAJFOR2N.js");
5671
+ const { mindLimitError, readGlobalConfig: readGlobalConfig2 } = await import("./setup-WZSXHHRJ.js");
5462
5672
  const limitError = mindLimitError(await countCappedMinds(), readGlobalConfig2().maxMinds);
5463
5673
  if (limitError) return c.json({ error: limitError }, 409);
5464
5674
  }
5465
5675
  ensureVoluteHome();
5466
5676
  const dest = mindDir(name);
5467
- if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5677
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5468
5678
  const templatesRoot = findTemplatesRoot();
5469
5679
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
5470
5680
  try {
@@ -5473,7 +5683,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5473
5683
  const { publicKeyPem } = generateIdentity(dest);
5474
5684
  let effectiveModel = body.model;
5475
5685
  {
5476
- const { readGlobalConfig: readGlobal } = await import("./setup-CAJFOR2N.js");
5686
+ const { readGlobalConfig: readGlobal } = await import("./setup-WZSXHHRJ.js");
5477
5687
  const mindDefaults = readGlobal().mindDefaults;
5478
5688
  const config2 = readVoluteConfig(dest);
5479
5689
  if (!config2) throw new Error("Failed to read volute.json after identity generation");
@@ -5501,9 +5711,9 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5501
5711
  writeVoluteConfig(dest, config2);
5502
5712
  const modelId = body.model ?? cog?.model;
5503
5713
  effectiveModel = modelId ? qualifyModelId(modelId) : void 0;
5504
- const sdkConfigPath = resolve11(dest, "home/.config/config.json");
5714
+ const sdkConfigPath = resolve13(dest, "home/.config/config.json");
5505
5715
  if (modelId || cog?.compaction) {
5506
- const existing = existsSync7(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
5716
+ const existing = existsSync8(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
5507
5717
  if (modelId) {
5508
5718
  existing.model = template === "pi" ? qualifyModelId(modelId) : unqualifyModelId(modelId);
5509
5719
  }
@@ -5516,7 +5726,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5516
5726
  }
5517
5727
  const mindPrompts = await getMindPromptDefaults();
5518
5728
  writeFileSync8(
5519
- resolve11(dest, "home/.config/prompts.json"),
5729
+ resolve13(dest, "home/.config/prompts.json"),
5520
5730
  `${JSON.stringify(mindPrompts, null, 2)}
5521
5731
  `
5522
5732
  );
@@ -5528,7 +5738,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5528
5738
  } catch (err) {
5529
5739
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
5530
5740
  }
5531
- const homeDir = resolve11(dest, "home");
5741
+ const homeDir = resolve13(dest, "home");
5532
5742
  ensureVoluteGroup();
5533
5743
  createMindUser(name, homeDir);
5534
5744
  await chownMindDir(dest, name);
@@ -5541,7 +5751,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5541
5751
  await initTemplateBranch(dest, composedDir, manifest, name, env);
5542
5752
  } catch (err) {
5543
5753
  logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
5544
- rmSync3(resolve11(dest, ".git"), { recursive: true, force: true });
5754
+ rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
5545
5755
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
5546
5756
  }
5547
5757
  if (body.stage === "seed") {
@@ -5550,11 +5760,11 @@ Your creator described you as: "${body.description}"
5550
5760
  ` : "";
5551
5761
  const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
5552
5762
  const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
5553
- writeFileSync8(resolve11(dest, "home/SOUL.md"), seedSoul);
5763
+ writeFileSync8(resolve13(dest, "home/SOUL.md"), seedSoul);
5554
5764
  }
5555
5765
  let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
5556
5766
  if (body.stage === "seed" && !body.skills) {
5557
- const { isImagegenEnabled } = await import("./setup-CAJFOR2N.js");
5767
+ const { isImagegenEnabled } = await import("./setup-WZSXHHRJ.js");
5558
5768
  if (isImagegenEnabled()) {
5559
5769
  skillSet = [...skillSet, "imagegen"];
5560
5770
  }
@@ -5576,7 +5786,7 @@ Your creator described you as: "${body.description}"
5576
5786
  const spiritName = getSpiritName();
5577
5787
  const spiritEntry = await findMind(spiritName);
5578
5788
  if (spiritEntry) {
5579
- const { spiritDir } = await import("./spirit-IL5EMWYZ.js");
5789
+ const { spiritDir } = await import("./spirit-4ECYIVGE.js");
5580
5790
  const sDir = spiritEntry.dir ?? spiritDir();
5581
5791
  const spiritConfig = readVoluteConfig(sDir) ?? {};
5582
5792
  const schedules = spiritConfig.schedules ?? [];
@@ -5591,7 +5801,7 @@ Your creator described you as: "${body.description}"
5591
5801
  });
5592
5802
  spiritConfig.schedules = schedules;
5593
5803
  writeVoluteConfig(sDir, spiritConfig);
5594
- const { getScheduler: getScheduler2 } = await import("./scheduler-UV6RUJ7P.js");
5804
+ const { getScheduler: getScheduler2 } = await import("./scheduler-JTTWZDYU.js");
5595
5805
  getScheduler2().loadSchedules(spiritName, sDir);
5596
5806
  }
5597
5807
  }
@@ -5602,11 +5812,11 @@ Your creator described you as: "${body.description}"
5602
5812
  if (body.stage !== "seed") {
5603
5813
  const customSoul = await getPromptIfCustom("default_soul");
5604
5814
  if (customSoul) {
5605
- writeFileSync8(resolve11(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
5815
+ writeFileSync8(resolve13(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
5606
5816
  }
5607
5817
  const customMemory = await getPromptIfCustom("default_memory");
5608
5818
  if (customMemory) {
5609
- writeFileSync8(resolve11(dest, "home/MEMORY.md"), customMemory);
5819
+ writeFileSync8(resolve13(dest, "home/MEMORY.md"), customMemory);
5610
5820
  }
5611
5821
  }
5612
5822
  await chownMindDir(dest, name);
@@ -5624,8 +5834,9 @@ Your creator described you as: "${body.description}"
5624
5834
  description: body.description
5625
5835
  }
5626
5836
  });
5627
- announceToSystem(`${name} has joined`).catch(() => {
5628
- });
5837
+ announceToSystem(`${name} has joined`).catch(
5838
+ (err) => logger_default.warn(`failed to announce ${name} joining #system`, logger_default.errorData(err))
5839
+ );
5629
5840
  const credentialWarning = await missingCredentialWarning(
5630
5841
  template,
5631
5842
  effectiveModel,
@@ -5645,10 +5856,11 @@ Your creator described you as: "${body.description}"
5645
5856
  ...skillWarnings.length > 0 && { skillWarnings }
5646
5857
  });
5647
5858
  } catch (err) {
5648
- if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
5859
+ if (existsSync8(dest)) rmSync3(dest, { recursive: true, force: true });
5649
5860
  try {
5650
5861
  await removeMind(name);
5651
- } catch {
5862
+ } catch (cleanupErr) {
5863
+ logger_default.warn(`failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
5652
5864
  }
5653
5865
  return c.json({ error: err instanceof Error ? err.message : "Failed to create mind" }, 500);
5654
5866
  } finally {
@@ -5665,13 +5877,13 @@ Your creator described you as: "${body.description}"
5665
5877
  return importFromArchive(c, body.archivePath, body.name, body.manifest);
5666
5878
  }
5667
5879
  const wsDir = body.workspacePath;
5668
- if (!wsDir || !existsSync7(resolve11(wsDir, "SOUL.md")) || !existsSync7(resolve11(wsDir, "IDENTITY.md"))) {
5880
+ if (!wsDir || !existsSync8(resolve13(wsDir, "SOUL.md")) || !existsSync8(resolve13(wsDir, "IDENTITY.md"))) {
5669
5881
  return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
5670
5882
  }
5671
- const soul = readFileSync9(resolve11(wsDir, "SOUL.md"), "utf-8");
5672
- const identity = readFileSync9(resolve11(wsDir, "IDENTITY.md"), "utf-8");
5673
- const userPath = resolve11(wsDir, "USER.md");
5674
- const user = existsSync7(userPath) ? readFileSync9(userPath, "utf-8") : "";
5883
+ const soul = readFileSync9(resolve13(wsDir, "SOUL.md"), "utf-8");
5884
+ const identity = readFileSync9(resolve13(wsDir, "IDENTITY.md"), "utf-8");
5885
+ const userPath = resolve13(wsDir, "USER.md");
5886
+ const user = existsSync8(userPath) ? readFileSync9(userPath, "utf-8") : "";
5675
5887
  const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
5676
5888
  const template = body.template ?? "claude";
5677
5889
  const nameErr = validateMindName(name);
@@ -5691,33 +5903,33 @@ ${user.trimEnd()}
5691
5903
  ` : "";
5692
5904
  ensureVoluteHome();
5693
5905
  const dest = mindDir(name);
5694
- if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5906
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5695
5907
  const templatesRoot = findTemplatesRoot();
5696
5908
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
5697
5909
  try {
5698
5910
  copyTemplateToDir(composedDir, dest, name, manifest);
5699
5911
  applyInitFiles(dest);
5700
5912
  const { publicKeyPem: importPublicKey } = generateIdentity(dest);
5701
- writeFileSync8(resolve11(dest, "home/SOUL.md"), mergedSoul);
5702
- const wsMemoryPath = resolve11(wsDir, "MEMORY.md");
5703
- const hasMemory = existsSync7(wsMemoryPath);
5913
+ writeFileSync8(resolve13(dest, "home/SOUL.md"), mergedSoul);
5914
+ const wsMemoryPath = resolve13(wsDir, "MEMORY.md");
5915
+ const hasMemory = existsSync8(wsMemoryPath);
5704
5916
  if (hasMemory) {
5705
5917
  const existingMemory = readFileSync9(wsMemoryPath, "utf-8");
5706
5918
  writeFileSync8(
5707
- resolve11(dest, "home/MEMORY.md"),
5919
+ resolve13(dest, "home/MEMORY.md"),
5708
5920
  `${existingMemory.trimEnd()}${mergedMemoryExtra}`
5709
5921
  );
5710
5922
  } else if (user) {
5711
- writeFileSync8(resolve11(dest, "home/MEMORY.md"), `${user.trimEnd()}
5923
+ writeFileSync8(resolve13(dest, "home/MEMORY.md"), `${user.trimEnd()}
5712
5924
  `);
5713
5925
  }
5714
- const wsMemoryDir = resolve11(wsDir, "memory");
5926
+ const wsMemoryDir = resolve13(wsDir, "memory");
5715
5927
  let dailyLogCount = 0;
5716
- if (existsSync7(wsMemoryDir)) {
5717
- const destMemoryDir = resolve11(dest, "home/memory");
5928
+ if (existsSync8(wsMemoryDir)) {
5929
+ const destMemoryDir = resolve13(dest, "home/memory");
5718
5930
  const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
5719
5931
  for (const file of files) {
5720
- cpSync(resolve11(wsMemoryDir, file), resolve11(destMemoryDir, file));
5932
+ cpSync(resolve13(wsMemoryDir, file), resolve13(destMemoryDir, file));
5721
5933
  }
5722
5934
  dailyLogCount = files.length;
5723
5935
  }
@@ -5728,7 +5940,7 @@ ${user.trimEnd()}
5728
5940
  } catch (err) {
5729
5941
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
5730
5942
  }
5731
- const homeDir = resolve11(dest, "home");
5943
+ const homeDir = resolve13(dest, "home");
5732
5944
  ensureVoluteGroup();
5733
5945
  createMindUser(name, homeDir);
5734
5946
  await chownMindDir(dest, name);
@@ -5736,20 +5948,20 @@ ${user.trimEnd()}
5736
5948
  if (!hasMemory && dailyLogCount > 0) {
5737
5949
  await consolidateMemory(dest);
5738
5950
  }
5739
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
5951
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dest, "home") } : void 0;
5740
5952
  await gitExec(["init"], { cwd: dest, mindName: name, env });
5741
5953
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
5742
5954
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
5743
5955
  await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
5744
- const sessionFile = body.sessionPath ? resolve11(body.sessionPath) : findOpenClawSession(wsDir);
5745
- if (sessionFile && existsSync7(sessionFile)) {
5956
+ const sessionFile = body.sessionPath ? resolve13(body.sessionPath) : findOpenClawSession(wsDir);
5957
+ if (sessionFile && existsSync8(sessionFile)) {
5746
5958
  if (template === "pi") {
5747
5959
  importPiSession(sessionFile, dest);
5748
5960
  } else if (template === "claude") {
5749
5961
  const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
5750
- const mindRuntimeDir = resolve11(dest, ".mind");
5962
+ const mindRuntimeDir = resolve13(dest, ".mind");
5751
5963
  mkdirSync6(mindRuntimeDir, { recursive: true });
5752
- writeFileSync8(resolve11(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
5964
+ writeFileSync8(resolve13(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
5753
5965
  }
5754
5966
  }
5755
5967
  importOpenClawConnectors(name, dest);
@@ -5759,10 +5971,11 @@ ${user.trimEnd()}
5759
5971
  );
5760
5972
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
5761
5973
  } catch (err) {
5762
- if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
5974
+ if (existsSync8(dest)) rmSync3(dest, { recursive: true, force: true });
5763
5975
  try {
5764
5976
  await removeMind(name);
5765
- } catch {
5977
+ } catch (cleanupErr) {
5978
+ logger_default.warn(`failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
5766
5979
  }
5767
5980
  return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
5768
5981
  } finally {
@@ -5794,7 +6007,7 @@ ${user.trimEnd()}
5794
6007
  stage: entry.stage,
5795
6008
  dir: entry.dir
5796
6009
  });
5797
- const hasPages = existsSync7(resolve11(mindDir(entry.name), "home", "pages"));
6010
+ const hasPages = existsSync8(resolve13(mindDir(entry.name), "home", "pages"));
5798
6011
  const lastActiveAt = lastActiveMap.get(entry.name) ?? null;
5799
6012
  if (!privileged) return toPublicMind(entry, mindStatus, { hasPages, lastActiveAt });
5800
6013
  return {
@@ -5812,12 +6025,12 @@ ${user.trimEnd()}
5812
6025
  const entry = await findMind(name);
5813
6026
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5814
6027
  const dir = entry.dir ?? mindDir(entry.parent ?? name);
5815
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
6028
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
5816
6029
  const mindStatus = await getMindStatus(name, entry.port, void 0, {
5817
6030
  stage: entry.stage,
5818
6031
  dir: entry.dir
5819
6032
  });
5820
- const hasPages = existsSync7(resolve11(mindDir(name), "home", "pages"));
6033
+ const hasPages = existsSync8(resolve13(mindDir(name), "home", "pages"));
5821
6034
  if (!isPrivileged(c)) {
5822
6035
  return c.json(toPublicMind(entry, mindStatus, { hasPages }));
5823
6036
  }
@@ -5857,7 +6070,7 @@ ${user.trimEnd()}
5857
6070
  const projectDir = entry.dir ?? mindDir(name);
5858
6071
  if (entry.parent) {
5859
6072
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
5860
- } else if (!existsSync7(projectDir)) {
6073
+ } else if (!existsSync8(projectDir)) {
5861
6074
  return c.json({ error: "Mind directory missing" }, 404);
5862
6075
  }
5863
6076
  if (getMindManager().isRunning(name)) {
@@ -5878,7 +6091,7 @@ ${user.trimEnd()}
5878
6091
  const projectDir = entry.dir ?? mindDir(name);
5879
6092
  if (entry.parent) {
5880
6093
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
5881
- } else if (!existsSync7(projectDir)) {
6094
+ } else if (!existsSync8(projectDir)) {
5882
6095
  return c.json({ error: "Mind directory missing" }, 404);
5883
6096
  }
5884
6097
  let context;
@@ -5894,7 +6107,7 @@ ${user.trimEnd()}
5894
6107
  const manager = getMindManager();
5895
6108
  try {
5896
6109
  if (context?.type === "reload") {
5897
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
6110
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
5898
6111
  const sleepState = getSleepManagerIfReady2()?.getState(name);
5899
6112
  if (sleepState?.sleeping) {
5900
6113
  logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
@@ -5914,7 +6127,8 @@ ${user.trimEnd()}
5914
6127
  const variantEntry = await findMind(mergeVariantName);
5915
6128
  if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
5916
6129
  const projectRoot = mindDir(baseName);
5917
- if (existsSync7(variantEntry.dir)) {
6130
+ const discardUnresolved = context.discardUnresolvedHomeFiles === true;
6131
+ if (existsSync8(variantEntry.dir)) {
5918
6132
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
5919
6133
  if (status) {
5920
6134
  try {
@@ -5941,13 +6155,44 @@ ${user.trimEnd()}
5941
6155
  logger_default.error(`failed to auto-commit main worktree for ${baseName}`, logger_default.errorData(e));
5942
6156
  }
5943
6157
  }
6158
+ const preMergeHead = (await gitExec(["rev-parse", "HEAD"], { cwd: projectRoot })).trim();
5944
6159
  const memoryDelta = await mergeVariantExcludingMemory(projectRoot, variantEntry.branch);
5945
- if (memoryDelta && context) context.memoryDelta = memoryDelta;
5946
- await cleanupVariant(mergeVariantName, projectRoot, variantEntry.dir);
5947
- try {
5948
- await npmInstallAsMind(projectRoot, baseName);
5949
- } catch (e) {
5950
- logger_default.error(`npm install failed after merge for ${baseName}`, logger_default.errorData(e));
6160
+ let unresolvedNotice;
6161
+ if (existsSync8(variantEntry.dir) && !discardUnresolved) {
6162
+ try {
6163
+ const unresolved = await findUnresolvedHomeFiles(variantEntry.dir);
6164
+ if (unresolved.totalCount > 0) {
6165
+ unresolvedNotice = formatUnresolvedHomeFilesMessage(mergeVariantName, unresolved);
6166
+ }
6167
+ } catch (err) {
6168
+ unresolvedNotice = `Could not verify variant ${mergeVariantName} has no unresolved home/ files: ${err instanceof Error ? err.message : String(err)}. Variant left intact \u2014 retry the join.`;
6169
+ }
6170
+ }
6171
+ if (unresolvedNotice) {
6172
+ logger_default.warn(
6173
+ `variant join blocked for ${baseName}: ${mergeVariantName} \u2014 ${unresolvedNotice}`
6174
+ );
6175
+ await recordNotice({
6176
+ mind: baseName,
6177
+ thread: "main",
6178
+ kind: "join_blocked",
6179
+ reason: "unresolved_variant_files",
6180
+ detail: unresolvedNotice
6181
+ });
6182
+ if (context) {
6183
+ context.type = "restart";
6184
+ context.name = void 0;
6185
+ }
6186
+ } else {
6187
+ if (memoryDelta && context) context.memoryDelta = memoryDelta;
6188
+ await cleanupVariant(mergeVariantName, baseName, projectRoot, variantEntry.dir);
6189
+ if (await npmInstallNeeded(projectRoot, preMergeHead)) {
6190
+ try {
6191
+ await npmInstallAsMind(projectRoot, baseName);
6192
+ } catch (e) {
6193
+ logger_default.error(`npm install failed after merge for ${baseName}`, logger_default.errorData(e));
6194
+ }
6195
+ }
5951
6196
  }
5952
6197
  }
5953
6198
  }
@@ -5989,6 +6234,7 @@ ${user.trimEnd()}
5989
6234
  );
5990
6235
  return c.json({ ok: true, port: targetPort });
5991
6236
  } catch (err) {
6237
+ logger_default.error(`failed to restart mind ${name}`, logger_default.errorData(err));
5992
6238
  return c.json({ error: err instanceof Error ? err.message : "Failed to restart mind" }, 500);
5993
6239
  }
5994
6240
  }).post("/:name/stop", requireSelf(), async (c) => {
@@ -6003,13 +6249,14 @@ ${user.trimEnd()}
6003
6249
  await stopMindFull(name);
6004
6250
  return c.json({ ok: true });
6005
6251
  } catch (err) {
6252
+ logger_default.error(`failed to stop mind ${name}`, logger_default.errorData(err));
6006
6253
  return c.json({ error: err instanceof Error ? err.message : "Failed to stop mind" }, 500);
6007
6254
  }
6008
6255
  }).get("/:name/sleep", requireSelf(), async (c) => {
6009
6256
  const name = c.req.param("name");
6010
6257
  const entry = await findMind(name);
6011
6258
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6012
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
6259
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
6013
6260
  const sm = getSleepManagerIfReady2();
6014
6261
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6015
6262
  return c.json(sm.getState(name));
@@ -6017,7 +6264,7 @@ ${user.trimEnd()}
6017
6264
  const name = c.req.param("name");
6018
6265
  const entry = await findMind(name);
6019
6266
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6020
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
6267
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
6021
6268
  const sm = getSleepManagerIfReady2();
6022
6269
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6023
6270
  if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
@@ -6037,7 +6284,7 @@ ${user.trimEnd()}
6037
6284
  const name = c.req.param("name");
6038
6285
  const entry = await findMind(name);
6039
6286
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6040
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
6287
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
6041
6288
  const sm = getSleepManagerIfReady2();
6042
6289
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6043
6290
  const sleepState = sm.getState(name);
@@ -6052,7 +6299,7 @@ ${user.trimEnd()}
6052
6299
  const name = c.req.param("name");
6053
6300
  const entry = await findMind(name);
6054
6301
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6055
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
6302
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
6056
6303
  const sm = getSleepManagerIfReady2();
6057
6304
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6058
6305
  const flushed = await sm.flushQueuedMessages(name);
@@ -6068,19 +6315,19 @@ ${user.trimEnd()}
6068
6315
  if (body.displayName !== void 0) profile.displayName = body.displayName;
6069
6316
  if (body.description !== void 0) profile.description = body.description;
6070
6317
  if (body.avatar !== void 0) {
6071
- const homeDir = resolve11(dir, "home");
6318
+ const homeDir = resolve13(dir, "home");
6072
6319
  const avatarPath = safeResolveWithinBase(homeDir, body.avatar);
6073
6320
  if (!avatarPath) {
6074
6321
  return c.json({ error: "Avatar path must be inside the mind's home directory" }, 400);
6075
6322
  }
6076
- if (!existsSync7(avatarPath)) {
6323
+ if (!existsSync8(avatarPath)) {
6077
6324
  return c.json({ error: `Avatar file not found: ${relative(homeDir, avatarPath)}` }, 400);
6078
6325
  }
6079
6326
  profile.avatar = relative(homeDir, avatarPath);
6080
6327
  }
6081
6328
  config2.profile = profile;
6082
6329
  writeVoluteConfig(dir, config2);
6083
- const { syncMindProfile: syncMindProfile2 } = await import("./auth-DT7GY7EZ.js");
6330
+ const { syncMindProfile: syncMindProfile2 } = await import("./auth-KRDSM55Q.js");
6084
6331
  await syncMindProfile2(name, profile);
6085
6332
  broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
6086
6333
  return c.json({ ok: true });
@@ -6124,7 +6371,7 @@ ${user.trimEnd()}
6124
6371
  });
6125
6372
  }
6126
6373
  if (!force) {
6127
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
6374
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
6128
6375
  if (getSleepManagerIfReady2()?.isSleeping(name)) {
6129
6376
  return c.json({ output: "" });
6130
6377
  }
@@ -6179,7 +6426,7 @@ ${user.trimEnd()}
6179
6426
  }
6180
6427
  }
6181
6428
  const dir = entry.dir ?? mindDir(name);
6182
- const { evaluateSeedChecklist: evaluateSeedChecklist2 } = await import("./seed-readiness-QRA46ZJQ.js");
6429
+ const { evaluateSeedChecklist: evaluateSeedChecklist2 } = await import("./seed-readiness-DYOVCYHN.js");
6183
6430
  const checklist = evaluateSeedChecklist2(dir);
6184
6431
  const done = [];
6185
6432
  const remaining = [];
@@ -6221,7 +6468,7 @@ ${user.trimEnd()}
6221
6468
  try {
6222
6469
  const sproutedDir = entry.dir ?? mindDir(name);
6223
6470
  if (setupDefaultDreaming(sproutedDir).schedulesChanged) {
6224
- const { getScheduler: getScheduler2 } = await import("./scheduler-UV6RUJ7P.js");
6471
+ const { getScheduler: getScheduler2 } = await import("./scheduler-JTTWZDYU.js");
6225
6472
  getScheduler2().loadSchedules(name, sproutedDir);
6226
6473
  }
6227
6474
  } catch (err) {
@@ -6231,7 +6478,7 @@ ${user.trimEnd()}
6231
6478
  const spiritName = getSpiritName();
6232
6479
  const spiritEntry = await findMind(spiritName);
6233
6480
  if (spiritEntry) {
6234
- const { firstWeekSchedules, spiritDir } = await import("./spirit-IL5EMWYZ.js");
6481
+ const { firstWeekSchedules, spiritDir } = await import("./spirit-4ECYIVGE.js");
6235
6482
  const sDir = spiritEntry.dir ?? spiritDir();
6236
6483
  const spiritConfig = readVoluteConfig(sDir);
6237
6484
  if (spiritConfig) {
@@ -6245,7 +6492,7 @@ ${user.trimEnd()}
6245
6492
  spiritConfig.schedules = schedules;
6246
6493
  writeVoluteConfig(sDir, spiritConfig);
6247
6494
  try {
6248
- const { getScheduler: getScheduler2 } = await import("./scheduler-UV6RUJ7P.js");
6495
+ const { getScheduler: getScheduler2 } = await import("./scheduler-JTTWZDYU.js");
6249
6496
  getScheduler2().loadSchedules(spiritName, sDir);
6250
6497
  } catch (err) {
6251
6498
  logger_default.warn(
@@ -6267,26 +6514,42 @@ ${user.trimEnd()}
6267
6514
  const name = c.req.param("name");
6268
6515
  const entry = await findMind(name);
6269
6516
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6517
+ const manager = getMindManager();
6518
+ if (entry.parent) {
6519
+ if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 500);
6520
+ const parentEntry = await findMind(entry.parent);
6521
+ if (!parentEntry) return c.json({ error: `Parent mind ${entry.parent} not found` }, 404);
6522
+ if (manager.isRunning(name)) {
6523
+ await stopMindFull(name);
6524
+ }
6525
+ await cleanupVariant(name, entry.parent, parentEntry.dir ?? mindDir(entry.parent), entry.dir);
6526
+ invalidateMindUserCache(name);
6527
+ fireWebhook({
6528
+ event: "mind_deleted",
6529
+ mind: name,
6530
+ data: { port: entry.port, stage: entry.stage, template: entry.template }
6531
+ });
6532
+ return c.json({ ok: true, variant: true });
6533
+ }
6270
6534
  const dir = mindDir(name);
6271
6535
  const force = c.req.query("force") === "true";
6272
- const manager = getMindManager();
6273
6536
  if (manager.isRunning(name)) {
6274
6537
  await stopMindFull(name);
6275
6538
  }
6276
6539
  const variants = await findVariants(name);
6277
6540
  for (const s of variants) {
6278
6541
  if (s.dir) {
6279
- await cleanupVariant(s.name, dir, s.dir, { stop: true });
6542
+ await cleanupVariant(s.name, name, dir, s.dir, { stop: true });
6280
6543
  }
6281
6544
  }
6282
6545
  await removeMind(name);
6283
6546
  await deleteMindUser2(name);
6284
6547
  invalidateMindUserCache(name);
6285
6548
  const state = stateDir(name);
6286
- if (existsSync7(state)) {
6549
+ if (existsSync8(state)) {
6287
6550
  rmSync3(state, { recursive: true, force: true });
6288
6551
  }
6289
- if (force && existsSync7(dir)) {
6552
+ if (force && existsSync8(dir)) {
6290
6553
  rmSync3(dir, { recursive: true, force: true });
6291
6554
  deleteMindUser(name);
6292
6555
  }
@@ -6301,7 +6564,7 @@ ${user.trimEnd()}
6301
6564
  const entry = await findMind(mindName);
6302
6565
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6303
6566
  const dir = mindDir(mindName);
6304
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
6567
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
6305
6568
  let body = {};
6306
6569
  try {
6307
6570
  body = await c.req.json();
@@ -6314,21 +6577,21 @@ ${user.trimEnd()}
6314
6577
  }
6315
6578
  const UPGRADE_BRANCH = "upgrade";
6316
6579
  const upgradeVariantName = `${mindName}-upgrade`;
6317
- const worktreeDir = resolve11(dir, ".variants", UPGRADE_BRANCH);
6580
+ const worktreeDir = resolve13(dir, ".variants", UPGRADE_BRANCH);
6318
6581
  if (body.abort) {
6319
- if (!existsSync7(worktreeDir)) {
6582
+ if (!existsSync8(worktreeDir)) {
6320
6583
  return c.json({ error: "No upgrade in progress" }, 400);
6321
6584
  }
6322
6585
  try {
6323
6586
  try {
6324
- const gitDirContent = readFileSync9(resolve11(worktreeDir, ".git"), "utf-8").trim();
6587
+ const gitDirContent = readFileSync9(resolve13(worktreeDir, ".git"), "utf-8").trim();
6325
6588
  const gitDir = gitDirContent.replace("gitdir: ", "");
6326
- if (existsSync7(resolve11(gitDir, "MERGE_HEAD"))) {
6589
+ if (existsSync8(resolve13(gitDir, "MERGE_HEAD"))) {
6327
6590
  await gitExec(["merge", "--abort"], { cwd: worktreeDir });
6328
6591
  }
6329
6592
  } catch {
6330
6593
  }
6331
- await cleanupVariant(upgradeVariantName, dir, worktreeDir, { stop: true });
6594
+ await cleanupVariant(upgradeVariantName, mindName, dir, worktreeDir, { stop: true });
6332
6595
  try {
6333
6596
  await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
6334
6597
  } catch {
@@ -6342,7 +6605,7 @@ ${user.trimEnd()}
6342
6605
  }
6343
6606
  }
6344
6607
  if (body.continue) {
6345
- if (!existsSync7(worktreeDir)) {
6608
+ if (!existsSync8(worktreeDir)) {
6346
6609
  return c.json({ error: "No upgrade in progress" }, 400);
6347
6610
  }
6348
6611
  const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
@@ -6392,9 +6655,9 @@ ${user.trimEnd()}
6392
6655
  }
6393
6656
  }
6394
6657
  if (body.accept) {
6395
- if (existsSync7(worktreeDir)) {
6658
+ if (existsSync8(worktreeDir)) {
6396
6659
  try {
6397
- await cleanupVariant(upgradeVariantName, dir, worktreeDir, { stop: true });
6660
+ await cleanupVariant(upgradeVariantName, mindName, dir, worktreeDir, { stop: true });
6398
6661
  } catch (err) {
6399
6662
  logger_default.warn(`failed to clean up legacy upgrade variant for ${mindName}`, logger_default.errorData(err));
6400
6663
  }
@@ -6407,7 +6670,7 @@ ${user.trimEnd()}
6407
6670
  }
6408
6671
  if (body.diff) {
6409
6672
  try {
6410
- if (!existsSync7(resolve11(dir, ".git"))) {
6673
+ if (!existsSync8(resolve13(dir, ".git"))) {
6411
6674
  return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
6412
6675
  }
6413
6676
  await updateTemplateBranch(dir, template, mindName);
@@ -6425,22 +6688,22 @@ ${user.trimEnd()}
6425
6688
  );
6426
6689
  }
6427
6690
  }
6428
- if (existsSync7(worktreeDir)) {
6691
+ if (existsSync8(worktreeDir)) {
6429
6692
  return c.json(
6430
6693
  { error: "Upgrade variant already exists. Use continue or delete it first." },
6431
6694
  409
6432
6695
  );
6433
6696
  }
6434
- if (!existsSync7(resolve11(dir, ".git"))) {
6697
+ if (!existsSync8(resolve13(dir, ".git"))) {
6435
6698
  try {
6436
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dir, "home") } : void 0;
6699
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dir, "home") } : void 0;
6437
6700
  await gitExec(["init"], { cwd: dir, mindName, env });
6438
6701
  await configureGitIdentity(mindName, { cwd: dir, mindName, env });
6439
6702
  await gitExec(["add", "-A"], { cwd: dir, mindName, env });
6440
6703
  await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
6441
6704
  await chownMindDir(dir, mindName);
6442
6705
  } catch (err) {
6443
- rmSync3(resolve11(dir, ".git"), { recursive: true, force: true });
6706
+ rmSync3(resolve13(dir, ".git"), { recursive: true, force: true });
6444
6707
  return c.json(
6445
6708
  {
6446
6709
  error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
@@ -6455,8 +6718,8 @@ ${user.trimEnd()}
6455
6718
  } catch {
6456
6719
  }
6457
6720
  await updateTemplateBranch(dir, template, mindName);
6458
- const parentDir = resolve11(dir, ".variants");
6459
- if (!existsSync7(parentDir)) {
6721
+ const parentDir = resolve13(dir, ".variants");
6722
+ if (!existsSync8(parentDir)) {
6460
6723
  mkdirSync6(parentDir, { recursive: true });
6461
6724
  }
6462
6725
  await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
@@ -6519,7 +6782,7 @@ ${user.trimEnd()}
6519
6782
  return c.json(result);
6520
6783
  } catch (err) {
6521
6784
  try {
6522
- await cleanupVariant(upgradeVariantName, dir, worktreeDir);
6785
+ await cleanupVariant(upgradeVariantName, mindName, dir, worktreeDir);
6523
6786
  } catch (cleanupErr) {
6524
6787
  logger_default.warn(`cleanup failed after upgrade error for ${mindName}`, logger_default.errorData(cleanupErr));
6525
6788
  }
@@ -6589,11 +6852,11 @@ ${user.trimEnd()}
6589
6852
  const entry = await findMind(name);
6590
6853
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6591
6854
  const dir = entry.dir ?? mindDir(name);
6592
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
6855
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
6593
6856
  let config2 = readVoluteConfig(dir);
6594
6857
  if (!config2 && entry.template === "pi") {
6595
- const piConfigPath = resolve11(dir, "home/.config/config.json");
6596
- if (existsSync7(piConfigPath)) {
6858
+ const piConfigPath = resolve13(dir, "home/.config/config.json");
6859
+ if (existsSync8(piConfigPath)) {
6597
6860
  try {
6598
6861
  config2 = JSON.parse(readFileSync9(piConfigPath, "utf-8"));
6599
6862
  } catch {
@@ -6601,8 +6864,8 @@ ${user.trimEnd()}
6601
6864
  }
6602
6865
  }
6603
6866
  let templateConfig = {};
6604
- const configJsonPath = resolve11(dir, "home/.config/config.json");
6605
- if (existsSync7(configJsonPath)) {
6867
+ const configJsonPath = resolve13(dir, "home/.config/config.json");
6868
+ if (existsSync8(configJsonPath)) {
6606
6869
  try {
6607
6870
  templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
6608
6871
  } catch {
@@ -6644,7 +6907,7 @@ ${user.trimEnd()}
6644
6907
  const entry = await findMind(name);
6645
6908
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6646
6909
  const dir = entry.dir ?? mindDir(name);
6647
- if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
6910
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
6648
6911
  const body = c.req.valid("json");
6649
6912
  const existing = readVoluteConfig(dir) ?? {};
6650
6913
  if (body.model !== void 0) existing.model = body.model;
@@ -6670,14 +6933,14 @@ ${user.trimEnd()}
6670
6933
  }
6671
6934
  writeVoluteConfig(dir, existing);
6672
6935
  if (body.unescapeNewlines !== void 0) {
6673
- const { clearEchoTextCache } = await import("./echo-text-WYAAODBV.js");
6936
+ const { clearEchoTextCache } = await import("./echo-text-PEEV7FQU.js");
6674
6937
  clearEchoTextCache(name);
6675
6938
  }
6676
6939
  const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.compaction !== void 0;
6677
6940
  if (needsConfigJson) {
6678
- const configJsonPath = resolve11(dir, "home/.config/config.json");
6941
+ const configJsonPath = resolve13(dir, "home/.config/config.json");
6679
6942
  let templateConfig = {};
6680
- if (existsSync7(configJsonPath)) {
6943
+ if (existsSync8(configJsonPath)) {
6681
6944
  try {
6682
6945
  templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
6683
6946
  } catch {
@@ -6707,7 +6970,7 @@ ${user.trimEnd()}
6707
6970
  }
6708
6971
  if (entry.mindType === "spirit" && body.model !== void 0) {
6709
6972
  try {
6710
- const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-CAJFOR2N.js");
6973
+ const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-WZSXHHRJ.js");
6711
6974
  const globalConfig = readGlobalConfig2();
6712
6975
  globalConfig.spiritModel = body.model;
6713
6976
  writeGlobalConfig2(globalConfig);
@@ -6750,7 +7013,7 @@ ${user.trimEnd()}
6750
7013
  if (!body.systemPrompt || !body.message) {
6751
7014
  return c.json({ error: "systemPrompt and message required" }, 400);
6752
7015
  }
6753
- const { aiComplete: aiCompleteFn, isAiConfigured: isAiConfigured2 } = await import("./ai-service-V4CTNGI3.js");
7016
+ const { aiComplete: aiCompleteFn, isAiConfigured: isAiConfigured2 } = await import("./ai-service-YW5SWCSB.js");
6754
7017
  if (!isAiConfigured2()) {
6755
7018
  return c.json({ error: "AI service not configured" }, 503);
6756
7019
  }
@@ -7444,7 +7707,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
7444
7707
  const entry = await findMind(name);
7445
7708
  if (!entry) return c.json({ error: "Mind not found" }, 404);
7446
7709
  const body = await c.req.text();
7447
- const { deliverEvent: deliverEvent2 } = await import("./system-events-A6IGCEC3.js");
7710
+ const { deliverEvent: deliverEvent2 } = await import("./system-events-V43HDA3T.js");
7448
7711
  const { id, delivered } = await deliverEvent2(name, {
7449
7712
  type: "webhook",
7450
7713
  body,
@@ -7462,14 +7725,14 @@ var schedules_default = app15;
7462
7725
  // packages/daemon/src/web/api/setup.ts
7463
7726
  import { mkdirSync as mkdirSync7 } from "fs";
7464
7727
  import { homedir as homedir2 } from "os";
7465
- import { resolve as resolve12 } from "path";
7728
+ import { resolve as resolve14 } from "path";
7466
7729
  import { Hono as Hono19 } from "hono";
7467
7730
  import { setCookie as setCookie2 } from "hono/cookie";
7468
7731
  var DEFAULT_API_URL2 = "https://volute.systems";
7469
7732
  var setup = new Hono19();
7470
7733
  function writeSetupConfig(systemName, description) {
7471
- const configHome = process.env.VOLUTE_HOME ?? resolve12(homedir2(), ".volute");
7472
- const mindsDir = resolve12(configHome, "minds");
7734
+ const configHome = process.env.VOLUTE_HOME ?? resolve14(homedir2(), ".volute");
7735
+ const mindsDir = resolve14(configHome, "minds");
7473
7736
  mkdirSync7(configHome, { recursive: true });
7474
7737
  mkdirSync7(mindsDir, { recursive: true });
7475
7738
  const existingConfig = readGlobalConfig();
@@ -7503,11 +7766,11 @@ setup.get("/status", async (c) => {
7503
7766
  let hasAccount = false;
7504
7767
  if (hasSystem) {
7505
7768
  try {
7506
- const { listUsersByType: listUsersByType2 } = await import("./auth-DT7GY7EZ.js");
7769
+ const { listUsersByType: listUsersByType2 } = await import("./auth-KRDSM55Q.js");
7507
7770
  const brains = await listUsersByType2("human");
7508
7771
  hasAccount = brains.length > 0;
7509
7772
  } catch (err) {
7510
- logger_default.debug("could not check for existing accounts during setup status", logger_default.errorData(err));
7773
+ logger_default.error("could not check for existing accounts during setup status", logger_default.errorData(err));
7511
7774
  }
7512
7775
  }
7513
7776
  return c.json({
@@ -7715,7 +7978,7 @@ setup.post("/account", async (c) => {
7715
7978
  }
7716
7979
  }
7717
7980
  try {
7718
- const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-DT7GY7EZ.js");
7981
+ const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-KRDSM55Q.js");
7719
7982
  const user = await createUser2(body.username.trim(), body.password);
7720
7983
  if (body.displayName?.trim()) {
7721
7984
  await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
@@ -7761,7 +8024,7 @@ setup.post("/models", async (c) => {
7761
8024
  return c.json({ error: "Spirit model is required" }, 400);
7762
8025
  }
7763
8026
  try {
7764
- const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-V4CTNGI3.js");
8027
+ const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-YW5SWCSB.js");
7765
8028
  setEnabledModels3(body.models);
7766
8029
  const config2 = readGlobalConfig();
7767
8030
  config2.spiritModel = body.spiritModel.trim();
@@ -7808,15 +8071,15 @@ setup.post("/complete", async (c) => {
7808
8071
  return c.json({ error: "Setup already complete" }, 400);
7809
8072
  }
7810
8073
  try {
7811
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-IL5EMWYZ.js");
7812
- const { startSpiritFull } = await import("./mind-service-5TA7UJAT.js");
8074
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-4ECYIVGE.js");
8075
+ const { startSpiritFull } = await import("./mind-service-E26ZQXDS.js");
7813
8076
  await ensureSpiritProject();
7814
8077
  await syncSpiritTemplate();
7815
8078
  const warnings = [];
7816
8079
  const config2 = readGlobalConfig();
7817
8080
  if (config2.setup?.service && config2.setup?.type === "local") {
7818
8081
  try {
7819
- const { installUserService } = await import("./service-install-MOKTEG7B.js");
8082
+ const { installUserService } = await import("./service-install-2EWD7KXD.js");
7820
8083
  const installed = await installUserService(config2.port, config2.hostname);
7821
8084
  if (!installed) {
7822
8085
  warnings.push("Service installation is not supported on this platform.");
@@ -7846,8 +8109,8 @@ setup.post("/complete", async (c) => {
7846
8109
  }
7847
8110
  let spiritConversationId;
7848
8111
  try {
7849
- const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-DT7GY7EZ.js");
7850
- const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-S2232Y5I.js");
8112
+ const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-KRDSM55Q.js");
8113
+ const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-ZL6PHROD.js");
7851
8114
  const spiritUser = await getOrCreateMindUser2(spiritName);
7852
8115
  const brains = await listUsersByType2("human");
7853
8116
  const admin2 = brains.find((u) => u.role === "admin");
@@ -7869,8 +8132,8 @@ setup.post("/complete", async (c) => {
7869
8132
  logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
7870
8133
  if (spiritConversationId && spiritStarted) {
7871
8134
  try {
7872
- const { deliverMessage: deliverMessage2 } = await import("./message-delivery-3456PXB7.js");
7873
- const { listUsersByType: listUsers2 } = await import("./auth-DT7GY7EZ.js");
8135
+ const { deliverMessage: deliverMessage2 } = await import("./message-delivery-7URC52ME.js");
8136
+ const { listUsersByType: listUsers2 } = await import("./auth-KRDSM55Q.js");
7874
8137
  const admins = await listUsers2("human");
7875
8138
  const admin2 = admins.find((u) => u.role === "admin");
7876
8139
  if (admin2) {
@@ -7911,9 +8174,9 @@ setup.post("/complete", async (c) => {
7911
8174
  var setup_default = setup;
7912
8175
 
7913
8176
  // packages/daemon/src/web/api/skills.ts
7914
- import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync4 } from "fs";
8177
+ import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync4 } from "fs";
7915
8178
  import { tmpdir } from "os";
7916
- import { join, resolve as resolve13 } from "path";
8179
+ import { join, resolve as resolve15 } from "path";
7917
8180
  import AdmZip from "adm-zip";
7918
8181
  import { Hono as Hono20 } from "hono";
7919
8182
  var app16 = new Hono20().get("/", async (c) => {
@@ -7989,19 +8252,19 @@ var app16 = new Hono20().get("/", async (c) => {
7989
8252
  try {
7990
8253
  const zip = new AdmZip(buffer);
7991
8254
  for (const entry of zip.getEntries()) {
7992
- const target = resolve13(tmpDir, entry.entryName);
8255
+ const target = resolve15(tmpDir, entry.entryName);
7993
8256
  if (!target.startsWith(tmpDir)) {
7994
8257
  return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
7995
8258
  }
7996
8259
  }
7997
8260
  zip.extractAllTo(tmpDir, true);
7998
8261
  let skillDir = null;
7999
- if (existsSync8(join(tmpDir, "SKILL.md"))) {
8262
+ if (existsSync9(join(tmpDir, "SKILL.md"))) {
8000
8263
  skillDir = tmpDir;
8001
8264
  } else {
8002
8265
  const entries = readdirSync3(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
8003
8266
  for (const entry of entries) {
8004
- if (existsSync8(join(tmpDir, entry.name, "SKILL.md"))) {
8267
+ if (existsSync9(join(tmpDir, entry.name, "SKILL.md"))) {
8005
8268
  skillDir = join(tmpDir, entry.name);
8006
8269
  break;
8007
8270
  }
@@ -8363,8 +8626,8 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
8363
8626
  });
8364
8627
  }, 15e3);
8365
8628
  cleanups.push(() => clearInterval(keepAlive));
8366
- await new Promise((resolve19) => {
8367
- stream.onAbort(() => resolve19());
8629
+ await new Promise((resolve21) => {
8630
+ stream.onAbort(() => resolve21());
8368
8631
  });
8369
8632
  } finally {
8370
8633
  for (const cleanup of cleanups) {
@@ -8382,7 +8645,7 @@ var events_default = app20;
8382
8645
  import { Hono as Hono25 } from "hono";
8383
8646
 
8384
8647
  // packages/daemon/src/lib/events/feed.ts
8385
- import { and as and6, desc as desc5, eq as eq8, gte as gte3, inArray as inArray3, sql as sql5 } from "drizzle-orm";
8648
+ import { and as and6, desc as desc5, eq as eq8, gte as gte3, inArray as inArray3, ne, sql as sql5 } from "drizzle-orm";
8386
8649
  var BURST_GAP_MS = 45 * 6e4;
8387
8650
  var FEED_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
8388
8651
  var LIFECYCLE_TYPES = [
@@ -8459,7 +8722,13 @@ async function getChatFeedEvents(opts) {
8459
8722
  content: messages.content,
8460
8723
  created_at: messages.created_at,
8461
8724
  conversation_type: conversations.type
8462
- }).from(messages).innerJoin(conversations, eq8(conversations.id, messages.conversation_id)).where(and6(gte3(messages.created_at, cutoff), eq8(conversations.private, 0))).orderBy(desc5(messages.id)).limit(5e3);
8725
+ }).from(messages).innerJoin(conversations, eq8(conversations.id, messages.conversation_id)).where(
8726
+ and6(
8727
+ gte3(messages.created_at, cutoff),
8728
+ eq8(conversations.private, 0),
8729
+ ne(messages.role, "event")
8730
+ )
8731
+ ).orderBy(desc5(messages.id)).limit(5e3);
8463
8732
  const ascending = rows.reverse();
8464
8733
  const bursts = splitIntoBursts(ascending);
8465
8734
  bursts.sort((a, b) => {
@@ -8639,23 +8908,23 @@ var app21 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
8639
8908
  var feed_default = app21;
8640
8909
 
8641
8910
  // packages/daemon/src/web/api/variants.ts
8642
- import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
8643
- import { resolve as resolve16 } from "path";
8911
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
8912
+ import { resolve as resolve18 } from "path";
8644
8913
  import { Hono as Hono26 } from "hono";
8645
8914
 
8646
8915
  // packages/daemon/src/lib/mind/farewell.ts
8647
- import { existsSync as existsSync9, readFileSync as readFileSync10, rmSync as rmSync5 } from "fs";
8648
- import { resolve as resolve14 } from "path";
8916
+ import { existsSync as existsSync10, readFileSync as readFileSync10, rmSync as rmSync5 } from "fs";
8917
+ import { resolve as resolve16 } from "path";
8649
8918
  var flog = logger_default.child("farewell");
8650
8919
  var FAREWELL_RELPATH = ".mind/farewell.md";
8651
8920
  var FAREWELL_MIND_PATH = "../.mind/farewell.md";
8652
8921
  var FAREWELL_TIMEOUT_MS = 12e4;
8653
8922
  function farewellPath(variantDir) {
8654
- return resolve14(variantDir, FAREWELL_RELPATH);
8923
+ return resolve16(variantDir, FAREWELL_RELPATH);
8655
8924
  }
8656
8925
  function readFarewell(variantDir) {
8657
8926
  const path = farewellPath(variantDir);
8658
- if (!existsSync9(path)) return void 0;
8927
+ if (!existsSync10(path)) return void 0;
8659
8928
  try {
8660
8929
  const text = readFileSync10(path, "utf-8").trim();
8661
8930
  return text || void 0;
@@ -8678,7 +8947,7 @@ function waitForFarewell(name, variantDir, timeoutMs) {
8678
8947
  const unsub = subscribe((event) => {
8679
8948
  if (event.mind !== name) return;
8680
8949
  if (event.type !== "mind_done" && event.type !== "mind_idle") return;
8681
- if (existsSync9(farewellPath(variantDir))) cleanup();
8950
+ if (existsSync10(farewellPath(variantDir))) cleanup();
8682
8951
  });
8683
8952
  return { done, cancel: cleanup };
8684
8953
  }
@@ -8711,8 +8980,8 @@ async function runFarewellTurn(opts) {
8711
8980
 
8712
8981
  // packages/daemon/src/lib/mind/spawn-server.ts
8713
8982
  import { spawn as spawn4 } from "child_process";
8714
- import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
8715
- import { resolve as resolve15 } from "path";
8983
+ import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11, statSync as statSync4 } from "fs";
8984
+ import { resolve as resolve17 } from "path";
8716
8985
  async function spawnServer(cwd, port, options) {
8717
8986
  let cmd = process.env.VOLUTE_NODE_PATH ?? process.execPath;
8718
8987
  let args = ["--import", "tsx", "src/server.ts", "--port", String(port)];
@@ -8733,34 +9002,34 @@ function spawnAttached(cmd, args, cwd) {
8733
9002
  cwd,
8734
9003
  stdio: ["ignore", "pipe", "pipe"]
8735
9004
  });
8736
- return new Promise((resolve19) => {
8737
- const timeout = setTimeout(() => resolve19(null), 3e4);
9005
+ return new Promise((resolve21) => {
9006
+ const timeout = setTimeout(() => resolve21(null), 3e4);
8738
9007
  function checkOutput(data) {
8739
9008
  const match = data.toString().match(/listening on :(\d+)/);
8740
9009
  if (match) {
8741
9010
  clearTimeout(timeout);
8742
- resolve19({ child, actualPort: parseInt(match[1], 10) });
9011
+ resolve21({ child, actualPort: parseInt(match[1], 10) });
8743
9012
  }
8744
9013
  }
8745
9014
  child.stdout?.on("data", checkOutput);
8746
9015
  child.stderr?.on("data", checkOutput);
8747
9016
  child.on("error", () => {
8748
9017
  clearTimeout(timeout);
8749
- resolve19(null);
9018
+ resolve21(null);
8750
9019
  });
8751
9020
  child.on("exit", () => {
8752
9021
  clearTimeout(timeout);
8753
- resolve19(null);
9022
+ resolve21(null);
8754
9023
  });
8755
9024
  });
8756
9025
  }
8757
9026
  function spawnDetached(cmd, args, cwd, logDir) {
8758
- const logsDir = logDir ?? resolve15(cwd, ".mind", "logs");
9027
+ const logsDir = logDir ?? resolve17(cwd, ".mind", "logs");
8759
9028
  mkdirSync8(logsDir, { recursive: true });
8760
- const logPath = resolve15(logsDir, "mind.log");
9029
+ const logPath = resolve17(logsDir, "mind.log");
8761
9030
  let startOffset = 0;
8762
9031
  try {
8763
- startOffset = statSync3(logPath).size;
9032
+ startOffset = statSync4(logPath).size;
8764
9033
  } catch {
8765
9034
  }
8766
9035
  const logFd = openSync(logPath, "a");
@@ -8898,11 +9167,11 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
8898
9167
  return c.json({ error: `Name already in use: ${variantName}` }, 409);
8899
9168
  }
8900
9169
  const projectRoot = entry.dir ?? mindDir(mindName);
8901
- const variantDir = resolve16(projectRoot, ".variants", variantName);
8902
- if (existsSync10(variantDir)) {
9170
+ const variantDir = resolve18(projectRoot, ".variants", variantName);
9171
+ if (existsSync11(variantDir)) {
8903
9172
  return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
8904
9173
  }
8905
- mkdirSync9(resolve16(projectRoot, ".variants"), { recursive: true });
9174
+ mkdirSync9(resolve18(projectRoot, ".variants"), { recursive: true });
8906
9175
  try {
8907
9176
  await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
8908
9177
  } catch (e) {
@@ -8921,18 +9190,10 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
8921
9190
  }
8922
9191
  const rollbackSplit = async () => {
8923
9192
  try {
8924
- await cleanupVariant(variantName, projectRoot, variantDir, { stop: true });
9193
+ await cleanupVariant(variantName, mindName, projectRoot, variantDir, { stop: true });
8925
9194
  } catch (err2) {
8926
9195
  logger_default.warn(`failed to roll back split of ${variantName}`, logger_default.errorData(err2));
8927
9196
  }
8928
- try {
8929
- await chownMindDir(projectRoot, mindName);
8930
- } catch (err2) {
8931
- logger_default.warn(
8932
- `failed to restore ${mindName} ownership after split rollback`,
8933
- logger_default.errorData(err2)
8934
- );
8935
- }
8936
9197
  };
8937
9198
  try {
8938
9199
  await chownMindDir(projectRoot, mindName);
@@ -8946,7 +9207,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
8946
9207
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
8947
9208
  await exec(cmd, args, {
8948
9209
  cwd: variantDir,
8949
- env: { ...process.env, HOME: resolve16(variantDir, "home") }
9210
+ env: { ...process.env, HOME: resolve18(variantDir, "home") }
8950
9211
  });
8951
9212
  } else {
8952
9213
  await exec("npm", ["install"], { cwd: variantDir });
@@ -8957,7 +9218,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
8957
9218
  return c.json({ error: `npm install failed: ${msg}` }, 500);
8958
9219
  }
8959
9220
  if (body.soul) {
8960
- writeFileSync9(resolve16(variantDir, "home/SOUL.md"), body.soul);
9221
+ writeFileSync9(resolve18(variantDir, "home/SOUL.md"), body.soul);
8961
9222
  }
8962
9223
  const variantPort = body.port ?? await nextPort();
8963
9224
  try {
@@ -9010,7 +9271,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9010
9271
  } catch {
9011
9272
  }
9012
9273
  const projectRoot = parentEntry.dir ?? mindDir(mindName);
9013
- const failAfterGitWrite = async (message, extra) => {
9274
+ const failAfterGitWrite = async (message, extra, status = 500) => {
9014
9275
  try {
9015
9276
  await chownMindDir(projectRoot, mindName);
9016
9277
  } catch (err) {
@@ -9026,7 +9287,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9026
9287
  500
9027
9288
  );
9028
9289
  }
9029
- return c.json({ error: message, ...extra }, 500);
9290
+ return c.json({ error: message, ...extra }, status);
9030
9291
  };
9031
9292
  let farewell;
9032
9293
  try {
@@ -9040,7 +9301,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9040
9301
  logger_default.warn(`farewell turn failed for ${variantName}`, logger_default.errorData(err));
9041
9302
  }
9042
9303
  try {
9043
- if (existsSync10(variantEntry.dir)) {
9304
+ if (existsSync11(variantEntry.dir)) {
9044
9305
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
9045
9306
  if (status) {
9046
9307
  try {
@@ -9098,12 +9359,33 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9098
9359
  { conflicts: err.conflicts }
9099
9360
  );
9100
9361
  }
9101
- await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
9362
+ if (existsSync11(variantEntry.dir) && !body.discardUnresolved) {
9363
+ let unresolved;
9364
+ try {
9365
+ unresolved = await findUnresolvedHomeFiles(variantEntry.dir);
9366
+ } catch (err) {
9367
+ return failAfterGitWrite(
9368
+ `Could not verify variant ${variantName} has no unresolved home/ files: ${err instanceof Error ? err.message : String(err)}. Variant left intact \u2014 retry the join.`
9369
+ );
9370
+ }
9371
+ if (unresolved.totalCount > 0) {
9372
+ return failAfterGitWrite(
9373
+ formatUnresolvedHomeFilesMessage(variantName, unresolved),
9374
+ {
9375
+ unresolvedFiles: unresolved.files,
9376
+ unresolvedCount: unresolved.totalCount,
9377
+ unresolvedBytes: unresolved.totalBytes
9378
+ },
9379
+ 409
9380
+ );
9381
+ }
9382
+ }
9383
+ await cleanupVariant(variantName, mindName, projectRoot, variantEntry.dir, { stop: true });
9102
9384
  await chownMindDir(projectRoot, mindName);
9103
9385
  if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
9104
9386
  try {
9105
- const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-GSOUARW4.js");
9106
- const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-4UF6OCOE.js");
9387
+ const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-YKWMVYMM.js");
9388
+ const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-TRC2HJXR.js");
9107
9389
  const tmpl = parentEntry.template ?? "claude";
9108
9390
  await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
9109
9391
  } catch (err) {
@@ -9116,7 +9398,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9116
9398
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
9117
9399
  await exec(cmd, args, {
9118
9400
  cwd: projectRoot,
9119
- env: { ...process.env, HOME: resolve16(projectRoot, "home") }
9401
+ env: { ...process.env, HOME: resolve18(projectRoot, "home") }
9120
9402
  });
9121
9403
  } else {
9122
9404
  await exec("npm", ["install"], { cwd: projectRoot });
@@ -9164,7 +9446,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9164
9446
  }
9165
9447
  if (!variantEntry.dir) return c.json({ error: `Variant ${variantName} has no directory` }, 500);
9166
9448
  const projectRoot = parentEntry.dir ?? mindDir(mindName);
9167
- await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
9449
+ await cleanupVariant(variantName, mindName, projectRoot, variantEntry.dir, { stop: true });
9168
9450
  return c.json({ ok: true });
9169
9451
  });
9170
9452
  var variants_default = app22;
@@ -9603,11 +9885,11 @@ var app24 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
9603
9885
  if (!stream.aborted) console.error("[chat] SSE ping error:", err);
9604
9886
  });
9605
9887
  }, 15e3);
9606
- await new Promise((resolve19) => {
9888
+ await new Promise((resolve21) => {
9607
9889
  stream.onAbort(() => {
9608
9890
  unsubscribe();
9609
9891
  clearInterval(keepAlive);
9610
- resolve19();
9892
+ resolve21();
9611
9893
  });
9612
9894
  });
9613
9895
  });
@@ -9824,10 +10106,10 @@ app26.route("/api/conversations", conversations_default);
9824
10106
  var app_default = app26;
9825
10107
 
9826
10108
  // packages/daemon/src/web/server.ts
9827
- import { existsSync as existsSync11 } from "fs";
10109
+ import { existsSync as existsSync12 } from "fs";
9828
10110
  import { readFile as readFile2, stat as stat2 } from "fs/promises";
9829
10111
  import { createServer as createHttpsServer } from "https";
9830
- import { dirname as dirname3, extname as extname3, resolve as resolve17 } from "path";
10112
+ import { dirname as dirname3, extname as extname3, resolve as resolve19 } from "path";
9831
10113
  import { serve } from "@hono/node-server";
9832
10114
  var MIME_TYPES2 = {
9833
10115
  ".html": "text/html",
@@ -9846,8 +10128,8 @@ async function startServer({
9846
10128
  let assetsDir = "";
9847
10129
  let searchDir = dirname3(new URL(import.meta.url).pathname);
9848
10130
  for (let i = 0; i < 5; i++) {
9849
- const candidate = resolve17(searchDir, "dist", "web-assets");
9850
- if (existsSync11(candidate)) {
10131
+ const candidate = resolve19(searchDir, "dist", "web-assets");
10132
+ if (existsSync12(candidate)) {
9851
10133
  assetsDir = candidate;
9852
10134
  break;
9853
10135
  }
@@ -9857,7 +10139,7 @@ async function startServer({
9857
10139
  app_default.get("*", async (c) => {
9858
10140
  const urlPath = new URL(c.req.url).pathname;
9859
10141
  if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
9860
- const filePath = resolve17(assetsDir, urlPath.slice(1));
10142
+ const filePath = resolve19(assetsDir, urlPath.slice(1));
9861
10143
  if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
9862
10144
  const s = await stat2(filePath).catch(() => null);
9863
10145
  if (s?.isFile()) {
@@ -9870,7 +10152,7 @@ async function startServer({
9870
10152
  const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
9871
10153
  return c.body(body, 200, { "Content-Type": mime, "Cache-Control": cacheControl });
9872
10154
  }
9873
- const indexPath = resolve17(assetsDir, "index.html");
10155
+ const indexPath = resolve19(assetsDir, "index.html");
9874
10156
  const indexStat = await stat2(indexPath).catch(() => null);
9875
10157
  if (indexStat?.isFile()) {
9876
10158
  const body = await readFile2(indexPath, "utf-8");
@@ -9887,10 +10169,10 @@ async function startServer({
9887
10169
  createServer: createHttpsServer,
9888
10170
  serverOptions: { key: tls.key, cert: tls.cert }
9889
10171
  });
9890
- await new Promise((resolve19, reject) => {
10172
+ await new Promise((resolve21, reject) => {
9891
10173
  server2.on("listening", () => {
9892
10174
  logger_default.info("Volute UI running (https)", { hostname, port });
9893
- resolve19();
10175
+ resolve21();
9894
10176
  });
9895
10177
  server2.on("error", (err) => {
9896
10178
  reject(err);
@@ -9898,13 +10180,13 @@ async function startServer({
9898
10180
  });
9899
10181
  const internalPort = port + 1;
9900
10182
  const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
9901
- await new Promise((resolve19, reject) => {
10183
+ await new Promise((resolve21, reject) => {
9902
10184
  internalServer.on("listening", () => {
9903
10185
  logger_default.info("Volute API running (http, internal)", {
9904
10186
  hostname: "127.0.0.1",
9905
10187
  port: internalPort
9906
10188
  });
9907
- resolve19();
10189
+ resolve21();
9908
10190
  });
9909
10191
  internalServer.on("error", (err) => {
9910
10192
  reject(err);
@@ -9913,10 +10195,10 @@ async function startServer({
9913
10195
  return { server: server2, internalPort };
9914
10196
  }
9915
10197
  const server = serve({ fetch: app_default.fetch, port, hostname });
9916
- await new Promise((resolve19, reject) => {
10198
+ await new Promise((resolve21, reject) => {
9917
10199
  server.on("listening", () => {
9918
10200
  logger_default.info("Volute API running (http)", { hostname, port });
9919
- resolve19();
10201
+ resolve21();
9920
10202
  });
9921
10203
  server.on("error", (err) => {
9922
10204
  reject(err);
@@ -9927,17 +10209,17 @@ async function startServer({
9927
10209
 
9928
10210
  // packages/daemon/src/daemon.ts
9929
10211
  if (!process.env.VOLUTE_HOME) {
9930
- process.env.VOLUTE_HOME = resolve18(homedir3(), ".volute");
10212
+ process.env.VOLUTE_HOME = resolve20(homedir3(), ".volute");
9931
10213
  }
9932
10214
  if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
9933
10215
  process.env.TZ = process.env.VOLUTE_TIMEZONE;
9934
10216
  }
9935
10217
  function writeDaemonConfig(systemDir, config2, token) {
9936
- const configPath = resolve18(systemDir, "daemon.json");
10218
+ const configPath = resolve20(systemDir, "daemon.json");
9937
10219
  writeFileSync10(configPath, `${JSON.stringify(config2, null, 2)}
9938
10220
  `, { mode: 420 });
9939
10221
  chmodSync(configPath, 420);
9940
- const tokenPath = resolve18(systemDir, "daemon-token");
10222
+ const tokenPath = resolve20(systemDir, "daemon-token");
9941
10223
  writeFileSync10(tokenPath, `${token}
9942
10224
  `, { mode: 384 });
9943
10225
  chmodSync(tokenPath, 384);
@@ -9948,7 +10230,7 @@ async function startDaemon(opts) {
9948
10230
  const home = voluteHome();
9949
10231
  const systemDir = voluteSystemDir();
9950
10232
  if (!opts.foreground) {
9951
- const rotatingLog = new RotatingLog(resolve18(systemDir, "daemon.log"));
10233
+ const rotatingLog = new RotatingLog(resolve20(systemDir, "daemon.log"));
9952
10234
  logger_default.setOutput((line) => rotatingLog.write(`${line}
9953
10235
  `));
9954
10236
  const write = (...args) => rotatingLog.write(`${format(...args)}
@@ -9958,38 +10240,38 @@ async function startDaemon(opts) {
9958
10240
  console.warn = write;
9959
10241
  console.info = write;
9960
10242
  }
9961
- const DAEMON_PID_PATH = resolve18(systemDir, "daemon.pid");
9962
- const DAEMON_JSON_PATH = resolve18(systemDir, "daemon.json");
9963
- const DAEMON_TOKEN_PATH = resolve18(systemDir, "daemon-token");
10243
+ const DAEMON_PID_PATH = resolve20(systemDir, "daemon.pid");
10244
+ const DAEMON_JSON_PATH = resolve20(systemDir, "daemon.json");
10245
+ const DAEMON_TOKEN_PATH = resolve20(systemDir, "daemon-token");
9964
10246
  mkdirSync10(home, { recursive: true });
9965
10247
  ensureSystemDir();
9966
- const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-CAJFOR2N.js");
10248
+ const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-WZSXHHRJ.js");
9967
10249
  migrateConfigSecrets();
9968
10250
  migrateSetupCompleted();
9969
- const { migrateAiModelQualification } = await import("./ai-service-V4CTNGI3.js");
10251
+ const { migrateAiModelQualification } = await import("./ai-service-YW5SWCSB.js");
9970
10252
  migrateAiModelQualification();
9971
- await (await import("./db-QAY45Z56.js")).getDb();
10253
+ await (await import("./db-BONH4SUG.js")).getDb();
9972
10254
  try {
9973
10255
  const { eq: eq9, and: and7 } = await import("drizzle-orm");
9974
- const { users: users2 } = await import("./schema-C2GIPD4V.js");
9975
- const db = await (await import("./db-QAY45Z56.js")).getDb();
10256
+ const { users: users2 } = await import("./schema-ZZQVM4GR.js");
10257
+ const db = await (await import("./db-BONH4SUG.js")).getDb();
9976
10258
  await db.update(users2).set({ role: "system" }).where(and7(eq9(users2.user_type, "system"), eq9(users2.role, "user")));
9977
10259
  } catch (err) {
9978
10260
  logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
9979
10261
  }
9980
10262
  try {
9981
- const { migrateAvatarSizes } = await import("./avatar-image-QEPAP3A5.js");
10263
+ const { migrateAvatarSizes } = await import("./avatar-image-52WGXLHV.js");
9982
10264
  await migrateAvatarSizes();
9983
10265
  } catch (err) {
9984
10266
  logger_default.warn("avatar size migration failed", logger_default.errorData(err));
9985
10267
  }
9986
10268
  try {
9987
- const { migrateThreadConfigs } = await import("./migrate-thread-config-4A5LW36Z.js");
10269
+ const { migrateThreadConfigs } = await import("./migrate-thread-config-EQSEP2XG.js");
9988
10270
  await migrateThreadConfigs();
9989
10271
  } catch (err) {
9990
10272
  logger_default.warn("session\u2192thread config migration failed", logger_default.errorData(err));
9991
10273
  }
9992
- const { initSandbox } = await import("./sandbox-EGEU5GGE.js");
10274
+ const { initSandbox } = await import("./sandbox-NNWCTHRI.js");
9993
10275
  await initSandbox();
9994
10276
  try {
9995
10277
  await syncBuiltinSkills();
@@ -10021,7 +10303,7 @@ async function startDaemon(opts) {
10021
10303
  logger_default.warn("failed to backfill minds into #system", logger_default.errorData(err));
10022
10304
  }
10023
10305
  try {
10024
- const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-DT7GY7EZ.js");
10306
+ const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-KRDSM55Q.js");
10025
10307
  await getOrCreateSystemUser2();
10026
10308
  } catch (err) {
10027
10309
  logger_default.warn(
@@ -10030,12 +10312,12 @@ async function startDaemon(opts) {
10030
10312
  );
10031
10313
  }
10032
10314
  const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes3(32).toString("hex");
10033
- const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-CAJFOR2N.js");
10315
+ const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-WZSXHHRJ.js");
10034
10316
  const globalCfg = readGlobalConfig2();
10035
10317
  let tls;
10036
10318
  if (opts.tailscale || globalCfg.tailscale) {
10037
10319
  try {
10038
- const { getTailscaleTls } = await import("./tailscale-SJUW2ETR.js");
10320
+ const { getTailscaleTls } = await import("./tailscale-KHNZFC7L.js");
10039
10321
  const tlsConfig = await getTailscaleTls();
10040
10322
  tls = { key: tlsConfig.key, cert: tlsConfig.cert };
10041
10323
  logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
@@ -10089,7 +10371,7 @@ async function startDaemon(opts) {
10089
10371
  const orphanedTurns = await completeOrphanedTurns();
10090
10372
  summarizeOrphanedTurns(orphanedTurns);
10091
10373
  try {
10092
- const { reconcileVariants } = await import("./variant-cleanup-HJ7Z75R6.js");
10374
+ const { reconcileVariants } = await import("./variant-cleanup-STC6B5HR.js");
10093
10375
  await reconcileVariants();
10094
10376
  } catch (err) {
10095
10377
  logger_default.error("failed to reconcile variants", logger_default.errorData(err));
@@ -10123,15 +10405,15 @@ async function startDaemon(opts) {
10123
10405
  await Promise.all(workers);
10124
10406
  }
10125
10407
  try {
10126
- const { isSetupComplete: isSetupComplete2, getSpiritName: getSpiritName2 } = await import("./setup-CAJFOR2N.js");
10408
+ const { isSetupComplete: isSetupComplete2, getSpiritName: getSpiritName2 } = await import("./setup-WZSXHHRJ.js");
10127
10409
  if (isSetupComplete2()) {
10128
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-IL5EMWYZ.js");
10129
- const { startSpiritFull } = await import("./mind-service-5TA7UJAT.js");
10410
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-4ECYIVGE.js");
10411
+ const { startSpiritFull } = await import("./mind-service-E26ZQXDS.js");
10130
10412
  await ensureSpiritProject();
10131
10413
  const spiritName = getSpiritName2();
10132
10414
  const spiritEntry = await findMind(spiritName);
10133
10415
  if (spiritEntry?.dir) {
10134
- const { registerMindDir } = await import("./delivery-router-JE7SHYNU.js");
10416
+ const { registerMindDir } = await import("./delivery-router-RHANNTAC.js");
10135
10417
  registerMindDir(spiritName, spiritEntry.dir);
10136
10418
  }
10137
10419
  await syncSpiritTemplate();
@@ -10145,7 +10427,7 @@ async function startDaemon(opts) {
10145
10427
  bridgeManager.startBridges(daemonPort).catch((err) => {
10146
10428
  logger_default.warn("failed to start bridges", logger_default.errorData(err));
10147
10429
  });
10148
- import("./cloud-sync-IDJFVDEP.js").then(
10430
+ import("./cloud-sync-H7EWZASD.js").then(
10149
10431
  ({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
10150
10432
  logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
10151
10433
  })
@@ -10153,7 +10435,7 @@ async function startDaemon(opts) {
10153
10435
  logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
10154
10436
  });
10155
10437
  try {
10156
- const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-7BUHWZNT.js");
10438
+ const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-LBVHH5YV.js");
10157
10439
  backfillTemplateHashes().then(() => warnStaleTemplates()).catch((err) => {
10158
10440
  logger_default.warn("failed to check template staleness", logger_default.errorData(err));
10159
10441
  });