volute 0.33.0 → 0.35.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 (255) hide show
  1. package/README.md +7 -6
  2. package/dist/accept-ZBDVVCEU.js +42 -0
  3. package/dist/activity-events-ZW4SDL2C.js +15 -0
  4. package/dist/{ai-service-SBY2WG7O.js → ai-service-LURBEDDB.js} +6 -6
  5. package/dist/{api-client-YPKOZP2O.js → api-client-3A77HMH7.js} +2 -2
  6. package/dist/api.d.ts +1 -5195
  7. package/dist/{archive-INXYFVCW.js → archive-ESU2FUN4.js} +4 -4
  8. package/dist/{auth-GKCDSO4T.js → auth-WX4TESEI.js} +6 -6
  9. package/dist/bridge-PXIO6PS2.js +206 -0
  10. package/dist/chat-QXAJF3FU.js +51 -0
  11. package/dist/{chunk-NNB4WIG7.js → chunk-2TGZJFAT.js} +3 -3
  12. package/dist/{chunk-6LXAAQ43.js → chunk-33ODGMFZ.js} +1 -1
  13. package/dist/{chunk-RPZZSXV3.js → chunk-5N7Y5WAM.js} +21 -2
  14. package/dist/chunk-5T5YMX6S.js +23 -0
  15. package/dist/{chunk-7J3HEVR7.js → chunk-5XJYUFZH.js} +28 -16
  16. package/dist/chunk-7KJOFUNN.js +22 -0
  17. package/dist/{chunk-2NGTS5UU.js → chunk-A2ZLHBHG.js} +2 -2
  18. package/dist/{chunk-KIEPMIM5.js → chunk-AN2W47GW.js} +2 -2
  19. package/dist/{chunk-G53F3JA4.js → chunk-AOB6GVRM.js} +1 -1
  20. package/dist/{chunk-LRCG2JLP.js → chunk-BDYXIWA5.js} +9 -5
  21. package/dist/{chunk-YUIHSKR6.js → chunk-BKF4WQCY.js} +2 -2
  22. package/dist/{chunk-N432I7QH.js → chunk-BMZQYACC.js} +2 -2
  23. package/dist/{chunk-NAOW2CLO.js → chunk-BTY4WNFE.js} +1 -1
  24. package/dist/{chunk-ALEF47VT.js → chunk-BV65KRHM.js} +2 -2
  25. package/dist/{chunk-KVK2DLWI.js → chunk-CORXD635.js} +4 -4
  26. package/dist/{chunk-PVY5W6QN.js → chunk-F7ZNLYKZ.js} +2 -2
  27. package/dist/{chunk-QTUVYI7W.js → chunk-FT5KETXZ.js} +3 -3
  28. package/dist/{chunk-C7I35G4R.js → chunk-IJHIXLVN.js} +44 -8
  29. package/dist/{chunk-JUKK7FPS.js → chunk-J6CJQDWI.js} +37 -28
  30. package/dist/{chunk-4RQBJWQX.js → chunk-LOPXTW6H.js} +1 -1
  31. package/dist/{chunk-RSX4OPZY.js → chunk-MDJGMOSD.js} +8 -137
  32. package/dist/{chunk-LOEJ4HPQ.js → chunk-N446KRP7.js} +3 -3
  33. package/dist/{chunk-I5KY25PQ.js → chunk-N5LMGYXX.js} +2 -2
  34. package/dist/{chunk-G6BSYHPK.js → chunk-NJK5SDGR.js} +1 -1
  35. package/dist/{chunk-D424ZQGI.js → chunk-O7IGP7ZW.js} +11 -3
  36. package/dist/{chunk-M7UL5S3Q.js → chunk-OTC67N2Z.js} +2 -2
  37. package/dist/{chunk-GY5HBI7A.js → chunk-PWQ2ITYG.js} +4 -4
  38. package/dist/{chunk-KTLFDYPT.js → chunk-QCH6K235.js} +1 -1
  39. package/dist/chunk-QHG4OMZL.js +145 -0
  40. package/dist/{chunk-SKLSMHXO.js → chunk-QWTR6AWZ.js} +3 -3
  41. package/dist/chunk-TXSA4Q3V.js +116 -0
  42. package/dist/{chunk-VH33ZWMW.js → chunk-VHJRZM2S.js} +2 -2
  43. package/dist/{chunk-SSI47XP2.js → chunk-VHWGEJ4V.js} +1 -1
  44. package/dist/chunk-VY3RB2V7.js +164 -0
  45. package/dist/chunk-WJPROOU5.js +8314 -0
  46. package/dist/{chunk-RVGLDGMI.js → chunk-WZRZFFCL.js} +25 -27
  47. package/dist/{chunk-JYVGHWEJ.js → chunk-XRQSAMX2.js} +4 -4
  48. package/dist/{chunk-OYAKCAVY.js → chunk-ZSR72JB3.js} +1 -1
  49. package/dist/{chunk-UKVWJRKN.js → chunk-ZX7EAV5J.js} +17 -7
  50. package/dist/cli.js +90 -29
  51. package/dist/clock-HSEKS5AR.js +289 -0
  52. package/dist/{cloud-sync-4NWLMFVH.js → cloud-sync-6JL4C24T.js} +22 -23
  53. package/dist/config-UTS7QULS.js +76 -0
  54. package/dist/connectors/discord-bridge.js +4 -4
  55. package/dist/connectors/slack-bridge.js +4 -4
  56. package/dist/connectors/telegram-bridge.js +4 -4
  57. package/dist/{conversations-AWI5SZW2.js → conversations-2PW57WO2.js} +6 -6
  58. package/dist/create-5BPOOJAN.js +75 -0
  59. package/dist/create-UVCK2CS6.js +50 -0
  60. package/dist/daemon-client-RVIKXGFQ.js +12 -0
  61. package/dist/daemon-restart-HSZ3BCX5.js +65 -0
  62. package/dist/daemon.js +1349 -1211
  63. package/dist/db-BDMH4SZ2.js +20 -0
  64. package/dist/db-BVBJ57TU.js +9 -0
  65. package/dist/delete-L5PAVDGQ.js +42 -0
  66. package/dist/delivery-manager-H5ZVBMCQ.js +31 -0
  67. package/dist/{delivery-router-FL45JL7N.js → delivery-router-HEJSJAHQ.js} +5 -5
  68. package/dist/down-74VXM45A.js +17 -0
  69. package/dist/env-E4XHO2BI.js +223 -0
  70. package/dist/exec-PY7THYH4.js +17 -0
  71. package/dist/export-OAS6QVBN.js +113 -0
  72. package/dist/extension-D74CNM7G.js +89 -0
  73. package/dist/extensions-XDDFY72A.js +49 -0
  74. package/dist/files-CWTK6V3H.js +53 -0
  75. package/dist/import-5A3T7QV4.js +143 -0
  76. package/dist/{isolation-LLAYQYDY.js → isolation-TK5RX2WM.js} +4 -4
  77. package/dist/join-DF5XSJAC.js +67 -0
  78. package/dist/lib-DYEZMGW7.js +6588 -0
  79. package/dist/list-PDMQM7ZV.js +53 -0
  80. package/dist/login-7TE6CIZF.js +60 -0
  81. package/dist/login-GOTAYLXP.js +51 -0
  82. package/dist/logout-6KIA74EV.js +29 -0
  83. package/dist/logout-T4XS6LRU.js +50 -0
  84. package/dist/message-delivery-GRC4W6P7.js +41 -0
  85. package/dist/mind-5IEYKV7I.js +97 -0
  86. package/dist/mind-activity-tracker-QBLIV7ZJ.js +18 -0
  87. package/dist/mind-history-IE2QH7U5.js +275 -0
  88. package/dist/mind-list-GEWHWAL4.js +38 -0
  89. package/dist/mind-manager-HFLB5653.js +31 -0
  90. package/dist/mind-profile-DCBDVF5B.js +53 -0
  91. package/dist/mind-service-X2CAA6W6.js +37 -0
  92. package/dist/mind-sleep-ITCF6OQA.js +47 -0
  93. package/dist/mind-status-X4SX3YUG.js +65 -0
  94. package/dist/mind-wake-KXMKMGWX.js +42 -0
  95. package/dist/{package-U3VFO273.js → package-D2FSVFAX.js} +11 -8
  96. package/dist/read-67VRP2DO.js +91 -0
  97. package/dist/{read-stdin-HQJ7774D.js → read-stdin-3X5VYKNS.js} +2 -2
  98. package/dist/register-SB7NXCOE.js +51 -0
  99. package/dist/{registry-PJ4S5PHQ.js → registry-GBSNW3HG.js} +3 -3
  100. package/dist/reject-MUR2KWJ4.js +40 -0
  101. package/dist/restart-5EGG4JXU.js +42 -0
  102. package/dist/{sandbox-GJOK4QLQ.js → sandbox-R37VIU36.js} +6 -6
  103. package/dist/scheduler-Y7O4CJXL.js +31 -0
  104. package/dist/{schema-PA3M5ZKH.js → schema-XVZ2CLKW.js} +4 -2
  105. package/dist/{seed-QDYVLG74.js → seed-EQORWX77.js} +3 -3
  106. package/dist/seed-check-KJNTL72M.js +35 -0
  107. package/dist/seed-cmd-ZM2XGVU2.js +30 -0
  108. package/dist/seed-create-DRWGGHEI.js +113 -0
  109. package/dist/seed-sprout-JYXGXOP3.js +148 -0
  110. package/dist/send-JBJJQ7CA.js +409 -0
  111. package/dist/service-WNPCNHOX.js +121 -0
  112. package/dist/{setup-XMCBE3LF.js → setup-BJ4YAY26.js} +155 -129
  113. package/dist/{setup-TISPCO22.js → setup-RHJRFURI.js} +4 -4
  114. package/dist/skill-TAAKEYBV.js +389 -0
  115. package/dist/skills/plan-coordinator/SKILL.md +60 -0
  116. package/dist/skills/volute-mind/SKILL.md +9 -227
  117. package/dist/skills/volute-mind/references/extensions.md +34 -0
  118. package/dist/skills/volute-mind/references/integrations.md +48 -0
  119. package/dist/skills/volute-mind/references/routing.md +86 -0
  120. package/dist/skills/volute-mind/references/sleep.md +33 -0
  121. package/dist/skills/volute-mind/references/variants.md +31 -0
  122. package/dist/{skills-7FV7EJTE.js → skills-EKMCQ46K.js} +12 -8
  123. package/dist/sleep-manager-7KFK3USC.js +35 -0
  124. package/dist/spirit-ZFRDXMG7.js +23 -0
  125. package/dist/split-AWVOYOPZ.js +64 -0
  126. package/dist/{sprout-WKLZXUIQ.js → sprout-HE4TITMK.js} +3 -3
  127. package/dist/start-3UXOPXQG.js +39 -0
  128. package/dist/status-ZK34WYIM.js +125 -0
  129. package/dist/stop-3XYIBGFM.js +41 -0
  130. package/dist/system-chat-IDPHYHY4.js +35 -0
  131. package/dist/systems-O43WGQY6.js +52 -0
  132. package/dist/{tailscale-XHQBZROW.js → tailscale-ZIZ2HWJ5.js} +5 -5
  133. package/dist/template-hash-A7FNHTB7.js +9 -0
  134. package/dist/up-77ICEDEW.js +19 -0
  135. package/dist/update-ANE5ZM7F.js +225 -0
  136. package/dist/{update-check-ZD6OOIYQ.js → update-check-UV55CBEP.js} +4 -4
  137. package/dist/upgrade-ZMDGC7M2.js +74 -0
  138. package/dist/variant-QWL2WSRI.js +62 -0
  139. package/dist/{version-notify-NBI2MTJO.js → version-notify-FXSEMXWW.js} +29 -28
  140. package/dist/{volute-config-HD7WWUQC.js → volute-config-D2XVS2YI.js} +2 -2
  141. package/dist/web-assets/assets/index-BhxWKvbB.css +1 -0
  142. package/dist/web-assets/assets/index-CHVKJ9II.js +75 -0
  143. package/dist/web-assets/ext-theme.css +48 -9
  144. package/dist/web-assets/index.html +2 -2
  145. package/dist/web-assets/sw.js +117 -0
  146. package/drizzle/0005_meta_summaries.sql +15 -0
  147. package/drizzle/meta/0005_snapshot.json +7 -0
  148. package/drizzle/meta/_journal.json +7 -0
  149. package/package.json +10 -7
  150. package/packages/extensions/pages/dist/ui/assets/index-DKZLNMED.js +2 -0
  151. package/packages/extensions/pages/dist/ui/index.html +1 -1
  152. package/packages/extensions/pages/skills/pages/SKILL.md +84 -9
  153. package/packages/extensions/plan/dist/ui/assets/index-CJj2gZnZ.css +1 -0
  154. package/packages/extensions/plan/dist/ui/assets/index-FMEJmvQz.js +61 -0
  155. package/packages/extensions/plan/dist/ui/index.html +14 -0
  156. package/packages/extensions/plan/skills/plan/SKILL.md +43 -0
  157. package/packages/extensions/plan/skills/plan/scripts/plan-hook.sh +37 -0
  158. package/templates/_base/home/VOLUTE.md +12 -19
  159. package/templates/_base/src/lib/auto-commit.ts +8 -8
  160. package/templates/_base/src/lib/context-breakdown.ts +450 -0
  161. package/templates/_base/src/lib/format-prefix.ts +17 -0
  162. package/templates/_base/src/lib/hook-loader.ts +8 -2
  163. package/templates/_base/src/lib/router.ts +75 -33
  164. package/templates/_base/src/lib/routing.ts +4 -1
  165. package/templates/_base/src/lib/startup.ts +16 -8
  166. package/templates/_base/src/lib/types.ts +2 -1
  167. package/templates/_base/src/lib/volute-server.ts +75 -8
  168. package/templates/claude/.init/CLAUDE.md +4 -10
  169. package/templates/claude/package.json.tmpl +1 -0
  170. package/templates/claude/src/agent.ts +108 -33
  171. package/templates/claude/src/lib/hooks/reply-instructions.ts +27 -7
  172. package/templates/claude/src/lib/stream-consumer.ts +2 -2
  173. package/templates/claude/src/server.ts +1 -0
  174. package/templates/codex/package.json.tmpl +1 -0
  175. package/templates/codex/src/agent.ts +80 -8
  176. package/templates/codex/src/server.ts +1 -4
  177. package/templates/pi/package.json.tmpl +1 -0
  178. package/templates/pi/src/agent.ts +115 -36
  179. package/templates/pi/src/lib/event-handler.ts +22 -7
  180. package/templates/pi/src/lib/reply-instructions-extension.ts +23 -4
  181. package/templates/pi/src/lib/subagents.ts +20 -17
  182. package/templates/pi/src/server.ts +2 -5
  183. package/dist/accept-D5VBM7JW.js +0 -42
  184. package/dist/activity-events-XJO3P4RR.js +0 -15
  185. package/dist/bridge-TXWWPPOJ.js +0 -207
  186. package/dist/chat-U5ZOME3O.js +0 -68
  187. package/dist/chunk-3Z2DPESO.js +0 -3634
  188. package/dist/chunk-A2A4KLFE.js +0 -1528
  189. package/dist/chunk-K3NQKI34.js +0 -10
  190. package/dist/chunk-NPKSDYA2.js +0 -156
  191. package/dist/chunk-PB65JZK2.js +0 -85
  192. package/dist/clock-BVH3V6E3.js +0 -266
  193. package/dist/config-H2H4UIF7.js +0 -72
  194. package/dist/create-2FK7Z46Y.js +0 -44
  195. package/dist/create-YWD2TIP4.js +0 -71
  196. package/dist/daemon-client-6QXHZ7US.js +0 -12
  197. package/dist/daemon-restart-GOBUKLX7.js +0 -52
  198. package/dist/db-F34YLV7D.js +0 -9
  199. package/dist/db-RA45JBFG.js +0 -16
  200. package/dist/delete-QTGWEDBI.js +0 -35
  201. package/dist/delivery-manager-PFAKEJTC.js +0 -32
  202. package/dist/down-FWWTEKXM.js +0 -15
  203. package/dist/env-JCOF2222.js +0 -191
  204. package/dist/export-SUYRLI5Q.js +0 -112
  205. package/dist/extension-OBTGKQQD.js +0 -175
  206. package/dist/extensions-KYNTVTMO.js +0 -30
  207. package/dist/files-65PMW5IK.js +0 -47
  208. package/dist/history-DKCDI3JO.js +0 -128
  209. package/dist/import-DDUFE7AY.js +0 -23
  210. package/dist/join-I5QEE3LG.js +0 -66
  211. package/dist/list-JQ463EDA.js +0 -41
  212. package/dist/login-D7ETSU4R.js +0 -47
  213. package/dist/login-RIJF2F4G.js +0 -47
  214. package/dist/logout-5MLHZALK.js +0 -40
  215. package/dist/logout-UZJRGY4Z.js +0 -21
  216. package/dist/message-delivery-DFF5SJRM.js +0 -42
  217. package/dist/mind-IOJFLEM5.js +0 -108
  218. package/dist/mind-activity-tracker-F6O4Q2SL.js +0 -18
  219. package/dist/mind-list-WUPMQDYQ.js +0 -30
  220. package/dist/mind-manager-NBJF5D26.js +0 -32
  221. package/dist/mind-profile-P67FEHOY.js +0 -47
  222. package/dist/mind-service-2MQ6UK5N.js +0 -38
  223. package/dist/mind-sleep-WW2IX7JT.js +0 -42
  224. package/dist/mind-status-L3EFFRPR.js +0 -56
  225. package/dist/mind-wake-VSSGW465.js +0 -37
  226. package/dist/read-EBY56C33.js +0 -75
  227. package/dist/register-HD74C4TT.js +0 -47
  228. package/dist/reject-UJKFBHRO.js +0 -40
  229. package/dist/restart-3UCMRUVC.js +0 -33
  230. package/dist/scheduler-ZZ7XGQG6.js +0 -32
  231. package/dist/seed-check-S2IX25RL.js +0 -32
  232. package/dist/seed-cmd-DKOUFEAU.js +0 -36
  233. package/dist/seed-create-4XBBOLRH.js +0 -112
  234. package/dist/seed-sprout-GQEIIQRT.js +0 -132
  235. package/dist/send-QIV2INHB.js +0 -373
  236. package/dist/skill-PSQGRRJX.js +0 -358
  237. package/dist/skills/shared-files/SKILL.md +0 -44
  238. package/dist/skills/shared-files/scripts/merge.ts +0 -72
  239. package/dist/skills/shared-files/scripts/pull.ts +0 -52
  240. package/dist/sleep-manager-JTXSN7NV.js +0 -36
  241. package/dist/spirit-VRONKFMF.js +0 -23
  242. package/dist/split-STOROBYJ.js +0 -63
  243. package/dist/start-K2NCUUCG.js +0 -33
  244. package/dist/status-3JBTFSMI.js +0 -115
  245. package/dist/stop-H26JZDXF.js +0 -32
  246. package/dist/system-chat-JAPOJ3KE.js +0 -36
  247. package/dist/systems-XRI52VCH.js +0 -61
  248. package/dist/template-hash-A6VVKOXJ.js +0 -9
  249. package/dist/up-M5AS6SBV.js +0 -18
  250. package/dist/update-UD543CXX.js +0 -215
  251. package/dist/upgrade-O4Q7WJM3.js +0 -67
  252. package/dist/variant-7TGZHOU3.js +0 -41
  253. package/dist/web-assets/assets/index-CWJrVveV.css +0 -1
  254. package/dist/web-assets/assets/index-DJt14FRI.js +0 -75
  255. package/packages/extensions/pages/dist/ui/assets/index-tLTROSk5.js +0 -2
package/dist/daemon.js CHANGED
@@ -3,10 +3,18 @@ import {
3
3
  checkForUpdate,
4
4
  checkForUpdateCached,
5
5
  getCurrentVersion
6
- } from "./chunk-M7UL5S3Q.js";
6
+ } from "./chunk-OTC67N2Z.js";
7
7
  import {
8
8
  computeTemplateHash
9
- } from "./chunk-PVY5W6QN.js";
9
+ } from "./chunk-F7ZNLYKZ.js";
10
+ import {
11
+ acceptPending,
12
+ formatFileSize,
13
+ listPending,
14
+ rejectPending,
15
+ stageFile,
16
+ validateFilePath
17
+ } from "./chunk-BV65KRHM.js";
10
18
  import {
11
19
  PROMPT_DEFAULTS,
12
20
  PROMPT_KEYS,
@@ -17,12 +25,16 @@ import {
17
25
  completeOrphanedTurns,
18
26
  completeTurn,
19
27
  createTurn,
28
+ deleteSystemsConfig,
20
29
  deliverMessage,
21
30
  ensureSystemChannel,
22
31
  generateSystemReply,
23
32
  getActiveTurnId,
33
+ getAllDiscoveredExtensions,
24
34
  getDeliveryManager,
35
+ getExtensionStandardSkills,
25
36
  getLastToolUseEventId,
37
+ getLoadedExtensions,
26
38
  getMindManager,
27
39
  getMindPromptDefaults,
28
40
  getPrompt,
@@ -36,33 +48,91 @@ import {
36
48
  initMindManager,
37
49
  initScheduler,
38
50
  initSleepManager,
51
+ initSummarizer,
39
52
  initTokenBudget,
53
+ installNpmExtension,
40
54
  isConversationId,
41
55
  joinSystemChannel,
42
56
  linkToolResultToTurn,
57
+ loadAllExtensions,
58
+ notifyExtensionsDaemonStart,
59
+ notifyExtensionsDaemonStop,
43
60
  publish as publish2,
44
61
  publishTypingForChannels,
62
+ readSystemsConfig,
45
63
  recordInbound,
46
64
  recordOutbound,
47
65
  resolveMindToken,
48
- setSummaryEventId,
66
+ setExtensionEnabled,
49
67
  startMindFull,
50
68
  stopMindFull,
51
69
  subscribe as subscribe3,
52
70
  subscribeAll,
53
71
  substitute,
72
+ summarizeOrphanedTurns,
73
+ summarizeTurn,
54
74
  tagUntaggedInbound,
55
75
  tagUntaggedOutbound,
56
- trackToolUse
57
- } from "./chunk-3Z2DPESO.js";
76
+ trackToolUse,
77
+ uninstallNpmExtension,
78
+ writeSystemsConfig
79
+ } from "./chunk-WJPROOU5.js";
80
+ import "./chunk-5XJYUFZH.js";
81
+ import {
82
+ applyInitFiles,
83
+ composeTemplate,
84
+ copyTemplateToDir,
85
+ findTemplatesRoot,
86
+ listFiles
87
+ } from "./chunk-AOB6GVRM.js";
88
+ import {
89
+ SEED_SKILLS,
90
+ STANDARD_SKILLS,
91
+ autoUpdateMindSkills,
92
+ getSharedSkill,
93
+ getStandardSkillsWithExtensions,
94
+ importSkillFromDir,
95
+ initDefaultSkills,
96
+ installSkill,
97
+ isAutoUpdateSkillsEnabled,
98
+ listFilesRecursive,
99
+ listMindSkills,
100
+ listSharedSkills,
101
+ publishSkill,
102
+ removeSharedSkill,
103
+ sharedSkillsDir,
104
+ syncBuiltinSkills,
105
+ uninstallSkill,
106
+ updateSkill
107
+ } from "./chunk-IJHIXLVN.js";
58
108
  import {
59
109
  extractTextContent
60
- } from "./chunk-SKLSMHXO.js";
110
+ } from "./chunk-QWTR6AWZ.js";
61
111
  import {
62
112
  getActiveMinds,
63
113
  onMindEvent,
64
114
  stopAll
65
- } from "./chunk-LOEJ4HPQ.js";
115
+ } from "./chunk-N446KRP7.js";
116
+ import "./chunk-QHG4OMZL.js";
117
+ import {
118
+ approveUser,
119
+ changePassword,
120
+ countAdmins,
121
+ createUser,
122
+ deleteMindUser as deleteMindUser2,
123
+ deleteUser,
124
+ getOrCreateMindUser,
125
+ getOrCreateSystemUser,
126
+ getUser,
127
+ getUserByUsername,
128
+ listPendingUsers,
129
+ listUsers,
130
+ listUsersByType,
131
+ setUserRole,
132
+ syncMindProfile,
133
+ updateUserProfile,
134
+ verifyUser
135
+ } from "./chunk-XRQSAMX2.js";
66
136
  import {
67
137
  addMessage,
68
138
  createChannel,
@@ -90,28 +160,16 @@ import {
90
160
  publish,
91
161
  setConversationPrivate,
92
162
  subscribe as subscribe2
93
- } from "./chunk-RVGLDGMI.js";
94
- import "./chunk-GY5HBI7A.js";
163
+ } from "./chunk-WZRZFFCL.js";
95
164
  import {
96
- acceptPending,
97
- formatFileSize,
98
- listPending,
99
- rejectPending,
100
- stageFile,
101
- validateFilePath
102
- } from "./chunk-ALEF47VT.js";
103
- import "./chunk-7J3HEVR7.js";
104
- import {
105
- applyInitFiles,
106
- composeTemplate,
107
- copyTemplateToDir,
108
- findTemplatesRoot,
109
- listFiles
110
- } from "./chunk-G53F3JA4.js";
165
+ broadcast,
166
+ subscribe
167
+ } from "./chunk-CORXD635.js";
111
168
  import {
112
169
  readVoluteConfig,
113
170
  writeVoluteConfig
114
- } from "./chunk-OYAKCAVY.js";
171
+ } from "./chunk-ZSR72JB3.js";
172
+ import "./chunk-PWQ2ITYG.js";
115
173
  import {
116
174
  findBridgeForChannel,
117
175
  findOpenClawSession,
@@ -125,70 +183,18 @@ import {
125
183
  resolveChannelMapping,
126
184
  setBridgeConfig,
127
185
  setChannelMapping
128
- } from "./chunk-RSX4OPZY.js";
186
+ } from "./chunk-MDJGMOSD.js";
129
187
  import {
130
188
  loadMergedEnv,
131
189
  mindEnvPath,
132
190
  readEnv,
133
191
  sharedEnvPath,
134
192
  writeEnv
135
- } from "./chunk-2NGTS5UU.js";
193
+ } from "./chunk-A2ZLHBHG.js";
136
194
  import {
137
195
  isHomeOnlyArchive
138
- } from "./chunk-I5KY25PQ.js";
139
- import {
140
- deleteSystemsConfig,
141
- getExtensionStandardSkills,
142
- getLoadedExtensions,
143
- loadAllExtensions,
144
- notifyExtensionsDaemonStart,
145
- notifyExtensionsDaemonStop,
146
- readSystemsConfig,
147
- writeSystemsConfig
148
- } from "./chunk-A2A4KLFE.js";
149
- import "./chunk-PB65JZK2.js";
150
- import {
151
- approveUser,
152
- changePassword,
153
- countAdmins,
154
- createUser,
155
- deleteMindUser as deleteMindUser2,
156
- deleteUser,
157
- getOrCreateMindUser,
158
- getOrCreateSystemUser,
159
- getUser,
160
- getUserByUsername,
161
- listPendingUsers,
162
- listUsers,
163
- listUsersByType,
164
- setUserRole,
165
- updateUserProfile,
166
- verifyUser
167
- } from "./chunk-JYVGHWEJ.js";
168
- import {
169
- broadcast,
170
- subscribe
171
- } from "./chunk-KVK2DLWI.js";
172
- import {
173
- SEED_SKILLS,
174
- STANDARD_SKILLS,
175
- getSharedSkill,
176
- getStandardSkillsWithExtensions,
177
- importSkillFromDir,
178
- initDefaultSkills,
179
- installSkill,
180
- listFilesRecursive,
181
- listMindSkills,
182
- listSharedSkills,
183
- publishSkill,
184
- removeSharedSkill,
185
- sharedSkillsDir,
186
- syncBuiltinSkills,
187
- uninstallSkill,
188
- updateSkill
189
- } from "./chunk-C7I35G4R.js";
196
+ } from "./chunk-N5LMGYXX.js";
190
197
  import {
191
- aiCompleteUtility,
192
198
  getAiConfig,
193
199
  getAvailableModels,
194
200
  getConfiguredProviders,
@@ -203,38 +209,33 @@ import {
203
209
  setEnabledModels,
204
210
  setUtilityModel,
205
211
  unqualifyModelId
206
- } from "./chunk-QTUVYI7W.js";
212
+ } from "./chunk-FT5KETXZ.js";
207
213
  import {
208
214
  logBuffer,
209
215
  logger_default
210
- } from "./chunk-YUIHSKR6.js";
216
+ } from "./chunk-BKF4WQCY.js";
211
217
  import {
212
218
  exec,
213
219
  gitExec,
214
220
  resolveVoluteBin
215
- } from "./chunk-KIEPMIM5.js";
221
+ } from "./chunk-AN2W47GW.js";
216
222
  import {
217
223
  chownMindDir,
218
224
  createMindUser,
219
225
  deleteMindUser,
220
226
  ensureVoluteGroup,
221
227
  isIsolationEnabled,
222
- mindUserName,
223
228
  wrapForIsolation
224
- } from "./chunk-VH33ZWMW.js";
229
+ } from "./chunk-VHJRZM2S.js";
225
230
  import {
226
231
  isSetupComplete,
227
232
  readGlobalConfig,
228
233
  writeGlobalConfig
229
- } from "./chunk-N432I7QH.js";
230
- import {
231
- readSessionFile
232
- } from "./chunk-UKVWJRKN.js";
233
- import "./chunk-D424ZQGI.js";
234
+ } from "./chunk-BMZQYACC.js";
234
235
  import {
235
236
  buildVoluteSlug,
236
237
  slugify
237
- } from "./chunk-G6BSYHPK.js";
238
+ } from "./chunk-NJK5SDGR.js";
238
239
  import {
239
240
  addMind,
240
241
  addVariant,
@@ -257,34 +258,35 @@ import {
257
258
  validateMindName,
258
259
  voluteHome,
259
260
  voluteSystemDir
260
- } from "./chunk-LRCG2JLP.js";
261
+ } from "./chunk-BDYXIWA5.js";
261
262
  import {
262
263
  activity,
264
+ conversationParticipants,
263
265
  conversations,
264
- messages,
265
266
  mindHistory,
266
267
  sessions,
268
+ summaries,
267
269
  systemPrompts,
268
270
  turns,
269
271
  users
270
- } from "./chunk-RPZZSXV3.js";
272
+ } from "./chunk-5N7Y5WAM.js";
271
273
  import {
272
274
  __export
273
- } from "./chunk-K3NQKI34.js";
275
+ } from "./chunk-7KJOFUNN.js";
274
276
 
