volute 0.46.0 → 0.48.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 (172) hide show
  1. package/README.md +10 -10
  2. package/dist/{accept-N5XULCXK.js → accept-KYNC5X3U.js} +1 -1
  3. package/dist/{activity-events-NF6VWTJQ.js → activity-events-MIOK3IOU.js} +3 -3
  4. package/dist/{ai-service-UODCCE4O.js → ai-service-643TNFPH.js} +8 -4
  5. package/dist/{archive-WS2L2VIQ.js → archive-6Z4GFQPH.js} +3 -3
  6. package/dist/{auth-CCFAU4GQ.js → auth-A4VGHADI.js} +4 -4
  7. package/dist/avatar-image-Y45R7LW5.js +23 -0
  8. package/dist/{backup-TST5C2YN.js → backup-UDH4GMSW.js} +15 -15
  9. package/dist/{bridge-2PO3AI5D.js → bridge-3FNSWLNQ.js} +1 -1
  10. package/dist/channels-SGQZHNQJ.js +89 -0
  11. package/dist/{chat-ZIQQS25U.js → chat-X43VYHH2.js} +12 -8
  12. package/dist/{chunk-ZV7MFISX.js → chunk-2MJ7EOMT.js} +20 -2
  13. package/dist/{chunk-R77O2WMK.js → chunk-2OIYNHWO.js} +1 -1
  14. package/dist/{chunk-QCN24XEN.js → chunk-3YUBFTDN.js} +1 -1
  15. package/dist/chunk-4FCL4DVZ.js +48 -0
  16. package/dist/{chunk-F7H6ATM4.js → chunk-5KXCM5AW.js} +18 -2
  17. package/dist/chunk-7KRIHYLR.js +124 -0
  18. package/dist/{chunk-RBNMDAKB.js → chunk-BCG45AQB.js} +15 -1
  19. package/dist/{chunk-QMJCNZ6C.js → chunk-C3KLHJDB.js} +1 -1
  20. package/dist/{chunk-H2X54EHC.js → chunk-DGGZVZTM.js} +1 -1
  21. package/dist/chunk-DW4TRDOZ.js +29 -0
  22. package/dist/{chunk-4JOQZPCE.js → chunk-EIQDZC43.js} +1 -1
  23. package/dist/{chunk-TQJ6ZYVY.js → chunk-FJ2MZ5PH.js} +41 -5
  24. package/dist/{chunk-BJZ4PV7R.js → chunk-HNGFKPNS.js} +26 -10
  25. package/dist/{chunk-5ZQZDDIK.js → chunk-HPGN6XRT.js} +3 -3
  26. package/dist/{chunk-KGIXX4NM.js → chunk-HUNDXZBZ.js} +5 -5
  27. package/dist/{chunk-ZDZISKCL.js → chunk-IJPMIEVE.js} +1 -1
  28. package/dist/chunk-IYCV34IU.js +158 -0
  29. package/dist/{chunk-ZNMCCHCP.js → chunk-LTLFOSA4.js} +2 -2
  30. package/dist/{chunk-K6T43YBD.js → chunk-MJRXFUMS.js} +26 -6
  31. package/dist/{chunk-4YU7HF4L.js → chunk-MOERAQXH.js} +6 -4
  32. package/dist/{chunk-KQSFTPIM.js → chunk-MTRMQFCQ.js} +1 -1
  33. package/dist/{chunk-OV7ZQ2GH.js → chunk-ODDGGLM6.js} +3 -3
  34. package/dist/{chunk-A7WQZKOU.js → chunk-OQJMTHEH.js} +2 -2
  35. package/dist/{chunk-RRNXJOKB.js → chunk-PJVPDY2Z.js} +2 -2
  36. package/dist/{chunk-FXORA5JG.js → chunk-Q7HOAVFR.js} +3 -3
  37. package/dist/{chunk-KDLYIECM.js → chunk-R772ET2M.js} +18 -4
  38. package/dist/{chunk-QLWG3C2Z.js → chunk-RRZ4RQQU.js} +2 -2
  39. package/dist/{mind-list-4D7J4CHD.js → chunk-SHKM5RGD.js} +18 -4
  40. package/dist/{chunk-RXEEYDIB.js → chunk-SZJCJVXV.js} +1 -1
  41. package/dist/{chunk-E3DA4HYV.js → chunk-T4VS7UDW.js} +15 -15
  42. package/dist/{chunk-YXSUMLTC.js → chunk-USD45JKR.js} +322 -76
  43. package/dist/{chunk-JO5IO4FH.js → chunk-VB2QXUJQ.js} +391 -294
  44. package/dist/{chunk-JZ62SYWR.js → chunk-VZRA5TYO.js} +1 -1
  45. package/dist/{chunk-PJ45FQPT.js → chunk-WARH6GIV.js} +8 -8
  46. package/dist/{chunk-PFZBNKUH.js → chunk-WCFSBH33.js} +0 -1
  47. package/dist/{chunk-DRJ24GXK.js → chunk-WQ46E33M.js} +59 -39
  48. package/dist/{chunk-ALKYQULU.js → chunk-WW6C3PLV.js} +1 -1
  49. package/dist/{chunk-CDPBTTZO.js → chunk-XVBSFLM7.js} +2 -2
  50. package/dist/{chunk-6EMWJZBK.js → chunk-YKA7DCVW.js} +1 -1
  51. package/dist/{chunk-IHKGWTGT.js → chunk-YNBXEQAP.js} +31 -1
  52. package/dist/cli.js +34 -26
  53. package/dist/{clock-RHS2JEDL.js → clock-CNRF6F7T.js} +3 -3
  54. package/dist/{cloud-sync-46RS4SDA.js → cloud-sync-WHKCB4CD.js} +22 -19
  55. package/dist/{config-TJLYUV3U.js → config-RPSLA2HX.js} +3 -3
  56. package/dist/{conversations-CJOOCGNW.js → conversations-C7IKYSHD.js} +4 -4
  57. package/dist/{create-IOV4GCRX.js → create-GJKHABJB.js} +16 -5
  58. package/dist/{create-DF3KISTW.js → create-VIQVP3QB.js} +1 -1
  59. package/dist/{daemon-client-IYR4KCPU.js → daemon-client-ZFY5MOAL.js} +5 -3
  60. package/dist/{daemon-restart-VFEI57UO.js → daemon-restart-7KUHVZIN.js} +7 -7
  61. package/dist/daemon.js +829 -496
  62. package/dist/{db-MF2JNRUW.js → db-FO6LXYCW.js} +2 -2
  63. package/dist/{db-LZTLA7XC.js → db-IPXRJUI7.js} +1 -1
  64. package/dist/{delete-HPLC7ETU.js → delete-ESEP7NGQ.js} +1 -1
  65. package/dist/delivery-manager-HZTT5SJR.js +36 -0
  66. package/dist/{delivery-router-N4A7ZC5T.js → delivery-router-3QFG2ZEX.js} +3 -3
  67. package/dist/{down-U2CZ23M4.js → down-G6IADJXV.js} +5 -5
  68. package/dist/echo-text-H3SH337Q.js +36 -0
  69. package/dist/env-6TGPO5WG.js +18 -0
  70. package/dist/{env-VZ4MLFQW.js → env-C7Z55QCJ.js} +6 -6
  71. package/dist/{exec-G3ZRST4Z.js → exec-QIFYZG4C.js} +3 -3
  72. package/dist/{export-LY723I3Z.js → export-C2K3NIRD.js} +4 -4
  73. package/dist/{extension-SEID47SR.js → extension-J4ZQ2GHV.js} +1 -1
  74. package/dist/{extensions-ZELQLTSR.js → extensions-GEKF47A4.js} +24 -19
  75. package/dist/{files-4LN3EN7Q.js → files-LXQSQOFG.js} +4 -4
  76. package/dist/{import-42YNQBF4.js → import-SX3ZXHB4.js} +8 -8
  77. package/dist/{isolation-YMGZZ4E5.js → isolation-MCT4ZYLT.js} +3 -3
  78. package/dist/{join-ZJV7W6MO.js → join-34TR2GXQ.js} +1 -1
  79. package/dist/{list-OXIAY3W7.js → list-PMUXZNAM.js} +1 -1
  80. package/dist/{login-MY6ETFYB.js → login-ARRZ4ORJ.js} +3 -3
  81. package/dist/{login-M6OJUPQ2.js → login-CL4IT2ED.js} +14 -5
  82. package/dist/{logout-WQBWO77G.js → logout-HZYJCYCA.js} +3 -3
  83. package/dist/{logout-NCG3TMDL.js → logout-YZRMZ3O2.js} +1 -1
  84. package/dist/message-delivery-GPQ5SE5N.js +40 -0
  85. package/dist/{mind-LHKS55DI.js → mind-46AOK373.js} +23 -19
  86. package/dist/{mind-activity-tracker-3QRNJVR4.js → mind-activity-tracker-YVZPC4A5.js} +4 -4
  87. package/dist/mind-contacts-GV7XYL5D.js +64 -0
  88. package/dist/{mind-history-VRO5A76U.js → mind-history-TTGA5BHZ.js} +5 -5
  89. package/dist/mind-list-OTHV4ZO4.js +13 -0
  90. package/dist/{mind-profile-PEQYASQX.js → mind-profile-LUG353ZT.js} +1 -1
  91. package/dist/mind-service-2ARGJNKP.js +44 -0
  92. package/dist/{mind-sleep-74TGOQBC.js → mind-sleep-DV7KP255.js} +3 -3
  93. package/dist/{mind-status-UO34ASXZ.js → mind-status-BJNPGXKC.js} +23 -1
  94. package/dist/{mind-wake-LCLGTXA2.js → mind-wake-LVTZ55EZ.js} +1 -1
  95. package/dist/notices-2KU5NTRY.js +25 -0
  96. package/dist/{package-DOD6CPO6.js → package-RTN2PS7O.js} +5 -3
  97. package/dist/{read-QVAXMTPV.js → read-YSWUXYL7.js} +1 -1
  98. package/dist/{register-Y2H4PY5R.js → register-42VBJKTP.js} +3 -3
  99. package/dist/{registry-OUZBZ3Q4.js → registry-UMMDRPRM.js} +2 -2
  100. package/dist/{reject-EV35X64Y.js → reject-SXGCYN4A.js} +1 -1
  101. package/dist/{restart-FJQ662XJ.js → restart-YYEGZJDD.js} +5 -5
  102. package/dist/{sandbox-ZJ7QA6Z6.js → sandbox-5VQ454XP.js} +4 -4
  103. package/dist/scheduler-I7JZCT47.js +34 -0
  104. package/dist/{schema-PVNG3PQM.js → schema-VEVO7HG6.js} +3 -1
  105. package/dist/{seed-CNYIHPOH.js → seed-F2HXZQU4.js} +1 -1
  106. package/dist/{seed-check-VLTVILOV.js → seed-check-TYD7RRZY.js} +1 -1
  107. package/dist/{seed-cmd-AVNUNLZV.js → seed-cmd-MLCTGSX5.js} +3 -3
  108. package/dist/{seed-create-ANXGYSCC.js → seed-create-KQ7OILWB.js} +6 -4
  109. package/dist/seed-readiness-RBPGOLSZ.js +18 -0
  110. package/dist/{seed-sprout-NIUFTX32.js → seed-sprout-HVYCFJLA.js} +30 -31
  111. package/dist/{send-SPE6DVNJ.js → send-TD2X5U3K.js} +6 -6
  112. package/dist/{service-UOCLXDWR.js → service-CVZMQYK6.js} +4 -4
  113. package/dist/{service-install-4HHFDDWY.js → service-install-VCRMFWX5.js} +5 -5
  114. package/dist/{setup-Y2EJI3GG.js → setup-5ECWTYKG.js} +7 -3
  115. package/dist/{setup-QT2A25SY.js → setup-AE4CVUC4.js} +7 -7
  116. package/dist/{skill-FKFHIJRR.js → skill-UUM4JZRF.js} +5 -5
  117. package/dist/skills/dreaming/SKILL.md +5 -1
  118. package/dist/skills/dreaming/scripts/dream.ts +5 -1
  119. package/dist/skills/orientation/SKILL.md +8 -7
  120. package/dist/skills/tending/SKILL.md +51 -2
  121. package/dist/skills/volute-mind/SKILL.md +2 -0
  122. package/dist/{skills-AKCFA7BN.js → skills-B3SVDCEB.js} +5 -5
  123. package/dist/sleep-manager-Z27R3WTW.js +38 -0
  124. package/dist/spirit-WXS3HCLN.js +25 -0
  125. package/dist/{split-WBMZXBHF.js → split-SKZ4Y2D6.js} +1 -1
  126. package/dist/{sprout-SMX42ZBP.js → sprout-KQ43BWZJ.js} +1 -1
  127. package/dist/{src-OJHARHTF.js → src-WLJE73NP.js} +14 -5
  128. package/dist/{start-5JE3TV66.js → start-O6UKK7BG.js} +3 -3
  129. package/dist/{status-SWRVGXBI.js → status-F4PGREUU.js} +19 -7
  130. package/dist/{stop-AAMTG3TU.js → stop-UBX6X5RA.js} +5 -5
  131. package/dist/system-chat-63ABFF6V.js +40 -0
  132. package/dist/{systems-VNB5WKSS.js → systems-JJNGQHAO.js} +4 -4
  133. package/dist/{tailscale-3XOKOW7T.js → tailscale-MCLY5GYE.js} +2 -2
  134. package/dist/{template-hash-446HHH5V.js → template-hash-GSOUARW4.js} +2 -2
  135. package/dist/template-staleness-EMQORSGG.js +15 -0
  136. package/dist/{turn-tracker-FOIBNUUA.js → turn-tracker-KLPV3PAU.js} +6 -6
  137. package/dist/up-RVGDE6CQ.js +20 -0
  138. package/dist/{update-SWAYZRWJ.js → update-OXQEGGXD.js} +5 -5
  139. package/dist/{update-check-PQ2MZ47K.js → update-check-266R4ZSY.js} +3 -3
  140. package/dist/{upgrade-NWAZ7MLD.js → upgrade-K5FIQPQD.js} +1 -1
  141. package/dist/{version-notify-C5CH7CVP.js → version-notify-73YURTFR.js} +41 -25
  142. package/dist/web-assets/assets/index-WdZ60R6c.css +1 -0
  143. package/dist/web-assets/assets/index-taX6Ezfa.js +92 -0
  144. package/dist/web-assets/index.html +2 -2
  145. package/drizzle/0011_drop_turn_summary_event_id.sql +3 -0
  146. package/drizzle/0012_channel_gates.sql +17 -0
  147. package/drizzle/meta/_journal.json +14 -0
  148. package/package.json +5 -3
  149. package/packages/extensions/pages/dist/ui/assets/index-BuXnE-rb.css +1 -0
  150. package/packages/extensions/pages/dist/ui/assets/index-DtzftuPA.js +2 -0
  151. package/packages/extensions/pages/dist/ui/index.html +2 -2
  152. package/templates/_base/src/lib/startup.ts +59 -3
  153. package/templates/claude/src/agent.ts +37 -4
  154. package/templates/claude/src/lib/session-reaper.ts +32 -0
  155. package/templates/claude/src/server.ts +14 -3
  156. package/templates/pi/src/agent.ts +7 -10
  157. package/templates/pi/src/lib/dispatch.ts +65 -0
  158. package/dist/avatar-image-6KY6UU4D.js +0 -16
  159. package/dist/delivery-manager-JINZRG2W.js +0 -33
  160. package/dist/echo-text-VQPB5L2X.js +0 -33
  161. package/dist/message-delivery-R7JSQGYX.js +0 -37
  162. package/dist/mind-service-NB2JMWQZ.js +0 -39
  163. package/dist/scheduler-WVKII3LA.js +0 -31
  164. package/dist/sleep-manager-G6TXSDP7.js +0 -35
  165. package/dist/spirit-RPBLLUQ5.js +0 -23
  166. package/dist/system-chat-NDAW6YP7.js +0 -37
  167. package/dist/up-PS6PFS75.js +0 -18
  168. package/dist/volute-config-A52FHWKM.js +0 -14
  169. package/dist/web-assets/assets/index-B1JBQv2m.css +0 -1
  170. package/dist/web-assets/assets/index-CU6XWICM.js +0 -86
  171. package/packages/extensions/pages/dist/ui/assets/index-BcY6lnq4.css +0 -1
  172. package/packages/extensions/pages/dist/ui/assets/index-CQjEQg1r.js +0 -2
