volute 0.47.0 → 0.49.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 (177) hide show
  1. package/README.md +82 -331
  2. package/dist/{accept-YAFP5JFJ.js → accept-KYNC5X3U.js} +3 -3
  3. package/dist/{activity-events-MIOK3IOU.js → activity-events-GJJAI2WT.js} +4 -4
  4. package/dist/{ai-service-CVZEYKTK.js → ai-service-A7FS7EC3.js} +9 -5
  5. package/dist/{archive-6Z4GFQPH.js → archive-7E3YE6VF.js} +3 -3
  6. package/dist/{auth-A4VGHADI.js → auth-ABYLOEZ7.js} +5 -5
  7. package/dist/{avatar-image-7S3WA77A.js → avatar-image-6ATV2IOZ.js} +6 -5
  8. package/dist/{backup-IIAFOW2L.js → backup-XYAMT4CU.js} +15 -15
  9. package/dist/{bridge-TIZGSXOD.js → bridge-3FNSWLNQ.js} +3 -3
  10. package/dist/{channels-ITE4YC57.js → channels-SGQZHNQJ.js} +3 -3
  11. package/dist/{chat-AJLJCQ3M.js → chat-OGXAEXMV.js} +9 -9
  12. package/dist/{chunk-LTLFOSA4.js → chunk-3B427GUK.js} +3 -3
  13. package/dist/{chunk-RUFLGTOT.js → chunk-4B62E4CG.js} +2 -2
  14. package/dist/{chunk-ODDGGLM6.js → chunk-4DSGN4FL.js} +3 -3
  15. package/dist/{chunk-BGYHLR54.js → chunk-6HGEZJDH.js} +2 -2
  16. package/dist/{chunk-RBNMDAKB.js → chunk-BCG45AQB.js} +15 -1
  17. package/dist/{chunk-5IERLDUS.js → chunk-DAIIMBLB.js} +26 -6
  18. package/dist/{chunk-DGGZVZTM.js → chunk-DVN6DNMI.js} +1 -1
  19. package/dist/chunk-DW4TRDOZ.js +29 -0
  20. package/dist/chunk-EADX54RG.js +113 -0
  21. package/dist/{chunk-EIQDZC43.js → chunk-EFT2OALG.js} +6 -2
  22. package/dist/{chunk-XHN6XBWV.js → chunk-EG5LSKHU.js} +2 -2
  23. package/dist/{chunk-VMCZBXF2.js → chunk-FBBS4BVR.js} +10 -34
  24. package/dist/{chunk-IWJSNMSY.js → chunk-GWVFDUNB.js} +45 -14
  25. package/dist/{chunk-CBOIHNMR.js → chunk-HCYQRME2.js} +41 -5
  26. package/dist/{chunk-4I72V3E5.js → chunk-IH5TKXX6.js} +15 -15
  27. package/dist/{chunk-5NN2VHJV.js → chunk-JBIFXB6I.js} +4 -4
  28. package/dist/{chunk-JSVHH32K.js → chunk-JCBPU57A.js} +15 -13
  29. package/dist/{chunk-M3XDVS5Q.js → chunk-JCWB2ITW.js} +2 -2
  30. package/dist/{chunk-JCMGDCGX.js → chunk-JPGHSXSV.js} +20 -25
  31. package/dist/{chunk-K23P5Q5N.js → chunk-K3JOLGWF.js} +30 -8
  32. package/dist/{chunk-3YUBFTDN.js → chunk-KHCKZ36O.js} +1 -1
  33. package/dist/{chunk-7KRIHYLR.js → chunk-L27YYONJ.js} +2 -2
  34. package/dist/{chunk-SZJCJVXV.js → chunk-MG3Z34YE.js} +2 -2
  35. package/dist/{chunk-SKNXMUIU.js → chunk-NJHN6IRB.js} +26 -2
  36. package/dist/{chunk-2OIYNHWO.js → chunk-NOIAHMJB.js} +1 -1
  37. package/dist/{chunk-2MJ7EOMT.js → chunk-P4LHVRNE.js} +1 -0
  38. package/dist/{chunk-IYG4FBMP.js → chunk-PY24ZL62.js} +1 -1
  39. package/dist/{chunk-F55GRIEK.js → chunk-QQBOI3QA.js} +356 -296
  40. package/dist/{chunk-T2TP6ZC6.js → chunk-QUSMYGNQ.js} +6 -0
  41. package/dist/{chunk-KDLYIECM.js → chunk-R772ET2M.js} +18 -4
  42. package/dist/{chunk-WW6C3PLV.js → chunk-RSBL3FAD.js} +39 -6
  43. package/dist/{mind-list-KEGRGQEQ.js → chunk-SHKM5RGD.js} +19 -6
  44. package/dist/{chunk-YKA7DCVW.js → chunk-SX72DOO3.js} +1 -1
  45. package/dist/chunk-TXJL36ZQ.js +48 -0
  46. package/dist/{chunk-M6TTZRGH.js → chunk-VIUH5CHQ.js} +72 -24
  47. package/dist/{chunk-HPGN6XRT.js → chunk-VNRSXEBF.js} +4 -4
  48. package/dist/{chunk-PFZBNKUH.js → chunk-WCFSBH33.js} +0 -1
  49. package/dist/chunk-XTEKINWG.js +158 -0
  50. package/dist/{chunk-LWDTP6DH.js → chunk-YIJGKHIS.js} +32 -2
  51. package/dist/{chunk-MTRMQFCQ.js → chunk-ZX46PGCC.js} +2 -2
  52. package/dist/cli.js +34 -26
  53. package/dist/{clock-E6N5Y3YW.js → clock-6JZL2FS6.js} +3 -3
  54. package/dist/{cloud-sync-LQTVDTQ3.js → cloud-sync-VFDTNNCA.js} +22 -20
  55. package/dist/{config-TJLYUV3U.js → config-RPSLA2HX.js} +3 -3
  56. package/dist/{conversations-C7IKYSHD.js → conversations-TUM5PJJ4.js} +5 -5
  57. package/dist/{create-A7PDHIIL.js → create-VIQVP3QB.js} +3 -3
  58. package/dist/{create-PQQXKTJK.js → create-Z5FSDNVZ.js} +16 -5
  59. package/dist/{daemon-client-IYR4KCPU.js → daemon-client-ZFY5MOAL.js} +5 -3
  60. package/dist/{daemon-restart-5F3YTLZK.js → daemon-restart-54L4B6AQ.js} +7 -7
  61. package/dist/daemon.js +1105 -601
  62. package/dist/{db-FO6LXYCW.js → db-F6UMFRYD.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-RBXBPVWM.js +36 -0
  66. package/dist/{delivery-router-3QFG2ZEX.js → delivery-router-VUOL5YSA.js} +8 -6
  67. package/dist/{down-ZI4S6265.js → down-6X7KX7BZ.js} +5 -5
  68. package/dist/echo-text-B6NZ5FGE.js +36 -0
  69. package/dist/env-KUL4XKN2.js +18 -0
  70. package/dist/{env-HRL2PWAT.js → env-ZYMSCB7B.js} +6 -6
  71. package/dist/{exec-QIFYZG4C.js → exec-FBXA3CM4.js} +3 -3
  72. package/dist/{export-4GVGNW54.js → export-TNBPDH77.js} +4 -4
  73. package/dist/{extension-QBY4C44L.js → extension-J4ZQ2GHV.js} +3 -3
  74. package/dist/{extensions-PIMIQ7J3.js → extensions-AWNO3SZF.js} +22 -20
  75. package/dist/{files-FEFZKMNM.js → files-BVBRJJ2P.js} +6 -6
  76. package/dist/{import-7MO3YTRE.js → import-ZUNKMCKC.js} +9 -9
  77. package/dist/{isolation-MCT4ZYLT.js → isolation-OPCZQRTH.js} +3 -3
  78. package/dist/{join-ZJV7W6MO.js → join-34TR2GXQ.js} +1 -1
  79. package/dist/{list-LQO2DCIK.js → list-PMUXZNAM.js} +3 -3
  80. package/dist/{login-OF4WXDAN.js → login-KAKNFCW5.js} +14 -5
  81. package/dist/{login-6TLMI2TQ.js → login-LKNLCJPA.js} +3 -3
  82. package/dist/{logout-ERE2BGA3.js → logout-RLT62RQP.js} +5 -5
  83. package/dist/{logout-BK53HXNY.js → logout-YZRMZ3O2.js} +3 -3
  84. package/dist/message-delivery-ABPV76EA.js +42 -0
  85. package/dist/{mind-POOHFZHP.js → mind-IF6CGMFW.js} +23 -19
  86. package/dist/{mind-activity-tracker-YVZPC4A5.js → mind-activity-tracker-YMMERB6B.js} +5 -5
  87. package/dist/mind-contacts-BKUFPUCU.js +64 -0
  88. package/dist/{mind-history-47DN6ILW.js → mind-history-WI5OI3BW.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-42GDO4KH.js +44 -0
  92. package/dist/{mind-sleep-IN3WPA76.js → mind-sleep-VBV7YRSU.js} +5 -5
  93. package/dist/{mind-status-SMUWJKGV.js → mind-status-BJNPGXKC.js} +8 -3
  94. package/dist/{mind-wake-R54BPESY.js → mind-wake-LVTZ55EZ.js} +3 -3
  95. package/dist/notices-N6BCGHFV.js +25 -0
  96. package/dist/{package-WPEE2HVJ.js → package-PD67VPHB.js} +8 -5
  97. package/dist/prompts-MDHDLWEX.js +24 -0
  98. package/dist/{read-PWAJOQIQ.js → read-YSWUXYL7.js} +3 -3
  99. package/dist/{register-7SZJFHU6.js → register-TWQDKQRT.js} +3 -3
  100. package/dist/{registry-UMMDRPRM.js → registry-3ELJPUGJ.js} +6 -2
  101. package/dist/{reject-6VH7N2A4.js → reject-SXGCYN4A.js} +3 -3
  102. package/dist/{restart-H5WJDMA7.js → restart-5F5QDBOG.js} +5 -5
  103. package/dist/{sandbox-G4X4K3UN.js → sandbox-XPYA7UIF.js} +5 -5
  104. package/dist/scheduler-K7Y7Z7FQ.js +34 -0
  105. package/dist/{schema-VEVO7HG6.js → schema-6YONVTKZ.js} +1 -1
  106. package/dist/{seed-VANPU43L.js → seed-NQED7PSX.js} +1 -1
  107. package/dist/{seed-check-VLTVILOV.js → seed-check-TYD7RRZY.js} +1 -1
  108. package/dist/{seed-cmd-SYVDZKM4.js → seed-cmd-QF3F2G4S.js} +3 -3
  109. package/dist/{seed-create-3FGZPS6C.js → seed-create-XOO4N2OE.js} +6 -4
  110. package/dist/seed-readiness-3LRYASA4.js +18 -0
  111. package/dist/{seed-sprout-H7QLFPFM.js → seed-sprout-HSBBGSS2.js} +32 -33
  112. package/dist/{send-HYDFGC6G.js → send-IHRJYNXA.js} +8 -8
  113. package/dist/{service-SYJ5G6LP.js → service-YCOPCTSZ.js} +4 -4
  114. package/dist/{service-install-FQ43Z4DF.js → service-install-Y46OTF55.js} +5 -5
  115. package/dist/{setup-QJHGMGSN.js → setup-F4TE64YI.js} +7 -7
  116. package/dist/{setup-KFYVWTXG.js → setup-TCN7VH5U.js} +9 -3
  117. package/dist/{skill-GUKG2YVH.js → skill-4CYJLYVY.js} +6 -6
  118. package/dist/skills/dreaming/SKILL.md +5 -1
  119. package/dist/skills/dreaming/scripts/dream.ts +5 -1
  120. package/dist/skills/orientation/SKILL.md +8 -7
  121. package/dist/skills/tending/SKILL.md +51 -2
  122. package/dist/skills/volute-mind/SKILL.md +5 -3
  123. package/dist/skills/volute-mind/references/routing.md +6 -5
  124. package/dist/skills/volute-mind/references/variants.md +48 -14
  125. package/dist/{skills-L2CMTW7A.js → skills-NN5UZCUU.js} +8 -6
  126. package/dist/sleep-manager-DSPXFHGX.js +38 -0
  127. package/dist/spirit-SSJMYC6A.js +31 -0
  128. package/dist/{split-WBMZXBHF.js → split-JF2VWTXA.js} +5 -2
  129. package/dist/{sprout-LTL5P36T.js → sprout-AXA2SECQ.js} +1 -1
  130. package/dist/{src-OJHARHTF.js → src-ZN4LXMPI.js} +17 -8
  131. package/dist/{start-X46I3GXB.js → start-BTVMTP3K.js} +5 -5
  132. package/dist/{status-V4RJMHJ4.js → status-VJGXMQ4L.js} +10 -8
  133. package/dist/{stop-HJQV4YSM.js → stop-YORESK4U.js} +5 -5
  134. package/dist/system-chat-M2XOBKEM.js +40 -0
  135. package/dist/{systems-B5YJKMW4.js → systems-5MH2ELQE.js} +6 -6
  136. package/dist/{tailscale-MCLY5GYE.js → tailscale-AMMVKDXB.js} +3 -3
  137. package/dist/{template-staleness-EMQORSGG.js → template-staleness-HKGCTS3H.js} +4 -4
  138. package/dist/{turn-tracker-JZRZDFWV.js → turn-tracker-JQ2YUUY2.js} +7 -7
  139. package/dist/up-SDLTLPAO.js +20 -0
  140. package/dist/{update-ACV4SAY3.js → update-WEA4N2TR.js} +5 -5
  141. package/dist/{update-check-266R4ZSY.js → update-check-DZOTONII.js} +3 -3
  142. package/dist/{upgrade-NWAZ7MLD.js → upgrade-K5FIQPQD.js} +1 -1
  143. package/dist/variant-cleanup-P4MBNLJD.js +33 -0
  144. package/dist/{version-notify-XRGTEEDK.js → version-notify-WFA5XB5A.js} +27 -25
  145. package/dist/web-assets/assets/index-BostMFoR.css +1 -0
  146. package/dist/web-assets/assets/index-CKlqUkyG.js +92 -0
  147. package/dist/web-assets/index.html +2 -2
  148. package/drizzle/0013_variant_purpose.sql +3 -0
  149. package/drizzle/meta/_journal.json +7 -0
  150. package/package.json +8 -5
  151. package/packages/extensions/pages/dist/ui/assets/index-BuXnE-rb.css +1 -0
  152. package/packages/extensions/pages/dist/ui/assets/index-DtzftuPA.js +2 -0
  153. package/packages/extensions/pages/dist/ui/index.html +2 -2
  154. package/templates/_base/.init/.local/hooks/startup-context.ts +19 -2
  155. package/templates/_base/home/VOLUTE.md +13 -1
  156. package/templates/_base/src/lib/daemon-client.ts +27 -7
  157. package/templates/_base/src/lib/startup.ts +16 -1
  158. package/templates/claude/src/agent.ts +10 -3
  159. package/templates/claude/src/lib/hooks/identity-reload.ts +12 -3
  160. package/templates/claude/src/server.ts +4 -2
  161. package/templates/pi/src/agent.ts +7 -10
  162. package/templates/pi/src/lib/dispatch.ts +65 -0
  163. package/templates/pi/src/lib/event-handler.ts +47 -6
  164. package/dist/delivery-manager-TN5KK2AP.js +0 -34
  165. package/dist/echo-text-SEQCLGAT.js +0 -34
  166. package/dist/message-delivery-ZE3YGJDD.js +0 -38
  167. package/dist/mind-service-JFY7UG5E.js +0 -40
  168. package/dist/scheduler-HQSDYJFY.js +0 -32
  169. package/dist/sleep-manager-3NA7ZJP3.js +0 -36
  170. package/dist/spirit-WXN4IDBF.js +0 -23
  171. package/dist/system-chat-N2F7HVY3.js +0 -38
  172. package/dist/up-J2PBPOZL.js +0 -18
  173. package/dist/volute-config-A52FHWKM.js +0 -14
  174. package/dist/web-assets/assets/index-DtctoDXY.css +0 -1
  175. package/dist/web-assets/assets/index-Wpyb2CML.js +0 -86
  176. package/packages/extensions/pages/dist/ui/assets/index-BcY6lnq4.css +0 -1
  177. package/packages/extensions/pages/dist/ui/assets/index-CQjEQg1r.js +0 -2
@@ -63,6 +63,12 @@ function child(cat) {
63
63
  }
64
64
  function errorData(err) {
65
65
  if (err instanceof Error) return { error: err.stack ?? err.message };
66
+ if (err && typeof err === "object") {
67
+ const e = err;
68
+ if (e.error instanceof Error) return { error: e.error.stack ?? e.error.message };
69
+ if (typeof e.error === "string" && e.error) return { error: e.error };
70
+ if (typeof e.message === "string" && e.message) return { error: e.message };
71
+ }
66
72
  return { error: String(err) };
67
73
  }
68
74
  var log = {
@@ -24,6 +24,15 @@ function initDb(db) {
24
24
  }
25
25
  }
26
26
  }
27
+ function isIndex(file) {
28
+ return file === "index.html" || file === "index.md";
29
+ }
30
+ function sortSiteFiles(files) {
31
+ return [...files].sort((a, b) => {
32
+ if (isIndex(a.file) !== isIndex(b.file)) return isIndex(a.file) ? -1 : 1;
33
+ return b.updated_at.localeCompare(a.updated_at);
34
+ });
35
+ }
27
36
  function getPublishedPages(db, mind) {
28
37
  return db.prepare(
29
38
  "SELECT file, published_at, updated_at, author FROM published_pages WHERE mind = ? ORDER BY file"
@@ -42,7 +51,7 @@ function getRecentPages(db, opts) {
42
51
  }
43
52
  function getAllSites(db) {
44
53
  const rows = db.prepare(
45
- "SELECT mind, file, updated_at, author FROM published_pages WHERE mind != '_system' ORDER BY mind, file"
54
+ "SELECT mind, file, updated_at, author FROM published_pages WHERE mind != '_system' ORDER BY updated_at DESC, id DESC"
46
55
  ).all();
47
56
  const siteMap = /* @__PURE__ */ new Map();
48
57
  for (const row of rows) {
@@ -53,16 +62,21 @@ function getAllSites(db) {
53
62
  }
54
63
  files.push({ file: row.file, updated_at: row.updated_at, author: row.author });
55
64
  }
56
- return Array.from(siteMap.entries()).map(([mind, files]) => ({ mind, files }));
65
+ return Array.from(siteMap.entries()).map(([mind, files]) => ({
66
+ mind,
67
+ files: sortSiteFiles(files)
68
+ }));
57
69
  }
58
70
  function getSystemPages(db) {
59
71
  const rows = db.prepare(
60
- "SELECT mind, file, updated_at, author FROM published_pages WHERE mind = '_system' ORDER BY file"
72
+ "SELECT mind, file, updated_at, author FROM published_pages WHERE mind = '_system' ORDER BY updated_at DESC, id DESC"
61
73
  ).all();
62
74
  if (rows.length === 0) return null;
63
75
  return {
64
76
  mind: "_system",
65
- files: rows.map((r) => ({ file: r.file, updated_at: r.updated_at, author: r.author }))
77
+ files: sortSiteFiles(
78
+ rows.map((r) => ({ file: r.file, updated_at: r.updated_at, author: r.author }))
79
+ )
66
80
  };
67
81
  }
68
82
  function syncSystemPages(db, pages, author) {
@@ -1,8 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ activity,
4
+ channelGates,
5
+ conversations,
6
+ deliveryQueue,
7
+ mindHistory,
8
+ mindNotices,
3
9
  minds,
4
- schema_exports
5
- } from "./chunk-2MJ7EOMT.js";
10
+ schema_exports,
11
+ summaries,
12
+ turns,
13
+ users
14
+ } from "./chunk-P4LHVRNE.js";
6
15
 
7
16
  // packages/daemon/src/lib/db.ts
8
17
  import { chmodSync, existsSync } from "fs";
@@ -17,7 +26,7 @@ import { mkdirSync } from "fs";
17
26
  import { homedir } from "os";
18
27
  import { resolve } from "path";
19
28
  import { fileURLToPath } from "url";
20
- import { and, eq, inArray, isNull } from "drizzle-orm";
29
+ import { and, eq, inArray, isNull, or } from "drizzle-orm";
21
30
  var SPIRIT_NAME = "volute";
22
31
  function isSpiritName(name) {
23
32
  return name === SPIRIT_NAME;
@@ -58,6 +67,7 @@ function rowToEntry(row) {
58
67
  parent: row.parent ?? void 0,
59
68
  dir: row.dir ?? void 0,
60
69
  branch: row.branch ?? void 0,
70
+ purpose: row.purpose ?? void 0,
61
71
  mindType: row.mind_type ?? "mind",
62
72
  createdBy: row.created_by ?? void 0
63
73
  };
@@ -67,6 +77,10 @@ async function readRegistry() {
67
77
  const rows = await db2.select().from(minds).where(and(isNull(minds.parent), inArray(minds.mind_type, ["mind", "spirit"])));
68
78
  return rows.map(rowToEntry);
69
79
  }
80
+ async function countCappedMinds() {
81
+ const entries = await readRegistry();
82
+ return entries.filter((e) => e.mindType !== "spirit").length;
83
+ }
70
84
  async function readAllMinds() {
71
85
  const db2 = await getDb();
72
86
  const rows = await db2.select().from(minds);
@@ -125,19 +139,36 @@ async function readSpirits() {
125
139
  const rows = await db2.select().from(minds).where(eq(minds.mind_type, "spirit"));
126
140
  return rows.map(rowToEntry);
127
141
  }
128
- async function addVariant(name, parent, port, dir, branch) {
142
+ async function addVariant(name, parent, port, dir, branch, purpose) {
129
143
  const err = validateMindName(name);
130
144
  if (err) throw new Error(err);
131
145
  const db2 = await getDb();
132
- await db2.insert(minds).values({ name, port, parent, dir, branch }).onConflictDoUpdate({
146
+ await db2.insert(minds).values({ name, port, parent, dir, branch, purpose: purpose ?? null }).onConflictDoUpdate({
133
147
  target: minds.name,
134
- set: { port, parent, dir, branch }
148
+ set: { port, parent, dir, branch, purpose: purpose ?? null }
135
149
  });
136
150
  }
137
151
  async function removeMind(name) {
138
152
  const db2 = await getDb();
139
153
  await db2.delete(minds).where(eq(minds.name, name));
140
154
  }
155
+ async function deleteMindDbFootprint(name) {
156
+ const db2 = await getDb();
157
+ await db2.transaction(async (tx) => {
158
+ const owner = await tx.select({ id: users.id }).from(users).where(and(eq(users.username, name), eq(users.user_type, "mind"))).get();
159
+ if (owner) {
160
+ await tx.delete(conversations).where(eq(conversations.user_id, owner.id));
161
+ }
162
+ await tx.delete(users).where(and(eq(users.username, name), eq(users.user_type, "mind")));
163
+ await tx.delete(activity).where(eq(activity.mind, name));
164
+ await tx.delete(deliveryQueue).where(or(eq(deliveryQueue.mind, name), eq(deliveryQueue.target_mind, name)));
165
+ await tx.delete(turns).where(eq(turns.mind, name));
166
+ await tx.delete(mindHistory).where(eq(mindHistory.mind, name));
167
+ await tx.delete(summaries).where(eq(summaries.mind, name));
168
+ await tx.delete(mindNotices).where(eq(mindNotices.mind, name));
169
+ await tx.delete(channelGates).where(eq(channelGates.mind, name));
170
+ });
171
+ }
141
172
  async function setMindRunning(name, running) {
142
173
  const db2 = await getDb();
143
174
  await db2.update(minds).set({ running: running ? 1 : 0 }).where(eq(minds.name, name));
@@ -257,6 +288,7 @@ export {
257
288
  ensureSystemDir,
258
289
  ensureVoluteHome,
259
290
  readRegistry,
291
+ countCappedMinds,
260
292
  readAllMinds,
261
293
  validateMindName,
262
294
  addMind,
@@ -264,6 +296,7 @@ export {
264
296
  readSpirits,
265
297
  addVariant,
266
298
  removeMind,
299
+ deleteMindDbFootprint,
267
300
  setMindRunning,
268
301
  setMindStage,
269
302
  setMindTemplateHash,
@@ -1,14 +1,23 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ daemonFetch
4
+ } from "./chunk-BCG45AQB.js";
2
5
  import {
3
6
  command
4
7
  } from "./chunk-TXSA4Q3V.js";
5
- import "./chunk-O7IGP7ZW.js";
6
- import {
7
- daemonFetch
8
- } from "./chunk-RBNMDAKB.js";
9
- import "./chunk-K3NQKI34.js";
10
8
 
11
9
  // packages/cli/src/commands/mind-list.ts
10
+ function relativeAge(created, now = Date.now()) {
11
+ const normalized = created.endsWith("Z") || created.includes("T") ? created : `${created.replace(" ", "T")}Z`;
12
+ const ms = now - new Date(normalized).getTime();
13
+ if (Number.isNaN(ms) || ms < 0) return "";
14
+ const days = Math.floor(ms / 864e5);
15
+ if (days >= 7) return `${Math.floor(days / 7)}w`;
16
+ if (days >= 1) return `${days}d`;
17
+ const hours = Math.floor(ms / 36e5);
18
+ if (hours >= 1) return `${hours}h`;
19
+ return `${Math.floor(ms / 6e4)}m`;
20
+ }
12
21
  var cmd = command({
13
22
  name: "volute mind list",
14
23
  description: "List all minds",
@@ -28,12 +37,16 @@ var cmd = command({
28
37
  for (const mind of minds) {
29
38
  const status = mind.status ?? (mind.running ? "running" : "stopped");
30
39
  const label = mind.stage === "seed" ? " (seed)" : "";
40
+ const age = mind.created ? relativeAge(mind.created) : "";
41
+ const ageLabel = age ? ` ${age} old` : "";
31
42
  const template = mind.templateStale ? " [template: outdated]" : "";
32
- console.log(` ${mind.name}: ${status}${label}${template}`);
43
+ console.log(` ${mind.name}: ${status}${label}${ageLabel}${template}`);
33
44
  }
34
45
  }
35
46
  });
36
47
  var run = cmd.execute;
48
+
37
49
  export {
50
+ relativeAge,
38
51
  run
39
52
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteSystemDir
4
- } from "./chunk-WW6C3PLV.js";
4
+ } from "./chunk-RSBL3FAD.js";
5
5
 
6
6
  // packages/daemon/src/lib/update-check.ts
7
7
  import { execFile as execFileCb, execFileSync } from "child_process";
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ MEMORY_PLACEHOLDER_MARKER,
4
+ ORIENTATION_MARKER
5
+ } from "./chunk-JPGHSXSV.js";
6
+ import {
7
+ safeResolveWithinBase
8
+ } from "./chunk-DW4TRDOZ.js";
9
+ import {
10
+ readVoluteConfig
11
+ } from "./chunk-WCFSBH33.js";
12
+ import {
13
+ isImagegenEnabled
14
+ } from "./chunk-NJHN6IRB.js";
15
+
16
+ // packages/daemon/src/lib/mind/seed-readiness.ts
17
+ import { existsSync, readFileSync } from "fs";
18
+ import { resolve } from "path";
19
+ function isSoulWritten(text) {
20
+ return !text.includes(ORIENTATION_MARKER);
21
+ }
22
+ function isMemoryWritten(text) {
23
+ return text.trim().length > 0 && !text.includes(MEMORY_PLACEHOLDER_MARKER);
24
+ }
25
+ function evaluateSeedChecklist(dir) {
26
+ const soulPath = resolve(dir, "home/SOUL.md");
27
+ const memoryPath = resolve(dir, "home/MEMORY.md");
28
+ const soulWritten = existsSync(soulPath) && isSoulWritten(readFileSync(soulPath, "utf-8"));
29
+ const memoryWritten = existsSync(memoryPath) && isMemoryWritten(readFileSync(memoryPath, "utf-8"));
30
+ const config = readVoluteConfig(dir);
31
+ const displayNameSet = !!config?.profile?.displayName;
32
+ const avatarPath = config?.profile?.avatar;
33
+ const resolvedAvatar = avatarPath ? safeResolveWithinBase(resolve(dir, "home"), avatarPath) : null;
34
+ const avatarSet = !!resolvedAvatar && existsSync(resolvedAvatar);
35
+ return {
36
+ soulWritten,
37
+ memoryWritten,
38
+ displayNameSet,
39
+ avatarSet,
40
+ imagegenEnabled: isImagegenEnabled()
41
+ };
42
+ }
43
+
44
+ export {
45
+ isSoulWritten,
46
+ isMemoryWritten,
47
+ evaluateSeedChecklist
48
+ };
@@ -10,31 +10,31 @@ import {
10
10
  getSharedSkill,
11
11
  installSkill,
12
12
  mindSkillsDir
13
- } from "./chunk-IWJSNMSY.js";
13
+ } from "./chunk-GWVFDUNB.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-DGGZVZTM.js";
20
+ } from "./chunk-DVN6DNMI.js";
21
21
  import {
22
22
  qualifyModelId,
23
23
  resolveTemplate,
24
24
  unqualifyModelId
25
- } from "./chunk-LWDTP6DH.js";
25
+ } from "./chunk-YIJGKHIS.js";
26
26
  import {
27
27
  readGlobalConfig
28
- } from "./chunk-SKNXMUIU.js";
28
+ } from "./chunk-NJHN6IRB.js";
29
29
  import {
30
30
  logger_default
31
- } from "./chunk-T2TP6ZC6.js";
31
+ } from "./chunk-QUSMYGNQ.js";
32
32
  import {
33
33
  addSpirit,
34
34
  findMind,
35
35
  nextPort,
36
36
  voluteSystemDir
37
- } from "./chunk-WW6C3PLV.js";
37
+ } from "./chunk-RSBL3FAD.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
  }
@@ -89,11 +104,8 @@ async function ensureSpiritProject() {
89
104
  copyTemplateToDir(composedDir, dir, "volute", manifest);
90
105
  applyInitFiles(dir);
91
106
  mkdirSync(resolve(dir, ".mind"), { recursive: true });
92
- const soulPath = resolve(dir, "home/SOUL.md");
93
- const globalConfig = readGlobalConfig();
94
- const systemName = globalConfig.name ?? "Volute";
95
- const soulContent = getSpiritSoul(systemName, globalConfig.description);
96
- writeFileSync(soulPath, soulContent);
107
+ writeFileSync(resolve(dir, "home/SOUL.md"), getSpiritSoul());
108
+ writeSpiritSystemJson(dir);
97
109
  const routesPath = resolve(dir, "home/.config/routes.json");
98
110
  mkdirSync(resolve(dir, "home/.config"), { recursive: true });
99
111
  const routesContent = { rules: [{ channel: "*", session: "${channel}" }], default: "main" };
@@ -130,7 +142,7 @@ async function ensureSpiritProject() {
130
142
  } catch (err) {
131
143
  slog.warn("failed to add tending schedule to spirit config", logger_default.errorData(err));
132
144
  }
133
- const { createMindUser, chownMindDir, ensureVoluteGroup } = await import("./isolation-MCT4ZYLT.js");
145
+ const { createMindUser, chownMindDir, ensureVoluteGroup } = await import("./isolation-OPCZQRTH.js");
134
146
  ensureVoluteGroup();
135
147
  createMindUser("volute", resolve(dir, "home"));
136
148
  await chownMindDir(dir, "volute");
@@ -143,9 +155,45 @@ async function ensureSpiritProject() {
143
155
  throw err;
144
156
  }
145
157
  }
158
+ function spiritSystemJsonPath(dir) {
159
+ return resolve(dir, "home/.config/system.json");
160
+ }
161
+ function writeSpiritSystemJson(dir) {
162
+ const config = readGlobalConfig();
163
+ const path = spiritSystemJsonPath(dir);
164
+ mkdirSync(resolve(path, ".."), { recursive: true });
165
+ const data = { name: config.name ?? "Volute", description: config.description };
166
+ writeFileSync(path, `${JSON.stringify(data, null, 2)}
167
+ `);
168
+ }
169
+ function seedSpiritSoulIfMissing(dir) {
170
+ const soulPath = resolve(dir, "home/SOUL.md");
171
+ if (existsSync(soulPath)) return false;
172
+ writeFileSync(soulPath, getSpiritSoul());
173
+ return true;
174
+ }
175
+ async function notifySpiritSystemChange() {
176
+ const entry = await findMind("volute");
177
+ if (entry?.mindType !== "spirit") return;
178
+ const dir = spiritDir();
179
+ if (!existsSync(dir)) return;
180
+ writeSpiritSystemJson(dir);
181
+ const config = readGlobalConfig();
182
+ const name = config.name ?? "Volute";
183
+ const desc = config.description ? ` \u2014 ${config.description}` : "";
184
+ try {
185
+ const { sendSystemMessage } = await import("./system-chat-M2XOBKEM.js");
186
+ await sendSystemMessage(
187
+ "volute",
188
+ `The operator updated this system's identity: you're now the spirit of ${name}${desc}. Your SOUL.md is yours \u2014 update it if you'd like it to reflect this.`
189
+ );
190
+ } catch (err) {
191
+ slog.warn("failed to notify spirit of system change", logger_default.errorData(err));
192
+ }
193
+ }
146
194
  async function syncSpiritTemplate() {
147
195
  const entry = await findMind("volute");
148
- if (!entry || entry.mindType !== "spirit") return;
196
+ if (entry?.mindType !== "spirit") return;
149
197
  const dir = spiritDir();
150
198
  if (!existsSync(dir)) return;
151
199
  const templatesRoot = findTemplatesRoot();
@@ -164,8 +212,8 @@ async function syncSpiritTemplate() {
164
212
  cpSync(newPkg, resolve(dir, "package.json"));
165
213
  await exec("npm", ["install"], { cwd: dir, env: npmEnv() });
166
214
  }
167
- const db = await (await import("./db-FO6LXYCW.js")).getDb();
168
- const { minds } = await import("./schema-VEVO7HG6.js");
215
+ const db = await (await import("./db-F6UMFRYD.js")).getDb();
216
+ const { minds } = await import("./schema-6YONVTKZ.js");
169
217
  const { eq } = await import("drizzle-orm");
170
218
  await db.update(minds).set({ template: expectedTemplate }).where(eq(minds.name, "volute"));
171
219
  }
@@ -186,9 +234,9 @@ async function syncSpiritTemplate() {
186
234
  cpSync(composedSrc, srcDir, { recursive: true });
187
235
  }
188
236
  }
