volute 0.48.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 (137) hide show
  1. package/README.md +79 -328
  2. package/dist/{activity-events-MIOK3IOU.js → activity-events-GJJAI2WT.js} +4 -4
  3. package/dist/{ai-service-643TNFPH.js → ai-service-A7FS7EC3.js} +5 -5
  4. package/dist/{archive-6Z4GFQPH.js → archive-7E3YE6VF.js} +3 -3
  5. package/dist/{auth-A4VGHADI.js → auth-ABYLOEZ7.js} +5 -5
  6. package/dist/{avatar-image-Y45R7LW5.js → avatar-image-6ATV2IOZ.js} +4 -4
  7. package/dist/{backup-UDH4GMSW.js → backup-XYAMT4CU.js} +15 -15
  8. package/dist/{chat-X43VYHH2.js → chat-OGXAEXMV.js} +2 -2
  9. package/dist/{chunk-LTLFOSA4.js → chunk-3B427GUK.js} +3 -3
  10. package/dist/{chunk-XVBSFLM7.js → chunk-4B62E4CG.js} +2 -2
  11. package/dist/{chunk-ODDGGLM6.js → chunk-4DSGN4FL.js} +3 -3
  12. package/dist/{chunk-PJVPDY2Z.js → chunk-6HGEZJDH.js} +2 -2
  13. package/dist/{chunk-MJRXFUMS.js → chunk-DAIIMBLB.js} +3 -3
  14. package/dist/{chunk-DGGZVZTM.js → chunk-DVN6DNMI.js} +1 -1
  15. package/dist/chunk-EADX54RG.js +113 -0
  16. package/dist/{chunk-EIQDZC43.js → chunk-EFT2OALG.js} +6 -2
  17. package/dist/{chunk-OQJMTHEH.js → chunk-EG5LSKHU.js} +2 -2
  18. package/dist/{chunk-WQ46E33M.js → chunk-FBBS4BVR.js} +2 -2
  19. package/dist/{chunk-HUNDXZBZ.js → chunk-GWVFDUNB.js} +45 -14
  20. package/dist/{chunk-FJ2MZ5PH.js → chunk-HCYQRME2.js} +2 -2
  21. package/dist/{chunk-T4VS7UDW.js → chunk-IH5TKXX6.js} +9 -9
  22. package/dist/{chunk-Q7HOAVFR.js → chunk-JBIFXB6I.js} +4 -4
  23. package/dist/{chunk-WARH6GIV.js → chunk-JCBPU57A.js} +12 -10
  24. package/dist/{chunk-VZRA5TYO.js → chunk-JCWB2ITW.js} +2 -2
  25. package/dist/{chunk-MOERAQXH.js → chunk-JPGHSXSV.js} +18 -25
  26. package/dist/{chunk-USD45JKR.js → chunk-K3JOLGWF.js} +7 -7
  27. package/dist/{chunk-3YUBFTDN.js → chunk-KHCKZ36O.js} +1 -1
  28. package/dist/{chunk-7KRIHYLR.js → chunk-L27YYONJ.js} +2 -2
  29. package/dist/{chunk-SZJCJVXV.js → chunk-MG3Z34YE.js} +2 -2
  30. package/dist/{chunk-5KXCM5AW.js → chunk-NJHN6IRB.js} +9 -1
  31. package/dist/{chunk-2OIYNHWO.js → chunk-NOIAHMJB.js} +1 -1
  32. package/dist/{chunk-2MJ7EOMT.js → chunk-P4LHVRNE.js} +1 -0
  33. package/dist/{chunk-IJPMIEVE.js → chunk-PY24ZL62.js} +1 -1
  34. package/dist/{chunk-VB2QXUJQ.js → chunk-QQBOI3QA.js} +202 -117
  35. package/dist/{chunk-T2TP6ZC6.js → chunk-QUSMYGNQ.js} +6 -0
  36. package/dist/{chunk-WW6C3PLV.js → chunk-RSBL3FAD.js} +39 -6
  37. package/dist/{chunk-YKA7DCVW.js → chunk-SX72DOO3.js} +1 -1
  38. package/dist/{chunk-4FCL4DVZ.js → chunk-TXJL36ZQ.js} +2 -2
  39. package/dist/{chunk-HNGFKPNS.js → chunk-VIUH5CHQ.js} +55 -23
  40. package/dist/{chunk-HPGN6XRT.js → chunk-VNRSXEBF.js} +4 -4
  41. package/dist/{chunk-IYCV34IU.js → chunk-XTEKINWG.js} +3 -3
  42. package/dist/{chunk-YNBXEQAP.js → chunk-YIJGKHIS.js} +3 -3
  43. package/dist/{chunk-MTRMQFCQ.js → chunk-ZX46PGCC.js} +2 -2
  44. package/dist/cli.js +20 -20
  45. package/dist/{clock-CNRF6F7T.js → clock-6JZL2FS6.js} +2 -2
  46. package/dist/{cloud-sync-WHKCB4CD.js → cloud-sync-VFDTNNCA.js} +20 -20
  47. package/dist/{conversations-C7IKYSHD.js → conversations-TUM5PJJ4.js} +5 -5
  48. package/dist/{create-GJKHABJB.js → create-Z5FSDNVZ.js} +1 -1
  49. package/dist/{daemon-restart-7KUHVZIN.js → daemon-restart-54L4B6AQ.js} +7 -7
  50. package/dist/daemon.js +647 -410
  51. package/dist/{db-FO6LXYCW.js → db-F6UMFRYD.js} +2 -2
  52. package/dist/delivery-manager-RBXBPVWM.js +36 -0
  53. package/dist/{delivery-router-3QFG2ZEX.js → delivery-router-VUOL5YSA.js} +8 -6
  54. package/dist/{down-G6IADJXV.js → down-6X7KX7BZ.js} +5 -5
  55. package/dist/echo-text-B6NZ5FGE.js +36 -0
  56. package/dist/{env-6TGPO5WG.js → env-KUL4XKN2.js} +3 -3
  57. package/dist/{exec-QIFYZG4C.js → exec-FBXA3CM4.js} +3 -3
  58. package/dist/{export-C2K3NIRD.js → export-TNBPDH77.js} +3 -3
  59. package/dist/{extensions-GEKF47A4.js → extensions-AWNO3SZF.js} +20 -20
  60. package/dist/{files-LXQSQOFG.js → files-BVBRJJ2P.js} +3 -3
  61. package/dist/{import-SX3ZXHB4.js → import-ZUNKMCKC.js} +7 -7
  62. package/dist/{isolation-MCT4ZYLT.js → isolation-OPCZQRTH.js} +3 -3
  63. package/dist/{login-CL4IT2ED.js → login-KAKNFCW5.js} +6 -6
  64. package/dist/{logout-HZYJCYCA.js → logout-RLT62RQP.js} +2 -2
  65. package/dist/message-delivery-ABPV76EA.js +42 -0
  66. package/dist/{mind-46AOK373.js → mind-IF6CGMFW.js} +12 -12
  67. package/dist/{mind-activity-tracker-YVZPC4A5.js → mind-activity-tracker-YMMERB6B.js} +5 -5
  68. package/dist/mind-service-42GDO4KH.js +44 -0
  69. package/dist/{mind-sleep-DV7KP255.js → mind-sleep-VBV7YRSU.js} +2 -2
  70. package/dist/{notices-2KU5NTRY.js → notices-N6BCGHFV.js} +4 -4
  71. package/dist/{package-RTN2PS7O.js → package-PD67VPHB.js} +5 -4
  72. package/dist/prompts-MDHDLWEX.js +24 -0
  73. package/dist/{registry-UMMDRPRM.js → registry-3ELJPUGJ.js} +6 -2
  74. package/dist/{sandbox-5VQ454XP.js → sandbox-XPYA7UIF.js} +5 -5
  75. package/dist/scheduler-K7Y7Z7FQ.js +34 -0
  76. package/dist/{schema-VEVO7HG6.js → schema-6YONVTKZ.js} +1 -1
  77. package/dist/{seed-F2HXZQU4.js → seed-NQED7PSX.js} +1 -1
  78. package/dist/{seed-cmd-MLCTGSX5.js → seed-cmd-QF3F2G4S.js} +2 -2
  79. package/dist/{seed-create-KQ7OILWB.js → seed-create-XOO4N2OE.js} +3 -3
  80. package/dist/{seed-readiness-RBPGOLSZ.js → seed-readiness-3LRYASA4.js} +5 -5
  81. package/dist/{seed-sprout-HVYCFJLA.js → seed-sprout-HSBBGSS2.js} +11 -11
  82. package/dist/{send-TD2X5U3K.js → send-IHRJYNXA.js} +6 -6
  83. package/dist/{service-CVZMQYK6.js → service-YCOPCTSZ.js} +4 -4
  84. package/dist/{service-install-VCRMFWX5.js → service-install-Y46OTF55.js} +5 -5
  85. package/dist/{setup-AE4CVUC4.js → setup-F4TE64YI.js} +7 -7
  86. package/dist/{setup-5ECWTYKG.js → setup-TCN7VH5U.js} +5 -3
  87. package/dist/skills/volute-mind/SKILL.md +3 -3
  88. package/dist/skills/volute-mind/references/routing.md +6 -5
  89. package/dist/skills/volute-mind/references/variants.md +48 -14
  90. package/dist/{skills-B3SVDCEB.js → skills-NN5UZCUU.js} +8 -6
  91. package/dist/sleep-manager-DSPXFHGX.js +38 -0
  92. package/dist/spirit-SSJMYC6A.js +31 -0
  93. package/dist/{split-SKZ4Y2D6.js → split-JF2VWTXA.js} +4 -1
  94. package/dist/{sprout-KQ43BWZJ.js → sprout-AXA2SECQ.js} +1 -1
  95. package/dist/{status-F4PGREUU.js → status-VJGXMQ4L.js} +5 -5
  96. package/dist/system-chat-M2XOBKEM.js +40 -0
  97. package/dist/{systems-JJNGQHAO.js → systems-5MH2ELQE.js} +2 -2
  98. package/dist/{tailscale-MCLY5GYE.js → tailscale-AMMVKDXB.js} +3 -3
  99. package/dist/{template-staleness-EMQORSGG.js → template-staleness-HKGCTS3H.js} +4 -4
  100. package/dist/{turn-tracker-KLPV3PAU.js → turn-tracker-JQ2YUUY2.js} +7 -7
  101. package/dist/{up-RVGDE6CQ.js → up-SDLTLPAO.js} +6 -6
  102. package/dist/{update-OXQEGGXD.js → update-WEA4N2TR.js} +5 -5
  103. package/dist/{update-check-266R4ZSY.js → update-check-DZOTONII.js} +3 -3
  104. package/dist/variant-cleanup-P4MBNLJD.js +33 -0
  105. package/dist/{version-notify-73YURTFR.js → version-notify-WFA5XB5A.js} +23 -23
  106. package/dist/web-assets/assets/index-BostMFoR.css +1 -0
  107. package/dist/web-assets/assets/index-CKlqUkyG.js +92 -0
  108. package/dist/web-assets/index.html +2 -2
  109. package/drizzle/0013_variant_purpose.sql +3 -0
  110. package/drizzle/meta/_journal.json +7 -0
  111. package/package.json +5 -4
  112. package/templates/_base/.init/.local/hooks/startup-context.ts +19 -2
  113. package/templates/_base/home/VOLUTE.md +13 -1
  114. package/templates/_base/src/lib/daemon-client.ts +27 -7
  115. package/templates/claude/src/agent.ts +1 -1
  116. package/templates/claude/src/lib/hooks/identity-reload.ts +12 -3
  117. package/templates/pi/src/lib/event-handler.ts +47 -6
  118. package/dist/delivery-manager-HZTT5SJR.js +0 -36
  119. package/dist/echo-text-H3SH337Q.js +0 -36
  120. package/dist/message-delivery-GPQ5SE5N.js +0 -40
  121. package/dist/mind-service-2ARGJNKP.js +0 -44
  122. package/dist/scheduler-I7JZCT47.js +0 -34
  123. package/dist/sleep-manager-Z27R3WTW.js +0 -38
  124. package/dist/spirit-WXS3HCLN.js +0 -25
  125. package/dist/system-chat-63ABFF6V.js +0 -40
  126. package/dist/web-assets/assets/index-WdZ60R6c.css +0 -1
  127. package/dist/web-assets/assets/index-taX6Ezfa.js +0 -92
  128. package/dist/{env-C7Z55QCJ.js → env-ZYMSCB7B.js} +5 -5
  129. package/dist/{login-ARRZ4ORJ.js → login-LKNLCJPA.js} +3 -3
  130. package/dist/{mind-contacts-GV7XYL5D.js → mind-contacts-BKUFPUCU.js} +6 -6
  131. package/dist/{mind-history-TTGA5BHZ.js → mind-history-WI5OI3BW.js} +4 -4
  132. package/dist/{register-42VBJKTP.js → register-TWQDKQRT.js} +3 -3
  133. package/dist/{restart-YYEGZJDD.js → restart-5F5QDBOG.js} +4 -4
  134. package/dist/{skill-UUM4JZRF.js → skill-4CYJLYVY.js} +4 -4
  135. package/dist/{src-WLJE73NP.js → src-ZN4LXMPI.js} +3 -3
  136. package/dist/{start-O6UKK7BG.js → start-BTVMTP3K.js} +3 -3
  137. package/dist/{stop-UBX6X5RA.js → stop-YORESK4U.js} +4 -4