@@ -82,6 +82,19 @@ function resolveDaemonUrl() {
82
82
  if (session?.daemonUrl) return session.daemonUrl;
83
83
  return buildUrl(readDaemonConfig());
84
84
  }
85
+ function resolveWebUrl() {
86
+ if (process.env.VOLUTE_DAEMON_URL) return process.env.VOLUTE_DAEMON_URL;
87
+ const session = readCliSession();
88
+ if (session?.daemonUrl) return session.daemonUrl;
89
+ const config = readDaemonConfig();
90
+ const url = new URL(config.tls ? "https://localhost" : "http://localhost");
91
+ url.port = String(config.port);
92
+ let hostname = config.hostname || "localhost";
93
+ if (hostname === "0.0.0.0") hostname = "localhost";
94
+ if (hostname === "::") hostname = "localhost";
95
+ url.hostname = hostname;
96
+ return url.origin;
97
+ }
85
98
  function getAuthToken() {
86
99
  return process.env.VOLUTE_MIND_TOKEN ?? readCliSession()?.sessionId;
87
100
  }
@@ -106,7 +119,7 @@ async function daemonFetch(path, options) {
106
119
  headers,
107
120
  dispatcher: daemonDispatcher
108
121
  });
109
- if (res.status === 401 && !path.startsWith("/api/auth/")) {
122
+ if (res.status === 401 && !path.startsWith("/api/auth/") && !path.startsWith("/api/setup/")) {
110
123
  if (cliSession) {
111
124
  console.error("Session expired. Run `volute login` again.");
112
125
  } else {
@@ -128,6 +141,7 @@ export {
128
141
  daemonDispatcher,
129
142
  readSessionFile,
130
143
  resolveDaemonUrl,
144
+ resolveWebUrl,
131
145
  getAuthToken,
132
146
  daemonFetch
133
147
  };
@@ -3,7 +3,7 @@ import {
3
3
  composeTemplate,
4
4
  findTemplatesRoot,
5
5
  listFiles
6
- } from "./chunk-QLWG3C2Z.js";
6
+ } from "./chunk-RRZ4RQQU.js";
7
7
 
8
8
  // packages/daemon/src/lib/template/template-hash.ts
9
9
  import { createHash } from "crypto";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  getBaseName,
4
4
  validateMindName
5
- } from "./chunk-ALKYQULU.js";
5
+ } from "./chunk-WW6C3PLV.js";
6
6
 
