volute 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +7 -7
  2. package/dist/{activity-events-OMXKXD5N.js → activity-events-3WHHCOBB.js} +3 -4
  3. package/dist/api.d.ts +4294 -0
  4. package/dist/{archive-ZCFOSTKB.js → archive-4ZQYK5MN.js} +4 -2
  5. package/dist/auth-HM2RSPY7.js +37 -0
  6. package/dist/{channel-PUQKGSQM.js → channel-BOOMFULW.js} +2 -2
  7. package/dist/{chunk-UU7A7KLB.js → chunk-A4S7H6G6.js} +5 -7
  8. package/dist/chunk-AKPFNL7L.js +148 -0
  9. package/dist/{chunk-EBGCNDMM.js → chunk-B2CPS4QU.js} +128 -114
  10. package/dist/chunk-G5KRTU2F.js +76 -0
  11. package/dist/{chunk-FCDU5BFX.js → chunk-HFCBO2GL.js} +2 -2
  12. package/dist/{chunk-GZ7DW4YL.js → chunk-HGCDWKSP.js} +2 -2
  13. package/dist/{chunk-7UFKREVW.js → chunk-JNFRY2WU.js} +2 -2
  14. package/dist/{chunk-DYZGP3EW.js → chunk-JTDFJWI2.js} +2 -1
  15. package/dist/{chunk-WC6ZHVRL.js → chunk-KFI7TQJ6.js} +2 -2
  16. package/dist/{chunk-AW7P4EVV.js → chunk-KTJGZ7M7.js} +55 -7
  17. package/dist/{chunk-OGXOMR65.js → chunk-NWPT4ASZ.js} +1 -1
  18. package/dist/{chunk-SCUDS4US.js → chunk-ON3FF5JA.js} +1 -1
  19. package/dist/chunk-OSFGKF2T.js +2651 -0
  20. package/dist/{chunk-TIWH32HP.js → chunk-PHHKNGA3.js} +3 -3
  21. package/dist/{chunk-VDWCHYTS.js → chunk-PHU4DEAJ.js} +1 -1
  22. package/dist/{chunk-7NO7EV5Z.js → chunk-QIXPN3OO.js} +2 -2
  23. package/dist/{chunk-O6ASDHFO.js → chunk-RK627D57.js} +40 -63
  24. package/dist/{chunk-NSE7VJQA.js → chunk-SGPEZ32F.js} +29 -1
  25. package/dist/{chunk-IKMY5X76.js → chunk-TFS25FIM.js} +12 -9
  26. package/dist/{chunk-PUVXOZ6T.js → chunk-VNVCRVYI.js} +118 -69
  27. package/dist/{chunk-32VR2EOH.js → chunk-VT5QODNE.js} +2 -2
  28. package/dist/{chunk-RHEGSQFJ.js → chunk-WSLPZF72.js} +1 -1
  29. package/dist/chunk-XLC342FO.js +29 -0
  30. package/dist/cli.js +57 -119
  31. package/dist/cloud-sync-C6WRYRVR.js +96 -0
  32. package/dist/{connector-JBVNZ7VK.js → connector-PYT5UOTZ.js} +6 -6
  33. package/dist/connectors/discord.js +2 -2
  34. package/dist/connectors/slack.js +2 -2
  35. package/dist/connectors/telegram.js +2 -2
  36. package/dist/{create-HP4OVVHF.js → create-WIDA3M4C.js} +1 -1
  37. package/dist/{daemon-client-ITWUCNFO.js → daemon-client-ZHCDL4RS.js} +2 -2
  38. package/dist/{daemon-restart-KPSWNYTH.js → daemon-restart-TPQ2XBRZ.js} +6 -6
  39. package/dist/daemon.js +2250 -1985
  40. package/dist/{delete-BSU7K3RY.js → delete-LOIANQGD.js} +1 -1
  41. package/dist/down-WSUASL5E.js +14 -0
  42. package/dist/{env-A3LMO777.js → env-4PHIHTF4.js} +2 -2
  43. package/dist/{export-6QBUOQGC.js → export-XD6PJBQP.js} +19 -8
  44. package/dist/{file-C57SK5DK.js → file-X4L5TTOL.js} +2 -2
  45. package/dist/{history-WNK3DFUM.js → history-HTEKRNID.js} +2 -2
  46. package/dist/{import-XEC34Y4Z.js → import-EAXTHHXL.js} +4 -3
  47. package/dist/{log-PPPZDVEF.js → log-SRO5Q6AD.js} +2 -2
  48. package/dist/{login-HNH3EUQV.js → login-UO6AOVEA.js} +4 -4
  49. package/dist/{logout-I5CB5UZS.js → logout-UKD5LA37.js} +2 -2
  50. package/dist/{logs-SF2IMJN4.js → logs-HNTNNBDW.js} +2 -2
  51. package/dist/{merge-33C237A4.js → merge-B6SYTGI7.js} +2 -2
  52. package/dist/message-delivery-WUS4K4ZC.js +21 -0
  53. package/dist/{mind-Z7CKD6DG.js → mind-BTXR5B3C.js} +35 -11
  54. package/dist/{mind-activity-tracker-624QLQLC.js → mind-activity-tracker-PGC3DBJ7.js} +4 -5
  55. package/dist/{mind-manager-3DMYKZPB.js → mind-manager-P5OBDUKI.js} +5 -6
  56. package/dist/mind-sleep-FWRBIFBS.js +41 -0
  57. package/dist/mind-wake-LJK2YU5X.js +36 -0
  58. package/dist/{package-4NHAVUUI.js → package-A7PEYJI2.js} +10 -1
  59. package/dist/{pages-4DGQT7ZA.js → pages-YSTRWJR4.js} +6 -6
  60. package/dist/{publish-TAJUET4I.js → publish-BZNHKUUK.js} +6 -6
  61. package/dist/{pull-XAEWQJ47.js → pull-GRQAXM2E.js} +2 -2
  62. package/dist/{register-VSPCMHKX.js → register-U2UO6TC4.js} +5 -5
  63. package/dist/registry-D2BSQ2X5.js +42 -0
  64. package/dist/{restart-IQKMCK5M.js → restart-CIDAKGG2.js} +3 -6
  65. package/dist/{schedule-FFZG23IW.js → schedule-NLR3LZLY.js} +2 -2
  66. package/dist/{seed-J43YDKXG.js → seed-3H2MRREW.js} +2 -2
  67. package/dist/{send-KVIZIGCE.js → send-RP2TA7SG.js} +132 -36
  68. package/dist/{service-LUR7WDO7.js → service-7BFXDI6J.js} +31 -13
  69. package/dist/{setup-52YRV7VP.js → setup-SSIIXQMI.js} +9 -34
  70. package/dist/{shared-KO35ZM44.js → shared-2OGT3NSL.js} +4 -4
  71. package/dist/{skill-BCVNI6TV.js → skill-Q2Y6PQ3L.js} +2 -2
  72. package/dist/skills/orientation/SKILL.md +2 -2
  73. package/dist/skills/volute-mind/SKILL.md +5 -5
  74. package/dist/sleep-manager-3RWUX2ZR.js +27 -0
  75. package/dist/{sprout-QN7Y4VVO.js → sprout-UKCYBGHK.js} +34 -30
  76. package/dist/{start-I5JYB65M.js → start-JR6CUUWF.js} +3 -6
  77. package/dist/{status-D7E5HHBV.js → status-5XDGYHKP.js} +2 -2
  78. package/dist/{status-4ESFLGH4.js → status-H2MKDN6L.js} +5 -5
  79. package/dist/{status-FU2PFVVF.js → status-LV34BG6G.js} +3 -3
  80. package/dist/{stop-NBVKEFQQ.js → stop-VKPGK25U.js} +2 -5
  81. package/dist/template-hash-BIMA4ILT.js +8 -0
  82. package/dist/{up-FS7CKM6V.js → up-JKGC7PPF.js} +5 -5
  83. package/dist/{update-FJIHDJKM.js → update-ELC6MEUT.js} +5 -5
  84. package/dist/{update-check-MWE5AH4U.js → update-check-F5Z3ALXX.js} +2 -2
  85. package/dist/{upgrade-AIT24B5I.js → upgrade-GXW2EQY3.js} +12 -3
  86. package/dist/{variant-63ZWO2W7.js → variant-A4I7PHXS.js} +16 -24
  87. package/dist/version-notify-5FGUAVSF.js +181 -0
  88. package/dist/web-assets/assets/index-DWBxl4LO.js +69 -0
  89. package/dist/web-assets/assets/index-ZqMd1mx1.css +1 -0
  90. package/dist/web-assets/index.html +2 -2
  91. package/package.json +10 -1
  92. package/templates/_base/.init/.config/prompts.json +1 -0
  93. package/templates/_base/home/.config/config.json.tmpl +4 -1
  94. package/templates/_base/src/lib/logger.ts +68 -23
  95. package/templates/_base/src/lib/startup.ts +12 -3
  96. package/templates/claude/src/agent.ts +150 -29
  97. package/templates/claude/src/lib/hooks/pre-compact.ts +18 -4
  98. package/templates/claude/src/lib/message-channel.ts +6 -0
  99. package/templates/claude/src/lib/stream-consumer.ts +7 -0
  100. package/templates/claude/src/server.ts +3 -1
  101. package/templates/pi/home/.config/config.json.tmpl +4 -1
  102. package/templates/pi/src/agent.ts +87 -0
  103. package/templates/pi/src/lib/event-handler.ts +13 -1
  104. package/templates/pi/src/server.ts +3 -1
  105. package/dist/chunk-5XNT2472.js +0 -36
  106. package/dist/chunk-FGSYHIS3.js +0 -891
  107. package/dist/chunk-UJ6GHNR7.js +0 -675
  108. package/dist/db-C2CJ46ZU.js +0 -10
  109. package/dist/delivery-manager-CSG7LXA4.js +0 -16
  110. package/dist/down-ZY35KMHR.js +0 -14
  111. package/dist/schema-GFH6RV3W.js +0 -26
  112. package/dist/variants-JAGWGBXG.js +0 -26
  113. package/dist/web-assets/assets/index-CUZTZzaW.js +0 -64
  114. package/dist/web-assets/assets/index-adVuCkqy.css +0 -1
