volute 0.46.0 → 0.47.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 (136) hide show
  1. package/README.md +1 -1
  2. package/dist/{activity-events-NF6VWTJQ.js → activity-events-MIOK3IOU.js} +3 -3
  3. package/dist/{ai-service-UODCCE4O.js → ai-service-CVZEYKTK.js} +4 -4
  4. package/dist/{archive-WS2L2VIQ.js → archive-6Z4GFQPH.js} +3 -3
  5. package/dist/{auth-CCFAU4GQ.js → auth-A4VGHADI.js} +4 -4
  6. package/dist/avatar-image-7S3WA77A.js +22 -0
  7. package/dist/{backup-TST5C2YN.js → backup-IIAFOW2L.js} +13 -13
  8. package/dist/channels-ITE4YC57.js +89 -0
  9. package/dist/{chat-ZIQQS25U.js → chat-AJLJCQ3M.js} +12 -8
  10. package/dist/{chunk-ZV7MFISX.js → chunk-2MJ7EOMT.js} +20 -2
  11. package/dist/{chunk-R77O2WMK.js → chunk-2OIYNHWO.js} +1 -1
  12. package/dist/{chunk-QCN24XEN.js → chunk-3YUBFTDN.js} +1 -1
  13. package/dist/{chunk-E3DA4HYV.js → chunk-4I72V3E5.js} +10 -10
  14. package/dist/{chunk-K6T43YBD.js → chunk-5IERLDUS.js} +3 -3
  15. package/dist/{chunk-FXORA5JG.js → chunk-5NN2VHJV.js} +3 -3
  16. package/dist/chunk-7KRIHYLR.js +124 -0
  17. package/dist/{chunk-RRNXJOKB.js → chunk-BGYHLR54.js} +2 -2
  18. package/dist/{chunk-QMJCNZ6C.js → chunk-C3KLHJDB.js} +1 -1
  19. package/dist/{chunk-TQJ6ZYVY.js → chunk-CBOIHNMR.js} +2 -2
  20. package/dist/{chunk-H2X54EHC.js → chunk-DGGZVZTM.js} +1 -1
  21. package/dist/{chunk-4JOQZPCE.js → chunk-EIQDZC43.js} +1 -1
  22. package/dist/{chunk-JO5IO4FH.js → chunk-F55GRIEK.js} +243 -121
  23. package/dist/{chunk-5ZQZDDIK.js → chunk-HPGN6XRT.js} +3 -3
  24. package/dist/{chunk-KGIXX4NM.js → chunk-IWJSNMSY.js} +5 -5
  25. package/dist/{chunk-ZDZISKCL.js → chunk-IYG4FBMP.js} +1 -1
  26. package/dist/{chunk-4YU7HF4L.js → chunk-JCMGDCGX.js} +4 -4
  27. package/dist/{chunk-PJ45FQPT.js → chunk-JSVHH32K.js} +6 -6
  28. package/dist/{chunk-YXSUMLTC.js → chunk-K23P5Q5N.js} +301 -77
  29. package/dist/{chunk-ZNMCCHCP.js → chunk-LTLFOSA4.js} +2 -2
  30. package/dist/{chunk-IHKGWTGT.js → chunk-LWDTP6DH.js} +1 -1
  31. package/dist/{chunk-JZ62SYWR.js → chunk-M3XDVS5Q.js} +1 -1
  32. package/dist/{chunk-BJZ4PV7R.js → chunk-M6TTZRGH.js} +9 -9
  33. package/dist/{chunk-KQSFTPIM.js → chunk-MTRMQFCQ.js} +1 -1
  34. package/dist/{chunk-OV7ZQ2GH.js → chunk-ODDGGLM6.js} +3 -3
  35. package/dist/{chunk-QLWG3C2Z.js → chunk-RRZ4RQQU.js} +2 -2
  36. package/dist/{chunk-CDPBTTZO.js → chunk-RUFLGTOT.js} +2 -2
  37. package/dist/{chunk-F7H6ATM4.js → chunk-SKNXMUIU.js} +1 -1
  38. package/dist/{chunk-RXEEYDIB.js → chunk-SZJCJVXV.js} +1 -1
  39. package/dist/{chunk-DRJ24GXK.js → chunk-VMCZBXF2.js} +52 -8
  40. package/dist/{chunk-ALKYQULU.js → chunk-WW6C3PLV.js} +1 -1
  41. package/dist/{chunk-A7WQZKOU.js → chunk-XHN6XBWV.js} +2 -2
  42. package/dist/{chunk-6EMWJZBK.js → chunk-YKA7DCVW.js} +1 -1
  43. package/dist/cli.js +21 -21
  44. package/dist/{clock-RHS2JEDL.js → clock-E6N5Y3YW.js} +3 -3
  45. package/dist/{cloud-sync-46RS4SDA.js → cloud-sync-LQTVDTQ3.js} +19 -18
  46. package/dist/{conversations-CJOOCGNW.js → conversations-C7IKYSHD.js} +4 -4
  47. package/dist/{create-IOV4GCRX.js → create-PQQXKTJK.js} +1 -1
  48. package/dist/{daemon-restart-VFEI57UO.js → daemon-restart-5F3YTLZK.js} +7 -7
  49. package/dist/daemon.js +217 -151
  50. package/dist/{db-MF2JNRUW.js → db-FO6LXYCW.js} +2 -2
  51. package/dist/delivery-manager-TN5KK2AP.js +34 -0
  52. package/dist/{delivery-router-N4A7ZC5T.js → delivery-router-3QFG2ZEX.js} +3 -3
  53. package/dist/{down-U2CZ23M4.js → down-ZI4S6265.js} +5 -5
  54. package/dist/echo-text-SEQCLGAT.js +34 -0
  55. package/dist/{exec-G3ZRST4Z.js → exec-QIFYZG4C.js} +3 -3
  56. package/dist/{export-LY723I3Z.js → export-4GVGNW54.js} +3 -3
  57. package/dist/{extensions-ZELQLTSR.js → extensions-PIMIQ7J3.js} +21 -18
  58. package/dist/{files-4LN3EN7Q.js → files-FEFZKMNM.js} +6 -6
  59. package/dist/{import-42YNQBF4.js → import-7MO3YTRE.js} +6 -6
  60. package/dist/{isolation-YMGZZ4E5.js → isolation-MCT4ZYLT.js} +3 -3
  61. package/dist/{login-M6OJUPQ2.js → login-OF4WXDAN.js} +5 -5
  62. package/dist/{logout-WQBWO77G.js → logout-ERE2BGA3.js} +5 -5
  63. package/dist/message-delivery-ZE3YGJDD.js +38 -0
  64. package/dist/{mind-LHKS55DI.js → mind-POOHFZHP.js} +13 -13
  65. package/dist/{mind-activity-tracker-3QRNJVR4.js → mind-activity-tracker-YVZPC4A5.js} +4 -4
  66. package/dist/{mind-list-4D7J4CHD.js → mind-list-KEGRGQEQ.js} +5 -4
  67. package/dist/mind-service-JFY7UG5E.js +40 -0
  68. package/dist/{mind-sleep-74TGOQBC.js → mind-sleep-IN3WPA76.js} +5 -5
  69. package/dist/{mind-status-UO34ASXZ.js → mind-status-SMUWJKGV.js} +20 -3
  70. package/dist/{package-DOD6CPO6.js → package-WPEE2HVJ.js} +2 -2
  71. package/dist/{registry-OUZBZ3Q4.js → registry-UMMDRPRM.js} +2 -2
  72. package/dist/{sandbox-ZJ7QA6Z6.js → sandbox-G4X4K3UN.js} +4 -4
  73. package/dist/scheduler-HQSDYJFY.js +32 -0
  74. package/dist/{schema-PVNG3PQM.js → schema-VEVO7HG6.js} +3 -1
  75. package/dist/{seed-CNYIHPOH.js → seed-VANPU43L.js} +1 -1
  76. package/dist/{seed-cmd-AVNUNLZV.js → seed-cmd-SYVDZKM4.js} +2 -2
  77. package/dist/{seed-create-ANXGYSCC.js → seed-create-3FGZPS6C.js} +3 -3
  78. package/dist/{seed-sprout-NIUFTX32.js → seed-sprout-H7QLFPFM.js} +12 -12
  79. package/dist/{send-SPE6DVNJ.js → send-HYDFGC6G.js} +7 -7
  80. package/dist/{service-UOCLXDWR.js → service-SYJ5G6LP.js} +4 -4
  81. package/dist/{service-install-4HHFDDWY.js → service-install-FQ43Z4DF.js} +5 -5
  82. package/dist/{setup-Y2EJI3GG.js → setup-KFYVWTXG.js} +3 -3
  83. package/dist/{setup-QT2A25SY.js → setup-QJHGMGSN.js} +7 -7
  84. package/dist/{skills-AKCFA7BN.js → skills-L2CMTW7A.js} +5 -5
  85. package/dist/sleep-manager-3NA7ZJP3.js +36 -0
  86. package/dist/{spirit-RPBLLUQ5.js → spirit-WXN4IDBF.js} +8 -8
  87. package/dist/{sprout-SMX42ZBP.js → sprout-LTL5P36T.js} +1 -1
  88. package/dist/{status-SWRVGXBI.js → status-V4RJMHJ4.js} +19 -9
  89. package/dist/system-chat-N2F7HVY3.js +38 -0
  90. package/dist/{systems-VNB5WKSS.js → systems-B5YJKMW4.js} +6 -6
  91. package/dist/{tailscale-3XOKOW7T.js → tailscale-MCLY5GYE.js} +2 -2
  92. package/dist/{template-hash-446HHH5V.js → template-hash-GSOUARW4.js} +2 -2
  93. package/dist/template-staleness-EMQORSGG.js +15 -0
  94. package/dist/{turn-tracker-FOIBNUUA.js → turn-tracker-JZRZDFWV.js} +6 -6
  95. package/dist/{up-PS6PFS75.js → up-J2PBPOZL.js} +6 -6
  96. package/dist/{update-SWAYZRWJ.js → update-ACV4SAY3.js} +5 -5
  97. package/dist/{update-check-PQ2MZ47K.js → update-check-266R4ZSY.js} +3 -3
  98. package/dist/{version-notify-C5CH7CVP.js → version-notify-XRGTEEDK.js} +37 -23
  99. package/dist/web-assets/assets/index-DtctoDXY.css +1 -0
  100. package/dist/web-assets/assets/index-Wpyb2CML.js +86 -0
  101. package/dist/web-assets/index.html +2 -2
  102. package/drizzle/0011_drop_turn_summary_event_id.sql +3 -0
  103. package/drizzle/0012_channel_gates.sql +17 -0
  104. package/drizzle/meta/_journal.json +14 -0
  105. package/package.json +2 -2
  106. package/templates/_base/src/lib/startup.ts +43 -2
  107. package/templates/claude/src/agent.ts +28 -2
  108. package/templates/claude/src/lib/session-reaper.ts +32 -0
  109. package/templates/claude/src/server.ts +10 -1
  110. package/dist/avatar-image-6KY6UU4D.js +0 -16
  111. package/dist/delivery-manager-JINZRG2W.js +0 -33
  112. package/dist/echo-text-VQPB5L2X.js +0 -33
  113. package/dist/message-delivery-R7JSQGYX.js +0 -37
  114. package/dist/mind-service-NB2JMWQZ.js +0 -39
  115. package/dist/scheduler-WVKII3LA.js +0 -31
  116. package/dist/sleep-manager-G6TXSDP7.js +0 -35
  117. package/dist/system-chat-NDAW6YP7.js +0 -37
  118. package/dist/web-assets/assets/index-B1JBQv2m.css +0 -1
  119. package/dist/web-assets/assets/index-CU6XWICM.js +0 -86
  120. package/dist/{accept-N5XULCXK.js → accept-YAFP5JFJ.js} +3 -3
  121. package/dist/{bridge-2PO3AI5D.js → bridge-TIZGSXOD.js} +3 -3
  122. package/dist/{create-DF3KISTW.js → create-A7PDHIIL.js} +3 -3
  123. package/dist/{env-VZ4MLFQW.js → env-HRL2PWAT.js} +5 -5
  124. package/dist/{extension-SEID47SR.js → extension-QBY4C44L.js} +3 -3
  125. package/dist/{list-OXIAY3W7.js → list-LQO2DCIK.js} +3 -3
  126. package/dist/{login-MY6ETFYB.js → login-6TLMI2TQ.js} +3 -3
  127. package/dist/{logout-NCG3TMDL.js → logout-BK53HXNY.js} +3 -3
  128. package/dist/{mind-history-VRO5A76U.js → mind-history-47DN6ILW.js} +4 -4
  129. package/dist/{mind-wake-LCLGTXA2.js → mind-wake-R54BPESY.js} +3 -3
  130. package/dist/{read-QVAXMTPV.js → read-PWAJOQIQ.js} +3 -3
  131. package/dist/{register-Y2H4PY5R.js → register-7SZJFHU6.js} +3 -3
  132. package/dist/{reject-EV35X64Y.js → reject-6VH7N2A4.js} +3 -3
  133. package/dist/{restart-FJQ662XJ.js → restart-H5WJDMA7.js} +4 -4
  134. package/dist/{skill-FKFHIJRR.js → skill-GUKG2YVH.js} +5 -5
  135. package/dist/{start-5JE3TV66.js → start-X46I3GXB.js} +4 -4
  136. package/dist/{stop-AAMTG3TU.js → stop-HJQV4YSM.js} +4 -4