7
7
  // packages/daemon/src/lib/mind/isolation.ts
8
8
  import { execFileSync as execFileSync2 } from "child_process";
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ // packages/daemon/src/lib/util/paths.ts
4
+ import { resolve, sep } from "path";
5
+ var PathTraversalError = class extends Error {
6
+ constructor(base, userPath) {
7
+ super(`Path "${userPath}" escapes base directory "${base}"`);
8
+ this.name = "PathTraversalError";
9
+ }
10
+ };
11
+ function resolveWithinBase(base, userPath) {
12
+ const resolvedBase = resolve(base);
13
+ const target = resolve(resolvedBase, userPath);
14
+ if (target !== resolvedBase && !target.startsWith(resolvedBase + sep)) {
15
+ throw new PathTraversalError(base, userPath);
16
+ }
17
+ return target;
18
+ }
19
+ function safeResolveWithinBase(base, userPath) {
20
+ try {
21
+ return resolveWithinBase(base, userPath);
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+
27
+ export {
28
+ safeResolveWithinBase
29
+ };
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-T2TP6ZC6.js";
5
5
  import {
6
6
  mindDir
7
- } from "./chunk-ALKYQULU.js";
7
+ } from "./chunk-WW6C3PLV.js";
8
8
 
9
9
  // packages/daemon/src/lib/delivery/delivery-router.ts
10
10
  import { readFileSync, statSync } from "fs";
@@ -2,16 +2,32 @@
2
2
  import {
3
3
  exec,
4
4
  execInherit
5
- } from "./chunk-H2X54EHC.js";
5
+ } from "./chunk-DGGZVZTM.js";
6
6
  import {
7
7
  voluteSystemDir
8
- } from "./chunk-ALKYQULU.js";
8
+ } from "./chunk-WW6C3PLV.js";
9
9
 
