volute 0.43.1 → 0.45.1
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-UISXY4Y5.js → activity-events-OMCSM7XW.js} +3 -3
- package/dist/{ai-service-FBCCEYF5.js → ai-service-534WUSDL.js} +4 -4
- package/dist/{archive-I32WHCJB.js → archive-6R4TZMYI.js} +3 -3
- package/dist/{auth-3RIG6GKN.js → auth-FRZJVQCS.js} +4 -4
- package/dist/avatar-image-BNWEXSA2.js +16 -0
- package/dist/backup-OZXHL3JS.js +381 -0
- package/dist/{chat-SDSX5TVV.js → chat-I4EDDIQC.js} +2 -2
- package/dist/{chunk-AD46AYZQ.js → chunk-2BONPXAF.js} +1 -1
- package/dist/{chunk-ONM2AZFL.js → chunk-322KE457.js} +14 -2
- package/dist/{chunk-PGPLCJPK.js → chunk-3ERIL673.js} +1 -1
- package/dist/{chunk-QQ2MVQU3.js → chunk-5AUKCNPE.js} +10 -10
- package/dist/{chunk-XDTKJNW5.js → chunk-5GT7URXJ.js} +1 -1
- package/dist/{chunk-DMUPZQWP.js → chunk-77BVGCTZ.js} +8 -8
- package/dist/{chunk-EFL5UOY4.js → chunk-CYI534DJ.js} +3 -3
- package/dist/{chunk-57LBQ7JX.js → chunk-FHWUQZXE.js} +1 -1
- package/dist/{chunk-YAMO2U3N.js → chunk-H7IYFRAL.js} +1 -1
- package/dist/{chunk-M3PGGSBH.js → chunk-HVU45COX.js} +1 -1
- package/dist/{chunk-O3DXAR44.js → chunk-IEWZ7VD7.js} +2 -2
- package/dist/{chunk-DE4EKQFH.js → chunk-IUXTYXFI.js} +3 -3
- package/dist/{chunk-K4EHBHPE.js → chunk-JSRMEY52.js} +7 -5
- package/dist/{chunk-QHG4OMZL.js → chunk-KDLYIECM.js} +37 -32
- package/dist/{chunk-EE7SZJON.js → chunk-KP3KWNG6.js} +2 -2
- package/dist/chunk-KVKP6VQ5.js +318 -0
- package/dist/{chunk-JVK6QQIC.js → chunk-LAXUUY7O.js} +2 -2
- package/dist/{chunk-C6ESRQZ7.js → chunk-OXXTPILE.js} +1 -1
- package/dist/{chunk-ATEX3ZPG.js → chunk-Q2XBKQ2D.js} +2 -2
- package/dist/{chunk-GZJFC76M.js → chunk-QMJJWTNL.js} +1 -1
- package/dist/chunk-SQL7M4HJ.js +120 -0
- package/dist/{chunk-YSV3VJQK.js → chunk-TSK5PI4L.js} +1 -1
- package/dist/{chunk-4SVONUTC.js → chunk-U4FNXDEJ.js} +350 -207
- package/dist/{chunk-2HIVYZDB.js → chunk-UMI4UIJG.js} +1 -1
- package/dist/{chunk-Y6O35D7K.js → chunk-V4GYPD5L.js} +41 -16
- package/dist/{chunk-KINGVQSR.js → chunk-Y6OPHHGM.js} +5 -5
- package/dist/{chunk-SPLPKBES.js → chunk-ZIR7HSAX.js} +6 -9
- package/dist/{chunk-7G2EEYXX.js → chunk-ZMIMSIIT.js} +2 -2
- package/dist/{chunk-L3IV23ZG.js → chunk-ZV7MFISX.js} +2 -0
- package/dist/cli.js +53 -22
- package/dist/{cloud-sync-KOEBUFW5.js → cloud-sync-Y7LQPRE3.js} +16 -16
- package/dist/{conversations-67WWW25C.js → conversations-G4LYMHAA.js} +4 -4
- package/dist/{create-7W3LV3J7.js → create-2YY7IPMS.js} +1 -1
- package/dist/{daemon-restart-EZXB73W7.js → daemon-restart-ABP54YN6.js} +7 -7
- package/dist/daemon.js +679 -478
- package/dist/{db-CZV62BOQ.js → db-JFLCJCUG.js} +2 -2
- package/dist/{db-IJL6B26S.js → db-LZTLA7XC.js} +1 -1
- package/dist/delivery-manager-R5STGLXB.js +32 -0
- package/dist/{delivery-router-HXBHYHJM.js → delivery-router-6DE3BPHO.js} +3 -3
- package/dist/{down-RRNYIGCU.js → down-XCAZXWLB.js} +5 -5
- package/dist/echo-text-NQXYYYCU.js +32 -0
- package/dist/{exec-SJK7VUTC.js → exec-4GWHAHS3.js} +3 -3
- package/dist/{export-7LPCIB5J.js → export-ID7YUSLZ.js} +3 -3
- package/dist/{extensions-PQPZT6LS.js → extensions-AYSPKE33.js} +16 -16
- package/dist/{files-LV7VAKPC.js → files-BQ2WB5KC.js} +3 -3
- package/dist/{import-XPECCRQ7.js → import-SKID37QX.js} +6 -6
- package/dist/{isolation-5AYEY55P.js → isolation-5MB3J73F.js} +3 -3
- package/dist/{login-KJSN4JDA.js → login-LM4NF4ZB.js} +2 -2
- package/dist/{logout-TMJGUQV2.js → logout-CTJVIZGA.js} +2 -2
- package/dist/message-delivery-IUAXRPFE.js +34 -0
- package/dist/{mind-G3WEWDM4.js → mind-KLHC7XYR.js} +6 -6
- package/dist/{mind-activity-tracker-JHAT6456.js → mind-activity-tracker-BGHJE7XA.js} +4 -4
- package/dist/mind-service-BF2LUQMH.js +36 -0
- package/dist/{package-24TL6DTF.js → package-BMHV7UHN.js} +1 -1
- package/dist/{registry-7LQHTRUX.js → registry-NUJAGLQJ.js} +2 -2
- package/dist/{sandbox-SJH3XOPZ.js → sandbox-72MWV4JL.js} +4 -4
- package/dist/scheduler-YHJRRV6E.js +30 -0
- package/dist/{schema-LRUTFOER.js → schema-PVNG3PQM.js} +1 -1
- package/dist/{seed-HHSJTM6O.js → seed-4MKQ5QKG.js} +1 -1
- package/dist/{seed-cmd-UPQY35RG.js → seed-cmd-LCXMR7VV.js} +2 -2
- package/dist/{seed-create-MDRMXMSB.js → seed-create-N2BF5LQX.js} +3 -3
- package/dist/{seed-sprout-4M7IQWMH.js → seed-sprout-6ZDP23YI.js} +7 -7
- package/dist/{send-ZBZWIH5V.js → send-O6W53VBM.js} +37 -42
- package/dist/{service-4EVZKGSA.js → service-AQVPUANO.js} +4 -4
- package/dist/{service-install-SHRM6NHK.js → service-install-4QNUDKLM.js} +5 -5
- package/dist/{setup-ZD2LMBR3.js → setup-3O6O2JZ7.js} +7 -7
- package/dist/{setup-25MMJ3J4.js → setup-73YBJZQL.js} +3 -3
- package/dist/skills/memory/SKILL.md +2 -1
- package/dist/skills/orientation/SKILL.md +1 -1
- package/dist/skills/plan-coordinator/SKILL.md +2 -2
- package/dist/skills/volute-admin/SKILL.md +9 -6
- package/dist/skills/volute-mind/SKILL.md +15 -16
- package/dist/skills/volute-mind/references/extensions.md +5 -3
- package/dist/skills/volute-mind/references/integrations.md +2 -2
- package/dist/skills/volute-mind/references/routing.md +19 -23
- package/dist/skills/volute-mind/references/variants.md +10 -8
- package/dist/{skills-4KQ377GN.js → skills-ND4ZFYUW.js} +5 -5
- package/dist/sleep-manager-YAHDOIU5.js +34 -0
- package/dist/{spirit-53DQPRNR.js → spirit-II3HOERM.js} +7 -7
- package/dist/{sprout-YKM2M6D6.js → sprout-4KUG76AQ.js} +1 -1
- package/dist/{src-O4PRLMKM.js → src-JAOVXO5W.js} +1 -0
- package/dist/{src-3EGHHKZK.js → src-XFY3XDD6.js} +16 -11
- package/dist/{src-L2Z7X5UT.js → src-YH6PQ7LJ.js} +264 -52
- package/dist/{status-WTU2WHXY.js → status-E5DQNCAO.js} +5 -5
- package/dist/system-chat-E65T2HSW.js +34 -0
- package/dist/{tailscale-Q7ZJL7S7.js → tailscale-QAWVKQKU.js} +2 -2
- package/dist/{turn-tracker-JZYERJ76.js → turn-tracker-QQIVVXBI.js} +7 -5
- package/dist/{up-LFZA4GI5.js → up-LG56MPBI.js} +6 -6
- package/dist/{update-EJ7Q77ZX.js → update-7TYVH5FM.js} +5 -5
- package/dist/{update-check-ACYYNKTW.js → update-check-VSCSJ7KX.js} +3 -3
- package/dist/{upgrade-37DAGF6H.js → upgrade-NWAZ7MLD.js} +1 -1
- package/dist/{version-notify-SRQJCQEK.js → version-notify-CODJOUW2.js} +17 -17
- package/dist/web-assets/assets/index-B1JBQv2m.css +1 -0
- package/dist/web-assets/assets/index-Dac11sKB.js +86 -0
- package/dist/web-assets/ext-theme.css +5 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +1 -1
- package/packages/extensions/notes/dist/ui/assets/{index-BbgANhFm.js → index-D0-DgYyj.js} +1 -1
- package/packages/extensions/notes/dist/ui/index.html +1 -1
- package/packages/extensions/notes/skills/notes/SKILL.md +31 -3
- package/packages/extensions/pages/dist/ui/assets/{index-Dxk-DYrf.js → index-CQjEQg1r.js} +1 -1
- package/packages/extensions/pages/dist/ui/index.html +1 -1
- package/packages/extensions/plan/skills/plan/SKILL.md +1 -1
- package/templates/_base/.init/.config/prompts.json +1 -1
- package/templates/_base/.init/.local/hooks/startup-context.ts +21 -4
- package/templates/_base/.init/MEMORY.md +2 -1
- package/templates/_base/home/VOLUTE.md +6 -9
- package/templates/_base/src/lib/startup.ts +1 -1
- package/templates/claude/.init/CLAUDE.md +1 -1
- package/templates/claude/src/agent.ts +30 -3
- package/templates/claude/src/lib/stream-consumer.ts +7 -4
- package/templates/codex/.init/AGENTS.md +4 -2
- package/templates/codex/src/agent.ts +13 -7
- package/templates/pi/.init/MINDS.md +4 -2
- package/templates/pi/src/lib/event-handler.ts +6 -2
- package/dist/delivery-manager-OMCCJEVP.js +0 -32
- package/dist/echo-text-KFSUCZXL.js +0 -32
- package/dist/message-delivery-HRGFNWO7.js +0 -34
- package/dist/mind-service-2C7AINBO.js +0 -36
- package/dist/scheduler-VON2TB52.js +0 -30
- package/dist/sleep-manager-AG5SQDDI.js +0 -34
- package/dist/system-chat-54RN3K67.js +0 -34
- package/dist/web-assets/assets/index-D7udpBCx.css +0 -1
- package/dist/web-assets/assets/index-DTDmEfPU.js +0 -79
- package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +0 -40
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadMergedEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5GT7URXJ.js";
|
|
5
5
|
import {
|
|
6
6
|
spiritDir
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5AUKCNPE.js";
|
|
8
8
|
import {
|
|
9
9
|
hashSkillDir,
|
|
10
10
|
importSkillFromDir,
|
|
11
11
|
removeSharedSkill,
|
|
12
12
|
sharedSkillsDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-Y6OPHHGM.js";
|
|
14
14
|
import {
|
|
15
15
|
addMessage,
|
|
16
16
|
createChannel,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
getParticipants,
|
|
21
21
|
joinChannel,
|
|
22
22
|
publish as publish2
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-JSRMEY52.js";
|
|
24
24
|
import {
|
|
25
25
|
readVoluteConfig,
|
|
26
26
|
writeVoluteConfig
|
|
@@ -28,52 +28,52 @@ import {
|
|
|
28
28
|
import {
|
|
29
29
|
isSandboxEnabled,
|
|
30
30
|
wrapForSandbox
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-OXXTPILE.js";
|
|
32
32
|
import {
|
|
33
33
|
markIdle,
|
|
34
34
|
onMindEvent
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-HVU45COX.js";
|
|
36
36
|
import {
|
|
37
37
|
extractTextContent,
|
|
38
38
|
getRoutingConfig,
|
|
39
39
|
resolveDeliveryMode,
|
|
40
40
|
resolveRoute,
|
|
41
41
|
setRoutesChangeListener
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-ZIR7HSAX.js";
|
|
43
43
|
import {
|
|
44
44
|
clearMind,
|
|
45
45
|
getPrompt,
|
|
46
46
|
linkInboundToActiveTurn,
|
|
47
47
|
publish as publish3,
|
|
48
48
|
summarizeOrphanedTurns
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-V4GYPD5L.js";
|
|
50
50
|
import {
|
|
51
51
|
chownMindDir,
|
|
52
52
|
exec,
|
|
53
53
|
isIsolationEnabled,
|
|
54
54
|
mindUserName,
|
|
55
55
|
wrapForIsolation
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-3ERIL673.js";
|
|
57
57
|
import {
|
|
58
58
|
aiCompleteUtility,
|
|
59
59
|
getAiConfig,
|
|
60
60
|
resolveApiKey
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-TSK5PI4L.js";
|
|
62
62
|
import {
|
|
63
63
|
readGlobalConfig,
|
|
64
64
|
writeGlobalConfig
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-322KE457.js";
|
|
66
66
|
import {
|
|
67
67
|
getOrCreateMindUser,
|
|
68
68
|
getOrCreateSystemUser,
|
|
69
69
|
getUser,
|
|
70
70
|
getUserByUsername,
|
|
71
71
|
syncMindProfile
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-CYI534DJ.js";
|
|
73
73
|
import {
|
|
74
74
|
publish,
|
|
75
75
|
subscribe
|
|
76
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-ZMIMSIIT.js";
|
|
77
77
|
import {
|
|
78
78
|
logger_default
|
|
79
79
|
} from "./chunk-T2TP6ZC6.js";
|
|
@@ -87,7 +87,7 @@ import {
|
|
|
87
87
|
stateDir,
|
|
88
88
|
voluteHome,
|
|
89
89
|
voluteSystemDir
|
|
90
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-H7IYFRAL.js";
|
|
91
91
|
import {
|
|
92
92
|
activity,
|
|
93
93
|
deliveryQueue,
|
|
@@ -95,10 +95,10 @@ import {
|
|
|
95
95
|
mindHistory,
|
|
96
96
|
mindNotices,
|
|
97
97
|
sessions
|
|
98
|
-
} from "./chunk-
|
|
98
|
+
} from "./chunk-ZV7MFISX.js";
|
|
99
99
|
|
|
100
100
|
// packages/daemon/src/lib/delivery/message-delivery.ts
|
|
101
|
-
import { and as
|
|
101
|
+
import { and as and5, eq as eq6, inArray as inArray3 } from "drizzle-orm";
|
|
102
102
|
|
|
103
103
|
// packages/daemon/src/lib/daemon/sleep-manager.ts
|
|
104
104
|
import { execFile as execFile2, spawn as spawnChild } from "child_process";
|
|
@@ -337,8 +337,9 @@ function resolveMindToken(token) {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
// packages/daemon/src/lib/daemon/notices.ts
|
|
340
|
-
import { and, asc, eq, lte, sql } from "drizzle-orm";
|
|
340
|
+
import { and, asc, eq, lte, or, sql } from "drizzle-orm";
|
|
341
341
|
var nlog = logger_default.child("notices");
|
|
342
|
+
var MIND_LEVEL_SESSION = "";
|
|
342
343
|
var MAX_NOTICES_PER_SESSION = 100;
|
|
343
344
|
async function recordNotice(input) {
|
|
344
345
|
try {
|
|
@@ -360,7 +361,12 @@ async function recordNotice(input) {
|
|
|
360
361
|
}
|
|
361
362
|
async function drainNotices(mind, session, limit = MAX_NOTICES_PER_SESSION) {
|
|
362
363
|
const db = await getDb();
|
|
363
|
-
return db.select().from(mindNotices).where(
|
|
364
|
+
return db.select().from(mindNotices).where(
|
|
365
|
+
and(
|
|
366
|
+
eq(mindNotices.mind, mind),
|
|
367
|
+
or(eq(mindNotices.session, session), eq(mindNotices.session, MIND_LEVEL_SESSION))
|
|
368
|
+
)
|
|
369
|
+
).orderBy(asc(mindNotices.id)).limit(limit);
|
|
364
370
|
}
|
|
365
371
|
async function clearDeliveredNotices(mind, session, uptoId) {
|
|
366
372
|
try {
|
|
@@ -368,7 +374,7 @@ async function clearDeliveredNotices(mind, session, uptoId) {
|
|
|
368
374
|
await db.delete(mindNotices).where(
|
|
369
375
|
and(
|
|
370
376
|
eq(mindNotices.mind, mind),
|
|
371
|
-
eq(mindNotices.session, session),
|
|
377
|
+
or(eq(mindNotices.session, session), eq(mindNotices.session, MIND_LEVEL_SESSION)),
|
|
372
378
|
lte(mindNotices.id, uptoId)
|
|
373
379
|
)
|
|
374
380
|
);
|
|
@@ -379,25 +385,51 @@ async function clearDeliveredNotices(mind, session, uptoId) {
|
|
|
379
385
|
var NOTICE_HEADER = "[Notices] While you were unavailable, one or more turns failed. You're back now:";
|
|
380
386
|
function formatNotices(notices) {
|
|
381
387
|
if (notices.length === 0) return null;
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
388
|
+
const failures = notices.filter((n) => n.kind !== "extension" && n.kind !== "budget");
|
|
389
|
+
const budgets = notices.filter((n) => n.kind === "budget");
|
|
390
|
+
const extensions = notices.filter((n) => n.kind === "extension");
|
|
391
|
+
const blocks = [];
|
|
392
|
+
if (failures.length > 0) {
|
|
393
|
+
const groups = /* @__PURE__ */ new Map();
|
|
394
|
+
for (const n of failures) {
|
|
395
|
+
const time = localHM(n.created_at);
|
|
396
|
+
const g = groups.get(n.reason);
|
|
397
|
+
if (g) {
|
|
398
|
+
g.count += 1;
|
|
399
|
+
g.detail = n.detail;
|
|
400
|
+
g.last = time;
|
|
401
|
+
} else {
|
|
402
|
+
groups.set(n.reason, { count: 1, detail: n.detail, first: time, last: time });
|
|
403
|
+
}
|
|
392
404
|
}
|
|
405
|
+
const lines = [...groups.values()].map((g) => {
|
|
406
|
+
const span = g.first === g.last ? g.first : `${g.first}\u2013${g.last}`;
|
|
407
|
+
const plural = g.count === 1 ? "turn" : "turns";
|
|
408
|
+
return `- ${g.count} ${plural} failed (${span}): ${g.detail}`;
|
|
409
|
+
});
|
|
410
|
+
blocks.push(`${NOTICE_HEADER}
|
|
411
|
+
${lines.join("\n")}`);
|
|
393
412
|
}
|
|
394
|
-
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
|
|
413
|
+
if (budgets.length > 0) {
|
|
414
|
+
const lines = budgets.map((n) => `- ${localHM(n.created_at)} ${n.detail}`);
|
|
415
|
+
blocks.push(`[Budget]
|
|
416
|
+
${lines.join("\n")}`);
|
|
417
|
+
}
|
|
418
|
+
if (extensions.length > 0) {
|
|
419
|
+
const byExt = /* @__PURE__ */ new Map();
|
|
420
|
+
for (const n of extensions) {
|
|
421
|
+
const arr = byExt.get(n.reason);
|
|
422
|
+
if (arr) arr.push(n);
|
|
423
|
+
else byExt.set(n.reason, [n]);
|
|
424
|
+
}
|
|
425
|
+
for (const [ext, items] of byExt) {
|
|
426
|
+
const header = `[${ext.charAt(0).toUpperCase()}${ext.slice(1)}]`;
|
|
427
|
+
const lines = items.map((n) => `- ${localHM(n.created_at)} ${n.detail}`);
|
|
428
|
+
blocks.push(`${header}
|
|
429
|
+
${lines.join("\n")}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return blocks.join("\n\n");
|
|
401
433
|
}
|
|
402
434
|
function localHM(createdAt) {
|
|
403
435
|
const iso = createdAt.endsWith("Z") ? createdAt : `${createdAt.replace(" ", "T")}Z`;
|
|
@@ -861,6 +893,8 @@ var MindManager = class {
|
|
|
861
893
|
const parts = [];
|
|
862
894
|
if (context.type === "merge" || context.type === "merged") {
|
|
863
895
|
parts.push(await getPrompt("merge_message", { name: String(context.name ?? "") }));
|
|
896
|
+
} else if (context.type === "split") {
|
|
897
|
+
parts.push(await getPrompt("split_message", { name, parent: String(context.parent ?? "") }));
|
|
864
898
|
} else if (context.type === "sprouted") {
|
|
865
899
|
parts.push(await getPrompt("sprout_message"));
|
|
866
900
|
} else if (context.type === "upgraded") {
|
|
@@ -905,7 +939,7 @@ var MindManager = class {
|
|
|
905
939
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
906
940
|
mlog.error(`mind ${name} exited with code ${code}`);
|
|
907
941
|
try {
|
|
908
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
942
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
|
|
909
943
|
const sleepMgr = getSleepManagerIfReady2();
|
|
910
944
|
const sleepState = sleepMgr?.getState(name);
|
|
911
945
|
if (sleepState?.sleeping) {
|
|
@@ -935,15 +969,15 @@ var MindManager = class {
|
|
|
935
969
|
(err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
|
|
936
970
|
);
|
|
937
971
|
try {
|
|
938
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
972
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-R5STGLXB.js");
|
|
939
973
|
getDeliveryManager2().clearMindSessions(name);
|
|
940
974
|
} catch (err) {
|
|
941
975
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
942
976
|
mlog.warn(`failed to clear delivery state for ${name} after crash`, logger_default.errorData(err));
|
|
943
977
|
}
|
|
944
978
|
}
|
|
945
|
-
import("./mind-activity-tracker-
|
|
946
|
-
import("./activity-events-
|
|
979
|
+
import("./mind-activity-tracker-BGHJE7XA.js").then(({ markIdle: markIdle2 }) => markIdle2(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
|
|
980
|
+
import("./activity-events-OMCSM7XW.js").then(
|
|
947
981
|
({ publish: publish4 }) => publish4({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
|
|
948
982
|
).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
|
|
949
983
|
const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
|
|
@@ -1001,7 +1035,7 @@ var MindManager = class {
|
|
|
1001
1035
|
mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
|
|
1002
1036
|
}
|
|
1003
1037
|
try {
|
|
1004
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
1038
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-R5STGLXB.js");
|
|
1005
1039
|
getDeliveryManager2().clearMindSessions(name);
|
|
1006
1040
|
} catch (err) {
|
|
1007
1041
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
@@ -1009,7 +1043,7 @@ var MindManager = class {
|
|
|
1009
1043
|
}
|
|
1010
1044
|
}
|
|
1011
1045
|
try {
|
|
1012
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
1046
|
+
const { clearEchoTextCache } = await import("./echo-text-NQXYYYCU.js");
|
|
1013
1047
|
clearEchoTextCache(name);
|
|
1014
1048
|
} catch (err) {
|
|
1015
1049
|
mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
|
|
@@ -1462,6 +1496,24 @@ async function buildContext(manifest, dataDir, authMw) {
|
|
|
1462
1496
|
},
|
|
1463
1497
|
getSystemsConfig: () => readSystemsConfig(),
|
|
1464
1498
|
announceToSystem: (text) => announceToSystem(text),
|
|
1499
|
+
recordNotice: async (mindName, text) => {
|
|
1500
|
+
try {
|
|
1501
|
+
const user = await getUserByUsername(mindName);
|
|
1502
|
+
if (user?.user_type !== "mind") return;
|
|
1503
|
+
await recordNotice({
|
|
1504
|
+
mind: mindName,
|
|
1505
|
+
session: MIND_LEVEL_SESSION,
|
|
1506
|
+
kind: "extension",
|
|
1507
|
+
reason: manifest.id,
|
|
1508
|
+
detail: text.slice(0, 500)
|
|
1509
|
+
});
|
|
1510
|
+
} catch (err) {
|
|
1511
|
+
logger_default.warn(
|
|
1512
|
+
`extension ${manifest.id}: failed to record notice for ${mindName}`,
|
|
1513
|
+
logger_default.errorData(err)
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
},
|
|
1465
1517
|
isIsolationEnabled,
|
|
1466
1518
|
getMindUser: mindUserName,
|
|
1467
1519
|
dataDir
|
|
@@ -1646,9 +1698,9 @@ function resolveSkillsDir(manifest) {
|
|
|
1646
1698
|
}
|
|
1647
1699
|
async function discoverBuiltinExtensions(disabledIds) {
|
|
1648
1700
|
const builtins = [
|
|
1649
|
-
{ id: "notes", load: async () => (await import("./src-
|
|
1650
|
-
{ id: "pages", load: async () => (await import("./src-
|
|
1651
|
-
{ id: "plan", load: async () => (await import("./src-
|
|
1701
|
+
{ id: "notes", load: async () => (await import("./src-YH6PQ7LJ.js")).default },
|
|
1702
|
+
{ id: "pages", load: async () => (await import("./src-XFY3XDD6.js")).default },
|
|
1703
|
+
{ id: "plan", load: async () => (await import("./src-JAOVXO5W.js")).default }
|
|
1652
1704
|
];
|
|
1653
1705
|
const results = [];
|
|
1654
1706
|
for (const { id, load } of builtins) {
|
|
@@ -1781,6 +1833,15 @@ async function loadAllExtensions(app, authMw) {
|
|
|
1781
1833
|
}
|
|
1782
1834
|
return c.json(result);
|
|
1783
1835
|
});
|
|
1836
|
+
app.get("/api/extensions/mind-docs", (c) => {
|
|
1837
|
+
const result = loaded.filter(({ manifest }) => manifest.mindDoc || manifest.commands).map(({ manifest }) => ({
|
|
1838
|
+
id: manifest.id,
|
|
1839
|
+
name: manifest.name,
|
|
1840
|
+
mindDoc: manifest.mindDoc ?? manifest.description ?? "",
|
|
1841
|
+
commands: Object.keys(manifest.commands ?? {})
|
|
1842
|
+
}));
|
|
1843
|
+
return c.json(result);
|
|
1844
|
+
});
|
|
1784
1845
|
}
|
|
1785
1846
|
function getLoadedExtensions() {
|
|
1786
1847
|
return loaded.map(({ manifest }) => {
|
|
@@ -1888,7 +1949,7 @@ async function installNpmExtension(pkg) {
|
|
|
1888
1949
|
throw new Error(`Extension "${pkg}" is already installed`);
|
|
1889
1950
|
}
|
|
1890
1951
|
const dir = ensureExtensionsNpmDir();
|
|
1891
|
-
const { exec: exec2 } = await import("./exec-
|
|
1952
|
+
const { exec: exec2 } = await import("./exec-4GWHAHS3.js");
|
|
1892
1953
|
try {
|
|
1893
1954
|
await exec2("npm", ["install", "--ignore-scripts", pkg], { cwd: dir });
|
|
1894
1955
|
} catch (err) {
|
|
@@ -1909,7 +1970,7 @@ async function uninstallNpmExtension(pkg) {
|
|
|
1909
1970
|
packages.splice(idx, 1);
|
|
1910
1971
|
writeExtensionsConfig(packages);
|
|
1911
1972
|
try {
|
|
1912
|
-
const { exec: exec2 } = await import("./exec-
|
|
1973
|
+
const { exec: exec2 } = await import("./exec-4GWHAHS3.js");
|
|
1913
1974
|
await exec2("npm", ["uninstall", "--ignore-scripts", pkg], { cwd: extensionsNpmDir() });
|
|
1914
1975
|
} catch (err) {
|
|
1915
1976
|
logger_default.warn(
|
|
@@ -2466,24 +2527,24 @@ var TokenBudget = class {
|
|
|
2466
2527
|
this.budgets.delete(mind);
|
|
2467
2528
|
}
|
|
2468
2529
|
recordUsage(mind, inputTokens, outputTokens) {
|
|
2469
|
-
const
|
|
2470
|
-
if (!
|
|
2471
|
-
|
|
2530
|
+
const state2 = this.budgets.get(mind);
|
|
2531
|
+
if (!state2) return;
|
|
2532
|
+
state2.tokensUsed += inputTokens + outputTokens;
|
|
2472
2533
|
this.dirty.add(mind);
|
|
2473
2534
|
}
|
|
2474
2535
|
/** Returns current budget status. Does not mutate state — call acknowledgeWarning() after delivering a warning. */
|
|
2475
2536
|
checkBudget(mind) {
|
|
2476
|
-
const
|
|
2477
|
-
if (!
|
|
2478
|
-
const pct =
|
|
2537
|
+
const state2 = this.budgets.get(mind);
|
|
2538
|
+
if (!state2) return "ok";
|
|
2539
|
+
const pct = state2.tokensUsed / state2.tokenLimit;
|
|
2479
2540
|
if (pct >= 1) return "exceeded";
|
|
2480
|
-
if (pct >= 0.8 && !
|
|
2541
|
+
if (pct >= 0.8 && !state2.warningInjected) return "warning";
|
|
2481
2542
|
return "ok";
|
|
2482
2543
|
}
|
|
2483
2544
|
/** Mark warning as delivered for this period. Call after successfully injecting the warning. */
|
|
2484
2545
|
acknowledgeWarning(mind) {
|
|
2485
|
-
const
|
|
2486
|
-
if (
|
|
2546
|
+
const state2 = this.budgets.get(mind);
|
|
2547
|
+
if (state2) state2.warningInjected = true;
|
|
2487
2548
|
}
|
|
2488
2549
|
/**
|
|
2489
2550
|
* Returns true exactly once per budget period when the mind first crosses its limit,
|
|
@@ -2491,50 +2552,50 @@ var TokenBudget = class {
|
|
|
2491
2552
|
* every subsequent usage event. The flag resets when the period rolls over.
|
|
2492
2553
|
*/
|
|
2493
2554
|
noteExceeded(mind) {
|
|
2494
|
-
const
|
|
2495
|
-
if (!
|
|
2496
|
-
if (
|
|
2497
|
-
if (
|
|
2498
|
-
|
|
2555
|
+
const state2 = this.budgets.get(mind);
|
|
2556
|
+
if (!state2) return false;
|
|
2557
|
+
if (state2.tokensUsed < state2.tokenLimit) return false;
|
|
2558
|
+
if (state2.exceededNotified) return false;
|
|
2559
|
+
state2.exceededNotified = true;
|
|
2499
2560
|
this.dirty.add(mind);
|
|
2500
2561
|
return true;
|
|
2501
2562
|
}
|
|
2502
2563
|
enqueue(mind, message) {
|
|
2503
|
-
const
|
|
2504
|
-
if (!
|
|
2505
|
-
if (
|
|
2506
|
-
|
|
2564
|
+
const state2 = this.budgets.get(mind);
|
|
2565
|
+
if (!state2) return;
|
|
2566
|
+
if (state2.queue.length >= MAX_QUEUE_SIZE) {
|
|
2567
|
+
state2.queue.shift();
|
|
2507
2568
|
}
|
|
2508
|
-
|
|
2569
|
+
state2.queue.push(message);
|
|
2509
2570
|
}
|
|
2510
2571
|
drain(mind) {
|
|
2511
|
-
const
|
|
2512
|
-
if (!
|
|
2513
|
-
const messages2 =
|
|
2514
|
-
|
|
2572
|
+
const state2 = this.budgets.get(mind);
|
|
2573
|
+
if (!state2) return [];
|
|
2574
|
+
const messages2 = state2.queue;
|
|
2575
|
+
state2.queue = [];
|
|
2515
2576
|
return messages2;
|
|
2516
2577
|
}
|
|
2517
2578
|
getUsage(mind) {
|
|
2518
|
-
const
|
|
2519
|
-
if (!
|
|
2579
|
+
const state2 = this.budgets.get(mind);
|
|
2580
|
+
if (!state2) return null;
|
|
2520
2581
|
return {
|
|
2521
|
-
tokensUsed:
|
|
2522
|
-
tokenLimit:
|
|
2523
|
-
periodMinutes:
|
|
2524
|
-
periodStart:
|
|
2525
|
-
queueLength:
|
|
2526
|
-
percentUsed: Math.round(
|
|
2582
|
+
tokensUsed: state2.tokensUsed,
|
|
2583
|
+
tokenLimit: state2.tokenLimit,
|
|
2584
|
+
periodMinutes: state2.periodMinutes,
|
|
2585
|
+
periodStart: state2.periodStart,
|
|
2586
|
+
queueLength: state2.queue.length,
|
|
2587
|
+
percentUsed: Math.round(state2.tokensUsed / state2.tokenLimit * 100)
|
|
2527
2588
|
};
|
|
2528
2589
|
}
|
|
2529
2590
|
async tick() {
|
|
2530
2591
|
const now = Date.now();
|
|
2531
|
-
for (const [mind,
|
|
2532
|
-
const elapsed = now -
|
|
2533
|
-
if (elapsed >=
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2592
|
+
for (const [mind, state2] of this.budgets) {
|
|
2593
|
+
const elapsed = now - state2.periodStart;
|
|
2594
|
+
if (elapsed >= state2.periodMinutes * 6e4) {
|
|
2595
|
+
state2.tokensUsed = 0;
|
|
2596
|
+
state2.periodStart = now;
|
|
2597
|
+
state2.warningInjected = false;
|
|
2598
|
+
state2.exceededNotified = false;
|
|
2538
2599
|
this.dirty.add(mind);
|
|
2539
2600
|
const queued = this.drain(mind);
|
|
2540
2601
|
if (queued.length > 0) {
|
|
@@ -2552,24 +2613,24 @@ var TokenBudget = class {
|
|
|
2552
2613
|
this.dirty.clear();
|
|
2553
2614
|
const writes = [];
|
|
2554
2615
|
for (const mind of flushing) {
|
|
2555
|
-
const
|
|
2556
|
-
if (
|
|
2616
|
+
const state2 = this.budgets.get(mind);
|
|
2617
|
+
if (state2) writes.push(this.saveBudgetState(mind, state2).catch(() => this.dirty.add(mind)));
|
|
2557
2618
|
}
|
|
2558
2619
|
await Promise.all(writes);
|
|
2559
2620
|
}
|
|
2560
2621
|
budgetStatePath(mind) {
|
|
2561
2622
|
return resolve6(stateDir(mind), "budget.json");
|
|
2562
2623
|
}
|
|
2563
|
-
async saveBudgetState(mind,
|
|
2624
|
+
async saveBudgetState(mind, state2) {
|
|
2564
2625
|
try {
|
|
2565
2626
|
const dir = stateDir(mind);
|
|
2566
2627
|
await mkdir(dir, { recursive: true });
|
|
2567
2628
|
const data = {
|
|
2568
|
-
periodStart:
|
|
2569
|
-
tokensUsed:
|
|
2570
|
-
warningInjected:
|
|
2571
|
-
exceededNotified:
|
|
2572
|
-
queue:
|
|
2629
|
+
periodStart: state2.periodStart,
|
|
2630
|
+
tokensUsed: state2.tokensUsed,
|
|
2631
|
+
warningInjected: state2.warningInjected,
|
|
2632
|
+
exceededNotified: state2.exceededNotified,
|
|
2633
|
+
queue: state2.queue
|
|
2573
2634
|
};
|
|
2574
2635
|
await writeFile2(this.budgetStatePath(mind), `${JSON.stringify(data)}
|
|
2575
2636
|
`);
|
|
@@ -2615,8 +2676,8 @@ ${summary}`
|
|
|
2615
2676
|
tlog.info(`replayed ${messages2.length} queued message(s) for ${mindName}`);
|
|
2616
2677
|
} catch (err) {
|
|
2617
2678
|
tlog.warn(`failed to replay for ${mindName}`, logger_default.errorData(err));
|
|
2618
|
-
const
|
|
2619
|
-
if (
|
|
2679
|
+
const state2 = this.budgets.get(mindName);
|
|
2680
|
+
if (state2) state2.queue.push(...messages2);
|
|
2620
2681
|
}
|
|
2621
2682
|
}
|
|
2622
2683
|
};
|
|
@@ -2710,7 +2771,7 @@ async function wakeMind(name) {
|
|
|
2710
2771
|
async function startSpiritFull(name) {
|
|
2711
2772
|
const entry = await findMind(name);
|
|
2712
2773
|
if (entry?.dir) {
|
|
2713
|
-
const { registerMindDir } = await import("./delivery-router-
|
|
2774
|
+
const { registerMindDir } = await import("./delivery-router-6DE3BPHO.js");
|
|
2714
2775
|
registerMindDir(name, entry.dir);
|
|
2715
2776
|
}
|
|
2716
2777
|
await getMindManager().startMind(name);
|
|
@@ -2732,8 +2793,8 @@ async function stopSpiritFull(name) {
|
|
|
2732
2793
|
}).catch((err) => logger_default.error("failed to publish spirit_stopped activity", logger_default.errorData(err)));
|
|
2733
2794
|
}
|
|
2734
2795
|
async function ensureCreatorDM(mindName, creatorUsername) {
|
|
2735
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-
|
|
2736
|
-
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-
|
|
2796
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-FRZJVQCS.js");
|
|
2797
|
+
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-G4LYMHAA.js");
|
|
2737
2798
|
const mindUser = await getOrCreateMindUser2(mindName);
|
|
2738
2799
|
const creatorUser = await getUserByUsername2(creatorUsername);
|
|
2739
2800
|
if (!creatorUser) {
|
|
@@ -2785,6 +2846,22 @@ function formatCurrentDate() {
|
|
|
2785
2846
|
day: "numeric"
|
|
2786
2847
|
});
|
|
2787
2848
|
}
|
|
2849
|
+
function formatWakeTime(wakeAt) {
|
|
2850
|
+
if (wakeAt) {
|
|
2851
|
+
const date = new Date(wakeAt);
|
|
2852
|
+
if (!Number.isNaN(date.getTime())) {
|
|
2853
|
+
const time = date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
|
|
2854
|
+
const day = date.toLocaleDateString("en-US", {
|
|
2855
|
+
weekday: "long",
|
|
2856
|
+
month: "long",
|
|
2857
|
+
day: "numeric"
|
|
2858
|
+
});
|
|
2859
|
+
return `at ${time} on ${day}`;
|
|
2860
|
+
}
|
|
2861
|
+
return `at ${wakeAt}`;
|
|
2862
|
+
}
|
|
2863
|
+
return "when something needs you \u2014 a message, a mention, or someone waking you";
|
|
2864
|
+
}
|
|
2788
2865
|
function formatDuration(from, to) {
|
|
2789
2866
|
const ms = to.getTime() - from.getTime();
|
|
2790
2867
|
const hours = Math.floor(ms / 36e5);
|
|
@@ -2826,9 +2903,9 @@ var SleepManager = class {
|
|
|
2826
2903
|
try {
|
|
2827
2904
|
if (existsSync8(this.statePath)) {
|
|
2828
2905
|
const data = JSON.parse(readFileSync7(this.statePath, "utf-8"));
|
|
2829
|
-
for (const [name,
|
|
2830
|
-
|
|
2831
|
-
this.states.set(name,
|
|
2906
|
+
for (const [name, state2] of Object.entries(data)) {
|
|
2907
|
+
state2.triggerWakeHistory ??= [];
|
|
2908
|
+
this.states.set(name, state2);
|
|
2832
2909
|
}
|
|
2833
2910
|
}
|
|
2834
2911
|
} catch (err) {
|
|
@@ -2837,8 +2914,8 @@ var SleepManager = class {
|
|
|
2837
2914
|
}
|
|
2838
2915
|
saveState() {
|
|
2839
2916
|
const data = {};
|
|
2840
|
-
for (const [name,
|
|
2841
|
-
if (
|
|
2917
|
+
for (const [name, state2] of this.states) {
|
|
2918
|
+
if (state2.sleeping) data[name] = state2;
|
|
2842
2919
|
}
|
|
2843
2920
|
try {
|
|
2844
2921
|
writeFileSync6(this.statePath, `${JSON.stringify(data, null, 2)}
|
|
@@ -2849,9 +2926,9 @@ var SleepManager = class {
|
|
|
2849
2926
|
}
|
|
2850
2927
|
// --- Public API ---
|
|
2851
2928
|
isSleeping(name) {
|
|
2852
|
-
const
|
|
2853
|
-
if (!
|
|
2854
|
-
if (
|
|
2929
|
+
const state2 = this.states.get(name);
|
|
2930
|
+
if (!state2?.sleeping) return false;
|
|
2931
|
+
if (state2.wokenByTrigger) return false;
|
|
2855
2932
|
return true;
|
|
2856
2933
|
}
|
|
2857
2934
|
getState(name) {
|
|
@@ -2862,8 +2939,8 @@ var SleepManager = class {
|
|
|
2862
2939
|
* this just clears the sleep state so onActivityEvent won't return it to sleep.
|
|
2863
2940
|
*/
|
|
2864
2941
|
convertTriggerToFullWake(name) {
|
|
2865
|
-
const
|
|
2866
|
-
if (!
|
|
2942
|
+
const state2 = this.states.get(name);
|
|
2943
|
+
if (!state2?.sleeping || !state2.wokenByTrigger) return;
|
|
2867
2944
|
this.markAwake(name);
|
|
2868
2945
|
slog3.info(`${name} trigger-wake converted to full wake`);
|
|
2869
2946
|
}
|
|
@@ -2875,11 +2952,11 @@ var SleepManager = class {
|
|
|
2875
2952
|
* return-to-sleep in onActivityEvent, minus stopping an already-dead process.
|
|
2876
2953
|
*/
|
|
2877
2954
|
async returnToSleepAfterCrash(name) {
|
|
2878
|
-
const
|
|
2879
|
-
if (!
|
|
2955
|
+
const state2 = this.states.get(name);
|
|
2956
|
+
if (!state2?.sleeping || !state2.wokenByTrigger) return;
|
|
2880
2957
|
if (this.transitioning.has(name)) return;
|
|
2881
2958
|
this.transitioning.add(name);
|
|
2882
|
-
|
|
2959
|
+
state2.wokenByTrigger = false;
|
|
2883
2960
|
try {
|
|
2884
2961
|
try {
|
|
2885
2962
|
await sleepMind(name);
|
|
@@ -2887,9 +2964,9 @@ var SleepManager = class {
|
|
|
2887
2964
|
slog3.warn(`sleepMind during crash-return failed for ${name}`, logger_default.errorData(err));
|
|
2888
2965
|
}
|
|
2889
2966
|
await this.archiveSessions(name);
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2967
|
+
state2.sleeping = true;
|
|
2968
|
+
state2.sleepingSince = (/* @__PURE__ */ new Date()).toISOString();
|
|
2969
|
+
state2.scheduledWakeAt = this.getNextWakeTime(this.getSleepConfig(name));
|
|
2893
2970
|
this.saveState();
|
|
2894
2971
|
slog3.info(`${name} returned to sleep after crashing during trigger wake`);
|
|
2895
2972
|
} catch (err) {
|
|
@@ -2932,7 +3009,8 @@ var SleepManager = class {
|
|
|
2932
3009
|
const entry = await findMind(name);
|
|
2933
3010
|
if (!entry) return;
|
|
2934
3011
|
const sleepConfig = this.getSleepConfig(name);
|
|
2935
|
-
const
|
|
3012
|
+
const wakeAt = opts?.voluntaryWakeAt ?? this.getNextWakeTime(sleepConfig);
|
|
3013
|
+
const wakeTime = formatWakeTime(wakeAt);
|
|
2936
3014
|
const preSleepMsg = await getPrompt("pre_sleep", { wakeTime });
|
|
2937
3015
|
let conversationId;
|
|
2938
3016
|
try {
|
|
@@ -2973,8 +3051,8 @@ var SleepManager = class {
|
|
|
2973
3051
|
* Wake a sleeping mind. Starts the process, delivers wake summary.
|
|
2974
3052
|
*/
|
|
2975
3053
|
async initiateWake(name, opts) {
|
|
2976
|
-
const
|
|
2977
|
-
if (!
|
|
3054
|
+
const state2 = this.states.get(name);
|
|
3055
|
+
if (!state2?.sleeping) return;
|
|
2978
3056
|
if (this.transitioning.has(name)) return;
|
|
2979
3057
|
this.transitioning.add(name);
|
|
2980
3058
|
try {
|
|
@@ -2990,14 +3068,14 @@ var SleepManager = class {
|
|
|
2990
3068
|
const entry = await findMind(name);
|
|
2991
3069
|
if (!entry) return;
|
|
2992
3070
|
if (opts?.trigger) {
|
|
2993
|
-
|
|
2994
|
-
|
|
3071
|
+
state2.wokenByTrigger = true;
|
|
3072
|
+
state2.triggerWakeHistory.push({
|
|
2995
3073
|
channel: opts.trigger.channel,
|
|
2996
3074
|
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2997
3075
|
});
|
|
2998
3076
|
this.saveState();
|
|
2999
3077
|
} else {
|
|
3000
|
-
const sleepingSince =
|
|
3078
|
+
const sleepingSince = state2.sleepingSince ? new Date(state2.sleepingSince) : /* @__PURE__ */ new Date();
|
|
3001
3079
|
const now = /* @__PURE__ */ new Date();
|
|
3002
3080
|
const duration = formatDuration(sleepingSince, now);
|
|
3003
3081
|
const currentDate = formatCurrentDate();
|
|
@@ -3005,10 +3083,10 @@ var SleepManager = class {
|
|
|
3005
3083
|
hour: "numeric",
|
|
3006
3084
|
minute: "2-digit"
|
|
3007
3085
|
});
|
|
3008
|
-
const triggerWakeSummary = this.buildTriggerWakeSummary(
|
|
3086
|
+
const triggerWakeSummary = this.buildTriggerWakeSummary(state2);
|
|
3009
3087
|
const wakeContext = await this.runWakeContextScript(
|
|
3010
3088
|
name,
|
|
3011
|
-
|
|
3089
|
+
state2.sleepingSince ?? sleepingSince.toISOString(),
|
|
3012
3090
|
duration
|
|
3013
3091
|
);
|
|
3014
3092
|
const queuedSummary = await this.buildQueuedSummary(name);
|
|
@@ -3094,9 +3172,9 @@ var SleepManager = class {
|
|
|
3094
3172
|
status: "sleep-queued",
|
|
3095
3173
|
payload: JSON.stringify(payload)
|
|
3096
3174
|
});
|
|
3097
|
-
const
|
|
3098
|
-
if (
|
|
3099
|
-
|
|
3175
|
+
const state2 = this.states.get(name);
|
|
3176
|
+
if (state2) {
|
|
3177
|
+
state2.queuedMessageCount++;
|
|
3100
3178
|
this.saveState();
|
|
3101
3179
|
}
|
|
3102
3180
|
}
|
|
@@ -3108,7 +3186,7 @@ var SleepManager = class {
|
|
|
3108
3186
|
const db = await getDb();
|
|
3109
3187
|
const rows = await db.select().from(deliveryQueue).where(and2(eq3(deliveryQueue.mind, name), eq3(deliveryQueue.status, "sleep-queued"))).all();
|
|
3110
3188
|
if (rows.length === 0) return 0;
|
|
3111
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
3189
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-IUAXRPFE.js");
|
|
3112
3190
|
const delivered = [];
|
|
3113
3191
|
for (const row of rows) {
|
|
3114
3192
|
try {
|
|
@@ -3121,9 +3199,9 @@ var SleepManager = class {
|
|
|
3121
3199
|
if (delivered.length > 0) {
|
|
3122
3200
|
await db.delete(deliveryQueue).where(inArray(deliveryQueue.id, delivered));
|
|
3123
3201
|
}
|
|
3124
|
-
const
|
|
3125
|
-
if (
|
|
3126
|
-
|
|
3202
|
+
const state2 = this.states.get(name);
|
|
3203
|
+
if (state2) {
|
|
3204
|
+
state2.queuedMessageCount = Math.max(0, state2.queuedMessageCount - delivered.length);
|
|
3127
3205
|
}
|
|
3128
3206
|
return delivered.length;
|
|
3129
3207
|
} catch (err) {
|
|
@@ -3134,7 +3212,7 @@ var SleepManager = class {
|
|
|
3134
3212
|
// --- Internal methods ---
|
|
3135
3213
|
markSleeping(name, opts) {
|
|
3136
3214
|
const sleepConfig = this.getSleepConfig(name);
|
|
3137
|
-
const
|
|
3215
|
+
const state2 = {
|
|
3138
3216
|
sleeping: true,
|
|
3139
3217
|
sleepingSince: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3140
3218
|
scheduledWakeAt: this.getNextWakeTime(sleepConfig),
|
|
@@ -3143,7 +3221,7 @@ var SleepManager = class {
|
|
|
3143
3221
|
queuedMessageCount: this.states.get(name)?.queuedMessageCount ?? 0,
|
|
3144
3222
|
triggerWakeHistory: []
|
|
3145
3223
|
};
|
|
3146
|
-
this.states.set(name,
|
|
3224
|
+
this.states.set(name, state2);
|
|
3147
3225
|
this.saveState();
|
|
3148
3226
|
}
|
|
3149
3227
|
markAwake(name) {
|
|
@@ -3171,9 +3249,9 @@ var SleepManager = class {
|
|
|
3171
3249
|
}
|
|
3172
3250
|
const config = this.getSleepConfig(name);
|
|
3173
3251
|
if (!config?.enabled || !config.schedule) continue;
|
|
3174
|
-
const
|
|
3175
|
-
if (
|
|
3176
|
-
const wakeAt = new Date(
|
|
3252
|
+
const state2 = this.states.get(name);
|
|
3253
|
+
if (state2?.sleeping && state2.voluntaryWakeAt) {
|
|
3254
|
+
const wakeAt = new Date(state2.voluntaryWakeAt);
|
|
3177
3255
|
if (now >= wakeAt) {
|
|
3178
3256
|
this.initiateWake(name).catch(
|
|
3179
3257
|
(err) => slog3.error(`failed voluntary wake for ${name}`, logger_default.errorData(err))
|
|
@@ -3181,8 +3259,8 @@ var SleepManager = class {
|
|
|
3181
3259
|
continue;
|
|
3182
3260
|
}
|
|
3183
3261
|
}
|
|
3184
|
-
if (
|
|
3185
|
-
const wakeAt = new Date(
|
|
3262
|
+
if (state2?.sleeping && state2.scheduledWakeAt) {
|
|
3263
|
+
const wakeAt = new Date(state2.scheduledWakeAt);
|
|
3186
3264
|
if (now >= wakeAt) {
|
|
3187
3265
|
this.initiateWake(name).catch(
|
|
3188
3266
|
(err) => slog3.error(`failed scheduled wake for ${name}`, logger_default.errorData(err))
|
|
@@ -3190,7 +3268,7 @@ var SleepManager = class {
|
|
|
3190
3268
|
continue;
|
|
3191
3269
|
}
|
|
3192
3270
|
}
|
|
3193
|
-
if (!
|
|
3271
|
+
if (!state2?.sleeping) {
|
|
3194
3272
|
if (this.shouldSleep(config.schedule.sleep, epochMinute)) {
|
|
3195
3273
|
this.initiateSleep(name).catch(
|
|
3196
3274
|
(err) => slog3.error(`failed to initiate sleep for ${name}`, logger_default.errorData(err))
|
|
@@ -3303,8 +3381,8 @@ var SleepManager = class {
|
|
|
3303
3381
|
return "";
|
|
3304
3382
|
}
|
|
3305
3383
|
}
|
|
3306
|
-
buildTriggerWakeSummary(
|
|
3307
|
-
const history =
|
|
3384
|
+
buildTriggerWakeSummary(state2) {
|
|
3385
|
+
const history = state2.triggerWakeHistory;
|
|
3308
3386
|
if (!history || history.length === 0) return "";
|
|
3309
3387
|
const channels = [...new Set(history.map((h) => h.channel))];
|
|
3310
3388
|
const times = history.length === 1 ? "once" : `${history.length} times`;
|
|
@@ -3390,18 +3468,18 @@ var SleepManager = class {
|
|
|
3390
3468
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
3391
3469
|
}
|
|
3392
3470
|
onActivityEvent(event) {
|
|
3393
|
-
const
|
|
3394
|
-
if (!
|
|
3471
|
+
const state2 = this.states.get(event.mind);
|
|
3472
|
+
if (!state2?.sleeping || !state2.wokenByTrigger) return;
|
|
3395
3473
|
if (this.transitioning.has(event.mind)) return;
|
|
3396
3474
|
if (event.type === "mind_idle") {
|
|
3397
3475
|
slog3.info(`${event.mind} going back to sleep after trigger wake`);
|
|
3398
|
-
|
|
3476
|
+
state2.wokenByTrigger = false;
|
|
3399
3477
|
this.transitioning.add(event.mind);
|
|
3400
3478
|
sleepMind(event.mind).then(() => this.archiveSessions(event.mind)).then(() => {
|
|
3401
|
-
|
|
3402
|
-
|
|
3479
|
+
state2.sleeping = true;
|
|
3480
|
+
state2.sleepingSince = (/* @__PURE__ */ new Date()).toISOString();
|
|
3403
3481
|
const sleepConfig = this.getSleepConfig(event.mind);
|
|
3404
|
-
|
|
3482
|
+
state2.scheduledWakeAt = this.getNextWakeTime(sleepConfig);
|
|
3405
3483
|
this.saveState();
|
|
3406
3484
|
slog3.info(`${event.mind} returned to sleep`);
|
|
3407
3485
|
}).catch((err) => {
|
|
@@ -3429,7 +3507,7 @@ function getSleepManagerIfReady() {
|
|
|
3429
3507
|
// packages/daemon/src/lib/delivery/delivery-manager.ts
|
|
3430
3508
|
import { readFile, realpath } from "fs/promises";
|
|
3431
3509
|
import { extname, resolve as resolve8 } from "path";
|
|
3432
|
-
import { and as
|
|
3510
|
+
import { and as and4, eq as eq5, inArray as inArray2, sql as sql2 } from "drizzle-orm";
|
|
3433
3511
|
|
|
3434
3512
|
// packages/daemon/src/lib/chat/typing.ts
|
|
3435
3513
|
var DEFAULT_TTL_MS = 1e4;
|
|
@@ -3522,6 +3600,73 @@ function publishTypingForChannels(channels, map) {
|
|
|
3522
3600
|
}
|
|
3523
3601
|
}
|
|
3524
3602
|
|
|
3603
|
+
// packages/daemon/src/lib/delivery/send-gate.ts
|
|
3604
|
+
import { and as and3, asc as asc2, desc, eq as eq4, gt } from "drizzle-orm";
|
|
3605
|
+
var state = /* @__PURE__ */ new Map();
|
|
3606
|
+
function extractText(raw) {
|
|
3607
|
+
if (!raw) return "";
|
|
3608
|
+
try {
|
|
3609
|
+
const blocks = JSON.parse(raw);
|
|
3610
|
+
if (Array.isArray(blocks)) {
|
|
3611
|
+
return blocks.filter((b) => b?.type === "text" && typeof b.text === "string").map((b) => b.text).join(" ").trim();
|
|
3612
|
+
}
|
|
3613
|
+
} catch {
|
|
3614
|
+
}
|
|
3615
|
+
return String(raw);
|
|
3616
|
+
}
|
|
3617
|
+
async function maxMessageId(conversationId) {
|
|
3618
|
+
const db = await getDb();
|
|
3619
|
+
const rows = await db.select({ id: messages.id }).from(messages).where(eq4(messages.conversation_id, conversationId)).orderBy(desc(messages.id)).limit(1);
|
|
3620
|
+
return rows[0]?.id ?? 0;
|
|
3621
|
+
}
|
|
3622
|
+
async function onDeliveredToMind(mindBase, conversationId) {
|
|
3623
|
+
if (!conversationId) return;
|
|
3624
|
+
let conv = state.get(mindBase);
|
|
3625
|
+
if (!conv) {
|
|
3626
|
+
conv = /* @__PURE__ */ new Map();
|
|
3627
|
+
state.set(mindBase, conv);
|
|
3628
|
+
}
|
|
3629
|
+
const existing = conv.get(conversationId);
|
|
3630
|
+
if (existing?.turnOpen) return;
|
|
3631
|
+
const baselineId = await maxMessageId(conversationId);
|
|
3632
|
+
conv.set(conversationId, { baselineId, heldThisTurn: false, turnOpen: true });
|
|
3633
|
+
}
|
|
3634
|
+
async function checkStaleSend(mindBase, conversationId, excludeSenders) {
|
|
3635
|
+
const entry = state.get(mindBase)?.get(conversationId);
|
|
3636
|
+
if (!entry?.turnOpen || entry.heldThisTurn) return { held: false };
|
|
3637
|
+
const db = await getDb();
|
|
3638
|
+
const rows = await db.select({ id: messages.id, sender: messages.sender_name, content: messages.content }).from(messages).where(and3(eq4(messages.conversation_id, conversationId), gt(messages.id, entry.baselineId))).orderBy(asc2(messages.id));
|
|
3639
|
+
const excluded = new Set(excludeSenders.filter(Boolean));
|
|
3640
|
+
const unseen = rows.filter((r) => r.sender && !excluded.has(r.sender));
|
|
3641
|
+
if (unseen.length === 0) return { held: false };
|
|
3642
|
+
entry.heldThisTurn = true;
|
|
3643
|
+
entry.baselineId = rows[rows.length - 1].id;
|
|
3644
|
+
return {
|
|
3645
|
+
held: true,
|
|
3646
|
+
unseen: unseen.map((r) => ({ sender: r.sender, text: extractText(r.content) }))
|
|
3647
|
+
};
|
|
3648
|
+
}
|
|
3649
|
+
function resetTurn(mindBase) {
|
|
3650
|
+
const conv = state.get(mindBase);
|
|
3651
|
+
if (!conv) return;
|
|
3652
|
+
for (const entry of conv.values()) {
|
|
3653
|
+
entry.turnOpen = false;
|
|
3654
|
+
entry.heldThisTurn = false;
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
function formatHoldNotice(channelLabel, unseen) {
|
|
3658
|
+
const lines = unseen.map((m) => {
|
|
3659
|
+
const t = m.text.length > 120 ? `${m.text.slice(0, 119)}\u2026` : m.text;
|
|
3660
|
+
return ` ${m.sender}: ${JSON.stringify(t)}`;
|
|
3661
|
+
});
|
|
3662
|
+
const n = unseen.length;
|
|
3663
|
+
return [
|
|
3664
|
+
`\u26A0 Not sent \u2014 ${n} newer message${n === 1 ? "" : "s"} arrived on ${channelLabel} while you were composing:`,
|
|
3665
|
+
...lines,
|
|
3666
|
+
"Your message was NOT posted. Revise it in light of these, or run the same send again to post as-is."
|
|
3667
|
+
].join("\n");
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3525
3670
|
// packages/daemon/src/lib/delivery/delivery-manager.ts
|
|
3526
3671
|
var dlog = logger_default.child("delivery-manager");
|
|
3527
3672
|
var MAX_BATCH_SIZE = 50;
|
|
@@ -3638,6 +3783,7 @@ var DeliveryManager = class {
|
|
|
3638
3783
|
* causing isSessionBusy to return true even when no deliveries are pending.
|
|
3639
3784
|
*/
|
|
3640
3785
|
sessionDone(baseName, session) {
|
|
3786
|
+
resetTurn(baseName);
|
|
3641
3787
|
if (session) {
|
|
3642
3788
|
this.decrementActive(baseName, session);
|
|
3643
3789
|
} else {
|
|
@@ -3675,8 +3821,8 @@ var DeliveryManager = class {
|
|
|
3675
3821
|
try {
|
|
3676
3822
|
const db = await getDb();
|
|
3677
3823
|
rows = await db.select().from(deliveryQueue).where(
|
|
3678
|
-
|
|
3679
|
-
|
|
3824
|
+
and4(
|
|
3825
|
+
eq5(deliveryQueue.status, "pending"),
|
|
3680
3826
|
sql2`(${deliveryQueue.next_attempt_at} IS NULL OR ${deliveryQueue.next_attempt_at} <= datetime('now'))`
|
|
3681
3827
|
)
|
|
3682
3828
|
).orderBy(deliveryQueue.id).limit(REDRIVE_BATCH_LIMIT);
|
|
@@ -3731,7 +3877,7 @@ var DeliveryManager = class {
|
|
|
3731
3877
|
let rows;
|
|
3732
3878
|
try {
|
|
3733
3879
|
const db = await getDb();
|
|
3734
|
-
rows = await db.select().from(deliveryQueue).where(
|
|
3880
|
+
rows = await db.select().from(deliveryQueue).where(and4(eq5(deliveryQueue.mind, baseName), eq5(deliveryQueue.status, "gated")));
|
|
3735
3881
|
} catch (err) {
|
|
3736
3882
|
dlog.warn(`failed to read gated rows for ${baseName}`, logger_default.errorData(err));
|
|
3737
3883
|
return;
|
|
@@ -3771,7 +3917,7 @@ var DeliveryManager = class {
|
|
|
3771
3917
|
*/
|
|
3772
3918
|
async getPending(mindName) {
|
|
3773
3919
|
const db = await getDb();
|
|
3774
|
-
const rows = await db.select().from(deliveryQueue).where(
|
|
3920
|
+
const rows = await db.select().from(deliveryQueue).where(and4(eq5(deliveryQueue.mind, mindName), eq5(deliveryQueue.status, "gated")));
|
|
3775
3921
|
const byChannel = /* @__PURE__ */ new Map();
|
|
3776
3922
|
for (const row of rows) {
|
|
3777
3923
|
const ch = row.channel ?? "unknown";
|
|
@@ -3796,8 +3942,8 @@ var DeliveryManager = class {
|
|
|
3796
3942
|
* Check if a session is currently busy (has active deliveries).
|
|
3797
3943
|
*/
|
|
3798
3944
|
isSessionBusy(mindName, session) {
|
|
3799
|
-
const
|
|
3800
|
-
return (
|
|
3945
|
+
const state2 = this.sessionStates.get(mindName)?.get(session);
|
|
3946
|
+
return (state2?.activeCount ?? 0) > 0;
|
|
3801
3947
|
}
|
|
3802
3948
|
/**
|
|
3803
3949
|
* Check if any session for a mind is currently busy.
|
|
@@ -3805,8 +3951,8 @@ var DeliveryManager = class {
|
|
|
3805
3951
|
isMindBusy(mindName) {
|
|
3806
3952
|
const mindSessions = this.sessionStates.get(mindName);
|
|
3807
3953
|
if (!mindSessions) return false;
|
|
3808
|
-
for (const [,
|
|
3809
|
-
if (
|
|
3954
|
+
for (const [, state2] of mindSessions) {
|
|
3955
|
+
if (state2.activeCount > 0) return true;
|
|
3810
3956
|
}
|
|
3811
3957
|
return false;
|
|
3812
3958
|
}
|
|
@@ -3844,10 +3990,10 @@ var DeliveryManager = class {
|
|
|
3844
3990
|
* count was reset.
|
|
3845
3991
|
*/
|
|
3846
3992
|
clearSessionActive(mindName, session, minIdleMs) {
|
|
3847
|
-
const
|
|
3848
|
-
if (!
|
|
3849
|
-
if (Date.now() -
|
|
3850
|
-
|
|
3993
|
+
const state2 = this.sessionStates.get(mindName)?.get(session);
|
|
3994
|
+
if (!state2) return false;
|
|
3995
|
+
if (Date.now() - state2.lastDeliveredAt < minIdleMs) return false;
|
|
3996
|
+
state2.activeCount = 0;
|
|
3851
3997
|
return true;
|
|
3852
3998
|
}
|
|
3853
3999
|
/**
|
|
@@ -3940,7 +4086,7 @@ var DeliveryManager = class {
|
|
|
3940
4086
|
const backoffSec = Math.round(
|
|
3941
4087
|
Math.min(RETRY_MAX_MS, RETRY_BASE_MS * 2 ** Math.min(attempts, 20)) / 1e3
|
|
3942
4088
|
);
|
|
3943
|
-
await db.update(deliveryQueue).set({ attempts, next_attempt_at: sql2`datetime('now', ${`+${backoffSec} seconds`})` }).where(
|
|
4089
|
+
await db.update(deliveryQueue).set({ attempts, next_attempt_at: sql2`datetime('now', ${`+${backoffSec} seconds`})` }).where(eq5(deliveryQueue.id, row.id));
|
|
3944
4090
|
}
|
|
3945
4091
|
} catch (err) {
|
|
3946
4092
|
dlog.warn("failed to schedule delivery retry", logger_default.errorData(err));
|
|
@@ -3961,6 +4107,7 @@ var DeliveryManager = class {
|
|
|
3961
4107
|
const channels = /* @__PURE__ */ new Set();
|
|
3962
4108
|
if (payload.channel) channels.add(payload.channel);
|
|
3963
4109
|
this.incrementActive(baseName, session, senders, channels);
|
|
4110
|
+
await onDeliveredToMind(baseName, payload.conversationId);
|
|
3964
4111
|
linkInboundToActiveTurn(baseName, session, payload.channel).catch(
|
|
3965
4112
|
(err) => dlog.warn(`failed to link mid-turn inbound for ${baseName}`, logger_default.errorData(err))
|
|
3966
4113
|
);
|
|
@@ -3976,7 +4123,6 @@ var DeliveryManager = class {
|
|
|
3976
4123
|
const body = JSON.stringify({
|
|
3977
4124
|
...enrichedPayload,
|
|
3978
4125
|
session,
|
|
3979
|
-
interrupt: sessionConfig.interrupt,
|
|
3980
4126
|
instructions: sessionConfig.instructions
|
|
3981
4127
|
});
|
|
3982
4128
|
try {
|
|
@@ -3998,7 +4144,7 @@ var DeliveryManager = class {
|
|
|
3998
4144
|
}
|
|
3999
4145
|
});
|
|
4000
4146
|
}
|
|
4001
|
-
async deliverBatchToMind(mindName, session, messages2, sessionConfig
|
|
4147
|
+
async deliverBatchToMind(mindName, session, messages2, sessionConfig) {
|
|
4002
4148
|
const queueIds = messages2.map((m) => m.queueId).filter((id) => typeof id === "number");
|
|
4003
4149
|
await this.runSequential(`${mindName}:${session}`, async () => {
|
|
4004
4150
|
const resolved = await this.resolvePort(mindName);
|
|
@@ -4035,6 +4181,13 @@ var DeliveryManager = class {
|
|
|
4035
4181
|
if (msg.channel) channelSet.add(msg.channel);
|
|
4036
4182
|
}
|
|
4037
4183
|
this.incrementActive(baseName, session, senders, channelSet);
|
|
4184
|
+
const convIds = /* @__PURE__ */ new Set();
|
|
4185
|
+
for (const msg of messages2) {
|
|
4186
|
+
if (msg.payload.conversationId) convIds.add(msg.payload.conversationId);
|
|
4187
|
+
}
|
|
4188
|
+
for (const convId of convIds) {
|
|
4189
|
+
await onDeliveredToMind(baseName, convId);
|
|
4190
|
+
}
|
|
4038
4191
|
for (const ch of channelSet) {
|
|
4039
4192
|
linkInboundToActiveTurn(baseName, session, ch).catch(
|
|
4040
4193
|
(err) => dlog.warn(`failed to link mid-turn inbound for ${baseName}`, logger_default.errorData(err))
|
|
@@ -4054,7 +4207,6 @@ var DeliveryManager = class {
|
|
|
4054
4207
|
const body = JSON.stringify({
|
|
4055
4208
|
session,
|
|
4056
4209
|
batch: { channels },
|
|
4057
|
-
interrupt: interruptOverride ?? sessionConfig.interrupt,
|
|
4058
4210
|
instructions: sessionConfig.instructions
|
|
4059
4211
|
});
|
|
4060
4212
|
try {
|
|
@@ -4078,7 +4230,6 @@ var DeliveryManager = class {
|
|
|
4078
4230
|
}
|
|
4079
4231
|
async enqueueBatch(mindName, session, payload, sessionConfig) {
|
|
4080
4232
|
const delivery = sessionConfig.delivery;
|
|
4081
|
-
const baseName = await getBaseName(mindName);
|
|
4082
4233
|
const queueId = await this.persistToQueue(mindName, session, payload);
|
|
4083
4234
|
if (queueId != null) this.inFlight.add(queueId);
|
|
4084
4235
|
const msg = {
|
|
@@ -4096,12 +4247,6 @@ var DeliveryManager = class {
|
|
|
4096
4247
|
return;
|
|
4097
4248
|
}
|
|
4098
4249
|
}
|
|
4099
|
-
const state = this.sessionStates.get(baseName)?.get(session);
|
|
4100
|
-
if (state && state.activeCount > 0 && payload.sender && !state.lastDeliverySenders.has(payload.sender) && payload.channel && state.lastDeliveryChannels.has(payload.channel) && Date.now() - state.lastDeliveredAt < delivery.maxWait * 1e3 && Date.now() - state.lastInterruptAt > delivery.debounce * 1e3) {
|
|
4101
|
-
state.lastInterruptAt = Date.now();
|
|
4102
|
-
await this.flushBatch(mindName, session, [msg], true);
|
|
4103
|
-
return;
|
|
4104
|
-
}
|
|
4105
4250
|
this.addToBatchBuffer(mindName, session, sessionConfig, msg);
|
|
4106
4251
|
}
|
|
4107
4252
|
addToBatchBuffer(mindName, session, sessionConfig, msg) {
|
|
@@ -4141,7 +4286,7 @@ var DeliveryManager = class {
|
|
|
4141
4286
|
buffer.maxWaitTimer.unref();
|
|
4142
4287
|
}
|
|
4143
4288
|
}
|
|
4144
|
-
async flushBatch(mindName, session, extra
|
|
4289
|
+
async flushBatch(mindName, session, extra) {
|
|
4145
4290
|
const bufferKey = `${mindName}:${session}`;
|
|
4146
4291
|
const buffer = this.batchBuffers.get(bufferKey);
|
|
4147
4292
|
const messages2 = [];
|
|
@@ -4158,14 +4303,10 @@ var DeliveryManager = class {
|
|
|
4158
4303
|
const baseName = await getBaseName(mindName);
|
|
4159
4304
|
const config = getRoutingConfig(baseName);
|
|
4160
4305
|
const sessionConfig = resolveDeliveryMode(config, session);
|
|
4161
|
-
dlog.info(
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
(err) => {
|
|
4166
|
-
dlog.warn(`failed to flush batch for ${mindName}/${session}`, logger_default.errorData(err));
|
|
4167
|
-
}
|
|
4168
|
-
);
|
|
4306
|
+
dlog.info(`flushing batch for ${mindName}/${session}: ${messages2.length} messages`);
|
|
4307
|
+
this.deliverBatchToMind(mindName, session, messages2, sessionConfig).catch((err) => {
|
|
4308
|
+
dlog.warn(`failed to flush batch for ${mindName}/${session}`, logger_default.errorData(err));
|
|
4309
|
+
});
|
|
4169
4310
|
}
|
|
4170
4311
|
async gateMessage(mindName, session, payload) {
|
|
4171
4312
|
const baseName = await getBaseName(mindName);
|
|
@@ -4173,10 +4314,10 @@ var DeliveryManager = class {
|
|
|
4173
4314
|
try {
|
|
4174
4315
|
const db = await getDb();
|
|
4175
4316
|
const count = await db.select({ count: sql2`count(*)` }).from(deliveryQueue).where(
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4317
|
+
and4(
|
|
4318
|
+
eq5(deliveryQueue.mind, baseName),
|
|
4319
|
+
eq5(deliveryQueue.channel, payload.channel),
|
|
4320
|
+
eq5(deliveryQueue.status, "gated")
|
|
4180
4321
|
)
|
|
4181
4322
|
);
|
|
4182
4323
|
if ((count[0]?.count ?? 0) <= 1) {
|
|
@@ -4192,16 +4333,17 @@ var DeliveryManager = class {
|
|
|
4192
4333
|
const channel = payload.channel ?? "unknown";
|
|
4193
4334
|
const notification = [
|
|
4194
4335
|
`[New channel: ${channel}]`,
|
|
4336
|
+
`Someone new is reaching out \u2014 you don't have a route for this channel yet.`,
|
|
4195
4337
|
`Sender: ${payload.sender ?? "unknown"}`,
|
|
4196
4338
|
payload.platform ? `Platform: ${payload.platform}` : null,
|
|
4197
4339
|
payload.participantCount ? `Participants: ${payload.participantCount}` : null,
|
|
4198
4340
|
"",
|
|
4199
4341
|
`Preview: ${preview}`,
|
|
4200
4342
|
"",
|
|
4201
|
-
`To
|
|
4202
|
-
`Messages are
|
|
4343
|
+
`To start hearing this channel, add a routing rule for "${channel}" to .config/routes.json.`,
|
|
4344
|
+
`Messages are held safely until you do \u2014 nothing is lost. If you'd rather not engage, just leave it unrouted.`
|
|
4203
4345
|
].filter((line) => line !== null).join("\n");
|
|
4204
|
-
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-
|
|
4346
|
+
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-E65T2HSW.js");
|
|
4205
4347
|
await sendSystemMessage2(mindName, notification);
|
|
4206
4348
|
}
|
|
4207
4349
|
/**
|
|
@@ -4237,10 +4379,10 @@ var DeliveryManager = class {
|
|
|
4237
4379
|
async enrichWithProfiles(mindName, session, payload) {
|
|
4238
4380
|
if (!payload.conversationId || !payload.channel) return payload;
|
|
4239
4381
|
const mindSessions = this.sessionStates.get(mindName);
|
|
4240
|
-
const
|
|
4241
|
-
if (!
|
|
4382
|
+
const state2 = mindSessions?.get(session);
|
|
4383
|
+
if (!state2) return payload;
|
|
4242
4384
|
const channelKey = payload.channel;
|
|
4243
|
-
if (
|
|
4385
|
+
if (state2.seenChannelProfiles.has(channelKey)) return payload;
|
|
4244
4386
|
try {
|
|
4245
4387
|
const participants = await getParticipants(payload.conversationId);
|
|
4246
4388
|
const profiles = participants.map((p) => ({
|
|
@@ -4250,7 +4392,7 @@ var DeliveryManager = class {
|
|
|
4250
4392
|
description: p.description
|
|
4251
4393
|
}));
|
|
4252
4394
|
const avatarBlocks = await this.loadAvatarBlocks(participants);
|
|
4253
|
-
|
|
4395
|
+
state2.seenChannelProfiles.add(channelKey);
|
|
4254
4396
|
const enriched = { ...payload, participantProfiles: profiles };
|
|
4255
4397
|
if (avatarBlocks.length > 0) {
|
|
4256
4398
|
const existing = Array.isArray(payload.content) ? payload.content : typeof payload.content === "string" ? [{ type: "text", text: payload.content }] : [];
|
|
@@ -4351,27 +4493,26 @@ var DeliveryManager = class {
|
|
|
4351
4493
|
mindSessions = /* @__PURE__ */ new Map();
|
|
4352
4494
|
this.sessionStates.set(mind, mindSessions);
|
|
4353
4495
|
}
|
|
4354
|
-
const
|
|
4496
|
+
const state2 = mindSessions.get(session) ?? {
|
|
4355
4497
|
activeCount: 0,
|
|
4356
4498
|
lastDeliveredAt: 0,
|
|
4357
4499
|
lastDeliverySenders: /* @__PURE__ */ new Set(),
|
|
4358
4500
|
lastDeliveryChannels: /* @__PURE__ */ new Set(),
|
|
4359
|
-
lastInterruptAt: 0,
|
|
4360
4501
|
seenChannelProfiles: /* @__PURE__ */ new Set()
|
|
4361
4502
|
};
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
if (senders)
|
|
4365
|
-
if (channels)
|
|
4366
|
-
mindSessions.set(session,
|
|
4503
|
+
state2.activeCount++;
|
|
4504
|
+
state2.lastDeliveredAt = Date.now();
|
|
4505
|
+
if (senders) state2.lastDeliverySenders = senders;
|
|
4506
|
+
if (channels) state2.lastDeliveryChannels = channels;
|
|
4507
|
+
mindSessions.set(session, state2);
|
|
4367
4508
|
}
|
|
4368
4509
|
decrementActive(mind, session) {
|
|
4369
4510
|
const mindSessions = this.sessionStates.get(mind);
|
|
4370
4511
|
if (!mindSessions) return;
|
|
4371
|
-
const
|
|
4372
|
-
if (!
|
|
4373
|
-
|
|
4374
|
-
if (
|
|
4512
|
+
const state2 = mindSessions.get(session);
|
|
4513
|
+
if (!state2) return;
|
|
4514
|
+
state2.activeCount = Math.max(0, state2.activeCount - 1);
|
|
4515
|
+
if (state2.activeCount === 0) {
|
|
4375
4516
|
const bufferKey = `${mind}:${session}`;
|
|
4376
4517
|
const buffer = this.batchBuffers.get(bufferKey);
|
|
4377
4518
|
if (buffer && buffer.messages.length > 0) {
|
|
@@ -4453,7 +4594,7 @@ async function linkToolResultToTurn(mind, turnId, toolResultContent, toolUseEven
|
|
|
4453
4594
|
content: mindHistory.content,
|
|
4454
4595
|
message_id: mindHistory.message_id,
|
|
4455
4596
|
turn_id: mindHistory.turn_id
|
|
4456
|
-
}).from(mindHistory).where(
|
|
4597
|
+
}).from(mindHistory).where(and5(eq6(mindHistory.id, outboundId), eq6(mindHistory.mind, mind))).limit(1);
|
|
4457
4598
|
const row = rows[0];
|
|
4458
4599
|
if (!row) {
|
|
4459
4600
|
dlog2.warn(`outbound marker references missing record: mind=${mind} id=${outboundId}`);
|
|
@@ -4461,13 +4602,13 @@ async function linkToolResultToTurn(mind, turnId, toolResultContent, toolUseEven
|
|
|
4461
4602
|
}
|
|
4462
4603
|
const alreadyTagged = row.turn_id != null;
|
|
4463
4604
|
if (!alreadyTagged) {
|
|
4464
|
-
await db.update(mindHistory).set({ turn_id: turnId }).where(
|
|
4605
|
+
await db.update(mindHistory).set({ turn_id: turnId }).where(eq6(mindHistory.id, outboundId));
|
|
4465
4606
|
}
|
|
4466
4607
|
if (row.message_id) {
|
|
4467
4608
|
await db.update(messages).set({
|
|
4468
4609
|
turn_id: turnId,
|
|
4469
4610
|
...toolUseEventId != null ? { source_event_id: toolUseEventId } : {}
|
|
4470
|
-
}).where(
|
|
4611
|
+
}).where(eq6(messages.id, Number(row.message_id)));
|
|
4471
4612
|
}
|
|
4472
4613
|
if (!alreadyTagged) {
|
|
4473
4614
|
publish3(mind, {
|
|
@@ -4643,12 +4784,12 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
4643
4784
|
if (config.sleep.schedule?.wake) contextParts.push(`Wake cron: ${config.sleep.schedule.wake}`);
|
|
4644
4785
|
}
|
|
4645
4786
|
try {
|
|
4646
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4787
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
|
|
4647
4788
|
const sm = getSleepManagerIfReady2();
|
|
4648
4789
|
if (sm) {
|
|
4649
|
-
const
|
|
4650
|
-
if (
|
|
4651
|
-
contextParts.push(`Sleep state: sleeping since ${
|
|
4790
|
+
const state2 = sm.getState(mindName);
|
|
4791
|
+
if (state2.sleeping) {
|
|
4792
|
+
contextParts.push(`Sleep state: sleeping since ${state2.sleepingSince}`);
|
|
4652
4793
|
}
|
|
4653
4794
|
}
|
|
4654
4795
|
} catch (err) {
|
|
@@ -4704,6 +4845,8 @@ export {
|
|
|
4704
4845
|
getTypingMap,
|
|
4705
4846
|
isConversationId,
|
|
4706
4847
|
publishTypingForChannels,
|
|
4848
|
+
checkStaleSend,
|
|
4849
|
+
formatHoldNotice,
|
|
4707
4850
|
DeliveryManager,
|
|
4708
4851
|
initDeliveryManager,
|
|
4709
4852
|
getDeliveryManager,
|