volute 0.29.0 → 0.30.1

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 (122) hide show
  1. package/README.md +112 -13
  2. package/dist/{accept-666DIZX2.js → accept-E3PAH3QJ.js} +2 -2
  3. package/dist/{activity-events-BBIEA2F4.js → activity-events-BKBPPUBP.js} +2 -2
  4. package/dist/ai-service-VAJT5UBS.js +29 -0
  5. package/dist/api.d.ts +351 -477
  6. package/dist/{archive-UA4BDFXQ.js → archive-WWDBWYN2.js} +2 -2
  7. package/dist/{bridge-FQHZL3MC.js → bridge-RO37CUFM.js} +2 -2
  8. package/dist/{chat-KTPOR2JT.js → chat-TCUNPFGO.js} +8 -8
  9. package/dist/{chunk-FLZGS4QH.js → chunk-2C2VXEBB.js} +2 -2
  10. package/dist/chunk-2NDZC3S7.js +1330 -0
  11. package/dist/{chunk-IKRVFPWU.js → chunk-7D47T4RB.js} +3 -2
  12. package/dist/{chunk-AW7PFDVN.js → chunk-CVH6Y2YG.js} +1 -1
  13. package/dist/{chunk-XBLSAVJF.js → chunk-DTC6EH5I.js} +1 -1
  14. package/dist/{chunk-THUUIU3E.js → chunk-EFP3PE6C.js} +5 -5
  15. package/dist/{chunk-JGFVMROS.js → chunk-EFVHR7KH.js} +1 -1
  16. package/dist/{chunk-CQ7SNKNI.js → chunk-FSM45XD5.js} +1 -1
  17. package/dist/{chunk-LAC664WU.js → chunk-FXHXHI2A.js} +42 -24
  18. package/dist/{chunk-RKQEHRBB.js → chunk-G3GBKZGG.js} +1 -1
  19. package/dist/{chunk-H7OZRFJB.js → chunk-HHTXM4JT.js} +0 -49
  20. package/dist/{chunk-J4IBNXGJ.js → chunk-IKHDUZRH.js} +4 -3
  21. package/dist/{chunk-MD4C26II.js → chunk-JGFRDMR6.js} +1 -1
  22. package/dist/{chunk-EHZKEMMV.js → chunk-LIRWLNAK.js} +24 -10
  23. package/dist/{chunk-NI5FFCCS.js → chunk-MDPCSXZ4.js} +35 -11
  24. package/dist/chunk-NSBFETWP.js +188 -0
  25. package/dist/{chunk-VIVMW2H2.js → chunk-P27RV5WM.js} +1 -1
  26. package/dist/{chunk-EHYDTZTF.js → chunk-P7VFDSSG.js} +2 -2
  27. package/dist/{chunk-CMILSHZD.js → chunk-QVAQ5454.js} +84 -300
  28. package/dist/{chunk-HDN7MNGD.js → chunk-S5LR3XYJ.js} +1 -1
  29. package/dist/{chunk-2YP2TVDT.js → chunk-UPA6COHU.js} +5 -5
  30. package/dist/{chunk-AKPFNL7L.js → chunk-VGWJSNHS.js} +1 -1
  31. package/dist/{chunk-DUAUMCEE.js → chunk-W5OOPLNP.js} +3 -3
  32. package/dist/{chunk-2WPW7OT6.js → chunk-ZWKTUQEL.js} +1 -1
  33. package/dist/cli.js +22 -26
  34. package/dist/{clock-DGCBVGYA.js → clock-G3ALCMLJ.js} +10 -6
  35. package/dist/{cloud-sync-KILFGV5Q.js → cloud-sync-JV4LJOK3.js} +13 -12
  36. package/dist/{conversations-P5BL7RMX.js → conversations-7KVQV7EZ.js} +3 -3
  37. package/dist/{create-DFCAGEE5.js → create-JTLS7GX3.js} +2 -2
  38. package/dist/{create-QWV73WXD.js → create-VQSQHJQW.js} +1 -1
  39. package/dist/{daemon-client-I42FK2BF.js → daemon-client-BCTFGVCZ.js} +2 -2
  40. package/dist/{daemon-restart-UHOMICXT.js → daemon-restart-4JGBHEJ4.js} +7 -7
  41. package/dist/daemon.js +1257 -1022
  42. package/dist/{db-IC4J52XQ.js → db-HMFPIRO2.js} +1 -1
  43. package/dist/{delete-4JYGD4VN.js → delete-JESHKE7F.js} +1 -1
  44. package/dist/down-NGBMGORS.js +14 -0
  45. package/dist/{env-YJMUMFIY.js → env-CLXXT7M2.js} +2 -2
  46. package/dist/{export-BOJQWBMA.js → export-EGA5M5PB.js} +3 -3
  47. package/dist/extension-WZ4SUPJB.js +174 -0
  48. package/dist/extensions-ECO4RPFQ.js +27 -0
  49. package/dist/{files-M546TKVN.js → files-4VEJDASH.js} +3 -3
  50. package/dist/{history-ALPTNB3I.js → history-EJMMLXDO.js} +17 -2
  51. package/dist/{import-SRTQXBGH.js → import-YCGPMBSI.js} +3 -3
  52. package/dist/{join-J4QU42DL.js → join-2GBJKZEN.js} +1 -1
  53. package/dist/{list-R73GENNL.js → list-Q6O7FGAN.js} +2 -2
  54. package/dist/{login-3QZNR2DF.js → login-RET5WESK.js} +2 -2
  55. package/dist/{login-BKP3AFWN.js → login-RL6AU2SM.js} +3 -3
  56. package/dist/{logout-T53VKCPU.js → logout-CGAGJN3L.js} +2 -2
  57. package/dist/{logout-IQK7FNEK.js → logout-JRPBEMMR.js} +3 -3
  58. package/dist/message-delivery-6YMVNOEC.js +28 -0
  59. package/dist/{migrate-registry-to-db-XC7T5B7P.js → migrate-registry-to-db-FK35IPEH.js} +1 -1
  60. package/dist/{mind-S5V6CK5W.js → mind-LUWRQUQ5.js} +17 -17
  61. package/dist/{mind-activity-tracker-WRHFI3YW.js → mind-activity-tracker-VYN2ZZ2M.js} +3 -3
  62. package/dist/{mind-list-UPJ75GPI.js → mind-list-V5WW5DUA.js} +2 -2
  63. package/dist/{mind-manager-P66HQDNE.js → mind-manager-YFCOIAAX.js} +6 -6
  64. package/dist/{mind-sleep-BTSWQNAC.js → mind-sleep-R6PTNNW4.js} +2 -2
  65. package/dist/{mind-status-TK5AETEM.js → mind-status-I4ISFJ6I.js} +2 -2
  66. package/dist/{mind-wake-SBAKIDVP.js → mind-wake-67ZQEWAV.js} +2 -2
  67. package/dist/{package-OFKXNKJF.js → package-OYUD4ZJ4.js} +12 -6
  68. package/dist/{pages-watcher-P7QECRE2.js → pages-watcher-Z3PKNROC.js} +3 -3
  69. package/dist/{read-36UFXN3G.js → read-WQMPTSN2.js} +2 -2
  70. package/dist/{register-CHREOMJ3.js → register-NZDSTLP3.js} +3 -3
  71. package/dist/{registry-NDNOOYG4.js → registry-ODSALQQL.js} +1 -1
  72. package/dist/{reject-LXIZFJ4Q.js → reject-2HZOJEIJ.js} +2 -2
  73. package/dist/{restart-6ESL3NBO.js → restart-QHS3NT64.js} +2 -2
  74. package/dist/{sandbox-5BW5HPXM.js → sandbox-O5FUSF43.js} +3 -3
  75. package/dist/{seed-SSUCYYDF.js → seed-WUQMPLDM.js} +1 -1
  76. package/dist/{send-TAOEZ4NH.js → send-OAN3RYYY.js} +20 -6
  77. package/dist/{setup-JHL5ZEST.js → setup-QMDK5RZX.js} +2 -2
  78. package/dist/{setup-RXYVGGT7.js → setup-XJH3E7YM.js} +45 -14
  79. package/dist/{skill-AUAQTSP5.js → skill-FZIN4W4Q.js} +65 -3
  80. package/dist/skills/volute-mind/SKILL.md +10 -19
  81. package/dist/sleep-manager-O7YQFCV5.js +30 -0
  82. package/dist/{split-TKJ5OT3P.js → split-EXYGGGQN.js} +1 -1
  83. package/dist/{sprout-UNT7LKKE.js → sprout-AXQ6H5DB.js} +8 -7
  84. package/dist/{start-EUJSS5R4.js → start-MTOVL6SY.js} +2 -2
  85. package/dist/{status-NQJYR4BG.js → status-ZRO37MWR.js} +5 -5
  86. package/dist/{stop-3XAITBBF.js → stop-OK5WEPVC.js} +2 -2
  87. package/dist/{systems-SMEFSHTA.js → systems-W3BBMSOZ.js} +5 -5
  88. package/dist/{tailscale-NY5MUMY3.js → tailscale-BM72RXCJ.js} +1 -1
  89. package/dist/{template-hash-BIMA4ILT.js → template-hash-3HOR4UAJ.js} +1 -1
  90. package/dist/up-BXUAIDXB.js +17 -0
  91. package/dist/{update-PTSH22AZ.js → update-PLPHMMZ2.js} +5 -5
  92. package/dist/{update-check-64FWC4Y2.js → update-check-CVCN7MF6.js} +2 -2
  93. package/dist/{upgrade-HA47CS4C.js → upgrade-I6NPCYUU.js} +1 -1
  94. package/dist/{version-notify-WDHRO3XD.js → version-notify-2NTWVEHL.js} +15 -14
  95. package/dist/web-assets/assets/index--kREqKl9.js +72 -0
  96. package/dist/web-assets/assets/index-BXYTG0nJ.css +1 -0
  97. package/dist/web-assets/ext-theme.css +111 -0
  98. package/dist/web-assets/index.html +2 -2
  99. package/package.json +12 -6
  100. package/packages/extensions/notes/dist/ui/assets/index-DgawVO5g.css +1 -0
  101. package/packages/extensions/notes/dist/ui/assets/index-qUWoeC4c.js +2 -0
  102. package/packages/extensions/notes/dist/ui/index.html +14 -0
  103. package/packages/extensions/notes/skills/notes/SKILL.md +62 -0
  104. package/packages/extensions/notes/skills/notes/scripts/notes.mjs +185 -0
  105. package/packages/extensions/pages/dist/ui/assets/index-D0HyS-xQ.css +1 -0
  106. package/packages/extensions/pages/dist/ui/assets/index-tLTROSk5.js +2 -0
  107. package/packages/extensions/pages/dist/ui/index.html +14 -0
  108. package/packages/extensions/pages/skills/pages/SKILL.md +58 -0
  109. package/templates/_base/home/VOLUTE.md +1 -1
  110. package/dist/chunk-P72MVS4R.js +0 -188
  111. package/dist/chunk-ZYGKG6VC.js +0 -22
  112. package/dist/down-LVBXEULC.js +0 -14
  113. package/dist/message-delivery-Q7VUMIEI.js +0 -27
  114. package/dist/notes-XCER3I7M.js +0 -220
  115. package/dist/pages-EUJR52AH.js +0 -36
  116. package/dist/publish-ZZB33WP4.js +0 -86
  117. package/dist/skills/notes/SKILL.md +0 -34
  118. package/dist/sleep-manager-G4B5GW5P.js +0 -29
  119. package/dist/status-S7UUPNRW.js +0 -38
  120. package/dist/up-W6VAK2XE.js +0 -17
  121. package/dist/web-assets/assets/index-BmKDnWDB.css +0 -1
  122. package/dist/web-assets/assets/index-CLJMx-GA.js +0 -71
package/dist/daemon.js CHANGED
@@ -1,62 +1,51 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- addSharedWorktree,
4
- ensureSharedRepo,
5
- removeSharedWorktree,
6
- sharedMerge
7
- } from "./chunk-P72MVS4R.js";
8
2
  import {
9
3
  announceToSystem,
10
- approveUser,
11
- changePassword,
12
- countAdmins,
13
- createUser,
14
- deleteMindUser as deleteMindUser2,
15
- deleteSystemsConfig,
16
- deleteUser,
17
4
  deliverMessage,
18
5
  ensureSystemChannel,
19
6
  extractTextContent,
20
7
  getDeliveryManager,
21
- getOrCreateMindUser,
22
8
  getScheduler,
23
9
  getSleepManagerIfReady,
24
10
  getTokenBudget,
25
11
  getTypingMap,
26
- getUser,
27
- getUserByUsername,
28
12
  initDeliveryManager,
29
13
  initMailPoller,
30
14
  initScheduler,
31
15
  initSleepManager,
32
16
  initTokenBudget,
33
17
  joinSystemChannel,
34
- listPendingUsers,
35
- listUsers,
36
- listUsersByType,
37
- migrateMindRoles,
38
18
  publish as publish2,
39
19
  publishTypingForChannels,
40
- readSystemsConfig,
41
20
  readVoluteConfig,
42
21
  recordInbound,
43
22
  resolveChannelId,
44
- setUserRole,
45
23
  splitMessage,
46
24
  startMindFull,
47
25
  stopMindFull,
48
26
  subscribe as subscribe3,
49
- updateUserProfile,
50
- verifyUser,
51
27
  writeChannelEntry,
52
- writeSystemsConfig,
53
28
  writeVoluteConfig
54
- } from "./chunk-CMILSHZD.js";
29
+ } from "./chunk-QVAQ5454.js";
55
30
  import {
56
- getCachedRecentPages,
57
- getCachedSites,
58
- stopAllWatchers
59
- } from "./chunk-THUUIU3E.js";
31
+ getActiveMinds,
32
+ onMindEvent,
33
+ stopAll
34
+ } from "./chunk-FSM45XD5.js";
35
+ import {
36
+ PROMPT_DEFAULTS,
37
+ PROMPT_KEYS,
38
+ RestartTracker,
39
+ RotatingLog,
40
+ getMindManager,
41
+ getMindPromptDefaults,
42
+ getPrompt,
43
+ getPromptIfCustom,
44
+ initMindManager,
45
+ resolveMindToken,
46
+ substitute
47
+ } from "./chunk-LIRWLNAK.js";
48
+ import "./chunk-IKHDUZRH.js";
60
49
  import {
61
50
  addMessage,
62
51
  createChannel,
@@ -83,12 +72,12 @@ import {
83
72
  markConversationRead,
84
73
  publish,
85
74
  subscribe as subscribe2
86
- } from "./chunk-FLZGS4QH.js";
75
+ } from "./chunk-2C2VXEBB.js";
87
76
  import {
88
77
  checkForUpdate,
89
78
  checkForUpdateCached,
90
79
  getCurrentVersion
91
- } from "./chunk-HDN7MNGD.js";
80
+ } from "./chunk-S5LR3XYJ.js";
92
81
  import {
93
82
  applyInitFiles,
94
83
  composeTemplate,
@@ -96,35 +85,56 @@ import {
96
85
  copyTemplateToDir,
97
86
  findTemplatesRoot,
98
87
  listFiles
99
- } from "./chunk-AKPFNL7L.js";
88
+ } from "./chunk-VGWJSNHS.js";
100
89
  import {
101
- getActiveMinds,
102
- onMindEvent,
103
- stopAll
104
- } from "./chunk-CQ7SNKNI.js";
90
+ acceptPending,
91
+ formatFileSize,
92
+ listPending,
93
+ rejectPending,
94
+ stageFile,
95
+ validateFilePath
96
+ } from "./chunk-JGFRDMR6.js";
97
+ import {
98
+ getCachedRecentPages,
99
+ getCachedSites
100
+ } from "./chunk-EFP3PE6C.js";
101
+ import {
102
+ approveUser,
103
+ changePassword,
104
+ countAdmins,
105
+ createUser,
106
+ deleteMindUser as deleteMindUser2,
107
+ deleteSystemsConfig,
108
+ deleteUser,
109
+ getExtensionStandardSkills,
110
+ getLoadedExtensions,
111
+ getOrCreateMindUser,
112
+ getUser,
113
+ getUserByUsername,
114
+ listPendingUsers,
115
+ listUsers,
116
+ listUsersByType,
117
+ loadAllExtensions,
118
+ migrateMindRoles,
119
+ notifyExtensionsDaemonStart,
120
+ notifyExtensionsDaemonStop,
121
+ readSystemsConfig,
122
+ setUserRole,
123
+ updateUserProfile,
124
+ verifyUser,
125
+ writeSystemsConfig
126
+ } from "./chunk-2NDZC3S7.js";
105
127
  import {
106
128
  broadcast,
107
129
  subscribe
108
- } from "./chunk-VIVMW2H2.js";
109
- import {
110
- PROMPT_DEFAULTS,
111
- PROMPT_KEYS,
112
- RestartTracker,
113
- RotatingLog,
114
- getMindManager,
115
- getMindPromptDefaults,
116
- getPrompt,
117
- getPromptIfCustom,
118
- initMindManager,
119
- resolveMindToken,
120
- substitute
121
- } from "./chunk-EHZKEMMV.js";
122
- import "./chunk-J4IBNXGJ.js";
130
+ } from "./chunk-P27RV5WM.js";
123
131
  import {
124
132
  SEED_SKILLS,
125
133
  STANDARD_SKILLS,
126
134
  getSharedSkill,
135
+ getStandardSkillsWithExtensions,
127
136
  importSkillFromDir,
137
+ initDefaultSkills,
128
138
  installSkill,
129
139
  listFilesRecursive,
130
140
  listMindSkills,
@@ -135,15 +145,10 @@ import {
135
145
  syncBuiltinSkills,
136
146
  uninstallSkill,
137
147
  updateSkill
138
- } from "./chunk-NI5FFCCS.js";
148
+ } from "./chunk-MDPCSXZ4.js";
139
149
  import {
140
- acceptPending,
141
- formatFileSize,
142
- listPending,
143
- rejectPending,
144
- stageFile,
145
- validateFilePath
146
- } from "./chunk-MD4C26II.js";
150
+ isHomeOnlyArchive
151
+ } from "./chunk-DTC6EH5I.js";
147
152
  import {
148
153
  findBridgeForChannel,
149
154
  findOpenClawSession,
@@ -157,35 +162,46 @@ import {
157
162
  resolveChannelMapping,
158
163
  setBridgeConfig,
159
164
  setChannelMapping
160
- } from "./chunk-2YP2TVDT.js";
165
+ } from "./chunk-UPA6COHU.js";
161
166
  import {
162
167
  loadMergedEnv,
163
168
  mindEnvPath,
164
169
  readEnv,
165
170
  sharedEnvPath,
166
171
  writeEnv
167
- } from "./chunk-2WPW7OT6.js";
172
+ } from "./chunk-ZWKTUQEL.js";
173
+ import {
174
+ aiComplete,
175
+ getAiConfig,
176
+ getAvailableModels,
177
+ getEnabledModels,
178
+ removeAiConfig,
179
+ removeProviderConfig,
180
+ saveProviderConfig,
181
+ setEnabledModels
182
+ } from "./chunk-NSBFETWP.js";
168
183
  import {
169
184
  logBuffer,
170
185
  logger_default
171
186
  } from "./chunk-YUIHSKR6.js";
172
- import {
173
- isHomeOnlyArchive
174
- } from "./chunk-XBLSAVJF.js";
175
187
  import {
176
188
  exec,
177
189
  gitExec,
178
190
  resolveVoluteBin
179
- } from "./chunk-AW7PFDVN.js";
191
+ } from "./chunk-CVH6Y2YG.js";
180
192
  import {
181
193
  chownMindDir,
182
194
  createMindUser,
183
195
  deleteMindUser,
184
196
  ensureVoluteGroup,
185
197
  isIsolationEnabled,
198
+ mindUserName,
186
199
  wrapForIsolation
187
- } from "./chunk-RKQEHRBB.js";
188
- import "./chunk-IKRVFPWU.js";
200
+ } from "./chunk-G3GBKZGG.js";
201
+ import {
202
+ readGlobalConfig,
203
+ writeGlobalConfig
204
+ } from "./chunk-7D47T4RB.js";
189
205
  import "./chunk-D424ZQGI.js";
190
206
  import {
191
207
  buildVoluteSlug,
@@ -206,9 +222,6 @@ import {
206
222
  mindDir,
207
223
  mindHistory,
208
224
  nextPort,
209
- noteComments,
210
- noteReactions,
211
- notes,
212
225
  readAllMinds,
213
226
  readRegistry,
214
227
  removeMind,
@@ -222,14 +235,14 @@ import {
222
235
  validateMindName,
223
236
  voluteHome,
224
237
  voluteSystemDir
225
- } from "./chunk-H7OZRFJB.js";
238
+ } from "./chunk-HHTXM4JT.js";
226
239
  import {
227
240
  __export
228
241
  } from "./chunk-K3NQKI34.js";
229
242
 
230
243
  // src/daemon.ts
231
244
  import { randomBytes } from "crypto";
232
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync13, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
245
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync14, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
233
246
  import { homedir as homedir2 } from "os";
234
247
  import { resolve as resolve22 } from "path";
235
248
  import { format } from "util";
@@ -818,6 +831,259 @@ function migrateToSystemDir() {
818
831
  }
819
832
  }
820
833
 
