codesesh 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-VAUC2W7I.js → chunk-PSHZZITB.js} +1919 -731
- package/dist/chunk-PSHZZITB.js.map +1 -0
- package/dist/chunk-RF3EEWPH.js +17 -0
- package/dist/chunk-RF3EEWPH.js.map +1 -0
- package/dist/{chunk-PROMURPM.js → chunk-WAKW5OPS.js} +2 -2
- package/dist/{dist-6QTGTYZT.js → dist-RHDY2EZX.js} +18 -20
- package/dist/index.js +1291 -672
- package/dist/index.js.map +1 -1
- package/dist/project-identity-worker.js +27 -0
- package/dist/project-identity-worker.js.map +1 -0
- package/dist/scan-refresh-worker.js +15 -6
- package/dist/scan-refresh-worker.js.map +1 -1
- package/dist/search-index-worker.js +16 -6
- package/dist/search-index-worker.js.map +1 -1
- package/dist/smart-tag-worker.js +2 -2
- package/dist/web/assets/{ErrorBoundary-D11tWzfn.js → ErrorBoundary-DIwwrdlw.js} +1 -1
- package/dist/web/assets/InteractiveReceipt-DmNBDVms.js +1 -0
- package/dist/web/assets/{OverviewScreen-DriPmCam.js → OverviewScreen-B5JekBLC.js} +1 -1
- package/dist/web/assets/Projects-Bj6ma2k2.js +1 -0
- package/dist/web/assets/{SearchFilterBar-Bx73B5e9.js → SearchFilterBar-B9VrUbym.js} +1 -1
- package/dist/web/assets/{SearchResultsPanel-A6_3TPhW.js → SearchResultsPanel-C1cuuOKq.js} +1 -1
- package/dist/web/assets/{SessionDetail-BnpvDwtH.js → SessionDetail-Dc2CQ3hH.js} +5 -5
- package/dist/web/assets/contract-DXvltqJG.js +1 -0
- package/dist/web/assets/index-3UfOiTBO.css +1 -0
- package/dist/web/assets/index-DBMiEd61.js +1410 -0
- package/dist/web/assets/panel-CLnL4kMN.js +1 -0
- package/dist/web/assets/{session-indexes-0oT5HSS5.js → session-indexes-BPhf1X2P.js} +2 -2
- package/dist/web/assets/{utils-DgUgu15E.js → utils-BKqufGdv.js} +1 -1
- package/dist/web/index.html +8 -8
- package/package.json +1 -1
- package/dist/chunk-VAUC2W7I.js.map +0 -1
- package/dist/web/assets/InteractiveReceipt-DYGyHeZd.js +0 -1
- package/dist/web/assets/Projects-BPDIcUBK.js +0 -1
- package/dist/web/assets/contract-V6QuD0UY.js +0 -1
- package/dist/web/assets/index-99ZxpV2y.js +0 -1410
- package/dist/web/assets/index-B2RqUgZ5.css +0 -1
- package/dist/web/assets/panel-C-gc79Gz.js +0 -1
- /package/dist/{chunk-PROMURPM.js.map → chunk-WAKW5OPS.js.map} +0 -0
- /package/dist/{dist-6QTGTYZT.js.map → dist-RHDY2EZX.js.map} +0 -0
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../core/dist/chunk-
|
|
3
|
+
// ../core/dist/chunk-46YWSMES.mjs
|
|
4
|
+
var UNKNOWN_AGENT_NAME = "unknown";
|
|
5
|
+
function normalizeSessionReference(reference) {
|
|
6
|
+
return {
|
|
7
|
+
agentName: reference.agentName.trim().toLowerCase(),
|
|
8
|
+
sessionId: reference.sessionId
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function parseSessionReference(value) {
|
|
12
|
+
const separatorIndex = value.indexOf("/");
|
|
13
|
+
if (separatorIndex <= 0 || separatorIndex === value.length - 1) return null;
|
|
14
|
+
const agentName = value.slice(0, separatorIndex).trim().toLowerCase();
|
|
15
|
+
if (!agentName) return null;
|
|
16
|
+
return {
|
|
17
|
+
agentName,
|
|
18
|
+
sessionId: value.slice(separatorIndex + 1)
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function formatSessionReference(reference) {
|
|
22
|
+
const normalized = normalizeSessionReference(reference);
|
|
23
|
+
return `${normalized.agentName}/${normalized.sessionId}`;
|
|
24
|
+
}
|
|
25
|
+
function getSessionAgentKey(session) {
|
|
26
|
+
return parseSessionReference(session.slug ?? "")?.agentName ?? UNKNOWN_AGENT_NAME;
|
|
27
|
+
}
|
|
28
|
+
function agentRoutePath(agentName) {
|
|
29
|
+
return `/${encodeURIComponent(agentName.trim().toLowerCase())}`;
|
|
30
|
+
}
|
|
31
|
+
function sessionRoutePath(reference) {
|
|
32
|
+
const normalized = normalizeSessionReference(reference);
|
|
33
|
+
return `${agentRoutePath(normalized.agentName)}/${encodeURIComponent(normalized.sessionId)}`;
|
|
34
|
+
}
|
|
4
35
|
function toRecord(value) {
|
|
5
36
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6
37
|
}
|
|
@@ -121,7 +152,7 @@ function normalizeMessageParts(value) {
|
|
|
121
152
|
});
|
|
122
153
|
}
|
|
123
154
|
|
|
124
|
-
// ../core/dist/chunk-
|
|
155
|
+
// ../core/dist/chunk-TBIXUIQV.mjs
|
|
125
156
|
import { chmodSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
126
157
|
import { basename, dirname, join } from "path";
|
|
127
158
|
import { createHash } from "crypto";
|
|
@@ -135,7 +166,7 @@ import { existsSync as existsSync4, readFileSync as readFileSync2, statSync as s
|
|
|
135
166
|
import { join as join5, basename as basename3, dirname as dirname2 } from "path";
|
|
136
167
|
import { existsSync as existsSync3 } from "fs";
|
|
137
168
|
import { homedir as homedir2, platform } from "os";
|
|
138
|
-
import { join as join4 } from "path";
|
|
169
|
+
import { isAbsolute, join as join4, resolve } from "path";
|
|
139
170
|
import { closeSync, openSync, readSync } from "fs";
|
|
140
171
|
import { StringDecoder } from "string_decoder";
|
|
141
172
|
import { basename as basename2 } from "path";
|
|
@@ -147,7 +178,7 @@ import { createHash as createHash2 } from "crypto";
|
|
|
147
178
|
import { existsSync as existsSync6, readFileSync as readFileSync3, statSync as statSync5 } from "fs";
|
|
148
179
|
import { join as join8, basename as basename5, dirname as dirname4 } from "path";
|
|
149
180
|
import { existsSync as existsSync7, readFileSync as readFileSync4, statSync as statSync6 } from "fs";
|
|
150
|
-
import { basename as basename6, dirname as dirname5, join as join9, resolve } from "path";
|
|
181
|
+
import { basename as basename6, dirname as dirname5, join as join9, resolve as resolve2 } from "path";
|
|
151
182
|
import { existsSync as existsSync8, readFileSync as readFileSync5, statSync as statSync7 } from "fs";
|
|
152
183
|
import { join as join10, basename as basename7 } from "path";
|
|
153
184
|
import { existsSync as existsSync9, lstatSync, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
|
|
@@ -162,8 +193,7 @@ import { dirname as dirname6, join as join14 } from "path";
|
|
|
162
193
|
import { existsSync as existsSync12, readdirSync as readdirSync4, realpathSync, statSync as statSync10 } from "fs";
|
|
163
194
|
import { basename as basename9, join as join17 } from "path";
|
|
164
195
|
import { closeSync as closeSync2, openSync as openSync2, readFileSync as readFileSync8, readSync as readSync2, statSync as statSync9 } from "fs";
|
|
165
|
-
import {
|
|
166
|
-
import { join as join15, resolve as resolve2 } from "path";
|
|
196
|
+
import { join as join15 } from "path";
|
|
167
197
|
import * as zlib from "zlib";
|
|
168
198
|
import { createHash as createHash3 } from "crypto";
|
|
169
199
|
import { readFileSync as readFileSync9 } from "fs";
|
|
@@ -968,12 +998,32 @@ function reportAgentScanFailure(failure, baselineRetained) {
|
|
|
968
998
|
});
|
|
969
999
|
}
|
|
970
1000
|
var BaseAgent = class {
|
|
1001
|
+
/**
|
|
1002
|
+
* Commit the observation made by the latest checkForChanges. Called by the
|
|
1003
|
+
* scan orchestrator only after the scan consuming that check succeeded, so
|
|
1004
|
+
* a failed scan leaves the baseline behind and the next check re-detects
|
|
1005
|
+
* the same change instead of silently skipping it.
|
|
1006
|
+
*/
|
|
1007
|
+
commitChangeCheck() {
|
|
1008
|
+
}
|
|
1009
|
+
/** Wrap an enumeration/database read so failures follow the error taxonomy. */
|
|
1010
|
+
scanStep(stage, sourcePath, read) {
|
|
1011
|
+
try {
|
|
1012
|
+
return read();
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
if (error instanceof SessionScanError) throw error;
|
|
1015
|
+
throw new SessionScanError(this.name, stage, { cause: error, sourcePath });
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
971
1018
|
filterCachedSessions(sessions) {
|
|
972
1019
|
return sessions;
|
|
973
1020
|
}
|
|
974
1021
|
getUri(sessionId) {
|
|
975
1022
|
return `${this.name}://${sessionId}`;
|
|
976
1023
|
}
|
|
1024
|
+
sessionSlug(sessionId) {
|
|
1025
|
+
return formatSessionReference({ agentName: this.name, sessionId });
|
|
1026
|
+
}
|
|
977
1027
|
};
|
|
978
1028
|
var FileSystemSessionSource = class extends BaseAgent {
|
|
979
1029
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
@@ -1215,6 +1265,7 @@ function latestSqliteSourceMtime(dbPath) {
|
|
|
1215
1265
|
var DatabaseSessionSource = class extends BaseAgent {
|
|
1216
1266
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
1217
1267
|
lastSourceFingerprint = null;
|
|
1268
|
+
pendingSourceFingerprint = null;
|
|
1218
1269
|
/** 记录单个会话的缓存 meta(sourcePath = dbPath)。 */
|
|
1219
1270
|
rememberSession(sessionId, additionalMeta = {}) {
|
|
1220
1271
|
const dbPath = this.getDatabasePath();
|
|
@@ -1239,6 +1290,11 @@ var DatabaseSessionSource = class extends BaseAgent {
|
|
|
1239
1290
|
setSessionMetaMap(meta) {
|
|
1240
1291
|
this.sessionMetaMap = meta;
|
|
1241
1292
|
}
|
|
1293
|
+
commitChangeCheck() {
|
|
1294
|
+
if (this.pendingSourceFingerprint == null) return;
|
|
1295
|
+
this.lastSourceFingerprint = this.pendingSourceFingerprint;
|
|
1296
|
+
this.pendingSourceFingerprint = null;
|
|
1297
|
+
}
|
|
1242
1298
|
/**
|
|
1243
1299
|
* 变更检测:数据库内部变更难以按行定位,按库文件集合的指纹判定。
|
|
1244
1300
|
*
|
|
@@ -1263,7 +1319,7 @@ var DatabaseSessionSource = class extends BaseAgent {
|
|
|
1263
1319
|
if (pricingChanged) return { hasChanges: true, timestamp: Date.now() };
|
|
1264
1320
|
const fingerprint = sqliteSourceFingerprint(dbPath);
|
|
1265
1321
|
const previous = this.lastSourceFingerprint;
|
|
1266
|
-
this.
|
|
1322
|
+
this.pendingSourceFingerprint = fingerprint;
|
|
1267
1323
|
const hasChanges = previous == null ? latestSqliteSourceMtime(dbPath) > sinceTimestamp : fingerprint !== previous;
|
|
1268
1324
|
return {
|
|
1269
1325
|
hasChanges,
|
|
@@ -1369,10 +1425,16 @@ var PerfTracer = class {
|
|
|
1369
1425
|
}
|
|
1370
1426
|
};
|
|
1371
1427
|
var perf = new PerfTracer();
|
|
1428
|
+
function expandHomePath(path2) {
|
|
1429
|
+
if (path2 === "~") return homedir2();
|
|
1430
|
+
if (path2.startsWith("~/") || path2.startsWith("~\\")) return join4(homedir2(), path2.slice(2));
|
|
1431
|
+
return path2;
|
|
1432
|
+
}
|
|
1372
1433
|
function readEnvPath(name) {
|
|
1373
1434
|
const value = process.env[name];
|
|
1374
|
-
if (!value) return null;
|
|
1375
|
-
|
|
1435
|
+
if (!value || !value.trim()) return null;
|
|
1436
|
+
const expanded = expandHomePath(value.trim());
|
|
1437
|
+
return isAbsolute(expanded) ? expanded : resolve(expanded);
|
|
1376
1438
|
}
|
|
1377
1439
|
function firstExisting(...paths) {
|
|
1378
1440
|
for (const p of paths) {
|
|
@@ -1605,18 +1667,35 @@ function estimateTokenCost(model, tokens) {
|
|
|
1605
1667
|
return estimateCostForTokens(model, tokens)?.cost ?? null;
|
|
1606
1668
|
}
|
|
1607
1669
|
var TIMEZONE_SUFFIX_PATTERN = /(?:Z|[+-]\d{2}:?\d{2})$/i;
|
|
1608
|
-
function
|
|
1670
|
+
function parseAgentTimestamp(value, agentName, options = {}) {
|
|
1671
|
+
if (value == null) return null;
|
|
1672
|
+
if (typeof value === "number") {
|
|
1673
|
+
if (Number.isFinite(value)) return value;
|
|
1674
|
+
reportTimestampParseFailure(agentName, value);
|
|
1675
|
+
return null;
|
|
1676
|
+
}
|
|
1677
|
+
if (typeof value !== "string") {
|
|
1678
|
+
reportTimestampParseFailure(agentName, value);
|
|
1679
|
+
return null;
|
|
1680
|
+
}
|
|
1609
1681
|
const timestamp = value.trim().replace(" ", "T");
|
|
1610
|
-
if (!timestamp) return
|
|
1682
|
+
if (!timestamp) return null;
|
|
1683
|
+
if (options.numericStrings) {
|
|
1684
|
+
const numeric = Number(timestamp);
|
|
1685
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
1686
|
+
}
|
|
1611
1687
|
const normalized = TIMEZONE_SUFFIX_PATTERN.test(timestamp) ? timestamp : `${timestamp}Z`;
|
|
1612
1688
|
const timestampMs = Date.parse(normalized);
|
|
1613
1689
|
if (Number.isFinite(timestampMs)) return timestampMs;
|
|
1690
|
+
reportTimestampParseFailure(agentName, value, normalized);
|
|
1691
|
+
return null;
|
|
1692
|
+
}
|
|
1693
|
+
function reportTimestampParseFailure(agentName, value, normalized) {
|
|
1614
1694
|
getCoreDiagnostics()?.warn("agent.timestamp_parse_failed", {
|
|
1615
1695
|
agentName,
|
|
1616
|
-
value,
|
|
1696
|
+
value: typeof value === "string" || typeof value === "number" ? value : typeof value,
|
|
1617
1697
|
normalized
|
|
1618
1698
|
});
|
|
1619
|
-
return 0;
|
|
1620
1699
|
}
|
|
1621
1700
|
function asRecord(value) {
|
|
1622
1701
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
@@ -1672,6 +1751,9 @@ var TranscriptBuilder = class {
|
|
|
1672
1751
|
options;
|
|
1673
1752
|
messages = [];
|
|
1674
1753
|
pendingToolCalls = /* @__PURE__ */ new Map();
|
|
1754
|
+
// Tracks per-message part kinds so grouping checks are O(1) instead of
|
|
1755
|
+
// rescanning the growing parts array on every append (CS-283).
|
|
1756
|
+
partFlags = /* @__PURE__ */ new WeakMap();
|
|
1675
1757
|
currentAssistant = null;
|
|
1676
1758
|
latestTextAssistant = null;
|
|
1677
1759
|
beginTurn() {
|
|
@@ -1681,10 +1763,13 @@ var TranscriptBuilder = class {
|
|
|
1681
1763
|
appendMessage(input) {
|
|
1682
1764
|
const message = this.createMessage(input);
|
|
1683
1765
|
this.messages.push(message);
|
|
1684
|
-
|
|
1766
|
+
for (const part of message.parts) {
|
|
1767
|
+
this.registerToolCall(part);
|
|
1768
|
+
this.notePart(message, part);
|
|
1769
|
+
}
|
|
1685
1770
|
if (message.role === "assistant") {
|
|
1686
1771
|
this.currentAssistant = message;
|
|
1687
|
-
if (
|
|
1772
|
+
if (this.flagsFor(message).hasText) {
|
|
1688
1773
|
this.latestTextAssistant = message;
|
|
1689
1774
|
}
|
|
1690
1775
|
} else if (message.role === "user") {
|
|
@@ -1694,11 +1779,12 @@ var TranscriptBuilder = class {
|
|
|
1694
1779
|
}
|
|
1695
1780
|
appendAssistantPart(part, input, options = {}) {
|
|
1696
1781
|
const current = this.currentAssistant;
|
|
1697
|
-
const
|
|
1782
|
+
const currentFlags = current === null ? null : this.flagsFor(current);
|
|
1783
|
+
const canReuse = current !== null && (options.grouping === "current" || (part.type === "text" ? !currentFlags.hasTool : part.type === "reasoning" ? !currentFlags.hasText && !currentFlags.hasTool : false));
|
|
1698
1784
|
const message = canReuse ? current : this.appendMessage({ ...input, role: "assistant", parts: [part] });
|
|
1699
1785
|
if (canReuse) {
|
|
1700
1786
|
if (options.deduplicateTail) this.appendPartIfNew(message, part);
|
|
1701
|
-
else
|
|
1787
|
+
else this.pushPart(message, part);
|
|
1702
1788
|
this.applyMissingMetadata(message, input);
|
|
1703
1789
|
}
|
|
1704
1790
|
if (part.type === "text") {
|
|
@@ -1717,7 +1803,7 @@ var TranscriptBuilder = class {
|
|
|
1717
1803
|
parts: [part]
|
|
1718
1804
|
});
|
|
1719
1805
|
if (target) {
|
|
1720
|
-
|
|
1806
|
+
this.pushPart(message, part);
|
|
1721
1807
|
this.applyMissingMetadata(message, input);
|
|
1722
1808
|
if (options.markModeAsTool) message.mode = "tool";
|
|
1723
1809
|
this.registerToolCall(part);
|
|
@@ -1727,7 +1813,7 @@ var TranscriptBuilder = class {
|
|
|
1727
1813
|
}
|
|
1728
1814
|
appendToCurrentAssistant(part) {
|
|
1729
1815
|
if (!this.currentAssistant) return false;
|
|
1730
|
-
this.currentAssistant
|
|
1816
|
+
this.pushPart(this.currentAssistant, part);
|
|
1731
1817
|
return true;
|
|
1732
1818
|
}
|
|
1733
1819
|
updateToolCall(callId, update) {
|
|
@@ -1804,9 +1890,6 @@ var TranscriptBuilder = class {
|
|
|
1804
1890
|
nickname: input.nickname
|
|
1805
1891
|
};
|
|
1806
1892
|
}
|
|
1807
|
-
registerToolCalls(parts) {
|
|
1808
|
-
for (const part of parts) this.registerToolCall(part);
|
|
1809
|
-
}
|
|
1810
1893
|
registerToolCall(part) {
|
|
1811
1894
|
if (part.type === "tool" && part.callID) {
|
|
1812
1895
|
this.pendingToolCalls.set(part.callID, part);
|
|
@@ -1817,7 +1900,24 @@ var TranscriptBuilder = class {
|
|
|
1817
1900
|
if ("text" in part && tail?.type === part.type && "text" in tail && tail.text === part.text) {
|
|
1818
1901
|
return;
|
|
1819
1902
|
}
|
|
1903
|
+
this.pushPart(message, part);
|
|
1904
|
+
}
|
|
1905
|
+
pushPart(message, part) {
|
|
1820
1906
|
message.parts.push(part);
|
|
1907
|
+
this.notePart(message, part);
|
|
1908
|
+
}
|
|
1909
|
+
notePart(message, part) {
|
|
1910
|
+
const flags = this.flagsFor(message);
|
|
1911
|
+
if (part.type === "text") flags.hasText = true;
|
|
1912
|
+
else if (part.type === "tool") flags.hasTool = true;
|
|
1913
|
+
}
|
|
1914
|
+
flagsFor(message) {
|
|
1915
|
+
let flags = this.partFlags.get(message);
|
|
1916
|
+
if (!flags) {
|
|
1917
|
+
flags = { hasText: false, hasTool: false };
|
|
1918
|
+
this.partFlags.set(message, flags);
|
|
1919
|
+
}
|
|
1920
|
+
return flags;
|
|
1821
1921
|
}
|
|
1822
1922
|
applyMissingMetadata(message, input) {
|
|
1823
1923
|
if (!message.id && input.id) message.id = input.id;
|
|
@@ -1855,18 +1955,12 @@ var TranscriptBuilder = class {
|
|
|
1855
1955
|
};
|
|
1856
1956
|
}
|
|
1857
1957
|
};
|
|
1858
|
-
var HEAD_INDEX_VERSION = "claudecode-head-
|
|
1958
|
+
var HEAD_INDEX_VERSION = "claudecode-head-v7";
|
|
1859
1959
|
function resolveClaudeCodeDataRoot() {
|
|
1860
1960
|
return resolveHomePath("CLAUDE_CONFIG_DIR", ".claude");
|
|
1861
1961
|
}
|
|
1862
1962
|
function parseTimestampMs(data) {
|
|
1863
|
-
|
|
1864
|
-
const value = asString(raw);
|
|
1865
|
-
if (value === void 0) {
|
|
1866
|
-
if (raw !== void 0 && raw !== null) reportFieldMismatch("claudecode", "timestamp");
|
|
1867
|
-
return 0;
|
|
1868
|
-
}
|
|
1869
|
-
return parseAgentTimestampMs(value, "claudecode");
|
|
1963
|
+
return parseAgentTimestamp(data["timestamp"], "claudecode") ?? 0;
|
|
1870
1964
|
}
|
|
1871
1965
|
function readUsageNumber(usage, field) {
|
|
1872
1966
|
const raw = usage[field];
|
|
@@ -2067,7 +2161,7 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
2067
2161
|
reference: { agentName: this.name, sessionId: meta.id },
|
|
2068
2162
|
id: meta.id,
|
|
2069
2163
|
title: meta.title,
|
|
2070
|
-
slug:
|
|
2164
|
+
slug: this.sessionSlug(meta.id),
|
|
2071
2165
|
directory: meta.directory,
|
|
2072
2166
|
parent_reference: meta.parentSessionId == null ? void 0 : { agentName: this.name, sessionId: meta.parentSessionId },
|
|
2073
2167
|
version: void 0,
|
|
@@ -2347,7 +2441,7 @@ var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
|
2347
2441
|
if (messageCount === 0) return filteredSession("no visible messages");
|
|
2348
2442
|
return parsedSession({
|
|
2349
2443
|
id: sessionId,
|
|
2350
|
-
slug:
|
|
2444
|
+
slug: this.sessionSlug(sessionId),
|
|
2351
2445
|
title,
|
|
2352
2446
|
directory,
|
|
2353
2447
|
parent_reference: child?.parentSessionId == null ? void 0 : { agentName: this.name, sessionId: child.parentSessionId },
|
|
@@ -2997,7 +3091,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2997
3091
|
const messageTitle = context?.messageTitle ?? null;
|
|
2998
3092
|
return parsedSession({
|
|
2999
3093
|
id,
|
|
3000
|
-
slug:
|
|
3094
|
+
slug: this.sessionSlug(id),
|
|
3001
3095
|
title: resolveSessionTitle(String(row.title ?? ""), messageTitle, null),
|
|
3002
3096
|
directory: String(row.directory ?? ""),
|
|
3003
3097
|
parent_reference: row.parent_id == null || String(row.parent_id) === "" ? void 0 : { agentName: this.name, sessionId: String(row.parent_id) },
|
|
@@ -3379,6 +3473,14 @@ var OpenCodeAgent = class extends OpenCodeSqliteAgent {
|
|
|
3379
3473
|
});
|
|
3380
3474
|
}
|
|
3381
3475
|
};
|
|
3476
|
+
function normalizeToolArguments(raw) {
|
|
3477
|
+
if (typeof raw !== "string") return raw;
|
|
3478
|
+
try {
|
|
3479
|
+
return JSON.parse(raw);
|
|
3480
|
+
} catch {
|
|
3481
|
+
return raw;
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3382
3484
|
var KIMI_TOOL_TITLE_MAP = {
|
|
3383
3485
|
ReadFile: "read",
|
|
3384
3486
|
Glob: "glob",
|
|
@@ -3388,7 +3490,7 @@ var KIMI_TOOL_TITLE_MAP = {
|
|
|
3388
3490
|
Shell: "bash"
|
|
3389
3491
|
};
|
|
3390
3492
|
var KIMI_IGNORED_TOOLS = /* @__PURE__ */ new Set(["SetTodoList"]);
|
|
3391
|
-
var KIMI_PARSER_REVISION = "kimi-parser-
|
|
3493
|
+
var KIMI_PARSER_REVISION = "kimi-parser-v2";
|
|
3392
3494
|
function resolveKimiDataRoot() {
|
|
3393
3495
|
return resolveHomePath("KIMI_SHARE_DIR", ".kimi");
|
|
3394
3496
|
}
|
|
@@ -3402,26 +3504,53 @@ function readWireTimestamp(record) {
|
|
|
3402
3504
|
return narrowField("kimi", "wire.timestamp", record.timestamp, asNumber) ?? 0;
|
|
3403
3505
|
}
|
|
3404
3506
|
function parseTimestamp(raw) {
|
|
3405
|
-
|
|
3406
|
-
if (typeof raw !== "string" || raw.trim() === "") return null;
|
|
3407
|
-
const numeric = Number(raw);
|
|
3408
|
-
if (Number.isFinite(numeric)) return numeric;
|
|
3409
|
-
const parsed = Date.parse(raw);
|
|
3410
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
3507
|
+
return parseAgentTimestamp(raw, "kimi", { numericStrings: true });
|
|
3411
3508
|
}
|
|
3412
3509
|
function extractTokenField(usage, field) {
|
|
3413
3510
|
return narrowField("kimi", `usage.${field}`, usage[field], asNumber) ?? 0;
|
|
3414
3511
|
}
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3512
|
+
var KimiUsageAccumulator = class {
|
|
3513
|
+
constructor(model, usesWireTotalFallback) {
|
|
3514
|
+
this.model = model;
|
|
3515
|
+
this.usesWireTotalFallback = usesWireTotalFallback;
|
|
3516
|
+
}
|
|
3517
|
+
model;
|
|
3518
|
+
usesWireTotalFallback;
|
|
3519
|
+
stats = {
|
|
3520
|
+
total_cost: 0,
|
|
3521
|
+
total_input_tokens: 0,
|
|
3522
|
+
total_output_tokens: 0,
|
|
3523
|
+
total_tokens: 0,
|
|
3524
|
+
message_count: 0
|
|
3525
|
+
};
|
|
3526
|
+
totalCost = 0;
|
|
3527
|
+
applyContextRecord(record) {
|
|
3528
|
+
if (record.role !== "_usage") return;
|
|
3529
|
+
const tokenCount = asNumber(record.token_count);
|
|
3530
|
+
if (tokenCount === void 0) {
|
|
3531
|
+
reportFieldMismatch("kimi", "usage.token_count");
|
|
3532
|
+
return;
|
|
3421
3533
|
}
|
|
3534
|
+
this.stats.total_tokens = tokenCount;
|
|
3535
|
+
}
|
|
3536
|
+
applyWireRecord(record) {
|
|
3537
|
+
if (this.usesWireTotalFallback) this.applyContextRecord(record);
|
|
3538
|
+
const tokenUsage = asRecord(asRecord(record.message)?.usage);
|
|
3539
|
+
if (!tokenUsage) return null;
|
|
3540
|
+
const inputTokens = extractTokenField(tokenUsage, "input_tokens");
|
|
3541
|
+
const outputTokens = extractTokenField(tokenUsage, "output_tokens");
|
|
3542
|
+
const cost = estimateTokenCost(this.model, { input: inputTokens, output: outputTokens });
|
|
3543
|
+
this.stats.total_input_tokens += inputTokens;
|
|
3544
|
+
this.stats.total_output_tokens += outputTokens;
|
|
3545
|
+
if (cost !== null) this.totalCost += cost;
|
|
3546
|
+
return { inputTokens, outputTokens, cost };
|
|
3547
|
+
}
|
|
3548
|
+
finish() {
|
|
3549
|
+
const stats = { ...this.stats, total_cost: Number(this.totalCost.toFixed(8)) };
|
|
3550
|
+
if (stats.total_cost > 0) stats.cost_source = "estimated";
|
|
3551
|
+
return stats;
|
|
3422
3552
|
}
|
|
3423
|
-
|
|
3424
|
-
}
|
|
3553
|
+
};
|
|
3425
3554
|
function normalizeToolOutputParts(content, timestampMs) {
|
|
3426
3555
|
if (typeof content === "string") {
|
|
3427
3556
|
const text2 = cleanInternalText(content);
|
|
@@ -3647,7 +3776,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3647
3776
|
const stats = this.extractStats(meta.sourcePath);
|
|
3648
3777
|
return parsedSession({
|
|
3649
3778
|
id: meta.id,
|
|
3650
|
-
slug:
|
|
3779
|
+
slug: this.sessionSlug(meta.id),
|
|
3651
3780
|
title: meta.title,
|
|
3652
3781
|
directory: meta.cwd,
|
|
3653
3782
|
time_created: meta.createdAt,
|
|
@@ -3667,10 +3796,12 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3667
3796
|
if (!meta.contextFile) throw new Error("context.jsonl is missing");
|
|
3668
3797
|
const builder = new TranscriptBuilder();
|
|
3669
3798
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
3799
|
+
const accumulator = new KimiUsageAccumulator(this.defaultModel, false);
|
|
3670
3800
|
let seq = 0;
|
|
3671
3801
|
const fallbackTs = meta.createdAt;
|
|
3672
3802
|
for (const record of readJsonlFile(meta.contextFile)) {
|
|
3673
3803
|
seq++;
|
|
3804
|
+
accumulator.applyContextRecord(record);
|
|
3674
3805
|
try {
|
|
3675
3806
|
const role = String(record.role ?? "");
|
|
3676
3807
|
if (role === "_checkpoint" || role === "_usage" || isInternalEventType(role)) continue;
|
|
@@ -3716,8 +3847,8 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3716
3847
|
} catch {
|
|
3717
3848
|
}
|
|
3718
3849
|
}
|
|
3719
|
-
|
|
3720
|
-
return this.buildSessionData(meta, builder,
|
|
3850
|
+
this.collectWireUsage(meta.sourcePath, accumulator);
|
|
3851
|
+
return this.buildSessionData(meta, builder, accumulator.finish());
|
|
3721
3852
|
}
|
|
3722
3853
|
getSessionDataFromWire(meta) {
|
|
3723
3854
|
const wirePath = meta.wireFile ?? join8(meta.sourcePath, "wire.jsonl");
|
|
@@ -3725,29 +3856,25 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3725
3856
|
const builder = new TranscriptBuilder();
|
|
3726
3857
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
3727
3858
|
const openToolArgumentBuffer = /* @__PURE__ */ new Map();
|
|
3859
|
+
const accumulator = new KimiUsageAccumulator(this.defaultModel, true);
|
|
3728
3860
|
let openToolCallId = null;
|
|
3729
3861
|
let seq = 0;
|
|
3730
3862
|
for (const record of readJsonlFile(wirePath)) {
|
|
3731
3863
|
seq++;
|
|
3732
3864
|
try {
|
|
3865
|
+
const tokenUsage = accumulator.applyWireRecord(record);
|
|
3733
3866
|
const message = asRecord(record.message) ?? {};
|
|
3734
3867
|
const msgType = asString(message.type) ?? "";
|
|
3735
3868
|
if (isInternalEventType(msgType)) continue;
|
|
3736
3869
|
const payload = asRecord(message.payload) ?? {};
|
|
3737
3870
|
const timestampMs = Math.floor(readWireTimestamp(record) * 1e3);
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
builder.attachUsageToLatestAssistant(tokens, {
|
|
3746
|
-
model: this.defaultModel,
|
|
3747
|
-
cost: cost ?? void 0,
|
|
3748
|
-
costSource: cost === null ? void 0 : "estimated"
|
|
3749
|
-
});
|
|
3750
|
-
}
|
|
3871
|
+
if (tokenUsage && (tokenUsage.inputTokens || tokenUsage.outputTokens)) {
|
|
3872
|
+
const tokens = { input: tokenUsage.inputTokens, output: tokenUsage.outputTokens };
|
|
3873
|
+
builder.attachUsageToLatestAssistant(tokens, {
|
|
3874
|
+
model: this.defaultModel,
|
|
3875
|
+
cost: tokenUsage.cost ?? void 0,
|
|
3876
|
+
costSource: tokenUsage.cost === null ? void 0 : "estimated"
|
|
3877
|
+
});
|
|
3751
3878
|
}
|
|
3752
3879
|
if (msgType === "TurnBegin") {
|
|
3753
3880
|
const userInput = payload.user_input;
|
|
@@ -3852,8 +3979,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3852
3979
|
} catch {
|
|
3853
3980
|
}
|
|
3854
3981
|
}
|
|
3855
|
-
|
|
3856
|
-
return this.buildSessionData(meta, builder, stats);
|
|
3982
|
+
return this.buildSessionData(meta, builder, accumulator.finish());
|
|
3857
3983
|
}
|
|
3858
3984
|
// --- Helpers ---
|
|
3859
3985
|
sourceFingerprint(meta) {
|
|
@@ -3950,59 +4076,27 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
3950
4076
|
if (!outputParts.length || !callId) return false;
|
|
3951
4077
|
return builder.resolveToolCall(callId, { output: [...outputParts] });
|
|
3952
4078
|
}
|
|
3953
|
-
/** Applies a `_usage` record's running total; other records are ignored. */
|
|
3954
|
-
applyUsageTotal(record, stats) {
|
|
3955
|
-
if (record.role !== "_usage") return;
|
|
3956
|
-
const tokenCount = asNumber(record.token_count);
|
|
3957
|
-
if (tokenCount === void 0) {
|
|
3958
|
-
reportFieldMismatch("kimi", "usage.token_count");
|
|
3959
|
-
return;
|
|
3960
|
-
}
|
|
3961
|
-
stats.total_tokens = tokenCount;
|
|
3962
|
-
}
|
|
3963
4079
|
extractStats(sessionDir) {
|
|
3964
|
-
let totalCost = 0;
|
|
3965
|
-
const stats = {
|
|
3966
|
-
total_cost: 0,
|
|
3967
|
-
total_input_tokens: 0,
|
|
3968
|
-
total_output_tokens: 0,
|
|
3969
|
-
total_tokens: 0,
|
|
3970
|
-
message_count: 0
|
|
3971
|
-
};
|
|
3972
4080
|
const contextPath = join8(sessionDir, "context.jsonl");
|
|
3973
4081
|
const hasContext = existsSync6(contextPath);
|
|
4082
|
+
const accumulator = new KimiUsageAccumulator(this.defaultModel, !hasContext);
|
|
3974
4083
|
if (hasContext) {
|
|
3975
4084
|
try {
|
|
3976
|
-
for (const record of readJsonlFile(contextPath))
|
|
4085
|
+
for (const record of readJsonlFile(contextPath)) accumulator.applyContextRecord(record);
|
|
3977
4086
|
} catch {
|
|
3978
4087
|
}
|
|
3979
4088
|
}
|
|
4089
|
+
this.collectWireUsage(sessionDir, accumulator);
|
|
4090
|
+
return accumulator.finish();
|
|
4091
|
+
}
|
|
4092
|
+
collectWireUsage(sessionDir, accumulator) {
|
|
3980
4093
|
const wirePath = join8(sessionDir, "wire.jsonl");
|
|
3981
4094
|
if (existsSync6(wirePath)) {
|
|
3982
4095
|
try {
|
|
3983
|
-
for (const record of readJsonlFile(wirePath))
|
|
3984
|
-
const tokenUsage = asRecord(asRecord(record.message)?.usage);
|
|
3985
|
-
if (tokenUsage) {
|
|
3986
|
-
const inputTokens = extractTokenField(tokenUsage, "input_tokens");
|
|
3987
|
-
const outputTokens = extractTokenField(tokenUsage, "output_tokens");
|
|
3988
|
-
stats.total_input_tokens += inputTokens;
|
|
3989
|
-
stats.total_output_tokens += outputTokens;
|
|
3990
|
-
const cost = estimateTokenCost(this.defaultModel, {
|
|
3991
|
-
input: inputTokens,
|
|
3992
|
-
output: outputTokens
|
|
3993
|
-
});
|
|
3994
|
-
if (cost !== null) totalCost += cost;
|
|
3995
|
-
}
|
|
3996
|
-
if (!hasContext) this.applyUsageTotal(record, stats);
|
|
3997
|
-
}
|
|
4096
|
+
for (const record of readJsonlFile(wirePath)) accumulator.applyWireRecord(record);
|
|
3998
4097
|
} catch {
|
|
3999
4098
|
}
|
|
4000
4099
|
}
|
|
4001
|
-
stats.total_cost = Number(totalCost.toFixed(8));
|
|
4002
|
-
if (stats.total_cost > 0) {
|
|
4003
|
-
stats.cost_source = "estimated";
|
|
4004
|
-
}
|
|
4005
|
-
return stats;
|
|
4006
4100
|
}
|
|
4007
4101
|
buildSessionData(meta, builder, stats) {
|
|
4008
4102
|
const transcript = builder.finish(stats);
|
|
@@ -4010,7 +4104,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
4010
4104
|
reference: { agentName: this.name, sessionId: meta.id },
|
|
4011
4105
|
id: meta.id,
|
|
4012
4106
|
title: meta.title,
|
|
4013
|
-
slug:
|
|
4107
|
+
slug: this.sessionSlug(meta.id),
|
|
4014
4108
|
directory: meta.cwd,
|
|
4015
4109
|
time_created: meta.createdAt,
|
|
4016
4110
|
time_updated: meta.activityAt,
|
|
@@ -4036,28 +4130,15 @@ var KIMI_CODE_TOOL_TITLE_MAP = {
|
|
|
4036
4130
|
Shell: "bash"
|
|
4037
4131
|
};
|
|
4038
4132
|
var KIMI_CODE_IGNORED_TOOLS = /* @__PURE__ */ new Set(["SetTodoList"]);
|
|
4039
|
-
var KIMI_CODE_PARSER_REVISION = "kimi-code-parser-
|
|
4133
|
+
var KIMI_CODE_PARSER_REVISION = "kimi-code-parser-v2";
|
|
4040
4134
|
function resolveKimiCodeDataRoot() {
|
|
4041
4135
|
return resolveHomePath("KIMI_CODE_HOME", ".kimi-code");
|
|
4042
4136
|
}
|
|
4043
4137
|
function mapToolTitle2(toolName) {
|
|
4044
4138
|
return KIMI_CODE_TOOL_TITLE_MAP[toolName] ?? toolName;
|
|
4045
4139
|
}
|
|
4046
|
-
function normalizeToolArguments2(raw) {
|
|
4047
|
-
if (typeof raw !== "string") return raw;
|
|
4048
|
-
try {
|
|
4049
|
-
return JSON.parse(raw);
|
|
4050
|
-
} catch {
|
|
4051
|
-
return raw;
|
|
4052
|
-
}
|
|
4053
|
-
}
|
|
4054
4140
|
function parseTimestamp2(raw) {
|
|
4055
|
-
|
|
4056
|
-
if (typeof raw !== "string" || raw.trim() === "") return null;
|
|
4057
|
-
const numeric = Number(raw);
|
|
4058
|
-
if (Number.isFinite(numeric)) return numeric;
|
|
4059
|
-
const parsed = Date.parse(raw);
|
|
4060
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
4141
|
+
return parseAgentTimestamp(raw, "kimi-code", { numericStrings: true });
|
|
4061
4142
|
}
|
|
4062
4143
|
function timestampFromRecord(record) {
|
|
4063
4144
|
return parseTimestamp2(record.time) ?? 0;
|
|
@@ -4173,7 +4254,7 @@ function toolCallParts(message, timestampMs, ignoredToolCallIds) {
|
|
|
4173
4254
|
continue;
|
|
4174
4255
|
}
|
|
4175
4256
|
const rawArguments = functionRecord?.arguments ?? callRecord?.arguments;
|
|
4176
|
-
parts.push(toolPart(toolName, callId,
|
|
4257
|
+
parts.push(toolPart(toolName, callId, normalizeToolArguments(rawArguments), timestampMs));
|
|
4177
4258
|
}
|
|
4178
4259
|
return parts;
|
|
4179
4260
|
}
|
|
@@ -4274,7 +4355,7 @@ var KimiCodeAgent = class extends FileSystemSessionSource {
|
|
|
4274
4355
|
const sessionPath = asString(record.sessionDir);
|
|
4275
4356
|
const workDir = asString(record.workDir);
|
|
4276
4357
|
if (!sessionPath || !workDir) continue;
|
|
4277
|
-
this.workDirBySessionPath.set(
|
|
4358
|
+
this.workDirBySessionPath.set(resolve2(sessionPath), workDir);
|
|
4278
4359
|
}
|
|
4279
4360
|
}
|
|
4280
4361
|
listSessionDirs() {
|
|
@@ -4308,7 +4389,7 @@ var KimiCodeAgent = class extends FileSystemSessionSource {
|
|
|
4308
4389
|
wireStat.mtimeMs
|
|
4309
4390
|
);
|
|
4310
4391
|
const custom = asRecord(state.custom);
|
|
4311
|
-
const workDir = asString(state.workDir) ?? asString(custom?.cwd) ?? this.workDirBySessionPath.get(
|
|
4392
|
+
const workDir = asString(state.workDir) ?? asString(custom?.cwd) ?? this.workDirBySessionPath.get(resolve2(sessionDir)) ?? "";
|
|
4312
4393
|
const explicitTitle = asString(state.title) ?? asString(state.customTitle) ?? "";
|
|
4313
4394
|
return parsedSession({
|
|
4314
4395
|
id: basename6(sessionDir),
|
|
@@ -4416,7 +4497,7 @@ var KimiCodeAgent = class extends FileSystemSessionSource {
|
|
|
4416
4497
|
this.sessionMetaMap.set(meta.id, meta);
|
|
4417
4498
|
return parsedSession({
|
|
4418
4499
|
id: meta.id,
|
|
4419
|
-
slug:
|
|
4500
|
+
slug: this.sessionSlug(meta.id),
|
|
4420
4501
|
title: meta.title,
|
|
4421
4502
|
directory: meta.workDir,
|
|
4422
4503
|
time_created: meta.createdAt,
|
|
@@ -4434,7 +4515,7 @@ var KimiCodeAgent = class extends FileSystemSessionSource {
|
|
|
4434
4515
|
reference: { agentName: this.name, sessionId: meta.id },
|
|
4435
4516
|
id: meta.id,
|
|
4436
4517
|
title: meta.title,
|
|
4437
|
-
slug:
|
|
4518
|
+
slug: this.sessionSlug(meta.id),
|
|
4438
4519
|
directory: meta.workDir,
|
|
4439
4520
|
time_created: meta.createdAt,
|
|
4440
4521
|
time_updated: meta.activityAt,
|
|
@@ -4865,7 +4946,7 @@ var JsValueReader = class {
|
|
|
4865
4946
|
var PROPOSED_PLAN_PATTERN = /<proposed_plan>\s*([\s\S]*?)\s*<\/proposed_plan>/;
|
|
4866
4947
|
var PLAN_APPROVAL_PREFIX = "PLEASE IMPLEMENT THIS PLAN";
|
|
4867
4948
|
var SUBAGENT_NOTIFICATION_PATTERN = /<subagent_notification>\s*([\s\S]*?)\s*<\/subagent_notification>/;
|
|
4868
|
-
var HEAD_INDEX_VERSION2 = "codex-head-
|
|
4949
|
+
var HEAD_INDEX_VERSION2 = "codex-head-v2";
|
|
4869
4950
|
var PARSER_VERSION = "codex-parser-v8";
|
|
4870
4951
|
function resolveCodexDataRoot() {
|
|
4871
4952
|
return resolveHomePath("CODEX_HOME", ".codex");
|
|
@@ -4897,7 +4978,7 @@ function extractSessionId(filename) {
|
|
|
4897
4978
|
return stem;
|
|
4898
4979
|
}
|
|
4899
4980
|
function parseTimestampMs2(data) {
|
|
4900
|
-
return
|
|
4981
|
+
return parseAgentTimestamp(data["timestamp"], "codex") ?? 0;
|
|
4901
4982
|
}
|
|
4902
4983
|
function extractModelName(raw) {
|
|
4903
4984
|
return typeof raw === "string" && raw.trim() ? raw.trim() : null;
|
|
@@ -4920,9 +5001,10 @@ function extractThreadMeta(firstRecord) {
|
|
|
4920
5001
|
const agentNickname = asString(payload["agent_nickname"]) ?? null;
|
|
4921
5002
|
return { threadSource, parentThreadId, agentNickname };
|
|
4922
5003
|
}
|
|
5004
|
+
var LEADING_READ_CHUNK_BYTES = 64 * 1024;
|
|
4923
5005
|
function readLeadingJsonlLines(filePath, limit) {
|
|
4924
5006
|
const lines = [];
|
|
4925
|
-
for (const line of readJsonlFileLines(filePath)) {
|
|
5007
|
+
for (const line of readJsonlFileLines(filePath, LEADING_READ_CHUNK_BYTES)) {
|
|
4926
5008
|
if (!line.trim()) continue;
|
|
4927
5009
|
lines.push(line);
|
|
4928
5010
|
if (lines.length === limit) break;
|
|
@@ -4954,16 +5036,6 @@ function resolveToolIdentity(name, namespace) {
|
|
|
4954
5036
|
metadata: { name, namespace: namespaceText }
|
|
4955
5037
|
};
|
|
4956
5038
|
}
|
|
4957
|
-
function normalizeToolArguments3(raw) {
|
|
4958
|
-
if (typeof raw === "string") {
|
|
4959
|
-
try {
|
|
4960
|
-
return JSON.parse(raw);
|
|
4961
|
-
} catch {
|
|
4962
|
-
return raw;
|
|
4963
|
-
}
|
|
4964
|
-
}
|
|
4965
|
-
return raw;
|
|
4966
|
-
}
|
|
4967
5039
|
function normalizeCustomToolArguments(toolName, input) {
|
|
4968
5040
|
if (toolName === "apply_patch") {
|
|
4969
5041
|
return parseApplyPatchInput(input);
|
|
@@ -5073,6 +5145,36 @@ function extractPatchContent(lines, startIndex) {
|
|
|
5073
5145
|
}
|
|
5074
5146
|
return { text: contentLines.join("\n"), nextLineIndex: i };
|
|
5075
5147
|
}
|
|
5148
|
+
var ChildMessageVisibilityIndex = class {
|
|
5149
|
+
visibleSubagentIds = /* @__PURE__ */ new Set();
|
|
5150
|
+
visibleNicknameTexts = /* @__PURE__ */ new Map();
|
|
5151
|
+
constructor(messages) {
|
|
5152
|
+
for (const message of messages) this.add(message);
|
|
5153
|
+
}
|
|
5154
|
+
hasEquivalent(message) {
|
|
5155
|
+
if (message.subagent_id !== void 0 && this.visibleSubagentIds.has(message.subagent_id)) {
|
|
5156
|
+
return true;
|
|
5157
|
+
}
|
|
5158
|
+
const nickname = message.nickname;
|
|
5159
|
+
const text = message.parts.find((part) => part.type === "text")?.text;
|
|
5160
|
+
return nickname !== void 0 && text !== void 0 && this.visibleNicknameTexts.get(nickname)?.has(text) === true;
|
|
5161
|
+
}
|
|
5162
|
+
add(message) {
|
|
5163
|
+
if (message.subagent_id !== void 0) {
|
|
5164
|
+
this.visibleSubagentIds.add(message.subagent_id);
|
|
5165
|
+
return;
|
|
5166
|
+
}
|
|
5167
|
+
if (message.nickname === void 0) return;
|
|
5168
|
+
let texts = this.visibleNicknameTexts.get(message.nickname);
|
|
5169
|
+
if (!texts) {
|
|
5170
|
+
texts = /* @__PURE__ */ new Set();
|
|
5171
|
+
this.visibleNicknameTexts.set(message.nickname, texts);
|
|
5172
|
+
}
|
|
5173
|
+
for (const part of message.parts) {
|
|
5174
|
+
if (part.type === "text") texts.add(part.text);
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
};
|
|
5076
5178
|
function compareSourceActivityDesc(left, right) {
|
|
5077
5179
|
const leftTimestamp = sourceTimestamp(left.file, left.stat.mtimeMs);
|
|
5078
5180
|
const rightTimestamp = sourceTimestamp(right.file, right.stat.mtimeMs);
|
|
@@ -5093,7 +5195,11 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5093
5195
|
sessionIndexMtime;
|
|
5094
5196
|
sessionIndexPath;
|
|
5095
5197
|
subagentIndex = null;
|
|
5198
|
+
// Thread meta lives in a rollout's immutable first line; fingerprinting by
|
|
5199
|
+
// (mtime, size) lets index rebuilds stat files instead of re-reading them.
|
|
5200
|
+
threadMetaByPath = /* @__PURE__ */ new Map();
|
|
5096
5201
|
subagentStatsByParent = /* @__PURE__ */ new Map();
|
|
5202
|
+
childFinalMessagesByParent = /* @__PURE__ */ new Map();
|
|
5097
5203
|
// ---- BaseAgent implementation ----
|
|
5098
5204
|
findBasePath() {
|
|
5099
5205
|
return firstExisting(join10(resolveCodexDataRoot(), "sessions"));
|
|
@@ -5126,6 +5232,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5126
5232
|
super.setSessionMetaMap(meta);
|
|
5127
5233
|
this.subagentIndex = null;
|
|
5128
5234
|
this.subagentStatsByParent.clear();
|
|
5235
|
+
this.childFinalMessagesByParent.clear();
|
|
5129
5236
|
}
|
|
5130
5237
|
/**
|
|
5131
5238
|
* A changed subagent file leaves its parent's aggregated token stats stale,
|
|
@@ -5143,14 +5250,20 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5143
5250
|
expanded.add(parentId);
|
|
5144
5251
|
this.subagentIndex = null;
|
|
5145
5252
|
this.subagentStatsByParent.delete(parentId);
|
|
5253
|
+
this.childFinalMessagesByParent.delete(parentId);
|
|
5146
5254
|
}
|
|
5147
5255
|
return [...expanded];
|
|
5148
5256
|
}
|
|
5149
5257
|
readThreadMeta(filePath) {
|
|
5150
5258
|
try {
|
|
5259
|
+
const { mtimeMs, size } = statSync7(filePath);
|
|
5260
|
+
const fingerprint = `${mtimeMs}:${size}`;
|
|
5261
|
+
const cached = this.threadMetaByPath.get(filePath);
|
|
5262
|
+
if (cached && cached.fingerprint === fingerprint) return cached.meta;
|
|
5151
5263
|
const firstLine = readLeadingJsonlLines(filePath, 1)[0];
|
|
5152
|
-
|
|
5153
|
-
|
|
5264
|
+
const meta = firstLine ? extractThreadMeta(JSON.parse(firstLine)) : null;
|
|
5265
|
+
this.threadMetaByPath.set(filePath, { fingerprint, meta });
|
|
5266
|
+
return meta;
|
|
5154
5267
|
} catch (error) {
|
|
5155
5268
|
getCoreDiagnostics()?.warn("codex.thread_meta_read_failed", {
|
|
5156
5269
|
filePath,
|
|
@@ -5317,19 +5430,13 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5317
5430
|
});
|
|
5318
5431
|
this.applyChildStats(result.stats, meta.id);
|
|
5319
5432
|
const childMessages = this.collectChildMessages(meta.id);
|
|
5320
|
-
|
|
5321
|
-
const messageText = message.parts.find((part) => part.type === "text")?.text;
|
|
5322
|
-
const alreadyVisible = result.messages.some(
|
|
5323
|
-
(existing) => message.subagent_id !== void 0 && existing.subagent_id === message.subagent_id || existing.subagent_id === void 0 && message.nickname !== void 0 && messageText !== void 0 && existing.nickname === message.nickname && existing.parts.some((part) => part.type === "text" && part.text === messageText)
|
|
5324
|
-
);
|
|
5325
|
-
if (!alreadyVisible) result.messages.push(message);
|
|
5326
|
-
}
|
|
5433
|
+
this.mergeChildMessages(result.messages, childMessages);
|
|
5327
5434
|
result.stats.message_count = result.messages.length;
|
|
5328
5435
|
return {
|
|
5329
5436
|
reference: { agentName: this.name, sessionId: meta.id },
|
|
5330
5437
|
id: meta.id,
|
|
5331
5438
|
title: meta.title,
|
|
5332
|
-
slug:
|
|
5439
|
+
slug: this.sessionSlug(meta.id),
|
|
5333
5440
|
directory: meta.directory,
|
|
5334
5441
|
parent_reference: meta.parentThreadId == null ? void 0 : { agentName: this.name, sessionId: meta.parentThreadId },
|
|
5335
5442
|
time_created: meta.createdAt,
|
|
@@ -5347,7 +5454,8 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5347
5454
|
if (this.subagentIndex) return this.subagentIndex;
|
|
5348
5455
|
this.basePath ??= this.findBasePath();
|
|
5349
5456
|
const index = { childFilesByParent: /* @__PURE__ */ new Map(), subagentFiles: /* @__PURE__ */ new Set() };
|
|
5350
|
-
|
|
5457
|
+
const paths = this.listRolloutFilePaths();
|
|
5458
|
+
for (const file of paths) {
|
|
5351
5459
|
const threadMeta = this.readThreadMeta(file);
|
|
5352
5460
|
if (threadMeta?.threadSource !== "subagent") continue;
|
|
5353
5461
|
index.subagentFiles.add(file);
|
|
@@ -5356,6 +5464,12 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5356
5464
|
if (files) files.push(file);
|
|
5357
5465
|
else index.childFilesByParent.set(threadMeta.parentThreadId, [file]);
|
|
5358
5466
|
}
|
|
5467
|
+
if (this.threadMetaByPath.size > paths.length) {
|
|
5468
|
+
const active = new Set(paths);
|
|
5469
|
+
for (const path2 of this.threadMetaByPath.keys()) {
|
|
5470
|
+
if (!active.has(path2)) this.threadMetaByPath.delete(path2);
|
|
5471
|
+
}
|
|
5472
|
+
}
|
|
5359
5473
|
this.subagentIndex = index;
|
|
5360
5474
|
return index;
|
|
5361
5475
|
}
|
|
@@ -5381,17 +5495,56 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5381
5495
|
collectChildFiles(parentSessionId) {
|
|
5382
5496
|
return this.ensureSubagentIndex().childFilesByParent.get(parentSessionId) ?? [];
|
|
5383
5497
|
}
|
|
5498
|
+
mergeChildMessages(visibleMessages, childMessages) {
|
|
5499
|
+
const visible = new ChildMessageVisibilityIndex(visibleMessages);
|
|
5500
|
+
for (const message of childMessages) {
|
|
5501
|
+
if (visible.hasEquivalent(message)) continue;
|
|
5502
|
+
visibleMessages.push(message);
|
|
5503
|
+
visible.add(message);
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5384
5506
|
collectChildMessages(parentSessionId) {
|
|
5385
|
-
|
|
5386
|
-
|
|
5507
|
+
const childFiles = this.collectChildFiles(parentSessionId);
|
|
5508
|
+
this.reconcileChildFinalMessageCache(parentSessionId, childFiles);
|
|
5509
|
+
return childFiles.flatMap((file) => {
|
|
5510
|
+
const message = this.getChildFinalMessage(parentSessionId, file);
|
|
5387
5511
|
return message ? [message] : [];
|
|
5388
5512
|
}).sort((left, right) => left.time_created - right.time_created);
|
|
5389
5513
|
}
|
|
5390
|
-
|
|
5514
|
+
getChildFinalMessage(parentSessionId, filePath) {
|
|
5515
|
+
const sourceFingerprint = this.childFinalMessageFingerprint(filePath);
|
|
5516
|
+
let cache = this.childFinalMessagesByParent.get(parentSessionId);
|
|
5517
|
+
if (!cache) {
|
|
5518
|
+
cache = /* @__PURE__ */ new Map();
|
|
5519
|
+
this.childFinalMessagesByParent.set(parentSessionId, cache);
|
|
5520
|
+
}
|
|
5521
|
+
const cached = cache.get(filePath);
|
|
5522
|
+
if (cached?.sourceFingerprint === sourceFingerprint && cached.parserVersion === PARSER_VERSION) {
|
|
5523
|
+
return cached.message;
|
|
5524
|
+
}
|
|
5525
|
+
const message = this.readChildFinalMessage(filePath);
|
|
5526
|
+
cache.set(filePath, { sourceFingerprint, parserVersion: PARSER_VERSION, message });
|
|
5527
|
+
return message;
|
|
5528
|
+
}
|
|
5529
|
+
reconcileChildFinalMessageCache(parentSessionId, childFiles) {
|
|
5530
|
+
const cache = this.childFinalMessagesByParent.get(parentSessionId);
|
|
5531
|
+
if (!cache) return;
|
|
5532
|
+
const activeFiles = new Set(childFiles);
|
|
5533
|
+
for (const filePath of cache.keys()) {
|
|
5534
|
+
if (!activeFiles.has(filePath)) cache.delete(filePath);
|
|
5535
|
+
}
|
|
5536
|
+
if (cache.size === 0) this.childFinalMessagesByParent.delete(parentSessionId);
|
|
5537
|
+
}
|
|
5538
|
+
childFinalMessageFingerprint(filePath) {
|
|
5539
|
+
const { mtimeMs, size } = statSync7(filePath);
|
|
5540
|
+
return JSON.stringify([mtimeMs, size]);
|
|
5541
|
+
}
|
|
5542
|
+
readChildFinalMessage(filePath) {
|
|
5391
5543
|
const sessionId = extractSessionId(filePath);
|
|
5392
5544
|
const threadMeta = this.readThreadMeta(filePath);
|
|
5393
5545
|
let latestOutput = null;
|
|
5394
5546
|
let finalOutput = null;
|
|
5547
|
+
let fallbackMtimeMs = null;
|
|
5395
5548
|
for (const record of readJsonlFile(filePath)) {
|
|
5396
5549
|
try {
|
|
5397
5550
|
const recordType = String(record["type"] ?? "");
|
|
@@ -5404,7 +5557,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5404
5557
|
const candidate = {
|
|
5405
5558
|
id: asString(payload["id"]) ?? `codex-subagent-${sessionId}`,
|
|
5406
5559
|
text,
|
|
5407
|
-
timestampMs: parseTimestampMs2(record) || parseTimestampMs2(payload) || statSync7(filePath).mtimeMs,
|
|
5560
|
+
timestampMs: parseTimestampMs2(record) || parseTimestampMs2(payload) || (fallbackMtimeMs ??= statSync7(filePath).mtimeMs),
|
|
5408
5561
|
isFinal: String(record["phase"] ?? "") === "final_answer" || String(payload["phase"] ?? "") === "final_answer"
|
|
5409
5562
|
};
|
|
5410
5563
|
latestOutput = candidate;
|
|
@@ -5533,7 +5686,11 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5533
5686
|
}
|
|
5534
5687
|
this.sessionIndexCache = cache;
|
|
5535
5688
|
this.sessionIndexMtime = mtime;
|
|
5536
|
-
} catch {
|
|
5689
|
+
} catch (error) {
|
|
5690
|
+
getCoreDiagnostics()?.warn("codex.session_index_read_failed", {
|
|
5691
|
+
path: indexPath,
|
|
5692
|
+
message: error instanceof Error ? error.message : String(error)
|
|
5693
|
+
});
|
|
5537
5694
|
this.sessionIndexCache.clear();
|
|
5538
5695
|
this.sessionIndexMtime = void 0;
|
|
5539
5696
|
}
|
|
@@ -5675,7 +5832,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5675
5832
|
const title = resolveSessionTitle(indexTitle, messageTitle, basenameTitle(directory || null));
|
|
5676
5833
|
return parsedSession({
|
|
5677
5834
|
id: sessionId,
|
|
5678
|
-
slug:
|
|
5835
|
+
slug: this.sessionSlug(sessionId),
|
|
5679
5836
|
title,
|
|
5680
5837
|
directory,
|
|
5681
5838
|
parent_reference: parentThreadId == null ? void 0 : { agentName: this.name, sessionId: parentThreadId },
|
|
@@ -5719,7 +5876,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5719
5876
|
const title = resolveSessionTitle(indexTitle, messageTitle, directoryTitle);
|
|
5720
5877
|
return parsedSession({
|
|
5721
5878
|
id: sessionId,
|
|
5722
|
-
slug:
|
|
5879
|
+
slug: this.sessionSlug(sessionId),
|
|
5723
5880
|
title,
|
|
5724
5881
|
directory,
|
|
5725
5882
|
parent_reference: parentThreadId == null ? void 0 : { agentName: this.name, sessionId: parentThreadId },
|
|
@@ -5934,7 +6091,7 @@ var CodexAgent = class extends SingleFileSessionSource {
|
|
|
5934
6091
|
const name = String(payload["name"] ?? "").trim();
|
|
5935
6092
|
if (!name) return;
|
|
5936
6093
|
const toolIdentity = resolveToolIdentity(name, payload["namespace"]);
|
|
5937
|
-
const arguments_ =
|
|
6094
|
+
const arguments_ = normalizeToolArguments(payload["arguments"]);
|
|
5938
6095
|
const toolPart2 = {
|
|
5939
6096
|
type: "tool",
|
|
5940
6097
|
tool: toolIdentity.tool,
|
|
@@ -6407,7 +6564,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
6407
6564
|
return getParsedSession(
|
|
6408
6565
|
hasFastMessages && fastMessageCount === 0 && !hasSubagents ? filteredSession("no visible messages") : parsedSession({
|
|
6409
6566
|
id: composerId,
|
|
6410
|
-
slug:
|
|
6567
|
+
slug: this.sessionSlug(composerId),
|
|
6411
6568
|
title: fastTitle,
|
|
6412
6569
|
directory,
|
|
6413
6570
|
time_created: createdAt,
|
|
@@ -6526,7 +6683,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
6526
6683
|
reference: { agentName: this.name, sessionId: composerId },
|
|
6527
6684
|
id: composerId,
|
|
6528
6685
|
title,
|
|
6529
|
-
slug:
|
|
6686
|
+
slug: this.sessionSlug(composerId),
|
|
6530
6687
|
directory,
|
|
6531
6688
|
time_created: createdAt,
|
|
6532
6689
|
time_updated: updatedAt || void 0,
|
|
@@ -6604,7 +6761,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
6604
6761
|
if (directory) this.directoryCache.set(composerId, directory);
|
|
6605
6762
|
return {
|
|
6606
6763
|
id: composerId,
|
|
6607
|
-
slug:
|
|
6764
|
+
slug: this.sessionSlug(composerId),
|
|
6608
6765
|
title,
|
|
6609
6766
|
directory,
|
|
6610
6767
|
time_created: createdAt,
|
|
@@ -6665,33 +6822,18 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
6665
6822
|
const messageTitle = firstUserMessageTitle(messages) ?? composer.text;
|
|
6666
6823
|
return resolveSessionTitle(explicit, messageTitle, null);
|
|
6667
6824
|
}
|
|
6668
|
-
/**
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
for (const row of rows) {
|
|
6674
|
-
try {
|
|
6675
|
-
const bubble = parseBubbleRow(row.value);
|
|
6676
|
-
if (bubble?.type === 1 || bubble?.type === 2) {
|
|
6677
|
-
count++;
|
|
6678
|
-
}
|
|
6679
|
-
} catch {
|
|
6680
|
-
}
|
|
6681
|
-
}
|
|
6682
|
-
return count;
|
|
6683
|
-
} catch {
|
|
6684
|
-
return 0;
|
|
6685
|
-
}
|
|
6686
|
-
}
|
|
6687
|
-
/** Load one composer's bubbles, for the detail path that only needs a single session. */
|
|
6825
|
+
/**
|
|
6826
|
+
* Load one composer's bubbles, for the detail path that only needs a
|
|
6827
|
+
* single session. A failing bubble query throws instead of returning [] —
|
|
6828
|
+
* a broken database must not read as an empty session.
|
|
6829
|
+
*/
|
|
6688
6830
|
loadMessagesFromBubbles(db, composerId, initialModelName) {
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6831
|
+
const rows = this.scanStep(
|
|
6832
|
+
"reading composer bubbles",
|
|
6833
|
+
this.getDatabasePath() ?? "",
|
|
6834
|
+
() => db.prepare("SELECT rowid AS row_id, key, value FROM cursorDiskKV WHERE key LIKE ?").all(`bubbleId:${composerId}:%`)
|
|
6835
|
+
);
|
|
6836
|
+
return this.messagesFromBubbles(groupBubbleRows(rows), initialModelName);
|
|
6695
6837
|
}
|
|
6696
6838
|
/** Build messages from bubbles already parsed once, in insertion order. */
|
|
6697
6839
|
messagesFromBubbles(bubbles, initialModelName) {
|
|
@@ -6859,18 +7001,11 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
6859
7001
|
}
|
|
6860
7002
|
}
|
|
6861
7003
|
};
|
|
6862
|
-
var HEAD_INDEX_VERSION3 = "pi-head-
|
|
7004
|
+
var HEAD_INDEX_VERSION3 = "pi-head-v2";
|
|
6863
7005
|
var PARSER_VERSION2 = "pi-parser-v3";
|
|
6864
7006
|
function resolvePiDataRoot() {
|
|
6865
7007
|
return resolveHomePath("PI_HOME", ".pi");
|
|
6866
7008
|
}
|
|
6867
|
-
function parseTimestampMs3(value) {
|
|
6868
|
-
if (typeof value === "number") return Number.isFinite(value) ? value : 0;
|
|
6869
|
-
const text = String(value ?? "").trim();
|
|
6870
|
-
if (!text) return 0;
|
|
6871
|
-
const ts = Date.parse(text);
|
|
6872
|
-
return Number.isNaN(ts) ? 0 : ts;
|
|
6873
|
-
}
|
|
6874
7009
|
function narrowPiField(field, value, narrow) {
|
|
6875
7010
|
return narrowField("pi", field, value, narrow);
|
|
6876
7011
|
}
|
|
@@ -6880,7 +7015,7 @@ function narrowTimestampMs(field, value) {
|
|
|
6880
7015
|
value,
|
|
6881
7016
|
(v) => typeof v === "number" || typeof v === "string" ? v : void 0
|
|
6882
7017
|
);
|
|
6883
|
-
return shaped === void 0 ? 0 :
|
|
7018
|
+
return shaped === void 0 ? 0 : parseAgentTimestamp(shaped, "pi", { numericStrings: true }) ?? 0;
|
|
6884
7019
|
}
|
|
6885
7020
|
function extractSessionIdFromFilename(filePath) {
|
|
6886
7021
|
const stem = basename8(filePath, ".jsonl");
|
|
@@ -6974,7 +7109,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
6974
7109
|
reference: { agentName: this.name, sessionId: meta.id },
|
|
6975
7110
|
id: meta.id,
|
|
6976
7111
|
title: meta.title,
|
|
6977
|
-
slug:
|
|
7112
|
+
slug: this.sessionSlug(meta.id),
|
|
6978
7113
|
directory: meta.directory,
|
|
6979
7114
|
time_created: meta.createdAt,
|
|
6980
7115
|
time_updated: meta.updatedAt,
|
|
@@ -7024,7 +7159,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
7024
7159
|
const modelUsage = Object.keys(state.modelUsage).length > 0 ? state.modelUsage : void 0;
|
|
7025
7160
|
return parsedSession({
|
|
7026
7161
|
id: parsed.sessionId,
|
|
7027
|
-
slug:
|
|
7162
|
+
slug: this.sessionSlug(parsed.sessionId),
|
|
7028
7163
|
title: parsed.title,
|
|
7029
7164
|
directory: parsed.directory,
|
|
7030
7165
|
time_created: parsed.createdAt,
|
|
@@ -7144,7 +7279,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
7144
7279
|
const model = typeof message["model"] === "string" ? message["model"].trim() : null;
|
|
7145
7280
|
const estimatedCost = usage.cost === null ? estimateTokenCost(model, usage.tokens) : null;
|
|
7146
7281
|
const cost = usage.cost ?? estimatedCost ?? 0;
|
|
7147
|
-
const
|
|
7282
|
+
const costSource2 = cost > 0 ? usage.cost === null ? "estimated" : "recorded" : void 0;
|
|
7148
7283
|
return {
|
|
7149
7284
|
message: {
|
|
7150
7285
|
id,
|
|
@@ -7156,7 +7291,7 @@ var PiAgent = class extends SingleFileSessionSource {
|
|
|
7156
7291
|
model,
|
|
7157
7292
|
tokens: usage.tokens,
|
|
7158
7293
|
cost: cost || void 0,
|
|
7159
|
-
costSource
|
|
7294
|
+
costSource: costSource2
|
|
7160
7295
|
},
|
|
7161
7296
|
totalTokens: usage.totalTokens,
|
|
7162
7297
|
model
|
|
@@ -8008,7 +8143,7 @@ var GrokAgent = class extends FileSystemSessionSource {
|
|
|
8008
8143
|
const fingerprint = this.sourceFingerprint(source);
|
|
8009
8144
|
const head = {
|
|
8010
8145
|
id: summary.id,
|
|
8011
|
-
slug:
|
|
8146
|
+
slug: this.sessionSlug(summary.id),
|
|
8012
8147
|
title,
|
|
8013
8148
|
directory: summary.cwd,
|
|
8014
8149
|
parent_reference: summary.parentSessionId ? { agentName: this.name, sessionId: summary.parentSessionId } : void 0,
|
|
@@ -8042,7 +8177,7 @@ var GrokAgent = class extends FileSystemSessionSource {
|
|
|
8042
8177
|
reference: { agentName: this.name, sessionId: meta.id },
|
|
8043
8178
|
id: meta.id,
|
|
8044
8179
|
title: meta.title,
|
|
8045
|
-
slug:
|
|
8180
|
+
slug: this.sessionSlug(meta.id),
|
|
8046
8181
|
directory: meta.directory,
|
|
8047
8182
|
parent_reference: meta.parentSessionId ? { agentName: this.name, sessionId: meta.parentSessionId } : void 0,
|
|
8048
8183
|
version: void 0,
|
|
@@ -8122,15 +8257,8 @@ var DshSessionLogError = class extends Error {
|
|
|
8122
8257
|
this.name = "DshSessionLogError";
|
|
8123
8258
|
}
|
|
8124
8259
|
};
|
|
8125
|
-
function expandHomePath(path2) {
|
|
8126
|
-
if (path2 === "~") return homedir5();
|
|
8127
|
-
if (path2.startsWith("~/") || path2.startsWith("~\\")) return join15(homedir5(), path2.slice(2));
|
|
8128
|
-
return path2;
|
|
8129
|
-
}
|
|
8130
8260
|
function resolveDshDataRoot() {
|
|
8131
|
-
|
|
8132
|
-
const selected = fromEnv !== void 0 && fromEnv.trim().length > 0 ? fromEnv : join15(homedir5(), ".dsh");
|
|
8133
|
-
return resolve2(expandHomePath(selected));
|
|
8261
|
+
return resolveHomePath("DSH_HOME", ".dsh");
|
|
8134
8262
|
}
|
|
8135
8263
|
function dshSessionsRoot(dataRoot) {
|
|
8136
8264
|
return join15(dataRoot, "sessions");
|
|
@@ -9115,7 +9243,7 @@ var DshAgent = class extends FileSystemSessionSource {
|
|
|
9115
9243
|
const stats = this.statArtifact(artifact.sourcePath);
|
|
9116
9244
|
if (!stats) continue;
|
|
9117
9245
|
if (!matchesScanWindow(Number(stats.mtimeMs), options)) continue;
|
|
9118
|
-
const header = this.
|
|
9246
|
+
const header = this.scanStep(
|
|
9119
9247
|
"reading session headers",
|
|
9120
9248
|
artifact.sourcePath,
|
|
9121
9249
|
() => readDshSessionHeader(artifact.sourcePath, artifact.encoding)
|
|
@@ -9157,7 +9285,7 @@ var DshAgent = class extends FileSystemSessionSource {
|
|
|
9157
9285
|
}
|
|
9158
9286
|
const head = {
|
|
9159
9287
|
id: header.id,
|
|
9160
|
-
slug:
|
|
9288
|
+
slug: this.sessionSlug(header.id),
|
|
9161
9289
|
title: this.resolveTitle(header, projection),
|
|
9162
9290
|
directory: header.cwd ?? "",
|
|
9163
9291
|
...header.parentSession ? { parent_reference: { agentName: this.name, sessionId: header.parentSession } } : {},
|
|
@@ -9193,7 +9321,7 @@ var DshAgent = class extends FileSystemSessionSource {
|
|
|
9193
9321
|
reference: { agentName: this.name, sessionId: header.id },
|
|
9194
9322
|
id: header.id,
|
|
9195
9323
|
title: this.resolveTitle(header, projection),
|
|
9196
|
-
slug:
|
|
9324
|
+
slug: this.sessionSlug(header.id),
|
|
9197
9325
|
directory: header.cwd ?? "",
|
|
9198
9326
|
...header.parentSession ? { parent_reference: { agentName: this.name, sessionId: header.parentSession } } : {},
|
|
9199
9327
|
version: "0",
|
|
@@ -9301,13 +9429,6 @@ var DshAgent = class extends FileSystemSessionSource {
|
|
|
9301
9429
|
);
|
|
9302
9430
|
}
|
|
9303
9431
|
}
|
|
9304
|
-
enumerationStep(stage, sourcePath, read) {
|
|
9305
|
-
try {
|
|
9306
|
-
return read();
|
|
9307
|
-
} catch (error) {
|
|
9308
|
-
throw new SessionScanError(this.name, stage, { cause: error, sourcePath });
|
|
9309
|
-
}
|
|
9310
|
-
}
|
|
9311
9432
|
statArtifact(sourcePath) {
|
|
9312
9433
|
try {
|
|
9313
9434
|
return statSync10(sourcePath, { bigint: true });
|
|
@@ -9405,39 +9526,39 @@ registerAgent({
|
|
|
9405
9526
|
toolStrategy: "custom",
|
|
9406
9527
|
create: () => new CursorAgent()
|
|
9407
9528
|
});
|
|
9408
|
-
var CACHE_SCHEMA_VERSION =
|
|
9529
|
+
var CACHE_SCHEMA_VERSION = 29;
|
|
9409
9530
|
|
|
9410
|
-
// ../core/dist/chunk-
|
|
9531
|
+
// ../core/dist/chunk-BORKFBHP.mjs
|
|
9411
9532
|
function mergeSessionsUpdatedEvents(previous, next) {
|
|
9412
9533
|
const changedSessionHeads = /* @__PURE__ */ new Map();
|
|
9413
9534
|
const projectionRelatedSessionHeads = /* @__PURE__ */ new Map();
|
|
9414
9535
|
const projectionSessionOrder = /* @__PURE__ */ new Map();
|
|
9415
9536
|
const newSessionRefs = /* @__PURE__ */ new Map();
|
|
9416
9537
|
const removedSessionRefs = /* @__PURE__ */ new Map();
|
|
9417
|
-
const
|
|
9538
|
+
const sessionKey22 = (agentName, sessionId) => `${agentName}\0${sessionId}`;
|
|
9418
9539
|
const addChanged = (item) => {
|
|
9419
|
-
const key =
|
|
9540
|
+
const key = sessionKey22(item.reference.agentName, item.reference.sessionId);
|
|
9420
9541
|
removedSessionRefs.delete(key);
|
|
9421
9542
|
projectionRelatedSessionHeads.delete(key);
|
|
9422
9543
|
changedSessionHeads.set(key, item);
|
|
9423
9544
|
};
|
|
9424
9545
|
const addProjectionRelated = (item) => {
|
|
9425
|
-
const key =
|
|
9546
|
+
const key = sessionKey22(item.reference.agentName, item.reference.sessionId);
|
|
9426
9547
|
if (changedSessionHeads.has(key) || removedSessionRefs.has(key)) return;
|
|
9427
9548
|
projectionRelatedSessionHeads.set(key, item);
|
|
9428
9549
|
};
|
|
9429
9550
|
const addNew = (item) => {
|
|
9430
|
-
const key =
|
|
9551
|
+
const key = sessionKey22(item.agentName, item.sessionId);
|
|
9431
9552
|
removedSessionRefs.delete(key);
|
|
9432
9553
|
newSessionRefs.set(key, item);
|
|
9433
9554
|
};
|
|
9434
9555
|
const addProjectionOrder = (item) => {
|
|
9435
|
-
const key =
|
|
9556
|
+
const key = sessionKey22(item.agentName, item.sessionId);
|
|
9436
9557
|
projectionSessionOrder.delete(key);
|
|
9437
9558
|
projectionSessionOrder.set(key, item);
|
|
9438
9559
|
};
|
|
9439
9560
|
const addRemoved = (item) => {
|
|
9440
|
-
const key =
|
|
9561
|
+
const key = sessionKey22(item.agentName, item.sessionId);
|
|
9441
9562
|
changedSessionHeads.delete(key);
|
|
9442
9563
|
projectionRelatedSessionHeads.delete(key);
|
|
9443
9564
|
projectionSessionOrder.delete(key);
|
|
@@ -9512,37 +9633,6 @@ function matchesProjectIdentity(identity, expected) {
|
|
|
9512
9633
|
function getProjectAgentKey(projectIdentityKey, agentName) {
|
|
9513
9634
|
return `${projectIdentityKey}\0${agentName.toLowerCase()}`;
|
|
9514
9635
|
}
|
|
9515
|
-
var UNKNOWN_AGENT_NAME = "unknown";
|
|
9516
|
-
function normalizeSessionReference(reference) {
|
|
9517
|
-
return {
|
|
9518
|
-
agentName: reference.agentName.trim().toLowerCase(),
|
|
9519
|
-
sessionId: reference.sessionId
|
|
9520
|
-
};
|
|
9521
|
-
}
|
|
9522
|
-
function parseSessionReference(value) {
|
|
9523
|
-
const separatorIndex = value.indexOf("/");
|
|
9524
|
-
if (separatorIndex <= 0 || separatorIndex === value.length - 1) return null;
|
|
9525
|
-
const agentName = value.slice(0, separatorIndex).trim().toLowerCase();
|
|
9526
|
-
if (!agentName) return null;
|
|
9527
|
-
return {
|
|
9528
|
-
agentName,
|
|
9529
|
-
sessionId: value.slice(separatorIndex + 1)
|
|
9530
|
-
};
|
|
9531
|
-
}
|
|
9532
|
-
function formatSessionReference(reference) {
|
|
9533
|
-
const normalized = normalizeSessionReference(reference);
|
|
9534
|
-
return `${normalized.agentName}/${normalized.sessionId}`;
|
|
9535
|
-
}
|
|
9536
|
-
function getSessionAgentKey(session) {
|
|
9537
|
-
return parseSessionReference(session.slug)?.agentName ?? UNKNOWN_AGENT_NAME;
|
|
9538
|
-
}
|
|
9539
|
-
function agentRoutePath(agentName) {
|
|
9540
|
-
return `/${encodeURIComponent(agentName.trim().toLowerCase())}`;
|
|
9541
|
-
}
|
|
9542
|
-
function sessionRoutePath(reference) {
|
|
9543
|
-
const normalized = normalizeSessionReference(reference);
|
|
9544
|
-
return `${agentRoutePath(normalized.agentName)}/${encodeURIComponent(normalized.sessionId)}`;
|
|
9545
|
-
}
|
|
9546
9636
|
function compareSessionActivityDesc(a, b) {
|
|
9547
9637
|
return (b.time_updated ?? b.time_created) - (a.time_updated ?? a.time_created);
|
|
9548
9638
|
}
|
|
@@ -9628,12 +9718,6 @@ function hasActivityInWindow(session, from, to) {
|
|
|
9628
9718
|
const activity = activityTime(session);
|
|
9629
9719
|
return (from == null || activity >= from) && (to == null || activity <= to);
|
|
9630
9720
|
}
|
|
9631
|
-
function isChildSession(session) {
|
|
9632
|
-
return session.parent_reference != null;
|
|
9633
|
-
}
|
|
9634
|
-
function getRootSessions(sessions) {
|
|
9635
|
-
return sessions.filter((session) => !session.parent_reference);
|
|
9636
|
-
}
|
|
9637
9721
|
function ownStats(session) {
|
|
9638
9722
|
const stats = session.stats;
|
|
9639
9723
|
const cost = stats.total_cost ?? 0;
|
|
@@ -9833,115 +9917,6 @@ function createSessionProjectionContext(previousSessions, nextSessions, changedS
|
|
|
9833
9917
|
);
|
|
9834
9918
|
return { relatedSessionHeads, sessionOrder };
|
|
9835
9919
|
}
|
|
9836
|
-
var SAMPLE_SESSION_HEAD = {
|
|
9837
|
-
id: "session-1",
|
|
9838
|
-
slug: "claudecode/session-1",
|
|
9839
|
-
title: "Fix flaky search index test",
|
|
9840
|
-
directory: "/Users/dev/project",
|
|
9841
|
-
project_identity: {
|
|
9842
|
-
kind: "git_remote",
|
|
9843
|
-
key: "github.com/example/project",
|
|
9844
|
-
displayName: "example/project"
|
|
9845
|
-
},
|
|
9846
|
-
time_created: 17e11,
|
|
9847
|
-
time_updated: 17000036e5,
|
|
9848
|
-
stats: {
|
|
9849
|
-
message_count: 12,
|
|
9850
|
-
total_input_tokens: 4200,
|
|
9851
|
-
total_output_tokens: 1800,
|
|
9852
|
-
total_cost: 0.042,
|
|
9853
|
-
cost_source: "recorded",
|
|
9854
|
-
total_tokens: 6e3,
|
|
9855
|
-
total_cache_read_tokens: 3e3,
|
|
9856
|
-
total_cache_create_tokens: 500
|
|
9857
|
-
},
|
|
9858
|
-
model_usage: { "claude-5-sonnet": 6e3 },
|
|
9859
|
-
smart_tags: ["bugfix"],
|
|
9860
|
-
smart_tags_source_updated_at: 17000036e5
|
|
9861
|
-
};
|
|
9862
|
-
var SAMPLE_SESSIONS_UPDATED_EVENT = {
|
|
9863
|
-
type: "sessions-updated",
|
|
9864
|
-
changedAgents: ["claudecode"],
|
|
9865
|
-
newSessions: 1,
|
|
9866
|
-
newSessionRefs: [{ agentName: "claudecode", sessionId: SAMPLE_SESSION_HEAD.id }],
|
|
9867
|
-
updatedSessions: 0,
|
|
9868
|
-
removedSessions: 0,
|
|
9869
|
-
totalSessions: 43,
|
|
9870
|
-
timestamp: 170000002e4,
|
|
9871
|
-
changedSessionHeads: [
|
|
9872
|
-
{
|
|
9873
|
-
reference: { agentName: "claudecode", sessionId: SAMPLE_SESSION_HEAD.id },
|
|
9874
|
-
session: SAMPLE_SESSION_HEAD
|
|
9875
|
-
}
|
|
9876
|
-
],
|
|
9877
|
-
projectionRelatedSessionHeads: [],
|
|
9878
|
-
projectionSessionOrder: [{ agentName: "claudecode", sessionId: SAMPLE_SESSION_HEAD.id }],
|
|
9879
|
-
removedSessionRefs: []
|
|
9880
|
-
};
|
|
9881
|
-
var SAMPLE_DASHBOARD_DATA = {
|
|
9882
|
-
totals: {
|
|
9883
|
-
sessions: 1,
|
|
9884
|
-
messages: 12,
|
|
9885
|
-
tokens: 6e3,
|
|
9886
|
-
cost: 0.042,
|
|
9887
|
-
costRecorded: 0.042,
|
|
9888
|
-
costEstimated: 0,
|
|
9889
|
-
cacheReadTokens: 3e3,
|
|
9890
|
-
cost_source: "recorded",
|
|
9891
|
-
latestActivity: 17000036e5,
|
|
9892
|
-
latestActivityProject: "example/project",
|
|
9893
|
-
latestActivityAgent: "claudecode"
|
|
9894
|
-
},
|
|
9895
|
-
scopeCounts: { projects: 1, agents: 1 },
|
|
9896
|
-
perAgent: [
|
|
9897
|
-
{
|
|
9898
|
-
name: "claudecode",
|
|
9899
|
-
displayName: "Claude Code",
|
|
9900
|
-
icon: "claude",
|
|
9901
|
-
sessions: 1,
|
|
9902
|
-
messages: 12,
|
|
9903
|
-
tokens: 6e3,
|
|
9904
|
-
cost: 0.042
|
|
9905
|
-
}
|
|
9906
|
-
],
|
|
9907
|
-
dailyActivity: [
|
|
9908
|
-
{
|
|
9909
|
-
date: "2023-11-14",
|
|
9910
|
-
sessions: 1,
|
|
9911
|
-
messages: 12,
|
|
9912
|
-
cost: 0.042,
|
|
9913
|
-
input: 700,
|
|
9914
|
-
output: 1800,
|
|
9915
|
-
cache_read: 3e3,
|
|
9916
|
-
cache_create: 500
|
|
9917
|
-
}
|
|
9918
|
-
],
|
|
9919
|
-
modelDistribution: [{ model: "claude-5-sonnet", tokens: 6e3, sessions: 1 }],
|
|
9920
|
-
perProject: [
|
|
9921
|
-
{
|
|
9922
|
-
identityKind: "git_remote",
|
|
9923
|
-
identityKey: "github.com/example/project",
|
|
9924
|
-
displayName: "example/project",
|
|
9925
|
-
sessions: 1,
|
|
9926
|
-
messages: 12,
|
|
9927
|
-
tokens: 6e3,
|
|
9928
|
-
cost: 0.042,
|
|
9929
|
-
cost_source: "recorded",
|
|
9930
|
-
agents: ["claudecode"],
|
|
9931
|
-
sparkline: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.042]
|
|
9932
|
-
}
|
|
9933
|
-
],
|
|
9934
|
-
projectRollup: { projects: 0, sessions: 0, tokens: 0, cost: 0 },
|
|
9935
|
-
recentSessions: [
|
|
9936
|
-
{
|
|
9937
|
-
reference: { agentName: "claudecode", sessionId: SAMPLE_SESSION_HEAD.id },
|
|
9938
|
-
session: SAMPLE_SESSION_HEAD
|
|
9939
|
-
}
|
|
9940
|
-
],
|
|
9941
|
-
recentFileActivities: [],
|
|
9942
|
-
modelCost: null,
|
|
9943
|
-
window: { from: 16999e8, to: 17000036e5, days: 1 }
|
|
9944
|
-
};
|
|
9945
9920
|
|
|
9946
9921
|
// ../core/dist/index.mjs
|
|
9947
9922
|
import { availableParallelism } from "os";
|
|
@@ -9951,7 +9926,6 @@ import { spawnSync } from "child_process";
|
|
|
9951
9926
|
import * as os from "os";
|
|
9952
9927
|
import * as path from "path";
|
|
9953
9928
|
import { createHash as createHash4 } from "crypto";
|
|
9954
|
-
import { resolve as resolve3, sep } from "path";
|
|
9955
9929
|
import { existsSync as existsSync32, rmSync as rmSync2, unlinkSync } from "fs";
|
|
9956
9930
|
import { existsSync as existsSync22 } from "fs";
|
|
9957
9931
|
import { join as join18 } from "path";
|
|
@@ -10017,6 +9991,7 @@ function normalizeGitRemote(url) {
|
|
|
10017
9991
|
return value.toLowerCase();
|
|
10018
9992
|
}
|
|
10019
9993
|
var IDENTITY_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
9994
|
+
var IDENTITY_CACHE_MAX_ENTRIES = 512;
|
|
10020
9995
|
var identityCache = /* @__PURE__ */ new Map();
|
|
10021
9996
|
function computeIdentity(cwd, fs) {
|
|
10022
9997
|
return computeIdentityProjection(cwd, fs).identity;
|
|
@@ -10025,15 +10000,25 @@ function normalizeProjectDirectory(cwd) {
|
|
|
10025
10000
|
if (!cwd) return "";
|
|
10026
10001
|
return getPathOps(cwd).resolve(cwd);
|
|
10027
10002
|
}
|
|
10028
|
-
function computeIdentityProjection(cwd, fs, resolverRevision = PROJECT_IDENTITY_RESOLVER_REVISION) {
|
|
10003
|
+
function computeIdentityProjection(cwd, fs = realFs, resolverRevision = PROJECT_IDENTITY_RESOLVER_REVISION) {
|
|
10029
10004
|
if (fs !== realFs) return resolveIdentityProjection(cwd, fs, resolverRevision);
|
|
10030
10005
|
const key = normalizeProjectDirectory(cwd);
|
|
10031
10006
|
const cached = identityCache.get(key);
|
|
10032
|
-
if (cached
|
|
10033
|
-
|
|
10007
|
+
if (cached) {
|
|
10008
|
+
if (cached.projection.resolverRevision === resolverRevision && Date.now() - cached.resolvedAt < IDENTITY_CACHE_TTL_MS) {
|
|
10009
|
+
identityCache.delete(key);
|
|
10010
|
+
identityCache.set(key, cached);
|
|
10011
|
+
return cached.projection;
|
|
10012
|
+
}
|
|
10013
|
+
identityCache.delete(key);
|
|
10034
10014
|
}
|
|
10035
10015
|
const projection = resolveIdentityProjection(cwd, fs, resolverRevision);
|
|
10036
10016
|
identityCache.set(key, { projection, resolvedAt: Date.now() });
|
|
10017
|
+
while (identityCache.size > IDENTITY_CACHE_MAX_ENTRIES) {
|
|
10018
|
+
const oldestKey = identityCache.keys().next().value;
|
|
10019
|
+
if (oldestKey == null) break;
|
|
10020
|
+
identityCache.delete(oldestKey);
|
|
10021
|
+
}
|
|
10037
10022
|
return projection;
|
|
10038
10023
|
}
|
|
10039
10024
|
function resolveIdentityProjection(cwd, fs, resolverRevision) {
|
|
@@ -10224,9 +10209,12 @@ function buildProjectGroups(sessions) {
|
|
|
10224
10209
|
}
|
|
10225
10210
|
function createProjectScopeMatcher(queryPath, fs = realFs) {
|
|
10226
10211
|
const identity = computeIdentity(queryPath, fs);
|
|
10212
|
+
return createProjectScopeMatcherFromIdentity(queryPath, identity);
|
|
10213
|
+
}
|
|
10214
|
+
function createProjectScopeMatcherFromIdentity(queryPath, identity) {
|
|
10227
10215
|
return {
|
|
10228
10216
|
identity: { kind: identity.kind, key: identity.key },
|
|
10229
|
-
path:
|
|
10217
|
+
path: normalizeProjectScopePath(queryPath)
|
|
10230
10218
|
};
|
|
10231
10219
|
}
|
|
10232
10220
|
function matchesProjectScope(session, scope) {
|
|
@@ -10238,11 +10226,11 @@ function filterSessionsByProjectScope(sessions, queryPath, fs) {
|
|
|
10238
10226
|
return sessions.filter((session) => matchesProjectScope(session, scope));
|
|
10239
10227
|
}
|
|
10240
10228
|
function isPathScopeMatch(queryPath, sessionPath) {
|
|
10241
|
-
const session =
|
|
10229
|
+
const session = normalizeProjectScopePath(sessionPath);
|
|
10242
10230
|
return session === queryPath || session.startsWith(queryPath + "/") || queryPath.startsWith(session + "/");
|
|
10243
10231
|
}
|
|
10244
|
-
function
|
|
10245
|
-
return
|
|
10232
|
+
function normalizeProjectScopePath(path2) {
|
|
10233
|
+
return normalizeProjectDirectory(path2).replaceAll("\\", "/");
|
|
10246
10234
|
}
|
|
10247
10235
|
var SMART_TAG_CLASSIFIER_REVISION = "smart-tags-v1";
|
|
10248
10236
|
var TAG_ORDER = [
|
|
@@ -10596,6 +10584,44 @@ function normalizeFilePathSearch(value) {
|
|
|
10596
10584
|
return value.trim().replace(/^"|"$/g, "");
|
|
10597
10585
|
}
|
|
10598
10586
|
var MESSAGE_PARTS_FORMAT_VERSION = 1;
|
|
10587
|
+
function messageCursorContentFromCachedRow(row) {
|
|
10588
|
+
return {
|
|
10589
|
+
messageId: String(row.message_id),
|
|
10590
|
+
role: String(row.role),
|
|
10591
|
+
timeCreated: Number(row.time_created),
|
|
10592
|
+
timeCompleted: row.time_completed,
|
|
10593
|
+
agent: row.agent,
|
|
10594
|
+
mode: row.mode,
|
|
10595
|
+
model: row.model,
|
|
10596
|
+
provider: row.provider,
|
|
10597
|
+
tokensJson: row.tokens_json,
|
|
10598
|
+
cost: row.cost,
|
|
10599
|
+
costSource: row.cost_source,
|
|
10600
|
+
partsJson: String(row.parts_json),
|
|
10601
|
+
partsFormatVersion: row.parts_format_version,
|
|
10602
|
+
subagentId: row.subagent_id,
|
|
10603
|
+
nickname: row.nickname
|
|
10604
|
+
};
|
|
10605
|
+
}
|
|
10606
|
+
function messageCursorContentFromStructuredRecord(record) {
|
|
10607
|
+
return {
|
|
10608
|
+
messageId: record.id,
|
|
10609
|
+
role: record.role,
|
|
10610
|
+
timeCreated: record.timeCreated,
|
|
10611
|
+
timeCompleted: record.timeCompleted,
|
|
10612
|
+
agent: record.agent,
|
|
10613
|
+
mode: record.mode,
|
|
10614
|
+
model: record.model,
|
|
10615
|
+
provider: record.provider,
|
|
10616
|
+
tokensJson: record.tokensJson,
|
|
10617
|
+
cost: record.cost,
|
|
10618
|
+
costSource: record.costSource,
|
|
10619
|
+
partsJson: record.partsJson,
|
|
10620
|
+
partsFormatVersion: MESSAGE_PARTS_FORMAT_VERSION,
|
|
10621
|
+
subagentId: record.subagentId,
|
|
10622
|
+
nickname: record.nickname
|
|
10623
|
+
};
|
|
10624
|
+
}
|
|
10599
10625
|
function stringifyOptionalJson(value) {
|
|
10600
10626
|
return value == null ? null : JSON.stringify(value);
|
|
10601
10627
|
}
|
|
@@ -10610,6 +10636,13 @@ function sourcePathFromMetaJson(metaJson) {
|
|
|
10610
10636
|
const meta = JSON.parse(metaJson);
|
|
10611
10637
|
return sourcePathFromMeta(meta);
|
|
10612
10638
|
}
|
|
10639
|
+
function requireSessionProjectIdentity(agentName, session) {
|
|
10640
|
+
if (session.project_identity) return session.project_identity;
|
|
10641
|
+
throw new Error(`Session ${agentName}/${session.id} is missing project_identity`);
|
|
10642
|
+
}
|
|
10643
|
+
function assertSessionProjectIdentities(agentName, sessions) {
|
|
10644
|
+
for (const session of sessions) requireSessionProjectIdentity(agentName, session);
|
|
10645
|
+
}
|
|
10613
10646
|
function prepareUpsertSession(db) {
|
|
10614
10647
|
return db.prepare(`
|
|
10615
10648
|
INSERT INTO sessions(
|
|
@@ -10749,7 +10782,7 @@ function prepareUpsertIndexedSession(db) {
|
|
|
10749
10782
|
return prepareIndexedSession(db);
|
|
10750
10783
|
}
|
|
10751
10784
|
function upsertSessionRow(statement, agentName, session, metaJson, sortIndex, sourcePath) {
|
|
10752
|
-
const identity =
|
|
10785
|
+
const identity = requireSessionProjectIdentity(agentName, session);
|
|
10753
10786
|
const activityTime2 = session.time_updated ?? session.time_created;
|
|
10754
10787
|
statement.run(
|
|
10755
10788
|
agentName,
|
|
@@ -11174,16 +11207,62 @@ function discardPublicationStaging(db, publicationId) {
|
|
|
11174
11207
|
db.prepare("DELETE FROM search_index_publication_entries").run();
|
|
11175
11208
|
}
|
|
11176
11209
|
}
|
|
11177
|
-
function
|
|
11178
|
-
const
|
|
11179
|
-
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11210
|
+
function prepareLegacyProjectIdentityResolver(db, currentVersion) {
|
|
11211
|
+
const directories = /* @__PURE__ */ new Set();
|
|
11212
|
+
if (currentVersion < 7 && tableExists(db, "cached_sessions")) {
|
|
11213
|
+
const rows = db.prepare("SELECT agent_name, session_id, session_json FROM cached_sessions").all();
|
|
11214
|
+
for (const row of rows) {
|
|
11215
|
+
if (!row.session_json) continue;
|
|
11216
|
+
try {
|
|
11217
|
+
const session = JSON.parse(row.session_json);
|
|
11218
|
+
if (session.directory != null) {
|
|
11219
|
+
directories.add(String(session.directory));
|
|
11220
|
+
} else {
|
|
11221
|
+
getCoreDiagnostics()?.warn("sqlite.migration.identity_precompute.missing_directory", {
|
|
11222
|
+
agent_name: row.agent_name,
|
|
11223
|
+
session_id: row.session_id
|
|
11224
|
+
});
|
|
11225
|
+
}
|
|
11226
|
+
} catch {
|
|
11227
|
+
continue;
|
|
11228
|
+
}
|
|
11229
|
+
}
|
|
11230
|
+
}
|
|
11231
|
+
if (currentVersion < 12) {
|
|
11232
|
+
for (const table of ["session_documents", "sessions", "project_sessions"]) {
|
|
11233
|
+
if (!tableExists(db, table) || !columnExists(db, table, "directory")) continue;
|
|
11234
|
+
const rows = db.prepare(`SELECT directory FROM ${table}`).all();
|
|
11235
|
+
for (const row of rows) directories.add(String(row.directory ?? ""));
|
|
11236
|
+
}
|
|
11237
|
+
}
|
|
11238
|
+
const identities = /* @__PURE__ */ new Map();
|
|
11239
|
+
for (const directory of directories) {
|
|
11240
|
+
try {
|
|
11241
|
+
identities.set(directory, computeIdentity(directory, realFs));
|
|
11242
|
+
} catch (error) {
|
|
11243
|
+
identities.set(directory, error instanceof Error ? error : new Error(String(error)));
|
|
11244
|
+
}
|
|
11245
|
+
}
|
|
11246
|
+
return (directory) => {
|
|
11247
|
+
const identity = identities.get(directory);
|
|
11248
|
+
if (identity instanceof Error) throw identity;
|
|
11249
|
+
if (identity) return identity;
|
|
11250
|
+
throw new Error(`Missing precomputed project identity for legacy directory: ${directory}`);
|
|
11251
|
+
};
|
|
11252
|
+
}
|
|
11253
|
+
function getLegacySessionDirectory(session) {
|
|
11254
|
+
return typeof session.directory === "string" ? session.directory : null;
|
|
11255
|
+
}
|
|
11256
|
+
function withCacheConnection(fn) {
|
|
11257
|
+
const cachePath = getCachePath2();
|
|
11258
|
+
const connection = getCacheConnection(cachePath);
|
|
11259
|
+
if (!connection) return null;
|
|
11260
|
+
try {
|
|
11261
|
+
if (getSchemaEnsuredPath() !== cachePath) {
|
|
11262
|
+
ensureSchema(connection.db, cachePath);
|
|
11263
|
+
setSchemaEnsuredPath(cachePath);
|
|
11264
|
+
}
|
|
11265
|
+
return fn(connection);
|
|
11187
11266
|
} catch (error) {
|
|
11188
11267
|
getCoreDiagnostics()?.warn("cache.write_failed", {
|
|
11189
11268
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -11211,6 +11290,13 @@ function cleanPublicationStaging(connection) {
|
|
|
11211
11290
|
function withCacheDb(fn) {
|
|
11212
11291
|
return withCacheConnection(({ db }) => fn(db));
|
|
11213
11292
|
}
|
|
11293
|
+
function withCacheDbOutcome(fn) {
|
|
11294
|
+
let result = { status: "failed" };
|
|
11295
|
+
withCacheConnection(({ db }) => {
|
|
11296
|
+
result = { status: "success", value: fn(db) };
|
|
11297
|
+
});
|
|
11298
|
+
return result;
|
|
11299
|
+
}
|
|
11214
11300
|
function withCacheDbReadOnly(fn) {
|
|
11215
11301
|
const cachePath = getCachePath2();
|
|
11216
11302
|
if (!hasCacheStorage()) return { status: "failed" };
|
|
@@ -11328,6 +11414,9 @@ function createSessionTables(db) {
|
|
|
11328
11414
|
CREATE INDEX IF NOT EXISTS idx_sessions_agent_activity_order
|
|
11329
11415
|
ON sessions(agent_name, activity_time DESC, session_id);
|
|
11330
11416
|
|
|
11417
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_activity
|
|
11418
|
+
ON sessions(activity_time DESC, agent_name, session_id);
|
|
11419
|
+
|
|
11331
11420
|
CREATE INDEX IF NOT EXISTS idx_sessions_project
|
|
11332
11421
|
ON sessions(project_identity_kind, project_identity_key, activity_time);
|
|
11333
11422
|
|
|
@@ -11351,6 +11440,7 @@ function createSessionTables(db) {
|
|
|
11351
11440
|
cost_source TEXT,
|
|
11352
11441
|
parts_json TEXT NOT NULL,
|
|
11353
11442
|
parts_format_version INTEGER NOT NULL DEFAULT 0,
|
|
11443
|
+
content_chain_digest TEXT,
|
|
11354
11444
|
subagent_id TEXT,
|
|
11355
11445
|
nickname TEXT,
|
|
11356
11446
|
content_text TEXT NOT NULL,
|
|
@@ -11363,9 +11453,62 @@ function createSessionTables(db) {
|
|
|
11363
11453
|
|
|
11364
11454
|
CREATE INDEX IF NOT EXISTS idx_messages_session
|
|
11365
11455
|
ON messages(agent_name, session_id, message_index);
|
|
11456
|
+
|
|
11457
|
+
CREATE INDEX IF NOT EXISTS idx_messages_usage_time
|
|
11458
|
+
ON messages(
|
|
11459
|
+
CASE
|
|
11460
|
+
WHEN time_completed > 0 THEN time_completed
|
|
11461
|
+
WHEN time_created > 0 THEN time_created
|
|
11462
|
+
END,
|
|
11463
|
+
agent_name,
|
|
11464
|
+
session_id
|
|
11465
|
+
);
|
|
11366
11466
|
`);
|
|
11467
|
+
createSessionModelCostTable(db);
|
|
11468
|
+
createSessionCostSummaryTable(db);
|
|
11367
11469
|
createMessageToolTables(db);
|
|
11368
11470
|
}
|
|
11471
|
+
function createSessionModelCostTable(db) {
|
|
11472
|
+
db.exec(`
|
|
11473
|
+
CREATE TABLE IF NOT EXISTS session_model_cost (
|
|
11474
|
+
agent_name TEXT NOT NULL,
|
|
11475
|
+
session_id TEXT NOT NULL,
|
|
11476
|
+
model TEXT NOT NULL,
|
|
11477
|
+
cost REAL NOT NULL,
|
|
11478
|
+
cost_recorded REAL NOT NULL,
|
|
11479
|
+
PRIMARY KEY (agent_name, session_id, model),
|
|
11480
|
+
FOREIGN KEY (agent_name, session_id)
|
|
11481
|
+
REFERENCES sessions(agent_name, session_id)
|
|
11482
|
+
ON DELETE CASCADE
|
|
11483
|
+
);
|
|
11484
|
+
`);
|
|
11485
|
+
}
|
|
11486
|
+
function createSessionCostSummaryTable(db) {
|
|
11487
|
+
db.exec(`
|
|
11488
|
+
CREATE TABLE IF NOT EXISTS session_cost_summary (
|
|
11489
|
+
agent_name TEXT NOT NULL,
|
|
11490
|
+
session_id TEXT NOT NULL,
|
|
11491
|
+
message_count INTEGER NOT NULL DEFAULT 0,
|
|
11492
|
+
untimed_message_count INTEGER NOT NULL DEFAULT 0,
|
|
11493
|
+
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11494
|
+
output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11495
|
+
reasoning_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11496
|
+
cache_read_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11497
|
+
cache_create_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11498
|
+
untimed_input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11499
|
+
untimed_output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11500
|
+
untimed_reasoning_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11501
|
+
untimed_cache_read_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11502
|
+
untimed_cache_create_tokens INTEGER NOT NULL DEFAULT 0,
|
|
11503
|
+
message_cost REAL NOT NULL,
|
|
11504
|
+
untimed_message_cost REAL NOT NULL,
|
|
11505
|
+
PRIMARY KEY (agent_name, session_id),
|
|
11506
|
+
FOREIGN KEY (agent_name, session_id)
|
|
11507
|
+
REFERENCES sessions(agent_name, session_id)
|
|
11508
|
+
ON DELETE CASCADE
|
|
11509
|
+
);
|
|
11510
|
+
`);
|
|
11511
|
+
}
|
|
11369
11512
|
function createMessageToolTables(db) {
|
|
11370
11513
|
db.exec(`
|
|
11371
11514
|
CREATE TABLE IF NOT EXISTS message_tools (
|
|
@@ -11725,7 +11868,7 @@ function hasAnyCacheSchema(db) {
|
|
|
11725
11868
|
"project_sessions"
|
|
11726
11869
|
].some((table) => tableExists(db, table));
|
|
11727
11870
|
}
|
|
11728
|
-
function backfillProjectSessions(db) {
|
|
11871
|
+
function backfillProjectSessions(db, resolveIdentity) {
|
|
11729
11872
|
if (!tableExists(db, "cached_sessions") || !tableExists(db, "project_sessions")) {
|
|
11730
11873
|
return;
|
|
11731
11874
|
}
|
|
@@ -11753,14 +11896,16 @@ function backfillProjectSessions(db) {
|
|
|
11753
11896
|
}
|
|
11754
11897
|
try {
|
|
11755
11898
|
const session = JSON.parse(row.session_json);
|
|
11756
|
-
const
|
|
11899
|
+
const directory = getLegacySessionDirectory(session);
|
|
11900
|
+
if (directory == null) continue;
|
|
11901
|
+
const identity = session.project_identity ?? resolveIdentity(directory);
|
|
11757
11902
|
upsert.run(
|
|
11758
11903
|
row.agent_name,
|
|
11759
11904
|
row.session_id,
|
|
11760
11905
|
identity.kind,
|
|
11761
11906
|
identity.key,
|
|
11762
11907
|
identity.displayName,
|
|
11763
|
-
|
|
11908
|
+
directory,
|
|
11764
11909
|
session.time_updated ?? session.time_created
|
|
11765
11910
|
);
|
|
11766
11911
|
} catch {
|
|
@@ -11768,7 +11913,7 @@ function backfillProjectSessions(db) {
|
|
|
11768
11913
|
}
|
|
11769
11914
|
}
|
|
11770
11915
|
}
|
|
11771
|
-
function backfillSessionDocumentProjects(db) {
|
|
11916
|
+
function backfillSessionDocumentProjects(db, resolveIdentity) {
|
|
11772
11917
|
if (!tableExists(db, "session_documents") || !columnExists(db, "session_documents", "project_identity_key")) {
|
|
11773
11918
|
return;
|
|
11774
11919
|
}
|
|
@@ -11782,17 +11927,17 @@ function backfillSessionDocumentProjects(db) {
|
|
|
11782
11927
|
WHERE id = ?
|
|
11783
11928
|
`);
|
|
11784
11929
|
for (const row of rows) {
|
|
11785
|
-
const identity =
|
|
11930
|
+
const identity = resolveIdentity(String(row.directory ?? ""));
|
|
11786
11931
|
update.run(identity.kind, identity.key, identity.displayName, Number(row.id));
|
|
11787
11932
|
}
|
|
11788
11933
|
}
|
|
11789
|
-
function migrateProjectIdentity(db) {
|
|
11934
|
+
function migrateProjectIdentity(db, resolveIdentity) {
|
|
11790
11935
|
ensureLegacySessionDocumentColumns(db);
|
|
11791
11936
|
createProjectTables(db);
|
|
11792
|
-
backfillProjectSessions(db);
|
|
11793
|
-
backfillSessionDocumentProjects(db);
|
|
11937
|
+
backfillProjectSessions(db, resolveIdentity);
|
|
11938
|
+
backfillSessionDocumentProjects(db, resolveIdentity);
|
|
11794
11939
|
}
|
|
11795
|
-
function refreshProjectIdentities(db) {
|
|
11940
|
+
function refreshProjectIdentities(db, resolveIdentity) {
|
|
11796
11941
|
if (tableExists(db, "sessions") && columnExists(db, "sessions", "project_identity_key") && columnExists(db, "sessions", "directory")) {
|
|
11797
11942
|
const rows = db.prepare("SELECT agent_name, session_id, directory FROM sessions").all();
|
|
11798
11943
|
const update = db.prepare(`
|
|
@@ -11809,7 +11954,7 @@ function refreshProjectIdentities(db) {
|
|
|
11809
11954
|
WHERE agent_name = ? AND session_id = ?
|
|
11810
11955
|
`) : null;
|
|
11811
11956
|
for (const row of rows) {
|
|
11812
|
-
const identity =
|
|
11957
|
+
const identity = resolveIdentity(String(row.directory ?? ""));
|
|
11813
11958
|
update.run(identity.kind, identity.key, identity.displayName, row.agent_name, row.session_id);
|
|
11814
11959
|
updateFileActivity?.run(identity.key, row.agent_name, row.session_id);
|
|
11815
11960
|
}
|
|
@@ -11825,14 +11970,14 @@ function refreshProjectIdentities(db) {
|
|
|
11825
11970
|
WHERE agent_name = ? AND session_id = ?
|
|
11826
11971
|
`);
|
|
11827
11972
|
for (const row of rows) {
|
|
11828
|
-
const identity =
|
|
11973
|
+
const identity = resolveIdentity(String(row.directory ?? ""));
|
|
11829
11974
|
update.run(identity.kind, identity.key, identity.displayName, row.agent_name, row.session_id);
|
|
11830
11975
|
}
|
|
11831
11976
|
}
|
|
11832
|
-
backfillSessionDocumentProjects(db);
|
|
11977
|
+
backfillSessionDocumentProjects(db, resolveIdentity);
|
|
11833
11978
|
recreateProjectGroupsView(db);
|
|
11834
11979
|
}
|
|
11835
|
-
function backfillStructuredSessions(db) {
|
|
11980
|
+
function backfillStructuredSessions(db, resolveIdentity) {
|
|
11836
11981
|
createSessionTables(db);
|
|
11837
11982
|
recreateProjectGroupsView(db);
|
|
11838
11983
|
const upsertSession = prepareUpsertSession(db);
|
|
@@ -11845,7 +11990,10 @@ function backfillStructuredSessions(db) {
|
|
|
11845
11990
|
continue;
|
|
11846
11991
|
}
|
|
11847
11992
|
try {
|
|
11848
|
-
const
|
|
11993
|
+
const parsed = JSON.parse(row.session_json);
|
|
11994
|
+
const directory = getLegacySessionDirectory(parsed);
|
|
11995
|
+
if (directory == null) continue;
|
|
11996
|
+
const session = parsed.project_identity == null ? { ...parsed, project_identity: resolveIdentity(directory) } : parsed;
|
|
11849
11997
|
upsertSessionRow(
|
|
11850
11998
|
upsertSession,
|
|
11851
11999
|
String(row.agent_name),
|
|
@@ -11889,7 +12037,7 @@ function backfillStructuredSessions(db) {
|
|
|
11889
12037
|
kind: row.project_identity_kind,
|
|
11890
12038
|
key: String(row.project_identity_key),
|
|
11891
12039
|
displayName: String(row.project_display_name)
|
|
11892
|
-
} :
|
|
12040
|
+
} : resolveIdentity(directory);
|
|
11893
12041
|
upsertSessionRow(
|
|
11894
12042
|
upsertSession,
|
|
11895
12043
|
String(row.agent_name),
|
|
@@ -12015,6 +12163,174 @@ function replaceSessionActivityIndex(db) {
|
|
|
12015
12163
|
ON sessions(agent_name, activity_time DESC, session_id);
|
|
12016
12164
|
`);
|
|
12017
12165
|
}
|
|
12166
|
+
function addSessionActivityIndex(db) {
|
|
12167
|
+
if (!tableExists(db, "sessions")) return;
|
|
12168
|
+
db.exec(`
|
|
12169
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_activity
|
|
12170
|
+
ON sessions(activity_time DESC, agent_name, session_id)
|
|
12171
|
+
`);
|
|
12172
|
+
}
|
|
12173
|
+
function addSessionModelCostRollup(db) {
|
|
12174
|
+
createSessionModelCostTable(db);
|
|
12175
|
+
if (!tableExists(db, "messages")) return;
|
|
12176
|
+
db.exec(`
|
|
12177
|
+
INSERT OR REPLACE INTO session_model_cost(agent_name, session_id, model, cost, cost_recorded)
|
|
12178
|
+
SELECT
|
|
12179
|
+
agent_name,
|
|
12180
|
+
session_id,
|
|
12181
|
+
model,
|
|
12182
|
+
SUM(COALESCE(cost, 0)),
|
|
12183
|
+
SUM(CASE WHEN cost_source = 'recorded' THEN COALESCE(cost, 0) ELSE 0 END)
|
|
12184
|
+
FROM messages
|
|
12185
|
+
WHERE model IS NOT NULL AND model <> ''
|
|
12186
|
+
GROUP BY agent_name, session_id, model
|
|
12187
|
+
`);
|
|
12188
|
+
}
|
|
12189
|
+
function addSessionCostSummary(db) {
|
|
12190
|
+
db.exec(`
|
|
12191
|
+
CREATE TABLE IF NOT EXISTS session_cost_summary (
|
|
12192
|
+
agent_name TEXT NOT NULL,
|
|
12193
|
+
session_id TEXT NOT NULL,
|
|
12194
|
+
message_cost REAL NOT NULL,
|
|
12195
|
+
untimed_message_cost REAL NOT NULL,
|
|
12196
|
+
PRIMARY KEY (agent_name, session_id),
|
|
12197
|
+
FOREIGN KEY (agent_name, session_id)
|
|
12198
|
+
REFERENCES sessions(agent_name, session_id)
|
|
12199
|
+
ON DELETE CASCADE
|
|
12200
|
+
);
|
|
12201
|
+
`);
|
|
12202
|
+
if (!tableExists(db, "messages")) return;
|
|
12203
|
+
db.exec(`
|
|
12204
|
+
CREATE INDEX IF NOT EXISTS idx_messages_cost_time
|
|
12205
|
+
ON messages(
|
|
12206
|
+
CASE
|
|
12207
|
+
WHEN time_completed > 0 THEN time_completed
|
|
12208
|
+
WHEN time_created > 0 THEN time_created
|
|
12209
|
+
END,
|
|
12210
|
+
agent_name,
|
|
12211
|
+
session_id
|
|
12212
|
+
)
|
|
12213
|
+
WHERE cost > 0;
|
|
12214
|
+
`);
|
|
12215
|
+
if (columnExists(db, "session_cost_summary", "message_count")) return;
|
|
12216
|
+
db.exec(`
|
|
12217
|
+
|
|
12218
|
+
INSERT OR REPLACE INTO session_cost_summary(
|
|
12219
|
+
agent_name,
|
|
12220
|
+
session_id,
|
|
12221
|
+
message_cost,
|
|
12222
|
+
untimed_message_cost
|
|
12223
|
+
)
|
|
12224
|
+
SELECT
|
|
12225
|
+
m.agent_name,
|
|
12226
|
+
m.session_id,
|
|
12227
|
+
SUM(CASE WHEN m.cost > 0 THEN m.cost ELSE 0 END),
|
|
12228
|
+
SUM(
|
|
12229
|
+
CASE
|
|
12230
|
+
WHEN m.cost > 0
|
|
12231
|
+
AND COALESCE(m.time_completed, 0) <= 0
|
|
12232
|
+
AND COALESCE(m.time_created, 0) <= 0
|
|
12233
|
+
THEN m.cost
|
|
12234
|
+
ELSE 0
|
|
12235
|
+
END
|
|
12236
|
+
)
|
|
12237
|
+
FROM messages m
|
|
12238
|
+
JOIN sessions s
|
|
12239
|
+
ON s.agent_name = m.agent_name
|
|
12240
|
+
AND s.session_id = m.session_id
|
|
12241
|
+
GROUP BY m.agent_name, m.session_id
|
|
12242
|
+
`);
|
|
12243
|
+
}
|
|
12244
|
+
function addSessionUsageSummary(db) {
|
|
12245
|
+
if (!tableExists(db, "session_cost_summary")) addSessionCostSummary(db);
|
|
12246
|
+
const columns = {
|
|
12247
|
+
message_count: "INTEGER NOT NULL DEFAULT 0",
|
|
12248
|
+
untimed_message_count: "INTEGER NOT NULL DEFAULT 0",
|
|
12249
|
+
input_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
12250
|
+
output_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
12251
|
+
reasoning_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
12252
|
+
cache_read_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
12253
|
+
cache_create_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
12254
|
+
untimed_input_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
12255
|
+
untimed_output_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
12256
|
+
untimed_reasoning_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
12257
|
+
untimed_cache_read_tokens: "INTEGER NOT NULL DEFAULT 0",
|
|
12258
|
+
untimed_cache_create_tokens: "INTEGER NOT NULL DEFAULT 0"
|
|
12259
|
+
};
|
|
12260
|
+
for (const [name, definition] of Object.entries(columns)) {
|
|
12261
|
+
if (!columnExists(db, "session_cost_summary", name)) {
|
|
12262
|
+
db.exec(`ALTER TABLE session_cost_summary ADD COLUMN ${name} ${definition}`);
|
|
12263
|
+
}
|
|
12264
|
+
}
|
|
12265
|
+
if (!tableExists(db, "messages")) return;
|
|
12266
|
+
db.exec(`
|
|
12267
|
+
DROP INDEX IF EXISTS idx_messages_cost_time;
|
|
12268
|
+
CREATE INDEX IF NOT EXISTS idx_messages_usage_time
|
|
12269
|
+
ON messages(
|
|
12270
|
+
CASE
|
|
12271
|
+
WHEN time_completed > 0 THEN time_completed
|
|
12272
|
+
WHEN time_created > 0 THEN time_created
|
|
12273
|
+
END,
|
|
12274
|
+
agent_name,
|
|
12275
|
+
session_id
|
|
12276
|
+
);
|
|
12277
|
+
|
|
12278
|
+
DELETE FROM session_cost_summary;
|
|
12279
|
+
WITH normalized AS (
|
|
12280
|
+
SELECT
|
|
12281
|
+
m.agent_name,
|
|
12282
|
+
m.session_id,
|
|
12283
|
+
COALESCE(m.time_completed, 0) <= 0 AND COALESCE(m.time_created, 0) <= 0 AS untimed,
|
|
12284
|
+
MAX(CAST(COALESCE(json_extract(m.tokens_json, '$.input'), 0) AS INTEGER), 0) AS input_tokens,
|
|
12285
|
+
MAX(CAST(COALESCE(json_extract(m.tokens_json, '$.output'), 0) AS INTEGER), 0) AS output_tokens,
|
|
12286
|
+
MAX(CAST(COALESCE(json_extract(m.tokens_json, '$.reasoning'), 0) AS INTEGER), 0) AS reasoning_tokens,
|
|
12287
|
+
MAX(CAST(COALESCE(json_extract(m.tokens_json, '$.cache_read'), 0) AS INTEGER), 0) AS cache_read_tokens,
|
|
12288
|
+
MAX(CAST(COALESCE(json_extract(m.tokens_json, '$.cache_create'), 0) AS INTEGER), 0) AS cache_create_tokens,
|
|
12289
|
+
CASE WHEN m.cost > 0 THEN m.cost ELSE 0 END AS cost
|
|
12290
|
+
FROM messages m
|
|
12291
|
+
JOIN sessions s
|
|
12292
|
+
ON s.agent_name = m.agent_name
|
|
12293
|
+
AND s.session_id = m.session_id
|
|
12294
|
+
)
|
|
12295
|
+
INSERT INTO session_cost_summary(
|
|
12296
|
+
agent_name,
|
|
12297
|
+
session_id,
|
|
12298
|
+
message_count,
|
|
12299
|
+
untimed_message_count,
|
|
12300
|
+
input_tokens,
|
|
12301
|
+
output_tokens,
|
|
12302
|
+
reasoning_tokens,
|
|
12303
|
+
cache_read_tokens,
|
|
12304
|
+
cache_create_tokens,
|
|
12305
|
+
untimed_input_tokens,
|
|
12306
|
+
untimed_output_tokens,
|
|
12307
|
+
untimed_reasoning_tokens,
|
|
12308
|
+
untimed_cache_read_tokens,
|
|
12309
|
+
untimed_cache_create_tokens,
|
|
12310
|
+
message_cost,
|
|
12311
|
+
untimed_message_cost
|
|
12312
|
+
)
|
|
12313
|
+
SELECT
|
|
12314
|
+
agent_name,
|
|
12315
|
+
session_id,
|
|
12316
|
+
COUNT(*),
|
|
12317
|
+
SUM(CASE WHEN untimed THEN 1 ELSE 0 END),
|
|
12318
|
+
SUM(input_tokens),
|
|
12319
|
+
SUM(output_tokens),
|
|
12320
|
+
SUM(reasoning_tokens),
|
|
12321
|
+
SUM(cache_read_tokens),
|
|
12322
|
+
SUM(cache_create_tokens),
|
|
12323
|
+
SUM(CASE WHEN untimed THEN input_tokens ELSE 0 END),
|
|
12324
|
+
SUM(CASE WHEN untimed THEN output_tokens ELSE 0 END),
|
|
12325
|
+
SUM(CASE WHEN untimed THEN reasoning_tokens ELSE 0 END),
|
|
12326
|
+
SUM(CASE WHEN untimed THEN cache_read_tokens ELSE 0 END),
|
|
12327
|
+
SUM(CASE WHEN untimed THEN cache_create_tokens ELSE 0 END),
|
|
12328
|
+
SUM(cost),
|
|
12329
|
+
SUM(CASE WHEN untimed THEN cost ELSE 0 END)
|
|
12330
|
+
FROM normalized
|
|
12331
|
+
GROUP BY agent_name, session_id;
|
|
12332
|
+
`);
|
|
12333
|
+
}
|
|
12018
12334
|
function compactSessionDocuments(db) {
|
|
12019
12335
|
if (!tableExists(db, "session_documents")) {
|
|
12020
12336
|
createSearchTables(db);
|
|
@@ -12058,6 +12374,12 @@ function addMessagePartsFormatVersion(db) {
|
|
|
12058
12374
|
}
|
|
12059
12375
|
db.exec("ALTER TABLE messages ADD COLUMN parts_format_version INTEGER NOT NULL DEFAULT 0");
|
|
12060
12376
|
}
|
|
12377
|
+
function addMessageContentChainDigest(db) {
|
|
12378
|
+
if (!tableExists(db, "messages") || columnExists(db, "messages", "content_chain_digest")) {
|
|
12379
|
+
return;
|
|
12380
|
+
}
|
|
12381
|
+
db.exec("ALTER TABLE messages ADD COLUMN content_chain_digest TEXT");
|
|
12382
|
+
}
|
|
12061
12383
|
function addSessionParentReference(db) {
|
|
12062
12384
|
if (!tableExists(db, "sessions")) return;
|
|
12063
12385
|
if (!columnExists(db, "sessions", "parent_agent_name")) {
|
|
@@ -12213,6 +12535,7 @@ function ensureSchema(db, dbPath) {
|
|
|
12213
12535
|
migrateSubagentTree(db);
|
|
12214
12536
|
return;
|
|
12215
12537
|
}
|
|
12538
|
+
const resolveLegacyProjectIdentity = prepareLegacyProjectIdentityResolver(db, currentVersion);
|
|
12216
12539
|
runSchemaMigrations(db, {
|
|
12217
12540
|
dbPath,
|
|
12218
12541
|
currentVersion,
|
|
@@ -12232,7 +12555,10 @@ function ensureSchema(db, dbPath) {
|
|
|
12232
12555
|
migrations: [
|
|
12233
12556
|
{ version: 3, migrate: createCacheTables },
|
|
12234
12557
|
{ version: 4, migrate: createSearchTables },
|
|
12235
|
-
{
|
|
12558
|
+
{
|
|
12559
|
+
version: 5,
|
|
12560
|
+
migrate: (migrationDb) => migrateProjectIdentity(migrationDb, resolveLegacyProjectIdentity)
|
|
12561
|
+
},
|
|
12236
12562
|
{
|
|
12237
12563
|
version: 6,
|
|
12238
12564
|
destructive: true,
|
|
@@ -12246,7 +12572,7 @@ function ensureSchema(db, dbPath) {
|
|
|
12246
12572
|
version: 7,
|
|
12247
12573
|
migrate(db2) {
|
|
12248
12574
|
addSessionParentReference(db2);
|
|
12249
|
-
backfillStructuredSessions(db2);
|
|
12575
|
+
backfillStructuredSessions(db2, resolveLegacyProjectIdentity);
|
|
12250
12576
|
}
|
|
12251
12577
|
},
|
|
12252
12578
|
{ version: 8, migrate: backfillFileActivity },
|
|
@@ -12266,7 +12592,7 @@ function ensureSchema(db, dbPath) {
|
|
|
12266
12592
|
{
|
|
12267
12593
|
version: 12,
|
|
12268
12594
|
migrate(db2) {
|
|
12269
|
-
refreshProjectIdentities(db2);
|
|
12595
|
+
refreshProjectIdentities(db2, resolveLegacyProjectIdentity);
|
|
12270
12596
|
}
|
|
12271
12597
|
},
|
|
12272
12598
|
{ version: 13, migrate: createCacheTables },
|
|
@@ -12279,7 +12605,12 @@ function ensureSchema(db, dbPath) {
|
|
|
12279
12605
|
{ version: 21, migrate: addSessionPublicationId },
|
|
12280
12606
|
{ version: 22, migrate: addAtomicPublicationStaging },
|
|
12281
12607
|
{ version: 23, migrate: replaceSessionActivityIndex },
|
|
12282
|
-
{ version: 24, migrate: dropLegacyMessageSearchIndex }
|
|
12608
|
+
{ version: 24, migrate: dropLegacyMessageSearchIndex },
|
|
12609
|
+
{ version: 25, migrate: addMessageContentChainDigest },
|
|
12610
|
+
{ version: 26, migrate: addSessionActivityIndex },
|
|
12611
|
+
{ version: 27, migrate: addSessionModelCostRollup },
|
|
12612
|
+
{ version: 28, migrate: addSessionCostSummary },
|
|
12613
|
+
{ version: 29, migrate: addSessionUsageSummary }
|
|
12283
12614
|
]
|
|
12284
12615
|
});
|
|
12285
12616
|
createLatestCacheSchema(db);
|
|
@@ -12293,6 +12624,25 @@ function ensureSchema(db, dbPath) {
|
|
|
12293
12624
|
migrateOpenCodeSubagentFold(db);
|
|
12294
12625
|
migrateSubagentTree(db);
|
|
12295
12626
|
}
|
|
12627
|
+
var ANALYTICS_REVISION_KEY = "analytics_revision";
|
|
12628
|
+
function readAnalyticsRevision(db) {
|
|
12629
|
+
const row = db.prepare("SELECT value FROM cache_meta WHERE key = ?").get(ANALYTICS_REVISION_KEY);
|
|
12630
|
+
return String(row?.value ?? "0");
|
|
12631
|
+
}
|
|
12632
|
+
function advanceAnalyticsRevision(db) {
|
|
12633
|
+
db.prepare(
|
|
12634
|
+
`
|
|
12635
|
+
INSERT INTO cache_meta(key, value)
|
|
12636
|
+
VALUES (?, '1')
|
|
12637
|
+
ON CONFLICT(key) DO UPDATE SET value = CAST(value AS INTEGER) + 1
|
|
12638
|
+
`
|
|
12639
|
+
).run(ANALYTICS_REVISION_KEY);
|
|
12640
|
+
}
|
|
12641
|
+
function getAnalyticsRevision() {
|
|
12642
|
+
if (!hasCacheStorage()) return null;
|
|
12643
|
+
const read = withCacheDbReadOnly((db) => readAnalyticsRevision(db));
|
|
12644
|
+
return read.status === "success" ? read.value : null;
|
|
12645
|
+
}
|
|
12296
12646
|
function escapeFtsTerm(value) {
|
|
12297
12647
|
return value.replaceAll('"', '""');
|
|
12298
12648
|
}
|
|
@@ -12417,6 +12767,54 @@ function toFtsQuery(input) {
|
|
|
12417
12767
|
(token, index, values) => token !== "OR" || index > 0 && index < values.length - 1 && values[index - 1] !== "OR" && values[index + 1] !== "OR"
|
|
12418
12768
|
).join(" ");
|
|
12419
12769
|
}
|
|
12770
|
+
var MESSAGE_CURSOR_VERSION = 2;
|
|
12771
|
+
function updateField(hash, value) {
|
|
12772
|
+
if (value == null) {
|
|
12773
|
+
hash.update("n;");
|
|
12774
|
+
return;
|
|
12775
|
+
}
|
|
12776
|
+
const text = String(value);
|
|
12777
|
+
hash.update(`v${text.length}:`).update(text).update(";");
|
|
12778
|
+
}
|
|
12779
|
+
function updateMessageContent(hash, content) {
|
|
12780
|
+
updateField(hash, content.messageId);
|
|
12781
|
+
updateField(hash, content.role);
|
|
12782
|
+
updateField(hash, content.timeCreated);
|
|
12783
|
+
updateField(hash, content.timeCompleted);
|
|
12784
|
+
updateField(hash, content.agent);
|
|
12785
|
+
updateField(hash, content.mode);
|
|
12786
|
+
updateField(hash, content.model);
|
|
12787
|
+
updateField(hash, content.provider);
|
|
12788
|
+
updateField(hash, content.tokensJson);
|
|
12789
|
+
updateField(hash, content.cost);
|
|
12790
|
+
updateField(hash, content.costSource);
|
|
12791
|
+
updateField(hash, content.partsJson);
|
|
12792
|
+
updateField(hash, content.partsFormatVersion);
|
|
12793
|
+
updateField(hash, content.subagentId);
|
|
12794
|
+
updateField(hash, content.nickname);
|
|
12795
|
+
}
|
|
12796
|
+
function initialMessageCursorDigest(reference) {
|
|
12797
|
+
const hash = createHash22("sha256");
|
|
12798
|
+
hash.update("codesesh-session-messages\0");
|
|
12799
|
+
updateField(hash, MESSAGE_CURSOR_VERSION);
|
|
12800
|
+
updateField(hash, reference.agentName);
|
|
12801
|
+
updateField(hash, reference.sessionId);
|
|
12802
|
+
return hash.digest("hex");
|
|
12803
|
+
}
|
|
12804
|
+
function advanceMessageCursorDigest(previousDigest, content) {
|
|
12805
|
+
const hash = createHash22("sha256");
|
|
12806
|
+
hash.update("codesesh-session-messages-chain\0");
|
|
12807
|
+
updateField(hash, previousDigest);
|
|
12808
|
+
updateMessageContent(hash, content);
|
|
12809
|
+
return hash.digest("hex");
|
|
12810
|
+
}
|
|
12811
|
+
function computeMessageCursorDigest(reference, messages) {
|
|
12812
|
+
let digest = initialMessageCursorDigest(reference);
|
|
12813
|
+
for (const message of messages) {
|
|
12814
|
+
digest = advanceMessageCursorDigest(digest, message);
|
|
12815
|
+
}
|
|
12816
|
+
return digest;
|
|
12817
|
+
}
|
|
12420
12818
|
var DETAIL_PROJECTION_VERSION = "session-detail-v1";
|
|
12421
12819
|
function sessionDetailVersion(meta) {
|
|
12422
12820
|
const parserVersions = Object.entries(meta ?? {}).filter(
|
|
@@ -12621,7 +13019,7 @@ function loadSearchIndexEntry(agentName, change, loadSessionData, detailVersion,
|
|
|
12621
13019
|
try {
|
|
12622
13020
|
const data = loadSessionData(change.session.id);
|
|
12623
13021
|
const messages = normalizeMessages(data);
|
|
12624
|
-
const identity =
|
|
13022
|
+
const identity = requireSessionProjectIdentity(agentName, change.session);
|
|
12625
13023
|
return {
|
|
12626
13024
|
session: change.session,
|
|
12627
13025
|
messages,
|
|
@@ -12673,6 +13071,77 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries, failure
|
|
|
12673
13071
|
const deleteFileActivity = db.prepare(
|
|
12674
13072
|
"DELETE FROM session_file_activity WHERE agent_name = ? AND session_id = ?"
|
|
12675
13073
|
);
|
|
13074
|
+
const deleteModelCost = db.prepare(
|
|
13075
|
+
"DELETE FROM session_model_cost WHERE agent_name = ? AND session_id = ?"
|
|
13076
|
+
);
|
|
13077
|
+
const deleteCostSummary = db.prepare(
|
|
13078
|
+
"DELETE FROM session_cost_summary WHERE agent_name = ? AND session_id = ?"
|
|
13079
|
+
);
|
|
13080
|
+
const rebuildModelCost = db.prepare(`
|
|
13081
|
+
INSERT INTO session_model_cost(agent_name, session_id, model, cost, cost_recorded)
|
|
13082
|
+
SELECT
|
|
13083
|
+
agent_name,
|
|
13084
|
+
session_id,
|
|
13085
|
+
model,
|
|
13086
|
+
SUM(COALESCE(cost, 0)),
|
|
13087
|
+
SUM(CASE WHEN cost_source = 'recorded' THEN COALESCE(cost, 0) ELSE 0 END)
|
|
13088
|
+
FROM messages
|
|
13089
|
+
WHERE agent_name = ? AND session_id = ? AND model IS NOT NULL AND model <> ''
|
|
13090
|
+
GROUP BY agent_name, session_id, model
|
|
13091
|
+
`);
|
|
13092
|
+
const rebuildCostSummary = db.prepare(`
|
|
13093
|
+
WITH normalized AS (
|
|
13094
|
+
SELECT
|
|
13095
|
+
agent_name,
|
|
13096
|
+
session_id,
|
|
13097
|
+
COALESCE(time_completed, 0) <= 0 AND COALESCE(time_created, 0) <= 0 AS untimed,
|
|
13098
|
+
MAX(CAST(COALESCE(json_extract(tokens_json, '$.input'), 0) AS INTEGER), 0) AS input_tokens,
|
|
13099
|
+
MAX(CAST(COALESCE(json_extract(tokens_json, '$.output'), 0) AS INTEGER), 0) AS output_tokens,
|
|
13100
|
+
MAX(CAST(COALESCE(json_extract(tokens_json, '$.reasoning'), 0) AS INTEGER), 0) AS reasoning_tokens,
|
|
13101
|
+
MAX(CAST(COALESCE(json_extract(tokens_json, '$.cache_read'), 0) AS INTEGER), 0) AS cache_read_tokens,
|
|
13102
|
+
MAX(CAST(COALESCE(json_extract(tokens_json, '$.cache_create'), 0) AS INTEGER), 0) AS cache_create_tokens,
|
|
13103
|
+
CASE WHEN cost > 0 THEN cost ELSE 0 END AS normalized_cost
|
|
13104
|
+
FROM messages
|
|
13105
|
+
WHERE agent_name = ? AND session_id = ?
|
|
13106
|
+
)
|
|
13107
|
+
INSERT INTO session_cost_summary(
|
|
13108
|
+
agent_name,
|
|
13109
|
+
session_id,
|
|
13110
|
+
message_count,
|
|
13111
|
+
untimed_message_count,
|
|
13112
|
+
input_tokens,
|
|
13113
|
+
output_tokens,
|
|
13114
|
+
reasoning_tokens,
|
|
13115
|
+
cache_read_tokens,
|
|
13116
|
+
cache_create_tokens,
|
|
13117
|
+
untimed_input_tokens,
|
|
13118
|
+
untimed_output_tokens,
|
|
13119
|
+
untimed_reasoning_tokens,
|
|
13120
|
+
untimed_cache_read_tokens,
|
|
13121
|
+
untimed_cache_create_tokens,
|
|
13122
|
+
message_cost,
|
|
13123
|
+
untimed_message_cost
|
|
13124
|
+
)
|
|
13125
|
+
SELECT
|
|
13126
|
+
agent_name,
|
|
13127
|
+
session_id,
|
|
13128
|
+
COUNT(*),
|
|
13129
|
+
SUM(CASE WHEN untimed THEN 1 ELSE 0 END),
|
|
13130
|
+
SUM(input_tokens),
|
|
13131
|
+
SUM(output_tokens),
|
|
13132
|
+
SUM(reasoning_tokens),
|
|
13133
|
+
SUM(cache_read_tokens),
|
|
13134
|
+
SUM(cache_create_tokens),
|
|
13135
|
+
SUM(CASE WHEN untimed THEN input_tokens ELSE 0 END),
|
|
13136
|
+
SUM(CASE WHEN untimed THEN output_tokens ELSE 0 END),
|
|
13137
|
+
SUM(CASE WHEN untimed THEN reasoning_tokens ELSE 0 END),
|
|
13138
|
+
SUM(CASE WHEN untimed THEN cache_read_tokens ELSE 0 END),
|
|
13139
|
+
SUM(CASE WHEN untimed THEN cache_create_tokens ELSE 0 END),
|
|
13140
|
+
SUM(normalized_cost),
|
|
13141
|
+
SUM(CASE WHEN untimed THEN normalized_cost ELSE 0 END)
|
|
13142
|
+
FROM normalized
|
|
13143
|
+
GROUP BY agent_name, session_id
|
|
13144
|
+
`);
|
|
12676
13145
|
const writeIndexedSession = prepareUpsertIndexedSession(db);
|
|
12677
13146
|
const insertFileActivity = prepareInsertFileActivity(db);
|
|
12678
13147
|
const insertMessageTool = prepareInsertMessageTool(db);
|
|
@@ -12694,11 +13163,12 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries, failure
|
|
|
12694
13163
|
cost_source,
|
|
12695
13164
|
parts_json,
|
|
12696
13165
|
parts_format_version,
|
|
13166
|
+
content_chain_digest,
|
|
12697
13167
|
subagent_id,
|
|
12698
13168
|
nickname,
|
|
12699
13169
|
content_text,
|
|
12700
13170
|
tool_metadata_json
|
|
12701
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
13171
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
12702
13172
|
ON CONFLICT(agent_name, session_id, message_index) DO UPDATE SET
|
|
12703
13173
|
message_id = excluded.message_id,
|
|
12704
13174
|
role = excluded.role,
|
|
@@ -12713,6 +13183,7 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries, failure
|
|
|
12713
13183
|
cost_source = excluded.cost_source,
|
|
12714
13184
|
parts_json = excluded.parts_json,
|
|
12715
13185
|
parts_format_version = excluded.parts_format_version,
|
|
13186
|
+
content_chain_digest = excluded.content_chain_digest,
|
|
12716
13187
|
subagent_id = excluded.subagent_id,
|
|
12717
13188
|
nickname = excluded.nickname,
|
|
12718
13189
|
content_text = excluded.content_text,
|
|
@@ -12748,6 +13219,8 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries, failure
|
|
|
12748
13219
|
deleteFileActivity.run(agentName, sessionId);
|
|
12749
13220
|
deleteMessageTools.run(agentName, sessionId, 0);
|
|
12750
13221
|
deleteMessages.run(agentName, sessionId, 0);
|
|
13222
|
+
deleteModelCost.run(agentName, sessionId);
|
|
13223
|
+
deleteCostSummary.run(agentName, sessionId);
|
|
12751
13224
|
clearPendingReindex.run(agentName, sessionId);
|
|
12752
13225
|
}
|
|
12753
13226
|
let indexed = 0;
|
|
@@ -12764,7 +13237,15 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries, failure
|
|
|
12764
13237
|
deleteMessageTools.run(agentName, entry.session.id, 0);
|
|
12765
13238
|
clearPendingReindex.run(agentName, entry.session.id);
|
|
12766
13239
|
writeFileActivityRows(insertFileActivity, entry.fileActivity);
|
|
13240
|
+
let contentChainDigest = initialMessageCursorDigest({
|
|
13241
|
+
agentName,
|
|
13242
|
+
sessionId: entry.session.id
|
|
13243
|
+
});
|
|
12767
13244
|
for (const message of entry.messages) {
|
|
13245
|
+
contentChainDigest = advanceMessageCursorDigest(
|
|
13246
|
+
contentChainDigest,
|
|
13247
|
+
messageCursorContentFromStructuredRecord(message)
|
|
13248
|
+
);
|
|
12768
13249
|
upsertMessage.run(
|
|
12769
13250
|
agentName,
|
|
12770
13251
|
entry.session.id,
|
|
@@ -12782,6 +13263,7 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries, failure
|
|
|
12782
13263
|
message.costSource ?? null,
|
|
12783
13264
|
message.partsJson,
|
|
12784
13265
|
MESSAGE_PARTS_FORMAT_VERSION,
|
|
13266
|
+
contentChainDigest,
|
|
12785
13267
|
message.subagentId ?? null,
|
|
12786
13268
|
message.nickname ?? null,
|
|
12787
13269
|
message.contentText,
|
|
@@ -12792,6 +13274,10 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries, failure
|
|
|
12792
13274
|
}
|
|
12793
13275
|
}
|
|
12794
13276
|
deleteMessages.run(agentName, entry.session.id, entry.messages.length);
|
|
13277
|
+
deleteModelCost.run(agentName, entry.session.id);
|
|
13278
|
+
deleteCostSummary.run(agentName, entry.session.id);
|
|
13279
|
+
rebuildModelCost.run(agentName, entry.session.id);
|
|
13280
|
+
rebuildCostSummary.run(agentName, entry.session.id);
|
|
12795
13281
|
upsertRow.run(
|
|
12796
13282
|
agentName,
|
|
12797
13283
|
entry.session.id,
|
|
@@ -13042,11 +13528,20 @@ function executeSearchIndexPlan(db, plan, loadSessionData, largeBacklogStrategy)
|
|
|
13042
13528
|
if (largeBacklogStrategy === "chunked" && plan.changes.length > SEARCH_INDEX_COMMIT_CHUNK_SIZE) {
|
|
13043
13529
|
runSearchIndexWrite(db, false, () => {
|
|
13044
13530
|
indexed += writeSearchIndexRows(db, plan.agentName, plan.removedSessionIds, [], failures);
|
|
13531
|
+
if (plan.removedSessionIds.length > 0) advanceAnalyticsRevision(db);
|
|
13045
13532
|
});
|
|
13046
13533
|
for (let offset = 0; offset < plan.changes.length; offset += SEARCH_INDEX_COMMIT_CHUNK_SIZE) {
|
|
13047
13534
|
const chunk = plan.changes.slice(offset, offset + SEARCH_INDEX_COMMIT_CHUNK_SIZE);
|
|
13048
13535
|
runSearchIndexWrite(db, false, () => {
|
|
13049
|
-
|
|
13536
|
+
const chunkIndexed = writeSearchIndexRows(
|
|
13537
|
+
db,
|
|
13538
|
+
plan.agentName,
|
|
13539
|
+
[],
|
|
13540
|
+
loadEntries(chunk),
|
|
13541
|
+
failures
|
|
13542
|
+
);
|
|
13543
|
+
indexed += chunkIndexed;
|
|
13544
|
+
if (chunkIndexed > 0) advanceAnalyticsRevision(db);
|
|
13050
13545
|
});
|
|
13051
13546
|
}
|
|
13052
13547
|
return searchIndexSyncResult(plan, indexed, failures, void 0, "incremental");
|
|
@@ -13059,10 +13554,12 @@ function executeSearchIndexPlan(db, plan, loadSessionData, largeBacklogStrategy)
|
|
|
13059
13554
|
loadEntries(plan.changes),
|
|
13060
13555
|
failures
|
|
13061
13556
|
);
|
|
13557
|
+
if (indexed > 0 || plan.removedSessionIds.length > 0) advanceAnalyticsRevision(db);
|
|
13062
13558
|
});
|
|
13063
13559
|
return searchIndexSyncResult(plan, indexed, failures, rebuildDurationMs);
|
|
13064
13560
|
}
|
|
13065
13561
|
function syncSessionSearchIndex(agentName, sessions, loadSessionData, options = {}) {
|
|
13562
|
+
assertSessionProjectIdentities(agentName, sessions);
|
|
13066
13563
|
return withSearchIndexDb(
|
|
13067
13564
|
(db) => executeSearchIndexPlan(
|
|
13068
13565
|
db,
|
|
@@ -13085,6 +13582,10 @@ function syncSessionSearchIndexChanges(agentName, changes, removedSessionIds, lo
|
|
|
13085
13582
|
durationMs: 0
|
|
13086
13583
|
};
|
|
13087
13584
|
}
|
|
13585
|
+
assertSessionProjectIdentities(
|
|
13586
|
+
agentName,
|
|
13587
|
+
changes.map(({ session }) => session)
|
|
13588
|
+
);
|
|
13088
13589
|
return withSearchIndexDb(
|
|
13089
13590
|
(db) => executeSearchIndexPlan(
|
|
13090
13591
|
db,
|
|
@@ -13111,7 +13612,6 @@ function mergeSearchQueryOptions(query, options) {
|
|
|
13111
13612
|
project: options.project ?? parsed.filters.project,
|
|
13112
13613
|
projectKind: options.projectKind ?? parsed.filters.projectKind,
|
|
13113
13614
|
projectKey: options.projectKey ?? parsed.filters.projectKey,
|
|
13114
|
-
cwd: options.cwd ?? parsed.filters.cwd,
|
|
13115
13615
|
tags: mergeSearchLists(options.tags, parsed.filters.tags),
|
|
13116
13616
|
tools: mergeSearchLists(options.tools, parsed.filters.tools),
|
|
13117
13617
|
file: options.file ?? parsed.filters.file,
|
|
@@ -13124,6 +13624,9 @@ function mergeSearchQueryOptions(query, options) {
|
|
|
13124
13624
|
parsed
|
|
13125
13625
|
};
|
|
13126
13626
|
}
|
|
13627
|
+
function getSearchProjectDirectory(query, options) {
|
|
13628
|
+
return options.cwd ?? parseSearchQuery(query).filters.cwd;
|
|
13629
|
+
}
|
|
13127
13630
|
function sessionMatchesSearchCost(session, options, cost = session.stats.total_cost) {
|
|
13128
13631
|
if (options.costMin != null) {
|
|
13129
13632
|
if (options.costMinExclusive ? cost <= options.costMin : cost < options.costMin) {
|
|
@@ -13152,12 +13655,19 @@ function buildSessionSearchFilters(options) {
|
|
|
13152
13655
|
clauses.push("0");
|
|
13153
13656
|
}
|
|
13154
13657
|
}
|
|
13155
|
-
if (options.
|
|
13156
|
-
const
|
|
13658
|
+
if (options.projectScope) {
|
|
13659
|
+
const scopePath = normalizeProjectScopePath(options.projectScope.path).toLowerCase();
|
|
13660
|
+
const normalizedDirectory = "REPLACE(LOWER(s.directory), char(92), '/')";
|
|
13157
13661
|
clauses.push(
|
|
13158
|
-
|
|
13662
|
+
`((s.project_identity_kind = ? AND s.project_identity_key = ?) OR ${normalizedDirectory} = ? OR instr(${normalizedDirectory}, ? || '/') = 1 OR instr(?, ${normalizedDirectory} || '/') = 1)`
|
|
13663
|
+
);
|
|
13664
|
+
params.push(
|
|
13665
|
+
options.projectScope.identity.kind,
|
|
13666
|
+
options.projectScope.identity.key,
|
|
13667
|
+
scopePath,
|
|
13668
|
+
scopePath,
|
|
13669
|
+
scopePath
|
|
13159
13670
|
);
|
|
13160
|
-
params.push(identity.kind, identity.key, likePattern(options.cwd));
|
|
13161
13671
|
}
|
|
13162
13672
|
if (options.project) {
|
|
13163
13673
|
clauses.push(
|
|
@@ -13348,6 +13858,7 @@ function searchResultRowKey(row) {
|
|
|
13348
13858
|
return `${String(row.agent_name)}\0${String(row.session_id)}`;
|
|
13349
13859
|
}
|
|
13350
13860
|
function fetchMessageSearchMatches(db, rows, terms) {
|
|
13861
|
+
const startedAt = performance.now();
|
|
13351
13862
|
const candidates = rows.filter((row) => !textMatchesTerms(String(row.title ?? ""), terms));
|
|
13352
13863
|
if (candidates.length === 0) {
|
|
13353
13864
|
return /* @__PURE__ */ new Map();
|
|
@@ -13357,11 +13868,11 @@ function fetchMessageSearchMatches(db, rows, terms) {
|
|
|
13357
13868
|
String(row.agent_name),
|
|
13358
13869
|
String(row.session_id)
|
|
13359
13870
|
]);
|
|
13360
|
-
|
|
13361
|
-
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
);
|
|
13871
|
+
let predicateEvaluations = 0;
|
|
13872
|
+
db.function("codesesh_message_matches_terms", { deterministic: true }, (text) => {
|
|
13873
|
+
predicateEvaluations += 1;
|
|
13874
|
+
return textMatchesTerms(String(text ?? ""), terms) ? 1 : 0;
|
|
13875
|
+
});
|
|
13365
13876
|
const messageRows = db.prepare(
|
|
13366
13877
|
`
|
|
13367
13878
|
WITH candidate_sessions(agent_name, session_id) AS (
|
|
@@ -13405,6 +13916,12 @@ function fetchMessageSearchMatches(db, rows, terms) {
|
|
|
13405
13916
|
matchType: messageMatchType(message)
|
|
13406
13917
|
});
|
|
13407
13918
|
}
|
|
13919
|
+
getCoreDiagnostics()?.info?.("search.message_match_projection", {
|
|
13920
|
+
candidate_sessions: candidates.length,
|
|
13921
|
+
predicate_evaluations: predicateEvaluations,
|
|
13922
|
+
matched_sessions: matches.size,
|
|
13923
|
+
duration_ms: Math.round(performance.now() - startedAt)
|
|
13924
|
+
});
|
|
13408
13925
|
return matches;
|
|
13409
13926
|
}
|
|
13410
13927
|
function resolveSearchMatch(row, terms, messageMatches) {
|
|
@@ -13496,14 +14013,14 @@ function searchSessions(query, options = {}) {
|
|
|
13496
14013
|
}
|
|
13497
14014
|
function fileActivityFilters(options) {
|
|
13498
14015
|
const path2 = options.path ? normalizeFilePathSearch(options.path) : "";
|
|
13499
|
-
const
|
|
14016
|
+
const scope = options.projectScope;
|
|
13500
14017
|
return {
|
|
13501
14018
|
projectKind: options.projectKind ?? null,
|
|
13502
14019
|
projectKey: options.projectKey ?? null,
|
|
13503
14020
|
projectLike: options.project ? likePattern(options.project) : null,
|
|
13504
|
-
|
|
13505
|
-
|
|
13506
|
-
|
|
14021
|
+
scopeKind: scope?.identity.kind ?? null,
|
|
14022
|
+
scopeKey: scope?.identity.key ?? null,
|
|
14023
|
+
scopePath: scope ? normalizeProjectScopePath(scope.path).toLowerCase() : null,
|
|
13507
14024
|
path: path2,
|
|
13508
14025
|
pathLike: path2 ? likePattern(path2) : null
|
|
13509
14026
|
};
|
|
@@ -13547,11 +14064,18 @@ function buildFileActivityWhere(options) {
|
|
|
13547
14064
|
);
|
|
13548
14065
|
params.push(filters.projectLike, filters.projectLike, filters.projectLike);
|
|
13549
14066
|
}
|
|
13550
|
-
if (filters.
|
|
14067
|
+
if (filters.scopeKey != null && filters.scopePath != null) {
|
|
14068
|
+
const normalizedDirectory = "REPLACE(LOWER(s.directory), char(92), '/')";
|
|
13551
14069
|
clauses.push(
|
|
13552
|
-
|
|
14070
|
+
`((s.project_identity_kind = ? AND s.project_identity_key = ?) OR ${normalizedDirectory} = ? OR instr(${normalizedDirectory}, ? || '/') = 1 OR instr(?, ${normalizedDirectory} || '/') = 1)`
|
|
14071
|
+
);
|
|
14072
|
+
params.push(
|
|
14073
|
+
filters.scopeKind,
|
|
14074
|
+
filters.scopeKey,
|
|
14075
|
+
filters.scopePath,
|
|
14076
|
+
filters.scopePath,
|
|
14077
|
+
filters.scopePath
|
|
13553
14078
|
);
|
|
13554
|
-
params.push(filters.cwdKind, filters.cwdKey, filters.cwdLike);
|
|
13555
14079
|
}
|
|
13556
14080
|
if (filters.pathLike != null) {
|
|
13557
14081
|
const pathQuery = filePathFtsQuery(filters.path);
|
|
@@ -13771,11 +14295,11 @@ function deleteLegacyCacheFile() {
|
|
|
13771
14295
|
} catch {
|
|
13772
14296
|
}
|
|
13773
14297
|
}
|
|
13774
|
-
function
|
|
14298
|
+
function readCachedSessions(agentName) {
|
|
13775
14299
|
if (!hasCacheStorage()) {
|
|
13776
|
-
return null;
|
|
14300
|
+
return { status: "success", value: null };
|
|
13777
14301
|
}
|
|
13778
|
-
return
|
|
14302
|
+
return withCacheDbOutcome((db) => {
|
|
13779
14303
|
const timestampRow = db.prepare("SELECT timestamp AS value FROM agent_cache WHERE agent_name = ?").get(agentName);
|
|
13780
14304
|
const timestamp = Number(timestampRow?.value ?? 0);
|
|
13781
14305
|
if (!timestamp) {
|
|
@@ -13801,6 +14325,10 @@ function loadCachedSessions(agentName) {
|
|
|
13801
14325
|
return { sessions, meta, timestamp };
|
|
13802
14326
|
});
|
|
13803
14327
|
}
|
|
14328
|
+
function loadCachedSessions(agentName) {
|
|
14329
|
+
const outcome = readCachedSessions(agentName);
|
|
14330
|
+
return outcome.status === "success" ? outcome.value : null;
|
|
14331
|
+
}
|
|
13804
14332
|
function loadCachedSessionHeads(references) {
|
|
13805
14333
|
if (references.length === 0 || !hasCacheStorage()) return [];
|
|
13806
14334
|
const unique2 = /* @__PURE__ */ new Map();
|
|
@@ -13859,10 +14387,6 @@ function readAgentCacheInitialization(agentName, indexVersion = CACHE_INITIALIZA
|
|
|
13859
14387
|
return row?.index_version === indexVersion;
|
|
13860
14388
|
});
|
|
13861
14389
|
}
|
|
13862
|
-
function isAgentCacheInitialized(agentName, indexVersion = CACHE_INITIALIZATION_VERSION) {
|
|
13863
|
-
const outcome = readAgentCacheInitialization(agentName, indexVersion);
|
|
13864
|
-
return outcome.status === "success" && outcome.value;
|
|
13865
|
-
}
|
|
13866
14390
|
function markAgentCacheInitialized(agentName, indexVersion = CACHE_INITIALIZATION_VERSION) {
|
|
13867
14391
|
withCacheDb((db) => {
|
|
13868
14392
|
db.prepare(
|
|
@@ -13876,7 +14400,7 @@ function markAgentCacheInitialized(agentName, indexVersion = CACHE_INITIALIZATIO
|
|
|
13876
14400
|
});
|
|
13877
14401
|
}
|
|
13878
14402
|
function markAgentFullSyncStarted(agentName) {
|
|
13879
|
-
withCacheDb((db) => {
|
|
14403
|
+
const persisted = withCacheDb((db) => {
|
|
13880
14404
|
db.prepare(
|
|
13881
14405
|
`
|
|
13882
14406
|
UPDATE cache_initialization
|
|
@@ -13884,7 +14408,9 @@ function markAgentFullSyncStarted(agentName) {
|
|
|
13884
14408
|
WHERE agent_name = ?
|
|
13885
14409
|
`
|
|
13886
14410
|
).run(agentName);
|
|
14411
|
+
return true;
|
|
13887
14412
|
});
|
|
14413
|
+
return persisted ?? false;
|
|
13888
14414
|
}
|
|
13889
14415
|
function getAgentFullSyncCursor(agentName) {
|
|
13890
14416
|
if (!hasCacheStorage()) return null;
|
|
@@ -13895,8 +14421,8 @@ function getAgentFullSyncCursor(agentName) {
|
|
|
13895
14421
|
return outcome.status === "success" ? outcome.value : null;
|
|
13896
14422
|
}
|
|
13897
14423
|
function markAgentFullSyncProgress(agentName, cursor) {
|
|
13898
|
-
if (!cursor) return;
|
|
13899
|
-
withCacheDb((db) => {
|
|
14424
|
+
if (!cursor) return true;
|
|
14425
|
+
const persisted = withCacheDb((db) => {
|
|
13900
14426
|
db.prepare(
|
|
13901
14427
|
`
|
|
13902
14428
|
INSERT INTO cache_meta(key, value)
|
|
@@ -13904,14 +14430,9 @@ function markAgentFullSyncProgress(agentName, cursor) {
|
|
|
13904
14430
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
13905
14431
|
`
|
|
13906
14432
|
).run(`${FULL_SYNC_CURSOR_PREFIX}${agentName}`, cursor);
|
|
14433
|
+
return true;
|
|
13907
14434
|
});
|
|
13908
|
-
|
|
13909
|
-
function clearAgentFullSyncCursor(agentName) {
|
|
13910
|
-
withCacheDb((db) => {
|
|
13911
|
-
db.prepare("DELETE FROM cache_meta WHERE key = ?").run(
|
|
13912
|
-
`${FULL_SYNC_CURSOR_PREFIX}${agentName}`
|
|
13913
|
-
);
|
|
13914
|
-
});
|
|
14435
|
+
return persisted ?? false;
|
|
13915
14436
|
}
|
|
13916
14437
|
function readAgentLastFullSyncAt(agentName) {
|
|
13917
14438
|
if (!hasCacheStorage()) {
|
|
@@ -13929,97 +14450,149 @@ function readAgentLastFullSyncAt(agentName) {
|
|
|
13929
14450
|
return row?.last_sync_at || null;
|
|
13930
14451
|
});
|
|
13931
14452
|
}
|
|
13932
|
-
function getAgentLastFullSyncAt(agentName) {
|
|
13933
|
-
const outcome = readAgentLastFullSyncAt(agentName);
|
|
13934
|
-
return outcome.status === "success" ? outcome.value : null;
|
|
13935
|
-
}
|
|
13936
14453
|
function markAgentFullSyncCompleted(agentName) {
|
|
13937
|
-
|
|
13938
|
-
|
|
14454
|
+
const completedAt = Date.now();
|
|
14455
|
+
const persisted = withCacheDb((db) => {
|
|
14456
|
+
db.transaction(() => {
|
|
14457
|
+
db.prepare(
|
|
14458
|
+
`
|
|
14459
|
+
INSERT INTO cache_initialization(agent_name, initialized_at, index_version, last_sync_at)
|
|
14460
|
+
VALUES (?, ?, ?, ?)
|
|
14461
|
+
ON CONFLICT(agent_name) DO UPDATE SET
|
|
14462
|
+
last_sync_at = excluded.last_sync_at
|
|
14463
|
+
`
|
|
14464
|
+
).run(agentName, completedAt, CACHE_INITIALIZATION_VERSION, completedAt);
|
|
14465
|
+
db.prepare("DELETE FROM cache_meta WHERE key = ?").run(
|
|
14466
|
+
`${FULL_SYNC_CURSOR_PREFIX}${agentName}`
|
|
14467
|
+
);
|
|
14468
|
+
}).immediate();
|
|
14469
|
+
return true;
|
|
14470
|
+
});
|
|
14471
|
+
return persisted ?? false;
|
|
14472
|
+
}
|
|
14473
|
+
function loadCachedSessionEntryBase(db, agentName, sessionId) {
|
|
14474
|
+
const row = db.prepare(
|
|
14475
|
+
`
|
|
14476
|
+
SELECT
|
|
14477
|
+
sessions.*,
|
|
14478
|
+
documents.detail_version AS detail_version
|
|
14479
|
+
FROM sessions
|
|
14480
|
+
LEFT JOIN session_documents AS documents
|
|
14481
|
+
ON documents.agent_name = sessions.agent_name
|
|
14482
|
+
AND documents.session_id = sessions.session_id
|
|
14483
|
+
WHERE sessions.agent_name = ?
|
|
14484
|
+
AND sessions.session_id = ?
|
|
14485
|
+
AND sessions.publication_id IS NULL
|
|
13939
14486
|
`
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
14487
|
+
).get(agentName, sessionId);
|
|
14488
|
+
if (!row) return null;
|
|
14489
|
+
const pendingReindex = db.prepare("SELECT 1 FROM pending_reindex WHERE agent_name = ? AND session_id = ?").get(agentName, sessionId) != null;
|
|
14490
|
+
const fileActivityRows = db.prepare(
|
|
14491
|
+
`
|
|
14492
|
+
SELECT agent_name, session_id, project_identity_key, path, kind, count, latest_time
|
|
14493
|
+
FROM session_file_activity
|
|
14494
|
+
WHERE agent_name = ? AND session_id = ?
|
|
14495
|
+
ORDER BY latest_time DESC, count DESC, path
|
|
14496
|
+
LIMIT 500
|
|
13943
14497
|
`
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
|
|
14498
|
+
).all(agentName, sessionId);
|
|
14499
|
+
return {
|
|
14500
|
+
data: {
|
|
14501
|
+
...sessionFromRow(row),
|
|
14502
|
+
reference: { agentName, sessionId },
|
|
14503
|
+
file_activity: fileActivityRows.map((activityRow) => fileActivityFromRow(activityRow))
|
|
14504
|
+
},
|
|
14505
|
+
meta: parseCachedSessionMeta(row.meta_json),
|
|
14506
|
+
detailVersion: typeof row.detail_version === "string" ? row.detail_version : null,
|
|
14507
|
+
pendingReindex
|
|
14508
|
+
};
|
|
14509
|
+
}
|
|
14510
|
+
function readCachedSessionMessageRows(db, agentName, sessionId, startIndex) {
|
|
14511
|
+
return db.prepare(
|
|
14512
|
+
`
|
|
14513
|
+
SELECT
|
|
14514
|
+
message_id,
|
|
14515
|
+
role,
|
|
14516
|
+
time_created,
|
|
14517
|
+
time_completed,
|
|
14518
|
+
agent,
|
|
14519
|
+
mode,
|
|
14520
|
+
model,
|
|
14521
|
+
provider,
|
|
14522
|
+
tokens_json,
|
|
14523
|
+
cost,
|
|
14524
|
+
cost_source,
|
|
14525
|
+
parts_json,
|
|
14526
|
+
parts_format_version,
|
|
14527
|
+
content_chain_digest,
|
|
14528
|
+
subagent_id,
|
|
14529
|
+
nickname
|
|
14530
|
+
FROM messages
|
|
14531
|
+
WHERE agent_name = ? AND session_id = ? AND message_index >= ?
|
|
14532
|
+
ORDER BY message_index
|
|
14533
|
+
`
|
|
14534
|
+
).all(agentName, sessionId, startIndex);
|
|
13947
14535
|
}
|
|
13948
14536
|
function loadCachedSessionRawEntry(agentName, sessionId) {
|
|
13949
|
-
if (!hasCacheStorage())
|
|
13950
|
-
return null;
|
|
13951
|
-
}
|
|
14537
|
+
if (!hasCacheStorage()) return null;
|
|
13952
14538
|
const outcome = withCacheDbReadOnly((db) => {
|
|
13953
|
-
const
|
|
13954
|
-
|
|
13955
|
-
|
|
13956
|
-
|
|
13957
|
-
|
|
13958
|
-
|
|
13959
|
-
|
|
13960
|
-
|
|
13961
|
-
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
`
|
|
13966
|
-
).get(agentName, sessionId);
|
|
13967
|
-
if (!row) {
|
|
13968
|
-
return null;
|
|
13969
|
-
}
|
|
13970
|
-
const pendingReindex = db.prepare("SELECT 1 FROM pending_reindex WHERE agent_name = ? AND session_id = ?").get(agentName, sessionId) != null;
|
|
13971
|
-
const messageRows = db.prepare(
|
|
13972
|
-
`
|
|
13973
|
-
SELECT
|
|
13974
|
-
message_id,
|
|
13975
|
-
role,
|
|
13976
|
-
time_created,
|
|
13977
|
-
time_completed,
|
|
13978
|
-
agent,
|
|
13979
|
-
mode,
|
|
13980
|
-
model,
|
|
13981
|
-
provider,
|
|
13982
|
-
tokens_json,
|
|
13983
|
-
cost,
|
|
13984
|
-
cost_source,
|
|
13985
|
-
parts_json,
|
|
13986
|
-
parts_format_version,
|
|
13987
|
-
subagent_id,
|
|
13988
|
-
nickname
|
|
13989
|
-
FROM messages
|
|
13990
|
-
WHERE agent_name = ? AND session_id = ?
|
|
13991
|
-
ORDER BY message_index
|
|
13992
|
-
`
|
|
13993
|
-
).all(agentName, sessionId);
|
|
13994
|
-
const head = sessionFromRow(row);
|
|
13995
|
-
const fileActivityRows = db.prepare(
|
|
14539
|
+
const entry = loadCachedSessionEntryBase(db, agentName, sessionId);
|
|
14540
|
+
return entry ? { ...entry, messageRows: readCachedSessionMessageRows(db, agentName, sessionId, 0) } : null;
|
|
14541
|
+
});
|
|
14542
|
+
return outcome.status === "success" ? outcome.value : null;
|
|
14543
|
+
}
|
|
14544
|
+
function readCachedSessionMessageDigest(db, agentName, sessionId, messageCount) {
|
|
14545
|
+
if (!Number.isSafeInteger(messageCount) || messageCount <= 0) return null;
|
|
14546
|
+
const row = db.prepare(
|
|
14547
|
+
`
|
|
14548
|
+
SELECT content_chain_digest
|
|
14549
|
+
FROM messages
|
|
14550
|
+
WHERE agent_name = ? AND session_id = ? AND message_index = ?
|
|
13996
14551
|
`
|
|
13997
|
-
|
|
13998
|
-
|
|
13999
|
-
|
|
14000
|
-
|
|
14001
|
-
|
|
14552
|
+
).get(agentName, sessionId, messageCount - 1);
|
|
14553
|
+
return typeof row?.content_chain_digest === "string" ? row.content_chain_digest : null;
|
|
14554
|
+
}
|
|
14555
|
+
function readCachedSessionCursor(agentName, sessionId, read) {
|
|
14556
|
+
if (!hasCacheStorage()) return null;
|
|
14557
|
+
const outcome = withCacheDbReadOnly(
|
|
14558
|
+
(db) => db.transaction(() => {
|
|
14559
|
+
const entryBase = loadCachedSessionEntryBase(db, agentName, sessionId);
|
|
14560
|
+
if (!entryBase) return null;
|
|
14561
|
+
const messageState = db.prepare(
|
|
14002
14562
|
`
|
|
14003
|
-
|
|
14004
|
-
|
|
14005
|
-
|
|
14006
|
-
|
|
14007
|
-
|
|
14008
|
-
|
|
14009
|
-
|
|
14010
|
-
|
|
14011
|
-
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
|
|
14563
|
+
SELECT
|
|
14564
|
+
COUNT(*) AS message_count,
|
|
14565
|
+
(
|
|
14566
|
+
SELECT content_chain_digest
|
|
14567
|
+
FROM messages
|
|
14568
|
+
WHERE agent_name = ? AND session_id = ?
|
|
14569
|
+
ORDER BY message_index DESC
|
|
14570
|
+
LIMIT 1
|
|
14571
|
+
) AS content_chain_digest
|
|
14572
|
+
FROM messages
|
|
14573
|
+
WHERE agent_name = ? AND session_id = ?
|
|
14574
|
+
`
|
|
14575
|
+
).get(agentName, sessionId, agentName, sessionId);
|
|
14576
|
+
const entry = {
|
|
14577
|
+
...entryBase,
|
|
14578
|
+
messageCount: Number(messageState.message_count ?? 0),
|
|
14579
|
+
messageDigest: typeof messageState.content_chain_digest === "string" ? messageState.content_chain_digest : null
|
|
14580
|
+
};
|
|
14581
|
+
return read(entry, {
|
|
14582
|
+
messageDigest: (messageCount) => readCachedSessionMessageDigest(db, agentName, sessionId, messageCount),
|
|
14583
|
+
messageRows: (startIndex) => Number.isSafeInteger(startIndex) && startIndex >= 0 ? readCachedSessionMessageRows(db, agentName, sessionId, startIndex) : []
|
|
14584
|
+
});
|
|
14585
|
+
})()
|
|
14586
|
+
);
|
|
14016
14587
|
return outcome.status === "success" ? outcome.value : null;
|
|
14017
14588
|
}
|
|
14018
14589
|
function saveCachedSessions(agentName, sessions, meta = {}, options = {}) {
|
|
14590
|
+
assertSessionProjectIdentities(agentName, sessions);
|
|
14019
14591
|
const persisted = withCacheDb((db) => {
|
|
14020
|
-
db.transaction(
|
|
14021
|
-
|
|
14022
|
-
|
|
14592
|
+
db.transaction(() => {
|
|
14593
|
+
writeCachedSessionSnapshot(db, agentName, sessions, meta, options);
|
|
14594
|
+
advanceAnalyticsRevision(db);
|
|
14595
|
+
}).immediate();
|
|
14023
14596
|
deleteLegacyCacheFile();
|
|
14024
14597
|
return true;
|
|
14025
14598
|
});
|
|
@@ -14032,6 +14605,12 @@ function writeCachedSessionSnapshot(db, agentName, sessions, meta = {}, options
|
|
|
14032
14605
|
"DELETE FROM session_documents WHERE agent_name = ? AND session_id = ?"
|
|
14033
14606
|
);
|
|
14034
14607
|
const deleteMessages = db.prepare("DELETE FROM messages WHERE agent_name = ? AND session_id = ?");
|
|
14608
|
+
const deleteModelCost = db.prepare(
|
|
14609
|
+
"DELETE FROM session_model_cost WHERE agent_name = ? AND session_id = ?"
|
|
14610
|
+
);
|
|
14611
|
+
const deleteCostSummary = db.prepare(
|
|
14612
|
+
"DELETE FROM session_cost_summary WHERE agent_name = ? AND session_id = ?"
|
|
14613
|
+
);
|
|
14035
14614
|
const deleteMessageTools = db.prepare(
|
|
14036
14615
|
"DELETE FROM message_tools WHERE agent_name = ? AND session_id = ?"
|
|
14037
14616
|
);
|
|
@@ -14058,6 +14637,8 @@ function writeCachedSessionSnapshot(db, agentName, sessions, meta = {}, options
|
|
|
14058
14637
|
deleteSearchDocument.run(agentName, sessionId);
|
|
14059
14638
|
deleteMessageTools.run(agentName, sessionId);
|
|
14060
14639
|
deleteMessages.run(agentName, sessionId);
|
|
14640
|
+
deleteModelCost.run(agentName, sessionId);
|
|
14641
|
+
deleteCostSummary.run(agentName, sessionId);
|
|
14061
14642
|
deleteFileActivity.run(agentName, sessionId);
|
|
14062
14643
|
deleteSession.run(agentName, sessionId);
|
|
14063
14644
|
}
|
|
@@ -14075,10 +14656,17 @@ function writeCachedSessionSnapshot(db, agentName, sessions, meta = {}, options
|
|
|
14075
14656
|
});
|
|
14076
14657
|
}
|
|
14077
14658
|
function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta = {}) {
|
|
14659
|
+
assertSessionProjectIdentities(
|
|
14660
|
+
agentName,
|
|
14661
|
+
changes.map(({ session }) => session)
|
|
14662
|
+
);
|
|
14078
14663
|
const persisted = withCacheDb((db) => {
|
|
14079
|
-
db.transaction(
|
|
14080
|
-
|
|
14081
|
-
|
|
14664
|
+
db.transaction(() => {
|
|
14665
|
+
writeCachedSessionChanges(db, agentName, changes, removedSessionIds, meta);
|
|
14666
|
+
if (changes.length > 0 || removedSessionIds.length > 0) {
|
|
14667
|
+
advanceAnalyticsRevision(db);
|
|
14668
|
+
}
|
|
14669
|
+
}).immediate();
|
|
14082
14670
|
deleteLegacyCacheFile();
|
|
14083
14671
|
return true;
|
|
14084
14672
|
});
|
|
@@ -14090,6 +14678,12 @@ function writeCachedSessionChanges(db, agentName, changes, removedSessionIds, me
|
|
|
14090
14678
|
"DELETE FROM session_documents WHERE agent_name = ? AND session_id = ?"
|
|
14091
14679
|
);
|
|
14092
14680
|
const deleteMessages = db.prepare("DELETE FROM messages WHERE agent_name = ? AND session_id = ?");
|
|
14681
|
+
const deleteModelCost = db.prepare(
|
|
14682
|
+
"DELETE FROM session_model_cost WHERE agent_name = ? AND session_id = ?"
|
|
14683
|
+
);
|
|
14684
|
+
const deleteCostSummary = db.prepare(
|
|
14685
|
+
"DELETE FROM session_cost_summary WHERE agent_name = ? AND session_id = ?"
|
|
14686
|
+
);
|
|
14093
14687
|
const deleteMessageTools = db.prepare(
|
|
14094
14688
|
"DELETE FROM message_tools WHERE agent_name = ? AND session_id = ?"
|
|
14095
14689
|
);
|
|
@@ -14107,6 +14701,8 @@ function writeCachedSessionChanges(db, agentName, changes, removedSessionIds, me
|
|
|
14107
14701
|
deleteSearchDocument.run(agentName, sessionId);
|
|
14108
14702
|
deleteMessageTools.run(agentName, sessionId);
|
|
14109
14703
|
deleteMessages.run(agentName, sessionId);
|
|
14704
|
+
deleteModelCost.run(agentName, sessionId);
|
|
14705
|
+
deleteCostSummary.run(agentName, sessionId);
|
|
14110
14706
|
deleteFileActivity.run(agentName, sessionId);
|
|
14111
14707
|
deleteSession.run(agentName, sessionId);
|
|
14112
14708
|
}
|
|
@@ -14136,17 +14732,26 @@ function clearCache() {
|
|
|
14136
14732
|
return;
|
|
14137
14733
|
}
|
|
14138
14734
|
withCacheDb((db) => {
|
|
14139
|
-
db.
|
|
14140
|
-
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
|
|
14735
|
+
db.transaction(() => {
|
|
14736
|
+
db.exec(`
|
|
14737
|
+
DELETE FROM agent_cache;
|
|
14738
|
+
DELETE FROM cache_initialization;
|
|
14739
|
+
DELETE FROM cached_sessions;
|
|
14740
|
+
DELETE FROM pending_reindex;
|
|
14741
|
+
DELETE FROM search_index_publication_entries;
|
|
14742
|
+
DELETE FROM session_documents;
|
|
14743
|
+
DELETE FROM session_file_activity;
|
|
14744
|
+
DELETE FROM message_tools;
|
|
14745
|
+
DELETE FROM messages;
|
|
14746
|
+
DELETE FROM session_model_cost;
|
|
14747
|
+
DELETE FROM session_cost_summary;
|
|
14748
|
+
DELETE FROM sessions;
|
|
14749
|
+
DELETE FROM project_sessions;
|
|
14750
|
+
-- analytics_revision is an invalidation counter, not cached data.
|
|
14751
|
+
DELETE FROM cache_meta WHERE key <> 'analytics_revision';
|
|
14752
|
+
`);
|
|
14753
|
+
advanceAnalyticsRevision(db);
|
|
14754
|
+
}).immediate();
|
|
14150
14755
|
});
|
|
14151
14756
|
closeCacheStorage();
|
|
14152
14757
|
deleteLegacyCacheFile();
|
|
@@ -14163,13 +14768,13 @@ function clearCache() {
|
|
|
14163
14768
|
}
|
|
14164
14769
|
}
|
|
14165
14770
|
}
|
|
14166
|
-
function attachMissingProjectIdentities(sessions,
|
|
14771
|
+
function attachMissingProjectIdentities(sessions, resolve3 = (directory) => computeIdentityProjection(directory, realFs)) {
|
|
14167
14772
|
const projections = /* @__PURE__ */ new Map();
|
|
14168
14773
|
return sessions.map((session) => {
|
|
14169
14774
|
const directory = normalizeProjectDirectory(session.directory);
|
|
14170
14775
|
let projection = projections.get(directory);
|
|
14171
14776
|
if (!projection) {
|
|
14172
|
-
projection =
|
|
14777
|
+
projection = resolve3(directory);
|
|
14173
14778
|
projections.set(directory, projection);
|
|
14174
14779
|
}
|
|
14175
14780
|
const identity = session.project_identity;
|
|
@@ -14194,28 +14799,61 @@ function buildAgentCacheMeta(agent, sessionIds) {
|
|
|
14194
14799
|
}
|
|
14195
14800
|
return meta;
|
|
14196
14801
|
}
|
|
14802
|
+
function signatureValues(value, spec) {
|
|
14803
|
+
const values = [];
|
|
14804
|
+
for (const key of Object.keys(spec)) {
|
|
14805
|
+
values.push(...spec[key](value));
|
|
14806
|
+
}
|
|
14807
|
+
return values;
|
|
14808
|
+
}
|
|
14809
|
+
function objectSignatureValues(value, spec) {
|
|
14810
|
+
const keys = Object.keys(spec);
|
|
14811
|
+
if (!value) return keys.map(() => null);
|
|
14812
|
+
return keys.map((key) => spec[key](value));
|
|
14813
|
+
}
|
|
14814
|
+
var SESSION_REFERENCE_SIGNATURE_SPEC = {
|
|
14815
|
+
agentName: (reference) => reference.agentName,
|
|
14816
|
+
sessionId: (reference) => reference.sessionId
|
|
14817
|
+
};
|
|
14818
|
+
var PROJECT_IDENTITY_SIGNATURE_SPEC = {
|
|
14819
|
+
kind: (identity) => identity.kind,
|
|
14820
|
+
key: (identity) => identity.key,
|
|
14821
|
+
displayName: (identity) => identity.displayName
|
|
14822
|
+
};
|
|
14823
|
+
var SESSION_STATS_SIGNATURE_SPEC = {
|
|
14824
|
+
message_count: (stats) => stats.message_count,
|
|
14825
|
+
total_input_tokens: (stats) => stats.total_input_tokens,
|
|
14826
|
+
total_output_tokens: (stats) => stats.total_output_tokens,
|
|
14827
|
+
total_cost: (stats) => stats.total_cost,
|
|
14828
|
+
cost_source: (stats) => stats.cost_source ?? null,
|
|
14829
|
+
total_tokens: (stats) => stats.total_tokens ?? 0,
|
|
14830
|
+
total_cache_read_tokens: (stats) => stats.total_cache_read_tokens ?? 0,
|
|
14831
|
+
total_cache_create_tokens: (stats) => stats.total_cache_create_tokens ?? 0
|
|
14832
|
+
};
|
|
14833
|
+
function modelUsageSignature(modelUsage) {
|
|
14834
|
+
if (!modelUsage) return null;
|
|
14835
|
+
return Object.entries(modelUsage).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([model, tokens]) => [model, tokens]);
|
|
14836
|
+
}
|
|
14837
|
+
var SESSION_HEAD_SIGNATURE_SPEC = {
|
|
14838
|
+
slug: (session) => [session.slug],
|
|
14839
|
+
title: (session) => [session.title],
|
|
14840
|
+
directory: (session) => [session.directory],
|
|
14841
|
+
parent_reference: (session) => objectSignatureValues(session.parent_reference, SESSION_REFERENCE_SIGNATURE_SPEC),
|
|
14842
|
+
project_identity: (session) => objectSignatureValues(session.project_identity, PROJECT_IDENTITY_SIGNATURE_SPEC),
|
|
14843
|
+
project_identity_resolver_revision: (session) => [
|
|
14844
|
+
session.project_identity_resolver_revision ?? null
|
|
14845
|
+
],
|
|
14846
|
+
project_identity_input_signature: (session) => [session.project_identity_input_signature ?? null],
|
|
14847
|
+
time_created: (session) => [session.time_created],
|
|
14848
|
+
time_updated: (session) => [session.time_updated ?? session.time_created],
|
|
14849
|
+
stats: (session) => objectSignatureValues(session.stats, SESSION_STATS_SIGNATURE_SPEC),
|
|
14850
|
+
model_usage: (session) => [modelUsageSignature(session.model_usage)],
|
|
14851
|
+
smart_tags: (session) => [session.smart_tags ? [...session.smart_tags].sort() : null],
|
|
14852
|
+
smart_tags_source_updated_at: (session) => [session.smart_tags_source_updated_at ?? null],
|
|
14853
|
+
smart_tags_classifier_revision: (session) => [session.smart_tags_classifier_revision ?? null]
|
|
14854
|
+
};
|
|
14197
14855
|
function sessionSignature(session) {
|
|
14198
|
-
return JSON.stringify(
|
|
14199
|
-
session.title,
|
|
14200
|
-
session.directory,
|
|
14201
|
-
session.parent_reference?.agentName ?? null,
|
|
14202
|
-
session.parent_reference?.sessionId ?? null,
|
|
14203
|
-
session.time_created,
|
|
14204
|
-
session.time_updated ?? session.time_created,
|
|
14205
|
-
session.stats.message_count,
|
|
14206
|
-
session.stats.total_input_tokens,
|
|
14207
|
-
session.stats.total_output_tokens,
|
|
14208
|
-
session.stats.total_cost,
|
|
14209
|
-
session.stats.total_tokens ?? 0,
|
|
14210
|
-
session.project_identity?.kind ?? null,
|
|
14211
|
-
session.project_identity?.key ?? null,
|
|
14212
|
-
session.project_identity?.displayName ?? null,
|
|
14213
|
-
session.project_identity_resolver_revision ?? null,
|
|
14214
|
-
session.project_identity_input_signature ?? null,
|
|
14215
|
-
session.smart_tags ? [...session.smart_tags].sort() : null,
|
|
14216
|
-
session.smart_tags_source_updated_at ?? null,
|
|
14217
|
-
session.smart_tags_classifier_revision ?? null
|
|
14218
|
-
]);
|
|
14856
|
+
return JSON.stringify(signatureValues(session, SESSION_HEAD_SIGNATURE_SPEC));
|
|
14219
14857
|
}
|
|
14220
14858
|
function sortSessions(sessions) {
|
|
14221
14859
|
return sortSessionsByActivity(sessions);
|
|
@@ -14286,7 +14924,20 @@ function saveCachedSessionDiff(agent, cachedSessions, updatedSessions, changedId
|
|
|
14286
14924
|
const diff = computeSessionDiff(cachedSessions, updatedSessions, changedIds, sessionSignature);
|
|
14287
14925
|
const explicitRemovals = new Set(explicitRemovedSessionIds);
|
|
14288
14926
|
const removedSessionIds = completeness === "complete" ? diff.removedSessionIds : diff.removedSessionIds.filter((sessionId) => explicitRemovals.has(sessionId));
|
|
14289
|
-
|
|
14927
|
+
const persisted = saveCachedSessionChanges(
|
|
14928
|
+
agent.name,
|
|
14929
|
+
diff.changes,
|
|
14930
|
+
removedSessionIds,
|
|
14931
|
+
buildAgentCacheMeta(agent)
|
|
14932
|
+
);
|
|
14933
|
+
if (persisted === false) {
|
|
14934
|
+
getCoreDiagnostics()?.warn("cache.save_failed", {
|
|
14935
|
+
agent: agent.name,
|
|
14936
|
+
changed_sessions: diff.changes.length,
|
|
14937
|
+
removed_sessions: removedSessionIds.length
|
|
14938
|
+
});
|
|
14939
|
+
}
|
|
14940
|
+
return persisted;
|
|
14290
14941
|
}
|
|
14291
14942
|
function getSmartTagWorkerCount(sessionCount) {
|
|
14292
14943
|
if (sessionCount < 50) return 1;
|
|
@@ -14357,30 +15008,46 @@ function ensureSessionTagsSync(agent, sessions, onProgress, classifierRevision =
|
|
|
14357
15008
|
});
|
|
14358
15009
|
return { sessions: tagged, changed, timing };
|
|
14359
15010
|
}
|
|
15011
|
+
var SMART_TAG_WORKER_TIMEOUT_MS = 3e5;
|
|
14360
15012
|
async function classifySessionTagsInWorker(workerUrl, agentName, sessionIds, meta) {
|
|
14361
|
-
|
|
14362
|
-
|
|
14363
|
-
|
|
14364
|
-
|
|
14365
|
-
|
|
14366
|
-
|
|
14367
|
-
|
|
14368
|
-
|
|
14369
|
-
|
|
14370
|
-
|
|
14371
|
-
|
|
14372
|
-
|
|
14373
|
-
|
|
14374
|
-
|
|
14375
|
-
|
|
15013
|
+
const worker = new Worker(workerUrl, {
|
|
15014
|
+
workerData: {
|
|
15015
|
+
pricingGenerationId: getPricingGeneration().id,
|
|
15016
|
+
agentName,
|
|
15017
|
+
sessionIds,
|
|
15018
|
+
meta
|
|
15019
|
+
}
|
|
15020
|
+
});
|
|
15021
|
+
let timer;
|
|
15022
|
+
try {
|
|
15023
|
+
return await new Promise((resolveWorker, rejectWorker) => {
|
|
15024
|
+
timer = setTimeout(() => {
|
|
15025
|
+
rejectWorker(
|
|
15026
|
+
new Error(`Smart tag worker timed out after ${SMART_TAG_WORKER_TIMEOUT_MS}ms`)
|
|
15027
|
+
);
|
|
15028
|
+
}, SMART_TAG_WORKER_TIMEOUT_MS);
|
|
15029
|
+
worker.on("message", (message) => {
|
|
15030
|
+
if (isWorkerLogMessage(message)) {
|
|
15031
|
+
relayWorkerLogMessage(message);
|
|
15032
|
+
return;
|
|
15033
|
+
}
|
|
15034
|
+
const results = message;
|
|
15035
|
+
if (results.length === 0 && sessionIds.length > 0) {
|
|
15036
|
+
rejectWorker(new Error("Smart tag worker returned no results"));
|
|
15037
|
+
return;
|
|
15038
|
+
}
|
|
15039
|
+
resolveWorker(results);
|
|
15040
|
+
});
|
|
15041
|
+
worker.once("error", rejectWorker);
|
|
15042
|
+
worker.once("exit", (code) => {
|
|
15043
|
+
rejectWorker(new Error(`Smart tag worker exited with code ${code} before responding`));
|
|
15044
|
+
});
|
|
14376
15045
|
});
|
|
14377
|
-
|
|
14378
|
-
|
|
14379
|
-
|
|
14380
|
-
rejectWorker(new Error(`Smart tag worker exited with code ${code}`));
|
|
14381
|
-
}
|
|
15046
|
+
} finally {
|
|
15047
|
+
if (timer) clearTimeout(timer);
|
|
15048
|
+
worker.terminate().catch(() => {
|
|
14382
15049
|
});
|
|
14383
|
-
}
|
|
15050
|
+
}
|
|
14384
15051
|
}
|
|
14385
15052
|
function relayWorkerLogMessage(message) {
|
|
14386
15053
|
const detail = {
|
|
@@ -14455,18 +15122,19 @@ async function finalizeAgentScan(agent, sessions, context) {
|
|
|
14455
15122
|
tagged = options.includeSmartTags === false ? tagged : await ensureSessionTags(agent, sessionsWithIdentity, options.smartTagWorkerUrl);
|
|
14456
15123
|
timing.tags = performance.now() - tagsStart;
|
|
14457
15124
|
}
|
|
15125
|
+
let cachePersistence = "not-requested";
|
|
14458
15126
|
if (options.writeCache !== false) {
|
|
14459
15127
|
if (finalization.kind === "incremental") {
|
|
14460
|
-
saveCachedSessionDiff(
|
|
15128
|
+
cachePersistence = saveCachedSessionDiff(
|
|
14461
15129
|
agent,
|
|
14462
15130
|
finalization.cached.sessions,
|
|
14463
15131
|
tagged.sessions,
|
|
14464
15132
|
finalization.changedIds,
|
|
14465
15133
|
context.completeness,
|
|
14466
15134
|
finalization.explicitRemovedSessionIds
|
|
14467
|
-
);
|
|
15135
|
+
) === false ? "failed" : "persisted";
|
|
14468
15136
|
} else if (finalization.kind === "unchanged" && (identityChanged || tagged.changed)) {
|
|
14469
|
-
saveCachedSessionDiff(agent, finalization.cached.sessions, tagged.sessions);
|
|
15137
|
+
cachePersistence = saveCachedSessionDiff(agent, finalization.cached.sessions, tagged.sessions) === false ? "failed" : "persisted";
|
|
14470
15138
|
}
|
|
14471
15139
|
}
|
|
14472
15140
|
if (isIncremental) {
|
|
@@ -14478,10 +15146,11 @@ async function finalizeAgentScan(agent, sessions, context) {
|
|
|
14478
15146
|
status: context.completeness,
|
|
14479
15147
|
agent,
|
|
14480
15148
|
heads,
|
|
15149
|
+
cachePersistence,
|
|
14481
15150
|
fromCache: true,
|
|
14482
15151
|
...isIncremental ? { refreshed: true } : {},
|
|
14483
15152
|
timing,
|
|
14484
|
-
cacheTimestamp: isIncremental ? finalization.cacheTimestamp : finalization.cached.timestamp
|
|
15153
|
+
cacheTimestamp: isIncremental && cachePersistence === "persisted" ? finalization.cacheTimestamp : finalization.cached.timestamp
|
|
14485
15154
|
};
|
|
14486
15155
|
}
|
|
14487
15156
|
async function refreshCachedFileAgent(agent, cached, options, timing, agentStart, onProgress) {
|
|
@@ -14597,6 +15266,7 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
14597
15266
|
)
|
|
14598
15267
|
);
|
|
14599
15268
|
timing.scan = performance.now() - t2;
|
|
15269
|
+
agent.commitChangeCheck();
|
|
14600
15270
|
return finalizeAgentScan(agent, updatedSessions, {
|
|
14601
15271
|
finalization: {
|
|
14602
15272
|
kind: "incremental",
|
|
@@ -14611,6 +15281,7 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
14611
15281
|
onProgress
|
|
14612
15282
|
});
|
|
14613
15283
|
}
|
|
15284
|
+
agent.commitChangeCheck();
|
|
14614
15285
|
return finalizeAgentScan(agent, cached.sessions, {
|
|
14615
15286
|
finalization: { kind: "unchanged", cached },
|
|
14616
15287
|
options,
|
|
@@ -14661,15 +15332,20 @@ async function scanAgentFull(agent, options, onProgress, timing = { total: 0 },
|
|
|
14661
15332
|
const tagged = options.includeSmartTags === false ? { sessions: headsWithIdentity, changed: false } : await ensureSessionTags(agent, headsWithIdentity, options.smartTagWorkerUrl);
|
|
14662
15333
|
timing.tags = performance.now() - t2;
|
|
14663
15334
|
const meta = buildAgentCacheMeta(agent);
|
|
15335
|
+
let cachePersistence = "not-requested";
|
|
14664
15336
|
if (options.writeCache !== false) {
|
|
14665
15337
|
const isFullWindow = options.from == null && options.to == null;
|
|
14666
15338
|
const persisted = saveCachedSessions(agent.name, tagged.sessions, meta, {
|
|
14667
15339
|
completeness: isFullWindow && sourceFailures.length === 0 ? "complete" : "partial"
|
|
14668
15340
|
});
|
|
14669
|
-
if (persisted) {
|
|
14670
|
-
|
|
15341
|
+
if (persisted !== false) {
|
|
15342
|
+
cachePersistence = "persisted";
|
|
14671
15343
|
markAgentCacheInitialized(agent.name);
|
|
15344
|
+
if (isFullWindow && sourceFailures.length === 0 && !markAgentFullSyncCompleted(agent.name)) {
|
|
15345
|
+
getCoreDiagnostics()?.warn("cache.full_sync_marker_failed", { agent: agent.name });
|
|
15346
|
+
}
|
|
14672
15347
|
} else {
|
|
15348
|
+
cachePersistence = "failed";
|
|
14673
15349
|
getCoreDiagnostics()?.warn("cache.save_failed", {
|
|
14674
15350
|
agent: agent.name,
|
|
14675
15351
|
sessions: tagged.sessions.length
|
|
@@ -14683,6 +15359,7 @@ async function scanAgentFull(agent, options, onProgress, timing = { total: 0 },
|
|
|
14683
15359
|
status: options.from == null && options.to == null && sourceFailures.length === 0 ? "complete" : "partial",
|
|
14684
15360
|
agent,
|
|
14685
15361
|
heads: filtered,
|
|
15362
|
+
cachePersistence,
|
|
14686
15363
|
fromCache: false,
|
|
14687
15364
|
timing
|
|
14688
15365
|
};
|
|
@@ -14718,6 +15395,7 @@ async function scanSessions(options = {}, onProgress) {
|
|
|
14718
15395
|
const allSessions = [];
|
|
14719
15396
|
const availableAgents = [];
|
|
14720
15397
|
const cacheTimestamps = {};
|
|
15398
|
+
const cacheFailures = {};
|
|
14721
15399
|
const scanFailures = {};
|
|
14722
15400
|
const agentFilter = options.agents?.length ? new Set(options.agents.map((a) => a.toLowerCase())) : null;
|
|
14723
15401
|
const agentsToScan = agents.filter((agent) => {
|
|
@@ -14741,6 +15419,9 @@ async function scanSessions(options = {}, onProgress) {
|
|
|
14741
15419
|
} else {
|
|
14742
15420
|
byAgent[result.agent.name] = result.heads;
|
|
14743
15421
|
allSessions.push(...result.heads);
|
|
15422
|
+
if (result.cachePersistence === "failed") {
|
|
15423
|
+
cacheFailures[result.agent.name] = { agentName: result.agent.name };
|
|
15424
|
+
}
|
|
14744
15425
|
}
|
|
14745
15426
|
if (result.timing) {
|
|
14746
15427
|
timings[result.agent.name] = result.timing;
|
|
@@ -14757,11 +15438,11 @@ async function scanSessions(options = {}, onProgress) {
|
|
|
14757
15438
|
agents: availableAgents,
|
|
14758
15439
|
timings,
|
|
14759
15440
|
cacheTimestamps: Object.keys(cacheTimestamps).length > 0 ? cacheTimestamps : void 0,
|
|
15441
|
+
cacheFailures: Object.keys(cacheFailures).length > 0 ? cacheFailures : void 0,
|
|
14760
15442
|
scanFailures: Object.keys(scanFailures).length > 0 ? scanFailures : void 0
|
|
14761
15443
|
};
|
|
14762
15444
|
}
|
|
14763
15445
|
var sessionDetailLookups = /* @__PURE__ */ new WeakMap();
|
|
14764
|
-
var MESSAGE_CURSOR_VERSION = 1;
|
|
14765
15446
|
var MAX_MESSAGE_CURSOR_LENGTH = 512;
|
|
14766
15447
|
function parseMessageCursor(value) {
|
|
14767
15448
|
if (!value || value.length > MAX_MESSAGE_CURSOR_LENGTH) return null;
|
|
@@ -14782,49 +15463,36 @@ function encodeMessageCursor(count, digest) {
|
|
|
14782
15463
|
"base64url"
|
|
14783
15464
|
);
|
|
14784
15465
|
}
|
|
14785
|
-
function
|
|
14786
|
-
return createHash22("sha256").update("codesesh-session-messages-v1\0").update(JSON.stringify([reference.agentName, reference.sessionId])).update("\n");
|
|
14787
|
-
}
|
|
14788
|
-
function updateMessageCursorField(hash, value) {
|
|
14789
|
-
if (value == null) {
|
|
14790
|
-
hash.update("n;");
|
|
14791
|
-
return;
|
|
14792
|
-
}
|
|
14793
|
-
const text = String(value);
|
|
14794
|
-
hash.update(`v${text.length}:`).update(text).update(";");
|
|
14795
|
-
}
|
|
14796
|
-
function updateMessageCursorHash(hash, row) {
|
|
14797
|
-
updateMessageCursorField(hash, row.message_id);
|
|
14798
|
-
updateMessageCursorField(hash, row.role);
|
|
14799
|
-
updateMessageCursorField(hash, row.time_created);
|
|
14800
|
-
updateMessageCursorField(hash, row.time_completed);
|
|
14801
|
-
updateMessageCursorField(hash, row.agent);
|
|
14802
|
-
updateMessageCursorField(hash, row.mode);
|
|
14803
|
-
updateMessageCursorField(hash, row.model);
|
|
14804
|
-
updateMessageCursorField(hash, row.provider);
|
|
14805
|
-
updateMessageCursorField(hash, row.tokens_json);
|
|
14806
|
-
updateMessageCursorField(hash, row.cost);
|
|
14807
|
-
updateMessageCursorField(hash, row.cost_source);
|
|
14808
|
-
updateMessageCursorField(hash, row.parts_json);
|
|
14809
|
-
updateMessageCursorField(hash, row.parts_format_version);
|
|
14810
|
-
updateMessageCursorField(hash, row.subagent_id);
|
|
14811
|
-
updateMessageCursorField(hash, row.nickname);
|
|
14812
|
-
hash.update("\n");
|
|
14813
|
-
}
|
|
14814
|
-
function projectMessageStream(reference, rows, requestedCursor) {
|
|
15466
|
+
function loadCachedMessageStream(reference, entry, cursor, requestedCursor) {
|
|
14815
15467
|
const requested = parseMessageCursor(requestedCursor);
|
|
14816
|
-
const
|
|
14817
|
-
|
|
14818
|
-
|
|
14819
|
-
|
|
14820
|
-
|
|
14821
|
-
|
|
14822
|
-
|
|
14823
|
-
|
|
15468
|
+
const initialDigest = initialMessageCursorDigest(reference);
|
|
15469
|
+
const hasStoredChain = entry.messageCount === 0 || entry.messageDigest !== null;
|
|
15470
|
+
const canCheckAppend = hasStoredChain && requested !== null && requested.count <= entry.messageCount;
|
|
15471
|
+
const requestedDigest = canCheckAppend && requested ? requested.count === 0 ? initialDigest : cursor.messageDigest(requested.count) : null;
|
|
15472
|
+
const canAppend = canCheckAppend && requested !== null && requestedDigest === requested.digest;
|
|
15473
|
+
let startIndex = canAppend ? requested.count : 0;
|
|
15474
|
+
let update = canAppend ? "append" : "reset";
|
|
15475
|
+
let messageRows = entry.messageCount === 0 ? [] : cursor.messageRows(startIndex);
|
|
15476
|
+
let messageCount = entry.messageCount;
|
|
15477
|
+
let digest = entry.messageCount === 0 ? initialDigest : entry.messageDigest;
|
|
15478
|
+
if (messageRows.length !== entry.messageCount - startIndex) {
|
|
15479
|
+
startIndex = 0;
|
|
15480
|
+
update = "reset";
|
|
15481
|
+
messageRows = cursor.messageRows(startIndex);
|
|
15482
|
+
messageCount = messageRows.length;
|
|
15483
|
+
digest = null;
|
|
15484
|
+
}
|
|
15485
|
+
if (!digest) {
|
|
15486
|
+
digest = computeMessageCursorDigest(
|
|
15487
|
+
reference,
|
|
15488
|
+
messageRows.map((row) => messageCursorContentFromCachedRow(row))
|
|
15489
|
+
);
|
|
15490
|
+
}
|
|
14824
15491
|
return {
|
|
14825
|
-
cursor: encodeMessageCursor(
|
|
14826
|
-
|
|
14827
|
-
|
|
15492
|
+
cursor: encodeMessageCursor(messageCount, digest),
|
|
15493
|
+
update,
|
|
15494
|
+
messageRows,
|
|
15495
|
+
messageCount
|
|
14828
15496
|
};
|
|
14829
15497
|
}
|
|
14830
15498
|
function sessionReferenceKey(agentName, sessionId) {
|
|
@@ -14859,14 +15527,14 @@ function getSessionDetailContext(scanResult, reference) {
|
|
|
14859
15527
|
)
|
|
14860
15528
|
};
|
|
14861
15529
|
}
|
|
14862
|
-
function cachedDetailState(cachedEntry, currentMeta) {
|
|
14863
|
-
if (!cachedEntry ||
|
|
15530
|
+
function cachedDetailState(cachedEntry, currentMeta, messageCount) {
|
|
15531
|
+
if (!cachedEntry || messageCount === 0 && cachedEntry.data.stats.message_count > 0) {
|
|
14864
15532
|
return "missing";
|
|
14865
15533
|
}
|
|
14866
15534
|
return !cachedEntry.pendingReindex && cachedEntry.detailVersion === sessionDetailVersion(currentMeta) ? "fresh" : "stale";
|
|
14867
15535
|
}
|
|
14868
|
-
function getProjectIdentity(data, head) {
|
|
14869
|
-
return head?.project_identity ?? data.project_identity ??
|
|
15536
|
+
function getProjectIdentity(data, head, fallback) {
|
|
15537
|
+
return head?.project_identity ?? data.project_identity ?? fallback ?? null;
|
|
14870
15538
|
}
|
|
14871
15539
|
function getSmartTags(data) {
|
|
14872
15540
|
if (Array.isArray(data.smart_tags) && data.smart_tags_classifier_revision === SMART_TAG_CLASSIFIER_REVISION) {
|
|
@@ -14874,10 +15542,14 @@ function getSmartTags(data) {
|
|
|
14874
15542
|
}
|
|
14875
15543
|
return classifySessionTags(data);
|
|
14876
15544
|
}
|
|
14877
|
-
function materializeStructuredSessionDetail(context, reference, cachedEntry = loadCachedSessionRawEntry(reference.agentName, reference.sessionId)) {
|
|
15545
|
+
function materializeStructuredSessionDetail(context, reference, cachedEntry = loadCachedSessionRawEntry(reference.agentName, reference.sessionId), identityFallback) {
|
|
14878
15546
|
const { agent, head } = context;
|
|
14879
15547
|
const currentMeta = head ? agent.getSessionMetaMap().get(reference.sessionId) : void 0;
|
|
14880
|
-
const cacheState = cachedDetailState(
|
|
15548
|
+
const cacheState = cachedDetailState(
|
|
15549
|
+
cachedEntry,
|
|
15550
|
+
currentMeta,
|
|
15551
|
+
cachedEntry?.messageRows.length ?? 0
|
|
15552
|
+
);
|
|
14881
15553
|
let useCache = cacheState === "fresh";
|
|
14882
15554
|
let freshness = useCache ? "fresh" : void 0;
|
|
14883
15555
|
let data = useCache ? {
|
|
@@ -14912,7 +15584,8 @@ function materializeStructuredSessionDetail(context, reference, cachedEntry = lo
|
|
|
14912
15584
|
if (!data) {
|
|
14913
15585
|
return { status: "not-ready" };
|
|
14914
15586
|
}
|
|
14915
|
-
const projectIdentity = getProjectIdentity(data, head);
|
|
15587
|
+
const projectIdentity = getProjectIdentity(data, head, identityFallback);
|
|
15588
|
+
if (!projectIdentity) return { status: "needs-identity", directory: data.directory };
|
|
14916
15589
|
const fileActivity = data.file_activity ?? (useCache ? listSessionFileActivity(reference.agentName, reference.sessionId) : extractSessionFileActivity(
|
|
14917
15590
|
reference.agentName,
|
|
14918
15591
|
reference.sessionId,
|
|
@@ -14935,23 +15608,60 @@ function materializeStructuredSessionDetail(context, reference, cachedEntry = lo
|
|
|
14935
15608
|
}
|
|
14936
15609
|
};
|
|
14937
15610
|
}
|
|
14938
|
-
function* serializeCachedMessages(
|
|
14939
|
-
for (
|
|
14940
|
-
yield messageJsonFromCachedRow(
|
|
15611
|
+
function* serializeCachedMessages(messageRows) {
|
|
15612
|
+
for (const messageRow of messageRows) {
|
|
15613
|
+
yield messageJsonFromCachedRow(messageRow);
|
|
14941
15614
|
}
|
|
14942
15615
|
}
|
|
14943
15616
|
function materializeSessionDetailResponse(scanResult, reference, options = {}) {
|
|
14944
15617
|
const context = getSessionDetailContext(scanResult, reference);
|
|
14945
15618
|
if (!context) return { status: "unknown-agent" };
|
|
14946
|
-
const cachedEntry = loadCachedSessionRawEntry(reference.agentName, reference.sessionId);
|
|
14947
15619
|
const currentMeta = context.head ? context.agent.getSessionMetaMap().get(reference.sessionId) : void 0;
|
|
14948
|
-
const
|
|
15620
|
+
const startedAt = performance.now();
|
|
15621
|
+
const cursorRead = readCachedSessionCursor(
|
|
15622
|
+
reference.agentName,
|
|
15623
|
+
reference.sessionId,
|
|
15624
|
+
(entry, cursor) => {
|
|
15625
|
+
const cacheState2 = cachedDetailState(entry, currentMeta, entry.messageCount);
|
|
15626
|
+
const stream2 = cacheState2 === "fresh" && entry.data.smart_tags != null && entry.data.smart_tags_classifier_revision === SMART_TAG_CLASSIFIER_REVISION ? loadCachedMessageStream(reference, entry, cursor, options.messageCursor) : null;
|
|
15627
|
+
return { entry, cacheState: cacheState2, stream: stream2 };
|
|
15628
|
+
}
|
|
15629
|
+
);
|
|
15630
|
+
const cachedEntry = cursorRead?.entry ?? null;
|
|
15631
|
+
const cacheState = cursorRead?.cacheState ?? "missing";
|
|
14949
15632
|
if (!cachedEntry || cacheState !== "fresh" || cachedEntry.data.smart_tags == null || cachedEntry.data.smart_tags_classifier_revision !== SMART_TAG_CLASSIFIER_REVISION) {
|
|
14950
|
-
const result = materializeStructuredSessionDetail(
|
|
15633
|
+
const result = materializeStructuredSessionDetail(
|
|
15634
|
+
context,
|
|
15635
|
+
reference,
|
|
15636
|
+
void 0,
|
|
15637
|
+
options.projectIdentityFallback
|
|
15638
|
+
);
|
|
14951
15639
|
return result.status === "found" ? { ...result, data: { ...result.data, message_update: "reset" } } : result;
|
|
14952
15640
|
}
|
|
14953
15641
|
const data = cachedEntry.data;
|
|
14954
|
-
const
|
|
15642
|
+
const projectIdentity = getProjectIdentity(data, context.head, options.projectIdentityFallback);
|
|
15643
|
+
if (!projectIdentity) return { status: "needs-identity", directory: data.directory };
|
|
15644
|
+
const stream = cursorRead?.stream;
|
|
15645
|
+
if (!stream) {
|
|
15646
|
+
const result = materializeStructuredSessionDetail(
|
|
15647
|
+
context,
|
|
15648
|
+
reference,
|
|
15649
|
+
void 0,
|
|
15650
|
+
options.projectIdentityFallback
|
|
15651
|
+
);
|
|
15652
|
+
return result.status === "found" ? { ...result, data: { ...result.data, message_update: "reset" } } : result;
|
|
15653
|
+
}
|
|
15654
|
+
const partsJsonBytes = stream.messageRows.reduce(
|
|
15655
|
+
(total, row) => total + Buffer.byteLength(String(row.parts_json)),
|
|
15656
|
+
0
|
|
15657
|
+
);
|
|
15658
|
+
getCoreDiagnostics()?.info?.("session_detail.cursor_stream", {
|
|
15659
|
+
update: stream.update,
|
|
15660
|
+
message_count: stream.messageCount,
|
|
15661
|
+
sent_message_count: stream.messageRows.length,
|
|
15662
|
+
parts_json_bytes: partsJsonBytes,
|
|
15663
|
+
duration_ms: Math.round(performance.now() - startedAt)
|
|
15664
|
+
});
|
|
14955
15665
|
return {
|
|
14956
15666
|
status: "found-json",
|
|
14957
15667
|
data: {
|
|
@@ -14960,13 +15670,13 @@ function materializeSessionDetailResponse(scanResult, reference, options = {}) {
|
|
|
14960
15670
|
detail_freshness: "fresh",
|
|
14961
15671
|
message_cursor: stream.cursor,
|
|
14962
15672
|
message_update: stream.update,
|
|
14963
|
-
project_identity:
|
|
15673
|
+
project_identity: projectIdentity,
|
|
14964
15674
|
smart_tags_source_updated_at: getSmartTagSourceTimestamp(data),
|
|
14965
15675
|
file_activity: data.file_activity ?? listSessionFileActivity(reference.agentName, reference.sessionId)
|
|
14966
15676
|
},
|
|
14967
|
-
messages: serializeCachedMessages(
|
|
14968
|
-
messageCount:
|
|
14969
|
-
sentMessageCount:
|
|
15677
|
+
messages: serializeCachedMessages(stream.messageRows),
|
|
15678
|
+
messageCount: stream.messageCount,
|
|
15679
|
+
sentMessageCount: stream.messageRows.length
|
|
14970
15680
|
};
|
|
14971
15681
|
}
|
|
14972
15682
|
function listCachedProjectGroups(sessions) {
|
|
@@ -14997,6 +15707,158 @@ function listCachedProjectGroups(sessions) {
|
|
|
14997
15707
|
lastActivity: row.last_activity == null ? null : Number(row.last_activity)
|
|
14998
15708
|
}));
|
|
14999
15709
|
}
|
|
15710
|
+
var EFFECTIVE_COST_TIME = `CASE
|
|
15711
|
+
WHEN m.time_completed > 0 THEN m.time_completed
|
|
15712
|
+
WHEN m.time_created > 0 THEN m.time_created
|
|
15713
|
+
END`;
|
|
15714
|
+
function sessionKey2(agentName, sessionId) {
|
|
15715
|
+
return `${agentName}\0${sessionId}`;
|
|
15716
|
+
}
|
|
15717
|
+
function costSource(value) {
|
|
15718
|
+
return value === "recorded" || value === "estimated" ? value : void 0;
|
|
15719
|
+
}
|
|
15720
|
+
function nonNegative2(value) {
|
|
15721
|
+
return Math.max(0, Number(value ?? 0));
|
|
15722
|
+
}
|
|
15723
|
+
function readCostFacts(db, options) {
|
|
15724
|
+
const summaries = db.prepare(
|
|
15725
|
+
`
|
|
15726
|
+
SELECT
|
|
15727
|
+
c.agent_name,
|
|
15728
|
+
c.session_id,
|
|
15729
|
+
c.message_count,
|
|
15730
|
+
c.untimed_message_count,
|
|
15731
|
+
c.input_tokens,
|
|
15732
|
+
c.output_tokens,
|
|
15733
|
+
c.reasoning_tokens,
|
|
15734
|
+
c.cache_read_tokens,
|
|
15735
|
+
c.cache_create_tokens,
|
|
15736
|
+
c.untimed_input_tokens,
|
|
15737
|
+
c.untimed_output_tokens,
|
|
15738
|
+
c.untimed_reasoning_tokens,
|
|
15739
|
+
c.untimed_cache_read_tokens,
|
|
15740
|
+
c.untimed_cache_create_tokens,
|
|
15741
|
+
c.message_cost,
|
|
15742
|
+
c.untimed_message_cost
|
|
15743
|
+
FROM session_cost_summary c
|
|
15744
|
+
JOIN sessions s
|
|
15745
|
+
ON s.agent_name = c.agent_name
|
|
15746
|
+
AND s.session_id = c.session_id
|
|
15747
|
+
AND s.publication_id IS NULL
|
|
15748
|
+
ORDER BY c.agent_name, c.session_id
|
|
15749
|
+
`
|
|
15750
|
+
).all();
|
|
15751
|
+
const bySession = /* @__PURE__ */ new Map();
|
|
15752
|
+
for (const row of summaries) {
|
|
15753
|
+
const agentName = String(row.agent_name ?? "");
|
|
15754
|
+
const sessionId = String(row.session_id ?? "");
|
|
15755
|
+
const summary = {
|
|
15756
|
+
reference: { agentName, sessionId },
|
|
15757
|
+
messageCount: nonNegative2(row.message_count),
|
|
15758
|
+
untimedMessageCount: nonNegative2(row.untimed_message_count),
|
|
15759
|
+
inputTokens: nonNegative2(row.input_tokens),
|
|
15760
|
+
outputTokens: nonNegative2(row.output_tokens),
|
|
15761
|
+
reasoningTokens: nonNegative2(row.reasoning_tokens),
|
|
15762
|
+
cacheReadTokens: nonNegative2(row.cache_read_tokens),
|
|
15763
|
+
cacheCreateTokens: nonNegative2(row.cache_create_tokens),
|
|
15764
|
+
untimedInputTokens: nonNegative2(row.untimed_input_tokens),
|
|
15765
|
+
untimedOutputTokens: nonNegative2(row.untimed_output_tokens),
|
|
15766
|
+
untimedReasoningTokens: nonNegative2(row.untimed_reasoning_tokens),
|
|
15767
|
+
untimedCacheReadTokens: nonNegative2(row.untimed_cache_read_tokens),
|
|
15768
|
+
untimedCacheCreateTokens: nonNegative2(row.untimed_cache_create_tokens),
|
|
15769
|
+
messageCost: Number(row.message_cost ?? 0),
|
|
15770
|
+
untimedMessageCost: Number(row.untimed_message_cost ?? 0),
|
|
15771
|
+
modelCosts: []
|
|
15772
|
+
};
|
|
15773
|
+
bySession.set(sessionKey2(agentName, sessionId), summary);
|
|
15774
|
+
}
|
|
15775
|
+
const modelRows = options.includeModelCosts === false ? [] : db.prepare(
|
|
15776
|
+
`
|
|
15777
|
+
SELECT
|
|
15778
|
+
m.agent_name,
|
|
15779
|
+
m.session_id,
|
|
15780
|
+
m.model,
|
|
15781
|
+
m.cost,
|
|
15782
|
+
m.cost_recorded
|
|
15783
|
+
FROM session_model_cost m
|
|
15784
|
+
JOIN sessions s
|
|
15785
|
+
ON s.agent_name = m.agent_name
|
|
15786
|
+
AND s.session_id = m.session_id
|
|
15787
|
+
AND s.publication_id IS NULL
|
|
15788
|
+
ORDER BY m.agent_name, m.session_id, m.model
|
|
15789
|
+
`
|
|
15790
|
+
).all();
|
|
15791
|
+
for (const row of modelRows) {
|
|
15792
|
+
const summary = bySession.get(
|
|
15793
|
+
sessionKey2(String(row.agent_name ?? ""), String(row.session_id ?? ""))
|
|
15794
|
+
);
|
|
15795
|
+
if (!summary) continue;
|
|
15796
|
+
summary.modelCosts.push({
|
|
15797
|
+
model: String(row.model ?? ""),
|
|
15798
|
+
cost: Number(row.cost ?? 0),
|
|
15799
|
+
costRecorded: Number(row.cost_recorded ?? 0)
|
|
15800
|
+
});
|
|
15801
|
+
}
|
|
15802
|
+
const clauses = [`${EFFECTIVE_COST_TIME} IS NOT NULL`];
|
|
15803
|
+
const params = [];
|
|
15804
|
+
if (options.from != null) {
|
|
15805
|
+
clauses.push(`${EFFECTIVE_COST_TIME} >= ?`);
|
|
15806
|
+
params.push(options.from);
|
|
15807
|
+
}
|
|
15808
|
+
if (options.to != null) {
|
|
15809
|
+
clauses.push(`${EFFECTIVE_COST_TIME} <= ?`);
|
|
15810
|
+
params.push(options.to);
|
|
15811
|
+
}
|
|
15812
|
+
const messageRows = db.prepare(
|
|
15813
|
+
`
|
|
15814
|
+
SELECT
|
|
15815
|
+
m.agent_name,
|
|
15816
|
+
m.session_id,
|
|
15817
|
+
${EFFECTIVE_COST_TIME} AS cost_time,
|
|
15818
|
+
m.model,
|
|
15819
|
+
CAST(COALESCE(json_extract(m.tokens_json, '$.input'), 0) AS INTEGER) AS input_tokens,
|
|
15820
|
+
CAST(COALESCE(json_extract(m.tokens_json, '$.output'), 0) AS INTEGER) AS output_tokens,
|
|
15821
|
+
CAST(COALESCE(json_extract(m.tokens_json, '$.reasoning'), 0) AS INTEGER) AS reasoning_tokens,
|
|
15822
|
+
CAST(COALESCE(json_extract(m.tokens_json, '$.cache_read'), 0) AS INTEGER) AS cache_read_tokens,
|
|
15823
|
+
CAST(COALESCE(json_extract(m.tokens_json, '$.cache_create'), 0) AS INTEGER) AS cache_create_tokens,
|
|
15824
|
+
m.cost,
|
|
15825
|
+
m.cost_source
|
|
15826
|
+
FROM messages m INDEXED BY idx_messages_usage_time
|
|
15827
|
+
JOIN sessions s
|
|
15828
|
+
ON s.agent_name = m.agent_name
|
|
15829
|
+
AND s.session_id = m.session_id
|
|
15830
|
+
AND s.publication_id IS NULL
|
|
15831
|
+
WHERE ${clauses.join(" AND ")}
|
|
15832
|
+
ORDER BY cost_time, m.agent_name, m.session_id, m.message_index
|
|
15833
|
+
`
|
|
15834
|
+
).all(...params);
|
|
15835
|
+
const messages = messageRows.map((row) => {
|
|
15836
|
+
const model = typeof row.model === "string" && row.model.length > 0 ? row.model : void 0;
|
|
15837
|
+
return {
|
|
15838
|
+
reference: {
|
|
15839
|
+
agentName: String(row.agent_name ?? ""),
|
|
15840
|
+
sessionId: String(row.session_id ?? "")
|
|
15841
|
+
},
|
|
15842
|
+
time: Number(row.cost_time ?? 0),
|
|
15843
|
+
inputTokens: nonNegative2(row.input_tokens),
|
|
15844
|
+
outputTokens: nonNegative2(row.output_tokens),
|
|
15845
|
+
reasoningTokens: nonNegative2(row.reasoning_tokens),
|
|
15846
|
+
cacheReadTokens: nonNegative2(row.cache_read_tokens),
|
|
15847
|
+
cacheCreateTokens: nonNegative2(row.cache_create_tokens),
|
|
15848
|
+
cost: Number(row.cost ?? 0),
|
|
15849
|
+
costSource: costSource(row.cost_source),
|
|
15850
|
+
...model ? { model } : {}
|
|
15851
|
+
};
|
|
15852
|
+
});
|
|
15853
|
+
return { messages, sessions: [...bySession.values()] };
|
|
15854
|
+
}
|
|
15855
|
+
function listDashboardCostFacts(options = {}) {
|
|
15856
|
+
if (!hasCacheStorage()) return null;
|
|
15857
|
+
const read = withCacheDbReadOnly(
|
|
15858
|
+
(db) => db.transaction(() => readCostFacts(db, options))()
|
|
15859
|
+
);
|
|
15860
|
+
return read.status === "success" ? read.value : null;
|
|
15861
|
+
}
|
|
15000
15862
|
var DEFAULT_MODEL_COST_LIMIT = 20;
|
|
15001
15863
|
function buildModelCostWhere(options) {
|
|
15002
15864
|
const clauses = ["m.model IS NOT NULL", "m.model <> ''"];
|
|
@@ -15026,10 +15888,10 @@ function buildModelCostWhere(options) {
|
|
|
15026
15888
|
var MODEL_COST_SQL = `
|
|
15027
15889
|
SELECT
|
|
15028
15890
|
m.model AS model,
|
|
15029
|
-
SUM(
|
|
15030
|
-
SUM(
|
|
15031
|
-
SUM(
|
|
15032
|
-
FROM
|
|
15891
|
+
SUM(m.cost) AS cost,
|
|
15892
|
+
SUM(m.cost_recorded) AS cost_recorded,
|
|
15893
|
+
SUM(m.cost - m.cost_recorded) AS cost_estimated
|
|
15894
|
+
FROM session_model_cost m
|
|
15033
15895
|
JOIN sessions s
|
|
15034
15896
|
ON s.agent_name = m.agent_name
|
|
15035
15897
|
AND s.session_id = m.session_id
|
|
@@ -15081,6 +15943,10 @@ function publicationSearchOptions(publication, options, publicationId) {
|
|
|
15081
15943
|
};
|
|
15082
15944
|
}
|
|
15083
15945
|
function commitDurableSessionPublication(publication, loadSessionData, searchOptions = {}) {
|
|
15946
|
+
assertSessionProjectIdentities(
|
|
15947
|
+
publication.agentName,
|
|
15948
|
+
publication.kind === "snapshot" ? publication.sessions : publication.changes.map(({ session }) => session)
|
|
15949
|
+
);
|
|
15084
15950
|
const publicationId = publication.publicationId ?? randomUUID();
|
|
15085
15951
|
let failureStage = "prepare";
|
|
15086
15952
|
const diagnostics2 = getCoreDiagnostics();
|
|
@@ -15139,6 +16005,7 @@ function commitDurableSessionPublication(publication, loadSessionData, searchOpt
|
|
|
15139
16005
|
...detail,
|
|
15140
16006
|
stage: "search_staged"
|
|
15141
16007
|
});
|
|
16008
|
+
advanceAnalyticsRevision(db);
|
|
15142
16009
|
failureStage = "commit";
|
|
15143
16010
|
return result;
|
|
15144
16011
|
}).immediate();
|
|
@@ -15554,9 +16421,187 @@ function materializeBookmarkViews(bookmarks, options) {
|
|
|
15554
16421
|
});
|
|
15555
16422
|
return views.sort(compareBookmarkViews);
|
|
15556
16423
|
}
|
|
16424
|
+
var COST_ABSOLUTE_TOLERANCE = 1e-8;
|
|
16425
|
+
var COST_RELATIVE_TOLERANCE = 1e-6;
|
|
16426
|
+
function costFactKey(agentName, sessionId) {
|
|
16427
|
+
return getSessionRouteKey(agentName, sessionId);
|
|
16428
|
+
}
|
|
16429
|
+
function indexFacts(facts) {
|
|
16430
|
+
const messagesBySession = /* @__PURE__ */ new Map();
|
|
16431
|
+
const summariesBySession = /* @__PURE__ */ new Map();
|
|
16432
|
+
if (!facts) return { messagesBySession, summariesBySession };
|
|
16433
|
+
for (const summary of facts.sessions) {
|
|
16434
|
+
summariesBySession.set(
|
|
16435
|
+
costFactKey(summary.reference.agentName, summary.reference.sessionId),
|
|
16436
|
+
summary
|
|
16437
|
+
);
|
|
16438
|
+
}
|
|
16439
|
+
for (const message of facts.messages) {
|
|
16440
|
+
const key = costFactKey(message.reference.agentName, message.reference.sessionId);
|
|
16441
|
+
const messages = messagesBySession.get(key);
|
|
16442
|
+
if (messages) messages.push(message);
|
|
16443
|
+
else messagesBySession.set(key, [message]);
|
|
16444
|
+
}
|
|
16445
|
+
return { messagesBySession, summariesBySession };
|
|
16446
|
+
}
|
|
16447
|
+
function nonNegative22(value) {
|
|
16448
|
+
return Math.max(0, Number(value ?? 0));
|
|
16449
|
+
}
|
|
16450
|
+
function costsReconcile(left, right) {
|
|
16451
|
+
const tolerance = Math.max(
|
|
16452
|
+
COST_ABSOLUTE_TOLERANCE,
|
|
16453
|
+
Math.max(Math.abs(left), Math.abs(right)) * COST_RELATIVE_TOLERANCE
|
|
16454
|
+
);
|
|
16455
|
+
return Math.abs(left - right) <= tolerance;
|
|
16456
|
+
}
|
|
16457
|
+
function hasDetailedCost(session, summary) {
|
|
16458
|
+
const totalCost = Math.max(0, session.stats.total_cost);
|
|
16459
|
+
return totalCost > 0 && summary != null && summary.untimedMessageCost <= COST_ABSOLUTE_TOLERANCE && costsReconcile(summary.messageCost, totalCost);
|
|
16460
|
+
}
|
|
16461
|
+
function hasDetailedMessages(session, summary) {
|
|
16462
|
+
return summary != null && summary.untimedMessageCount === 0 && summary.messageCount === nonNegative22(session.stats.message_count);
|
|
16463
|
+
}
|
|
16464
|
+
function detailedOutputIncludesReasoning(session, summary) {
|
|
16465
|
+
if (!summary) return null;
|
|
16466
|
+
const stats = session.stats;
|
|
16467
|
+
const sessionInput = nonNegative22(stats.total_input_tokens);
|
|
16468
|
+
const sessionOutput = nonNegative22(stats.total_output_tokens);
|
|
16469
|
+
const sessionCacheRead = nonNegative22(stats.total_cache_read_tokens);
|
|
16470
|
+
const sessionCacheCreate = nonNegative22(stats.total_cache_create_tokens);
|
|
16471
|
+
const sessionTotal = nonNegative22(stats.total_tokens ?? sessionInput + sessionOutput);
|
|
16472
|
+
const outputIncludesReasoning = summary.outputTokens + summary.reasoningTokens;
|
|
16473
|
+
const outputMatches = summary.outputTokens === sessionOutput;
|
|
16474
|
+
const outputWithReasoningMatches = outputIncludesReasoning === sessionOutput;
|
|
16475
|
+
const effectiveOutput = outputMatches ? summary.outputTokens : outputWithReasoningMatches ? outputIncludesReasoning : null;
|
|
16476
|
+
if (effectiveOutput == null || summary.inputTokens !== sessionInput || summary.cacheReadTokens !== sessionCacheRead || summary.cacheCreateTokens !== sessionCacheCreate || summary.inputTokens + effectiveOutput !== sessionTotal || summary.untimedInputTokens > 0 || summary.untimedOutputTokens > 0 || summary.untimedReasoningTokens > 0 || summary.untimedCacheReadTokens > 0 || summary.untimedCacheCreateTokens > 0) {
|
|
16477
|
+
return null;
|
|
16478
|
+
}
|
|
16479
|
+
return !outputMatches && outputWithReasoningMatches;
|
|
16480
|
+
}
|
|
16481
|
+
function isInWindow(time, from, to) {
|
|
16482
|
+
return (from == null || time >= from) && time <= to;
|
|
16483
|
+
}
|
|
16484
|
+
function resolvedCostSource(session, source) {
|
|
16485
|
+
return source ?? session.stats.cost_source ?? "recorded";
|
|
16486
|
+
}
|
|
16487
|
+
function fallbackModelCosts(summary, totalCost) {
|
|
16488
|
+
if (!summary) return [];
|
|
16489
|
+
const modelTotal = summary.modelCosts.reduce((sum, model) => sum + model.cost, 0);
|
|
16490
|
+
return modelTotal > totalCost && !costsReconcile(modelTotal, totalCost) ? [] : summary.modelCosts;
|
|
16491
|
+
}
|
|
16492
|
+
function visitAttributedCosts(tree, options, visit) {
|
|
16493
|
+
const factIndex = indexFacts(options.facts);
|
|
16494
|
+
const factsAvailable = options.facts != null;
|
|
16495
|
+
for (const entry of tree.entries) {
|
|
16496
|
+
if (options.matchesEntry && !options.matchesEntry(entry.session)) continue;
|
|
16497
|
+
const fallbackTime = entry.session.time_updated ?? entry.session.time_created;
|
|
16498
|
+
const pending2 = [entry];
|
|
16499
|
+
while (pending2.length > 0) {
|
|
16500
|
+
const node = pending2.pop();
|
|
16501
|
+
for (const child of node.children) pending2.push(child);
|
|
16502
|
+
const session = node.session;
|
|
16503
|
+
const totalCost = Math.max(0, session.stats.total_cost);
|
|
16504
|
+
if (totalCost <= 0) continue;
|
|
16505
|
+
const agentName = getSessionAgentKey(session);
|
|
16506
|
+
const key = costFactKey(agentName, session.id);
|
|
16507
|
+
const summary = factIndex.summariesBySession.get(key);
|
|
16508
|
+
if (factsAvailable && hasDetailedCost(session, summary)) {
|
|
16509
|
+
for (const message of factIndex.messagesBySession.get(key) ?? []) {
|
|
16510
|
+
if (!isInWindow(message.time, options.from, options.to) || message.cost <= 0) continue;
|
|
16511
|
+
const source = resolvedCostSource(session, message.costSource);
|
|
16512
|
+
visit({
|
|
16513
|
+
entry,
|
|
16514
|
+
time: message.time,
|
|
16515
|
+
cost: message.cost,
|
|
16516
|
+
source,
|
|
16517
|
+
modelCosts: message.model ? [
|
|
16518
|
+
{
|
|
16519
|
+
model: message.model,
|
|
16520
|
+
cost: message.cost,
|
|
16521
|
+
costRecorded: source === "recorded" ? message.cost : 0
|
|
16522
|
+
}
|
|
16523
|
+
] : []
|
|
16524
|
+
});
|
|
16525
|
+
}
|
|
16526
|
+
continue;
|
|
16527
|
+
}
|
|
16528
|
+
if (!isInWindow(fallbackTime, options.from, options.to)) continue;
|
|
16529
|
+
visit({
|
|
16530
|
+
entry,
|
|
16531
|
+
time: fallbackTime,
|
|
16532
|
+
cost: totalCost,
|
|
16533
|
+
source: resolvedCostSource(session),
|
|
16534
|
+
modelCosts: factsAvailable ? fallbackModelCosts(summary, totalCost) : []
|
|
16535
|
+
});
|
|
16536
|
+
}
|
|
16537
|
+
}
|
|
16538
|
+
}
|
|
16539
|
+
function visitAttributedUsage(tree, options, visit) {
|
|
16540
|
+
const factIndex = indexFacts(options.facts);
|
|
16541
|
+
const factsAvailable = options.facts != null;
|
|
16542
|
+
for (const entry of tree.entries) {
|
|
16543
|
+
if (options.matchesEntry && !options.matchesEntry(entry.session)) continue;
|
|
16544
|
+
const fallbackTime = entry.session.time_updated ?? entry.session.time_created;
|
|
16545
|
+
const pending2 = [entry];
|
|
16546
|
+
while (pending2.length > 0) {
|
|
16547
|
+
const node = pending2.pop();
|
|
16548
|
+
for (const child of node.children) pending2.push(child);
|
|
16549
|
+
const session = node.session;
|
|
16550
|
+
const key = costFactKey(getSessionAgentKey(session), session.id);
|
|
16551
|
+
const summary = factIndex.summariesBySession.get(key);
|
|
16552
|
+
const detailedMessages = factsAvailable && hasDetailedMessages(session, summary);
|
|
16553
|
+
const outputIncludesReasoning = factsAvailable ? detailedOutputIncludesReasoning(session, summary) : null;
|
|
16554
|
+
const detailedTokens = outputIncludesReasoning != null;
|
|
16555
|
+
if (detailedMessages || detailedTokens) {
|
|
16556
|
+
for (const message of factIndex.messagesBySession.get(key) ?? []) {
|
|
16557
|
+
if (!isInWindow(message.time, options.from, options.to)) continue;
|
|
16558
|
+
const outputTokens = detailedTokens ? message.outputTokens + (outputIncludesReasoning ? message.reasoningTokens : 0) : 0;
|
|
16559
|
+
visit({
|
|
16560
|
+
entry,
|
|
16561
|
+
time: message.time,
|
|
16562
|
+
messages: detailedMessages ? 1 : 0,
|
|
16563
|
+
totalTokens: detailedTokens ? message.inputTokens + outputTokens : 0,
|
|
16564
|
+
inputTokens: detailedTokens ? message.inputTokens : 0,
|
|
16565
|
+
outputTokens,
|
|
16566
|
+
cacheReadTokens: detailedTokens ? message.cacheReadTokens : 0,
|
|
16567
|
+
cacheCreateTokens: detailedTokens ? message.cacheCreateTokens : 0
|
|
16568
|
+
});
|
|
16569
|
+
}
|
|
16570
|
+
}
|
|
16571
|
+
if (!isInWindow(fallbackTime, options.from, options.to)) continue;
|
|
16572
|
+
if (!detailedMessages) {
|
|
16573
|
+
visit({
|
|
16574
|
+
entry,
|
|
16575
|
+
time: fallbackTime,
|
|
16576
|
+
messages: nonNegative22(session.stats.message_count),
|
|
16577
|
+
totalTokens: 0,
|
|
16578
|
+
inputTokens: 0,
|
|
16579
|
+
outputTokens: 0,
|
|
16580
|
+
cacheReadTokens: 0,
|
|
16581
|
+
cacheCreateTokens: 0
|
|
16582
|
+
});
|
|
16583
|
+
}
|
|
16584
|
+
if (!detailedTokens) {
|
|
16585
|
+
const inputTokens = nonNegative22(session.stats.total_input_tokens);
|
|
16586
|
+
const outputTokens = nonNegative22(session.stats.total_output_tokens);
|
|
16587
|
+
visit({
|
|
16588
|
+
entry,
|
|
16589
|
+
time: fallbackTime,
|
|
16590
|
+
messages: 0,
|
|
16591
|
+
totalTokens: nonNegative22(session.stats.total_tokens ?? inputTokens + outputTokens),
|
|
16592
|
+
inputTokens,
|
|
16593
|
+
outputTokens,
|
|
16594
|
+
cacheReadTokens: nonNegative22(session.stats.total_cache_read_tokens),
|
|
16595
|
+
cacheCreateTokens: nonNegative22(session.stats.total_cache_create_tokens)
|
|
16596
|
+
});
|
|
16597
|
+
}
|
|
16598
|
+
}
|
|
16599
|
+
}
|
|
16600
|
+
}
|
|
15557
16601
|
var DASHBOARD_RECENT_LIMIT = 10;
|
|
15558
16602
|
var DASHBOARD_PROJECT_LIMIT = 12;
|
|
15559
16603
|
var PROJECT_SPARKLINE_DAYS = 14;
|
|
16604
|
+
var MODEL_COST_LIMIT = 20;
|
|
15560
16605
|
function getSessionAgentName(session) {
|
|
15561
16606
|
return getSessionAgentKey(session);
|
|
15562
16607
|
}
|
|
@@ -15611,7 +16656,9 @@ function createAccumulator(byAgentNames, scope, to) {
|
|
|
15611
16656
|
agentKeys: /* @__PURE__ */ new Set(),
|
|
15612
16657
|
daily: /* @__PURE__ */ new Map(),
|
|
15613
16658
|
models: /* @__PURE__ */ new Map(),
|
|
16659
|
+
modelCosts: /* @__PURE__ */ new Map(),
|
|
15614
16660
|
projects: /* @__PURE__ */ new Map(),
|
|
16661
|
+
projectKeys: /* @__PURE__ */ new Set(),
|
|
15615
16662
|
sparklineSlots,
|
|
15616
16663
|
recent: []
|
|
15617
16664
|
};
|
|
@@ -15624,9 +16671,9 @@ function foldModelUsage(node, into) {
|
|
|
15624
16671
|
}
|
|
15625
16672
|
for (const child of node.children) foldModelUsage(child, into);
|
|
15626
16673
|
}
|
|
15627
|
-
function
|
|
15628
|
-
const identity =
|
|
15629
|
-
if (!identity) return;
|
|
16674
|
+
function getOrCreateProject(session, acc) {
|
|
16675
|
+
const identity = session.project_identity;
|
|
16676
|
+
if (!identity) return void 0;
|
|
15630
16677
|
const key = getProjectIdentityKey(identity);
|
|
15631
16678
|
let project = acc.projects.get(key);
|
|
15632
16679
|
if (!project) {
|
|
@@ -15644,36 +16691,25 @@ function trackProject(node, acc, dayKey, agentKey) {
|
|
|
15644
16691
|
};
|
|
15645
16692
|
acc.projects.set(key, project);
|
|
15646
16693
|
}
|
|
15647
|
-
|
|
16694
|
+
return project;
|
|
16695
|
+
}
|
|
16696
|
+
function trackProjectActivity(node, acc, agentKey) {
|
|
16697
|
+
const identity = node.session.project_identity;
|
|
16698
|
+
const project = getOrCreateProject(node.session, acc);
|
|
16699
|
+
if (!identity || !project) return;
|
|
15648
16700
|
project.sessions += 1;
|
|
15649
|
-
project.messages += stats.messageCount;
|
|
15650
|
-
project.tokens += stats.totalTokens;
|
|
15651
|
-
project.cost += stats.cost;
|
|
15652
|
-
if (stats.costSource === "estimated") project.hasEstimatedCost = true;
|
|
15653
16701
|
project.agentSessions.set(agentKey, (project.agentSessions.get(agentKey) ?? 0) + 1);
|
|
15654
|
-
|
|
15655
|
-
if (slot != null) project.sparkline[slot] += stats.cost;
|
|
16702
|
+
acc.projectKeys.add(getProjectIdentityKey(identity));
|
|
15656
16703
|
}
|
|
15657
|
-
function
|
|
16704
|
+
function accumulateActivity(node, acc) {
|
|
15658
16705
|
const session = node.session;
|
|
15659
|
-
const stats = node.inclusiveStats;
|
|
15660
16706
|
const activity = getSessionActivityTime(session);
|
|
15661
16707
|
const agentKey = getSessionAgentName(session);
|
|
15662
16708
|
acc.sessions += 1;
|
|
15663
|
-
acc.messages += stats.messageCount;
|
|
15664
|
-
acc.tokens += stats.totalTokens;
|
|
15665
|
-
acc.cost += stats.cost;
|
|
15666
|
-
acc.cacheReadTokens += stats.cacheReadTokens;
|
|
15667
16709
|
acc.agentKeys.add(agentKey);
|
|
15668
|
-
if (stats.costSource === "recorded") acc.costRecorded += stats.cost;
|
|
15669
|
-
else acc.costEstimated += stats.cost;
|
|
15670
|
-
if (stats.costSource === "estimated") acc.hasEstimatedCost = true;
|
|
15671
16710
|
const metric = acc.agents.get(agentKey);
|
|
15672
16711
|
if (metric) {
|
|
15673
16712
|
metric.sessions += 1;
|
|
15674
|
-
metric.messages += stats.messageCount;
|
|
15675
|
-
metric.tokens += stats.totalTokens;
|
|
15676
|
-
metric.cost += stats.cost;
|
|
15677
16713
|
}
|
|
15678
16714
|
const dayKey = toCalendarDayKey(activity);
|
|
15679
16715
|
let bucket = acc.daily.get(dayKey);
|
|
@@ -15682,12 +16718,6 @@ function accumulate(node, acc) {
|
|
|
15682
16718
|
acc.daily.set(dayKey, bucket);
|
|
15683
16719
|
}
|
|
15684
16720
|
bucket.sessions += 1;
|
|
15685
|
-
bucket.messages += stats.messageCount;
|
|
15686
|
-
bucket.cost += stats.cost;
|
|
15687
|
-
bucket.input += Math.max(0, stats.inputTokens - stats.cacheReadTokens - stats.cacheCreateTokens);
|
|
15688
|
-
bucket.output += stats.outputTokens;
|
|
15689
|
-
bucket.cache_read += stats.cacheReadTokens;
|
|
15690
|
-
bucket.cache_create += stats.cacheCreateTokens;
|
|
15691
16721
|
const usage = /* @__PURE__ */ new Map();
|
|
15692
16722
|
foldModelUsage(node, usage);
|
|
15693
16723
|
for (const [model, tokens] of usage) {
|
|
@@ -15699,7 +16729,7 @@ function accumulate(node, acc) {
|
|
|
15699
16729
|
acc.models.set(model, { tokens, sessions: 1 });
|
|
15700
16730
|
}
|
|
15701
16731
|
}
|
|
15702
|
-
|
|
16732
|
+
trackProjectActivity(node, acc, agentKey);
|
|
15703
16733
|
let recentIndex = acc.recent.length;
|
|
15704
16734
|
for (let i = 0; i < acc.recent.length; i += 1) {
|
|
15705
16735
|
if (activity > acc.recent[i].activity) {
|
|
@@ -15712,6 +16742,103 @@ function accumulate(node, acc) {
|
|
|
15712
16742
|
if (acc.recent.length > DASHBOARD_RECENT_LIMIT) acc.recent.pop();
|
|
15713
16743
|
}
|
|
15714
16744
|
}
|
|
16745
|
+
function addUsage(acc, entry, time, usage) {
|
|
16746
|
+
const { messages, totalTokens, inputTokens, outputTokens, cacheReadTokens, cacheCreateTokens } = usage;
|
|
16747
|
+
if (messages <= 0 && totalTokens <= 0 && inputTokens <= 0 && outputTokens <= 0 && cacheReadTokens <= 0 && cacheCreateTokens <= 0) {
|
|
16748
|
+
return;
|
|
16749
|
+
}
|
|
16750
|
+
acc.messages += messages;
|
|
16751
|
+
acc.tokens += totalTokens;
|
|
16752
|
+
acc.cacheReadTokens += cacheReadTokens;
|
|
16753
|
+
const agentKey = getSessionAgentName(entry.session);
|
|
16754
|
+
let metric = acc.agents.get(agentKey);
|
|
16755
|
+
if (!metric) {
|
|
16756
|
+
metric = { name: agentKey, sessions: 0, messages: 0, tokens: 0, cost: 0 };
|
|
16757
|
+
acc.agents.set(agentKey, metric);
|
|
16758
|
+
}
|
|
16759
|
+
metric.messages += messages;
|
|
16760
|
+
metric.tokens += totalTokens;
|
|
16761
|
+
const dayKey = toCalendarDayKey(time);
|
|
16762
|
+
let bucket = acc.daily.get(dayKey);
|
|
16763
|
+
if (!bucket) {
|
|
16764
|
+
bucket = emptyDailyBucket(dayKey);
|
|
16765
|
+
acc.daily.set(dayKey, bucket);
|
|
16766
|
+
}
|
|
16767
|
+
bucket.messages += messages;
|
|
16768
|
+
bucket.input += Math.max(0, inputTokens - cacheReadTokens - cacheCreateTokens);
|
|
16769
|
+
bucket.output += outputTokens;
|
|
16770
|
+
bucket.cache_read += cacheReadTokens;
|
|
16771
|
+
bucket.cache_create += cacheCreateTokens;
|
|
16772
|
+
const project = getOrCreateProject(entry.session, acc);
|
|
16773
|
+
if (!project) return;
|
|
16774
|
+
project.messages += messages;
|
|
16775
|
+
project.tokens += totalTokens;
|
|
16776
|
+
}
|
|
16777
|
+
function accumulateUsage(tree, scope, acc, from, to, costFacts) {
|
|
16778
|
+
visitAttributedUsage(
|
|
16779
|
+
tree,
|
|
16780
|
+
{ from, to, facts: costFacts, matchesEntry: (session) => matchesScope(session, scope) },
|
|
16781
|
+
({ entry, time, ...usage }) => addUsage(acc, entry, time, usage)
|
|
16782
|
+
);
|
|
16783
|
+
}
|
|
16784
|
+
function addModelCost(acc, model, cost, recordedCost) {
|
|
16785
|
+
if (!model || cost <= 0) return;
|
|
16786
|
+
const recorded = Math.max(0, Math.min(cost, recordedCost));
|
|
16787
|
+
const current = acc.modelCosts.get(model);
|
|
16788
|
+
if (current) {
|
|
16789
|
+
current.cost += cost;
|
|
16790
|
+
current.costRecorded += recorded;
|
|
16791
|
+
current.costEstimated += cost - recorded;
|
|
16792
|
+
return;
|
|
16793
|
+
}
|
|
16794
|
+
acc.modelCosts.set(model, {
|
|
16795
|
+
model,
|
|
16796
|
+
cost,
|
|
16797
|
+
costRecorded: recorded,
|
|
16798
|
+
costEstimated: cost - recorded
|
|
16799
|
+
});
|
|
16800
|
+
}
|
|
16801
|
+
function addCost(acc, entry, time, cost, source) {
|
|
16802
|
+
if (cost <= 0) return;
|
|
16803
|
+
acc.cost += cost;
|
|
16804
|
+
if (source === "recorded") acc.costRecorded += cost;
|
|
16805
|
+
else {
|
|
16806
|
+
acc.costEstimated += cost;
|
|
16807
|
+
acc.hasEstimatedCost = true;
|
|
16808
|
+
}
|
|
16809
|
+
const agentKey = getSessionAgentName(entry.session);
|
|
16810
|
+
let metric = acc.agents.get(agentKey);
|
|
16811
|
+
if (!metric) {
|
|
16812
|
+
metric = { name: agentKey, sessions: 0, messages: 0, tokens: 0, cost: 0 };
|
|
16813
|
+
acc.agents.set(agentKey, metric);
|
|
16814
|
+
}
|
|
16815
|
+
metric.cost += cost;
|
|
16816
|
+
const dayKey = toCalendarDayKey(time);
|
|
16817
|
+
let bucket = acc.daily.get(dayKey);
|
|
16818
|
+
if (!bucket) {
|
|
16819
|
+
bucket = emptyDailyBucket(dayKey);
|
|
16820
|
+
acc.daily.set(dayKey, bucket);
|
|
16821
|
+
}
|
|
16822
|
+
bucket.cost += cost;
|
|
16823
|
+
const project = getOrCreateProject(entry.session, acc);
|
|
16824
|
+
if (!project) return;
|
|
16825
|
+
project.cost += cost;
|
|
16826
|
+
if (source === "estimated") project.hasEstimatedCost = true;
|
|
16827
|
+
const slot = acc.sparklineSlots.get(dayKey);
|
|
16828
|
+
if (slot != null) project.sparkline[slot] += cost;
|
|
16829
|
+
}
|
|
16830
|
+
function accumulateCosts(tree, scope, acc, from, to, costFacts) {
|
|
16831
|
+
visitAttributedCosts(
|
|
16832
|
+
tree,
|
|
16833
|
+
{ from, to, facts: costFacts, matchesEntry: (session) => matchesScope(session, scope) },
|
|
16834
|
+
({ entry, time, cost, source, modelCosts }) => {
|
|
16835
|
+
addCost(acc, entry, time, cost, source);
|
|
16836
|
+
for (const model of modelCosts) {
|
|
16837
|
+
addModelCost(acc, model.model, model.cost, model.costRecorded);
|
|
16838
|
+
}
|
|
16839
|
+
}
|
|
16840
|
+
);
|
|
16841
|
+
}
|
|
15715
16842
|
function toPreviousTotals(acc) {
|
|
15716
16843
|
return { sessions: acc.sessions, messages: acc.messages, tokens: acc.tokens, cost: acc.cost };
|
|
15717
16844
|
}
|
|
@@ -15730,8 +16857,9 @@ function toProjectStat(project) {
|
|
|
15730
16857
|
};
|
|
15731
16858
|
}
|
|
15732
16859
|
function buildDashboard(sessions, options) {
|
|
15733
|
-
const { byAgentNames, scope, from, to, agentInfoMap, compare } = options;
|
|
16860
|
+
const { byAgentNames, scope, from, to, agentInfoMap, compare, costFacts } = options;
|
|
15734
16861
|
const tree = buildSessionTree(sessions);
|
|
16862
|
+
const costFactsAvailable = costFacts != null;
|
|
15735
16863
|
const acc = createAccumulator(byAgentNames, scope, to);
|
|
15736
16864
|
if (from != null) {
|
|
15737
16865
|
const bucketDays = countCalendarDays(from, to);
|
|
@@ -15740,13 +16868,17 @@ function buildDashboard(sessions, options) {
|
|
|
15740
16868
|
acc.daily.set(key, emptyDailyBucket(key));
|
|
15741
16869
|
}
|
|
15742
16870
|
}
|
|
15743
|
-
for (const node of scopedEntries(tree, scope, from, to))
|
|
16871
|
+
for (const node of scopedEntries(tree, scope, from, to)) accumulateActivity(node, acc);
|
|
16872
|
+
accumulateUsage(tree, scope, acc, from, to, costFacts);
|
|
16873
|
+
accumulateCosts(tree, scope, acc, from, to, costFacts);
|
|
15744
16874
|
let previous;
|
|
15745
16875
|
if (compare) {
|
|
15746
16876
|
const compareAcc = createAccumulator(byAgentNames, scope, compare.to);
|
|
15747
16877
|
for (const node of scopedEntries(tree, scope, compare.from, compare.to)) {
|
|
15748
|
-
|
|
16878
|
+
accumulateActivity(node, compareAcc);
|
|
15749
16879
|
}
|
|
16880
|
+
accumulateUsage(tree, scope, compareAcc, compare.from, compare.to, costFacts);
|
|
16881
|
+
accumulateCosts(tree, scope, compareAcc, compare.from, compare.to, costFacts);
|
|
15750
16882
|
previous = toPreviousTotals(compareAcc);
|
|
15751
16883
|
}
|
|
15752
16884
|
const perAgent = [...acc.agents.values()].map((metrics) => {
|
|
@@ -15761,9 +16893,10 @@ function buildDashboard(sessions, options) {
|
|
|
15761
16893
|
tokens: metrics.tokens,
|
|
15762
16894
|
cost: metrics.cost
|
|
15763
16895
|
};
|
|
15764
|
-
}).filter((item) => item.sessions > 0).sort((a, b) => b.sessions - a.sessions);
|
|
16896
|
+
}).filter((item) => item.sessions > 0 || item.messages > 0 || item.tokens > 0 || item.cost > 0).sort((a, b) => b.sessions - a.sessions || b.cost - a.cost);
|
|
15765
16897
|
const dailyActivity = [...acc.daily.values()].sort((a, b) => a.date.localeCompare(b.date));
|
|
15766
16898
|
const modelDistribution = [...acc.models.entries()].map(([model, { tokens, sessions: count }]) => ({ model, tokens, sessions: count })).sort((a, b) => b.tokens - a.tokens);
|
|
16899
|
+
const modelCost = costFactsAvailable ? [...acc.modelCosts.values()].sort((a, b) => b.cost - a.cost).slice(0, MODEL_COST_LIMIT) : null;
|
|
15767
16900
|
const rankedProjects = [...acc.projects.values()].sort((a, b) => b.cost - a.cost);
|
|
15768
16901
|
const perProject = rankedProjects.slice(0, DASHBOARD_PROJECT_LIMIT).map(toProjectStat);
|
|
15769
16902
|
const projectRollup = rankedProjects.slice(DASHBOARD_PROJECT_LIMIT).reduce(
|
|
@@ -15795,10 +16928,11 @@ function buildDashboard(sessions, options) {
|
|
|
15795
16928
|
latestActivityAgent: latest ? getSessionAgentName(latest.session) : void 0,
|
|
15796
16929
|
previous
|
|
15797
16930
|
},
|
|
15798
|
-
scopeCounts: { projects: acc.
|
|
16931
|
+
scopeCounts: { projects: acc.projectKeys.size, agents: acc.agentKeys.size },
|
|
15799
16932
|
perAgent,
|
|
15800
16933
|
dailyActivity,
|
|
15801
16934
|
modelDistribution,
|
|
16935
|
+
modelCost,
|
|
15802
16936
|
perProject,
|
|
15803
16937
|
projectRollup,
|
|
15804
16938
|
recentSessions
|
|
@@ -15815,15 +16949,20 @@ function emptyMetrics() {
|
|
|
15815
16949
|
};
|
|
15816
16950
|
}
|
|
15817
16951
|
function attachProjectMetrics(projects, sessions) {
|
|
15818
|
-
|
|
16952
|
+
const tree = buildSessionTree(sessions);
|
|
16953
|
+
return attachProjectMetricsToEntries(projects, tree, tree.entries);
|
|
15819
16954
|
}
|
|
15820
|
-
function attachProjectMetricsFromTree(projects, tree, from, to) {
|
|
16955
|
+
function attachProjectMetricsFromTree(projects, tree, from, to, costFacts) {
|
|
15821
16956
|
return attachProjectMetricsToEntries(
|
|
15822
16957
|
projects,
|
|
15823
|
-
|
|
16958
|
+
tree,
|
|
16959
|
+
filterSessionTreeEntriesByActivityWindow(tree, from, to),
|
|
16960
|
+
from,
|
|
16961
|
+
to,
|
|
16962
|
+
costFacts
|
|
15824
16963
|
);
|
|
15825
16964
|
}
|
|
15826
|
-
function attachProjectMetricsToEntries(projects, entries) {
|
|
16965
|
+
function attachProjectMetricsToEntries(projects, tree, entries, from, to, costFacts) {
|
|
15827
16966
|
const metrics = /* @__PURE__ */ new Map();
|
|
15828
16967
|
for (const node of entries) {
|
|
15829
16968
|
const identity = node.session.project_identity;
|
|
@@ -15834,29 +16973,79 @@ function attachProjectMetricsToEntries(projects, entries) {
|
|
|
15834
16973
|
current = emptyMetrics();
|
|
15835
16974
|
metrics.set(key, current);
|
|
15836
16975
|
}
|
|
15837
|
-
const stats = node.inclusiveStats;
|
|
15838
16976
|
current.sessions += 1;
|
|
15839
|
-
current.messages += stats.messageCount;
|
|
15840
|
-
current.tokens += stats.totalTokens;
|
|
15841
|
-
current.cost += stats.cost;
|
|
15842
|
-
if (stats.costSource === "estimated") current.hasEstimatedCost = true;
|
|
15843
16977
|
const agentName = getSessionAgentName(node.session);
|
|
15844
16978
|
const agent = current.agentStats.get(agentName);
|
|
15845
16979
|
if (agent) {
|
|
15846
16980
|
agent.sessions += 1;
|
|
15847
|
-
agent.messages += stats.messageCount;
|
|
15848
|
-
agent.tokens += stats.totalTokens;
|
|
15849
|
-
agent.cost += stats.cost;
|
|
15850
16981
|
} else {
|
|
15851
16982
|
current.agentStats.set(agentName, {
|
|
15852
16983
|
name: agentName,
|
|
15853
16984
|
sessions: 1,
|
|
15854
|
-
messages:
|
|
15855
|
-
tokens:
|
|
15856
|
-
cost:
|
|
16985
|
+
messages: 0,
|
|
16986
|
+
tokens: 0,
|
|
16987
|
+
cost: 0
|
|
15857
16988
|
});
|
|
15858
16989
|
}
|
|
15859
16990
|
}
|
|
16991
|
+
visitAttributedUsage(
|
|
16992
|
+
tree,
|
|
16993
|
+
{ from, to: to ?? Number.POSITIVE_INFINITY, facts: costFacts },
|
|
16994
|
+
({ entry, messages, totalTokens }) => {
|
|
16995
|
+
const identity = entry.session.project_identity;
|
|
16996
|
+
if (!identity) return;
|
|
16997
|
+
const key = getProjectIdentityKey(identity);
|
|
16998
|
+
let current = metrics.get(key);
|
|
16999
|
+
if (!current) {
|
|
17000
|
+
current = emptyMetrics();
|
|
17001
|
+
metrics.set(key, current);
|
|
17002
|
+
}
|
|
17003
|
+
current.messages += messages;
|
|
17004
|
+
current.tokens += totalTokens;
|
|
17005
|
+
const agentName = getSessionAgentName(entry.session);
|
|
17006
|
+
const agent = current.agentStats.get(agentName);
|
|
17007
|
+
if (agent) {
|
|
17008
|
+
agent.messages += messages;
|
|
17009
|
+
agent.tokens += totalTokens;
|
|
17010
|
+
} else {
|
|
17011
|
+
current.agentStats.set(agentName, {
|
|
17012
|
+
name: agentName,
|
|
17013
|
+
sessions: 0,
|
|
17014
|
+
messages,
|
|
17015
|
+
tokens: totalTokens,
|
|
17016
|
+
cost: 0
|
|
17017
|
+
});
|
|
17018
|
+
}
|
|
17019
|
+
}
|
|
17020
|
+
);
|
|
17021
|
+
visitAttributedCosts(
|
|
17022
|
+
tree,
|
|
17023
|
+
{ from, to: to ?? Number.POSITIVE_INFINITY, facts: costFacts },
|
|
17024
|
+
({ entry, cost, source }) => {
|
|
17025
|
+
const identity = entry.session.project_identity;
|
|
17026
|
+
if (!identity) return;
|
|
17027
|
+
const key = getProjectIdentityKey(identity);
|
|
17028
|
+
let current = metrics.get(key);
|
|
17029
|
+
if (!current) {
|
|
17030
|
+
current = emptyMetrics();
|
|
17031
|
+
metrics.set(key, current);
|
|
17032
|
+
}
|
|
17033
|
+
current.cost += cost;
|
|
17034
|
+
if (source === "estimated") current.hasEstimatedCost = true;
|
|
17035
|
+
const agentName = getSessionAgentName(entry.session);
|
|
17036
|
+
const agent = current.agentStats.get(agentName);
|
|
17037
|
+
if (agent) agent.cost += cost;
|
|
17038
|
+
else {
|
|
17039
|
+
current.agentStats.set(agentName, {
|
|
17040
|
+
name: agentName,
|
|
17041
|
+
sessions: 0,
|
|
17042
|
+
messages: 0,
|
|
17043
|
+
tokens: 0,
|
|
17044
|
+
cost
|
|
17045
|
+
});
|
|
17046
|
+
}
|
|
17047
|
+
}
|
|
17048
|
+
);
|
|
15860
17049
|
return projects.map((project) => {
|
|
15861
17050
|
const metric = metrics.get(
|
|
15862
17051
|
getProjectIdentityKey({ kind: project.identityKind, key: project.identityKey })
|
|
@@ -15872,10 +17061,10 @@ function attachProjectMetricsToEntries(projects, entries) {
|
|
|
15872
17061
|
};
|
|
15873
17062
|
});
|
|
15874
17063
|
}
|
|
15875
|
-
function executeSessionSearch(query, options, snapshot) {
|
|
17064
|
+
function executeSessionSearch(query, options, snapshot, context = {}) {
|
|
15876
17065
|
const merged = mergeSearchQueryOptions(query, options);
|
|
15877
17066
|
if (!needsIndexedSearch(merged.text, merged.options)) {
|
|
15878
|
-
return searchRecentSessions(snapshot, merged.options);
|
|
17067
|
+
return searchRecentSessions(snapshot, merged.options, context);
|
|
15879
17068
|
}
|
|
15880
17069
|
return searchIndexedSessions(query, merged.text, merged.parsed, merged.options);
|
|
15881
17070
|
}
|
|
@@ -15917,9 +17106,10 @@ function matchesSessionSearchFilters(agentName, session, options, projectScope =
|
|
|
15917
17106
|
function sessionReferenceKey2(agentName, sessionId) {
|
|
15918
17107
|
return `${agentName}\0${sessionId}`;
|
|
15919
17108
|
}
|
|
15920
|
-
function filterSessionSearchCandidates(candidates, options,
|
|
15921
|
-
const projectScope = options.
|
|
15922
|
-
const
|
|
17109
|
+
function filterSessionSearchCandidates(candidates, options, context = {}) {
|
|
17110
|
+
const projectScope = options.projectScope ?? null;
|
|
17111
|
+
const sessionSnapshot = context.sessionSnapshot ?? candidates.map((candidate) => candidate.session);
|
|
17112
|
+
const inclusiveCosts = buildInclusiveCostLookup(sessionSnapshot, options, context.sessionTree);
|
|
15923
17113
|
const headMatches = candidates.filter(
|
|
15924
17114
|
(candidate) => matchesSessionSearchFilters(
|
|
15925
17115
|
candidate.reference.agentName,
|
|
@@ -15947,11 +17137,11 @@ function filterSessionSearchCandidates(candidates, options, sessionSnapshot = ca
|
|
|
15947
17137
|
)
|
|
15948
17138
|
);
|
|
15949
17139
|
}
|
|
15950
|
-
function searchRecentSessions(snapshot, options) {
|
|
17140
|
+
function searchRecentSessions(snapshot, options, context) {
|
|
15951
17141
|
const limit = Math.max(0, Math.trunc(options.limit ?? 50));
|
|
15952
17142
|
if (limit === 0) return [];
|
|
15953
|
-
const projectScope = options.
|
|
15954
|
-
const inclusiveCosts = buildInclusiveCostLookup(snapshot.sessions, options);
|
|
17143
|
+
const projectScope = options.projectScope ?? null;
|
|
17144
|
+
const inclusiveCosts = buildInclusiveCostLookup(snapshot.sessions, options, context.sessionTree);
|
|
15955
17145
|
const sessions = options.agent ? snapshot.byAgent[options.agent] ?? [] : snapshot.sessions;
|
|
15956
17146
|
const results = [];
|
|
15957
17147
|
for (const session of sessions) {
|
|
@@ -15976,9 +17166,9 @@ function searchRecentSessions(snapshot, options) {
|
|
|
15976
17166
|
}
|
|
15977
17167
|
return results;
|
|
15978
17168
|
}
|
|
15979
|
-
function buildInclusiveCostLookup(sessions, options) {
|
|
17169
|
+
function buildInclusiveCostLookup(sessions, options, sessionTree) {
|
|
15980
17170
|
if (options.costMin == null && options.costMax == null) return null;
|
|
15981
|
-
return buildSessionTree(sessions).byRouteKey;
|
|
17171
|
+
return (sessionTree ?? buildSessionTree(sessions)).byRouteKey;
|
|
15982
17172
|
}
|
|
15983
17173
|
function inclusiveCostFor(agentName, session, lookup) {
|
|
15984
17174
|
return lookup?.get(getSessionRouteKey(agentName, session.id))?.inclusiveStats.cost ?? session.stats.total_cost;
|
|
@@ -16019,6 +17209,10 @@ function searchIndexedSessions(query, textQuery, parsed, options) {
|
|
|
16019
17209
|
}
|
|
16020
17210
|
|
|
16021
17211
|
export {
|
|
17212
|
+
normalizeSessionReference,
|
|
17213
|
+
formatSessionReference,
|
|
17214
|
+
getSessionAgentKey,
|
|
17215
|
+
sessionRoutePath,
|
|
16022
17216
|
registerAgent,
|
|
16023
17217
|
createRegisteredAgents,
|
|
16024
17218
|
getRegisteredAgents,
|
|
@@ -16033,9 +17227,7 @@ export {
|
|
|
16033
17227
|
hasPendingPricing,
|
|
16034
17228
|
refreshPricingCache,
|
|
16035
17229
|
PRICING_CAPTURE_EPOCH,
|
|
16036
|
-
createSessionSourceFailure,
|
|
16037
17230
|
reportSessionSourceOutcome,
|
|
16038
|
-
diffSessionSources,
|
|
16039
17231
|
synchronizeSessionSources,
|
|
16040
17232
|
BaseAgent,
|
|
16041
17233
|
FileSystemSessionSource,
|
|
@@ -16046,19 +17238,15 @@ export {
|
|
|
16046
17238
|
countCalendarDays,
|
|
16047
17239
|
isProjectIdentityKind,
|
|
16048
17240
|
matchesProjectIdentity,
|
|
16049
|
-
normalizeSessionReference,
|
|
16050
|
-
formatSessionReference,
|
|
16051
|
-
getSessionAgentKey,
|
|
16052
|
-
sessionRoutePath,
|
|
16053
17241
|
mergeSortedSessions,
|
|
16054
|
-
getSessionRouteKey,
|
|
16055
17242
|
createSessionIndex,
|
|
16056
|
-
isChildSession,
|
|
16057
|
-
getRootSessions,
|
|
16058
17243
|
buildSessionTree,
|
|
16059
17244
|
filterSessionTreeByActivityWindow,
|
|
16060
17245
|
createSessionProjectionContext,
|
|
16061
|
-
|
|
17246
|
+
PROJECT_IDENTITY_RESOLVER_REVISION,
|
|
17247
|
+
normalizeProjectDirectory,
|
|
17248
|
+
computeIdentityProjection,
|
|
17249
|
+
createProjectScopeMatcherFromIdentity,
|
|
16062
17250
|
matchesProjectScope,
|
|
16063
17251
|
SMART_TAG_CLASSIFIER_REVISION,
|
|
16064
17252
|
getSmartTagSourceTimestamp,
|
|
@@ -16066,23 +17254,23 @@ export {
|
|
|
16066
17254
|
WORKER_LOG_MESSAGE_TYPE,
|
|
16067
17255
|
isWorkerLogMessage,
|
|
16068
17256
|
closeCacheStorage,
|
|
16069
|
-
|
|
17257
|
+
getAnalyticsRevision,
|
|
16070
17258
|
sessionDetailVersion,
|
|
16071
17259
|
readPendingSearchIndexMaintenance,
|
|
16072
17260
|
syncSessionSearchIndex,
|
|
16073
17261
|
syncSessionSearchIndexChanges,
|
|
16074
17262
|
mergeSearchQueryOptions,
|
|
17263
|
+
getSearchProjectDirectory,
|
|
16075
17264
|
listFileActivity,
|
|
17265
|
+
readCachedSessions,
|
|
16076
17266
|
loadCachedSessions,
|
|
16077
17267
|
loadCachedSessionHeads,
|
|
16078
17268
|
readAgentCacheInitialization,
|
|
16079
|
-
isAgentCacheInitialized,
|
|
16080
17269
|
markAgentCacheInitialized,
|
|
16081
17270
|
markAgentFullSyncStarted,
|
|
16082
17271
|
getAgentFullSyncCursor,
|
|
16083
17272
|
markAgentFullSyncProgress,
|
|
16084
17273
|
readAgentLastFullSyncAt,
|
|
16085
|
-
getAgentLastFullSyncAt,
|
|
16086
17274
|
markAgentFullSyncCompleted,
|
|
16087
17275
|
saveCachedSessions,
|
|
16088
17276
|
saveCachedSessionChanges,
|
|
@@ -16096,6 +17284,7 @@ export {
|
|
|
16096
17284
|
scanSessions,
|
|
16097
17285
|
materializeSessionDetailResponse,
|
|
16098
17286
|
listCachedProjectGroups,
|
|
17287
|
+
listDashboardCostFacts,
|
|
16099
17288
|
listModelCostDistribution,
|
|
16100
17289
|
commitDurableSessionPublication,
|
|
16101
17290
|
StateStorageUnavailableError,
|
|
@@ -16107,7 +17296,6 @@ export {
|
|
|
16107
17296
|
listSessionAliases,
|
|
16108
17297
|
upsertSessionAlias,
|
|
16109
17298
|
deleteSessionAlias,
|
|
16110
|
-
compareBookmarkViews,
|
|
16111
17299
|
materializeBookmarkViews,
|
|
16112
17300
|
getSessionActivityTime,
|
|
16113
17301
|
buildDashboard,
|
|
@@ -16116,4 +17304,4 @@ export {
|
|
|
16116
17304
|
executeSessionSearch,
|
|
16117
17305
|
filterSessionSearchCandidates
|
|
16118
17306
|
};
|
|
16119
|
-
//# sourceMappingURL=chunk-
|
|
17307
|
+
//# sourceMappingURL=chunk-PSHZZITB.js.map
|