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
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ exec
4
+ } from "./chunk-3ERIL673.js";
5
+ import {
6
+ readGlobalConfig
7
+ } from "./chunk-322KE457.js";
8
+ import {
9
+ logger_default
10
+ } from "./chunk-T2TP6ZC6.js";
11
+ import {
12
+ getDb,
13
+ readAllMinds,
14
+ voluteHome,
15
+ voluteSystemDir
16
+ } from "./chunk-H7IYFRAL.js";
17
+
18
+ // packages/daemon/src/lib/backup/restic.ts
19
+ import { writeFileSync } from "fs";
20
+ import { resolve } from "path";
21
+ var RESTIC_INSTALL_HINT = "Install restic: `brew install restic` (macOS), `apt-get install restic` (Debian/Ubuntu), or see https://restic.net";
22
+ async function resticVersion() {
23
+ try {
24
+ const out = await exec("restic", ["version"]);
25
+ return out.trim();
26
+ } catch (err) {
27
+ if (err.code !== "ENOENT") {
28
+ logger_default.child("backup").warn("restic exists but failed to run", logger_default.errorData(err));
29
+ }
30
+ return null;
31
+ }
32
+ }
33
+ function resticEnv(config) {
34
+ if (!config.repository) throw new Error("Backup repository is not configured");
35
+ if (!config.password) throw new Error("Backup password is not configured");
36
+ return {
37
+ ...process.env,
38
+ ...config.env,
39
+ RESTIC_REPOSITORY: config.repository,
40
+ RESTIC_PASSWORD: config.password
41
+ };
42
+ }
43
+ async function runRestic(args, config, options) {
44
+ if (!await resticVersion()) {
45
+ throw new Error(`restic is not installed. ${RESTIC_INSTALL_HINT}`);
46
+ }
47
+ return exec("restic", args, { cwd: options?.cwd, env: resticEnv(config) });
48
+ }
49
+ var BASE_EXCLUDES = [
50
+ // dependency trees and build output — rehydrated via lockfiles
51
+ "node_modules",
52
+ ".venv",
53
+ "venv",
54
+ "__pycache__",
55
+ "dist",
56
+ // mind runtime state that is regenerated
57
+ ".variants",
58
+ ".worktrees",
59
+ ".mind/tmp",
60
+ ".npm",
61
+ // toolchains and caches minds install into their home. home/.local must NOT
62
+ // be excluded wholesale: .local/hooks and .local/bin hold mind-authored hooks
63
+ // and skill shims that are never regenerated — only its toolchain subdirs go.
64
+ "home/.cache",
65
+ "home/.npm",
66
+ "home/.rustup",
67
+ "home/.cargo",
68
+ "home/.local/share",
69
+ "home/.local/state",
70
+ "home/.local/lib",
71
+ "home/.local/pipx"
72
+ ];
73
+ function systemExcludes() {
74
+ const sys = voluteSystemDir();
75
+ return [
76
+ resolve(sys, "volute.db"),
77
+ resolve(sys, "volute.db-wal"),
78
+ resolve(sys, "volute.db-shm"),
79
+ `${resolve(sys, "extension-data")}/*/data.db`,
80
+ `${resolve(sys, "extension-data")}/*/data.db-wal`,
81
+ `${resolve(sys, "extension-data")}/*/data.db-shm`,
82
+ `${resolve(sys, "state")}/*/logs`,
83
+ resolve(sys, "daemon.log"),
84
+ resolve(sys, "daemon.pid"),
85
+ resolve(sys, "backup-excludes.txt")
86
+ ];
87
+ }
88
+ var SESSION_EXCLUDES = [
89
+ "home/.claude/projects",
90
+ "home/.claude/debug",
91
+ "home/.claude/telemetry",
92
+ "home/.claude/todos",
93
+ "home/.claude/session-env",
94
+ ".mind/codex/sessions",
95
+ ".mind/pi-sessions"
96
+ ];
97
+ function buildExcludePatterns(config) {
98
+ const patterns = [...BASE_EXCLUDES, ...systemExcludes()];
99
+ if (!config.includeSessions) patterns.push(...SESSION_EXCLUDES);
100
+ return patterns;
101
+ }
102
+ function writeExcludeFile(config) {
103
+ const path = resolve(voluteSystemDir(), "backup-excludes.txt");
104
+ writeFileSync(path, `${buildExcludePatterns(config).join("\n")}
105
+ `);
106
+ return path;
107
+ }
108
+
109
+ // packages/daemon/src/lib/backup/backup.ts
110
+ import {
111
+ existsSync,
112
+ mkdirSync,
113
+ readdirSync,
114
+ readFileSync,
115
+ renameSync,
116
+ rmSync,
117
+ writeFileSync as writeFileSync2
118
+ } from "fs";
119
+ import { dirname, resolve as resolve2 } from "path";
120
+ import { sql } from "drizzle-orm";
121
+ var blog = logger_default.child("backup");
122
+ var DEFAULT_KEEP = { daily: 7, weekly: 4, monthly: 12 };
123
+ function stateFilePath() {
124
+ return resolve2(voluteSystemDir(), "backup-state.json");
125
+ }
126
+ function readBackupState() {
127
+ const path = stateFilePath();
128
+ if (!existsSync(path)) return {};
129
+ try {
130
+ return JSON.parse(readFileSync(path, "utf-8"));
131
+ } catch (err) {
132
+ blog.warn("backup-state.json unreadable, treating as empty", logger_default.errorData(err));
133
+ return {};
134
+ }
135
+ }
136
+ function writeBackupState(state) {
137
+ try {
138
+ writeFileSync2(stateFilePath(), `${JSON.stringify(state, null, 2)}
139
+ `);
140
+ } catch (err) {
141
+ blog.error("failed to write backup-state.json", logger_default.errorData(err));
142
+ }
143
+ }
144
+ function stagingDir() {
145
+ return resolve2(voluteSystemDir(), "backup-staging");
146
+ }
147
+ async function stageDatabases() {
148
+ const staging = stagingDir();
149
+ rmSync(staging, { recursive: true, force: true });
150
+ mkdirSync(staging, { recursive: true });
151
+ const staged = [];
152
+ const mainTarget = resolve2(staging, "volute.db");
153
+ const db = await getDb();
154
+ await db.run(sql.raw(`VACUUM INTO '${mainTarget.replaceAll("'", "''")}'`));
155
+ staged.push(mainTarget);
156
+ const extDataDir = resolve2(voluteSystemDir(), "extension-data");
157
+ if (existsSync(extDataDir)) {
158
+ const { default: Database } = await import("libsql");
159
+ for (const id of readdirSync(extDataDir)) {
160
+ const dbPath = resolve2(extDataDir, id, "data.db");
161
+ if (!existsSync(dbPath)) continue;
162
+ const target = resolve2(staging, "extension-data", id, "data.db");
163
+ mkdirSync(dirname(target), { recursive: true });
164
+ const extDb = new Database(dbPath);
165
+ try {
166
+ extDb.exec(`VACUUM INTO '${target.replaceAll("'", "''")}'`);
167
+ staged.push(target);
168
+ } finally {
169
+ extDb.close();
170
+ }
171
+ }
172
+ }
173
+ return staged;
174
+ }
175
+ async function backupRoots() {
176
+ const roots = /* @__PURE__ */ new Set([voluteHome()]);
177
+ const mindsDir = process.env.VOLUTE_MINDS_DIR;
178
+ if (mindsDir && existsSync(mindsDir)) roots.add(resolve2(mindsDir));
179
+ for (const mind of await readAllMinds()) {
180
+ if (!mind.dir || !existsSync(mind.dir)) continue;
181
+ const dir = resolve2(mind.dir);
182
+ if (![...roots].some((root) => dir === root || dir.startsWith(`${root}/`))) {
183
+ roots.add(dir);
184
+ }
185
+ }
186
+ return [...roots];
187
+ }
188
+ var backupInFlight = false;
189
+ async function runBackup() {
190
+ if (backupInFlight) throw new Error("A backup is already running");
191
+ backupInFlight = true;
192
+ const started = Date.now();
193
+ try {
194
+ const config = readGlobalConfig().backup ?? {};
195
+ if (!config.repository || !config.password) {
196
+ throw new Error("Backup repository is not configured. Run: volute backup init");
197
+ }
198
+ blog.info("starting backup", { repository: config.repository });
199
+ await stageDatabases();
200
+ const roots = await backupRoots();
201
+ const excludeFile = writeExcludeFile(config);
202
+ const out = await runRestic(
203
+ [
204
+ "backup",
205
+ ...roots,
206
+ "--exclude-file",
207
+ excludeFile,
208
+ "--exclude-caches",
209
+ "--tag",
210
+ "volute",
211
+ "--json"
212
+ ],
213
+ config
214
+ );
215
+ const summary = parseBackupSummary(out, Date.now() - started);
216
+ const keep = { ...DEFAULT_KEEP, ...config.keep };
217
+ let pruneError;
218
+ try {
219
+ await runRestic(
220
+ [
221
+ "forget",
222
+ "--tag",
223
+ "volute",
224
+ "--keep-daily",
225
+ String(keep.daily),
226
+ "--keep-weekly",
227
+ String(keep.weekly),
228
+ "--keep-monthly",
229
+ String(keep.monthly),
230
+ "--prune"
231
+ ],
232
+ config
233
+ );
234
+ } catch (err) {
235
+ pruneError = err instanceof Error ? err.message : String(err);
236
+ blog.warn("retention prune failed (snapshot is intact)", logger_default.errorData(err));
237
+ }
238
+ writeBackupState({
239
+ lastRun: (/* @__PURE__ */ new Date()).toISOString(),
240
+ lastSnapshotId: summary.snapshotId,
241
+ lastDurationMs: summary.durationMs,
242
+ ...pruneError ? { pruneError } : {}
243
+ });
244
+ blog.info("backup complete", { snapshot: summary.snapshotId, ms: summary.durationMs });
245
+ return summary;
246
+ } catch (err) {
247
+ writeBackupState({
248
+ ...readBackupState(),
249
+ lastAttempt: (/* @__PURE__ */ new Date()).toISOString(),
250
+ lastError: err instanceof Error ? err.message : String(err)
251
+ });
252
+ throw err;
253
+ } finally {
254
+ backupInFlight = false;
255
+ }
256
+ }
257
+ function parseBackupSummary(output, durationMs) {
258
+ for (const line of output.trim().split("\n").reverse()) {
259
+ let msg;
260
+ try {
261
+ msg = JSON.parse(line);
262
+ } catch {
263
+ continue;
264
+ }
265
+ if (msg.message_type !== "summary") continue;
266
+ if (!msg.snapshot_id) throw new Error("restic summary is missing snapshot_id");
267
+ return {
268
+ snapshotId: msg.snapshot_id,
269
+ filesNew: msg.files_new ?? 0,
270
+ filesChanged: msg.files_changed ?? 0,
271
+ dataAdded: msg.data_added ?? 0,
272
+ totalFilesProcessed: msg.total_files_processed ?? 0,
273
+ durationMs
274
+ };
275
+ }
276
+ throw new Error("restic backup produced no summary");
277
+ }
278
+ async function listSnapshots() {
279
+ const config = readGlobalConfig().backup ?? {};
280
+ const out = await runRestic(["snapshots", "--tag", "volute", "--json"], config);
281
+ const parsed = JSON.parse(out || "[]");
282
+ return parsed.map((s) => ({
283
+ id: s.id,
284
+ short_id: s.short_id,
285
+ time: s.time,
286
+ hostname: s.hostname,
287
+ paths: s.paths
288
+ }));
289
+ }
290
+ async function initRepo(config) {
291
+ try {
292
+ await runRestic(["init"], config);
293
+ } catch (err) {
294
+ const stderr = err.stderr ?? "";
295
+ const msg = err instanceof Error ? err.message : String(err);
296
+ if (/already exists|already initialized/i.test(stderr + msg)) return;
297
+ throw err;
298
+ }
299
+ }
300
+ function restoreStagedDb(stagedPath, livePath) {
301
+ if (!existsSync(stagedPath)) return false;
302
+ mkdirSync(dirname(livePath), { recursive: true });
303
+ rmSync(livePath, { force: true });
304
+ rmSync(`${livePath}-wal`, { force: true });
305
+ rmSync(`${livePath}-shm`, { force: true });
306
+ renameSync(stagedPath, livePath);
307
+ return true;
308
+ }
309
+
310
+ export {
311
+ RESTIC_INSTALL_HINT,
312
+ resticVersion,
313
+ readBackupState,
314
+ runBackup,
315
+ listSnapshots,
316
+ initRepo,
317
+ restoreStagedDb
318
+ };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  readGlobalConfig
4
- } from "./chunk-PNV24SEP.js";
4
+ } from "./chunk-322KE457.js";
5
5
  import {
6
6
  logger_default
7
7
  } from "./chunk-T2TP6ZC6.js";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  readGlobalConfig,
