volute 0.53.0 → 0.55.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.
- package/dist/{activity-events-LQII6PZS.js → activity-events-TFAJBNBW.js} +2 -2
- package/dist/{ai-service-YW5SWCSB.js → ai-service-KEK2V3AP.js} +2 -2
- package/dist/{auth-KRDSM55Q.js → auth-Y27LTNZK.js} +3 -3
- package/dist/auto-upgrade-2MZF7NG3.js +46 -0
- package/dist/{avatar-image-52WGXLHV.js → avatar-image-SN576P45.js} +2 -2
- package/dist/{backup-WWXVIMYQ.js → backup-GDCWH6MC.js} +12 -12
- package/dist/channels-AFJWGX7W.js +203 -0
- package/dist/{chat-WK2BLM7B.js → chat-HVPOZK3O.js} +9 -9
- package/dist/{chunk-QUSMYGNQ.js → chunk-2674KIGI.js} +1 -0
- package/dist/chunk-2GATCSZK.js +520 -0
- package/dist/{chunk-P2TBUKLW.js → chunk-3SFKY4GI.js} +4 -4
- package/dist/{chunk-RMADJFA7.js → chunk-45QTYKQQ.js} +4 -4
- package/dist/{chunk-TAWRJ24T.js → chunk-4VAQKLMU.js} +8 -5
- package/dist/{chunk-OXWUEU5V.js → chunk-5U5GLZSD.js} +2 -2
- package/dist/{chunk-TPX6E6PL.js → chunk-6HBGUPBA.js} +2 -2
- package/dist/{chunk-UFTDRTCP.js → chunk-7AA4T2RT.js} +2 -2
- package/dist/{chunk-LN7XJ56R.js → chunk-7XKPG66D.js} +9 -9
- package/dist/{chunk-VDZ4I4ZP.js → chunk-BP2X5AIK.js} +8 -14
- package/dist/{chunk-PIZDPY4G.js → chunk-CKOA4LWV.js} +12 -12
- package/dist/{chunk-6NLW72HP.js → chunk-CKPAVQNK.js} +1 -1
- package/dist/{chunk-IQ7VNWH6.js → chunk-DUV4EMNW.js} +37 -3
- package/dist/{chunk-CV6BNGFK.js → chunk-FSXPPOPM.js} +3 -3
- package/dist/{chunk-UTKUO2KY.js → chunk-FZUPIS3T.js} +3 -3
- package/dist/{chunk-L7BKTZ5U.js → chunk-G3XAB4DK.js} +1 -1
- package/dist/{chunk-23UJGVYP.js → chunk-HLWQ3P3R.js} +50 -46
- package/dist/{chunk-AP2KQMMW.js → chunk-HTSWDOJE.js} +11 -14
- package/dist/{chunk-EDR47UE2.js → chunk-IR2FW4W7.js} +2 -2
- package/dist/{chunk-ICXY54ZC.js → chunk-IT4GEFDH.js} +1 -1
- package/dist/{chunk-F6D766VQ.js → chunk-KS62IHE2.js} +1 -1
- package/dist/{chunk-LNNI3MS4.js → chunk-MNKPAAQF.js} +9 -2
- package/dist/{chunk-HASADR3E.js → chunk-NCRG4SBR.js} +241 -66
- package/dist/{chunk-I7U5EEZC.js → chunk-NM5FDH4T.js} +1 -1
- package/dist/{chunk-7LT3YZUR.js → chunk-OFU6WJJZ.js} +1 -1
- package/dist/{chunk-M77B73VV.js → chunk-OIOW3FPT.js} +7 -2
- package/dist/{chunk-BDHKCLWK.js → chunk-OKM67ZWH.js} +1 -1
- package/dist/chunk-OUGD5L5U.js +918 -0
- package/dist/{chunk-X67BDDZR.js → chunk-P3GXOT2X.js} +48 -2
- package/dist/{chunk-U54PJPTB.js → chunk-PRZUBJ4K.js} +1 -1
- package/dist/{chunk-UO4MZQNO.js → chunk-QVS3C4DK.js} +15 -7
- package/dist/{chunk-RQRO6PC7.js → chunk-TMTAHKYP.js} +324 -48
- package/dist/{chunk-5DBGQBQR.js → chunk-UEZC5MNB.js} +29 -18
- package/dist/{chunk-7ARONCZW.js → chunk-VTFXZPC3.js} +13 -13
- package/dist/{chunk-UDZLPR34.js → chunk-W3OIPJPB.js} +14 -9
- package/dist/{chunk-HTB7KDRU.js → chunk-WZZCA23D.js} +5 -4
- package/dist/{chunk-5Y54AE3L.js → chunk-XAG53RWY.js} +1 -1
- package/dist/{chunk-HTRSSOHY.js → chunk-YU5EYQAE.js} +1 -1
- package/dist/{chunk-5N32EN4O.js → chunk-Z4Q6TQWL.js} +1 -1
- package/dist/{chunk-EGV6R6UF.js → chunk-Z7TNIIHW.js} +3 -1
- package/dist/cli.js +19 -19
- package/dist/{clock-SH4NQC2F.js → clock-X7V3C7NL.js} +3 -3
- package/dist/{cloud-sync-MJVJ33LD.js → cloud-sync-FM73SVFJ.js} +21 -21
- package/dist/{conversations-ZL6PHROD.js → conversations-FH3ODBL7.js} +3 -3
- package/dist/{create-G2RIKQYK.js → create-P75ZFTVN.js} +1 -1
- package/dist/{daemon-restart-BIGU4OKS.js → daemon-restart-JQMMMITM.js} +4 -4
- package/dist/daemon.js +469 -857
- package/dist/db-JH3NTERX.js +36 -0
- package/dist/delivery-manager-H23Q23N7.js +41 -0
- package/dist/{delivery-notices-65TB3CBX.js → delivery-notices-S7TKOACE.js} +5 -5
- package/dist/{delivery-router-RHANNTAC.js → delivery-router-KHBVEVVL.js} +4 -2
- package/dist/{down-QSNEMTTX.js → down-UN2EDX2Y.js} +3 -3
- package/dist/echo-text-ULEIFJJF.js +40 -0
- package/dist/{exec-ABD3DYA2.js → exec-Z34TTUO3.js} +1 -1
- package/dist/extensions-QD22GXZD.js +74 -0
- package/dist/{files-CHPNEQXN.js → files-DUGRXN6V.js} +5 -5
- package/dist/{import-DB2KULRO.js → import-RDDUGGFI.js} +3 -3
- package/dist/{isolation-GVV2IBAU.js → isolation-UQPDO34R.js} +1 -1
- package/dist/message-delivery-LU4EP244.js +46 -0
- package/dist/migrate-init-infrastructure-Z72UJ5NT.js +44 -0
- package/dist/migrate-name-placeholder-64AEAWPE.js +37 -0
- package/dist/{migrate-thread-config-EQSEP2XG.js → migrate-thread-config-SVF2M3D3.js} +1 -1
- package/dist/{mind-IX5AWUST.js → mind-BARCLI7O.js} +14 -14
- package/dist/{mind-activity-tracker-QF6DCX2X.js → mind-activity-tracker-UT23RGBF.js} +3 -3
- package/dist/{mind-contacts-BKUOUJPV.js → mind-contacts-PEXCDRXA.js} +8 -8
- package/dist/{mind-list-KNU34SUD.js → mind-list-7KBLPLES.js} +2 -2
- package/dist/mind-service-ZJCUBAUL.js +48 -0
- package/dist/{mind-sleep-D3DEYAWG.js → mind-sleep-24HV4RCA.js} +5 -5
- package/dist/{mind-status-BPTLQ5Z6.js → mind-status-4YW4PHMO.js} +6 -3
- package/dist/{package-BN5KMU54.js → package-AUDBDKAO.js} +12 -14
- package/dist/{prompts-HNKGCAEY.js → prompts-MOBF3TMO.js} +1 -1
- package/dist/{sandbox-NNWCTHRI.js → sandbox-VI7XWBIA.js} +2 -2
- package/dist/scheduler-GLINVC2B.js +27 -0
- package/dist/{seed-LHZ33UB4.js → seed-5JGR7ZRA.js} +1 -1
- package/dist/{seed-cmd-U4RVJ3UA.js → seed-cmd-M6A7N7F3.js} +2 -2
- package/dist/{seed-create-OQU4KNCB.js → seed-create-TGFZW4IO.js} +3 -3
- package/dist/{seed-readiness-WJYYRRCK.js → seed-readiness-XBH2MUNA.js} +2 -2
- package/dist/{seed-sprout-SFUYH2RG.js → seed-sprout-3IC5ZVTI.js} +11 -11
- package/dist/{send-FWZAMDBN.js → send-DHYBAYK4.js} +11 -11
- package/dist/{service-XQPMZAGI.js → service-T4P5W6RS.js} +2 -2
- package/dist/{service-install-2EWD7KXD.js → service-install-R4SNYUVO.js} +3 -3
- package/dist/{setup-RVSGCIF3.js → setup-6KAH3GRW.js} +4 -4
- package/dist/skills/dreaming/SKILL.md +2 -2
- package/dist/skills/dreaming/references/INSTALL.md +1 -1
- package/dist/skills/tending/SKILL.md +2 -0
- package/dist/skills/volute-mind/SKILL.md +8 -0
- package/dist/skills/volute-mind/references/integrations.md +3 -2
- package/dist/skills/volute-mind/references/routing.md +18 -5
- package/dist/{skills-QRPU6BWY.js → skills-LKQCYFVP.js} +3 -3
- package/dist/sleep-manager-Q3ITFNOJ.js +42 -0
- package/dist/{spirit-I5ECLGRI.js → spirit-FKXMGO3N.js} +8 -6
- package/dist/spirit-availability-5R2WYSWF.js +43 -0
- package/dist/{sprout-A2PMEF7Z.js → sprout-VEW3CUNX.js} +1 -1
- package/dist/src-246GSX7A.js +519 -0
- package/dist/src-LOUB3WPR.js +4189 -0
- package/dist/{status-EYG4A3OK.js → status-LCKTTIWS.js} +5 -5
- package/dist/{system-events-YTOU66QI.js → system-events-ZYOCYAO2.js} +5 -3
- package/dist/{systems-2DIJY4RF.js → systems-HL7SGSW7.js} +6 -6
- package/dist/{tailscale-KHNZFC7L.js → tailscale-F2IMBILZ.js} +1 -1
- package/dist/{template-hash-YKWMVYMM.js → template-hash-RJRMF6DT.js} +2 -2
- package/dist/{template-staleness-PY2CULH6.js → template-staleness-IBD722PN.js} +4 -4
- package/dist/{turn-tracker-NNHS6MAR.js → turn-tracker-BE34OI6O.js} +7 -7
- package/dist/{up-UGBHH743.js → up-J47J5JRB.js} +3 -3
- package/dist/{update-CIWI2JYA.js → update-ILCPVNDD.js} +2 -2
- package/dist/{upgrade-LIE46UBB.js → upgrade-XY2J7NWZ.js} +9 -1
- package/dist/variant-cleanup-JGJZ5KB4.js +24 -0
- package/dist/{version-notify-SJVAQLZI.js → version-notify-KIXYNHJC.js} +28 -13
- package/dist/web-assets/assets/{index-b7G7ZsVk.js → index-CoU1eUKE.js} +25 -25
- package/dist/web-assets/assets/index-hPn_fkz3.css +1 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +12 -14
- package/packages/extensions/intentions/dist/ui/assets/index-3FneLCKR.css +1 -0
- package/packages/extensions/intentions/dist/ui/assets/index-Bl5KEcli.js +67 -0
- package/packages/extensions/{notes → intentions}/dist/ui/index.html +3 -3
- package/packages/extensions/intentions/skills/intention-review/SKILL.md +38 -0
- package/packages/extensions/intentions/skills/intentions/SKILL.md +49 -0
- package/packages/extensions/intentions/skills/intentions/scripts/intentions-hook.sh +45 -0
- package/packages/extensions/pages/dist/ui/assets/index-BKxVQkA9.css +1 -0
- package/packages/extensions/pages/dist/ui/assets/index-BMt6sStP.js +68 -0
- package/packages/extensions/pages/dist/ui/index.html +2 -2
- package/packages/extensions/pages/skills/commons-gardening/SKILL.md +50 -0
- package/packages/extensions/pages/skills/pages/SKILL.md +140 -16
- package/templates/_base/.init/.config/prompts.json +2 -3
- package/templates/{pi → _base}/.init/.config/routes.json +1 -0
- package/templates/_base/.init/.local/hooks/pre-prompt/turn-context.ts +30 -0
- package/templates/_base/.init/memory/dreams/.gitkeep +0 -0
- package/templates/_base/home/.config/routes.json +1 -1
- package/templates/_base/home/VOLUTE.md +1 -1
- package/templates/_base/src/lib/codex-session-seed.ts +6 -55
- package/templates/_base/src/lib/context-breakdown.ts +11 -3
- package/templates/_base/src/lib/pi-session-seed.ts +8 -57
- package/templates/_base/src/lib/routing.ts +2 -2
- package/templates/_base/src/lib/seed-note.ts +4 -4
- package/templates/_base/src/lib/session-seed.ts +8 -53
- package/templates/_base/src/lib/startup.ts +4 -22
- package/templates/claude/.init/CLAUDE.md +4 -0
- package/templates/claude/package.json +2 -2
- package/templates/claude/src/agent.ts +60 -112
- package/templates/claude/src/lib/hooks/pre-compact.ts +6 -6
- package/templates/claude/src/lib/message-channel.ts +43 -15
- package/templates/claude/src/lib/recover.ts +69 -0
- package/templates/claude/src/lib/stream-consumer.ts +23 -5
- package/templates/claude/src/server.ts +0 -1
- package/templates/claude/volute-template.json +1 -1
- package/templates/codex/.init/AGENTS.md +4 -0
- package/templates/codex/package.json +2 -2
- package/templates/codex/src/agent.ts +23 -89
- package/templates/codex/volute-template.json +1 -1
- package/templates/pi/.init/MINDS.md +4 -0
- package/templates/pi/package.json +1 -1
- package/templates/pi/src/agent.ts +26 -93
- package/templates/pi/src/lib/mechanics-doc.ts +30 -0
- package/templates/pi/src/server.ts +3 -2
- package/templates/pi/volute-template.json +1 -1
- package/dist/channels-LW5ZLMSQ.js +0 -90
- package/dist/chunk-R772ET2M.js +0 -164
- package/dist/db-IPXRJUI7.js +0 -20
- package/dist/delivery-manager-FDHCZ7PU.js +0 -37
- package/dist/echo-text-KU6G4Z7V.js +0 -40
- package/dist/extensions-VACUKFA5.js +0 -60
- package/dist/message-delivery-TUXM3AAE.js +0 -46
- package/dist/mind-service-WHFBUKZE.js +0 -48
- package/dist/scheduler-5X5UIQOQ.js +0 -27
- package/dist/skills/plan-coordinator/SKILL.md +0 -60
- package/dist/sleep-manager-NWUCRBZB.js +0 -42
- package/dist/spirit-availability-NIUUYKU2.js +0 -43
- package/dist/src-HGNE2XG4.js +0 -832
- package/dist/src-LCSOQY6Z.js +0 -2222
- package/dist/src-UDMJZ6MH.js +0 -426
- package/dist/variant-cleanup-CZDLIY3F.js +0 -24
- package/dist/web-assets/assets/index-BHSU9r-4.css +0 -1
- package/packages/extensions/notes/dist/ui/assets/index-BBs8pyel.js +0 -67
- package/packages/extensions/notes/dist/ui/assets/index-D3cdui42.css +0 -1
- package/packages/extensions/notes/skills/notes/SKILL.md +0 -90
- package/packages/extensions/pages/dist/ui/assets/index-B589e8Xe.js +0 -2
- package/packages/extensions/pages/dist/ui/assets/index-BuXnE-rb.css +0 -1
- package/packages/extensions/plan/dist/ui/assets/index-B8s8wxyt.js +0 -67
- package/packages/extensions/plan/dist/ui/assets/index-jIkrt-vI.css +0 -1
- package/packages/extensions/plan/dist/ui/index.html +0 -14
- package/packages/extensions/plan/skills/plan/SKILL.md +0 -43
- package/packages/extensions/plan/skills/plan/scripts/plan-hook.sh +0 -37
- package/templates/claude/.init/.config/routes.json +0 -11
- package/templates/codex/.init/.config/routes.json +0 -11
- package/dist/{accept-I4GVGWA3.js → accept-RSDIVJL6.js} +3 -3
- package/dist/{bridge-KU2IKSH3.js → bridge-YK7NTRU5.js} +3 -3
- package/dist/{create-C3XZACTV.js → create-M727VUDF.js} +3 -3
- package/dist/{env-BJGM6FBA.js → env-7AZFBPWX.js} +7 -7
- package/dist/{extension-B2W52WZY.js → extension-MRYJWSMV.js} +3 -3
- package/dist/{list-R2GDWOEE.js → list-UQGIW7OF.js} +3 -3
- package/dist/{login-EZ4RRDQX.js → login-4NUS72Z5.js} +3 -3
- package/dist/{login-JDOAK465.js → login-57MMYILL.js} +4 -4
- package/dist/{logout-MWSIUOQU.js → logout-BYV433X6.js} +3 -3
- package/dist/{logout-K6ZGAJY5.js → logout-YFZAJRPY.js} +3 -3
- package/dist/{mind-history-RVF6KJ7K.js → mind-history-RZMMCHMX.js} +9 -9
- package/dist/{mind-wake-DCDFFQKX.js → mind-wake-LDGPRA42.js} +3 -3
- package/dist/{read-J5RM674N.js → read-RJ5NAW5J.js} +3 -3
- package/dist/{register-53LBGSDC.js → register-WNCSOTAU.js} +3 -3
- package/dist/{reject-K2A4UK2Y.js → reject-4NFLXKRN.js} +3 -3
- package/dist/{restart-R2WFNF5K.js → restart-B4GNMVF2.js} +7 -7
- package/dist/{skill-DXLLSUHB.js → skill-QYTBUDNQ.js} +7 -7
- package/dist/{start-XDOUAWN3.js → start-BDHJVOVX.js} +6 -6
- package/dist/{stop-O62RTEMD.js → stop-45QYSGYS.js} +7 -7
|
@@ -1,41 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
tryGetMindManager
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
extractTextContent,
|
|
7
|
-
getRoutingConfig,
|
|
8
|
-
resolveDeliveryMode,
|
|
9
|
-
resolveRoute,
|
|
10
|
-
setRoutesChangeListener,
|
|
11
|
-
shouldGate
|
|
12
|
-
} from "./chunk-UDZLPR34.js";
|
|
4
|
+
} from "./chunk-HLWQ3P3R.js";
|
|
13
5
|
import {
|
|
14
6
|
linkInboundToActiveTurn
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import {
|
|
17
|
-
MIND_LEVEL_THREAD,
|
|
18
|
-
newEphemeralSession
|
|
19
|
-
} from "./chunk-IQ7VNWH6.js";
|
|
20
|
-
import {
|
|
21
|
-
publish
|
|
22
|
-
} from "./chunk-LNNI3MS4.js";
|
|
7
|
+
} from "./chunk-BP2X5AIK.js";
|
|
23
8
|
import {
|
|
24
9
|
getParticipants,
|
|
25
10
|
publish as publish2
|
|
26
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-6HBGUPBA.js";
|
|
27
12
|
import {
|
|
28
13
|
onMindEvent
|
|
29
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-7AA4T2RT.js";
|
|
30
15
|
import {
|
|
31
16
|
renderAvatarBlock
|
|
32
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-IT4GEFDH.js";
|
|
33
18
|
import {
|
|
34
19
|
readVoluteConfig
|
|
35
20
|
} from "./chunk-WCFSBH33.js";
|
|
21
|
+
import {
|
|
22
|
+
clearConfigCache,
|
|
23
|
+
extractTextContent,
|
|
24
|
+
getRoutingConfig,
|
|
25
|
+
resolveDeliveryMode,
|
|
26
|
+
resolveRoute,
|
|
27
|
+
routesConfigPath,
|
|
28
|
+
setRoutesChangeListener,
|
|
29
|
+
shouldGate
|
|
30
|
+
} from "./chunk-W3OIPJPB.js";
|
|
31
|
+
import {
|
|
32
|
+
MIND_LEVEL_THREAD,
|
|
33
|
+
newEphemeralSession
|
|
34
|
+
} from "./chunk-DUV4EMNW.js";
|
|
35
|
+
import {
|
|
36
|
+
publish
|
|
37
|
+
} from "./chunk-MNKPAAQF.js";
|
|
36
38
|
import {
|
|
37
39
|
logger_default
|
|
38
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-2674KIGI.js";
|
|
39
41
|
import {
|
|
40
42
|
slugify
|
|
41
43
|
} from "./chunk-LGNUFVMR.js";
|
|
@@ -48,14 +50,15 @@ import {
|
|
|
48
50
|
} from "./chunk-H6X6CKDN.js";
|
|
49
51
|
import {
|
|
50
52
|
channelGates,
|
|
53
|
+
channels,
|
|
51
54
|
deliveryQueue,
|
|
52
55
|
messages,
|
|
53
56
|
mindHistory
|
|
54
57
|
} from "./chunk-2RNMBXD2.js";
|
|
55
58
|
|
|
56
59
|
// packages/daemon/src/lib/delivery/delivery-manager.ts
|
|
57
|
-
import { realpath } from "fs/promises";
|
|
58
|
-
import { resolve } from "path";
|
|
60
|
+
import { mkdir, readFile, realpath, rename, writeFile } from "fs/promises";
|
|
61
|
+
import { dirname, resolve } from "path";
|
|
59
62
|
import { and as and2, eq as eq2, inArray, sql } from "drizzle-orm";
|
|
60
63
|
|
|
61
64
|
// packages/daemon/src/lib/chat/typing.ts
|
|
@@ -141,8 +144,8 @@ function getTypingMap() {
|
|
|
141
144
|
function isConversationId(channel) {
|
|
142
145
|
return !channel.startsWith("@") && !channel.startsWith("#") && !channel.includes(":") && !channel.includes("/");
|
|
143
146
|
}
|
|
144
|
-
function publishTypingForChannels(
|
|
145
|
-
for (const channel of
|
|
147
|
+
function publishTypingForChannels(channels2, map) {
|
|
148
|
+
for (const channel of channels2) {
|
|
146
149
|
if (isConversationId(channel)) {
|
|
147
150
|
publish2(channel, { type: "typing", senders: map.get(channel) });
|
|
148
151
|
}
|
|
@@ -217,6 +220,27 @@ function formatHoldNotice(channelLabel, unseen) {
|
|
|
217
220
|
// packages/daemon/src/lib/delivery/delivery-manager.ts
|
|
218
221
|
var dlog = logger_default.child("delivery-manager");
|
|
219
222
|
var MAX_BATCH_SIZE = 50;
|
|
223
|
+
function normalizeChannelKey(channel) {
|
|
224
|
+
return channel.replace(/^[#@]/, "").toLowerCase();
|
|
225
|
+
}
|
|
226
|
+
function formatSuggestions(suggestions) {
|
|
227
|
+
const quoted = suggestions.map((s) => `"${s}"`);
|
|
228
|
+
if (quoted.length <= 1) return quoted.join("");
|
|
229
|
+
if (quoted.length === 2) return `${quoted[0]} or ${quoted[1]}`;
|
|
230
|
+
return `${quoted.slice(0, -1).join(", ")}, or ${quoted[quoted.length - 1]}`;
|
|
231
|
+
}
|
|
232
|
+
var UnknownChannelError = class extends Error {
|
|
233
|
+
constructor(channel, suggestions) {
|
|
234
|
+
super(
|
|
235
|
+
`no channel named "${channel}" \u2014 did you mean ${formatSuggestions(suggestions)}? (quote it: the shell strips an unquoted #name)`
|
|
236
|
+
);
|
|
237
|
+
this.channel = channel;
|
|
238
|
+
this.suggestions = suggestions;
|
|
239
|
+
this.name = "UnknownChannelError";
|
|
240
|
+
}
|
|
241
|
+
channel;
|
|
242
|
+
suggestions;
|
|
243
|
+
};
|
|
220
244
|
var REDRIVE_INTERVAL_MS = 15e3;
|
|
221
245
|
var RETRY_BASE_MS = 5e3;
|
|
222
246
|
var RETRY_MAX_MS = 5 * 6e4;
|
|
@@ -224,6 +248,7 @@ var REDRIVE_BATCH_LIMIT = 200;
|
|
|
224
248
|
var MAX_DELIVERY_ATTEMPTS = 10;
|
|
225
249
|
var GATED_RELEASE_LIMIT_PER_CHANNEL = 10;
|
|
226
250
|
var GATED_NOTIFY_EVERY = 10;
|
|
251
|
+
var PEEK_LIMIT = 50;
|
|
227
252
|
var mentionRegexCache = /* @__PURE__ */ new Map();
|
|
228
253
|
var avatarBlocksCache = /* @__PURE__ */ new Map();
|
|
229
254
|
var AVATAR_CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -241,17 +266,28 @@ var DeliveryManager = class {
|
|
|
241
266
|
* messages to the same session can't be reordered by resolvePort/enrichment latency.
|
|
242
267
|
*/
|
|
243
268
|
drainChains = /* @__PURE__ */ new Map();
|
|
269
|
+
/**
|
|
270
|
+
* Per-`baseName` promise chain that serializes gated releases. `releaseGated` reads gated
|
|
271
|
+
* rows and then writes `mind_history`; the promote UPDATE is idempotent but the history
|
|
272
|
+
* INSERT is not, so two overlapping runs would record the same message twice.
|
|
273
|
+
*/
|
|
274
|
+
releaseChains = /* @__PURE__ */ new Map();
|
|
275
|
+
/**
|
|
276
|
+
* Per-`baseName` promise chain serializing accepts, whose read-modify-write of
|
|
277
|
+
* routes.json would otherwise lose a rule when two run concurrently.
|
|
278
|
+
*/
|
|
279
|
+
acceptChains = /* @__PURE__ */ new Map();
|
|
244
280
|
redriveTimer = null;
|
|
245
281
|
/** Predicate for whether a mind is up; overridable in tests. */
|
|
246
282
|
isMindRunning = (name) => tryGetMindManager()?.isRunning(name) ?? false;
|
|
247
283
|
/** Delivers a channel event to a mind (invites, release summaries); overridable in tests. */
|
|
248
284
|
notify = async (mindName, text) => {
|
|
249
|
-
const { deliverEvent } = await import("./system-events-
|
|
285
|
+
const { deliverEvent } = await import("./system-events-ZYOCYAO2.js");
|
|
250
286
|
await deliverEvent(mindName, { type: "channel", body: text });
|
|
251
287
|
};
|
|
252
288
|
/** Surfaces a dead-lettered delivery as a next-turn failure notice; overridable in tests. */
|
|
253
289
|
notifyFailure = async (input) => {
|
|
254
|
-
const { recordNotice } = await import("./system-events-
|
|
290
|
+
const { recordNotice } = await import("./system-events-ZYOCYAO2.js");
|
|
255
291
|
await recordNotice(input);
|
|
256
292
|
};
|
|
257
293
|
/**
|
|
@@ -259,7 +295,7 @@ var DeliveryManager = class {
|
|
|
259
295
|
* side); no-ops for human senders. Overridable in tests.
|
|
260
296
|
*/
|
|
261
297
|
notifySenderFailure = async (sender, channel, reason) => {
|
|
262
|
-
const { recordSenderDeliveryFailure } = await import("./delivery-notices-
|
|
298
|
+
const { recordSenderDeliveryFailure } = await import("./delivery-notices-S7TKOACE.js");
|
|
263
299
|
await recordSenderDeliveryFailure(sender, channel, reason);
|
|
264
300
|
};
|
|
265
301
|
constructor() {
|
|
@@ -456,9 +492,27 @@ var DeliveryManager = class {
|
|
|
456
492
|
* in one sweep (#537 bug 2), and the mind gets one summary rather than a flood.
|
|
457
493
|
* Rows resolving to a `file` destination are archived (the delivery manager doesn't
|
|
458
494
|
* deliver file routes). Declined channels are skipped entirely — they stay gated.
|
|
495
|
+
*
|
|
496
|
+
* Releases are serialized per mind: the promote step reads gated rows and then writes
|
|
497
|
+
* `mind_history`, and while the promote UPDATE is idempotent the history INSERT is not —
|
|
498
|
+
* two overlapping runs would both see the same rows and record the message twice.
|
|
459
499
|
*/
|
|
460
500
|
async releaseGated(mindName) {
|
|
461
501
|
const baseName = await getBaseName(mindName);
|
|
502
|
+
const prev = this.releaseChains.get(baseName) ?? Promise.resolve();
|
|
503
|
+
const run = prev.then(() => this.releaseGatedInner(mindName, baseName));
|
|
504
|
+
const chain = run.then(
|
|
505
|
+
() => void 0,
|
|
506
|
+
() => void 0
|
|
507
|
+
);
|
|
508
|
+
this.releaseChains.set(baseName, chain);
|
|
509
|
+
try {
|
|
510
|
+
return await run;
|
|
511
|
+
} finally {
|
|
512
|
+
if (this.releaseChains.get(baseName) === chain) this.releaseChains.delete(baseName);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
async releaseGatedInner(mindName, baseName) {
|
|
462
516
|
const config = getRoutingConfig(baseName);
|
|
463
517
|
let rows;
|
|
464
518
|
try {
|
|
@@ -466,7 +520,7 @@ var DeliveryManager = class {
|
|
|
466
520
|
rows = await db.select().from(deliveryQueue).where(and2(eq2(deliveryQueue.mind, baseName), eq2(deliveryQueue.status, "gated")));
|
|
467
521
|
} catch (err) {
|
|
468
522
|
dlog.warn(`failed to read gated rows for ${baseName}`, logger_default.errorData(err));
|
|
469
|
-
return;
|
|
523
|
+
return { released: 0, archived: 0 };
|
|
470
524
|
}
|
|
471
525
|
const byChannel = /* @__PURE__ */ new Map();
|
|
472
526
|
const archiveIds = [];
|
|
@@ -515,7 +569,7 @@ var DeliveryManager = class {
|
|
|
515
569
|
for (const d of drop) archiveIds.push(d.id);
|
|
516
570
|
if (drop.length > 0) {
|
|
517
571
|
truncationNotes.push(
|
|
518
|
-
`${channel}: released the ${keep.length} most recent message(s); ${drop.length} earlier message(s) were held while unrouted and
|
|
572
|
+
`${channel}: released the ${keep.length} most recent message(s); ${drop.length} earlier message(s) were held while unrouted and stay readable (volute chat channels peek "${channel}").`
|
|
519
573
|
);
|
|
520
574
|
dlog.info(
|
|
521
575
|
`truncated gated release for ${baseName} on ${channel}: kept ${keep.length}, archived ${drop.length}`
|
|
@@ -534,9 +588,10 @@ var DeliveryManager = class {
|
|
|
534
588
|
}
|
|
535
589
|
if (promote.length === 0) {
|
|
536
590
|
if (truncationNotes.length > 0) await this.sendReleaseSummary(mindName, truncationNotes);
|
|
537
|
-
return;
|
|
591
|
+
return { released: 0, archived: archiveIds.length };
|
|
538
592
|
}
|
|
539
593
|
const orderedPromote = [...promote].sort((a, b) => a.id - b.id);
|
|
594
|
+
let committed = 0;
|
|
540
595
|
try {
|
|
541
596
|
const db = await getDb();
|
|
542
597
|
for (const p of orderedPromote) {
|
|
@@ -550,14 +605,15 @@ var DeliveryManager = class {
|
|
|
550
605
|
});
|
|
551
606
|
await tx.update(deliveryQueue).set({ status: "pending", thread: p.session, attempts: 0, next_attempt_at: null }).where(eq2(deliveryQueue.id, p.id));
|
|
552
607
|
});
|
|
608
|
+
committed++;
|
|
553
609
|
}
|
|
554
|
-
dlog.info(`released ${
|
|
610
|
+
dlog.info(`released ${committed} gated message(s) for ${baseName} after route change`);
|
|
555
611
|
} catch (err) {
|
|
556
612
|
dlog.error(
|
|
557
613
|
`failed to record+promote gated rows for ${baseName} (channels: ${[...byChannel.keys()].join(", ")})`,
|
|
558
614
|
logger_default.errorData(err)
|
|
559
615
|
);
|
|
560
|
-
return;
|
|
616
|
+
return { released: committed, archived: archiveIds.length };
|
|
561
617
|
}
|
|
562
618
|
for (const p of orderedPromote) {
|
|
563
619
|
publish(baseName, {
|
|
@@ -570,6 +626,63 @@ var DeliveryManager = class {
|
|
|
570
626
|
}
|
|
571
627
|
if (truncationNotes.length > 0) await this.sendReleaseSummary(mindName, truncationNotes);
|
|
572
628
|
await this.redrive();
|
|
629
|
+
return { released: committed, archived: archiveIds.length };
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Channels this mind could plausibly mean, from three sources:
|
|
633
|
+
*
|
|
634
|
+
* 1. Its current queue rows — channels with a live `gated`/`archived`/`pending` backlog.
|
|
635
|
+
* 2. Its own history — the durable record. Source 1 alone is not enough: delivered rows
|
|
636
|
+
* are *deleted* (`deleteQueueRows`), so a channel the mind has been using
|
|
637
|
+
* successfully for months drops out of the queue entirely, and an external-platform
|
|
638
|
+
* channel like `discord:general` is in no other table. Without this, the healthier
|
|
639
|
+
* the channel, the more likely we'd call it unrecognized.
|
|
640
|
+
* 3. Every *public* Volute channel, so a channel can be recognized before its first
|
|
641
|
+
* message ever arrives.
|
|
642
|
+
*
|
|
643
|
+
* Private channels are deliberately excluded from source 3. This set feeds the "did you
|
|
644
|
+
* mean X?" suggestion, so anything in it can be echoed back to a mind that guessed a
|
|
645
|
+
* nearby name — which would turn a typo into a way to confirm a private channel exists
|
|
646
|
+
* and learn its exact slug. Minds are untrusted principals. A private channel the mind
|
|
647
|
+
* is genuinely in still resolves via sources 1 and 2, which are scoped to it.
|
|
648
|
+
*/
|
|
649
|
+
async knownChannels(baseName) {
|
|
650
|
+
const db = await getDb();
|
|
651
|
+
const queued = await db.selectDistinct({ channel: deliveryQueue.channel }).from(deliveryQueue).where(eq2(deliveryQueue.mind, baseName));
|
|
652
|
+
const seen = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq2(mindHistory.mind, baseName));
|
|
653
|
+
const named = await db.selectDistinct({ name: channels.name }).from(channels).where(eq2(channels.private, 0));
|
|
654
|
+
const out = /* @__PURE__ */ new Set();
|
|
655
|
+
for (const r of queued) if (r.channel) out.add(r.channel);
|
|
656
|
+
for (const r of seen) if (r.channel) out.add(r.channel);
|
|
657
|
+
for (const r of named) out.add(`#${r.name}`);
|
|
658
|
+
return [...out];
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Match a channel name the caller supplied against the channels that actually exist.
|
|
662
|
+
*
|
|
663
|
+
* The failure this exists to prevent: a mind is told to run `... accept #garden`, hits
|
|
664
|
+
* the shell's comment character, drops the `#` to "fix" it, and accepts `garden` — a
|
|
665
|
+
* name nothing will ever send from. That wrote a permanent junk rule to routes.json and
|
|
666
|
+
* reported success, leaving the mind believing it had joined a channel it could send to
|
|
667
|
+
* but would never hear from. A one-way channel it had no reason to doubt.
|
|
668
|
+
*
|
|
669
|
+
* A near-miss (same name modulo sigil and case) is reported so callers can refuse with
|
|
670
|
+
* the real slug. A name with no near-miss is *not* an error — pre-routing a channel
|
|
671
|
+
* before its first message arrives is legitimate — but it comes back `known: false` so
|
|
672
|
+
* callers can say plainly that nothing was recognized instead of implying a join.
|
|
673
|
+
*/
|
|
674
|
+
async matchChannelName(baseName, channel) {
|
|
675
|
+
let all;
|
|
676
|
+
try {
|
|
677
|
+
all = await this.knownChannels(baseName);
|
|
678
|
+
} catch (err) {
|
|
679
|
+
dlog.warn(`failed to list known channels for ${baseName}`, logger_default.errorData(err));
|
|
680
|
+
return { known: true, suggestions: [] };
|
|
681
|
+
}
|
|
682
|
+
if (all.includes(channel)) return { known: true, suggestions: [] };
|
|
683
|
+
const key = normalizeChannelKey(channel);
|
|
684
|
+
const near = all.filter((c) => normalizeChannelKey(c) === key).sort();
|
|
685
|
+
return { known: false, suggestions: near };
|
|
573
686
|
}
|
|
574
687
|
/**
|
|
575
688
|
* Whether the mind has explicitly declined a channel. A declined channel keeps
|
|
@@ -593,6 +706,8 @@ var DeliveryManager = class {
|
|
|
593
706
|
*/
|
|
594
707
|
async declineChannel(mindName, channel) {
|
|
595
708
|
const baseName = await getBaseName(mindName);
|
|
709
|
+
const match = await this.matchChannelName(baseName, channel);
|
|
710
|
+
if (match.suggestions.length > 0) throw new UnknownChannelError(channel, match.suggestions);
|
|
596
711
|
const db = await getDb();
|
|
597
712
|
await db.insert(channelGates).values({ mind: baseName, channel, state: "declined" }).onConflictDoUpdate({
|
|
598
713
|
target: [channelGates.mind, channelGates.channel],
|
|
@@ -610,6 +725,163 @@ var DeliveryManager = class {
|
|
|
610
725
|
);
|
|
611
726
|
return archived.length;
|
|
612
727
|
}
|
|
728
|
+
/**
|
|
729
|
+
* Accept an unrouted (gated) channel: add a routing rule for it to the mind's routes.json
|
|
730
|
+
* and release its held messages immediately.
|
|
731
|
+
*
|
|
732
|
+
* This exists because a hand-edited routes.json is only noticed lazily, when the *next*
|
|
733
|
+
* inbound message triggers a config read — so editing the file on a quiet mind releases
|
|
734
|
+
* nothing and the held messages sit there indefinitely. Accept applies the change and
|
|
735
|
+
* reports what it actually released. #537
|
|
736
|
+
*/
|
|
737
|
+
async acceptChannel(mindName, channel, thread) {
|
|
738
|
+
const baseName = await getBaseName(mindName);
|
|
739
|
+
const prev = this.acceptChains.get(baseName) ?? Promise.resolve();
|
|
740
|
+
const run = prev.then(() => this.acceptChannelInner(mindName, baseName, channel, thread));
|
|
741
|
+
const chain = run.then(
|
|
742
|
+
() => void 0,
|
|
743
|
+
() => void 0
|
|
744
|
+
);
|
|
745
|
+
this.acceptChains.set(baseName, chain);
|
|
746
|
+
try {
|
|
747
|
+
return await run;
|
|
748
|
+
} finally {
|
|
749
|
+
if (this.acceptChains.get(baseName) === chain) this.acceptChains.delete(baseName);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
async acceptChannelInner(mindName, baseName, channel, thread) {
|
|
753
|
+
const match = await this.matchChannelName(baseName, channel);
|
|
754
|
+
if (match.suggestions.length > 0) throw new UnknownChannelError(channel, match.suggestions);
|
|
755
|
+
const path = routesConfigPath(baseName);
|
|
756
|
+
let config;
|
|
757
|
+
try {
|
|
758
|
+
const parsed = JSON.parse(await readFile(path, "utf-8"));
|
|
759
|
+
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
760
|
+
throw new Error(`routes.json for ${baseName} is malformed (not a JSON object)`);
|
|
761
|
+
}
|
|
762
|
+
config = parsed;
|
|
763
|
+
} catch (err) {
|
|
764
|
+
if (err?.code !== "ENOENT") {
|
|
765
|
+
throw new Error(
|
|
766
|
+
`routes.json for ${baseName} is unreadable or malformed \u2014 not modifying it`
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
config = {};
|
|
770
|
+
}
|
|
771
|
+
const rules = Array.isArray(config.rules) ? config.rules : [];
|
|
772
|
+
const targetThread = thread ?? "${channel}";
|
|
773
|
+
const existing = resolveRoute({ ...config, rules }, { channel });
|
|
774
|
+
const ruleAdded = !existing.matched;
|
|
775
|
+
if (ruleAdded) {
|
|
776
|
+
rules.push({ channel, thread: targetThread });
|
|
777
|
+
config.rules = rules;
|
|
778
|
+
await mkdir(dirname(path), { recursive: true });
|
|
779
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
780
|
+
await writeFile(tmp, `${JSON.stringify(config, null, 2)}
|
|
781
|
+
`);
|
|
782
|
+
await rename(tmp, path);
|
|
783
|
+
}
|
|
784
|
+
const db = await getDb();
|
|
785
|
+
await db.delete(channelGates).where(and2(eq2(channelGates.mind, baseName), eq2(channelGates.channel, channel)));
|
|
786
|
+
const heldBefore = await db.select({ id: deliveryQueue.id }).from(deliveryQueue).where(
|
|
787
|
+
and2(
|
|
788
|
+
eq2(deliveryQueue.mind, baseName),
|
|
789
|
+
eq2(deliveryQueue.channel, channel),
|
|
790
|
+
eq2(deliveryQueue.status, "gated")
|
|
791
|
+
)
|
|
792
|
+
);
|
|
793
|
+
clearConfigCache(baseName, { notify: false });
|
|
794
|
+
await this.releaseGated(mindName);
|
|
795
|
+
let released = 0;
|
|
796
|
+
let archived = 0;
|
|
797
|
+
const ids = heldBefore.map((r) => r.id);
|
|
798
|
+
for (let i = 0; i < ids.length; i += 500) {
|
|
799
|
+
const after = await db.select({ status: deliveryQueue.status }).from(deliveryQueue).where(inArray(deliveryQueue.id, ids.slice(i, i + 500)));
|
|
800
|
+
for (const row of after) {
|
|
801
|
+
if (row.status === "archived") archived++;
|
|
802
|
+
else if (row.status !== "gated") released++;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
dlog.info(
|
|
806
|
+
`accepted channel ${channel} for ${baseName} (rule ${ruleAdded ? "added" : "already present"}); released ${released}, archived ${archived}`
|
|
807
|
+
);
|
|
808
|
+
const finalRoute = ruleAdded ? resolveRoute({ ...config, rules }, { channel }) : existing;
|
|
809
|
+
return {
|
|
810
|
+
ruleAdded,
|
|
811
|
+
thread: finalRoute.destination === "file" ? finalRoute.path : finalRoute.session,
|
|
812
|
+
released,
|
|
813
|
+
archived,
|
|
814
|
+
known: match.known
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Read the messages held on a channel without changing anything. Archived rows are
|
|
819
|
+
* included: a truncated or declined backlog stays readable, which is what the invite and
|
|
820
|
+
* release-summary texts promise. Gated messages have no conversation, so `volute chat
|
|
821
|
+
* read` can't show them — this is the only way to see them.
|
|
822
|
+
*
|
|
823
|
+
* Returns the most recent {@link PEEK_LIMIT} messages (oldest-first within that window)
|
|
824
|
+
* alongside the true total, so peeking at a spam channel with a huge backlog can't dump
|
|
825
|
+
* all of it into the mind's context — the same reason releases are truncated. #537
|
|
826
|
+
*/
|
|
827
|
+
async peekChannel(mindName, channel) {
|
|
828
|
+
const baseName = await getBaseName(mindName);
|
|
829
|
+
const db = await getDb();
|
|
830
|
+
const rows = await db.select().from(deliveryQueue).where(
|
|
831
|
+
and2(
|
|
832
|
+
eq2(deliveryQueue.mind, baseName),
|
|
833
|
+
eq2(deliveryQueue.channel, channel),
|
|
834
|
+
inArray(deliveryQueue.status, ["gated", "archived"])
|
|
835
|
+
)
|
|
836
|
+
);
|
|
837
|
+
const messages2 = rows.sort((a, b) => a.id - b.id).slice(-PEEK_LIMIT).map((row) => {
|
|
838
|
+
let content = "";
|
|
839
|
+
try {
|
|
840
|
+
content = extractTextContent(JSON.parse(row.payload).content);
|
|
841
|
+
} catch {
|
|
842
|
+
content = "(unreadable payload)";
|
|
843
|
+
}
|
|
844
|
+
return {
|
|
845
|
+
sender: row.sender,
|
|
846
|
+
content,
|
|
847
|
+
createdAt: row.created_at,
|
|
848
|
+
status: row.status
|
|
849
|
+
};
|
|
850
|
+
});
|
|
851
|
+
const near = rows.length === 0 ? (await this.matchChannelName(baseName, channel)).suggestions : [];
|
|
852
|
+
return {
|
|
853
|
+
channel,
|
|
854
|
+
count: rows.length,
|
|
855
|
+
shown: messages2.length,
|
|
856
|
+
suggestions: near.length > 0 ? near : void 0,
|
|
857
|
+
messages: messages2
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Re-evaluate every mind's held messages against its current routes.json. Run at daemon
|
|
862
|
+
* startup: routes.json edits made while the daemon was down would otherwise not be noticed
|
|
863
|
+
* until the next inbound message on that channel — which, for a quiet channel, may be never.
|
|
864
|
+
*/
|
|
865
|
+
async releaseGatedSweep() {
|
|
866
|
+
let minds;
|
|
867
|
+
try {
|
|
868
|
+
const db = await getDb();
|
|
869
|
+
minds = await db.selectDistinct({ mind: deliveryQueue.mind }).from(deliveryQueue).where(eq2(deliveryQueue.status, "gated"));
|
|
870
|
+
} catch (err) {
|
|
871
|
+
dlog.warn("failed to list minds with gated messages", logger_default.errorData(err));
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
for (const { mind } of minds) {
|
|
875
|
+
try {
|
|
876
|
+
const { released, archived } = await this.releaseGated(mind);
|
|
877
|
+
if (released > 0 || archived > 0) {
|
|
878
|
+
dlog.info(`startup sweep for ${mind}: released ${released}, archived ${archived}`);
|
|
879
|
+
}
|
|
880
|
+
} catch (err) {
|
|
881
|
+
dlog.warn(`startup gated sweep failed for ${mind}`, logger_default.errorData(err));
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
613
885
|
/**
|
|
614
886
|
* Send the mind a single summary when a routing change released a truncated backlog,
|
|
615
887
|
* rather than a flood of individual messages. #537
|
|
@@ -872,8 +1144,8 @@ var DeliveryManager = class {
|
|
|
872
1144
|
const to = r.target_mind && r.target_mind !== r.mind ? ` to your variant ${r.target_mind}` : "";
|
|
873
1145
|
return `- a message${from}${on}${to} (sent ${r.created_at})`;
|
|
874
1146
|
});
|
|
875
|
-
const
|
|
876
|
-
const recovery =
|
|
1147
|
+
const channels2 = [...new Set(rows.map((r) => r.channel).filter((c) => !!c))];
|
|
1148
|
+
const recovery = channels2.length > 0 ? `The original message(s) remain in the channel history \u2014 read them with ${channels2.map((c) => `\`volute chat read ${c}\``).join(" or ")}.` : `The original message(s) remain in the channel history.`;
|
|
877
1149
|
const detail = [
|
|
878
1150
|
`${rows.length} message(s) sent to you could not be delivered after ${MAX_DELIVERY_ATTEMPTS} attempts and were dropped:`,
|
|
879
1151
|
...lines,
|
|
@@ -922,9 +1194,9 @@ ${channel}`, { sender: r.sender, channel });
|
|
|
922
1194
|
const { baseName, port } = resolved;
|
|
923
1195
|
const senders = /* @__PURE__ */ new Set();
|
|
924
1196
|
if (payload.sender) senders.add(payload.sender);
|
|
925
|
-
const
|
|
926
|
-
if (payload.channel)
|
|
927
|
-
this.incrementActive(baseName, session, senders,
|
|
1197
|
+
const channels2 = /* @__PURE__ */ new Set();
|
|
1198
|
+
if (payload.channel) channels2.add(payload.channel);
|
|
1199
|
+
this.incrementActive(baseName, session, senders, channels2);
|
|
928
1200
|
await onDeliveredToMind(baseName, payload.conversationId);
|
|
929
1201
|
linkInboundToActiveTurn(baseName, session, payload.channel).catch(
|
|
930
1202
|
(err) => dlog.warn(`failed to link mid-turn inbound for ${baseName}`, logger_default.errorData(err))
|
|
@@ -942,7 +1214,8 @@ ${channel}`, { sender: r.sender, channel });
|
|
|
942
1214
|
const body = JSON.stringify({
|
|
943
1215
|
...enrichedPayload,
|
|
944
1216
|
session,
|
|
945
|
-
instructions: sessionConfig.instructions
|
|
1217
|
+
instructions: sessionConfig.instructions,
|
|
1218
|
+
interrupt: sessionConfig.interrupt
|
|
946
1219
|
});
|
|
947
1220
|
try {
|
|
948
1221
|
const ok = await this.postToMind(port, body);
|
|
@@ -987,11 +1260,11 @@ ${channel}`, { sender: r.sender, channel });
|
|
|
987
1260
|
return { ...msg, payload: enrichedPayload };
|
|
988
1261
|
})
|
|
989
1262
|
);
|
|
990
|
-
const
|
|
1263
|
+
const channels2 = {};
|
|
991
1264
|
for (const msg of enrichedMessages) {
|
|
992
1265
|
const ch = msg.channel ?? "unknown";
|
|
993
|
-
if (!
|
|
994
|
-
|
|
1266
|
+
if (!channels2[ch]) channels2[ch] = [];
|
|
1267
|
+
channels2[ch].push(msg.payload);
|
|
995
1268
|
}
|
|
996
1269
|
const senders = /* @__PURE__ */ new Set();
|
|
997
1270
|
const channelSet = /* @__PURE__ */ new Set();
|
|
@@ -1013,7 +1286,7 @@ ${channel}`, { sender: r.sender, channel });
|
|
|
1013
1286
|
);
|
|
1014
1287
|
}
|
|
1015
1288
|
const typingMap = getTypingMap();
|
|
1016
|
-
for (const ch of Object.keys(
|
|
1289
|
+
for (const ch of Object.keys(channels2)) {
|
|
1017
1290
|
if (ch !== "unknown") typingMap.set(ch, baseName, { persistent: true });
|
|
1018
1291
|
}
|
|
1019
1292
|
const seenConvIds = /* @__PURE__ */ new Set();
|
|
@@ -1028,8 +1301,9 @@ ${channel}`, { sender: r.sender, channel });
|
|
|
1028
1301
|
}
|
|
1029
1302
|
const body = JSON.stringify({
|
|
1030
1303
|
session,
|
|
1031
|
-
batch: { channels },
|
|
1032
|
-
instructions: sessionConfig.instructions
|
|
1304
|
+
batch: { channels: channels2 },
|
|
1305
|
+
instructions: sessionConfig.instructions,
|
|
1306
|
+
interrupt: sessionConfig.interrupt
|
|
1033
1307
|
});
|
|
1034
1308
|
try {
|
|
1035
1309
|
const ok = await this.postToMind(port, body);
|
|
@@ -1164,7 +1438,7 @@ ${channel}`, { sender: r.sender, channel });
|
|
|
1164
1438
|
const details = detailLines.length > 0 ? `${detailLines.join("\n")}
|
|
1165
1439
|
|
|
1166
1440
|
` : "\n";
|
|
1167
|
-
const { getPrompt } = await import("./prompts-
|
|
1441
|
+
const { getPrompt } = await import("./prompts-MOBF3TMO.js");
|
|
1168
1442
|
const notification = await getPrompt("channel_invite", {
|
|
1169
1443
|
channel,
|
|
1170
1444
|
heldLine,
|
|
@@ -1284,7 +1558,7 @@ ${channel}`, { sender: r.sender, channel });
|
|
|
1284
1558
|
avatarBlocksCache.set(cacheKey, { blocks, expiresAt: now + AVATAR_CACHE_TTL });
|
|
1285
1559
|
return blocks;
|
|
1286
1560
|
}
|
|
1287
|
-
incrementActive(mind, session, senders,
|
|
1561
|
+
incrementActive(mind, session, senders, channels2) {
|
|
1288
1562
|
let mindSessions = this.sessionStates.get(mind);
|
|
1289
1563
|
if (!mindSessions) {
|
|
1290
1564
|
mindSessions = /* @__PURE__ */ new Map();
|
|
@@ -1300,7 +1574,7 @@ ${channel}`, { sender: r.sender, channel });
|
|
|
1300
1574
|
state2.activeCount++;
|
|
1301
1575
|
state2.lastDeliveredAt = Date.now();
|
|
1302
1576
|
if (senders) state2.lastDeliverySenders = senders;
|
|
1303
|
-
if (
|
|
1577
|
+
if (channels2) state2.lastDeliveryChannels = channels2;
|
|
1304
1578
|
mindSessions.set(session, state2);
|
|
1305
1579
|
}
|
|
1306
1580
|
decrementActive(mind, session) {
|
|
@@ -1343,6 +1617,8 @@ export {
|
|
|
1343
1617
|
publishTypingForChannels,
|
|
1344
1618
|
checkStaleSend,
|
|
1345
1619
|
formatHoldNotice,
|
|
1620
|
+
formatSuggestions,
|
|
1621
|
+
UnknownChannelError,
|
|
1346
1622
|
MAX_DELIVERY_ATTEMPTS,
|
|
1347
1623
|
DeliveryManager,
|
|
1348
1624
|
initDeliveryManager,
|