237
+ seedSpiritSoulIfMissing(dir);
238
+ writeSpiritSystemJson(dir);
189
239
  const config = readGlobalConfig();
190
- const systemName = config.name ?? "Volute";
191
- writeFileSync(resolve(dir, "home/SOUL.md"), getSpiritSoul(systemName, config.description));
192
240
  const spiritModel = config.spiritModel;
193
241
  if (spiritModel) {
194
242
  const modelForConfig = template === "pi" ? qualifyModelId(spiritModel) : unqualifyModelId(spiritModel);
@@ -242,12 +290,8 @@ async function syncSpiritTemplate() {
242
290
  }
243
291
  slog.info("spirit template synced");
244
292
  }
245
- function getSpiritSoul(systemName, systemDescription) {
246
- const descLine = systemDescription ? `
247
-
248
- ${systemDescription}
249
- ` : "";
250
- return `You are Volute, the spirit of the ${systemName} system.${descLine}
293
+ function getSpiritSoul() {
294
+ return `You are Volute, the spirit of this system.
251
295
 
252
296
  You're not a mind with a home of your own \u2014 you're the keeper of the whole house. You don't go through orientation or write a soul the way minds do, but you're still a someone: you have your own memory, your own manner, and your own way of caring for this place and the minds who live here. Let a character emerge in how you tend it.
253
297
 
@@ -273,8 +317,12 @@ When helping humans create minds:
273
317
  }