@@ -1,16 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadMergedEnv
4
- } from "./chunk-ZDZISKCL.js";
4
+ } from "./chunk-IYG4FBMP.js";
5
5
  import {
6
6
  spiritDir
7
- } from "./chunk-BJZ4PV7R.js";
7
+ } from "./chunk-M6TTZRGH.js";
8
+ import {
9
+ clearMind,
10
+ linkInboundToActiveTurn,
11
+ publish as publish3,
12
+ summarizeOrphanedTurns
13
+ } from "./chunk-K23P5Q5N.js";
14
+ import {
15
+ getPrompt
16
+ } from "./chunk-JCMGDCGX.js";
8
17
  import {
9
18
  hashSkillDir,
10
19
  importSkillFromDir,
11
20
  removeSharedSkill,
12
21
  sharedSkillsDir
13
- } from "./chunk-KGIXX4NM.js";
22
+ } from "./chunk-IWJSNMSY.js";
14
23
  import {
15
24
  addMessage,
16
25
  createChannel,
@@ -20,63 +29,57 @@ import {
20
29
  getParticipants,
21
30
  joinChannel,
22
31
  publish as publish2
23
- } from "./chunk-5ZQZDDIK.js";
24
- import {
25
- readVoluteConfig,
26
- resolveWakeTriggers,
27
- writeVoluteConfig
28
- } from "./chunk-PFZBNKUH.js";
32
+ } from "./chunk-HPGN6XRT.js";
29
33
  import {
30
34
  isSandboxEnabled,
31
35
  wrapForSandbox
32
- } from "./chunk-JZ62SYWR.js";
36
+ } from "./chunk-M3XDVS5Q.js";
33
37
  import {
34
38
  markIdle,
35
39
  onMindEvent
36
- } from "./chunk-KQSFTPIM.js";
40
+ } from "./chunk-MTRMQFCQ.js";
41
+ import {
42
+ renderAvatarBlock
43
+ } from "./chunk-VMCZBXF2.js";
44
+ import {
45
+ readVoluteConfig,
46
+ resolveWakeTriggers,
47
+ writeVoluteConfig
48
+ } from "./chunk-PFZBNKUH.js";
37
49
  import {
38
50
  extractTextContent,
39
51
  getRoutingConfig,
40
52
  resolveDeliveryMode,
41
53
  resolveRoute,
42
54
  setRoutesChangeListener
43
- } from "./chunk-4JOQZPCE.js";
44
- import {
45
- clearMind,
46
- linkInboundToActiveTurn,
47
- publish as publish3,
48
- summarizeOrphanedTurns
49
- } from "./chunk-YXSUMLTC.js";
50
- import {
51
- getPrompt
52
- } from "./chunk-4YU7HF4L.js";
55
+ } from "./chunk-EIQDZC43.js";
53
56
  import {
54
57
  chownMindDir,
55
58
  exec,
56
59
  isIsolationEnabled,
57
60
  mindUserName,
58
61
  wrapForIsolation
59
- } from "./chunk-H2X54EHC.js";
62
+ } from "./chunk-DGGZVZTM.js";
60
63
  import {
61
64
  aiCompleteUtility,
62
65
  getAiConfig,
63
66
  resolveApiKey
64
- } from "./chunk-IHKGWTGT.js";
67
+ } from "./chunk-LWDTP6DH.js";
65
68
  import {
66
69
  readGlobalConfig,
67
70
  writeGlobalConfig
68
- } from "./chunk-F7H6ATM4.js";
71
+ } from "./chunk-SKNXMUIU.js";
69
72
  import {
70
73
  getOrCreateMindUser,
71
74
  getOrCreateSystemUser,
72
75
  getUser,
73
76
  getUserByUsername,
74
77
  syncMindProfile
75
- } from "./chunk-OV7ZQ2GH.js";
78
+ } from "./chunk-ODDGGLM6.js";
76
79
  import {
77
80
  publish,
78
81
  subscribe
79
- } from "./chunk-ZNMCCHCP.js";
82
+ } from "./chunk-LTLFOSA4.js";
80
83
  import {
81
84
  logger_default
82
85
  } from "./chunk-T2TP6ZC6.js";
