volute 0.45.1 → 0.46.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/README.md +3 -3
- package/dist/{activity-events-OMCSM7XW.js → activity-events-NF6VWTJQ.js} +2 -2
- package/dist/{ai-service-534WUSDL.js → ai-service-UODCCE4O.js} +3 -3
- package/dist/{archive-6R4TZMYI.js → archive-WS2L2VIQ.js} +2 -2
- package/dist/{auth-FRZJVQCS.js → auth-CCFAU4GQ.js} +3 -3
- package/dist/{avatar-image-BNWEXSA2.js → avatar-image-6KY6UU4D.js} +3 -3
- package/dist/{backup-OZXHL3JS.js → backup-TST5C2YN.js} +7 -7
- package/dist/{chat-I4EDDIQC.js → chat-ZIQQS25U.js} +2 -2
- package/dist/{chunk-ZIR7HSAX.js → chunk-4JOQZPCE.js} +1 -1
- package/dist/chunk-4YU7HF4L.js +237 -0
- package/dist/{chunk-JSRMEY52.js → chunk-5ZQZDDIK.js} +10 -5
- package/dist/{chunk-2BONPXAF.js → chunk-6EMWJZBK.js} +1 -1
- package/dist/{chunk-LAXUUY7O.js → chunk-A7WQZKOU.js} +2 -2
- package/dist/{chunk-H7IYFRAL.js → chunk-ALKYQULU.js} +10 -5
- package/dist/{chunk-5AUKCNPE.js → chunk-BJZ4PV7R.js} +8 -8
- package/dist/{chunk-KP3KWNG6.js → chunk-CDPBTTZO.js} +2 -2
- package/dist/{chunk-SQL7M4HJ.js → chunk-DRJ24GXK.js} +2 -2
- package/dist/{chunk-77BVGCTZ.js → chunk-E3DA4HYV.js} +9 -9
- package/dist/{chunk-322KE457.js → chunk-F7H6ATM4.js} +1 -1
- package/dist/{chunk-KVKP6VQ5.js → chunk-FXORA5JG.js} +12 -3
- package/dist/{chunk-3ERIL673.js → chunk-H2X54EHC.js} +1 -1
- package/dist/{chunk-TSK5PI4L.js → chunk-IHKGWTGT.js} +1 -1
- package/dist/{chunk-U4FNXDEJ.js → chunk-JO5IO4FH.js} +422 -168
- package/dist/{chunk-OXXTPILE.js → chunk-JZ62SYWR.js} +1 -1
- package/dist/{chunk-IUXTYXFI.js → chunk-K6T43YBD.js} +3 -3
- package/dist/{chunk-Y6OPHHGM.js → chunk-KGIXX4NM.js} +4 -4
- package/dist/{chunk-HVU45COX.js → chunk-KQSFTPIM.js} +1 -1
- package/dist/{chunk-CYI534DJ.js → chunk-OV7ZQ2GH.js} +2 -2
- package/dist/{chunk-CU6OFXMM.js → chunk-PFZBNKUH.js} +9 -0
- package/dist/{clock-K43IMROX.js → chunk-PJ45FQPT.js} +21 -16
- package/dist/{chunk-QMJJWTNL.js → chunk-QCN24XEN.js} +1 -1
- package/dist/{chunk-UMI4UIJG.js → chunk-R77O2WMK.js} +1 -1
- package/dist/{chunk-IEWZ7VD7.js → chunk-RRNXJOKB.js} +2 -2
- package/dist/{chunk-FHWUQZXE.js → chunk-RXEEYDIB.js} +1 -1
- package/dist/{chunk-Q2XBKQ2D.js → chunk-TQJ6ZYVY.js} +2 -2
- package/dist/{chunk-V4GYPD5L.js → chunk-YXSUMLTC.js} +46 -269
- package/dist/{chunk-5GT7URXJ.js → chunk-ZDZISKCL.js} +1 -1
- package/dist/{chunk-ZMIMSIIT.js → chunk-ZNMCCHCP.js} +48 -2
- package/dist/cli.js +20 -20
- package/dist/clock-RHS2JEDL.js +16 -0
- package/dist/{cloud-sync-Y7LQPRE3.js → cloud-sync-46RS4SDA.js} +17 -16
- package/dist/{conversations-G4LYMHAA.js → conversations-CJOOCGNW.js} +3 -3
- package/dist/{create-2YY7IPMS.js → create-IOV4GCRX.js} +1 -1
- package/dist/{daemon-restart-ABP54YN6.js → daemon-restart-VFEI57UO.js} +6 -6
- package/dist/daemon.js +392 -270
- package/dist/{db-JFLCJCUG.js → db-MF2JNRUW.js} +1 -1
- package/dist/{delete-BFMIGAIB.js → delete-HPLC7ETU.js} +1 -1
- package/dist/delivery-manager-JINZRG2W.js +33 -0
- package/dist/{delivery-router-6DE3BPHO.js → delivery-router-N4A7ZC5T.js} +2 -2
- package/dist/{down-XCAZXWLB.js → down-U2CZ23M4.js} +4 -4
- package/dist/echo-text-VQPB5L2X.js +33 -0
- package/dist/{exec-4GWHAHS3.js → exec-G3ZRST4Z.js} +2 -2
- package/dist/{export-ID7YUSLZ.js → export-LY723I3Z.js} +5 -5
- package/dist/{extensions-AYSPKE33.js → extensions-ZELQLTSR.js} +17 -16
- package/dist/{files-BQ2WB5KC.js → files-4LN3EN7Q.js} +2 -2
- package/dist/{import-SKID37QX.js → import-42YNQBF4.js} +5 -5
- package/dist/{isolation-5MB3J73F.js → isolation-YMGZZ4E5.js} +2 -2
- package/dist/{join-RXPXGV4L.js → join-ZJV7W6MO.js} +4 -0
- package/dist/{login-LM4NF4ZB.js → login-M6OJUPQ2.js} +1 -1
- package/dist/{logout-CTJVIZGA.js → logout-WQBWO77G.js} +1 -1
- package/dist/message-delivery-R7JSQGYX.js +37 -0
- package/dist/{mind-KLHC7XYR.js → mind-LHKS55DI.js} +12 -12
- package/dist/{mind-activity-tracker-BGHJE7XA.js → mind-activity-tracker-3QRNJVR4.js} +3 -3
- package/dist/mind-service-NB2JMWQZ.js +39 -0
- package/dist/mind-sleep-74TGOQBC.js +83 -0
- package/dist/{package-BMHV7UHN.js → package-DOD6CPO6.js} +2 -1
- package/dist/{registry-NUJAGLQJ.js → registry-OUZBZ3Q4.js} +5 -1
- package/dist/{sandbox-72MWV4JL.js → sandbox-ZJ7QA6Z6.js} +3 -3
- package/dist/scheduler-WVKII3LA.js +31 -0
- package/dist/{seed-4MKQ5QKG.js → seed-CNYIHPOH.js} +1 -1
- package/dist/{seed-cmd-LCXMR7VV.js → seed-cmd-AVNUNLZV.js} +2 -2
- package/dist/{seed-create-N2BF5LQX.js → seed-create-ANXGYSCC.js} +5 -5
- package/dist/{seed-sprout-6ZDP23YI.js → seed-sprout-NIUFTX32.js} +10 -8
- package/dist/{send-O6W53VBM.js → send-SPE6DVNJ.js} +8 -2
- package/dist/{service-AQVPUANO.js → service-UOCLXDWR.js} +3 -3
- package/dist/{service-install-4QNUDKLM.js → service-install-4HHFDDWY.js} +4 -4
- package/dist/{setup-3O6O2JZ7.js → setup-QT2A25SY.js} +6 -6
- package/dist/{setup-73YBJZQL.js → setup-Y2EJI3GG.js} +2 -2
- package/dist/skills/imagegen/scripts/imagegen.ts +100 -40
- package/dist/skills/orientation/SKILL.md +1 -1
- package/dist/skills/volute-mind/SKILL.md +13 -9
- package/dist/skills/volute-mind/references/sleep.md +8 -1
- package/dist/{skills-ND4ZFYUW.js → skills-AKCFA7BN.js} +4 -4
- package/dist/sleep-manager-G6TXSDP7.js +35 -0
- package/dist/{spirit-II3HOERM.js → spirit-RPBLLUQ5.js} +7 -7
- package/dist/{sprout-4KUG76AQ.js → sprout-SMX42ZBP.js} +1 -1
- package/dist/{src-XFY3XDD6.js → src-OJHARHTF.js} +28 -17
- package/dist/{status-E5DQNCAO.js → status-SWRVGXBI.js} +4 -4
- package/dist/system-chat-NDAW6YP7.js +37 -0
- package/dist/{tailscale-QAWVKQKU.js → tailscale-3XOKOW7T.js} +1 -1
- package/dist/{turn-tracker-QQIVVXBI.js → turn-tracker-FOIBNUUA.js} +5 -4
- package/dist/{up-LG56MPBI.js → up-PS6PFS75.js} +5 -5
- package/dist/{update-7TYVH5FM.js → update-SWAYZRWJ.js} +4 -4
- package/dist/{update-check-VSCSJ7KX.js → update-check-PQ2MZ47K.js} +2 -2
- package/dist/{version-notify-CODJOUW2.js → version-notify-C5CH7CVP.js} +24 -19
- package/dist/{volute-config-TS62GS6A.js → volute-config-A52FHWKM.js} +5 -1
- package/dist/web-assets/assets/{index-Dac11sKB.js → index-CU6XWICM.js} +15 -15
- package/dist/web-assets/index.html +1 -1
- package/package.json +2 -1
- package/templates/_base/home/VOLUTE.md +6 -2
- package/templates/_base/src/lib/context-breakdown.ts +57 -67
- package/templates/_base/src/lib/file-handler.ts +3 -3
- package/templates/_base/src/lib/router.ts +21 -25
- package/templates/_base/src/lib/startup.ts +2 -0
- package/templates/_base/src/lib/types.ts +2 -2
- package/templates/_base/src/lib/volute-server.ts +2 -2
- package/templates/claude/src/agent.ts +147 -46
- package/templates/claude/src/lib/message-channel.ts +23 -0
- package/templates/claude/src/lib/session-reaper.ts +68 -0
- package/templates/claude/src/server.ts +1 -0
- package/templates/codex/src/agent.ts +80 -46
- package/templates/pi/src/agent.ts +73 -45
- package/dist/delivery-manager-R5STGLXB.js +0 -32
- package/dist/echo-text-NQXYYYCU.js +0 -32
- package/dist/message-delivery-IUAXRPFE.js +0 -34
- package/dist/mind-service-BF2LUQMH.js +0 -36
- package/dist/mind-sleep-YMBR3I35.js +0 -47
- package/dist/scheduler-YHJRRV6E.js +0 -30
- package/dist/sleep-manager-YAHDOIU5.js +0 -34
- package/dist/system-chat-E65T2HSW.js +0 -34
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadMergedEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZDZISKCL.js";
|
|
5
5
|
import {
|
|
6
6
|
spiritDir
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BJZ4PV7R.js";
|
|
8
8
|
import {
|
|
9
9
|
hashSkillDir,
|
|
10
10
|
importSkillFromDir,
|
|
11
11
|
removeSharedSkill,
|
|
12
12
|
sharedSkillsDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KGIXX4NM.js";
|
|
14
14
|
import {
|
|
15
15
|
addMessage,
|
|
16
16
|
createChannel,
|
|
@@ -20,74 +20,79 @@ import {
|
|
|
20
20
|
getParticipants,
|
|
21
21
|
joinChannel,
|
|
22
22
|
publish as publish2
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-5ZQZDDIK.js";
|
|
24
24
|
import {
|
|
25
25
|
readVoluteConfig,
|
|
26
|
+
resolveWakeTriggers,
|
|
26
27
|
writeVoluteConfig
|
|
27
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-PFZBNKUH.js";
|
|
28
29
|
import {
|
|
29
30
|
isSandboxEnabled,
|
|
30
31
|
wrapForSandbox
|
|
31
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-JZ62SYWR.js";
|
|
32
33
|
import {
|
|
33
34
|
markIdle,
|
|
34
35
|
onMindEvent
|
|
35
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-KQSFTPIM.js";
|
|
36
37
|
import {
|
|
37
38
|
extractTextContent,
|
|
38
39
|
getRoutingConfig,
|
|
39
40
|
resolveDeliveryMode,
|
|
40
41
|
resolveRoute,
|
|
41
42
|
setRoutesChangeListener
|
|
42
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-4JOQZPCE.js";
|
|
43
44
|
import {
|
|
44
45
|
clearMind,
|
|
45
|
-
getPrompt,
|
|
46
46
|
linkInboundToActiveTurn,
|
|
47
47
|
publish as publish3,
|
|
48
48
|
summarizeOrphanedTurns
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-YXSUMLTC.js";
|
|
50
|
+
import {
|
|
51
|
+
getPrompt
|
|
52
|
+
} from "./chunk-4YU7HF4L.js";
|
|
50
53
|
import {
|
|
51
54
|
chownMindDir,
|
|
52
55
|
exec,
|
|
53
56
|
isIsolationEnabled,
|
|
54
57
|
mindUserName,
|
|
55
58
|
wrapForIsolation
|
|
56
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-H2X54EHC.js";
|
|
57
60
|
import {
|
|
58
61
|
aiCompleteUtility,
|
|
59
62
|
getAiConfig,
|
|
60
63
|
resolveApiKey
|
|
61
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-IHKGWTGT.js";
|
|
62
65
|
import {
|
|
63
66
|
readGlobalConfig,
|
|
64
67
|
writeGlobalConfig
|
|
65
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-F7H6ATM4.js";
|
|
66
69
|
import {
|
|
67
70
|
getOrCreateMindUser,
|
|
68
71
|
getOrCreateSystemUser,
|
|
69
72
|
getUser,
|
|
70
73
|
getUserByUsername,
|
|
71
74
|
syncMindProfile
|
|
72
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-OV7ZQ2GH.js";
|
|
73
76
|
import {
|
|
74
77
|
publish,
|
|
75
78
|
subscribe
|
|
76
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-ZNMCCHCP.js";
|
|
77
80
|
import {
|
|
78
81
|
logger_default
|
|
79
82
|
} from "./chunk-T2TP6ZC6.js";
|
|
80
83
|
import {
|
|
84
|
+
SPIRIT_NAME,
|
|
81
85
|
findMind,
|
|
82
86
|
getBaseName,
|
|
83
87
|
getDb,
|
|
88
|
+
isSpiritName,
|
|
84
89
|
mindDir,
|
|
85
90
|
mindTmpDir,
|
|
86
91
|
setMindRunning,
|
|
87
92
|
stateDir,
|
|
88
93
|
voluteHome,
|
|
89
94
|
voluteSystemDir
|
|
90
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-ALKYQULU.js";
|
|
91
96
|
import {
|
|
92
97
|
activity,
|
|
93
98
|
deliveryQueue,
|
|
@@ -335,6 +340,9 @@ function revokeMindToken(mindName) {
|
|
|
335
340
|
function resolveMindToken(token) {
|
|
336
341
|
return tokenToMind.get(token) ?? null;
|
|
337
342
|
}
|
|
343
|
+
function getMindToken(mindName) {
|
|
344
|
+
return mindToToken.get(mindName) ?? null;
|
|
345
|
+
}
|
|
338
346
|
|
|
339
347
|
// packages/daemon/src/lib/daemon/notices.ts
|
|
340
348
|
import { and, asc, eq, lte, or, sql } from "drizzle-orm";
|
|
@@ -763,21 +771,8 @@ var MindManager = class {
|
|
|
763
771
|
if (isIsolationEnabled()) {
|
|
764
772
|
env.HOME = resolve2(dir, "home");
|
|
765
773
|
}
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
let baseArgs;
|
|
769
|
-
if (customNode) {
|
|
770
|
-
baseBin = customNode;
|
|
771
|
-
baseArgs = [
|
|
772
|
-
resolve2(dir, "node_modules", ".bin", "tsx"),
|
|
773
|
-
"src/server.ts",
|
|
774
|
-
"--port",
|
|
775
|
-
String(port)
|
|
776
|
-
];
|
|
777
|
-
} else {
|
|
778
|
-
baseBin = resolve2(dir, "node_modules", ".bin", "tsx");
|
|
779
|
-
baseArgs = ["src/server.ts", "--port", String(port)];
|
|
780
|
-
}
|
|
774
|
+
const baseBin = process.env.VOLUTE_NODE_PATH ?? process.execPath;
|
|
775
|
+
const baseArgs = ["--import", "tsx", "src/server.ts", "--port", String(port)];
|
|
781
776
|
let spawnCmd;
|
|
782
777
|
let spawnArgs;
|
|
783
778
|
if (isIsolationEnabled()) {
|
|
@@ -939,7 +934,7 @@ var MindManager = class {
|
|
|
939
934
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
940
935
|
mlog.error(`mind ${name} exited with code ${code}`);
|
|
941
936
|
try {
|
|
942
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
937
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
943
938
|
const sleepMgr = getSleepManagerIfReady2();
|
|
944
939
|
const sleepState = sleepMgr?.getState(name);
|
|
945
940
|
if (sleepState?.sleeping) {
|
|
@@ -969,15 +964,15 @@ var MindManager = class {
|
|
|
969
964
|
(err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
|
|
970
965
|
);
|
|
971
966
|
try {
|
|
972
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
967
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-JINZRG2W.js");
|
|
973
968
|
getDeliveryManager2().clearMindSessions(name);
|
|
974
969
|
} catch (err) {
|
|
975
970
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
976
971
|
mlog.warn(`failed to clear delivery state for ${name} after crash`, logger_default.errorData(err));
|
|
977
972
|
}
|
|
978
973
|
}
|
|
979
|
-
import("./mind-activity-tracker-
|
|
980
|
-
import("./activity-events-
|
|
974
|
+
import("./mind-activity-tracker-3QRNJVR4.js").then(({ markIdle: markIdle2 }) => markIdle2(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
|
|
975
|
+
import("./activity-events-NF6VWTJQ.js").then(
|
|
981
976
|
({ publish: publish4 }) => publish4({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
|
|
982
977
|
).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
|
|
983
978
|
const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
|
|
@@ -1035,7 +1030,7 @@ var MindManager = class {
|
|
|
1035
1030
|
mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
|
|
1036
1031
|
}
|
|
1037
1032
|
try {
|
|
1038
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
1033
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-JINZRG2W.js");
|
|
1039
1034
|
getDeliveryManager2().clearMindSessions(name);
|
|
1040
1035
|
} catch (err) {
|
|
1041
1036
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
@@ -1043,7 +1038,7 @@ var MindManager = class {
|
|
|
1043
1038
|
}
|
|
1044
1039
|
}
|
|
1045
1040
|
try {
|
|
1046
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
1041
|
+
const { clearEchoTextCache } = await import("./echo-text-VQPB5L2X.js");
|
|
1047
1042
|
clearEchoTextCache(name);
|
|
1048
1043
|
} catch (err) {
|
|
1049
1044
|
mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
|
|
@@ -1220,6 +1215,10 @@ async function getSessionUserId(sessionId) {
|
|
|
1220
1215
|
return row.userId;
|
|
1221
1216
|
}
|
|
1222
1217
|
async function cleanExpiredSessions() {
|
|
1218
|
+
const now = Date.now();
|
|
1219
|
+
for (const [id, cached] of sessionCache) {
|
|
1220
|
+
if (cached.expires <= now) sessionCache.delete(id);
|
|
1221
|
+
}
|
|
1223
1222
|
const db = await getDb();
|
|
1224
1223
|
const cutoff = Date.now() - SESSION_MAX_AGE;
|
|
1225
1224
|
await db.delete(sessions).where(lt(sessions.createdAt, cutoff));
|
|
@@ -1699,7 +1698,7 @@ function resolveSkillsDir(manifest) {
|
|
|
1699
1698
|
async function discoverBuiltinExtensions(disabledIds) {
|
|
1700
1699
|
const builtins = [
|
|
1701
1700
|
{ id: "notes", load: async () => (await import("./src-YH6PQ7LJ.js")).default },
|
|
1702
|
-
{ id: "pages", load: async () => (await import("./src-
|
|
1701
|
+
{ id: "pages", load: async () => (await import("./src-OJHARHTF.js")).default },
|
|
1703
1702
|
{ id: "plan", load: async () => (await import("./src-JAOVXO5W.js")).default }
|
|
1704
1703
|
];
|
|
1705
1704
|
const results = [];
|
|
@@ -1949,7 +1948,7 @@ async function installNpmExtension(pkg) {
|
|
|
1949
1948
|
throw new Error(`Extension "${pkg}" is already installed`);
|
|
1950
1949
|
}
|
|
1951
1950
|
const dir = ensureExtensionsNpmDir();
|
|
1952
|
-
const { exec: exec2 } = await import("./exec-
|
|
1951
|
+
const { exec: exec2 } = await import("./exec-G3ZRST4Z.js");
|
|
1953
1952
|
try {
|
|
1954
1953
|
await exec2("npm", ["install", "--ignore-scripts", pkg], { cwd: dir });
|
|
1955
1954
|
} catch (err) {
|
|
@@ -1970,7 +1969,7 @@ async function uninstallNpmExtension(pkg) {
|
|
|
1970
1969
|
packages.splice(idx, 1);
|
|
1971
1970
|
writeExtensionsConfig(packages);
|
|
1972
1971
|
try {
|
|
1973
|
-
const { exec: exec2 } = await import("./exec-
|
|
1972
|
+
const { exec: exec2 } = await import("./exec-G3ZRST4Z.js");
|
|
1974
1973
|
await exec2("npm", ["uninstall", "--ignore-scripts", pkg], { cwd: extensionsNpmDir() });
|
|
1975
1974
|
} catch (err) {
|
|
1976
1975
|
logger_default.warn(
|
|
@@ -2300,6 +2299,7 @@ async function ensureMailAddress(mindName) {
|
|
|
2300
2299
|
import { resolve as resolve5 } from "path";
|
|
2301
2300
|
import { CronExpressionParser } from "cron-parser";
|
|
2302
2301
|
var slog2 = logger_default.child("scheduler");
|
|
2302
|
+
var CATCHUP_STALE_MINUTES = 10;
|
|
2303
2303
|
var Scheduler = class {
|
|
2304
2304
|
schedules = /* @__PURE__ */ new Map();
|
|
2305
2305
|
mindDirs = /* @__PURE__ */ new Map();
|
|
@@ -2339,6 +2339,26 @@ var Scheduler = class {
|
|
|
2339
2339
|
} else {
|
|
2340
2340
|
this.schedules.delete(mindName);
|
|
2341
2341
|
}
|
|
2342
|
+
const epochMinute = Math.floor(Date.now() / 6e4);
|
|
2343
|
+
const validKeys = new Set(schedules.map((s) => `${mindName}:${s.id}`));
|
|
2344
|
+
let changed = false;
|
|
2345
|
+
for (const key of this.lastFired.keys()) {
|
|
2346
|
+
if (key.startsWith(`${mindName}:`) && !validKeys.has(key)) {
|
|
2347
|
+
this.lastFired.delete(key);
|
|
2348
|
+
changed = true;
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
for (const key of validKeys) {
|
|
2352
|
+
if (!this.lastFired.has(key)) {
|
|
2353
|
+
this.lastFired.set(key, epochMinute);
|
|
2354
|
+
changed = true;
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
if (changed) {
|
|
2358
|
+
this.saveState().catch(
|
|
2359
|
+
(err) => slog2.warn(`failed to persist scheduler state for ${mindName}`, logger_default.errorData(err))
|
|
2360
|
+
);
|
|
2361
|
+
}
|
|
2342
2362
|
}
|
|
2343
2363
|
unloadSchedules(mindName) {
|
|
2344
2364
|
this.schedules.delete(mindName);
|
|
@@ -2384,11 +2404,14 @@ var Scheduler = class {
|
|
|
2384
2404
|
return false;
|
|
2385
2405
|
}
|
|
2386
2406
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2407
|
+
const lastFired = this.lastFired.get(key) ?? epochMinute;
|
|
2408
|
+
if (prevMinute <= lastFired) return false;
|
|
2409
|
+
this.lastFired.set(key, prevMinute);
|
|
2410
|
+
if (epochMinute - prevMinute > CATCHUP_STALE_MINUTES) {
|
|
2411
|
+
slog2.info(`skipping stale catch-up fire for ${key} (${epochMinute - prevMinute}min late)`);
|
|
2412
|
+
return false;
|
|
2390
2413
|
}
|
|
2391
|
-
return
|
|
2414
|
+
return true;
|
|
2392
2415
|
}
|
|
2393
2416
|
async fire(mindName, schedule) {
|
|
2394
2417
|
try {
|
|
@@ -2415,7 +2438,9 @@ ${stderr}` : ""}`;
|
|
|
2415
2438
|
return;
|
|
2416
2439
|
}
|
|
2417
2440
|
await this.deliverSystem(mindName, `[${schedule.id}] ${text}`, {
|
|
2418
|
-
|
|
2441
|
+
// Default schedule fires to "queue" while asleep so an unadorned cron
|
|
2442
|
+
// schedule doesn't inherit the DM wake-trigger fallback and wake the mind.
|
|
2443
|
+
whileSleeping: schedule.whileSleeping ?? "queue",
|
|
2419
2444
|
session: schedule.session
|
|
2420
2445
|
});
|
|
2421
2446
|
slog2.info(`fired "${schedule.id}" for ${mindName}`);
|
|
@@ -2452,15 +2477,41 @@ ${stderr}` : ""}`;
|
|
|
2452
2477
|
}
|
|
2453
2478
|
}
|
|
2454
2479
|
async runScript(script, cwd, mindName) {
|
|
2480
|
+
const env = await this.buildScriptEnv(mindName);
|
|
2455
2481
|
if (isSandboxEnabled()) {
|
|
2456
2482
|
const dir = this.mindDirs.get(mindName) ?? mindDir(mindName);
|
|
2457
2483
|
const [cmd, args] = await wrapForSandbox("bash", ["-c", script], dir, mindName, [
|
|
2458
2484
|
dir,
|
|
2459
2485
|
mindTmpDir(dir)
|
|
2460
2486
|
]);
|
|
2461
|
-
return exec(cmd, args, { cwd });
|
|
2487
|
+
return exec(cmd, args, { cwd, env });
|
|
2462
2488
|
}
|
|
2463
|
-
return exec("bash", ["-c", script], { cwd, mindName });
|
|
2489
|
+
return exec("bash", ["-c", script], { cwd, mindName, env });
|
|
2490
|
+
}
|
|
2491
|
+
/**
|
|
2492
|
+
* Build the environment for a scheduled script — mirrors the mind process env
|
|
2493
|
+
* (allowlisted base + merged mind env + VOLUTE_* runtime vars), authenticated
|
|
2494
|
+
* with the mind's own per-mind, non-admin token. Reuses the running mind's
|
|
2495
|
+
* token when present; otherwise mints one (stable across fires, regenerated on
|
|
2496
|
+
* next mind start). The daemon admin token is never included.
|
|
2497
|
+
*/
|
|
2498
|
+
async buildScriptEnv(mindName) {
|
|
2499
|
+
const dir = this.mindDirs.get(mindName) ?? mindDir(mindName);
|
|
2500
|
+
const entry = await findMind(mindName);
|
|
2501
|
+
const token = getMindToken(mindName) ?? generateMindToken(mindName);
|
|
2502
|
+
const mindLocalBin = resolve5(dir, "home", ".local", "bin");
|
|
2503
|
+
const currentPath = process.env.PATH ?? "";
|
|
2504
|
+
return {
|
|
2505
|
+
...buildMindBaseEnv(),
|
|
2506
|
+
...loadMergedEnv(mindName),
|
|
2507
|
+
VOLUTE_MIND: mindName,
|
|
2508
|
+
VOLUTE_STATE_DIR: stateDir(mindName),
|
|
2509
|
+
VOLUTE_MIND_DIR: dir,
|
|
2510
|
+
VOLUTE_MIND_PORT: entry ? String(entry.port) : void 0,
|
|
2511
|
+
VOLUTE_MIND_TOKEN: token,
|
|
2512
|
+
TMPDIR: mindTmpDir(dir),
|
|
2513
|
+
PATH: `${mindLocalBin}:${currentPath}`
|
|
2514
|
+
};
|
|
2464
2515
|
}
|
|
2465
2516
|
deliverSystem(mindName, text, opts) {
|
|
2466
2517
|
return sendSystemMessage(mindName, text, opts);
|
|
@@ -2705,11 +2756,7 @@ async function startMindFull(name) {
|
|
|
2705
2756
|
if (entry?.parent) return;
|
|
2706
2757
|
if (!entry || entry.stage === "seed") {
|
|
2707
2758
|
if (entry?.stage === "seed") {
|
|
2708
|
-
|
|
2709
|
-
sendSystemMessage(
|
|
2710
|
-
baseName,
|
|
2711
|
-
`You've just been created. A human planted you as a seed. Start a conversation with them \u2014 introduce yourself, ask questions, and begin exploring who you want to be.${creatorNote}`
|
|
2712
|
-
).catch(
|
|
2759
|
+
sendSeedOrientation(baseName, entry.createdBy).catch(
|
|
2713
2760
|
(err) => logger_default.error(`failed to send seed orientation for ${baseName}`, logger_default.errorData(err))
|
|
2714
2761
|
);
|
|
2715
2762
|
if (entry.createdBy) {
|
|
@@ -2771,7 +2818,7 @@ async function wakeMind(name) {
|
|
|
2771
2818
|
async function startSpiritFull(name) {
|
|
2772
2819
|
const entry = await findMind(name);
|
|
2773
2820
|
if (entry?.dir) {
|
|
2774
|
-
const { registerMindDir } = await import("./delivery-router-
|
|
2821
|
+
const { registerMindDir } = await import("./delivery-router-N4A7ZC5T.js");
|
|
2775
2822
|
registerMindDir(name, entry.dir);
|
|
2776
2823
|
}
|
|
2777
2824
|
await getMindManager().startMind(name);
|
|
@@ -2792,9 +2839,23 @@ async function stopSpiritFull(name) {
|
|
|
2792
2839
|
summary: `${name} spirit stopped`
|
|
2793
2840
|
}).catch((err) => logger_default.error("failed to publish spirit_stopped activity", logger_default.errorData(err)));
|
|
2794
2841
|
}
|
|
2842
|
+
function buildSeedOrientation(createdBy, creatorType) {
|
|
2843
|
+
const intro = "You've just been created as a seed. Start a conversation \u2014 introduce yourself, ask questions, and begin exploring who you want to be.";
|
|
2844
|
+
if (!createdBy) return intro;
|
|
2845
|
+
const who = creatorType === "mind" ? `another mind, ${createdBy}` : creatorType === "system" ? `the spirit of this system (${createdBy})` : createdBy;
|
|
2846
|
+
return `${intro} Your creator is ${who}. Send them a message to introduce yourself.`;
|
|
2847
|
+
}
|
|
2848
|
+
async function sendSeedOrientation(mindName, createdBy) {
|
|
2849
|
+
let creatorType;
|
|
2850
|
+
if (createdBy) {
|
|
2851
|
+
const { getUserByUsername: getUserByUsername2 } = await import("./auth-CCFAU4GQ.js");
|
|
2852
|
+
creatorType = (await getUserByUsername2(createdBy))?.user_type;
|
|
2853
|
+
}
|
|
2854
|
+
await sendSystemMessage(mindName, buildSeedOrientation(createdBy, creatorType));
|
|
2855
|
+
}
|
|
2795
2856
|
async function ensureCreatorDM(mindName, creatorUsername) {
|
|
2796
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-
|
|
2797
|
-
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-
|
|
2857
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-CCFAU4GQ.js");
|
|
2858
|
+
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-CJOOCGNW.js");
|
|
2798
2859
|
const mindUser = await getOrCreateMindUser2(mindName);
|
|
2799
2860
|
const creatorUser = await getUserByUsername2(creatorUsername);
|
|
2800
2861
|
if (!creatorUser) {
|
|
@@ -2827,6 +2888,8 @@ async function stopMindFull(name) {
|
|
|
2827
2888
|
|
|
2828
2889
|
// packages/daemon/src/lib/daemon/sleep-manager.ts
|
|
2829
2890
|
var slog3 = logger_default.child("sleep");
|
|
2891
|
+
var MAX_WAKE_ATTEMPTS = 5;
|
|
2892
|
+
var WAKE_BACKOFF_MS = [6e4, 5 * 6e4, 15 * 6e4, 60 * 6e4];
|
|
2830
2893
|
function defaultState() {
|
|
2831
2894
|
return {
|
|
2832
2895
|
sleeping: false,
|
|
@@ -2835,7 +2898,10 @@ function defaultState() {
|
|
|
2835
2898
|
wokenByTrigger: false,
|
|
2836
2899
|
voluntaryWakeAt: null,
|
|
2837
2900
|
queuedMessageCount: 0,
|
|
2838
|
-
triggerWakeHistory: []
|
|
2901
|
+
triggerWakeHistory: [],
|
|
2902
|
+
wakeFailures: 0,
|
|
2903
|
+
nextWakeAttemptAt: null,
|
|
2904
|
+
lastWakeAt: null
|
|
2839
2905
|
};
|
|
2840
2906
|
}
|
|
2841
2907
|
function formatCurrentDate() {
|
|
@@ -2905,6 +2971,9 @@ var SleepManager = class {
|
|
|
2905
2971
|
const data = JSON.parse(readFileSync7(this.statePath, "utf-8"));
|
|
2906
2972
|
for (const [name, state2] of Object.entries(data)) {
|
|
2907
2973
|
state2.triggerWakeHistory ??= [];
|
|
2974
|
+
state2.wakeFailures ??= 0;
|
|
2975
|
+
state2.nextWakeAttemptAt ??= null;
|
|
2976
|
+
state2.lastWakeAt ??= null;
|
|
2908
2977
|
this.states.set(name, state2);
|
|
2909
2978
|
}
|
|
2910
2979
|
}
|
|
@@ -2915,7 +2984,7 @@ var SleepManager = class {
|
|
|
2915
2984
|
saveState() {
|
|
2916
2985
|
const data = {};
|
|
2917
2986
|
for (const [name, state2] of this.states) {
|
|
2918
|
-
if (state2.sleeping) data[name] = state2;
|
|
2987
|
+
if (state2.sleeping || state2.lastWakeAt) data[name] = state2;
|
|
2919
2988
|
}
|
|
2920
2989
|
try {
|
|
2921
2990
|
writeFileSync6(this.statePath, `${JSON.stringify(data, null, 2)}
|
|
@@ -2965,8 +3034,9 @@ var SleepManager = class {
|
|
|
2965
3034
|
}
|
|
2966
3035
|
await this.archiveSessions(name);
|
|
2967
3036
|
state2.sleeping = true;
|
|
2968
|
-
|
|
2969
|
-
|
|
3037
|
+
if (!state2.voluntaryWakeAt) {
|
|
3038
|
+
state2.scheduledWakeAt = this.getNextWakeTime(this.getSleepConfig(name));
|
|
3039
|
+
}
|
|
2970
3040
|
this.saveState();
|
|
2971
3041
|
slog3.info(`${name} returned to sleep after crashing during trigger wake`);
|
|
2972
3042
|
} catch (err) {
|
|
@@ -3061,12 +3131,13 @@ var SleepManager = class {
|
|
|
3061
3131
|
try {
|
|
3062
3132
|
await wakeMind(name);
|
|
3063
3133
|
} catch (err) {
|
|
3064
|
-
|
|
3134
|
+
await this.handleWakeFailure(name, err);
|
|
3065
3135
|
return;
|
|
3066
3136
|
}
|
|
3067
3137
|
}
|
|
3068
3138
|
const entry = await findMind(name);
|
|
3069
3139
|
if (!entry) return;
|
|
3140
|
+
this.resetWakeBackoff(name);
|
|
3070
3141
|
if (opts?.trigger) {
|
|
3071
3142
|
state2.wokenByTrigger = true;
|
|
3072
3143
|
state2.triggerWakeHistory.push({
|
|
@@ -3121,6 +3192,7 @@ var SleepManager = class {
|
|
|
3121
3192
|
} catch (err) {
|
|
3122
3193
|
slog3.warn(`failed to deliver wake summary to ${name}`, logger_default.errorData(err));
|
|
3123
3194
|
}
|
|
3195
|
+
await this.waitForIdle(name, 12e4);
|
|
3124
3196
|
}
|
|
3125
3197
|
const flushed = await this.flushQueuedMessages(name);
|
|
3126
3198
|
if (flushed > 0) {
|
|
@@ -3140,8 +3212,7 @@ var SleepManager = class {
|
|
|
3140
3212
|
checkWakeTrigger(name, payload) {
|
|
3141
3213
|
const config = this.getSleepConfig(name);
|
|
3142
3214
|
const triggers = config?.wakeTriggers;
|
|
3143
|
-
const mentionsEnabled = triggers
|
|
3144
|
-
const dmsEnabled = triggers?.dms !== false;
|
|
3215
|
+
const { mentions: mentionsEnabled, dms: dmsEnabled } = resolveWakeTriggers(triggers);
|
|
3145
3216
|
if (dmsEnabled && payload.isDM) return true;
|
|
3146
3217
|
if (mentionsEnabled && payload.content) {
|
|
3147
3218
|
const text = typeof payload.content === "string" ? payload.content : Array.isArray(payload.content) ? payload.content.filter((b) => b.type === "text" && b.text).map((b) => b.text).join(" ") : "";
|
|
@@ -3179,53 +3250,159 @@ var SleepManager = class {
|
|
|
3179
3250
|
}
|
|
3180
3251
|
}
|
|
3181
3252
|
/**
|
|
3182
|
-
* Flush all queued sleep messages for a mind
|
|
3253
|
+
* Flush all queued sleep messages for a mind on wake. Rows are grouped by channel
|
|
3254
|
+
* and each channel group is delivered as ONE pre-batched turn (#382) — a night's
|
|
3255
|
+
* backlog becomes one stack per channel, not N cold-start doorbells. Delivery is
|
|
3256
|
+
* all-or-nothing per channel: a failed group's rows stay queued for the next wake,
|
|
3257
|
+
* while other channels still flush.
|
|
3183
3258
|
*/
|
|
3184
3259
|
async flushQueuedMessages(name) {
|
|
3185
3260
|
try {
|
|
3186
3261
|
const db = await getDb();
|
|
3187
3262
|
const rows = await db.select().from(deliveryQueue).where(and2(eq3(deliveryQueue.mind, name), eq3(deliveryQueue.status, "sleep-queued"))).all();
|
|
3188
3263
|
if (rows.length === 0) return 0;
|
|
3189
|
-
const
|
|
3190
|
-
const delivered = [];
|
|
3264
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3191
3265
|
for (const row of rows) {
|
|
3266
|
+
let payload;
|
|
3192
3267
|
try {
|
|
3193
|
-
|
|
3194
|
-
delivered.push(row.id);
|
|
3268
|
+
payload = JSON.parse(row.payload);
|
|
3195
3269
|
} catch (err) {
|
|
3196
|
-
slog3.warn(
|
|
3270
|
+
slog3.warn(
|
|
3271
|
+
`dropping unparseable queued message ${row.id} for ${name}`,
|
|
3272
|
+
logger_default.errorData(err)
|
|
3273
|
+
);
|
|
3274
|
+
await db.delete(deliveryQueue).where(eq3(deliveryQueue.id, row.id));
|
|
3275
|
+
continue;
|
|
3197
3276
|
}
|
|
3277
|
+
const channel = payload.channel ?? "unknown";
|
|
3278
|
+
if (!groups.has(channel)) groups.set(channel, []);
|
|
3279
|
+
groups.get(channel)?.push({ id: row.id, payload });
|
|
3198
3280
|
}
|
|
3199
|
-
|
|
3200
|
-
|
|
3281
|
+
let deliveredCount = 0;
|
|
3282
|
+
for (const [channel, items] of groups) {
|
|
3283
|
+
const delivered = await this.deliverQueuedBatch(
|
|
3284
|
+
name,
|
|
3285
|
+
items.map((i) => i.payload)
|
|
3286
|
+
);
|
|
3287
|
+
if (!delivered) {
|
|
3288
|
+
slog3.warn(
|
|
3289
|
+
`failed to flush ${items.length} queued message(s) on ${channel} for ${name}; leaving them queued`
|
|
3290
|
+
);
|
|
3291
|
+
continue;
|
|
3292
|
+
}
|
|
3293
|
+
await db.delete(deliveryQueue).where(
|
|
3294
|
+
inArray(
|
|
3295
|
+
deliveryQueue.id,
|
|
3296
|
+
items.map((i) => i.id)
|
|
3297
|
+
)
|
|
3298
|
+
);
|
|
3299
|
+
deliveredCount += items.length;
|
|
3201
3300
|
}
|
|
3202
3301
|
const state2 = this.states.get(name);
|
|
3203
3302
|
if (state2) {
|
|
3204
|
-
state2.queuedMessageCount = Math.max(0, state2.queuedMessageCount -
|
|
3303
|
+
state2.queuedMessageCount = Math.max(0, state2.queuedMessageCount - deliveredCount);
|
|
3205
3304
|
}
|
|
3206
|
-
return
|
|
3305
|
+
return deliveredCount;
|
|
3207
3306
|
} catch (err) {
|
|
3208
3307
|
slog3.warn(`failed to flush queued messages for ${name}`, logger_default.errorData(err));
|
|
3209
3308
|
return 0;
|
|
3210
3309
|
}
|
|
3211
3310
|
}
|
|
3311
|
+
/**
|
|
3312
|
+
* Deliver one channel's worth of queued messages as a single pre-batched turn on
|
|
3313
|
+
* the wake path. Extracted as a seam so the flush loop can be unit-tested without
|
|
3314
|
+
* the real delivery stack. Returns whether the batch was handed off to the mind.
|
|
3315
|
+
*/
|
|
3316
|
+
async deliverQueuedBatch(name, payloads) {
|
|
3317
|
+
const { deliverBatch: deliverBatch2 } = await import("./message-delivery-R7JSQGYX.js");
|
|
3318
|
+
return deliverBatch2(name, payloads);
|
|
3319
|
+
}
|
|
3320
|
+
// --- Wake failure handling (#419) ---
|
|
3321
|
+
/**
|
|
3322
|
+
* Record a failed wake attempt. Applies exponential backoff between retries and,
|
|
3323
|
+
* after MAX_WAKE_ATTEMPTS consecutive failures, gives up and marks the mind errored
|
|
3324
|
+
* rather than retrying every tick forever.
|
|
3325
|
+
*/
|
|
3326
|
+
async handleWakeFailure(name, err) {
|
|
3327
|
+
const state2 = this.states.get(name);
|
|
3328
|
+
if (!state2?.sleeping) return;
|
|
3329
|
+
state2.wakeFailures = (state2.wakeFailures ?? 0) + 1;
|
|
3330
|
+
if (state2.wakeFailures >= MAX_WAKE_ATTEMPTS) {
|
|
3331
|
+
await this.markWakeErrored(name, err);
|
|
3332
|
+
return;
|
|
3333
|
+
}
|
|
3334
|
+
const backoff = WAKE_BACKOFF_MS[Math.min(state2.wakeFailures - 1, WAKE_BACKOFF_MS.length - 1)];
|
|
3335
|
+
state2.nextWakeAttemptAt = new Date(Date.now() + backoff).toISOString();
|
|
3336
|
+
this.saveState();
|
|
3337
|
+
slog3.warn(
|
|
3338
|
+
`failed to wake ${name} (attempt ${state2.wakeFailures}/${MAX_WAKE_ATTEMPTS}); retrying in ${Math.round(backoff / 6e4)}m`,
|
|
3339
|
+
logger_default.errorData(err)
|
|
3340
|
+
);
|
|
3341
|
+
}
|
|
3342
|
+
/** Clear wake-failure backoff after a successful wake. */
|
|
3343
|
+
resetWakeBackoff(name) {
|
|
3344
|
+
const state2 = this.states.get(name);
|
|
3345
|
+
if (!state2) return;
|
|
3346
|
+
if (state2.wakeFailures === 0 && state2.nextWakeAttemptAt === null) return;
|
|
3347
|
+
state2.wakeFailures = 0;
|
|
3348
|
+
state2.nextWakeAttemptAt = null;
|
|
3349
|
+
this.saveState();
|
|
3350
|
+
}
|
|
3351
|
+
/**
|
|
3352
|
+
* A mind has failed to wake too many times — it isn't asleep, it's broken.
|
|
3353
|
+
* Clear the sleep state (so the tick stops retrying) and surface the failure
|
|
3354
|
+
* loudly: error log, activity event for the web UI, and a mind notice so the
|
|
3355
|
+
* mind learns what happened on its next successful session. A manual
|
|
3356
|
+
* `volute clock wake` / `volute mind start` is the recovery path.
|
|
3357
|
+
*/
|
|
3358
|
+
async markWakeErrored(name, err) {
|
|
3359
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
3360
|
+
this.states.delete(name);
|
|
3361
|
+
this.saveState();
|
|
3362
|
+
slog3.error(
|
|
3363
|
+
`${name} failed to wake ${MAX_WAKE_ATTEMPTS} times in a row; giving up and marking errored`,
|
|
3364
|
+
logger_default.errorData(err)
|
|
3365
|
+
);
|
|
3366
|
+
try {
|
|
3367
|
+
await publish({
|
|
3368
|
+
type: "mind_stopped",
|
|
3369
|
+
mind: name,
|
|
3370
|
+
summary: `${name} failed to wake after ${MAX_WAKE_ATTEMPTS} attempts`,
|
|
3371
|
+
metadata: { error: "wake_failed", detail }
|
|
3372
|
+
});
|
|
3373
|
+
} catch (e) {
|
|
3374
|
+
slog3.error(`failed to publish wake-failure activity for ${name}`, logger_default.errorData(e));
|
|
3375
|
+
}
|
|
3376
|
+
await recordNotice({
|
|
3377
|
+
mind: name,
|
|
3378
|
+
session: MIND_LEVEL_SESSION,
|
|
3379
|
+
kind: "startup",
|
|
3380
|
+
reason: "startup_failed",
|
|
3381
|
+
detail: `Wake failed ${MAX_WAKE_ATTEMPTS} times in a row and was given up. Last error: ${detail}`
|
|
3382
|
+
});
|
|
3383
|
+
}
|
|
3212
3384
|
// --- Internal methods ---
|
|
3213
3385
|
markSleeping(name, opts) {
|
|
3214
3386
|
const sleepConfig = this.getSleepConfig(name);
|
|
3215
3387
|
const state2 = {
|
|
3216
3388
|
sleeping: true,
|
|
3217
3389
|
sleepingSince: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3218
|
-
|
|
3390
|
+
// An explicit voluntary wake-at is authoritative for the night — null out
|
|
3391
|
+
// the cron wake so an earlier scheduled wake can't silently override it.
|
|
3392
|
+
scheduledWakeAt: opts?.voluntaryWakeAt ? null : this.getNextWakeTime(sleepConfig),
|
|
3219
3393
|
wokenByTrigger: false,
|
|
3220
3394
|
voluntaryWakeAt: opts?.voluntaryWakeAt ?? null,
|
|
3221
3395
|
queuedMessageCount: this.states.get(name)?.queuedMessageCount ?? 0,
|
|
3222
|
-
triggerWakeHistory: []
|
|
3396
|
+
triggerWakeHistory: [],
|
|
3397
|
+
wakeFailures: 0,
|
|
3398
|
+
nextWakeAttemptAt: null,
|
|
3399
|
+
lastWakeAt: this.states.get(name)?.lastWakeAt ?? null
|
|
3223
3400
|
};
|
|
3224
3401
|
this.states.set(name, state2);
|
|
3225
3402
|
this.saveState();
|
|
3226
3403
|
}
|
|
3227
3404
|
markAwake(name) {
|
|
3228
|
-
this.states.
|
|
3405
|
+
this.states.set(name, { ...defaultState(), lastWakeAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3229
3406
|
this.saveState();
|
|
3230
3407
|
}
|
|
3231
3408
|
getNextWakeTime(config) {
|
|
@@ -3240,52 +3417,78 @@ var SleepManager = class {
|
|
|
3240
3417
|
}
|
|
3241
3418
|
async tick() {
|
|
3242
3419
|
const now = /* @__PURE__ */ new Date();
|
|
3243
|
-
const epochMinute = Math.floor(now.getTime() / 6e4);
|
|
3244
3420
|
const mindNames = /* @__PURE__ */ new Set([...this.sleepConfigs.keys(), ...this.states.keys()]);
|
|
3245
3421
|
for (const name of mindNames) {
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
this.initiateWake(name).catch(
|
|
3266
|
-
(err) => slog3.error(`failed scheduled wake for ${name}`, logger_default.errorData(err))
|
|
3267
|
-
);
|
|
3268
|
-
continue;
|
|
3269
|
-
}
|
|
3422
|
+
await this.evaluateMind(name, now);
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
/**
|
|
3426
|
+
* Per-mind tick evaluation. The wake checks depend only on the persisted sleep
|
|
3427
|
+
* state — never on a currently-enabled sleep config — so a mind that was put to
|
|
3428
|
+
* sleep with a wake time still wakes even if the schedule is later disabled or
|
|
3429
|
+
* removed (or never existed, e.g. a bare `clock sleep --wake-at`). Only the
|
|
3430
|
+
* sleep-onset check requires a running mind with an enabled schedule.
|
|
3431
|
+
*/
|
|
3432
|
+
async evaluateMind(name, now) {
|
|
3433
|
+
const state2 = this.states.get(name);
|
|
3434
|
+
if (state2?.sleeping) {
|
|
3435
|
+
if (state2.nextWakeAttemptAt && now < new Date(state2.nextWakeAttemptAt)) return;
|
|
3436
|
+
if (state2.voluntaryWakeAt && now >= new Date(state2.voluntaryWakeAt)) {
|
|
3437
|
+
this.initiateWake(name).catch(
|
|
3438
|
+
(err) => slog3.error(`failed voluntary wake for ${name}`, logger_default.errorData(err))
|
|
3439
|
+
);
|
|
3440
|
+
return;
|
|
3270
3441
|
}
|
|
3271
|
-
if (
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
}
|
|
3442
|
+
if (state2.scheduledWakeAt && now >= new Date(state2.scheduledWakeAt)) {
|
|
3443
|
+
this.initiateWake(name).catch(
|
|
3444
|
+
(err) => slog3.error(`failed scheduled wake for ${name}`, logger_default.errorData(err))
|
|
3445
|
+
);
|
|
3446
|
+
return;
|
|
3277
3447
|
}
|
|
3448
|
+
return;
|
|
3449
|
+
}
|
|
3450
|
+
const mind = await findMind(name);
|
|
3451
|
+
if (!mind?.running) return;
|
|
3452
|
+
const config = this.getSleepConfig(name);
|
|
3453
|
+
if (!config?.enabled || !config.schedule) return;
|
|
3454
|
+
if (this.shouldSleepNow(config.schedule, state2?.lastWakeAt ?? null, now)) {
|
|
3455
|
+
this.initiateSleep(name).catch(
|
|
3456
|
+
(err) => slog3.error(`failed to initiate sleep for ${name}`, logger_default.errorData(err))
|
|
3457
|
+
);
|
|
3278
3458
|
}
|
|
3279
3459
|
}
|
|
3280
|
-
|
|
3460
|
+
/**
|
|
3461
|
+
* Level-triggered sleep-onset decision: sleep whenever we're inside the nightly
|
|
3462
|
+
* window, not only on the exact sleep minute — so a daemon restart or a stalled
|
|
3463
|
+
* tick across the sleep minute still puts the mind to bed on the next tick.
|
|
3464
|
+
*
|
|
3465
|
+
* Manual-wake exemption: a full wake inside the current window (human
|
|
3466
|
+
* `clock wake`, or a scheduled wake) suppresses re-sleep until the next window
|
|
3467
|
+
* begins, so `volute clock wake` at 3am sticks instead of being undone a minute
|
|
3468
|
+
* later.
|
|
3469
|
+
*/
|
|
3470
|
+
shouldSleepNow(schedule, lastWakeAt, now) {
|
|
3471
|
+
const { inWindow, windowStart } = this.inSleepWindow(schedule, now);
|
|
3472
|
+
if (!inWindow || !windowStart) return false;
|
|
3473
|
+
if (lastWakeAt && new Date(lastWakeAt) >= windowStart) return false;
|
|
3474
|
+
return true;
|
|
3475
|
+
}
|
|
3476
|
+
/**
|
|
3477
|
+
* Whether `now` falls inside the mind's nightly sleep window: the most recent
|
|
3478
|
+
* sleep-cron fire is more recent than the most recent wake-cron fire. Returns
|
|
3479
|
+
* the window start (last sleep fire) for the manual-wake exemption check.
|
|
3480
|
+
*/
|
|
3481
|
+
inSleepWindow(schedule, now) {
|
|
3281
3482
|
try {
|
|
3282
|
-
const
|
|
3283
|
-
const
|
|
3284
|
-
|
|
3285
|
-
return prevMinute === epochMinute;
|
|
3483
|
+
const prevSleep = CronExpressionParser2.parse(schedule.sleep, { currentDate: now }).prev().toDate();
|
|
3484
|
+
const prevWake = CronExpressionParser2.parse(schedule.wake, { currentDate: now }).prev().toDate();
|
|
3485
|
+
return { inWindow: prevSleep > prevWake, windowStart: prevSleep };
|
|
3286
3486
|
} catch (err) {
|
|
3287
|
-
slog3.warn(
|
|
3288
|
-
|
|
3487
|
+
slog3.warn(
|
|
3488
|
+
`invalid sleep/wake cron "${schedule.sleep}"/"${schedule.wake}"`,
|
|
3489
|
+
logger_default.errorData(err)
|
|
3490
|
+
);
|
|
3491
|
+
return { inWindow: false, windowStart: null };
|
|
3289
3492
|
}
|
|
3290
3493
|
}
|
|
3291
3494
|
async waitForIdle(name, timeoutMs) {
|
|
@@ -3477,9 +3680,10 @@ var SleepManager = class {
|
|
|
3477
3680
|
this.transitioning.add(event.mind);
|
|
3478
3681
|
sleepMind(event.mind).then(() => this.archiveSessions(event.mind)).then(() => {
|
|
3479
3682
|
state2.sleeping = true;
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3683
|
+
if (!state2.voluntaryWakeAt) {
|
|
3684
|
+
const sleepConfig = this.getSleepConfig(event.mind);
|
|
3685
|
+
state2.scheduledWakeAt = this.getNextWakeTime(sleepConfig);
|
|
3686
|
+
}
|
|
3483
3687
|
this.saveState();
|
|
3484
3688
|
slog3.info(`${event.mind} returned to sleep`);
|
|
3485
3689
|
}).catch((err) => {
|
|
@@ -3646,13 +3850,11 @@ async function checkStaleSend(mindBase, conversationId, excludeSenders) {
|
|
|
3646
3850
|
unseen: unseen.map((r) => ({ sender: r.sender, text: extractText(r.content) }))
|
|
3647
3851
|
};
|
|
3648
3852
|
}
|
|
3853
|
+
function clearMind2(mindBase) {
|
|
3854
|
+
state.delete(mindBase);
|
|
3855
|
+
}
|
|
3649
3856
|
function resetTurn(mindBase) {
|
|
3650
|
-
|
|
3651
|
-
if (!conv) return;
|
|
3652
|
-
for (const entry of conv.values()) {
|
|
3653
|
-
entry.turnOpen = false;
|
|
3654
|
-
entry.heldThisTurn = false;
|
|
3655
|
-
}
|
|
3857
|
+
clearMind2(mindBase);
|
|
3656
3858
|
}
|
|
3657
3859
|
function formatHoldNotice(channelLabel, unseen) {
|
|
3658
3860
|
const lines = unseen.map((m) => {
|
|
@@ -3962,6 +4164,7 @@ var DeliveryManager = class {
|
|
|
3962
4164
|
*/
|
|
3963
4165
|
clearMindSessions(mindName) {
|
|
3964
4166
|
this.sessionStates.delete(mindName);
|
|
4167
|
+
clearMind2(mindName);
|
|
3965
4168
|
const toDelete = [];
|
|
3966
4169
|
for (const [bufferKey, buffer] of this.batchBuffers) {
|
|
3967
4170
|
if (bufferKey.startsWith(`${mindName}:`)) {
|
|
@@ -4343,7 +4546,7 @@ var DeliveryManager = class {
|
|
|
4343
4546
|
`To start hearing this channel, add a routing rule for "${channel}" to .config/routes.json.`,
|
|
4344
4547
|
`Messages are held safely until you do \u2014 nothing is lost. If you'd rather not engage, just leave it unrouted.`
|
|
4345
4548
|
].filter((line) => line !== null).join("\n");
|
|
4346
|
-
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-
|
|
4549
|
+
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-NDAW6YP7.js");
|
|
4347
4550
|
await sendSystemMessage2(mindName, notification);
|
|
4348
4551
|
}
|
|
4349
4552
|
/**
|
|
@@ -4484,7 +4687,11 @@ var DeliveryManager = class {
|
|
|
4484
4687
|
}
|
|
4485
4688
|
}
|
|
4486
4689
|
}
|
|
4487
|
-
|
|
4690
|
+
const now = Date.now();
|
|
4691
|
+
for (const [key, entry] of avatarBlocksCache) {
|
|
4692
|
+
if (entry.expiresAt <= now) avatarBlocksCache.delete(key);
|
|
4693
|
+
}
|
|
4694
|
+
avatarBlocksCache.set(cacheKey, { blocks, expiresAt: now + AVATAR_CACHE_TTL });
|
|
4488
4695
|
return blocks;
|
|
4489
4696
|
}
|
|
4490
4697
|
incrementActive(mind, session, senders, channels) {
|
|
@@ -4517,6 +4724,9 @@ var DeliveryManager = class {
|
|
|
4517
4724
|
const buffer = this.batchBuffers.get(bufferKey);
|
|
4518
4725
|
if (buffer && buffer.messages.length > 0) {
|
|
4519
4726
|
this.scheduleBatchTimers(mind, session, bufferKey);
|
|
4727
|
+
} else if (session.startsWith("new-")) {
|
|
4728
|
+
mindSessions.delete(session);
|
|
4729
|
+
if (mindSessions.size === 0) this.sessionStates.delete(mind);
|
|
4520
4730
|
}
|
|
4521
4731
|
}
|
|
4522
4732
|
}
|
|
@@ -4657,40 +4867,78 @@ function resolveSleepAction(sleepBehavior, wokenByTrigger, wakeTriggerMatches) {
|
|
|
4657
4867
|
if (!sleepBehavior && wakeTriggerMatches) return "queue-and-wake";
|
|
4658
4868
|
return "queue";
|
|
4659
4869
|
}
|
|
4660
|
-
async function deliverMessage(mindName, payload) {
|
|
4870
|
+
async function deliverMessage(mindName, payload, opts = {}) {
|
|
4661
4871
|
try {
|
|
4662
4872
|
const baseName = await getBaseName(mindName);
|
|
4663
4873
|
const entry = await findMind(baseName);
|
|
4664
4874
|
if (!entry) {
|
|
4665
4875
|
dlog2.warn(`cannot deliver to ${mindName}: mind not found`);
|
|
4666
|
-
return;
|
|
4876
|
+
return false;
|
|
4667
4877
|
}
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
if (action === "skip") {
|
|
4679
|
-
dlog2.info(
|
|
4680
|
-
`skipped delivery to ${baseName} (sleeping, whileSleeping=skip, channel=${payload.channel})`
|
|
4878
|
+
if (!opts.isFlush) {
|
|
4879
|
+
const textContent = extractTextContent(payload.content);
|
|
4880
|
+
await recordInbound(baseName, payload.channel, payload.sender ?? null, textContent);
|
|
4881
|
+
const sleepManager = getSleepManagerIfReady();
|
|
4882
|
+
if (sleepManager?.isSleeping(baseName)) {
|
|
4883
|
+
const sleepState = sleepManager.getState(baseName);
|
|
4884
|
+
const action = resolveSleepAction(
|
|
4885
|
+
payload.whileSleeping,
|
|
4886
|
+
sleepState.wokenByTrigger,
|
|
4887
|
+
sleepManager.checkWakeTrigger(baseName, payload)
|
|
4681
4888
|
);
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4889
|
+
if (action === "skip") {
|
|
4890
|
+
dlog2.info(
|
|
4891
|
+
`skipped delivery to ${baseName} (sleeping, whileSleeping=skip, channel=${payload.channel})`
|
|
4892
|
+
);
|
|
4893
|
+
return true;
|
|
4894
|
+
}
|
|
4895
|
+
await sleepManager.queueSleepMessage(baseName, payload);
|
|
4896
|
+
if (action === "queue-and-wake") {
|
|
4897
|
+
sleepManager.initiateWake(baseName, { trigger: { channel: payload.channel } }).catch((err) => dlog2.warn(`failed to trigger-wake ${baseName}`, logger_default.errorData(err)));
|
|
4898
|
+
}
|
|
4899
|
+
return true;
|
|
4687
4900
|
}
|
|
4688
|
-
return;
|
|
4689
4901
|
}
|
|
4690
4902
|
const manager = getDeliveryManager();
|
|
4691
4903
|
await manager.routeAndDeliver(mindName, payload);
|
|
4904
|
+
return true;
|
|
4692
4905
|
} catch (err) {
|
|
4693
4906
|
dlog2.warn(`unexpected error delivering to ${mindName}`, logger_default.errorData(err));
|
|
4907
|
+
return false;
|
|
4908
|
+
}
|
|
4909
|
+
}
|
|
4910
|
+
async function deliverBatch(mindName, payloads) {
|
|
4911
|
+
if (payloads.length === 0) return true;
|
|
4912
|
+
try {
|
|
4913
|
+
const baseName = await getBaseName(mindName);
|
|
4914
|
+
const entry = await findMind(baseName);
|
|
4915
|
+
if (!entry) {
|
|
4916
|
+
dlog2.warn(`cannot deliver batch to ${mindName}: mind not found`);
|
|
4917
|
+
return false;
|
|
4918
|
+
}
|
|
4919
|
+
const channels = {};
|
|
4920
|
+
for (const p of payloads) {
|
|
4921
|
+
const ch = p.channel ?? "unknown";
|
|
4922
|
+
if (!channels[ch]) channels[ch] = [];
|
|
4923
|
+
channels[ch].push(p);
|
|
4924
|
+
}
|
|
4925
|
+
const first = payloads[0];
|
|
4926
|
+
const route = resolveRoute(getRoutingConfig(baseName), {
|
|
4927
|
+
channel: first.channel,
|
|
4928
|
+
sender: first.sender ?? void 0,
|
|
4929
|
+
isDM: first.isDM,
|
|
4930
|
+
participantCount: first.participantCount
|
|
4931
|
+
});
|
|
4932
|
+
const session = first.session ?? (route.destination === "mind" ? route.session : "main");
|
|
4933
|
+
const res = await fetch(`http://127.0.0.1:${entry.port}/message`, {
|
|
4934
|
+
method: "POST",
|
|
4935
|
+
headers: { "Content-Type": "application/json" },
|
|
4936
|
+
body: JSON.stringify({ session, batch: { channels } })
|
|
4937
|
+
});
|
|
4938
|
+
return res.ok;
|
|
4939
|
+
} catch (err) {
|
|
4940
|
+
dlog2.warn(`unexpected error delivering batch to ${mindName}`, logger_default.errorData(err));
|
|
4941
|
+
return false;
|
|
4694
4942
|
}
|
|
4695
4943
|
}
|
|
4696
4944
|
|
|
@@ -4700,6 +4948,12 @@ var dmCache = /* @__PURE__ */ new Map();
|
|
|
4700
4948
|
function resetSystemDMCache() {
|
|
4701
4949
|
dmCache.clear();
|
|
4702
4950
|
}
|
|
4951
|
+
function shouldGenerateSystemFallback(opts) {
|
|
4952
|
+
const { senderIsMind, hasMessage, convType, participants, replyTarget } = opts;
|
|
4953
|
+
if (!senderIsMind || !hasMessage) return false;
|
|
4954
|
+
if (isSpiritName(replyTarget)) return false;
|
|
4955
|
+
return convType === "dm" && participants.length === 2 && participants.some((p) => p.userType === "system");
|
|
4956
|
+
}
|
|
4703
4957
|
async function ensureSystemDM(mindName) {
|
|
4704
4958
|
const cached = dmCache.get(mindName);
|
|
4705
4959
|
if (cached) return { conversationId: cached };
|
|
@@ -4720,7 +4974,7 @@ async function ensureSystemDM(mindName) {
|
|
|
4720
4974
|
return { conversationId: conv.id };
|
|
4721
4975
|
}
|
|
4722
4976
|
async function sendSystemMessage(mindName, text, opts) {
|
|
4723
|
-
const isSpirit = mindName
|
|
4977
|
+
const isSpirit = isSpiritName(mindName);
|
|
4724
4978
|
let conversationId;
|
|
4725
4979
|
if (!isSpirit) {
|
|
4726
4980
|
const dm = await ensureSystemDM(mindName);
|
|
@@ -4740,32 +4994,29 @@ async function sendSystemMessage(mindName, text, opts) {
|
|
|
4740
4994
|
});
|
|
4741
4995
|
}
|
|
4742
4996
|
async function sendSystemMessageDirect(mindName, text) {
|
|
4997
|
+
if (isSpiritName(mindName)) {
|
|
4998
|
+
await recordInbound(mindName, "@volute", "volute", text);
|
|
4999
|
+
return {};
|
|
5000
|
+
}
|
|
4743
5001
|
const { conversationId } = await ensureSystemDM(mindName);
|
|
4744
5002
|
await addMessage(conversationId, "user", "volute", [{ type: "text", text }]);
|
|
4745
5003
|
await recordInbound(mindName, "@volute", "volute", text);
|
|
4746
5004
|
return { conversationId };
|
|
4747
5005
|
}
|
|
4748
|
-
async function
|
|
4749
|
-
const spiritEntry = await findMind(
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
channel: `@${mindName}`,
|
|
4758
|
-
conversationId,
|
|
4759
|
-
sender: mindName,
|
|
4760
|
-
isDM: true,
|
|
4761
|
-
participants: ["volute", mindName],
|
|
4762
|
-
participantCount: 2
|
|
4763
|
-
});
|
|
4764
|
-
return;
|
|
4765
|
-
} catch (err) {
|
|
4766
|
-
slog4.warn(`failed to route to spirit, falling back to aiCompleteUtility`, logger_default.errorData(err));
|
|
4767
|
-
}
|
|
5006
|
+
async function spiritWillHandle() {
|
|
5007
|
+
const spiritEntry = await findMind(SPIRIT_NAME);
|
|
5008
|
+
if (spiritEntry?.running && spiritEntry.mindType === "spirit") return true;
|
|
5009
|
+
try {
|
|
5010
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
5011
|
+
const sm = getSleepManagerIfReady2();
|
|
5012
|
+
if (sm?.isSleeping(SPIRIT_NAME)) return true;
|
|
5013
|
+
} catch (err) {
|
|
5014
|
+
slog4.debug("could not check spirit sleep state", logger_default.errorData(err));
|
|
4768
5015
|
}
|
|
5016
|
+
return false;
|
|
5017
|
+
}
|
|
5018
|
+
async function generateSystemFallbackReply(conversationId, mindName, message) {
|
|
5019
|
+
if (await spiritWillHandle()) return;
|
|
4769
5020
|
const entry = await findMind(mindName);
|
|
4770
5021
|
const dir = mindDir(mindName);
|
|
4771
5022
|
const config = readVoluteConfig(dir);
|
|
@@ -4784,7 +5035,7 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
4784
5035
|
if (config.sleep.schedule?.wake) contextParts.push(`Wake cron: ${config.sleep.schedule.wake}`);
|
|
4785
5036
|
}
|
|
4786
5037
|
try {
|
|
4787
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5038
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
4788
5039
|
const sm = getSleepManagerIfReady2();
|
|
4789
5040
|
if (sm) {
|
|
4790
5041
|
const state2 = sm.getState(mindName);
|
|
@@ -4830,10 +5081,11 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
4830
5081
|
|
|
4831
5082
|
export {
|
|
4832
5083
|
resetSystemDMCache,
|
|
5084
|
+
shouldGenerateSystemFallback,
|
|
4833
5085
|
ensureSystemDM,
|
|
4834
5086
|
sendSystemMessage,
|
|
4835
5087
|
sendSystemMessageDirect,
|
|
4836
|
-
|
|
5088
|
+
generateSystemFallbackReply,
|
|
4837
5089
|
checkHealth,
|
|
4838
5090
|
RotatingLog,
|
|
4839
5091
|
syncProviderToMinds,
|
|
@@ -4892,6 +5144,7 @@ export {
|
|
|
4892
5144
|
wakeMind,
|
|
4893
5145
|
startSpiritFull,
|
|
4894
5146
|
stopSpiritFull,
|
|
5147
|
+
buildSeedOrientation,
|
|
4895
5148
|
stopMindFull,
|
|
4896
5149
|
matchesGlob,
|
|
4897
5150
|
SleepManager,
|
|
@@ -4903,6 +5156,7 @@ export {
|
|
|
4903
5156
|
linkToolResultToTurn,
|
|
4904
5157
|
resolveSleepAction,
|
|
4905
5158
|
deliverMessage,
|
|
5159
|
+
deliverBatch,
|
|
4906
5160
|
ensureSystemChannel,
|
|
4907
5161
|
joinSystemChannel,
|
|
4908
5162
|
announceToSystem
|