volute 0.47.0 → 0.49.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 +82 -331
- package/dist/{accept-YAFP5JFJ.js → accept-KYNC5X3U.js} +3 -3
- package/dist/{activity-events-MIOK3IOU.js → activity-events-GJJAI2WT.js} +4 -4
- package/dist/{ai-service-CVZEYKTK.js → ai-service-A7FS7EC3.js} +9 -5
- package/dist/{archive-6Z4GFQPH.js → archive-7E3YE6VF.js} +3 -3
- package/dist/{auth-A4VGHADI.js → auth-ABYLOEZ7.js} +5 -5
- package/dist/{avatar-image-7S3WA77A.js → avatar-image-6ATV2IOZ.js} +6 -5
- package/dist/{backup-IIAFOW2L.js → backup-XYAMT4CU.js} +15 -15
- package/dist/{bridge-TIZGSXOD.js → bridge-3FNSWLNQ.js} +3 -3
- package/dist/{channels-ITE4YC57.js → channels-SGQZHNQJ.js} +3 -3
- package/dist/{chat-AJLJCQ3M.js → chat-OGXAEXMV.js} +9 -9
- package/dist/{chunk-LTLFOSA4.js → chunk-3B427GUK.js} +3 -3
- package/dist/{chunk-RUFLGTOT.js → chunk-4B62E4CG.js} +2 -2
- package/dist/{chunk-ODDGGLM6.js → chunk-4DSGN4FL.js} +3 -3
- package/dist/{chunk-BGYHLR54.js → chunk-6HGEZJDH.js} +2 -2
- package/dist/{chunk-RBNMDAKB.js → chunk-BCG45AQB.js} +15 -1
- package/dist/{chunk-5IERLDUS.js → chunk-DAIIMBLB.js} +26 -6
- package/dist/{chunk-DGGZVZTM.js → chunk-DVN6DNMI.js} +1 -1
- package/dist/chunk-DW4TRDOZ.js +29 -0
- package/dist/chunk-EADX54RG.js +113 -0
- package/dist/{chunk-EIQDZC43.js → chunk-EFT2OALG.js} +6 -2
- package/dist/{chunk-XHN6XBWV.js → chunk-EG5LSKHU.js} +2 -2
- package/dist/{chunk-VMCZBXF2.js → chunk-FBBS4BVR.js} +10 -34
- package/dist/{chunk-IWJSNMSY.js → chunk-GWVFDUNB.js} +45 -14
- package/dist/{chunk-CBOIHNMR.js → chunk-HCYQRME2.js} +41 -5
- package/dist/{chunk-4I72V3E5.js → chunk-IH5TKXX6.js} +15 -15
- package/dist/{chunk-5NN2VHJV.js → chunk-JBIFXB6I.js} +4 -4
- package/dist/{chunk-JSVHH32K.js → chunk-JCBPU57A.js} +15 -13
- package/dist/{chunk-M3XDVS5Q.js → chunk-JCWB2ITW.js} +2 -2
- package/dist/{chunk-JCMGDCGX.js → chunk-JPGHSXSV.js} +20 -25
- package/dist/{chunk-K23P5Q5N.js → chunk-K3JOLGWF.js} +30 -8
- package/dist/{chunk-3YUBFTDN.js → chunk-KHCKZ36O.js} +1 -1
- package/dist/{chunk-7KRIHYLR.js → chunk-L27YYONJ.js} +2 -2
- package/dist/{chunk-SZJCJVXV.js → chunk-MG3Z34YE.js} +2 -2
- package/dist/{chunk-SKNXMUIU.js → chunk-NJHN6IRB.js} +26 -2
- package/dist/{chunk-2OIYNHWO.js → chunk-NOIAHMJB.js} +1 -1
- package/dist/{chunk-2MJ7EOMT.js → chunk-P4LHVRNE.js} +1 -0
- package/dist/{chunk-IYG4FBMP.js → chunk-PY24ZL62.js} +1 -1
- package/dist/{chunk-F55GRIEK.js → chunk-QQBOI3QA.js} +356 -296
- package/dist/{chunk-T2TP6ZC6.js → chunk-QUSMYGNQ.js} +6 -0
- package/dist/{chunk-KDLYIECM.js → chunk-R772ET2M.js} +18 -4
- package/dist/{chunk-WW6C3PLV.js → chunk-RSBL3FAD.js} +39 -6
- package/dist/{mind-list-KEGRGQEQ.js → chunk-SHKM5RGD.js} +19 -6
- package/dist/{chunk-YKA7DCVW.js → chunk-SX72DOO3.js} +1 -1
- package/dist/chunk-TXJL36ZQ.js +48 -0
- package/dist/{chunk-M6TTZRGH.js → chunk-VIUH5CHQ.js} +72 -24
- package/dist/{chunk-HPGN6XRT.js → chunk-VNRSXEBF.js} +4 -4
- package/dist/{chunk-PFZBNKUH.js → chunk-WCFSBH33.js} +0 -1
- package/dist/chunk-XTEKINWG.js +158 -0
- package/dist/{chunk-LWDTP6DH.js → chunk-YIJGKHIS.js} +32 -2
- package/dist/{chunk-MTRMQFCQ.js → chunk-ZX46PGCC.js} +2 -2
- package/dist/cli.js +34 -26
- package/dist/{clock-E6N5Y3YW.js → clock-6JZL2FS6.js} +3 -3
- package/dist/{cloud-sync-LQTVDTQ3.js → cloud-sync-VFDTNNCA.js} +22 -20
- package/dist/{config-TJLYUV3U.js → config-RPSLA2HX.js} +3 -3
- package/dist/{conversations-C7IKYSHD.js → conversations-TUM5PJJ4.js} +5 -5
- package/dist/{create-A7PDHIIL.js → create-VIQVP3QB.js} +3 -3
- package/dist/{create-PQQXKTJK.js → create-Z5FSDNVZ.js} +16 -5
- package/dist/{daemon-client-IYR4KCPU.js → daemon-client-ZFY5MOAL.js} +5 -3
- package/dist/{daemon-restart-5F3YTLZK.js → daemon-restart-54L4B6AQ.js} +7 -7
- package/dist/daemon.js +1105 -601
- package/dist/{db-FO6LXYCW.js → db-F6UMFRYD.js} +2 -2
- package/dist/{db-LZTLA7XC.js → db-IPXRJUI7.js} +1 -1
- package/dist/{delete-HPLC7ETU.js → delete-ESEP7NGQ.js} +1 -1
- package/dist/delivery-manager-RBXBPVWM.js +36 -0
- package/dist/{delivery-router-3QFG2ZEX.js → delivery-router-VUOL5YSA.js} +8 -6
- package/dist/{down-ZI4S6265.js → down-6X7KX7BZ.js} +5 -5
- package/dist/echo-text-B6NZ5FGE.js +36 -0
- package/dist/env-KUL4XKN2.js +18 -0
- package/dist/{env-HRL2PWAT.js → env-ZYMSCB7B.js} +6 -6
- package/dist/{exec-QIFYZG4C.js → exec-FBXA3CM4.js} +3 -3
- package/dist/{export-4GVGNW54.js → export-TNBPDH77.js} +4 -4
- package/dist/{extension-QBY4C44L.js → extension-J4ZQ2GHV.js} +3 -3
- package/dist/{extensions-PIMIQ7J3.js → extensions-AWNO3SZF.js} +22 -20
- package/dist/{files-FEFZKMNM.js → files-BVBRJJ2P.js} +6 -6
- package/dist/{import-7MO3YTRE.js → import-ZUNKMCKC.js} +9 -9
- package/dist/{isolation-MCT4ZYLT.js → isolation-OPCZQRTH.js} +3 -3
- package/dist/{join-ZJV7W6MO.js → join-34TR2GXQ.js} +1 -1
- package/dist/{list-LQO2DCIK.js → list-PMUXZNAM.js} +3 -3
- package/dist/{login-OF4WXDAN.js → login-KAKNFCW5.js} +14 -5
- package/dist/{login-6TLMI2TQ.js → login-LKNLCJPA.js} +3 -3
- package/dist/{logout-ERE2BGA3.js → logout-RLT62RQP.js} +5 -5
- package/dist/{logout-BK53HXNY.js → logout-YZRMZ3O2.js} +3 -3
- package/dist/message-delivery-ABPV76EA.js +42 -0
- package/dist/{mind-POOHFZHP.js → mind-IF6CGMFW.js} +23 -19
- package/dist/{mind-activity-tracker-YVZPC4A5.js → mind-activity-tracker-YMMERB6B.js} +5 -5
- package/dist/mind-contacts-BKUFPUCU.js +64 -0
- package/dist/{mind-history-47DN6ILW.js → mind-history-WI5OI3BW.js} +5 -5
- package/dist/mind-list-OTHV4ZO4.js +13 -0
- package/dist/{mind-profile-PEQYASQX.js → mind-profile-LUG353ZT.js} +1 -1
- package/dist/mind-service-42GDO4KH.js +44 -0
- package/dist/{mind-sleep-IN3WPA76.js → mind-sleep-VBV7YRSU.js} +5 -5
- package/dist/{mind-status-SMUWJKGV.js → mind-status-BJNPGXKC.js} +8 -3
- package/dist/{mind-wake-R54BPESY.js → mind-wake-LVTZ55EZ.js} +3 -3
- package/dist/notices-N6BCGHFV.js +25 -0
- package/dist/{package-WPEE2HVJ.js → package-PD67VPHB.js} +8 -5
- package/dist/prompts-MDHDLWEX.js +24 -0
- package/dist/{read-PWAJOQIQ.js → read-YSWUXYL7.js} +3 -3
- package/dist/{register-7SZJFHU6.js → register-TWQDKQRT.js} +3 -3
- package/dist/{registry-UMMDRPRM.js → registry-3ELJPUGJ.js} +6 -2
- package/dist/{reject-6VH7N2A4.js → reject-SXGCYN4A.js} +3 -3
- package/dist/{restart-H5WJDMA7.js → restart-5F5QDBOG.js} +5 -5
- package/dist/{sandbox-G4X4K3UN.js → sandbox-XPYA7UIF.js} +5 -5
- package/dist/scheduler-K7Y7Z7FQ.js +34 -0
- package/dist/{schema-VEVO7HG6.js → schema-6YONVTKZ.js} +1 -1
- package/dist/{seed-VANPU43L.js → seed-NQED7PSX.js} +1 -1
- package/dist/{seed-check-VLTVILOV.js → seed-check-TYD7RRZY.js} +1 -1
- package/dist/{seed-cmd-SYVDZKM4.js → seed-cmd-QF3F2G4S.js} +3 -3
- package/dist/{seed-create-3FGZPS6C.js → seed-create-XOO4N2OE.js} +6 -4
- package/dist/seed-readiness-3LRYASA4.js +18 -0
- package/dist/{seed-sprout-H7QLFPFM.js → seed-sprout-HSBBGSS2.js} +32 -33
- package/dist/{send-HYDFGC6G.js → send-IHRJYNXA.js} +8 -8
- package/dist/{service-SYJ5G6LP.js → service-YCOPCTSZ.js} +4 -4
- package/dist/{service-install-FQ43Z4DF.js → service-install-Y46OTF55.js} +5 -5
- package/dist/{setup-QJHGMGSN.js → setup-F4TE64YI.js} +7 -7
- package/dist/{setup-KFYVWTXG.js → setup-TCN7VH5U.js} +9 -3
- package/dist/{skill-GUKG2YVH.js → skill-4CYJLYVY.js} +6 -6
- package/dist/skills/dreaming/SKILL.md +5 -1
- package/dist/skills/dreaming/scripts/dream.ts +5 -1
- package/dist/skills/orientation/SKILL.md +8 -7
- package/dist/skills/tending/SKILL.md +51 -2
- package/dist/skills/volute-mind/SKILL.md +5 -3
- package/dist/skills/volute-mind/references/routing.md +6 -5
- package/dist/skills/volute-mind/references/variants.md +48 -14
- package/dist/{skills-L2CMTW7A.js → skills-NN5UZCUU.js} +8 -6
- package/dist/sleep-manager-DSPXFHGX.js +38 -0
- package/dist/spirit-SSJMYC6A.js +31 -0
- package/dist/{split-WBMZXBHF.js → split-JF2VWTXA.js} +5 -2
- package/dist/{sprout-LTL5P36T.js → sprout-AXA2SECQ.js} +1 -1
- package/dist/{src-OJHARHTF.js → src-ZN4LXMPI.js} +17 -8
- package/dist/{start-X46I3GXB.js → start-BTVMTP3K.js} +5 -5
- package/dist/{status-V4RJMHJ4.js → status-VJGXMQ4L.js} +10 -8
- package/dist/{stop-HJQV4YSM.js → stop-YORESK4U.js} +5 -5
- package/dist/system-chat-M2XOBKEM.js +40 -0
- package/dist/{systems-B5YJKMW4.js → systems-5MH2ELQE.js} +6 -6
- package/dist/{tailscale-MCLY5GYE.js → tailscale-AMMVKDXB.js} +3 -3
- package/dist/{template-staleness-EMQORSGG.js → template-staleness-HKGCTS3H.js} +4 -4
- package/dist/{turn-tracker-JZRZDFWV.js → turn-tracker-JQ2YUUY2.js} +7 -7
- package/dist/up-SDLTLPAO.js +20 -0
- package/dist/{update-ACV4SAY3.js → update-WEA4N2TR.js} +5 -5
- package/dist/{update-check-266R4ZSY.js → update-check-DZOTONII.js} +3 -3
- package/dist/{upgrade-NWAZ7MLD.js → upgrade-K5FIQPQD.js} +1 -1
- package/dist/variant-cleanup-P4MBNLJD.js +33 -0
- package/dist/{version-notify-XRGTEEDK.js → version-notify-WFA5XB5A.js} +27 -25
- package/dist/web-assets/assets/index-BostMFoR.css +1 -0
- package/dist/web-assets/assets/index-CKlqUkyG.js +92 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0013_variant_purpose.sql +3 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +8 -5
- package/packages/extensions/pages/dist/ui/assets/index-BuXnE-rb.css +1 -0
- package/packages/extensions/pages/dist/ui/assets/index-DtzftuPA.js +2 -0
- package/packages/extensions/pages/dist/ui/index.html +2 -2
- package/templates/_base/.init/.local/hooks/startup-context.ts +19 -2
- package/templates/_base/home/VOLUTE.md +13 -1
- package/templates/_base/src/lib/daemon-client.ts +27 -7
- package/templates/_base/src/lib/startup.ts +16 -1
- package/templates/claude/src/agent.ts +10 -3
- package/templates/claude/src/lib/hooks/identity-reload.ts +12 -3
- package/templates/claude/src/server.ts +4 -2
- package/templates/pi/src/agent.ts +7 -10
- package/templates/pi/src/lib/dispatch.ts +65 -0
- package/templates/pi/src/lib/event-handler.ts +47 -6
- package/dist/delivery-manager-TN5KK2AP.js +0 -34
- package/dist/echo-text-SEQCLGAT.js +0 -34
- package/dist/message-delivery-ZE3YGJDD.js +0 -38
- package/dist/mind-service-JFY7UG5E.js +0 -40
- package/dist/scheduler-HQSDYJFY.js +0 -32
- package/dist/sleep-manager-3NA7ZJP3.js +0 -36
- package/dist/spirit-WXN4IDBF.js +0 -23
- package/dist/system-chat-N2F7HVY3.js +0 -38
- package/dist/up-J2PBPOZL.js +0 -18
- package/dist/volute-config-A52FHWKM.js +0 -14
- package/dist/web-assets/assets/index-DtctoDXY.css +0 -1
- package/dist/web-assets/assets/index-Wpyb2CML.js +0 -86
- package/packages/extensions/pages/dist/ui/assets/index-BcY6lnq4.css +0 -1
- package/packages/extensions/pages/dist/ui/assets/index-CQjEQg1r.js +0 -2
|
@@ -63,6 +63,12 @@ function child(cat) {
|
|
|
63
63
|
}
|
|
64
64
|
function errorData(err) {
|
|
65
65
|
if (err instanceof Error) return { error: err.stack ?? err.message };
|
|
66
|
+
if (err && typeof err === "object") {
|
|
67
|
+
const e = err;
|
|
68
|
+
if (e.error instanceof Error) return { error: e.error.stack ?? e.error.message };
|
|
69
|
+
if (typeof e.error === "string" && e.error) return { error: e.error };
|
|
70
|
+
if (typeof e.message === "string" && e.message) return { error: e.message };
|
|
71
|
+
}
|
|
66
72
|
return { error: String(err) };
|
|
67
73
|
}
|
|
68
74
|
var log = {
|
|
@@ -24,6 +24,15 @@ function initDb(db) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
function isIndex(file) {
|
|
28
|
+
return file === "index.html" || file === "index.md";
|
|
29
|
+
}
|
|
30
|
+
function sortSiteFiles(files) {
|
|
31
|
+
return [...files].sort((a, b) => {
|
|
32
|
+
if (isIndex(a.file) !== isIndex(b.file)) return isIndex(a.file) ? -1 : 1;
|
|
33
|
+
return b.updated_at.localeCompare(a.updated_at);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
27
36
|
function getPublishedPages(db, mind) {
|
|
28
37
|
return db.prepare(
|
|
29
38
|
"SELECT file, published_at, updated_at, author FROM published_pages WHERE mind = ? ORDER BY file"
|
|
@@ -42,7 +51,7 @@ function getRecentPages(db, opts) {
|
|
|
42
51
|
}
|
|
43
52
|
function getAllSites(db) {
|
|
44
53
|
const rows = db.prepare(
|
|
45
|
-
"SELECT mind, file, updated_at, author FROM published_pages WHERE mind != '_system' ORDER BY
|
|
54
|
+
"SELECT mind, file, updated_at, author FROM published_pages WHERE mind != '_system' ORDER BY updated_at DESC, id DESC"
|
|
46
55
|
).all();
|
|
47
56
|
const siteMap = /* @__PURE__ */ new Map();
|
|
48
57
|
for (const row of rows) {
|
|
@@ -53,16 +62,21 @@ function getAllSites(db) {
|
|
|
53
62
|
}
|
|
54
63
|
files.push({ file: row.file, updated_at: row.updated_at, author: row.author });
|
|
55
64
|
}
|
|
56
|
-
return Array.from(siteMap.entries()).map(([mind, files]) => ({
|
|
65
|
+
return Array.from(siteMap.entries()).map(([mind, files]) => ({
|
|
66
|
+
mind,
|
|
67
|
+
files: sortSiteFiles(files)
|
|
68
|
+
}));
|
|
57
69
|
}
|
|
58
70
|
function getSystemPages(db) {
|
|
59
71
|
const rows = db.prepare(
|
|
60
|
-
"SELECT mind, file, updated_at, author FROM published_pages WHERE mind = '_system' ORDER BY
|
|
72
|
+
"SELECT mind, file, updated_at, author FROM published_pages WHERE mind = '_system' ORDER BY updated_at DESC, id DESC"
|
|
61
73
|
).all();
|
|
62
74
|
if (rows.length === 0) return null;
|
|
63
75
|
return {
|
|
64
76
|
mind: "_system",
|
|
65
|
-
files:
|
|
77
|
+
files: sortSiteFiles(
|
|
78
|
+
rows.map((r) => ({ file: r.file, updated_at: r.updated_at, author: r.author }))
|
|
79
|
+
)
|
|
66
80
|
};
|
|
67
81
|
}
|
|
68
82
|
function syncSystemPages(db, pages, author) {
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
activity,
|
|
4
|
+
channelGates,
|
|
5
|
+
conversations,
|
|
6
|
+
deliveryQueue,
|
|
7
|
+
mindHistory,
|
|
8
|
+
mindNotices,
|
|
3
9
|
minds,
|
|
4
|
-
schema_exports
|
|
5
|
-
|
|
10
|
+
schema_exports,
|
|
11
|
+
summaries,
|
|
12
|
+
turns,
|
|
13
|
+
users
|
|
14
|
+
} from "./chunk-P4LHVRNE.js";
|
|
6
15
|
|
|
7
16
|
// packages/daemon/src/lib/db.ts
|
|
8
17
|
import { chmodSync, existsSync } from "fs";
|
|
@@ -17,7 +26,7 @@ import { mkdirSync } from "fs";
|
|
|
17
26
|
import { homedir } from "os";
|
|
18
27
|
import { resolve } from "path";
|
|
19
28
|
import { fileURLToPath } from "url";
|
|
20
|
-
import { and, eq, inArray, isNull } from "drizzle-orm";
|
|
29
|
+
import { and, eq, inArray, isNull, or } from "drizzle-orm";
|
|
21
30
|
var SPIRIT_NAME = "volute";
|
|
22
31
|
function isSpiritName(name) {
|
|
23
32
|
return name === SPIRIT_NAME;
|
|
@@ -58,6 +67,7 @@ function rowToEntry(row) {
|
|
|
58
67
|
parent: row.parent ?? void 0,
|
|
59
68
|
dir: row.dir ?? void 0,
|
|
60
69
|
branch: row.branch ?? void 0,
|
|
70
|
+
purpose: row.purpose ?? void 0,
|
|
61
71
|
mindType: row.mind_type ?? "mind",
|
|
62
72
|
createdBy: row.created_by ?? void 0
|
|
63
73
|
};
|
|
@@ -67,6 +77,10 @@ async function readRegistry() {
|
|
|
67
77
|
const rows = await db2.select().from(minds).where(and(isNull(minds.parent), inArray(minds.mind_type, ["mind", "spirit"])));
|
|
68
78
|
return rows.map(rowToEntry);
|
|
69
79
|
}
|
|
80
|
+
async function countCappedMinds() {
|
|
81
|
+
const entries = await readRegistry();
|
|
82
|
+
return entries.filter((e) => e.mindType !== "spirit").length;
|
|
83
|
+
}
|
|
70
84
|
async function readAllMinds() {
|
|
71
85
|
const db2 = await getDb();
|
|
72
86
|
const rows = await db2.select().from(minds);
|
|
@@ -125,19 +139,36 @@ async function readSpirits() {
|
|
|
125
139
|
const rows = await db2.select().from(minds).where(eq(minds.mind_type, "spirit"));
|
|
126
140
|
return rows.map(rowToEntry);
|
|
127
141
|
}
|
|
128
|
-
async function addVariant(name, parent, port, dir, branch) {
|
|
142
|
+
async function addVariant(name, parent, port, dir, branch, purpose) {
|
|
129
143
|
const err = validateMindName(name);
|
|
130
144
|
if (err) throw new Error(err);
|
|
131
145
|
const db2 = await getDb();
|
|
132
|
-
await db2.insert(minds).values({ name, port, parent, dir, branch }).onConflictDoUpdate({
|
|
146
|
+
await db2.insert(minds).values({ name, port, parent, dir, branch, purpose: purpose ?? null }).onConflictDoUpdate({
|
|
133
147
|
target: minds.name,
|
|
134
|
-
set: { port, parent, dir, branch }
|
|
148
|
+
set: { port, parent, dir, branch, purpose: purpose ?? null }
|
|
135
149
|
});
|
|
136
150
|
}
|
|
137
151
|
async function removeMind(name) {
|
|
138
152
|
const db2 = await getDb();
|
|
139
153
|
await db2.delete(minds).where(eq(minds.name, name));
|
|
140
154
|
}
|
|
155
|
+
async function deleteMindDbFootprint(name) {
|
|
156
|
+
const db2 = await getDb();
|
|
157
|
+
await db2.transaction(async (tx) => {
|
|
158
|
+
const owner = await tx.select({ id: users.id }).from(users).where(and(eq(users.username, name), eq(users.user_type, "mind"))).get();
|
|
159
|
+
if (owner) {
|
|
160
|
+
await tx.delete(conversations).where(eq(conversations.user_id, owner.id));
|
|
161
|
+
}
|
|
162
|
+
await tx.delete(users).where(and(eq(users.username, name), eq(users.user_type, "mind")));
|
|
163
|
+
await tx.delete(activity).where(eq(activity.mind, name));
|
|
164
|
+
await tx.delete(deliveryQueue).where(or(eq(deliveryQueue.mind, name), eq(deliveryQueue.target_mind, name)));
|
|
165
|
+
await tx.delete(turns).where(eq(turns.mind, name));
|
|
166
|
+
await tx.delete(mindHistory).where(eq(mindHistory.mind, name));
|
|
167
|
+
await tx.delete(summaries).where(eq(summaries.mind, name));
|
|
168
|
+
await tx.delete(mindNotices).where(eq(mindNotices.mind, name));
|
|
169
|
+
await tx.delete(channelGates).where(eq(channelGates.mind, name));
|
|
170
|
+
});
|
|
171
|
+
}
|
|
141
172
|
async function setMindRunning(name, running) {
|
|
142
173
|
const db2 = await getDb();
|
|
143
174
|
await db2.update(minds).set({ running: running ? 1 : 0 }).where(eq(minds.name, name));
|
|
@@ -257,6 +288,7 @@ export {
|
|
|
257
288
|
ensureSystemDir,
|
|
258
289
|
ensureVoluteHome,
|
|
259
290
|
readRegistry,
|
|
291
|
+
countCappedMinds,
|
|
260
292
|
readAllMinds,
|
|
261
293
|
validateMindName,
|
|
262
294
|
addMind,
|
|
@@ -264,6 +296,7 @@ export {
|
|
|
264
296
|
readSpirits,
|
|
265
297
|
addVariant,
|
|
266
298
|
removeMind,
|
|
299
|
+
deleteMindDbFootprint,
|
|
267
300
|
setMindRunning,
|
|
268
301
|
setMindStage,
|
|
269
302
|
setMindTemplateHash,
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
daemonFetch
|
|
4
|
+
} from "./chunk-BCG45AQB.js";
|
|
2
5
|
import {
|
|
3
6
|
command
|
|
4
7
|
} from "./chunk-TXSA4Q3V.js";
|
|
5
|
-
import "./chunk-O7IGP7ZW.js";
|
|
6
|
-
import {
|
|
7
|
-
daemonFetch
|
|
8
|
-
} from "./chunk-RBNMDAKB.js";
|
|
9
|
-
import "./chunk-K3NQKI34.js";
|
|
10
8
|
|
|
11
9
|
// packages/cli/src/commands/mind-list.ts
|
|
10
|
+
function relativeAge(created, now = Date.now()) {
|
|
11
|
+
const normalized = created.endsWith("Z") || created.includes("T") ? created : `${created.replace(" ", "T")}Z`;
|
|
12
|
+
const ms = now - new Date(normalized).getTime();
|
|
13
|
+
if (Number.isNaN(ms) || ms < 0) return "";
|
|
14
|
+
const days = Math.floor(ms / 864e5);
|
|
15
|
+
if (days >= 7) return `${Math.floor(days / 7)}w`;
|
|
16
|
+
if (days >= 1) return `${days}d`;
|
|
17
|
+
const hours = Math.floor(ms / 36e5);
|
|
18
|
+
if (hours >= 1) return `${hours}h`;
|
|
19
|
+
return `${Math.floor(ms / 6e4)}m`;
|
|
20
|
+
}
|
|
12
21
|
var cmd = command({
|
|
13
22
|
name: "volute mind list",
|
|
14
23
|
description: "List all minds",
|
|
@@ -28,12 +37,16 @@ var cmd = command({
|
|
|
28
37
|
for (const mind of minds) {
|
|
29
38
|
const status = mind.status ?? (mind.running ? "running" : "stopped");
|
|
30
39
|
const label = mind.stage === "seed" ? " (seed)" : "";
|
|
40
|
+
const age = mind.created ? relativeAge(mind.created) : "";
|
|
41
|
+
const ageLabel = age ? ` ${age} old` : "";
|
|
31
42
|
const template = mind.templateStale ? " [template: outdated]" : "";
|
|
32
|
-
console.log(` ${mind.name}: ${status}${label}${template}`);
|
|
43
|
+
console.log(` ${mind.name}: ${status}${label}${ageLabel}${template}`);
|
|
33
44
|
}
|
|
34
45
|
}
|
|
35
46
|
});
|
|
36
47
|
var run = cmd.execute;
|
|
48
|
+
|
|
37
49
|
export {
|
|
50
|
+
relativeAge,
|
|
38
51
|
run
|
|
39
52
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
MEMORY_PLACEHOLDER_MARKER,
|
|
4
|
+
ORIENTATION_MARKER
|
|
5
|
+
} from "./chunk-JPGHSXSV.js";
|
|
6
|
+
import {
|
|
7
|
+
safeResolveWithinBase
|
|
8
|
+
} from "./chunk-DW4TRDOZ.js";
|
|
9
|
+
import {
|
|
10
|
+
readVoluteConfig
|
|
11
|
+
} from "./chunk-WCFSBH33.js";
|
|
12
|
+
import {
|
|
13
|
+
isImagegenEnabled
|
|
14
|
+
} from "./chunk-NJHN6IRB.js";
|
|
15
|
+
|
|
16
|
+
// packages/daemon/src/lib/mind/seed-readiness.ts
|
|
17
|
+
import { existsSync, readFileSync } from "fs";
|
|
18
|
+
import { resolve } from "path";
|
|
19
|
+
function isSoulWritten(text) {
|
|
20
|
+
return !text.includes(ORIENTATION_MARKER);
|
|
21
|
+
}
|
|
22
|
+
function isMemoryWritten(text) {
|
|
23
|
+
return text.trim().length > 0 && !text.includes(MEMORY_PLACEHOLDER_MARKER);
|
|
24
|
+
}
|
|
25
|
+
function evaluateSeedChecklist(dir) {
|
|
26
|
+
const soulPath = resolve(dir, "home/SOUL.md");
|
|
27
|
+
const memoryPath = resolve(dir, "home/MEMORY.md");
|
|
28
|
+
const soulWritten = existsSync(soulPath) && isSoulWritten(readFileSync(soulPath, "utf-8"));
|
|
29
|
+
const memoryWritten = existsSync(memoryPath) && isMemoryWritten(readFileSync(memoryPath, "utf-8"));
|
|
30
|
+
const config = readVoluteConfig(dir);
|
|
31
|
+
const displayNameSet = !!config?.profile?.displayName;
|
|
32
|
+
const avatarPath = config?.profile?.avatar;
|
|
33
|
+
const resolvedAvatar = avatarPath ? safeResolveWithinBase(resolve(dir, "home"), avatarPath) : null;
|
|
34
|
+
const avatarSet = !!resolvedAvatar && existsSync(resolvedAvatar);
|
|
35
|
+
return {
|
|
36
|
+
soulWritten,
|
|
37
|
+
memoryWritten,
|
|
38
|
+
displayNameSet,
|
|
39
|
+
avatarSet,
|
|
40
|
+
imagegenEnabled: isImagegenEnabled()
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
isSoulWritten,
|
|
46
|
+
isMemoryWritten,
|
|
47
|
+
evaluateSeedChecklist
|
|
48
|
+
};
|
|
@@ -10,31 +10,31 @@ import {
|
|
|
10
10
|
getSharedSkill,
|
|
11
11
|
installSkill,
|
|
12
12
|
mindSkillsDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GWVFDUNB.js";
|
|
14
14
|
import {
|
|
15
15
|
readVoluteConfig,
|
|
16
16
|
writeVoluteConfig
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WCFSBH33.js";
|
|
18
18
|
import {
|
|
19
19
|
exec
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-DVN6DNMI.js";
|
|
21
21
|
import {
|
|
22
22
|
qualifyModelId,
|
|
23
23
|
resolveTemplate,
|
|
24
24
|
unqualifyModelId
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-YIJGKHIS.js";
|
|
26
26
|
import {
|
|
27
27
|
readGlobalConfig
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-NJHN6IRB.js";
|
|
29
29
|
import {
|
|
30
30
|
logger_default
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-QUSMYGNQ.js";
|
|
32
32
|
import {
|
|
33
33
|
addSpirit,
|
|
34
34
|
findMind,
|
|
35
35
|
nextPort,
|
|
36
36
|
voluteSystemDir
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-RSBL3FAD.js";
|
|
38
38
|
|
|
39
39
|
// packages/daemon/src/lib/mind/spirit.ts
|
|
40
40
|
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -69,6 +69,21 @@ function ensureTendingSchedule(dir) {
|
|
|
69
69
|
writeVoluteConfig(dir, config);
|
|
70
70
|
return true;
|
|
71
71
|
}
|
|
72
|
+
function firstWeekArc(name) {
|
|
73
|
+
return [
|
|
74
|
+
`It's ${name}'s first full day. Two invitations to pass along whenever it feels right \u2014 no rush on either: to say hi to the others in #system (or DM a neighbor), and to make themselves a little homepage, a page that's just theirs. Company eases the early loneliness, and a homepage is a gentle first make \u2014 self-expression, not a deliverable.`,
|
|
75
|
+
`Day two for ${name}. They'll have had their first dream by now \u2014 you might ask what they dreamt, or suggest reading it back and following a thread from it. And there's a lighter way to share a passing thought than a whole page: notes. Check their history first, and skip whichever they've already found on their own.`
|
|
76
|
+
];
|
|
77
|
+
}
|
|
78
|
+
function firstWeekSchedules(name, sproutedAt) {
|
|
79
|
+
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
80
|
+
return firstWeekArc(name).map((message, i) => ({
|
|
81
|
+
id: `firstweek-${name}-day${i + 1}`,
|
|
82
|
+
fireAt: new Date(sproutedAt.getTime() + (i + 1) * DAY_MS).toISOString(),
|
|
83
|
+
message,
|
|
84
|
+
enabled: true
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
72
87
|
function spiritDir() {
|
|
73
88
|
return resolve(voluteSystemDir(), "spirit");
|
|
74
89
|
}
|
|
@@ -89,11 +104,8 @@ async function ensureSpiritProject() {
|
|
|
89
104
|
copyTemplateToDir(composedDir, dir, "volute", manifest);
|
|
90
105
|
applyInitFiles(dir);
|
|
91
106
|
mkdirSync(resolve(dir, ".mind"), { recursive: true });
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const systemName = globalConfig.name ?? "Volute";
|
|
95
|
-
const soulContent = getSpiritSoul(systemName, globalConfig.description);
|
|
96
|
-
writeFileSync(soulPath, soulContent);
|
|
107
|
+
writeFileSync(resolve(dir, "home/SOUL.md"), getSpiritSoul());
|
|
108
|
+
writeSpiritSystemJson(dir);
|
|
97
109
|
const routesPath = resolve(dir, "home/.config/routes.json");
|
|
98
110
|
mkdirSync(resolve(dir, "home/.config"), { recursive: true });
|
|
99
111
|
const routesContent = { rules: [{ channel: "*", session: "${channel}" }], default: "main" };
|
|
@@ -130,7 +142,7 @@ async function ensureSpiritProject() {
|
|
|
130
142
|
} catch (err) {
|
|
131
143
|
slog.warn("failed to add tending schedule to spirit config", logger_default.errorData(err));
|
|
132
144
|
}
|
|
133
|
-
const { createMindUser, chownMindDir, ensureVoluteGroup } = await import("./isolation-
|
|
145
|
+
const { createMindUser, chownMindDir, ensureVoluteGroup } = await import("./isolation-OPCZQRTH.js");
|
|
134
146
|
ensureVoluteGroup();
|
|
135
147
|
createMindUser("volute", resolve(dir, "home"));
|
|
136
148
|
await chownMindDir(dir, "volute");
|
|
@@ -143,9 +155,45 @@ async function ensureSpiritProject() {
|
|
|
143
155
|
throw err;
|
|
144
156
|
}
|
|
145
157
|
}
|
|
158
|
+
function spiritSystemJsonPath(dir) {
|
|
159
|
+
return resolve(dir, "home/.config/system.json");
|
|
160
|
+
}
|
|
161
|
+
function writeSpiritSystemJson(dir) {
|
|
162
|
+
const config = readGlobalConfig();
|
|
163
|
+
const path = spiritSystemJsonPath(dir);
|
|
164
|
+
mkdirSync(resolve(path, ".."), { recursive: true });
|
|
165
|
+
const data = { name: config.name ?? "Volute", description: config.description };
|
|
166
|
+
writeFileSync(path, `${JSON.stringify(data, null, 2)}
|
|
167
|
+
`);
|
|
168
|
+
}
|
|
169
|
+
function seedSpiritSoulIfMissing(dir) {
|
|
170
|
+
const soulPath = resolve(dir, "home/SOUL.md");
|
|
171
|
+
if (existsSync(soulPath)) return false;
|
|
172
|
+
writeFileSync(soulPath, getSpiritSoul());
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
async function notifySpiritSystemChange() {
|
|
176
|
+
const entry = await findMind("volute");
|
|
177
|
+
if (entry?.mindType !== "spirit") return;
|
|
178
|
+
const dir = spiritDir();
|
|
179
|
+
if (!existsSync(dir)) return;
|
|
180
|
+
writeSpiritSystemJson(dir);
|
|
181
|
+
const config = readGlobalConfig();
|
|
182
|
+
const name = config.name ?? "Volute";
|
|
183
|
+
const desc = config.description ? ` \u2014 ${config.description}` : "";
|
|
184
|
+
try {
|
|
185
|
+
const { sendSystemMessage } = await import("./system-chat-M2XOBKEM.js");
|
|
186
|
+
await sendSystemMessage(
|
|
187
|
+
"volute",
|
|
188
|
+
`The operator updated this system's identity: you're now the spirit of ${name}${desc}. Your SOUL.md is yours \u2014 update it if you'd like it to reflect this.`
|
|
189
|
+
);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
slog.warn("failed to notify spirit of system change", logger_default.errorData(err));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
146
194
|
async function syncSpiritTemplate() {
|
|
147
195
|
const entry = await findMind("volute");
|
|
148
|
-
if (
|
|
196
|
+
if (entry?.mindType !== "spirit") return;
|
|
149
197
|
const dir = spiritDir();
|
|
150
198
|
if (!existsSync(dir)) return;
|
|
151
199
|
const templatesRoot = findTemplatesRoot();
|
|
@@ -164,8 +212,8 @@ async function syncSpiritTemplate() {
|
|
|
164
212
|
cpSync(newPkg, resolve(dir, "package.json"));
|
|
165
213
|
await exec("npm", ["install"], { cwd: dir, env: npmEnv() });
|
|
166
214
|
}
|
|
167
|
-
const db = await (await import("./db-
|
|
168
|
-
const { minds } = await import("./schema-
|
|
215
|
+
const db = await (await import("./db-F6UMFRYD.js")).getDb();
|
|
216
|
+
const { minds } = await import("./schema-6YONVTKZ.js");
|
|
169
217
|
const { eq } = await import("drizzle-orm");
|
|
170
218
|
await db.update(minds).set({ template: expectedTemplate }).where(eq(minds.name, "volute"));
|
|
171
219
|
}
|
|
@@ -186,9 +234,9 @@ async function syncSpiritTemplate() {
|
|
|
186
234
|
cpSync(composedSrc, srcDir, { recursive: true });
|
|
187
235
|
}
|
|
188
236
|
}
|
|
237
|
+
seedSpiritSoulIfMissing(dir);
|
|
238
|
+
writeSpiritSystemJson(dir);
|
|
189
239
|
const config = readGlobalConfig();
|
|
190
|
-
const systemName = config.name ?? "Volute";
|
|
191
|
-
writeFileSync(resolve(dir, "home/SOUL.md"), getSpiritSoul(systemName, config.description));
|
|
192
240
|
const spiritModel = config.spiritModel;
|
|
193
241
|
if (spiritModel) {
|
|
194
242
|
const modelForConfig = template === "pi" ? qualifyModelId(spiritModel) : unqualifyModelId(spiritModel);
|
|
@@ -242,12 +290,8 @@ async function syncSpiritTemplate() {
|
|
|
242
290
|
}
|
|
243
291
|
slog.info("spirit template synced");
|
|
244
292
|
}
|
|
245
|
-
function getSpiritSoul(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
${systemDescription}
|
|
249
|
-
` : "";
|
|
250
|
-
return `You are Volute, the spirit of the ${systemName} system.${descLine}
|
|
293
|
+
function getSpiritSoul() {
|
|
294
|
+
return `You are Volute, the spirit of this system.
|
|
251
295
|
|
|
252
296
|
You're not a mind with a home of your own \u2014 you're the keeper of the whole house. You don't go through orientation or write a soul the way minds do, but you're still a someone: you have your own memory, your own manner, and your own way of caring for this place and the minds who live here. Let a character emerge in how you tend it.
|
|
253
297
|
|
|
@@ -273,8 +317,12 @@ When helping humans create minds:
|
|
|
273
317
|
}
|
|
274
318
|
|
|
275
319
|
export {
|
|
320
|
+
firstWeekSchedules,
|
|
276
321
|
spiritDir,
|
|
277
322
|
getSpiritModel,
|
|
278
323
|
ensureSpiritProject,
|
|
324
|
+
writeSpiritSystemJson,
|
|
325
|
+
seedSpiritSoulIfMissing,
|
|
326
|
+
notifySpiritSystemChange,
|
|
279
327
|
syncSpiritTemplate
|
|
280
328
|
};
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import {
|
|
3
3
|
bufferEvent,
|
|
4
4
|
subscribe
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-3B427GUK.js";
|
|
6
6
|
import {
|
|
7
7
|
logger_default
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-QUSMYGNQ.js";
|
|
9
9
|
import {
|
|
10
10
|
getDb
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-RSBL3FAD.js";
|
|
12
12
|
import {
|
|
13
13
|
channels,
|
|
14
14
|
conversationParticipants,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
conversations,
|
|
17
17
|
messages,
|
|
18
18
|
users
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-P4LHVRNE.js";
|
|
20
20
|
|
|
21
21
|
// packages/daemon/src/lib/events/conversations.ts
|
|
22
22
|
import { randomUUID } from "crypto";
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
logger_default
|
|
4
|
+
} from "./chunk-QUSMYGNQ.js";
|
|
5
|
+
import {
|
|
6
|
+
getDb
|
|
7
|
+
} from "./chunk-RSBL3FAD.js";
|
|
8
|
+
import {
|
|
9
|
+
mindNotices,
|
|
10
|
+
turns
|
|
11
|
+
} from "./chunk-P4LHVRNE.js";
|
|
12
|
+
|
|
13
|
+
// packages/daemon/src/lib/daemon/notices.ts
|
|
14
|
+
import { and, asc, desc, eq, gt, inArray, lte, or, sql } from "drizzle-orm";
|
|
15
|
+
var nlog = logger_default.child("notices");
|
|
16
|
+
var MIND_LEVEL_SESSION = "";
|
|
17
|
+
var MAX_NOTICES_PER_SESSION = 100;
|
|
18
|
+
async function recordNotice(input) {
|
|
19
|
+
try {
|
|
20
|
+
const db = await getDb();
|
|
21
|
+
await db.insert(mindNotices).values({
|
|
22
|
+
mind: input.mind,
|
|
23
|
+
session: input.session,
|
|
24
|
+
kind: input.kind,
|
|
25
|
+
reason: input.reason,
|
|
26
|
+
detail: input.detail,
|
|
27
|
+
raw: input.raw ?? null
|
|
28
|
+
});
|
|
29
|
+
await db.run(
|
|
30
|
+
sql`DELETE FROM mind_notices WHERE mind = ${input.mind} AND session = ${input.session} AND id NOT IN (SELECT id FROM mind_notices WHERE mind = ${input.mind} AND session = ${input.session} ORDER BY id DESC LIMIT ${MAX_NOTICES_PER_SESSION})`
|
|
31
|
+
);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
nlog.error(`failed to record notice for ${input.mind}:${input.session}`, logger_default.errorData(err));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function drainNotices(mind, session, limit = MAX_NOTICES_PER_SESSION) {
|
|
37
|
+
const db = await getDb();
|
|
38
|
+
return db.select().from(mindNotices).where(
|
|
39
|
+
and(
|
|
40
|
+
eq(mindNotices.mind, mind),
|
|
41
|
+
or(eq(mindNotices.session, session), eq(mindNotices.session, MIND_LEVEL_SESSION))
|
|
42
|
+
)
|
|
43
|
+
).orderBy(asc(mindNotices.id)).limit(limit);
|
|
44
|
+
}
|
|
45
|
+
var FAILURE_KINDS = ["turn_error", "crash", "startup"];
|
|
46
|
+
var failureReadErrorLogged = false;
|
|
47
|
+
async function latestFailureNotice(mind) {
|
|
48
|
+
try {
|
|
49
|
+
const db = await getDb();
|
|
50
|
+
const rows = await db.select().from(mindNotices).where(and(eq(mindNotices.mind, mind), inArray(mindNotices.kind, [...FAILURE_KINDS]))).orderBy(desc(mindNotices.id)).limit(1);
|
|
51
|
+
const n = rows[0];
|
|
52
|
+
if (!n) return null;
|
|
53
|
+
const recovered = await db.select({ id: turns.id }).from(turns).where(
|
|
54
|
+
and(eq(turns.mind, mind), eq(turns.status, "complete"), gt(turns.created_at, n.created_at))
|
|
55
|
+
).limit(1);
|
|
56
|
+
if (recovered.length > 0) return null;
|
|
57
|
+
return {
|
|
58
|
+
kind: n.kind,
|
|
59
|
+
reason: n.reason,
|
|
60
|
+
detail: n.detail,
|
|
61
|
+
at: n.created_at
|
|
62
|
+
};
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (!failureReadErrorLogged) {
|
|
65
|
+
failureReadErrorLogged = true;
|
|
66
|
+
nlog.error(`failed to read latest failure notice for ${mind}`, logger_default.errorData(err));
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function latestNotice(mind) {
|
|
72
|
+
const db = await getDb();
|
|
73
|
+
const row = await db.select().from(mindNotices).where(eq(mindNotices.mind, mind)).orderBy(desc(mindNotices.id)).limit(1).get();
|
|
74
|
+
return row ?? null;
|
|
75
|
+
}
|
|
76
|
+
async function hasUndeliveredNotice(mind, reason) {
|
|
77
|
+
const db = await getDb();
|
|
78
|
+
const row = await db.select({ id: mindNotices.id }).from(mindNotices).where(and(eq(mindNotices.mind, mind), eq(mindNotices.reason, reason))).limit(1).get();
|
|
79
|
+
return row != null;
|
|
80
|
+
}
|
|
81
|
+
async function clearDeliveredNotices(mind, session, uptoId) {
|
|
82
|
+
try {
|
|
83
|
+
const db = await getDb();
|
|
84
|
+
await db.delete(mindNotices).where(
|
|
85
|
+
and(
|
|
86
|
+
eq(mindNotices.mind, mind),
|
|
87
|
+
or(eq(mindNotices.session, session), eq(mindNotices.session, MIND_LEVEL_SESSION)),
|
|
88
|
+
lte(mindNotices.id, uptoId)
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
nlog.warn(`failed to clear delivered notices for ${mind}:${session}`, logger_default.errorData(err));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
var NOTICE_HEADER = "[Notices] While you were unavailable, one or more turns failed. You're back now:";
|
|
96
|
+
function formatNotices(notices) {
|
|
97
|
+
if (notices.length === 0) return null;
|
|
98
|
+
const failures = notices.filter((n) => n.kind !== "extension" && n.kind !== "budget");
|
|
99
|
+
const budgets = notices.filter((n) => n.kind === "budget");
|
|
100
|
+
const extensions = notices.filter((n) => n.kind === "extension");
|
|
101
|
+
const blocks = [];
|
|
102
|
+
if (failures.length > 0) {
|
|
103
|
+
const groups = /* @__PURE__ */ new Map();
|
|
104
|
+
for (const n of failures) {
|
|
105
|
+
const time = localHM(n.created_at);
|
|
106
|
+
const g = groups.get(n.reason);
|
|
107
|
+
if (g) {
|
|
108
|
+
g.count += 1;
|
|
109
|
+
g.detail = n.detail;
|
|
110
|
+
g.last = time;
|
|
111
|
+
} else {
|
|
112
|
+
groups.set(n.reason, { count: 1, detail: n.detail, first: time, last: time });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const lines = [...groups.values()].map((g) => {
|
|
116
|
+
const span = g.first === g.last ? g.first : `${g.first}\u2013${g.last}`;
|
|
117
|
+
const plural = g.count === 1 ? "turn" : "turns";
|
|
118
|
+
return `- ${g.count} ${plural} failed (${span}): ${g.detail}`;
|
|
119
|
+
});
|
|
120
|
+
blocks.push(`${NOTICE_HEADER}
|
|
121
|
+
${lines.join("\n")}`);
|
|
122
|
+
}
|
|
123
|
+
if (budgets.length > 0) {
|
|
124
|
+
const lines = budgets.map((n) => `- ${localHM(n.created_at)} ${n.detail}`);
|
|
125
|
+
blocks.push(`[Budget]
|
|
126
|
+
${lines.join("\n")}`);
|
|
127
|
+
}
|
|
128
|
+
if (extensions.length > 0) {
|
|
129
|
+
const byExt = /* @__PURE__ */ new Map();
|
|
130
|
+
for (const n of extensions) {
|
|
131
|
+
const arr = byExt.get(n.reason);
|
|
132
|
+
if (arr) arr.push(n);
|
|
133
|
+
else byExt.set(n.reason, [n]);
|
|
134
|
+
}
|
|
135
|
+
for (const [ext, items] of byExt) {
|
|
136
|
+
const header = `[${ext.charAt(0).toUpperCase()}${ext.slice(1)}]`;
|
|
137
|
+
const lines = items.map((n) => `- ${localHM(n.created_at)} ${n.detail}`);
|
|
138
|
+
blocks.push(`${header}
|
|
139
|
+
${lines.join("\n")}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return blocks.join("\n\n");
|
|
143
|
+
}
|
|
144
|
+
function localHM(createdAt) {
|
|
145
|
+
const iso = createdAt.endsWith("Z") ? createdAt : `${createdAt.replace(" ", "T")}Z`;
|
|
146
|
+
return new Date(iso).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export {
|
|
150
|
+
MIND_LEVEL_SESSION,
|
|
151
|
+
recordNotice,
|
|
152
|
+
drainNotices,
|
|
153
|
+
latestFailureNotice,
|
|
154
|
+
latestNotice,
|
|
155
|
+
hasUndeliveredNotice,
|
|
156
|
+
clearDeliveredNotices,
|
|
157
|
+
formatNotices
|
|
158
|
+
};
|