volute 0.52.0 → 0.54.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 (153) hide show
  1. package/dist/{accept-5NK5XJB5.js → accept-RSDIVJL6.js} +3 -0
  2. package/dist/auto-upgrade-HEVZKPTF.js +46 -0
  3. package/dist/{backup-YVGV2ZNM.js → backup-QRNX6RK7.js} +10 -10
  4. package/dist/channels-CKCY64TD.js +203 -0
  5. package/dist/{chat-VAZ25KVT.js → chat-ZUGNL5GS.js} +5 -5
  6. package/dist/chunk-3LHUXY3E.js +102 -0
  7. package/dist/{chunk-SW2U6HRB.js → chunk-444RR3YF.js} +21 -4
  8. package/dist/{chunk-YVR6EHWL.js → chunk-56IT2ZXF.js} +160 -18
  9. package/dist/chunk-5RBEEAKW.js +58 -0
  10. package/dist/{chunk-OXWUEU5V.js → chunk-5U5GLZSD.js} +2 -2
  11. package/dist/{chunk-G7A5LEVI.js → chunk-7XKPG66D.js} +5 -5
  12. package/dist/{chunk-R772ET2M.js → chunk-AAPNU7GA.js} +4 -0
  13. package/dist/{chunk-XRWUQPCA.js → chunk-ATZF2RNJ.js} +25 -3
  14. package/dist/{chunk-6NLW72HP.js → chunk-CKPAVQNK.js} +1 -1
  15. package/dist/{chunk-JHKBALS2.js → chunk-CLCHOXLR.js} +1 -1
  16. package/dist/{chunk-DMK5ASKV.js → chunk-F6D766VQ.js} +6 -2
  17. package/dist/{chunk-AXFLY5GU.js → chunk-HKGYZVWN.js} +16 -1
  18. package/dist/{chunk-ZEZQNP2G.js → chunk-LGXVGIWN.js} +103 -25
  19. package/dist/{chunk-CW7BQKF6.js → chunk-LKUGUQUW.js} +3 -3
  20. package/dist/{chunk-FXWBFDQR.js → chunk-OAYY3WF3.js} +59 -12
  21. package/dist/{chunk-ITK6HV47.js → chunk-OGPVJ53N.js} +14 -6
  22. package/dist/{chunk-M77B73VV.js → chunk-OIOW3FPT.js} +7 -2
  23. package/dist/chunk-OT5SXF7L.js +904 -0
  24. package/dist/{chunk-EDR47UE2.js → chunk-OYF25B62.js} +1 -1
  25. package/dist/{chunk-RPPTGPYF.js → chunk-PPSN2MLC.js} +2 -2
  26. package/dist/{chunk-K2F237KS.js → chunk-RGHF3OK5.js} +154 -18
  27. package/dist/{chunk-RU74YG46.js → chunk-VDUCLVZI.js} +338 -30
  28. package/dist/{chunk-724DVYHU.js → chunk-WHL6PKYP.js} +10 -10
  29. package/dist/{chunk-EEOMA4DX.js → chunk-WZZCA23D.js} +2 -1
  30. package/dist/{chunk-UDZLPR34.js → chunk-YSRYCS3N.js} +13 -8
  31. package/dist/{chunk-5N32EN4O.js → chunk-Z4Q6TQWL.js} +1 -1
  32. package/dist/{chunk-VPOMJMV6.js → chunk-ZNHL5KQJ.js} +1 -1
  33. package/dist/cli.js +17 -17
  34. package/dist/{clock-ZT5YT6HA.js → clock-X7V3C7NL.js} +2 -2
  35. package/dist/{cloud-sync-H7EWZASD.js → cloud-sync-52YNJRMR.js} +12 -11
  36. package/dist/{daemon-restart-4FWUA5JF.js → daemon-restart-QWHN5VMU.js} +4 -4
  37. package/dist/daemon.js +538 -782
  38. package/dist/{db-IPXRJUI7.js → db-I4EH5SLF.js} +3 -1
  39. package/dist/{delivery-manager-VYXVKUMP.js → delivery-manager-7R2YIQ2W.js} +13 -7
  40. package/dist/delivery-notices-RZQ6GONP.js +18 -0
  41. package/dist/{delivery-router-RHANNTAC.js → delivery-router-QS4NLJR6.js} +3 -1
  42. package/dist/{down-QSNEMTTX.js → down-UN2EDX2Y.js} +3 -3
  43. package/dist/{echo-text-PEEV7FQU.js → echo-text-73CYQRL7.js} +14 -13
  44. package/dist/{exec-ABD3DYA2.js → exec-Z34TTUO3.js} +1 -1
  45. package/dist/{extensions-N6L5NYTK.js → extensions-QT65JGV6.js} +22 -11
  46. package/dist/{files-BRY5RRIM.js → files-PCKBQHAY.js} +2 -1
  47. package/dist/{isolation-GVV2IBAU.js → isolation-UQPDO34R.js} +1 -1
  48. package/dist/{message-delivery-7URC52ME.js → message-delivery-TRKT4T2P.js} +16 -13
  49. package/dist/migrate-name-placeholder-C7NCNLWF.js +37 -0
  50. package/dist/{mind-NEXGT3KE.js → mind-CT6ANXK4.js} +11 -11
  51. package/dist/{mind-contacts-QYU4TPEA.js → mind-contacts-PEXCDRXA.js} +6 -6
  52. package/dist/{mind-history-OGNR4L7W.js → mind-history-RZMMCHMX.js} +83 -11
  53. package/dist/{mind-list-SYRA7CH5.js → mind-list-7KBLPLES.js} +1 -1
  54. package/dist/{mind-service-E26ZQXDS.js → mind-service-7EFIJ3ZA.js} +12 -11
  55. package/dist/{mind-sleep-LZAFBDZD.js → mind-sleep-24HV4RCA.js} +2 -2
  56. package/dist/{mind-status-Z2LQFA6F.js → mind-status-4YW4PHMO.js} +15 -0
  57. package/dist/{package-2W2XKSIP.js → package-F6S5NHY2.js} +4 -4
  58. package/dist/{prompts-NBO3HRK3.js → prompts-JECJIFPQ.js} +1 -1
  59. package/dist/{reject-TWB4QJHG.js → reject-4NFLXKRN.js} +6 -2
  60. package/dist/{scheduler-JTTWZDYU.js → scheduler-73Q4MQ7Y.js} +8 -6
  61. package/dist/{seed-64XEHRUP.js → seed-DRKAP3XV.js} +1 -1
  62. package/dist/{seed-cmd-UEL2QB3Q.js → seed-cmd-6IMTO3LG.js} +2 -2
  63. package/dist/{seed-create-4JRQDPGA.js → seed-create-VNLDYRFK.js} +1 -1
  64. package/dist/{seed-readiness-DYOVCYHN.js → seed-readiness-H5TTPAQH.js} +2 -2
  65. package/dist/{seed-sprout-NIDIOJXT.js → seed-sprout-PPGYGK4G.js} +5 -5
  66. package/dist/{send-2YKOZJJ5.js → send-UXUQKO4N.js} +37 -22
  67. package/dist/{service-XQPMZAGI.js → service-T4P5W6RS.js} +2 -2
  68. package/dist/{service-install-2EWD7KXD.js → service-install-R4SNYUVO.js} +3 -3
  69. package/dist/{setup-4IX6PCD3.js → setup-YUAQMDVZ.js} +4 -4
  70. package/dist/skills/dreaming/SKILL.md +2 -2
  71. package/dist/skills/dreaming/references/INSTALL.md +1 -1
  72. package/dist/skills/memory/SKILL.md +47 -43
  73. package/dist/skills/tending/SKILL.md +2 -0
  74. package/dist/skills/volute-mind/references/integrations.md +3 -2
  75. package/dist/skills/volute-mind/references/routing.md +18 -5
  76. package/dist/{skills-MPUALV3S.js → skills-GMQ5UIIW.js} +2 -2
  77. package/dist/{sleep-manager-OFP2GFXF.js → sleep-manager-KHI65FEX.js} +12 -11
  78. package/dist/{spirit-4ECYIVGE.js → spirit-Q5VGFWUI.js} +19 -3
  79. package/dist/{spirit-availability-Y54Y3675.js → spirit-availability-G5FMWKRH.js} +13 -12
  80. package/dist/{sprout-GJMZFRGZ.js → sprout-PMTRL7QN.js} +1 -1
  81. package/dist/{src-LCSOQY6Z.js → src-3FIW7DLH.js} +241 -87
  82. package/dist/src-H3UCOP6B.js +516 -0
  83. package/dist/{src-HGNE2XG4.js → src-LTTQVN2Z.js} +1 -1
  84. package/dist/{status-MWLE3RVC.js → status-LCKTTIWS.js} +2 -2
  85. package/dist/{system-events-V43HDA3T.js → system-events-Q7IVZ5T4.js} +5 -1
  86. package/dist/{systems-VDRUZXNF.js → systems-HL7SGSW7.js} +2 -2
  87. package/dist/{turn-tracker-C5PVIC3B.js → turn-tracker-HZDPMSCC.js} +4 -2
  88. package/dist/{up-H6LJZDRK.js → up-XZDSJVMU.js} +3 -3
  89. package/dist/{update-CIWI2JYA.js → update-ILCPVNDD.js} +2 -2
  90. package/dist/{upgrade-LIE46UBB.js → upgrade-XY2J7NWZ.js} +9 -1
  91. package/dist/{variant-cleanup-STC6B5HR.js → variant-cleanup-QHGGY7ND.js} +8 -6
  92. package/dist/{version-notify-LBVHH5YV.js → version-notify-VNVFX6UP.js} +22 -7
  93. package/dist/web-assets/assets/{index-B_eYTa3R.js → index-CwtxGwe_.js} +16 -16
  94. package/dist/web-assets/assets/index-DhM1yYRN.css +1 -0
  95. package/dist/web-assets/index.html +2 -2
  96. package/package.json +4 -4
  97. package/packages/extensions/intentions/dist/ui/assets/index-3FneLCKR.css +1 -0
  98. package/packages/extensions/intentions/dist/ui/assets/index-CQz4aceM.js +67 -0
  99. package/packages/extensions/{plan → intentions}/dist/ui/index.html +3 -3
  100. package/packages/extensions/intentions/skills/intention-review/SKILL.md +38 -0
  101. package/packages/extensions/intentions/skills/intentions/SKILL.md +49 -0
  102. package/packages/extensions/intentions/skills/intentions/scripts/intentions-hook.sh +45 -0
  103. package/packages/extensions/pages/skills/commons-gardening/SKILL.md +50 -0
  104. package/packages/extensions/pages/skills/pages/SKILL.md +18 -13
  105. package/templates/_base/.init/.config/prompts.json +3 -3
  106. package/templates/{pi → _base}/.init/.config/routes.json +1 -0
  107. package/templates/_base/.init/memory/dreams/.gitkeep +0 -0
  108. package/templates/_base/home/.config/routes.json +1 -1
  109. package/templates/_base/home/VOLUTE.md +1 -1
  110. package/templates/_base/src/lib/codex-session-seed.ts +463 -0
  111. package/templates/_base/src/lib/context-breakdown.ts +11 -3
  112. package/templates/_base/src/lib/daemon-client.ts +35 -0
  113. package/templates/_base/src/lib/pi-session-seed.ts +434 -0
  114. package/templates/_base/src/lib/routing.ts +2 -2
  115. package/templates/_base/src/lib/seed-note.ts +78 -0
  116. package/templates/_base/src/lib/session-seed.ts +375 -0
  117. package/templates/_base/src/lib/startup.ts +112 -6
  118. package/templates/claude/.init/CLAUDE.md +7 -3
  119. package/templates/claude/src/agent.ts +295 -101
  120. package/templates/claude/src/lib/hooks/pre-compact.ts +30 -5
  121. package/templates/claude/src/lib/message-channel.ts +43 -15
  122. package/templates/claude/src/lib/recover.ts +69 -0
  123. package/templates/claude/src/lib/stream-consumer.ts +33 -2
  124. package/templates/claude/src/server.ts +2 -1
  125. package/templates/claude/volute-template.json +1 -1
  126. package/templates/codex/.init/AGENTS.md +7 -3
  127. package/templates/codex/src/agent.ts +256 -39
  128. package/templates/codex/src/server.ts +2 -1
  129. package/templates/codex/volute-template.json +1 -1
  130. package/templates/pi/.init/MINDS.md +7 -3
  131. package/templates/pi/src/agent.ts +269 -43
  132. package/templates/pi/src/lib/event-handler.ts +11 -0
  133. package/templates/pi/src/lib/mechanics-doc.ts +30 -0
  134. package/templates/pi/src/server.ts +5 -1
  135. package/templates/pi/volute-template.json +1 -1
  136. package/dist/channels-FSAHOQWI.js +0 -90
  137. package/dist/skills/plan-coordinator/SKILL.md +0 -60
  138. package/dist/src-UDMJZ6MH.js +0 -426
  139. package/dist/web-assets/assets/index-DhZrzddW.css +0 -1
  140. package/packages/extensions/plan/dist/ui/assets/index-B8s8wxyt.js +0 -67
  141. package/packages/extensions/plan/dist/ui/assets/index-jIkrt-vI.css +0 -1
  142. package/packages/extensions/plan/skills/plan/SKILL.md +0 -43
  143. package/packages/extensions/plan/skills/plan/scripts/plan-hook.sh +0 -37
  144. package/templates/claude/.init/.config/routes.json +0 -11
  145. package/templates/codex/.init/.config/routes.json +0 -11
  146. package/dist/{env-IBW2QI6K.js → env-7AZFBPWX.js} +5 -5
  147. package/dist/{login-RJJTCVR3.js → login-4NUS72Z5.js} +3 -3
  148. package/dist/{login-SB5OENMV.js → login-57MMYILL.js} +4 -4
  149. package/dist/{register-OX4HRSGC.js → register-WNCSOTAU.js} +3 -3
  150. package/dist/{restart-AGEGNSYQ.js → restart-B4GNMVF2.js} +4 -4
  151. package/dist/{skill-B5OJEQUA.js → skill-QYTBUDNQ.js} +4 -4
  152. package/dist/{start-MUMNFR52.js → start-BDHJVOVX.js} +3 -3
  153. package/dist/{stop-EKERNYTX.js → stop-45QYSGYS.js} +4 -4
package/dist/daemon.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  readBackupState,
7
7
  resticVersion,
8
8
  runBackup
9
- } from "./chunk-EDR47UE2.js";
9
+ } from "./chunk-OYF25B62.js";
10
10
  import {
11
11
  acceptPending,
12
12
  formatFileSize,
@@ -14,23 +14,43 @@ import {
14
14
  rejectPending,
15
15
  stageFile,
16
16
  validateFilePath
17
- } from "./chunk-DMK5ASKV.js";
17
+ } from "./chunk-F6D766VQ.js";
18
+ import {
19
+ getMemoryStatus
20
+ } from "./chunk-5RBEEAKW.js";
18
21
  import {
19
22
  findOpenClawSession,
20
23
  importOpenClawConnectors,
21
24
  importPiSession,
22
25
  parseNameFromIdentity
23
26
  } from "./chunk-I7U5EEZC.js";
27
+ import {
28
+ checkForUpdate,
29
+ checkForUpdateCached,
30
+ getCurrentVersion
31
+ } from "./chunk-LAUCO2HC.js";
24
32
  import {
25
33
  SPIRIT_NOTICE_PREFIX,
26
34
  ensureSpiritAvailable
27
- } from "./chunk-CW7BQKF6.js";
35
+ } from "./chunk-LKUGUQUW.js";
28
36
  import {
29
- isHomeOnlyArchive
30
- } from "./chunk-FLIEXG2T.js";
31
- import {
32
- evaluateSeedChecklist
33
- } from "./chunk-JHKBALS2.js";
37
+ TEMPLATE_BRANCH,
38
+ UpgradeInProgressError,
39
+ VariantMergeError,
40
+ abortUpgrade,
41
+ configureGitIdentity,
42
+ continueUpgrade,
43
+ findUnresolvedHomeFiles,
44
+ formatUnresolvedHomeFilesMessage,
45
+ getUpgradeBlocked,
46
+ mergeVariantExcludingMemory,
47
+ npmInstallAsMind,
48
+ npmInstallNeeded,
49
+ runUpgrade,
50
+ upgradeDiff,
51
+ upgradeInProgress,
52
+ validateBranchName
53
+ } from "./chunk-OT5SXF7L.js";
34
54
  import {
35
55
  isTemplateStale
36
56
  } from "./chunk-CV6BNGFK.js";
@@ -39,19 +59,20 @@ import {
39
59
  } from "./chunk-HTRSSOHY.js";