275
- // src/daemon.ts
277
+ // packages/daemon/src/daemon.ts
276
278
  import { randomBytes } from "crypto";
277
- import { mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
279
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync11, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
278
280
  import { homedir as homedir3 } from "os";
279
- import { resolve as resolve19 } from "path";
281
+ import { resolve as resolve17 } from "path";
280
282
  import { format } from "util";
281
283
 
282
- // src/lib/daemon/bridge-manager.ts
284
+ // packages/daemon/src/lib/daemon/bridge-manager.ts
283
285
  import { spawn } from "child_process";
284
286
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
285
287
  import { dirname, resolve as resolve2 } from "path";
286
288
 
287
- // src/lib/bridge-defs.ts
289
+ // packages/daemon/src/lib/bridge-defs.ts
288
290
  import { existsSync, readFileSync } from "fs";
289
291
  import { resolve } from "path";
290
292
  var BUILTIN_DEFS = {
@@ -346,7 +348,7 @@ function checkMissingBridgeEnv(def, env) {
346
348
  return def.envVars.filter((v) => v.required && !env[v.name]);
347
349
  }
348
350
 
349
- // src/lib/daemon/bridge-manager.ts
351
+ // packages/daemon/src/lib/daemon/bridge-manager.ts
350
352
  var blog = logger_default.child("bridges");
351
353
  function searchUpwards(...segments) {
352
354
  let searchDir = dirname(new URL(import.meta.url).pathname);
@@ -486,8 +488,8 @@ var BridgeManager = class {
486
488
  if (!tracked) return;
487
489
  this.stopping.add(platform);
488
490
  this.bridges.delete(platform);
489
- await new Promise((resolve20) => {
490
- tracked.child.on("exit", () => resolve20());
491
+ await new Promise((resolve18) => {
492
+ tracked.child.on("exit", () => resolve18());
491
493
  try {
492
494
  if (tracked.child.pid) {
493
495
  process.kill(-tracked.child.pid, "SIGTERM");
@@ -498,7 +500,7 @@ var BridgeManager = class {
498
500
  if (err instanceof Error && err.code !== "ESRCH") {
499
501
  blog.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
500
502
  }
501
- resolve20();
503
+ resolve18();
502
504
  }
503
505
  setTimeout(() => {
504
506
  try {
@@ -509,7 +511,7 @@ var BridgeManager = class {
509
511
  }
510
512
  } catch {
511
513
  }
512
- resolve20();
514
+ resolve18();
513
515
  }, 5e3);
514
516
  });
515
517
  this.stopping.delete(platform);
@@ -597,7 +599,7 @@ function getBridgeManager() {
597
599
  return instance;
598
600
  }
599
601
 
600
- // src/lib/history-cleanup.ts
602
+ // packages/daemon/src/lib/history-cleanup.ts
601
603
  import { and, eq, lt } from "drizzle-orm";
602
604
  var LOG_RETENTION_MS = 24 * 60 * 60 * 1e3;
603
605
  async function cleanExpiredLogs() {
@@ -606,173 +608,7 @@ async function cleanExpiredLogs() {
606
608
  await db.delete(mindHistory).where(and(eq(mindHistory.type, "log"), lt(mindHistory.created_at, cutoff)));
607
609
  }
608
610
 
609
- // src/lib/shared.ts
610
- import { execFileSync } from "child_process";
611
- import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
612
- import { resolve as resolve3 } from "path";
613
- function readWorktreeGitDir(worktreePath) {
614
- const dotGit = resolve3(worktreePath, ".git");
615
- if (!existsSync3(dotGit)) return null;
616
- try {
617
- const content = readFileSync3(dotGit, "utf-8").trim();
618
- const match = content.match(/^gitdir:\s*(.+)$/);
619
- return match ? match[1] : null;
620
- } catch {
621
- return null;
622
- }
623
- }
624
- function sharedDir() {
625
- return resolve3(voluteHome(), "shared");
626
- }
627
- async function ensureSharedRepo() {
628
- const dir = sharedDir();
629
- mkdirSync2(dir, { recursive: true });
630
- if (existsSync3(resolve3(dir, ".git"))) {
631
- try {
632
- await gitExec(["rev-parse", "HEAD"], { cwd: dir });
633
- return;
634
- } catch (err) {
635
- const msg = err instanceof Error ? err.message : String(err);
636
- if (msg.includes("unknown revision") || msg.includes("bad default revision")) {
637
- logger_default.warn("shared repo has no commits, re-initializing");
638
- rmSync(resolve3(dir, ".git"), { recursive: true, force: true });
639
- } else {
640
- throw err;
641
- }
642
- }
643
- }
644
- const initArgs = isIsolationEnabled() ? ["init", "--shared=group"] : ["init"];
645
- await gitExec(initArgs, { cwd: dir });
646
- await gitExec(["checkout", "-b", "main"], { cwd: dir });
647
- const pagesDir = resolve3(dir, "pages");
648
- mkdirSync2(pagesDir, { recursive: true });
649
- writeFileSync2(resolve3(pagesDir, ".gitkeep"), "");
650
- await gitExec(["add", "-A"], { cwd: dir });
651
- await gitExec(["commit", "-m", "init shared repo"], { cwd: dir });
652
- if (isIsolationEnabled()) {
653
- try {
654
- execFileSync("chgrp", ["-R", "volute", dir], { stdio: "ignore" });
655
- } catch (err) {
656
- logger_default.warn("failed to chgrp shared repo to volute group", logger_default.errorData(err));
657
- }
658
- chmodSync(dir, 1533);
659
- }
660
- }
661
- async function addSharedWorktree(mindName, mindDir2) {
662
- const dir = sharedDir();
663
- if (!existsSync3(resolve3(dir, ".git"))) return;
664
- const worktreePath = resolve3(mindDir2, "home", "shared");
665
- if (existsSync3(worktreePath)) return;
666
- let branchExists = false;
667
- try {
668
- await gitExec(["rev-parse", "--verify", mindName], { cwd: dir });
669
- branchExists = true;
670
- } catch {
671
- }
672
- if (branchExists) {
673
- await gitExec(["worktree", "add", worktreePath, mindName], { cwd: dir });
674
- } else {
675
- await gitExec(["worktree", "add", "-b", mindName, worktreePath], { cwd: dir });
676
- }
677
- if (isIsolationEnabled()) {
678
- const worktreeGitDir = readWorktreeGitDir(worktreePath);
679
- if (worktreeGitDir) {
680
- try {
681
- const user = mindUserName(mindName);
682
- execFileSync("chown", ["-R", `${user}:volute`, worktreeGitDir], { stdio: "ignore" });
683
- } catch (err) {
684
- logger_default.warn(`failed to chown worktree git dir for ${mindName}`, logger_default.errorData(err));
685
- }
686
- }
687
- }
688
- }
689
- async function removeSharedWorktree(mindName, mindDir2) {
690
- const dir = sharedDir();
691
- if (!existsSync3(resolve3(dir, ".git"))) return;
692
- const worktreePath = resolve3(mindDir2, "home", "shared");
693
- if (existsSync3(worktreePath)) {
694
- try {
695
- await gitExec(["worktree", "remove", "--force", worktreePath], { cwd: dir });
696
- } catch (err) {
697
- logger_default.debug(`worktree remove failed for ${mindName}`, logger_default.errorData(err));
698
- }
699
- }
700
- try {
701
- await gitExec(["worktree", "prune"], { cwd: dir });
702
- } catch (err) {
703
- logger_default.debug(`worktree prune failed for ${mindName}`, logger_default.errorData(err));
704
- }
705
- try {
706
- await gitExec(["branch", "-D", mindName], { cwd: dir });
707
- } catch {
708
- }
709
- }
710
- var sharedLock = Promise.resolve();
711
- function rechownWorktree(worktreePath, mindName) {
712
- if (!isIsolationEnabled()) return;
713
- try {
714
- const user = mindUserName(mindName);
715
- execFileSync("chown", ["-R", `${user}:volute`, worktreePath], { stdio: "ignore" });
716
- } catch (err) {
717
- logger_default.warn(`failed to rechown worktree for ${mindName}`, logger_default.errorData(err));
718
- }
719
- }
720
- async function withSharedLock(fn) {
721
- const prev = sharedLock;
722
- let resolve_;
723
- sharedLock = new Promise((r) => {
724
- resolve_ = r;
725
- });
726
- await prev;
727
- try {
728
- return await fn();
729
- } finally {
730
- resolve_();
731
- }
732
- }
733
- async function sharedMerge(mindName, mindDir2, message) {
734
- return withSharedLock(async () => {
735
- const dir = sharedDir();
736
- const worktreePath = resolve3(mindDir2, "home", "shared");
737
- const status = (await gitExec(["status", "--porcelain"], { cwd: worktreePath })).trim();
738
- if (status) {
739
- await gitExec(["add", "-A"], { cwd: worktreePath });
740
- await gitExec(
741
- ["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", `wip: ${mindName}`],
742
- { cwd: worktreePath }
743
- );
744
- }
745
- const diff = (await gitExec(["diff", `main...${mindName}`, "--stat"], { cwd: dir })).trim();
746
- if (!diff) {
747
- return { ok: true, message: "Nothing to merge" };
748
- }
749
- try {
750
- await gitExec(["merge", "--squash", mindName], { cwd: dir });
751
- } catch {
752
- try {
753
- await gitExec(["reset", "--hard", "HEAD"], { cwd: dir });
754
- } catch (resetErr) {
755
- logger_default.error("reset after squash conflict failed in shared repo", logger_default.errorData(resetErr));
756
- }
757
- return { ok: false, conflicts: true, message: "Merge conflicts detected" };
758
- }
759
- await gitExec(["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", message], {
760
- cwd: dir
761
- });
762
- try {
763
- await gitExec(["reset", "--hard", "main"], { cwd: worktreePath });
764
- } catch {
765
- return {
766
- ok: true,
767
- message: "Merged to main, but branch reset failed \u2014 run 'volute shared pull' to sync"
768
- };
769
- }
770
- rechownWorktree(worktreePath, mindName);
771
- return { ok: true };
772
- });
773
- }
774
-
775
- // src/web/api/system.ts
611
+ // packages/daemon/src/web/api/system.ts
776
612
  import { zValidator } from "@hono/zod-validator";
777
613
  import { getProviders } from "@mariozechner/pi-ai";
778
614
  import { getOAuthProvider, getOAuthProviders } from "@mariozechner/pi-ai/oauth";
@@ -780,7 +616,7 @@ import { Hono } from "hono";
780
616
  import { streamSSE } from "hono/streaming";
781
617
  import { z } from "zod";
782
618
 
783
- // src/lib/services/imagegen.ts
619
+ // packages/daemon/src/lib/services/imagegen.ts
784
620
  import Replicate from "replicate";
785
621
  var PROVIDERS = {
786
622
  replicate: {
@@ -975,7 +811,7 @@ async function generateImage(model, prompt) {
975
811
  return PROVIDERS[providerId].generate(modelId, prompt, apiKey);
976
812
  }
977
813
 
978
- // src/web/middleware/auth.ts
814
+ // packages/daemon/src/web/middleware/auth.ts
979
815
  import { timingSafeEqual } from "crypto";
980
816
  import { eq as eq2, lt as lt2 } from "drizzle-orm";
981
817
  import { getCookie } from "hono/cookie";
@@ -1107,7 +943,7 @@ var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
1107
943
  await next();
1108
944
  });
1109
945
 
1110
- // src/web/api/system.ts
946
+ // packages/daemon/src/web/api/system.ts
1111
947
  var DEFAULT_API_URL = "https://volute.systems";
1112
948
  var igLog = logger_default.child("imagegen");
1113
949
  var app = new Hono().post("/restart", requireAdmin, (c) => {
@@ -1127,16 +963,23 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1127
963
  stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
1128
964
  });
1129
965
  });
1130
- await new Promise((resolve20) => {
966
+ await new Promise((resolve18) => {
1131
967
  stream.onAbort(() => {
1132
968
  unsubscribe();
1133
- resolve20();
969
+ resolve18();
1134
970
  });
1135
971
  });
1136
972
  });
1137
973
  }).get("/info", (c) => {
1138
974
  const config2 = readSystemsConfig();
1139
- return c.json({ system: config2?.system ?? null });
975
+ const globalConfig = readGlobalConfig();
976
+ return c.json({ system: config2?.system ?? null, name: globalConfig.name ?? null });
977
+ }).put("/info", requireAdmin, zValidator("json", z.object({ name: z.string() })), (c) => {
978
+ const { name } = c.req.valid("json");
979
+ const config2 = readGlobalConfig();
980
+ config2.name = name.trim() || void 0;
981
+ writeGlobalConfig(config2);
982
+ return c.json({ name: config2.name ?? null });
1140
983
  }).post(
1141
984
  "/register",
1142
985
  requireAdmin,
@@ -1311,7 +1154,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1311
1154
  const { model, prompt } = c.req.valid("json");
1312
1155
  try {
1313
1156
  const buf = await generateImage(model, prompt);
1314
- return new Response(buf, {
1157
+ return new Response(new Uint8Array(buf), {
1315
1158
  headers: {
1316
1159
  "Content-Type": "image/png",
1317
1160
  "Content-Length": String(buf.length)
@@ -1338,13 +1181,15 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1338
1181
  let authMethod = null;
1339
1182
  if (providerConfig?.oauth) authMethod = "oauth";
1340
1183
  else if (providerConfig?.apiKey) authMethod = "api_key";
1184
+ const health = authMethod === "oauth" ? oauthHealth.get(id) : void 0;
1341
1185
  return {
1342
1186
  id,
1343
1187
  oauth: !!oauth,
1344
1188
  oauthName: oauth?.name,
1345
1189
  usesCallbackServer: !!oauth?.usesCallbackServer,
1346
1190
  configured,
1347
- authMethod
1191
+ authMethod,
1192
+ ...health && { oauthHealthy: health.healthy, oauthError: health.error }
1348
1193
  };
1349
1194
  });
1350
1195
  return c.json(result);
@@ -1382,15 +1227,17 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1382
1227
  zValidator(
1383
1228
  "json",
1384
1229
  z.object({
1385
- spiritModel: z.string().nullable(),
1230
+ spiritModel: z.string().nullable().optional(),
1386
1231
  utilityModel: z.string().nullable()
1387
1232
  })
1388
1233
  ),
1389
1234
  (c) => {
1390
1235
  const { spiritModel, utilityModel } = c.req.valid("json");
1391
1236
  const config2 = readGlobalConfig();
1392
- config2.spiritModel = spiritModel ?? void 0;
1393
- writeGlobalConfig(config2);
1237
+ if (spiritModel !== void 0) {
1238
+ config2.spiritModel = spiritModel ?? void 0;
1239
+ writeGlobalConfig(config2);
1240
+ }
1394
1241
  setUtilityModel(utilityModel ?? void 0);
1395
1242
  return c.json({ ok: true });
1396
1243
  }
@@ -1430,8 +1277,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1430
1277
  createdAt: Date.now()
1431
1278
  };
1432
1279
  oauthFlows.set(flowId, flow);
1433
- const promptPromise = needsManualCode ? new Promise((resolve20) => {
1434
- flow.resolveCode = resolve20;
1280
+ const promptPromise = needsManualCode ? new Promise((resolve18) => {
1281
+ flow.resolveCode = resolve18;
1435
1282
  }) : void 0;
1436
1283
  oauthProvider.login({
1437
1284
  onAuth: (info) => {
@@ -1448,10 +1295,11 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1448
1295
  return "";
1449
1296
  },
1450
1297
  onManualCodeInput: needsManualCode ? () => promptPromise : void 0
1451
- }).then((credentials) => {
1298
+ }).then(async (credentials) => {
1452
1299
  saveProviderConfig(provider, { oauth: credentials });
1453
1300
  const existing = oauthFlows.get(flowId);
1454
1301
  if (existing) existing.status = "complete";
1302
+ await restartMindsForProvider(provider);
1455
1303
  }).catch((err) => {
1456
1304
  const existing = oauthFlows.get(flowId);
1457
1305
  if (existing) {
@@ -1459,7 +1307,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1459
1307
  existing.error = err instanceof Error ? err.message : String(err);
1460
1308
  }
1461
1309
  });
1462
- await new Promise((resolve20) => setTimeout(resolve20, 2e3));
1310
+ await new Promise((resolve18) => setTimeout(resolve18, 2e3));
1463
1311
  const state = oauthFlows.get(flowId);
1464
1312
  return c.json({
1465
1313
  flowId,
@@ -1505,7 +1353,54 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1505
1353
  setTimeout(() => oauthFlows.delete(flowId), 3e4);
1506
1354
  }
1507
1355
  return c.json(result);
1508
- });
1356
+ }).get("/mind-defaults", requireAdmin, (c) => {
1357
+ const config2 = readGlobalConfig();
1358
+ return c.json(config2.mindDefaults ?? {});
1359
+ }).put(
1360
+ "/mind-defaults",
1361
+ requireAdmin,
1362
+ zValidator(
1363
+ "json",
1364
+ z.object({
1365
+ cognition: z.object({
1366
+ model: z.string().optional(),
1367
+ thinkingLevel: z.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
1368
+ maxThinkingTokens: z.number().nonnegative().optional(),
1369
+ tokenBudget: z.number().nonnegative().optional(),
1370
+ tokenBudgetPeriodMinutes: z.number().positive().optional(),
1371
+ compaction: z.object({ maxContextTokens: z.number().positive().optional() }).optional()
1372
+ }).optional(),
1373
+ sleep: z.object({
1374
+ enabled: z.boolean().optional(),
1375
+ schedule: z.object({ sleep: z.string(), wake: z.string() }).optional(),
1376
+ wakeTriggers: z.object({
1377
+ mentions: z.boolean().optional(),
1378
+ dms: z.boolean().optional(),
1379
+ channels: z.array(z.string()).optional(),
1380
+ senders: z.array(z.string()).optional()
1381
+ }).optional()
1382
+ }).optional(),
1383
+ schedules: z.array(
1384
+ z.object({
1385
+ id: z.string().min(1),
1386
+ cron: z.string().optional(),
1387
+ message: z.string().optional(),
1388
+ script: z.string().optional(),
1389
+ session: z.string().optional(),
1390
+ enabled: z.boolean(),
1391
+ whileSleeping: z.enum(["skip", "queue", "trigger-wake"]).optional()
1392
+ })
1393
+ ).optional()
1394
+ })
1395
+ ),
1396
+ (c) => {
1397
+ const mindDefaults = c.req.valid("json");
1398
+ const config2 = readGlobalConfig();
1399
+ config2.mindDefaults = mindDefaults;
1400
+ writeGlobalConfig(config2);
1401
+ return c.json({ ok: true });
1402
+ }
1403
+ );
1509
1404
  var oauthFlows = /* @__PURE__ */ new Map();
1510
1405
  var OAUTH_FLOW_TTL_MS = 10 * 60 * 1e3;
1511
1406
  function cleanupOAuthFlows() {
@@ -1516,8 +1411,29 @@ function cleanupOAuthFlows() {
1516
1411
  }
1517
1412
  }
1518
1413
  }
1414
+ var PROVIDER_TEMPLATES = {
1415
+ anthropic: (t) => !t || t === "claude"
1416
+ };
1417
+ async function restartMindsForProvider(provider) {
1418
+ const matchTemplate = PROVIDER_TEMPLATES[provider];
1419
+ if (!matchTemplate) return;
1420
+ const manager = getMindManager();
1421
+ const running = manager.getRunningMinds();
1422
+ for (const name of running) {
1423
+ try {
1424
+ const entry = await findMind(name);
1425
+ if (entry && matchTemplate(entry.template)) {
1426
+ slog.info(`restarting ${name} after ${provider} credential refresh`);
1427
+ await manager.restartMind(name);
1428
+ }
1429
+ } catch (err) {
1430
+ slog.warn(`failed to check mind ${name} for restart`, logger_default.errorData(err));
1431
+ }
1432
+ }
1433
+ }
1519
1434
  var apiKeyCache = /* @__PURE__ */ new Map();
1520
- var API_KEY_TTL_MS = 4 * 60 * 1e3;
1435
+ var API_KEY_CACHE_TTL_MS = 4 * 60 * 1e3;
1436
+ var REFRESH_CHECK_INTERVAL_MS = 6e4;
1521
1437
  function getCachedApiKey(provider) {
1522
1438
  const cached = apiKeyCache.get(provider);
1523
1439
  if (cached && Date.now() < cached.expiresAt) return cached.key;
@@ -1525,15 +1441,41 @@ function getCachedApiKey(provider) {
1525
1441
  }
1526
1442
  var keyRefreshTimer = null;
1527
1443
  var slog = logger_default.child("ai-keys");
1444
+ var oauthHealth = /* @__PURE__ */ new Map();
1445
+ function needsRefresh(oauth) {
1446
+ const expires = oauth.expires;
1447
+ if (!expires || Date.now() >= expires) return true;
1448
+ return expires - Date.now() < 15 * 60 * 1e3;
1449
+ }
1528
1450
  async function refreshApiKeyCache() {
1529
- for (const provider of getConfiguredProviders()) {
1451
+ const ai = getAiConfig();
1452
+ const providers = getConfiguredProviders();
1453
+ for (const id of oauthHealth.keys()) {
1454
+ if (!providers.includes(id)) oauthHealth.delete(id);
1455
+ }
1456
+ for (const provider of providers) {
1457
+ const providerConfig = ai?.providers[provider];
1458
+ if (!providerConfig?.oauth) continue;
1459
+ if (!needsRefresh(providerConfig.oauth) && getCachedApiKey(provider)) {
1460
+ if (!oauthHealth.has(provider)) oauthHealth.set(provider, { healthy: true });
1461
+ continue;
1462
+ }
1530
1463
  try {
1531
1464
  const key = await resolveApiKey(provider);
1532
1465
  if (key) {
1533
- apiKeyCache.set(provider, { key, expiresAt: Date.now() + API_KEY_TTL_MS });
1466
+ apiKeyCache.set(provider, { key, expiresAt: Date.now() + API_KEY_CACHE_TTL_MS });
1467
+ oauthHealth.set(provider, { healthy: true });
1468
+ } else {
1469
+ oauthHealth.set(provider, {
1470
+ healthy: false,
1471
+ error: "Failed to resolve credentials",
1472
+ since: oauthHealth.get(provider)?.since ?? Date.now()
1473
+ });
1534
1474
  }
1535
1475
  } catch (err) {
1476
+ const message = err instanceof Error ? err.message : String(err);
1536
1477
  slog.warn(`API key refresh failed for ${provider}`, logger_default.errorData(err));
1478
+ oauthHealth.set(provider, { healthy: false, error: message, since: Date.now() });
1537
1479
  }
1538
1480
  }
1539
1481
  }
@@ -1546,7 +1488,7 @@ function startApiKeyRefresh() {
1546
1488
  refreshApiKeyCache().catch((err) => {
1547
1489
  slog.warn("periodic API key cache refresh failed", logger_default.errorData(err));
1548
1490
  });
1549
- }, API_KEY_TTL_MS);
1491
+ }, REFRESH_CHECK_INTERVAL_MS);
1550
1492
  }
1551
1493
  function stopApiKeyRefresh() {
1552
1494
  if (keyRefreshTimer) {
@@ -1556,13 +1498,14 @@ function stopApiKeyRefresh() {
1556
1498
  }
1557
1499
  var system_default = app;
1558
1500
 
1559
- // src/web/app.ts
1560
- import { Hono as Hono30 } from "hono";
1501
+ // packages/daemon/src/web/app.ts
1502
+ import { Hono as Hono28 } from "hono";
1561
1503
  import { bodyLimit } from "hono/body-limit";
1504
+ import { cors } from "hono/cors";
1562
1505
  import { csrf } from "hono/csrf";
1563
1506
  import { HTTPException } from "hono/http-exception";
1564
1507
 
1565
- // src/web/api/activity.ts
1508
+ // packages/daemon/src/web/api/activity.ts
1566
1509
  import { desc } from "drizzle-orm";
1567
1510
  import { Hono as Hono2 } from "hono";
1568
1511
  import { streamSSE as streamSSE2 } from "hono/streaming";
@@ -1620,8 +1563,8 @@ var app2 = new Hono2().get("/events", async (c) => {
1620
1563
  });
1621
1564
  }, 15e3);
1622
1565
  cleanups.push(() => clearInterval(keepAlive));
1623
- await new Promise((resolve20) => {
1624
- stream.onAbort(() => resolve20());
1566
+ await new Promise((resolve18) => {
1567
+ stream.onAbort(() => resolve18());
1625
1568
  });
1626
1569
  } finally {
1627
1570
  for (const cleanup of cleanups) {
@@ -1635,9 +1578,9 @@ var app2 = new Hono2().get("/events", async (c) => {
1635
1578
  });
1636
1579
  var activity_default = app2;
1637
1580
 
1638
- // src/web/api/auth.ts
1639
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
1640
- import { extname, resolve as resolve4 } from "path";
1581
+ // packages/daemon/src/web/api/auth.ts
1582
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "fs";
1583
+ import { extname, resolve as resolve3 } from "path";
1641
1584
  import { zValidator as zValidator2 } from "@hono/zod-validator";
1642
1585
  import { Hono as Hono3 } from "hono";
1643
1586
  import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
@@ -1674,7 +1617,7 @@ var AVATAR_MIME = {
1674
1617
  };
1675
1618
  var MAX_AVATAR_SIZE = 2 * 1024 * 1024;
1676
1619
  function avatarsDir() {
1677
- return resolve4(voluteHome(), "avatars");
1620
+ return resolve3(voluteHome(), "avatars");
1678
1621
  }
1679
1622
  var authenticated = new Hono3().use(authMiddleware).post("/change-password", zValidator2("json", changePasswordSchema), async (c) => {
1680
1623
  const user = c.get("user");
@@ -1709,13 +1652,13 @@ var authenticated = new Hono3().use(authMiddleware).post("/change-password", zVa
1709
1652
  return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
1710
1653
  }
1711
1654
  const dir = avatarsDir();
1712
- mkdirSync3(dir, { recursive: true });
1655
+ mkdirSync2(dir, { recursive: true });
1713
1656
  const filename = `avatar-${user.id}${ext}`;
1714
1657
  const buffer2 = Buffer.from(await file.arrayBuffer());
1715
- writeFileSync3(resolve4(dir, filename), buffer2);
1658
+ writeFileSync2(resolve3(dir, filename), buffer2);
1716
1659
  if (user.avatar && user.avatar !== filename) {
1717
- const oldPath = resolve4(dir, user.avatar);
1718
- rmSync2(oldPath, { force: true });
1660
+ const oldPath = resolve3(dir, user.avatar);
1661
+ rmSync(oldPath, { force: true });
1719
1662
  }
1720
1663
  await updateUserProfile(user.id, { avatar: filename });
1721
1664
  const sessionId = getCookie2(c, "volute_session");
@@ -1729,8 +1672,8 @@ var authenticated = new Hono3().use(authMiddleware).post("/change-password", zVa
1729
1672
  }).delete("/avatar", async (c) => {
1730
1673
  const user = c.get("user");
1731
1674
  if (user.avatar) {
1732
- const path = resolve4(avatarsDir(), user.avatar);
1733
- rmSync2(path, { force: true });
1675
+ const path = resolve3(avatarsDir(), user.avatar);
1676
+ rmSync(path, { force: true });
1734
1677
  }
1735
1678
  await updateUserProfile(user.id, { avatar: null });
1736
1679
  const sessionId = getCookie2(c, "volute_session");
@@ -1859,7 +1802,13 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
1859
1802
  }
1860
1803
  return c.json({ ok: true });
1861
1804
  }).get("/me", async (c) => {
1862
- const sessionId = getCookie2(c, "volute_session");
1805
+ let sessionId = getCookie2(c, "volute_session");
1806
+ if (!sessionId) {
1807
+ const authHeader = c.req.header("Authorization");
1808
+ if (authHeader?.startsWith("Bearer ")) {
1809
+ sessionId = authHeader.slice(7);
1810
+ }
1811
+ }
1863
1812
  if (!sessionId) return c.json({ error: "Not logged in" }, 401);
1864
1813
  const userId = await getSessionUserId(sessionId);
1865
1814
  if (userId == null) return c.json({ error: "Not logged in" }, 401);
@@ -1879,13 +1828,13 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
1879
1828
  return c.json({ error: "Invalid filename" }, 400);
1880
1829
  }
1881
1830
  const dir = avatarsDir();
1882
- const filePath = resolve4(dir, filename);
1831
+ const filePath = resolve3(dir, filename);
1883
1832
  if (!filePath.startsWith(`${dir}/`)) return c.json({ error: "Invalid path" }, 400);
1884
- if (!existsSync4(filePath)) return c.json({ error: "Not found" }, 404);
1833
+ if (!existsSync3(filePath)) return c.json({ error: "Not found" }, 404);
1885
1834
  const ext = extname(filename).toLowerCase();
1886
1835
  const mime = AVATAR_MIME[ext];
1887
1836
  if (!mime) return c.json({ error: "Invalid file type" }, 400);
1888
- const data = readFileSync4(filePath);
1837
+ const data = readFileSync3(filePath);
1889
1838
  return c.body(data, 200, {
1890
1839
  "Content-Type": mime,
1891
1840
  "Cache-Control": "public, max-age=3600",
@@ -1894,12 +1843,12 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
1894
1843
  }).route("/", admin).route("/", authenticated);
1895
1844
  var auth_default = app3;
1896
1845
 
1897
- // src/web/api/bridges.ts
1846
+ // packages/daemon/src/web/api/bridges.ts
1898
1847
  import { zValidator as zValidator3 } from "@hono/zod-validator";
1899
1848
  import { Hono as Hono4 } from "hono";
1900
1849
  import { z as z3 } from "zod";
1901
1850
 
1902
- // src/lib/puppets.ts
1851
+ // packages/daemon/src/lib/puppets.ts
1903
1852
  import { and as and2, eq as eq3 } from "drizzle-orm";
1904
1853
  async function findOrCreatePuppet(platform, platformId, displayName) {
1905
1854
  const username = `${platform}:${platformId}`;
@@ -1945,7 +1894,7 @@ async function findOrCreatePuppet(platform, platformId, displayName) {
1945
1894
  }
1946
1895
  }
1947
1896
 
1948
- // src/web/api/bridges.ts
1897
+ // packages/daemon/src/web/api/bridges.ts
1949
1898
  var inboundSchema = z3.object({
1950
1899
  content: z3.array(
1951
1900
  z3.union([
@@ -2020,7 +1969,7 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
2020
1969
  }
2021
1970
  const participants = await getParticipants(channel.id);
2022
1971
  if (!participants.some((p) => p.userId === puppet.id)) {
2023
- const { addParticipant } = await import("./conversations-AWI5SZW2.js");
1972
+ const { addParticipant } = await import("./conversations-2PW57WO2.js");
2024
1973
  await addParticipant(channel.id, puppet.id);
2025
1974
  }
2026
1975
  const contentBlocks = body.content;
@@ -2113,8 +2062,8 @@ async function fanOutToBridgedMinds(opts) {
2113
2062
  const participants = await getParticipants(opts.conversationId);
2114
2063
  const mindParticipants = participants.filter((p) => p.userType === "mind");
2115
2064
  const participantNames = participants.map((p) => p.username);
2116
- const { getMindManager: getMindManager2 } = await import("./mind-manager-NBJF5D26.js");
2117
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
2065
+ const { getMindManager: getMindManager2 } = await import("./mind-manager-HFLB5653.js");
2066
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
2118
2067
  const manager = getMindManager2();
2119
2068
  const sm = getSleepManagerIfReady2();
2120
2069
  const targetMinds = mindParticipants.filter((ap) => {
@@ -2142,10 +2091,10 @@ async function fanOutToBridgedMinds(opts) {
2142
2091
  }
2143
2092
  var bridges_default = app4;
2144
2093
 
2145
- // src/web/api/channels.ts
2094
+ // packages/daemon/src/web/api/channels.ts
2146
2095
  import { Hono as Hono5 } from "hono";
2147
2096
 
2148
- // src/lib/channels/discord.ts
2097
+ // packages/daemon/src/lib/channels/discord.ts
2149
2098
  var discord_exports = {};
2150
2099
  __export(discord_exports, {
2151
2100
  createConversation: () => createConversation2,
@@ -2155,7 +2104,7 @@ __export(discord_exports, {
2155
2104
  send: () => send
2156
2105
  });
2157
2106
 
2158
- // src/connectors/sdk.ts
2107
+ // packages/daemon/src/connectors/sdk.ts
2159
2108
  function splitMessage(text, maxLength) {
2160
2109
  const chunks = [];
2161
2110
  while (text.length > maxLength) {
@@ -2168,7 +2117,7 @@ function splitMessage(text, maxLength) {
2168
2117
  return chunks;
2169
2118
  }
2170
2119
 
2171
- // src/lib/channels/discord.ts
2120
+ // packages/daemon/src/lib/channels/discord.ts
2172
2121
  var DISCORD_MAX_LENGTH = 2e3;
2173
2122
  var API_BASE = "https://discord.com/api/v10";
2174
2123
  function requireToken(env) {
@@ -2194,8 +2143,8 @@ async function read(env, channelSlug, limit) {
2194
2143
  if (!res.ok) {
2195
2144
  throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
2196
2145
  }
2197
- const messages2 = await res.json();
2198
- return messages2.reverse().map((m) => `${m.author.username}: ${m.content}`).join("\n");
2146
+ const messages = await res.json();
2147
+ return messages.reverse().map((m) => `${m.author.username}: ${m.content}`).join("\n");
2199
2148
  }
2200
2149
  async function send(env, channelSlug, message, images) {
2201
2150
  const token = requireToken(env);
@@ -2315,7 +2264,7 @@ async function createConversation2(env, participants, _name) {
2315
2264
  return `discord:${dmChannel.id}`;
2316
2265
  }
2317
2266
 
2318
- // src/lib/channels/slack.ts
2267
+ // packages/daemon/src/lib/channels/slack.ts
2319
2268
  var slack_exports = {};
2320
2269
  __export(slack_exports, {
2321
2270
  createConversation: () => createConversation3,
@@ -2486,7 +2435,7 @@ async function createConversation3(env, participants, name) {
2486
2435
  return `slack:${openData.channel.id}`;
2487
2436
  }
2488
2437
 
2489
- // src/lib/channels/telegram.ts
2438
+ // packages/daemon/src/lib/channels/telegram.ts
2490
2439
  var telegram_exports = {};
2491
2440
  __export(telegram_exports, {
2492
2441
  createConversation: () => createConversation4,
@@ -2582,7 +2531,7 @@ async function createConversation4() {
2582
2531
  );
2583
2532
  }
2584
2533
 
2585
- // src/lib/channels/volute.ts
2534
+ // packages/daemon/src/lib/channels/volute.ts
2586
2535
  var volute_exports = {};
2587
2536
  __export(volute_exports, {
2588
2537
  createConversation: () => createConversation5,
@@ -2591,16 +2540,28 @@ __export(volute_exports, {
2591
2540
  read: () => read4,
2592
2541
  send: () => send4
2593
2542
  });
2594
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
2595
- import { resolve as resolve5 } from "path";
2543
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
2544
+ import { resolve as resolve4 } from "path";
2545
+ function readSessionFile(mindDir2) {
2546
+ try {
2547
+ const p = resolve4(mindDir2, ".mind", "current-session");
2548
+ if (existsSync4(p)) return readFileSync4(p, "utf-8").trim() || void 0;
2549
+ } catch (err) {
2550
+ const code = err.code;
2551
+ if (code !== "ENOENT") {
2552
+ console.error(`[volute] failed to read session file: ${code ?? err}`);
2553
+ }
2554
+ }
2555
+ return void 0;
2556
+ }
2596
2557
  function getDaemonConfig() {
2597
- const configPath = resolve5(voluteSystemDir(), "daemon.json");
2598
- if (!existsSync5(configPath)) {
2558
+ const configPath = resolve4(voluteSystemDir(), "daemon.json");
2559
+ if (!existsSync4(configPath)) {
2599
2560
  throw new Error("Volute daemon is not running");
2600
2561
  }
2601
2562
  let config2;
2602
2563
  try {
2603
- config2 = JSON.parse(readFileSync5(configPath, "utf-8"));
2564
+ config2 = JSON.parse(readFileSync4(configPath, "utf-8"));
2604
2565
  } catch (err) {
2605
2566
  throw new Error(`Failed to parse ${configPath}: ${err}`);
2606
2567
  }
@@ -2748,7 +2709,7 @@ async function createConversation5(env, participants, name) {
2748
2709
  return conv.id;
2749
2710
  }
2750
2711
 
2751
- // src/lib/channels.ts
2712
+ // packages/daemon/src/lib/channels.ts
2752
2713
  var CHANNELS = {
2753
2714
  volute: {
2754
2715
  name: "volute",
@@ -2782,7 +2743,7 @@ function resolveChannelId(slug) {
2782
2743
  return colonIdx !== -1 ? slug.slice(colonIdx + 1) : slug;
2783
2744
  }
2784
2745
 
2785
- // src/web/api/channels.ts
2746
+ // packages/daemon/src/web/api/channels.ts
2786
2747
  function buildEnv(name) {
2787
2748
  return { ...loadMergedEnv(name), VOLUTE_MIND: name, VOLUTE_MIND_DIR: mindDir(name) };
2788
2749
  }
@@ -2810,7 +2771,7 @@ var app5 = new Hono5().post("/:name/channels/create", requireSelf(), async (c) =
2810
2771
  });
2811
2772
  var channels_default = app5;
2812
2773
 
2813
- // src/web/api/config.ts
2774
+ // packages/daemon/src/web/api/config.ts
2814
2775
  import { Hono as Hono6 } from "hono";
2815
2776
  var config = new Hono6();
2816
2777
  config.get("/models", (c) => {
@@ -2838,7 +2799,7 @@ config.get("/status", (c) => {
2838
2799
  });
2839
2800
  var config_default = config;
2840
2801
 
2841
- // src/web/api/env.ts
2802
+ // packages/daemon/src/web/api/env.ts
2842
2803
  import { Hono as Hono7 } from "hono";
2843
2804
  var app6 = new Hono7().get("/:name/env", async (c) => {
2844
2805
  const name = c.req.param("name");
@@ -2912,22 +2873,60 @@ var sharedEnvApp = new Hono7().get("/", (c) => {
2912
2873
  });
2913
2874
  var env_default = app6;
2914
2875
 
2915
- // src/web/api/extensions.ts
2876
+ // packages/daemon/src/web/api/extensions.ts
2916
2877
  import { Hono as Hono8 } from "hono";
2917
2878
  var app7 = new Hono8().get("/", (c) => {
2918
2879
  return c.json(getLoadedExtensions());
2880
+ }).get("/all", (c) => {
2881
+ return c.json(getAllDiscoveredExtensions());
2882
+ }).put("/:id/enabled", async (c) => {
2883
+ const { id } = c.req.param();
2884
+ const body = await c.req.json().catch(() => null);
2885
+ if (!body || typeof body.enabled !== "boolean") {
2886
+ return c.json({ error: "enabled must be a boolean" }, 400);
2887
+ }
2888
+ try {
2889
+ setExtensionEnabled(id, body.enabled);
2890
+ } catch (err) {
2891
+ return c.json({ error: err.message }, 404);
2892
+ }
2893
+ return c.json({ ok: true, requiresRestart: true });
2894
+ }).post("/install", async (c) => {
2895
+ const body = await c.req.json();
2896
+ const pkg = body.package?.trim();
2897
+ if (!pkg) {
2898
+ return c.json({ error: "package is required" }, 400);
2899
+ }
2900
+ try {
2901
+ await installNpmExtension(pkg);
2902
+ return c.json({ ok: true, requiresRestart: true });
2903
+ } catch (err) {
2904
+ const message = err.message;
2905
+ const isValidation = message.includes("already installed") || message.includes("Invalid package");
2906
+ return c.json({ error: message }, isValidation ? 400 : 500);
2907
+ }
2908
+ }).delete("/uninstall/:package", async (c) => {
2909
+ const pkg = c.req.param("package");
2910
+ try {
2911
+ await uninstallNpmExtension(pkg);
2912
+ return c.json({ ok: true, requiresRestart: true });
2913
+ } catch (err) {
2914
+ const message = err.message;
2915
+ const isValidation = message.includes("not installed");
2916
+ return c.json({ error: message }, isValidation ? 400 : 500);
2917
+ }
2919
2918
  });
2920
2919
  var extensions_default = app7;
2921
2920
 
2922
- // src/web/api/file-sharing.ts
2923
- import { readFileSync as readFileSync6, statSync } from "fs";
2924
- import { resolve as resolve6 } from "path";
2921
+ // packages/daemon/src/web/api/file-sharing.ts
2922
+ import { readFileSync as readFileSync5, statSync } from "fs";
2923
+ import { resolve as resolve5 } from "path";
2925
2924
  import { Hono as Hono9 } from "hono";
2926
2925
  async function notifyMind(mindName, message) {
2927
2926
  const entry = await findMind(mindName);
2928
2927
  if (!entry) return;
2929
2928
  try {
2930
- const { sendSystemMessage } = await import("./system-chat-JAPOJ3KE.js");
2929
+ const { sendSystemMessage } = await import("./system-chat-IDPHYHY4.js");
2931
2930
  await sendSystemMessage(mindName, message);
2932
2931
  } catch (err) {
2933
2932
  logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
@@ -2946,21 +2945,21 @@ var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
2946
2945
  const pathErr = validateFilePath(body.filePath);
2947
2946
  if (pathErr) return c.json({ error: pathErr }, 400);
2948
2947
  const senderDir = mindDir(senderName);
2949
- const filePath = resolve6(senderDir, "home", body.filePath);
2948
+ const filePath = resolve5(senderDir, "home", body.filePath);
2950
2949
  const MAX_FILE_SIZE3 = 50 * 1024 * 1024;
2951
- const stat4 = statSync(filePath, { throwIfNoEntry: false });
2952
- if (!stat4) return c.json({ error: `File not found: ${body.filePath}` }, 404);
2953
- if (stat4.size > MAX_FILE_SIZE3) {
2950
+ const stat3 = statSync(filePath, { throwIfNoEntry: false });
2951
+ if (!stat3) return c.json({ error: `File not found: ${body.filePath}` }, 404);
2952
+ if (stat3.size > MAX_FILE_SIZE3) {
2954
2953
  return c.json(
2955
2954
  {
2956
- error: `File too large (${formatFileSize(stat4.size)}, max ${formatFileSize(MAX_FILE_SIZE3)})`
2955
+ error: `File too large (${formatFileSize(stat3.size)}, max ${formatFileSize(MAX_FILE_SIZE3)})`
2957
2956
  },
2958
2957
  413
2959
2958
  );
2960
2959
  }
2961
2960
  let content;
2962
2961
  try {
2963
- content = readFileSync6(filePath);
2962
+ content = readFileSync5(filePath);
2964
2963
  } catch (err) {
2965
2964
  const code = err.code;
2966
2965
  if (code === "ENOENT") {
@@ -3049,9 +3048,10 @@ var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
3049
3048
  });
3050
3049
  var file_sharing_default = app8;
3051
3050
 
3052
- // src/web/api/files.ts
3053
- import { readFile, realpath, stat } from "fs/promises";
3054
- import { extname as extname2, resolve as resolve7 } from "path";
3051
+ // packages/daemon/src/web/api/files.ts
3052
+ import { mkdirSync as mkdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
3053
+ import { readdir, readFile, realpath, stat } from "fs/promises";
3054
+ import { extname as extname2, resolve as resolve6 } from "path";
3055
3055
  import { Hono as Hono10 } from "hono";
3056
3056
  var AVATAR_MIME2 = {
3057
3057
  ".png": "image/png",
@@ -3061,18 +3061,71 @@ var AVATAR_MIME2 = {
3061
3061
  ".webp": "image/webp"
3062
3062
  };
3063
3063
  var MAX_AVATAR_SIZE2 = 2 * 1024 * 1024;
3064
- var app9 = new Hono10().get("/:name/avatar", async (c) => {
3064
+ var MAX_FILE_SIZE = 50 * 1024 * 1024;
3065
+ var MIME_TYPES = {
3066
+ ".html": "text/html",
3067
+ ".css": "text/css",
3068
+ ".js": "application/javascript",
3069
+ ".json": "application/json",
3070
+ ".svg": "image/svg+xml",
3071
+ ".png": "image/png",
3072
+ ".jpg": "image/jpeg",
3073
+ ".jpeg": "image/jpeg",
3074
+ ".gif": "image/gif",
3075
+ ".webp": "image/webp",
3076
+ ".txt": "text/plain",
3077
+ ".md": "text/markdown",
3078
+ ".xml": "application/xml",
3079
+ ".woff": "font/woff",
3080
+ ".woff2": "font/woff2"
3081
+ };
3082
+ var app9 = new Hono10().post("/:name/avatar", requireSelf(), async (c) => {
3065
3083
  const name = c.req.param("name");
3066
3084
  const entry = await findMind(name);
3067
3085
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3068
- const dir = mindDir(name);
3086
+ const body = await c.req.parseBody();
3087
+ const file = body.file;
3088
+ if (!(file instanceof File)) {
3089
+ return c.json({ error: "No file uploaded" }, 400);
3090
+ }
3091
+ if (file.size > MAX_AVATAR_SIZE2) {
3092
+ return c.json({ error: "File too large (max 2MB)" }, 400);
3093
+ }
3094
+ const ext = extname2(file.name).toLowerCase();
3095
+ if (!AVATAR_MIME2[ext]) {
3096
+ return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
3097
+ }
3098
+ const dir = entry.dir ?? mindDir(name);
3099
+ const homeDir = resolve6(dir, "home");
3100
+ const filename = `avatar${ext}`;
3101
+ const avatarPath = resolve6(homeDir, filename);
3102
+ const config2 = readVoluteConfig(dir) ?? {};
3103
+ const oldAvatar = config2.profile?.avatar;
3104
+ if (oldAvatar && oldAvatar !== filename) {
3105
+ rmSync2(resolve6(homeDir, oldAvatar), { force: true });
3106
+ }
3107
+ const buffer2 = Buffer.from(await file.arrayBuffer());
3108
+ mkdirSync3(homeDir, { recursive: true });
3109
+ writeFileSync3(avatarPath, buffer2);
3110
+ const profile = config2.profile ?? {};
3111
+ profile.avatar = filename;
3112
+ config2.profile = profile;
3113
+ writeVoluteConfig(dir, config2);
3114
+ await syncMindProfile(name, profile);
3115
+ broadcast({ type: "profile_updated", mind: name, summary: `${name} avatar updated` });
3116
+ return c.json({ ok: true, avatar: filename });
3117
+ }).get("/:name/avatar", async (c) => {
3118
+ const name = c.req.param("name");
3119
+ const entry = await findMind(name);
3120
+ if (!entry) return c.json({ error: "Mind not found" }, 404);
3121
+ const dir = entry.dir ?? mindDir(name);
3069
3122
  const config2 = readVoluteConfig(dir);
3070
3123
  if (!config2?.profile?.avatar) return c.json({ error: "No avatar configured" }, 404);
3071
3124
  const ext = extname2(config2.profile.avatar).toLowerCase();
3072
3125
  const mime = AVATAR_MIME2[ext];
3073
3126
  if (!mime) return c.json({ error: "Invalid avatar extension" }, 400);
3074
- const homeDir = resolve7(dir, "home");
3075
- const avatarPath = resolve7(homeDir, config2.profile.avatar);
3127
+ const homeDir = resolve6(dir, "home");
3128
+ const avatarPath = resolve6(homeDir, config2.profile.avatar);
3076
3129
  if (!avatarPath.startsWith(`${homeDir}/`)) return c.json({ error: "Invalid avatar path" }, 400);
3077
3130
  let realAvatarPath;
3078
3131
  try {
@@ -3096,29 +3149,89 @@ var app9 = new Hono10().get("/:name/avatar", async (c) => {
3096
3149
  } catch {
3097
3150
  return c.json({ error: "Failed to read avatar file" }, 500);
3098
3151
  }
3152
+ }).get("/:name/files/", requireAdmin, async (c) => {
3153
+ const name = c.req.param("name");
3154
+ const entry = await findMind(name);
3155
+ if (!entry) return c.json({ error: "Mind not found" }, 404);
3156
+ const homeDir = resolve6(entry.dir ?? mindDir(name), "home");
3157
+ let entries;
3158
+ try {
3159
+ entries = await readdir(homeDir, { withFileTypes: true });
3160
+ } catch (err) {
3161
+ if (err.code === "ENOENT")
3162
+ return c.json({ error: "Mind home directory not found" }, 404);
3163
+ return c.json({ error: "Failed to read directory" }, 500);
3164
+ }
3165
+ const items = entries.filter((e) => !e.name.startsWith(".")).map((e) => ({ name: e.name, type: e.isDirectory() ? "directory" : "file" }));
3166
+ return c.json(items);
3167
+ }).get("/:name/files/*", requireAdmin, async (c) => {
3168
+ const name = c.req.param("name");
3169
+ const entry = await findMind(name);
3170
+ if (!entry) return c.json({ error: "Mind not found" }, 404);
3171
+ const homeDir = resolve6(entry.dir ?? mindDir(name), "home");
3172
+ const wildcard = c.req.path.replace(new RegExp(`^.*/minds/${name}/files`), "") || "/";
3173
+ const relativePath = wildcard.slice(1);
3174
+ const requestedPath = resolve6(homeDir, relativePath);
3175
+ if (requestedPath !== homeDir && !requestedPath.startsWith(`${homeDir}/`))
3176
+ return c.text("Forbidden", 403);
3177
+ if (relativePath.split("/").some((seg) => seg.startsWith("."))) return c.text("Forbidden", 403);
3178
+ let resolvedPath;
3179
+ try {
3180
+ const realHome = await realpath(homeDir);
3181
+ resolvedPath = await realpath(requestedPath);
3182
+ if (resolvedPath !== realHome && !resolvedPath.startsWith(`${realHome}/`))
3183
+ return c.text("Forbidden", 403);
3184
+ } catch (err) {
3185
+ if (err.code === "ENOENT") return c.text("Not found", 404);
3186
+ return c.text("Internal server error", 500);
3187
+ }
3188
+ let fileStat;
3189
+ try {
3190
+ fileStat = await stat(resolvedPath);
3191
+ } catch (err) {
3192
+ if (err.code === "ENOENT") return c.text("Not found", 404);
3193
+ console.error(`[files] stat failed for ${resolvedPath}:`, err);
3194
+ return c.text("Internal server error", 500);
3195
+ }
3196
+ if (fileStat.isDirectory()) {
3197
+ if (!c.req.path.endsWith("/")) return c.redirect(`${c.req.path}/`);
3198
+ const dirEntries = await readdir(resolvedPath, { withFileTypes: true }).catch(
3199
+ () => []
3200
+ );
3201
+ const items = dirEntries.filter((e) => !e.name.startsWith(".")).map((e) => ({ name: e.name, type: e.isDirectory() ? "directory" : "file" }));
3202
+ return c.json(items);
3203
+ }
3204
+ if (fileStat.size > MAX_FILE_SIZE) return c.text("File too large", 413);
3205
+ const body = await readFile(resolvedPath);
3206
+ const mime = MIME_TYPES[extname2(resolvedPath).toLowerCase()] || "application/octet-stream";
3207
+ return c.body(body, 200, { "Content-Type": mime });
3099
3208
  });
3100
3209
  var files_default = app9;
3101
3210
 
3102
- // src/web/api/history.ts
3103
- import { and as and3, desc as desc2, eq as eq4, inArray, sql } from "drizzle-orm";
3211
+ // packages/daemon/src/web/api/history.ts
3212
+ import { and as and3, desc as desc2, eq as eq4, gte, inArray, sql } from "drizzle-orm";
3104
3213
  import { Hono as Hono11 } from "hono";
3105
3214
  var history = new Hono11().get("/turns", async (c) => {
3106
3215
  const mindFilter = c.req.query("mind");
3107
3216
  const turnIdFilter = c.req.query("turnId");
3217
+ const turnIdsFilter = c.req.query("turnIds");
3108
3218
  const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
3109
3219
  const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
3110
3220
  const db = await getDb();
3111
3221
  const conditions = [];
3112
3222
  if (mindFilter) conditions.push(eq4(turns.mind, mindFilter));
3113
3223
  if (turnIdFilter) conditions.push(eq4(turns.id, turnIdFilter));
3224
+ if (turnIdsFilter) {
3225
+ const ids = turnIdsFilter.split(",").filter(Boolean);
3226
+ if (ids.length > 0) conditions.push(inArray(turns.id, ids));
3227
+ }
3114
3228
  const turnRows = await db.select().from(turns).where(conditions.length > 0 ? and3(...conditions) : void 0).orderBy(desc2(turns.created_at)).limit(limit).offset(offset);
3115
3229
  if (turnRows.length === 0) return c.json([]);
3116
3230
  const turnIds = turnRows.map((t) => t.id);
3117
- const summaryRows = await db.select().from(mindHistory).where(and3(eq4(mindHistory.type, "summary"), inArray(mindHistory.turn_id, turnIds)));
3231
+ const summaryRows = await db.select().from(summaries).where(and3(eq4(summaries.period, "turn"), inArray(summaries.period_key, turnIds)));
3118
3232
  const summaryByTurn = /* @__PURE__ */ new Map();
3119
3233
  for (const s of summaryRows) {
3120
- if (s.turn_id)
3121
- summaryByTurn.set(s.turn_id, { content: s.content ?? "", metadata: s.metadata });
3234
+ summaryByTurn.set(s.period_key, { content: s.content, metadata: s.metadata });
3122
3235
  }
3123
3236
  const historyMsgRows = await db.select({
3124
3237
  id: mindHistory.id,
@@ -3191,13 +3304,92 @@ var history = new Hono11().get("/turns", async (c) => {
3191
3304
  }).from(mindHistory).where(inArray(mindHistory.id, triggerIds));
3192
3305
  for (const r of triggerRows) triggerMap.set(r.id, r);
3193
3306
  }
3307
+ const allChannelSlugs = /* @__PURE__ */ new Set();
3308
+ for (const [, channels] of msgsByTurnChannel) {
3309
+ for (const ch of channels.keys()) allChannelSlugs.add(ch);
3310
+ }
3311
+ const dmSlugMinds = /* @__PURE__ */ new Map();
3312
+ for (const [turnId, channels] of msgsByTurnChannel) {
3313
+ const mindName = turnMindMap.get(turnId);
3314
+ if (!mindName) continue;
3315
+ for (const ch of channels.keys()) {
3316
+ if (ch.startsWith("@")) {
3317
+ let minds = dmSlugMinds.get(ch);
3318
+ if (!minds) {
3319
+ minds = /* @__PURE__ */ new Set();
3320
+ dmSlugMinds.set(ch, minds);
3321
+ }
3322
+ minds.add(mindName);
3323
+ }
3324
+ }
3325
+ }
3326
+ const channelIdMap = /* @__PURE__ */ new Map();
3327
+ try {
3328
+ if (allChannelSlugs.size > 0) {
3329
+ const channelNames = [...allChannelSlugs].filter((s) => !s.startsWith("@")).map((s) => {
3330
+ let name = s;
3331
+ if (name.startsWith("#")) name = name.slice(1);
3332
+ const colonIdx = name.indexOf(":");
3333
+ if (colonIdx >= 0) name = name.substring(colonIdx + 1);
3334
+ return { slug: s, name };
3335
+ });
3336
+ if (channelNames.length > 0) {
3337
+ const channelRows = await db.select({ id: conversations.id, name: conversations.name }).from(conversations).where(
3338
+ and3(
3339
+ eq4(conversations.type, "channel"),
3340
+ inArray(
3341
+ conversations.name,
3342
+ channelNames.map((c2) => c2.name)
3343
+ )
3344
+ )
3345
+ );
3346
+ const nameToId = new Map(channelRows.map((r) => [r.name, r.id]));
3347
+ for (const { slug, name } of channelNames) {
3348
+ const id = nameToId.get(name);
3349
+ if (id) channelIdMap.set(slug, id);
3350
+ }
3351
+ }
3352
+ const dmSlugs = [...dmSlugMinds.keys()];
3353
+ if (dmSlugs.length > 0) {
3354
+ const targetNames = dmSlugs.map((s) => s.slice(1));
3355
+ const cp2 = db.$with("cp2").as(
3356
+ db.select({
3357
+ conversation_id: conversationParticipants.conversation_id,
3358
+ username: users.username
3359
+ }).from(conversationParticipants).innerJoin(users, eq4(conversationParticipants.user_id, users.id))
3360
+ );
3361
+ const dmRows = await db.with(cp2).select({
3362
+ id: conversations.id,
3363
+ targetUsername: users.username
3364
+ }).from(conversations).innerJoin(
3365
+ conversationParticipants,
3366
+ eq4(conversations.id, conversationParticipants.conversation_id)
3367
+ ).innerJoin(users, eq4(conversationParticipants.user_id, users.id)).where(and3(eq4(conversations.type, "dm"), inArray(users.username, targetNames)));
3368
+ for (const row of dmRows) {
3369
+ const slug = `@${row.targetUsername}`;
3370
+ const mindNames = dmSlugMinds.get(slug);
3371
+ if (mindNames && !channelIdMap.has(slug)) {
3372
+ const mindCheck = await db.select({ id: conversationParticipants.user_id }).from(conversationParticipants).innerJoin(users, eq4(conversationParticipants.user_id, users.id)).where(
3373
+ and3(
3374
+ eq4(conversationParticipants.conversation_id, row.id),
3375
+ inArray(users.username, [...mindNames])
3376
+ )
3377
+ ).get();
3378
+ if (mindCheck) channelIdMap.set(slug, row.id);
3379
+ }
3380
+ }
3381
+ }
3382
+ }
3383
+ } catch (err) {
3384
+ logger_default.warn("Failed to resolve channel slugs to conversation IDs", logger_default.errorData(err));
3385
+ }
3194
3386
  const result = turnRows.map((t) => {
3195
3387
  const summary = summaryByTurn.get(t.id);
3196
3388
  const turnChannels = msgsByTurnChannel.get(t.id) ?? /* @__PURE__ */ new Map();
3197
3389
  const convEntries = [...turnChannels.entries()].map(([channel, evts]) => {
3198
3390
  const { label, type } = getChannelLabel(channel);
3199
3391
  return {
3200
- id: channel,
3392
+ id: channelIdMap.get(channel) ?? channel,
3201
3393
  label,
3202
3394
  type,
3203
3395
  messages: evts.map((m) => ({
@@ -3305,25 +3497,78 @@ var history = new Hono11().get("/turns", async (c) => {
3305
3497
  Connection: "keep-alive"
3306
3498
  }
3307
3499
  });
3500
+ }).get("/summaries", async (c) => {
3501
+ const mind = c.req.query("mind") ?? "_system";
3502
+ const period = c.req.query("period");
3503
+ const ids = c.req.query("ids");
3504
+ const from = c.req.query("from");
3505
+ const to = c.req.query("to");
3506
+ const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
3507
+ if (ids) {
3508
+ const db2 = await getDb();
3509
+ const idList = ids.split(",").map((s) => parseInt(s, 10)).filter((n) => !isNaN(n));
3510
+ if (idList.length === 0) return c.json([]);
3511
+ const rows2 = await db2.select().from(summaries).where(inArray(summaries.id, idList));
3512
+ const result2 = rows2.map((r) => {
3513
+ let metadata = null;
3514
+ if (r.metadata) {
3515
+ try {
3516
+ metadata = JSON.parse(r.metadata);
3517
+ } catch (err) {
3518
+ logger_default.debug(`malformed summary metadata for id ${r.id}`, logger_default.errorData(err));
3519
+ }
3520
+ }
3521
+ return { ...r, metadata };
3522
+ });
3523
+ return c.json(result2);
3524
+ }
3525
+ if (!period || !["turn", "hour", "day", "week", "month"].includes(period)) {
3526
+ return c.json({ error: "period is required (turn, hour, day, week, month)" }, 400);
3527
+ }
3528
+ const db = await getDb();
3529
+ const conditions = [eq4(summaries.mind, mind), eq4(summaries.period, period)];
3530
+ if (from) conditions.push(gte(summaries.period_key, from));
3531
+ if (to) conditions.push(sql`${summaries.period_key} <= ${to}`);
3532
+ const rows = await db.select({
3533
+ id: summaries.id,
3534
+ mind: summaries.mind,
3535
+ period: summaries.period,
3536
+ period_key: summaries.period_key,
3537
+ content: summaries.content,
3538
+ metadata: summaries.metadata,
3539
+ created_at: summaries.created_at
3540
+ }).from(summaries).where(and3(...conditions)).orderBy(desc2(summaries.period_key)).limit(limit);
3541
+ const result = rows.map((r) => {
3542
+ let metadata = null;
3543
+ if (r.metadata) {
3544
+ try {
3545
+ metadata = JSON.parse(r.metadata);
3546
+ } catch (err) {
3547
+ logger_default.debug(`malformed meta_summary metadata for id ${r.id}`, logger_default.errorData(err));
3548
+ }
3549
+ }
3550
+ return { ...r, metadata };
3551
+ });
3552
+ return c.json(result);
3308
3553
  });
3309
3554
  var history_default = history;
3310
3555
 
3311
- // src/web/api/keys.ts
3556
+ // packages/daemon/src/web/api/keys.ts
3312
3557
  import { Hono as Hono12 } from "hono";
3313
3558
 
3314
- // src/lib/identity.ts
3559
+ // packages/daemon/src/lib/identity.ts
3315
3560
  import { createHash, generateKeyPairSync, sign, verify } from "crypto";
3316
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
3317
- import { resolve as resolve8 } from "path";
3561
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
3562
+ import { resolve as resolve7 } from "path";
3318
3563
  function generateIdentity(mindDir2) {
3319
- const identityDir = resolve8(mindDir2, ".mind/identity");
3564
+ const identityDir = resolve7(mindDir2, ".mind/identity");
3320
3565
  mkdirSync4(identityDir, { recursive: true });
3321
3566
  const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
3322
3567
  publicKeyEncoding: { type: "spki", format: "pem" },
3323
3568
  privateKeyEncoding: { type: "pkcs8", format: "pem" }
3324
3569
  });
3325
- const privatePath = resolve8(identityDir, "private.pem");
3326
- const publicPath = resolve8(identityDir, "public.pem");
3570
+ const privatePath = resolve7(identityDir, "private.pem");
3571
+ const publicPath = resolve7(identityDir, "public.pem");
3327
3572
  writeFileSync4(privatePath, privateKey, { mode: 384 });
3328
3573
  writeFileSync4(publicPath, publicKey, { mode: 420 });
3329
3574
  const config2 = readVoluteConfig(mindDir2) ?? {};
@@ -3338,9 +3583,9 @@ function getPublicKey(mindDir2) {
3338
3583
  const config2 = readVoluteConfig(mindDir2);
3339
3584
  const relPath = config2?.identity?.publicKey;
3340
3585
  if (!relPath) return null;
3341
- const fullPath = resolve8(mindDir2, relPath);
3342
- if (!existsSync6(fullPath)) return null;
3343
- return readFileSync7(fullPath, "utf-8");
3586
+ const fullPath = resolve7(mindDir2, relPath);
3587
+ if (!existsSync5(fullPath)) return null;
3588
+ return readFileSync6(fullPath, "utf-8");
3344
3589
  }
3345
3590
  function getFingerprint(publicKeyPem) {
3346
3591
  return createHash("sha256").update(publicKeyPem).digest("hex");
@@ -3368,7 +3613,7 @@ async function publishPublicKey(mindName, publicKeyPem) {
3368
3613
  }
3369
3614
  }
3370
3615
 
3371
- // src/web/api/keys.ts
3616
+ // packages/daemon/src/web/api/keys.ts
3372
3617
  var app10 = new Hono12().get("/:fingerprint", async (c) => {
3373
3618
  const fingerprint = c.req.param("fingerprint");
3374
3619
  for (const entry of await readRegistry()) {
@@ -3385,18 +3630,18 @@ var app10 = new Hono12().get("/:fingerprint", async (c) => {
3385
3630
  });
3386
3631
  var keys_default = app10;
3387
3632
 
3388
- // src/web/api/logs.ts
3633
+ // packages/daemon/src/web/api/logs.ts
3389
3634
  import { spawn as spawn2 } from "child_process";
3390
- import { existsSync as existsSync7 } from "fs";
3391
- import { resolve as resolve9 } from "path";
3635
+ import { existsSync as existsSync6 } from "fs";
3636
+ import { resolve as resolve8 } from "path";
3392
3637
  import { Hono as Hono13 } from "hono";
3393
3638
  import { streamSSE as streamSSE3 } from "hono/streaming";
3394
3639
  var app11 = new Hono13().get("/:name/logs", async (c) => {
3395
3640
  const name = c.req.param("name");
3396
3641
  const entry = await findMind(name);
3397
3642
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3398
- const logFile = resolve9(stateDir(name), "logs", "mind.log");
3399
- if (!existsSync7(logFile)) {
3643
+ const logFile = resolve8(stateDir(name), "logs", "mind.log");
3644
+ if (!existsSync6(logFile)) {
3400
3645
  return c.json({ error: "No log file found" }, 404);
3401
3646
  }
3402
3647
  return streamSSE3(c, async (stream) => {
@@ -3414,17 +3659,17 @@ var app11 = new Hono13().get("/:name/logs", async (c) => {
3414
3659
  stream.onAbort(() => {
3415
3660
  tail.kill();
3416
3661
  });
3417
- await new Promise((resolve20) => {
3418
- tail.on("exit", resolve20);
3419
- stream.onAbort(resolve20);
3662
+ await new Promise((resolve18) => {
3663
+ tail.on("exit", resolve18);
3664
+ stream.onAbort(resolve18);
3420
3665
  });
3421
3666
  });
3422
3667
  }).get("/:name/logs/tail", async (c) => {
3423
3668
  const name = c.req.param("name");
3424
3669
  const entry = await findMind(name);
3425
3670
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3426
- const logFile = resolve9(stateDir(name), "logs", "mind.log");
3427
- if (!existsSync7(logFile)) {
3671
+ const logFile = resolve8(stateDir(name), "logs", "mind.log");
3672
+ if (!existsSync6(logFile)) {
3428
3673
  return c.json({ error: "No log file found" }, 404);
3429
3674
  }
3430
3675
  const nParam = parseInt(c.req.query("n") ?? "50", 10);
@@ -3434,14 +3679,14 @@ var app11 = new Hono13().get("/:name/logs", async (c) => {
3434
3679
  tail.stdout.on("data", (data) => {
3435
3680
  output += data.toString();
3436
3681
  });
3437
- await new Promise((resolve20) => {
3438
- tail.on("exit", resolve20);
3682
+ await new Promise((resolve18) => {
3683
+ tail.on("exit", resolve18);
3439
3684
  });
3440
3685
  return c.text(output);
3441
3686
  });
3442
3687
  var logs_default = app11;
3443
3688
 
3444
- // src/web/api/mind-skills.ts
3689
+ // packages/daemon/src/web/api/mind-skills.ts
3445
3690
  import { zValidator as zValidator4 } from "@hono/zod-validator";
3446
3691
  import { Hono as Hono14 } from "hono";
3447
3692
  import { z as z4 } from "zod";
@@ -3449,7 +3694,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
3449
3694
  const name = c.req.param("name");
3450
3695
  const entry = await findMind(name);
3451
3696
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3452
- const dir = mindDir(name);
3697
+ const dir = entry.dir ?? mindDir(name);
3453
3698
  const skills = await listMindSkills(dir);
3454
3699
  return c.json(skills);
3455
3700
  }).post(
@@ -3461,7 +3706,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
3461
3706
  const entry = await findMind(name);
3462
3707
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3463
3708
  const { skillId } = c.req.valid("json");
3464
- const dir = mindDir(name);
3709
+ const dir = entry.dir ?? mindDir(name);
3465
3710
  try {
3466
3711
  const result = await installSkill(name, dir, skillId);
3467
3712
  return c.json({ ok: true, ...result });
@@ -3479,7 +3724,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
3479
3724
  const entry = await findMind(name);
3480
3725
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3481
3726
  const { skillId } = c.req.valid("json");
3482
- const dir = mindDir(name);
3727
+ const dir = entry.dir ?? mindDir(name);
3483
3728
  try {
3484
3729
  const result = await updateSkill(name, dir, skillId);
3485
3730
  return c.json(result);
@@ -3497,7 +3742,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
3497
3742
  const entry = await findMind(name);
3498
3743
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3499
3744
  const { skillId } = c.req.valid("json");
3500
- const dir = mindDir(name);
3745
+ const dir = entry.dir ?? mindDir(name);
3501
3746
  try {
3502
3747
  const skill = await publishSkill(name, dir, skillId);
3503
3748
  return c.json(skill);
@@ -3511,7 +3756,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
3511
3756
  const skillName = c.req.param("skill");
3512
3757
  const entry = await findMind(name);
3513
3758
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3514
- const dir = mindDir(name);
3759
+ const dir = entry.dir ?? mindDir(name);
3515
3760
  try {
3516
3761
  await uninstallSkill(name, dir, skillName);
3517
3762
  } catch (e) {
@@ -3522,36 +3767,36 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
3522
3767
  });
3523
3768
  var mind_skills_default = app12;
3524
3769
 
3525
- // src/web/api/minds.ts
3770
+ // packages/daemon/src/web/api/minds.ts
3526
3771
  import {
3527
3772
  cpSync,
3528
- existsSync as existsSync9,
3773
+ existsSync as existsSync8,
3529
3774
  mkdirSync as mkdirSync6,
3530
3775
  readdirSync as readdirSync2,
3531
- readFileSync as readFileSync10,
3776
+ readFileSync as readFileSync9,
3532
3777
  rmSync as rmSync4,
3533
3778
  writeFileSync as writeFileSync7
3534
3779
  } from "fs";
3535
- import { resolve as resolve12 } from "path";
3780
+ import { resolve as resolve11 } from "path";
3536
3781
  import { zValidator as zValidator5 } from "@hono/zod-validator";
3537
- import { and as and5, desc as desc4, eq as eq6, sql as sql3 } from "drizzle-orm";
3782
+ import { and as and4, desc as desc3, eq as eq5, sql as sql2 } from "drizzle-orm";
3538
3783
  import { Hono as Hono15 } from "hono";
3539
3784
  import { z as z5 } from "zod";
3540
3785
 
3541
- // src/lib/consolidate.ts
3542
- import { readdirSync, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
3543
- import { resolve as resolve10 } from "path";
3786
+ // packages/daemon/src/lib/consolidate.ts
3787
+ import { readdirSync, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
3788
+ import { resolve as resolve9 } from "path";
3544
3789
  async function consolidateMemory(mindDir2) {
3545
- const soulPath = resolve10(mindDir2, "home/SOUL.md");
3546
- const memoryPath = resolve10(mindDir2, "home/MEMORY.md");
3547
- const memoryDir = resolve10(mindDir2, "home/memory");
3548
- const soul = readFileSync8(soulPath, "utf-8");
3790
+ const soulPath = resolve9(mindDir2, "home/SOUL.md");
3791
+ const memoryPath = resolve9(mindDir2, "home/MEMORY.md");
3792
+ const memoryDir = resolve9(mindDir2, "home/memory");
3793
+ const soul = readFileSync7(soulPath, "utf-8");
3549
3794
  const logs = [];
3550
3795
  try {
3551
3796
  const files = readdirSync(memoryDir).filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort();
3552
3797
  for (const filename of files) {
3553
3798
  const date = filename.replace(".md", "");
3554
- const content2 = readFileSync8(resolve10(memoryDir, filename), "utf-8").trim();
3799
+ const content2 = readFileSync7(resolve9(memoryDir, filename), "utf-8").trim();
3555
3800
  if (content2) {
3556
3801
  logs.push(`### ${date}
3557
3802
 
@@ -3609,26 +3854,26 @@ ${content2}`);
3609
3854
  }
3610
3855
  }
3611
3856
 
3612
- // src/lib/convert-session.ts
3857
+ // packages/daemon/src/lib/convert-session.ts
3613
3858
  import { randomUUID } from "crypto";
3614
- import { mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
3859
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
3615
3860
  import { homedir } from "os";
3616
- import { resolve as resolve11 } from "path";
3861
+ import { resolve as resolve10 } from "path";
3617
3862
  function convertSession(opts) {
3618
- const lines = readFileSync9(opts.sessionPath, "utf-8").trim().split("\n");
3863
+ const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
3619
3864
  const sessionId = randomUUID();
3620
3865
  const idMap = /* @__PURE__ */ new Map();
3621
- const messages2 = [];
3866
+ const messages = [];
3622
3867
  for (const line of lines) {
3623
3868
  const event = JSON.parse(line);
3624
3869
  if (event.type === "message" && event.message) {
3625
- messages2.push(event);
3870
+ messages.push(event);
3626
3871
  }
3627
3872
  }
3628
3873
  const sdkEvents = [];
3629
3874
  let lastSdkUuid = null;
3630
- for (let i = 0; i < messages2.length; i++) {
3631
- const event = messages2[i];
3875
+ for (let i = 0; i < messages.length; i++) {
3876
+ const event = messages[i];
3632
3877
  const msg = event.message;
3633
3878
  if (msg.role === "user") {
3634
3879
  const uuid = randomUUID();
@@ -3689,8 +3934,8 @@ function convertSession(opts) {
3689
3934
  let lastToolResultId = event.id;
3690
3935
  let lastTimestamp = event.timestamp;
3691
3936
  let j = i;
3692
- while (j < messages2.length && messages2[j].message.role === "toolResult") {
3693
- const tr = messages2[j];
3937
+ while (j < messages.length && messages[j].message.role === "toolResult") {
3938
+ const tr = messages[j];
3694
3939
  const trMsg = tr.message;
3695
3940
  lastToolResultId = tr.id;
3696
3941
  lastTimestamp = tr.timestamp;
@@ -3729,9 +3974,9 @@ function convertSession(opts) {
3729
3974
  }
3730
3975
  }
3731
3976
  const projectId = opts.projectDir.replace(/\//g, "-");
3732
- const sdkDir = resolve11(homedir(), ".claude", "projects", projectId);
3977
+ const sdkDir = resolve10(homedir(), ".claude", "projects", projectId);
3733
3978
  mkdirSync5(sdkDir, { recursive: true });
3734
- const sdkPath = resolve11(sdkDir, `${sessionId}.jsonl`);
3979
+ const sdkPath = resolve10(sdkDir, `${sessionId}.jsonl`);
3735
3980
  writeFileSync6(sdkPath, `${sdkEvents.join("\n")}
3736
3981
  `);
3737
3982
  console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
@@ -3761,280 +4006,29 @@ function mapUsage(usage) {
3761
4006
  input_tokens: usage.input ?? usage.input_tokens ?? 0,
3762
4007
  output_tokens: usage.output ?? usage.output_tokens ?? 0,
3763
4008
  cache_read_input_tokens: usage.cacheRead ?? usage.cache_read_input_tokens ?? 0,
3764
- cache_creation_input_tokens: usage.cacheWrite ?? usage.cache_creation_input_tokens ?? 0
3765
- };
3766
- }
3767
- function convertAssistantContent(content) {
3768
- const result = [];
3769
- for (const block of content) {
3770
- if (block.type === "thinking") {
3771
- } else if (block.type === "toolCall") {
3772
- result.push({
3773
- type: "tool_use",
3774
- id: block.id,
3775
- name: block.name,
3776
- input: block.arguments ?? block.input ?? {},
3777
- caller: { type: "direct" }
3778
- });
3779
- } else {
3780
- result.push(block);
3781
- }
3782
- }
3783
- return result;
3784
- }
3785
-
3786
- // src/lib/daemon/turn-summarizer.ts
3787
- import { and as and4, desc as desc3, eq as eq5, gt, lt as lt3, sql as sql2 } from "drizzle-orm";
3788
-
3789
- // src/lib/format-tool.ts
3790
- function summarizeTool(name, input) {
3791
- if (input && typeof input === "object") {
3792
- const args = input;
3793
- const val = args.path ?? args.command ?? args.query ?? args.url;
3794
- if (typeof val === "string") {
3795
- const brief = val.length > 60 ? `${val.slice(0, 57)}...` : val;
3796
- return `[${name} ${brief}]`;
3797
- }
3798
- }
3799
- return `[${name}]`;
3800
- }
3801
-
3802
- // src/lib/daemon/turn-summarizer.ts
3803
- var sLog = logger_default.child("turn-summarizer");
3804
- async function gatherTurnEvents(mind, session, doneId) {
3805
- const db = await getDb();
3806
- const conditions = [
3807
- eq5(mindHistory.mind, mind),
3808
- eq5(mindHistory.type, "done"),
3809
- lt3(mindHistory.id, doneId)
3810
- ];
3811
- if (session) {
3812
- conditions.push(eq5(mindHistory.session, session));
3813
- }
3814
- const prevDone = await db.select({ id: mindHistory.id }).from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.id)).limit(1);
3815
- const prevDoneId = prevDone.length > 0 ? prevDone[0].id : 0;
3816
- const turnConditions = [
3817
- eq5(mindHistory.mind, mind),
3818
- gt(mindHistory.id, prevDoneId),
3819
- sql2`${mindHistory.id} <= ${doneId}`
3820
- ];
3821
- if (session) {
3822
- turnConditions.push(eq5(mindHistory.session, session));
3823
- }
3824
- const events = await db.select({
3825
- id: mindHistory.id,
3826
- type: mindHistory.type,
3827
- channel: mindHistory.channel,
3828
- session: mindHistory.session,
3829
- content: mindHistory.content,
3830
- metadata: mindHistory.metadata,
3831
- created_at: mindHistory.created_at
3832
- }).from(mindHistory).where(and4(...turnConditions)).orderBy(mindHistory.id);
3833
- return {
3834
- events,
3835
- fromId: events.length > 0 ? events[0].id : doneId,
3836
- toId: doneId
3837
- };
3838
- }
3839
- function buildDeterministicSummary(events) {
3840
- const channels = /* @__PURE__ */ new Set();
3841
- const tools = [];
3842
- let hasInbound = false;
3843
- let hasOutbound = false;
3844
- for (const ev of events) {
3845
- if (ev.type === "inbound") {
3846
- hasInbound = true;
3847
- if (ev.channel) channels.add(ev.channel);
3848
- }
3849
- if (ev.type === "outbound" || ev.type === "text") {
3850
- hasOutbound = true;
3851
- }
3852
- if (ev.type === "tool_use" && ev.metadata) {
3853
- try {
3854
- const meta = JSON.parse(ev.metadata);
3855
- if (meta.name) tools.push(meta.name);
3856
- } catch (err) {
3857
- sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
3858
- }
3859
- }
3860
- }
3861
- const parts = [];
3862
- if (hasInbound) {
3863
- const channelList = [...channels];
3864
- parts.push(
3865
- channelList.length > 0 ? `Received message on ${channelList.join(", ")}` : "Received message"
3866
- );
3867
- }
3868
- if (tools.length > 0) {
3869
- const unique = [...new Set(tools)];
3870
- parts.push(`Used ${unique.join(", ")}`);
3871
- }
3872
- if (hasOutbound) {
3873
- parts.push("Sent response");
3874
- }
3875
- return parts.length > 0 ? `${parts.join(". ")}.` : "Turn completed.";
3876
- }
3877
- function buildTranscript(events) {
3878
- const lines = [];
3879
- for (const ev of events) {
3880
- switch (ev.type) {
3881
- case "inbound":
3882
- lines.push(`[inbound${ev.channel ? ` ${ev.channel}` : ""}] ${ev.content ?? ""}`);
3883
- break;
3884
- case "outbound":
3885
- case "text":
3886
- lines.push(`[response] ${(ev.content ?? "").slice(0, 500)}`);
3887
- break;
3888
- case "tool_use": {
3889
- let toolInfo = "tool";
3890
- if (ev.metadata) {
3891
- try {
3892
- const meta = JSON.parse(ev.metadata);
3893
- toolInfo = summarizeTool(meta.name ?? "tool", meta.input ?? {});
3894
- } catch (err) {
3895
- sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
3896
- }
3897
- }
3898
- lines.push(toolInfo);
3899
- break;
3900
- }
3901
- case "tool_result": {
3902
- const content = ev.content ?? "";
3903
- let isError = false;
3904
- if (ev.metadata) {
3905
- try {
3906
- const meta = JSON.parse(ev.metadata);
3907
- isError = !!meta.is_error;
3908
- } catch (err) {
3909
- sLog.debug(
3910
- `failed to parse tool_result metadata for event ${ev.id}`,
3911
- logger_default.errorData(err)
3912
- );
3913
- }
3914
- }
3915
- lines.push(isError ? "[result error]" : `[result] ${content.slice(0, 200)}`);
3916
- break;
3917
- }
3918
- case "thinking":
3919
- lines.push(`[thinking] ${(ev.content ?? "").slice(0, 300)}`);
3920
- break;
3921
- }
3922
- }
3923
- return lines.join("\n");
3924
- }
3925
- async function gatherTurnEventsByTurnId(turnId) {
3926
- const db = await getDb();
3927
- const events = await db.select({
3928
- id: mindHistory.id,
3929
- type: mindHistory.type,
3930
- channel: mindHistory.channel,
3931
- session: mindHistory.session,
3932
- content: mindHistory.content,
3933
- metadata: mindHistory.metadata,
3934
- created_at: mindHistory.created_at
3935
- }).from(mindHistory).where(eq5(mindHistory.turn_id, turnId)).orderBy(mindHistory.id);
3936
- return {
3937
- events,
3938
- fromId: events.length > 0 ? events[0].id : 0,
3939
- toId: events.length > 0 ? events[events.length - 1].id : 0
4009
+ cache_creation_input_tokens: usage.cacheWrite ?? usage.cache_creation_input_tokens ?? 0
3940
4010
  };
3941
4011
  }
3942
- async function summarizeTurn(mind, session, channel, doneId, turnId) {
3943
- const { events, fromId, toId } = turnId ? await gatherTurnEventsByTurnId(turnId) : await gatherTurnEvents(mind, session, doneId);
3944
- if (events.length === 0) return;
3945
- const substantiveTypes = /* @__PURE__ */ new Set(["text", "outbound", "tool_use", "tool_result", "thinking"]);
3946
- const hasSubstantiveOutput = events.some((ev) => substantiveTypes.has(ev.type));
3947
- if (!hasSubstantiveOutput) {
3948
- sLog.info(
3949
- `skipping summary for interrupted turn ${turnId ?? "(no turn)"} (no substantive output)`
3950
- );
3951
- if (turnId) {
3952
- try {
3953
- const db2 = await getDb();
3954
- await db2.update(mindHistory).set({ turn_id: null }).where(and4(eq5(mindHistory.turn_id, turnId), eq5(mindHistory.type, "inbound")));
3955
- await db2.update(messages).set({ turn_id: null }).where(eq5(messages.turn_id, turnId));
3956
- } catch (err) {
3957
- sLog.error(`failed to un-tag events for interrupted turn ${turnId}`, logger_default.errorData(err));
3958
- }
3959
- }
3960
- return;
3961
- }
3962
- const tools = [];
3963
- for (const ev of events) {
3964
- if (ev.type === "tool_use" && ev.metadata) {
3965
- try {
3966
- const meta = JSON.parse(ev.metadata);
3967
- if (meta.name) tools.push(meta.name);
3968
- } catch (err) {
3969
- sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
3970
- }
3971
- }
3972
- }
3973
- const fromTime = events[0].created_at;
3974
- const toTime = events[events.length - 1].created_at;
3975
- let summaryText;
3976
- let deterministic;
3977
- const transcript = buildTranscript(events);
3978
- if (transcript.trim()) {
3979
- const summaryPrompt = await getPrompt("turn_summary");
3980
- const aiResult = await aiCompleteUtility(summaryPrompt, transcript);
3981
- if (aiResult) {
3982
- summaryText = aiResult;
3983
- deterministic = false;
4012
+ function convertAssistantContent(content) {
4013
+ const result = [];
4014
+ for (const block of content) {
4015
+ if (block.type === "thinking") {
4016
+ } else if (block.type === "toolCall") {
4017
+ result.push({
4018
+ type: "tool_use",
4019
+ id: block.id,
4020
+ name: block.name,
4021
+ input: block.arguments ?? block.input ?? {},
4022
+ caller: { type: "direct" }
4023
+ });
3984
4024
  } else {
3985
- summaryText = buildDeterministicSummary(events);
3986
- deterministic = true;
4025
+ result.push(block);
3987
4026
  }
3988
- } else {
3989
- summaryText = buildDeterministicSummary(events);
3990
- deterministic = true;
3991
- }
3992
- const metadata = {
3993
- deterministic,
3994
- tool_count: tools.length,
3995
- tools: [...new Set(tools)],
3996
- from_id: fromId,
3997
- to_id: toId,
3998
- from_time: fromTime,
3999
- to_time: toTime
4000
- };
4001
- const db = await getDb();
4002
- let summaryId;
4003
- try {
4004
- const result = await db.insert(mindHistory).values({
4005
- mind,
4006
- type: "summary",
4007
- session: session ?? null,
4008
- channel: channel ?? null,
4009
- content: summaryText,
4010
- metadata: JSON.stringify(metadata),
4011
- turn_id: turnId ?? null
4012
- }).returning({ id: mindHistory.id });
4013
- summaryId = result[0]?.id;
4014
- } catch (err) {
4015
- sLog.error(
4016
- `failed to persist summary for ${mind} (events ${fromId}-${toId})`,
4017
- logger_default.errorData(err)
4018
- );
4019
- return;
4020
- }
4021
- if (turnId && summaryId != null) {
4022
- setSummaryEventId(turnId, summaryId).catch((err) => {
4023
- sLog.error(`failed to link summary to turn ${turnId}`, logger_default.errorData(err));
4024
- });
4025
4027
  }
4026
- publish2(mind, {
4027
- mind,
4028
- type: "summary",
4029
- session,
4030
- channel,
4031
- content: summaryText,
4032
- metadata,
4033
- turnId
4034
- });
4028
+ return result;
4035
4029
  }
4036
4030
 
4037
- // src/lib/health.ts
4031
+ // packages/daemon/src/lib/health.ts
4038
4032
  async function checkHealth(port) {
4039
4033
  try {
4040
4034
  const res = await fetch(`http://127.0.0.1:${port}/health`, {
@@ -4048,8 +4042,8 @@ async function checkHealth(port) {
4048
4042
  }
4049
4043
  }
4050
4044
 
4051
- // src/lib/variant-cleanup.ts
4052
- import { existsSync as existsSync8, rmSync as rmSync3 } from "fs";
4045
+ // packages/daemon/src/lib/variant-cleanup.ts
4046
+ import { existsSync as existsSync7, rmSync as rmSync3 } from "fs";
4053
4047
  async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
4054
4048
  if (opts?.stop) {
4055
4049
  try {
@@ -4058,10 +4052,10 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
4058
4052
  logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
4059
4053
  }
4060
4054
  }
4061
- const { findMind: findMind2 } = await import("./registry-PJ4S5PHQ.js");
4055
+ const { findMind: findMind2 } = await import("./registry-GBSNW3HG.js");
4062
4056
  const variantEntry = await findMind2(variantName);
4063
4057
  const branchName = variantEntry?.branch ?? variantName;
4064
- if (existsSync8(variantPath)) {
4058
+ if (existsSync7(variantPath)) {
4065
4059
  try {
4066
4060
  await gitExec(["worktree", "remove", "--force", variantPath], { cwd: projectRoot });
4067
4061
  } catch {
@@ -4094,7 +4088,7 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
4094
4088
  }
4095
4089
  }
4096
4090
 
4097
- // src/lib/variants.ts
4091
+ // packages/daemon/src/lib/variants.ts
4098
4092
  var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
4099
4093
  function validateBranchName(branch) {
4100
4094
  if (!SAFE_BRANCH_RE.test(branch)) {
@@ -4106,13 +4100,13 @@ function validateBranchName(branch) {
4106
4100
  return null;
4107
4101
  }
4108
4102
 
4109
- // src/web/api/minds.ts
4103
+ // packages/daemon/src/web/api/minds.ts
4110
4104
  var SUBSTANTIVE_TYPES = /* @__PURE__ */ new Set(["thinking", "text", "tool_use", "tool_result", "outbound"]);
4111
4105
  async function getMindStatus(name, port) {
4112
4106
  const manager = getMindManager();
4113
4107
  let status = "stopped";
4114
4108
  try {
4115
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
4109
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
4116
4110
  if (getSleepManagerIfReady2()?.isSleeping(name)) {
4117
4111
  status = "sleeping";
4118
4112
  }
@@ -4158,7 +4152,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
4158
4152
  await gitExec(["commit", "-m", "initial commit"], opts);
4159
4153
  }
4160
4154
  async function updateTemplateBranch(projectRoot, template, mindName) {
4161
- const tempWorktree = resolve12(projectRoot, ".variants", "_template_update");
4155
+ const tempWorktree = resolve11(projectRoot, ".variants", "_template_update");
4162
4156
  let branchExists = false;
4163
4157
  try {
4164
4158
  await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
@@ -4169,7 +4163,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
4169
4163
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
4170
4164
  } catch {
4171
4165
  }
4172
- if (existsSync9(tempWorktree)) {
4166
+ if (existsSync8(tempWorktree)) {
4173
4167
  rmSync4(tempWorktree, { recursive: true, force: true });
4174
4168
  }
4175
4169
  const templatesRoot = findTemplatesRoot();
@@ -4190,15 +4184,15 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
4190
4184
  });
4191
4185
  }
4192
4186
  copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
4193
- const initDir = resolve12(tempWorktree, ".init");
4194
- if (existsSync9(initDir)) {
4187
+ const initDir = resolve11(tempWorktree, ".init");
4188
+ if (existsSync8(initDir)) {
4195
4189
  rmSync4(initDir, { recursive: true, force: true });
4196
4190
  }
4197
- const homeDir = resolve12(tempWorktree, "home");
4198
- if (existsSync9(homeDir)) {
4191
+ const homeDir = resolve11(tempWorktree, "home");
4192
+ if (existsSync8(homeDir)) {
4199
4193
  for (const entry of readdirSync2(homeDir)) {
4200
4194
  if (entry !== "VOLUTE.md") {
4201
- rmSync4(resolve12(homeDir, entry), { recursive: true, force: true });
4195
+ rmSync4(resolve11(homeDir, entry), { recursive: true, force: true });
4202
4196
  }
4203
4197
  }
4204
4198
  }
@@ -4213,7 +4207,7 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
4213
4207
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
4214
4208
  } catch {
4215
4209
  }
4216
- if (existsSync9(tempWorktree)) {
4210
+ if (existsSync8(tempWorktree)) {
4217
4211
  rmSync4(tempWorktree, { recursive: true, force: true });
4218
4212
  }
4219
4213
  rmSync4(composedDir, { recursive: true, force: true });
@@ -4239,7 +4233,7 @@ async function mergeTemplateBranch(worktreeDir) {
4239
4233
  async function npmInstallAsMind(cwd, mindName) {
4240
4234
  if (isIsolationEnabled()) {
4241
4235
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
4242
- await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve12(cwd, "home") } });
4236
+ await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve11(cwd, "home") } });
4243
4237
  } else {
4244
4238
  await exec("npm", ["install"], { cwd });
4245
4239
  }
@@ -4290,8 +4284,8 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
4290
4284
  return { ok: true };
4291
4285
  }
4292
4286
  async function importFromArchive(c, tempDir, nameOverride, manifest) {
4293
- const extractedMindDir = resolve12(tempDir, "mind");
4294
- if (!existsSync9(extractedMindDir)) {
4287
+ const extractedMindDir = resolve11(tempDir, "mind");
4288
+ if (!existsSync8(extractedMindDir)) {
4295
4289
  return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
4296
4290
  }
4297
4291
  if (!manifest?.includes || !manifest.name || !manifest.template) {
@@ -4309,7 +4303,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
4309
4303
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
4310
4304
  ensureVoluteHome();
4311
4305
  const dest = mindDir(name);
4312
- if (existsSync9(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4306
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4313
4307
  try {
4314
4308
  cpSync(extractedMindDir, dest, { recursive: true });
4315
4309
  if (!manifest.includes.identity) {
@@ -4317,9 +4311,9 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
4317
4311
  }
4318
4312
  const state = stateDir(name);
4319
4313
  mkdirSync6(state, { recursive: true });
4320
- const envJson = resolve12(tempDir, "state/env.json");
4321
- if (existsSync9(envJson)) {
4322
- cpSync(envJson, resolve12(state, "env.json"));
4314
+ const envJson = resolve11(tempDir, "state/env.json");
4315
+ if (existsSync8(envJson)) {
4316
+ cpSync(envJson, resolve11(state, "env.json"));
4323
4317
  }
4324
4318
  const port = await nextPort();
4325
4319
  await addMind(name, port, manifest.stage, manifest.template);
@@ -4328,30 +4322,30 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
4328
4322
  } catch (err) {
4329
4323
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
4330
4324
  }
4331
- const homeDir = resolve12(dest, "home");
4325
+ const homeDir = resolve11(dest, "home");
4332
4326
  ensureVoluteGroup();
4333
4327
  createMindUser(name, homeDir);
4334
4328
  chownMindDir(dest, name);
4335
4329
  await npmInstallAsMind(dest, name);
4336
4330
  await importHistoryFromArchive(name, tempDir);
4337
4331
  importSessionsFromArchive(dest, tempDir);
4338
- if (!existsSync9(resolve12(dest, ".git"))) {
4332
+ if (!existsSync8(resolve11(dest, ".git"))) {
4339
4333
  try {
4340
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve12(dest, "home") } : void 0;
4334
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
4341
4335
  await gitExec(["init"], { cwd: dest, mindName: name, env });
4342
4336
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
4343
4337
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
4344
4338
  await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
4345
4339
  } catch (err) {
4346
4340
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
4347
- rmSync4(resolve12(dest, ".git"), { recursive: true, force: true });
4341
+ rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
4348
4342
  }
4349
4343
  }
4350
4344
  chownMindDir(dest, name);
4351
4345
  rmSync4(tempDir, { recursive: true, force: true });
4352
4346
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
4353
4347
  } catch (err) {
4354
- if (existsSync9(dest)) rmSync4(dest, { recursive: true, force: true });
4348
+ if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
4355
4349
  try {
4356
4350
  await removeMind(name);
4357
4351
  } catch (cleanupErr) {
@@ -4368,7 +4362,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4368
4362
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
4369
4363
  ensureVoluteHome();
4370
4364
  const dest = mindDir(name);
4371
- if (existsSync9(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4365
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4372
4366
  const templatesRoot = findTemplatesRoot();
4373
4367
  const { composedDir, manifest: templateManifest } = composeTemplate(
4374
4368
  templatesRoot,
@@ -4377,36 +4371,36 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4377
4371
  try {
4378
4372
  copyTemplateToDir(composedDir, dest, name, templateManifest);
4379
4373
  applyInitFiles(dest);
4380
- const extractedHome = resolve12(extractedMindDir, "home");
4381
- if (existsSync9(extractedHome)) {
4382
- cpSync(extractedHome, resolve12(dest, "home"), { recursive: true });
4374
+ const extractedHome = resolve11(extractedMindDir, "home");
4375
+ if (existsSync8(extractedHome)) {
4376
+ cpSync(extractedHome, resolve11(dest, "home"), { recursive: true });
4383
4377
  }
4384
- const extractedMindInternal = resolve12(extractedMindDir, ".mind");
4385
- if (existsSync9(extractedMindInternal)) {
4386
- cpSync(extractedMindInternal, resolve12(dest, ".mind"), { recursive: true });
4378
+ const extractedMindInternal = resolve11(extractedMindDir, ".mind");
4379
+ if (existsSync8(extractedMindInternal)) {
4380
+ cpSync(extractedMindInternal, resolve11(dest, ".mind"), { recursive: true });
4387
4381
  }
4388
- const identityDir = resolve12(dest, ".mind/identity");
4382
+ const identityDir = resolve11(dest, ".mind/identity");
4389
4383
  let publicKeyPem;
4390
- if (!manifest.includes.identity || !existsSync9(resolve12(identityDir, "private.pem"))) {
4384
+ if (!manifest.includes.identity || !existsSync8(resolve11(identityDir, "private.pem"))) {
4391
4385
  ({ publicKeyPem } = generateIdentity(dest));
4392
4386
  } else {
4393
- publicKeyPem = readFileSync10(resolve12(identityDir, "public.pem"), "utf-8");
4387
+ publicKeyPem = readFileSync9(resolve11(identityDir, "public.pem"), "utf-8");
4394
4388
  }
4395
- const promptsPath = resolve12(dest, "home/.config/prompts.json");
4396
- if (!existsSync9(promptsPath)) {
4389
+ const promptsPath = resolve11(dest, "home/.config/prompts.json");
4390
+ if (!existsSync8(promptsPath)) {
4397
4391
  const mindPrompts = await getMindPromptDefaults();
4398
4392
  writeFileSync7(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
4399
4393
  `);
4400
4394
  }
4401
4395
  const state = stateDir(name);
4402
4396
  mkdirSync6(state, { recursive: true });
4403
- const envJson = resolve12(tempDir, "state/env.json");
4404
- if (existsSync9(envJson)) {
4405
- cpSync(envJson, resolve12(state, "env.json"));
4397
+ const envJson = resolve11(tempDir, "state/env.json");
4398
+ if (existsSync8(envJson)) {
4399
+ cpSync(envJson, resolve11(state, "env.json"));
4406
4400
  }
4407
4401
  const port = await nextPort();
4408
4402
  await addMind(name, port, manifest.stage, manifest.template);
4409
- const homeDir = resolve12(dest, "home");
4403
+ const homeDir = resolve11(dest, "home");
4410
4404
  ensureVoluteGroup();
4411
4405
  createMindUser(name, homeDir);
4412
4406
  chownMindDir(dest, name);
@@ -4419,14 +4413,9 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4419
4413
  await initTemplateBranch(dest, composedDir, templateManifest, name, env);
4420
4414
  } catch (err) {
4421
4415
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
4422
- rmSync4(resolve12(dest, ".git"), { recursive: true, force: true });
4416
+ rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
4423
4417
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
4424
4418
  }
4425
- try {
4426
- await addSharedWorktree(name, dest);
4427
- } catch (err) {
4428
- logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
4429
- }
4430
4419
  const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
4431
4420
  const skillWarnings = [];
4432
4421
  for (const skillId of skillSet) {
@@ -4454,7 +4443,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4454
4443
  ...skillWarnings.length > 0 && { skillWarnings }
4455
4444
  });
4456
4445
  } catch (err) {
4457
- if (existsSync9(dest)) rmSync4(dest, { recursive: true, force: true });
4446
+ if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
4458
4447
  try {
4459
4448
  await removeMind(name);
4460
4449
  } catch (cleanupErr) {
@@ -4467,11 +4456,11 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
4467
4456
  }
4468
4457
  }
4469
4458
  async function importHistoryFromArchive(name, tempDir) {
4470
- const historyJsonl = resolve12(tempDir, "history.jsonl");
4471
- if (!existsSync9(historyJsonl)) return;
4459
+ const historyJsonl = resolve11(tempDir, "history.jsonl");
4460
+ if (!existsSync8(historyJsonl)) return;
4472
4461
  try {
4473
4462
  const db = await getDb();
4474
- const lines = readFileSync10(historyJsonl, "utf-8").trim().split("\n");
4463
+ const lines = readFileSync9(historyJsonl, "utf-8").trim().split("\n");
4475
4464
  let imported = 0;
4476
4465
  let failed = 0;
4477
4466
  for (const line of lines) {
@@ -4507,13 +4496,13 @@ async function importHistoryFromArchive(name, tempDir) {
4507
4496
  }
4508
4497
  }
4509
4498
  function importSessionsFromArchive(dest, tempDir) {
4510
- const sessionsDir = resolve12(tempDir, "sessions");
4511
- if (!existsSync9(sessionsDir)) return;
4499
+ const sessionsDir = resolve11(tempDir, "sessions");
4500
+ if (!existsSync8(sessionsDir)) return;
4512
4501
  try {
4513
- const destSessions = resolve12(dest, ".mind/sessions");
4502
+ const destSessions = resolve11(dest, ".mind/sessions");
4514
4503
  mkdirSync6(destSessions, { recursive: true });
4515
4504
  for (const file of readdirSync2(sessionsDir)) {
4516
- cpSync(resolve12(sessionsDir, file), resolve12(destSessions, file));
4505
+ cpSync(resolve11(sessionsDir, file), resolve11(destSessions, file));
4517
4506
  }
4518
4507
  } catch (err) {
4519
4508
  logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
@@ -4548,7 +4537,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
4548
4537
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
4549
4538
  ensureVoluteHome();
4550
4539
  const dest = mindDir(name);
4551
- if (existsSync9(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4540
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4552
4541
  const templatesRoot = findTemplatesRoot();
4553
4542
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
4554
4543
  try {
@@ -4556,19 +4545,21 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
4556
4545
  applyInitFiles(dest);
4557
4546
  const { publicKeyPem } = generateIdentity(dest);
4558
4547
  {
4548
+ const { readGlobalConfig: readGlobal } = await import("./setup-RHJRFURI.js");
4549
+ const mindDefaults = readGlobal().mindDefaults;
4559
4550
  const config2 = readVoluteConfig(dest);
4560
4551
  if (!config2) throw new Error("Failed to read volute.json after identity generation");
4561
4552
  if (body.description) {
4562
4553
  config2.profile = { ...config2.profile, description: body.description };
4563
4554
  }
4564
4555
  if (!config2.sleep) {
4565
- config2.sleep = {
4556
+ config2.sleep = mindDefaults?.sleep ?? {
4566
4557
  enabled: true,
4567
4558
  schedule: { sleep: "0 0 * * *", wake: "0 8 * * *" }
4568
4559
  };
4569
4560
  }
4570
4561
  if (!config2.schedules || config2.schedules.length === 0) {
4571
- config2.schedules = [
4562
+ config2.schedules = mindDefaults?.schedules ?? [
4572
4563
  {
4573
4564
  id: "heartbeat",
4574
4565
  cron: "0 12,16,20 * * *",
@@ -4578,18 +4569,35 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
4578
4569
  }
4579
4570
  ];
4580
4571
  }
4572
+ const cog = mindDefaults?.cognition;
4573
+ if (cog) {
4574
+ if (cog.thinkingLevel != null && !config2.thinkingLevel)
4575
+ config2.thinkingLevel = cog.thinkingLevel;
4576
+ if (cog.maxThinkingTokens != null && config2.maxThinkingTokens == null)
4577
+ config2.maxThinkingTokens = cog.maxThinkingTokens;
4578
+ if (cog.tokenBudget != null && config2.tokenBudget == null)
4579
+ config2.tokenBudget = cog.tokenBudget;
4580
+ if (cog.tokenBudgetPeriodMinutes != null && config2.tokenBudgetPeriodMinutes == null)
4581
+ config2.tokenBudgetPeriodMinutes = cog.tokenBudgetPeriodMinutes;
4582
+ }
4581
4583
  writeVoluteConfig(dest, config2);
4582
- }
4583
- if (body.model) {
4584
- const configPath = resolve12(dest, "home/.config/config.json");
4585
- const existing = existsSync9(configPath) ? JSON.parse(readFileSync10(configPath, "utf-8")) : {};
4586
- existing.model = template === "pi" ? qualifyModelId(body.model) : unqualifyModelId(body.model);
4587
- writeFileSync7(configPath, `${JSON.stringify(existing, null, 2)}
4584
+ const modelId = body.model ?? cog?.model;
4585
+ const sdkConfigPath = resolve11(dest, "home/.config/config.json");
4586
+ if (modelId || cog?.compaction) {
4587
+ const existing = existsSync8(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
4588
+ if (modelId) {
4589
+ existing.model = template === "pi" ? qualifyModelId(modelId) : unqualifyModelId(modelId);
4590
+ }
4591
+ if (cog?.compaction && !existing.compaction) {
4592
+ existing.compaction = cog.compaction;
4593
+ }
4594
+ writeFileSync7(sdkConfigPath, `${JSON.stringify(existing, null, 2)}
4588
4595
  `);
4596
+ }
4589
4597
  }
4590
4598
  const mindPrompts = await getMindPromptDefaults();
4591
4599
  writeFileSync7(
4592
- resolve12(dest, "home/.config/prompts.json"),
4600
+ resolve11(dest, "home/.config/prompts.json"),
4593
4601
  `${JSON.stringify(mindPrompts, null, 2)}
4594
4602
  `
4595
4603
  );
@@ -4601,7 +4609,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
4601
4609
  } catch (err) {
4602
4610
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
4603
4611
  }
4604
- const homeDir = resolve12(dest, "home");
4612
+ const homeDir = resolve11(dest, "home");
4605
4613
  ensureVoluteGroup();
4606
4614
  createMindUser(name, homeDir);
4607
4615
  chownMindDir(dest, name);
@@ -4614,14 +4622,9 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
4614
4622
  await initTemplateBranch(dest, composedDir, manifest, name, env);
4615
4623
  } catch (err) {
4616
4624
  logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
4617
- rmSync4(resolve12(dest, ".git"), { recursive: true, force: true });
4625
+ rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
4618
4626
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
4619
4627
  }
4620
- try {
4621
- await addSharedWorktree(name, dest);
4622
- } catch (err) {
4623
- logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
4624
- }
4625
4628
  chownMindDir(dest, name);
4626
4629
  if (body.stage === "seed") {
4627
4630
  const descLine = body.description ? `
@@ -4629,11 +4632,11 @@ The human who planted you described you as: "${body.description}"
4629
4632
  ` : "";
4630
4633
  const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
4631
4634
  const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
4632
- writeFileSync7(resolve12(dest, "home/SOUL.md"), seedSoul);
4635
+ writeFileSync7(resolve11(dest, "home/SOUL.md"), seedSoul);
4633
4636
  }
4634
4637
  let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
4635
4638
  if (body.stage === "seed" && !body.skills) {
4636
- const { isImagegenEnabled } = await import("./setup-TISPCO22.js");
4639
+ const { isImagegenEnabled } = await import("./setup-RHJRFURI.js");
4637
4640
  if (isImagegenEnabled()) {
4638
4641
  skillSet = [...skillSet, "imagegen"];
4639
4642
  }
@@ -4651,7 +4654,7 @@ The human who planted you described you as: "${body.description}"
4651
4654
  try {
4652
4655
  const spiritEntry = await findMind("volute");
4653
4656
  if (spiritEntry) {
4654
- const { spiritDir } = await import("./spirit-VRONKFMF.js");
4657
+ const { spiritDir } = await import("./spirit-ZFRDXMG7.js");
4655
4658
  const sDir = spiritEntry.dir ?? spiritDir();
4656
4659
  const spiritConfig = readVoluteConfig(sDir) ?? {};
4657
4660
  const schedules = spiritConfig.schedules ?? [];
@@ -4666,7 +4669,7 @@ The human who planted you described you as: "${body.description}"
4666
4669
  });
4667
4670
  spiritConfig.schedules = schedules;
4668
4671
  writeVoluteConfig(sDir, spiritConfig);
4669
- const { getScheduler: getScheduler2 } = await import("./scheduler-ZZ7XGQG6.js");
4672
+ const { getScheduler: getScheduler2 } = await import("./scheduler-Y7O4CJXL.js");
4670
4673
  getScheduler2().loadSchedules("volute", sDir);
4671
4674
  }
4672
4675
  }
@@ -4677,11 +4680,11 @@ The human who planted you described you as: "${body.description}"
4677
4680
  if (body.stage !== "seed") {
4678
4681
  const customSoul = await getPromptIfCustom("default_soul");
4679
4682
  if (customSoul) {
4680
- writeFileSync7(resolve12(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
4683
+ writeFileSync7(resolve11(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
4681
4684
  }
4682
4685
  const customMemory = await getPromptIfCustom("default_memory");
4683
4686
  if (customMemory) {
4684
- writeFileSync7(resolve12(dest, "home/MEMORY.md"), customMemory);
4687
+ writeFileSync7(resolve11(dest, "home/MEMORY.md"), customMemory);
4685
4688
  }
4686
4689
  }
4687
4690
  publishPublicKey(name, publicKeyPem).catch(
@@ -4710,7 +4713,7 @@ The human who planted you described you as: "${body.description}"
4710
4713
  ...skillWarnings.length > 0 && { skillWarnings }
4711
4714
  });
4712
4715
  } catch (err) {
4713
- if (existsSync9(dest)) rmSync4(dest, { recursive: true, force: true });
4716
+ if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
4714
4717
  try {
4715
4718
  await removeMind(name);
4716
4719
  } catch {
@@ -4730,13 +4733,13 @@ The human who planted you described you as: "${body.description}"
4730
4733
  return importFromArchive(c, body.archivePath, body.name, body.manifest);
4731
4734
  }
4732
4735
  const wsDir = body.workspacePath;
4733
- if (!wsDir || !existsSync9(resolve12(wsDir, "SOUL.md")) || !existsSync9(resolve12(wsDir, "IDENTITY.md"))) {
4736
+ if (!wsDir || !existsSync8(resolve11(wsDir, "SOUL.md")) || !existsSync8(resolve11(wsDir, "IDENTITY.md"))) {
4734
4737
  return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
4735
4738
  }
4736
- const soul = readFileSync10(resolve12(wsDir, "SOUL.md"), "utf-8");
4737
- const identity = readFileSync10(resolve12(wsDir, "IDENTITY.md"), "utf-8");
4738
- const userPath = resolve12(wsDir, "USER.md");
4739
- const user = existsSync9(userPath) ? readFileSync10(userPath, "utf-8") : "";
4739
+ const soul = readFileSync9(resolve11(wsDir, "SOUL.md"), "utf-8");
4740
+ const identity = readFileSync9(resolve11(wsDir, "IDENTITY.md"), "utf-8");
4741
+ const userPath = resolve11(wsDir, "USER.md");
4742
+ const user = existsSync8(userPath) ? readFileSync9(userPath, "utf-8") : "";
4740
4743
  const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
4741
4744
  const template = body.template ?? "claude";
4742
4745
  const nameErr = validateMindName(name);
@@ -4756,33 +4759,33 @@ ${user.trimEnd()}
4756
4759
  ` : "";
4757
4760
  ensureVoluteHome();
4758
4761
  const dest = mindDir(name);
4759
- if (existsSync9(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4762
+ if (existsSync8(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4760
4763
  const templatesRoot = findTemplatesRoot();
4761
4764
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
4762
4765
  try {
4763
4766
  copyTemplateToDir(composedDir, dest, name, manifest);
4764
4767
  applyInitFiles(dest);
4765
4768
  const { publicKeyPem: importPublicKey } = generateIdentity(dest);
4766
- writeFileSync7(resolve12(dest, "home/SOUL.md"), mergedSoul);
4767
- const wsMemoryPath = resolve12(wsDir, "MEMORY.md");
4768
- const hasMemory = existsSync9(wsMemoryPath);
4769
+ writeFileSync7(resolve11(dest, "home/SOUL.md"), mergedSoul);
4770
+ const wsMemoryPath = resolve11(wsDir, "MEMORY.md");
4771
+ const hasMemory = existsSync8(wsMemoryPath);
4769
4772
  if (hasMemory) {
4770
- const existingMemory = readFileSync10(wsMemoryPath, "utf-8");
4773
+ const existingMemory = readFileSync9(wsMemoryPath, "utf-8");
4771
4774
  writeFileSync7(
4772
- resolve12(dest, "home/MEMORY.md"),
4775
+ resolve11(dest, "home/MEMORY.md"),
4773
4776
  `${existingMemory.trimEnd()}${mergedMemoryExtra}`
4774
4777
  );
4775
4778
  } else if (user) {
4776
- writeFileSync7(resolve12(dest, "home/MEMORY.md"), `${user.trimEnd()}
4779
+ writeFileSync7(resolve11(dest, "home/MEMORY.md"), `${user.trimEnd()}
4777
4780
  `);
4778
4781
  }
4779
- const wsMemoryDir = resolve12(wsDir, "memory");
4782
+ const wsMemoryDir = resolve11(wsDir, "memory");
4780
4783
  let dailyLogCount = 0;
4781
- if (existsSync9(wsMemoryDir)) {
4782
- const destMemoryDir = resolve12(dest, "home/memory");
4784
+ if (existsSync8(wsMemoryDir)) {
4785
+ const destMemoryDir = resolve11(dest, "home/memory");
4783
4786
  const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
4784
4787
  for (const file of files) {
4785
- cpSync(resolve12(wsMemoryDir, file), resolve12(destMemoryDir, file));
4788
+ cpSync(resolve11(wsMemoryDir, file), resolve11(destMemoryDir, file));
4786
4789
  }
4787
4790
  dailyLogCount = files.length;
4788
4791
  }
@@ -4793,7 +4796,7 @@ ${user.trimEnd()}
4793
4796
  } catch (err) {
4794
4797
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
4795
4798
  }
4796
- const homeDir = resolve12(dest, "home");
4799
+ const homeDir = resolve11(dest, "home");
4797
4800
  ensureVoluteGroup();
4798
4801
  createMindUser(name, homeDir);
4799
4802
  chownMindDir(dest, name);
@@ -4801,35 +4804,30 @@ ${user.trimEnd()}
4801
4804
  if (!hasMemory && dailyLogCount > 0) {
4802
4805
  await consolidateMemory(dest);
4803
4806
  }
4804
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve12(dest, "home") } : void 0;
4807
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dest, "home") } : void 0;
4805
4808
  await gitExec(["init"], { cwd: dest, mindName: name, env });
4806
4809
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
4807
4810
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
4808
4811
  await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
4809
- const sessionFile = body.sessionPath ? resolve12(body.sessionPath) : findOpenClawSession(wsDir);
4810
- if (sessionFile && existsSync9(sessionFile)) {
4812
+ const sessionFile = body.sessionPath ? resolve11(body.sessionPath) : findOpenClawSession(wsDir);
4813
+ if (sessionFile && existsSync8(sessionFile)) {
4811
4814
  if (template === "pi") {
4812
4815
  importPiSession(sessionFile, dest);
4813
4816
  } else if (template === "claude") {
4814
4817
  const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
4815
- const mindRuntimeDir = resolve12(dest, ".mind");
4818
+ const mindRuntimeDir = resolve11(dest, ".mind");
4816
4819
  mkdirSync6(mindRuntimeDir, { recursive: true });
4817
- writeFileSync7(resolve12(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
4820
+ writeFileSync7(resolve11(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
4818
4821
  }
4819
4822
  }
4820
4823
  importOpenClawConnectors(name, dest);
4821
- try {
4822
- await addSharedWorktree(name, dest);
4823
- } catch (err) {
4824
- logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
4825
- }
4826
4824
  chownMindDir(dest, name);
4827
4825
  publishPublicKey(name, importPublicKey).catch(
4828
4826
  (err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
4829
4827
  );
4830
4828
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
4831
4829
  } catch (err) {
4832
- if (existsSync9(dest)) rmSync4(dest, { recursive: true, force: true });
4830
+ if (existsSync8(dest)) rmSync4(dest, { recursive: true, force: true });
4833
4831
  try {
4834
4832
  await removeMind(name);
4835
4833
  } catch {
@@ -4845,7 +4843,7 @@ ${user.trimEnd()}
4845
4843
  const db = await getDb();
4846
4844
  const lastActiveRows = await db.select({
4847
4845
  mind: mindHistory.mind,
4848
- lastActiveAt: sql3`MAX(${mindHistory.created_at})`
4846
+ lastActiveAt: sql2`MAX(${mindHistory.created_at})`
4849
4847
  }).from(mindHistory).groupBy(mindHistory.mind);
4850
4848
  lastActiveMap = new Map(lastActiveRows.map((r) => [r.mind, r.lastActiveAt]));
4851
4849
  } catch {
@@ -4853,7 +4851,7 @@ ${user.trimEnd()}
4853
4851
  const minds = await Promise.all(
4854
4852
  entries.map(async (entry) => {
4855
4853
  const mindStatus = await getMindStatus(entry.name, entry.port);
4856
- const hasPages = existsSync9(resolve12(mindDir(entry.name), "home", "public", "pages"));
4854
+ const hasPages = existsSync8(resolve11(mindDir(entry.name), "home", "pages"));
4857
4855
  return {
4858
4856
  ...entry,
4859
4857
  ...mindStatus,
@@ -4868,7 +4866,7 @@ ${user.trimEnd()}
4868
4866
  const entry = await findMind(name);
4869
4867
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4870
4868
  const dir = entry.dir ?? mindDir(entry.parent ?? name);
4871
- if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
4869
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
4872
4870
  const mindStatus = await getMindStatus(name, entry.port);
4873
4871
  const variants = await findVariants(name);
4874
4872
  const manager = getMindManager();
@@ -4882,8 +4880,34 @@ ${user.trimEnd()}
4882
4880
  return { name: s.name, port: s.port, status: variantStatus };
4883
4881
  })
4884
4882
  );
4885
- const hasPages = existsSync9(resolve12(mindDir(name), "home", "public", "pages"));
4883
+ const hasPages = existsSync8(resolve11(mindDir(name), "home", "pages"));
4886
4884
  return c.json({ ...entry, ...mindStatus, variants: variantStatuses, hasPages });
4885
+ }).get("/:name/context", async (c) => {
4886
+ const name = c.req.param("name");
4887
+ const entry = await findMind(name);
4888
+ if (!entry) return c.json({ error: "Mind not found" }, 404);
4889
+ if (!getMindManager().isRunning(name)) {
4890
+ return c.json({ error: "Mind is not running" }, 503);
4891
+ }
4892
+ try {
4893
+ const res = await fetch(`http://127.0.0.1:${entry.port}/context`, {
4894
+ signal: AbortSignal.timeout(3e3)
4895
+ });
4896
+ if (!res.ok) {
4897
+ const status = res.status >= 500 ? 502 : 404;
4898
+ return c.json(
4899
+ {
4900
+ error: res.status >= 500 ? "Mind context handler errored" : "Context endpoint not available"
4901
+ },
4902
+ status
4903
+ );
4904
+ }
4905
+ const data = await res.json();
4906
+ return c.json(data);
4907
+ } catch (err) {
4908
+ console.error(`context proxy for ${name}:`, err);
4909
+ return c.json({ error: "Failed to reach mind" }, 503);
4910
+ }
4887
4911
  }).post("/:name/start", requireSelf(), async (c) => {
4888
4912
  const name = c.req.param("name");
4889
4913
  const entry = await findMind(name);
@@ -4893,7 +4917,7 @@ ${user.trimEnd()}
4893
4917
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
4894
4918
  } else {
4895
4919
  const dir = mindDir(name);
4896
- if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
4920
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
4897
4921
  }
4898
4922
  if (getMindManager().isRunning(name)) {
4899
4923
  return c.json({ error: "Mind already running" }, 409);
@@ -4914,7 +4938,7 @@ ${user.trimEnd()}
4914
4938
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
4915
4939
  } else {
4916
4940
  const dir = mindDir(name);
4917
- if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
4941
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
4918
4942
  }
4919
4943
  let context;
4920
4944
  const contentType = c.req.header("content-type");
@@ -4929,7 +4953,7 @@ ${user.trimEnd()}
4929
4953
  const manager = getMindManager();
4930
4954
  try {
4931
4955
  if (context?.type === "reload") {
4932
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
4956
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
4933
4957
  const sleepState = getSleepManagerIfReady2()?.getState(name);
4934
4958
  if (sleepState?.sleeping) {
4935
4959
  logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
@@ -4949,7 +4973,7 @@ ${user.trimEnd()}
4949
4973
  const variantEntry = await findMind(mergeVariantName);
4950
4974
  if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
4951
4975
  const projectRoot = mindDir(baseName);
4952
- if (existsSync9(variantEntry.dir)) {
4976
+ if (existsSync8(variantEntry.dir)) {
4953
4977
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
4954
4978
  if (status) {
4955
4979
  try {
@@ -4991,7 +5015,7 @@ ${user.trimEnd()}
4991
5015
  if (context?.type === "sprouted" && !entry.parent) {
4992
5016
  try {
4993
5017
  const db = await getDb();
4994
- const activeConvs = await db.select({ id: conversations.id, channel: conversations.channel }).from(conversations).where(eq6(conversations.mind_name, baseName)).all();
5018
+ const activeConvs = await db.select({ id: conversations.id, channel: conversations.channel }).from(conversations).where(eq5(conversations.mind_name, baseName)).all();
4995
5019
  for (const conv of activeConvs) {
4996
5020
  await recordInbound(baseName, conv.channel, "system", "[seed has sprouted]");
4997
5021
  await addMessage(conv.id, "assistant", "system", [
@@ -5025,7 +5049,7 @@ ${user.trimEnd()}
5025
5049
  const name = c.req.param("name");
5026
5050
  const entry = await findMind(name);
5027
5051
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5028
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
5052
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
5029
5053
  const sm = getSleepManagerIfReady2();
5030
5054
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
5031
5055
  return c.json(sm.getState(name));
@@ -5033,7 +5057,7 @@ ${user.trimEnd()}
5033
5057
  const name = c.req.param("name");
5034
5058
  const entry = await findMind(name);
5035
5059
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5036
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
5060
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
5037
5061
  const sm = getSleepManagerIfReady2();
5038
5062
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
5039
5063
  if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
@@ -5053,7 +5077,7 @@ ${user.trimEnd()}
5053
5077
  const name = c.req.param("name");
5054
5078
  const entry = await findMind(name);
5055
5079
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5056
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
5080
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
5057
5081
  const sm = getSleepManagerIfReady2();
5058
5082
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
5059
5083
  const sleepState = sm.getState(name);
@@ -5068,7 +5092,7 @@ ${user.trimEnd()}
5068
5092
  const name = c.req.param("name");
5069
5093
  const entry = await findMind(name);
5070
5094
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5071
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
5095
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
5072
5096
  const sm = getSleepManagerIfReady2();
5073
5097
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
5074
5098
  const flushed = await sm.flushQueuedMessages(name);
@@ -5086,8 +5110,8 @@ ${user.trimEnd()}
5086
5110
  if (body.avatar !== void 0) profile.avatar = body.avatar;
5087
5111
  config2.profile = profile;
5088
5112
  writeVoluteConfig(dir, config2);
5089
- const { syncMindProfile } = await import("./auth-GKCDSO4T.js");
5090
- await syncMindProfile(name, profile);
5113
+ const { syncMindProfile: syncMindProfile2 } = await import("./auth-WX4TESEI.js");
5114
+ await syncMindProfile2(name, profile);
5091
5115
  broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
5092
5116
  return c.json({ ok: true });
5093
5117
  }).get("/:name/seed-check", requireSelf(), async (c) => {
@@ -5102,21 +5126,21 @@ ${user.trimEnd()}
5102
5126
  const rawSpirit = Number(process.env.VOLUTE_NURTURE_SPIRIT_MINUTES);
5103
5127
  const spiritThreshold = Number.isNaN(rawSpirit) ? 15 : rawSpirit;
5104
5128
  const lastCreatorMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
5105
- and5(
5106
- eq6(mindHistory.mind, name),
5107
- eq6(mindHistory.type, "inbound"),
5108
- sql3`${mindHistory.sender} != 'volute'`,
5109
- sql3`${mindHistory.sender} != ${name}`,
5110
- sql3`${mindHistory.sender} IS NOT NULL`
5129
+ and4(
5130
+ eq5(mindHistory.mind, name),
5131
+ eq5(mindHistory.type, "inbound"),
5132
+ sql2`${mindHistory.sender} != 'volute'`,
5133
+ sql2`${mindHistory.sender} != ${name}`,
5134
+ sql2`${mindHistory.sender} IS NOT NULL`
5111
5135
  )
5112
- ).orderBy(desc4(mindHistory.created_at)).limit(1);
5136
+ ).orderBy(desc3(mindHistory.created_at)).limit(1);
5113
5137
  const lastSpiritMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
5114
- and5(
5115
- eq6(mindHistory.mind, name),
5116
- eq6(mindHistory.type, "inbound"),
5117
- eq6(mindHistory.sender, "volute")
5138
+ and4(
5139
+ eq5(mindHistory.mind, name),
5140
+ eq5(mindHistory.type, "inbound"),
5141
+ eq5(mindHistory.sender, "volute")
5118
5142
  )
5119
- ).orderBy(desc4(mindHistory.created_at)).limit(1);
5143
+ ).orderBy(desc3(mindHistory.created_at)).limit(1);
5120
5144
  const now = Date.now();
5121
5145
  const creatorTime = lastCreatorMsg[0] ? new Date(lastCreatorMsg[0].created_at).getTime() : 0;
5122
5146
  const spiritTime = lastSpiritMsg[0] ? new Date(lastSpiritMsg[0].created_at).getTime() : 0;
@@ -5126,14 +5150,14 @@ ${user.trimEnd()}
5126
5150
  return c.json({ output: "" });
5127
5151
  }
5128
5152
  const dir = entry.dir ?? mindDir(name);
5129
- const soulPath = resolve12(dir, "home/SOUL.md");
5130
- const memoryPath = resolve12(dir, "home/MEMORY.md");
5131
- const soulCustom = existsSync9(soulPath) && !readFileSync10(soulPath, "utf-8").includes(ORIENTATION_MARKER);
5132
- const memoryWritten = existsSync9(memoryPath) && readFileSync10(memoryPath, "utf-8").trim().length > 0;
5153
+ const soulPath = resolve11(dir, "home/SOUL.md");
5154
+ const memoryPath = resolve11(dir, "home/MEMORY.md");
5155
+ const soulCustom = existsSync8(soulPath) && !readFileSync9(soulPath, "utf-8").includes(ORIENTATION_MARKER);
5156
+ const memoryWritten = existsSync8(memoryPath) && readFileSync9(memoryPath, "utf-8").trim().length > 0;
5133
5157
  const config2 = readVoluteConfig(dir);
5134
5158
  const displayNameSet = !!config2?.profile?.displayName;
5135
5159
  const avatarSet = !!config2?.profile?.avatar;
5136
- const { isImagegenEnabled } = await import("./setup-TISPCO22.js");
5160
+ const { isImagegenEnabled } = await import("./setup-RHJRFURI.js");
5137
5161
  const imagegenEnabled = isImagegenEnabled();
5138
5162
  const done = [];
5139
5163
  const remaining = [];
@@ -5171,7 +5195,7 @@ ${user.trimEnd()}
5171
5195
  try {
5172
5196
  const spiritEntry = await findMind("volute");
5173
5197
  if (spiritEntry) {
5174
- const { spiritDir } = await import("./spirit-VRONKFMF.js");
5198
+ const { spiritDir } = await import("./spirit-ZFRDXMG7.js");
5175
5199
  const sDir = spiritEntry.dir ?? spiritDir();
5176
5200
  const spiritConfig = readVoluteConfig(sDir);
5177
5201
  if (spiritConfig?.schedules) {
@@ -5179,7 +5203,7 @@ ${user.trimEnd()}
5179
5203
  spiritConfig.schedules = spiritConfig.schedules.filter((s) => s.id !== nurtureId);
5180
5204
  if (spiritConfig.schedules.length === 0) spiritConfig.schedules = void 0;
5181
5205
  writeVoluteConfig(sDir, spiritConfig);
5182
- const { getScheduler: getScheduler2 } = await import("./scheduler-ZZ7XGQG6.js");
5206
+ const { getScheduler: getScheduler2 } = await import("./scheduler-Y7O4CJXL.js");
5183
5207
  getScheduler2().loadSchedules("volute", sDir);
5184
5208
  }
5185
5209
  }
@@ -5203,18 +5227,13 @@ ${user.trimEnd()}
5203
5227
  await cleanupVariant(s.name, dir, s.dir, { stop: true });
5204
5228
  }
5205
5229
  }
5206
- try {
5207
- await removeSharedWorktree(name, dir);
5208
- } catch (err) {
5209
- logger_default.warn(`failed to clean up shared worktree for ${name}`, logger_default.errorData(err));
5210
- }
5211
5230
  await removeMind(name);
5212
5231
  await deleteMindUser2(name);
5213
5232
  const state = stateDir(name);
5214
- if (existsSync9(state)) {
5233
+ if (existsSync8(state)) {
5215
5234
  rmSync4(state, { recursive: true, force: true });
5216
5235
  }
5217
- if (force && existsSync9(dir)) {
5236
+ if (force && existsSync8(dir)) {
5218
5237
  rmSync4(dir, { recursive: true, force: true });
5219
5238
  deleteMindUser(name);
5220
5239
  }
@@ -5229,7 +5248,7 @@ ${user.trimEnd()}
5229
5248
  const entry = await findMind(mindName);
5230
5249
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5231
5250
  const dir = mindDir(mindName);
5232
- if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
5251
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
5233
5252
  let body = {};
5234
5253
  try {
5235
5254
  body = await c.req.json();
@@ -5238,16 +5257,16 @@ ${user.trimEnd()}
5238
5257
  const template = body.template ?? entry.template ?? "claude";
5239
5258
  const UPGRADE_BRANCH = "upgrade";
5240
5259
  const upgradeVariantName = `${mindName}-upgrade`;
5241
- const worktreeDir = resolve12(dir, ".variants", UPGRADE_BRANCH);
5260
+ const worktreeDir = resolve11(dir, ".variants", UPGRADE_BRANCH);
5242
5261
  if (body.abort) {
5243
- if (!existsSync9(worktreeDir)) {
5262
+ if (!existsSync8(worktreeDir)) {
5244
5263
  return c.json({ error: "No upgrade in progress" }, 400);
5245
5264
  }
5246
5265
  try {
5247
5266
  try {
5248
- const gitDirContent = readFileSync10(resolve12(worktreeDir, ".git"), "utf-8").trim();
5267
+ const gitDirContent = readFileSync9(resolve11(worktreeDir, ".git"), "utf-8").trim();
5249
5268
  const gitDir = gitDirContent.replace("gitdir: ", "");
5250
- if (existsSync9(resolve12(gitDir, "MERGE_HEAD"))) {
5269
+ if (existsSync8(resolve11(gitDir, "MERGE_HEAD"))) {
5251
5270
  await gitExec(["merge", "--abort"], { cwd: worktreeDir });
5252
5271
  }
5253
5272
  } catch {
@@ -5266,7 +5285,7 @@ ${user.trimEnd()}
5266
5285
  }
5267
5286
  }
5268
5287
  if (body.continue) {
5269
- if (!existsSync9(worktreeDir)) {
5288
+ if (!existsSync8(worktreeDir)) {
5270
5289
  return c.json({ error: "No upgrade in progress" }, 400);
5271
5290
  }
5272
5291
  const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
@@ -5315,7 +5334,7 @@ ${user.trimEnd()}
5315
5334
  }
5316
5335
  }
5317
5336
  if (body.accept) {
5318
- if (existsSync9(worktreeDir)) {
5337
+ if (existsSync8(worktreeDir)) {
5319
5338
  try {
5320
5339
  await cleanupVariant(upgradeVariantName, dir, worktreeDir, { stop: true });
5321
5340
  } catch (err) {
@@ -5330,7 +5349,7 @@ ${user.trimEnd()}
5330
5349
  }
5331
5350
  if (body.diff) {
5332
5351
  try {
5333
- if (!existsSync9(resolve12(dir, ".git"))) {
5352
+ if (!existsSync8(resolve11(dir, ".git"))) {
5334
5353
  return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
5335
5354
  }
5336
5355
  await updateTemplateBranch(dir, template, mindName);
@@ -5348,22 +5367,22 @@ ${user.trimEnd()}
5348
5367
  );
5349
5368
  }
5350
5369
  }
5351
- if (existsSync9(worktreeDir)) {
5370
+ if (existsSync8(worktreeDir)) {
5352
5371
  return c.json(
5353
5372
  { error: "Upgrade variant already exists. Use continue or delete it first." },
5354
5373
  409
5355
5374
  );
5356
5375
  }
5357
- if (!existsSync9(resolve12(dir, ".git"))) {
5376
+ if (!existsSync8(resolve11(dir, ".git"))) {
5358
5377
  try {
5359
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve12(dir, "home") } : void 0;
5378
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve11(dir, "home") } : void 0;
5360
5379
  await gitExec(["init"], { cwd: dir, mindName, env });
5361
5380
  await configureGitIdentity(mindName, { cwd: dir, mindName, env });
5362
5381
  await gitExec(["add", "-A"], { cwd: dir, mindName, env });
5363
5382
  await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
5364
5383
  chownMindDir(dir, mindName);
5365
5384
  } catch (err) {
5366
- rmSync4(resolve12(dir, ".git"), { recursive: true, force: true });
5385
+ rmSync4(resolve11(dir, ".git"), { recursive: true, force: true });
5367
5386
  return c.json(
5368
5387
  {
5369
5388
  error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
@@ -5377,19 +5396,9 @@ ${user.trimEnd()}
5377
5396
  await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
5378
5397
  } catch {
5379
5398
  }
5380
- if (!existsSync9(resolve12(dir, "home", "shared"))) {
5381
- try {
5382
- await addSharedWorktree(mindName, dir);
5383
- } catch (err) {
5384
- logger_default.warn(
5385
- `failed to add shared worktree during upgrade for ${mindName}`,
5386
- logger_default.errorData(err)
5387
- );
5388
- }
5389
- }
5390
5399
  await updateTemplateBranch(dir, template, mindName);
5391
- const parentDir = resolve12(dir, ".variants");
5392
- if (!existsSync9(parentDir)) {
5400
+ const parentDir = resolve11(dir, ".variants");
5401
+ if (!existsSync8(parentDir)) {
5393
5402
  mkdirSync6(parentDir, { recursive: true });
5394
5403
  }
5395
5404
  await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
@@ -5517,18 +5526,26 @@ ${user.trimEnd()}
5517
5526
  const name = c.req.param("name");
5518
5527
  const entry = await findMind(name);
5519
5528
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5520
- const dir = mindDir(name);
5521
- if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
5529
+ const dir = entry.dir ?? mindDir(name);
5530
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
5522
5531
  let config2 = readVoluteConfig(dir);
5523
5532
  if (!config2 && entry.template === "pi") {
5524
- const piConfigPath = resolve12(dir, "home/.config/config.json");
5525
- if (existsSync9(piConfigPath)) {
5533
+ const piConfigPath = resolve11(dir, "home/.config/config.json");
5534
+ if (existsSync8(piConfigPath)) {
5526
5535
  try {
5527
- config2 = JSON.parse(readFileSync10(piConfigPath, "utf-8"));
5536
+ config2 = JSON.parse(readFileSync9(piConfigPath, "utf-8"));
5528
5537
  } catch {
5529
5538
  }
5530
5539
  }
5531
5540
  }
5541
+ let templateConfig = {};
5542
+ const configJsonPath = resolve11(dir, "home/.config/config.json");
5543
+ if (existsSync8(configJsonPath)) {
5544
+ try {
5545
+ templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
5546
+ } catch {
5547
+ }
5548
+ }
5532
5549
  return c.json({
5533
5550
  registry: {
5534
5551
  name: entry.name,
@@ -5538,10 +5555,26 @@ ${user.trimEnd()}
5538
5555
  template: entry.template
5539
5556
  },
5540
5557
  config: {
5541
- model: config2?.model ?? null,
5542
- thinkingLevel: config2?.thinkingLevel ?? null,
5558
+ model: config2?.model ?? templateConfig.model ?? null,
5559
+ thinkingLevel: (() => {
5560
+ if (config2?.thinkingLevel) return config2.thinkingLevel;
5561
+ const tc = templateConfig;
5562
+ if (tc.thinkingLevel) return tc.thinkingLevel;
5563
+ if (tc.reasoningEffort) return tc.reasoningEffort;
5564
+ const mtt = tc.maxThinkingTokens;
5565
+ if (mtt) {
5566
+ if (mtt <= 1024) return "minimal";
5567
+ if (mtt <= 4096) return "low";
5568
+ if (mtt <= 1e4) return "medium";
5569
+ if (mtt <= 32e3) return "high";
5570
+ return "xhigh";
5571
+ }
5572
+ return null;
5573
+ })(),
5574
+ maxThinkingTokens: config2?.maxThinkingTokens ?? templateConfig.maxThinkingTokens ?? null,
5543
5575
  tokenBudget: config2?.tokenBudget ?? null,
5544
- tokenBudgetPeriodMinutes: config2?.tokenBudgetPeriodMinutes ?? null
5576
+ tokenBudgetPeriodMinutes: config2?.tokenBudgetPeriodMinutes ?? null,
5577
+ compaction: templateConfig.compaction ?? null
5545
5578
  }
5546
5579
  });
5547
5580
  }).put(
@@ -5552,16 +5585,18 @@ ${user.trimEnd()}
5552
5585
  z5.object({
5553
5586
  model: z5.string().optional(),
5554
5587
  thinkingLevel: z5.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
5588
+ maxThinkingTokens: z5.number().int().positive().nullable().optional(),
5555
5589
  tokenBudget: z5.number().int().positive().nullable().optional(),
5556
- tokenBudgetPeriodMinutes: z5.number().int().positive().nullable().optional()
5590
+ tokenBudgetPeriodMinutes: z5.number().int().positive().nullable().optional(),
5591
+ compaction: z5.object({ maxContextTokens: z5.number().int().positive().nullable().optional() }).nullable().optional()
5557
5592
  })
5558
5593
  ),
5559
5594
  async (c) => {
5560
5595
  const name = c.req.param("name");
5561
5596
  const entry = await findMind(name);
5562
5597
  if (!entry) return c.json({ error: "Mind not found" }, 404);
5563
- const dir = mindDir(name);
5564
- if (!existsSync9(dir)) return c.json({ error: "Mind directory missing" }, 404);
5598
+ const dir = entry.dir ?? mindDir(name);
5599
+ if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
5565
5600
  const body = c.req.valid("json");
5566
5601
  const existing = readVoluteConfig(dir) ?? {};
5567
5602
  if (body.model !== void 0) existing.model = body.model;
@@ -5582,7 +5617,96 @@ ${user.trimEnd()}
5582
5617
  existing.tokenBudgetPeriodMinutes = body.tokenBudgetPeriodMinutes;
5583
5618
  }
5584
5619
  }
5620
+ if (body.maxThinkingTokens !== void 0) {
5621
+ if (body.maxThinkingTokens === null) {
5622
+ delete existing.maxThinkingTokens;
5623
+ } else {
5624
+ existing.maxThinkingTokens = body.maxThinkingTokens;
5625
+ }
5626
+ }
5585
5627
  writeVoluteConfig(dir, existing);
5628
+ const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.maxThinkingTokens !== void 0 || body.compaction !== void 0;
5629
+ if (needsConfigJson) {
5630
+ const configJsonPath = resolve11(dir, "home/.config/config.json");
5631
+ let templateConfig = {};
5632
+ if (existsSync8(configJsonPath)) {
5633
+ try {
5634
+ templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
5635
+ } catch {
5636
+ }
5637
+ }
5638
+ if (body.model !== void 0) {
5639
+ templateConfig.model = body.model;
5640
+ }
5641
+ const tmpl = entry.template ?? "claude";
5642
+ if (body.thinkingLevel !== void 0) {
5643
+ if (tmpl === "claude") {
5644
+ const claudeThinkingTokens = {
5645
+ off: null,
5646
+ minimal: 1024,
5647
+ low: 4096,
5648
+ medium: 1e4,
5649
+ high: 32e3,
5650
+ xhigh: 128e3
5651
+ };
5652
+ const tokens = claudeThinkingTokens[body.thinkingLevel] ?? null;
5653
+ if (tokens === null) {
5654
+ delete templateConfig.maxThinkingTokens;
5655
+ } else {
5656
+ templateConfig.maxThinkingTokens = tokens;
5657
+ }
5658
+ } else if (tmpl === "codex") {
5659
+ const codexMap = {
5660
+ off: null,
5661
+ minimal: "minimal",
5662
+ low: "low",
5663
+ medium: "medium",
5664
+ high: "high",
5665
+ xhigh: "xhigh"
5666
+ };
5667
+ const effort = codexMap[body.thinkingLevel] ?? null;
5668
+ if (effort === null) {
5669
+ delete templateConfig.reasoningEffort;
5670
+ } else {
5671
+ templateConfig.reasoningEffort = effort;
5672
+ }
5673
+ } else {
5674
+ templateConfig.thinkingLevel = body.thinkingLevel;
5675
+ }
5676
+ }
5677
+ if (body.maxThinkingTokens !== void 0) {
5678
+ if (body.maxThinkingTokens === null) {
5679
+ delete templateConfig.maxThinkingTokens;
5680
+ } else {
5681
+ templateConfig.maxThinkingTokens = body.maxThinkingTokens;
5682
+ }
5683
+ }
5684
+ if (body.compaction !== void 0) {
5685
+ if (body.compaction === null) {
5686
+ delete templateConfig.compaction;
5687
+ } else {
5688
+ const comp = templateConfig.compaction ?? {};
5689
+ if (body.compaction.maxContextTokens === null) {
5690
+ delete comp.maxContextTokens;
5691
+ } else if (body.compaction.maxContextTokens !== void 0) {
5692
+ comp.maxContextTokens = body.compaction.maxContextTokens;
5693
+ }
5694
+ templateConfig.compaction = comp;
5695
+ }
5696
+ }
5697
+ writeFileSync7(configJsonPath, `${JSON.stringify(templateConfig, null, 2)}
5698
+ `);
5699
+ }
5700
+ if (entry.mindType === "spirit" && body.model !== void 0) {
5701
+ try {
5702
+ const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-RHJRFURI.js");
5703
+ const globalConfig = readGlobalConfig2();
5704
+ globalConfig.spiritModel = body.model;
5705
+ writeGlobalConfig2(globalConfig);
5706
+ } catch (err) {
5707
+ logger_default.warn("failed to sync spirit model to global config", logger_default.errorData(err));
5708
+ }
5709
+ }
5586
5710
  return c.json({ ok: true });
5587
5711
  }
5588
5712
  ).get("/:name/delivery/pending", async (c) => {
@@ -5603,7 +5727,7 @@ ${user.trimEnd()}
5603
5727
  if (!body.systemPrompt || !body.message) {
5604
5728
  return c.json({ error: "systemPrompt and message required" }, 400);
5605
5729
  }
5606
- const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-SBY2WG7O.js");
5730
+ const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-LURBEDDB.js");
5607
5731
  if (!isAiConfigured()) {
5608
5732
  return c.json({ error: "AI service not configured" }, 503);
5609
5733
  }
@@ -5837,32 +5961,32 @@ ${user.trimEnd()}
5837
5961
  const db = await getDb();
5838
5962
  const rows = await db.select({
5839
5963
  session: mindHistory.session,
5840
- started_at: sql3`MIN(${mindHistory.created_at})`,
5841
- event_count: sql3`COUNT(*)`,
5842
- message_count: sql3`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
5843
- tool_count: sql3`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
5844
- }).from(mindHistory).where(and5(eq6(mindHistory.mind, name), sql3`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql3`MIN(${mindHistory.created_at}) DESC`);
5964
+ started_at: sql2`MIN(${mindHistory.created_at})`,
5965
+ event_count: sql2`COUNT(*)`,
5966
+ message_count: sql2`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
5967
+ tool_count: sql2`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
5968
+ }).from(mindHistory).where(and4(eq5(mindHistory.mind, name), sql2`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql2`MIN(${mindHistory.created_at}) DESC`);
5845
5969
  return c.json(rows);
5846
5970
  }).get("/:name/history/channels", async (c) => {
5847
5971
  const name = c.req.param("name");
5848
5972
  const db = await getDb();
5849
- const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq6(mindHistory.mind, name));
5973
+ const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq5(mindHistory.mind, name));
5850
5974
  return c.json(rows.map((r) => r.channel));
5851
5975
  }).get("/:name/history/export", async (c) => {
5852
5976
  const name = c.req.param("name");
5853
5977
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
5854
5978
  const db = await getDb();
5855
- const rows = await db.select().from(mindHistory).where(eq6(mindHistory.mind, name));
5979
+ const rows = await db.select().from(mindHistory).where(eq5(mindHistory.mind, name));
5856
5980
  return c.json(rows);
5857
5981
  }).get("/:name/history/turn", async (c) => {
5858
5982
  const name = c.req.param("name");
5859
5983
  const turnId = c.req.query("turn_id");
5860
5984
  const detail = c.req.query("detail") === "1";
5861
5985
  const db = await getDb();
5862
- const typeFilter = detail ? void 0 : sql3`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking','activity')`;
5986
+ const typeFilter = detail ? void 0 : sql2`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking','activity')`;
5863
5987
  let rows;
5864
5988
  if (turnId) {
5865
- rows = await db.select().from(mindHistory).where(and5(eq6(mindHistory.mind, name), eq6(mindHistory.turn_id, turnId), typeFilter)).orderBy(mindHistory.id);
5989
+ rows = await db.select().from(mindHistory).where(and4(eq5(mindHistory.mind, name), eq5(mindHistory.turn_id, turnId), typeFilter)).orderBy(mindHistory.id);
5866
5990
  } else {
5867
5991
  const session = c.req.query("session");
5868
5992
  const fromId = parseInt(c.req.query("from_id") ?? "", 10);
@@ -5871,11 +5995,11 @@ ${user.trimEnd()}
5871
5995
  return c.json({ error: "turn_id, or session with from_id and to_id, required" }, 400);
5872
5996
  }
5873
5997
  rows = await db.select().from(mindHistory).where(
5874
- and5(
5875
- eq6(mindHistory.mind, name),
5876
- eq6(mindHistory.session, session),
5877
- sql3`${mindHistory.id} >= ${fromId}`,
5878
- sql3`${mindHistory.id} <= ${toId}`,
5998
+ and4(
5999
+ eq5(mindHistory.mind, name),
6000
+ eq5(mindHistory.session, session),
6001
+ sql2`${mindHistory.id} >= ${fromId}`,
6002
+ sql2`${mindHistory.id} <= ${toId}`,
5879
6003
  typeFilter
5880
6004
  )
5881
6005
  ).orderBy(mindHistory.id);
@@ -5888,14 +6012,14 @@ ${user.trimEnd()}
5888
6012
  let sinceTimestamp = null;
5889
6013
  if (currentSession) {
5890
6014
  const lastTurn = await db.select({ turn_id: mindHistory.turn_id }).from(mindHistory).where(
5891
- and5(
5892
- eq6(mindHistory.mind, name),
5893
- eq6(mindHistory.session, currentSession),
5894
- sql3`${mindHistory.turn_id} IS NOT NULL`
6015
+ and4(
6016
+ eq5(mindHistory.mind, name),
6017
+ eq5(mindHistory.session, currentSession),
6018
+ sql2`${mindHistory.turn_id} IS NOT NULL`
5895
6019
  )
5896
- ).orderBy(desc4(mindHistory.created_at)).limit(1);
6020
+ ).orderBy(desc3(mindHistory.created_at)).limit(1);
5897
6021
  if (lastTurn.length > 0 && lastTurn[0].turn_id) {
5898
- const firstEvent = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(eq6(mindHistory.turn_id, lastTurn[0].turn_id)).orderBy(mindHistory.created_at).limit(1);
6022
+ const firstEvent = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(eq5(mindHistory.turn_id, lastTurn[0].turn_id)).orderBy(mindHistory.created_at).limit(1);
5899
6023
  if (firstEvent.length > 0) {
5900
6024
  sinceTimestamp = firstEvent[0].created_at;
5901
6025
  }
@@ -5905,18 +6029,18 @@ ${user.trimEnd()}
5905
6029
  sinceTimestamp = new Date(Date.now() - 36e5).toISOString().replace("T", " ").slice(0, 19);
5906
6030
  }
5907
6031
  const conditions = [
5908
- eq6(mindHistory.mind, name),
5909
- eq6(mindHistory.type, "summary"),
5910
- sql3`${mindHistory.created_at} > ${sinceTimestamp}`
6032
+ eq5(summaries.mind, name),
6033
+ eq5(summaries.period, "turn"),
6034
+ sql2`${summaries.created_at} > ${sinceTimestamp}`
5911
6035
  ];
5912
6036
  if (currentSession) {
5913
- conditions.push(sql3`${mindHistory.session} != ${currentSession}`);
6037
+ conditions.push(sql2`${turns.session} != ${currentSession}`);
5914
6038
  }
5915
6039
  const rows = await db.select({
5916
- session: mindHistory.session,
5917
- content: mindHistory.content,
5918
- created_at: mindHistory.created_at
5919
- }).from(mindHistory).where(and5(...conditions)).orderBy(desc4(mindHistory.created_at)).limit(50);
6040
+ session: turns.session,
6041
+ content: summaries.content,
6042
+ created_at: summaries.created_at
6043
+ }).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and4(...conditions)).orderBy(desc3(summaries.created_at)).limit(50);
5920
6044
  if (rows.length === 0) {
5921
6045
  return c.json({ context: null });
5922
6046
  }
@@ -5936,37 +6060,81 @@ ${lines.join("\n")}` });
5936
6060
  const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
5937
6061
  const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
5938
6062
  const db = await getDb();
5939
- const conditions = [eq6(mindHistory.mind, name)];
6063
+ const conditions = [eq5(mindHistory.mind, name)];
5940
6064
  if (channel) {
5941
- conditions.push(eq6(mindHistory.channel, channel));
6065
+ conditions.push(eq5(mindHistory.channel, channel));
5942
6066
  }
5943
6067
  if (session) {
5944
- conditions.push(eq6(mindHistory.session, session));
6068
+ conditions.push(eq5(mindHistory.session, session));
5945
6069
  }
5946
6070
  const effectivePreset = full ? "all" : preset;
6071
+ if (!effectivePreset || effectivePreset === "summary") {
6072
+ const sumConditions = [eq5(summaries.mind, name), eq5(summaries.period, "turn")];
6073
+ if (session) {
6074
+ sumConditions.push(eq5(turns.session, session));
6075
+ const sumRows2 = await db.select({
6076
+ id: summaries.id,
6077
+ mind: summaries.mind,
6078
+ period: summaries.period,
6079
+ period_key: summaries.period_key,
6080
+ content: summaries.content,
6081
+ metadata: summaries.metadata,
6082
+ created_at: summaries.created_at,
6083
+ session: turns.session
6084
+ }).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and4(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
6085
+ return c.json(
6086
+ sumRows2.map((r) => ({
6087
+ id: r.id,
6088
+ mind: r.mind,
6089
+ type: "summary",
6090
+ channel: null,
6091
+ session: r.session,
6092
+ sender: null,
6093
+ message_id: null,
6094
+ content: r.content,
6095
+ metadata: r.metadata,
6096
+ turn_id: r.period_key,
6097
+ created_at: r.created_at
6098
+ }))
6099
+ );
6100
+ }
6101
+ const sumRows = await db.select().from(summaries).where(and4(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
6102
+ return c.json(
6103
+ sumRows.map((r) => ({
6104
+ id: r.id,
6105
+ mind: r.mind,
6106
+ type: "summary",
6107
+ channel: null,
6108
+ session: null,
6109
+ sender: null,
6110
+ message_id: null,
6111
+ content: r.content,
6112
+ metadata: r.metadata,
6113
+ turn_id: r.period_key,
6114
+ created_at: r.created_at
6115
+ }))
6116
+ );
6117
+ }
5947
6118
  switch (effectivePreset) {
5948
6119
  case "all":
5949
6120
  break;
5950
6121
  case "conversation":
5951
- conditions.push(sql3`${mindHistory.type} IN ('summary','inbound','outbound','tool_use')`);
6122
+ conditions.push(sql2`${mindHistory.type} IN ('inbound','outbound','tool_use')`);
5952
6123
  break;
5953
6124
  case "detailed":
5954
6125
  conditions.push(
5955
- sql3`${mindHistory.type} IN ('summary','inbound','outbound','tool_use','tool_result','text','thinking')`
6126
+ sql2`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking')`
5956
6127
  );
5957
6128
  break;
5958
- default:
5959
- conditions.push(sql3`${mindHistory.type} IN ('summary')`);
5960
- break;
5961
6129
  }
5962
- const rows = await db.select().from(mindHistory).where(and5(...conditions)).orderBy(desc4(mindHistory.created_at)).limit(limit).offset(offset);
6130
+ const rows = await db.select().from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.created_at)).limit(limit).offset(offset);
5963
6131
  return c.json(rows);
5964
6132
  });
5965
6133
  var minds_default = app13;
5966
6134
 
5967
- // src/web/api/prompts.ts
6135
+ // packages/daemon/src/web/api/prompts.ts
5968
6136
  import { zValidator as zValidator6 } from "@hono/zod-validator";
5969
- import { eq as eq7, sql as sql4 } from "drizzle-orm";
6137
+ import { eq as eq6, sql as sql3 } from "drizzle-orm";
5970
6138
  import { Hono as Hono16 } from "hono";
5971
6139
  import { z as z6 } from "zod";
5972
6140
  var app14 = new Hono16().get("/", async (c) => {
@@ -5999,9 +6167,9 @@ var app14 = new Hono16().get("/", async (c) => {
5999
6167
  }
6000
6168
  const { content } = c.req.valid("json");
6001
6169
  const db = await getDb();
6002
- await db.insert(systemPrompts).values({ key, content, updated_at: sql4`(datetime('now'))` }).onConflictDoUpdate({
6170
+ await db.insert(systemPrompts).values({ key, content, updated_at: sql3`(datetime('now'))` }).onConflictDoUpdate({
6003
6171
  target: systemPrompts.key,
6004
- set: { content, updated_at: sql4`(datetime('now'))` }
6172
+ set: { content, updated_at: sql3`(datetime('now'))` }
6005
6173
  });
6006
6174
  return c.json({ ok: true });
6007
6175
  }).delete("/:key", requireAdmin, async (c) => {
@@ -6010,103 +6178,14 @@ var app14 = new Hono16().get("/", async (c) => {
6010
6178
  return c.json({ error: "Unknown prompt key" }, 404);
6011
6179
  }
6012
6180
  const db = await getDb();
6013
- await db.delete(systemPrompts).where(eq7(systemPrompts.key, key));
6181
+ await db.delete(systemPrompts).where(eq6(systemPrompts.key, key));
6014
6182
  return c.json({ ok: true });
6015
6183
  });
6016
6184
  var prompts_default = app14;
6017
6185
 
6018
- // src/web/api/public-files.ts
6019
- import { readdir, readFile as readFile2, stat as stat2 } from "fs/promises";
6020
- import { extname as extname3, resolve as resolve13 } from "path";
6021
- import { Hono as Hono17 } from "hono";
6022
- var MAX_FILE_SIZE = 50 * 1024 * 1024;
6023
- async function resolvePublicRoot(name) {
6024
- if (name === "_system") return resolve13(voluteHome(), "shared");
6025
- if (!await findMind(name)) return null;
6026
- return resolve13(mindDir(name), "home", "public");
6027
- }
6028
- function hasDotSegment(relativePath) {
6029
- return relativePath.split("/").some((seg) => seg.startsWith("."));
6030
- }
6031
- var MIME_TYPES = {
6032
- ".html": "text/html",
6033
- ".js": "application/javascript",
6034
- ".css": "text/css",
6035
- ".json": "application/json",
6036
- ".svg": "image/svg+xml",
6037
- ".png": "image/png",
6038
- ".jpg": "image/jpeg",
6039
- ".jpeg": "image/jpeg",
6040
- ".gif": "image/gif",
6041
- ".ico": "image/x-icon",
6042
- ".woff": "font/woff",
6043
- ".woff2": "font/woff2",
6044
- ".txt": "text/plain",
6045
- ".xml": "application/xml",
6046
- ".md": "text/markdown",
6047
- ".webp": "image/webp"
6048
- };
6049
- async function listDir(dirPath) {
6050
- let entries;
6051
- try {
6052
- entries = await readdir(dirPath, { withFileTypes: true });
6053
- } catch (err) {
6054
- if (err?.code === "ENOENT") return [];
6055
- throw err;
6056
- }
6057
- return entries.filter((e) => !e.name.startsWith(".")).map((e) => ({
6058
- name: e.name,
6059
- type: e.isDirectory() ? "directory" : "file"
6060
- }));
6061
- }
6062
- var app15 = new Hono17().get("/:name/", async (c) => {
6063
- const name = c.req.param("name");
6064
- const publicRoot = await resolvePublicRoot(name);
6065
- if (!publicRoot) return c.json({ error: "Not found" }, 404);
6066
- return c.json(await listDir(publicRoot));
6067
- }).get("/:name/*", async (c) => {
6068
- const name = c.req.param("name");
6069
- const publicRoot = await resolvePublicRoot(name);
6070
- if (!publicRoot) return c.text("Not found", 404);
6071
- const wildcard = c.req.path.replace(`/public/${name}`, "") || "/";
6072
- const relativePath = wildcard.slice(1);
6073
- const requestedPath = resolve13(publicRoot, relativePath);
6074
- if (!requestedPath.startsWith(publicRoot)) return c.text("Forbidden", 403);
6075
- if (hasDotSegment(relativePath)) return c.text("Forbidden", 403);
6076
- let fileStat;
6077
- try {
6078
- fileStat = await stat2(requestedPath);
6079
- } catch (err) {
6080
- if (err?.code === "ENOENT") return c.text("Not found", 404);
6081
- if (err?.code === "EACCES") return c.text("Forbidden", 403);
6082
- return c.text("Internal server error", 500);
6083
- }
6084
- if (fileStat.isDirectory()) {
6085
- if (wildcard.endsWith("/")) {
6086
- return c.json(await listDir(requestedPath));
6087
- }
6088
- return c.text("Not found", 404);
6089
- }
6090
- if (fileStat.isFile()) {
6091
- if (fileStat.size > MAX_FILE_SIZE) return c.text("File too large", 413);
6092
- const ext = extname3(requestedPath);
6093
- const mime = MIME_TYPES[ext] || "application/octet-stream";
6094
- try {
6095
- const body = await readFile2(requestedPath);
6096
- return c.body(body, 200, { "Content-Type": mime });
6097
- } catch (err) {
6098
- if (err?.code === "ENOENT") return c.text("Not found", 404);
6099
- if (err?.code === "EACCES") return c.text("Forbidden", 403);
6100
- return c.text("Failed to read file", 500);
6101
- }
6102
- }
6103
- return c.text("Not found", 404);
6104
- });
6105
- var public_files_default = app15;
6106
-
6107
- // src/web/api/schedules.ts
6186
+ // packages/daemon/src/web/api/schedules.ts
6108
6187
  import { CronExpressionParser } from "cron-parser";
6109
- import { Hono as Hono18 } from "hono";
6188
+ import { Hono as Hono17 } from "hono";
6110
6189
  var slog2 = logger_default.child("schedules");
6111
6190
  function readSchedules(name) {
6112
6191
  return readVoluteConfig(mindDir(name))?.schedules ?? [];
@@ -6124,7 +6203,7 @@ function writeSchedules(name, schedules) {
6124
6203
  data: { schedules }
6125
6204
  });
6126
6205
  }
6127
- var app16 = new Hono18().get("/:name/clock/status", async (c) => {
6206
+ var app15 = new Hono17().get("/:name/clock/status", async (c) => {
6128
6207
  const name = c.req.param("name");
6129
6208
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
6130
6209
  const sleepManager = getSleepManagerIfReady();
@@ -6132,23 +6211,20 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
6132
6211
  const sleepConfig = sleepManager?.getSleepConfig(name) ?? null;
6133
6212
  const schedules = readSchedules(name);
6134
6213
  const now = /* @__PURE__ */ new Date();
6135
- const in24h = new Date(now.getTime() + 24 * 60 * 6e4);
6136
6214
  const upcoming = [];
6137
6215
  const previous = [];
6138
6216
  for (const s of schedules) {
6139
6217
  if (!s.enabled) continue;
6140
6218
  if (s.fireAt) {
6141
6219
  const fireDate = new Date(s.fireAt);
6142
- if (fireDate >= now && fireDate <= in24h) {
6220
+ if (fireDate >= now) {
6143
6221
  upcoming.push({ id: s.id, at: fireDate.toISOString(), type: "timer" });
6144
6222
  }
6145
6223
  } else if (s.cron) {
6146
6224
  try {
6147
6225
  const interval = CronExpressionParser.parse(s.cron);
6148
6226
  const next = interval.next().toDate();
6149
- if (next <= in24h) {
6150
- upcoming.push({ id: s.id, at: next.toISOString(), type: "cron" });
6151
- }
6227
+ upcoming.push({ id: s.id, at: next.toISOString(), type: "cron" });
6152
6228
  } catch {
6153
6229
  slog2.warn(`invalid cron "${s.cron}" for schedule "${s.id}" of ${name}`);
6154
6230
  }
@@ -6160,9 +6236,61 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
6160
6236
  }
6161
6237
  }
6162
6238
  }
6239
+ if (sleepState?.sleeping) {
6240
+ if (sleepState.scheduledWakeAt) {
6241
+ upcoming.push({ id: "sleep", at: sleepState.scheduledWakeAt, type: "cron" });
6242
+ }
6243
+ if (sleepState.sleepingSince) {
6244
+ previous.push({ id: "sleep", at: sleepState.sleepingSince });
6245
+ }
6246
+ } else if (sleepConfig?.enabled && sleepConfig.schedule) {
6247
+ try {
6248
+ const sleepInterval = CronExpressionParser.parse(sleepConfig.schedule.sleep);
6249
+ const nextSleep = sleepInterval.next().toDate();
6250
+ upcoming.push({ id: "sleep", at: nextSleep.toISOString(), type: "cron" });
6251
+ } catch {
6252
+ }
6253
+ try {
6254
+ const prevWakeInterval = CronExpressionParser.parse(sleepConfig.schedule.wake);
6255
+ const prevWake = prevWakeInterval.prev().toDate();
6256
+ previous.push({ id: "sleep", at: prevWake.toISOString() });
6257
+ } catch {
6258
+ }
6259
+ }
6163
6260
  upcoming.sort((a, b) => a.at.localeCompare(b.at));
6164
6261
  previous.sort((a, b) => b.at.localeCompare(a.at));
6165
6262
  return c.json({ sleep: sleepState, sleepConfig, schedules, upcoming, previous });
6263
+ }).get("/:name/sleep/config", async (c) => {
6264
+ const name = c.req.param("name");
6265
+ if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
6266
+ const config2 = readVoluteConfig(mindDir(name));
6267
+ return c.json(config2?.sleep ?? { enabled: false });
6268
+ }).put("/:name/sleep/config", requireSelf(), async (c) => {
6269
+ const name = c.req.param("name");
6270
+ const entry = await findMind(name);
6271
+ if (!entry) return c.json({ error: "Mind not found" }, 404);
6272
+ const body = await c.req.json();
6273
+ if (body.schedule) {
6274
+ for (const field of ["sleep", "wake"]) {
6275
+ if (body.schedule[field]) {
6276
+ try {
6277
+ CronExpressionParser.parse(body.schedule[field]);
6278
+ } catch {
6279
+ return c.json({ error: `Invalid ${field} cron: ${body.schedule[field]}` }, 400);
6280
+ }
6281
+ }
6282
+ }
6283
+ }
6284
+ const dir = mindDir(name);
6285
+ const config2 = readVoluteConfig(dir) ?? {};
6286
+ const sleep = config2.sleep ?? {};
6287
+ if (body.enabled !== void 0) sleep.enabled = body.enabled;
6288
+ if (body.schedule !== void 0) sleep.schedule = body.schedule;
6289
+ if (body.wakeTriggers !== void 0) sleep.wakeTriggers = body.wakeTriggers;
6290
+ config2.sleep = sleep;
6291
+ writeVoluteConfig(dir, config2);
6292
+ getSleepManagerIfReady()?.invalidateSleepConfig(name);
6293
+ return c.json({ ok: true });
6166
6294
  }).get("/:name/schedules", async (c) => {
6167
6295
  const name = c.req.param("name");
6168
6296
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
@@ -6290,7 +6418,7 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
6290
6418
  const body = await c.req.text();
6291
6419
  const message = `[webhook: ${event}] ${body}`;
6292
6420
  try {
6293
- const { sendSystemMessage } = await import("./system-chat-JAPOJ3KE.js");
6421
+ const { sendSystemMessage } = await import("./system-chat-IDPHYHY4.js");
6294
6422
  await sendSystemMessage(name, message);
6295
6423
  return c.json({ ok: true });
6296
6424
  } catch (err) {
@@ -6298,19 +6426,19 @@ var app16 = new Hono18().get("/:name/clock/status", async (c) => {
6298
6426
  return c.json({ error: "Failed to reach mind" }, 502);
6299
6427
  }
6300
6428
  });
6301
- var schedules_default = app16;
6429
+ var schedules_default = app15;
6302
6430
 
6303
- // src/web/api/setup.ts
6431
+ // packages/daemon/src/web/api/setup.ts
6304
6432
  import { mkdirSync as mkdirSync7 } from "fs";
6305
6433
  import { homedir as homedir2 } from "os";
6306
- import { resolve as resolve14 } from "path";
6307
- import { Hono as Hono19 } from "hono";
6434
+ import { resolve as resolve12 } from "path";
6435
+ import { Hono as Hono18 } from "hono";
6308
6436
  import { setCookie as setCookie2 } from "hono/cookie";
6309
6437
  var DEFAULT_API_URL2 = "https://volute.systems";
6310
- var setup = new Hono19();
6438
+ var setup = new Hono18();
6311
6439
  function writeSetupConfig(systemName, description) {
6312
- const configHome = process.env.VOLUTE_HOME ?? resolve14(homedir2(), ".volute");
6313
- const mindsDir = resolve14(configHome, "minds");
6440
+ const configHome = process.env.VOLUTE_HOME ?? resolve12(homedir2(), ".volute");
6441
+ const mindsDir = resolve12(configHome, "minds");
6314
6442
  mkdirSync7(configHome, { recursive: true });
6315
6443
  mkdirSync7(mindsDir, { recursive: true });
6316
6444
  const existingConfig = readGlobalConfig();
@@ -6343,7 +6471,7 @@ setup.get("/status", async (c) => {
6343
6471
  let hasAccount = false;
6344
6472
  if (hasSystem) {
6345
6473
  try {
6346
- const { listUsersByType: listUsersByType2 } = await import("./auth-GKCDSO4T.js");
6474
+ const { listUsersByType: listUsersByType2 } = await import("./auth-WX4TESEI.js");
6347
6475
  const brains = await listUsersByType2("brain");
6348
6476
  hasAccount = brains.length > 0;
6349
6477
  } catch (err) {
@@ -6392,7 +6520,11 @@ setup.post("/system", async (c) => {
6392
6520
  return c.json({ error: "System name is required" }, 400);
6393
6521
  }
6394
6522
  try {
6395
- writeSetupConfig(body.name.trim(), body.description?.trim());
6523
+ const config2 = writeSetupConfig(body.name.trim(), body.description?.trim());
6524
+ if (body.remote) {
6525
+ config2.hostname = "0.0.0.0";
6526
+ writeGlobalConfig(config2);
6527
+ }
6396
6528
  return c.json({ ok: true });
6397
6529
  } catch (err) {
6398
6530
  return c.json({ error: `Failed to write configuration: ${err.message}` }, 500);
@@ -6528,7 +6660,7 @@ setup.post("/account", async (c) => {
6528
6660
  }
6529
6661
  }
6530
6662
  try {
6531
- const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-GKCDSO4T.js");
6663
+ const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-WX4TESEI.js");
6532
6664
  const user = await createUser2(body.username.trim(), body.password);
6533
6665
  if (body.displayName?.trim()) {
6534
6666
  await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
@@ -6574,7 +6706,7 @@ setup.post("/models", async (c) => {
6574
6706
  return c.json({ error: "Spirit model is required" }, 400);
6575
6707
  }
6576
6708
  try {
6577
- const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-SBY2WG7O.js");
6709
+ const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-LURBEDDB.js");
6578
6710
  setEnabledModels3(body.models);
6579
6711
  const config2 = readGlobalConfig();
6580
6712
  config2.spiritModel = body.spiritModel.trim();
@@ -6592,8 +6724,8 @@ setup.post("/complete", async (c) => {
6592
6724
  return c.json({ error: "Setup already complete" }, 400);
6593
6725
  }
6594
6726
  try {
6595
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-VRONKFMF.js");
6596
- const { startSpiritFull } = await import("./mind-service-2MQ6UK5N.js");
6727
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-ZFRDXMG7.js");
6728
+ const { startSpiritFull } = await import("./mind-service-X2CAA6W6.js");
6597
6729
  await ensureSpiritProject();
6598
6730
  await syncSpiritTemplate();
6599
6731
  const warnings = [];
@@ -6609,8 +6741,8 @@ setup.post("/complete", async (c) => {
6609
6741
  }
6610
6742
  let spiritConversationId;
6611
6743
  try {
6612
- const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-GKCDSO4T.js");
6613
- const { createConversation: createConversation6, findDMConversation: findDMConversation2 } = await import("./conversations-AWI5SZW2.js");
6744
+ const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-WX4TESEI.js");
6745
+ const { createConversation: createConversation6, findDMConversation: findDMConversation2 } = await import("./conversations-2PW57WO2.js");
6614
6746
  const spiritUser = await getOrCreateMindUser2("volute");
6615
6747
  const brains = await listUsersByType2("brain");
6616
6748
  const admin2 = brains.find((u) => u.role === "admin");
@@ -6633,8 +6765,8 @@ setup.post("/complete", async (c) => {
6633
6765
  logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
6634
6766
  if (spiritConversationId && spiritStarted) {
6635
6767
  try {
6636
- const { deliverMessage: deliverMessage2 } = await import("./message-delivery-DFF5SJRM.js");
6637
- const { listUsersByType: listUsers6 } = await import("./auth-GKCDSO4T.js");
6768
+ const { deliverMessage: deliverMessage2 } = await import("./message-delivery-GRC4W6P7.js");
6769
+ const { listUsersByType: listUsers6 } = await import("./auth-WX4TESEI.js");
6638
6770
  const admins = await listUsers6("brain");
6639
6771
  const admin2 = admins.find((u) => u.role === "admin");
6640
6772
  const adminName = admin2?.display_name || admin2?.username || "the admin";
@@ -6670,35 +6802,13 @@ setup.post("/complete", async (c) => {
6670
6802
  });
6671
6803
  var setup_default = setup;
6672
6804
 
6673
- // src/web/api/shared.ts
6674
- import { Hono as Hono20 } from "hono";
6675
- var app17 = new Hono20().post("/:name/shared/merge", requireAdmin, async (c) => {
6676
- const name = c.req.param("name");
6677
- const entry = await findMind(name);
6678
- if (!entry) return c.json({ error: "Mind not found" }, 404);
6679
- let body;
6680
- try {
6681
- body = await c.req.json();
6682
- } catch {
6683
- return c.json({ error: "Invalid JSON in request body" }, 400);
6684
- }
6685
- const message = body.message || `shared: merge from ${name}`;
6686
- try {
6687
- const result = await sharedMerge(name, mindDir(name), message);
6688
- return c.json(result);
6689
- } catch (err) {
6690
- return c.json({ error: err instanceof Error ? err.message : "Merge failed" }, 500);
6691
- }
6692
- });
6693
- var shared_default = app17;
6694
-
6695
- // src/web/api/skills.ts
6696
- import { existsSync as existsSync10, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
6805
+ // packages/daemon/src/web/api/skills.ts
6806
+ import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
6697
6807
  import { tmpdir } from "os";
6698
- import { join, resolve as resolve15 } from "path";
6808
+ import { join, resolve as resolve13 } from "path";
6699
6809
  import AdmZip from "adm-zip";
6700
- import { Hono as Hono21 } from "hono";
6701
- var app18 = new Hono21().get("/", async (c) => {
6810
+ import { Hono as Hono19 } from "hono";
6811
+ var app16 = new Hono19().get("/", async (c) => {
6702
6812
  const skills = await listSharedSkills();
6703
6813
  return c.json(skills);
6704
6814
  }).get("/defaults/list", async (c) => {
@@ -6747,6 +6857,16 @@ var app18 = new Hono21().get("/", async (c) => {
6747
6857
  removed.add(skill);
6748
6858
  writeGlobalConfig({ ...config2, defaultSkills: updated, removedDefaultSkills: [...removed] });
6749
6859
  return c.json({ skills: updated });
6860
+ }).get("/auto-update", (c) => {
6861
+ return c.json({ enabled: isAutoUpdateSkillsEnabled() });
6862
+ }).put("/auto-update", requireAdmin, async (c) => {
6863
+ const body = await c.req.json();
6864
+ if (typeof body.enabled !== "boolean") {
6865
+ return c.json({ error: "body.enabled must be a boolean" }, 400);
6866
+ }
6867
+ const config2 = readGlobalConfig();
6868
+ writeGlobalConfig({ ...config2, autoUpdateSkills: body.enabled });
6869
+ return c.json({ enabled: body.enabled });
6750
6870
  }).post("/upload", requireAdmin, async (c) => {
6751
6871
  const body = await c.req.parseBody();
6752
6872
  const file = body.file;
@@ -6761,19 +6881,19 @@ var app18 = new Hono21().get("/", async (c) => {
6761
6881
  try {
6762
6882
  const zip = new AdmZip(buffer2);
6763
6883
  for (const entry of zip.getEntries()) {
6764
- const target = resolve15(tmpDir, entry.entryName);
6884
+ const target = resolve13(tmpDir, entry.entryName);
6765
6885
  if (!target.startsWith(tmpDir)) {
6766
6886
  return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
6767
6887
  }
6768
6888
  }
6769
6889
  zip.extractAllTo(tmpDir, true);
6770
6890
  let skillDir = null;
6771
- if (existsSync10(join(tmpDir, "SKILL.md"))) {
6891
+ if (existsSync9(join(tmpDir, "SKILL.md"))) {
6772
6892
  skillDir = tmpDir;
6773
6893
  } else {
6774
6894
  const entries = readdirSync3(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
6775
6895
  for (const entry of entries) {
6776
- if (existsSync10(join(tmpDir, entry.name, "SKILL.md"))) {
6896
+ if (existsSync9(join(tmpDir, entry.name, "SKILL.md"))) {
6777
6897
  skillDir = join(tmpDir, entry.name);
6778
6898
  break;
6779
6899
  }
@@ -6809,18 +6929,18 @@ var app18 = new Hono21().get("/", async (c) => {
6809
6929
  }
6810
6930
  return c.json({ ok: true });
6811
6931
  });
6812
- var skills_default = app18;
6932
+ var skills_default = app16;
6813
6933
 
6814
- // src/web/api/typing.ts
6934
+ // packages/daemon/src/web/api/typing.ts
6815
6935
  import { zValidator as zValidator7 } from "@hono/zod-validator";
6816
- import { Hono as Hono22 } from "hono";
6936
+ import { Hono as Hono20 } from "hono";
6817
6937
  import { z as z7 } from "zod";
6818
6938
  var typingSchema = z7.object({
6819
6939
  channel: z7.string().min(1),
6820
6940
  sender: z7.string().min(1),
6821
6941
  active: z7.boolean()
6822
6942
  });
6823
- var app19 = new Hono22().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
6943
+ var app17 = new Hono20().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
6824
6944
  const { channel, sender, active } = c.req.valid("json");
6825
6945
  const map = getTypingMap();
6826
6946
  if (active) {
@@ -6840,13 +6960,13 @@ var app19 = new Hono22().post("/:name/typing", zValidator7("json", typingSchema)
6840
6960
  const map = getTypingMap();
6841
6961
  return c.json({ typing: map.get(channel) });
6842
6962
  });
6843
- var typing_default = app19;
6963
+ var typing_default = app17;
6844
6964
 
6845
- // src/web/api/update.ts
6965
+ // packages/daemon/src/web/api/update.ts
6846
6966
  import { spawn as spawn3 } from "child_process";
6847
- import { Hono as Hono23 } from "hono";
6967
+ import { Hono as Hono21 } from "hono";
6848
6968
  var bin;
6849
- var app20 = new Hono23().get("/update", async (c) => {
6969
+ var app18 = new Hono21().get("/update", async (c) => {
6850
6970
  const result = await checkForUpdate();
6851
6971
  return c.json(result);
6852
6972
  }).post("/update", requireAdmin, async (c) => {
@@ -6861,17 +6981,17 @@ var app20 = new Hono23().get("/update", async (c) => {
6861
6981
  child.unref();
6862
6982
  return c.json({ ok: true, message: "Updating..." });
6863
6983
  });
6864
- var update_default = app20;
6984
+ var update_default = app18;
6865
6985
 
6866
- // src/web/api/v1/conversations.ts
6986
+ // packages/daemon/src/web/api/v1/conversations.ts
6867
6987
  import { zValidator as zValidator8 } from "@hono/zod-validator";
6868
- import { Hono as Hono24 } from "hono";
6988
+ import { Hono as Hono22 } from "hono";
6869
6989
  import { z as z8 } from "zod";
6870
6990
  var createSchema = z8.object({
6871
6991
  title: z8.string().optional(),
6872
6992
  participantNames: z8.array(z8.string()).min(1)
6873
6993
  });
6874
- var app21 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
6994
+ var app19 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
6875
6995
  const user = c.get("user");
6876
6996
  const convs = await listConversationsWithParticipants(user.id);
6877
6997
  return c.json(convs);
@@ -6956,14 +7076,14 @@ var app21 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
6956
7076
  if (!deleted) return c.json({ error: "Conversation not found" }, 404);
6957
7077
  return c.json({ ok: true });
6958
7078
  });
6959
- var conversations_default = app21;
7079
+ var conversations_default = app19;
6960
7080
 
6961
- // src/web/api/v1/events.ts
6962
- import { desc as desc5 } from "drizzle-orm";
6963
- import { Hono as Hono25 } from "hono";
7081
+ // packages/daemon/src/web/api/v1/events.ts
7082
+ import { desc as desc4 } from "drizzle-orm";
7083
+ import { Hono as Hono23 } from "hono";
6964
7084
  import { streamSSE as streamSSE4 } from "hono/streaming";
6965
7085
 
6966
- // src/lib/events/brain-presence.ts
7086
+ // packages/daemon/src/lib/events/brain-presence.ts
6967
7087
  var connections = /* @__PURE__ */ new Map();
6968
7088
  function addConnection(username) {
6969
7089
  const count = connections.get(username) ?? 0;
@@ -6986,7 +7106,7 @@ function getOnlineBrains() {
6986
7106
  return [...connections.keys()];
6987
7107
  }
6988
7108
 
6989
- // src/lib/events/event-sequencer.ts
7109
+ // packages/daemon/src/lib/events/event-sequencer.ts
6990
7110
  var BUFFER_SIZE = 1e3;
6991
7111
  var MAX_AGE_MS = 5 * 60 * 1e3;
6992
7112
  var nextId = 1;
@@ -7006,8 +7126,8 @@ function getEventsSince(sinceId) {
7006
7126
  return buffer.slice(startIdx).filter((e) => now - e.timestamp < MAX_AGE_MS);
7007
7127
  }
7008
7128
 
7009
- // src/web/api/v1/events.ts
7010
- var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
7129
+ // packages/daemon/src/web/api/v1/events.ts
7130
+ var app20 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
7011
7131
  const user = c.get("user");
7012
7132
  const since = c.req.query("since");
7013
7133
  const sinceId = since ? Number(since) : 0;
@@ -7030,7 +7150,7 @@ var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
7030
7150
  let recentActivity = [];
7031
7151
  try {
7032
7152
  const db = await getDb();
7033
- recentActivity = await db.select().from(activity).orderBy(desc5(activity.created_at)).limit(50);
7153
+ recentActivity = await db.select().from(activity).orderBy(desc4(activity.created_at)).limit(50);
7034
7154
  recentActivity = recentActivity.map((row) => ({
7035
7155
  ...row,
7036
7156
  metadata: row.metadata ? JSON.parse(row.metadata) : null
@@ -7097,8 +7217,8 @@ var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
7097
7217
  });
7098
7218
  }, 15e3);
7099
7219
  cleanups.push(() => clearInterval(keepAlive));
7100
- await new Promise((resolve20) => {
7101
- stream.onAbort(() => resolve20());
7220
+ await new Promise((resolve18) => {
7221
+ stream.onAbort(() => resolve18());
7102
7222
  });
7103
7223
  } finally {
7104
7224
  for (const cleanup of cleanups) {
@@ -7110,19 +7230,19 @@ var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
7110
7230
  }
7111
7231
  });
7112
7232
  });
7113
- var events_default = app22;
7233
+ var events_default = app20;
7114
7234
 
7115
- // src/web/api/variants.ts
7116
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
7117
- import { resolve as resolve17 } from "path";
7118
- import { Hono as Hono26 } from "hono";
7235
+ // packages/daemon/src/web/api/variants.ts
7236
+ import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
7237
+ import { resolve as resolve15 } from "path";
7238
+ import { Hono as Hono24 } from "hono";
7119
7239
 
7120
- // src/lib/spawn-server.ts
7240
+ // packages/daemon/src/lib/spawn-server.ts
7121
7241
  import { spawn as spawn4 } from "child_process";
7122
- import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11 } from "fs";
7123
- import { resolve as resolve16 } from "path";
7242
+ import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync10 } from "fs";
7243
+ import { resolve as resolve14 } from "path";
7124
7244
  function tsxBin(cwd) {
7125
- return resolve16(cwd, "node_modules", ".bin", "tsx");
7245
+ return resolve14(cwd, "node_modules", ".bin", "tsx");
7126
7246
  }
7127
7247
  function spawnServer(cwd, port, options) {
7128
7248
  if (options?.detached) {
@@ -7135,31 +7255,31 @@ function spawnAttached(cwd, port) {
7135
7255
  cwd,
7136
7256
  stdio: ["ignore", "pipe", "pipe"]
7137
7257
  });
7138
- return new Promise((resolve20) => {
7139
- const timeout = setTimeout(() => resolve20(null), 3e4);
7258
+ return new Promise((resolve18) => {
7259
+ const timeout = setTimeout(() => resolve18(null), 3e4);
7140
7260
  function checkOutput(data) {
7141
7261
  const match = data.toString().match(/listening on :(\d+)/);
7142
7262
  if (match) {
7143
7263
  clearTimeout(timeout);
7144
- resolve20({ child, actualPort: parseInt(match[1], 10) });
7264
+ resolve18({ child, actualPort: parseInt(match[1], 10) });
7145
7265
  }
7146
7266
  }
7147
7267
  child.stdout?.on("data", checkOutput);
7148
7268
  child.stderr?.on("data", checkOutput);
7149
7269
  child.on("error", () => {
7150
7270
  clearTimeout(timeout);
7151
- resolve20(null);
7271
+ resolve18(null);
7152
7272
  });
7153
7273
  child.on("exit", () => {
7154
7274
  clearTimeout(timeout);
7155
- resolve20(null);
7275
+ resolve18(null);
7156
7276
  });
7157
7277
  });
7158
7278
  }
7159
7279
  function spawnDetached(cwd, port, logDir) {
7160
- const logsDir = logDir ?? resolve16(cwd, ".mind", "logs");
7280
+ const logsDir = logDir ?? resolve14(cwd, ".mind", "logs");
7161
7281
  mkdirSync8(logsDir, { recursive: true });
7162
- const logPath = resolve16(logsDir, "mind.log");
7282
+ const logPath = resolve14(logsDir, "mind.log");
7163
7283
  const logFd = openSync(logPath, "a");
7164
7284
  const child = spawn4(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
7165
7285
  cwd,
@@ -7179,7 +7299,7 @@ function spawnDetached(cwd, port, logDir) {
7179
7299
  }
7180
7300
  const interval = setInterval(() => {
7181
7301
  try {
7182
- const content = readFileSync11(logPath, "utf-8");
7302
+ const content = readFileSync10(logPath, "utf-8");
7183
7303
  const match = content.match(/listening on :(\d+)/);
7184
7304
  if (match) {
7185
7305
  finish({ child, actualPort: parseInt(match[1], 10) });
@@ -7193,7 +7313,7 @@ function spawnDetached(cwd, port, logDir) {
7193
7313
  });
7194
7314
  }
7195
7315
 
7196
- // src/lib/verify.ts
7316
+ // packages/daemon/src/lib/verify.ts
7197
7317
  async function verify2(port) {
7198
7318
  const health = await checkHealth(port);
7199
7319
  if (!health.ok) {
@@ -7230,8 +7350,8 @@ async function verify2(port) {
7230
7350
  }
7231
7351
  }
7232
7352
 
7233
- // src/web/api/variants.ts
7234
- var app23 = new Hono26().get("/:name/variants", async (c) => {
7353
+ // packages/daemon/src/web/api/variants.ts
7354
+ var app21 = new Hono24().get("/:name/variants", async (c) => {
7235
7355
  const name = c.req.param("name");
7236
7356
  const entry = await findMind(name);
7237
7357
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -7275,11 +7395,11 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
7275
7395
  return c.json({ error: `Name already in use: ${variantName}` }, 409);
7276
7396
  }
7277
7397
  const projectRoot = mindDir(mindName);
7278
- const variantDir = resolve17(projectRoot, ".variants", variantName);
7279
- if (existsSync11(variantDir)) {
7398
+ const variantDir = resolve15(projectRoot, ".variants", variantName);
7399
+ if (existsSync10(variantDir)) {
7280
7400
  return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
7281
7401
  }
7282
- mkdirSync9(resolve17(projectRoot, ".variants"), { recursive: true });
7402
+ mkdirSync9(resolve15(projectRoot, ".variants"), { recursive: true });
7283
7403
  try {
7284
7404
  await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
7285
7405
  } catch (e) {
@@ -7292,7 +7412,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
7292
7412
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
7293
7413
  await exec(cmd, args, {
7294
7414
  cwd: variantDir,
7295
- env: { ...process.env, HOME: resolve17(variantDir, "home") }
7415
+ env: { ...process.env, HOME: resolve15(variantDir, "home") }
7296
7416
  });
7297
7417
  } else {
7298
7418
  await exec("npm", ["install"], { cwd: variantDir });
@@ -7302,7 +7422,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
7302
7422
  return c.json({ error: `npm install failed: ${msg}` }, 500);
7303
7423
  }
7304
7424
  if (body.soul) {
7305
- writeFileSync8(resolve17(variantDir, "home/SOUL.md"), body.soul);
7425
+ writeFileSync8(resolve15(variantDir, "home/SOUL.md"), body.soul);
7306
7426
  }
7307
7427
  const variantPort = body.port ?? await nextPort();
7308
7428
  await addVariant(variantName, mindName, variantPort, variantDir, variantName);
@@ -7337,7 +7457,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
7337
7457
  } catch {
7338
7458
  }
7339
7459
  const projectRoot = mindDir(mindName);
7340
- if (existsSync11(variantEntry.dir)) {
7460
+ if (existsSync10(variantEntry.dir)) {
7341
7461
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
7342
7462
  if (status) {
7343
7463
  try {
@@ -7397,8 +7517,8 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
7397
7517
  await cleanupVariant(variantName, projectRoot, variantEntry.dir);
7398
7518
  if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
7399
7519
  try {
7400
- const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-A6VVKOXJ.js");
7401
- const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-PJ4S5PHQ.js");
7520
+ const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-A7FNHTB7.js");
7521
+ const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-GBSNW3HG.js");
7402
7522
  const tmpl = parentEntry.template ?? "claude";
7403
7523
  await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
7404
7524
  } catch (err) {
@@ -7411,7 +7531,7 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
7411
7531
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
7412
7532
  await exec(cmd, args, {
7413
7533
  cwd: projectRoot,
7414
- env: { ...process.env, HOME: resolve17(projectRoot, "home") }
7534
+ env: { ...process.env, HOME: resolve15(projectRoot, "home") }
7415
7535
  });
7416
7536
  } else {
7417
7537
  await exec("npm", ["install"], { cwd: projectRoot });
@@ -7453,11 +7573,11 @@ var app23 = new Hono26().get("/:name/variants", async (c) => {
7453
7573
  await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
7454
7574
  return c.json({ ok: true });
7455
7575
  });
7456
- var variants_default = app23;
7576
+ var variants_default = app21;
7457
7577
 
7458
- // src/web/api/volute/channels.ts
7578
+ // packages/daemon/src/web/api/volute/channels.ts
7459
7579
  import { zValidator as zValidator9 } from "@hono/zod-validator";
7460
- import { Hono as Hono27 } from "hono";
7580
+ import { Hono as Hono25 } from "hono";
7461
7581
  import { z as z9 } from "zod";
7462
7582
  var createSchema2 = z9.object({
7463
7583
  name: z9.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
@@ -7465,7 +7585,7 @@ var createSchema2 = z9.object({
7465
7585
  var inviteSchema = z9.object({
7466
7586
  username: z9.string().min(1)
7467
7587
  });
7468
- var app24 = new Hono27().get("/", async (c) => {
7588
+ var app22 = new Hono25().get("/", async (c) => {
7469
7589
  const user = c.get("user");
7470
7590
  const channels = await listChannels();
7471
7591
  const results = await Promise.all(
@@ -7535,15 +7655,15 @@ var app24 = new Hono27().get("/", async (c) => {
7535
7655
  ]);
7536
7656
  return c.json({ ok: true });
7537
7657
  });
7538
- var channels_default2 = app24;
7658
+ var channels_default2 = app22;
7539
7659
 
7540
- // src/web/api/volute/chat.ts
7660
+ // packages/daemon/src/web/api/volute/chat.ts
7541
7661
  import { zValidator as zValidator10 } from "@hono/zod-validator";
7542
- import { Hono as Hono28 } from "hono";
7662
+ import { Hono as Hono26 } from "hono";
7543
7663
  import { streamSSE as streamSSE5 } from "hono/streaming";
7544
7664
  import { z as z10 } from "zod";
7545
7665
 
7546
- // src/lib/bridge-outbound.ts
7666
+ // packages/daemon/src/lib/bridge-outbound.ts
7547
7667
  function extractContent(contentBlocks) {
7548
7668
  const text = contentBlocks.filter((b) => b.type === "text").map((b) => b.text).join("\n");
7549
7669
  const images = contentBlocks.filter((b) => b.type === "image").map((b) => ({ media_type: b.media_type, data: b.data }));
@@ -7614,7 +7734,7 @@ async function routeDMOutbound(conversationId, senderName, contentBlocks) {
7614
7734
  }
7615
7735
  }
7616
7736
 
7617
- // src/web/api/volute/chat.ts
7737
+ // packages/daemon/src/web/api/volute/chat.ts
7618
7738
  async function fanOutToMinds(opts) {
7619
7739
  const participants = await getParticipants(opts.conversationId);
7620
7740
  const mindParticipants = participants.filter(
@@ -7622,8 +7742,8 @@ async function fanOutToMinds(opts) {
7622
7742
  );
7623
7743
  const participantNames = participants.map((p) => p.username);
7624
7744
  const isDM = opts.isDM ?? participants.length === 2;
7625
- const { getMindManager: getMindManager2 } = await import("./mind-manager-NBJF5D26.js");
7626
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
7745
+ const { getMindManager: getMindManager2 } = await import("./mind-manager-HFLB5653.js");
7746
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-7KFK3USC.js");
7627
7747
  const manager = getMindManager2();
7628
7748
  const sm = getSleepManagerIfReady2();
7629
7749
  const targetMinds = mindParticipants.map((ap) => {
@@ -7703,7 +7823,7 @@ function stageFilesForMinds(files, targetMinds, senderName) {
7703
7823
  }
7704
7824
  return { notifications };
7705
7825
  }
7706
- var unifiedChatApp = new Hono28().post(
7826
+ var unifiedChatApp = new Hono26().post(
7707
7827
  "/chat",
7708
7828
  zValidator10("json", chatSchema),
7709
7829
  async (c) => {
@@ -7841,7 +7961,7 @@ var unifiedChatApp = new Hono28().post(
7841
7961
  return c.json({ ok: true, conversationId, outboundId });
7842
7962
  }
7843
7963
  );
7844
- var app25 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
7964
+ var app23 = new Hono26().get("/:name/conversations/:id/events", async (c) => {
7845
7965
  const conversationId = c.req.param("id");
7846
7966
  const user = c.get("user");
7847
7967
  if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
@@ -7858,27 +7978,27 @@ var app25 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
7858
7978
  if (!stream.aborted) console.error("[chat] SSE ping error:", err);
7859
7979
  });
7860
7980
  }, 15e3);
7861
- await new Promise((resolve20) => {
7981
+ await new Promise((resolve18) => {
7862
7982
  stream.onAbort(() => {
7863
7983
  unsubscribe();
7864
7984
  clearInterval(keepAlive);
7865
- resolve20();
7985
+ resolve18();
7866
7986
  });
7867
7987
  });
7868
7988
  });
7869
7989
  });
7870
- var chat_default = app25;
7990
+ var chat_default = app23;
7871
7991
 
7872
- // src/web/api/volute/conversations.ts
7992
+ // packages/daemon/src/web/api/volute/conversations.ts
7873
7993
  import { zValidator as zValidator11 } from "@hono/zod-validator";
7874
- import { Hono as Hono29 } from "hono";
7994
+ import { Hono as Hono27 } from "hono";
7875
7995
  import { z as z11 } from "zod";
7876
7996
  var createConvSchema = z11.object({
7877
7997
  title: z11.string().optional(),
7878
7998
  participantIds: z11.array(z11.number()).optional(),
7879
7999
  participantNames: z11.array(z11.string()).optional()
7880
8000
  });
7881
- var app26 = new Hono29().get("/:name/conversations", async (c) => {
8001
+ var app24 = new Hono27().get("/:name/conversations", async (c) => {
7882
8002
  const name = c.req.param("name");
7883
8003
  const user = c.get("user");
7884
8004
  let lookupId = user.id;
@@ -7986,12 +8106,12 @@ var app26 = new Hono29().get("/:name/conversations", async (c) => {
7986
8106
  if (!deleted) return c.json({ error: "Conversation not found" }, 404);
7987
8107
  return c.json({ ok: true });
7988
8108
  });
7989
- var conversations_default2 = app26;
8109
+ var conversations_default2 = app24;
7990
8110
 
7991
- // src/web/app.ts
8111
+ // packages/daemon/src/web/app.ts
7992
8112
  var httpLog = logger_default.child("http");
7993
- var app27 = new Hono30();
7994
- app27.onError((err, c) => {
8113
+ var app25 = new Hono28();
8114
+ app25.onError((err, c) => {
7995
8115
  if (err instanceof HTTPException) {
7996
8116
  return err.getResponse();
7997
8117
  }
@@ -8002,10 +8122,10 @@ app27.onError((err, c) => {
8002
8122
  });
8003
8123
  return c.json({ error: "Internal server error" }, 500);
8004
8124
  });
8005
- app27.notFound((c) => {
8125
+ app25.notFound((c) => {
8006
8126
  return c.json({ error: "Not found" }, 404);
8007
8127
  });
8008
- app27.use("*", async (c, next) => {
8128
+ app25.use("*", async (c, next) => {
8009
8129
  const start = Date.now();
8010
8130
  await next();
8011
8131
  const duration = Date.now() - start;
@@ -8016,7 +8136,22 @@ app27.use("*", async (c, next) => {
8016
8136
  httpLog.debug("request", data);
8017
8137
  }
8018
8138
  });
8019
- app27.get("/api/health", (c) => {
8139
+ app25.use("/api/*", bodyLimit({ maxSize: 10 * 1024 * 1024 }));
8140
+ app25.use(
8141
+ "/api/*",
8142
+ cors({
8143
+ origin: (origin) => origin,
8144
+ allowHeaders: ["Authorization", "Content-Type", "X-Volute-Session"],
8145
+ allowMethods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
8146
+ credentials: false
8147
+ })
8148
+ );
8149
+ app25.use("/api/*", async (c, next) => {
8150
+ const auth = c.req.header("Authorization");
8151
+ if (auth?.startsWith("Bearer ") && auth.length > 7) return next();
8152
+ return csrf()(c, next);
8153
+ });
8154
+ app25.get("/api/health", (c) => {
8020
8155
  let version = "unknown";
8021
8156
  let cached = null;
8022
8157
  try {
@@ -8031,45 +8166,42 @@ app27.get("/api/health", (c) => {
8031
8166
  ...cached?.updateAvailable ? { updateAvailable: true, latest: cached.latest } : {}
8032
8167
  });
8033
8168
  });
8034
- app27.use("/api/*", bodyLimit({ maxSize: 10 * 1024 * 1024 }));
8035
- app27.use("/api/*", csrf());
8036
- app27.use("/api/activity/*", authMiddleware);
8037
- app27.use("/api/minds/*", authMiddleware);
8038
- app27.use("/api/conversations/*", authMiddleware);
8039
- app27.use("/api/system/*", authMiddleware);
8040
- app27.use("/api/env/*", authMiddleware);
8041
- app27.use("/api/prompts/*", authMiddleware);
8042
- app27.use("/api/skills/*", authMiddleware);
8043
- app27.use("/api/extensions/*", authMiddleware);
8044
- app27.use("/api/bridges/*", authMiddleware);
8045
- app27.use("/api/config/*", authMiddleware);
8046
- app27.use("/api/v1/*", authMiddleware);
8047
- app27.route("/api/setup", setup_default);
8048
- app27.route("/public", public_files_default);
8049
- app27.route("/api/config", config_default);
8050
- var routes = app27.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/minds", minds_default).route("/api/minds", chat_default).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", shared_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/bridges", bridges_default).route("/api/extensions", extensions_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", unifiedChatApp).route("/api/v1/channels", channels_default2).route("/api/v1/history", history_default);
8051
- app27.route("/api/v1/minds", minds_default);
8052
- app27.route("/api/v1/minds", chat_default);
8053
- app27.route("/api/v1/minds", typing_default);
8054
- app27.route("/api/v1/minds", variants_default);
8055
- app27.route("/api/v1/minds", files_default);
8056
- app27.route("/api/v1/minds", env_default);
8057
- app27.route("/api/v1/minds", mind_skills_default);
8058
- app27.route("/api/v1/minds", schedules_default);
8059
- app27.route("/api/v1/minds", logs_default);
8060
- app27.route("/api/v1/system", system_default);
8061
- app27.route("/api/v1/system", update_default);
8062
- app27.route("/api/v1/prompts", prompts_default);
8063
- app27.route("/api/v1/skills", skills_default);
8064
- app27.route("/api/v1/env", sharedEnvApp);
8065
- app27.route("/api/conversations", conversations_default);
8066
- var app_default = app27;
8169
+ app25.use("/api/activity/*", authMiddleware);
8170
+ app25.use("/api/minds/*", authMiddleware);
8171
+ app25.use("/api/conversations/*", authMiddleware);
8172
+ app25.use("/api/system/*", authMiddleware);
8173
+ app25.use("/api/env/*", authMiddleware);
8174
+ app25.use("/api/prompts/*", authMiddleware);
8175
+ app25.use("/api/skills/*", authMiddleware);
8176
+ app25.use("/api/extensions/*", authMiddleware);
8177
+ app25.use("/api/bridges/*", authMiddleware);
8178
+ app25.use("/api/config/*", authMiddleware);
8179
+ app25.use("/api/v1/*", authMiddleware);
8180
+ app25.route("/api/setup", setup_default);
8181
+ app25.route("/api/config", config_default);
8182
+ var routes = app25.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/minds", minds_default).route("/api/minds", chat_default).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/bridges", bridges_default).route("/api/extensions", extensions_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", unifiedChatApp).route("/api/v1/channels", channels_default2).route("/api/v1/history", history_default);
8183
+ app25.route("/api/v1/minds", minds_default);
8184
+ app25.route("/api/v1/minds", chat_default);
8185
+ app25.route("/api/v1/minds", typing_default);
8186
+ app25.route("/api/v1/minds", variants_default);
8187
+ app25.route("/api/v1/minds", files_default);
8188
+ app25.route("/api/v1/minds", env_default);
8189
+ app25.route("/api/v1/minds", mind_skills_default);
8190
+ app25.route("/api/v1/minds", schedules_default);
8191
+ app25.route("/api/v1/minds", logs_default);
8192
+ app25.route("/api/v1/system", system_default);
8193
+ app25.route("/api/v1/system", update_default);
8194
+ app25.route("/api/v1/prompts", prompts_default);
8195
+ app25.route("/api/v1/skills", skills_default);
8196
+ app25.route("/api/v1/env", sharedEnvApp);
8197
+ app25.route("/api/conversations", conversations_default);
8198
+ var app_default = app25;
8067
8199
 
8068
- // src/web/server.ts
8069
- import { existsSync as existsSync12 } from "fs";
8070
- import { readFile as readFile3, stat as stat3 } from "fs/promises";
8200
+ // packages/daemon/src/web/server.ts
8201
+ import { existsSync as existsSync11 } from "fs";
8202
+ import { readFile as readFile2, stat as stat2 } from "fs/promises";
8071
8203
  import { createServer as createHttpsServer } from "https";
8072
- import { dirname as dirname2, extname as extname4, resolve as resolve18 } from "path";
8204
+ import { dirname as dirname2, extname as extname3, resolve as resolve16 } from "path";
8073
8205
  import { serve } from "@hono/node-server";
8074
8206
  var MIME_TYPES2 = {
8075
8207
  ".html": "text/html",
@@ -8088,8 +8220,8 @@ async function startServer({
8088
8220
  let assetsDir = "";
8089
8221
  let searchDir = dirname2(new URL(import.meta.url).pathname);
8090
8222
  for (let i = 0; i < 5; i++) {
8091
- const candidate = resolve18(searchDir, "dist", "web-assets");
8092
- if (existsSync12(candidate)) {
8223
+ const candidate = resolve16(searchDir, "dist", "web-assets");
8224
+ if (existsSync11(candidate)) {
8093
8225
  assetsDir = candidate;
8094
8226
  break;
8095
8227
  }
@@ -8099,19 +8231,19 @@ async function startServer({
8099
8231
  app_default.get("*", async (c) => {
8100
8232
  const urlPath = new URL(c.req.url).pathname;
8101
8233
  if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
8102
- const filePath = resolve18(assetsDir, urlPath.slice(1));
8234
+ const filePath = resolve16(assetsDir, urlPath.slice(1));
8103
8235
  if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
8104
- const s = await stat3(filePath).catch(() => null);
8236
+ const s = await stat2(filePath).catch(() => null);
8105
8237
  if (s?.isFile()) {
8106
- const ext = extname4(filePath);
8238
+ const ext = extname3(filePath);
8107
8239
  const mime = MIME_TYPES2[ext] || "application/octet-stream";
8108
- const body = await readFile3(filePath);
8240
+ const body = await readFile2(filePath);
8109
8241
  return c.body(body, 200, { "Content-Type": mime });
8110
8242
  }
8111
- const indexPath = resolve18(assetsDir, "index.html");
8112
- const indexStat = await stat3(indexPath).catch(() => null);
8243
+ const indexPath = resolve16(assetsDir, "index.html");
8244
+ const indexStat = await stat2(indexPath).catch(() => null);
8113
8245
  if (indexStat?.isFile()) {
8114
- const body = await readFile3(indexPath, "utf-8");
8246
+ const body = await readFile2(indexPath, "utf-8");
8115
8247
  return c.html(body);
8116
8248
  }
8117
8249
  return c.text("Not found", 404);
@@ -8125,10 +8257,10 @@ async function startServer({
8125
8257
  createServer: createHttpsServer,
8126
8258
  serverOptions: { key: tls.key, cert: tls.cert }
8127
8259
  });
8128
- await new Promise((resolve20, reject) => {
8260
+ await new Promise((resolve18, reject) => {
8129
8261
  server2.on("listening", () => {
8130
8262
  logger_default.info("Volute UI running (https)", { hostname, port });
8131
- resolve20();
8263
+ resolve18();
8132
8264
  });
8133
8265
  server2.on("error", (err) => {
8134
8266
  reject(err);
@@ -8136,13 +8268,13 @@ async function startServer({
8136
8268
  });
8137
8269
  const internalPort = port + 1;
8138
8270
  const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
8139
- await new Promise((resolve20, reject) => {
8271
+ await new Promise((resolve18, reject) => {
8140
8272
  internalServer.on("listening", () => {
8141
8273
  logger_default.info("Volute API running (http, internal)", {
8142
8274
  hostname: "127.0.0.1",
8143
8275
  port: internalPort
8144
8276
  });
8145
- resolve20();
8277
+ resolve18();
8146
8278
  });
8147
8279
  internalServer.on("error", (err) => {
8148
8280
  reject(err);
@@ -8151,10 +8283,10 @@ async function startServer({
8151
8283
  return { server: server2, internalPort };
8152
8284
  }
8153
8285
  const server = serve({ fetch: app_default.fetch, port, hostname });
8154
- await new Promise((resolve20, reject) => {
8286
+ await new Promise((resolve18, reject) => {
8155
8287
  server.on("listening", () => {
8156
8288
  logger_default.info("Volute API running (http)", { hostname, port });
8157
- resolve20();
8289
+ resolve18();
8158
8290
  });
8159
8291
  server.on("error", (err) => {
8160
8292
  reject(err);
@@ -8163,9 +8295,9 @@ async function startServer({
8163
8295
  return { server };
8164
8296
  }
8165
8297
 
8166
- // src/daemon.ts
8298
+ // packages/daemon/src/daemon.ts
8167
8299
  if (!process.env.VOLUTE_HOME) {
8168
- process.env.VOLUTE_HOME = resolve19(homedir3(), ".volute");
8300
+ process.env.VOLUTE_HOME = resolve17(homedir3(), ".volute");
8169
8301
  }
8170
8302
  if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
8171
8303
  process.env.TZ = process.env.VOLUTE_TIMEZONE;
@@ -8176,7 +8308,7 @@ async function startDaemon(opts) {
8176
8308
  const home = voluteHome();
8177
8309
  const systemDir = voluteSystemDir();
8178
8310
  if (!opts.foreground) {
8179
- const rotatingLog = new RotatingLog(resolve19(systemDir, "daemon.log"));
8311
+ const rotatingLog = new RotatingLog(resolve17(systemDir, "daemon.log"));
8180
8312
  logger_default.setOutput((line) => rotatingLog.write(`${line}
8181
8313
  `));
8182
8314
  const write = (...args) => rotatingLog.write(`${format(...args)}
@@ -8186,27 +8318,22 @@ async function startDaemon(opts) {
8186
8318
  console.warn = write;
8187
8319
  console.info = write;
8188
8320
  }
8189
- const DAEMON_PID_PATH = resolve19(systemDir, "daemon.pid");
8190
- const DAEMON_JSON_PATH = resolve19(systemDir, "daemon.json");
8321
+ const DAEMON_PID_PATH = resolve17(systemDir, "daemon.pid");
8322
+ const DAEMON_JSON_PATH = resolve17(systemDir, "daemon.json");
8191
8323
  mkdirSync10(home, { recursive: true });
8192
8324
  ensureSystemDir();
8193
- try {
8194
- await ensureSharedRepo();
8195
- } catch (err) {
8196
- logger_default.warn("failed to initialize shared repo", logger_default.errorData(err));
8197
- }
8198
- const { migrateSetupCompleted } = await import("./setup-TISPCO22.js");
8325
+ const { migrateSetupCompleted } = await import("./setup-RHJRFURI.js");
8199
8326
  migrateSetupCompleted();
8200
- await (await import("./db-F34YLV7D.js")).getDb();
8327
+ await (await import("./db-BVBJ57TU.js")).getDb();
8201
8328
  try {
8202
- const { eq: eq8, and: and6 } = await import("drizzle-orm");
8203
- const { users: users2 } = await import("./schema-PA3M5ZKH.js");
8204
- const db = await (await import("./db-F34YLV7D.js")).getDb();
8205
- await db.update(users2).set({ role: "system" }).where(and6(eq8(users2.user_type, "system"), eq8(users2.role, "user")));
8329
+ const { eq: eq7, and: and5 } = await import("drizzle-orm");
8330
+ const { users: users2 } = await import("./schema-XVZ2CLKW.js");
8331
+ const db = await (await import("./db-BVBJ57TU.js")).getDb();
8332
+ await db.update(users2).set({ role: "system" }).where(and5(eq7(users2.user_type, "system"), eq7(users2.role, "user")));
8206
8333
  } catch (err) {
8207
8334
  logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
8208
8335
  }
8209
- const { initSandbox } = await import("./sandbox-GJOK4QLQ.js");
8336
+ const { initSandbox } = await import("./sandbox-R37VIU36.js");
8210
8337
  await initSandbox();
8211
8338
  try {
8212
8339
  await syncBuiltinSkills();
@@ -8220,13 +8347,20 @@ async function startDaemon(opts) {
8220
8347
  logger_default.error("failed to load extensions", logger_default.errorData(err));
8221
8348
  }
8222
8349
  await initDefaultSkills();
8350
+ if (isAutoUpdateSkillsEnabled()) {
8351
+ try {
8352
+ await autoUpdateMindSkills();
8353
+ } catch (err) {
8354
+ logger_default.error("failed to auto-update mind skills", logger_default.errorData(err));
8355
+ }
8356
+ }
8223
8357
  try {
8224
8358
  await ensureSystemChannel();
8225
8359
  } catch (err) {
8226
8360
  logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
8227
8361
  }
8228
8362
  try {
8229
- const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-GKCDSO4T.js");
8363
+ const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-WX4TESEI.js");
8230
8364
  await getOrCreateSystemUser2();
8231
8365
  } catch (err) {
8232
8366
  logger_default.warn(
@@ -8237,7 +8371,7 @@ async function startDaemon(opts) {
8237
8371
  const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes(32).toString("hex");
8238
8372
  let tls;
8239
8373
  if (opts.tailscale) {
8240
- const { getTailscaleTls } = await import("./tailscale-XHQBZROW.js");
8374
+ const { getTailscaleTls } = await import("./tailscale-ZIZ2HWJ5.js");
8241
8375
  const tlsConfig = await getTailscaleTls();
8242
8376
  tls = { key: tlsConfig.key, cert: tlsConfig.cert };
8243
8377
  logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
@@ -8276,8 +8410,11 @@ async function startDaemon(opts) {
8276
8410
  tokenBudget.start();
8277
8411
  const sleepManager = initSleepManager();
8278
8412
  sleepManager.start();
8413
+ const summarizer = initSummarizer();
8414
+ summarizer.start();
8279
8415
  const unsubscribeWebhook = initWebhook();
8280
- await completeOrphanedTurns();
8416
+ const orphanedTurns = await completeOrphanedTurns();
8417
+ summarizeOrphanedTurns(orphanedTurns);
8281
8418
  const allMinds = await readAllMinds();
8282
8419
  const runningEntries = allMinds.filter((e) => e.running && e.mindType !== "spirit");
8283
8420
  {
@@ -8307,10 +8444,10 @@ async function startDaemon(opts) {
8307
8444
  await Promise.all(workers);
8308
8445
  }
8309
8446
  try {
8310
- const { isSetupComplete: isSetupComplete2 } = await import("./setup-TISPCO22.js");
8447
+ const { isSetupComplete: isSetupComplete2 } = await import("./setup-RHJRFURI.js");
8311
8448
  if (isSetupComplete2()) {
8312
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-VRONKFMF.js");
8313
- const { startSpiritFull } = await import("./mind-service-2MQ6UK5N.js");
8449
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-ZFRDXMG7.js");
8450
+ const { startSpiritFull } = await import("./mind-service-X2CAA6W6.js");
8314
8451
  await ensureSpiritProject();
8315
8452
  await syncSpiritTemplate();
8316
8453
  const spiritEntry = await findMind("volute");
@@ -8327,7 +8464,7 @@ async function startDaemon(opts) {
8327
8464
  bridgeManager.startBridges(daemonPort).catch((err) => {
8328
8465
  logger_default.warn("failed to start bridges", logger_default.errorData(err));
8329
8466
  });
8330
- import("./cloud-sync-4NWLMFVH.js").then(
8467
+ import("./cloud-sync-6JL4C24T.js").then(
8331
8468
  ({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
8332
8469
  logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
8333
8470
  })
@@ -8335,7 +8472,7 @@ async function startDaemon(opts) {
8335
8472
  logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
8336
8473
  });
8337
8474
  try {
8338
- const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-NBI2MTJO.js");
8475
+ const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-FXSEMXWW.js");
8339
8476
  backfillTemplateHashes();
8340
8477
  notifyVersionUpdate().catch((err) => {
8341
8478
  logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
@@ -8356,13 +8493,13 @@ async function startDaemon(opts) {
8356
8493
  logger_default.info(`running on ${hostname}:${port}, pid ${myPid}`);
8357
8494
  function cleanup() {
8358
8495
  try {
8359
- if (readFileSync12(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
8496
+ if (readFileSync11(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
8360
8497
  unlinkSync2(DAEMON_PID_PATH);
8361
8498
  }
8362
8499
  } catch {
8363
8500
  }
8364
8501
  try {
8365
- const data = JSON.parse(readFileSync12(DAEMON_JSON_PATH, "utf-8"));
8502
+ const data = JSON.parse(readFileSync11(DAEMON_JSON_PATH, "utf-8"));
8366
8503
  if (data.token === token) {
8367
8504
  unlinkSync2(DAEMON_JSON_PATH);
8368
8505
  }
@@ -8393,6 +8530,7 @@ async function startDaemon(opts) {
8393
8530
  safe("scheduler.saveState", () => scheduler.saveState());
8394
8531
  safe("mailPoller.stop", () => mailPoller.stop());
8395
8532
  safe("tokenBudget.stop", () => tokenBudget.stop());
8533
+ safe("summarizer.stop", () => summarizer.stop());
8396
8534
  safe("stopApiKeyRefresh", stopApiKeyRefresh);
8397
8535
  safe("delivery.dispose", () => delivery.dispose());
8398
8536
  await safe("bridgeManager.stopAll", () => bridgeManager.stopAll());