volute 0.39.0 → 0.40.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{accept-AHAOUFBK.js → accept-IVHOHXQS.js} +1 -1
- package/dist/{activity-events-CR4RIHJT.js → activity-events-DS4QRWBY.js} +4 -4
- package/dist/{ai-service-PL7GKFQC.js → ai-service-AUWIPWLP.js} +9 -5
- package/dist/{api-client-LC5YRA32.js → api-client-NFM6GX2N.js} +1 -1
- package/dist/{archive-UEWIFRAR.js → archive-UDHFZN4G.js} +4 -4
- package/dist/{auth-OJ2YW3NR.js → auth-X6ZITZ7D.js} +5 -5
- package/dist/{bridge-F3ZJEKDN.js → bridge-MSZC4HUT.js} +1 -1
- package/dist/{chat-RQ5NV5EA.js → chat-JD3TJINV.js} +9 -9
- package/dist/{chunk-CJ26DXZL.js → chunk-2HZRJ35V.js} +17 -1
- package/dist/{chunk-QDWPM73D.js → chunk-3Y7UDHO7.js} +3 -3
- package/dist/{chunk-6V2U3GFQ.js → chunk-4UMXOMZ6.js} +2 -2
- package/dist/{chunk-7OS7QFDF.js → chunk-5EVW4KOD.js} +3 -3
- package/dist/{chunk-6FSSMAXU.js → chunk-6SYHHEQ7.js} +1 -1
- package/dist/{chunk-OEXOFBW5.js → chunk-AEAZRTEV.js} +1 -1
- package/dist/{chunk-DZ4LYU6J.js → chunk-ANSTTB55.js} +1 -1
- package/dist/{chunk-QDWP435B.js → chunk-B4WPXYPI.js} +1 -1
- package/dist/{chunk-CHCISTH7.js → chunk-BLAXARBZ.js} +511 -335
- package/dist/{chunk-TCNCRDB5.js → chunk-DNU6XLE2.js} +1 -1
- package/dist/{chunk-7W3UWCQD.js → chunk-E25SO3QW.js} +3 -3
- package/dist/{chunk-IA2SZSLB.js → chunk-I57LLNAQ.js} +1 -1
- package/dist/{chunk-WUH54G5S.js → chunk-ITH5ZLZJ.js} +1 -1
- package/dist/{chunk-QJ4GU5XT.js → chunk-JYQQDI3M.js} +2 -2
- package/dist/{chunk-EVKGKQ2P.js → chunk-KMHC36CA.js} +1 -7
- package/dist/chunk-LMMQX4CK.js +49 -0
- package/dist/{chunk-4BO4I7UF.js → chunk-LZ6CQYRO.js} +7 -7
- package/dist/{chunk-RAVQXKAG.js → chunk-MNUXL4XN.js} +67 -3
- package/dist/{chunk-OS5MRIEP.js → chunk-NERYUSER.js} +1 -1
- package/dist/{chunk-XOSAY7PY.js → chunk-PKGA23XM.js} +1 -1
- package/dist/{chunk-IBOU6Q3Y.js → chunk-QV7P3TYO.js} +5 -5
- package/dist/{chunk-7GZB3ZJP.js → chunk-QYI65HCL.js} +2 -2
- package/dist/{chunk-B7MWXFAH.js → chunk-S4SXBQ7F.js} +15 -1
- package/dist/{chunk-X7GM2OPM.js → chunk-SVXOBKQY.js} +2 -2
- package/dist/{chunk-Y5UC6G3O.js → chunk-TOANT6FD.js} +2 -2
- package/dist/{chunk-TT4WT6Y7.js → chunk-YHWGN4FX.js} +1 -1
- package/dist/{chunk-Y4W3OS7Q.js → chunk-YTNBO7S6.js} +8 -8
- package/dist/cli.js +24 -24
- package/dist/{clock-BMLJ2TR6.js → clock-YLDJUKBW.js} +3 -3
- package/dist/{cloud-sync-NQCWJLYI.js → cloud-sync-MAQCJEFJ.js} +17 -17
- package/dist/{config-QB7W3Z7P.js → config-PO4EZLIK.js} +4 -4
- package/dist/connectors/discord-bridge.js +1 -1
- package/dist/connectors/slack-bridge.js +1 -1
- package/dist/connectors/telegram-bridge.js +1 -1
- package/dist/{conversations-EUXHBWJD.js → conversations-AYLXPOGC.js} +5 -5
- package/dist/{create-3QH7DVLC.js → create-T4UJ64WB.js} +4 -4
- package/dist/{create-C3BBFYV7.js → create-ZMTGINV3.js} +1 -1
- package/dist/{daemon-client-2MIPKY3E.js → daemon-client-H6MEKK2W.js} +1 -1
- package/dist/{daemon-restart-UEB3PLKB.js → daemon-restart-LP2DR24J.js} +9 -9
- package/dist/daemon.js +424 -294
- package/dist/{db-IJL6B26S.js → db-FSYOTHSN.js} +1 -1
- package/dist/db-ZEPKEJJD.js +9 -0
- package/dist/{delete-NLXES2C7.js → delete-TXVOH2OC.js} +3 -3
- package/dist/delivery-manager-VNHLQTTO.js +30 -0
- package/dist/{delivery-router-EUW4RIWG.js → delivery-router-T6CYTS3M.js} +4 -4
- package/dist/down-6DYO2QV7.js +17 -0
- package/dist/echo-text-QFCH3H6G.js +31 -0
- package/dist/{env-IQ6Q2333.js → env-77IOWG4U.js} +1 -1
- package/dist/exec-5DGKQ5V3.js +17 -0
- package/dist/{export-JDAK3THX.js → export-TZKOCSCE.js} +6 -6
- package/dist/{extension-LZYHBNLV.js → extension-35NB6GHW.js} +1 -1
- package/dist/{extensions-V7TXKAKM.js → extensions-LQX2N7TI.js} +17 -17
- package/dist/{files-K5XV23H6.js → files-WNNVJWJB.js} +4 -4
- package/dist/{import-GZ5IYVV7.js → import-BNNFCQ7U.js} +11 -11
- package/dist/{isolation-FPF4UTH2.js → isolation-4Q4P7HRV.js} +4 -4
- package/dist/{join-6SZCA5FX.js → join-E2EJEXZO.js} +3 -3
- package/dist/{list-KHJZJPEJ.js → list-BYCEDASJ.js} +1 -1
- package/dist/{login-GYTH67ES.js → login-JNSIYFVH.js} +1 -1
- package/dist/{login-BGF5JY3I.js → login-Y6UAYQVO.js} +3 -3
- package/dist/{logout-YHQLOFLR.js → logout-M6FI62BL.js} +1 -1
- package/dist/{logout-GQHI6KH6.js → logout-VHGWVH63.js} +3 -3
- package/dist/message-delivery-7BCYJLH2.js +40 -0
- package/dist/{mind-QMJIWWWW.js → mind-ELPD6VBP.js} +19 -19
- package/dist/{mind-activity-tracker-Z3KBQTVU.js → mind-activity-tracker-XFRVGXFS.js} +5 -5
- package/dist/{mind-history-WHCNZ6I5.js → mind-history-W4JPTPTV.js} +1 -1
- package/dist/{mind-list-H3HC2ZRG.js → mind-list-QROHCLGT.js} +1 -1
- package/dist/mind-manager-EDCLALBF.js +30 -0
- package/dist/{mind-profile-7VYRJGFZ.js → mind-profile-3LNKXNPA.js} +2 -2
- package/dist/mind-service-WTOFVNLW.js +36 -0
- package/dist/{mind-sleep-ZL5ZXFTM.js → mind-sleep-PVCMLTAH.js} +1 -1
- package/dist/{mind-status-ZWULKOUO.js → mind-status-RAJFLRCN.js} +1 -1
- package/dist/{mind-wake-HK5ORGUK.js → mind-wake-HZUOVE7J.js} +1 -1
- package/dist/{package-IR4ZEQA2.js → package-GO3PHHUF.js} +28 -27
- package/dist/{read-CP7MYMJQ.js → read-OOINVR2H.js} +1 -1
- package/dist/{read-stdin-4B5UYPPM.js → read-stdin-P6HCZUAA.js} +1 -1
- package/dist/{register-XOBFEMI4.js → register-ISOH34RF.js} +1 -1
- package/dist/{registry-LWEOAO53.js → registry-GV35XFT7.js} +3 -3
- package/dist/{reject-43AGXB6B.js → reject-ZBWP62X5.js} +1 -1
- package/dist/{restart-O5QIYQJT.js → restart-QBJV5455.js} +1 -1
- package/dist/{sandbox-KUAVC37A.js → sandbox-5R32QKWQ.js} +5 -5
- package/dist/scheduler-GQBDCGLU.js +30 -0
- package/dist/{schema-K575EBPE.js → schema-LYQUDL7G.js} +4 -2
- package/dist/{seed-GPBERPXJ.js → seed-MO2C7CWC.js} +2 -2
- package/dist/{seed-check-HZPVFJKZ.js → seed-check-3WXXRHTC.js} +2 -2
- package/dist/{seed-cmd-PWF4FYFG.js → seed-cmd-ETIF4WD6.js} +4 -4
- package/dist/{seed-create-NTIICZVB.js → seed-create-BPQC2IOR.js} +6 -6
- package/dist/{seed-sprout-PRPFKGJF.js → seed-sprout-TX72K33T.js} +11 -11
- package/dist/{send-IMHDYJHW.js → send-WO3CVG73.js} +4 -4
- package/dist/{service-DWBRRFXS.js → service-WMV6R6VJ.js} +6 -6
- package/dist/service-install-W3FQXJKB.js +17 -0
- package/dist/{setup-IFYS3TLS.js → setup-OTEC3ZPM.js} +4 -4
- package/dist/{setup-4CWQ53VV.js → setup-ZTW4FVMQ.js} +9 -9
- package/dist/{skill-ICN6Y2ZF.js → skill-QHACP7EU.js} +1 -1
- package/dist/{skills-FJ5W73AV.js → skills-JEWMHIIE.js} +7 -7
- package/dist/sleep-manager-5277OBHI.js +34 -0
- package/dist/{spirit-U6MDQLYT.js → spirit-NVXWSVKM.js} +9 -9
- package/dist/{split-5YBEQTBF.js → split-7AO4IO42.js} +3 -3
- package/dist/{sprout-ABX2G76C.js → sprout-NFJZKYEW.js} +2 -2
- package/dist/{src-OYWRPLC6.js → src-2IMB3V27.js} +1 -1
- package/dist/src-OS75U2O7.js +241910 -0
- package/dist/{src-O4PRLMKM.js → src-XNUXP2FP.js} +1 -1
- package/dist/{start-LMXXRR3X.js → start-2U57BBK7.js} +1 -1
- package/dist/{status-C6BBWNU3.js → status-BRYYYO7C.js} +7 -7
- package/dist/{stop-TWDKVEUX.js → stop-QILBDUAR.js} +1 -1
- package/dist/system-chat-LQSJVY55.js +34 -0
- package/dist/{systems-Y2WZV2K4.js → systems-3XTAHA35.js} +4 -4
- package/dist/{tailscale-2KKXDMNK.js → tailscale-X4HRKRTW.js} +3 -3
- package/dist/{template-hash-SSIBEEYK.js → template-hash-DMHSLRDX.js} +1 -1
- package/dist/up-4H7TL6WB.js +19 -0
- package/dist/{update-V26PT4VN.js → update-OY2JYXYK.js} +61 -14
- package/dist/update-check-IUB7QHAF.js +24 -0
- package/dist/{upgrade-RXFZR5FI.js → upgrade-25J6AE47.js} +3 -3
- package/dist/{variant-HHDTW74J.js → variant-5LQOPCQA.js} +1 -1
- package/dist/{version-notify-ZBMY2TEX.js → version-notify-N37N2C4G.js} +18 -18
- package/dist/{volute-config-TS62GS6A.js → volute-config-7FO6QKWU.js} +1 -1
- package/dist/web-assets/assets/index-DaUp9iE2.js +79 -0
- package/dist/web-assets/assets/index-DqHSW9Fr.css +1 -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 +27 -26
- package/packages/extensions/notes/dist/ui/assets/index-BbgANhFm.js +67 -0
- package/packages/extensions/notes/dist/ui/index.html +1 -1
- package/packages/extensions/pages/dist/ui/assets/index-Dxk-DYrf.js +2 -0
- package/packages/extensions/pages/dist/ui/index.html +1 -1
- package/packages/extensions/plan/dist/ui/assets/index-Cfzs-0G9.js +67 -0
- package/packages/extensions/plan/dist/ui/index.html +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/package.json.tmpl +5 -5
- package/templates/claude/src/agent.ts +54 -16
- package/templates/claude/src/server.ts +1 -0
- package/templates/codex/package.json.tmpl +5 -5
- package/templates/codex/src/agent.ts +45 -12
- package/templates/codex/src/server.ts +1 -0
- package/templates/pi/package.json.tmpl +6 -6
- package/templates/pi/src/agent.ts +74 -13
- package/templates/pi/src/lib/subagents.ts +19 -17
- package/templates/pi/src/server.ts +1 -0
- package/dist/chunk-K3NQKI34.js +0 -10
- package/dist/db-32YFKMFL.js +0 -9
- package/dist/delivery-manager-N34XKUGL.js +0 -30
- package/dist/down-TCEBGTMY.js +0 -17
- package/dist/echo-text-N3BC3WNX.js +0 -31
- package/dist/exec-2MFHTGOS.js +0 -17
- package/dist/message-delivery-X7MKA3TA.js +0 -40
- package/dist/mind-manager-7GWTTKWD.js +0 -30
- package/dist/mind-service-MO2XJKJN.js +0 -36
- package/dist/scheduler-CGUPAEBA.js +0 -30
- package/dist/service-install-KEBDNZVE.js +0 -17
- package/dist/sleep-manager-6QRMUNHK.js +0 -34
- package/dist/src-6Z4XYDH5.js +0 -2164
- package/dist/system-chat-D343HYGR.js +0 -34
- package/dist/up-ZU6ILVMA.js +0 -19
- package/dist/update-check-XRERV36Y.js +0 -18
- package/dist/web-assets/assets/index-B1zwqwt8.css +0 -1
- package/dist/web-assets/assets/index-D1guJF1i.js +0 -73
- package/packages/extensions/notes/dist/ui/assets/index-Dsua7alJ.js +0 -61
- package/packages/extensions/pages/dist/ui/assets/index-BM7gRdg7.js +0 -2
- package/packages/extensions/plan/dist/ui/assets/index-DWiqtzBv.js +0 -61
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadMergedEnv
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ITH5ZLZJ.js";
|
|
5
5
|
import {
|
|
6
6
|
spiritDir
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YTNBO7S6.js";
|
|
8
8
|
import {
|
|
9
9
|
hashSkillDir,
|
|
10
10
|
importSkillFromDir,
|
|
11
11
|
removeSharedSkill,
|
|
12
12
|
sharedSkillsDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-QV7P3TYO.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-3Y7UDHO7.js";
|
|
24
24
|
import {
|
|
25
25
|
readVoluteConfig,
|
|
26
26
|
writeVoluteConfig
|
|
@@ -28,46 +28,46 @@ import {
|
|
|
28
28
|
import {
|
|
29
29
|
isSandboxEnabled,
|
|
30
30
|
wrapForSandbox
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-KMHC36CA.js";
|
|
32
32
|
import {
|
|
33
33
|
markIdle,
|
|
34
34
|
onMindEvent
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-ANSTTB55.js";
|
|
36
36
|
import {
|
|
37
37
|
extractTextContent,
|
|
38
38
|
getRoutingConfig,
|
|
39
39
|
resolveDeliveryMode,
|
|
40
40
|
resolveRoute
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-NERYUSER.js";
|
|
42
42
|
import {
|
|
43
43
|
exec
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-AEAZRTEV.js";
|
|
45
45
|
import {
|
|
46
46
|
chownMindDir,
|
|
47
47
|
isIsolationEnabled,
|
|
48
48
|
mindUserName,
|
|
49
49
|
wrapForIsolation
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-6SYHHEQ7.js";
|
|
51
|
+
import {
|
|
52
|
+
aiCompleteUtility,
|
|
53
|
+
getAiConfig,
|
|
54
|
+
resolveApiKey
|
|
55
|
+
} from "./chunk-S4SXBQ7F.js";
|
|
56
|
+
import {
|
|
57
|
+
readGlobalConfig,
|
|
58
|
+
writeGlobalConfig
|
|
59
|
+
} from "./chunk-DNU6XLE2.js";
|
|
51
60
|
import {
|
|
52
61
|
getOrCreateMindUser,
|
|
53
62
|
getOrCreateSystemUser,
|
|
54
63
|
getUser,
|
|
55
64
|
getUserByUsername,
|
|
56
65
|
syncMindProfile
|
|
57
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-E25SO3QW.js";
|
|
58
67
|
import {
|
|
59
68
|
publish,
|
|
60
69
|
subscribe
|
|
61
|
-
} from "./chunk-
|
|
62
|
-
import {
|
|
63
|
-
aiCompleteUtility,
|
|
64
|
-
getAiConfig,
|
|
65
|
-
resolveApiKey
|
|
66
|
-
} from "./chunk-B7MWXFAH.js";
|
|
67
|
-
import {
|
|
68
|
-
readGlobalConfig,
|
|
69
|
-
writeGlobalConfig
|
|
70
|
-
} from "./chunk-TCNCRDB5.js";
|
|
70
|
+
} from "./chunk-QYI65HCL.js";
|
|
71
71
|
import {
|
|
72
72
|
logger_default
|
|
73
73
|
} from "./chunk-T2TP6ZC6.js";
|
|
@@ -80,37 +80,38 @@ import {
|
|
|
80
80
|
stateDir,
|
|
81
81
|
voluteHome,
|
|
82
82
|
voluteSystemDir
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-B4WPXYPI.js";
|
|
84
84
|
import {
|
|
85
85
|
activity,
|
|
86
86
|
conversationParticipants,
|
|
87
87
|
deliveryQueue,
|
|
88
88
|
messages,
|
|
89
89
|
mindHistory,
|
|
90
|
+
mindNotices,
|
|
90
91
|
summaries,
|
|
91
92
|
systemPrompts,
|
|
92
93
|
turns,
|
|
93
94
|
users
|
|
94
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-2HZRJ35V.js";
|
|
95
96
|
|
|
96
97
|
// packages/daemon/src/lib/delivery/message-delivery.ts
|
|
97
|
-
import { and as
|
|
98
|
+
import { and as and5, desc as desc2, eq as eq7, inArray as inArray3, sql as sql4 } from "drizzle-orm";
|
|
98
99
|
|
|
99
100
|
// packages/daemon/src/lib/daemon/sleep-manager.ts
|
|
100
101
|
import { execFile as execFile2, spawn as spawnChild } from "child_process";
|
|
101
102
|
import {
|
|
102
|
-
existsSync as
|
|
103
|
-
mkdirSync as
|
|
103
|
+
existsSync as existsSync8,
|
|
104
|
+
mkdirSync as mkdirSync5,
|
|
104
105
|
readdirSync as readdirSync2,
|
|
105
|
-
readFileSync as
|
|
106
|
+
readFileSync as readFileSync7,
|
|
106
107
|
readlinkSync,
|
|
107
108
|
renameSync,
|
|
108
|
-
writeFileSync as
|
|
109
|
+
writeFileSync as writeFileSync6
|
|
109
110
|
} from "fs";
|
|
110
|
-
import { resolve as
|
|
111
|
+
import { resolve as resolve7 } from "path";
|
|
111
112
|
import { promisify as promisify2 } from "util";
|
|
112
113
|
import { CronExpressionParser as CronExpressionParser2 } from "cron-parser";
|
|
113
|
-
import { and as
|
|
114
|
+
import { and as and3, eq as eq5, inArray as inArray2 } from "drizzle-orm";
|
|
114
115
|
|
|
115
116
|
// packages/daemon/src/lib/prompts.ts
|
|
116
117
|
import { eq } from "drizzle-orm";
|
|
@@ -326,8 +327,8 @@ async function getMindPromptDefaults() {
|
|
|
326
327
|
|
|
327
328
|
// packages/daemon/src/lib/daemon/mind-manager.ts
|
|
328
329
|
import { execFile, spawn } from "child_process";
|
|
329
|
-
import { existsSync as
|
|
330
|
-
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";
|
|
331
332
|
import { promisify } from "util";
|
|
332
333
|
|
|
333
334
|
// packages/daemon/src/lib/util/json-state.ts
|
|
@@ -436,6 +437,78 @@ var RotatingLog = class extends Writable {
|
|
|
436
437
|
}
|
|
437
438
|
};
|
|
438
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
|
+
|
|
439
512
|
// packages/daemon/src/lib/daemon/mind-tokens.ts
|
|
440
513
|
import { randomUUID } from "crypto";
|
|
441
514
|
var tokenToMind = /* @__PURE__ */ new Map();
|
|
@@ -458,6 +531,74 @@ function resolveMindToken(token) {
|
|
|
458
531
|
return tokenToMind.get(token) ?? null;
|
|
459
532
|
}
|
|
460
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
|
+
|
|
461
602
|
// packages/daemon/src/lib/daemon/restart-tracker.ts
|
|
462
603
|
var DEFAULT_MAX_ATTEMPTS = 5;
|
|
463
604
|
var DEFAULT_BASE_DELAY = 3e3;
|
|
@@ -505,10 +646,10 @@ var RestartTracker = class {
|
|
|
505
646
|
|
|
506
647
|
// packages/daemon/src/lib/daemon/turn-tracker.ts
|
|
507
648
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
508
|
-
import { eq as
|
|
649
|
+
import { eq as eq4 } from "drizzle-orm";
|
|
509
650
|
|
|
510
651
|
// packages/daemon/src/lib/daemon/summarizer.ts
|
|
511
|
-
import { and, desc, eq as
|
|
652
|
+
import { and as and2, desc, eq as eq3, gte, inArray, like, lt, sql as sql2 } from "drizzle-orm";
|
|
512
653
|
|
|
513
654
|
// packages/daemon/src/lib/events/mind-events.ts
|
|
514
655
|
var subscribers = /* @__PURE__ */ new Map();
|
|
@@ -675,18 +816,18 @@ function getTimeRange(periodKey, period) {
|
|
|
675
816
|
async function gatherTurnEvents(mind, session, doneId) {
|
|
676
817
|
const db = await getDb();
|
|
677
818
|
const subConditions = [
|
|
678
|
-
|
|
679
|
-
|
|
819
|
+
eq3(mindHistory.mind, mind),
|
|
820
|
+
eq3(mindHistory.type, "done"),
|
|
680
821
|
lt(mindHistory.id, doneId)
|
|
681
822
|
];
|
|
682
|
-
if (session) subConditions.push(
|
|
683
|
-
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);
|
|
684
825
|
const turnConditions = [
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
826
|
+
eq3(mindHistory.mind, mind),
|
|
827
|
+
sql2`${mindHistory.id} > COALESCE((${prevDoneSubquery}), 0)`,
|
|
828
|
+
sql2`${mindHistory.id} <= ${doneId}`
|
|
688
829
|
];
|
|
689
|
-
if (session) turnConditions.push(
|
|
830
|
+
if (session) turnConditions.push(eq3(mindHistory.session, session));
|
|
690
831
|
const events = await db.select({
|
|
691
832
|
id: mindHistory.id,
|
|
692
833
|
type: mindHistory.type,
|
|
@@ -695,7 +836,7 @@ async function gatherTurnEvents(mind, session, doneId) {
|
|
|
695
836
|
content: mindHistory.content,
|
|
696
837
|
metadata: mindHistory.metadata,
|
|
697
838
|
created_at: mindHistory.created_at
|
|
698
|
-
}).from(mindHistory).where(
|
|
839
|
+
}).from(mindHistory).where(and2(...turnConditions)).orderBy(mindHistory.id);
|
|
699
840
|
return {
|
|
700
841
|
events,
|
|
701
842
|
fromId: events.length > 0 ? events[0].id : doneId,
|
|
@@ -712,7 +853,7 @@ async function gatherTurnEventsByTurnId(turnId) {
|
|
|
712
853
|
content: mindHistory.content,
|
|
713
854
|
metadata: mindHistory.metadata,
|
|
714
855
|
created_at: mindHistory.created_at
|
|
715
|
-
}).from(mindHistory).where(
|
|
856
|
+
}).from(mindHistory).where(eq3(mindHistory.turn_id, turnId)).orderBy(mindHistory.id);
|
|
716
857
|
return {
|
|
717
858
|
events,
|
|
718
859
|
fromId: events.length > 0 ? events[0].id : 0,
|
|
@@ -812,8 +953,8 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
812
953
|
if (turnId) {
|
|
813
954
|
try {
|
|
814
955
|
const db2 = await getDb();
|
|
815
|
-
await db2.update(mindHistory).set({ turn_id: null }).where(
|
|
816
|
-
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));
|
|
817
958
|
} catch (err) {
|
|
818
959
|
sLog.error(`failed to un-tag events for interrupted turn ${turnId}`, logger_default.errorData(err));
|
|
819
960
|
}
|
|
@@ -870,10 +1011,10 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
870
1011
|
summaryId = result[0]?.id;
|
|
871
1012
|
if (summaryId == null) {
|
|
872
1013
|
const existing = await db.select({ id: summaries.id }).from(summaries).where(
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1014
|
+
and2(
|
|
1015
|
+
eq3(summaries.mind, mind),
|
|
1016
|
+
eq3(summaries.period, "turn"),
|
|
1017
|
+
eq3(summaries.period_key, periodKey)
|
|
877
1018
|
)
|
|
878
1019
|
).get();
|
|
879
1020
|
summaryId = existing?.id;
|
|
@@ -902,7 +1043,7 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
902
1043
|
}
|
|
903
1044
|
async function setSummaryId(turnId, summaryId) {
|
|
904
1045
|
const db = await getDb();
|
|
905
|
-
await db.update(turns).set({ summary_id: summaryId }).where(
|
|
1046
|
+
await db.update(turns).set({ summary_id: summaryId }).where(eq3(turns.id, turnId));
|
|
906
1047
|
}
|
|
907
1048
|
function getChildPeriod(period) {
|
|
908
1049
|
switch (period) {
|
|
@@ -946,9 +1087,9 @@ async function gatherChildSummaries(mind, period, periodKey) {
|
|
|
946
1087
|
if (period === "hour") {
|
|
947
1088
|
const { start: start2, end: end2 } = getTimeRange(periodKey, "hour");
|
|
948
1089
|
const rows2 = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1090
|
+
and2(
|
|
1091
|
+
eq3(summaries.mind, mind),
|
|
1092
|
+
eq3(summaries.period, childPeriod),
|
|
952
1093
|
gte(summaries.created_at, start2),
|
|
953
1094
|
lt(summaries.created_at, end2)
|
|
954
1095
|
)
|
|
@@ -960,9 +1101,9 @@ async function gatherChildSummaries(mind, period, periodKey) {
|
|
|
960
1101
|
}
|
|
961
1102
|
if (period === "day") {
|
|
962
1103
|
const rows2 = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1104
|
+
and2(
|
|
1105
|
+
eq3(summaries.mind, mind),
|
|
1106
|
+
eq3(summaries.period, childPeriod),
|
|
966
1107
|
like(summaries.period_key, `${periodKey}%`)
|
|
967
1108
|
)
|
|
968
1109
|
).orderBy(summaries.period_key);
|
|
@@ -975,11 +1116,11 @@ async function gatherChildSummaries(mind, period, periodKey) {
|
|
|
975
1116
|
const startKey = start.slice(0, 10);
|
|
976
1117
|
const endKey = end.slice(0, 10);
|
|
977
1118
|
const rows = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1119
|
+
and2(
|
|
1120
|
+
eq3(summaries.mind, mind),
|
|
1121
|
+
eq3(summaries.period, childPeriod),
|
|
981
1122
|
gte(summaries.period_key, startKey),
|
|
982
|
-
|
|
1123
|
+
sql2`${summaries.period_key} <= ${endKey}`
|
|
983
1124
|
)
|
|
984
1125
|
).orderBy(summaries.period_key);
|
|
985
1126
|
return {
|
|
@@ -1059,10 +1200,10 @@ async function summarizeSystem(period, periodKey) {
|
|
|
1059
1200
|
if (await summaryExists(SYSTEM_MIND, period, periodKey)) return;
|
|
1060
1201
|
const db = await getDb();
|
|
1061
1202
|
const rows = await db.select({ mind: summaries.mind, content: summaries.content }).from(summaries).where(
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1203
|
+
and2(
|
|
1204
|
+
eq3(summaries.period, period),
|
|
1205
|
+
eq3(summaries.period_key, periodKey),
|
|
1206
|
+
sql2`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1066
1207
|
)
|
|
1067
1208
|
).orderBy(summaries.mind);
|
|
1068
1209
|
if (rows.length === 0) return;
|
|
@@ -1097,11 +1238,11 @@ async function summarizeSystem(period, periodKey) {
|
|
|
1097
1238
|
async function mindsWithTurnSummaries(start, end) {
|
|
1098
1239
|
const db = await getDb();
|
|
1099
1240
|
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
1100
|
-
|
|
1101
|
-
|
|
1241
|
+
and2(
|
|
1242
|
+
eq3(summaries.period, "turn"),
|
|
1102
1243
|
gte(summaries.created_at, start),
|
|
1103
1244
|
lt(summaries.created_at, end),
|
|
1104
|
-
|
|
1245
|
+
sql2`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1105
1246
|
)
|
|
1106
1247
|
).groupBy(summaries.mind);
|
|
1107
1248
|
return rows.map((r) => r.mind);
|
|
@@ -1109,10 +1250,10 @@ async function mindsWithTurnSummaries(start, end) {
|
|
|
1109
1250
|
async function mindsWithSummaries(period, keyPattern) {
|
|
1110
1251
|
const db = await getDb();
|
|
1111
1252
|
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
1112
|
-
|
|
1113
|
-
|
|
1253
|
+
and2(
|
|
1254
|
+
eq3(summaries.period, period),
|
|
1114
1255
|
like(summaries.period_key, keyPattern),
|
|
1115
|
-
|
|
1256
|
+
sql2`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1116
1257
|
)
|
|
1117
1258
|
).groupBy(summaries.mind);
|
|
1118
1259
|
return rows.map((r) => r.mind);
|
|
@@ -1120,11 +1261,11 @@ async function mindsWithSummaries(period, keyPattern) {
|
|
|
1120
1261
|
async function mindsWithDailySummariesInRange(startKey, endKey) {
|
|
1121
1262
|
const db = await getDb();
|
|
1122
1263
|
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
1123
|
-
|
|
1124
|
-
|
|
1264
|
+
and2(
|
|
1265
|
+
eq3(summaries.period, "day"),
|
|
1125
1266
|
gte(summaries.period_key, startKey),
|
|
1126
|
-
|
|
1127
|
-
|
|
1267
|
+
sql2`${summaries.period_key} <= ${endKey}`,
|
|
1268
|
+
sql2`${summaries.mind} != ${SYSTEM_MIND}`
|
|
1128
1269
|
)
|
|
1129
1270
|
).groupBy(summaries.mind);
|
|
1130
1271
|
return rows.map((r) => r.mind);
|
|
@@ -1188,10 +1329,10 @@ async function processMonth(periodKey) {
|
|
|
1188
1329
|
async function summaryExists(mind, period, periodKey) {
|
|
1189
1330
|
const db = await getDb();
|
|
1190
1331
|
const row = await db.select({ id: summaries.id }).from(summaries).where(
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1332
|
+
and2(
|
|
1333
|
+
eq3(summaries.mind, mind),
|
|
1334
|
+
eq3(summaries.period, period),
|
|
1335
|
+
eq3(summaries.period_key, periodKey)
|
|
1195
1336
|
)
|
|
1196
1337
|
).get();
|
|
1197
1338
|
return !!row;
|
|
@@ -1221,7 +1362,7 @@ async function backfill() {
|
|
|
1221
1362
|
}
|
|
1222
1363
|
const allKeys = candidates.map((c) => c.key);
|
|
1223
1364
|
const db = await getDb();
|
|
1224
|
-
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)));
|
|
1225
1366
|
const existingSet = new Set(existingRows.map((r) => `${r.period}:${r.period_key}`));
|
|
1226
1367
|
const processFn = {
|
|
1227
1368
|
hour: processHour,
|
|
@@ -1298,6 +1439,13 @@ var activeTurns = /* @__PURE__ */ new Map();
|
|
|
1298
1439
|
function key(mind, session) {
|
|
1299
1440
|
return `${mind}:${session ?? "*"}`;
|
|
1300
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
|
+
}
|
|
1301
1449
|
async function createTurn(mind) {
|
|
1302
1450
|
const k = key(mind);
|
|
1303
1451
|
const existing = activeTurns.get(k);
|
|
@@ -1334,7 +1482,7 @@ async function assignSession(mind, turnId, session) {
|
|
|
1334
1482
|
}
|
|
1335
1483
|
try {
|
|
1336
1484
|
const db = await getDb();
|
|
1337
|
-
await db.update(turns).set({ session }).where(
|
|
1485
|
+
await db.update(turns).set({ session }).where(eq4(turns.id, turnId));
|
|
1338
1486
|
} catch (err) {
|
|
1339
1487
|
tlog.error(`failed to assign session to turn ${turnId}`, logger_default.errorData(err));
|
|
1340
1488
|
return;
|
|
@@ -1349,7 +1497,7 @@ async function completeTurn(mind, session) {
|
|
|
1349
1497
|
if (!entry) return void 0;
|
|
1350
1498
|
try {
|
|
1351
1499
|
const db = await getDb();
|
|
1352
|
-
await db.update(turns).set({ status: "complete" }).where(
|
|
1500
|
+
await db.update(turns).set({ status: "complete" }).where(eq4(turns.id, entry.turnId));
|
|
1353
1501
|
} catch (err) {
|
|
1354
1502
|
tlog.error(`failed to complete turn ${entry.turnId}`, logger_default.errorData(err));
|
|
1355
1503
|
return void 0;
|
|
@@ -1360,10 +1508,10 @@ async function completeTurn(mind, session) {
|
|
|
1360
1508
|
}
|
|
1361
1509
|
async function completeOrphanedTurns() {
|
|
1362
1510
|
const db = await getDb();
|
|
1363
|
-
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"));
|
|
1364
1512
|
if (active.length === 0) return [];
|
|
1365
1513
|
try {
|
|
1366
|
-
await db.update(turns).set({ status: "complete" }).where(
|
|
1514
|
+
await db.update(turns).set({ status: "complete" }).where(eq4(turns.status, "active"));
|
|
1367
1515
|
} catch (err) {
|
|
1368
1516
|
tlog.error("failed to complete orphaned turns on startup", logger_default.errorData(err));
|
|
1369
1517
|
}
|
|
@@ -1389,11 +1537,14 @@ async function clearMind(mind) {
|
|
|
1389
1537
|
}
|
|
1390
1538
|
}
|
|
1391
1539
|
for (const k of toDelete) activeTurns.delete(k);
|
|
1540
|
+
for (const k of [...erroredSessions]) {
|
|
1541
|
+
if (k.startsWith(`${mind}:`)) erroredSessions.delete(k);
|
|
1542
|
+
}
|
|
1392
1543
|
if (orphaned.length > 0) {
|
|
1393
1544
|
try {
|
|
1394
1545
|
const db = await getDb();
|
|
1395
1546
|
for (const { turnId } of orphaned) {
|
|
1396
|
-
await db.update(turns).set({ status: "complete" }).where(
|
|
1547
|
+
await db.update(turns).set({ status: "complete" }).where(eq4(turns.id, turnId));
|
|
1397
1548
|
}
|
|
1398
1549
|
} catch (err) {
|
|
1399
1550
|
tlog.error(`failed to complete orphaned turns for ${mind}`, logger_default.errorData(err));
|
|
@@ -1413,7 +1564,7 @@ function summarizeOrphanedTurns(orphanedTurns) {
|
|
|
1413
1564
|
var mlog = logger_default.child("minds");
|
|
1414
1565
|
var execFileAsync = promisify(execFile);
|
|
1415
1566
|
function mindPidPath(name) {
|
|
1416
|
-
return
|
|
1567
|
+
return resolve2(stateDir(name), "mind.pid");
|
|
1417
1568
|
}
|
|
1418
1569
|
var MindManager = class {
|
|
1419
1570
|
minds = /* @__PURE__ */ new Map();
|
|
@@ -1429,7 +1580,7 @@ var MindManager = class {
|
|
|
1429
1580
|
return { dir: entry.dir, port: entry.port, baseName: entry.parent, template: entry.template };
|
|
1430
1581
|
}
|
|
1431
1582
|
const dir = entry.dir ?? mindDir(name);
|
|
1432
|
-
if (!
|
|
1583
|
+
if (!existsSync4(dir)) throw new Error(`Mind directory missing: ${dir}`);
|
|
1433
1584
|
return { dir, port: entry.port, baseName: name, template: entry.template };
|
|
1434
1585
|
}
|
|
1435
1586
|
async startMind(name) {
|
|
@@ -1441,8 +1592,8 @@ var MindManager = class {
|
|
|
1441
1592
|
const port = target.port;
|
|
1442
1593
|
const pidFile = mindPidPath(name);
|
|
1443
1594
|
try {
|
|
1444
|
-
if (
|
|
1445
|
-
const stalePid = parseInt(
|
|
1595
|
+
if (existsSync4(pidFile)) {
|
|
1596
|
+
const stalePid = parseInt(readFileSync3(pidFile, "utf-8").trim(), 10);
|
|
1446
1597
|
if (stalePid > 0) {
|
|
1447
1598
|
try {
|
|
1448
1599
|
process.kill(stalePid, 0);
|
|
@@ -1475,8 +1626,8 @@ var MindManager = class {
|
|
|
1475
1626
|
} catch {
|
|
1476
1627
|
}
|
|
1477
1628
|
const mindStateDir = stateDir(name);
|
|
1478
|
-
const logsDir =
|
|
1479
|
-
|
|
1629
|
+
const logsDir = resolve2(mindStateDir, "logs");
|
|
1630
|
+
mkdirSync2(logsDir, { recursive: true });
|
|
1480
1631
|
if (isIsolationEnabled()) {
|
|
1481
1632
|
try {
|
|
1482
1633
|
chownMindDir(mindStateDir, baseName);
|
|
@@ -1486,10 +1637,10 @@ var MindManager = class {
|
|
|
1486
1637
|
);
|
|
1487
1638
|
}
|
|
1488
1639
|
}
|
|
1489
|
-
const logStream = new RotatingLog(
|
|
1640
|
+
const logStream = new RotatingLog(resolve2(logsDir, "mind.log"));
|
|
1490
1641
|
const mindToken = generateMindToken(name);
|
|
1491
1642
|
const mindEnv = loadMergedEnv(name);
|
|
1492
|
-
const mindLocalBin =
|
|
1643
|
+
const mindLocalBin = resolve2(dir, "home", ".local", "bin");
|
|
1493
1644
|
const currentPath = process.env.PATH ?? "";
|
|
1494
1645
|
const env = {
|
|
1495
1646
|
...process.env,
|
|
@@ -1505,24 +1656,30 @@ var MindManager = class {
|
|
|
1505
1656
|
};
|
|
1506
1657
|
if (target.template === "pi") {
|
|
1507
1658
|
try {
|
|
1508
|
-
const configPath2 =
|
|
1509
|
-
if (
|
|
1510
|
-
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"));
|
|
1511
1662
|
const modelStr = config.model;
|
|
1512
1663
|
if (modelStr?.includes(":")) {
|
|
1513
1664
|
const provider = modelStr.split(":")[0];
|
|
1514
1665
|
const apiKey = await resolveApiKey(provider);
|
|
1515
1666
|
if (apiKey) {
|
|
1516
|
-
const piAgentDir =
|
|
1517
|
-
|
|
1518
|
-
const authPath = resolve(piAgentDir, "auth.json");
|
|
1519
|
-
const authData = existsSync3(authPath) ? JSON.parse(readFileSync2(authPath, "utf-8")) : {};
|
|
1520
|
-
authData[provider] = { type: "api_key", key: apiKey };
|
|
1521
|
-
writeFileSync2(authPath, JSON.stringify(authData, null, 2), { mode: 384 });
|
|
1522
|
-
if (isIsolationEnabled()) {
|
|
1523
|
-
chownMindDir(piAgentDir, baseName);
|
|
1524
|
-
}
|
|
1667
|
+
const piAgentDir = resolve2(dir, ".mind", "pi-agent");
|
|
1668
|
+
writePiProviderKey(piAgentDir, baseName, provider, apiKey);
|
|
1525
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;
|
|
1526
1683
|
} else {
|
|
1527
1684
|
mlog.warn(
|
|
1528
1685
|
`no API key found for provider "${provider}" \u2014 mind ${name} may fail to start`
|
|
@@ -1539,11 +1696,11 @@ var MindManager = class {
|
|
|
1539
1696
|
const ai = getAiConfig();
|
|
1540
1697
|
const codexConfig = ai?.providers["openai-codex"];
|
|
1541
1698
|
if (codexConfig?.oauth) {
|
|
1542
|
-
const codexDir =
|
|
1543
|
-
|
|
1699
|
+
const codexDir = resolve2(dir, ".mind", "codex");
|
|
1700
|
+
mkdirSync2(codexDir, { recursive: true });
|
|
1544
1701
|
env.CODEX_HOME = codexDir;
|
|
1545
|
-
const authPath =
|
|
1546
|
-
|
|
1702
|
+
const authPath = resolve2(codexDir, "auth.json");
|
|
1703
|
+
writeFileSync3(
|
|
1547
1704
|
authPath,
|
|
1548
1705
|
JSON.stringify({
|
|
1549
1706
|
auth_mode: "chatgpt",
|
|
@@ -1556,9 +1713,9 @@ var MindManager = class {
|
|
|
1556
1713
|
}),
|
|
1557
1714
|
{ mode: 384 }
|
|
1558
1715
|
);
|
|
1559
|
-
const configTomlPath =
|
|
1560
|
-
if (!
|
|
1561
|
-
|
|
1716
|
+
const configTomlPath = resolve2(codexDir, "config.toml");
|
|
1717
|
+
if (!existsSync4(configTomlPath)) {
|
|
1718
|
+
writeFileSync3(configTomlPath, 'cli_auth_credentials_store = "file"\n');
|
|
1562
1719
|
}
|
|
1563
1720
|
if (isIsolationEnabled()) {
|
|
1564
1721
|
chownMindDir(codexDir, baseName);
|
|
@@ -1577,10 +1734,10 @@ var MindManager = class {
|
|
|
1577
1734
|
env.OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
|
1578
1735
|
}
|
|
1579
1736
|
}
|
|
1580
|
-
const homeDir =
|
|
1737
|
+
const homeDir = resolve2(dir, "home");
|
|
1581
1738
|
const zshenvLines = Object.entries(env).filter(([k, v]) => k.startsWith("VOLUTE_") && v != null).map(([k, v]) => `export ${k}=${JSON.stringify(v)}`);
|
|
1582
1739
|
zshenvLines.push(`export PATH=${JSON.stringify(env.PATH ?? "")}`);
|
|
1583
|
-
|
|
1740
|
+
writeFileSync3(resolve2(homeDir, ".zshenv"), `${zshenvLines.join("\n")}
|
|
1584
1741
|
`, { mode: 384 });
|
|
1585
1742
|
}
|
|
1586
1743
|
if (target.template === "claude" || !target.template) {
|
|
@@ -1589,27 +1746,10 @@ var MindManager = class {
|
|
|
1589
1746
|
const anthropicConfig = ai?.providers.anthropic;
|
|
1590
1747
|
if (anthropicConfig?.oauth) {
|
|
1591
1748
|
const key2 = await resolveApiKey("anthropic");
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
const claudeDir =
|
|
1595
|
-
mkdirSync(claudeDir, { recursive: true });
|
|
1749
|
+
const oauth = getAiConfig()?.providers.anthropic?.oauth;
|
|
1750
|
+
if (key2 && oauth) {
|
|
1751
|
+
const claudeDir = writeClaudeCredentials(resolve2(dir, "home"), baseName, oauth);
|
|
1596
1752
|
env.CLAUDE_CONFIG_DIR = claudeDir;
|
|
1597
|
-
const credsPath = resolve(claudeDir, ".credentials.json");
|
|
1598
|
-
writeFileSync2(
|
|
1599
|
-
credsPath,
|
|
1600
|
-
JSON.stringify({
|
|
1601
|
-
claudeAiOauth: {
|
|
1602
|
-
accessToken: key2,
|
|
1603
|
-
refreshToken: anthropicConfig.oauth.refresh,
|
|
1604
|
-
expiresAt: anthropicConfig.oauth.expires ? new Date(anthropicConfig.oauth.expires).toISOString() : null,
|
|
1605
|
-
scopes: ["user:inference", "user:profile"]
|
|
1606
|
-
}
|
|
1607
|
-
}),
|
|
1608
|
-
{ mode: 384 }
|
|
1609
|
-
);
|
|
1610
|
-
if (isIsolationEnabled()) {
|
|
1611
|
-
chownMindDir(claudeDir, baseName);
|
|
1612
|
-
}
|
|
1613
1753
|
}
|
|
1614
1754
|
} else if (anthropicConfig?.apiKey) {
|
|
1615
1755
|
env.ANTHROPIC_API_KEY = anthropicConfig.apiKey;
|
|
@@ -1619,7 +1759,7 @@ var MindManager = class {
|
|
|
1619
1759
|
}
|
|
1620
1760
|
}
|
|
1621
1761
|
if (isIsolationEnabled()) {
|
|
1622
|
-
env.HOME =
|
|
1762
|
+
env.HOME = resolve2(dir, "home");
|
|
1623
1763
|
}
|
|
1624
1764
|
const customNode = process.env.VOLUTE_NODE_PATH;
|
|
1625
1765
|
let baseBin;
|
|
@@ -1627,13 +1767,13 @@ var MindManager = class {
|
|
|
1627
1767
|
if (customNode) {
|
|
1628
1768
|
baseBin = customNode;
|
|
1629
1769
|
baseArgs = [
|
|
1630
|
-
|
|
1770
|
+
resolve2(dir, "node_modules", ".bin", "tsx"),
|
|
1631
1771
|
"src/server.ts",
|
|
1632
1772
|
"--port",
|
|
1633
1773
|
String(port)
|
|
1634
1774
|
];
|
|
1635
1775
|
} else {
|
|
1636
|
-
baseBin =
|
|
1776
|
+
baseBin = resolve2(dir, "node_modules", ".bin", "tsx");
|
|
1637
1777
|
baseArgs = ["src/server.ts", "--port", String(port)];
|
|
1638
1778
|
}
|
|
1639
1779
|
let spawnCmd;
|
|
@@ -1667,14 +1807,14 @@ var MindManager = class {
|
|
|
1667
1807
|
while (recentStderr.length > 20) recentStderr.shift();
|
|
1668
1808
|
});
|
|
1669
1809
|
try {
|
|
1670
|
-
await new Promise((
|
|
1810
|
+
await new Promise((resolve9, reject) => {
|
|
1671
1811
|
const timeout = setTimeout(() => {
|
|
1672
1812
|
reject(new Error(`Mind ${name} did not start within 30s`));
|
|
1673
1813
|
}, 3e4);
|
|
1674
1814
|
function checkOutput(data) {
|
|
1675
1815
|
if (data.toString().match(/listening on :\d+/)) {
|
|
1676
1816
|
clearTimeout(timeout);
|
|
1677
|
-
|
|
1817
|
+
resolve9();
|
|
1678
1818
|
}
|
|
1679
1819
|
}
|
|
1680
1820
|
child.stdout?.on("data", checkOutput);
|
|
@@ -1702,7 +1842,7 @@ var MindManager = class {
|
|
|
1702
1842
|
}
|
|
1703
1843
|
if (child.pid) {
|
|
1704
1844
|
try {
|
|
1705
|
-
|
|
1845
|
+
writeFileSync3(pidFile, String(child.pid));
|
|
1706
1846
|
} catch (err) {
|
|
1707
1847
|
mlog.warn(`failed to write PID file for ${name}`, logger_default.errorData(err));
|
|
1708
1848
|
}
|
|
@@ -1770,7 +1910,7 @@ var MindManager = class {
|
|
|
1770
1910
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
1771
1911
|
mlog.error(`mind ${name} exited with code ${code}`);
|
|
1772
1912
|
try {
|
|
1773
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
1913
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-5277OBHI.js");
|
|
1774
1914
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
1775
1915
|
if (sleepState?.sleeping) {
|
|
1776
1916
|
mlog.info(`${name} is sleeping \u2014 skipping crash recovery`);
|
|
@@ -1779,19 +1919,30 @@ var MindManager = class {
|
|
|
1779
1919
|
} catch (err) {
|
|
1780
1920
|
mlog.warn(`failed to check sleep state for ${name}`, logger_default.errorData(err));
|
|
1781
1921
|
}
|
|
1782
|
-
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(
|
|
1783
1934
|
(err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
|
|
1784
1935
|
);
|
|
1785
1936
|
try {
|
|
1786
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
1937
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-VNHLQTTO.js");
|
|
1787
1938
|
getDeliveryManager2().clearMindSessions(name);
|
|
1788
1939
|
} catch (err) {
|
|
1789
1940
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
1790
1941
|
mlog.warn(`failed to clear delivery state for ${name} after crash`, logger_default.errorData(err));
|
|
1791
1942
|
}
|
|
1792
1943
|
}
|
|
1793
|
-
import("./mind-activity-tracker-
|
|
1794
|
-
import("./activity-events-
|
|
1944
|
+
import("./mind-activity-tracker-XFRVGXFS.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-DS4QRWBY.js").then(
|
|
1795
1946
|
({ publish: publish4 }) => publish4({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
|
|
1796
1947
|
).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
|
|
1797
1948
|
const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
|
|
@@ -1818,19 +1969,19 @@ var MindManager = class {
|
|
|
1818
1969
|
this.stopping.add(name);
|
|
1819
1970
|
const { child } = tracked;
|
|
1820
1971
|
this.minds.delete(name);
|
|
1821
|
-
await new Promise((
|
|
1822
|
-
child.on("exit", () =>
|
|
1972
|
+
await new Promise((resolve9) => {
|
|
1973
|
+
child.on("exit", () => resolve9());
|
|
1823
1974
|
try {
|
|
1824
1975
|
process.kill(-child.pid, "SIGTERM");
|
|
1825
1976
|
} catch {
|
|
1826
|
-
|
|
1977
|
+
resolve9();
|
|
1827
1978
|
}
|
|
1828
1979
|
setTimeout(() => {
|
|
1829
1980
|
try {
|
|
1830
1981
|
process.kill(-child.pid, "SIGKILL");
|
|
1831
1982
|
} catch {
|
|
1832
1983
|
}
|
|
1833
|
-
|
|
1984
|
+
resolve9();
|
|
1834
1985
|
}, 5e3);
|
|
1835
1986
|
});
|
|
1836
1987
|
this.stopping.delete(name);
|
|
@@ -1842,7 +1993,7 @@ var MindManager = class {
|
|
|
1842
1993
|
mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
|
|
1843
1994
|
}
|
|
1844
1995
|
try {
|
|
1845
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
1996
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-VNHLQTTO.js");
|
|
1846
1997
|
getDeliveryManager2().clearMindSessions(name);
|
|
1847
1998
|
} catch (err) {
|
|
1848
1999
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
@@ -1850,7 +2001,7 @@ var MindManager = class {
|
|
|
1850
2001
|
}
|
|
1851
2002
|
}
|
|
1852
2003
|
try {
|
|
1853
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
2004
|
+
const { clearEchoTextCache } = await import("./echo-text-QFCH3H6G.js");
|
|
1854
2005
|
clearEchoTextCache(name);
|
|
1855
2006
|
} catch (err) {
|
|
1856
2007
|
mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
|
|
@@ -1878,7 +2029,7 @@ var MindManager = class {
|
|
|
1878
2029
|
return [...this.minds.keys()];
|
|
1879
2030
|
}
|
|
1880
2031
|
get crashAttemptsPath() {
|
|
1881
|
-
return
|
|
2032
|
+
return resolve2(voluteSystemDir(), "crash-attempts.json");
|
|
1882
2033
|
}
|
|
1883
2034
|
loadCrashAttempts() {
|
|
1884
2035
|
this.restartTracker.load(loadJsonMap(this.crashAttemptsPath));
|
|
@@ -1976,20 +2127,20 @@ async function announceToSystem(text) {
|
|
|
1976
2127
|
}
|
|
1977
2128
|
|
|
1978
2129
|
// packages/daemon/src/lib/extensions.ts
|
|
1979
|
-
import { existsSync as
|
|
1980
|
-
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";
|
|
1981
2132
|
|
|
1982
2133
|
// packages/daemon/src/lib/config/systems-config.ts
|
|
1983
|
-
import { existsSync as
|
|
1984
|
-
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";
|
|
1985
2136
|
var DEFAULT_API_URL = "https://volute.systems";
|
|
1986
2137
|
function configPath() {
|
|
1987
|
-
return
|
|
2138
|
+
return resolve3(voluteSystemDir(), "systems.json");
|
|
1988
2139
|
}
|
|
1989
2140
|
function readSystemsConfig() {
|
|
1990
2141
|
const path = configPath();
|
|
1991
|
-
if (!
|
|
1992
|
-
const raw =
|
|
2142
|
+
if (!existsSync5(path)) return null;
|
|
2143
|
+
const raw = readFileSync4(path, "utf-8");
|
|
1993
2144
|
let data;
|
|
1994
2145
|
try {
|
|
1995
2146
|
data = JSON.parse(raw);
|
|
@@ -2007,8 +2158,8 @@ function readSystemsConfig() {
|
|
|
2007
2158
|
};
|
|
2008
2159
|
}
|
|
2009
2160
|
function writeSystemsConfig(config) {
|
|
2010
|
-
|
|
2011
|
-
|
|
2161
|
+
mkdirSync3(voluteSystemDir(), { recursive: true });
|
|
2162
|
+
writeFileSync4(configPath(), `${JSON.stringify(config, null, 2)}
|
|
2012
2163
|
`, { mode: 384 });
|
|
2013
2164
|
}
|
|
2014
2165
|
function deleteSystemsConfig() {
|
|
@@ -2067,19 +2218,19 @@ function parseCommandArgs(rawArgs, argDefs, flagDefs) {
|
|
|
2067
2218
|
return { args: namedArgs, flags, rest };
|
|
2068
2219
|
}
|
|
2069
2220
|
function extensionsBaseDir() {
|
|
2070
|
-
return
|
|
2221
|
+
return resolve4(voluteHome(), "extensions");
|
|
2071
2222
|
}
|
|
2072
2223
|
function extensionDataDir(id) {
|
|
2073
|
-
return
|
|
2224
|
+
return resolve4(voluteSystemDir(), "extension-data", id);
|
|
2074
2225
|
}
|
|
2075
2226
|
function extensionsConfigPath() {
|
|
2076
|
-
return
|
|
2227
|
+
return resolve4(voluteHome(), "system", "extensions.json");
|
|
2077
2228
|
}
|
|
2078
2229
|
function readExtensionsConfig() {
|
|
2079
2230
|
const configPath2 = extensionsConfigPath();
|
|
2080
|
-
if (!
|
|
2231
|
+
if (!existsSync6(configPath2)) return [];
|
|
2081
2232
|
try {
|
|
2082
|
-
const data = JSON.parse(
|
|
2233
|
+
const data = JSON.parse(readFileSync5(configPath2, "utf-8"));
|
|
2083
2234
|
return Array.isArray(data) ? data : [];
|
|
2084
2235
|
} catch (err) {
|
|
2085
2236
|
logger_default.warn("failed to read extensions config, ignoring installed extensions", {
|
|
@@ -2097,7 +2248,7 @@ async function getLibsqlDatabase() {
|
|
|
2097
2248
|
return _LibsqlDatabase;
|
|
2098
2249
|
}
|
|
2099
2250
|
async function openExtensionDb(_id, dataDir) {
|
|
2100
|
-
const dbPath =
|
|
2251
|
+
const dbPath = resolve4(dataDir, "data.db");
|
|
2101
2252
|
const Database = await getLibsqlDatabase();
|
|
2102
2253
|
return new Database(dbPath);
|
|
2103
2254
|
}
|
|
@@ -2139,7 +2290,7 @@ async function buildContext(manifest, dataDir, authMw) {
|
|
|
2139
2290
|
getMindDir: (name) => {
|
|
2140
2291
|
try {
|
|
2141
2292
|
const dir = mindDir(name);
|
|
2142
|
-
return
|
|
2293
|
+
return existsSync6(dir) ? dir : null;
|
|
2143
2294
|
} catch (err) {
|
|
2144
2295
|
logger_default.warn(
|
|
2145
2296
|
`extension ${manifest.id}: failed to resolve mind dir for ${name}`,
|
|
@@ -2161,7 +2312,7 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2161
2312
|
return;
|
|
2162
2313
|
}
|
|
2163
2314
|
const dataDir = extensionDataDir(manifest.id);
|
|
2164
|
-
|
|
2315
|
+
mkdirSync4(dataDir, { recursive: true });
|
|
2165
2316
|
const context = await buildContext(manifest, dataDir, authMw);
|
|
2166
2317
|
const routesApp = manifest.routes(context);
|
|
2167
2318
|
const extApiPath = `/api/ext/${manifest.id}`;
|
|
@@ -2224,18 +2375,18 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2224
2375
|
}
|
|
2225
2376
|
}
|
|
2226
2377
|
let resolvedAssetsDir = manifest.ui?.assetsDir ?? "";
|
|
2227
|
-
if (resolvedAssetsDir && !
|
|
2378
|
+
if (resolvedAssetsDir && !existsSync6(resolvedAssetsDir)) {
|
|
2228
2379
|
let searchDir = dirname(new URL(import.meta.url).pathname);
|
|
2229
2380
|
for (let i = 0; i < 5; i++) {
|
|
2230
|
-
const candidate =
|
|
2231
|
-
if (
|
|
2381
|
+
const candidate = resolve4(searchDir, "packages", "extensions", manifest.id, "dist", "ui");
|
|
2382
|
+
if (existsSync6(candidate)) {
|
|
2232
2383
|
resolvedAssetsDir = candidate;
|
|
2233
2384
|
break;
|
|
2234
2385
|
}
|
|
2235
2386
|
searchDir = dirname(searchDir);
|
|
2236
2387
|
}
|
|
2237
2388
|
}
|
|
2238
|
-
if (resolvedAssetsDir &&
|
|
2389
|
+
if (resolvedAssetsDir && existsSync6(resolvedAssetsDir)) {
|
|
2239
2390
|
const assetsDir = resolvedAssetsDir;
|
|
2240
2391
|
const { readFile: readFile2, stat: fsStat } = await import("fs/promises");
|
|
2241
2392
|
const { extname: ext } = await import("path");
|
|
@@ -2252,11 +2403,11 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2252
2403
|
".woff2": "font/woff2"
|
|
2253
2404
|
};
|
|
2254
2405
|
const prefix = `/ext/${manifest.id}`;
|
|
2255
|
-
const indexPath =
|
|
2406
|
+
const indexPath = resolve4(assetsDir, "index.html");
|
|
2256
2407
|
const serveExtAssets = async (c) => {
|
|
2257
2408
|
const urlPath = new URL(c.req.url).pathname;
|
|
2258
2409
|
const relativePath = urlPath.slice(prefix.length).replace(/^\//, "") || "index.html";
|
|
2259
|
-
const filePath =
|
|
2410
|
+
const filePath = resolve4(assetsDir, relativePath);
|
|
2260
2411
|
if (filePath !== assetsDir && !filePath.startsWith(`${assetsDir}/`))
|
|
2261
2412
|
return c.text("Forbidden", 403);
|
|
2262
2413
|
const s = await fsStat(filePath).catch(() => null);
|
|
@@ -2265,7 +2416,7 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2265
2416
|
const body = await readFile2(filePath);
|
|
2266
2417
|
return c.body(body, 200, { "Content-Type": mime });
|
|
2267
2418
|
}
|
|
2268
|
-
if (
|
|
2419
|
+
if (existsSync6(indexPath)) {
|
|
2269
2420
|
const body = await readFile2(indexPath, "utf-8");
|
|
2270
2421
|
return c.html(body);
|
|
2271
2422
|
}
|
|
@@ -2286,10 +2437,10 @@ async function loadExtension(manifest, app, authMw) {
|
|
|
2286
2437
|
for (const entry of entries) {
|
|
2287
2438
|
if (!entry.isDirectory()) continue;
|
|
2288
2439
|
try {
|
|
2289
|
-
const skillPath =
|
|
2440
|
+
const skillPath = resolve4(skillsDir, entry.name);
|
|
2290
2441
|
const sourceHash = hashSkillDir(skillPath);
|
|
2291
|
-
const destDir =
|
|
2292
|
-
if (
|
|
2442
|
+
const destDir = resolve4(sharedSkillsDir(), entry.name);
|
|
2443
|
+
if (existsSync6(destDir)) {
|
|
2293
2444
|
const destHash = hashSkillDir(destDir);
|
|
2294
2445
|
if (sourceHash === destHash) continue;
|
|
2295
2446
|
}
|
|
@@ -2316,14 +2467,14 @@ function resolveSkillsDir(manifest) {
|
|
|
2316
2467
|
if (cached !== void 0) return cached;
|
|
2317
2468
|
let searchDir = dirname(new URL(import.meta.url).pathname);
|
|
2318
2469
|
for (let i = 0; i < 5; i++) {
|
|
2319
|
-
const candidate =
|
|
2320
|
-
if (
|
|
2470
|
+
const candidate = resolve4(searchDir, "packages", "extensions", manifest.id, "skills");
|
|
2471
|
+
if (existsSync6(candidate)) {
|
|
2321
2472
|
skillsDirCache.set(manifest.id, candidate);
|
|
2322
2473
|
return candidate;
|
|
2323
2474
|
}
|
|
2324
2475
|
searchDir = dirname(searchDir);
|
|
2325
2476
|
}
|
|
2326
|
-
if (
|
|
2477
|
+
if (existsSync6(manifest.skillsDir)) {
|
|
2327
2478
|
skillsDirCache.set(manifest.id, manifest.skillsDir);
|
|
2328
2479
|
return manifest.skillsDir;
|
|
2329
2480
|
}
|
|
@@ -2333,9 +2484,9 @@ function resolveSkillsDir(manifest) {
|
|
|
2333
2484
|
}
|
|
2334
2485
|
async function discoverBuiltinExtensions(disabledIds) {
|
|
2335
2486
|
const builtins = [
|
|
2336
|
-
{ id: "notes", load: async () => (await import("./src-
|
|
2337
|
-
{ id: "pages", load: async () => (await import("./src-
|
|
2338
|
-
{ id: "plan", load: async () => (await import("./src-
|
|
2487
|
+
{ id: "notes", load: async () => (await import("./src-2IMB3V27.js")).default },
|
|
2488
|
+
{ id: "pages", load: async () => (await import("./src-OS75U2O7.js")).default },
|
|
2489
|
+
{ id: "plan", load: async () => (await import("./src-XNUXP2FP.js")).default }
|
|
2339
2490
|
];
|
|
2340
2491
|
const results = [];
|
|
2341
2492
|
for (const { id, load } of builtins) {
|
|
@@ -2351,14 +2502,14 @@ async function discoverBuiltinExtensions(disabledIds) {
|
|
|
2351
2502
|
async function discoverInstalledExtensions() {
|
|
2352
2503
|
const results = [];
|
|
2353
2504
|
const packages = readExtensionsConfig();
|
|
2354
|
-
const npmDir =
|
|
2505
|
+
const npmDir = resolve4(voluteHome(), "extensions", "_npm");
|
|
2355
2506
|
const { createRequire } = await import("module");
|
|
2356
2507
|
for (const pkg of packages) {
|
|
2357
2508
|
try {
|
|
2358
2509
|
let resolved = pkg;
|
|
2359
|
-
const npmPkgDir =
|
|
2360
|
-
if (
|
|
2361
|
-
const require2 = createRequire(
|
|
2510
|
+
const npmPkgDir = resolve4(npmDir, "node_modules", pkg);
|
|
2511
|
+
if (existsSync6(npmPkgDir)) {
|
|
2512
|
+
const require2 = createRequire(resolve4(npmDir, "noop.js"));
|
|
2362
2513
|
resolved = require2.resolve(pkg);
|
|
2363
2514
|
}
|
|
2364
2515
|
const mod = await import(resolved);
|
|
@@ -2401,7 +2552,7 @@ function validateManifest(manifest, source) {
|
|
|
2401
2552
|
}
|
|
2402
2553
|
async function discoverLocalExtensions() {
|
|
2403
2554
|
const baseDir = extensionsBaseDir();
|
|
2404
|
-
if (!
|
|
2555
|
+
if (!existsSync6(baseDir)) return [];
|
|
2405
2556
|
const manifests = [];
|
|
2406
2557
|
let entries;
|
|
2407
2558
|
try {
|
|
@@ -2411,9 +2562,9 @@ async function discoverLocalExtensions() {
|
|
|
2411
2562
|
return [];
|
|
2412
2563
|
}
|
|
2413
2564
|
for (const dir of entries) {
|
|
2414
|
-
const extDir =
|
|
2415
|
-
const candidates = [
|
|
2416
|
-
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));
|
|
2417
2568
|
if (!entryPoint) continue;
|
|
2418
2569
|
try {
|
|
2419
2570
|
const mod = await import(entryPoint);
|
|
@@ -2548,21 +2699,21 @@ function setExtensionEnabled(id, enabled) {
|
|
|
2548
2699
|
writeGlobalConfig(config);
|
|
2549
2700
|
}
|
|
2550
2701
|
function extensionsNpmDir() {
|
|
2551
|
-
return
|
|
2702
|
+
return resolve4(voluteHome(), "extensions", "_npm");
|
|
2552
2703
|
}
|
|
2553
2704
|
function ensureExtensionsNpmDir() {
|
|
2554
2705
|
const dir = extensionsNpmDir();
|
|
2555
|
-
|
|
2556
|
-
const pkgPath =
|
|
2557
|
-
if (!
|
|
2558
|
-
|
|
2706
|
+
mkdirSync4(dir, { recursive: true });
|
|
2707
|
+
const pkgPath = resolve4(dir, "package.json");
|
|
2708
|
+
if (!existsSync6(pkgPath)) {
|
|
2709
|
+
writeFileSync5(pkgPath, '{"private":true,"dependencies":{}}\n');
|
|
2559
2710
|
}
|
|
2560
2711
|
return dir;
|
|
2561
2712
|
}
|
|
2562
2713
|
function writeExtensionsConfig(packages) {
|
|
2563
2714
|
const configPath2 = extensionsConfigPath();
|
|
2564
|
-
|
|
2565
|
-
|
|
2715
|
+
mkdirSync4(resolve4(configPath2, ".."), { recursive: true });
|
|
2716
|
+
writeFileSync5(configPath2, `${JSON.stringify(packages, null, 2)}
|
|
2566
2717
|
`);
|
|
2567
2718
|
}
|
|
2568
2719
|
var VALID_NPM_PACKAGE = /^(@[a-z0-9-~][a-z0-9._-~]*\/)?[a-z0-9-~][a-z0-9._-~]*(@[^\s]+)?$/;
|
|
@@ -2575,7 +2726,7 @@ async function installNpmExtension(pkg) {
|
|
|
2575
2726
|
throw new Error(`Extension "${pkg}" is already installed`);
|
|
2576
2727
|
}
|
|
2577
2728
|
const dir = ensureExtensionsNpmDir();
|
|
2578
|
-
const { exec: exec2 } = await import("./exec-
|
|
2729
|
+
const { exec: exec2 } = await import("./exec-5DGKQ5V3.js");
|
|
2579
2730
|
try {
|
|
2580
2731
|
await exec2("npm", ["install", pkg], { cwd: dir });
|
|
2581
2732
|
} catch (err) {
|
|
@@ -2596,7 +2747,7 @@ async function uninstallNpmExtension(pkg) {
|
|
|
2596
2747
|
packages.splice(idx, 1);
|
|
2597
2748
|
writeExtensionsConfig(packages);
|
|
2598
2749
|
try {
|
|
2599
|
-
const { exec: exec2 } = await import("./exec-
|
|
2750
|
+
const { exec: exec2 } = await import("./exec-5DGKQ5V3.js");
|
|
2600
2751
|
await exec2("npm", ["uninstall", pkg], { cwd: extensionsNpmDir() });
|
|
2601
2752
|
} catch (err) {
|
|
2602
2753
|
logger_default.warn(
|
|
@@ -2608,13 +2759,13 @@ async function uninstallNpmExtension(pkg) {
|
|
|
2608
2759
|
}
|
|
2609
2760
|
async function cleanupExtensionSkills(pkg) {
|
|
2610
2761
|
try {
|
|
2611
|
-
const pkgDir =
|
|
2612
|
-
if (!
|
|
2762
|
+
const pkgDir = resolve4(extensionsNpmDir(), "node_modules", pkg);
|
|
2763
|
+
if (!existsSync6(pkgDir)) return;
|
|
2613
2764
|
const { createRequire } = await import("module");
|
|
2614
|
-
const require2 = createRequire(
|
|
2765
|
+
const require2 = createRequire(resolve4(extensionsNpmDir(), "noop.js"));
|
|
2615
2766
|
const mod = require2(pkg);
|
|
2616
2767
|
const manifest = mod.default ?? mod.extension ?? mod;
|
|
2617
|
-
if (!manifest?.skillsDir || !
|
|
2768
|
+
if (!manifest?.skillsDir || !existsSync6(manifest.skillsDir)) return;
|
|
2618
2769
|
const skillDirs = readdirSync(manifest.skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
2619
2770
|
for (const skillId of skillDirs) {
|
|
2620
2771
|
try {
|
|
@@ -2923,9 +3074,9 @@ async function ensureMailAddress(mindName) {
|
|
|
2923
3074
|
}
|
|
2924
3075
|
|
|
2925
3076
|
// packages/daemon/src/lib/daemon/scheduler.ts
|
|
2926
|
-
import { resolve as
|
|
3077
|
+
import { resolve as resolve5 } from "path";
|
|
2927
3078
|
import { CronExpressionParser } from "cron-parser";
|
|
2928
|
-
var
|
|
3079
|
+
var slog2 = logger_default.child("scheduler");
|
|
2929
3080
|
var Scheduler = class {
|
|
2930
3081
|
schedules = /* @__PURE__ */ new Map();
|
|
2931
3082
|
mindDirs = /* @__PURE__ */ new Map();
|
|
@@ -2934,12 +3085,12 @@ var Scheduler = class {
|
|
|
2934
3085
|
lastFired = /* @__PURE__ */ new Map();
|
|
2935
3086
|
// "mind:scheduleId" → epoch minute
|
|
2936
3087
|
get statePath() {
|
|
2937
|
-
return
|
|
3088
|
+
return resolve5(voluteSystemDir(), "scheduler-state.json");
|
|
2938
3089
|
}
|
|
2939
3090
|
start() {
|
|
2940
3091
|
this.loadState();
|
|
2941
3092
|
this.interval = setInterval(() => {
|
|
2942
|
-
this.tick().catch((err) =>
|
|
3093
|
+
this.tick().catch((err) => slog2.error("scheduler tick failed", logger_default.errorData(err)));
|
|
2943
3094
|
}, 6e4);
|
|
2944
3095
|
}
|
|
2945
3096
|
stop() {
|
|
@@ -3006,7 +3157,7 @@ var Scheduler = class {
|
|
|
3006
3157
|
prevMinute = Math.floor(prev.getTime() / 6e4);
|
|
3007
3158
|
cronCache.set(schedule.cron, prevMinute);
|
|
3008
3159
|
} catch (err) {
|
|
3009
|
-
|
|
3160
|
+
slog2.warn(`invalid cron "${schedule.cron}" for ${mind}:${schedule.id}`, logger_default.errorData(err));
|
|
3010
3161
|
return false;
|
|
3011
3162
|
}
|
|
3012
3163
|
}
|
|
@@ -3020,11 +3171,11 @@ var Scheduler = class {
|
|
|
3020
3171
|
try {
|
|
3021
3172
|
let text;
|
|
3022
3173
|
if (schedule.script) {
|
|
3023
|
-
const homeDir =
|
|
3174
|
+
const homeDir = resolve5(this.mindDirs.get(mindName) ?? mindDir(mindName), "home");
|
|
3024
3175
|
try {
|
|
3025
3176
|
const output = await this.runScript(schedule.script, homeDir, mindName);
|
|
3026
3177
|
if (!output.trim()) {
|
|
3027
|
-
|
|
3178
|
+
slog2.info(`fired script "${schedule.id}" for ${mindName} (no output)`);
|
|
3028
3179
|
return;
|
|
3029
3180
|
}
|
|
3030
3181
|
text = output;
|
|
@@ -3032,24 +3183,24 @@ var Scheduler = class {
|
|
|
3032
3183
|
const stderr = err.stderr ?? "";
|
|
3033
3184
|
text = `[script error] ${err.message}${stderr ? `
|
|
3034
3185
|
${stderr}` : ""}`;
|
|
3035
|
-
|
|
3186
|
+
slog2.warn(`script "${schedule.id}" failed for ${mindName}`, logger_default.errorData(err));
|
|
3036
3187
|
}
|
|
3037
3188
|
} else if (schedule.message) {
|
|
3038
3189
|
text = schedule.message;
|
|
3039
3190
|
} else {
|
|
3040
|
-
|
|
3191
|
+
slog2.warn(`schedule "${schedule.id}" for ${mindName} has no message or script`);
|
|
3041
3192
|
return;
|
|
3042
3193
|
}
|
|
3043
3194
|
await this.deliverSystem(mindName, `[${schedule.id}] ${text}`, {
|
|
3044
3195
|
whileSleeping: schedule.whileSleeping,
|
|
3045
3196
|
session: schedule.session
|
|
3046
3197
|
});
|
|
3047
|
-
|
|
3198
|
+
slog2.info(`fired "${schedule.id}" for ${mindName}`);
|
|
3048
3199
|
if (schedule.fireAt) {
|
|
3049
3200
|
this.removeSchedule(mindName, schedule.id);
|
|
3050
3201
|
}
|
|
3051
3202
|
} catch (err) {
|
|
3052
|
-
|
|
3203
|
+
slog2.warn(`failed to fire "${schedule.id}" for ${mindName}`, logger_default.errorData(err));
|
|
3053
3204
|
}
|
|
3054
3205
|
}
|
|
3055
3206
|
removeSchedule(mindName, scheduleId) {
|
|
@@ -3069,9 +3220,9 @@ ${stderr}` : ""}`;
|
|
|
3069
3220
|
config.schedules = config.schedules.filter((s) => s.id !== scheduleId);
|
|
3070
3221
|
if (config.schedules.length === 0) config.schedules = void 0;
|
|
3071
3222
|
writeVoluteConfig(dir, config);
|
|
3072
|
-
|
|
3223
|
+
slog2.info(`removed one-time schedule "${scheduleId}" for ${mindName}`);
|
|
3073
3224
|
} catch (err) {
|
|
3074
|
-
|
|
3225
|
+
slog2.error(
|
|
3075
3226
|
`failed to persist removal of schedule "${scheduleId}" for ${mindName} (removed from memory)`,
|
|
3076
3227
|
logger_default.errorData(err)
|
|
3077
3228
|
);
|
|
@@ -3096,9 +3247,9 @@ function getScheduler() {
|
|
|
3096
3247
|
}
|
|
3097
3248
|
|
|
3098
3249
|
// packages/daemon/src/lib/daemon/token-budget.ts
|
|
3099
|
-
import { existsSync as
|
|
3250
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
3100
3251
|
import { mkdir, writeFile as writeFile2 } from "fs/promises";
|
|
3101
|
-
import { resolve as
|
|
3252
|
+
import { resolve as resolve6 } from "path";
|
|
3102
3253
|
var tlog2 = logger_default.child("token-budget");
|
|
3103
3254
|
var DEFAULT_BUDGET_PERIOD_MINUTES = 60;
|
|
3104
3255
|
var MAX_QUEUE_SIZE = 100;
|
|
@@ -3135,7 +3286,8 @@ var TokenBudget = class {
|
|
|
3135
3286
|
periodMinutes,
|
|
3136
3287
|
tokenLimit,
|
|
3137
3288
|
queue: [],
|
|
3138
|
-
warningInjected: false
|
|
3289
|
+
warningInjected: false,
|
|
3290
|
+
exceededNotified: false
|
|
3139
3291
|
});
|
|
3140
3292
|
}
|
|
3141
3293
|
}
|
|
@@ -3163,6 +3315,20 @@ var TokenBudget = class {
|
|
|
3163
3315
|
const state = this.budgets.get(mind);
|
|
3164
3316
|
if (state) state.warningInjected = true;
|
|
3165
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
|
+
}
|
|
3166
3332
|
enqueue(mind, message) {
|
|
3167
3333
|
const state = this.budgets.get(mind);
|
|
3168
3334
|
if (!state) return;
|
|
@@ -3198,6 +3364,7 @@ var TokenBudget = class {
|
|
|
3198
3364
|
state.tokensUsed = 0;
|
|
3199
3365
|
state.periodStart = now;
|
|
3200
3366
|
state.warningInjected = false;
|
|
3367
|
+
state.exceededNotified = false;
|
|
3201
3368
|
this.dirty.add(mind);
|
|
3202
3369
|
const queued = this.drain(mind);
|
|
3203
3370
|
if (queued.length > 0) {
|
|
@@ -3221,7 +3388,7 @@ var TokenBudget = class {
|
|
|
3221
3388
|
await Promise.all(writes);
|
|
3222
3389
|
}
|
|
3223
3390
|
budgetStatePath(mind) {
|
|
3224
|
-
return
|
|
3391
|
+
return resolve6(stateDir(mind), "budget.json");
|
|
3225
3392
|
}
|
|
3226
3393
|
async saveBudgetState(mind, state) {
|
|
3227
3394
|
try {
|
|
@@ -3231,6 +3398,7 @@ var TokenBudget = class {
|
|
|
3231
3398
|
periodStart: state.periodStart,
|
|
3232
3399
|
tokensUsed: state.tokensUsed,
|
|
3233
3400
|
warningInjected: state.warningInjected,
|
|
3401
|
+
exceededNotified: state.exceededNotified,
|
|
3234
3402
|
queue: state.queue
|
|
3235
3403
|
};
|
|
3236
3404
|
await writeFile2(this.budgetStatePath(mind), `${JSON.stringify(data)}
|
|
@@ -3242,13 +3410,14 @@ var TokenBudget = class {
|
|
|
3242
3410
|
loadBudgetState(mind) {
|
|
3243
3411
|
try {
|
|
3244
3412
|
const path = this.budgetStatePath(mind);
|
|
3245
|
-
if (!
|
|
3246
|
-
const data = JSON.parse(
|
|
3413
|
+
if (!existsSync7(path)) return null;
|
|
3414
|
+
const data = JSON.parse(readFileSync6(path, "utf-8"));
|
|
3247
3415
|
if (typeof data.periodStart !== "number" || typeof data.tokensUsed !== "number") return null;
|
|
3248
3416
|
return {
|
|
3249
3417
|
periodStart: data.periodStart,
|
|
3250
3418
|
tokensUsed: data.tokensUsed,
|
|
3251
3419
|
warningInjected: data.warningInjected ?? false,
|
|
3420
|
+
exceededNotified: data.exceededNotified ?? false,
|
|
3252
3421
|
queue: Array.isArray(data.queue) ? data.queue : [],
|
|
3253
3422
|
periodMinutes: 0,
|
|
3254
3423
|
// will be overwritten by caller
|
|
@@ -3371,7 +3540,7 @@ async function wakeMind(name) {
|
|
|
3371
3540
|
async function startSpiritFull(name) {
|
|
3372
3541
|
const entry = await findMind(name);
|
|
3373
3542
|
if (entry?.dir) {
|
|
3374
|
-
const { registerMindDir } = await import("./delivery-router-
|
|
3543
|
+
const { registerMindDir } = await import("./delivery-router-T6CYTS3M.js");
|
|
3375
3544
|
registerMindDir(name, entry.dir);
|
|
3376
3545
|
}
|
|
3377
3546
|
await getMindManager().startMind(name);
|
|
@@ -3393,8 +3562,8 @@ async function stopSpiritFull(name) {
|
|
|
3393
3562
|
}).catch((err) => logger_default.error("failed to publish spirit_stopped activity", logger_default.errorData(err)));
|
|
3394
3563
|
}
|
|
3395
3564
|
async function ensureCreatorDM(mindName, creatorUsername) {
|
|
3396
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-
|
|
3397
|
-
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-
|
|
3565
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-X6ZITZ7D.js");
|
|
3566
|
+
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-AYLXPOGC.js");
|
|
3398
3567
|
const mindUser = await getOrCreateMindUser2(mindName);
|
|
3399
3568
|
const creatorUser = await getUserByUsername2(creatorUsername);
|
|
3400
3569
|
if (!creatorUser) {
|
|
@@ -3426,7 +3595,7 @@ async function stopMindFull(name) {
|
|
|
3426
3595
|
}
|
|
3427
3596
|
|
|
3428
3597
|
// packages/daemon/src/lib/daemon/sleep-manager.ts
|
|
3429
|
-
var
|
|
3598
|
+
var slog3 = logger_default.child("sleep");
|
|
3430
3599
|
function defaultState() {
|
|
3431
3600
|
return {
|
|
3432
3601
|
sleeping: false,
|
|
@@ -3469,7 +3638,7 @@ var SleepManager = class {
|
|
|
3469
3638
|
transitioning = /* @__PURE__ */ new Set();
|
|
3470
3639
|
sleepConfigs = /* @__PURE__ */ new Map();
|
|
3471
3640
|
get statePath() {
|
|
3472
|
-
return
|
|
3641
|
+
return resolve7(voluteSystemDir(), "sleep-state.json");
|
|
3473
3642
|
}
|
|
3474
3643
|
start() {
|
|
3475
3644
|
this.loadState();
|
|
@@ -3485,15 +3654,15 @@ var SleepManager = class {
|
|
|
3485
3654
|
// --- State persistence ---
|
|
3486
3655
|
loadState() {
|
|
3487
3656
|
try {
|
|
3488
|
-
if (
|
|
3489
|
-
const data = JSON.parse(
|
|
3657
|
+
if (existsSync8(this.statePath)) {
|
|
3658
|
+
const data = JSON.parse(readFileSync7(this.statePath, "utf-8"));
|
|
3490
3659
|
for (const [name, state] of Object.entries(data)) {
|
|
3491
3660
|
state.triggerWakeHistory ??= [];
|
|
3492
3661
|
this.states.set(name, state);
|
|
3493
3662
|
}
|
|
3494
3663
|
}
|
|
3495
3664
|
} catch (err) {
|
|
3496
|
-
|
|
3665
|
+
slog3.warn("failed to load sleep state", logger_default.errorData(err));
|
|
3497
3666
|
}
|
|
3498
3667
|
}
|
|
3499
3668
|
saveState() {
|
|
@@ -3502,10 +3671,10 @@ var SleepManager = class {
|
|
|
3502
3671
|
if (state.sleeping) data[name] = state;
|
|
3503
3672
|
}
|
|
3504
3673
|
try {
|
|
3505
|
-
|
|
3674
|
+
writeFileSync6(this.statePath, `${JSON.stringify(data, null, 2)}
|
|
3506
3675
|
`);
|
|
3507
3676
|
} catch (err) {
|
|
3508
|
-
|
|
3677
|
+
slog3.error("failed to save sleep state", logger_default.errorData(err));
|
|
3509
3678
|
}
|
|
3510
3679
|
}
|
|
3511
3680
|
// --- Public API ---
|
|
@@ -3526,7 +3695,7 @@ var SleepManager = class {
|
|
|
3526
3695
|
const state = this.states.get(name);
|
|
3527
3696
|
if (!state?.sleeping || !state.wokenByTrigger) return;
|
|
3528
3697
|
this.markAwake(name);
|
|
3529
|
-
|
|
3698
|
+
slog3.info(`${name} trigger-wake converted to full wake`);
|
|
3530
3699
|
}
|
|
3531
3700
|
getSleepConfig(name) {
|
|
3532
3701
|
if (this.sleepConfigs.has(name)) {
|
|
@@ -3569,7 +3738,7 @@ var SleepManager = class {
|
|
|
3569
3738
|
const result = await sendSystemMessageDirect(name, preSleepMsg);
|
|
3570
3739
|
conversationId = result.conversationId;
|
|
3571
3740
|
} catch (err) {
|
|
3572
|
-
|
|
3741
|
+
slog3.error(`failed to persist pre-sleep message for ${name}`, logger_default.errorData(err));
|
|
3573
3742
|
}
|
|
3574
3743
|
try {
|
|
3575
3744
|
await fetch(`http://127.0.0.1:${entry.port}/message`, {
|
|
@@ -3586,7 +3755,7 @@ var SleepManager = class {
|
|
|
3586
3755
|
})
|
|
3587
3756
|
});
|
|
3588
3757
|
} catch (err) {
|
|
3589
|
-
|
|
3758
|
+
slog3.warn(`failed to send pre-sleep message to ${name}`, logger_default.errorData(err));
|
|
3590
3759
|
}
|
|
3591
3760
|
await this.waitForIdle(name, 12e4);
|
|
3592
3761
|
await new Promise((r) => setTimeout(r, 3e3));
|
|
@@ -3594,7 +3763,7 @@ var SleepManager = class {
|
|
|
3594
3763
|
await this.killOrphanOnPort(entry.port);
|
|
3595
3764
|
await this.archiveSessions(name);
|
|
3596
3765
|
this.markSleeping(name, opts);
|
|
3597
|
-
|
|
3766
|
+
slog3.info(`${name} is now sleeping`);
|
|
3598
3767
|
} finally {
|
|
3599
3768
|
this.transitioning.delete(name);
|
|
3600
3769
|
}
|
|
@@ -3613,7 +3782,7 @@ var SleepManager = class {
|
|
|
3613
3782
|
try {
|
|
3614
3783
|
await wakeMind(name);
|
|
3615
3784
|
} catch (err) {
|
|
3616
|
-
|
|
3785
|
+
slog3.error(`failed to wake ${name}`, logger_default.errorData(err));
|
|
3617
3786
|
return;
|
|
3618
3787
|
}
|
|
3619
3788
|
}
|
|
@@ -3654,7 +3823,7 @@ var SleepManager = class {
|
|
|
3654
3823
|
const result = await sendSystemMessageDirect(name, summaryText);
|
|
3655
3824
|
wakeConvId = result.conversationId;
|
|
3656
3825
|
} catch (err) {
|
|
3657
|
-
|
|
3826
|
+
slog3.error(`failed to persist wake summary for ${name}`, logger_default.errorData(err));
|
|
3658
3827
|
}
|
|
3659
3828
|
try {
|
|
3660
3829
|
await fetch(`http://127.0.0.1:${entry.port}/message`, {
|
|
@@ -3671,17 +3840,17 @@ var SleepManager = class {
|
|
|
3671
3840
|
})
|
|
3672
3841
|
});
|
|
3673
3842
|
} catch (err) {
|
|
3674
|
-
|
|
3843
|
+
slog3.warn(`failed to deliver wake summary to ${name}`, logger_default.errorData(err));
|
|
3675
3844
|
}
|
|
3676
3845
|
}
|
|
3677
3846
|
const flushed = await this.flushQueuedMessages(name);
|
|
3678
3847
|
if (flushed > 0) {
|
|
3679
|
-
|
|
3848
|
+
slog3.info(`flushed ${flushed} queued message(s) for ${name}`);
|
|
3680
3849
|
}
|
|
3681
3850
|
if (!opts?.trigger) {
|
|
3682
3851
|
this.markAwake(name);
|
|
3683
3852
|
}
|
|
3684
|
-
|
|
3853
|
+
slog3.info(`${name} is now awake${opts?.trigger ? " (trigger wake)" : ""}`);
|
|
3685
3854
|
} finally {
|
|
3686
3855
|
this.transitioning.delete(name);
|
|
3687
3856
|
}
|
|
@@ -3736,16 +3905,16 @@ var SleepManager = class {
|
|
|
3736
3905
|
async flushQueuedMessages(name) {
|
|
3737
3906
|
try {
|
|
3738
3907
|
const db = await getDb();
|
|
3739
|
-
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();
|
|
3740
3909
|
if (rows.length === 0) return 0;
|
|
3741
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
3910
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-7BCYJLH2.js");
|
|
3742
3911
|
const delivered = [];
|
|
3743
3912
|
for (const row of rows) {
|
|
3744
3913
|
try {
|
|
3745
3914
|
await deliverMessage2(name, JSON.parse(row.payload));
|
|
3746
3915
|
delivered.push(row.id);
|
|
3747
3916
|
} catch (err) {
|
|
3748
|
-
|
|
3917
|
+
slog3.warn(`failed to flush queued message ${row.id} for ${name}`, logger_default.errorData(err));
|
|
3749
3918
|
}
|
|
3750
3919
|
}
|
|
3751
3920
|
if (delivered.length > 0) {
|
|
@@ -3757,7 +3926,7 @@ var SleepManager = class {
|
|
|
3757
3926
|
}
|
|
3758
3927
|
return delivered.length;
|
|
3759
3928
|
} catch (err) {
|
|
3760
|
-
|
|
3929
|
+
slog3.warn(`failed to flush queued messages for ${name}`, logger_default.errorData(err));
|
|
3761
3930
|
return 0;
|
|
3762
3931
|
}
|
|
3763
3932
|
}
|
|
@@ -3786,7 +3955,7 @@ var SleepManager = class {
|
|
|
3786
3955
|
const interval = CronExpressionParser2.parse(config.schedule.wake);
|
|
3787
3956
|
return interval.next().toDate().toISOString();
|
|
3788
3957
|
} catch (err) {
|
|
3789
|
-
|
|
3958
|
+
slog3.warn(`invalid wake cron "${config.schedule.wake}"`, logger_default.errorData(err));
|
|
3790
3959
|
return null;
|
|
3791
3960
|
}
|
|
3792
3961
|
}
|
|
@@ -3806,7 +3975,7 @@ var SleepManager = class {
|
|
|
3806
3975
|
const wakeAt = new Date(state.voluntaryWakeAt);
|
|
3807
3976
|
if (now >= wakeAt) {
|
|
3808
3977
|
this.initiateWake(name).catch(
|
|
3809
|
-
(err) =>
|
|
3978
|
+
(err) => slog3.error(`failed voluntary wake for ${name}`, logger_default.errorData(err))
|
|
3810
3979
|
);
|
|
3811
3980
|
continue;
|
|
3812
3981
|
}
|
|
@@ -3815,7 +3984,7 @@ var SleepManager = class {
|
|
|
3815
3984
|
const wakeAt = new Date(state.scheduledWakeAt);
|
|
3816
3985
|
if (now >= wakeAt) {
|
|
3817
3986
|
this.initiateWake(name).catch(
|
|
3818
|
-
(err) =>
|
|
3987
|
+
(err) => slog3.error(`failed scheduled wake for ${name}`, logger_default.errorData(err))
|
|
3819
3988
|
);
|
|
3820
3989
|
continue;
|
|
3821
3990
|
}
|
|
@@ -3823,7 +3992,7 @@ var SleepManager = class {
|
|
|
3823
3992
|
if (!state?.sleeping) {
|
|
3824
3993
|
if (this.shouldSleep(config.schedule.sleep, epochMinute)) {
|
|
3825
3994
|
this.initiateSleep(name).catch(
|
|
3826
|
-
(err) =>
|
|
3995
|
+
(err) => slog3.error(`failed to initiate sleep for ${name}`, logger_default.errorData(err))
|
|
3827
3996
|
);
|
|
3828
3997
|
}
|
|
3829
3998
|
}
|
|
@@ -3836,22 +4005,22 @@ var SleepManager = class {
|
|
|
3836
4005
|
const prevMinute = Math.floor(prev.getTime() / 6e4);
|
|
3837
4006
|
return prevMinute === epochMinute;
|
|
3838
4007
|
} catch (err) {
|
|
3839
|
-
|
|
4008
|
+
slog3.warn(`invalid sleep cron "${cronExpr}"`, logger_default.errorData(err));
|
|
3840
4009
|
return false;
|
|
3841
4010
|
}
|
|
3842
4011
|
}
|
|
3843
4012
|
async waitForIdle(name, timeoutMs) {
|
|
3844
|
-
return new Promise((
|
|
4013
|
+
return new Promise((resolve9) => {
|
|
3845
4014
|
const timeout = setTimeout(() => {
|
|
3846
4015
|
unsub();
|
|
3847
|
-
|
|
4016
|
+
resolve9();
|
|
3848
4017
|
}, timeoutMs);
|
|
3849
4018
|
const unsub = subscribe((event) => {
|
|
3850
4019
|
if (event.mind !== name) return;
|
|
3851
4020
|
if (event.type === "mind_done" || event.type === "mind_idle") {
|
|
3852
4021
|
clearTimeout(timeout);
|
|
3853
4022
|
unsub();
|
|
3854
|
-
|
|
4023
|
+
resolve9();
|
|
3855
4024
|
}
|
|
3856
4025
|
});
|
|
3857
4026
|
});
|
|
@@ -3859,41 +4028,41 @@ var SleepManager = class {
|
|
|
3859
4028
|
async archiveSessions(name) {
|
|
3860
4029
|
const dir = mindDir(name);
|
|
3861
4030
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 16);
|
|
3862
|
-
const sessionsDir =
|
|
3863
|
-
if (
|
|
3864
|
-
const archiveDir =
|
|
3865
|
-
|
|
4031
|
+
const sessionsDir = resolve7(dir, ".mind", "sessions");
|
|
4032
|
+
if (existsSync8(sessionsDir)) {
|
|
4033
|
+
const archiveDir = resolve7(sessionsDir, "archive");
|
|
4034
|
+
mkdirSync5(archiveDir, { recursive: true });
|
|
3866
4035
|
for (const file of readdirSync2(sessionsDir)) {
|
|
3867
4036
|
if (file === "archive" || !file.endsWith(".json")) continue;
|
|
3868
|
-
const src =
|
|
4037
|
+
const src = resolve7(sessionsDir, file);
|
|
3869
4038
|
const base = file.replace(/\.json$/, "");
|
|
3870
|
-
const dest =
|
|
4039
|
+
const dest = resolve7(archiveDir, `${base}-${timestamp}.json`);
|
|
3871
4040
|
try {
|
|
3872
4041
|
renameSync(src, dest);
|
|
3873
4042
|
} catch (err) {
|
|
3874
|
-
|
|
4043
|
+
slog3.warn(`failed to archive session ${file} for ${name}`, logger_default.errorData(err));
|
|
3875
4044
|
}
|
|
3876
4045
|
}
|
|
3877
4046
|
}
|
|
3878
|
-
const piSessionsDir =
|
|
3879
|
-
if (
|
|
3880
|
-
const archiveDir =
|
|
3881
|
-
|
|
4047
|
+
const piSessionsDir = resolve7(dir, ".mind", "pi-sessions");
|
|
4048
|
+
if (existsSync8(piSessionsDir)) {
|
|
4049
|
+
const archiveDir = resolve7(piSessionsDir, "archive");
|
|
4050
|
+
mkdirSync5(archiveDir, { recursive: true });
|
|
3882
4051
|
for (const entry of readdirSync2(piSessionsDir, { withFileTypes: true })) {
|
|
3883
4052
|
if (entry.name === "archive" || !entry.isDirectory()) continue;
|
|
3884
|
-
const src =
|
|
3885
|
-
const dest =
|
|
4053
|
+
const src = resolve7(piSessionsDir, entry.name);
|
|
4054
|
+
const dest = resolve7(archiveDir, `${entry.name}-${timestamp}`);
|
|
3886
4055
|
try {
|
|
3887
4056
|
renameSync(src, dest);
|
|
3888
4057
|
} catch (err) {
|
|
3889
|
-
|
|
4058
|
+
slog3.warn(`failed to archive pi-session ${entry.name} for ${name}`, logger_default.errorData(err));
|
|
3890
4059
|
}
|
|
3891
4060
|
}
|
|
3892
4061
|
}
|
|
3893
4062
|
}
|
|
3894
4063
|
async runWakeContextScript(name, sleepingSince, duration) {
|
|
3895
|
-
const scriptPath =
|
|
3896
|
-
if (!
|
|
4064
|
+
const scriptPath = resolve7(mindDir(name), "home", ".local", "hooks", "wake-context.sh");
|
|
4065
|
+
if (!existsSync8(scriptPath)) return "";
|
|
3897
4066
|
const input = JSON.stringify({
|
|
3898
4067
|
sleepingSince,
|
|
3899
4068
|
duration,
|
|
@@ -3929,7 +4098,7 @@ var SleepManager = class {
|
|
|
3929
4098
|
});
|
|
3930
4099
|
return result.trim();
|
|
3931
4100
|
} catch (err) {
|
|
3932
|
-
|
|
4101
|
+
slog3.warn(`wake-context script failed for ${name}`, logger_default.errorData(err));
|
|
3933
4102
|
return "";
|
|
3934
4103
|
}
|
|
3935
4104
|
}
|
|
@@ -3943,7 +4112,7 @@ var SleepManager = class {
|
|
|
3943
4112
|
async buildQueuedSummary(name) {
|
|
3944
4113
|
try {
|
|
3945
4114
|
const db = await getDb();
|
|
3946
|
-
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();
|
|
3947
4116
|
if (rows.length === 0) return "No messages arrived while you slept.";
|
|
3948
4117
|
const channelCounts = /* @__PURE__ */ new Map();
|
|
3949
4118
|
const senders = /* @__PURE__ */ new Set();
|
|
@@ -3956,7 +4125,7 @@ var SleepManager = class {
|
|
|
3956
4125
|
const senderNote = senders.size > 0 ? ` from ${[...senders].join(", ")}` : "";
|
|
3957
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.`;
|
|
3958
4127
|
} catch (err) {
|
|
3959
|
-
|
|
4128
|
+
slog3.error(`failed to build queued summary for ${name}`, logger_default.errorData(err));
|
|
3960
4129
|
return "Unable to check for queued messages \u2014 there may be messages waiting.";
|
|
3961
4130
|
}
|
|
3962
4131
|
}
|
|
@@ -3971,7 +4140,7 @@ var SleepManager = class {
|
|
|
3971
4140
|
} catch {
|
|
3972
4141
|
return;
|
|
3973
4142
|
}
|
|
3974
|
-
|
|
4143
|
+
slog3.warn(`orphan process found on port ${port} after sleep, killing`);
|
|
3975
4144
|
const execFileAsync2 = promisify2(execFile2);
|
|
3976
4145
|
try {
|
|
3977
4146
|
const { stdout } = await execFileAsync2("lsof", ["-ti", `:${port}`, "-sTCP:LISTEN"]);
|
|
@@ -3982,7 +4151,7 @@ var SleepManager = class {
|
|
|
3982
4151
|
process.kill(pid, "SIGTERM");
|
|
3983
4152
|
} catch (err) {
|
|
3984
4153
|
if (err.code !== "ESRCH") {
|
|
3985
|
-
|
|
4154
|
+
slog3.warn(`failed to kill orphan pid ${pid}`, logger_default.errorData(err));
|
|
3986
4155
|
}
|
|
3987
4156
|
}
|
|
3988
4157
|
}
|
|
@@ -3990,7 +4159,7 @@ var SleepManager = class {
|
|
|
3990
4159
|
} catch {
|
|
3991
4160
|
try {
|
|
3992
4161
|
const portHex = port.toString(16).toUpperCase().padStart(4, "0");
|
|
3993
|
-
const tcp6 =
|
|
4162
|
+
const tcp6 = readFileSync7("/proc/net/tcp6", "utf-8");
|
|
3994
4163
|
for (const line of tcp6.split("\n")) {
|
|
3995
4164
|
if (!line.includes(`:${portHex} `)) continue;
|
|
3996
4165
|
const fields = line.trim().split(/\s+/);
|
|
@@ -4014,7 +4183,7 @@ var SleepManager = class {
|
|
|
4014
4183
|
}
|
|
4015
4184
|
}
|
|
4016
4185
|
} catch (err) {
|
|
4017
|
-
|
|
4186
|
+
slog3.warn(`failed to kill orphan on port ${port} via /proc`, logger_default.errorData(err));
|
|
4018
4187
|
}
|
|
4019
4188
|
}
|
|
4020
4189
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
@@ -4024,7 +4193,7 @@ var SleepManager = class {
|
|
|
4024
4193
|
if (!state?.sleeping || !state.wokenByTrigger) return;
|
|
4025
4194
|
if (this.transitioning.has(event.mind)) return;
|
|
4026
4195
|
if (event.type === "mind_idle") {
|
|
4027
|
-
|
|
4196
|
+
slog3.info(`${event.mind} going back to sleep after trigger wake`);
|
|
4028
4197
|
state.wokenByTrigger = false;
|
|
4029
4198
|
this.transitioning.add(event.mind);
|
|
4030
4199
|
sleepMind(event.mind).then(() => this.archiveSessions(event.mind)).then(() => {
|
|
@@ -4033,9 +4202,9 @@ var SleepManager = class {
|
|
|
4033
4202
|
const sleepConfig = this.getSleepConfig(event.mind);
|
|
4034
4203
|
state.scheduledWakeAt = this.getNextWakeTime(sleepConfig);
|
|
4035
4204
|
this.saveState();
|
|
4036
|
-
|
|
4205
|
+
slog3.info(`${event.mind} returned to sleep`);
|
|
4037
4206
|
}).catch((err) => {
|
|
4038
|
-
|
|
4207
|
+
slog3.error(`failed to return ${event.mind} to sleep`, logger_default.errorData(err));
|
|
4039
4208
|
}).finally(() => {
|
|
4040
4209
|
this.transitioning.delete(event.mind);
|
|
4041
4210
|
});
|
|
@@ -4058,8 +4227,8 @@ function getSleepManagerIfReady() {
|
|
|
4058
4227
|
|
|
4059
4228
|
// packages/daemon/src/lib/delivery/delivery-manager.ts
|
|
4060
4229
|
import { readFile, realpath } from "fs/promises";
|
|
4061
|
-
import { extname, resolve as
|
|
4062
|
-
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";
|
|
4063
4232
|
|
|
4064
4233
|
// packages/daemon/src/lib/chat/typing.ts
|
|
4065
4234
|
var DEFAULT_TTL_MS = 1e4;
|
|
@@ -4257,7 +4426,7 @@ var DeliveryManager = class {
|
|
|
4257
4426
|
async restoreFromDb() {
|
|
4258
4427
|
try {
|
|
4259
4428
|
const db = await getDb();
|
|
4260
|
-
const rows = await db.select().from(deliveryQueue).where(
|
|
4429
|
+
const rows = await db.select().from(deliveryQueue).where(eq6(deliveryQueue.status, "pending"));
|
|
4261
4430
|
for (const row of rows) {
|
|
4262
4431
|
let payload;
|
|
4263
4432
|
try {
|
|
@@ -4275,7 +4444,7 @@ var DeliveryManager = class {
|
|
|
4275
4444
|
this.addToBatchBuffer(row.mind, row.session, payload, sessionConfig);
|
|
4276
4445
|
} else {
|
|
4277
4446
|
try {
|
|
4278
|
-
await db.delete(deliveryQueue).where(
|
|
4447
|
+
await db.delete(deliveryQueue).where(eq6(deliveryQueue.id, row.id));
|
|
4279
4448
|
} catch (err) {
|
|
4280
4449
|
dlog.warn(`failed to delete queue row ${row.id} for ${row.mind}`, logger_default.errorData(err));
|
|
4281
4450
|
}
|
|
@@ -4296,7 +4465,7 @@ var DeliveryManager = class {
|
|
|
4296
4465
|
*/
|
|
4297
4466
|
async getPending(mindName) {
|
|
4298
4467
|
const db = await getDb();
|
|
4299
|
-
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")));
|
|
4300
4469
|
const byChannel = /* @__PURE__ */ new Map();
|
|
4301
4470
|
for (const row of rows) {
|
|
4302
4471
|
const ch = row.channel ?? "unknown";
|
|
@@ -4491,10 +4660,10 @@ var DeliveryManager = class {
|
|
|
4491
4660
|
try {
|
|
4492
4661
|
const db = await getDb();
|
|
4493
4662
|
await db.delete(deliveryQueue).where(
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4663
|
+
and4(
|
|
4664
|
+
eq6(deliveryQueue.mind, baseName),
|
|
4665
|
+
eq6(deliveryQueue.session, session),
|
|
4666
|
+
eq6(deliveryQueue.status, "pending")
|
|
4498
4667
|
)
|
|
4499
4668
|
);
|
|
4500
4669
|
} catch (err) {
|
|
@@ -4620,11 +4789,11 @@ var DeliveryManager = class {
|
|
|
4620
4789
|
await this.persistToQueue(baseName, session, payload, "gated");
|
|
4621
4790
|
try {
|
|
4622
4791
|
const db = await getDb();
|
|
4623
|
-
const count = await db.select({ count:
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
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")
|
|
4628
4797
|
)
|
|
4629
4798
|
);
|
|
4630
4799
|
if ((count[0]?.count ?? 0) <= 1) {
|
|
@@ -4649,7 +4818,7 @@ var DeliveryManager = class {
|
|
|
4649
4818
|
`To accept this channel, add a routing rule for "${channel}" to your routes.json.`,
|
|
4650
4819
|
`Messages are being held until a route is configured.`
|
|
4651
4820
|
].filter((line) => line !== null).join("\n");
|
|
4652
|
-
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-
|
|
4821
|
+
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-LQSJVY55.js");
|
|
4653
4822
|
await sendSystemMessage2(mindName, notification);
|
|
4654
4823
|
}
|
|
4655
4824
|
async persistToQueue(mindName, session, payload, status = "pending") {
|
|
@@ -4723,8 +4892,8 @@ var DeliveryManager = class {
|
|
|
4723
4892
|
const dir = mindDir(p.username);
|
|
4724
4893
|
const config = readVoluteConfig(dir);
|
|
4725
4894
|
if (!config?.profile?.avatar) continue;
|
|
4726
|
-
filePath =
|
|
4727
|
-
const homeDir =
|
|
4895
|
+
filePath = resolve8(dir, "home", config.profile.avatar);
|
|
4896
|
+
const homeDir = resolve8(dir, "home");
|
|
4728
4897
|
if (!filePath.startsWith(`${homeDir}/`)) {
|
|
4729
4898
|
dlog.warn(`avatar path for ${p.username} escapes home directory, skipping`);
|
|
4730
4899
|
continue;
|
|
@@ -4743,7 +4912,7 @@ var DeliveryManager = class {
|
|
|
4743
4912
|
throw err;
|
|
4744
4913
|
}
|
|
4745
4914
|
} else {
|
|
4746
|
-
filePath =
|
|
4915
|
+
filePath = resolve8(voluteHome(), "avatars", p.avatar);
|
|
4747
4916
|
}
|
|
4748
4917
|
const ext = extname(filePath).toLowerCase();
|
|
4749
4918
|
const mimeMap = {
|
|
@@ -4885,18 +5054,18 @@ async function linkToolResultToTurn(mind, turnId, toolResultContent, toolUseEven
|
|
|
4885
5054
|
channel: mindHistory.channel,
|
|
4886
5055
|
content: mindHistory.content,
|
|
4887
5056
|
message_id: mindHistory.message_id
|
|
4888
|
-
}).from(mindHistory).where(
|
|
5057
|
+
}).from(mindHistory).where(and5(eq7(mindHistory.id, outboundId), eq7(mindHistory.mind, mind))).limit(1);
|
|
4889
5058
|
const row = rows[0];
|
|
4890
5059
|
if (!row) {
|
|
4891
5060
|
dlog2.warn(`outbound marker references missing record: mind=${mind} id=${outboundId}`);
|
|
4892
5061
|
continue;
|
|
4893
5062
|
}
|
|
4894
|
-
await db.update(mindHistory).set({ turn_id: turnId }).where(
|
|
5063
|
+
await db.update(mindHistory).set({ turn_id: turnId }).where(eq7(mindHistory.id, outboundId));
|
|
4895
5064
|
if (row.message_id) {
|
|
4896
5065
|
await db.update(messages).set({
|
|
4897
5066
|
turn_id: turnId,
|
|
4898
5067
|
...toolUseEventId != null ? { source_event_id: toolUseEventId } : {}
|
|
4899
|
-
}).where(
|
|
5068
|
+
}).where(eq7(messages.id, Number(row.message_id)));
|
|
4900
5069
|
}
|
|
4901
5070
|
publish3(mind, {
|
|
4902
5071
|
mind,
|
|
@@ -4940,19 +5109,19 @@ async function linkToolResultToTurn(mind, turnId, toolResultContent, toolUseEven
|
|
|
4940
5109
|
async function tagUntaggedOutbound(mind, turnId) {
|
|
4941
5110
|
const db = await getDb();
|
|
4942
5111
|
const range = await db.select({
|
|
4943
|
-
minId:
|
|
4944
|
-
maxId:
|
|
4945
|
-
}).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)));
|
|
4946
5115
|
const minId = range[0]?.minId;
|
|
4947
5116
|
const maxId = range[0]?.maxId;
|
|
4948
5117
|
if (minId == null || maxId == null) return;
|
|
4949
5118
|
const orphans = await db.select({ id: mindHistory.id, message_id: mindHistory.message_id }).from(mindHistory).where(
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
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}`
|
|
4956
5125
|
)
|
|
4957
5126
|
);
|
|
4958
5127
|
if (orphans.length === 0) return;
|
|
@@ -4961,10 +5130,10 @@ async function tagUntaggedOutbound(mind, turnId) {
|
|
|
4961
5130
|
const orphansWithMessages = orphans.filter((o) => o.message_id);
|
|
4962
5131
|
if (orphansWithMessages.length > 0) {
|
|
4963
5132
|
const toolUseEvents = await db.select({ id: mindHistory.id }).from(mindHistory).where(
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
5133
|
+
and5(
|
|
5134
|
+
eq7(mindHistory.mind, mind),
|
|
5135
|
+
eq7(mindHistory.turn_id, turnId),
|
|
5136
|
+
eq7(mindHistory.type, "tool_use")
|
|
4968
5137
|
)
|
|
4969
5138
|
).orderBy(mindHistory.id);
|
|
4970
5139
|
for (const orphan of orphansWithMessages) {
|
|
@@ -4978,7 +5147,7 @@ async function tagUntaggedOutbound(mind, turnId) {
|
|
|
4978
5147
|
await db.update(messages).set({
|
|
4979
5148
|
turn_id: turnId,
|
|
4980
5149
|
...sourceEventId != null ? { source_event_id: sourceEventId } : {}
|
|
4981
|
-
}).where(
|
|
5150
|
+
}).where(eq7(messages.id, Number(orphan.message_id)));
|
|
4982
5151
|
}
|
|
4983
5152
|
}
|
|
4984
5153
|
dlog2.info(`tagged ${orphans.length} orphaned outbound record(s) for ${mind} with turn ${turnId}`);
|
|
@@ -4992,22 +5161,22 @@ async function tagUntaggedInbound(mind, turnId, {
|
|
|
4992
5161
|
const tagHistoryPromise = (async () => {
|
|
4993
5162
|
if (!channel) return;
|
|
4994
5163
|
const historyConditions = [
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
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)
|
|
5000
5169
|
];
|
|
5001
|
-
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);
|
|
5002
5171
|
if (recentInbounds.length > 0) {
|
|
5003
5172
|
const ids = recentInbounds.map((r) => r.id);
|
|
5004
5173
|
await db.update(mindHistory).set({ turn_id: turnId }).where(inArray3(mindHistory.id, ids));
|
|
5005
5174
|
if (setTrigger) {
|
|
5006
|
-
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));
|
|
5007
5176
|
}
|
|
5008
5177
|
}
|
|
5009
5178
|
})();
|
|
5010
|
-
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();
|
|
5011
5180
|
const [tagResult, mindUserResult] = await Promise.allSettled([
|
|
5012
5181
|
tagHistoryPromise,
|
|
5013
5182
|
mindUserPromise
|
|
@@ -5016,13 +5185,13 @@ async function tagUntaggedInbound(mind, turnId, {
|
|
|
5016
5185
|
dlog2.error("failed to tag history inbound", logger_default.errorData(tagResult.reason));
|
|
5017
5186
|
}
|
|
5018
5187
|
const mindUser = mindUserResult.status === "fulfilled" ? mindUserResult.value : void 0;
|
|
5019
|
-
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) : [];
|
|
5020
5189
|
const recentMsgs = mindConvIds.length > 0 ? await db.select({ id: messages.id }).from(messages).where(
|
|
5021
|
-
|
|
5190
|
+
and5(
|
|
5022
5191
|
inArray3(messages.conversation_id, mindConvIds),
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5192
|
+
sql4`${messages.turn_id} IS NULL`,
|
|
5193
|
+
sql4`${messages.sender_name} != ${mind}`,
|
|
5194
|
+
sql4`${messages.created_at} > datetime('now', '-60 seconds')`
|
|
5026
5195
|
)
|
|
5027
5196
|
).orderBy(desc2(messages.id)).limit(limit) : [];
|
|
5028
5197
|
if (recentMsgs.length > 0) {
|
|
@@ -5083,7 +5252,7 @@ async function deliverMessage(mindName, payload) {
|
|
|
5083
5252
|
}
|
|
5084
5253
|
|
|
5085
5254
|
// packages/daemon/src/lib/chat/system-chat.ts
|
|
5086
|
-
var
|
|
5255
|
+
var slog4 = logger_default.child("system-chat");
|
|
5087
5256
|
var dmCache = /* @__PURE__ */ new Map();
|
|
5088
5257
|
function resetSystemDMCache() {
|
|
5089
5258
|
dmCache.clear();
|
|
@@ -5151,7 +5320,7 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
5151
5320
|
});
|
|
5152
5321
|
return;
|
|
5153
5322
|
} catch (err) {
|
|
5154
|
-
|
|
5323
|
+
slog4.warn(`failed to route to spirit, falling back to aiCompleteUtility`, logger_default.errorData(err));
|
|
5155
5324
|
}
|
|
5156
5325
|
}
|
|
5157
5326
|
const entry = await findMind(mindName);
|
|
@@ -5172,7 +5341,7 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
5172
5341
|
if (config.sleep.schedule?.wake) contextParts.push(`Wake cron: ${config.sleep.schedule.wake}`);
|
|
5173
5342
|
}
|
|
5174
5343
|
try {
|
|
5175
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5344
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-5277OBHI.js");
|
|
5176
5345
|
const sm = getSleepManagerIfReady2();
|
|
5177
5346
|
if (sm) {
|
|
5178
5347
|
const state = sm.getState(mindName);
|
|
@@ -5181,7 +5350,7 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
5181
5350
|
}
|
|
5182
5351
|
}
|
|
5183
5352
|
} catch (err) {
|
|
5184
|
-
|
|
5353
|
+
slog4.debug("could not retrieve sleep state for system reply", logger_default.errorData(err));
|
|
5185
5354
|
}
|
|
5186
5355
|
try {
|
|
5187
5356
|
const schedules = config?.schedules;
|
|
@@ -5194,12 +5363,12 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
5194
5363
|
}
|
|
5195
5364
|
}
|
|
5196
5365
|
} catch (err) {
|
|
5197
|
-
|
|
5366
|
+
slog4.debug("could not retrieve schedules for system reply", logger_default.errorData(err));
|
|
5198
5367
|
}
|
|
5199
5368
|
const systemPrompt = contextParts.join("\n");
|
|
5200
5369
|
const response = await aiCompleteUtility(systemPrompt, message);
|
|
5201
5370
|
if (!response) {
|
|
5202
|
-
|
|
5371
|
+
slog4.warn(`no AI model available for system reply to ${mindName}`);
|
|
5203
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.";
|
|
5204
5373
|
await addMessage(conversationId, "assistant", "volute", [{ type: "text", text: fallback }]);
|
|
5205
5374
|
return;
|
|
@@ -5229,13 +5398,20 @@ export {
|
|
|
5229
5398
|
getPromptIfCustom,
|
|
5230
5399
|
getMindPromptDefaults,
|
|
5231
5400
|
RotatingLog,
|
|
5401
|
+
syncProviderToMinds,
|
|
5232
5402
|
resolveMindToken,
|
|
5403
|
+
recordNotice,
|
|
5404
|
+
drainNotices,
|
|
5405
|
+
clearDeliveredNotices,
|
|
5406
|
+
formatNotices,
|
|
5233
5407
|
RestartTracker,
|
|
5234
5408
|
subscribe2 as subscribe,
|
|
5235
5409
|
subscribeAll,
|
|
5236
5410
|
publish3 as publish,
|
|
5237
5411
|
summarizeTurn,
|
|
5238
5412
|
initSummarizer,
|
|
5413
|
+
markErrored,
|
|
5414
|
+
takeErrored,
|
|
5239
5415
|
createTurn,
|
|
5240
5416
|
getActiveTurnId,
|
|
5241
5417
|
trackToolUse,
|