volute 0.51.0 → 0.53.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 (112) hide show
  1. package/dist/{accept-5NK5XJB5.js → accept-I4GVGWA3.js} +6 -3
  2. package/dist/{auth-X6EMNEGB.js → auth-KRDSM55Q.js} +3 -1
  3. package/dist/{chat-VAZ25KVT.js → chat-WK2BLM7B.js} +9 -9
  4. package/dist/{chunk-VAEZCIJC.js → chunk-23UJGVYP.js} +9 -9
  5. package/dist/{chunk-VSBMLB2W.js → chunk-5DBGQBQR.js} +138 -13
  6. package/dist/chunk-5RBEEAKW.js +58 -0
  7. package/dist/{chunk-BSHZ23MS.js → chunk-7ARONCZW.js} +25 -3
  8. package/dist/{chunk-HKP6OAJP.js → chunk-7LT3YZUR.js} +12 -0
  9. package/dist/{chunk-FXWBFDQR.js → chunk-AP2KQMMW.js} +48 -9
  10. package/dist/{chunk-DMK5ASKV.js → chunk-F6D766VQ.js} +6 -2
  11. package/dist/{chunk-VP5PFNZO.js → chunk-HASADR3E.js} +73 -20
  12. package/dist/{chunk-ICL6T4SN.js → chunk-IQ7VNWH6.js} +26 -2
  13. package/dist/{chunk-G7A5LEVI.js → chunk-LN7XJ56R.js} +6 -6
  14. package/dist/{chunk-DN3LKLYR.js → chunk-P2TBUKLW.js} +3 -3
  15. package/dist/{chunk-Q7XQLKZB.js → chunk-PIZDPY4G.js} +20 -3
  16. package/dist/{chunk-JHKBALS2.js → chunk-RMADJFA7.js} +1 -1
  17. package/dist/{chunk-7HN63Q7J.js → chunk-RQRO6PC7.js} +40 -6
  18. package/dist/{chunk-QT4D5XCI.js → chunk-TAWRJ24T.js} +1 -1
  19. package/dist/{chunk-AI47FDGL.js → chunk-UO4MZQNO.js} +1 -1
  20. package/dist/chunk-UTKUO2KY.js +102 -0
  21. package/dist/{chunk-CDKQJWGK.js → chunk-VDZ4I4ZP.js} +160 -18
  22. package/dist/cli.js +16 -16
  23. package/dist/{clock-ZT5YT6HA.js → clock-SH4NQC2F.js} +3 -3
  24. package/dist/{cloud-sync-GPXYPTIC.js → cloud-sync-MJVJ33LD.js} +11 -10
  25. package/dist/{daemon-restart-2EWBRTN5.js → daemon-restart-BIGU4OKS.js} +1 -1
  26. package/dist/daemon.js +543 -236
  27. package/dist/{delete-O7PEFQDF.js → delete-CYVZUAV2.js} +4 -0
  28. package/dist/{delivery-manager-HGVZSEES.js → delivery-manager-FDHCZ7PU.js} +7 -5
  29. package/dist/delivery-notices-65TB3CBX.js +18 -0
  30. package/dist/{echo-text-6ANI5HHN.js → echo-text-KU6G4Z7V.js} +12 -11
  31. package/dist/{extensions-NY6WST33.js → extensions-VACUKFA5.js} +11 -10
  32. package/dist/{files-BRY5RRIM.js → files-CHPNEQXN.js} +5 -4
  33. package/dist/{join-PYA36HEC.js → join-KLRU72JJ.js} +10 -2
  34. package/dist/{message-delivery-JHMAJ2GU.js → message-delivery-TUXM3AAE.js} +15 -12
  35. package/dist/{mind-WF6QIXWV.js → mind-IX5AWUST.js} +13 -13
  36. package/dist/{mind-contacts-QYU4TPEA.js → mind-contacts-BKUOUJPV.js} +5 -5
  37. package/dist/{mind-history-OGNR4L7W.js → mind-history-RVF6KJ7K.js} +83 -11
  38. package/dist/{mind-list-SYRA7CH5.js → mind-list-KNU34SUD.js} +2 -2
  39. package/dist/{mind-service-DZCUUC7J.js → mind-service-WHFBUKZE.js} +11 -10
  40. package/dist/{mind-sleep-LZAFBDZD.js → mind-sleep-D3DEYAWG.js} +5 -5
  41. package/dist/{mind-status-Z2LQFA6F.js → mind-status-BPTLQ5Z6.js} +15 -3
  42. package/dist/{package-B6O5APCZ.js → package-BN5KMU54.js} +2 -2
  43. package/dist/{prompts-NBO3HRK3.js → prompts-HNKGCAEY.js} +1 -1
  44. package/dist/{reject-TWB4QJHG.js → reject-K2A4UK2Y.js} +9 -5
  45. package/dist/{scheduler-TNBN54ZK.js → scheduler-5X5UIQOQ.js} +7 -5
  46. package/dist/{seed-K2LYODIH.js → seed-LHZ33UB4.js} +1 -1
  47. package/dist/{seed-cmd-2EWBTR6P.js → seed-cmd-U4RVJ3UA.js} +2 -2
  48. package/dist/{seed-create-VGTLU5FD.js → seed-create-OQU4KNCB.js} +1 -1
  49. package/dist/{seed-readiness-DYOVCYHN.js → seed-readiness-WJYYRRCK.js} +2 -2
  50. package/dist/{seed-sprout-PILHJ3YS.js → seed-sprout-SFUYH2RG.js} +7 -7
  51. package/dist/{send-2YKOZJJ5.js → send-FWZAMDBN.js} +35 -20
  52. package/dist/{setup-MDPSK2XG.js → setup-RVSGCIF3.js} +1 -1
  53. package/dist/skills/memory/SKILL.md +47 -43
  54. package/dist/skills/volute-mind/references/variants.md +1 -1
  55. package/dist/{skills-NH2GQKAF.js → skills-QRPU6BWY.js} +1 -1
  56. package/dist/{sleep-manager-7JXEQQ2P.js → sleep-manager-NWUCRBZB.js} +11 -10
  57. package/dist/{spirit-25JVAL3M.js → spirit-I5ECLGRI.js} +16 -2
  58. package/dist/{spirit-availability-PJDWZJ3J.js → spirit-availability-NIUUYKU2.js} +12 -11
  59. package/dist/{sprout-EXUZMIOR.js → sprout-A2PMEF7Z.js} +1 -1
  60. package/dist/{system-events-ZPSH63QX.js → system-events-YTOU66QI.js} +7 -1
  61. package/dist/{systems-VDRUZXNF.js → systems-2DIJY4RF.js} +6 -6
  62. package/dist/{turn-tracker-TAT2HGK6.js → turn-tracker-NNHS6MAR.js} +4 -2
  63. package/dist/{up-J7XCAH7P.js → up-UGBHH743.js} +1 -1
  64. package/dist/{variant-cleanup-YXN25ZVW.js → variant-cleanup-CZDLIY3F.js} +7 -5
  65. package/dist/{version-notify-EN3KJ7HH.js → version-notify-SJVAQLZI.js} +4 -4
  66. package/dist/web-assets/assets/index-BHSU9r-4.css +1 -0
  67. package/dist/web-assets/assets/index-b7G7ZsVk.js +97 -0
  68. package/dist/web-assets/index.html +2 -2
  69. package/package.json +2 -2
  70. package/templates/_base/.init/.config/prompts.json +1 -1
  71. package/templates/_base/src/lib/auto-commit.ts +49 -11
  72. package/templates/_base/src/lib/codex-session-seed.ts +463 -0
  73. package/templates/_base/src/lib/daemon-client.ts +35 -0
  74. package/templates/_base/src/lib/pi-session-seed.ts +434 -0
  75. package/templates/_base/src/lib/seed-note.ts +78 -0
  76. package/templates/_base/src/lib/session-seed.ts +375 -0
  77. package/templates/_base/src/lib/startup.ts +108 -4
  78. package/templates/claude/.init/CLAUDE.md +3 -3
  79. package/templates/claude/src/agent.ts +267 -89
  80. package/templates/claude/src/lib/hooks/pre-compact.ts +30 -5
  81. package/templates/claude/src/lib/stream-consumer.ts +13 -0
  82. package/templates/claude/src/server.ts +2 -1
  83. package/templates/codex/.init/AGENTS.md +3 -3
  84. package/templates/codex/src/agent.ts +256 -39
  85. package/templates/codex/src/server.ts +2 -1
  86. package/templates/pi/.init/MINDS.md +3 -3
  87. package/templates/pi/src/agent.ts +269 -43
  88. package/templates/pi/src/lib/event-handler.ts +11 -0
  89. package/templates/pi/src/server.ts +3 -1
  90. package/dist/web-assets/assets/index-BOyJ4Gqs.css +0 -1
  91. package/dist/web-assets/assets/index-CrxBwnzp.js +0 -95
  92. package/dist/{backup-YVGV2ZNM.js → backup-WWXVIMYQ.js} +5 -5
  93. package/dist/{bridge-YK7NTRU5.js → bridge-KU2IKSH3.js} +3 -3
  94. package/dist/{channels-FSAHOQWI.js → channels-LW5ZLMSQ.js} +3 -3
  95. package/dist/{chunk-IWAVIJO2.js → chunk-BDHKCLWK.js} +0 -0
  96. package/dist/{chunk-EEOMA4DX.js → chunk-HTB7KDRU.js} +3 -3
  97. package/dist/{create-M727VUDF.js → create-C3XZACTV.js} +3 -3
  98. package/dist/{env-IBW2QI6K.js → env-BJGM6FBA.js} +5 -5
  99. package/dist/{extension-MRYJWSMV.js → extension-B2W52WZY.js} +3 -3
  100. package/dist/{list-UQGIW7OF.js → list-R2GDWOEE.js} +3 -3
  101. package/dist/{login-RJJTCVR3.js → login-EZ4RRDQX.js} +3 -3
  102. package/dist/{login-SB5OENMV.js → login-JDOAK465.js} +4 -4
  103. package/dist/{logout-YFZAJRPY.js → logout-K6ZGAJY5.js} +3 -3
  104. package/dist/{logout-BYV433X6.js → logout-MWSIUOQU.js} +3 -3
  105. package/dist/{mind-wake-LDGPRA42.js → mind-wake-DCDFFQKX.js} +3 -3
  106. package/dist/{read-RJ5NAW5J.js → read-J5RM674N.js} +3 -3
  107. package/dist/{register-OX4HRSGC.js → register-53LBGSDC.js} +3 -3
  108. package/dist/{restart-AGEGNSYQ.js → restart-R2WFNF5K.js} +4 -4
  109. package/dist/{skill-B5OJEQUA.js → skill-DXLLSUHB.js} +5 -5
  110. package/dist/{start-MUMNFR52.js → start-XDOUAWN3.js} +4 -4
  111. package/dist/{status-MWLE3RVC.js → status-EYG4A3OK.js} +3 -3
  112. package/dist/{stop-EKERNYTX.js → stop-O62RTEMD.js} +4 -4
package/dist/daemon.js CHANGED
@@ -14,23 +14,31 @@ import {
14
14
  rejectPending,
15
15
  stageFile,
16
16
  validateFilePath
17
- } from "./chunk-DMK5ASKV.js";
17
+ } from "./chunk-F6D766VQ.js";
18
18
  import {
19
19
  findOpenClawSession,
20
20
  importOpenClawConnectors,
21
21
  importPiSession,
22
22
  parseNameFromIdentity
23
23
  } from "./chunk-I7U5EEZC.js";
24
+ import {
25
+ getMemoryStatus
26
+ } from "./chunk-5RBEEAKW.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-DN3LKLYR.js";
35
+ } from "./chunk-P2TBUKLW.js";
28
36
  import {
29
37
  isHomeOnlyArchive
30
38
  } from "./chunk-FLIEXG2T.js";
31
39
  import {
32
40
  evaluateSeedChecklist
33
- } from "./chunk-JHKBALS2.js";
41
+ } from "./chunk-RMADJFA7.js";
34
42
  import {
35
43
  isTemplateStale
36
44
  } from "./chunk-CV6BNGFK.js";
@@ -39,19 +47,14 @@ import {
39
47
  } from "./chunk-HTRSSOHY.js";
