codesesh 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/{chunk-5UOLRSFH.js → chunk-BIXOP5QX.js} +2103 -2145
- package/dist/chunk-BIXOP5QX.js.map +1 -0
- package/dist/{dist-VEIV33FR.js → dist-T737I76S.js} +30 -2
- package/dist/index.js +469 -636
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +7 -13
- package/dist/scan-refresh-worker.js.map +1 -1
- package/dist/search-index-worker.js +1 -1
- package/dist/smart-tag-worker.js +1 -1
- package/dist/web/assets/index-DlZEB-Op.css +2 -0
- package/dist/web/assets/index-O6YSX0s1.js +113 -0
- package/dist/web/assets/vendor-CR3FdUHs.js +40 -0
- package/dist/web/icon/agent/zcode.svg +1 -0
- package/dist/web/index.html +3 -3
- package/package.json +4 -3
- package/dist/chunk-5UOLRSFH.js.map +0 -1
- package/dist/web/assets/index-BHmQBGNx.css +0 -2
- package/dist/web/assets/index-CT4gLIAq.js +0 -111
- package/dist/web/assets/vendor-Da5am_Td.js +0 -40
- /package/dist/{dist-VEIV33FR.js.map → dist-T737I76S.js.map} +0 -0
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// ../core/dist/index.mjs
|
|
4
|
-
import { existsSync as
|
|
4
|
+
import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
|
|
5
5
|
import { join as join3, basename as basename2, dirname } from "path";
|
|
6
|
-
import { existsSync } from "fs";
|
|
6
|
+
import { existsSync, statSync } from "fs";
|
|
7
|
+
import { existsSync as existsSync2 } from "fs";
|
|
7
8
|
import { homedir, platform } from "os";
|
|
8
9
|
import { join } from "path";
|
|
9
10
|
import { readFileSync } from "fs";
|
|
10
11
|
import { basename } from "path";
|
|
11
|
-
import { existsSync as
|
|
12
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
12
13
|
import { homedir as homedir2 } from "os";
|
|
13
14
|
import { join as join2 } from "path";
|
|
14
|
-
import { existsSync as existsSync5, statSync as statSync2 } from "fs";
|
|
15
15
|
import { join as join5 } from "path";
|
|
16
|
-
import { existsSync as
|
|
16
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
17
17
|
import { basename as basename3, dirname as dirname2, join as join4 } from "path";
|
|
18
18
|
import { createRequire } from "module";
|
|
19
19
|
import { createHash } from "crypto";
|
|
@@ -33,6 +33,7 @@ import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as
|
|
|
33
33
|
import { join as join8, normalize } from "path";
|
|
34
34
|
import { existsSync as existsSync9, readFileSync as readFileSync7, readdirSync as readdirSync5, statSync as statSync6 } from "fs";
|
|
35
35
|
import { basename as basename6, join as join9 } from "path";
|
|
36
|
+
import { join as join10 } from "path";
|
|
36
37
|
import { availableParallelism } from "os";
|
|
37
38
|
import { Worker } from "worker_threads";
|
|
38
39
|
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
@@ -40,11 +41,12 @@ import { spawnSync } from "child_process";
|
|
|
40
41
|
import * as os from "os";
|
|
41
42
|
import * as path from "path";
|
|
42
43
|
import { resolve, sep } from "path";
|
|
43
|
-
import { existsSync as existsSync11
|
|
44
|
-
import { join as
|
|
44
|
+
import { existsSync as existsSync11 } from "fs";
|
|
45
|
+
import { join as join11 } from "path";
|
|
45
46
|
import { homedir as homedir4 } from "os";
|
|
47
|
+
import { existsSync as existsSync12, rmSync, unlinkSync } from "fs";
|
|
46
48
|
import { homedir as homedir5, platform as platform2 } from "os";
|
|
47
|
-
import { join as
|
|
49
|
+
import { join as join12 } from "path";
|
|
48
50
|
var registrations = [];
|
|
49
51
|
function registerAgent(reg) {
|
|
50
52
|
registrations.push(reg);
|
|
@@ -88,6 +90,105 @@ var BaseAgent = class {
|
|
|
88
90
|
return `${this.name}://${sessionId}`;
|
|
89
91
|
}
|
|
90
92
|
};
|
|
93
|
+
var FileSystemSessionSource = class extends BaseAgent {
|
|
94
|
+
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
95
|
+
getSessionMetaMap() {
|
|
96
|
+
return this.sessionMetaMap;
|
|
97
|
+
}
|
|
98
|
+
setSessionMetaMap(meta) {
|
|
99
|
+
this.sessionMetaMap = meta;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 变更检测:枚举当前源 → 与缓存 metaMap 的指纹/路径比对。
|
|
103
|
+
* 新增、变更、删除三类统一产出 changedIds。
|
|
104
|
+
*/
|
|
105
|
+
checkForChanges(_sinceTimestamp, cachedSessions) {
|
|
106
|
+
const currentRefs = this.listSessionSources();
|
|
107
|
+
const currentIds = new Set(currentRefs.map((ref) => ref.sessionId));
|
|
108
|
+
const changedIds = /* @__PURE__ */ new Set();
|
|
109
|
+
for (const ref of currentRefs) {
|
|
110
|
+
const meta = this.sessionMetaMap.get(ref.sessionId);
|
|
111
|
+
const samePath = meta?.sourcePath === ref.sourcePath;
|
|
112
|
+
const sameFingerprint = typeof meta?.sourceFingerprint === "string" && meta.sourceFingerprint === ref.fingerprint;
|
|
113
|
+
if (!samePath || !sameFingerprint) changedIds.add(ref.sessionId);
|
|
114
|
+
}
|
|
115
|
+
for (const session of cachedSessions) {
|
|
116
|
+
if (!currentIds.has(session.id)) changedIds.add(session.id);
|
|
117
|
+
}
|
|
118
|
+
const changedIdList = [...changedIds];
|
|
119
|
+
return {
|
|
120
|
+
hasChanges: changedIdList.length > 0,
|
|
121
|
+
changedIds: changedIdList,
|
|
122
|
+
timestamp: Date.now()
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 增量扫描:对变更/新增源调用 scanSessionSource 重解析,
|
|
127
|
+
* 删除已消失的源,合并回 cachedSessions。
|
|
128
|
+
*/
|
|
129
|
+
incrementalScan(cachedSessions, changedIds) {
|
|
130
|
+
const sessionMap = new Map(cachedSessions.map((session) => [session.id, session]));
|
|
131
|
+
const changedSet = new Set(changedIds);
|
|
132
|
+
const currentIds = /* @__PURE__ */ new Set();
|
|
133
|
+
for (const ref of this.listSessionSources()) {
|
|
134
|
+
currentIds.add(ref.sessionId);
|
|
135
|
+
if (!changedSet.has(ref.sessionId)) continue;
|
|
136
|
+
const head = this.scanSessionSource(ref.sourcePath);
|
|
137
|
+
if (head) {
|
|
138
|
+
sessionMap.set(head.id, head);
|
|
139
|
+
} else {
|
|
140
|
+
sessionMap.delete(ref.sessionId);
|
|
141
|
+
this.sessionMetaMap.delete(ref.sessionId);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const id of changedSet) {
|
|
145
|
+
if (!currentIds.has(id)) {
|
|
146
|
+
sessionMap.delete(id);
|
|
147
|
+
this.sessionMetaMap.delete(id);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return [...sessionMap.values()];
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
var DatabaseSessionSource = class extends BaseAgent {
|
|
154
|
+
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
155
|
+
/** 记录单个会话的缓存 meta(sourcePath = dbPath)。 */
|
|
156
|
+
rememberSession(sessionId) {
|
|
157
|
+
const dbPath = this.getDatabasePath();
|
|
158
|
+
if (!dbPath) return;
|
|
159
|
+
this.sessionMetaMap.set(sessionId, { id: sessionId, sourcePath: dbPath });
|
|
160
|
+
}
|
|
161
|
+
getSessionMetaMap() {
|
|
162
|
+
return this.sessionMetaMap;
|
|
163
|
+
}
|
|
164
|
+
setSessionMetaMap(meta) {
|
|
165
|
+
this.sessionMetaMap = meta;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 变更检测:数据库内部变更难以按行定位,简单起见按库文件 mtime 判定。
|
|
169
|
+
* 库有变更则标记全部缓存会话刷新。
|
|
170
|
+
*/
|
|
171
|
+
checkForChanges(sinceTimestamp, cachedSessions) {
|
|
172
|
+
const dbPath = this.getDatabasePath();
|
|
173
|
+
if (!dbPath || !existsSync(dbPath)) {
|
|
174
|
+
return { hasChanges: false, timestamp: Date.now() };
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const hasChanges = statSync(dbPath).mtimeMs > sinceTimestamp;
|
|
178
|
+
return {
|
|
179
|
+
hasChanges,
|
|
180
|
+
changedIds: hasChanges ? cachedSessions.map((session) => session.id) : [],
|
|
181
|
+
timestamp: Date.now()
|
|
182
|
+
};
|
|
183
|
+
} catch {
|
|
184
|
+
return { hasChanges: false, timestamp: Date.now() };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** 增量扫描:数据库型无法增量,直接全量重扫。 */
|
|
188
|
+
incrementalScan(_cachedSessions, _changedIds) {
|
|
189
|
+
return this.scan();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
91
192
|
function envPath(name) {
|
|
92
193
|
const value = process.env[name];
|
|
93
194
|
if (!value) return null;
|
|
@@ -95,7 +196,7 @@ function envPath(name) {
|
|
|
95
196
|
}
|
|
96
197
|
function firstExisting(...paths) {
|
|
97
198
|
for (const p of paths) {
|
|
98
|
-
if (
|
|
199
|
+
if (existsSync2(p)) return p;
|
|
99
200
|
}
|
|
100
201
|
return null;
|
|
101
202
|
}
|
|
@@ -115,7 +216,8 @@ function resolveProviderRoots() {
|
|
|
115
216
|
claudeRoot: envPath("CLAUDE_CONFIG_DIR") ?? join(home, ".claude"),
|
|
116
217
|
kimiRoot: envPath("KIMI_SHARE_DIR") ?? join(home, ".kimi"),
|
|
117
218
|
opencodeRoot: join(getDataHome(), "opencode"),
|
|
118
|
-
piRoot: envPath("PI_HOME") ?? join(home, ".pi")
|
|
219
|
+
piRoot: envPath("PI_HOME") ?? join(home, ".pi"),
|
|
220
|
+
zcodeRoot: getZCodeDataPath()
|
|
119
221
|
};
|
|
120
222
|
}
|
|
121
223
|
function getCursorDataPath() {
|
|
@@ -135,6 +237,13 @@ function getCursorDataPath() {
|
|
|
135
237
|
}
|
|
136
238
|
return null;
|
|
137
239
|
}
|
|
240
|
+
function getZCodeDataPath() {
|
|
241
|
+
const p = platform();
|
|
242
|
+
if (p === "darwin" || p === "win32") {
|
|
243
|
+
return join(homedir(), ".zcode");
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
138
247
|
function* parseJsonlLines(content) {
|
|
139
248
|
for (const line of content.split("\n")) {
|
|
140
249
|
const trimmed = line.trim();
|
|
@@ -527,7 +636,7 @@ function parseLiteLLMData(data) {
|
|
|
527
636
|
}
|
|
528
637
|
function loadDiskCache() {
|
|
529
638
|
const path2 = getCachePath();
|
|
530
|
-
if (!
|
|
639
|
+
if (!existsSync3(path2)) return;
|
|
531
640
|
try {
|
|
532
641
|
const cached = JSON.parse(readFileSync2(path2, "utf-8"));
|
|
533
642
|
if (Date.now() - cached.timestamp <= CACHE_TTL_MS) {
|
|
@@ -550,7 +659,7 @@ function hasBillablePricing(pricing) {
|
|
|
550
659
|
}
|
|
551
660
|
async function refreshPricingCache() {
|
|
552
661
|
const path2 = getCachePath();
|
|
553
|
-
if (
|
|
662
|
+
if (existsSync3(path2)) {
|
|
554
663
|
try {
|
|
555
664
|
const cached = JSON.parse(readFileSync2(path2, "utf-8"));
|
|
556
665
|
if (typeof cached.timestamp === "number" && Date.now() - cached.timestamp <= CACHE_TTL_MS) {
|
|
@@ -733,13 +842,13 @@ function extractClaudeUsage(data, msg) {
|
|
|
733
842
|
cacheCreate: numericUsage(u["cache_creation_input_tokens"])
|
|
734
843
|
};
|
|
735
844
|
}
|
|
736
|
-
var ClaudeCodeAgent = class extends
|
|
845
|
+
var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
737
846
|
name = "claudecode";
|
|
738
847
|
displayName = "Claude Code";
|
|
739
848
|
basePath = null;
|
|
740
849
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
741
850
|
sessionsIndexCache = {};
|
|
742
|
-
|
|
851
|
+
sessionsIndexMtime = {};
|
|
743
852
|
findBasePath() {
|
|
744
853
|
const roots = resolveProviderRoots();
|
|
745
854
|
return firstExisting(join3(roots.claudeRoot, "projects"), "data/claudecode");
|
|
@@ -750,7 +859,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
750
859
|
try {
|
|
751
860
|
for (const entry of readdirSync(this.basePath)) {
|
|
752
861
|
const dir = join3(this.basePath, entry);
|
|
753
|
-
if (
|
|
862
|
+
if (existsSync4(dir) && readdirSync(dir).some((f) => f.endsWith(".jsonl"))) {
|
|
754
863
|
return true;
|
|
755
864
|
}
|
|
756
865
|
}
|
|
@@ -769,7 +878,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
769
878
|
const fileMarker = perf.start(`listJsonlFiles:${basename2(projectDir)}`);
|
|
770
879
|
const files = this.listJsonlFiles(projectDir).filter((file) => {
|
|
771
880
|
try {
|
|
772
|
-
return matchesScanWindow(
|
|
881
|
+
return matchesScanWindow(statSync2(file).mtimeMs, options);
|
|
773
882
|
} catch {
|
|
774
883
|
return false;
|
|
775
884
|
}
|
|
@@ -823,18 +932,12 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
823
932
|
}
|
|
824
933
|
return head;
|
|
825
934
|
}
|
|
826
|
-
getSessionMetaMap() {
|
|
827
|
-
return this.sessionMetaMap;
|
|
828
|
-
}
|
|
829
|
-
setSessionMetaMap(meta) {
|
|
830
|
-
this.sessionMetaMap = meta;
|
|
831
|
-
}
|
|
832
935
|
getSessionData(sessionId) {
|
|
833
936
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
834
937
|
if (!meta) {
|
|
835
938
|
throw new Error(`Session not found: ${sessionId}`);
|
|
836
939
|
}
|
|
837
|
-
if (!
|
|
940
|
+
if (!existsSync4(meta.sourcePath)) {
|
|
838
941
|
throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
839
942
|
}
|
|
840
943
|
const content = readFileSync3(meta.sourcePath, "utf-8");
|
|
@@ -894,83 +997,11 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
894
997
|
messages: cleanedMessages
|
|
895
998
|
};
|
|
896
999
|
}
|
|
897
|
-
/**
|
|
898
|
-
* 检测文件系统变更
|
|
899
|
-
* - 已有 session:statSync 检测文件修改(APFS 写文件不更新 dir mtime,必须 file-level)
|
|
900
|
-
* - 新 session 检测:readdirSync 文件列表比对,比 dir statSync 快 ~10x
|
|
901
|
-
*/
|
|
902
|
-
checkForChanges(_sinceTimestamp, cachedSessions) {
|
|
903
|
-
if (!this.basePath) {
|
|
904
|
-
return { hasChanges: false, timestamp: Date.now() };
|
|
905
|
-
}
|
|
906
|
-
const changedIds = /* @__PURE__ */ new Set();
|
|
907
|
-
for (const session of cachedSessions) {
|
|
908
|
-
const meta = this.sessionMetaMap.get(session.id);
|
|
909
|
-
if (!meta) {
|
|
910
|
-
changedIds.add(session.id);
|
|
911
|
-
continue;
|
|
912
|
-
}
|
|
913
|
-
try {
|
|
914
|
-
if (this.hasMetaChanged(meta)) {
|
|
915
|
-
changedIds.add(session.id);
|
|
916
|
-
delete this.sessionsIndexCache[basename2(dirname(meta.sourcePath))];
|
|
917
|
-
}
|
|
918
|
-
} catch {
|
|
919
|
-
changedIds.add(session.id);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
const cachedIdSet = new Set(cachedSessions.map((s) => s.id));
|
|
923
|
-
let hasNewFiles = false;
|
|
924
|
-
try {
|
|
925
|
-
outer: for (const dir of this.listProjectDirs()) {
|
|
926
|
-
try {
|
|
927
|
-
for (const file of this.listJsonlFiles(dir)) {
|
|
928
|
-
if (!cachedIdSet.has(basename2(file, ".jsonl"))) {
|
|
929
|
-
hasNewFiles = true;
|
|
930
|
-
delete this.sessionsIndexCache[basename2(dir)];
|
|
931
|
-
break outer;
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
} catch {
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
} catch {
|
|
938
|
-
}
|
|
939
|
-
return {
|
|
940
|
-
hasChanges: changedIds.size > 0 || hasNewFiles,
|
|
941
|
-
changedIds: Array.from(changedIds),
|
|
942
|
-
timestamp: Date.now()
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
/**
|
|
946
|
-
* 增量扫描 - 只扫描变更的会话
|
|
947
|
-
*/
|
|
948
|
-
incrementalScan(cachedSessions, changedIds) {
|
|
949
|
-
if (!this.basePath) return cachedSessions;
|
|
950
|
-
const sessionMap = new Map(cachedSessions.map((s) => [s.id, s]));
|
|
951
|
-
const changedSet = new Set(changedIds);
|
|
952
|
-
for (const projectDir of this.listProjectDirs()) {
|
|
953
|
-
for (const file of this.listJsonlFiles(projectDir)) {
|
|
954
|
-
try {
|
|
955
|
-
const sessionId = basename2(file, ".jsonl");
|
|
956
|
-
if (changedSet.has(sessionId) || !sessionMap.has(sessionId)) {
|
|
957
|
-
const head = getParsedSession(this.parseSessionHeadResult(file, projectDir));
|
|
958
|
-
if (head) {
|
|
959
|
-
sessionMap.set(head.id, head);
|
|
960
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, file, projectDir));
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
} catch {
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
return Array.from(sessionMap.values());
|
|
968
|
-
}
|
|
969
1000
|
// --- Private helpers ---
|
|
970
1001
|
listProjectDirs() {
|
|
971
1002
|
if (!this.basePath) return [];
|
|
972
1003
|
try {
|
|
973
|
-
return readdirSync(this.basePath).map((e) => join3(this.basePath, e)).filter((p) =>
|
|
1004
|
+
return readdirSync(this.basePath).map((e) => join3(this.basePath, e)).filter((p) => existsSync4(p));
|
|
974
1005
|
} catch {
|
|
975
1006
|
return [];
|
|
976
1007
|
}
|
|
@@ -989,8 +1020,8 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
989
1020
|
title: head.title,
|
|
990
1021
|
sourcePath: file,
|
|
991
1022
|
sourceFingerprint: this.sourceFingerprint(file, projectDir),
|
|
992
|
-
sourceMtimeMs:
|
|
993
|
-
indexPath:
|
|
1023
|
+
sourceMtimeMs: statSync2(file).mtimeMs,
|
|
1024
|
+
indexPath: existsSync4(indexPath) ? indexPath : null,
|
|
994
1025
|
indexMtimeMs: this.getFileMtimeMs(indexPath),
|
|
995
1026
|
headIndexVersion: HEAD_INDEX_VERSION,
|
|
996
1027
|
directory: head.directory,
|
|
@@ -1000,15 +1031,8 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
1000
1031
|
updatedAt: head.time_updated ?? head.time_created
|
|
1001
1032
|
};
|
|
1002
1033
|
}
|
|
1003
|
-
hasMetaChanged(meta) {
|
|
1004
|
-
if (meta.headIndexVersion !== HEAD_INDEX_VERSION) return true;
|
|
1005
|
-
if (typeof meta.sourceMtimeMs !== "number") return true;
|
|
1006
|
-
if (statSync(meta.sourcePath).mtimeMs !== meta.sourceMtimeMs) return true;
|
|
1007
|
-
const indexPath = meta.indexPath ?? this.getSessionsIndexPath(dirname(meta.sourcePath));
|
|
1008
|
-
return this.getFileMtimeMs(indexPath) !== (meta.indexMtimeMs ?? null);
|
|
1009
|
-
}
|
|
1010
1034
|
sourceFingerprint(file, projectDir) {
|
|
1011
|
-
const stat =
|
|
1035
|
+
const stat = statSync2(file);
|
|
1012
1036
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1013
1037
|
return JSON.stringify([
|
|
1014
1038
|
HEAD_INDEX_VERSION,
|
|
@@ -1022,19 +1046,20 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
1022
1046
|
}
|
|
1023
1047
|
getFileMtimeMs(filePath) {
|
|
1024
1048
|
try {
|
|
1025
|
-
return
|
|
1049
|
+
return statSync2(filePath).mtimeMs;
|
|
1026
1050
|
} catch {
|
|
1027
1051
|
return null;
|
|
1028
1052
|
}
|
|
1029
1053
|
}
|
|
1030
1054
|
loadSessionsIndex(projectDir) {
|
|
1031
1055
|
const cacheKey = basename2(projectDir);
|
|
1032
|
-
|
|
1056
|
+
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1057
|
+
const mtime = this.getFileMtimeMs(indexPath);
|
|
1058
|
+
if (cacheKey in this.sessionsIndexCache && this.sessionsIndexMtime[cacheKey] === mtime) {
|
|
1033
1059
|
return this.sessionsIndexCache[cacheKey];
|
|
1034
1060
|
}
|
|
1035
|
-
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1036
1061
|
const map = /* @__PURE__ */ new Map();
|
|
1037
|
-
if (
|
|
1062
|
+
if (existsSync4(indexPath)) {
|
|
1038
1063
|
try {
|
|
1039
1064
|
const data = JSON.parse(readFileSync3(indexPath, "utf-8"));
|
|
1040
1065
|
const entries = data?.entries ?? [];
|
|
@@ -1048,6 +1073,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
1048
1073
|
}
|
|
1049
1074
|
}
|
|
1050
1075
|
this.sessionsIndexCache[cacheKey] = map;
|
|
1076
|
+
this.sessionsIndexMtime[cacheKey] = mtime;
|
|
1051
1077
|
return map;
|
|
1052
1078
|
}
|
|
1053
1079
|
parseSessionHead(filePath, projectDir) {
|
|
@@ -1064,7 +1090,7 @@ var ClaudeCodeAgent = class extends BaseAgent {
|
|
|
1064
1090
|
} catch {
|
|
1065
1091
|
return skippedSession("malformed first record");
|
|
1066
1092
|
}
|
|
1067
|
-
const createdAt = parseTimestampMs(firstRecord) ||
|
|
1093
|
+
const createdAt = parseTimestampMs(firstRecord) || statSync2(filePath).mtimeMs;
|
|
1068
1094
|
const index = this.loadSessionsIndex(projectDir);
|
|
1069
1095
|
const indexEntry = index.get(sessionId);
|
|
1070
1096
|
const explicitTitle = indexEntry?.summary ? String(indexEntry.summary) : null;
|
|
@@ -1636,7 +1662,7 @@ function backupDatabase(db, dbPath, label) {
|
|
|
1636
1662
|
}
|
|
1637
1663
|
const timestamp = new Date(Date.now()).toISOString().replaceAll(":", "").replaceAll(".", "-");
|
|
1638
1664
|
let backupPath = join4(dirname2(dbPath), `${basename3(dbPath)}.${timestamp}.${label}.bak`);
|
|
1639
|
-
for (let counter = 1;
|
|
1665
|
+
for (let counter = 1; existsSync5(backupPath); counter += 1) {
|
|
1640
1666
|
backupPath = join4(dirname2(dbPath), `${basename3(dbPath)}.${timestamp}.${label}.${counter}.bak`);
|
|
1641
1667
|
}
|
|
1642
1668
|
db.exec(`VACUUM INTO ${quoteSqlString(backupPath)}`);
|
|
@@ -1709,16 +1735,25 @@ function openDb(dbPath) {
|
|
|
1709
1735
|
function isSqliteAvailable() {
|
|
1710
1736
|
return DatabaseConstructor !== null;
|
|
1711
1737
|
}
|
|
1712
|
-
var
|
|
1713
|
-
|
|
1714
|
-
|
|
1738
|
+
var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
1739
|
+
constructor(config) {
|
|
1740
|
+
super();
|
|
1741
|
+
this.config = config;
|
|
1742
|
+
this.name = config.name;
|
|
1743
|
+
this.displayName = config.displayName;
|
|
1744
|
+
}
|
|
1745
|
+
config;
|
|
1746
|
+
name;
|
|
1747
|
+
displayName;
|
|
1715
1748
|
dbPath = null;
|
|
1716
|
-
|
|
1717
|
-
|
|
1749
|
+
getDatabasePath() {
|
|
1750
|
+
if (!this.dbPath) {
|
|
1751
|
+
this.dbPath = this.findDbPath();
|
|
1752
|
+
}
|
|
1753
|
+
return this.dbPath;
|
|
1754
|
+
}
|
|
1718
1755
|
findDbPath() {
|
|
1719
|
-
|
|
1720
|
-
const roots = resolveProviderRoots();
|
|
1721
|
-
return firstExisting(join5(roots.opencodeRoot, "opencode.db"), "data/opencode/opencode.db");
|
|
1756
|
+
return this.config.findDbPath();
|
|
1722
1757
|
}
|
|
1723
1758
|
isAvailable() {
|
|
1724
1759
|
this.dbPath = this.findDbPath();
|
|
@@ -1793,7 +1828,7 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1793
1828
|
const messageTitle = context?.messageTitle ?? null;
|
|
1794
1829
|
return parsedSession({
|
|
1795
1830
|
id,
|
|
1796
|
-
slug:
|
|
1831
|
+
slug: `${this.name}/${id}`,
|
|
1797
1832
|
title: resolveSessionTitle(String(row.title ?? ""), messageTitle, null),
|
|
1798
1833
|
directory: String(row.directory ?? ""),
|
|
1799
1834
|
time_created: timeCreated,
|
|
@@ -1830,42 +1865,6 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1830
1865
|
`
|
|
1831
1866
|
).all(cutoffTime);
|
|
1832
1867
|
}
|
|
1833
|
-
getSessionMetaMap() {
|
|
1834
|
-
return this.sessionMetaMap;
|
|
1835
|
-
}
|
|
1836
|
-
setSessionMetaMap(meta) {
|
|
1837
|
-
this.sessionMetaMap = meta;
|
|
1838
|
-
}
|
|
1839
|
-
/**
|
|
1840
|
-
* 检测数据库变更
|
|
1841
|
-
* 对于 SQLite,检测数据库文件修改时间
|
|
1842
|
-
*/
|
|
1843
|
-
checkForChanges(sinceTimestamp, cachedSessions) {
|
|
1844
|
-
if (!this.dbPath) {
|
|
1845
|
-
this.dbPath = this.findDbPath();
|
|
1846
|
-
}
|
|
1847
|
-
if (!this.dbPath || !existsSync5(this.dbPath)) {
|
|
1848
|
-
return { hasChanges: false, timestamp: Date.now() };
|
|
1849
|
-
}
|
|
1850
|
-
try {
|
|
1851
|
-
const stat = statSync2(this.dbPath);
|
|
1852
|
-
const hasChanges = stat.mtimeMs > sinceTimestamp;
|
|
1853
|
-
const changedIds = hasChanges ? cachedSessions.map((s) => s.id) : [];
|
|
1854
|
-
return {
|
|
1855
|
-
hasChanges,
|
|
1856
|
-
changedIds,
|
|
1857
|
-
timestamp: Date.now()
|
|
1858
|
-
};
|
|
1859
|
-
} catch {
|
|
1860
|
-
return { hasChanges: false, timestamp: Date.now() };
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
/**
|
|
1864
|
-
* 增量扫描 - 重新查询数据库
|
|
1865
|
-
*/
|
|
1866
|
-
incrementalScan(_cachedSessions, _changedIds) {
|
|
1867
|
-
return this.scan();
|
|
1868
|
-
}
|
|
1869
1868
|
parsePartRow(partRow) {
|
|
1870
1869
|
const partData = JSON.parse(String(partRow.data ?? "{}"));
|
|
1871
1870
|
const partType = String(partData.type ?? "");
|
|
@@ -1969,11 +1968,11 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1969
1968
|
this.dbPath = this.findDbPath();
|
|
1970
1969
|
}
|
|
1971
1970
|
if (!this.dbPath) {
|
|
1972
|
-
throw new Error(
|
|
1971
|
+
throw new Error(`${this.displayName} database is missing`);
|
|
1973
1972
|
}
|
|
1974
1973
|
const db = openDbReadOnly(this.dbPath);
|
|
1975
1974
|
if (!db) {
|
|
1976
|
-
throw new Error(
|
|
1975
|
+
throw new Error(`${this.displayName} database is missing`);
|
|
1977
1976
|
}
|
|
1978
1977
|
try {
|
|
1979
1978
|
const sessionRow = db.prepare("SELECT * FROM session WHERE id = ?").get(sessionId);
|
|
@@ -1981,7 +1980,7 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
1981
1980
|
throw new Error(`Session not found: ${sessionId}`);
|
|
1982
1981
|
}
|
|
1983
1982
|
const id = String(sessionRow.id ?? sessionId);
|
|
1984
|
-
const slug =
|
|
1983
|
+
const slug = `${this.name}/${id}`;
|
|
1985
1984
|
const directory = String(sessionRow.directory ?? "");
|
|
1986
1985
|
const timeCreated = Number(sessionRow.time_created ?? 0);
|
|
1987
1986
|
const timeUpdated = Number(sessionRow.time_updated ?? timeCreated);
|
|
@@ -2052,6 +2051,20 @@ var OpenCodeAgent = class extends BaseAgent {
|
|
|
2052
2051
|
}
|
|
2053
2052
|
}
|
|
2054
2053
|
};
|
|
2054
|
+
function findOpenCodeDbPath() {
|
|
2055
|
+
if (!isSqliteAvailable()) return null;
|
|
2056
|
+
const roots = resolveProviderRoots();
|
|
2057
|
+
return firstExisting(join5(roots.opencodeRoot, "opencode.db"), "data/opencode/opencode.db");
|
|
2058
|
+
}
|
|
2059
|
+
var OpenCodeAgent = class extends OpenCodeSqliteAgent {
|
|
2060
|
+
constructor() {
|
|
2061
|
+
super({
|
|
2062
|
+
name: "opencode",
|
|
2063
|
+
displayName: "OpenCode",
|
|
2064
|
+
findDbPath: findOpenCodeDbPath
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
};
|
|
2055
2068
|
var KIMI_TOOL_TITLE_MAP = {
|
|
2056
2069
|
ReadFile: "read",
|
|
2057
2070
|
Glob: "glob",
|
|
@@ -2145,11 +2158,10 @@ function extractFirstUserTitle(contextFile, wireFile) {
|
|
|
2145
2158
|
}
|
|
2146
2159
|
return null;
|
|
2147
2160
|
}
|
|
2148
|
-
var KimiAgent = class extends
|
|
2161
|
+
var KimiAgent = class extends FileSystemSessionSource {
|
|
2149
2162
|
name = "kimi";
|
|
2150
2163
|
displayName = "Kimi-Cli";
|
|
2151
2164
|
basePath = null;
|
|
2152
|
-
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
2153
2165
|
projectMap = /* @__PURE__ */ new Map();
|
|
2154
2166
|
defaultModel = null;
|
|
2155
2167
|
findBasePath() {
|
|
@@ -2310,8 +2322,6 @@ var KimiAgent = class extends BaseAgent {
|
|
|
2310
2322
|
for (const dir of this.listSessionDirs()) {
|
|
2311
2323
|
const meta = getParsedSession(this.parseSessionDirResult(dir));
|
|
2312
2324
|
if (!meta) continue;
|
|
2313
|
-
meta.sourceFingerprint = this.sourceFingerprint(meta);
|
|
2314
|
-
this.sessionMetaMap.set(meta.id, meta);
|
|
2315
2325
|
refs.push({
|
|
2316
2326
|
sessionId: meta.id,
|
|
2317
2327
|
sourcePath: meta.sourcePath,
|
|
@@ -2336,87 +2346,6 @@ var KimiAgent = class extends BaseAgent {
|
|
|
2336
2346
|
stats
|
|
2337
2347
|
};
|
|
2338
2348
|
}
|
|
2339
|
-
getSessionMetaMap() {
|
|
2340
|
-
return this.sessionMetaMap;
|
|
2341
|
-
}
|
|
2342
|
-
setSessionMetaMap(meta) {
|
|
2343
|
-
this.sessionMetaMap = meta;
|
|
2344
|
-
}
|
|
2345
|
-
/**
|
|
2346
|
-
* 检测文件系统变更
|
|
2347
|
-
*/
|
|
2348
|
-
checkForChanges(sinceTimestamp, cachedSessions) {
|
|
2349
|
-
const changedIds = /* @__PURE__ */ new Set();
|
|
2350
|
-
const cachedIds = new Set(cachedSessions.map((session) => session.id));
|
|
2351
|
-
const currentIds = /* @__PURE__ */ new Set();
|
|
2352
|
-
for (const dir of this.listSessionDirs()) {
|
|
2353
|
-
const meta = getParsedSession(this.parseSessionDirResult(dir));
|
|
2354
|
-
if (!meta) continue;
|
|
2355
|
-
currentIds.add(meta.id);
|
|
2356
|
-
this.sessionMetaMap.set(meta.id, meta);
|
|
2357
|
-
if (!cachedIds.has(meta.id)) {
|
|
2358
|
-
changedIds.add(meta.id);
|
|
2359
|
-
continue;
|
|
2360
|
-
}
|
|
2361
|
-
const dataFile = meta.wireFile || meta.contextFile;
|
|
2362
|
-
try {
|
|
2363
|
-
const metaStat = statSync3(meta.metaFile);
|
|
2364
|
-
if (metaStat.mtimeMs > sinceTimestamp) {
|
|
2365
|
-
changedIds.add(meta.id);
|
|
2366
|
-
continue;
|
|
2367
|
-
}
|
|
2368
|
-
if (dataFile) {
|
|
2369
|
-
const dataStat = statSync3(dataFile);
|
|
2370
|
-
if (dataStat.mtimeMs > sinceTimestamp) {
|
|
2371
|
-
changedIds.add(meta.id);
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
} catch {
|
|
2375
|
-
changedIds.add(meta.id);
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
for (const session of cachedSessions) {
|
|
2379
|
-
if (!currentIds.has(session.id)) changedIds.add(session.id);
|
|
2380
|
-
}
|
|
2381
|
-
const changedIdList = Array.from(changedIds);
|
|
2382
|
-
return {
|
|
2383
|
-
hasChanges: changedIdList.length > 0,
|
|
2384
|
-
changedIds: changedIdList,
|
|
2385
|
-
timestamp: Date.now()
|
|
2386
|
-
};
|
|
2387
|
-
}
|
|
2388
|
-
/**
|
|
2389
|
-
* 增量扫描
|
|
2390
|
-
*/
|
|
2391
|
-
incrementalScan(cachedSessions, changedIds) {
|
|
2392
|
-
const sessionMap = new Map(cachedSessions.map((s) => [s.id, s]));
|
|
2393
|
-
const changedIdSet = new Set(changedIds);
|
|
2394
|
-
for (const id of changedIdSet) {
|
|
2395
|
-
sessionMap.delete(id);
|
|
2396
|
-
this.sessionMetaMap.delete(id);
|
|
2397
|
-
}
|
|
2398
|
-
for (const dir of this.listSessionDirs()) {
|
|
2399
|
-
try {
|
|
2400
|
-
const meta = getParsedSession(this.parseSessionDirResult(dir));
|
|
2401
|
-
if (!meta) continue;
|
|
2402
|
-
if (changedIdSet.has(meta.id)) {
|
|
2403
|
-
this.sessionMetaMap.set(meta.id, meta);
|
|
2404
|
-
const stats = this.extractStats(meta.sourcePath);
|
|
2405
|
-
sessionMap.set(meta.id, {
|
|
2406
|
-
id: meta.id,
|
|
2407
|
-
slug: `kimi/${meta.id}`,
|
|
2408
|
-
title: meta.title,
|
|
2409
|
-
directory: meta.cwd,
|
|
2410
|
-
time_created: meta.createdAt,
|
|
2411
|
-
time_updated: meta.createdAt,
|
|
2412
|
-
stats
|
|
2413
|
-
});
|
|
2414
|
-
}
|
|
2415
|
-
} catch {
|
|
2416
|
-
}
|
|
2417
|
-
}
|
|
2418
|
-
return Array.from(sessionMap.values());
|
|
2419
|
-
}
|
|
2420
2349
|
getSessionData(sessionId) {
|
|
2421
2350
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
2422
2351
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
@@ -3019,12 +2948,12 @@ function extractPatchContent(lines, startIndex) {
|
|
|
3019
2948
|
}
|
|
3020
2949
|
return { text: contentLines.join("\n"), nextLineIndex: i };
|
|
3021
2950
|
}
|
|
3022
|
-
var CodexAgent = class extends
|
|
2951
|
+
var CodexAgent = class extends FileSystemSessionSource {
|
|
3023
2952
|
name = "codex";
|
|
3024
2953
|
displayName = "Codex";
|
|
3025
2954
|
basePath = null;
|
|
3026
2955
|
sessionIndexCache = /* @__PURE__ */ new Map();
|
|
3027
|
-
|
|
2956
|
+
sessionIndexMtime = null;
|
|
3028
2957
|
// ---- BaseAgent implementation ----
|
|
3029
2958
|
findBasePath() {
|
|
3030
2959
|
const roots = resolveProviderRoots();
|
|
@@ -3087,120 +3016,32 @@ var CodexAgent = class extends BaseAgent {
|
|
|
3087
3016
|
}
|
|
3088
3017
|
return head;
|
|
3089
3018
|
}
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
}
|
|
3112
|
-
const meta = this.sessionMetaMap.get(session.id);
|
|
3113
|
-
if (!meta) {
|
|
3114
|
-
changedIds.add(session.id);
|
|
3115
|
-
continue;
|
|
3116
|
-
}
|
|
3019
|
+
getSessionData(sessionId) {
|
|
3020
|
+
const meta = this.sessionMetaMap.get(sessionId);
|
|
3021
|
+
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
3022
|
+
if (!existsSync7(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
3023
|
+
const content = readFileSync5(meta.sourcePath, "utf-8");
|
|
3024
|
+
const messages = [];
|
|
3025
|
+
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
3026
|
+
let totalInputTokens = 0;
|
|
3027
|
+
let totalOutputTokens = 0;
|
|
3028
|
+
let totalCacheReadTokens = 0;
|
|
3029
|
+
let totalCost = 0;
|
|
3030
|
+
let currentAssistantIndex = null;
|
|
3031
|
+
let latestAssistantTextIndex = null;
|
|
3032
|
+
let pendingPlan = null;
|
|
3033
|
+
let activeModel = meta.model;
|
|
3034
|
+
let prevCumulativeTotal = 0;
|
|
3035
|
+
let prevInput = 0;
|
|
3036
|
+
let prevOutput = 0;
|
|
3037
|
+
let prevReasoning = 0;
|
|
3038
|
+
let prevCachedInput = 0;
|
|
3039
|
+
for (const record of parseJsonlLines(content)) {
|
|
3117
3040
|
try {
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
changedIds.add(session.id);
|
|
3123
|
-
}
|
|
3124
|
-
}
|
|
3125
|
-
const hasAddedSessions = currentFiles.some((file) => !cachedIds.has(extractSessionId(file)));
|
|
3126
|
-
if (hasAddedSessions || changedIds.size > 0) {
|
|
3127
|
-
this.sessionIndexCache.clear();
|
|
3128
|
-
}
|
|
3129
|
-
return {
|
|
3130
|
-
hasChanges: changedIds.size > 0 || hasAddedSessions,
|
|
3131
|
-
changedIds: Array.from(changedIds),
|
|
3132
|
-
timestamp: Date.now()
|
|
3133
|
-
};
|
|
3134
|
-
}
|
|
3135
|
-
/**
|
|
3136
|
-
* 增量扫描
|
|
3137
|
-
*/
|
|
3138
|
-
incrementalScan(cachedSessions, changedIds) {
|
|
3139
|
-
if (!this.basePath) return cachedSessions;
|
|
3140
|
-
const sessionMap = new Map(cachedSessions.map((s) => [s.id, s]));
|
|
3141
|
-
const changedSet = new Set(changedIds);
|
|
3142
|
-
const currentFiles = this.listRolloutFiles();
|
|
3143
|
-
const currentIds = new Set(currentFiles.map((file) => extractSessionId(file)));
|
|
3144
|
-
for (const session of cachedSessions) {
|
|
3145
|
-
if (!currentIds.has(session.id)) {
|
|
3146
|
-
sessionMap.delete(session.id);
|
|
3147
|
-
this.sessionMetaMap.delete(session.id);
|
|
3148
|
-
}
|
|
3149
|
-
}
|
|
3150
|
-
for (const file of currentFiles) {
|
|
3151
|
-
try {
|
|
3152
|
-
const sessionId = extractSessionId(file);
|
|
3153
|
-
if (changedSet.has(sessionId)) {
|
|
3154
|
-
const head = this.parseSessionHead(file);
|
|
3155
|
-
if (head) {
|
|
3156
|
-
sessionMap.set(head.id, head);
|
|
3157
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, file));
|
|
3158
|
-
}
|
|
3159
|
-
}
|
|
3160
|
-
} catch {
|
|
3161
|
-
}
|
|
3162
|
-
}
|
|
3163
|
-
for (const file of currentFiles) {
|
|
3164
|
-
try {
|
|
3165
|
-
const sessionId = extractSessionId(file);
|
|
3166
|
-
if (!sessionMap.has(sessionId)) {
|
|
3167
|
-
const head = this.parseSessionHead(file);
|
|
3168
|
-
if (head) {
|
|
3169
|
-
sessionMap.set(head.id, head);
|
|
3170
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, file));
|
|
3171
|
-
}
|
|
3172
|
-
}
|
|
3173
|
-
} catch {
|
|
3174
|
-
}
|
|
3175
|
-
}
|
|
3176
|
-
return Array.from(sessionMap.values());
|
|
3177
|
-
}
|
|
3178
|
-
getSessionData(sessionId) {
|
|
3179
|
-
const meta = this.sessionMetaMap.get(sessionId);
|
|
3180
|
-
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
3181
|
-
if (!existsSync7(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
3182
|
-
const content = readFileSync5(meta.sourcePath, "utf-8");
|
|
3183
|
-
const messages = [];
|
|
3184
|
-
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
3185
|
-
let totalInputTokens = 0;
|
|
3186
|
-
let totalOutputTokens = 0;
|
|
3187
|
-
let totalCacheReadTokens = 0;
|
|
3188
|
-
let totalCost = 0;
|
|
3189
|
-
let currentAssistantIndex = null;
|
|
3190
|
-
let latestAssistantTextIndex = null;
|
|
3191
|
-
let pendingPlan = null;
|
|
3192
|
-
let activeModel = meta.model;
|
|
3193
|
-
let prevCumulativeTotal = 0;
|
|
3194
|
-
let prevInput = 0;
|
|
3195
|
-
let prevOutput = 0;
|
|
3196
|
-
let prevReasoning = 0;
|
|
3197
|
-
let prevCachedInput = 0;
|
|
3198
|
-
for (const record of parseJsonlLines(content)) {
|
|
3199
|
-
try {
|
|
3200
|
-
const recordType = String(record["type"] ?? "");
|
|
3201
|
-
if (recordType === "turn_context") {
|
|
3202
|
-
const payload = record["payload"] ?? {};
|
|
3203
|
-
activeModel = extractModelName(payload["model"]) ?? activeModel;
|
|
3041
|
+
const recordType = String(record["type"] ?? "");
|
|
3042
|
+
if (recordType === "turn_context") {
|
|
3043
|
+
const payload = record["payload"] ?? {};
|
|
3044
|
+
activeModel = extractModelName(payload["model"]) ?? activeModel;
|
|
3204
3045
|
}
|
|
3205
3046
|
const result = this.convertRecord(
|
|
3206
3047
|
record,
|
|
@@ -3352,14 +3193,6 @@ var CodexAgent = class extends BaseAgent {
|
|
|
3352
3193
|
updatedAt: head.time_updated ?? head.time_created
|
|
3353
3194
|
};
|
|
3354
3195
|
}
|
|
3355
|
-
hasMetaChanged(meta) {
|
|
3356
|
-
if (meta.headIndexVersion !== HEAD_INDEX_VERSION2) return true;
|
|
3357
|
-
if (meta.parserVersion !== PARSER_VERSION) return true;
|
|
3358
|
-
if (typeof meta.sourceMtimeMs !== "number") return true;
|
|
3359
|
-
if (statSync4(meta.sourcePath).mtimeMs !== meta.sourceMtimeMs) return true;
|
|
3360
|
-
const indexTitle = this.getTitleForSession(meta.id);
|
|
3361
|
-
return indexTitle !== null && indexTitle !== meta.title;
|
|
3362
|
-
}
|
|
3363
3196
|
sourceFingerprint(file) {
|
|
3364
3197
|
const stat = statSync4(file);
|
|
3365
3198
|
const sessionId = extractSessionId(file);
|
|
@@ -3384,9 +3217,12 @@ var CodexAgent = class extends BaseAgent {
|
|
|
3384
3217
|
}
|
|
3385
3218
|
// ---- Session index ----
|
|
3386
3219
|
loadSessionIndex() {
|
|
3387
|
-
if (this.sessionIndexCache.size > 0) return;
|
|
3388
3220
|
const indexPath = this.getSessionIndexPath();
|
|
3389
|
-
|
|
3221
|
+
const mtime = this.getFileMtimeMs(indexPath);
|
|
3222
|
+
if (this.sessionIndexCache.size > 0 && this.sessionIndexMtime === mtime) return;
|
|
3223
|
+
this.sessionIndexCache.clear();
|
|
3224
|
+
this.sessionIndexMtime = mtime;
|
|
3225
|
+
if (mtime === null) return;
|
|
3390
3226
|
try {
|
|
3391
3227
|
const content = readFileSync5(indexPath, "utf-8");
|
|
3392
3228
|
for (const record of parseJsonlLines(content)) {
|
|
@@ -4122,14 +3958,18 @@ function convertActionToPart(action, timestampMs) {
|
|
|
4122
3958
|
}
|
|
4123
3959
|
return null;
|
|
4124
3960
|
}
|
|
4125
|
-
var CursorAgent = class extends
|
|
3961
|
+
var CursorAgent = class extends DatabaseSessionSource {
|
|
4126
3962
|
name = "cursor";
|
|
4127
3963
|
displayName = "Cursor";
|
|
4128
3964
|
dbPath = null;
|
|
4129
3965
|
// Cache composer data from scan so getSessionData can reuse it
|
|
4130
3966
|
composerCache = /* @__PURE__ */ new Map();
|
|
4131
|
-
|
|
4132
|
-
|
|
3967
|
+
getDatabasePath() {
|
|
3968
|
+
if (!this.dbPath) {
|
|
3969
|
+
this.dbPath = this.findDbPath();
|
|
3970
|
+
}
|
|
3971
|
+
return this.dbPath;
|
|
3972
|
+
}
|
|
4133
3973
|
findDbPath() {
|
|
4134
3974
|
if (!isSqliteAvailable()) return null;
|
|
4135
3975
|
const dataPath = getCursorDataPath();
|
|
@@ -4332,42 +4172,6 @@ var CursorAgent = class extends BaseAgent {
|
|
|
4332
4172
|
db.close();
|
|
4333
4173
|
}
|
|
4334
4174
|
}
|
|
4335
|
-
getSessionMetaMap() {
|
|
4336
|
-
return this.sessionMetaMap;
|
|
4337
|
-
}
|
|
4338
|
-
setSessionMetaMap(meta) {
|
|
4339
|
-
this.sessionMetaMap = meta;
|
|
4340
|
-
}
|
|
4341
|
-
/**
|
|
4342
|
-
* 检测数据库变更
|
|
4343
|
-
* 对于 SQLite,检测数据库文件修改时间
|
|
4344
|
-
*/
|
|
4345
|
-
checkForChanges(sinceTimestamp, cachedSessions) {
|
|
4346
|
-
if (!this.dbPath) {
|
|
4347
|
-
this.dbPath = this.findDbPath();
|
|
4348
|
-
}
|
|
4349
|
-
if (!this.dbPath || !existsSync8(this.dbPath)) {
|
|
4350
|
-
return { hasChanges: false, timestamp: Date.now() };
|
|
4351
|
-
}
|
|
4352
|
-
try {
|
|
4353
|
-
const stat = statSync5(this.dbPath);
|
|
4354
|
-
const hasChanges = stat.mtimeMs > sinceTimestamp;
|
|
4355
|
-
const changedIds = hasChanges ? cachedSessions.map((s) => s.id) : [];
|
|
4356
|
-
return {
|
|
4357
|
-
hasChanges,
|
|
4358
|
-
changedIds,
|
|
4359
|
-
timestamp: Date.now()
|
|
4360
|
-
};
|
|
4361
|
-
} catch {
|
|
4362
|
-
return { hasChanges: false, timestamp: Date.now() };
|
|
4363
|
-
}
|
|
4364
|
-
}
|
|
4365
|
-
/**
|
|
4366
|
-
* 增量扫描 - 重新查询数据库
|
|
4367
|
-
*/
|
|
4368
|
-
incrementalScan(_cachedSessions, _changedIds) {
|
|
4369
|
-
return this.scan();
|
|
4370
|
-
}
|
|
4371
4175
|
getSessionData(sessionId) {
|
|
4372
4176
|
if (!this.dbPath) {
|
|
4373
4177
|
this.dbPath = this.findDbPath();
|
|
@@ -4766,11 +4570,10 @@ function buildCurrentPathEntries(entries) {
|
|
|
4766
4570
|
}
|
|
4767
4571
|
return path2.reverse();
|
|
4768
4572
|
}
|
|
4769
|
-
var PiAgent = class extends
|
|
4573
|
+
var PiAgent = class extends FileSystemSessionSource {
|
|
4770
4574
|
name = "pi";
|
|
4771
4575
|
displayName = "Pi";
|
|
4772
4576
|
basePath = null;
|
|
4773
|
-
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
4774
4577
|
findBasePath() {
|
|
4775
4578
|
const roots = resolveProviderRoots();
|
|
4776
4579
|
return firstExisting(join9(roots.piRoot, "agent", "sessions"), "data/pi");
|
|
@@ -4818,69 +4621,6 @@ var PiAgent = class extends BaseAgent {
|
|
|
4818
4621
|
}
|
|
4819
4622
|
return head;
|
|
4820
4623
|
}
|
|
4821
|
-
getSessionMetaMap() {
|
|
4822
|
-
return this.sessionMetaMap;
|
|
4823
|
-
}
|
|
4824
|
-
setSessionMetaMap(meta) {
|
|
4825
|
-
this.sessionMetaMap = meta;
|
|
4826
|
-
}
|
|
4827
|
-
checkForChanges(_sinceTimestamp, cachedSessions) {
|
|
4828
|
-
if (!this.basePath) return { hasChanges: false, timestamp: Date.now() };
|
|
4829
|
-
const currentFiles = this.listSessionFiles();
|
|
4830
|
-
const currentIds = new Set(currentFiles.map((file) => extractSessionIdFromFilename(file)));
|
|
4831
|
-
const cachedIds = new Set(cachedSessions.map((session) => session.id));
|
|
4832
|
-
const changedIds = /* @__PURE__ */ new Set();
|
|
4833
|
-
for (const session of cachedSessions) {
|
|
4834
|
-
if (!currentIds.has(session.id)) {
|
|
4835
|
-
changedIds.add(session.id);
|
|
4836
|
-
continue;
|
|
4837
|
-
}
|
|
4838
|
-
const meta = this.sessionMetaMap.get(session.id);
|
|
4839
|
-
if (!meta) {
|
|
4840
|
-
changedIds.add(session.id);
|
|
4841
|
-
continue;
|
|
4842
|
-
}
|
|
4843
|
-
try {
|
|
4844
|
-
if (this.hasMetaChanged(meta)) changedIds.add(session.id);
|
|
4845
|
-
} catch {
|
|
4846
|
-
changedIds.add(session.id);
|
|
4847
|
-
}
|
|
4848
|
-
}
|
|
4849
|
-
const hasAddedSessions = currentFiles.some(
|
|
4850
|
-
(file) => !cachedIds.has(extractSessionIdFromFilename(file))
|
|
4851
|
-
);
|
|
4852
|
-
return {
|
|
4853
|
-
hasChanges: changedIds.size > 0 || hasAddedSessions,
|
|
4854
|
-
changedIds: [...changedIds],
|
|
4855
|
-
timestamp: Date.now()
|
|
4856
|
-
};
|
|
4857
|
-
}
|
|
4858
|
-
incrementalScan(cachedSessions, changedIds) {
|
|
4859
|
-
if (!this.basePath) return cachedSessions;
|
|
4860
|
-
const sessionMap = new Map(cachedSessions.map((session) => [session.id, session]));
|
|
4861
|
-
const changedSet = new Set(changedIds);
|
|
4862
|
-
const currentFiles = this.listSessionFiles();
|
|
4863
|
-
const currentIds = new Set(currentFiles.map((file) => extractSessionIdFromFilename(file)));
|
|
4864
|
-
for (const session of cachedSessions) {
|
|
4865
|
-
if (!currentIds.has(session.id)) {
|
|
4866
|
-
sessionMap.delete(session.id);
|
|
4867
|
-
this.sessionMetaMap.delete(session.id);
|
|
4868
|
-
}
|
|
4869
|
-
}
|
|
4870
|
-
for (const file of currentFiles) {
|
|
4871
|
-
try {
|
|
4872
|
-
const sessionId = extractSessionIdFromFilename(file);
|
|
4873
|
-
if (!changedSet.has(sessionId) && sessionMap.has(sessionId)) continue;
|
|
4874
|
-
const head = getParsedSession(this.parseSessionHeadResult(file));
|
|
4875
|
-
if (head) {
|
|
4876
|
-
sessionMap.set(head.id, head);
|
|
4877
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, file));
|
|
4878
|
-
}
|
|
4879
|
-
} catch {
|
|
4880
|
-
}
|
|
4881
|
-
}
|
|
4882
|
-
return [...sessionMap.values()];
|
|
4883
|
-
}
|
|
4884
4624
|
getSessionData(sessionId) {
|
|
4885
4625
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
4886
4626
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
@@ -4943,11 +4683,6 @@ var PiAgent = class extends BaseAgent {
|
|
|
4943
4683
|
updatedAt: head.time_updated ?? head.time_created
|
|
4944
4684
|
};
|
|
4945
4685
|
}
|
|
4946
|
-
hasMetaChanged(meta) {
|
|
4947
|
-
if (meta.headIndexVersion !== HEAD_INDEX_VERSION3) return true;
|
|
4948
|
-
if (meta.parserVersion !== PARSER_VERSION2) return true;
|
|
4949
|
-
return statSync6(meta.sourcePath).mtimeMs !== meta.sourceMtimeMs;
|
|
4950
|
-
}
|
|
4951
4686
|
sourceFingerprint(file) {
|
|
4952
4687
|
const stat = statSync6(file);
|
|
4953
4688
|
return JSON.stringify([HEAD_INDEX_VERSION3, PARSER_VERSION2, stat.mtimeMs, stat.size]);
|
|
@@ -5274,6 +5009,21 @@ var PiAgent = class extends BaseAgent {
|
|
|
5274
5009
|
};
|
|
5275
5010
|
}
|
|
5276
5011
|
};
|
|
5012
|
+
function findZCodeDbPath() {
|
|
5013
|
+
if (!isSqliteAvailable()) return null;
|
|
5014
|
+
const roots = resolveProviderRoots();
|
|
5015
|
+
if (!roots.zcodeRoot) return null;
|
|
5016
|
+
return firstExisting(join10(roots.zcodeRoot, "cli", "db", "db.sqlite"));
|
|
5017
|
+
}
|
|
5018
|
+
var ZCodeAgent = class extends OpenCodeSqliteAgent {
|
|
5019
|
+
constructor() {
|
|
5020
|
+
super({
|
|
5021
|
+
name: "zcode",
|
|
5022
|
+
displayName: "ZCode",
|
|
5023
|
+
findDbPath: findZCodeDbPath
|
|
5024
|
+
});
|
|
5025
|
+
}
|
|
5026
|
+
};
|
|
5277
5027
|
registerAgent({
|
|
5278
5028
|
name: "claudecode",
|
|
5279
5029
|
displayName: "Claude Code",
|
|
@@ -5286,6 +5036,12 @@ registerAgent({
|
|
|
5286
5036
|
icon: "/icon/agent/opencode.svg",
|
|
5287
5037
|
create: () => new OpenCodeAgent()
|
|
5288
5038
|
});
|
|
5039
|
+
registerAgent({
|
|
5040
|
+
name: "zcode",
|
|
5041
|
+
displayName: "ZCode",
|
|
5042
|
+
icon: "/icon/agent/zcode.svg",
|
|
5043
|
+
create: () => new ZCodeAgent()
|
|
5044
|
+
});
|
|
5289
5045
|
registerAgent({
|
|
5290
5046
|
name: "kimi",
|
|
5291
5047
|
displayName: "Kimi-Cli",
|
|
@@ -5805,688 +5561,861 @@ function extractSessionFileActivity(agentName, sessionId, projectIdentityKey, me
|
|
|
5805
5561
|
extractFileActivityOccurrences(messages)
|
|
5806
5562
|
);
|
|
5807
5563
|
}
|
|
5808
|
-
var CACHE_SCHEMA_VERSION = 13;
|
|
5809
|
-
var CACHE_INITIALIZATION_VERSION = "session-cache-v2";
|
|
5810
5564
|
var CACHE_FILENAME = "codesesh.db";
|
|
5811
5565
|
var LEGACY_CACHE_FILENAME = "scan-cache.json";
|
|
5812
5566
|
var SEARCH_INDEX_BULK_SYNC_THRESHOLD = 100;
|
|
5813
5567
|
var ftsIntegrityCheckedPath = null;
|
|
5568
|
+
function getFtsIntegrityCheckedPath() {
|
|
5569
|
+
return ftsIntegrityCheckedPath;
|
|
5570
|
+
}
|
|
5571
|
+
function setFtsIntegrityCheckedPath(path2) {
|
|
5572
|
+
ftsIntegrityCheckedPath = path2;
|
|
5573
|
+
}
|
|
5814
5574
|
function getCacheDir2() {
|
|
5815
|
-
return
|
|
5575
|
+
return join11(homedir4(), ".cache", "codesesh");
|
|
5816
5576
|
}
|
|
5817
5577
|
function getCachePath2() {
|
|
5818
|
-
return
|
|
5578
|
+
return join11(getCacheDir2(), CACHE_FILENAME);
|
|
5819
5579
|
}
|
|
5820
5580
|
function getLegacyCachePath() {
|
|
5821
|
-
return
|
|
5581
|
+
return join11(getCacheDir2(), LEGACY_CACHE_FILENAME);
|
|
5822
5582
|
}
|
|
5823
5583
|
function hasCacheStorage() {
|
|
5824
5584
|
return existsSync11(getCachePath2());
|
|
5825
5585
|
}
|
|
5826
|
-
function
|
|
5827
|
-
|
|
5828
|
-
const db = openDb(cachePath);
|
|
5829
|
-
if (!db) return null;
|
|
5830
|
-
try {
|
|
5831
|
-
ensureSchema(db, cachePath);
|
|
5832
|
-
return fn(db);
|
|
5833
|
-
} catch {
|
|
5834
|
-
return null;
|
|
5835
|
-
} finally {
|
|
5836
|
-
db.close();
|
|
5837
|
-
}
|
|
5586
|
+
function likePattern(value) {
|
|
5587
|
+
return `%${value.trim().toLowerCase().replace(/[\\%_]/g, "\\$&")}%`;
|
|
5838
5588
|
}
|
|
5839
|
-
function
|
|
5840
|
-
const
|
|
5841
|
-
if (
|
|
5842
|
-
|
|
5843
|
-
return fn(db);
|
|
5844
|
-
} catch {
|
|
5845
|
-
return null;
|
|
5846
|
-
} finally {
|
|
5847
|
-
db.close();
|
|
5848
|
-
}
|
|
5589
|
+
function filePathFtsQuery(value) {
|
|
5590
|
+
const path2 = normalizeFilePathSearch(value);
|
|
5591
|
+
if (path2.length < 3) return null;
|
|
5592
|
+
return `"${path2.replaceAll('"', '""')}"`;
|
|
5849
5593
|
}
|
|
5850
|
-
function
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5594
|
+
function escapeRegExp(value) {
|
|
5595
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5596
|
+
}
|
|
5597
|
+
function normalizeFilePathSearch(value) {
|
|
5598
|
+
return value.trim().replace(/^"|"$/g, "");
|
|
5599
|
+
}
|
|
5600
|
+
function stringifyOptionalJson(value) {
|
|
5601
|
+
return value == null ? null : JSON.stringify(value);
|
|
5602
|
+
}
|
|
5603
|
+
function parseOptionalJson(value) {
|
|
5604
|
+
return value == null ? void 0 : JSON.parse(String(value));
|
|
5605
|
+
}
|
|
5606
|
+
function sourcePathFromMeta(meta) {
|
|
5607
|
+
return typeof meta?.sourcePath === "string" ? meta.sourcePath : null;
|
|
5608
|
+
}
|
|
5609
|
+
function sourcePathFromMetaJson(metaJson) {
|
|
5610
|
+
if (!metaJson) return null;
|
|
5611
|
+
const meta = JSON.parse(metaJson);
|
|
5612
|
+
return sourcePathFromMeta(meta);
|
|
5613
|
+
}
|
|
5614
|
+
function prepareUpsertCachedSession(db) {
|
|
5615
|
+
return db.prepare(`
|
|
5616
|
+
INSERT INTO cached_sessions(agent_name, session_id, session_json, meta_json)
|
|
5617
|
+
VALUES (?, ?, ?, ?)
|
|
5618
|
+
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
5619
|
+
session_json = excluded.session_json,
|
|
5620
|
+
meta_json = excluded.meta_json
|
|
5876
5621
|
`);
|
|
5877
5622
|
}
|
|
5878
|
-
function
|
|
5879
|
-
db.
|
|
5880
|
-
|
|
5881
|
-
agent_name
|
|
5882
|
-
session_id
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
total_input_tokens INTEGER NOT NULL,
|
|
5896
|
-
total_output_tokens INTEGER NOT NULL,
|
|
5897
|
-
total_cache_read_tokens INTEGER,
|
|
5898
|
-
total_cache_create_tokens INTEGER,
|
|
5899
|
-
total_cost REAL NOT NULL,
|
|
5900
|
-
cost_source TEXT,
|
|
5901
|
-
total_tokens INTEGER,
|
|
5902
|
-
model_usage_json TEXT,
|
|
5903
|
-
smart_tags_json TEXT,
|
|
5904
|
-
smart_tags_source_updated_at INTEGER,
|
|
5905
|
-
meta_json TEXT,
|
|
5906
|
-
PRIMARY KEY (agent_name, session_id)
|
|
5907
|
-
);
|
|
5908
|
-
|
|
5909
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_agent_activity
|
|
5910
|
-
ON sessions(agent_name, activity_time);
|
|
5911
|
-
|
|
5912
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_project
|
|
5913
|
-
ON sessions(project_identity_kind, project_identity_key, activity_time);
|
|
5914
|
-
|
|
5915
|
-
CREATE TABLE IF NOT EXISTS messages (
|
|
5916
|
-
agent_name TEXT NOT NULL,
|
|
5917
|
-
session_id TEXT NOT NULL,
|
|
5918
|
-
message_index INTEGER NOT NULL,
|
|
5919
|
-
message_id TEXT NOT NULL,
|
|
5920
|
-
role TEXT NOT NULL,
|
|
5921
|
-
time_created INTEGER NOT NULL,
|
|
5922
|
-
time_completed INTEGER,
|
|
5923
|
-
agent TEXT,
|
|
5924
|
-
mode TEXT,
|
|
5925
|
-
model TEXT,
|
|
5926
|
-
provider TEXT,
|
|
5927
|
-
tokens_json TEXT,
|
|
5928
|
-
cost REAL,
|
|
5929
|
-
cost_source TEXT,
|
|
5930
|
-
parts_json TEXT NOT NULL,
|
|
5931
|
-
subagent_id TEXT,
|
|
5932
|
-
nickname TEXT,
|
|
5933
|
-
content_text TEXT NOT NULL,
|
|
5934
|
-
tool_metadata_json TEXT,
|
|
5935
|
-
PRIMARY KEY (agent_name, session_id, message_index),
|
|
5936
|
-
FOREIGN KEY (agent_name, session_id)
|
|
5937
|
-
REFERENCES sessions(agent_name, session_id)
|
|
5938
|
-
ON DELETE CASCADE
|
|
5939
|
-
);
|
|
5940
|
-
|
|
5941
|
-
CREATE INDEX IF NOT EXISTS idx_messages_session
|
|
5942
|
-
ON messages(agent_name, session_id, message_index);
|
|
5623
|
+
function prepareUpsertProjectSession(db) {
|
|
5624
|
+
return db.prepare(`
|
|
5625
|
+
INSERT INTO project_sessions(
|
|
5626
|
+
agent_name,
|
|
5627
|
+
session_id,
|
|
5628
|
+
identity_kind,
|
|
5629
|
+
identity_key,
|
|
5630
|
+
display_name,
|
|
5631
|
+
directory,
|
|
5632
|
+
activity_time
|
|
5633
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
5634
|
+
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
5635
|
+
identity_kind = excluded.identity_kind,
|
|
5636
|
+
identity_key = excluded.identity_key,
|
|
5637
|
+
display_name = excluded.display_name,
|
|
5638
|
+
directory = excluded.directory,
|
|
5639
|
+
activity_time = excluded.activity_time
|
|
5943
5640
|
`);
|
|
5944
|
-
createMessageToolTables(db);
|
|
5945
5641
|
}
|
|
5946
|
-
function
|
|
5947
|
-
db.
|
|
5948
|
-
|
|
5949
|
-
agent_name
|
|
5950
|
-
session_id
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5642
|
+
function prepareUpsertSession(db) {
|
|
5643
|
+
return db.prepare(`
|
|
5644
|
+
INSERT INTO sessions(
|
|
5645
|
+
agent_name,
|
|
5646
|
+
session_id,
|
|
5647
|
+
sort_index,
|
|
5648
|
+
slug,
|
|
5649
|
+
title,
|
|
5650
|
+
source_path,
|
|
5651
|
+
directory,
|
|
5652
|
+
project_identity_kind,
|
|
5653
|
+
project_identity_key,
|
|
5654
|
+
project_display_name,
|
|
5655
|
+
time_created,
|
|
5656
|
+
time_updated,
|
|
5657
|
+
activity_time,
|
|
5658
|
+
message_count,
|
|
5659
|
+
total_input_tokens,
|
|
5660
|
+
total_output_tokens,
|
|
5661
|
+
total_cache_read_tokens,
|
|
5662
|
+
total_cache_create_tokens,
|
|
5663
|
+
total_cost,
|
|
5664
|
+
cost_source,
|
|
5665
|
+
total_tokens,
|
|
5666
|
+
model_usage_json,
|
|
5667
|
+
smart_tags_json,
|
|
5668
|
+
smart_tags_source_updated_at,
|
|
5669
|
+
meta_json
|
|
5670
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5671
|
+
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
5672
|
+
sort_index = excluded.sort_index,
|
|
5673
|
+
slug = excluded.slug,
|
|
5674
|
+
title = excluded.title,
|
|
5675
|
+
source_path = excluded.source_path,
|
|
5676
|
+
directory = excluded.directory,
|
|
5677
|
+
project_identity_kind = excluded.project_identity_kind,
|
|
5678
|
+
project_identity_key = excluded.project_identity_key,
|
|
5679
|
+
project_display_name = excluded.project_display_name,
|
|
5680
|
+
time_created = excluded.time_created,
|
|
5681
|
+
time_updated = excluded.time_updated,
|
|
5682
|
+
activity_time = excluded.activity_time,
|
|
5683
|
+
message_count = excluded.message_count,
|
|
5684
|
+
total_input_tokens = excluded.total_input_tokens,
|
|
5685
|
+
total_output_tokens = excluded.total_output_tokens,
|
|
5686
|
+
total_cache_read_tokens = excluded.total_cache_read_tokens,
|
|
5687
|
+
total_cache_create_tokens = excluded.total_cache_create_tokens,
|
|
5688
|
+
total_cost = excluded.total_cost,
|
|
5689
|
+
cost_source = excluded.cost_source,
|
|
5690
|
+
total_tokens = excluded.total_tokens,
|
|
5691
|
+
model_usage_json = excluded.model_usage_json,
|
|
5692
|
+
smart_tags_json = excluded.smart_tags_json,
|
|
5693
|
+
smart_tags_source_updated_at = excluded.smart_tags_source_updated_at,
|
|
5694
|
+
meta_json = excluded.meta_json
|
|
5961
5695
|
`);
|
|
5962
5696
|
}
|
|
5963
|
-
function
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5697
|
+
function prepareUpsertIndexedSession(db) {
|
|
5698
|
+
return db.prepare(`
|
|
5699
|
+
INSERT INTO sessions(
|
|
5700
|
+
agent_name,
|
|
5701
|
+
session_id,
|
|
5702
|
+
sort_index,
|
|
5703
|
+
slug,
|
|
5704
|
+
title,
|
|
5705
|
+
source_path,
|
|
5706
|
+
directory,
|
|
5707
|
+
project_identity_kind,
|
|
5708
|
+
project_identity_key,
|
|
5709
|
+
project_display_name,
|
|
5710
|
+
time_created,
|
|
5711
|
+
time_updated,
|
|
5712
|
+
activity_time,
|
|
5713
|
+
message_count,
|
|
5714
|
+
total_input_tokens,
|
|
5715
|
+
total_output_tokens,
|
|
5716
|
+
total_cache_read_tokens,
|
|
5717
|
+
total_cache_create_tokens,
|
|
5718
|
+
total_cost,
|
|
5719
|
+
cost_source,
|
|
5720
|
+
total_tokens,
|
|
5721
|
+
model_usage_json,
|
|
5722
|
+
smart_tags_json,
|
|
5723
|
+
smart_tags_source_updated_at,
|
|
5724
|
+
meta_json
|
|
5725
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5726
|
+
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
5727
|
+
slug = excluded.slug,
|
|
5728
|
+
title = excluded.title,
|
|
5729
|
+
directory = excluded.directory,
|
|
5730
|
+
project_identity_kind = excluded.project_identity_kind,
|
|
5731
|
+
project_identity_key = excluded.project_identity_key,
|
|
5732
|
+
project_display_name = excluded.project_display_name,
|
|
5733
|
+
time_created = excluded.time_created,
|
|
5734
|
+
time_updated = excluded.time_updated,
|
|
5735
|
+
activity_time = excluded.activity_time,
|
|
5736
|
+
message_count = excluded.message_count,
|
|
5737
|
+
total_input_tokens = excluded.total_input_tokens,
|
|
5738
|
+
total_output_tokens = excluded.total_output_tokens,
|
|
5739
|
+
total_cache_read_tokens = excluded.total_cache_read_tokens,
|
|
5740
|
+
total_cache_create_tokens = excluded.total_cache_create_tokens,
|
|
5741
|
+
total_cost = excluded.total_cost,
|
|
5742
|
+
cost_source = excluded.cost_source,
|
|
5743
|
+
total_tokens = excluded.total_tokens,
|
|
5744
|
+
model_usage_json = excluded.model_usage_json,
|
|
5745
|
+
smart_tags_json = excluded.smart_tags_json,
|
|
5746
|
+
smart_tags_source_updated_at = excluded.smart_tags_source_updated_at
|
|
5973
5747
|
`);
|
|
5974
|
-
createMessageSearchTriggers(db);
|
|
5975
5748
|
}
|
|
5976
|
-
function
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5749
|
+
function upsertSessionRow(statement, agentName, session, metaJson, sortIndex, sourcePath) {
|
|
5750
|
+
const identity = session.project_identity ?? computeIdentity(session.directory, realFs);
|
|
5751
|
+
const activityTime = session.time_updated ?? session.time_created;
|
|
5752
|
+
statement.run(
|
|
5753
|
+
agentName,
|
|
5754
|
+
session.id,
|
|
5755
|
+
sortIndex,
|
|
5756
|
+
session.slug,
|
|
5757
|
+
session.title,
|
|
5758
|
+
sourcePath,
|
|
5759
|
+
session.directory,
|
|
5760
|
+
identity.kind,
|
|
5761
|
+
identity.key,
|
|
5762
|
+
identity.displayName,
|
|
5763
|
+
session.time_created,
|
|
5764
|
+
session.time_updated ?? null,
|
|
5765
|
+
activityTime,
|
|
5766
|
+
session.stats.message_count,
|
|
5767
|
+
session.stats.total_input_tokens,
|
|
5768
|
+
session.stats.total_output_tokens,
|
|
5769
|
+
session.stats.total_cache_read_tokens ?? null,
|
|
5770
|
+
session.stats.total_cache_create_tokens ?? null,
|
|
5771
|
+
session.stats.total_cost,
|
|
5772
|
+
session.stats.cost_source ?? null,
|
|
5773
|
+
session.stats.total_tokens ?? null,
|
|
5774
|
+
stringifyOptionalJson(session.model_usage),
|
|
5775
|
+
stringifyOptionalJson(session.smart_tags),
|
|
5776
|
+
session.smart_tags_source_updated_at ?? null,
|
|
5777
|
+
metaJson
|
|
5778
|
+
);
|
|
5779
|
+
}
|
|
5780
|
+
function prepareInsertFileActivity(db) {
|
|
5781
|
+
return db.prepare(`
|
|
5782
|
+
INSERT INTO session_file_activity(
|
|
5783
|
+
agent_name,
|
|
5784
|
+
session_id,
|
|
5785
|
+
project_identity_key,
|
|
5786
|
+
path,
|
|
5787
|
+
kind,
|
|
5788
|
+
count,
|
|
5789
|
+
latest_time
|
|
5790
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
5994
5791
|
`);
|
|
5995
5792
|
}
|
|
5996
|
-
function
|
|
5997
|
-
db.
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
5793
|
+
function prepareInsertMessageTool(db) {
|
|
5794
|
+
return db.prepare(`
|
|
5795
|
+
INSERT OR IGNORE INTO message_tools(
|
|
5796
|
+
agent_name,
|
|
5797
|
+
session_id,
|
|
5798
|
+
message_index,
|
|
5799
|
+
tool_name
|
|
5800
|
+
) VALUES (?, ?, ?, ?)
|
|
6001
5801
|
`);
|
|
6002
5802
|
}
|
|
6003
|
-
function
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
agent_name
|
|
6007
|
-
session_id
|
|
6008
|
-
project_identity_key
|
|
6009
|
-
path
|
|
6010
|
-
kind
|
|
6011
|
-
count
|
|
6012
|
-
latest_time
|
|
6013
|
-
PRIMARY KEY (agent_name, session_id, project_identity_key, path, kind),
|
|
6014
|
-
FOREIGN KEY (agent_name, session_id)
|
|
6015
|
-
REFERENCES sessions(agent_name, session_id)
|
|
6016
|
-
ON DELETE CASCADE
|
|
6017
|
-
);
|
|
6018
|
-
|
|
6019
|
-
CREATE INDEX IF NOT EXISTS idx_file_activity_project_latest
|
|
6020
|
-
ON session_file_activity(project_identity_key, latest_time);
|
|
6021
|
-
|
|
6022
|
-
CREATE INDEX IF NOT EXISTS idx_file_activity_latest
|
|
6023
|
-
ON session_file_activity(latest_time DESC, count DESC, path);
|
|
6024
|
-
|
|
6025
|
-
CREATE INDEX IF NOT EXISTS idx_file_activity_agent_latest
|
|
6026
|
-
ON session_file_activity(agent_name, latest_time DESC, count DESC, path);
|
|
6027
|
-
|
|
6028
|
-
CREATE INDEX IF NOT EXISTS idx_file_activity_project_latest_ordered
|
|
6029
|
-
ON session_file_activity(project_identity_key, latest_time DESC, count DESC, path);
|
|
6030
|
-
|
|
6031
|
-
CREATE INDEX IF NOT EXISTS idx_file_activity_path
|
|
6032
|
-
ON session_file_activity(path);
|
|
6033
|
-
|
|
6034
|
-
CREATE INDEX IF NOT EXISTS idx_file_activity_kind
|
|
6035
|
-
ON session_file_activity(kind);
|
|
6036
|
-
`);
|
|
6037
|
-
createFileActivityPathSearchTables(db);
|
|
6038
|
-
}
|
|
6039
|
-
function createFileActivityPathSearchTables(db) {
|
|
6040
|
-
db.exec(`
|
|
6041
|
-
CREATE VIRTUAL TABLE IF NOT EXISTS session_file_activity_path_fts USING fts5(
|
|
6042
|
-
path,
|
|
6043
|
-
content='session_file_activity',
|
|
6044
|
-
content_rowid='rowid',
|
|
6045
|
-
tokenize='trigram'
|
|
5803
|
+
function writeFileActivityRows(statement, activities) {
|
|
5804
|
+
for (const activity of activities) {
|
|
5805
|
+
statement.run(
|
|
5806
|
+
activity.agent_name,
|
|
5807
|
+
activity.session_id,
|
|
5808
|
+
activity.project_identity_key,
|
|
5809
|
+
activity.path,
|
|
5810
|
+
activity.kind,
|
|
5811
|
+
activity.count,
|
|
5812
|
+
activity.latest_time
|
|
6046
5813
|
);
|
|
6047
|
-
`);
|
|
6048
|
-
createFileActivityPathSearchTriggers(db);
|
|
6049
|
-
}
|
|
6050
|
-
function createFileActivityPathSearchTriggers(db) {
|
|
6051
|
-
db.exec(`
|
|
6052
|
-
CREATE TRIGGER IF NOT EXISTS session_file_activity_path_ai
|
|
6053
|
-
AFTER INSERT ON session_file_activity BEGIN
|
|
6054
|
-
INSERT INTO session_file_activity_path_fts(rowid, path)
|
|
6055
|
-
VALUES (new.rowid, new.path);
|
|
6056
|
-
END;
|
|
6057
|
-
|
|
6058
|
-
CREATE TRIGGER IF NOT EXISTS session_file_activity_path_ad
|
|
6059
|
-
AFTER DELETE ON session_file_activity BEGIN
|
|
6060
|
-
INSERT INTO session_file_activity_path_fts(session_file_activity_path_fts, rowid, path)
|
|
6061
|
-
VALUES ('delete', old.rowid, old.path);
|
|
6062
|
-
END;
|
|
6063
|
-
|
|
6064
|
-
CREATE TRIGGER IF NOT EXISTS session_file_activity_path_au
|
|
6065
|
-
AFTER UPDATE ON session_file_activity BEGIN
|
|
6066
|
-
INSERT INTO session_file_activity_path_fts(session_file_activity_path_fts, rowid, path)
|
|
6067
|
-
VALUES ('delete', old.rowid, old.path);
|
|
6068
|
-
INSERT INTO session_file_activity_path_fts(rowid, path)
|
|
6069
|
-
VALUES (new.rowid, new.path);
|
|
6070
|
-
END;
|
|
6071
|
-
`);
|
|
6072
|
-
}
|
|
6073
|
-
function rebuildFileActivityPathIndex(db) {
|
|
6074
|
-
if (!tableExists(db, "session_file_activity_path_fts")) {
|
|
6075
|
-
return;
|
|
6076
5814
|
}
|
|
6077
|
-
|
|
6078
|
-
|
|
5815
|
+
}
|
|
5816
|
+
function writeProjectSessionRow(statement, agentName, session, identity) {
|
|
5817
|
+
statement.run(
|
|
5818
|
+
agentName,
|
|
5819
|
+
session.id,
|
|
5820
|
+
identity.kind,
|
|
5821
|
+
identity.key,
|
|
5822
|
+
identity.displayName,
|
|
5823
|
+
session.directory,
|
|
5824
|
+
session.time_updated ?? session.time_created
|
|
6079
5825
|
);
|
|
6080
5826
|
}
|
|
6081
|
-
function
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
5827
|
+
function sessionFromRow(row) {
|
|
5828
|
+
const session = {
|
|
5829
|
+
id: String(row.session_id),
|
|
5830
|
+
slug: String(row.slug),
|
|
5831
|
+
title: String(row.title),
|
|
5832
|
+
directory: String(row.directory),
|
|
5833
|
+
time_created: Number(row.time_created),
|
|
5834
|
+
stats: {
|
|
5835
|
+
message_count: Number(row.message_count ?? 0),
|
|
5836
|
+
total_input_tokens: Number(row.total_input_tokens ?? 0),
|
|
5837
|
+
total_output_tokens: Number(row.total_output_tokens ?? 0),
|
|
5838
|
+
total_cost: Number(row.total_cost ?? 0)
|
|
5839
|
+
}
|
|
5840
|
+
};
|
|
5841
|
+
if (row.project_identity_key) {
|
|
5842
|
+
session.project_identity = {
|
|
5843
|
+
kind: row.project_identity_kind ?? "path",
|
|
5844
|
+
key: String(row.project_identity_key),
|
|
5845
|
+
displayName: String(row.project_display_name ?? "")
|
|
5846
|
+
};
|
|
5847
|
+
}
|
|
5848
|
+
if (row.time_updated != null) {
|
|
5849
|
+
session.time_updated = Number(row.time_updated);
|
|
5850
|
+
}
|
|
5851
|
+
if (row.total_cache_read_tokens != null) {
|
|
5852
|
+
session.stats.total_cache_read_tokens = Number(row.total_cache_read_tokens);
|
|
5853
|
+
}
|
|
5854
|
+
if (row.total_cache_create_tokens != null) {
|
|
5855
|
+
session.stats.total_cache_create_tokens = Number(row.total_cache_create_tokens);
|
|
5856
|
+
}
|
|
5857
|
+
if (row.cost_source) {
|
|
5858
|
+
session.stats.cost_source = row.cost_source;
|
|
5859
|
+
}
|
|
5860
|
+
if (row.total_tokens != null) {
|
|
5861
|
+
session.stats.total_tokens = Number(row.total_tokens);
|
|
5862
|
+
}
|
|
5863
|
+
const modelUsage = parseOptionalJson(row.model_usage_json);
|
|
5864
|
+
if (modelUsage) {
|
|
5865
|
+
session.model_usage = modelUsage;
|
|
5866
|
+
}
|
|
5867
|
+
const smartTags = parseOptionalJson(row.smart_tags_json);
|
|
5868
|
+
if (smartTags) {
|
|
5869
|
+
session.smart_tags = smartTags;
|
|
5870
|
+
}
|
|
5871
|
+
if (row.smart_tags_source_updated_at != null) {
|
|
5872
|
+
session.smart_tags_source_updated_at = Number(row.smart_tags_source_updated_at);
|
|
5873
|
+
}
|
|
5874
|
+
return session;
|
|
6107
5875
|
}
|
|
6108
|
-
function
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
INSERT INTO session_documents_fts(rowid, title, content_text)
|
|
6124
|
-
VALUES (new.id, new.title, new.content_text);
|
|
6125
|
-
END;
|
|
6126
|
-
`);
|
|
5876
|
+
function messageFromBackfillRow(row) {
|
|
5877
|
+
const role = row.role === "assistant" || row.role === "tool" ? row.role : "user";
|
|
5878
|
+
return {
|
|
5879
|
+
id: String(row.message_id ?? ""),
|
|
5880
|
+
role,
|
|
5881
|
+
agent: row.agent ?? null,
|
|
5882
|
+
time_created: Number(row.time_created ?? 0),
|
|
5883
|
+
time_completed: row.time_completed == null ? null : Number(row.time_completed),
|
|
5884
|
+
mode: row.mode ?? null,
|
|
5885
|
+
model: row.model ?? null,
|
|
5886
|
+
provider: row.provider ?? null,
|
|
5887
|
+
parts: JSON.parse(String(row.parts_json ?? "[]")),
|
|
5888
|
+
subagent_id: row.subagent_id ?? void 0,
|
|
5889
|
+
nickname: row.nickname ?? void 0
|
|
5890
|
+
};
|
|
6127
5891
|
}
|
|
6128
|
-
function
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
5892
|
+
function messageFromCachedRow(row) {
|
|
5893
|
+
const message = messageFromBackfillRow(row);
|
|
5894
|
+
const tokens = parseOptionalJson(row.tokens_json);
|
|
5895
|
+
if (tokens) {
|
|
5896
|
+
message.tokens = tokens;
|
|
5897
|
+
}
|
|
5898
|
+
if (row.cost != null) {
|
|
5899
|
+
message.cost = Number(row.cost);
|
|
5900
|
+
}
|
|
5901
|
+
if (row.cost_source) {
|
|
5902
|
+
message.cost_source = row.cost_source;
|
|
5903
|
+
}
|
|
5904
|
+
return message;
|
|
6134
5905
|
}
|
|
6135
|
-
function
|
|
6136
|
-
if (
|
|
5906
|
+
function appendPlainText(value, chunks) {
|
|
5907
|
+
if (value == null) return;
|
|
5908
|
+
if (typeof value === "string") {
|
|
5909
|
+
const normalized = value.trim();
|
|
5910
|
+
if (normalized) {
|
|
5911
|
+
chunks.push(normalized);
|
|
5912
|
+
}
|
|
6137
5913
|
return;
|
|
6138
5914
|
}
|
|
6139
|
-
if (
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
);
|
|
5915
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
5916
|
+
chunks.push(String(value));
|
|
5917
|
+
return;
|
|
6143
5918
|
}
|
|
6144
|
-
if (
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
5919
|
+
if (Array.isArray(value)) {
|
|
5920
|
+
for (const item of value) {
|
|
5921
|
+
appendPlainText(item, chunks);
|
|
5922
|
+
}
|
|
5923
|
+
return;
|
|
6148
5924
|
}
|
|
6149
|
-
if (
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
5925
|
+
if (typeof value === "object") {
|
|
5926
|
+
for (const nested of Object.values(value)) {
|
|
5927
|
+
appendPlainText(nested, chunks);
|
|
5928
|
+
}
|
|
6153
5929
|
}
|
|
6154
5930
|
}
|
|
6155
|
-
function
|
|
6156
|
-
|
|
6157
|
-
db.exec(`
|
|
6158
|
-
CREATE TABLE IF NOT EXISTS project_sessions (
|
|
6159
|
-
agent_name TEXT NOT NULL,
|
|
6160
|
-
session_id TEXT NOT NULL,
|
|
6161
|
-
identity_kind TEXT NOT NULL,
|
|
6162
|
-
identity_key TEXT NOT NULL,
|
|
6163
|
-
display_name TEXT NOT NULL,
|
|
6164
|
-
directory TEXT NOT NULL,
|
|
6165
|
-
activity_time INTEGER NOT NULL,
|
|
6166
|
-
PRIMARY KEY (agent_name, session_id)
|
|
6167
|
-
);
|
|
6168
|
-
|
|
6169
|
-
CREATE INDEX IF NOT EXISTS idx_project_sessions_identity
|
|
6170
|
-
ON project_sessions(identity_kind, identity_key);
|
|
6171
|
-
`);
|
|
6172
|
-
createProjectGroupsView(db);
|
|
5931
|
+
function compactRecord(record) {
|
|
5932
|
+
return Object.fromEntries(Object.entries(record).filter(([, value]) => value != null));
|
|
6173
5933
|
}
|
|
6174
|
-
function
|
|
6175
|
-
if (
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
5934
|
+
function normalizeToolName2(value) {
|
|
5935
|
+
if (typeof value !== "string") return null;
|
|
5936
|
+
const name = value.trim().toLowerCase();
|
|
5937
|
+
return name || null;
|
|
5938
|
+
}
|
|
5939
|
+
function toolNamesFromMetadataJson(value) {
|
|
5940
|
+
if (!value) return [];
|
|
5941
|
+
try {
|
|
5942
|
+
const metadata = JSON.parse(String(value));
|
|
5943
|
+
if (!Array.isArray(metadata)) return [];
|
|
5944
|
+
const tools = /* @__PURE__ */ new Set();
|
|
5945
|
+
for (const item of metadata) {
|
|
5946
|
+
if (item == null || typeof item !== "object") continue;
|
|
5947
|
+
const toolName = normalizeToolName2(item.tool);
|
|
5948
|
+
if (toolName) tools.add(toolName);
|
|
5949
|
+
}
|
|
5950
|
+
return [...tools];
|
|
5951
|
+
} catch {
|
|
5952
|
+
return [];
|
|
6189
5953
|
}
|
|
6190
|
-
db.exec(`
|
|
6191
|
-
CREATE VIEW IF NOT EXISTS project_groups_v AS
|
|
6192
|
-
SELECT
|
|
6193
|
-
project_identity_kind AS identity_kind,
|
|
6194
|
-
project_identity_key AS identity_key,
|
|
6195
|
-
MIN(project_display_name) AS display_name,
|
|
6196
|
-
GROUP_CONCAT(DISTINCT agent_name) AS sources_csv,
|
|
6197
|
-
COUNT(*) AS session_count,
|
|
6198
|
-
MAX(activity_time) AS last_activity
|
|
6199
|
-
FROM sessions
|
|
6200
|
-
GROUP BY project_identity_kind, project_identity_key;
|
|
6201
|
-
`);
|
|
6202
5954
|
}
|
|
6203
|
-
function
|
|
6204
|
-
|
|
6205
|
-
|
|
5955
|
+
function toolNamesFromMessage(message) {
|
|
5956
|
+
const tools = /* @__PURE__ */ new Set();
|
|
5957
|
+
for (const part of message.parts) {
|
|
5958
|
+
if (part.type !== "tool") continue;
|
|
5959
|
+
const toolName = normalizeToolName2(part.tool);
|
|
5960
|
+
if (toolName) tools.add(toolName);
|
|
5961
|
+
}
|
|
5962
|
+
return [...tools];
|
|
6206
5963
|
}
|
|
6207
|
-
function
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
5964
|
+
function summarizeToolPart(part) {
|
|
5965
|
+
const state = part.state == null ? void 0 : compactRecord({
|
|
5966
|
+
status: part.state.status,
|
|
5967
|
+
error: part.state.error,
|
|
5968
|
+
metadata: part.state.metadata
|
|
5969
|
+
});
|
|
5970
|
+
return compactRecord({
|
|
5971
|
+
type: part.type,
|
|
5972
|
+
tool: part.tool,
|
|
5973
|
+
title: part.title,
|
|
5974
|
+
nickname: part.nickname,
|
|
5975
|
+
callID: part.callID,
|
|
5976
|
+
approval_status: part.approval_status,
|
|
5977
|
+
state
|
|
5978
|
+
});
|
|
6214
5979
|
}
|
|
6215
|
-
function
|
|
6216
|
-
|
|
5980
|
+
function buildMessageText(message) {
|
|
5981
|
+
const chunks = [];
|
|
5982
|
+
chunks.push(message.role);
|
|
5983
|
+
appendPlainText(message.agent, chunks);
|
|
5984
|
+
appendPlainText(message.model, chunks);
|
|
5985
|
+
for (const part of message.parts) {
|
|
5986
|
+
appendPlainText(part.type, chunks);
|
|
5987
|
+
appendPlainText(part.title, chunks);
|
|
5988
|
+
appendPlainText(part.nickname, chunks);
|
|
5989
|
+
appendPlainText(part.tool, chunks);
|
|
5990
|
+
appendPlainText(part.text, chunks);
|
|
5991
|
+
appendPlainText(part.input, chunks);
|
|
5992
|
+
appendPlainText(part.output, chunks);
|
|
5993
|
+
appendPlainText(part.state, chunks);
|
|
5994
|
+
}
|
|
5995
|
+
return chunks.join("\n");
|
|
6217
5996
|
}
|
|
6218
|
-
function
|
|
6219
|
-
return
|
|
5997
|
+
function normalizeMessages(session) {
|
|
5998
|
+
return session.messages.map((message, index) => {
|
|
5999
|
+
const toolMetadata = message.parts.filter((part) => part.type === "tool").map((part) => summarizeToolPart(part));
|
|
6000
|
+
return {
|
|
6001
|
+
index,
|
|
6002
|
+
id: message.id || `${session.id}:${index}`,
|
|
6003
|
+
role: message.role,
|
|
6004
|
+
timeCreated: message.time_created,
|
|
6005
|
+
timeCompleted: message.time_completed ?? null,
|
|
6006
|
+
agent: message.agent ?? null,
|
|
6007
|
+
mode: message.mode ?? null,
|
|
6008
|
+
model: message.model ?? null,
|
|
6009
|
+
provider: message.provider ?? null,
|
|
6010
|
+
tokensJson: stringifyOptionalJson(message.tokens),
|
|
6011
|
+
cost: message.cost ?? null,
|
|
6012
|
+
costSource: message.cost_source ?? null,
|
|
6013
|
+
partsJson: JSON.stringify(message.parts),
|
|
6014
|
+
subagentId: message.subagent_id ?? null,
|
|
6015
|
+
nickname: message.nickname ?? null,
|
|
6016
|
+
contentText: buildMessageText(message),
|
|
6017
|
+
toolMetadataJson: toolMetadata.length > 0 ? JSON.stringify(toolMetadata) : null,
|
|
6018
|
+
toolNames: toolNamesFromMessage(message)
|
|
6019
|
+
};
|
|
6020
|
+
});
|
|
6220
6021
|
}
|
|
6221
|
-
function
|
|
6222
|
-
|
|
6022
|
+
function buildSessionContentFromMessages(title, messages) {
|
|
6023
|
+
const chunks = [];
|
|
6024
|
+
appendPlainText(title, chunks);
|
|
6025
|
+
for (const message of messages) {
|
|
6026
|
+
appendPlainText(message.contentText, chunks);
|
|
6027
|
+
}
|
|
6028
|
+
return chunks.join("\n");
|
|
6223
6029
|
}
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
const
|
|
6227
|
-
|
|
6030
|
+
var CACHE_SCHEMA_VERSION = 13;
|
|
6031
|
+
function withCacheDb(fn) {
|
|
6032
|
+
const cachePath = getCachePath2();
|
|
6033
|
+
const db = openDb(cachePath);
|
|
6034
|
+
if (!db) return null;
|
|
6035
|
+
try {
|
|
6036
|
+
ensureSchema(db, cachePath);
|
|
6037
|
+
return fn(db);
|
|
6038
|
+
} catch {
|
|
6039
|
+
return null;
|
|
6040
|
+
} finally {
|
|
6041
|
+
db.close();
|
|
6042
|
+
}
|
|
6228
6043
|
}
|
|
6229
|
-
function
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6044
|
+
function withCacheDbReadOnly(fn) {
|
|
6045
|
+
const db = openDbReadOnly(getCachePath2());
|
|
6046
|
+
if (!db) return null;
|
|
6047
|
+
try {
|
|
6048
|
+
return fn(db);
|
|
6049
|
+
} catch {
|
|
6050
|
+
return null;
|
|
6051
|
+
} finally {
|
|
6052
|
+
db.close();
|
|
6053
|
+
}
|
|
6237
6054
|
}
|
|
6238
|
-
function
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
)
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6055
|
+
function createCacheTables(db) {
|
|
6056
|
+
db.exec(`
|
|
6057
|
+
CREATE TABLE IF NOT EXISTS cache_meta (
|
|
6058
|
+
key TEXT PRIMARY KEY,
|
|
6059
|
+
value TEXT NOT NULL
|
|
6060
|
+
);
|
|
6061
|
+
|
|
6062
|
+
CREATE TABLE IF NOT EXISTS agent_cache (
|
|
6063
|
+
agent_name TEXT PRIMARY KEY,
|
|
6064
|
+
timestamp INTEGER NOT NULL
|
|
6065
|
+
);
|
|
6066
|
+
|
|
6067
|
+
CREATE TABLE IF NOT EXISTS cached_sessions (
|
|
6068
|
+
agent_name TEXT NOT NULL,
|
|
6069
|
+
session_id TEXT NOT NULL,
|
|
6070
|
+
session_json TEXT NOT NULL,
|
|
6071
|
+
meta_json TEXT,
|
|
6072
|
+
PRIMARY KEY (agent_name, session_id)
|
|
6073
|
+
);
|
|
6074
|
+
|
|
6075
|
+
CREATE TABLE IF NOT EXISTS cache_initialization (
|
|
6076
|
+
agent_name TEXT PRIMARY KEY,
|
|
6077
|
+
initialized_at INTEGER NOT NULL,
|
|
6078
|
+
index_version TEXT NOT NULL,
|
|
6079
|
+
last_sync_at INTEGER NOT NULL
|
|
6080
|
+
);
|
|
6255
6081
|
`);
|
|
6256
6082
|
}
|
|
6257
|
-
function
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
agent_name,
|
|
6261
|
-
session_id,
|
|
6262
|
-
sort_index,
|
|
6263
|
-
slug,
|
|
6264
|
-
title,
|
|
6265
|
-
source_path,
|
|
6266
|
-
directory,
|
|
6267
|
-
project_identity_kind,
|
|
6268
|
-
project_identity_key,
|
|
6269
|
-
project_display_name,
|
|
6270
|
-
time_created,
|
|
6271
|
-
time_updated,
|
|
6272
|
-
activity_time,
|
|
6273
|
-
message_count,
|
|
6274
|
-
total_input_tokens,
|
|
6275
|
-
total_output_tokens,
|
|
6276
|
-
total_cache_read_tokens,
|
|
6277
|
-
total_cache_create_tokens,
|
|
6278
|
-
total_cost,
|
|
6279
|
-
cost_source,
|
|
6280
|
-
total_tokens,
|
|
6281
|
-
model_usage_json,
|
|
6282
|
-
smart_tags_json,
|
|
6283
|
-
smart_tags_source_updated_at,
|
|
6284
|
-
meta_json
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
project_identity_kind
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6083
|
+
function createSessionTables(db) {
|
|
6084
|
+
db.exec(`
|
|
6085
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
6086
|
+
agent_name TEXT NOT NULL,
|
|
6087
|
+
session_id TEXT NOT NULL,
|
|
6088
|
+
sort_index INTEGER NOT NULL DEFAULT 0,
|
|
6089
|
+
slug TEXT NOT NULL,
|
|
6090
|
+
title TEXT NOT NULL,
|
|
6091
|
+
source_path TEXT,
|
|
6092
|
+
directory TEXT NOT NULL,
|
|
6093
|
+
project_identity_kind TEXT NOT NULL,
|
|
6094
|
+
project_identity_key TEXT NOT NULL,
|
|
6095
|
+
project_display_name TEXT NOT NULL,
|
|
6096
|
+
time_created INTEGER NOT NULL,
|
|
6097
|
+
time_updated INTEGER,
|
|
6098
|
+
activity_time INTEGER NOT NULL,
|
|
6099
|
+
message_count INTEGER NOT NULL,
|
|
6100
|
+
total_input_tokens INTEGER NOT NULL,
|
|
6101
|
+
total_output_tokens INTEGER NOT NULL,
|
|
6102
|
+
total_cache_read_tokens INTEGER,
|
|
6103
|
+
total_cache_create_tokens INTEGER,
|
|
6104
|
+
total_cost REAL NOT NULL,
|
|
6105
|
+
cost_source TEXT,
|
|
6106
|
+
total_tokens INTEGER,
|
|
6107
|
+
model_usage_json TEXT,
|
|
6108
|
+
smart_tags_json TEXT,
|
|
6109
|
+
smart_tags_source_updated_at INTEGER,
|
|
6110
|
+
meta_json TEXT,
|
|
6111
|
+
PRIMARY KEY (agent_name, session_id)
|
|
6112
|
+
);
|
|
6113
|
+
|
|
6114
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_agent_activity
|
|
6115
|
+
ON sessions(agent_name, activity_time);
|
|
6116
|
+
|
|
6117
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_project
|
|
6118
|
+
ON sessions(project_identity_kind, project_identity_key, activity_time);
|
|
6119
|
+
|
|
6120
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
6121
|
+
agent_name TEXT NOT NULL,
|
|
6122
|
+
session_id TEXT NOT NULL,
|
|
6123
|
+
message_index INTEGER NOT NULL,
|
|
6124
|
+
message_id TEXT NOT NULL,
|
|
6125
|
+
role TEXT NOT NULL,
|
|
6126
|
+
time_created INTEGER NOT NULL,
|
|
6127
|
+
time_completed INTEGER,
|
|
6128
|
+
agent TEXT,
|
|
6129
|
+
mode TEXT,
|
|
6130
|
+
model TEXT,
|
|
6131
|
+
provider TEXT,
|
|
6132
|
+
tokens_json TEXT,
|
|
6133
|
+
cost REAL,
|
|
6134
|
+
cost_source TEXT,
|
|
6135
|
+
parts_json TEXT NOT NULL,
|
|
6136
|
+
subagent_id TEXT,
|
|
6137
|
+
nickname TEXT,
|
|
6138
|
+
content_text TEXT NOT NULL,
|
|
6139
|
+
tool_metadata_json TEXT,
|
|
6140
|
+
PRIMARY KEY (agent_name, session_id, message_index),
|
|
6141
|
+
FOREIGN KEY (agent_name, session_id)
|
|
6142
|
+
REFERENCES sessions(agent_name, session_id)
|
|
6143
|
+
ON DELETE CASCADE
|
|
6144
|
+
);
|
|
6145
|
+
|
|
6146
|
+
CREATE INDEX IF NOT EXISTS idx_messages_session
|
|
6147
|
+
ON messages(agent_name, session_id, message_index);
|
|
6310
6148
|
`);
|
|
6149
|
+
createMessageToolTables(db);
|
|
6311
6150
|
}
|
|
6312
|
-
function
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
agent_name,
|
|
6316
|
-
session_id,
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
activity_time,
|
|
6328
|
-
message_count,
|
|
6329
|
-
total_input_tokens,
|
|
6330
|
-
total_output_tokens,
|
|
6331
|
-
total_cache_read_tokens,
|
|
6332
|
-
total_cache_create_tokens,
|
|
6333
|
-
total_cost,
|
|
6334
|
-
cost_source,
|
|
6335
|
-
total_tokens,
|
|
6336
|
-
model_usage_json,
|
|
6337
|
-
smart_tags_json,
|
|
6338
|
-
smart_tags_source_updated_at,
|
|
6339
|
-
meta_json
|
|
6340
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
6341
|
-
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
6342
|
-
slug = excluded.slug,
|
|
6343
|
-
title = excluded.title,
|
|
6344
|
-
directory = excluded.directory,
|
|
6345
|
-
project_identity_kind = excluded.project_identity_kind,
|
|
6346
|
-
project_identity_key = excluded.project_identity_key,
|
|
6347
|
-
project_display_name = excluded.project_display_name,
|
|
6348
|
-
time_created = excluded.time_created,
|
|
6349
|
-
time_updated = excluded.time_updated,
|
|
6350
|
-
activity_time = excluded.activity_time,
|
|
6351
|
-
message_count = excluded.message_count,
|
|
6352
|
-
total_input_tokens = excluded.total_input_tokens,
|
|
6353
|
-
total_output_tokens = excluded.total_output_tokens,
|
|
6354
|
-
total_cache_read_tokens = excluded.total_cache_read_tokens,
|
|
6355
|
-
total_cache_create_tokens = excluded.total_cache_create_tokens,
|
|
6356
|
-
total_cost = excluded.total_cost,
|
|
6357
|
-
cost_source = excluded.cost_source,
|
|
6358
|
-
total_tokens = excluded.total_tokens,
|
|
6359
|
-
model_usage_json = excluded.model_usage_json,
|
|
6360
|
-
smart_tags_json = excluded.smart_tags_json,
|
|
6361
|
-
smart_tags_source_updated_at = excluded.smart_tags_source_updated_at
|
|
6151
|
+
function createMessageToolTables(db) {
|
|
6152
|
+
db.exec(`
|
|
6153
|
+
CREATE TABLE IF NOT EXISTS message_tools (
|
|
6154
|
+
agent_name TEXT NOT NULL,
|
|
6155
|
+
session_id TEXT NOT NULL,
|
|
6156
|
+
message_index INTEGER NOT NULL,
|
|
6157
|
+
tool_name TEXT NOT NULL,
|
|
6158
|
+
PRIMARY KEY (agent_name, session_id, message_index, tool_name),
|
|
6159
|
+
FOREIGN KEY (agent_name, session_id, message_index)
|
|
6160
|
+
REFERENCES messages(agent_name, session_id, message_index)
|
|
6161
|
+
ON DELETE CASCADE
|
|
6162
|
+
);
|
|
6163
|
+
|
|
6164
|
+
CREATE INDEX IF NOT EXISTS idx_message_tools_filter
|
|
6165
|
+
ON message_tools(tool_name, agent_name, session_id);
|
|
6362
6166
|
`);
|
|
6363
6167
|
}
|
|
6364
|
-
function
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
identity.key,
|
|
6377
|
-
identity.displayName,
|
|
6378
|
-
session.time_created,
|
|
6379
|
-
session.time_updated ?? null,
|
|
6380
|
-
activityTime,
|
|
6381
|
-
session.stats.message_count,
|
|
6382
|
-
session.stats.total_input_tokens,
|
|
6383
|
-
session.stats.total_output_tokens,
|
|
6384
|
-
session.stats.total_cache_read_tokens ?? null,
|
|
6385
|
-
session.stats.total_cache_create_tokens ?? null,
|
|
6386
|
-
session.stats.total_cost,
|
|
6387
|
-
session.stats.cost_source ?? null,
|
|
6388
|
-
session.stats.total_tokens ?? null,
|
|
6389
|
-
stringifyOptionalJson(session.model_usage),
|
|
6390
|
-
stringifyOptionalJson(session.smart_tags),
|
|
6391
|
-
session.smart_tags_source_updated_at ?? null,
|
|
6392
|
-
metaJson
|
|
6393
|
-
);
|
|
6168
|
+
function createMessageSearchTables(db) {
|
|
6169
|
+
if (!tableExists(db, "messages")) {
|
|
6170
|
+
createSessionTables(db);
|
|
6171
|
+
}
|
|
6172
|
+
db.exec(`
|
|
6173
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
|
|
6174
|
+
content_text,
|
|
6175
|
+
content='messages',
|
|
6176
|
+
content_rowid='rowid'
|
|
6177
|
+
);
|
|
6178
|
+
`);
|
|
6179
|
+
createMessageSearchTriggers(db);
|
|
6394
6180
|
}
|
|
6395
|
-
function
|
|
6396
|
-
|
|
6397
|
-
INSERT
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6181
|
+
function createMessageSearchTriggers(db) {
|
|
6182
|
+
db.exec(`
|
|
6183
|
+
CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN
|
|
6184
|
+
INSERT INTO messages_fts(rowid, content_text)
|
|
6185
|
+
VALUES (new.rowid, new.content_text);
|
|
6186
|
+
END;
|
|
6187
|
+
|
|
6188
|
+
CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN
|
|
6189
|
+
INSERT INTO messages_fts(messages_fts, rowid, content_text)
|
|
6190
|
+
VALUES ('delete', old.rowid, old.content_text);
|
|
6191
|
+
END;
|
|
6192
|
+
|
|
6193
|
+
CREATE TRIGGER IF NOT EXISTS messages_au AFTER UPDATE ON messages BEGIN
|
|
6194
|
+
INSERT INTO messages_fts(messages_fts, rowid, content_text)
|
|
6195
|
+
VALUES ('delete', old.rowid, old.content_text);
|
|
6196
|
+
INSERT INTO messages_fts(rowid, content_text)
|
|
6197
|
+
VALUES (new.rowid, new.content_text);
|
|
6198
|
+
END;
|
|
6406
6199
|
`);
|
|
6407
6200
|
}
|
|
6408
|
-
function
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
message_index,
|
|
6414
|
-
tool_name
|
|
6415
|
-
) VALUES (?, ?, ?, ?)
|
|
6201
|
+
function dropMessageSearchTriggers(db) {
|
|
6202
|
+
db.exec(`
|
|
6203
|
+
DROP TRIGGER IF EXISTS messages_ai;
|
|
6204
|
+
DROP TRIGGER IF EXISTS messages_ad;
|
|
6205
|
+
DROP TRIGGER IF EXISTS messages_au;
|
|
6416
6206
|
`);
|
|
6417
6207
|
}
|
|
6418
|
-
function
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6208
|
+
function createFileActivityTables(db) {
|
|
6209
|
+
db.exec(`
|
|
6210
|
+
CREATE TABLE IF NOT EXISTS session_file_activity (
|
|
6211
|
+
agent_name TEXT NOT NULL,
|
|
6212
|
+
session_id TEXT NOT NULL,
|
|
6213
|
+
project_identity_key TEXT NOT NULL,
|
|
6214
|
+
path TEXT NOT NULL,
|
|
6215
|
+
kind TEXT NOT NULL,
|
|
6216
|
+
count INTEGER NOT NULL,
|
|
6217
|
+
latest_time INTEGER NOT NULL,
|
|
6218
|
+
PRIMARY KEY (agent_name, session_id, project_identity_key, path, kind),
|
|
6219
|
+
FOREIGN KEY (agent_name, session_id)
|
|
6220
|
+
REFERENCES sessions(agent_name, session_id)
|
|
6221
|
+
ON DELETE CASCADE
|
|
6428
6222
|
);
|
|
6429
|
-
|
|
6223
|
+
|
|
6224
|
+
CREATE INDEX IF NOT EXISTS idx_file_activity_project_latest
|
|
6225
|
+
ON session_file_activity(project_identity_key, latest_time);
|
|
6226
|
+
|
|
6227
|
+
CREATE INDEX IF NOT EXISTS idx_file_activity_latest
|
|
6228
|
+
ON session_file_activity(latest_time DESC, count DESC, path);
|
|
6229
|
+
|
|
6230
|
+
CREATE INDEX IF NOT EXISTS idx_file_activity_agent_latest
|
|
6231
|
+
ON session_file_activity(agent_name, latest_time DESC, count DESC, path);
|
|
6232
|
+
|
|
6233
|
+
CREATE INDEX IF NOT EXISTS idx_file_activity_project_latest_ordered
|
|
6234
|
+
ON session_file_activity(project_identity_key, latest_time DESC, count DESC, path);
|
|
6235
|
+
|
|
6236
|
+
CREATE INDEX IF NOT EXISTS idx_file_activity_path
|
|
6237
|
+
ON session_file_activity(path);
|
|
6238
|
+
|
|
6239
|
+
CREATE INDEX IF NOT EXISTS idx_file_activity_kind
|
|
6240
|
+
ON session_file_activity(kind);
|
|
6241
|
+
`);
|
|
6242
|
+
createFileActivityPathSearchTables(db);
|
|
6430
6243
|
}
|
|
6431
|
-
function
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
);
|
|
6244
|
+
function createFileActivityPathSearchTables(db) {
|
|
6245
|
+
db.exec(`
|
|
6246
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_file_activity_path_fts USING fts5(
|
|
6247
|
+
path,
|
|
6248
|
+
content='session_file_activity',
|
|
6249
|
+
content_rowid='rowid',
|
|
6250
|
+
tokenize='trigram'
|
|
6251
|
+
);
|
|
6252
|
+
`);
|
|
6253
|
+
createFileActivityPathSearchTriggers(db);
|
|
6441
6254
|
}
|
|
6442
|
-
function
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
if (
|
|
6467
|
-
|
|
6468
|
-
}
|
|
6469
|
-
if (row.total_cache_create_tokens != null) {
|
|
6470
|
-
session.stats.total_cache_create_tokens = Number(row.total_cache_create_tokens);
|
|
6255
|
+
function createFileActivityPathSearchTriggers(db) {
|
|
6256
|
+
db.exec(`
|
|
6257
|
+
CREATE TRIGGER IF NOT EXISTS session_file_activity_path_ai
|
|
6258
|
+
AFTER INSERT ON session_file_activity BEGIN
|
|
6259
|
+
INSERT INTO session_file_activity_path_fts(rowid, path)
|
|
6260
|
+
VALUES (new.rowid, new.path);
|
|
6261
|
+
END;
|
|
6262
|
+
|
|
6263
|
+
CREATE TRIGGER IF NOT EXISTS session_file_activity_path_ad
|
|
6264
|
+
AFTER DELETE ON session_file_activity BEGIN
|
|
6265
|
+
INSERT INTO session_file_activity_path_fts(session_file_activity_path_fts, rowid, path)
|
|
6266
|
+
VALUES ('delete', old.rowid, old.path);
|
|
6267
|
+
END;
|
|
6268
|
+
|
|
6269
|
+
CREATE TRIGGER IF NOT EXISTS session_file_activity_path_au
|
|
6270
|
+
AFTER UPDATE ON session_file_activity BEGIN
|
|
6271
|
+
INSERT INTO session_file_activity_path_fts(session_file_activity_path_fts, rowid, path)
|
|
6272
|
+
VALUES ('delete', old.rowid, old.path);
|
|
6273
|
+
INSERT INTO session_file_activity_path_fts(rowid, path)
|
|
6274
|
+
VALUES (new.rowid, new.path);
|
|
6275
|
+
END;
|
|
6276
|
+
`);
|
|
6277
|
+
}
|
|
6278
|
+
function rebuildFileActivityPathIndex(db) {
|
|
6279
|
+
if (!tableExists(db, "session_file_activity_path_fts")) {
|
|
6280
|
+
return;
|
|
6471
6281
|
}
|
|
6472
|
-
|
|
6473
|
-
|
|
6282
|
+
db.exec(
|
|
6283
|
+
"INSERT INTO session_file_activity_path_fts(session_file_activity_path_fts) VALUES ('rebuild')"
|
|
6284
|
+
);
|
|
6285
|
+
}
|
|
6286
|
+
function createSearchTables(db) {
|
|
6287
|
+
db.exec(`
|
|
6288
|
+
CREATE TABLE IF NOT EXISTS session_documents (
|
|
6289
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6290
|
+
agent_name TEXT NOT NULL,
|
|
6291
|
+
session_id TEXT NOT NULL,
|
|
6292
|
+
slug TEXT NOT NULL,
|
|
6293
|
+
title TEXT NOT NULL,
|
|
6294
|
+
directory TEXT NOT NULL,
|
|
6295
|
+
time_created INTEGER NOT NULL,
|
|
6296
|
+
time_updated INTEGER,
|
|
6297
|
+
activity_time INTEGER NOT NULL,
|
|
6298
|
+
content_text TEXT NOT NULL,
|
|
6299
|
+
content_hash TEXT NOT NULL,
|
|
6300
|
+
indexed_at INTEGER NOT NULL,
|
|
6301
|
+
UNIQUE(agent_name, session_id)
|
|
6302
|
+
);
|
|
6303
|
+
|
|
6304
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS session_documents_fts USING fts5(
|
|
6305
|
+
title,
|
|
6306
|
+
content_text,
|
|
6307
|
+
content='session_documents',
|
|
6308
|
+
content_rowid='id'
|
|
6309
|
+
);
|
|
6310
|
+
`);
|
|
6311
|
+
createSearchTriggers(db);
|
|
6312
|
+
}
|
|
6313
|
+
function createSearchTriggers(db) {
|
|
6314
|
+
db.exec(`
|
|
6315
|
+
CREATE TRIGGER IF NOT EXISTS session_documents_ai AFTER INSERT ON session_documents BEGIN
|
|
6316
|
+
INSERT INTO session_documents_fts(rowid, title, content_text)
|
|
6317
|
+
VALUES (new.id, new.title, new.content_text);
|
|
6318
|
+
END;
|
|
6319
|
+
|
|
6320
|
+
CREATE TRIGGER IF NOT EXISTS session_documents_ad AFTER DELETE ON session_documents BEGIN
|
|
6321
|
+
INSERT INTO session_documents_fts(session_documents_fts, rowid, title, content_text)
|
|
6322
|
+
VALUES ('delete', old.id, old.title, old.content_text);
|
|
6323
|
+
END;
|
|
6324
|
+
|
|
6325
|
+
CREATE TRIGGER IF NOT EXISTS session_documents_au AFTER UPDATE ON session_documents BEGIN
|
|
6326
|
+
INSERT INTO session_documents_fts(session_documents_fts, rowid, title, content_text)
|
|
6327
|
+
VALUES ('delete', old.id, old.title, old.content_text);
|
|
6328
|
+
INSERT INTO session_documents_fts(rowid, title, content_text)
|
|
6329
|
+
VALUES (new.id, new.title, new.content_text);
|
|
6330
|
+
END;
|
|
6331
|
+
`);
|
|
6332
|
+
}
|
|
6333
|
+
function dropSearchTriggers(db) {
|
|
6334
|
+
db.exec(`
|
|
6335
|
+
DROP TRIGGER IF EXISTS session_documents_ai;
|
|
6336
|
+
DROP TRIGGER IF EXISTS session_documents_ad;
|
|
6337
|
+
DROP TRIGGER IF EXISTS session_documents_au;
|
|
6338
|
+
`);
|
|
6339
|
+
}
|
|
6340
|
+
function ensureProjectColumns(db) {
|
|
6341
|
+
if (!tableExists(db, "session_documents")) {
|
|
6342
|
+
return;
|
|
6474
6343
|
}
|
|
6475
|
-
if (
|
|
6476
|
-
|
|
6344
|
+
if (!columnExists(db, "session_documents", "project_identity_kind")) {
|
|
6345
|
+
db.exec(
|
|
6346
|
+
"ALTER TABLE session_documents ADD COLUMN project_identity_kind TEXT NOT NULL DEFAULT 'path'"
|
|
6347
|
+
);
|
|
6477
6348
|
}
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6349
|
+
if (!columnExists(db, "session_documents", "project_identity_key")) {
|
|
6350
|
+
db.exec(
|
|
6351
|
+
"ALTER TABLE session_documents ADD COLUMN project_identity_key TEXT NOT NULL DEFAULT ''"
|
|
6352
|
+
);
|
|
6481
6353
|
}
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6354
|
+
if (!columnExists(db, "session_documents", "project_display_name")) {
|
|
6355
|
+
db.exec(
|
|
6356
|
+
"ALTER TABLE session_documents ADD COLUMN project_display_name TEXT NOT NULL DEFAULT ''"
|
|
6357
|
+
);
|
|
6485
6358
|
}
|
|
6486
|
-
|
|
6487
|
-
|
|
6359
|
+
}
|
|
6360
|
+
function createProjectTables(db) {
|
|
6361
|
+
ensureProjectColumns(db);
|
|
6362
|
+
db.exec(`
|
|
6363
|
+
CREATE TABLE IF NOT EXISTS project_sessions (
|
|
6364
|
+
agent_name TEXT NOT NULL,
|
|
6365
|
+
session_id TEXT NOT NULL,
|
|
6366
|
+
identity_kind TEXT NOT NULL,
|
|
6367
|
+
identity_key TEXT NOT NULL,
|
|
6368
|
+
display_name TEXT NOT NULL,
|
|
6369
|
+
directory TEXT NOT NULL,
|
|
6370
|
+
activity_time INTEGER NOT NULL,
|
|
6371
|
+
PRIMARY KEY (agent_name, session_id)
|
|
6372
|
+
);
|
|
6373
|
+
|
|
6374
|
+
CREATE INDEX IF NOT EXISTS idx_project_sessions_identity
|
|
6375
|
+
ON project_sessions(identity_kind, identity_key);
|
|
6376
|
+
`);
|
|
6377
|
+
createProjectGroupsView(db);
|
|
6378
|
+
}
|
|
6379
|
+
function createProjectGroupsView(db) {
|
|
6380
|
+
if (!tableExists(db, "sessions")) {
|
|
6381
|
+
db.exec(`
|
|
6382
|
+
CREATE VIEW IF NOT EXISTS project_groups_v AS
|
|
6383
|
+
SELECT
|
|
6384
|
+
identity_kind,
|
|
6385
|
+
identity_key,
|
|
6386
|
+
MIN(display_name) AS display_name,
|
|
6387
|
+
GROUP_CONCAT(DISTINCT agent_name) AS sources_csv,
|
|
6388
|
+
COUNT(*) AS session_count,
|
|
6389
|
+
MAX(activity_time) AS last_activity
|
|
6390
|
+
FROM project_sessions
|
|
6391
|
+
GROUP BY identity_kind, identity_key;
|
|
6392
|
+
`);
|
|
6393
|
+
return;
|
|
6488
6394
|
}
|
|
6489
|
-
|
|
6395
|
+
db.exec(`
|
|
6396
|
+
CREATE VIEW IF NOT EXISTS project_groups_v AS
|
|
6397
|
+
SELECT
|
|
6398
|
+
project_identity_kind AS identity_kind,
|
|
6399
|
+
project_identity_key AS identity_key,
|
|
6400
|
+
MIN(project_display_name) AS display_name,
|
|
6401
|
+
GROUP_CONCAT(DISTINCT agent_name) AS sources_csv,
|
|
6402
|
+
COUNT(*) AS session_count,
|
|
6403
|
+
MAX(activity_time) AS last_activity
|
|
6404
|
+
FROM sessions
|
|
6405
|
+
GROUP BY project_identity_kind, project_identity_key;
|
|
6406
|
+
`);
|
|
6407
|
+
}
|
|
6408
|
+
function recreateProjectGroupsView(db) {
|
|
6409
|
+
db.exec("DROP VIEW IF EXISTS project_groups_v");
|
|
6410
|
+
createProjectGroupsView(db);
|
|
6411
|
+
}
|
|
6412
|
+
function createLatestCacheSchema(db) {
|
|
6413
|
+
createCacheTables(db);
|
|
6414
|
+
createSessionTables(db);
|
|
6415
|
+
createMessageSearchTables(db);
|
|
6416
|
+
createFileActivityTables(db);
|
|
6417
|
+
createSearchTables(db);
|
|
6418
|
+
createProjectTables(db);
|
|
6490
6419
|
}
|
|
6491
6420
|
function recreateSearchIndexSchema(db) {
|
|
6492
6421
|
db.exec(`
|
|
@@ -6743,36 +6672,6 @@ function backfillStructuredSessions(db) {
|
|
|
6743
6672
|
);
|
|
6744
6673
|
}
|
|
6745
6674
|
}
|
|
6746
|
-
function messageFromBackfillRow(row) {
|
|
6747
|
-
const role = row.role === "assistant" || row.role === "tool" ? row.role : "user";
|
|
6748
|
-
return {
|
|
6749
|
-
id: String(row.message_id ?? ""),
|
|
6750
|
-
role,
|
|
6751
|
-
agent: row.agent ?? null,
|
|
6752
|
-
time_created: Number(row.time_created ?? 0),
|
|
6753
|
-
time_completed: row.time_completed == null ? null : Number(row.time_completed),
|
|
6754
|
-
mode: row.mode ?? null,
|
|
6755
|
-
model: row.model ?? null,
|
|
6756
|
-
provider: row.provider ?? null,
|
|
6757
|
-
parts: JSON.parse(String(row.parts_json ?? "[]")),
|
|
6758
|
-
subagent_id: row.subagent_id ?? void 0,
|
|
6759
|
-
nickname: row.nickname ?? void 0
|
|
6760
|
-
};
|
|
6761
|
-
}
|
|
6762
|
-
function messageFromCachedRow(row) {
|
|
6763
|
-
const message = messageFromBackfillRow(row);
|
|
6764
|
-
const tokens = parseOptionalJson(row.tokens_json);
|
|
6765
|
-
if (tokens) {
|
|
6766
|
-
message.tokens = tokens;
|
|
6767
|
-
}
|
|
6768
|
-
if (row.cost != null) {
|
|
6769
|
-
message.cost = Number(row.cost);
|
|
6770
|
-
}
|
|
6771
|
-
if (row.cost_source) {
|
|
6772
|
-
message.cost_source = row.cost_source;
|
|
6773
|
-
}
|
|
6774
|
-
return message;
|
|
6775
|
-
}
|
|
6776
6675
|
function backfillMessageTools(db) {
|
|
6777
6676
|
createMessageToolTables(db);
|
|
6778
6677
|
if (!tableExists(db, "messages")) {
|
|
@@ -6866,13 +6765,6 @@ function rebuildMessageSearchIndex(db) {
|
|
|
6866
6765
|
}
|
|
6867
6766
|
db.exec("INSERT INTO messages_fts(messages_fts) VALUES ('rebuild')");
|
|
6868
6767
|
}
|
|
6869
|
-
function shouldBulkSyncSearchIndex(options, changedCount) {
|
|
6870
|
-
if (options.isBulk != null) {
|
|
6871
|
-
return options.isBulk;
|
|
6872
|
-
}
|
|
6873
|
-
const threshold = options.bulkThreshold ?? SEARCH_INDEX_BULK_SYNC_THRESHOLD;
|
|
6874
|
-
return threshold > 0 && changedCount >= threshold;
|
|
6875
|
-
}
|
|
6876
6768
|
function ensureFtsReady(db) {
|
|
6877
6769
|
if (!tableExists(db, "session_documents_fts")) {
|
|
6878
6770
|
createSearchTables(db);
|
|
@@ -6887,7 +6779,7 @@ function ensureFtsReady(db) {
|
|
|
6887
6779
|
function ensureFtsConsistency(db) {
|
|
6888
6780
|
ensureFtsReady(db);
|
|
6889
6781
|
const cachePath = getCachePath2();
|
|
6890
|
-
if (
|
|
6782
|
+
if (getFtsIntegrityCheckedPath() === cachePath) {
|
|
6891
6783
|
return;
|
|
6892
6784
|
}
|
|
6893
6785
|
try {
|
|
@@ -6895,11 +6787,11 @@ function ensureFtsConsistency(db) {
|
|
|
6895
6787
|
"INSERT INTO session_documents_fts(session_documents_fts, rank) VALUES ('integrity-check', 1)"
|
|
6896
6788
|
);
|
|
6897
6789
|
db.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES ('integrity-check', 1)");
|
|
6898
|
-
|
|
6790
|
+
setFtsIntegrityCheckedPath(cachePath);
|
|
6899
6791
|
} catch {
|
|
6900
6792
|
rebuildSearchIndex(db);
|
|
6901
6793
|
rebuildMessageSearchIndex(db);
|
|
6902
|
-
|
|
6794
|
+
setFtsIntegrityCheckedPath(cachePath);
|
|
6903
6795
|
}
|
|
6904
6796
|
}
|
|
6905
6797
|
function setCacheSchemaVersion(db) {
|
|
@@ -6959,655 +6851,187 @@ function ensureSchema(db, dbPath) {
|
|
|
6959
6851
|
}
|
|
6960
6852
|
},
|
|
6961
6853
|
{
|
|
6962
|
-
version: 10,
|
|
6963
|
-
migrate(db2) {
|
|
6964
|
-
createFileActivityPathSearchTables(db2);
|
|
6965
|
-
rebuildFileActivityPathIndex(db2);
|
|
6966
|
-
}
|
|
6967
|
-
},
|
|
6968
|
-
{
|
|
6969
|
-
version: 11,
|
|
6970
|
-
migrate(db2) {
|
|
6971
|
-
backfillMessageTools(db2);
|
|
6972
|
-
}
|
|
6973
|
-
},
|
|
6974
|
-
{
|
|
6975
|
-
version: 12,
|
|
6976
|
-
migrate(db2) {
|
|
6977
|
-
refreshProjectIdentities(db2);
|
|
6978
|
-
}
|
|
6979
|
-
},
|
|
6980
|
-
{ version: 13, migrate: createCacheTables }
|
|
6981
|
-
]
|
|
6982
|
-
});
|
|
6983
|
-
createLatestCacheSchema(db);
|
|
6984
|
-
if (getUserVersion(db) <= CACHE_SCHEMA_VERSION) {
|
|
6985
|
-
setCacheSchemaVersion(db);
|
|
6986
|
-
}
|
|
6987
|
-
}
|
|
6988
|
-
function sessionContentHash(session) {
|
|
6989
|
-
return JSON.stringify([
|
|
6990
|
-
session.slug,
|
|
6991
|
-
session.title,
|
|
6992
|
-
session.directory,
|
|
6993
|
-
session.time_created,
|
|
6994
|
-
session.time_updated ?? session.time_created,
|
|
6995
|
-
session.stats.message_count,
|
|
6996
|
-
session.stats.total_input_tokens,
|
|
6997
|
-
session.stats.total_output_tokens,
|
|
6998
|
-
session.stats.total_cache_read_tokens ?? 0,
|
|
6999
|
-
session.stats.total_cache_create_tokens ?? 0,
|
|
7000
|
-
session.stats.total_cost,
|
|
7001
|
-
session.stats.cost_source ?? "",
|
|
7002
|
-
session.stats.total_tokens ?? 0
|
|
7003
|
-
]);
|
|
7004
|
-
}
|
|
7005
|
-
function escapeFtsTerm(value) {
|
|
7006
|
-
return value.replaceAll('"', '""');
|
|
7007
|
-
}
|
|
7008
|
-
function splitSearchTokens(input) {
|
|
7009
|
-
const tokens = [];
|
|
7010
|
-
let token = "";
|
|
7011
|
-
let inQuote = false;
|
|
7012
|
-
for (const char of input) {
|
|
7013
|
-
if (char === '"') {
|
|
7014
|
-
inQuote = !inQuote;
|
|
7015
|
-
token += char;
|
|
7016
|
-
continue;
|
|
7017
|
-
}
|
|
7018
|
-
if (/\s/.test(char) && !inQuote) {
|
|
7019
|
-
if (token) {
|
|
7020
|
-
tokens.push(token);
|
|
7021
|
-
token = "";
|
|
7022
|
-
}
|
|
7023
|
-
continue;
|
|
7024
|
-
}
|
|
7025
|
-
token += char;
|
|
7026
|
-
}
|
|
7027
|
-
if (token) {
|
|
7028
|
-
tokens.push(token);
|
|
7029
|
-
}
|
|
7030
|
-
return tokens;
|
|
7031
|
-
}
|
|
7032
|
-
function unwrapSearchValue(value) {
|
|
7033
|
-
const trimmed = value.trim();
|
|
7034
|
-
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
7035
|
-
return trimmed.slice(1, -1).trim();
|
|
7036
|
-
}
|
|
7037
|
-
return trimmed;
|
|
7038
|
-
}
|
|
7039
|
-
function parseCostQualifier(value, filters) {
|
|
7040
|
-
const raw = unwrapSearchValue(value);
|
|
7041
|
-
const range = raw.match(/^(\d+(?:\.\d+)?)\.\.(\d+(?:\.\d+)?)$/);
|
|
7042
|
-
if (range) {
|
|
7043
|
-
filters.costMin = Number(range[1]);
|
|
7044
|
-
filters.costMax = Number(range[2]);
|
|
7045
|
-
return;
|
|
7046
|
-
}
|
|
7047
|
-
const comparison = raw.match(/^(>=|>|<=|<)(\d+(?:\.\d+)?)$/);
|
|
7048
|
-
if (comparison) {
|
|
7049
|
-
const amount2 = Number(comparison[2]);
|
|
7050
|
-
if (comparison[1]?.includes(">")) {
|
|
7051
|
-
filters.costMin = amount2;
|
|
7052
|
-
filters.costMinExclusive = comparison[1] === ">";
|
|
7053
|
-
} else {
|
|
7054
|
-
filters.costMax = amount2;
|
|
7055
|
-
filters.costMaxExclusive = comparison[1] === "<";
|
|
7056
|
-
}
|
|
7057
|
-
return;
|
|
7058
|
-
}
|
|
7059
|
-
const amount = Number(raw);
|
|
7060
|
-
if (!Number.isNaN(amount)) {
|
|
7061
|
-
filters.costMin = amount;
|
|
7062
|
-
filters.costMax = amount;
|
|
7063
|
-
}
|
|
7064
|
-
}
|
|
7065
|
-
function appendUnique(values, value) {
|
|
7066
|
-
if (values?.includes(value)) return values;
|
|
7067
|
-
return [...values ?? [], value];
|
|
7068
|
-
}
|
|
7069
|
-
function isSmartTag(value) {
|
|
7070
|
-
return value === "bugfix" || value === "refactoring" || value === "feature-dev" || value === "testing" || value === "docs" || value === "git-ops" || value === "build-deploy" || value === "exploration" || value === "planning";
|
|
7071
|
-
}
|
|
7072
|
-
function parseSearchQuery(input) {
|
|
7073
|
-
const filters = {};
|
|
7074
|
-
const textTokens = [];
|
|
7075
|
-
let hasQualifiers = false;
|
|
7076
|
-
for (const token of splitSearchTokens(input)) {
|
|
7077
|
-
const match = token.match(/^([a-zA-Z][a-zA-Z_-]*):(.+)$/);
|
|
7078
|
-
if (!match) {
|
|
7079
|
-
textTokens.push(token);
|
|
7080
|
-
continue;
|
|
7081
|
-
}
|
|
7082
|
-
const key = match[1].toLowerCase();
|
|
7083
|
-
const value = unwrapSearchValue(match[2]);
|
|
7084
|
-
if (!value) continue;
|
|
7085
|
-
let consumed = true;
|
|
7086
|
-
if (key === "agent") filters.agent = value.toLowerCase();
|
|
7087
|
-
else if (key === "project") filters.project = value;
|
|
7088
|
-
else if (key === "projectkey" || key === "project-key") filters.projectKey = value;
|
|
7089
|
-
else if (key === "cwd") filters.cwd = value;
|
|
7090
|
-
else if (key === "tool") filters.tools = appendUnique(filters.tools, value.toLowerCase());
|
|
7091
|
-
else if (key === "file" || key === "path") filters.file = value;
|
|
7092
|
-
else if (key === "kind" || key === "filekind" || key === "file-kind") {
|
|
7093
|
-
if (value === "read" || value === "edit" || value === "write" || value === "delete") {
|
|
7094
|
-
filters.fileKind = value;
|
|
7095
|
-
} else {
|
|
7096
|
-
consumed = false;
|
|
7097
|
-
}
|
|
7098
|
-
} else if (key === "tag" || key === "signal") {
|
|
7099
|
-
const tag = value.toLowerCase();
|
|
7100
|
-
if (isSmartTag(tag)) {
|
|
7101
|
-
filters.tags = appendUnique(filters.tags, tag);
|
|
7102
|
-
} else {
|
|
7103
|
-
consumed = false;
|
|
7104
|
-
}
|
|
7105
|
-
} else if (key === "cost") {
|
|
7106
|
-
parseCostQualifier(value, filters);
|
|
7107
|
-
} else {
|
|
7108
|
-
consumed = false;
|
|
7109
|
-
}
|
|
7110
|
-
if (consumed) {
|
|
7111
|
-
hasQualifiers = true;
|
|
7112
|
-
} else {
|
|
7113
|
-
textTokens.push(token);
|
|
7114
|
-
}
|
|
7115
|
-
}
|
|
7116
|
-
return {
|
|
7117
|
-
text: textTokens.join(" ").trim(),
|
|
7118
|
-
filters,
|
|
7119
|
-
hasQualifiers
|
|
7120
|
-
};
|
|
7121
|
-
}
|
|
7122
|
-
function toFtsQuery(input) {
|
|
7123
|
-
const tokens = splitSearchTokens(input);
|
|
7124
|
-
const mapped = tokens.map((token) => {
|
|
7125
|
-
if (/^OR$/i.test(token)) {
|
|
7126
|
-
return "OR";
|
|
7127
|
-
}
|
|
7128
|
-
if (token.startsWith('"') && token.endsWith('"')) {
|
|
7129
|
-
return `"${escapeFtsTerm(token.slice(1, -1))}"`;
|
|
7130
|
-
}
|
|
7131
|
-
return `"${escapeFtsTerm(token)}"`;
|
|
7132
|
-
}).filter(
|
|
7133
|
-
(token, index, values) => token !== "OR" || index > 0 && index < values.length - 1 && values[index - 1] !== "OR" && values[index + 1] !== "OR"
|
|
7134
|
-
);
|
|
7135
|
-
return mapped.join(" ");
|
|
7136
|
-
}
|
|
7137
|
-
function appendPlainText(value, chunks) {
|
|
7138
|
-
if (value == null) return;
|
|
7139
|
-
if (typeof value === "string") {
|
|
7140
|
-
const normalized = value.trim();
|
|
7141
|
-
if (normalized) {
|
|
7142
|
-
chunks.push(normalized);
|
|
7143
|
-
}
|
|
7144
|
-
return;
|
|
7145
|
-
}
|
|
7146
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
7147
|
-
chunks.push(String(value));
|
|
7148
|
-
return;
|
|
7149
|
-
}
|
|
7150
|
-
if (Array.isArray(value)) {
|
|
7151
|
-
for (const item of value) {
|
|
7152
|
-
appendPlainText(item, chunks);
|
|
7153
|
-
}
|
|
7154
|
-
return;
|
|
7155
|
-
}
|
|
7156
|
-
if (typeof value === "object") {
|
|
7157
|
-
for (const nested of Object.values(value)) {
|
|
7158
|
-
appendPlainText(nested, chunks);
|
|
7159
|
-
}
|
|
7160
|
-
}
|
|
7161
|
-
}
|
|
7162
|
-
function compactRecord(record) {
|
|
7163
|
-
return Object.fromEntries(Object.entries(record).filter(([, value]) => value != null));
|
|
7164
|
-
}
|
|
7165
|
-
function normalizeToolName2(value) {
|
|
7166
|
-
if (typeof value !== "string") return null;
|
|
7167
|
-
const name = value.trim().toLowerCase();
|
|
7168
|
-
return name || null;
|
|
7169
|
-
}
|
|
7170
|
-
function toolNamesFromMetadataJson(value) {
|
|
7171
|
-
if (!value) return [];
|
|
7172
|
-
try {
|
|
7173
|
-
const metadata = JSON.parse(String(value));
|
|
7174
|
-
if (!Array.isArray(metadata)) return [];
|
|
7175
|
-
const tools = /* @__PURE__ */ new Set();
|
|
7176
|
-
for (const item of metadata) {
|
|
7177
|
-
if (item == null || typeof item !== "object") continue;
|
|
7178
|
-
const toolName = normalizeToolName2(item.tool);
|
|
7179
|
-
if (toolName) tools.add(toolName);
|
|
7180
|
-
}
|
|
7181
|
-
return [...tools];
|
|
7182
|
-
} catch {
|
|
7183
|
-
return [];
|
|
7184
|
-
}
|
|
7185
|
-
}
|
|
7186
|
-
function toolNamesFromMessage(message) {
|
|
7187
|
-
const tools = /* @__PURE__ */ new Set();
|
|
7188
|
-
for (const part of message.parts) {
|
|
7189
|
-
if (part.type !== "tool") continue;
|
|
7190
|
-
const toolName = normalizeToolName2(part.tool);
|
|
7191
|
-
if (toolName) tools.add(toolName);
|
|
7192
|
-
}
|
|
7193
|
-
return [...tools];
|
|
7194
|
-
}
|
|
7195
|
-
function summarizeToolPart(part) {
|
|
7196
|
-
const state = part.state == null ? void 0 : compactRecord({
|
|
7197
|
-
status: part.state.status,
|
|
7198
|
-
error: part.state.error,
|
|
7199
|
-
metadata: part.state.metadata
|
|
7200
|
-
});
|
|
7201
|
-
return compactRecord({
|
|
7202
|
-
type: part.type,
|
|
7203
|
-
tool: part.tool,
|
|
7204
|
-
title: part.title,
|
|
7205
|
-
nickname: part.nickname,
|
|
7206
|
-
callID: part.callID,
|
|
7207
|
-
approval_status: part.approval_status,
|
|
7208
|
-
state
|
|
7209
|
-
});
|
|
7210
|
-
}
|
|
7211
|
-
function buildMessageText(message) {
|
|
7212
|
-
const chunks = [];
|
|
7213
|
-
chunks.push(message.role);
|
|
7214
|
-
appendPlainText(message.agent, chunks);
|
|
7215
|
-
appendPlainText(message.model, chunks);
|
|
7216
|
-
for (const part of message.parts) {
|
|
7217
|
-
appendPlainText(part.type, chunks);
|
|
7218
|
-
appendPlainText(part.title, chunks);
|
|
7219
|
-
appendPlainText(part.nickname, chunks);
|
|
7220
|
-
appendPlainText(part.tool, chunks);
|
|
7221
|
-
appendPlainText(part.text, chunks);
|
|
7222
|
-
appendPlainText(part.input, chunks);
|
|
7223
|
-
appendPlainText(part.output, chunks);
|
|
7224
|
-
appendPlainText(part.state, chunks);
|
|
7225
|
-
}
|
|
7226
|
-
return chunks.join("\n");
|
|
7227
|
-
}
|
|
7228
|
-
function normalizeMessages(session) {
|
|
7229
|
-
return session.messages.map((message, index) => {
|
|
7230
|
-
const toolMetadata = message.parts.filter((part) => part.type === "tool").map((part) => summarizeToolPart(part));
|
|
7231
|
-
return {
|
|
7232
|
-
index,
|
|
7233
|
-
id: message.id || `${session.id}:${index}`,
|
|
7234
|
-
role: message.role,
|
|
7235
|
-
timeCreated: message.time_created,
|
|
7236
|
-
timeCompleted: message.time_completed ?? null,
|
|
7237
|
-
agent: message.agent ?? null,
|
|
7238
|
-
mode: message.mode ?? null,
|
|
7239
|
-
model: message.model ?? null,
|
|
7240
|
-
provider: message.provider ?? null,
|
|
7241
|
-
tokensJson: stringifyOptionalJson(message.tokens),
|
|
7242
|
-
cost: message.cost ?? null,
|
|
7243
|
-
costSource: message.cost_source ?? null,
|
|
7244
|
-
partsJson: JSON.stringify(message.parts),
|
|
7245
|
-
subagentId: message.subagent_id ?? null,
|
|
7246
|
-
nickname: message.nickname ?? null,
|
|
7247
|
-
contentText: buildMessageText(message),
|
|
7248
|
-
toolMetadataJson: toolMetadata.length > 0 ? JSON.stringify(toolMetadata) : null,
|
|
7249
|
-
toolNames: toolNamesFromMessage(message)
|
|
7250
|
-
};
|
|
7251
|
-
});
|
|
7252
|
-
}
|
|
7253
|
-
function buildSessionContentFromMessages(title, messages) {
|
|
7254
|
-
const chunks = [];
|
|
7255
|
-
appendPlainText(title, chunks);
|
|
7256
|
-
for (const message of messages) {
|
|
7257
|
-
appendPlainText(message.contentText, chunks);
|
|
7258
|
-
}
|
|
7259
|
-
return chunks.join("\n");
|
|
7260
|
-
}
|
|
7261
|
-
function deleteLegacyCacheFile() {
|
|
7262
|
-
const legacyPath = getLegacyCachePath();
|
|
7263
|
-
if (!existsSync11(legacyPath)) {
|
|
7264
|
-
return;
|
|
7265
|
-
}
|
|
7266
|
-
try {
|
|
7267
|
-
unlinkSync(legacyPath);
|
|
7268
|
-
} catch {
|
|
7269
|
-
}
|
|
7270
|
-
}
|
|
7271
|
-
function loadCachedSessions(agentName) {
|
|
7272
|
-
if (!hasCacheStorage()) {
|
|
7273
|
-
return null;
|
|
7274
|
-
}
|
|
7275
|
-
return withCacheDb((db) => {
|
|
7276
|
-
const timestampRow = db.prepare("SELECT timestamp AS value FROM agent_cache WHERE agent_name = ?").get(agentName);
|
|
7277
|
-
const timestamp = Number(timestampRow?.value ?? 0);
|
|
7278
|
-
if (!timestamp) {
|
|
7279
|
-
return null;
|
|
7280
|
-
}
|
|
7281
|
-
const rows = db.prepare(
|
|
7282
|
-
`
|
|
7283
|
-
SELECT
|
|
7284
|
-
session_id,
|
|
7285
|
-
sort_index,
|
|
7286
|
-
slug,
|
|
7287
|
-
title,
|
|
7288
|
-
source_path,
|
|
7289
|
-
directory,
|
|
7290
|
-
project_identity_kind,
|
|
7291
|
-
project_identity_key,
|
|
7292
|
-
project_display_name,
|
|
7293
|
-
time_created,
|
|
7294
|
-
time_updated,
|
|
7295
|
-
message_count,
|
|
7296
|
-
total_input_tokens,
|
|
7297
|
-
total_output_tokens,
|
|
7298
|
-
total_cache_read_tokens,
|
|
7299
|
-
total_cache_create_tokens,
|
|
7300
|
-
total_cost,
|
|
7301
|
-
cost_source,
|
|
7302
|
-
total_tokens,
|
|
7303
|
-
model_usage_json,
|
|
7304
|
-
smart_tags_json,
|
|
7305
|
-
smart_tags_source_updated_at,
|
|
7306
|
-
meta_json
|
|
7307
|
-
FROM sessions
|
|
7308
|
-
WHERE agent_name = ?
|
|
7309
|
-
ORDER BY sort_index, activity_time DESC
|
|
7310
|
-
`
|
|
7311
|
-
).all(agentName);
|
|
7312
|
-
const sessions = [];
|
|
7313
|
-
const meta = {};
|
|
7314
|
-
for (const row of rows) {
|
|
7315
|
-
const session = sessionFromRow(row);
|
|
7316
|
-
sessions.push(session);
|
|
7317
|
-
if (row.meta_json) {
|
|
7318
|
-
meta[session.id] = JSON.parse(row.meta_json);
|
|
7319
|
-
}
|
|
7320
|
-
}
|
|
7321
|
-
return { sessions, meta, timestamp };
|
|
7322
|
-
});
|
|
7323
|
-
}
|
|
7324
|
-
function isAgentCacheInitialized(agentName, indexVersion = CACHE_INITIALIZATION_VERSION) {
|
|
7325
|
-
if (!hasCacheStorage()) {
|
|
7326
|
-
return false;
|
|
7327
|
-
}
|
|
7328
|
-
return withCacheDbReadOnly((db) => {
|
|
7329
|
-
if (!tableExists(db, "cache_initialization")) return false;
|
|
7330
|
-
const row = db.prepare(
|
|
7331
|
-
`
|
|
7332
|
-
SELECT index_version
|
|
7333
|
-
FROM cache_initialization
|
|
7334
|
-
WHERE agent_name = ?
|
|
7335
|
-
`
|
|
7336
|
-
).get(agentName);
|
|
7337
|
-
return row?.index_version === indexVersion;
|
|
7338
|
-
}) ?? false;
|
|
7339
|
-
}
|
|
7340
|
-
function markAgentCacheInitialized(agentName, indexVersion = CACHE_INITIALIZATION_VERSION) {
|
|
7341
|
-
withCacheDb((db) => {
|
|
7342
|
-
const now = Date.now();
|
|
7343
|
-
db.prepare(
|
|
7344
|
-
`
|
|
7345
|
-
INSERT INTO cache_initialization(agent_name, initialized_at, index_version, last_sync_at)
|
|
7346
|
-
VALUES (?, ?, ?, ?)
|
|
7347
|
-
ON CONFLICT(agent_name) DO UPDATE SET
|
|
7348
|
-
index_version = excluded.index_version,
|
|
7349
|
-
last_sync_at = excluded.last_sync_at
|
|
7350
|
-
`
|
|
7351
|
-
).run(agentName, now, indexVersion, now);
|
|
7352
|
-
});
|
|
7353
|
-
}
|
|
7354
|
-
function loadCachedSessionData(agentName, sessionId) {
|
|
7355
|
-
if (!hasCacheStorage()) {
|
|
7356
|
-
return null;
|
|
7357
|
-
}
|
|
7358
|
-
return withCacheDbReadOnly((db) => {
|
|
7359
|
-
const row = db.prepare(
|
|
7360
|
-
`
|
|
7361
|
-
SELECT
|
|
7362
|
-
session_id,
|
|
7363
|
-
sort_index,
|
|
7364
|
-
slug,
|
|
7365
|
-
title,
|
|
7366
|
-
source_path,
|
|
7367
|
-
directory,
|
|
7368
|
-
project_identity_kind,
|
|
7369
|
-
project_identity_key,
|
|
7370
|
-
project_display_name,
|
|
7371
|
-
time_created,
|
|
7372
|
-
time_updated,
|
|
7373
|
-
message_count,
|
|
7374
|
-
total_input_tokens,
|
|
7375
|
-
total_output_tokens,
|
|
7376
|
-
total_cache_read_tokens,
|
|
7377
|
-
total_cache_create_tokens,
|
|
7378
|
-
total_cost,
|
|
7379
|
-
cost_source,
|
|
7380
|
-
total_tokens,
|
|
7381
|
-
model_usage_json,
|
|
7382
|
-
smart_tags_json,
|
|
7383
|
-
smart_tags_source_updated_at,
|
|
7384
|
-
meta_json
|
|
7385
|
-
FROM sessions
|
|
7386
|
-
WHERE agent_name = ? AND session_id = ?
|
|
7387
|
-
`
|
|
7388
|
-
).get(agentName, sessionId);
|
|
7389
|
-
if (!row) {
|
|
7390
|
-
return null;
|
|
7391
|
-
}
|
|
7392
|
-
const messageRows = db.prepare(
|
|
7393
|
-
`
|
|
7394
|
-
SELECT
|
|
7395
|
-
message_id,
|
|
7396
|
-
role,
|
|
7397
|
-
time_created,
|
|
7398
|
-
time_completed,
|
|
7399
|
-
agent,
|
|
7400
|
-
mode,
|
|
7401
|
-
model,
|
|
7402
|
-
provider,
|
|
7403
|
-
tokens_json,
|
|
7404
|
-
cost,
|
|
7405
|
-
cost_source,
|
|
7406
|
-
parts_json,
|
|
7407
|
-
subagent_id,
|
|
7408
|
-
nickname
|
|
7409
|
-
FROM messages
|
|
7410
|
-
WHERE agent_name = ? AND session_id = ?
|
|
7411
|
-
ORDER BY message_index
|
|
7412
|
-
`
|
|
7413
|
-
).all(agentName, sessionId);
|
|
7414
|
-
const head = sessionFromRow(row);
|
|
7415
|
-
const fileActivityRows = db.prepare(
|
|
7416
|
-
`
|
|
7417
|
-
SELECT agent_name, session_id, project_identity_key, path, kind, count, latest_time
|
|
7418
|
-
FROM session_file_activity
|
|
7419
|
-
WHERE agent_name = ? AND session_id = ?
|
|
7420
|
-
ORDER BY latest_time DESC, count DESC, path
|
|
7421
|
-
LIMIT 500
|
|
7422
|
-
`
|
|
7423
|
-
).all(agentName, sessionId);
|
|
7424
|
-
return {
|
|
7425
|
-
...head,
|
|
7426
|
-
messages: messageRows.map((messageRow) => messageFromCachedRow(messageRow)),
|
|
7427
|
-
file_activity: fileActivityRows.map((activityRow) => fileActivityFromRow(activityRow))
|
|
7428
|
-
};
|
|
7429
|
-
});
|
|
7430
|
-
}
|
|
7431
|
-
function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
7432
|
-
withCacheDb((db) => {
|
|
7433
|
-
const deleteAgent = db.prepare("DELETE FROM agent_cache WHERE agent_name = ?");
|
|
7434
|
-
const deleteLegacySessions = db.prepare("DELETE FROM cached_sessions WHERE agent_name = ?");
|
|
7435
|
-
const deleteSession = db.prepare(
|
|
7436
|
-
"DELETE FROM sessions WHERE agent_name = ? AND session_id = ?"
|
|
7437
|
-
);
|
|
7438
|
-
const deleteSearchDocument = db.prepare(
|
|
7439
|
-
"DELETE FROM session_documents WHERE agent_name = ? AND session_id = ?"
|
|
7440
|
-
);
|
|
7441
|
-
const deleteMessages = db.prepare(
|
|
7442
|
-
"DELETE FROM messages WHERE agent_name = ? AND session_id = ?"
|
|
7443
|
-
);
|
|
7444
|
-
const deleteMessageTools = db.prepare(
|
|
7445
|
-
"DELETE FROM message_tools WHERE agent_name = ? AND session_id = ?"
|
|
7446
|
-
);
|
|
7447
|
-
const deleteFileActivity = db.prepare(
|
|
7448
|
-
"DELETE FROM session_file_activity WHERE agent_name = ? AND session_id = ?"
|
|
7449
|
-
);
|
|
7450
|
-
const deleteProjectSession = db.prepare(
|
|
7451
|
-
"DELETE FROM project_sessions WHERE agent_name = ? AND session_id = ?"
|
|
7452
|
-
);
|
|
7453
|
-
const deleteProjectSessions = db.prepare("DELETE FROM project_sessions WHERE agent_name = ?");
|
|
7454
|
-
const upsertAgent = db.prepare(`
|
|
7455
|
-
INSERT INTO agent_cache(agent_name, timestamp)
|
|
7456
|
-
VALUES (?, ?)
|
|
7457
|
-
ON CONFLICT(agent_name) DO UPDATE SET timestamp = excluded.timestamp
|
|
7458
|
-
`);
|
|
7459
|
-
const upsertCachedSession = prepareUpsertCachedSession(db);
|
|
7460
|
-
const upsertSession = prepareUpsertSession(db);
|
|
7461
|
-
const upsertProjectSession = prepareUpsertProjectSession(db);
|
|
7462
|
-
const write = db.transaction(() => {
|
|
7463
|
-
const timestamp = Date.now();
|
|
7464
|
-
const sessionIds = new Set(sessions.map((session) => session.id));
|
|
7465
|
-
const existingSessionIds = db.prepare("SELECT session_id FROM sessions WHERE agent_name = ?").all(agentName);
|
|
7466
|
-
deleteAgent.run(agentName);
|
|
7467
|
-
deleteLegacySessions.run(agentName);
|
|
7468
|
-
deleteProjectSessions.run(agentName);
|
|
7469
|
-
upsertAgent.run(agentName, timestamp);
|
|
7470
|
-
for (const row of existingSessionIds) {
|
|
7471
|
-
const sessionId = String(row.session_id);
|
|
7472
|
-
if (!sessionIds.has(sessionId)) {
|
|
7473
|
-
deleteSearchDocument.run(agentName, sessionId);
|
|
7474
|
-
deleteMessageTools.run(agentName, sessionId);
|
|
7475
|
-
deleteMessages.run(agentName, sessionId);
|
|
7476
|
-
deleteFileActivity.run(agentName, sessionId);
|
|
7477
|
-
deleteProjectSession.run(agentName, sessionId);
|
|
7478
|
-
deleteSession.run(agentName, sessionId);
|
|
7479
|
-
}
|
|
7480
|
-
}
|
|
7481
|
-
sessions.forEach((session, index) => {
|
|
7482
|
-
const identity = session.project_identity ?? computeIdentity(session.directory, realFs);
|
|
7483
|
-
const sessionMeta = meta[session.id];
|
|
7484
|
-
const metaJson = sessionMeta ? JSON.stringify(sessionMeta) : null;
|
|
7485
|
-
upsertCachedSession.run(agentName, session.id, JSON.stringify(session), metaJson);
|
|
7486
|
-
upsertSessionRow(
|
|
7487
|
-
upsertSession,
|
|
7488
|
-
agentName,
|
|
7489
|
-
session,
|
|
7490
|
-
metaJson,
|
|
7491
|
-
index,
|
|
7492
|
-
sourcePathFromMeta(sessionMeta)
|
|
7493
|
-
);
|
|
7494
|
-
writeProjectSessionRow(upsertProjectSession, agentName, session, identity);
|
|
7495
|
-
});
|
|
7496
|
-
});
|
|
7497
|
-
write();
|
|
7498
|
-
deleteLegacyCacheFile();
|
|
6854
|
+
version: 10,
|
|
6855
|
+
migrate(db2) {
|
|
6856
|
+
createFileActivityPathSearchTables(db2);
|
|
6857
|
+
rebuildFileActivityPathIndex(db2);
|
|
6858
|
+
}
|
|
6859
|
+
},
|
|
6860
|
+
{
|
|
6861
|
+
version: 11,
|
|
6862
|
+
migrate(db2) {
|
|
6863
|
+
backfillMessageTools(db2);
|
|
6864
|
+
}
|
|
6865
|
+
},
|
|
6866
|
+
{
|
|
6867
|
+
version: 12,
|
|
6868
|
+
migrate(db2) {
|
|
6869
|
+
refreshProjectIdentities(db2);
|
|
6870
|
+
}
|
|
6871
|
+
},
|
|
6872
|
+
{ version: 13, migrate: createCacheTables }
|
|
6873
|
+
]
|
|
7499
6874
|
});
|
|
6875
|
+
createLatestCacheSchema(db);
|
|
6876
|
+
if (getUserVersion(db) <= CACHE_SCHEMA_VERSION) {
|
|
6877
|
+
setCacheSchemaVersion(db);
|
|
6878
|
+
}
|
|
7500
6879
|
}
|
|
7501
|
-
function
|
|
7502
|
-
if (
|
|
7503
|
-
return;
|
|
6880
|
+
function shouldBulkSyncSearchIndex(options, changedCount) {
|
|
6881
|
+
if (options.isBulk != null) {
|
|
6882
|
+
return options.isBulk;
|
|
7504
6883
|
}
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
deleteFileActivity.run(agentName, sessionId);
|
|
7543
|
-
deleteProjectSession.run(agentName, sessionId);
|
|
7544
|
-
deleteSession.run(agentName, sessionId);
|
|
7545
|
-
}
|
|
7546
|
-
for (const { session, sortIndex } of changes) {
|
|
7547
|
-
const identity = session.project_identity ?? computeIdentity(session.directory, realFs);
|
|
7548
|
-
const sessionMeta = meta[session.id];
|
|
7549
|
-
const metaJson = sessionMeta ? JSON.stringify(sessionMeta) : null;
|
|
7550
|
-
upsertCachedSession.run(agentName, session.id, JSON.stringify(session), metaJson);
|
|
7551
|
-
upsertSessionRow(
|
|
7552
|
-
upsertSession,
|
|
7553
|
-
agentName,
|
|
7554
|
-
session,
|
|
7555
|
-
metaJson,
|
|
7556
|
-
sortIndex,
|
|
7557
|
-
sourcePathFromMeta(sessionMeta)
|
|
7558
|
-
);
|
|
7559
|
-
writeProjectSessionRow(upsertProjectSession, agentName, session, identity);
|
|
6884
|
+
const threshold = options.bulkThreshold ?? SEARCH_INDEX_BULK_SYNC_THRESHOLD;
|
|
6885
|
+
return threshold > 0 && changedCount >= threshold;
|
|
6886
|
+
}
|
|
6887
|
+
function sessionContentHash(session) {
|
|
6888
|
+
return JSON.stringify([
|
|
6889
|
+
session.slug,
|
|
6890
|
+
session.title,
|
|
6891
|
+
session.directory,
|
|
6892
|
+
session.time_created,
|
|
6893
|
+
session.time_updated ?? session.time_created,
|
|
6894
|
+
session.stats.message_count,
|
|
6895
|
+
session.stats.total_input_tokens,
|
|
6896
|
+
session.stats.total_output_tokens,
|
|
6897
|
+
session.stats.total_cache_read_tokens ?? 0,
|
|
6898
|
+
session.stats.total_cache_create_tokens ?? 0,
|
|
6899
|
+
session.stats.total_cost,
|
|
6900
|
+
session.stats.cost_source ?? "",
|
|
6901
|
+
session.stats.total_tokens ?? 0
|
|
6902
|
+
]);
|
|
6903
|
+
}
|
|
6904
|
+
function escapeFtsTerm(value) {
|
|
6905
|
+
return value.replaceAll('"', '""');
|
|
6906
|
+
}
|
|
6907
|
+
function splitSearchTokens(input) {
|
|
6908
|
+
const tokens = [];
|
|
6909
|
+
let token = "";
|
|
6910
|
+
let inQuote = false;
|
|
6911
|
+
for (const char of input) {
|
|
6912
|
+
if (char === '"') {
|
|
6913
|
+
inQuote = !inQuote;
|
|
6914
|
+
token += char;
|
|
6915
|
+
continue;
|
|
6916
|
+
}
|
|
6917
|
+
if (/\s/.test(char) && !inQuote) {
|
|
6918
|
+
if (token) {
|
|
6919
|
+
tokens.push(token);
|
|
6920
|
+
token = "";
|
|
7560
6921
|
}
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
}
|
|
6922
|
+
continue;
|
|
6923
|
+
}
|
|
6924
|
+
token += char;
|
|
6925
|
+
}
|
|
6926
|
+
if (token) {
|
|
6927
|
+
tokens.push(token);
|
|
6928
|
+
}
|
|
6929
|
+
return tokens;
|
|
7565
6930
|
}
|
|
7566
|
-
function
|
|
7567
|
-
|
|
7568
|
-
if (
|
|
7569
|
-
|
|
6931
|
+
function unwrapSearchValue(value) {
|
|
6932
|
+
const trimmed = value.trim();
|
|
6933
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
6934
|
+
return trimmed.slice(1, -1).trim();
|
|
6935
|
+
}
|
|
6936
|
+
return trimmed;
|
|
6937
|
+
}
|
|
6938
|
+
function parseCostQualifier(value, filters) {
|
|
6939
|
+
const raw = unwrapSearchValue(value);
|
|
6940
|
+
const range = raw.match(/^(\d+(?:\.\d+)?)\.\.(\d+(?:\.\d+)?)$/);
|
|
6941
|
+
if (range) {
|
|
6942
|
+
filters.costMin = Number(range[1]);
|
|
6943
|
+
filters.costMax = Number(range[2]);
|
|
7570
6944
|
return;
|
|
7571
6945
|
}
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
6946
|
+
const comparison = raw.match(/^(>=|>|<=|<)(\d+(?:\.\d+)?)$/);
|
|
6947
|
+
if (comparison) {
|
|
6948
|
+
const amount2 = Number(comparison[2]);
|
|
6949
|
+
if (comparison[1]?.includes(">")) {
|
|
6950
|
+
filters.costMin = amount2;
|
|
6951
|
+
filters.costMinExclusive = comparison[1] === ">";
|
|
6952
|
+
} else {
|
|
6953
|
+
filters.costMax = amount2;
|
|
6954
|
+
filters.costMaxExclusive = comparison[1] === "<";
|
|
6955
|
+
}
|
|
6956
|
+
return;
|
|
6957
|
+
}
|
|
6958
|
+
const amount = Number(raw);
|
|
6959
|
+
if (!Number.isNaN(amount)) {
|
|
6960
|
+
filters.costMin = amount;
|
|
6961
|
+
filters.costMax = amount;
|
|
6962
|
+
}
|
|
6963
|
+
}
|
|
6964
|
+
function appendUnique(values, value) {
|
|
6965
|
+
if (values?.includes(value)) return values;
|
|
6966
|
+
return [...values ?? [], value];
|
|
6967
|
+
}
|
|
6968
|
+
function isSmartTag(value) {
|
|
6969
|
+
return value === "bugfix" || value === "refactoring" || value === "feature-dev" || value === "testing" || value === "docs" || value === "git-ops" || value === "build-deploy" || value === "exploration" || value === "planning";
|
|
6970
|
+
}
|
|
6971
|
+
function parseSearchQuery(input) {
|
|
6972
|
+
const filters = {};
|
|
6973
|
+
const textTokens = [];
|
|
6974
|
+
let hasQualifiers = false;
|
|
6975
|
+
for (const token of splitSearchTokens(input)) {
|
|
6976
|
+
const match = token.match(/^([a-zA-Z][a-zA-Z_-]*):(.+)$/);
|
|
6977
|
+
if (!match) {
|
|
6978
|
+
textTokens.push(token);
|
|
7591
6979
|
continue;
|
|
7592
6980
|
}
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
6981
|
+
const key = match[1].toLowerCase();
|
|
6982
|
+
const value = unwrapSearchValue(match[2]);
|
|
6983
|
+
if (!value) continue;
|
|
6984
|
+
let consumed = true;
|
|
6985
|
+
if (key === "agent") filters.agent = value.toLowerCase();
|
|
6986
|
+
else if (key === "project") filters.project = value;
|
|
6987
|
+
else if (key === "projectkey" || key === "project-key") filters.projectKey = value;
|
|
6988
|
+
else if (key === "cwd") filters.cwd = value;
|
|
6989
|
+
else if (key === "tool") filters.tools = appendUnique(filters.tools, value.toLowerCase());
|
|
6990
|
+
else if (key === "file" || key === "path") filters.file = value;
|
|
6991
|
+
else if (key === "kind" || key === "filekind" || key === "file-kind") {
|
|
6992
|
+
if (value === "read" || value === "edit" || value === "write" || value === "delete") {
|
|
6993
|
+
filters.fileKind = value;
|
|
6994
|
+
} else {
|
|
6995
|
+
consumed = false;
|
|
6996
|
+
}
|
|
6997
|
+
} else if (key === "tag" || key === "signal") {
|
|
6998
|
+
const tag = value.toLowerCase();
|
|
6999
|
+
if (isSmartTag(tag)) {
|
|
7000
|
+
filters.tags = appendUnique(filters.tags, tag);
|
|
7001
|
+
} else {
|
|
7002
|
+
consumed = false;
|
|
7003
|
+
}
|
|
7004
|
+
} else if (key === "cost") {
|
|
7005
|
+
parseCostQualifier(value, filters);
|
|
7006
|
+
} else {
|
|
7007
|
+
consumed = false;
|
|
7008
|
+
}
|
|
7009
|
+
if (consumed) {
|
|
7010
|
+
hasQualifiers = true;
|
|
7011
|
+
} else {
|
|
7012
|
+
textTokens.push(token);
|
|
7596
7013
|
}
|
|
7597
7014
|
}
|
|
7015
|
+
return {
|
|
7016
|
+
text: textTokens.join(" ").trim(),
|
|
7017
|
+
filters,
|
|
7018
|
+
hasQualifiers
|
|
7019
|
+
};
|
|
7598
7020
|
}
|
|
7599
|
-
function
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
return {
|
|
7609
|
-
})
|
|
7610
|
-
|
|
7021
|
+
function toFtsQuery(input) {
|
|
7022
|
+
const tokens = splitSearchTokens(input);
|
|
7023
|
+
const mapped = tokens.map((token) => {
|
|
7024
|
+
if (/^OR$/i.test(token)) {
|
|
7025
|
+
return "OR";
|
|
7026
|
+
}
|
|
7027
|
+
if (token.startsWith('"') && token.endsWith('"')) {
|
|
7028
|
+
return `"${escapeFtsTerm(token.slice(1, -1))}"`;
|
|
7029
|
+
}
|
|
7030
|
+
return `"${escapeFtsTerm(token)}"`;
|
|
7031
|
+
}).filter(
|
|
7032
|
+
(token, index, values) => token !== "OR" || index > 0 && index < values.length - 1 && values[index - 1] !== "OR" && values[index + 1] !== "OR"
|
|
7033
|
+
);
|
|
7034
|
+
return mapped.join(" ");
|
|
7611
7035
|
}
|
|
7612
7036
|
function loadSearchIndexEntry(agentName, change, loadSessionData) {
|
|
7613
7037
|
try {
|
|
@@ -7943,17 +7367,6 @@ function sessionMatchesSearchCost(session, options) {
|
|
|
7943
7367
|
}
|
|
7944
7368
|
return true;
|
|
7945
7369
|
}
|
|
7946
|
-
function likePattern(value) {
|
|
7947
|
-
return `%${value.trim().toLowerCase().replace(/[\\%_]/g, "\\$&")}%`;
|
|
7948
|
-
}
|
|
7949
|
-
function filePathFtsQuery(value) {
|
|
7950
|
-
const path2 = normalizeFilePathSearch(value);
|
|
7951
|
-
if (path2.length < 3) return null;
|
|
7952
|
-
return `"${path2.replaceAll('"', '""')}"`;
|
|
7953
|
-
}
|
|
7954
|
-
function escapeRegExp(value) {
|
|
7955
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7956
|
-
}
|
|
7957
7370
|
function buildSessionSearchFilters(options) {
|
|
7958
7371
|
const clauses = [];
|
|
7959
7372
|
const params = [];
|
|
@@ -8081,307 +7494,656 @@ function buildTermSnippet(text, terms) {
|
|
|
8081
7494
|
const end = Math.min(text.length, index + term.length + 80);
|
|
8082
7495
|
return `${start > 0 ? "\u2026 " : ""}${highlightTerm(text.slice(start, end), term)}${end < text.length ? " \u2026" : ""}`;
|
|
8083
7496
|
}
|
|
8084
|
-
function messageMatchType(row) {
|
|
8085
|
-
if (row.role === "user") return "user_message";
|
|
8086
|
-
if (row.role === "tool" || row.mode === "tool" || row.tool_metadata_json) return "tool_output";
|
|
8087
|
-
return "assistant_reply";
|
|
7497
|
+
function messageMatchType(row) {
|
|
7498
|
+
if (row.role === "user") return "user_message";
|
|
7499
|
+
if (row.role === "tool" || row.mode === "tool" || row.tool_metadata_json) return "tool_output";
|
|
7500
|
+
return "assistant_reply";
|
|
7501
|
+
}
|
|
7502
|
+
function searchResultRowKey(row) {
|
|
7503
|
+
return `${String(row.agent_name)}\0${String(row.session_id)}`;
|
|
7504
|
+
}
|
|
7505
|
+
function fetchMessageSearchMatches(db, rows, ftsQuery, terms) {
|
|
7506
|
+
const candidates = rows.filter((row) => !textMatchesTerms(String(row.title ?? ""), terms));
|
|
7507
|
+
if (candidates.length === 0) {
|
|
7508
|
+
return /* @__PURE__ */ new Map();
|
|
7509
|
+
}
|
|
7510
|
+
const clauses = [];
|
|
7511
|
+
const params = [ftsQuery];
|
|
7512
|
+
for (const row of candidates) {
|
|
7513
|
+
clauses.push("(m.agent_name = ? AND m.session_id = ?)");
|
|
7514
|
+
params.push(String(row.agent_name), String(row.session_id));
|
|
7515
|
+
}
|
|
7516
|
+
const messageRows = db.prepare(
|
|
7517
|
+
`
|
|
7518
|
+
SELECT
|
|
7519
|
+
m.agent_name,
|
|
7520
|
+
m.session_id,
|
|
7521
|
+
m.message_index,
|
|
7522
|
+
m.role,
|
|
7523
|
+
m.mode,
|
|
7524
|
+
m.content_text,
|
|
7525
|
+
m.tool_metadata_json
|
|
7526
|
+
FROM messages_fts
|
|
7527
|
+
JOIN messages m ON m.rowid = messages_fts.rowid
|
|
7528
|
+
WHERE messages_fts MATCH ?
|
|
7529
|
+
AND (${clauses.join(" OR ")})
|
|
7530
|
+
ORDER BY m.message_index
|
|
7531
|
+
`
|
|
7532
|
+
).all(...params);
|
|
7533
|
+
const matches = /* @__PURE__ */ new Map();
|
|
7534
|
+
for (const message of messageRows) {
|
|
7535
|
+
const key = searchResultRowKey(message);
|
|
7536
|
+
if (matches.has(key)) continue;
|
|
7537
|
+
const text = String(message.content_text ?? "");
|
|
7538
|
+
if (!textMatchesTerms(text, terms)) continue;
|
|
7539
|
+
matches.set(key, {
|
|
7540
|
+
snippet: buildTermSnippet(text, terms),
|
|
7541
|
+
matchType: messageMatchType(message)
|
|
7542
|
+
});
|
|
7543
|
+
}
|
|
7544
|
+
return matches;
|
|
7545
|
+
}
|
|
7546
|
+
function resolveSearchMatch(row, terms, messageMatches) {
|
|
7547
|
+
const title = String(row.title ?? "");
|
|
7548
|
+
if (terms.terms.length === 0) {
|
|
7549
|
+
return {
|
|
7550
|
+
snippet: `Recent session \xB7 ${String(row.directory ?? "")}`,
|
|
7551
|
+
matchType: "recent"
|
|
7552
|
+
};
|
|
7553
|
+
}
|
|
7554
|
+
if (textMatchesTerms(title, terms)) {
|
|
7555
|
+
return { snippet: buildTermSnippet(title, terms), matchType: "title" };
|
|
7556
|
+
}
|
|
7557
|
+
const messageMatch = messageMatches.get(searchResultRowKey(row));
|
|
7558
|
+
if (messageMatch) {
|
|
7559
|
+
return messageMatch;
|
|
7560
|
+
}
|
|
7561
|
+
return {
|
|
7562
|
+
snippet: String(row.snippet ?? ""),
|
|
7563
|
+
matchType: "assistant_reply"
|
|
7564
|
+
};
|
|
7565
|
+
}
|
|
7566
|
+
function rowsToSearchResults(db, rows, textQuery, ftsQuery = toFtsQuery(textQuery)) {
|
|
7567
|
+
const terms = parseTextTerms(textQuery);
|
|
7568
|
+
const messageMatches = terms.terms.length > 0 && ftsQuery ? fetchMessageSearchMatches(db, rows, ftsQuery, terms) : /* @__PURE__ */ new Map();
|
|
7569
|
+
return rows.map((row) => {
|
|
7570
|
+
const match = resolveSearchMatch(row, terms, messageMatches);
|
|
7571
|
+
return {
|
|
7572
|
+
agentName: String(row.agent_name),
|
|
7573
|
+
session: sessionHeadFromSearchRow(row),
|
|
7574
|
+
snippet: match.snippet,
|
|
7575
|
+
matchType: match.matchType
|
|
7576
|
+
};
|
|
7577
|
+
});
|
|
7578
|
+
}
|
|
7579
|
+
function searchSessions(query, options = {}) {
|
|
7580
|
+
const search = mergeSearchQueryOptions(query, options);
|
|
7581
|
+
const normalizedQuery = search.text.trim();
|
|
7582
|
+
if (!hasCacheStorage()) {
|
|
7583
|
+
return [];
|
|
7584
|
+
}
|
|
7585
|
+
const results = withCacheDb((db) => {
|
|
7586
|
+
ensureFtsReady(db);
|
|
7587
|
+
const filters = buildSessionSearchFilters(search.options);
|
|
7588
|
+
if (!normalizedQuery) {
|
|
7589
|
+
const rows2 = db.prepare(
|
|
7590
|
+
`
|
|
7591
|
+
SELECT
|
|
7592
|
+
${searchSessionColumns()},
|
|
7593
|
+
'' AS snippet
|
|
7594
|
+
FROM sessions s
|
|
7595
|
+
WHERE 1 = 1
|
|
7596
|
+
${filters.where}
|
|
7597
|
+
ORDER BY s.activity_time DESC
|
|
7598
|
+
LIMIT ?
|
|
7599
|
+
`
|
|
7600
|
+
).all(...filters.params, search.options.limit ?? 50);
|
|
7601
|
+
return rowsToSearchResults(db, rows2, "");
|
|
7602
|
+
}
|
|
7603
|
+
const ftsQuery = toFtsQuery(normalizedQuery);
|
|
7604
|
+
if (!ftsQuery) return [];
|
|
7605
|
+
const rows = db.prepare(
|
|
7606
|
+
`
|
|
7607
|
+
SELECT
|
|
7608
|
+
${searchSessionColumns()},
|
|
7609
|
+
COALESCE(
|
|
7610
|
+
NULLIF(snippet(session_documents_fts, 1, '<mark>', '</mark>', ' \u2026 ', 18), ''),
|
|
7611
|
+
highlight(session_documents_fts, 0, '<mark>', '</mark>')
|
|
7612
|
+
) AS snippet
|
|
7613
|
+
FROM session_documents_fts
|
|
7614
|
+
JOIN session_documents d ON d.id = session_documents_fts.rowid
|
|
7615
|
+
JOIN sessions s ON s.agent_name = d.agent_name AND s.session_id = d.session_id
|
|
7616
|
+
WHERE session_documents_fts MATCH ?
|
|
7617
|
+
${filters.where}
|
|
7618
|
+
ORDER BY bm25(session_documents_fts, 8.0, 1.0), s.activity_time DESC
|
|
7619
|
+
LIMIT ?
|
|
7620
|
+
`
|
|
7621
|
+
).all(ftsQuery, ...filters.params, search.options.limit ?? 50);
|
|
7622
|
+
return rowsToSearchResults(db, rows, normalizedQuery, ftsQuery);
|
|
7623
|
+
});
|
|
7624
|
+
return results ?? [];
|
|
7625
|
+
}
|
|
7626
|
+
function fileActivityFilters(options) {
|
|
7627
|
+
const path2 = options.path ? normalizeFilePathSearch(options.path) : "";
|
|
7628
|
+
return {
|
|
7629
|
+
projectKey: options.projectKey ?? null,
|
|
7630
|
+
projectLike: options.project ? likePattern(options.project) : null,
|
|
7631
|
+
cwdKey: options.cwd ? computeIdentity(options.cwd, realFs).key : null,
|
|
7632
|
+
cwdLike: options.cwd ? likePattern(options.cwd) : null,
|
|
7633
|
+
path: path2,
|
|
7634
|
+
pathLike: path2 ? likePattern(path2) : null
|
|
7635
|
+
};
|
|
7636
|
+
}
|
|
7637
|
+
function fileActivityFromRow(row) {
|
|
7638
|
+
return {
|
|
7639
|
+
agent_name: String(row.agent_name),
|
|
7640
|
+
session_id: String(row.session_id),
|
|
7641
|
+
project_identity_key: String(row.project_identity_key ?? ""),
|
|
7642
|
+
path: String(row.path ?? ""),
|
|
7643
|
+
kind: row.kind ?? "read",
|
|
7644
|
+
count: Number(row.count ?? 0),
|
|
7645
|
+
latest_time: Number(row.latest_time ?? 0)
|
|
7646
|
+
};
|
|
7647
|
+
}
|
|
7648
|
+
function buildFileActivityWhere(options) {
|
|
7649
|
+
const filters = fileActivityFilters(options);
|
|
7650
|
+
const clauses = [];
|
|
7651
|
+
const params = [];
|
|
7652
|
+
if (options.agent != null) {
|
|
7653
|
+
clauses.push("fa.agent_name = ?");
|
|
7654
|
+
params.push(options.agent);
|
|
7655
|
+
}
|
|
7656
|
+
if (options.sessionId != null) {
|
|
7657
|
+
clauses.push("fa.session_id = ?");
|
|
7658
|
+
params.push(options.sessionId);
|
|
7659
|
+
}
|
|
7660
|
+
if (filters.projectKey != null) {
|
|
7661
|
+
clauses.push("fa.project_identity_key = ?");
|
|
7662
|
+
params.push(filters.projectKey);
|
|
7663
|
+
}
|
|
7664
|
+
if (filters.projectLike != null) {
|
|
7665
|
+
clauses.push(
|
|
7666
|
+
"(LOWER(fa.project_identity_key) LIKE ? ESCAPE '\\' OR LOWER(s.project_display_name) LIKE ? ESCAPE '\\' OR LOWER(s.directory) LIKE ? ESCAPE '\\')"
|
|
7667
|
+
);
|
|
7668
|
+
params.push(filters.projectLike, filters.projectLike, filters.projectLike);
|
|
7669
|
+
}
|
|
7670
|
+
if (filters.cwdKey != null) {
|
|
7671
|
+
clauses.push("(s.project_identity_key = ? OR LOWER(s.directory) LIKE ? ESCAPE '\\')");
|
|
7672
|
+
params.push(filters.cwdKey, filters.cwdLike);
|
|
7673
|
+
}
|
|
7674
|
+
if (filters.pathLike != null) {
|
|
7675
|
+
const pathQuery = filePathFtsQuery(filters.path);
|
|
7676
|
+
if (pathQuery) {
|
|
7677
|
+
clauses.push(
|
|
7678
|
+
"fa.rowid IN (SELECT rowid FROM session_file_activity_path_fts WHERE path MATCH ?)"
|
|
7679
|
+
);
|
|
7680
|
+
params.push(pathQuery);
|
|
7681
|
+
} else {
|
|
7682
|
+
clauses.push("LOWER(fa.path) LIKE ? ESCAPE '\\'");
|
|
7683
|
+
params.push(filters.pathLike);
|
|
7684
|
+
}
|
|
7685
|
+
}
|
|
7686
|
+
if (options.kind != null) {
|
|
7687
|
+
clauses.push("fa.kind = ?");
|
|
7688
|
+
params.push(options.kind);
|
|
7689
|
+
}
|
|
7690
|
+
if (options.from != null) {
|
|
7691
|
+
clauses.push("fa.latest_time >= ?");
|
|
7692
|
+
params.push(options.from);
|
|
7693
|
+
}
|
|
7694
|
+
if (options.to != null) {
|
|
7695
|
+
clauses.push("fa.latest_time <= ?");
|
|
7696
|
+
params.push(options.to);
|
|
7697
|
+
}
|
|
7698
|
+
return {
|
|
7699
|
+
where: clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "",
|
|
7700
|
+
params
|
|
7701
|
+
};
|
|
7702
|
+
}
|
|
7703
|
+
function listFileActivity(options = {}) {
|
|
7704
|
+
if (!hasCacheStorage()) {
|
|
7705
|
+
return [];
|
|
7706
|
+
}
|
|
7707
|
+
const filters = buildFileActivityWhere(options);
|
|
7708
|
+
const queryRows = (db) => db.prepare(
|
|
7709
|
+
`
|
|
7710
|
+
SELECT
|
|
7711
|
+
fa.agent_name,
|
|
7712
|
+
fa.session_id,
|
|
7713
|
+
fa.project_identity_key,
|
|
7714
|
+
fa.path,
|
|
7715
|
+
fa.kind,
|
|
7716
|
+
fa.count,
|
|
7717
|
+
fa.latest_time,
|
|
7718
|
+
s.slug,
|
|
7719
|
+
s.title,
|
|
7720
|
+
s.directory,
|
|
7721
|
+
s.project_identity_kind,
|
|
7722
|
+
s.project_display_name,
|
|
7723
|
+
s.time_created,
|
|
7724
|
+
s.time_updated,
|
|
7725
|
+
s.message_count,
|
|
7726
|
+
s.total_input_tokens,
|
|
7727
|
+
s.total_output_tokens,
|
|
7728
|
+
s.total_cache_read_tokens,
|
|
7729
|
+
s.total_cache_create_tokens,
|
|
7730
|
+
s.total_cost,
|
|
7731
|
+
s.cost_source,
|
|
7732
|
+
s.total_tokens
|
|
7733
|
+
FROM session_file_activity fa
|
|
7734
|
+
JOIN sessions s ON s.agent_name = fa.agent_name AND s.session_id = fa.session_id
|
|
7735
|
+
${filters.where}
|
|
7736
|
+
ORDER BY fa.latest_time DESC, fa.count DESC, fa.path
|
|
7737
|
+
LIMIT ?
|
|
7738
|
+
`
|
|
7739
|
+
).all(...filters.params, options.limit ?? 50);
|
|
7740
|
+
let rows = withCacheDbReadOnly(queryRows);
|
|
7741
|
+
if (rows == null && options.path) {
|
|
7742
|
+
rows = withCacheDb(queryRows);
|
|
7743
|
+
}
|
|
7744
|
+
return (rows ?? []).map((row) => ({
|
|
7745
|
+
...fileActivityFromRow(row),
|
|
7746
|
+
session: sessionHeadFromSearchRow(row)
|
|
7747
|
+
}));
|
|
7748
|
+
}
|
|
7749
|
+
function listSessionFileActivity(agentName, sessionId) {
|
|
7750
|
+
return listFileActivity({ agent: agentName, sessionId, limit: 500 }).map(
|
|
7751
|
+
({ session: _session, ...activity }) => activity
|
|
7752
|
+
);
|
|
8088
7753
|
}
|
|
8089
|
-
function
|
|
8090
|
-
|
|
7754
|
+
function highlightFilePath(path2, query) {
|
|
7755
|
+
const needle = normalizeFilePathSearch(query);
|
|
7756
|
+
if (!needle) return path2;
|
|
7757
|
+
const lower = path2.toLowerCase();
|
|
7758
|
+
const index = lower.indexOf(needle.toLowerCase());
|
|
7759
|
+
if (index < 0) return path2;
|
|
7760
|
+
return `${path2.slice(0, index)}<mark>${path2.slice(index, index + needle.length)}</mark>${path2.slice(
|
|
7761
|
+
index + needle.length
|
|
7762
|
+
)}`;
|
|
8091
7763
|
}
|
|
8092
|
-
function
|
|
8093
|
-
const
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8104
|
-
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
).all(...params);
|
|
8120
|
-
const matches = /* @__PURE__ */ new Map();
|
|
8121
|
-
for (const message of messageRows) {
|
|
8122
|
-
const key = searchResultRowKey(message);
|
|
8123
|
-
if (matches.has(key)) continue;
|
|
8124
|
-
const text = String(message.content_text ?? "");
|
|
8125
|
-
if (!textMatchesTerms(text, terms)) continue;
|
|
8126
|
-
matches.set(key, {
|
|
8127
|
-
snippet: buildTermSnippet(text, terms),
|
|
8128
|
-
matchType: messageMatchType(message)
|
|
7764
|
+
function searchFileActivitySessions(query, options = {}) {
|
|
7765
|
+
const search = mergeSearchQueryOptions(query, options);
|
|
7766
|
+
const path2 = normalizeFilePathSearch(search.options.file ?? search.text);
|
|
7767
|
+
if (!path2) return [];
|
|
7768
|
+
const rows = listFileActivity({
|
|
7769
|
+
agent: search.options.agent,
|
|
7770
|
+
projectKey: search.options.projectKey,
|
|
7771
|
+
project: search.options.project,
|
|
7772
|
+
cwd: search.options.cwd,
|
|
7773
|
+
path: path2,
|
|
7774
|
+
kind: search.options.fileKind,
|
|
7775
|
+
from: search.options.from,
|
|
7776
|
+
to: search.options.to,
|
|
7777
|
+
limit: (search.options.limit ?? 50) * 3
|
|
7778
|
+
});
|
|
7779
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7780
|
+
const results = [];
|
|
7781
|
+
for (const row of rows) {
|
|
7782
|
+
const key = `${row.agent_name}/${row.session_id}`;
|
|
7783
|
+
if (seen.has(key)) continue;
|
|
7784
|
+
if (!sessionMatchesSearchCost(row.session, search.options)) continue;
|
|
7785
|
+
seen.add(key);
|
|
7786
|
+
results.push({
|
|
7787
|
+
agentName: row.agent_name,
|
|
7788
|
+
session: row.session,
|
|
7789
|
+
snippet: `${row.kind} ${highlightFilePath(row.path, path2)} \xB7 ${row.count} events`,
|
|
7790
|
+
matchType: "file_path"
|
|
8129
7791
|
});
|
|
7792
|
+
if (results.length >= (search.options.limit ?? 50)) break;
|
|
8130
7793
|
}
|
|
8131
|
-
return
|
|
7794
|
+
return results;
|
|
8132
7795
|
}
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
matchType: "recent"
|
|
8139
|
-
};
|
|
7796
|
+
var CACHE_INITIALIZATION_VERSION = "session-cache-v2";
|
|
7797
|
+
function deleteLegacyCacheFile() {
|
|
7798
|
+
const legacyPath = getLegacyCachePath();
|
|
7799
|
+
if (!existsSync12(legacyPath)) {
|
|
7800
|
+
return;
|
|
8140
7801
|
}
|
|
8141
|
-
|
|
8142
|
-
|
|
7802
|
+
try {
|
|
7803
|
+
unlinkSync(legacyPath);
|
|
7804
|
+
} catch {
|
|
8143
7805
|
}
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
7806
|
+
}
|
|
7807
|
+
function loadCachedSessions(agentName) {
|
|
7808
|
+
if (!hasCacheStorage()) {
|
|
7809
|
+
return null;
|
|
8147
7810
|
}
|
|
8148
|
-
return {
|
|
8149
|
-
|
|
8150
|
-
|
|
8151
|
-
|
|
7811
|
+
return withCacheDb((db) => {
|
|
7812
|
+
const timestampRow = db.prepare("SELECT timestamp AS value FROM agent_cache WHERE agent_name = ?").get(agentName);
|
|
7813
|
+
const timestamp = Number(timestampRow?.value ?? 0);
|
|
7814
|
+
if (!timestamp) {
|
|
7815
|
+
return null;
|
|
7816
|
+
}
|
|
7817
|
+
const rows = db.prepare(
|
|
7818
|
+
`
|
|
7819
|
+
SELECT
|
|
7820
|
+
session_id,
|
|
7821
|
+
sort_index,
|
|
7822
|
+
slug,
|
|
7823
|
+
title,
|
|
7824
|
+
source_path,
|
|
7825
|
+
directory,
|
|
7826
|
+
project_identity_kind,
|
|
7827
|
+
project_identity_key,
|
|
7828
|
+
project_display_name,
|
|
7829
|
+
time_created,
|
|
7830
|
+
time_updated,
|
|
7831
|
+
message_count,
|
|
7832
|
+
total_input_tokens,
|
|
7833
|
+
total_output_tokens,
|
|
7834
|
+
total_cache_read_tokens,
|
|
7835
|
+
total_cache_create_tokens,
|
|
7836
|
+
total_cost,
|
|
7837
|
+
cost_source,
|
|
7838
|
+
total_tokens,
|
|
7839
|
+
model_usage_json,
|
|
7840
|
+
smart_tags_json,
|
|
7841
|
+
smart_tags_source_updated_at,
|
|
7842
|
+
meta_json
|
|
7843
|
+
FROM sessions
|
|
7844
|
+
WHERE agent_name = ?
|
|
7845
|
+
ORDER BY sort_index, activity_time DESC
|
|
7846
|
+
`
|
|
7847
|
+
).all(agentName);
|
|
7848
|
+
const sessions = [];
|
|
7849
|
+
const meta = {};
|
|
7850
|
+
for (const row of rows) {
|
|
7851
|
+
const session = sessionFromRow(row);
|
|
7852
|
+
sessions.push(session);
|
|
7853
|
+
if (row.meta_json) {
|
|
7854
|
+
meta[session.id] = JSON.parse(row.meta_json);
|
|
7855
|
+
}
|
|
7856
|
+
}
|
|
7857
|
+
return { sessions, meta, timestamp };
|
|
7858
|
+
});
|
|
8152
7859
|
}
|
|
8153
|
-
function
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
return
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
7860
|
+
function isAgentCacheInitialized(agentName, indexVersion = CACHE_INITIALIZATION_VERSION) {
|
|
7861
|
+
if (!hasCacheStorage()) {
|
|
7862
|
+
return false;
|
|
7863
|
+
}
|
|
7864
|
+
return withCacheDbReadOnly((db) => {
|
|
7865
|
+
if (!tableExists(db, "cache_initialization")) return false;
|
|
7866
|
+
const row = db.prepare(
|
|
7867
|
+
`
|
|
7868
|
+
SELECT index_version
|
|
7869
|
+
FROM cache_initialization
|
|
7870
|
+
WHERE agent_name = ?
|
|
7871
|
+
`
|
|
7872
|
+
).get(agentName);
|
|
7873
|
+
return row?.index_version === indexVersion;
|
|
7874
|
+
}) ?? false;
|
|
7875
|
+
}
|
|
7876
|
+
function markAgentCacheInitialized(agentName, indexVersion = CACHE_INITIALIZATION_VERSION) {
|
|
7877
|
+
withCacheDb((db) => {
|
|
7878
|
+
const now = Date.now();
|
|
7879
|
+
db.prepare(
|
|
7880
|
+
`
|
|
7881
|
+
INSERT INTO cache_initialization(agent_name, initialized_at, index_version, last_sync_at)
|
|
7882
|
+
VALUES (?, ?, ?, ?)
|
|
7883
|
+
ON CONFLICT(agent_name) DO UPDATE SET
|
|
7884
|
+
index_version = excluded.index_version,
|
|
7885
|
+
last_sync_at = excluded.last_sync_at
|
|
7886
|
+
`
|
|
7887
|
+
).run(agentName, now, indexVersion, now);
|
|
8164
7888
|
});
|
|
8165
7889
|
}
|
|
8166
|
-
function
|
|
8167
|
-
const search = mergeSearchQueryOptions(query, options);
|
|
8168
|
-
const normalizedQuery = search.text.trim();
|
|
7890
|
+
function loadCachedSessionData(agentName, sessionId) {
|
|
8169
7891
|
if (!hasCacheStorage()) {
|
|
8170
|
-
return
|
|
7892
|
+
return null;
|
|
8171
7893
|
}
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
|
|
7894
|
+
return withCacheDbReadOnly((db) => {
|
|
7895
|
+
const row = db.prepare(
|
|
7896
|
+
`
|
|
7897
|
+
SELECT
|
|
7898
|
+
session_id,
|
|
7899
|
+
sort_index,
|
|
7900
|
+
slug,
|
|
7901
|
+
title,
|
|
7902
|
+
source_path,
|
|
7903
|
+
directory,
|
|
7904
|
+
project_identity_kind,
|
|
7905
|
+
project_identity_key,
|
|
7906
|
+
project_display_name,
|
|
7907
|
+
time_created,
|
|
7908
|
+
time_updated,
|
|
7909
|
+
message_count,
|
|
7910
|
+
total_input_tokens,
|
|
7911
|
+
total_output_tokens,
|
|
7912
|
+
total_cache_read_tokens,
|
|
7913
|
+
total_cache_create_tokens,
|
|
7914
|
+
total_cost,
|
|
7915
|
+
cost_source,
|
|
7916
|
+
total_tokens,
|
|
7917
|
+
model_usage_json,
|
|
7918
|
+
smart_tags_json,
|
|
7919
|
+
smart_tags_source_updated_at,
|
|
7920
|
+
meta_json
|
|
7921
|
+
FROM sessions
|
|
7922
|
+
WHERE agent_name = ? AND session_id = ?
|
|
8177
7923
|
`
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
FROM sessions s
|
|
8182
|
-
WHERE 1 = 1
|
|
8183
|
-
${filters.where}
|
|
8184
|
-
ORDER BY s.activity_time DESC
|
|
8185
|
-
LIMIT ?
|
|
8186
|
-
`
|
|
8187
|
-
).all(...filters.params, search.options.limit ?? 50);
|
|
8188
|
-
return rowsToSearchResults(db, rows2, "");
|
|
7924
|
+
).get(agentName, sessionId);
|
|
7925
|
+
if (!row) {
|
|
7926
|
+
return null;
|
|
8189
7927
|
}
|
|
8190
|
-
const
|
|
8191
|
-
if (!ftsQuery) return [];
|
|
8192
|
-
const rows = db.prepare(
|
|
7928
|
+
const messageRows = db.prepare(
|
|
8193
7929
|
`
|
|
8194
7930
|
SELECT
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
7931
|
+
message_id,
|
|
7932
|
+
role,
|
|
7933
|
+
time_created,
|
|
7934
|
+
time_completed,
|
|
7935
|
+
agent,
|
|
7936
|
+
mode,
|
|
7937
|
+
model,
|
|
7938
|
+
provider,
|
|
7939
|
+
tokens_json,
|
|
7940
|
+
cost,
|
|
7941
|
+
cost_source,
|
|
7942
|
+
parts_json,
|
|
7943
|
+
subagent_id,
|
|
7944
|
+
nickname
|
|
7945
|
+
FROM messages
|
|
7946
|
+
WHERE agent_name = ? AND session_id = ?
|
|
7947
|
+
ORDER BY message_index
|
|
8207
7948
|
`
|
|
8208
|
-
).all(
|
|
8209
|
-
|
|
7949
|
+
).all(agentName, sessionId);
|
|
7950
|
+
const head = sessionFromRow(row);
|
|
7951
|
+
const fileActivityRows = db.prepare(
|
|
7952
|
+
`
|
|
7953
|
+
SELECT agent_name, session_id, project_identity_key, path, kind, count, latest_time
|
|
7954
|
+
FROM session_file_activity
|
|
7955
|
+
WHERE agent_name = ? AND session_id = ?
|
|
7956
|
+
ORDER BY latest_time DESC, count DESC, path
|
|
7957
|
+
LIMIT 500
|
|
7958
|
+
`
|
|
7959
|
+
).all(agentName, sessionId);
|
|
7960
|
+
return {
|
|
7961
|
+
...head,
|
|
7962
|
+
messages: messageRows.map((messageRow) => messageFromCachedRow(messageRow)),
|
|
7963
|
+
file_activity: fileActivityRows.map((activityRow) => fileActivityFromRow(activityRow))
|
|
7964
|
+
};
|
|
8210
7965
|
});
|
|
8211
|
-
return results ?? [];
|
|
8212
|
-
}
|
|
8213
|
-
function normalizeFilePathSearch(value) {
|
|
8214
|
-
return value.trim().replace(/^"|"$/g, "");
|
|
8215
|
-
}
|
|
8216
|
-
function fileActivityFilters(options) {
|
|
8217
|
-
const path2 = options.path ? normalizeFilePathSearch(options.path) : "";
|
|
8218
|
-
return {
|
|
8219
|
-
projectKey: options.projectKey ?? null,
|
|
8220
|
-
projectLike: options.project ? likePattern(options.project) : null,
|
|
8221
|
-
cwdKey: options.cwd ? computeIdentity(options.cwd, realFs).key : null,
|
|
8222
|
-
cwdLike: options.cwd ? likePattern(options.cwd) : null,
|
|
8223
|
-
path: path2,
|
|
8224
|
-
pathLike: path2 ? likePattern(path2) : null
|
|
8225
|
-
};
|
|
8226
7966
|
}
|
|
8227
|
-
function
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
7967
|
+
function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
7968
|
+
withCacheDb((db) => {
|
|
7969
|
+
const deleteAgent = db.prepare("DELETE FROM agent_cache WHERE agent_name = ?");
|
|
7970
|
+
const deleteLegacySessions = db.prepare("DELETE FROM cached_sessions WHERE agent_name = ?");
|
|
7971
|
+
const deleteSession = db.prepare(
|
|
7972
|
+
"DELETE FROM sessions WHERE agent_name = ? AND session_id = ?"
|
|
7973
|
+
);
|
|
7974
|
+
const deleteSearchDocument = db.prepare(
|
|
7975
|
+
"DELETE FROM session_documents WHERE agent_name = ? AND session_id = ?"
|
|
7976
|
+
);
|
|
7977
|
+
const deleteMessages = db.prepare(
|
|
7978
|
+
"DELETE FROM messages WHERE agent_name = ? AND session_id = ?"
|
|
7979
|
+
);
|
|
7980
|
+
const deleteMessageTools = db.prepare(
|
|
7981
|
+
"DELETE FROM message_tools WHERE agent_name = ? AND session_id = ?"
|
|
7982
|
+
);
|
|
7983
|
+
const deleteFileActivity = db.prepare(
|
|
7984
|
+
"DELETE FROM session_file_activity WHERE agent_name = ? AND session_id = ?"
|
|
7985
|
+
);
|
|
7986
|
+
const deleteProjectSession = db.prepare(
|
|
7987
|
+
"DELETE FROM project_sessions WHERE agent_name = ? AND session_id = ?"
|
|
7988
|
+
);
|
|
7989
|
+
const deleteProjectSessions = db.prepare("DELETE FROM project_sessions WHERE agent_name = ?");
|
|
7990
|
+
const upsertAgent = db.prepare(`
|
|
7991
|
+
INSERT INTO agent_cache(agent_name, timestamp)
|
|
7992
|
+
VALUES (?, ?)
|
|
7993
|
+
ON CONFLICT(agent_name) DO UPDATE SET timestamp = excluded.timestamp
|
|
7994
|
+
`);
|
|
7995
|
+
const upsertCachedSession = prepareUpsertCachedSession(db);
|
|
7996
|
+
const upsertSession = prepareUpsertSession(db);
|
|
7997
|
+
const upsertProjectSession = prepareUpsertProjectSession(db);
|
|
7998
|
+
const write = db.transaction(() => {
|
|
7999
|
+
const timestamp = Date.now();
|
|
8000
|
+
const sessionIds = new Set(sessions.map((session) => session.id));
|
|
8001
|
+
const existingSessionIds = db.prepare("SELECT session_id FROM sessions WHERE agent_name = ?").all(agentName);
|
|
8002
|
+
deleteAgent.run(agentName);
|
|
8003
|
+
deleteLegacySessions.run(agentName);
|
|
8004
|
+
deleteProjectSessions.run(agentName);
|
|
8005
|
+
upsertAgent.run(agentName, timestamp);
|
|
8006
|
+
for (const row of existingSessionIds) {
|
|
8007
|
+
const sessionId = String(row.session_id);
|
|
8008
|
+
if (!sessionIds.has(sessionId)) {
|
|
8009
|
+
deleteSearchDocument.run(agentName, sessionId);
|
|
8010
|
+
deleteMessageTools.run(agentName, sessionId);
|
|
8011
|
+
deleteMessages.run(agentName, sessionId);
|
|
8012
|
+
deleteFileActivity.run(agentName, sessionId);
|
|
8013
|
+
deleteProjectSession.run(agentName, sessionId);
|
|
8014
|
+
deleteSession.run(agentName, sessionId);
|
|
8015
|
+
}
|
|
8016
|
+
}
|
|
8017
|
+
sessions.forEach((session, index) => {
|
|
8018
|
+
const identity = session.project_identity ?? computeIdentity(session.directory, realFs);
|
|
8019
|
+
const sessionMeta = meta[session.id];
|
|
8020
|
+
const metaJson = sessionMeta ? JSON.stringify(sessionMeta) : null;
|
|
8021
|
+
upsertCachedSession.run(agentName, session.id, JSON.stringify(session), metaJson);
|
|
8022
|
+
upsertSessionRow(
|
|
8023
|
+
upsertSession,
|
|
8024
|
+
agentName,
|
|
8025
|
+
session,
|
|
8026
|
+
metaJson,
|
|
8027
|
+
index,
|
|
8028
|
+
sourcePathFromMeta(sessionMeta)
|
|
8029
|
+
);
|
|
8030
|
+
writeProjectSessionRow(upsertProjectSession, agentName, session, identity);
|
|
8031
|
+
});
|
|
8032
|
+
});
|
|
8033
|
+
write();
|
|
8034
|
+
deleteLegacyCacheFile();
|
|
8035
|
+
});
|
|
8237
8036
|
}
|
|
8238
|
-
function
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
const params = [];
|
|
8242
|
-
if (options.agent != null) {
|
|
8243
|
-
clauses.push("fa.agent_name = ?");
|
|
8244
|
-
params.push(options.agent);
|
|
8245
|
-
}
|
|
8246
|
-
if (options.sessionId != null) {
|
|
8247
|
-
clauses.push("fa.session_id = ?");
|
|
8248
|
-
params.push(options.sessionId);
|
|
8249
|
-
}
|
|
8250
|
-
if (filters.projectKey != null) {
|
|
8251
|
-
clauses.push("fa.project_identity_key = ?");
|
|
8252
|
-
params.push(filters.projectKey);
|
|
8037
|
+
function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta = {}) {
|
|
8038
|
+
if (changes.length === 0 && removedSessionIds.length === 0) {
|
|
8039
|
+
return;
|
|
8253
8040
|
}
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
"
|
|
8041
|
+
withCacheDb((db) => {
|
|
8042
|
+
const deleteLegacySession = db.prepare(
|
|
8043
|
+
"DELETE FROM cached_sessions WHERE agent_name = ? AND session_id = ?"
|
|
8257
8044
|
);
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8045
|
+
const deleteSession = db.prepare(
|
|
8046
|
+
"DELETE FROM sessions WHERE agent_name = ? AND session_id = ?"
|
|
8047
|
+
);
|
|
8048
|
+
const deleteSearchDocument = db.prepare(
|
|
8049
|
+
"DELETE FROM session_documents WHERE agent_name = ? AND session_id = ?"
|
|
8050
|
+
);
|
|
8051
|
+
const deleteMessages = db.prepare(
|
|
8052
|
+
"DELETE FROM messages WHERE agent_name = ? AND session_id = ?"
|
|
8053
|
+
);
|
|
8054
|
+
const deleteMessageTools = db.prepare(
|
|
8055
|
+
"DELETE FROM message_tools WHERE agent_name = ? AND session_id = ?"
|
|
8056
|
+
);
|
|
8057
|
+
const deleteFileActivity = db.prepare(
|
|
8058
|
+
"DELETE FROM session_file_activity WHERE agent_name = ? AND session_id = ?"
|
|
8059
|
+
);
|
|
8060
|
+
const deleteProjectSession = db.prepare(
|
|
8061
|
+
"DELETE FROM project_sessions WHERE agent_name = ? AND session_id = ?"
|
|
8062
|
+
);
|
|
8063
|
+
const upsertAgent = db.prepare(`
|
|
8064
|
+
INSERT INTO agent_cache(agent_name, timestamp)
|
|
8065
|
+
VALUES (?, ?)
|
|
8066
|
+
ON CONFLICT(agent_name) DO UPDATE SET timestamp = excluded.timestamp
|
|
8067
|
+
`);
|
|
8068
|
+
const upsertCachedSession = prepareUpsertCachedSession(db);
|
|
8069
|
+
const upsertSession = prepareUpsertSession(db);
|
|
8070
|
+
const upsertProjectSession = prepareUpsertProjectSession(db);
|
|
8071
|
+
const write = db.transaction(() => {
|
|
8072
|
+
upsertAgent.run(agentName, Date.now());
|
|
8073
|
+
for (const sessionId of new Set(removedSessionIds)) {
|
|
8074
|
+
deleteLegacySession.run(agentName, sessionId);
|
|
8075
|
+
deleteSearchDocument.run(agentName, sessionId);
|
|
8076
|
+
deleteMessageTools.run(agentName, sessionId);
|
|
8077
|
+
deleteMessages.run(agentName, sessionId);
|
|
8078
|
+
deleteFileActivity.run(agentName, sessionId);
|
|
8079
|
+
deleteProjectSession.run(agentName, sessionId);
|
|
8080
|
+
deleteSession.run(agentName, sessionId);
|
|
8081
|
+
}
|
|
8082
|
+
for (const { session, sortIndex } of changes) {
|
|
8083
|
+
const identity = session.project_identity ?? computeIdentity(session.directory, realFs);
|
|
8084
|
+
const sessionMeta = meta[session.id];
|
|
8085
|
+
const metaJson = sessionMeta ? JSON.stringify(sessionMeta) : null;
|
|
8086
|
+
upsertCachedSession.run(agentName, session.id, JSON.stringify(session), metaJson);
|
|
8087
|
+
upsertSessionRow(
|
|
8088
|
+
upsertSession,
|
|
8089
|
+
agentName,
|
|
8090
|
+
session,
|
|
8091
|
+
metaJson,
|
|
8092
|
+
sortIndex,
|
|
8093
|
+
sourcePathFromMeta(sessionMeta)
|
|
8094
|
+
);
|
|
8095
|
+
writeProjectSessionRow(upsertProjectSession, agentName, session, identity);
|
|
8096
|
+
}
|
|
8097
|
+
});
|
|
8098
|
+
write();
|
|
8099
|
+
deleteLegacyCacheFile();
|
|
8100
|
+
});
|
|
8292
8101
|
}
|
|
8293
|
-
function
|
|
8102
|
+
function clearCache() {
|
|
8103
|
+
setFtsIntegrityCheckedPath(null);
|
|
8294
8104
|
if (!hasCacheStorage()) {
|
|
8295
|
-
|
|
8105
|
+
deleteLegacyCacheFile();
|
|
8106
|
+
return;
|
|
8296
8107
|
}
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
s.total_tokens
|
|
8323
|
-
FROM session_file_activity fa
|
|
8324
|
-
JOIN sessions s ON s.agent_name = fa.agent_name AND s.session_id = fa.session_id
|
|
8325
|
-
${filters.where}
|
|
8326
|
-
ORDER BY fa.latest_time DESC, fa.count DESC, fa.path
|
|
8327
|
-
LIMIT ?
|
|
8328
|
-
`
|
|
8329
|
-
).all(...filters.params, options.limit ?? 50);
|
|
8330
|
-
let rows = withCacheDbReadOnly(queryRows);
|
|
8331
|
-
if (rows == null && options.path) {
|
|
8332
|
-
rows = withCacheDb(queryRows);
|
|
8108
|
+
withCacheDb((db) => {
|
|
8109
|
+
db.exec(`
|
|
8110
|
+
DELETE FROM agent_cache;
|
|
8111
|
+
DELETE FROM cache_initialization;
|
|
8112
|
+
DELETE FROM cached_sessions;
|
|
8113
|
+
DELETE FROM session_documents;
|
|
8114
|
+
DELETE FROM session_file_activity;
|
|
8115
|
+
DELETE FROM message_tools;
|
|
8116
|
+
DELETE FROM messages;
|
|
8117
|
+
DELETE FROM sessions;
|
|
8118
|
+
DELETE FROM project_sessions;
|
|
8119
|
+
`);
|
|
8120
|
+
});
|
|
8121
|
+
deleteLegacyCacheFile();
|
|
8122
|
+
const cachePath = getCachePath2();
|
|
8123
|
+
const walPath = `${cachePath}-wal`;
|
|
8124
|
+
const shmPath = `${cachePath}-shm`;
|
|
8125
|
+
for (const filePath of [walPath, shmPath]) {
|
|
8126
|
+
if (!existsSync12(filePath)) {
|
|
8127
|
+
continue;
|
|
8128
|
+
}
|
|
8129
|
+
try {
|
|
8130
|
+
rmSync(filePath, { force: true });
|
|
8131
|
+
} catch {
|
|
8132
|
+
}
|
|
8333
8133
|
}
|
|
8334
|
-
return (rows ?? []).map((row) => ({
|
|
8335
|
-
...fileActivityFromRow(row),
|
|
8336
|
-
session: sessionHeadFromSearchRow(row)
|
|
8337
|
-
}));
|
|
8338
|
-
}
|
|
8339
|
-
function listSessionFileActivity(agentName, sessionId) {
|
|
8340
|
-
return listFileActivity({ agent: agentName, sessionId, limit: 500 }).map(
|
|
8341
|
-
({ session: _session, ...activity }) => activity
|
|
8342
|
-
);
|
|
8343
|
-
}
|
|
8344
|
-
function highlightFilePath(path2, query) {
|
|
8345
|
-
const needle = normalizeFilePathSearch(query);
|
|
8346
|
-
if (!needle) return path2;
|
|
8347
|
-
const lower = path2.toLowerCase();
|
|
8348
|
-
const index = lower.indexOf(needle.toLowerCase());
|
|
8349
|
-
if (index < 0) return path2;
|
|
8350
|
-
return `${path2.slice(0, index)}<mark>${path2.slice(index, index + needle.length)}</mark>${path2.slice(
|
|
8351
|
-
index + needle.length
|
|
8352
|
-
)}`;
|
|
8353
8134
|
}
|
|
8354
|
-
function
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
if (!path2) return [];
|
|
8358
|
-
const rows = listFileActivity({
|
|
8359
|
-
agent: search.options.agent,
|
|
8360
|
-
projectKey: search.options.projectKey,
|
|
8361
|
-
project: search.options.project,
|
|
8362
|
-
cwd: search.options.cwd,
|
|
8363
|
-
path: path2,
|
|
8364
|
-
kind: search.options.fileKind,
|
|
8365
|
-
from: search.options.from,
|
|
8366
|
-
to: search.options.to,
|
|
8367
|
-
limit: (search.options.limit ?? 50) * 3
|
|
8368
|
-
});
|
|
8369
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8370
|
-
const results = [];
|
|
8371
|
-
for (const row of rows) {
|
|
8372
|
-
const key = `${row.agent_name}/${row.session_id}`;
|
|
8373
|
-
if (seen.has(key)) continue;
|
|
8374
|
-
if (!sessionMatchesSearchCost(row.session, search.options)) continue;
|
|
8375
|
-
seen.add(key);
|
|
8376
|
-
results.push({
|
|
8377
|
-
agentName: row.agent_name,
|
|
8378
|
-
session: row.session,
|
|
8379
|
-
snippet: `${row.kind} ${highlightFilePath(row.path, path2)} \xB7 ${row.count} events`,
|
|
8380
|
-
matchType: "file_path"
|
|
8381
|
-
});
|
|
8382
|
-
if (results.length >= (search.options.limit ?? 50)) break;
|
|
8135
|
+
function getCacheInfo() {
|
|
8136
|
+
if (!hasCacheStorage()) {
|
|
8137
|
+
return { lastScanTime: null, size: 0 };
|
|
8383
8138
|
}
|
|
8384
|
-
|
|
8139
|
+
const info = withCacheDb((db) => {
|
|
8140
|
+
const timestampRow = db.prepare("SELECT MAX(timestamp) AS value FROM agent_cache").get();
|
|
8141
|
+
const sizeRow = db.prepare("SELECT COUNT(*) AS value FROM sessions").get();
|
|
8142
|
+
const lastScanTime = Number(timestampRow?.value ?? 0) || null;
|
|
8143
|
+
const size = Number(sizeRow?.value ?? 0);
|
|
8144
|
+
return { lastScanTime, size };
|
|
8145
|
+
});
|
|
8146
|
+
return info ?? { lastScanTime: null, size: 0 };
|
|
8385
8147
|
}
|
|
8386
8148
|
function listCachedProjectGroups(sessions) {
|
|
8387
8149
|
if (sessions) {
|
|
@@ -8423,57 +8185,89 @@ function createIdentityResolver() {
|
|
|
8423
8185
|
return identity;
|
|
8424
8186
|
};
|
|
8425
8187
|
}
|
|
8426
|
-
function
|
|
8188
|
+
function attachMissingProjectIdentities(sessions) {
|
|
8427
8189
|
const resolveIdentity = createIdentityResolver();
|
|
8428
8190
|
return sessions.map((session) => {
|
|
8429
8191
|
if (session.project_identity) return session;
|
|
8430
|
-
return {
|
|
8431
|
-
...session,
|
|
8432
|
-
project_identity: resolveIdentity(session.directory)
|
|
8433
|
-
};
|
|
8192
|
+
return { ...session, project_identity: resolveIdentity(session.directory) };
|
|
8434
8193
|
});
|
|
8435
8194
|
}
|
|
8436
|
-
function
|
|
8437
|
-
let result = sessions;
|
|
8438
|
-
if (options.cwd) {
|
|
8439
|
-
result = filterSessionsByProjectScope(result, options.cwd);
|
|
8440
|
-
}
|
|
8441
|
-
if (options.from != null) {
|
|
8442
|
-
result = result.filter((s) => (s.time_updated ?? s.time_created) >= options.from);
|
|
8443
|
-
}
|
|
8444
|
-
if (options.to != null) {
|
|
8445
|
-
result = result.filter((s) => (s.time_updated ?? s.time_created) <= options.to);
|
|
8446
|
-
}
|
|
8447
|
-
return result;
|
|
8448
|
-
}
|
|
8449
|
-
function buildAgentCacheMeta(agent) {
|
|
8195
|
+
function buildAgentCacheMeta(agent, sessionIds) {
|
|
8450
8196
|
const metaMap = agent.getSessionMetaMap?.();
|
|
8451
8197
|
const meta = {};
|
|
8452
8198
|
if (!metaMap) return meta;
|
|
8453
8199
|
for (const [id, data] of metaMap.entries()) {
|
|
8200
|
+
if (sessionIds && !sessionIds.has(id)) continue;
|
|
8454
8201
|
meta[id] = { id, ...data };
|
|
8455
8202
|
}
|
|
8456
8203
|
return meta;
|
|
8457
8204
|
}
|
|
8458
|
-
function
|
|
8459
|
-
return JSON.stringify(
|
|
8205
|
+
function sessionSignature(session) {
|
|
8206
|
+
return JSON.stringify([
|
|
8207
|
+
session.title,
|
|
8208
|
+
session.directory,
|
|
8209
|
+
session.time_created,
|
|
8210
|
+
session.time_updated ?? session.time_created,
|
|
8211
|
+
session.stats.message_count,
|
|
8212
|
+
session.stats.total_input_tokens,
|
|
8213
|
+
session.stats.total_output_tokens,
|
|
8214
|
+
session.stats.total_cost,
|
|
8215
|
+
session.stats.total_tokens ?? 0,
|
|
8216
|
+
session.smart_tags_source_updated_at ?? null
|
|
8217
|
+
]);
|
|
8218
|
+
}
|
|
8219
|
+
function sortSessions(sessions) {
|
|
8220
|
+
return [...sessions].sort(
|
|
8221
|
+
(a, b) => (b.time_updated ?? b.time_created) - (a.time_updated ?? a.time_created)
|
|
8222
|
+
);
|
|
8460
8223
|
}
|
|
8461
|
-
function
|
|
8224
|
+
function computeSessionDiff(cachedSessions, updatedSessions, changedIds = [], signature = sessionSignature) {
|
|
8462
8225
|
const cachedMap = new Map(cachedSessions.map((session) => [session.id, session]));
|
|
8463
8226
|
const updatedIds = new Set(updatedSessions.map((session) => session.id));
|
|
8464
8227
|
const changedIdSet = new Set(changedIds);
|
|
8465
|
-
const removedSessionIds = cachedSessions.filter((session) => !updatedIds.has(session.id)).map((session) => session.id);
|
|
8466
8228
|
const changes = [];
|
|
8229
|
+
const removedSessionIds = [];
|
|
8230
|
+
let newCount = 0;
|
|
8231
|
+
let updatedCount = 0;
|
|
8467
8232
|
updatedSessions.forEach((session, sortIndex) => {
|
|
8468
8233
|
const cached = cachedMap.get(session.id);
|
|
8469
|
-
if (!cached
|
|
8234
|
+
if (!cached) {
|
|
8235
|
+
newCount += 1;
|
|
8236
|
+
changes.push({ session, sortIndex });
|
|
8237
|
+
return;
|
|
8238
|
+
}
|
|
8239
|
+
const hasSignatureChange = signature(cached) !== signature(session);
|
|
8240
|
+
if (changedIdSet.has(session.id) || hasSignatureChange) {
|
|
8241
|
+
updatedCount += 1;
|
|
8470
8242
|
changes.push({ session, sortIndex });
|
|
8471
8243
|
}
|
|
8472
8244
|
});
|
|
8473
|
-
|
|
8245
|
+
for (const session of cachedSessions) {
|
|
8246
|
+
if (!updatedIds.has(session.id)) {
|
|
8247
|
+
removedSessionIds.push(session.id);
|
|
8248
|
+
}
|
|
8249
|
+
}
|
|
8250
|
+
return {
|
|
8251
|
+
changes,
|
|
8252
|
+
removedSessionIds,
|
|
8253
|
+
counts: { new: newCount, updated: updatedCount, removed: removedSessionIds.length }
|
|
8254
|
+
};
|
|
8255
|
+
}
|
|
8256
|
+
function filterSessions(sessions, options) {
|
|
8257
|
+
let result = sessions;
|
|
8258
|
+
if (options.cwd) {
|
|
8259
|
+
result = filterSessionsByProjectScope(result, options.cwd);
|
|
8260
|
+
}
|
|
8261
|
+
if (options.from != null) {
|
|
8262
|
+
result = result.filter((s) => (s.time_updated ?? s.time_created) >= options.from);
|
|
8263
|
+
}
|
|
8264
|
+
if (options.to != null) {
|
|
8265
|
+
result = result.filter((s) => (s.time_updated ?? s.time_created) <= options.to);
|
|
8266
|
+
}
|
|
8267
|
+
return result;
|
|
8474
8268
|
}
|
|
8475
8269
|
function saveCachedSessionDiff(agent, cachedSessions, updatedSessions, changedIds = []) {
|
|
8476
|
-
const diff =
|
|
8270
|
+
const diff = computeSessionDiff(cachedSessions, updatedSessions, changedIds, sessionSignature);
|
|
8477
8271
|
saveCachedSessionChanges(
|
|
8478
8272
|
agent.name,
|
|
8479
8273
|
diff.changes,
|
|
@@ -8573,19 +8367,16 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
8573
8367
|
const agentStart = performance.now();
|
|
8574
8368
|
const timing = { total: 0 };
|
|
8575
8369
|
const useCache = options.useCache ?? true;
|
|
8576
|
-
const canValidateCache = Boolean(agent.checkForChanges && agent.incrementalScan);
|
|
8577
8370
|
if (useCache) {
|
|
8578
8371
|
const t0 = performance.now();
|
|
8579
8372
|
const cached = loadCachedSessions(agent.name);
|
|
8580
8373
|
timing.cacheLoad = performance.now() - t0;
|
|
8581
8374
|
if (cached !== null) {
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
metaMap.set(id, meta);
|
|
8586
|
-
}
|
|
8587
|
-
agent.setSessionMetaMap(metaMap);
|
|
8375
|
+
const metaMap = /* @__PURE__ */ new Map();
|
|
8376
|
+
for (const [id, meta] of Object.entries(cached.meta)) {
|
|
8377
|
+
metaMap.set(id, meta);
|
|
8588
8378
|
}
|
|
8379
|
+
agent.setSessionMetaMap(metaMap);
|
|
8589
8380
|
if (options.cacheOnly) {
|
|
8590
8381
|
onProgress?.({
|
|
8591
8382
|
agent: agent.name,
|
|
@@ -8594,7 +8385,7 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
8594
8385
|
});
|
|
8595
8386
|
onProgress?.({ agent: agent.name, phase: "complete", newCount: cached.sessions.length });
|
|
8596
8387
|
const t32 = performance.now();
|
|
8597
|
-
const cachedWithIdentity2 =
|
|
8388
|
+
const cachedWithIdentity2 = attachMissingProjectIdentities(cached.sessions);
|
|
8598
8389
|
timing.identity = performance.now() - t32;
|
|
8599
8390
|
const filtered3 = filterSessions(cachedWithIdentity2, options);
|
|
8600
8391
|
timing.total = performance.now() - agentStart;
|
|
@@ -8615,58 +8406,56 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
8615
8406
|
phase: "cache",
|
|
8616
8407
|
cachedCount: cached.sessions.length
|
|
8617
8408
|
});
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8409
|
+
onProgress?.({ agent: agent.name, phase: "checking" });
|
|
8410
|
+
const t1 = performance.now();
|
|
8411
|
+
const checkResult = await Promise.resolve(
|
|
8412
|
+
agent.checkForChanges(cached.timestamp, cached.sessions)
|
|
8413
|
+
);
|
|
8414
|
+
timing.checkChanges = performance.now() - t1;
|
|
8415
|
+
if (checkResult.hasChanges) {
|
|
8416
|
+
onProgress?.({
|
|
8417
|
+
agent: agent.name,
|
|
8418
|
+
phase: "incremental",
|
|
8419
|
+
changedCount: checkResult.changedIds?.length
|
|
8420
|
+
});
|
|
8421
|
+
const t2 = performance.now();
|
|
8422
|
+
const updatedSessions = await Promise.resolve(
|
|
8423
|
+
agent.incrementalScan(cached.sessions, checkResult.changedIds || [])
|
|
8623
8424
|
);
|
|
8624
|
-
timing.
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
agent.incrementalScan(cached.sessions, checkResult.changedIds || [])
|
|
8634
|
-
);
|
|
8635
|
-
timing.scan = performance.now() - t2;
|
|
8636
|
-
const t32 = performance.now();
|
|
8637
|
-
const sessionsWithIdentity = attachProjectIdentities(updatedSessions);
|
|
8638
|
-
timing.identity = performance.now() - t32;
|
|
8639
|
-
const t42 = performance.now();
|
|
8640
|
-
const tagged2 = options.includeSmartTags === false ? { sessions: sessionsWithIdentity, changed: false } : await ensureSessionTags(agent, sessionsWithIdentity, options.smartTagWorkerUrl);
|
|
8641
|
-
timing.tags = performance.now() - t42;
|
|
8642
|
-
if (options.writeCache !== false) {
|
|
8643
|
-
saveCachedSessionDiff(
|
|
8644
|
-
agent,
|
|
8645
|
-
cached.sessions,
|
|
8646
|
-
tagged2.sessions,
|
|
8647
|
-
checkResult.changedIds ?? []
|
|
8648
|
-
);
|
|
8649
|
-
}
|
|
8650
|
-
onProgress?.({
|
|
8651
|
-
agent: agent.name,
|
|
8652
|
-
phase: "complete",
|
|
8653
|
-
newCount: tagged2.sessions.length
|
|
8654
|
-
});
|
|
8655
|
-
const filtered3 = filterSessions(tagged2.sessions, options);
|
|
8656
|
-
timing.total = performance.now() - agentStart;
|
|
8657
|
-
return {
|
|
8425
|
+
timing.scan = performance.now() - t2;
|
|
8426
|
+
const t32 = performance.now();
|
|
8427
|
+
const sessionsWithIdentity = attachMissingProjectIdentities(updatedSessions);
|
|
8428
|
+
timing.identity = performance.now() - t32;
|
|
8429
|
+
const t42 = performance.now();
|
|
8430
|
+
const tagged2 = options.includeSmartTags === false ? { sessions: sessionsWithIdentity, changed: false } : await ensureSessionTags(agent, sessionsWithIdentity, options.smartTagWorkerUrl);
|
|
8431
|
+
timing.tags = performance.now() - t42;
|
|
8432
|
+
if (options.writeCache !== false) {
|
|
8433
|
+
saveCachedSessionDiff(
|
|
8658
8434
|
agent,
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
cacheTimestamp: checkResult.timestamp
|
|
8664
|
-
};
|
|
8435
|
+
cached.sessions,
|
|
8436
|
+
tagged2.sessions,
|
|
8437
|
+
checkResult.changedIds ?? []
|
|
8438
|
+
);
|
|
8665
8439
|
}
|
|
8666
|
-
onProgress?.({
|
|
8440
|
+
onProgress?.({
|
|
8441
|
+
agent: agent.name,
|
|
8442
|
+
phase: "complete",
|
|
8443
|
+
newCount: tagged2.sessions.length
|
|
8444
|
+
});
|
|
8445
|
+
const filtered3 = filterSessions(tagged2.sessions, options);
|
|
8446
|
+
timing.total = performance.now() - agentStart;
|
|
8447
|
+
return {
|
|
8448
|
+
agent,
|
|
8449
|
+
heads: filtered3,
|
|
8450
|
+
fromCache: true,
|
|
8451
|
+
refreshed: true,
|
|
8452
|
+
timing,
|
|
8453
|
+
cacheTimestamp: checkResult.timestamp
|
|
8454
|
+
};
|
|
8667
8455
|
}
|
|
8456
|
+
onProgress?.({ agent: agent.name, phase: "complete", newCount: cached.sessions.length });
|
|
8668
8457
|
const t3 = performance.now();
|
|
8669
|
-
const cachedWithIdentity =
|
|
8458
|
+
const cachedWithIdentity = attachMissingProjectIdentities(cached.sessions);
|
|
8670
8459
|
timing.identity = performance.now() - t3;
|
|
8671
8460
|
const t4 = performance.now();
|
|
8672
8461
|
const tagged = options.includeSmartTags === false ? { sessions: cachedWithIdentity, changed: false } : await ensureSessionTags(agent, cachedWithIdentity, options.smartTagWorkerUrl);
|
|
@@ -8718,7 +8507,7 @@ async function scanAgentFull(agent, options, onProgress, timing = { total: 0 },
|
|
|
8718
8507
|
perf.end(scanMarker);
|
|
8719
8508
|
timing.scan = performance.now() - t0;
|
|
8720
8509
|
const t1 = performance.now();
|
|
8721
|
-
const headsWithIdentity =
|
|
8510
|
+
const headsWithIdentity = attachMissingProjectIdentities(heads);
|
|
8722
8511
|
timing.identity = performance.now() - t1;
|
|
8723
8512
|
const t2 = performance.now();
|
|
8724
8513
|
const tagged = options.includeSmartTags === false ? { sessions: headsWithIdentity, changed: false } : await ensureSessionTags(agent, headsWithIdentity, options.smartTagWorkerUrl);
|
|
@@ -8795,16 +8584,16 @@ function getStateDir() {
|
|
|
8795
8584
|
}
|
|
8796
8585
|
const p = platform2();
|
|
8797
8586
|
if (p === "darwin") {
|
|
8798
|
-
return
|
|
8587
|
+
return join12(homedir5(), "Library", "Application Support", "codesesh");
|
|
8799
8588
|
}
|
|
8800
8589
|
if (p === "win32") {
|
|
8801
8590
|
const appData = process.env.APPDATA ?? process.env.LOCALAPPDATA;
|
|
8802
|
-
return
|
|
8591
|
+
return join12(appData ?? join12(homedir5(), "AppData", "Roaming"), "codesesh");
|
|
8803
8592
|
}
|
|
8804
|
-
return
|
|
8593
|
+
return join12(process.env.XDG_DATA_HOME ?? join12(homedir5(), ".local", "share"), "codesesh");
|
|
8805
8594
|
}
|
|
8806
8595
|
function getStateDbPath() {
|
|
8807
|
-
return
|
|
8596
|
+
return join12(getStateDir(), BOOKMARK_DB_FILENAME);
|
|
8808
8597
|
}
|
|
8809
8598
|
function useMemoryStateStore() {
|
|
8810
8599
|
return process.env.CODESESH_STATE_STORE === MEMORY_STATE_STORE;
|
|
@@ -9090,6 +8879,161 @@ function deleteBookmark(agentKey, sessionId) {
|
|
|
9090
8879
|
).run(agentKey, sessionId);
|
|
9091
8880
|
});
|
|
9092
8881
|
}
|
|
8882
|
+
var DASHBOARD_RECENT_LIMIT = 10;
|
|
8883
|
+
function getTotalTokens(stats) {
|
|
8884
|
+
return stats.total_tokens ?? stats.total_input_tokens + stats.total_output_tokens;
|
|
8885
|
+
}
|
|
8886
|
+
function getSessionAgentName(session) {
|
|
8887
|
+
return session.slug.split("/")[0]?.toLowerCase() || "unknown";
|
|
8888
|
+
}
|
|
8889
|
+
function getSessionActivityTime(session) {
|
|
8890
|
+
return session.time_updated ?? session.time_created;
|
|
8891
|
+
}
|
|
8892
|
+
function toLocalDateKey(ts) {
|
|
8893
|
+
const d = new Date(ts);
|
|
8894
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(
|
|
8895
|
+
d.getDate()
|
|
8896
|
+
).padStart(2, "0")}`;
|
|
8897
|
+
}
|
|
8898
|
+
function startOfLocalDay(ts) {
|
|
8899
|
+
const d = new Date(ts);
|
|
8900
|
+
d.setHours(0, 0, 0, 0);
|
|
8901
|
+
return d.getTime();
|
|
8902
|
+
}
|
|
8903
|
+
function buildDashboard(sessions, options) {
|
|
8904
|
+
const { byAgentNames, scope, from, to, agentInfoMap } = options;
|
|
8905
|
+
const agentMetrics = /* @__PURE__ */ new Map();
|
|
8906
|
+
const agentMetricKeyByName = /* @__PURE__ */ new Map();
|
|
8907
|
+
for (const name of byAgentNames) {
|
|
8908
|
+
if (scope.agent && name.toLowerCase() !== scope.agent) continue;
|
|
8909
|
+
agentMetrics.set(name, { sessions: 0, messages: 0, tokens: 0 });
|
|
8910
|
+
agentMetricKeyByName.set(name.toLowerCase(), name);
|
|
8911
|
+
}
|
|
8912
|
+
let totalSessions = 0;
|
|
8913
|
+
let totalMessages = 0;
|
|
8914
|
+
let totalTokens = 0;
|
|
8915
|
+
let totalCost = 0;
|
|
8916
|
+
let hasEstimatedCost = false;
|
|
8917
|
+
let latestActivity = 0;
|
|
8918
|
+
const recentCandidates = [];
|
|
8919
|
+
const modelAgg = /* @__PURE__ */ new Map();
|
|
8920
|
+
const dailyMap = /* @__PURE__ */ new Map();
|
|
8921
|
+
const dailyTokenMap = /* @__PURE__ */ new Map();
|
|
8922
|
+
if (from != null) {
|
|
8923
|
+
const bucketStart = startOfLocalDay(from);
|
|
8924
|
+
const bucketDays = Math.floor((startOfLocalDay(to) - bucketStart) / 864e5) + 1;
|
|
8925
|
+
for (let i = 0; i < bucketDays; i += 1) {
|
|
8926
|
+
const ts = bucketStart + i * 864e5;
|
|
8927
|
+
const key = toLocalDateKey(ts);
|
|
8928
|
+
dailyMap.set(key, { date: key, sessions: 0, messages: 0 });
|
|
8929
|
+
dailyTokenMap.set(key, { date: key, input: 0, output: 0, cache_read: 0, cache_create: 0 });
|
|
8930
|
+
}
|
|
8931
|
+
}
|
|
8932
|
+
for (const session of sessions) {
|
|
8933
|
+
const agentName = getSessionAgentName(session);
|
|
8934
|
+
if (scope.agent && agentName !== scope.agent) continue;
|
|
8935
|
+
if (scope.projectKey) {
|
|
8936
|
+
const identity = session.project_identity;
|
|
8937
|
+
if (!identity || identity.key !== scope.projectKey) continue;
|
|
8938
|
+
if (scope.projectKind && identity.kind !== scope.projectKind) continue;
|
|
8939
|
+
}
|
|
8940
|
+
const activity = getSessionActivityTime(session);
|
|
8941
|
+
if (from != null && activity < from) continue;
|
|
8942
|
+
if (activity > to) continue;
|
|
8943
|
+
const messageCount = session.stats.message_count;
|
|
8944
|
+
const sessionTokens = getTotalTokens(session.stats);
|
|
8945
|
+
totalSessions += 1;
|
|
8946
|
+
totalMessages += messageCount;
|
|
8947
|
+
totalTokens += sessionTokens;
|
|
8948
|
+
totalCost += session.stats.total_cost ?? 0;
|
|
8949
|
+
if (session.stats.cost_source === "estimated") hasEstimatedCost = true;
|
|
8950
|
+
if (activity > latestActivity) latestActivity = activity;
|
|
8951
|
+
const metricKey = agentMetricKeyByName.get(agentName);
|
|
8952
|
+
if (metricKey) {
|
|
8953
|
+
const metric = agentMetrics.get(metricKey);
|
|
8954
|
+
metric.sessions += 1;
|
|
8955
|
+
metric.messages += messageCount;
|
|
8956
|
+
metric.tokens += sessionTokens;
|
|
8957
|
+
}
|
|
8958
|
+
const key = toLocalDateKey(activity);
|
|
8959
|
+
let bucket = dailyMap.get(key);
|
|
8960
|
+
if (!bucket) {
|
|
8961
|
+
bucket = { date: key, sessions: 0, messages: 0 };
|
|
8962
|
+
dailyMap.set(key, bucket);
|
|
8963
|
+
}
|
|
8964
|
+
bucket.sessions += 1;
|
|
8965
|
+
bucket.messages += messageCount;
|
|
8966
|
+
let tokenBucket = dailyTokenMap.get(key);
|
|
8967
|
+
if (!tokenBucket) {
|
|
8968
|
+
tokenBucket = { date: key, input: 0, output: 0, cache_read: 0, cache_create: 0 };
|
|
8969
|
+
dailyTokenMap.set(key, tokenBucket);
|
|
8970
|
+
}
|
|
8971
|
+
const cacheRead = session.stats.total_cache_read_tokens ?? 0;
|
|
8972
|
+
const cacheCreate = session.stats.total_cache_create_tokens ?? 0;
|
|
8973
|
+
const pureInput = session.stats.total_input_tokens - cacheRead - cacheCreate;
|
|
8974
|
+
tokenBucket.input += Math.max(0, pureInput);
|
|
8975
|
+
tokenBucket.output += session.stats.total_output_tokens;
|
|
8976
|
+
tokenBucket.cache_read += cacheRead;
|
|
8977
|
+
tokenBucket.cache_create += cacheCreate;
|
|
8978
|
+
if (session.model_usage) {
|
|
8979
|
+
for (const [model, tokens] of Object.entries(session.model_usage)) {
|
|
8980
|
+
const entry = modelAgg.get(model);
|
|
8981
|
+
if (entry) {
|
|
8982
|
+
entry.tokens += tokens;
|
|
8983
|
+
entry.sessions += 1;
|
|
8984
|
+
} else {
|
|
8985
|
+
modelAgg.set(model, { tokens, sessions: 1 });
|
|
8986
|
+
}
|
|
8987
|
+
}
|
|
8988
|
+
}
|
|
8989
|
+
let recentIndex = recentCandidates.length;
|
|
8990
|
+
for (let i = 0; i < recentCandidates.length; i += 1) {
|
|
8991
|
+
if (activity > recentCandidates[i].activity) {
|
|
8992
|
+
recentIndex = i;
|
|
8993
|
+
break;
|
|
8994
|
+
}
|
|
8995
|
+
}
|
|
8996
|
+
if (recentIndex < DASHBOARD_RECENT_LIMIT) {
|
|
8997
|
+
recentCandidates.splice(recentIndex, 0, { session, activity });
|
|
8998
|
+
if (recentCandidates.length > DASHBOARD_RECENT_LIMIT) recentCandidates.pop();
|
|
8999
|
+
}
|
|
9000
|
+
}
|
|
9001
|
+
const perAgent = [...agentMetrics.entries()].map(([name, metrics]) => {
|
|
9002
|
+
const info = agentInfoMap?.get(name);
|
|
9003
|
+
return {
|
|
9004
|
+
name,
|
|
9005
|
+
displayName: info?.displayName ?? name,
|
|
9006
|
+
icon: info?.icon ?? "",
|
|
9007
|
+
sessions: metrics.sessions,
|
|
9008
|
+
messages: metrics.messages,
|
|
9009
|
+
tokens: metrics.tokens
|
|
9010
|
+
};
|
|
9011
|
+
}).filter((item) => item.sessions > 0).sort((a, b) => b.sessions - a.sessions);
|
|
9012
|
+
const dailyActivity = [...dailyMap.values()].sort((a, b) => a.date.localeCompare(b.date));
|
|
9013
|
+
const dailyTokenActivity = [...dailyTokenMap.values()].sort(
|
|
9014
|
+
(a, b) => a.date.localeCompare(b.date)
|
|
9015
|
+
);
|
|
9016
|
+
const modelDistribution = [...modelAgg.entries()].map(([model, { tokens, sessions: count }]) => ({ model, tokens, sessions: count })).sort((a, b) => b.tokens - a.tokens);
|
|
9017
|
+
const recentSessions = recentCandidates.map(({ session }) => {
|
|
9018
|
+
const agentKey = getSessionAgentName(session);
|
|
9019
|
+
return { ...session, agentName: agentKey };
|
|
9020
|
+
});
|
|
9021
|
+
return {
|
|
9022
|
+
totals: {
|
|
9023
|
+
sessions: totalSessions,
|
|
9024
|
+
messages: totalMessages,
|
|
9025
|
+
tokens: totalTokens,
|
|
9026
|
+
cost: totalCost,
|
|
9027
|
+
cost_source: totalCost > 0 ? hasEstimatedCost ? "estimated" : "recorded" : void 0,
|
|
9028
|
+
latestActivity: latestActivity || void 0
|
|
9029
|
+
},
|
|
9030
|
+
perAgent,
|
|
9031
|
+
dailyActivity,
|
|
9032
|
+
dailyTokenActivity,
|
|
9033
|
+
modelDistribution,
|
|
9034
|
+
recentSessions
|
|
9035
|
+
};
|
|
9036
|
+
}
|
|
9093
9037
|
|
|
9094
9038
|
export {
|
|
9095
9039
|
registerAgent,
|
|
@@ -9102,6 +9046,8 @@ export {
|
|
|
9102
9046
|
filteredSession,
|
|
9103
9047
|
getParsedSession,
|
|
9104
9048
|
BaseAgent,
|
|
9049
|
+
FileSystemSessionSource,
|
|
9050
|
+
DatabaseSessionSource,
|
|
9105
9051
|
firstExisting,
|
|
9106
9052
|
resolveProviderRoots,
|
|
9107
9053
|
getCursorDataPath,
|
|
@@ -9148,6 +9094,12 @@ export {
|
|
|
9148
9094
|
summarizeFileActivity,
|
|
9149
9095
|
extractSessionFileActivity,
|
|
9150
9096
|
parseSearchQuery,
|
|
9097
|
+
syncSessionSearchIndex,
|
|
9098
|
+
syncSessionSearchIndexChanges,
|
|
9099
|
+
searchSessions,
|
|
9100
|
+
listFileActivity,
|
|
9101
|
+
listSessionFileActivity,
|
|
9102
|
+
searchFileActivitySessions,
|
|
9151
9103
|
loadCachedSessions,
|
|
9152
9104
|
isAgentCacheInitialized,
|
|
9153
9105
|
markAgentCacheInitialized,
|
|
@@ -9156,13 +9108,12 @@ export {
|
|
|
9156
9108
|
saveCachedSessionChanges,
|
|
9157
9109
|
clearCache,
|
|
9158
9110
|
getCacheInfo,
|
|
9159
|
-
syncSessionSearchIndex,
|
|
9160
|
-
syncSessionSearchIndexChanges,
|
|
9161
|
-
searchSessions,
|
|
9162
|
-
listFileActivity,
|
|
9163
|
-
listSessionFileActivity,
|
|
9164
|
-
searchFileActivitySessions,
|
|
9165
9111
|
listCachedProjectGroups,
|
|
9112
|
+
attachMissingProjectIdentities,
|
|
9113
|
+
buildAgentCacheMeta,
|
|
9114
|
+
sessionSignature,
|
|
9115
|
+
sortSessions,
|
|
9116
|
+
computeSessionDiff,
|
|
9166
9117
|
filterSessions,
|
|
9167
9118
|
scanSessions,
|
|
9168
9119
|
scanSessionsAsync,
|
|
@@ -9170,6 +9121,13 @@ export {
|
|
|
9170
9121
|
listBookmarks,
|
|
9171
9122
|
upsertBookmark,
|
|
9172
9123
|
importBookmarks,
|
|
9173
|
-
deleteBookmark
|
|
9124
|
+
deleteBookmark,
|
|
9125
|
+
DASHBOARD_RECENT_LIMIT,
|
|
9126
|
+
getTotalTokens,
|
|
9127
|
+
getSessionAgentName,
|
|
9128
|
+
getSessionActivityTime,
|
|
9129
|
+
toLocalDateKey,
|
|
9130
|
+
startOfLocalDay,
|
|
9131
|
+
buildDashboard
|
|
9174
9132
|
};
|
|
9175
|
-
//# sourceMappingURL=chunk-
|
|
9133
|
+
//# sourceMappingURL=chunk-BIXOP5QX.js.map
|