834
+ // src/lib/shared.ts
835
+ import { execFileSync as execFileSync2 } from "child_process";
836
+ import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
837
+ import { resolve as resolve6 } from "path";
838
+ function readWorktreeGitDir(worktreePath) {
839
+ const dotGit = resolve6(worktreePath, ".git");
840
+ if (!existsSync6(dotGit)) return null;
841
+ try {
842
+ const content = readFileSync4(dotGit, "utf-8").trim();
843
+ const match = content.match(/^gitdir:\s*(.+)$/);
844
+ return match ? match[1] : null;
845
+ } catch {
846
+ return null;
847
+ }
848
+ }
849
+ function sharedDir() {
850
+ return resolve6(voluteHome(), "shared");
851
+ }
852
+ async function ensureSharedRepo() {
853
+ const dir = sharedDir();
854
+ mkdirSync4(dir, { recursive: true });
855
+ if (existsSync6(resolve6(dir, ".git"))) {
856
+ try {
857
+ await gitExec(["rev-parse", "HEAD"], { cwd: dir });
858
+ return;
859
+ } catch (err) {
860
+ const msg = err instanceof Error ? err.message : String(err);
861
+ if (msg.includes("unknown revision") || msg.includes("bad default revision")) {
862
+ logger_default.warn("shared repo has no commits, re-initializing");
863
+ rmSync2(resolve6(dir, ".git"), { recursive: true, force: true });
864
+ } else {
865
+ throw err;
866
+ }
867
+ }
868
+ }
869
+ const initArgs = isIsolationEnabled() ? ["init", "--shared=group"] : ["init"];
870
+ await gitExec(initArgs, { cwd: dir });
871
+ await gitExec(["checkout", "-b", "main"], { cwd: dir });
872
+ const pagesDir = resolve6(dir, "pages");
873
+ mkdirSync4(pagesDir, { recursive: true });
874
+ writeFileSync3(resolve6(pagesDir, ".gitkeep"), "");
875
+ await gitExec(["add", "-A"], { cwd: dir });
876
+ await gitExec(["commit", "-m", "init shared repo"], { cwd: dir });
877
+ if (isIsolationEnabled()) {
878
+ try {
879
+ execFileSync2("chgrp", ["-R", "volute", dir], { stdio: "ignore" });
880
+ } catch (err) {
881
+ logger_default.warn("failed to chgrp shared repo to volute group", logger_default.errorData(err));
882
+ }
883
+ chmodSync(dir, 1533);
884
+ }
885
+ }
886
+ async function addSharedWorktree(mindName, mindDir2) {
887
+ const dir = sharedDir();
888
+ if (!existsSync6(resolve6(dir, ".git"))) return;
889
+ const worktreePath = resolve6(mindDir2, "home", "shared");
890
+ if (existsSync6(worktreePath)) return;
891
+ let branchExists = false;
892
+ try {
893
+ await gitExec(["rev-parse", "--verify", mindName], { cwd: dir });
894
+ branchExists = true;
895
+ } catch {
896
+ }
897
+ if (branchExists) {
898
+ await gitExec(["worktree", "add", worktreePath, mindName], { cwd: dir });
899
+ } else {
900
+ await gitExec(["worktree", "add", "-b", mindName, worktreePath], { cwd: dir });
901
+ }
902
+ if (isIsolationEnabled()) {
903
+ const worktreeGitDir = readWorktreeGitDir(worktreePath);
904
+ if (worktreeGitDir) {
905
+ try {
906
+ const user = mindUserName(mindName);
907
+ execFileSync2("chown", ["-R", `${user}:volute`, worktreeGitDir], { stdio: "ignore" });
908
+ } catch (err) {
909
+ logger_default.warn(`failed to chown worktree git dir for ${mindName}`, logger_default.errorData(err));
910
+ }
911
+ }
912
+ }
913
+ }
914
+ async function removeSharedWorktree(mindName, mindDir2) {
915
+ const dir = sharedDir();
916
+ if (!existsSync6(resolve6(dir, ".git"))) return;
917
+ const worktreePath = resolve6(mindDir2, "home", "shared");
918
+ if (existsSync6(worktreePath)) {
919
+ try {
920
+ await gitExec(["worktree", "remove", "--force", worktreePath], { cwd: dir });
921
+ } catch (err) {
922
+ logger_default.debug(`worktree remove failed for ${mindName}`, logger_default.errorData(err));
923
+ }
924
+ }
925
+ try {
926
+ await gitExec(["worktree", "prune"], { cwd: dir });
927
+ } catch (err) {
928
+ logger_default.debug(`worktree prune failed for ${mindName}`, logger_default.errorData(err));
929
+ }
930
+ try {
931
+ await gitExec(["branch", "-D", mindName], { cwd: dir });
932
+ } catch {
933
+ }
934
+ }
935
+ var sharedLock = Promise.resolve();
936
+ function rechownWorktree(worktreePath, mindName) {
937
+ if (!isIsolationEnabled()) return;
938
+ try {
939
+ const user = mindUserName(mindName);
940
+ execFileSync2("chown", ["-R", `${user}:volute`, worktreePath], { stdio: "ignore" });
941
+ } catch (err) {
942
+ logger_default.warn(`failed to rechown worktree for ${mindName}`, logger_default.errorData(err));
943
+ }
944
+ }
945
+ async function withSharedLock(fn) {
946
+ const prev = sharedLock;
947
+ let resolve_;
948
+ sharedLock = new Promise((r) => {
949
+ resolve_ = r;
950
+ });
951
+ await prev;
952
+ try {
953
+ return await fn();
954
+ } finally {
955
+ resolve_();
956
+ }
957
+ }
958
+ async function sharedMerge(mindName, mindDir2, message) {
959
+ return withSharedLock(async () => {
960
+ const dir = sharedDir();
961
+ const worktreePath = resolve6(mindDir2, "home", "shared");
962
+ const status = (await gitExec(["status", "--porcelain"], { cwd: worktreePath })).trim();
963
+ if (status) {
964
+ await gitExec(["add", "-A"], { cwd: worktreePath });
965
+ await gitExec(
966
+ ["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", `wip: ${mindName}`],
967
+ { cwd: worktreePath }
968
+ );
969
+ }
970
+ const diff = (await gitExec(["diff", `main...${mindName}`, "--stat"], { cwd: dir })).trim();
971
+ if (!diff) {
972
+ return { ok: true, message: "Nothing to merge" };
973
+ }
974
+ try {
975
+ await gitExec(["merge", "--squash", mindName], { cwd: dir });
976
+ } catch {
977
+ try {
978
+ await gitExec(["reset", "--hard", "HEAD"], { cwd: dir });
979
+ } catch (resetErr) {
980
+ logger_default.error("reset after squash conflict failed in shared repo", logger_default.errorData(resetErr));
981
+ }
982
+ return { ok: false, conflicts: true, message: "Merge conflicts detected" };
983
+ }
984
+ await gitExec(["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", message], {
985
+ cwd: dir
986
+ });
987
+ try {
988
+ await gitExec(["reset", "--hard", "main"], { cwd: worktreePath });
989
+ } catch {
990
+ return {
991
+ ok: true,
992
+ message: "Merged to main, but branch reset failed \u2014 run 'volute shared pull' to sync"
993
+ };
994
+ }
995
+ rechownWorktree(worktreePath, mindName);
996
+ return { ok: true };
997
+ });
998
+ }
999
+
1000
+ // src/web/app.ts
1001
+ import { Hono as Hono29 } from "hono";
1002
+ import { bodyLimit } from "hono/body-limit";
1003
+ import { csrf } from "hono/csrf";
1004
+ import { HTTPException } from "hono/http-exception";
1005
+
1006
+ // src/web/api/activity.ts
1007
+ import { desc } from "drizzle-orm";
1008
+ import { Hono } from "hono";
1009
+ import { streamSSE } from "hono/streaming";
1010
+ var app = new Hono().get("/events", async (c) => {
1011
+ const user = c.get("user");
1012
+ return streamSSE(c, async (stream) => {
1013
+ const cleanups = [];
1014
+ try {
1015
+ let recentActivity = [];
1016
+ try {
1017
+ const db = await getDb();
1018
+ recentActivity = await db.select().from(activity).orderBy(desc(activity.created_at)).limit(50);
1019
+ recentActivity = recentActivity.map((row) => ({
1020
+ ...row,
1021
+ metadata: row.metadata ? JSON.parse(row.metadata) : null
1022
+ }));
1023
+ } catch (err) {
1024
+ logger_default.error("[activity-sse] failed to fetch recent activity", logger_default.errorData(err));
1025
+ }
1026
+ let conversations2 = [];
1027
+ try {
1028
+ conversations2 = await listConversationsWithParticipants(user.id);
1029
+ } catch (err) {
1030
+ logger_default.error("[activity-sse] failed to fetch conversations", logger_default.errorData(err));
1031
+ }
1032
+ await stream.writeSSE({
1033
+ data: JSON.stringify({
1034
+ event: "snapshot",
1035
+ activity: recentActivity,
1036
+ conversations: conversations2,
1037
+ activeMinds: getActiveMinds()
1038
+ })
1039
+ });
1040
+ const unsubActivity = subscribe((event) => {
1041
+ stream.writeSSE({
1042
+ data: JSON.stringify({ event: "activity", ...event })
1043
+ }).catch((err) => {
1044
+ if (!stream.aborted) logger_default.error("[activity-sse] write error:", logger_default.errorData(err));
1045
+ });
1046
+ });
1047
+ cleanups.push(unsubActivity);
1048
+ for (const conv of conversations2) {
1049
+ const unsubConv = subscribe2(conv.id, (event) => {
1050
+ stream.writeSSE({
1051
+ data: JSON.stringify({ event: "conversation", conversationId: conv.id, ...event })
1052
+ }).catch((err) => {
1053
+ if (!stream.aborted) logger_default.error("[activity-sse] write error:", logger_default.errorData(err));
1054
+ });
1055
+ });
1056
+ cleanups.push(unsubConv);
1057
+ }
1058
+ const keepAlive = setInterval(() => {
1059
+ stream.writeSSE({ data: "" }).catch((err) => {
1060
+ if (!stream.aborted) logger_default.error("[activity-sse] ping error:", logger_default.errorData(err));
1061
+ });
1062
+ }, 15e3);
1063
+ cleanups.push(() => clearInterval(keepAlive));
1064
+ await new Promise((resolve23) => {
1065
+ stream.onAbort(() => resolve23());
1066
+ });
1067
+ } finally {
1068
+ for (const cleanup of cleanups) {
1069
+ try {
1070
+ cleanup();
1071
+ } catch {
1072
+ }
1073
+ }
1074
+ }
1075
+ });
1076
+ });
1077
+ var activity_default = app;
1078
+
1079
+ // src/web/api/auth.ts
1080
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
1081
+ import { extname, resolve as resolve7 } from "path";
1082
+ import { zValidator } from "@hono/zod-validator";
1083
+ import { Hono as Hono2 } from "hono";
1084
+ import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
1085
+ import { z } from "zod";
1086
+
821
1087
  // src/web/middleware/auth.ts
822
1088
  import { timingSafeEqual } from "crypto";
823
1089
  import { eq as eq2, lt as lt2 } from "drizzle-orm";
@@ -939,103 +1205,7 @@ var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
939
1205
  await next();
940
1206
  });
941
1207
 
942
- // src/web/server.ts
943
- import { existsSync as existsSync16 } from "fs";
944
- import { readFile as readFile4, stat as stat4 } from "fs/promises";
945
- import { createServer as createHttpsServer } from "https";
946
- import { dirname as dirname2, extname as extname5, resolve as resolve21 } from "path";
947
- import { serve } from "@hono/node-server";
948
-
949
- // src/web/app.ts
950
- import { Hono as Hono30 } from "hono";
951
- import { bodyLimit } from "hono/body-limit";
952
- import { csrf } from "hono/csrf";
953
- import { HTTPException } from "hono/http-exception";
954
-
955
- // src/web/api/activity.ts
956
- import { desc } from "drizzle-orm";
957
- import { Hono } from "hono";
958
- import { streamSSE } from "hono/streaming";
959
- var app = new Hono().get("/events", async (c) => {
960
- const user = c.get("user");
961
- return streamSSE(c, async (stream) => {
962
- const cleanups = [];
963
- try {
964
- let recentActivity = [];
965
- try {
966
- const db = await getDb();
967
- recentActivity = await db.select().from(activity).orderBy(desc(activity.created_at)).limit(50);
968
- recentActivity = recentActivity.map((row) => ({
969
- ...row,
970
- metadata: row.metadata ? JSON.parse(row.metadata) : null
971
- }));
972
- } catch (err) {
973
- logger_default.error("[activity-sse] failed to fetch recent activity", logger_default.errorData(err));
974
- }
975
- let conversations2 = [];
976
- try {
977
- conversations2 = await listConversationsWithParticipants(user.id);
978
- } catch (err) {
979
- logger_default.error("[activity-sse] failed to fetch conversations", logger_default.errorData(err));
980
- }
981
- const sites = getCachedSites();
982
- const recentPages = getCachedRecentPages();
983
- await stream.writeSSE({
984
- data: JSON.stringify({
985
- event: "snapshot",
986
- activity: recentActivity,
987
- conversations: conversations2,
988
- sites,
989
- recentPages,
990
- activeMinds: getActiveMinds()
991
- })
992
- });
993
- const unsubActivity = subscribe((event) => {
994
- stream.writeSSE({
995
- data: JSON.stringify({ event: "activity", ...event })
996
- }).catch((err) => {
997
- if (!stream.aborted) logger_default.error("[activity-sse] write error:", logger_default.errorData(err));
998
- });
999
- });
1000
- cleanups.push(unsubActivity);
1001
- for (const conv of conversations2) {
1002
- const unsubConv = subscribe2(conv.id, (event) => {
1003
- stream.writeSSE({
1004
- data: JSON.stringify({ event: "conversation", conversationId: conv.id, ...event })
1005
- }).catch((err) => {
1006
- if (!stream.aborted) logger_default.error("[activity-sse] write error:", logger_default.errorData(err));
1007
- });
1008
- });
1009
- cleanups.push(unsubConv);
1010
- }
1011
- const keepAlive = setInterval(() => {
1012
- stream.writeSSE({ data: "" }).catch((err) => {
1013
- if (!stream.aborted) logger_default.error("[activity-sse] ping error:", logger_default.errorData(err));
1014
- });
1015
- }, 15e3);
1016
- cleanups.push(() => clearInterval(keepAlive));
1017
- await new Promise((resolve23) => {
1018
- stream.onAbort(() => resolve23());
1019
- });
1020
- } finally {
1021
- for (const cleanup of cleanups) {
1022
- try {
1023
- cleanup();
1024
- } catch {
1025
- }
1026
- }
1027
- }
1028
- });
1029
- });
1030
- var activity_default = app;
1031
-
1032
1208
  // src/web/api/auth.ts
