volute 0.44.0 → 0.45.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 (73) hide show
  1. package/dist/{ai-service-7PHSRDMO.js → ai-service-534WUSDL.js} +2 -2
  2. package/dist/backup-OZXHL3JS.js +381 -0
  3. package/dist/{chunk-PNV24SEP.js → chunk-322KE457.js} +13 -1
  4. package/dist/{chunk-7FT24JBQ.js → chunk-5AUKCNPE.js} +5 -5
  5. package/dist/{chunk-EQZD4YU3.js → chunk-77BVGCTZ.js} +3 -3
  6. package/dist/{chunk-7KWANSLX.js → chunk-IUXTYXFI.js} +1 -1
  7. package/dist/{chunk-FZMEVCWC.js → chunk-JSRMEY52.js} +4 -2
  8. package/dist/chunk-KVKP6VQ5.js +318 -0
  9. package/dist/{chunk-3GYVW2X3.js → chunk-OXXTPILE.js} +1 -1
  10. package/dist/{chunk-J47TIYHA.js → chunk-TSK5PI4L.js} +1 -1
  11. package/dist/{chunk-OMETFX5H.js → chunk-U4FNXDEJ.js} +47 -21
  12. package/dist/{chunk-NF75HPNR.js → chunk-V4GYPD5L.js} +39 -14
  13. package/dist/{chunk-I2Y2YHIO.js → chunk-Y6OPHHGM.js} +2 -2
  14. package/dist/{chunk-5AHYQHNT.js → chunk-ZIR7HSAX.js} +2 -1
  15. package/dist/cli.js +13 -7
  16. package/dist/{cloud-sync-6WJ7WFYD.js → cloud-sync-Y7LQPRE3.js} +9 -9
  17. package/dist/{conversations-WAJYZKOC.js → conversations-G4LYMHAA.js} +1 -1
  18. package/dist/{create-YZIV5OJC.js → create-2YY7IPMS.js} +1 -1
  19. package/dist/{daemon-restart-OUJVINXV.js → daemon-restart-ABP54YN6.js} +2 -2
  20. package/dist/daemon.js +426 -228
  21. package/dist/{delivery-manager-TEVOHX5W.js → delivery-manager-R5STGLXB.js} +9 -9
  22. package/dist/{delivery-router-GODSIS62.js → delivery-router-6DE3BPHO.js} +1 -1
  23. package/dist/{echo-text-T7ZA5EYD.js → echo-text-NQXYYYCU.js} +10 -10
  24. package/dist/{extensions-3QNDGW5E.js → extensions-AYSPKE33.js} +9 -9
  25. package/dist/{message-delivery-2EA5LLMD.js → message-delivery-IUAXRPFE.js} +9 -9
  26. package/dist/{mind-MI7Z6KM2.js → mind-KLHC7XYR.js} +4 -4
  27. package/dist/{mind-service-S3SY7HAM.js → mind-service-BF2LUQMH.js} +9 -9
  28. package/dist/{package-5B4XDTJH.js → package-BMHV7UHN.js} +1 -1
  29. package/dist/{sandbox-IOPWWILM.js → sandbox-72MWV4JL.js} +2 -2
  30. package/dist/{scheduler-CXWQ6Z4E.js → scheduler-YHJRRV6E.js} +9 -9
  31. package/dist/{seed-UUPVQWFB.js → seed-4MKQ5QKG.js} +1 -1
  32. package/dist/{seed-cmd-7XPKDQUC.js → seed-cmd-LCXMR7VV.js} +2 -2
  33. package/dist/{seed-create-IZ4M5ESO.js → seed-create-N2BF5LQX.js} +3 -3
  34. package/dist/{seed-sprout-POJXHJWV.js → seed-sprout-6ZDP23YI.js} +4 -4
  35. package/dist/{setup-5GCO3CUB.js → setup-3O6O2JZ7.js} +2 -2
  36. package/dist/{setup-MLRKNP7Y.js → setup-73YBJZQL.js} +1 -1
  37. package/dist/skills/memory/SKILL.md +2 -1
  38. package/dist/skills/orientation/SKILL.md +1 -1
  39. package/dist/skills/plan-coordinator/SKILL.md +2 -2
  40. package/dist/skills/volute-admin/SKILL.md +9 -6
  41. package/dist/skills/volute-mind/SKILL.md +15 -16
  42. package/dist/skills/volute-mind/references/extensions.md +5 -3
  43. package/dist/skills/volute-mind/references/integrations.md +2 -2
  44. package/dist/skills/volute-mind/references/routing.md +19 -23
  45. package/dist/skills/volute-mind/references/variants.md +10 -8
  46. package/dist/{skills-775C7QFF.js → skills-ND4ZFYUW.js} +2 -2
  47. package/dist/{sleep-manager-HINI6Y46.js → sleep-manager-YAHDOIU5.js} +9 -9
  48. package/dist/{spirit-IK5OYWMV.js → spirit-II3HOERM.js} +4 -4
  49. package/dist/{sprout-ZY5QGOEL.js → sprout-4KUG76AQ.js} +1 -1
  50. package/dist/{system-chat-4DQZOEZI.js → system-chat-E65T2HSW.js} +9 -9
  51. package/dist/{turn-tracker-RWZMY7M6.js → turn-tracker-QQIVVXBI.js} +5 -3
  52. package/dist/{up-FLCDR2UB.js → up-LG56MPBI.js} +2 -2
  53. package/dist/{upgrade-37DAGF6H.js → upgrade-NWAZ7MLD.js} +1 -1
  54. package/dist/{version-notify-B67N35A3.js → version-notify-CODJOUW2.js} +9 -9
  55. package/dist/web-assets/assets/{index-dJdNzAPn.css → index-B1JBQv2m.css} +1 -1
  56. package/dist/web-assets/assets/index-Dac11sKB.js +86 -0
  57. package/dist/web-assets/index.html +2 -2
  58. package/package.json +1 -1
  59. package/packages/extensions/plan/skills/plan/SKILL.md +1 -1
  60. package/templates/_base/.init/.config/prompts.json +1 -1
  61. package/templates/_base/.init/.local/hooks/startup-context.ts +0 -8
  62. package/templates/_base/.init/MEMORY.md +2 -1
  63. package/templates/_base/home/VOLUTE.md +6 -9
  64. package/templates/_base/src/lib/startup.ts +1 -1
  65. package/templates/claude/.init/CLAUDE.md +1 -1
  66. package/templates/claude/src/agent.ts +22 -2
  67. package/templates/claude/src/lib/stream-consumer.ts +7 -4
  68. package/templates/codex/.init/AGENTS.md +4 -2
  69. package/templates/codex/src/agent.ts +13 -7
  70. package/templates/pi/.init/MINDS.md +4 -2
  71. package/templates/pi/src/lib/event-handler.ts +6 -2
  72. package/dist/web-assets/assets/index-Domoz5Cg.js +0 -79
  73. package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +0 -40
