volute 0.51.0 → 0.53.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 (112) hide show
  1. package/dist/{accept-5NK5XJB5.js → accept-I4GVGWA3.js} +6 -3
  2. package/dist/{auth-X6EMNEGB.js → auth-KRDSM55Q.js} +3 -1
  3. package/dist/{chat-VAZ25KVT.js → chat-WK2BLM7B.js} +9 -9
  4. package/dist/{chunk-VAEZCIJC.js → chunk-23UJGVYP.js} +9 -9
  5. package/dist/{chunk-VSBMLB2W.js → chunk-5DBGQBQR.js} +138 -13
  6. package/dist/chunk-5RBEEAKW.js +58 -0
  7. package/dist/{chunk-BSHZ23MS.js → chunk-7ARONCZW.js} +25 -3
  8. package/dist/{chunk-HKP6OAJP.js → chunk-7LT3YZUR.js} +12 -0
  9. package/dist/{chunk-FXWBFDQR.js → chunk-AP2KQMMW.js} +48 -9
  10. package/dist/{chunk-DMK5ASKV.js → chunk-F6D766VQ.js} +6 -2
  11. package/dist/{chunk-VP5PFNZO.js → chunk-HASADR3E.js} +73 -20
  12. package/dist/{chunk-ICL6T4SN.js → chunk-IQ7VNWH6.js} +26 -2
  13. package/dist/{chunk-G7A5LEVI.js → chunk-LN7XJ56R.js} +6 -6
  14. package/dist/{chunk-DN3LKLYR.js → chunk-P2TBUKLW.js} +3 -3
  15. package/dist/{chunk-Q7XQLKZB.js → chunk-PIZDPY4G.js} +20 -3
  16. package/dist/{chunk-JHKBALS2.js → chunk-RMADJFA7.js} +1 -1
  17. package/dist/{chunk-7HN63Q7J.js → chunk-RQRO6PC7.js} +40 -6
  18. package/dist/{chunk-QT4D5XCI.js → chunk-TAWRJ24T.js} +1 -1
  19. package/dist/{chunk-AI47FDGL.js → chunk-UO4MZQNO.js} +1 -1
  20. package/dist/chunk-UTKUO2KY.js +102 -0
  21. package/dist/{chunk-CDKQJWGK.js → chunk-VDZ4I4ZP.js} +160 -18
  22. package/dist/cli.js +16 -16
  23. package/dist/{clock-ZT5YT6HA.js → clock-SH4NQC2F.js} +3 -3
  24. package/dist/{cloud-sync-GPXYPTIC.js → cloud-sync-MJVJ33LD.js} +11 -10
  25. package/dist/{daemon-restart-2EWBRTN5.js → daemon-restart-BIGU4OKS.js} +1 -1
  26. package/dist/daemon.js +543 -236
  27. package/dist/{delete-O7PEFQDF.js → delete-CYVZUAV2.js} +4 -0
  28. package/dist/{delivery-manager-HGVZSEES.js → delivery-manager-FDHCZ7PU.js} +7 -5
  29. package/dist/delivery-notices-65TB3CBX.js +18 -0
  30. package/dist/{echo-text-6ANI5HHN.js → echo-text-KU6G4Z7V.js} +12 -11
  31. package/dist/{extensions-NY6WST33.js → extensions-VACUKFA5.js} +11 -10
  32. package/dist/{files-BRY5RRIM.js → files-CHPNEQXN.js} +5 -4
  33. package/dist/{join-PYA36HEC.js → join-KLRU72JJ.js} +10 -2
  34. package/dist/{message-delivery-JHMAJ2GU.js → message-delivery-TUXM3AAE.js} +15 -12
  35. package/dist/{mind-WF6QIXWV.js → mind-IX5AWUST.js} +13 -13
  36. package/dist/{mind-contacts-QYU4TPEA.js → mind-contacts-BKUOUJPV.js} +5 -5
  37. package/dist/{mind-history-OGNR4L7W.js → mind-history-RVF6KJ7K.js} +83 -11
  38. package/dist/{mind-list-SYRA7CH5.js → mind-list-KNU34SUD.js} +2 -2
  39. package/dist/{mind-service-DZCUUC7J.js → mind-service-WHFBUKZE.js} +11 -10
  40. package/dist/{mind-sleep-LZAFBDZD.js → mind-sleep-D3DEYAWG.js} +5 -5
  41. package/dist/{mind-status-Z2LQFA6F.js → mind-status-BPTLQ5Z6.js} +15 -3
  42. package/dist/{package-B6O5APCZ.js → package-BN5KMU54.js} +2 -2
  43. package/dist/{prompts-NBO3HRK3.js → prompts-HNKGCAEY.js} +1 -1
  44. package/dist/{reject-TWB4QJHG.js → reject-K2A4UK2Y.js} +9 -5
  45. package/dist/{scheduler-TNBN54ZK.js → scheduler-5X5UIQOQ.js} +7 -5
  46. package/dist/{seed-K2LYODIH.js → seed-LHZ33UB4.js} +1 -1
  47. package/dist/{seed-cmd-2EWBTR6P.js → seed-cmd-U4RVJ3UA.js} +2 -2
  48. package/dist/{seed-create-VGTLU5FD.js → seed-create-OQU4KNCB.js} +1 -1
  49. package/dist/{seed-readiness-DYOVCYHN.js → seed-readiness-WJYYRRCK.js} +2 -2
  50. package/dist/{seed-sprout-PILHJ3YS.js → seed-sprout-SFUYH2RG.js} +7 -7
  51. package/dist/{send-2YKOZJJ5.js → send-FWZAMDBN.js} +35 -20
  52. package/dist/{setup-MDPSK2XG.js → setup-RVSGCIF3.js} +1 -1
  53. package/dist/skills/memory/SKILL.md +47 -43
  54. package/dist/skills/volute-mind/references/variants.md +1 -1
  55. package/dist/{skills-NH2GQKAF.js → skills-QRPU6BWY.js} +1 -1
  56. package/dist/{sleep-manager-7JXEQQ2P.js → sleep-manager-NWUCRBZB.js} +11 -10
  57. package/dist/{spirit-25JVAL3M.js → spirit-I5ECLGRI.js} +16 -2
  58. package/dist/{spirit-availability-PJDWZJ3J.js → spirit-availability-NIUUYKU2.js} +12 -11
  59. package/dist/{sprout-EXUZMIOR.js → sprout-A2PMEF7Z.js} +1 -1
  60. package/dist/{system-events-ZPSH63QX.js → system-events-YTOU66QI.js} +7 -1
  61. package/dist/{systems-VDRUZXNF.js → systems-2DIJY4RF.js} +6 -6
  62. package/dist/{turn-tracker-TAT2HGK6.js → turn-tracker-NNHS6MAR.js} +4 -2
  63. package/dist/{up-J7XCAH7P.js → up-UGBHH743.js} +1 -1
  64. package/dist/{variant-cleanup-YXN25ZVW.js → variant-cleanup-CZDLIY3F.js} +7 -5
  65. package/dist/{version-notify-EN3KJ7HH.js → version-notify-SJVAQLZI.js} +4 -4
  66. package/dist/web-assets/assets/index-BHSU9r-4.css +1 -0
  67. package/dist/web-assets/assets/index-b7G7ZsVk.js +97 -0
  68. package/dist/web-assets/index.html +2 -2
  69. package/package.json +2 -2
  70. package/templates/_base/.init/.config/prompts.json +1 -1
  71. package/templates/_base/src/lib/auto-commit.ts +49 -11
  72. package/templates/_base/src/lib/codex-session-seed.ts +463 -0
  73. package/templates/_base/src/lib/daemon-client.ts +35 -0
  74. package/templates/_base/src/lib/pi-session-seed.ts +434 -0
  75. package/templates/_base/src/lib/seed-note.ts +78 -0
  76. package/templates/_base/src/lib/session-seed.ts +375 -0
  77. package/templates/_base/src/lib/startup.ts +108 -4
  78. package/templates/claude/.init/CLAUDE.md +3 -3
  79. package/templates/claude/src/agent.ts +267 -89
  80. package/templates/claude/src/lib/hooks/pre-compact.ts +30 -5
  81. package/templates/claude/src/lib/stream-consumer.ts +13 -0
  82. package/templates/claude/src/server.ts +2 -1
  83. package/templates/codex/.init/AGENTS.md +3 -3
  84. package/templates/codex/src/agent.ts +256 -39
  85. package/templates/codex/src/server.ts +2 -1
  86. package/templates/pi/.init/MINDS.md +3 -3
  87. package/templates/pi/src/agent.ts +269 -43
  88. package/templates/pi/src/lib/event-handler.ts +11 -0
  89. package/templates/pi/src/server.ts +3 -1
  90. package/dist/web-assets/assets/index-BOyJ4Gqs.css +0 -1
  91. package/dist/web-assets/assets/index-CrxBwnzp.js +0 -95
  92. package/dist/{backup-YVGV2ZNM.js → backup-WWXVIMYQ.js} +5 -5
  93. package/dist/{bridge-YK7NTRU5.js → bridge-KU2IKSH3.js} +3 -3
  94. package/dist/{channels-FSAHOQWI.js → channels-LW5ZLMSQ.js} +3 -3
  95. package/dist/{chunk-IWAVIJO2.js → chunk-BDHKCLWK.js} +0 -0
  96. package/dist/{chunk-EEOMA4DX.js → chunk-HTB7KDRU.js} +3 -3
  97. package/dist/{create-M727VUDF.js → create-C3XZACTV.js} +3 -3
  98. package/dist/{env-IBW2QI6K.js → env-BJGM6FBA.js} +5 -5
  99. package/dist/{extension-MRYJWSMV.js → extension-B2W52WZY.js} +3 -3
  100. package/dist/{list-UQGIW7OF.js → list-R2GDWOEE.js} +3 -3
  101. package/dist/{login-RJJTCVR3.js → login-EZ4RRDQX.js} +3 -3
  102. package/dist/{login-SB5OENMV.js → login-JDOAK465.js} +4 -4
  103. package/dist/{logout-YFZAJRPY.js → logout-K6ZGAJY5.js} +3 -3
  104. package/dist/{logout-BYV433X6.js → logout-MWSIUOQU.js} +3 -3
  105. package/dist/{mind-wake-LDGPRA42.js → mind-wake-DCDFFQKX.js} +3 -3
  106. package/dist/{read-RJ5NAW5J.js → read-J5RM674N.js} +3 -3
  107. package/dist/{register-OX4HRSGC.js → register-53LBGSDC.js} +3 -3
  108. package/dist/{restart-AGEGNSYQ.js → restart-R2WFNF5K.js} +4 -4
  109. package/dist/{skill-B5OJEQUA.js → skill-DXLLSUHB.js} +5 -5
  110. package/dist/{start-MUMNFR52.js → start-XDOUAWN3.js} +4 -4
  111. package/dist/{status-MWLE3RVC.js → status-EYG4A3OK.js} +3 -3
  112. package/dist/{stop-EKERNYTX.js → stop-O62RTEMD.js} +4 -4
