volute 0.52.0 → 0.54.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 (153) hide show
  1. package/dist/{accept-5NK5XJB5.js → accept-RSDIVJL6.js} +3 -0
  2. package/dist/auto-upgrade-HEVZKPTF.js +46 -0
  3. package/dist/{backup-YVGV2ZNM.js → backup-QRNX6RK7.js} +10 -10
  4. package/dist/channels-CKCY64TD.js +203 -0
  5. package/dist/{chat-VAZ25KVT.js → chat-ZUGNL5GS.js} +5 -5
  6. package/dist/chunk-3LHUXY3E.js +102 -0
  7. package/dist/{chunk-SW2U6HRB.js → chunk-444RR3YF.js} +21 -4
  8. package/dist/{chunk-YVR6EHWL.js → chunk-56IT2ZXF.js} +160 -18
  9. package/dist/chunk-5RBEEAKW.js +58 -0
  10. package/dist/{chunk-OXWUEU5V.js → chunk-5U5GLZSD.js} +2 -2
  11. package/dist/{chunk-G7A5LEVI.js → chunk-7XKPG66D.js} +5 -5
  12. package/dist/{chunk-R772ET2M.js → chunk-AAPNU7GA.js} +4 -0
  13. package/dist/{chunk-XRWUQPCA.js → chunk-ATZF2RNJ.js} +25 -3
  14. package/dist/{chunk-6NLW72HP.js → chunk-CKPAVQNK.js} +1 -1
  15. package/dist/{chunk-JHKBALS2.js → chunk-CLCHOXLR.js} +1 -1
  16. package/dist/{chunk-DMK5ASKV.js → chunk-F6D766VQ.js} +6 -2
  17. package/dist/{chunk-AXFLY5GU.js → chunk-HKGYZVWN.js} +16 -1
  18. package/dist/{chunk-ZEZQNP2G.js → chunk-LGXVGIWN.js} +103 -25
  19. package/dist/{chunk-CW7BQKF6.js → chunk-LKUGUQUW.js} +3 -3
  20. package/dist/{chunk-FXWBFDQR.js → chunk-OAYY3WF3.js} +59 -12
  21. package/dist/{chunk-ITK6HV47.js → chunk-OGPVJ53N.js} +14 -6
  22. package/dist/{chunk-M77B73VV.js → chunk-OIOW3FPT.js} +7 -2
  23. package/dist/chunk-OT5SXF7L.js +904 -0
  24. package/dist/{chunk-EDR47UE2.js → chunk-OYF25B62.js} +1 -1
  25. package/dist/{chunk-RPPTGPYF.js → chunk-PPSN2MLC.js} +2 -2
  26. package/dist/{chunk-K2F237KS.js → chunk-RGHF3OK5.js} +154 -18
  27. package/dist/{chunk-RU74YG46.js → chunk-VDUCLVZI.js} +338 -30
  28. package/dist/{chunk-724DVYHU.js → chunk-WHL6PKYP.js} +10 -10
  29. package/dist/{chunk-EEOMA4DX.js → chunk-WZZCA23D.js} +2 -1
  30. package/dist/{chunk-UDZLPR34.js → chunk-YSRYCS3N.js} +13 -8
  31. package/dist/{chunk-5N32EN4O.js → chunk-Z4Q6TQWL.js} +1 -1
  32. package/dist/{chunk-VPOMJMV6.js → chunk-ZNHL5KQJ.js} +1 -1
  33. package/dist/cli.js +17 -17
  34. package/dist/{clock-ZT5YT6HA.js → clock-X7V3C7NL.js} +2 -2
  35. package/dist/{cloud-sync-H7EWZASD.js → cloud-sync-52YNJRMR.js} +12 -11
  36. package/dist/{daemon-restart-4FWUA5JF.js → daemon-restart-QWHN5VMU.js} +4 -4
  37. package/dist/daemon.js +538 -782
  38. package/dist/{db-IPXRJUI7.js → db-I4EH5SLF.js} +3 -1
  39. package/dist/{delivery-manager-VYXVKUMP.js → delivery-manager-7R2YIQ2W.js} +13 -7
  40. package/dist/delivery-notices-RZQ6GONP.js +18 -0
  41. package/dist/{delivery-router-RHANNTAC.js → delivery-router-QS4NLJR6.js} +3 -1
  42. package/dist/{down-QSNEMTTX.js → down-UN2EDX2Y.js} +3 -3
  43. package/dist/{echo-text-PEEV7FQU.js → echo-text-73CYQRL7.js} +14 -13
  44. package/dist/{exec-ABD3DYA2.js → exec-Z34TTUO3.js} +1 -1
  45. package/dist/{extensions-N6L5NYTK.js → extensions-QT65JGV6.js} +22 -11
  46. package/dist/{files-BRY5RRIM.js → files-PCKBQHAY.js} +2 -1
  47. package/dist/{isolation-GVV2IBAU.js → isolation-UQPDO34R.js} +1 -1
  48. package/dist/{message-delivery-7URC52ME.js → message-delivery-TRKT4T2P.js} +16 -13
  49. package/dist/migrate-name-placeholder-C7NCNLWF.js +37 -0
  50. package/dist/{mind-NEXGT3KE.js → mind-CT6ANXK4.js} +11 -11
  51. package/dist/{mind-contacts-QYU4TPEA.js → mind-contacts-PEXCDRXA.js} +6 -6
  52. package/dist/{mind-history-OGNR4L7W.js → mind-history-RZMMCHMX.js} +83 -11
  53. package/dist/{mind-list-SYRA7CH5.js → mind-list-7KBLPLES.js} +1 -1
  54. package/dist/{mind-service-E26ZQXDS.js → mind-service-7EFIJ3ZA.js} +12 -11
  55. package/dist/{mind-sleep-LZAFBDZD.js → mind-sleep-24HV4RCA.js} +2 -2
  56. package/dist/{mind-status-Z2LQFA6F.js → mind-status-4YW4PHMO.js} +15 -0
  57. package/dist/{package-2W2XKSIP.js → package-F6S5NHY2.js} +4 -4
  58. package/dist/{prompts-NBO3HRK3.js → prompts-JECJIFPQ.js} +1 -1
  59. package/dist/{reject-TWB4QJHG.js → reject-4NFLXKRN.js} +6 -2
  60. package/dist/{scheduler-JTTWZDYU.js → scheduler-73Q4MQ7Y.js} +8 -6
  61. package/dist/{seed-64XEHRUP.js → seed-DRKAP3XV.js} +1 -1
  62. package/dist/{seed-cmd-UEL2QB3Q.js → seed-cmd-6IMTO3LG.js} +2 -2
  63. package/dist/{seed-create-4JRQDPGA.js → seed-create-VNLDYRFK.js} +1 -1
  64. package/dist/{seed-readiness-DYOVCYHN.js → seed-readiness-H5TTPAQH.js} +2 -2
  65. package/dist/{seed-sprout-NIDIOJXT.js → seed-sprout-PPGYGK4G.js} +5 -5
  66. package/dist/{send-2YKOZJJ5.js → send-UXUQKO4N.js} +37 -22
  67. package/dist/{service-XQPMZAGI.js → service-T4P5W6RS.js} +2 -2
  68. package/dist/{service-install-2EWD7KXD.js → service-install-R4SNYUVO.js} +3 -3
  69. package/dist/{setup-4IX6PCD3.js → setup-YUAQMDVZ.js} +4 -4
  70. package/dist/skills/dreaming/SKILL.md +2 -2
  71. package/dist/skills/dreaming/references/INSTALL.md +1 -1
  72. package/dist/skills/memory/SKILL.md +47 -43
  73. package/dist/skills/tending/SKILL.md +2 -0
  74. package/dist/skills/volute-mind/references/integrations.md +3 -2
  75. package/dist/skills/volute-mind/references/routing.md +18 -5
  76. package/dist/{skills-MPUALV3S.js → skills-GMQ5UIIW.js} +2 -2
  77. package/dist/{sleep-manager-OFP2GFXF.js → sleep-manager-KHI65FEX.js} +12 -11
  78. package/dist/{spirit-4ECYIVGE.js → spirit-Q5VGFWUI.js} +19 -3
  79. package/dist/{spirit-availability-Y54Y3675.js → spirit-availability-G5FMWKRH.js} +13 -12
  80. package/dist/{sprout-GJMZFRGZ.js → sprout-PMTRL7QN.js} +1 -1
  81. package/dist/{src-LCSOQY6Z.js → src-3FIW7DLH.js} +241 -87
  82. package/dist/src-H3UCOP6B.js +516 -0
  83. package/dist/{src-HGNE2XG4.js → src-LTTQVN2Z.js} +1 -1
  84. package/dist/{status-MWLE3RVC.js → status-LCKTTIWS.js} +2 -2
  85. package/dist/{system-events-V43HDA3T.js → system-events-Q7IVZ5T4.js} +5 -1
  86. package/dist/{systems-VDRUZXNF.js → systems-HL7SGSW7.js} +2 -2
  87. package/dist/{turn-tracker-C5PVIC3B.js → turn-tracker-HZDPMSCC.js} +4 -2
  88. package/dist/{up-H6LJZDRK.js → up-XZDSJVMU.js} +3 -3
  89. package/dist/{update-CIWI2JYA.js → update-ILCPVNDD.js} +2 -2
  90. package/dist/{upgrade-LIE46UBB.js → upgrade-XY2J7NWZ.js} +9 -1
  91. package/dist/{variant-cleanup-STC6B5HR.js → variant-cleanup-QHGGY7ND.js} +8 -6
  92. package/dist/{version-notify-LBVHH5YV.js → version-notify-VNVFX6UP.js} +22 -7
  93. package/dist/web-assets/assets/{index-B_eYTa3R.js → index-CwtxGwe_.js} +16 -16
  94. package/dist/web-assets/assets/index-DhM1yYRN.css +1 -0
  95. package/dist/web-assets/index.html +2 -2
  96. package/package.json +4 -4
  97. package/packages/extensions/intentions/dist/ui/assets/index-3FneLCKR.css +1 -0
  98. package/packages/extensions/intentions/dist/ui/assets/index-CQz4aceM.js +67 -0
  99. package/packages/extensions/{plan → intentions}/dist/ui/index.html +3 -3
  100. package/packages/extensions/intentions/skills/intention-review/SKILL.md +38 -0
  101. package/packages/extensions/intentions/skills/intentions/SKILL.md +49 -0
  102. package/packages/extensions/intentions/skills/intentions/scripts/intentions-hook.sh +45 -0
  103. package/packages/extensions/pages/skills/commons-gardening/SKILL.md +50 -0
  104. package/packages/extensions/pages/skills/pages/SKILL.md +18 -13
  105. package/templates/_base/.init/.config/prompts.json +3 -3
  106. package/templates/{pi → _base}/.init/.config/routes.json +1 -0
  107. package/templates/_base/.init/memory/dreams/.gitkeep +0 -0
  108. package/templates/_base/home/.config/routes.json +1 -1
  109. package/templates/_base/home/VOLUTE.md +1 -1
  110. package/templates/_base/src/lib/codex-session-seed.ts +463 -0
  111. package/templates/_base/src/lib/context-breakdown.ts +11 -3
  112. package/templates/_base/src/lib/daemon-client.ts +35 -0
  113. package/templates/_base/src/lib/pi-session-seed.ts +434 -0
  114. package/templates/_base/src/lib/routing.ts +2 -2
  115. package/templates/_base/src/lib/seed-note.ts +78 -0
  116. package/templates/_base/src/lib/session-seed.ts +375 -0
  117. package/templates/_base/src/lib/startup.ts +112 -6
  118. package/templates/claude/.init/CLAUDE.md +7 -3
  119. package/templates/claude/src/agent.ts +295 -101
  120. package/templates/claude/src/lib/hooks/pre-compact.ts +30 -5
  121. package/templates/claude/src/lib/message-channel.ts +43 -15
  122. package/templates/claude/src/lib/recover.ts +69 -0
  123. package/templates/claude/src/lib/stream-consumer.ts +33 -2
  124. package/templates/claude/src/server.ts +2 -1
  125. package/templates/claude/volute-template.json +1 -1
  126. package/templates/codex/.init/AGENTS.md +7 -3
  127. package/templates/codex/src/agent.ts +256 -39
  128. package/templates/codex/src/server.ts +2 -1
  129. package/templates/codex/volute-template.json +1 -1
  130. package/templates/pi/.init/MINDS.md +7 -3
  131. package/templates/pi/src/agent.ts +269 -43
  132. package/templates/pi/src/lib/event-handler.ts +11 -0
  133. package/templates/pi/src/lib/mechanics-doc.ts +30 -0
  134. package/templates/pi/src/server.ts +5 -1
  135. package/templates/pi/volute-template.json +1 -1
  136. package/dist/channels-FSAHOQWI.js +0 -90
  137. package/dist/skills/plan-coordinator/SKILL.md +0 -60
  138. package/dist/src-UDMJZ6MH.js +0 -426
  139. package/dist/web-assets/assets/index-DhZrzddW.css +0 -1
  140. package/packages/extensions/plan/dist/ui/assets/index-B8s8wxyt.js +0 -67
  141. package/packages/extensions/plan/dist/ui/assets/index-jIkrt-vI.css +0 -1
  142. package/packages/extensions/plan/skills/plan/SKILL.md +0 -43
  143. package/packages/extensions/plan/skills/plan/scripts/plan-hook.sh +0 -37
  144. package/templates/claude/.init/.config/routes.json +0 -11
  145. package/templates/codex/.init/.config/routes.json +0 -11
  146. package/dist/{env-IBW2QI6K.js → env-7AZFBPWX.js} +5 -5
  147. package/dist/{login-RJJTCVR3.js → login-4NUS72Z5.js} +3 -3
  148. package/dist/{login-SB5OENMV.js → login-57MMYILL.js} +4 -4
  149. package/dist/{register-OX4HRSGC.js → register-WNCSOTAU.js} +3 -3
  150. package/dist/{restart-AGEGNSYQ.js → restart-B4GNMVF2.js} +4 -4
  151. package/dist/{skill-B5OJEQUA.js → skill-QYTBUDNQ.js} +4 -4
  152. package/dist/{start-MUMNFR52.js → start-BDHJVOVX.js} +3 -3
  153. package/dist/{stop-EKERNYTX.js → stop-45QYSGYS.js} +4 -4