@@ -1,29 +1,34 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  spiritDir
4
- } from "./chunk-HNGFKPNS.js";
4
+ } from "./chunk-VIUH5CHQ.js";
5
+ import {
6
+ extractTextContent,
7
+ getRoutingConfig,
8
+ resolveDeliveryMode,
9
+ resolveRoute,
10
+ setRoutesChangeListener,
11
+ shouldGate
12
+ } from "./chunk-EFT2OALG.js";
5
13
  import {
6
14
  clearMind,
7
15
  linkInboundToActiveTurn,
8
16
  publish as publish3,
9
17
  summarizeOrphanedTurns
10
- } from "./chunk-USD45JKR.js";
11
- import {
12
- getPrompt
13
- } from "./chunk-MOERAQXH.js";
18
+ } from "./chunk-K3JOLGWF.js";
14
19
  import {
15
20
  hashSkillDir,
16
21
  importSkillFromDir,
17
22
  removeSharedSkill,
18
23
  sharedSkillsDir
19
- } from "./chunk-HUNDXZBZ.js";
24
+ } from "./chunk-GWVFDUNB.js";
20
25
  import {
21
26
  getOrCreateMindUser,
22
27
  getOrCreateSystemUser,
23
28
  getUser,
24
29
  getUserByUsername,
25
30
  syncMindProfile
26
- } from "./chunk-ODDGGLM6.js";
31
+ } from "./chunk-4DSGN4FL.js";
27
32
  import {
28
33
  addMessage,
29
34
  createChannel,
@@ -33,63 +38,60 @@ import {
33
38
  getChannelSettings,
34
39
  getParticipants,
35
40
  joinChannel,
41
+ listConversationsForMind,
36
42
  publish as publish2,
37
43
  updateChannelSettings
38
- } from "./chunk-HPGN6XRT.js";
44
+ } from "./chunk-VNRSXEBF.js";
45
+ import {
46
+ getPrompt
47
+ } from "./chunk-JPGHSXSV.js";
39
48
  import {
40
49
  isSandboxEnabled,
41
50
  wrapForSandbox
42
- } from "./chunk-VZRA5TYO.js";
51
+ } from "./chunk-JCWB2ITW.js";
43
52
  import {
44
53
  MIND_LEVEL_SESSION,
45
54
  recordNotice
46
- } from "./chunk-IYCV34IU.js";
55
+ } from "./chunk-XTEKINWG.js";
47
56
  import {
48
57
  markIdle,
49
58
  onMindEvent
50
- } from "./chunk-MTRMQFCQ.js";
59
+ } from "./chunk-ZX46PGCC.js";
51
60
  import {
52
61
  renderAvatarBlock
53
- } from "./chunk-WQ46E33M.js";
62
+ } from "./chunk-FBBS4BVR.js";
54
63
  import {
55
64
  readVoluteConfig,
56
65
  resolveWakeTriggers,
57
66
  writeVoluteConfig
58
67
  } from "./chunk-WCFSBH33.js";
