volute 0.52.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{accept-5NK5XJB5.js → accept-RSDIVJL6.js} +3 -0
- package/dist/auto-upgrade-HEVZKPTF.js +46 -0
- package/dist/{backup-YVGV2ZNM.js → backup-QRNX6RK7.js} +10 -10
- package/dist/channels-CKCY64TD.js +203 -0
- package/dist/{chat-VAZ25KVT.js → chat-ZUGNL5GS.js} +5 -5
- package/dist/chunk-3LHUXY3E.js +102 -0
- package/dist/{chunk-SW2U6HRB.js → chunk-444RR3YF.js} +21 -4
- package/dist/{chunk-YVR6EHWL.js → chunk-56IT2ZXF.js} +160 -18
- package/dist/chunk-5RBEEAKW.js +58 -0
- package/dist/{chunk-OXWUEU5V.js → chunk-5U5GLZSD.js} +2 -2
- package/dist/{chunk-G7A5LEVI.js → chunk-7XKPG66D.js} +5 -5
- package/dist/{chunk-R772ET2M.js → chunk-AAPNU7GA.js} +4 -0
- package/dist/{chunk-XRWUQPCA.js → chunk-ATZF2RNJ.js} +25 -3
- package/dist/{chunk-6NLW72HP.js → chunk-CKPAVQNK.js} +1 -1
- package/dist/{chunk-JHKBALS2.js → chunk-CLCHOXLR.js} +1 -1
- package/dist/{chunk-DMK5ASKV.js → chunk-F6D766VQ.js} +6 -2
- package/dist/{chunk-AXFLY5GU.js → chunk-HKGYZVWN.js} +16 -1
- package/dist/{chunk-ZEZQNP2G.js → chunk-LGXVGIWN.js} +103 -25
- package/dist/{chunk-CW7BQKF6.js → chunk-LKUGUQUW.js} +3 -3
- package/dist/{chunk-FXWBFDQR.js → chunk-OAYY3WF3.js} +59 -12
- package/dist/{chunk-ITK6HV47.js → chunk-OGPVJ53N.js} +14 -6
- package/dist/{chunk-M77B73VV.js → chunk-OIOW3FPT.js} +7 -2
- package/dist/chunk-OT5SXF7L.js +904 -0
- package/dist/{chunk-EDR47UE2.js → chunk-OYF25B62.js} +1 -1
- package/dist/{chunk-RPPTGPYF.js → chunk-PPSN2MLC.js} +2 -2
- package/dist/{chunk-K2F237KS.js → chunk-RGHF3OK5.js} +154 -18
- package/dist/{chunk-RU74YG46.js → chunk-VDUCLVZI.js} +338 -30
- package/dist/{chunk-724DVYHU.js → chunk-WHL6PKYP.js} +10 -10
- package/dist/{chunk-EEOMA4DX.js → chunk-WZZCA23D.js} +2 -1
- package/dist/{chunk-UDZLPR34.js → chunk-YSRYCS3N.js} +13 -8
- package/dist/{chunk-5N32EN4O.js → chunk-Z4Q6TQWL.js} +1 -1
- package/dist/{chunk-VPOMJMV6.js → chunk-ZNHL5KQJ.js} +1 -1
- package/dist/cli.js +17 -17
- package/dist/{clock-ZT5YT6HA.js → clock-X7V3C7NL.js} +2 -2
- package/dist/{cloud-sync-H7EWZASD.js → cloud-sync-52YNJRMR.js} +12 -11
- package/dist/{daemon-restart-4FWUA5JF.js → daemon-restart-QWHN5VMU.js} +4 -4
- package/dist/daemon.js +538 -782
- package/dist/{db-IPXRJUI7.js → db-I4EH5SLF.js} +3 -1
- package/dist/{delivery-manager-VYXVKUMP.js → delivery-manager-7R2YIQ2W.js} +13 -7
- package/dist/delivery-notices-RZQ6GONP.js +18 -0
- package/dist/{delivery-router-RHANNTAC.js → delivery-router-QS4NLJR6.js} +3 -1
- package/dist/{down-QSNEMTTX.js → down-UN2EDX2Y.js} +3 -3
- package/dist/{echo-text-PEEV7FQU.js → echo-text-73CYQRL7.js} +14 -13
- package/dist/{exec-ABD3DYA2.js → exec-Z34TTUO3.js} +1 -1
- package/dist/{extensions-N6L5NYTK.js → extensions-QT65JGV6.js} +22 -11
- package/dist/{files-BRY5RRIM.js → files-PCKBQHAY.js} +2 -1
- package/dist/{isolation-GVV2IBAU.js → isolation-UQPDO34R.js} +1 -1
- package/dist/{message-delivery-7URC52ME.js → message-delivery-TRKT4T2P.js} +16 -13
- package/dist/migrate-name-placeholder-C7NCNLWF.js +37 -0
- package/dist/{mind-NEXGT3KE.js → mind-CT6ANXK4.js} +11 -11
- package/dist/{mind-contacts-QYU4TPEA.js → mind-contacts-PEXCDRXA.js} +6 -6
- package/dist/{mind-history-OGNR4L7W.js → mind-history-RZMMCHMX.js} +83 -11
- package/dist/{mind-list-SYRA7CH5.js → mind-list-7KBLPLES.js} +1 -1
- package/dist/{mind-service-E26ZQXDS.js → mind-service-7EFIJ3ZA.js} +12 -11
- package/dist/{mind-sleep-LZAFBDZD.js → mind-sleep-24HV4RCA.js} +2 -2
- package/dist/{mind-status-Z2LQFA6F.js → mind-status-4YW4PHMO.js} +15 -0
- package/dist/{package-2W2XKSIP.js → package-F6S5NHY2.js} +4 -4
- package/dist/{prompts-NBO3HRK3.js → prompts-JECJIFPQ.js} +1 -1
- package/dist/{reject-TWB4QJHG.js → reject-4NFLXKRN.js} +6 -2
- package/dist/{scheduler-JTTWZDYU.js → scheduler-73Q4MQ7Y.js} +8 -6
- package/dist/{seed-64XEHRUP.js → seed-DRKAP3XV.js} +1 -1
- package/dist/{seed-cmd-UEL2QB3Q.js → seed-cmd-6IMTO3LG.js} +2 -2
- package/dist/{seed-create-4JRQDPGA.js → seed-create-VNLDYRFK.js} +1 -1
- package/dist/{seed-readiness-DYOVCYHN.js → seed-readiness-H5TTPAQH.js} +2 -2
- package/dist/{seed-sprout-NIDIOJXT.js → seed-sprout-PPGYGK4G.js} +5 -5
- package/dist/{send-2YKOZJJ5.js → send-UXUQKO4N.js} +37 -22
- 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-4IX6PCD3.js → setup-YUAQMDVZ.js} +4 -4
- package/dist/skills/dreaming/SKILL.md +2 -2
- package/dist/skills/dreaming/references/INSTALL.md +1 -1
- package/dist/skills/memory/SKILL.md +47 -43
- package/dist/skills/tending/SKILL.md +2 -0
- package/dist/skills/volute-mind/references/integrations.md +3 -2
- package/dist/skills/volute-mind/references/routing.md +18 -5
- package/dist/{skills-MPUALV3S.js → skills-GMQ5UIIW.js} +2 -2
- package/dist/{sleep-manager-OFP2GFXF.js → sleep-manager-KHI65FEX.js} +12 -11
- package/dist/{spirit-4ECYIVGE.js → spirit-Q5VGFWUI.js} +19 -3
- package/dist/{spirit-availability-Y54Y3675.js → spirit-availability-G5FMWKRH.js} +13 -12
- package/dist/{sprout-GJMZFRGZ.js → sprout-PMTRL7QN.js} +1 -1
- package/dist/{src-LCSOQY6Z.js → src-3FIW7DLH.js} +241 -87
- package/dist/src-H3UCOP6B.js +516 -0
- package/dist/{src-HGNE2XG4.js → src-LTTQVN2Z.js} +1 -1
- package/dist/{status-MWLE3RVC.js → status-LCKTTIWS.js} +2 -2
- package/dist/{system-events-V43HDA3T.js → system-events-Q7IVZ5T4.js} +5 -1
- package/dist/{systems-VDRUZXNF.js → systems-HL7SGSW7.js} +2 -2
- package/dist/{turn-tracker-C5PVIC3B.js → turn-tracker-HZDPMSCC.js} +4 -2
- package/dist/{up-H6LJZDRK.js → up-XZDSJVMU.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-STC6B5HR.js → variant-cleanup-QHGGY7ND.js} +8 -6
- package/dist/{version-notify-LBVHH5YV.js → version-notify-VNVFX6UP.js} +22 -7
- package/dist/web-assets/assets/{index-B_eYTa3R.js → index-CwtxGwe_.js} +16 -16
- package/dist/web-assets/assets/index-DhM1yYRN.css +1 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +4 -4
- package/packages/extensions/intentions/dist/ui/assets/index-3FneLCKR.css +1 -0
- package/packages/extensions/intentions/dist/ui/assets/index-CQz4aceM.js +67 -0
- package/packages/extensions/{plan → 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/skills/commons-gardening/SKILL.md +50 -0
- package/packages/extensions/pages/skills/pages/SKILL.md +18 -13
- package/templates/_base/.init/.config/prompts.json +3 -3
- package/templates/{pi → _base}/.init/.config/routes.json +1 -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 +463 -0
- package/templates/_base/src/lib/context-breakdown.ts +11 -3
- package/templates/_base/src/lib/daemon-client.ts +35 -0
- package/templates/_base/src/lib/pi-session-seed.ts +434 -0
- package/templates/_base/src/lib/routing.ts +2 -2
- package/templates/_base/src/lib/seed-note.ts +78 -0
- package/templates/_base/src/lib/session-seed.ts +375 -0
- package/templates/_base/src/lib/startup.ts +112 -6
- package/templates/claude/.init/CLAUDE.md +7 -3
- package/templates/claude/src/agent.ts +295 -101
- package/templates/claude/src/lib/hooks/pre-compact.ts +30 -5
- 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 +33 -2
- package/templates/claude/src/server.ts +2 -1
- package/templates/claude/volute-template.json +1 -1
- package/templates/codex/.init/AGENTS.md +7 -3
- package/templates/codex/src/agent.ts +256 -39
- package/templates/codex/src/server.ts +2 -1
- package/templates/codex/volute-template.json +1 -1
- package/templates/pi/.init/MINDS.md +7 -3
- package/templates/pi/src/agent.ts +269 -43
- package/templates/pi/src/lib/event-handler.ts +11 -0
- package/templates/pi/src/lib/mechanics-doc.ts +30 -0
- package/templates/pi/src/server.ts +5 -1
- package/templates/pi/volute-template.json +1 -1
- package/dist/channels-FSAHOQWI.js +0 -90
- package/dist/skills/plan-coordinator/SKILL.md +0 -60
- package/dist/src-UDMJZ6MH.js +0 -426
- package/dist/web-assets/assets/index-DhZrzddW.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/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/{env-IBW2QI6K.js → env-7AZFBPWX.js} +5 -5
- package/dist/{login-RJJTCVR3.js → login-4NUS72Z5.js} +3 -3
- package/dist/{login-SB5OENMV.js → login-57MMYILL.js} +4 -4
- package/dist/{register-OX4HRSGC.js → register-WNCSOTAU.js} +3 -3
- package/dist/{restart-AGEGNSYQ.js → restart-B4GNMVF2.js} +4 -4
- package/dist/{skill-B5OJEQUA.js → skill-QYTBUDNQ.js} +4 -4
- package/dist/{start-MUMNFR52.js → start-BDHJVOVX.js} +3 -3
- package/dist/{stop-EKERNYTX.js → stop-45QYSGYS.js} +4 -4
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
tryGetMindManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WHL6PKYP.js";
|
|
5
5
|
import {
|
|
6
|
+
clearConfigCache,
|
|
6
7
|
extractTextContent,
|
|
7
8
|
getRoutingConfig,
|
|
8
9
|
resolveDeliveryMode,
|
|
9
10
|
resolveRoute,
|
|
11
|
+
routesConfigPath,
|
|
10
12
|
setRoutesChangeListener,
|
|
11
13
|
shouldGate
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-YSRYCS3N.js";
|
|
13
15
|
import {
|
|
14
16
|
linkInboundToActiveTurn
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-56IT2ZXF.js";
|
|
16
18
|
import {
|
|
17
19
|
MIND_LEVEL_THREAD,
|
|
18
20
|
newEphemeralSession
|
|
19
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-HKGYZVWN.js";
|
|
20
22
|
import {
|
|
21
23
|
publish
|
|
22
24
|
} from "./chunk-LNNI3MS4.js";
|
|
@@ -36,6 +38,9 @@ import {
|
|
|
36
38
|
import {
|
|
37
39
|
logger_default
|
|
38
40
|
} from "./chunk-QUSMYGNQ.js";
|
|
41
|
+
import {
|
|
42
|
+
slugify
|
|
43
|
+
} from "./chunk-LGNUFVMR.js";
|
|
39
44
|
import {
|
|
40
45
|
findMind,
|
|
41
46
|
getBaseName,
|
|
@@ -45,14 +50,15 @@ import {
|
|
|
45
50
|
} from "./chunk-H6X6CKDN.js";
|
|
46
51
|
import {
|
|
47
52
|
channelGates,
|
|
53
|
+
channels,
|
|
48
54
|
deliveryQueue,
|
|
49
55
|
messages,
|
|
50
56
|
mindHistory
|
|
51
57
|
} from "./chunk-2RNMBXD2.js";
|
|
52
58
|
|
|
53
59
|
// packages/daemon/src/lib/delivery/delivery-manager.ts
|
|
54
|
-
import { realpath } from "fs/promises";
|
|
55
|
-
import { resolve } from "path";
|
|
60
|
+
import { mkdir, readFile, realpath, rename, writeFile } from "fs/promises";
|
|
61
|
+
import { dirname, resolve } from "path";
|
|
56
62
|
import { and as and2, eq as eq2, inArray, sql } from "drizzle-orm";
|
|
57
63
|
|
|
58
64
|
// packages/daemon/src/lib/chat/typing.ts
|
|
@@ -138,8 +144,8 @@ function getTypingMap() {
|
|
|
138
144
|
function isConversationId(channel) {
|
|
139
145
|
return !channel.startsWith("@") && !channel.startsWith("#") && !channel.includes(":") && !channel.includes("/");
|
|
140
146
|
}
|
|
141
|
-
function publishTypingForChannels(
|
|
142
|
-
for (const channel of
|
|
147
|
+
function publishTypingForChannels(channels2, map) {
|
|
148
|
+
for (const channel of channels2) {
|
|
143
149
|
if (isConversationId(channel)) {
|
|
144
150
|
publish2(channel, { type: "typing", senders: map.get(channel) });
|
|
145
151
|
}
|
|
@@ -214,6 +220,25 @@ function formatHoldNotice(channelLabel, unseen) {
|
|
|
214
220
|
// packages/daemon/src/lib/delivery/delivery-manager.ts
|
|
215
221
|
var dlog = logger_default.child("delivery-manager");
|
|
216
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
|
+
};
|
|
217
242
|
var REDRIVE_INTERVAL_MS = 15e3;
|
|
218
243
|
var RETRY_BASE_MS = 5e3;
|
|
219
244
|
var RETRY_MAX_MS = 5 * 6e4;
|
|
@@ -221,6 +246,7 @@ var REDRIVE_BATCH_LIMIT = 200;
|
|
|
221
246
|
var MAX_DELIVERY_ATTEMPTS = 10;
|
|
222
247
|
var GATED_RELEASE_LIMIT_PER_CHANNEL = 10;
|
|
223
248
|
var GATED_NOTIFY_EVERY = 10;
|
|
249
|
+
var PEEK_LIMIT = 50;
|
|
224
250
|
var mentionRegexCache = /* @__PURE__ */ new Map();
|
|
225
251
|
var avatarBlocksCache = /* @__PURE__ */ new Map();
|
|
226
252
|
var AVATAR_CACHE_TTL = 5 * 60 * 1e3;
|
|
@@ -238,19 +264,38 @@ var DeliveryManager = class {
|
|
|
238
264
|
* messages to the same session can't be reordered by resolvePort/enrichment latency.
|
|
239
265
|
*/
|
|
240
266
|
drainChains = /* @__PURE__ */ new Map();
|
|
267
|
+
/**
|
|
268
|
+
* Per-`baseName` promise chain that serializes gated releases. `releaseGated` reads gated
|
|
269
|
+
* rows and then writes `mind_history`; the promote UPDATE is idempotent but the history
|
|
270
|
+
* INSERT is not, so two overlapping runs would record the same message twice.
|
|
271
|
+
*/
|
|
272
|
+
releaseChains = /* @__PURE__ */ new Map();
|
|
273
|
+
/**
|
|
274
|
+
* Per-`baseName` promise chain serializing accepts, whose read-modify-write of
|
|
275
|
+
* routes.json would otherwise lose a rule when two run concurrently.
|
|
276
|
+
*/
|
|
277
|
+
acceptChains = /* @__PURE__ */ new Map();
|
|
241
278
|
redriveTimer = null;
|
|
242
279
|
/** Predicate for whether a mind is up; overridable in tests. */
|
|
243
280
|
isMindRunning = (name) => tryGetMindManager()?.isRunning(name) ?? false;
|
|
244
281
|
/** Delivers a channel event to a mind (invites, release summaries); overridable in tests. */
|
|
245
282
|
notify = async (mindName, text) => {
|
|
246
|
-
const { deliverEvent } = await import("./system-events-
|
|
283
|
+
const { deliverEvent } = await import("./system-events-Q7IVZ5T4.js");
|
|
247
284
|
await deliverEvent(mindName, { type: "channel", body: text });
|
|
248
285
|
};
|
|
249
286
|
/** Surfaces a dead-lettered delivery as a next-turn failure notice; overridable in tests. */
|
|
250
287
|
notifyFailure = async (input) => {
|
|
251
|
-
const { recordNotice } = await import("./system-events-
|
|
288
|
+
const { recordNotice } = await import("./system-events-Q7IVZ5T4.js");
|
|
252
289
|
await recordNotice(input);
|
|
253
290
|
};
|
|
291
|
+
/**
|
|
292
|
+
* Tells a mind *sender* its message was dropped (dead-lettered on the recipient's
|
|
293
|
+
* side); no-ops for human senders. Overridable in tests.
|
|
294
|
+
*/
|
|
295
|
+
notifySenderFailure = async (sender, channel, reason) => {
|
|
296
|
+
const { recordSenderDeliveryFailure } = await import("./delivery-notices-RZQ6GONP.js");
|
|
297
|
+
await recordSenderDeliveryFailure(sender, channel, reason);
|
|
298
|
+
};
|
|
254
299
|
constructor() {
|
|
255
300
|
setRoutesChangeListener((mind) => {
|
|
256
301
|
this.releaseGated(mind).catch(
|
|
@@ -270,6 +315,10 @@ var DeliveryManager = class {
|
|
|
270
315
|
setFailureNotifier(fn) {
|
|
271
316
|
this.notifyFailure = fn;
|
|
272
317
|
}
|
|
318
|
+
/** Test seam: capture/override sender-side dead-letter notices. */
|
|
319
|
+
setSenderFailureNotifier(fn) {
|
|
320
|
+
this.notifySenderFailure = fn;
|
|
321
|
+
}
|
|
273
322
|
// --- Public API ---
|
|
274
323
|
/**
|
|
275
324
|
* Route and deliver a message to a mind. This is the main entry point.
|
|
@@ -441,9 +490,27 @@ var DeliveryManager = class {
|
|
|
441
490
|
* in one sweep (#537 bug 2), and the mind gets one summary rather than a flood.
|
|
442
491
|
* Rows resolving to a `file` destination are archived (the delivery manager doesn't
|
|
443
492
|
* deliver file routes). Declined channels are skipped entirely — they stay gated.
|
|
493
|
+
*
|
|
494
|
+
* Releases are serialized per mind: the promote step reads gated rows and then writes
|
|
495
|
+
* `mind_history`, and while the promote UPDATE is idempotent the history INSERT is not —
|
|
496
|
+
* two overlapping runs would both see the same rows and record the message twice.
|
|
444
497
|
*/
|
|
445
498
|
async releaseGated(mindName) {
|
|
446
499
|
const baseName = await getBaseName(mindName);
|
|
500
|
+
const prev = this.releaseChains.get(baseName) ?? Promise.resolve();
|
|
501
|
+
const run = prev.then(() => this.releaseGatedInner(mindName, baseName));
|
|
502
|
+
const chain = run.then(
|
|
503
|
+
() => void 0,
|
|
504
|
+
() => void 0
|
|
505
|
+
);
|
|
506
|
+
this.releaseChains.set(baseName, chain);
|
|
507
|
+
try {
|
|
508
|
+
return await run;
|
|
509
|
+
} finally {
|
|
510
|
+
if (this.releaseChains.get(baseName) === chain) this.releaseChains.delete(baseName);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async releaseGatedInner(mindName, baseName) {
|
|
447
514
|
const config = getRoutingConfig(baseName);
|
|
448
515
|
let rows;
|
|
449
516
|
try {
|
|
@@ -451,7 +518,7 @@ var DeliveryManager = class {
|
|
|
451
518
|
rows = await db.select().from(deliveryQueue).where(and2(eq2(deliveryQueue.mind, baseName), eq2(deliveryQueue.status, "gated")));
|
|
452
519
|
} catch (err) {
|
|
453
520
|
dlog.warn(`failed to read gated rows for ${baseName}`, logger_default.errorData(err));
|
|
454
|
-
return;
|
|
521
|
+
return { released: 0, archived: 0 };
|
|
455
522
|
}
|
|
456
523
|
const byChannel = /* @__PURE__ */ new Map();
|
|
457
524
|
const archiveIds = [];
|
|
@@ -500,7 +567,7 @@ var DeliveryManager = class {
|
|
|
500
567
|
for (const d of drop) archiveIds.push(d.id);
|
|
501
568
|
if (drop.length > 0) {
|
|
502
569
|
truncationNotes.push(
|
|
503
|
-
`${channel}: released the ${keep.length} most recent message(s); ${drop.length} earlier message(s) were held while unrouted and
|
|
570
|
+
`${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}").`
|
|
504
571
|
);
|
|
505
572
|
dlog.info(
|
|
506
573
|
`truncated gated release for ${baseName} on ${channel}: kept ${keep.length}, archived ${drop.length}`
|
|
@@ -519,9 +586,10 @@ var DeliveryManager = class {
|
|
|
519
586
|
}
|
|
520
587
|
if (promote.length === 0) {
|
|
521
588
|
if (truncationNotes.length > 0) await this.sendReleaseSummary(mindName, truncationNotes);
|
|
522
|
-
return;
|
|
589
|
+
return { released: 0, archived: archiveIds.length };
|
|
523
590
|
}
|
|
524
591
|
const orderedPromote = [...promote].sort((a, b) => a.id - b.id);
|
|
592
|
+
let committed = 0;
|
|
525
593
|
try {
|
|
526
594
|
const db = await getDb();
|
|
527
595
|
for (const p of orderedPromote) {
|
|
@@ -535,14 +603,15 @@ var DeliveryManager = class {
|
|
|
535
603
|
});
|
|
536
604
|
await tx.update(deliveryQueue).set({ status: "pending", thread: p.session, attempts: 0, next_attempt_at: null }).where(eq2(deliveryQueue.id, p.id));
|
|
537
605
|
});
|
|
606
|
+
committed++;
|
|
538
607
|
}
|
|
539
|
-
dlog.info(`released ${
|
|
608
|
+
dlog.info(`released ${committed} gated message(s) for ${baseName} after route change`);
|
|
540
609
|
} catch (err) {
|
|
541
610
|
dlog.error(
|
|
542
611
|
`failed to record+promote gated rows for ${baseName} (channels: ${[...byChannel.keys()].join(", ")})`,
|
|
543
612
|
logger_default.errorData(err)
|
|
544
613
|
);
|
|
545
|
-
return;
|
|
614
|
+
return { released: committed, archived: archiveIds.length };
|
|
546
615
|
}
|
|
547
616
|
for (const p of orderedPromote) {
|
|
548
617
|
publish(baseName, {
|
|
@@ -555,6 +624,63 @@ var DeliveryManager = class {
|
|
|
555
624
|
}
|
|
556
625
|
if (truncationNotes.length > 0) await this.sendReleaseSummary(mindName, truncationNotes);
|
|
557
626
|
await this.redrive();
|
|
627
|
+
return { released: committed, archived: archiveIds.length };
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Channels this mind could plausibly mean, from three sources:
|
|
631
|
+
*
|
|
632
|
+
* 1. Its current queue rows — channels with a live `gated`/`archived`/`pending` backlog.
|
|
633
|
+
* 2. Its own history — the durable record. Source 1 alone is not enough: delivered rows
|
|
634
|
+
* are *deleted* (`deleteQueueRows`), so a channel the mind has been using
|
|
635
|
+
* successfully for months drops out of the queue entirely, and an external-platform
|
|
636
|
+
* channel like `discord:general` is in no other table. Without this, the healthier
|
|
637
|
+
* the channel, the more likely we'd call it unrecognized.
|
|
638
|
+
* 3. Every *public* Volute channel, so a channel can be recognized before its first
|
|
639
|
+
* message ever arrives.
|
|
640
|
+
*
|
|
641
|
+
* Private channels are deliberately excluded from source 3. This set feeds the "did you
|
|
642
|
+
* mean X?" suggestion, so anything in it can be echoed back to a mind that guessed a
|
|
643
|
+
* nearby name — which would turn a typo into a way to confirm a private channel exists
|
|
644
|
+
* and learn its exact slug. Minds are untrusted principals. A private channel the mind
|
|
645
|
+
* is genuinely in still resolves via sources 1 and 2, which are scoped to it.
|
|
646
|
+
*/
|
|
647
|
+
async knownChannels(baseName) {
|
|
648
|
+
const db = await getDb();
|
|
649
|
+
const queued = await db.selectDistinct({ channel: deliveryQueue.channel }).from(deliveryQueue).where(eq2(deliveryQueue.mind, baseName));
|
|
650
|
+
const seen = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq2(mindHistory.mind, baseName));
|
|
651
|
+
const named = await db.selectDistinct({ name: channels.name }).from(channels).where(eq2(channels.private, 0));
|
|
652
|
+
const out = /* @__PURE__ */ new Set();
|
|
653
|
+
for (const r of queued) if (r.channel) out.add(r.channel);
|
|
654
|
+
for (const r of seen) if (r.channel) out.add(r.channel);
|
|
655
|
+
for (const r of named) out.add(`#${r.name}`);
|
|
656
|
+
return [...out];
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Match a channel name the caller supplied against the channels that actually exist.
|
|
660
|
+
*
|
|
661
|
+
* The failure this exists to prevent: a mind is told to run `... accept #garden`, hits
|
|
662
|
+
* the shell's comment character, drops the `#` to "fix" it, and accepts `garden` — a
|
|
663
|
+
* name nothing will ever send from. That wrote a permanent junk rule to routes.json and
|
|
664
|
+
* reported success, leaving the mind believing it had joined a channel it could send to
|
|
665
|
+
* but would never hear from. A one-way channel it had no reason to doubt.
|
|
666
|
+
*
|
|
667
|
+
* A near-miss (same name modulo sigil and case) is reported so callers can refuse with
|
|
668
|
+
* the real slug. A name with no near-miss is *not* an error — pre-routing a channel
|
|
669
|
+
* before its first message arrives is legitimate — but it comes back `known: false` so
|
|
670
|
+
* callers can say plainly that nothing was recognized instead of implying a join.
|
|
671
|
+
*/
|
|
672
|
+
async matchChannelName(baseName, channel) {
|
|
673
|
+
let all;
|
|
674
|
+
try {
|
|
675
|
+
all = await this.knownChannels(baseName);
|
|
676
|
+
} catch (err) {
|
|
677
|
+
dlog.warn(`failed to list known channels for ${baseName}`, logger_default.errorData(err));
|
|
678
|
+
return { known: true, suggestions: [] };
|
|
679
|
+
}
|
|
680
|
+
if (all.includes(channel)) return { known: true, suggestions: [] };
|
|
681
|
+
const key = normalizeChannelKey(channel);
|
|
682
|
+
const near = all.filter((c) => normalizeChannelKey(c) === key).sort();
|
|
683
|
+
return { known: false, suggestions: near };
|
|
558
684
|
}
|
|
559
685
|
/**
|
|
560
686
|
* Whether the mind has explicitly declined a channel. A declined channel keeps
|
|
@@ -578,6 +704,8 @@ var DeliveryManager = class {
|
|
|
578
704
|
*/
|
|
579
705
|
async declineChannel(mindName, channel) {
|
|
580
706
|
const baseName = await getBaseName(mindName);
|
|
707
|
+
const match = await this.matchChannelName(baseName, channel);
|
|
708
|
+
if (match.suggestions.length > 0) throw new UnknownChannelError(channel, match.suggestions);
|
|
581
709
|
const db = await getDb();
|
|
582
710
|
await db.insert(channelGates).values({ mind: baseName, channel, state: "declined" }).onConflictDoUpdate({
|
|
583
711
|
target: [channelGates.mind, channelGates.channel],
|
|
@@ -595,6 +723,163 @@ var DeliveryManager = class {
|
|
|
595
723
|
);
|
|
596
724
|
return archived.length;
|
|
597
725
|
}
|
|
726
|
+
/**
|
|
727
|
+
* Accept an unrouted (gated) channel: add a routing rule for it to the mind's routes.json
|
|
728
|
+
* and release its held messages immediately.
|
|
729
|
+
*
|
|
730
|
+
* This exists because a hand-edited routes.json is only noticed lazily, when the *next*
|
|
731
|
+
* inbound message triggers a config read — so editing the file on a quiet mind releases
|
|
732
|
+
* nothing and the held messages sit there indefinitely. Accept applies the change and
|
|
733
|
+
* reports what it actually released. #537
|
|
734
|
+
*/
|
|
735
|
+
async acceptChannel(mindName, channel, thread) {
|
|
736
|
+
const baseName = await getBaseName(mindName);
|
|
737
|
+
const prev = this.acceptChains.get(baseName) ?? Promise.resolve();
|
|
738
|
+
const run = prev.then(() => this.acceptChannelInner(mindName, baseName, channel, thread));
|
|
739
|
+
const chain = run.then(
|
|
740
|
+
() => void 0,
|
|
741
|
+
() => void 0
|
|
742
|
+
);
|
|
743
|
+
this.acceptChains.set(baseName, chain);
|
|
744
|
+
try {
|
|
745
|
+
return await run;
|
|
746
|
+
} finally {
|
|
747
|
+
if (this.acceptChains.get(baseName) === chain) this.acceptChains.delete(baseName);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
async acceptChannelInner(mindName, baseName, channel, thread) {
|
|
751
|
+
const match = await this.matchChannelName(baseName, channel);
|
|
752
|
+
if (match.suggestions.length > 0) throw new UnknownChannelError(channel, match.suggestions);
|
|
753
|
+
const path = routesConfigPath(baseName);
|
|
754
|
+
let config;
|
|
755
|
+
try {
|
|
756
|
+
const parsed = JSON.parse(await readFile(path, "utf-8"));
|
|
757
|
+
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
758
|
+
throw new Error(`routes.json for ${baseName} is malformed (not a JSON object)`);
|
|
759
|
+
}
|
|
760
|
+
config = parsed;
|
|
761
|
+
} catch (err) {
|
|
762
|
+
if (err?.code !== "ENOENT") {
|
|
763
|
+
throw new Error(
|
|
764
|
+
`routes.json for ${baseName} is unreadable or malformed \u2014 not modifying it`
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
config = {};
|
|
768
|
+
}
|
|
769
|
+
const rules = Array.isArray(config.rules) ? config.rules : [];
|
|
770
|
+
const targetThread = thread ?? "${channel}";
|
|
771
|
+
const existing = resolveRoute({ ...config, rules }, { channel });
|
|
772
|
+
const ruleAdded = !existing.matched;
|
|
773
|
+
if (ruleAdded) {
|
|
774
|
+
rules.push({ channel, thread: targetThread });
|
|
775
|
+
config.rules = rules;
|
|
776
|
+
await mkdir(dirname(path), { recursive: true });
|
|
777
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
778
|
+
await writeFile(tmp, `${JSON.stringify(config, null, 2)}
|
|
779
|
+
`);
|
|
780
|
+
await rename(tmp, path);
|
|
781
|
+
}
|
|
782
|
+
const db = await getDb();
|
|
783
|
+
await db.delete(channelGates).where(and2(eq2(channelGates.mind, baseName), eq2(channelGates.channel, channel)));
|
|
784
|
+
const heldBefore = await db.select({ id: deliveryQueue.id }).from(deliveryQueue).where(
|
|
785
|
+
and2(
|
|
786
|
+
eq2(deliveryQueue.mind, baseName),
|
|
787
|
+
eq2(deliveryQueue.channel, channel),
|
|
788
|
+
eq2(deliveryQueue.status, "gated")
|
|
789
|
+
)
|
|
790
|
+
);
|
|
791
|
+
clearConfigCache(baseName, { notify: false });
|
|
792
|
+
await this.releaseGated(mindName);
|
|
793
|
+
let released = 0;
|
|
794
|
+
let archived = 0;
|
|
795
|
+
const ids = heldBefore.map((r) => r.id);
|
|
796
|
+
for (let i = 0; i < ids.length; i += 500) {
|
|
797
|
+
const after = await db.select({ status: deliveryQueue.status }).from(deliveryQueue).where(inArray(deliveryQueue.id, ids.slice(i, i + 500)));
|
|
798
|
+
for (const row of after) {
|
|
799
|
+
if (row.status === "archived") archived++;
|
|
800
|
+
else if (row.status !== "gated") released++;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
dlog.info(
|
|
804
|
+
`accepted channel ${channel} for ${baseName} (rule ${ruleAdded ? "added" : "already present"}); released ${released}, archived ${archived}`
|
|
805
|
+
);
|
|
806
|
+
const finalRoute = ruleAdded ? resolveRoute({ ...config, rules }, { channel }) : existing;
|
|
807
|
+
return {
|
|
808
|
+
ruleAdded,
|
|
809
|
+
thread: finalRoute.destination === "file" ? finalRoute.path : finalRoute.session,
|
|
810
|
+
released,
|
|
811
|
+
archived,
|
|
812
|
+
known: match.known
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Read the messages held on a channel without changing anything. Archived rows are
|
|
817
|
+
* included: a truncated or declined backlog stays readable, which is what the invite and
|
|
818
|
+
* release-summary texts promise. Gated messages have no conversation, so `volute chat
|
|
819
|
+
* read` can't show them — this is the only way to see them.
|
|
820
|
+
*
|
|
821
|
+
* Returns the most recent {@link PEEK_LIMIT} messages (oldest-first within that window)
|
|
822
|
+
* alongside the true total, so peeking at a spam channel with a huge backlog can't dump
|
|
823
|
+
* all of it into the mind's context — the same reason releases are truncated. #537
|
|
824
|
+
*/
|
|
825
|
+
async peekChannel(mindName, channel) {
|
|
826
|
+
const baseName = await getBaseName(mindName);
|
|
827
|
+
const db = await getDb();
|
|
828
|
+
const rows = await db.select().from(deliveryQueue).where(
|
|
829
|
+
and2(
|
|
830
|
+
eq2(deliveryQueue.mind, baseName),
|
|
831
|
+
eq2(deliveryQueue.channel, channel),
|
|
832
|
+
inArray(deliveryQueue.status, ["gated", "archived"])
|
|
833
|
+
)
|
|
834
|
+
);
|
|
835
|
+
const messages2 = rows.sort((a, b) => a.id - b.id).slice(-PEEK_LIMIT).map((row) => {
|
|
836
|
+
let content = "";
|
|
837
|
+
try {
|
|
838
|
+
content = extractTextContent(JSON.parse(row.payload).content);
|
|
839
|
+
} catch {
|
|
840
|
+
content = "(unreadable payload)";
|
|
841
|
+
}
|
|
842
|
+
return {
|
|
843
|
+
sender: row.sender,
|
|
844
|
+
content,
|
|
845
|
+
createdAt: row.created_at,
|
|
846
|
+
status: row.status
|
|
847
|
+
};
|
|
848
|
+
});
|
|
849
|
+
const near = rows.length === 0 ? (await this.matchChannelName(baseName, channel)).suggestions : [];
|
|
850
|
+
return {
|
|
851
|
+
channel,
|
|
852
|
+
count: rows.length,
|
|
853
|
+
shown: messages2.length,
|
|
854
|
+
suggestions: near.length > 0 ? near : void 0,
|
|
855
|
+
messages: messages2
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Re-evaluate every mind's held messages against its current routes.json. Run at daemon
|
|
860
|
+
* startup: routes.json edits made while the daemon was down would otherwise not be noticed
|
|
861
|
+
* until the next inbound message on that channel — which, for a quiet channel, may be never.
|
|
862
|
+
*/
|
|
863
|
+
async releaseGatedSweep() {
|
|
864
|
+
let minds;
|
|
865
|
+
try {
|
|
866
|
+
const db = await getDb();
|
|
867
|
+
minds = await db.selectDistinct({ mind: deliveryQueue.mind }).from(deliveryQueue).where(eq2(deliveryQueue.status, "gated"));
|
|
868
|
+
} catch (err) {
|
|
869
|
+
dlog.warn("failed to list minds with gated messages", logger_default.errorData(err));
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
for (const { mind } of minds) {
|
|
873
|
+
try {
|
|
874
|
+
const { released, archived } = await this.releaseGated(mind);
|
|
875
|
+
if (released > 0 || archived > 0) {
|
|
876
|
+
dlog.info(`startup sweep for ${mind}: released ${released}, archived ${archived}`);
|
|
877
|
+
}
|
|
878
|
+
} catch (err) {
|
|
879
|
+
dlog.warn(`startup gated sweep failed for ${mind}`, logger_default.errorData(err));
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
598
883
|
/**
|
|
599
884
|
* Send the mind a single summary when a routing change released a truncated backlog,
|
|
600
885
|
* rather than a flood of individual messages. #537
|
|
@@ -857,8 +1142,8 @@ var DeliveryManager = class {
|
|
|
857
1142
|
const to = r.target_mind && r.target_mind !== r.mind ? ` to your variant ${r.target_mind}` : "";
|
|
858
1143
|
return `- a message${from}${on}${to} (sent ${r.created_at})`;
|
|
859
1144
|
});
|
|
860
|
-
const
|
|
861
|
-
const recovery =
|
|
1145
|
+
const channels2 = [...new Set(rows.map((r) => r.channel).filter((c) => !!c))];
|
|
1146
|
+
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.`;
|
|
862
1147
|
const detail = [
|
|
863
1148
|
`${rows.length} message(s) sent to you could not be delivered after ${MAX_DELIVERY_ATTEMPTS} attempts and were dropped:`,
|
|
864
1149
|
...lines,
|
|
@@ -875,6 +1160,25 @@ var DeliveryManager = class {
|
|
|
875
1160
|
} catch (err) {
|
|
876
1161
|
dlog.warn(`failed to send dead-letter notice for ${mind}`, logger_default.errorData(err));
|
|
877
1162
|
}
|
|
1163
|
+
const senderPairs = /* @__PURE__ */ new Map();
|
|
1164
|
+
for (const r of rows) {
|
|
1165
|
+
if (!r.sender || r.sender === r.mind) continue;
|
|
1166
|
+
const recipient = r.target_mind ?? r.mind;
|
|
1167
|
+
const channel = !r.channel || r.channel === `@${slugify(r.sender)}` ? `@${recipient}` : r.channel;
|
|
1168
|
+
senderPairs.set(`${r.sender}
|
|
1169
|
+
${channel}`, { sender: r.sender, channel });
|
|
1170
|
+
}
|
|
1171
|
+
for (const { sender, channel } of senderPairs.values()) {
|
|
1172
|
+
try {
|
|
1173
|
+
await this.notifySenderFailure(
|
|
1174
|
+
sender,
|
|
1175
|
+
channel,
|
|
1176
|
+
`the recipient's process rejected it after ${MAX_DELIVERY_ATTEMPTS} attempts`
|
|
1177
|
+
);
|
|
1178
|
+
} catch (err) {
|
|
1179
|
+
dlog.warn(`failed to send sender dead-letter notice for ${sender}`, logger_default.errorData(err));
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
878
1182
|
}
|
|
879
1183
|
async deliverToMind(mindName, session, payload, sessionConfig, queueId) {
|
|
880
1184
|
if (queueId != null) this.inFlight.add(queueId);
|
|
@@ -888,9 +1192,9 @@ var DeliveryManager = class {
|
|
|
888
1192
|
const { baseName, port } = resolved;
|
|
889
1193
|
const senders = /* @__PURE__ */ new Set();
|
|
890
1194
|
if (payload.sender) senders.add(payload.sender);
|
|
891
|
-
const
|
|
892
|
-
if (payload.channel)
|
|
893
|
-
this.incrementActive(baseName, session, senders,
|
|
1195
|
+
const channels2 = /* @__PURE__ */ new Set();
|
|
1196
|
+
if (payload.channel) channels2.add(payload.channel);
|
|
1197
|
+
this.incrementActive(baseName, session, senders, channels2);
|
|
894
1198
|
await onDeliveredToMind(baseName, payload.conversationId);
|
|
895
1199
|
linkInboundToActiveTurn(baseName, session, payload.channel).catch(
|
|
896
1200
|
(err) => dlog.warn(`failed to link mid-turn inbound for ${baseName}`, logger_default.errorData(err))
|
|
@@ -908,7 +1212,8 @@ var DeliveryManager = class {
|
|
|
908
1212
|
const body = JSON.stringify({
|
|
909
1213
|
...enrichedPayload,
|
|
910
1214
|
session,
|
|
911
|
-
instructions: sessionConfig.instructions
|
|
1215
|
+
instructions: sessionConfig.instructions,
|
|
1216
|
+
interrupt: sessionConfig.interrupt
|
|
912
1217
|
});
|
|
913
1218
|
try {
|
|
914
1219
|
const ok = await this.postToMind(port, body);
|
|
@@ -953,11 +1258,11 @@ var DeliveryManager = class {
|
|
|
953
1258
|
return { ...msg, payload: enrichedPayload };
|
|
954
1259
|
})
|
|
955
1260
|
);
|
|
956
|
-
const
|
|
1261
|
+
const channels2 = {};
|
|
957
1262
|
for (const msg of enrichedMessages) {
|
|
958
1263
|
const ch = msg.channel ?? "unknown";
|
|
959
|
-
if (!
|
|
960
|
-
|
|
1264
|
+
if (!channels2[ch]) channels2[ch] = [];
|
|
1265
|
+
channels2[ch].push(msg.payload);
|
|
961
1266
|
}
|
|
962
1267
|
const senders = /* @__PURE__ */ new Set();
|
|
963
1268
|
const channelSet = /* @__PURE__ */ new Set();
|
|
@@ -979,7 +1284,7 @@ var DeliveryManager = class {
|
|
|
979
1284
|
);
|
|
980
1285
|
}
|
|
981
1286
|
const typingMap = getTypingMap();
|
|
982
|
-
for (const ch of Object.keys(
|
|
1287
|
+
for (const ch of Object.keys(channels2)) {
|
|
983
1288
|
if (ch !== "unknown") typingMap.set(ch, baseName, { persistent: true });
|
|
984
1289
|
}
|
|
985
1290
|
const seenConvIds = /* @__PURE__ */ new Set();
|
|
@@ -994,8 +1299,9 @@ var DeliveryManager = class {
|
|
|
994
1299
|
}
|
|
995
1300
|
const body = JSON.stringify({
|
|
996
1301
|
session,
|
|
997
|
-
batch: { channels },
|
|
998
|
-
instructions: sessionConfig.instructions
|
|
1302
|
+
batch: { channels: channels2 },
|
|
1303
|
+
instructions: sessionConfig.instructions,
|
|
1304
|
+
interrupt: sessionConfig.interrupt
|
|
999
1305
|
});
|
|
1000
1306
|
try {
|
|
1001
1307
|
const ok = await this.postToMind(port, body);
|
|
@@ -1130,7 +1436,7 @@ var DeliveryManager = class {
|
|
|
1130
1436
|
const details = detailLines.length > 0 ? `${detailLines.join("\n")}
|
|
1131
1437
|
|
|
1132
1438
|
` : "\n";
|
|
1133
|
-
const { getPrompt } = await import("./prompts-
|
|
1439
|
+
const { getPrompt } = await import("./prompts-JECJIFPQ.js");
|
|
1134
1440
|
const notification = await getPrompt("channel_invite", {
|
|
1135
1441
|
channel,
|
|
1136
1442
|
heldLine,
|
|
@@ -1250,7 +1556,7 @@ var DeliveryManager = class {
|
|
|
1250
1556
|
avatarBlocksCache.set(cacheKey, { blocks, expiresAt: now + AVATAR_CACHE_TTL });
|
|
1251
1557
|
return blocks;
|
|
1252
1558
|
}
|
|
1253
|
-
incrementActive(mind, session, senders,
|
|
1559
|
+
incrementActive(mind, session, senders, channels2) {
|
|
1254
1560
|
let mindSessions = this.sessionStates.get(mind);
|
|
1255
1561
|
if (!mindSessions) {
|
|
1256
1562
|
mindSessions = /* @__PURE__ */ new Map();
|
|
@@ -1266,7 +1572,7 @@ var DeliveryManager = class {
|
|
|
1266
1572
|
state2.activeCount++;
|
|
1267
1573
|
state2.lastDeliveredAt = Date.now();
|
|
1268
1574
|
if (senders) state2.lastDeliverySenders = senders;
|
|
1269
|
-
if (
|
|
1575
|
+
if (channels2) state2.lastDeliveryChannels = channels2;
|
|
1270
1576
|
mindSessions.set(session, state2);
|
|
1271
1577
|
}
|
|
1272
1578
|
decrementActive(mind, session) {
|
|
@@ -1309,6 +1615,8 @@ export {
|
|
|
1309
1615
|
publishTypingForChannels,
|
|
1310
1616
|
checkStaleSend,
|
|
1311
1617
|
formatHoldNotice,
|
|
1618
|
+
formatSuggestions,
|
|
1619
|
+
UnknownChannelError,
|
|
1312
1620
|
MAX_DELIVERY_ATTEMPTS,
|
|
1313
1621
|
DeliveryManager,
|
|
1314
1622
|
initDeliveryManager,
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import {
|
|
3
3
|
clearMind,
|
|
4
4
|
summarizeOrphanedTurns
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-56IT2ZXF.js";
|
|
6
6
|
import {
|
|
7
7
|
deliverEvent,
|
|
8
8
|
recordNotice
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-HKGYZVWN.js";
|
|
10
10
|
import {
|
|
11
11
|
getPrompt
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OAYY3WF3.js";
|
|
13
13
|
import {
|
|
14
14
|
isSandboxEnabled,
|
|
15
15
|
wrapForSandbox
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
chownMindDir,
|
|
19
19
|
isIsolationEnabled,
|
|
20
20
|
wrapForIsolation
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-OIOW3FPT.js";
|
|
22
22
|
import {
|
|
23
23
|
loadMergedEnv
|
|
24
24
|
} from "./chunk-GLM7TVWP.js";
|
|
@@ -805,9 +805,9 @@ var MindManager = class {
|
|
|
805
805
|
mlog.info(`started mind ${name} on port ${port}`);
|
|
806
806
|
await this.deliverPendingContext(name);
|
|
807
807
|
try {
|
|
808
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
808
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-KHI65FEX.js");
|
|
809
809
|
if (!getSleepManagerIfReady()?.isSleeping(name)) {
|
|
810
|
-
const { flushQueuedEvents } = await import("./system-events-
|
|
810
|
+
const { flushQueuedEvents } = await import("./system-events-Q7IVZ5T4.js");
|
|
811
811
|
const flushed = await flushQueuedEvents(name);
|
|
812
812
|
if (flushed > 0) mlog.info(`redelivered ${flushed} pending event(s) to ${name}`);
|
|
813
813
|
}
|
|
@@ -843,7 +843,7 @@ var MindManager = class {
|
|
|
843
843
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
844
844
|
mlog.error(`mind ${name} exited with code ${code}`);
|
|
845
845
|
try {
|
|
846
|
-
const { getSleepManagerIfReady } = await import("./sleep-manager-
|
|
846
|
+
const { getSleepManagerIfReady } = await import("./sleep-manager-KHI65FEX.js");
|
|
847
847
|
const sleepMgr = getSleepManagerIfReady();
|
|
848
848
|
const sleepState = sleepMgr?.getState(name);
|
|
849
849
|
if (sleepState?.sleeping) {
|
|
@@ -873,7 +873,7 @@ var MindManager = class {
|
|
|
873
873
|
(err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
|
|
874
874
|
);
|
|
875
875
|
try {
|
|
876
|
-
const { getDeliveryManager } = await import("./delivery-manager-
|
|
876
|
+
const { getDeliveryManager } = await import("./delivery-manager-7R2YIQ2W.js");
|
|
877
877
|
getDeliveryManager().clearMindSessions(name);
|
|
878
878
|
} catch (err) {
|
|
879
879
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
@@ -939,7 +939,7 @@ var MindManager = class {
|
|
|
939
939
|
mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
|
|
940
940
|
}
|
|
941
941
|
try {
|
|
942
|
-
const { getDeliveryManager } = await import("./delivery-manager-
|
|
942
|
+
const { getDeliveryManager } = await import("./delivery-manager-7R2YIQ2W.js");
|
|
943
943
|
getDeliveryManager().clearMindSessions(name);
|
|
944
944
|
} catch (err) {
|
|
945
945
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
@@ -947,7 +947,7 @@ var MindManager = class {
|
|
|
947
947
|
}
|
|
948
948
|
}
|
|
949
949
|
try {
|
|
950
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
950
|
+
const { clearEchoTextCache } = await import("./echo-text-73CYQRL7.js");
|
|
951
951
|
clearEchoTextCache(name);
|
|
952
952
|
} catch (err) {
|
|
953
953
|
mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
|
|
@@ -40,7 +40,8 @@ var cmd = command({
|
|
|
40
40
|
const age = mind.created ? relativeAge(mind.created) : "";
|
|
41
41
|
const ageLabel = age ? ` ${age} old` : "";
|
|
42
42
|
const template = mind.templateStale ? " [template: outdated]" : "";
|
|
43
|
-
|
|
43
|
+
const upgradeBlocked = mind.upgradeBlocked ? ` [upgrade blocked: ${mind.upgradeBlocked}]` : "";
|
|
44
|
+
console.log(` ${mind.name}: ${status}${label}${ageLabel}${template}${upgradeBlocked}`);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
});
|