@@ -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
  }
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  spiritDir
4
- } from "./chunk-VSBMLB2W.js";
4
+ } from "./chunk-5DBGQBQR.js";
5
5
  import {
6
6
  getScheduler
7
- } from "./chunk-Q7XQLKZB.js";
7
+ } from "./chunk-PIZDPY4G.js";
8
8
  import {
9
9
  getDeliveryManager
10
- } from "./chunk-7HN63Q7J.js";
10
+ } from "./chunk-RQRO6PC7.js";
11
11
  import {
12
12
  getMindManager,
13
13
  resolveMindToken
14
- } from "./chunk-VAEZCIJC.js";
14
+ } from "./chunk-23UJGVYP.js";
15
15
  import {
16
16
  extractTextContent,
17
17
  getRoutingConfig,
@@ -23,14 +23,14 @@ import {
23
23
  importSkillFromDir,
24
24
  removeSharedSkill,
25
25
  sharedSkillsDir
26
- } from "./chunk-QT4D5XCI.js";
26
+ } from "./chunk-TAWRJ24T.js";
27
27
  import {
28
28
  getOrCreateMindUser,
29
29
  getOrCreateSystemUser,
30
30
  getUser,
31
31
  getUserByUsername,
32
32
  syncMindProfile
33
- } from "./chunk-HKP6OAJP.js";
33
+ } from "./chunk-7LT3YZUR.js";
34
34
  import {
35
35
  MIND_LEVEL_THREAD,
36
36
  deliverEvent,
@@ -38,13 +38,13 @@ import {
38
38
  pendingEventCount,
39
39
  pendingEventsLine,
40
40
  recordNotice
41
- } from "./chunk-ICL6T4SN.js";
41
+ } from "./chunk-IQ7VNWH6.js";
42
42
  import {
43
43
  publish as publish2
44
44
  } from "./chunk-LNNI3MS4.js";
