volute 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{accept-KYNC5X3U.js → accept-5NK5XJB5.js} +2 -1
- package/dist/{activity-events-GJJAI2WT.js → activity-events-H7YI3BTF.js} +3 -3
- package/dist/{ai-service-A7FS7EC3.js → ai-service-V4CTNGI3.js} +5 -4
- package/dist/{archive-7E3YE6VF.js → archive-ORM3ENHI.js} +3 -3
- package/dist/{auth-ABYLOEZ7.js → auth-DT7GY7EZ.js} +10 -6
- package/dist/{avatar-image-6ATV2IOZ.js → avatar-image-QEPAP3A5.js} +3 -3
- package/dist/{backup-XYAMT4CU.js → backup-IE6PMXJO.js} +11 -12
- package/dist/{bridge-3FNSWLNQ.js → bridge-YK7NTRU5.js} +2 -1
- package/dist/{channels-SGQZHNQJ.js → channels-FSAHOQWI.js} +2 -1
- package/dist/{chat-OGXAEXMV.js → chat-W5K6HCBE.js} +9 -9
- package/dist/{chunk-3B427GUK.js → chunk-3BAFMGCJ.js} +2 -2
- package/dist/{chunk-4B62E4CG.js → chunk-4CN7OCJE.js} +2 -2
- package/dist/{chunk-VNRSXEBF.js → chunk-574M6I7F.js} +38 -5
- package/dist/{chunk-HCYQRME2.js → chunk-5FVANDON.js} +2 -2
- package/dist/{chunk-P4LHVRNE.js → chunk-5IIVOWSP.js} +27 -19
- package/dist/{chunk-TXJL36ZQ.js → chunk-5PKFJHCR.js} +2 -2
- package/dist/{chunk-JPGHSXSV.js → chunk-5TPC4KCV.js} +23 -9
- package/dist/{chunk-YIJGKHIS.js → chunk-632FJ23O.js} +24 -20
- package/dist/{chunk-IH5TKXX6.js → chunk-636GFQNE.js} +11 -11
- package/dist/{chunk-EFT2OALG.js → chunk-6BM2I736.js} +20 -5
- package/dist/{chunk-JCBPU57A.js → chunk-74T3CEXK.js} +6 -6
- package/dist/{chunk-EG5LSKHU.js → chunk-77IZUVBB.js} +2 -2
- package/dist/{chunk-K3JOLGWF.js → chunk-7DXLIYPY.js} +76 -63
- package/dist/chunk-7FDWPQCU.js +289 -0
- package/dist/chunk-7OZWKHA3.js +548 -0
- package/dist/{chunk-PY24ZL62.js → chunk-7UWXAJMK.js} +1 -1
- package/dist/{chunk-MG3Z34YE.js → chunk-7XDOTN4R.js} +1 -1
- package/dist/{chunk-SX72DOO3.js → chunk-AFSLNUTT.js} +1 -1
- package/dist/{chunk-DVN6DNMI.js → chunk-AJWFDIEY.js} +17 -2
- package/dist/chunk-ALIVXDM4.js +1014 -0
- package/dist/{chunk-RSBL3FAD.js → chunk-CAIYV33P.js} +234 -24
- package/dist/{chunk-ZX46PGCC.js → chunk-E5S3ILVA.js} +1 -1
- package/dist/{chunk-SHKM5RGD.js → chunk-EEOMA4DX.js} +1 -1
- package/dist/chunk-EQKSRVDE.js +2857 -0
- package/dist/chunk-FNJCBRD3.js +31 -0
- package/dist/{chunk-EADX54RG.js → chunk-HW6Q7AMB.js} +4 -4
- package/dist/chunk-JI67D7O2.js +1222 -0
- package/dist/{chunk-KHCKZ36O.js → chunk-KAEZOVP6.js} +1 -1
- package/dist/chunk-LNNI3MS4.js +54 -0
- package/dist/{chunk-BCG45AQB.js → chunk-M74I3HHF.js} +15 -5
- package/dist/chunk-MLZBBLMT.js +109 -0
- package/dist/{chunk-5T5YMX6S.js → chunk-PW65BXY5.js} +5 -0
- package/dist/{chunk-NOIAHMJB.js → chunk-QFVGPKJI.js} +1 -1
- package/dist/{chunk-VIUH5CHQ.js → chunk-SFRYRW37.js} +50 -33
- package/dist/{chunk-L27YYONJ.js → chunk-SSNZ6ZNN.js} +1 -1
- package/dist/{chunk-GWVFDUNB.js → chunk-TGJHGVBZ.js} +8 -10
- package/dist/{chunk-6HGEZJDH.js → chunk-U3EJJ7JN.js} +2 -2
- package/dist/{chunk-4DSGN4FL.js → chunk-UC6BNMGT.js} +43 -13
- package/dist/{chunk-JBIFXB6I.js → chunk-WBBKYUGF.js} +3 -5
- package/dist/{chunk-FBBS4BVR.js → chunk-X67VT4QO.js} +1 -1
- package/dist/{chunk-DAIIMBLB.js → chunk-YQOYZ6G7.js} +3 -5
- package/dist/{chunk-JCWB2ITW.js → chunk-ZUG76D3P.js} +35 -5
- package/dist/cli.js +34 -27
- package/dist/{clock-6JZL2FS6.js → clock-LH7UFNLJ.js} +4 -3
- package/dist/{cloud-sync-VFDTNNCA.js → cloud-sync-IDJFVDEP.js} +22 -19
- package/dist/{config-RPSLA2HX.js → config-UGJS57VT.js} +3 -3
- package/dist/{conversations-TUM5PJJ4.js → conversations-S2232Y5I.js} +8 -4
- package/dist/{create-Z5FSDNVZ.js → create-2M7DAHQX.js} +2 -2
- package/dist/{create-VIQVP3QB.js → create-M727VUDF.js} +2 -1
- package/dist/{daemon-client-ZFY5MOAL.js → daemon-client-VVYFZI2D.js} +2 -1
- package/dist/{daemon-restart-54L4B6AQ.js → daemon-restart-2AAGYFHE.js} +6 -7
- package/dist/daemon.js +1625 -529
- package/dist/{db-F6UMFRYD.js → db-QAY45Z56.js} +2 -2
- package/dist/{delete-ESEP7NGQ.js → delete-O7PEFQDF.js} +1 -1
- package/dist/delivery-manager-B4OKWD2P.js +33 -0
- package/dist/{delivery-router-VUOL5YSA.js → delivery-router-JE7SHYNU.js} +7 -5
- package/dist/{down-6X7KX7BZ.js → down-D4IRNEMJ.js} +5 -5
- package/dist/echo-text-WYAAODBV.js +39 -0
- package/dist/{env-ZYMSCB7B.js → env-ABM5IMA7.js} +3 -2
- package/dist/{env-KUL4XKN2.js → env-AZK5X4W7.js} +3 -3
- package/dist/{exec-FBXA3CM4.js → exec-2H65LMTA.js} +3 -3
- package/dist/{export-TNBPDH77.js → export-3E3EL6W3.js} +4 -4
- package/dist/{extension-J4ZQ2GHV.js → extension-MRYJWSMV.js} +2 -1
- package/dist/{extensions-AWNO3SZF.js → extensions-7KP3X6G7.js} +22 -19
- package/dist/{files-BVBRJJ2P.js → files-6BMKVG5G.js} +5 -4
- package/dist/{import-ZUNKMCKC.js → import-UPNTEBBT.js} +8 -8
- package/dist/{isolation-OPCZQRTH.js → isolation-OKDFM6WA.js} +5 -3
- package/dist/{join-34TR2GXQ.js → join-PYA36HEC.js} +1 -1
- package/dist/{list-PMUXZNAM.js → list-4BVGH7AN.js} +8 -3
- package/dist/{login-KAKNFCW5.js → login-MOXVLULE.js} +4 -3
- package/dist/{login-LKNLCJPA.js → login-RJJTCVR3.js} +2 -1
- package/dist/{logout-YZRMZ3O2.js → logout-BYV433X6.js} +2 -1
- package/dist/{logout-RLT62RQP.js → logout-DWD3OQVT.js} +4 -3
- package/dist/message-delivery-3456PXB7.js +43 -0
- package/dist/migrate-thread-config-4A5LW36Z.js +90 -0
- package/dist/{mind-IF6CGMFW.js → mind-NYRALSV2.js} +19 -19
- package/dist/{mind-activity-tracker-YMMERB6B.js → mind-activity-tracker-L7GCYXQR.js} +4 -4
- package/dist/{mind-contacts-BKUFPUCU.js → mind-contacts-QYU4TPEA.js} +3 -2
- package/dist/{mind-history-WI5OI3BW.js → mind-history-NXXOOTX6.js} +30 -7
- package/dist/{mind-list-OTHV4ZO4.js → mind-list-SYRA7CH5.js} +3 -2
- package/dist/{mind-profile-LUG353ZT.js → mind-profile-MRCVZPYT.js} +1 -1
- package/dist/mind-service-5TA7UJAT.js +47 -0
- package/dist/{mind-sleep-VBV7YRSU.js → mind-sleep-TH6BRQ5X.js} +4 -3
- package/dist/{mind-status-BJNPGXKC.js → mind-status-Z2LQFA6F.js} +3 -2
- package/dist/{mind-wake-LVTZ55EZ.js → mind-wake-LDGPRA42.js} +2 -1
- package/dist/{package-PD67VPHB.js → package-XYSV7NUT.js} +23 -23
- package/dist/{prompts-MDHDLWEX.js → prompts-QKFPX6DQ.js} +3 -3
- package/dist/{read-YSWUXYL7.js → read-ZXGUT4QC.js} +5 -3
- package/dist/{register-TWQDKQRT.js → register-OX4HRSGC.js} +2 -1
- package/dist/{registry-3ELJPUGJ.js → registry-4UF6OCOE.js} +6 -4
- package/dist/{reject-SXGCYN4A.js → reject-TWB4QJHG.js} +2 -1
- package/dist/{restart-5F5QDBOG.js → restart-AGEGNSYQ.js} +2 -1
- package/dist/{sandbox-XPYA7UIF.js → sandbox-EGEU5GGE.js} +7 -4
- package/dist/scheduler-UV6RUJ7P.js +25 -0
- package/dist/{schema-6YONVTKZ.js → schema-C2GIPD4V.js} +3 -3
- package/dist/{seed-NQED7PSX.js → seed-3IMBOAGK.js} +1 -1
- package/dist/{seed-check-TYD7RRZY.js → seed-check-NRJEQZST.js} +14 -5
- package/dist/{seed-cmd-QF3F2G4S.js → seed-cmd-U73XVQMI.js} +3 -3
- package/dist/{seed-create-XOO4N2OE.js → seed-create-FBLNVSUP.js} +4 -4
- package/dist/{seed-readiness-3LRYASA4.js → seed-readiness-QRA46ZJQ.js} +4 -5
- package/dist/{seed-sprout-HSBBGSS2.js → seed-sprout-3B4SZX7J.js} +9 -9
- package/dist/{send-IHRJYNXA.js → send-NBRZJXKX.js} +32 -10
- package/dist/{service-YCOPCTSZ.js → service-XMXAXGJD.js} +4 -4
- package/dist/{service-install-Y46OTF55.js → service-install-MOKTEG7B.js} +5 -5
- package/dist/{setup-TCN7VH5U.js → setup-CAJFOR2N.js} +10 -3
- package/dist/{setup-F4TE64YI.js → setup-JGYAXXW7.js} +6 -7
- package/dist/{skill-4CYJLYVY.js → skill-NXIHDQCG.js} +3 -2
- package/dist/skills/dreaming/references/INSTALL.md +2 -2
- package/dist/skills/imagegen/SKILL.md +21 -4
- package/dist/skills/imagegen/scripts/imagegen.ts +124 -21
- package/dist/skills/tending/SKILL.md +7 -7
- package/dist/skills/volute-mind/SKILL.md +3 -3
- package/dist/skills/volute-mind/references/integrations.md +2 -2
- package/dist/skills/volute-mind/references/routing.md +15 -15
- package/dist/{skills-NN5UZCUU.js → skills-5MSMT7PT.js} +4 -5
- package/dist/sleep-manager-LIEPO6WG.js +41 -0
- package/dist/{spirit-SSJMYC6A.js → spirit-IL5EMWYZ.js} +10 -7
- package/dist/spirit-availability-YFEGJNJ2.js +42 -0
- package/dist/{split-JF2VWTXA.js → split-BU5VWX6P.js} +1 -1
- package/dist/{sprout-AXA2SECQ.js → sprout-UPXGJ5HX.js} +1 -1
- package/dist/{src-YH6PQ7LJ.js → src-HGNE2XG4.js} +7 -1
- package/dist/{src-ZN4LXMPI.js → src-LCSOQY6Z.js} +57 -51
- package/dist/{src-JAOVXO5W.js → src-UDMJZ6MH.js} +8 -8
- package/dist/{start-BTVMTP3K.js → start-MUMNFR52.js} +2 -1
- package/dist/{status-VJGXMQ4L.js → status-IL67CYM4.js} +7 -6
- package/dist/{stop-YORESK4U.js → stop-EKERNYTX.js} +2 -1
- package/dist/system-events-A6IGCEC3.js +46 -0
- package/dist/system-install-YZSWCPDK.js +8 -0
- package/dist/{systems-5MH2ELQE.js → systems-VDRUZXNF.js} +5 -4
- package/dist/{tailscale-AMMVKDXB.js → tailscale-SJUW2ETR.js} +2 -2
- package/dist/{template-staleness-HKGCTS3H.js → template-staleness-T27VCGYA.js} +3 -3
- package/dist/{turn-tracker-JQ2YUUY2.js → turn-tracker-6EI2EL7G.js} +6 -6
- package/dist/{up-SDLTLPAO.js → up-CLL4665A.js} +5 -6
- package/dist/{update-WEA4N2TR.js → update-HVAUCQPN.js} +5 -5
- package/dist/{update-check-DZOTONII.js → update-check-2MGBBAFF.js} +3 -3
- package/dist/{upgrade-K5FIQPQD.js → upgrade-LIE46UBB.js} +1 -1
- package/dist/variant-cleanup-HJ7Z75R6.js +22 -0
- package/dist/{version-notify-WFA5XB5A.js → version-notify-7BUHWZNT.js} +10 -27
- package/dist/web-assets/assets/index-ClsUcXiW.js +95 -0
- package/dist/web-assets/assets/index-Dz3wTbtR.css +1 -0
- package/dist/web-assets/index.html +2 -2
- package/dist/xai-P42CRCZA.js +129 -0
- package/drizzle/0014_system_events.sql +26 -0
- package/drizzle/0015_rename_session_to_thread.sql +12 -0
- package/drizzle/0016_brain_to_human.sql +4 -0
- package/drizzle/0017_event_history_rows.sql +9 -0
- package/drizzle/meta/_journal.json +28 -0
- package/package.json +23 -23
- package/packages/extensions/notes/dist/ui/assets/index-BBs8pyel.js +67 -0
- package/packages/extensions/notes/dist/ui/index.html +1 -1
- package/packages/extensions/pages/dist/ui/assets/index-B589e8Xe.js +2 -0
- package/packages/extensions/pages/dist/ui/index.html +1 -1
- package/packages/extensions/plan/dist/ui/assets/index-B8s8wxyt.js +67 -0
- package/packages/extensions/plan/dist/ui/index.html +1 -1
- package/templates/_base/.init/.config/prompts.json +1 -0
- package/templates/_base/.init/.local/hooks/startup-context.ts +24 -9
- package/templates/_base/home/.config/routes.json +2 -2
- package/templates/_base/home/VOLUTE.md +18 -5
- package/templates/_base/src/lib/daemon-client.ts +1 -1
- package/templates/_base/src/lib/event-turn.ts +39 -0
- package/templates/_base/src/lib/format-prefix.ts +21 -2
- package/templates/_base/src/lib/router.ts +6 -2
- package/templates/_base/src/lib/routing.ts +8 -8
- package/templates/_base/src/lib/startup.ts +9 -1
- package/templates/_base/src/lib/types.ts +7 -1
- package/templates/_base/src/lib/volute-server.ts +30 -1
- package/templates/claude/.init/.config/routes.json +3 -3
- package/templates/claude/.init/CLAUDE.md +1 -1
- package/templates/claude/package.json.tmpl +4 -4
- package/templates/claude/src/agent.ts +4 -1
- package/templates/claude/src/lib/hooks/reply-instructions.ts +26 -1
- package/templates/codex/.init/.config/routes.json +3 -3
- package/templates/codex/.init/AGENTS.md +1 -1
- package/templates/codex/package.json.tmpl +4 -4
- package/templates/codex/src/agent.ts +11 -11
- package/templates/codex/src/lib/turn-context.ts +51 -0
- package/templates/pi/.init/.config/routes.json +3 -3
- package/templates/pi/.init/MINDS.md +1 -1
- package/templates/pi/package.json.tmpl +6 -6
- package/templates/pi/src/agent.ts +9 -1
- package/templates/pi/src/lib/event-handler.ts +1 -1
- package/templates/pi/src/lib/reply-instructions-extension.ts +28 -1
- package/dist/chunk-NJHN6IRB.js +0 -177
- package/dist/chunk-QQBOI3QA.js +0 -5345
- package/dist/chunk-XTEKINWG.js +0 -158
- package/dist/delivery-manager-RBXBPVWM.js +0 -36
- package/dist/echo-text-B6NZ5FGE.js +0 -36
- package/dist/message-delivery-ABPV76EA.js +0 -42
- package/dist/mind-service-42GDO4KH.js +0 -44
- package/dist/notices-N6BCGHFV.js +0 -25
- package/dist/scheduler-K7Y7Z7FQ.js +0 -34
- package/dist/sleep-manager-DSPXFHGX.js +0 -38
- package/dist/system-chat-M2XOBKEM.js +0 -40
- package/dist/variant-cleanup-P4MBNLJD.js +0 -33
- package/dist/web-assets/assets/index-BostMFoR.css +0 -1
- package/dist/web-assets/assets/index-CKlqUkyG.js +0 -92
- package/packages/extensions/notes/dist/ui/assets/index-D0-DgYyj.js +0 -67
- package/packages/extensions/pages/dist/ui/assets/index-DtzftuPA.js +0 -2
- package/packages/extensions/plan/dist/ui/assets/index-Cfzs-0G9.js +0 -67
package/dist/daemon.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
readBackupState,
|
|
7
7
|
resticVersion,
|
|
8
8
|
runBackup
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-WBBKYUGF.js";
|
|
10
10
|
import {
|
|
11
11
|
acceptPending,
|
|
12
12
|
formatFileSize,
|
|
@@ -14,37 +14,44 @@ import {
|
|
|
14
14
|
rejectPending,
|
|
15
15
|
stageFile,
|
|
16
16
|
validateFilePath
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-QFVGPKJI.js";
|
|
18
18
|
import {
|
|
19
19
|
findOpenClawSession,
|
|
20
20
|
importOpenClawConnectors,
|
|
21
21
|
importPiSession,
|
|
22
22
|
parseNameFromIdentity
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-77IZUVBB.js";
|
|
24
|
+
import {
|
|
25
|
+
SPIRIT_NOTICE_PREFIX,
|
|
26
|
+
ensureSpiritAvailable
|
|
27
|
+
} from "./chunk-MLZBBLMT.js";
|
|
24
28
|
import {
|
|
25
29
|
isHomeOnlyArchive
|
|
26
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-KAEZOVP6.js";
|
|
27
31
|
import {
|
|
28
|
-
|
|
29
|
-
} from "./chunk-
|
|
32
|
+
evaluateSeedChecklist
|
|
33
|
+
} from "./chunk-5PKFJHCR.js";
|
|
30
34
|
import {
|
|
31
35
|
isTemplateStale
|
|
32
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-SSNZ6ZNN.js";
|
|
33
37
|
import {
|
|
34
38
|
computeTemplateHash
|
|
35
39
|
} from "./chunk-C3KLHJDB.js";
|
|
40
|
+
import {
|
|
41
|
+
cleanupVariant
|
|
42
|
+
} from "./chunk-HW6Q7AMB.js";
|
|
36
43
|
import {
|
|
37
44
|
checkForUpdate,
|
|
38
45
|
checkForUpdateCached,
|
|
39
46
|
getCurrentVersion
|
|
40
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-AFSLNUTT.js";
|
|
41
48
|
import {
|
|
42
49
|
PLATFORMS,
|
|
43
50
|
echoTextToChannel,
|
|
44
51
|
fixModelEscapes,
|
|
45
52
|
getPlatformDriver,
|
|
46
53
|
routeOutboundBridge
|
|
47
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-636GFQNE.js";
|
|
48
55
|
import {
|
|
49
56
|
getBridgeConfig,
|
|
50
57
|
readBridgesConfig,
|
|
@@ -53,71 +60,50 @@ import {
|
|
|
53
60
|
resolveChannelMapping,
|
|
54
61
|
setBridgeConfig,
|
|
55
62
|
setChannelMapping
|
|
56
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-7XDOTN4R.js";
|
|
57
64
|
import {
|
|
58
|
-
MindStartupError,
|
|
59
|
-
RestartTracker,
|
|
60
|
-
RotatingLog,
|
|
61
65
|
SESSION_MAX_AGE,
|
|
66
|
+
announceSprout,
|
|
62
67
|
announceToSystem,
|
|
63
68
|
authMiddleware,
|
|
64
69
|
backfillSystemChannelMembers,
|
|
65
|
-
checkHealth,
|
|
66
|
-
checkStaleSend,
|
|
67
70
|
cleanExpiredSessions,
|
|
68
71
|
createSession,
|
|
69
72
|
deleteSession,
|
|
70
73
|
deleteSystemsConfig,
|
|
71
74
|
deliverMessage,
|
|
72
|
-
deliverSproutedNotice,
|
|
73
75
|
ensureSystemChannel,
|
|
74
|
-
formatHoldNotice,
|
|
75
|
-
generateSystemFallbackReply,
|
|
76
76
|
getAllDiscoveredExtensions,
|
|
77
77
|
getAllDiscoveredExtensionsDetailed,
|
|
78
|
-
getDeliveryManager,
|
|
79
78
|
getExtensionStandardSkills,
|
|
80
79
|
getLoadedExtensions,
|
|
81
|
-
getMindManager,
|
|
82
|
-
getScheduler,
|
|
83
80
|
getSessionUserId,
|
|
84
81
|
getSleepManagerIfReady,
|
|
85
82
|
getTokenBudget,
|
|
86
|
-
getTypingMap,
|
|
87
|
-
initDeliveryManager,
|
|
88
83
|
initMailPoller,
|
|
89
|
-
initMindManager,
|
|
90
|
-
initScheduler,
|
|
91
84
|
initSleepManager,
|
|
92
85
|
initTokenBudget,
|
|
93
86
|
installNpmExtension,
|
|
94
87
|
invalidateMindUserCache,
|
|
95
88
|
invalidateSessionCache,
|
|
96
|
-
isConversationId,
|
|
97
89
|
joinSystemChannel,
|
|
98
90
|
joinSystemChannelForMind,
|
|
99
91
|
linkToolResultToTurn,
|
|
100
92
|
loadAllExtensions,
|
|
101
93
|
notifyExtensionsDaemonStart,
|
|
102
94
|
notifyExtensionsDaemonStop,
|
|
103
|
-
publishTypingForChannels,
|
|
104
95
|
readSystemsConfig,
|
|
105
96
|
recordOutbound,
|
|
106
97
|
requireAdmin,
|
|
107
98
|
requireAdminOrSystem,
|
|
108
99
|
requireSelf,
|
|
109
|
-
sendSystemMessage,
|
|
110
|
-
sendSystemMessageDirect,
|
|
111
100
|
setExtensionEnabled,
|
|
112
|
-
shouldGenerateSystemFallback,
|
|
113
101
|
startMindFull,
|
|
114
102
|
stopMindFull,
|
|
115
|
-
syncProviderToMinds,
|
|
116
|
-
tryGetMindManager,
|
|
117
103
|
uninstallNpmExtension,
|
|
118
104
|
writeSystemsConfig
|
|
119
|
-
} from "./chunk-
|
|
120
|
-
import "./chunk-
|
|
105
|
+
} from "./chunk-EQKSRVDE.js";
|
|
106
|
+
import "./chunk-SFRYRW37.js";
|
|
121
107
|
import {
|
|
122
108
|
applyInitFiles,
|
|
123
109
|
applyTemplateHomeFiles,
|
|
@@ -127,28 +113,52 @@ import {
|
|
|
127
113
|
isKnownTemplate,
|
|
128
114
|
listFiles
|
|
129
115
|
} from "./chunk-RRZ4RQQU.js";
|
|
116
|
+
import {
|
|
117
|
+
getScheduler,
|
|
118
|
+
initScheduler
|
|
119
|
+
} from "./chunk-7FDWPQCU.js";
|
|
120
|
+
import {
|
|
121
|
+
checkStaleSend,
|
|
122
|
+
formatHoldNotice,
|
|
123
|
+
getDeliveryManager,
|
|
124
|
+
getTypingMap,
|
|
125
|
+
initDeliveryManager,
|
|
126
|
+
isConversationId,
|
|
127
|
+
publishTypingForChannels
|
|
128
|
+
} from "./chunk-JI67D7O2.js";
|
|
129
|
+
import {
|
|
130
|
+
MindStartupError,
|
|
131
|
+
RestartTracker,
|
|
132
|
+
RotatingLog,
|
|
133
|
+
checkHealth,
|
|
134
|
+
getMindManager,
|
|
135
|
+
initMindManager,
|
|
136
|
+
syncProviderToMinds,
|
|
137
|
+
tryGetMindManager
|
|
138
|
+
} from "./chunk-ALIVXDM4.js";
|
|
130
139
|
import {
|
|
131
140
|
extractTextContent
|
|
132
|
-
} from "./chunk-
|
|
141
|
+
} from "./chunk-6BM2I736.js";
|
|
133
142
|
import {
|
|
143
|
+
SYSTEM_MIND,
|
|
134
144
|
assignSession,
|
|
135
145
|
completeOrphanedTurns,
|
|
136
146
|
completeTurn,
|
|
137
147
|
createTurn,
|
|
138
148
|
getActiveTurnId,
|
|
149
|
+
getPeriodKey,
|
|
139
150
|
getToolUseEventId,
|
|
151
|
+
getUtcTimeRange,
|
|
140
152
|
initSummarizer,
|
|
141
153
|
isoWeekKeyForDateStr,
|
|
142
154
|
markErrored,
|
|
143
|
-
|
|
144
|
-
subscribe as subscribe3,
|
|
145
|
-
subscribeAll,
|
|
155
|
+
parseUtcDateTime,
|
|
146
156
|
summarizeOrphanedTurns,
|
|
147
157
|
summarizeTurn,
|
|
148
158
|
takeErrored,
|
|
149
159
|
trackToolUse,
|
|
150
160
|
utcDateTimeStr
|
|
151
|
-
} from "./chunk-
|
|
161
|
+
} from "./chunk-7DXLIYPY.js";
|
|
152
162
|
import {
|
|
153
163
|
SEED_SKILLS,
|
|
154
164
|
STANDARD_SKILLS,
|
|
@@ -170,7 +180,7 @@ import {
|
|
|
170
180
|
syncBuiltinSkills,
|
|
171
181
|
uninstallSkill,
|
|
172
182
|
updateSkill
|
|
173
|
-
} from "./chunk-
|
|
183
|
+
} from "./chunk-TGJHGVBZ.js";
|
|
174
184
|
import {
|
|
175
185
|
approveUser,
|
|
176
186
|
changePassword,
|
|
@@ -178,6 +188,7 @@ import {
|
|
|
178
188
|
createUser,
|
|
179
189
|
deleteMindUser as deleteMindUser2,
|
|
180
190
|
deleteUser,
|
|
191
|
+
getDisplayNames,
|
|
181
192
|
getOrCreateMindUser,
|
|
182
193
|
getOrCreateSystemUser,
|
|
183
194
|
getUser,
|
|
@@ -188,8 +199,39 @@ import {
|
|
|
188
199
|
setUserRole,
|
|
189
200
|
syncMindProfile,
|
|
190
201
|
updateUserProfile,
|
|
191
|
-
verifyUser
|
|
192
|
-
|
|
202
|
+
verifyUser,
|
|
203
|
+
withSenderDisplayNames
|
|
204
|
+
} from "./chunk-UC6BNMGT.js";
|
|
205
|
+
import {
|
|
206
|
+
captureReflection,
|
|
207
|
+
clearDeliveredEvents,
|
|
208
|
+
deliverEvent,
|
|
209
|
+
drainEvents,
|
|
210
|
+
eventLabel,
|
|
211
|
+
formatEvents,
|
|
212
|
+
latestEvent,
|
|
213
|
+
latestFailureEvent,
|
|
214
|
+
listEvents,
|
|
215
|
+
parseMeta,
|
|
216
|
+
recordNotice
|
|
217
|
+
} from "./chunk-7OZWKHA3.js";
|
|
218
|
+
import {
|
|
219
|
+
publish as publish2,
|
|
220
|
+
subscribe as subscribe2,
|
|
221
|
+
subscribeAll
|
|
222
|
+
} from "./chunk-LNNI3MS4.js";
|
|
223
|
+
import {
|
|
224
|
+
PROMPT_DEFAULTS,
|
|
225
|
+
PROMPT_KEYS,
|
|
226
|
+
getMindPromptDefaults,
|
|
227
|
+
getPrompt,
|
|
228
|
+
getPromptIfCustom,
|
|
229
|
+
substitute
|
|
230
|
+
} from "./chunk-5TPC4KCV.js";
|
|
231
|
+
import {
|
|
232
|
+
isSandboxEnabled,
|
|
233
|
+
wrapForSandbox
|
|
234
|
+
} from "./chunk-ZUG76D3P.js";
|
|
193
235
|
import {
|
|
194
236
|
addMessage,
|
|
195
237
|
createChannel,
|
|
@@ -203,6 +245,8 @@ import {
|
|
|
203
245
|
getChannelName,
|
|
204
246
|
getChannelSettings,
|
|
205
247
|
getConversation,
|
|
248
|
+
getConversationWithParticipants,
|
|
249
|
+
getLastMessageBySender,
|
|
206
250
|
getMessages,
|
|
207
251
|
getMessagesPaginated,
|
|
208
252
|
getParticipants,
|
|
@@ -218,39 +262,27 @@ import {
|
|
|
218
262
|
listConversationsForUser,
|
|
219
263
|
listConversationsWithParticipants,
|
|
220
264
|
markConversationRead,
|
|
221
|
-
publish as
|
|
265
|
+
publish as publish3,
|
|
222
266
|
setConversationPrivate,
|
|
223
|
-
subscribe as
|
|
267
|
+
subscribe as subscribe3,
|
|
268
|
+
subscribeParticipantAdded,
|
|
224
269
|
updateChannelSettings
|
|
225
|
-
} from "./chunk-
|
|
226
|
-
import {
|
|
227
|
-
PROMPT_DEFAULTS,
|
|
228
|
-
PROMPT_KEYS,
|
|
229
|
-
getMindPromptDefaults,
|
|
230
|
-
getPrompt,
|
|
231
|
-
getPromptIfCustom,
|
|
232
|
-
substitute
|
|
233
|
-
} from "./chunk-JPGHSXSV.js";
|
|
234
|
-
import {
|
|
235
|
-
isSandboxEnabled,
|
|
236
|
-
wrapForSandbox
|
|
237
|
-
} from "./chunk-JCWB2ITW.js";
|
|
238
|
-
import {
|
|
239
|
-
clearDeliveredNotices,
|
|
240
|
-
drainNotices,
|
|
241
|
-
formatNotices,
|
|
242
|
-
latestFailureNotice,
|
|
243
|
-
latestNotice,
|
|
244
|
-
recordNotice
|
|
245
|
-
} from "./chunk-XTEKINWG.js";
|
|
270
|
+
} from "./chunk-574M6I7F.js";
|
|
246
271
|
import {
|
|
247
272
|
getActiveMinds,
|
|
248
273
|
onMindEvent,
|
|
249
274
|
stopAll
|
|
250
|
-
} from "./chunk-
|
|
275
|
+
} from "./chunk-E5S3ILVA.js";
|
|
276
|
+
import {
|
|
277
|
+
broadcast,
|
|
278
|
+
getEventsSince,
|
|
279
|
+
nextEventId,
|
|
280
|
+
publish,
|
|
281
|
+
subscribe
|
|
282
|
+
} from "./chunk-3BAFMGCJ.js";
|
|
251
283
|
import {
|
|
252
284
|
normalizeAvatar
|
|
253
|
-
} from "./chunk-
|
|
285
|
+
} from "./chunk-X67VT4QO.js";
|
|
254
286
|
import {
|
|
255
287
|
safeResolveWithinBase
|
|
256
288
|
} from "./chunk-DW4TRDOZ.js";
|
|
@@ -258,9 +290,10 @@ import {
|
|
|
258
290
|
readVoluteConfig,
|
|
259
291
|
writeVoluteConfig
|
|
260
292
|
} from "./chunk-WCFSBH33.js";
|
|
261
|
-
import "./chunk-
|
|
293
|
+
import "./chunk-5FVANDON.js";
|
|
262
294
|
import {
|
|
263
295
|
chownMindDir,
|
|
296
|
+
chownMindFile,
|
|
264
297
|
createMindUser,
|
|
265
298
|
deleteMindUser,
|
|
266
299
|
ensureVoluteGroup,
|
|
@@ -269,14 +302,14 @@ import {
|
|
|
269
302
|
isIsolationEnabled,
|
|
270
303
|
resolveVoluteBin,
|
|
271
304
|
wrapForIsolation
|
|
272
|
-
} from "./chunk-
|
|
305
|
+
} from "./chunk-AJWFDIEY.js";
|
|
273
306
|
import {
|
|
274
307
|
loadMergedEnv,
|
|
275
308
|
mindEnvPath,
|
|
276
309
|
readEnv,
|
|
277
310
|
sharedEnvPath,
|
|
278
311
|
writeEnv
|
|
279
|
-
} from "./chunk-
|
|
312
|
+
} from "./chunk-7UWXAJMK.js";
|
|
280
313
|
import {
|
|
281
314
|
addCustomModel,
|
|
282
315
|
aiCompleteUtility,
|
|
@@ -287,31 +320,21 @@ import {
|
|
|
287
320
|
getCustomModels,
|
|
288
321
|
getEnabledModels,
|
|
289
322
|
getUtilityModel,
|
|
323
|
+
isAiConfigured,
|
|
290
324
|
missingCredentialWarning,
|
|
291
325
|
qualifyModelId,
|
|
292
326
|
removeAiConfig,
|
|
293
327
|
removeCustomModel,
|
|
294
328
|
removeProviderConfig,
|
|
295
329
|
resolveApiKey,
|
|
330
|
+
resolveOAuthCredentials,
|
|
296
331
|
resolveTemplate,
|
|
297
332
|
saveProviderConfig,
|
|
298
333
|
setEnabledModels,
|
|
299
334
|
setProviderRefreshHook,
|
|
300
335
|
setUtilityModel,
|
|
301
336
|
unqualifyModelId
|
|
302
|
-
} from "./chunk-
|
|
303
|
-
import {
|
|
304
|
-
isSetupComplete,
|
|
305
|
-
readGlobalConfig,
|
|
306
|
-
writeGlobalConfig
|
|
307
|
-
} from "./chunk-NJHN6IRB.js";
|
|
308
|
-
import {
|
|
309
|
-
broadcast,
|
|
310
|
-
getEventsSince,
|
|
311
|
-
nextEventId,
|
|
312
|
-
publish,
|
|
313
|
-
subscribe
|
|
314
|
-
} from "./chunk-3B427GUK.js";
|
|
337
|
+
} from "./chunk-632FJ23O.js";
|
|
315
338
|
import {
|
|
316
339
|
logBuffer,
|
|
317
340
|
logger_default
|
|
@@ -320,7 +343,6 @@ import {
|
|
|
320
343
|
buildVoluteSlug
|
|
321
344
|
} from "./chunk-LGNUFVMR.js";
|
|
322
345
|
import {
|
|
323
|
-
SPIRIT_NAME,
|
|
324
346
|
addMind,
|
|
325
347
|
addVariant,
|
|
326
348
|
countCappedMinds,
|
|
@@ -331,10 +353,14 @@ import {
|
|
|
331
353
|
findVariants,
|
|
332
354
|
getBaseName,
|
|
333
355
|
getDb,
|
|
356
|
+
getSpiritName,
|
|
357
|
+
isSetupComplete,
|
|
358
|
+
isSpiritName,
|
|
334
359
|
mindDir,
|
|
335
360
|
mindTmpDir,
|
|
336
361
|
nextPort,
|
|
337
362
|
readAllMinds,
|
|
363
|
+
readGlobalConfig,
|
|
338
364
|
readRegistry,
|
|
339
365
|
removeMind,
|
|
340
366
|
resolveMindDir,
|
|
@@ -344,20 +370,23 @@ import {
|
|
|
344
370
|
setMindTemplateHash,
|
|
345
371
|
stateDir,
|
|
346
372
|
validateMindName,
|
|
373
|
+
validateSpiritName,
|
|
347
374
|
voluteHome,
|
|
348
|
-
voluteSystemDir
|
|
349
|
-
|
|
375
|
+
voluteSystemDir,
|
|
376
|
+
writeGlobalConfig
|
|
377
|
+
} from "./chunk-CAIYV33P.js";
|
|
350
378
|
import {
|
|
351
379
|
activity,
|
|
352
380
|
channels,
|
|
353
381
|
conversationParticipants,
|
|
354
382
|
conversations,
|
|
383
|
+
messages,
|
|
355
384
|
mindHistory,
|
|
356
385
|
summaries,
|
|
357
386
|
systemPrompts,
|
|
358
387
|
turns,
|
|
359
388
|
users
|
|
360
|
-
} from "./chunk-
|
|
389
|
+
} from "./chunk-5IIVOWSP.js";
|
|
361
390
|
import "./chunk-K3NQKI34.js";
|
|
362
391
|
|
|
363
392
|
// packages/daemon/src/daemon.ts
|
|
@@ -454,7 +483,7 @@ var BackupManager = class {
|
|
|
454
483
|
this.consecutiveFailures = 0;
|
|
455
484
|
}
|
|
456
485
|
/**
|
|
457
|
-
* Track consecutive scheduled-backup failures and actively notify the
|
|
486
|
+
* Track consecutive scheduled-backup failures and actively notify the host
|
|
458
487
|
* on the 1st and then every Nth. Returns whether it notified (for tests).
|
|
459
488
|
* Notification failures are swallowed — surfacing a backup failure must never
|
|
460
489
|
* take the daemon's backup loop down with it.
|
|
@@ -468,24 +497,21 @@ var BackupManager = class {
|
|
|
468
497
|
try {
|
|
469
498
|
await publish({
|
|
470
499
|
type: "backup_failed",
|
|
471
|
-
mind:
|
|
500
|
+
mind: getSpiritName(),
|
|
472
501
|
summary,
|
|
473
502
|
metadata: { consecutiveFailures: n, error: reason }
|
|
474
503
|
});
|
|
475
504
|
} catch (pubErr) {
|
|
476
505
|
blog.error("failed to publish backup-failure activity", logger_default.errorData(pubErr));
|
|
477
506
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
507
|
+
const { deliverEvent: deliverEvent2 } = await import("./system-events-A6IGCEC3.js");
|
|
508
|
+
await deliverEvent2(getSpiritName(), {
|
|
509
|
+
type: "notice",
|
|
510
|
+
meta: { subtype: "backup" },
|
|
511
|
+
body: `${summary}
|
|
483
512
|
|
|
484
513
|
Backups are not being saved. Check \`volute backup status\` and the Backups settings tab.`
|
|
485
|
-
|
|
486
|
-
} catch (msgErr) {
|
|
487
|
-
blog.error("failed to send backup-failure system message", logger_default.errorData(msgErr));
|
|
488
|
-
}
|
|
514
|
+
});
|
|
489
515
|
return true;
|
|
490
516
|
}
|
|
491
517
|
};
|
|
@@ -852,6 +878,240 @@ import { z } from "zod";
|
|
|
852
878
|
|
|
853
879
|
// packages/daemon/src/lib/services/imagegen.ts
|
|
854
880
|
import Replicate from "replicate";
|
|
881
|
+
|
|
882
|
+
// packages/daemon/src/lib/services/imagegen-codex.ts
|
|
883
|
+
var CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
884
|
+
var HOST_MODEL = "gpt-5.4-mini";
|
|
885
|
+
var CODEX_NOT_ENTITLED_SENTINEL = "Tool choice 'image_generation' not found in 'tools' parameter.";
|
|
886
|
+
var CodexNotEntitledError = class extends Error {
|
|
887
|
+
constructor() {
|
|
888
|
+
super(CODEX_NOT_ENTITLED_SENTINEL);
|
|
889
|
+
this.name = "CodexNotEntitledError";
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
function accountIdFromToken(token) {
|
|
893
|
+
const parts = token.split(".");
|
|
894
|
+
if (parts.length !== 3) return void 0;
|
|
895
|
+
try {
|
|
896
|
+
const claims = JSON.parse(Buffer.from(parts[1], "base64url").toString());
|
|
897
|
+
const auth = claims["https://api.openai.com/auth"];
|
|
898
|
+
return auth?.chatgpt_account_id;
|
|
899
|
+
} catch {
|
|
900
|
+
return void 0;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
function codexHeaders(token) {
|
|
904
|
+
const headers = {
|
|
905
|
+
Authorization: `Bearer ${token}`,
|
|
906
|
+
"Content-Type": "application/json",
|
|
907
|
+
"OpenAI-Beta": "responses=experimental",
|
|
908
|
+
originator: "codex_cli_rs",
|
|
909
|
+
session_id: crypto.randomUUID()
|
|
910
|
+
};
|
|
911
|
+
const accountId = accountIdFromToken(token);
|
|
912
|
+
if (accountId) headers["chatgpt-account-id"] = accountId;
|
|
913
|
+
return headers;
|
|
914
|
+
}
|
|
915
|
+
function buildCodexRequest(imageModel, prompt) {
|
|
916
|
+
return {
|
|
917
|
+
model: HOST_MODEL,
|
|
918
|
+
instructions: "Use the image_generation tool immediately to render the request. Never ask questions.",
|
|
919
|
+
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: prompt }] }],
|
|
920
|
+
tools: [
|
|
921
|
+
{
|
|
922
|
+
type: "image_generation",
|
|
923
|
+
model: imageModel,
|
|
924
|
+
size: "1024x1024",
|
|
925
|
+
quality: "auto",
|
|
926
|
+
output_format: "png",
|
|
927
|
+
partial_images: 1
|
|
928
|
+
}
|
|
929
|
+
],
|
|
930
|
+
tool_choice: { type: "image_generation" },
|
|
931
|
+
stream: true,
|
|
932
|
+
store: false
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
async function parseCodexImageStream(body, signal) {
|
|
936
|
+
const reader = body.getReader();
|
|
937
|
+
const decoder = new TextDecoder();
|
|
938
|
+
let buf = "";
|
|
939
|
+
let best = "";
|
|
940
|
+
let completed = false;
|
|
941
|
+
const consumeLine = (line) => {
|
|
942
|
+
if (!line.startsWith("data:")) return;
|
|
943
|
+
const payload = line.slice(5).trim();
|
|
944
|
+
if (!payload || payload === "[DONE]") return;
|
|
945
|
+
let evt;
|
|
946
|
+
try {
|
|
947
|
+
evt = JSON.parse(payload);
|
|
948
|
+
} catch {
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
if (evt.type === "response.completed") completed = true;
|
|
952
|
+
const candidate = evt.partial_image_b64 ?? evt.result ?? evt.item?.result ?? void 0;
|
|
953
|
+
if (candidate && candidate.length > best.length) best = candidate;
|
|
954
|
+
};
|
|
955
|
+
for (; ; ) {
|
|
956
|
+
if (signal?.aborted) throw new Error("Codex stream aborted");
|
|
957
|
+
const { value, done } = await reader.read();
|
|
958
|
+
if (done) break;
|
|
959
|
+
buf += decoder.decode(value, { stream: true });
|
|
960
|
+
for (let nl = buf.indexOf("\n"); nl !== -1; nl = buf.indexOf("\n")) {
|
|
961
|
+
consumeLine(buf.slice(0, nl));
|
|
962
|
+
buf = buf.slice(nl + 1);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
consumeLine(buf);
|
|
966
|
+
if (!completed) {
|
|
967
|
+
throw new Error("Codex stream closed before completion \u2014 only a partial image was received");
|
|
968
|
+
}
|
|
969
|
+
if (!best) throw new Error("Codex stream produced no image");
|
|
970
|
+
return Buffer.from(best, "base64");
|
|
971
|
+
}
|
|
972
|
+
async function codexGenerate(model, prompt, cred, signal) {
|
|
973
|
+
const res = await fetch(CODEX_RESPONSES_URL, {
|
|
974
|
+
method: "POST",
|
|
975
|
+
headers: codexHeaders(cred.token),
|
|
976
|
+
body: JSON.stringify(buildCodexRequest(model, prompt)),
|
|
977
|
+
signal
|
|
978
|
+
});
|
|
979
|
+
if (res.status === 400) {
|
|
980
|
+
const errBody = await res.text().catch(() => "");
|
|
981
|
+
if (errBody.includes(CODEX_NOT_ENTITLED_SENTINEL)) throw new CodexNotEntitledError();
|
|
982
|
+
throw new Error(`Codex image generation failed (400): ${errBody.slice(0, 200)}`);
|
|
983
|
+
}
|
|
984
|
+
if (!res.ok || !res.body) {
|
|
985
|
+
const errBody = await res.text().catch(() => `HTTP ${res.status}`);
|
|
986
|
+
throw new Error(`Codex image generation failed (${res.status}): ${errBody.slice(0, 200)}`);
|
|
987
|
+
}
|
|
988
|
+
return parseCodexImageStream(res.body, signal);
|
|
989
|
+
}
|
|
990
|
+
async function probeCodexEntitlement(cred) {
|
|
991
|
+
const controller = new AbortController();
|
|
992
|
+
let res;
|
|
993
|
+
try {
|
|
994
|
+
res = await fetch(CODEX_RESPONSES_URL, {
|
|
995
|
+
method: "POST",
|
|
996
|
+
headers: codexHeaders(cred.token),
|
|
997
|
+
body: JSON.stringify(buildCodexRequest("gpt-image-2", "entitlement probe")),
|
|
998
|
+
signal: controller.signal
|
|
999
|
+
});
|
|
1000
|
+
} catch (err) {
|
|
1001
|
+
if (err instanceof Error && err.name === "AbortError") return "entitled";
|
|
1002
|
+
throw err;
|
|
1003
|
+
}
|
|
1004
|
+
if (res.status === 400) {
|
|
1005
|
+
const body = await res.text().catch(() => "");
|
|
1006
|
+
if (body.includes(CODEX_NOT_ENTITLED_SENTINEL)) return "not_entitled";
|
|
1007
|
+
throw new Error(`Codex entitlement probe failed (400): ${body.slice(0, 200)}`);
|
|
1008
|
+
}
|
|
1009
|
+
if (!res.ok || !res.body) {
|
|
1010
|
+
throw new Error(`Codex entitlement probe failed (${res.status})`);
|
|
1011
|
+
}
|
|
1012
|
+
const reader = res.body.getReader();
|
|
1013
|
+
const decoder = new TextDecoder();
|
|
1014
|
+
let buf = "";
|
|
1015
|
+
try {
|
|
1016
|
+
for (; ; ) {
|
|
1017
|
+
const { value, done } = await reader.read();
|
|
1018
|
+
if (done) break;
|
|
1019
|
+
buf += decoder.decode(value, { stream: true });
|
|
1020
|
+
if (buf.includes("image_generation_call")) {
|
|
1021
|
+
controller.abort();
|
|
1022
|
+
return "entitled";
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
} catch (err) {
|
|
1026
|
+
if (err instanceof Error && err.name === "AbortError") return "entitled";
|
|
1027
|
+
throw err;
|
|
1028
|
+
}
|
|
1029
|
+
return "not_entitled";
|
|
1030
|
+
}
|
|
1031
|
+
async function codexSearch(_query, _cred) {
|
|
1032
|
+
return [
|
|
1033
|
+
{
|
|
1034
|
+
id: "openai-codex:gpt-image-2",
|
|
1035
|
+
name: "gpt-image-2 (ChatGPT subscription)",
|
|
1036
|
+
description: "OpenAI's image model via a ChatGPT/Codex subscription \u2014 no API key required.",
|
|
1037
|
+
owner: "openai"
|
|
1038
|
+
}
|
|
1039
|
+
];
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// packages/daemon/src/lib/services/imagegen-xai.ts
|
|
1043
|
+
var xaiLog = logger_default.child("imagegen-xai");
|
|
1044
|
+
var XAI_IMAGES_URL = "https://api.x.ai/v1/images/generations";
|
|
1045
|
+
var XaiNotEntitledError = class extends Error {
|
|
1046
|
+
constructor() {
|
|
1047
|
+
super("xAI plan tier is not entitled to image generation");
|
|
1048
|
+
this.name = "XaiNotEntitledError";
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
function xaiApiKeyFallback() {
|
|
1052
|
+
const config2 = readGlobalConfig();
|
|
1053
|
+
return config2.imagegen?.providers?.xai?.apiKey ?? config2.ai?.providers?.xai?.apiKey ?? process.env.XAI_API_KEY ?? void 0;
|
|
1054
|
+
}
|
|
1055
|
+
async function requestXaiImage(model, prompt, token, signal) {
|
|
1056
|
+
const res = await fetch(XAI_IMAGES_URL, {
|
|
1057
|
+
method: "POST",
|
|
1058
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
1059
|
+
body: JSON.stringify({ model, prompt, n: 1, response_format: "b64_json" }),
|
|
1060
|
+
signal
|
|
1061
|
+
});
|
|
1062
|
+
if (!res.ok) {
|
|
1063
|
+
return {
|
|
1064
|
+
ok: false,
|
|
1065
|
+
status: res.status,
|
|
1066
|
+
body: (await res.text().catch(() => "")).slice(0, 200)
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
const data = await res.json();
|
|
1070
|
+
const item = data.data?.[0];
|
|
1071
|
+
if (item?.b64_json) return { ok: true, buffer: Buffer.from(item.b64_json, "base64") };
|
|
1072
|
+
if (item?.url) {
|
|
1073
|
+
const imgRes = await fetch(item.url, { signal });
|
|
1074
|
+
if (!imgRes.ok) throw new Error(`Failed to fetch xAI image URL: ${imgRes.status}`);
|
|
1075
|
+
return { ok: true, buffer: Buffer.from(await imgRes.arrayBuffer()) };
|
|
1076
|
+
}
|
|
1077
|
+
throw new Error("xAI returned no image");
|
|
1078
|
+
}
|
|
1079
|
+
async function xaiGenerate(model, prompt, cred, signal) {
|
|
1080
|
+
const first = await requestXaiImage(model, prompt, cred.token, signal);
|
|
1081
|
+
if (first.ok) return first.buffer;
|
|
1082
|
+
if (first.status === 403 && cred.kind === "oauth") {
|
|
1083
|
+
const apiKey = xaiApiKeyFallback();
|
|
1084
|
+
if (apiKey) {
|
|
1085
|
+
xaiLog.warn(
|
|
1086
|
+
"xAI OAuth token not entitled for image generation; retrying with XAI_API_KEY (billed to the API account)."
|
|
1087
|
+
);
|
|
1088
|
+
const retry = await requestXaiImage(model, prompt, apiKey, signal);
|
|
1089
|
+
if (retry.ok) return retry.buffer;
|
|
1090
|
+
if (retry.status === 403) throw new XaiNotEntitledError();
|
|
1091
|
+
throw new Error(`xAI image generation failed (${retry.status}): ${retry.body}`);
|
|
1092
|
+
}
|
|
1093
|
+
throw new XaiNotEntitledError();
|
|
1094
|
+
}
|
|
1095
|
+
throw new Error(`xAI image generation failed (${first.status}): ${first.body}`);
|
|
1096
|
+
}
|
|
1097
|
+
async function xaiSearch(_query, _cred) {
|
|
1098
|
+
return [
|
|
1099
|
+
{
|
|
1100
|
+
id: "xai:grok-imagine-image",
|
|
1101
|
+
name: "Grok Imagine",
|
|
1102
|
+
description: "xAI Grok Imagine image model ($0.02/image via API key).",
|
|
1103
|
+
owner: "xai"
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
id: "xai:grok-imagine-image-quality",
|
|
1107
|
+
name: "Grok Imagine (quality)",
|
|
1108
|
+
description: "Higher-quality Grok Imagine image model ($0.05/image via API key).",
|
|
1109
|
+
owner: "xai"
|
|
1110
|
+
}
|
|
1111
|
+
];
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// packages/daemon/src/lib/services/imagegen.ts
|
|
855
1115
|
var PROVIDERS = {
|
|
856
1116
|
replicate: {
|
|
857
1117
|
envVar: "REPLICATE_API_TOKEN",
|
|
@@ -862,17 +1122,33 @@ var PROVIDERS = {
|
|
|
862
1122
|
envVar: "OPENROUTER_API_KEY",
|
|
863
1123
|
generate: openrouterGenerate,
|
|
864
1124
|
search: openrouterSearch
|
|
1125
|
+
},
|
|
1126
|
+
"openai-codex": {
|
|
1127
|
+
// Subscription-only: no env var API key path. Credentials come from the
|
|
1128
|
+
// openai-codex AI provider's OAuth (chat/image unification).
|
|
1129
|
+
aiProviderId: "openai-codex",
|
|
1130
|
+
generate: codexGenerate,
|
|
1131
|
+
search: codexSearch
|
|
1132
|
+
},
|
|
1133
|
+
xai: {
|
|
1134
|
+
// Both credential kinds hit the same endpoint: subscription OAuth (preferred)
|
|
1135
|
+
// or a metered XAI_API_KEY. OAuth 403s fall back to the key (see imagegen-xai).
|
|
1136
|
+
envVar: "XAI_API_KEY",
|
|
1137
|
+
aiProviderId: "xai",
|
|
1138
|
+
generate: xaiGenerate,
|
|
1139
|
+
search: xaiSearch
|
|
865
1140
|
}
|
|
866
1141
|
};
|
|
867
|
-
async function replicateGenerate(model, prompt,
|
|
868
|
-
const replicate = new Replicate({ auth:
|
|
1142
|
+
async function replicateGenerate(model, prompt, cred, signal) {
|
|
1143
|
+
const replicate = new Replicate({ auth: cred.token });
|
|
869
1144
|
const output = await replicate.run(model, {
|
|
870
|
-
input: { prompt }
|
|
1145
|
+
input: { prompt },
|
|
1146
|
+
signal
|
|
871
1147
|
});
|
|
872
1148
|
const file = Array.isArray(output) ? output[0] : output;
|
|
873
1149
|
if (!file) throw new Error(`Model ${model} returned no output`);
|
|
874
1150
|
if (typeof file === "string") {
|
|
875
|
-
const res = await fetch(file);
|
|
1151
|
+
const res = await fetch(file, { signal });
|
|
876
1152
|
if (!res.ok) throw new Error(`Failed to fetch image from URL: ${res.status}`);
|
|
877
1153
|
return Buffer.from(await res.arrayBuffer());
|
|
878
1154
|
}
|
|
@@ -882,8 +1158,8 @@ async function replicateGenerate(model, prompt, apiKey) {
|
|
|
882
1158
|
}
|
|
883
1159
|
return Buffer.concat(chunks);
|
|
884
1160
|
}
|
|
885
|
-
async function replicateSearch(query,
|
|
886
|
-
const replicate = new Replicate({ auth:
|
|
1161
|
+
async function replicateSearch(query, cred) {
|
|
1162
|
+
const replicate = new Replicate({ auth: cred.token });
|
|
887
1163
|
const response = await replicate.models.search(query || "text to image");
|
|
888
1164
|
return response.results.slice(0, 20).map((m) => ({
|
|
889
1165
|
id: `replicate:${m.owner}/${m.name}`,
|
|
@@ -892,18 +1168,19 @@ async function replicateSearch(query, apiKey) {
|
|
|
892
1168
|
owner: m.owner
|
|
893
1169
|
}));
|
|
894
1170
|
}
|
|
895
|
-
async function openrouterGenerate(model, prompt,
|
|
1171
|
+
async function openrouterGenerate(model, prompt, cred, signal) {
|
|
896
1172
|
const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
897
1173
|
method: "POST",
|
|
898
1174
|
headers: {
|
|
899
1175
|
"Content-Type": "application/json",
|
|
900
|
-
Authorization: `Bearer ${
|
|
1176
|
+
Authorization: `Bearer ${cred.token}`
|
|
901
1177
|
},
|
|
902
1178
|
body: JSON.stringify({
|
|
903
1179
|
model,
|
|
904
1180
|
messages: [{ role: "user", content: prompt }],
|
|
905
1181
|
modalities: ["image", "text"]
|
|
906
|
-
})
|
|
1182
|
+
}),
|
|
1183
|
+
signal
|
|
907
1184
|
});
|
|
908
1185
|
if (!res.ok) {
|
|
909
1186
|
const body = await res.text().catch(() => `HTTP ${res.status}`);
|
|
@@ -916,11 +1193,11 @@ async function openrouterGenerate(model, prompt, apiKey) {
|
|
|
916
1193
|
if (match) {
|
|
917
1194
|
return Buffer.from(match[1], "base64");
|
|
918
1195
|
}
|
|
919
|
-
const imgRes = await fetch(imageUrl);
|
|
1196
|
+
const imgRes = await fetch(imageUrl, { signal });
|
|
920
1197
|
if (!imgRes.ok) throw new Error(`Failed to fetch image from URL: ${imgRes.status}`);
|
|
921
1198
|
return Buffer.from(await imgRes.arrayBuffer());
|
|
922
1199
|
}
|
|
923
|
-
async function openrouterSearch(query,
|
|
1200
|
+
async function openrouterSearch(query, _cred) {
|
|
924
1201
|
const res = await fetch("https://openrouter.ai/api/v1/models?output_modalities=image");
|
|
925
1202
|
if (!res.ok) throw new Error(`OpenRouter model list failed: ${res.status}`);
|
|
926
1203
|
const data = await res.json();
|
|
@@ -978,23 +1255,33 @@ function getConfiguredProviders2() {
|
|
|
978
1255
|
const config2 = readGlobalConfig();
|
|
979
1256
|
const ig = config2.imagegen ?? null;
|
|
980
1257
|
const aiProviders = config2.ai?.providers;
|
|
981
|
-
return Object.entries(PROVIDERS).map(([id, { envVar }]) => {
|
|
1258
|
+
return Object.entries(PROVIDERS).map(([id, { envVar, aiProviderId }]) => {
|
|
1259
|
+
const aiId = aiProviderId ?? id;
|
|
982
1260
|
const providerConfig = ig?.providers?.[id];
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
if (
|
|
986
|
-
|
|
1261
|
+
const entitlement = ig?.entitlements?.[id];
|
|
1262
|
+
let base;
|
|
1263
|
+
if (providerConfig?.apiKey) base = { configured: true, authMethod: "api_key" };
|
|
1264
|
+
else if (aiProviders?.[aiId]?.oauth) base = { configured: true, authMethod: "oauth" };
|
|
1265
|
+
else if (aiProviders?.[aiId]?.apiKey) base = { configured: true, authMethod: "api_key" };
|
|
1266
|
+
else if (envVar && process.env[envVar]) base = { configured: true, authMethod: "env_var" };
|
|
1267
|
+
else base = { configured: false, authMethod: null };
|
|
1268
|
+
return { id, ...base, entitlement };
|
|
987
1269
|
});
|
|
988
1270
|
}
|
|
989
|
-
function
|
|
1271
|
+
async function resolveCredential(providerId) {
|
|
990
1272
|
const provider = PROVIDERS[providerId];
|
|
991
1273
|
if (!provider) return void 0;
|
|
1274
|
+
const aiProviderId = provider.aiProviderId ?? providerId;
|
|
992
1275
|
const config2 = readGlobalConfig();
|
|
993
1276
|
const configKey = config2.imagegen?.providers?.[providerId]?.apiKey;
|
|
994
|
-
if (configKey) return configKey;
|
|
995
|
-
const
|
|
996
|
-
if (
|
|
997
|
-
|
|
1277
|
+
if (configKey) return { kind: "api_key", token: configKey };
|
|
1278
|
+
const oauth = await resolveOAuthCredentials(aiProviderId);
|
|
1279
|
+
if (oauth) return { kind: "oauth", token: oauth.access };
|
|
1280
|
+
const aiKey = config2.ai?.providers?.[aiProviderId]?.apiKey;
|
|
1281
|
+
if (aiKey) return { kind: "api_key", token: aiKey };
|
|
1282
|
+
const envKey = provider.envVar ? process.env[provider.envVar] : void 0;
|
|
1283
|
+
if (envKey) return { kind: "api_key", token: envKey };
|
|
1284
|
+
return void 0;
|
|
998
1285
|
}
|
|
999
1286
|
function getEnabledModels2() {
|
|
1000
1287
|
return getImagegenConfig()?.models ?? [];
|
|
@@ -1023,26 +1310,74 @@ async function searchModels(query, provider) {
|
|
|
1023
1310
|
if (provider) {
|
|
1024
1311
|
const providerDef = PROVIDERS[provider];
|
|
1025
1312
|
if (!providerDef) throw new Error(`Unknown imagegen provider: ${provider}`);
|
|
1026
|
-
const
|
|
1027
|
-
if (!
|
|
1028
|
-
return providerDef.search(query || "text to image",
|
|
1313
|
+
const cred = await resolveCredential(provider);
|
|
1314
|
+
if (!cred) throw new Error(`No ${provider} credentials configured`);
|
|
1315
|
+
return providerDef.search(query || "text to image", cred);
|
|
1029
1316
|
}
|
|
1030
1317
|
const searches = [];
|
|
1031
1318
|
for (const [id, def] of Object.entries(PROVIDERS)) {
|
|
1032
|
-
const
|
|
1033
|
-
if (!
|
|
1034
|
-
searches.push(def.search(query || "text to image",
|
|
1319
|
+
const cred = await resolveCredential(id);
|
|
1320
|
+
if (!cred) continue;
|
|
1321
|
+
searches.push(def.search(query || "text to image", cred).catch(() => []));
|
|
1035
1322
|
}
|
|
1036
1323
|
if (searches.length === 0) {
|
|
1037
1324
|
throw new Error("No imagegen providers configured");
|
|
1038
1325
|
}
|
|
1039
1326
|
return (await Promise.all(searches)).flat();
|
|
1040
1327
|
}
|
|
1041
|
-
|
|
1328
|
+
var NOT_ENTITLED_MESSAGE = {
|
|
1329
|
+
"openai-codex": "Your ChatGPT plan doesn't include Codex's hosted image tool. Ask an admin to configure an OpenAI API key or another image provider.",
|
|
1330
|
+
xai: "Your xAI plan tier isn't allowlisted for image generation (SuperGrok Heavy required). Ask an admin to set XAI_API_KEY to use Grok Imagine at $0.02/image."
|
|
1331
|
+
};
|
|
1332
|
+
var ENTITLEMENT_PROVIDERS = /* @__PURE__ */ new Set(["openai-codex", "xai"]);
|
|
1333
|
+
function setEntitlement(providerId, state, reason) {
|
|
1334
|
+
const entitlement = state === "not_entitled" ? { state, reason: reason ?? "not entitled", checkedAt: Date.now() } : { state, checkedAt: Date.now() };
|
|
1335
|
+
updateImagegenConfig((ig) => {
|
|
1336
|
+
ig.entitlements = ig.entitlements ?? {};
|
|
1337
|
+
ig.entitlements[providerId] = entitlement;
|
|
1338
|
+
});
|
|
1339
|
+
return entitlement;
|
|
1340
|
+
}
|
|
1341
|
+
async function probeEntitlement(providerId) {
|
|
1342
|
+
if (!ENTITLEMENT_PROVIDERS.has(providerId)) {
|
|
1343
|
+
return setEntitlement(providerId, "entitled");
|
|
1344
|
+
}
|
|
1345
|
+
const cred = await resolveCredential(providerId);
|
|
1346
|
+
if (!cred) throw new Error(`No ${providerId} credentials configured`);
|
|
1347
|
+
if (providerId === "openai-codex") {
|
|
1348
|
+
const state = await probeCodexEntitlement(cred);
|
|
1349
|
+
return setEntitlement(
|
|
1350
|
+
providerId,
|
|
1351
|
+
state,
|
|
1352
|
+
state === "not_entitled" ? NOT_ENTITLED_MESSAGE[providerId] : void 0
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
try {
|
|
1356
|
+
await xaiGenerate("grok-imagine-image", "entitlement probe", cred);
|
|
1357
|
+
return setEntitlement(providerId, "entitled");
|
|
1358
|
+
} catch (err) {
|
|
1359
|
+
if (err instanceof XaiNotEntitledError) {
|
|
1360
|
+
return setEntitlement(providerId, "not_entitled", NOT_ENTITLED_MESSAGE[providerId]);
|
|
1361
|
+
}
|
|
1362
|
+
throw err;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
async function generateImage(model, prompt, signal) {
|
|
1042
1366
|
const { provider: providerId, model: modelId } = parseModelId(model);
|
|
1043
|
-
const
|
|
1044
|
-
if (!
|
|
1045
|
-
|
|
1367
|
+
const cred = await resolveCredential(providerId);
|
|
1368
|
+
if (!cred) throw new Error(`No ${providerId} credentials configured`);
|
|
1369
|
+
try {
|
|
1370
|
+
const buf = await PROVIDERS[providerId].generate(modelId, prompt, cred, signal);
|
|
1371
|
+
if (ENTITLEMENT_PROVIDERS.has(providerId)) setEntitlement(providerId, "entitled");
|
|
1372
|
+
return buf;
|
|
1373
|
+
} catch (err) {
|
|
1374
|
+
if (err instanceof CodexNotEntitledError || err instanceof XaiNotEntitledError) {
|
|
1375
|
+
const message = NOT_ENTITLED_MESSAGE[providerId] ?? err.message;
|
|
1376
|
+
setEntitlement(providerId, "not_entitled", message);
|
|
1377
|
+
throw new Error(message);
|
|
1378
|
+
}
|
|
1379
|
+
throw err;
|
|
1380
|
+
}
|
|
1046
1381
|
}
|
|
1047
1382
|
|
|
1048
1383
|
// packages/daemon/src/web/api/system.ts
|
|
@@ -1079,7 +1414,12 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1079
1414
|
return c.json({
|
|
1080
1415
|
system: config2?.system ?? null,
|
|
1081
1416
|
name: globalConfig.name ?? null,
|
|
1082
|
-
|
|
1417
|
+
spiritName: getSpiritName(),
|
|
1418
|
+
timezone,
|
|
1419
|
+
// Whether any AI model is available for minds to think with. A providerless
|
|
1420
|
+
// system spawns every mind mute, so the dashboard surfaces a banner (#606).
|
|
1421
|
+
// Boolean only — no provider details or secrets are exposed here.
|
|
1422
|
+
aiConfigured: isAiConfigured()
|
|
1083
1423
|
});
|
|
1084
1424
|
}).put("/info", requireAdmin, zValidator("json", z.object({ name: z.string() })), async (c) => {
|
|
1085
1425
|
const { name } = c.req.valid("json");
|
|
@@ -1088,7 +1428,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1088
1428
|
config2.name = name.trim() || void 0;
|
|
1089
1429
|
writeGlobalConfig(config2);
|
|
1090
1430
|
if (config2.name !== previousName) {
|
|
1091
|
-
const { notifySpiritSystemChange } = await import("./spirit-
|
|
1431
|
+
const { notifySpiritSystemChange } = await import("./spirit-IL5EMWYZ.js");
|
|
1092
1432
|
await notifySpiritSystemChange();
|
|
1093
1433
|
}
|
|
1094
1434
|
return c.json({ name: config2.name ?? null });
|
|
@@ -1246,10 +1586,19 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1246
1586
|
return c.json({ error: err instanceof Error ? err.message : "Failed to remove" }, 400);
|
|
1247
1587
|
}
|
|
1248
1588
|
return c.json({ ok: true });
|
|
1589
|
+
}).post("/imagegen/providers/:id/probe", requireAdmin, async (c) => {
|
|
1590
|
+
try {
|
|
1591
|
+
const entitlement = await probeEntitlement(c.req.param("id"));
|
|
1592
|
+
return c.json(entitlement);
|
|
1593
|
+
} catch (err) {
|
|
1594
|
+
return c.json({ error: err instanceof Error ? err.message : "Probe failed" }, 400);
|
|
1595
|
+
}
|
|
1249
1596
|
}).get("/imagegen/models", requireAdmin, (c) => {
|
|
1250
1597
|
const models = getEnabledModels2();
|
|
1251
1598
|
const defaultModel = getDefaultModel();
|
|
1252
1599
|
return c.json({ models, defaultModel: defaultModel ?? null });
|
|
1600
|
+
}).get("/imagegen/default-model", (c) => {
|
|
1601
|
+
return c.json({ defaultModel: getDefaultModel() ?? null });
|
|
1253
1602
|
}).put(
|
|
1254
1603
|
"/imagegen/models",
|
|
1255
1604
|
requireAdmin,
|
|
@@ -1455,7 +1804,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1455
1804
|
instructions: `Enter code: ${info.userCode}`
|
|
1456
1805
|
});
|
|
1457
1806
|
},
|
|
1458
|
-
onSelect:
|
|
1807
|
+
onSelect: selectLoginMethod,
|
|
1459
1808
|
onPrompt: async (_prompt) => {
|
|
1460
1809
|
if (promptPromise) {
|
|
1461
1810
|
const existing = oauthFlows.get(flowId);
|
|
@@ -1559,7 +1908,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1559
1908
|
message: z.string().optional(),
|
|
1560
1909
|
messages: z.array(z.string().min(1)).optional(),
|
|
1561
1910
|
script: z.string().optional(),
|
|
1562
|
-
|
|
1911
|
+
thread: z.string().optional(),
|
|
1563
1912
|
enabled: z.boolean(),
|
|
1564
1913
|
whileSleeping: z.enum(["skip", "queue", "trigger-wake"]).optional()
|
|
1565
1914
|
})
|
|
@@ -1574,6 +1923,9 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1574
1923
|
return c.json({ ok: true });
|
|
1575
1924
|
}
|
|
1576
1925
|
);
|
|
1926
|
+
async function selectLoginMethod(prompt) {
|
|
1927
|
+
return prompt.options[0]?.id;
|
|
1928
|
+
}
|
|
1577
1929
|
var oauthFlows = /* @__PURE__ */ new Map();
|
|
1578
1930
|
function cleanupOAuthFlows() {
|
|
1579
1931
|
for (const [id, flow] of oauthFlows) {
|
|
@@ -1672,7 +2024,7 @@ function stopApiKeyRefresh() {
|
|
|
1672
2024
|
var system_default = app;
|
|
1673
2025
|
|
|
1674
2026
|
// packages/daemon/src/web/app.ts
|
|
1675
|
-
import { Hono as
|
|
2027
|
+
import { Hono as Hono30 } from "hono";
|
|
1676
2028
|
import { bodyLimit } from "hono/body-limit";
|
|
1677
2029
|
import { cors } from "hono/cors";
|
|
1678
2030
|
import { csrf } from "hono/csrf";
|
|
@@ -1721,7 +2073,7 @@ var app2 = new Hono2().get("/events", async (c) => {
|
|
|
1721
2073
|
});
|
|
1722
2074
|
cleanups.push(unsubActivity);
|
|
1723
2075
|
for (const conv of conversations2) {
|
|
1724
|
-
const unsubConv =
|
|
2076
|
+
const unsubConv = subscribe3(conv.id, (event) => {
|
|
1725
2077
|
stream.writeSSE({
|
|
1726
2078
|
data: JSON.stringify({ event: "conversation", conversationId: conv.id, ...event })
|
|
1727
2079
|
}).catch((err) => {
|
|
@@ -1884,7 +2236,7 @@ var admin = new Hono3().use(authMiddleware).get("/users", async (c) => {
|
|
|
1884
2236
|
await getOrCreateMindUser(mind.name);
|
|
1885
2237
|
}
|
|
1886
2238
|
const type = c.req.query("type");
|
|
1887
|
-
if (type === "
|
|
2239
|
+
if (type === "human" || type === "mind") {
|
|
1888
2240
|
return c.json(await listUsersByType(type));
|
|
1889
2241
|
}
|
|
1890
2242
|
return c.json(await listUsers());
|
|
@@ -2208,6 +2560,14 @@ async function fanOutToMinds(opts) {
|
|
|
2208
2560
|
const targetMinds = mindParticipants.map((ap) => {
|
|
2209
2561
|
const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
|
|
2210
2562
|
if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
|
|
2563
|
+
if (ap.username !== opts.senderName) {
|
|
2564
|
+
logger_default.warn(
|
|
2565
|
+
`fan-out: skipping ${ap.username} (not running) for conversation ${opts.conversationId}`
|
|
2566
|
+
);
|
|
2567
|
+
if (ap.userType === "system") {
|
|
2568
|
+
import("./spirit-availability-YFEGJNJ2.js").then(({ ensureSpiritAvailable: ensureSpiritAvailable2 }) => ensureSpiritAvailable2()).catch((err) => logger_default.warn("fan-out: on-demand spirit start failed", logger_default.errorData(err)));
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2211
2571
|
return null;
|
|
2212
2572
|
}).filter((n) => n !== null && n !== opts.senderName);
|
|
2213
2573
|
for (const mindName of targetMinds) {
|
|
@@ -2308,7 +2668,7 @@ var app4 = new Hono5().post("/:platform/inbound", zValidator4("json", inboundSch
|
|
|
2308
2668
|
}
|
|
2309
2669
|
const participants = await getParticipants(channel.id);
|
|
2310
2670
|
if (!participants.some((p) => p.userId === puppet.id)) {
|
|
2311
|
-
const { addParticipant } = await import("./conversations-
|
|
2671
|
+
const { addParticipant } = await import("./conversations-S2232Y5I.js");
|
|
2312
2672
|
await addParticipant(channel.id, puppet.id);
|
|
2313
2673
|
}
|
|
2314
2674
|
const contentBlocks = body.content;
|
|
@@ -2592,8 +2952,8 @@ async function notifyMind(mindName, message) {
|
|
|
2592
2952
|
const entry = await findMind(mindName);
|
|
2593
2953
|
if (!entry) return;
|
|
2594
2954
|
try {
|
|
2595
|
-
const {
|
|
2596
|
-
await
|
|
2955
|
+
const { deliverEvent: deliverEvent2 } = await import("./system-events-A6IGCEC3.js");
|
|
2956
|
+
await deliverEvent2(mindName, { type: "file-share", body: message });
|
|
2597
2957
|
} catch (err) {
|
|
2598
2958
|
logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
|
|
2599
2959
|
}
|
|
@@ -2889,14 +3249,222 @@ var app9 = new Hono11().post("/:name/avatar", requireSelf(), async (c) => {
|
|
|
2889
3249
|
var files_default = app9;
|
|
2890
3250
|
|
|
2891
3251
|
// packages/daemon/src/web/api/history.ts
|
|
2892
|
-
import { and as and3, desc as desc2, eq as eq3, gte, inArray, sql } from "drizzle-orm";
|
|
3252
|
+
import { and as and3, desc as desc2, eq as eq3, gte, inArray, lt as lt2, sql } from "drizzle-orm";
|
|
2893
3253
|
import { Hono as Hono12 } from "hono";
|
|
2894
|
-
var AWAY_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2895
3254
|
async function resolveMindFilter(c) {
|
|
2896
3255
|
const user = c.get("user");
|
|
2897
3256
|
const privileged = user.role === "admin" || user.role === "system";
|
|
2898
3257
|
return privileged ? c.req.query("mind") ?? void 0 : getBaseName(user.username);
|
|
2899
3258
|
}
|
|
3259
|
+
function eventLabelOf(metadata, channel) {
|
|
3260
|
+
if (metadata) {
|
|
3261
|
+
try {
|
|
3262
|
+
const parsed = JSON.parse(metadata);
|
|
3263
|
+
if (typeof parsed.label === "string" && parsed.label) return parsed.label;
|
|
3264
|
+
} catch {
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
return channel.split(":")[1] || "Event";
|
|
3268
|
+
}
|
|
3269
|
+
function getChannelLabel(channel) {
|
|
3270
|
+
const isDM = channel.startsWith("@");
|
|
3271
|
+
const colonIdx = channel.indexOf(":");
|
|
3272
|
+
const raw = colonIdx >= 0 ? channel.substring(colonIdx + 1) : channel;
|
|
3273
|
+
const label = isDM ? raw : raw.startsWith("#") ? raw : `#${raw}`;
|
|
3274
|
+
return { label, type: isDM ? "dm" : "channel" };
|
|
3275
|
+
}
|
|
3276
|
+
async function resolveConversationIds(db, allChannelSlugs, dmSlugMinds) {
|
|
3277
|
+
const channelIdMap = /* @__PURE__ */ new Map();
|
|
3278
|
+
try {
|
|
3279
|
+
if (allChannelSlugs.size > 0) {
|
|
3280
|
+
const channelNames = [...allChannelSlugs].filter((s) => !s.startsWith("@")).map((s) => {
|
|
3281
|
+
let name = s;
|
|
3282
|
+
if (name.startsWith("#")) name = name.slice(1);
|
|
3283
|
+
const colonIdx = name.indexOf(":");
|
|
3284
|
+
if (colonIdx >= 0) name = name.substring(colonIdx + 1);
|
|
3285
|
+
return { slug: s, name };
|
|
3286
|
+
});
|
|
3287
|
+
if (channelNames.length > 0) {
|
|
3288
|
+
const channelRows = await db.select({ conversationId: channels.conversation_id, name: channels.name }).from(channels).where(
|
|
3289
|
+
inArray(
|
|
3290
|
+
channels.name,
|
|
3291
|
+
channelNames.map((c) => c.name)
|
|
3292
|
+
)
|
|
3293
|
+
);
|
|
3294
|
+
const nameToId = new Map(channelRows.map((r) => [r.name, r.conversationId]));
|
|
3295
|
+
for (const { slug, name } of channelNames) {
|
|
3296
|
+
const id = nameToId.get(name);
|
|
3297
|
+
if (id) channelIdMap.set(slug, id);
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
const dmSlugs = [...dmSlugMinds.keys()];
|
|
3301
|
+
if (dmSlugs.length > 0) {
|
|
3302
|
+
const targetNames = dmSlugs.map((s) => s.slice(1));
|
|
3303
|
+
const cp2 = db.$with("cp2").as(
|
|
3304
|
+
db.select({
|
|
3305
|
+
conversation_id: conversationParticipants.conversation_id,
|
|
3306
|
+
username: users.username
|
|
3307
|
+
}).from(conversationParticipants).innerJoin(users, eq3(conversationParticipants.user_id, users.id))
|
|
3308
|
+
);
|
|
3309
|
+
const dmRows = await db.with(cp2).select({
|
|
3310
|
+
id: conversations.id,
|
|
3311
|
+
targetUsername: users.username
|
|
3312
|
+
}).from(conversations).innerJoin(
|
|
3313
|
+
conversationParticipants,
|
|
3314
|
+
eq3(conversations.id, conversationParticipants.conversation_id)
|
|
3315
|
+
).innerJoin(users, eq3(conversationParticipants.user_id, users.id)).where(and3(eq3(conversations.type, "dm"), inArray(users.username, targetNames)));
|
|
3316
|
+
const dmCandidates = [];
|
|
3317
|
+
for (const row of dmRows) {
|
|
3318
|
+
const slug = `@${row.targetUsername}`;
|
|
3319
|
+
const mindNames = dmSlugMinds.get(slug);
|
|
3320
|
+
if (mindNames && !channelIdMap.has(slug)) {
|
|
3321
|
+
dmCandidates.push({ slug, conversationId: row.id, mindNames: [...mindNames] });
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
if (dmCandidates.length > 0) {
|
|
3325
|
+
const candidateConvIds = dmCandidates.map((d) => d.conversationId);
|
|
3326
|
+
const allMindNames = [...new Set(dmCandidates.flatMap((d) => d.mindNames))];
|
|
3327
|
+
const verifyRows = await db.select({
|
|
3328
|
+
conversation_id: conversationParticipants.conversation_id,
|
|
3329
|
+
username: users.username
|
|
3330
|
+
}).from(conversationParticipants).innerJoin(users, eq3(conversationParticipants.user_id, users.id)).where(
|
|
3331
|
+
and3(
|
|
3332
|
+
inArray(conversationParticipants.conversation_id, candidateConvIds),
|
|
3333
|
+
inArray(users.username, allMindNames)
|
|
3334
|
+
)
|
|
3335
|
+
);
|
|
3336
|
+
const verifiedConvs = new Set(verifyRows.map((r) => r.conversation_id));
|
|
3337
|
+
for (const candidate of dmCandidates) {
|
|
3338
|
+
if (verifiedConvs.has(candidate.conversationId)) {
|
|
3339
|
+
channelIdMap.set(candidate.slug, candidate.conversationId);
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
} catch (err) {
|
|
3346
|
+
logger_default.warn("Failed to resolve channel slugs to conversation IDs", logger_default.errorData(err));
|
|
3347
|
+
}
|
|
3348
|
+
return channelIdMap;
|
|
3349
|
+
}
|
|
3350
|
+
var NOISE_ACTIVITY_TYPES = sql`('mind_started', 'mind_stopped', 'mind_active', 'mind_idle', 'mind_done', 'mind_sleeping', 'mind_waking', 'brain_online', 'brain_offline')`;
|
|
3351
|
+
var PERIODIC_TIERS = /* @__PURE__ */ new Set(["hour", "day", "week", "month"]);
|
|
3352
|
+
async function attachSummaryIcons(db, rows) {
|
|
3353
|
+
const allChannelSlugs = /* @__PURE__ */ new Set();
|
|
3354
|
+
const dmSlugMinds = /* @__PURE__ */ new Map();
|
|
3355
|
+
const pending = [];
|
|
3356
|
+
for (const row of rows) {
|
|
3357
|
+
if (row.mind === "_system" || !PERIODIC_TIERS.has(row.period)) continue;
|
|
3358
|
+
const { start, end } = getUtcTimeRange(row.period_key, row.period);
|
|
3359
|
+
const convCounts = await db.select({ channel: mindHistory.channel, count: sql`count(*)` }).from(mindHistory).where(
|
|
3360
|
+
and3(
|
|
3361
|
+
eq3(mindHistory.mind, row.mind),
|
|
3362
|
+
sql`${mindHistory.type} IN ('inbound', 'outbound')`,
|
|
3363
|
+
gte(mindHistory.created_at, start),
|
|
3364
|
+
lt2(mindHistory.created_at, end)
|
|
3365
|
+
)
|
|
3366
|
+
).groupBy(mindHistory.channel).limit(50);
|
|
3367
|
+
for (const cc of convCounts) {
|
|
3368
|
+
if (!cc.channel) continue;
|
|
3369
|
+
allChannelSlugs.add(cc.channel);
|
|
3370
|
+
if (cc.channel.startsWith("@")) {
|
|
3371
|
+
let minds = dmSlugMinds.get(cc.channel);
|
|
3372
|
+
if (!minds) {
|
|
3373
|
+
minds = /* @__PURE__ */ new Set();
|
|
3374
|
+
dmSlugMinds.set(cc.channel, minds);
|
|
3375
|
+
}
|
|
3376
|
+
minds.add(row.mind);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
const eventRows = await db.select({ channel: mindHistory.channel, metadata: mindHistory.metadata }).from(mindHistory).where(
|
|
3380
|
+
and3(
|
|
3381
|
+
eq3(mindHistory.mind, row.mind),
|
|
3382
|
+
eq3(mindHistory.type, "event"),
|
|
3383
|
+
gte(mindHistory.created_at, start),
|
|
3384
|
+
lt2(mindHistory.created_at, end)
|
|
3385
|
+
)
|
|
3386
|
+
).orderBy(mindHistory.created_at, mindHistory.id).limit(2e3);
|
|
3387
|
+
const eventGroups = /* @__PURE__ */ new Map();
|
|
3388
|
+
for (const ev of eventRows) {
|
|
3389
|
+
const label = eventLabelOf(ev.metadata, ev.channel ?? "");
|
|
3390
|
+
eventGroups.set(label, (eventGroups.get(label) ?? 0) + 1);
|
|
3391
|
+
}
|
|
3392
|
+
const actCounts = await db.select({ type: activity.type, count: sql`count(*)` }).from(activity).where(
|
|
3393
|
+
and3(
|
|
3394
|
+
eq3(activity.mind, row.mind),
|
|
3395
|
+
gte(activity.created_at, start),
|
|
3396
|
+
lt2(activity.created_at, end),
|
|
3397
|
+
sql`${activity.type} NOT IN ${NOISE_ACTIVITY_TYPES}`
|
|
3398
|
+
)
|
|
3399
|
+
).groupBy(activity.type);
|
|
3400
|
+
let actGroups = [];
|
|
3401
|
+
if (actCounts.length > 0) {
|
|
3402
|
+
const actRows = await db.select({
|
|
3403
|
+
id: activity.id,
|
|
3404
|
+
type: activity.type,
|
|
3405
|
+
summary: activity.summary,
|
|
3406
|
+
metadata: activity.metadata,
|
|
3407
|
+
created_at: activity.created_at
|
|
3408
|
+
}).from(activity).where(
|
|
3409
|
+
and3(
|
|
3410
|
+
eq3(activity.mind, row.mind),
|
|
3411
|
+
gte(activity.created_at, start),
|
|
3412
|
+
lt2(activity.created_at, end),
|
|
3413
|
+
sql`${activity.type} NOT IN ${NOISE_ACTIVITY_TYPES}`
|
|
3414
|
+
)
|
|
3415
|
+
).orderBy(desc2(activity.created_at), desc2(activity.id)).limit(200);
|
|
3416
|
+
const byType = /* @__PURE__ */ new Map();
|
|
3417
|
+
for (const a of actRows) {
|
|
3418
|
+
let metadata = null;
|
|
3419
|
+
if (a.metadata) {
|
|
3420
|
+
try {
|
|
3421
|
+
metadata = JSON.parse(a.metadata);
|
|
3422
|
+
} catch (err) {
|
|
3423
|
+
logger_default.debug(`malformed activity metadata for activity ${a.id}`, logger_default.errorData(err));
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
let group = byType.get(a.type);
|
|
3427
|
+
if (!group) {
|
|
3428
|
+
group = {
|
|
3429
|
+
type: a.type,
|
|
3430
|
+
count: 0,
|
|
3431
|
+
...typeof metadata?.icon === "string" ? { icon: metadata.icon } : {},
|
|
3432
|
+
...typeof metadata?.color === "string" ? { color: metadata.color } : {},
|
|
3433
|
+
items: []
|
|
3434
|
+
};
|
|
3435
|
+
byType.set(a.type, group);
|
|
3436
|
+
}
|
|
3437
|
+
if (group.items.length < 20) {
|
|
3438
|
+
group.items.push({ id: a.id, summary: a.summary, metadata, created_at: a.created_at });
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
const countByType = new Map(actCounts.map((c) => [c.type, c.count]));
|
|
3442
|
+
for (const group of byType.values()) {
|
|
3443
|
+
group.count = countByType.get(group.type) ?? group.items.length;
|
|
3444
|
+
group.items.reverse();
|
|
3445
|
+
}
|
|
3446
|
+
actGroups = [...byType.values()];
|
|
3447
|
+
}
|
|
3448
|
+
pending.push({
|
|
3449
|
+
row,
|
|
3450
|
+
convCounts: convCounts.filter((c) => !!c.channel),
|
|
3451
|
+
events: [...eventGroups.entries()].map(([label, count]) => ({ label, count })),
|
|
3452
|
+
activities: actGroups
|
|
3453
|
+
});
|
|
3454
|
+
}
|
|
3455
|
+
if (pending.length === 0) return;
|
|
3456
|
+
const channelIdMap = await resolveConversationIds(db, allChannelSlugs, dmSlugMinds);
|
|
3457
|
+
for (const p of pending) {
|
|
3458
|
+
p.row.icons = {
|
|
3459
|
+
conversations: p.convCounts.map((cc) => {
|
|
3460
|
+
const { label, type } = getChannelLabel(cc.channel);
|
|
3461
|
+
return { id: channelIdMap.get(cc.channel) ?? cc.channel, label, type, count: cc.count };
|
|
3462
|
+
}),
|
|
3463
|
+
events: p.events,
|
|
3464
|
+
activities: p.activities
|
|
3465
|
+
};
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
2900
3468
|
var history = new Hono12().use("*", async (c, next) => {
|
|
2901
3469
|
c.set("mindFilter", await resolveMindFilter(c));
|
|
2902
3470
|
await next();
|
|
@@ -2929,15 +3497,20 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
2929
3497
|
channel: mindHistory.channel,
|
|
2930
3498
|
sender: mindHistory.sender,
|
|
2931
3499
|
content: mindHistory.content,
|
|
3500
|
+
metadata: mindHistory.metadata,
|
|
2932
3501
|
turn_id: mindHistory.turn_id,
|
|
2933
3502
|
created_at: mindHistory.created_at
|
|
2934
3503
|
}).from(mindHistory).where(
|
|
2935
3504
|
and3(
|
|
2936
3505
|
inArray(mindHistory.turn_id, turnIds),
|
|
2937
|
-
|
|
3506
|
+
// `event` rows are system events, not messages. They're fetched here but split off
|
|
3507
|
+
// below into a separate per-turn `events` array — never into `conversations`, so no
|
|
3508
|
+
// surface can render them as chat with a phantom sender.
|
|
3509
|
+
sql`${mindHistory.type} IN ('inbound', 'event', 'outbound')`
|
|
2938
3510
|
)
|
|
2939
3511
|
).orderBy(mindHistory.created_at, mindHistory.id);
|
|
2940
3512
|
const msgsByTurnChannel = /* @__PURE__ */ new Map();
|
|
3513
|
+
const systemEventsByTurn = /* @__PURE__ */ new Map();
|
|
2941
3514
|
function addToTurnChannel(turnId, channel, event) {
|
|
2942
3515
|
let byChannel = msgsByTurnChannel.get(turnId);
|
|
2943
3516
|
if (!byChannel) {
|
|
@@ -2955,6 +3528,17 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
2955
3528
|
for (const t of turnRows) turnMindMap.set(t.id, t.mind);
|
|
2956
3529
|
for (const m of historyMsgRows) {
|
|
2957
3530
|
if (!m.turn_id || !m.channel) continue;
|
|
3531
|
+
if (m.type === "event") {
|
|
3532
|
+
const arr = systemEventsByTurn.get(m.turn_id) ?? [];
|
|
3533
|
+
arr.push({
|
|
3534
|
+
id: m.id,
|
|
3535
|
+
label: eventLabelOf(m.metadata, m.channel),
|
|
3536
|
+
content: m.content,
|
|
3537
|
+
created_at: m.created_at
|
|
3538
|
+
});
|
|
3539
|
+
systemEventsByTurn.set(m.turn_id, arr);
|
|
3540
|
+
continue;
|
|
3541
|
+
}
|
|
2958
3542
|
const mindName = turnMindMap.get(m.turn_id) ?? m.mind;
|
|
2959
3543
|
addToTurnChannel(m.turn_id, m.channel, {
|
|
2960
3544
|
id: m.id,
|
|
@@ -2964,13 +3548,6 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
2964
3548
|
created_at: m.created_at
|
|
2965
3549
|
});
|
|
2966
3550
|
}
|
|
2967
|
-
function getChannelLabel(channel) {
|
|
2968
|
-
const isDM = channel.startsWith("@");
|
|
2969
|
-
const colonIdx = channel.indexOf(":");
|
|
2970
|
-
const raw = colonIdx >= 0 ? channel.substring(colonIdx + 1) : channel;
|
|
2971
|
-
const label = isDM ? raw : raw.startsWith("#") ? raw : `#${raw}`;
|
|
2972
|
-
return { label, type: isDM ? "dm" : "channel" };
|
|
2973
|
-
}
|
|
2974
3551
|
const activityRows = await db.select().from(activity).where(inArray(activity.turn_id, turnIds)).orderBy(activity.created_at, activity.id);
|
|
2975
3552
|
const activitiesByTurn = /* @__PURE__ */ new Map();
|
|
2976
3553
|
for (const a of activityRows) {
|
|
@@ -2989,7 +3566,9 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
2989
3566
|
id: mindHistory.id,
|
|
2990
3567
|
channel: mindHistory.channel,
|
|
2991
3568
|
sender: mindHistory.sender,
|
|
2992
|
-
content: mindHistory.content
|
|
3569
|
+
content: mindHistory.content,
|
|
3570
|
+
type: mindHistory.type,
|
|
3571
|
+
metadata: mindHistory.metadata
|
|
2993
3572
|
}).from(mindHistory).where(inArray(mindHistory.id, triggerIds));
|
|
2994
3573
|
for (const r of triggerRows) triggerMap.set(r.id, r);
|
|
2995
3574
|
}
|
|
@@ -3012,77 +3591,7 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
3012
3591
|
}
|
|
3013
3592
|
}
|
|
3014
3593
|
}
|
|
3015
|
-
const channelIdMap =
|
|
3016
|
-
try {
|
|
3017
|
-
if (allChannelSlugs.size > 0) {
|
|
3018
|
-
const channelNames = [...allChannelSlugs].filter((s) => !s.startsWith("@")).map((s) => {
|
|
3019
|
-
let name = s;
|
|
3020
|
-
if (name.startsWith("#")) name = name.slice(1);
|
|
3021
|
-
const colonIdx = name.indexOf(":");
|
|
3022
|
-
if (colonIdx >= 0) name = name.substring(colonIdx + 1);
|
|
3023
|
-
return { slug: s, name };
|
|
3024
|
-
});
|
|
3025
|
-
if (channelNames.length > 0) {
|
|
3026
|
-
const channelRows = await db.select({ conversationId: channels.conversation_id, name: channels.name }).from(channels).where(
|
|
3027
|
-
inArray(
|
|
3028
|
-
channels.name,
|
|
3029
|
-
channelNames.map((c2) => c2.name)
|
|
3030
|
-
)
|
|
3031
|
-
);
|
|
3032
|
-
const nameToId = new Map(channelRows.map((r) => [r.name, r.conversationId]));
|
|
3033
|
-
for (const { slug, name } of channelNames) {
|
|
3034
|
-
const id = nameToId.get(name);
|
|
3035
|
-
if (id) channelIdMap.set(slug, id);
|
|
3036
|
-
}
|
|
3037
|
-
}
|
|
3038
|
-
const dmSlugs = [...dmSlugMinds.keys()];
|
|
3039
|
-
if (dmSlugs.length > 0) {
|
|
3040
|
-
const targetNames = dmSlugs.map((s) => s.slice(1));
|
|
3041
|
-
const cp2 = db.$with("cp2").as(
|
|
3042
|
-
db.select({
|
|
3043
|
-
conversation_id: conversationParticipants.conversation_id,
|
|
3044
|
-
username: users.username
|
|
3045
|
-
}).from(conversationParticipants).innerJoin(users, eq3(conversationParticipants.user_id, users.id))
|
|
3046
|
-
);
|
|
3047
|
-
const dmRows = await db.with(cp2).select({
|
|
3048
|
-
id: conversations.id,
|
|
3049
|
-
targetUsername: users.username
|
|
3050
|
-
}).from(conversations).innerJoin(
|
|
3051
|
-
conversationParticipants,
|
|
3052
|
-
eq3(conversations.id, conversationParticipants.conversation_id)
|
|
3053
|
-
).innerJoin(users, eq3(conversationParticipants.user_id, users.id)).where(and3(eq3(conversations.type, "dm"), inArray(users.username, targetNames)));
|
|
3054
|
-
const dmCandidates = [];
|
|
3055
|
-
for (const row of dmRows) {
|
|
3056
|
-
const slug = `@${row.targetUsername}`;
|
|
3057
|
-
const mindNames = dmSlugMinds.get(slug);
|
|
3058
|
-
if (mindNames && !channelIdMap.has(slug)) {
|
|
3059
|
-
dmCandidates.push({ slug, conversationId: row.id, mindNames: [...mindNames] });
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
|
-
if (dmCandidates.length > 0) {
|
|
3063
|
-
const candidateConvIds = dmCandidates.map((d) => d.conversationId);
|
|
3064
|
-
const allMindNames = [...new Set(dmCandidates.flatMap((d) => d.mindNames))];
|
|
3065
|
-
const verifyRows = await db.select({
|
|
3066
|
-
conversation_id: conversationParticipants.conversation_id,
|
|
3067
|
-
username: users.username
|
|
3068
|
-
}).from(conversationParticipants).innerJoin(users, eq3(conversationParticipants.user_id, users.id)).where(
|
|
3069
|
-
and3(
|
|
3070
|
-
inArray(conversationParticipants.conversation_id, candidateConvIds),
|
|
3071
|
-
inArray(users.username, allMindNames)
|
|
3072
|
-
)
|
|
3073
|
-
);
|
|
3074
|
-
const verifiedConvs = new Set(verifyRows.map((r) => r.conversation_id));
|
|
3075
|
-
for (const candidate of dmCandidates) {
|
|
3076
|
-
if (verifiedConvs.has(candidate.conversationId)) {
|
|
3077
|
-
channelIdMap.set(candidate.slug, candidate.conversationId);
|
|
3078
|
-
}
|
|
3079
|
-
}
|
|
3080
|
-
}
|
|
3081
|
-
}
|
|
3082
|
-
}
|
|
3083
|
-
} catch (err) {
|
|
3084
|
-
logger_default.warn("Failed to resolve channel slugs to conversation IDs", logger_default.errorData(err));
|
|
3085
|
-
}
|
|
3594
|
+
const channelIdMap = await resolveConversationIds(db, allChannelSlugs, dmSlugMinds);
|
|
3086
3595
|
const result = turnRows.map((t) => {
|
|
3087
3596
|
const summary = summaryByTurn.get(t.id);
|
|
3088
3597
|
const turnChannels = msgsByTurnChannel.get(t.id) ?? /* @__PURE__ */ new Map();
|
|
@@ -3129,6 +3638,7 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
3129
3638
|
}
|
|
3130
3639
|
}
|
|
3131
3640
|
const trigger = t.trigger_event_id ? triggerMap.get(t.trigger_event_id) : null;
|
|
3641
|
+
const isEventTrigger = trigger?.type === "event";
|
|
3132
3642
|
return {
|
|
3133
3643
|
id: t.id,
|
|
3134
3644
|
mind: t.mind,
|
|
@@ -3136,8 +3646,23 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
3136
3646
|
summary_meta: summaryMeta,
|
|
3137
3647
|
status: t.status,
|
|
3138
3648
|
created_at: t.created_at,
|
|
3139
|
-
trigger: trigger ? {
|
|
3649
|
+
trigger: trigger ? {
|
|
3650
|
+
// The trigger's mind_history row id — matches events[].id /
|
|
3651
|
+
// messages[].source_event_id so the UI can dedupe the trigger
|
|
3652
|
+
// from the per-turn icon stack.
|
|
3653
|
+
eventId: t.trigger_event_id,
|
|
3654
|
+
channel: trigger.channel,
|
|
3655
|
+
sender: trigger.sender,
|
|
3656
|
+
content: trigger.content,
|
|
3657
|
+
...isEventTrigger && trigger.channel ? {
|
|
3658
|
+
event: {
|
|
3659
|
+
type: trigger.channel.split(":")[1] || "event",
|
|
3660
|
+
label: eventLabelOf(trigger.metadata, trigger.channel)
|
|
3661
|
+
}
|
|
3662
|
+
} : {}
|
|
3663
|
+
} : null,
|
|
3140
3664
|
conversations: convEntries,
|
|
3665
|
+
events: systemEventsByTurn.get(t.id) ?? [],
|
|
3141
3666
|
activities: turnActivities
|
|
3142
3667
|
};
|
|
3143
3668
|
});
|
|
@@ -3162,7 +3687,7 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
3162
3687
|
}
|
|
3163
3688
|
}, 15e3);
|
|
3164
3689
|
if (mindFilter) {
|
|
3165
|
-
unsubscribe =
|
|
3690
|
+
unsubscribe = subscribe2(mindFilter, (event) => {
|
|
3166
3691
|
try {
|
|
3167
3692
|
send(JSON.stringify(event));
|
|
3168
3693
|
} catch {
|
|
@@ -3254,6 +3779,9 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
3254
3779
|
}
|
|
3255
3780
|
return { ...r, metadata };
|
|
3256
3781
|
});
|
|
3782
|
+
if (c.req.query("include") === "icons") {
|
|
3783
|
+
await attachSummaryIcons(db, result);
|
|
3784
|
+
}
|
|
3257
3785
|
return c.json(result);
|
|
3258
3786
|
}).get("/activity", async (c) => {
|
|
3259
3787
|
const mind = c.get("mindFilter");
|
|
@@ -3265,9 +3793,7 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
3265
3793
|
if (mind) conditions.push(eq3(activity.mind, mind));
|
|
3266
3794
|
if (from) conditions.push(gte(activity.created_at, from));
|
|
3267
3795
|
if (to) conditions.push(sql`${activity.created_at} <= ${to}`);
|
|
3268
|
-
conditions.push(
|
|
3269
|
-
sql`${activity.type} NOT IN ('mind_started', 'mind_stopped', 'mind_active', 'mind_idle', 'mind_done', 'mind_sleeping', 'mind_waking', 'brain_online', 'brain_offline')`
|
|
3270
|
-
);
|
|
3796
|
+
conditions.push(sql`${activity.type} NOT IN ${NOISE_ACTIVITY_TYPES}`);
|
|
3271
3797
|
const rows = await db.select({
|
|
3272
3798
|
id: activity.id,
|
|
3273
3799
|
type: activity.type,
|
|
@@ -3285,36 +3811,9 @@ var history = new Hono12().use("*", async (c, next) => {
|
|
|
3285
3811
|
logger_default.debug(`malformed activity metadata for id ${r.id}`, logger_default.errorData(err));
|
|
3286
3812
|
}
|
|
3287
3813
|
}
|
|
3288
|
-
return { ...r, metadata };
|
|
3289
|
-
});
|
|
3290
|
-
return c.json(result);
|
|
3291
|
-
}).get("/away", async (c) => {
|
|
3292
|
-
const mindFilter = c.get("mindFilter");
|
|
3293
|
-
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 100);
|
|
3294
|
-
const db = await getDb();
|
|
3295
|
-
const cutoff = utcDateTimeStr(new Date(Date.now() - AWAY_WINDOW_MS));
|
|
3296
|
-
const conditions = [
|
|
3297
|
-
eq3(summaries.period, "turn"),
|
|
3298
|
-
gte(summaries.created_at, cutoff),
|
|
3299
|
-
sql`(${turns.trigger_event_id} IS NULL OR ${mindHistory.sender} IS NULL OR ${users.user_type} IN ('mind', 'system'))`
|
|
3300
|
-
];
|
|
3301
|
-
if (mindFilter) conditions.push(eq3(summaries.mind, mindFilter));
|
|
3302
|
-
const rows = await db.select({
|
|
3303
|
-
id: summaries.id,
|
|
3304
|
-
mind: summaries.mind,
|
|
3305
|
-
content: summaries.content,
|
|
3306
|
-
created_at: summaries.created_at,
|
|
3307
|
-
turn_id: turns.id
|
|
3308
|
-
}).from(summaries).innerJoin(turns, eq3(turns.id, summaries.period_key)).leftJoin(mindHistory, eq3(mindHistory.id, turns.trigger_event_id)).leftJoin(users, eq3(users.username, mindHistory.sender)).where(and3(...conditions)).orderBy(desc2(summaries.created_at)).limit(limit);
|
|
3309
|
-
return c.json(
|
|
3310
|
-
rows.map((r) => ({
|
|
3311
|
-
id: r.id,
|
|
3312
|
-
mind: r.mind,
|
|
3313
|
-
summary: r.content,
|
|
3314
|
-
turnId: r.turn_id,
|
|
3315
|
-
created_at: r.created_at
|
|
3316
|
-
}))
|
|
3317
|
-
);
|
|
3814
|
+
return { ...r, metadata };
|
|
3815
|
+
});
|
|
3816
|
+
return c.json(result);
|
|
3318
3817
|
});
|
|
3319
3818
|
var history_default = history;
|
|
3320
3819
|
|
|
@@ -3562,7 +4061,7 @@ function classify(raw) {
|
|
|
3562
4061
|
)) {
|
|
3563
4062
|
return {
|
|
3564
4063
|
reason: "auth_error",
|
|
3565
|
-
detail: "Your last turn failed because the model provider rejected your credentials. A one-off is usually a briefly-expired token that the daemon refreshes automatically. If it keeps happening, your API key is likely missing or invalid \u2014
|
|
4064
|
+
detail: "Your last turn failed because the model provider rejected your credentials. A one-off is usually a briefly-expired token that the daemon refreshes automatically. If it keeps happening, your API key is likely missing or invalid \u2014 a host needs to set a valid key (e.g. `volute env set ANTHROPIC_API_KEY <key>`) or reconnect the provider in the dashboard."
|
|
3566
4065
|
};
|
|
3567
4066
|
}
|
|
3568
4067
|
if (/\b429\b|rate.?limit|too many requests/.test(s)) {
|
|
@@ -3606,7 +4105,7 @@ function markDeliveredOnCleanTurn(mind, session) {
|
|
|
3606
4105
|
noticeDrainWatermarks.delete(wmKey);
|
|
3607
4106
|
const errored = takeErrored(mind, session);
|
|
3608
4107
|
if (!errored && watermark != null) {
|
|
3609
|
-
|
|
4108
|
+
clearDeliveredEvents(mind, session, watermark).catch(
|
|
3610
4109
|
(err) => llog.warn(`failed to clear delivered notices for ${mind}:${session}`, logger_default.errorData(err))
|
|
3611
4110
|
);
|
|
3612
4111
|
}
|
|
@@ -3617,12 +4116,15 @@ async function linkPendingInbound(mind, turnId, channel, session) {
|
|
|
3617
4116
|
const db = await getDb();
|
|
3618
4117
|
let lowerBound;
|
|
3619
4118
|
if (session) {
|
|
3620
|
-
const prev = await db.select({ max: sql2`max(${turns.created_at})` }).from(turns).where(and4(eq4(turns.mind, mind), eq4(turns.
|
|
4119
|
+
const prev = await db.select({ max: sql2`max(${turns.created_at})` }).from(turns).where(and4(eq4(turns.mind, mind), eq4(turns.thread, session), sql2`${turns.id} != ${turnId}`)).get();
|
|
3621
4120
|
lowerBound = prev?.max ?? void 0;
|
|
3622
4121
|
}
|
|
3623
4122
|
const conditions = [
|
|
3624
4123
|
eq4(mindHistory.mind, mind),
|
|
3625
|
-
|
|
4124
|
+
// "event" rows are system events (see recordEventRow) — not messages, but they
|
|
4125
|
+
// trigger turns the same way, and this linkage is what sets `trigger_event_id` and
|
|
4126
|
+
// thus drives reflection capture. Dropping them here breaks it silently.
|
|
4127
|
+
inArray2(mindHistory.type, ["inbound", "event"]),
|
|
3626
4128
|
sql2`${mindHistory.turn_id} IS NULL`,
|
|
3627
4129
|
eq4(mindHistory.channel, scopeChannel)
|
|
3628
4130
|
];
|
|
@@ -3644,7 +4146,7 @@ async function handleMindEvent(mind, event) {
|
|
|
3644
4146
|
if (!turnId) {
|
|
3645
4147
|
llog.warn(`skipping turn tracking for ${mind}: createTurn failed`);
|
|
3646
4148
|
} else {
|
|
3647
|
-
|
|
4149
|
+
publish2(mind, { mind, type: "turn_created", turnId });
|
|
3648
4150
|
if (event.session) await assignSession(mind, turnId, event.session);
|
|
3649
4151
|
try {
|
|
3650
4152
|
await linkPendingInbound(mind, turnId, event.channel, event.session);
|
|
@@ -3663,7 +4165,7 @@ async function handleMindEvent(mind, event) {
|
|
|
3663
4165
|
const result = await db.insert(mindHistory).values({
|
|
3664
4166
|
mind,
|
|
3665
4167
|
type: event.type,
|
|
3666
|
-
|
|
4168
|
+
thread: event.session ?? null,
|
|
3667
4169
|
channel: event.channel ?? null,
|
|
3668
4170
|
message_id: event.messageId ?? null,
|
|
3669
4171
|
content: cleanContent ?? null,
|
|
@@ -3690,7 +4192,7 @@ async function handleMindEvent(mind, event) {
|
|
|
3690
4192
|
llog.error("failed to link tool_result to turn", logger_default.errorData(err));
|
|
3691
4193
|
}
|
|
3692
4194
|
}
|
|
3693
|
-
|
|
4195
|
+
publish2(mind, {
|
|
3694
4196
|
mind,
|
|
3695
4197
|
type: event.type,
|
|
3696
4198
|
session: event.session,
|
|
@@ -3706,16 +4208,12 @@ async function handleMindEvent(mind, event) {
|
|
|
3706
4208
|
);
|
|
3707
4209
|
}
|
|
3708
4210
|
onMindEvent(mind, event.type, event.channel);
|
|
3709
|
-
if ((event.type === "text" || event.type === "outbound") && event.channel) {
|
|
3710
|
-
const map = getTypingMap();
|
|
3711
|
-
publishTypingForChannels(map.deleteSender(mind), map);
|
|
3712
|
-
}
|
|
3713
4211
|
if (event.type === "error" && event.session) {
|
|
3714
4212
|
markErrored(mind, event.session);
|
|
3715
4213
|
const { reason, detail } = classify(event.content ?? "");
|
|
3716
4214
|
await recordNotice({
|
|
3717
4215
|
mind,
|
|
3718
|
-
|
|
4216
|
+
thread: event.session,
|
|
3719
4217
|
kind: "turn_error",
|
|
3720
4218
|
reason,
|
|
3721
4219
|
detail,
|
|
@@ -3744,7 +4242,7 @@ async function handleMindEvent(mind, event) {
|
|
|
3744
4242
|
if (event.session && tb.noteExceeded(mind)) {
|
|
3745
4243
|
void recordNotice({
|
|
3746
4244
|
mind,
|
|
3747
|
-
|
|
4245
|
+
thread: event.session,
|
|
3748
4246
|
kind: "budget",
|
|
3749
4247
|
reason: "token_budget",
|
|
3750
4248
|
detail: "You've used your token budget for this period, so your activity may pause until it resets. This isn't anything you did wrong \u2014 it's just a rest imposed by the budget. Anything that arrives while you're paused will be kept for you. If you're mid-thought, this turn is a good moment to jot it down."
|
|
@@ -3757,6 +4255,9 @@ async function completeTurnAndSummarize(mind, event, insertedId) {
|
|
|
3757
4255
|
const finish = async () => {
|
|
3758
4256
|
const completedTurnId = await completeTurn(mind, event.session);
|
|
3759
4257
|
markDeliveredOnCleanTurn(mind, event.session);
|
|
4258
|
+
captureReflection(mind, completedTurnId).catch(
|
|
4259
|
+
(err) => llog.warn("failed to capture event reflection", logger_default.errorData(err))
|
|
4260
|
+
);
|
|
3760
4261
|
if (insertedId != null) {
|
|
3761
4262
|
summarizeTurn(mind, event.session, event.channel, insertedId, completedTurnId).catch(
|
|
3762
4263
|
(err) => llog.error("turn summarization failed", logger_default.errorData(err))
|
|
@@ -3870,7 +4371,7 @@ function defaultDreamSchedule() {
|
|
|
3870
4371
|
cron: "0 3 * * *",
|
|
3871
4372
|
message: "it's 3am. you are dreaming.\n\ngather your material \u2014 read your latest journal entry, read MEMORY.md, surface random memories if you have a way to. then construct a dream premise from that material and invoke the dreamer subagent to experience the dream.",
|
|
3872
4373
|
enabled: true,
|
|
3873
|
-
|
|
4374
|
+
thread: "$new",
|
|
3874
4375
|
whileSleeping: "trigger-wake"
|
|
3875
4376
|
};
|
|
3876
4377
|
}
|
|
@@ -4120,6 +4621,124 @@ async function assertExcludedPathsRestored(opts) {
|
|
|
4120
4621
|
}
|
|
4121
4622
|
}
|
|
4122
4623
|
|
|
4624
|
+
// packages/daemon/src/lib/services/imagegen-jobs.ts
|
|
4625
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
4626
|
+
import { dirname as dirname2 } from "path";
|
|
4627
|
+
var jobLog = logger_default.child("imagegen-jobs");
|
|
4628
|
+
var MAX_INFLIGHT_PER_MIND = 4;
|
|
4629
|
+
var DONE_TTL_MS = 10 * 60 * 1e3;
|
|
4630
|
+
var GENERATION_TIMEOUT_MS = 3 * 60 * 1e3;
|
|
4631
|
+
var jobs = /* @__PURE__ */ new Map();
|
|
4632
|
+
function publicView(r) {
|
|
4633
|
+
const base = { id: r.id, mind: r.mind, createdAt: r.createdAt };
|
|
4634
|
+
if (r.status === "done") {
|
|
4635
|
+
if (!r.path) throw new Error(`job ${r.id} is done but has no path`);
|
|
4636
|
+
return { ...base, status: "done", completedAt: r.completedAt ?? r.createdAt, path: r.path };
|
|
4637
|
+
}
|
|
4638
|
+
if (r.status === "error") {
|
|
4639
|
+
return {
|
|
4640
|
+
...base,
|
|
4641
|
+
status: "error",
|
|
4642
|
+
completedAt: r.completedAt ?? r.createdAt,
|
|
4643
|
+
error: r.error ?? "unknown error"
|
|
4644
|
+
};
|
|
4645
|
+
}
|
|
4646
|
+
return { ...base, status: "running" };
|
|
4647
|
+
}
|
|
4648
|
+
function countInflight(mind) {
|
|
4649
|
+
let n = 0;
|
|
4650
|
+
for (const r of jobs.values()) if (r.mind === mind && r.status === "running") n++;
|
|
4651
|
+
return n;
|
|
4652
|
+
}
|
|
4653
|
+
function settle(record, patch) {
|
|
4654
|
+
Object.assign(record, patch, { completedAt: Date.now() });
|
|
4655
|
+
record.resolveSettled();
|
|
4656
|
+
setTimeout(() => jobs.delete(record.id), DONE_TTL_MS).unref?.();
|
|
4657
|
+
}
|
|
4658
|
+
async function notify(deliver, mind, body) {
|
|
4659
|
+
try {
|
|
4660
|
+
const { delivered } = await deliver(mind, {
|
|
4661
|
+
type: "imagegen",
|
|
4662
|
+
body,
|
|
4663
|
+
delivery: "immediate",
|
|
4664
|
+
whileSleeping: "queue"
|
|
4665
|
+
});
|
|
4666
|
+
if (!delivered) jobLog.warn(`imagegen event not delivered to ${mind}: ${body}`);
|
|
4667
|
+
} catch (err) {
|
|
4668
|
+
jobLog.warn(`failed to deliver imagegen event to ${mind}`, logger_default.errorData(err));
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
async function runJob(record, model, prompt, filename, deps) {
|
|
4672
|
+
const generate = deps.generate ?? generateImage;
|
|
4673
|
+
const deliver = deps.deliver ?? deliverEvent;
|
|
4674
|
+
const controller = new AbortController();
|
|
4675
|
+
const timer = setTimeout(() => controller.abort(), GENERATION_TIMEOUT_MS);
|
|
4676
|
+
timer.unref?.();
|
|
4677
|
+
try {
|
|
4678
|
+
const buf = await generate(model, prompt, controller.signal);
|
|
4679
|
+
const home = `${await resolveMindDir(record.mind)}/home`;
|
|
4680
|
+
const target = safeResolveWithinBase(home, `images/${filename}.png`);
|
|
4681
|
+
if (!target) throw new Error(`Invalid image filename: ${filename}`);
|
|
4682
|
+
await mkdir(dirname2(target), { recursive: true });
|
|
4683
|
+
await writeFile(target, buf);
|
|
4684
|
+
await chownMindFile(target, record.mind);
|
|
4685
|
+
settle(record, { status: "done", path: target });
|
|
4686
|
+
if (record.notifyOnDone) await notify(deliver, record.mind, `image ready: ${target}`);
|
|
4687
|
+
} catch (err) {
|
|
4688
|
+
const message = controller.signal.aborted ? `image generation timed out after ${Math.round(GENERATION_TIMEOUT_MS / 1e3)}s` : err instanceof Error ? err.message : String(err);
|
|
4689
|
+
jobLog.error(`job ${record.id} failed`, logger_default.errorData(err));
|
|
4690
|
+
settle(record, { status: "error", error: message });
|
|
4691
|
+
if (record.notifyOnDone)
|
|
4692
|
+
await notify(deliver, record.mind, `image generation failed: ${message}`);
|
|
4693
|
+
} finally {
|
|
4694
|
+
clearTimeout(timer);
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
function createImagegenJob(mind, model, prompt, filename, deps = {}) {
|
|
4698
|
+
if (countInflight(mind) >= MAX_INFLIGHT_PER_MIND) {
|
|
4699
|
+
throw new Error(
|
|
4700
|
+
`Too many image generations in progress (max ${MAX_INFLIGHT_PER_MIND}). Wait for one to finish.`
|
|
4701
|
+
);
|
|
4702
|
+
}
|
|
4703
|
+
const id = crypto.randomUUID();
|
|
4704
|
+
let resolveSettled;
|
|
4705
|
+
const settled = new Promise((r) => {
|
|
4706
|
+
resolveSettled = r;
|
|
4707
|
+
});
|
|
4708
|
+
const record = {
|
|
4709
|
+
id,
|
|
4710
|
+
mind,
|
|
4711
|
+
status: "running",
|
|
4712
|
+
createdAt: Date.now(),
|
|
4713
|
+
settled,
|
|
4714
|
+
resolveSettled,
|
|
4715
|
+
notifyOnDone: false
|
|
4716
|
+
};
|
|
4717
|
+
jobs.set(id, record);
|
|
4718
|
+
void runJob(record, model, prompt, filename, deps);
|
|
4719
|
+
return id;
|
|
4720
|
+
}
|
|
4721
|
+
function getImagegenJob(mind, id) {
|
|
4722
|
+
const record = jobs.get(id);
|
|
4723
|
+
if (!record || record.mind !== mind) return void 0;
|
|
4724
|
+
return publicView(record);
|
|
4725
|
+
}
|
|
4726
|
+
async function waitForImagegenJob(mind, id, timeoutMs) {
|
|
4727
|
+
const record = jobs.get(id);
|
|
4728
|
+
if (!record || record.mind !== mind) return void 0;
|
|
4729
|
+
if (record.status === "running" && timeoutMs > 0) {
|
|
4730
|
+
let timer;
|
|
4731
|
+
const timeout = new Promise((r) => {
|
|
4732
|
+
timer = setTimeout(r, timeoutMs);
|
|
4733
|
+
timer.unref?.();
|
|
4734
|
+
});
|
|
4735
|
+
await Promise.race([record.settled, timeout]);
|
|
4736
|
+
if (timer) clearTimeout(timer);
|
|
4737
|
+
if (record.status === "running") record.notifyOnDone = true;
|
|
4738
|
+
}
|
|
4739
|
+
return publicView(record);
|
|
4740
|
+
}
|
|
4741
|
+
|
|
4123
4742
|
// packages/daemon/src/lib/template/convert-session.ts
|
|
4124
4743
|
import { randomUUID } from "crypto";
|
|
4125
4744
|
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
@@ -4129,17 +4748,17 @@ function convertSession(opts) {
|
|
|
4129
4748
|
const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
|
|
4130
4749
|
const sessionId = randomUUID();
|
|
4131
4750
|
const idMap = /* @__PURE__ */ new Map();
|
|
4132
|
-
const
|
|
4751
|
+
const messages2 = [];
|
|
4133
4752
|
for (const line of lines) {
|
|
4134
4753
|
const event = JSON.parse(line);
|
|
4135
4754
|
if (event.type === "message" && event.message) {
|
|
4136
|
-
|
|
4755
|
+
messages2.push(event);
|
|
4137
4756
|
}
|
|
4138
4757
|
}
|
|
4139
4758
|
const sdkEvents = [];
|
|
4140
4759
|
let lastSdkUuid = null;
|
|
4141
|
-
for (let i = 0; i <
|
|
4142
|
-
const event =
|
|
4760
|
+
for (let i = 0; i < messages2.length; i++) {
|
|
4761
|
+
const event = messages2[i];
|
|
4143
4762
|
const msg = event.message;
|
|
4144
4763
|
if (msg.role === "user") {
|
|
4145
4764
|
const uuid = randomUUID();
|
|
@@ -4200,8 +4819,8 @@ function convertSession(opts) {
|
|
|
4200
4819
|
let lastToolResultId = event.id;
|
|
4201
4820
|
let lastTimestamp = event.timestamp;
|
|
4202
4821
|
let j = i;
|
|
4203
|
-
while (j <
|
|
4204
|
-
const tr =
|
|
4822
|
+
while (j < messages2.length && messages2[j].message.role === "toolResult") {
|
|
4823
|
+
const tr = messages2[j];
|
|
4205
4824
|
const trMsg = tr.message;
|
|
4206
4825
|
lastToolResultId = tr.id;
|
|
4207
4826
|
lastTimestamp = tr.timestamp;
|
|
@@ -4294,15 +4913,22 @@ function convertAssistantContent(content) {
|
|
|
4294
4913
|
return result;
|
|
4295
4914
|
}
|
|
4296
4915
|
|
|
4916
|
+
// packages/daemon/src/lib/util/time.ts
|
|
4917
|
+
function parseDbTimestamp(ts) {
|
|
4918
|
+
if (/[zZ]$|[+-]\d\d:?\d\d$/.test(ts)) return new Date(ts);
|
|
4919
|
+
return /* @__PURE__ */ new Date(`${ts.replace(" ", "T")}Z`);
|
|
4920
|
+
}
|
|
4921
|
+
|
|
4297
4922
|
// packages/daemon/src/web/api/minds.ts
|
|
4298
4923
|
var _lastActiveCache = { map: /* @__PURE__ */ new Map(), ts: 0 };
|
|
4299
4924
|
var _LAST_ACTIVE_TTL = 6e4;
|
|
4300
|
-
|
|
4925
|
+
var DAEMON_AUTHORED_TYPES = /* @__PURE__ */ new Set(["inbound", "event"]);
|
|
4926
|
+
async function getMindStatus(name, port, registryRunning, seed) {
|
|
4301
4927
|
const manager = getMindManager();
|
|
4302
4928
|
let status = "stopped";
|
|
4303
4929
|
let wakeAt = null;
|
|
4304
4930
|
try {
|
|
4305
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4931
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
|
|
4306
4932
|
const sleepManager = getSleepManagerIfReady2();
|
|
4307
4933
|
if (sleepManager?.isSleeping(name)) {
|
|
4308
4934
|
status = "sleeping";
|
|
@@ -4326,7 +4952,8 @@ async function getMindStatus(name, port, registryRunning) {
|
|
|
4326
4952
|
status: status === "running" ? "connected" : "disconnected"
|
|
4327
4953
|
});
|
|
4328
4954
|
}
|
|
4329
|
-
const lastError = await
|
|
4955
|
+
const lastError = await latestFailureEvent(name);
|
|
4956
|
+
const seedChecklist = seed?.stage === "seed" ? evaluateSeedChecklist(seed.dir ?? mindDir(name)) : void 0;
|
|
4330
4957
|
return {
|
|
4331
4958
|
status,
|
|
4332
4959
|
wakeAt,
|
|
@@ -4334,7 +4961,8 @@ async function getMindStatus(name, port, registryRunning) {
|
|
|
4334
4961
|
channels: channels2,
|
|
4335
4962
|
displayName: config2?.profile?.displayName,
|
|
4336
4963
|
description: config2?.profile?.description,
|
|
4337
|
-
avatar: config2?.profile?.avatar
|
|
4964
|
+
avatar: config2?.profile?.avatar,
|
|
4965
|
+
seedChecklist
|
|
4338
4966
|
};
|
|
4339
4967
|
}
|
|
4340
4968
|
function isPrivileged(c) {
|
|
@@ -4356,6 +4984,7 @@ function toPublicMind(entry, status, extras) {
|
|
|
4356
4984
|
displayName: status.displayName,
|
|
4357
4985
|
description: status.description,
|
|
4358
4986
|
avatar: status.avatar,
|
|
4987
|
+
seedChecklist: status.seedChecklist,
|
|
4359
4988
|
hasPages: extras.hasPages,
|
|
4360
4989
|
lastActiveAt: extras.lastActiveAt ?? null
|
|
4361
4990
|
};
|
|
@@ -4726,7 +5355,8 @@ async function importHistoryFromArchive(name, tempDir) {
|
|
|
4726
5355
|
await db.insert(mindHistory).values({
|
|
4727
5356
|
mind: name,
|
|
4728
5357
|
channel: row.channel ?? null,
|
|
4729
|
-
|
|
5358
|
+
// Archives are long-lived files: pre-rename exports carry `session`.
|
|
5359
|
+
thread: row.thread ?? row.session ?? null,
|
|
4730
5360
|
sender: row.sender ?? null,
|
|
4731
5361
|
message_id: row.message_id ?? null,
|
|
4732
5362
|
type: row.type,
|
|
@@ -4819,8 +5449,16 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
4819
5449
|
const nameErr = validateMindName(name);
|
|
4820
5450
|
if (nameErr) return c.json({ error: nameErr }, 400);
|
|
4821
5451
|
if (await findMind(name)) return c.json({ error: `Mind already exists: ${name}` }, 409);
|
|
5452
|
+
if (!isAiConfigured()) {
|
|
5453
|
+
return c.json(
|
|
5454
|
+
{
|
|
5455
|
+
error: "Minds cannot think yet \u2014 no AI provider is configured. Add a provider in Settings before creating a mind."
|
|
5456
|
+
},
|
|
5457
|
+
409
|
|
5458
|
+
);
|
|
5459
|
+
}
|
|
4822
5460
|
{
|
|
4823
|
-
const { mindLimitError, readGlobalConfig: readGlobalConfig2 } = await import("./setup-
|
|
5461
|
+
const { mindLimitError, readGlobalConfig: readGlobalConfig2 } = await import("./setup-CAJFOR2N.js");
|
|
4824
5462
|
const limitError = mindLimitError(await countCappedMinds(), readGlobalConfig2().maxMinds);
|
|
4825
5463
|
if (limitError) return c.json({ error: limitError }, 409);
|
|
4826
5464
|
}
|
|
@@ -4835,7 +5473,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
4835
5473
|
const { publicKeyPem } = generateIdentity(dest);
|
|
4836
5474
|
let effectiveModel = body.model;
|
|
4837
5475
|
{
|
|
4838
|
-
const { readGlobalConfig: readGlobal } = await import("./setup-
|
|
5476
|
+
const { readGlobalConfig: readGlobal } = await import("./setup-CAJFOR2N.js");
|
|
4839
5477
|
const mindDefaults = readGlobal().mindDefaults;
|
|
4840
5478
|
const config2 = readVoluteConfig(dest);
|
|
4841
5479
|
if (!config2) throw new Error("Failed to read volute.json after identity generation");
|
|
@@ -4916,7 +5554,7 @@ Your creator described you as: "${body.description}"
|
|
|
4916
5554
|
}
|
|
4917
5555
|
let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
|
|
4918
5556
|
if (body.stage === "seed" && !body.skills) {
|
|
4919
|
-
const { isImagegenEnabled } = await import("./setup-
|
|
5557
|
+
const { isImagegenEnabled } = await import("./setup-CAJFOR2N.js");
|
|
4920
5558
|
if (isImagegenEnabled()) {
|
|
4921
5559
|
skillSet = [...skillSet, "imagegen"];
|
|
4922
5560
|
}
|
|
@@ -4935,9 +5573,10 @@ Your creator described you as: "${body.description}"
|
|
|
4935
5573
|
}
|
|
4936
5574
|
if (body.stage === "seed") {
|
|
4937
5575
|
try {
|
|
4938
|
-
const
|
|
5576
|
+
const spiritName = getSpiritName();
|
|
5577
|
+
const spiritEntry = await findMind(spiritName);
|
|
4939
5578
|
if (spiritEntry) {
|
|
4940
|
-
const { spiritDir } = await import("./spirit-
|
|
5579
|
+
const { spiritDir } = await import("./spirit-IL5EMWYZ.js");
|
|
4941
5580
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
4942
5581
|
const spiritConfig = readVoluteConfig(sDir) ?? {};
|
|
4943
5582
|
const schedules = spiritConfig.schedules ?? [];
|
|
@@ -4946,14 +5585,14 @@ Your creator described you as: "${body.description}"
|
|
|
4946
5585
|
schedules.push({
|
|
4947
5586
|
id: nurtureId,
|
|
4948
5587
|
cron: process.env.VOLUTE_NURTURE_CRON ?? "*/5 * * * *",
|
|
4949
|
-
script: `volute seed check ${name}`,
|
|
5588
|
+
script: `volute seed check ${name} --nurture`,
|
|
4950
5589
|
enabled: true,
|
|
4951
5590
|
whileSleeping: "skip"
|
|
4952
5591
|
});
|
|
4953
5592
|
spiritConfig.schedules = schedules;
|
|
4954
5593
|
writeVoluteConfig(sDir, spiritConfig);
|
|
4955
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
4956
|
-
getScheduler2().loadSchedules(
|
|
5594
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-UV6RUJ7P.js");
|
|
5595
|
+
getScheduler2().loadSchedules(spiritName, sDir);
|
|
4957
5596
|
}
|
|
4958
5597
|
}
|
|
4959
5598
|
} catch (err) {
|
|
@@ -5151,7 +5790,10 @@ ${user.trimEnd()}
|
|
|
5151
5790
|
const privileged = isPrivileged(c);
|
|
5152
5791
|
const minds = await Promise.all(
|
|
5153
5792
|
entries.map(async (entry) => {
|
|
5154
|
-
const mindStatus = await getMindStatus(entry.name, entry.port, entry.running
|
|
5793
|
+
const mindStatus = await getMindStatus(entry.name, entry.port, entry.running, {
|
|
5794
|
+
stage: entry.stage,
|
|
5795
|
+
dir: entry.dir
|
|
5796
|
+
});
|
|
5155
5797
|
const hasPages = existsSync7(resolve11(mindDir(entry.name), "home", "pages"));
|
|
5156
5798
|
const lastActiveAt = lastActiveMap.get(entry.name) ?? null;
|
|
5157
5799
|
if (!privileged) return toPublicMind(entry, mindStatus, { hasPages, lastActiveAt });
|
|
@@ -5171,7 +5813,10 @@ ${user.trimEnd()}
|
|
|
5171
5813
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5172
5814
|
const dir = entry.dir ?? mindDir(entry.parent ?? name);
|
|
5173
5815
|
if (!existsSync7(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
5174
|
-
const mindStatus = await getMindStatus(name, entry.port
|
|
5816
|
+
const mindStatus = await getMindStatus(name, entry.port, void 0, {
|
|
5817
|
+
stage: entry.stage,
|
|
5818
|
+
dir: entry.dir
|
|
5819
|
+
});
|
|
5175
5820
|
const hasPages = existsSync7(resolve11(mindDir(name), "home", "pages"));
|
|
5176
5821
|
if (!isPrivileged(c)) {
|
|
5177
5822
|
return c.json(toPublicMind(entry, mindStatus, { hasPages }));
|
|
@@ -5188,7 +5833,7 @@ ${user.trimEnd()}
|
|
|
5188
5833
|
return { name: s.name, port: s.port, status: variantStatus };
|
|
5189
5834
|
})
|
|
5190
5835
|
);
|
|
5191
|
-
const notice = await
|
|
5836
|
+
const notice = await latestEvent(name);
|
|
5192
5837
|
return c.json({
|
|
5193
5838
|
...entry,
|
|
5194
5839
|
...mindStatus,
|
|
@@ -5249,7 +5894,7 @@ ${user.trimEnd()}
|
|
|
5249
5894
|
const manager = getMindManager();
|
|
5250
5895
|
try {
|
|
5251
5896
|
if (context?.type === "reload") {
|
|
5252
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5897
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
|
|
5253
5898
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
5254
5899
|
if (sleepState?.sleeping) {
|
|
5255
5900
|
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
@@ -5309,13 +5954,6 @@ ${user.trimEnd()}
|
|
|
5309
5954
|
if (context && context.type !== "reload") {
|
|
5310
5955
|
manager.setPendingContext(name, context);
|
|
5311
5956
|
}
|
|
5312
|
-
if (context?.type === "sprouted" && !entry.parent) {
|
|
5313
|
-
try {
|
|
5314
|
-
await deliverSproutedNotice(baseName);
|
|
5315
|
-
} catch (err) {
|
|
5316
|
-
logger_default.error(`failed to inject sprouted message for ${baseName}`, logger_default.errorData(err));
|
|
5317
|
-
}
|
|
5318
|
-
}
|
|
5319
5957
|
const repoDir = projectDir;
|
|
5320
5958
|
try {
|
|
5321
5959
|
await startMindFull(name);
|
|
@@ -5333,7 +5971,7 @@ ${user.trimEnd()}
|
|
|
5333
5971
|
const errLine = (stderr ?? startMsg).trim().split("\n").filter(Boolean).pop() ?? "";
|
|
5334
5972
|
await recordNotice({
|
|
5335
5973
|
mind: name,
|
|
5336
|
-
|
|
5974
|
+
thread: "main",
|
|
5337
5975
|
kind: "startup",
|
|
5338
5976
|
reason: "startup_failed",
|
|
5339
5977
|
detail: `Your last change to src/ broke startup, so it was rolled back and your previous working code was restored. The broken change is preserved on branch \`${rollback.branch}\` \u2014 check it out to inspect and fix it. Error: ${errLine}`,
|
|
@@ -5371,7 +6009,7 @@ ${user.trimEnd()}
|
|
|
5371
6009
|
const name = c.req.param("name");
|
|
5372
6010
|
const entry = await findMind(name);
|
|
5373
6011
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5374
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6012
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
|
|
5375
6013
|
const sm = getSleepManagerIfReady2();
|
|
5376
6014
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5377
6015
|
return c.json(sm.getState(name));
|
|
@@ -5379,7 +6017,7 @@ ${user.trimEnd()}
|
|
|
5379
6017
|
const name = c.req.param("name");
|
|
5380
6018
|
const entry = await findMind(name);
|
|
5381
6019
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5382
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6020
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
|
|
5383
6021
|
const sm = getSleepManagerIfReady2();
|
|
5384
6022
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5385
6023
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
@@ -5399,7 +6037,7 @@ ${user.trimEnd()}
|
|
|
5399
6037
|
const name = c.req.param("name");
|
|
5400
6038
|
const entry = await findMind(name);
|
|
5401
6039
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5402
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6040
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
|
|
5403
6041
|
const sm = getSleepManagerIfReady2();
|
|
5404
6042
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5405
6043
|
const sleepState = sm.getState(name);
|
|
@@ -5414,7 +6052,7 @@ ${user.trimEnd()}
|
|
|
5414
6052
|
const name = c.req.param("name");
|
|
5415
6053
|
const entry = await findMind(name);
|
|
5416
6054
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5417
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6055
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
|
|
5418
6056
|
const sm = getSleepManagerIfReady2();
|
|
5419
6057
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
5420
6058
|
const flushed = await sm.flushQueuedMessages(name);
|
|
@@ -5442,25 +6080,68 @@ ${user.trimEnd()}
|
|
|
5442
6080
|
}
|
|
5443
6081
|
config2.profile = profile;
|
|
5444
6082
|
writeVoluteConfig(dir, config2);
|
|
5445
|
-
const { syncMindProfile: syncMindProfile2 } = await import("./auth-
|
|
6083
|
+
const { syncMindProfile: syncMindProfile2 } = await import("./auth-DT7GY7EZ.js");
|
|
5446
6084
|
await syncMindProfile2(name, profile);
|
|
5447
6085
|
broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
|
|
5448
6086
|
return c.json({ ok: true });
|
|
6087
|
+
}).post(
|
|
6088
|
+
"/:name/imagegen/jobs",
|
|
6089
|
+
requireSelf(),
|
|
6090
|
+
zValidator6(
|
|
6091
|
+
"json",
|
|
6092
|
+
z6.object({
|
|
6093
|
+
model: z6.string().min(1),
|
|
6094
|
+
prompt: z6.string().min(1),
|
|
6095
|
+
filename: z6.string().min(1)
|
|
6096
|
+
})
|
|
6097
|
+
),
|
|
6098
|
+
async (c) => {
|
|
6099
|
+
const name = c.req.param("name");
|
|
6100
|
+
const { model, prompt, filename } = c.req.valid("json");
|
|
6101
|
+
try {
|
|
6102
|
+
const jobId = createImagegenJob(name, model, prompt, filename);
|
|
6103
|
+
return c.json({ jobId }, 202);
|
|
6104
|
+
} catch (err) {
|
|
6105
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to start job" }, 429);
|
|
6106
|
+
}
|
|
6107
|
+
}
|
|
6108
|
+
).get("/:name/imagegen/jobs/:jobId", requireSelf(), async (c) => {
|
|
6109
|
+
const name = c.req.param("name");
|
|
6110
|
+
const jobId = c.req.param("jobId");
|
|
6111
|
+
const waitParam = Number(c.req.query("wait"));
|
|
6112
|
+
const waitMs = Number.isFinite(waitParam) ? Math.min(Math.max(waitParam, 0), 60) * 1e3 : 0;
|
|
6113
|
+
const job = waitMs > 0 ? await waitForImagegenJob(name, jobId, waitMs) : getImagegenJob(name, jobId);
|
|
6114
|
+
if (!job) return c.json({ error: "Job not found" }, 404);
|
|
6115
|
+
return c.json(job);
|
|
5449
6116
|
}).get("/:name/seed-check", requireSelf(), async (c) => {
|
|
5450
6117
|
const name = c.req.param("name");
|
|
6118
|
+
const force = c.req.query("force") === "1" || c.req.query("force") === "true";
|
|
5451
6119
|
const entry = await findMind(name);
|
|
5452
6120
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5453
|
-
if (entry.stage !== "seed")
|
|
6121
|
+
if (entry.stage !== "seed") {
|
|
6122
|
+
return c.json({
|
|
6123
|
+
output: force ? `${name} is no longer a seed (stage: ${entry.stage}).` : ""
|
|
6124
|
+
});
|
|
6125
|
+
}
|
|
6126
|
+
if (!force) {
|
|
6127
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-LIEPO6WG.js");
|
|
6128
|
+
if (getSleepManagerIfReady2()?.isSleeping(name)) {
|
|
6129
|
+
return c.json({ output: "" });
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
5454
6132
|
const db = await getDb();
|
|
5455
6133
|
const rawCreator = Number(process.env.VOLUTE_NURTURE_CREATOR_MINUTES);
|
|
5456
6134
|
const creatorThreshold = Number.isNaN(rawCreator) ? 5 : rawCreator;
|
|
5457
6135
|
const rawSpirit = Number(process.env.VOLUTE_NURTURE_SPIRIT_MINUTES);
|
|
5458
6136
|
const spiritThreshold = Number.isNaN(rawSpirit) ? 15 : rawSpirit;
|
|
5459
|
-
const
|
|
6137
|
+
const rawNudge = Number(process.env.VOLUTE_NURTURE_NUDGE_MINUTES);
|
|
6138
|
+
const nudgeThreshold = Number.isNaN(rawNudge) ? 30 : rawNudge;
|
|
6139
|
+
const spiritName = getSpiritName();
|
|
6140
|
+
const lastActivityMsg = await db.select({ created_at: mindHistory.created_at, sender: mindHistory.sender }).from(mindHistory).where(
|
|
5460
6141
|
and5(
|
|
5461
6142
|
eq5(mindHistory.mind, name),
|
|
5462
6143
|
eq5(mindHistory.type, "inbound"),
|
|
5463
|
-
sql3`${mindHistory.sender} !=
|
|
6144
|
+
sql3`${mindHistory.sender} != ${spiritName}`,
|
|
5464
6145
|
sql3`${mindHistory.sender} != ${name}`,
|
|
5465
6146
|
sql3`${mindHistory.sender} IS NOT NULL`
|
|
5466
6147
|
)
|
|
@@ -5469,20 +6150,37 @@ ${user.trimEnd()}
|
|
|
5469
6150
|
and5(
|
|
5470
6151
|
eq5(mindHistory.mind, name),
|
|
5471
6152
|
eq5(mindHistory.type, "inbound"),
|
|
5472
|
-
eq5(mindHistory.sender,
|
|
6153
|
+
eq5(mindHistory.sender, spiritName)
|
|
5473
6154
|
)
|
|
5474
6155
|
).orderBy(desc3(mindHistory.created_at)).limit(1);
|
|
5475
6156
|
const now = Date.now();
|
|
5476
|
-
const
|
|
5477
|
-
const spiritTime = lastSpiritMsg[0] ?
|
|
5478
|
-
const
|
|
6157
|
+
const activityTime = lastActivityMsg[0] ? parseDbTimestamp(lastActivityMsg[0].created_at).getTime() : 0;
|
|
6158
|
+
const spiritTime = lastSpiritMsg[0] ? parseDbTimestamp(lastSpiritMsg[0].created_at).getTime() : 0;
|
|
6159
|
+
const minutesSinceActivity = activityTime ? (now - activityTime) / 6e4 : Infinity;
|
|
5479
6160
|
const minutesSinceSpirit = spiritTime ? (now - spiritTime) / 6e4 : Infinity;
|
|
5480
|
-
if (
|
|
6161
|
+
if (!force && (minutesSinceActivity < creatorThreshold || minutesSinceSpirit < spiritThreshold)) {
|
|
5481
6162
|
return c.json({ output: "" });
|
|
5482
6163
|
}
|
|
6164
|
+
if (!force) {
|
|
6165
|
+
const nudgePattern = `Seed: ${name.replace(/[\\%_]/g, "\\$&")}
|
|
6166
|
+
%`;
|
|
6167
|
+
const lastNudge = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
|
|
6168
|
+
and5(
|
|
6169
|
+
eq5(mindHistory.mind, spiritName),
|
|
6170
|
+
eq5(mindHistory.type, "event"),
|
|
6171
|
+
sql3`${mindHistory.content} LIKE ${nudgePattern} ESCAPE '\\'`
|
|
6172
|
+
)
|
|
6173
|
+
).orderBy(desc3(mindHistory.created_at)).limit(1);
|
|
6174
|
+
if (lastNudge[0]) {
|
|
6175
|
+
const minutesSinceNudge = (now - parseDbTimestamp(lastNudge[0].created_at).getTime()) / 6e4;
|
|
6176
|
+
if (minutesSinceNudge < nudgeThreshold) {
|
|
6177
|
+
return c.json({ output: "" });
|
|
6178
|
+
}
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
5483
6181
|
const dir = entry.dir ?? mindDir(name);
|
|
5484
|
-
const { evaluateSeedChecklist } = await import("./seed-readiness-
|
|
5485
|
-
const checklist =
|
|
6182
|
+
const { evaluateSeedChecklist: evaluateSeedChecklist2 } = await import("./seed-readiness-QRA46ZJQ.js");
|
|
6183
|
+
const checklist = evaluateSeedChecklist2(dir);
|
|
5486
6184
|
const done = [];
|
|
5487
6185
|
const remaining = [];
|
|
5488
6186
|
if (checklist.soulWritten) done.push("SOUL.md written");
|
|
@@ -5495,8 +6193,8 @@ ${user.trimEnd()}
|
|
|
5495
6193
|
if (checklist.avatarSet) done.push("Avatar set");
|
|
5496
6194
|
else remaining.push("Generate and set avatar");
|
|
5497
6195
|
}
|
|
5498
|
-
const
|
|
5499
|
-
const lines = [`Seed: ${name}`,
|
|
6196
|
+
const activityStatus = minutesSinceActivity === Infinity ? `No one has messaged ${name} yet` : `Last message to ${name}: ${Math.round(minutesSinceActivity)} minutes ago (from ${lastActivityMsg[0].sender})`;
|
|
6197
|
+
const lines = [`Seed: ${name}`, activityStatus];
|
|
5500
6198
|
if (done.length > 0) lines.push(`Done: ${done.join(", ")}`);
|
|
5501
6199
|
if (remaining.length > 0) lines.push(`Remaining: ${remaining.join(", ")}`);
|
|
5502
6200
|
if (remaining.length > 0) {
|
|
@@ -5519,19 +6217,21 @@ ${user.trimEnd()}
|
|
|
5519
6217
|
await joinSystemChannelForMind(name).catch(
|
|
5520
6218
|
(err) => logger_default.warn(`failed to join #system on sprout for ${name}`, logger_default.errorData(err))
|
|
5521
6219
|
);
|
|
6220
|
+
await announceSprout(name);
|
|
5522
6221
|
try {
|
|
5523
6222
|
const sproutedDir = entry.dir ?? mindDir(name);
|
|
5524
6223
|
if (setupDefaultDreaming(sproutedDir).schedulesChanged) {
|
|
5525
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
6224
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-UV6RUJ7P.js");
|
|
5526
6225
|
getScheduler2().loadSchedules(name, sproutedDir);
|
|
5527
6226
|
}
|
|
5528
6227
|
} catch (err) {
|
|
5529
6228
|
logger_default.warn(`failed to set up default dreaming for ${name}`, logger_default.errorData(err));
|
|
5530
6229
|
}
|
|
5531
6230
|
try {
|
|
5532
|
-
const
|
|
6231
|
+
const spiritName = getSpiritName();
|
|
6232
|
+
const spiritEntry = await findMind(spiritName);
|
|
5533
6233
|
if (spiritEntry) {
|
|
5534
|
-
const { firstWeekSchedules, spiritDir } = await import("./spirit-
|
|
6234
|
+
const { firstWeekSchedules, spiritDir } = await import("./spirit-IL5EMWYZ.js");
|
|
5535
6235
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
5536
6236
|
const spiritConfig = readVoluteConfig(sDir);
|
|
5537
6237
|
if (spiritConfig) {
|
|
@@ -5545,8 +6245,8 @@ ${user.trimEnd()}
|
|
|
5545
6245
|
spiritConfig.schedules = schedules;
|
|
5546
6246
|
writeVoluteConfig(sDir, spiritConfig);
|
|
5547
6247
|
try {
|
|
5548
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
5549
|
-
getScheduler2().loadSchedules(
|
|
6248
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-UV6RUJ7P.js");
|
|
6249
|
+
getScheduler2().loadSchedules(spiritName, sDir);
|
|
5550
6250
|
} catch (err) {
|
|
5551
6251
|
logger_default.warn(
|
|
5552
6252
|
`spirit schedules for sprout of ${name} written to disk but not reloaded into the running scheduler (effective on next spirit restart)`,
|
|
@@ -5866,7 +6566,7 @@ ${user.trimEnd()}
|
|
|
5866
6566
|
const limitStr = c.req.query("limit");
|
|
5867
6567
|
if (!beforeStr && !limitStr) {
|
|
5868
6568
|
const msgs = await getMessages(convId);
|
|
5869
|
-
return c.json({ items: msgs, hasMore: false });
|
|
6569
|
+
return c.json({ items: await withSenderDisplayNames(msgs), hasMore: false });
|
|
5870
6570
|
}
|
|
5871
6571
|
const before = beforeStr ? parseInt(beforeStr, 10) : void 0;
|
|
5872
6572
|
const limit = limitStr ? parseInt(limitStr, 10) : void 0;
|
|
@@ -5874,7 +6574,10 @@ ${user.trimEnd()}
|
|
|
5874
6574
|
return c.json({ error: "Invalid pagination parameters" }, 400);
|
|
5875
6575
|
}
|
|
5876
6576
|
const result = await getMessagesPaginated(convId, { before, limit });
|
|
5877
|
-
return c.json({
|
|
6577
|
+
return c.json({
|
|
6578
|
+
items: await withSenderDisplayNames(result.messages),
|
|
6579
|
+
hasMore: result.hasMore
|
|
6580
|
+
});
|
|
5878
6581
|
}).get("/:name/budget", requireSelf(), async (c) => {
|
|
5879
6582
|
const name = c.req.param("name");
|
|
5880
6583
|
const baseName = await getBaseName(name);
|
|
@@ -5967,7 +6670,7 @@ ${user.trimEnd()}
|
|
|
5967
6670
|
}
|
|
5968
6671
|
writeVoluteConfig(dir, existing);
|
|
5969
6672
|
if (body.unescapeNewlines !== void 0) {
|
|
5970
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
6673
|
+
const { clearEchoTextCache } = await import("./echo-text-WYAAODBV.js");
|
|
5971
6674
|
clearEchoTextCache(name);
|
|
5972
6675
|
}
|
|
5973
6676
|
const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.compaction !== void 0;
|
|
@@ -6004,7 +6707,7 @@ ${user.trimEnd()}
|
|
|
6004
6707
|
}
|
|
6005
6708
|
if (entry.mindType === "spirit" && body.model !== void 0) {
|
|
6006
6709
|
try {
|
|
6007
|
-
const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-
|
|
6710
|
+
const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-CAJFOR2N.js");
|
|
6008
6711
|
const globalConfig = readGlobalConfig2();
|
|
6009
6712
|
globalConfig.spiritModel = body.model;
|
|
6010
6713
|
writeGlobalConfig2(globalConfig);
|
|
@@ -6047,8 +6750,8 @@ ${user.trimEnd()}
|
|
|
6047
6750
|
if (!body.systemPrompt || !body.message) {
|
|
6048
6751
|
return c.json({ error: "systemPrompt and message required" }, 400);
|
|
6049
6752
|
}
|
|
6050
|
-
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-
|
|
6051
|
-
if (!
|
|
6753
|
+
const { aiComplete: aiCompleteFn, isAiConfigured: isAiConfigured2 } = await import("./ai-service-V4CTNGI3.js");
|
|
6754
|
+
if (!isAiConfigured2()) {
|
|
6052
6755
|
return c.json({ error: "AI service not configured" }, 503);
|
|
6053
6756
|
}
|
|
6054
6757
|
const text = await aiCompleteFn(body.systemPrompt, body.message, body.model);
|
|
@@ -6068,6 +6771,9 @@ ${user.trimEnd()}
|
|
|
6068
6771
|
if (!body.type) {
|
|
6069
6772
|
return c.json({ error: "type required" }, 400);
|
|
6070
6773
|
}
|
|
6774
|
+
if (DAEMON_AUTHORED_TYPES.has(body.type)) {
|
|
6775
|
+
return c.json({ error: `type "${body.type}" is daemon-authored` }, 400);
|
|
6776
|
+
}
|
|
6071
6777
|
await handleMindEvent(baseName, body);
|
|
6072
6778
|
return c.json({ ok: true });
|
|
6073
6779
|
}).get("/:name/events", requireSelf(), async (c) => {
|
|
@@ -6095,7 +6801,7 @@ ${user.trimEnd()}
|
|
|
6095
6801
|
unsubscribe?.();
|
|
6096
6802
|
}
|
|
6097
6803
|
}, 15e3);
|
|
6098
|
-
unsubscribe =
|
|
6804
|
+
unsubscribe = subscribe2(baseName, (event) => {
|
|
6099
6805
|
if (typeFilter && !typeFilter.includes(event.type)) return;
|
|
6100
6806
|
if (sessionFilter && event.session !== sessionFilter) return;
|
|
6101
6807
|
if (channelFilter && event.channel !== channelFilter) return;
|
|
@@ -6156,12 +6862,12 @@ ${user.trimEnd()}
|
|
|
6156
6862
|
const name = c.req.param("name");
|
|
6157
6863
|
const db = await getDb();
|
|
6158
6864
|
const rows = await db.select({
|
|
6159
|
-
|
|
6865
|
+
thread: mindHistory.thread,
|
|
6160
6866
|
started_at: sql3`MIN(${mindHistory.created_at})`,
|
|
6161
6867
|
event_count: sql3`COUNT(*)`,
|
|
6162
6868
|
message_count: sql3`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
|
|
6163
6869
|
tool_count: sql3`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
|
|
6164
|
-
}).from(mindHistory).where(and5(eq5(mindHistory.mind, name), sql3`${mindHistory.
|
|
6870
|
+
}).from(mindHistory).where(and5(eq5(mindHistory.mind, name), sql3`${mindHistory.thread} IS NOT NULL`)).groupBy(mindHistory.thread).orderBy(sql3`MIN(${mindHistory.created_at}) DESC`);
|
|
6165
6871
|
return c.json(rows);
|
|
6166
6872
|
}).get("/:name/history/channels", requireSelf(), async (c) => {
|
|
6167
6873
|
const name = c.req.param("name");
|
|
@@ -6211,7 +6917,7 @@ ${user.trimEnd()}
|
|
|
6211
6917
|
const turnId = c.req.query("turn_id");
|
|
6212
6918
|
const detail = c.req.query("detail") === "1";
|
|
6213
6919
|
const db = await getDb();
|
|
6214
|
-
const typeFilter = detail ? void 0 : sql3`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking','activity')`;
|
|
6920
|
+
const typeFilter = detail ? void 0 : sql3`${mindHistory.type} IN ('inbound','event','outbound','tool_use','tool_result','text','thinking','activity')`;
|
|
6215
6921
|
let rows;
|
|
6216
6922
|
if (turnId) {
|
|
6217
6923
|
rows = await db.select().from(mindHistory).where(and5(eq5(mindHistory.mind, name), eq5(mindHistory.turn_id, turnId), typeFilter)).orderBy(mindHistory.id);
|
|
@@ -6225,7 +6931,7 @@ ${user.trimEnd()}
|
|
|
6225
6931
|
rows = await db.select().from(mindHistory).where(
|
|
6226
6932
|
and5(
|
|
6227
6933
|
eq5(mindHistory.mind, name),
|
|
6228
|
-
eq5(mindHistory.
|
|
6934
|
+
eq5(mindHistory.thread, session),
|
|
6229
6935
|
sql3`${mindHistory.id} >= ${fromId}`,
|
|
6230
6936
|
sql3`${mindHistory.id} <= ${toId}`,
|
|
6231
6937
|
typeFilter
|
|
@@ -6242,7 +6948,7 @@ ${user.trimEnd()}
|
|
|
6242
6948
|
const lastTurn = await db.select({ turn_id: mindHistory.turn_id }).from(mindHistory).where(
|
|
6243
6949
|
and5(
|
|
6244
6950
|
eq5(mindHistory.mind, name),
|
|
6245
|
-
eq5(mindHistory.
|
|
6951
|
+
eq5(mindHistory.thread, currentSession),
|
|
6246
6952
|
sql3`${mindHistory.turn_id} IS NOT NULL`
|
|
6247
6953
|
)
|
|
6248
6954
|
).orderBy(desc3(mindHistory.created_at)).limit(1);
|
|
@@ -6262,10 +6968,10 @@ ${user.trimEnd()}
|
|
|
6262
6968
|
sql3`${summaries.created_at} > ${sinceTimestamp}`
|
|
6263
6969
|
];
|
|
6264
6970
|
if (currentSession) {
|
|
6265
|
-
conditions.push(sql3`${turns.
|
|
6971
|
+
conditions.push(sql3`${turns.thread} != ${currentSession}`);
|
|
6266
6972
|
}
|
|
6267
6973
|
const rows = await db.select({
|
|
6268
|
-
|
|
6974
|
+
thread: turns.thread,
|
|
6269
6975
|
content: summaries.content,
|
|
6270
6976
|
created_at: summaries.created_at
|
|
6271
6977
|
}).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and5(...conditions)).orderBy(desc3(summaries.created_at)).limit(50);
|
|
@@ -6273,9 +6979,9 @@ ${user.trimEnd()}
|
|
|
6273
6979
|
return c.json({ context: null });
|
|
6274
6980
|
}
|
|
6275
6981
|
const lines = rows.map((row) => {
|
|
6276
|
-
const ts =
|
|
6982
|
+
const ts = parseDbTimestamp(row.created_at);
|
|
6277
6983
|
const ago = formatTimeAgo(ts);
|
|
6278
|
-
return `- ${row.
|
|
6984
|
+
return `- ${row.thread ?? "unknown"} (${ago}): ${row.content ?? ""}`;
|
|
6279
6985
|
});
|
|
6280
6986
|
return c.json({ context: `[Session Activity]
|
|
6281
6987
|
${lines.join("\n")}` });
|
|
@@ -6284,11 +6990,33 @@ ${lines.join("\n")}` });
|
|
|
6284
6990
|
const baseName = await getBaseName(name);
|
|
6285
6991
|
const session = c.req.query("session");
|
|
6286
6992
|
if (!session) return c.json({ context: null, notices: [] });
|
|
6287
|
-
const notices = await
|
|
6993
|
+
const notices = await drainEvents(baseName, session);
|
|
6288
6994
|
if (notices.length === 0) return c.json({ context: null, notices: [] });
|
|
6289
6995
|
const maxId = notices.reduce((m, n) => Math.max(m, n.id), 0);
|
|
6290
6996
|
setNoticeDrainWatermark(baseName, session, maxId);
|
|
6291
|
-
return c.json({ context:
|
|
6997
|
+
return c.json({ context: formatEvents(notices), notices });
|
|
6998
|
+
}).get("/:name/system-events", requireSelf(), async (c) => {
|
|
6999
|
+
const name = c.req.param("name");
|
|
7000
|
+
const baseName = await getBaseName(name);
|
|
7001
|
+
const limit = Math.min(Number(c.req.query("limit") ?? 100) || 100, 200);
|
|
7002
|
+
const before = c.req.query("before") ? Number(c.req.query("before")) : void 0;
|
|
7003
|
+
const events = await listEvents(baseName, limit, before);
|
|
7004
|
+
return c.json({
|
|
7005
|
+
events: events.map((e) => {
|
|
7006
|
+
const meta = parseMeta(e.meta, `event ${e.id}`);
|
|
7007
|
+
return {
|
|
7008
|
+
id: e.id,
|
|
7009
|
+
type: e.type,
|
|
7010
|
+
label: eventLabel(e.type, meta),
|
|
7011
|
+
body: e.body,
|
|
7012
|
+
meta,
|
|
7013
|
+
delivery: e.delivery,
|
|
7014
|
+
reflection: e.reflection,
|
|
7015
|
+
created_at: e.created_at,
|
|
7016
|
+
delivered_at: e.delivered_at
|
|
7017
|
+
};
|
|
7018
|
+
})
|
|
7019
|
+
});
|
|
6292
7020
|
}).get("/:name/history", requireSelf(), async (c) => {
|
|
6293
7021
|
const name = c.req.param("name");
|
|
6294
7022
|
const channel = c.req.query("channel");
|
|
@@ -6303,13 +7031,13 @@ ${lines.join("\n")}` });
|
|
|
6303
7031
|
conditions.push(eq5(mindHistory.channel, channel));
|
|
6304
7032
|
}
|
|
6305
7033
|
if (session) {
|
|
6306
|
-
conditions.push(eq5(mindHistory.
|
|
7034
|
+
conditions.push(eq5(mindHistory.thread, session));
|
|
6307
7035
|
}
|
|
6308
7036
|
const effectivePreset = full ? "all" : preset;
|
|
6309
7037
|
if (!effectivePreset || effectivePreset === "summary") {
|
|
6310
7038
|
const sumConditions = [eq5(summaries.mind, name), eq5(summaries.period, "turn")];
|
|
6311
7039
|
if (session) {
|
|
6312
|
-
sumConditions.push(eq5(turns.
|
|
7040
|
+
sumConditions.push(eq5(turns.thread, session));
|
|
6313
7041
|
const sumRows2 = await db.select({
|
|
6314
7042
|
id: summaries.id,
|
|
6315
7043
|
mind: summaries.mind,
|
|
@@ -6318,7 +7046,7 @@ ${lines.join("\n")}` });
|
|
|
6318
7046
|
content: summaries.content,
|
|
6319
7047
|
metadata: summaries.metadata,
|
|
6320
7048
|
created_at: summaries.created_at,
|
|
6321
|
-
|
|
7049
|
+
thread: turns.thread
|
|
6322
7050
|
}).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and5(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
|
|
6323
7051
|
return c.json(
|
|
6324
7052
|
sumRows2.map((r) => ({
|
|
@@ -6326,7 +7054,7 @@ ${lines.join("\n")}` });
|
|
|
6326
7054
|
mind: r.mind,
|
|
6327
7055
|
type: "summary",
|
|
6328
7056
|
channel: null,
|
|
6329
|
-
|
|
7057
|
+
thread: r.thread,
|
|
6330
7058
|
sender: null,
|
|
6331
7059
|
message_id: null,
|
|
6332
7060
|
content: r.content,
|
|
@@ -6343,7 +7071,7 @@ ${lines.join("\n")}` });
|
|
|
6343
7071
|
mind: r.mind,
|
|
6344
7072
|
type: "summary",
|
|
6345
7073
|
channel: null,
|
|
6346
|
-
|
|
7074
|
+
thread: null,
|
|
6347
7075
|
sender: null,
|
|
6348
7076
|
message_id: null,
|
|
6349
7077
|
content: r.content,
|
|
@@ -6357,16 +7085,22 @@ ${lines.join("\n")}` });
|
|
|
6357
7085
|
case "all":
|
|
6358
7086
|
break;
|
|
6359
7087
|
case "conversation":
|
|
6360
|
-
conditions.push(sql3`${mindHistory.type} IN ('inbound','outbound','tool_use')`);
|
|
7088
|
+
conditions.push(sql3`${mindHistory.type} IN ('inbound','event','outbound','tool_use')`);
|
|
6361
7089
|
break;
|
|
6362
7090
|
case "detailed":
|
|
6363
7091
|
conditions.push(
|
|
6364
|
-
sql3`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking')`
|
|
7092
|
+
sql3`${mindHistory.type} IN ('inbound','event','outbound','tool_use','tool_result','text','thinking')`
|
|
6365
7093
|
);
|
|
6366
7094
|
break;
|
|
6367
7095
|
}
|
|
6368
7096
|
const rows = await db.select().from(mindHistory).where(and5(...conditions)).orderBy(desc3(mindHistory.created_at)).limit(limit).offset(offset);
|
|
6369
|
-
|
|
7097
|
+
const displayNames = await getDisplayNames(rows.map((r) => r.sender));
|
|
7098
|
+
return c.json(
|
|
7099
|
+
rows.map((r) => ({
|
|
7100
|
+
...r,
|
|
7101
|
+
sender_display_name: r.sender ? displayNames.get(r.sender) ?? null : null
|
|
7102
|
+
}))
|
|
7103
|
+
);
|
|
6370
7104
|
});
|
|
6371
7105
|
var minds_default = app13;
|
|
6372
7106
|
|
|
@@ -6428,9 +7162,9 @@ var slog2 = logger_default.child("schedules");
|
|
|
6428
7162
|
function readSchedules(dir) {
|
|
6429
7163
|
return readVoluteConfig(dir)?.schedules ?? [];
|
|
6430
7164
|
}
|
|
6431
|
-
function validateMessages(
|
|
6432
|
-
if (
|
|
6433
|
-
if (!Array.isArray(
|
|
7165
|
+
function validateMessages(messages2) {
|
|
7166
|
+
if (messages2 === void 0) return null;
|
|
7167
|
+
if (!Array.isArray(messages2) || messages2.some((m) => typeof m !== "string" || !m.trim())) {
|
|
6434
7168
|
return "messages must be an array of non-empty strings";
|
|
6435
7169
|
}
|
|
6436
7170
|
return null;
|
|
@@ -6616,6 +7350,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6616
7350
|
if (body.messages?.length) schedule.messages = body.messages;
|
|
6617
7351
|
if (body.script) schedule.script = body.script;
|
|
6618
7352
|
if (body.channel) schedule.channel = body.channel;
|
|
7353
|
+
if (body.thread) schedule.thread = body.thread;
|
|
6619
7354
|
if (body.whileSleeping) schedule.whileSleeping = body.whileSleeping;
|
|
6620
7355
|
schedules.push(schedule);
|
|
6621
7356
|
writeSchedules(name, dir, schedules);
|
|
@@ -6680,6 +7415,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6680
7415
|
}
|
|
6681
7416
|
if (body.enabled !== void 0) schedules[idx].enabled = body.enabled;
|
|
6682
7417
|
if (body.channel !== void 0) schedules[idx].channel = body.channel || void 0;
|
|
7418
|
+
if (body.thread !== void 0) schedules[idx].thread = body.thread || void 0;
|
|
6683
7419
|
if (body.whileSleeping !== void 0)
|
|
6684
7420
|
schedules[idx].whileSleeping = body.whileSleeping || void 0;
|
|
6685
7421
|
const touchedAction = body.message !== void 0 || body.messages !== void 0 || body.script !== void 0;
|
|
@@ -6708,15 +7444,18 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6708
7444
|
const entry = await findMind(name);
|
|
6709
7445
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6710
7446
|
const body = await c.req.text();
|
|
6711
|
-
const
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
}
|
|
6717
|
-
|
|
6718
|
-
|
|
7447
|
+
const { deliverEvent: deliverEvent2 } = await import("./system-events-A6IGCEC3.js");
|
|
7448
|
+
const { id, delivered } = await deliverEvent2(name, {
|
|
7449
|
+
type: "webhook",
|
|
7450
|
+
body,
|
|
7451
|
+
meta: { source: event }
|
|
7452
|
+
});
|
|
7453
|
+
if (id == null) {
|
|
7454
|
+
slog2.warn(`webhook event for ${name} could not be recorded`);
|
|
7455
|
+
return c.json({ error: "Failed to record event" }, 502);
|
|
6719
7456
|
}
|
|
7457
|
+
if (!delivered) return c.json({ ok: true, delivered: false, id }, 202);
|
|
7458
|
+
return c.json({ ok: true, delivered: true, id });
|
|
6720
7459
|
});
|
|
6721
7460
|
var schedules_default = app15;
|
|
6722
7461
|
|
|
@@ -6764,8 +7503,8 @@ setup.get("/status", async (c) => {
|
|
|
6764
7503
|
let hasAccount = false;
|
|
6765
7504
|
if (hasSystem) {
|
|
6766
7505
|
try {
|
|
6767
|
-
const { listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6768
|
-
const brains = await listUsersByType2("
|
|
7506
|
+
const { listUsersByType: listUsersByType2 } = await import("./auth-DT7GY7EZ.js");
|
|
7507
|
+
const brains = await listUsersByType2("human");
|
|
6769
7508
|
hasAccount = brains.length > 0;
|
|
6770
7509
|
} catch (err) {
|
|
6771
7510
|
logger_default.debug("could not check for existing accounts during setup status", logger_default.errorData(err));
|
|
@@ -6775,7 +7514,8 @@ setup.get("/status", async (c) => {
|
|
|
6775
7514
|
complete,
|
|
6776
7515
|
hasSystem,
|
|
6777
7516
|
hasAccount,
|
|
6778
|
-
setupType: config2.setup?.type ?? null
|
|
7517
|
+
setupType: config2.setup?.type ?? null,
|
|
7518
|
+
spiritName: config2.setup?.spiritName ?? null
|
|
6779
7519
|
});
|
|
6780
7520
|
});
|
|
6781
7521
|
setup.post("/configure", async (c) => {
|
|
@@ -6975,7 +7715,7 @@ setup.post("/account", async (c) => {
|
|
|
6975
7715
|
}
|
|
6976
7716
|
}
|
|
6977
7717
|
try {
|
|
6978
|
-
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-
|
|
7718
|
+
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-DT7GY7EZ.js");
|
|
6979
7719
|
const user = await createUser2(body.username.trim(), body.password);
|
|
6980
7720
|
if (body.displayName?.trim()) {
|
|
6981
7721
|
await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
|
|
@@ -7021,7 +7761,7 @@ setup.post("/models", async (c) => {
|
|
|
7021
7761
|
return c.json({ error: "Spirit model is required" }, 400);
|
|
7022
7762
|
}
|
|
7023
7763
|
try {
|
|
7024
|
-
const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-
|
|
7764
|
+
const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-V4CTNGI3.js");
|
|
7025
7765
|
setEnabledModels3(body.models);
|
|
7026
7766
|
const config2 = readGlobalConfig();
|
|
7027
7767
|
config2.spiritModel = body.spiritModel.trim();
|
|
@@ -7034,20 +7774,49 @@ setup.post("/models", async (c) => {
|
|
|
7034
7774
|
return c.json({ error: `Failed to save models: ${err.message}` }, 500);
|
|
7035
7775
|
}
|
|
7036
7776
|
});
|
|
7777
|
+
setup.post("/spirit", async (c) => {
|
|
7778
|
+
if (isSetupComplete()) {
|
|
7779
|
+
return c.json({ error: "Setup already complete" }, 400);
|
|
7780
|
+
}
|
|
7781
|
+
let body;
|
|
7782
|
+
try {
|
|
7783
|
+
body = await c.req.json();
|
|
7784
|
+
} catch {
|
|
7785
|
+
return c.json({ error: "Invalid JSON in request body" }, 400);
|
|
7786
|
+
}
|
|
7787
|
+
const name = body.name?.trim() ?? "";
|
|
7788
|
+
const nameErr = validateSpiritName(name);
|
|
7789
|
+
if (nameErr) {
|
|
7790
|
+
return c.json({ error: nameErr }, 400);
|
|
7791
|
+
}
|
|
7792
|
+
const config2 = readGlobalConfig();
|
|
7793
|
+
if (!config2.setup) {
|
|
7794
|
+
return c.json({ error: "Complete the system step first" }, 400);
|
|
7795
|
+
}
|
|
7796
|
+
const existing = await findMind(name);
|
|
7797
|
+
if (existing && existing.mindType !== "spirit") {
|
|
7798
|
+
return c.json({ error: `A mind named "${name}" already exists` }, 400);
|
|
7799
|
+
}
|
|
7800
|
+
config2.setup.spiritName = name;
|
|
7801
|
+
const temperament = body.temperament?.trim();
|
|
7802
|
+
config2.setup.spiritTemperament = temperament || void 0;
|
|
7803
|
+
writeGlobalConfig(config2);
|
|
7804
|
+
return c.json({ ok: true });
|
|
7805
|
+
});
|
|
7037
7806
|
setup.post("/complete", async (c) => {
|
|
7038
7807
|
if (isSetupComplete()) {
|
|
7039
7808
|
return c.json({ error: "Setup already complete" }, 400);
|
|
7040
7809
|
}
|
|
7041
7810
|
try {
|
|
7042
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
7043
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
7811
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-IL5EMWYZ.js");
|
|
7812
|
+
const { startSpiritFull } = await import("./mind-service-5TA7UJAT.js");
|
|
7044
7813
|
await ensureSpiritProject();
|
|
7045
7814
|
await syncSpiritTemplate();
|
|
7046
7815
|
const warnings = [];
|
|
7047
7816
|
const config2 = readGlobalConfig();
|
|
7048
7817
|
if (config2.setup?.service && config2.setup?.type === "local") {
|
|
7049
7818
|
try {
|
|
7050
|
-
const { installUserService } = await import("./service-install-
|
|
7819
|
+
const { installUserService } = await import("./service-install-MOKTEG7B.js");
|
|
7051
7820
|
const installed = await installUserService(config2.port, config2.hostname);
|
|
7052
7821
|
if (!installed) {
|
|
7053
7822
|
warnings.push("Service installation is not supported on this platform.");
|
|
@@ -7063,10 +7832,11 @@ setup.post("/complete", async (c) => {
|
|
|
7063
7832
|
writeGlobalConfig(config2);
|
|
7064
7833
|
}
|
|
7065
7834
|
}
|
|
7835
|
+
const spiritName = getSpiritName();
|
|
7066
7836
|
let spiritStarted = false;
|
|
7067
7837
|
try {
|
|
7068
7838
|
logger_default.info("starting spirit during setup...");
|
|
7069
|
-
await startSpiritFull(
|
|
7839
|
+
await startSpiritFull(spiritName);
|
|
7070
7840
|
spiritStarted = true;
|
|
7071
7841
|
logger_default.info("spirit started successfully during setup");
|
|
7072
7842
|
} catch (err) {
|
|
@@ -7076,10 +7846,10 @@ setup.post("/complete", async (c) => {
|
|
|
7076
7846
|
}
|
|
7077
7847
|
let spiritConversationId;
|
|
7078
7848
|
try {
|
|
7079
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-
|
|
7080
|
-
const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-
|
|
7081
|
-
const spiritUser = await getOrCreateMindUser2(
|
|
7082
|
-
const brains = await listUsersByType2("
|
|
7849
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-DT7GY7EZ.js");
|
|
7850
|
+
const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-S2232Y5I.js");
|
|
7851
|
+
const spiritUser = await getOrCreateMindUser2(spiritName);
|
|
7852
|
+
const brains = await listUsersByType2("human");
|
|
7083
7853
|
const admin2 = brains.find((u) => u.role === "admin");
|
|
7084
7854
|
if (admin2) {
|
|
7085
7855
|
const existing = await findDMConversation2([admin2.id, spiritUser.id]);
|
|
@@ -7099,20 +7869,22 @@ setup.post("/complete", async (c) => {
|
|
|
7099
7869
|
logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
|
|
7100
7870
|
if (spiritConversationId && spiritStarted) {
|
|
7101
7871
|
try {
|
|
7102
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
7103
|
-
const { listUsersByType: listUsers2 } = await import("./auth-
|
|
7104
|
-
const admins = await listUsers2("
|
|
7872
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-3456PXB7.js");
|
|
7873
|
+
const { listUsersByType: listUsers2 } = await import("./auth-DT7GY7EZ.js");
|
|
7874
|
+
const admins = await listUsers2("human");
|
|
7105
7875
|
const admin2 = admins.find((u) => u.role === "admin");
|
|
7106
7876
|
if (admin2) {
|
|
7107
7877
|
const adminName = admin2.display_name || admin2.username;
|
|
7108
|
-
const
|
|
7109
|
-
|
|
7878
|
+
const cfg = readGlobalConfig();
|
|
7879
|
+
const temperament = cfg.setup?.spiritTemperament;
|
|
7880
|
+
const welcomePrompt = `Setup is complete. You are ${spiritName}, the spirit of ${cfg.name ?? "this system"}` + (cfg.description ? ` \u2014 ${cfg.description}` : "") + `. ${adminName} chose your name just now` + (temperament ? ` and imagined your temperament as: "${temperament}"` : "") + `, and is now in your chat. Greet ${adminName} by name and help them create their first mind. Keep it brief \u2014 a short greeting in your own voice, then ask what kind of being they'd like to bring into the world. Remember: minds are beings with their own experience, not tools \u2014 ask about personality and character, never about tasks or utility.`;
|
|
7881
|
+
await deliverMessage2(spiritName, {
|
|
7110
7882
|
content: [{ type: "text", text: welcomePrompt }],
|
|
7111
7883
|
channel: `@${admin2.username}`,
|
|
7112
7884
|
conversationId: spiritConversationId,
|
|
7113
7885
|
sender: admin2.username,
|
|
7114
7886
|
isDM: true,
|
|
7115
|
-
participants: [
|
|
7887
|
+
participants: [spiritName, admin2.username],
|
|
7116
7888
|
participantCount: 2
|
|
7117
7889
|
});
|
|
7118
7890
|
}
|
|
@@ -7285,7 +8057,7 @@ var app17 = new Hono21().post("/:name/typing", zValidator8("json", typingSchema)
|
|
|
7285
8057
|
map.delete(channel, sender);
|
|
7286
8058
|
}
|
|
7287
8059
|
if (isConversationId(channel)) {
|
|
7288
|
-
|
|
8060
|
+
publish3(channel, { type: "typing", senders: map.get(channel) });
|
|
7289
8061
|
}
|
|
7290
8062
|
return c.json({ ok: true });
|
|
7291
8063
|
}).get("/:name/typing", (c) => {
|
|
@@ -7326,6 +8098,13 @@ import { z as z9 } from "zod";
|
|
|
7326
8098
|
var createSchema = z9.object({
|
|
7327
8099
|
participantNames: z9.array(z9.string()).min(1)
|
|
7328
8100
|
});
|
|
8101
|
+
async function canReadConversation(id, user) {
|
|
8102
|
+
const conv = await getConversation(id);
|
|
8103
|
+
if (!conv) return false;
|
|
8104
|
+
if (conv.private !== 1) return true;
|
|
8105
|
+
if (user.id === 0 || user.role === "admin" || user.role === "system") return true;
|
|
8106
|
+
return isParticipantOrOwner(id, user.id);
|
|
8107
|
+
}
|
|
7329
8108
|
var app19 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
7330
8109
|
const user = c.get("user");
|
|
7331
8110
|
const convs = await listConversationsWithParticipants(user.id);
|
|
@@ -7333,7 +8112,7 @@ var app19 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7333
8112
|
}).get("/:id/messages", async (c) => {
|
|
7334
8113
|
const id = c.req.param("id");
|
|
7335
8114
|
const user = c.get("user");
|
|
7336
|
-
if (
|
|
8115
|
+
if (!await canReadConversation(id, user)) {
|
|
7337
8116
|
return c.json({ error: "Conversation not found" }, 404);
|
|
7338
8117
|
}
|
|
7339
8118
|
const beforeStr = c.req.query("before");
|
|
@@ -7348,7 +8127,7 @@ var app19 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7348
8127
|
}).get("/:id/participants", async (c) => {
|
|
7349
8128
|
const id = c.req.param("id");
|
|
7350
8129
|
const user = c.get("user");
|
|
7351
|
-
if (
|
|
8130
|
+
if (!await canReadConversation(id, user)) {
|
|
7352
8131
|
return c.json({ error: "Conversation not found" }, 404);
|
|
7353
8132
|
}
|
|
7354
8133
|
const participants = await getParticipants(id);
|
|
@@ -7457,11 +8236,25 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7457
8236
|
const activityMind = privileged ? void 0 : await getBaseName(user.username);
|
|
7458
8237
|
return streamSSE4(c, async (stream) => {
|
|
7459
8238
|
const cleanups = [];
|
|
7460
|
-
if (user.user_type === "
|
|
8239
|
+
if (user.user_type === "human") {
|
|
7461
8240
|
addConnection(user.username);
|
|
7462
8241
|
cleanups.push(() => removeConnection(user.username));
|
|
7463
8242
|
}
|
|
7464
8243
|
try {
|
|
8244
|
+
let subscribeToConversation2 = function(convId) {
|
|
8245
|
+
const unsubConv = subscribe3(convId, (event) => {
|
|
8246
|
+
const { seq, ...rest } = event;
|
|
8247
|
+
const data = { event: "conversation", conversationId: convId, ...rest };
|
|
8248
|
+
stream.writeSSE({
|
|
8249
|
+
id: String(seq),
|
|
8250
|
+
data: JSON.stringify(data)
|
|
8251
|
+
}).catch((err) => {
|
|
8252
|
+
if (!stream.aborted) logger_default.error("[v1-events] write error:", logger_default.errorData(err));
|
|
8253
|
+
});
|
|
8254
|
+
});
|
|
8255
|
+
cleanups.push(unsubConv);
|
|
8256
|
+
};
|
|
8257
|
+
var subscribeToConversation = subscribeToConversation2;
|
|
7465
8258
|
let conversations2 = [];
|
|
7466
8259
|
try {
|
|
7467
8260
|
conversations2 = await listConversationsWithParticipants(user.id);
|
|
@@ -7524,19 +8317,46 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7524
8317
|
});
|
|
7525
8318
|
});
|
|
7526
8319
|
cleanups.push(unsubActivity);
|
|
8320
|
+
const subscribedConvIds = new Set(conversations2.map((c2) => c2.id));
|
|
7527
8321
|
for (const conv of conversations2) {
|
|
7528
|
-
|
|
8322
|
+
subscribeToConversation2(conv.id);
|
|
8323
|
+
}
|
|
8324
|
+
const unsubParticipant = subscribeParticipantAdded(({ conversationId, userId }) => {
|
|
8325
|
+
if (userId !== user.id || subscribedConvIds.has(conversationId)) return;
|
|
8326
|
+
subscribedConvIds.add(conversationId);
|
|
8327
|
+
function forward(event) {
|
|
7529
8328
|
const { seq, ...rest } = event;
|
|
7530
|
-
const data = { event: "conversation", conversationId
|
|
7531
|
-
stream.writeSSE({
|
|
7532
|
-
id: String(seq),
|
|
7533
|
-
data: JSON.stringify(data)
|
|
7534
|
-
}).catch((err) => {
|
|
8329
|
+
const data = { event: "conversation", conversationId, ...rest };
|
|
8330
|
+
stream.writeSSE({ id: String(seq), data: JSON.stringify(data) }).catch((err) => {
|
|
7535
8331
|
if (!stream.aborted) logger_default.error("[v1-events] write error:", logger_default.errorData(err));
|
|
7536
8332
|
});
|
|
8333
|
+
}
|
|
8334
|
+
let queued = [];
|
|
8335
|
+
const unsubConv = subscribe3(conversationId, (event) => {
|
|
8336
|
+
if (queued) queued.push(event);
|
|
8337
|
+
else forward(event);
|
|
7537
8338
|
});
|
|
7538
8339
|
cleanups.push(unsubConv);
|
|
7539
|
-
|
|
8340
|
+
getConversationWithParticipants(conversationId).then((conv) => {
|
|
8341
|
+
if (!conv) {
|
|
8342
|
+
logger_default.error("[v1-events] conversation_added: enrich returned null", { conversationId });
|
|
8343
|
+
return;
|
|
8344
|
+
}
|
|
8345
|
+
const data = {
|
|
8346
|
+
event: "conversation_added",
|
|
8347
|
+
conversation: conv
|
|
8348
|
+
};
|
|
8349
|
+
return stream.writeSSE({ id: String(nextEventId()), data: JSON.stringify(data) });
|
|
8350
|
+
}).catch((err) => {
|
|
8351
|
+
if (!stream.aborted)
|
|
8352
|
+
logger_default.error("[v1-events] conversation_added error:", logger_default.errorData(err));
|
|
8353
|
+
}).finally(() => {
|
|
8354
|
+
const pending = queued ?? [];
|
|
8355
|
+
queued = null;
|
|
8356
|
+
for (const event of pending) forward(event);
|
|
8357
|
+
});
|
|
8358
|
+
});
|
|
8359
|
+
cleanups.push(unsubParticipant);
|
|
7540
8360
|
const keepAlive = setInterval(() => {
|
|
7541
8361
|
stream.writeSSE({ data: "" }).catch((err) => {
|
|
7542
8362
|
if (!stream.aborted) logger_default.error("[v1-events] ping error:", logger_default.errorData(err));
|
|
@@ -7558,10 +8378,270 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7558
8378
|
});
|
|
7559
8379
|
var events_default = app20;
|
|
7560
8380
|
|
|
8381
|
+
// packages/daemon/src/web/api/v1/feed.ts
|
|
8382
|
+
import { Hono as Hono25 } from "hono";
|
|
8383
|
+
|
|
8384
|
+
// packages/daemon/src/lib/events/feed.ts
|
|
8385
|
+
import { and as and6, desc as desc5, eq as eq8, gte as gte3, inArray as inArray3, sql as sql5 } from "drizzle-orm";
|
|
8386
|
+
var BURST_GAP_MS = 45 * 6e4;
|
|
8387
|
+
var FEED_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
8388
|
+
var LIFECYCLE_TYPES = [
|
|
8389
|
+
"mind_started",
|
|
8390
|
+
"mind_stopped",
|
|
8391
|
+
"mind_sleeping",
|
|
8392
|
+
"mind_waking",
|
|
8393
|
+
"mind_error",
|
|
8394
|
+
"backup_failed"
|
|
8395
|
+
];
|
|
8396
|
+
var SNIPPET_MESSAGES = 3;
|
|
8397
|
+
var SNIPPET_CHARS = 280;
|
|
8398
|
+
var DIGEST_MIND = "system";
|
|
8399
|
+
function splitIntoBursts(rows, gapMs = BURST_GAP_MS) {
|
|
8400
|
+
const byConv = /* @__PURE__ */ new Map();
|
|
8401
|
+
for (const row of rows) {
|
|
8402
|
+
let arr = byConv.get(row.conversation_id);
|
|
8403
|
+
if (!arr) {
|
|
8404
|
+
arr = [];
|
|
8405
|
+
byConv.set(row.conversation_id, arr);
|
|
8406
|
+
}
|
|
8407
|
+
arr.push(row);
|
|
8408
|
+
}
|
|
8409
|
+
const bursts = [];
|
|
8410
|
+
for (const [conversationId, msgs] of byConv) {
|
|
8411
|
+
let current = [];
|
|
8412
|
+
let prevTime = 0;
|
|
8413
|
+
for (const msg of msgs) {
|
|
8414
|
+
const t = parseUtcDateTime(msg.created_at).getTime();
|
|
8415
|
+
if (current.length > 0 && t - prevTime > gapMs) {
|
|
8416
|
+
bursts.push(makeBurst(conversationId, current));
|
|
8417
|
+
current = [];
|
|
8418
|
+
}
|
|
8419
|
+
current.push(msg);
|
|
8420
|
+
prevTime = t;
|
|
8421
|
+
}
|
|
8422
|
+
if (current.length > 0) bursts.push(makeBurst(conversationId, current));
|
|
8423
|
+
}
|
|
8424
|
+
return bursts;
|
|
8425
|
+
}
|
|
8426
|
+
function makeBurst(conversationId, msgs) {
|
|
8427
|
+
return {
|
|
8428
|
+
conversationId,
|
|
8429
|
+
messages: msgs,
|
|
8430
|
+
startedAt: msgs[0].created_at,
|
|
8431
|
+
endedAt: msgs[msgs.length - 1].created_at,
|
|
8432
|
+
messageCount: msgs.length
|
|
8433
|
+
};
|
|
8434
|
+
}
|
|
8435
|
+
function extractText(raw) {
|
|
8436
|
+
try {
|
|
8437
|
+
const parsed = JSON.parse(raw);
|
|
8438
|
+
if (Array.isArray(parsed)) {
|
|
8439
|
+
const block = parsed.find((b) => b?.type === "text");
|
|
8440
|
+
return block && typeof block.text === "string" ? block.text : "";
|
|
8441
|
+
}
|
|
8442
|
+
return raw;
|
|
8443
|
+
} catch {
|
|
8444
|
+
return raw;
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
function truncate(text, max) {
|
|
8448
|
+
const t = text.trim();
|
|
8449
|
+
return t.length > max ? `${t.slice(0, max).trimEnd()}\u2026` : t;
|
|
8450
|
+
}
|
|
8451
|
+
async function getChatFeedEvents(opts) {
|
|
8452
|
+
const db = await getDb();
|
|
8453
|
+
const cutoff = utcDateTimeStr(new Date(Date.now() - FEED_WINDOW_MS));
|
|
8454
|
+
const rows = await db.select({
|
|
8455
|
+
id: messages.id,
|
|
8456
|
+
conversation_id: messages.conversation_id,
|
|
8457
|
+
role: messages.role,
|
|
8458
|
+
sender_name: messages.sender_name,
|
|
8459
|
+
content: messages.content,
|
|
8460
|
+
created_at: messages.created_at,
|
|
8461
|
+
conversation_type: conversations.type
|
|
8462
|
+
}).from(messages).innerJoin(conversations, eq8(conversations.id, messages.conversation_id)).where(and6(gte3(messages.created_at, cutoff), eq8(conversations.private, 0))).orderBy(desc5(messages.id)).limit(5e3);
|
|
8463
|
+
const ascending = rows.reverse();
|
|
8464
|
+
const bursts = splitIntoBursts(ascending);
|
|
8465
|
+
bursts.sort((a, b) => {
|
|
8466
|
+
const at = parseUtcDateTime(a.endedAt).getTime();
|
|
8467
|
+
const bt = parseUtcDateTime(b.endedAt).getTime();
|
|
8468
|
+
if (bt !== at) return bt - at;
|
|
8469
|
+
return lastId(b) - lastId(a);
|
|
8470
|
+
});
|
|
8471
|
+
const top = bursts.slice(0, opts?.limit ?? 50);
|
|
8472
|
+
if (top.length === 0) return [];
|
|
8473
|
+
const convIds = [...new Set(top.map((b) => b.conversationId))];
|
|
8474
|
+
const { participantsByConv, channelByConv } = await enrichBurstConversations(convIds);
|
|
8475
|
+
return top.map((burst) => {
|
|
8476
|
+
const snippetMsgs = burst.messages.slice(-SNIPPET_MESSAGES);
|
|
8477
|
+
const activeSenders = [
|
|
8478
|
+
...new Set(burst.messages.map((m) => m.sender_name).filter((s) => !!s))
|
|
8479
|
+
];
|
|
8480
|
+
return {
|
|
8481
|
+
kind: "chat",
|
|
8482
|
+
conversationId: burst.conversationId,
|
|
8483
|
+
conversationType: burst.messages[0].conversation_type === "channel" ? "channel" : "dm",
|
|
8484
|
+
channelName: channelByConv.get(burst.conversationId) ?? null,
|
|
8485
|
+
participants: participantsByConv.get(burst.conversationId) ?? [],
|
|
8486
|
+
activeSenders,
|
|
8487
|
+
messageCount: burst.messageCount,
|
|
8488
|
+
startedAt: burst.startedAt,
|
|
8489
|
+
endedAt: burst.endedAt,
|
|
8490
|
+
snippet: snippetMsgs.map((m) => ({
|
|
8491
|
+
role: m.role,
|
|
8492
|
+
senderName: m.sender_name,
|
|
8493
|
+
text: truncate(extractText(m.content), SNIPPET_CHARS),
|
|
8494
|
+
createdAt: m.created_at
|
|
8495
|
+
}))
|
|
8496
|
+
};
|
|
8497
|
+
});
|
|
8498
|
+
}
|
|
8499
|
+
function lastId(burst) {
|
|
8500
|
+
return burst.messages[burst.messages.length - 1].id;
|
|
8501
|
+
}
|
|
8502
|
+
async function enrichBurstConversations(convIds) {
|
|
8503
|
+
const db = await getDb();
|
|
8504
|
+
const rows = await db.select({
|
|
8505
|
+
conversationId: conversationParticipants.conversation_id,
|
|
8506
|
+
userId: users.id,
|
|
8507
|
+
username: users.username,
|
|
8508
|
+
userType: users.user_type,
|
|
8509
|
+
role: conversationParticipants.role,
|
|
8510
|
+
displayName: users.display_name,
|
|
8511
|
+
description: users.description,
|
|
8512
|
+
avatar: users.avatar,
|
|
8513
|
+
channelName: channels.name
|
|
8514
|
+
}).from(conversationParticipants).innerJoin(users, eq8(conversationParticipants.user_id, users.id)).leftJoin(channels, eq8(channels.conversation_id, conversationParticipants.conversation_id)).where(inArray3(conversationParticipants.conversation_id, convIds));
|
|
8515
|
+
const participantsByConv = /* @__PURE__ */ new Map();
|
|
8516
|
+
const channelByConv = /* @__PURE__ */ new Map();
|
|
8517
|
+
for (const r of rows) {
|
|
8518
|
+
let arr = participantsByConv.get(r.conversationId);
|
|
8519
|
+
if (!arr) {
|
|
8520
|
+
arr = [];
|
|
8521
|
+
participantsByConv.set(r.conversationId, arr);
|
|
8522
|
+
}
|
|
8523
|
+
if (!arr.some((p) => p.userId === r.userId)) {
|
|
8524
|
+
arr.push({
|
|
8525
|
+
userId: r.userId,
|
|
8526
|
+
username: r.username,
|
|
8527
|
+
userType: r.userType,
|
|
8528
|
+
role: r.role,
|
|
8529
|
+
displayName: r.displayName,
|
|
8530
|
+
description: r.description,
|
|
8531
|
+
avatar: r.avatar
|
|
8532
|
+
});
|
|
8533
|
+
}
|
|
8534
|
+
if (r.channelName && !channelByConv.has(r.conversationId)) {
|
|
8535
|
+
channelByConv.set(r.conversationId, r.channelName);
|
|
8536
|
+
}
|
|
8537
|
+
}
|
|
8538
|
+
return { participantsByConv, channelByConv };
|
|
8539
|
+
}
|
|
8540
|
+
async function getLifecycleFeedEvents(opts) {
|
|
8541
|
+
const db = await getDb();
|
|
8542
|
+
const cutoff = utcDateTimeStr(new Date(Date.now() - FEED_WINDOW_MS));
|
|
8543
|
+
const rows = await db.select({
|
|
8544
|
+
id: activity.id,
|
|
8545
|
+
type: activity.type,
|
|
8546
|
+
mind: activity.mind,
|
|
8547
|
+
summary: activity.summary,
|
|
8548
|
+
created_at: activity.created_at
|
|
8549
|
+
}).from(activity).where(and6(inArray3(activity.type, [...LIFECYCLE_TYPES]), gte3(activity.created_at, cutoff))).orderBy(desc5(activity.created_at), desc5(activity.id)).limit(opts.limit ?? 100);
|
|
8550
|
+
return rows.map((r) => ({
|
|
8551
|
+
kind: "lifecycle",
|
|
8552
|
+
id: r.id,
|
|
8553
|
+
type: r.type,
|
|
8554
|
+
mind: r.mind,
|
|
8555
|
+
summary: redactLifecycleSummary(r.type, r.summary, opts.privileged),
|
|
8556
|
+
createdAt: r.created_at
|
|
8557
|
+
}));
|
|
8558
|
+
}
|
|
8559
|
+
function redactLifecycleSummary(type, summary, privileged) {
|
|
8560
|
+
if (privileged) return summary;
|
|
8561
|
+
if (type === "mind_error") return "An error interrupted this mind.";
|
|
8562
|
+
if (type === "backup_failed") return "A scheduled backup failed.";
|
|
8563
|
+
return summary;
|
|
8564
|
+
}
|
|
8565
|
+
async function getDailyDigest(complete = aiCompleteUtility) {
|
|
8566
|
+
const db = await getDb();
|
|
8567
|
+
const periodKey = getPeriodKey(/* @__PURE__ */ new Date(), "day");
|
|
8568
|
+
const cached = await db.select({ content: summaries.content }).from(summaries).where(
|
|
8569
|
+
and6(
|
|
8570
|
+
eq8(summaries.mind, DIGEST_MIND),
|
|
8571
|
+
eq8(summaries.period, "day"),
|
|
8572
|
+
eq8(summaries.period_key, periodKey)
|
|
8573
|
+
)
|
|
8574
|
+
).get();
|
|
8575
|
+
if (cached) return { content: cached.content };
|
|
8576
|
+
const cutoff = utcDateTimeStr(new Date(Date.now() - 24 * 60 * 60 * 1e3));
|
|
8577
|
+
const hourRows = await db.select({ content: summaries.content }).from(summaries).where(
|
|
8578
|
+
and6(
|
|
8579
|
+
eq8(summaries.mind, SYSTEM_MIND),
|
|
8580
|
+
eq8(summaries.period, "hour"),
|
|
8581
|
+
gte3(summaries.created_at, cutoff)
|
|
8582
|
+
)
|
|
8583
|
+
).orderBy(summaries.period_key);
|
|
8584
|
+
let material = hourRows.map((r) => r.content).join("\n\n");
|
|
8585
|
+
if (!material.trim()) {
|
|
8586
|
+
const turnRows = await db.select({ mind: summaries.mind, content: summaries.content }).from(summaries).where(
|
|
8587
|
+
and6(
|
|
8588
|
+
eq8(summaries.period, "turn"),
|
|
8589
|
+
gte3(summaries.created_at, cutoff),
|
|
8590
|
+
sql5`${summaries.mind} != ${SYSTEM_MIND}`
|
|
8591
|
+
)
|
|
8592
|
+
).orderBy(desc5(summaries.created_at)).limit(40);
|
|
8593
|
+
material = turnRows.map((r) => `[${r.mind}] ${truncate(r.content, 300)}`).join("\n");
|
|
8594
|
+
}
|
|
8595
|
+
if (!material.trim()) return { content: "" };
|
|
8596
|
+
const aiResult = await complete(await getPrompt("system_daily_digest"), material);
|
|
8597
|
+
if (aiResult) {
|
|
8598
|
+
await db.insert(summaries).values({ mind: DIGEST_MIND, period: "day", period_key: periodKey, content: aiResult }).onConflictDoNothing();
|
|
8599
|
+
return { content: aiResult };
|
|
8600
|
+
}
|
|
8601
|
+
const counts = await digestCounts(cutoff);
|
|
8602
|
+
const mindWord = counts.minds === 1 ? "mind was" : "minds were";
|
|
8603
|
+
const convWord = counts.conversations === 1 ? "conversation" : "conversations";
|
|
8604
|
+
return {
|
|
8605
|
+
content: `${counts.minds} ${mindWord} active across ${counts.conversations} ${convWord} today.`
|
|
8606
|
+
};
|
|
8607
|
+
}
|
|
8608
|
+
async function digestCounts(cutoff) {
|
|
8609
|
+
const db = await getDb();
|
|
8610
|
+
const convRows = await db.select({ id: messages.conversation_id }).from(messages).where(gte3(messages.created_at, cutoff)).groupBy(messages.conversation_id);
|
|
8611
|
+
const mindRows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
8612
|
+
and6(
|
|
8613
|
+
eq8(summaries.period, "turn"),
|
|
8614
|
+
gte3(summaries.created_at, cutoff),
|
|
8615
|
+
sql5`${summaries.mind} != ${SYSTEM_MIND}`
|
|
8616
|
+
)
|
|
8617
|
+
).groupBy(summaries.mind);
|
|
8618
|
+
return { minds: mindRows.length, conversations: convRows.length };
|
|
8619
|
+
}
|
|
8620
|
+
|
|
8621
|
+
// packages/daemon/src/web/api/v1/feed.ts
|
|
8622
|
+
function eventTime(e) {
|
|
8623
|
+
return e.kind === "chat" ? e.endedAt : e.createdAt;
|
|
8624
|
+
}
|
|
8625
|
+
var app21 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
8626
|
+
const user = c.get("user");
|
|
8627
|
+
const privileged = user.role === "admin" || user.role === "system";
|
|
8628
|
+
const [chat, lifecycle] = await Promise.all([
|
|
8629
|
+
getChatFeedEvents(),
|
|
8630
|
+
getLifecycleFeedEvents({ privileged })
|
|
8631
|
+
]);
|
|
8632
|
+
const events = [...chat, ...lifecycle].sort(
|
|
8633
|
+
(a, b) => parseUtcDateTime(eventTime(b)).getTime() - parseUtcDateTime(eventTime(a)).getTime()
|
|
8634
|
+
);
|
|
8635
|
+
return c.json({ events });
|
|
8636
|
+
}).get("/digest", async (c) => {
|
|
8637
|
+
return c.json(await getDailyDigest());
|
|
8638
|
+
});
|
|
8639
|
+
var feed_default = app21;
|
|
8640
|
+
|
|
7561
8641
|
// packages/daemon/src/web/api/variants.ts
|
|
7562
8642
|
import { existsSync as existsSync10, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
|
|
7563
8643
|
import { resolve as resolve16 } from "path";
|
|
7564
|
-
import { Hono as
|
|
8644
|
+
import { Hono as Hono26 } from "hono";
|
|
7565
8645
|
|
|
7566
8646
|
// packages/daemon/src/lib/mind/farewell.ts
|
|
7567
8647
|
import { existsSync as existsSync9, readFileSync as readFileSync10, rmSync as rmSync5 } from "fs";
|
|
@@ -7585,14 +8665,14 @@ function readFarewell(variantDir) {
|
|
|
7585
8665
|
}
|
|
7586
8666
|
}
|
|
7587
8667
|
function waitForFarewell(name, variantDir, timeoutMs) {
|
|
7588
|
-
let
|
|
8668
|
+
let settle2;
|
|
7589
8669
|
const done = new Promise((resolvePromise) => {
|
|
7590
|
-
|
|
8670
|
+
settle2 = resolvePromise;
|
|
7591
8671
|
});
|
|
7592
8672
|
const cleanup = () => {
|
|
7593
8673
|
clearTimeout(timeout);
|
|
7594
8674
|
unsub();
|
|
7595
|
-
|
|
8675
|
+
settle2();
|
|
7596
8676
|
};
|
|
7597
8677
|
const timeout = setTimeout(cleanup, timeoutMs);
|
|
7598
8678
|
const unsub = subscribe((event) => {
|
|
@@ -7603,7 +8683,7 @@ function waitForFarewell(name, variantDir, timeoutMs) {
|
|
|
7603
8683
|
return { done, cancel: cleanup };
|
|
7604
8684
|
}
|
|
7605
8685
|
async function runFarewellTurn(opts) {
|
|
7606
|
-
const { variantName, parentName, variantDir,
|
|
8686
|
+
const { variantName, parentName, variantDir, running } = opts;
|
|
7607
8687
|
const timeoutMs = opts.timeoutMs ?? FAREWELL_TIMEOUT_MS;
|
|
7608
8688
|
if (!running) return readFarewell(variantDir);
|
|
7609
8689
|
try {
|
|
@@ -7615,35 +8695,12 @@ async function runFarewellTurn(opts) {
|
|
|
7615
8695
|
parent: parentName,
|
|
7616
8696
|
path: FAREWELL_MIND_PATH
|
|
7617
8697
|
});
|
|
7618
|
-
let conversationId;
|
|
7619
|
-
try {
|
|
7620
|
-
const result = await sendSystemMessageDirect(variantName, message);
|
|
7621
|
-
conversationId = result.conversationId;
|
|
7622
|
-
} catch (err) {
|
|
7623
|
-
flog.error(`failed to persist farewell message for ${variantName}`, logger_default.errorData(err));
|
|
7624
|
-
}
|
|
7625
8698
|
const waiter = waitForFarewell(variantName, variantDir, timeoutMs);
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
body: JSON.stringify({
|
|
7632
|
-
content: [{ type: "text", text: message }],
|
|
7633
|
-
channel: "@volute",
|
|
7634
|
-
sender: "volute",
|
|
7635
|
-
isDM: true,
|
|
7636
|
-
participants: ["volute", variantName],
|
|
7637
|
-
participantCount: 2,
|
|
7638
|
-
...conversationId ? { conversationId } : {}
|
|
7639
|
-
}),
|
|
7640
|
-
signal: AbortSignal.timeout(1e4)
|
|
7641
|
-
});
|
|
7642
|
-
if (res.ok) delivered = true;
|
|
7643
|
-
else flog.warn(`farewell delivery to ${variantName} returned HTTP ${res.status}`);
|
|
7644
|
-
} catch (err) {
|
|
7645
|
-
flog.warn(`failed to deliver farewell message to ${variantName}`, logger_default.errorData(err));
|
|
7646
|
-
}
|
|
8699
|
+
const { delivered } = await deliverEvent(variantName, {
|
|
8700
|
+
type: "lifecycle",
|
|
8701
|
+
body: message,
|
|
8702
|
+
meta: { subtype: "farewell", parent: parentName }
|
|
8703
|
+
});
|
|
7647
8704
|
if (delivered) {
|
|
7648
8705
|
await waiter.done;
|
|
7649
8706
|
} else {
|
|
@@ -7785,12 +8842,13 @@ async function establishVariantDialogue(parent, variant, purpose) {
|
|
|
7785
8842
|
await createConversation({ participantIds: [parentUser.id, variantUser.id] });
|
|
7786
8843
|
}
|
|
7787
8844
|
const purposeLine = purpose ? ` Its purpose: ${purpose}.` : "";
|
|
7788
|
-
await
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
8845
|
+
await deliverEvent(parent, {
|
|
8846
|
+
type: "lifecycle",
|
|
8847
|
+
meta: { subtype: "variant-created", variant },
|
|
8848
|
+
body: `You've split off a variant, ${variant} \u2014 a parallel version of you exploring on its own.${purposeLine} Reach it at @${variant} to check in on how the experiment is going, and merge its work back with \`volute mind join ${variant}\` when you're ready.`
|
|
8849
|
+
});
|
|
7792
8850
|
}
|
|
7793
|
-
var
|
|
8851
|
+
var app22 = new Hono26().get("/:name/variants", async (c) => {
|
|
7794
8852
|
const name = c.req.param("name");
|
|
7795
8853
|
const entry = await findMind(name);
|
|
7796
8854
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -7976,7 +9034,6 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
7976
9034
|
variantName,
|
|
7977
9035
|
parentName: mindName,
|
|
7978
9036
|
variantDir: variantEntry.dir,
|
|
7979
|
-
port: variantEntry.port,
|
|
7980
9037
|
running: tryGetMindManager()?.isRunning(variantName) ?? false
|
|
7981
9038
|
});
|
|
7982
9039
|
} catch (err) {
|
|
@@ -8046,7 +9103,7 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
8046
9103
|
if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
|
|
8047
9104
|
try {
|
|
8048
9105
|
const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-GSOUARW4.js");
|
|
8049
|
-
const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-
|
|
9106
|
+
const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-4UF6OCOE.js");
|
|
8050
9107
|
const tmpl = parentEntry.template ?? "claude";
|
|
8051
9108
|
await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
|
|
8052
9109
|
} catch (err) {
|
|
@@ -8110,11 +9167,11 @@ var app21 = new Hono25().get("/:name/variants", async (c) => {
|
|
|
8110
9167
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
|
|
8111
9168
|
return c.json({ ok: true });
|
|
8112
9169
|
});
|
|
8113
|
-
var variants_default =
|
|
9170
|
+
var variants_default = app22;
|
|
8114
9171
|
|
|
8115
9172
|
// packages/daemon/src/web/api/volute/channels.ts
|
|
8116
9173
|
import { zValidator as zValidator10 } from "@hono/zod-validator";
|
|
8117
|
-
import { Hono as
|
|
9174
|
+
import { Hono as Hono27 } from "hono";
|
|
8118
9175
|
import { z as z10 } from "zod";
|
|
8119
9176
|
var channelSettingsSchema = z10.object({
|
|
8120
9177
|
description: z10.string().nullable().optional(),
|
|
@@ -8129,7 +9186,7 @@ var createSchema2 = z10.object({
|
|
|
8129
9186
|
var inviteSchema = z10.object({
|
|
8130
9187
|
username: z10.string().min(1)
|
|
8131
9188
|
});
|
|
8132
|
-
var
|
|
9189
|
+
var app23 = new Hono27().get("/", async (c) => {
|
|
8133
9190
|
const user = c.get("user");
|
|
8134
9191
|
const channels2 = await listChannels();
|
|
8135
9192
|
const results = await Promise.all(
|
|
@@ -8251,11 +9308,11 @@ var app22 = new Hono26().get("/", async (c) => {
|
|
|
8251
9308
|
]);
|
|
8252
9309
|
return c.json({ ok: true });
|
|
8253
9310
|
});
|
|
8254
|
-
var channels_default2 =
|
|
9311
|
+
var channels_default2 = app23;
|
|
8255
9312
|
|
|
8256
9313
|
// packages/daemon/src/web/api/volute/chat.ts
|
|
8257
9314
|
import { zValidator as zValidator11 } from "@hono/zod-validator";
|
|
8258
|
-
import { Hono as
|
|
9315
|
+
import { Hono as Hono28 } from "hono";
|
|
8259
9316
|
import { streamSSE as streamSSE5 } from "hono/streaming";
|
|
8260
9317
|
import { z as z11 } from "zod";
|
|
8261
9318
|
var fileSchema = z11.object({
|
|
@@ -8302,7 +9359,25 @@ function stageFilesForMinds(files, targetMinds, senderName) {
|
|
|
8302
9359
|
}
|
|
8303
9360
|
return { notifications };
|
|
8304
9361
|
}
|
|
8305
|
-
var
|
|
9362
|
+
var spiritNoticeInFlight = /* @__PURE__ */ new Set();
|
|
9363
|
+
async function persistSpiritNotice(conversationId, spiritUsername, notice) {
|
|
9364
|
+
if (spiritNoticeInFlight.has(conversationId)) return;
|
|
9365
|
+
spiritNoticeInFlight.add(conversationId);
|
|
9366
|
+
try {
|
|
9367
|
+
const last = await getLastMessageBySender(conversationId, spiritUsername);
|
|
9368
|
+
const alreadyNoticed = last?.content.some(
|
|
9369
|
+
(b) => b.type === "text" && b.text.startsWith(SPIRIT_NOTICE_PREFIX)
|
|
9370
|
+
);
|
|
9371
|
+
if (!alreadyNoticed) {
|
|
9372
|
+
await addMessage(conversationId, "assistant", spiritUsername, [
|
|
9373
|
+
{ type: "text", text: notice }
|
|
9374
|
+
]);
|
|
9375
|
+
}
|
|
9376
|
+
} finally {
|
|
9377
|
+
spiritNoticeInFlight.delete(conversationId);
|
|
9378
|
+
}
|
|
9379
|
+
}
|
|
9380
|
+
var unifiedChatApp = new Hono28().post(
|
|
8306
9381
|
"/chat",
|
|
8307
9382
|
zValidator11("json", chatSchema),
|
|
8308
9383
|
async (c) => {
|
|
@@ -8334,7 +9409,7 @@ var unifiedChatApp = new Hono27().post(
|
|
|
8334
9409
|
if (user.id !== 0) {
|
|
8335
9410
|
participantIds.push(user.id);
|
|
8336
9411
|
} else if (body.sender) {
|
|
8337
|
-
if (body.sender
|
|
9412
|
+
if (isSpiritName(body.sender)) {
|
|
8338
9413
|
const systemUser = await getOrCreateSystemUser();
|
|
8339
9414
|
participantIds.push(systemUser.id);
|
|
8340
9415
|
} else {
|
|
@@ -8461,7 +9536,7 @@ var unifiedChatApp = new Hono27().post(
|
|
|
8461
9536
|
});
|
|
8462
9537
|
if (outboundTurnId) {
|
|
8463
9538
|
const mindKey = senderBase ?? senderName;
|
|
8464
|
-
|
|
9539
|
+
publish2(mindKey, {
|
|
8465
9540
|
mind: mindKey,
|
|
8466
9541
|
type: "outbound",
|
|
8467
9542
|
channel,
|
|
@@ -8473,6 +9548,30 @@ var unifiedChatApp = new Hono27().post(
|
|
|
8473
9548
|
logger_default.warn(`recordOutbound failed for ${senderName}`, logger_default.errorData(err));
|
|
8474
9549
|
}
|
|
8475
9550
|
}
|
|
9551
|
+
let spiritStatus;
|
|
9552
|
+
let spiritName;
|
|
9553
|
+
try {
|
|
9554
|
+
const mindishRecipients = participants.filter(
|
|
9555
|
+
(p) => (p.userType === "mind" || p.userType === "system") && p.username !== senderName
|
|
9556
|
+
);
|
|
9557
|
+
const spiritRecipient = conv.type === "dm" && mindishRecipients.length === 1 && mindishRecipients[0].userType === "system" ? mindishRecipients[0] : void 0;
|
|
9558
|
+
if (spiritRecipient) {
|
|
9559
|
+
const availability = await ensureSpiritAvailable();
|
|
9560
|
+
if (availability) {
|
|
9561
|
+
spiritStatus = availability.status;
|
|
9562
|
+
spiritName = spiritRecipient.username;
|
|
9563
|
+
if (availability.status === "unavailable" && availability.notice) {
|
|
9564
|
+
await persistSpiritNotice(
|
|
9565
|
+
conversationId,
|
|
9566
|
+
spiritRecipient.username,
|
|
9567
|
+
availability.notice
|
|
9568
|
+
);
|
|
9569
|
+
}
|
|
9570
|
+
}
|
|
9571
|
+
}
|
|
9572
|
+
} catch (err) {
|
|
9573
|
+
logger_default.error("spirit availability check failed \u2014 proceeding with delivery", logger_default.errorData(err));
|
|
9574
|
+
}
|
|
8476
9575
|
const isDM = conv.type === "dm";
|
|
8477
9576
|
await fanOutToMinds({
|
|
8478
9577
|
conversationId,
|
|
@@ -8484,29 +9583,17 @@ var unifiedChatApp = new Hono27().post(
|
|
|
8484
9583
|
// Variant-aware targeting: when targetMind is a variant, route to the variant name
|
|
8485
9584
|
targetName: baseName ? (username) => username === baseName ? variantName : username : void 0
|
|
8486
9585
|
});
|
|
8487
|
-
|
|
8488
|
-
if (shouldGenerateSystemFallback({
|
|
8489
|
-
senderIsMind: !!senderIsMind,
|
|
8490
|
-
hasMessage: !!body.message,
|
|
8491
|
-
convType: conv.type,
|
|
8492
|
-
participants,
|
|
8493
|
-
replyTarget: systemReplyTarget
|
|
8494
|
-
})) {
|
|
8495
|
-
generateSystemFallbackReply(conversationId, systemReplyTarget, body.message).catch(
|
|
8496
|
-
(err) => logger_default.error(`system reply generation failed for ${systemReplyTarget}`, logger_default.errorData(err))
|
|
8497
|
-
);
|
|
8498
|
-
}
|
|
8499
|
-
return c.json({ ok: true, conversationId, outboundId });
|
|
9586
|
+
return c.json({ ok: true, conversationId, outboundId, spirit: spiritStatus, spiritName });
|
|
8500
9587
|
}
|
|
8501
9588
|
);
|
|
8502
|
-
var
|
|
9589
|
+
var app24 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
|
|
8503
9590
|
const conversationId = c.req.param("id");
|
|
8504
9591
|
const user = c.get("user");
|
|
8505
9592
|
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
8506
9593
|
return c.json({ error: "Conversation not found" }, 404);
|
|
8507
9594
|
}
|
|
8508
9595
|
return streamSSE5(c, async (stream) => {
|
|
8509
|
-
const unsubscribe =
|
|
9596
|
+
const unsubscribe = subscribe3(conversationId, (event) => {
|
|
8510
9597
|
stream.writeSSE({ data: JSON.stringify(event) }).catch((err) => {
|
|
8511
9598
|
if (!stream.aborted) console.error("[chat] SSE write error:", err);
|
|
8512
9599
|
});
|
|
@@ -8525,17 +9612,17 @@ var app23 = new Hono27().get("/:name/conversations/:id/events", async (c) => {
|
|
|
8525
9612
|
});
|
|
8526
9613
|
});
|
|
8527
9614
|
});
|
|
8528
|
-
var chat_default =
|
|
9615
|
+
var chat_default = app24;
|
|
8529
9616
|
|
|
8530
9617
|
// packages/daemon/src/web/api/volute/conversations.ts
|
|
8531
9618
|
import { zValidator as zValidator12 } from "@hono/zod-validator";
|
|
8532
|
-
import { Hono as
|
|
9619
|
+
import { Hono as Hono29 } from "hono";
|
|
8533
9620
|
import { z as z12 } from "zod";
|
|
8534
9621
|
var createConvSchema = z12.object({
|
|
8535
9622
|
participantIds: z12.array(z12.number()).optional(),
|
|
8536
9623
|
participantNames: z12.array(z12.string()).optional()
|
|
8537
9624
|
});
|
|
8538
|
-
var
|
|
9625
|
+
var app25 = new Hono29().get("/:name/conversations", async (c) => {
|
|
8539
9626
|
const name = c.req.param("name");
|
|
8540
9627
|
const user = c.get("user");
|
|
8541
9628
|
let lookupId = user.id;
|
|
@@ -8616,7 +9703,7 @@ var app24 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
8616
9703
|
const beforeStr = c.req.query("before");
|
|
8617
9704
|
if (!limitStr && !beforeStr) {
|
|
8618
9705
|
const msgs = await getMessages(id);
|
|
8619
|
-
return c.json({ items: msgs, hasMore: false });
|
|
9706
|
+
return c.json({ items: await withSenderDisplayNames(msgs), hasMore: false });
|
|
8620
9707
|
}
|
|
8621
9708
|
const limit = limitStr ? parseInt(limitStr, 10) : void 0;
|
|
8622
9709
|
const before = beforeStr ? parseInt(beforeStr, 10) : void 0;
|
|
@@ -8624,7 +9711,10 @@ var app24 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
8624
9711
|
return c.json({ error: "Invalid pagination parameters" }, 400);
|
|
8625
9712
|
}
|
|
8626
9713
|
const result = await getMessagesPaginated(id, { before, limit });
|
|
8627
|
-
return c.json({
|
|
9714
|
+
return c.json({
|
|
9715
|
+
items: await withSenderDisplayNames(result.messages),
|
|
9716
|
+
hasMore: result.hasMore
|
|
9717
|
+
});
|
|
8628
9718
|
}).get("/:name/conversations/:id/participants", async (c) => {
|
|
8629
9719
|
const id = c.req.param("id");
|
|
8630
9720
|
const user = c.get("user");
|
|
@@ -8640,12 +9730,12 @@ var app24 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
8640
9730
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
8641
9731
|
return c.json({ ok: true });
|
|
8642
9732
|
});
|
|
8643
|
-
var conversations_default2 =
|
|
9733
|
+
var conversations_default2 = app25;
|
|
8644
9734
|
|
|
8645
9735
|
// packages/daemon/src/web/app.ts
|
|
8646
9736
|
var httpLog = logger_default.child("http");
|
|
8647
|
-
var
|
|
8648
|
-
|
|
9737
|
+
var app26 = new Hono30();
|
|
9738
|
+
app26.onError((err, c) => {
|
|
8649
9739
|
if (err instanceof HTTPException) {
|
|
8650
9740
|
return err.getResponse();
|
|
8651
9741
|
}
|
|
@@ -8656,10 +9746,10 @@ app25.onError((err, c) => {
|
|
|
8656
9746
|
});
|
|
8657
9747
|
return c.json({ error: "Internal server error" }, 500);
|
|
8658
9748
|
});
|
|
8659
|
-
|
|
9749
|
+
app26.notFound((c) => {
|
|
8660
9750
|
return c.json({ error: "Not found" }, 404);
|
|
8661
9751
|
});
|
|
8662
|
-
|
|
9752
|
+
app26.use("*", async (c, next) => {
|
|
8663
9753
|
const start = Date.now();
|
|
8664
9754
|
await next();
|
|
8665
9755
|
const duration = Date.now() - start;
|
|
@@ -8670,23 +9760,23 @@ app25.use("*", async (c, next) => {
|
|
|
8670
9760
|
httpLog.debug("request", data);
|
|
8671
9761
|
}
|
|
8672
9762
|
});
|
|
8673
|
-
|
|
8674
|
-
|
|
9763
|
+
app26.use("/api/*", bodyLimit({ maxSize: 10 * 1024 * 1024 }));
|
|
9764
|
+
app26.use(
|
|
8675
9765
|
"/api/*",
|
|
8676
9766
|
cors({
|
|
8677
9767
|
origin: (origin) => origin,
|
|
8678
|
-
allowHeaders: ["Authorization", "Content-Type", "X-Volute-
|
|
9768
|
+
allowHeaders: ["Authorization", "Content-Type", "X-Volute-Thread"],
|
|
8679
9769
|
allowMethods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
8680
9770
|
credentials: false
|
|
8681
9771
|
})
|
|
8682
9772
|
);
|
|
8683
9773
|
var csrfMiddleware = csrf();
|
|
8684
|
-
|
|
9774
|
+
app26.use("/api/*", async (c, next) => {
|
|
8685
9775
|
const auth = c.req.header("Authorization");
|
|
8686
9776
|
if (auth?.startsWith("Bearer ") && auth.length > 7) return next();
|
|
8687
9777
|
return csrfMiddleware(c, next);
|
|
8688
9778
|
});
|
|
8689
|
-
|
|
9779
|
+
app26.get("/api/health", (c) => {
|
|
8690
9780
|
let version = "unknown";
|
|
8691
9781
|
let cached = null;
|
|
8692
9782
|
try {
|
|
@@ -8701,43 +9791,43 @@ app25.get("/api/health", (c) => {
|
|
|
8701
9791
|
...cached?.updateAvailable ? { updateAvailable: true, latest: cached.latest } : {}
|
|
8702
9792
|
});
|
|
8703
9793
|
});
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
var routes =
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
var app_default =
|
|
9794
|
+
app26.use("/api/activity/*", authMiddleware);
|
|
9795
|
+
app26.use("/api/minds/*", authMiddleware);
|
|
9796
|
+
app26.use("/api/conversations/*", authMiddleware);
|
|
9797
|
+
app26.use("/api/system/*", authMiddleware);
|
|
9798
|
+
app26.use("/api/env/*", authMiddleware);
|
|
9799
|
+
app26.use("/api/prompts/*", authMiddleware);
|
|
9800
|
+
app26.use("/api/skills/*", authMiddleware);
|
|
9801
|
+
app26.use("/api/extensions/*", authMiddleware);
|
|
9802
|
+
app26.use("/api/bridges/*", authMiddleware);
|
|
9803
|
+
app26.use("/api/config/*", authMiddleware);
|
|
9804
|
+
app26.use("/api/backup/*", authMiddleware);
|
|
9805
|
+
app26.use("/api/v1/*", authMiddleware);
|
|
9806
|
+
app26.route("/api/setup", setup_default);
|
|
9807
|
+
app26.route("/api/config", config_default);
|
|
9808
|
+
var routes = app26.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/backup", backup_default).route("/api/minds", minds_default).route("/api/minds", chat_default).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/bridges", bridges_default).route("/api/extensions", extensions_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1/feed", feed_default).route("/api/v1", unifiedChatApp).route("/api/v1/channels", channels_default2).route("/api/v1/history", history_default);
|
|
9809
|
+
app26.route("/api/v1/minds", minds_default);
|
|
9810
|
+
app26.route("/api/v1/minds", chat_default);
|
|
9811
|
+
app26.route("/api/v1/minds", typing_default);
|
|
9812
|
+
app26.route("/api/v1/minds", variants_default);
|
|
9813
|
+
app26.route("/api/v1/minds", files_default);
|
|
9814
|
+
app26.route("/api/v1/minds", env_default);
|
|
9815
|
+
app26.route("/api/v1/minds", mind_skills_default);
|
|
9816
|
+
app26.route("/api/v1/minds", schedules_default);
|
|
9817
|
+
app26.route("/api/v1/minds", logs_default);
|
|
9818
|
+
app26.route("/api/v1/system", system_default);
|
|
9819
|
+
app26.route("/api/v1/system", update_default);
|
|
9820
|
+
app26.route("/api/v1/prompts", prompts_default);
|
|
9821
|
+
app26.route("/api/v1/skills", skills_default);
|
|
9822
|
+
app26.route("/api/v1/env", sharedEnvApp);
|
|
9823
|
+
app26.route("/api/conversations", conversations_default);
|
|
9824
|
+
var app_default = app26;
|
|
8735
9825
|
|
|
8736
9826
|
// packages/daemon/src/web/server.ts
|
|
8737
9827
|
import { existsSync as existsSync11 } from "fs";
|
|
8738
9828
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
8739
9829
|
import { createServer as createHttpsServer } from "https";
|
|
8740
|
-
import { dirname as
|
|
9830
|
+
import { dirname as dirname3, extname as extname3, resolve as resolve17 } from "path";
|
|
8741
9831
|
import { serve } from "@hono/node-server";
|
|
8742
9832
|
var MIME_TYPES2 = {
|
|
8743
9833
|
".html": "text/html",
|
|
@@ -8754,14 +9844,14 @@ async function startServer({
|
|
|
8754
9844
|
tls
|
|
8755
9845
|
}) {
|
|
8756
9846
|
let assetsDir = "";
|
|
8757
|
-
let searchDir =
|
|
9847
|
+
let searchDir = dirname3(new URL(import.meta.url).pathname);
|
|
8758
9848
|
for (let i = 0; i < 5; i++) {
|
|
8759
9849
|
const candidate = resolve17(searchDir, "dist", "web-assets");
|
|
8760
9850
|
if (existsSync11(candidate)) {
|
|
8761
9851
|
assetsDir = candidate;
|
|
8762
9852
|
break;
|
|
8763
9853
|
}
|
|
8764
|
-
searchDir =
|
|
9854
|
+
searchDir = dirname3(searchDir);
|
|
8765
9855
|
}
|
|
8766
9856
|
if (assetsDir) {
|
|
8767
9857
|
app_default.get("*", async (c) => {
|
|
@@ -8873,27 +9963,33 @@ async function startDaemon(opts) {
|
|
|
8873
9963
|
const DAEMON_TOKEN_PATH = resolve18(systemDir, "daemon-token");
|
|
8874
9964
|
mkdirSync10(home, { recursive: true });
|
|
8875
9965
|
ensureSystemDir();
|
|
8876
|
-
const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-
|
|
9966
|
+
const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-CAJFOR2N.js");
|
|
8877
9967
|
migrateConfigSecrets();
|
|
8878
9968
|
migrateSetupCompleted();
|
|
8879
|
-
const { migrateAiModelQualification } = await import("./ai-service-
|
|
9969
|
+
const { migrateAiModelQualification } = await import("./ai-service-V4CTNGI3.js");
|
|
8880
9970
|
migrateAiModelQualification();
|
|
8881
|
-
await (await import("./db-
|
|
9971
|
+
await (await import("./db-QAY45Z56.js")).getDb();
|
|
8882
9972
|
try {
|
|
8883
|
-
const { eq:
|
|
8884
|
-
const { users: users2 } = await import("./schema-
|
|
8885
|
-
const db = await (await import("./db-
|
|
8886
|
-
await db.update(users2).set({ role: "system" }).where(
|
|
9973
|
+
const { eq: eq9, and: and7 } = await import("drizzle-orm");
|
|
9974
|
+
const { users: users2 } = await import("./schema-C2GIPD4V.js");
|
|
9975
|
+
const db = await (await import("./db-QAY45Z56.js")).getDb();
|
|
9976
|
+
await db.update(users2).set({ role: "system" }).where(and7(eq9(users2.user_type, "system"), eq9(users2.role, "user")));
|
|
8887
9977
|
} catch (err) {
|
|
8888
9978
|
logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
|
|
8889
9979
|
}
|
|
8890
9980
|
try {
|
|
8891
|
-
const { migrateAvatarSizes } = await import("./avatar-image-
|
|
9981
|
+
const { migrateAvatarSizes } = await import("./avatar-image-QEPAP3A5.js");
|
|
8892
9982
|
await migrateAvatarSizes();
|
|
8893
9983
|
} catch (err) {
|
|
8894
9984
|
logger_default.warn("avatar size migration failed", logger_default.errorData(err));
|
|
8895
9985
|
}
|
|
8896
|
-
|
|
9986
|
+
try {
|
|
9987
|
+
const { migrateThreadConfigs } = await import("./migrate-thread-config-4A5LW36Z.js");
|
|
9988
|
+
await migrateThreadConfigs();
|
|
9989
|
+
} catch (err) {
|
|
9990
|
+
logger_default.warn("session\u2192thread config migration failed", logger_default.errorData(err));
|
|
9991
|
+
}
|
|
9992
|
+
const { initSandbox } = await import("./sandbox-EGEU5GGE.js");
|
|
8897
9993
|
await initSandbox();
|
|
8898
9994
|
try {
|
|
8899
9995
|
await syncBuiltinSkills();
|
|
@@ -8925,7 +10021,7 @@ async function startDaemon(opts) {
|
|
|
8925
10021
|
logger_default.warn("failed to backfill minds into #system", logger_default.errorData(err));
|
|
8926
10022
|
}
|
|
8927
10023
|
try {
|
|
8928
|
-
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-
|
|
10024
|
+
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-DT7GY7EZ.js");
|
|
8929
10025
|
await getOrCreateSystemUser2();
|
|
8930
10026
|
} catch (err) {
|
|
8931
10027
|
logger_default.warn(
|
|
@@ -8934,12 +10030,12 @@ async function startDaemon(opts) {
|
|
|
8934
10030
|
);
|
|
8935
10031
|
}
|
|
8936
10032
|
const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes3(32).toString("hex");
|
|
8937
|
-
const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-
|
|
10033
|
+
const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-CAJFOR2N.js");
|
|
8938
10034
|
const globalCfg = readGlobalConfig2();
|
|
8939
10035
|
let tls;
|
|
8940
10036
|
if (opts.tailscale || globalCfg.tailscale) {
|
|
8941
10037
|
try {
|
|
8942
|
-
const { getTailscaleTls } = await import("./tailscale-
|
|
10038
|
+
const { getTailscaleTls } = await import("./tailscale-SJUW2ETR.js");
|
|
8943
10039
|
const tlsConfig = await getTailscaleTls();
|
|
8944
10040
|
tls = { key: tlsConfig.key, cert: tlsConfig.cert };
|
|
8945
10041
|
logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
|
|
@@ -8974,6 +10070,8 @@ async function startDaemon(opts) {
|
|
|
8974
10070
|
const delivery = initDeliveryManager();
|
|
8975
10071
|
const manager = initMindManager();
|
|
8976
10072
|
manager.loadCrashAttempts();
|
|
10073
|
+
const { registerXaiOAuthProvider } = await import("./xai-P42CRCZA.js");
|
|
10074
|
+
registerXaiOAuthProvider();
|
|
8977
10075
|
const bridgeManager = initBridgeManager();
|
|
8978
10076
|
const scheduler = initScheduler();
|
|
8979
10077
|
scheduler.start();
|
|
@@ -8991,7 +10089,7 @@ async function startDaemon(opts) {
|
|
|
8991
10089
|
const orphanedTurns = await completeOrphanedTurns();
|
|
8992
10090
|
summarizeOrphanedTurns(orphanedTurns);
|
|
8993
10091
|
try {
|
|
8994
|
-
const { reconcileVariants } = await import("./variant-cleanup-
|
|
10092
|
+
const { reconcileVariants } = await import("./variant-cleanup-HJ7Z75R6.js");
|
|
8995
10093
|
await reconcileVariants();
|
|
8996
10094
|
} catch (err) {
|
|
8997
10095
|
logger_default.error("failed to reconcile variants", logger_default.errorData(err));
|
|
@@ -9025,31 +10123,29 @@ async function startDaemon(opts) {
|
|
|
9025
10123
|
await Promise.all(workers);
|
|
9026
10124
|
}
|
|
9027
10125
|
try {
|
|
9028
|
-
const { isSetupComplete: isSetupComplete2 } = await import("./setup-
|
|
10126
|
+
const { isSetupComplete: isSetupComplete2, getSpiritName: getSpiritName2 } = await import("./setup-CAJFOR2N.js");
|
|
9029
10127
|
if (isSetupComplete2()) {
|
|
9030
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
9031
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
10128
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-IL5EMWYZ.js");
|
|
10129
|
+
const { startSpiritFull } = await import("./mind-service-5TA7UJAT.js");
|
|
9032
10130
|
await ensureSpiritProject();
|
|
9033
|
-
const
|
|
10131
|
+
const spiritName = getSpiritName2();
|
|
10132
|
+
const spiritEntry = await findMind(spiritName);
|
|
9034
10133
|
if (spiritEntry?.dir) {
|
|
9035
|
-
const { registerMindDir } = await import("./delivery-router-
|
|
9036
|
-
registerMindDir(
|
|
10134
|
+
const { registerMindDir } = await import("./delivery-router-JE7SHYNU.js");
|
|
10135
|
+
registerMindDir(spiritName, spiritEntry.dir);
|
|
9037
10136
|
}
|
|
9038
10137
|
await syncSpiritTemplate();
|
|
9039
|
-
if (spiritEntry && !manager.isRunning(
|
|
9040
|
-
await startSpiritFull(
|
|
10138
|
+
if (spiritEntry && !manager.isRunning(spiritName)) {
|
|
10139
|
+
await startSpiritFull(spiritName);
|
|
9041
10140
|
}
|
|
9042
10141
|
}
|
|
9043
10142
|
} catch (err) {
|
|
9044
|
-
logger_default.warn(
|
|
9045
|
-
"failed to start system spirit \u2014 system replies will use aiComplete fallback",
|
|
9046
|
-
logger_default.errorData(err)
|
|
9047
|
-
);
|
|
10143
|
+
logger_default.warn("failed to start system spirit", logger_default.errorData(err));
|
|
9048
10144
|
}
|
|
9049
10145
|
bridgeManager.startBridges(daemonPort).catch((err) => {
|
|
9050
10146
|
logger_default.warn("failed to start bridges", logger_default.errorData(err));
|
|
9051
10147
|
});
|
|
9052
|
-
import("./cloud-sync-
|
|
10148
|
+
import("./cloud-sync-IDJFVDEP.js").then(
|
|
9053
10149
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
9054
10150
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
9055
10151
|
})
|
|
@@ -9057,7 +10153,7 @@ async function startDaemon(opts) {
|
|
|
9057
10153
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
9058
10154
|
});
|
|
9059
10155
|
try {
|
|
9060
|
-
const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-
|
|
10156
|
+
const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-7BUHWZNT.js");
|
|
9061
10157
|
backfillTemplateHashes().then(() => warnStaleTemplates()).catch((err) => {
|
|
9062
10158
|
logger_default.warn("failed to check template staleness", logger_default.errorData(err));
|
|
9063
10159
|
});
|