4
4
  writeGlobalConfig
5
- } from "./chunk-PNV24SEP.js";
5
+ } from "./chunk-322KE457.js";
6
6
  import {
7
7
  logger_default
8
8
  } from "./chunk-T2TP6ZC6.js";
@@ -4,13 +4,13 @@ import {
4
4
  } from "./chunk-5GT7URXJ.js";
5
5
  import {
6
6
  spiritDir
7
- } from "./chunk-7FT24JBQ.js";
7
+ } from "./chunk-5AUKCNPE.js";
8
8
  import {
9
9
  hashSkillDir,
10
10
  importSkillFromDir,
11
11
  removeSharedSkill,
12
12
  sharedSkillsDir
13
- } from "./chunk-I2Y2YHIO.js";
13
+ } from "./chunk-Y6OPHHGM.js";
14
14
  import {
15
15
  addMessage,
16
16
  createChannel,
@@ -20,7 +20,7 @@ import {
20
20
  getParticipants,
21
21
  joinChannel,
22
22
  publish as publish2
23
- } from "./chunk-FZMEVCWC.js";
23
+ } from "./chunk-JSRMEY52.js";
24
24
  import {
25
25
  readVoluteConfig,
26
26
  writeVoluteConfig
@@ -28,7 +28,7 @@ import {
28
28
  import {
29
29
  isSandboxEnabled,
30
30
  wrapForSandbox
31
- } from "./chunk-3GYVW2X3.js";
31
+ } from "./chunk-OXXTPILE.js";
32
32
  import {
33
33
  markIdle,
34
34
  onMindEvent
@@ -39,14 +39,14 @@ import {
39
39
  resolveDeliveryMode,
40
40
  resolveRoute,
41
41
  setRoutesChangeListener
42
- } from "./chunk-5AHYQHNT.js";
42
+ } from "./chunk-ZIR7HSAX.js";
43
43
  import {
44
44
  clearMind,
45
45
  getPrompt,
46
46
  linkInboundToActiveTurn,
47
47
  publish as publish3,
48
48
  summarizeOrphanedTurns
49
- } from "./chunk-NF75HPNR.js";
49
+ } from "./chunk-V4GYPD5L.js";
50
50
  import {
51
51
  chownMindDir,
52
52
  exec,
@@ -58,11 +58,11 @@ import {
58
58
  aiCompleteUtility,
59
59
  getAiConfig,
60
60
  resolveApiKey
61
- } from "./chunk-J47TIYHA.js";
61
+ } from "./chunk-TSK5PI4L.js";
62
62
  import {
63
63
  readGlobalConfig,
64
64
  writeGlobalConfig
65
- } from "./chunk-PNV24SEP.js";
65
+ } from "./chunk-322KE457.js";
66
66
  import {
67
67
  getOrCreateMindUser,
68
68
  getOrCreateSystemUser,
@@ -385,7 +385,8 @@ async function clearDeliveredNotices(mind, session, uptoId) {
385
385
  var NOTICE_HEADER = "[Notices] While you were unavailable, one or more turns failed. You're back now:";
386
386
  function formatNotices(notices) {
387
387
  if (notices.length === 0) return null;
388
- const failures = notices.filter((n) => n.kind !== "extension");
388
+ const failures = notices.filter((n) => n.kind !== "extension" && n.kind !== "budget");
389
+ const budgets = notices.filter((n) => n.kind === "budget");
389
390
  const extensions = notices.filter((n) => n.kind === "extension");
390
391
  const blocks = [];
391
392
  if (failures.length > 0) {
@@ -407,6 +408,11 @@ function formatNotices(notices) {
407
408
  return `- ${g.count} ${plural} failed (${span}): ${g.detail}`;
408
409
  });
409
410
  blocks.push(`${NOTICE_HEADER}
411
+ ${lines.join("\n")}`);
412
+ }
413
+ if (budgets.length > 0) {
414
+ const lines = budgets.map((n) => `- ${localHM(n.created_at)} ${n.detail}`);
415
+ blocks.push(`[Budget]
410
416
  ${lines.join("\n")}`);
411
417
  }
412
418
  if (extensions.length > 0) {
@@ -887,6 +893,8 @@ var MindManager = class {
887
893
  const parts = [];
888
894
  if (context.type === "merge" || context.type === "merged") {
889
895
  parts.push(await getPrompt("merge_message", { name: String(context.name ?? "") }));
896
+ } else if (context.type === "split") {
897
+ parts.push(await getPrompt("split_message", { name, parent: String(context.parent ?? "") }));
890
898
  } else if (context.type === "sprouted") {
891
899
  parts.push(await getPrompt("sprout_message"));
892
900
  } else if (context.type === "upgraded") {
@@ -931,7 +939,7 @@ var MindManager = class {
931
939
  if (this.shuttingDown || this.stopping.has(name)) return;
932
940
  mlog.error(`mind ${name} exited with code ${code}`);
933
941
  try {
934
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-HINI6Y46.js");
942
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
935
943
  const sleepMgr = getSleepManagerIfReady2();
936
944
  const sleepState = sleepMgr?.getState(name);
937
945
  if (sleepState?.sleeping) {
@@ -961,7 +969,7 @@ var MindManager = class {
961
969
  (err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
962
970
  );
963
971
  try {
964
- const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-TEVOHX5W.js");
972
+ const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-R5STGLXB.js");
965
973
  getDeliveryManager2().clearMindSessions(name);
966
974
  } catch (err) {
967
975
  if (!(err instanceof Error && err.message.includes("not initialized"))) {
@@ -1027,7 +1035,7 @@ var MindManager = class {
1027
1035
  mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
1028
1036
  }
1029
1037
  try {
1030
- const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-TEVOHX5W.js");
1038
+ const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-R5STGLXB.js");
1031
1039
  getDeliveryManager2().clearMindSessions(name);
1032
1040
  } catch (err) {
1033
1041
  if (!(err instanceof Error && err.message.includes("not initialized"))) {
@@ -1035,7 +1043,7 @@ var MindManager = class {
1035
1043
  }
1036
1044
  }
1037
1045
  try {
1038
- const { clearEchoTextCache } = await import("./echo-text-T7ZA5EYD.js");
1046
+ const { clearEchoTextCache } = await import("./echo-text-NQXYYYCU.js");
1039
1047
  clearEchoTextCache(name);
1040
1048
  } catch (err) {
1041
1049
  mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
@@ -2763,7 +2771,7 @@ async function wakeMind(name) {
2763
2771
  async function startSpiritFull(name) {
2764
2772
  const entry = await findMind(name);
2765
2773
  if (entry?.dir) {
2766
- const { registerMindDir } = await import("./delivery-router-GODSIS62.js");
2774
+ const { registerMindDir } = await import("./delivery-router-6DE3BPHO.js");
2767
2775
  registerMindDir(name, entry.dir);
2768
2776
  }
2769
2777
  await getMindManager().startMind(name);
@@ -2786,7 +2794,7 @@ async function stopSpiritFull(name) {
2786
2794
  }
2787
2795
  async function ensureCreatorDM(mindName, creatorUsername) {
2788
2796
  const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-FRZJVQCS.js");
2789
- const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-WAJYZKOC.js");
2797
+ const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-G4LYMHAA.js");
2790
2798
  const mindUser = await getOrCreateMindUser2(mindName);
2791
2799
  const creatorUser = await getUserByUsername2(creatorUsername);
2792
2800
  if (!creatorUser) {
@@ -2838,6 +2846,22 @@ function formatCurrentDate() {
2838
2846
  day: "numeric"
2839
2847
  });
2840
2848
  }
2849
+ function formatWakeTime(wakeAt) {
2850
+ if (wakeAt) {
2851
+ const date = new Date(wakeAt);
2852
+ if (!Number.isNaN(date.getTime())) {
2853
+ const time = date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
2854
+ const day = date.toLocaleDateString("en-US", {
2855
+ weekday: "long",
2856
+ month: "long",
2857
+ day: "numeric"
2858
+ });
2859
+ return `at ${time} on ${day}`;
2860
+ }
2861
+ return `at ${wakeAt}`;
2862
+ }
2863
+ return "when something needs you \u2014 a message, a mention, or someone waking you";
2864
+ }
2841
2865
  function formatDuration(from, to) {
2842
2866
  const ms = to.getTime() - from.getTime();
2843
2867
  const hours = Math.floor(ms / 36e5);
@@ -2985,7 +3009,8 @@ var SleepManager = class {
2985
3009
  const entry = await findMind(name);
2986
3010
  if (!entry) return;
2987
3011
  const sleepConfig = this.getSleepConfig(name);
2988
- const wakeTime = opts?.voluntaryWakeAt ?? this.getNextWakeTime(sleepConfig) ?? "scheduled time";
3012
+ const wakeAt = opts?.voluntaryWakeAt ?? this.getNextWakeTime(sleepConfig);
3013
+ const wakeTime = formatWakeTime(wakeAt);
2989
3014
  const preSleepMsg = await getPrompt("pre_sleep", { wakeTime });
2990
3015
  let conversationId;
2991
3016
  try {
@@ -3161,7 +3186,7 @@ var SleepManager = class {
3161
3186
  const db = await getDb();
3162
3187
  const rows = await db.select().from(deliveryQueue).where(and2(eq3(deliveryQueue.mind, name), eq3(deliveryQueue.status, "sleep-queued"))).all();
3163
3188
  if (rows.length === 0) return 0;
3164
- const { deliverMessage: deliverMessage2 } = await import("./message-delivery-2EA5LLMD.js");
3189
+ const { deliverMessage: deliverMessage2 } = await import("./message-delivery-IUAXRPFE.js");
3165
3190
  const delivered = [];
3166
3191
  for (const row of rows) {
3167
3192
  try {
@@ -4308,16 +4333,17 @@ var DeliveryManager = class {
4308
4333
  const channel = payload.channel ?? "unknown";
4309
4334
  const notification = [
4310
4335
  `[New channel: ${channel}]`,
4336
+ `Someone new is reaching out \u2014 you don't have a route for this channel yet.`,
4311
4337
  `Sender: ${payload.sender ?? "unknown"}`,
4312
4338
  payload.platform ? `Platform: ${payload.platform}` : null,
4313
4339
  payload.participantCount ? `Participants: ${payload.participantCount}` : null,
4314
4340
  "",
4315
4341
  `Preview: ${preview}`,
4316
4342
  "",
4317
- `To accept this channel, add a routing rule for "${channel}" to your routes.json.`,
4318
- `Messages are being held until a route is configured.`
4343
+ `To start hearing this channel, add a routing rule for "${channel}" to .config/routes.json.`,
4344
+ `Messages are held safely until you do \u2014 nothing is lost. If you'd rather not engage, just leave it unrouted.`
4319
4345
  ].filter((line) => line !== null).join("\n");
4320
- const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-4DQZOEZI.js");
4346
+ const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-E65T2HSW.js");
4321
4347
  await sendSystemMessage2(mindName, notification);
4322
4348
  }
4323
4349
  /**
@@ -4758,7 +4784,7 @@ async function generateSystemReply(conversationId, mindName, message) {
4758
4784
  if (config.sleep.schedule?.wake) contextParts.push(`Wake cron: ${config.sleep.schedule.wake}`);
4759
4785
  }
4760
4786
  try {
4761
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-HINI6Y46.js");
4787
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
4762
4788
  const sm = getSleepManagerIfReady2();
4763
4789
  if (sm) {
4764
4790
  const state2 = sm.getState(mindName);