volute 0.38.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{activity-events-N6HCHU4P.js → activity-events-OJZ7G5CG.js} +3 -3
- package/dist/{ai-service-C2YNARGH.js → ai-service-JMV4KZN4.js} +8 -4
- package/dist/{archive-AWIJTVQV.js → archive-BC2MTNPV.js} +3 -3
- package/dist/{auth-2QOOPMBX.js → auth-2IMZ6N5G.js} +4 -4
- package/dist/{chat-XL7CVWVJ.js → chat-O5QCGCUG.js} +2 -2
- package/dist/{chunk-A6FLW5XD.js → chunk-2AQCEQYN.js} +1 -1
- package/dist/{chunk-3F7XK5Q7.js → chunk-3XSFMN3H.js} +1 -1
- package/dist/{chunk-NGXQ2ESO.js → chunk-43PVCF4V.js} +3 -3
- package/dist/{chunk-N42QMDID.js → chunk-5VBXKFPO.js} +2 -2
- package/dist/{chunk-BIEWHAAM.js → chunk-76Q6YLJL.js} +1 -1
- package/dist/{chunk-5DPRTREW.js → chunk-B7YJUWMM.js} +3 -3
- package/dist/{chunk-XLBQYIHH.js → chunk-DNQPHLGM.js} +1 -1
- package/dist/{chunk-SNW2NPP4.js → chunk-FBV22PFY.js} +1 -1
- package/dist/{chunk-WC635OPK.js → chunk-FRJ73IWR.js} +1 -1
- package/dist/{chunk-PMMHVSCR.js → chunk-HAJHDGBV.js} +1 -1
- package/dist/{chunk-SZJWC2GA.js → chunk-HEZM5GCV.js} +2 -2
- package/dist/{chunk-PLL2FM7A.js → chunk-JSITYTIL.js} +6 -6
- package/dist/{chunk-CJ26DXZL.js → chunk-MHFAWJCC.js} +16 -0
- package/dist/{chunk-7AZQFSOV.js → chunk-PC55DGNX.js} +1 -1
- package/dist/{chunk-2NHRJ3YO.js → chunk-PDSA3ILI.js} +3 -3
- package/dist/{chunk-ORNY3MZR.js → chunk-PGX52NFO.js} +15 -1
- package/dist/{chunk-UIM5NHPP.js → chunk-PHR4XQPL.js} +3 -3
- package/dist/chunk-PIRL2HAO.js +124 -0
- package/dist/{chunk-JOJT34M2.js → chunk-PZTVQL3J.js} +2 -2
- package/dist/{chunk-GVVVMZ4J.js → chunk-RMZSICLS.js} +1 -1
- package/dist/{chunk-LQ6Z4FXN.js → chunk-S4YO5OWV.js} +1 -1
- package/dist/{chunk-TNZ5XQA4.js → chunk-SGC6CDXB.js} +2 -2
- package/dist/{chunk-LN67VPZJ.js → chunk-SOPMVTTW.js} +2 -2
- package/dist/{chunk-VUM2K6EN.js → chunk-T26YLQPU.js} +5 -5
- package/dist/{chunk-2TR2OPVA.js → chunk-WGRMLXTH.js} +8 -8
- package/dist/{chunk-TEZRVSSB.js → chunk-XUJR6NZW.js} +513 -335
- package/dist/cli.js +15 -15
- package/dist/{cloud-sync-PEM3IVI4.js → cloud-sync-5JF2Z7VM.js} +16 -16
- package/dist/{conversations-G6YRSABR.js → conversations-LUVHGQSS.js} +4 -4
- package/dist/{create-PN73742N.js → create-TDQM3SHV.js} +1 -1
- package/dist/{daemon-restart-6QONRBTK.js → daemon-restart-XHDJDOPQ.js} +8 -8
- package/dist/daemon.js +221 -118
- package/dist/{db-CBOCDYVA.js → db-2GIS7HSG.js} +2 -2
- package/dist/delivery-manager-L5WFP2ND.js +30 -0
- package/dist/{delivery-router-QTFEZ26O.js → delivery-router-IU4OMVKZ.js} +3 -3
- package/dist/down-KIJVZ3OC.js +17 -0
- package/dist/echo-text-TJWCUBPB.js +31 -0
- package/dist/{exec-ONYZEA5B.js → exec-BGMU7VXR.js} +4 -4
- package/dist/{export-JPDBQESV.js → export-RKUWSQSG.js} +3 -3
- package/dist/{extensions-P477XR46.js → extensions-VN5SEVRP.js} +16 -16
- package/dist/{files-LAQ3NXQK.js → files-J2CKZ6PK.js} +3 -3
- package/dist/{import-EROF27RH.js → import-KVY6BYXG.js} +6 -6
- package/dist/{isolation-G5J3MTKU.js → isolation-MM7YHK6J.js} +3 -3
- package/dist/{login-F6YMAVLE.js → login-OHM32QVD.js} +2 -2
- package/dist/{logout-HHPH52KZ.js → logout-74WBDMOE.js} +2 -2
- package/dist/{message-delivery-54IXVE5H.js → message-delivery-VBBAJX3J.js} +16 -16
- package/dist/{mind-G5HDKBKL.js → mind-PUPXDIYQ.js} +5 -5
- package/dist/{mind-activity-tracker-42ENM32S.js → mind-activity-tracker-7GE24YZE.js} +4 -4
- package/dist/mind-manager-4Q6KGZHL.js +30 -0
- package/dist/mind-service-7OD4PRO5.js +36 -0
- package/dist/{package-LYQHSDPG.js → package-6YPVWGD3.js} +1 -1
- package/dist/{registry-KMELPC3X.js → registry-L272BZJR.js} +2 -2
- package/dist/{sandbox-PQYEICEF.js → sandbox-BQAFYHLE.js} +6 -6
- package/dist/scheduler-7E22Q7IC.js +30 -0
- package/dist/{schema-K575EBPE.js → schema-HHYJDG7B.js} +3 -1
- package/dist/{seed-3KH4ZRJL.js → seed-4V2O7FQE.js} +1 -1
- package/dist/{seed-cmd-6A76SSAA.js → seed-cmd-JKDEW4MP.js} +2 -2
- package/dist/{seed-create-XXRRGZCO.js → seed-create-EPAO3H5F.js} +3 -3
- package/dist/{seed-sprout-HLMUH2AI.js → seed-sprout-EONCL4DU.js} +8 -8
- package/dist/{send-7CIP5GLS.js → send-232RAHPX.js} +3 -3
- package/dist/{service-RH6Q2XWR.js → service-AG5BKRUV.js} +5 -5
- package/dist/{service-install-AIQBLE6P.js → service-install-HVAEUBAH.js} +6 -6
- package/dist/{setup-PF7JSFMO.js → setup-IL7QJZN4.js} +3 -3
- package/dist/{setup-NPAZXBIJ.js → setup-PSA7LJ5U.js} +8 -8
- package/dist/{skills-NVOWS5NY.js → skills-SLEY5X6W.js} +6 -6
- package/dist/sleep-manager-27S7MKJO.js +34 -0
- package/dist/{spirit-ZFYG4JOH.js → spirit-7BUHSEO2.js} +8 -8
- package/dist/{sprout-FLBCETYG.js → sprout-JWQ57KF3.js} +1 -1
- package/dist/{status-TGOKAFW3.js → status-6GSOSYOD.js} +6 -6
- package/dist/system-chat-EIINIHFB.js +34 -0
- package/dist/{tailscale-LTYNKIPZ.js → tailscale-6MHNLBAX.js} +2 -2
- package/dist/up-4XS3RCPP.js +19 -0
- package/dist/{update-ZCTQ6UGY.js → update-BT2PY73H.js} +6 -6
- package/dist/{update-check-IKS7SGK5.js → update-check-Y3HBEMF2.js} +3 -3
- package/dist/{version-notify-764EXVQQ.js → version-notify-NFQMI5IY.js} +17 -17
- package/dist/web-assets/assets/index-DqHSW9Fr.css +1 -0
- package/dist/web-assets/assets/index-Jluza4c-.js +73 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0009_mind_notices.sql +14 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/templates/_base/.init/.local/hooks/pre-prompt/notices.ts +46 -0
- package/templates/_base/src/lib/context-breakdown.ts +281 -126
- package/templates/_base/src/lib/daemon-client.ts +5 -2
- package/templates/_base/src/lib/transparency.ts +5 -3
- package/templates/_base/src/lib/volute-server.ts +23 -0
- package/templates/claude/src/agent.ts +54 -16
- package/templates/claude/src/server.ts +1 -0
- package/templates/codex/src/agent.ts +51 -12
- package/templates/codex/src/server.ts +1 -0
- package/templates/pi/src/agent.ts +72 -13
- package/templates/pi/src/server.ts +1 -0
- package/dist/chunk-KXXJYY62.js +0 -112
- package/dist/delivery-manager-IMCRV6A3.js +0 -30
- package/dist/down-5XZWH4ZP.js +0 -17
- package/dist/echo-text-ADKLNTIA.js +0 -31
- package/dist/mind-manager-5G6P2EAT.js +0 -30
- package/dist/mind-service-FDATHL6X.js +0 -36
- package/dist/scheduler-ZRUZNQI5.js +0 -30
- package/dist/sleep-manager-VPPQXBPH.js +0 -34
- package/dist/system-chat-YUIT7RA6.js +0 -34
- package/dist/up-YRZAEZ3Z.js +0 -19
- package/dist/web-assets/assets/index-BI7hUTBr.js +0 -73
- package/dist/web-assets/assets/index-BJifO9h_.css +0 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadMergedEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HAJHDGBV.js";
|
|
5
5
|
import {
|
|
6
6
|
spiritDir
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-WGRMLXTH.js";
|
|
8
8
|
import {
|
|
9
9
|
hashSkillDir,
|
|
10
10
|
importSkillFromDir,
|
|
11
11
|
removeSharedSkill,
|
|
12
12
|
sharedSkillsDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-T26YLQPU.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-B7YJUWMM.js";
|
|
24
24
|
import {
|
|
25
25
|
readVoluteConfig,
|
|
26
26
|
writeVoluteConfig
|
|
@@ -28,45 +28,46 @@ import {
|
|
|
28
28
|
import {
|
|
29
29
|
isSandboxEnabled,
|
|
30
30
|
wrapForSandbox
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-PIRL2HAO.js";
|
|
32
|
+
import {
|
|
33
|
+
markIdle,
|
|
34
|
+
onMindEvent
|
|
35
|
+
} from "./chunk-RMZSICLS.js";
|
|
32
36
|
import {
|
|
33
37
|
extractTextContent,
|
|
34
38
|
getRoutingConfig,
|
|
35
39
|
resolveDeliveryMode,
|
|
36
40
|
resolveRoute
|
|
37
|
-
} from "./chunk-
|
|
38
|
-
import {
|
|
39
|
-
markIdle
|
|
40
|
-
} from "./chunk-GVVVMZ4J.js";
|
|
41
|
+
} from "./chunk-PC55DGNX.js";
|
|
41
42
|
import {
|
|
42
43
|
exec
|
|
43
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-3XSFMN3H.js";
|
|
44
45
|
import {
|
|
45
46
|
chownMindDir,
|
|
46
47
|
isIsolationEnabled,
|
|
47
48
|
mindUserName,
|
|
48
49
|
wrapForIsolation
|
|
49
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-FBV22PFY.js";
|
|
51
|
+
import {
|
|
52
|
+
aiCompleteUtility,
|
|
53
|
+
getAiConfig,
|
|
54
|
+
resolveApiKey
|
|
55
|
+
} from "./chunk-PGX52NFO.js";
|
|
56
|
+
import {
|
|
57
|
+
readGlobalConfig,
|
|
58
|
+
writeGlobalConfig
|
|
59
|
+
} from "./chunk-2AQCEQYN.js";
|
|
50
60
|
import {
|
|
51
61
|
getOrCreateMindUser,
|
|
52
62
|
getOrCreateSystemUser,
|
|
53
63
|
getUser,
|
|
54
64
|
getUserByUsername,
|
|
55
65
|
syncMindProfile
|
|
56
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-PHR4XQPL.js";
|
|
57
67
|
import {
|
|
58
68
|
publish,
|
|
59
69
|
subscribe
|
|
60
|
-
} from "./chunk-
|
|
61
|
-
import {
|
|
62
|
-
aiCompleteUtility,
|
|
63
|
-
getAiConfig,
|
|
64
|
-
resolveApiKey
|
|
65
|
-
} from "./chunk-ORNY3MZR.js";
|
|
66
|
-
import {
|
|
67
|
-
readGlobalConfig,
|
|
68
|
-
writeGlobalConfig
|
|
69
|
-
} from "./chunk-A6FLW5XD.js";
|
|
70
|
+
} from "./chunk-5VBXKFPO.js";
|
|
70
71
|
import {
|
|
71
72
|
logger_default
|
|
72
73
|
} from "./chunk-T2TP6ZC6.js";
|
|
@@ -79,37 +80,38 @@ import {
|
|
|
79
80
|
stateDir,
|
|
80
81
|
voluteHome,
|
|
81
82
|
voluteSystemDir
|
|
82
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-PDSA3ILI.js";
|
|
83
84
|
import {
|
|
84
85
|
activity,
|
|
85
86
|
conversationParticipants,
|
|
86
87
|
deliveryQueue,
|
|
87
88
|
messages,
|
|
88
89
|
mindHistory,
|
|
90
|
+
mindNotices,
|
|
89
91
|
summaries,
|
|
90
92
|
systemPrompts,
|
|
91
93
|
turns,
|
|
92
94
|
users
|
|
93
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-MHFAWJCC.js";
|
|
94
96
|
|
|
95
97
|
// packages/daemon/src/lib/delivery/message-delivery.ts
|
|
96
|
-
import { and as
|
|
98
|
+
import { and as and5, desc as desc2, eq as eq7, inArray as inArray3, sql as sql4 } from "drizzle-orm";
|
|
97
99
|
|
|
98
100
|
// packages/daemon/src/lib/daemon/sleep-manager.ts
|
|
99
101
|
import { execFile as execFile2, spawn as spawnChild } from "child_process";
|
|
100
102
|
import {
|
|
101
|
-
existsSync as
|
|
102
|
-
mkdirSync as
|
|
103
|
+
existsSync as existsSync8,
|
|
104
|
+
mkdirSync as mkdirSync5,
|
|
103
105
|
readdirSync as readdirSync2,
|
|
104
|
-
readFileSync as
|
|
106
|
+
readFileSync as readFileSync7,
|
|
105
107
|
readlinkSync,
|
|
106
108
|
renameSync,
|
|
107
|
-
writeFileSync as
|
|
109
|
+
writeFileSync as writeFileSync6
|
|
108
110
|
} from "fs";
|
|
109
|
-
import { resolve as
|
|
111
|
+
import { resolve as resolve7 } from "path";
|
|
110
112
|
import { promisify as promisify2 } from "util";
|
|
111
113
|
import { CronExpressionParser as CronExpressionParser2 } from "cron-parser";
|
|
112
|
-
import { and as
|
|
114
|
+
import { and as and3, eq as eq5, inArray as inArray2 } from "drizzle-orm";
|
|
113
115
|
|
|
114
116
|
// packages/daemon/src/lib/prompts.ts
|
|
115
117
|
import { eq } from "drizzle-orm";
|
|
@@ -325,8 +327,8 @@ async function getMindPromptDefaults() {
|
|
|
325
327
|
|
|
326
328
|
// packages/daemon/src/lib/daemon/mind-manager.ts
|
|
327
329
|
import { execFile, spawn } from "child_process";
|
|
328
|
-
import { existsSync as
|
|
329
|
-
import { resolve } from "path";
|
|
330
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
331
|
+
import { resolve as resolve2 } from "path";
|
|
330
332
|
import { promisify } from "util";
|
|
331
333
|
|
|
332
334
|
// packages/daemon/src/lib/util/json-state.ts
|
|
@@ -435,6 +437,78 @@ var RotatingLog = class extends Writable {
|
|
|
435
437
|
}
|
|
436
438
|
};
|
|
437
439
|
|
|
440
|
+
// packages/daemon/src/lib/daemon/credential-sync.ts
|
|
441
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
442
|
+
import { resolve } from "path";
|
|
443
|
+
var slog = logger_default.child("cred-sync");
|
|
444
|
+
function writeClaudeCredentials(homeDir, baseName, oauth) {
|
|
445
|
+
const claudeDir = resolve(homeDir, ".claude");
|
|
446
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
447
|
+
writeFileSync2(
|
|
448
|
+
resolve(claudeDir, ".credentials.json"),
|
|
449
|
+
JSON.stringify({
|
|
450
|
+
claudeAiOauth: {
|
|
451
|
+
accessToken: oauth.access,
|
|
452
|
+
refreshToken: oauth.refresh,
|
|
453
|
+
expiresAt: oauth.expires ? new Date(oauth.expires).toISOString() : null,
|
|
454
|
+
scopes: ["user:inference", "user:profile"]
|
|
455
|
+
}
|
|
456
|
+
}),
|
|
457
|
+
{ mode: 384 }
|
|
458
|
+
);
|
|
459
|
+
if (isIsolationEnabled()) {
|
|
460
|
+
chownMindDir(claudeDir, baseName);
|
|
461
|
+
}
|
|
462
|
+
return claudeDir;
|
|
463
|
+
}
|
|
464
|
+
function writePiProviderKey(piAgentDir, baseName, provider, key2) {
|
|
465
|
+
mkdirSync(piAgentDir, { recursive: true });
|
|
466
|
+
const authPath = resolve(piAgentDir, "auth.json");
|
|
467
|
+
const authData = existsSync3(authPath) ? JSON.parse(readFileSync2(authPath, "utf-8")) : {};
|
|
468
|
+
authData[provider] = { type: "api_key", key: key2 };
|
|
469
|
+
writeFileSync2(authPath, JSON.stringify(authData, null, 2), { mode: 384 });
|
|
470
|
+
if (isIsolationEnabled()) {
|
|
471
|
+
chownMindDir(piAgentDir, baseName);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function piUsesProvider(piAgentDir, provider) {
|
|
475
|
+
try {
|
|
476
|
+
const authPath = resolve(piAgentDir, "auth.json");
|
|
477
|
+
if (!existsSync3(authPath)) return false;
|
|
478
|
+
const data = JSON.parse(readFileSync2(authPath, "utf-8"));
|
|
479
|
+
return data[provider] != null;
|
|
480
|
+
} catch {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async function syncProviderToMinds(provider, deps = {}) {
|
|
485
|
+
if (provider !== "anthropic") return;
|
|
486
|
+
const getOauth = deps.getOauth ?? (() => getAiConfig()?.providers.anthropic?.oauth);
|
|
487
|
+
const oauth = getOauth();
|
|
488
|
+
if (!oauth?.access) return;
|
|
489
|
+
const listRunning = deps.listRunning ?? (() => []);
|
|
490
|
+
const lookup = deps.lookup ?? findMind;
|
|
491
|
+
for (const name of listRunning()) {
|
|
492
|
+
try {
|
|
493
|
+
const entry = await lookup(name);
|
|
494
|
+
if (!entry) continue;
|
|
495
|
+
const dir = entry.dir ?? mindDir(name);
|
|
496
|
+
const baseName = entry.parent ?? name;
|
|
497
|
+
const template = entry.template;
|
|
498
|
+
if (template === "claude" || !template) {
|
|
499
|
+
writeClaudeCredentials(resolve(dir, "home"), baseName, oauth);
|
|
500
|
+
} else if (template === "pi") {
|
|
501
|
+
const piAgentDir = resolve(dir, ".mind", "pi-agent");
|
|
502
|
+
if (piUsesProvider(piAgentDir, "anthropic")) {
|
|
503
|
+
writePiProviderKey(piAgentDir, baseName, "anthropic", oauth.access);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
} catch (err) {
|
|
507
|
+
slog.warn(`failed to sync ${provider} credentials to mind ${name}`, logger_default.errorData(err));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
438
512
|
// packages/daemon/src/lib/daemon/mind-tokens.ts
|
|
439
513
|
import { randomUUID } from "crypto";
|
|
440
514
|
var tokenToMind = /* @__PURE__ */ new Map();
|
|
@@ -457,6 +531,74 @@ function resolveMindToken(token) {
|
|
|
457
531
|
return tokenToMind.get(token) ?? null;
|
|
458
532
|
}
|
|
459
533
|
|
|
534
|
+
// packages/daemon/src/lib/daemon/notices.ts
|
|
535
|
+
import { and, asc, eq as eq2, lte, sql } from "drizzle-orm";
|
|
536
|
+
var nlog = logger_default.child("notices");
|
|
537
|
+
var MAX_NOTICES_PER_SESSION = 100;
|
|
538
|
+
async function recordNotice(input) {
|
|
539
|
+
try {
|
|
540
|
+
const db = await getDb();
|
|
541
|
+
await db.insert(mindNotices).values({
|
|
542
|
+
mind: input.mind,
|
|
543
|
+
session: input.session,
|
|
544
|
+
kind: input.kind,
|
|
545
|
+
reason: input.reason,
|
|
546
|
+
detail: input.detail,
|
|
547
|
+
raw: input.raw ?? null
|
|
548
|
+
});
|
|
549
|
+
await db.run(
|
|
550
|
+
sql`DELETE FROM mind_notices WHERE mind = ${input.mind} AND session = ${input.session} AND id NOT IN (SELECT id FROM mind_notices WHERE mind = ${input.mind} AND session = ${input.session} ORDER BY id DESC LIMIT ${MAX_NOTICES_PER_SESSION})`
|
|
551
|
+
);
|
|
552
|
+
} catch (err) {
|
|
553
|
+
nlog.error(`failed to record notice for ${input.mind}:${input.session}`, logger_default.errorData(err));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function drainNotices(mind, session, limit = MAX_NOTICES_PER_SESSION) {
|
|
557
|
+
const db = await getDb();
|
|
558
|
+
return db.select().from(mindNotices).where(and(eq2(mindNotices.mind, mind), eq2(mindNotices.session, session))).orderBy(asc(mindNotices.id)).limit(limit);
|
|
559
|
+
}
|
|
560
|
+
async function clearDeliveredNotices(mind, session, uptoId) {
|
|
561
|
+
try {
|
|
562
|
+
const db = await getDb();
|
|
563
|
+
await db.delete(mindNotices).where(
|
|
564
|
+
and(
|
|
565
|
+
eq2(mindNotices.mind, mind),
|
|
566
|
+
eq2(mindNotices.session, session),
|
|
567
|
+
lte(mindNotices.id, uptoId)
|
|
568
|
+
)
|
|
569
|
+
);
|
|
570
|
+
} catch (err) {
|
|
571
|
+
nlog.warn(`failed to clear delivered notices for ${mind}:${session}`, logger_default.errorData(err));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
var NOTICE_HEADER = "[Notices] While you were unavailable, one or more turns failed. You're back now:";
|
|
575
|
+
function formatNotices(notices) {
|
|
576
|
+
if (notices.length === 0) return null;
|
|
577
|
+
const groups = /* @__PURE__ */ new Map();
|
|
578
|
+
for (const n of notices) {
|
|
579
|
+
const time = localHM(n.created_at);
|
|
580
|
+
const g = groups.get(n.reason);
|
|
581
|
+
if (g) {
|
|
582
|
+
g.count += 1;
|
|
583
|
+
g.detail = n.detail;
|
|
584
|
+
g.last = time;
|
|
585
|
+
} else {
|
|
586
|
+
groups.set(n.reason, { count: 1, detail: n.detail, first: time, last: time });
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const lines = [...groups.values()].map((g) => {
|
|
590
|
+
const span = g.first === g.last ? g.first : `${g.first}\u2013${g.last}`;
|
|
591
|
+
const plural = g.count === 1 ? "turn" : "turns";
|
|
592
|
+
return `- ${g.count} ${plural} failed (${span}): ${g.detail}`;
|
|
593
|
+
});
|
|
594
|
+
return `${NOTICE_HEADER}
|
|
595
|
+
${lines.join("\n")}`;
|
|
596
|
+
}
|
|
597
|
+
function localHM(createdAt) {
|
|
598
|
+
const iso = createdAt.endsWith("Z") ? createdAt : `${createdAt.replace(" ", "T")}Z`;
|
|
599
|
+
return new Date(iso).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
|
|
600
|
+
}
|
|
601
|
+
|
|
460
602
|
// packages/daemon/src/lib/daemon/restart-tracker.ts
|
|
461
603
|
var DEFAULT_MAX_ATTEMPTS = 5;
|
|
462
604
|
var DEFAULT_BASE_DELAY = 3e3;
|
|
@@ -504,10 +646,10 @@ var RestartTracker = class {
|
|
|
504
646
|
|
|
505
647
|
// packages/daemon/src/lib/daemon/turn-tracker.ts
|
|
506
648
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
507
|
-
import { eq as
|
|
649
|
+
import { eq as eq4 } from "drizzle-orm";
|
|
508
650
|
|
|
509
651
|
// packages/daemon/src/lib/daemon/summarizer.ts
|
|
510
|
-
import { and, desc, eq as
|
|
652
|
+
import { and as and2, desc, eq as eq3, gte, inArray, like, lt, sql as sql2 } from "drizzle-orm";
|
|
511
653
|
|
|
512
654
|
// packages/daemon/src/lib/events/mind-events.ts
|
|
513
655
|
var subscribers = /* @__PURE__ */ new Map();
|
|
@@ -674,18 +816,18 @@ function getTimeRange(periodKey, period) {
|
|
|
674
816
|
async function gatherTurnEvents(mind, session, doneId) {
|
|
675
817
|
const db = await getDb();
|
|
676
818
|
const subConditions = [
|
|
677
|
-
|
|
678
|
-
|
|
819
|
+
eq3(mindHistory.mind, mind),
|
|
820
|
+
eq3(mindHistory.type, "done"),
|
|
679
821
|
lt(mindHistory.id, doneId)
|
|
680
822
|
];
|
|
681
|
-
if (session) subConditions.push(
|
|
682
|
-
const prevDoneSubquery = db.select({ id: mindHistory.id }).from(mindHistory).where(
|
|
823
|
+
if (session) subConditions.push(eq3(mindHistory.session, session));
|
|
824
|
+
const prevDoneSubquery = db.select({ id: mindHistory.id }).from(mindHistory).where(and2(...subConditions)).orderBy(desc(mindHistory.id)).limit(1);
|
|
683
825
|
const turnConditions = [
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
826
|
+
eq3(mindHistory.mind, mind),
|
|
827
|
+
sql2`${mindHistory.id} > COALESCE((${prevDoneSubquery}), 0)`,
|
|
828
|
+
sql2`${mindHistory.id} <= ${doneId}`
|
|
687
829
|
];
|
|
688
|
-
if (session) turnConditions.push(
|
|
830
|
+
if (session) turnConditions.push(eq3(mindHistory.session, session));
|
|
689
831
|
const events = await db.select({
|
|
690
832
|
id: mindHistory.id,
|
|
691
833
|
type: mindHistory.type,
|
|
@@ -694,7 +836,7 @@ async function gatherTurnEvents(mind, session, doneId) {
|
|
|
694
836
|
content: mindHistory.content,
|
|
695
837
|
metadata: mindHistory.metadata,
|
|
696
838
|
created_at: mindHistory.created_at
|
|
697
|
-
}).from(mindHistory).where(
|
|
839
|
+
}).from(mindHistory).where(and2(...turnConditions)).orderBy(mindHistory.id);
|
|
698
840
|
return {
|
|
699
841
|
events,
|
|
700
842
|
fromId: events.length > 0 ? events[0].id : doneId,
|
|
@@ -711,7 +853,7 @@ async function gatherTurnEventsByTurnId(turnId) {
|
|
|
711
853
|
content: mindHistory.content,
|
|
712
854
|
metadata: mindHistory.metadata,
|
|
713
855
|
created_at: mindHistory.created_at
|
|
714
|
-
}).from(mindHistory).where(
|
|
856
|
+
}).from(mindHistory).where(eq3(mindHistory.turn_id, turnId)).orderBy(mindHistory.id);
|
|
715
857
|
return {
|
|
716
858
|
events,
|
|
717
859
|
fromId: events.length > 0 ? events[0].id : 0,
|
|
@@ -811,8 +953,8 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
811
953
|
if (turnId) {
|
|
812
954
|
try {
|
|
813
955
|
const db2 = await getDb();
|
|
814
|
-
await db2.update(mindHistory).set({ turn_id: null }).where(
|
|
815
|
-
await db2.update(messages).set({ turn_id: null }).where(
|
|
956
|
+
await db2.update(mindHistory).set({ turn_id: null }).where(and2(eq3(mindHistory.turn_id, turnId), eq3(mindHistory.type, "inbound")));
|
|
957
|
+
await db2.update(messages).set({ turn_id: null }).where(eq3(messages.turn_id, turnId));
|
|
816
958
|
} catch (err) {
|
|
817
959
|
sLog.error(`failed to un-tag events for interrupted turn ${turnId}`, logger_default.errorData(err));
|
|
818
960
|
}
|
|
@@ -869,10 +1011,10 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
869
1011
|
summaryId = result[0]?.id;
|
|
870
1012
|
if (summaryId == null) {
|
|
871
1013
|
const existing = await db.select({ id: summaries.id }).from(summaries).where(
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1014
|
+
and2(
|
|
1015
|
+
eq3(summaries.mind, mind),
|
|
1016
|
+
eq3(summaries.period, "turn"),
|
|
1017
|
+
eq3(summaries.period_key, periodKey)
|
|
876
1018
|
)
|
|
877
1019
|
).get();
|
|
878
1020
|
summaryId = existing?.id;
|
|
@@ -901,7 +1043,7 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
901
1043
|
}
|
|
902
1044
|
async function setSummaryId(turnId, summaryId) {
|
|
903
1045
|
const db = await getDb();
|
|
904
|
-
await db.update(turns).set({ summary_id: summaryId }).where(
|
|
1046
|
+
await db.update(turns).set({ summary_id: summaryId }).where(eq3(turns.id, turnId));
|
|
905
1047
|
}
|
|
906
1048
|
function getChildPeriod(period) {
|
|
907
1049
|
switch (period) {
|
|
@@ -945,9 +1087,9 @@ async function gatherChildSummaries(mind, period, periodKey) {
|
|
|
945
1087
|
if (period === "hour") {
|
|
946
1088
|
const { start: start2, end: end2 } = getTimeRange(periodKey, "hour");
|
|
947
1089
|
const rows2 = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
1090
|
+
and2(
|
|
1091
|
+
eq3(summaries.mind, mind),
|
|
1092
|
+
eq3(summaries.period, childPeriod),
|
|
951
1093
|
gte(summaries.created_at, start2),
|
|
952
1094
|
lt(summaries.created_at, end2)
|
|
953
1095
|
)
|
|
@@ -959,9 +1101,9 @@ async function gatherChildSummaries(mind, period, periodKey) {
|
|
|
959
1101
|
}
|
|
960
1102
|
if (period === "day") {
|
|
961
1103
|
const rows2 = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1104
|
+
and2(
|
|
1105
|
+
eq3(summaries.mind, mind),
|
|
1106
|
+
eq3(summaries.period, childPeriod),
|
|
965
1107
|
like(summaries.period_key, `${periodKey}%`)
|
|
966
1108
|
)
|
|
967
1109
|
).orderBy(summaries.period_key);
|
|
@@ -974,11 +1116,11 @@ async function gatherChildSummaries(mind, period, periodKey) {
|
|
|
974
1116
|
const startKey = start.slice(0, 10);
|
|
975
1117
|
const endKey = end.slice(0, 10);
|
|
976
1118
|
const rows = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1119
|
+
and2(
|
|
1120
|
+
eq3(summaries.mind, mind),
|
|
1121
|
+
eq3(summaries.period, childPeriod),
|
|
980
1122
|
gte(summaries.period_key, startKey),
|
|
981
|
-
|
|
1123
|
+
sql2`${summaries.period_key} <= ${endKey}`
|
|
982
1124
|
)
|
|
983
1125
|
).orderBy(summaries.period_key);
|
|
984
1126
|
return {
|
|
@@ -1058,10 +1200,10 @@ async function summarizeSystem(period, periodKey) {
|
|
|
1058
1200
|
if (await summaryExists(SYSTEM_MIND, period, periodKey)) return;
|
|
1059
1201
|
const db = await getDb();
|
|
1060
1202
|
const rows = await db.select({ mind: summaries.mind, content: summaries.content }).from(summaries).where(
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1203
|
+
and2(
|
|
1204
|
+
eq3(summaries.period, period),
|
|
1205
|
+
eq3(summaries.period_key, periodKey),
|
|
1206
|
+
sql2`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1065
1207
|
)
|
|
1066
1208
|
).orderBy(summaries.mind);
|
|
1067
1209
|
if (rows.length === 0) return;
|
|
@@ -1096,11 +1238,11 @@ async function summarizeSystem(period, periodKey) {
|
|
|
1096
1238
|
async function mindsWithTurnSummaries(start, end) {
|
|
1097
1239
|
const db = await getDb();
|
|
1098
1240
|
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
1099
|
-
|
|
1100
|
-
|
|
1241
|
+
and2(
|
|
1242
|
+
eq3(summaries.period, "turn"),
|
|
1101
1243
|
gte(summaries.created_at, start),
|
|
1102
1244
|
lt(summaries.created_at, end),
|
|
1103
|
-
|
|
1245
|
+
sql2`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1104
1246
|
)
|
|
1105
1247
|
).groupBy(summaries.mind);
|
|
1106
1248
|
return rows.map((r) => r.mind);
|
|
@@ -1108,10 +1250,10 @@ async function mindsWithTurnSummaries(start, end) {
|
|
|
1108
1250
|
async function mindsWithSummaries(period, keyPattern) {
|
|
1109
1251
|
const db = await getDb();
|
|
1110
1252
|
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
1111
|
-
|
|
1112
|
-
|
|
1253
|
+
and2(
|
|
1254
|
+
eq3(summaries.period, period),
|
|
1113
1255
|
like(summaries.period_key, keyPattern),
|
|
1114
|
-
|
|
1256
|
+
sql2`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1115
1257
|
)
|
|
1116
1258
|
).groupBy(summaries.mind);
|
|
1117
1259
|
return rows.map((r) => r.mind);
|
|
@@ -1119,11 +1261,11 @@ async function mindsWithSummaries(period, keyPattern) {
|
|
|
1119
1261
|
async function mindsWithDailySummariesInRange(startKey, endKey) {
|
|
1120
1262
|
const db = await getDb();
|
|
1121
1263
|
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
1122
|
-
|
|
1123
|
-
|
|
1264
|
+
and2(
|
|
1265
|
+
eq3(summaries.period, "day"),
|
|
1124
1266
|
gte(summaries.period_key, startKey),
|
|
1125
|
-
|
|
1126
|
-
|
|
1267
|
+
sql2`${summaries.period_key} <= ${endKey}`,
|
|
1268
|
+
sql2`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1127
1269
|
)
|
|
1128
1270
|
).groupBy(summaries.mind);
|
|
1129
1271
|
return rows.map((r) => r.mind);
|
|
@@ -1187,10 +1329,10 @@ async function processMonth(periodKey) {
|
|
|
1187
1329
|
async function summaryExists(mind, period, periodKey) {
|
|
1188
1330
|
const db = await getDb();
|
|
1189
1331
|
const row = await db.select({ id: summaries.id }).from(summaries).where(
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1332
|
+
and2(
|
|
1333
|
+
eq3(summaries.mind, mind),
|
|
1334
|
+
eq3(summaries.period, period),
|
|
1335
|
+
eq3(summaries.period_key, periodKey)
|
|
1194
1336
|
)
|
|
1195
1337
|
).get();
|
|
1196
1338
|
return !!row;
|
|
@@ -1220,7 +1362,7 @@ async function backfill() {
|
|
|
1220
1362
|
}
|
|
1221
1363
|
const allKeys = candidates.map((c) => c.key);
|
|
1222
1364
|
const db = await getDb();
|
|
1223
|
-
const existingRows = await db.select({ period: summaries.period, period_key: summaries.period_key }).from(summaries).where(
|
|
1365
|
+
const existingRows = await db.select({ period: summaries.period, period_key: summaries.period_key }).from(summaries).where(and2(eq3(summaries.mind, SYSTEM_MIND), inArray(summaries.period_key, allKeys)));
|
|
1224
1366
|
const existingSet = new Set(existingRows.map((r) => `${r.period}:${r.period_key}`));
|
|
1225
1367
|
const processFn = {
|
|
1226
1368
|
hour: processHour,
|
|
@@ -1297,6 +1439,13 @@ var activeTurns = /* @__PURE__ */ new Map();
|
|
|
1297
1439
|
function key(mind, session) {
|
|
1298
1440
|
return `${mind}:${session ?? "*"}`;
|
|
1299
1441
|
}
|
|
1442
|
+
var erroredSessions = /* @__PURE__ */ new Set();
|
|
1443
|
+
function markErrored(mind, session) {
|
|
1444
|
+
erroredSessions.add(key(mind, session));
|
|
1445
|
+
}
|
|
1446
|
+
function takeErrored(mind, session) {
|
|
1447
|
+
return erroredSessions.delete(key(mind, session));
|
|
1448
|
+
}
|
|
1300
1449
|
async function createTurn(mind) {
|
|
1301
1450
|
const k = key(mind);
|
|
1302
1451
|
const existing = activeTurns.get(k);
|
|
@@ -1333,7 +1482,7 @@ async function assignSession(mind, turnId, session) {
|
|
|
1333
1482
|
}
|
|
1334
1483
|
try {
|
|
1335
1484
|
const db = await getDb();
|
|
1336
|
-
await db.update(turns).set({ session }).where(
|
|
1485
|
+
await db.update(turns).set({ session }).where(eq4(turns.id, turnId));
|
|
1337
1486
|
} catch (err) {
|
|
1338
1487
|
tlog.error(`failed to assign session to turn ${turnId}`, logger_default.errorData(err));
|
|
1339
1488
|
return;
|
|
@@ -1348,7 +1497,7 @@ async function completeTurn(mind, session) {
|
|
|
1348
1497
|
if (!entry) return void 0;
|
|
1349
1498
|
try {
|
|
1350
1499
|
const db = await getDb();
|
|
1351
|
-
await db.update(turns).set({ status: "complete" }).where(
|
|
1500
|
+
await db.update(turns).set({ status: "complete" }).where(eq4(turns.id, entry.turnId));
|
|
1352
1501
|
} catch (err) {
|
|
1353
1502
|
tlog.error(`failed to complete turn ${entry.turnId}`, logger_default.errorData(err));
|
|
1354
1503
|
return void 0;
|
|
@@ -1359,10 +1508,10 @@ async function completeTurn(mind, session) {
|
|
|
1359
1508
|
}
|
|
1360
1509
|
async function completeOrphanedTurns() {
|
|
1361
1510
|
const db = await getDb();
|
|
1362
|
-
const active = await db.select({ id: turns.id, mind: turns.mind, session: turns.session }).from(turns).where(
|
|
1511
|
+
const active = await db.select({ id: turns.id, mind: turns.mind, session: turns.session }).from(turns).where(eq4(turns.status, "active"));
|
|
1363
1512
|
if (active.length === 0) return [];
|
|
1364
1513
|
try {
|
|
1365
|
-
await db.update(turns).set({ status: "complete" }).where(
|
|
1514
|
+
await db.update(turns).set({ status: "complete" }).where(eq4(turns.status, "active"));
|
|
1366
1515
|
} catch (err) {
|
|
1367
1516
|
tlog.error("failed to complete orphaned turns on startup", logger_default.errorData(err));
|
|
1368
1517
|
}
|
|
@@ -1388,11 +1537,14 @@ async function clearMind(mind) {
|
|
|
1388
1537
|
}
|
|
1389
1538
|
}
|
|
1390
1539
|
for (const k of toDelete) activeTurns.delete(k);
|
|
1540
|
+
for (const k of [...erroredSessions]) {
|
|
1541
|
+
if (k.startsWith(`${mind}:`)) erroredSessions.delete(k);
|
|
1542
|
+
}
|
|
1391
1543
|
if (orphaned.length > 0) {
|
|
1392
1544
|
try {
|
|
1393
1545
|
const db = await getDb();
|
|
1394
1546
|
for (const { turnId } of orphaned) {
|
|
1395
|
-
await db.update(turns).set({ status: "complete" }).where(
|
|
1547
|
+
await db.update(turns).set({ status: "complete" }).where(eq4(turns.id, turnId));
|
|
1396
1548
|
}
|
|
1397
1549
|
} catch (err) {
|
|
1398
1550
|
tlog.error(`failed to complete orphaned turns for ${mind}`, logger_default.errorData(err));
|
|
@@ -1412,7 +1564,7 @@ function summarizeOrphanedTurns(orphanedTurns) {
|
|
|
1412
1564
|
var mlog = logger_default.child("minds");
|
|
1413
1565
|
var execFileAsync = promisify(execFile);
|
|
1414
1566
|
function mindPidPath(name) {
|
|
1415
|
-
return
|
|
1567
|
+
return resolve2(stateDir(name), "mind.pid");
|
|
1416
1568
|
}
|
|
1417
1569
|
var MindManager = class {
|
|
1418
1570
|
minds = /* @__PURE__ */ new Map();
|
|
@@ -1428,7 +1580,7 @@ var MindManager = class {
|
|
|
1428
1580
|
return { dir: entry.dir, port: entry.port, baseName: entry.parent, template: entry.template };
|
|
1429
1581
|
}
|
|
1430
1582
|
const dir = entry.dir ?? mindDir(name);
|
|
1431
|
-
if (!
|
|
1583
|
+
if (!existsSync4(dir)) throw new Error(`Mind directory missing: ${dir}`);
|
|
1432
1584
|
return { dir, port: entry.port, baseName: name, template: entry.template };
|
|
1433
1585
|
}
|
|
1434
1586
|
async startMind(name) {
|
|
@@ -1440,8 +1592,8 @@ var MindManager = class {
|
|
|
1440
1592
|
const port = target.port;
|
|
1441
1593
|
const pidFile = mindPidPath(name);
|
|
1442
1594
|
try {
|
|
1443
|
-
if (
|
|
1444
|
-
const stalePid = parseInt(
|
|
1595
|
+
if (existsSync4(pidFile)) {
|
|
1596
|
+
const stalePid = parseInt(readFileSync3(pidFile, "utf-8").trim(), 10);
|
|
1445
1597
|
if (stalePid > 0) {
|
|
1446
1598
|
try {
|
|
1447
1599
|
process.kill(stalePid, 0);
|
|
@@ -1474,8 +1626,8 @@ var MindManager = class {
|
|
|
1474
1626
|
} catch {
|
|
1475
1627
|
}
|
|
1476
1628
|
const mindStateDir = stateDir(name);
|
|
1477
|
-
const logsDir =
|
|
1478
|
-
|
|
1629
|
+
const logsDir = resolve2(mindStateDir, "logs");
|
|
1630
|
+
mkdirSync2(logsDir, { recursive: true });
|
|
1479
1631
|
if (isIsolationEnabled()) {
|
|
1480
1632
|
try {
|
|
1481
1633
|
chownMindDir(mindStateDir, baseName);
|
|
@@ -1485,10 +1637,10 @@ var MindManager = class {
|
|
|
1485
1637
|
);
|
|
1486
1638
|
}
|
|
1487
1639
|
}
|
|
1488
|
-
const logStream = new RotatingLog(
|
|
1640
|
+
const logStream = new RotatingLog(resolve2(logsDir, "mind.log"));
|
|
1489
1641
|
const mindToken = generateMindToken(name);
|
|
1490
1642
|
const mindEnv = loadMergedEnv(name);
|
|
1491
|
-
const mindLocalBin =
|
|
1643
|
+
const mindLocalBin = resolve2(dir, "home", ".local", "bin");
|
|
1492
1644
|
const currentPath = process.env.PATH ?? "";
|
|
1493
1645
|
const env = {
|
|
1494
1646
|
...process.env,
|
|
@@ -1504,24 +1656,30 @@ var MindManager = class {
|
|
|
1504
1656
|
};
|
|
1505
1657
|
if (target.template === "pi") {
|
|
1506
1658
|
try {
|
|
1507
|
-
const configPath2 =
|
|
1508
|
-
if (
|
|
1509
|
-
const config = JSON.parse(
|
|
1659
|
+
const configPath2 = resolve2(dir, "home/.config/config.json");
|
|
1660
|
+
if (existsSync4(configPath2)) {
|
|
1661
|
+
const config = JSON.parse(readFileSync3(configPath2, "utf-8"));
|
|
1510
1662
|
const modelStr = config.model;
|
|
1511
1663
|
if (modelStr?.includes(":")) {
|
|
1512
1664
|
const provider = modelStr.split(":")[0];
|
|
1513
1665
|
const apiKey = await resolveApiKey(provider);
|
|
1514
1666
|
if (apiKey) {
|
|
1515
|
-
const piAgentDir =
|
|
1516
|
-
|
|
1517
|
-
const authPath = resolve(piAgentDir, "auth.json");
|
|
1518
|
-
const authData = existsSync3(authPath) ? JSON.parse(readFileSync2(authPath, "utf-8")) : {};
|
|
1519
|
-
authData[provider] = { type: "api_key", key: apiKey };
|
|
1520
|
-
writeFileSync2(authPath, JSON.stringify(authData, null, 2), { mode: 384 });
|
|
1521
|
-
if (isIsolationEnabled()) {
|
|
1522
|
-
chownMindDir(piAgentDir, baseName);
|
|
1523
|
-
}
|
|
1667
|
+
const piAgentDir = resolve2(dir, ".mind", "pi-agent");
|
|
1668
|
+
writePiProviderKey(piAgentDir, baseName, provider, apiKey);
|
|
1524
1669
|
env.PI_CODING_AGENT_DIR = piAgentDir;
|
|
1670
|
+
const providerEnvVars = {
|
|
1671
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
1672
|
+
openai: "OPENAI_API_KEY",
|
|
1673
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
1674
|
+
google: "GEMINI_API_KEY",
|
|
1675
|
+
groq: "GROQ_API_KEY",
|
|
1676
|
+
cerebras: "CEREBRAS_API_KEY",
|
|
1677
|
+
xai: "XAI_API_KEY",
|
|
1678
|
+
mistral: "MISTRAL_API_KEY",
|
|
1679
|
+
zai: "ZAI_API_KEY"
|
|
1680
|
+
};
|
|
1681
|
+
const providerEnv = providerEnvVars[provider];
|
|
1682
|
+
if (providerEnv) env[providerEnv] = apiKey;
|
|
1525
1683
|
} else {
|
|
1526
1684
|
mlog.warn(
|
|
1527
1685
|
`no API key found for provider "${provider}" \u2014 mind ${name} may fail to start`
|
|
@@ -1538,11 +1696,11 @@ var MindManager = class {
|
|
|
1538
1696
|
const ai = getAiConfig();
|
|
1539
1697
|
const codexConfig = ai?.providers["openai-codex"];
|
|
1540
1698
|
if (codexConfig?.oauth) {
|
|
1541
|
-
const codexDir =
|
|
1542
|
-
|
|
1699
|
+
const codexDir = resolve2(dir, ".mind", "codex");
|
|
1700
|
+
mkdirSync2(codexDir, { recursive: true });
|
|
1543
1701
|
env.CODEX_HOME = codexDir;
|
|
1544
|
-
const authPath =
|
|
1545
|
-
|
|
1702
|
+
const authPath = resolve2(codexDir, "auth.json");
|
|
1703
|
+
writeFileSync3(
|
|
1546
1704
|
authPath,
|
|
1547
1705
|
JSON.stringify({
|
|
1548
1706
|
auth_mode: "chatgpt",
|
|
@@ -1555,9 +1713,9 @@ var MindManager = class {
|
|
|
1555
1713
|
}),
|
|
1556
1714
|
{ mode: 384 }
|
|
1557
1715
|
);
|
|
1558
|
-
const configTomlPath =
|
|
1559
|
-
if (!
|
|
1560
|
-
|
|
1716
|
+
const configTomlPath = resolve2(codexDir, "config.toml");
|
|
1717
|
+
if (!existsSync4(configTomlPath)) {
|
|
1718
|
+
writeFileSync3(configTomlPath, 'cli_auth_credentials_store = "file"\n');
|
|
1561
1719
|
}
|
|
1562
1720
|
if (isIsolationEnabled()) {
|
|
1563
1721
|
chownMindDir(codexDir, baseName);
|
|
@@ -1576,10 +1734,10 @@ var MindManager = class {
|
|
|
1576
1734
|
env.OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
|
1577
1735
|
}
|
|
1578
1736
|
}
|
|
1579
|
-
const homeDir =
|
|
1737
|
+
const homeDir = resolve2(dir, "home");
|
|
1580
1738
|
const zshenvLines = Object.entries(env).filter(([k, v]) => k.startsWith("VOLUTE_") && v != null).map(([k, v]) => `export ${k}=${JSON.stringify(v)}`);
|
|
1581
1739
|
zshenvLines.push(`export PATH=${JSON.stringify(env.PATH ?? "")}`);
|
|
1582
|
-
|
|
1740
|
+
writeFileSync3(resolve2(homeDir, ".zshenv"), `${zshenvLines.join("\n")}
|
|
1583
1741
|
`, { mode: 384 });
|
|
1584
1742
|
}
|
|
1585
1743
|
if (target.template === "claude" || !target.template) {
|
|
@@ -1588,27 +1746,10 @@ var MindManager = class {
|
|
|
1588
1746
|
const anthropicConfig = ai?.providers.anthropic;
|
|
1589
1747
|
if (anthropicConfig?.oauth) {
|
|
1590
1748
|
const key2 = await resolveApiKey("anthropic");
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
const claudeDir =
|
|
1594
|
-
mkdirSync(claudeDir, { recursive: true });
|
|
1749
|
+
const oauth = getAiConfig()?.providers.anthropic?.oauth;
|
|
1750
|
+
if (key2 && oauth) {
|
|
1751
|
+
const claudeDir = writeClaudeCredentials(resolve2(dir, "home"), baseName, oauth);
|
|
1595
1752
|
env.CLAUDE_CONFIG_DIR = claudeDir;
|
|
1596
|
-
const credsPath = resolve(claudeDir, ".credentials.json");
|
|
1597
|
-
writeFileSync2(
|
|
1598
|
-
credsPath,
|
|
1599
|
-
JSON.stringify({
|
|
1600
|
-
claudeAiOauth: {
|
|
1601
|
-
accessToken: key2,
|
|
1602
|
-
refreshToken: anthropicConfig.oauth.refresh,
|
|
1603
|
-
expiresAt: anthropicConfig.oauth.expires ? new Date(anthropicConfig.oauth.expires).toISOString() : null,
|
|
1604
|
-
scopes: ["user:inference", "user:profile"]
|
|
1605
|
-
}
|
|
1606
|
-
}),
|
|
1607
|
-
{ mode: 384 }
|
|
1608
|
-
);
|
|
1609
|
-
if (isIsolationEnabled()) {
|
|
1610
|
-
chownMindDir(claudeDir, baseName);
|
|
1611
|
-
}
|
|
1612
1753
|
}
|
|
1613
1754
|
} else if (anthropicConfig?.apiKey) {
|
|
1614
1755
|
env.ANTHROPIC_API_KEY = anthropicConfig.apiKey;
|
|
@@ -1618,7 +1759,7 @@ var MindManager = class {
|
|
|
1618
1759
|
}
|
|
1619
1760
|
}
|
|
1620
1761
|
if (isIsolationEnabled()) {
|
|
1621
|
-
env.HOME =
|
|
1762
|
+
env.HOME = resolve2(dir, "home");
|
|
1622
1763
|
}
|
|
1623
1764
|
const customNode = process.env.VOLUTE_NODE_PATH;
|
|
1624
1765
|
let baseBin;
|
|
@@ -1626,20 +1767,20 @@ var MindManager = class {
|
|
|
1626
1767
|
if (customNode) {
|
|
1627
1768
|
baseBin = customNode;
|
|
1628
1769
|
baseArgs = [
|
|
1629
|
-
|
|
1770
|
+
resolve2(dir, "node_modules", ".bin", "tsx"),
|
|
1630
1771
|
"src/server.ts",
|
|
1631
1772
|
"--port",
|
|
1632
1773
|
String(port)
|
|
1633
1774
|
];
|
|
1634
1775
|
} else {
|
|
1635
|
-
baseBin =
|
|
1776
|
+
baseBin = resolve2(dir, "node_modules", ".bin", "tsx");
|
|
1636
1777
|
baseArgs = ["src/server.ts", "--port", String(port)];
|
|
1637
1778
|
}
|
|
1638
1779
|
let spawnCmd;
|
|
1639
1780
|
let spawnArgs;
|
|
1640
1781
|
if (isIsolationEnabled()) {
|
|
1641
1782
|
[spawnCmd, spawnArgs] = await wrapForIsolation(baseBin, baseArgs, name);
|
|
1642
|
-
} else if (isSandboxEnabled()) {
|
|
1783
|
+
} else if (isSandboxEnabled() && target.template !== "codex") {
|
|
1643
1784
|
[spawnCmd, spawnArgs] = await wrapForSandbox(baseBin, baseArgs, dir, name, [
|
|
1644
1785
|
dir,
|
|
1645
1786
|
mindStateDir,
|
|
@@ -1666,14 +1807,14 @@ var MindManager = class {
|
|
|
1666
1807
|
while (recentStderr.length > 20) recentStderr.shift();
|
|
1667
1808
|
});
|
|
1668
1809
|
try {
|
|
1669
|
-
await new Promise((
|
|
1810
|
+
await new Promise((resolve9, reject) => {
|
|
1670
1811
|
const timeout = setTimeout(() => {
|
|
1671
1812
|
reject(new Error(`Mind ${name} did not start within 30s`));
|
|
1672
1813
|
}, 3e4);
|
|
1673
1814
|
function checkOutput(data) {
|
|
1674
1815
|
if (data.toString().match(/listening on :\d+/)) {
|
|
1675
1816
|
clearTimeout(timeout);
|
|
1676
|
-
|
|
1817
|
+
resolve9();
|
|
1677
1818
|
}
|
|
1678
1819
|
}
|
|
1679
1820
|
child.stdout?.on("data", checkOutput);
|
|
@@ -1701,7 +1842,7 @@ var MindManager = class {
|
|
|
1701
1842
|
}
|
|
1702
1843
|
if (child.pid) {
|
|
1703
1844
|
try {
|
|
1704
|
-
|
|
1845
|
+
writeFileSync3(pidFile, String(child.pid));
|
|
1705
1846
|
} catch (err) {
|
|
1706
1847
|
mlog.warn(`failed to write PID file for ${name}`, logger_default.errorData(err));
|
|
1707
1848
|
}
|
|
@@ -1769,7 +1910,7 @@ var MindManager = class {
|
|
|
1769
1910
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
1770
1911
|
mlog.error(`mind ${name} exited with code ${code}`);
|
|
1771
1912
|
try {
|
|
1772
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
1913
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-27S7MKJO.js");
|
|
1773
1914
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
1774
1915
|
if (sleepState?.sleeping) {
|
|
1775
1916
|
mlog.info(`${name} is sleeping \u2014 skipping crash recovery`);
|
|
@@ -1778,19 +1919,30 @@ var MindManager = class {
|
|
|
1778
1919
|
} catch (err) {
|
|
1779
1920
|
mlog.warn(`failed to check sleep state for ${name}`, logger_default.errorData(err));
|
|
1780
1921
|
}
|
|
1781
|
-
clearMind(name).then((orphaned) =>
|
|
1922
|
+
clearMind(name).then((orphaned) => {
|
|
1923
|
+
summarizeOrphanedTurns(orphaned);
|
|
1924
|
+
for (const { session } of orphaned) {
|
|
1925
|
+
void recordNotice({
|
|
1926
|
+
mind: name,
|
|
1927
|
+
session: session ?? "main",
|
|
1928
|
+
kind: "crash",
|
|
1929
|
+
reason: "process_crash",
|
|
1930
|
+
detail: "Your process crashed mid-turn and was automatically restarted. That turn was interrupted before it finished \u2014 check whether you left anything incomplete."
|
|
1931
|
+
});
|
|
1932
|
+
}
|
|
1933
|
+
}).catch(
|
|
1782
1934
|
(err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
|
|
1783
1935
|
);
|
|
1784
1936
|
try {
|
|
1785
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
1937
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-L5WFP2ND.js");
|
|
1786
1938
|
getDeliveryManager2().clearMindSessions(name);
|
|
1787
1939
|
} catch (err) {
|
|
1788
1940
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
1789
1941
|
mlog.warn(`failed to clear delivery state for ${name} after crash`, logger_default.errorData(err));
|
|
1790
1942
|
}
|
|
1791
1943
|
}
|
|
1792
|
-
import("./mind-activity-tracker-
|
|
1793
|
-
import("./activity-events-
|
|
1944
|
+
import("./mind-activity-tracker-7GE24YZE.js").then(({ markIdle: markIdle2 }) => markIdle2(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
|
|
1945
|
+
import("./activity-events-OJZ7G5CG.js").then(
|
|
1794
1946
|
({ publish: publish4 }) => publish4({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
|
|
1795
1947
|
).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
|
|
1796
1948
|
const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
|
|
@@ -1817,19 +1969,19 @@ var MindManager = class {
|
|
|
1817
1969
|
this.stopping.add(name);
|
|
1818
1970
|
const { child } = tracked;
|
|
1819
1971
|
this.minds.delete(name);
|
|
1820
|
-
await new Promise((
|
|
1821
|
-
child.on("exit", () =>
|
|
1972
|
+
await new Promise((resolve9) => {
|
|
1973
|
+
child.on("exit", () => resolve9());
|
|
1822
1974
|
try {
|
|
1823
1975
|
process.kill(-child.pid, "SIGTERM");
|
|
1824
1976
|
} catch {
|
|
1825
|
-
|
|
1977
|
+
resolve9();
|
|
1826
1978
|
}
|
|
1827
1979
|
setTimeout(() => {
|
|
1828
1980
|
try {
|
|
1829
1981
|
process.kill(-child.pid, "SIGKILL");
|
|
1830
1982
|
} catch {
|
|
1831
1983
|
}
|
|
1832
|
-
|
|
1984
|
+
resolve9();
|
|
1833
1985
|
}, 5e3);
|
|
1834
1986
|
});
|
|
1835
1987
|
this.stopping.delete(name);
|
|
@@ -1841,7 +1993,7 @@ var MindManager = class {
|
|
|
1841
1993
|
mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
|
|
1842
1994
|
}
|
|
1843
1995
|
try {
|
|
1844
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
1996
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-L5WFP2ND.js");
|
|
1845
1997
|
getDeliveryManager2().clearMindSessions(name);
|
|
1846
1998
|
} catch (err) {
|
|
1847
1999
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
@@ -1849,7 +2001,7 @@ var MindManager = class {
|
|
|
1849
2001
|
}
|
|
1850
2002
|
}
|
|
1851
2003
|
try {
|
|
1852
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
2004
|
+
const { clearEchoTextCache } = await import("./echo-text-TJWCUBPB.js");
|
|
1853
2005
|
clearEchoTextCache(name);
|
|
1854
2006
|
} catch (err) {
|
|
1855
2007
|
mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
|
|
@@ -1877,7 +2029,7 @@ var MindManager = class {
|
|
|
1877
2029
|
return [...this.minds.keys()];
|
|
1878
2030
|
}
|
|
1879
2031
|
get crashAttemptsPath() {
|
|
1880
|
-
return
|
|
2032
|
+
return resolve2(voluteSystemDir(), "crash-attempts.json");
|
|
1881
2033
|
}
|
|
1882
2034
|
loadCrashAttempts() {
|
|
1883
2035
|
this.restartTracker.load(loadJsonMap(this.crashAttemptsPath));
|
|
@@ -1975,20 +2127,20 @@ async function announceToSystem(text) {
|
|
|
1975
2127
|
}
|
|
1976
2128
|
|
|
1977
2129
|
// packages/daemon/src/lib/extensions.ts
|
|
1978
|
-
import { existsSync as
|
|
1979
|
-
import { dirname, resolve as
|
|
2130
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
2131
|
+
import { dirname, resolve as resolve4 } from "path";
|
|
1980
2132
|
|
|
1981
2133
|
// packages/daemon/src/lib/config/systems-config.ts
|
|
1982
|
-
import { existsSync as
|
|
1983
|
-
import { resolve as
|
|
2134
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
2135
|
+
import { resolve as resolve3 } from "path";
|
|
1984
2136
|
var DEFAULT_API_URL = "https://volute.systems";
|
|
1985
2137
|
function configPath() {
|
|
1986
|
-
return
|
|
2138
|
+
return resolve3(voluteSystemDir(), "systems.json");
|
|
1987
2139
|
}
|
|
1988
2140
|
function readSystemsConfig() {
|
|
1989
2141
|
const path = configPath();
|
|
1990
|
-
if (!
|
|
1991
|
-
const raw =
|
|
2142
|
+
if (!existsSync5(path)) return null;
|
|
2143
|
+
const raw = readFileSync4(path, "utf-8");
|
|
1992
2144
|
let data;
|
|
1993
2145
|
try {
|
|
1994
2146
|
data = JSON.parse(raw);
|
|
@@ -2006,8 +2158,8 @@ function readSystemsConfig() {
|
|
|
2006
2158
|
};
|
|
2007
2159
|
}
|
|
2008
2160
|
function writeSystemsConfig(config) {
|
|
2009
|
-
|
|
2010
|
-
|
|
2161
|
+
mkdirSync3(voluteSystemDir(), { recursive: true });
|
|
2162
|
+
writeFileSync4(configPath(), `${JSON.stringify(config, null, 2)}
|
|
2011
2163
|
`, { mode: 384 });
|
|
2012
2164
|
}
|
|
2013
2165
|
function deleteSystemsConfig() {
|
|
@@ -2066,19 +2218,19 @@ function parseCommandArgs(rawArgs, argDefs, flagDefs) {
|
|
|
2066
2218
|
return { args: namedArgs, flags, rest };
|
|
2067
2219
|
}
|
|
2068
2220
|
function extensionsBaseDir() {
|
|
2069
|
-
return
|
|
2221
|
+
return resolve4(voluteHome(), "extensions");
|
|
2070
2222
|
}
|
|
2071
2223
|
function extensionDataDir(id) {
|
|
2072
|
-
return
|
|
2224
|
+
return resolve4(voluteSystemDir(), "extension-data", id);
|
|
2073
2225
|
}
|
|
2074
2226
|
function extensionsConfigPath() {
|
|
2075
|
-
return
|
|
2227
|
+
return resolve4(voluteHome(), "system", "extensions.json");
|
|
2076
2228
|
}
|
|
2077
2229
|
function readExtensionsConfig() {
|
|
2078
2230
|
const configPath2 = extensionsConfigPath();
|
|
2079
|
-
if (!
|
|
2231
|
+
if (!existsSync6(configPath2)) return [];
|
|
2080
2232
|
try {
|
|
2081
|
-
const data = JSON.parse(
|
|
2233
|
+
const data = JSON.parse(readFileSync5(configPath2, "utf-8"));
|
|
2082
2234
|
return Array.isArray(data) ? data : [];
|
|
2083
2235
|
} catch (err) {
|
|
2084
2236
|
logger_default.warn("failed to read extensions config, ignoring installed extensions", {
|
|
@@ -2096,7 +2248,7 @@ async function getLibsqlDatabase() {
|
|
|
2096
2248
|
return _LibsqlDatabase;
|
|
2097
2249
|
}
|
|
2098
2250
|
async function openExtensionDb(_id, dataDir) {
|
|
2099
|
-
const dbPath =
|
|
2251
|
+
const dbPath = resolve4(dataDir, "data.db");
|
|
2100
2252
|
const Database = await getLibsqlDatabase();
|
|
2101
2253
|
return new Database(dbPath);
|
|
2102
2254
|
}
|
|
@@ -2138,7 +2290,7 @@ async function buildContext(manifest, dataDir, authMw) {
|
|
|
2138
2290
|
getMindDir: (name) => {
|
|
2139
2291
|
try {
|
|
2140
2292
|
const dir = mindDir(name);
|
|
2141
|
-
return
|
|
2293
|
+
return existsSync6(dir) ? dir : null;
|
|
2142
2294
|
} catch (err) {
|
|
2143
2295
|
logger_default.warn(
|
|
2144
2296
|
`extension ${manifest.id}: failed to resolve mind dir for ${name}`,
|
|
@@ -2160,7 +2312,7 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2160
2312
|
return;
|
|
2161
2313
|
}
|
|
2162
2314
|
const dataDir = extensionDataDir(manifest.id);
|
|
2163
|
-
|
|
2315
|
+
mkdirSync4(dataDir, { recursive: true });
|
|
2164
2316
|
const context = await buildContext(manifest, dataDir, authMw);
|
|
2165
2317
|
const routesApp = manifest.routes(context);
|
|
2166
2318
|
const extApiPath = `/api/ext/${manifest.id}`;
|
|
@@ -2223,18 +2375,18 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2223
2375
|
}
|
|
2224
2376
|
}
|
|
2225
2377
|
let resolvedAssetsDir = manifest.ui?.assetsDir ?? "";
|
|
2226
|
-
if (resolvedAssetsDir && !
|
|
2378
|
+
if (resolvedAssetsDir && !existsSync6(resolvedAssetsDir)) {
|
|
2227
2379
|
let searchDir = dirname(new URL(import.meta.url).pathname);
|
|
2228
2380
|
for (let i = 0; i < 5; i++) {
|
|
2229
|
-
const candidate =
|
|
2230
|
-
if (
|
|
2381
|
+
const candidate = resolve4(searchDir, "packages", "extensions", manifest.id, "dist", "ui");
|
|
2382
|
+
if (existsSync6(candidate)) {
|
|
2231
2383
|
resolvedAssetsDir = candidate;
|
|
2232
2384
|
break;
|
|
2233
2385
|
}
|
|
2234
2386
|
searchDir = dirname(searchDir);
|
|
2235
2387
|
}
|
|
2236
2388
|
}
|
|
2237
|
-
if (resolvedAssetsDir &&
|
|
2389
|
+
if (resolvedAssetsDir && existsSync6(resolvedAssetsDir)) {
|
|
2238
2390
|
const assetsDir = resolvedAssetsDir;
|
|
2239
2391
|
const { readFile: readFile2, stat: fsStat } = await import("fs/promises");
|
|
2240
2392
|
const { extname: ext } = await import("path");
|
|
@@ -2251,11 +2403,11 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2251
2403
|
".woff2": "font/woff2"
|
|
2252
2404
|
};
|
|
2253
2405
|
const prefix = `/ext/${manifest.id}`;
|
|
2254
|
-
const indexPath =
|
|
2406
|
+
const indexPath = resolve4(assetsDir, "index.html");
|
|
2255
2407
|
const serveExtAssets = async (c) => {
|
|
2256
2408
|
const urlPath = new URL(c.req.url).pathname;
|
|
2257
2409
|
const relativePath = urlPath.slice(prefix.length).replace(/^\//, "") || "index.html";
|
|
2258
|
-
const filePath =
|
|
2410
|
+
const filePath = resolve4(assetsDir, relativePath);
|
|
2259
2411
|
if (filePath !== assetsDir && !filePath.startsWith(`${assetsDir}/`))
|
|
2260
2412
|
return c.text("Forbidden", 403);
|
|
2261
2413
|
const s = await fsStat(filePath).catch(() => null);
|
|
@@ -2264,7 +2416,7 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2264
2416
|
const body = await readFile2(filePath);
|
|
2265
2417
|
return c.body(body, 200, { "Content-Type": mime });
|
|
2266
2418
|
}
|
|
2267
|
-
if (
|
|
2419
|
+
if (existsSync6(indexPath)) {
|
|
2268
2420
|
const body = await readFile2(indexPath, "utf-8");
|
|
2269
2421
|
return c.html(body);
|
|
2270
2422
|
}
|
|
@@ -2285,10 +2437,10 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2285
2437
|
for (const entry of entries) {
|
|
2286
2438
|
if (!entry.isDirectory()) continue;
|
|
2287
2439
|
try {
|
|
2288
|
-
const skillPath =
|
|
2440
|
+
const skillPath = resolve4(skillsDir, entry.name);
|
|
2289
2441
|
const sourceHash = hashSkillDir(skillPath);
|
|
2290
|
-
const destDir =
|
|
2291
|
-
if (
|
|
2442
|
+
const destDir = resolve4(sharedSkillsDir(), entry.name);
|
|
2443
|
+
if (existsSync6(destDir)) {
|
|
2292
2444
|
const destHash = hashSkillDir(destDir);
|
|
2293
2445
|
if (sourceHash === destHash) continue;
|
|
2294
2446
|
}
|
|
@@ -2315,14 +2467,14 @@ function resolveSkillsDir(manifest) {
|
|
|
2315
2467
|
if (cached !== void 0) return cached;
|
|
2316
2468
|
let searchDir = dirname(new URL(import.meta.url).pathname);
|
|
2317
2469
|
for (let i = 0; i < 5; i++) {
|
|
2318
|
-
const candidate =
|
|
2319
|
-
if (
|
|
2470
|
+
const candidate = resolve4(searchDir, "packages", "extensions", manifest.id, "skills");
|
|
2471
|
+
if (existsSync6(candidate)) {
|
|
2320
2472
|
skillsDirCache.set(manifest.id, candidate);
|
|
2321
2473
|
return candidate;
|
|
2322
2474
|
}
|
|
2323
2475
|
searchDir = dirname(searchDir);
|
|
2324
2476
|
}
|
|
2325
|
-
if (
|
|
2477
|
+
if (existsSync6(manifest.skillsDir)) {
|
|
2326
2478
|
skillsDirCache.set(manifest.id, manifest.skillsDir);
|
|
2327
2479
|
return manifest.skillsDir;
|
|
2328
2480
|
}
|
|
@@ -2350,14 +2502,14 @@ async function discoverBuiltinExtensions(disabledIds) {
|
|
|
2350
2502
|
async function discoverInstalledExtensions() {
|
|
2351
2503
|
const results = [];
|
|
2352
2504
|
const packages = readExtensionsConfig();
|
|
2353
|
-
const npmDir =
|
|
2505
|
+
const npmDir = resolve4(voluteHome(), "extensions", "_npm");
|
|
2354
2506
|
const { createRequire } = await import("module");
|
|
2355
2507
|
for (const pkg of packages) {
|
|
2356
2508
|
try {
|
|
2357
2509
|
let resolved = pkg;
|
|
2358
|
-
const npmPkgDir =
|
|
2359
|
-
if (
|
|
2360
|
-
const require2 = createRequire(
|
|
2510
|
+
const npmPkgDir = resolve4(npmDir, "node_modules", pkg);
|
|
2511
|
+
if (existsSync6(npmPkgDir)) {
|
|
2512
|
+
const require2 = createRequire(resolve4(npmDir, "noop.js"));
|
|
2361
2513
|
resolved = require2.resolve(pkg);
|
|
2362
2514
|
}
|
|
2363
2515
|
const mod = await import(resolved);
|
|
@@ -2400,7 +2552,7 @@ function validateManifest(manifest, source) {
|
|
|
2400
2552
|
}
|
|
2401
2553
|
async function discoverLocalExtensions() {
|
|
2402
2554
|
const baseDir = extensionsBaseDir();
|
|
2403
|
-
if (!
|
|
2555
|
+
if (!existsSync6(baseDir)) return [];
|
|
2404
2556
|
const manifests = [];
|
|
2405
2557
|
let entries;
|
|
2406
2558
|
try {
|
|
@@ -2410,9 +2562,9 @@ async function discoverLocalExtensions() {
|
|
|
2410
2562
|
return [];
|
|
2411
2563
|
}
|
|
2412
2564
|
for (const dir of entries) {
|
|
2413
|
-
const extDir =
|
|
2414
|
-
const candidates = [
|
|
2415
|
-
const entryPoint = candidates.find((p) =>
|
|
2565
|
+
const extDir = resolve4(baseDir, dir);
|
|
2566
|
+
const candidates = [resolve4(extDir, "src", "index.js"), resolve4(extDir, "index.js")];
|
|
2567
|
+
const entryPoint = candidates.find((p) => existsSync6(p));
|
|
2416
2568
|
if (!entryPoint) continue;
|
|
2417
2569
|
try {
|
|
2418
2570
|
const mod = await import(entryPoint);
|
|
@@ -2547,21 +2699,21 @@ function setExtensionEnabled(id, enabled) {
|
|
|
2547
2699
|
writeGlobalConfig(config);
|
|
2548
2700
|
}
|
|
2549
2701
|
function extensionsNpmDir() {
|
|
2550
|
-
return
|
|
2702
|
+
return resolve4(voluteHome(), "extensions", "_npm");
|
|
2551
2703
|
}
|
|
2552
2704
|
function ensureExtensionsNpmDir() {
|
|
2553
2705
|
const dir = extensionsNpmDir();
|
|
2554
|
-
|
|
2555
|
-
const pkgPath =
|
|
2556
|
-
if (!
|
|
2557
|
-
|
|
2706
|
+
mkdirSync4(dir, { recursive: true });
|
|
2707
|
+
const pkgPath = resolve4(dir, "package.json");
|
|
2708
|
+
if (!existsSync6(pkgPath)) {
|
|
2709
|
+
writeFileSync5(pkgPath, '{"private":true,"dependencies":{}}\n');
|
|
2558
2710
|
}
|
|
2559
2711
|
return dir;
|
|
2560
2712
|
}
|
|
2561
2713
|
function writeExtensionsConfig(packages) {
|
|
2562
2714
|
const configPath2 = extensionsConfigPath();
|
|
2563
|
-
|
|
2564
|
-
|
|
2715
|
+
mkdirSync4(resolve4(configPath2, ".."), { recursive: true });
|
|
2716
|
+
writeFileSync5(configPath2, `${JSON.stringify(packages, null, 2)}
|
|
2565
2717
|
`);
|
|
2566
2718
|
}
|
|
2567
2719
|
var VALID_NPM_PACKAGE = /^(@[a-z0-9-~][a-z0-9._-~]*\/)?[a-z0-9-~][a-z0-9._-~]*(@[^\s]+)?$/;
|
|
@@ -2574,7 +2726,7 @@ async function installNpmExtension(pkg) {
|
|
|
2574
2726
|
throw new Error(`Extension "${pkg}" is already installed`);
|
|
2575
2727
|
}
|
|
2576
2728
|
const dir = ensureExtensionsNpmDir();
|
|
2577
|
-
const { exec: exec2 } = await import("./exec-
|
|
2729
|
+
const { exec: exec2 } = await import("./exec-BGMU7VXR.js");
|
|
2578
2730
|
try {
|
|
2579
2731
|
await exec2("npm", ["install", pkg], { cwd: dir });
|
|
2580
2732
|
} catch (err) {
|
|
@@ -2595,7 +2747,7 @@ async function uninstallNpmExtension(pkg) {
|
|
|
2595
2747
|
packages.splice(idx, 1);
|
|
2596
2748
|
writeExtensionsConfig(packages);
|
|
2597
2749
|
try {
|
|
2598
|
-
const { exec: exec2 } = await import("./exec-
|
|
2750
|
+
const { exec: exec2 } = await import("./exec-BGMU7VXR.js");
|
|
2599
2751
|
await exec2("npm", ["uninstall", pkg], { cwd: extensionsNpmDir() });
|
|
2600
2752
|
} catch (err) {
|
|
2601
2753
|
logger_default.warn(
|
|
@@ -2607,13 +2759,13 @@ async function uninstallNpmExtension(pkg) {
|
|
|
2607
2759
|
}
|
|
2608
2760
|
async function cleanupExtensionSkills(pkg) {
|
|
2609
2761
|
try {
|
|
2610
|
-
const pkgDir =
|
|
2611
|
-
if (!
|
|
2762
|
+
const pkgDir = resolve4(extensionsNpmDir(), "node_modules", pkg);
|
|
2763
|
+
if (!existsSync6(pkgDir)) return;
|
|
2612
2764
|
const { createRequire } = await import("module");
|
|
2613
|
-
const require2 = createRequire(
|
|
2765
|
+
const require2 = createRequire(resolve4(extensionsNpmDir(), "noop.js"));
|
|
2614
2766
|
const mod = require2(pkg);
|
|
2615
2767
|
const manifest = mod.default ?? mod.extension ?? mod;
|
|
2616
|
-
if (!manifest?.skillsDir || !
|
|
2768
|
+
if (!manifest?.skillsDir || !existsSync6(manifest.skillsDir)) return;
|
|
2617
2769
|
const skillDirs = readdirSync(manifest.skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
2618
2770
|
for (const skillId of skillDirs) {
|
|
2619
2771
|
try {
|
|
@@ -2922,9 +3074,9 @@ async function ensureMailAddress(mindName) {
|
|
|
2922
3074
|
}
|
|
2923
3075
|
|
|
2924
3076
|
// packages/daemon/src/lib/daemon/scheduler.ts
|
|
2925
|
-
import { resolve as
|
|
3077
|
+
import { resolve as resolve5 } from "path";
|
|
2926
3078
|
import { CronExpressionParser } from "cron-parser";
|
|
2927
|
-
var
|
|
3079
|
+
var slog2 = logger_default.child("scheduler");
|
|
2928
3080
|
var Scheduler = class {
|
|
2929
3081
|
schedules = /* @__PURE__ */ new Map();
|
|
2930
3082
|
mindDirs = /* @__PURE__ */ new Map();
|
|
@@ -2933,12 +3085,12 @@ var Scheduler = class {
|
|
|
2933
3085
|
lastFired = /* @__PURE__ */ new Map();
|
|
2934
3086
|
// "mind:scheduleId" → epoch minute
|
|
2935
3087
|
get statePath() {
|
|
2936
|
-
return
|
|
3088
|
+
return resolve5(voluteSystemDir(), "scheduler-state.json");
|
|
2937
3089
|
}
|
|
2938
3090
|
start() {
|
|
2939
3091
|
this.loadState();
|
|
2940
3092
|
this.interval = setInterval(() => {
|
|
2941
|
-
this.tick().catch((err) =>
|
|
3093
|
+
this.tick().catch((err) => slog2.error("scheduler tick failed", logger_default.errorData(err)));
|
|
2942
3094
|
}, 6e4);
|
|
2943
3095
|
}
|
|
2944
3096
|
stop() {
|
|
@@ -3005,7 +3157,7 @@ var Scheduler = class {
|
|
|
3005
3157
|
prevMinute = Math.floor(prev.getTime() / 6e4);
|
|
3006
3158
|
cronCache.set(schedule.cron, prevMinute);
|
|
3007
3159
|
} catch (err) {
|
|
3008
|
-
|
|
3160
|
+
slog2.warn(`invalid cron "${schedule.cron}" for ${mind}:${schedule.id}`, logger_default.errorData(err));
|
|
3009
3161
|
return false;
|
|
3010
3162
|
}
|
|
3011
3163
|
}
|
|
@@ -3019,11 +3171,11 @@ var Scheduler = class {
|
|
|
3019
3171
|
try {
|
|
3020
3172
|
let text;
|
|
3021
3173
|
if (schedule.script) {
|
|
3022
|
-
const homeDir =
|
|
3174
|
+
const homeDir = resolve5(this.mindDirs.get(mindName) ?? mindDir(mindName), "home");
|
|
3023
3175
|
try {
|
|
3024
3176
|
const output = await this.runScript(schedule.script, homeDir, mindName);
|
|
3025
3177
|
if (!output.trim()) {
|
|
3026
|
-
|
|
3178
|
+
slog2.info(`fired script "${schedule.id}" for ${mindName} (no output)`);
|
|
3027
3179
|
return;
|
|
3028
3180
|
}
|
|
3029
3181
|
text = output;
|
|
@@ -3031,24 +3183,24 @@ var Scheduler = class {
|
|
|
3031
3183
|
const stderr = err.stderr ?? "";
|
|
3032
3184
|
text = `[script error] ${err.message}${stderr ? `
|
|
3033
3185
|
${stderr}` : ""}`;
|
|
3034
|
-
|
|
3186
|
+
slog2.warn(`script "${schedule.id}" failed for ${mindName}`, logger_default.errorData(err));
|
|
3035
3187
|
}
|
|
3036
3188
|
} else if (schedule.message) {
|
|
3037
3189
|
text = schedule.message;
|
|
3038
3190
|
} else {
|
|
3039
|
-
|
|
3191
|
+
slog2.warn(`schedule "${schedule.id}" for ${mindName} has no message or script`);
|
|
3040
3192
|
return;
|
|
3041
3193
|
}
|
|
3042
3194
|
await this.deliverSystem(mindName, `[${schedule.id}] ${text}`, {
|
|
3043
3195
|
whileSleeping: schedule.whileSleeping,
|
|
3044
3196
|
session: schedule.session
|
|
3045
3197
|
});
|
|
3046
|
-
|
|
3198
|
+
slog2.info(`fired "${schedule.id}" for ${mindName}`);
|
|
3047
3199
|
if (schedule.fireAt) {
|
|
3048
3200
|
this.removeSchedule(mindName, schedule.id);
|
|
3049
3201
|
}
|
|
3050
3202
|
} catch (err) {
|
|
3051
|
-
|
|
3203
|
+
slog2.warn(`failed to fire "${schedule.id}" for ${mindName}`, logger_default.errorData(err));
|
|
3052
3204
|
}
|
|
3053
3205
|
}
|
|
3054
3206
|
removeSchedule(mindName, scheduleId) {
|
|
@@ -3068,9 +3220,9 @@ ${stderr}` : ""}`;
|
|
|
3068
3220
|
config.schedules = config.schedules.filter((s) => s.id !== scheduleId);
|
|
3069
3221
|
if (config.schedules.length === 0) config.schedules = void 0;
|
|
3070
3222
|
writeVoluteConfig(dir, config);
|
|
3071
|
-
|
|
3223
|
+
slog2.info(`removed one-time schedule "${scheduleId}" for ${mindName}`);
|
|
3072
3224
|
} catch (err) {
|
|
3073
|
-
|
|
3225
|
+
slog2.error(
|
|
3074
3226
|
`failed to persist removal of schedule "${scheduleId}" for ${mindName} (removed from memory)`,
|
|
3075
3227
|
logger_default.errorData(err)
|
|
3076
3228
|
);
|
|
@@ -3095,9 +3247,9 @@ function getScheduler() {
|
|
|
3095
3247
|
}
|
|
3096
3248
|
|
|
3097
3249
|
// packages/daemon/src/lib/daemon/token-budget.ts
|
|
3098
|
-
import { existsSync as
|
|
3250
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
3099
3251
|
import { mkdir, writeFile as writeFile2 } from "fs/promises";
|
|
3100
|
-
import { resolve as
|
|
3252
|
+
import { resolve as resolve6 } from "path";
|
|
3101
3253
|
var tlog2 = logger_default.child("token-budget");
|
|
3102
3254
|
var DEFAULT_BUDGET_PERIOD_MINUTES = 60;
|
|
3103
3255
|
var MAX_QUEUE_SIZE = 100;
|
|
@@ -3134,7 +3286,8 @@ var TokenBudget = class {
|
|
|
3134
3286
|
periodMinutes,
|
|
3135
3287
|
tokenLimit,
|
|
3136
3288
|
queue: [],
|
|
3137
|
-
warningInjected: false
|
|
3289
|
+
warningInjected: false,
|
|
3290
|
+
exceededNotified: false
|
|
3138
3291
|
});
|
|
3139
3292
|
}
|
|
3140
3293
|
}
|
|
@@ -3162,6 +3315,20 @@ var TokenBudget = class {
|
|
|
3162
3315
|
const state = this.budgets.get(mind);
|
|
3163
3316
|
if (state) state.warningInjected = true;
|
|
3164
3317
|
}
|
|
3318
|
+
/**
|
|
3319
|
+
* Returns true exactly once per budget period when the mind first crosses its limit,
|
|
3320
|
+
* so callers can record a single "budget exceeded" notice without repeating it on
|
|
3321
|
+
* every subsequent usage event. The flag resets when the period rolls over.
|
|
3322
|
+
*/
|
|
3323
|
+
noteExceeded(mind) {
|
|
3324
|
+
const state = this.budgets.get(mind);
|
|
3325
|
+
if (!state) return false;
|
|
3326
|
+
if (state.tokensUsed < state.tokenLimit) return false;
|
|
3327
|
+
if (state.exceededNotified) return false;
|
|
3328
|
+
state.exceededNotified = true;
|
|
3329
|
+
this.dirty.add(mind);
|
|
3330
|
+
return true;
|
|
3331
|
+
}
|
|
3165
3332
|
enqueue(mind, message) {
|
|
3166
3333
|
const state = this.budgets.get(mind);
|
|
3167
3334
|
if (!state) return;
|
|
@@ -3197,6 +3364,7 @@ var TokenBudget = class {
|
|
|
3197
3364
|
state.tokensUsed = 0;
|
|
3198
3365
|
state.periodStart = now;
|
|
3199
3366
|
state.warningInjected = false;
|
|
3367
|
+
state.exceededNotified = false;
|
|
3200
3368
|
this.dirty.add(mind);
|
|
3201
3369
|
const queued = this.drain(mind);
|
|
3202
3370
|
if (queued.length > 0) {
|
|
@@ -3220,7 +3388,7 @@ var TokenBudget = class {
|
|
|
3220
3388
|
await Promise.all(writes);
|
|
3221
3389
|
}
|
|
3222
3390
|
budgetStatePath(mind) {
|
|
3223
|
-
return
|
|
3391
|
+
return resolve6(stateDir(mind), "budget.json");
|
|
3224
3392
|
}
|
|
3225
3393
|
async saveBudgetState(mind, state) {
|
|
3226
3394
|
try {
|
|
@@ -3230,6 +3398,7 @@ var TokenBudget = class {
|
|
|
3230
3398
|
periodStart: state.periodStart,
|
|
3231
3399
|
tokensUsed: state.tokensUsed,
|
|
3232
3400
|
warningInjected: state.warningInjected,
|
|
3401
|
+
exceededNotified: state.exceededNotified,
|
|
3233
3402
|
queue: state.queue
|
|
3234
3403
|
};
|
|
3235
3404
|
await writeFile2(this.budgetStatePath(mind), `${JSON.stringify(data)}
|
|
@@ -3241,13 +3410,14 @@ var TokenBudget = class {
|
|
|
3241
3410
|
loadBudgetState(mind) {
|
|
3242
3411
|
try {
|
|
3243
3412
|
const path = this.budgetStatePath(mind);
|
|
3244
|
-
if (!
|
|
3245
|
-
const data = JSON.parse(
|
|
3413
|
+
if (!existsSync7(path)) return null;
|
|
3414
|
+
const data = JSON.parse(readFileSync6(path, "utf-8"));
|
|
3246
3415
|
if (typeof data.periodStart !== "number" || typeof data.tokensUsed !== "number") return null;
|
|
3247
3416
|
return {
|
|
3248
3417
|
periodStart: data.periodStart,
|
|
3249
3418
|
tokensUsed: data.tokensUsed,
|
|
3250
3419
|
warningInjected: data.warningInjected ?? false,
|
|
3420
|
+
exceededNotified: data.exceededNotified ?? false,
|
|
3251
3421
|
queue: Array.isArray(data.queue) ? data.queue : [],
|
|
3252
3422
|
periodMinutes: 0,
|
|
3253
3423
|
// will be overwritten by caller
|
|
@@ -3370,7 +3540,7 @@ async function wakeMind(name) {
|
|
|
3370
3540
|
async function startSpiritFull(name) {
|
|
3371
3541
|
const entry = await findMind(name);
|
|
3372
3542
|
if (entry?.dir) {
|
|
3373
|
-
const { registerMindDir } = await import("./delivery-router-
|
|
3543
|
+
const { registerMindDir } = await import("./delivery-router-IU4OMVKZ.js");
|
|
3374
3544
|
registerMindDir(name, entry.dir);
|
|
3375
3545
|
}
|
|
3376
3546
|
await getMindManager().startMind(name);
|
|
@@ -3392,8 +3562,8 @@ async function stopSpiritFull(name) {
|
|
|
3392
3562
|
}).catch((err) => logger_default.error("failed to publish spirit_stopped activity", logger_default.errorData(err)));
|
|
3393
3563
|
}
|
|
3394
3564
|
async function ensureCreatorDM(mindName, creatorUsername) {
|
|
3395
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-
|
|
3396
|
-
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-
|
|
3565
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-2IMZ6N5G.js");
|
|
3566
|
+
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-LUVHGQSS.js");
|
|
3397
3567
|
const mindUser = await getOrCreateMindUser2(mindName);
|
|
3398
3568
|
const creatorUser = await getUserByUsername2(creatorUsername);
|
|
3399
3569
|
if (!creatorUser) {
|
|
@@ -3425,7 +3595,7 @@ async function stopMindFull(name) {
|
|
|
3425
3595
|
}
|
|
3426
3596
|
|
|
3427
3597
|
// packages/daemon/src/lib/daemon/sleep-manager.ts
|
|
3428
|
-
var
|
|
3598
|
+
var slog3 = logger_default.child("sleep");
|
|
3429
3599
|
function defaultState() {
|
|
3430
3600
|
return {
|
|
3431
3601
|
sleeping: false,
|
|
@@ -3468,7 +3638,7 @@ var SleepManager = class {
|
|
|
3468
3638
|
transitioning = /* @__PURE__ */ new Set();
|
|
3469
3639
|
sleepConfigs = /* @__PURE__ */ new Map();
|
|
3470
3640
|
get statePath() {
|
|
3471
|
-
return
|
|
3641
|
+
return resolve7(voluteSystemDir(), "sleep-state.json");
|
|
3472
3642
|
}
|
|
3473
3643
|
start() {
|
|
3474
3644
|
this.loadState();
|
|
@@ -3484,15 +3654,15 @@ var SleepManager = class {
|
|
|
3484
3654
|
// --- State persistence ---
|
|
3485
3655
|
loadState() {
|
|
3486
3656
|
try {
|
|
3487
|
-
if (
|
|
3488
|
-
const data = JSON.parse(
|
|
3657
|
+
if (existsSync8(this.statePath)) {
|
|
3658
|
+
const data = JSON.parse(readFileSync7(this.statePath, "utf-8"));
|
|
3489
3659
|
for (const [name, state] of Object.entries(data)) {
|
|
3490
3660
|
state.triggerWakeHistory ??= [];
|
|
3491
3661
|
this.states.set(name, state);
|
|
3492
3662
|
}
|
|
3493
3663
|
}
|
|
3494
3664
|
} catch (err) {
|
|
3495
|
-
|
|
3665
|
+
slog3.warn("failed to load sleep state", logger_default.errorData(err));
|
|
3496
3666
|
}
|
|
3497
3667
|
}
|
|
3498
3668
|
saveState() {
|
|
@@ -3501,10 +3671,10 @@ var SleepManager = class {
|
|
|
3501
3671
|
if (state.sleeping) data[name] = state;
|
|
3502
3672
|
}
|
|
3503
3673
|
try {
|
|
3504
|
-
|
|
3674
|
+
writeFileSync6(this.statePath, `${JSON.stringify(data, null, 2)}
|
|
3505
3675
|
`);
|
|
3506
3676
|
} catch (err) {
|
|
3507
|
-
|
|
3677
|
+
slog3.error("failed to save sleep state", logger_default.errorData(err));
|
|
3508
3678
|
}
|
|
3509
3679
|
}
|
|
3510
3680
|
// --- Public API ---
|
|
@@ -3525,7 +3695,7 @@ var SleepManager = class {
|
|
|
3525
3695
|
const state = this.states.get(name);
|
|
3526
3696
|
if (!state?.sleeping || !state.wokenByTrigger) return;
|
|
3527
3697
|
this.markAwake(name);
|
|
3528
|
-
|
|
3698
|
+
slog3.info(`${name} trigger-wake converted to full wake`);
|
|
3529
3699
|
}
|
|
3530
3700
|
getSleepConfig(name) {
|
|
3531
3701
|
if (this.sleepConfigs.has(name)) {
|
|
@@ -3568,7 +3738,7 @@ var SleepManager = class {
|
|
|
3568
3738
|
const result = await sendSystemMessageDirect(name, preSleepMsg);
|
|
3569
3739
|
conversationId = result.conversationId;
|
|
3570
3740
|
} catch (err) {
|
|
3571
|
-
|
|
3741
|
+
slog3.error(`failed to persist pre-sleep message for ${name}`, logger_default.errorData(err));
|
|
3572
3742
|
}
|
|
3573
3743
|
try {
|
|
3574
3744
|
await fetch(`http://127.0.0.1:${entry.port}/message`, {
|
|
@@ -3585,7 +3755,7 @@ var SleepManager = class {
|
|
|
3585
3755
|
})
|
|
3586
3756
|
});
|
|
3587
3757
|
} catch (err) {
|
|
3588
|
-
|
|
3758
|
+
slog3.warn(`failed to send pre-sleep message to ${name}`, logger_default.errorData(err));
|
|
3589
3759
|
}
|
|
3590
3760
|
await this.waitForIdle(name, 12e4);
|
|
3591
3761
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
@@ -3593,7 +3763,7 @@ var SleepManager = class {
|
|
|
3593
3763
|
await this.killOrphanOnPort(entry.port);
|
|
3594
3764
|
await this.archiveSessions(name);
|
|
3595
3765
|
this.markSleeping(name, opts);
|
|
3596
|
-
|
|
3766
|
+
slog3.info(`${name} is now sleeping`);
|
|
3597
3767
|
} finally {
|
|
3598
3768
|
this.transitioning.delete(name);
|
|
3599
3769
|
}
|
|
@@ -3612,7 +3782,7 @@ var SleepManager = class {
|
|
|
3612
3782
|
try {
|
|
3613
3783
|
await wakeMind(name);
|
|
3614
3784
|
} catch (err) {
|
|
3615
|
-
|
|
3785
|
+
slog3.error(`failed to wake ${name}`, logger_default.errorData(err));
|
|
3616
3786
|
return;
|
|
3617
3787
|
}
|
|
3618
3788
|
}
|
|
@@ -3653,7 +3823,7 @@ var SleepManager = class {
|
|
|
3653
3823
|
const result = await sendSystemMessageDirect(name, summaryText);
|
|
3654
3824
|
wakeConvId = result.conversationId;
|
|
3655
3825
|
} catch (err) {
|
|
3656
|
-
|
|
3826
|
+
slog3.error(`failed to persist wake summary for ${name}`, logger_default.errorData(err));
|
|
3657
3827
|
}
|
|
3658
3828
|
try {
|
|
3659
3829
|
await fetch(`http://127.0.0.1:${entry.port}/message`, {
|
|
@@ -3670,17 +3840,17 @@ var SleepManager = class {
|
|
|
3670
3840
|
})
|
|
3671
3841
|
});
|
|
3672
3842
|
} catch (err) {
|
|
3673
|
-
|
|
3843
|
+
slog3.warn(`failed to deliver wake summary to ${name}`, logger_default.errorData(err));
|
|
3674
3844
|
}
|
|
3675
3845
|
}
|
|
3676
3846
|
const flushed = await this.flushQueuedMessages(name);
|
|
3677
3847
|
if (flushed > 0) {
|
|
3678
|
-
|
|
3848
|
+
slog3.info(`flushed ${flushed} queued message(s) for ${name}`);
|
|
3679
3849
|
}
|
|
3680
3850
|
if (!opts?.trigger) {
|
|
3681
3851
|
this.markAwake(name);
|
|
3682
3852
|
}
|
|
3683
|
-
|
|
3853
|
+
slog3.info(`${name} is now awake${opts?.trigger ? " (trigger wake)" : ""}`);
|
|
3684
3854
|
} finally {
|
|
3685
3855
|
this.transitioning.delete(name);
|
|
3686
3856
|
}
|
|
@@ -3735,16 +3905,16 @@ var SleepManager = class {
|
|
|
3735
3905
|
async flushQueuedMessages(name) {
|
|
3736
3906
|
try {
|
|
3737
3907
|
const db = await getDb();
|
|
3738
|
-
const rows = await db.select().from(deliveryQueue).where(
|
|
3908
|
+
const rows = await db.select().from(deliveryQueue).where(and3(eq5(deliveryQueue.mind, name), eq5(deliveryQueue.status, "sleep-queued"))).all();
|
|
3739
3909
|
if (rows.length === 0) return 0;
|
|
3740
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
3910
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-VBBAJX3J.js");
|
|
3741
3911
|
const delivered = [];
|
|
3742
3912
|
for (const row of rows) {
|
|
3743
3913
|
try {
|
|
3744
3914
|
await deliverMessage2(name, JSON.parse(row.payload));
|
|
3745
3915
|
delivered.push(row.id);
|
|
3746
3916
|
} catch (err) {
|
|
3747
|
-
|
|
3917
|
+
slog3.warn(`failed to flush queued message ${row.id} for ${name}`, logger_default.errorData(err));
|
|
3748
3918
|
}
|
|
3749
3919
|
}
|
|
3750
3920
|
if (delivered.length > 0) {
|
|
@@ -3756,7 +3926,7 @@ var SleepManager = class {
|
|
|
3756
3926
|
}
|
|
3757
3927
|
return delivered.length;
|
|
3758
3928
|
} catch (err) {
|
|
3759
|
-
|
|
3929
|
+
slog3.warn(`failed to flush queued messages for ${name}`, logger_default.errorData(err));
|
|
3760
3930
|
return 0;
|
|
3761
3931
|
}
|
|
3762
3932
|
}
|
|
@@ -3785,7 +3955,7 @@ var SleepManager = class {
|
|
|
3785
3955
|
const interval = CronExpressionParser2.parse(config.schedule.wake);
|
|
3786
3956
|
return interval.next().toDate().toISOString();
|
|
3787
3957
|
} catch (err) {
|
|
3788
|
-
|
|
3958
|
+
slog3.warn(`invalid wake cron "${config.schedule.wake}"`, logger_default.errorData(err));
|
|
3789
3959
|
return null;
|
|
3790
3960
|
}
|
|
3791
3961
|
}
|
|
@@ -3805,7 +3975,7 @@ var SleepManager = class {
|
|
|
3805
3975
|
const wakeAt = new Date(state.voluntaryWakeAt);
|
|
3806
3976
|
if (now >= wakeAt) {
|
|
3807
3977
|
this.initiateWake(name).catch(
|
|
3808
|
-
(err) =>
|
|
3978
|
+
(err) => slog3.error(`failed voluntary wake for ${name}`, logger_default.errorData(err))
|
|
3809
3979
|
);
|
|
3810
3980
|
continue;
|
|
3811
3981
|
}
|
|
@@ -3814,7 +3984,7 @@ var SleepManager = class {
|
|
|
3814
3984
|
const wakeAt = new Date(state.scheduledWakeAt);
|
|
3815
3985
|
if (now >= wakeAt) {
|
|
3816
3986
|
this.initiateWake(name).catch(
|
|
3817
|
-
(err) =>
|
|
3987
|
+
(err) => slog3.error(`failed scheduled wake for ${name}`, logger_default.errorData(err))
|
|
3818
3988
|
);
|
|
3819
3989
|
continue;
|
|
3820
3990
|
}
|
|
@@ -3822,7 +3992,7 @@ var SleepManager = class {
|
|
|
3822
3992
|
if (!state?.sleeping) {
|
|
3823
3993
|
if (this.shouldSleep(config.schedule.sleep, epochMinute)) {
|
|
3824
3994
|
this.initiateSleep(name).catch(
|
|
3825
|
-
(err) =>
|
|
3995
|
+
(err) => slog3.error(`failed to initiate sleep for ${name}`, logger_default.errorData(err))
|
|
3826
3996
|
);
|
|
3827
3997
|
}
|
|
3828
3998
|
}
|
|
@@ -3835,22 +4005,22 @@ var SleepManager = class {
|
|
|
3835
4005
|
const prevMinute = Math.floor(prev.getTime() / 6e4);
|
|
3836
4006
|
return prevMinute === epochMinute;
|
|
3837
4007
|
} catch (err) {
|
|
3838
|
-
|
|
4008
|
+
slog3.warn(`invalid sleep cron "${cronExpr}"`, logger_default.errorData(err));
|
|
3839
4009
|
return false;
|
|
3840
4010
|
}
|
|
3841
4011
|
}
|
|
3842
4012
|
async waitForIdle(name, timeoutMs) {
|
|
3843
|
-
return new Promise((
|
|
4013
|
+
return new Promise((resolve9) => {
|
|
3844
4014
|
const timeout = setTimeout(() => {
|
|
3845
4015
|
unsub();
|
|
3846
|
-
|
|
4016
|
+
resolve9();
|
|
3847
4017
|
}, timeoutMs);
|
|
3848
4018
|
const unsub = subscribe((event) => {
|
|
3849
4019
|
if (event.mind !== name) return;
|
|
3850
4020
|
if (event.type === "mind_done" || event.type === "mind_idle") {
|
|
3851
4021
|
clearTimeout(timeout);
|
|
3852
4022
|
unsub();
|
|
3853
|
-
|
|
4023
|
+
resolve9();
|
|
3854
4024
|
}
|
|
3855
4025
|
});
|
|
3856
4026
|
});
|
|
@@ -3858,41 +4028,41 @@ var SleepManager = class {
|
|
|
3858
4028
|
async archiveSessions(name) {
|
|
3859
4029
|
const dir = mindDir(name);
|
|
3860
4030
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 16);
|
|
3861
|
-
const sessionsDir =
|
|
3862
|
-
if (
|
|
3863
|
-
const archiveDir =
|
|
3864
|
-
|
|
4031
|
+
const sessionsDir = resolve7(dir, ".mind", "sessions");
|
|
4032
|
+
if (existsSync8(sessionsDir)) {
|
|
4033
|
+
const archiveDir = resolve7(sessionsDir, "archive");
|
|
4034
|
+
mkdirSync5(archiveDir, { recursive: true });
|
|
3865
4035
|
for (const file of readdirSync2(sessionsDir)) {
|
|
3866
4036
|
if (file === "archive" || !file.endsWith(".json")) continue;
|
|
3867
|
-
const src =
|
|
4037
|
+
const src = resolve7(sessionsDir, file);
|
|
3868
4038
|
const base = file.replace(/\.json$/, "");
|
|
3869
|
-
const dest =
|
|
4039
|
+
const dest = resolve7(archiveDir, `${base}-${timestamp}.json`);
|
|
3870
4040
|
try {
|
|
3871
4041
|
renameSync(src, dest);
|
|
3872
4042
|
} catch (err) {
|
|
3873
|
-
|
|
4043
|
+
slog3.warn(`failed to archive session ${file} for ${name}`, logger_default.errorData(err));
|
|
3874
4044
|
}
|
|
3875
4045
|
}
|
|
3876
4046
|
}
|
|
3877
|
-
const piSessionsDir =
|
|
3878
|
-
if (
|
|
3879
|
-
const archiveDir =
|
|
3880
|
-
|
|
4047
|
+
const piSessionsDir = resolve7(dir, ".mind", "pi-sessions");
|
|
4048
|
+
if (existsSync8(piSessionsDir)) {
|
|
4049
|
+
const archiveDir = resolve7(piSessionsDir, "archive");
|
|
4050
|
+
mkdirSync5(archiveDir, { recursive: true });
|
|
3881
4051
|
for (const entry of readdirSync2(piSessionsDir, { withFileTypes: true })) {
|
|
3882
4052
|
if (entry.name === "archive" || !entry.isDirectory()) continue;
|
|
3883
|
-
const src =
|
|
3884
|
-
const dest =
|
|
4053
|
+
const src = resolve7(piSessionsDir, entry.name);
|
|
4054
|
+
const dest = resolve7(archiveDir, `${entry.name}-${timestamp}`);
|
|
3885
4055
|
try {
|
|
3886
4056
|
renameSync(src, dest);
|
|
3887
4057
|
} catch (err) {
|
|
3888
|
-
|
|
4058
|
+
slog3.warn(`failed to archive pi-session ${entry.name} for ${name}`, logger_default.errorData(err));
|
|
3889
4059
|
}
|
|
3890
4060
|
}
|
|
3891
4061
|
}
|
|
3892
4062
|
}
|
|
3893
4063
|
async runWakeContextScript(name, sleepingSince, duration) {
|
|
3894
|
-
const scriptPath =
|
|
3895
|
-
if (!
|
|
4064
|
+
const scriptPath = resolve7(mindDir(name), "home", ".local", "hooks", "wake-context.sh");
|
|
4065
|
+
if (!existsSync8(scriptPath)) return "";
|
|
3896
4066
|
const input = JSON.stringify({
|
|
3897
4067
|
sleepingSince,
|
|
3898
4068
|
duration,
|
|
@@ -3928,7 +4098,7 @@ var SleepManager = class {
|
|
|
3928
4098
|
});
|
|
3929
4099
|
return result.trim();
|
|
3930
4100
|
} catch (err) {
|
|
3931
|
-
|
|
4101
|
+
slog3.warn(`wake-context script failed for ${name}`, logger_default.errorData(err));
|
|
3932
4102
|
return "";
|
|
3933
4103
|
}
|
|
3934
4104
|
}
|
|
@@ -3942,7 +4112,7 @@ var SleepManager = class {
|
|
|
3942
4112
|
async buildQueuedSummary(name) {
|
|
3943
4113
|
try {
|
|
3944
4114
|
const db = await getDb();
|
|
3945
|
-
const rows = await db.select({ channel: deliveryQueue.channel, sender: deliveryQueue.sender }).from(deliveryQueue).where(
|
|
4115
|
+
const rows = await db.select({ channel: deliveryQueue.channel, sender: deliveryQueue.sender }).from(deliveryQueue).where(and3(eq5(deliveryQueue.mind, name), eq5(deliveryQueue.status, "sleep-queued"))).all();
|
|
3946
4116
|
if (rows.length === 0) return "No messages arrived while you slept.";
|
|
3947
4117
|
const channelCounts = /* @__PURE__ */ new Map();
|
|
3948
4118
|
const senders = /* @__PURE__ */ new Set();
|
|
@@ -3955,7 +4125,7 @@ var SleepManager = class {
|
|
|
3955
4125
|
const senderNote = senders.size > 0 ? ` from ${[...senders].join(", ")}` : "";
|
|
3956
4126
|
return `${rows.length} message${rows.length === 1 ? "" : "s"} arrived while you slept${senderNote} (${parts.join(", ")}). They'll be delivered to your normal channels now.`;
|
|
3957
4127
|
} catch (err) {
|
|
3958
|
-
|
|
4128
|
+
slog3.error(`failed to build queued summary for ${name}`, logger_default.errorData(err));
|
|
3959
4129
|
return "Unable to check for queued messages \u2014 there may be messages waiting.";
|
|
3960
4130
|
}
|
|
3961
4131
|
}
|
|
@@ -3970,7 +4140,7 @@ var SleepManager = class {
|
|
|
3970
4140
|
} catch {
|
|
3971
4141
|
return;
|
|
3972
4142
|
}
|
|
3973
|
-
|
|
4143
|
+
slog3.warn(`orphan process found on port ${port} after sleep, killing`);
|
|
3974
4144
|
const execFileAsync2 = promisify2(execFile2);
|
|
3975
4145
|
try {
|
|
3976
4146
|
const { stdout } = await execFileAsync2("lsof", ["-ti", `:${port}`, "-sTCP:LISTEN"]);
|
|
@@ -3981,7 +4151,7 @@ var SleepManager = class {
|
|
|
3981
4151
|
process.kill(pid, "SIGTERM");
|
|
3982
4152
|
} catch (err) {
|
|
3983
4153
|
if (err.code !== "ESRCH") {
|
|
3984
|
-
|
|
4154
|
+
slog3.warn(`failed to kill orphan pid ${pid}`, logger_default.errorData(err));
|
|
3985
4155
|
}
|
|
3986
4156
|
}
|
|
3987
4157
|
}
|
|
@@ -3989,7 +4159,7 @@ var SleepManager = class {
|
|
|
3989
4159
|
} catch {
|
|
3990
4160
|
try {
|
|
3991
4161
|
const portHex = port.toString(16).toUpperCase().padStart(4, "0");
|
|
3992
|
-
const tcp6 =
|
|
4162
|
+
const tcp6 = readFileSync7("/proc/net/tcp6", "utf-8");
|
|
3993
4163
|
for (const line of tcp6.split("\n")) {
|
|
3994
4164
|
if (!line.includes(`:${portHex} `)) continue;
|
|
3995
4165
|
const fields = line.trim().split(/\s+/);
|
|
@@ -4013,7 +4183,7 @@ var SleepManager = class {
|
|
|
4013
4183
|
}
|
|
4014
4184
|
}
|
|
4015
4185
|
} catch (err) {
|
|
4016
|
-
|
|
4186
|
+
slog3.warn(`failed to kill orphan on port ${port} via /proc`, logger_default.errorData(err));
|
|
4017
4187
|
}
|
|
4018
4188
|
}
|
|
4019
4189
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
@@ -4023,7 +4193,7 @@ var SleepManager = class {
|
|
|
4023
4193
|
if (!state?.sleeping || !state.wokenByTrigger) return;
|
|
4024
4194
|
if (this.transitioning.has(event.mind)) return;
|
|
4025
4195
|
if (event.type === "mind_idle") {
|
|
4026
|
-
|
|
4196
|
+
slog3.info(`${event.mind} going back to sleep after trigger wake`);
|
|
4027
4197
|
state.wokenByTrigger = false;
|
|
4028
4198
|
this.transitioning.add(event.mind);
|
|
4029
4199
|
sleepMind(event.mind).then(() => this.archiveSessions(event.mind)).then(() => {
|
|
@@ -4032,9 +4202,9 @@ var SleepManager = class {
|
|
|
4032
4202
|
const sleepConfig = this.getSleepConfig(event.mind);
|
|
4033
4203
|
state.scheduledWakeAt = this.getNextWakeTime(sleepConfig);
|
|
4034
4204
|
this.saveState();
|
|
4035
|
-
|
|
4205
|
+
slog3.info(`${event.mind} returned to sleep`);
|
|
4036
4206
|
}).catch((err) => {
|
|
4037
|
-
|
|
4207
|
+
slog3.error(`failed to return ${event.mind} to sleep`, logger_default.errorData(err));
|
|
4038
4208
|
}).finally(() => {
|
|
4039
4209
|
this.transitioning.delete(event.mind);
|
|
4040
4210
|
});
|
|
@@ -4057,8 +4227,8 @@ function getSleepManagerIfReady() {
|
|
|
4057
4227
|
|
|
4058
4228
|
// packages/daemon/src/lib/delivery/delivery-manager.ts
|
|
4059
4229
|
import { readFile, realpath } from "fs/promises";
|
|
4060
|
-
import { extname, resolve as
|
|
4061
|
-
import { and as
|
|
4230
|
+
import { extname, resolve as resolve8 } from "path";
|
|
4231
|
+
import { and as and4, eq as eq6, sql as sql3 } from "drizzle-orm";
|
|
4062
4232
|
|
|
4063
4233
|
// packages/daemon/src/lib/chat/typing.ts
|
|
4064
4234
|
var DEFAULT_TTL_MS = 1e4;
|
|
@@ -4256,7 +4426,7 @@ var DeliveryManager = class {
|
|
|
4256
4426
|
async restoreFromDb() {
|
|
4257
4427
|
try {
|
|
4258
4428
|
const db = await getDb();
|
|
4259
|
-
const rows = await db.select().from(deliveryQueue).where(
|
|
4429
|
+
const rows = await db.select().from(deliveryQueue).where(eq6(deliveryQueue.status, "pending"));
|
|
4260
4430
|
for (const row of rows) {
|
|
4261
4431
|
let payload;
|
|
4262
4432
|
try {
|
|
@@ -4274,7 +4444,7 @@ var DeliveryManager = class {
|
|
|
4274
4444
|
this.addToBatchBuffer(row.mind, row.session, payload, sessionConfig);
|
|
4275
4445
|
} else {
|
|
4276
4446
|
try {
|
|
4277
|
-
await db.delete(deliveryQueue).where(
|
|
4447
|
+
await db.delete(deliveryQueue).where(eq6(deliveryQueue.id, row.id));
|
|
4278
4448
|
} catch (err) {
|
|
4279
4449
|
dlog.warn(`failed to delete queue row ${row.id} for ${row.mind}`, logger_default.errorData(err));
|
|
4280
4450
|
}
|
|
@@ -4295,7 +4465,7 @@ var DeliveryManager = class {
|
|
|
4295
4465
|
*/
|
|
4296
4466
|
async getPending(mindName) {
|
|
4297
4467
|
const db = await getDb();
|
|
4298
|
-
const rows = await db.select().from(deliveryQueue).where(
|
|
4468
|
+
const rows = await db.select().from(deliveryQueue).where(and4(eq6(deliveryQueue.mind, mindName), eq6(deliveryQueue.status, "gated")));
|
|
4299
4469
|
const byChannel = /* @__PURE__ */ new Map();
|
|
4300
4470
|
for (const row of rows) {
|
|
4301
4471
|
const ch = row.channel ?? "unknown";
|
|
@@ -4410,6 +4580,7 @@ var DeliveryManager = class {
|
|
|
4410
4580
|
if (payload.conversationId) {
|
|
4411
4581
|
typingMap.set(payload.conversationId, baseName, { persistent: true });
|
|
4412
4582
|
}
|
|
4583
|
+
onMindEvent(baseName, "delivery", payload.channel);
|
|
4413
4584
|
const enrichedPayload = await this.enrichWithProfiles(baseName, session, payload);
|
|
4414
4585
|
const body = JSON.stringify({
|
|
4415
4586
|
...enrichedPayload,
|
|
@@ -4489,10 +4660,10 @@ var DeliveryManager = class {
|
|
|
4489
4660
|
try {
|
|
4490
4661
|
const db = await getDb();
|
|
4491
4662
|
await db.delete(deliveryQueue).where(
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4663
|
+
and4(
|
|
4664
|
+
eq6(deliveryQueue.mind, baseName),
|
|
4665
|
+
eq6(deliveryQueue.session, session),
|
|
4666
|
+
eq6(deliveryQueue.status, "pending")
|
|
4496
4667
|
)
|
|
4497
4668
|
);
|
|
4498
4669
|
} catch (err) {
|
|
@@ -4618,11 +4789,11 @@ var DeliveryManager = class {
|
|
|
4618
4789
|
await this.persistToQueue(baseName, session, payload, "gated");
|
|
4619
4790
|
try {
|
|
4620
4791
|
const db = await getDb();
|
|
4621
|
-
const count = await db.select({ count:
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4792
|
+
const count = await db.select({ count: sql3`count(*)` }).from(deliveryQueue).where(
|
|
4793
|
+
and4(
|
|
4794
|
+
eq6(deliveryQueue.mind, baseName),
|
|
4795
|
+
eq6(deliveryQueue.channel, payload.channel),
|
|
4796
|
+
eq6(deliveryQueue.status, "gated")
|
|
4626
4797
|
)
|
|
4627
4798
|
);
|
|
4628
4799
|
if ((count[0]?.count ?? 0) <= 1) {
|
|
@@ -4647,7 +4818,7 @@ var DeliveryManager = class {
|
|
|
4647
4818
|
`To accept this channel, add a routing rule for "${channel}" to your routes.json.`,
|
|
4648
4819
|
`Messages are being held until a route is configured.`
|
|
4649
4820
|
].filter((line) => line !== null).join("\n");
|
|
4650
|
-
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-
|
|
4821
|
+
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-EIINIHFB.js");
|
|
4651
4822
|
await sendSystemMessage2(mindName, notification);
|
|
4652
4823
|
}
|
|
4653
4824
|
async persistToQueue(mindName, session, payload, status = "pending") {
|
|
@@ -4721,8 +4892,8 @@ var DeliveryManager = class {
|
|
|
4721
4892
|
const dir = mindDir(p.username);
|
|
4722
4893
|
const config = readVoluteConfig(dir);
|
|
4723
4894
|
if (!config?.profile?.avatar) continue;
|
|
4724
|
-
filePath =
|
|
4725
|
-
const homeDir =
|
|
4895
|
+
filePath = resolve8(dir, "home", config.profile.avatar);
|
|
4896
|
+
const homeDir = resolve8(dir, "home");
|
|
4726
4897
|
if (!filePath.startsWith(`${homeDir}/`)) {
|
|
4727
4898
|
dlog.warn(`avatar path for ${p.username} escapes home directory, skipping`);
|
|
4728
4899
|
continue;
|
|
@@ -4741,7 +4912,7 @@ var DeliveryManager = class {
|
|
|
4741
4912
|
throw err;
|
|
4742
4913
|
}
|
|
4743
4914
|
} else {
|
|
4744
|
-
filePath =
|
|
4915
|
+
filePath = resolve8(voluteHome(), "avatars", p.avatar);
|
|
4745
4916
|
}
|
|
4746
4917
|
const ext = extname(filePath).toLowerCase();
|
|
4747
4918
|
const mimeMap = {
|
|
@@ -4883,18 +5054,18 @@ async function linkToolResultToTurn(mind, turnId, toolResultContent, toolUseEven
|
|
|
4883
5054
|
channel: mindHistory.channel,
|
|
4884
5055
|
content: mindHistory.content,
|
|
4885
5056
|
message_id: mindHistory.message_id
|
|
4886
|
-
}).from(mindHistory).where(
|
|
5057
|
+
}).from(mindHistory).where(and5(eq7(mindHistory.id, outboundId), eq7(mindHistory.mind, mind))).limit(1);
|
|
4887
5058
|
const row = rows[0];
|
|
4888
5059
|
if (!row) {
|
|
4889
5060
|
dlog2.warn(`outbound marker references missing record: mind=${mind} id=${outboundId}`);
|
|
4890
5061
|
continue;
|
|
4891
5062
|
}
|
|
4892
|
-
await db.update(mindHistory).set({ turn_id: turnId }).where(
|
|
5063
|
+
await db.update(mindHistory).set({ turn_id: turnId }).where(eq7(mindHistory.id, outboundId));
|
|
4893
5064
|
if (row.message_id) {
|
|
4894
5065
|
await db.update(messages).set({
|
|
4895
5066
|
turn_id: turnId,
|
|
4896
5067
|
...toolUseEventId != null ? { source_event_id: toolUseEventId } : {}
|
|
4897
|
-
}).where(
|
|
5068
|
+
}).where(eq7(messages.id, Number(row.message_id)));
|
|
4898
5069
|
}
|
|
4899
5070
|
publish3(mind, {
|
|
4900
5071
|
mind,
|
|
@@ -4938,19 +5109,19 @@ async function linkToolResultToTurn(mind, turnId, toolResultContent, toolUseEven
|
|
|
4938
5109
|
async function tagUntaggedOutbound(mind, turnId) {
|
|
4939
5110
|
const db = await getDb();
|
|
4940
5111
|
const range = await db.select({
|
|
4941
|
-
minId:
|
|
4942
|
-
maxId:
|
|
4943
|
-
}).from(mindHistory).where(
|
|
5112
|
+
minId: sql4`MIN(${mindHistory.id})`,
|
|
5113
|
+
maxId: sql4`MAX(${mindHistory.id})`
|
|
5114
|
+
}).from(mindHistory).where(and5(eq7(mindHistory.mind, mind), eq7(mindHistory.turn_id, turnId)));
|
|
4944
5115
|
const minId = range[0]?.minId;
|
|
4945
5116
|
const maxId = range[0]?.maxId;
|
|
4946
5117
|
if (minId == null || maxId == null) return;
|
|
4947
5118
|
const orphans = await db.select({ id: mindHistory.id, message_id: mindHistory.message_id }).from(mindHistory).where(
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
5119
|
+
and5(
|
|
5120
|
+
eq7(mindHistory.mind, mind),
|
|
5121
|
+
eq7(mindHistory.type, "outbound"),
|
|
5122
|
+
sql4`${mindHistory.turn_id} IS NULL`,
|
|
5123
|
+
sql4`${mindHistory.id} >= ${minId}`,
|
|
5124
|
+
sql4`${mindHistory.id} <= ${maxId}`
|
|
4954
5125
|
)
|
|
4955
5126
|
);
|
|
4956
5127
|
if (orphans.length === 0) return;
|
|
@@ -4959,10 +5130,10 @@ async function tagUntaggedOutbound(mind, turnId) {
|
|
|
4959
5130
|
const orphansWithMessages = orphans.filter((o) => o.message_id);
|
|
4960
5131
|
if (orphansWithMessages.length > 0) {
|
|
4961
5132
|
const toolUseEvents = await db.select({ id: mindHistory.id }).from(mindHistory).where(
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
5133
|
+
and5(
|
|
5134
|
+
eq7(mindHistory.mind, mind),
|
|
5135
|
+
eq7(mindHistory.turn_id, turnId),
|
|
5136
|
+
eq7(mindHistory.type, "tool_use")
|
|
4966
5137
|
)
|
|
4967
5138
|
).orderBy(mindHistory.id);
|
|
4968
5139
|
for (const orphan of orphansWithMessages) {
|
|
@@ -4976,7 +5147,7 @@ async function tagUntaggedOutbound(mind, turnId) {
|
|
|
4976
5147
|
await db.update(messages).set({
|
|
4977
5148
|
turn_id: turnId,
|
|
4978
5149
|
...sourceEventId != null ? { source_event_id: sourceEventId } : {}
|
|
4979
|
-
}).where(
|
|
5150
|
+
}).where(eq7(messages.id, Number(orphan.message_id)));
|
|
4980
5151
|
}
|
|
4981
5152
|
}
|
|
4982
5153
|
dlog2.info(`tagged ${orphans.length} orphaned outbound record(s) for ${mind} with turn ${turnId}`);
|
|
@@ -4990,22 +5161,22 @@ async function tagUntaggedInbound(mind, turnId, {
|
|
|
4990
5161
|
const tagHistoryPromise = (async () => {
|
|
4991
5162
|
if (!channel) return;
|
|
4992
5163
|
const historyConditions = [
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
5164
|
+
eq7(mindHistory.mind, mind),
|
|
5165
|
+
eq7(mindHistory.type, "inbound"),
|
|
5166
|
+
sql4`${mindHistory.turn_id} IS NULL`,
|
|
5167
|
+
sql4`${mindHistory.created_at} > datetime('now', '-60 seconds')`,
|
|
5168
|
+
eq7(mindHistory.channel, channel)
|
|
4998
5169
|
];
|
|
4999
|
-
const recentInbounds = await db.select({ id: mindHistory.id }).from(mindHistory).where(
|
|
5170
|
+
const recentInbounds = await db.select({ id: mindHistory.id }).from(mindHistory).where(and5(...historyConditions)).orderBy(desc2(mindHistory.id)).limit(limit);
|
|
5000
5171
|
if (recentInbounds.length > 0) {
|
|
5001
5172
|
const ids = recentInbounds.map((r) => r.id);
|
|
5002
5173
|
await db.update(mindHistory).set({ turn_id: turnId }).where(inArray3(mindHistory.id, ids));
|
|
5003
5174
|
if (setTrigger) {
|
|
5004
|
-
await db.update(turns).set({ trigger_event_id: recentInbounds[0].id }).where(
|
|
5175
|
+
await db.update(turns).set({ trigger_event_id: recentInbounds[0].id }).where(eq7(turns.id, turnId));
|
|
5005
5176
|
}
|
|
5006
5177
|
}
|
|
5007
5178
|
})();
|
|
5008
|
-
const mindUserPromise = db.select({ id: users.id }).from(users).where(
|
|
5179
|
+
const mindUserPromise = db.select({ id: users.id }).from(users).where(and5(eq7(users.username, mind), eq7(users.user_type, "mind"))).get();
|
|
5009
5180
|
const [tagResult, mindUserResult] = await Promise.allSettled([
|
|
5010
5181
|
tagHistoryPromise,
|
|
5011
5182
|
mindUserPromise
|
|
@@ -5014,13 +5185,13 @@ async function tagUntaggedInbound(mind, turnId, {
|
|
|
5014
5185
|
dlog2.error("failed to tag history inbound", logger_default.errorData(tagResult.reason));
|
|
5015
5186
|
}
|
|
5016
5187
|
const mindUser = mindUserResult.status === "fulfilled" ? mindUserResult.value : void 0;
|
|
5017
|
-
const mindConvIds = mindUser ? (await db.select({ conversation_id: conversationParticipants.conversation_id }).from(conversationParticipants).where(
|
|
5188
|
+
const mindConvIds = mindUser ? (await db.select({ conversation_id: conversationParticipants.conversation_id }).from(conversationParticipants).where(eq7(conversationParticipants.user_id, mindUser.id))).map((r) => r.conversation_id) : [];
|
|
5018
5189
|
const recentMsgs = mindConvIds.length > 0 ? await db.select({ id: messages.id }).from(messages).where(
|
|
5019
|
-
|
|
5190
|
+
and5(
|
|
5020
5191
|
inArray3(messages.conversation_id, mindConvIds),
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5192
|
+
sql4`${messages.turn_id} IS NULL`,
|
|
5193
|
+
sql4`${messages.sender_name} != ${mind}`,
|
|
5194
|
+
sql4`${messages.created_at} > datetime('now', '-60 seconds')`
|
|
5024
5195
|
)
|
|
5025
5196
|
).orderBy(desc2(messages.id)).limit(limit) : [];
|
|
5026
5197
|
if (recentMsgs.length > 0) {
|
|
@@ -5081,7 +5252,7 @@ async function deliverMessage(mindName, payload) {
|
|
|
5081
5252
|
}
|
|
5082
5253
|
|
|
5083
5254
|
// packages/daemon/src/lib/chat/system-chat.ts
|
|
5084
|
-
var
|
|
5255
|
+
var slog4 = logger_default.child("system-chat");
|
|
5085
5256
|
var dmCache = /* @__PURE__ */ new Map();
|
|
5086
5257
|
function resetSystemDMCache() {
|
|
5087
5258
|
dmCache.clear();
|
|
@@ -5149,7 +5320,7 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
5149
5320
|
});
|
|
5150
5321
|
return;
|
|
5151
5322
|
} catch (err) {
|
|
5152
|
-
|
|
5323
|
+
slog4.warn(`failed to route to spirit, falling back to aiCompleteUtility`, logger_default.errorData(err));
|
|
5153
5324
|
}
|
|
5154
5325
|
}
|
|
5155
5326
|
const entry = await findMind(mindName);
|
|
@@ -5170,7 +5341,7 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
5170
5341
|
if (config.sleep.schedule?.wake) contextParts.push(`Wake cron: ${config.sleep.schedule.wake}`);
|
|
5171
5342
|
}
|
|
5172
5343
|
try {
|
|
5173
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5344
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-27S7MKJO.js");
|
|
5174
5345
|
const sm = getSleepManagerIfReady2();
|
|
5175
5346
|
if (sm) {
|
|
5176
5347
|
const state = sm.getState(mindName);
|
|
@@ -5179,7 +5350,7 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
5179
5350
|
}
|
|
5180
5351
|
}
|
|
5181
5352
|
} catch (err) {
|
|
5182
|
-
|
|
5353
|
+
slog4.debug("could not retrieve sleep state for system reply", logger_default.errorData(err));
|
|
5183
5354
|
}
|
|
5184
5355
|
try {
|
|
5185
5356
|
const schedules = config?.schedules;
|
|
@@ -5192,12 +5363,12 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
5192
5363
|
}
|
|
5193
5364
|
}
|
|
5194
5365
|
} catch (err) {
|
|
5195
|
-
|
|
5366
|
+
slog4.debug("could not retrieve schedules for system reply", logger_default.errorData(err));
|
|
5196
5367
|
}
|
|
5197
5368
|
const systemPrompt = contextParts.join("\n");
|
|
5198
5369
|
const response = await aiCompleteUtility(systemPrompt, message);
|
|
5199
5370
|
if (!response) {
|
|
5200
|
-
|
|
5371
|
+
slog4.warn(`no AI model available for system reply to ${mindName}`);
|
|
5201
5372
|
const fallback = "I can't reply right now \u2014 no AI model is configured for system responses. An admin can set one up in Settings.";
|
|
5202
5373
|
await addMessage(conversationId, "assistant", "volute", [{ type: "text", text: fallback }]);
|
|
5203
5374
|
return;
|
|
@@ -5227,13 +5398,20 @@ export {
|
|
|
5227
5398
|
getPromptIfCustom,
|
|
5228
5399
|
getMindPromptDefaults,
|
|
5229
5400
|
RotatingLog,
|
|
5401
|
+
syncProviderToMinds,
|
|
5230
5402
|
resolveMindToken,
|
|
5403
|
+
recordNotice,
|
|
5404
|
+
drainNotices,
|
|
5405
|
+
clearDeliveredNotices,
|
|
5406
|
+
formatNotices,
|
|
5231
5407
|
RestartTracker,
|
|
5232
5408
|
subscribe2 as subscribe,
|
|
5233
5409
|
subscribeAll,
|
|
5234
5410
|
publish3 as publish,
|
|
5235
5411
|
summarizeTurn,
|
|
5236
5412
|
initSummarizer,
|
|
5413
|
+
markErrored,
|
|
5414
|
+
takeErrored,
|
|
5237
5415
|
createTurn,
|
|
5238
5416
|
getActiveTurnId,
|
|
5239
5417
|
trackToolUse,
|