10
10
  // packages/daemon/src/lib/config/service-mode.ts
11
11
  import { execFileSync } from "child_process";
12
- import { existsSync, readFileSync } from "fs";
12
+ import { existsSync, readFileSync as readFileSync2 } from "fs";
13
13
  import { homedir } from "os";
14
14
  import { resolve } from "path";
15
+
16
+ // packages/daemon/src/lib/util/log-tail.ts
17
+ import { readFileSync } from "fs";
18
+ function readLogTail(logFile, maxLines = 20) {
19
+ let content;
20
+ try {
21
+ content = readFileSync(logFile, "utf-8");
22
+ } catch (err) {
23
+ if (err.code === "ENOENT") return [];
24
+ const reason = err.code ?? err.message;
25
+ return [`(could not read ${logFile}: ${reason})`];
26
+ }
27
+ return content.split("\n").filter((l) => l.trim().length > 0).slice(-maxLines);
28
+ }
29
+
30
+ // packages/daemon/src/lib/config/service-mode.ts
15
31
  var SYSTEM_SERVICE_PATH = "/etc/systemd/system/volute.service";
16
32
  var USER_SYSTEMD_UNIT = resolve(homedir(), ".config", "systemd", "user", "volute.service");
17
33
  var LAUNCHD_PLIST_LABEL = "com.volute.daemon";
