@cortexkit/opencode-magic-context 0.49.34 → 0.49.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto-search-worker.js +11 -6
- package/dist/chunk-embedding-write-worker.js +16 -14
- package/dist/{compaction-marker-candidates-31h86ph0.js → compaction-marker-candidates-g0p67ced.js} +3 -2
- package/dist/compaction-marker-candidates-worker.js +1 -1
- package/dist/{compartment-chunk-embedding-5q5gj67r.js → compartment-chunk-embedding-y4136are.js} +8 -7
- package/dist/{conflict-warning-hook-xgskea1v.js → conflict-warning-hook-hhw8z5ak.js} +9 -8
- package/dist/{context-p3xrarzt.js → context-fk8e3h9p.js} +1400 -701
- package/dist/{derive-budgets-pqybnx09.js → derive-budgets-htj16n4x.js} +3 -2
- package/dist/embedding-backfill-q6n72sy0.js +27 -0
- package/dist/embedding-worker-process.js +2 -1
- package/dist/embedding-worker.js +5 -4
- package/dist/expand-attachments-h33jbgyz.js +21 -0
- package/dist/features/magic-context/auto-search-diagnostics.d.ts +21 -0
- package/dist/features/magic-context/auto-search-diagnostics.d.ts.map +1 -0
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts +1 -1
- package/dist/features/magic-context/compartment-chunk-embedding.d.ts.map +1 -1
- package/dist/features/magic-context/search.d.ts +4 -0
- package/dist/features/magic-context/search.d.ts.map +1 -1
- package/dist/features/magic-context/store-generation-rebase-labels.d.ts +6 -0
- package/dist/features/magic-context/store-generation-rebase-labels.d.ts.map +1 -0
- package/dist/features/magic-context/store-generation-rebase.d.ts.map +1 -1
- package/dist/{history-owner-36wp3mkb.js → history-owner-0z620wgz.js} +3 -2
- package/dist/hooks/magic-context/apply-operations.d.ts +20 -3
- package/dist/hooks/magic-context/apply-operations.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-observation.d.ts +13 -0
- package/dist/hooks/magic-context/auto-search-observation.d.ts.map +1 -0
- package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
- package/dist/hooks/magic-context/auto-search-worker-client.d.ts +5 -1
- package/dist/hooks/magic-context/auto-search-worker-client.d.ts.map +1 -1
- package/dist/hooks/magic-context/compartment-commit-hashes.d.ts +4 -0
- package/dist/hooks/magic-context/compartment-commit-hashes.d.ts.map +1 -0
- package/dist/hooks/magic-context/compartment-runner-validation.d.ts +1 -0
- package/dist/hooks/magic-context/compartment-runner-validation.d.ts.map +1 -1
- package/dist/hooks/magic-context/event-resolvers.d.ts.map +1 -1
- package/dist/hooks/magic-context/final-wire-completeness.d.ts +15 -0
- package/dist/hooks/magic-context/final-wire-completeness.d.ts.map +1 -0
- package/dist/hooks/magic-context/final-wire-token-estimate.d.ts.map +1 -1
- package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts +1 -0
- package/dist/hooks/magic-context/raw-ordinal-warmup.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-chunk.d.ts +11 -0
- package/dist/hooks/magic-context/read-session-chunk.d.ts.map +1 -1
- package/dist/hooks/magic-context/read-session-formatting.d.ts +2 -0
- package/dist/hooks/magic-context/read-session-formatting.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
- package/dist/hooks/magic-context/transform.d.ts.map +1 -1
- package/dist/{index-as2amvn3.js → index-0c7p8mq5.js} +6 -531
- package/dist/{index-51rdvtkz.js → index-1rhgxqs8.js} +2 -2
- package/dist/index-2amn2s73.js +123 -0
- package/dist/{index-c7phnbvk.js → index-2p37txes.js} +1 -1
- package/dist/index-2qbtqg1w.js +529 -0
- package/dist/{index-waktfbjy.js → index-2v6nvy3k.js} +4 -4
- package/dist/{index-ftr84kkv.js → index-3d3872c6.js} +7 -5
- package/dist/{index-0677w9sd.js → index-3j9t1v4n.js} +55 -21
- package/dist/{index-atekmqef.js → index-4bgk5ztz.js} +363 -5499
- package/dist/{index-b88dwwrh.js → index-4xyjr62e.js} +1 -1
- package/dist/index-66a50f3e.js +777 -0
- package/dist/{index-04z754rd.js → index-87ctnzz9.js} +33 -5
- package/dist/{index-xrnsygfq.js → index-9wnbnx8f.js} +1 -1
- package/dist/{index-97spytxq.js → index-b6xz3qbe.js} +246 -420
- package/dist/{index-7g9dr8gy.js → index-brg2w6qc.js} +113 -133
- package/dist/{index-spwjbkc7.js → index-dnqqqc5f.js} +1 -1
- package/dist/index-dy7w6wm4.js +194 -0
- package/dist/{index-m1x9ghk5.js → index-ezv6dz3y.js} +37 -7
- package/dist/index-f297z1n8.js +4557 -0
- package/dist/{index-qgh1qwjj.js → index-f9b3vtt1.js} +1 -1
- package/dist/{index-v15ne3a3.js → index-faxb7mck.js} +2 -2
- package/dist/{index-rrv26qjz.js → index-fw42ms3m.js} +1 -1
- package/dist/{index-t5pck3ss.js → index-fxbk7gk3.js} +1 -1
- package/dist/{index-wfybj1dw.js → index-g4t7850m.js} +2 -2
- package/dist/{index-fm36bjv4.js → index-gyaa159j.js} +18 -276
- package/dist/{index-4pnsgvfk.js → index-hacf2tyx.js} +1 -1
- package/dist/{index-s1n8dd2n.js → index-jkanybgq.js} +2 -2
- package/dist/{index-2743x98y.js → index-jn0ddrhp.js} +29 -138
- package/dist/{index-h7tgn49j.js → index-jrfre9ah.js} +5 -222
- package/dist/{index-kfyb3k6n.js → index-jz5bk7xd.js} +2 -2
- package/dist/{index-np3rgfw4.js → index-ksv0a31x.js} +3 -3
- package/dist/{index-7kkphb6c.js → index-nj360hcs.js} +4 -4
- package/dist/{index-gkee6p4g.js → index-p56dszfp.js} +3 -3
- package/dist/{index-9gpw8hrm.js → index-pq0dpwrc.js} +4 -4
- package/dist/{index-rfxzazzy.js → index-pswsyf1w.js} +4 -4
- package/dist/{index-y1qwzvxn.js → index-pztqab6k.js} +2 -2
- package/dist/{index-rverk2vz.js → index-qncw9hey.js} +1 -1
- package/dist/index-qx8j6kds.js +270 -0
- package/dist/{index-kq5w68dp.js → index-txs8tce6.js} +1 -1
- package/dist/index-vzhhr1ht.js +532 -0
- package/dist/index-wh9fxds5.js +265 -0
- package/dist/{index-hbx7m8he.js → index-x2mrq5xv.js} +170 -48
- package/dist/{index-07vskjn8.js → index-xwwsbsd3.js} +15 -13
- package/dist/index-z4p0ry94.js +222 -0
- package/dist/index-z9zygwse.js +89 -0
- package/dist/{index-v7287tzq.js → index-zhqwda72.js} +19 -8
- package/dist/index.js +83 -72
- package/dist/{logger-94kew5wm.js → logger-qwnbcfj9.js} +2 -1
- package/dist/{message-fts-rowid-map-va36knh4.js → message-fts-rowid-map-xg7hr0mk.js} +5 -4
- package/dist/{message-index-bounded-qh91fdr3.js → message-index-bounded-narw7bnz.js} +12 -11
- package/dist/{message-time-backfill-4qva0b4f.js → message-time-backfill-39bm498k.js} +3 -2
- package/dist/migration-worker.js +13 -11
- package/dist/{project-embedding-registry-6yex0r9f.js → project-embedding-registry-tdz7985n.js} +13 -11
- package/dist/raw-ordinal-warmup-worker.js +12 -7
- package/dist/{read-session-chunk-kaghtr75.js → read-session-chunk-vmg1zjg0.js} +15 -10
- package/dist/{read-session-raw-b2sd67v4.js → read-session-raw-mncmzbm9.js} +3 -2
- package/dist/recomp-orchestrator-qyyhnc9b.js +68 -0
- package/dist/render-c5p0k846.js +43 -0
- package/dist/{safe-notification-target-mkr9yzvr.js → safe-notification-target-wztd7bgk.js} +3 -2
- package/dist/{search-archive-1kw2rp19.js → search-archive-0ny7p3bw.js} +13 -11
- package/dist/{search-p684j8ww.js → search-f6hwvcxk.js} +26 -23
- package/dist/{send-session-notification-vz86nt79.js → send-session-notification-0gwcz3fw.js} +7 -6
- package/dist/{storage-tags-6hvkk01c.js → storage-tags-zx1023km.js} +5 -4
- package/dist/store-620rjzt9.js +21 -0
- package/dist/store-reader-yh19h38c.js +14 -0
- package/dist/tools/ctx-expand/render.d.ts +5 -3
- package/dist/tools/ctx-expand/render.d.ts.map +1 -1
- package/dist/tools/ctx-expand/search-archive.d.ts.map +1 -1
- package/dist/tools/ctx-expand/tools.d.ts +2 -0
- package/dist/tools/ctx-expand/tools.d.ts.map +1 -1
- package/dist/tools/ctx-expand/v2-archive-reader.d.ts +33 -0
- package/dist/tools/ctx-expand/v2-archive-reader.d.ts.map +1 -0
- package/dist/{transform-postprocess-phase-ph38mhrs.js → transform-postprocess-phase-807ff6t2.js} +36 -30
- package/dist/v2/auto-search-worker.js +11 -6
- package/dist/v2/chunk-embedding-write-worker.js +16 -14
- package/dist/v2/{compaction-marker-candidates-m4vmye3w.js → compaction-marker-candidates-t1aq6gsj.js} +3 -2
- package/dist/v2/compaction-marker-candidates-worker.js +1 -1
- package/dist/v2/{compartment-chunk-embedding-rc31e6md.js → compartment-chunk-embedding-ncsk0r9e.js} +8 -7
- package/dist/v2/{context-s60hcatx.js → context-znqfkap4.js} +1725 -1095
- package/dist/v2/{derive-budgets-knf763tt.js → derive-budgets-sa0v7bz0.js} +4 -3
- package/dist/v2/embedding-backfill-11j425e0.js +26 -0
- package/dist/v2/embedding-worker-process.js +2 -1
- package/dist/v2/embedding-worker.js +5 -4
- package/dist/v2/expand-attachments-h33jbgyz.js +21 -0
- package/dist/v2/fold/idle.d.ts +15 -0
- package/dist/v2/fold/idle.d.ts.map +1 -0
- package/dist/v2/fold/native-replay-v3.d.ts +68 -0
- package/dist/v2/fold/native-replay-v3.d.ts.map +1 -0
- package/dist/v2/fold/native-replay.d.ts +58 -2
- package/dist/v2/fold/native-replay.d.ts.map +1 -1
- package/dist/v2/fold/policy.d.ts +2 -1
- package/dist/v2/fold/policy.d.ts.map +1 -1
- package/dist/v2/{history-owner-ad0crnrn.js → history-owner-fpka43cn.js} +3 -2
- package/dist/v2/hooks/context.d.ts.map +1 -1
- package/dist/v2/hooks/expand-attachments.d.ts +8 -0
- package/dist/v2/hooks/expand-attachments.d.ts.map +1 -0
- package/dist/v2/hooks/overflow-recovery.d.ts +45 -0
- package/dist/v2/hooks/overflow-recovery.d.ts.map +1 -0
- package/dist/v2/hooks/store.d.ts +7 -2
- package/dist/v2/hooks/store.d.ts.map +1 -1
- package/dist/v2/{logger-p34mjtja.js → logger-4cj6d03m.js} +2 -1
- package/dist/v2/{message-fts-rowid-map-78awh03v.js → message-fts-rowid-map-rmk571mb.js} +5 -4
- package/dist/v2/{message-index-bounded-4htdr1j6.js → message-index-bounded-620bppw7.js} +12 -11
- package/dist/v2/{message-time-backfill-zvthw6xb.js → message-time-backfill-0c4h7sg6.js} +3 -2
- package/dist/v2/migration-worker.js +13 -11
- package/dist/v2/ordinal-range.fixture.d.ts +7 -0
- package/dist/v2/ordinal-range.fixture.d.ts.map +1 -0
- package/dist/v2/{project-embedding-registry-1x5qe2fy.js → project-embedding-registry-bm5fe1a8.js} +13 -11
- package/dist/v2/raw-ordinal-warmup-worker.js +12 -7
- package/dist/v2/{read-session-chunk-1fa9qzwb.js → read-session-chunk-qfwv1k71.js} +15 -10
- package/dist/v2/{read-session-raw-5x088amc.js → read-session-raw-q294t8xs.js} +3 -2
- package/dist/v2/recomp-orchestrator-fcnjref7.js +67 -0
- package/dist/v2/render-98a1qv0b.js +42 -0
- package/dist/v2/{safe-notification-target-khnzg57c.js → safe-notification-target-gphhast7.js} +3 -2
- package/dist/v2/{search-archive-xfp51s0g.js → search-archive-r0y1bafr.js} +13 -11
- package/dist/v2/{search-wf8rqxhm.js → search-z1zqzsmh.js} +25 -22
- package/dist/v2/{send-session-notification-x5nzy34v.js → send-session-notification-fst6hkg7.js} +7 -6
- package/dist/v2/{server-qvy4qavp.js → server-1xesqm5h.js} +113 -133
- package/dist/v2/server-2amn2s73.js +123 -0
- package/dist/v2/{server-6g98c227.js → server-3schkz95.js} +4 -4
- package/dist/v2/server-468mya5v.js +209 -0
- package/dist/v2/server-55v5hpth.js +270 -0
- package/dist/v2/server-5jp38p98.js +532 -0
- package/dist/v2/{server-qjvk4a9t.js → server-69m30b21.js} +1 -1
- package/dist/v2/{server-jw5qe9y8.js → server-7ccxt4nq.js} +14 -216
- package/dist/v2/{server-3mz2qh5a.js → server-969cdpmg.js} +1 -1
- package/dist/v2/{server-835q39kd.js → server-9k0mjef7.js} +2 -2
- package/dist/v2/{server-qc915kw2.js → server-bez34e60.js} +4 -4
- package/dist/v2/{server-nxas8v1d.js → server-bpvsvtph.js} +3 -3
- package/dist/v2/{server-ab8017gw.js → server-bxpz2dcn.js} +29 -138
- package/dist/v2/{server-bsgd4dk1.js → server-dcws5831.js} +2 -2
- package/dist/v2/server-ef1mskas.js +777 -0
- package/dist/v2/{server-1bw1wcbp.js → server-f0j864ma.js} +1 -1
- package/dist/v2/{server-z2wft9zv.js → server-fqj6ddpr.js} +402 -5497
- package/dist/v2/{server-grakwy1v.js → server-hnknwaz8.js} +37 -7
- package/dist/v2/{server-h1byrhwx.js → server-hsf0qsn5.js} +19 -8
- package/dist/v2/{server-6495nmn9.js → server-jb6tjyeb.js} +5 -222
- package/dist/v2/{server-2aasmpsp.js → server-kkxzwa6w.js} +4 -4
- package/dist/v2/{server-a7x00tw8.js → server-mfn1yf9d.js} +1 -1
- package/dist/v2/{server-tkdabx8q.js → server-q0z2q2dx.js} +5 -530
- package/dist/v2/{server-ajxhrgjv.js → server-qa74bj91.js} +77 -17
- package/dist/v2/server-qf2kzsh2.js +4513 -0
- package/dist/v2/{server-gyy4k6mf.js → server-qmmkhz2z.js} +1 -1
- package/dist/v2/{server-29qp8nz4.js → server-qxzbmtqt.js} +1 -1
- package/dist/v2/{server-m1pfg2d4.js → server-r3chnxs5.js} +4 -4
- package/dist/v2/{server-vr4szghm.js → server-rktnzs89.js} +2 -2
- package/dist/v2/{server-6vd2n183.js → server-s9xgyyj9.js} +1 -1
- package/dist/v2/{server-53eh1r8r.js → server-sd8hvzte.js} +2 -2
- package/dist/v2/{server-k3t06fxr.js → server-sefxj8k1.js} +1 -1
- package/dist/v2/{server-v77xa5wj.js → server-tpt7bwq8.js} +1 -1
- package/dist/v2/{server-awr8qa4c.js → server-ws5exe6q.js} +2 -2
- package/dist/v2/server-x4m6xcn4.js +194 -0
- package/dist/v2/{server-806rzhs6.js → server-xbtw0p9t.js} +1 -1
- package/dist/v2/{server-cqjvxyrp.js → server-xy7w7hrd.js} +2 -2
- package/dist/v2/server-ypf1ap0v.js +529 -0
- package/dist/v2/server-z4p0ry94.js +222 -0
- package/dist/v2/server-z9zygwse.js +89 -0
- package/dist/v2/{server-ab1g0wn9.js → server-zzgtgqmd.js} +2 -2
- package/dist/v2/server.js +6 -5
- package/dist/v2/{storage-tags-18nbaanf.js → storage-tags-19s0ebf0.js} +5 -4
- package/dist/v2/store-reader-cgx47zkp.js +14 -0
- package/dist/v2/store-reader.d.ts +9 -2
- package/dist/v2/store-reader.d.ts.map +1 -1
- package/dist/v2/store-vdm8v3jd.js +21 -0
- package/dist/v2/v2-archive-reader-t2c0eypn.js +11 -0
- package/dist/v2/{wrapup-orchestrator-wxf7n5k6.js → wrapup-orchestrator-aqpqsveg.js} +46 -39
- package/dist/v2-archive-reader-tm2eayp2.js +11 -0
- package/dist/wrapup-orchestrator-k5yp19a6.js +55 -0
- package/package.json +1 -1
- package/src/shared/sqlite-transaction-mode-guard.test.ts +2 -0
- package/dist/embedding-backfill-0jgtqxen.js +0 -25
- package/dist/recomp-orchestrator-0y983w7h.js +0 -62
- package/dist/v2/embedding-backfill-f43hd8y6.js +0 -24
- package/dist/v2/recomp-orchestrator-7q9nvxte.js +0 -61
- package/dist/wrapup-orchestrator-ys2nrkkb.js +0 -49
|
@@ -0,0 +1,777 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveOpenCodeDbPath,
|
|
3
|
+
assertOpenCodeStoreGeneration
|
|
4
|
+
} from "./index-58e3y90n.js";
|
|
5
|
+
import {
|
|
6
|
+
Database2
|
|
7
|
+
} from "./index-gzy5w8ya.js";
|
|
8
|
+
import {
|
|
9
|
+
sameProjectDirectory
|
|
10
|
+
} from "./index-knvkbfvh.js";
|
|
11
|
+
|
|
12
|
+
// src/v2/store-reader.ts
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
14
|
+
import { statSync } from "node:fs";
|
|
15
|
+
import { hostname } from "node:os";
|
|
16
|
+
import { resolve } from "node:path";
|
|
17
|
+
function gaDatabasePath2(dataHome, channel = "latest", env = process.env) {
|
|
18
|
+
return resolveOpenCodeDbPath("v2", { dataHome, channel, env }).path;
|
|
19
|
+
}
|
|
20
|
+
var RAW_MESSAGE_TYPES2 = [
|
|
21
|
+
"user",
|
|
22
|
+
"synthetic",
|
|
23
|
+
"assistant",
|
|
24
|
+
"skill",
|
|
25
|
+
"shell",
|
|
26
|
+
"system"
|
|
27
|
+
];
|
|
28
|
+
var RAW_MESSAGE_TYPE_PARAMETERS = RAW_MESSAGE_TYPES2.map(() => "?").join(", ");
|
|
29
|
+
var V2_MESSAGE_PAGE_SQL = `WITH bounds AS (
|
|
30
|
+
SELECT
|
|
31
|
+
CASE WHEN ? = 0 THEN -1 ELSE COALESCE((
|
|
32
|
+
SELECT seq FROM session_message
|
|
33
|
+
WHERE session_id = ? AND type IN (${RAW_MESSAGE_TYPE_PARAMETERS})
|
|
34
|
+
ORDER BY seq ASC LIMIT 1 OFFSET ?
|
|
35
|
+
), (SELECT MAX(seq) FROM session_message WHERE session_id = ?), -1) END AS after_seq,
|
|
36
|
+
COALESCE((
|
|
37
|
+
SELECT seq FROM session_message
|
|
38
|
+
WHERE session_id = ? AND type IN (${RAW_MESSAGE_TYPE_PARAMETERS})
|
|
39
|
+
ORDER BY seq ASC LIMIT 1 OFFSET ?
|
|
40
|
+
), ?) AS watermark_seq
|
|
41
|
+
)
|
|
42
|
+
SELECT id, session_id, type, seq, time_created, data FROM session_message, bounds
|
|
43
|
+
WHERE session_id = ?
|
|
44
|
+
AND type IN (${RAW_MESSAGE_TYPE_PARAMETERS})
|
|
45
|
+
AND seq > bounds.after_seq
|
|
46
|
+
AND seq <= bounds.watermark_seq
|
|
47
|
+
ORDER BY seq ASC LIMIT ?`;
|
|
48
|
+
var V2_STORE_READER_DEBUG_COUNTER_KEY = "magic-context.v2.store-reader-debug";
|
|
49
|
+
var debugSymbol = Symbol.for(V2_STORE_READER_DEBUG_COUNTER_KEY);
|
|
50
|
+
var debugGlobal = globalThis;
|
|
51
|
+
var QUERY_STATEMENTS = {
|
|
52
|
+
messagePage: V2_MESSAGE_PAGE_SQL,
|
|
53
|
+
messageCount: "SELECT COUNT(*) AS count FROM session_message WHERE session_id = ? AND type IN (?)",
|
|
54
|
+
storedMessageCount: "SELECT COUNT(*) AS count FROM session_message WHERE session_id = ?",
|
|
55
|
+
messageById: "SELECT id, session_id, type, seq, time_created, data FROM session_message WHERE session_id = ? AND id = ? AND type IN (?) LIMIT 1",
|
|
56
|
+
messageExistsById: "SELECT 1 FROM session_message WHERE id = ? AND session_id = ? AND type IN (?) LIMIT 1",
|
|
57
|
+
messageOrdinalById: "SELECT seq FROM session_message WHERE session_id = ? AND id = ? AND type IN (?) LIMIT 1",
|
|
58
|
+
messageIdOrdinals: "SELECT id FROM session_message WHERE session_id = ? AND seq >= ? AND type IN (?) ORDER BY seq ASC LIMIT ?",
|
|
59
|
+
messageOrdinalPage: "SELECT id, type, seq, json_valid(data) AS valid FROM session_message WHERE session_id = ? AND seq > ? ORDER BY seq ASC, id ASC LIMIT ?",
|
|
60
|
+
rawRowsThrough: "SELECT id, session_id, type, seq, time_created, data FROM session_message WHERE session_id = ? AND type IN (?) AND seq <= ? ORDER BY seq DESC LIMIT ?"
|
|
61
|
+
};
|
|
62
|
+
function debugCounters() {
|
|
63
|
+
const counters = debugGlobal[debugSymbol] ??= {
|
|
64
|
+
decodedRows: 0,
|
|
65
|
+
operations: {},
|
|
66
|
+
openReaders: 0,
|
|
67
|
+
maxOpenReaders: 0,
|
|
68
|
+
readersOpened: 0,
|
|
69
|
+
readersClosed: 0
|
|
70
|
+
};
|
|
71
|
+
counters.openReaders ??= 0;
|
|
72
|
+
counters.maxOpenReaders ??= 0;
|
|
73
|
+
counters.readersOpened ??= 0;
|
|
74
|
+
counters.readersClosed ??= 0;
|
|
75
|
+
return counters;
|
|
76
|
+
}
|
|
77
|
+
function returnedRowCount(value) {
|
|
78
|
+
if (Array.isArray(value))
|
|
79
|
+
return value.length;
|
|
80
|
+
if (value === null || value === undefined || value === false)
|
|
81
|
+
return 0;
|
|
82
|
+
return 1;
|
|
83
|
+
}
|
|
84
|
+
function trackDecodeOperation(name, operation) {
|
|
85
|
+
const counters = debugCounters();
|
|
86
|
+
const before = counters.decodedRows;
|
|
87
|
+
const started = performance.now();
|
|
88
|
+
let value;
|
|
89
|
+
try {
|
|
90
|
+
value = operation();
|
|
91
|
+
return value;
|
|
92
|
+
} finally {
|
|
93
|
+
const decodedRows = counters.decodedRows - before;
|
|
94
|
+
const current = counters.operations[name] ?? {
|
|
95
|
+
calls: 0,
|
|
96
|
+
decodedRows: 0,
|
|
97
|
+
maxDecodedRows: 0
|
|
98
|
+
};
|
|
99
|
+
current.calls += 1;
|
|
100
|
+
current.decodedRows += decodedRows;
|
|
101
|
+
current.maxDecodedRows = Math.max(current.maxDecodedRows, decodedRows);
|
|
102
|
+
counters.operations[name] = current;
|
|
103
|
+
const statement = QUERY_STATEMENTS[name];
|
|
104
|
+
if (counters.captureQueries && statement) {
|
|
105
|
+
(counters.queries ??= []).push({
|
|
106
|
+
operation: name,
|
|
107
|
+
statement,
|
|
108
|
+
rows: returnedRowCount(value),
|
|
109
|
+
elapsedMs: performance.now() - started
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function decode(row) {
|
|
115
|
+
debugCounters().decodedRows += 1;
|
|
116
|
+
const data = JSON.parse(row.data);
|
|
117
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
118
|
+
throw new Error(`Invalid session_message data at seq ${row.seq}`);
|
|
119
|
+
}
|
|
120
|
+
if (row.type === "idle" && !["succeeded", "failed", "interrupted"].includes(String(data.outcome))) {
|
|
121
|
+
throw new Error(`Invalid idle outcome at seq ${row.seq}`);
|
|
122
|
+
}
|
|
123
|
+
return { ...row, data };
|
|
124
|
+
}
|
|
125
|
+
function connect(path, identity = "") {
|
|
126
|
+
const stat = statSync(path);
|
|
127
|
+
const acquiredIdentity = identity || `${stat.dev}:${stat.ino}:${stat.birthtimeMs}`;
|
|
128
|
+
const db = new Database2(path, { readonly: true, fileMustExist: true });
|
|
129
|
+
try {
|
|
130
|
+
assertOpenCodeStoreGeneration(db, "v2", path);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
db.close();
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
const counters = debugCounters();
|
|
136
|
+
counters.openReaders += 1;
|
|
137
|
+
counters.readersOpened += 1;
|
|
138
|
+
counters.maxOpenReaders = Math.max(counters.maxOpenReaders, counters.openReaders);
|
|
139
|
+
return {
|
|
140
|
+
db,
|
|
141
|
+
statements: new Map,
|
|
142
|
+
identity: acquiredIdentity,
|
|
143
|
+
leases: 0,
|
|
144
|
+
retired: false,
|
|
145
|
+
ordinalSequences: new Map
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function closeConnection(connection) {
|
|
149
|
+
connection.statements.clear();
|
|
150
|
+
try {
|
|
151
|
+
connection.db.close();
|
|
152
|
+
} finally {
|
|
153
|
+
const counters = debugCounters();
|
|
154
|
+
counters.openReaders = Math.max(0, counters.openReaders - 1);
|
|
155
|
+
counters.readersClosed += 1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
class V2StoreReaderPool2 {
|
|
160
|
+
capacity;
|
|
161
|
+
connections = new Map;
|
|
162
|
+
closed = false;
|
|
163
|
+
closing = false;
|
|
164
|
+
holds = 0;
|
|
165
|
+
idleWaiters = new Set;
|
|
166
|
+
constructor(capacity = 4) {
|
|
167
|
+
this.capacity = capacity;
|
|
168
|
+
if (!Number.isSafeInteger(capacity) || capacity < 1)
|
|
169
|
+
throw new Error("Invalid reader capacity");
|
|
170
|
+
}
|
|
171
|
+
open(path) {
|
|
172
|
+
return new V2StoreReader2(path, this);
|
|
173
|
+
}
|
|
174
|
+
acquire(path) {
|
|
175
|
+
if (this.closed)
|
|
176
|
+
throw new Error("Store reader pool is closed");
|
|
177
|
+
const key = resolve(path);
|
|
178
|
+
const stat = statSync(key);
|
|
179
|
+
const identity = `${stat.dev}:${stat.ino}:${stat.birthtimeMs}`;
|
|
180
|
+
let connection = this.connections.get(key);
|
|
181
|
+
if (connection && connection.identity !== identity) {
|
|
182
|
+
this.connections.delete(key);
|
|
183
|
+
this.retire(connection);
|
|
184
|
+
connection = undefined;
|
|
185
|
+
}
|
|
186
|
+
if (!connection)
|
|
187
|
+
connection = connect(key, identity);
|
|
188
|
+
connection.leases++;
|
|
189
|
+
this.connections.delete(key);
|
|
190
|
+
this.connections.set(key, connection);
|
|
191
|
+
while (this.connections.size > this.capacity) {
|
|
192
|
+
const oldest = this.connections.entries().next().value;
|
|
193
|
+
if (!oldest)
|
|
194
|
+
break;
|
|
195
|
+
this.connections.delete(oldest[0]);
|
|
196
|
+
this.retire(oldest[1]);
|
|
197
|
+
}
|
|
198
|
+
return connection;
|
|
199
|
+
}
|
|
200
|
+
release(connection) {
|
|
201
|
+
connection.leases--;
|
|
202
|
+
if (connection.retired && connection.leases === 0)
|
|
203
|
+
closeConnection(connection);
|
|
204
|
+
}
|
|
205
|
+
retire(connection) {
|
|
206
|
+
connection.retired = true;
|
|
207
|
+
if (connection.leases === 0)
|
|
208
|
+
closeConnection(connection);
|
|
209
|
+
}
|
|
210
|
+
hold() {
|
|
211
|
+
if (this.closed || this.closing)
|
|
212
|
+
throw new Error("Store reader pool is closed");
|
|
213
|
+
this.holds++;
|
|
214
|
+
let released = false;
|
|
215
|
+
return () => {
|
|
216
|
+
if (released)
|
|
217
|
+
return;
|
|
218
|
+
released = true;
|
|
219
|
+
this.holds--;
|
|
220
|
+
if (this.holds === 0 && this.closing)
|
|
221
|
+
this.finishClose();
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
isClosed() {
|
|
225
|
+
return this.closed;
|
|
226
|
+
}
|
|
227
|
+
holdCount() {
|
|
228
|
+
return this.holds;
|
|
229
|
+
}
|
|
230
|
+
close() {
|
|
231
|
+
if (this.closed || this.closing)
|
|
232
|
+
return;
|
|
233
|
+
this.closing = true;
|
|
234
|
+
if (this.holds === 0)
|
|
235
|
+
this.finishClose();
|
|
236
|
+
}
|
|
237
|
+
async closeWhenIdle(timeoutMs) {
|
|
238
|
+
this.close();
|
|
239
|
+
if (this.closed)
|
|
240
|
+
return true;
|
|
241
|
+
let timer;
|
|
242
|
+
let waiter;
|
|
243
|
+
try {
|
|
244
|
+
await new Promise((resolve) => {
|
|
245
|
+
waiter = resolve;
|
|
246
|
+
this.idleWaiters.add(resolve);
|
|
247
|
+
timer = setTimeout(resolve, Math.max(0, timeoutMs));
|
|
248
|
+
});
|
|
249
|
+
} finally {
|
|
250
|
+
if (timer)
|
|
251
|
+
clearTimeout(timer);
|
|
252
|
+
if (waiter)
|
|
253
|
+
this.idleWaiters.delete(waiter);
|
|
254
|
+
}
|
|
255
|
+
return this.closed;
|
|
256
|
+
}
|
|
257
|
+
finishClose() {
|
|
258
|
+
if (this.closed)
|
|
259
|
+
return;
|
|
260
|
+
this.closed = true;
|
|
261
|
+
for (const connection of this.connections.values())
|
|
262
|
+
this.retire(connection);
|
|
263
|
+
this.connections.clear();
|
|
264
|
+
for (const resolve of this.idleWaiters)
|
|
265
|
+
resolve();
|
|
266
|
+
this.idleWaiters.clear();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function messageTimesQuery(count, withVersions = false) {
|
|
270
|
+
return `SELECT id, COALESCE(time_created, json_extract(data, '$.time.created')) AS created,
|
|
271
|
+
json_extract(data, '$.time.completed') AS completed
|
|
272
|
+
${withVersions ? ", type, seq, time_created AS version_created, time_updated AS version_updated" : ""}
|
|
273
|
+
FROM session_message WHERE +session_id = ? AND id IN (${Array.from({ length: count }, () => "?").join(",")})`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
class V2StoreReader2 {
|
|
277
|
+
path;
|
|
278
|
+
pool;
|
|
279
|
+
db;
|
|
280
|
+
connection;
|
|
281
|
+
closed = false;
|
|
282
|
+
constructor(path, pool) {
|
|
283
|
+
this.path = path;
|
|
284
|
+
this.pool = pool;
|
|
285
|
+
this.connection = pool ? pool.acquire(path) : connect(path);
|
|
286
|
+
this.db = this.connection.db;
|
|
287
|
+
}
|
|
288
|
+
hostIdentity() {
|
|
289
|
+
return createHash("sha256").update(JSON.stringify([hostname(), resolve(this.path), this.connection.identity])).digest("hex");
|
|
290
|
+
}
|
|
291
|
+
prepare(sql) {
|
|
292
|
+
if (this.closed)
|
|
293
|
+
throw new Error("Store reader is closed");
|
|
294
|
+
const statements = this.connection.statements;
|
|
295
|
+
let statement = statements.get(sql);
|
|
296
|
+
if (!statement) {
|
|
297
|
+
statement = this.db.prepare(sql);
|
|
298
|
+
if (statements.size >= 128)
|
|
299
|
+
statements.clear();
|
|
300
|
+
statements.set(sql, statement);
|
|
301
|
+
}
|
|
302
|
+
return statement;
|
|
303
|
+
}
|
|
304
|
+
close() {
|
|
305
|
+
if (this.closed)
|
|
306
|
+
return;
|
|
307
|
+
this.closed = true;
|
|
308
|
+
if (this.pool)
|
|
309
|
+
this.pool.release(this.connection);
|
|
310
|
+
else
|
|
311
|
+
closeConnection(this.connection);
|
|
312
|
+
}
|
|
313
|
+
page(sessionID, options = {}) {
|
|
314
|
+
return trackDecodeOperation("page", () => {
|
|
315
|
+
const limit = options.limit ?? 100;
|
|
316
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1e4)
|
|
317
|
+
throw new Error("Invalid page limit");
|
|
318
|
+
const after = options.after ?? -1;
|
|
319
|
+
if (!Number.isSafeInteger(after))
|
|
320
|
+
throw new Error("Invalid seq cursor");
|
|
321
|
+
if (options.through !== undefined && !Number.isSafeInteger(options.through))
|
|
322
|
+
throw new Error("Invalid seq upper bound");
|
|
323
|
+
const predicates = ["session_id = ?", "seq > ?"];
|
|
324
|
+
const parameters = [sessionID, after];
|
|
325
|
+
if (options.through !== undefined) {
|
|
326
|
+
predicates.push("seq <= ?");
|
|
327
|
+
parameters.push(options.through);
|
|
328
|
+
}
|
|
329
|
+
if (options.type) {
|
|
330
|
+
predicates.push("type = ?");
|
|
331
|
+
parameters.push(options.type);
|
|
332
|
+
}
|
|
333
|
+
const rows = this.prepare(`SELECT id, session_id, type, seq, time_created, data FROM session_message
|
|
334
|
+
WHERE ${predicates.join(" AND ")}
|
|
335
|
+
ORDER BY seq ASC LIMIT ?`).all(...parameters, limit).map(decode);
|
|
336
|
+
return { rows, cursor: rows.at(-1)?.seq };
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
messagePage(sessionID, afterOrdinal, limit, finalWatermark, after) {
|
|
340
|
+
return trackDecodeOperation("messagePage", () => {
|
|
341
|
+
if (!Number.isSafeInteger(afterOrdinal) || afterOrdinal < 0)
|
|
342
|
+
throw new Error("Invalid raw-message ordinal cursor");
|
|
343
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1e4)
|
|
344
|
+
throw new Error("Invalid page limit");
|
|
345
|
+
if (!Number.isSafeInteger(finalWatermark) || finalWatermark < 0)
|
|
346
|
+
throw new Error("Invalid raw-message watermark");
|
|
347
|
+
const pageSize = Math.min(limit, finalWatermark - afterOrdinal);
|
|
348
|
+
if (pageSize <= 0)
|
|
349
|
+
return [];
|
|
350
|
+
if (after) {
|
|
351
|
+
const anchor = this.prepare("SELECT seq FROM session_message WHERE session_id = ? AND id = ?").get(sessionID, after.id);
|
|
352
|
+
if (anchor) {
|
|
353
|
+
return this.prepare(`SELECT id, session_id, type, seq, time_created, data
|
|
354
|
+
FROM session_message WHERE session_id = ? AND seq > ?
|
|
355
|
+
AND type IN (${RAW_MESSAGE_TYPE_PARAMETERS}) ORDER BY seq ASC LIMIT ?`).all(sessionID, anchor.seq, ...RAW_MESSAGE_TYPES2, pageSize).map(decode);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const maximumSeq = Number.MAX_SAFE_INTEGER;
|
|
359
|
+
const rows = this.prepare(V2_MESSAGE_PAGE_SQL).all(afterOrdinal, sessionID, ...RAW_MESSAGE_TYPES2, Math.max(0, afterOrdinal - 1), sessionID, sessionID, ...RAW_MESSAGE_TYPES2, finalWatermark - 1, maximumSeq, sessionID, ...RAW_MESSAGE_TYPES2, pageSize);
|
|
360
|
+
return rows.map(decode);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
messageCount(sessionID) {
|
|
364
|
+
return trackDecodeOperation("messageCount", () => {
|
|
365
|
+
const rawTypes = RAW_MESSAGE_TYPES2.map(() => "?").join(", ");
|
|
366
|
+
const row = this.prepare(`SELECT COUNT(*) AS count FROM session_message
|
|
367
|
+
WHERE session_id = ? AND type IN (${rawTypes})`).get(sessionID, ...RAW_MESSAGE_TYPES2);
|
|
368
|
+
return typeof row?.count === "number" ? row.count : 0;
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
messageCountAfterSequence(sessionID, after, through) {
|
|
372
|
+
const rawTypes = RAW_MESSAGE_TYPES2.map(() => "?").join(", ");
|
|
373
|
+
const row = this.prepare(`SELECT COUNT(*) AS count FROM session_message
|
|
374
|
+
WHERE session_id = ? AND seq > ? AND seq <= ? AND type IN (${rawTypes})`).get(sessionID, after, through, ...RAW_MESSAGE_TYPES2);
|
|
375
|
+
return typeof row?.count === "number" ? row.count : 0;
|
|
376
|
+
}
|
|
377
|
+
sessionDirectoryPage(afterSessionID, limit) {
|
|
378
|
+
const rows = this.prepare(`SELECT id, COALESCE(directory, '') AS directory FROM session_v2
|
|
379
|
+
WHERE id > ? ORDER BY id ASC LIMIT ?`).all(afterSessionID ?? "", limit);
|
|
380
|
+
return rows.map((row) => ({ sessionId: row.id, directory: row.directory }));
|
|
381
|
+
}
|
|
382
|
+
latestMessageTime(sessionID) {
|
|
383
|
+
const row = this.prepare("SELECT MAX(time_created) AS time FROM session_message WHERE session_id = ?").get(sessionID);
|
|
384
|
+
return row?.time ?? undefined;
|
|
385
|
+
}
|
|
386
|
+
isSubagent(sessionID) {
|
|
387
|
+
const row = this.prepare("SELECT parent_id FROM session_v2 WHERE id = ?").get(sessionID);
|
|
388
|
+
if (!row)
|
|
389
|
+
return null;
|
|
390
|
+
return typeof row.parent_id === "string" && row.parent_id.length > 0;
|
|
391
|
+
}
|
|
392
|
+
latestRootSessionInDirectory(directory) {
|
|
393
|
+
const columns = new Set(this.prepare("PRAGMA table_info(session_v2)").all().map((column) => column.name));
|
|
394
|
+
const archived = columns.has("time_archived") ? " AND time_archived IS NULL" : "";
|
|
395
|
+
const order = columns.has("time_updated") ? "time_updated DESC, id DESC" : "id DESC";
|
|
396
|
+
const page = this.prepare(`SELECT id, COALESCE(directory, '') AS directory FROM session_v2
|
|
397
|
+
WHERE parent_id IS NULL${archived}
|
|
398
|
+
AND COALESCE(json_extract(metadata, '$.magic_context'), '') <> 'hidden-run'
|
|
399
|
+
ORDER BY ${order} LIMIT ? OFFSET ?`);
|
|
400
|
+
const pageSize = 500;
|
|
401
|
+
for (let offset = 0;; offset += pageSize) {
|
|
402
|
+
const rows = page.all(pageSize, offset);
|
|
403
|
+
const match = rows.find((row) => sameProjectDirectory(row.directory, directory));
|
|
404
|
+
if (match)
|
|
405
|
+
return match.id;
|
|
406
|
+
if (rows.length < pageSize)
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
rootSessionActivity(sessionIDs) {
|
|
411
|
+
const roots = new Map;
|
|
412
|
+
for (let offset = 0;offset < sessionIDs.length; offset += 500) {
|
|
413
|
+
const chunk = sessionIDs.slice(offset, offset + 500);
|
|
414
|
+
const rows = this.prepare(`SELECT id, (
|
|
415
|
+
SELECT MAX(time_created) FROM session_message
|
|
416
|
+
WHERE session_id = session_v2.id AND type = 'user'
|
|
417
|
+
) AS time FROM session_v2
|
|
418
|
+
WHERE parent_id IS NULL
|
|
419
|
+
AND COALESCE(json_extract(metadata, '$.magic_context'), '') <> 'hidden-run'
|
|
420
|
+
AND id IN (${chunk.map(() => "?").join(",")})`).all(...chunk);
|
|
421
|
+
for (const row of rows)
|
|
422
|
+
if (row.time !== null)
|
|
423
|
+
roots.set(row.id, row.time);
|
|
424
|
+
}
|
|
425
|
+
return roots;
|
|
426
|
+
}
|
|
427
|
+
oldestUserMessageTimesSince(sessionIDs, sinceMs) {
|
|
428
|
+
const result = new Map;
|
|
429
|
+
for (let offset = 0;offset < sessionIDs.length; offset += 500) {
|
|
430
|
+
const chunk = sessionIDs.slice(offset, offset + 500);
|
|
431
|
+
const rows = this.prepare(`SELECT session_id, MIN(time_created) AS time FROM session_message
|
|
432
|
+
WHERE type = 'user' AND time_created > ? AND session_id IN (${chunk.map(() => "?").join(",")})
|
|
433
|
+
GROUP BY session_id`).all(sinceMs, ...chunk);
|
|
434
|
+
for (const row of rows)
|
|
435
|
+
result.set(row.session_id, row.time);
|
|
436
|
+
}
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
retrospectiveUserPage(sessionID, options) {
|
|
440
|
+
const { boundaryMs, before, limit, maxChars, truncationMarker } = options;
|
|
441
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1e4)
|
|
442
|
+
throw new Error("Invalid page limit");
|
|
443
|
+
if (!Number.isSafeInteger(maxChars) || maxChars <= truncationMarker.length)
|
|
444
|
+
throw new Error("Invalid text limit");
|
|
445
|
+
const direction = before ? "DESC" : "ASC";
|
|
446
|
+
const head = Math.floor((maxChars - truncationMarker.length) / 2);
|
|
447
|
+
const tail = maxChars - truncationMarker.length - head;
|
|
448
|
+
return this.prepare(`WITH users AS (
|
|
449
|
+
SELECT session_id, seq, time_created, data FROM session_message
|
|
450
|
+
WHERE session_id = ? AND type = 'user' AND time_created ${before ? "<=" : ">"}
|
|
451
|
+
?
|
|
452
|
+
ORDER BY time_created ${direction}, seq ${direction} LIMIT ?
|
|
453
|
+
), texts AS (
|
|
454
|
+
SELECT session_id, seq, time_created, CASE
|
|
455
|
+
WHEN json_extract(data, '$.synthetic') = 1 OR json_extract(data, '$.ignored') = 1 THEN ''
|
|
456
|
+
WHEN json_type(data, '$.text') = 'text' THEN json_extract(data, '$.text')
|
|
457
|
+
ELSE COALESCE((SELECT group_concat(json_extract(value, '$.text'), char(10))
|
|
458
|
+
FROM json_each(users.data, '$.content')
|
|
459
|
+
WHERE json_extract(value, '$.type') IN ('text', 'input_text')
|
|
460
|
+
AND COALESCE(json_extract(value, '$.synthetic'), 0) <> 1
|
|
461
|
+
AND COALESCE(json_extract(value, '$.ignored'), 0) <> 1), '') END AS text
|
|
462
|
+
FROM users WHERE json_valid(data)
|
|
463
|
+
) SELECT seq, time_created, CASE WHEN length(text) > ?
|
|
464
|
+
THEN substr(text, 1, ?) || ? || substr(text, -?) ELSE text END AS text,
|
|
465
|
+
EXISTS (
|
|
466
|
+
SELECT 1 FROM session_message assistant_reply
|
|
467
|
+
WHERE assistant_reply.session_id = texts.session_id
|
|
468
|
+
AND assistant_reply.type = 'assistant'
|
|
469
|
+
AND assistant_reply.seq < texts.seq
|
|
470
|
+
AND EXISTS (
|
|
471
|
+
SELECT 1 FROM session_message prior_user
|
|
472
|
+
WHERE prior_user.session_id = texts.session_id
|
|
473
|
+
AND prior_user.type = 'user'
|
|
474
|
+
AND prior_user.seq < assistant_reply.seq
|
|
475
|
+
)
|
|
476
|
+
) AS follows_assistant_reply
|
|
477
|
+
FROM texts ORDER BY time_created ${direction}, seq ${direction}`).all(sessionID, boundaryMs, limit, maxChars, head, truncationMarker, tail);
|
|
478
|
+
}
|
|
479
|
+
storedRowsAfter(sessionID, after) {
|
|
480
|
+
const row = this.prepare("SELECT COUNT(*) AS count FROM session_message WHERE session_id = ? AND seq > ?").get(sessionID, after);
|
|
481
|
+
return row.count;
|
|
482
|
+
}
|
|
483
|
+
storedMessageCount(sessionID) {
|
|
484
|
+
return trackDecodeOperation("storedMessageCount", () => {
|
|
485
|
+
const row = this.prepare("SELECT COUNT(*) AS count FROM session_message WHERE session_id = ?").get(sessionID);
|
|
486
|
+
return typeof row?.count === "number" ? row.count : 0;
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
messageById(sessionID, id) {
|
|
490
|
+
return trackDecodeOperation("messageById", () => {
|
|
491
|
+
const rawTypes = RAW_MESSAGE_TYPES2.map(() => "?").join(", ");
|
|
492
|
+
const row = this.prepare(`SELECT id, session_id, type, seq, time_created, data FROM session_message
|
|
493
|
+
WHERE session_id = ? AND id = ? AND type IN (${rawTypes}) LIMIT 1`).get(sessionID, id, ...RAW_MESSAGE_TYPES2);
|
|
494
|
+
return row ? decode(row) : null;
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
messageTimeVersions(sessionID, ids, span) {
|
|
498
|
+
const versions = new Map;
|
|
499
|
+
if (ids.length > 0 && span && span.last - span.first < ids.length * 2) {
|
|
500
|
+
const wanted = new Set(ids);
|
|
501
|
+
const rows = this.prepare(`SELECT id, type, seq, time_created AS created, time_updated AS updated
|
|
502
|
+
FROM session_message WHERE session_id = ? AND seq >= ? AND seq <= ?`).all(sessionID, span.first, span.last);
|
|
503
|
+
for (const row of rows)
|
|
504
|
+
if (wanted.has(row.id))
|
|
505
|
+
versions.set(row.id, row);
|
|
506
|
+
for (const [id, version] of this.messageTimeVersions(sessionID, ids.filter((id) => !versions.has(id))))
|
|
507
|
+
versions.set(id, version);
|
|
508
|
+
return versions;
|
|
509
|
+
}
|
|
510
|
+
for (let offset = 0;offset < ids.length; offset += 500) {
|
|
511
|
+
const batch = ids.slice(offset, offset + 500);
|
|
512
|
+
const rows = this.prepare(`SELECT id, type, seq, time_created AS created, time_updated AS updated
|
|
513
|
+
FROM session_message WHERE +session_id = ? AND id IN (${batch.map(() => "?").join(",")})`).all(sessionID, ...batch);
|
|
514
|
+
for (const row of rows)
|
|
515
|
+
versions.set(row.id, row);
|
|
516
|
+
}
|
|
517
|
+
return versions;
|
|
518
|
+
}
|
|
519
|
+
messageTimes(sessionID, ids, versions) {
|
|
520
|
+
const times = new Map;
|
|
521
|
+
for (let offset = 0;offset < ids.length; offset += 500) {
|
|
522
|
+
const batch = ids.slice(offset, offset + 500);
|
|
523
|
+
const rows = this.prepare(messageTimesQuery(batch.length, versions !== undefined)).all(sessionID, ...batch);
|
|
524
|
+
for (const row of rows) {
|
|
525
|
+
times.set(row.id, {
|
|
526
|
+
created: typeof row.created === "number" ? row.created : undefined,
|
|
527
|
+
completed: typeof row.completed === "number" ? row.completed : undefined
|
|
528
|
+
});
|
|
529
|
+
versions?.set(row.id, {
|
|
530
|
+
id: row.id,
|
|
531
|
+
type: row.type,
|
|
532
|
+
seq: row.seq,
|
|
533
|
+
created: row.version_created,
|
|
534
|
+
updated: row.version_updated
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return times;
|
|
539
|
+
}
|
|
540
|
+
messageExistsById(sessionID, id) {
|
|
541
|
+
return trackDecodeOperation("messageExistsById", () => {
|
|
542
|
+
const rawTypes = RAW_MESSAGE_TYPES2.map(() => "?").join(", ");
|
|
543
|
+
const row = this.prepare(`SELECT 1 FROM session_message
|
|
544
|
+
WHERE id = ? AND session_id = ? AND type IN (${rawTypes}) LIMIT 1`).get(id, sessionID, ...RAW_MESSAGE_TYPES2);
|
|
545
|
+
return row != null;
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
withOrdinalSequences(sessionID, read) {
|
|
549
|
+
return this.db.transaction(() => {
|
|
550
|
+
this.prepare("SELECT seq FROM session_message WHERE session_id = ? ORDER BY seq DESC LIMIT 1").get(sessionID);
|
|
551
|
+
const { data_version: version } = this.prepare("PRAGMA data_version").get();
|
|
552
|
+
if (version !== this.connection.ordinalVersion) {
|
|
553
|
+
this.connection.ordinalSequences.clear();
|
|
554
|
+
this.connection.ordinalVersion = version;
|
|
555
|
+
}
|
|
556
|
+
let sequences = this.connection.ordinalSequences.get(sessionID);
|
|
557
|
+
if (!sequences) {
|
|
558
|
+
const rows = this.prepare(`SELECT seq FROM session_message
|
|
559
|
+
WHERE session_id = ? AND type IN (${RAW_MESSAGE_TYPE_PARAMETERS}) ORDER BY seq ASC`).all(sessionID, ...RAW_MESSAGE_TYPES2);
|
|
560
|
+
sequences = rows.map((row) => row.seq);
|
|
561
|
+
if (this.connection.ordinalSequences.size >= 8)
|
|
562
|
+
this.connection.ordinalSequences.clear();
|
|
563
|
+
this.connection.ordinalSequences.set(sessionID, sequences);
|
|
564
|
+
}
|
|
565
|
+
return read(sequences);
|
|
566
|
+
}).deferred();
|
|
567
|
+
}
|
|
568
|
+
messageOrdinalById(sessionID, id) {
|
|
569
|
+
return trackDecodeOperation("messageOrdinalById", () => this.withOrdinalSequences(sessionID, (sequences) => {
|
|
570
|
+
const row = this.prepare(`SELECT seq FROM session_message
|
|
571
|
+
WHERE session_id = ? AND id = ? AND type IN (${RAW_MESSAGE_TYPE_PARAMETERS}) LIMIT 1`).get(sessionID, id, ...RAW_MESSAGE_TYPES2);
|
|
572
|
+
if (!row)
|
|
573
|
+
return null;
|
|
574
|
+
let low = 0;
|
|
575
|
+
let high = sequences.length;
|
|
576
|
+
while (low < high) {
|
|
577
|
+
const middle = low + high >>> 1;
|
|
578
|
+
if (sequences[middle] < row.seq)
|
|
579
|
+
low = middle + 1;
|
|
580
|
+
else
|
|
581
|
+
high = middle;
|
|
582
|
+
}
|
|
583
|
+
return sequences[low] === row.seq ? low + 1 : null;
|
|
584
|
+
}));
|
|
585
|
+
}
|
|
586
|
+
messageIdOrdinals(sessionID, fromOrdinal, toOrdinal) {
|
|
587
|
+
return trackDecodeOperation("messageIdOrdinals", () => {
|
|
588
|
+
if (!Number.isSafeInteger(fromOrdinal) || fromOrdinal < 1)
|
|
589
|
+
throw new Error("Invalid raw-message range start");
|
|
590
|
+
if (!Number.isSafeInteger(toOrdinal) || toOrdinal < fromOrdinal)
|
|
591
|
+
throw new Error("Invalid raw-message range end");
|
|
592
|
+
return this.withOrdinalSequences(sessionID, (sequences) => {
|
|
593
|
+
const anchor = sequences[fromOrdinal - 1];
|
|
594
|
+
if (anchor === undefined)
|
|
595
|
+
return new Map;
|
|
596
|
+
const rows = this.prepare(`SELECT id FROM session_message
|
|
597
|
+
WHERE session_id = ? AND seq >= ? AND type IN (${RAW_MESSAGE_TYPE_PARAMETERS})
|
|
598
|
+
ORDER BY seq ASC LIMIT ?`).all(sessionID, anchor, ...RAW_MESSAGE_TYPES2, toOrdinal - fromOrdinal + 1);
|
|
599
|
+
return new Map(rows.map((row, index) => [row.id, fromOrdinal + index]));
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
messageOrdinalPage(sessionID, after, limit) {
|
|
604
|
+
return trackDecodeOperation("messageOrdinalPage", () => {
|
|
605
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1e4)
|
|
606
|
+
throw new Error("Invalid page limit");
|
|
607
|
+
const rows = after ? this.prepare(`SELECT id, type, seq, json_valid(data) AS valid
|
|
608
|
+
FROM session_message
|
|
609
|
+
WHERE session_id = ? AND (seq > ? OR (seq = ? AND id > ?))
|
|
610
|
+
ORDER BY seq ASC, id ASC LIMIT ?`).all(sessionID, after.timeCreated, after.timeCreated, after.id, limit) : this.prepare(`SELECT id, type, seq, json_valid(data) AS valid
|
|
611
|
+
FROM session_message
|
|
612
|
+
WHERE session_id = ?
|
|
613
|
+
ORDER BY seq ASC, id ASC LIMIT ?`).all(sessionID, limit);
|
|
614
|
+
const rawTypes = new Set(RAW_MESSAGE_TYPES2);
|
|
615
|
+
return rows.map((row) => ({
|
|
616
|
+
id: row.id,
|
|
617
|
+
timeCreated: row.seq,
|
|
618
|
+
contributesOrdinal: rawTypes.has(row.type),
|
|
619
|
+
hasValidInfo: row.valid === 1
|
|
620
|
+
}));
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
range(sessionID, after, through) {
|
|
624
|
+
return trackDecodeOperation("range", () => through <= after ? [] : this.all(sessionID, after, undefined, through));
|
|
625
|
+
}
|
|
626
|
+
spanRowStamps(sessionID, after, through) {
|
|
627
|
+
if (!Number.isSafeInteger(after) || !Number.isSafeInteger(through))
|
|
628
|
+
throw new Error("Invalid seq span");
|
|
629
|
+
const rows = this.prepare(`SELECT seq, id, time_updated AS updated, length(CAST(data AS BLOB)) AS bytes
|
|
630
|
+
FROM session_message WHERE session_id = ? AND seq > ? AND seq <= ?
|
|
631
|
+
ORDER BY seq ASC`).all(sessionID, after, through);
|
|
632
|
+
const stamps = new Map;
|
|
633
|
+
for (const row of rows)
|
|
634
|
+
stamps.set(row.seq, `${row.id}\x00${row.updated ?? ""}\x00${row.bytes ?? ""}`);
|
|
635
|
+
return stamps;
|
|
636
|
+
}
|
|
637
|
+
rawRowsThrough(sessionID, throughSeq, limit) {
|
|
638
|
+
return trackDecodeOperation("rawRowsThrough", () => {
|
|
639
|
+
if (!Number.isSafeInteger(throughSeq))
|
|
640
|
+
throw new Error("Invalid seq upper bound");
|
|
641
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1e4)
|
|
642
|
+
throw new Error("Invalid page limit");
|
|
643
|
+
const rawTypes = RAW_MESSAGE_TYPES2.map(() => "?").join(", ");
|
|
644
|
+
return this.prepare(`SELECT id, session_id, type, seq, time_created, data FROM session_message
|
|
645
|
+
WHERE session_id = ? AND type IN (${rawTypes}) AND seq <= ?
|
|
646
|
+
ORDER BY seq DESC LIMIT ?`).all(sessionID, ...RAW_MESSAGE_TYPES2, throughSeq, limit).map(decode);
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
instructionRowsThrough(sessionID, throughSeq) {
|
|
650
|
+
return trackDecodeOperation("instructionRowsThrough", () => {
|
|
651
|
+
if (!Number.isSafeInteger(throughSeq))
|
|
652
|
+
throw new Error("Invalid seq upper bound");
|
|
653
|
+
return this.prepare(`SELECT id, session_id, type, seq, time_created, data FROM session_message
|
|
654
|
+
WHERE session_id = ? AND type = 'system' AND seq <= ?
|
|
655
|
+
ORDER BY seq ASC`).all(sessionID, throughSeq).map(decode);
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
sequenceForId(sessionID, id) {
|
|
659
|
+
if (!id)
|
|
660
|
+
return;
|
|
661
|
+
const row = this.prepare("SELECT seq FROM session_message WHERE session_id = ? AND id = ? LIMIT 1").get(sessionID, id);
|
|
662
|
+
return typeof row?.seq === "number" ? row.seq : undefined;
|
|
663
|
+
}
|
|
664
|
+
syntheticMessageIDs(sessionID) {
|
|
665
|
+
const rows = this.prepare("SELECT id FROM session_message WHERE session_id = ? AND type = 'synthetic'").all(sessionID);
|
|
666
|
+
return new Set(rows.map((row) => row.id));
|
|
667
|
+
}
|
|
668
|
+
forkOrigin(sessionID) {
|
|
669
|
+
const columns = new Set(this.prepare("PRAGMA table_info(session_v2)").all().map((column) => column.name));
|
|
670
|
+
if (!columns.has("fork_session_id") || !columns.has("fork_boundary"))
|
|
671
|
+
return null;
|
|
672
|
+
const row = this.prepare("SELECT fork_session_id, fork_boundary FROM session_v2 WHERE id = ?").get(sessionID);
|
|
673
|
+
if (!row?.fork_session_id || !row.fork_boundary)
|
|
674
|
+
return null;
|
|
675
|
+
try {
|
|
676
|
+
const boundary = JSON.parse(row.fork_boundary);
|
|
677
|
+
if (boundary.type !== "before" && boundary.type !== "through" || typeof boundary.messageID !== "string" || boundary.messageID.length === 0)
|
|
678
|
+
return null;
|
|
679
|
+
return {
|
|
680
|
+
parentSessionID: row.fork_session_id,
|
|
681
|
+
boundary: { type: boundary.type, messageID: boundary.messageID }
|
|
682
|
+
};
|
|
683
|
+
} catch {
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
sessionExists(sessionID) {
|
|
688
|
+
return this.prepare("SELECT 1 FROM session_v2 WHERE id = ?").get(sessionID) != null;
|
|
689
|
+
}
|
|
690
|
+
rowStampsThrough(sessionID, throughSeq, after) {
|
|
691
|
+
if (after !== undefined)
|
|
692
|
+
return this.prepare(`SELECT id, type, seq, time_created FROM session_message
|
|
693
|
+
WHERE session_id = ? AND seq > ? AND seq <= ? ORDER BY seq ASC`).all(sessionID, after, throughSeq);
|
|
694
|
+
return this.prepare(`SELECT id, type, seq, time_created FROM session_message
|
|
695
|
+
WHERE session_id = ? AND seq <= ? ORDER BY seq ASC`).all(sessionID, throughSeq);
|
|
696
|
+
}
|
|
697
|
+
latestSequenceForIds(sessionID, ids) {
|
|
698
|
+
let latest = -1;
|
|
699
|
+
for (let offset = 0;offset < ids.length; offset += 500) {
|
|
700
|
+
const chunk = ids.slice(offset, offset + 500);
|
|
701
|
+
const row = this.prepare(`SELECT MAX(seq) AS seq FROM session_message
|
|
702
|
+
WHERE session_id = ? AND id IN (${chunk.map(() => "?").join(", ")})`).get(sessionID, ...chunk);
|
|
703
|
+
if (typeof row?.seq === "number")
|
|
704
|
+
latest = Math.max(latest, row.seq);
|
|
705
|
+
}
|
|
706
|
+
return latest;
|
|
707
|
+
}
|
|
708
|
+
compactionByStatus(sessionID, status) {
|
|
709
|
+
const row = this.prepare(`SELECT id, session_id, type, seq, time_created, data FROM session_message
|
|
710
|
+
WHERE session_id = ? AND type = 'compaction'
|
|
711
|
+
AND json_extract(data, '$.status') = ?
|
|
712
|
+
ORDER BY seq DESC LIMIT 1`).get(sessionID, status);
|
|
713
|
+
return row ? decode(row) : undefined;
|
|
714
|
+
}
|
|
715
|
+
latestCompaction(sessionID) {
|
|
716
|
+
return trackDecodeOperation("latestCompaction", () => this.compactionByStatus(sessionID, "completed"));
|
|
717
|
+
}
|
|
718
|
+
latestRunningCompaction(sessionID) {
|
|
719
|
+
return trackDecodeOperation("latestRunningCompaction", () => this.compactionByStatus(sessionID, "running"));
|
|
720
|
+
}
|
|
721
|
+
idleRows(sessionID, after = -1) {
|
|
722
|
+
return trackDecodeOperation("idleRows", () => this.all(sessionID, after, "idle"));
|
|
723
|
+
}
|
|
724
|
+
earliestSequence(sessionID) {
|
|
725
|
+
const row = this.prepare("SELECT MIN(seq) AS seq FROM session_message WHERE session_id = ?").get(sessionID);
|
|
726
|
+
return typeof row?.seq === "number" ? row.seq : undefined;
|
|
727
|
+
}
|
|
728
|
+
latestSequence(sessionID) {
|
|
729
|
+
const row = this.prepare("SELECT MAX(seq) AS seq FROM session_message WHERE session_id = ?").get(sessionID);
|
|
730
|
+
return typeof row?.seq === "number" ? row.seq : -1;
|
|
731
|
+
}
|
|
732
|
+
assistantSince(sessionID, afterSeq) {
|
|
733
|
+
return trackDecodeOperation("assistantSince", () => {
|
|
734
|
+
const rows = this.prepare(`SELECT id, session_id, type, seq, time_created, data FROM session_message
|
|
735
|
+
WHERE session_id = ? AND type = 'assistant' AND seq > ?
|
|
736
|
+
ORDER BY seq ASC`).all(sessionID, afterSeq);
|
|
737
|
+
return rows.map((row) => decode(row));
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
latestAssistant(sessionID) {
|
|
741
|
+
return trackDecodeOperation("latestAssistant", () => {
|
|
742
|
+
const row = this.prepare(`SELECT id, session_id, type, seq, time_created, data FROM session_message
|
|
743
|
+
WHERE session_id = ? AND type = 'assistant'
|
|
744
|
+
ORDER BY seq DESC LIMIT 1`).get(sessionID);
|
|
745
|
+
return row ? decode(row) : undefined;
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
latestIdle(sessionID) {
|
|
749
|
+
return trackDecodeOperation("latestIdle", () => {
|
|
750
|
+
const row = this.prepare(`SELECT id, session_id, type, seq, time_created, data FROM session_message
|
|
751
|
+
WHERE session_id = ? AND type = 'idle'
|
|
752
|
+
ORDER BY seq DESC LIMIT 1`).get(sessionID);
|
|
753
|
+
return row ? decode(row) : undefined;
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
window(sessionID) {
|
|
757
|
+
return trackDecodeOperation("window", () => this.db.transaction(() => {
|
|
758
|
+
const cut = this.latestCompaction(sessionID);
|
|
759
|
+
return this.all(sessionID, cut ? cut.seq - 1 : -1);
|
|
760
|
+
}).deferred());
|
|
761
|
+
}
|
|
762
|
+
history(sessionID) {
|
|
763
|
+
return trackDecodeOperation("history", () => this.all(sessionID, -1));
|
|
764
|
+
}
|
|
765
|
+
all(sessionID, after, type, through) {
|
|
766
|
+
const rows = [];
|
|
767
|
+
for (;; ) {
|
|
768
|
+
const page = this.page(sessionID, { after, through, type });
|
|
769
|
+
rows.push(...page.rows);
|
|
770
|
+
if (page.rows.length < 100 || page.cursor === undefined)
|
|
771
|
+
return rows;
|
|
772
|
+
after = page.cursor;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
export { gaDatabasePath2, RAW_MESSAGE_TYPES2, V2StoreReaderPool2, V2StoreReader2 };
|