@@ -4,14 +4,14 @@ import {
4
4
  } from "./chunk-YUIHSKR6.js";
5
5
  import {
6
6
  gitExec
7
- } from "./chunk-DYZGP3EW.js";
7
+ } from "./chunk-JTDFJWI2.js";
8
8
  import {
9
9
  isIsolationEnabled,
10
10
  mindUserName
11
- } from "./chunk-OGXOMR65.js";
11
+ } from "./chunk-NWPT4ASZ.js";
12
12
  import {
13
13
  voluteHome
14
- } from "./chunk-EBGCNDMM.js";
14
+ } from "./chunk-B2CPS4QU.js";
15
15
 
16
16
  // src/lib/shared.ts
17
17
  import { execFileSync } from "child_process";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  stateDir,
4
4
  voluteHome
5
- } from "./chunk-EBGCNDMM.js";
5
+ } from "./chunk-B2CPS4QU.js";
6
6
 
7
7
  // src/lib/env.ts
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -5,10 +5,10 @@ import {
5
5
  pollHealthDown,
6
6
  readDaemonConfig,
7
7
  stopService
8
- } from "./chunk-32VR2EOH.js";
8
+ } from "./chunk-VT5QODNE.js";
9
9
  import {
10
10
  voluteHome
11
- } from "./chunk-EBGCNDMM.js";
11
+ } from "./chunk-B2CPS4QU.js";
12
12
 