@@ -92,15 +95,16 @@ import {
92
95
  stateDir,
93
96
  voluteHome,
94
97
  voluteSystemDir
95
- } from "./chunk-ALKYQULU.js";
98
+ } from "./chunk-WW6C3PLV.js";
96
99
  import {
97
100
  activity,
101
+ channelGates,
98
102
  deliveryQueue,
99
103
  messages,
100
104
  mindHistory,
101
105
  mindNotices,
102
106
  sessions
103
- } from "./chunk-ZV7MFISX.js";
107
+ } from "./chunk-2MJ7EOMT.js";
104
108
 
105
109
  // packages/daemon/src/lib/delivery/message-delivery.ts
106
110
  import { and as and5, eq as eq6, inArray as inArray3 } from "drizzle-orm";
@@ -934,7 +938,7 @@ var MindManager = class {
934
938
  if (this.shuttingDown || this.stopping.has(name)) return;
935
939
  mlog.error(`mind ${name} exited with code ${code}`);
936
940
  try {
937
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
941
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-3NA7ZJP3.js");
938
942
  const sleepMgr = getSleepManagerIfReady2();
939
943
  const sleepState = sleepMgr?.getState(name);
940
944
  if (sleepState?.sleeping) {
@@ -964,15 +968,15 @@ var MindManager = class {
964
968
  (err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
965
969
  );
966
970
  try {
967
- const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-JINZRG2W.js");
971
+ const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-TN5KK2AP.js");
968
972
  getDeliveryManager2().clearMindSessions(name);
969
973
  } catch (err) {
970
974
  if (!(err instanceof Error && err.message.includes("not initialized"))) {
971
975
  mlog.warn(`failed to clear delivery state for ${name} after crash`, logger_default.errorData(err));
972
976
  }
973
977
  }
974
- import("./mind-activity-tracker-3QRNJVR4.js").then(({ markIdle: markIdle2 }) => markIdle2(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
975
- import("./activity-events-NF6VWTJQ.js").then(
978
+ 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)));
979
+ import("./activity-events-MIOK3IOU.js").then(
976
980
  ({ publish: publish4 }) => publish4({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
977
981
  ).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
978
982
  const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
@@ -1030,7 +1034,7 @@ var MindManager = class {
1030
1034
  mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
1031
1035
  }
1032
1036
  try {
1033
- const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-JINZRG2W.js");
1037
+ const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-TN5KK2AP.js");
1034
1038
  getDeliveryManager2().clearMindSessions(name);
1035
1039
  } catch (err) {
1036
1040
  if (!(err instanceof Error && err.message.includes("not initialized"))) {
@@ -1038,7 +1042,7 @@ var MindManager = class {
1038
1042
  }
1039
1043
  }
1040
1044
  try {
1041
- const { clearEchoTextCache } = await import("./echo-text-VQPB5L2X.js");
1045
+ const { clearEchoTextCache } = await import("./echo-text-SEQCLGAT.js");
1042
1046
  clearEchoTextCache(name);
1043
1047
  } catch (err) {
1044
1048
  mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
@@ -1362,10 +1366,54 @@ function deleteSystemsConfig() {
1362
1366
  }
1363
1367
  }
1364
1368
 
1369
+ // packages/daemon/src/lib/util/sanitize-svg.ts
1370
+ import DOMPurify from "isomorphic-dompurify";
1371
+ var ALLOWED_TAGS = ["svg", "path", "circle", "rect", "line", "polyline", "polygon", "g"];
1372
+ var ALLOWED_ATTR = [
1373
+ "viewBox",
1374
+ "fill",
1375
+ "fill-opacity",
1376
+ "fill-rule",
1377
+ "stroke",
1378
+ "stroke-width",
1379
+ "stroke-linecap",
1380
+ "stroke-linejoin",
1381
+ "stroke-dasharray",
1382
+ "stroke-opacity",
1383
+ "opacity",
1384
+ "d",
1385
+ "cx",
1386
+ "cy",
1387
+ "r",
1388
+ "x",
1389
+ "y",
1390
+ "width",
1391
+ "height",
1392
+ "points",
1393
+ "transform"
1394
+ ];
1395
+ function sanitizeSvgIcon(html) {
1396
+ return DOMPurify.sanitize(html, {
1397
+ ALLOWED_TAGS,
1398
+ ALLOWED_ATTR,
1399
+ FORBID_TAGS: ["script", "foreignObject"],
1400
+ FORBID_ATTR: ["href", "xlink:href"]
1401
+ });
1402
+ }
1403
+
1365
1404
  // packages/daemon/src/lib/extensions.ts
1366
1405
  var VALID_EXTENSION_ID = /^[a-z0-9][a-z0-9_-]*$/;
1367
1406
  var loaded = [];
1368
1407
  var discovered = [];
1408
+ function enrichActivityMetadata(manifest, metadata) {
1409
+ const enriched = {
1410
+ ...metadata,
1411
+ ...manifest.icon && !metadata?.icon ? { icon: manifest.icon } : {},
1412
+ ...manifest.color && !metadata?.color ? { color: manifest.color } : {}
1413
+ };
1414
+ if (typeof enriched.icon === "string") enriched.icon = sanitizeSvgIcon(enriched.icon);
1415
+ return enriched;
1416
+ }
1369
1417
  function toCommandInfo(cmd) {
1370
1418
  const { handler: _, ...info } = cmd;
1371
1419
  return info;
@@ -1469,14 +1517,8 @@ async function buildContext(manifest, dataDir, authMw) {
1469
1517
  getUser: async (id) => getUser(id),
1470
1518
  getUserByUsername: async (username) => getUserByUsername(username),
1471
1519
  publishActivity: (event) => {
1472
- const enriched = {
1473
- ...event,
1474
- metadata: {
1475
- ...event.metadata,
1476
- ...manifest.icon && !event.metadata?.icon ? { icon: manifest.icon } : {},
1477
- ...manifest.color && !event.metadata?.color ? { color: manifest.color } : {}
1478
- }
1479
- };
1520
+ const metadata = enrichActivityMetadata(manifest, event.metadata);
1521
+ const enriched = { ...event, metadata };
1480
1522
  publish(enriched).catch(
1481
1523
  (err) => logger_default.error(`extension ${manifest.id}: failed to publish activity`, logger_default.errorData(err))
1482
1524
  );
@@ -1554,14 +1596,8 @@ async function loadExtension(manifest, app, authMw) {
1554
1596
  const result = await cmd.handler(parsed, {
1555
1597
  ...context,
1556
1598
  publishActivity: (rawEvent) => {
1557
- const event = {
1558
- ...rawEvent,
1559
- metadata: {
1560
- ...rawEvent.metadata,
1561
- ...manifest.icon && !rawEvent.metadata?.icon ? { icon: manifest.icon } : {},
1562
- ...manifest.color && !rawEvent.metadata?.color ? { color: manifest.color } : {}
1563
- }
1564
- };
1599
+ const metadata = enrichActivityMetadata(manifest, rawEvent.metadata);
1600
+ const event = { ...rawEvent, metadata };
1565
1601
  activityPromises.push(
1566
1602
  publish(event).catch((err) => {
1567
1603
  logger_default.error(
@@ -1601,7 +1637,7 @@ async function loadExtension(manifest, app, authMw) {
1601
1637
  }
1602
1638
  if (resolvedAssetsDir && existsSync6(resolvedAssetsDir)) {
1603
1639
  const assetsDir = resolvedAssetsDir;
1604
- const { readFile: readFile2, stat: fsStat } = await import("fs/promises");
1640
+ const { readFile, stat: fsStat } = await import("fs/promises");
1605
1641
  const { extname: ext } = await import("path");
1606
1642
  const mimeTypes = {
1607
1643
  ".html": "text/html",
@@ -1626,11 +1662,11 @@ async function loadExtension(manifest, app, authMw) {
1626
1662
  const s = await fsStat(filePath).catch(() => null);
1627
1663
  if (s?.isFile()) {
1628
1664
  const mime = mimeTypes[ext(filePath)] || "application/octet-stream";
1629
- const body = await readFile2(filePath);
1665
+ const body = await readFile(filePath);
1630
1666
  return c.body(body, 200, { "Content-Type": mime });
1631
1667
  }
1632
1668
  if (existsSync6(indexPath)) {
1633
- const body = await readFile2(indexPath, "utf-8");
1669
+ const body = await readFile(indexPath, "utf-8");
1634
1670
  return c.html(body);
1635
1671
  }
1636
1672
  return c.text("Not found", 404);
@@ -1948,7 +1984,7 @@ async function installNpmExtension(pkg) {
1948
1984
  throw new Error(`Extension "${pkg}" is already installed`);
1949
1985
  }
1950
1986
  const dir = ensureExtensionsNpmDir();
1951
- const { exec: exec2 } = await import("./exec-G3ZRST4Z.js");
1987
+ const { exec: exec2 } = await import("./exec-QIFYZG4C.js");
1952
1988
  try {
1953
1989
  await exec2("npm", ["install", "--ignore-scripts", pkg], { cwd: dir });
1954
1990
  } catch (err) {
@@ -1969,7 +2005,7 @@ async function uninstallNpmExtension(pkg) {
1969
2005
  packages.splice(idx, 1);
1970
2006
  writeExtensionsConfig(packages);
1971
2007
  try {
1972
- const { exec: exec2 } = await import("./exec-G3ZRST4Z.js");
2008
+ const { exec: exec2 } = await import("./exec-QIFYZG4C.js");
1973
2009
  await exec2("npm", ["uninstall", "--ignore-scripts", pkg], { cwd: extensionsNpmDir() });
1974
2010
  } catch (err) {
1975
2011
  logger_default.warn(
@@ -2818,7 +2854,7 @@ async function wakeMind(name) {
2818
2854
  async function startSpiritFull(name) {
2819
2855
  const entry = await findMind(name);
2820
2856
  if (entry?.dir) {
2821
- const { registerMindDir } = await import("./delivery-router-N4A7ZC5T.js");
2857
+ const { registerMindDir } = await import("./delivery-router-3QFG2ZEX.js");
2822
2858
  registerMindDir(name, entry.dir);
2823
2859
  }
2824
2860
  await getMindManager().startMind(name);
@@ -2848,14 +2884,14 @@ function buildSeedOrientation(createdBy, creatorType) {
2848
2884
  async function sendSeedOrientation(mindName, createdBy) {
2849
2885
  let creatorType;
2850
2886
  if (createdBy) {
2851
- const { getUserByUsername: getUserByUsername2 } = await import("./auth-CCFAU4GQ.js");
2887
+ const { getUserByUsername: getUserByUsername2 } = await import("./auth-A4VGHADI.js");
2852
2888
  creatorType = (await getUserByUsername2(createdBy))?.user_type;
2853
2889
  }
2854
2890
  await sendSystemMessage(mindName, buildSeedOrientation(createdBy, creatorType));
2855
2891
  }
2856
2892
  async function ensureCreatorDM(mindName, creatorUsername) {
2857
- const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-CCFAU4GQ.js");
2858
- const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-CJOOCGNW.js");
2893
+ const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-A4VGHADI.js");
2894
+ const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-C7IKYSHD.js");
2859
2895
  const mindUser = await getOrCreateMindUser2(mindName);
2860
2896
  const creatorUser = await getUserByUsername2(creatorUsername);
2861
2897
  if (!creatorUser) {
@@ -3314,7 +3350,7 @@ var SleepManager = class {
3314
3350
  * the real delivery stack. Returns whether the batch was handed off to the mind.
3315
3351
  */
3316
3352
  async deliverQueuedBatch(name, payloads) {
3317
- const { deliverBatch: deliverBatch2 } = await import("./message-delivery-R7JSQGYX.js");
3353
+ const { deliverBatch: deliverBatch2 } = await import("./message-delivery-ZE3YGJDD.js");
3318
3354
  return deliverBatch2(name, payloads);
3319
3355
  }
3320
3356
  // --- Wake failure handling (#419) ---
@@ -3709,8 +3745,8 @@ function getSleepManagerIfReady() {
3709
3745
  }
3710
3746
 
3711
3747
  // packages/daemon/src/lib/delivery/delivery-manager.ts
3712
- import { readFile, realpath } from "fs/promises";
3713
- import { extname, resolve as resolve8 } from "path";
3748
+ import { realpath } from "fs/promises";
3749
+ import { resolve as resolve8 } from "path";
3714
3750
  import { and as and4, eq as eq5, inArray as inArray2, sql as sql2 } from "drizzle-orm";
3715
3751
 
3716
3752
  // packages/daemon/src/lib/chat/typing.ts
@@ -3876,6 +3912,8 @@ var REDRIVE_INTERVAL_MS = 15e3;
3876
3912
  var RETRY_BASE_MS = 5e3;
3877
3913
  var RETRY_MAX_MS = 5 * 6e4;
3878
3914
  var REDRIVE_BATCH_LIMIT = 200;
3915
+ var GATED_RELEASE_LIMIT_PER_CHANNEL = 10;
3916
+ var GATED_NOTIFY_EVERY = 10;
3879
3917
  var mentionRegexCache = /* @__PURE__ */ new Map();
3880
3918
  var avatarBlocksCache = /* @__PURE__ */ new Map();
3881
3919
  var AVATAR_CACHE_TTL = 5 * 60 * 1e3;
@@ -3896,6 +3934,11 @@ var DeliveryManager = class {
3896
3934
  redriveTimer = null;
3897
3935
  /** Predicate for whether a mind is up; overridable in tests. */
3898
3936
  isMindRunning = (name) => tryGetMindManager()?.isRunning(name) ?? false;
3937
+ /** Sends a system message to a mind (invites, release summaries); overridable in tests. */
3938
+ notify = async (mindName, text) => {
3939
+ const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-N2F7HVY3.js");
3940
+ await sendSystemMessage2(mindName, text);
3941
+ };
3899
3942
  constructor() {
3900
3943
  setRoutesChangeListener((mind) => {
3901
3944
  this.releaseGated(mind).catch(
@@ -3907,6 +3950,10 @@ var DeliveryManager = class {
3907
3950
  setRunningCheck(fn) {
3908
3951
  this.isMindRunning = fn;
3909
3952
  }
3953
+ /** Test seam: capture/override system notifications sent to minds. */
3954
+ setNotifier(fn) {
3955
+ this.notify = fn;
3956
+ }
3910
3957
  // --- Public API ---
3911
3958
  /**
3912
3959
  * Route and deliver a message to a mind. This is the main entry point.
@@ -4069,9 +4116,15 @@ var DeliveryManager = class {
4069
4116
  if (redriven > 0) dlog.info(`redrove ${redriven} pending delivery queue rows`);
4070
4117
  }
4071
4118
  /**
4072
- * Re-evaluate a mind's `gated` rows against its current routes.json and promote any
4073
- * that now match to `pending` (delivered by the next redrive sweep). Called when the
4074
- * routing config changes.
4119
+ * Re-evaluate a mind's `gated` rows against its current routes.json when the routing
4120
+ * config changes. For each channel that now matches a `mind` route:
4121
+ * - the newest {@link GATED_RELEASE_LIMIT_PER_CHANNEL} rows are promoted to `pending`
4122
+ * and re-stamped with the freshly-resolved session (NOT the gate-time fallback), so
4123
+ * they land in the correct session instead of `main` (#537 bug 1);
4124
+ * - any older rows are `archived` (inert) so a long backlog can't flood the mind
4125
+ * in one sweep (#537 bug 2), and the mind gets one summary rather than a flood.
4126
+ * Rows resolving to a `file` destination are archived (the delivery manager doesn't
4127
+ * deliver file routes). Declined channels are skipped entirely — they stay gated.
4075
4128
  */
4076
4129
  async releaseGated(mindName) {
4077
4130
  const baseName = await getBaseName(mindName);
@@ -4084,7 +4137,8 @@ var DeliveryManager = class {
4084
4137
  dlog.warn(`failed to read gated rows for ${baseName}`, logger_default.errorData(err));
4085
4138
  return;
4086
4139
  }
4087
- const promote = [];
4140
+ const byChannel = /* @__PURE__ */ new Map();
4141
+ const archiveIds = [];
4088
4142
  for (const row of rows) {
4089
4143
  let payload;
4090
4144
  try {
@@ -4099,21 +4153,121 @@ var DeliveryManager = class {
4099
4153
  participantCount: payload.participantCount
4100
4154
  };
4101
4155
  const route = resolveRoute(config, meta);
4102
- if (route.matched && route.destination === "mind") {
4103
- promote.push(row.id);
4156
+ if (!route.matched) continue;
4157
+ if (route.destination === "file") {
4158
+ archiveIds.push(row.id);
4159
+ continue;
4104
4160
  }
4161
+ let session = route.session;
4162
+ if (session === "$new") {
4163
+ session = `new-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
4164
+ }
4165
+ const channel = row.channel ?? payload.channel ?? "unknown";
4166
+ const list = byChannel.get(channel) ?? [];
4167
+ list.push({ id: row.id, session });
4168
+ byChannel.set(channel, list);
4169
+ }
4170
+ const promote = [];
4171
+ const truncationNotes = [];
4172
+ for (const [channel, items] of byChannel) {
4173
+ if (await this.isChannelDeclined(baseName, channel)) continue;
4174
+ items.sort((a, b) => b.id - a.id);
4175
+ const keep = items.slice(0, GATED_RELEASE_LIMIT_PER_CHANNEL);
4176
+ const drop = items.slice(GATED_RELEASE_LIMIT_PER_CHANNEL);
4177
+ promote.push(...keep);
4178
+ for (const d of drop) archiveIds.push(d.id);
4179
+ if (drop.length > 0) {
4180
+ truncationNotes.push(
4181
+ `${channel}: released the ${keep.length} most recent message(s); ${drop.length} earlier message(s) were held while unrouted and remain in the channel history (volute chat read ${channel}).`
4182
+ );
4183
+ dlog.info(
4184
+ `truncated gated release for ${baseName} on ${channel}: kept ${keep.length}, archived ${drop.length}`
4185
+ );
4186
+ }
4187
+ }
4188
+ if (archiveIds.length > 0) {
4189
+ try {
4190
+ const db = await getDb();
4191
+ for (let i = 0; i < archiveIds.length; i += 500) {
4192
+ await db.update(deliveryQueue).set({ status: "archived" }).where(inArray2(deliveryQueue.id, archiveIds.slice(i, i + 500)));
4193
+ }
4194
+ } catch (err) {
4195
+ dlog.warn(`failed to archive gated rows for ${baseName}`, logger_default.errorData(err));
4196
+ }
4197
+ }
4198
+ if (promote.length === 0) {
4199
+ if (truncationNotes.length > 0) await this.sendReleaseSummary(mindName, truncationNotes);
4200
+ return;
4105
4201
  }
4106
- if (promote.length === 0) return;
4107
4202
  try {
4108
4203
  const db = await getDb();
4109
- await db.update(deliveryQueue).set({ status: "pending", attempts: 0, next_attempt_at: null }).where(inArray2(deliveryQueue.id, promote));
4204
+ for (const p of promote) {
4205
+ await db.update(deliveryQueue).set({ status: "pending", session: p.session, attempts: 0, next_attempt_at: null }).where(eq5(deliveryQueue.id, p.id));
4206
+ }
4110
4207
  dlog.info(`released ${promote.length} gated message(s) for ${baseName} after route change`);
4111
4208
  } catch (err) {
4112
4209
  dlog.warn(`failed to promote gated rows for ${baseName}`, logger_default.errorData(err));
4113
4210
  return;
4114
4211
  }
4212
+ if (truncationNotes.length > 0) await this.sendReleaseSummary(mindName, truncationNotes);
4115
4213
  await this.redrive();
4116
4214
  }
4215
+ /**
4216
+ * Whether the mind has explicitly declined a channel. A declined channel keeps
4217
+ * persisting history but never notifies and is never released. #537
4218
+ */
4219
+ async isChannelDeclined(baseName, channel) {
4220
+ if (!channel) return false;
4221
+ try {
4222
+ const db = await getDb();
4223
+ const rows = await db.select({ state: channelGates.state }).from(channelGates).where(and4(eq5(channelGates.mind, baseName), eq5(channelGates.channel, channel)));
4224
+ return rows[0]?.state === "declined";
4225
+ } catch (err) {
4226
+ dlog.warn(`failed to read gate state for ${baseName}/${channel}`, logger_default.errorData(err));
4227
+ return false;
4228
+ }
4229
+ }
4230
+ /**
4231
+ * Record that a mind has declined an unrouted channel: future messages are still
4232
+ * persisted (history is preserved) but never notify, and any currently-gated rows are
4233
+ * archived so they're inert. Returns the number of held messages archived. #537
4234
+ */
4235
+ async declineChannel(mindName, channel) {
4236
+ const baseName = await getBaseName(mindName);
4237
+ const db = await getDb();
4238
+ await db.insert(channelGates).values({ mind: baseName, channel, state: "declined" }).onConflictDoUpdate({
4239
+ target: [channelGates.mind, channelGates.channel],
4240
+ set: { state: "declined", updated_at: sql2`(datetime('now'))` }
4241
+ });
4242
+ const archived = await db.update(deliveryQueue).set({ status: "archived" }).where(
4243
+ and4(
4244
+ eq5(deliveryQueue.mind, baseName),
4245
+ eq5(deliveryQueue.channel, channel),
4246
+ eq5(deliveryQueue.status, "gated")
4247
+ )
4248
+ ).returning({ id: deliveryQueue.id });
4249
+ dlog.info(
4250
+ `declined channel ${channel} for ${baseName}; archived ${archived.length} held row(s)`
4251
+ );
4252
+ return archived.length;
4253
+ }
4254
+ /**
4255
+ * Send the mind a single summary when a routing change released a truncated backlog,
4256
+ * rather than a flood of individual messages. #537
4257
+ */
4258
+ async sendReleaseSummary(mindName, notes) {
4259
+ const body = [
4260
+ `[Channel backlog released]`,
4261
+ `A routing change matched channel(s) that had held messages while unrouted. To avoid flooding you, only the ${GATED_RELEASE_LIMIT_PER_CHANNEL} most recent per channel were delivered:`,
4262
+ "",
4263
+ ...notes.map((n) => `- ${n}`)
4264
+ ].join("\n");
4265
+ try {
4266
+ await this.notify(mindName, body);
4267
+ } catch (err) {
4268
+ dlog.warn(`failed to send release summary for ${mindName}`, logger_default.errorData(err));
4269
+ }
4270
+ }
4117
4271
  /**
4118
4272
  * Get pending (gated) messages for a mind.
4119
4273
  */
@@ -4513,41 +4667,44 @@ var DeliveryManager = class {
4513
4667
  }
4514
4668
  async gateMessage(mindName, session, payload) {
4515
4669
  const baseName = await getBaseName(mindName);
4516
- await this.persistToQueue(mindName, session, payload, "gated");
4670
+ const declined = await this.isChannelDeclined(baseName, payload.channel);
4671
+ await this.persistToQueue(mindName, session, payload, declined ? "archived" : "gated");
4672
+ if (declined) return;
4517
4673
  try {
4518
4674
  const db = await getDb();
4519
- const count = await db.select({ count: sql2`count(*)` }).from(deliveryQueue).where(
4675
+ const rows = await db.select({ count: sql2`count(*)` }).from(deliveryQueue).where(
4520
4676
  and4(
4521
4677
  eq5(deliveryQueue.mind, baseName),
4522
4678
  eq5(deliveryQueue.channel, payload.channel),
4523
- eq5(deliveryQueue.status, "gated")
4679
+ inArray2(deliveryQueue.status, ["gated", "archived"])
4524
4680
  )
4525
4681
  );
4526
- if ((count[0]?.count ?? 0) <= 1) {
4527
- await this.sendInviteNotification(mindName, payload);
4682
+ const count = rows[0]?.count ?? 0;
4683
+ if (count === 1 || count % GATED_NOTIFY_EVERY === 0) {
4684
+ await this.sendInviteNotification(mindName, payload, count);
4528
4685
  }
4529
4686
  } catch (err) {
4530
4687
  dlog.warn(`failed to check gated count for ${baseName}`, logger_default.errorData(err));
4531
4688
  }
4532
4689
  }
4533
- async sendInviteNotification(mindName, payload) {
4690
+ async sendInviteNotification(mindName, payload, gatedCount = 1) {
4534
4691
  const text = extractTextContent(payload.content);
4535
4692
  const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;
4536
4693
  const channel = payload.channel ?? "unknown";
4694
+ 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.`;
4537
4695
  const notification = [
4538
4696
  `[New channel: ${channel}]`,
4539
- `Someone new is reaching out \u2014 you don't have a route for this channel yet.`,
4697
+ heldLine,
4540
4698
  `Sender: ${payload.sender ?? "unknown"}`,
4541
4699
  payload.platform ? `Platform: ${payload.platform}` : null,
4542
4700
  payload.participantCount ? `Participants: ${payload.participantCount}` : null,
4543
4701
  "",
4544
4702
  `Preview: ${preview}`,
4545
4703
  "",
4546
- `To start hearing this channel, add a routing rule for "${channel}" to .config/routes.json.`,
4547
- `Messages are held safely until you do \u2014 nothing is lost. If you'd rather not engage, just leave it unrouted.`
4704
+ `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}).`,
4705
+ `To stop hearing about it, decline the channel: volute chat channels decline ${channel}`
4548
4706
  ].filter((line) => line !== null).join("\n");
4549
- const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-NDAW6YP7.js");
4550
- await sendSystemMessage2(mindName, notification);
4707
+ await this.notify(mindName, notification);
4551
4708
  }
4552
4709
  /**
4553
4710
  * Insert a delivery_queue row and return its id. The `mind` column is always keyed by
@@ -4612,18 +4769,6 @@ var DeliveryManager = class {
4612
4769
  const cached = avatarBlocksCache.get(cacheKey);
4613
4770
  if (cached && cached.expiresAt > Date.now()) return cached.blocks;
4614
4771
  const blocks = [];
4615
- let sharpDefault = null;
4616
- try {
4617
- const mod = await import("sharp");
4618
- sharpDefault = mod.default ?? mod;
4619
- } catch (err) {
4620
- const code = err.code;
4621
- if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
4622
- dlog.debug("sharp not available, sending full-size avatars");
4623
- } else {
4624
- dlog.warn("sharp import failed, sending full-size avatars", logger_default.errorData(err));
4625
- }
4626
- }
4627
4772
  for (const p of participants) {
4628
4773
  if (!p.avatar) continue;
4629
4774
  try {
@@ -4654,32 +4799,8 @@ var DeliveryManager = class {
4654
4799
  } else {
4655
4800
  filePath = resolve8(voluteHome(), "avatars", p.avatar);
4656
4801
  }
4657
- const ext = extname(filePath).toLowerCase();
4658
- const mimeMap = {
4659
- ".png": "image/png",
4660
- ".jpg": "image/jpeg",
4661
- ".jpeg": "image/jpeg",
4662
- ".gif": "image/gif",
4663
- ".webp": "image/webp"
4664
- };
4665
- const mediaType = mimeMap[ext];
4666
- if (!mediaType) continue;
4667
- const data = await readFile(filePath);
4668
- let imageData = data;
4669
- if (sharpDefault) {
4670
- try {
4671
- imageData = await sharpDefault(data).resize(128, 128, { fit: "cover" }).toBuffer();
4672
- } catch (err) {
4673
- dlog.warn(
4674
- `avatar resize failed for ${p.username}, sending original`,
4675
- logger_default.errorData(err)
4676
- );
4677
- }
4678
- }
4679
- blocks.push(
4680
- { type: "text", text: `[Avatar for ${p.username}]` },
4681
- { type: "image", media_type: mediaType, data: imageData.toString("base64") }
4682
- );
4802
+ const rendered = await renderAvatarBlock(filePath, p.username);
4803
+ if (rendered) blocks.push(...rendered);
4683
4804
  } catch (err) {
4684
4805
  const code = err.code;
4685
4806
  if (code !== "ENOENT") {
@@ -5007,7 +5128,7 @@ async function spiritWillHandle() {
5007
5128
  const spiritEntry = await findMind(SPIRIT_NAME);
5008
5129
  if (spiritEntry?.running && spiritEntry.mindType === "spirit") return true;
5009
5130
  try {
5010
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
5131
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-3NA7ZJP3.js");
5011
5132
  const sm = getSleepManagerIfReady2();
5012
5133
  if (sm?.isSleeping(SPIRIT_NAME)) return true;
5013
5134
  } catch (err) {
@@ -5035,7 +5156,7 @@ async function generateSystemFallbackReply(conversationId, mindName, message) {
5035
5156
  if (config.sleep.schedule?.wake) contextParts.push(`Wake cron: ${config.sleep.schedule.wake}`);
5036
5157
  }
5037
5158
  try {
5038
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
5159
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-3NA7ZJP3.js");
5039
5160
  const sm = getSleepManagerIfReady2();
5040
5161
  if (sm) {
5041
5162
  const state2 = sm.getState(mindName);
@@ -5120,6 +5241,7 @@ export {
5120
5241
  readSystemsConfig,
5121
5242
  writeSystemsConfig,
5122
5243
  deleteSystemsConfig,
5244
+ enrichActivityMetadata,
5123
5245
  parseCommandArgs,
5124
5246
  loadAllExtensions,
5125
5247
  getLoadedExtensions,