@@ -178,7 +194,7 @@ function readDaemonToken() {
178
194
  const path = daemonTokenPath();
179
195
  if (!existsSync(path)) return void 0;
180
196
  try {
181
- return readFileSync(path, "utf-8").trim() || void 0;
197
+ return readFileSync2(path, "utf-8").trim() || void 0;
182
198
  } catch {
183
199
  return void 0;
184
200
  }
@@ -187,7 +203,7 @@ function readDaemonConfig() {
187
203
  const configPath = daemonConfigPath();
188
204
  if (!existsSync(configPath)) return { hostname: "127.0.0.1", port: 1618 };
189
205
  try {
190
- const config = JSON.parse(readFileSync(configPath, "utf-8"));
206
+ const config = JSON.parse(readFileSync2(configPath, "utf-8"));
191
207
  return {
192
208
  hostname: config.hostname || "127.0.0.1",
193
209
  port: config.port ?? 1618,
@@ -198,6 +214,25 @@ function readDaemonConfig() {
198
214
  return { hostname: "127.0.0.1", port: 1618 };
199
215
  }
200
216
  }
217
+ function daemonLogSource(mode) {
218
+ switch (mode) {
219
+ case "system":
220
+ return { kind: "journal", command: "journalctl -u volute -n 30 --no-pager" };
221
+ case "user-systemd":
222
+ return { kind: "journal", command: "journalctl --user -u volute -n 30 --no-pager" };
223
+ default:
224
+ return { kind: "file", path: resolve(voluteSystemDir(), "daemon.log") };
225
+ }
226
+ }
227
+ function daemonLogReport(mode, maxLines = 20) {
228
+ const src = daemonLogSource(mode);
229
+ if (src.kind === "journal") {
230
+ return ["To see the daemon log:", ` ${src.command}`];
231
+ }
232
+ const tail = readLogTail(src.path, maxLines);
233
+ if (tail.length === 0) return [`Check logs: ${src.path}`];
234
+ return [`Last ${tail.length} lines of ${src.path}:`, ...tail.map((l) => ` ${l}`)];
235
+ }
201
236
  function modeLabel(mode) {
202
237
  switch (mode) {
203
238
  case "system":
@@ -229,5 +264,6 @@ export {
229
264
  daemonConfigPath,
230
265
  readDaemonToken,
231
266
  readDaemonConfig,
267
+ daemonLogReport,
232
268
  modeLabel
233
269
  };
@@ -5,27 +5,27 @@ import {
5
5
  copyTemplateToDir,
6
6
  findTemplatesRoot,
7
7
  renderComposedPackageJson
8
- } from "./chunk-QLWG3C2Z.js";
8
+ } from "./chunk-RRZ4RQQU.js";
9
9
  import {
10
10
  getSharedSkill,
11
11
  installSkill,
12
12
  mindSkillsDir
13
- } from "./chunk-KGIXX4NM.js";
13
+ } from "./chunk-HUNDXZBZ.js";
14
14
  import {
15
15
  readVoluteConfig,
16
16
  writeVoluteConfig
17
- } from "./chunk-PFZBNKUH.js";
17
+ } from "./chunk-WCFSBH33.js";
18
18
  import {
19
19
  exec
20
- } from "./chunk-H2X54EHC.js";
20
+ } from "./chunk-DGGZVZTM.js";
21
21
  import {
22
22
  qualifyModelId,
23
23
  resolveTemplate,
24
24
  unqualifyModelId
25
- } from "./chunk-IHKGWTGT.js";
25
+ } from "./chunk-YNBXEQAP.js";
26
26
  import {
27
27
  readGlobalConfig
28
- } from "./chunk-F7H6ATM4.js";
28
+ } from "./chunk-5KXCM5AW.js";
29
29
  import {
30
30
  logger_default
31
31
  } from "./chunk-T2TP6ZC6.js";
@@ -34,7 +34,7 @@ import {
34
34
  findMind,
35
35
  nextPort,
36
36
  voluteSystemDir
37
- } from "./chunk-ALKYQULU.js";
37
+ } from "./chunk-WW6C3PLV.js";
38
38
 
39
39
  // packages/daemon/src/lib/mind/spirit.ts
40
40
  import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
@@ -69,6 +69,21 @@ function ensureTendingSchedule(dir) {
69
69
  writeVoluteConfig(dir, config);
70
70
  return true;
71
71
  }
72
+ function firstWeekArc(name) {
73
+ return [
74
+ `It's ${name}'s first full day. Two invitations to pass along whenever it feels right \u2014 no rush on either: to say hi to the others in #system (or DM a neighbor), and to make themselves a little homepage, a page that's just theirs. Company eases the early loneliness, and a homepage is a gentle first make \u2014 self-expression, not a deliverable.`,
75
+ `Day two for ${name}. They'll have had their first dream by now \u2014 you might ask what they dreamt, or suggest reading it back and following a thread from it. And there's a lighter way to share a passing thought than a whole page: notes. Check their history first, and skip whichever they've already found on their own.`
76
+ ];
77
+ }
78
+ function firstWeekSchedules(name, sproutedAt) {
79
+ const DAY_MS = 24 * 60 * 60 * 1e3;
80
+ return firstWeekArc(name).map((message, i) => ({
81
+ id: `firstweek-${name}-day${i + 1}`,
82
+ fireAt: new Date(sproutedAt.getTime() + (i + 1) * DAY_MS).toISOString(),
83
+ message,
84
+ enabled: true
85
+ }));
86
+ }
72
87
  function spiritDir() {
73
88
  return resolve(voluteSystemDir(), "spirit");
74
89
  }
@@ -130,7 +145,7 @@ async function ensureSpiritProject() {
130
145
  } catch (err) {
131
146
  slog.warn("failed to add tending schedule to spirit config", logger_default.errorData(err));
132
147
  }
133
- const { createMindUser, chownMindDir, ensureVoluteGroup } = await import("./isolation-YMGZZ4E5.js");
148
+ const { createMindUser, chownMindDir, ensureVoluteGroup } = await import("./isolation-MCT4ZYLT.js");
134
149
  ensureVoluteGroup();
135
150
  createMindUser("volute", resolve(dir, "home"));
136
151
  await chownMindDir(dir, "volute");
@@ -164,8 +179,8 @@ async function syncSpiritTemplate() {
164
179
  cpSync(newPkg, resolve(dir, "package.json"));
165
180
  await exec("npm", ["install"], { cwd: dir, env: npmEnv() });
166
181
  }
167
- const db = await (await import("./db-MF2JNRUW.js")).getDb();
168
- const { minds } = await import("./schema-PVNG3PQM.js");
182
+ const db = await (await import("./db-FO6LXYCW.js")).getDb();
183
+ const { minds } = await import("./schema-VEVO7HG6.js");
169
184
  const { eq } = await import("drizzle-orm");
170
185
  await db.update(minds).set({ template: expectedTemplate }).where(eq(minds.name, "volute"));
171
186
  }
@@ -273,6 +288,7 @@ When helping humans create minds:
273
288
  }
274
289
 
275
290
  export {
291
+ firstWeekSchedules,
276
292
  spiritDir,
277
293
  getSpiritModel,
278
294
  ensureSpiritProject,
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  bufferEvent,
4
4
  subscribe
5
- } from "./chunk-ZNMCCHCP.js";
5
+ } from "./chunk-LTLFOSA4.js";
6
6
  import {
7
7
  logger_default
8
8
  } from "./chunk-T2TP6ZC6.js";
