volute 0.46.0 → 0.48.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 +10 -10
- package/dist/{accept-N5XULCXK.js → accept-KYNC5X3U.js} +1 -1
- package/dist/{activity-events-NF6VWTJQ.js → activity-events-MIOK3IOU.js} +3 -3
- package/dist/{ai-service-UODCCE4O.js → ai-service-643TNFPH.js} +8 -4
- package/dist/{archive-WS2L2VIQ.js → archive-6Z4GFQPH.js} +3 -3
- package/dist/{auth-CCFAU4GQ.js → auth-A4VGHADI.js} +4 -4
- package/dist/avatar-image-Y45R7LW5.js +23 -0
- package/dist/{backup-TST5C2YN.js → backup-UDH4GMSW.js} +15 -15
- package/dist/{bridge-2PO3AI5D.js → bridge-3FNSWLNQ.js} +1 -1
- package/dist/channels-SGQZHNQJ.js +89 -0
- package/dist/{chat-ZIQQS25U.js → chat-X43VYHH2.js} +12 -8
- package/dist/{chunk-ZV7MFISX.js → chunk-2MJ7EOMT.js} +20 -2
- package/dist/{chunk-R77O2WMK.js → chunk-2OIYNHWO.js} +1 -1
- package/dist/{chunk-QCN24XEN.js → chunk-3YUBFTDN.js} +1 -1
- package/dist/chunk-4FCL4DVZ.js +48 -0
- package/dist/{chunk-F7H6ATM4.js → chunk-5KXCM5AW.js} +18 -2
- package/dist/chunk-7KRIHYLR.js +124 -0
- package/dist/{chunk-RBNMDAKB.js → chunk-BCG45AQB.js} +15 -1
- package/dist/{chunk-QMJCNZ6C.js → chunk-C3KLHJDB.js} +1 -1
- package/dist/{chunk-H2X54EHC.js → chunk-DGGZVZTM.js} +1 -1
- package/dist/chunk-DW4TRDOZ.js +29 -0
- package/dist/{chunk-4JOQZPCE.js → chunk-EIQDZC43.js} +1 -1
- package/dist/{chunk-TQJ6ZYVY.js → chunk-FJ2MZ5PH.js} +41 -5
- package/dist/{chunk-BJZ4PV7R.js → chunk-HNGFKPNS.js} +26 -10
- package/dist/{chunk-5ZQZDDIK.js → chunk-HPGN6XRT.js} +3 -3
- package/dist/{chunk-KGIXX4NM.js → chunk-HUNDXZBZ.js} +5 -5
- package/dist/{chunk-ZDZISKCL.js → chunk-IJPMIEVE.js} +1 -1
- package/dist/chunk-IYCV34IU.js +158 -0
- package/dist/{chunk-ZNMCCHCP.js → chunk-LTLFOSA4.js} +2 -2
- package/dist/{chunk-K6T43YBD.js → chunk-MJRXFUMS.js} +26 -6
- package/dist/{chunk-4YU7HF4L.js → chunk-MOERAQXH.js} +6 -4
- package/dist/{chunk-KQSFTPIM.js → chunk-MTRMQFCQ.js} +1 -1
- package/dist/{chunk-OV7ZQ2GH.js → chunk-ODDGGLM6.js} +3 -3
- package/dist/{chunk-A7WQZKOU.js → chunk-OQJMTHEH.js} +2 -2
- package/dist/{chunk-RRNXJOKB.js → chunk-PJVPDY2Z.js} +2 -2
- package/dist/{chunk-FXORA5JG.js → chunk-Q7HOAVFR.js} +3 -3
- package/dist/{chunk-KDLYIECM.js → chunk-R772ET2M.js} +18 -4
- package/dist/{chunk-QLWG3C2Z.js → chunk-RRZ4RQQU.js} +2 -2
- package/dist/{mind-list-4D7J4CHD.js → chunk-SHKM5RGD.js} +18 -4
- package/dist/{chunk-RXEEYDIB.js → chunk-SZJCJVXV.js} +1 -1
- package/dist/{chunk-E3DA4HYV.js → chunk-T4VS7UDW.js} +15 -15
- package/dist/{chunk-YXSUMLTC.js → chunk-USD45JKR.js} +322 -76
- package/dist/{chunk-JO5IO4FH.js → chunk-VB2QXUJQ.js} +391 -294
- package/dist/{chunk-JZ62SYWR.js → chunk-VZRA5TYO.js} +1 -1
- package/dist/{chunk-PJ45FQPT.js → chunk-WARH6GIV.js} +8 -8
- package/dist/{chunk-PFZBNKUH.js → chunk-WCFSBH33.js} +0 -1
- package/dist/{chunk-DRJ24GXK.js → chunk-WQ46E33M.js} +59 -39
- package/dist/{chunk-ALKYQULU.js → chunk-WW6C3PLV.js} +1 -1
- package/dist/{chunk-CDPBTTZO.js → chunk-XVBSFLM7.js} +2 -2
- package/dist/{chunk-6EMWJZBK.js → chunk-YKA7DCVW.js} +1 -1
- package/dist/{chunk-IHKGWTGT.js → chunk-YNBXEQAP.js} +31 -1
- package/dist/cli.js +34 -26
- package/dist/{clock-RHS2JEDL.js → clock-CNRF6F7T.js} +3 -3
- package/dist/{cloud-sync-46RS4SDA.js → cloud-sync-WHKCB4CD.js} +22 -19
- package/dist/{config-TJLYUV3U.js → config-RPSLA2HX.js} +3 -3
- package/dist/{conversations-CJOOCGNW.js → conversations-C7IKYSHD.js} +4 -4
- package/dist/{create-IOV4GCRX.js → create-GJKHABJB.js} +16 -5
- package/dist/{create-DF3KISTW.js → create-VIQVP3QB.js} +1 -1
- package/dist/{daemon-client-IYR4KCPU.js → daemon-client-ZFY5MOAL.js} +5 -3
- package/dist/{daemon-restart-VFEI57UO.js → daemon-restart-7KUHVZIN.js} +7 -7
- package/dist/daemon.js +829 -496
- package/dist/{db-MF2JNRUW.js → db-FO6LXYCW.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-HZTT5SJR.js +36 -0
- package/dist/{delivery-router-N4A7ZC5T.js → delivery-router-3QFG2ZEX.js} +3 -3
- package/dist/{down-U2CZ23M4.js → down-G6IADJXV.js} +5 -5
- package/dist/echo-text-H3SH337Q.js +36 -0
- package/dist/env-6TGPO5WG.js +18 -0
- package/dist/{env-VZ4MLFQW.js → env-C7Z55QCJ.js} +6 -6
- package/dist/{exec-G3ZRST4Z.js → exec-QIFYZG4C.js} +3 -3
- package/dist/{export-LY723I3Z.js → export-C2K3NIRD.js} +4 -4
- package/dist/{extension-SEID47SR.js → extension-J4ZQ2GHV.js} +1 -1
- package/dist/{extensions-ZELQLTSR.js → extensions-GEKF47A4.js} +24 -19
- package/dist/{files-4LN3EN7Q.js → files-LXQSQOFG.js} +4 -4
- package/dist/{import-42YNQBF4.js → import-SX3ZXHB4.js} +8 -8
- package/dist/{isolation-YMGZZ4E5.js → isolation-MCT4ZYLT.js} +3 -3
- package/dist/{join-ZJV7W6MO.js → join-34TR2GXQ.js} +1 -1
- package/dist/{list-OXIAY3W7.js → list-PMUXZNAM.js} +1 -1
- package/dist/{login-MY6ETFYB.js → login-ARRZ4ORJ.js} +3 -3
- package/dist/{login-M6OJUPQ2.js → login-CL4IT2ED.js} +14 -5
- package/dist/{logout-WQBWO77G.js → logout-HZYJCYCA.js} +3 -3
- package/dist/{logout-NCG3TMDL.js → logout-YZRMZ3O2.js} +1 -1
- package/dist/message-delivery-GPQ5SE5N.js +40 -0
- package/dist/{mind-LHKS55DI.js → mind-46AOK373.js} +23 -19
- package/dist/{mind-activity-tracker-3QRNJVR4.js → mind-activity-tracker-YVZPC4A5.js} +4 -4
- package/dist/mind-contacts-GV7XYL5D.js +64 -0
- package/dist/{mind-history-VRO5A76U.js → mind-history-TTGA5BHZ.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-2ARGJNKP.js +44 -0
- package/dist/{mind-sleep-74TGOQBC.js → mind-sleep-DV7KP255.js} +3 -3
- package/dist/{mind-status-UO34ASXZ.js → mind-status-BJNPGXKC.js} +23 -1
- package/dist/{mind-wake-LCLGTXA2.js → mind-wake-LVTZ55EZ.js} +1 -1
- package/dist/notices-2KU5NTRY.js +25 -0
- package/dist/{package-DOD6CPO6.js → package-RTN2PS7O.js} +5 -3
- package/dist/{read-QVAXMTPV.js → read-YSWUXYL7.js} +1 -1
- package/dist/{register-Y2H4PY5R.js → register-42VBJKTP.js} +3 -3
- package/dist/{registry-OUZBZ3Q4.js → registry-UMMDRPRM.js} +2 -2
- package/dist/{reject-EV35X64Y.js → reject-SXGCYN4A.js} +1 -1
- package/dist/{restart-FJQ662XJ.js → restart-YYEGZJDD.js} +5 -5
- package/dist/{sandbox-ZJ7QA6Z6.js → sandbox-5VQ454XP.js} +4 -4
- package/dist/scheduler-I7JZCT47.js +34 -0
- package/dist/{schema-PVNG3PQM.js → schema-VEVO7HG6.js} +3 -1
- package/dist/{seed-CNYIHPOH.js → seed-F2HXZQU4.js} +1 -1
- package/dist/{seed-check-VLTVILOV.js → seed-check-TYD7RRZY.js} +1 -1
- package/dist/{seed-cmd-AVNUNLZV.js → seed-cmd-MLCTGSX5.js} +3 -3
- package/dist/{seed-create-ANXGYSCC.js → seed-create-KQ7OILWB.js} +6 -4
- package/dist/seed-readiness-RBPGOLSZ.js +18 -0
- package/dist/{seed-sprout-NIUFTX32.js → seed-sprout-HVYCFJLA.js} +30 -31
- package/dist/{send-SPE6DVNJ.js → send-TD2X5U3K.js} +6 -6
- package/dist/{service-UOCLXDWR.js → service-CVZMQYK6.js} +4 -4
- package/dist/{service-install-4HHFDDWY.js → service-install-VCRMFWX5.js} +5 -5
- package/dist/{setup-Y2EJI3GG.js → setup-5ECWTYKG.js} +7 -3
- package/dist/{setup-QT2A25SY.js → setup-AE4CVUC4.js} +7 -7
- package/dist/{skill-FKFHIJRR.js → skill-UUM4JZRF.js} +5 -5
- 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 +2 -0
- package/dist/{skills-AKCFA7BN.js → skills-B3SVDCEB.js} +5 -5
- package/dist/sleep-manager-Z27R3WTW.js +38 -0
- package/dist/spirit-WXS3HCLN.js +25 -0
- package/dist/{split-WBMZXBHF.js → split-SKZ4Y2D6.js} +1 -1
- package/dist/{sprout-SMX42ZBP.js → sprout-KQ43BWZJ.js} +1 -1
- package/dist/{src-OJHARHTF.js → src-WLJE73NP.js} +14 -5
- package/dist/{start-5JE3TV66.js → start-O6UKK7BG.js} +3 -3
- package/dist/{status-SWRVGXBI.js → status-F4PGREUU.js} +19 -7
- package/dist/{stop-AAMTG3TU.js → stop-UBX6X5RA.js} +5 -5
- package/dist/system-chat-63ABFF6V.js +40 -0
- package/dist/{systems-VNB5WKSS.js → systems-JJNGQHAO.js} +4 -4
- package/dist/{tailscale-3XOKOW7T.js → tailscale-MCLY5GYE.js} +2 -2
- package/dist/{template-hash-446HHH5V.js → template-hash-GSOUARW4.js} +2 -2
- package/dist/template-staleness-EMQORSGG.js +15 -0
- package/dist/{turn-tracker-FOIBNUUA.js → turn-tracker-KLPV3PAU.js} +6 -6
- package/dist/up-RVGDE6CQ.js +20 -0
- package/dist/{update-SWAYZRWJ.js → update-OXQEGGXD.js} +5 -5
- package/dist/{update-check-PQ2MZ47K.js → update-check-266R4ZSY.js} +3 -3
- package/dist/{upgrade-NWAZ7MLD.js → upgrade-K5FIQPQD.js} +1 -1
- package/dist/{version-notify-C5CH7CVP.js → version-notify-73YURTFR.js} +41 -25
- package/dist/web-assets/assets/index-WdZ60R6c.css +1 -0
- package/dist/web-assets/assets/index-taX6Ezfa.js +92 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0011_drop_turn_summary_event_id.sql +3 -0
- package/drizzle/0012_channel_gates.sql +17 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +5 -3
- 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/src/lib/startup.ts +59 -3
- package/templates/claude/src/agent.ts +37 -4
- package/templates/claude/src/lib/session-reaper.ts +32 -0
- package/templates/claude/src/server.ts +14 -3
- package/templates/pi/src/agent.ts +7 -10
- package/templates/pi/src/lib/dispatch.ts +65 -0
- package/dist/avatar-image-6KY6UU4D.js +0 -16
- package/dist/delivery-manager-JINZRG2W.js +0 -33
- package/dist/echo-text-VQPB5L2X.js +0 -33
- package/dist/message-delivery-R7JSQGYX.js +0 -37
- package/dist/mind-service-NB2JMWQZ.js +0 -39
- package/dist/scheduler-WVKII3LA.js +0 -31
- package/dist/sleep-manager-G6TXSDP7.js +0 -35
- package/dist/spirit-RPBLLUQ5.js +0 -23
- package/dist/system-chat-NDAW6YP7.js +0 -37
- package/dist/up-PS6PFS75.js +0 -18
- package/dist/volute-config-A52FHWKM.js +0 -14
- package/dist/web-assets/assets/index-B1JBQv2m.css +0 -1
- package/dist/web-assets/assets/index-CU6XWICM.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
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getPrompt
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MOERAQXH.js";
|
|
5
5
|
import {
|
|
6
6
|
aiCompleteUtility
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YNBXEQAP.js";
|
|
8
8
|
import {
|
|
9
9
|
logger_default
|
|
10
10
|
} from "./chunk-T2TP6ZC6.js";
|
|
11
11
|
import {
|
|
12
12
|
getDb
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WW6C3PLV.js";
|
|
14
14
|
import {
|
|
15
15
|
messages,
|
|
16
16
|
mindHistory,
|
|
17
17
|
summaries,
|
|
18
18
|
turns
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-2MJ7EOMT.js";
|
|
20
20
|
|
|
21
21
|
// packages/daemon/src/lib/daemon/turn-tracker.ts
|
|
22
22
|
import { randomUUID } from "crypto";
|
|
@@ -82,10 +82,8 @@ function summarizeTool(name, input) {
|
|
|
82
82
|
return `[${name}]`;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
// packages/daemon/src/lib/
|
|
86
|
-
var
|
|
87
|
-
var SYSTEM_MIND = "_system";
|
|
88
|
-
var WEDGED_TURN_IDLE_MS = 15 * 6e4;
|
|
85
|
+
// packages/daemon/src/lib/util/period-keys.ts
|
|
86
|
+
var DATE_STR_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
89
87
|
function getPeriodKey(date, period) {
|
|
90
88
|
switch (period) {
|
|
91
89
|
case "hour": {
|
|
@@ -117,12 +115,27 @@ function getISOWeekKey(date) {
|
|
|
117
115
|
const weekNum = Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
118
116
|
return `${d.getFullYear()}-W${String(weekNum).padStart(2, "0")}`;
|
|
119
117
|
}
|
|
118
|
+
function isoWeekToDate(weekKey) {
|
|
119
|
+
const [yearStr, weekStr] = weekKey.split("-W");
|
|
120
|
+
const year = parseInt(yearStr, 10);
|
|
121
|
+
const week = parseInt(weekStr, 10);
|
|
122
|
+
const jan4 = new Date(year, 0, 4);
|
|
123
|
+
const dayOfWeek = jan4.getDay() || 7;
|
|
124
|
+
const monday = new Date(jan4);
|
|
125
|
+
monday.setDate(jan4.getDate() - dayOfWeek + 1 + (week - 1) * 7);
|
|
126
|
+
return monday;
|
|
127
|
+
}
|
|
128
|
+
function isoWeekKeyForDateStr(dateStr) {
|
|
129
|
+
if (!DATE_STR_RE.test(dateStr)) return dateStr;
|
|
130
|
+
const [y, m, d] = dateStr.split("-").map(Number);
|
|
131
|
+
return getISOWeekKey(new Date(y, m - 1, d));
|
|
132
|
+
}
|
|
120
133
|
function getPreviousPeriodKey(key2, period) {
|
|
121
134
|
switch (period) {
|
|
122
135
|
case "hour": {
|
|
123
136
|
const d = /* @__PURE__ */ new Date(`${key2.slice(0, 10)}T${key2.slice(11)}:00:00`);
|
|
124
|
-
|
|
125
|
-
return getPeriodKey(
|
|
137
|
+
const prev = new Date(d.getTime() - 36e5);
|
|
138
|
+
return getPeriodKey(prev, "hour");
|
|
126
139
|
}
|
|
127
140
|
case "day": {
|
|
128
141
|
const d = /* @__PURE__ */ new Date(`${key2}T00:00:00`);
|
|
@@ -141,16 +154,6 @@ function getPreviousPeriodKey(key2, period) {
|
|
|
141
154
|
}
|
|
142
155
|
}
|
|
143
156
|
}
|
|
144
|
-
function isoWeekToDate(weekKey) {
|
|
145
|
-
const [yearStr, weekStr] = weekKey.split("-W");
|
|
146
|
-
const year = parseInt(yearStr, 10);
|
|
147
|
-
const week = parseInt(weekStr, 10);
|
|
148
|
-
const jan4 = new Date(year, 0, 4);
|
|
149
|
-
const dayOfWeek = jan4.getDay() || 7;
|
|
150
|
-
const monday = new Date(jan4);
|
|
151
|
-
monday.setDate(jan4.getDate() - dayOfWeek + 1 + (week - 1) * 7);
|
|
152
|
-
return monday;
|
|
153
|
-
}
|
|
154
157
|
function localDateStr(d) {
|
|
155
158
|
const y = d.getFullYear();
|
|
156
159
|
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
@@ -160,6 +163,9 @@ function localDateStr(d) {
|
|
|
160
163
|
function utcDateTimeStr(d) {
|
|
161
164
|
return d.toISOString().replace("T", " ").slice(0, 19);
|
|
162
165
|
}
|
|
166
|
+
function parseUtcDateTime(s) {
|
|
167
|
+
return /* @__PURE__ */ new Date(`${s.replace(" ", "T")}Z`);
|
|
168
|
+
}
|
|
163
169
|
function getTimeRange(periodKey, period) {
|
|
164
170
|
switch (period) {
|
|
165
171
|
case "hour": {
|
|
@@ -188,6 +194,11 @@ function getTimeRange(periodKey, period) {
|
|
|
188
194
|
}
|
|
189
195
|
}
|
|
190
196
|
}
|
|
197
|
+
|
|
198
|
+
// packages/daemon/src/lib/daemon/summarizer.ts
|
|
199
|
+
var sLog = logger_default.child("summarizer");
|
|
200
|
+
var SYSTEM_MIND = "_system";
|
|
201
|
+
var WEDGED_TURN_IDLE_MS = 15 * 6e4;
|
|
191
202
|
async function gatherTurnEvents(mind, session, doneId) {
|
|
192
203
|
const db = await getDb();
|
|
193
204
|
const subConditions = [
|
|
@@ -210,6 +221,7 @@ async function gatherTurnEvents(mind, session, doneId) {
|
|
|
210
221
|
session: mindHistory.session,
|
|
211
222
|
content: mindHistory.content,
|
|
212
223
|
metadata: mindHistory.metadata,
|
|
224
|
+
turn_id: mindHistory.turn_id,
|
|
213
225
|
created_at: mindHistory.created_at
|
|
214
226
|
}).from(mindHistory).where(and(...turnConditions)).orderBy(mindHistory.id);
|
|
215
227
|
return {
|
|
@@ -227,6 +239,7 @@ async function gatherTurnEventsByTurnId(turnId) {
|
|
|
227
239
|
session: mindHistory.session,
|
|
228
240
|
content: mindHistory.content,
|
|
229
241
|
metadata: mindHistory.metadata,
|
|
242
|
+
turn_id: mindHistory.turn_id,
|
|
230
243
|
created_at: mindHistory.created_at
|
|
231
244
|
}).from(mindHistory).where(eq(mindHistory.turn_id, turnId)).orderBy(mindHistory.id);
|
|
232
245
|
return {
|
|
@@ -321,19 +334,22 @@ function buildTranscript(events, parsedMeta) {
|
|
|
321
334
|
async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
322
335
|
const { events, fromId, toId } = turnId ? await gatherTurnEventsByTurnId(turnId) : await gatherTurnEvents(mind, session, doneId);
|
|
323
336
|
if (events.length === 0) return;
|
|
337
|
+
const effectiveTurnId = turnId ?? events.find((ev) => ev.turn_id)?.turn_id ?? void 0;
|
|
338
|
+
if (effectiveTurnId && await summaryExists(mind, "turn", effectiveTurnId)) return;
|
|
324
339
|
const substantiveTypes = /* @__PURE__ */ new Set(["text", "outbound", "tool_use", "tool_result", "thinking"]);
|
|
325
340
|
const hasSubstantiveOutput = events.some((ev) => substantiveTypes.has(ev.type));
|
|
326
341
|
if (!hasSubstantiveOutput) {
|
|
327
342
|
sLog.info(
|
|
328
|
-
`skipping summary for interrupted turn ${
|
|
343
|
+
`skipping summary for interrupted turn ${effectiveTurnId ?? "(no turn)"} (no substantive output)`
|
|
329
344
|
);
|
|
330
|
-
if (
|
|
345
|
+
if (effectiveTurnId) {
|
|
331
346
|
try {
|
|
332
347
|
const db2 = await getDb();
|
|
333
|
-
await db2.update(mindHistory).set({ turn_id: null }).where(and(eq(mindHistory.turn_id,
|
|
334
|
-
await db2.update(messages).set({ turn_id: null }).where(eq(messages.turn_id,
|
|
348
|
+
await db2.update(mindHistory).set({ turn_id: null }).where(and(eq(mindHistory.turn_id, effectiveTurnId), eq(mindHistory.type, "inbound")));
|
|
349
|
+
await db2.update(messages).set({ turn_id: null }).where(eq(messages.turn_id, effectiveTurnId));
|
|
350
|
+
await db2.delete(turns).where(eq(turns.id, effectiveTurnId));
|
|
335
351
|
} catch (err) {
|
|
336
|
-
sLog.error(`failed to
|
|
352
|
+
sLog.error(`failed to clean up interrupted turn ${effectiveTurnId}`, logger_default.errorData(err));
|
|
337
353
|
}
|
|
338
354
|
}
|
|
339
355
|
return;
|
|
@@ -374,7 +390,7 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
374
390
|
from_time: fromTime,
|
|
375
391
|
to_time: toTime
|
|
376
392
|
};
|
|
377
|
-
const periodKey =
|
|
393
|
+
const periodKey = effectiveTurnId ?? `${mind}-${doneId}`;
|
|
378
394
|
const db = await getDb();
|
|
379
395
|
let summaryId;
|
|
380
396
|
try {
|
|
@@ -403,9 +419,9 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
403
419
|
);
|
|
404
420
|
return;
|
|
405
421
|
}
|
|
406
|
-
if (
|
|
407
|
-
setSummaryId(
|
|
408
|
-
sLog.error(`failed to link summary to turn ${
|
|
422
|
+
if (effectiveTurnId && summaryId != null) {
|
|
423
|
+
setSummaryId(effectiveTurnId, summaryId).catch((err) => {
|
|
424
|
+
sLog.error(`failed to link summary to turn ${effectiveTurnId}`, logger_default.errorData(err));
|
|
409
425
|
});
|
|
410
426
|
}
|
|
411
427
|
publish(mind, {
|
|
@@ -415,7 +431,7 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
415
431
|
channel,
|
|
416
432
|
content: summaryText,
|
|
417
433
|
metadata,
|
|
418
|
-
turnId
|
|
434
|
+
turnId: effectiveTurnId
|
|
419
435
|
});
|
|
420
436
|
}
|
|
421
437
|
async function setSummaryId(turnId, summaryId) {
|
|
@@ -439,31 +455,107 @@ function getScopeInstruction(mind) {
|
|
|
439
455
|
}
|
|
440
456
|
return 'Write in first person as the mind who performed the actions (e.g. "I explored...", "I worked on...").';
|
|
441
457
|
}
|
|
442
|
-
|
|
443
|
-
|
|
458
|
+
var DIGEST_CHILD_CHARS = 200;
|
|
459
|
+
var DIGEST_MAX_CHARS = 4e3;
|
|
460
|
+
var DIGEST_NOTICE = "(auto-generated digest \u2014 AI summary pending)";
|
|
461
|
+
var ROLLUP_CHILD_CHARS = 2e3;
|
|
462
|
+
var PROVISIONAL_MAX_ATTEMPTS = 5;
|
|
463
|
+
var PROVISIONAL_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
464
|
+
var PROVISIONAL_RETRY_BACKOFF_MS = PROVISIONAL_WINDOW_MS / PROVISIONAL_MAX_ATTEMPTS;
|
|
465
|
+
function truncateChars(text, max) {
|
|
466
|
+
const t = text.trim();
|
|
467
|
+
return t.length > max ? `${t.slice(0, max).trimEnd()}\u2026` : t;
|
|
468
|
+
}
|
|
469
|
+
function digestLine(text) {
|
|
470
|
+
const flat = text.trim().replace(/\s+/g, " ");
|
|
471
|
+
const match = flat.match(/^.*?[.!?](?=\s|$)/);
|
|
472
|
+
const line = match ? match[0] : flat;
|
|
473
|
+
return line.length > DIGEST_CHILD_CHARS ? `${line.slice(0, DIGEST_CHILD_CHARS).trimEnd()}\u2026` : line;
|
|
474
|
+
}
|
|
475
|
+
function buildBoundedDigest(entries, period, periodKey) {
|
|
476
|
+
const label = period === "week" ? `Week ${periodKey}` : periodKey;
|
|
477
|
+
const header = `${label} ${DIGEST_NOTICE}`;
|
|
478
|
+
const lines = [];
|
|
479
|
+
let total = header.length + 2;
|
|
480
|
+
for (let i = 0; i < entries.length; i++) {
|
|
481
|
+
const line = `${entries[i].key}: ${digestLine(entries[i].text)}`;
|
|
482
|
+
if (lines.length > 0 && total + line.length + 1 > DIGEST_MAX_CHARS) {
|
|
483
|
+
lines.push(`\u2026and ${entries.length - i} more`);
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
lines.push(line);
|
|
487
|
+
total += line.length + 1;
|
|
488
|
+
}
|
|
489
|
+
return `${header}
|
|
490
|
+
|
|
491
|
+
${lines.join("\n")}`;
|
|
492
|
+
}
|
|
493
|
+
function buildPeriodicDeterministicSummary(entries, period, periodKey) {
|
|
494
|
+
if (entries.length === 0) return "";
|
|
444
495
|
switch (period) {
|
|
445
496
|
case "hour":
|
|
446
|
-
return `Activity during ${periodKey.slice(11)}:00: ${
|
|
497
|
+
return `Activity during ${periodKey.slice(11)}:00: ${entries.map((e) => e.text).join(" ")}`;
|
|
447
498
|
case "day":
|
|
448
499
|
return `Activity on ${periodKey}:
|
|
449
500
|
|
|
450
|
-
${
|
|
501
|
+
${entries.map((e) => e.text).join("\n\n")}`;
|
|
451
502
|
case "week":
|
|
452
|
-
return `Week ${periodKey} summary:
|
|
453
|
-
|
|
454
|
-
${sources.join("\n\n")}`;
|
|
455
503
|
case "month":
|
|
456
|
-
return
|
|
504
|
+
return buildBoundedDigest(entries, period, periodKey);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
var DIGEST_HEADER_PREFIX = new RegExp(
|
|
508
|
+
`^(?:Week )?\\S+ ${DIGEST_NOTICE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\n\\n`
|
|
509
|
+
);
|
|
510
|
+
function stripPeriodPrefix(text) {
|
|
511
|
+
return text.replace(/^Activity during \d{2}:00: /, "").replace(/^Activity on \d{4}-\d{2}-\d{2}:\n\n/, "").replace(DIGEST_HEADER_PREFIX, "");
|
|
512
|
+
}
|
|
513
|
+
function buildSystemDeterministicSummary(entries, period, periodKey) {
|
|
514
|
+
if (entries.length === 0) return "";
|
|
515
|
+
const stripped = entries.map((e) => ({ key: e.key, text: stripPeriodPrefix(e.text) }));
|
|
516
|
+
switch (period) {
|
|
517
|
+
case "hour":
|
|
518
|
+
return `Activity during ${periodKey.slice(11)}:00: ${stripped.map((e) => `[${e.key}] ${e.text}`).join(" ")}`;
|
|
519
|
+
case "day":
|
|
520
|
+
return `Activity on ${periodKey}:
|
|
457
521
|
|
|
458
|
-
${
|
|
522
|
+
${stripped.map((e) => `[${e.key}] ${e.text}`).join("\n\n")}`;
|
|
523
|
+
case "week":
|
|
524
|
+
case "month":
|
|
525
|
+
return buildBoundedDigest(stripped, period, periodKey);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
function parseMeta(raw) {
|
|
529
|
+
if (!raw) return {};
|
|
530
|
+
try {
|
|
531
|
+
return JSON.parse(raw);
|
|
532
|
+
} catch {
|
|
533
|
+
return {};
|
|
459
534
|
}
|
|
460
535
|
}
|
|
536
|
+
function shouldRetry(period, meta) {
|
|
537
|
+
if (period !== "week" && period !== "month") return false;
|
|
538
|
+
if (meta.deterministic !== true) return false;
|
|
539
|
+
const attempts = typeof meta.attempts === "number" ? meta.attempts : 0;
|
|
540
|
+
if (attempts >= PROVISIONAL_MAX_ATTEMPTS) return false;
|
|
541
|
+
const first = typeof meta.first_attempt_at === "string" ? Date.parse(meta.first_attempt_at) : NaN;
|
|
542
|
+
if (!Number.isNaN(first) && Date.now() - first > PROVISIONAL_WINDOW_MS) return false;
|
|
543
|
+
const last = typeof meta.last_attempt_at === "string" ? Date.parse(meta.last_attempt_at) : NaN;
|
|
544
|
+
if (!Number.isNaN(last) && Date.now() - last < PROVISIONAL_RETRY_BACKOFF_MS) return false;
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
function trackProvisionalAttempt(metadata, prev) {
|
|
548
|
+
const prevAttempts = prev && typeof prev.attempts === "number" ? prev.attempts : 0;
|
|
549
|
+
metadata.attempts = prevAttempts + 1;
|
|
550
|
+
metadata.first_attempt_at = prev && typeof prev.first_attempt_at === "string" ? prev.first_attempt_at : (/* @__PURE__ */ new Date()).toISOString();
|
|
551
|
+
metadata.last_attempt_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
552
|
+
}
|
|
461
553
|
async function gatherChildSummaries(mind, period, periodKey) {
|
|
462
554
|
const db = await getDb();
|
|
463
555
|
const childPeriod = getChildPeriod(period);
|
|
464
556
|
if (period === "hour") {
|
|
465
557
|
const { start: start2, end: end2 } = getTimeRange(periodKey, "hour");
|
|
466
|
-
const rows2 = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
558
|
+
const rows2 = await db.select({ id: summaries.id, content: summaries.content, key: summaries.period_key }).from(summaries).where(
|
|
467
559
|
and(
|
|
468
560
|
eq(summaries.mind, mind),
|
|
469
561
|
eq(summaries.period, childPeriod),
|
|
@@ -473,11 +565,12 @@ async function gatherChildSummaries(mind, period, periodKey) {
|
|
|
473
565
|
).orderBy(summaries.created_at);
|
|
474
566
|
return {
|
|
475
567
|
texts: rows2.map((r) => r.content),
|
|
476
|
-
sourceIds: rows2.map((r) => r.id)
|
|
568
|
+
sourceIds: rows2.map((r) => r.id),
|
|
569
|
+
keys: rows2.map((r) => r.key)
|
|
477
570
|
};
|
|
478
571
|
}
|
|
479
572
|
if (period === "day") {
|
|
480
|
-
const rows2 = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
573
|
+
const rows2 = await db.select({ id: summaries.id, content: summaries.content, key: summaries.period_key }).from(summaries).where(
|
|
481
574
|
and(
|
|
482
575
|
eq(summaries.mind, mind),
|
|
483
576
|
eq(summaries.period, childPeriod),
|
|
@@ -486,13 +579,14 @@ async function gatherChildSummaries(mind, period, periodKey) {
|
|
|
486
579
|
).orderBy(summaries.period_key);
|
|
487
580
|
return {
|
|
488
581
|
texts: rows2.map((r) => r.content),
|
|
489
|
-
sourceIds: rows2.map((r) => r.id)
|
|
582
|
+
sourceIds: rows2.map((r) => r.id),
|
|
583
|
+
keys: rows2.map((r) => r.key)
|
|
490
584
|
};
|
|
491
585
|
}
|
|
492
586
|
const { start, end } = getTimeRange(periodKey, period);
|
|
493
587
|
const startKey = start.slice(0, 10);
|
|
494
588
|
const endKey = end.slice(0, 10);
|
|
495
|
-
const rows = await db.select({ id: summaries.id, content: summaries.content }).from(summaries).where(
|
|
589
|
+
const rows = await db.select({ id: summaries.id, content: summaries.content, key: summaries.period_key }).from(summaries).where(
|
|
496
590
|
and(
|
|
497
591
|
eq(summaries.mind, mind),
|
|
498
592
|
eq(summaries.period, childPeriod),
|
|
@@ -502,15 +596,24 @@ async function gatherChildSummaries(mind, period, periodKey) {
|
|
|
502
596
|
).orderBy(summaries.period_key);
|
|
503
597
|
return {
|
|
504
598
|
texts: rows.map((r) => r.content),
|
|
505
|
-
sourceIds: rows.map((r) => r.id)
|
|
599
|
+
sourceIds: rows.map((r) => r.id),
|
|
600
|
+
keys: rows.map((r) => r.key)
|
|
506
601
|
};
|
|
507
602
|
}
|
|
508
|
-
async function summarizePeriod(mind, period, periodKey) {
|
|
509
|
-
if (await summaryExists(mind, period, periodKey)) return false;
|
|
603
|
+
async function summarizePeriod(mind, period, periodKey, complete = aiCompleteUtility) {
|
|
510
604
|
const db = await getDb();
|
|
605
|
+
const existing = await db.select({ id: summaries.id, metadata: summaries.metadata }).from(summaries).where(
|
|
606
|
+
and(
|
|
607
|
+
eq(summaries.mind, mind),
|
|
608
|
+
eq(summaries.period, period),
|
|
609
|
+
eq(summaries.period_key, periodKey)
|
|
610
|
+
)
|
|
611
|
+
).get();
|
|
612
|
+
const existingMeta = existing ? parseMeta(existing.metadata) : null;
|
|
613
|
+
if (existing && !shouldRetry(period, existingMeta)) return false;
|
|
511
614
|
const sources = await gatherChildSummaries(mind, period, periodKey);
|
|
512
615
|
if (sources.texts.length === 0) return false;
|
|
513
|
-
if (sources.texts.length === 1) {
|
|
616
|
+
if (!existing && sources.texts.length === 1) {
|
|
514
617
|
try {
|
|
515
618
|
await db.insert(summaries).values({
|
|
516
619
|
mind,
|
|
@@ -534,33 +637,39 @@ async function summarizePeriod(mind, period, periodKey) {
|
|
|
534
637
|
sLog.info(`promoted single-child ${period} summary for ${mind} (${periodKey})`);
|
|
535
638
|
return true;
|
|
536
639
|
}
|
|
640
|
+
const entries = sources.texts.map((text, i) => ({ key: sources.keys[i], text }));
|
|
537
641
|
const promptKey = `meta_summary_${period}`;
|
|
538
642
|
const scopeInstruction = getScopeInstruction(mind);
|
|
539
643
|
const systemPrompt = await getPrompt(promptKey, { scope_instruction: scopeInstruction });
|
|
540
644
|
const userMessage = sources.texts.join("\n\n---\n\n");
|
|
541
645
|
let content;
|
|
542
646
|
let deterministic;
|
|
543
|
-
const
|
|
647
|
+
const metadata = {
|
|
648
|
+
source_count: sources.texts.length,
|
|
649
|
+
source_ids: sources.sourceIds
|
|
650
|
+
};
|
|
651
|
+
const aiResult = await complete(systemPrompt, userMessage);
|
|
544
652
|
if (aiResult) {
|
|
545
653
|
content = aiResult;
|
|
546
654
|
deterministic = false;
|
|
547
655
|
} else {
|
|
548
|
-
content = buildPeriodicDeterministicSummary(
|
|
656
|
+
content = buildPeriodicDeterministicSummary(entries, period, periodKey);
|
|
549
657
|
deterministic = true;
|
|
658
|
+
if (period === "week" || period === "month") trackProvisionalAttempt(metadata, existingMeta);
|
|
550
659
|
}
|
|
551
|
-
|
|
552
|
-
deterministic,
|
|
553
|
-
source_count: sources.texts.length,
|
|
554
|
-
source_ids: sources.sourceIds
|
|
555
|
-
};
|
|
660
|
+
metadata.deterministic = deterministic;
|
|
556
661
|
try {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
662
|
+
if (existing) {
|
|
663
|
+
await db.update(summaries).set({ content, metadata: JSON.stringify(metadata) }).where(eq(summaries.id, existing.id));
|
|
664
|
+
} else {
|
|
665
|
+
await db.insert(summaries).values({
|
|
666
|
+
mind,
|
|
667
|
+
period,
|
|
668
|
+
period_key: periodKey,
|
|
669
|
+
content,
|
|
670
|
+
metadata: JSON.stringify(metadata)
|
|
671
|
+
}).onConflictDoNothing();
|
|
672
|
+
}
|
|
564
673
|
} catch (err) {
|
|
565
674
|
sLog.error(
|
|
566
675
|
`failed to persist ${period} summary for ${mind} (${periodKey})`,
|
|
@@ -573,9 +682,17 @@ async function summarizePeriod(mind, period, periodKey) {
|
|
|
573
682
|
);
|
|
574
683
|
return true;
|
|
575
684
|
}
|
|
576
|
-
async function summarizeSystem(period, periodKey) {
|
|
577
|
-
if (await summaryExists(SYSTEM_MIND, period, periodKey)) return;
|
|
685
|
+
async function summarizeSystem(period, periodKey, complete = aiCompleteUtility) {
|
|
578
686
|
const db = await getDb();
|
|
687
|
+
const existing = await db.select({ id: summaries.id, metadata: summaries.metadata }).from(summaries).where(
|
|
688
|
+
and(
|
|
689
|
+
eq(summaries.mind, SYSTEM_MIND),
|
|
690
|
+
eq(summaries.period, period),
|
|
691
|
+
eq(summaries.period_key, periodKey)
|
|
692
|
+
)
|
|
693
|
+
).get();
|
|
694
|
+
const existingMeta = existing ? parseMeta(existing.metadata) : null;
|
|
695
|
+
if (existing && !shouldRetry(period, existingMeta)) return;
|
|
579
696
|
const rows = await db.select({ mind: summaries.mind, content: summaries.content }).from(summaries).where(
|
|
580
697
|
and(
|
|
581
698
|
eq(summaries.period, period),
|
|
@@ -585,33 +702,69 @@ async function summarizeSystem(period, periodKey) {
|
|
|
585
702
|
).orderBy(summaries.mind);
|
|
586
703
|
if (rows.length === 0) return;
|
|
587
704
|
const minds = [...new Set(rows.map((r) => r.mind))];
|
|
588
|
-
const
|
|
705
|
+
const entries = rows.map((r) => ({
|
|
706
|
+
key: r.mind,
|
|
707
|
+
text: truncateChars(r.content, ROLLUP_CHILD_CHARS)
|
|
708
|
+
}));
|
|
709
|
+
const texts = entries.map((e) => `[${e.key}] ${e.text}`);
|
|
589
710
|
const promptKey = `meta_summary_${period}`;
|
|
590
711
|
const scopeInstruction = getScopeInstruction(SYSTEM_MIND);
|
|
591
712
|
const systemPrompt = await getPrompt(promptKey, { scope_instruction: scopeInstruction });
|
|
592
713
|
const userMessage = texts.join("\n\n---\n\n");
|
|
593
714
|
let content;
|
|
594
715
|
let deterministic;
|
|
595
|
-
const
|
|
716
|
+
const metadata = { minds, source_count: rows.length };
|
|
717
|
+
const aiResult = await complete(systemPrompt, userMessage);
|
|
596
718
|
if (aiResult) {
|
|
597
719
|
content = aiResult;
|
|
598
720
|
deterministic = false;
|
|
599
721
|
} else {
|
|
600
|
-
content =
|
|
722
|
+
content = buildSystemDeterministicSummary(entries, period, periodKey);
|
|
601
723
|
deterministic = true;
|
|
724
|
+
if (period === "week" || period === "month") trackProvisionalAttempt(metadata, existingMeta);
|
|
602
725
|
}
|
|
726
|
+
metadata.deterministic = deterministic;
|
|
603
727
|
try {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
728
|
+
if (existing) {
|
|
729
|
+
await db.update(summaries).set({ content, metadata: JSON.stringify(metadata) }).where(eq(summaries.id, existing.id));
|
|
730
|
+
} else {
|
|
731
|
+
await db.insert(summaries).values({
|
|
732
|
+
mind: SYSTEM_MIND,
|
|
733
|
+
period,
|
|
734
|
+
period_key: periodKey,
|
|
735
|
+
content,
|
|
736
|
+
metadata: JSON.stringify(metadata)
|
|
737
|
+
}).onConflictDoNothing();
|
|
738
|
+
}
|
|
611
739
|
} catch (err) {
|
|
612
740
|
sLog.error(`failed to persist system ${period} summary (${periodKey})`, logger_default.errorData(err));
|
|
613
741
|
}
|
|
614
742
|
}
|
|
743
|
+
async function repairProvisionalSummaries(complete = aiCompleteUtility) {
|
|
744
|
+
const db = await getDb();
|
|
745
|
+
const rows = await db.select({
|
|
746
|
+
mind: summaries.mind,
|
|
747
|
+
period: summaries.period,
|
|
748
|
+
period_key: summaries.period_key,
|
|
749
|
+
metadata: summaries.metadata
|
|
750
|
+
}).from(summaries).where(inArray(summaries.period, ["week", "month"]));
|
|
751
|
+
const due = rows.filter((r) => shouldRetry(r.period, parseMeta(r.metadata)));
|
|
752
|
+
due.sort((a, b) => (a.mind === SYSTEM_MIND ? 1 : 0) - (b.mind === SYSTEM_MIND ? 1 : 0));
|
|
753
|
+
for (const r of due) {
|
|
754
|
+
try {
|
|
755
|
+
if (r.mind === SYSTEM_MIND) {
|
|
756
|
+
await summarizeSystem(r.period, r.period_key, complete);
|
|
757
|
+
} else {
|
|
758
|
+
await summarizePeriod(r.mind, r.period, r.period_key, complete);
|
|
759
|
+
}
|
|
760
|
+
} catch (err) {
|
|
761
|
+
sLog.error(
|
|
762
|
+
`failed to repair provisional ${r.period} summary for ${r.mind} (${r.period_key})`,
|
|
763
|
+
logger_default.errorData(err)
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
615
768
|
async function mindsWithTurnSummaries(start, end) {
|
|
616
769
|
const db = await getDb();
|
|
617
770
|
const rows = await db.select({ mind: summaries.mind }).from(summaries).where(
|
|
@@ -756,6 +909,95 @@ async function backfill() {
|
|
|
756
909
|
}
|
|
757
910
|
}
|
|
758
911
|
}
|
|
912
|
+
async function healMissing(period, needed, process) {
|
|
913
|
+
if (needed.size === 0) return;
|
|
914
|
+
const db = await getDb();
|
|
915
|
+
const keys = [...needed.keys()];
|
|
916
|
+
const existing = await db.select({ mind: summaries.mind, period_key: summaries.period_key }).from(summaries).where(and(eq(summaries.period, period), inArray(summaries.period_key, keys)));
|
|
917
|
+
const have = new Set(existing.map((r) => `${r.mind}|${r.period_key}`));
|
|
918
|
+
for (const [key2, minds] of needed) {
|
|
919
|
+
let gap = !have.has(`${SYSTEM_MIND}|${key2}`);
|
|
920
|
+
if (!gap) {
|
|
921
|
+
for (const mind of minds) {
|
|
922
|
+
if (!have.has(`${mind}|${key2}`)) {
|
|
923
|
+
gap = true;
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
if (!gap) continue;
|
|
929
|
+
try {
|
|
930
|
+
await process(key2);
|
|
931
|
+
} catch (err) {
|
|
932
|
+
sLog.error(`reconcile ${period} ${key2} failed`, logger_default.errorData(err));
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
function addNeeded(map, key2, mind) {
|
|
937
|
+
let set = map.get(key2);
|
|
938
|
+
if (!set) {
|
|
939
|
+
set = /* @__PURE__ */ new Set();
|
|
940
|
+
map.set(key2, set);
|
|
941
|
+
}
|
|
942
|
+
set.add(mind);
|
|
943
|
+
}
|
|
944
|
+
async function reconcileMissingSummaries(lookbackDays = 7) {
|
|
945
|
+
const db = await getDb();
|
|
946
|
+
const now = /* @__PURE__ */ new Date();
|
|
947
|
+
const windowStart = new Date(now);
|
|
948
|
+
windowStart.setDate(windowStart.getDate() - lookbackDays);
|
|
949
|
+
const currentHourKey = getPeriodKey(now, "hour");
|
|
950
|
+
const currentDayKey = getPeriodKey(now, "day");
|
|
951
|
+
const currentWeekKey = getPeriodKey(now, "week");
|
|
952
|
+
const currentMonthKey = getPeriodKey(now, "month");
|
|
953
|
+
const cutoffDayKey = getPeriodKey(windowStart, "day");
|
|
954
|
+
const turnRows = await db.select({ mind: summaries.mind, created_at: summaries.created_at }).from(summaries).where(
|
|
955
|
+
and(
|
|
956
|
+
eq(summaries.period, "turn"),
|
|
957
|
+
gte(summaries.created_at, utcDateTimeStr(windowStart)),
|
|
958
|
+
sql`${summaries.mind} != ${SYSTEM_MIND}`
|
|
959
|
+
)
|
|
960
|
+
);
|
|
961
|
+
const neededHours = /* @__PURE__ */ new Map();
|
|
962
|
+
for (const r of turnRows) {
|
|
963
|
+
const key2 = getPeriodKey(parseUtcDateTime(r.created_at), "hour");
|
|
964
|
+
if (key2 === currentHourKey) continue;
|
|
965
|
+
addNeeded(neededHours, key2, r.mind);
|
|
966
|
+
}
|
|
967
|
+
await healMissing("hour", neededHours, processHour);
|
|
968
|
+
const hourRows = await db.select({ mind: summaries.mind, period_key: summaries.period_key }).from(summaries).where(
|
|
969
|
+
and(
|
|
970
|
+
eq(summaries.period, "hour"),
|
|
971
|
+
gte(summaries.period_key, cutoffDayKey),
|
|
972
|
+
sql`${summaries.mind} != ${SYSTEM_MIND}`
|
|
973
|
+
)
|
|
974
|
+
);
|
|
975
|
+
const neededDays = /* @__PURE__ */ new Map();
|
|
976
|
+
for (const r of hourRows) {
|
|
977
|
+
const key2 = r.period_key.slice(0, 10);
|
|
978
|
+
if (key2 === currentDayKey) continue;
|
|
979
|
+
addNeeded(neededDays, key2, r.mind);
|
|
980
|
+
}
|
|
981
|
+
await healMissing("day", neededDays, processDay);
|
|
982
|
+
const dayRows = await db.select({ mind: summaries.mind, period_key: summaries.period_key }).from(summaries).where(
|
|
983
|
+
and(
|
|
984
|
+
eq(summaries.period, "day"),
|
|
985
|
+
gte(summaries.period_key, cutoffDayKey),
|
|
986
|
+
sql`${summaries.mind} != ${SYSTEM_MIND}`
|
|
987
|
+
)
|
|
988
|
+
);
|
|
989
|
+
const neededWeeks = /* @__PURE__ */ new Map();
|
|
990
|
+
const neededMonths = /* @__PURE__ */ new Map();
|
|
991
|
+
for (const r of dayRows) {
|
|
992
|
+
const d = /* @__PURE__ */ new Date(`${r.period_key}T00:00:00`);
|
|
993
|
+
const weekKey = getPeriodKey(d, "week");
|
|
994
|
+
if (weekKey !== currentWeekKey) addNeeded(neededWeeks, weekKey, r.mind);
|
|
995
|
+
const monthKey = r.period_key.slice(0, 7);
|
|
996
|
+
if (monthKey !== currentMonthKey) addNeeded(neededMonths, monthKey, r.mind);
|
|
997
|
+
}
|
|
998
|
+
await healMissing("week", neededWeeks, processWeek);
|
|
999
|
+
await healMissing("month", neededMonths, processMonth);
|
|
1000
|
+
}
|
|
759
1001
|
var Summarizer = class {
|
|
760
1002
|
interval = null;
|
|
761
1003
|
lastHourKey = null;
|
|
@@ -777,6 +1019,7 @@ var Summarizer = class {
|
|
|
777
1019
|
this.hasBackfilled = true;
|
|
778
1020
|
}
|
|
779
1021
|
await reconcileWedgedTurns(WEDGED_TURN_IDLE_MS);
|
|
1022
|
+
await reconcileMissingSummaries();
|
|
780
1023
|
const now = /* @__PURE__ */ new Date();
|
|
781
1024
|
const currentHourKey = getPeriodKey(now, "hour");
|
|
782
1025
|
if (this.lastHourKey && this.lastHourKey !== currentHourKey) {
|
|
@@ -799,17 +1042,18 @@ var Summarizer = class {
|
|
|
799
1042
|
if (!await summaryExists(SYSTEM_MIND, "month", prevMonthKey)) {
|
|
800
1043
|
await processMonth(prevMonthKey);
|
|
801
1044
|
}
|
|
1045
|
+
await repairProvisionalSummaries();
|
|
802
1046
|
} catch (err) {
|
|
803
1047
|
sLog.error("tick failed", logger_default.errorData(err));
|
|
804
1048
|
}
|
|
805
1049
|
}
|
|
806
1050
|
};
|
|
807
1051
|
async function reconcileWedgedTurns(idleMs) {
|
|
808
|
-
const { sweepWedgedTurns: sweepWedgedTurns2, summarizeOrphanedTurns: summarizeOrphanedTurns2 } = await import("./turn-tracker-
|
|
1052
|
+
const { sweepWedgedTurns: sweepWedgedTurns2, summarizeOrphanedTurns: summarizeOrphanedTurns2 } = await import("./turn-tracker-KLPV3PAU.js");
|
|
809
1053
|
const wedged = await sweepWedgedTurns2(idleMs);
|
|
810
1054
|
if (wedged.length === 0) return;
|
|
811
1055
|
summarizeOrphanedTurns2(wedged);
|
|
812
|
-
const { tryGetDeliveryManager } = await import("./delivery-manager-
|
|
1056
|
+
const { tryGetDeliveryManager } = await import("./delivery-manager-HZTT5SJR.js");
|
|
813
1057
|
const dm = tryGetDeliveryManager();
|
|
814
1058
|
if (!dm) return;
|
|
815
1059
|
for (const t of wedged) {
|
|
@@ -1010,6 +1254,8 @@ export {
|
|
|
1010
1254
|
subscribe,
|
|
1011
1255
|
subscribeAll,
|
|
1012
1256
|
publish,
|
|
1257
|
+
isoWeekKeyForDateStr,
|
|
1258
|
+
utcDateTimeStr,
|
|
1013
1259
|
summarizeTurn,
|
|
1014
1260
|
initSummarizer,
|
|
1015
1261
|
markErrored,
|