@@ -1,10 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ getUserByUsername
4
+ } from "./chunk-7LT3YZUR.js";
2
5
  import {
3
6
  publish
4
7
  } from "./chunk-LNNI3MS4.js";
5
8
  import {
6
9
  getPrompt
7
- } from "./chunk-FXWBFDQR.js";
10
+ } from "./chunk-OAYY3WF3.js";
8
11
  import {
9
12
  aiCompleteUtility
10
13
  } from "./chunk-EGV6R6UF.js";
@@ -12,7 +15,8 @@ import {
12
15
  logger_default
13
16
  } from "./chunk-QUSMYGNQ.js";
14
17
  import {
15
- getDb
18
+ getDb,
19
+ resolveMindDir
16
20
  } from "./chunk-H6X6CKDN.js";
17
21
  import {
18
22
  messages,
@@ -26,6 +30,8 @@ import { randomUUID } from "crypto";
26
30
  import { and as and2, eq as eq2, inArray as inArray2, sql as sql2 } from "drizzle-orm";
27
31
 
28
32
  // packages/daemon/src/lib/daemon/summarizer.ts
33
+ import { readFile } from "fs/promises";
34
+ import { join } from "path";
29
35
  import { and, desc, eq, gte, inArray, like, lt, sql } from "drizzle-orm";
30
36
 
31
37
  // packages/daemon/src/lib/util/format-tool.ts
@@ -181,6 +187,12 @@ function getTimeRange(periodKey, period) {
181
187
  }
182
188
  }
