codesesh 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{chunk-A4U2SMJJ.js → chunk-DHNP33L4.js} +20 -12
- package/dist/chunk-DHNP33L4.js.map +1 -0
- package/dist/{chunk-7APNDHQ6.js → chunk-HIYT2ZJL.js} +659 -353
- package/dist/chunk-HIYT2ZJL.js.map +1 -0
- package/dist/{dist-KEPJFHOC.js → dist-DMGLFBW6.js} +14 -4
- package/dist/index.js +224 -81
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +2 -2
- package/dist/search-index-worker.js +73 -39
- package/dist/search-index-worker.js.map +1 -1
- package/dist/smart-tag-worker.js +2 -2
- package/dist/web/assets/AgentIcon-DcUJM198.js +1 -0
- package/dist/web/assets/BookmarkButton-CJjTjyae.js +1 -0
- package/dist/web/assets/Dashboard-wjTLT17d.js +39 -0
- package/dist/web/assets/DialogTitle-Bz0eXTGu.js +1 -0
- package/dist/web/assets/InteractiveReceipt-xnRqJvfd.js +1 -0
- package/dist/web/assets/PrismHighlighter-C7OfXHq2.js +6 -0
- package/dist/web/assets/Projects-DBxubQuM.js +1 -0
- package/dist/web/assets/SearchFilterBar-BG9ah_Po.js +1 -0
- package/dist/web/assets/SearchResultsPanel-Clmf2xKw.js +1 -0
- package/dist/web/assets/SessionDetail-CgR7ipmi.js +54 -0
- package/dist/web/assets/index-DR07vs5V.js +1890 -0
- package/dist/web/assets/index-DuB6tGoe.css +2 -0
- package/dist/web/assets/index.dom-CCLJ9_FR.js +1 -0
- package/dist/web/assets/jsx-runtime-n5LQ9ujS.js +1 -0
- package/dist/web/assets/preload-helper-CmKXJxR3.js +1 -0
- package/dist/web/assets/session-indexes-EHJdYE4j.js +4 -0
- package/dist/web/assets/session-title-CNhapdPJ.js +1 -0
- package/dist/web/assets/useTheme-CwDcVdGz.js +1 -0
- package/dist/web/assets/utils-B6KiDbIe.js +1 -0
- package/dist/web/index.html +11 -7
- package/package.json +3 -3
- package/dist/chunk-7APNDHQ6.js.map +0 -1
- package/dist/chunk-A4U2SMJJ.js.map +0 -1
- package/dist/web/assets/index-B076CRkD.js +0 -109
- package/dist/web/assets/index-CWNbVhet.css +0 -2
- package/dist/web/assets/markdown-dR4kAewZ.js +0 -14
- package/dist/web/assets/react-D9s532MZ.js +0 -1821
- package/dist/web/assets/rolldown-runtime-QTnfLwEv.js +0 -1
- package/dist/web/assets/syntax-mZXQuVcd.js +0 -6
- package/dist/web/assets/vendor-i9U7_fNF.js +0 -41
- /package/dist/{dist-KEPJFHOC.js.map → dist-DMGLFBW6.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../core/dist/chunk-
|
|
3
|
+
// ../core/dist/chunk-DKIXFG56.mjs
|
|
4
4
|
function toRecord(value) {
|
|
5
5
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6
6
|
}
|
|
@@ -120,6 +120,76 @@ function normalizeMessageParts(value) {
|
|
|
120
120
|
return normalized ? [normalized] : [];
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
|
+
function mergeSessionsUpdatedEvents(previous, next) {
|
|
124
|
+
const changedSessionHeads = /* @__PURE__ */ new Map();
|
|
125
|
+
const removedSessionRefs = /* @__PURE__ */ new Map();
|
|
126
|
+
const sessionKey = (agentName, sessionId) => `${agentName}\0${sessionId}`;
|
|
127
|
+
const addChanged = (item) => {
|
|
128
|
+
const key = sessionKey(item.reference.agentName, item.reference.sessionId);
|
|
129
|
+
removedSessionRefs.delete(key);
|
|
130
|
+
changedSessionHeads.set(key, item);
|
|
131
|
+
};
|
|
132
|
+
const addRemoved = (item) => {
|
|
133
|
+
const key = sessionKey(item.agentName, item.sessionId);
|
|
134
|
+
changedSessionHeads.delete(key);
|
|
135
|
+
removedSessionRefs.set(key, item);
|
|
136
|
+
};
|
|
137
|
+
for (const item of previous.changedSessionHeads) addChanged(item);
|
|
138
|
+
for (const item of previous.removedSessionRefs) addRemoved(item);
|
|
139
|
+
for (const item of next.changedSessionHeads) addChanged(item);
|
|
140
|
+
for (const item of next.removedSessionRefs) addRemoved(item);
|
|
141
|
+
return {
|
|
142
|
+
type: "sessions-updated",
|
|
143
|
+
changedAgents: Array.from(/* @__PURE__ */ new Set([...previous.changedAgents, ...next.changedAgents])),
|
|
144
|
+
newSessions: previous.newSessions + next.newSessions,
|
|
145
|
+
updatedSessions: previous.updatedSessions + next.updatedSessions,
|
|
146
|
+
removedSessions: previous.removedSessions + next.removedSessions,
|
|
147
|
+
totalSessions: next.totalSessions,
|
|
148
|
+
timestamp: next.timestamp,
|
|
149
|
+
changedSessionHeads: [...changedSessionHeads.values()],
|
|
150
|
+
removedSessionRefs: [...removedSessionRefs.values()]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function startOfCalendarDay(timestamp) {
|
|
154
|
+
const date = new Date(timestamp);
|
|
155
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
|
|
156
|
+
}
|
|
157
|
+
function addCalendarDays(timestamp, days) {
|
|
158
|
+
const date = new Date(timestamp);
|
|
159
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days).getTime();
|
|
160
|
+
}
|
|
161
|
+
function toCalendarDayNumber(timestamp) {
|
|
162
|
+
const date = new Date(timestamp);
|
|
163
|
+
return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / 864e5;
|
|
164
|
+
}
|
|
165
|
+
function countCalendarDays(from, to) {
|
|
166
|
+
return Math.max(1, toCalendarDayNumber(to) - toCalendarDayNumber(from) + 1);
|
|
167
|
+
}
|
|
168
|
+
function toCalendarDayKey(timestamp) {
|
|
169
|
+
const date = new Date(timestamp);
|
|
170
|
+
return `${String(date.getFullYear()).padStart(4, "0")}-${String(date.getMonth() + 1).padStart(
|
|
171
|
+
2,
|
|
172
|
+
"0"
|
|
173
|
+
)}-${String(date.getDate()).padStart(2, "0")}`;
|
|
174
|
+
}
|
|
175
|
+
var PROJECT_IDENTITY_KINDS = [
|
|
176
|
+
"git_remote",
|
|
177
|
+
"git_common_dir",
|
|
178
|
+
"manifest_path",
|
|
179
|
+
"synthetic",
|
|
180
|
+
"path",
|
|
181
|
+
"loose"
|
|
182
|
+
];
|
|
183
|
+
var KIND_LOOKUP = new Set(PROJECT_IDENTITY_KINDS);
|
|
184
|
+
function isProjectIdentityKind(value) {
|
|
185
|
+
return KIND_LOOKUP.has(value);
|
|
186
|
+
}
|
|
187
|
+
function getProjectIdentityKey(identity) {
|
|
188
|
+
return `${identity.kind}:${identity.key}`;
|
|
189
|
+
}
|
|
190
|
+
function matchesProjectIdentity(identity, expected) {
|
|
191
|
+
return identity?.kind === expected.kind && identity.key === expected.key;
|
|
192
|
+
}
|
|
123
193
|
var UNKNOWN_AGENT_NAME = "unknown";
|
|
124
194
|
function normalizeSessionReference(reference) {
|
|
125
195
|
return {
|
|
@@ -144,6 +214,13 @@ function formatSessionReference(reference) {
|
|
|
144
214
|
function getSessionAgentKey(session) {
|
|
145
215
|
return parseSessionReference(session.slug)?.agentName ?? UNKNOWN_AGENT_NAME;
|
|
146
216
|
}
|
|
217
|
+
function agentRoutePath(agentName) {
|
|
218
|
+
return `/${encodeURIComponent(agentName.trim().toLowerCase())}`;
|
|
219
|
+
}
|
|
220
|
+
function sessionRoutePath(reference) {
|
|
221
|
+
const normalized = normalizeSessionReference(reference);
|
|
222
|
+
return `${agentRoutePath(normalized.agentName)}/${encodeURIComponent(normalized.sessionId)}`;
|
|
223
|
+
}
|
|
147
224
|
function compareSessionActivityDesc(a, b) {
|
|
148
225
|
return (b.time_updated ?? b.time_created) - (a.time_updated ?? a.time_created);
|
|
149
226
|
}
|
|
@@ -253,8 +330,8 @@ var SAMPLE_DASHBOARD_DATA = {
|
|
|
253
330
|
};
|
|
254
331
|
|
|
255
332
|
// ../core/dist/index.mjs
|
|
256
|
-
import { existsSync as
|
|
257
|
-
import { join as
|
|
333
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync2, statSync as statSync3 } from "fs";
|
|
334
|
+
import { join as join5, basename as basename3, dirname as dirname2 } from "path";
|
|
258
335
|
import { existsSync, readdirSync, statSync } from "fs";
|
|
259
336
|
import { join } from "path";
|
|
260
337
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -263,38 +340,40 @@ import { join as join2 } from "path";
|
|
|
263
340
|
import { closeSync, openSync, readSync } from "fs";
|
|
264
341
|
import { StringDecoder } from "string_decoder";
|
|
265
342
|
import { basename } from "path";
|
|
266
|
-
import { existsSync as
|
|
343
|
+
import { existsSync as existsSync4, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
267
344
|
import { homedir as homedir2 } from "os";
|
|
268
|
-
import { join as
|
|
269
|
-
import {
|
|
270
|
-
import {
|
|
271
|
-
import {
|
|
345
|
+
import { join as join4 } from "path";
|
|
346
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
347
|
+
import { basename as basename2, dirname, join as join3 } from "path";
|
|
348
|
+
import { join as join7 } from "path";
|
|
349
|
+
import { existsSync as existsSync6 } from "fs";
|
|
350
|
+
import { basename as basename4, dirname as dirname3, join as join6 } from "path";
|
|
272
351
|
import { createRequire } from "module";
|
|
273
352
|
import { createHash } from "crypto";
|
|
274
|
-
import { existsSync as
|
|
275
|
-
import { join as
|
|
276
|
-
import { closeSync as closeSync2, existsSync as
|
|
277
|
-
import { join as
|
|
278
|
-
import { existsSync as
|
|
353
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
|
|
354
|
+
import { join as join8, basename as basename5, dirname as dirname4 } from "path";
|
|
355
|
+
import { closeSync as closeSync2, existsSync as existsSync8, openSync as openSync2, readFileSync as readFileSync4, readSync as readSync2, statSync as statSync5 } from "fs";
|
|
356
|
+
import { join as join9, basename as basename6 } from "path";
|
|
357
|
+
import { existsSync as existsSync9, readdirSync as readdirSync5, readFileSync as readFileSync5, statSync as statSync6 } from "fs";
|
|
279
358
|
import { homedir as homedir3, platform as platform2 } from "os";
|
|
280
|
-
import { join as
|
|
281
|
-
import { existsSync as
|
|
282
|
-
import { basename as
|
|
359
|
+
import { join as join10, normalize } from "path";
|
|
360
|
+
import { existsSync as existsSync10 } from "fs";
|
|
361
|
+
import { basename as basename7, join as join11 } from "path";
|
|
283
362
|
import { homedir as homedir4, platform as platform3 } from "os";
|
|
284
|
-
import { join as
|
|
363
|
+
import { join as join12 } from "path";
|
|
285
364
|
import { availableParallelism } from "os";
|
|
286
365
|
import { Worker } from "worker_threads";
|
|
287
|
-
import { existsSync as
|
|
366
|
+
import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
|
|
288
367
|
import { spawnSync } from "child_process";
|
|
289
368
|
import * as os from "os";
|
|
290
369
|
import * as path from "path";
|
|
291
370
|
import { resolve, sep } from "path";
|
|
292
|
-
import { existsSync as
|
|
293
|
-
import { existsSync as
|
|
294
|
-
import { join as
|
|
371
|
+
import { existsSync as existsSync13, rmSync as rmSync2, unlinkSync } from "fs";
|
|
372
|
+
import { existsSync as existsSync12 } from "fs";
|
|
373
|
+
import { join as join13 } from "path";
|
|
295
374
|
import { homedir as homedir6 } from "os";
|
|
296
375
|
import { homedir as homedir7, platform as platform4 } from "os";
|
|
297
|
-
import { join as
|
|
376
|
+
import { join as join14 } from "path";
|
|
298
377
|
var registrations = [];
|
|
299
378
|
function registerAgent(reg) {
|
|
300
379
|
registrations.push(reg);
|
|
@@ -547,8 +626,41 @@ var SingleFileSessionSource = class extends FileSystemSessionSource {
|
|
|
547
626
|
};
|
|
548
627
|
}
|
|
549
628
|
};
|
|
629
|
+
var SessionScanError = class extends Error {
|
|
630
|
+
constructor(agentName, stage, options) {
|
|
631
|
+
super(`${agentName} session scan failed while ${stage}`, options);
|
|
632
|
+
this.agentName = agentName;
|
|
633
|
+
this.stage = stage;
|
|
634
|
+
this.name = "SessionScanError";
|
|
635
|
+
}
|
|
636
|
+
agentName;
|
|
637
|
+
stage;
|
|
638
|
+
};
|
|
639
|
+
function sqliteSourceFiles(dbPath) {
|
|
640
|
+
return [dbPath, `${dbPath}-wal`];
|
|
641
|
+
}
|
|
642
|
+
function statOrNull(path2) {
|
|
643
|
+
try {
|
|
644
|
+
return statSync(path2);
|
|
645
|
+
} catch {
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function sqliteSourceFingerprint(dbPath) {
|
|
650
|
+
return sqliteSourceFiles(dbPath).map((path2) => {
|
|
651
|
+
const stats = statOrNull(path2);
|
|
652
|
+
return stats ? `${stats.size}:${Math.round(stats.mtimeMs)}` : "-";
|
|
653
|
+
}).join("|");
|
|
654
|
+
}
|
|
655
|
+
function latestSqliteSourceMtime(dbPath) {
|
|
656
|
+
return sqliteSourceFiles(dbPath).reduce((latest, path2) => {
|
|
657
|
+
const stats = statOrNull(path2);
|
|
658
|
+
return stats && stats.mtimeMs > latest ? stats.mtimeMs : latest;
|
|
659
|
+
}, 0);
|
|
660
|
+
}
|
|
550
661
|
var DatabaseSessionSource = class extends BaseAgent {
|
|
551
662
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
663
|
+
lastSourceFingerprint = null;
|
|
552
664
|
/** 记录单个会话的缓存 meta(sourcePath = dbPath)。 */
|
|
553
665
|
rememberSession(sessionId) {
|
|
554
666
|
const dbPath = this.getDatabasePath();
|
|
@@ -562,7 +674,12 @@ var DatabaseSessionSource = class extends BaseAgent {
|
|
|
562
674
|
this.sessionMetaMap = meta;
|
|
563
675
|
}
|
|
564
676
|
/**
|
|
565
|
-
*
|
|
677
|
+
* 变更检测:数据库内部变更难以按行定位,按库文件集合的指纹判定。
|
|
678
|
+
*
|
|
679
|
+
* In WAL mode a commit appends to the sidecar and leaves the main file
|
|
680
|
+
* untouched until checkpoint, so watching the database alone misses recent
|
|
681
|
+
* writes entirely. Size is part of the fingerprint because an uncheckpointed
|
|
682
|
+
* commit may land in the same millisecond as the previous one.
|
|
566
683
|
*/
|
|
567
684
|
checkForChanges(sinceTimestamp, _cachedSessions) {
|
|
568
685
|
const dbPath = this.getDatabasePath();
|
|
@@ -570,7 +687,10 @@ var DatabaseSessionSource = class extends BaseAgent {
|
|
|
570
687
|
return { hasChanges: false, timestamp: Date.now() };
|
|
571
688
|
}
|
|
572
689
|
try {
|
|
573
|
-
const
|
|
690
|
+
const fingerprint = sqliteSourceFingerprint(dbPath);
|
|
691
|
+
const previous = this.lastSourceFingerprint;
|
|
692
|
+
this.lastSourceFingerprint = fingerprint;
|
|
693
|
+
const hasChanges = previous == null ? latestSqliteSourceMtime(dbPath) > sinceTimestamp : fingerprint !== previous;
|
|
574
694
|
return {
|
|
575
695
|
hasChanges,
|
|
576
696
|
timestamp: Date.now()
|
|
@@ -630,17 +750,17 @@ function* readJsonlFileLines(filePath, chunkBytes = READ_CHUNK_BYTES) {
|
|
|
630
750
|
try {
|
|
631
751
|
const buffer = Buffer.alloc(chunkBytes);
|
|
632
752
|
const decoder = new StringDecoder("utf8");
|
|
633
|
-
let
|
|
753
|
+
let pending2 = [];
|
|
634
754
|
let bytesRead = readSync(fd, buffer, 0, chunkBytes, -1);
|
|
635
755
|
while (bytesRead > 0) {
|
|
636
756
|
const decoded = decoder.write(buffer.subarray(0, bytesRead));
|
|
637
757
|
const lastBreak = decoded.lastIndexOf("\n");
|
|
638
758
|
if (lastBreak === -1) {
|
|
639
|
-
if (decoded)
|
|
759
|
+
if (decoded) pending2.push(decoded);
|
|
640
760
|
} else {
|
|
641
|
-
|
|
642
|
-
const complete =
|
|
643
|
-
|
|
761
|
+
pending2.push(decoded.slice(0, lastBreak));
|
|
762
|
+
const complete = pending2.join("");
|
|
763
|
+
pending2 = [decoded.slice(lastBreak + 1)];
|
|
644
764
|
for (const line of complete.split("\n")) {
|
|
645
765
|
const trimmed = line.trim();
|
|
646
766
|
if (trimmed) yield trimmed;
|
|
@@ -648,8 +768,8 @@ function* readJsonlFileLines(filePath, chunkBytes = READ_CHUNK_BYTES) {
|
|
|
648
768
|
}
|
|
649
769
|
bytesRead = readSync(fd, buffer, 0, chunkBytes, -1);
|
|
650
770
|
}
|
|
651
|
-
|
|
652
|
-
const tail =
|
|
771
|
+
pending2.push(decoder.end());
|
|
772
|
+
const tail = pending2.join("").trim();
|
|
653
773
|
if (tail) yield tail;
|
|
654
774
|
} finally {
|
|
655
775
|
closeSync(fd);
|
|
@@ -833,6 +953,56 @@ var aliases_default = {
|
|
|
833
953
|
"gpt-5.1-codex-high": "gpt-5.3-codex",
|
|
834
954
|
"gpt-5.2-low": "gpt-5.2"
|
|
835
955
|
};
|
|
956
|
+
var PRIVATE_DIR_MODE = 448;
|
|
957
|
+
var PRIVATE_FILE_MODE = 384;
|
|
958
|
+
var SQLITE_SIDECAR_SUFFIXES = ["-wal", "-shm", "-journal"];
|
|
959
|
+
var isPosix = process.platform !== "win32";
|
|
960
|
+
function reportChmodFailure(path2, error) {
|
|
961
|
+
getCoreDiagnostics()?.warn("storage.permissions_failed", {
|
|
962
|
+
path: path2,
|
|
963
|
+
message: error instanceof Error ? error.message : String(error)
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
function restrict(path2, mode) {
|
|
967
|
+
if (!isPosix || !existsSync3(path2)) return;
|
|
968
|
+
try {
|
|
969
|
+
if ((statSync2(path2).mode & 511) === mode) return;
|
|
970
|
+
chmodSync(path2, mode);
|
|
971
|
+
} catch (error) {
|
|
972
|
+
reportChmodFailure(path2, error);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
function ensurePrivateDirectory(path2) {
|
|
976
|
+
mkdirSync(path2, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
977
|
+
restrict(path2, PRIVATE_DIR_MODE);
|
|
978
|
+
}
|
|
979
|
+
function restrictPrivateFile(path2) {
|
|
980
|
+
restrict(path2, PRIVATE_FILE_MODE);
|
|
981
|
+
}
|
|
982
|
+
function restrictExistingPrivateFiles(directory, owns) {
|
|
983
|
+
try {
|
|
984
|
+
for (const entry of readdirSync2(directory)) {
|
|
985
|
+
if (owns(entry)) restrictPrivateFile(join3(directory, entry));
|
|
986
|
+
}
|
|
987
|
+
} catch (error) {
|
|
988
|
+
reportChmodFailure(directory, error);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
var sweptBackupsFor = /* @__PURE__ */ new Set();
|
|
992
|
+
function restrictExistingBackups(dbPath) {
|
|
993
|
+
if (sweptBackupsFor.has(dbPath)) return;
|
|
994
|
+
sweptBackupsFor.add(dbPath);
|
|
995
|
+
const prefix = `${basename2(dbPath)}.`;
|
|
996
|
+
restrictExistingPrivateFiles(
|
|
997
|
+
dirname(dbPath),
|
|
998
|
+
(name) => name.startsWith(prefix) && name.endsWith(".bak")
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
function restrictPrivateDatabase(dbPath) {
|
|
1002
|
+
restrictPrivateFile(dbPath);
|
|
1003
|
+
for (const suffix of SQLITE_SIDECAR_SUFFIXES) restrictPrivateFile(`${dbPath}${suffix}`);
|
|
1004
|
+
restrictExistingBackups(dbPath);
|
|
1005
|
+
}
|
|
836
1006
|
var snapshot_default = {
|
|
837
1007
|
"claude-3-5-haiku": [8e-7, 4e-6, 1e-6, 8e-8],
|
|
838
1008
|
"claude-3-5-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
|
|
@@ -879,7 +1049,9 @@ var snapshot_default = {
|
|
|
879
1049
|
var LITELLM_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
|
|
880
1050
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
881
1051
|
var WEB_SEARCH_COST = 0.01;
|
|
882
|
-
var
|
|
1052
|
+
var REFRESH_TIMEOUT_MS = 1e4;
|
|
1053
|
+
var published = { id: 1, pricing: loadSnapshot() };
|
|
1054
|
+
var pending = null;
|
|
883
1055
|
loadDiskCache();
|
|
884
1056
|
function normalizeKey(key) {
|
|
885
1057
|
return key.trim().toLowerCase();
|
|
@@ -888,10 +1060,10 @@ function costNumber(value, fallback) {
|
|
|
888
1060
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
889
1061
|
}
|
|
890
1062
|
function getCacheDir() {
|
|
891
|
-
return
|
|
1063
|
+
return join4(homedir2(), ".cache", "codesesh");
|
|
892
1064
|
}
|
|
893
1065
|
function getCachePath() {
|
|
894
|
-
return
|
|
1066
|
+
return join4(getCacheDir(), "litellm-pricing.json");
|
|
895
1067
|
}
|
|
896
1068
|
function loadSnapshot() {
|
|
897
1069
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -957,7 +1129,7 @@ function parseLiteLLMData(data) {
|
|
|
957
1129
|
}
|
|
958
1130
|
function loadDiskCache() {
|
|
959
1131
|
const path2 = getCachePath();
|
|
960
|
-
if (!
|
|
1132
|
+
if (!existsSync4(path2)) return;
|
|
961
1133
|
try {
|
|
962
1134
|
const cached = JSON.parse(readFileSync(path2, "utf-8"));
|
|
963
1135
|
if (Date.now() - cached.timestamp <= CACHE_TTL_MS) {
|
|
@@ -967,20 +1139,51 @@ function loadDiskCache() {
|
|
|
967
1139
|
if (!pricing) continue;
|
|
968
1140
|
next.set(normalizeKey(name), pricing);
|
|
969
1141
|
}
|
|
970
|
-
|
|
1142
|
+
published = { id: published.id + 1, pricing: next };
|
|
971
1143
|
}
|
|
972
1144
|
} catch {
|
|
973
1145
|
}
|
|
974
1146
|
}
|
|
975
1147
|
function getPricingRegistry() {
|
|
976
|
-
return
|
|
1148
|
+
return published.pricing;
|
|
1149
|
+
}
|
|
1150
|
+
function getPricingGeneration() {
|
|
1151
|
+
return published;
|
|
1152
|
+
}
|
|
1153
|
+
function publishPendingPricing() {
|
|
1154
|
+
if (!pending) return false;
|
|
1155
|
+
published = { id: published.id + 1, pricing: pending };
|
|
1156
|
+
pending = null;
|
|
1157
|
+
getCoreDiagnostics()?.info?.("pricing.generation.published", {
|
|
1158
|
+
generation: published.id,
|
|
1159
|
+
models: published.pricing.size
|
|
1160
|
+
});
|
|
1161
|
+
return true;
|
|
1162
|
+
}
|
|
1163
|
+
function hasPendingPricing() {
|
|
1164
|
+
return pending !== null;
|
|
977
1165
|
}
|
|
978
1166
|
function hasBillablePricing(pricing) {
|
|
979
1167
|
return pricing.inputCostPerToken > 0 || pricing.outputCostPerToken > 0 || pricing.cacheReadCostPerToken > 0 || pricing.cacheCreateCostPerToken > 0;
|
|
980
1168
|
}
|
|
981
|
-
|
|
1169
|
+
function writeDiskCacheAtomically(path2, pricing) {
|
|
1170
|
+
const temporaryPath = `${path2}.${process.pid}.tmp`;
|
|
1171
|
+
const payload = JSON.stringify({ timestamp: Date.now(), data: Object.fromEntries(pricing) });
|
|
1172
|
+
try {
|
|
1173
|
+
ensurePrivateDirectory(getCacheDir());
|
|
1174
|
+
writeFileSync(temporaryPath, payload);
|
|
1175
|
+
restrictPrivateFile(temporaryPath);
|
|
1176
|
+
renameSync(temporaryPath, path2);
|
|
1177
|
+
} catch (error) {
|
|
1178
|
+
rmSync(temporaryPath, { force: true });
|
|
1179
|
+
getCoreDiagnostics()?.warn("pricing.cache_write_failed", {
|
|
1180
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
async function refreshPricingCache(options = {}) {
|
|
982
1185
|
const path2 = getCachePath();
|
|
983
|
-
if (
|
|
1186
|
+
if (existsSync4(path2)) {
|
|
984
1187
|
try {
|
|
985
1188
|
const cached = JSON.parse(readFileSync(path2, "utf-8"));
|
|
986
1189
|
if (typeof cached.timestamp === "number" && Date.now() - cached.timestamp <= CACHE_TTL_MS) {
|
|
@@ -989,8 +1192,11 @@ async function refreshPricingCache() {
|
|
|
989
1192
|
} catch {
|
|
990
1193
|
}
|
|
991
1194
|
}
|
|
1195
|
+
const timeout = AbortSignal.timeout(options.timeoutMs ?? REFRESH_TIMEOUT_MS);
|
|
1196
|
+
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
1197
|
+
getCoreDiagnostics()?.info?.("pricing.refresh.started", { generation: published.id });
|
|
992
1198
|
try {
|
|
993
|
-
const response = await fetch(LITELLM_URL);
|
|
1199
|
+
const response = await fetch(LITELLM_URL, { signal });
|
|
994
1200
|
if (!response.ok) return false;
|
|
995
1201
|
const data = await response.json();
|
|
996
1202
|
const remote = parseLiteLLMData(data);
|
|
@@ -999,11 +1205,19 @@ async function refreshPricingCache() {
|
|
|
999
1205
|
for (const [name, pricing] of remote.entries()) {
|
|
1000
1206
|
next.set(name, pricing);
|
|
1001
1207
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1208
|
+
pending = next;
|
|
1209
|
+
writeDiskCacheAtomically(path2, next);
|
|
1210
|
+
getCoreDiagnostics()?.info?.("pricing.refresh.completed", {
|
|
1211
|
+
generation: published.id,
|
|
1212
|
+
models: next.size
|
|
1213
|
+
});
|
|
1005
1214
|
return true;
|
|
1006
|
-
} catch {
|
|
1215
|
+
} catch (error) {
|
|
1216
|
+
getCoreDiagnostics()?.warn("pricing.refresh.failed", {
|
|
1217
|
+
generation: published.id,
|
|
1218
|
+
aborted: signal.aborted,
|
|
1219
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1220
|
+
});
|
|
1007
1221
|
return false;
|
|
1008
1222
|
}
|
|
1009
1223
|
}
|
|
@@ -1370,22 +1584,22 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1370
1584
|
sessionsIndexCache = {};
|
|
1371
1585
|
sessionsIndexMtime = {};
|
|
1372
1586
|
findBasePath() {
|
|
1373
|
-
return firstExisting(
|
|
1587
|
+
return firstExisting(join5(resolveClaudeCodeDataRoot(), "projects"), "data/claudecode");
|
|
1374
1588
|
}
|
|
1375
1589
|
getSessionWatchPlan() {
|
|
1376
1590
|
const dataRoot = resolveClaudeCodeDataRoot();
|
|
1377
1591
|
return {
|
|
1378
1592
|
status: "supported",
|
|
1379
|
-
targets: [{ root: dataRoot, path:
|
|
1593
|
+
targets: [{ root: dataRoot, path: join5(dataRoot, "projects") }, { path: "data/claudecode" }]
|
|
1380
1594
|
};
|
|
1381
1595
|
}
|
|
1382
1596
|
isAvailable() {
|
|
1383
1597
|
this.basePath = this.findBasePath();
|
|
1384
1598
|
if (!this.basePath) return false;
|
|
1385
1599
|
try {
|
|
1386
|
-
for (const entry of
|
|
1387
|
-
const dir =
|
|
1388
|
-
if (
|
|
1600
|
+
for (const entry of readdirSync3(this.basePath)) {
|
|
1601
|
+
const dir = join5(this.basePath, entry);
|
|
1602
|
+
if (existsSync5(dir) && readdirSync3(dir).some((f) => f.endsWith(".jsonl"))) {
|
|
1389
1603
|
return true;
|
|
1390
1604
|
}
|
|
1391
1605
|
}
|
|
@@ -1405,13 +1619,13 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1405
1619
|
recursive: false,
|
|
1406
1620
|
scanWindow: options
|
|
1407
1621
|
}).map(({ file, stat }) => ({
|
|
1408
|
-
sessionId:
|
|
1622
|
+
sessionId: basename3(file, ".jsonl"),
|
|
1409
1623
|
sourcePath: file,
|
|
1410
|
-
fingerprint: this.sourceFingerprint(stat, indexMtimes.get(
|
|
1624
|
+
fingerprint: this.sourceFingerprint(stat, indexMtimes.get(dirname2(file)) ?? null)
|
|
1411
1625
|
}));
|
|
1412
1626
|
}
|
|
1413
1627
|
parseFileSessionHead(sourcePath) {
|
|
1414
|
-
const projectDir =
|
|
1628
|
+
const projectDir = dirname2(sourcePath);
|
|
1415
1629
|
return getParsedSession(this.parseSessionHeadResult(sourcePath, projectDir));
|
|
1416
1630
|
}
|
|
1417
1631
|
getSessionData(sessionId) {
|
|
@@ -1419,7 +1633,7 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1419
1633
|
if (!meta) {
|
|
1420
1634
|
throw new Error(`Session not found: ${sessionId}`);
|
|
1421
1635
|
}
|
|
1422
|
-
if (!
|
|
1636
|
+
if (!existsSync5(meta.sourcePath)) {
|
|
1423
1637
|
throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
1424
1638
|
}
|
|
1425
1639
|
const builder = new TranscriptBuilder();
|
|
@@ -1449,13 +1663,13 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1449
1663
|
listProjectDirs() {
|
|
1450
1664
|
if (!this.basePath) return [];
|
|
1451
1665
|
try {
|
|
1452
|
-
return
|
|
1666
|
+
return readdirSync3(this.basePath).map((e) => join5(this.basePath, e)).filter((p) => existsSync5(p));
|
|
1453
1667
|
} catch {
|
|
1454
1668
|
return [];
|
|
1455
1669
|
}
|
|
1456
1670
|
}
|
|
1457
1671
|
createFileSessionMeta(head, source) {
|
|
1458
|
-
const projectDir =
|
|
1672
|
+
const projectDir = dirname2(source.file);
|
|
1459
1673
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1460
1674
|
const indexMtime = this.readFileMtimeMs(indexPath);
|
|
1461
1675
|
return this.buildFileSessionMeta({
|
|
@@ -1475,17 +1689,17 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1475
1689
|
return JSON.stringify([HEAD_INDEX_VERSION, stat.mtimeMs, stat.size, indexMtime]);
|
|
1476
1690
|
}
|
|
1477
1691
|
getSessionsIndexPath(projectDir) {
|
|
1478
|
-
return
|
|
1692
|
+
return join5(projectDir, "sessions-index.json");
|
|
1479
1693
|
}
|
|
1480
1694
|
loadSessionsIndex(projectDir) {
|
|
1481
|
-
const cacheKey =
|
|
1695
|
+
const cacheKey = basename3(projectDir);
|
|
1482
1696
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1483
1697
|
const mtime = this.readFileMtimeMs(indexPath);
|
|
1484
1698
|
if (cacheKey in this.sessionsIndexCache && this.sessionsIndexMtime[cacheKey] === mtime) {
|
|
1485
1699
|
return this.sessionsIndexCache[cacheKey];
|
|
1486
1700
|
}
|
|
1487
1701
|
const map = /* @__PURE__ */ new Map();
|
|
1488
|
-
if (
|
|
1702
|
+
if (existsSync5(indexPath)) {
|
|
1489
1703
|
try {
|
|
1490
1704
|
const data = JSON.parse(readFileSync2(indexPath, "utf-8"));
|
|
1491
1705
|
const entries = data?.entries ?? [];
|
|
@@ -1503,7 +1717,7 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1503
1717
|
return map;
|
|
1504
1718
|
}
|
|
1505
1719
|
parseSessionHeadResult(filePath, projectDir) {
|
|
1506
|
-
const sessionId =
|
|
1720
|
+
const sessionId = basename3(filePath, ".jsonl");
|
|
1507
1721
|
const index = this.loadSessionsIndex(projectDir);
|
|
1508
1722
|
const indexEntry = index.get(sessionId);
|
|
1509
1723
|
const explicitTitle = indexEntry?.summary ? String(indexEntry.summary) : null;
|
|
@@ -1531,7 +1745,7 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
1531
1745
|
continue;
|
|
1532
1746
|
}
|
|
1533
1747
|
if (lineIndex === 0) {
|
|
1534
|
-
createdAt = parseTimestampMs(data) ||
|
|
1748
|
+
createdAt = parseTimestampMs(data) || statSync3(filePath).mtimeMs;
|
|
1535
1749
|
updatedAt = createdAt;
|
|
1536
1750
|
}
|
|
1537
1751
|
const recordIndex = lineIndex;
|
|
@@ -1967,11 +2181,12 @@ function backupDatabase(db, dbPath, label) {
|
|
|
1967
2181
|
return null;
|
|
1968
2182
|
}
|
|
1969
2183
|
const timestamp = new Date(Date.now()).toISOString().replaceAll(":", "").replaceAll(".", "-");
|
|
1970
|
-
let backupPath =
|
|
1971
|
-
for (let counter = 1;
|
|
1972
|
-
backupPath =
|
|
2184
|
+
let backupPath = join6(dirname3(dbPath), `${basename4(dbPath)}.${timestamp}.${label}.bak`);
|
|
2185
|
+
for (let counter = 1; existsSync6(backupPath); counter += 1) {
|
|
2186
|
+
backupPath = join6(dirname3(dbPath), `${basename4(dbPath)}.${timestamp}.${label}.${counter}.bak`);
|
|
1973
2187
|
}
|
|
1974
2188
|
db.exec(`VACUUM INTO ${quoteSqlString(backupPath)}`);
|
|
2189
|
+
restrictPrivateFile(backupPath);
|
|
1975
2190
|
return backupPath;
|
|
1976
2191
|
}
|
|
1977
2192
|
function backupDatabaseIfPopulated(db, dbPath, label, tables) {
|
|
@@ -2066,8 +2281,9 @@ function openDb(dbPath) {
|
|
|
2066
2281
|
return null;
|
|
2067
2282
|
}
|
|
2068
2283
|
try {
|
|
2069
|
-
|
|
2284
|
+
ensurePrivateDirectory(dirname3(dbPath));
|
|
2070
2285
|
const db = DatabaseConstructor(dbPath);
|
|
2286
|
+
restrictPrivateDatabase(dbPath);
|
|
2071
2287
|
try {
|
|
2072
2288
|
db.pragma("journal_mode = WAL");
|
|
2073
2289
|
db.pragma("synchronous = NORMAL");
|
|
@@ -2138,7 +2354,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2138
2354
|
scan(options) {
|
|
2139
2355
|
if (!this.dbPath) return [];
|
|
2140
2356
|
const db = openDbReadOnly(this.dbPath);
|
|
2141
|
-
if (!db)
|
|
2357
|
+
if (!db) throw new SessionScanError(this.name, "opening the database");
|
|
2142
2358
|
try {
|
|
2143
2359
|
const cutoffTime = options?.from ?? Date.now() - 3650 * 24 * 60 * 60 * 1e3;
|
|
2144
2360
|
const hasMessageTable = Boolean(
|
|
@@ -2187,8 +2403,8 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2187
2403
|
options?.onProgress?.({ total: rows.length, processed, sessions: heads.length });
|
|
2188
2404
|
}
|
|
2189
2405
|
return heads;
|
|
2190
|
-
} catch {
|
|
2191
|
-
|
|
2406
|
+
} catch (error) {
|
|
2407
|
+
throw new SessionScanError(this.name, "reading sessions", { cause: error });
|
|
2192
2408
|
} finally {
|
|
2193
2409
|
db.close();
|
|
2194
2410
|
}
|
|
@@ -2237,7 +2453,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2237
2453
|
JOIN message m ON m.id = p.message_id
|
|
2238
2454
|
JOIN session s ON s.id = m.session_id
|
|
2239
2455
|
WHERE COALESCE(s.time_updated, s.time_created) >= ?
|
|
2240
|
-
ORDER BY p.message_id, p.time_created ASC
|
|
2456
|
+
ORDER BY p.message_id, p.time_created ASC, p.id ASC
|
|
2241
2457
|
`
|
|
2242
2458
|
).all(cutoffTime);
|
|
2243
2459
|
}
|
|
@@ -2250,9 +2466,21 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2250
2466
|
]);
|
|
2251
2467
|
return part ? cleanMessagePart(part) : null;
|
|
2252
2468
|
}
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2469
|
+
/**
|
|
2470
|
+
* Every part of one session in a single read. Fetching per message turned a
|
|
2471
|
+
* detail into M+2 queries, and without an index on part(message_id) each one
|
|
2472
|
+
* scanned the whole table.
|
|
2473
|
+
*/
|
|
2474
|
+
readSessionPartRows(db, sessionId) {
|
|
2475
|
+
return db.prepare(
|
|
2476
|
+
`
|
|
2477
|
+
SELECT p.message_id, p.data, p.time_created
|
|
2478
|
+
FROM part p
|
|
2479
|
+
JOIN message m ON m.id = p.message_id
|
|
2480
|
+
WHERE m.session_id = ?
|
|
2481
|
+
ORDER BY p.message_id, p.time_created ASC, p.id ASC
|
|
2482
|
+
`
|
|
2483
|
+
).all(sessionId);
|
|
2256
2484
|
}
|
|
2257
2485
|
buildPartsByMessage(partRows) {
|
|
2258
2486
|
const partsByMessage = /* @__PURE__ */ new Map();
|
|
@@ -2349,7 +2577,8 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2349
2577
|
let totalInputTokens = 0;
|
|
2350
2578
|
let totalOutputTokens = 0;
|
|
2351
2579
|
let hasEstimatedCost = false;
|
|
2352
|
-
const msgRows = db.prepare("SELECT * FROM message WHERE session_id = ? ORDER BY time_created ASC").all(sessionId);
|
|
2580
|
+
const msgRows = db.prepare("SELECT * FROM message WHERE session_id = ? ORDER BY time_created ASC, id ASC").all(sessionId);
|
|
2581
|
+
const partsByMessage = this.buildPartsByMessage(this.readSessionPartRows(db, sessionId));
|
|
2353
2582
|
for (const msgRow of msgRows) {
|
|
2354
2583
|
const msgData = parseJsonRecord(msgRow.data, this.name, "message.data");
|
|
2355
2584
|
if (isInternalEventType(msgData.type)) continue;
|
|
@@ -2360,7 +2589,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2360
2589
|
const model = parseModel(msgData.modelID, this.name);
|
|
2361
2590
|
const estimatedCost = cost > 0 ? null : estimateTokenCost(model, { input: inputTokens, output: outputTokens });
|
|
2362
2591
|
const resolvedCost = cost || estimatedCost || 0;
|
|
2363
|
-
const parts =
|
|
2592
|
+
const parts = partsByMessage.get(String(msgRow.id ?? "")) ?? [];
|
|
2364
2593
|
if (parts.length === 0) continue;
|
|
2365
2594
|
messages.push({
|
|
2366
2595
|
id: String(msgRow.id ?? ""),
|
|
@@ -2413,18 +2642,18 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2413
2642
|
}
|
|
2414
2643
|
};
|
|
2415
2644
|
function resolveOpenCodeDataRoot() {
|
|
2416
|
-
return
|
|
2645
|
+
return join7(resolveDataHome(), "opencode");
|
|
2417
2646
|
}
|
|
2418
2647
|
function findOpenCodeDbPath() {
|
|
2419
2648
|
if (!isSqliteAvailable()) return null;
|
|
2420
|
-
return firstExisting(
|
|
2649
|
+
return firstExisting(join7(resolveOpenCodeDataRoot(), "opencode.db"), "data/opencode/opencode.db");
|
|
2421
2650
|
}
|
|
2422
2651
|
function getOpenCodeSessionWatchPlan() {
|
|
2423
2652
|
const dataRoot = resolveOpenCodeDataRoot();
|
|
2424
2653
|
return {
|
|
2425
2654
|
status: "supported",
|
|
2426
2655
|
targets: [
|
|
2427
|
-
{ root: dataRoot, path:
|
|
2656
|
+
{ root: dataRoot, path: join7(dataRoot, "opencode.db") },
|
|
2428
2657
|
{ root: "data/opencode", path: "data/opencode/opencode.db" }
|
|
2429
2658
|
]
|
|
2430
2659
|
};
|
|
@@ -2521,14 +2750,14 @@ function kimiContentText(content) {
|
|
|
2521
2750
|
}).join(" ");
|
|
2522
2751
|
}
|
|
2523
2752
|
function extractFirstUserTitle(contextFile, wireFile) {
|
|
2524
|
-
if (contextFile &&
|
|
2753
|
+
if (contextFile && existsSync7(contextFile)) {
|
|
2525
2754
|
for (const record of readJsonlFile(contextFile)) {
|
|
2526
2755
|
if (record.role !== "user") continue;
|
|
2527
2756
|
const title = normalizeTitleText(kimiContentText(record.content));
|
|
2528
2757
|
if (title) return title;
|
|
2529
2758
|
}
|
|
2530
2759
|
}
|
|
2531
|
-
if (wireFile &&
|
|
2760
|
+
if (wireFile && existsSync7(wireFile)) {
|
|
2532
2761
|
for (const record of readJsonlFile(wireFile)) {
|
|
2533
2762
|
const message = asRecord(record.message) ?? {};
|
|
2534
2763
|
if (message.type !== "TurnBegin") continue;
|
|
@@ -2548,25 +2777,25 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2548
2777
|
projectMap = /* @__PURE__ */ new Map();
|
|
2549
2778
|
defaultModel = null;
|
|
2550
2779
|
findBasePath() {
|
|
2551
|
-
return firstExisting(
|
|
2780
|
+
return firstExisting(join8(resolveKimiDataRoot(), "sessions"), "data/kimi");
|
|
2552
2781
|
}
|
|
2553
2782
|
getSessionWatchPlan() {
|
|
2554
2783
|
const dataRoot = resolveKimiDataRoot();
|
|
2555
2784
|
return {
|
|
2556
2785
|
status: "supported",
|
|
2557
|
-
targets: [{ root: dataRoot, path:
|
|
2786
|
+
targets: [{ root: dataRoot, path: join8(dataRoot, "sessions") }, { path: "data/kimi" }]
|
|
2558
2787
|
};
|
|
2559
2788
|
}
|
|
2560
2789
|
/** Parse kimi.json and build md5(project_path) → cwd mapping */
|
|
2561
2790
|
loadKimiConfig() {
|
|
2562
2791
|
const dataRoot = resolveKimiDataRoot();
|
|
2563
|
-
const configPath =
|
|
2564
|
-
const tomlPath =
|
|
2565
|
-
if (
|
|
2792
|
+
const configPath = join8(dataRoot, "kimi.json");
|
|
2793
|
+
const tomlPath = join8(dataRoot, "config.toml");
|
|
2794
|
+
if (existsSync7(tomlPath)) {
|
|
2566
2795
|
const configText = readFileSync3(tomlPath, "utf-8");
|
|
2567
2796
|
this.defaultModel = configText.match(/^default_model\s*=\s*"([^"]+)"/m)?.[1] ?? null;
|
|
2568
2797
|
}
|
|
2569
|
-
if (!
|
|
2798
|
+
if (!existsSync7(configPath)) return;
|
|
2570
2799
|
try {
|
|
2571
2800
|
const raw = asRecord(JSON.parse(readFileSync3(configPath, "utf-8")));
|
|
2572
2801
|
const workDirs = asArray(raw?.work_dirs);
|
|
@@ -2595,14 +2824,14 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2595
2824
|
if (!this.basePath) return [];
|
|
2596
2825
|
const dirs = [];
|
|
2597
2826
|
try {
|
|
2598
|
-
for (const hashEntry of
|
|
2827
|
+
for (const hashEntry of readdirSync4(this.basePath, { withFileTypes: true })) {
|
|
2599
2828
|
if (!hashEntry.isDirectory()) continue;
|
|
2600
|
-
const hashPath =
|
|
2829
|
+
const hashPath = join8(this.basePath, hashEntry.name);
|
|
2601
2830
|
try {
|
|
2602
|
-
for (const sessionEntry of
|
|
2831
|
+
for (const sessionEntry of readdirSync4(hashPath, { withFileTypes: true })) {
|
|
2603
2832
|
if (!sessionEntry.isDirectory()) continue;
|
|
2604
|
-
const sessionPath =
|
|
2605
|
-
if (
|
|
2833
|
+
const sessionPath = join8(hashPath, sessionEntry.name);
|
|
2834
|
+
if (existsSync7(join8(sessionPath, "metadata.json")) || existsSync7(join8(sessionPath, "state.json"))) {
|
|
2606
2835
|
dirs.push(sessionPath);
|
|
2607
2836
|
}
|
|
2608
2837
|
}
|
|
@@ -2619,32 +2848,32 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2619
2848
|
*/
|
|
2620
2849
|
resolveSessionSourceResult(sessionDir) {
|
|
2621
2850
|
try {
|
|
2622
|
-
const sessionId =
|
|
2623
|
-
const projectHash =
|
|
2624
|
-
const contextFile =
|
|
2625
|
-
const wireFile =
|
|
2626
|
-
const existingContextFile =
|
|
2627
|
-
const existingWireFile =
|
|
2851
|
+
const sessionId = basename5(sessionDir);
|
|
2852
|
+
const projectHash = basename5(dirname4(sessionDir));
|
|
2853
|
+
const contextFile = join8(sessionDir, "context.jsonl");
|
|
2854
|
+
const wireFile = join8(sessionDir, "wire.jsonl");
|
|
2855
|
+
const existingContextFile = existsSync7(contextFile) ? contextFile : null;
|
|
2856
|
+
const existingWireFile = existsSync7(wireFile) ? wireFile : null;
|
|
2628
2857
|
if (!existingContextFile && !existingWireFile) {
|
|
2629
2858
|
return skippedSession("missing transcript");
|
|
2630
2859
|
}
|
|
2631
|
-
const statePath =
|
|
2632
|
-
const metaPath =
|
|
2860
|
+
const statePath = join8(sessionDir, "state.json");
|
|
2861
|
+
const metaPath = join8(sessionDir, "metadata.json");
|
|
2633
2862
|
let explicitTitle = "";
|
|
2634
2863
|
let wireMtime = null;
|
|
2635
2864
|
let metaFile = "";
|
|
2636
|
-
if (
|
|
2865
|
+
if (existsSync7(statePath)) {
|
|
2637
2866
|
const state = asRecord(JSON.parse(readFileSync3(statePath, "utf-8"))) ?? {};
|
|
2638
2867
|
explicitTitle = String(state.custom_title ?? "");
|
|
2639
2868
|
wireMtime = readWireMtime(state);
|
|
2640
2869
|
metaFile = statePath;
|
|
2641
|
-
} else if (
|
|
2870
|
+
} else if (existsSync7(metaPath)) {
|
|
2642
2871
|
const meta = asRecord(JSON.parse(readFileSync3(metaPath, "utf-8"))) ?? {};
|
|
2643
2872
|
explicitTitle = String(meta.title ?? "");
|
|
2644
2873
|
wireMtime = readWireMtime(meta);
|
|
2645
2874
|
metaFile = metaPath;
|
|
2646
2875
|
}
|
|
2647
|
-
const createdAt = wireMtime !== null ? wireMtime * 1e3 : metaFile ?
|
|
2876
|
+
const createdAt = wireMtime !== null ? wireMtime * 1e3 : metaFile ? statSync4(metaFile).mtimeMs : statSync4(sessionDir).mtimeMs;
|
|
2648
2877
|
return parsedSession({
|
|
2649
2878
|
id: sessionId,
|
|
2650
2879
|
sourcePath: sessionDir,
|
|
@@ -2765,8 +2994,8 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2765
2994
|
return this.buildSessionData(meta, builder, stats);
|
|
2766
2995
|
}
|
|
2767
2996
|
getSessionDataFromWire(meta) {
|
|
2768
|
-
const wirePath = meta.wireFile ??
|
|
2769
|
-
if (!
|
|
2997
|
+
const wirePath = meta.wireFile ?? join8(meta.sourcePath, "wire.jsonl");
|
|
2998
|
+
if (!existsSync7(wirePath)) throw new Error("wire.jsonl is missing");
|
|
2770
2999
|
const builder = new TranscriptBuilder();
|
|
2771
3000
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
2772
3001
|
const openToolArgumentBuffer = /* @__PURE__ */ new Map();
|
|
@@ -3004,10 +3233,10 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3004
3233
|
total_tokens: 0,
|
|
3005
3234
|
message_count: 0
|
|
3006
3235
|
};
|
|
3007
|
-
const wirePath =
|
|
3008
|
-
if (!
|
|
3009
|
-
const contextPath =
|
|
3010
|
-
const hasContext =
|
|
3236
|
+
const wirePath = join8(sessionDir, "wire.jsonl");
|
|
3237
|
+
if (!existsSync7(wirePath)) return stats;
|
|
3238
|
+
const contextPath = join8(sessionDir, "context.jsonl");
|
|
3239
|
+
const hasContext = existsSync7(contextPath);
|
|
3011
3240
|
try {
|
|
3012
3241
|
for (const record of readJsonlFile(wirePath)) {
|
|
3013
3242
|
const tokenUsage = asRecord(asRecord(record.message)?.usage);
|
|
@@ -3337,7 +3566,7 @@ var CODEX_TOOL_TITLE_MAP = {
|
|
|
3337
3566
|
subagent: "subagent"
|
|
3338
3567
|
};
|
|
3339
3568
|
function extractSessionId(filename) {
|
|
3340
|
-
const stem =
|
|
3569
|
+
const stem = basename6(filename, ".jsonl");
|
|
3341
3570
|
const parts = stem.split("-");
|
|
3342
3571
|
if (parts.length >= 5) {
|
|
3343
3572
|
return parts.slice(-5).join("-");
|
|
@@ -3509,15 +3738,15 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3509
3738
|
sessionIndexPath;
|
|
3510
3739
|
// ---- BaseAgent implementation ----
|
|
3511
3740
|
findBasePath() {
|
|
3512
|
-
return firstExisting(
|
|
3741
|
+
return firstExisting(join9(resolveCodexDataRoot(), "sessions"));
|
|
3513
3742
|
}
|
|
3514
3743
|
getSessionWatchPlan() {
|
|
3515
3744
|
const dataRoot = resolveCodexDataRoot();
|
|
3516
3745
|
return {
|
|
3517
3746
|
status: "supported",
|
|
3518
3747
|
targets: [
|
|
3519
|
-
{ path:
|
|
3520
|
-
{ path:
|
|
3748
|
+
{ path: join9(dataRoot, "sessions") },
|
|
3749
|
+
{ path: join9(dataRoot, "session_index.jsonl") }
|
|
3521
3750
|
]
|
|
3522
3751
|
};
|
|
3523
3752
|
}
|
|
@@ -3538,7 +3767,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3538
3767
|
getSessionData(sessionId) {
|
|
3539
3768
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
3540
3769
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
3541
|
-
if (!
|
|
3770
|
+
if (!existsSync8(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
3542
3771
|
const transcript = new TranscriptBuilder();
|
|
3543
3772
|
let totalInputTokens = 0;
|
|
3544
3773
|
let totalOutputTokens = 0;
|
|
@@ -3670,7 +3899,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3670
3899
|
]);
|
|
3671
3900
|
}
|
|
3672
3901
|
getSessionIndexPath() {
|
|
3673
|
-
this.sessionIndexPath ??=
|
|
3902
|
+
this.sessionIndexPath ??= join9(resolveCodexDataRoot(), "session_index.jsonl");
|
|
3674
3903
|
return this.sessionIndexPath;
|
|
3675
3904
|
}
|
|
3676
3905
|
// ---- Session index ----
|
|
@@ -3756,7 +3985,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3756
3985
|
return skippedSession("malformed first record");
|
|
3757
3986
|
}
|
|
3758
3987
|
firstPayload = extractPayload(firstRecord);
|
|
3759
|
-
createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(firstPayload) ||
|
|
3988
|
+
createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(firstPayload) || statSync5(filePath).mtimeMs;
|
|
3760
3989
|
updatedAt = createdAt;
|
|
3761
3990
|
}
|
|
3762
3991
|
try {
|
|
@@ -3878,7 +4107,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
3878
4107
|
return skippedSession("malformed first record");
|
|
3879
4108
|
}
|
|
3880
4109
|
const payload = extractPayload(firstRecord);
|
|
3881
|
-
const stat =
|
|
4110
|
+
const stat = statSync5(filePath);
|
|
3882
4111
|
const createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(payload) || stat.mtimeMs;
|
|
3883
4112
|
const indexTitle = this.getTitleForSession(sessionId);
|
|
3884
4113
|
const messageTitle = this.extractTitleFromLines(lines);
|
|
@@ -4289,15 +4518,15 @@ function resolveCursorDataRoot() {
|
|
|
4289
4518
|
if (override) return override;
|
|
4290
4519
|
const currentPlatform = platform2();
|
|
4291
4520
|
if (currentPlatform === "darwin") {
|
|
4292
|
-
return firstExisting(
|
|
4521
|
+
return firstExisting(join10(homedir3(), "Library", "Application Support", "Cursor", "User"));
|
|
4293
4522
|
}
|
|
4294
4523
|
if (currentPlatform === "linux") {
|
|
4295
|
-
const configRoot = readEnvPath("XDG_CONFIG_HOME") ??
|
|
4296
|
-
return firstExisting(
|
|
4524
|
+
const configRoot = readEnvPath("XDG_CONFIG_HOME") ?? join10(homedir3(), ".config");
|
|
4525
|
+
return firstExisting(join10(configRoot, "Cursor", "User"));
|
|
4297
4526
|
}
|
|
4298
4527
|
if (currentPlatform === "win32") {
|
|
4299
|
-
const appData = readEnvPath("APPDATA") ??
|
|
4300
|
-
return firstExisting(
|
|
4528
|
+
const appData = readEnvPath("APPDATA") ?? join10(homedir3(), "AppData", "Roaming");
|
|
4529
|
+
return firstExisting(join10(appData, "Cursor", "User"));
|
|
4301
4530
|
}
|
|
4302
4531
|
return null;
|
|
4303
4532
|
}
|
|
@@ -4358,6 +4587,24 @@ function parseComposerRow(value) {
|
|
|
4358
4587
|
chatMessages: parseChatMessages(record.chatMessages)
|
|
4359
4588
|
};
|
|
4360
4589
|
}
|
|
4590
|
+
function composerIdFromBubbleKey(key) {
|
|
4591
|
+
const start = key.indexOf(":") + 1;
|
|
4592
|
+
const end = key.indexOf(":", start);
|
|
4593
|
+
return end === -1 ? key.slice(start) : key.slice(start, end);
|
|
4594
|
+
}
|
|
4595
|
+
function groupBubbleRows(rows) {
|
|
4596
|
+
const byKey = [];
|
|
4597
|
+
for (const row of rows) {
|
|
4598
|
+
const bubble = parseBubbleRow(row.value);
|
|
4599
|
+
if (bubble) byKey.push({ rowId: row.row_id, key: row.key, bubble });
|
|
4600
|
+
}
|
|
4601
|
+
byKey.sort((left, right) => left.key.localeCompare(right.key));
|
|
4602
|
+
return {
|
|
4603
|
+
composerId: rows.length > 0 ? composerIdFromBubbleKey(rows[0].key) : "",
|
|
4604
|
+
byKey,
|
|
4605
|
+
byRowId: [...byKey].sort((left, right) => left.rowId - right.rowId)
|
|
4606
|
+
};
|
|
4607
|
+
}
|
|
4361
4608
|
function parseBubbleRow(value) {
|
|
4362
4609
|
const record = safeParseJsonRecord(value);
|
|
4363
4610
|
if (!record) return null;
|
|
@@ -4517,7 +4764,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4517
4764
|
if (!isSqliteAvailable()) return null;
|
|
4518
4765
|
const dataPath = resolveCursorDataRoot();
|
|
4519
4766
|
if (!dataPath) return null;
|
|
4520
|
-
return
|
|
4767
|
+
return join10(dataPath, "globalStorage", "state.vscdb");
|
|
4521
4768
|
}
|
|
4522
4769
|
getSessionWatchPlan() {
|
|
4523
4770
|
const dataPath = resolveCursorDataRoot();
|
|
@@ -4526,9 +4773,9 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4526
4773
|
targets: dataPath ? [
|
|
4527
4774
|
{
|
|
4528
4775
|
root: dataPath,
|
|
4529
|
-
path:
|
|
4776
|
+
path: join10(dataPath, "globalStorage", "state.vscdb")
|
|
4530
4777
|
},
|
|
4531
|
-
{ root: dataPath, path:
|
|
4778
|
+
{ root: dataPath, path: join10(dataPath, "workspaceStorage") }
|
|
4532
4779
|
] : []
|
|
4533
4780
|
};
|
|
4534
4781
|
}
|
|
@@ -4540,23 +4787,23 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4540
4787
|
const map = /* @__PURE__ */ new Map();
|
|
4541
4788
|
const dataPath = resolveCursorDataRoot();
|
|
4542
4789
|
if (!dataPath) return map;
|
|
4543
|
-
const wsStoragePath =
|
|
4544
|
-
if (!
|
|
4790
|
+
const wsStoragePath = join10(dataPath, "workspaceStorage");
|
|
4791
|
+
if (!existsSync9(wsStoragePath)) return map;
|
|
4545
4792
|
let entryNames;
|
|
4546
4793
|
try {
|
|
4547
|
-
entryNames =
|
|
4794
|
+
entryNames = readdirSync5(wsStoragePath);
|
|
4548
4795
|
} catch {
|
|
4549
4796
|
return map;
|
|
4550
4797
|
}
|
|
4551
4798
|
for (const name of entryNames) {
|
|
4552
|
-
const wsDir =
|
|
4799
|
+
const wsDir = join10(wsStoragePath, name);
|
|
4553
4800
|
try {
|
|
4554
|
-
if (!
|
|
4801
|
+
if (!statSync6(wsDir).isDirectory()) continue;
|
|
4555
4802
|
} catch {
|
|
4556
4803
|
continue;
|
|
4557
4804
|
}
|
|
4558
|
-
const wsJsonPath =
|
|
4559
|
-
if (!
|
|
4805
|
+
const wsJsonPath = join10(wsDir, "workspace.json");
|
|
4806
|
+
if (!existsSync9(wsJsonPath)) continue;
|
|
4560
4807
|
let workspacePath;
|
|
4561
4808
|
try {
|
|
4562
4809
|
const data = asRecord(JSON.parse(readFileSync5(wsJsonPath, "utf-8")));
|
|
@@ -4566,8 +4813,8 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4566
4813
|
} catch {
|
|
4567
4814
|
continue;
|
|
4568
4815
|
}
|
|
4569
|
-
const wsDbPath =
|
|
4570
|
-
if (!
|
|
4816
|
+
const wsDbPath = join10(wsDir, "state.vscdb");
|
|
4817
|
+
if (!existsSync9(wsDbPath)) continue;
|
|
4571
4818
|
const wsDb = openDbReadOnly(wsDbPath);
|
|
4572
4819
|
if (!wsDb) continue;
|
|
4573
4820
|
try {
|
|
@@ -4590,7 +4837,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4590
4837
|
}
|
|
4591
4838
|
isAvailable() {
|
|
4592
4839
|
this.dbPath = this.findDbPath();
|
|
4593
|
-
return this.dbPath !== null &&
|
|
4840
|
+
return this.dbPath !== null && existsSync9(this.dbPath);
|
|
4594
4841
|
}
|
|
4595
4842
|
scan(options) {
|
|
4596
4843
|
if (!this.dbPath) return [];
|
|
@@ -4598,13 +4845,15 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4598
4845
|
const dbMarker = perf.start("openDatabase");
|
|
4599
4846
|
const db = this.openDatabase();
|
|
4600
4847
|
perf.end(dbMarker);
|
|
4601
|
-
if (!db)
|
|
4848
|
+
if (!db) throw new SessionScanError(this.name, "opening the database");
|
|
4602
4849
|
const wsMarker = perf.start("buildWorkspacePathMap");
|
|
4603
4850
|
const workspacePathMap = this.buildWorkspacePathMap();
|
|
4604
4851
|
perf.end(wsMarker);
|
|
4605
4852
|
try {
|
|
4606
4853
|
const rows = db.prepare("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'composerData:%'").all();
|
|
4607
|
-
const
|
|
4854
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
4855
|
+
const pending2 = [];
|
|
4856
|
+
let order = 0;
|
|
4608
4857
|
options?.onProgress?.({ total: rows.length, processed: 0, sessions: 0 });
|
|
4609
4858
|
let processed = 0;
|
|
4610
4859
|
for (const row of rows) {
|
|
@@ -4620,8 +4869,8 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4620
4869
|
const fastMessageCount = composer.chatMessages?.length ?? 0;
|
|
4621
4870
|
const hasSubagents = Array.isArray(composer.subagentInfos) && composer.subagentInfos.length > 0;
|
|
4622
4871
|
if (options?.fast) {
|
|
4623
|
-
const
|
|
4624
|
-
const
|
|
4872
|
+
const directory = workspacePathMap.get(composerId) ?? "";
|
|
4873
|
+
const totalCost = estimateTokenCost(composer.modelConfig?.modelName ?? composer.model, {
|
|
4625
4874
|
input: composer.inputTokenCount ?? 0,
|
|
4626
4875
|
output: composer.outputTokenCount ?? 0
|
|
4627
4876
|
}) ?? 0;
|
|
@@ -4630,20 +4879,21 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4630
4879
|
id: composerId,
|
|
4631
4880
|
slug: `cursor/${composerId}`,
|
|
4632
4881
|
title: fastTitle,
|
|
4633
|
-
directory
|
|
4882
|
+
directory,
|
|
4634
4883
|
time_created: createdAt,
|
|
4635
4884
|
time_updated: updatedAt || void 0,
|
|
4636
4885
|
stats: {
|
|
4637
4886
|
message_count: fastMessageCount,
|
|
4638
4887
|
total_input_tokens: composer.inputTokenCount ?? 0,
|
|
4639
4888
|
total_output_tokens: composer.outputTokenCount ?? 0,
|
|
4640
|
-
total_cost:
|
|
4641
|
-
cost_source:
|
|
4889
|
+
total_cost: totalCost,
|
|
4890
|
+
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
4642
4891
|
}
|
|
4643
4892
|
})
|
|
4644
4893
|
);
|
|
4645
4894
|
if (!head) continue;
|
|
4646
|
-
|
|
4895
|
+
emitted.set(order, head);
|
|
4896
|
+
order += 1;
|
|
4647
4897
|
this.composerCache.set(composerId, composer);
|
|
4648
4898
|
this.sessionMetaMap.set(composerId, {
|
|
4649
4899
|
id: composerId,
|
|
@@ -4651,74 +4901,38 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4651
4901
|
});
|
|
4652
4902
|
continue;
|
|
4653
4903
|
}
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
const parsedMessages = cleanParsedMessages(
|
|
4657
|
-
this.loadMessagesFromBubbles(
|
|
4658
|
-
db,
|
|
4659
|
-
composerId,
|
|
4660
|
-
sessionId,
|
|
4661
|
-
composer.modelConfig?.modelName ?? composer.model ?? null
|
|
4662
|
-
)
|
|
4663
|
-
);
|
|
4664
|
-
const messages = getParsedSession(
|
|
4665
|
-
parsedMessages.length === 0 && !hasSubagents ? filteredSession("no visible messages") : parsedSession(parsedMessages)
|
|
4666
|
-
);
|
|
4667
|
-
if (!messages) continue;
|
|
4668
|
-
const messageCount = messages.length;
|
|
4669
|
-
const title = this.extractTitle(composer, messages);
|
|
4670
|
-
const directory = workspacePathMap.get(composerId) ?? "";
|
|
4671
|
-
const modelUsageMap = {};
|
|
4672
|
-
let totalCost = 0;
|
|
4673
|
-
for (const msg of messages) {
|
|
4674
|
-
totalCost += msg.cost ?? 0;
|
|
4675
|
-
if (msg.model) {
|
|
4676
|
-
const msgTokens = (msg.tokens?.input ?? 0) + (msg.tokens?.output ?? 0);
|
|
4677
|
-
if (msgTokens > 0) {
|
|
4678
|
-
modelUsageMap[msg.model] = (modelUsageMap[msg.model] ?? 0) + msgTokens;
|
|
4679
|
-
}
|
|
4680
|
-
}
|
|
4681
|
-
}
|
|
4682
|
-
const hasModelUsage = Object.keys(modelUsageMap).length > 0;
|
|
4683
|
-
heads.push({
|
|
4684
|
-
id: sessionId,
|
|
4685
|
-
slug: `cursor/${sessionId}`,
|
|
4686
|
-
title,
|
|
4687
|
-
directory,
|
|
4688
|
-
time_created: createdAt,
|
|
4689
|
-
time_updated: updatedAt || void 0,
|
|
4690
|
-
stats: {
|
|
4691
|
-
message_count: messageCount,
|
|
4692
|
-
total_input_tokens: composer.inputTokenCount ?? 0,
|
|
4693
|
-
total_output_tokens: composer.outputTokenCount ?? 0,
|
|
4694
|
-
total_cost: totalCost,
|
|
4695
|
-
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
4696
|
-
},
|
|
4697
|
-
model_usage: hasModelUsage ? modelUsageMap : void 0
|
|
4698
|
-
});
|
|
4699
|
-
this.composerCache.set(sessionId, composer);
|
|
4700
|
-
this.composerCache.set(`__mapping__${composerId}`, {
|
|
4701
|
-
sessionId
|
|
4702
|
-
});
|
|
4703
|
-
if (directory) {
|
|
4704
|
-
this.composerCache.set(`__dir__${composerId}`, {
|
|
4705
|
-
directory
|
|
4706
|
-
});
|
|
4707
|
-
}
|
|
4708
|
-
this.sessionMetaMap.set(sessionId, {
|
|
4709
|
-
id: sessionId,
|
|
4710
|
-
sourcePath: this.dbPath || ""
|
|
4711
|
-
});
|
|
4904
|
+
pending2.push({ composer, composerId, createdAt, updatedAt, hasSubagents, order });
|
|
4905
|
+
order += 1;
|
|
4712
4906
|
} catch {
|
|
4713
4907
|
} finally {
|
|
4714
4908
|
processed += 1;
|
|
4715
|
-
options?.onProgress?.({ total: rows.length, processed, sessions:
|
|
4909
|
+
options?.onProgress?.({ total: rows.length, processed, sessions: emitted.size });
|
|
4716
4910
|
}
|
|
4717
4911
|
}
|
|
4912
|
+
const bubbleMarker = perf.start("cursor:bubbles");
|
|
4913
|
+
const wanted = new Map(pending2.map((entry) => [entry.composerId, entry]));
|
|
4914
|
+
this.forEachComposerBubbles(db, wanted, (bubbles) => {
|
|
4915
|
+
const entry = wanted.get(bubbles.composerId);
|
|
4916
|
+
if (!entry) return;
|
|
4917
|
+
wanted.delete(bubbles.composerId);
|
|
4918
|
+
const head = this.buildScanHead(entry, bubbles, workspacePathMap);
|
|
4919
|
+
if (head) emitted.set(entry.order, head);
|
|
4920
|
+
});
|
|
4921
|
+
for (const entry of wanted.values()) {
|
|
4922
|
+
const head = this.buildScanHead(
|
|
4923
|
+
entry,
|
|
4924
|
+
{ composerId: entry.composerId, byKey: [], byRowId: [] },
|
|
4925
|
+
workspacePathMap
|
|
4926
|
+
);
|
|
4927
|
+
if (head) emitted.set(entry.order, head);
|
|
4928
|
+
}
|
|
4929
|
+
perf.end(bubbleMarker);
|
|
4930
|
+
const heads = [...emitted.entries()].sort(([left], [right]) => left - right).map(([, head]) => head);
|
|
4931
|
+
options?.onProgress?.({ total: rows.length, processed, sessions: heads.length });
|
|
4718
4932
|
perf.end(scanMarker);
|
|
4719
4933
|
return heads;
|
|
4720
|
-
} catch {
|
|
4721
|
-
|
|
4934
|
+
} catch (error) {
|
|
4935
|
+
throw new SessionScanError(this.name, "reading composers", { cause: error });
|
|
4722
4936
|
} finally {
|
|
4723
4937
|
db.close();
|
|
4724
4938
|
}
|
|
@@ -4806,20 +5020,90 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4806
5020
|
if (!this.dbPath) return null;
|
|
4807
5021
|
return openDbReadOnly(this.dbPath);
|
|
4808
5022
|
}
|
|
4809
|
-
/**
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
5023
|
+
/**
|
|
5024
|
+
* Streams every bubble row once, in key order, handing each composer's group
|
|
5025
|
+
* to the caller. A key-ordered scan keeps a composer's bubbles contiguous, so
|
|
5026
|
+
* only one group is materialized at a time.
|
|
5027
|
+
*/
|
|
5028
|
+
forEachComposerBubbles(db, wanted, onGroup) {
|
|
5029
|
+
if (wanted.size === 0) return;
|
|
5030
|
+
let currentId = null;
|
|
5031
|
+
let group = [];
|
|
5032
|
+
const flush = () => {
|
|
5033
|
+
if (currentId != null && group.length > 0) onGroup(groupBubbleRows(group));
|
|
5034
|
+
group = [];
|
|
5035
|
+
};
|
|
5036
|
+
const rows = db.prepare(
|
|
5037
|
+
"SELECT rowid AS row_id, key, value FROM cursorDiskKV WHERE key LIKE 'bubbleId:%' ORDER BY key"
|
|
5038
|
+
).iterate();
|
|
5039
|
+
for (const row of rows) {
|
|
5040
|
+
const composerId = composerIdFromBubbleKey(row.key);
|
|
5041
|
+
if (composerId !== currentId) {
|
|
5042
|
+
flush();
|
|
5043
|
+
currentId = composerId;
|
|
5044
|
+
}
|
|
5045
|
+
if (wanted.has(composerId)) group.push(row);
|
|
5046
|
+
}
|
|
5047
|
+
flush();
|
|
5048
|
+
}
|
|
5049
|
+
/** Builds the head for one composer from bubbles that were parsed once. */
|
|
5050
|
+
buildScanHead(entry, bubbles, workspacePathMap) {
|
|
5051
|
+
const { composer, composerId, createdAt, updatedAt, hasSubagents } = entry;
|
|
5052
|
+
const requestId = this.requestIdFromBubbles(bubbles);
|
|
5053
|
+
const sessionId = requestId || composerId;
|
|
5054
|
+
const parsedMessages = cleanParsedMessages(
|
|
5055
|
+
this.messagesFromBubbles(bubbles, composer.modelConfig?.modelName ?? composer.model ?? null)
|
|
5056
|
+
);
|
|
5057
|
+
const messages = getParsedSession(
|
|
5058
|
+
parsedMessages.length === 0 && !hasSubagents ? filteredSession("no visible messages") : parsedSession(parsedMessages)
|
|
5059
|
+
);
|
|
5060
|
+
if (!messages) return null;
|
|
5061
|
+
const title = this.extractTitle(composer, messages);
|
|
5062
|
+
const directory = workspacePathMap.get(composerId) ?? "";
|
|
5063
|
+
const modelUsageMap = {};
|
|
5064
|
+
let totalCost = 0;
|
|
5065
|
+
for (const msg of messages) {
|
|
5066
|
+
totalCost += msg.cost ?? 0;
|
|
5067
|
+
if (msg.model) {
|
|
5068
|
+
const msgTokens = (msg.tokens?.input ?? 0) + (msg.tokens?.output ?? 0);
|
|
5069
|
+
if (msgTokens > 0) {
|
|
5070
|
+
modelUsageMap[msg.model] = (modelUsageMap[msg.model] ?? 0) + msgTokens;
|
|
4820
5071
|
}
|
|
4821
5072
|
}
|
|
4822
|
-
}
|
|
5073
|
+
}
|
|
5074
|
+
const hasModelUsage = Object.keys(modelUsageMap).length > 0;
|
|
5075
|
+
this.composerCache.set(sessionId, composer);
|
|
5076
|
+
this.composerCache.set(`__mapping__${composerId}`, {
|
|
5077
|
+
sessionId
|
|
5078
|
+
});
|
|
5079
|
+
if (directory) {
|
|
5080
|
+
this.composerCache.set(`__dir__${composerId}`, {
|
|
5081
|
+
directory
|
|
5082
|
+
});
|
|
5083
|
+
}
|
|
5084
|
+
this.sessionMetaMap.set(sessionId, { id: sessionId, sourcePath: this.dbPath || "" });
|
|
5085
|
+
return {
|
|
5086
|
+
id: sessionId,
|
|
5087
|
+
slug: `cursor/${sessionId}`,
|
|
5088
|
+
title,
|
|
5089
|
+
directory,
|
|
5090
|
+
time_created: createdAt,
|
|
5091
|
+
time_updated: updatedAt || void 0,
|
|
5092
|
+
stats: {
|
|
5093
|
+
message_count: messages.length,
|
|
5094
|
+
total_input_tokens: composer.inputTokenCount ?? 0,
|
|
5095
|
+
total_output_tokens: composer.outputTokenCount ?? 0,
|
|
5096
|
+
total_cost: totalCost,
|
|
5097
|
+
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
5098
|
+
},
|
|
5099
|
+
model_usage: hasModelUsage ? modelUsageMap : void 0
|
|
5100
|
+
};
|
|
5101
|
+
}
|
|
5102
|
+
/** First request id in key order, matching what agent-dump reports. */
|
|
5103
|
+
requestIdFromBubbles(bubbles) {
|
|
5104
|
+
for (const entry of bubbles.byKey) {
|
|
5105
|
+
const requestId = entry.bubble.requestId?.trim();
|
|
5106
|
+
if (requestId) return requestId;
|
|
4823
5107
|
}
|
|
4824
5108
|
return null;
|
|
4825
5109
|
}
|
|
@@ -4868,18 +5152,26 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4868
5152
|
return 0;
|
|
4869
5153
|
}
|
|
4870
5154
|
}
|
|
4871
|
-
/** Load
|
|
5155
|
+
/** Load one composer's bubbles, for the detail path that only needs a single session. */
|
|
4872
5156
|
loadMessagesFromBubbles(db, composerId, _sessionId, initialModelName) {
|
|
4873
|
-
const messages = [];
|
|
4874
5157
|
try {
|
|
4875
|
-
const rows = db.prepare("SELECT key, value FROM cursorDiskKV WHERE key LIKE ?
|
|
5158
|
+
const rows = db.prepare("SELECT rowid AS row_id, key, value FROM cursorDiskKV WHERE key LIKE ?").all(`bubbleId:${composerId}:%`);
|
|
5159
|
+
return this.messagesFromBubbles(groupBubbleRows(rows), initialModelName);
|
|
5160
|
+
} catch {
|
|
5161
|
+
return [];
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
/** Build messages from bubbles already parsed once, in insertion order. */
|
|
5165
|
+
messagesFromBubbles(bubbles, initialModelName) {
|
|
5166
|
+
const messages = [];
|
|
5167
|
+
{
|
|
4876
5168
|
let activeModelName = initialModelName;
|
|
4877
5169
|
let messageIndex = 0;
|
|
4878
|
-
for (const
|
|
4879
|
-
|
|
4880
|
-
const bubble =
|
|
4881
|
-
if (
|
|
4882
|
-
const bubbleId =
|
|
5170
|
+
for (const entry of bubbles.byRowId) {
|
|
5171
|
+
{
|
|
5172
|
+
const bubble = entry.bubble;
|
|
5173
|
+
if (isInternalBubble(bubble)) continue;
|
|
5174
|
+
const bubbleId = entry.key.split(":").pop() || String(messageIndex);
|
|
4883
5175
|
const role = bubble.type === 2 ? "assistant" : "user";
|
|
4884
5176
|
let timestampMs = 0;
|
|
4885
5177
|
if (bubble.timingInfo?.clientRpcSendTime) {
|
|
@@ -4910,7 +5202,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4910
5202
|
const tokens = { input: inputTokens, output: outputTokens };
|
|
4911
5203
|
const cost = estimateTokenCost(modelName, tokens);
|
|
4912
5204
|
messages.push({
|
|
4913
|
-
id: `cursor-${composerId}-${bubbleId}`,
|
|
5205
|
+
id: `cursor-${bubbles.composerId}-${bubbleId}`,
|
|
4914
5206
|
role,
|
|
4915
5207
|
agent: "cursor",
|
|
4916
5208
|
time_created: timestampMs,
|
|
@@ -4924,10 +5216,8 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4924
5216
|
parts
|
|
4925
5217
|
});
|
|
4926
5218
|
messageIndex++;
|
|
4927
|
-
} catch {
|
|
4928
5219
|
}
|
|
4929
5220
|
}
|
|
4930
|
-
} catch {
|
|
4931
5221
|
}
|
|
4932
5222
|
return messages;
|
|
4933
5223
|
}
|
|
@@ -5063,7 +5353,7 @@ function narrowTimestampMs(field, value) {
|
|
|
5063
5353
|
return shaped === void 0 ? 0 : parseTimestampMs3(shaped);
|
|
5064
5354
|
}
|
|
5065
5355
|
function extractSessionIdFromFilename(filePath) {
|
|
5066
|
-
const stem =
|
|
5356
|
+
const stem = basename7(filePath, ".jsonl");
|
|
5067
5357
|
const underscore = stem.indexOf("_");
|
|
5068
5358
|
return underscore >= 0 ? stem.slice(underscore + 1) || stem : stem;
|
|
5069
5359
|
}
|
|
@@ -5119,14 +5409,14 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5119
5409
|
displayName = "Pi";
|
|
5120
5410
|
basePath = null;
|
|
5121
5411
|
findBasePath() {
|
|
5122
|
-
return firstExisting(
|
|
5412
|
+
return firstExisting(join11(resolvePiDataRoot(), "agent", "sessions"), "data/pi");
|
|
5123
5413
|
}
|
|
5124
5414
|
getSessionWatchPlan() {
|
|
5125
5415
|
const dataRoot = resolvePiDataRoot();
|
|
5126
5416
|
return {
|
|
5127
5417
|
status: "supported",
|
|
5128
5418
|
targets: [
|
|
5129
|
-
{ root: dataRoot, path:
|
|
5419
|
+
{ root: dataRoot, path: join11(dataRoot, "agent", "sessions") },
|
|
5130
5420
|
{ root: "data/pi", path: "data/pi" }
|
|
5131
5421
|
]
|
|
5132
5422
|
};
|
|
@@ -5147,7 +5437,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5147
5437
|
getSessionData(sessionId) {
|
|
5148
5438
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
5149
5439
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
5150
|
-
if (!
|
|
5440
|
+
if (!existsSync10(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
5151
5441
|
const parsed = this.parsePiFile(meta.sourcePath);
|
|
5152
5442
|
const state = this.convertEntries(parsed.pathEntries);
|
|
5153
5443
|
return {
|
|
@@ -5240,7 +5530,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5240
5530
|
const sessionId = String(header["id"] ?? extractSessionIdFromFilename(filePath)).trim();
|
|
5241
5531
|
if (!sessionId) throw new Error("missing session id");
|
|
5242
5532
|
const stat = this.sessionSourceFile(filePath).stat;
|
|
5243
|
-
const directory = String(header["cwd"] ?? "").trim() ||
|
|
5533
|
+
const directory = String(header["cwd"] ?? "").trim() || basename7(filePath, ".jsonl");
|
|
5244
5534
|
const createdAt = narrowTimestampMs("session.timestamp", header["timestamp"]) || stat.mtimeMs;
|
|
5245
5535
|
const updatedAt = pathEntries.reduce(
|
|
5246
5536
|
(max, entry) => Math.max(max, getEntryTimestamp(entry)),
|
|
@@ -5490,19 +5780,19 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
5490
5780
|
function resolveZCodeDataRoot() {
|
|
5491
5781
|
const currentPlatform = platform3();
|
|
5492
5782
|
if (currentPlatform !== "darwin" && currentPlatform !== "win32") return null;
|
|
5493
|
-
return
|
|
5783
|
+
return join12(homedir4(), ".zcode");
|
|
5494
5784
|
}
|
|
5495
5785
|
function findZCodeDbPath() {
|
|
5496
5786
|
if (!isSqliteAvailable()) return null;
|
|
5497
5787
|
const dataRoot = resolveZCodeDataRoot();
|
|
5498
5788
|
if (!dataRoot) return null;
|
|
5499
|
-
return firstExisting(
|
|
5789
|
+
return firstExisting(join12(dataRoot, "cli", "db", "db.sqlite"));
|
|
5500
5790
|
}
|
|
5501
5791
|
function getZCodeSessionWatchPlan() {
|
|
5502
5792
|
const dataRoot = resolveZCodeDataRoot();
|
|
5503
5793
|
return {
|
|
5504
5794
|
status: "supported",
|
|
5505
|
-
targets: dataRoot ? [{ root: dataRoot, path:
|
|
5795
|
+
targets: dataRoot ? [{ root: dataRoot, path: join12(dataRoot, "cli", "db", "db.sqlite") }] : []
|
|
5506
5796
|
};
|
|
5507
5797
|
}
|
|
5508
5798
|
var ZCodeAgent = class extends OpenCodeSqliteAgent {
|
|
@@ -5573,7 +5863,7 @@ function fallbackDisplayName(input) {
|
|
|
5573
5863
|
}
|
|
5574
5864
|
var realFs = {
|
|
5575
5865
|
exists(path2) {
|
|
5576
|
-
return
|
|
5866
|
+
return existsSync11(path2);
|
|
5577
5867
|
},
|
|
5578
5868
|
readText(path2) {
|
|
5579
5869
|
try {
|
|
@@ -5602,23 +5892,6 @@ var MANIFESTS = [
|
|
|
5602
5892
|
var PARSEABLE_MANIFESTS = ["package.json", "Cargo.toml", "pyproject.toml"];
|
|
5603
5893
|
var LOOSE_DIRS = /* @__PURE__ */ new Set(["/tmp", "/private/tmp"]);
|
|
5604
5894
|
var LOOSE_HOME_DIRS = ["Desktop", "Downloads", "Documents"];
|
|
5605
|
-
var PROJECT_IDENTITY_KINDS = /* @__PURE__ */ new Set([
|
|
5606
|
-
"git_remote",
|
|
5607
|
-
"git_common_dir",
|
|
5608
|
-
"manifest_path",
|
|
5609
|
-
"synthetic",
|
|
5610
|
-
"path",
|
|
5611
|
-
"loose"
|
|
5612
|
-
]);
|
|
5613
|
-
function isProjectIdentityKind(value) {
|
|
5614
|
-
return PROJECT_IDENTITY_KINDS.has(value);
|
|
5615
|
-
}
|
|
5616
|
-
function getProjectIdentityKey(identity) {
|
|
5617
|
-
return `${identity.kind}:${identity.key}`;
|
|
5618
|
-
}
|
|
5619
|
-
function matchesProjectIdentity(identity, expected) {
|
|
5620
|
-
return identity?.kind === expected.kind && identity.key === expected.key;
|
|
5621
|
-
}
|
|
5622
5895
|
function normalizeGitRemote(url) {
|
|
5623
5896
|
if (!url) return null;
|
|
5624
5897
|
let value = url.trim().replace(/\.git$/, "");
|
|
@@ -6092,16 +6365,16 @@ function setSchemaEnsuredPath(path2) {
|
|
|
6092
6365
|
schemaEnsuredPath = path2;
|
|
6093
6366
|
}
|
|
6094
6367
|
function getCacheDir2() {
|
|
6095
|
-
return
|
|
6368
|
+
return join13(homedir6(), ".cache", "codesesh");
|
|
6096
6369
|
}
|
|
6097
6370
|
function getCachePath2() {
|
|
6098
|
-
return
|
|
6371
|
+
return join13(getCacheDir2(), CACHE_FILENAME);
|
|
6099
6372
|
}
|
|
6100
6373
|
function getLegacyCachePath() {
|
|
6101
|
-
return
|
|
6374
|
+
return join13(getCacheDir2(), LEGACY_CACHE_FILENAME);
|
|
6102
6375
|
}
|
|
6103
6376
|
function hasCacheStorage() {
|
|
6104
|
-
return
|
|
6377
|
+
return existsSync12(getCachePath2());
|
|
6105
6378
|
}
|
|
6106
6379
|
function likePattern(value) {
|
|
6107
6380
|
return `%${value.trim().toLowerCase().replace(/[\\%_]/g, "\\$&")}%`;
|
|
@@ -8445,55 +8718,90 @@ function buildFileActivityWhere(options) {
|
|
|
8445
8718
|
params
|
|
8446
8719
|
};
|
|
8447
8720
|
}
|
|
8721
|
+
var FILE_ACTIVITY_COLUMNS = `
|
|
8722
|
+
fa.agent_name,
|
|
8723
|
+
fa.session_id,
|
|
8724
|
+
fa.project_identity_key,
|
|
8725
|
+
fa.path,
|
|
8726
|
+
fa.kind,
|
|
8727
|
+
fa.count,
|
|
8728
|
+
fa.latest_time,
|
|
8729
|
+
s.slug,
|
|
8730
|
+
s.title,
|
|
8731
|
+
s.directory,
|
|
8732
|
+
s.project_identity_kind,
|
|
8733
|
+
s.project_display_name,
|
|
8734
|
+
s.time_created,
|
|
8735
|
+
s.time_updated,
|
|
8736
|
+
s.message_count,
|
|
8737
|
+
s.total_input_tokens,
|
|
8738
|
+
s.total_output_tokens,
|
|
8739
|
+
s.total_cache_read_tokens,
|
|
8740
|
+
s.total_cache_create_tokens,
|
|
8741
|
+
s.total_cost,
|
|
8742
|
+
s.cost_source,
|
|
8743
|
+
s.total_tokens,
|
|
8744
|
+
s.model_usage_json,
|
|
8745
|
+
s.smart_tags_json,
|
|
8746
|
+
s.smart_tags_source_updated_at
|
|
8747
|
+
`;
|
|
8748
|
+
var FILE_ACTIVITY_JOIN = `
|
|
8749
|
+
FROM session_file_activity fa
|
|
8750
|
+
JOIN sessions s ON s.agent_name = fa.agent_name AND s.session_id = fa.session_id
|
|
8751
|
+
`;
|
|
8752
|
+
var FILE_ACTIVITY_ORDER = "fa.latest_time DESC, fa.count DESC, fa.path";
|
|
8448
8753
|
function listFileActivity(options = {}) {
|
|
8449
8754
|
return queryFileActivity(options);
|
|
8450
8755
|
}
|
|
8451
|
-
function
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
const filters = buildFileActivityWhere(options);
|
|
8456
|
-
const sessionFilters = sessionSearchOptions ? buildSessionSearchFilters(sessionSearchOptions) : { where: "", params: [] };
|
|
8457
|
-
const whereClauses = [
|
|
8756
|
+
function fileActivityWhere(query) {
|
|
8757
|
+
const filters = buildFileActivityWhere(query.options);
|
|
8758
|
+
const sessionFilters = query.sessionSearchOptions ? buildSessionSearchFilters(query.sessionSearchOptions) : { where: "", params: [] };
|
|
8759
|
+
const clauses = [
|
|
8458
8760
|
filters.where.replace(/^WHERE /, ""),
|
|
8459
8761
|
sessionFilters.where.replace(/^ AND /, "")
|
|
8460
8762
|
].filter(Boolean);
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8464
|
-
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8479
|
-
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
)
|
|
8763
|
+
return {
|
|
8764
|
+
where: clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "",
|
|
8765
|
+
params: [...filters.params, ...sessionFilters.params]
|
|
8766
|
+
};
|
|
8767
|
+
}
|
|
8768
|
+
function fileActivitySql(query, where) {
|
|
8769
|
+
if (!query.onePerSession) {
|
|
8770
|
+
return `
|
|
8771
|
+
SELECT ${FILE_ACTIVITY_COLUMNS}
|
|
8772
|
+
${FILE_ACTIVITY_JOIN}
|
|
8773
|
+
${where}
|
|
8774
|
+
ORDER BY ${FILE_ACTIVITY_ORDER}
|
|
8775
|
+
LIMIT ?
|
|
8776
|
+
`;
|
|
8777
|
+
}
|
|
8778
|
+
return `
|
|
8779
|
+
SELECT ${FILE_ACTIVITY_COLUMNS}
|
|
8780
|
+
FROM (
|
|
8781
|
+
SELECT
|
|
8782
|
+
fa.rowid AS activity_rowid,
|
|
8783
|
+
ROW_NUMBER() OVER (
|
|
8784
|
+
PARTITION BY fa.agent_name, fa.session_id
|
|
8785
|
+
ORDER BY ${FILE_ACTIVITY_ORDER}
|
|
8786
|
+
) AS session_rank
|
|
8787
|
+
${FILE_ACTIVITY_JOIN}
|
|
8788
|
+
${where}
|
|
8789
|
+
) ranked
|
|
8790
|
+
JOIN session_file_activity fa ON fa.rowid = ranked.activity_rowid
|
|
8791
|
+
JOIN sessions s ON s.agent_name = fa.agent_name AND s.session_id = fa.session_id
|
|
8792
|
+
WHERE ranked.session_rank = 1
|
|
8793
|
+
ORDER BY ${FILE_ACTIVITY_ORDER}
|
|
8794
|
+
LIMIT ?
|
|
8795
|
+
`;
|
|
8796
|
+
}
|
|
8797
|
+
function queryFileActivity(options, sessionSearchOptions, onePerSession = false) {
|
|
8798
|
+
if (!hasCacheStorage()) {
|
|
8799
|
+
return [];
|
|
8800
|
+
}
|
|
8801
|
+
const query = { options, sessionSearchOptions, onePerSession };
|
|
8802
|
+
const { where, params } = fileActivityWhere(query);
|
|
8803
|
+
const sql = fileActivitySql(query, where);
|
|
8804
|
+
const queryRows = (db) => db.prepare(sql).all(...params, options.limit ?? 50);
|
|
8497
8805
|
let rows = withCacheDbReadOnly(queryRows);
|
|
8498
8806
|
if (rows == null && options.path) {
|
|
8499
8807
|
rows = withCacheDb(queryRows);
|
|
@@ -8526,25 +8834,17 @@ function searchFileActivitySessions(query, options = {}) {
|
|
|
8526
8834
|
{
|
|
8527
8835
|
path: path2,
|
|
8528
8836
|
kind: search.options.fileKind,
|
|
8529
|
-
limit:
|
|
8837
|
+
limit: search.options.limit ?? 50
|
|
8530
8838
|
},
|
|
8531
|
-
search.options
|
|
8839
|
+
search.options,
|
|
8840
|
+
true
|
|
8532
8841
|
);
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
results.push({
|
|
8540
|
-
reference: row.reference,
|
|
8541
|
-
session: row.session,
|
|
8542
|
-
snippet: `${row.kind} ${highlightFilePath(row.path, path2)} \xB7 ${row.count} events`,
|
|
8543
|
-
matchType: "file_path"
|
|
8544
|
-
});
|
|
8545
|
-
if (results.length >= (search.options.limit ?? 50)) break;
|
|
8546
|
-
}
|
|
8547
|
-
return results;
|
|
8842
|
+
return rows.map((row) => ({
|
|
8843
|
+
reference: row.reference,
|
|
8844
|
+
session: row.session,
|
|
8845
|
+
snippet: `${row.kind} ${highlightFilePath(row.path, path2)} \xB7 ${row.count} events`,
|
|
8846
|
+
matchType: "file_path"
|
|
8847
|
+
}));
|
|
8548
8848
|
}
|
|
8549
8849
|
var CACHE_INITIALIZATION_VERSION = "session-cache-v2";
|
|
8550
8850
|
function parseCachedSessionMeta(value) {
|
|
@@ -8557,7 +8857,7 @@ function parseCachedSessionMeta(value) {
|
|
|
8557
8857
|
}
|
|
8558
8858
|
function deleteLegacyCacheFile() {
|
|
8559
8859
|
const legacyPath = getLegacyCachePath();
|
|
8560
|
-
if (!
|
|
8860
|
+
if (!existsSync13(legacyPath)) {
|
|
8561
8861
|
return;
|
|
8562
8862
|
}
|
|
8563
8863
|
try {
|
|
@@ -8757,7 +9057,7 @@ function loadCachedSessionRawEntry(agentName, sessionId) {
|
|
|
8757
9057
|
});
|
|
8758
9058
|
}
|
|
8759
9059
|
function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
8760
|
-
withCacheDb((db) => {
|
|
9060
|
+
const persisted = withCacheDb((db) => {
|
|
8761
9061
|
const deleteSession = db.prepare(
|
|
8762
9062
|
"DELETE FROM sessions WHERE agent_name = ? AND session_id = ?"
|
|
8763
9063
|
);
|
|
@@ -8809,13 +9109,15 @@ function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
|
8809
9109
|
});
|
|
8810
9110
|
write();
|
|
8811
9111
|
deleteLegacyCacheFile();
|
|
9112
|
+
return true;
|
|
8812
9113
|
});
|
|
9114
|
+
return persisted ?? false;
|
|
8813
9115
|
}
|
|
8814
9116
|
function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta = {}) {
|
|
8815
9117
|
if (changes.length === 0 && removedSessionIds.length === 0) {
|
|
8816
|
-
return;
|
|
9118
|
+
return true;
|
|
8817
9119
|
}
|
|
8818
|
-
withCacheDb((db) => {
|
|
9120
|
+
const persisted = withCacheDb((db) => {
|
|
8819
9121
|
const deleteSession = db.prepare(
|
|
8820
9122
|
"DELETE FROM sessions WHERE agent_name = ? AND session_id = ?"
|
|
8821
9123
|
);
|
|
@@ -8861,7 +9163,9 @@ function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta =
|
|
|
8861
9163
|
});
|
|
8862
9164
|
write();
|
|
8863
9165
|
deleteLegacyCacheFile();
|
|
9166
|
+
return true;
|
|
8864
9167
|
});
|
|
9168
|
+
return persisted ?? false;
|
|
8865
9169
|
}
|
|
8866
9170
|
function clearCache() {
|
|
8867
9171
|
setSchemaEnsuredPath(null);
|
|
@@ -8887,11 +9191,11 @@ function clearCache() {
|
|
|
8887
9191
|
const walPath = `${cachePath}-wal`;
|
|
8888
9192
|
const shmPath = `${cachePath}-shm`;
|
|
8889
9193
|
for (const filePath of [walPath, shmPath]) {
|
|
8890
|
-
if (!
|
|
9194
|
+
if (!existsSync13(filePath)) {
|
|
8891
9195
|
continue;
|
|
8892
9196
|
}
|
|
8893
9197
|
try {
|
|
8894
|
-
|
|
9198
|
+
rmSync2(filePath, { force: true });
|
|
8895
9199
|
} catch {
|
|
8896
9200
|
}
|
|
8897
9201
|
}
|
|
@@ -9231,11 +9535,17 @@ async function scanAgentFull(agent, options, onProgress, timing = { total: 0 },
|
|
|
9231
9535
|
timing.tags = performance.now() - t2;
|
|
9232
9536
|
const meta = buildAgentCacheMeta(agent);
|
|
9233
9537
|
if (options.writeCache !== false) {
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9538
|
+
const isFullWindow = options.from == null && options.to == null;
|
|
9539
|
+
const persisted = isFullWindow ? saveCachedSessions(agent.name, tagged.sessions, meta) : true;
|
|
9540
|
+
if (persisted) {
|
|
9541
|
+
if (isFullWindow) markAgentFullSyncCompleted(agent.name);
|
|
9542
|
+
markAgentCacheInitialized(agent.name);
|
|
9543
|
+
} else {
|
|
9544
|
+
getCoreDiagnostics()?.warn("cache.save_failed", {
|
|
9545
|
+
agent: agent.name,
|
|
9546
|
+
sessions: tagged.sessions.length
|
|
9547
|
+
});
|
|
9237
9548
|
}
|
|
9238
|
-
markAgentCacheInitialized(agent.name);
|
|
9239
9549
|
}
|
|
9240
9550
|
onProgress?.({ agent: agent.name, phase: "complete", newCount: tagged.sessions.length });
|
|
9241
9551
|
const filtered = filterSessions(tagged.sessions, options);
|
|
@@ -9440,16 +9750,16 @@ function getStateDir() {
|
|
|
9440
9750
|
if (process.env.CODESESH_STATE_DIR) return process.env.CODESESH_STATE_DIR;
|
|
9441
9751
|
const currentPlatform = platform4();
|
|
9442
9752
|
if (currentPlatform === "darwin") {
|
|
9443
|
-
return
|
|
9753
|
+
return join14(homedir7(), "Library", "Application Support", "codesesh");
|
|
9444
9754
|
}
|
|
9445
9755
|
if (currentPlatform === "win32") {
|
|
9446
9756
|
const appData = process.env.APPDATA ?? process.env.LOCALAPPDATA;
|
|
9447
|
-
return
|
|
9757
|
+
return join14(appData ?? join14(homedir7(), "AppData", "Roaming"), "codesesh");
|
|
9448
9758
|
}
|
|
9449
|
-
return
|
|
9759
|
+
return join14(process.env.XDG_DATA_HOME ?? join14(homedir7(), ".local", "share"), "codesesh");
|
|
9450
9760
|
}
|
|
9451
9761
|
function getStateDbPath() {
|
|
9452
|
-
return
|
|
9762
|
+
return join14(getStateDir(), STATE_DB_FILENAME);
|
|
9453
9763
|
}
|
|
9454
9764
|
function useMemoryStateStore() {
|
|
9455
9765
|
return process.env.CODESESH_STATE_STORE === MEMORY_STATE_STORE;
|
|
@@ -9866,17 +10176,6 @@ function getSessionAgentName(session) {
|
|
|
9866
10176
|
function getSessionActivityTime(session) {
|
|
9867
10177
|
return session.time_updated ?? session.time_created;
|
|
9868
10178
|
}
|
|
9869
|
-
function toLocalDateKey(ts) {
|
|
9870
|
-
const d = new Date(ts);
|
|
9871
|
-
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(
|
|
9872
|
-
d.getDate()
|
|
9873
|
-
).padStart(2, "0")}`;
|
|
9874
|
-
}
|
|
9875
|
-
function startOfLocalDay(ts) {
|
|
9876
|
-
const d = new Date(ts);
|
|
9877
|
-
d.setHours(0, 0, 0, 0);
|
|
9878
|
-
return d.getTime();
|
|
9879
|
-
}
|
|
9880
10179
|
function buildDashboard(sessions, options) {
|
|
9881
10180
|
const { byAgentNames, scope, from, to, agentInfoMap } = options;
|
|
9882
10181
|
const agentMetrics = /* @__PURE__ */ new Map();
|
|
@@ -9897,11 +10196,9 @@ function buildDashboard(sessions, options) {
|
|
|
9897
10196
|
const dailyMap = /* @__PURE__ */ new Map();
|
|
9898
10197
|
const dailyTokenMap = /* @__PURE__ */ new Map();
|
|
9899
10198
|
if (from != null) {
|
|
9900
|
-
const
|
|
9901
|
-
const bucketDays = Math.floor((startOfLocalDay(to) - bucketStart) / 864e5) + 1;
|
|
10199
|
+
const bucketDays = countCalendarDays(from, to);
|
|
9902
10200
|
for (let i = 0; i < bucketDays; i += 1) {
|
|
9903
|
-
const
|
|
9904
|
-
const key = toLocalDateKey(ts);
|
|
10201
|
+
const key = toCalendarDayKey(addCalendarDays(from, i));
|
|
9905
10202
|
dailyMap.set(key, { date: key, sessions: 0, messages: 0 });
|
|
9906
10203
|
dailyTokenMap.set(key, { date: key, input: 0, output: 0, cache_read: 0, cache_create: 0 });
|
|
9907
10204
|
}
|
|
@@ -9933,7 +10230,7 @@ function buildDashboard(sessions, options) {
|
|
|
9933
10230
|
metric.messages += messageCount;
|
|
9934
10231
|
metric.tokens += sessionTokens;
|
|
9935
10232
|
}
|
|
9936
|
-
const key =
|
|
10233
|
+
const key = toCalendarDayKey(activity);
|
|
9937
10234
|
let bucket = dailyMap.get(key);
|
|
9938
10235
|
if (!bucket) {
|
|
9939
10236
|
bucket = { date: key, sessions: 0, messages: 0 };
|
|
@@ -10016,9 +10313,6 @@ function buildDashboard(sessions, options) {
|
|
|
10016
10313
|
recentSessions
|
|
10017
10314
|
};
|
|
10018
10315
|
}
|
|
10019
|
-
function getProjectGroupKey(identityKind, identityKey) {
|
|
10020
|
-
return `${identityKind}:${identityKey}`;
|
|
10021
|
-
}
|
|
10022
10316
|
function emptyMetrics() {
|
|
10023
10317
|
return { messages: 0, tokens: 0, cost: 0, hasEstimatedCost: false, agentStats: /* @__PURE__ */ new Map() };
|
|
10024
10318
|
}
|
|
@@ -10027,7 +10321,7 @@ function attachProjectMetrics(projects, sessions) {
|
|
|
10027
10321
|
for (const session of sessions) {
|
|
10028
10322
|
const identity = session.project_identity;
|
|
10029
10323
|
if (!identity) continue;
|
|
10030
|
-
const key =
|
|
10324
|
+
const key = getProjectIdentityKey(identity);
|
|
10031
10325
|
let current = metrics.get(key);
|
|
10032
10326
|
if (!current) {
|
|
10033
10327
|
current = emptyMetrics();
|
|
@@ -10057,7 +10351,9 @@ function attachProjectMetrics(projects, sessions) {
|
|
|
10057
10351
|
}
|
|
10058
10352
|
}
|
|
10059
10353
|
return projects.map((project) => {
|
|
10060
|
-
const metric = metrics.get(
|
|
10354
|
+
const metric = metrics.get(
|
|
10355
|
+
getProjectIdentityKey({ kind: project.identityKind, key: project.identityKey })
|
|
10356
|
+
);
|
|
10061
10357
|
return {
|
|
10062
10358
|
...project,
|
|
10063
10359
|
messages: metric?.messages ?? 0,
|
|
@@ -10194,9 +10490,16 @@ function searchIndexedSessions(query, textQuery, parsed, options) {
|
|
|
10194
10490
|
}
|
|
10195
10491
|
|
|
10196
10492
|
export {
|
|
10493
|
+
mergeSessionsUpdatedEvents,
|
|
10494
|
+
startOfCalendarDay,
|
|
10495
|
+
addCalendarDays,
|
|
10496
|
+
countCalendarDays,
|
|
10497
|
+
isProjectIdentityKind,
|
|
10498
|
+
matchesProjectIdentity,
|
|
10197
10499
|
normalizeSessionReference,
|
|
10198
10500
|
formatSessionReference,
|
|
10199
10501
|
getSessionAgentKey,
|
|
10502
|
+
sessionRoutePath,
|
|
10200
10503
|
mergeSortedSessions,
|
|
10201
10504
|
registerAgent,
|
|
10202
10505
|
createRegisteredAgents,
|
|
@@ -10206,10 +10509,14 @@ export {
|
|
|
10206
10509
|
diffSessionSources,
|
|
10207
10510
|
BaseAgent,
|
|
10208
10511
|
FileSystemSessionSource,
|
|
10512
|
+
ensurePrivateDirectory,
|
|
10513
|
+
restrictPrivateFile,
|
|
10514
|
+
restrictExistingPrivateFiles,
|
|
10515
|
+
getPricingGeneration,
|
|
10516
|
+
publishPendingPricing,
|
|
10517
|
+
hasPendingPricing,
|
|
10209
10518
|
refreshPricingCache,
|
|
10210
10519
|
perf,
|
|
10211
|
-
isProjectIdentityKind,
|
|
10212
|
-
matchesProjectIdentity,
|
|
10213
10520
|
createProjectScopeMatcher,
|
|
10214
10521
|
matchesProjectScope,
|
|
10215
10522
|
getSmartTagSourceTimestamp,
|
|
@@ -10246,10 +10553,9 @@ export {
|
|
|
10246
10553
|
upsertSessionAlias,
|
|
10247
10554
|
deleteSessionAlias,
|
|
10248
10555
|
getSessionActivityTime,
|
|
10249
|
-
startOfLocalDay,
|
|
10250
10556
|
buildDashboard,
|
|
10251
10557
|
attachProjectMetrics,
|
|
10252
10558
|
executeSessionSearch,
|
|
10253
10559
|
filterSessionSearchCandidates
|
|
10254
10560
|
};
|
|
10255
|
-
//# sourceMappingURL=chunk-
|
|
10561
|
+
//# sourceMappingURL=chunk-HIYT2ZJL.js.map
|