package/dist/daemon.js CHANGED
@@ -1,4 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ RESTIC_INSTALL_HINT,
4
+ initRepo,
5
+ listSnapshots,
6
+ readBackupState,
7
+ resticVersion,
8
+ runBackup
9
+ } from "./chunk-KVKP6VQ5.js";
2
10
  import {
3
11
  acceptPending,
4
12
  formatFileSize,
@@ -34,7 +42,7 @@ import {
34
42
  fixModelEscapes,
35
43
  getPlatformDriver,
36
44
  routeOutboundBridge
37
- } from "./chunk-EQZD4YU3.js";
45
+ } from "./chunk-77BVGCTZ.js";
38
46
  import {
39
47
  getBridgeConfig,
40
48
  readBridgesConfig,
@@ -104,7 +112,7 @@ import {
104
112
  syncProviderToMinds,
105
113
  uninstallNpmExtension,
106
114
  writeSystemsConfig
107
- } from "./chunk-OMETFX5H.js";
115
+ } from "./chunk-U4FNXDEJ.js";
108
116
  import {
109
117
  loadMergedEnv,
110
118
  mindEnvPath,
@@ -112,7 +120,7 @@ import {
112
120
  sharedEnvPath,
113
121
  writeEnv
114
122
  } from "./chunk-5GT7URXJ.js";
115
- import "./chunk-7FT24JBQ.js";
123
+ import "./chunk-5AUKCNPE.js";
116
124
  import {
117
125
  applyInitFiles,
118
126
  applyTemplateHomeFiles,
@@ -141,7 +149,7 @@ import {
141
149
  syncBuiltinSkills,
142
150
  uninstallSkill,
143
151
  updateSkill
144
- } from "./chunk-I2Y2YHIO.js";
152
+ } from "./chunk-Y6OPHHGM.js";
145
153
  import {
146
154
  addMessage,
147
155
  createChannel,
@@ -173,7 +181,7 @@ import {
173
181
  setConversationPrivate,
174
182
  subscribe as subscribe2,
175
183
  updateChannelSettings
176
- } from "./chunk-FZMEVCWC.js";
184
+ } from "./chunk-JSRMEY52.js";
177
185
  import {
178
186
  readVoluteConfig,
179
187
  writeVoluteConfig
@@ -181,7 +189,7 @@ import {
181
189
  import {
182
190
  isSandboxEnabled,
183
191
  wrapForSandbox
184
- } from "./chunk-3GYVW2X3.js";
192
+ } from "./chunk-OXXTPILE.js";
185
193
  import {
186
194
  getActiveMinds,
187
195
  onMindEvent,
@@ -189,7 +197,7 @@ import {
189
197
  } from "./chunk-HVU45COX.js";
190
198
  import {
191
199
  extractTextContent
192
- } from "./chunk-5AHYQHNT.js";
200
+ } from "./chunk-ZIR7HSAX.js";
193
201
  import {
194
202
  PROMPT_DEFAULTS,
195
203
  PROMPT_KEYS,
@@ -198,10 +206,10 @@ import {
198
206
  completeTurn,
199
207
  createTurn,
200
208
  getActiveTurnId,
201
- getLastToolUseEventId,
202
209
  getMindPromptDefaults,
203
210
  getPrompt,
204
211
  getPromptIfCustom,
212
+ getToolUseEventId,
205
213
  initSummarizer,
206
214
  markErrored,
207
215
  publish as publish2,
@@ -212,7 +220,7 @@ import {
212
220
  summarizeTurn,
213
221
  takeErrored,
214
222
  trackToolUse
215
- } from "./chunk-NF75HPNR.js";
223
+ } from "./chunk-V4GYPD5L.js";
216
224
  import "./chunk-Q2XBKQ2D.js";
217
225
  import {
218
226
  chownMindDir,
@@ -245,12 +253,12 @@ import {
245
253
  setProviderRefreshHook,
246
254
  setUtilityModel,
247
255
  unqualifyModelId
248
- } from "./chunk-J47TIYHA.js";
256
+ } from "./chunk-TSK5PI4L.js";
249
257
  import {
250
258
  isSetupComplete,
251
259
  readGlobalConfig,
252
260
  writeGlobalConfig
253
- } from "./chunk-PNV24SEP.js";
261
+ } from "./chunk-322KE457.js";
254
262
  import {
255
263
  approveUser,
256
264
  changePassword,
@@ -320,12 +328,76 @@ import {
320
328
  import "./chunk-K3NQKI34.js";
321
329
 
322
330
  // packages/daemon/src/daemon.ts
323
- import { randomBytes as randomBytes2 } from "crypto";
331
+ import { randomBytes as randomBytes3 } from "crypto";
324
332
  import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync10, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
325
333
  import { homedir as homedir3 } from "os";
326
334
  import { resolve as resolve16 } from "path";
327
335
  import { format } from "util";
328
336
 
337
+ // packages/daemon/src/lib/daemon/backup-manager.ts
338
+ import { CronExpressionParser } from "cron-parser";
339
+ var blog = logger_default.child("backup");
340
+ var DEFAULT_BACKUP_SCHEDULE = "0 3 * * *";
341
+ var BackupManager = class {
342
+ interval = null;
343
+ lastFiredMinute = -1;
344
+ warnedUnconfigured = false;
345
+ start() {
346
+ this.interval = setInterval(() => {
347
+ this.tick().catch((err) => blog.error("backup tick failed", logger_default.errorData(err)));
348
+ }, 6e4);
349
+ }
350
+ stop() {
351
+ if (this.interval) clearInterval(this.interval);
352
+ this.interval = null;
353
+ }
354
+ /**
355
+ * True at most once per scheduled fire minute (records the minute it
356
+ * consumed), and only when backups are enabled and runnable. Not a pure
357
+ * predicate — a second call in the same minute returns false.
358
+ */
359
+ isDue(now = /* @__PURE__ */ new Date()) {
360
+ const config2 = readGlobalConfig().backup;
361
+ if (!config2?.enabled) return false;
362
+ if (!config2.repository || !config2.password) {
363
+ if (!this.warnedUnconfigured) {
364
+ blog.warn("scheduled backups are enabled but no repository is initialized \u2014 not running");
365
+ this.warnedUnconfigured = true;
366
+ }
367
+ return false;
368
+ }
369
+ this.warnedUnconfigured = false;
370
+ const cron = config2.schedule || DEFAULT_BACKUP_SCHEDULE;
371
+ const epochMinute = Math.floor(now.getTime() / 6e4);
372
+ if (epochMinute === this.lastFiredMinute) return false;
373
+ let prevMinute;
374
+ try {
375
+ const interval = CronExpressionParser.parse(cron, { currentDate: now });
376
+ prevMinute = Math.floor(interval.prev().toDate().getTime() / 6e4);
377
+ } catch (err) {
378
+ blog.warn(`invalid backup cron "${cron}"`, logger_default.errorData(err));
379
+ return false;
380
+ }
381
+ if (prevMinute !== epochMinute) return false;
382
+ this.lastFiredMinute = epochMinute;
383
+ return true;
384
+ }
385
+ async tick() {
386
+ if (!this.isDue()) return;
387
+ blog.info("scheduled backup starting");
388
+ try {
389
+ await runBackup();
390
+ } catch (err) {
391
+ blog.error("scheduled backup failed", logger_default.errorData(err));
392
+ }
393
+ }
394
+ };
395
+ var backupManager = null;
396
+ function initBackupManager() {
397
+ if (!backupManager) backupManager = new BackupManager();
398
+ return backupManager;
399
+ }
400
+
329
401
  // packages/daemon/src/lib/daemon/bridge-manager.ts
330
402
  import { spawn } from "child_process";
331
403
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
@@ -394,7 +466,7 @@ function checkMissingBridgeEnv(def, env) {
394
466
  }
395
467
 
396
468
  // packages/daemon/src/lib/daemon/bridge-manager.ts
397
- var blog = logger_default.child("bridges");
469
+ var blog2 = logger_default.child("bridges");
398
470
  function searchUpwards(...segments) {
399
471
  let searchDir = dirname(new URL(import.meta.url).pathname);
400
472
  for (let i = 0; i < 5; i++) {
@@ -415,7 +487,7 @@ var BridgeManager = class {
415
487
  await Promise.all(
416
488
  platforms.map(
417
489
  (platform) => this.startBridge(platform, daemonPort).catch((err) => {
418
- blog.warn(`failed to start bridge ${platform}`, logger_default.errorData(err));
490
+ blog2.warn(`failed to start bridge ${platform}`, logger_default.errorData(err));
419
491
  })
420
492
  )
421
493
  );
@@ -444,7 +516,7 @@ var BridgeManager = class {
444
516
  }
445
517
  } catch (err) {
446
518
  if (err instanceof Error && err.code !== "ESRCH") {
447
- blog.warn(`failed to stop existing bridge ${platform}`, logger_default.errorData(err));
519
+ blog2.warn(`failed to stop existing bridge ${platform}`, logger_default.errorData(err));
448
520
  }
449
521
  res();
450
522
  }
@@ -509,24 +581,24 @@ var BridgeManager = class {
509
581
  }
510
582
  if (this.shuttingDown) return;
511
583
  if (this.stopping.has(platform)) return;
512
- blog.error(`bridge ${platform} exited with code ${code}`);
513
- if (lastStderr) blog.warn(`bridge ${platform} last output: ${lastStderr}`);
584
+ blog2.error(`bridge ${platform} exited with code ${code}`);
585
+ if (lastStderr) blog2.warn(`bridge ${platform} last output: ${lastStderr}`);
514
586
  const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(platform);
515
587
  if (!shouldRestart) {
516
- blog.error(`bridge ${platform} crashed ${attempt} times \u2014 giving up`);
588
+ blog2.error(`bridge ${platform} crashed ${attempt} times \u2014 giving up`);
517
589
  return;
518
590
  }
519
- blog.info(
591
+ blog2.info(
520
592
  `restarting bridge ${platform} \u2014 attempt ${attempt}/${this.restartTracker.maxRestartAttempts}, in ${delay}ms`
521
593
  );
522
594
  setTimeout(() => {
523
595
  if (this.shuttingDown || this.stopping.has(platform)) return;
524
596
  this.startBridge(platform, daemonPort).catch((err) => {
525
- blog.error(`failed to restart bridge ${platform}`, logger_default.errorData(err));
597
+ blog2.error(`failed to restart bridge ${platform}`, logger_default.errorData(err));
526
598
  });
527
599
  }, delay);
528
600
  });
529
- blog.info(`started bridge ${platform}`);
601
+ blog2.info(`started bridge ${platform}`);
530
602
  }
531
603
  async stopBridge(platform) {
532
604
  const tracked = this.bridges.get(platform);
@@ -543,7 +615,7 @@ var BridgeManager = class {
543
615
  }
544
616
  } catch (err) {
545
617
  if (err instanceof Error && err.code !== "ESRCH") {
546
- blog.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
618
+ blog2.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
547
619
  }
548
620
  resolve17();
549
621
  }
@@ -564,9 +636,9 @@ var BridgeManager = class {
564
636
  try {
565
637
  this.removeBridgePid(platform);
566
638
  } catch (err) {
567
- blog.warn(`failed to remove PID file for bridge ${platform}`, logger_default.errorData(err));
639
+ blog2.warn(`failed to remove PID file for bridge ${platform}`, logger_default.errorData(err));
568
640
  }
569
- blog.info(`stopped bridge ${platform}`);
641
+ blog2.info(`stopped bridge ${platform}`);
570
642
  }
571
643
  async stopAll() {
572
644
  this.shuttingDown = true;
@@ -596,7 +668,7 @@ var BridgeManager = class {
596
668
  unlinkSync(this.bridgePidPath(platform));
597
669
  } catch (err) {
598
670
  if (err instanceof Error && err.code !== "ENOENT") {
599
- blog.warn(`failed to remove PID file for bridge ${platform}`, logger_default.errorData(err));
671
+ blog2.warn(`failed to remove PID file for bridge ${platform}`, logger_default.errorData(err));
600
672
  }
601
673
  }
602
674
  }
@@ -614,18 +686,18 @@ var BridgeManager = class {
614
686
  } catch {
615
687
  }
616
688
  }
617
- blog.warn(`killed orphan bridge ${platform} (pid ${pid})`);
689
+ blog2.warn(`killed orphan bridge ${platform} (pid ${pid})`);
618
690
  }
619
691
  } catch (err) {
620
692
  if (err instanceof Error && err.code !== "ESRCH") {
621
- blog.debug(`orphan bridge ${platform} cleanup: ${err}`);
693
+ blog2.debug(`orphan bridge ${platform} cleanup: ${err}`);
622
694
  }
623
695
  }
624
696
  try {
625
697
  unlinkSync(pidPath);
626
698
  } catch (err) {
627
699
  if (err instanceof Error && err.code !== "ENOENT") {
628
- blog.warn(`failed to clean up PID file for orphan bridge ${platform}`, logger_default.errorData(err));
700
+ blog2.warn(`failed to clean up PID file for orphan bridge ${platform}`, logger_default.errorData(err));
629
701
  }
630
702
  }
631
703
  }
@@ -1457,7 +1529,7 @@ function stopApiKeyRefresh() {
1457
1529
  var system_default = app;
1458
1530
 
1459
1531
  // packages/daemon/src/web/app.ts
1460
- import { Hono as Hono28 } from "hono";
1532
+ import { Hono as Hono29 } from "hono";
1461
1533
  import { bodyLimit } from "hono/body-limit";
1462
1534
  import { cors } from "hono/cors";
1463
1535
  import { csrf } from "hono/csrf";
@@ -1824,10 +1896,115 @@ var app3 = new Hono3().post("/register", zValidator2("json", credentialsSchema),
1824
1896
  }).route("/", admin).route("/", authenticated);
1825
1897
  var auth_default = app3;
1826
1898
 
1827
- // packages/daemon/src/web/api/bridges.ts
1899
+ // packages/daemon/src/web/api/backup.ts
1900
+ import { randomBytes } from "crypto";
1828
1901
  import { zValidator as zValidator3 } from "@hono/zod-validator";
1902
+ import { CronExpressionParser as CronExpressionParser2 } from "cron-parser";
1829
1903
  import { Hono as Hono4 } from "hono";
1830
1904
  import { z as z3 } from "zod";