45
45
  import {
46
46
  getPrompt
47
- } from "./chunk-FXWBFDQR.js";
47
+ } from "./chunk-AP2KQMMW.js";
48
48
  import {
49
49
  addMessage,
50
50
  createChannel,
@@ -891,8 +891,16 @@ async function startMindFull(name) {
891
891
  (err) => logger_default.error(`failed to ensure system DM for ${baseName}`, logger_default.errorData(err))
892
892
  );
893
893
  const dir = mindDir(baseName);
894
- getScheduler().loadSchedules(baseName);
895
- getSleepManagerIfReady()?.loadSleepConfig(baseName);
894
+ try {
895
+ getScheduler().loadSchedules(baseName);
896
+ } catch (err) {
897
+ logger_default.error(`failed to load schedules for ${baseName}`, logger_default.errorData(err));
898
+ }
899
+ try {
900
+ getSleepManagerIfReady()?.loadSleepConfig(baseName);
901
+ } catch (err) {
902
+ logger_default.error(`failed to load sleep config for ${baseName}`, logger_default.errorData(err));
903
+ }
896
904
  ensureMailAddress(baseName).catch(
897
905
  (err) => logger_default.error(`failed to ensure mail address for ${baseName}`, logger_default.errorData(err))
898
906
  );
@@ -906,16 +914,24 @@ async function startMindFull(name) {
906
914
  (err) => logger_default.warn(`failed to join #system for ${baseName}`, logger_default.errorData(err))
907
915
  );
908
916
  if (config?.tokenBudget) {
909
- getTokenBudget().setBudget(
910
- baseName,
911
- config.tokenBudget,
912
- config.tokenBudgetPeriodMinutes ?? DEFAULT_BUDGET_PERIOD_MINUTES
913
- );
917
+ try {
918
+ getTokenBudget().setBudget(
919
+ baseName,
920
+ config.tokenBudget,
921
+ config.tokenBudgetPeriodMinutes ?? DEFAULT_BUDGET_PERIOD_MINUTES
922
+ );
923
+ } catch (err) {
924
+ logger_default.error(`failed to set token budget for ${baseName}`, logger_default.errorData(err));
925
+ }
926
+ }
927
+ try {
928
+ notifyExtensionsMindStart(baseName);
929
+ } catch (err) {
930
+ logger_default.error(`failed to notify extensions of mind start for ${baseName}`, logger_default.errorData(err));
914
931
  }
915
- notifyExtensionsMindStart(baseName);
916
932
  }
