volute 0.45.2 → 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-BUR2RYTJ.js → activity-events-NF6VWTJQ.js} +1 -1
- package/dist/{auth-FLAEXXZK.js → auth-CCFAU4GQ.js} +2 -2
- package/dist/{chat-76MRWWQX.js → chat-ZIQQS25U.js} +1 -1
- package/dist/chunk-4YU7HF4L.js +237 -0
- package/dist/{chunk-OP4JG2BS.js → chunk-5ZQZDDIK.js} +6 -2
- package/dist/{chunk-FR6S2KNN.js → chunk-BJZ4PV7R.js} +1 -1
- package/dist/{chunk-NYC5U6K7.js → chunk-E3DA4HYV.js} +4 -4
- package/dist/{chunk-YT7PEESJ.js → chunk-JO5IO4FH.js} +245 -124
- package/dist/{chunk-7ZUB63PS.js → chunk-KGIXX4NM.js} +1 -1
- package/dist/{chunk-EJVPTVTI.js → chunk-KQSFTPIM.js} +1 -1
- package/dist/{chunk-T6HYVHPG.js → chunk-OV7ZQ2GH.js} +1 -1
- package/dist/{clock-K43IMROX.js → chunk-PJ45FQPT.js} +21 -16
- package/dist/{chunk-UYLGEDVD.js → chunk-YXSUMLTC.js} +44 -267
- package/dist/{chunk-7C44R625.js → chunk-ZNMCCHCP.js} +47 -1
- package/dist/cli.js +11 -11
- package/dist/clock-RHS2JEDL.js +16 -0
- package/dist/{cloud-sync-3OJOPZRT.js → cloud-sync-46RS4SDA.js} +9 -8
- package/dist/{conversations-JZTD6KLR.js → conversations-CJOOCGNW.js} +2 -2
- package/dist/{daemon-restart-TIHKVVVA.js → daemon-restart-VFEI57UO.js} +1 -1
- package/dist/daemon.js +249 -170
- package/dist/{delete-BFMIGAIB.js → delete-HPLC7ETU.js} +1 -1
- package/dist/{delivery-manager-VDVKVA46.js → delivery-manager-JINZRG2W.js} +9 -8
- package/dist/{echo-text-6JJOPUD4.js → echo-text-VQPB5L2X.js} +10 -9
- package/dist/{export-GGIVO76O.js → export-LY723I3Z.js} +3 -3
- package/dist/{extensions-JONTW2HF.js → extensions-ZELQLTSR.js} +9 -8
- package/dist/{message-delivery-3C65ZI6L.js → message-delivery-R7JSQGYX.js} +11 -8
- package/dist/{mind-DWB6XYCH.js → mind-LHKS55DI.js} +9 -9
- package/dist/{mind-activity-tracker-POBLU3LC.js → mind-activity-tracker-3QRNJVR4.js} +2 -2
- package/dist/{mind-service-3F4I46CI.js → mind-service-NB2JMWQZ.js} +11 -8
- package/dist/mind-sleep-74TGOQBC.js +83 -0
- package/dist/{package-A7KXT67Q.js → package-DOD6CPO6.js} +2 -1
- package/dist/{scheduler-P6DXLLXI.js → scheduler-WVKII3LA.js} +9 -8
- package/dist/{seed-HCP5NYKH.js → seed-CNYIHPOH.js} +1 -1
- package/dist/{seed-cmd-ILOGNIFB.js → seed-cmd-AVNUNLZV.js} +2 -2
- package/dist/{seed-create-OURS2LS4.js → seed-create-ANXGYSCC.js} +3 -3
- package/dist/{seed-sprout-ELDFDAN6.js → seed-sprout-NIUFTX32.js} +5 -3
- package/dist/{send-AAIOPJAV.js → send-SPE6DVNJ.js} +6 -0
- package/dist/{setup-DQM5VPY4.js → setup-QT2A25SY.js} +1 -1
- package/dist/skills/orientation/SKILL.md +1 -1
- package/dist/skills/volute-mind/SKILL.md +8 -8
- package/dist/skills/volute-mind/references/sleep.md +8 -1
- package/dist/{skills-BKINFL7W.js → skills-AKCFA7BN.js} +1 -1
- package/dist/{sleep-manager-HCUH4PM4.js → sleep-manager-G6TXSDP7.js} +9 -8
- package/dist/{spirit-DBTIW7P4.js → spirit-RPBLLUQ5.js} +2 -2
- package/dist/{sprout-W3KC7UGK.js → sprout-SMX42ZBP.js} +1 -1
- package/dist/{system-chat-XOALE2RD.js → system-chat-NDAW6YP7.js} +9 -8
- package/dist/{turn-tracker-2W6Q2NSR.js → turn-tracker-FOIBNUUA.js} +2 -1
- package/dist/{up-FTK7CTO3.js → up-PS6PFS75.js} +1 -1
- package/dist/{version-notify-3XMAIQTE.js → version-notify-C5CH7CVP.js} +15 -10
- package/dist/web-assets/assets/{index-Bc2m8gRC.js → index-CU6XWICM.js} +3 -3
- 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/volute-server.ts +2 -2
- package/templates/claude/src/agent.ts +78 -42
- package/templates/claude/src/lib/session-reaper.ts +31 -0
- package/templates/codex/src/agent.ts +62 -40
- package/templates/pi/src/agent.ts +55 -38
- package/dist/mind-sleep-YMBR3I35.js +0 -47
- /package/dist/{chunk-QNQMSWZE.js → chunk-K6T43YBD.js} +0 -0
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
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,7 +20,7 @@ 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
26
|
resolveWakeTriggers,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
import {
|
|
34
34
|
markIdle,
|
|
35
35
|
onMindEvent
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-KQSFTPIM.js";
|
|
37
37
|
import {
|
|
38
38
|
extractTextContent,
|
|
39
39
|
getRoutingConfig,
|
|
@@ -43,11 +43,13 @@ import {
|
|
|
43
43
|
} from "./chunk-4JOQZPCE.js";
|
|
44
44
|
import {
|
|
45
45
|
clearMind,
|
|
46
|
-
getPrompt,
|
|
47
46
|
linkInboundToActiveTurn,
|
|
48
47
|
publish as publish3,
|
|
49
48
|
summarizeOrphanedTurns
|
|
50
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-YXSUMLTC.js";
|
|
50
|
+
import {
|
|
51
|
+
getPrompt
|
|
52
|
+
} from "./chunk-4YU7HF4L.js";
|
|
51
53
|
import {
|
|
52
54
|
chownMindDir,
|
|
53
55
|
exec,
|
|
@@ -70,11 +72,11 @@ import {
|
|
|
70
72
|
getUser,
|
|
71
73
|
getUserByUsername,
|
|
72
74
|
syncMindProfile
|
|
73
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-OV7ZQ2GH.js";
|
|
74
76
|
import {
|
|
75
77
|
publish,
|
|
76
78
|
subscribe
|
|
77
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-ZNMCCHCP.js";
|
|
78
80
|
import {
|
|
79
81
|
logger_default
|
|
80
82
|
} from "./chunk-T2TP6ZC6.js";
|
|
@@ -101,7 +103,7 @@ import {
|
|
|
101
103
|
} from "./chunk-ZV7MFISX.js";
|
|
102
104
|
|
|
103
105
|
// packages/daemon/src/lib/delivery/message-delivery.ts
|
|
104
|
-
import { and as and5, eq as eq6, inArray as
|
|
106
|
+
import { and as and5, eq as eq6, inArray as inArray3 } from "drizzle-orm";
|
|
105
107
|
|
|
106
108
|
// packages/daemon/src/lib/daemon/sleep-manager.ts
|
|
107
109
|
import { execFile as execFile2, spawn as spawnChild } from "child_process";
|
|
@@ -117,7 +119,7 @@ import {
|
|
|
117
119
|
import { resolve as resolve7 } from "path";
|
|
118
120
|
import { promisify as promisify2 } from "util";
|
|
119
121
|
import { CronExpressionParser as CronExpressionParser2 } from "cron-parser";
|
|
120
|
-
import { and as and2, eq as eq3 } from "drizzle-orm";
|
|
122
|
+
import { and as and2, eq as eq3, inArray } from "drizzle-orm";
|
|
121
123
|
|
|
122
124
|
// packages/daemon/src/lib/daemon/mind-manager.ts
|
|
123
125
|
import { execFile, spawn } from "child_process";
|
|
@@ -769,21 +771,8 @@ var MindManager = class {
|
|
|
769
771
|
if (isIsolationEnabled()) {
|
|
770
772
|
env.HOME = resolve2(dir, "home");
|
|
771
773
|
}
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
let baseArgs;
|
|
775
|
-
if (customNode) {
|
|
776
|
-
baseBin = customNode;
|
|
777
|
-
baseArgs = [
|
|
778
|
-
resolve2(dir, "node_modules", ".bin", "tsx"),
|
|
779
|
-
"src/server.ts",
|
|
780
|
-
"--port",
|
|
781
|
-
String(port)
|
|
782
|
-
];
|
|
783
|
-
} else {
|
|
784
|
-
baseBin = resolve2(dir, "node_modules", ".bin", "tsx");
|
|
785
|
-
baseArgs = ["src/server.ts", "--port", String(port)];
|
|
786
|
-
}
|
|
774
|
+
const baseBin = process.env.VOLUTE_NODE_PATH ?? process.execPath;
|
|
775
|
+
const baseArgs = ["--import", "tsx", "src/server.ts", "--port", String(port)];
|
|
787
776
|
let spawnCmd;
|
|
788
777
|
let spawnArgs;
|
|
789
778
|
if (isIsolationEnabled()) {
|
|
@@ -945,7 +934,7 @@ var MindManager = class {
|
|
|
945
934
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
946
935
|
mlog.error(`mind ${name} exited with code ${code}`);
|
|
947
936
|
try {
|
|
948
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
937
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
949
938
|
const sleepMgr = getSleepManagerIfReady2();
|
|
950
939
|
const sleepState = sleepMgr?.getState(name);
|
|
951
940
|
if (sleepState?.sleeping) {
|
|
@@ -975,15 +964,15 @@ var MindManager = class {
|
|
|
975
964
|
(err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
|
|
976
965
|
);
|
|
977
966
|
try {
|
|
978
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
967
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-JINZRG2W.js");
|
|
979
968
|
getDeliveryManager2().clearMindSessions(name);
|
|
980
969
|
} catch (err) {
|
|
981
970
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
982
971
|
mlog.warn(`failed to clear delivery state for ${name} after crash`, logger_default.errorData(err));
|
|
983
972
|
}
|
|
984
973
|
}
|
|
985
|
-
import("./mind-activity-tracker-
|
|
986
|
-
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(
|
|
987
976
|
({ publish: publish4 }) => publish4({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
|
|
988
977
|
).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
|
|
989
978
|
const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
|
|
@@ -1041,7 +1030,7 @@ var MindManager = class {
|
|
|
1041
1030
|
mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
|
|
1042
1031
|
}
|
|
1043
1032
|
try {
|
|
1044
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
1033
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-JINZRG2W.js");
|
|
1045
1034
|
getDeliveryManager2().clearMindSessions(name);
|
|
1046
1035
|
} catch (err) {
|
|
1047
1036
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
@@ -1049,7 +1038,7 @@ var MindManager = class {
|
|
|
1049
1038
|
}
|
|
1050
1039
|
}
|
|
1051
1040
|
try {
|
|
1052
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
1041
|
+
const { clearEchoTextCache } = await import("./echo-text-VQPB5L2X.js");
|
|
1053
1042
|
clearEchoTextCache(name);
|
|
1054
1043
|
} catch (err) {
|
|
1055
1044
|
mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
|
|
@@ -1226,6 +1215,10 @@ async function getSessionUserId(sessionId) {
|
|
|
1226
1215
|
return row.userId;
|
|
1227
1216
|
}
|
|
1228
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
|
+
}
|
|
1229
1222
|
const db = await getDb();
|
|
1230
1223
|
const cutoff = Date.now() - SESSION_MAX_AGE;
|
|
1231
1224
|
await db.delete(sessions).where(lt(sessions.createdAt, cutoff));
|
|
@@ -2306,6 +2299,7 @@ async function ensureMailAddress(mindName) {
|
|
|
2306
2299
|
import { resolve as resolve5 } from "path";
|
|
2307
2300
|
import { CronExpressionParser } from "cron-parser";
|
|
2308
2301
|
var slog2 = logger_default.child("scheduler");
|
|
2302
|
+
var CATCHUP_STALE_MINUTES = 10;
|
|
2309
2303
|
var Scheduler = class {
|
|
2310
2304
|
schedules = /* @__PURE__ */ new Map();
|
|
2311
2305
|
mindDirs = /* @__PURE__ */ new Map();
|
|
@@ -2345,6 +2339,26 @@ var Scheduler = class {
|
|
|
2345
2339
|
} else {
|
|
2346
2340
|
this.schedules.delete(mindName);
|
|
2347
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
|
+
}
|
|
2348
2362
|
}
|
|
2349
2363
|
unloadSchedules(mindName) {
|
|
2350
2364
|
this.schedules.delete(mindName);
|
|
@@ -2390,11 +2404,14 @@ var Scheduler = class {
|
|
|
2390
2404
|
return false;
|
|
2391
2405
|
}
|
|
2392
2406
|
}
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
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;
|
|
2396
2413
|
}
|
|
2397
|
-
return
|
|
2414
|
+
return true;
|
|
2398
2415
|
}
|
|
2399
2416
|
async fire(mindName, schedule) {
|
|
2400
2417
|
try {
|
|
@@ -2739,11 +2756,7 @@ async function startMindFull(name) {
|
|
|
2739
2756
|
if (entry?.parent) return;
|
|
2740
2757
|
if (!entry || entry.stage === "seed") {
|
|
2741
2758
|
if (entry?.stage === "seed") {
|
|
2742
|
-
|
|
2743
|
-
sendSystemMessage(
|
|
2744
|
-
baseName,
|
|
2745
|
-
`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}`
|
|
2746
|
-
).catch(
|
|
2759
|
+
sendSeedOrientation(baseName, entry.createdBy).catch(
|
|
2747
2760
|
(err) => logger_default.error(`failed to send seed orientation for ${baseName}`, logger_default.errorData(err))
|
|
2748
2761
|
);
|
|
2749
2762
|
if (entry.createdBy) {
|
|
@@ -2826,9 +2839,23 @@ async function stopSpiritFull(name) {
|
|
|
2826
2839
|
summary: `${name} spirit stopped`
|
|
2827
2840
|
}).catch((err) => logger_default.error("failed to publish spirit_stopped activity", logger_default.errorData(err)));
|
|
2828
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
|
+
}
|
|
2829
2856
|
async function ensureCreatorDM(mindName, creatorUsername) {
|
|
2830
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-
|
|
2831
|
-
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");
|
|
2832
2859
|
const mindUser = await getOrCreateMindUser2(mindName);
|
|
2833
2860
|
const creatorUser = await getUserByUsername2(creatorUsername);
|
|
2834
2861
|
if (!creatorUser) {
|
|
@@ -2873,7 +2900,8 @@ function defaultState() {
|
|
|
2873
2900
|
queuedMessageCount: 0,
|
|
2874
2901
|
triggerWakeHistory: [],
|
|
2875
2902
|
wakeFailures: 0,
|
|
2876
|
-
nextWakeAttemptAt: null
|
|
2903
|
+
nextWakeAttemptAt: null,
|
|
2904
|
+
lastWakeAt: null
|
|
2877
2905
|
};
|
|
2878
2906
|
}
|
|
2879
2907
|
function formatCurrentDate() {
|
|
@@ -2945,6 +2973,7 @@ var SleepManager = class {
|
|
|
2945
2973
|
state2.triggerWakeHistory ??= [];
|
|
2946
2974
|
state2.wakeFailures ??= 0;
|
|
2947
2975
|
state2.nextWakeAttemptAt ??= null;
|
|
2976
|
+
state2.lastWakeAt ??= null;
|
|
2948
2977
|
this.states.set(name, state2);
|
|
2949
2978
|
}
|
|
2950
2979
|
}
|
|
@@ -2955,7 +2984,7 @@ var SleepManager = class {
|
|
|
2955
2984
|
saveState() {
|
|
2956
2985
|
const data = {};
|
|
2957
2986
|
for (const [name, state2] of this.states) {
|
|
2958
|
-
if (state2.sleeping) data[name] = state2;
|
|
2987
|
+
if (state2.sleeping || state2.lastWakeAt) data[name] = state2;
|
|
2959
2988
|
}
|
|
2960
2989
|
try {
|
|
2961
2990
|
writeFileSync6(this.statePath, `${JSON.stringify(data, null, 2)}
|
|
@@ -3005,8 +3034,9 @@ var SleepManager = class {
|
|
|
3005
3034
|
}
|
|
3006
3035
|
await this.archiveSessions(name);
|
|
3007
3036
|
state2.sleeping = true;
|
|
3008
|
-
|
|
3009
|
-
|
|
3037
|
+
if (!state2.voluntaryWakeAt) {
|
|
3038
|
+
state2.scheduledWakeAt = this.getNextWakeTime(this.getSleepConfig(name));
|
|
3039
|
+
}
|
|
3010
3040
|
this.saveState();
|
|
3011
3041
|
slog3.info(`${name} returned to sleep after crashing during trigger wake`);
|
|
3012
3042
|
} catch (err) {
|
|
@@ -3162,6 +3192,7 @@ var SleepManager = class {
|
|
|
3162
3192
|
} catch (err) {
|
|
3163
3193
|
slog3.warn(`failed to deliver wake summary to ${name}`, logger_default.errorData(err));
|
|
3164
3194
|
}
|
|
3195
|
+
await this.waitForIdle(name, 12e4);
|
|
3165
3196
|
}
|
|
3166
3197
|
const flushed = await this.flushQueuedMessages(name);
|
|
3167
3198
|
if (flushed > 0) {
|
|
@@ -3219,14 +3250,18 @@ var SleepManager = class {
|
|
|
3219
3250
|
}
|
|
3220
3251
|
}
|
|
3221
3252
|
/**
|
|
3222
|
-
* 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.
|
|
3223
3258
|
*/
|
|
3224
3259
|
async flushQueuedMessages(name) {
|
|
3225
3260
|
try {
|
|
3226
3261
|
const db = await getDb();
|
|
3227
3262
|
const rows = await db.select().from(deliveryQueue).where(and2(eq3(deliveryQueue.mind, name), eq3(deliveryQueue.status, "sleep-queued"))).all();
|
|
3228
3263
|
if (rows.length === 0) return 0;
|
|
3229
|
-
|
|
3264
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3230
3265
|
for (const row of rows) {
|
|
3231
3266
|
let payload;
|
|
3232
3267
|
try {
|
|
@@ -3239,13 +3274,29 @@ var SleepManager = class {
|
|
|
3239
3274
|
await db.delete(deliveryQueue).where(eq3(deliveryQueue.id, row.id));
|
|
3240
3275
|
continue;
|
|
3241
3276
|
}
|
|
3242
|
-
const
|
|
3277
|
+
const channel = payload.channel ?? "unknown";
|
|
3278
|
+
if (!groups.has(channel)) groups.set(channel, []);
|
|
3279
|
+
groups.get(channel)?.push({ id: row.id, payload });
|
|
3280
|
+
}
|
|
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
|
+
);
|
|
3243
3287
|
if (!delivered) {
|
|
3244
|
-
slog3.warn(
|
|
3245
|
-
|
|
3288
|
+
slog3.warn(
|
|
3289
|
+
`failed to flush ${items.length} queued message(s) on ${channel} for ${name}; leaving them queued`
|
|
3290
|
+
);
|
|
3291
|
+
continue;
|
|
3246
3292
|
}
|
|
3247
|
-
await db.delete(deliveryQueue).where(
|
|
3248
|
-
|
|
3293
|
+
await db.delete(deliveryQueue).where(
|
|
3294
|
+
inArray(
|
|
3295
|
+
deliveryQueue.id,
|
|
3296
|
+
items.map((i) => i.id)
|
|
3297
|
+
)
|
|
3298
|
+
);
|
|
3299
|
+
deliveredCount += items.length;
|
|
3249
3300
|
}
|
|
3250
3301
|
const state2 = this.states.get(name);
|
|
3251
3302
|
if (state2) {
|
|
@@ -3258,14 +3309,13 @@ var SleepManager = class {
|
|
|
3258
3309
|
}
|
|
3259
3310
|
}
|
|
3260
3311
|
/**
|
|
3261
|
-
* Deliver one
|
|
3262
|
-
*
|
|
3263
|
-
*
|
|
3264
|
-
* Extracted as a seam so the flush loop can be unit-tested without the real delivery stack.
|
|
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.
|
|
3265
3315
|
*/
|
|
3266
|
-
async
|
|
3267
|
-
const {
|
|
3268
|
-
return
|
|
3316
|
+
async deliverQueuedBatch(name, payloads) {
|
|
3317
|
+
const { deliverBatch: deliverBatch2 } = await import("./message-delivery-R7JSQGYX.js");
|
|
3318
|
+
return deliverBatch2(name, payloads);
|
|
3269
3319
|
}
|
|
3270
3320
|
// --- Wake failure handling (#419) ---
|
|
3271
3321
|
/**
|
|
@@ -3307,7 +3357,8 @@ var SleepManager = class {
|
|
|
3307
3357
|
*/
|
|
3308
3358
|
async markWakeErrored(name, err) {
|
|
3309
3359
|
const detail = err instanceof Error ? err.message : String(err);
|
|
3310
|
-
this.
|
|
3360
|
+
this.states.delete(name);
|
|
3361
|
+
this.saveState();
|
|
3311
3362
|
slog3.error(
|
|
3312
3363
|
`${name} failed to wake ${MAX_WAKE_ATTEMPTS} times in a row; giving up and marking errored`,
|
|
3313
3364
|
logger_default.errorData(err)
|
|
@@ -3336,19 +3387,22 @@ var SleepManager = class {
|
|
|
3336
3387
|
const state2 = {
|
|
3337
3388
|
sleeping: true,
|
|
3338
3389
|
sleepingSince: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3339
|
-
|
|
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),
|
|
3340
3393
|
wokenByTrigger: false,
|
|
3341
3394
|
voluntaryWakeAt: opts?.voluntaryWakeAt ?? null,
|
|
3342
3395
|
queuedMessageCount: this.states.get(name)?.queuedMessageCount ?? 0,
|
|
3343
3396
|
triggerWakeHistory: [],
|
|
3344
3397
|
wakeFailures: 0,
|
|
3345
|
-
nextWakeAttemptAt: null
|
|
3398
|
+
nextWakeAttemptAt: null,
|
|
3399
|
+
lastWakeAt: this.states.get(name)?.lastWakeAt ?? null
|
|
3346
3400
|
};
|
|
3347
3401
|
this.states.set(name, state2);
|
|
3348
3402
|
this.saveState();
|
|
3349
3403
|
}
|
|
3350
3404
|
markAwake(name) {
|
|
3351
|
-
this.states.
|
|
3405
|
+
this.states.set(name, { ...defaultState(), lastWakeAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3352
3406
|
this.saveState();
|
|
3353
3407
|
}
|
|
3354
3408
|
getNextWakeTime(config) {
|
|
@@ -3363,55 +3417,78 @@ var SleepManager = class {
|
|
|
3363
3417
|
}
|
|
3364
3418
|
async tick() {
|
|
3365
3419
|
const now = /* @__PURE__ */ new Date();
|
|
3366
|
-
const epochMinute = Math.floor(now.getTime() / 6e4);
|
|
3367
3420
|
const mindNames = /* @__PURE__ */ new Set([...this.sleepConfigs.keys(), ...this.states.keys()]);
|
|
3368
3421
|
for (const name of mindNames) {
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
if (state2?.sleeping && state2.scheduledWakeAt) {
|
|
3389
|
-
const wakeAt = new Date(state2.scheduledWakeAt);
|
|
3390
|
-
if (now >= wakeAt) {
|
|
3391
|
-
this.initiateWake(name).catch(
|
|
3392
|
-
(err) => slog3.error(`failed scheduled wake for ${name}`, logger_default.errorData(err))
|
|
3393
|
-
);
|
|
3394
|
-
continue;
|
|
3395
|
-
}
|
|
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;
|
|
3396
3441
|
}
|
|
3397
|
-
if (
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
}
|
|
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;
|
|
3403
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
|
+
);
|
|
3404
3458
|
}
|
|
3405
3459
|
}
|
|
3406
|
-
|
|
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) {
|
|
3407
3482
|
try {
|
|
3408
|
-
const
|
|
3409
|
-
const
|
|
3410
|
-
|
|
3411
|
-
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 };
|
|
3412
3486
|
} catch (err) {
|
|
3413
|
-
slog3.warn(
|
|
3414
|
-
|
|
3487
|
+
slog3.warn(
|
|
3488
|
+
`invalid sleep/wake cron "${schedule.sleep}"/"${schedule.wake}"`,
|
|
3489
|
+
logger_default.errorData(err)
|
|
3490
|
+
);
|
|
3491
|
+
return { inWindow: false, windowStart: null };
|
|
3415
3492
|
}
|
|
3416
3493
|
}
|
|
3417
3494
|
async waitForIdle(name, timeoutMs) {
|
|
@@ -3603,9 +3680,10 @@ var SleepManager = class {
|
|
|
3603
3680
|
this.transitioning.add(event.mind);
|
|
3604
3681
|
sleepMind(event.mind).then(() => this.archiveSessions(event.mind)).then(() => {
|
|
3605
3682
|
state2.sleeping = true;
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3683
|
+
if (!state2.voluntaryWakeAt) {
|
|
3684
|
+
const sleepConfig = this.getSleepConfig(event.mind);
|
|
3685
|
+
state2.scheduledWakeAt = this.getNextWakeTime(sleepConfig);
|
|
3686
|
+
}
|
|
3609
3687
|
this.saveState();
|
|
3610
3688
|
slog3.info(`${event.mind} returned to sleep`);
|
|
3611
3689
|
}).catch((err) => {
|
|
@@ -3633,7 +3711,7 @@ function getSleepManagerIfReady() {
|
|
|
3633
3711
|
// packages/daemon/src/lib/delivery/delivery-manager.ts
|
|
3634
3712
|
import { readFile, realpath } from "fs/promises";
|
|
3635
3713
|
import { extname, resolve as resolve8 } from "path";
|
|
3636
|
-
import { and as and4, eq as eq5, inArray, sql as sql2 } from "drizzle-orm";
|
|
3714
|
+
import { and as and4, eq as eq5, inArray as inArray2, sql as sql2 } from "drizzle-orm";
|
|
3637
3715
|
|
|
3638
3716
|
// packages/daemon/src/lib/chat/typing.ts
|
|
3639
3717
|
var DEFAULT_TTL_MS = 1e4;
|
|
@@ -3772,13 +3850,11 @@ async function checkStaleSend(mindBase, conversationId, excludeSenders) {
|
|
|
3772
3850
|
unseen: unseen.map((r) => ({ sender: r.sender, text: extractText(r.content) }))
|
|
3773
3851
|
};
|
|
3774
3852
|
}
|
|
3853
|
+
function clearMind2(mindBase) {
|
|
3854
|
+
state.delete(mindBase);
|
|
3855
|
+
}
|
|
3775
3856
|
function resetTurn(mindBase) {
|
|
3776
|
-
|
|
3777
|
-
if (!conv) return;
|
|
3778
|
-
for (const entry of conv.values()) {
|
|
3779
|
-
entry.turnOpen = false;
|
|
3780
|
-
entry.heldThisTurn = false;
|
|
3781
|
-
}
|
|
3857
|
+
clearMind2(mindBase);
|
|
3782
3858
|
}
|
|
3783
3859
|
function formatHoldNotice(channelLabel, unseen) {
|
|
3784
3860
|
const lines = unseen.map((m) => {
|
|
@@ -4030,7 +4106,7 @@ var DeliveryManager = class {
|
|
|
4030
4106
|
if (promote.length === 0) return;
|
|
4031
4107
|
try {
|
|
4032
4108
|
const db = await getDb();
|
|
4033
|
-
await db.update(deliveryQueue).set({ status: "pending", attempts: 0, next_attempt_at: null }).where(
|
|
4109
|
+
await db.update(deliveryQueue).set({ status: "pending", attempts: 0, next_attempt_at: null }).where(inArray2(deliveryQueue.id, promote));
|
|
4034
4110
|
dlog.info(`released ${promote.length} gated message(s) for ${baseName} after route change`);
|
|
4035
4111
|
} catch (err) {
|
|
4036
4112
|
dlog.warn(`failed to promote gated rows for ${baseName}`, logger_default.errorData(err));
|
|
@@ -4088,6 +4164,7 @@ var DeliveryManager = class {
|
|
|
4088
4164
|
*/
|
|
4089
4165
|
clearMindSessions(mindName) {
|
|
4090
4166
|
this.sessionStates.delete(mindName);
|
|
4167
|
+
clearMind2(mindName);
|
|
4091
4168
|
const toDelete = [];
|
|
4092
4169
|
for (const [bufferKey, buffer] of this.batchBuffers) {
|
|
4093
4170
|
if (bufferKey.startsWith(`${mindName}:`)) {
|
|
@@ -4195,7 +4272,7 @@ var DeliveryManager = class {
|
|
|
4195
4272
|
if (valid.length === 0) return;
|
|
4196
4273
|
try {
|
|
4197
4274
|
const db = await getDb();
|
|
4198
|
-
await db.delete(deliveryQueue).where(
|
|
4275
|
+
await db.delete(deliveryQueue).where(inArray2(deliveryQueue.id, valid));
|
|
4199
4276
|
} catch (err) {
|
|
4200
4277
|
dlog.warn("failed to delete delivered delivery queue rows", logger_default.errorData(err));
|
|
4201
4278
|
}
|
|
@@ -4206,7 +4283,7 @@ var DeliveryManager = class {
|
|
|
4206
4283
|
if (valid.length === 0) return;
|
|
4207
4284
|
try {
|
|
4208
4285
|
const db = await getDb();
|
|
4209
|
-
const rows = await db.select({ id: deliveryQueue.id, attempts: deliveryQueue.attempts }).from(deliveryQueue).where(
|
|
4286
|
+
const rows = await db.select({ id: deliveryQueue.id, attempts: deliveryQueue.attempts }).from(deliveryQueue).where(inArray2(deliveryQueue.id, valid));
|
|
4210
4287
|
for (const row of rows) {
|
|
4211
4288
|
const attempts = row.attempts + 1;
|
|
4212
4289
|
const backoffSec = Math.round(
|
|
@@ -4469,7 +4546,7 @@ var DeliveryManager = class {
|
|
|
4469
4546
|
`To start hearing this channel, add a routing rule for "${channel}" to .config/routes.json.`,
|
|
4470
4547
|
`Messages are held safely until you do \u2014 nothing is lost. If you'd rather not engage, just leave it unrouted.`
|
|
4471
4548
|
].filter((line) => line !== null).join("\n");
|
|
4472
|
-
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-
|
|
4549
|
+
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-NDAW6YP7.js");
|
|
4473
4550
|
await sendSystemMessage2(mindName, notification);
|
|
4474
4551
|
}
|
|
4475
4552
|
/**
|
|
@@ -4610,7 +4687,11 @@ var DeliveryManager = class {
|
|
|
4610
4687
|
}
|
|
4611
4688
|
}
|
|
4612
4689
|
}
|
|
4613
|
-
|
|
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 });
|
|
4614
4695
|
return blocks;
|
|
4615
4696
|
}
|
|
4616
4697
|
incrementActive(mind, session, senders, channels) {
|
|
@@ -4761,8 +4842,8 @@ async function linkToolResultToTurn(mind, turnId, toolResultContent, toolUseEven
|
|
|
4761
4842
|
await db.update(activity).set({
|
|
4762
4843
|
turn_id: turnId,
|
|
4763
4844
|
...toolUseEventId != null ? { source_event_id: toolUseEventId } : {}
|
|
4764
|
-
}).where(
|
|
4765
|
-
const actRows = await db.select().from(activity).where(
|
|
4845
|
+
}).where(inArray3(activity.id, activityIds));
|
|
4846
|
+
const actRows = await db.select().from(activity).where(inArray3(activity.id, activityIds));
|
|
4766
4847
|
if (actRows.length > 0) {
|
|
4767
4848
|
await db.insert(mindHistory).values(
|
|
4768
4849
|
actRows.map((a) => ({
|
|
@@ -4826,6 +4907,40 @@ async function deliverMessage(mindName, payload, opts = {}) {
|
|
|
4826
4907
|
return false;
|
|
4827
4908
|
}
|
|
4828
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;
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4829
4944
|
|
|
4830
4945
|
// packages/daemon/src/lib/chat/system-chat.ts
|
|
4831
4946
|
var slog4 = logger_default.child("system-chat");
|
|
@@ -4879,6 +4994,10 @@ async function sendSystemMessage(mindName, text, opts) {
|
|
|
4879
4994
|
});
|
|
4880
4995
|
}
|
|
4881
4996
|
async function sendSystemMessageDirect(mindName, text) {
|
|
4997
|
+
if (isSpiritName(mindName)) {
|
|
4998
|
+
await recordInbound(mindName, "@volute", "volute", text);
|
|
4999
|
+
return {};
|
|
5000
|
+
}
|
|
4882
5001
|
const { conversationId } = await ensureSystemDM(mindName);
|
|
4883
5002
|
await addMessage(conversationId, "user", "volute", [{ type: "text", text }]);
|
|
4884
5003
|
await recordInbound(mindName, "@volute", "volute", text);
|
|
@@ -4888,7 +5007,7 @@ async function spiritWillHandle() {
|
|
|
4888
5007
|
const spiritEntry = await findMind(SPIRIT_NAME);
|
|
4889
5008
|
if (spiritEntry?.running && spiritEntry.mindType === "spirit") return true;
|
|
4890
5009
|
try {
|
|
4891
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5010
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
4892
5011
|
const sm = getSleepManagerIfReady2();
|
|
4893
5012
|
if (sm?.isSleeping(SPIRIT_NAME)) return true;
|
|
4894
5013
|
} catch (err) {
|
|
@@ -4916,7 +5035,7 @@ async function generateSystemFallbackReply(conversationId, mindName, message) {
|
|
|
4916
5035
|
if (config.sleep.schedule?.wake) contextParts.push(`Wake cron: ${config.sleep.schedule.wake}`);
|
|
4917
5036
|
}
|
|
4918
5037
|
try {
|
|
4919
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5038
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
4920
5039
|
const sm = getSleepManagerIfReady2();
|
|
4921
5040
|
if (sm) {
|
|
4922
5041
|
const state2 = sm.getState(mindName);
|
|
@@ -5025,6 +5144,7 @@ export {
|
|
|
5025
5144
|
wakeMind,
|
|
5026
5145
|
startSpiritFull,
|
|
5027
5146
|
stopSpiritFull,
|
|
5147
|
+
buildSeedOrientation,
|
|
5028
5148
|
stopMindFull,
|
|
5029
5149
|
matchesGlob,
|
|
5030
5150
|
SleepManager,
|
|
@@ -5036,6 +5156,7 @@ export {
|
|
|
5036
5156
|
linkToolResultToTurn,
|
|
5037
5157
|
resolveSleepAction,
|
|
5038
5158
|
deliverMessage,
|
|
5159
|
+
deliverBatch,
|
|
5039
5160
|
ensureSystemChannel,
|
|
5040
5161
|
joinSystemChannel,
|
|
5041
5162
|
announceToSystem
|