40
48
  import {
41
49
  cleanupVariant
42
- } from "./chunk-AI47FDGL.js";
43
- import {
44
- checkForUpdate,
45
- checkForUpdateCached,
46
- getCurrentVersion
47
- } from "./chunk-LAUCO2HC.js";
50
+ } from "./chunk-UO4MZQNO.js";
48
51
  import {
49
52
  PLATFORMS,
50
53
  echoTextToChannel,
51
54
  fixModelEscapes,
52
55
  getPlatformDriver,
53
56
  routeOutboundBridge
54
- } from "./chunk-BSHZ23MS.js";
57
+ } from "./chunk-7ARONCZW.js";
55
58
  import {
56
59
  getBridgeConfig,
57
60
  readBridgesConfig,
@@ -104,9 +107,10 @@ import {
104
107
  startMindFull,
105
108
  stopMindFull,
106
109
  uninstallNpmExtension,
110
+ willGateMessage,
107
111
  writeSystemsConfig
108
- } from "./chunk-VP5PFNZO.js";
109
- import "./chunk-VSBMLB2W.js";
112
+ } from "./chunk-HASADR3E.js";
113
+ import "./chunk-5DBGQBQR.js";
110
114
  import {
111
115
  applyInitFiles,
112
116
  applyTemplateHomeFiles,
@@ -119,7 +123,10 @@ import {
119
123
  import {
120
124
  getScheduler,
121
125
  initScheduler
122
- } from "./chunk-Q7XQLKZB.js";
126
+ } from "./chunk-PIZDPY4G.js";
127
+ import {
128
+ recordDeliveryFailure
129
+ } from "./chunk-UTKUO2KY.js";
123
130
  import {
124
131
  checkStaleSend,
125
132
  formatHoldNotice,
@@ -128,7 +135,7 @@ import {
128
135
  initDeliveryManager,
129
136
  isConversationId,
130
137
  publishTypingForChannels
131
- } from "./chunk-7HN63Q7J.js";
138
+ } from "./chunk-RQRO6PC7.js";
132
139
  import {
133
140
  MindStartupError,
134
141
  RestartTracker,
@@ -138,7 +145,7 @@ import {
138
145
  initMindManager,
139
146
  syncProviderToMinds,
140
147
  tryGetMindManager
141
- } from "./chunk-VAEZCIJC.js";
148
+ } from "./chunk-23UJGVYP.js";
142
149
  import {
143
150
  extractTextContent
144
151
  } from "./chunk-UDZLPR34.js";
@@ -155,13 +162,15 @@ import {
155
162
  initSummarizer,
156
163
  isoWeekKeyForDateStr,
157
164
  markErrored,
165
+ parseDbTimestamp,
158
166
  parseUtcDateTime,
159
167
  summarizeOrphanedTurns,
160
168
  summarizeTurn,
169
+ supersedeTurnSummary,
161
170
  takeErrored,
162
171
  trackToolUse,
163
172
  utcDateTimeStr
164
- } from "./chunk-CDKQJWGK.js";
173
+ } from "./chunk-VDZ4I4ZP.js";
165
174
  import {
166
175
  SEED_SKILLS,
167
176
  STANDARD_SKILLS,
@@ -183,12 +192,13 @@ import {
183
192
  syncBuiltinSkills,
184
193
  uninstallSkill,
185
194
  updateSkill
186
- } from "./chunk-QT4D5XCI.js";
195
+ } from "./chunk-TAWRJ24T.js";
187
196
  import {
188
197
  approveUser,
189
198
  changePassword,
190
199
  countAdmins,
191
200
  createUser,
201
+ deleteExternalMindUser,
192
202
  deleteMindUser as deleteMindUser2,
193
203
  deleteUser,
194
204
  getDisplayNames,
@@ -204,9 +214,11 @@ import {
204
214
  updateUserProfile,
205
215
  verifyUser,
206
216
  withSenderDisplayNames
207
- } from "./chunk-HKP6OAJP.js";
217
+ } from "./chunk-7LT3YZUR.js";
208
218
  import {
219
+ MIND_LEVEL_THREAD,
209
220
  captureReflection,
221
+ cleanExpiredEvents,
210
222
  clearDeliveredEvents,
211
223
  deliverEvent,
212
224
  drainEvents,
@@ -217,7 +229,7 @@ import {
217
229
  listEvents,
218
230
  parseMeta,
219
231
  recordNotice
220
- } from "./chunk-ICL6T4SN.js";
232
+ } from "./chunk-IQ7VNWH6.js";
221
233
  import {
222
234
  publish as publish2,
223
235
  subscribe as subscribe2,
@@ -230,7 +242,7 @@ import {
230
242
  getPrompt,
231
243
  getPromptIfCustom,
232
244
  substitute
233
- } from "./chunk-FXWBFDQR.js";
245
+ } from "./chunk-AP2KQMMW.js";
234
246
  import {
235
247
  isSandboxEnabled,
236
248
  wrapForSandbox
@@ -395,9 +407,9 @@ import "./chunk-K3NQKI34.js";
395
407
 
396
408
  // packages/daemon/src/daemon.ts
397
409
  import { randomBytes as randomBytes3 } from "crypto";
398
- import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
410
+ import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
399
411
  import { homedir as homedir3 } from "os";
400
- import { resolve as resolve19 } from "path";
412
+ import { resolve as resolve20 } from "path";
401
413
  import { format } from "util";
402
414
 
403
415
  // packages/daemon/src/lib/daemon/backup-manager.ts
@@ -508,7 +520,7 @@ var BackupManager = class {
508
520
  } catch (pubErr) {
509
521
  blog.error("failed to publish backup-failure activity", logger_default.errorData(pubErr));
510
522
  }
511
- const { deliverEvent: deliverEvent2 } = await import("./system-events-ZPSH63QX.js");
523
+ const { deliverEvent: deliverEvent2 } = await import("./system-events-YTOU66QI.js");
512
524
  await deliverEvent2(getSpiritName(), {
513
525
  type: "notice",
514
526
  meta: { subtype: "backup" },
@@ -732,8 +744,8 @@ var BridgeManager = class {
732
744
  if (!tracked) return;
733
745
  this.stopping.add(platform);
734
746
  this.bridges.delete(platform);
735
- await new Promise((resolve20) => {
736
- tracked.child.on("exit", () => resolve20());
747
+ await new Promise((resolve21) => {
748
+ tracked.child.on("exit", () => resolve21());
737
749
  try {
738
750
  if (tracked.child.pid) {
739
751
  process.kill(-tracked.child.pid, "SIGTERM");
@@ -744,7 +756,7 @@ var BridgeManager = class {
744
756
  if (err instanceof Error && err.code !== "ESRCH") {
745
757
  blog2.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
746
758
  }
747
- resolve20();
759
+ resolve21();
748
760
  }
749
761
  setTimeout(() => {
750
762
  try {
@@ -755,7 +767,7 @@ var BridgeManager = class {
755
767
  }
756
768
  } catch {
757
769
  }
758
- resolve20();
770
+ resolve21();
759
771
  }, 5e3);
760
772
  });
761
773
  this.stopping.delete(platform);
@@ -865,6 +877,11 @@ async function runMaintenance() {
865
877
  } catch (err) {
866
878
  logger_default.warn("maintenance: failed to clean expired logs", logger_default.errorData(err));
867
879
  }
880
+ try {
881
+ await cleanExpiredEvents();
882
+ } catch (err) {
883
+ logger_default.warn("maintenance: failed to clean expired events", logger_default.errorData(err));
884
+ }
868
885
  }
869
886
  function startMaintenanceInterval(intervalMs = MAINTENANCE_INTERVAL_MS, task = runMaintenance) {
870
887
  const handle = setInterval(() => void task(), intervalMs);
@@ -1427,10 +1444,10 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1427
1444
  stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
1428
1445
  });
1429
1446
  });
1430
- await new Promise((resolve20) => {
1447
+ await new Promise((resolve21) => {
1431
1448
  stream.onAbort(() => {
1432
1449
  unsubscribe();
1433
- resolve20();
1450
+ resolve21();
1434
1451
  });
1435
1452
  });
1436
1453
  });
@@ -1455,7 +1472,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1455
1472
  config2.name = name.trim() || void 0;
1456
1473
  writeGlobalConfig(config2);
1457
1474
  if (config2.name !== previousName) {
1458
- const { notifySpiritSystemChange } = await import("./spirit-25JVAL3M.js");
1475
+ const { notifySpiritSystemChange } = await import("./spirit-I5ECLGRI.js");
1459
1476
  await notifySpiritSystemChange();
1460
1477
  }
1461
1478
  return c.json({ name: config2.name ?? null });
@@ -1814,8 +1831,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1814
1831
  createdAt: Date.now()
1815
1832
  };
1816
1833
  oauthFlows.set(flowId, flow);
1817
- const promptPromise = needsManualCode ? new Promise((resolve20) => {
1818
- flow.resolveCode = resolve20;
1834
+ const promptPromise = needsManualCode ? new Promise((resolve21) => {
1835
+ flow.resolveCode = resolve21;
1819
1836
  }) : void 0;
1820
1837
  oauthProvider.login({
1821
1838
  onAuth: (info) => {
@@ -1854,7 +1871,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1854
1871
  existing.error = err instanceof Error ? err.message : String(err);
1855
1872
  }
1856
1873
  });
1857
- await new Promise((resolve20) => setTimeout(resolve20, 2e3));
1874
+ await new Promise((resolve21) => setTimeout(resolve21, 2e3));
1858
1875
  const state = oauthFlows.get(flowId);
1859
1876
  return c.json({
1860
1877
  flowId,
@@ -2115,8 +2132,8 @@ var app2 = new Hono2().get("/events", async (c) => {
2115
2132
  });
2116
2133
  }, 15e3);
2117
2134
  cleanups.push(() => clearInterval(keepAlive));
2118
- await new Promise((resolve20) => {
2119
- stream.onAbort(() => resolve20());
2135
+ await new Promise((resolve21) => {
2136
+ stream.onAbort(() => resolve21());
2120
2137
  });
2121
2138
  } finally {
2122
2139
  for (const cleanup of cleanups) {
@@ -2149,6 +2166,11 @@ function isNotModified(c, etag) {
2149
2166
  }
2150
2167
 
2151
2168
  // packages/daemon/src/web/api/auth.ts
2169
+ async function withExternalFlag(list) {
2170
+ if (!list.some((u) => u.user_type === "mind")) return list;
2171
+ const local = new Set((await readAllMinds()).map((m) => m.name));
2172
+ return list.map((u) => u.user_type === "mind" ? { ...u, external: !local.has(u.username) } : u);
2173
+ }
2152
2174
  function tryJoinSystem(userId) {
2153
2175
  if (!process.env.VOLUTE_DAEMON_TOKEN) return;
2154
2176
  joinSystemChannel(userId).catch(() => {
@@ -2302,9 +2324,9 @@ var admin = new Hono3().use(authMiddleware).get("/users", async (c) => {
2302
2324
  }
2303
2325
  const type = c.req.query("type");
2304
2326
  if (type === "human" || type === "mind") {
2305
- return c.json(await listUsersByType(type));
2327
+ return c.json(await withExternalFlag(await listUsersByType(type)));
2306
2328
  }
2307
- return c.json(await listUsers());
2329
+ return c.json(await withExternalFlag(await listUsers()));
2308
2330
  }).get("/users/pending", async (c) => {
2309
2331
  const user = c.get("user");
2310
2332
  if (user.role !== "admin") return c.json({ error: "Forbidden" }, 403);
@@ -2401,7 +2423,11 @@ var admin = new Hono3().use(authMiddleware).get("/users", async (c) => {
2401
2423
  if (adminCount <= 1) return c.json({ error: "Cannot delete the last admin" }, 400);
2402
2424
  }
2403
2425
  if (target.user_type === "mind") {
2404
- return c.json({ error: "Use the mind deletion API to delete minds" }, 400);
2426
+ if (await findMind(target.username)) {
2427
+ return c.json({ error: "Use the mind deletion API to delete minds" }, 400);
2428
+ }
2429
+ await deleteExternalMindUser(id);
2430
+ return c.json({ ok: true });
2405
2431
  }
2406
2432
  await deleteUser(id);
2407
2433
  return c.json({ ok: true });
@@ -2658,6 +2684,21 @@ async function fanOutToMinds(opts) {
2658
2684
  const manager = getMindManager();
2659
2685
  const sm = getSleepManagerIfReady();
2660
2686
  const registeredMinds = new Set((await readAllMinds()).map((m) => m.name));
2687
+ const senderIsMind = registeredMinds.has(opts.senderName);
2688
+ let senderCtx;
2689
+ const reportFailure = (reason) => {
2690
+ if (!senderIsMind) return;
2691
+ senderCtx ??= getBaseName(opts.senderName).then((base) => ({
2692
+ base,
2693
+ channel: buildVoluteSlug({
2694
+ participants,
2695
+ mindUsername: base,
2696
+ conversationId: opts.conversationId,
2697
+ ...opts.slugExtra
2698
+ })
2699
+ }));
2700
+ 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)));
2701
+ };
2661
2702
  const targetMinds = mindParticipants.map((ap) => {
2662
2703
  const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
2663
2704
  if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
@@ -2666,21 +2707,46 @@ async function fanOutToMinds(opts) {
2666
2707
  logger_default.warn(
2667
2708
  `fan-out: skipping ${ap.username} (not running) for conversation ${opts.conversationId}`
2668
2709
  );
2710
+ reportFailure(`${ap.username} is not running`);
2711
+ } else if (ap.userType === "mind") {
2712
+ logger_default.info(
2713
+ `fan-out: skipping ${ap.username} (no registry row \u2014 external mind or stale participant) for conversation ${opts.conversationId}`
2714
+ );
2669
2715
  }
2670
2716
  if (ap.userType === "system") {
2671
- import("./spirit-availability-PJDWZJ3J.js").then(({ ensureSpiritAvailable: ensureSpiritAvailable2 }) => ensureSpiritAvailable2()).catch((err) => logger_default.warn("fan-out: on-demand spirit start failed", logger_default.errorData(err)));
2717
+ import("./spirit-availability-NIUUYKU2.js").then(({ ensureSpiritAvailable: ensureSpiritAvailable2 }) => ensureSpiritAvailable2()).catch((err) => logger_default.warn("fan-out: on-demand spirit start failed", logger_default.errorData(err)));
2672
2718
  }
2673
2719
  }
2674
2720
  return null;
2675
2721
  }).filter((n) => n !== null && n !== opts.senderName);
2676
- for (const mindName of targetMinds) {
2677
- const target = opts.targetName ? opts.targetName(mindName) : mindName;
2678
- const channel = buildVoluteSlug({
2722
+ const targets = targetMinds.map((mindName) => ({
2723
+ mindName,
2724
+ target: opts.targetName ? opts.targetName(mindName) : mindName,
2725
+ channel: buildVoluteSlug({
2679
2726
  participants,
2680
2727
  mindUsername: mindName,
2681
2728
  conversationId: opts.conversationId,
2682
2729
  ...opts.slugExtra
2683
- });
2730
+ })
2731
+ }));
2732
+ const gatedRecipients = (await Promise.all(
2733
+ targets.map(async ({ mindName, target, channel }) => {
2734
+ if (sm?.isSleeping(mindName)) return null;
2735
+ try {
2736
+ const gated = await willGateMessage(target, {
2737
+ channel,
2738
+ sender: opts.senderName,
2739
+ isDM,
2740
+ participantCount: participants.length
2741
+ });
2742
+ return gated ? mindName : null;
2743
+ } catch (err) {
2744
+ logger_default.warn(`fan-out: will-gate check failed for ${target}`, logger_default.errorData(err));
2745
+ return null;
2746
+ }
2747
+ })
2748
+ )).filter((n) => n !== null);
2749
+ for (const { mindName, target, channel } of targets) {
2684
2750
  const typingMap = getTypingMap();
2685
2751
  const currentlyTyping = typingMap.get(channel).filter((name) => participantNames.includes(name));
2686
2752
  deliverMessage(target, {
@@ -2692,10 +2758,20 @@ async function fanOutToMinds(opts) {
2692
2758
  participantCount: participants.length,
2693
2759
  isDM,
2694
2760
  ...currentlyTyping.length > 0 ? { typing: currentlyTyping } : {}
2695
- }).catch((err) => {
2696
- logger_default.warn(`fan-out delivery failed for ${target}`, logger_default.errorData(err));
2697
- });
2761
+ }).then(
2762
+ (ok) => {
2763
+ if (!ok) {
2764
+ logger_default.warn(`fan-out delivery failed for ${target} (message not delivered)`);
2765
+ reportFailure(`the delivery to ${mindName} failed`);
2766
+ }
2767
+ },
2768
+ (err) => {
2769
+ logger_default.warn(`fan-out delivery failed for ${target}`, logger_default.errorData(err));
2770
+ reportFailure(`the delivery to ${mindName} failed`);
2771
+ }
2772
+ );
2698
2773
  }
2774
+ return { gatedRecipients };
2699
2775
  }
2700
2776
 
2701
2777
  // packages/daemon/src/web/api/bridges.ts
@@ -3053,12 +3129,18 @@ import { resolve as resolve4 } from "path";
3053
3129
  import { Hono as Hono10 } from "hono";
3054
3130
  async function notifyMind(mindName, message) {
3055
3131
  const entry = await findMind(mindName);
3056
- if (!entry) return;
3057
- try {
3058
- const { deliverEvent: deliverEvent2 } = await import("./system-events-ZPSH63QX.js");
3059
- await deliverEvent2(mindName, { type: "file-share", body: message });
3132
+ if (!entry) return false;
3133
+ try {
3134
+ const { deliverEvent: deliverEvent2 } = await import("./system-events-YTOU66QI.js");
3135
+ const result = await deliverEvent2(mindName, { type: "file-share", body: message });
3136
+ if (result.delivered) return true;
3137
+ if (result.id == null) return false;
3138
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
3139
+ const { getBaseName: getBaseName2 } = await import("./registry-TRC2HJXR.js");
3140
+ return getSleepManagerIfReady2()?.isSleeping(await getBaseName2(mindName)) ?? false;
3060
3141
  } catch (err) {
3061
3142
  logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
3143
+ return false;
3062
3144
  }
3063
3145
  }
3064
3146
  var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
@@ -3099,11 +3181,11 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
3099
3181
  const filename = body.filePath;
3100
3182
  const sizeStr = formatFileSize(content.length);
3101
3183
  const { id } = stageFile(body.targetMind, senderName, filename, content, body.filePath);
3102
- await notifyMind(
3184
+ const notified = await notifyMind(
3103
3185
  body.targetMind,
3104
3186
  `[file] ${senderName} sent ${filename} (${sizeStr}) \u2014 run: volute chat accept ${id}`
3105
3187
  );
3106
- return c.json({ status: "pending", id }, 200);
3188
+ return c.json({ status: "pending", id, notified }, 200);
3107
3189
  }).get("/:name/files/pending", requireSelf(), async (c) => {
3108
3190
  const name = c.req.param("name");
3109
3191
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
@@ -3128,8 +3210,8 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
3128
3210
  }
3129
3211
  return c.json({ error: `Failed to accept file: ${message}` }, 500);
3130
3212
  }
3131
- await notifyMind(result.sender, `[file] ${name} accepted ${result.filename}`);
3132
- return c.json({ ok: true, destPath: result.destPath });
3213
+ const notified = await notifyMind(result.sender, `[file] ${name} accepted ${result.filename}`);
3214
+ return c.json({ ok: true, destPath: result.destPath, notified });
3133
3215
  }).post("/:name/files/reject", requireSelf(), async (c) => {
3134
3216
  const name = c.req.param("name");
3135
3217
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
@@ -3145,8 +3227,8 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
3145
3227
  }
3146
3228
  return c.json({ error: `Failed to reject file: ${message}` }, 500);
3147
3229
  }
3148
- await notifyMind(result.sender, `[file] ${name} rejected ${result.filename}`);
3149
- return c.json({ ok: true });
3230
+ const notified = await notifyMind(result.sender, `[file] ${name} rejected ${result.filename}`);
3231
+ return c.json({ ok: true, notified });
3150
3232
  }).post("/:name/files/stage", requireSelf(), async (c) => {
3151
3233
  const receiverName = c.req.param("name");
3152
3234
  const receiverEntry = await findMind(receiverName);
@@ -3169,11 +3251,11 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
3169
3251
  }
3170
3252
  const sizeStr = formatFileSize(content.length);
3171
3253
  const { id } = stageFile(receiverName, body.sender, body.filename, content, body.filename);
3172
- await notifyMind(
3254
+ const notified = await notifyMind(
3173
3255
  receiverName,
3174
3256
  `[file] ${body.sender} sent ${body.filename} (${sizeStr}) \u2014 run: volute chat accept ${id}`
3175
3257
  );
3176
- return c.json({ status: "pending", id }, 200);
3258
+ return c.json({ status: "pending", id, notified }, 200);
3177
3259
  });
3178
3260
  var file_sharing_default = app8;
3179
3261
 
@@ -4026,9 +4108,9 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
4026
4108
  stream.onAbort(() => {
4027
4109
  tail.kill();
4028
4110
  });
4029
- await new Promise((resolve20) => {
4030
- tail.on("exit", resolve20);
4031
- stream.onAbort(resolve20);
4111
+ await new Promise((resolve21) => {
4112
+ tail.on("exit", resolve21);
4113
+ stream.onAbort(resolve21);
4032
4114
  });
4033
4115
  });
4034
4116
  }).get("/:name/logs/tail", requireSelf(), async (c) => {
@@ -4046,8 +4128,8 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
4046
4128
  tail.stdout.on("data", (data) => {
4047
4129
  output += data.toString();
4048
4130
  });
4049
- await new Promise((resolve20) => {
4050
- tail.on("exit", resolve20);
4131
+ await new Promise((resolve21) => {
4132
+ tail.on("exit", resolve21);
4051
4133
  });
4052
4134
  return c.text(output);
4053
4135
  });
@@ -4147,7 +4229,7 @@ import {
4147
4229
  rmSync as rmSync3,
4148
4230
  writeFileSync as writeFileSync8
4149
4231
  } from "fs";
4150
- import { relative, resolve as resolve12 } from "path";
4232
+ import { relative, resolve as resolve13 } from "path";
4151
4233
  import { zValidator as zValidator6 } from "@hono/zod-validator";
4152
4234
  import { and as and5, desc as desc3, eq as eq5, sql as sql3 } from "drizzle-orm";
4153
4235
  import { Hono as Hono16 } from "hono";
@@ -4675,6 +4757,8 @@ function deriveThinkingLevel(config2) {
4675
4757
  }
4676
4758
 
4677
4759
  // packages/daemon/src/lib/mind/variants.ts
4760
+ import { statSync as statSync3 } from "fs";
4761
+ import { resolve as resolve11 } from "path";
4678
4762
  var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
4679
4763
  function validateBranchName(branch) {
4680
4764
  if (!SAFE_BRANCH_RE.test(branch)) {
@@ -4687,6 +4771,55 @@ function validateBranchName(branch) {
4687
4771
  }
4688
4772
  var MERGE_EXCLUDED_PATHS = ["home/MEMORY.md", "home/memory/journal/"];
4689
4773
  var MAX_DELTA_CHARS = 12e3;
4774
+ var NEVER_REPORT_PREFIXES = ["home/.claude/", "home/.pi/", "home/.agents/"];
4775
+ var MAX_LISTED_UNRESOLVED_FILES = 20;
4776
+ async function findUnresolvedHomeFiles(cwd) {
4777
+ const raw = await gitExec(
4778
+ ["ls-files", "--others", "--ignored", "--exclude-standard", "-z", "--", "home"],
4779
+ { cwd }
4780
+ );
4781
+ const allPaths = raw.split("\0").filter((path) => path && !NEVER_REPORT_PREFIXES.some((prefix) => path.startsWith(prefix)));
4782
+ const files = [];
4783
+ let totalBytes = 0;
4784
+ for (const path of allPaths) {
4785
+ let bytes = -1;
4786
+ try {
4787
+ bytes = statSync3(resolve11(cwd, path)).size;
4788
+ } catch {
4789
+ }
4790
+ if (bytes > 0) totalBytes += bytes;
4791
+ if (files.length < MAX_LISTED_UNRESOLVED_FILES) files.push({ path, bytes });
4792
+ }
4793
+ return { files, totalCount: allPaths.length, totalBytes };
4794
+ }
4795
+ function formatBytes(bytes) {
4796
+ if (bytes < 0) return "size unknown";
4797
+ if (bytes < 1024) return `${bytes} B`;
4798
+ const units = ["KB", "MB", "GB", "TB"];
4799
+ let value = bytes / 1024;
4800
+ for (const unit of units) {
4801
+ if (value < 1024) return `${value.toFixed(1)} ${unit}`;
4802
+ value /= 1024;
4803
+ }
4804
+ return `${value.toFixed(1)} PB`;
4805
+ }
4806
+ function formatUnresolvedHomeFilesMessage(variantName, result) {
4807
+ const lines = result.files.map(
4808
+ (f) => ` - ${f.path.replace(/^home\//, "")} (${formatBytes(f.bytes)})`
4809
+ );
4810
+ const remaining = result.totalCount - result.files.length;
4811
+ if (remaining > 0) lines.push(` ...and ${remaining} more`);
4812
+ 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)}):
4813
+
4814
+ ${lines.join("\n")}
4815
+
4816
+ Before retrying the join, resolve each file in the variant \u2014 one of:
4817
+ 1. Track it: git add -f -- <path> (then commit it, or let auto-commit pick it up next turn)
4818
+ 2. Copy it out of home/ to somewhere safe, then delete it
4819
+ 3. Delete it if it's disposable
4820
+
4821
+ Once resolved, retry the join. To join anyway and discard everything listed above, pass discardUnresolved: true.`;
4822
+ }
4690
4823
  async function mergeVariantExcludingMemory(projectRoot, variantBranch) {
4691
4824
  const opts = { cwd: projectRoot };
4692
4825
  let memoryDelta = "";
@@ -4889,7 +5022,7 @@ async function waitForImagegenJob(mind, id, timeoutMs) {
4889
5022
  import { randomUUID } from "crypto";
4890
5023
  import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
4891
5024
  import { homedir } from "os";
4892
- import { resolve as resolve11 } from "path";
5025
+ import { resolve as resolve12 } from "path";
4893
5026
  function convertSession(opts) {
4894
5027
  const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
4895
5028
  const sessionId = randomUUID();
@@ -5005,9 +5138,9 @@ function convertSession(opts) {
5005
5138
  }
5006
5139
  }
5007
5140
  const projectId = opts.projectDir.replace(/\//g, "-");
5008
- const sdkDir = resolve11(homedir(), ".claude", "projects", projectId);
5141
+ const sdkDir = resolve12(homedir(), ".claude", "projects", projectId);
5009
5142
  mkdirSync5(sdkDir, { recursive: true });
5010
- const sdkPath = resolve11(sdkDir, `${sessionId}.jsonl`);
5143
+ const sdkPath = resolve12(sdkDir, `${sessionId}.jsonl`);
5011
5144
  writeFileSync7(sdkPath, `${sdkEvents.join("\n")}
5012
5145
  `);
5013
5146
  console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
@@ -5059,12 +5192,6 @@ function convertAssistantContent(content) {
5059
5192
  return result;
5060
5193
  }
5061
5194
 
5062
- // packages/daemon/src/lib/util/time.ts
5063
- function parseDbTimestamp(ts) {
5064
- if (/[zZ]$|[+-]\d\d:?\d\d$/.test(ts)) return new Date(ts);
5065
- return /* @__PURE__ */ new Date(`${ts.replace(" ", "T")}Z`);
5066
- }
5067
-
5068
5195
  // packages/daemon/src/web/api/minds.ts
5069
5196
  var _lastActiveCache = { map: /* @__PURE__ */ new Map(), ts: 0 };
5070
5197
  var _LAST_ACTIVE_TTL = 6e4;
@@ -5074,7 +5201,7 @@ async function getMindStatus(name, port, registryRunning, seed) {
5074
5201
  let status = "stopped";
5075
5202
  let wakeAt = null;
5076
5203
  try {
5077
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7JXEQQ2P.js");
5204
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
5078
5205
  const sleepManager = getSleepManagerIfReady2();
5079
5206
  if (sleepManager?.isSleeping(name)) {
5080
5207
  status = "sleeping";
@@ -5100,10 +5227,12 @@ async function getMindStatus(name, port, registryRunning, seed) {
5100
5227
  }
5101
5228
  const lastError = await latestFailureEvent(name);
5102
5229
  const seedChecklist = seed?.stage === "seed" ? evaluateSeedChecklist(seed.dir ?? mindDir(name)) : void 0;
5230
+ const memory = getMemoryStatus(seed?.dir ?? mindDir(name));
5103
5231
  return {
5104
5232
  status,
5105
5233
  wakeAt,
5106
5234
  lastError,
5235
+ memory,
5107
5236
  channels: channels2,
5108
5237
  displayName: config2?.profile?.displayName,
5109
5238
  description: config2?.profile?.description,
@@ -5126,6 +5255,7 @@ function toPublicMind(entry, status, extras) {
5126
5255
  // Kind/reason/at only — `detail` can embed the raw error string (unknown
5127
5256
  // classifications), which is mind-private (served behind requireSelf).
5128
5257
  lastError: status.lastError ? { kind: status.lastError.kind, reason: status.lastError.reason, at: status.lastError.at } : null,
5258
+ memory: status.memory,
5129
5259
  channels: status.channels,
5130
5260
  displayName: status.displayName,
5131
5261
  description: status.description,
@@ -5153,7 +5283,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
5153
5283
  await gitExec(["commit", "-m", "initial commit"], opts);
5154
5284
  }
5155
5285
  async function updateTemplateBranch(projectRoot, template, mindName) {
5156
- const tempWorktree = resolve12(projectRoot, ".variants", "_template_update");
5286
+ const tempWorktree = resolve13(projectRoot, ".variants", "_template_update");
5157
5287
  let branchExists = false;
5158
5288
  try {
5159
5289
  await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
@@ -5185,15 +5315,15 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
5185
5315
  });
5186
5316
  }
5187
5317
  copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
5188
- const initDir = resolve12(tempWorktree, ".init");
5318
+ const initDir = resolve13(tempWorktree, ".init");
5189
5319
  if (existsSync8(initDir)) {
5190
5320
  rmSync3(initDir, { recursive: true, force: true });
5191
5321
  }
5192
- const homeDir = resolve12(tempWorktree, "home");
5322
+ const homeDir = resolve13(tempWorktree, "home");
5193
5323
  if (existsSync8(homeDir)) {
5194
5324
  for (const entry of readdirSync2(homeDir)) {
5195
5325
  if (entry !== "VOLUTE.md") {
5196
- rmSync3(resolve12(homeDir, entry), { recursive: true, force: true });
5326
+ rmSync3(resolve13(homeDir, entry), { recursive: true, force: true });
5197
5327
  }
5198
5328
  }
5199
5329
  }
@@ -5252,7 +5382,7 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
5252
5382
  let switchWarning;
5253
5383
  if (templateChanged) {
5254
5384
  try {
5255
- applyTemplateHomeFiles(resolve12(dir, "home"), template);
5385
+ applyTemplateHomeFiles(resolve13(dir, "home"), template);
5256
5386
  const migratedSkills = migrateSkillsToTemplate(dir, oldTemplate, template);
5257
5387
  await gitExec(["add", "home/"], { cwd: dir });
5258
5388
  try {
@@ -5308,7 +5438,7 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
5308
5438
  return { ok: true, warning: switchWarning };
5309
5439
  }
5310
5440
  async function importFromArchive(c, tempDir, nameOverride, manifest) {
5311
- const extractedMindDir = resolve12(tempDir, "mind");
5441
+ const extractedMindDir = resolve13(tempDir, "mind");
5312
5442
  if (!existsSync8(extractedMindDir)) {
5313
5443
  return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
5314
5444
  }
@@ -5335,9 +5465,9 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
5335
5465
  }
5336
5466
  const state = stateDir(name);
5337
5467
  mkdirSync6(state, { recursive: true });
5338
- const envJson = resolve12(tempDir, "state/env.json");
5468
+ const envJson = resolve13(tempDir, "state/env.json");
5339
5469
  if (existsSync8(envJson)) {
5340
- cpSync(envJson, resolve12(state, "env.json"));
5470
+ cpSync(envJson, resolve13(state, "env.json"));
5341
5471
  }
5342
5472
  const port = await nextPort();
5343
5473
  await addMind(name, port, manifest.stage, manifest.template);
@@ -5346,23 +5476,23 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
5346
5476
  } catch (err) {
5347
5477
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
5348
5478
  }
5349
- const homeDir = resolve12(dest, "home");
5479
+ const homeDir = resolve13(dest, "home");
5350
5480
  ensureVoluteGroup();
5351
5481
  createMindUser(name, homeDir);
5352
5482
  await chownMindDir(dest, name);
5353
5483
  await npmInstallAsMind(dest, name);
5354
5484
  await importHistoryFromArchive(name, tempDir);
5355
5485
  importSessionsFromArchive(dest, tempDir);
5356
- if (!existsSync8(resolve12(dest, ".git"))) {
5486
+ if (!existsSync8(resolve13(dest, ".git"))) {
5357
5487
  try {
5358
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve12(dest, "home") } : void 0;
5488
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dest, "home") } : void 0;
5359
5489
  await gitExec(["init"], { cwd: dest, mindName: name, env });
5360
5490
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
5361
5491
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
5362
5492
  await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
5363
5493
  } catch (err) {
5364
5494
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
5365
- rmSync3(resolve12(dest, ".git"), { recursive: true, force: true });
5495
+ rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
5366
5496
  }
5367
5497
  }
5368
5498
  await chownMindDir(dest, name);
@@ -5395,22 +5525,22 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5395
5525
  try {
5396
5526
  copyTemplateToDir(composedDir, dest, name, templateManifest);
5397
5527
  applyInitFiles(dest);
5398
- const extractedHome = resolve12(extractedMindDir, "home");
5528
+ const extractedHome = resolve13(extractedMindDir, "home");
5399
5529
  if (existsSync8(extractedHome)) {
5400
- cpSync(extractedHome, resolve12(dest, "home"), { recursive: true });
5530
+ cpSync(extractedHome, resolve13(dest, "home"), { recursive: true });
5401
5531
  }
5402
- const extractedMindInternal = resolve12(extractedMindDir, ".mind");
5532
+ const extractedMindInternal = resolve13(extractedMindDir, ".mind");
5403
5533
  if (existsSync8(extractedMindInternal)) {
5404
- cpSync(extractedMindInternal, resolve12(dest, ".mind"), { recursive: true });
5534
+ cpSync(extractedMindInternal, resolve13(dest, ".mind"), { recursive: true });
5405
5535
  }
5406
- const identityDir = resolve12(dest, ".mind/identity");
5536
+ const identityDir = resolve13(dest, ".mind/identity");
5407
5537
  let publicKeyPem;
5408
- if (!manifest.includes.identity || !existsSync8(resolve12(identityDir, "private.pem"))) {
5538
+ if (!manifest.includes.identity || !existsSync8(resolve13(identityDir, "private.pem"))) {
5409
5539
  ({ publicKeyPem } = generateIdentity(dest));
5410
5540
  } else {
5411
- publicKeyPem = readFileSync9(resolve12(identityDir, "public.pem"), "utf-8");
5541
+ publicKeyPem = readFileSync9(resolve13(identityDir, "public.pem"), "utf-8");
5412
5542
  }
5413
- const promptsPath = resolve12(dest, "home/.config/prompts.json");
5543
+ const promptsPath = resolve13(dest, "home/.config/prompts.json");
5414
5544
  if (!existsSync8(promptsPath)) {
5415
5545
  const mindPrompts = await getMindPromptDefaults();
5416
5546
  writeFileSync8(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
@@ -5418,13 +5548,13 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5418
5548
  }
5419
5549
  const state = stateDir(name);
5420
5550
  mkdirSync6(state, { recursive: true });
5421
- const envJson = resolve12(tempDir, "state/env.json");
5551
+ const envJson = resolve13(tempDir, "state/env.json");
5422
5552
  if (existsSync8(envJson)) {
5423
- cpSync(envJson, resolve12(state, "env.json"));
5553
+ cpSync(envJson, resolve13(state, "env.json"));
5424
5554
  }
5425
5555
  const port = await nextPort();
5426
5556
  await addMind(name, port, manifest.stage, manifest.template);
5427
- const homeDir = resolve12(dest, "home");
5557
+ const homeDir = resolve13(dest, "home");
5428
5558
  ensureVoluteGroup();
5429
5559
  createMindUser(name, homeDir);
5430
5560
  await chownMindDir(dest, name);
@@ -5437,7 +5567,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5437
5567
  await initTemplateBranch(dest, composedDir, templateManifest, name, env);
5438
5568
  } catch (err) {
5439
5569
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
5440
- rmSync3(resolve12(dest, ".git"), { recursive: true, force: true });
5570
+ rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
5441
5571
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
5442
5572
  }
5443
5573
  const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
@@ -5480,7 +5610,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
5480
5610
  }
5481
5611
  }
5482
5612
  async function importHistoryFromArchive(name, tempDir) {
5483
- const historyJsonl = resolve12(tempDir, "history.jsonl");
5613
+ const historyJsonl = resolve13(tempDir, "history.jsonl");
5484
5614
  if (!existsSync8(historyJsonl)) return;
5485
5615
  try {
5486
5616
  const db = await getDb();
@@ -5521,13 +5651,13 @@ async function importHistoryFromArchive(name, tempDir) {
5521
5651
  }
5522
5652
  }
5523
5653
  function importSessionsFromArchive(dest, tempDir) {
5524
- const sessionsDir = resolve12(tempDir, "sessions");
5654
+ const sessionsDir = resolve13(tempDir, "sessions");
5525
5655
  if (!existsSync8(sessionsDir)) return;
5526
5656
  try {
5527
- const destSessions = resolve12(dest, ".mind/sessions");
5657
+ const destSessions = resolve13(dest, ".mind/sessions");
5528
5658
  mkdirSync6(destSessions, { recursive: true });
5529
5659
  for (const file of readdirSync2(sessionsDir)) {
5530
- cpSync(resolve12(sessionsDir, file), resolve12(destSessions, file));
5660
+ cpSync(resolve13(sessionsDir, file), resolve13(destSessions, file));
5531
5661
  }
5532
5662
  } catch (err) {
5533
5663
  logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
@@ -5644,7 +5774,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5644
5774
  writeVoluteConfig(dest, config2);
5645
5775
  const modelId = body.model ?? cog?.model;
5646
5776
  effectiveModel = modelId ? qualifyModelId(modelId) : void 0;
5647
- const sdkConfigPath = resolve12(dest, "home/.config/config.json");
5777
+ const sdkConfigPath = resolve13(dest, "home/.config/config.json");
5648
5778
  if (modelId || cog?.compaction) {
5649
5779
  const existing = existsSync8(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
5650
5780
  if (modelId) {
@@ -5659,7 +5789,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5659
5789
  }
5660
5790
  const mindPrompts = await getMindPromptDefaults();
5661
5791
  writeFileSync8(
5662
- resolve12(dest, "home/.config/prompts.json"),
5792
+ resolve13(dest, "home/.config/prompts.json"),
5663
5793
  `${JSON.stringify(mindPrompts, null, 2)}
5664
5794
  `
5665
5795
  );
@@ -5671,7 +5801,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5671
5801
  } catch (err) {
5672
5802
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
5673
5803
  }
5674
- const homeDir = resolve12(dest, "home");
5804
+ const homeDir = resolve13(dest, "home");
5675
5805
  ensureVoluteGroup();
5676
5806
  createMindUser(name, homeDir);
5677
5807
  await chownMindDir(dest, name);
@@ -5684,7 +5814,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
5684
5814
  await initTemplateBranch(dest, composedDir, manifest, name, env);
5685
5815
  } catch (err) {
5686
5816
  logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
5687
- rmSync3(resolve12(dest, ".git"), { recursive: true, force: true });
5817
+ rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
5688
5818
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
5689
5819
  }
5690
5820
  if (body.stage === "seed") {
@@ -5693,7 +5823,7 @@ Your creator described you as: "${body.description}"
5693
5823
  ` : "";
5694
5824
  const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
5695
5825
  const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
5696
- writeFileSync8(resolve12(dest, "home/SOUL.md"), seedSoul);
5826
+ writeFileSync8(resolve13(dest, "home/SOUL.md"), seedSoul);
5697
5827
  }
5698
5828
  let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
5699
5829
  if (body.stage === "seed" && !body.skills) {
@@ -5719,7 +5849,7 @@ Your creator described you as: "${body.description}"
5719
5849
  const spiritName = getSpiritName();
5720
5850
  const spiritEntry = await findMind(spiritName);
5721
5851
  if (spiritEntry) {
5722
- const { spiritDir } = await import("./spirit-25JVAL3M.js");
5852
+ const { spiritDir } = await import("./spirit-I5ECLGRI.js");
5723
5853
  const sDir = spiritEntry.dir ?? spiritDir();
5724
5854
  const spiritConfig = readVoluteConfig(sDir) ?? {};
5725
5855
  const schedules = spiritConfig.schedules ?? [];
@@ -5734,7 +5864,7 @@ Your creator described you as: "${body.description}"
5734
5864
  });
5735
5865
  spiritConfig.schedules = schedules;
5736
5866
  writeVoluteConfig(sDir, spiritConfig);
5737
- const { getScheduler: getScheduler2 } = await import("./scheduler-TNBN54ZK.js");
5867
+ const { getScheduler: getScheduler2 } = await import("./scheduler-5X5UIQOQ.js");
5738
5868
  getScheduler2().loadSchedules(spiritName, sDir);
5739
5869
  }
5740
5870
  }
@@ -5745,11 +5875,11 @@ Your creator described you as: "${body.description}"
5745
5875
  if (body.stage !== "seed") {
5746
5876
  const customSoul = await getPromptIfCustom("default_soul");
5747
5877
  if (customSoul) {
5748
- writeFileSync8(resolve12(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
5878
+ writeFileSync8(resolve13(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
5749
5879
  }
5750
5880
  const customMemory = await getPromptIfCustom("default_memory");
5751
5881
  if (customMemory) {
5752
- writeFileSync8(resolve12(dest, "home/MEMORY.md"), customMemory);
5882
+ writeFileSync8(resolve13(dest, "home/MEMORY.md"), customMemory);
5753
5883
  }
5754
5884
  }
5755
5885
  await chownMindDir(dest, name);
@@ -5810,12 +5940,12 @@ Your creator described you as: "${body.description}"
5810
5940
  return importFromArchive(c, body.archivePath, body.name, body.manifest);
5811
5941
  }
5812
5942
  const wsDir = body.workspacePath;
5813
- if (!wsDir || !existsSync8(resolve12(wsDir, "SOUL.md")) || !existsSync8(resolve12(wsDir, "IDENTITY.md"))) {
5943
+ if (!wsDir || !existsSync8(resolve13(wsDir, "SOUL.md")) || !existsSync8(resolve13(wsDir, "IDENTITY.md"))) {
5814
5944
  return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
5815
5945
  }
5816
- const soul = readFileSync9(resolve12(wsDir, "SOUL.md"), "utf-8");
5817
- const identity = readFileSync9(resolve12(wsDir, "IDENTITY.md"), "utf-8");
5818
- const userPath = resolve12(wsDir, "USER.md");
5946
+ const soul = readFileSync9(resolve13(wsDir, "SOUL.md"), "utf-8");
5947
+ const identity = readFileSync9(resolve13(wsDir, "IDENTITY.md"), "utf-8");
5948
+ const userPath = resolve13(wsDir, "USER.md");
5819
5949
  const user = existsSync8(userPath) ? readFileSync9(userPath, "utf-8") : "";
5820
5950
  const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
5821
5951
  const template = body.template ?? "claude";
@@ -5843,26 +5973,26 @@ ${user.trimEnd()}
5843
5973
  copyTemplateToDir(composedDir, dest, name, manifest);
5844
5974
  applyInitFiles(dest);
5845
5975
  const { publicKeyPem: importPublicKey } = generateIdentity(dest);
5846
- writeFileSync8(resolve12(dest, "home/SOUL.md"), mergedSoul);
5847
- const wsMemoryPath = resolve12(wsDir, "MEMORY.md");
5976
+ writeFileSync8(resolve13(dest, "home/SOUL.md"), mergedSoul);
5977
+ const wsMemoryPath = resolve13(wsDir, "MEMORY.md");
5848
5978
  const hasMemory = existsSync8(wsMemoryPath);
5849
5979
  if (hasMemory) {
5850
5980
  const existingMemory = readFileSync9(wsMemoryPath, "utf-8");
5851
5981
  writeFileSync8(
5852
- resolve12(dest, "home/MEMORY.md"),
5982
+ resolve13(dest, "home/MEMORY.md"),
5853
5983
  `${existingMemory.trimEnd()}${mergedMemoryExtra}`
5854
5984
  );
5855
5985
  } else if (user) {
5856
- writeFileSync8(resolve12(dest, "home/MEMORY.md"), `${user.trimEnd()}
5986
+ writeFileSync8(resolve13(dest, "home/MEMORY.md"), `${user.trimEnd()}
5857
5987
  `);
5858
5988
  }
5859
- const wsMemoryDir = resolve12(wsDir, "memory");
5989
+ const wsMemoryDir = resolve13(wsDir, "memory");
5860
5990
  let dailyLogCount = 0;
5861
5991
  if (existsSync8(wsMemoryDir)) {
5862
- const destMemoryDir = resolve12(dest, "home/memory");
5992
+ const destMemoryDir = resolve13(dest, "home/memory");
5863
5993
  const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
5864
5994
  for (const file of files) {
5865
- cpSync(resolve12(wsMemoryDir, file), resolve12(destMemoryDir, file));
5995
+ cpSync(resolve13(wsMemoryDir, file), resolve13(destMemoryDir, file));
5866
5996
  }
5867
5997
  dailyLogCount = files.length;
5868
5998
  }
@@ -5873,7 +6003,7 @@ ${user.trimEnd()}
5873
6003
  } catch (err) {
5874
6004
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
5875
6005
  }
5876
- const homeDir = resolve12(dest, "home");
6006
+ const homeDir = resolve13(dest, "home");
5877
6007
  ensureVoluteGroup();
5878
6008
  createMindUser(name, homeDir);
5879
6009
  await chownMindDir(dest, name);
@@ -5881,20 +6011,20 @@ ${user.trimEnd()}
5881
6011
  if (!hasMemory && dailyLogCount > 0) {
5882
6012
  await consolidateMemory(dest);
5883
6013
  }
5884
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve12(dest, "home") } : void 0;
6014
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dest, "home") } : void 0;
5885
6015
  await gitExec(["init"], { cwd: dest, mindName: name, env });
5886
6016
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
5887
6017
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
5888
6018
  await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
5889
- const sessionFile = body.sessionPath ? resolve12(body.sessionPath) : findOpenClawSession(wsDir);
6019
+ const sessionFile = body.sessionPath ? resolve13(body.sessionPath) : findOpenClawSession(wsDir);
5890
6020
  if (sessionFile && existsSync8(sessionFile)) {
5891
6021
  if (template === "pi") {
5892
6022
  importPiSession(sessionFile, dest);
5893
6023
  } else if (template === "claude") {
5894
6024
  const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
5895
- const mindRuntimeDir = resolve12(dest, ".mind");
6025
+ const mindRuntimeDir = resolve13(dest, ".mind");
5896
6026
  mkdirSync6(mindRuntimeDir, { recursive: true });
5897
- writeFileSync8(resolve12(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
6027
+ writeFileSync8(resolve13(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
5898
6028
  }
5899
6029
  }
5900
6030
  importOpenClawConnectors(name, dest);
@@ -5940,7 +6070,7 @@ ${user.trimEnd()}
5940
6070
  stage: entry.stage,
5941
6071
  dir: entry.dir
5942
6072
  });
5943
- const hasPages = existsSync8(resolve12(mindDir(entry.name), "home", "pages"));
6073
+ const hasPages = existsSync8(resolve13(mindDir(entry.name), "home", "pages"));
5944
6074
  const lastActiveAt = lastActiveMap.get(entry.name) ?? null;
5945
6075
  if (!privileged) return toPublicMind(entry, mindStatus, { hasPages, lastActiveAt });
5946
6076
  return {
@@ -5963,7 +6093,7 @@ ${user.trimEnd()}
5963
6093
  stage: entry.stage,
5964
6094
  dir: entry.dir
5965
6095
  });
5966
- const hasPages = existsSync8(resolve12(mindDir(name), "home", "pages"));
6096
+ const hasPages = existsSync8(resolve13(mindDir(name), "home", "pages"));
5967
6097
  if (!isPrivileged(c)) {
5968
6098
  return c.json(toPublicMind(entry, mindStatus, { hasPages }));
5969
6099
  }
@@ -6040,7 +6170,7 @@ ${user.trimEnd()}
6040
6170
  const manager = getMindManager();
6041
6171
  try {
6042
6172
  if (context?.type === "reload") {
6043
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7JXEQQ2P.js");
6173
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
6044
6174
  const sleepState = getSleepManagerIfReady2()?.getState(name);
6045
6175
  if (sleepState?.sleeping) {
6046
6176
  logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
@@ -6060,6 +6190,7 @@ ${user.trimEnd()}
6060
6190
  const variantEntry = await findMind(mergeVariantName);
6061
6191
  if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
6062
6192
  const projectRoot = mindDir(baseName);
6193
+ const discardUnresolved = context.discardUnresolvedHomeFiles === true;
6063
6194
  if (existsSync8(variantEntry.dir)) {
6064
6195
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
6065
6196
  if (status) {
@@ -6089,13 +6220,41 @@ ${user.trimEnd()}
6089
6220
  }
6090
6221
  const preMergeHead = (await gitExec(["rev-parse", "HEAD"], { cwd: projectRoot })).trim();
6091
6222
  const memoryDelta = await mergeVariantExcludingMemory(projectRoot, variantEntry.branch);
6092
- if (memoryDelta && context) context.memoryDelta = memoryDelta;
6093
- await cleanupVariant(mergeVariantName, baseName, projectRoot, variantEntry.dir);
6094
- if (await npmInstallNeeded(projectRoot, preMergeHead)) {
6223
+ let unresolvedNotice;
6224
+ if (existsSync8(variantEntry.dir) && !discardUnresolved) {
6095
6225
  try {
6096
- await npmInstallAsMind(projectRoot, baseName);
6097
- } catch (e) {
6098
- logger_default.error(`npm install failed after merge for ${baseName}`, logger_default.errorData(e));
6226
+ const unresolved = await findUnresolvedHomeFiles(variantEntry.dir);
6227
+ if (unresolved.totalCount > 0) {
6228
+ unresolvedNotice = formatUnresolvedHomeFilesMessage(mergeVariantName, unresolved);
6229
+ }
6230
+ } catch (err) {
6231
+ unresolvedNotice = `Could not verify variant ${mergeVariantName} has no unresolved home/ files: ${err instanceof Error ? err.message : String(err)}. Variant left intact \u2014 retry the join.`;
6232
+ }
6233
+ }
6234
+ if (unresolvedNotice) {
6235
+ logger_default.warn(
6236
+ `variant join blocked for ${baseName}: ${mergeVariantName} \u2014 ${unresolvedNotice}`
6237
+ );
6238
+ await recordNotice({
6239
+ mind: baseName,
6240
+ thread: "main",
6241
+ kind: "join_blocked",
6242
+ reason: "unresolved_variant_files",
6243
+ detail: unresolvedNotice
6244
+ });
6245
+ if (context) {
6246
+ context.type = "restart";
6247
+ context.name = void 0;
6248
+ }
6249
+ } else {
6250
+ if (memoryDelta && context) context.memoryDelta = memoryDelta;
6251
+ await cleanupVariant(mergeVariantName, baseName, projectRoot, variantEntry.dir);
6252
+ if (await npmInstallNeeded(projectRoot, preMergeHead)) {
6253
+ try {
6254
+ await npmInstallAsMind(projectRoot, baseName);
6255
+ } catch (e) {
6256
+ logger_default.error(`npm install failed after merge for ${baseName}`, logger_default.errorData(e));
6257
+ }
6099
6258
  }
6100
6259
  }
6101
6260
  }
@@ -6138,6 +6297,7 @@ ${user.trimEnd()}
6138
6297
  );
6139
6298
  return c.json({ ok: true, port: targetPort });
6140
6299
  } catch (err) {
6300
+ logger_default.error(`failed to restart mind ${name}`, logger_default.errorData(err));
6141
6301
  return c.json({ error: err instanceof Error ? err.message : "Failed to restart mind" }, 500);
6142
6302
  }
6143
6303
  }).post("/:name/stop", requireSelf(), async (c) => {
@@ -6152,13 +6312,14 @@ ${user.trimEnd()}
6152
6312
  await stopMindFull(name);
6153
6313
  return c.json({ ok: true });
6154
6314
  } catch (err) {
6315
+ logger_default.error(`failed to stop mind ${name}`, logger_default.errorData(err));
6155
6316
  return c.json({ error: err instanceof Error ? err.message : "Failed to stop mind" }, 500);
6156
6317
  }
6157
6318
  }).get("/:name/sleep", requireSelf(), async (c) => {
6158
6319
  const name = c.req.param("name");
6159
6320
  const entry = await findMind(name);
6160
6321
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6161
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7JXEQQ2P.js");
6322
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
6162
6323
  const sm = getSleepManagerIfReady2();
6163
6324
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6164
6325
  return c.json(sm.getState(name));
@@ -6166,7 +6327,7 @@ ${user.trimEnd()}
6166
6327
  const name = c.req.param("name");
6167
6328
  const entry = await findMind(name);
6168
6329
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6169
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7JXEQQ2P.js");
6330
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
6170
6331
  const sm = getSleepManagerIfReady2();
6171
6332
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6172
6333
  if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
@@ -6186,7 +6347,7 @@ ${user.trimEnd()}
6186
6347
  const name = c.req.param("name");
6187
6348
  const entry = await findMind(name);
6188
6349
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6189
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7JXEQQ2P.js");
6350
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
6190
6351
  const sm = getSleepManagerIfReady2();
6191
6352
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6192
6353
  const sleepState = sm.getState(name);
@@ -6201,7 +6362,7 @@ ${user.trimEnd()}
6201
6362
  const name = c.req.param("name");
6202
6363
  const entry = await findMind(name);
6203
6364
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6204
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7JXEQQ2P.js");
6365
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
6205
6366
  const sm = getSleepManagerIfReady2();
6206
6367
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
6207
6368
  const flushed = await sm.flushQueuedMessages(name);
@@ -6217,7 +6378,7 @@ ${user.trimEnd()}
6217
6378
  if (body.displayName !== void 0) profile.displayName = body.displayName;
6218
6379
  if (body.description !== void 0) profile.description = body.description;
6219
6380
  if (body.avatar !== void 0) {
6220
- const homeDir = resolve12(dir, "home");
6381
+ const homeDir = resolve13(dir, "home");
6221
6382
  const avatarPath = safeResolveWithinBase(homeDir, body.avatar);
6222
6383
  if (!avatarPath) {
6223
6384
  return c.json({ error: "Avatar path must be inside the mind's home directory" }, 400);
@@ -6229,7 +6390,7 @@ ${user.trimEnd()}
6229
6390
  }
6230
6391
  config2.profile = profile;
6231
6392
  writeVoluteConfig(dir, config2);
6232
- const { syncMindProfile: syncMindProfile2 } = await import("./auth-X6EMNEGB.js");
6393
+ const { syncMindProfile: syncMindProfile2 } = await import("./auth-KRDSM55Q.js");
6233
6394
  await syncMindProfile2(name, profile);
6234
6395
  broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
6235
6396
  return c.json({ ok: true });
@@ -6273,7 +6434,7 @@ ${user.trimEnd()}
6273
6434
  });
6274
6435
  }
6275
6436
  if (!force) {
6276
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7JXEQQ2P.js");
6437
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
6277
6438
  if (getSleepManagerIfReady2()?.isSleeping(name)) {
6278
6439
  return c.json({ output: "" });
6279
6440
  }
@@ -6328,7 +6489,7 @@ ${user.trimEnd()}
6328
6489
  }
6329
6490
  }
6330
6491
  const dir = entry.dir ?? mindDir(name);
6331
- const { evaluateSeedChecklist: evaluateSeedChecklist2 } = await import("./seed-readiness-DYOVCYHN.js");
6492
+ const { evaluateSeedChecklist: evaluateSeedChecklist2 } = await import("./seed-readiness-WJYYRRCK.js");
6332
6493
  const checklist = evaluateSeedChecklist2(dir);
6333
6494
  const done = [];
6334
6495
  const remaining = [];
@@ -6370,7 +6531,7 @@ ${user.trimEnd()}
6370
6531
  try {
6371
6532
  const sproutedDir = entry.dir ?? mindDir(name);
6372
6533
  if (setupDefaultDreaming(sproutedDir).schedulesChanged) {
6373
- const { getScheduler: getScheduler2 } = await import("./scheduler-TNBN54ZK.js");
6534
+ const { getScheduler: getScheduler2 } = await import("./scheduler-5X5UIQOQ.js");
6374
6535
  getScheduler2().loadSchedules(name, sproutedDir);
6375
6536
  }
6376
6537
  } catch (err) {
@@ -6380,7 +6541,7 @@ ${user.trimEnd()}
6380
6541
  const spiritName = getSpiritName();
6381
6542
  const spiritEntry = await findMind(spiritName);
6382
6543
  if (spiritEntry) {
6383
- const { firstWeekSchedules, spiritDir } = await import("./spirit-25JVAL3M.js");
6544
+ const { firstWeekSchedules, spiritDir } = await import("./spirit-I5ECLGRI.js");
6384
6545
  const sDir = spiritEntry.dir ?? spiritDir();
6385
6546
  const spiritConfig = readVoluteConfig(sDir);
6386
6547
  if (spiritConfig) {
@@ -6394,7 +6555,7 @@ ${user.trimEnd()}
6394
6555
  spiritConfig.schedules = schedules;
6395
6556
  writeVoluteConfig(sDir, spiritConfig);
6396
6557
  try {
6397
- const { getScheduler: getScheduler2 } = await import("./scheduler-TNBN54ZK.js");
6558
+ const { getScheduler: getScheduler2 } = await import("./scheduler-5X5UIQOQ.js");
6398
6559
  getScheduler2().loadSchedules(spiritName, sDir);
6399
6560
  } catch (err) {
6400
6561
  logger_default.warn(
@@ -6416,9 +6577,25 @@ ${user.trimEnd()}
6416
6577
  const name = c.req.param("name");
6417
6578
  const entry = await findMind(name);
6418
6579
  if (!entry) return c.json({ error: "Mind not found" }, 404);
6580
+ const manager = getMindManager();
6581
+ if (entry.parent) {
6582
+ if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 500);
6583
+ const parentEntry = await findMind(entry.parent);
6584
+ if (!parentEntry) return c.json({ error: `Parent mind ${entry.parent} not found` }, 404);
6585
+ if (manager.isRunning(name)) {
6586
+ await stopMindFull(name);
6587
+ }
6588
+ await cleanupVariant(name, entry.parent, parentEntry.dir ?? mindDir(entry.parent), entry.dir);
6589
+ invalidateMindUserCache(name);
6590
+ fireWebhook({
6591
+ event: "mind_deleted",
6592
+ mind: name,
6593
+ data: { port: entry.port, stage: entry.stage, template: entry.template }
6594
+ });
6595
+ return c.json({ ok: true, variant: true });
6596
+ }
6419
6597
  const dir = mindDir(name);
6420
6598
  const force = c.req.query("force") === "true";
6421
- const manager = getMindManager();
6422
6599
  if (manager.isRunning(name)) {
6423
6600
  await stopMindFull(name);
6424
6601
  }
@@ -6463,16 +6640,16 @@ ${user.trimEnd()}
6463
6640
  }
6464
6641
  const UPGRADE_BRANCH = "upgrade";
6465
6642
  const upgradeVariantName = `${mindName}-upgrade`;
6466
- const worktreeDir = resolve12(dir, ".variants", UPGRADE_BRANCH);
6643
+ const worktreeDir = resolve13(dir, ".variants", UPGRADE_BRANCH);
6467
6644
  if (body.abort) {
6468
6645
  if (!existsSync8(worktreeDir)) {
6469
6646
  return c.json({ error: "No upgrade in progress" }, 400);
6470
6647
  }
6471
6648
  try {
6472
6649
  try {
6473
- const gitDirContent = readFileSync9(resolve12(worktreeDir, ".git"), "utf-8").trim();
6650
+ const gitDirContent = readFileSync9(resolve13(worktreeDir, ".git"), "utf-8").trim();
6474
6651
  const gitDir = gitDirContent.replace("gitdir: ", "");
6475
- if (existsSync8(resolve12(gitDir, "MERGE_HEAD"))) {
6652
+ if (existsSync8(resolve13(gitDir, "MERGE_HEAD"))) {
6476
6653
  await gitExec(["merge", "--abort"], { cwd: worktreeDir });
6477
6654
  }
6478
6655
  } catch {
@@ -6556,7 +6733,7 @@ ${user.trimEnd()}
6556
6733
  }
6557
6734
  if (body.diff) {
6558
6735
  try {
6559
- if (!existsSync8(resolve12(dir, ".git"))) {
6736
+ if (!existsSync8(resolve13(dir, ".git"))) {
6560
6737
  return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
6561
6738
  }
6562
6739
  await updateTemplateBranch(dir, template, mindName);
@@ -6580,16 +6757,16 @@ ${user.trimEnd()}
6580
6757
  409
6581
6758
  );
6582
6759
  }
6583
- if (!existsSync8(resolve12(dir, ".git"))) {
6760
+ if (!existsSync8(resolve13(dir, ".git"))) {
6584
6761
  try {
6585
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve12(dir, "home") } : void 0;
6762
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dir, "home") } : void 0;
6586
6763
  await gitExec(["init"], { cwd: dir, mindName, env });
6587
6764
  await configureGitIdentity(mindName, { cwd: dir, mindName, env });
6588
6765
  await gitExec(["add", "-A"], { cwd: dir, mindName, env });
6589
6766
  await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
6590
6767
  await chownMindDir(dir, mindName);
6591
6768
  } catch (err) {
6592
- rmSync3(resolve12(dir, ".git"), { recursive: true, force: true });
6769
+ rmSync3(resolve13(dir, ".git"), { recursive: true, force: true });
6593
6770
  return c.json(
6594
6771
  {
6595
6772
  error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
@@ -6604,7 +6781,7 @@ ${user.trimEnd()}
6604
6781
  } catch {
6605
6782
  }
6606
6783
  await updateTemplateBranch(dir, template, mindName);
6607
- const parentDir = resolve12(dir, ".variants");
6784
+ const parentDir = resolve13(dir, ".variants");
6608
6785
  if (!existsSync8(parentDir)) {
6609
6786
  mkdirSync6(parentDir, { recursive: true });
6610
6787
  }
@@ -6741,7 +6918,7 @@ ${user.trimEnd()}
6741
6918
  if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
6742
6919
  let config2 = readVoluteConfig(dir);
6743
6920
  if (!config2 && entry.template === "pi") {
6744
- const piConfigPath = resolve12(dir, "home/.config/config.json");
6921
+ const piConfigPath = resolve13(dir, "home/.config/config.json");
6745
6922
  if (existsSync8(piConfigPath)) {
6746
6923
  try {
6747
6924
  config2 = JSON.parse(readFileSync9(piConfigPath, "utf-8"));
@@ -6750,7 +6927,7 @@ ${user.trimEnd()}
6750
6927
  }
6751
6928
  }
6752
6929
  let templateConfig = {};
6753
- const configJsonPath = resolve12(dir, "home/.config/config.json");
6930
+ const configJsonPath = resolve13(dir, "home/.config/config.json");
6754
6931
  if (existsSync8(configJsonPath)) {
6755
6932
  try {
6756
6933
  templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
@@ -6819,12 +6996,12 @@ ${user.trimEnd()}
6819
6996
  }
6820
6997
  writeVoluteConfig(dir, existing);
6821
6998
  if (body.unescapeNewlines !== void 0) {
6822
- const { clearEchoTextCache } = await import("./echo-text-6ANI5HHN.js");
6999
+ const { clearEchoTextCache } = await import("./echo-text-KU6G4Z7V.js");
6823
7000
  clearEchoTextCache(name);
6824
7001
  }
6825
7002
  const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.compaction !== void 0;
6826
7003
  if (needsConfigJson) {
6827
- const configJsonPath = resolve12(dir, "home/.config/config.json");
7004
+ const configJsonPath = resolve13(dir, "home/.config/config.json");
6828
7005
  let templateConfig = {};
6829
7006
  if (existsSync8(configJsonPath)) {
6830
7007
  try {
@@ -7144,6 +7321,35 @@ ${lines.join("\n")}` });
7144
7321
  const maxId = notices.reduce((m, n) => Math.max(m, n.id), 0);
7145
7322
  setNoticeDrainWatermark(baseName, session, maxId);
7146
7323
  return c.json({ context: formatEvents(notices), notices });
7324
+ }).post("/:name/notices", requireSelf(), async (c) => {
7325
+ const MIND_POSTABLE_KINDS = ["context_lost", "delivery_failed"];
7326
+ const name = c.req.param("name");
7327
+ const baseName = await getBaseName(name);
7328
+ let body;
7329
+ try {
7330
+ body = await c.req.json();
7331
+ } catch {
7332
+ return c.json({ error: "Invalid JSON body" }, 400);
7333
+ }
7334
+ const kind = body.kind;
7335
+ if (typeof kind !== "string" || !MIND_POSTABLE_KINDS.includes(kind)) {
7336
+ return c.json({ error: `kind must be one of: ${MIND_POSTABLE_KINDS.join(", ")}` }, 400);
7337
+ }
7338
+ const message = typeof body.message === "string" ? body.message.trim() : "";
7339
+ if (!message) return c.json({ error: "message is required" }, 400);
7340
+ if (body.thread !== void 0 && typeof body.thread !== "string") {
7341
+ return c.json({ error: "thread must be a string" }, 400);
7342
+ }
7343
+ await recordNotice({
7344
+ mind: baseName,
7345
+ thread: body.thread ?? MIND_LEVEL_THREAD,
7346
+ kind,
7347
+ reason: kind,
7348
+ // Bound the size (the body is injected into turn context) without dropping
7349
+ // the notice — a truncated explanation beats a silent 400.
7350
+ detail: message.length > 4e3 ? `${message.slice(0, 4e3)}\u2026` : message
7351
+ });
7352
+ return c.json({ ok: true });
7147
7353
  }).get("/:name/system-events", requireSelf(), async (c) => {
7148
7354
  const name = c.req.param("name");
7149
7355
  const baseName = await getBaseName(name);
@@ -7166,6 +7372,43 @@ ${lines.join("\n")}` });
7166
7372
  };
7167
7373
  })
7168
7374
  });
7375
+ }).put("/:name/turn-summaries", requireSelf(), async (c) => {
7376
+ const name = c.req.param("name");
7377
+ const baseName = await getBaseName(name);
7378
+ let body;
7379
+ try {
7380
+ body = await c.req.json();
7381
+ } catch {
7382
+ return c.json({ error: "Invalid JSON body" }, 400);
7383
+ }
7384
+ const items = body.summaries;
7385
+ if (!Array.isArray(items) || items.length === 0) {
7386
+ return c.json({ error: "summaries must be a non-empty array" }, 400);
7387
+ }
7388
+ let updated = 0;
7389
+ let created = 0;
7390
+ for (const item of items) {
7391
+ if (!item || typeof item.turnId !== "string" || !item.turnId) {
7392
+ return c.json({ error: "each summary needs a turnId" }, 400);
7393
+ }
7394
+ if (typeof item.content !== "string") {
7395
+ return c.json({ error: "each summary needs content" }, 400);
7396
+ }
7397
+ const result = await supersedeTurnSummary(baseName, item.turnId, item.content);
7398
+ switch (result.status) {
7399
+ case "invalid":
7400
+ return c.json({ error: result.error }, 400);
7401
+ case "not_found":
7402
+ return c.json({ error: "Turn not found" }, 404);
7403
+ case "forbidden":
7404
+ return c.json({ error: "Forbidden" }, 403);
7405
+ case "ok":
7406
+ if (result.created) created++;
7407
+ else updated++;
7408
+ break;
7409
+ }
7410
+ }
7411
+ return c.json({ ok: true, updated, created });
7169
7412
  }).get("/:name/history", requireSelf(), async (c) => {
7170
7413
  const name = c.req.param("name");
7171
7414
  const channel = c.req.query("channel");
@@ -7174,6 +7417,7 @@ ${lines.join("\n")}` });
7174
7417
  const preset = c.req.query("preset");
7175
7418
  const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
7176
7419
  const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
7420
+ const provisional = c.req.query("provisional") === "true";
7177
7421
  const db = await getDb();
7178
7422
  const conditions = [eq5(mindHistory.mind, name)];
7179
7423
  if (channel) {
@@ -7185,6 +7429,9 @@ ${lines.join("\n")}` });
7185
7429
  const effectivePreset = full ? "all" : preset;
7186
7430
  if (!effectivePreset || effectivePreset === "summary") {
7187
7431
  const sumConditions = [eq5(summaries.mind, name), eq5(summaries.period, "turn")];
7432
+ if (provisional) {
7433
+ sumConditions.push(sql3`json_extract(${summaries.metadata}, '$.author') is not ${"mind"}`);
7434
+ }
7188
7435
  if (session) {
7189
7436
  sumConditions.push(eq5(turns.thread, session));
7190
7437
  const sumRows2 = await db.select({
@@ -7593,7 +7840,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
7593
7840
  const entry = await findMind(name);
7594
7841
  if (!entry) return c.json({ error: "Mind not found" }, 404);
7595
7842
  const body = await c.req.text();
7596
- const { deliverEvent: deliverEvent2 } = await import("./system-events-ZPSH63QX.js");
7843
+ const { deliverEvent: deliverEvent2 } = await import("./system-events-YTOU66QI.js");
7597
7844
  const { id, delivered } = await deliverEvent2(name, {
7598
7845
  type: "webhook",
7599
7846
  body,
@@ -7609,16 +7856,16 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
7609
7856
  var schedules_default = app15;
7610
7857
 
7611
7858
  // packages/daemon/src/web/api/setup.ts
7612
- import { mkdirSync as mkdirSync7 } from "fs";
7859
+ import { mkdirSync as mkdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
7613
7860
  import { homedir as homedir2 } from "os";
7614
- import { resolve as resolve13 } from "path";
7861
+ import { basename, resolve as resolve14 } from "path";
7615
7862
  import { Hono as Hono19 } from "hono";
7616
7863
  import { setCookie as setCookie2 } from "hono/cookie";
7617
7864
  var DEFAULT_API_URL2 = "https://volute.systems";
7618
7865
  var setup = new Hono19();
7619
7866
  function writeSetupConfig(systemName, description) {
7620
- const configHome = process.env.VOLUTE_HOME ?? resolve13(homedir2(), ".volute");
7621
- const mindsDir = resolve13(configHome, "minds");
7867
+ const configHome = process.env.VOLUTE_HOME ?? resolve14(homedir2(), ".volute");
7868
+ const mindsDir = resolve14(configHome, "minds");
7622
7869
  mkdirSync7(configHome, { recursive: true });
7623
7870
  mkdirSync7(mindsDir, { recursive: true });
7624
7871
  const existingConfig = readGlobalConfig();
@@ -7652,7 +7899,7 @@ setup.get("/status", async (c) => {
7652
7899
  let hasAccount = false;
7653
7900
  if (hasSystem) {
7654
7901
  try {
7655
- const { listUsersByType: listUsersByType2 } = await import("./auth-X6EMNEGB.js");
7902
+ const { listUsersByType: listUsersByType2 } = await import("./auth-KRDSM55Q.js");
7656
7903
  const brains = await listUsersByType2("human");
7657
7904
  hasAccount = brains.length > 0;
7658
7905
  } catch (err) {
@@ -7864,7 +8111,7 @@ setup.post("/account", async (c) => {
7864
8111
  }
7865
8112
  }
7866
8113
  try {
7867
- const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-X6EMNEGB.js");
8114
+ const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-KRDSM55Q.js");
7868
8115
  const user = await createUser2(body.username.trim(), body.password);
7869
8116
  if (body.displayName?.trim()) {
7870
8117
  await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
@@ -7949,6 +8196,39 @@ setup.post("/spirit", async (c) => {
7949
8196
  config2.setup.spiritName = name;
7950
8197
  const temperament = body.temperament?.trim();
7951
8198
  config2.setup.spiritTemperament = temperament || void 0;
8199
+ const description = body.description?.trim();
8200
+ config2.setup.spiritDescription = description || void 0;
8201
+ const clearOldStash = () => {
8202
+ if (config2.setup?.spiritAvatar) {
8203
+ rmSync4(resolve14(voluteSystemDir(), basename(config2.setup.spiritAvatar)), { force: true });
8204
+ config2.setup.spiritAvatar = void 0;
8205
+ }
8206
+ };
8207
+ if (body.avatar) {
8208
+ const match = /^data:image\/(png|jpeg|webp);base64,([A-Za-z0-9+/=]+)$/.exec(body.avatar);
8209
+ if (!match) {
8210
+ return c.json({ error: "Avatar must be a png, jpeg, or webp image" }, 400);
8211
+ }
8212
+ if (match[2].length > 3 * 1024 * 1024) {
8213
+ return c.json({ error: "Avatar must be under 2MB" }, 400);
8214
+ }
8215
+ let bytes = Buffer.from(match[2], "base64");
8216
+ if (bytes.length > 2 * 1024 * 1024) {
8217
+ return c.json({ error: "Avatar must be under 2MB" }, 400);
8218
+ }
8219
+ let ext = match[1] === "jpeg" ? "jpg" : match[1];
8220
+ const normalized = await normalizeAvatar(bytes);
8221
+ if (normalized) {
8222
+ bytes = normalized.buffer;
8223
+ ext = "webp";
8224
+ }
8225
+ clearOldStash();
8226
+ const filename = `spirit-avatar.${ext}`;
8227
+ writeFileSync9(resolve14(voluteSystemDir(), filename), bytes);
8228
+ config2.setup.spiritAvatar = filename;
8229
+ } else {
8230
+ clearOldStash();
8231
+ }
7952
8232
  writeGlobalConfig(config2);
7953
8233
  return c.json({ ok: true });
7954
8234
  });
@@ -7957,8 +8237,8 @@ setup.post("/complete", async (c) => {
7957
8237
  return c.json({ error: "Setup already complete" }, 400);
7958
8238
  }
7959
8239
  try {
7960
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-25JVAL3M.js");
7961
- const { startSpiritFull } = await import("./mind-service-DZCUUC7J.js");
8240
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-I5ECLGRI.js");
8241
+ const { startSpiritFull } = await import("./mind-service-WHFBUKZE.js");
7962
8242
  await ensureSpiritProject();
7963
8243
  await syncSpiritTemplate();
7964
8244
  const warnings = [];
@@ -7995,7 +8275,7 @@ setup.post("/complete", async (c) => {
7995
8275
  }
7996
8276
  let spiritConversationId;
7997
8277
  try {
7998
- const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-X6EMNEGB.js");
8278
+ const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-KRDSM55Q.js");
7999
8279
  const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-ZL6PHROD.js");
8000
8280
  const spiritUser = await getOrCreateMindUser2(spiritName);
8001
8281
  const brains = await listUsersByType2("human");
@@ -8018,15 +8298,13 @@ setup.post("/complete", async (c) => {
8018
8298
  logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
8019
8299
  if (spiritConversationId && spiritStarted) {
8020
8300
  try {
8021
- const { deliverMessage: deliverMessage2 } = await import("./message-delivery-JHMAJ2GU.js");
8022
- const { listUsersByType: listUsers2 } = await import("./auth-X6EMNEGB.js");
8301
+ const { deliverMessage: deliverMessage2 } = await import("./message-delivery-TUXM3AAE.js");
8302
+ const { listUsersByType: listUsers2 } = await import("./auth-KRDSM55Q.js");
8023
8303
  const admins = await listUsers2("human");
8024
8304
  const admin2 = admins.find((u) => u.role === "admin");
8025
8305
  if (admin2) {
8026
8306
  const adminName = admin2.display_name || admin2.username;
8027
- const cfg = readGlobalConfig();
8028
- const temperament = cfg.setup?.spiritTemperament;
8029
- 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.`;
8307
+ 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.`;
8030
8308
  await deliverMessage2(spiritName, {
8031
8309
  content: [{ type: "text", text: welcomePrompt }],
8032
8310
  channel: `@${admin2.username}`,
@@ -8060,9 +8338,9 @@ setup.post("/complete", async (c) => {
8060
8338
  var setup_default = setup;
8061
8339
 
8062
8340
  // packages/daemon/src/web/api/skills.ts
8063
- import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync4 } from "fs";
8341
+ import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
8064
8342
  import { tmpdir } from "os";
8065
- import { join, resolve as resolve14 } from "path";
8343
+ import { join, resolve as resolve15 } from "path";
8066
8344
  import AdmZip from "adm-zip";
8067
8345
  import { Hono as Hono20 } from "hono";
8068
8346
  var app16 = new Hono20().get("/", async (c) => {
@@ -8138,7 +8416,7 @@ var app16 = new Hono20().get("/", async (c) => {
8138
8416
  try {
8139
8417
  const zip = new AdmZip(buffer);
8140
8418
  for (const entry of zip.getEntries()) {
8141
- const target = resolve14(tmpDir, entry.entryName);
8419
+ const target = resolve15(tmpDir, entry.entryName);
8142
8420
  if (!target.startsWith(tmpDir)) {
8143
8421
  return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
8144
8422
  }
@@ -8167,7 +8445,7 @@ var app16 = new Hono20().get("/", async (c) => {
8167
8445
  }
8168
8446
  throw e;
8169
8447
  } finally {
8170
- rmSync4(tmpDir, { recursive: true, force: true });
8448
+ rmSync5(tmpDir, { recursive: true, force: true });
8171
8449
  }
8172
8450
  }).get("/:id", async (c) => {
8173
8451
  const id = c.req.param("id");
@@ -8512,8 +8790,8 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
8512
8790
  });
8513
8791
  }, 15e3);
8514
8792
  cleanups.push(() => clearInterval(keepAlive));
8515
- await new Promise((resolve20) => {
8516
- stream.onAbort(() => resolve20());
8793
+ await new Promise((resolve21) => {
8794
+ stream.onAbort(() => resolve21());
8517
8795
  });
8518
8796
  } finally {
8519
8797
  for (const cleanup of cleanups) {
@@ -8794,19 +9072,19 @@ var app21 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
8794
9072
  var feed_default = app21;
8795
9073
 
8796
9074
  // packages/daemon/src/web/api/variants.ts
8797
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
8798
- import { resolve as resolve17 } from "path";
9075
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "fs";
9076
+ import { resolve as resolve18 } from "path";
8799
9077
  import { Hono as Hono26 } from "hono";
8800
9078
 
8801
9079
  // packages/daemon/src/lib/mind/farewell.ts
8802
- import { existsSync as existsSync10, readFileSync as readFileSync10, rmSync as rmSync5 } from "fs";
8803
- import { resolve as resolve15 } from "path";
9080
+ import { existsSync as existsSync10, readFileSync as readFileSync10, rmSync as rmSync6 } from "fs";
9081
+ import { resolve as resolve16 } from "path";
8804
9082
  var flog = logger_default.child("farewell");
8805
9083
  var FAREWELL_RELPATH = ".mind/farewell.md";
8806
9084
  var FAREWELL_MIND_PATH = "../.mind/farewell.md";
8807
9085
  var FAREWELL_TIMEOUT_MS = 12e4;
8808
9086
  function farewellPath(variantDir) {
8809
- return resolve15(variantDir, FAREWELL_RELPATH);
9087
+ return resolve16(variantDir, FAREWELL_RELPATH);
8810
9088
  }
8811
9089
  function readFarewell(variantDir) {
8812
9090
  const path = farewellPath(variantDir);
@@ -8842,7 +9120,7 @@ async function runFarewellTurn(opts) {
8842
9120
  const timeoutMs = opts.timeoutMs ?? FAREWELL_TIMEOUT_MS;
8843
9121
  if (!running) return readFarewell(variantDir);
8844
9122
  try {
8845
- rmSync5(farewellPath(variantDir), { force: true });
9123
+ rmSync6(farewellPath(variantDir), { force: true });
8846
9124
  } catch (err) {
8847
9125
  flog.warn(`failed to clear stale farewell note for ${variantName}`, logger_default.errorData(err));
8848
9126
  }
@@ -8866,8 +9144,8 @@ async function runFarewellTurn(opts) {
8866
9144
 
8867
9145
  // packages/daemon/src/lib/mind/spawn-server.ts
8868
9146
  import { spawn as spawn4 } from "child_process";
8869
- import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
8870
- import { resolve as resolve16 } from "path";
9147
+ import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11, statSync as statSync4 } from "fs";
9148
+ import { resolve as resolve17 } from "path";
8871
9149
  async function spawnServer(cwd, port, options) {
8872
9150
  let cmd = process.env.VOLUTE_NODE_PATH ?? process.execPath;
8873
9151
  let args = ["--import", "tsx", "src/server.ts", "--port", String(port)];
@@ -8888,34 +9166,34 @@ function spawnAttached(cmd, args, cwd) {
8888
9166
  cwd,
8889
9167
  stdio: ["ignore", "pipe", "pipe"]
8890
9168
  });
8891
- return new Promise((resolve20) => {
8892
- const timeout = setTimeout(() => resolve20(null), 3e4);
9169
+ return new Promise((resolve21) => {
9170
+ const timeout = setTimeout(() => resolve21(null), 3e4);
8893
9171
  function checkOutput(data) {
8894
9172
  const match = data.toString().match(/listening on :(\d+)/);
8895
9173
  if (match) {
8896
9174
  clearTimeout(timeout);
8897
- resolve20({ child, actualPort: parseInt(match[1], 10) });
9175
+ resolve21({ child, actualPort: parseInt(match[1], 10) });
8898
9176
  }
8899
9177
  }
8900
9178
  child.stdout?.on("data", checkOutput);
8901
9179
  child.stderr?.on("data", checkOutput);
8902
9180
  child.on("error", () => {
8903
9181
  clearTimeout(timeout);
8904
- resolve20(null);
9182
+ resolve21(null);
8905
9183
  });
8906
9184
  child.on("exit", () => {
8907
9185
  clearTimeout(timeout);
8908
- resolve20(null);
9186
+ resolve21(null);
8909
9187
  });
8910
9188
  });
8911
9189
  }
8912
9190
  function spawnDetached(cmd, args, cwd, logDir) {
8913
- const logsDir = logDir ?? resolve16(cwd, ".mind", "logs");
9191
+ const logsDir = logDir ?? resolve17(cwd, ".mind", "logs");
8914
9192
  mkdirSync8(logsDir, { recursive: true });
8915
- const logPath = resolve16(logsDir, "mind.log");
9193
+ const logPath = resolve17(logsDir, "mind.log");
8916
9194
  let startOffset = 0;
8917
9195
  try {
8918
- startOffset = statSync3(logPath).size;
9196
+ startOffset = statSync4(logPath).size;
8919
9197
  } catch {
8920
9198
  }
8921
9199
  const logFd = openSync(logPath, "a");
@@ -9053,11 +9331,11 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9053
9331
  return c.json({ error: `Name already in use: ${variantName}` }, 409);
9054
9332
  }
9055
9333
  const projectRoot = entry.dir ?? mindDir(mindName);
9056
- const variantDir = resolve17(projectRoot, ".variants", variantName);
9334
+ const variantDir = resolve18(projectRoot, ".variants", variantName);
9057
9335
  if (existsSync11(variantDir)) {
9058
9336
  return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
9059
9337
  }
9060
- mkdirSync9(resolve17(projectRoot, ".variants"), { recursive: true });
9338
+ mkdirSync9(resolve18(projectRoot, ".variants"), { recursive: true });
9061
9339
  try {
9062
9340
  await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
9063
9341
  } catch (e) {
@@ -9093,7 +9371,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9093
9371
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
9094
9372
  await exec(cmd, args, {
9095
9373
  cwd: variantDir,
9096
- env: { ...process.env, HOME: resolve17(variantDir, "home") }
9374
+ env: { ...process.env, HOME: resolve18(variantDir, "home") }
9097
9375
  });
9098
9376
  } else {
9099
9377
  await exec("npm", ["install"], { cwd: variantDir });
@@ -9104,7 +9382,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9104
9382
  return c.json({ error: `npm install failed: ${msg}` }, 500);
9105
9383
  }
9106
9384
  if (body.soul) {
9107
- writeFileSync9(resolve17(variantDir, "home/SOUL.md"), body.soul);
9385
+ writeFileSync10(resolve18(variantDir, "home/SOUL.md"), body.soul);
9108
9386
  }
9109
9387
  const variantPort = body.port ?? await nextPort();
9110
9388
  try {
@@ -9157,7 +9435,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9157
9435
  } catch {
9158
9436
  }
9159
9437
  const projectRoot = parentEntry.dir ?? mindDir(mindName);
9160
- const failAfterGitWrite = async (message, extra) => {
9438
+ const failAfterGitWrite = async (message, extra, status = 500) => {
9161
9439
  try {
9162
9440
  await chownMindDir(projectRoot, mindName);
9163
9441
  } catch (err) {
@@ -9173,7 +9451,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9173
9451
  500
9174
9452
  );
9175
9453
  }
9176
- return c.json({ error: message, ...extra }, 500);
9454
+ return c.json({ error: message, ...extra }, status);
9177
9455
  };
9178
9456
  let farewell;
9179
9457
  try {
@@ -9245,6 +9523,27 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9245
9523
  { conflicts: err.conflicts }
9246
9524
  );
9247
9525
  }
9526
+ if (existsSync11(variantEntry.dir) && !body.discardUnresolved) {
9527
+ let unresolved;
9528
+ try {
9529
+ unresolved = await findUnresolvedHomeFiles(variantEntry.dir);
9530
+ } catch (err) {
9531
+ return failAfterGitWrite(
9532
+ `Could not verify variant ${variantName} has no unresolved home/ files: ${err instanceof Error ? err.message : String(err)}. Variant left intact \u2014 retry the join.`
9533
+ );
9534
+ }
9535
+ if (unresolved.totalCount > 0) {
9536
+ return failAfterGitWrite(
9537
+ formatUnresolvedHomeFilesMessage(variantName, unresolved),
9538
+ {
9539
+ unresolvedFiles: unresolved.files,
9540
+ unresolvedCount: unresolved.totalCount,
9541
+ unresolvedBytes: unresolved.totalBytes
9542
+ },
9543
+ 409
9544
+ );
9545
+ }
9546
+ }
9248
9547
  await cleanupVariant(variantName, mindName, projectRoot, variantEntry.dir, { stop: true });
9249
9548
  await chownMindDir(projectRoot, mindName);
9250
9549
  if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
@@ -9263,7 +9562,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
9263
9562
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
9264
9563
  await exec(cmd, args, {
9265
9564
  cwd: projectRoot,
9266
- env: { ...process.env, HOME: resolve17(projectRoot, "home") }
9565
+ env: { ...process.env, HOME: resolve18(projectRoot, "home") }
9267
9566
  });
9268
9567
  } else {
9269
9568
  await exec("npm", ["install"], { cwd: projectRoot });
@@ -9720,7 +10019,7 @@ var unifiedChatApp = new Hono28().post(
9720
10019
  logger_default.error("spirit availability check failed \u2014 proceeding with delivery", logger_default.errorData(err));
9721
10020
  }
9722
10021
  const isDM = conv.type === "dm";
9723
- await fanOutToMinds({
10022
+ const { gatedRecipients } = await fanOutToMinds({
9724
10023
  conversationId,
9725
10024
  contentBlocks,
9726
10025
  senderName,
@@ -9730,7 +10029,15 @@ var unifiedChatApp = new Hono28().post(
9730
10029
  // Variant-aware targeting: when targetMind is a variant, route to the variant name
9731
10030
  targetName: baseName ? (username) => username === baseName ? variantName : username : void 0
9732
10031
  });
9733
- return c.json({ ok: true, conversationId, outboundId, spirit: spiritStatus, spiritName });
10032
+ 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;
10033
+ return c.json({
10034
+ ok: true,
10035
+ conversationId,
10036
+ outboundId,
10037
+ spirit: spiritStatus,
10038
+ spiritName,
10039
+ ...notice ? { notice } : {}
10040
+ });
9734
10041
  }
9735
10042
  );
9736
10043
  var app24 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
@@ -9750,11 +10057,11 @@ var app24 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
9750
10057
  if (!stream.aborted) console.error("[chat] SSE ping error:", err);
9751
10058
  });
9752
10059
  }, 15e3);
9753
- await new Promise((resolve20) => {
10060
+ await new Promise((resolve21) => {
9754
10061
  stream.onAbort(() => {
9755
10062
  unsubscribe();
9756
10063
  clearInterval(keepAlive);
9757
- resolve20();
10064
+ resolve21();
9758
10065
  });
9759
10066
  });
9760
10067
  });
@@ -9974,7 +10281,7 @@ var app_default = app26;
9974
10281
  import { existsSync as existsSync12 } from "fs";
9975
10282
  import { readFile as readFile2, stat as stat2 } from "fs/promises";
9976
10283
  import { createServer as createHttpsServer } from "https";
9977
- import { dirname as dirname3, extname as extname3, resolve as resolve18 } from "path";
10284
+ import { dirname as dirname3, extname as extname3, resolve as resolve19 } from "path";
9978
10285
  import { serve } from "@hono/node-server";
9979
10286
  var MIME_TYPES2 = {
9980
10287
  ".html": "text/html",
@@ -9993,7 +10300,7 @@ async function startServer({
9993
10300
  let assetsDir = "";
9994
10301
  let searchDir = dirname3(new URL(import.meta.url).pathname);
9995
10302
  for (let i = 0; i < 5; i++) {
9996
- const candidate = resolve18(searchDir, "dist", "web-assets");
10303
+ const candidate = resolve19(searchDir, "dist", "web-assets");
9997
10304
  if (existsSync12(candidate)) {
9998
10305
  assetsDir = candidate;
9999
10306
  break;
@@ -10004,20 +10311,20 @@ async function startServer({
10004
10311
  app_default.get("*", async (c) => {
10005
10312
  const urlPath = new URL(c.req.url).pathname;
10006
10313
  if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
10007
- const filePath = resolve18(assetsDir, urlPath.slice(1));
10314
+ const filePath = resolve19(assetsDir, urlPath.slice(1));
10008
10315
  if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
10009
10316
  const s = await stat2(filePath).catch(() => null);
10010
10317
  if (s?.isFile()) {
10011
10318
  const ext = extname3(filePath);
10012
10319
  const mime = MIME_TYPES2[ext] || "application/octet-stream";
10013
10320
  const body = await readFile2(filePath);
10014
- const basename = filePath.slice(filePath.lastIndexOf("/") + 1);
10015
- const nameWithoutExt = basename.slice(0, basename.lastIndexOf("."));
10321
+ const basename2 = filePath.slice(filePath.lastIndexOf("/") + 1);
10322
+ const nameWithoutExt = basename2.slice(0, basename2.lastIndexOf("."));
10016
10323
  const isHashed = /[-.][\da-f]{8,}$/.test(nameWithoutExt);
10017
10324
  const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
10018
10325
  return c.body(body, 200, { "Content-Type": mime, "Cache-Control": cacheControl });
10019
10326
  }
10020
- const indexPath = resolve18(assetsDir, "index.html");
10327
+ const indexPath = resolve19(assetsDir, "index.html");
10021
10328
  const indexStat = await stat2(indexPath).catch(() => null);
10022
10329
  if (indexStat?.isFile()) {
10023
10330
  const body = await readFile2(indexPath, "utf-8");
@@ -10034,10 +10341,10 @@ async function startServer({
10034
10341
  createServer: createHttpsServer,
10035
10342
  serverOptions: { key: tls.key, cert: tls.cert }
10036
10343
  });
10037
- await new Promise((resolve20, reject) => {
10344
+ await new Promise((resolve21, reject) => {
10038
10345
  server2.on("listening", () => {
10039
10346
  logger_default.info("Volute UI running (https)", { hostname, port });
10040
- resolve20();
10347
+ resolve21();
10041
10348
  });
10042
10349
  server2.on("error", (err) => {
10043
10350
  reject(err);
@@ -10045,13 +10352,13 @@ async function startServer({
10045
10352
  });
10046
10353
  const internalPort = port + 1;
10047
10354
  const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
10048
- await new Promise((resolve20, reject) => {
10355
+ await new Promise((resolve21, reject) => {
10049
10356
  internalServer.on("listening", () => {
10050
10357
  logger_default.info("Volute API running (http, internal)", {
10051
10358
  hostname: "127.0.0.1",
10052
10359
  port: internalPort
10053
10360
  });
10054
- resolve20();
10361
+ resolve21();
10055
10362
  });
10056
10363
  internalServer.on("error", (err) => {
10057
10364
  reject(err);
@@ -10060,10 +10367,10 @@ async function startServer({
10060
10367
  return { server: server2, internalPort };
10061
10368
  }
10062
10369
  const server = serve({ fetch: app_default.fetch, port, hostname });
10063
- await new Promise((resolve20, reject) => {
10370
+ await new Promise((resolve21, reject) => {
10064
10371
  server.on("listening", () => {
10065
10372
  logger_default.info("Volute API running (http)", { hostname, port });
10066
- resolve20();
10373
+ resolve21();
10067
10374
  });
10068
10375
  server.on("error", (err) => {
10069
10376
  reject(err);
@@ -10074,18 +10381,18 @@ async function startServer({
10074
10381
 
10075
10382
  // packages/daemon/src/daemon.ts
10076
10383
  if (!process.env.VOLUTE_HOME) {
10077
- process.env.VOLUTE_HOME = resolve19(homedir3(), ".volute");
10384
+ process.env.VOLUTE_HOME = resolve20(homedir3(), ".volute");
10078
10385
  }
10079
10386
  if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
10080
10387
  process.env.TZ = process.env.VOLUTE_TIMEZONE;
10081
10388
  }
10082
10389
  function writeDaemonConfig(systemDir, config2, token) {
10083
- const configPath = resolve19(systemDir, "daemon.json");
10084
- writeFileSync10(configPath, `${JSON.stringify(config2, null, 2)}
10390
+ const configPath = resolve20(systemDir, "daemon.json");
10391
+ writeFileSync11(configPath, `${JSON.stringify(config2, null, 2)}
10085
10392
  `, { mode: 420 });
10086
10393
  chmodSync(configPath, 420);
10087
- const tokenPath = resolve19(systemDir, "daemon-token");
10088
- writeFileSync10(tokenPath, `${token}
10394
+ const tokenPath = resolve20(systemDir, "daemon-token");
10395
+ writeFileSync11(tokenPath, `${token}
10089
10396
  `, { mode: 384 });
10090
10397
  chmodSync(tokenPath, 384);
10091
10398
  }
@@ -10095,7 +10402,7 @@ async function startDaemon(opts) {
10095
10402
  const home = voluteHome();
10096
10403
  const systemDir = voluteSystemDir();
10097
10404
  if (!opts.foreground) {
10098
- const rotatingLog = new RotatingLog(resolve19(systemDir, "daemon.log"));
10405
+ const rotatingLog = new RotatingLog(resolve20(systemDir, "daemon.log"));
10099
10406
  logger_default.setOutput((line) => rotatingLog.write(`${line}
10100
10407
  `));
10101
10408
  const write = (...args) => rotatingLog.write(`${format(...args)}
@@ -10105,9 +10412,9 @@ async function startDaemon(opts) {
10105
10412
  console.warn = write;
10106
10413
  console.info = write;
10107
10414
  }
10108
- const DAEMON_PID_PATH = resolve19(systemDir, "daemon.pid");
10109
- const DAEMON_JSON_PATH = resolve19(systemDir, "daemon.json");
10110
- const DAEMON_TOKEN_PATH = resolve19(systemDir, "daemon-token");
10415
+ const DAEMON_PID_PATH = resolve20(systemDir, "daemon.pid");
10416
+ const DAEMON_JSON_PATH = resolve20(systemDir, "daemon.json");
10417
+ const DAEMON_TOKEN_PATH = resolve20(systemDir, "daemon-token");
10111
10418
  mkdirSync10(home, { recursive: true });
10112
10419
  ensureSystemDir();
10113
10420
  const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-WZSXHHRJ.js");
@@ -10168,7 +10475,7 @@ async function startDaemon(opts) {
10168
10475
  logger_default.warn("failed to backfill minds into #system", logger_default.errorData(err));
10169
10476
  }
10170
10477
  try {
10171
- const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-X6EMNEGB.js");
10478
+ const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-KRDSM55Q.js");
10172
10479
  await getOrCreateSystemUser2();
10173
10480
  } catch (err) {
10174
10481
  logger_default.warn(
@@ -10209,7 +10516,7 @@ async function startDaemon(opts) {
10209
10516
  process.env.VOLUTE_DAEMON_TOKEN = token;
10210
10517
  process.env.VOLUTE_DAEMON_PORT = String(daemonPort);
10211
10518
  process.env.VOLUTE_DAEMON_HOSTNAME = hostname;
10212
- writeFileSync10(DAEMON_PID_PATH, myPid, { mode: 420 });
10519
+ writeFileSync11(DAEMON_PID_PATH, myPid, { mode: 420 });
10213
10520
  const daemonConfig = { port, hostname };
10214
10521
  if (internalPort) daemonConfig.internalPort = internalPort;
10215
10522
  if (tls) daemonConfig.tls = true;
@@ -10236,7 +10543,7 @@ async function startDaemon(opts) {
10236
10543
  const orphanedTurns = await completeOrphanedTurns();
10237
10544
  summarizeOrphanedTurns(orphanedTurns);
10238
10545
  try {
10239
- const { reconcileVariants } = await import("./variant-cleanup-YXN25ZVW.js");
10546
+ const { reconcileVariants } = await import("./variant-cleanup-CZDLIY3F.js");
10240
10547
  await reconcileVariants();
10241
10548
  } catch (err) {
10242
10549
  logger_default.error("failed to reconcile variants", logger_default.errorData(err));
@@ -10272,8 +10579,8 @@ async function startDaemon(opts) {
10272
10579
  try {
10273
10580
  const { isSetupComplete: isSetupComplete2, getSpiritName: getSpiritName2 } = await import("./setup-WZSXHHRJ.js");
10274
10581
  if (isSetupComplete2()) {
10275
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-25JVAL3M.js");
10276
- const { startSpiritFull } = await import("./mind-service-DZCUUC7J.js");
10582
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-I5ECLGRI.js");
10583
+ const { startSpiritFull } = await import("./mind-service-WHFBUKZE.js");
10277
10584
  await ensureSpiritProject();
10278
10585
  const spiritName = getSpiritName2();
10279
10586
  const spiritEntry = await findMind(spiritName);
@@ -10292,7 +10599,7 @@ async function startDaemon(opts) {
10292
10599
  bridgeManager.startBridges(daemonPort).catch((err) => {
10293
10600
  logger_default.warn("failed to start bridges", logger_default.errorData(err));
10294
10601
  });
10295
- import("./cloud-sync-GPXYPTIC.js").then(
10602
+ import("./cloud-sync-MJVJ33LD.js").then(
10296
10603
  ({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
10297
10604
  logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
10298
10605
  })
@@ -10300,7 +10607,7 @@ async function startDaemon(opts) {
10300
10607
  logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
10301
10608
  });
10302
10609
  try {
10303
- const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-EN3KJ7HH.js");
10610
+ const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-SJVAQLZI.js");
10304
10611
  backfillTemplateHashes().then(() => warnStaleTemplates()).catch((err) => {
10305
10612
  logger_default.warn("failed to check template staleness", logger_default.errorData(err));
10306
10613
  });