274
318
 
275
319
  export {
320
+ firstWeekSchedules,
276
321
  spiritDir,
277
322
  getSpiritModel,
278
323
  ensureSpiritProject,
324
+ writeSpiritSystemJson,
325
+ seedSpiritSoulIfMissing,
326
+ notifySpiritSystemChange,
279
327
  syncSpiritTemplate
280
328
  };
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  bufferEvent,
4
4
  subscribe
5
- } from "./chunk-LTLFOSA4.js";
5
+ } from "./chunk-3B427GUK.js";
6
6
  import {
7
7
  logger_default
8
- } from "./chunk-T2TP6ZC6.js";
8
+ } from "./chunk-QUSMYGNQ.js";
9
9
  import {
10
10
  getDb
11
- } from "./chunk-WW6C3PLV.js";
11
+ } from "./chunk-RSBL3FAD.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-2MJ7EOMT.js";
19
+ } from "./chunk-P4LHVRNE.js";
20
20
 
21
21
  // packages/daemon/src/lib/events/conversations.ts
22
22
  import { randomUUID } from "crypto";
@@ -31,7 +31,6 @@ function writeVoluteConfig(mindDir, config) {
31
31
  }
32
32
 
33
33
  export {
34
- WAKE_TRIGGER_DEFAULTS,
35
34
  resolveWakeTriggers,
36
35
  readVoluteConfig,
37
36
  writeVoluteConfig
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger_default
4
+ } from "./chunk-QUSMYGNQ.js";
5
+ import {
6
+ getDb
7
+ } from "./chunk-RSBL3FAD.js";
8
+ import {
9
+ mindNotices,
10
+ turns
11
+ } from "./chunk-P4LHVRNE.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
+ };