59
- import {
60
- extractTextContent,
61
- getRoutingConfig,
62
- resolveDeliveryMode,
63
- resolveRoute,
64
- setRoutesChangeListener
65
- } from "./chunk-EIQDZC43.js";
66
68
  import {
67
69
  chownMindDir,
68
70
  exec,
69
71
  isIsolationEnabled,
70
72
  mindUserName,
71
73
  wrapForIsolation
72
- } from "./chunk-DGGZVZTM.js";
74
+ } from "./chunk-DVN6DNMI.js";
73
75
  import {
74
76
  loadMergedEnv
75
- } from "./chunk-IJPMIEVE.js";
77
+ } from "./chunk-PY24ZL62.js";
76
78
  import {
77
79
  aiCompleteUtility,
78
80
  getAiConfig,
79
81
  missingCredentialWarning,
80
82
  resolveApiKey
81
- } from "./chunk-YNBXEQAP.js";
83
+ } from "./chunk-YIJGKHIS.js";
82
84
  import {
83
85
  readGlobalConfig,
84
86
  writeGlobalConfig
85
- } from "./chunk-5KXCM5AW.js";
87
+ } from "./chunk-NJHN6IRB.js";
86
88
  import {
87
89
  publish,
88
90
  subscribe
89
- } from "./chunk-LTLFOSA4.js";
91
+ } from "./chunk-3B427GUK.js";
90
92
  import {
91
93
  logger_default
92
- } from "./chunk-T2TP6ZC6.js";
94
+ } from "./chunk-QUSMYGNQ.js";
93
95
  import {
94
96
  SPIRIT_NAME,
95
97
  findMind,
@@ -103,7 +105,7 @@ import {
103
105
  stateDir,
104
106
  voluteHome,
105
107
  voluteSystemDir
106
- } from "./chunk-WW6C3PLV.js";
108
+ } from "./chunk-RSBL3FAD.js";
107
109
  import {
108
110
  activity,
109
111
  channelGates,
@@ -111,7 +113,7 @@ import {
111
113
  messages,
112
114
  mindHistory,
113
115
  sessions
114
- } from "./chunk-2MJ7EOMT.js";
116
+ } from "./chunk-P4LHVRNE.js";
115
117
 