1905
+ function redactedConfig() {
1906
+ const backup2 = readGlobalConfig().backup ?? {};
1907
+ return {
1908
+ repository: backup2.repository,
1909
+ schedule: backup2.schedule ?? DEFAULT_BACKUP_SCHEDULE,
1910
+ enabled: backup2.enabled,
1911
+ keep: backup2.keep,
1912
+ includeSessions: backup2.includeSessions,
1913
+ hasPassword: !!backup2.password,
1914
+ envKeys: Object.keys(backup2.env ?? {})
1915
+ };
1916
+ }
1917
+ var configSchema = z3.object({
1918
+ repository: z3.string().optional(),
1919
+ schedule: z3.string().refine(
1920
+ (cron) => {
1921
+ try {
1922
+ CronExpressionParser2.parse(cron);
1923
+ return true;
1924
+ } catch {
1925
+ return false;
1926
+ }
1927
+ },
1928
+ { message: "Invalid cron expression" }
1929
+ ).optional(),
1930
+ enabled: z3.boolean().optional(),
1931
+ keep: z3.object({
1932
+ daily: z3.number().int().min(0).optional(),
1933
+ weekly: z3.number().int().min(0).optional(),
1934
+ monthly: z3.number().int().min(0).optional()
1935
+ }).refine((keep) => !(keep.daily === 0 && keep.weekly === 0 && keep.monthly === 0), {
1936
+ message: "Retention cannot be zero everywhere \u2014 prune would delete every snapshot"
1937
+ }).optional(),
1938
+ includeSessions: z3.boolean().optional(),
1939
+ password: z3.string().optional(),
1940
+ env: z3.record(z3.string(), z3.string()).optional()
1941
+ });
1942
+ var backup = new Hono4().get("/config", requireAdmin, (c) => c.json(redactedConfig())).put("/config", requireAdmin, zValidator3("json", configSchema), (c) => {
1943
+ const updates = c.req.valid("json");
1944
+ const config2 = readGlobalConfig();
1945
+ const current = config2.backup ?? {};
1946
+ config2.backup = {
1947
+ ...current,
1948
+ ...updates,
1949
+ // Empty-string password means "keep existing" so the UI can save
1950
+ // settings without re-entering the passphrase.
1951
+ password: updates.password || current.password,
1952
+ env: updates.env ?? current.env
1953
+ };
1954
+ writeGlobalConfig(config2);
1955
+ return c.json(redactedConfig());
1956
+ }).post("/init", requireAdmin, async (c) => {
1957
+ const config2 = readGlobalConfig();
1958
+ if (!config2.backup?.repository) {
1959
+ return c.json({ error: "Backup repository is not configured" }, 400);
1960
+ }
1961
+ let generatedPassword = null;
1962
+ if (!config2.backup.password) {
1963
+ generatedPassword = randomBytes(32).toString("base64url");
1964
+ config2.backup.password = generatedPassword;
1965
+ }
1966
+ try {
1967
+ await initRepo(config2.backup);
1968
+ } catch (err) {
1969
+ const stderr = err.stderr;
1970
+ const detail = err instanceof Error ? err.message : String(err);
1971
+ return c.json({ error: stderr?.trim() || detail }, 500);
1972
+ }
1973
+ if (generatedPassword) writeGlobalConfig(config2);
1974
+ return c.json({ ok: true, password: generatedPassword });
1975
+ }).post("/run", requireAdmin, async (c) => {
1976
+ try {
1977
+ const summary = await runBackup();
1978
+ return c.json(summary);
1979
+ } catch (err) {
1980
+ const stderr = err.stderr;
1981
+ const detail = err instanceof Error ? err.message : String(err);
1982
+ return c.json({ error: stderr?.trim() || detail }, 500);
1983
+ }
1984
+ }).get("/snapshots", requireAdmin, async (c) => {
1985
+ try {
1986
+ return c.json(await listSnapshots());
1987
+ } catch (err) {
1988
+ const stderr = err.stderr;
1989
+ const detail = err instanceof Error ? err.message : String(err);
1990
+ return c.json({ error: stderr?.trim() || detail }, 500);
1991
+ }
1992
+ }).get("/status", requireAdmin, async (c) => {
1993
+ const version = await resticVersion();
1994
+ return c.json({
1995
+ resticInstalled: version !== null,
1996
+ resticVersion: version,
1997
+ installHint: version === null ? RESTIC_INSTALL_HINT : null,
1998
+ config: redactedConfig(),
1999
+ state: readBackupState()
2000
+ });
2001
+ });
2002
+ var backup_default = backup;
2003
+
2004
+ // packages/daemon/src/web/api/bridges.ts
2005
+ import { zValidator as zValidator4 } from "@hono/zod-validator";
2006
+ import { Hono as Hono5 } from "hono";
2007
+ import { z as z4 } from "zod";
1831
2008
 
1832
2009
  // packages/daemon/src/lib/chat/puppets.ts
1833
2010
  import { and as and2, eq as eq2 } from "drizzle-orm";
@@ -1916,27 +2093,27 @@ async function fanOutToMinds(opts) {
1916
2093
  }
1917
2094
 
1918
2095
  // packages/daemon/src/web/api/bridges.ts