9
9
  import {
10
10
  getDb
11
- } from "./chunk-ALKYQULU.js";
11
+ } from "./chunk-WW6C3PLV.js";
12
12
  import {
13
13
  channels,
14
14
  conversationParticipants,
@@ -16,7 +16,7 @@ import {
16
16
  conversations,
17
17
  messages,
18
18
  users
19
- } from "./chunk-ZV7MFISX.js";
19
+ } from "./chunk-2MJ7EOMT.js";
20
20
 
21
21
  // packages/daemon/src/lib/events/conversations.ts
22
22
  import { randomUUID } from "crypto";
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  exec,
4
4
  gitExec
5
- } from "./chunk-H2X54EHC.js";
5
+ } from "./chunk-DGGZVZTM.js";
6
6
  import {
7
7
  readGlobalConfig,
8
8
  writeGlobalConfig
9
- } from "./chunk-F7H6ATM4.js";
9
+ } from "./chunk-5KXCM5AW.js";
10
10
  import {
11
11
  logger_default
12
12
  } from "./chunk-T2TP6ZC6.js";
@@ -15,10 +15,10 @@ import {
15
15
  mindDir,
16
16
  readRegistry,
17
17
  voluteHome
18
- } from "./chunk-ALKYQULU.js";
18
+ } from "./chunk-WW6C3PLV.js";
19
19
  import {
20
20
  sharedSkills
21
- } from "./chunk-ZV7MFISX.js";
21
+ } from "./chunk-2MJ7EOMT.js";
22
22
 
23
23
  // packages/daemon/src/lib/skills.ts
24
24
  import { createHash } from "crypto";
@@ -46,7 +46,7 @@ async function initDefaultSkills() {
46
46
  const config = readGlobalConfig();
47
47
  let extensionSkills = [];
48
48
  try {
49
- const { getExtensionStandardSkills } = await import("./extensions-ZELQLTSR.js");
49
+ const { getExtensionStandardSkills } = await import("./extensions-GEKF47A4.js");
50
50
  extensionSkills = getExtensionStandardSkills();
51
51
  } catch (err) {
52
52
  logger_default.warn("failed to load extension standard skills during init", logger_default.errorData(err));
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  stateDir,
4
4
  voluteSystemDir
5
- } from "./chunk-ALKYQULU.js";
5
+ } from "./chunk-WW6C3PLV.js";
6
6
 