917
933
  async function recordMissingCredentialsNotice(mind, template) {
918
- const { hasUndeliveredEvent, recordNotice: recordNotice2, MIND_LEVEL_THREAD: MIND_LEVEL_THREAD2 } = await import("./system-events-ZPSH63QX.js");
934
+ const { hasUndeliveredEvent, recordNotice: recordNotice2, MIND_LEVEL_THREAD: MIND_LEVEL_THREAD2 } = await import("./system-events-YTOU66QI.js");
919
935
  const warning = await missingCredentialWarning(template, readMindModel(mind, template), mind);
920
936
  if (!warning) return;
921
937
  if (await hasUndeliveredEvent(mind, "no_credentials")) return;
@@ -992,7 +1008,7 @@ function buildSeedOrientation(createdBy, creatorType) {
992
1008
  async function sendSeedOrientation(mindName, createdBy) {
993
1009
  let creatorType;
994
1010
  if (createdBy) {
995
- const { getUserByUsername: getUserByUsername2 } = await import("./auth-X6EMNEGB.js");
1011
+ const { getUserByUsername: getUserByUsername2 } = await import("./auth-KRDSM55Q.js");
996
1012
  creatorType = (await getUserByUsername2(createdBy))?.user_type;
997
1013
  }
998
1014
  await deliverEvent(mindName, {
@@ -1001,7 +1017,7 @@ async function sendSeedOrientation(mindName, createdBy) {
1001
1017
  });
1002
1018
  }
1003
1019
  async function ensureCreatorDM(mindName, creatorUsername) {
1004
- const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-X6EMNEGB.js");
1020
+ const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-KRDSM55Q.js");
1005
1021
  const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-ZL6PHROD.js");
1006
1022
  const mindUser = await getOrCreateMindUser2(mindName);
1007
1023
  const creatorUser = await getUserByUsername2(creatorUsername);
@@ -1174,6 +1190,13 @@ var SleepManager = class {
1174
1190
  this.transitioning.add(name);
1175
1191
  state.wokenByTrigger = false;
1176
1192
  try {
1193
+ await recordNotice({
1194
+ mind: name,
1195
+ thread: MIND_LEVEL_THREAD,
1196
+ kind: "crash",
1197
+ reason: "trigger_wake_crash",
1198
+ detail: await getPrompt("trigger_wake_crash_notice")
1199
+ });
1177
1200
  try {
1178
1201
  await sleepMind(name);
1179
1202
  } catch (err) {
@@ -1230,6 +1253,15 @@ var SleepManager = class {
1230
1253
  const wakeTime = formatWakeTime(wakeAt);
1231
1254
  const preSleepMsg = await getPrompt("pre_sleep", { wakeTime });
1232
1255
  const { delivered } = await deliverEvent(name, { type: "sleep", body: preSleepMsg });
1256
+ if (!delivered) {
1257
+ await recordNotice({
1258
+ mind: name,
1259
+ thread: MIND_LEVEL_THREAD,
1260
+ kind: "delivery_failed",
1261
+ reason: "pre_sleep_failed",
1262
+ detail: await getPrompt("pre_sleep_failure_notice")
1263
+ });
1264
+ }
1233
1265
  if (delivered) await this.waitForIdle(name, 12e4);
1234
1266
  await new Promise((r) => setTimeout(r, 3e3));
1235
1267
  await sleepMind(name);
@@ -1420,7 +1452,7 @@ var SleepManager = class {
1420
1452
  * the real delivery stack. Returns whether the batch was handed off to the mind.
1421
1453
  */
1422
1454
  async deliverQueuedBatch(name, payloads) {
1423
- const { deliverBatch: deliverBatch2 } = await import("./message-delivery-JHMAJ2GU.js");
1455
+ const { deliverBatch: deliverBatch2 } = await import("./message-delivery-TUXM3AAE.js");
1424
1456
  return deliverBatch2(name, payloads);
1425
1457
  }
1426
1458
  // --- Wake failure handling (#419) ---
@@ -1647,6 +1679,22 @@ var SleepManager = class {
1647
1679
  }
1648
1680
  }
1649
1681
  }
1682
+ const codexSessionsDir = resolve4(dir, ".mind", "codex-sessions");
1683
+ if (existsSync4(codexSessionsDir)) {
1684
+ const archiveDir = resolve4(codexSessionsDir, "archive");
1685
+ mkdirSync2(archiveDir, { recursive: true });
1686
+ for (const file of readdirSync(codexSessionsDir)) {
1687
+ if (file === "archive" || !file.endsWith(".json")) continue;
1688
+ const src = resolve4(codexSessionsDir, file);
1689
+ const base = file.replace(/\.json$/, "");
1690
+ const dest = resolve4(archiveDir, `${base}-${timestamp}.json`);
1691
+ try {
1692
+ renameSync(src, dest);
1693
+ } catch (err) {
1694
+ slog.warn(`failed to archive codex-session ${file} for ${name}`, logger_default.errorData(err));
1695
+ }
1696
+ }
1697
+ }
1650
1698
  }
1651
1699
  async runWakeContextScript(name, sleepingSince, duration) {
1652
1700
  const scriptPath = resolve4(mindDir(name), "home", ".local", "hooks", "wake-context.sh");
@@ -1945,6 +1993,10 @@ function willGate(baseName, payload) {
1945
1993
  });
1946
1994
  return shouldGate(config, route);
1947
1995
  }
1996
+ async function willGateMessage(mindName, payload) {
1997
+ const baseName = await getBaseName(mindName);
1998
+ return willGate(baseName, payload);
1999
+ }
1948
2000
  async function deliverMessage(mindName, payload, opts = {}) {
1949
2001
  try {
1950
2002
  const baseName = await getBaseName(mindName);
@@ -2894,6 +2946,7 @@ export {
2894
2946
  recordOutbound,
2895
2947
  linkToolResultToTurn,
2896
2948
  resolveSleepAction,
2949
+ willGateMessage,
2897
2950
  deliverMessage,
2898
2951
  deliverBatch,
2899
2952
  ensureSystemChannel,
@@ -17,7 +17,7 @@ import {
17
17
  } from "./chunk-2RNMBXD2.js";
18
18
 
19
19
  // packages/daemon/src/lib/chat/system-events.ts
20
- import { and, asc, desc, eq, gt, isNull, lte, or, sql } from "drizzle-orm";
20
+ import { and, asc, desc, eq, gt, isNotNull, isNull, lt, lte, or, sql } from "drizzle-orm";
21
21
 
22
22
  // packages/daemon/src/lib/util/session-name.ts
23
23
  function newEphemeralSession() {
@@ -29,6 +29,7 @@ var elog = logger_default.child("system-events");
29
29
  var MIND_LEVEL_THREAD = "";
30
30
  var MAX_NEXT_TURN_EVENTS = 100;
31
31
  var FLUSH_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
32
+ var EVENT_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
32
33
  function parseMeta(meta, context) {
33
34
  if (!meta) return {};
34
35
  try {
@@ -93,6 +94,10 @@ function eventLabel(type, meta) {
93
94
  return s("reason") ? `Notice: ${s("reason")}` : "Notice";
94
95
  case "delivery_failed":
95
96
  return "Delivery failed";
97
+ case "context_lost":
98
+ return "Context lost";
99
+ case "join_blocked":
100
+ return "Variant join blocked";
96
101
  default:
97
102
  return "Notice";
98
103
  }
@@ -100,6 +105,16 @@ function eventLabel(type, meta) {
100
105
  return type.charAt(0).toUpperCase() + type.slice(1);
101
106
  }
102
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
+ ];
103
118
  async function recordNotice(input) {
104
119
  const type = input.kind === "budget" ? "budget" : "notice";
105
120
  await deliverEvent(input.mind, {
@@ -108,6 +123,7 @@ async function recordNotice(input) {
108
123
  thread: input.thread,
109
124
  delivery: "next-turn",
110
125
  meta: {
126
+ ...input.meta ?? {},
111
127
  subtype: input.kind,
112
128
  reason: input.reason,
113
129
  ...input.raw ? { raw: input.raw } : {}
@@ -241,7 +257,7 @@ async function deliverEvent(mind, input) {
241
257
  return { id: eventId, delivered: false };
242
258
  }
243
259
  const baseName = await getBaseName(mind);
244
- const { getSleepManagerIfReady } = await import("./sleep-manager-7JXEQQ2P.js");
260
+ const { getSleepManagerIfReady } = await import("./sleep-manager-NWUCRBZB.js");
245
261
  const sleepManager = getSleepManagerIfReady();
246
262
  const sleeping = !input.force && sleepManager?.isSleeping(baseName);
247
263
  const behavior = input.whileSleeping ?? "queue";
@@ -325,6 +341,11 @@ async function pendingEventCount(mind) {
325
341
  return 0;
326
342
  }
327
343
  }
344
+ async function cleanExpiredEvents() {
345
+ const db = await getDb();
346
+ const cutoff = new Date(Date.now() - EVENT_RETENTION_MS).toISOString().replace("T", " ").slice(0, 19);
347
+ await db.delete(systemEvents).where(and(isNotNull(systemEvents.delivered_at), lt(systemEvents.created_at, cutoff)));
348
+ }
328
349
  function pendingEventsLine(count) {
329
350
  if (count <= 0) return "";
330
351
  return `${count} system event${count === 1 ? "" : "s"} arrived while you slept and will follow shortly.`;
@@ -544,14 +565,17 @@ export {
544
565
  MIND_LEVEL_THREAD,
545
566
  parseMeta,
546
567
  eventLabel,
568
+ NOTICE_KINDS,
547
569
  recordNotice,
548
570
  eventChannel,
549
571
  deliverEvent,
550
572
  flushQueuedEvents,
551
573
  pendingEventCount,
574
+ cleanExpiredEvents,
552
575
  pendingEventsLine,
553
576
  drainEvents,
554
577
  formatEvents,
578
+ localHM,
555
579
  clearDeliveredEvents,
556
580
  latestFailureEvent,
557
581
  latestEvent,
@@ -6,6 +6,10 @@ import {
6
6
  import {
7
7
  resolveMindName
8
8
  } from "./chunk-BTY4WNFE.js";
9
+ import {
10
+ command,
11
+ subcommands
12
+ } from "./chunk-TXSA4Q3V.js";
9
13
  import {
10
14
  daemonFetch
11
15
  } from "./chunk-M74I3HHF.js";
@@ -13,10 +17,6 @@ import {
13
17
  getClient,
14
18
  urlOf
15
19
  } from "./chunk-LOPXTW6H.js";
16
- import {
17
- command,
18
- subcommands
19
- } from "./chunk-TXSA4Q3V.js";
20
20
 
21
21
  // packages/cli/src/commands/clock.ts
22
22
  import { CronExpressionParser } from "cron-parser";
@@ -279,11 +279,11 @@ 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-D3DEYAWG.js").then((m) => m.run(args))
283
283
  },
284
284
  wake: {
285
285
  description: "Wake a sleeping mind",
286
- run: (args) => import("./mind-wake-LDGPRA42.js").then((m) => m.run(args))
286
+ run: (args) => import("./mind-wake-DCDFFQKX.js").then((m) => m.run(args))
287
287
  }
288
288
  },
289
289
  footer: "Use --mind <name> or VOLUTE_MIND to identify the mind."
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  getSleepManagerIfReady,
4
4
  startSpiritFull
5
- } from "./chunk-VP5PFNZO.js";
5
+ } from "./chunk-HASADR3E.js";
6
6
  import {
7
7
  spiritCreationInProgress
8
- } from "./chunk-VSBMLB2W.js";
8
+ } from "./chunk-5DBGQBQR.js";
9
9
  import {
10
10
  MindStartupError,
11
11
  tryGetMindManager
12
- } from "./chunk-VAEZCIJC.js";
12
+ } from "./chunk-23UJGVYP.js";
13
13
  import {
14
14
  logger_default
15
15
  } from "./chunk-QUSMYGNQ.js";
@@ -6,10 +6,15 @@ import {
6
6
  getMindToken,
7
7
  loadJsonMap,
8
8
  saveJsonMapAsync
9
- } from "./chunk-VAEZCIJC.js";
9
+ } from "./chunk-23UJGVYP.js";
10
10
  import {
11
- deliverEvent
12
- } from "./chunk-ICL6T4SN.js";
11
+ MIND_LEVEL_THREAD,
12
+ deliverEvent,
13
+ recordNotice
14
+ } from "./chunk-IQ7VNWH6.js";
15
+ import {
16
+ getPrompt
17
+ } from "./chunk-AP2KQMMW.js";
13
18
  import {
14
19
  isSandboxEnabled,
15
20
  wrapForSandbox
@@ -197,6 +202,18 @@ ${stderr}` : ""}`;
197
202
  }
198
203
  } catch (err) {
199
204
  slog.warn(`failed to fire "${schedule.id}" for ${mindName}`, logger_default.errorData(err));
205
+ const detail = await getPrompt("schedule_failure_notice", {
206
+ id: schedule.id,
207
+ reason: err instanceof Error && err.message ? err.message : String(err)
208
+ });
209
+ const thread = schedule.thread && !schedule.thread.startsWith("new-") ? schedule.thread : MIND_LEVEL_THREAD;
210
+ await recordNotice({
211
+ mind: mindName,
212
+ thread,
213
+ kind: "delivery_failed",
214
+ reason: "schedule_failed",
215
+ detail
216
+ });
200
217
  }
201
218
  }
202
219
  removeSchedule(mindName, scheduleId) {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  MEMORY_PLACEHOLDER_MARKER,
4
4
  ORIENTATION_MARKER
5
- } from "./chunk-FXWBFDQR.js";
5
+ } from "./chunk-AP2KQMMW.js";
6
6
  import {
7
7
  safeResolveWithinBase
8
8
  } from "./chunk-DW4TRDOZ.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  tryGetMindManager
4
- } from "./chunk-VAEZCIJC.js";
4
+ } from "./chunk-23UJGVYP.js";
5
5
  import {
6
6
  extractTextContent,
7
7
  getRoutingConfig,
@@ -12,11 +12,11 @@ import {
12
12
  } from "./chunk-UDZLPR34.js";
13
13
  import {
14
14
  linkInboundToActiveTurn
15
- } from "./chunk-CDKQJWGK.js";
15
+ } from "./chunk-VDZ4I4ZP.js";
16
16
  import {
17
17
  MIND_LEVEL_THREAD,
18
18
  newEphemeralSession
19
- } from "./chunk-ICL6T4SN.js";
19
+ } from "./chunk-IQ7VNWH6.js";
20
20
  import {
21
21
  publish
22
22
  } from "./chunk-LNNI3MS4.js";
@@ -36,6 +36,9 @@ import {
36
36
  import {
37
37
  logger_default
38
38
  } from "./chunk-QUSMYGNQ.js";
39
+ import {
40
+ slugify
41
+ } from "./chunk-LGNUFVMR.js";
39
42
  import {
40
43
  findMind,
41
44
  getBaseName,
@@ -243,14 +246,22 @@ var DeliveryManager = class {
243
246
  isMindRunning = (name) => tryGetMindManager()?.isRunning(name) ?? false;
244
247
  /** Delivers a channel event to a mind (invites, release summaries); overridable in tests. */
245
248
  notify = async (mindName, text) => {
246
- const { deliverEvent } = await import("./system-events-ZPSH63QX.js");
249
+ const { deliverEvent } = await import("./system-events-YTOU66QI.js");
247
250
  await deliverEvent(mindName, { type: "channel", body: text });
248
251
  };
249
252
  /** Surfaces a dead-lettered delivery as a next-turn failure notice; overridable in tests. */
250
253
  notifyFailure = async (input) => {
251
- const { recordNotice } = await import("./system-events-ZPSH63QX.js");
254
+ const { recordNotice } = await import("./system-events-YTOU66QI.js");
252
255
  await recordNotice(input);
253
256
  };
257
+ /**
258
+ * Tells a mind *sender* its message was dropped (dead-lettered on the recipient's
259
+ * side); no-ops for human senders. Overridable in tests.
260
+ */
261
+ notifySenderFailure = async (sender, channel, reason) => {
262
+ const { recordSenderDeliveryFailure } = await import("./delivery-notices-65TB3CBX.js");
263
+ await recordSenderDeliveryFailure(sender, channel, reason);
264
+ };
254
265
  constructor() {
255
266
  setRoutesChangeListener((mind) => {
256
267
  this.releaseGated(mind).catch(
@@ -270,6 +281,10 @@ var DeliveryManager = class {
270
281
  setFailureNotifier(fn) {
271
282
  this.notifyFailure = fn;
272
283
  }
284
+ /** Test seam: capture/override sender-side dead-letter notices. */
285
+ setSenderFailureNotifier(fn) {
286
+ this.notifySenderFailure = fn;
287
+ }
273
288
  // --- Public API ---
274
289
  /**
275
290
  * Route and deliver a message to a mind. This is the main entry point.
@@ -875,6 +890,25 @@ var DeliveryManager = class {
875
890
  } catch (err) {
876
891
  dlog.warn(`failed to send dead-letter notice for ${mind}`, logger_default.errorData(err));
877
892
  }
893
+ const senderPairs = /* @__PURE__ */ new Map();
894
+ for (const r of rows) {
895
+ if (!r.sender || r.sender === r.mind) continue;
896
+ const recipient = r.target_mind ?? r.mind;
897
+ const channel = !r.channel || r.channel === `@${slugify(r.sender)}` ? `@${recipient}` : r.channel;
898
+ senderPairs.set(`${r.sender}
899
+ ${channel}`, { sender: r.sender, channel });
900
+ }
901
+ for (const { sender, channel } of senderPairs.values()) {
902
+ try {
903
+ await this.notifySenderFailure(
904
+ sender,
905
+ channel,
906
+ `the recipient's process rejected it after ${MAX_DELIVERY_ATTEMPTS} attempts`
907
+ );
908
+ } catch (err) {
909
+ dlog.warn(`failed to send sender dead-letter notice for ${sender}`, logger_default.errorData(err));
910
+ }
911
+ }
878
912
  }
879
913
  async deliverToMind(mindName, session, payload, sessionConfig, queueId) {
880
914
  if (queueId != null) this.inFlight.add(queueId);
@@ -1130,7 +1164,7 @@ var DeliveryManager = class {
1130
1164
  const details = detailLines.length > 0 ? `${detailLines.join("\n")}
1131
1165
 
1132
1166
  ` : "\n";
1133
- const { getPrompt } = await import("./prompts-NBO3HRK3.js");
1167
+ const { getPrompt } = await import("./prompts-HNKGCAEY.js");
1134
1168
  const notification = await getPrompt("channel_invite", {
1135
1169
  channel,
1136
1170
  heldLine,
@@ -45,7 +45,7 @@ async function initDefaultSkills() {
45
45
  const config = readGlobalConfig();
46
46
  let extensionSkills = [];
47
47
  try {
48
- const { getExtensionStandardSkills } = await import("./extensions-NY6WST33.js");
48
+ const { getExtensionStandardSkills } = await import("./extensions-VACUKFA5.js");
49
49
  extensionSkills = getExtensionStandardSkills();
50
50
  } catch (err) {
51
51
  logger_default.warn("failed to load extension standard skills during init", logger_default.errorData(err));
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getMindManager
4
- } from "./chunk-VAEZCIJC.js";
4
+ } from "./chunk-23UJGVYP.js";
5
5
  import {
6
6
  chownMindDir,
7
7
  gitExec
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ MIND_LEVEL_THREAD,
4
+ localHM,
5
+ parseMeta,
6
+ recordNotice
7
+ } from "./chunk-IQ7VNWH6.js";
8
+ import {
9
+ getPrompt
10
+ } from "./chunk-AP2KQMMW.js";
11
+ import {
12
+ logger_default
13
+ } from "./chunk-QUSMYGNQ.js";
14
+ import {
15
+ findMind,
16
+ getDb
17
+ } from "./chunk-H6X6CKDN.js";
18
+ import {
19
+ systemEvents
20
+ } from "./chunk-2RNMBXD2.js";
21
+
22
+ // packages/daemon/src/lib/chat/delivery-notices.ts
23
+ import { and, desc, eq, isNull, sql } from "drizzle-orm";
24
+ var nlog = logger_default.child("delivery-notices");
25
+ var COALESCE_WINDOW_MINUTES = 15;
26
+ var recordChains = /* @__PURE__ */ new Map();
27
+ async function recordDeliveryFailure(input) {
28
+ const key = `${input.mind}
29
+ ${input.channel}`;
30
+ const next = (recordChains.get(key) ?? Promise.resolve()).then(
31
+ () => recordDeliveryFailureUnserialized(input)
32
+ );
33
+ recordChains.set(key, next);
34
+ await next;
35
+ if (recordChains.get(key) === next) recordChains.delete(key);
36
+ }
37
+ async function recordDeliveryFailureUnserialized(input) {
38
+ const thread = input.thread ?? MIND_LEVEL_THREAD;
39
+ try {
40
+ const db = await getDb();
41
+ const existing = await db.select().from(systemEvents).where(
42
+ and(
43
+ eq(systemEvents.mind, input.mind),
44
+ eq(systemEvents.type, "notice"),
45
+ eq(systemEvents.delivery, "next-turn"),
46
+ eq(systemEvents.thread, thread),
47
+ isNull(systemEvents.delivered_at),
48
+ // Literal subtype + bound channel value — not string-built SQL.
49
+ sql`json_extract(${systemEvents.meta}, '$.subtype') = 'delivery_failed'`,
50
+ sql`json_extract(${systemEvents.meta}, '$.channel') = ${input.channel}`,
51
+ sql`${systemEvents.created_at} > datetime('now', ${`-${COALESCE_WINDOW_MINUTES} minutes`})`
52
+ )
53
+ ).orderBy(desc(systemEvents.id)).limit(1).get();
54
+ if (existing) {
55
+ const meta = parseMeta(existing.meta, `event ${existing.id}`);
56
+ const count = (typeof meta.count === "number" ? meta.count : 1) + 1;
57
+ const body2 = await getPrompt("delivery_failure_coalesced", {
58
+ count: String(count),
59
+ channel: input.channel,
60
+ since: localHM(existing.created_at),
61
+ reason: input.reason
62
+ });
63
+ await db.update(systemEvents).set({ body: body2, meta: JSON.stringify({ ...meta, count }) }).where(eq(systemEvents.id, existing.id));
64
+ return;
65
+ }
66
+ const body = await getPrompt("delivery_failure_notice", {
67
+ channel: input.channel,
68
+ reason: input.reason
69
+ });
70
+ await recordNotice({
71
+ mind: input.mind,
72
+ thread,
73
+ kind: "delivery_failed",
74
+ reason: "delivery_failed",
75
+ detail: body,
76
+ meta: { channel: input.channel, count: 1 }
77
+ });
78
+ } catch (err) {
79
+ nlog.warn(
80
+ `failed to record delivery-failure notice for ${input.mind} \u2192 ${input.channel}`,
81
+ logger_default.errorData(err)
82
+ );
83
+ }
84
+ }
85
+ async function recordSenderDeliveryFailure(sender, channel, reason) {
86
+ try {
87
+ const entry = await findMind(sender);
88
+ if (!entry) return;
89
+ await recordDeliveryFailure({ mind: entry.parent ?? entry.name, channel, reason });
90
+ } catch (err) {
91
+ nlog.warn(
92
+ `failed to record sender delivery-failure notice for ${sender} \u2192 ${channel}`,
93
+ logger_default.errorData(err)
94
+ );
95
+ }
96
+ }
97
+
98
+ export {
99
+ COALESCE_WINDOW_MINUTES,
100
+ recordDeliveryFailure,
101
+ recordSenderDeliveryFailure
102
+ };