13
13
  // src/commands/down.ts
14
14
  import { existsSync, readFileSync, unlinkSync } from "fs";
@@ -1,9 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ readVoluteConfig,
4
+ writeVoluteConfig
5
+ } from "./chunk-XLC342FO.js";
2
6
  import {
3
7
  mindEnvPath,
4
8
  readEnv,
5
9
  writeEnv
6
- } from "./chunk-VDWCHYTS.js";
10
+ } from "./chunk-PHU4DEAJ.js";
7
11
  import {
8
12
  parseArgs
9
13
  } from "./chunk-D424ZQGI.js";
@@ -11,43 +15,18 @@ import {
11
15
  // src/commands/import.ts
12
16
  import {
13
17
  closeSync,
14
- existsSync as existsSync2,
15
- mkdirSync as mkdirSync2,
18
+ existsSync,
19
+ mkdirSync,
16
20
  openSync,
17
21
  readdirSync,
18
- readFileSync as readFileSync2,
22
+ readFileSync,
19
23
  readSync,
20
24
  rmSync,
21
25
  statSync,
22
- writeFileSync as writeFileSync2
26
+ writeFileSync
23
27
  } from "fs";
24
28
  import { homedir, tmpdir } from "os";
25
- import { basename, resolve as resolve2 } from "path";
26
-
27
- // src/lib/volute-config.ts
28
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
29
- import { dirname, resolve } from "path";
30
- function readJson(path) {
31
- if (!existsSync(path)) return null;
32
- try {
33
- return JSON.parse(readFileSync(path, "utf-8"));
34
- } catch (err) {
35
- console.error(`[volute-config] failed to parse ${path}: ${err}`);
36
- return null;
37
- }
38
- }
39
- function readVoluteConfig(mindDir) {
40
- const path = resolve(mindDir, "home/.config/volute.json");
41
- return readJson(path);
42
- }
43
- function writeVoluteConfig(mindDir, config) {
44
- const path = resolve(mindDir, "home/.config/volute.json");
45
- mkdirSync(dirname(path), { recursive: true });
46
- writeFileSync(path, `${JSON.stringify(config, null, 2)}
47
- `);
48
- }
49
-
50
- // src/commands/import.ts
29
+ import { basename, resolve } from "path";
51
30
  async function run(args) {
52
31
  const { positional, flags } = parseArgs(args, {
53
32
  name: { type: "string" },
@@ -56,11 +35,11 @@ async function run(args) {
56
35
  });
57
36
  const inputPath = positional[0];
58
37
  if (inputPath && (inputPath.endsWith(".volute") || isZipFile(inputPath))) {
59
- await importArchive(resolve2(inputPath), flags.name);
38
+ await importArchive(resolve(inputPath), flags.name);
60
39
  return;
61
40
  }
62
41
  const wsDir = resolveWorkspace(inputPath);
63
- const { daemonFetch } = await import("./daemon-client-ITWUCNFO.js");
42
+ const { daemonFetch } = await import("./daemon-client-ZHCDL4RS.js");
64
43
  const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
65
44
  const client = getClient();
66
45
  const res = await daemonFetch(urlOf(client.api.minds.import.$url()), {
@@ -84,8 +63,8 @@ ${data.message ?? `Imported mind: ${data.name} (port ${data.port})`}`);
84
63
  volute mind start ${data.name}`);
85
64
  }
86
65
  function isZipFile(path) {
87
- const resolved = resolve2(path);
88
- if (!existsSync2(resolved)) return false;
66
+ const resolved = resolve(path);
67
+ if (!existsSync(resolved)) return false;
89
68
  const fd = openSync(resolved, "r");
90
69
  try {
91
70
  const buf = Buffer.alloc(4);
@@ -96,13 +75,13 @@ function isZipFile(path) {
96
75
  }
97
76
  }
98
77
  async function importArchive(archivePath, nameOverride) {
99
- if (!existsSync2(archivePath)) {
78
+ if (!existsSync(archivePath)) {
100
79
  console.error(`File not found: ${archivePath}`);
101
80
  process.exit(1);
102
81
  }
103
- const { extractArchive } = await import("./archive-ZCFOSTKB.js");
104
- const tempDir = resolve2(tmpdir(), `volute-import-${Date.now()}`);
105
- mkdirSync2(tempDir, { recursive: true });
82
+ const { extractArchive } = await import("./archive-4ZQYK5MN.js");
83
+ const tempDir = resolve(tmpdir(), `volute-import-${Date.now()}`);
84
+ mkdirSync(tempDir, { recursive: true });
106
85
  let extracted;
107
86
  try {
108
87
  extracted = extractArchive(archivePath, tempDir);
@@ -112,7 +91,7 @@ async function importArchive(archivePath, nameOverride) {
112
91
  process.exit(1);
113
92
  }
114
93
  try {
115
- const { daemonFetch } = await import("./daemon-client-ITWUCNFO.js");
94
+ const { daemonFetch } = await import("./daemon-client-ZHCDL4RS.js");
116
95
  const { getClient, urlOf } = await import("./api-client-YPKOZP2O.js");
117
96
  const client = getClient();
118
97
  const res = await daemonFetch(urlOf(client.api.minds.import.$url()), {
@@ -140,20 +119,20 @@ ${data.message ?? `Imported mind: ${data.name} (port ${data.port})`}`);
140
119
  }
141
120
  function resolveWorkspace(explicitPath) {
142
121
  if (explicitPath) {
143
- const wsDir = resolve2(explicitPath);
144
- if (!existsSync2(resolve2(wsDir, "SOUL.md")) || !existsSync2(resolve2(wsDir, "IDENTITY.md"))) {
122
+ const wsDir = resolve(explicitPath);
123
+ if (!existsSync(resolve(wsDir, "SOUL.md")) || !existsSync(resolve(wsDir, "IDENTITY.md"))) {
145
124
  console.error("Not a valid OpenClaw workspace: missing SOUL.md or IDENTITY.md");
146
125
  process.exit(1);
147
126
  }
148
127
  return wsDir;
149
128
  }
150
129
  const cwd = process.cwd();
151
- if (existsSync2(resolve2(cwd, "SOUL.md")) && existsSync2(resolve2(cwd, "IDENTITY.md"))) {
130
+ if (existsSync(resolve(cwd, "SOUL.md")) && existsSync(resolve(cwd, "IDENTITY.md"))) {
152
131
  console.log(`Using workspace: ${cwd}`);
153
132
  return cwd;
154
133
  }
155
- const openclawWs = resolve2(homedir(), ".openclaw/workspace");
156
- if (existsSync2(resolve2(openclawWs, "SOUL.md")) && existsSync2(resolve2(openclawWs, "IDENTITY.md"))) {
134
+ const openclawWs = resolve(homedir(), ".openclaw/workspace");
135
+ if (existsSync(resolve(openclawWs, "SOUL.md")) && existsSync(resolve(openclawWs, "IDENTITY.md"))) {
157
136
  console.log(`Using workspace: ${openclawWs}`);
158
137
  return openclawWs;
159
138
  }
@@ -163,16 +142,16 @@ function resolveWorkspace(explicitPath) {
163
142
  process.exit(1);
164
143
  }
165
144
  function findOpenClawSession(workspaceDir) {
166
- const ocAgentsDir = resolve2(homedir(), ".openclaw/agents");
167
- if (!existsSync2(ocAgentsDir)) return void 0;
145
+ const ocAgentsDir = resolve(homedir(), ".openclaw/agents");
146
+ if (!existsSync(ocAgentsDir)) return void 0;
168
147
  const matches = [];
169
148
  try {
170
149
  for (const entry of readdirSync(ocAgentsDir)) {
171
- const sessionsDir = resolve2(ocAgentsDir, entry, "sessions");
172
- if (!existsSync2(sessionsDir)) continue;
150
+ const sessionsDir = resolve(ocAgentsDir, entry, "sessions");
151
+ if (!existsSync(sessionsDir)) continue;
173
152
  for (const file of readdirSync(sessionsDir)) {
174
153
  if (!file.endsWith(".jsonl")) continue;
175
- const fullPath = resolve2(sessionsDir, file);
154
+ const fullPath = resolve(sessionsDir, file);
176
155
  if (sessionMatchesWorkspace(fullPath, workspaceDir)) {
177
156
  matches.push({ path: fullPath, mtime: statSync(fullPath).mtimeMs });
178
157
  }
@@ -189,19 +168,19 @@ function findOpenClawSession(workspaceDir) {
189
168
  }
190
169
  function sessionMatchesWorkspace(sessionPath, workspaceDir) {
191
170
  try {
192
- const fd = readFileSync2(sessionPath, "utf-8");
171
+ const fd = readFileSync(sessionPath, "utf-8");
193
172
  const firstLine = fd.slice(0, fd.indexOf("\n"));
194
173
  const header = JSON.parse(firstLine);
195
- return header.type === "session" && resolve2(header.cwd) === resolve2(workspaceDir);
174
+ return header.type === "session" && resolve(header.cwd) === resolve(workspaceDir);
196
175
  } catch {
197
176
  return false;
198
177
  }
199
178
  }
200
179
  function importPiSession(sessionFile, mindDirPath) {
201
- const homeDir = resolve2(mindDirPath, "home");
202
- const piSessionDir = resolve2(mindDirPath, ".mind/pi-sessions/main");
203
- mkdirSync2(piSessionDir, { recursive: true });
204
- const content = readFileSync2(sessionFile, "utf-8");
180
+ const homeDir = resolve(mindDirPath, "home");
181
+ const piSessionDir = resolve(mindDirPath, ".mind/pi-sessions/main");
182
+ mkdirSync(piSessionDir, { recursive: true });
183
+ const content = readFileSync(sessionFile, "utf-8");
205
184
  const lines = content.trim().split("\n");
206
185
  try {
207
186
  const header = JSON.parse(lines[0]);
@@ -212,17 +191,17 @@ function importPiSession(sessionFile, mindDirPath) {
212
191
  } catch {
213
192
  }
214
193
  const filename = basename(sessionFile);
215
- const destPath = resolve2(piSessionDir, filename);
216
- writeFileSync2(destPath, `${lines.join("\n")}
194
+ const destPath = resolve(piSessionDir, filename);
195
+ writeFileSync(destPath, `${lines.join("\n")}
217
196
  `);
218
197
  console.log(`Imported session (${lines.length} entries)`);
219
198
  }
220
199
  function importOpenClawConnectors(name, mindDirPath) {
221
- const configPath = resolve2(homedir(), ".openclaw/openclaw.json");
222
- if (!existsSync2(configPath)) return;
200
+ const configPath = resolve(homedir(), ".openclaw/openclaw.json");
201
+ if (!existsSync(configPath)) return;
223
202
  let config;
224
203
  try {
225
- config = JSON.parse(readFileSync2(configPath, "utf-8"));
204
+ config = JSON.parse(readFileSync(configPath, "utf-8"));
226
205
  } catch (err) {
227
206
  console.warn("Warning: failed to parse openclaw.json:", err);
228
207
  return;
@@ -266,8 +245,6 @@ function parseNameFromIdentity(identity) {
266
245
  }
267
246
 
268
247
  export {
269
- readVoluteConfig,
270
- writeVoluteConfig,
271
248
  run,
272
249
  findOpenClawSession,
273
250
  sessionMatchesWorkspace,
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ voluteHome
4
+ } from "./chunk-B2CPS4QU.js";
2
5
  import {
3
6
  __export
4
7
  } from "./chunk-K3NQKI34.js";
@@ -144,6 +147,31 @@ var messages = sqliteTable(
144
147
  (table) => [index("idx_messages_conversation_id").on(table.conversation_id)]
145
148
  );
146
149
 
150
+ // src/lib/db.ts
151
+ import { chmodSync, existsSync } from "fs";
152
+ import { dirname, resolve } from "path";
153
+ import { fileURLToPath } from "url";
154
+ import { drizzle } from "drizzle-orm/libsql";
155
+ import { migrate } from "drizzle-orm/libsql/migrator";
156
+ var __dirname = dirname(fileURLToPath(import.meta.url));
157
+ var migrationsFolder = existsSync(resolve(__dirname, "../drizzle")) ? resolve(__dirname, "../drizzle") : resolve(__dirname, "../../drizzle");
158
+ var db = null;
159
+ async function getDb() {
160
+ if (db) return db;
161
+ const dbPath = process.env.VOLUTE_DB_PATH || resolve(voluteHome(), "volute.db");
162
+ db = drizzle({ connection: { url: `file:${dbPath}` }, schema: schema_exports });
163
+ await migrate(db, { migrationsFolder });
164
+ try {
165
+ chmodSync(dbPath, 384);
166
+ } catch (err) {
167
+ console.error(
168
+ `[volute] WARNING: Failed to restrict database file permissions on ${dbPath}:`,
169
+ err
170
+ );
171
+ }
172
+ return db;
173
+ }
174
+
147
175
  export {
148
176
  users,
149
177
  conversations,
@@ -155,5 +183,5 @@ export {
155
183
  deliveryQueue,
156
184
  activity,
157
185
  messages,
158
- schema_exports
186
+ getDb
159
187
  };
@@ -1,20 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ getDb,
4
+ sharedSkills
5
+ } from "./chunk-SGPEZ32F.js";
2
6
  import {
3
7
  logger_default
4
8
  } from "./chunk-YUIHSKR6.js";
5
- import {
6
- getDb
7
- } from "./chunk-5XNT2472.js";
8
- import {
9
- sharedSkills
10
- } from "./chunk-NSE7VJQA.js";
11
9
  import {
12
10
  exec,
13
11
  gitExec
14
- } from "./chunk-DYZGP3EW.js";
12
+ } from "./chunk-JTDFJWI2.js";
15
13
  import {
16
14
  voluteHome
17
- } from "./chunk-EBGCNDMM.js";
15
+ } from "./chunk-B2CPS4QU.js";
18
16
 
19
17
  // src/lib/skills.ts
20
18
  import { createHash } from "crypto";
@@ -316,11 +314,16 @@ function findSkillsRoot() {
316
314
  let dir = dirname(new URL(import.meta.url).pathname);
317
315
  for (let i = 0; i < 5; i++) {
318
316
  const candidate = resolve(dir, "skills");
319
- if (existsSync(candidate) && readdirSync(candidate).length > 0) return candidate;
317
+ if (existsSync(candidate) && hasSkillSubdir(candidate)) return candidate;
320
318
  dir = dirname(dir);
321
319
  }
322
320
  throw new Error("Skills directory not found");
323
321
  }
322
+ function hasSkillSubdir(dir) {
323
+ return readdirSync(dir, { withFileTypes: true }).some(
324
+ (e) => e.isDirectory() && existsSync(join(dir, e.name, "SKILL.md"))
325
+ );
326
+ }
324
327
  function hashSkillDir(dir) {
325
328
  const hash = createHash("sha256");
326
329
  const files = listFilesRecursive(dir).sort();
@@ -1,21 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- logger_default
4
- } from "./chunk-YUIHSKR6.js";
5
2
  import {
6
3
  loadMergedEnv
7
- } from "./chunk-VDWCHYTS.js";
8
- import {
9
- getDb
10
- } from "./chunk-5XNT2472.js";
4
+ } from "./chunk-PHU4DEAJ.js";
11
5
  import {
6
+ getDb,
7
+ mindHistory,
12
8
  systemPrompts
13
- } from "./chunk-NSE7VJQA.js";
9
+ } from "./chunk-SGPEZ32F.js";
10
+ import {
11
+ logger_default
12
+ } from "./chunk-YUIHSKR6.js";
14
13
  import {
15
14
  chownMindDir,
16
15
  isIsolationEnabled,
17
16
  wrapForIsolation
18
- } from "./chunk-OGXOMR65.js";
17
+ } from "./chunk-NWPT4ASZ.js";
19
18
  import {
20
19
  findMind,
21
20
  findVariant,
@@ -24,9 +23,9 @@ import {
24
23
  setVariantRunning,
25
24
  stateDir,
26
25
  voluteHome
27
- } from "./chunk-EBGCNDMM.js";
26
+ } from "./chunk-B2CPS4QU.js";
28
27
 
29
- // src/lib/mind-manager.ts
28
+ // src/lib/daemon/mind-manager.ts
30
29
  import { execFile, spawn } from "child_process";
31
30
  import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
32
31
  import { resolve } from "path";
@@ -79,8 +78,12 @@ var PROMPT_KEYS = [
79
78
  "restart_message",
80
79
  "merge_message",
81
80
  "compaction_warning",
81
+ "compaction_instructions",
82
82
  "reply_instructions",
83
- "channel_invite"
83
+ "channel_invite",
84
+ "pre_sleep",
85
+ "wake_summary",
86
+ "wake_trigger_summary"
84
87
  ];
85
88
  var PROMPT_DEFAULTS = {
86
89
  seed_soul: {
@@ -88,7 +91,7 @@ var PROMPT_DEFAULTS = {
88
91
 
89
92
  You don't have a soul yet. You're a seed \u2014 a new mind in the process of discovering who you are.
90
93
  \${description}
91
- Have a conversation with the human. Explore what kind of mind you want to be. When you're ready, write your SOUL.md and MEMORY.md, then run \`volute sprout\` to complete the transformation.
94
+ Have a conversation with the human. Explore what kind of mind you want to be. When you're ready, write your SOUL.md and MEMORY.md, then run \`volute mind sprout\` to complete the transformation.
92
95
  `,
93
96
  description: "SOUL.md for seed minds",
94
97
  variables: ["name", "description"],
@@ -131,6 +134,12 @@ Have a conversation with the human. Explore what kind of mind you want to be. Wh
131
134
  variables: ["date"],
132
135
  category: "mind"
133
136
  },
137
+ compaction_instructions: {
138
+ content: "Preserve your sense of who you are, what matters to you, what happened in this conversation, and the threads of thought and connection you'd want to return to.",
139
+ description: "Custom instructions for the compaction summarizer",
140
+ variables: [],
141
+ category: "mind"
142
+ },
134
143
  reply_instructions: {
135
144
  content: 'To reply to this message, use: volute send ${channel} "your message"',
136
145
  description: "First-message reply hint injected via hook",
@@ -162,6 +171,24 @@ To reject, delete \${filePath}`,
162
171
  "batchRecommendation"
163
172
  ],
164
173
  category: "mind"
174
+ },
175
+ pre_sleep: {
176
+ content: "It's time to sleep. Save anything important to memory or your journal before resting.\nYou'll wake at ${wakeTime}. ${queuedInfo}",
177
+ description: "Pre-sleep message sent before stopping the mind",
178
+ variables: ["wakeTime", "queuedInfo"],
179
+ category: "system"
180
+ },
181
+ wake_summary: {
182
+ content: "Good morning \u2014 it's ${currentDate}. You slept from ${sleepTime} to now (${duration}).\n${queuedSummary}",
183
+ description: "Wake-up summary after scheduled sleep",
184
+ variables: ["currentDate", "sleepTime", "duration", "queuedSummary"],
185
+ category: "system"
186
+ },
187
+ wake_trigger_summary: {
188
+ content: "You were woken at ${currentDate} by a message on ${triggerChannel}.\nYou've been sleeping since ${sleepTime} (${duration}). ${queuedSummary}\nYou'll go back to sleep after handling this.",
189
+ description: "Wake-up summary when woken by a trigger message",
190
+ variables: ["currentDate", "triggerChannel", "sleepTime", "duration", "queuedSummary"],
191
+ category: "system"
165
192
  }
166
193
  };
167
194
  function isValidKey(key) {
@@ -215,51 +242,6 @@ async function getMindPromptDefaults() {
215
242
  return result;
216
243
  }
217
244
 
218
- // src/lib/restart-tracker.ts
219
- var DEFAULT_MAX_ATTEMPTS = 5;
220
- var DEFAULT_BASE_DELAY = 3e3;
221
- var DEFAULT_MAX_DELAY = 6e4;
222
- var RestartTracker = class {
223
- attempts = /* @__PURE__ */ new Map();
224
- maxAttempts;
225
- baseDelay;
226
- maxDelay;
227
- constructor(opts) {
228
- this.maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
229
- this.baseDelay = opts?.baseDelay ?? DEFAULT_BASE_DELAY;
230
- this.maxDelay = opts?.maxDelay ?? DEFAULT_MAX_DELAY;
231
- }
232
- recordCrash(key) {
233
- const attempts = this.attempts.get(key) ?? 0;
234
- if (attempts >= this.maxAttempts) {
235
- return { shouldRestart: false, delay: 0, attempt: attempts };
236
- }
237
- const delay = Math.min(this.baseDelay * 2 ** attempts, this.maxDelay);
238
- this.attempts.set(key, attempts + 1);
239
- return { shouldRestart: true, delay, attempt: attempts + 1 };
240
- }
241
- reset(key) {
242
- return this.attempts.delete(key);
243
- }
244
- getAttempts(key) {
245
- return this.attempts.get(key) ?? 0;
246
- }
247
- get maxRestartAttempts() {
248
- return this.maxAttempts;
249
- }
250
- /** Bulk-load attempts from a Map (for persistence). */
251
- load(data) {
252
- this.attempts = new Map(data);
253
- }
254
- /** Export current attempts as a Map (for persistence). */
255
- save() {
256
- return new Map(this.attempts);
257
- }
258
- clear() {
259
- this.attempts.clear();
260
- }
261
- };
262
-
263
245
  // src/lib/rotating-log.ts
264
246
  import {
265
247
  createWriteStream,
@@ -313,7 +295,52 @@ var RotatingLog = class extends Writable {
313
295
  }
314
296
  };
315
297
 
316
- // src/lib/mind-manager.ts
298
+ // src/lib/daemon/restart-tracker.ts
299
+ var DEFAULT_MAX_ATTEMPTS = 5;
300
+ var DEFAULT_BASE_DELAY = 3e3;
301
+ var DEFAULT_MAX_DELAY = 6e4;
302
+ var RestartTracker = class {
303
+ attempts = /* @__PURE__ */ new Map();
304
+ maxAttempts;
305
+ baseDelay;
306
+ maxDelay;
307
+ constructor(opts) {
308
+ this.maxAttempts = opts?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
309
+ this.baseDelay = opts?.baseDelay ?? DEFAULT_BASE_DELAY;
310
+ this.maxDelay = opts?.maxDelay ?? DEFAULT_MAX_DELAY;
311
+ }
312
+ recordCrash(key) {
313
+ const attempts = this.attempts.get(key) ?? 0;
314
+ if (attempts >= this.maxAttempts) {
315
+ return { shouldRestart: false, delay: 0, attempt: attempts };
316
+ }
317
+ const delay = Math.min(this.baseDelay * 2 ** attempts, this.maxDelay);
318
+ this.attempts.set(key, attempts + 1);
319
+ return { shouldRestart: true, delay, attempt: attempts + 1 };
320
+ }
321
+ reset(key) {
322
+ return this.attempts.delete(key);
323
+ }
324
+ getAttempts(key) {
325
+ return this.attempts.get(key) ?? 0;
326
+ }
327
+ get maxRestartAttempts() {
328
+ return this.maxAttempts;
329
+ }
330
+ /** Bulk-load attempts from a Map (for persistence). */
331
+ load(data) {
332
+ this.attempts = new Map(data);
333
+ }
334
+ /** Export current attempts as a Map (for persistence). */
335
+ save() {
336
+ return new Map(this.attempts);
337
+ }
338
+ clear() {
339
+ this.attempts.clear();
340
+ }
341
+ };
342
+
343
+ // src/lib/daemon/mind-manager.ts
317
344
  var mlog = logger_default.child("minds");
318
345
  var execFileAsync = promisify(execFile);
319
346
  function mindPidPath(name) {
@@ -470,6 +497,9 @@ var MindManager = class {
470
497
  setPendingContext(name, context) {
471
498
  this.pendingContext.set(name, context);
472
499
  }
500
+ /** Deliver pending context (merge info, sprout, restart) directly to the mind via HTTP.
501
+ * Intentionally bypasses DeliveryManager — these are system messages that should not be
502
+ * routed, gated, or batched. */
473
503
  async deliverPendingContext(name) {
474
504
  const context = this.pendingContext.get(name);
475
505
  if (!context) return;
@@ -487,12 +517,24 @@ var MindManager = class {
487
517
  if (context.summary) parts.push(`Changes: ${context.summary}`);
488
518
  if (context.justification) parts.push(`Why: ${context.justification}`);
489
519
  if (context.memory) parts.push(`Context: ${context.memory}`);
520
+ const content = parts.join("\n");
521
+ try {
522
+ const db = await getDb();
523
+ await db.insert(mindHistory).values({
524
+ mind: name,
525
+ type: "inbound",
526
+ channel: "system",
527
+ content
528
+ });
529
+ } catch (err) {
530
+ mlog.error(`failed to persist pending context for ${name}`, logger_default.errorData(err));
531
+ }
490
532
  try {
491
533
  await fetch(`http://127.0.0.1:${tracked.port}/message`, {
492
534
  method: "POST",
493
535
  headers: { "Content-Type": "application/json" },
494
536
  body: JSON.stringify({
495
- content: [{ type: "text", text: parts.join("\n") }],
537
+ content: [{ type: "text", text: content }],
496
538
  channel: "system"
497
539
  })
498
540
  });
@@ -505,12 +547,19 @@ var MindManager = class {
505
547
  this.minds.delete(name);
506
548
  if (this.shuttingDown || this.stopping.has(name)) return;
507
549
  mlog.error(`mind ${name} exited with code ${code}`);
508
- import("./mind-activity-tracker-624QLQLC.js").then(({ markIdle }) => markIdle(name)).catch(() => {
509
- });
510
- import("./activity-events-OMXKXD5N.js").then(
550
+ try {
551
+ const { getSleepManagerIfReady } = await import("./sleep-manager-3RWUX2ZR.js");
552
+ if (getSleepManagerIfReady()?.isSleeping(name)) {
553
+ mlog.info(`${name} is sleeping \u2014 skipping crash recovery`);
554
+ return;
555
+ }
556
+ } catch (err) {
557
+ mlog.warn(`failed to check sleep state for ${name}`, logger_default.errorData(err));
558
+ }
559
+ import("./mind-activity-tracker-PGC3DBJ7.js").then(({ markIdle }) => markIdle(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
560
+ import("./activity-events-3WHHCOBB.js").then(
511
561
  ({ publish }) => publish({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
512
- ).catch(() => {
513
- });
562
+ ).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
514
563
  const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
515
564
  this.saveCrashAttempts();
516
565
  if (!shouldRestart) {
@@ -636,17 +685,17 @@ function getMindManager() {
636
685
  }
637
686
 
638
687
  export {
639
- RestartTracker,
640
688
  RotatingLog,
641
- loadJsonMap,
642
- saveJsonMap,
643
- clearJsonMap,
689
+ RestartTracker,
644
690
  PROMPT_KEYS,
645
691
  PROMPT_DEFAULTS,
646
692
  substitute,
647
693
  getPrompt,
648
694
  getPromptIfCustom,
649
695
  getMindPromptDefaults,
696
+ loadJsonMap,
697
+ saveJsonMap,
698
+ clearJsonMap,
650
699
  MindManager,
651
700
  initMindManager,
652
701
  getMindManager
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  execInherit
4
- } from "./chunk-DYZGP3EW.js";
4
+ } from "./chunk-JTDFJWI2.js";
5
5
  import {
6
6
  voluteHome
7
- } from "./chunk-EBGCNDMM.js";
7
+ } from "./chunk-B2CPS4QU.js";
8
8
 
9
9
  // src/lib/service-mode.ts
10
10
  import { execFileSync } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  stateDir
4
- } from "./chunk-EBGCNDMM.js";
4
+ } from "./chunk-B2CPS4QU.js";
5
5
 
6
6
  // src/lib/slugify.ts
7
7
  function slugify(text) {