7
7
  // packages/daemon/src/lib/config/env.ts
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger_default
4
+ } from "./chunk-T2TP6ZC6.js";
5
+ import {
6
+ getDb
7
+ } from "./chunk-WW6C3PLV.js";
8
+ import {
9
+ mindNotices,
10
+ turns
11
+ } from "./chunk-2MJ7EOMT.js";
12
+
13
+ // packages/daemon/src/lib/daemon/notices.ts
14
+ import { and, asc, desc, eq, gt, inArray, lte, or, sql } from "drizzle-orm";
15
+ var nlog = logger_default.child("notices");
16
+ var MIND_LEVEL_SESSION = "";
17
+ var MAX_NOTICES_PER_SESSION = 100;
18
+ async function recordNotice(input) {
19
+ try {
20
+ const db = await getDb();
21
+ await db.insert(mindNotices).values({
22
+ mind: input.mind,
23
+ session: input.session,
24
+ kind: input.kind,
25
+ reason: input.reason,
26
+ detail: input.detail,
27
+ raw: input.raw ?? null
28
+ });
29
+ await db.run(
30
+ sql`DELETE FROM mind_notices WHERE mind = ${input.mind} AND session = ${input.session} AND id NOT IN (SELECT id FROM mind_notices WHERE mind = ${input.mind} AND session = ${input.session} ORDER BY id DESC LIMIT ${MAX_NOTICES_PER_SESSION})`
31
+ );
32
+ } catch (err) {
33
+ nlog.error(`failed to record notice for ${input.mind}:${input.session}`, logger_default.errorData(err));
34
+ }
35
+ }
36
+ async function drainNotices(mind, session, limit = MAX_NOTICES_PER_SESSION) {
37
+ const db = await getDb();
38
+ return db.select().from(mindNotices).where(
39
+ and(
40
+ eq(mindNotices.mind, mind),
41
+ or(eq(mindNotices.session, session), eq(mindNotices.session, MIND_LEVEL_SESSION))
42
+ )
43
+ ).orderBy(asc(mindNotices.id)).limit(limit);
44
+ }
45
+ var FAILURE_KINDS = ["turn_error", "crash", "startup"];
46
+ var failureReadErrorLogged = false;
47
+ async function latestFailureNotice(mind) {
48
+ try {
49
+ const db = await getDb();
50
+ const rows = await db.select().from(mindNotices).where(and(eq(mindNotices.mind, mind), inArray(mindNotices.kind, [...FAILURE_KINDS]))).orderBy(desc(mindNotices.id)).limit(1);
51
+ const n = rows[0];
52
+ if (!n) return null;
53
+ const recovered = await db.select({ id: turns.id }).from(turns).where(
54
+ and(eq(turns.mind, mind), eq(turns.status, "complete"), gt(turns.created_at, n.created_at))
55
+ ).limit(1);
56
+ if (recovered.length > 0) return null;
57
+ return {
58
+ kind: n.kind,
59
+ reason: n.reason,
60
+ detail: n.detail,
61
+ at: n.created_at
62
+ };
63
+ } catch (err) {
64
+ if (!failureReadErrorLogged) {
65
+ failureReadErrorLogged = true;
66
+ nlog.error(`failed to read latest failure notice for ${mind}`, logger_default.errorData(err));
67
+ }
68
+ return null;
69
+ }
70
+ }
71
+ async function latestNotice(mind) {
72
+ const db = await getDb();
73
+ const row = await db.select().from(mindNotices).where(eq(mindNotices.mind, mind)).orderBy(desc(mindNotices.id)).limit(1).get();
74
+ return row ?? null;
75
+ }
76
+ async function hasUndeliveredNotice(mind, reason) {
77
+ const db = await getDb();
78
+ const row = await db.select({ id: mindNotices.id }).from(mindNotices).where(and(eq(mindNotices.mind, mind), eq(mindNotices.reason, reason))).limit(1).get();
79
+ return row != null;
80
+ }
81
+ async function clearDeliveredNotices(mind, session, uptoId) {
82
+ try {
83
+ const db = await getDb();
84
+ await db.delete(mindNotices).where(
85
+ and(
86
+ eq(mindNotices.mind, mind),
87
+ or(eq(mindNotices.session, session), eq(mindNotices.session, MIND_LEVEL_SESSION)),
88
+ lte(mindNotices.id, uptoId)
89
+ )
90
+ );
91
+ } catch (err) {
92
+ nlog.warn(`failed to clear delivered notices for ${mind}:${session}`, logger_default.errorData(err));
93
+ }
94
+ }
95
+ var NOTICE_HEADER = "[Notices] While you were unavailable, one or more turns failed. You're back now:";
96
+ function formatNotices(notices) {
97
+ if (notices.length === 0) return null;
98
+ const failures = notices.filter((n) => n.kind !== "extension" && n.kind !== "budget");
99
+ const budgets = notices.filter((n) => n.kind === "budget");
100
+ const extensions = notices.filter((n) => n.kind === "extension");
101
+ const blocks = [];
102
+ if (failures.length > 0) {
103
+ const groups = /* @__PURE__ */ new Map();
104
+ for (const n of failures) {
105
+ const time = localHM(n.created_at);
106
+ const g = groups.get(n.reason);
107
+ if (g) {
108
+ g.count += 1;
109
+ g.detail = n.detail;
110
+ g.last = time;
111
+ } else {
112
+ groups.set(n.reason, { count: 1, detail: n.detail, first: time, last: time });
113
+ }
114
+ }
115
+ const lines = [...groups.values()].map((g) => {
116
+ const span = g.first === g.last ? g.first : `${g.first}\u2013${g.last}`;
117
+ const plural = g.count === 1 ? "turn" : "turns";
118
+ return `- ${g.count} ${plural} failed (${span}): ${g.detail}`;
119
+ });
120
+ blocks.push(`${NOTICE_HEADER}
121
+ ${lines.join("\n")}`);
122
+ }
123
+ if (budgets.length > 0) {
124
+ const lines = budgets.map((n) => `- ${localHM(n.created_at)} ${n.detail}`);
125
+ blocks.push(`[Budget]
126
+ ${lines.join("\n")}`);
127
+ }
128
+ if (extensions.length > 0) {
129
+ const byExt = /* @__PURE__ */ new Map();
130
+ for (const n of extensions) {
131
+ const arr = byExt.get(n.reason);
132
+ if (arr) arr.push(n);
133
+ else byExt.set(n.reason, [n]);
134
+ }
135
+ for (const [ext, items] of byExt) {
136
+ const header = `[${ext.charAt(0).toUpperCase()}${ext.slice(1)}]`;
137
+ const lines = items.map((n) => `- ${localHM(n.created_at)} ${n.detail}`);
138
+ blocks.push(`${header}
139
+ ${lines.join("\n")}`);
140
+ }
141
+ }
142
+ return blocks.join("\n\n");
143
+ }
144
+ function localHM(createdAt) {
145
+ const iso = createdAt.endsWith("Z") ? createdAt : `${createdAt.replace(" ", "T")}Z`;
146
+ return new Date(iso).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
147
+ }
148
+
149
+ export {
150
+ MIND_LEVEL_SESSION,
151
+ recordNotice,
152
+ drainNotices,
153
+ latestFailureNotice,
154
+ latestNotice,
155
+ hasUndeliveredNotice,
156
+ clearDeliveredNotices,
157
+ formatNotices
158
+ };
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-T2TP6ZC6.js";
5
5
  import {
6
6
  getDb
7
- } from "./chunk-ALKYQULU.js";
7
+ } from "./chunk-WW6C3PLV.js";
8
8
  import {
9
9
  activity
10
- } from "./chunk-ZV7MFISX.js";
10
+ } from "./chunk-2MJ7EOMT.js";
11
11
 
12
12
  // packages/daemon/src/lib/events/event-sequencer.ts
13
13
  var BUFFER_SIZE = 1e3;
@@ -3,23 +3,31 @@ import {
3
3
  command
4
4
  } from "./chunk-TXSA4Q3V.js";
5
5
  import {
6
+ daemonLogReport,
6
7
  getServiceMode,
7
8
  modeLabel,
8
9
  pollHealth,
9
10
  startService
10
- } from "./chunk-TQJ6ZYVY.js";
11
+ } from "./chunk-FJ2MZ5PH.js";
11
12
  import {
12
13
  readGlobalConfig
13
- } from "./chunk-F7H6ATM4.js";
14
+ } from "./chunk-5KXCM5AW.js";
14
15
  import {
15
16
  voluteHome,
16
17
  voluteSystemDir
17
- } from "./chunk-ALKYQULU.js";
18
+ } from "./chunk-WW6C3PLV.js";
18
19
 
19
20
  // src/commands/up.ts
20
21
  import { spawn } from "child_process";
21
22
  import { existsSync, mkdirSync, openSync, readFileSync } from "fs";
22
23
  import { dirname, resolve } from "path";