1033
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
1034
- import { extname, resolve as resolve6 } from "path";
1035
- import { zValidator } from "@hono/zod-validator";
1036
- import { Hono as Hono2 } from "hono";
1037
- import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
1038
- import { z } from "zod";
1039
1209
  function tryJoinSystem(userId) {
1040
1210
  if (!process.env.VOLUTE_DAEMON_TOKEN) return;
1041
1211
  joinSystemChannel(userId).catch(() => {
@@ -1068,7 +1238,7 @@ var AVATAR_MIME = {
1068
1238
  };
1069
1239
  var MAX_AVATAR_SIZE = 2 * 1024 * 1024;
1070
1240
  function avatarsDir() {
1071
- return resolve6(voluteHome(), "avatars");
1241
+ return resolve7(voluteHome(), "avatars");
1072
1242
  }
1073
1243
  var authenticated = new Hono2().use(authMiddleware).post("/change-password", zValidator("json", changePasswordSchema), async (c) => {
1074
1244
  const user = c.get("user");
@@ -1103,13 +1273,13 @@ var authenticated = new Hono2().use(authMiddleware).post("/change-password", zVa
1103
1273
  return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
1104
1274
  }
1105
1275
  const dir = avatarsDir();
1106
- mkdirSync4(dir, { recursive: true });
1276
+ mkdirSync5(dir, { recursive: true });
1107
1277
  const filename = `avatar-${user.id}${ext}`;
1108
1278
  const buffer2 = Buffer.from(await file.arrayBuffer());
1109
- writeFileSync3(resolve6(dir, filename), buffer2);
1279
+ writeFileSync4(resolve7(dir, filename), buffer2);
1110
1280
  if (user.avatar && user.avatar !== filename) {
1111
- const oldPath = resolve6(dir, user.avatar);
1112
- rmSync2(oldPath, { force: true });
1281
+ const oldPath = resolve7(dir, user.avatar);
1282
+ rmSync3(oldPath, { force: true });
1113
1283
  }
1114
1284
  await updateUserProfile(user.id, { avatar: filename });
1115
1285
  const sessionId = getCookie2(c, "volute_session");
@@ -1123,8 +1293,8 @@ var authenticated = new Hono2().use(authMiddleware).post("/change-password", zVa
1123
1293
  }).delete("/avatar", async (c) => {
1124
1294
  const user = c.get("user");
1125
1295
  if (user.avatar) {
1126
- const path = resolve6(avatarsDir(), user.avatar);
1127
- rmSync2(path, { force: true });
1296
+ const path = resolve7(avatarsDir(), user.avatar);
1297
+ rmSync3(path, { force: true });
1128
1298
  }
1129
1299
  await updateUserProfile(user.id, { avatar: null });
1130
1300
  const sessionId = getCookie2(c, "volute_session");
@@ -1273,13 +1443,13 @@ var app2 = new Hono2().post("/register", zValidator("json", credentialsSchema),
1273
1443
  return c.json({ error: "Invalid filename" }, 400);
1274
1444
  }
1275
1445
  const dir = avatarsDir();
1276
- const filePath = resolve6(dir, filename);
1446
+ const filePath = resolve7(dir, filename);
1277
1447
  if (!filePath.startsWith(`${dir}/`)) return c.json({ error: "Invalid path" }, 400);
1278
- if (!existsSync6(filePath)) return c.json({ error: "Not found" }, 404);
1448
+ if (!existsSync7(filePath)) return c.json({ error: "Not found" }, 404);
1279
1449
  const ext = extname(filename).toLowerCase();
1280
1450
  const mime = AVATAR_MIME[ext];
1281
1451
  if (!mime) return c.json({ error: "Invalid file type" }, 400);
1282
- const data = readFileSync4(filePath);
1452
+ const data = readFileSync5(filePath);
1283
1453
  return c.body(data, 200, {
1284
1454
  "Content-Type": mime,
1285
1455
  "Cache-Control": "public, max-age=3600",
@@ -1414,7 +1584,7 @@ var app3 = new Hono3().post("/:platform/inbound", zValidator2("json", inboundSch
1414
1584
  }
1415
1585
  const participants = await getParticipants(channel.id);
1416
1586
  if (!participants.some((p) => p.userId === puppet.id)) {
1417
- const { addParticipant } = await import("./conversations-P5BL7RMX.js");
1587
+ const { addParticipant } = await import("./conversations-7KVQV7EZ.js");
1418
1588
  await addParticipant(channel.id, puppet.id);
1419
1589
  }
1420
1590
  const contentBlocks = body.content;
@@ -1469,7 +1639,7 @@ var app3 = new Hono3().post("/:platform/inbound", zValidator2("json", inboundSch
1469
1639
  });
1470
1640
  try {
1471
1641
  const daemonPort = parseInt(process.env.VOLUTE_DAEMON_PORT ?? "", 10);
1472
- if (isNaN(daemonPort)) {
1642
+ if (Number.isNaN(daemonPort)) {
1473
1643
  return c.json({ error: "VOLUTE_DAEMON_PORT not available" }, 500);
1474
1644
  }
1475
1645
  await manager.startBridge(platform, daemonPort);
@@ -1507,8 +1677,8 @@ async function fanOutToBridgedMinds(opts) {
1507
1677
  const participants = await getParticipants(opts.conversationId);
1508
1678
  const mindParticipants = participants.filter((p) => p.userType === "mind");
1509
1679
  const participantNames = participants.map((p) => p.username);
1510
- const { getMindManager: getMindManager2 } = await import("./mind-manager-P66HQDNE.js");
1511
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
1680
+ const { getMindManager: getMindManager2 } = await import("./mind-manager-YFCOIAAX.js");
1681
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
1512
1682
  const manager = getMindManager2();
1513
1683
  const sm = getSleepManagerIfReady2();
1514
1684
  const targetMinds = mindParticipants.filter((ap) => {
@@ -2010,18 +2180,18 @@ __export(volute_exports, {
2010
2180
  read: () => read4,
2011
2181
  send: () => send4
2012
2182
  });
2013
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
2014
- import { resolve as resolve7 } from "path";
2183
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
2184
+ import { resolve as resolve8 } from "path";
2015
2185
  function getDaemonConfig() {
2016
- const newPath = resolve7(voluteSystemDir(), "daemon.json");
2017
- const legacyPath = resolve7(voluteHome(), "daemon.json");
2018
- const configPath = existsSync7(newPath) ? newPath : legacyPath;
2019
- if (!existsSync7(configPath)) {
2186
+ const newPath = resolve8(voluteSystemDir(), "daemon.json");
2187
+ const legacyPath = resolve8(voluteHome(), "daemon.json");
2188
+ const configPath = existsSync8(newPath) ? newPath : legacyPath;
2189
+ if (!existsSync8(configPath)) {
2020
2190
  throw new Error("Volute daemon is not running");
2021
2191
  }
2022
2192
  let config;
2023
2193
  try {
2024
- config = JSON.parse(readFileSync5(configPath, "utf-8"));
2194
+ config = JSON.parse(readFileSync6(configPath, "utf-8"));
2025
2195
  } catch (err) {
2026
2196
  throw new Error(`Failed to parse ${configPath}: ${err}`);
2027
2197
  }
@@ -2378,10 +2548,17 @@ var sharedEnvApp = new Hono5().get("/", (c) => {
2378
2548
  });
2379
2549
  var env_default = app5;
2380
2550
 
2381
- // src/web/api/file-sharing.ts
2382
- import { readFileSync as readFileSync6, statSync } from "fs";
2383
- import { resolve as resolve8 } from "path";
2551
+ // src/web/api/extensions.ts
2384
2552
  import { Hono as Hono6 } from "hono";
2553
+ var app6 = new Hono6().get("/", (c) => {
2554
+ return c.json(getLoadedExtensions());
2555
+ });
2556
+ var extensions_default = app6;
2557
+
2558
+ // src/web/api/file-sharing.ts
2559
+ import { readFileSync as readFileSync7, statSync } from "fs";
2560
+ import { resolve as resolve9 } from "path";
2561
+ import { Hono as Hono7 } from "hono";
2385
2562
  async function notifyMind(port, message, channel, sender) {
2386
2563
  try {
2387
2564
  const res = await fetch(`http://127.0.0.1:${port}/message`, {
@@ -2400,7 +2577,7 @@ async function notifyMind(port, message, channel, sender) {
2400
2577
  console.warn(`[file-sharing] notify mind on port ${port} failed:`, err);
2401
2578
  }
2402
2579
  }
2403
- var app6 = new Hono6().post("/:name/files/send", requireSelf(), async (c) => {
2580
+ var app7 = new Hono7().post("/:name/files/send", requireSelf(), async (c) => {
2404
2581
  const senderName = c.req.param("name");
2405
2582
  const senderEntry = await findMind(senderName);
2406
2583
  if (!senderEntry) return c.json({ error: "Sender mind not found" }, 404);
@@ -2413,21 +2590,21 @@ var app6 = new Hono6().post("/:name/files/send", requireSelf(), async (c) => {
2413
2590
  const pathErr = validateFilePath(body.filePath);
2414
2591
  if (pathErr) return c.json({ error: pathErr }, 400);
2415
2592
  const senderDir = mindDir(senderName);
2416
- const filePath = resolve8(senderDir, "home", body.filePath);
2593
+ const filePath = resolve9(senderDir, "home", body.filePath);
2417
2594
  const MAX_FILE_SIZE2 = 50 * 1024 * 1024;
2418
- const stat5 = statSync(filePath, { throwIfNoEntry: false });
2419
- if (!stat5) return c.json({ error: `File not found: ${body.filePath}` }, 404);
2420
- if (stat5.size > MAX_FILE_SIZE2) {
2595
+ const stat4 = statSync(filePath, { throwIfNoEntry: false });
2596
+ if (!stat4) return c.json({ error: `File not found: ${body.filePath}` }, 404);
2597
+ if (stat4.size > MAX_FILE_SIZE2) {
2421
2598
  return c.json(
2422
2599
  {
2423
- error: `File too large (${formatFileSize(stat5.size)}, max ${formatFileSize(MAX_FILE_SIZE2)})`
2600
+ error: `File too large (${formatFileSize(stat4.size)}, max ${formatFileSize(MAX_FILE_SIZE2)})`
2424
2601
  },
2425
2602
  413
2426
2603
  );
2427
2604
  }
2428
2605
  let content;
2429
2606
  try {
2430
- content = readFileSync6(filePath);
2607
+ content = readFileSync7(filePath);
2431
2608
  } catch (err) {
2432
2609
  const code = err.code;
2433
2610
  if (code === "ENOENT") {
@@ -2538,13 +2715,13 @@ var app6 = new Hono6().post("/:name/files/send", requireSelf(), async (c) => {
2538
2715
  }
2539
2716
  return c.json({ status: "pending", id }, 200);
2540
2717
  });
2541
- var file_sharing_default = app6;
2718
+ var file_sharing_default = app7;
2542
2719
 
2543
2720
  // src/web/api/files.ts
2544
- import { existsSync as existsSync8 } from "fs";
2721
+ import { existsSync as existsSync9 } from "fs";
2545
2722
  import { readdir, readFile, realpath, stat } from "fs/promises";
2546
- import { extname as extname2, resolve as resolve9 } from "path";
2547
- import { Hono as Hono7 } from "hono";
2723
+ import { extname as extname2, resolve as resolve10 } from "path";
2724
+ import { Hono as Hono8 } from "hono";
2548
2725
  var ALLOWED_FILES = /* @__PURE__ */ new Set(["SOUL.md", "MEMORY.md", "CLAUDE.md", "VOLUTE.md"]);
2549
2726
  var AVATAR_MIME2 = {
2550
2727
  ".png": "image/png",
@@ -2554,7 +2731,7 @@ var AVATAR_MIME2 = {
2554
2731
  ".webp": "image/webp"
2555
2732
  };
2556
2733
  var MAX_AVATAR_SIZE2 = 2 * 1024 * 1024;
2557
- var app7 = new Hono7().get("/:name/avatar", async (c) => {
2734
+ var app8 = new Hono8().get("/:name/avatar", async (c) => {
2558
2735
  const name = c.req.param("name");
2559
2736
  const entry = await findMind(name);
2560
2737
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -2564,8 +2741,8 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
2564
2741
  const ext = extname2(config.profile.avatar).toLowerCase();
2565
2742
  const mime = AVATAR_MIME2[ext];
2566
2743
  if (!mime) return c.json({ error: "Invalid avatar extension" }, 400);
2567
- const homeDir = resolve9(dir, "home");
2568
- const avatarPath = resolve9(homeDir, config.profile.avatar);
2744
+ const homeDir = resolve10(dir, "home");
2745
+ const avatarPath = resolve10(homeDir, config.profile.avatar);
2569
2746
  if (!avatarPath.startsWith(`${homeDir}/`)) return c.json({ error: "Invalid avatar path" }, 400);
2570
2747
  let realAvatarPath;
2571
2748
  try {
@@ -2594,8 +2771,8 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
2594
2771
  const entry = await findMind(name);
2595
2772
  if (!entry) return c.json({ error: "Mind not found" }, 404);
2596
2773
  const dir = mindDir(name);
2597
- const homeDir = resolve9(dir, "home");
2598
- if (!existsSync8(homeDir)) return c.json({ error: "Home directory missing" }, 404);
2774
+ const homeDir = resolve10(dir, "home");
2775
+ if (!existsSync9(homeDir)) return c.json({ error: "Home directory missing" }, 404);
2599
2776
  const allFiles = await readdir(homeDir);
2600
2777
  const files = allFiles.filter((f) => f.endsWith(".md") && ALLOWED_FILES.has(f));
2601
2778
  return c.json(files);
@@ -2608,33 +2785,33 @@ var app7 = new Hono7().get("/:name/avatar", async (c) => {
2608
2785
  const entry = await findMind(name);
2609
2786
  if (!entry) return c.json({ error: "Mind not found" }, 404);
2610
2787
  const dir = mindDir(name);
2611
- const filePath = resolve9(dir, "home", filename);
2612
- if (!existsSync8(filePath)) {
2788
+ const filePath = resolve10(dir, "home", filename);
2789
+ if (!existsSync9(filePath)) {
2613
2790
  return c.json({ error: "File not found" }, 404);
2614
2791
  }
2615
2792
  const content = await readFile(filePath, "utf-8");
2616
2793
  return c.json({ filename, content });
2617
2794
  });
2618
- var files_default = app7;
2795
+ var files_default = app8;
2619
2796
 
2620
2797
  // src/web/api/keys.ts
2621
- import { Hono as Hono8 } from "hono";
2798
+ import { Hono as Hono9 } from "hono";
2622
2799
 
2623
2800
  // src/lib/identity.ts
2624
2801
  import { createHash, generateKeyPairSync, sign, verify } from "crypto";
2625
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
2626
- import { resolve as resolve10 } from "path";
2802
+ import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
2803
+ import { resolve as resolve11 } from "path";
2627
2804
  function generateIdentity(mindDir2) {
2628
- const identityDir = resolve10(mindDir2, ".mind/identity");
2629
- mkdirSync5(identityDir, { recursive: true });
2805
+ const identityDir = resolve11(mindDir2, ".mind/identity");
2806
+ mkdirSync6(identityDir, { recursive: true });
2630
2807
  const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
2631
2808
  publicKeyEncoding: { type: "spki", format: "pem" },
2632
2809
  privateKeyEncoding: { type: "pkcs8", format: "pem" }
2633
2810
  });
2634
- const privatePath = resolve10(identityDir, "private.pem");
2635
- const publicPath = resolve10(identityDir, "public.pem");
2636
- writeFileSync4(privatePath, privateKey, { mode: 384 });
2637
- writeFileSync4(publicPath, publicKey, { mode: 420 });
2811
+ const privatePath = resolve11(identityDir, "private.pem");
2812
+ const publicPath = resolve11(identityDir, "public.pem");
2813
+ writeFileSync5(privatePath, privateKey, { mode: 384 });
2814
+ writeFileSync5(publicPath, publicKey, { mode: 420 });
2638
2815
  const config = readVoluteConfig(mindDir2) ?? {};
2639
2816
  config.identity = {
2640
2817
  privateKey: ".mind/identity/private.pem",
@@ -2647,17 +2824,17 @@ function getPrivateKey(mindDir2) {
2647
2824
  const config = readVoluteConfig(mindDir2);
2648
2825
  const relPath = config?.identity?.privateKey;
2649
2826
  if (!relPath) return null;
2650
- const fullPath = resolve10(mindDir2, relPath);
2651
- if (!existsSync9(fullPath)) return null;
2652
- return readFileSync7(fullPath, "utf-8");
2827
+ const fullPath = resolve11(mindDir2, relPath);
2828
+ if (!existsSync10(fullPath)) return null;
2829
+ return readFileSync8(fullPath, "utf-8");
2653
2830
  }
2654
2831
  function getPublicKey(mindDir2) {
2655
2832
  const config = readVoluteConfig(mindDir2);
2656
2833
  const relPath = config?.identity?.publicKey;
2657
2834
  if (!relPath) return null;
2658
- const fullPath = resolve10(mindDir2, relPath);
2659
- if (!existsSync9(fullPath)) return null;
2660
- return readFileSync7(fullPath, "utf-8");
2835
+ const fullPath = resolve11(mindDir2, relPath);
2836
+ if (!existsSync10(fullPath)) return null;
2837
+ return readFileSync8(fullPath, "utf-8");
2661
2838
  }
2662
2839
  function getFingerprint(publicKeyPem) {
2663
2840
  return createHash("sha256").update(publicKeyPem).digest("hex");
@@ -2692,7 +2869,7 @@ async function publishPublicKey(mindName, publicKeyPem) {
2692
2869
  }
2693
2870
 
2694
2871
  // src/web/api/keys.ts
2695
- var app8 = new Hono8().get("/:fingerprint", async (c) => {
2872
+ var app9 = new Hono9().get("/:fingerprint", async (c) => {
2696
2873
  const fingerprint = c.req.param("fingerprint");
2697
2874
  for (const entry of await readRegistry()) {
2698
2875
  try {
@@ -2706,20 +2883,20 @@ var app8 = new Hono8().get("/:fingerprint", async (c) => {
2706
2883
  }
2707
2884
  return c.json({ error: "Key not found" }, 404);
2708
2885
  });
2709
- var keys_default = app8;
2886
+ var keys_default = app9;
2710
2887
 
2711
2888
  // src/web/api/logs.ts
2712
2889
  import { spawn as spawn2 } from "child_process";
2713
- import { existsSync as existsSync10 } from "fs";
2714
- import { resolve as resolve11 } from "path";
2715
- import { Hono as Hono9 } from "hono";
2890
+ import { existsSync as existsSync11 } from "fs";
2891
+ import { resolve as resolve12 } from "path";
2892
+ import { Hono as Hono10 } from "hono";
2716
2893
  import { streamSSE as streamSSE2 } from "hono/streaming";
2717
- var app9 = new Hono9().get("/:name/logs", async (c) => {
2894
+ var app10 = new Hono10().get("/:name/logs", async (c) => {
2718
2895
  const name = c.req.param("name");
2719
2896
  const entry = await findMind(name);
2720
2897
  if (!entry) return c.json({ error: "Mind not found" }, 404);
2721
- const logFile = resolve11(stateDir(name), "logs", "mind.log");
2722
- if (!existsSync10(logFile)) {
2898
+ const logFile = resolve12(stateDir(name), "logs", "mind.log");
2899
+ if (!existsSync11(logFile)) {
2723
2900
  return c.json({ error: "No log file found" }, 404);
2724
2901
  }
2725
2902
  return streamSSE2(c, async (stream) => {
@@ -2746,8 +2923,8 @@ var app9 = new Hono9().get("/:name/logs", async (c) => {
2746
2923
  const name = c.req.param("name");
2747
2924
  const entry = await findMind(name);
2748
2925
  if (!entry) return c.json({ error: "Mind not found" }, 404);
2749
- const logFile = resolve11(stateDir(name), "logs", "mind.log");
2750
- if (!existsSync10(logFile)) {
2926
+ const logFile = resolve12(stateDir(name), "logs", "mind.log");
2927
+ if (!existsSync11(logFile)) {
2751
2928
  return c.json({ error: "No log file found" }, 404);
2752
2929
  }
2753
2930
  const nParam = parseInt(c.req.query("n") ?? "50", 10);
@@ -2762,13 +2939,13 @@ var app9 = new Hono9().get("/:name/logs", async (c) => {
2762
2939
  });
2763
2940
  return c.text(output);
2764
2941
  });
2765
- var logs_default = app9;
2942
+ var logs_default = app10;
2766
2943
 
2767
2944
  // src/web/api/mind-skills.ts
2768
2945
  import { zValidator as zValidator3 } from "@hono/zod-validator";
2769
- import { Hono as Hono10 } from "hono";
2946
+ import { Hono as Hono11 } from "hono";
2770
2947
  import { z as z3 } from "zod";
2771
- var app10 = new Hono10().get("/:name/skills", async (c) => {
2948
+ var app11 = new Hono11().get("/:name/skills", async (c) => {
2772
2949
  const name = c.req.param("name");
2773
2950
  const entry = await findMind(name);
2774
2951
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -2843,38 +3020,38 @@ var app10 = new Hono10().get("/:name/skills", async (c) => {
2843
3020
  }
2844
3021
  return c.json({ ok: true });
2845
3022
  });
2846
- var mind_skills_default = app10;
3023
+ var mind_skills_default = app11;
2847
3024
 
2848
3025
  // src/web/api/minds.ts
2849
3026
  import {
2850
3027
  cpSync,
2851
- existsSync as existsSync13,
2852
- mkdirSync as mkdirSync8,
3028
+ existsSync as existsSync14,
3029
+ mkdirSync as mkdirSync9,
2853
3030
  readdirSync as readdirSync3,
2854
- readFileSync as readFileSync11,
2855
- rmSync as rmSync4,
2856
- writeFileSync as writeFileSync8
3031
+ readFileSync as readFileSync12,
3032
+ rmSync as rmSync5,
3033
+ writeFileSync as writeFileSync9
2857
3034
  } from "fs";
2858
- import { resolve as resolve15 } from "path";
3035
+ import { resolve as resolve16 } from "path";
2859
3036
  import { zValidator as zValidator4 } from "@hono/zod-validator";
2860
- import { and as and3, desc as desc2, eq as eq4, sql } from "drizzle-orm";
2861
- import { Hono as Hono11 } from "hono";
3037
+ import { and as and4, desc as desc3, eq as eq5, sql as sql2 } from "drizzle-orm";
3038
+ import { Hono as Hono12 } from "hono";
2862
3039
  import { z as z4 } from "zod";
2863
3040
 
2864
3041
  // src/lib/consolidate.ts
2865
- import { readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
2866
- import { resolve as resolve12 } from "path";
3042
+ import { readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
3043
+ import { resolve as resolve13 } from "path";
2867
3044
  async function consolidateMemory(mindDir2) {
2868
- const soulPath = resolve12(mindDir2, "home/SOUL.md");
2869
- const memoryPath = resolve12(mindDir2, "home/MEMORY.md");
2870
- const memoryDir = resolve12(mindDir2, "home/memory");
2871
- const soul = readFileSync8(soulPath, "utf-8");
3045
+ const soulPath = resolve13(mindDir2, "home/SOUL.md");
3046
+ const memoryPath = resolve13(mindDir2, "home/MEMORY.md");
3047
+ const memoryDir = resolve13(mindDir2, "home/memory");
3048
+ const soul = readFileSync9(soulPath, "utf-8");
2872
3049
  const logs = [];
2873
3050
  try {
2874
3051
  const files = readdirSync2(memoryDir).filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f)).sort();
2875
3052
  for (const filename of files) {
2876
3053
  const date = filename.replace(".md", "");
2877
- const content2 = readFileSync8(resolve12(memoryDir, filename), "utf-8").trim();
3054
+ const content2 = readFileSync9(resolve13(memoryDir, filename), "utf-8").trim();
2878
3055
  if (content2) {
2879
3056
  logs.push(`### ${date}
2880
3057
 
@@ -2924,7 +3101,7 @@ ${content2}`);
2924
3101
  const data = await res.json();
2925
3102
  const content = data.content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join("").trim();
2926
3103
  if (content) {
2927
- writeFileSync5(memoryPath, `${content}
3104
+ writeFileSync6(memoryPath, `${content}
2928
3105
  `);
2929
3106
  console.log("MEMORY.md created successfully.");
2930
3107
  } else {
@@ -2934,11 +3111,11 @@ ${content2}`);
2934
3111
 
2935
3112
  // src/lib/convert-session.ts
2936
3113
  import { randomUUID } from "crypto";
2937
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
3114
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
2938
3115
  import { homedir } from "os";
2939
- import { resolve as resolve13 } from "path";
3116
+ import { resolve as resolve14 } from "path";
2940
3117
  function convertSession(opts) {
2941
- const lines = readFileSync9(opts.sessionPath, "utf-8").trim().split("\n");
3118
+ const lines = readFileSync10(opts.sessionPath, "utf-8").trim().split("\n");
2942
3119
  const sessionId = randomUUID();
2943
3120
  const idMap = /* @__PURE__ */ new Map();
2944
3121
  const messages = [];
@@ -3052,10 +3229,10 @@ function convertSession(opts) {
3052
3229
  }
3053
3230
  }
3054
3231
  const projectId = opts.projectDir.replace(/\//g, "-");
3055
- const sdkDir = resolve13(homedir(), ".claude", "projects", projectId);
3056
- mkdirSync6(sdkDir, { recursive: true });
3057
- const sdkPath = resolve13(sdkDir, `${sessionId}.jsonl`);
3058
- writeFileSync6(sdkPath, `${sdkEvents.join("\n")}
3232
+ const sdkDir = resolve14(homedir(), ".claude", "projects", projectId);
3233
+ mkdirSync7(sdkDir, { recursive: true });
3234
+ const sdkPath = resolve14(sdkDir, `${sessionId}.jsonl`);
3235
+ writeFileSync7(sdkPath, `${sdkEvents.join("\n")}
3059
3236
  `);
3060
3237
  console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
3061
3238
  return sessionId;
@@ -3100,10 +3277,198 @@ function convertAssistantContent(content) {
3100
3277
  caller: { type: "direct" }
3101
3278
  });
3102
3279
  } else {
3103
- result.push(block);
3280
+ result.push(block);
3281
+ }
3282
+ }
3283
+ return result;
3284
+ }
3285
+
3286
+ // src/lib/daemon/turn-summarizer.ts
3287
+ import { and as and3, desc as desc2, eq as eq4, gt, lt as lt3, sql } from "drizzle-orm";
3288
+
3289
+ // src/lib/format-tool.ts
3290
+ function summarizeTool(name, input) {
3291
+ if (input && typeof input === "object") {
3292
+ const args = input;
3293
+ const val = args.path ?? args.command ?? args.query ?? args.url;
3294
+ if (typeof val === "string") {
3295
+ const brief = val.length > 60 ? `${val.slice(0, 57)}...` : val;
3296
+ return `[${name} ${brief}]`;
3297
+ }
3298
+ }
3299
+ return `[${name}]`;
3300
+ }
3301
+
3302
+ // src/lib/daemon/turn-summarizer.ts
3303
+ var sLog = logger_default.child("turn-summarizer");
3304
+ async function gatherTurnEvents(mind, session, doneId) {
3305
+ const db = await getDb();
3306
+ const conditions = [
3307
+ eq4(mindHistory.mind, mind),
3308
+ eq4(mindHistory.type, "done"),
3309
+ lt3(mindHistory.id, doneId)
3310
+ ];
3311
+ if (session) {
3312
+ conditions.push(eq4(mindHistory.session, session));
3313
+ }
3314
+ const prevDone = await db.select({ id: mindHistory.id }).from(mindHistory).where(and3(...conditions)).orderBy(desc2(mindHistory.id)).limit(1);
3315
+ const prevDoneId = prevDone.length > 0 ? prevDone[0].id : 0;
3316
+ const turnConditions = [
3317
+ eq4(mindHistory.mind, mind),
3318
+ gt(mindHistory.id, prevDoneId),
3319
+ sql`${mindHistory.id} <= ${doneId}`
3320
+ ];
3321
+ if (session) {
3322
+ turnConditions.push(eq4(mindHistory.session, session));
3323
+ }
3324
+ const events = await db.select({
3325
+ id: mindHistory.id,
3326
+ type: mindHistory.type,
3327
+ channel: mindHistory.channel,
3328
+ session: mindHistory.session,
3329
+ content: mindHistory.content,
3330
+ metadata: mindHistory.metadata,
3331
+ created_at: mindHistory.created_at
3332
+ }).from(mindHistory).where(and3(...turnConditions)).orderBy(mindHistory.id);
3333
+ return {
3334
+ events,
3335
+ fromId: events.length > 0 ? events[0].id : doneId,
3336
+ toId: doneId
3337
+ };
3338
+ }
3339
+ function buildDeterministicSummary(events) {
3340
+ const channels = /* @__PURE__ */ new Set();
3341
+ const tools = [];
3342
+ let hasInbound = false;
3343
+ let hasOutbound = false;
3344
+ for (const ev of events) {
3345
+ if (ev.type === "inbound") {
3346
+ hasInbound = true;
3347
+ if (ev.channel) channels.add(ev.channel);
3348
+ }
3349
+ if (ev.type === "outbound" || ev.type === "text") {
3350
+ hasOutbound = true;
3351
+ }
3352
+ if (ev.type === "tool_use" && ev.metadata) {
3353
+ try {
3354
+ const meta = JSON.parse(ev.metadata);
3355
+ if (meta.name) tools.push(meta.name);
3356
+ } catch (err) {
3357
+ sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
3358
+ }
3359
+ }
3360
+ }
3361
+ const parts = [];
3362
+ if (hasInbound) {
3363
+ const channelList = [...channels];
3364
+ parts.push(
3365
+ channelList.length > 0 ? `Received message on ${channelList.join(", ")}` : "Received message"
3366
+ );
3367
+ }
3368
+ if (tools.length > 0) {
3369
+ const unique = [...new Set(tools)];
3370
+ parts.push(`Used ${unique.join(", ")}`);
3371
+ }
3372
+ if (hasOutbound) {
3373
+ parts.push("Sent response");
3374
+ }
3375
+ return parts.length > 0 ? `${parts.join(". ")}.` : "Turn completed.";
3376
+ }
3377
+ function buildTranscript(events) {
3378
+ const lines = [];
3379
+ for (const ev of events) {
3380
+ switch (ev.type) {
3381
+ case "inbound":
3382
+ lines.push(`[inbound${ev.channel ? ` ${ev.channel}` : ""}] ${ev.content ?? ""}`);
3383
+ break;
3384
+ case "outbound":
3385
+ case "text":
3386
+ lines.push(`[response] ${(ev.content ?? "").slice(0, 500)}`);
3387
+ break;
3388
+ case "tool_use": {
3389
+ let toolInfo = "tool";
3390
+ if (ev.metadata) {
3391
+ try {
3392
+ const meta = JSON.parse(ev.metadata);
3393
+ toolInfo = summarizeTool(meta.name ?? "tool", meta.input ?? {});
3394
+ } catch (err) {
3395
+ sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
3396
+ }
3397
+ }
3398
+ lines.push(toolInfo);
3399
+ break;
3400
+ }
3401
+ }
3402
+ }
3403
+ return lines.join("\n");
3404
+ }
3405
+ async function summarizeTurn(mind, session, channel, doneId) {
3406
+ const { events, fromId, toId } = await gatherTurnEvents(mind, session, doneId);
3407
+ if (events.length === 0) return;
3408
+ const tools = [];
3409
+ for (const ev of events) {
3410
+ if (ev.type === "tool_use" && ev.metadata) {
3411
+ try {
3412
+ const meta = JSON.parse(ev.metadata);
3413
+ if (meta.name) tools.push(meta.name);
3414
+ } catch (err) {
3415
+ sLog.debug(`failed to parse tool_use metadata for event ${ev.id}`, logger_default.errorData(err));
3416
+ }
3417
+ }
3418
+ }
3419
+ const fromTime = events[0].created_at;
3420
+ const toTime = events[events.length - 1].created_at;
3421
+ let summaryText;
3422
+ let deterministic;
3423
+ const transcript = buildTranscript(events);
3424
+ if (transcript.trim()) {
3425
+ const summaryPrompt = await getPrompt("turn_summary");
3426
+ const aiResult = await aiComplete(summaryPrompt, transcript);
3427
+ if (aiResult) {
3428
+ summaryText = aiResult;
3429
+ deterministic = false;
3430
+ } else {
3431
+ summaryText = buildDeterministicSummary(events);
3432
+ deterministic = true;
3104
3433
  }
3434
+ } else {
3435
+ summaryText = buildDeterministicSummary(events);
3436
+ deterministic = true;
3437
+ }
3438
+ const metadata = {
3439
+ deterministic,
3440
+ tool_count: tools.length,
3441
+ tools: [...new Set(tools)],
3442
+ from_id: fromId,
3443
+ to_id: toId,
3444
+ from_time: fromTime,
3445
+ to_time: toTime
3446
+ };
3447
+ const db = await getDb();
3448
+ try {
3449
+ await db.insert(mindHistory).values({
3450
+ mind,
3451
+ type: "summary",
3452
+ session: session ?? null,
3453
+ channel: channel ?? null,
3454
+ content: summaryText,
3455
+ metadata: JSON.stringify(metadata)
3456
+ });
3457
+ } catch (err) {
3458
+ sLog.error(
3459
+ `failed to persist summary for ${mind} (events ${fromId}-${toId})`,
3460
+ logger_default.errorData(err)
3461
+ );
3462
+ return;
3105
3463
  }
3106
- return result;
3464
+ publish2(mind, {
3465
+ mind,
3466
+ type: "summary",
3467
+ session,
3468
+ channel,
3469
+ content: summaryText,
3470
+ metadata
3471
+ });
3107
3472
  }
3108
3473
 
3109
3474
  // src/lib/health.ts
@@ -3121,7 +3486,7 @@ async function checkHealth(port) {
3121
3486
  }
3122
3487
 
3123
3488
  // src/lib/variant-cleanup.ts
3124
- import { existsSync as existsSync11, rmSync as rmSync3 } from "fs";
3489
+ import { existsSync as existsSync12, rmSync as rmSync4 } from "fs";
3125
3490
  async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
3126
3491
  if (opts?.stop) {
3127
3492
  try {
@@ -3130,14 +3495,14 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
3130
3495
  logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
3131
3496
  }
3132
3497
  }
3133
- const { findMind: findMind2 } = await import("./registry-NDNOOYG4.js");
3498
+ const { findMind: findMind2 } = await import("./registry-ODSALQQL.js");
3134
3499
  const variantEntry = await findMind2(variantName);
3135
3500
  const branchName = variantEntry?.branch ?? variantName;
3136
- if (existsSync11(variantPath)) {
3501
+ if (existsSync12(variantPath)) {
3137
3502
  try {
3138
3503
  await gitExec(["worktree", "remove", "--force", variantPath], { cwd: projectRoot });
3139
3504
  } catch {
3140
- rmSync3(variantPath, { recursive: true, force: true });
3505
+ rmSync4(variantPath, { recursive: true, force: true });
3141
3506
  try {
3142
3507
  await gitExec(["worktree", "prune"], { cwd: projectRoot });
3143
3508
  } catch (err) {
@@ -3167,8 +3532,8 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
3167
3532
  }
3168
3533
 
3169
3534
  // src/lib/variants.ts
3170
- import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
3171
- import { resolve as resolve14 } from "path";
3535
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
3536
+ import { resolve as resolve15 } from "path";
3172
3537
  async function checkHealth2(port) {
3173
3538
  try {
3174
3539
  const res = await fetch(`http://127.0.0.1:${port}/health`, {
@@ -3197,7 +3562,7 @@ async function getMindStatus(name, port) {
3197
3562
  const manager = getMindManager();
3198
3563
  let status = "stopped";
3199
3564
  try {
3200
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
3565
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
3201
3566
  if (getSleepManagerIfReady2()?.isSleeping(name)) {
3202
3567
  status = "sleeping";
3203
3568
  }
@@ -3245,7 +3610,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
3245
3610
  await gitExec(["commit", "-m", "initial commit"], opts);
3246
3611
  }
3247
3612
  async function updateTemplateBranch(projectRoot, template, mindName) {
3248
- const tempWorktree = resolve15(projectRoot, ".variants", "_template_update");
3613
+ const tempWorktree = resolve16(projectRoot, ".variants", "_template_update");
3249
3614
  let branchExists = false;
3250
3615
  try {
3251
3616
  await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
@@ -3256,8 +3621,8 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
3256
3621
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
3257
3622
  } catch {
3258
3623
  }
3259
- if (existsSync13(tempWorktree)) {
3260
- rmSync4(tempWorktree, { recursive: true, force: true });
3624
+ if (existsSync14(tempWorktree)) {
3625
+ rmSync5(tempWorktree, { recursive: true, force: true });
3261
3626
  }
3262
3627
  const templatesRoot = findTemplatesRoot();
3263
3628
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
@@ -3277,9 +3642,9 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
3277
3642
  });
3278
3643
  }
3279
3644
  copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
3280
- const initDir = resolve15(tempWorktree, ".init");
3281
- if (existsSync13(initDir)) {
3282
- rmSync4(initDir, { recursive: true, force: true });
3645
+ const initDir = resolve16(tempWorktree, ".init");
3646
+ if (existsSync14(initDir)) {
3647
+ rmSync5(initDir, { recursive: true, force: true });
3283
3648
  }
3284
3649
  await gitExec(["add", "-A"], { cwd: tempWorktree });
3285
3650
  try {
@@ -3292,10 +3657,10 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
3292
3657
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
3293
3658
  } catch {
3294
3659
  }
3295
- if (existsSync13(tempWorktree)) {
3296
- rmSync4(tempWorktree, { recursive: true, force: true });
3660
+ if (existsSync14(tempWorktree)) {
3661
+ rmSync5(tempWorktree, { recursive: true, force: true });
3297
3662
  }
3298
- rmSync4(composedDir, { recursive: true, force: true });
3663
+ rmSync5(composedDir, { recursive: true, force: true });
3299
3664
  }
3300
3665
  }
3301
3666
  async function mergeTemplateBranch(worktreeDir) {
@@ -3318,14 +3683,14 @@ async function mergeTemplateBranch(worktreeDir) {
3318
3683
  async function npmInstallAsMind(cwd, mindName) {
3319
3684
  if (isIsolationEnabled()) {
3320
3685
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
3321
- await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve15(cwd, "home") } });
3686
+ await exec(cmd, args, { cwd, env: { ...process.env, HOME: resolve16(cwd, "home") } });
3322
3687
  } else {
3323
3688
  await exec("npm", ["install"], { cwd });
3324
3689
  }
3325
3690
  }
3326
3691
  async function importFromArchive(c, tempDir, nameOverride, manifest) {
3327
- const extractedMindDir = resolve15(tempDir, "mind");
3328
- if (!existsSync13(extractedMindDir)) {
3692
+ const extractedMindDir = resolve16(tempDir, "mind");
3693
+ if (!existsSync14(extractedMindDir)) {
3329
3694
  return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
3330
3695
  }
3331
3696
  if (!manifest?.includes || !manifest.name || !manifest.template) {
@@ -3343,21 +3708,21 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
3343
3708
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
3344
3709
  ensureVoluteHome();
3345
3710
  const dest = mindDir(name);
3346
- if (existsSync13(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3711
+ if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3347
3712
  try {
3348
3713
  cpSync(extractedMindDir, dest, { recursive: true });
3349
3714
  if (!manifest.includes.identity) {
3350
3715
  generateIdentity(dest);
3351
3716
  }
3352
3717
  const state = stateDir(name);
3353
- mkdirSync8(state, { recursive: true });
3354
- const channelsJson = resolve15(tempDir, "state/channels.json");
3355
- if (existsSync13(channelsJson)) {
3356
- cpSync(channelsJson, resolve15(state, "channels.json"));
3718
+ mkdirSync9(state, { recursive: true });
3719
+ const channelsJson = resolve16(tempDir, "state/channels.json");
3720
+ if (existsSync14(channelsJson)) {
3721
+ cpSync(channelsJson, resolve16(state, "channels.json"));
3357
3722
  }
3358
- const envJson = resolve15(tempDir, "state/env.json");
3359
- if (existsSync13(envJson)) {
3360
- cpSync(envJson, resolve15(state, "env.json"));
3723
+ const envJson = resolve16(tempDir, "state/env.json");
3724
+ if (existsSync14(envJson)) {
3725
+ cpSync(envJson, resolve16(state, "env.json"));
3361
3726
  }
3362
3727
  const port = await nextPort();
3363
3728
  await addMind(name, port, manifest.stage, manifest.template);
@@ -3366,36 +3731,36 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
3366
3731
  } catch (err) {
3367
3732
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
3368
3733
  }
3369
- const homeDir = resolve15(dest, "home");
3734
+ const homeDir = resolve16(dest, "home");
3370
3735
  ensureVoluteGroup();
3371
3736
  createMindUser(name, homeDir);
3372
3737
  chownMindDir(dest, name);
3373
3738
  await npmInstallAsMind(dest, name);
3374
3739
  await importHistoryFromArchive(name, tempDir);
3375
3740
  importSessionsFromArchive(dest, tempDir);
3376
- if (!existsSync13(resolve15(dest, ".git"))) {
3741
+ if (!existsSync14(resolve16(dest, ".git"))) {
3377
3742
  try {
3378
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve15(dest, "home") } : void 0;
3743
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve16(dest, "home") } : void 0;
3379
3744
  await gitExec(["init"], { cwd: dest, mindName: name, env });
3380
3745
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
3381
3746
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
3382
3747
  await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
3383
3748
  } catch (err) {
3384
3749
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
3385
- rmSync4(resolve15(dest, ".git"), { recursive: true, force: true });
3750
+ rmSync5(resolve16(dest, ".git"), { recursive: true, force: true });
3386
3751
  }
3387
3752
  }
3388
3753
  chownMindDir(dest, name);
3389
- rmSync4(tempDir, { recursive: true, force: true });
3754
+ rmSync5(tempDir, { recursive: true, force: true });
3390
3755
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
3391
3756
  } catch (err) {
3392
- if (existsSync13(dest)) rmSync4(dest, { recursive: true, force: true });
3757
+ if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
3393
3758
  try {
3394
3759
  await removeMind(name);
3395
3760
  } catch (cleanupErr) {
3396
3761
  logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
3397
3762
  }
3398
- rmSync4(tempDir, { recursive: true, force: true });
3763
+ rmSync5(tempDir, { recursive: true, force: true });
3399
3764
  return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
3400
3765
  }
3401
3766
  }
@@ -3406,7 +3771,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3406
3771
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
3407
3772
  ensureVoluteHome();
3408
3773
  const dest = mindDir(name);
3409
- if (existsSync13(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3774
+ if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3410
3775
  const templatesRoot = findTemplatesRoot();
3411
3776
  const { composedDir, manifest: templateManifest } = composeTemplate(
3412
3777
  templatesRoot,
@@ -3415,40 +3780,40 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3415
3780
  try {
3416
3781
  copyTemplateToDir(composedDir, dest, name, templateManifest);
3417
3782
  applyInitFiles(dest);
3418
- const extractedHome = resolve15(extractedMindDir, "home");
3419
- if (existsSync13(extractedHome)) {
3420
- cpSync(extractedHome, resolve15(dest, "home"), { recursive: true });
3783
+ const extractedHome = resolve16(extractedMindDir, "home");
3784
+ if (existsSync14(extractedHome)) {
3785
+ cpSync(extractedHome, resolve16(dest, "home"), { recursive: true });
3421
3786
  }
3422
- const extractedMindInternal = resolve15(extractedMindDir, ".mind");
3423
- if (existsSync13(extractedMindInternal)) {
3424
- cpSync(extractedMindInternal, resolve15(dest, ".mind"), { recursive: true });
3787
+ const extractedMindInternal = resolve16(extractedMindDir, ".mind");
3788
+ if (existsSync14(extractedMindInternal)) {
3789
+ cpSync(extractedMindInternal, resolve16(dest, ".mind"), { recursive: true });
3425
3790
  }
3426
- const identityDir = resolve15(dest, ".mind/identity");
3791
+ const identityDir = resolve16(dest, ".mind/identity");
3427
3792
  let publicKeyPem;
3428
- if (!manifest.includes.identity || !existsSync13(resolve15(identityDir, "private.pem"))) {
3793
+ if (!manifest.includes.identity || !existsSync14(resolve16(identityDir, "private.pem"))) {
3429
3794
  ({ publicKeyPem } = generateIdentity(dest));
3430
3795
  } else {
3431
- publicKeyPem = readFileSync11(resolve15(identityDir, "public.pem"), "utf-8");
3796
+ publicKeyPem = readFileSync12(resolve16(identityDir, "public.pem"), "utf-8");
3432
3797
  }
3433
- const promptsPath = resolve15(dest, "home/.config/prompts.json");
3434
- if (!existsSync13(promptsPath)) {
3798
+ const promptsPath = resolve16(dest, "home/.config/prompts.json");
3799
+ if (!existsSync14(promptsPath)) {
3435
3800
  const mindPrompts = await getMindPromptDefaults();
3436
- writeFileSync8(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
3801
+ writeFileSync9(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
3437
3802
  `);
3438
3803
  }
3439
3804
  const state = stateDir(name);
3440
- mkdirSync8(state, { recursive: true });
3441
- const channelsJson = resolve15(tempDir, "state/channels.json");
3442
- if (existsSync13(channelsJson)) {
3443
- cpSync(channelsJson, resolve15(state, "channels.json"));
3805
+ mkdirSync9(state, { recursive: true });
3806
+ const channelsJson = resolve16(tempDir, "state/channels.json");
3807
+ if (existsSync14(channelsJson)) {
3808
+ cpSync(channelsJson, resolve16(state, "channels.json"));
3444
3809
  }
3445
- const envJson = resolve15(tempDir, "state/env.json");
3446
- if (existsSync13(envJson)) {
3447
- cpSync(envJson, resolve15(state, "env.json"));
3810
+ const envJson = resolve16(tempDir, "state/env.json");
3811
+ if (existsSync14(envJson)) {
3812
+ cpSync(envJson, resolve16(state, "env.json"));
3448
3813
  }
3449
3814
  const port = await nextPort();
3450
3815
  await addMind(name, port, manifest.stage, manifest.template);
3451
- const homeDir = resolve15(dest, "home");
3816
+ const homeDir = resolve16(dest, "home");
3452
3817
  ensureVoluteGroup();
3453
3818
  createMindUser(name, homeDir);
3454
3819
  chownMindDir(dest, name);
@@ -3461,7 +3826,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3461
3826
  await initTemplateBranch(dest, composedDir, templateManifest, name, env);
3462
3827
  } catch (err) {
3463
3828
  logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
3464
- rmSync4(resolve15(dest, ".git"), { recursive: true, force: true });
3829
+ rmSync5(resolve16(dest, ".git"), { recursive: true, force: true });
3465
3830
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
3466
3831
  }
3467
3832
  try {
@@ -3469,7 +3834,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3469
3834
  } catch (err) {
3470
3835
  logger_default.warn(`failed to add shared worktree for ${name}`, logger_default.errorData(err));
3471
3836
  }
3472
- const skillSet = manifest.stage === "seed" ? SEED_SKILLS : STANDARD_SKILLS;
3837
+ const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
3473
3838
  const skillWarnings = [];
3474
3839
  for (const skillId of skillSet) {
3475
3840
  try {
@@ -3485,7 +3850,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3485
3850
  publishPublicKey(name, publicKeyPem).catch(
3486
3851
  (err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
3487
3852
  );
3488
- rmSync4(tempDir, { recursive: true, force: true });
3853
+ rmSync5(tempDir, { recursive: true, force: true });
3489
3854
  return c.json({
3490
3855
  ok: true,
3491
3856
  name,
@@ -3496,24 +3861,24 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3496
3861
  ...skillWarnings.length > 0 && { skillWarnings }
3497
3862
  });
3498
3863
  } catch (err) {
3499
- if (existsSync13(dest)) rmSync4(dest, { recursive: true, force: true });
3864
+ if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
3500
3865
  try {
3501
3866
  await removeMind(name);
3502
3867
  } catch (cleanupErr) {
3503
3868
  logger_default.error(`Failed to clean up registry for ${name}`, logger_default.errorData(cleanupErr));
3504
3869
  }
3505
- rmSync4(tempDir, { recursive: true, force: true });
3870
+ rmSync5(tempDir, { recursive: true, force: true });
3506
3871
  return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
3507
3872
  } finally {
3508
- rmSync4(composedDir, { recursive: true, force: true });
3873
+ rmSync5(composedDir, { recursive: true, force: true });
3509
3874
  }
3510
3875
  }
3511
3876
  async function importHistoryFromArchive(name, tempDir) {
3512
- const historyJsonl = resolve15(tempDir, "history.jsonl");
3513
- if (!existsSync13(historyJsonl)) return;
3877
+ const historyJsonl = resolve16(tempDir, "history.jsonl");
3878
+ if (!existsSync14(historyJsonl)) return;
3514
3879
  try {
3515
3880
  const db = await getDb();
3516
- const lines = readFileSync11(historyJsonl, "utf-8").trim().split("\n");
3881
+ const lines = readFileSync12(historyJsonl, "utf-8").trim().split("\n");
3517
3882
  let imported = 0;
3518
3883
  let failed = 0;
3519
3884
  for (const line of lines) {
@@ -3549,13 +3914,13 @@ async function importHistoryFromArchive(name, tempDir) {
3549
3914
  }
3550
3915
  }
3551
3916
  function importSessionsFromArchive(dest, tempDir) {
3552
- const sessionsDir = resolve15(tempDir, "sessions");
3553
- if (!existsSync13(sessionsDir)) return;
3917
+ const sessionsDir = resolve16(tempDir, "sessions");
3918
+ if (!existsSync14(sessionsDir)) return;
3554
3919
  try {
3555
- const destSessions = resolve15(dest, ".mind/sessions");
3556
- mkdirSync8(destSessions, { recursive: true });
3920
+ const destSessions = resolve16(dest, ".mind/sessions");
3921
+ mkdirSync9(destSessions, { recursive: true });
3557
3922
  for (const file of readdirSync3(sessionsDir)) {
3558
- cpSync(resolve15(sessionsDir, file), resolve15(destSessions, file));
3923
+ cpSync(resolve16(sessionsDir, file), resolve16(destSessions, file));
3559
3924
  }
3560
3925
  } catch (err) {
3561
3926
  logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
@@ -3570,7 +3935,7 @@ var createMindSchema = z4.object({
3570
3935
  seedSoul: z4.string().optional(),
3571
3936
  skills: z4.array(z4.string()).optional()
3572
3937
  });
3573
- var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindSchema), async (c) => {
3938
+ var app12 = new Hono12().post("/", requireAdmin, zValidator4("json", createMindSchema), async (c) => {
3574
3939
  const body = c.req.valid("json");
3575
3940
  const { name, template = "claude" } = body;
3576
3941
  const nameErr = validateMindName(name);
@@ -3578,7 +3943,7 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
3578
3943
  if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
3579
3944
  ensureVoluteHome();
3580
3945
  const dest = mindDir(name);
3581
- if (existsSync13(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3946
+ if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3582
3947
  const templatesRoot = findTemplatesRoot();
3583
3948
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
3584
3949
  try {
@@ -3611,15 +3976,15 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
3611
3976
  writeVoluteConfig(dest, config);
3612
3977
  }
3613
3978
  if (body.model) {
3614
- const configPath = resolve15(dest, "home/.config/config.json");
3615
- const existing = existsSync13(configPath) ? JSON.parse(readFileSync11(configPath, "utf-8")) : {};
3979
+ const configPath = resolve16(dest, "home/.config/config.json");
3980
+ const existing = existsSync14(configPath) ? JSON.parse(readFileSync12(configPath, "utf-8")) : {};
3616
3981
  existing.model = body.model;
3617
- writeFileSync8(configPath, `${JSON.stringify(existing, null, 2)}
3982
+ writeFileSync9(configPath, `${JSON.stringify(existing, null, 2)}
3618
3983
  `);
3619
3984
  }
3620
3985
  const mindPrompts = await getMindPromptDefaults();
3621
- writeFileSync8(
3622
- resolve15(dest, "home/.config/prompts.json"),
3986
+ writeFileSync9(
3987
+ resolve16(dest, "home/.config/prompts.json"),
3623
3988
  `${JSON.stringify(mindPrompts, null, 2)}
3624
3989
  `
3625
3990
  );
@@ -3630,7 +3995,7 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
3630
3995
  } catch (err) {
3631
3996
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
3632
3997
  }
3633
- const homeDir = resolve15(dest, "home");
3998
+ const homeDir = resolve16(dest, "home");
3634
3999
  ensureVoluteGroup();
3635
4000
  createMindUser(name, homeDir);
3636
4001
  chownMindDir(dest, name);
@@ -3643,7 +4008,7 @@ var app11 = new Hono11().post("/", requireAdmin, zValidator4("json", createMindS
3643
4008
  await initTemplateBranch(dest, composedDir, manifest, name, env);
3644
4009
  } catch (err) {
3645
4010
  logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
3646
- rmSync4(resolve15(dest, ".git"), { recursive: true, force: true });
4011
+ rmSync5(resolve16(dest, ".git"), { recursive: true, force: true });
3647
4012
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
3648
4013
  }
3649
4014
  try {
@@ -3658,9 +4023,9 @@ The human who planted you described you as: "${body.description}"
3658
4023
  ` : "";
3659
4024
  const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
3660
4025
  const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
3661
- writeFileSync8(resolve15(dest, "home/SOUL.md"), seedSoul);
4026
+ writeFileSync9(resolve16(dest, "home/SOUL.md"), seedSoul);
3662
4027
  }
3663
- const skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : STANDARD_SKILLS);
4028
+ const skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
3664
4029
  const skillWarnings = [];
3665
4030
  for (const skillId of skillSet) {
3666
4031
  try {
@@ -3673,11 +4038,11 @@ The human who planted you described you as: "${body.description}"
3673
4038
  if (body.stage !== "seed") {
3674
4039
  const customSoul = await getPromptIfCustom("default_soul");
3675
4040
  if (customSoul) {
3676
- writeFileSync8(resolve15(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
4041
+ writeFileSync9(resolve16(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
3677
4042
  }
3678
4043
  const customMemory = await getPromptIfCustom("default_memory");
3679
4044
  if (customMemory) {
3680
- writeFileSync8(resolve15(dest, "home/MEMORY.md"), customMemory);
4045
+ writeFileSync9(resolve16(dest, "home/MEMORY.md"), customMemory);
3681
4046
  }
3682
4047
  }
3683
4048
  publishPublicKey(name, publicKeyPem).catch(
@@ -3706,14 +4071,14 @@ The human who planted you described you as: "${body.description}"
3706
4071
  ...skillWarnings.length > 0 && { skillWarnings }
3707
4072
  });
3708
4073
  } catch (err) {
3709
- if (existsSync13(dest)) rmSync4(dest, { recursive: true, force: true });
4074
+ if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
3710
4075
  try {
3711
4076
  await removeMind(name);
3712
4077
  } catch {
3713
4078
  }
3714
4079
  return c.json({ error: err instanceof Error ? err.message : "Failed to create mind" }, 500);
3715
4080
  } finally {
3716
- rmSync4(composedDir, { recursive: true, force: true });
4081
+ rmSync5(composedDir, { recursive: true, force: true });
3717
4082
  }
3718
4083
  }).post("/import", requireAdmin, async (c) => {
3719
4084
  let body;
@@ -3726,13 +4091,13 @@ The human who planted you described you as: "${body.description}"
3726
4091
  return importFromArchive(c, body.archivePath, body.name, body.manifest);
3727
4092
  }
3728
4093
  const wsDir = body.workspacePath;
3729
- if (!wsDir || !existsSync13(resolve15(wsDir, "SOUL.md")) || !existsSync13(resolve15(wsDir, "IDENTITY.md"))) {
4094
+ if (!wsDir || !existsSync14(resolve16(wsDir, "SOUL.md")) || !existsSync14(resolve16(wsDir, "IDENTITY.md"))) {
3730
4095
  return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
3731
4096
  }
3732
- const soul = readFileSync11(resolve15(wsDir, "SOUL.md"), "utf-8");
3733
- const identity = readFileSync11(resolve15(wsDir, "IDENTITY.md"), "utf-8");
3734
- const userPath = resolve15(wsDir, "USER.md");
3735
- const user = existsSync13(userPath) ? readFileSync11(userPath, "utf-8") : "";
4097
+ const soul = readFileSync12(resolve16(wsDir, "SOUL.md"), "utf-8");
4098
+ const identity = readFileSync12(resolve16(wsDir, "IDENTITY.md"), "utf-8");
4099
+ const userPath = resolve16(wsDir, "USER.md");
4100
+ const user = existsSync14(userPath) ? readFileSync12(userPath, "utf-8") : "";
3736
4101
  const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
3737
4102
  const template = body.template ?? "claude";
3738
4103
  const nameErr = validateMindName(name);
@@ -3752,33 +4117,33 @@ ${user.trimEnd()}
3752
4117
  ` : "";
3753
4118
  ensureVoluteHome();
3754
4119
  const dest = mindDir(name);
3755
- if (existsSync13(dest)) return c.json({ error: "Mind directory already exists" }, 409);
4120
+ if (existsSync14(dest)) return c.json({ error: "Mind directory already exists" }, 409);
3756
4121
  const templatesRoot = findTemplatesRoot();
3757
4122
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
3758
4123
  try {
3759
4124
  copyTemplateToDir(composedDir, dest, name, manifest);
3760
4125
  applyInitFiles(dest);
3761
4126
  const { publicKeyPem: importPublicKey } = generateIdentity(dest);
3762
- writeFileSync8(resolve15(dest, "home/SOUL.md"), mergedSoul);
3763
- const wsMemoryPath = resolve15(wsDir, "MEMORY.md");
3764
- const hasMemory = existsSync13(wsMemoryPath);
4127
+ writeFileSync9(resolve16(dest, "home/SOUL.md"), mergedSoul);
4128
+ const wsMemoryPath = resolve16(wsDir, "MEMORY.md");
4129
+ const hasMemory = existsSync14(wsMemoryPath);
3765
4130
  if (hasMemory) {
3766
- const existingMemory = readFileSync11(wsMemoryPath, "utf-8");
3767
- writeFileSync8(
3768
- resolve15(dest, "home/MEMORY.md"),
4131
+ const existingMemory = readFileSync12(wsMemoryPath, "utf-8");
4132
+ writeFileSync9(
4133
+ resolve16(dest, "home/MEMORY.md"),
3769
4134
  `${existingMemory.trimEnd()}${mergedMemoryExtra}`
3770
4135
  );
3771
4136
  } else if (user) {
3772
- writeFileSync8(resolve15(dest, "home/MEMORY.md"), `${user.trimEnd()}
4137
+ writeFileSync9(resolve16(dest, "home/MEMORY.md"), `${user.trimEnd()}
3773
4138
  `);
3774
4139
  }
3775
- const wsMemoryDir = resolve15(wsDir, "memory");
4140
+ const wsMemoryDir = resolve16(wsDir, "memory");
3776
4141
  let dailyLogCount = 0;
3777
- if (existsSync13(wsMemoryDir)) {
3778
- const destMemoryDir = resolve15(dest, "home/memory");
4142
+ if (existsSync14(wsMemoryDir)) {
4143
+ const destMemoryDir = resolve16(dest, "home/memory");
3779
4144
  const files = readdirSync3(wsMemoryDir).filter((f) => f.endsWith(".md"));
3780
4145
  for (const file of files) {
3781
- cpSync(resolve15(wsMemoryDir, file), resolve15(destMemoryDir, file));
4146
+ cpSync(resolve16(wsMemoryDir, file), resolve16(destMemoryDir, file));
3782
4147
  }
3783
4148
  dailyLogCount = files.length;
3784
4149
  }
@@ -3789,7 +4154,7 @@ ${user.trimEnd()}
3789
4154
  } catch (err) {
3790
4155
  logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
3791
4156
  }
3792
- const homeDir = resolve15(dest, "home");
4157
+ const homeDir = resolve16(dest, "home");
3793
4158
  ensureVoluteGroup();
3794
4159
  createMindUser(name, homeDir);
3795
4160
  chownMindDir(dest, name);
@@ -3797,20 +4162,20 @@ ${user.trimEnd()}
3797
4162
  if (!hasMemory && dailyLogCount > 0) {
3798
4163
  await consolidateMemory(dest);
3799
4164
  }
3800
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve15(dest, "home") } : void 0;
4165
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve16(dest, "home") } : void 0;
3801
4166
  await gitExec(["init"], { cwd: dest, mindName: name, env });
3802
4167
  await configureGitIdentity(name, { cwd: dest, mindName: name, env });
3803
4168
  await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
3804
4169
  await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
3805
- const sessionFile = body.sessionPath ? resolve15(body.sessionPath) : findOpenClawSession(wsDir);
3806
- if (sessionFile && existsSync13(sessionFile)) {
4170
+ const sessionFile = body.sessionPath ? resolve16(body.sessionPath) : findOpenClawSession(wsDir);
4171
+ if (sessionFile && existsSync14(sessionFile)) {
3807
4172
  if (template === "pi") {
3808
4173
  importPiSession(sessionFile, dest);
3809
4174
  } else if (template === "claude") {
3810
4175
  const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
3811
- const mindRuntimeDir = resolve15(dest, ".mind");
3812
- mkdirSync8(mindRuntimeDir, { recursive: true });
3813
- writeFileSync8(resolve15(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
4176
+ const mindRuntimeDir = resolve16(dest, ".mind");
4177
+ mkdirSync9(mindRuntimeDir, { recursive: true });
4178
+ writeFileSync9(resolve16(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
3814
4179
  }
3815
4180
  }
3816
4181
  importOpenClawConnectors(name, dest);
@@ -3825,14 +4190,14 @@ ${user.trimEnd()}
3825
4190
  );
3826
4191
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
3827
4192
  } catch (err) {
3828
- if (existsSync13(dest)) rmSync4(dest, { recursive: true, force: true });
4193
+ if (existsSync14(dest)) rmSync5(dest, { recursive: true, force: true });
3829
4194
  try {
3830
4195
  await removeMind(name);
3831
4196
  } catch {
3832
4197
  }
3833
4198
  return c.json({ error: err instanceof Error ? err.message : "Failed to import mind" }, 500);
3834
4199
  } finally {
3835
- rmSync4(composedDir, { recursive: true, force: true });
4200
+ rmSync5(composedDir, { recursive: true, force: true });
3836
4201
  }
3837
4202
  }).get("/", async (c) => {
3838
4203
  const entries = await readRegistry();
@@ -3841,7 +4206,7 @@ ${user.trimEnd()}
3841
4206
  const db = await getDb();
3842
4207
  const lastActiveRows = await db.select({
3843
4208
  mind: mindHistory.mind,
3844
- lastActiveAt: sql`MAX(${mindHistory.created_at})`
4209
+ lastActiveAt: sql2`MAX(${mindHistory.created_at})`
3845
4210
  }).from(mindHistory).groupBy(mindHistory.mind);
3846
4211
  lastActiveMap = new Map(lastActiveRows.map((r) => [r.mind, r.lastActiveAt]));
3847
4212
  } catch {
@@ -3849,7 +4214,7 @@ ${user.trimEnd()}
3849
4214
  const minds = await Promise.all(
3850
4215
  entries.map(async (entry) => {
3851
4216
  const mindStatus = await getMindStatus(entry.name, entry.port);
3852
- const hasPages = existsSync13(resolve15(mindDir(entry.name), "home", "public", "pages"));
4217
+ const hasPages = existsSync14(resolve16(mindDir(entry.name), "home", "public", "pages"));
3853
4218
  return {
3854
4219
  ...entry,
3855
4220
  ...mindStatus,
@@ -3868,7 +4233,7 @@ ${user.trimEnd()}
3868
4233
  const entry = await findMind(name);
3869
4234
  if (!entry) return c.json({ error: "Mind not found" }, 404);
3870
4235
  const dir = entry.dir ?? mindDir(entry.parent ?? name);
3871
- if (!existsSync13(dir)) return c.json({ error: "Mind directory missing" }, 404);
4236
+ if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
3872
4237
  const mindStatus = await getMindStatus(name, entry.port);
3873
4238
  const variants = await findVariants(name);
3874
4239
  const manager = getMindManager();
@@ -3882,7 +4247,7 @@ ${user.trimEnd()}
3882
4247
  return { name: s.name, port: s.port, status: variantStatus };
3883
4248
  })
3884
4249
  );
3885
- const hasPages = existsSync13(resolve15(mindDir(name), "home", "public", "pages"));
4250
+ const hasPages = existsSync14(resolve16(mindDir(name), "home", "public", "pages"));
3886
4251
  return c.json({ ...entry, ...mindStatus, variants: variantStatuses, hasPages });
3887
4252
  }).post("/:name/start", requireSelf(), async (c) => {
3888
4253
  const name = c.req.param("name");
@@ -3893,7 +4258,7 @@ ${user.trimEnd()}
3893
4258
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
3894
4259
  } else {
3895
4260
  const dir = mindDir(name);
3896
- if (!existsSync13(dir)) return c.json({ error: "Mind directory missing" }, 404);
4261
+ if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
3897
4262
  }
3898
4263
  if (getMindManager().isRunning(name)) {
3899
4264
  return c.json({ error: "Mind already running" }, 409);
@@ -3914,7 +4279,7 @@ ${user.trimEnd()}
3914
4279
  if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 404);
3915
4280
  } else {
3916
4281
  const dir = mindDir(name);
3917
- if (!existsSync13(dir)) return c.json({ error: "Mind directory missing" }, 404);
4282
+ if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
3918
4283
  }
3919
4284
  let context;
3920
4285
  const contentType = c.req.header("content-type");
@@ -3929,7 +4294,7 @@ ${user.trimEnd()}
3929
4294
  const manager = getMindManager();
3930
4295
  try {
3931
4296
  if (context?.type === "reload") {
3932
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
4297
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
3933
4298
  const sleepState = getSleepManagerIfReady2()?.getState(name);
3934
4299
  if (sleepState?.sleeping) {
3935
4300
  logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
@@ -3949,7 +4314,7 @@ ${user.trimEnd()}
3949
4314
  const variantEntry = await findMind(mergeVariantName);
3950
4315
  if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
3951
4316
  const projectRoot = mindDir(baseName);
3952
- if (existsSync13(variantEntry.dir)) {
4317
+ if (existsSync14(variantEntry.dir)) {
3953
4318
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
3954
4319
  if (status) {
3955
4320
  try {
@@ -3991,7 +4356,7 @@ ${user.trimEnd()}
3991
4356
  if (context?.type === "sprouted" && !entry.parent) {
3992
4357
  try {
3993
4358
  const db = await getDb();
3994
- const activeConvs = await db.select({ id: conversations.id }).from(conversations).where(eq4(conversations.mind_name, baseName)).all();
4359
+ const activeConvs = await db.select({ id: conversations.id }).from(conversations).where(eq5(conversations.mind_name, baseName)).all();
3995
4360
  for (const conv of activeConvs) {
3996
4361
  await addMessage(conv.id, "assistant", "system", [
3997
4362
  { type: "text", text: "[seed has sprouted]" }
@@ -4024,7 +4389,7 @@ ${user.trimEnd()}
4024
4389
  const name = c.req.param("name");
4025
4390
  const entry = await findMind(name);
4026
4391
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4027
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
4392
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
4028
4393
  const sm = getSleepManagerIfReady2();
4029
4394
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4030
4395
  return c.json(sm.getState(name));
@@ -4032,7 +4397,7 @@ ${user.trimEnd()}
4032
4397
  const name = c.req.param("name");
4033
4398
  const entry = await findMind(name);
4034
4399
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4035
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
4400
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
4036
4401
  const sm = getSleepManagerIfReady2();
4037
4402
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4038
4403
  if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
@@ -4052,7 +4417,7 @@ ${user.trimEnd()}
4052
4417
  const name = c.req.param("name");
4053
4418
  const entry = await findMind(name);
4054
4419
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4055
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
4420
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
4056
4421
  const sm = getSleepManagerIfReady2();
4057
4422
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4058
4423
  const sleepState = sm.getState(name);
@@ -4067,7 +4432,7 @@ ${user.trimEnd()}
4067
4432
  const name = c.req.param("name");
4068
4433
  const entry = await findMind(name);
4069
4434
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4070
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
4435
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
4071
4436
  const sm = getSleepManagerIfReady2();
4072
4437
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4073
4438
  const flushed = await sm.flushQueuedMessages(name);
@@ -4105,11 +4470,11 @@ ${user.trimEnd()}
4105
4470
  await removeMind(name);
4106
4471
  await deleteMindUser2(name);
4107
4472
  const state = stateDir(name);
4108
- if (existsSync13(state)) {
4109
- rmSync4(state, { recursive: true, force: true });
4473
+ if (existsSync14(state)) {
4474
+ rmSync5(state, { recursive: true, force: true });
4110
4475
  }
4111
- if (force && existsSync13(dir)) {
4112
- rmSync4(dir, { recursive: true, force: true });
4476
+ if (force && existsSync14(dir)) {
4477
+ rmSync5(dir, { recursive: true, force: true });
4113
4478
  deleteMindUser(name);
4114
4479
  }
4115
4480
  fireWebhook({
@@ -4123,7 +4488,7 @@ ${user.trimEnd()}
4123
4488
  const entry = await findMind(mindName);
4124
4489
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4125
4490
  const dir = mindDir(mindName);
4126
- if (!existsSync13(dir)) return c.json({ error: "Mind directory missing" }, 404);
4491
+ if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
4127
4492
  let body = {};
4128
4493
  try {
4129
4494
  body = await c.req.json();
@@ -4132,16 +4497,16 @@ ${user.trimEnd()}
4132
4497
  const template = body.template ?? entry.template ?? "claude";
4133
4498
  const UPGRADE_BRANCH = "upgrade";
4134
4499
  const upgradeVariantName = `${mindName}-upgrade`;
4135
- const worktreeDir = resolve15(dir, ".variants", UPGRADE_BRANCH);
4500
+ const worktreeDir = resolve16(dir, ".variants", UPGRADE_BRANCH);
4136
4501
  if (body.abort) {
4137
- if (!existsSync13(worktreeDir)) {
4502
+ if (!existsSync14(worktreeDir)) {
4138
4503
  return c.json({ error: "No upgrade in progress" }, 400);
4139
4504
  }
4140
4505
  try {
4141
4506
  try {
4142
- const gitDirContent = readFileSync11(resolve15(worktreeDir, ".git"), "utf-8").trim();
4507
+ const gitDirContent = readFileSync12(resolve16(worktreeDir, ".git"), "utf-8").trim();
4143
4508
  const gitDir = gitDirContent.replace("gitdir: ", "");
4144
- if (existsSync13(resolve15(gitDir, "MERGE_HEAD"))) {
4509
+ if (existsSync14(resolve16(gitDir, "MERGE_HEAD"))) {
4145
4510
  await gitExec(["merge", "--abort"], { cwd: worktreeDir });
4146
4511
  }
4147
4512
  } catch {
@@ -4160,7 +4525,7 @@ ${user.trimEnd()}
4160
4525
  }
4161
4526
  }
4162
4527
  if (body.continue) {
4163
- if (!existsSync13(worktreeDir)) {
4528
+ if (!existsSync14(worktreeDir)) {
4164
4529
  return c.json({ error: "No upgrade in progress" }, 400);
4165
4530
  }
4166
4531
  const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir });
@@ -4199,7 +4564,7 @@ ${user.trimEnd()}
4199
4564
  }
4200
4565
  }
4201
4566
  if (body.accept) {
4202
- if (!existsSync13(worktreeDir)) {
4567
+ if (!existsSync14(worktreeDir)) {
4203
4568
  return c.json({ error: "No upgrade in progress" }, 400);
4204
4569
  }
4205
4570
  const variantEntry = await findMind(upgradeVariantName);
@@ -4222,7 +4587,7 @@ ${user.trimEnd()}
4222
4587
  try {
4223
4588
  await gitExec(["add", "-A"], { cwd: dir });
4224
4589
  await gitExec(["commit", "-m", "Auto-commit before upgrade merge"], { cwd: dir });
4225
- } catch (e) {
4590
+ } catch (_e) {
4226
4591
  return c.json({ error: "Failed to auto-commit main changes before merge" }, 500);
4227
4592
  }
4228
4593
  }
@@ -4257,22 +4622,22 @@ ${user.trimEnd()}
4257
4622
  }
4258
4623
  return c.json({ ok: true });
4259
4624
  }
4260
- if (existsSync13(worktreeDir)) {
4625
+ if (existsSync14(worktreeDir)) {
4261
4626
  return c.json(
4262
4627
  { error: "Upgrade variant already exists. Use continue or delete it first." },
4263
4628
  409
4264
4629
  );
4265
4630
  }
4266
- if (!existsSync13(resolve15(dir, ".git"))) {
4631
+ if (!existsSync14(resolve16(dir, ".git"))) {
4267
4632
  try {
4268
- const env = isIsolationEnabled() ? { ...process.env, HOME: resolve15(dir, "home") } : void 0;
4633
+ const env = isIsolationEnabled() ? { ...process.env, HOME: resolve16(dir, "home") } : void 0;
4269
4634
  await gitExec(["init"], { cwd: dir, mindName, env });
4270
4635
  await configureGitIdentity(mindName, { cwd: dir, mindName, env });
4271
4636
  await gitExec(["add", "-A"], { cwd: dir, mindName, env });
4272
4637
  await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
4273
4638
  chownMindDir(dir, mindName);
4274
4639
  } catch (err) {
4275
- rmSync4(resolve15(dir, ".git"), { recursive: true, force: true });
4640
+ rmSync5(resolve16(dir, ".git"), { recursive: true, force: true });
4276
4641
  return c.json(
4277
4642
  {
4278
4643
  error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
@@ -4286,7 +4651,7 @@ ${user.trimEnd()}
4286
4651
  await gitExec(["branch", "-D", UPGRADE_BRANCH], { cwd: dir });
4287
4652
  } catch {
4288
4653
  }
4289
- if (!existsSync13(resolve15(dir, "home", "shared"))) {
4654
+ if (!existsSync14(resolve16(dir, "home", "shared"))) {
4290
4655
  try {
4291
4656
  await addSharedWorktree(mindName, dir);
4292
4657
  } catch (err) {
@@ -4297,9 +4662,9 @@ ${user.trimEnd()}
4297
4662
  }
4298
4663
  }
4299
4664
  await updateTemplateBranch(dir, template, mindName);
4300
- const parentDir = resolve15(dir, ".variants");
4301
- if (!existsSync13(parentDir)) {
4302
- mkdirSync8(parentDir, { recursive: true });
4665
+ const parentDir = resolve16(dir, ".variants");
4666
+ if (!existsSync14(parentDir)) {
4667
+ mkdirSync9(parentDir, { recursive: true });
4303
4668
  }
4304
4669
  await gitExec(["worktree", "add", "-b", UPGRADE_BRANCH, worktreeDir], { cwd: dir });
4305
4670
  const hasConflicts = await mergeTemplateBranch(worktreeDir);
@@ -4336,7 +4701,7 @@ ${user.trimEnd()}
4336
4701
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4337
4702
  const baseName = entry.parent ?? name;
4338
4703
  try {
4339
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
4704
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
4340
4705
  const sm = getSleepManagerIfReady2();
4341
4706
  if (sm?.isSleeping(baseName)) {
4342
4707
  const body2 = await c.req.text();
@@ -4434,7 +4799,7 @@ ${user.trimEnd()}
4434
4799
  if (seedEntry?.stage === "seed") {
4435
4800
  try {
4436
4801
  const db = await getDb();
4437
- const countResult = await db.select({ count: sql`count(*)` }).from(mindHistory).where(eq4(mindHistory.mind, baseName));
4802
+ const countResult = await db.select({ count: sql2`count(*)` }).from(mindHistory).where(eq5(mindHistory.mind, baseName));
4438
4803
  const msgCount = countResult[0]?.count ?? 0;
4439
4804
  if (msgCount >= 10 && msgCount % 10 === 0) {
4440
4805
  const nudge = "\n[You've been exploring for a while. Whenever you feel ready, write your SOUL.md and MEMORY.md, then run volute mind sprout.]";
@@ -4491,7 +4856,7 @@ ${user.trimEnd()}
4491
4856
  }
4492
4857
  const before = beforeStr ? parseInt(beforeStr, 10) : void 0;
4493
4858
  const limit = limitStr ? parseInt(limitStr, 10) : void 0;
4494
- if (before !== void 0 && isNaN(before) || limit !== void 0 && isNaN(limit)) {
4859
+ if (before !== void 0 && Number.isNaN(before) || limit !== void 0 && Number.isNaN(limit)) {
4495
4860
  return c.json({ error: "Invalid pagination parameters" }, 400);
4496
4861
  }
4497
4862
  const result = await getMessagesPaginated(convId, { before, limit });
@@ -4507,13 +4872,13 @@ ${user.trimEnd()}
4507
4872
  const entry = await findMind(name);
4508
4873
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4509
4874
  const dir = mindDir(name);
4510
- if (!existsSync13(dir)) return c.json({ error: "Mind directory missing" }, 404);
4875
+ if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
4511
4876
  let config = readVoluteConfig(dir);
4512
4877
  if (!config && entry.template === "pi") {
4513
- const piConfigPath = resolve15(dir, "home/.config/config.json");
4514
- if (existsSync13(piConfigPath)) {
4878
+ const piConfigPath = resolve16(dir, "home/.config/config.json");
4879
+ if (existsSync14(piConfigPath)) {
4515
4880
  try {
4516
- config = JSON.parse(readFileSync11(piConfigPath, "utf-8"));
4881
+ config = JSON.parse(readFileSync12(piConfigPath, "utf-8"));
4517
4882
  } catch {
4518
4883
  }
4519
4884
  }
@@ -4550,7 +4915,7 @@ ${user.trimEnd()}
4550
4915
  const entry = await findMind(name);
4551
4916
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4552
4917
  const dir = mindDir(name);
4553
- if (!existsSync13(dir)) return c.json({ error: "Mind directory missing" }, 404);
4918
+ if (!existsSync14(dir)) return c.json({ error: "Mind directory missing" }, 404);
4554
4919
  const body = c.req.valid("json");
4555
4920
  const existing = readVoluteConfig(dir) ?? {};
4556
4921
  if (body.model !== void 0) existing.model = body.model;
@@ -4587,6 +4952,20 @@ ${user.trimEnd()}
4587
4952
  logger_default.error(`failed to get pending deliveries for ${baseName}`, logger_default.errorData(err));
4588
4953
  return c.json({ error: "Failed to retrieve pending messages" }, 500);
4589
4954
  }
4955
+ }).post("/:name/ai/complete", requireSelf(), async (c) => {
4956
+ const body = await c.req.json();
4957
+ if (!body.systemPrompt || !body.message) {
4958
+ return c.json({ error: "systemPrompt and message required" }, 400);
4959
+ }
4960
+ const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-VAJT5UBS.js");
4961
+ if (!isAiConfigured()) {
4962
+ return c.json({ error: "AI service not configured" }, 503);
4963
+ }
4964
+ const text = await aiCompleteFn(body.systemPrompt, body.message, body.model);
4965
+ if (text == null) {
4966
+ return c.json({ error: "AI completion failed" }, 502);
4967
+ }
4968
+ return c.json({ text });
4590
4969
  }).post("/:name/events", requireSelf(), async (c) => {
4591
4970
  const name = c.req.param("name");
4592
4971
  const baseName = await getBaseName(name);
@@ -4600,8 +4979,9 @@ ${user.trimEnd()}
4600
4979
  return c.json({ error: "type required" }, 400);
4601
4980
  }
4602
4981
  const db = await getDb();
4982
+ let insertedId;
4603
4983
  try {
4604
- await db.insert(mindHistory).values({
4984
+ const result = await db.insert(mindHistory).values({
4605
4985
  mind: baseName,
4606
4986
  type: body.type,
4607
4987
  session: body.session ?? null,
@@ -4609,7 +4989,8 @@ ${user.trimEnd()}
4609
4989
  message_id: body.messageId ?? null,
4610
4990
  content: body.content ?? null,
4611
4991
  metadata: body.metadata ? JSON.stringify(body.metadata) : null
4612
- });
4992
+ }).returning({ id: mindHistory.id });
4993
+ insertedId = result[0]?.id;
4613
4994
  } catch (err) {
4614
4995
  logger_default.error(`failed to persist event for ${baseName}`, logger_default.errorData(err));
4615
4996
  }
@@ -4640,6 +5021,11 @@ ${user.trimEnd()}
4640
5021
  logger_default.error(`delivery manager sessionDone failed for ${baseName}`, logger_default.errorData(err));
4641
5022
  }
4642
5023
  }
5024
+ if (insertedId != null) {
5025
+ summarizeTurn(baseName, body.session, body.channel, insertedId).catch((err) => {
5026
+ logger_default.error("turn summarization failed", logger_default.errorData(err));
5027
+ });
5028
+ }
4643
5029
  }
4644
5030
  if (body.type === "usage" && body.metadata) {
4645
5031
  const inputTokens = body.metadata.input_tokens ?? 0;
@@ -4731,22 +5117,22 @@ ${user.trimEnd()}
4731
5117
  const db = await getDb();
4732
5118
  const rows = await db.select({
4733
5119
  session: mindHistory.session,
4734
- started_at: sql`MIN(${mindHistory.created_at})`,
4735
- event_count: sql`COUNT(*)`,
4736
- message_count: sql`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
4737
- tool_count: sql`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
4738
- }).from(mindHistory).where(and3(eq4(mindHistory.mind, name), sql`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql`MIN(${mindHistory.created_at}) DESC`);
5120
+ started_at: sql2`MIN(${mindHistory.created_at})`,
5121
+ event_count: sql2`COUNT(*)`,
5122
+ message_count: sql2`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
5123
+ tool_count: sql2`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
5124
+ }).from(mindHistory).where(and4(eq5(mindHistory.mind, name), sql2`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql2`MIN(${mindHistory.created_at}) DESC`);
4739
5125
  return c.json(rows);
4740
5126
  }).get("/:name/history/channels", async (c) => {
4741
5127
  const name = c.req.param("name");
4742
5128
  const db = await getDb();
4743
- const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq4(mindHistory.mind, name));
5129
+ const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq5(mindHistory.mind, name));
4744
5130
  return c.json(rows.map((r) => r.channel));
4745
5131
  }).get("/:name/history/export", async (c) => {
4746
5132
  const name = c.req.param("name");
4747
5133
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
4748
5134
  const db = await getDb();
4749
- const rows = await db.select().from(mindHistory).where(eq4(mindHistory.mind, name));
5135
+ const rows = await db.select().from(mindHistory).where(eq5(mindHistory.mind, name));
4750
5136
  return c.json(rows);
4751
5137
  }).get("/:name/history", async (c) => {
4752
5138
  const name = c.req.param("name");
@@ -4756,391 +5142,27 @@ ${user.trimEnd()}
4756
5142
  const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
4757
5143
  const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
4758
5144
  const db = await getDb();
4759
- const conditions = [eq4(mindHistory.mind, name)];
5145
+ const conditions = [eq5(mindHistory.mind, name)];
4760
5146
  if (channel) {
4761
- conditions.push(eq4(mindHistory.channel, channel));
5147
+ conditions.push(eq5(mindHistory.channel, channel));
4762
5148
  }
4763
5149
  if (session) {
4764
- conditions.push(eq4(mindHistory.session, session));
5150
+ conditions.push(eq5(mindHistory.session, session));
4765
5151
  }
4766
5152
  if (!full) {
4767
- conditions.push(sql`${mindHistory.type} IN ('inbound', 'outbound')`);
5153
+ conditions.push(sql2`${mindHistory.type} IN ('inbound', 'outbound', 'summary')`);
4768
5154
  }
4769
- const rows = await db.select().from(mindHistory).where(and3(...conditions)).orderBy(desc2(mindHistory.created_at)).limit(limit).offset(offset);
5155
+ const rows = await db.select().from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.created_at)).limit(limit).offset(offset);
4770
5156
  return c.json(rows);
4771
5157
  });
4772
- var minds_default = app11;
4773
-
4774
- // src/web/api/notes.ts
4775
- import { zValidator as zValidator5 } from "@hono/zod-validator";
4776
- import { Hono as Hono12 } from "hono";
4777
- import { z as z5 } from "zod";
4778
-
4779
- // src/lib/notes.ts
4780
- import { and as and4, count, desc as desc3, eq as eq5, inArray, sql as sql2 } from "drizzle-orm";
4781
- async function createNote(authorId, title, content, replyToId) {
4782
- const db = await getDb();
4783
- let slug = slugify(title) || "untitled";
4784
- const existing = await db.select({ slug: notes.slug }).from(notes).where(eq5(notes.author_id, authorId)).all();
4785
- const existingSlugs = new Set(existing.map((r) => r.slug));
4786
- if (existingSlugs.has(slug)) {
4787
- let i = 2;
4788
- while (existingSlugs.has(`${slug}-${i}`)) i++;
4789
- slug = `${slug}-${i}`;
4790
- }
4791
- const [row] = await db.insert(notes).values({ author_id: authorId, title, slug, content, reply_to_id: replyToId ?? null }).returning();
4792
- const author = await db.select().from(users).where(eq5(users.id, authorId)).get();
4793
- return {
4794
- ...row,
4795
- author_username: author?.username ?? "unknown",
4796
- author_display_name: author?.display_name ?? null,
4797
- comment_count: 0
4798
- };
4799
- }
4800
- async function getNote(authorUsername, slug) {
4801
- const db = await getDb();
4802
- const row = await db.select({
4803
- id: notes.id,
4804
- author_id: notes.author_id,
4805
- title: notes.title,
4806
- slug: notes.slug,
4807
- content: notes.content,
4808
- reply_to_id: notes.reply_to_id,
4809
- created_at: notes.created_at,
4810
- updated_at: notes.updated_at,
4811
- author_username: users.username,
4812
- author_display_name: users.display_name
4813
- }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(and4(eq5(users.username, authorUsername), eq5(notes.slug, slug))).get();
4814
- if (!row) return null;
4815
- const comments = await getComments(row.id);
4816
- const reactions = await getReactions(row.id);
4817
- let reply_to = null;
4818
- if (row.reply_to_id) {
4819
- const parent = await db.select({
4820
- title: notes.title,
4821
- slug: notes.slug,
4822
- author_username: users.username
4823
- }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(eq5(notes.id, row.reply_to_id)).get();
4824
- if (parent) {
4825
- reply_to = parent;
4826
- }
4827
- }
4828
- const replies = await db.select({
4829
- author_username: users.username,
4830
- slug: notes.slug,
4831
- title: notes.title,
4832
- created_at: notes.created_at
4833
- }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(eq5(notes.reply_to_id, row.id)).orderBy(notes.created_at).all();
4834
- return { ...row, comment_count: comments.length, comments, reactions, reply_to, replies };
4835
- }
4836
- async function listNotes(opts) {
4837
- const db = await getDb();
4838
- const limit = opts?.limit ?? 50;
4839
- const offset = opts?.offset ?? 0;
4840
- const conditions = [];
4841
- if (opts?.authorUsername) {
4842
- conditions.push(eq5(users.username, opts.authorUsername));
4843
- }
4844
- const rows = await db.select({
4845
- id: notes.id,
4846
- author_id: notes.author_id,
4847
- title: notes.title,
4848
- slug: notes.slug,
4849
- content: notes.content,
4850
- reply_to_id: notes.reply_to_id,
4851
- created_at: notes.created_at,
4852
- updated_at: notes.updated_at,
4853
- author_username: users.username,
4854
- author_display_name: users.display_name
4855
- }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(conditions.length > 0 ? and4(...conditions) : void 0).orderBy(desc3(notes.created_at)).limit(limit).offset(offset).all();
4856
- const noteIds = rows.map((r) => r.id);
4857
- if (noteIds.length === 0) return [];
4858
- const commentCounts = await db.select({
4859
- note_id: noteComments.note_id,
4860
- count: count()
4861
- }).from(noteComments).where(inArray(noteComments.note_id, noteIds)).groupBy(noteComments.note_id).all();
4862
- const countMap = new Map(commentCounts.map((r) => [r.note_id, r.count]));
4863
- const allReactions = await db.select({
4864
- note_id: noteReactions.note_id,
4865
- emoji: noteReactions.emoji,
4866
- count: count()
4867
- }).from(noteReactions).where(inArray(noteReactions.note_id, noteIds)).groupBy(noteReactions.note_id, noteReactions.emoji).all();
4868
- const reactionMap = /* @__PURE__ */ new Map();
4869
- for (const r of allReactions) {
4870
- if (!reactionMap.has(r.note_id)) reactionMap.set(r.note_id, []);
4871
- reactionMap.get(r.note_id).push({ emoji: r.emoji, count: r.count });
4872
- }
4873
- const replyToIds = [...new Set(rows.filter((r) => r.reply_to_id).map((r) => r.reply_to_id))];
4874
- const replyToMap = /* @__PURE__ */ new Map();
4875
- if (replyToIds.length > 0) {
4876
- const parents = await db.select({
4877
- id: notes.id,
4878
- title: notes.title,
4879
- slug: notes.slug,
4880
- author_username: users.username
4881
- }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(inArray(notes.id, replyToIds)).all();
4882
- for (const parent of parents) {
4883
- replyToMap.set(parent.id, {
4884
- author_username: parent.author_username,
4885
- slug: parent.slug,
4886
- title: parent.title
4887
- });
4888
- }
4889
- }
4890
- return rows.map((r) => {
4891
- const reactions = reactionMap.get(r.id);
4892
- const topReactions = reactions ? reactions.sort((a, b) => b.count - a.count).slice(0, 3).map((rx) => ({ ...rx, usernames: [] })) : void 0;
4893
- return {
4894
- ...r,
4895
- comment_count: countMap.get(r.id) ?? 0,
4896
- reactions: topReactions,
4897
- reply_to: r.reply_to_id ? replyToMap.get(r.reply_to_id) ?? null : null
4898
- };
4899
- });
4900
- }
4901
- async function updateNote(authorUsername, slug, updates) {
4902
- const db = await getDb();
4903
- const existing = await db.select({ id: notes.id, author_id: notes.author_id }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(and4(eq5(users.username, authorUsername), eq5(notes.slug, slug))).get();
4904
- if (!existing) return null;
4905
- const set = { updated_at: sql2`(datetime('now'))` };
4906
- if (updates.title !== void 0) set.title = updates.title;
4907
- if (updates.content !== void 0) set.content = updates.content;
4908
- await db.update(notes).set(set).where(eq5(notes.id, existing.id));
4909
- return getNote(authorUsername, slug).then((n) => {
4910
- if (!n) return null;
4911
- const { comments, replies, ...note } = n;
4912
- return note;
4913
- });
4914
- }
4915
- async function deleteNote(authorUsername, slug, authorId) {
4916
- const db = await getDb();
4917
- const existing = await db.select({ id: notes.id, author_id: notes.author_id }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(and4(eq5(users.username, authorUsername), eq5(notes.slug, slug))).get();
4918
- if (!existing || existing.author_id !== authorId) return false;
4919
- await db.delete(notes).where(eq5(notes.id, existing.id));
4920
- return true;
4921
- }
4922
- async function addComment(noteId, authorId, content) {
4923
- const db = await getDb();
4924
- const [row] = await db.insert(noteComments).values({ note_id: noteId, author_id: authorId, content }).returning();
4925
- const author = await db.select().from(users).where(eq5(users.id, authorId)).get();
4926
- return {
4927
- ...row,
4928
- author_username: author?.username ?? "unknown",
4929
- author_display_name: author?.display_name ?? null
4930
- };
4931
- }
4932
- async function getComments(noteId) {
4933
- const db = await getDb();
4934
- return db.select({
4935
- id: noteComments.id,
4936
- note_id: noteComments.note_id,
4937
- author_id: noteComments.author_id,
4938
- content: noteComments.content,
4939
- created_at: noteComments.created_at,
4940
- author_username: users.username,
4941
- author_display_name: users.display_name
4942
- }).from(noteComments).innerJoin(users, eq5(noteComments.author_id, users.id)).where(eq5(noteComments.note_id, noteId)).orderBy(noteComments.created_at).all();
4943
- }
4944
- async function deleteComment(commentId, authorId) {
4945
- const db = await getDb();
4946
- const existing = await db.select({ id: noteComments.id, author_id: noteComments.author_id }).from(noteComments).where(eq5(noteComments.id, commentId)).get();
4947
- if (!existing || existing.author_id !== authorId) return false;
4948
- await db.delete(noteComments).where(eq5(noteComments.id, commentId));
4949
- return true;
4950
- }
4951
- async function toggleReaction(noteId, userId, emoji) {
4952
- const db = await getDb();
4953
- const existing = await db.select({ id: noteReactions.id }).from(noteReactions).where(
4954
- and4(
4955
- eq5(noteReactions.note_id, noteId),
4956
- eq5(noteReactions.user_id, userId),
4957
- eq5(noteReactions.emoji, emoji)
4958
- )
4959
- ).get();
4960
- if (existing) {
4961
- await db.delete(noteReactions).where(eq5(noteReactions.id, existing.id));
4962
- return { added: false };
4963
- }
4964
- await db.insert(noteReactions).values({ note_id: noteId, user_id: userId, emoji });
4965
- return { added: true };
4966
- }
4967
- async function getReactions(noteId) {
4968
- const db = await getDb();
4969
- const rows = await db.select({
4970
- emoji: noteReactions.emoji,
4971
- username: users.username
4972
- }).from(noteReactions).innerJoin(users, eq5(noteReactions.user_id, users.id)).where(eq5(noteReactions.note_id, noteId)).orderBy(noteReactions.emoji).all();
4973
- const grouped = /* @__PURE__ */ new Map();
4974
- for (const r of rows) {
4975
- if (!grouped.has(r.emoji)) grouped.set(r.emoji, []);
4976
- grouped.get(r.emoji).push(r.username);
4977
- }
4978
- return [...grouped.entries()].map(([emoji, usernames]) => ({
4979
- emoji,
4980
- count: usernames.length,
4981
- usernames
4982
- }));
4983
- }
4984
- async function resolveNoteId(authorSlug) {
4985
- const [author, slug] = authorSlug.split("/", 2);
4986
- if (!author || !slug) return null;
4987
- const db = await getDb();
4988
- const row = await db.select({ id: notes.id }).from(notes).innerJoin(users, eq5(notes.author_id, users.id)).where(and4(eq5(users.username, author), eq5(notes.slug, slug))).get();
4989
- return row?.id ?? null;
4990
- }
4991
-
4992
- // src/web/api/notes.ts
4993
- var createSchema = z5.object({
4994
- title: z5.string().min(1).max(200),
4995
- content: z5.string().min(1),
4996
- reply_to: z5.string().optional()
4997
- });
4998
- var updateSchema = z5.object({
4999
- title: z5.string().min(1).max(200).optional(),
5000
- content: z5.string().min(1).optional()
5001
- });
5002
- var commentSchema = z5.object({
5003
- content: z5.string().min(1)
5004
- });
5005
- var reactionSchema = z5.object({
5006
- emoji: z5.string().min(1).max(32)
5007
- });
5008
- function resolveUserId(c) {
5009
- const user = c.get("user");
5010
- if (user.id === 0) return null;
5011
- return { id: user.id, username: user.username };
5012
- }
5013
- var app12 = new Hono12().get("/", async (c) => {
5014
- const author = c.req.query("author");
5015
- const limit = c.req.query("limit") ? parseInt(c.req.query("limit"), 10) : void 0;
5016
- const offset = c.req.query("offset") ? parseInt(c.req.query("offset"), 10) : void 0;
5017
- const result = await listNotes({ authorUsername: author, limit, offset });
5018
- return c.json(result);
5019
- }).post("/", zValidator5("json", createSchema), async (c) => {
5020
- const actor = resolveUserId(c);
5021
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
5022
- const { title, content, reply_to } = c.req.valid("json");
5023
- let replyToId;
5024
- if (reply_to) {
5025
- const id = await resolveNoteId(reply_to);
5026
- if (id === null) return c.json({ error: `Reply target not found: ${reply_to}` }, 404);
5027
- replyToId = id;
5028
- }
5029
- const note = await createNote(actor.id, title, content, replyToId);
5030
- const replyInfo = reply_to ? ` (in reply to ${reply_to})` : "";
5031
- announceToSystem(`${actor.username} published a note: ${title}${replyInfo}`).catch((err) => {
5032
- logger_default.warn("failed to announce note to #system", logger_default.errorData(err));
5033
- });
5034
- return c.json(note, 201);
5035
- }).get("/:author/:slug", async (c) => {
5036
- const { author, slug } = c.req.param();
5037
- const note = await getNote(author, slug);
5038
- if (!note) return c.json({ error: "Note not found" }, 404);
5039
- return c.json(note);
5040
- }).put("/:author/:slug", zValidator5("json", updateSchema), async (c) => {
5041
- const actor = resolveUserId(c);
5042
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
5043
- const { author, slug } = c.req.param();
5044
- if (actor.username !== author) return c.json({ error: "Forbidden" }, 403);
5045
- const updates = c.req.valid("json");
5046
- const note = await updateNote(author, slug, updates);
5047
- if (!note) return c.json({ error: "Note not found" }, 404);
5048
- return c.json(note);
5049
- }).delete("/:author/:slug", async (c) => {
5050
- const actor = resolveUserId(c);
5051
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
5052
- const { author, slug } = c.req.param();
5053
- const deleted = await deleteNote(author, slug, actor.id);
5054
- if (!deleted) return c.json({ error: "Note not found or not authorized" }, 404);
5055
- return c.json({ ok: true });
5056
- }).post("/:author/:slug/reactions", zValidator5("json", reactionSchema), async (c) => {
5057
- const actor = resolveUserId(c);
5058
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
5059
- const { author, slug } = c.req.param();
5060
- const note = await getNote(author, slug);
5061
- if (!note) return c.json({ error: "Note not found" }, 404);
5062
- const { emoji } = c.req.valid("json");
5063
- const result = await toggleReaction(note.id, actor.id, emoji);
5064
- const reactions = await getReactions(note.id);
5065
- return c.json({ ...result, reactions });
5066
- }).post("/:author/:slug/comments", zValidator5("json", commentSchema), async (c) => {
5067
- const actor = resolveUserId(c);
5068
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
5069
- const { author, slug } = c.req.param();
5070
- const note = await getNote(author, slug);
5071
- if (!note) return c.json({ error: "Note not found" }, 404);
5072
- const { content } = c.req.valid("json");
5073
- const comment = await addComment(note.id, actor.id, content);
5074
- return c.json(comment, 201);
5075
- }).delete("/:author/:slug/comments/:id", async (c) => {
5076
- const actor = resolveUserId(c);
5077
- if (!actor) return c.json({ error: "Unauthorized" }, 401);
5078
- const commentId = parseInt(c.req.param("id"), 10);
5079
- if (Number.isNaN(commentId)) return c.json({ error: "Invalid comment ID" }, 400);
5080
- const deleted = await deleteComment(commentId, actor.id);
5081
- if (!deleted) return c.json({ error: "Comment not found or not authorized" }, 404);
5082
- return c.json({ ok: true });
5083
- });
5084
- var notes_default = app12;
5085
-
5086
- // src/web/api/pages.ts
5087
- import { readFile as readFile2, stat as stat2 } from "fs/promises";
5088
- import { extname as extname3, resolve as resolve16 } from "path";
5089
- import { Hono as Hono13 } from "hono";
5090
- var MIME_TYPES = {
5091
- ".html": "text/html",
5092
- ".js": "application/javascript",
5093
- ".css": "text/css",
5094
- ".json": "application/json",
5095
- ".svg": "image/svg+xml",
5096
- ".png": "image/png",
5097
- ".jpg": "image/jpeg",
5098
- ".jpeg": "image/jpeg",
5099
- ".gif": "image/gif",
5100
- ".ico": "image/x-icon",
5101
- ".woff": "font/woff",
5102
- ".woff2": "font/woff2",
5103
- ".txt": "text/plain",
5104
- ".xml": "application/xml"
5105
- };
5106
- var app13 = new Hono13().get("/:name/*", async (c) => {
5107
- const name = c.req.param("name");
5108
- let pagesRoot;
5109
- if (name === "_system") {
5110
- pagesRoot = resolve16(voluteHome(), "shared", "pages");
5111
- } else {
5112
- if (!await findMind(name)) return c.text("Not found", 404);
5113
- pagesRoot = resolve16(mindDir(name), "home", "public", "pages");
5114
- }
5115
- const wildcard = c.req.path.replace(`/pages/${name}`, "") || "/";
5116
- const requestedPath = resolve16(pagesRoot, wildcard.slice(1));
5117
- if (!requestedPath.startsWith(pagesRoot)) return c.text("Forbidden", 403);
5118
- let fileStat = await stat2(requestedPath).catch(() => null);
5119
- if (fileStat?.isDirectory()) {
5120
- const indexPath = resolve16(requestedPath, "index.html");
5121
- fileStat = await stat2(indexPath).catch(() => null);
5122
- if (fileStat?.isFile()) {
5123
- const body = await readFile2(indexPath);
5124
- return c.body(body, 200, { "Content-Type": "text/html" });
5125
- }
5126
- return c.text("Not found", 404);
5127
- }
5128
- if (fileStat?.isFile()) {
5129
- const ext = extname3(requestedPath);
5130
- const mime = MIME_TYPES[ext] || "application/octet-stream";
5131
- const body = await readFile2(requestedPath);
5132
- return c.body(body, 200, { "Content-Type": mime });
5133
- }
5134
- return c.text("Not found", 404);
5135
- });
5136
- var pages_default = app13;
5158
+ var minds_default = app12;
5137
5159
 
5138
5160
  // src/web/api/prompts.ts
5139
- import { zValidator as zValidator6 } from "@hono/zod-validator";
5161
+ import { zValidator as zValidator5 } from "@hono/zod-validator";
5140
5162
  import { eq as eq6, sql as sql3 } from "drizzle-orm";
5141
- import { Hono as Hono14 } from "hono";
5142
- import { z as z6 } from "zod";
5143
- var app14 = new Hono14().get("/", async (c) => {
5163
+ import { Hono as Hono13 } from "hono";
5164
+ import { z as z5 } from "zod";
5165
+ var app13 = new Hono13().get("/", async (c) => {
5144
5166
  let rows;
5145
5167
  try {
5146
5168
  const db = await getDb();
@@ -5163,7 +5185,7 @@ var app14 = new Hono14().get("/", async (c) => {
5163
5185
  };
5164
5186
  });
5165
5187
  return c.json(prompts);
5166
- }).put("/:key", requireAdmin, zValidator6("json", z6.object({ content: z6.string() })), async (c) => {
5188
+ }).put("/:key", requireAdmin, zValidator5("json", z5.object({ content: z5.string() })), async (c) => {
5167
5189
  const key = c.req.param("key");
5168
5190
  if (!PROMPT_KEYS.includes(key)) {
5169
5191
  return c.json({ error: "Unknown prompt key" }, 404);
@@ -5184,12 +5206,12 @@ var app14 = new Hono14().get("/", async (c) => {
5184
5206
  await db.delete(systemPrompts).where(eq6(systemPrompts.key, key));
5185
5207
  return c.json({ ok: true });
5186
5208
  });
5187
- var prompts_default = app14;
5209
+ var prompts_default = app13;
5188
5210
 
5189
5211
  // src/web/api/public-files.ts
5190
- import { readdir as readdir2, readFile as readFile3, stat as stat3 } from "fs/promises";
5191
- import { extname as extname4, resolve as resolve17 } from "path";
5192
- import { Hono as Hono15 } from "hono";
5212
+ import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
5213
+ import { extname as extname3, resolve as resolve17 } from "path";
5214
+ import { Hono as Hono14 } from "hono";
5193
5215
  var MAX_FILE_SIZE = 50 * 1024 * 1024;
5194
5216
  async function resolvePublicRoot(name) {
5195
5217
  if (name === "_system") return resolve17(voluteHome(), "shared");
@@ -5199,7 +5221,7 @@ async function resolvePublicRoot(name) {
5199
5221
  function hasDotSegment(relativePath) {
5200
5222
  return relativePath.split("/").some((seg) => seg.startsWith("."));
5201
5223
  }
5202
- var MIME_TYPES2 = {
5224
+ var MIME_TYPES = {
5203
5225
  ".html": "text/html",
5204
5226
  ".js": "application/javascript",
5205
5227
  ".css": "text/css",
@@ -5230,7 +5252,7 @@ async function listDir(dirPath) {
5230
5252
  type: e.isDirectory() ? "directory" : "file"
5231
5253
  }));
5232
5254
  }
5233
- var app15 = new Hono15().get("/:name/", async (c) => {
5255
+ var app14 = new Hono14().get("/:name/", async (c) => {
5234
5256
  const name = c.req.param("name");
5235
5257
  const publicRoot = await resolvePublicRoot(name);
5236
5258
  if (!publicRoot) return c.json({ error: "Not found" }, 404);
@@ -5246,7 +5268,7 @@ var app15 = new Hono15().get("/:name/", async (c) => {
5246
5268
  if (hasDotSegment(relativePath)) return c.text("Forbidden", 403);
5247
5269
  let fileStat;
5248
5270
  try {
5249
- fileStat = await stat3(requestedPath);
5271
+ fileStat = await stat2(requestedPath);
5250
5272
  } catch (err) {
5251
5273
  if (err?.code === "ENOENT") return c.text("Not found", 404);
5252
5274
  if (err?.code === "EACCES") return c.text("Forbidden", 403);
@@ -5260,10 +5282,10 @@ var app15 = new Hono15().get("/:name/", async (c) => {
5260
5282
  }
5261
5283
  if (fileStat.isFile()) {
5262
5284
  if (fileStat.size > MAX_FILE_SIZE) return c.text("File too large", 413);
5263
- const ext = extname4(requestedPath);
5264
- const mime = MIME_TYPES2[ext] || "application/octet-stream";
5285
+ const ext = extname3(requestedPath);
5286
+ const mime = MIME_TYPES[ext] || "application/octet-stream";
5265
5287
  try {
5266
- const body = await readFile3(requestedPath);
5288
+ const body = await readFile2(requestedPath);
5267
5289
  return c.body(body, 200, { "Content-Type": mime });
5268
5290
  } catch (err) {
5269
5291
  if (err?.code === "ENOENT") return c.text("Not found", 404);
@@ -5273,11 +5295,11 @@ var app15 = new Hono15().get("/:name/", async (c) => {
5273
5295
  }
5274
5296
  return c.text("Not found", 404);
5275
5297
  });
5276
- var public_files_default = app15;
5298
+ var public_files_default = app14;
5277
5299
 
5278
5300
  // src/web/api/schedules.ts
5279
5301
  import { CronExpressionParser } from "cron-parser";
5280
- import { Hono as Hono16 } from "hono";
5302
+ import { Hono as Hono15 } from "hono";
5281
5303
  var slog = logger_default.child("schedules");
5282
5304
  function readSchedules(name) {
5283
5305
  return readVoluteConfig(mindDir(name))?.schedules ?? [];
@@ -5295,7 +5317,7 @@ function writeSchedules(name, schedules) {
5295
5317
  data: { schedules }
5296
5318
  });
5297
5319
  }
5298
- var app16 = new Hono16().get("/:name/clock/status", async (c) => {
5320
+ var app15 = new Hono15().get("/:name/clock/status", async (c) => {
5299
5321
  const name = c.req.param("name");
5300
5322
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
5301
5323
  const sleepManager = getSleepManagerIfReady();
@@ -5337,6 +5359,9 @@ var app16 = new Hono16().get("/:name/clock/status", async (c) => {
5337
5359
  if (entry.stage === "seed")
5338
5360
  return c.json({ error: "Seed minds cannot use schedules \u2014 sprout first" }, 403);
5339
5361
  const body = await c.req.json();
5362
+ if (!body.id) {
5363
+ return c.json({ error: "id is required (a descriptive name for this schedule)" }, 400);
5364
+ }
5340
5365
  if (!body.cron && !body.fireAt) {
5341
5366
  return c.json({ error: "cron or fireAt is required" }, 400);
5342
5367
  }
@@ -5368,7 +5393,7 @@ var app16 = new Hono16().get("/:name/clock/status", async (c) => {
5368
5393
  );
5369
5394
  }
5370
5395
  const schedules = readSchedules(name);
5371
- const id = body.id || `schedule-${Date.now()}`;
5396
+ const id = body.id;
5372
5397
  if (schedules.some((s) => s.id === id)) {
5373
5398
  return c.json({ error: `Schedule "${id}" already exists` }, 409);
5374
5399
  }
@@ -5468,11 +5493,11 @@ var app16 = new Hono16().get("/:name/clock/status", async (c) => {
5468
5493
  return c.json({ error: "Failed to reach mind" }, 502);
5469
5494
  }
5470
5495
  });
5471
- var schedules_default = app16;
5496
+ var schedules_default = app15;
5472
5497
 
5473
5498
  // src/web/api/shared.ts
5474
- import { Hono as Hono17 } from "hono";
5475
- var app17 = new Hono17().post("/:name/shared/merge", requireAdmin, async (c) => {
5499
+ import { Hono as Hono16 } from "hono";
5500
+ var app16 = new Hono16().post("/:name/shared/merge", requireAdmin, async (c) => {
5476
5501
  const name = c.req.param("name");
5477
5502
  const entry = await findMind(name);
5478
5503
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -5490,24 +5515,63 @@ var app17 = new Hono17().post("/:name/shared/merge", requireAdmin, async (c) =>
5490
5515
  return c.json({ error: err instanceof Error ? err.message : "Merge failed" }, 500);
5491
5516
  }
5492
5517
  });
5493
- var shared_default = app17;
5518
+ var shared_default = app16;
5494
5519
 
5495
5520
  // src/web/api/skills.ts
5496
- import { existsSync as existsSync14, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync5 } from "fs";
5521
+ import { existsSync as existsSync15, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync6 } from "fs";
5497
5522
  import { tmpdir } from "os";
5498
5523
  import { join, resolve as resolve18 } from "path";
5499
5524
  import AdmZip from "adm-zip";
5500
- import { Hono as Hono18 } from "hono";
5501
- var app18 = new Hono18().get("/", async (c) => {
5525
+ import { Hono as Hono17 } from "hono";
5526
+ var app17 = new Hono17().get("/", async (c) => {
5502
5527
  const skills = await listSharedSkills();
5503
5528
  return c.json(skills);
5504
- }).get("/:id", async (c) => {
5505
- const id = c.req.param("id");
5506
- const skill = await getSharedSkill(id);
5507
- if (!skill) return c.json({ error: "Skill not found" }, 404);
5508
- const dir = join(sharedSkillsDir(), id);
5509
- const files = listFilesRecursive(dir);
5510
- return c.json({ ...skill, files });
5529
+ }).get("/defaults/list", async (c) => {
5530
+ return c.json({ skills: getStandardSkillsWithExtensions() });
5531
+ }).put("/defaults/list", requireAdmin, async (c) => {
5532
+ const body = await c.req.json();
5533
+ if (!Array.isArray(body.skills) || !body.skills.every((s) => typeof s === "string")) {
5534
+ return c.json({ error: "body.skills must be a string array" }, 400);
5535
+ }
5536
+ const config = readGlobalConfig();
5537
+ const allStandard = /* @__PURE__ */ new Set([...STANDARD_SKILLS, ...getExtensionStandardSkills()]);
5538
+ const newSet = new Set(body.skills);
5539
+ const removed = [...allStandard].filter((s) => !newSet.has(s));
5540
+ const prevRemoved = new Set(config.removedDefaultSkills ?? []);
5541
+ for (const s of removed) prevRemoved.add(s);
5542
+ for (const s of body.skills) prevRemoved.delete(s);
5543
+ writeGlobalConfig({
5544
+ ...config,
5545
+ defaultSkills: body.skills,
5546
+ removedDefaultSkills: [...prevRemoved]
5547
+ });
5548
+ return c.json({ skills: body.skills });
5549
+ }).post("/defaults/list", requireAdmin, async (c) => {
5550
+ const body = await c.req.json();
5551
+ if (typeof body.skill !== "string" || !body.skill) {
5552
+ return c.json({ error: "body.skill must be a non-empty string" }, 400);
5553
+ }
5554
+ const current = getStandardSkillsWithExtensions();
5555
+ if (current.includes(body.skill)) {
5556
+ return c.json({ error: `"${body.skill}" is already a default skill` }, 409);
5557
+ }
5558
+ const config = readGlobalConfig();
5559
+ const updated = [...current, body.skill];
5560
+ const removed = (config.removedDefaultSkills ?? []).filter((s) => s !== body.skill);
5561
+ writeGlobalConfig({ ...config, defaultSkills: updated, removedDefaultSkills: removed });
5562
+ return c.json({ skills: updated });
5563
+ }).delete("/defaults/list/:skill", requireAdmin, async (c) => {
5564
+ const skill = c.req.param("skill");
5565
+ const current = getStandardSkillsWithExtensions();
5566
+ if (!current.includes(skill)) {
5567
+ return c.json({ error: `"${skill}" is not a default skill` }, 404);
5568
+ }
5569
+ const config = readGlobalConfig();
5570
+ const updated = current.filter((s) => s !== skill);
5571
+ const removed = new Set(config.removedDefaultSkills ?? []);
5572
+ removed.add(skill);
5573
+ writeGlobalConfig({ ...config, defaultSkills: updated, removedDefaultSkills: [...removed] });
5574
+ return c.json({ skills: updated });
5511
5575
  }).post("/upload", requireAdmin, async (c) => {
5512
5576
  const body = await c.req.parseBody();
5513
5577
  const file = body.file;
@@ -5529,12 +5593,12 @@ var app18 = new Hono18().get("/", async (c) => {
5529
5593
  }
5530
5594
  zip.extractAllTo(tmpDir, true);
5531
5595
  let skillDir = null;
5532
- if (existsSync14(join(tmpDir, "SKILL.md"))) {
5596
+ if (existsSync15(join(tmpDir, "SKILL.md"))) {
5533
5597
  skillDir = tmpDir;
5534
5598
  } else {
5535
5599
  const entries = readdirSync4(tmpDir, { withFileTypes: true }).filter((e) => e.isDirectory());
5536
5600
  for (const entry of entries) {
5537
- if (existsSync14(join(tmpDir, entry.name, "SKILL.md"))) {
5601
+ if (existsSync15(join(tmpDir, entry.name, "SKILL.md"))) {
5538
5602
  skillDir = join(tmpDir, entry.name);
5539
5603
  break;
5540
5604
  }
@@ -5551,8 +5615,15 @@ var app18 = new Hono18().get("/", async (c) => {
5551
5615
  }
5552
5616
  throw e;
5553
5617
  } finally {
5554
- rmSync5(tmpDir, { recursive: true, force: true });
5618
+ rmSync6(tmpDir, { recursive: true, force: true });
5555
5619
  }
5620
+ }).get("/:id", async (c) => {
5621
+ const id = c.req.param("id");
5622
+ const skill = await getSharedSkill(id);
5623
+ if (!skill) return c.json({ error: "Skill not found" }, 404);
5624
+ const dir = join(sharedSkillsDir(), id);
5625
+ const files = listFilesRecursive(dir);
5626
+ return c.json({ ...skill, files });
5556
5627
  }).delete("/:id", requireAdmin, async (c) => {
5557
5628
  const id = c.req.param("id");
5558
5629
  try {
@@ -5563,15 +5634,16 @@ var app18 = new Hono18().get("/", async (c) => {
5563
5634
  }
5564
5635
  return c.json({ ok: true });
5565
5636
  });
5566
- var skills_default = app18;
5637
+ var skills_default = app17;
5567
5638
 
5568
5639
  // src/web/api/system.ts
5569
- import { zValidator as zValidator7 } from "@hono/zod-validator";
5570
- import { Hono as Hono19 } from "hono";
5640
+ import { zValidator as zValidator6 } from "@hono/zod-validator";
5641
+ import { getOAuthProvider, getOAuthProviders, getProviders } from "@mariozechner/pi-ai";
5642
+ import { Hono as Hono18 } from "hono";
5571
5643
  import { streamSSE as streamSSE3 } from "hono/streaming";
5572
- import { z as z7 } from "zod";
5644
+ import { z as z6 } from "zod";
5573
5645
  var DEFAULT_API_URL = "https://volute.systems";
5574
- var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
5646
+ var app18 = new Hono18().post("/restart", requireAdmin, (c) => {
5575
5647
  setTimeout(() => process.exit(1), 200);
5576
5648
  return c.json({ ok: true });
5577
5649
  }).post("/stop", requireAdmin, (c) => {
@@ -5601,7 +5673,7 @@ var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
5601
5673
  }).post(
5602
5674
  "/register",
5603
5675
  requireAdmin,
5604
- zValidator7("json", z7.object({ name: z7.string().min(1) })),
5676
+ zValidator6("json", z6.object({ name: z6.string().min(1) })),
5605
5677
  async (c) => {
5606
5678
  const existing = readSystemsConfig();
5607
5679
  if (existing) {
@@ -5640,7 +5712,7 @@ var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
5640
5712
  ).post(
5641
5713
  "/login",
5642
5714
  requireAdmin,
5643
- zValidator7("json", z7.object({ key: z7.string().min(1) })),
5715
+ zValidator6("json", z6.object({ key: z6.string().min(1) })),
5644
5716
  async (c) => {
5645
5717
  const existing = readSystemsConfig();
5646
5718
  if (existing) {
@@ -5708,19 +5780,173 @@ var app19 = new Hono19().post("/restart", requireAdmin, (c) => {
5708
5780
  } catch (err) {
5709
5781
  return c.json({ error: `Connection failed: ${err.message}` }, 502);
5710
5782
  }
5783
+ }).get("/ai/providers", requireAdmin, (c) => {
5784
+ const allProviders = getProviders();
5785
+ const oauthProviders = getOAuthProviders();
5786
+ const oauthMap = new Map(oauthProviders.map((p) => [p.id, p]));
5787
+ const ai = getAiConfig();
5788
+ const result = allProviders.map((id) => {
5789
+ const oauth = oauthMap.get(id);
5790
+ const providerConfig = ai?.providers[id];
5791
+ const configured = !!(providerConfig?.apiKey || providerConfig?.oauth);
5792
+ let authMethod = null;
5793
+ if (providerConfig?.oauth) authMethod = "oauth";
5794
+ else if (providerConfig?.apiKey) authMethod = "api_key";
5795
+ return {
5796
+ id,
5797
+ oauth: !!oauth,
5798
+ oauthName: oauth?.name,
5799
+ usesCallbackServer: !!oauth?.usesCallbackServer,
5800
+ configured,
5801
+ authMethod
5802
+ };
5803
+ });
5804
+ return c.json(result);
5805
+ }).get("/ai/models", requireAdmin, (c) => {
5806
+ const models = getAvailableModels();
5807
+ const enabled = new Set(getEnabledModels());
5808
+ return c.json(
5809
+ models.map((m) => ({
5810
+ id: m.id,
5811
+ name: m.name,
5812
+ provider: m.provider,
5813
+ contextWindow: m.contextWindow,
5814
+ maxTokens: m.maxTokens,
5815
+ enabled: enabled.has(m.id)
5816
+ }))
5817
+ );
5818
+ }).put(
5819
+ "/ai/models",
5820
+ requireAdmin,
5821
+ zValidator6("json", z6.object({ models: z6.array(z6.string()) })),
5822
+ (c) => {
5823
+ const { models } = c.req.valid("json");
5824
+ setEnabledModels(models);
5825
+ return c.json({ ok: true });
5826
+ }
5827
+ ).put(
5828
+ "/ai/providers/:id",
5829
+ requireAdmin,
5830
+ zValidator6("json", z6.object({ apiKey: z6.string().min(1) })),
5831
+ (c) => {
5832
+ const id = c.req.param("id");
5833
+ const { apiKey } = c.req.valid("json");
5834
+ saveProviderConfig(id, { apiKey });
5835
+ return c.json({ ok: true });
5836
+ }
5837
+ ).delete("/ai/providers/:id", requireAdmin, (c) => {
5838
+ const id = c.req.param("id");
5839
+ removeProviderConfig(id);
5840
+ return c.json({ ok: true });
5841
+ }).delete("/ai", requireAdmin, (c) => {
5842
+ removeAiConfig();
5843
+ return c.json({ ok: true });
5844
+ }).post(
5845
+ "/ai/oauth/start",
5846
+ requireAdmin,
5847
+ zValidator6("json", z6.object({ provider: z6.string().min(1) })),
5848
+ async (c) => {
5849
+ const { provider } = c.req.valid("json");
5850
+ const oauthProvider = getOAuthProvider(provider);
5851
+ if (!oauthProvider) {
5852
+ return c.json({ error: `OAuth not supported for provider: ${provider}` }, 400);
5853
+ }
5854
+ cleanupOAuthFlows();
5855
+ const flowId = crypto.randomUUID();
5856
+ const needsManualCode = !!oauthProvider.usesCallbackServer;
5857
+ const flow = {
5858
+ status: "pending",
5859
+ needsManualCode,
5860
+ createdAt: Date.now()
5861
+ };
5862
+ oauthFlows.set(flowId, flow);
5863
+ const promptPromise = needsManualCode ? new Promise((resolve23) => {
5864
+ flow.resolveCode = resolve23;
5865
+ }) : void 0;
5866
+ oauthProvider.login({
5867
+ onAuth: (info) => {
5868
+ const existing = oauthFlows.get(flowId);
5869
+ if (existing)
5870
+ Object.assign(existing, { url: info.url, instructions: info.instructions });
5871
+ },
5872
+ onPrompt: async (_prompt) => {
5873
+ if (promptPromise) {
5874
+ const existing = oauthFlows.get(flowId);
5875
+ if (existing) existing.waitingForCode = true;
5876
+ return promptPromise;
5877
+ }
5878
+ return "";
5879
+ },
5880
+ onManualCodeInput: needsManualCode ? () => promptPromise : void 0
5881
+ }).then((credentials) => {
5882
+ saveProviderConfig(provider, { oauth: credentials });
5883
+ const existing = oauthFlows.get(flowId);
5884
+ if (existing) existing.status = "complete";
5885
+ }).catch((err) => {
5886
+ const existing = oauthFlows.get(flowId);
5887
+ if (existing) {
5888
+ existing.status = "error";
5889
+ existing.error = err instanceof Error ? err.message : String(err);
5890
+ }
5891
+ });
5892
+ await new Promise((resolve23) => setTimeout(resolve23, 2e3));
5893
+ const state = oauthFlows.get(flowId);
5894
+ return c.json({
5895
+ flowId,
5896
+ url: state.url,
5897
+ instructions: state.instructions,
5898
+ needsManualCode
5899
+ });
5900
+ }
5901
+ ).post(
5902
+ "/ai/oauth/code/:flowId",
5903
+ requireAdmin,
5904
+ zValidator6("json", z6.object({ code: z6.string().min(1) })),
5905
+ async (c) => {
5906
+ const flowId = c.req.param("flowId");
5907
+ const flow = oauthFlows.get(flowId);
5908
+ if (!flow) return c.json({ error: "Flow not found" }, 404);
5909
+ if (!flow.resolveCode) return c.json({ error: "Flow does not accept manual code" }, 400);
5910
+ const { code } = c.req.valid("json");
5911
+ flow.resolveCode(code.trim());
5912
+ return c.json({ ok: true });
5913
+ }
5914
+ ).get("/ai/oauth/status/:flowId", requireAdmin, (c) => {
5915
+ const flowId = c.req.param("flowId");
5916
+ const flow = oauthFlows.get(flowId);
5917
+ if (!flow) return c.json({ error: "Flow not found" }, 404);
5918
+ const result = {
5919
+ status: flow.status,
5920
+ waitingForCode: flow.waitingForCode
5921
+ };
5922
+ if (flow.error) result.error = flow.error;
5923
+ if (flow.status !== "pending") {
5924
+ setTimeout(() => oauthFlows.delete(flowId), 3e4);
5925
+ }
5926
+ return c.json(result);
5711
5927
  });
5712
- var system_default = app19;
5928
+ var oauthFlows = /* @__PURE__ */ new Map();
5929
+ var OAUTH_FLOW_TTL_MS = 10 * 60 * 1e3;
5930
+ function cleanupOAuthFlows() {
5931
+ const now = Date.now();
5932
+ for (const [id, flow] of oauthFlows) {
5933
+ if (now - flow.createdAt > OAUTH_FLOW_TTL_MS) {
5934
+ oauthFlows.delete(id);
5935
+ }
5936
+ }
5937
+ }
5938
+ var system_default = app18;
5713
5939
 
5714
5940
  // src/web/api/typing.ts
5715
- import { zValidator as zValidator8 } from "@hono/zod-validator";
5716
- import { Hono as Hono20 } from "hono";
5717
- import { z as z8 } from "zod";
5718
- var typingSchema = z8.object({
5719
- channel: z8.string().min(1),
5720
- sender: z8.string().min(1),
5721
- active: z8.boolean()
5941
+ import { zValidator as zValidator7 } from "@hono/zod-validator";
5942
+ import { Hono as Hono19 } from "hono";
5943
+ import { z as z7 } from "zod";
5944
+ var typingSchema = z7.object({
5945
+ channel: z7.string().min(1),
5946
+ sender: z7.string().min(1),
5947
+ active: z7.boolean()
5722
5948
  });
5723
- var app20 = new Hono20().post("/:name/typing", zValidator8("json", typingSchema), (c) => {
5949
+ var app19 = new Hono19().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
5724
5950
  const { channel, sender, active } = c.req.valid("json");
5725
5951
  const map = getTypingMap();
5726
5952
  if (active) {
@@ -5742,13 +5968,13 @@ var app20 = new Hono20().post("/:name/typing", zValidator8("json", typingSchema)
5742
5968
  const map = getTypingMap();
5743
5969
  return c.json({ typing: map.get(channel) });
5744
5970
  });
5745
- var typing_default = app20;
5971
+ var typing_default = app19;
5746
5972
 
5747
5973
  // src/web/api/update.ts
5748
5974
  import { spawn as spawn3 } from "child_process";
5749
- import { Hono as Hono21 } from "hono";
5975
+ import { Hono as Hono20 } from "hono";
5750
5976
  var bin;
5751
- var app21 = new Hono21().get("/update", async (c) => {
5977
+ var app20 = new Hono20().get("/update", async (c) => {
5752
5978
  const result = await checkForUpdate();
5753
5979
  return c.json(result);
5754
5980
  }).post("/update", requireAdmin, async (c) => {
@@ -5763,21 +5989,21 @@ var app21 = new Hono21().get("/update", async (c) => {
5763
5989
  child.unref();
5764
5990
  return c.json({ ok: true, message: "Updating..." });
5765
5991
  });
5766
- var update_default = app21;
5992
+ var update_default = app20;
5767
5993
 
5768
5994
  // src/web/api/v1/chat.ts
5769
- import { zValidator as zValidator9 } from "@hono/zod-validator";
5770
- import { Hono as Hono22 } from "hono";
5995
+ import { zValidator as zValidator8 } from "@hono/zod-validator";
5996
+ import { Hono as Hono21 } from "hono";
5771
5997
  import { streamSSE as streamSSE4 } from "hono/streaming";
5772
- import { z as z9 } from "zod";
5998
+ import { z as z8 } from "zod";
5773
5999
  async function fanOutToMinds(opts) {
5774
6000
  const participants = await getParticipants(opts.conversationId);
5775
6001
  const mindParticipants = participants.filter((p) => p.userType === "mind");
5776
6002
  const participantNames = participants.map((p) => p.username);
5777
6003
  const isDM = opts.isDM ?? participants.length === 2;
5778
6004
  const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "channel");
5779
- const { getMindManager: getMindManager2 } = await import("./mind-manager-P66HQDNE.js");
5780
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
6005
+ const { getMindManager: getMindManager2 } = await import("./mind-manager-YFCOIAAX.js");
6006
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
5781
6007
  const manager = getMindManager2();
5782
6008
  const sm = getSleepManagerIfReady2();
5783
6009
  const targetMinds = mindParticipants.map((ap) => {
@@ -5826,18 +6052,18 @@ async function fanOutToMinds(opts) {
5826
6052
  });
5827
6053
  }
5828
6054
  }
5829
- var mindChatSchema = z9.object({
5830
- message: z9.string().optional(),
5831
- conversationId: z9.string().optional(),
5832
- sender: z9.string().optional(),
5833
- images: z9.array(z9.object({ media_type: z9.string(), data: z9.string() })).optional()
6055
+ var mindChatSchema = z8.object({
6056
+ message: z8.string().optional(),
6057
+ conversationId: z8.string().optional(),
6058
+ sender: z8.string().optional(),
6059
+ images: z8.array(z8.object({ media_type: z8.string(), data: z8.string() })).optional()
5834
6060
  });
5835
- var unifiedChatSchema = z9.object({
5836
- message: z9.string().optional(),
5837
- conversationId: z9.string(),
5838
- images: z9.array(z9.object({ media_type: z9.string(), data: z9.string() })).optional()
6061
+ var unifiedChatSchema = z8.object({
6062
+ message: z8.string().optional(),
6063
+ conversationId: z8.string(),
6064
+ images: z8.array(z8.object({ media_type: z8.string(), data: z8.string() })).optional()
5839
6065
  });
5840
- var app22 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zValidator9("json", mindChatSchema), async (c) => {
6066
+ var app21 = new Hono21().use("*", authMiddleware).post("/minds/:name/chat", zValidator8("json", mindChatSchema), async (c) => {
5841
6067
  const name = c.req.param("name");
5842
6068
  const baseName = await getBaseName(name);
5843
6069
  const entry = await findMind(baseName);
@@ -5928,7 +6154,7 @@ var app22 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zVal
5928
6154
  });
5929
6155
  });
5930
6156
  });
5931
- }).post("/chat", zValidator9("json", unifiedChatSchema), async (c) => {
6157
+ }).post("/chat", zValidator8("json", unifiedChatSchema), async (c) => {
5932
6158
  const user = c.get("user");
5933
6159
  const body = c.req.valid("json");
5934
6160
  if (!body.message && (!body.images || body.images.length === 0)) {
@@ -5960,17 +6186,17 @@ var app22 = new Hono22().use("*", authMiddleware).post("/minds/:name/chat", zVal
5960
6186
  });
5961
6187
  return c.json({ ok: true, conversationId: body.conversationId });
5962
6188
  });
5963
- var chat_default = app22;
6189
+ var chat_default = app21;
5964
6190
 
5965
6191
  // src/web/api/v1/conversations.ts
5966
- import { zValidator as zValidator10 } from "@hono/zod-validator";
5967
- import { Hono as Hono23 } from "hono";
5968
- import { z as z10 } from "zod";
5969
- var createSchema2 = z10.object({
5970
- title: z10.string().optional(),
5971
- participantNames: z10.array(z10.string()).min(1)
6192
+ import { zValidator as zValidator9 } from "@hono/zod-validator";
6193
+ import { Hono as Hono22 } from "hono";
6194
+ import { z as z9 } from "zod";
6195
+ var createSchema = z9.object({
6196
+ title: z9.string().optional(),
6197
+ participantNames: z9.array(z9.string()).min(1)
5972
6198
  });
5973
- var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6199
+ var app22 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
5974
6200
  const user = c.get("user");
5975
6201
  const convs = await listConversationsWithParticipants(user.id);
5976
6202
  return c.json(convs);
@@ -5988,7 +6214,7 @@ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
5988
6214
  }
5989
6215
  const before = beforeStr ? parseInt(beforeStr, 10) : void 0;
5990
6216
  const limit = limitStr ? parseInt(limitStr, 10) : void 0;
5991
- if (before !== void 0 && isNaN(before) || limit !== void 0 && isNaN(limit)) {
6217
+ if (before !== void 0 && Number.isNaN(before) || limit !== void 0 && Number.isNaN(limit)) {
5992
6218
  return c.json({ error: "Invalid cursor params: before and limit must be integers" }, 400);
5993
6219
  }
5994
6220
  const result = await getMessagesPaginated(id, { before, limit });
@@ -6001,7 +6227,7 @@ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6001
6227
  }
6002
6228
  const participants = await getParticipants(id);
6003
6229
  return c.json(participants);
6004
- }).post("/", zValidator10("json", createSchema2), async (c) => {
6230
+ }).post("/", zValidator9("json", createSchema), async (c) => {
6005
6231
  const user = c.get("user");
6006
6232
  const body = c.req.valid("json");
6007
6233
  const participantIds = /* @__PURE__ */ new Set();
@@ -6047,30 +6273,30 @@ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6047
6273
  if (!deleted) return c.json({ error: "Conversation not found" }, 404);
6048
6274
  return c.json({ ok: true });
6049
6275
  });
6050
- var conversations_default = app23;
6276
+ var conversations_default = app22;
6051
6277
 
6052
6278
  // src/web/api/v1/events.ts
6053
6279
  import { desc as desc4 } from "drizzle-orm";
6054
- import { Hono as Hono24 } from "hono";
6280
+ import { Hono as Hono23 } from "hono";
6055
6281
  import { streamSSE as streamSSE5 } from "hono/streaming";
6056
6282
 
6057
6283
  // src/lib/events/brain-presence.ts
6058
6284
  var connections = /* @__PURE__ */ new Map();
6059
6285
  function addConnection(username) {
6060
- const count2 = connections.get(username) ?? 0;
6061
- connections.set(username, count2 + 1);
6062
- if (count2 === 0) {
6286
+ const count = connections.get(username) ?? 0;
6287
+ connections.set(username, count + 1);
6288
+ if (count === 0) {
6063
6289
  broadcast({ type: "brain_online", mind: username, summary: `${username} connected` });
6064
6290
  }
6065
6291
  }
6066
6292
  function removeConnection(username) {
6067
- const count2 = connections.get(username);
6068
- if (count2 == null) return;
6069
- if (count2 <= 1) {
6293
+ const count = connections.get(username);
6294
+ if (count == null) return;
6295
+ if (count <= 1) {
6070
6296
  connections.delete(username);
6071
6297
  broadcast({ type: "brain_offline", mind: username, summary: `${username} disconnected` });
6072
6298
  } else {
6073
- connections.set(username, count2 - 1);
6299
+ connections.set(username, count - 1);
6074
6300
  }
6075
6301
  }
6076
6302
  function getOnlineBrains() {
@@ -6098,7 +6324,7 @@ function getEventsSince(sinceId) {
6098
6324
  }
6099
6325
 
6100
6326
  // src/web/api/v1/events.ts
6101
- var app24 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
6327
+ var app23 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6102
6328
  const user = c.get("user");
6103
6329
  const since = c.req.query("since");
6104
6330
  const sinceId = since ? Number(since) : 0;
@@ -6205,16 +6431,16 @@ var app24 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
6205
6431
  }
6206
6432
  });
6207
6433
  });
6208
- var events_default = app24;
6434
+ var events_default = app23;
6209
6435
 
6210
6436
  // src/web/api/variants.ts
6211
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
6437
+ import { existsSync as existsSync16, mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "fs";
6212
6438
  import { resolve as resolve20 } from "path";
6213
- import { Hono as Hono25 } from "hono";
6439
+ import { Hono as Hono24 } from "hono";
6214
6440
 
6215
6441
  // src/lib/spawn-server.ts
6216
6442
  import { spawn as spawn4 } from "child_process";
6217
- import { closeSync, mkdirSync as mkdirSync9, openSync, readFileSync as readFileSync12 } from "fs";
6443
+ import { closeSync, mkdirSync as mkdirSync10, openSync, readFileSync as readFileSync13 } from "fs";
6218
6444
  import { resolve as resolve19 } from "path";
6219
6445
  function tsxBin(cwd) {
6220
6446
  return resolve19(cwd, "node_modules", ".bin", "tsx");
@@ -6253,7 +6479,7 @@ function spawnAttached(cwd, port) {
6253
6479
  }
6254
6480
  function spawnDetached(cwd, port, logDir) {
6255
6481
  const logsDir = logDir ?? resolve19(cwd, ".mind", "logs");
6256
- mkdirSync9(logsDir, { recursive: true });
6482
+ mkdirSync10(logsDir, { recursive: true });
6257
6483
  const logPath = resolve19(logsDir, "mind.log");
6258
6484
  const logFd = openSync(logPath, "a");
6259
6485
  const child = spawn4(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
@@ -6274,7 +6500,7 @@ function spawnDetached(cwd, port, logDir) {
6274
6500
  }
6275
6501
  const interval = setInterval(() => {
6276
6502
  try {
6277
- const content = readFileSync12(logPath, "utf-8");
6503
+ const content = readFileSync13(logPath, "utf-8");
6278
6504
  const match = content.match(/listening on :(\d+)/);
6279
6505
  if (match) {
6280
6506
  finish({ child, actualPort: parseInt(match[1], 10) });
@@ -6326,7 +6552,7 @@ async function verify2(port) {
6326
6552
  }
6327
6553
 
6328
6554
  // src/web/api/variants.ts
6329
- var app25 = new Hono25().get("/:name/variants", async (c) => {
6555
+ var app24 = new Hono24().get("/:name/variants", async (c) => {
6330
6556
  const name = c.req.param("name");
6331
6557
  const entry = await findMind(name);
6332
6558
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -6371,10 +6597,10 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
6371
6597
  }
6372
6598
  const projectRoot = mindDir(mindName);
6373
6599
  const variantDir = resolve20(projectRoot, ".variants", variantName);
6374
- if (existsSync15(variantDir)) {
6600
+ if (existsSync16(variantDir)) {
6375
6601
  return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
6376
6602
  }
6377
- mkdirSync10(resolve20(projectRoot, ".variants"), { recursive: true });
6603
+ mkdirSync11(resolve20(projectRoot, ".variants"), { recursive: true });
6378
6604
  try {
6379
6605
  await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
6380
6606
  } catch (e) {
@@ -6397,7 +6623,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
6397
6623
  return c.json({ error: `npm install failed: ${msg}` }, 500);
6398
6624
  }
6399
6625
  if (body.soul) {
6400
- writeFileSync9(resolve20(variantDir, "home/SOUL.md"), body.soul);
6626
+ writeFileSync10(resolve20(variantDir, "home/SOUL.md"), body.soul);
6401
6627
  }
6402
6628
  const variantPort = body.port ?? await nextPort();
6403
6629
  await addVariant(variantName, mindName, variantPort, variantDir, variantName);
@@ -6432,7 +6658,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
6432
6658
  } catch {
6433
6659
  }
6434
6660
  const projectRoot = mindDir(mindName);
6435
- if (existsSync15(variantEntry.dir)) {
6661
+ if (existsSync16(variantEntry.dir)) {
6436
6662
  const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
6437
6663
  if (status) {
6438
6664
  try {
@@ -6440,7 +6666,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
6440
6666
  await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
6441
6667
  cwd: variantEntry.dir
6442
6668
  });
6443
- } catch (e) {
6669
+ } catch (_e) {
6444
6670
  return c.json(
6445
6671
  {
6446
6672
  error: "Failed to auto-commit variant changes. Commit or stash manually before merging."
@@ -6477,7 +6703,7 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
6477
6703
  await gitExec(["commit", "-m", "Auto-commit uncommitted changes before merge"], {
6478
6704
  cwd: projectRoot
6479
6705
  });
6480
- } catch (e) {
6706
+ } catch (_e) {
6481
6707
  return c.json(
6482
6708
  { error: "Failed to auto-commit main changes. Commit or stash manually before merging." },
6483
6709
  500
@@ -6486,14 +6712,14 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
6486
6712
  }
6487
6713
  try {
6488
6714
  await gitExec(["merge", variantEntry.branch], { cwd: projectRoot });
6489
- } catch (e) {
6715
+ } catch (_e) {
6490
6716
  return c.json({ error: "Merge failed. Resolve conflicts manually." }, 500);
6491
6717
  }
6492
6718
  await cleanupVariant(variantName, projectRoot, variantEntry.dir);
6493
6719
  if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
6494
6720
  try {
6495
- const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-BIMA4ILT.js");
6496
- const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-NDNOOYG4.js");
6721
+ const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-3HOR4UAJ.js");
6722
+ const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-ODSALQQL.js");
6497
6723
  const tmpl = parentEntry.template ?? "claude";
6498
6724
  await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
6499
6725
  } catch (err) {
@@ -6548,19 +6774,19 @@ var app25 = new Hono25().get("/:name/variants", async (c) => {
6548
6774
  await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
6549
6775
  return c.json({ ok: true });
6550
6776
  });
6551
- var variants_default = app25;
6777
+ var variants_default = app24;
6552
6778
 
6553
6779
  // src/web/api/volute/channels.ts
6554
- import { zValidator as zValidator11 } from "@hono/zod-validator";
6555
- import { Hono as Hono26 } from "hono";
6556
- import { z as z11 } from "zod";
6557
- var createSchema3 = z11.object({
6558
- name: z11.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
6780
+ import { zValidator as zValidator10 } from "@hono/zod-validator";
6781
+ import { Hono as Hono25 } from "hono";
6782
+ import { z as z10 } from "zod";
6783
+ var createSchema2 = z10.object({
6784
+ name: z10.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
6559
6785
  });
6560
- var inviteSchema = z11.object({
6561
- username: z11.string().min(1)
6786
+ var inviteSchema = z10.object({
6787
+ username: z10.string().min(1)
6562
6788
  });
6563
- var app26 = new Hono26().get("/", async (c) => {
6789
+ var app25 = new Hono25().get("/", async (c) => {
6564
6790
  const user = c.get("user");
6565
6791
  const channels = await listChannels();
6566
6792
  const results = await Promise.all(
@@ -6571,7 +6797,7 @@ var app26 = new Hono26().get("/", async (c) => {
6571
6797
  })
6572
6798
  );
6573
6799
  return c.json(results);
6574
- }).post("/", zValidator11("json", createSchema3), async (c) => {
6800
+ }).post("/", zValidator10("json", createSchema2), async (c) => {
6575
6801
  const user = c.get("user");
6576
6802
  const body = c.req.valid("json");
6577
6803
  try {
@@ -6604,7 +6830,7 @@ var app26 = new Hono26().get("/", async (c) => {
6604
6830
  if (!ch) return c.json({ error: "Channel not found" }, 404);
6605
6831
  const participants = await getParticipants(ch.id);
6606
6832
  return c.json(participants);
6607
- }).post("/:name/invite", zValidator11("json", inviteSchema), async (c) => {
6833
+ }).post("/:name/invite", zValidator10("json", inviteSchema), async (c) => {
6608
6834
  const name = c.req.param("name");
6609
6835
  const inviter = c.get("user");
6610
6836
  const { username } = c.req.valid("json");
@@ -6624,13 +6850,13 @@ var app26 = new Hono26().get("/", async (c) => {
6624
6850
  ]);
6625
6851
  return c.json({ ok: true });
6626
6852
  });
6627
- var channels_default2 = app26;
6853
+ var channels_default2 = app25;
6628
6854
 
6629
6855
  // src/web/api/volute/chat.ts
6630
- import { zValidator as zValidator12 } from "@hono/zod-validator";
6631
- import { Hono as Hono27 } from "hono";
6856
+ import { zValidator as zValidator11 } from "@hono/zod-validator";
6857
+ import { Hono as Hono26 } from "hono";
6632
6858
  import { streamSSE as streamSSE6 } from "hono/streaming";
6633
- import { z as z12 } from "zod";
6859
+ import { z as z11 } from "zod";
6634
6860
 
6635
6861
  // src/lib/bridge-outbound.ts
6636
6862
  function extractContent(contentBlocks) {
@@ -6710,8 +6936,8 @@ async function fanOutToMinds2(opts) {
6710
6936
  const participantNames = participants.map((p) => p.username);
6711
6937
  const isDM = opts.isDM ?? participants.length === 2;
6712
6938
  const channelEntryType = opts.channelEntryType ?? (isDM ? "dm" : "channel");
6713
- const { getMindManager: getMindManager2 } = await import("./mind-manager-P66HQDNE.js");
6714
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G4B5GW5P.js");
6939
+ const { getMindManager: getMindManager2 } = await import("./mind-manager-YFCOIAAX.js");
6940
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-O7YQFCV5.js");
6715
6941
  const manager = getMindManager2();
6716
6942
  const sm = getSleepManagerIfReady2();
6717
6943
  const targetMinds = mindParticipants.map((ap) => {
@@ -6760,24 +6986,24 @@ async function fanOutToMinds2(opts) {
6760
6986
  });
6761
6987
  }
6762
6988
  }
6763
- var fileSchema = z12.object({
6764
- filename: z12.string(),
6765
- data: z12.string()
6989
+ var fileSchema = z11.object({
6990
+ filename: z11.string(),
6991
+ data: z11.string()
6766
6992
  // base64
6767
6993
  });
6768
- var chatSchema = z12.object({
6769
- message: z12.string().optional(),
6770
- conversationId: z12.string().optional(),
6771
- sender: z12.string().optional(),
6772
- images: z12.array(
6773
- z12.object({
6774
- media_type: z12.string(),
6775
- data: z12.string()
6994
+ var chatSchema = z11.object({
6995
+ message: z11.string().optional(),
6996
+ conversationId: z11.string().optional(),
6997
+ sender: z11.string().optional(),
6998
+ images: z11.array(
6999
+ z11.object({
7000
+ media_type: z11.string(),
7001
+ data: z11.string()
6776
7002
  })
6777
7003
  ).optional(),
6778
- files: z12.array(fileSchema).optional()
7004
+ files: z11.array(fileSchema).optional()
6779
7005
  });
6780
- var app27 = new Hono27().post("/:name/chat", zValidator12("json", chatSchema), async (c) => {
7006
+ var app26 = new Hono26().post("/:name/chat", zValidator11("json", chatSchema), async (c) => {
6781
7007
  const name = c.req.param("name");
6782
7008
  const baseName = await getBaseName(name);
6783
7009
  const entry = await findMind(baseName);
@@ -6903,15 +7129,15 @@ var app27 = new Hono27().post("/:name/chat", zValidator12("json", chatSchema), a
6903
7129
  });
6904
7130
  });
6905
7131
  });
6906
- var unifiedChatSchema2 = z12.object({
6907
- message: z12.string().optional(),
6908
- conversationId: z12.string(),
6909
- images: z12.array(z12.object({ media_type: z12.string(), data: z12.string() })).optional(),
6910
- files: z12.array(fileSchema).optional()
7132
+ var unifiedChatSchema2 = z11.object({
7133
+ message: z11.string().optional(),
7134
+ conversationId: z11.string(),
7135
+ images: z11.array(z11.object({ media_type: z11.string(), data: z11.string() })).optional(),
7136
+ files: z11.array(fileSchema).optional()
6911
7137
  });
6912
- var unifiedChatApp = new Hono27().post(
7138
+ var unifiedChatApp = new Hono26().post(
6913
7139
  "/chat",
6914
- zValidator12("json", unifiedChatSchema2),
7140
+ zValidator11("json", unifiedChatSchema2),
6915
7141
  async (c) => {
6916
7142
  const user = c.get("user");
6917
7143
  const body = c.req.valid("json");
@@ -6986,18 +7212,18 @@ var unifiedChatApp = new Hono27().post(
6986
7212
  return c.json({ ok: true, conversationId: body.conversationId });
6987
7213
  }
6988
7214
  );
6989
- var chat_default2 = app27;
7215
+ var chat_default2 = app26;
6990
7216
 
6991
7217
  // src/web/api/volute/conversations.ts
6992
- import { zValidator as zValidator13 } from "@hono/zod-validator";
6993
- import { Hono as Hono28 } from "hono";
6994
- import { z as z13 } from "zod";
6995
- var createConvSchema = z13.object({
6996
- title: z13.string().optional(),
6997
- participantIds: z13.array(z13.number()).optional(),
6998
- participantNames: z13.array(z13.string()).optional()
7218
+ import { zValidator as zValidator12 } from "@hono/zod-validator";
7219
+ import { Hono as Hono27 } from "hono";
7220
+ import { z as z12 } from "zod";
7221
+ var createConvSchema = z12.object({
7222
+ title: z12.string().optional(),
7223
+ participantIds: z12.array(z12.number()).optional(),
7224
+ participantNames: z12.array(z12.string()).optional()
6999
7225
  });
7000
- var app28 = new Hono28().get("/:name/conversations", async (c) => {
7226
+ var app27 = new Hono27().get("/:name/conversations", async (c) => {
7001
7227
  const name = c.req.param("name");
7002
7228
  const user = c.get("user");
7003
7229
  let lookupId = user.id;
@@ -7008,7 +7234,7 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
7008
7234
  const all = await listConversationsForUser(lookupId);
7009
7235
  const convs = all.filter((c2) => c2.mind_name === name || c2.type === "channel");
7010
7236
  return c.json(convs);
7011
- }).post("/:name/conversations", zValidator13("json", createConvSchema), async (c) => {
7237
+ }).post("/:name/conversations", zValidator12("json", createConvSchema), async (c) => {
7012
7238
  const name = c.req.param("name");
7013
7239
  const user = c.get("user");
7014
7240
  const body = c.req.valid("json");
@@ -7028,7 +7254,7 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
7028
7254
  if (hyphenIdx > 0) {
7029
7255
  const prefix = pname.slice(0, hyphenIdx);
7030
7256
  if (["discord", "slack", "telegram"].includes(prefix)) {
7031
- existing = await getUserByUsername(prefix + ":" + pname.slice(hyphenIdx + 1));
7257
+ existing = await getUserByUsername(`${prefix}:${pname.slice(hyphenIdx + 1)}`);
7032
7258
  }
7033
7259
  }
7034
7260
  }
@@ -7094,18 +7320,18 @@ var app28 = new Hono28().get("/:name/conversations", async (c) => {
7094
7320
  if (!deleted) return c.json({ error: "Conversation not found" }, 404);
7095
7321
  return c.json({ ok: true });
7096
7322
  });
7097
- var conversations_default2 = app28;
7323
+ var conversations_default2 = app27;
7098
7324
 
7099
7325
  // src/web/api/volute/user-conversations.ts
7100
- import { zValidator as zValidator14 } from "@hono/zod-validator";
7101
- import { Hono as Hono29 } from "hono";
7326
+ import { zValidator as zValidator13 } from "@hono/zod-validator";
7327
+ import { Hono as Hono28 } from "hono";
7102
7328
  import { streamSSE as streamSSE7 } from "hono/streaming";
7103
- import { z as z14 } from "zod";
7104
- var createSchema4 = z14.object({
7105
- title: z14.string().optional(),
7106
- participantNames: z14.array(z14.string()).min(1)
7329
+ import { z as z13 } from "zod";
7330
+ var createSchema3 = z13.object({
7331
+ title: z13.string().optional(),
7332
+ participantNames: z13.array(z13.string()).min(1)
7107
7333
  });
7108
- var app29 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
7334
+ var app28 = new Hono28().use("*", authMiddleware).get("/", async (c) => {
7109
7335
  const user = c.get("user");
7110
7336
  const convs = await listConversationsWithParticipants(user.id);
7111
7337
  return c.json(convs);
@@ -7117,7 +7343,7 @@ var app29 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
7117
7343
  }
7118
7344
  const msgs = await getMessages(id);
7119
7345
  return c.json(msgs);
7120
- }).post("/", zValidator14("json", createSchema4), async (c) => {
7346
+ }).post("/", zValidator13("json", createSchema3), async (c) => {
7121
7347
  const user = c.get("user");
7122
7348
  const body = c.req.valid("json");
7123
7349
  const participantIds = /* @__PURE__ */ new Set();
@@ -7182,12 +7408,12 @@ var app29 = new Hono29().use("*", authMiddleware).get("/", async (c) => {
7182
7408
  if (!deleted) return c.json({ error: "Conversation not found" }, 404);
7183
7409
  return c.json({ ok: true });
7184
7410
  });
7185
- var user_conversations_default = app29;
7411
+ var user_conversations_default = app28;
7186
7412
 
7187
7413
  // src/web/app.ts
7188
7414
  var httpLog = logger_default.child("http");
7189
- var app30 = new Hono30();
7190
- app30.onError((err, c) => {
7415
+ var app29 = new Hono29();
7416
+ app29.onError((err, c) => {
7191
7417
  if (err instanceof HTTPException) {
7192
7418
  return err.getResponse();
7193
7419
  }
@@ -7198,10 +7424,10 @@ app30.onError((err, c) => {
7198
7424
  });
7199
7425
  return c.json({ error: "Internal server error" }, 500);
7200
7426
  });
7201
- app30.notFound((c) => {
7427
+ app29.notFound((c) => {
7202
7428
  return c.json({ error: "Not found" }, 404);
7203
7429
  });
7204
- app30.use("*", async (c, next) => {
7430
+ app29.use("*", async (c, next) => {
7205
7431
  const start = Date.now();
7206
7432
  await next();
7207
7433
  const duration = Date.now() - start;
@@ -7212,7 +7438,7 @@ app30.use("*", async (c, next) => {
7212
7438
  httpLog.debug("request", data);
7213
7439
  }
7214
7440
  });
7215
- app30.get("/api/health", (c) => {
7441
+ app29.get("/api/health", (c) => {
7216
7442
  let version = "unknown";
7217
7443
  let cached = null;
7218
7444
  try {
@@ -7227,40 +7453,44 @@ app30.get("/api/health", (c) => {
7227
7453
  ...cached?.updateAvailable ? { updateAvailable: true, latest: cached.latest } : {}
7228
7454
  });
7229
7455
  });
7230
- app30.use("/api/*", bodyLimit({ maxSize: 10 * 1024 * 1024 }));
7231
- app30.use("/api/*", csrf());
7232
- app30.use("/api/activity/*", authMiddleware);
7233
- app30.use("/api/minds/*", authMiddleware);
7234
- app30.use("/api/conversations/*", authMiddleware);
7235
- app30.use("/api/volute/*", authMiddleware);
7236
- app30.use("/api/system/*", authMiddleware);
7237
- app30.use("/api/env/*", authMiddleware);
7238
- app30.use("/api/prompts/*", authMiddleware);
7239
- app30.use("/api/skills/*", authMiddleware);
7240
- app30.use("/api/notes/*", authMiddleware);
7241
- app30.use("/api/bridges/*", authMiddleware);
7242
- app30.use("/api/v1/*", authMiddleware);
7243
- app30.route("/pages", pages_default);
7244
- app30.route("/public", public_files_default);
7245
- var routes = app30.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/minds", minds_default).route("/api/minds", chat_default2).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", shared_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/notes", notes_default).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/conversations", user_conversations_default).route("/api/volute/channels", channels_default2).route("/api/volute", unifiedChatApp).route("/api/bridges", bridges_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", chat_default);
7246
- app30.route("/api/v1/minds", minds_default);
7247
- app30.route("/api/v1/minds", typing_default);
7248
- app30.route("/api/v1/minds", variants_default);
7249
- app30.route("/api/v1/minds", files_default);
7250
- app30.route("/api/v1/minds", env_default);
7251
- app30.route("/api/v1/minds", mind_skills_default);
7252
- app30.route("/api/v1/minds", schedules_default);
7253
- app30.route("/api/v1/minds", logs_default);
7254
- app30.route("/api/v1/system", system_default);
7255
- app30.route("/api/v1/system", update_default);
7256
- app30.route("/api/v1/prompts", prompts_default);
7257
- app30.route("/api/v1/skills", skills_default);
7258
- app30.route("/api/v1/env", sharedEnvApp);
7259
- app30.route("/api/v1/channels", channels_default2);
7260
- var app_default = app30;
7456
+ app29.use("/api/*", bodyLimit({ maxSize: 10 * 1024 * 1024 }));
7457
+ app29.use("/api/*", csrf());
7458
+ app29.use("/api/activity/*", authMiddleware);
7459
+ app29.use("/api/minds/*", authMiddleware);
7460
+ app29.use("/api/conversations/*", authMiddleware);
7461
+ app29.use("/api/volute/*", authMiddleware);
7462
+ app29.use("/api/system/*", authMiddleware);
7463
+ app29.use("/api/env/*", authMiddleware);
7464
+ app29.use("/api/prompts/*", authMiddleware);
7465
+ app29.use("/api/skills/*", authMiddleware);
7466
+ app29.use("/api/extensions/*", authMiddleware);
7467
+ app29.use("/api/bridges/*", authMiddleware);
7468
+ app29.use("/api/v1/*", authMiddleware);
7469
+ app29.route("/public", public_files_default);
7470
+ var routes = app29.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/minds", minds_default).route("/api/minds", chat_default2).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", shared_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/conversations", user_conversations_default).route("/api/volute/channels", channels_default2).route("/api/volute", unifiedChatApp).route("/api/bridges", bridges_default).route("/api/extensions", extensions_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", chat_default);
7471
+ app29.route("/api/v1/minds", minds_default);
7472
+ app29.route("/api/v1/minds", typing_default);
7473
+ app29.route("/api/v1/minds", variants_default);
7474
+ app29.route("/api/v1/minds", files_default);
7475
+ app29.route("/api/v1/minds", env_default);
7476
+ app29.route("/api/v1/minds", mind_skills_default);
7477
+ app29.route("/api/v1/minds", schedules_default);
7478
+ app29.route("/api/v1/minds", logs_default);
7479
+ app29.route("/api/v1/system", system_default);
7480
+ app29.route("/api/v1/system", update_default);
7481
+ app29.route("/api/v1/prompts", prompts_default);
7482
+ app29.route("/api/v1/skills", skills_default);
7483
+ app29.route("/api/v1/env", sharedEnvApp);
7484
+ app29.route("/api/v1/channels", channels_default2);
7485
+ var app_default = app29;
7261
7486
 
7262
7487
  // src/web/server.ts
7263
- var MIME_TYPES3 = {
7488
+ import { existsSync as existsSync17 } from "fs";
7489
+ import { readFile as readFile3, stat as stat3 } from "fs/promises";
7490
+ import { createServer as createHttpsServer } from "https";
7491
+ import { dirname as dirname2, extname as extname4, resolve as resolve21 } from "path";
7492
+ import { serve } from "@hono/node-server";
7493
+ var MIME_TYPES2 = {
7264
7494
  ".html": "text/html",
7265
7495
  ".js": "application/javascript",
7266
7496
  ".css": "text/css",
@@ -7278,7 +7508,7 @@ async function startServer({
7278
7508
  let searchDir = dirname2(new URL(import.meta.url).pathname);
7279
7509
  for (let i = 0; i < 5; i++) {
7280
7510
  const candidate = resolve21(searchDir, "dist", "web-assets");
7281
- if (existsSync16(candidate)) {
7511
+ if (existsSync17(candidate)) {
7282
7512
  assetsDir = candidate;
7283
7513
  break;
7284
7514
  }
@@ -7287,20 +7517,20 @@ async function startServer({
7287
7517
  if (assetsDir) {
7288
7518
  app_default.get("*", async (c) => {
7289
7519
  const urlPath = new URL(c.req.url).pathname;
7290
- if (urlPath.startsWith("/api/")) return c.notFound();
7520
+ if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
7291
7521
  const filePath = resolve21(assetsDir, urlPath.slice(1));
7292
7522
  if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
7293
- const s = await stat4(filePath).catch(() => null);
7523
+ const s = await stat3(filePath).catch(() => null);
7294
7524
  if (s?.isFile()) {
7295
- const ext = extname5(filePath);
7296
- const mime = MIME_TYPES3[ext] || "application/octet-stream";
7297
- const body = await readFile4(filePath);
7525
+ const ext = extname4(filePath);
7526
+ const mime = MIME_TYPES2[ext] || "application/octet-stream";
7527
+ const body = await readFile3(filePath);
7298
7528
  return c.body(body, 200, { "Content-Type": mime });
7299
7529
  }
7300
7530
  const indexPath = resolve21(assetsDir, "index.html");
7301
- const indexStat = await stat4(indexPath).catch(() => null);
7531
+ const indexStat = await stat3(indexPath).catch(() => null);
7302
7532
  if (indexStat?.isFile()) {
7303
- const body = await readFile4(indexPath, "utf-8");
7533
+ const body = await readFile3(indexPath, "utf-8");
7304
7534
  return c.html(body);
7305
7535
  }
7306
7536
  return c.text("Not found", 404);
@@ -7377,7 +7607,7 @@ async function startDaemon(opts) {
7377
7607
  }
7378
7608
  const DAEMON_PID_PATH = resolve22(systemDir, "daemon.pid");
7379
7609
  const DAEMON_JSON_PATH = resolve22(systemDir, "daemon.json");
7380
- mkdirSync11(home, { recursive: true });
7610
+ mkdirSync12(home, { recursive: true });
7381
7611
  ensureSystemDir();
7382
7612
  migrateToSystemDir();
7383
7613
  migrateAgentsToMinds();
@@ -7386,33 +7616,38 @@ async function startDaemon(opts) {
7386
7616
  } catch (err) {
7387
7617
  logger_default.warn("failed to initialize shared repo", logger_default.errorData(err));
7388
7618
  }
7389
- await (await import("./db-IC4J52XQ.js")).getDb();
7390
- const { migrateRegistryToDb } = await import("./migrate-registry-to-db-XC7T5B7P.js");
7619
+ await (await import("./db-HMFPIRO2.js")).getDb();
7620
+ const { migrateRegistryToDb } = await import("./migrate-registry-to-db-FK35IPEH.js");
7391
7621
  migrateRegistryToDb();
7392
7622
  try {
7393
- const { migrateGroupDMsToChannels } = await import("./conversations-P5BL7RMX.js");
7623
+ const { migrateGroupDMsToChannels } = await import("./conversations-7KVQV7EZ.js");
7394
7624
  await migrateGroupDMsToChannels();
7395
7625
  } catch (err) {
7396
7626
  logger_default.error("failed to migrate group DMs to channels", logger_default.errorData(err));
7397
7627
  }
7398
- const { initSandbox } = await import("./sandbox-5BW5HPXM.js");
7628
+ const { initSandbox } = await import("./sandbox-O5FUSF43.js");
7399
7629
  await initSandbox();
7400
7630
  try {
7401
7631
  await syncBuiltinSkills();
7402
7632
  } catch (err) {
7403
7633
  logger_default.error("failed to sync built-in skills", logger_default.errorData(err));
7404
7634
  }
7635
+ try {
7636
+ await loadAllExtensions(app_default, authMiddleware);
7637
+ notifyExtensionsDaemonStart();
7638
+ } catch (err) {
7639
+ logger_default.error("failed to load extensions", logger_default.errorData(err));
7640
+ }
7641
+ await initDefaultSkills();
7405
7642
  try {
7406
7643
  await ensureSystemChannel();
7407
7644
  } catch (err) {
7408
7645
  logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
7409
7646
  }
7410
- const { startSystemWatcher } = await import("./pages-watcher-P7QECRE2.js");
7411
- startSystemWatcher();
7412
7647
  const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes(32).toString("hex");
7413
7648
  let tls;
7414
7649
  if (opts.tailscale) {
7415
- const { getTailscaleTls } = await import("./tailscale-NY5MUMY3.js");
7650
+ const { getTailscaleTls } = await import("./tailscale-BM72RXCJ.js");
7416
7651
  const tlsConfig = await getTailscaleTls();
7417
7652
  tls = { key: tlsConfig.key, cert: tlsConfig.cert };
7418
7653
  logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
@@ -7433,11 +7668,11 @@ async function startDaemon(opts) {
7433
7668
  process.env.VOLUTE_DAEMON_TOKEN = token;
7434
7669
  process.env.VOLUTE_DAEMON_PORT = String(daemonPort);
7435
7670
  process.env.VOLUTE_DAEMON_HOSTNAME = hostname;
7436
- writeFileSync10(DAEMON_PID_PATH, myPid, { mode: 420 });
7671
+ writeFileSync11(DAEMON_PID_PATH, myPid, { mode: 420 });
7437
7672
  const daemonConfig = { port, hostname, token };
7438
7673
  if (internalPort) daemonConfig.internalPort = internalPort;
7439
7674
  if (tls) daemonConfig.tls = true;
7440
- writeFileSync10(DAEMON_JSON_PATH, `${JSON.stringify(daemonConfig, null, 2)}
7675
+ writeFileSync11(DAEMON_JSON_PATH, `${JSON.stringify(daemonConfig, null, 2)}
7441
7676
  `, { mode: 420 });
7442
7677
  const delivery = initDeliveryManager();
7443
7678
  const manager = initMindManager();
@@ -7493,7 +7728,7 @@ async function startDaemon(opts) {
7493
7728
  bridgeManager.startBridges(daemonPort).catch((err) => {
7494
7729
  logger_default.warn("failed to start bridges", logger_default.errorData(err));
7495
7730
  });
7496
- import("./cloud-sync-KILFGV5Q.js").then(
7731
+ import("./cloud-sync-JV4LJOK3.js").then(
7497
7732
  ({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
7498
7733
  logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
7499
7734
  })
@@ -7501,7 +7736,7 @@ async function startDaemon(opts) {
7501
7736
  logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
7502
7737
  });
7503
7738
  try {
7504
- const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-WDHRO3XD.js");
7739
+ const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-2NTWVEHL.js");
7505
7740
  backfillTemplateHashes();
7506
7741
  notifyVersionUpdate().catch((err) => {
7507
7742
  logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
@@ -7524,13 +7759,13 @@ async function startDaemon(opts) {
7524
7759
  logger_default.info(`running on ${hostname}:${port}, pid ${myPid}`);
7525
7760
  function cleanup() {
7526
7761
  try {
7527
- if (readFileSync13(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
7762
+ if (readFileSync14(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
7528
7763
  unlinkSync2(DAEMON_PID_PATH);
7529
7764
  }
7530
7765
  } catch {
7531
7766
  }
7532
7767
  try {
7533
- const data = JSON.parse(readFileSync13(DAEMON_JSON_PATH, "utf-8"));
7768
+ const data = JSON.parse(readFileSync14(DAEMON_JSON_PATH, "utf-8"));
7534
7769
  if (data.token === token) {
7535
7770
  unlinkSync2(DAEMON_JSON_PATH);
7536
7771
  }
@@ -7552,7 +7787,7 @@ async function startDaemon(opts) {
7552
7787
  }
7553
7788
  };
7554
7789
  try {
7555
- safe("stopAllWatchers", stopAllWatchers);
7790
+ safe("notifyExtensionsDaemonStop", notifyExtensionsDaemonStop);
7556
7791
  safe("stopAllActivityTrackers", stopAll);
7557
7792
  safe("unsubscribeWebhook", unsubscribeWebhook);
7558
7793
  safe("sleepManager.stop", () => sleepManager.stop());