1919
- var inboundSchema = z3.object({
1920
- content: z3.array(
1921
- z3.union([
1922
- z3.object({ type: z3.literal("text"), text: z3.string() }),
1923
- z3.object({ type: z3.literal("image"), media_type: z3.string(), data: z3.string() })
2096
+ var inboundSchema = z4.object({
2097
+ content: z4.array(
2098
+ z4.union([
2099
+ z4.object({ type: z4.literal("text"), text: z4.string() }),
2100
+ z4.object({ type: z4.literal("image"), media_type: z4.string(), data: z4.string() })
1924
2101
  ])
1925
2102
  ).min(1),
1926
- platformUserId: z3.string(),
1927
- displayName: z3.string(),
1928
- externalChannel: z3.string(),
1929
- isDM: z3.boolean(),
1930
- targetMind: z3.string().optional()
2103
+ platformUserId: z4.string(),
2104
+ displayName: z4.string(),
2105
+ externalChannel: z4.string(),
2106
+ isDM: z4.boolean(),
2107
+ targetMind: z4.string().optional()
1931
2108
  });
1932
- var mappingSchema = z3.object({
1933
- externalChannel: z3.string(),
1934
- voluteChannel: z3.string()
2109
+ var mappingSchema = z4.object({
2110
+ externalChannel: z4.string(),
2111
+ voluteChannel: z4.string()
1935
2112
  });
1936
- var enableSchema = z3.object({
1937
- defaultMind: z3.string()
2113
+ var enableSchema = z4.object({
2114
+ defaultMind: z4.string()
1938
2115
  });
1939
- var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSchema), async (c) => {
2116
+ var app4 = new Hono5().post("/:platform/inbound", zValidator4("json", inboundSchema), async (c) => {
1940
2117
  const user = c.get("user");
1941
2118
  if (user.id !== 0) return c.json({ error: "Bridge inbound requires daemon token" }, 403);
1942
2119
  const platform = c.req.param("platform");
@@ -1988,7 +2165,7 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
1988
2165
  }
1989
2166
  const participants = await getParticipants(channel.id);
1990
2167
  if (!participants.some((p) => p.userId === puppet.id)) {
1991
- const { addParticipant } = await import("./conversations-WAJYZKOC.js");
2168
+ const { addParticipant } = await import("./conversations-G4LYMHAA.js");
1992
2169
  await addParticipant(channel.id, puppet.id);
1993
2170
  }
1994
2171
  const contentBlocks = body.content;
@@ -2018,7 +2195,7 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
2018
2195
  };
2019
2196
  });
2020
2197
  return c.json(bridges);
2021
- }).post("/:platform", requireAdmin, zValidator3("json", enableSchema), async (c) => {
2198
+ }).post("/:platform", requireAdmin, zValidator4("json", enableSchema), async (c) => {
2022
2199
  const platform = c.req.param("platform");
2023
2200
  const body = c.req.valid("json");
2024
2201
  const def = getBridgeDef(platform);
@@ -2057,7 +2234,7 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
2057
2234
  await manager.stopBridge(platform);
2058
2235
  removeBridgeConfig(platform);
2059
2236
  return c.json({ ok: true });
2060
- }).put("/:platform/mappings", requireAdmin, zValidator3("json", mappingSchema), (c) => {
2237
+ }).put("/:platform/mappings", requireAdmin, zValidator4("json", mappingSchema), (c) => {
2061
2238
  const platform = c.req.param("platform");
2062
2239
  const body = c.req.valid("json");
2063
2240
  try {
@@ -2088,11 +2265,11 @@ async function fanOutToBridgedMinds(opts) {
2088
2265
  var bridges_default = app4;
2089
2266
 
2090
2267
  // packages/daemon/src/web/api/channels.ts
2091
- import { Hono as Hono5 } from "hono";
2268
+ import { Hono as Hono6 } from "hono";
2092
2269
  function buildEnv(name) {
2093
2270
  return { ...loadMergedEnv(name), VOLUTE_MIND: name, VOLUTE_MIND_DIR: mindDir(name) };
2094
2271
  }
2095
- var app5 = new Hono5().post("/:name/channels/create", requireSelf(), async (c) => {
2272
+ var app5 = new Hono6().post("/:name/channels/create", requireSelf(), async (c) => {
2096
2273
  const name = c.req.param("name");
2097
2274
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
2098
2275
  const {
@@ -2117,8 +2294,8 @@ var app5 = new Hono5().post("/:name/channels/create", requireSelf(), async (c) =
2117
2294
  var channels_default = app5;
2118
2295
 
2119
2296
  // packages/daemon/src/web/api/config.ts
2120
- import { Hono as Hono6 } from "hono";
2121
- var config = new Hono6();
2297
+ import { Hono as Hono7 } from "hono";
2298
+ var config = new Hono7();
2122
2299
  config.get("/models", async (c) => {
2123
2300
  const enabled = new Set(getEnabledModels());
2124
2301
  const all = await getAvailableModels();
@@ -2145,8 +2322,8 @@ config.get("/status", (c) => {
2145
2322
  var config_default = config;
2146
2323
 
2147
2324
  // packages/daemon/src/web/api/env.ts
2148
- import { Hono as Hono7 } from "hono";
2149
- var app6 = new Hono7().get("/:name/env", requireSelf(), async (c) => {
2325
+ import { Hono as Hono8 } from "hono";
2326
+ var app6 = new Hono8().get("/:name/env", requireSelf(), async (c) => {
2150
2327
  const name = c.req.param("name");
2151
2328
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
2152
2329
  const shared = readEnv(sharedEnvPath());
@@ -2189,7 +2366,7 @@ var app6 = new Hono7().get("/:name/env", requireSelf(), async (c) => {
2189
2366
  writeEnv(path, env);
2190
2367
  return c.json({ ok: true });
2191
2368
  });
2192
- var sharedEnvApp = new Hono7().get("/", requireAdmin, (c) => {
2369
+ var sharedEnvApp = new Hono8().get("/", requireAdmin, (c) => {
2193
2370
  return c.json(readEnv(sharedEnvPath()));
2194
2371
  }).put("/:key", requireAdmin, async (c) => {
2195
2372
  const key = c.req.param("key");
@@ -2219,8 +2396,8 @@ var sharedEnvApp = new Hono7().get("/", requireAdmin, (c) => {
2219
2396
  var env_default = app6;
2220
2397
 
2221
2398
  // packages/daemon/src/web/api/extensions.ts
2222
- import { Hono as Hono8 } from "hono";
2223
- var app7 = new Hono8().get("/", (c) => {
2399
+ import { Hono as Hono9 } from "hono";
2400
+ var app7 = new Hono9().get("/", (c) => {
2224
2401
  return c.json(getLoadedExtensions());
2225
2402
  }).get("/all", (c) => {
2226
2403
  const detail = c.req.query("detail") === "true";
@@ -2267,18 +2444,18 @@ var extensions_default = app7;
2267
2444
  // packages/daemon/src/web/api/file-sharing.ts
2268
2445
  import { readFileSync as readFileSync4, statSync as statSync2 } from "fs";
2269
2446
  import { resolve as resolve4 } from "path";
2270
- import { Hono as Hono9 } from "hono";
2447
+ import { Hono as Hono10 } from "hono";
2271
2448
  async function notifyMind(mindName, message) {
2272
2449
  const entry = await findMind(mindName);
2273
2450
  if (!entry) return;
2274
2451
  try {
2275
- const { sendSystemMessage } = await import("./system-chat-4DQZOEZI.js");
2452
+ const { sendSystemMessage } = await import("./system-chat-E65T2HSW.js");
2276
2453
  await sendSystemMessage(mindName, message);
2277
2454
  } catch (err) {
2278
2455
  logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
2279
2456
  }
2280
2457
  }
2281
- var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
2458
+ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
2282
2459
  const senderName = c.req.param("name");
2283
2460
  const senderEntry = await findMind(senderName);
2284
2461
  if (!senderEntry) return c.json({ error: "Sender mind not found" }, 404);
@@ -2398,7 +2575,7 @@ var file_sharing_default = app8;
2398
2575
  import { mkdirSync as mkdirSync3, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
2399
2576
  import { readdir, readFile, realpath, stat } from "fs/promises";
2400
2577
  import { extname as extname2, resolve as resolve5 } from "path";
2401
- import { Hono as Hono10 } from "hono";
2578
+ import { Hono as Hono11 } from "hono";
2402
2579
  var AVATAR_MIME2 = {
2403
2580
  ".png": "image/png",
2404
2581
  ".jpg": "image/jpeg",
@@ -2425,7 +2602,7 @@ var MIME_TYPES = {
2425
2602
  ".woff": "font/woff",
2426
2603
  ".woff2": "font/woff2"
2427
2604
  };
2428
- var app9 = new Hono10().post("/:name/avatar", requireSelf(), async (c) => {
2605
+ var app9 = new Hono11().post("/:name/avatar", requireSelf(), async (c) => {
2429
2606
  const name = c.req.param("name");
2430
2607
  const entry = await findMind(name);
2431
2608
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -2570,7 +2747,7 @@ var files_default = app9;
2570
2747
 
2571
2748
  // packages/daemon/src/web/api/history.ts
2572
2749
  import { and as and3, desc as desc2, eq as eq3, gte, inArray, sql } from "drizzle-orm";
2573
- import { Hono as Hono11 } from "hono";
2750
+ import { Hono as Hono12 } from "hono";
2574
2751
  async function resolveMindFilter(c) {
2575
2752
  const user = c.get("user");
2576
2753
  if (user.role === "admin" || user.role === "system") {
@@ -2578,7 +2755,7 @@ async function resolveMindFilter(c) {
2578
2755
  }
2579
2756
  return getBaseName(user.username);
2580
2757
  }
2581
- var history = new Hono11().get("/turns", async (c) => {
2758
+ var history = new Hono12().get("/turns", async (c) => {
2582
2759
  const mindFilter = await resolveMindFilter(c);
2583
2760
  const turnIdFilter = c.req.query("turnId");
2584
2761
  const turnIdsFilter = c.req.query("turnIds");
@@ -2969,7 +3146,7 @@ var history = new Hono11().get("/turns", async (c) => {
2969
3146
  var history_default = history;
2970
3147
 
2971
3148
  // packages/daemon/src/web/api/keys.ts
2972
- import { Hono as Hono12 } from "hono";
3149
+ import { Hono as Hono13 } from "hono";
2973
3150
 
2974
3151
  // packages/daemon/src/lib/mind/identity.ts
2975
3152
  import { createHash, generateKeyPairSync, sign, verify } from "crypto";
@@ -3029,7 +3206,7 @@ async function publishPublicKey(mindName, publicKeyPem) {
3029
3206
  }
3030
3207
 
3031
3208
  // packages/daemon/src/web/api/keys.ts
3032
- var app10 = new Hono12().get("/:fingerprint", async (c) => {
3209
+ var app10 = new Hono13().get("/:fingerprint", async (c) => {
3033
3210
  const fingerprint = c.req.param("fingerprint");
3034
3211
  for (const entry of await readRegistry()) {
3035
3212
  try {
@@ -3049,9 +3226,9 @@ var keys_default = app10;
3049
3226
  import { spawn as spawn2 } from "child_process";
3050
3227
  import { existsSync as existsSync5 } from "fs";
3051
3228
  import { resolve as resolve7 } from "path";
3052
- import { Hono as Hono13 } from "hono";
3229
+ import { Hono as Hono14 } from "hono";
3053
3230
  import { streamSSE as streamSSE3 } from "hono/streaming";
3054
- var app11 = new Hono13().get("/:name/logs", requireSelf(), async (c) => {
3231
+ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
3055
3232
  const name = c.req.param("name");
3056
3233
  const entry = await findMind(name);
3057
3234
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -3102,10 +3279,10 @@ var app11 = new Hono13().get("/:name/logs", requireSelf(), async (c) => {
3102
3279
  var logs_default = app11;
3103
3280
 
3104
3281
  // packages/daemon/src/web/api/mind-skills.ts
3105
- import { zValidator as zValidator4 } from "@hono/zod-validator";
3106
- import { Hono as Hono14 } from "hono";
3107
- import { z as z4 } from "zod";
3108
- var app12 = new Hono14().get("/:name/skills", async (c) => {
3282
+ import { zValidator as zValidator5 } from "@hono/zod-validator";
3283
+ import { Hono as Hono15 } from "hono";
3284
+ import { z as z5 } from "zod";
3285
+ var app12 = new Hono15().get("/:name/skills", async (c) => {
3109
3286
  const name = c.req.param("name");
3110
3287
  const entry = await findMind(name);
3111
3288
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -3115,7 +3292,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
3115
3292
  }).post(
3116
3293
  "/:name/skills/install",
3117
3294
  requireSelf(),
3118
- zValidator4("json", z4.object({ skillId: z4.string() })),
3295
+ zValidator5("json", z5.object({ skillId: z5.string() })),
3119
3296
  async (c) => {
3120
3297
  const name = c.req.param("name");
3121
3298
  const entry = await findMind(name);
@@ -3133,7 +3310,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
3133
3310
  ).post(
3134
3311
  "/:name/skills/update",
3135
3312
  requireSelf(),
3136
- zValidator4("json", z4.object({ skillId: z4.string() })),
3313
+ zValidator5("json", z5.object({ skillId: z5.string() })),
3137
3314
  async (c) => {
3138
3315
  const name = c.req.param("name");
3139
3316
  const entry = await findMind(name);
@@ -3151,7 +3328,7 @@ var app12 = new Hono14().get("/:name/skills", async (c) => {
3151
3328
  ).post(
3152
3329
  "/:name/skills/publish",
3153
3330
  requireSelf(),
3154
- zValidator4("json", z4.object({ skillId: z4.string() })),
3331
+ zValidator5("json", z5.object({ skillId: z5.string() })),
3155
3332
  async (c) => {
3156
3333
  const name = c.req.param("name");
3157
3334
  const entry = await findMind(name);
@@ -3192,11 +3369,11 @@ import {
3192
3369
  rmSync as rmSync4,
3193
3370
  writeFileSync as writeFileSync7
3194
3371
  } from "fs";
3195
- import { basename, resolve as resolve10 } from "path";
3196
- import { zValidator as zValidator5 } from "@hono/zod-validator";
3372
+ import { relative, resolve as resolve10 } from "path";
3373
+ import { zValidator as zValidator6 } from "@hono/zod-validator";
3197
3374
  import { and as and5, desc as desc4, eq as eq5, sql as sql3 } from "drizzle-orm";
3198
- import { Hono as Hono15 } from "hono";
3199
- import { z as z5 } from "zod";
3375
+ import { Hono as Hono16 } from "hono";
3376
+ import { z as z6 } from "zod";
3200
3377
 
3201
3378
  // packages/daemon/src/lib/daemon/turn-lifecycle.ts
3202
3379
  import { and as and4, desc as desc3, eq as eq4, inArray as inArray2, sql as sql2 } from "drizzle-orm";
@@ -3313,13 +3490,18 @@ async function handleMindEvent(mind, event) {
3313
3490
  }).returning({ id: mindHistory.id });
3314
3491
  insertedId = result[0]?.id;
3315
3492
  } catch (err) {
3316
- llog.error(`failed to persist event for ${mind}`, logger_default.errorData(err));
3493
+ llog.error(
3494
+ `HISTORY GAP: failed to persist ${event.type} event for ${mind}${event.session ? ` (session ${event.session})` : ""}${event.channel ? ` on ${event.channel}` : ""}`,
3495
+ logger_default.errorData(err)
3496
+ );
3317
3497
  }
3318
3498
  if (event.type === "tool_use" && insertedId != null) {
3319
- trackToolUse(mind, event.session, insertedId);
3499
+ const toolUseId = typeof event.metadata?.id === "string" ? event.metadata.id : void 0;
3500
+ trackToolUse(mind, event.session, insertedId, toolUseId);
3320
3501
  }
3321
3502
  if (event.type === "tool_result" && turnId && event.content) {
3322
- const toolUseEventId = getLastToolUseEventId(mind, event.session);
3503
+ const resultToolUseId = typeof event.metadata?.tool_use_id === "string" ? event.metadata.tool_use_id : void 0;
3504
+ const toolUseEventId = getToolUseEventId(mind, event.session, resultToolUseId);
3323
3505
  try {
3324
3506
  await linkToolResultToTurn(mind, turnId, event.content, toolUseEventId);
3325
3507
  } catch (err) {
@@ -3382,7 +3564,7 @@ async function handleMindEvent(mind, event) {
3382
3564
  session: event.session,
3383
3565
  kind: "budget",
3384
3566
  reason: "token_budget",
3385
- detail: "You've reached your token budget for this period. Further activity may be paused until the budget resets \u2014 wrap up or prioritize accordingly."
3567
+ detail: "You've used your token budget for this period, so your activity may pause until it resets. This isn't anything you did wrong \u2014 it's just a rest imposed by the budget. Anything that arrives while you're paused will be kept for you. If you're mid-thought, this turn is a good moment to jot it down."
3386
3568
  });
3387
3569
  }
3388
3570
  }
@@ -3482,7 +3664,7 @@ ${content2}`);
3482
3664
  }
3483
3665
 
3484
3666
  // packages/daemon/src/lib/mind/last-known-good.ts
3485
- import { randomBytes } from "crypto";
3667
+ import { randomBytes as randomBytes2 } from "crypto";
3486
3668
  var glog = logger_default.child("last-known-good");
3487
3669
  async function hasSrcChanges(dir, mindName) {
3488
3670
  const status = (await gitExec(["status", "--porcelain", "--", "src"], { cwd: dir, mindName })).trim();
@@ -3491,7 +3673,7 @@ async function hasSrcChanges(dir, mindName) {
3491
3673
  async function rollbackSrcChanges(dir, mindName) {
3492
3674
  if (!await hasSrcChanges(dir, mindName)) return { parked: false };
3493
3675
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3494
- const branch = `broken/${ts}-${randomBytes(4).toString("hex")}`;
3676
+ const branch = `broken/${ts}-${randomBytes2(4).toString("hex")}`;
3495
3677
  const opts = { cwd: dir, mindName };
3496
3678
  await gitExec(["add", "--", "src"], opts);
3497
3679
  await gitExec(
@@ -3753,7 +3935,7 @@ async function getMindStatus(name, port, registryRunning) {
3753
3935
  const manager = getMindManager();
3754
3936
  let status = "stopped";
3755
3937
  try {
3756
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-HINI6Y46.js");
3938
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
3757
3939
  if (getSleepManagerIfReady2()?.isSleeping(name)) {
3758
3940
  status = "sleeping";
3759
3941
  }
@@ -4179,15 +4361,15 @@ function importSessionsFromArchive(dest, tempDir) {
4179
4361
  logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
4180
4362
  }
4181
4363
  }
4182
- var createMindSchema = z5.object({
4183
- name: z5.string(),
4184
- template: z5.string().optional(),
4185
- stage: z5.enum(["seed", "sprouted"]).optional(),
4186
- description: z5.string().optional(),
4187
- model: z5.string().optional(),
4188
- seedSoul: z5.string().optional(),
4189
- skills: z5.array(z5.string()).optional(),
4190
- createdBy: z5.string().optional()
4364
+ var createMindSchema = z6.object({
4365
+ name: z6.string(),
4366
+ template: z6.string().optional(),
4367
+ stage: z6.enum(["seed", "sprouted"]).optional(),
4368
+ description: z6.string().optional(),
4369
+ model: z6.string().optional(),
4370
+ seedSoul: z6.string().optional(),
4371
+ skills: z6.array(z6.string()).optional(),
4372
+ createdBy: z6.string().optional()
4191
4373
  });
4192
4374
  function formatTimeAgo(date) {
4193
4375
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -4231,7 +4413,7 @@ async function proxyToMind(c, path) {
4231
4413
  return c.json({ error: "Failed to reach mind" }, 503);
4232
4414
  }
4233
4415
  }
4234
- var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", createMindSchema), async (c) => {
4416
+ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", createMindSchema), async (c) => {
4235
4417
  const body = c.req.valid("json");
4236
4418
  const { name } = body;
4237
4419
  const template = body.template ?? await resolveTemplate(body.model);
@@ -4248,7 +4430,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
4248
4430
  applyInitFiles(dest);
4249
4431
  const { publicKeyPem } = generateIdentity(dest);
4250
4432
  {
4251
- const { readGlobalConfig: readGlobal } = await import("./setup-MLRKNP7Y.js");
4433
+ const { readGlobalConfig: readGlobal } = await import("./setup-73YBJZQL.js");
4252
4434
  const mindDefaults = readGlobal().mindDefaults;
4253
4435
  const config2 = readVoluteConfig(dest);
4254
4436
  if (!config2) throw new Error("Failed to read volute.json after identity generation");
@@ -4339,7 +4521,7 @@ The human who planted you described you as: "${body.description}"
4339
4521
  }
4340
4522
  let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
4341
4523
  if (body.stage === "seed" && !body.skills) {
4342
- const { isImagegenEnabled } = await import("./setup-MLRKNP7Y.js");
4524
+ const { isImagegenEnabled } = await import("./setup-73YBJZQL.js");
4343
4525
  if (isImagegenEnabled()) {
4344
4526
  skillSet = [...skillSet, "imagegen"];
4345
4527
  }
@@ -4357,7 +4539,7 @@ The human who planted you described you as: "${body.description}"
4357
4539
  try {
4358
4540
  const spiritEntry = await findMind("volute");
4359
4541
  if (spiritEntry) {
4360
- const { spiritDir } = await import("./spirit-IK5OYWMV.js");
4542
+ const { spiritDir } = await import("./spirit-II3HOERM.js");
4361
4543
  const sDir = spiritEntry.dir ?? spiritDir();
4362
4544
  const spiritConfig = readVoluteConfig(sDir) ?? {};
4363
4545
  const schedules = spiritConfig.schedules ?? [];
@@ -4372,7 +4554,7 @@ The human who planted you described you as: "${body.description}"
4372
4554
  });
4373
4555
  spiritConfig.schedules = schedules;
4374
4556
  writeVoluteConfig(sDir, spiritConfig);
4375
- const { getScheduler: getScheduler2 } = await import("./scheduler-CXWQ6Z4E.js");
4557
+ const { getScheduler: getScheduler2 } = await import("./scheduler-YHJRRV6E.js");
4376
4558
  getScheduler2().loadSchedules("volute", sDir);
4377
4559
  }
4378
4560
  }
@@ -4637,7 +4819,7 @@ ${user.trimEnd()}
4637
4819
  const manager = getMindManager();
4638
4820
  try {
4639
4821
  if (context?.type === "reload") {
4640
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-HINI6Y46.js");
4822
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
4641
4823
  const sleepState = getSleepManagerIfReady2()?.getState(name);
4642
4824
  if (sleepState?.sleeping) {
4643
4825
  logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
@@ -4764,7 +4946,7 @@ ${user.trimEnd()}
4764
4946
  const name = c.req.param("name");
4765
4947
  const entry = await findMind(name);
4766
4948
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4767
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-HINI6Y46.js");
4949
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
4768
4950
  const sm = getSleepManagerIfReady2();
4769
4951
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4770
4952
  return c.json(sm.getState(name));
@@ -4772,7 +4954,7 @@ ${user.trimEnd()}
4772
4954
  const name = c.req.param("name");
4773
4955
  const entry = await findMind(name);
4774
4956
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4775
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-HINI6Y46.js");
4957
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
4776
4958
  const sm = getSleepManagerIfReady2();
4777
4959
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4778
4960
  if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
@@ -4792,7 +4974,7 @@ ${user.trimEnd()}
4792
4974
  const name = c.req.param("name");
4793
4975
  const entry = await findMind(name);
4794
4976
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4795
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-HINI6Y46.js");
4977
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
4796
4978
  const sm = getSleepManagerIfReady2();
4797
4979
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4798
4980
  const sleepState = sm.getState(name);
@@ -4807,7 +4989,7 @@ ${user.trimEnd()}
4807
4989
  const name = c.req.param("name");
4808
4990
  const entry = await findMind(name);
4809
4991
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4810
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-HINI6Y46.js");
4992
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
4811
4993
  const sm = getSleepManagerIfReady2();
4812
4994
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4813
4995
  const flushed = await sm.flushQueuedMessages(name);
@@ -4822,7 +5004,17 @@ ${user.trimEnd()}
4822
5004
  const profile = config2.profile ?? {};
4823
5005
  if (body.displayName !== void 0) profile.displayName = body.displayName;
4824
5006
  if (body.description !== void 0) profile.description = body.description;
4825
- if (body.avatar !== void 0) profile.avatar = basename(body.avatar);
5007
+ if (body.avatar !== void 0) {
5008
+ const homeDir = resolve10(dir, "home");
5009
+ const avatarPath = safeResolveWithinBase(homeDir, body.avatar);
5010
+ if (!avatarPath) {
5011
+ return c.json({ error: "Avatar path must be inside the mind's home directory" }, 400);
5012
+ }
5013
+ if (!existsSync7(avatarPath)) {
5014
+ return c.json({ error: `Avatar file not found: ${relative(homeDir, avatarPath)}` }, 400);
5015
+ }
5016
+ profile.avatar = relative(homeDir, avatarPath);
5017
+ }
4826
5018
  config2.profile = profile;
4827
5019
  writeVoluteConfig(dir, config2);
4828
5020
  const { syncMindProfile: syncMindProfile2 } = await import("./auth-FRZJVQCS.js");
@@ -4872,7 +5064,7 @@ ${user.trimEnd()}
4872
5064
  const config2 = readVoluteConfig(dir);
4873
5065
  const displayNameSet = !!config2?.profile?.displayName;
4874
5066
  const avatarSet = !!config2?.profile?.avatar;
4875
- const { isImagegenEnabled } = await import("./setup-MLRKNP7Y.js");
5067
+ const { isImagegenEnabled } = await import("./setup-73YBJZQL.js");
4876
5068
  const imagegenEnabled = isImagegenEnabled();
4877
5069
  const done = [];
4878
5070
  const remaining = [];
@@ -4910,7 +5102,7 @@ ${user.trimEnd()}
4910
5102
  try {
4911
5103
  const spiritEntry = await findMind("volute");
4912
5104
  if (spiritEntry) {
4913
- const { spiritDir } = await import("./spirit-IK5OYWMV.js");
5105
+ const { spiritDir } = await import("./spirit-II3HOERM.js");
4914
5106
  const sDir = spiritEntry.dir ?? spiritDir();
4915
5107
  const spiritConfig = readVoluteConfig(sDir);
4916
5108
  if (spiritConfig?.schedules) {
@@ -4918,7 +5110,7 @@ ${user.trimEnd()}
4918
5110
  spiritConfig.schedules = spiritConfig.schedules.filter((s) => s.id !== nurtureId);
4919
5111
  if (spiritConfig.schedules.length === 0) spiritConfig.schedules = void 0;
4920
5112
  writeVoluteConfig(sDir, spiritConfig);
4921
- const { getScheduler: getScheduler2 } = await import("./scheduler-CXWQ6Z4E.js");
5113
+ const { getScheduler: getScheduler2 } = await import("./scheduler-YHJRRV6E.js");
4922
5114
  getScheduler2().loadSchedules("volute", sDir);
4923
5115
  }
4924
5116
  }
@@ -5303,16 +5495,16 @@ ${user.trimEnd()}
5303
5495
  }).put(
5304
5496
  "/:name/config",
5305
5497
  requireSelf(),
5306
- zValidator5(
5498
+ zValidator6(
5307
5499
  "json",
5308
- z5.object({
5309
- model: z5.string().optional(),
5310
- thinkingLevel: z5.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
5311
- maxThinkingTokens: z5.number().int().positive().nullable().optional(),
5312
- tokenBudget: z5.number().int().positive().nullable().optional(),
5313
- tokenBudgetPeriodMinutes: z5.number().int().positive().nullable().optional(),
5314
- compaction: z5.object({ maxContextTokens: z5.number().int().positive().nullable().optional() }).nullable().optional(),
5315
- unescapeNewlines: z5.boolean().optional()
5500
+ z6.object({
5501
+ model: z6.string().optional(),
5502
+ thinkingLevel: z6.enum(["off", "minimal", "low", "medium", "high", "xhigh"]).optional(),
5503
+ maxThinkingTokens: z6.number().int().positive().nullable().optional(),
5504
+ tokenBudget: z6.number().int().positive().nullable().optional(),
5505
+ tokenBudgetPeriodMinutes: z6.number().int().positive().nullable().optional(),
5506
+ compaction: z6.object({ maxContextTokens: z6.number().int().positive().nullable().optional() }).nullable().optional(),
5507
+ unescapeNewlines: z6.boolean().optional()
5316
5508
  })
5317
5509
  ),
5318
5510
  async (c) => {
@@ -5353,7 +5545,7 @@ ${user.trimEnd()}
5353
5545
  }
5354
5546
  writeVoluteConfig(dir, existing);
5355
5547
  if (body.unescapeNewlines !== void 0) {
5356
- const { clearEchoTextCache } = await import("./echo-text-T7ZA5EYD.js");
5548
+ const { clearEchoTextCache } = await import("./echo-text-NQXYYYCU.js");
5357
5549
  clearEchoTextCache(name);
5358
5550
  }
5359
5551
  const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.maxThinkingTokens !== void 0 || body.compaction !== void 0;
@@ -5430,7 +5622,7 @@ ${user.trimEnd()}
5430
5622
  }
5431
5623
  if (entry.mindType === "spirit" && body.model !== void 0) {
5432
5624
  try {
5433
- const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-MLRKNP7Y.js");
5625
+ const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-73YBJZQL.js");
5434
5626
  const globalConfig = readGlobalConfig2();
5435
5627
  globalConfig.spiritModel = body.model;
5436
5628
  writeGlobalConfig2(globalConfig);
@@ -5458,7 +5650,7 @@ ${user.trimEnd()}
5458
5650
  if (!body.systemPrompt || !body.message) {
5459
5651
  return c.json({ error: "systemPrompt and message required" }, 400);
5460
5652
  }
5461
- const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-7PHSRDMO.js");
5653
+ const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-534WUSDL.js");
5462
5654
  if (!isAiConfigured()) {
5463
5655
  return c.json({ error: "AI service not configured" }, 503);
5464
5656
  }
@@ -5750,11 +5942,11 @@ ${lines.join("\n")}` });
5750
5942
  var minds_default = app13;
5751
5943
 
5752
5944
  // packages/daemon/src/web/api/prompts.ts
5753
- import { zValidator as zValidator6 } from "@hono/zod-validator";
5945
+ import { zValidator as zValidator7 } from "@hono/zod-validator";
5754
5946
  import { eq as eq6, sql as sql4 } from "drizzle-orm";
5755
- import { Hono as Hono16 } from "hono";
5756
- import { z as z6 } from "zod";
5757
- var app14 = new Hono16().get("/", async (c) => {
5947
+ import { Hono as Hono17 } from "hono";
5948
+ import { z as z7 } from "zod";
5949
+ var app14 = new Hono17().get("/", async (c) => {
5758
5950
  let rows;
5759
5951
  try {
5760
5952
  const db = await getDb();
@@ -5777,7 +5969,7 @@ var app14 = new Hono16().get("/", async (c) => {
5777
5969
  };
5778
5970
  });
5779
5971
  return c.json(prompts);
5780
- }).put("/:key", requireAdmin, zValidator6("json", z6.object({ content: z6.string() })), async (c) => {
5972
+ }).put("/:key", requireAdmin, zValidator7("json", z7.object({ content: z7.string() })), async (c) => {
5781
5973
  const key = c.req.param("key");
5782
5974
  if (!PROMPT_KEYS.includes(key)) {
5783
5975
  return c.json({ error: "Unknown prompt key" }, 404);
@@ -5801,8 +5993,8 @@ var app14 = new Hono16().get("/", async (c) => {
5801
5993
  var prompts_default = app14;
5802
5994
 
5803
5995
  // packages/daemon/src/web/api/schedules.ts
5804
- import { CronExpressionParser } from "cron-parser";
5805
- import { Hono as Hono17 } from "hono";
5996
+ import { CronExpressionParser as CronExpressionParser3 } from "cron-parser";
5997
+ import { Hono as Hono18 } from "hono";
5806
5998
  var slog2 = logger_default.child("schedules");
5807
5999
  function readSchedules(dir) {
5808
6000
  return readVoluteConfig(dir)?.schedules ?? [];
@@ -5819,7 +6011,7 @@ function writeSchedules(name, dir, schedules) {
5819
6011
  data: { schedules }
5820
6012
  });
5821
6013
  }
5822
- var app15 = new Hono17().get("/:name/clock/status", requireSelf(), async (c) => {
6014
+ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) => {
5823
6015
  const name = c.req.param("name");
5824
6016
  const entry = await findMind(name);
5825
6017
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -5840,14 +6032,14 @@ var app15 = new Hono17().get("/:name/clock/status", requireSelf(), async (c) =>
5840
6032
  }
5841
6033
  } else if (s.cron) {
5842
6034
  try {
5843
- const interval = CronExpressionParser.parse(s.cron);
6035
+ const interval = CronExpressionParser3.parse(s.cron);
5844
6036
  const next = interval.next().toDate();
5845
6037
  upcoming.push({ id: s.id, at: next.toISOString(), type: "cron" });
5846
6038
  } catch {
5847
6039
  slog2.warn(`invalid cron "${s.cron}" for schedule "${s.id}" of ${name}`);
5848
6040
  }
5849
6041
  try {
5850
- const prevInterval = CronExpressionParser.parse(s.cron);
6042
+ const prevInterval = CronExpressionParser3.parse(s.cron);
5851
6043
  const prev = prevInterval.prev().toDate();
5852
6044
  previous.push({ id: s.id, at: prev.toISOString() });
5853
6045
  } catch {
@@ -5863,13 +6055,13 @@ var app15 = new Hono17().get("/:name/clock/status", requireSelf(), async (c) =>
5863
6055
  }
5864
6056
  } else if (sleepConfig?.enabled && sleepConfig.schedule) {
5865
6057
  try {
5866
- const sleepInterval = CronExpressionParser.parse(sleepConfig.schedule.sleep);
6058
+ const sleepInterval = CronExpressionParser3.parse(sleepConfig.schedule.sleep);
5867
6059
  const nextSleep = sleepInterval.next().toDate();
5868
6060
  upcoming.push({ id: "sleep", at: nextSleep.toISOString(), type: "cron" });
5869
6061
  } catch {
5870
6062
  }
5871
6063
  try {
5872
- const prevWakeInterval = CronExpressionParser.parse(sleepConfig.schedule.wake);
6064
+ const prevWakeInterval = CronExpressionParser3.parse(sleepConfig.schedule.wake);
5873
6065
  const prevWake = prevWakeInterval.prev().toDate();
5874
6066
  previous.push({ id: "sleep", at: prevWake.toISOString() });
5875
6067
  } catch {
@@ -5893,7 +6085,7 @@ var app15 = new Hono17().get("/:name/clock/status", requireSelf(), async (c) =>
5893
6085
  for (const field of ["sleep", "wake"]) {
5894
6086
  if (body.schedule[field]) {
5895
6087
  try {
5896
- CronExpressionParser.parse(body.schedule[field]);
6088
+ CronExpressionParser3.parse(body.schedule[field]);
5897
6089
  } catch {
5898
6090
  return c.json({ error: `Invalid ${field} cron: ${body.schedule[field]}` }, 400);
5899
6091
  }
@@ -5939,7 +6131,7 @@ var app15 = new Hono17().get("/:name/clock/status", requireSelf(), async (c) =>
5939
6131
  }
5940
6132
  if (body.cron) {
5941
6133
  try {
5942
- CronExpressionParser.parse(body.cron);
6134
+ CronExpressionParser3.parse(body.cron);
5943
6135
  } catch {
5944
6136
  return c.json({ error: `Invalid cron expression: ${body.cron}` }, 400);
5945
6137
  }
@@ -5986,7 +6178,7 @@ var app15 = new Hono17().get("/:name/clock/status", requireSelf(), async (c) =>
5986
6178
  }
5987
6179
  if (body.cron !== void 0) {
5988
6180
  try {
5989
- CronExpressionParser.parse(body.cron);
6181
+ CronExpressionParser3.parse(body.cron);
5990
6182
  } catch {
5991
6183
  return c.json({ error: `Invalid cron expression: ${body.cron}` }, 400);
5992
6184
  }
@@ -6043,7 +6235,7 @@ var app15 = new Hono17().get("/:name/clock/status", requireSelf(), async (c) =>
6043
6235
  const body = await c.req.text();
6044
6236
  const message = `[webhook: ${event}] ${body}`;
6045
6237
  try {
6046
- const { sendSystemMessage } = await import("./system-chat-4DQZOEZI.js");
6238
+ const { sendSystemMessage } = await import("./system-chat-E65T2HSW.js");
6047
6239
  await sendSystemMessage(name, message);
6048
6240
  return c.json({ ok: true });
6049
6241
  } catch (err) {
@@ -6057,10 +6249,10 @@ var schedules_default = app15;
6057
6249
  import { mkdirSync as mkdirSync7 } from "fs";
6058
6250
  import { homedir as homedir2 } from "os";
6059
6251
  import { resolve as resolve11 } from "path";
6060
- import { Hono as Hono18 } from "hono";
6252
+ import { Hono as Hono19 } from "hono";
6061
6253
  import { setCookie as setCookie2 } from "hono/cookie";
6062
6254
  var DEFAULT_API_URL2 = "https://volute.systems";
6063
- var setup = new Hono18();
6255
+ var setup = new Hono19();
6064
6256
  function writeSetupConfig(systemName, description) {
6065
6257
  const configHome = process.env.VOLUTE_HOME ?? resolve11(homedir2(), ".volute");
6066
6258
  const mindsDir = resolve11(configHome, "minds");
@@ -6354,7 +6546,7 @@ setup.post("/models", async (c) => {
6354
6546
  return c.json({ error: "Spirit model is required" }, 400);
6355
6547
  }
6356
6548
  try {
6357
- const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-7PHSRDMO.js");
6549
+ const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-534WUSDL.js");
6358
6550
  setEnabledModels3(body.models);
6359
6551
  const config2 = readGlobalConfig();
6360
6552
  config2.spiritModel = body.spiritModel.trim();
@@ -6372,8 +6564,8 @@ setup.post("/complete", async (c) => {
6372
6564
  return c.json({ error: "Setup already complete" }, 400);
6373
6565
  }
6374
6566
  try {
6375
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-IK5OYWMV.js");
6376
- const { startSpiritFull } = await import("./mind-service-S3SY7HAM.js");
6567
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-II3HOERM.js");
6568
+ const { startSpiritFull } = await import("./mind-service-BF2LUQMH.js");
6377
6569
  await ensureSpiritProject();
6378
6570
  await syncSpiritTemplate();
6379
6571
  const warnings = [];
@@ -6409,7 +6601,7 @@ setup.post("/complete", async (c) => {
6409
6601
  let spiritConversationId;
6410
6602
  try {
6411
6603
  const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-FRZJVQCS.js");
6412
- const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-WAJYZKOC.js");
6604
+ const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-G4LYMHAA.js");
6413
6605
  const spiritUser = await getOrCreateMindUser2("volute");
6414
6606
  const brains = await listUsersByType2("brain");
6415
6607
  const admin2 = brains.find((u) => u.role === "admin");
@@ -6431,7 +6623,7 @@ setup.post("/complete", async (c) => {
6431
6623
  logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
6432
6624
  if (spiritConversationId && spiritStarted) {
6433
6625
  try {
6434
- const { deliverMessage: deliverMessage2 } = await import("./message-delivery-2EA5LLMD.js");
6626
+ const { deliverMessage: deliverMessage2 } = await import("./message-delivery-IUAXRPFE.js");
6435
6627
  const { listUsersByType: listUsers2 } = await import("./auth-FRZJVQCS.js");
6436
6628
  const admins = await listUsers2("brain");
6437
6629
  const admin2 = admins.find((u) => u.role === "admin");
@@ -6473,8 +6665,8 @@ import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync3, rm
6473
6665
  import { tmpdir } from "os";
6474
6666
  import { join, resolve as resolve12 } from "path";
6475
6667
  import AdmZip from "adm-zip";
6476
- import { Hono as Hono19 } from "hono";
6477
- var app16 = new Hono19().get("/", async (c) => {
6668
+ import { Hono as Hono20 } from "hono";
6669
+ var app16 = new Hono20().get("/", async (c) => {
6478
6670
  const skills = await listSharedSkills();
6479
6671
  return c.json(skills);
6480
6672
  }).get("/defaults/list", async (c) => {
@@ -6598,15 +6790,15 @@ var app16 = new Hono19().get("/", async (c) => {
6598
6790
  var skills_default = app16;
6599
6791
 
6600
6792
  // packages/daemon/src/web/api/typing.ts
6601
- import { zValidator as zValidator7 } from "@hono/zod-validator";
6602
- import { Hono as Hono20 } from "hono";
6603
- import { z as z7 } from "zod";
6604
- var typingSchema = z7.object({
6605
- channel: z7.string().min(1),
6606
- sender: z7.string().min(1),
6607
- active: z7.boolean()
6793
+ import { zValidator as zValidator8 } from "@hono/zod-validator";
6794
+ import { Hono as Hono21 } from "hono";
6795
+ import { z as z8 } from "zod";
6796
+ var typingSchema = z8.object({
6797
+ channel: z8.string().min(1),
6798
+ sender: z8.string().min(1),
6799
+ active: z8.boolean()
6608
6800
  });
6609
- var app17 = new Hono20().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
6801
+ var app17 = new Hono21().post("/:name/typing", zValidator8("json", typingSchema), (c) => {
6610
6802
  const { channel, sender, active } = c.req.valid("json");
6611
6803
  const map = getTypingMap();
6612
6804
  if (active) {
@@ -6630,9 +6822,9 @@ var typing_default = app17;
6630
6822
 
6631
6823
  // packages/daemon/src/web/api/update.ts
6632
6824
  import { spawn as spawn3 } from "child_process";
6633
- import { Hono as Hono21 } from "hono";
6825
+ import { Hono as Hono22 } from "hono";
6634
6826
  var bin;
6635
- var app18 = new Hono21().get("/update", async (c) => {
6827
+ var app18 = new Hono22().get("/update", async (c) => {
6636
6828
  const result = await checkForUpdate();
6637
6829
  return c.json(result);
6638
6830
  }).post("/update", requireAdmin, async (c) => {
@@ -6650,13 +6842,13 @@ var app18 = new Hono21().get("/update", async (c) => {
6650
6842
  var update_default = app18;
6651
6843
 
6652
6844
  // packages/daemon/src/web/api/v1/conversations.ts
6653
- import { zValidator as zValidator8 } from "@hono/zod-validator";
6654
- import { Hono as Hono22 } from "hono";
6655
- import { z as z8 } from "zod";
6656
- var createSchema = z8.object({
6657
- participantNames: z8.array(z8.string()).min(1)
6845
+ import { zValidator as zValidator9 } from "@hono/zod-validator";
6846
+ import { Hono as Hono23 } from "hono";
6847
+ import { z as z9 } from "zod";
6848
+ var createSchema = z9.object({
6849
+ participantNames: z9.array(z9.string()).min(1)
6658
6850
  });
6659
- var app19 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
6851
+ var app19 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6660
6852
  const user = c.get("user");
6661
6853
  const convs = await listConversationsWithParticipants(user.id);
6662
6854
  return c.json(convs);
@@ -6683,7 +6875,7 @@ var app19 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
6683
6875
  }
6684
6876
  const participants = await getParticipants(id);
6685
6877
  return c.json(participants);
6686
- }).post("/", zValidator8("json", createSchema), async (c) => {
6878
+ }).post("/", zValidator9("json", createSchema), async (c) => {
6687
6879
  const user = c.get("user");
6688
6880
  const body = c.req.valid("json");
6689
6881
  const participantIds = /* @__PURE__ */ new Set();
@@ -6732,7 +6924,7 @@ var app19 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
6732
6924
  }
6733
6925
  await markConversationRead(user.id, id);
6734
6926
  return c.json({ ok: true });
6735
- }).put("/:id/private", zValidator8("json", z8.object({ private: z8.boolean() })), async (c) => {
6927
+ }).put("/:id/private", zValidator9("json", z9.object({ private: z9.boolean() })), async (c) => {
6736
6928
  const id = c.req.param("id");
6737
6929
  const user = c.get("user");
6738
6930
  if (!await isParticipantOrOwner(id, user.id)) {
@@ -6752,7 +6944,7 @@ var conversations_default = app19;
6752
6944
 
6753
6945
  // packages/daemon/src/web/api/v1/events.ts
6754
6946
  import { desc as desc5, eq as eq7 } from "drizzle-orm";
6755
- import { Hono as Hono23 } from "hono";
6947
+ import { Hono as Hono24 } from "hono";
6756
6948
  import { streamSSE as streamSSE4 } from "hono/streaming";
6757
6949
 
6758
6950
  // packages/daemon/src/lib/events/brain-presence.ts
@@ -6815,7 +7007,7 @@ function getEventsSince(sinceId, allowedConversationIds, allowedActivityMind) {
6815
7007
  }
6816
7008
 
6817
7009
  // packages/daemon/src/web/api/v1/events.ts
6818
- var app20 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
7010
+ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
6819
7011
  const user = c.get("user");
6820
7012
  const since = c.req.query("since");
6821
7013
  const sinceId = since ? Number(since) : 0;
@@ -6833,7 +7025,7 @@ var app20 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6833
7025
  conversations2 = await listConversationsWithParticipants(user.id);
6834
7026
  if (conversations2.length > 0) {
6835
7027
  const convIds = conversations2.map((c2) => c2.id);
6836
- const unreads = await getUnreadCounts(user.id, convIds);
7028
+ const unreads = await getUnreadCounts(user.id, convIds, user.username);
6837
7029
  for (const conv of conversations2) {
6838
7030
  conv.unreadCount = unreads[conv.id] ?? 0;
6839
7031
  }
@@ -6927,7 +7119,7 @@ var events_default = app20;
6927
7119
  // packages/daemon/src/web/api/variants.ts
6928
7120
  import { existsSync as existsSync9, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
6929
7121
  import { resolve as resolve14 } from "path";
6930
- import { Hono as Hono24 } from "hono";
7122
+ import { Hono as Hono25 } from "hono";
6931
7123
 
6932
7124
  // packages/daemon/src/lib/mind/spawn-server.ts
6933
7125
  import { spawn as spawn4 } from "child_process";
@@ -7052,7 +7244,7 @@ async function verify2(port) {
7052
7244
  }
7053
7245
 
7054
7246
  // packages/daemon/src/web/api/variants.ts
7055
- var app21 = new Hono24().get("/:name/variants", async (c) => {
7247
+ var app21 = new Hono25().get("/:name/variants", async (c) => {
7056
7248
  const name = c.req.param("name");
7057
7249
  const entry = await findMind(name);
7058
7250
  if (!entry) return c.json({ error: "Mind not found" }, 404);
@@ -7129,7 +7321,9 @@ var app21 = new Hono24().get("/:name/variants", async (c) => {
7129
7321
  await addVariant(variantName, mindName, variantPort, variantDir, variantName);
7130
7322
  if (!body.noStart) {
7131
7323
  try {
7132
- await getMindManager().startMind(variantName);
7324
+ const manager = getMindManager();
7325
+ manager.setPendingContext(variantName, { type: "split", parent: mindName });
7326
+ await manager.startMind(variantName);
7133
7327
  } catch (e) {
7134
7328
  const msg = e instanceof Error ? e.message : String(e);
7135
7329
  return c.json({ error: `Variant created but failed to start: ${msg}` }, 500);
@@ -7281,22 +7475,22 @@ var app21 = new Hono24().get("/:name/variants", async (c) => {
7281
7475
  var variants_default = app21;
7282
7476
 
7283
7477
  // packages/daemon/src/web/api/volute/channels.ts
7284
- import { zValidator as zValidator9 } from "@hono/zod-validator";
7285
- import { Hono as Hono25 } from "hono";
7286
- import { z as z9 } from "zod";
7287
- var channelSettingsSchema = z9.object({
7288
- description: z9.string().nullable().optional(),
7289
- rules: z9.string().nullable().optional(),
7290
- charLimit: z9.number().int().positive().nullable().optional(),
7291
- private: z9.boolean().optional()
7478
+ import { zValidator as zValidator10 } from "@hono/zod-validator";
7479
+ import { Hono as Hono26 } from "hono";
7480
+ import { z as z10 } from "zod";
7481
+ var channelSettingsSchema = z10.object({
7482
+ description: z10.string().nullable().optional(),
7483
+ rules: z10.string().nullable().optional(),
7484
+ charLimit: z10.number().int().positive().nullable().optional(),
7485
+ private: z10.boolean().optional()
7292
7486
  });
7293
- var createSchema2 = z9.object({
7294
- name: z9.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
7487
+ var createSchema2 = z10.object({
7488
+ name: z10.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
7295
7489
  }).merge(channelSettingsSchema);
7296
- var inviteSchema = z9.object({
7297
- username: z9.string().min(1)
7490
+ var inviteSchema = z10.object({
7491
+ username: z10.string().min(1)
7298
7492
  });
7299
- var app22 = new Hono25().get("/", async (c) => {
7493
+ var app22 = new Hono26().get("/", async (c) => {
7300
7494
  const user = c.get("user");
7301
7495
  const channels2 = await listChannels();
7302
7496
  const results = await Promise.all(
@@ -7307,7 +7501,7 @@ var app22 = new Hono25().get("/", async (c) => {
7307
7501
  })
7308
7502
  );
7309
7503
  return c.json(results);
7310
- }).post("/", zValidator9("json", createSchema2), async (c) => {
7504
+ }).post("/", zValidator10("json", createSchema2), async (c) => {
7311
7505
  const user = c.get("user");
7312
7506
  const body = c.req.valid("json");
7313
7507
  try {
@@ -7335,7 +7529,7 @@ var app22 = new Hono25().get("/", async (c) => {
7335
7529
  participants,
7336
7530
  settings: formatChannelSettings(settings)
7337
7531
  });
7338
- }).patch("/:name", zValidator9("json", channelSettingsSchema), async (c) => {
7532
+ }).patch("/:name", zValidator10("json", channelSettingsSchema), async (c) => {
7339
7533
  const name = c.req.param("name");
7340
7534
  const user = c.get("user");
7341
7535
  const body = c.req.valid("json");
@@ -7367,7 +7561,7 @@ var app22 = new Hono25().get("/", async (c) => {
7367
7561
  if (!ch) return c.json({ error: "Channel not found" }, 404);
7368
7562
  const participants = await getParticipants(ch.id);
7369
7563
  return c.json(participants);
7370
- }).post("/:name/invite", zValidator9("json", inviteSchema), async (c) => {
7564
+ }).post("/:name/invite", zValidator10("json", inviteSchema), async (c) => {
7371
7565
  const name = c.req.param("name");
7372
7566
  const inviter = c.get("user");
7373
7567
  const { username } = c.req.valid("json");
@@ -7393,27 +7587,27 @@ var app22 = new Hono25().get("/", async (c) => {
7393
7587
  var channels_default2 = app22;
7394
7588
 
7395
7589
  // packages/daemon/src/web/api/volute/chat.ts
7396
- import { zValidator as zValidator10 } from "@hono/zod-validator";
7397
- import { Hono as Hono26 } from "hono";
7590
+ import { zValidator as zValidator11 } from "@hono/zod-validator";
7591
+ import { Hono as Hono27 } from "hono";
7398
7592
  import { streamSSE as streamSSE5 } from "hono/streaming";
7399
- import { z as z10 } from "zod";
7400
- var fileSchema = z10.object({
7401
- filename: z10.string(),
7402
- data: z10.string()
7593
+ import { z as z11 } from "zod";
7594
+ var fileSchema = z11.object({
7595
+ filename: z11.string(),
7596
+ data: z11.string()
7403
7597
  // base64
7404
7598
  });
7405
- var chatSchema = z10.object({
7406
- message: z10.string().optional(),
7407
- conversationId: z10.string().optional(),
7408
- targetMind: z10.string().optional(),
7409
- sender: z10.string().optional(),
7410
- images: z10.array(
7411
- z10.object({
7412
- media_type: z10.string(),
7413
- data: z10.string()
7599
+ var chatSchema = z11.object({
7600
+ message: z11.string().optional(),
7601
+ conversationId: z11.string().optional(),
7602
+ targetMind: z11.string().optional(),
7603
+ sender: z11.string().optional(),
7604
+ images: z11.array(
7605
+ z11.object({
7606
+ media_type: z11.string(),
7607
+ data: z11.string()
7414
7608
  })
7415
7609
  ).optional(),
7416
- files: z10.array(fileSchema).optional()
7610
+ files: z11.array(fileSchema).optional()
7417
7611
  });
7418
7612
  var MAX_FILE_SIZE2 = 50 * 1024 * 1024;
7419
7613
  function stageFilesForMinds(files, targetMinds, senderName) {
@@ -7441,9 +7635,9 @@ function stageFilesForMinds(files, targetMinds, senderName) {
7441
7635
  }
7442
7636
  return { notifications };
7443
7637
  }
7444
- var unifiedChatApp = new Hono26().post(
7638
+ var unifiedChatApp = new Hono27().post(
7445
7639
  "/chat",
7446
- zValidator10("json", chatSchema),
7640
+ zValidator11("json", chatSchema),
7447
7641
  async (c) => {
7448
7642
  const user = c.get("user");
7449
7643
  const body = c.req.valid("json");
@@ -7635,7 +7829,7 @@ var unifiedChatApp = new Hono26().post(
7635
7829
  return c.json({ ok: true, conversationId, outboundId });
7636
7830
  }
7637
7831
  );
7638
- var app23 = new Hono26().get("/:name/conversations/:id/events", async (c) => {
7832
+ var app23 = new Hono27().get("/:name/conversations/:id/events", async (c) => {
7639
7833
  const conversationId = c.req.param("id");
7640
7834
  const user = c.get("user");
7641
7835
  if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
@@ -7664,14 +7858,14 @@ var app23 = new Hono26().get("/:name/conversations/:id/events", async (c) => {
7664
7858
  var chat_default = app23;
7665
7859
 
7666
7860
  // packages/daemon/src/web/api/volute/conversations.ts
7667
- import { zValidator as zValidator11 } from "@hono/zod-validator";
7668
- import { Hono as Hono27 } from "hono";
7669
- import { z as z11 } from "zod";
7670
- var createConvSchema = z11.object({
7671
- participantIds: z11.array(z11.number()).optional(),
7672
- participantNames: z11.array(z11.string()).optional()
7861
+ import { zValidator as zValidator12 } from "@hono/zod-validator";
7862
+ import { Hono as Hono28 } from "hono";
7863
+ import { z as z12 } from "zod";
7864
+ var createConvSchema = z12.object({
7865
+ participantIds: z12.array(z12.number()).optional(),
7866
+ participantNames: z12.array(z12.string()).optional()
7673
7867
  });
7674
- var app24 = new Hono27().get("/:name/conversations", async (c) => {
7868
+ var app24 = new Hono28().get("/:name/conversations", async (c) => {
7675
7869
  const name = c.req.param("name");
7676
7870
  const user = c.get("user");
7677
7871
  let lookupId = user.id;
@@ -7681,7 +7875,7 @@ var app24 = new Hono27().get("/:name/conversations", async (c) => {
7681
7875
  }
7682
7876
  const convs = await listConversationsForUser(lookupId);
7683
7877
  return c.json(convs);
7684
- }).post("/:name/conversations", zValidator11("json", createConvSchema), async (c) => {
7878
+ }).post("/:name/conversations", zValidator12("json", createConvSchema), async (c) => {
7685
7879
  const name = c.req.param("name");
7686
7880
  const user = c.get("user");
7687
7881
  const body = c.req.valid("json");
@@ -7777,7 +7971,7 @@ var conversations_default2 = app24;
7777
7971
 
7778
7972
  // packages/daemon/src/web/app.ts
7779
7973
  var httpLog = logger_default.child("http");
7780
- var app25 = new Hono28();
7974
+ var app25 = new Hono29();
7781
7975
  app25.onError((err, c) => {
7782
7976
  if (err instanceof HTTPException) {
7783
7977
  return err.getResponse();
@@ -7844,10 +8038,11 @@ app25.use("/api/skills/*", authMiddleware);
7844
8038
  app25.use("/api/extensions/*", authMiddleware);
7845
8039
  app25.use("/api/bridges/*", authMiddleware);
7846
8040
  app25.use("/api/config/*", authMiddleware);
8041
+ app25.use("/api/backup/*", authMiddleware);
7847
8042
  app25.use("/api/v1/*", authMiddleware);
7848
8043
  app25.route("/api/setup", setup_default);
7849
8044
  app25.route("/api/config", config_default);
7850
- var routes = app25.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/minds", minds_default).route("/api/minds", chat_default).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/bridges", bridges_default).route("/api/extensions", extensions_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", unifiedChatApp).route("/api/v1/channels", channels_default2).route("/api/v1/history", history_default);
8045
+ var routes = app25.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/backup", backup_default).route("/api/minds", minds_default).route("/api/minds", chat_default).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/bridges", bridges_default).route("/api/extensions", extensions_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", unifiedChatApp).route("/api/v1/channels", channels_default2).route("/api/v1/history", history_default);
7851
8046
  app25.route("/api/v1/minds", minds_default);
7852
8047
  app25.route("/api/v1/minds", chat_default);
7853
8048
  app25.route("/api/v1/minds", typing_default);
@@ -7906,8 +8101,8 @@ async function startServer({
7906
8101
  const ext = extname3(filePath);
7907
8102
  const mime = MIME_TYPES2[ext] || "application/octet-stream";
7908
8103
  const body = await readFile2(filePath);
7909
- const basename2 = filePath.slice(filePath.lastIndexOf("/") + 1);
7910
- const nameWithoutExt = basename2.slice(0, basename2.lastIndexOf("."));
8104
+ const basename = filePath.slice(filePath.lastIndexOf("/") + 1);
8105
+ const nameWithoutExt = basename.slice(0, basename.lastIndexOf("."));
7911
8106
  const isHashed = /[-.][\da-f]{8,}$/.test(nameWithoutExt);
7912
8107
  const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
7913
8108
  return c.body(body, 200, { "Content-Type": mime, "Cache-Control": cacheControl });
@@ -8005,10 +8200,10 @@ async function startDaemon(opts) {
8005
8200
  const DAEMON_TOKEN_PATH = resolve16(systemDir, "daemon-token");
8006
8201
  mkdirSync10(home, { recursive: true });
8007
8202
  ensureSystemDir();
8008
- const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-MLRKNP7Y.js");
8203
+ const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-73YBJZQL.js");
8009
8204
  migrateConfigSecrets();
8010
8205
  migrateSetupCompleted();
8011
- const { migrateAiModelQualification } = await import("./ai-service-7PHSRDMO.js");
8206
+ const { migrateAiModelQualification } = await import("./ai-service-534WUSDL.js");
8012
8207
  migrateAiModelQualification();
8013
8208
  await (await import("./db-JFLCJCUG.js")).getDb();
8014
8209
  try {
@@ -8025,7 +8220,7 @@ async function startDaemon(opts) {
8025
8220
  } catch (err) {
8026
8221
  logger_default.warn("avatar size migration failed", logger_default.errorData(err));
8027
8222
  }
8028
- const { initSandbox } = await import("./sandbox-IOPWWILM.js");
8223
+ const { initSandbox } = await import("./sandbox-72MWV4JL.js");
8029
8224
  await initSandbox();
8030
8225
  try {
8031
8226
  await syncBuiltinSkills();
@@ -8060,8 +8255,8 @@ async function startDaemon(opts) {
8060
8255
  logger_default.errorData(err)
8061
8256
  );
8062
8257
  }
8063
- const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes2(32).toString("hex");
8064
- const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-MLRKNP7Y.js");
8258
+ const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes3(32).toString("hex");
8259
+ const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-73YBJZQL.js");
8065
8260
  const globalCfg = readGlobalConfig2();
8066
8261
  let tls;
8067
8262
  if (opts.tailscale || globalCfg.tailscale) {
@@ -8112,6 +8307,8 @@ async function startDaemon(opts) {
8112
8307
  sleepManager.start();
8113
8308
  const summarizer = initSummarizer();
8114
8309
  summarizer.start();
8310
+ const backupManager2 = initBackupManager();
8311
+ backupManager2.start();
8115
8312
  const unsubscribeWebhook = initWebhook();
8116
8313
  const orphanedTurns = await completeOrphanedTurns();
8117
8314
  summarizeOrphanedTurns(orphanedTurns);
@@ -8144,10 +8341,10 @@ async function startDaemon(opts) {
8144
8341
  await Promise.all(workers);
8145
8342
  }
8146
8343
  try {
8147
- const { isSetupComplete: isSetupComplete2 } = await import("./setup-MLRKNP7Y.js");
8344
+ const { isSetupComplete: isSetupComplete2 } = await import("./setup-73YBJZQL.js");
8148
8345
  if (isSetupComplete2()) {
8149
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-IK5OYWMV.js");
8150
- const { startSpiritFull } = await import("./mind-service-S3SY7HAM.js");
8346
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-II3HOERM.js");
8347
+ const { startSpiritFull } = await import("./mind-service-BF2LUQMH.js");
8151
8348
  await ensureSpiritProject();
8152
8349
  await syncSpiritTemplate();
8153
8350
  const spiritEntry = await findMind("volute");
@@ -8164,7 +8361,7 @@ async function startDaemon(opts) {
8164
8361
  bridgeManager.startBridges(daemonPort).catch((err) => {
8165
8362
  logger_default.warn("failed to start bridges", logger_default.errorData(err));
8166
8363
  });
8167
- import("./cloud-sync-6WJ7WFYD.js").then(
8364
+ import("./cloud-sync-Y7LQPRE3.js").then(
8168
8365
  ({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
8169
8366
  logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
8170
8367
  })
@@ -8172,7 +8369,7 @@ async function startDaemon(opts) {
8172
8369
  logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
8173
8370
  });
8174
8371
  try {
8175
- const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-B67N35A3.js");
8372
+ const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-CODJOUW2.js");
8176
8373
  backfillTemplateHashes();
8177
8374
  notifyVersionUpdate().catch((err) => {
8178
8375
  logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
@@ -8240,6 +8437,7 @@ async function startDaemon(opts) {
8240
8437
  safe("mailPoller.stop", () => mailPoller.stop());
8241
8438
  safe("tokenBudget.stop", () => tokenBudget.stop());
8242
8439
  safe("summarizer.stop", () => summarizer.stop());
8440
+ safe("backupManager.stop", () => backupManager2.stop());
8243
8441
  safe("stopApiKeyRefresh", stopApiKeyRefresh);
8244
8442
  safe("delivery.dispose", () => delivery.dispose());
8245
8443
  await safe("bridgeManager.stopAll", () => bridgeManager.stopAll());