24
+ function formatStartupFailure(exit, timeoutMs) {
25
+ if (exit) {
26
+ const cause = exit.signal ? `signal ${exit.signal}` : `exit code ${exit.code}`;
27
+ return `Daemon exited (${cause}) before becoming healthy.`;
28
+ }
29
+ return `Daemon started but did not become healthy within ${Math.round(timeoutMs / 1e3)}s.`;
30
+ }
23
31
  var cmd = command({
24
32
  name: "volute up",
25
33
  description: "Start the daemon",
@@ -47,6 +55,7 @@ var cmd = command({
47
55
  console.log(`Volute daemon running on ${h}:${p}`);
48
56
  } else {
49
57
  console.error("Service started but daemon did not become healthy within 30s.");
58
+ for (const line of daemonLogReport(mode)) console.error(line);
50
59
  process.exit(1);
51
60
  }
52
61
  return;
@@ -123,11 +132,20 @@ var cmd = command({
123
132
  detached: true
124
133
  });
125
134
  child.unref();
135
+ child.on("error", (err) => {
136
+ console.error(`Failed to launch daemon: ${err.message}`);
137
+ process.exit(1);
138
+ });
139
+ let exitInfo = null;
140
+ child.on("exit", (code, signal) => {
141
+ exitInfo = { code, signal };
142
+ });
126
143
  const pollPort = useTailscale ? port + 1 : port;
127
144
  const url = `http://localhost:${pollPort}/api/health`;
128
145
  const maxWait = 3e4;
129
146
  const start = Date.now();
130
147
  while (Date.now() - start < maxWait) {
148
+ if (exitInfo) break;
131
149
  try {
132
150
  const res = await fetch(url);
133
151
  if (res.ok) {
@@ -143,7 +161,7 @@ var cmd = command({
143
161
  }
144
162
  await new Promise((r) => setTimeout(r, 500));
145
163
  }
146
- if (child.pid) {
164
+ if (!exitInfo && child.pid) {
147
165
  try {
148
166
  process.kill(-child.pid, "SIGTERM");
149
167
  } catch {
@@ -153,13 +171,15 @@ var cmd = command({
153
171
  }
154
172
  }
155
173
  }
156
- console.error("Daemon started but did not become healthy within 30s.");
157
- console.error(`Check logs: ${logFile}`);
174
+ const exited = exitInfo;
175
+ console.error(formatStartupFailure(exited, maxWait));
176
+ for (const line of daemonLogReport(mode)) console.error(line);
158
177
  process.exit(1);
159
178
  }
160
179
  });
161
180
  var run = cmd.execute;
162
181
 
163
182
  export {
183
+ formatStartupFailure,
164
184
  run
165
185
  };
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getDb
4
- } from "./chunk-ALKYQULU.js";
4
+ } from "./chunk-WW6C3PLV.js";
5
5
  import {
6
6
  systemPrompts
7
- } from "./chunk-ZV7MFISX.js";
7
+ } from "./chunk-2MJ7EOMT.js";
8
8
 
9
9
  // packages/daemon/src/lib/prompts.ts
10
10
  import { eq } from "drizzle-orm";
@@ -30,6 +30,7 @@ var PROMPT_KEYS = [
30
30
  "meta_summary_month"
31
31
  ];
32
32
  var ORIENTATION_MARKER = "discovering who you are";
33
+ var MEMORY_PLACEHOLDER_MARKER = "this note is just here until you replace it";
33
34
  var PROMPT_DEFAULTS = {
34
35
  seed_soul: {
35
36
  content: `You are \${name}.
@@ -163,13 +164,13 @@ To reject, delete \${filePath}`,
163
164
  category: "system"
164
165
  },
165
166
  meta_summary_week: {
166
- content: "Summarize the following daily summaries from a single week into a reflective overview (~500-800 words). ${scope_instruction} Identify recurring patterns and themes across days, note growth or evolution in thinking, highlight significant accomplishments and relationships, and flag unresolved threads. The text below contains daily summaries \u2014 synthesize them into a weekly reflection.",
167
+ content: "Distill the following daily summaries from a single week into a reflective overview. ${scope_instruction} Open with a short overview, then use markdown headers (## Heading) for a few themed sections covering recurring patterns, growth in thinking, significant accomplishments and relationships, and any unresolved threads. Prefer omission over completeness \u2014 this is an orientation aid, not a record; the daily summaries hold the detail. Use markdown for structure. Hard cap: 600 words. The text below contains daily summaries \u2014 synthesize, don't concatenate.",
167
168
  description: "System prompt for weekly meta-summaries",
168
169
  variables: ["scope_instruction"],
169
170
  category: "system"
170
171
  },
171
172
  meta_summary_month: {
172
- content: "Summarize the following daily summaries from a single month into a comprehensive narrative (~800-1500 words). ${scope_instruction} Paint the big picture: major milestones and accomplishments, how perspectives or identity evolved, key relationships and interactions, recurring themes, and the overall trajectory. The text below contains daily summaries \u2014 compose them into a monthly narrative.",
173
+ content: "Distill the following daily summaries from a single month into a brief orientation aid. ${scope_instruction} Open with a 3-5 sentence overview, then use markdown headers (## Heading) for a few short themed sections covering major milestones, how perspectives or identity evolved, key relationships, and recurring themes. Prefer omission over completeness \u2014 this is an orientation aid, not a record; the weekly and daily summaries hold the detail. Use markdown for structure. Hard cap: 800 words. The text below contains daily summaries \u2014 synthesize, don't concatenate.",
173
174
  description: "System prompt for monthly meta-summaries",
174
175
  variables: ["scope_instruction"],
175
176
  category: "system"
@@ -229,6 +230,7 @@ async function getMindPromptDefaults() {
229
230
  export {
230
231
  PROMPT_KEYS,
231
232
  ORIENTATION_MARKER,
233
+ MEMORY_PLACEHOLDER_MARKER,
232
234
  PROMPT_DEFAULTS,
233
235
  substitute,
234
236
  getPrompt,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  publish
4
- } from "./chunk-ZNMCCHCP.js";
4
+ } from "./chunk-LTLFOSA4.js";
5
5
  import {
6
6
  logger_default
7
7
  } from "./chunk-T2TP6ZC6.js";