40
60
  import {
41
61
  cleanupVariant
42
- } from "./chunk-ITK6HV47.js";
62
+ } from "./chunk-OGPVJ53N.js";
43
63
  import {
44
- checkForUpdate,
45
- checkForUpdateCached,
46
- getCurrentVersion
47
- } from "./chunk-LAUCO2HC.js";
64
+ isHomeOnlyArchive
65
+ } from "./chunk-FLIEXG2T.js";
66
+ import {
67
+ evaluateSeedChecklist
68
+ } from "./chunk-CLCHOXLR.js";
48
69
  import {
49
70
  PLATFORMS,
50
71
  echoTextToChannel,
51
72
  fixModelEscapes,
52
73
  getPlatformDriver,
53
74
  routeOutboundBridge
54
- } from "./chunk-XRWUQPCA.js";
75
+ } from "./chunk-ATZF2RNJ.js";
55
76
  import {
56
77
  getBridgeConfig,
57
78
  readBridgesConfig,
@@ -94,6 +115,7 @@ import {
94
115
  loadAllExtensions,
95
116
  notifyExtensionsDaemonStart,
96
117
  notifyExtensionsDaemonStop,
118
+ notifyExtensionsSpiritReady,
97
119
  readSystemsConfig,
98
120
  recordOutbound,
99
121
  requireAdmin,
@@ -104,12 +126,12 @@ import {
104
126
  startMindFull,
105
127
  stopMindFull,
106
128
  uninstallNpmExtension,
129
+ willGateMessage,
107
130
  writeSystemsConfig
108
- } from "./chunk-ZEZQNP2G.js";
109
- import "./chunk-K2F237KS.js";
131
+ } from "./chunk-LGXVGIWN.js";
132
+ import "./chunk-RGHF3OK5.js";
110
133
  import {
111
134
  applyInitFiles,
112
- applyTemplateHomeFiles,
113
135
  composeTemplate,
114
136
  copyTemplateToDir,
115
137
  findTemplatesRoot,
@@ -119,8 +141,12 @@ import {
119
141
  import {
120
142
  getScheduler,
121
143
  initScheduler
122
- } from "./chunk-SW2U6HRB.js";
144
+ } from "./chunk-444RR3YF.js";
145
+ import {
146
+ recordDeliveryFailure
147
+ } from "./chunk-3LHUXY3E.js";
123
148
  import {
149
+ UnknownChannelError,
124
150
  checkStaleSend,
125
151
  formatHoldNotice,
126
152
  getDeliveryManager,
@@ -128,7 +154,7 @@ import {
128
154
  initDeliveryManager,
129
155
  isConversationId,
130
156
  publishTypingForChannels
131
- } from "./chunk-RU74YG46.js";
157
+ } from "./chunk-VDUCLVZI.js";
132
158
  import {
133
159
  MindStartupError,
134
160
  RestartTracker,
@@ -138,10 +164,10 @@ import {
138
164
  initMindManager,
139
165
  syncProviderToMinds,
140
166
  tryGetMindManager
141
- } from "./chunk-724DVYHU.js";
167
+ } from "./chunk-WHL6PKYP.js";
142
168
  import {
143
169
  extractTextContent
144
- } from "./chunk-UDZLPR34.js";
170
+ } from "./chunk-YSRYCS3N.js";
145
171
  import {
146
172
  SYSTEM_MIND,
147
173
  assignSession,
@@ -155,13 +181,15 @@ import {
155
181
  initSummarizer,
156
182
  isoWeekKeyForDateStr,
157
183
  markErrored,
184
+ parseDbTimestamp,
158
185
  parseUtcDateTime,
159
186
  summarizeOrphanedTurns,
160
187
  summarizeTurn,
188
+ supersedeTurnSummary,
161
189
  takeErrored,
162
190
  trackToolUse,
163
191
  utcDateTimeStr
164
- } from "./chunk-YVR6EHWL.js";
192
+ } from "./chunk-56IT2ZXF.js";
165
193
  import {
166
194
  SEED_SKILLS,
167
195
  STANDARD_SKILLS,
@@ -175,7 +203,6 @@ import {
175
203
  listFilesRecursive,
176
204
  listMindSkills,
177
205
  listSharedSkills,
178
- migrateSkillsToTemplate,
179
206
  mindSkillsDir,
180
207
  publishSkill,
181
208
  removeSharedSkill,
@@ -183,7 +210,7 @@ import {
183
210
  syncBuiltinSkills,
184
211
  uninstallSkill,
185
212
  updateSkill
186
- } from "./chunk-RPPTGPYF.js";
213
+ } from "./chunk-PPSN2MLC.js";
187
214
  import {
188
215
  approveUser,
189
216
  changePassword,
@@ -207,6 +234,7 @@ import {
207
234
  withSenderDisplayNames
208
235
  } from "./chunk-7LT3YZUR.js";
209
236
  import {
237
+ MIND_LEVEL_THREAD,
210
238
  captureReflection,
211
239
  cleanExpiredEvents,
212
240
  clearDeliveredEvents,
@@ -219,7 +247,7 @@ import {
219
247
  listEvents,
220
248
  parseMeta,
221
249
  recordNotice
222
- } from "./chunk-AXFLY5GU.js";
250
+ } from "./chunk-HKGYZVWN.js";
223
251
  import {
224
252
  publish as publish2,
225
253
  subscribe as subscribe2,
@@ -232,7 +260,7 @@ import {
232
260
  getPrompt,
233
261
  getPromptIfCustom,
234
262
  substitute
235
- } from "./chunk-FXWBFDQR.js";
263
+ } from "./chunk-OAYY3WF3.js";
236
264
  import {
237
265
  isSandboxEnabled,
238
266
  wrapForSandbox
@@ -295,7 +323,7 @@ import {
295
323
  readVoluteConfig,
296
324
  writeVoluteConfig
297
325
  } from "./chunk-WCFSBH33.js";
298
- import "./chunk-6NLW72HP.js";
326
+ import "./chunk-CKPAVQNK.js";
299
327
  import {
300
328
  chownMindDir,
301
329
  chownMindFile,
@@ -307,7 +335,7 @@ import {
307
335
  isIsolationEnabled,
308
336
  resolveVoluteBin,
309
337
  wrapForIsolation
310
- } from "./chunk-M77B73VV.js";
338
+ } from "./chunk-OIOW3FPT.js";
311
339
  import {
312
340
  loadMergedEnv,
313
341
  mindEnvPath,
@@ -372,7 +400,6 @@ import {
372
400
  resolveMindDir,
373
401
  setMindRunning,
374
402
  setMindStage,
375
- setMindTemplate,
376
403
  setMindTemplateHash,
377
404
  stateDir,
378
405
  validateMindName,
@@ -397,9 +424,9 @@ import "./chunk-K3NQKI34.js";
397
424
 
398
425
  // packages/daemon/src/daemon.ts
399
426
  import { randomBytes as randomBytes3 } from "crypto";
400
- import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
427
+ import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
401
428
  import { homedir as homedir3 } from "os";
402
- import { resolve as resolve20 } from "path";
429
+ import { resolve as resolve18 } from "path";
403
430
  import { format } from "util";
404
431
 
405
432
  // packages/daemon/src/lib/daemon/backup-manager.ts
@@ -510,7 +537,7 @@ var BackupManager = class {
510
537
  } catch (pubErr) {
511
538
  blog.error("failed to publish backup-failure activity", logger_default.errorData(pubErr));
512
539
  }
513
- const { deliverEvent: deliverEvent2 } = await import("./system-events-V43HDA3T.js");
540
+ const { deliverEvent: deliverEvent2 } = await import("./system-events-Q7IVZ5T4.js");
514
541
  await deliverEvent2(getSpiritName(), {
515
542
  type: "notice",
516
543
  meta: { subtype: "backup" },
@@ -734,8 +761,8 @@ var BridgeManager = class {
734
761
  if (!tracked) return;
735
762
  this.stopping.add(platform);
736
763
  this.bridges.delete(platform);
737
- await new Promise((resolve21) => {
738
- tracked.child.on("exit", () => resolve21());
764
+ await new Promise((resolve19) => {
765
+ tracked.child.on("exit", () => resolve19());
739
766
  try {
740
767
  if (tracked.child.pid) {
741
768
  process.kill(-tracked.child.pid, "SIGTERM");
@@ -746,7 +773,7 @@ var BridgeManager = class {
746
773
  if (err instanceof Error && err.code !== "ESRCH") {
747
774
  blog2.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
748
775
  }
749
- resolve21();
776
+ resolve19();
750
777
  }
751
778
  setTimeout(() => {
752
779
  try {
@@ -757,7 +784,7 @@ var BridgeManager = class {
757
784
  }
758
785
  } catch {
759
786
  }
760
- resolve21();
787
+ resolve19();
761
788
  }, 5e3);
762
789
  });
763
790
  this.stopping.delete(platform);
@@ -1434,10 +1461,10 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1434
1461
  stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
1435
1462
  });
1436
1463
  });
1437
- await new Promise((resolve21) => {
1464
+ await new Promise((resolve19) => {
1438
1465
  stream.onAbort(() => {
1439
1466
  unsubscribe();
1440
- resolve21();
1467
+ resolve19();
1441
1468
  });
1442
1469
  });
1443
1470
  });
@@ -1462,7 +1489,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1462
1489
  config2.name = name.trim() || void 0;
1463
1490
  writeGlobalConfig(config2);
1464
1491
  if (config2.name !== previousName) {
1465
- const { notifySpiritSystemChange } = await import("./spirit-4ECYIVGE.js");
1492
+ const { notifySpiritSystemChange } = await import("./spirit-Q5VGFWUI.js");
1466
1493
  await notifySpiritSystemChange();
1467
1494
  }
1468
1495
  return c.json({ name: config2.name ?? null });
@@ -1821,8 +1848,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1821
1848
  createdAt: Date.now()
1822
1849
  };
1823
1850
  oauthFlows.set(flowId, flow);
1824
- const promptPromise = needsManualCode ? new Promise((resolve21) => {
1825
- flow.resolveCode = resolve21;
1851
+ const promptPromise = needsManualCode ? new Promise((resolve19) => {
1852
+ flow.resolveCode = resolve19;
1826
1853
  }) : void 0;
1827
1854
  oauthProvider.login({
1828
1855
  onAuth: (info) => {
@@ -1861,7 +1888,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1861
1888
  existing.error = err instanceof Error ? err.message : String(err);
1862
1889
  }
1863
1890
  });
1864
- await new Promise((resolve21) => setTimeout(resolve21, 2e3));
1891
+ await new Promise((resolve19) => setTimeout(resolve19, 2e3));
1865
1892
  const state = oauthFlows.get(flowId);
1866
1893
  return c.json({
1867
1894
  flowId,
@@ -2122,8 +2149,8 @@ var app2 = new Hono2().get("/events", async (c) => {
2122
2149
  });
2123
2150
  }, 15e3);
2124
2151
  cleanups.push(() => clearInterval(keepAlive));
2125
- await new Promise((resolve21) => {
2126
- stream.onAbort(() => resolve21());
2152
+ await new Promise((resolve19) => {
2153
+ stream.onAbort(() => resolve19());
2127
2154
  });
2128
2155
  } finally {
2129
2156
  for (const cleanup of cleanups) {
@@ -2674,6 +2701,21 @@ async function fanOutToMinds(opts) {
2674
2701
  const manager = getMindManager();
2675
2702
  const sm = getSleepManagerIfReady();
2676
2703
  const registeredMinds = new Set((await readAllMinds()).map((m) => m.name));
2704
+ const senderIsMind = registeredMinds.has(opts.senderName);
2705
+ let senderCtx;
2706
+ const reportFailure = (reason) => {
2707
+ if (!senderIsMind) return;
2708
+ senderCtx ??= getBaseName(opts.senderName).then((base) => ({
2709
+ base,
2710
+ channel: buildVoluteSlug({
2711
+ participants,
2712
+ mindUsername: base,
2713
+ conversationId: opts.conversationId,
2714
+ ...opts.slugExtra
2715
+ })
2716
+ }));
2717
+ senderCtx.then(({ base, channel }) => recordDeliveryFailure({ mind: base, channel, reason })).catch((err) => logger_default.warn("fan-out: failed to report send failure", logger_default.errorData(err)));
2718
+ };
2677
2719
  const targetMinds = mindParticipants.map((ap) => {
2678
2720
  const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
2679
2721
  if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
@@ -2682,21 +2724,46 @@ async function fanOutToMinds(opts) {
2682
2724
  logger_default.warn(
2683
2725
  `fan-out: skipping ${ap.username} (not running) for conversation ${opts.conversationId}`
2684
2726
  );
2727
+ reportFailure(`${ap.username} is not running`);
2728
+ } else if (ap.userType === "mind") {
2729
+ logger_default.info(
2730
+ `fan-out: skipping ${ap.username} (no registry row \u2014 external mind or stale participant) for conversation ${opts.conversationId}`
2731
+ );
2685
2732
  }
2686
2733
  if (ap.userType === "system") {
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)));
2734
+ import("./spirit-availability-G5FMWKRH.js").then(({ ensureSpiritAvailable: ensureSpiritAvailable2 }) => ensureSpiritAvailable2()).catch((err) => logger_default.warn("fan-out: on-demand spirit start failed", logger_default.errorData(err)));
2688
2735
  }
2689
2736
  }
2690
2737
  return null;
2691
2738
  }).filter((n) => n !== null && n !== opts.senderName);
2692
- for (const mindName of targetMinds) {
2693
- const target = opts.targetName ? opts.targetName(mindName) : mindName;
2694
- const channel = buildVoluteSlug({
2739
+ const targets = targetMinds.map((mindName) => ({
2740
+ mindName,
2741
+ target: opts.targetName ? opts.targetName(mindName) : mindName,
2742
+ channel: buildVoluteSlug({
2695
2743
  participants,
2696
2744
  mindUsername: mindName,
2697
2745
  conversationId: opts.conversationId,
2698
2746
  ...opts.slugExtra
2699
- });
2747
+ })
2748
+ }));
2749
+ const gatedRecipients = (await Promise.all(
2750
+ targets.map(async ({ mindName, target, channel }) => {
2751
+ if (sm?.isSleeping(mindName)) return null;
2752
+ try {
2753
+ const gated = await willGateMessage(target, {
2754
+ channel,
2755
+ sender: opts.senderName,
2756
+ isDM,
2757
+ participantCount: participants.length
2758
+ });
2759
+ return gated ? mindName : null;
2760
+ } catch (err) {
2761
+ logger_default.warn(`fan-out: will-gate check failed for ${target}`, logger_default.errorData(err));
2762
+ return null;
2763
+ }
2764
+ })
2765
+ )).filter((n) => n !== null);
2766
+ for (const { mindName, target, channel } of targets) {
2700
2767
  const typingMap = getTypingMap();
2701
2768
  const currentlyTyping = typingMap.get(channel).filter((name) => participantNames.includes(name));
2702
2769
  deliverMessage(target, {
@@ -2708,10 +2775,20 @@ async function fanOutToMinds(opts) {
2708
2775
  participantCount: participants.length,
2709
2776
  isDM,
2710
2777
  ...currentlyTyping.length > 0 ? { typing: currentlyTyping } : {}
2711
- }).catch((err) => {
2712
- logger_default.warn(`fan-out delivery failed for ${target}`, logger_default.errorData(err));
2713
- });
2778
+ }).then(
2779
+ (ok) => {
2780
+ if (!ok) {
2781
+ logger_default.warn(`fan-out delivery failed for ${target} (message not delivered)`);
2782
+ reportFailure(`the delivery to ${mindName} failed`);
2783
+ }
2784
+ },
2785
+ (err) => {
2786
+ logger_default.warn(`fan-out delivery failed for ${target}`, logger_default.errorData(err));
2787
+ reportFailure(`the delivery to ${mindName} failed`);
2788
+ }
2789
+ );
2714
2790
  }
2791
+ return { gatedRecipients };
2715
2792
  }
2716
2793
 
2717
2794
  // packages/daemon/src/web/api/bridges.ts
@@ -3069,12 +3146,18 @@ import { resolve as resolve4 } from "path";
3069
3146
  import { Hono as Hono10 } from "hono";
3070
3147
  async function notifyMind(mindName, message) {
3071
3148
  const entry = await findMind(mindName);
3072
- if (!entry) return;
3073
- try {
3074
- const { deliverEvent: deliverEvent2 } = await import("./system-events-V43HDA3T.js");
3075
- await deliverEvent2(mindName, { type: "file-share", body: message });
3149
+ if (!entry) return false;
3150
+ try {
3151
+ const { deliverEvent: deliverEvent2 } = await import("./system-events-Q7IVZ5T4.js");
3152
+ const result = await deliverEvent2(mindName, { type: "file-share", body: message });
3153
+ if (result.delivered) return true;
3154
+ if (result.id == null) return false;
3155
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-KHI65FEX.js");
3156
+ const { getBaseName: getBaseName2 } = await import("./registry-TRC2HJXR.js");
3157
+ return getSleepManagerIfReady2()?.isSleeping(await getBaseName2(mindName)) ?? false;
3076
3158
  } catch (err) {
3077
3159
  logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
3160
+ return false;
3078
3161
  }
3079
3162
  }
3080
3163
  var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
@@ -3115,11 +3198,11 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
3115
3198
  const filename = body.filePath;
3116
3199
  const sizeStr = formatFileSize(content.length);
3117
3200
  const { id } = stageFile(body.targetMind, senderName, filename, content, body.filePath);
3118
- await notifyMind(
3201
+ const notified = await notifyMind(
3119
3202
  body.targetMind,
3120
3203
  `[file] ${senderName} sent ${filename} (${sizeStr}) \u2014 run: volute chat accept ${id}`
3121
3204
  );
3122
- return c.json({ status: "pending", id }, 200);
3205
+ return c.json({ status: "pending", id, notified }, 200);
3123
3206
  }).get("/:name/files/pending", requireSelf(), async (c) => {
3124
3207
  const name = c.req.param("name");
3125
3208
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
@@ -3144,8 +3227,8 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
3144
3227
  }
3145
3228
  return c.json({ error: `Failed to accept file: ${message}` }, 500);
3146
3229
  }
3147
- await notifyMind(result.sender, `[file] ${name} accepted ${result.filename}`);
3148
- return c.json({ ok: true, destPath: result.destPath });
3230
+ const notified = await notifyMind(result.sender, `[file] ${name} accepted ${result.filename}`);
3231
+ return c.json({ ok: true, destPath: result.destPath, notified });
3149
3232
  }).post("/:name/files/reject", requireSelf(), async (c) => {
3150
3233
  const name = c.req.param("name");
3151
3234
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
@@ -3161,8 +3244,8 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
3161
3244
  }
3162
3245
  return c.json({ error: `Failed to reject file: ${message}` }, 500);
3163
3246
  }
3164
- await notifyMind(result.sender, `[file] ${name} rejected ${result.filename}`);
3165
- return c.json({ ok: true });
3247
+ const notified = await notifyMind(result.sender, `[file] ${name} rejected ${result.filename}`);
3248
+ return c.json({ ok: true, notified });
3166
3249
  }).post("/:name/files/stage", requireSelf(), async (c) => {
3167
3250
  const receiverName = c.req.param("name");
3168
3251
  const receiverEntry = await findMind(receiverName);
@@ -3185,11 +3268,11 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
3185
3268
  }
3186
3269
  const sizeStr = formatFileSize(content.length);
3187
3270
  const { id } = stageFile(receiverName, body.sender, body.filename, content, body.filename);
3188
- await notifyMind(
3271
+ const notified = await notifyMind(
3189
3272
  receiverName,
3190
3273
  `[file] ${body.sender} sent ${body.filename} (${sizeStr}) \u2014 run: volute chat accept ${id}`
3191
3274
  );
3192
- return c.json({ status: "pending", id }, 200);
3275
+ return c.json({ status: "pending", id, notified }, 200);
3193
3276
  });
3194
3277
  var file_sharing_default = app8;
3195
3278
 
@@ -4042,9 +4125,9 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
4042
4125
  stream.onAbort(() => {
4043
4126
  tail.kill();
4044
4127
  });
4045
- await new Promise((resolve21) => {
4046
- tail.on("exit", resolve21);
4047
- stream.onAbort(resolve21);
4128
+ await new Promise((resolve19) => {
4129
+ tail.on("exit", resolve19);
4130
+ stream.onAbort(resolve19);
4048
4131
  });
4049
4132
  });
4050
4133
  }).get("/:name/logs/tail", requireSelf(), async (c) => {
@@ -4062,8 +4145,8 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
4062
4145
  tail.stdout.on("data", (data) => {
4063
4146
  output += data.toString();
4064
4147
  });
4065
- await new Promise((resolve21) => {
4066
- tail.on("exit", resolve21);
4148
+ await new Promise((resolve19) => {
4149
+ tail.on("exit", resolve19);
4067
4150
  });
4068
4151
  return c.text(output);
4069
4152
  });
@@ -4156,14 +4239,14 @@ var mind_skills_default = app12;
4156
4239
  // packages/daemon/src/web/api/minds.ts
4157
4240
  import {
4158
4241
  cpSync,
4159
- existsSync as existsSync8,
4242
+ existsSync as existsSync7,
4160
4243
  mkdirSync as mkdirSync6,
4161
4244
  readdirSync as readdirSync2,
4162
4245
  readFileSync as readFileSync9,
4163
4246
  rmSync as rmSync3,
4164
4247
  writeFileSync as writeFileSync8
4165
4248
  } from "fs";
4166
- import { relative, resolve as resolve13 } from "path";
4249
+ import { relative, resolve as resolve11 } from "path";
4167
4250
  import { zValidator as zValidator6 } from "@hono/zod-validator";
4168
4251
  import { and as and5, desc as desc3, eq as eq5, sql as sql3 } from "drizzle-orm";
4169
4252
  import { Hono as Hono16 } from "hono";
@@ -4606,49 +4689,6 @@ async function commitSrcChanges(dir, mindName) {
4606
4689
  glog.info(`committed known-good src changes (${dir})`);
4607
4690
  }
4608
4691
 
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
-
4652
4692
  // packages/daemon/src/lib/mind/thinking-config.ts
4653
4693
  var CLAUDE_EFFORT = {
4654
4694
  minimal: "low",
@@ -4690,150 +4730,6 @@ function deriveThinkingLevel(config2) {
4690
4730
  return null;
4691
4731
  }
4692
4732
 
4693
- // packages/daemon/src/lib/mind/variants.ts
4694
- import { statSync as statSync3 } from "fs";
4695
- import { resolve as resolve11 } from "path";
4696
- var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
4697
- function validateBranchName(branch) {
4698
- if (!SAFE_BRANCH_RE.test(branch)) {
4699
- return `Invalid branch name: ${branch}. Only alphanumeric, '.', '_', '-', '/' allowed.`;
4700
- }
4701
- if (branch.includes("..")) {
4702
- return `Invalid branch name: ${branch}. '..' not allowed.`;
4703
- }
4704
- return null;
4705
- }
4706
- var MERGE_EXCLUDED_PATHS = ["home/MEMORY.md", "home/memory/journal/"];
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
- }
4757
- async function mergeVariantExcludingMemory(projectRoot, variantBranch) {
4758
- const opts = { cwd: projectRoot };
4759
- let memoryDelta = "";
4760
- try {
4761
- const base = (await gitExec(["merge-base", "HEAD", variantBranch], opts)).trim();
4762
- memoryDelta = (await gitExec(["diff", `${base}..${variantBranch}`, "--", ...MERGE_EXCLUDED_PATHS], opts)).trim();
4763
- } catch (err) {
4764
- logger_default.error(
4765
- `variant join: failed to compute memory delta for ${variantBranch}`,
4766
- logger_default.errorData(err)
4767
- );
4768
- memoryDelta = `(journal delta unavailable \u2014 ${err instanceof Error ? err.message : String(err)})`;
4769
- }
4770
- if (memoryDelta.length > MAX_DELTA_CHARS) {
4771
- memoryDelta = `${memoryDelta.slice(0, MAX_DELTA_CHARS)}
4772
- \u2026 (delta truncated)`;
4773
- }
4774
- try {
4775
- await gitExec(["merge", "--no-ff", "--no-commit", variantBranch], opts).catch(() => {
4776
- });
4777
- await gitExec(["reset", "-q", "HEAD", "--", ...MERGE_EXCLUDED_PATHS], opts);
4778
- for (const path of MERGE_EXCLUDED_PATHS) {
4779
- await gitExec(["checkout", "--", path], opts).catch(() => {
4780
- });
4781
- }
4782
- await gitExec(["clean", "-fdq", "--", ...MERGE_EXCLUDED_PATHS], opts);
4783
- await assertExcludedPathsRestored(opts);
4784
- if (await mergeInProgress(opts)) {
4785
- await gitExec(["commit", "--no-edit"], opts);
4786
- }
4787
- } catch (err) {
4788
- const conflicts = await listConflicts(opts);
4789
- const abortFailed = await gitExec(["merge", "--abort"], opts).then(
4790
- () => false,
4791
- () => true
4792
- );
4793
- throw new VariantMergeError(
4794
- `variant join aborted: ${err instanceof Error ? err.message : String(err)}`,
4795
- conflicts,
4796
- abortFailed
4797
- );
4798
- }
4799
- return memoryDelta;
4800
- }
4801
- var VariantMergeError = class extends Error {
4802
- constructor(message, conflicts, abortFailed = false) {
4803
- super(message);
4804
- this.conflicts = conflicts;
4805
- this.abortFailed = abortFailed;
4806
- this.name = "VariantMergeError";
4807
- }
4808
- };
4809
- async function mergeInProgress(opts) {
4810
- return gitExec(["rev-parse", "-q", "--verify", "MERGE_HEAD"], opts).then(
4811
- () => true,
4812
- () => false
4813
- );
4814
- }
4815
- async function listConflicts(opts) {
4816
- try {
4817
- const out = (await gitExec(["diff", "--name-only", "--diff-filter=U"], opts)).trim();
4818
- return out ? out.split("\n") : [];
4819
- } catch {
4820
- return [];
4821
- }
4822
- }
4823
- async function assertExcludedPathsRestored(opts) {
4824
- await gitExec(["diff", "--cached", "--quiet", "HEAD", "--", ...MERGE_EXCLUDED_PATHS], opts);
4825
- await gitExec(["diff", "--quiet", "HEAD", "--", ...MERGE_EXCLUDED_PATHS], opts);
4826
- const untracked = (await gitExec(
4827
- ["ls-files", "--others", "--exclude-standard", "--", ...MERGE_EXCLUDED_PATHS],
4828
- opts
4829
- )).trim();
4830
- if (untracked) {
4831
- throw new Error(
4832
- `untracked memory files remain after restore: ${untracked.replace(/\n/g, ", ")}`
4833
- );
4834
- }
4835
- }
4836
-
4837
4733
  // packages/daemon/src/lib/services/imagegen-jobs.ts
4838
4734
  import { mkdir, writeFile } from "fs/promises";
4839
4735
  import { dirname as dirname2 } from "path";
@@ -4956,7 +4852,7 @@ async function waitForImagegenJob(mind, id, timeoutMs) {
4956
4852
  import { randomUUID } from "crypto";
4957
4853
  import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
4958
4854
  import { homedir } from "os";
4959
- import { resolve as resolve12 } from "path";
4855
+ import { resolve as resolve10 } from "path";
4960
4856
  function convertSession(opts) {
4961
4857
  const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
4962
4858
  const sessionId = randomUUID();
@@ -5072,9 +4968,9 @@ function convertSession(opts) {
5072
4968
  }
5073
4969
  }
5074
4970
  const projectId = opts.projectDir.replace(/\//g, "-");
5075
- const sdkDir = resolve12(homedir(), ".claude", "projects", projectId);
4971
+ const sdkDir = resolve10(homedir(), ".claude", "projects", projectId);
5076
4972
  mkdirSync5(sdkDir, { recursive: true });
5077
- const sdkPath = resolve12(sdkDir, `${sessionId}.jsonl`);
4973
+ const sdkPath = resolve10(sdkDir, `${sessionId}.jsonl`);
5078
4974
  writeFileSync7(sdkPath, `${sdkEvents.join("\n")}
5079
4975
  `);
5080
4976
  console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
@@ -5126,12 +5022,6 @@ function convertAssistantContent(content) {
5126
5022
  return result;
5127
5023
  }
5128
5024
 
5129
- // packages/daemon/src/lib/util/time.ts
5130
- function parseDbTimestamp(ts) {
5131
- if (/[zZ]$|[+-]\d\d:?\d\d$/.test(ts)) return new Date(ts);
5132
- return /* @__PURE__ */ new Date(`${ts.replace(" ", "T")}Z`);
5133
- }
5134
-
5135
5025
  // packages/daemon/src/web/api/minds.ts
5136
5026
  var _lastActiveCache = { map: /* @__PURE__ */ new Map(), ts: 0 };
5137
5027
  var _LAST_ACTIVE_TTL = 6e4;
@@ -5141,7 +5031,7 @@ async function getMindStatus(name, port, registryRunning, seed) {
5141
5031
  let status = "stopped";
5142
5032
  let wakeAt = null;
5143
5033
  try {
5144
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
5034
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-KHI65FEX.js");
5145
5035
  const sleepManager = getSleepManagerIfReady2();
5146
5036
  if (sleepManager?.isSleeping(name)) {
5147
5037
  status = "sleeping";
@@ -5167,10 +5057,12 @@ async function getMindStatus(name, port, registryRunning, seed) {
5167
5057
  }
5168
5058
  const lastError = await latestFailureEvent(name);
5169
5059
  const seedChecklist = seed?.stage === "seed" ? evaluateSeedChecklist(seed.dir ?? mindDir(name)) : void 0;
5060
+ const memory = getMemoryStatus(seed?.dir ?? mindDir(name));
5170
5061
  return {
5171
5062
  status,
5172
5063
  wakeAt,
5173
5064
  lastError,
5065
+ memory,
5174
5066
  channels: channels2,
5175
5067
  displayName: config2?.profile?.displayName,
5176
5068
  description: config2?.profile?.description,
@@ -5193,6 +5085,7 @@ function toPublicMind(entry, status, extras) {
5193
5085
  // Kind/reason/at only — `detail` can embed the raw error string (unknown
5194
5086
  // classifications), which is mind-private (served behind requireSelf).
5195
5087
  lastError: status.lastError ? { kind: status.lastError.kind, reason: status.lastError.reason, at: status.lastError.at } : null,
5088
+ memory: status.memory,
5196
5089
  channels: status.channels,
5197
5090
  displayName: status.displayName,
5198
5091
  description: status.description,
@@ -5202,13 +5095,6 @@ function toPublicMind(entry, status, extras) {
5202
5095
  lastActiveAt: extras.lastActiveAt ?? null
5203
5096
  };
5204
5097
  }
5205
- var TEMPLATE_BRANCH = "volute/template";
5206
- async function configureGitIdentity(mindName, opts) {
5207
- const systemsConfig = readSystemsConfig();
5208
- const system = systemsConfig?.system ?? "local";
5209
- await gitExec(["config", "user.name", mindName], opts);
5210
- await gitExec(["config", "user.email", `${mindName}.${system}@volute.systems`], opts);
5211
- }
5212
5098
  async function initTemplateBranch(projectRoot, composedDir, manifest, mindName, env) {
5213
5099
  const templateFiles = listFiles(composedDir).filter((f) => !f.startsWith(".init/") && !f.startsWith(".init\\")).filter((f) => !f.startsWith("home/") && !f.startsWith("home\\") || f === "home/VOLUTE.md").map((f) => manifest.rename[f] ?? f);
5214
5100
  const opts = { cwd: projectRoot, mindName, env };
@@ -5219,164 +5105,9 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
5219
5105
  await gitExec(["add", "-A"], opts);
5220
5106
  await gitExec(["commit", "-m", "initial commit"], opts);
5221
5107
  }
5222
- async function updateTemplateBranch(projectRoot, template, mindName) {
5223
- const tempWorktree = resolve13(projectRoot, ".variants", "_template_update");
5224
- let branchExists = false;
5225
- try {
5226
- await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
5227
- branchExists = true;
5228
- } catch {
5229
- }
5230
- try {
5231
- await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
5232
- } catch {
5233
- }
5234
- if (existsSync8(tempWorktree)) {
5235
- rmSync3(tempWorktree, { recursive: true, force: true });
5236
- }
5237
- const templatesRoot = findTemplatesRoot();
5238
- const { composedDir, manifest } = composeTemplate(templatesRoot, template);
5239
- try {
5240
- if (branchExists) {
5241
- await gitExec(["worktree", "add", tempWorktree, TEMPLATE_BRANCH], {
5242
- cwd: projectRoot
5243
- });
5244
- } else {
5245
- await gitExec(["worktree", "add", "--detach", tempWorktree], { cwd: projectRoot });
5246
- await gitExec(["checkout", "--orphan", TEMPLATE_BRANCH], { cwd: tempWorktree });
5247
- await gitExec(["rm", "-rf", "--cached", "."], { cwd: tempWorktree });
5248
- await gitExec(["clean", "-fd"], { cwd: tempWorktree });
5249
- }
5250
- if (branchExists) {
5251
- await gitExec(["rm", "-rf", "."], { cwd: tempWorktree }).catch(() => {
5252
- });
5253
- }
5254
- copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
5255
- const initDir = resolve13(tempWorktree, ".init");
5256
- if (existsSync8(initDir)) {
5257
- rmSync3(initDir, { recursive: true, force: true });
5258
- }
5259
- const homeDir = resolve13(tempWorktree, "home");
5260
- if (existsSync8(homeDir)) {
5261
- for (const entry of readdirSync2(homeDir)) {
5262
- if (entry !== "VOLUTE.md") {
5263
- rmSync3(resolve13(homeDir, entry), { recursive: true, force: true });
5264
- }
5265
- }
5266
- }
5267
- await gitExec(["add", "-A"], { cwd: tempWorktree });
5268
- try {
5269
- await gitExec(["diff", "--cached", "--quiet"], { cwd: tempWorktree });
5270
- } catch {
5271
- await gitExec(["commit", "-m", "template update"], { cwd: tempWorktree });
5272
- }
5273
- } finally {
5274
- try {
5275
- await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
5276
- } catch {
5277
- }
5278
- if (existsSync8(tempWorktree)) {
5279
- rmSync3(tempWorktree, { recursive: true, force: true });
5280
- }
5281
- rmSync3(composedDir, { recursive: true, force: true });
5282
- }
5283
- }
5284
- async function mergeTemplateBranch(worktreeDir) {
5285
- try {
5286
- await gitExec(
5287
- ["merge", TEMPLATE_BRANCH, "--allow-unrelated-histories", "-m", "merge template update"],
5288
- { cwd: worktreeDir }
5289
- );
5290
- return false;
5291
- } catch (e) {
5292
- try {
5293
- const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
5294
- const hasConflictMarkers = status.split("\n").some((line) => line.startsWith("UU") || line.startsWith("AA"));
5295
- if (hasConflictMarkers) return true;
5296
- } catch {
5297
- }
5298
- throw e;
5299
- }
5300
- }
5301
- async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariantName, upgradeBranch, template, oldTemplate) {
5302
- const templateChanged = template !== oldTemplate;
5303
- const mainStatus = (await gitExec(["status", "--porcelain"], { cwd: dir })).trim();
5304
- if (mainStatus) {
5305
- await gitExec(["add", "-A"], { cwd: dir });
5306
- await gitExec(["commit", "-m", "Auto-commit before upgrade merge"], { cwd: dir });
5307
- }
5308
- const preMergeHead = (await gitExec(["rev-parse", "HEAD"], { cwd: dir })).trim();
5309
- await gitExec(["merge", upgradeBranch], { cwd: dir });
5310
- try {
5311
- await cleanupVariant(upgradeVariantName, mindName, dir, worktreeDir);
5312
- } catch (err) {
5313
- logger_default.warn(`failed to clean up upgrade worktree for ${mindName}`, logger_default.errorData(err));
5314
- }
5315
- try {
5316
- await gitExec(["branch", "-D", upgradeBranch], { cwd: dir });
5317
- } catch {
5318
- }
5319
- let switchWarning;
5320
- if (templateChanged) {
5321
- try {
5322
- applyTemplateHomeFiles(resolve13(dir, "home"), template);
5323
- const migratedSkills = migrateSkillsToTemplate(dir, oldTemplate, template);
5324
- await gitExec(["add", "home/"], { cwd: dir });
5325
- try {
5326
- await gitExec(["diff", "--cached", "--quiet"], { cwd: dir });
5327
- } catch {
5328
- await gitExec(["commit", "-m", `swap template-owned home files for ${template}`], {
5329
- cwd: dir
5330
- });
5331
- }
5332
- await chownMindDir(dir, mindName);
5333
- const skillNote = migratedSkills.length > 0 ? ` Migrated skills to the ${template} skills dir: ${migratedSkills.join(", ")}.` : "";
5334
- switchWarning = `Switched ${oldTemplate}\u2192${template}: config reset to ${template} defaults, mechanics doc replaced, conversation starts fresh (sessions aren't portable across runtimes).${skillNote}`;
5335
- } catch (err) {
5336
- logger_default.warn(`failed to swap template home files for ${mindName}`, logger_default.errorData(err));
5337
- return {
5338
- ok: true,
5339
- warning: `Upgrade merged but template switch ${oldTemplate}\u2192${template} failed: ${err instanceof Error ? err.message : String(err)}. The mind is still registered as ${oldTemplate}; re-run the switch or fix home/ manually.`
5340
- };
5341
- }
5342
- }
5343
- try {
5344
- await setMindTemplateHash(mindName, computeTemplateHash(template));
5345
- await setMindTemplate(mindName, template);
5346
- } catch (err) {
5347
- logger_default.warn(`failed to update template for ${mindName}`, logger_default.errorData(err));
5348
- }
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`);
5361
- }
5362
- const manager = getMindManager();
5363
- try {
5364
- if (manager.isRunning(mindName)) {
5365
- await manager.stopMind(mindName);
5366
- }
5367
- manager.setPendingContext(mindName, { type: "upgraded" });
5368
- await manager.startMind(mindName, { healthTimeoutMs: 12e4 });
5369
- } catch (e) {
5370
- return {
5371
- ok: true,
5372
- warning: `Upgrade merged but mind restart failed: ${e instanceof Error ? e.message : String(e)}`
5373
- };
5374
- }
5375
- return { ok: true, warning: switchWarning };
5376
- }
5377
5108
  async function importFromArchive(c, tempDir, nameOverride, manifest) {
5378
- const extractedMindDir = resolve13(tempDir, "mind");
5379
- if (!existsSync8(extractedMindDir)) {
5109
+ const extractedMindDir = resolve11(tempDir, "mind");
5110
+ if (!existsSync7(extractedMindDir)) {
5380
5111
  return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
5381
5112
  }
5382
5113
  if (!manifest?.includes || !manifest.name || !manifest.template) {
@@ -5394,7 +5125,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
5394
5125
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
5395
5126
  ensureVoluteHome();
5396
5127
  const dest = mindDir(name);
5397
- if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5128
+ if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5398
5129
  try {
5399
5130
  cpSync(extractedMindDir, dest, { recursive: true });
5400
5131
  if (!manifest.includes.identity) {
@@ -5402,9 +5133,9 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
5402
5133
  }
5403
5134
  const state = stateDir(name);
5404
5135
  mkdirSync6(state, { recursive: true });
5405
- const envJson = resolve13(tempDir, "state/env.json");
5406
- if (existsSync8(envJson)) {
5407
- cpSync(envJson, resolve13(state, "env.json"));
5136
+ const envJson = resolve11(tempDir, "state/env.json");
5137
+ if (existsSync7(envJson)) {
5138
+ cpSync(envJson, resolve11(state, "env.json"));
5408
5139
  }
5409
5140
  const port = await nextPort();
5410
5141
  await addMind(name, port, manifest.stage, manifest.template);
@@ -5413,30 +5144,30 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
5413
5144
  } catch (err) {
5414
5145
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
5415
5146
  }
5416
- const homeDir = resolve13(dest, "home");
5147
+ const homeDir = resolve11(dest, "home");
5417
5148
  ensureVoluteGroup();
5418
5149
  createMindUser(name, homeDir);
5419
5150
  await chownMindDir(dest, name);
5420
5151
  await npmInstallAsMind(dest, name);
5421
5152
  await importHistoryFromArchive(name, tempDir);
5422
5153
  importSessionsFromArchive(dest, tempDir);
5423
- if (!existsSync8(resolve13(dest, ".git"))) {
5154
+ if (!existsSync7(resolve11(dest, ".git"))) {
5424
5155
  try {
5425
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dest, "home") } : void 0;
5156
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
5426
5157
  await gitExec(["init"], { cwd: dest, mindName: name, env });
5427
5158
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
5428
5159
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
5429
5160
  await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
5430
5161
  } catch (err) {
5431
5162
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
5432
- rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
5163
+ rmSync3(resolve11(dest, ".git"), { recursive: true, force: true });
5433
5164
  }
5434
5165
  }
5435
5166
  await chownMindDir(dest, name);
5436
5167
  rmSync3(tempDir, { recursive: true, force: true });
5437
5168
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
5438
5169
  } catch (err) {
5439
- if (existsSync8(dest)) rmSync3(dest, { recursive: true, force: true });
5170
+ if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
5440
5171
  try {
5441
5172
  await removeMind(name);
5442
5173
  } catch (cleanupErr) {
@@ -5453,7 +5184,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5453
5184
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
5454
5185
  ensureVoluteHome();
5455
5186
  const dest = mindDir(name);
5456
- if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5187
+ if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5457
5188
  const templatesRoot = findTemplatesRoot();
5458
5189
  const { composedDir, manifest: templateManifest } = composeTemplate(
5459
5190
  templatesRoot,
@@ -5462,36 +5193,36 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5462
5193
  try {
5463
5194
  copyTemplateToDir(composedDir, dest, name, templateManifest);
5464
5195
  applyInitFiles(dest);
5465
- const extractedHome = resolve13(extractedMindDir, "home");
5466
- if (existsSync8(extractedHome)) {
5467
- cpSync(extractedHome, resolve13(dest, "home"), { recursive: true });
5196
+ const extractedHome = resolve11(extractedMindDir, "home");
5197
+ if (existsSync7(extractedHome)) {
5198
+ cpSync(extractedHome, resolve11(dest, "home"), { recursive: true });
5468
5199
  }
5469
- const extractedMindInternal = resolve13(extractedMindDir, ".mind");
5470
- if (existsSync8(extractedMindInternal)) {
5471
- cpSync(extractedMindInternal, resolve13(dest, ".mind"), { recursive: true });
5200
+ const extractedMindInternal = resolve11(extractedMindDir, ".mind");
5201
+ if (existsSync7(extractedMindInternal)) {
5202
+ cpSync(extractedMindInternal, resolve11(dest, ".mind"), { recursive: true });
5472
5203
  }
5473
- const identityDir = resolve13(dest, ".mind/identity");
5204
+ const identityDir = resolve11(dest, ".mind/identity");
5474
5205
  let publicKeyPem;
5475
- if (!manifest.includes.identity || !existsSync8(resolve13(identityDir, "private.pem"))) {
5206
+ if (!manifest.includes.identity || !existsSync7(resolve11(identityDir, "private.pem"))) {
5476
5207
  ({ publicKeyPem } = generateIdentity(dest));
5477
5208
  } else {
5478
- publicKeyPem = readFileSync9(resolve13(identityDir, "public.pem"), "utf-8");
5209
+ publicKeyPem = readFileSync9(resolve11(identityDir, "public.pem"), "utf-8");
5479
5210
  }
5480
- const promptsPath = resolve13(dest, "home/.config/prompts.json");
5481
- if (!existsSync8(promptsPath)) {
5211
+ const promptsPath = resolve11(dest, "home/.config/prompts.json");
5212
+ if (!existsSync7(promptsPath)) {
5482
5213
  const mindPrompts = await getMindPromptDefaults();
5483
5214
  writeFileSync8(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
5484
5215
  `);
5485
5216
  }
5486
5217
  const state = stateDir(name);
5487
5218
  mkdirSync6(state, { recursive: true });
5488
- const envJson = resolve13(tempDir, "state/env.json");
5489
- if (existsSync8(envJson)) {
5490
- cpSync(envJson, resolve13(state, "env.json"));
5219
+ const envJson = resolve11(tempDir, "state/env.json");
5220
+ if (existsSync7(envJson)) {
5221
+ cpSync(envJson, resolve11(state, "env.json"));
5491
5222
  }
5492
5223
  const port = await nextPort();
5493
5224
  await addMind(name, port, manifest.stage, manifest.template);
5494
- const homeDir = resolve13(dest, "home");
5225
+ const homeDir = resolve11(dest, "home");
5495
5226
  ensureVoluteGroup();
5496
5227
  createMindUser(name, homeDir);
5497
5228
  await chownMindDir(dest, name);
@@ -5504,7 +5235,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5504
5235
  await initTemplateBranch(dest, composedDir, templateManifest, name, env);
5505
5236
  } catch (err) {
5506
5237
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
5507
- rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
5238
+ rmSync3(resolve11(dest, ".git"), { recursive: true, force: true });
5508
5239
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
5509
5240
  }
5510
5241
  const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
@@ -5534,7 +5265,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5534
5265
  ...skillWarnings.length > 0 && { skillWarnings }
5535
5266
  });
5536
5267
  } catch (err) {
5537
- if (existsSync8(dest)) rmSync3(dest, { recursive: true, force: true });
5268
+ if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
5538
5269
  try {
5539
5270
  await removeMind(name);
5540
5271
  } catch (cleanupErr) {
@@ -5547,8 +5278,8 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5547
5278
  }
5548
5279
  }
5549
5280
  async function importHistoryFromArchive(name, tempDir) {
5550
- const historyJsonl = resolve13(tempDir, "history.jsonl");
5551
- if (!existsSync8(historyJsonl)) return;
5281
+ const historyJsonl = resolve11(tempDir, "history.jsonl");
5282
+ if (!existsSync7(historyJsonl)) return;
5552
5283
  try {
5553
5284
  const db = await getDb();
5554
5285
  const lines = readFileSync9(historyJsonl, "utf-8").trim().split("\n");
@@ -5588,13 +5319,13 @@ async function importHistoryFromArchive(name, tempDir) {
5588
5319
  }
5589
5320
  }
5590
5321
  function importSessionsFromArchive(dest, tempDir) {
5591
- const sessionsDir = resolve13(tempDir, "sessions");
5592
- if (!existsSync8(sessionsDir)) return;
5322
+ const sessionsDir = resolve11(tempDir, "sessions");
5323
+ if (!existsSync7(sessionsDir)) return;
5593
5324
  try {
5594
- const destSessions = resolve13(dest, ".mind/sessions");
5325
+ const destSessions = resolve11(dest, ".mind/sessions");
5595
5326
  mkdirSync6(destSessions, { recursive: true });
5596
5327
  for (const file of readdirSync2(sessionsDir)) {
5597
- cpSync(resolve13(sessionsDir, file), resolve13(destSessions, file));
5328
+ cpSync(resolve11(sessionsDir, file), resolve11(destSessions, file));
5598
5329
  }
5599
5330
  } catch (err) {
5600
5331
  logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
@@ -5674,7 +5405,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5674
5405
  }
5675
5406
  ensureVoluteHome();
5676
5407
  const dest = mindDir(name);
5677
- if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5408
+ if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5678
5409
  const templatesRoot = findTemplatesRoot();
5679
5410
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
5680
5411
  try {
@@ -5711,9 +5442,9 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5711
5442
  writeVoluteConfig(dest, config2);
5712
5443
  const modelId = body.model ?? cog?.model;
5713
5444
  effectiveModel = modelId ? qualifyModelId(modelId) : void 0;
5714
- const sdkConfigPath = resolve13(dest, "home/.config/config.json");
5445
+ const sdkConfigPath = resolve11(dest, "home/.config/config.json");
5715
5446
  if (modelId || cog?.compaction) {
5716
- const existing = existsSync8(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
5447
+ const existing = existsSync7(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
5717
5448
  if (modelId) {
5718
5449
  existing.model = template === "pi" ? qualifyModelId(modelId) : unqualifyModelId(modelId);
5719
5450
  }
@@ -5726,7 +5457,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5726
5457
  }
5727
5458
  const mindPrompts = await getMindPromptDefaults();
5728
5459
  writeFileSync8(
5729
- resolve13(dest, "home/.config/prompts.json"),
5460
+ resolve11(dest, "home/.config/prompts.json"),
5730
5461
  `${JSON.stringify(mindPrompts, null, 2)}
5731
5462
  `
5732
5463
  );
@@ -5738,7 +5469,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5738
5469
  } catch (err) {
5739
5470
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
5740
5471
  }
5741
- const homeDir = resolve13(dest, "home");
5472
+ const homeDir = resolve11(dest, "home");
5742
5473
  ensureVoluteGroup();
5743
5474
  createMindUser(name, homeDir);
5744
5475
  await chownMindDir(dest, name);
@@ -5751,7 +5482,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5751
5482
  await initTemplateBranch(dest, composedDir, manifest, name, env);
5752
5483
  } catch (err) {
5753
5484
  logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
5754
- rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
5485
+ rmSync3(resolve11(dest, ".git"), { recursive: true, force: true });
5755
5486
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
5756
5487
  }
5757
5488
  if (body.stage === "seed") {
@@ -5760,7 +5491,7 @@ Your creator described you as: "${body.description}"
5760
5491
  ` : "";
5761
5492
  const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
5762
5493
  const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
5763
- writeFileSync8(resolve13(dest, "home/SOUL.md"), seedSoul);
5494
+ writeFileSync8(resolve11(dest, "home/SOUL.md"), seedSoul);
5764
5495
  }
5765
5496
  let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
5766
5497
  if (body.stage === "seed" && !body.skills) {
@@ -5786,7 +5517,7 @@ Your creator described you as: "${body.description}"
5786
5517
  const spiritName = getSpiritName();
5787
5518
  const spiritEntry = await findMind(spiritName);
5788
5519
  if (spiritEntry) {
5789
- const { spiritDir } = await import("./spirit-4ECYIVGE.js");
5520
+ const { spiritDir } = await import("./spirit-Q5VGFWUI.js");
5790
5521
  const sDir = spiritEntry.dir ?? spiritDir();
5791
5522
  const spiritConfig = readVoluteConfig(sDir) ?? {};
5792
5523
  const schedules = spiritConfig.schedules ?? [];
@@ -5801,7 +5532,7 @@ Your creator described you as: "${body.description}"
5801
5532
  });
5802
5533
  spiritConfig.schedules = schedules;
5803
5534
  writeVoluteConfig(sDir, spiritConfig);
5804
- const { getScheduler: getScheduler2 } = await import("./scheduler-JTTWZDYU.js");
5535
+ const { getScheduler: getScheduler2 } = await import("./scheduler-73Q4MQ7Y.js");
5805
5536
  getScheduler2().loadSchedules(spiritName, sDir);
5806
5537
  }
5807
5538
  }
@@ -5812,11 +5543,11 @@ Your creator described you as: "${body.description}"
5812
5543
  if (body.stage !== "seed") {
5813
5544
  const customSoul = await getPromptIfCustom("default_soul");
5814
5545
  if (customSoul) {
5815
- writeFileSync8(resolve13(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
5546
+ writeFileSync8(resolve11(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
5816
5547
  }
5817
5548
  const customMemory = await getPromptIfCustom("default_memory");
5818
5549
  if (customMemory) {
5819
- writeFileSync8(resolve13(dest, "home/MEMORY.md"), customMemory);
5550
+ writeFileSync8(resolve11(dest, "home/MEMORY.md"), customMemory);
5820
5551
  }
5821
5552
  }
5822
5553
  await chownMindDir(dest, name);
@@ -5856,7 +5587,7 @@ Your creator described you as: "${body.description}"
5856
5587
  ...skillWarnings.length > 0 && { skillWarnings }
5857
5588
  });
5858
5589
  } catch (err) {
5859
- if (existsSync8(dest)) rmSync3(dest, { recursive: true, force: true });
5590
+ if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
5860
5591
  try {
5861
5592
  await removeMind(name);
5862
5593
  } catch (cleanupErr) {
@@ -5877,13 +5608,13 @@ Your creator described you as: "${body.description}"
5877
5608
  return importFromArchive(c, body.archivePath, body.name, body.manifest);
5878
5609
  }
5879
5610
  const wsDir = body.workspacePath;
5880
- if (!wsDir || !existsSync8(resolve13(wsDir, "SOUL.md")) || !existsSync8(resolve13(wsDir, "IDENTITY.md"))) {
5611
+ if (!wsDir || !existsSync7(resolve11(wsDir, "SOUL.md")) || !existsSync7(resolve11(wsDir, "IDENTITY.md"))) {
5881
5612
  return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
5882
5613
  }
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") : "";
5614
+ const soul = readFileSync9(resolve11(wsDir, "SOUL.md"), "utf-8");
5615
+ const identity = readFileSync9(resolve11(wsDir, "IDENTITY.md"), "utf-8");
5616
+ const userPath = resolve11(wsDir, "USER.md");
5617
+ const user = existsSync7(userPath) ? readFileSync9(userPath, "utf-8") : "";
5887
5618
  const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
5888
5619
  const template = body.template ?? "claude";
5889
5620
  const nameErr = validateMindName(name);
@@ -5903,33 +5634,33 @@ ${user.trimEnd()}
5903
5634
  ` : "";
5904
5635
  ensureVoluteHome();
5905
5636
  const dest = mindDir(name);
5906
- if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5637
+ if (existsSync7(dest)) return c.json({ error: "Mind directory already exists" }, 409);
5907
5638
  const templatesRoot = findTemplatesRoot();
5908
5639
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
5909
5640
  try {
5910
5641
  copyTemplateToDir(composedDir, dest, name, manifest);
5911
5642
  applyInitFiles(dest);
5912
5643
  const { publicKeyPem: importPublicKey } = generateIdentity(dest);
5913
- writeFileSync8(resolve13(dest, "home/SOUL.md"), mergedSoul);
5914
- const wsMemoryPath = resolve13(wsDir, "MEMORY.md");
5915
- const hasMemory = existsSync8(wsMemoryPath);
5644
+ writeFileSync8(resolve11(dest, "home/SOUL.md"), mergedSoul);
5645
+ const wsMemoryPath = resolve11(wsDir, "MEMORY.md");
5646
+ const hasMemory = existsSync7(wsMemoryPath);
5916
5647
  if (hasMemory) {
5917
5648
  const existingMemory = readFileSync9(wsMemoryPath, "utf-8");
5918
5649
  writeFileSync8(
5919
- resolve13(dest, "home/MEMORY.md"),
5650
+ resolve11(dest, "home/MEMORY.md"),
5920
5651
  `${existingMemory.trimEnd()}${mergedMemoryExtra}`
5921
5652
  );
5922
5653
  } else if (user) {
5923
- writeFileSync8(resolve13(dest, "home/MEMORY.md"), `${user.trimEnd()}
5654
+ writeFileSync8(resolve11(dest, "home/MEMORY.md"), `${user.trimEnd()}
5924
5655
  `);
5925
5656
  }
5926
- const wsMemoryDir = resolve13(wsDir, "memory");
5657
+ const wsMemoryDir = resolve11(wsDir, "memory");
5927
5658
  let dailyLogCount = 0;
5928
- if (existsSync8(wsMemoryDir)) {
5929
- const destMemoryDir = resolve13(dest, "home/memory");
5659
+ if (existsSync7(wsMemoryDir)) {
5660
+ const destMemoryDir = resolve11(dest, "home/memory");
5930
5661
  const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
5931
5662
  for (const file of files) {
5932
- cpSync(resolve13(wsMemoryDir, file), resolve13(destMemoryDir, file));
5663
+ cpSync(resolve11(wsMemoryDir, file), resolve11(destMemoryDir, file));
5933
5664
  }
5934
5665
  dailyLogCount = files.length;
5935
5666
  }
@@ -5940,7 +5671,7 @@ ${user.trimEnd()}
5940
5671
  } catch (err) {
5941
5672
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
5942
5673
  }
5943
- const homeDir = resolve13(dest, "home");
5674
+ const homeDir = resolve11(dest, "home");
5944
5675
  ensureVoluteGroup();
5945
5676
  createMindUser(name, homeDir);
5946
5677
  await chownMindDir(dest, name);
@@ -5948,20 +5679,20 @@ ${user.trimEnd()}
5948
5679
  if (!hasMemory && dailyLogCount > 0) {
5949
5680
  await consolidateMemory(dest);
5950
5681
  }
5951
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dest, "home") } : void 0;
5682
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
5952
5683
  await gitExec(["init"], { cwd: dest, mindName: name, env });
5953
5684
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
5954
5685
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
5955
5686
  await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
5956
- const sessionFile = body.sessionPath ? resolve13(body.sessionPath) : findOpenClawSession(wsDir);
5957
- if (sessionFile && existsSync8(sessionFile)) {
5687
+ const sessionFile = body.sessionPath ? resolve11(body.sessionPath) : findOpenClawSession(wsDir);
5688
+ if (sessionFile && existsSync7(sessionFile)) {
5958
5689
  if (template === "pi") {
5959
5690
  importPiSession(sessionFile, dest);
5960
5691
  } else if (template === "claude") {
5961
5692
  const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
5962
- const mindRuntimeDir = resolve13(dest, ".mind");
5693
+ const mindRuntimeDir = resolve11(dest, ".mind");
5963
5694
  mkdirSync6(mindRuntimeDir, { recursive: true });
5964
- writeFileSync8(resolve13(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
5695
+ writeFileSync8(resolve11(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
5965
5696
  }
5966
5697
  }
5967
5698
  importOpenClawConnectors(name, dest);
@@ -5971,7 +5702,7 @@ ${user.trimEnd()}
5971
5702
  );
5972
5703
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
5973
5704
  } catch (err) {
5974
- if (existsSync8(dest)) rmSync3(dest, { recursive: true, force: true });
5705
+ if (existsSync7(dest)) rmSync3(dest, { recursive: true, force: true });
5975
5706
  try {
5976
5707
  await removeMind(name);
5977
5708
  } catch (cleanupErr) {
@@ -6007,7 +5738,7 @@ ${user.trimEnd()}
6007
5738
  stage: entry.stage,
6008
5739
  dir: entry.dir
6009
5740
  });
6010
- const hasPages = existsSync8(resolve13(mindDir(entry.name), "home", "pages"));
5741
+ const hasPages = existsSync7(resolve11(mindDir(entry.name), "home", "pages"));
6011
5742
  const lastActiveAt = lastActiveMap.get(entry.name) ?? null;
6012
5743
  if (!privileged) return toPublicMind(entry, mindStatus, { hasPages, lastActiveAt });
6013
5744
  return {
@@ -6015,6 +5746,7 @@ ${user.trimEnd()}
6015
5746
  ...mindStatus,
6016
5747
  hasPages,
6017
5748
  templateStale: isTemplateStale(entry),
5749
+ upgradeBlocked: getUpgradeBlocked(entry.name)?.reason,
6018
5750
  lastActiveAt
6019
5751
  };
6020
5752
  })
@@ -6025,12 +5757,12 @@ ${user.trimEnd()}
6025
5757
  const entry = await findMind(name);
6026
5758
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6027
5759
  const dir = entry.dir ?? mindDir(entry.parent ?? name);
6028
- if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
5760
+ if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
6029
5761
  const mindStatus = await getMindStatus(name, entry.port, void 0, {
6030
5762
  stage: entry.stage,
6031
5763
  dir: entry.dir
6032
5764
  });
6033
- const hasPages = existsSync8(resolve13(mindDir(name), "home", "pages"));
5765
+ const hasPages = existsSync7(resolve11(mindDir(name), "home", "pages"));
6034
5766
  if (!isPrivileged(c)) {
6035
5767
  return c.json(toPublicMind(entry, mindStatus, { hasPages }));
6036
5768
  }
@@ -6053,6 +5785,7 @@ ${user.trimEnd()}
6053
5785
  variants: variantStatuses,
6054
5786
  hasPages,
6055
5787
  templateStale: isTemplateStale(entry),
5788
+ upgradeBlocked: getUpgradeBlocked(name)?.reason,
6056
5789
  ...notice && {
6057
5790
  lastNotice: {
6058
5791
  kind: notice.kind,
@@ -6070,7 +5803,7 @@ ${user.trimEnd()}
6070
5803
  const projectDir = entry.dir ?? mindDir(name);
6071
5804
  if (entry.parent) {
6072
5805
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
6073
- } else if (!existsSync8(projectDir)) {
5806
+ } else if (!existsSync7(projectDir)) {
6074
5807
  return c.json({ error: "Mind directory missing" }, 404);
6075
5808
  }
6076
5809
  if (getMindManager().isRunning(name)) {
@@ -6091,7 +5824,7 @@ ${user.trimEnd()}
6091
5824
  const projectDir = entry.dir ?? mindDir(name);
6092
5825
  if (entry.parent) {
6093
5826
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
6094
- } else if (!existsSync8(projectDir)) {
5827
+ } else if (!existsSync7(projectDir)) {
6095
5828
  return c.json({ error: "Mind directory missing" }, 404);
6096
5829
  }
6097
5830
  let context;
@@ -6107,7 +5840,7 @@ ${user.trimEnd()}
6107
5840
  const manager = getMindManager();
6108
5841
  try {
6109
5842
  if (context?.type === "reload") {
6110
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
5843
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-KHI65FEX.js");
6111
5844
  const sleepState = getSleepManagerIfReady2()?.getState(name);
6112
5845
  if (sleepState?.sleeping) {
6113
5846
  logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
@@ -6128,7 +5861,7 @@ ${user.trimEnd()}
6128
5861
  if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
6129
5862
  const projectRoot = mindDir(baseName);
6130
5863
  const discardUnresolved = context.discardUnresolvedHomeFiles === true;
6131
- if (existsSync8(variantEntry.dir)) {
5864
+ if (existsSync7(variantEntry.dir)) {
6132
5865
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
6133
5866
  if (status) {
6134
5867
  try {
@@ -6158,7 +5891,7 @@ ${user.trimEnd()}
6158
5891
  const preMergeHead = (await gitExec(["rev-parse", "HEAD"], { cwd: projectRoot })).trim();
6159
5892
  const memoryDelta = await mergeVariantExcludingMemory(projectRoot, variantEntry.branch);
6160
5893
  let unresolvedNotice;
6161
- if (existsSync8(variantEntry.dir) && !discardUnresolved) {
5894
+ if (existsSync7(variantEntry.dir) && !discardUnresolved) {
6162
5895
  try {
6163
5896
  const unresolved = await findUnresolvedHomeFiles(variantEntry.dir);
6164
5897
  if (unresolved.totalCount > 0) {
@@ -6256,7 +5989,7 @@ ${user.trimEnd()}
6256
5989
  const name = c.req.param("name");
6257
5990
  const entry = await findMind(name);
6258
5991
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6259
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
5992
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-KHI65FEX.js");
6260
5993
  const sm = getSleepManagerIfReady2();
6261
5994
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6262
5995
  return c.json(sm.getState(name));
@@ -6264,7 +5997,7 @@ ${user.trimEnd()}
6264
5997
  const name = c.req.param("name");
6265
5998
  const entry = await findMind(name);
6266
5999
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6267
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
6000
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-KHI65FEX.js");
6268
6001
  const sm = getSleepManagerIfReady2();
6269
6002
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6270
6003
  if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
@@ -6284,7 +6017,7 @@ ${user.trimEnd()}
6284
6017
  const name = c.req.param("name");
6285
6018
  const entry = await findMind(name);
6286
6019
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6287
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
6020
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-KHI65FEX.js");
6288
6021
  const sm = getSleepManagerIfReady2();
6289
6022
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6290
6023
  const sleepState = sm.getState(name);
@@ -6299,7 +6032,7 @@ ${user.trimEnd()}
6299
6032
  const name = c.req.param("name");
6300
6033
  const entry = await findMind(name);
6301
6034
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6302
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
6035
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-KHI65FEX.js");
6303
6036
  const sm = getSleepManagerIfReady2();
6304
6037
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6305
6038
  const flushed = await sm.flushQueuedMessages(name);
@@ -6315,12 +6048,12 @@ ${user.trimEnd()}
6315
6048
  if (body.displayName !== void 0) profile.displayName = body.displayName;
6316
6049
  if (body.description !== void 0) profile.description = body.description;
6317
6050
  if (body.avatar !== void 0) {
6318
- const homeDir = resolve13(dir, "home");
6051
+ const homeDir = resolve11(dir, "home");
6319
6052
  const avatarPath = safeResolveWithinBase(homeDir, body.avatar);
6320
6053
  if (!avatarPath) {
6321
6054
  return c.json({ error: "Avatar path must be inside the mind's home directory" }, 400);
6322
6055
  }
6323
- if (!existsSync8(avatarPath)) {
6056
+ if (!existsSync7(avatarPath)) {
6324
6057
  return c.json({ error: `Avatar file not found: ${relative(homeDir, avatarPath)}` }, 400);
6325
6058
  }
6326
6059
  profile.avatar = relative(homeDir, avatarPath);
@@ -6371,7 +6104,7 @@ ${user.trimEnd()}
6371
6104
  });
6372
6105
  }
6373
6106
  if (!force) {
6374
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-OFP2GFXF.js");
6107
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-KHI65FEX.js");
6375
6108
  if (getSleepManagerIfReady2()?.isSleeping(name)) {
6376
6109
  return c.json({ output: "" });
6377
6110
  }
@@ -6426,7 +6159,7 @@ ${user.trimEnd()}
6426
6159
  }
6427
6160
  }
6428
6161
  const dir = entry.dir ?? mindDir(name);
6429
- const { evaluateSeedChecklist: evaluateSeedChecklist2 } = await import("./seed-readiness-DYOVCYHN.js");
6162
+ const { evaluateSeedChecklist: evaluateSeedChecklist2 } = await import("./seed-readiness-H5TTPAQH.js");
6430
6163
  const checklist = evaluateSeedChecklist2(dir);
6431
6164
  const done = [];
6432
6165
  const remaining = [];
@@ -6468,7 +6201,7 @@ ${user.trimEnd()}
6468
6201
  try {
6469
6202
  const sproutedDir = entry.dir ?? mindDir(name);
6470
6203
  if (setupDefaultDreaming(sproutedDir).schedulesChanged) {
6471
- const { getScheduler: getScheduler2 } = await import("./scheduler-JTTWZDYU.js");
6204
+ const { getScheduler: getScheduler2 } = await import("./scheduler-73Q4MQ7Y.js");
6472
6205
  getScheduler2().loadSchedules(name, sproutedDir);
6473
6206
  }
6474
6207
  } catch (err) {
@@ -6478,7 +6211,7 @@ ${user.trimEnd()}
6478
6211
  const spiritName = getSpiritName();
6479
6212
  const spiritEntry = await findMind(spiritName);
6480
6213
  if (spiritEntry) {
6481
- const { firstWeekSchedules, spiritDir } = await import("./spirit-4ECYIVGE.js");
6214
+ const { firstWeekSchedules, spiritDir } = await import("./spirit-Q5VGFWUI.js");
6482
6215
  const sDir = spiritEntry.dir ?? spiritDir();
6483
6216
  const spiritConfig = readVoluteConfig(sDir);
6484
6217
  if (spiritConfig) {
@@ -6492,7 +6225,7 @@ ${user.trimEnd()}
6492
6225
  spiritConfig.schedules = schedules;
6493
6226
  writeVoluteConfig(sDir, spiritConfig);
6494
6227
  try {
6495
- const { getScheduler: getScheduler2 } = await import("./scheduler-JTTWZDYU.js");
6228
+ const { getScheduler: getScheduler2 } = await import("./scheduler-73Q4MQ7Y.js");
6496
6229
  getScheduler2().loadSchedules(spiritName, sDir);
6497
6230
  } catch (err) {
6498
6231
  logger_default.warn(
@@ -6546,10 +6279,10 @@ ${user.trimEnd()}
6546
6279
  await deleteMindUser2(name);
6547
6280
  invalidateMindUserCache(name);
6548
6281
  const state = stateDir(name);
6549
- if (existsSync8(state)) {
6282
+ if (existsSync7(state)) {
6550
6283
  rmSync3(state, { recursive: true, force: true });
6551
6284
  }
6552
- if (force && existsSync8(dir)) {
6285
+ if (force && existsSync7(dir)) {
6553
6286
  rmSync3(dir, { recursive: true, force: true });
6554
6287
  deleteMindUser(name);
6555
6288
  }
@@ -6564,7 +6297,7 @@ ${user.trimEnd()}
6564
6297
  const entry = await findMind(mindName);
6565
6298
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6566
6299
  const dir = mindDir(mindName);
6567
- if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
6300
+ if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
6568
6301
  let body = {};
6569
6302
  try {
6570
6303
  body = await c.req.json();
@@ -6575,27 +6308,12 @@ ${user.trimEnd()}
6575
6308
  if (!isKnownTemplate(template)) {
6576
6309
  return c.json({ error: `Unknown template: ${template}` }, 400);
6577
6310
  }
6578
- const UPGRADE_BRANCH = "upgrade";
6579
- const upgradeVariantName = `${mindName}-upgrade`;
6580
- const worktreeDir = resolve13(dir, ".variants", UPGRADE_BRANCH);
6581
6311
  if (body.abort) {
6582
- if (!existsSync8(worktreeDir)) {
6312
+ if (!upgradeInProgress(mindName)) {
6583
6313
  return c.json({ error: "No upgrade in progress" }, 400);
6584
6314
  }
6585
6315
  try {
6586
- try {
6587
- const gitDirContent = readFileSync9(resolve13(worktreeDir, ".git"), "utf-8").trim();
6588
- const gitDir = gitDirContent.replace("gitdir: ", "");
6589
- if (existsSync8(resolve13(gitDir, "MERGE_HEAD"))) {
6590
- await gitExec(["merge", "--abort"], { cwd: worktreeDir });
6591
- }
6592
- } catch {
6593
- }
6594
- await cleanupVariant(upgradeVariantName, mindName, dir, worktreeDir, { stop: true });
6595
- try {
6596
- await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
6597
- } catch {
6598
- }
6316
+ await abortUpgrade(mindName);
6599
6317
  return c.json({ ok: true });
6600
6318
  } catch (err) {
6601
6319
  return c.json(
@@ -6605,81 +6323,45 @@ ${user.trimEnd()}
6605
6323
  }
6606
6324
  }
6607
6325
  if (body.continue) {
6608
- if (!existsSync8(worktreeDir)) {
6326
+ if (!upgradeInProgress(mindName)) {
6609
6327
  return c.json({ error: "No upgrade in progress" }, 400);
6610
6328
  }
6611
- const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
6612
- const hasConflicts2 = status.split("\n").some((line) => line.startsWith("UU") || line.startsWith("AA"));
6613
- if (hasConflicts2) {
6614
- return c.json({ error: "Unresolved conflicts remain" }, 409);
6615
- }
6616
- try {
6617
- await gitExec(["add", "-A"], { cwd: worktreeDir });
6618
- await gitExec(["commit", "-m", "merge template update"], { cwd: worktreeDir });
6619
- } catch (e) {
6620
- const msg = e instanceof Error ? e.message : String(e);
6621
- const stderr = e?.stderr ?? "";
6622
- const stdout = e?.stdout ?? "";
6623
- if (!msg.includes("nothing to commit") && !stderr.includes("nothing to commit") && !stdout.includes("nothing to commit"))
6624
- throw e;
6625
- }
6626
- try {
6627
- await gitExec(["add", "home/"], { cwd: worktreeDir });
6628
- } catch (err) {
6629
- logger_default.warn(`failed to re-add home files during upgrade for ${mindName}`, logger_default.errorData(err));
6630
- }
6631
- try {
6632
- await gitExec(["diff", "--cached", "--quiet"], { cwd: worktreeDir });
6633
- } catch {
6634
- await gitExec(["commit", "-m", "re-add allowlisted home files"], {
6635
- cwd: worktreeDir
6636
- });
6637
- }
6638
- await chownMindDir(dir, mindName);
6639
6329
  try {
6640
- const result = await mergeUpgradeAndRestart(
6641
- mindName,
6642
- dir,
6643
- worktreeDir,
6644
- upgradeVariantName,
6645
- UPGRADE_BRANCH,
6646
- template,
6647
- oldTemplate
6648
- );
6649
- return c.json(result);
6330
+ const result = await continueUpgrade(mindName, { template });
6331
+ if (result.status === "conflicts") {
6332
+ return c.json({
6333
+ ok: false,
6334
+ conflicts: true,
6335
+ worktreeDir: result.worktreeDir,
6336
+ files: result.files,
6337
+ message: result.message ?? "Merge conflicts detected. Resolve them, then run with continue."
6338
+ });
6339
+ }
6340
+ return c.json({ ok: true, warning: result.warning });
6650
6341
  } catch (err) {
6651
- return c.json(
6652
- { error: err instanceof Error ? err.message : "Failed to merge upgrade" },
6653
- 500
6654
- );
6342
+ const msg = err instanceof Error ? err.message : "Failed to merge upgrade";
6343
+ if (msg === "Unresolved conflicts remain") {
6344
+ return c.json({ error: msg }, 409);
6345
+ }
6346
+ return c.json({ error: msg }, 500);
6655
6347
  }
6656
6348
  }
6657
6349
  if (body.accept) {
6658
- if (existsSync8(worktreeDir)) {
6350
+ if (upgradeInProgress(mindName)) {
6659
6351
  try {
6660
- await cleanupVariant(upgradeVariantName, mindName, dir, worktreeDir, { stop: true });
6352
+ await abortUpgrade(mindName);
6661
6353
  } catch (err) {
6662
6354
  logger_default.warn(`failed to clean up legacy upgrade variant for ${mindName}`, logger_default.errorData(err));
6663
6355
  }
6664
- try {
6665
- await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
6666
- } catch {
6667
- }
6668
6356
  }
6669
6357
  return c.json({ error: "Upgrades now auto-merge. Run 'volute mind upgrade' again." }, 400);
6670
6358
  }
6671
6359
  if (body.diff) {
6360
+ if (!existsSync7(resolve11(dir, ".git"))) {
6361
+ return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
6362
+ }
6672
6363
  try {
6673
- if (!existsSync8(resolve13(dir, ".git"))) {
6674
- return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
6675
- }
6676
- await updateTemplateBranch(dir, template, mindName);
6677
- let diff;
6678
- try {
6679
- diff = await gitExec(["diff", "HEAD...volute/template"], { cwd: dir });
6680
- } catch {
6681
- diff = await gitExec(["diff", "HEAD", "volute/template"], { cwd: dir });
6682
- }
6364
+ const diff = await upgradeDiff(mindName, template);
6683
6365
  return c.json({ ok: true, diff: diff || "(no changes)" });
6684
6366
  } catch (err) {
6685
6367
  return c.json(
@@ -6688,103 +6370,21 @@ ${user.trimEnd()}
6688
6370
  );
6689
6371
  }
6690
6372
  }
6691
- if (existsSync8(worktreeDir)) {
6692
- return c.json(
6693
- { error: "Upgrade variant already exists. Use continue or delete it first." },
6694
- 409
6695
- );
6696
- }
6697
- if (!existsSync8(resolve13(dir, ".git"))) {
6698
- try {
6699
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dir, "home") } : void 0;
6700
- await gitExec(["init"], { cwd: dir, mindName, env });
6701
- await configureGitIdentity(mindName, { cwd: dir, mindName, env });
6702
- await gitExec(["add", "-A"], { cwd: dir, mindName, env });
6703
- await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
6704
- await chownMindDir(dir, mindName);
6705
- } catch (err) {
6706
- rmSync3(resolve13(dir, ".git"), { recursive: true, force: true });
6707
- return c.json(
6708
- {
6709
- error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
6710
- },
6711
- 500
6712
- );
6713
- }
6714
- }
6715
- await gitExec(["worktree", "prune"], { cwd: dir });
6716
- try {
6717
- await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
6718
- } catch {
6719
- }
6720
- await updateTemplateBranch(dir, template, mindName);
6721
- const parentDir = resolve13(dir, ".variants");
6722
- if (!existsSync8(parentDir)) {
6723
- mkdirSync6(parentDir, { recursive: true });
6724
- }
6725
- await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
6726
- await gitExec(["rm", "-r", "--cached", "--ignore-unmatch", "home/"], {
6727
- cwd: worktreeDir
6728
- });
6729
6373
  try {
6730
- await gitExec(["checkout", "HEAD", "--", "home/VOLUTE.md"], { cwd: worktreeDir });
6731
- await gitExec(["add", "home/VOLUTE.md"], { cwd: worktreeDir });
6732
- } catch (err) {
6733
- const msg = String(err?.message ?? err);
6734
- if (!msg.includes("did not match")) {
6735
- logger_default.warn(
6736
- `unexpected error restoring VOLUTE.md during upgrade for ${mindName}`,
6737
- logger_default.errorData(err)
6738
- );
6739
- }
6740
- }
6741
- try {
6742
- await gitExec(["diff", "--cached", "--quiet"], { cwd: worktreeDir });
6743
- } catch {
6744
- await gitExec(["commit", "-m", "prepare for home/ allowlist migration"], {
6745
- cwd: worktreeDir
6746
- });
6747
- }
6748
- const hasConflicts = await mergeTemplateBranch(worktreeDir);
6749
- if (!hasConflicts) {
6750
- try {
6751
- await gitExec(["add", "home/"], { cwd: worktreeDir });
6752
- } catch (err) {
6753
- logger_default.warn(`failed to re-add home files during upgrade for ${mindName}`, logger_default.errorData(err));
6754
- }
6755
- try {
6756
- await gitExec(["diff", "--cached", "--quiet"], { cwd: worktreeDir });
6757
- } catch {
6758
- await gitExec(["commit", "-m", "re-add allowlisted home files"], {
6759
- cwd: worktreeDir
6374
+ const result = await runUpgrade(mindName, { template });
6375
+ if (result.status === "conflicts") {
6376
+ return c.json({
6377
+ ok: false,
6378
+ conflicts: true,
6379
+ worktreeDir: result.worktreeDir,
6380
+ files: result.files,
6381
+ message: result.message ?? "Merge conflicts detected. Resolve them, then run with continue."
6760
6382
  });
6761
6383
  }
6762
- }
6763
- await chownMindDir(dir, mindName);
6764
- if (hasConflicts) {
6765
- return c.json({
6766
- ok: false,
6767
- conflicts: true,
6768
- worktreeDir,
6769
- message: "Merge conflicts detected. Resolve them, then run with continue."
6770
- });
6771
- }
6772
- try {
6773
- const result = await mergeUpgradeAndRestart(
6774
- mindName,
6775
- dir,
6776
- worktreeDir,
6777
- upgradeVariantName,
6778
- UPGRADE_BRANCH,
6779
- template,
6780
- oldTemplate
6781
- );
6782
- return c.json(result);
6384
+ return c.json({ ok: true, warning: result.warning });
6783
6385
  } catch (err) {
6784
- try {
6785
- await cleanupVariant(upgradeVariantName, mindName, dir, worktreeDir);
6786
- } catch (cleanupErr) {
6787
- logger_default.warn(`cleanup failed after upgrade error for ${mindName}`, logger_default.errorData(cleanupErr));
6386
+ if (err instanceof UpgradeInProgressError) {
6387
+ return c.json({ error: err.message }, 409);
6788
6388
  }
6789
6389
  return c.json({ error: err instanceof Error ? err.message : "Failed to merge upgrade" }, 500);
6790
6390
  }
@@ -6852,11 +6452,11 @@ ${user.trimEnd()}
6852
6452
  const entry = await findMind(name);
6853
6453
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6854
6454
  const dir = entry.dir ?? mindDir(name);
6855
- if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
6455
+ if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
6856
6456
  let config2 = readVoluteConfig(dir);
6857
6457
  if (!config2 && entry.template === "pi") {
6858
- const piConfigPath = resolve13(dir, "home/.config/config.json");
6859
- if (existsSync8(piConfigPath)) {
6458
+ const piConfigPath = resolve11(dir, "home/.config/config.json");
6459
+ if (existsSync7(piConfigPath)) {
6860
6460
  try {
6861
6461
  config2 = JSON.parse(readFileSync9(piConfigPath, "utf-8"));
6862
6462
  } catch {
@@ -6864,8 +6464,8 @@ ${user.trimEnd()}
6864
6464
  }
6865
6465
  }
6866
6466
  let templateConfig = {};
6867
- const configJsonPath = resolve13(dir, "home/.config/config.json");
6868
- if (existsSync8(configJsonPath)) {
6467
+ const configJsonPath = resolve11(dir, "home/.config/config.json");
6468
+ if (existsSync7(configJsonPath)) {
6869
6469
  try {
6870
6470
  templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
6871
6471
  } catch {
@@ -6907,7 +6507,7 @@ ${user.trimEnd()}
6907
6507
  const entry = await findMind(name);
6908
6508
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6909
6509
  const dir = entry.dir ?? mindDir(name);
6910
- if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
6510
+ if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
6911
6511
  const body = c.req.valid("json");
6912
6512
  const existing = readVoluteConfig(dir) ?? {};
6913
6513
  if (body.model !== void 0) existing.model = body.model;
@@ -6933,14 +6533,14 @@ ${user.trimEnd()}
6933
6533
  }
6934
6534
  writeVoluteConfig(dir, existing);
6935
6535
  if (body.unescapeNewlines !== void 0) {
6936
- const { clearEchoTextCache } = await import("./echo-text-PEEV7FQU.js");
6536
+ const { clearEchoTextCache } = await import("./echo-text-73CYQRL7.js");
6937
6537
  clearEchoTextCache(name);
6938
6538
  }
6939
6539
  const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.compaction !== void 0;
6940
6540
  if (needsConfigJson) {
6941
- const configJsonPath = resolve13(dir, "home/.config/config.json");
6541
+ const configJsonPath = resolve11(dir, "home/.config/config.json");
6942
6542
  let templateConfig = {};
6943
- if (existsSync8(configJsonPath)) {
6543
+ if (existsSync7(configJsonPath)) {
6944
6544
  try {
6945
6545
  templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
6946
6546
  } catch {
@@ -7005,9 +6605,43 @@ ${user.trimEnd()}
7005
6605
  if (err instanceof Error && err.message.includes("not initialized")) {
7006
6606
  return c.json({ error: "Delivery manager not available" }, 503);
7007
6607
  }
6608
+ if (err instanceof UnknownChannelError) return c.json({ error: err.message }, 400);
7008
6609
  logger_default.error(`failed to decline channel ${channel} for ${name}`, logger_default.errorData(err));
7009
6610
  return c.json({ error: "Failed to decline channel" }, 500);
7010
6611
  }
6612
+ }).post("/:name/gates/accept", requireSelf(), async (c) => {
6613
+ const name = c.req.param("name");
6614
+ const body = await c.req.json().catch(() => ({}));
6615
+ const channel = body.channel?.trim();
6616
+ if (!channel) return c.json({ error: "channel required" }, 400);
6617
+ if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
6618
+ try {
6619
+ const result = await getDeliveryManager().acceptChannel(name, channel, body.thread?.trim());
6620
+ return c.json({ ok: true, channel, ...result });
6621
+ } catch (err) {
6622
+ if (err instanceof Error && err.message.includes("not initialized")) {
6623
+ return c.json({ error: "Delivery manager not available" }, 503);
6624
+ }
6625
+ if (err instanceof Error && err.message.includes("malformed")) {
6626
+ return c.json({ error: err.message }, 409);
6627
+ }
6628
+ if (err instanceof UnknownChannelError) return c.json({ error: err.message }, 400);
6629
+ logger_default.error(`failed to accept channel ${channel} for ${name}`, logger_default.errorData(err));
6630
+ return c.json({ error: "Failed to accept channel" }, 500);
6631
+ }
6632
+ }).get("/:name/gates/peek", requireSelf(), async (c) => {
6633
+ const name = c.req.param("name");
6634
+ const channel = c.req.query("channel")?.trim();
6635
+ if (!channel) return c.json({ error: "channel required" }, 400);
6636
+ try {
6637
+ return c.json(await getDeliveryManager().peekChannel(name, channel));
6638
+ } catch (err) {
6639
+ if (err instanceof Error && err.message.includes("not initialized")) {
6640
+ return c.json({ error: "Delivery manager not available" }, 503);
6641
+ }
6642
+ logger_default.error(`failed to peek channel ${channel} for ${name}`, logger_default.errorData(err));
6643
+ return c.json({ error: "Failed to read held messages" }, 500);
6644
+ }
7011
6645
  }).post("/:name/ai/complete", requireSelf(), async (c) => {
7012
6646
  const body = await c.req.json();
7013
6647
  if (!body.systemPrompt || !body.message) {
@@ -7258,6 +6892,35 @@ ${lines.join("\n")}` });
7258
6892
  const maxId = notices.reduce((m, n) => Math.max(m, n.id), 0);
7259
6893
  setNoticeDrainWatermark(baseName, session, maxId);
7260
6894
  return c.json({ context: formatEvents(notices), notices });
6895
+ }).post("/:name/notices", requireSelf(), async (c) => {
6896
+ const MIND_POSTABLE_KINDS = ["context_lost", "delivery_failed"];
6897
+ const name = c.req.param("name");
6898
+ const baseName = await getBaseName(name);
6899
+ let body;
6900
+ try {
6901
+ body = await c.req.json();
6902
+ } catch {
6903
+ return c.json({ error: "Invalid JSON body" }, 400);
6904
+ }
6905
+ const kind = body.kind;
6906
+ if (typeof kind !== "string" || !MIND_POSTABLE_KINDS.includes(kind)) {
6907
+ return c.json({ error: `kind must be one of: ${MIND_POSTABLE_KINDS.join(", ")}` }, 400);
6908
+ }
6909
+ const message = typeof body.message === "string" ? body.message.trim() : "";
6910
+ if (!message) return c.json({ error: "message is required" }, 400);
6911
+ if (body.thread !== void 0 && typeof body.thread !== "string") {
6912
+ return c.json({ error: "thread must be a string" }, 400);
6913
+ }
6914
+ await recordNotice({
6915
+ mind: baseName,
6916
+ thread: body.thread ?? MIND_LEVEL_THREAD,
6917
+ kind,
6918
+ reason: kind,
6919
+ // Bound the size (the body is injected into turn context) without dropping
6920
+ // the notice — a truncated explanation beats a silent 400.
6921
+ detail: message.length > 4e3 ? `${message.slice(0, 4e3)}\u2026` : message
6922
+ });
6923
+ return c.json({ ok: true });
7261
6924
  }).get("/:name/system-events", requireSelf(), async (c) => {
7262
6925
  const name = c.req.param("name");
7263
6926
  const baseName = await getBaseName(name);
@@ -7280,6 +6943,43 @@ ${lines.join("\n")}` });
7280
6943
  };
7281
6944
  })
7282
6945
  });
6946
+ }).put("/:name/turn-summaries", requireSelf(), async (c) => {
6947
+ const name = c.req.param("name");
6948
+ const baseName = await getBaseName(name);
6949
+ let body;
6950
+ try {
6951
+ body = await c.req.json();
6952
+ } catch {
6953
+ return c.json({ error: "Invalid JSON body" }, 400);
6954
+ }
6955
+ const items = body.summaries;
6956
+ if (!Array.isArray(items) || items.length === 0) {
6957
+ return c.json({ error: "summaries must be a non-empty array" }, 400);
6958
+ }
6959
+ let updated = 0;
6960
+ let created = 0;
6961
+ for (const item of items) {
6962
+ if (!item || typeof item.turnId !== "string" || !item.turnId) {
6963
+ return c.json({ error: "each summary needs a turnId" }, 400);
6964
+ }
6965
+ if (typeof item.content !== "string") {
6966
+ return c.json({ error: "each summary needs content" }, 400);
6967
+ }
6968
+ const result = await supersedeTurnSummary(baseName, item.turnId, item.content);
6969
+ switch (result.status) {
6970
+ case "invalid":
6971
+ return c.json({ error: result.error }, 400);
6972
+ case "not_found":
6973
+ return c.json({ error: "Turn not found" }, 404);
6974
+ case "forbidden":
6975
+ return c.json({ error: "Forbidden" }, 403);
6976
+ case "ok":
6977
+ if (result.created) created++;
6978
+ else updated++;
6979
+ break;
6980
+ }
6981
+ }
6982
+ return c.json({ ok: true, updated, created });
7283
6983
  }).get("/:name/history", requireSelf(), async (c) => {
7284
6984
  const name = c.req.param("name");
7285
6985
  const channel = c.req.query("channel");
@@ -7288,6 +6988,7 @@ ${lines.join("\n")}` });
7288
6988
  const preset = c.req.query("preset");
7289
6989
  const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
7290
6990
  const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
6991
+ const provisional = c.req.query("provisional") === "true";
7291
6992
  const db = await getDb();
7292
6993
  const conditions = [eq5(mindHistory.mind, name)];
7293
6994
  if (channel) {
@@ -7299,6 +7000,9 @@ ${lines.join("\n")}` });
7299
7000
  const effectivePreset = full ? "all" : preset;
7300
7001
  if (!effectivePreset || effectivePreset === "summary") {
7301
7002
  const sumConditions = [eq5(summaries.mind, name), eq5(summaries.period, "turn")];
7003
+ if (provisional) {
7004
+ sumConditions.push(sql3`json_extract(${summaries.metadata}, '$.author') is not ${"mind"}`);
7005
+ }
7302
7006
  if (session) {
7303
7007
  sumConditions.push(eq5(turns.thread, session));
7304
7008
  const sumRows2 = await db.select({
@@ -7707,7 +7411,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
7707
7411
  const entry = await findMind(name);
7708
7412
  if (!entry) return c.json({ error: "Mind not found" }, 404);
7709
7413
  const body = await c.req.text();
7710
- const { deliverEvent: deliverEvent2 } = await import("./system-events-V43HDA3T.js");
7414
+ const { deliverEvent: deliverEvent2 } = await import("./system-events-Q7IVZ5T4.js");
7711
7415
  const { id, delivered } = await deliverEvent2(name, {
7712
7416
  type: "webhook",
7713
7417
  body,
@@ -7723,16 +7427,16 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
7723
7427
  var schedules_default = app15;
7724
7428
 
7725
7429
  // packages/daemon/src/web/api/setup.ts
7726
- import { mkdirSync as mkdirSync7 } from "fs";
7430
+ import { mkdirSync as mkdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
7727
7431
  import { homedir as homedir2 } from "os";
7728
- import { resolve as resolve14 } from "path";
7432
+ import { basename, resolve as resolve12 } from "path";
7729
7433
  import { Hono as Hono19 } from "hono";
7730
7434
  import { setCookie as setCookie2 } from "hono/cookie";
7731
7435
  var DEFAULT_API_URL2 = "https://volute.systems";
7732
7436
  var setup = new Hono19();
7733
7437
  function writeSetupConfig(systemName, description) {
7734
- const configHome = process.env.VOLUTE_HOME ?? resolve14(homedir2(), ".volute");
7735
- const mindsDir = resolve14(configHome, "minds");
7438
+ const configHome = process.env.VOLUTE_HOME ?? resolve12(homedir2(), ".volute");
7439
+ const mindsDir = resolve12(configHome, "minds");
7736
7440
  mkdirSync7(configHome, { recursive: true });
7737
7441
  mkdirSync7(mindsDir, { recursive: true });
7738
7442
  const existingConfig = readGlobalConfig();
@@ -8063,6 +7767,39 @@ setup.post("/spirit", async (c) => {
8063
7767
  config2.setup.spiritName = name;
8064
7768
  const temperament = body.temperament?.trim();
8065
7769
  config2.setup.spiritTemperament = temperament || void 0;
7770
+ const description = body.description?.trim();
7771
+ config2.setup.spiritDescription = description || void 0;
7772
+ const clearOldStash = () => {
7773
+ if (config2.setup?.spiritAvatar) {
7774
+ rmSync4(resolve12(voluteSystemDir(), basename(config2.setup.spiritAvatar)), { force: true });
7775
+ config2.setup.spiritAvatar = void 0;
7776
+ }
7777
+ };
7778
+ if (body.avatar) {
7779
+ const match = /^data:image\/(png|jpeg|webp);base64,([A-Za-z0-9+/=]+)$/.exec(body.avatar);
7780
+ if (!match) {
7781
+ return c.json({ error: "Avatar must be a png, jpeg, or webp image" }, 400);
7782
+ }
7783
+ if (match[2].length > 3 * 1024 * 1024) {
7784
+ return c.json({ error: "Avatar must be under 2MB" }, 400);
7785
+ }
7786
+ let bytes = Buffer.from(match[2], "base64");
7787
+ if (bytes.length > 2 * 1024 * 1024) {
7788
+ return c.json({ error: "Avatar must be under 2MB" }, 400);
7789
+ }
7790
+ let ext = match[1] === "jpeg" ? "jpg" : match[1];
7791
+ const normalized = await normalizeAvatar(bytes);
7792
+ if (normalized) {
7793
+ bytes = normalized.buffer;
7794
+ ext = "webp";
7795
+ }
7796
+ clearOldStash();
7797
+ const filename = `spirit-avatar.${ext}`;
7798
+ writeFileSync9(resolve12(voluteSystemDir(), filename), bytes);
7799
+ config2.setup.spiritAvatar = filename;
7800
+ } else {
7801
+ clearOldStash();
7802
+ }
8066
7803
  writeGlobalConfig(config2);
8067
7804
  return c.json({ ok: true });
8068
7805
  });
@@ -8071,15 +7808,16 @@ setup.post("/complete", async (c) => {
8071
7808
  return c.json({ error: "Setup already complete" }, 400);
8072
7809
  }
8073
7810
  try {
8074
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-4ECYIVGE.js");
8075
- const { startSpiritFull } = await import("./mind-service-E26ZQXDS.js");
7811
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-Q5VGFWUI.js");
7812
+ const { startSpiritFull } = await import("./mind-service-7EFIJ3ZA.js");
8076
7813
  await ensureSpiritProject();
8077
7814
  await syncSpiritTemplate();
7815
+ await notifyExtensionsSpiritReady();
8078
7816
  const warnings = [];
8079
7817
  const config2 = readGlobalConfig();
8080
7818
  if (config2.setup?.service && config2.setup?.type === "local") {
8081
7819
  try {
8082
- const { installUserService } = await import("./service-install-2EWD7KXD.js");
7820
+ const { installUserService } = await import("./service-install-R4SNYUVO.js");
8083
7821
  const installed = await installUserService(config2.port, config2.hostname);
8084
7822
  if (!installed) {
8085
7823
  warnings.push("Service installation is not supported on this platform.");
@@ -8132,15 +7870,13 @@ setup.post("/complete", async (c) => {
8132
7870
  logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
8133
7871
  if (spiritConversationId && spiritStarted) {
8134
7872
  try {
8135
- const { deliverMessage: deliverMessage2 } = await import("./message-delivery-7URC52ME.js");
7873
+ const { deliverMessage: deliverMessage2 } = await import("./message-delivery-TRKT4T2P.js");
8136
7874
  const { listUsersByType: listUsers2 } = await import("./auth-KRDSM55Q.js");
8137
7875
  const admins = await listUsers2("human");
8138
7876
  const admin2 = admins.find((u) => u.role === "admin");
8139
7877
  if (admin2) {
8140
7878
  const adminName = admin2.display_name || admin2.username;
8141
- const cfg = readGlobalConfig();
8142
- const temperament = cfg.setup?.spiritTemperament;
8143
- const welcomePrompt = `Setup is complete. You are ${spiritName}, the spirit of ${cfg.name ?? "this system"}` + (cfg.description ? ` \u2014 ${cfg.description}` : "") + `. ${adminName} chose your name just now` + (temperament ? ` and imagined your temperament as: "${temperament}"` : "") + `, and is now in your chat. Greet ${adminName} by name and help them create their first mind. Keep it brief \u2014 a short greeting in your own voice, then ask what kind of being they'd like to bring into the world. Remember: minds are beings with their own experience, not tools \u2014 ask about personality and character, never about tasks or utility.`;
7879
+ const welcomePrompt = `Setup is complete, and ${adminName} is now in your chat. Greet ${adminName} by name and help them create their first mind. Keep it brief \u2014 a short greeting in your own voice, then ask what kind of being they'd like to bring into the world. Remember: minds are beings with their own experience, not tools \u2014 ask about personality and character, never about tasks or utility.`;
8144
7880
  await deliverMessage2(spiritName, {
8145
7881
  content: [{ type: "text", text: welcomePrompt }],
8146
7882
  channel: `@${admin2.username}`,
@@ -8174,9 +7910,9 @@ setup.post("/complete", async (c) => {
8174
7910
  var setup_default = setup;
8175
7911
 
8176
7912
  // packages/daemon/src/web/api/skills.ts
8177
- import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync4 } from "fs";
7913
+ import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
8178
7914
  import { tmpdir } from "os";
8179
- import { join, resolve as resolve15 } from "path";
7915
+ import { join, resolve as resolve13 } from "path";
8180
7916
  import AdmZip from "adm-zip";
8181
7917
  import { Hono as Hono20 } from "hono";
8182
7918
  var app16 = new Hono20().get("/", async (c) => {
@@ -8252,19 +7988,19 @@ var app16 = new Hono20().get("/", async (c) => {
8252
7988
  try {
8253
7989
  const zip = new AdmZip(buffer);
8254
7990
  for (const entry of zip.getEntries()) {
8255
- const target = resolve15(tmpDir, entry.entryName);
7991
+ const target = resolve13(tmpDir, entry.entryName);
8256
7992
  if (!target.startsWith(tmpDir)) {
8257
7993
  return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
8258
7994
  }
8259
7995
  }
8260
7996
  zip.extractAllTo(tmpDir, true);
8261
7997
  let skillDir = null;
8262
- if (existsSync9(join(tmpDir, "SKILL.md"))) {
7998
+ if (existsSync8(join(tmpDir, "SKILL.md"))) {
8263
7999
  skillDir = tmpDir;
8264
8000
  } else {
8265
8001
  const entries = readdirSync3(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
8266
8002
  for (const entry of entries) {
8267
- if (existsSync9(join(tmpDir, entry.name, "SKILL.md"))) {
8003
+ if (existsSync8(join(tmpDir, entry.name, "SKILL.md"))) {
8268
8004
  skillDir = join(tmpDir, entry.name);
8269
8005
  break;
8270
8006
  }
@@ -8281,7 +8017,7 @@ var app16 = new Hono20().get("/", async (c) => {
8281
8017
  }
8282
8018
  throw e;
8283
8019
  } finally {
8284
- rmSync4(tmpDir, { recursive: true, force: true });
8020
+ rmSync5(tmpDir, { recursive: true, force: true });
8285
8021
  }
8286
8022
  }).get("/:id", async (c) => {
8287
8023
  const id = c.req.param("id");
@@ -8626,8 +8362,8 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
8626
8362
  });
8627
8363
  }, 15e3);
8628
8364
  cleanups.push(() => clearInterval(keepAlive));
8629
- await new Promise((resolve21) => {
8630
- stream.onAbort(() => resolve21());
8365
+ await new Promise((resolve19) => {
8366
+ stream.onAbort(() => resolve19());
8631
8367
  });
8632
8368
  } finally {
8633
8369
  for (const cleanup of cleanups) {
@@ -8908,23 +8644,23 @@ var app21 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
8908
8644
  var feed_default = app21;
8909
8645
 
8910
8646
  // packages/daemon/src/web/api/variants.ts
8911
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
8912
- import { resolve as resolve18 } from "path";
8647
+ import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "fs";
8648
+ import { resolve as resolve16 } from "path";
8913
8649
  import { Hono as Hono26 } from "hono";
8914
8650
 
8915
8651
  // packages/daemon/src/lib/mind/farewell.ts
8916
- import { existsSync as existsSync10, readFileSync as readFileSync10, rmSync as rmSync5 } from "fs";
8917
- import { resolve as resolve16 } from "path";
8652
+ import { existsSync as existsSync9, readFileSync as readFileSync10, rmSync as rmSync6 } from "fs";
8653
+ import { resolve as resolve14 } from "path";
8918
8654
  var flog = logger_default.child("farewell");
8919
8655
  var FAREWELL_RELPATH = ".mind/farewell.md";
8920
8656
  var FAREWELL_MIND_PATH = "../.mind/farewell.md";
8921
8657
  var FAREWELL_TIMEOUT_MS = 12e4;
8922
8658
  function farewellPath(variantDir) {
8923
- return resolve16(variantDir, FAREWELL_RELPATH);
8659
+ return resolve14(variantDir, FAREWELL_RELPATH);
8924
8660
  }
8925
8661
  function readFarewell(variantDir) {
8926
8662
  const path = farewellPath(variantDir);
8927
- if (!existsSync10(path)) return void 0;
8663
+ if (!existsSync9(path)) return void 0;
8928
8664
  try {
8929
8665
  const text = readFileSync10(path, "utf-8").trim();
8930
8666
  return text || void 0;
@@ -8947,7 +8683,7 @@ function waitForFarewell(name, variantDir, timeoutMs) {
8947
8683
  const unsub = subscribe((event) => {
8948
8684
  if (event.mind !== name) return;
8949
8685
  if (event.type !== "mind_done" && event.type !== "mind_idle") return;
8950
- if (existsSync10(farewellPath(variantDir))) cleanup();
8686
+ if (existsSync9(farewellPath(variantDir))) cleanup();
8951
8687
  });
8952
8688
  return { done, cancel: cleanup };
8953
8689
  }
@@ -8956,7 +8692,7 @@ async function runFarewellTurn(opts) {
8956
8692
  const timeoutMs = opts.timeoutMs ?? FAREWELL_TIMEOUT_MS;
8957
8693
  if (!running) return readFarewell(variantDir);
8958
8694
  try {
8959
- rmSync5(farewellPath(variantDir), { force: true });
8695
+ rmSync6(farewellPath(variantDir), { force: true });
8960
8696
  } catch (err) {
8961
8697
  flog.warn(`failed to clear stale farewell note for ${variantName}`, logger_default.errorData(err));
8962
8698
  }
@@ -8980,8 +8716,8 @@ async function runFarewellTurn(opts) {
8980
8716
 
8981
8717
  // packages/daemon/src/lib/mind/spawn-server.ts
8982
8718
  import { spawn as spawn4 } from "child_process";
8983
- import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11, statSync as statSync4 } from "fs";
8984
- import { resolve as resolve17 } from "path";
8719
+ import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
8720
+ import { resolve as resolve15 } from "path";
8985
8721
  async function spawnServer(cwd, port, options) {
8986
8722
  let cmd = process.env.VOLUTE_NODE_PATH ?? process.execPath;
8987
8723
  let args = ["--import", "tsx", "src/server.ts", "--port", String(port)];
@@ -9002,34 +8738,34 @@ function spawnAttached(cmd, args, cwd) {
9002
8738
  cwd,
9003
8739
  stdio: ["ignore", "pipe", "pipe"]
9004
8740
  });
9005
- return new Promise((resolve21) => {
9006
- const timeout = setTimeout(() => resolve21(null), 3e4);
8741
+ return new Promise((resolve19) => {
8742
+ const timeout = setTimeout(() => resolve19(null), 3e4);
9007
8743
  function checkOutput(data) {
9008
8744
  const match = data.toString().match(/listening on :(\d+)/);
9009
8745
  if (match) {
9010
8746
  clearTimeout(timeout);
9011
- resolve21({ child, actualPort: parseInt(match[1], 10) });
8747
+ resolve19({ child, actualPort: parseInt(match[1], 10) });
9012
8748
  }
9013
8749
  }
9014
8750
  child.stdout?.on("data", checkOutput);
9015
8751
  child.stderr?.on("data", checkOutput);
9016
8752
  child.on("error", () => {
9017
8753
  clearTimeout(timeout);
9018
- resolve21(null);
8754
+ resolve19(null);
9019
8755
  });
9020
8756
  child.on("exit", () => {
9021
8757
  clearTimeout(timeout);
9022
- resolve21(null);
8758
+ resolve19(null);
9023
8759
  });
9024
8760
  });
9025
8761
  }
9026
8762
  function spawnDetached(cmd, args, cwd, logDir) {
9027
- const logsDir = logDir ?? resolve17(cwd, ".mind", "logs");
8763
+ const logsDir = logDir ?? resolve15(cwd, ".mind", "logs");
9028
8764
  mkdirSync8(logsDir, { recursive: true });
9029
- const logPath = resolve17(logsDir, "mind.log");
8765
+ const logPath = resolve15(logsDir, "mind.log");
9030
8766
  let startOffset = 0;
9031
8767
  try {
9032
- startOffset = statSync4(logPath).size;
8768
+ startOffset = statSync3(logPath).size;
9033
8769
  } catch {
9034
8770
  }
9035
8771
  const logFd = openSync(logPath, "a");
@@ -9167,11 +8903,11 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9167
8903
  return c.json({ error: `Name already in use: ${variantName}` }, 409);
9168
8904
  }
9169
8905
  const projectRoot = entry.dir ?? mindDir(mindName);
9170
- const variantDir = resolve18(projectRoot, ".variants", variantName);
9171
- if (existsSync11(variantDir)) {
8906
+ const variantDir = resolve16(projectRoot, ".variants", variantName);
8907
+ if (existsSync10(variantDir)) {
9172
8908
  return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
9173
8909
  }
9174
- mkdirSync9(resolve18(projectRoot, ".variants"), { recursive: true });
8910
+ mkdirSync9(resolve16(projectRoot, ".variants"), { recursive: true });
9175
8911
  try {
9176
8912
  await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
9177
8913
  } catch (e) {
@@ -9207,7 +8943,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9207
8943
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
9208
8944
  await exec(cmd, args, {
9209
8945
  cwd: variantDir,
9210
- env: { ...process.env, HOME: resolve18(variantDir, "home") }
8946
+ env: { ...process.env, HOME: resolve16(variantDir, "home") }
9211
8947
  });
9212
8948
  } else {
9213
8949
  await exec("npm", ["install"], { cwd: variantDir });
@@ -9218,7 +8954,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9218
8954
  return c.json({ error: `npm install failed: ${msg}` }, 500);
9219
8955
  }
9220
8956
  if (body.soul) {
9221
- writeFileSync9(resolve18(variantDir, "home/SOUL.md"), body.soul);
8957
+ writeFileSync10(resolve16(variantDir, "home/SOUL.md"), body.soul);
9222
8958
  }
9223
8959
  const variantPort = body.port ?? await nextPort();
9224
8960
  try {
@@ -9301,7 +9037,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9301
9037
  logger_default.warn(`farewell turn failed for ${variantName}`, logger_default.errorData(err));
9302
9038
  }
9303
9039
  try {
9304
- if (existsSync11(variantEntry.dir)) {
9040
+ if (existsSync10(variantEntry.dir)) {
9305
9041
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
9306
9042
  if (status) {
9307
9043
  try {
@@ -9359,7 +9095,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9359
9095
  { conflicts: err.conflicts }
9360
9096
  );
9361
9097
  }
9362
- if (existsSync11(variantEntry.dir) && !body.discardUnresolved) {
9098
+ if (existsSync10(variantEntry.dir) && !body.discardUnresolved) {
9363
9099
  let unresolved;
9364
9100
  try {
9365
9101
  unresolved = await findUnresolvedHomeFiles(variantEntry.dir);
@@ -9398,7 +9134,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9398
9134
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
9399
9135
  await exec(cmd, args, {
9400
9136
  cwd: projectRoot,
9401
- env: { ...process.env, HOME: resolve18(projectRoot, "home") }
9137
+ env: { ...process.env, HOME: resolve16(projectRoot, "home") }
9402
9138
  });
9403
9139
  } else {
9404
9140
  await exec("npm", ["install"], { cwd: projectRoot });
@@ -9855,7 +9591,7 @@ var unifiedChatApp = new Hono28().post(
9855
9591
  logger_default.error("spirit availability check failed \u2014 proceeding with delivery", logger_default.errorData(err));
9856
9592
  }
9857
9593
  const isDM = conv.type === "dm";
9858
- await fanOutToMinds({
9594
+ const { gatedRecipients } = await fanOutToMinds({
9859
9595
  conversationId,
9860
9596
  contentBlocks,
9861
9597
  senderName,
@@ -9865,7 +9601,15 @@ var unifiedChatApp = new Hono28().post(
9865
9601
  // Variant-aware targeting: when targetMind is a variant, route to the variant name
9866
9602
  targetName: baseName ? (username) => username === baseName ? variantName : username : void 0
9867
9603
  });
9868
- return c.json({ ok: true, conversationId, outboundId, spirit: spiritStatus, spiritName });
9604
+ const notice = gatedRecipients.length > 0 ? `Note: your message is held for ${gatedRecipients.join(", ")} pending channel approval \u2014 they haven't routed this channel yet. It will be delivered if they accept the channel.` : void 0;
9605
+ return c.json({
9606
+ ok: true,
9607
+ conversationId,
9608
+ outboundId,
9609
+ spirit: spiritStatus,
9610
+ spiritName,
9611
+ ...notice ? { notice } : {}
9612
+ });
9869
9613
  }
9870
9614
  );
9871
9615
  var app24 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
@@ -9885,11 +9629,11 @@ var app24 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
9885
9629
  if (!stream.aborted) console.error("[chat] SSE ping error:", err);
9886
9630
  });
9887
9631
  }, 15e3);
9888
- await new Promise((resolve21) => {
9632
+ await new Promise((resolve19) => {
9889
9633
  stream.onAbort(() => {
9890
9634
  unsubscribe();
9891
9635
  clearInterval(keepAlive);
9892
- resolve21();
9636
+ resolve19();
9893
9637
  });
9894
9638
  });
9895
9639
  });
@@ -10106,10 +9850,10 @@ app26.route("/api/conversations", conversations_default);
10106
9850
  var app_default = app26;
10107
9851
 
10108
9852
  // packages/daemon/src/web/server.ts
10109
- import { existsSync as existsSync12 } from "fs";
9853
+ import { existsSync as existsSync11 } from "fs";
10110
9854
  import { readFile as readFile2, stat as stat2 } from "fs/promises";
10111
9855
  import { createServer as createHttpsServer } from "https";
10112
- import { dirname as dirname3, extname as extname3, resolve as resolve19 } from "path";
9856
+ import { dirname as dirname3, extname as extname3, resolve as resolve17 } from "path";
10113
9857
  import { serve } from "@hono/node-server";
10114
9858
  var MIME_TYPES2 = {
10115
9859
  ".html": "text/html",
@@ -10128,8 +9872,8 @@ async function startServer({
10128
9872
  let assetsDir = "";
10129
9873
  let searchDir = dirname3(new URL(import.meta.url).pathname);
10130
9874
  for (let i = 0; i < 5; i++) {
10131
- const candidate = resolve19(searchDir, "dist", "web-assets");
10132
- if (existsSync12(candidate)) {
9875
+ const candidate = resolve17(searchDir, "dist", "web-assets");
9876
+ if (existsSync11(candidate)) {
10133
9877
  assetsDir = candidate;
10134
9878
  break;
10135
9879
  }
@@ -10139,20 +9883,20 @@ async function startServer({
10139
9883
  app_default.get("*", async (c) => {
10140
9884
  const urlPath = new URL(c.req.url).pathname;
10141
9885
  if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
10142
- const filePath = resolve19(assetsDir, urlPath.slice(1));
9886
+ const filePath = resolve17(assetsDir, urlPath.slice(1));
10143
9887
  if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
10144
9888
  const s = await stat2(filePath).catch(() => null);
10145
9889
  if (s?.isFile()) {
10146
9890
  const ext = extname3(filePath);
10147
9891
  const mime = MIME_TYPES2[ext] || "application/octet-stream";
10148
9892
  const body = await readFile2(filePath);
10149
- const basename = filePath.slice(filePath.lastIndexOf("/") + 1);
10150
- const nameWithoutExt = basename.slice(0, basename.lastIndexOf("."));
9893
+ const basename2 = filePath.slice(filePath.lastIndexOf("/") + 1);
9894
+ const nameWithoutExt = basename2.slice(0, basename2.lastIndexOf("."));
10151
9895
  const isHashed = /[-.][\da-f]{8,}$/.test(nameWithoutExt);
10152
9896
  const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
10153
9897
  return c.body(body, 200, { "Content-Type": mime, "Cache-Control": cacheControl });
10154
9898
  }
10155
- const indexPath = resolve19(assetsDir, "index.html");
9899
+ const indexPath = resolve17(assetsDir, "index.html");
10156
9900
  const indexStat = await stat2(indexPath).catch(() => null);
10157
9901
  if (indexStat?.isFile()) {
10158
9902
  const body = await readFile2(indexPath, "utf-8");
@@ -10169,10 +9913,10 @@ async function startServer({
10169
9913
  createServer: createHttpsServer,
10170
9914
  serverOptions: { key: tls.key, cert: tls.cert }
10171
9915
  });
10172
- await new Promise((resolve21, reject) => {
9916
+ await new Promise((resolve19, reject) => {
10173
9917
  server2.on("listening", () => {
10174
9918
  logger_default.info("Volute UI running (https)", { hostname, port });
10175
- resolve21();
9919
+ resolve19();
10176
9920
  });
10177
9921
  server2.on("error", (err) => {
10178
9922
  reject(err);
@@ -10180,13 +9924,13 @@ async function startServer({
10180
9924
  });
10181
9925
  const internalPort = port + 1;
10182
9926
  const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
10183
- await new Promise((resolve21, reject) => {
9927
+ await new Promise((resolve19, reject) => {
10184
9928
  internalServer.on("listening", () => {
10185
9929
  logger_default.info("Volute API running (http, internal)", {
10186
9930
  hostname: "127.0.0.1",
10187
9931
  port: internalPort
10188
9932
  });
10189
- resolve21();
9933
+ resolve19();
10190
9934
  });
10191
9935
  internalServer.on("error", (err) => {
10192
9936
  reject(err);
@@ -10195,10 +9939,10 @@ async function startServer({
10195
9939
  return { server: server2, internalPort };
10196
9940
  }
10197
9941
  const server = serve({ fetch: app_default.fetch, port, hostname });
10198
- await new Promise((resolve21, reject) => {
9942
+ await new Promise((resolve19, reject) => {
10199
9943
  server.on("listening", () => {
10200
9944
  logger_default.info("Volute API running (http)", { hostname, port });
10201
- resolve21();
9945
+ resolve19();
10202
9946
  });
10203
9947
  server.on("error", (err) => {
10204
9948
  reject(err);
@@ -10209,18 +9953,18 @@ async function startServer({
10209
9953
 
10210
9954
  // packages/daemon/src/daemon.ts
10211
9955
  if (!process.env.VOLUTE_HOME) {
10212
- process.env.VOLUTE_HOME = resolve20(homedir3(), ".volute");
9956
+ process.env.VOLUTE_HOME = resolve18(homedir3(), ".volute");
10213
9957
  }
10214
9958
  if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
10215
9959
  process.env.TZ = process.env.VOLUTE_TIMEZONE;
10216
9960
  }
10217
9961
  function writeDaemonConfig(systemDir, config2, token) {
10218
- const configPath = resolve20(systemDir, "daemon.json");
10219
- writeFileSync10(configPath, `${JSON.stringify(config2, null, 2)}
9962
+ const configPath = resolve18(systemDir, "daemon.json");
9963
+ writeFileSync11(configPath, `${JSON.stringify(config2, null, 2)}
10220
9964
  `, { mode: 420 });
10221
9965
  chmodSync(configPath, 420);
10222
- const tokenPath = resolve20(systemDir, "daemon-token");
10223
- writeFileSync10(tokenPath, `${token}
9966
+ const tokenPath = resolve18(systemDir, "daemon-token");
9967
+ writeFileSync11(tokenPath, `${token}
10224
9968
  `, { mode: 384 });
10225
9969
  chmodSync(tokenPath, 384);
10226
9970
  }
@@ -10230,7 +9974,7 @@ async function startDaemon(opts) {
10230
9974
  const home = voluteHome();
10231
9975
  const systemDir = voluteSystemDir();
10232
9976
  if (!opts.foreground) {
10233
- const rotatingLog = new RotatingLog(resolve20(systemDir, "daemon.log"));
9977
+ const rotatingLog = new RotatingLog(resolve18(systemDir, "daemon.log"));
10234
9978
  logger_default.setOutput((line) => rotatingLog.write(`${line}
10235
9979
  `));
10236
9980
  const write = (...args) => rotatingLog.write(`${format(...args)}
@@ -10240,9 +9984,9 @@ async function startDaemon(opts) {
10240
9984
  console.warn = write;
10241
9985
  console.info = write;
10242
9986
  }
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");
9987
+ const DAEMON_PID_PATH = resolve18(systemDir, "daemon.pid");
9988
+ const DAEMON_JSON_PATH = resolve18(systemDir, "daemon.json");
9989
+ const DAEMON_TOKEN_PATH = resolve18(systemDir, "daemon-token");
10246
9990
  mkdirSync10(home, { recursive: true });
10247
9991
  ensureSystemDir();
10248
9992
  const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-WZSXHHRJ.js");
@@ -10271,6 +10015,12 @@ async function startDaemon(opts) {
10271
10015
  } catch (err) {
10272
10016
  logger_default.warn("session\u2192thread config migration failed", logger_default.errorData(err));
10273
10017
  }
10018
+ try {
10019
+ const { migrateNamePlaceholders } = await import("./migrate-name-placeholder-C7NCNLWF.js");
10020
+ await migrateNamePlaceholders();
10021
+ } catch (err) {
10022
+ logger_default.warn("{{name}} placeholder migration failed", logger_default.errorData(err));
10023
+ }
10274
10024
  const { initSandbox } = await import("./sandbox-NNWCTHRI.js");
10275
10025
  await initSandbox();
10276
10026
  try {
@@ -10344,7 +10094,7 @@ async function startDaemon(opts) {
10344
10094
  process.env.VOLUTE_DAEMON_TOKEN = token;
10345
10095
  process.env.VOLUTE_DAEMON_PORT = String(daemonPort);
10346
10096
  process.env.VOLUTE_DAEMON_HOSTNAME = hostname;
10347
- writeFileSync10(DAEMON_PID_PATH, myPid, { mode: 420 });
10097
+ writeFileSync11(DAEMON_PID_PATH, myPid, { mode: 420 });
10348
10098
  const daemonConfig = { port, hostname };
10349
10099
  if (internalPort) daemonConfig.internalPort = internalPort;
10350
10100
  if (tls) daemonConfig.tls = true;
@@ -10371,7 +10121,7 @@ async function startDaemon(opts) {
10371
10121
  const orphanedTurns = await completeOrphanedTurns();
10372
10122
  summarizeOrphanedTurns(orphanedTurns);
10373
10123
  try {
10374
- const { reconcileVariants } = await import("./variant-cleanup-STC6B5HR.js");
10124
+ const { reconcileVariants } = await import("./variant-cleanup-QHGGY7ND.js");
10375
10125
  await reconcileVariants();
10376
10126
  } catch (err) {
10377
10127
  logger_default.error("failed to reconcile variants", logger_default.errorData(err));
@@ -10407,16 +10157,17 @@ async function startDaemon(opts) {
10407
10157
  try {
10408
10158
  const { isSetupComplete: isSetupComplete2, getSpiritName: getSpiritName2 } = await import("./setup-WZSXHHRJ.js");
10409
10159
  if (isSetupComplete2()) {
10410
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-4ECYIVGE.js");
10411
- const { startSpiritFull } = await import("./mind-service-E26ZQXDS.js");
10160
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-Q5VGFWUI.js");
10161
+ const { startSpiritFull } = await import("./mind-service-7EFIJ3ZA.js");
10412
10162
  await ensureSpiritProject();
10413
10163
  const spiritName = getSpiritName2();
10414
10164
  const spiritEntry = await findMind(spiritName);
10415
10165
  if (spiritEntry?.dir) {
10416
- const { registerMindDir } = await import("./delivery-router-RHANNTAC.js");
10166
+ const { registerMindDir } = await import("./delivery-router-QS4NLJR6.js");
10417
10167
  registerMindDir(spiritName, spiritEntry.dir);
10418
10168
  }
10419
10169
  await syncSpiritTemplate();
10170
+ if (spiritEntry) await notifyExtensionsSpiritReady();
10420
10171
  if (spiritEntry && !manager.isRunning(spiritName)) {
10421
10172
  await startSpiritFull(spiritName);
10422
10173
  }
@@ -10427,7 +10178,7 @@ async function startDaemon(opts) {
10427
10178
  bridgeManager.startBridges(daemonPort).catch((err) => {
10428
10179
  logger_default.warn("failed to start bridges", logger_default.errorData(err));
10429
10180
  });
10430
- import("./cloud-sync-H7EWZASD.js").then(
10181
+ import("./cloud-sync-52YNJRMR.js").then(
10431
10182
  ({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
10432
10183
  logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
10433
10184
  })
@@ -10435,12 +10186,14 @@ async function startDaemon(opts) {
10435
10186
  logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
10436
10187
  });
10437
10188
  try {
10438
- const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-LBVHH5YV.js");
10439
- backfillTemplateHashes().then(() => warnStaleTemplates()).catch((err) => {
10440
- logger_default.warn("failed to check template staleness", logger_default.errorData(err));
10441
- });
10189
+ const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-VNVFX6UP.js");
10442
10190
  notifyVersionUpdate().catch((err) => {
10443
10191
  logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
10192
+ }).then(() => backfillTemplateHashes()).then(() => warnStaleTemplates()).then(async () => {
10193
+ const { runAutoUpgrades } = await import("./auto-upgrade-HEVZKPTF.js");
10194
+ await runAutoUpgrades();
10195
+ }).catch((err) => {
10196
+ logger_default.warn("template staleness/auto-upgrade pass failed", logger_default.errorData(err));
10444
10197
  });
10445
10198
  } catch (err) {
10446
10199
  logger_default.warn("failed to initialize version notifications", logger_default.errorData(err));
@@ -10449,6 +10202,9 @@ async function startDaemon(opts) {
10449
10202
  logger_default.warn("failed to restore delivery queue", logger_default.errorData(err));
10450
10203
  });
10451
10204
  delivery.startRedrive();
10205
+ delivery.releaseGatedSweep().catch((err) => {
10206
+ logger_default.warn("failed to sweep gated messages", logger_default.errorData(err));
10207
+ });
10452
10208
  cleanExpiredSessions().catch((err) => {
10453
10209
  logger_default.warn("failed to clean expired sessions", logger_default.errorData(err));
10454
10210
  });