116
118
  // packages/daemon/src/lib/delivery/message-delivery.ts
117
119
  import { and as and4, eq as eq5, inArray as inArray3 } from "drizzle-orm";
@@ -159,7 +161,9 @@ function loadJsonMap(path) {
159
161
  const map = /* @__PURE__ */ new Map();
160
162
  try {
161
163
  if (existsSync(path)) {
162
- const data = JSON.parse(readFileSync(path, "utf-8"));
164
+ const raw = readFileSync(path, "utf-8").trim();
165
+ if (!raw) return map;
166
+ const data = JSON.parse(raw);
163
167
  for (const [key, value] of Object.entries(data)) {
164
168
  if (typeof value === "number") map.set(key, value);
165
169
  }
@@ -457,6 +461,35 @@ var MindStartupError = class extends Error {
457
461
  function mindPidPath(name) {
458
462
  return resolve2(stateDir(name), "mind.pid");
459
463
  }
464
+ async function buildPendingContextMessage(name, context) {
465
+ const parts = [];
466
+ if (context.type === "merge" || context.type === "merged") {
467
+ parts.push(await getPrompt("merge_message", { name: String(context.name ?? "") }));
468
+ } else if (context.type === "split") {
469
+ parts.push(await getPrompt("split_message", { name, parent: String(context.parent ?? "") }));
470
+ if (context.purpose) parts.push(`Why you were split off: ${String(context.purpose)}`);
471
+ } else if (context.type === "sprouted") {
472
+ parts.push(await getPrompt("sprout_message"));
473
+ } else if (context.type === "upgraded") {
474
+ parts.push(await getPrompt("upgrade_message"));
475
+ } else {
476
+ parts.push(await getPrompt("restart_message"));
477
+ }
478
+ if (context.summary) parts.push(`Changes: ${context.summary}`);
479
+ if (context.justification) parts.push(`Why: ${context.justification}`);
480
+ if (context.memory) parts.push(`Context: ${context.memory}`);
481
+ if (context.memoryDelta) {
482
+ parts.push(
483
+ `
484
+ Your variant's memory & journal (not merged \u2014 integrate into your own memory what you want to keep):
485
+ ${context.memoryDelta}`
486
+ );
487
+ }
488
+ if (context.farewell)
489
+ parts.push(`Your variant's parting note (this was you, winding down):
490
+ ${context.farewell}`);
491
+ return parts.join("\n");
492
+ }
460
493
  var MindManager = class {
461
494
  minds = /* @__PURE__ */ new Map();
462
495
  stopping = /* @__PURE__ */ new Set();
@@ -549,7 +582,7 @@ var MindManager = class {
549
582
  if (isIsolationEnabled()) {
550
583
  try {
551
584
  await chownMindDir(mindStateDir, baseName);
552
- await chownMindDir(mindTmp, baseName);
585
+ await chownMindDir(resolve2(dir, ".mind"), baseName);
553
586
  } catch (err) {
554
587
  throw new Error(
555
588
  `Cannot start mind ${name}: failed to set ownership on state directory ${mindStateDir}: ${err instanceof Error ? err.message : err}`
@@ -796,22 +829,7 @@ var MindManager = class {
796
829
  const tracked = this.minds.get(name);
797
830
  if (!tracked) return;
798
831
  this.pendingContext.delete(name);
799
- const parts = [];
800
- if (context.type === "merge" || context.type === "merged") {
801
- parts.push(await getPrompt("merge_message", { name: String(context.name ?? "") }));
802
- } else if (context.type === "split") {
803
- parts.push(await getPrompt("split_message", { name, parent: String(context.parent ?? "") }));
804
- } else if (context.type === "sprouted") {
805
- parts.push(await getPrompt("sprout_message"));
806
- } else if (context.type === "upgraded") {
807
- parts.push(await getPrompt("upgrade_message"));
808
- } else {
809
- parts.push(await getPrompt("restart_message"));
810
- }
811
- if (context.summary) parts.push(`Changes: ${context.summary}`);
812
- if (context.justification) parts.push(`Why: ${context.justification}`);
813
- if (context.memory) parts.push(`Context: ${context.memory}`);
814
- const content = parts.join("\n");
832
+ const content = await buildPendingContextMessage(name, context);
815
833
  let conversationId;
816
834
  try {
817
835
  const result = await sendSystemMessageDirect(name, content);
@@ -845,7 +863,7 @@ var MindManager = class {
845
863
  if (this.shuttingDown || this.stopping.has(name)) return;
846
864
  mlog.error(`mind ${name} exited with code ${code}`);
847
865
  try {
848
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-Z27R3WTW.js");
866
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-DSPXFHGX.js");
849
867
  const sleepMgr = getSleepManagerIfReady2();
850
868
  const sleepState = sleepMgr?.getState(name);
851
869
  if (sleepState?.sleeping) {
@@ -875,15 +893,15 @@ var MindManager = class {
875
893
  (err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
876
894
  );
877
895
  try {
878
- const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-HZTT5SJR.js");
896
+ const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-RBXBPVWM.js");
879
897
  getDeliveryManager2().clearMindSessions(name);
880
898
  } catch (err) {
881
899
  if (!(err instanceof Error && err.message.includes("not initialized"))) {
882
900
  mlog.warn(`failed to clear delivery state for ${name} after crash`, logger_default.errorData(err));
883
901
  }
884
902
  }
885
- import("./mind-activity-tracker-YVZPC4A5.js").then(({ markIdle: markIdle2 }) => markIdle2(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
886
- import("./activity-events-MIOK3IOU.js").then(
903
+ import("./mind-activity-tracker-YMMERB6B.js").then(({ markIdle: markIdle2 }) => markIdle2(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
904
+ import("./activity-events-GJJAI2WT.js").then(
887
905
  ({ publish: publish4 }) => publish4({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
888
906
  ).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
889
907
  const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
@@ -941,7 +959,7 @@ var MindManager = class {
941
959
  mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
942
960
  }
943
961
  try {
944
- const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-HZTT5SJR.js");
962
+ const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-RBXBPVWM.js");
945
963
  getDeliveryManager2().clearMindSessions(name);
946
964
  } catch (err) {
947
965
  if (!(err instanceof Error && err.message.includes("not initialized"))) {
@@ -949,7 +967,7 @@ var MindManager = class {
949
967
  }
950
968
  }
951
969
  try {
952
- const { clearEchoTextCache } = await import("./echo-text-H3SH337Q.js");
970
+ const { clearEchoTextCache } = await import("./echo-text-B6NZ5FGE.js");
953
971
  clearEchoTextCache(name);
954
972
  } catch (err) {
955
973
  mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
@@ -1256,7 +1274,7 @@ var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
1256
1274
  if (user.role !== "admin" && user.role !== "system") {
1257
1275
  const target = c.req.param(paramName) ?? "";
1258
1276
  const baseName = await getBaseName(target);
1259
- if (user.username !== baseName) {
1277
+ if (user.username !== baseName && await getBaseName(user.username) !== baseName) {
1260
1278
  return c.json({ error: "Forbidden" }, 403);
1261
1279
  }
1262
1280
  }
@@ -1673,7 +1691,7 @@ function resolveSkillsDir(manifest) {
1673
1691
  async function discoverBuiltinExtensions(disabledIds) {
1674
1692
  const builtins = [
1675
1693
  { id: "notes", load: async () => (await import("./src-YH6PQ7LJ.js")).default },
1676
- { id: "pages", load: async () => (await import("./src-WLJE73NP.js")).default },
1694
+ { id: "pages", load: async () => (await import("./src-ZN4LXMPI.js")).default },
1677
1695
  { id: "plan", load: async () => (await import("./src-JAOVXO5W.js")).default }
1678
1696
  ];
1679
1697
  const results = [];
@@ -1923,7 +1941,7 @@ async function installNpmExtension(pkg) {
1923
1941
  throw new Error(`Extension "${pkg}" is already installed`);
1924
1942
  }
1925
1943
  const dir = ensureExtensionsNpmDir();
1926
- const { exec: exec2 } = await import("./exec-QIFYZG4C.js");
1944
+ const { exec: exec2 } = await import("./exec-FBXA3CM4.js");
1927
1945
  try {
1928
1946
  await exec2("npm", ["install", "--ignore-scripts", pkg], { cwd: dir });
1929
1947
  } catch (err) {
@@ -1944,7 +1962,7 @@ async function uninstallNpmExtension(pkg) {
1944
1962
  packages.splice(idx, 1);
1945
1963
  writeExtensionsConfig(packages);
1946
1964
  try {
1947
- const { exec: exec2 } = await import("./exec-QIFYZG4C.js");
1965
+ const { exec: exec2 } = await import("./exec-FBXA3CM4.js");
1948
1966
  await exec2("npm", ["uninstall", "--ignore-scripts", pkg], { cwd: extensionsNpmDir() });
1949
1967
  } catch (err) {
1950
1968
  logger_default.warn(
@@ -2118,8 +2136,7 @@ var MailPoller = class {
2118
2136
  this.reconnectAttempts = 0;
2119
2137
  this.reconnectDelay = INITIAL_RECONNECT_MS;
2120
2138
  if (this.disconnectedAt) {
2121
- this.catchUp(this.disconnectedAt);
2122
- this.disconnectedAt = null;
2139
+ void this.catchUpAndClear(this.disconnectedAt);
2123
2140
  }
2124
2141
  if (this.pingTimer) clearInterval(this.pingTimer);
2125
2142
  this.pingTimer = setInterval(() => {
@@ -2136,7 +2153,7 @@ var MailPoller = class {
2136
2153
  this.handleMessage(String(event.data));
2137
2154
  };
2138
2155
  this.ws.onclose = () => {
2139
- mlog2.warn("disconnected");
2156
+ mlog2.info("disconnected");
2140
2157
  if (!this.disconnectedAt) {
2141
2158
  this.disconnectedAt = (/* @__PURE__ */ new Date()).toISOString();
2142
2159
  }
@@ -2144,7 +2161,7 @@ var MailPoller = class {
2144
2161
  this.scheduleReconnect();
2145
2162
  };
2146
2163
  this.ws.onerror = (err) => {
2147
- mlog2.warn("WebSocket error", logger_default.errorData(err));
2164
+ mlog2.info("WebSocket error", logger_default.errorData(err));
2148
2165
  };
2149
2166
  }
2150
2167
  cleanup() {
@@ -2167,26 +2184,38 @@ var MailPoller = class {
2167
2184
  }, this.reconnectDelay);
2168
2185
  this.reconnectDelay = Math.min(this.reconnectDelay * 2, MAX_RECONNECT_MS);
2169
2186
  }
2170
- /** Fetch emails that arrived while disconnected */
2171
- catchUp(since) {
2172
- if (!this.config) return;
2187
+ /**
2188
+ * Run catch-up and advance the watermark only on full success. On any failure
2189
+ * (fetch error, non-OK response, delivery throw) the watermark is retained so
2190
+ * the next reconnect re-fetches the gap — at-least-once, never dropped.
2191
+ */
2192
+ async catchUpAndClear(since) {
2193
+ try {
2194
+ await this.catchUp(since);
2195
+ } catch (err) {
2196
+ mlog2.info("catch-up failed \u2014 will retry on next reconnect", logger_default.errorData(err));
2197
+ return;
2198
+ }
2199
+ if (this.disconnectedAt === since && this.ws?.readyState === WebSocket.OPEN) {
2200
+ this.disconnectedAt = null;
2201
+ }
2202
+ }
2203
+ /** Fetch and deliver emails that arrived while disconnected. Throws on failure. */
2204
+ async catchUp(since) {
2205
+ if (!this.config) throw new Error("systems config missing");
2173
2206
  const url = `${this.config.apiUrl}/api/mail/system/poll?since=${encodeURIComponent(since)}`;
2174
- fetch(url, {
2207
+ const res = await fetch(url, {
2175
2208
  headers: { Authorization: `Bearer ${this.config.apiKey}` }
2176
- }).then(async (res) => {
2177
- if (!res.ok) {
2178
- mlog2.warn(`catch-up poll failed: HTTP ${res.status}`);
2179
- return;
2180
- }
2181
- const data = await res.json();
2182
- if (!Array.isArray(data.emails) || data.emails.length === 0) return;
2183
- mlog2.info(`catching up on ${data.emails.length} missed emails`);
2184
- for (const email of data.emails) {
2185
- await this.deliver(email.mind, email);
2186
- }
2187
- }).catch((err) => {
2188
- mlog2.warn("catch-up error", logger_default.errorData(err));
2189
2209
  });
2210
+ if (!res.ok) {
2211
+ throw new Error(`catch-up poll failed: HTTP ${res.status}`);
2212
+ }
2213
+ const data = await res.json();
2214
+ if (!Array.isArray(data.emails) || data.emails.length === 0) return;
2215
+ mlog2.info(`catching up on ${data.emails.length} missed emails`);
2216
+ for (const email of data.emails) {
2217
+ await this.deliver(email.mind, email);
2218
+ }
2190
2219
  }
2191
2220
  handleMessage(data) {
2192
2221
  if (data === "pong") return;
@@ -2223,24 +2252,22 @@ var MailPoller = class {
2223
2252
  await this.deliver(mind, { ...email, mind });
2224
2253
  }
2225
2254
  async deliver(mind, email) {
2226
- const entry = await findMind(mind);
2227
- if (!entry || !entry.running) {
2228
- mlog2.warn(`skipping delivery to ${mind}: ${!entry ? "not found" : "not running"}`);
2255
+ if (!await findMind(mind)) {
2256
+ mlog2.warn(`skipping delivery to ${mind}: mind not found`);
2229
2257
  return;
2230
2258
  }
2231
2259
  const text = formatEmailContent(email);
2232
- try {
2233
- await deliverMessage(mind, {
2234
- content: [{ type: "text", text }],
2235
- channel: `mail:${email.from.address}`,
2236
- sender: email.from.name || email.from.address,
2237
- platform: "Email",
2238
- isDM: true
2239
- });
2240
- mlog2.info(`delivered email from ${email.from.address} to ${mind}`);
2241
- } catch (err) {
2242
- mlog2.warn(`failed to deliver to ${mind}`, logger_default.errorData(err));
2260
+ const delivered = await deliverMessage(mind, {
2261
+ content: [{ type: "text", text }],
2262
+ channel: `mail:${email.from.address}`,
2263
+ sender: email.from.name || email.from.address,
2264
+ platform: "Email",
2265
+ isDM: true
2266
+ });
2267
+ if (!delivered) {
2268
+ throw new Error(`delivery to ${mind} failed`);
2243
2269
  }
2270
+ mlog2.info(`delivered email from ${email.from.address} to ${mind}`);
2244
2271
  }
2245
2272
  };
2246
2273
  var instance2 = null;
@@ -2772,7 +2799,7 @@ async function startMindFull(name) {
2772
2799
  );
2773
2800
  }
2774
2801
  joinSystemChannelForMind(baseName).catch(
2775
- (err) => logger_default.error(`failed to join #system for ${baseName}`, logger_default.errorData(err))
2802
+ (err) => logger_default.warn(`failed to join #system for ${baseName}`, logger_default.errorData(err))
2776
2803
  );
2777
2804
  if (config?.tokenBudget) {
2778
2805
  getTokenBudget().setBudget(
@@ -2784,7 +2811,7 @@ async function startMindFull(name) {
2784
2811
  notifyExtensionsMindStart(baseName);
2785
2812
  }
2786
2813
  async function recordMissingCredentialsNotice(mind, template) {
2787
- const { hasUndeliveredNotice, recordNotice: recordNotice2, MIND_LEVEL_SESSION: MIND_LEVEL_SESSION2 } = await import("./notices-2KU5NTRY.js");
2814
+ const { hasUndeliveredNotice, recordNotice: recordNotice2, MIND_LEVEL_SESSION: MIND_LEVEL_SESSION2 } = await import("./notices-N6BCGHFV.js");
2788
2815
  const warning = await missingCredentialWarning(template, readMindModel(mind, template), mind);
2789
2816
  if (!warning) return;
2790
2817
  if (await hasUndeliveredNotice(mind, "no_credentials")) return;
@@ -2828,7 +2855,7 @@ async function wakeMind(name) {
2828
2855
  async function startSpiritFull(name) {
2829
2856
  const entry = await findMind(name);
2830
2857
  if (entry?.dir) {
2831
- const { registerMindDir } = await import("./delivery-router-3QFG2ZEX.js");
2858
+ const { registerMindDir } = await import("./delivery-router-VUOL5YSA.js");
2832
2859
  registerMindDir(name, entry.dir);
2833
2860
  }
2834
2861
  await getMindManager().startMind(name);
@@ -2861,14 +2888,14 @@ function buildSeedOrientation(createdBy, creatorType) {
2861
2888
  async function sendSeedOrientation(mindName, createdBy) {
2862
2889
  let creatorType;
2863
2890
  if (createdBy) {
2864
- const { getUserByUsername: getUserByUsername2 } = await import("./auth-A4VGHADI.js");
2891
+ const { getUserByUsername: getUserByUsername2 } = await import("./auth-ABYLOEZ7.js");
2865
2892
  creatorType = (await getUserByUsername2(createdBy))?.user_type;
2866
2893
  }
2867
2894
  await sendSystemMessage(mindName, buildSeedOrientation(createdBy, creatorType));
2868
2895
  }
2869
2896
  async function ensureCreatorDM(mindName, creatorUsername) {
2870
- const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-A4VGHADI.js");
2871
- const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-C7IKYSHD.js");
2897
+ const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-ABYLOEZ7.js");
2898
+ const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-TUM5PJJ4.js");
2872
2899
  const mindUser = await getOrCreateMindUser2(mindName);
2873
2900
  const creatorUser = await getUserByUsername2(creatorUsername);
2874
2901
  if (!creatorUser) {
@@ -3327,7 +3354,7 @@ var SleepManager = class {
3327
3354
  * the real delivery stack. Returns whether the batch was handed off to the mind.
3328
3355
  */
3329
3356
  async deliverQueuedBatch(name, payloads) {
3330
- const { deliverBatch: deliverBatch2 } = await import("./message-delivery-GPQ5SE5N.js");
3357
+ const { deliverBatch: deliverBatch2 } = await import("./message-delivery-ABPV76EA.js");
3331
3358
  return deliverBatch2(name, payloads);
3332
3359
  }
3333
3360
  // --- Wake failure handling (#419) ---
@@ -3913,7 +3940,7 @@ var DeliveryManager = class {
3913
3940
  isMindRunning = (name) => tryGetMindManager()?.isRunning(name) ?? false;
3914
3941
  /** Sends a system message to a mind (invites, release summaries); overridable in tests. */
3915
3942
  notify = async (mindName, text) => {
3916
- const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-63ABFF6V.js");
3943
+ const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-M2XOBKEM.js");
3917
3944
  await sendSystemMessage2(mindName, text);
3918
3945
  };
3919
3946
  constructor() {
@@ -3967,7 +3994,7 @@ var DeliveryManager = class {
3967
3994
  if (route.destination === "file") {
3968
3995
  return { routed: true, session: route.path, destination: "file", mode: "immediate" };
3969
3996
  }
3970
- if (!route.matched && config.gateUnmatched !== false) {
3997
+ if (shouldGate(config, route)) {
3971
3998
  dlog.debug(`gating unmatched channel ${payload.channel} for ${mindName}`);
3972
3999
  await this.gateMessage(mindName, route.session, payload);
3973
4000
  return { routed: true, session: route.session, destination: "mind", mode: "gated" };
@@ -4141,7 +4168,13 @@ var DeliveryManager = class {
4141
4168
  }
4142
4169
  const channel = row.channel ?? payload.channel ?? "unknown";
4143
4170
  const list = byChannel.get(channel) ?? [];
4144
- list.push({ id: row.id, session });
4171
+ list.push({
4172
+ id: row.id,
4173
+ session,
4174
+ channel,
4175
+ sender: payload.sender ?? row.sender ?? null,
4176
+ content: extractTextContent(payload.content)
4177
+ });
4145
4178
  byChannel.set(channel, list);
4146
4179
  }
4147
4180
  const promote = [];
@@ -4176,16 +4209,38 @@ var DeliveryManager = class {
4176
4209
  if (truncationNotes.length > 0) await this.sendReleaseSummary(mindName, truncationNotes);
4177
4210
  return;
4178
4211
  }
4212
+ const orderedPromote = [...promote].sort((a, b) => a.id - b.id);
4179
4213
  try {
4180
4214
  const db = await getDb();
4181
- for (const p of promote) {
4182
- await db.update(deliveryQueue).set({ status: "pending", session: p.session, attempts: 0, next_attempt_at: null }).where(eq4(deliveryQueue.id, p.id));
4215
+ for (const p of orderedPromote) {
4216
+ await db.transaction(async (tx) => {
4217
+ await tx.insert(mindHistory).values({
4218
+ mind: baseName,
4219
+ type: "inbound",
4220
+ channel: p.channel,
4221
+ sender: p.sender,
4222
+ content: p.content
4223
+ });
4224
+ await tx.update(deliveryQueue).set({ status: "pending", session: p.session, attempts: 0, next_attempt_at: null }).where(eq4(deliveryQueue.id, p.id));
4225
+ });
4183
4226
  }
4184
4227
  dlog.info(`released ${promote.length} gated message(s) for ${baseName} after route change`);
4185
4228
  } catch (err) {
4186
- dlog.warn(`failed to promote gated rows for ${baseName}`, logger_default.errorData(err));
4229
+ dlog.error(
4230
+ `failed to record+promote gated rows for ${baseName} (channels: ${[...byChannel.keys()].join(", ")})`,
4231
+ logger_default.errorData(err)
4232
+ );
4187
4233
  return;
4188
4234
  }
4235
+ for (const p of orderedPromote) {
4236
+ publish3(baseName, {
4237
+ mind: baseName,
4238
+ type: "inbound",
4239
+ channel: p.channel,
4240
+ content: p.content ?? void 0,
4241
+ sender: p.sender ?? void 0
4242
+ });
4243
+ }
4189
4244
  if (truncationNotes.length > 0) await this.sendReleaseSummary(mindName, truncationNotes);
4190
4245
  await this.redrive();
4191
4246
  }
@@ -4669,18 +4724,22 @@ var DeliveryManager = class {
4669
4724
  const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;
4670
4725
  const channel = payload.channel ?? "unknown";
4671
4726
  const heldLine = gatedCount > 1 ? `${gatedCount} messages from this channel are being held, unrouted \u2014 you've not routed it yet.` : `Someone new is reaching out \u2014 you don't have a route for this channel yet.`;
4672
- const notification = [
4673
- `[New channel: ${channel}]`,
4674
- heldLine,
4675
- `Sender: ${payload.sender ?? "unknown"}`,
4727
+ const detailLines = [
4676
4728
  payload.platform ? `Platform: ${payload.platform}` : null,
4677
- payload.participantCount ? `Participants: ${payload.participantCount}` : null,
4678
- "",
4679
- `Preview: ${preview}`,
4680
- "",
4681
- `To start hearing this channel, add a routing rule for "${channel}" to .config/routes.json \u2014 only the ${GATED_RELEASE_LIMIT_PER_CHANNEL} most recent held messages are replayed; older ones stay in the channel history (volute chat read ${channel}).`,
4682
- `To stop hearing about it, decline the channel: volute chat channels decline ${channel}`
4683
- ].filter((line) => line !== null).join("\n");
4729
+ payload.participantCount ? `Participants: ${payload.participantCount}` : null
4730
+ ].filter((l) => l !== null);
4731
+ const details = detailLines.length > 0 ? `${detailLines.join("\n")}
4732
+
4733
+ ` : "\n";
4734
+ const { getPrompt: getPrompt2 } = await import("./prompts-MDHDLWEX.js");
4735
+ const notification = await getPrompt2("channel_invite", {
4736
+ channel,
4737
+ heldLine,
4738
+ sender: payload.sender ?? "unknown",
4739
+ details,
4740
+ preview,
4741
+ limit: String(GATED_RELEASE_LIMIT_PER_CHANNEL)
4742
+ });
4684
4743
  await this.notify(mindName, notification);
4685
4744
  }
4686
4745
  /**
@@ -4871,6 +4930,15 @@ async function recordInbound(mind, channel, sender, content) {
4871
4930
  });
4872
4931
  return insertedId;
4873
4932
  }
4933
+ async function deliverSproutedNotice(mind) {
4934
+ await recordInbound(mind, "system", "system", "[seed has sprouted]");
4935
+ const convs = await listConversationsForMind(mind);
4936
+ for (const conv of convs) {
4937
+ await addMessage(conv.id, "assistant", "system", [
4938
+ { type: "text", text: "[seed has sprouted]" }
4939
+ ]);
4940
+ }
4941
+ }
4874
4942
  async function recordOutbound(mind, channel, content, opts = {}) {
4875
4943
  try {
4876
4944
  const db = await getDb();
@@ -4965,6 +5033,17 @@ function resolveSleepAction(sleepBehavior, wokenByTrigger, wakeTriggerMatches) {
4965
5033
  if (!sleepBehavior && wakeTriggerMatches) return "queue-and-wake";
4966
5034
  return "queue";
4967
5035
  }
5036
+ function willGate(baseName, payload) {
5037
+ if (payload.session) return false;
5038
+ const config = getRoutingConfig(baseName);
5039
+ const route = resolveRoute(config, {
5040
+ channel: payload.channel,
5041
+ sender: payload.sender ?? void 0,
5042
+ isDM: payload.isDM,
5043
+ participantCount: payload.participantCount
5044
+ });
5045
+ return shouldGate(config, route);
5046
+ }
4968
5047
  async function deliverMessage(mindName, payload, opts = {}) {
4969
5048
  try {
4970
5049
  const baseName = await getBaseName(mindName);
@@ -4975,9 +5054,9 @@ async function deliverMessage(mindName, payload, opts = {}) {
4975
5054
  }
4976
5055
  if (!opts.isFlush) {
4977
5056
  const textContent = extractTextContent(payload.content);
4978
- await recordInbound(baseName, payload.channel, payload.sender ?? null, textContent);
4979
5057
  const sleepManager = getSleepManagerIfReady();
4980
5058
  if (sleepManager?.isSleeping(baseName)) {
5059
+ await recordInbound(baseName, payload.channel, payload.sender ?? null, textContent);
4981
5060
  const sleepState = sleepManager.getState(baseName);
4982
5061
  const action = resolveSleepAction(
4983
5062
  payload.whileSleeping,
@@ -4996,6 +5075,9 @@ async function deliverMessage(mindName, payload, opts = {}) {
4996
5075
  }
4997
5076
  return true;
4998
5077
  }
5078
+ if (!willGate(baseName, payload)) {
5079
+ await recordInbound(baseName, payload.channel, payload.sender ?? null, textContent);
5080
+ }
4999
5081
  }
5000
5082
  const manager = getDeliveryManager();
5001
5083
  await manager.routeAndDeliver(mindName, payload);
@@ -5105,7 +5187,7 @@ async function spiritWillHandle() {
5105
5187
  const spiritEntry = await findMind(SPIRIT_NAME);
5106
5188
  if (spiritEntry?.running && spiritEntry.mindType === "spirit") return true;
5107
5189
  try {
5108
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-Z27R3WTW.js");
5190
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-DSPXFHGX.js");
5109
5191
  const sm = getSleepManagerIfReady2();
5110
5192
  if (sm?.isSleeping(SPIRIT_NAME)) return true;
5111
5193
  } catch (err) {
@@ -5133,7 +5215,7 @@ async function generateSystemFallbackReply(conversationId, mindName, message) {
5133
5215
  if (config.sleep.schedule?.wake) contextParts.push(`Wake cron: ${config.sleep.schedule.wake}`);
5134
5216
  }
5135
5217
  try {
5136
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-Z27R3WTW.js");
5218
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-DSPXFHGX.js");
5137
5219
  const sm = getSleepManagerIfReady2();
5138
5220
  if (sm) {
5139
5221
  const state2 = sm.getState(mindName);
@@ -5200,6 +5282,7 @@ export {
5200
5282
  MindStartupError,
5201
5283
  initMindManager,
5202
5284
  getMindManager,
5285
+ tryGetMindManager,
5203
5286
  invalidateMindUserCache,
5204
5287
  SESSION_MAX_AGE,
5205
5288
  invalidateSessionCache,
@@ -5248,6 +5331,7 @@ export {
5248
5331
  getSleepManager,
5249
5332
  getSleepManagerIfReady,
5250
5333
  recordInbound,
5334
+ deliverSproutedNotice,
5251
5335
  recordOutbound,
5252
5336
  linkToolResultToTurn,
5253
5337
  resolveSleepAction,
@@ -5255,6 +5339,7 @@ export {
5255
5339
  deliverBatch,
5256
5340
  ensureSystemChannel,
5257
5341
  joinSystemChannel,
5342
+ joinSystemChannelForMind,
5258
5343
  backfillSystemChannelMembers,
5259
5344
  announceToSystem
5260
5345
  };
@@ -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 = {