183
189
 
190
+ // packages/daemon/src/lib/util/time.ts
191
+ function parseDbTimestamp(ts) {
192
+ if (/[zZ]$|[+-]\d\d:?\d\d$/.test(ts)) return new Date(ts);
193
+ return /* @__PURE__ */ new Date(`${ts.replace(" ", "T")}Z`);
194
+ }
195
+
184
196
  // packages/daemon/src/lib/daemon/summarizer.ts
185
197
  var sLog = logger_default.child("summarizer");
186
198
  var SYSTEM_MIND = "_system";
@@ -205,6 +217,7 @@ async function gatherTurnEvents(mind, session, doneId) {
205
217
  type: mindHistory.type,
206
218
  channel: mindHistory.channel,
207
219
  session: mindHistory.thread,
220
+ sender: mindHistory.sender,
208
221
  content: mindHistory.content,
209
222
  metadata: mindHistory.metadata,
210
223
  turn_id: mindHistory.turn_id,
@@ -223,6 +236,7 @@ async function gatherTurnEventsByTurnId(turnId) {
223
236
  type: mindHistory.type,
224
237
  channel: mindHistory.channel,
225
238
  session: mindHistory.thread,
239
+ sender: mindHistory.sender,
226
240
  content: mindHistory.content,
227
241
  metadata: mindHistory.metadata,
228
242
  turn_id: mindHistory.turn_id,
@@ -289,13 +303,19 @@ function buildTurnDeterministicSummary(events, parsedMeta) {
289
303
  }
290
304
  return parts.length > 0 ? `${parts.join(". ")}.` : "Turn completed.";
291
305
  }
292
- function buildTranscript(events, parsedMeta) {
306
+ function buildTranscript(events, parsedMeta, mind) {
293
307
  const lines = [];
294
308
  for (const ev of events) {
295
309
  switch (ev.type) {
296
- case "inbound":
297
- lines.push(`[inbound${ev.channel ? ` ${ev.channel}` : ""}] ${ev.content ?? ""}`);
310
+ // Inbound messages come from *other* people. Attribute them to the sender by name (and
311
+ // channel) so the summarizer never absorbs someone else's first-person statements into
312
+ // the mind's own "I" in a multi-party channel.
313
+ case "inbound": {
314
+ const on = ev.channel ? ` on ${ev.channel}` : "";
315
+ const from = ev.sender ? ` from ${ev.sender}` : "";
316
+ lines.push(`[inbound${on}${from}] ${ev.content ?? ""}`);
298
317
  break;
318
+ }
299
319
  // A system event is what triggered the turn — the summarizer needs to see it, or a
300
320
  // schedule/orientation/wake turn gets summarized from its tool calls alone, with no
301
321
  // idea what prompted them. Framed as an event, not an inbound message: it has no
@@ -306,9 +326,12 @@ function buildTranscript(events, parsedMeta) {
306
326
  lines.push(`[system event${named}] ${ev.content ?? ""}`);
307
327
  break;
308
328
  }
329
+ // The mind's own output/thoughts, labeled with its name so the "I" is unambiguous. Never
330
+ // truncated — a mid-sentence fragment is worse than a long line, because the model
331
+ // interpolates over the cut and invents a false memory.
309
332
  case "outbound":
310
333
  case "text":
311
- lines.push(`[response] ${(ev.content ?? "").slice(0, 500)}`);
334
+ lines.push(`[${mind} replied] ${ev.content ?? ""}`);
312
335
  break;
313
336
  case "tool_use": {
314
337
  const meta = parsedMeta.get(ev.id);
@@ -329,12 +352,26 @@ function buildTranscript(events, parsedMeta) {
329
352
  break;
330
353
  }
331
354
  case "thinking":
332
- lines.push(`[thinking] ${(ev.content ?? "").slice(0, 300)}`);
355
+ lines.push(`[${mind} thinking] ${ev.content ?? ""}`);
333
356
  break;
334
357
  }
335
358
  }
336
359
  return lines.join("\n");
337
360
  }
361
+ async function getMindIdentityLine(mind) {
362
+ try {
363
+ const user = await getUserByUsername(mind);
364
+ if (!user) return "";
365
+ const name = user.display_name?.trim();
366
+ const desc2 = user.description?.trim();
367
+ if (!name && !desc2) return "";
368
+ const who = name ? `${mind} (${name})` : mind;
369
+ return `[about the mind] ${who}${desc2 ? `: ${desc2}` : ""}`;
370
+ } catch (err) {
371
+ sLog.debug(`failed to load identity for ${mind}`, logger_default.errorData(err));
372
+ return "";
373
+ }
374
+ }
338
375
  async function summarizeTurn(mind, session, channel, doneId, turnId) {
339
376
  const { events, fromId, toId } = turnId ? await gatherTurnEventsByTurnId(turnId) : await gatherTurnEvents(mind, session, doneId);
340
377
  if (events.length === 0) return;
@@ -377,10 +414,14 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
377
414
  const toTime = events[events.length - 1].created_at;
378
415
  let summaryText;
379
416
  let deterministic;
380
- const transcript = buildTranscript(events, parsedMeta);
417
+ const transcript = buildTranscript(events, parsedMeta, mind);
381
418
  if (transcript.trim()) {
382
- const summaryPrompt = await getPrompt("turn_summary");
383
- const aiResult = await aiCompleteUtility(summaryPrompt, transcript);
419
+ const summaryPrompt = await getPrompt("turn_summary", { mind });
420
+ const identity = await getMindIdentityLine(mind);
421
+ const input = identity ? `${identity}
422
+
423
+ ${transcript}` : transcript;
424
+ const aiResult = await aiCompleteUtility(summaryPrompt, input);
384
425
  if (aiResult) {
385
426
  summaryText = aiResult;
386
427
  deterministic = false;
@@ -449,6 +490,62 @@ async function setSummaryId(turnId, summaryId) {
449
490
  const db = await getDb();
450
491
  await db.update(turns).set({ summary_id: summaryId }).where(eq(turns.id, turnId));
451
492
  }
493
+ var MIND_TURN_SUMMARY_MAX_CHARS = 4e3;
494
+ async function supersedeTurnSummary(mind, turnId, content) {
495
+ const text = content.trim();
496
+ if (!text) return { status: "invalid", error: "content is required" };
497
+ if (text.length > MIND_TURN_SUMMARY_MAX_CHARS) {
498
+ return {
499
+ status: "invalid",
500
+ error: `content exceeds ${MIND_TURN_SUMMARY_MAX_CHARS} characters`
501
+ };
502
+ }
503
+ const db = await getDb();
504
+ const turn = await db.select().from(turns).where(eq(turns.id, turnId)).get();
505
+ if (!turn) return { status: "not_found" };
506
+ if (turn.mind !== mind) return { status: "forbidden" };
507
+ const existing = await db.select({ id: summaries.id, content: summaries.content, metadata: summaries.metadata }).from(summaries).where(
508
+ and(eq(summaries.mind, mind), eq(summaries.period, "turn"), eq(summaries.period_key, turnId))
509
+ ).get();
510
+ const authoredAt = utcDateTimeStr(/* @__PURE__ */ new Date());
511
+ let created;
512
+ if (existing) {
513
+ const prevMeta = parseMeta(existing.metadata);
514
+ let superseded = prevMeta.superseded;
515
+ if (superseded === void 0) {
516
+ const captured = { content: existing.content };
517
+ if (typeof prevMeta.deterministic === "boolean")
518
+ captured.deterministic = prevMeta.deterministic;
519
+ superseded = captured;
520
+ }
521
+ const metadata = { ...prevMeta, author: "mind", authored_at: authoredAt, superseded };
522
+ await db.update(summaries).set({ content: text, metadata: JSON.stringify(metadata) }).where(eq(summaries.id, existing.id));
523
+ created = false;
524
+ } else {
525
+ const metadata = { author: "mind", authored_at: authoredAt };
526
+ const result = await db.insert(summaries).values({
527
+ mind,
528
+ period: "turn",
529
+ period_key: turnId,
530
+ content: text,
531
+ metadata: JSON.stringify(metadata)
532
+ }).returning({ id: summaries.id });
533
+ const summaryId = result[0]?.id;
534
+ if (summaryId != null && turn.summary_id == null) {
535
+ await setSummaryId(turnId, summaryId);
536
+ }
537
+ created = true;
538
+ }
539
+ publish(mind, {
540
+ mind,
541
+ type: "summary",
542
+ session: turn.thread ?? void 0,
543
+ content: text,
544
+ metadata: { author: "mind", authored_at: authoredAt },
545
+ turnId
546
+ });
547
+ return { status: "ok", created };
548
+ }
452
549
  function getChildPeriod(period) {
453
550
  switch (period) {
454
551
  case "hour":
@@ -561,12 +658,32 @@ function trackProvisionalAttempt(metadata, prev) {
561
658
  metadata.first_attempt_at = prev && typeof prev.first_attempt_at === "string" ? prev.first_attempt_at : (/* @__PURE__ */ new Date()).toISOString();
562
659
  metadata.last_attempt_at = (/* @__PURE__ */ new Date()).toISOString();
563
660
  }
661
+ function childLabel(period, key2, createdAt) {
662
+ switch (period) {
663
+ case "hour": {
664
+ const d = parseDbTimestamp(createdAt);
665
+ const hh = String(d.getHours()).padStart(2, "0");
666
+ const mm = String(d.getMinutes()).padStart(2, "0");
667
+ return `${hh}:${mm}`;
668
+ }
669
+ case "day":
670
+ return `${key2.slice(11)}:00`;
671
+ case "week":
672
+ case "month":
673
+ return key2;
674
+ }
675
+ }
564
676
  async function gatherChildSummaries(mind, period, periodKey) {
565
677
  const db = await getDb();
566
678
  const childPeriod = getChildPeriod(period);
567
679
  if (period === "hour") {
568
680
  const { start: start2, end: end2 } = getTimeRange(periodKey, "hour");
569
- const rows2 = await db.select({ id: summaries.id, content: summaries.content, key: summaries.period_key }).from(summaries).where(
681
+ const rows2 = await db.select({
682
+ id: summaries.id,
683
+ content: summaries.content,
684
+ key: summaries.period_key,
685
+ created_at: summaries.created_at
686
+ }).from(summaries).where(
570
687
  and(
571
688
  eq(summaries.mind, mind),
572
689
  eq(summaries.period, childPeriod),
@@ -577,7 +694,8 @@ async function gatherChildSummaries(mind, period, periodKey) {
577
694
  return {
578
695
  texts: rows2.map((r) => r.content),
579
696
  sourceIds: rows2.map((r) => r.id),
580
- keys: rows2.map((r) => r.key)
697
+ keys: rows2.map((r) => r.key),
698
+ labels: rows2.map((r) => childLabel(period, r.key, r.created_at))
581
699
  };
582
700
  }
583
701
  if (period === "day") {
@@ -591,7 +709,8 @@ async function gatherChildSummaries(mind, period, periodKey) {
591
709
  return {
592
710
  texts: rows2.map((r) => r.content),
593
711
  sourceIds: rows2.map((r) => r.id),
594
- keys: rows2.map((r) => r.key)
712
+ keys: rows2.map((r) => r.key),
713
+ labels: rows2.map((r) => childLabel(period, r.key, ""))
595
714
  };
596
715
  }
597
716
  const { start, end } = getTimeRange(periodKey, period);
@@ -608,9 +727,20 @@ async function gatherChildSummaries(mind, period, periodKey) {
608
727
  return {
609
728
  texts: rows.map((r) => r.content),
610
729
  sourceIds: rows.map((r) => r.id),
611
- keys: rows.map((r) => r.key)
730
+ keys: rows.map((r) => r.key),
731
+ labels: rows.map((r) => childLabel(period, r.key, ""))
612
732
  };
613
733
  }
734
+ var SOUL_MAX_CHARS = 8e3;
735
+ async function readMindSoul(mind) {
736
+ try {
737
+ const dir = await resolveMindDir(mind);
738
+ const soul = await readFile(join(dir, "home", "SOUL.md"), "utf8");
739
+ return soul.length > SOUL_MAX_CHARS ? soul.slice(0, SOUL_MAX_CHARS) : soul;
740
+ } catch {
741
+ return "";
742
+ }
743
+ }
614
744
  async function summarizePeriod(mind, period, periodKey, complete = aiCompleteUtility) {
615
745
  const db = await getDb();
616
746
  const existing = await db.select({ id: summaries.id, metadata: summaries.metadata }).from(summaries).where(
@@ -651,8 +781,18 @@ async function summarizePeriod(mind, period, periodKey, complete = aiCompleteUti
651
781
  const entries = sources.texts.map((text, i) => ({ key: sources.keys[i], text }));
652
782
  const promptKey = `meta_summary_${period}`;
653
783
  const scopeInstruction = getScopeInstruction(mind);
654
- const systemPrompt = await getPrompt(promptKey, { scope_instruction: scopeInstruction });
655
- const userMessage = sources.texts.join("\n\n---\n\n");
784
+ let systemPrompt = await getPrompt(promptKey, { scope_instruction: scopeInstruction });
785
+ if (period === "week" || period === "month") {
786
+ const soul = await readMindSoul(mind);
787
+ if (soul.trim()) {
788
+ systemPrompt = `${systemPrompt}
789
+
790
+ For voice and perspective, this is the mind's own self-description (SOUL.md):
791
+
792
+ ${soul.trim()}`;
793
+ }
794
+ }
795
+ const userMessage = sources.texts.map((text, i) => `[${sources.labels[i]}] ${text}`).join("\n\n---\n\n");
656
796
  let content;
657
797
  let deterministic;
658
798
  const metadata = {
@@ -1060,11 +1200,11 @@ var Summarizer = class {
1060
1200
  }
1061
1201
  };
1062
1202
  async function reconcileWedgedTurns(idleMs) {
1063
- const { sweepWedgedTurns: sweepWedgedTurns2, summarizeOrphanedTurns: summarizeOrphanedTurns2 } = await import("./turn-tracker-C5PVIC3B.js");
1203
+ const { sweepWedgedTurns: sweepWedgedTurns2, summarizeOrphanedTurns: summarizeOrphanedTurns2 } = await import("./turn-tracker-HZDPMSCC.js");
1064
1204
  const wedged = await sweepWedgedTurns2(idleMs);
1065
1205
  if (wedged.length === 0) return;
1066
1206
  summarizeOrphanedTurns2(wedged);
1067
- const { tryGetDeliveryManager } = await import("./delivery-manager-VYXVKUMP.js");
1207
+ const { tryGetDeliveryManager } = await import("./delivery-manager-7R2YIQ2W.js");
1068
1208
  const dm = tryGetDeliveryManager();
1069
1209
  if (!dm) return;
1070
1210
  for (const t of wedged) {
@@ -1268,8 +1408,10 @@ export {
1268
1408
  utcDateTimeStr,
1269
1409
  parseUtcDateTime,
1270
1410
  getUtcTimeRange,
1411
+ parseDbTimestamp,
1271
1412
  SYSTEM_MIND,
1272
1413
  summarizeTurn,
1414
+ supersedeTurnSummary,
1273
1415
  initSummarizer,
1274
1416
  markErrored,
1275
1417
  takeErrored,
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+
3
+ // packages/daemon/src/lib/mind/memory-size.ts
4
+ import { readFileSync, statSync } from "fs";
5
+ import { resolve } from "path";
6
+ var MEMORY_SOFT_BUDGET_TOKENS = 5e3;
7
+ var MEMORY_HARD_CAP_TOKENS = 25e3;
8
+ var MAX_READ_BYTES = 1e6;
9
+ function getMemoryStatus(projectDir) {
10
+ const path = resolve(projectDir, "home", "MEMORY.md");
11
+ let bytes;
12
+ let chars;
13
+ try {
14
+ const size = statSync(path).size;
15
+ if (size > MAX_READ_BYTES) {
16
+ bytes = size;
17
+ chars = size;
18
+ } else {
19
+ const text = readFileSync(path, "utf-8");
20
+ bytes = size;
21
+ chars = text.length;
22
+ }
23
+ } catch {
24
+ return null;
25
+ }
26
+ let softBudgetTokens = MEMORY_SOFT_BUDGET_TOKENS;
27
+ let hardCapTokens = MEMORY_HARD_CAP_TOKENS;
28
+ try {
29
+ const config = JSON.parse(
30
+ readFileSync(resolve(projectDir, "home", ".config", "config.json"), "utf-8")
31
+ );
32
+ if (typeof config?.memory?.softBudgetTokens === "number") {
33
+ softBudgetTokens = config.memory.softBudgetTokens;
34
+ }
35
+ if (typeof config?.memory?.hardCapTokens === "number") {
36
+ hardCapTokens = config.memory.hardCapTokens;
37
+ }
38
+ } catch {
39
+ }
40
+ const estTokens = Math.round(chars / 4);
41
+ return {
42
+ bytes,
43
+ estTokens,
44
+ softBudgetTokens,
45
+ hardCapTokens,
46
+ overBudget: estTokens > softBudgetTokens,
47
+ overHardCap: estTokens > hardCapTokens
48
+ };
49
+ }
50
+ function formatTokens(tokens) {
51
+ if (tokens < 1e3) return `~${tokens} tokens`;
52
+ return `~${(tokens / 1e3).toFixed(1).replace(/\.0$/, "")}k tokens`;
53
+ }
54
+
55
+ export {
56
+ getMemoryStatus,
57
+ formatTokens
58
+ };
@@ -3,10 +3,10 @@ import {
3
3
  LAUNCHD_PLIST_LABEL,
4
4
  LAUNCHD_PLIST_PATH,
5
5
  USER_SYSTEMD_UNIT
6
- } from "./chunk-6NLW72HP.js";
6
+ } from "./chunk-CKPAVQNK.js";
7
7
  import {
8
8
  resolveVoluteBin
9
- } from "./chunk-M77B73VV.js";
9
+ } from "./chunk-OIOW3FPT.js";
10
10
 
11
11
  // packages/daemon/src/lib/config/service-install.ts
12
12
  import { execFile } from "child_process";
@@ -3,16 +3,16 @@ import {
3
3
  compactDateTime,
4
4
  isCompact
5
5
  } from "./chunk-7YJY3F2R.js";
6
+ import {
7
+ getClient,
8
+ urlOf
9
+ } from "./chunk-LOPXTW6H.js";
6
10
  import {
7
11
  resolveMindName
8
12
  } from "./chunk-BTY4WNFE.js";
9
13
  import {
10
14
  daemonFetch
11
15
  } from "./chunk-M74I3HHF.js";
12
- import {
13
- getClient,
14
- urlOf
15
- } from "./chunk-LOPXTW6H.js";
16
16
  import {
17
17
  command,
18
18
  subcommands
@@ -279,7 +279,7 @@ var cmd = subcommands({
279
279
  },
280
280
  sleep: {
281
281
  description: "Put a mind to sleep",
282
- run: (args) => import("./mind-sleep-LZAFBDZD.js").then((m) => m.run(args))
282
+ run: (args) => import("./mind-sleep-24HV4RCA.js").then((m) => m.run(args))
283
283
  },
284
284
  wake: {
285
285
  description: "Wake a sleeping mind",
@@ -79,6 +79,9 @@ function getSystemPages(db) {
79
79
  )
80
80
  };
81
81
  }
82
+ function getMindsWithSites(db) {
83
+ return db.prepare("SELECT DISTINCT mind FROM published_pages WHERE mind != '_system' ORDER BY mind").all().map((r) => r.mind);
84
+ }
82
85
  function syncSystemPages(db, pages, author) {
83
86
  const existing = new Map(
84
87
  db.prepare("SELECT file, hash FROM published_pages WHERE mind = '_system'").all().map((r) => [r.file, r.hash])
@@ -159,6 +162,7 @@ export {
159
162
  getRecentPages,
160
163
  getAllSites,
161
164
  getSystemPages,
165
+ getMindsWithSites,
162
166
  syncSystemPages,
163
167
  syncPublishedPages
164
168
  };
@@ -5,7 +5,10 @@ import {
5
5
  } from "./chunk-L7BKTZ5U.js";
6
6
  import {
7
7
  recordOutbound
8
- } from "./chunk-ZEZQNP2G.js";
8
+ } from "./chunk-LGXVGIWN.js";
9
+ import {
10
+ recordSenderDeliveryFailure
11
+ } from "./chunk-3LHUXY3E.js";
9
12
  import {
10
13
  getOrCreateMindUser,
11
14
  getUserByUsername
@@ -27,7 +30,7 @@ import {
27
30
  import {
28
31
  daemonConfigPath,
29
32
  readDaemonToken
30
- } from "./chunk-6NLW72HP.js";
33
+ } from "./chunk-CKPAVQNK.js";
31
34
  import {
32
35
  readEnv,
33
36
  sharedEnvPath
@@ -732,7 +735,21 @@ async function routeChannelOutbound(channelName, senderName, contentBlocks) {
732
735
  if (!text) return;
733
736
  const env = readEnv(sharedEnvPath());
734
737
  env.VOLUTE_SENDER = senderName;
735
- await driver.send(env, bridgeInfo.externalChannel, text, images.length > 0 ? images : void 0);
738
+ try {
739
+ await driver.send(
740
+ env,
741
+ bridgeInfo.externalChannel,
742
+ text,
743
+ images.length > 0 ? images : void 0
744
+ );
745
+ } catch (err) {
746
+ await recordSenderDeliveryFailure(
747
+ senderName,
748
+ channelName,
749
+ err instanceof Error && err.message ? err.message : String(err)
750
+ );
751
+ throw err;
752
+ }
736
753
  logger_default.debug(`bridge outbound: sent to ${bridgeInfo.platform}:${bridgeInfo.externalChannel}`);
737
754
  }
738
755
  async function routeDMOutbound(conversationId, senderName, contentBlocks) {
@@ -768,6 +785,11 @@ async function routeDMOutbound(conversationId, senderName, contentBlocks) {
768
785
  logger_default.debug(`bridge outbound DM: sent to ${platform}:${externalUserId}`);
769
786
  } catch (err) {
770
787
  logger_default.error(`bridge outbound DM failed for puppet ${puppet.username}`, logger_default.errorData(err));
788
+ await recordSenderDeliveryFailure(
789
+ senderName,
790
+ puppet.username,
791
+ err instanceof Error && err.message ? err.message : String(err)
792
+ );
771
793
  }
772
794
  }
773
795
  }
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  exec,
4
4
  execInherit
5
- } from "./chunk-M77B73VV.js";
5
+ } from "./chunk-OIOW3FPT.js";
6
6
  import {
7
7
  voluteSystemDir
8
8
  } from "./chunk-H6X6CKDN.js";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  MEMORY_PLACEHOLDER_MARKER,
4
4
  ORIENTATION_MARKER
5
- } from "./chunk-FXWBFDQR.js";
5
+ } from "./chunk-OAYY3WF3.js";
6
6
  import {
7
7
  safeResolveWithinBase
8
8
  } from "./chunk-DW4TRDOZ.js";
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ logger_default
4
+ } from "./chunk-QUSMYGNQ.js";
2
5
  import {
3
6
  stateDir
4
7
  } from "./chunk-H6X6CKDN.js";
@@ -7,6 +10,7 @@ import {
7
10
  import { randomBytes } from "crypto";
8
11
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
9
12
  import { basename, join, normalize, resolve } from "path";
13
+ var flog = logger_default.child("file-sharing");
10
14
  function validateFilePath(filePath) {
11
15
  if (!filePath) return "File path is required";
12
16
  const normalized = normalize(filePath);
@@ -65,7 +69,7 @@ function listPending(receiver) {
65
69
  try {
66
70
  result.push(JSON.parse(readFileSync(metaPath, "utf-8")));
67
71
  } catch (err) {
68
- console.warn(`[file-sharing] skipping malformed pending entry ${entry.name}:`, err);
72
+ flog.warn(`skipping malformed pending entry ${entry.name}`, logger_default.errorData(err));
69
73
  }
70
74
  }
71
75
  return result.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
@@ -77,7 +81,7 @@ function getPending(receiver, id) {
77
81
  try {
78
82
  return JSON.parse(readFileSync(metaPath, "utf-8"));
79
83
  } catch (err) {
80
- console.warn(`[file-sharing] failed to read pending metadata for ${id}:`, err);
84
+ flog.warn(`failed to read pending metadata for ${id}`, logger_default.errorData(err));
81
85
  return null;
82
86
  }
83
87
  }
@@ -94,6 +94,8 @@ function eventLabel(type, meta) {
94
94
  return s("reason") ? `Notice: ${s("reason")}` : "Notice";
95
95
  case "delivery_failed":
96
96
  return "Delivery failed";
97
+ case "context_lost":
98
+ return "Context lost";
97
99
  case "join_blocked":
98
100
  return "Variant join blocked";
99
101
  default:
@@ -103,6 +105,16 @@ function eventLabel(type, meta) {
103
105
  return type.charAt(0).toUpperCase() + type.slice(1);
104
106
  }
105
107
  }
108
+ var NOTICE_KINDS = [
109
+ "turn_error",
110
+ "crash",
111
+ "budget",
112
+ "startup",
113
+ "extension",
114
+ "delivery_failed",
115
+ "context_lost",
116
+ "join_blocked"
117
+ ];
106
118
  async function recordNotice(input) {
107
119
  const type = input.kind === "budget" ? "budget" : "notice";
108
120
  await deliverEvent(input.mind, {
@@ -111,6 +123,7 @@ async function recordNotice(input) {
111
123
  thread: input.thread,
112
124
  delivery: "next-turn",
113
125
  meta: {
126
+ ...input.meta ?? {},
114
127
  subtype: input.kind,
115
128
  reason: input.reason,
116
129
  ...input.raw ? { raw: input.raw } : {}
@@ -244,7 +257,7 @@ async function deliverEvent(mind, input) {
244
257
  return { id: eventId, delivered: false };
245
258
  }
246
259
  const baseName = await getBaseName(mind);
247
- const { getSleepManagerIfReady } = await import("./sleep-manager-OFP2GFXF.js");
260
+ const { getSleepManagerIfReady } = await import("./sleep-manager-KHI65FEX.js");
248
261
  const sleepManager = getSleepManagerIfReady();
249
262
  const sleeping = !input.force && sleepManager?.isSleeping(baseName);
250
263
  const behavior = input.whileSleeping ?? "queue";
@@ -552,6 +565,7 @@ export {
552
565
  MIND_LEVEL_THREAD,
553
566
  parseMeta,
554
567
  eventLabel,
568
+ NOTICE_KINDS,
555
569
  recordNotice,
556
570
  eventChannel,
557
571
  deliverEvent,
@@ -561,6 +575,7 @@ export {
561
575
  pendingEventsLine,
562
576
  drainEvents,
563
577
  formatEvents,
578
+ localHM,
564
579
  clearDeliveredEvents,
565
580
  latestFailureEvent,
566
581
  latestEvent,