codesesh 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/{chunk-GCOAE7KI.js → chunk-VRVZJDNL.js} +1260 -1368
- package/dist/chunk-VRVZJDNL.js.map +1 -0
- package/dist/{dist-TZTL6VP4.js → dist-C3KC3XKJ.js} +25 -10
- package/dist/index.js +1772 -1241
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +1 -1
- package/dist/search-index-worker.js +1 -1
- package/dist/smart-tag-worker.js +1 -1
- package/dist/web/assets/index-BNLxu3RT.js +113 -0
- package/dist/web/assets/index-BYElqfX-.css +2 -0
- package/dist/web/assets/markdown-dR4kAewZ.js +14 -0
- package/dist/web/assets/react-3acXkZNw.js +1821 -0
- package/dist/web/assets/syntax-C_RLNYQn.js +6 -0
- package/dist/web/assets/vendor-Lx2mL9a6.js +40 -0
- package/dist/web/index.html +6 -6
- package/package.json +9 -8
- package/dist/chunk-GCOAE7KI.js.map +0 -1
- package/dist/web/assets/index-D05hsCzb.css +0 -2
- package/dist/web/assets/index-DOy8q3la.js +0 -113
- package/dist/web/assets/markdown-CGVJZBE7.js +0 -14
- package/dist/web/assets/react-DkEeEGji.js +0 -1821
- package/dist/web/assets/syntax-CFQ-7fm6.js +0 -6
- package/dist/web/assets/vendor-B2nLfkMs.js +0 -80
- /package/dist/{dist-TZTL6VP4.js.map → dist-C3KC3XKJ.js.map} +0 -0
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// ../core/dist/chunk-M5ISIPFR.mjs
|
|
4
|
+
function compareSessionActivityDesc(a, b) {
|
|
5
|
+
return (b.time_updated ?? b.time_created) - (a.time_updated ?? a.time_created);
|
|
6
|
+
}
|
|
7
|
+
function sortSessionsByActivity(sessions) {
|
|
8
|
+
for (let index = 1; index < sessions.length; index += 1) {
|
|
9
|
+
if (compareSessionActivityDesc(sessions[index - 1], sessions[index]) > 0) {
|
|
10
|
+
return [...sessions].sort(compareSessionActivityDesc);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return [...sessions];
|
|
14
|
+
}
|
|
15
|
+
|
|
3
16
|
// ../core/dist/index.mjs
|
|
4
|
-
import { existsSync as existsSync4, readdirSync, readFileSync as
|
|
17
|
+
import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
5
18
|
import { join as join3, basename as basename2, dirname } from "path";
|
|
6
19
|
import { existsSync, statSync } from "fs";
|
|
7
20
|
import { existsSync as existsSync2 } from "fs";
|
|
8
21
|
import { homedir, platform } from "os";
|
|
9
22
|
import { join } from "path";
|
|
10
|
-
import {
|
|
23
|
+
import { closeSync, openSync, readSync } from "fs";
|
|
24
|
+
import { StringDecoder } from "string_decoder";
|
|
11
25
|
import { basename } from "path";
|
|
12
|
-
import { existsSync as existsSync3, mkdirSync, readFileSync
|
|
26
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
13
27
|
import { homedir as homedir2 } from "os";
|
|
14
28
|
import { join as join2 } from "path";
|
|
15
29
|
import { join as join5 } from "path";
|
|
@@ -17,26 +31,26 @@ import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
|
17
31
|
import { basename as basename3, dirname as dirname2, join as join4 } from "path";
|
|
18
32
|
import { createRequire } from "module";
|
|
19
33
|
import { createHash } from "crypto";
|
|
20
|
-
import { existsSync as existsSync6, readFileSync as
|
|
34
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
21
35
|
import { join as join6, basename as basename4, dirname as dirname3 } from "path";
|
|
22
36
|
import {
|
|
23
|
-
closeSync,
|
|
37
|
+
closeSync as closeSync2,
|
|
24
38
|
existsSync as existsSync7,
|
|
25
|
-
openSync,
|
|
26
|
-
readFileSync as
|
|
27
|
-
readSync,
|
|
39
|
+
openSync as openSync2,
|
|
40
|
+
readFileSync as readFileSync4,
|
|
41
|
+
readSync as readSync2,
|
|
28
42
|
readdirSync as readdirSync3,
|
|
29
43
|
statSync as statSync4
|
|
30
44
|
} from "fs";
|
|
31
45
|
import { join as join7, basename as basename5 } from "path";
|
|
32
|
-
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as
|
|
46
|
+
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
|
|
33
47
|
import { join as join8, normalize } from "path";
|
|
34
|
-
import { existsSync as existsSync9, readFileSync as
|
|
48
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, readdirSync as readdirSync5, statSync as statSync6 } from "fs";
|
|
35
49
|
import { basename as basename6, join as join9 } from "path";
|
|
36
50
|
import { join as join10 } from "path";
|
|
37
51
|
import { availableParallelism } from "os";
|
|
38
52
|
import { Worker } from "worker_threads";
|
|
39
|
-
import { existsSync as existsSync10, readFileSync as
|
|
53
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
|
|
40
54
|
import { spawnSync } from "child_process";
|
|
41
55
|
import * as os from "os";
|
|
42
56
|
import * as path from "path";
|
|
@@ -58,15 +72,18 @@ function getRegisteredAgents() {
|
|
|
58
72
|
return registrations;
|
|
59
73
|
}
|
|
60
74
|
function getAgentInfoMap(sessionsByAgent) {
|
|
61
|
-
return registrations.map((
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
return registrations.map((registration) => {
|
|
76
|
+
const agent = registration.create();
|
|
77
|
+
return {
|
|
78
|
+
name: agent.name,
|
|
79
|
+
displayName: agent.displayName,
|
|
80
|
+
icon: registration.icon,
|
|
81
|
+
count: sessionsByAgent[agent.name] ?? 0
|
|
82
|
+
};
|
|
83
|
+
});
|
|
67
84
|
}
|
|
68
85
|
function getAgentByName(name) {
|
|
69
|
-
return registrations.find((
|
|
86
|
+
return registrations.find((registration) => registration.create().name === name);
|
|
70
87
|
}
|
|
71
88
|
function parsedSession(session) {
|
|
72
89
|
return { status: "parsed", data: session };
|
|
@@ -92,6 +109,26 @@ var BaseAgent = class {
|
|
|
92
109
|
};
|
|
93
110
|
var FileSystemSessionSource = class extends BaseAgent {
|
|
94
111
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
112
|
+
scan(options) {
|
|
113
|
+
const sources = this.listSessionSources(options);
|
|
114
|
+
const sessions = [];
|
|
115
|
+
options?.onProgress?.({ total: sources.length, processed: 0, sessions: 0 });
|
|
116
|
+
for (const [index, source] of sources.entries()) {
|
|
117
|
+
try {
|
|
118
|
+
const session = this.scanSessionSource(source.sourcePath, options);
|
|
119
|
+
if (session) sessions.push(session);
|
|
120
|
+
} catch {
|
|
121
|
+
continue;
|
|
122
|
+
} finally {
|
|
123
|
+
options?.onProgress?.({
|
|
124
|
+
total: sources.length,
|
|
125
|
+
processed: index + 1,
|
|
126
|
+
sessions: sessions.length
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return sessions;
|
|
131
|
+
}
|
|
95
132
|
getSessionMetaMap() {
|
|
96
133
|
return this.sessionMetaMap;
|
|
97
134
|
}
|
|
@@ -244,6 +281,7 @@ function getZCodeDataPath() {
|
|
|
244
281
|
}
|
|
245
282
|
return null;
|
|
246
283
|
}
|
|
284
|
+
var READ_CHUNK_BYTES = 1 << 20;
|
|
247
285
|
function* parseJsonlLines(content) {
|
|
248
286
|
for (const line of content.split("\n")) {
|
|
249
287
|
const trimmed = line.trim();
|
|
@@ -254,9 +292,35 @@ function* parseJsonlLines(content) {
|
|
|
254
292
|
}
|
|
255
293
|
}
|
|
256
294
|
}
|
|
257
|
-
function
|
|
258
|
-
const
|
|
259
|
-
|
|
295
|
+
function* readJsonlFileLines(filePath, chunkBytes = READ_CHUNK_BYTES) {
|
|
296
|
+
const fd = openSync(filePath, "r");
|
|
297
|
+
try {
|
|
298
|
+
const buffer = Buffer.alloc(chunkBytes);
|
|
299
|
+
const decoder = new StringDecoder("utf8");
|
|
300
|
+
let remainder = "";
|
|
301
|
+
let bytesRead = readSync(fd, buffer, 0, chunkBytes, -1);
|
|
302
|
+
while (bytesRead > 0) {
|
|
303
|
+
const lines = (remainder + decoder.write(buffer.subarray(0, bytesRead))).split("\n");
|
|
304
|
+
remainder = lines.pop();
|
|
305
|
+
for (const line of lines) {
|
|
306
|
+
const trimmed = line.trim();
|
|
307
|
+
if (trimmed) yield trimmed;
|
|
308
|
+
}
|
|
309
|
+
bytesRead = readSync(fd, buffer, 0, chunkBytes, -1);
|
|
310
|
+
}
|
|
311
|
+
const tail = (remainder + decoder.end()).trim();
|
|
312
|
+
if (tail) yield tail;
|
|
313
|
+
} finally {
|
|
314
|
+
closeSync(fd);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function* readJsonlFile(filePath) {
|
|
318
|
+
for (const line of readJsonlFileLines(filePath)) {
|
|
319
|
+
try {
|
|
320
|
+
yield JSON.parse(line);
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
}
|
|
260
324
|
}
|
|
261
325
|
var INTERNAL_TAGS = [
|
|
262
326
|
"command-message",
|
|
@@ -339,15 +403,6 @@ function resolveSessionTitle(explicit, message, directory) {
|
|
|
339
403
|
}
|
|
340
404
|
return UNTITLED_SESSION;
|
|
341
405
|
}
|
|
342
|
-
function parsed(data) {
|
|
343
|
-
return { status: "parsed", data };
|
|
344
|
-
}
|
|
345
|
-
function skipped(reason) {
|
|
346
|
-
return reason ? { status: "skipped", reason } : { status: "skipped" };
|
|
347
|
-
}
|
|
348
|
-
function filtered(reason) {
|
|
349
|
-
return reason ? { status: "filtered", reason } : { status: "filtered" };
|
|
350
|
-
}
|
|
351
406
|
function isInternalEventType2(value) {
|
|
352
407
|
return isInternalEventType(value);
|
|
353
408
|
}
|
|
@@ -416,80 +471,6 @@ function firstUserMessageTitle(messages) {
|
|
|
416
471
|
}
|
|
417
472
|
return null;
|
|
418
473
|
}
|
|
419
|
-
var PerfTracer = class {
|
|
420
|
-
rootMarkers = [];
|
|
421
|
-
activeStack = [];
|
|
422
|
-
enabled = false;
|
|
423
|
-
enable() {
|
|
424
|
-
this.enabled = true;
|
|
425
|
-
}
|
|
426
|
-
start(name) {
|
|
427
|
-
const marker = {
|
|
428
|
-
name,
|
|
429
|
-
startTime: performance.now(),
|
|
430
|
-
children: []
|
|
431
|
-
};
|
|
432
|
-
if (!this.enabled) return marker;
|
|
433
|
-
const parent = this.activeStack[this.activeStack.length - 1];
|
|
434
|
-
if (parent) {
|
|
435
|
-
marker.parent = parent;
|
|
436
|
-
parent.children.push(marker);
|
|
437
|
-
} else {
|
|
438
|
-
this.rootMarkers.push(marker);
|
|
439
|
-
}
|
|
440
|
-
this.activeStack.push(marker);
|
|
441
|
-
return marker;
|
|
442
|
-
}
|
|
443
|
-
end(marker) {
|
|
444
|
-
if (!this.enabled) return;
|
|
445
|
-
const target = marker ?? this.activeStack[this.activeStack.length - 1];
|
|
446
|
-
if (!target) return;
|
|
447
|
-
target.endTime = performance.now();
|
|
448
|
-
target.duration = target.endTime - target.startTime;
|
|
449
|
-
while (this.activeStack.length > 0) {
|
|
450
|
-
const popped = this.activeStack.pop();
|
|
451
|
-
if (popped === target) break;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
measure(name, fn) {
|
|
455
|
-
const marker = this.start(name);
|
|
456
|
-
try {
|
|
457
|
-
return fn();
|
|
458
|
-
} finally {
|
|
459
|
-
this.end(marker);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
async measureAsync(name, fn) {
|
|
463
|
-
const marker = this.start(name);
|
|
464
|
-
try {
|
|
465
|
-
return await fn();
|
|
466
|
-
} finally {
|
|
467
|
-
this.end(marker);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
getReport() {
|
|
471
|
-
if (!this.enabled) return "Performance tracing disabled";
|
|
472
|
-
const lines = [];
|
|
473
|
-
lines.push("\n=== Performance Report ===\n");
|
|
474
|
-
for (const marker of this.rootMarkers) {
|
|
475
|
-
this.formatMarker(marker, 0, lines);
|
|
476
|
-
}
|
|
477
|
-
return lines.join("\n");
|
|
478
|
-
}
|
|
479
|
-
formatMarker(marker, depth, lines) {
|
|
480
|
-
const indent = " ".repeat(depth);
|
|
481
|
-
const duration = marker.duration?.toFixed(2) ?? "?";
|
|
482
|
-
lines.push(`${indent}${marker.name}: ${duration}ms`);
|
|
483
|
-
for (const child of marker.children) {
|
|
484
|
-
this.formatMarker(child, depth + 1, lines);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
reset() {
|
|
488
|
-
this.rootMarkers = [];
|
|
489
|
-
this.activeStack = [];
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
var perf = new PerfTracer();
|
|
493
474
|
var aliases_default = {
|
|
494
475
|
"anthropic--claude-4.6-opus": "claude-opus-4-6",
|
|
495
476
|
"anthropic--claude-4.6-sonnet": "claude-sonnet-4-6",
|
|
@@ -638,7 +619,7 @@ function loadDiskCache() {
|
|
|
638
619
|
const path2 = getCachePath();
|
|
639
620
|
if (!existsSync3(path2)) return;
|
|
640
621
|
try {
|
|
641
|
-
const cached = JSON.parse(
|
|
622
|
+
const cached = JSON.parse(readFileSync(path2, "utf-8"));
|
|
642
623
|
if (Date.now() - cached.timestamp <= CACHE_TTL_MS) {
|
|
643
624
|
const next = loadSnapshot();
|
|
644
625
|
for (const [name, rawPricing] of Object.entries(cached.data)) {
|
|
@@ -661,7 +642,7 @@ async function refreshPricingCache() {
|
|
|
661
642
|
const path2 = getCachePath();
|
|
662
643
|
if (existsSync3(path2)) {
|
|
663
644
|
try {
|
|
664
|
-
const cached = JSON.parse(
|
|
645
|
+
const cached = JSON.parse(readFileSync(path2, "utf-8"));
|
|
665
646
|
if (typeof cached.timestamp === "number" && Date.now() - cached.timestamp <= CACHE_TTL_MS) {
|
|
666
647
|
return false;
|
|
667
648
|
}
|
|
@@ -813,6 +794,179 @@ function withEstimatedSessionCost(stats, model) {
|
|
|
813
794
|
function estimateTokenCost(model, tokens) {
|
|
814
795
|
return estimateCostForTokens(model, tokens)?.cost ?? null;
|
|
815
796
|
}
|
|
797
|
+
var TranscriptBuilder = class {
|
|
798
|
+
constructor(options = {}) {
|
|
799
|
+
this.options = options;
|
|
800
|
+
}
|
|
801
|
+
options;
|
|
802
|
+
messages = [];
|
|
803
|
+
pendingToolCalls = /* @__PURE__ */ new Map();
|
|
804
|
+
currentAssistant = null;
|
|
805
|
+
latestTextAssistant = null;
|
|
806
|
+
beginTurn() {
|
|
807
|
+
this.currentAssistant = null;
|
|
808
|
+
this.latestTextAssistant = null;
|
|
809
|
+
}
|
|
810
|
+
appendMessage(input) {
|
|
811
|
+
const message = this.createMessage(input);
|
|
812
|
+
this.messages.push(message);
|
|
813
|
+
this.registerToolCalls(message.parts);
|
|
814
|
+
if (message.role === "assistant") {
|
|
815
|
+
this.currentAssistant = message;
|
|
816
|
+
if (message.parts.some((part) => part.type === "text")) {
|
|
817
|
+
this.latestTextAssistant = message;
|
|
818
|
+
}
|
|
819
|
+
} else if (message.role === "user") {
|
|
820
|
+
this.beginTurn();
|
|
821
|
+
}
|
|
822
|
+
return message;
|
|
823
|
+
}
|
|
824
|
+
appendAssistantPart(part, input, options = {}) {
|
|
825
|
+
const current = this.currentAssistant;
|
|
826
|
+
const canReuse = current !== null && (options.grouping === "current" || (part.type === "text" ? !current.parts.some((item) => item.type === "tool") : part.type === "reasoning" ? !current.parts.some((item) => item.type === "text" || item.type === "tool") : false));
|
|
827
|
+
const message = canReuse ? current : this.appendMessage({ ...input, role: "assistant", parts: [part] });
|
|
828
|
+
if (canReuse) {
|
|
829
|
+
if (options.deduplicateTail) this.appendPartIfNew(message, part);
|
|
830
|
+
else message.parts.push(part);
|
|
831
|
+
this.applyMissingMetadata(message, input);
|
|
832
|
+
}
|
|
833
|
+
if (part.type === "text") {
|
|
834
|
+
this.latestTextAssistant = message;
|
|
835
|
+
} else if (options.resetLatestText) {
|
|
836
|
+
this.latestTextAssistant = null;
|
|
837
|
+
}
|
|
838
|
+
return message;
|
|
839
|
+
}
|
|
840
|
+
appendToolCall(part, input, options = {}) {
|
|
841
|
+
const target = options.target === "current" ? this.currentAssistant : this.latestTextAssistant ?? this.currentAssistant;
|
|
842
|
+
const message = target ? target : this.appendMessage({
|
|
843
|
+
...input,
|
|
844
|
+
role: "assistant",
|
|
845
|
+
mode: options.modeOnCreate ?? (options.markModeAsTool ? "tool" : input.mode),
|
|
846
|
+
parts: [part]
|
|
847
|
+
});
|
|
848
|
+
if (target) {
|
|
849
|
+
message.parts.push(part);
|
|
850
|
+
this.applyMissingMetadata(message, input);
|
|
851
|
+
if (options.markModeAsTool) message.mode = "tool";
|
|
852
|
+
this.registerToolCall(part);
|
|
853
|
+
}
|
|
854
|
+
this.currentAssistant = message;
|
|
855
|
+
return message;
|
|
856
|
+
}
|
|
857
|
+
appendToCurrentAssistant(part) {
|
|
858
|
+
if (!this.currentAssistant) return false;
|
|
859
|
+
this.currentAssistant.parts.push(part);
|
|
860
|
+
return true;
|
|
861
|
+
}
|
|
862
|
+
updateToolCall(callId, update) {
|
|
863
|
+
const part = this.pendingToolCalls.get(callId);
|
|
864
|
+
if (!part) return false;
|
|
865
|
+
update(part);
|
|
866
|
+
return true;
|
|
867
|
+
}
|
|
868
|
+
resolveToolCall(callId, resolution) {
|
|
869
|
+
return this.updateToolCall(callId, (part) => {
|
|
870
|
+
const state = part.state ?? (part.state = {});
|
|
871
|
+
if (resolution.output !== void 0) state.output = resolution.output;
|
|
872
|
+
if (resolution.status !== void 0) state.status = resolution.status;
|
|
873
|
+
if (resolution.metadata !== void 0) state.metadata = resolution.metadata;
|
|
874
|
+
if (resolution.consume) this.pendingToolCalls.delete(callId);
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
attachUsageToLatestAssistant(tokens, options = {}) {
|
|
878
|
+
for (let index = this.messages.length - 1; index >= 0; index -= 1) {
|
|
879
|
+
const message = this.messages[index];
|
|
880
|
+
if (message.role !== "assistant" || message.tokens) continue;
|
|
881
|
+
message.tokens = tokens;
|
|
882
|
+
if (options.model !== void 0) message.model ??= options.model;
|
|
883
|
+
if (options.cost !== void 0) message.cost = options.cost;
|
|
884
|
+
if (options.costSource !== void 0) message.cost_source = options.costSource;
|
|
885
|
+
return true;
|
|
886
|
+
}
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
finish(baseStats) {
|
|
890
|
+
const messages = cleanParsedMessages(this.messages);
|
|
891
|
+
const derived = this.deriveStats(messages);
|
|
892
|
+
if (!baseStats) return { messages, stats: derived };
|
|
893
|
+
return {
|
|
894
|
+
messages,
|
|
895
|
+
stats: {
|
|
896
|
+
...derived,
|
|
897
|
+
...baseStats,
|
|
898
|
+
message_count: messages.length,
|
|
899
|
+
cost_source: baseStats.cost_source
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
createMessage(input) {
|
|
904
|
+
const sparse = this.options.messageDefaults === "sparse";
|
|
905
|
+
return {
|
|
906
|
+
id: input.id,
|
|
907
|
+
role: input.role,
|
|
908
|
+
agent: sparse ? input.agent : input.agent ?? null,
|
|
909
|
+
time_created: input.timestampMs,
|
|
910
|
+
mode: sparse ? input.mode : input.mode ?? null,
|
|
911
|
+
model: sparse ? input.model : input.model ?? null,
|
|
912
|
+
provider: sparse ? input.provider : input.provider ?? null,
|
|
913
|
+
tokens: input.tokens,
|
|
914
|
+
cost: sparse ? input.cost : input.cost ?? 0,
|
|
915
|
+
cost_source: input.costSource,
|
|
916
|
+
parts: input.parts ?? [],
|
|
917
|
+
subagent_id: input.subagentId,
|
|
918
|
+
nickname: input.nickname
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
registerToolCalls(parts) {
|
|
922
|
+
for (const part of parts) this.registerToolCall(part);
|
|
923
|
+
}
|
|
924
|
+
registerToolCall(part) {
|
|
925
|
+
if (part.type === "tool" && part.callID) {
|
|
926
|
+
this.pendingToolCalls.set(part.callID, part);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
appendPartIfNew(message, part) {
|
|
930
|
+
const tail = message.parts.at(-1);
|
|
931
|
+
if (tail?.type === part.type && tail.text === part.text) return;
|
|
932
|
+
message.parts.push(part);
|
|
933
|
+
}
|
|
934
|
+
applyMissingMetadata(message, input) {
|
|
935
|
+
if (!message.id && input.id) message.id = input.id;
|
|
936
|
+
if (message.agent == null && input.agent !== void 0) message.agent = input.agent;
|
|
937
|
+
if (message.mode == null && input.mode !== void 0) message.mode = input.mode;
|
|
938
|
+
if (message.model == null && input.model !== void 0) message.model = input.model;
|
|
939
|
+
if (message.provider == null && input.provider !== void 0) message.provider = input.provider;
|
|
940
|
+
if (!message.tokens && input.tokens) message.tokens = input.tokens;
|
|
941
|
+
if ((message.cost ?? 0) === 0 && input.cost !== void 0) message.cost = input.cost;
|
|
942
|
+
if (!message.cost_source && input.costSource) message.cost_source = input.costSource;
|
|
943
|
+
}
|
|
944
|
+
deriveStats(messages) {
|
|
945
|
+
let totalInputTokens = 0;
|
|
946
|
+
let totalOutputTokens = 0;
|
|
947
|
+
let totalCacheReadTokens = 0;
|
|
948
|
+
let totalCacheCreateTokens = 0;
|
|
949
|
+
let totalCost = 0;
|
|
950
|
+
let hasEstimatedCost = false;
|
|
951
|
+
for (const message of messages) {
|
|
952
|
+
totalInputTokens += message.tokens?.input ?? 0;
|
|
953
|
+
totalOutputTokens += message.tokens?.output ?? 0;
|
|
954
|
+
totalCacheReadTokens += message.tokens?.cache_read ?? 0;
|
|
955
|
+
totalCacheCreateTokens += message.tokens?.cache_create ?? 0;
|
|
956
|
+
totalCost += message.cost ?? 0;
|
|
957
|
+
if (message.cost_source === "estimated") hasEstimatedCost = true;
|
|
958
|
+
}
|
|
959
|
+
return {
|
|
960
|
+
message_count: messages.length,
|
|
961
|
+
total_input_tokens: totalInputTokens,
|
|
962
|
+
total_output_tokens: totalOutputTokens,
|
|
963
|
+
total_cache_read_tokens: totalCacheReadTokens || void 0,
|
|
964
|
+
total_cache_create_tokens: totalCacheCreateTokens || void 0,
|
|
965
|
+
total_cost: totalCost,
|
|
966
|
+
cost_source: totalCost > 0 ? hasEstimatedCost ? "estimated" : "recorded" : void 0
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
};
|
|
816
970
|
var HEAD_INDEX_VERSION = "claudecode-head-v2";
|
|
817
971
|
function parseTimestampMs(data) {
|
|
818
972
|
const raw = String(data["timestamp"] ?? "").trim();
|
|
@@ -867,48 +1021,6 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
867
1021
|
}
|
|
868
1022
|
return false;
|
|
869
1023
|
}
|
|
870
|
-
scan(options) {
|
|
871
|
-
if (!this.basePath) return [];
|
|
872
|
-
const scanMarker = perf.start("claudecode:scan");
|
|
873
|
-
const heads = [];
|
|
874
|
-
const listMarker = perf.start("listProjectDirs");
|
|
875
|
-
const projectDirs = this.listProjectDirs();
|
|
876
|
-
perf.end(listMarker);
|
|
877
|
-
const filesByProject = projectDirs.map((projectDir) => {
|
|
878
|
-
const fileMarker = perf.start(`listJsonlFiles:${basename2(projectDir)}`);
|
|
879
|
-
const files = this.listJsonlFiles(projectDir).filter((file) => {
|
|
880
|
-
try {
|
|
881
|
-
return matchesScanWindow(statSync2(file).mtimeMs, options);
|
|
882
|
-
} catch {
|
|
883
|
-
return false;
|
|
884
|
-
}
|
|
885
|
-
});
|
|
886
|
-
perf.end(fileMarker);
|
|
887
|
-
return { projectDir, files };
|
|
888
|
-
});
|
|
889
|
-
const totalFiles = filesByProject.reduce((total, item) => total + item.files.length, 0);
|
|
890
|
-
options?.onProgress?.({ total: totalFiles, processed: 0, sessions: 0 });
|
|
891
|
-
let processed = 0;
|
|
892
|
-
for (const { projectDir, files } of filesByProject) {
|
|
893
|
-
for (const file of files) {
|
|
894
|
-
try {
|
|
895
|
-
const parseMarker = perf.start(`parseSessionHead:${basename2(file)}`);
|
|
896
|
-
const head = getParsedSession(this.parseSessionHeadResult(file, projectDir));
|
|
897
|
-
perf.end(parseMarker);
|
|
898
|
-
if (head) {
|
|
899
|
-
heads.push(head);
|
|
900
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, file, projectDir));
|
|
901
|
-
}
|
|
902
|
-
} catch {
|
|
903
|
-
} finally {
|
|
904
|
-
processed += 1;
|
|
905
|
-
options?.onProgress?.({ total: totalFiles, processed, sessions: heads.length });
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
perf.end(scanMarker);
|
|
910
|
-
return heads;
|
|
911
|
-
}
|
|
912
1024
|
listSessionSources(options) {
|
|
913
1025
|
if (!this.basePath) return [];
|
|
914
1026
|
const refs = [];
|
|
@@ -945,43 +1057,24 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
945
1057
|
if (!existsSync4(meta.sourcePath)) {
|
|
946
1058
|
throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
947
1059
|
}
|
|
948
|
-
const content =
|
|
949
|
-
const
|
|
950
|
-
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
1060
|
+
const content = readFileSync2(meta.sourcePath, "utf-8");
|
|
1061
|
+
const builder = new TranscriptBuilder();
|
|
951
1062
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
952
1063
|
const assistantUuidToToolCalls = /* @__PURE__ */ new Map();
|
|
953
1064
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
954
|
-
const assistantState = {
|
|
955
|
-
currentIndex: null,
|
|
956
|
-
latestTextIndex: null
|
|
957
|
-
};
|
|
958
|
-
let totalCost = 0;
|
|
959
|
-
let totalInputTokens = 0;
|
|
960
|
-
let totalOutputTokens = 0;
|
|
961
|
-
let totalCacheRead = 0;
|
|
962
|
-
let totalCacheCreate = 0;
|
|
963
1065
|
for (const record of parseJsonlLines(content)) {
|
|
964
1066
|
try {
|
|
965
1067
|
this.convertRecord(
|
|
966
1068
|
record,
|
|
967
|
-
|
|
968
|
-
pendingToolCalls,
|
|
1069
|
+
builder,
|
|
969
1070
|
ignoredToolCallIds,
|
|
970
1071
|
assistantUuidToToolCalls,
|
|
971
|
-
countedUsageKeys
|
|
972
|
-
assistantState
|
|
1072
|
+
countedUsageKeys
|
|
973
1073
|
);
|
|
974
1074
|
} catch {
|
|
975
1075
|
}
|
|
976
1076
|
}
|
|
977
|
-
const
|
|
978
|
-
for (const msg of cleanedMessages) {
|
|
979
|
-
totalCost += msg.cost ?? 0;
|
|
980
|
-
totalInputTokens += msg.tokens?.input ?? 0;
|
|
981
|
-
totalOutputTokens += msg.tokens?.output ?? 0;
|
|
982
|
-
totalCacheRead += msg.tokens?.cache_read ?? 0;
|
|
983
|
-
totalCacheCreate += msg.tokens?.cache_create ?? 0;
|
|
984
|
-
}
|
|
1077
|
+
const transcript = builder.finish();
|
|
985
1078
|
return {
|
|
986
1079
|
id: meta.id,
|
|
987
1080
|
title: meta.title,
|
|
@@ -990,16 +1083,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
990
1083
|
version: void 0,
|
|
991
1084
|
time_created: meta.createdAt,
|
|
992
1085
|
time_updated: meta.updatedAt,
|
|
993
|
-
stats:
|
|
994
|
-
|
|
995
|
-
total_input_tokens: totalInputTokens,
|
|
996
|
-
total_output_tokens: totalOutputTokens,
|
|
997
|
-
total_cost: totalCost,
|
|
998
|
-
cost_source: totalCost > 0 ? "estimated" : void 0,
|
|
999
|
-
total_cache_read_tokens: totalCacheRead,
|
|
1000
|
-
total_cache_create_tokens: totalCacheCreate
|
|
1001
|
-
},
|
|
1002
|
-
messages: cleanedMessages
|
|
1086
|
+
stats: transcript.stats,
|
|
1087
|
+
messages: transcript.messages
|
|
1003
1088
|
};
|
|
1004
1089
|
}
|
|
1005
1090
|
// --- Private helpers ---
|
|
@@ -1066,7 +1151,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1066
1151
|
const map = /* @__PURE__ */ new Map();
|
|
1067
1152
|
if (existsSync4(indexPath)) {
|
|
1068
1153
|
try {
|
|
1069
|
-
const data = JSON.parse(
|
|
1154
|
+
const data = JSON.parse(readFileSync2(indexPath, "utf-8"));
|
|
1070
1155
|
const entries = data?.entries ?? [];
|
|
1071
1156
|
for (const entry of entries) {
|
|
1072
1157
|
const sid = entry?.sessionId;
|
|
@@ -1085,7 +1170,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1085
1170
|
return getParsedSession(this.parseSessionHeadResult(filePath, projectDir));
|
|
1086
1171
|
}
|
|
1087
1172
|
parseSessionHeadResult(filePath, projectDir) {
|
|
1088
|
-
const content =
|
|
1173
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1089
1174
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
1090
1175
|
if (lines.length === 0) return skippedSession("empty file");
|
|
1091
1176
|
const sessionId = basename2(filePath, ".jsonl");
|
|
@@ -1210,41 +1295,30 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1210
1295
|
return null;
|
|
1211
1296
|
}
|
|
1212
1297
|
// --- Record conversion ---
|
|
1213
|
-
convertRecord(data,
|
|
1298
|
+
convertRecord(data, builder, ignoredToolCallIds, assistantUuidToToolCalls, countedUsageKeys) {
|
|
1214
1299
|
if (data["isMeta"] === true) return;
|
|
1215
1300
|
const msgType = String(data["type"] ?? "");
|
|
1216
1301
|
if (isInternalEventType(msgType)) return;
|
|
1217
1302
|
if (msgType === "assistant") {
|
|
1218
1303
|
this.convertAssistantRecord(
|
|
1219
1304
|
data,
|
|
1220
|
-
|
|
1221
|
-
pendingToolCalls,
|
|
1305
|
+
builder,
|
|
1222
1306
|
ignoredToolCallIds,
|
|
1223
1307
|
assistantUuidToToolCalls,
|
|
1224
|
-
countedUsageKeys
|
|
1225
|
-
assistantState
|
|
1308
|
+
countedUsageKeys
|
|
1226
1309
|
);
|
|
1227
1310
|
} else if (msgType === "user") {
|
|
1228
|
-
this.convertUserRecord(
|
|
1229
|
-
data,
|
|
1230
|
-
messages,
|
|
1231
|
-
pendingToolCalls,
|
|
1232
|
-
ignoredToolCallIds,
|
|
1233
|
-
assistantUuidToToolCalls,
|
|
1234
|
-
assistantState
|
|
1235
|
-
);
|
|
1311
|
+
this.convertUserRecord(data, builder, ignoredToolCallIds, assistantUuidToToolCalls);
|
|
1236
1312
|
} else if (msgType === "tool_result") {
|
|
1237
|
-
this.convertToolResultRecord(data,
|
|
1313
|
+
this.convertToolResultRecord(data, builder);
|
|
1238
1314
|
}
|
|
1239
1315
|
}
|
|
1240
|
-
convertAssistantRecord(data,
|
|
1316
|
+
convertAssistantRecord(data, builder, ignoredToolCallIds, assistantUuidToToolCalls, countedUsageKeys) {
|
|
1241
1317
|
const msg = data["message"] ?? {};
|
|
1242
1318
|
const timestampMs = parseTimestampMs(data);
|
|
1243
1319
|
const rawContent = msg["content"] ?? [];
|
|
1244
1320
|
const uuid = String(data["uuid"] ?? "");
|
|
1245
1321
|
const toolCallIds = [];
|
|
1246
|
-
let currentAssistantIndex = assistantState.currentIndex;
|
|
1247
|
-
let latestAssistantTextIndex = assistantState.latestTextIndex;
|
|
1248
1322
|
if (Array.isArray(rawContent)) {
|
|
1249
1323
|
for (const item of rawContent) {
|
|
1250
1324
|
if (!item || typeof item !== "object") continue;
|
|
@@ -1253,23 +1327,28 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1253
1327
|
if (partType === "thinking") {
|
|
1254
1328
|
const text = cleanInternalText(String(part["thinking"] ?? ""));
|
|
1255
1329
|
if (text) {
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
{
|
|
1259
|
-
|
|
1330
|
+
const message2 = builder.appendAssistantPart(
|
|
1331
|
+
this.buildReasoningPart(text, timestampMs),
|
|
1332
|
+
{ id: uuid, timestampMs, agent: "claude" },
|
|
1333
|
+
{ deduplicateTail: true }
|
|
1260
1334
|
);
|
|
1335
|
+
this.applyAssistantMetadata(message2, data, msg, countedUsageKeys);
|
|
1261
1336
|
}
|
|
1262
1337
|
continue;
|
|
1263
1338
|
}
|
|
1264
1339
|
if (partType === "text") {
|
|
1265
1340
|
const text = cleanInternalText(String(part["text"] ?? ""));
|
|
1266
1341
|
if (text) {
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
{
|
|
1270
|
-
|
|
1342
|
+
const message2 = builder.appendAssistantPart(
|
|
1343
|
+
this.buildTextPart(text, timestampMs),
|
|
1344
|
+
{
|
|
1345
|
+
id: uuid,
|
|
1346
|
+
timestampMs,
|
|
1347
|
+
agent: "claude"
|
|
1348
|
+
},
|
|
1349
|
+
{ deduplicateTail: true }
|
|
1271
1350
|
);
|
|
1272
|
-
|
|
1351
|
+
this.applyAssistantMetadata(message2, data, msg, countedUsageKeys);
|
|
1273
1352
|
}
|
|
1274
1353
|
continue;
|
|
1275
1354
|
}
|
|
@@ -1281,18 +1360,13 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1281
1360
|
continue;
|
|
1282
1361
|
}
|
|
1283
1362
|
const toolPart = this.buildToolPart(part, timestampMs);
|
|
1284
|
-
const
|
|
1285
|
-
messageId: uuid,
|
|
1286
|
-
data,
|
|
1287
|
-
msg,
|
|
1288
|
-
timestampMs,
|
|
1363
|
+
const message = builder.appendToolCall(
|
|
1289
1364
|
toolPart,
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1365
|
+
{ id: uuid, timestampMs, agent: "claude" },
|
|
1366
|
+
{ modeOnCreate: "tool" }
|
|
1367
|
+
);
|
|
1368
|
+
this.applyAssistantMetadata(message, data, msg, countedUsageKeys);
|
|
1294
1369
|
if (toolCallId) {
|
|
1295
|
-
pendingToolCalls.set(toolCallId, [msgIndex, partIndex]);
|
|
1296
1370
|
toolCallIds.push(toolCallId);
|
|
1297
1371
|
}
|
|
1298
1372
|
}
|
|
@@ -1300,10 +1374,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1300
1374
|
if (toolCallIds.length > 0) {
|
|
1301
1375
|
assistantUuidToToolCalls.set(uuid, toolCallIds);
|
|
1302
1376
|
}
|
|
1303
|
-
assistantState.currentIndex = currentAssistantIndex;
|
|
1304
|
-
assistantState.latestTextIndex = latestAssistantTextIndex;
|
|
1305
1377
|
}
|
|
1306
|
-
convertUserRecord(data,
|
|
1378
|
+
convertUserRecord(data, builder, ignoredToolCallIds, assistantUuidToToolCalls) {
|
|
1307
1379
|
const msg = data["message"] ?? {};
|
|
1308
1380
|
const timestampMs = parseTimestampMs(data);
|
|
1309
1381
|
const content = msg["content"] ?? "";
|
|
@@ -1311,18 +1383,14 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1311
1383
|
if (typeof content === "string") {
|
|
1312
1384
|
const parts = this.normalizeUserTextParts(content, timestampMs);
|
|
1313
1385
|
if (parts.length === 0) {
|
|
1314
|
-
|
|
1315
|
-
assistantState.latestTextIndex = null;
|
|
1386
|
+
builder.beginTurn();
|
|
1316
1387
|
return;
|
|
1317
1388
|
}
|
|
1318
|
-
|
|
1319
|
-
assistantState.currentIndex = null;
|
|
1320
|
-
assistantState.latestTextIndex = null;
|
|
1389
|
+
builder.appendMessage({ id: uuid, role: "user", timestampMs, parts });
|
|
1321
1390
|
return;
|
|
1322
1391
|
}
|
|
1323
1392
|
if (!Array.isArray(content)) {
|
|
1324
|
-
|
|
1325
|
-
assistantState.latestTextIndex = null;
|
|
1393
|
+
builder.beginTurn();
|
|
1326
1394
|
return;
|
|
1327
1395
|
}
|
|
1328
1396
|
const visibleParts = this.normalizeUserTextParts(content, timestampMs);
|
|
@@ -1334,13 +1402,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1334
1402
|
const toolCallId = this.resolveToolCallId(data, ci, assistantUuidToToolCalls);
|
|
1335
1403
|
if (toolCallId && ignoredToolCallIds.has(toolCallId)) continue;
|
|
1336
1404
|
const outputParts = this.normalizeClaudeToolOutput(ci["content"], timestampMs);
|
|
1337
|
-
if (this.backfillToolOutput(
|
|
1338
|
-
messages,
|
|
1339
|
-
pendingToolCalls,
|
|
1340
|
-
toolCallId,
|
|
1341
|
-
outputParts,
|
|
1342
|
-
toolStateUpdates
|
|
1343
|
-
)) {
|
|
1405
|
+
if (this.backfillToolOutput(builder, toolCallId, outputParts, toolStateUpdates)) {
|
|
1344
1406
|
continue;
|
|
1345
1407
|
}
|
|
1346
1408
|
const fallback = this.buildFallbackToolMessage({
|
|
@@ -1349,17 +1411,14 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1349
1411
|
toolCallId,
|
|
1350
1412
|
outputParts
|
|
1351
1413
|
});
|
|
1352
|
-
if (fallback)
|
|
1414
|
+
if (fallback) builder.appendMessage(fallback);
|
|
1353
1415
|
}
|
|
1354
1416
|
if (visibleParts.length > 0) {
|
|
1355
|
-
|
|
1356
|
-
this.buildMessage({ messageId: uuid, role: "user", timestampMs, parts: visibleParts })
|
|
1357
|
-
);
|
|
1417
|
+
builder.appendMessage({ id: uuid, role: "user", timestampMs, parts: visibleParts });
|
|
1358
1418
|
}
|
|
1359
|
-
|
|
1360
|
-
assistantState.latestTextIndex = null;
|
|
1419
|
+
builder.beginTurn();
|
|
1361
1420
|
}
|
|
1362
|
-
convertToolResultRecord(data,
|
|
1421
|
+
convertToolResultRecord(data, builder) {
|
|
1363
1422
|
const timestampMs = parseTimestampMs(data);
|
|
1364
1423
|
const msg = data["message"] ?? {};
|
|
1365
1424
|
const outputParts = this.normalizeClaudeToolOutput(msg["content"], timestampMs);
|
|
@@ -1370,25 +1429,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1370
1429
|
toolCallId: null,
|
|
1371
1430
|
outputParts
|
|
1372
1431
|
});
|
|
1373
|
-
if (fallback)
|
|
1374
|
-
|
|
1375
|
-
assistantState.latestTextIndex = null;
|
|
1376
|
-
}
|
|
1377
|
-
// --- Message building ---
|
|
1378
|
-
buildMessage(opts) {
|
|
1379
|
-
return {
|
|
1380
|
-
id: opts.messageId,
|
|
1381
|
-
role: opts.role,
|
|
1382
|
-
agent: opts.agent ?? null,
|
|
1383
|
-
time_created: opts.timestampMs,
|
|
1384
|
-
mode: opts.mode ?? null,
|
|
1385
|
-
model: opts.model ?? null,
|
|
1386
|
-
provider: opts.provider ?? null,
|
|
1387
|
-
tokens: opts.tokens ? opts.tokens : void 0,
|
|
1388
|
-
cost: opts.cost ?? 0,
|
|
1389
|
-
cost_source: opts.cost_source,
|
|
1390
|
-
parts: opts.parts
|
|
1391
|
-
};
|
|
1432
|
+
if (fallback) builder.appendMessage(fallback);
|
|
1433
|
+
builder.beginTurn();
|
|
1392
1434
|
}
|
|
1393
1435
|
buildTextPart(text, timestampMs) {
|
|
1394
1436
|
return { type: "text", text, time_created: timestampMs };
|
|
@@ -1431,71 +1473,6 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1431
1473
|
}
|
|
1432
1474
|
}
|
|
1433
1475
|
}
|
|
1434
|
-
// --- Assistant message grouping ---
|
|
1435
|
-
appendAssistantReasoning(messages, opts, currentIndex) {
|
|
1436
|
-
const part = this.buildReasoningPart(opts.text, opts.timestampMs);
|
|
1437
|
-
if (currentIndex !== null) {
|
|
1438
|
-
const message2 = messages[currentIndex];
|
|
1439
|
-
const hasText = message2.parts.some((p) => p.type === "text");
|
|
1440
|
-
const hasTool = message2.parts.some((p) => p.type === "tool");
|
|
1441
|
-
if (!hasText && !hasTool) {
|
|
1442
|
-
this.appendPartIfNew(message2, part);
|
|
1443
|
-
this.applyAssistantMetadata(message2, opts.data, opts.msg, opts.countedUsageKeys);
|
|
1444
|
-
return currentIndex;
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
const message = this.buildMessage({
|
|
1448
|
-
messageId: opts.messageId,
|
|
1449
|
-
role: "assistant",
|
|
1450
|
-
timestampMs: opts.timestampMs,
|
|
1451
|
-
parts: [part],
|
|
1452
|
-
agent: "claude"
|
|
1453
|
-
});
|
|
1454
|
-
this.applyAssistantMetadata(message, opts.data, opts.msg, opts.countedUsageKeys);
|
|
1455
|
-
messages.push(message);
|
|
1456
|
-
return messages.length - 1;
|
|
1457
|
-
}
|
|
1458
|
-
appendAssistantText(messages, opts, currentIndex) {
|
|
1459
|
-
const part = this.buildTextPart(opts.text, opts.timestampMs);
|
|
1460
|
-
if (currentIndex !== null) {
|
|
1461
|
-
const message2 = messages[currentIndex];
|
|
1462
|
-
const hasTool = message2.parts.some((p) => p.type === "tool");
|
|
1463
|
-
if (!hasTool) {
|
|
1464
|
-
this.appendPartIfNew(message2, part);
|
|
1465
|
-
this.applyAssistantMetadata(message2, opts.data, opts.msg, opts.countedUsageKeys);
|
|
1466
|
-
return currentIndex;
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
const message = this.buildMessage({
|
|
1470
|
-
messageId: opts.messageId,
|
|
1471
|
-
role: "assistant",
|
|
1472
|
-
timestampMs: opts.timestampMs,
|
|
1473
|
-
parts: [part],
|
|
1474
|
-
agent: "claude"
|
|
1475
|
-
});
|
|
1476
|
-
this.applyAssistantMetadata(message, opts.data, opts.msg, opts.countedUsageKeys);
|
|
1477
|
-
messages.push(message);
|
|
1478
|
-
return messages.length - 1;
|
|
1479
|
-
}
|
|
1480
|
-
attachToolCallToLatestAssistant(messages, opts) {
|
|
1481
|
-
if (opts.latestTextIndex !== null) {
|
|
1482
|
-
const message2 = messages[opts.latestTextIndex];
|
|
1483
|
-
message2.parts.push(opts.toolPart);
|
|
1484
|
-
this.applyAssistantMetadata(message2, opts.data, opts.msg, opts.countedUsageKeys);
|
|
1485
|
-
return [opts.latestTextIndex, message2.parts.length - 1];
|
|
1486
|
-
}
|
|
1487
|
-
const message = this.buildMessage({
|
|
1488
|
-
messageId: opts.messageId,
|
|
1489
|
-
role: "assistant",
|
|
1490
|
-
timestampMs: opts.timestampMs,
|
|
1491
|
-
parts: [opts.toolPart],
|
|
1492
|
-
agent: "claude",
|
|
1493
|
-
mode: "tool"
|
|
1494
|
-
});
|
|
1495
|
-
this.applyAssistantMetadata(message, opts.data, opts.msg, opts.countedUsageKeys);
|
|
1496
|
-
messages.push(message);
|
|
1497
|
-
return [messages.length - 1, 0];
|
|
1498
|
-
}
|
|
1499
1476
|
// --- User content normalization ---
|
|
1500
1477
|
normalizeUserTextParts(content, timestampMs) {
|
|
1501
1478
|
if (typeof content === "string") {
|
|
@@ -1543,29 +1520,19 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1543
1520
|
return text ? [this.buildTextPart(text, timestampMs)] : [];
|
|
1544
1521
|
}
|
|
1545
1522
|
// --- Tool backfill ---
|
|
1546
|
-
backfillToolOutput(
|
|
1523
|
+
backfillToolOutput(builder, callId, outputParts, stateUpdates) {
|
|
1547
1524
|
if (!callId) return false;
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
existing.push(...outputParts);
|
|
1556
|
-
} else if (existing === null || existing === void 0) {
|
|
1557
|
-
state.output = [...outputParts];
|
|
1558
|
-
} else {
|
|
1559
|
-
state.output = [existing, ...outputParts];
|
|
1525
|
+
return builder.updateToolCall(callId, (part) => {
|
|
1526
|
+
const state = part.state ?? (part.state = {});
|
|
1527
|
+
if (outputParts.length > 0) {
|
|
1528
|
+
const existing = state.output;
|
|
1529
|
+
if (Array.isArray(existing)) existing.push(...outputParts);
|
|
1530
|
+
else if (existing == null) state.output = [...outputParts];
|
|
1531
|
+
else state.output = [existing, ...outputParts];
|
|
1560
1532
|
}
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
}
|
|
1565
|
-
if (outputParts.length > 0 && !state.status) {
|
|
1566
|
-
state.status = "completed";
|
|
1567
|
-
}
|
|
1568
|
-
return outputParts.length > 0 || !!stateUpdates;
|
|
1533
|
+
if (stateUpdates) Object.assign(state, stateUpdates);
|
|
1534
|
+
if (outputParts.length > 0 && !state.status) state.status = "completed";
|
|
1535
|
+
});
|
|
1569
1536
|
}
|
|
1570
1537
|
resolveToolCallId(data, item, assistantUuidToToolCalls) {
|
|
1571
1538
|
const directId = String(item["tool_use_id"] ?? "").trim();
|
|
@@ -1593,25 +1560,17 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1593
1560
|
// --- Fallback ---
|
|
1594
1561
|
buildFallbackToolMessage(opts) {
|
|
1595
1562
|
if (opts.outputParts.length === 0) return null;
|
|
1596
|
-
return
|
|
1597
|
-
|
|
1563
|
+
return {
|
|
1564
|
+
id: opts.messageId,
|
|
1598
1565
|
role: "tool",
|
|
1599
1566
|
timestampMs: opts.timestampMs,
|
|
1600
1567
|
parts: opts.outputParts
|
|
1601
|
-
}
|
|
1568
|
+
};
|
|
1602
1569
|
}
|
|
1603
1570
|
// --- Utilities ---
|
|
1604
1571
|
shouldIgnoreTool(toolName) {
|
|
1605
1572
|
return toolName === "TodoWrite";
|
|
1606
1573
|
}
|
|
1607
|
-
appendPartIfNew(message, part) {
|
|
1608
|
-
const parts = message.parts;
|
|
1609
|
-
if (parts.length > 0 && parts[parts.length - 1].type === part.type) {
|
|
1610
|
-
const tail = parts[parts.length - 1];
|
|
1611
|
-
if (tail.text === part.text) return;
|
|
1612
|
-
}
|
|
1613
|
-
parts.push(part);
|
|
1614
|
-
}
|
|
1615
1574
|
};
|
|
1616
1575
|
var DatabaseConstructor = null;
|
|
1617
1576
|
try {
|
|
@@ -2142,7 +2101,7 @@ function kimiContentText(content) {
|
|
|
2142
2101
|
}
|
|
2143
2102
|
function extractFirstUserTitle(contextFile, wireFile) {
|
|
2144
2103
|
if (contextFile && existsSync6(contextFile)) {
|
|
2145
|
-
const content =
|
|
2104
|
+
const content = readFileSync3(contextFile, "utf-8");
|
|
2146
2105
|
for (const record of parseJsonlLines(content)) {
|
|
2147
2106
|
if (record.role !== "user") continue;
|
|
2148
2107
|
const title = normalizeTitleText(kimiContentText(record.content));
|
|
@@ -2150,7 +2109,7 @@ function extractFirstUserTitle(contextFile, wireFile) {
|
|
|
2150
2109
|
}
|
|
2151
2110
|
}
|
|
2152
2111
|
if (wireFile && existsSync6(wireFile)) {
|
|
2153
|
-
const content =
|
|
2112
|
+
const content = readFileSync3(wireFile, "utf-8");
|
|
2154
2113
|
for (const record of parseJsonlLines(content)) {
|
|
2155
2114
|
const message = record.message ?? {};
|
|
2156
2115
|
if (message.type !== "TurnBegin") continue;
|
|
@@ -2179,12 +2138,12 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2179
2138
|
const configPath = join6(roots.kimiRoot, "kimi.json");
|
|
2180
2139
|
const tomlPath = join6(roots.kimiRoot, "config.toml");
|
|
2181
2140
|
if (existsSync6(tomlPath)) {
|
|
2182
|
-
const configText =
|
|
2141
|
+
const configText = readFileSync3(tomlPath, "utf-8");
|
|
2183
2142
|
this.defaultModel = configText.match(/^default_model\s*=\s*"([^"]+)"/m)?.[1] ?? null;
|
|
2184
2143
|
}
|
|
2185
2144
|
if (!existsSync6(configPath)) return;
|
|
2186
2145
|
try {
|
|
2187
|
-
const raw = JSON.parse(
|
|
2146
|
+
const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
2188
2147
|
const workDirs = raw?.work_dirs;
|
|
2189
2148
|
if (!Array.isArray(workDirs)) return;
|
|
2190
2149
|
for (const wd of workDirs) {
|
|
@@ -2248,12 +2207,12 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2248
2207
|
let wireMtime = null;
|
|
2249
2208
|
let metaFile = "";
|
|
2250
2209
|
if (existsSync6(statePath)) {
|
|
2251
|
-
const state = JSON.parse(
|
|
2210
|
+
const state = JSON.parse(readFileSync3(statePath, "utf-8"));
|
|
2252
2211
|
title = String(state.custom_title ?? "");
|
|
2253
2212
|
wireMtime = typeof state.wire_mtime === "number" ? state.wire_mtime : null;
|
|
2254
2213
|
metaFile = statePath;
|
|
2255
2214
|
} else if (existsSync6(metaPath)) {
|
|
2256
|
-
const meta = JSON.parse(
|
|
2215
|
+
const meta = JSON.parse(readFileSync3(metaPath, "utf-8"));
|
|
2257
2216
|
title = String(meta.title ?? "");
|
|
2258
2217
|
wireMtime = typeof meta.wire_mtime === "number" ? meta.wire_mtime : null;
|
|
2259
2218
|
metaFile = metaPath;
|
|
@@ -2277,50 +2236,6 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2277
2236
|
return skippedSession("malformed metadata");
|
|
2278
2237
|
}
|
|
2279
2238
|
}
|
|
2280
|
-
scan(options) {
|
|
2281
|
-
if (!this.basePath) return [];
|
|
2282
|
-
const scanMarker = perf.start("kimi:scan");
|
|
2283
|
-
const listMarker = perf.start("listSessionDirs");
|
|
2284
|
-
const sessionDirs = this.listSessionDirs();
|
|
2285
|
-
perf.end(listMarker);
|
|
2286
|
-
const metas = [];
|
|
2287
|
-
for (const dir of sessionDirs) {
|
|
2288
|
-
try {
|
|
2289
|
-
const parseMarker = perf.start(`parseSessionDir:${basename4(dir)}`);
|
|
2290
|
-
const meta = getParsedSession(this.parseSessionDirResult(dir));
|
|
2291
|
-
perf.end(parseMarker);
|
|
2292
|
-
if (meta && matchesScanWindow(meta.createdAt, options)) {
|
|
2293
|
-
metas.push(meta);
|
|
2294
|
-
}
|
|
2295
|
-
} catch {
|
|
2296
|
-
}
|
|
2297
|
-
}
|
|
2298
|
-
options?.onProgress?.({ total: metas.length, processed: 0, sessions: 0 });
|
|
2299
|
-
const heads = [];
|
|
2300
|
-
let processed = 0;
|
|
2301
|
-
for (const meta of metas) {
|
|
2302
|
-
try {
|
|
2303
|
-
meta.sourceFingerprint = this.sourceFingerprint(meta);
|
|
2304
|
-
this.sessionMetaMap.set(meta.id, meta);
|
|
2305
|
-
const stats = this.extractStats(meta.sourcePath);
|
|
2306
|
-
heads.push({
|
|
2307
|
-
id: meta.id,
|
|
2308
|
-
slug: `kimi/${meta.id}`,
|
|
2309
|
-
title: meta.title,
|
|
2310
|
-
directory: meta.cwd,
|
|
2311
|
-
time_created: meta.createdAt,
|
|
2312
|
-
time_updated: meta.createdAt,
|
|
2313
|
-
stats
|
|
2314
|
-
});
|
|
2315
|
-
} catch {
|
|
2316
|
-
} finally {
|
|
2317
|
-
processed += 1;
|
|
2318
|
-
options?.onProgress?.({ total: metas.length, processed, sessions: heads.length });
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
perf.end(scanMarker);
|
|
2322
|
-
return heads;
|
|
2323
|
-
}
|
|
2324
2239
|
listSessionSources(options) {
|
|
2325
2240
|
if (!this.basePath) return [];
|
|
2326
2241
|
const refs = [];
|
|
@@ -2361,9 +2276,8 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2361
2276
|
}
|
|
2362
2277
|
getSessionDataFromContext(meta) {
|
|
2363
2278
|
if (!meta.contextFile) throw new Error("context.jsonl is missing");
|
|
2364
|
-
const content =
|
|
2365
|
-
const
|
|
2366
|
-
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
2279
|
+
const content = readFileSync3(meta.contextFile, "utf-8");
|
|
2280
|
+
const builder = new TranscriptBuilder();
|
|
2367
2281
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
2368
2282
|
let seq = 0;
|
|
2369
2283
|
const fallbackTs = meta.createdAt;
|
|
@@ -2375,65 +2289,55 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2375
2289
|
if (role === "user") {
|
|
2376
2290
|
const text = cleanInternalText(kimiContentText(record.content));
|
|
2377
2291
|
if (text) {
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
})
|
|
2385
|
-
);
|
|
2292
|
+
builder.appendMessage({
|
|
2293
|
+
id: `context-${seq}`,
|
|
2294
|
+
role: "user",
|
|
2295
|
+
timestampMs: fallbackTs,
|
|
2296
|
+
parts: [{ type: "text", text, time_created: fallbackTs }]
|
|
2297
|
+
});
|
|
2386
2298
|
}
|
|
2387
2299
|
continue;
|
|
2388
2300
|
}
|
|
2389
2301
|
if (role === "assistant") {
|
|
2390
|
-
const
|
|
2302
|
+
const message = this.buildContextAssistantMessage(
|
|
2391
2303
|
record,
|
|
2392
2304
|
seq,
|
|
2393
2305
|
ignoredToolCallIds,
|
|
2394
2306
|
fallbackTs
|
|
2395
2307
|
);
|
|
2396
2308
|
if (!message) continue;
|
|
2397
|
-
|
|
2398
|
-
messages.push(message);
|
|
2399
|
-
for (const [callId, partIndex] of toolIndexes) {
|
|
2400
|
-
pendingToolCalls.set(callId, [msgIndex, partIndex]);
|
|
2401
|
-
}
|
|
2309
|
+
builder.appendMessage(message);
|
|
2402
2310
|
continue;
|
|
2403
2311
|
}
|
|
2404
2312
|
if (role === "tool") {
|
|
2405
2313
|
const callId = String(record.tool_call_id ?? "").trim();
|
|
2406
2314
|
if (callId && ignoredToolCallIds.has(callId)) continue;
|
|
2407
2315
|
const outputParts = normalizeToolOutputParts(record.content, fallbackTs);
|
|
2408
|
-
if (callId && this.backfillToolOutput(
|
|
2316
|
+
if (callId && this.backfillToolOutput(builder, callId, outputParts)) {
|
|
2409
2317
|
continue;
|
|
2410
2318
|
}
|
|
2411
2319
|
if (outputParts.length > 0) {
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
})
|
|
2419
|
-
);
|
|
2320
|
+
builder.appendMessage({
|
|
2321
|
+
id: `context-${seq}`,
|
|
2322
|
+
role: "tool",
|
|
2323
|
+
timestampMs: fallbackTs,
|
|
2324
|
+
parts: outputParts
|
|
2325
|
+
});
|
|
2420
2326
|
}
|
|
2421
2327
|
}
|
|
2422
2328
|
} catch {
|
|
2423
2329
|
}
|
|
2424
2330
|
}
|
|
2425
2331
|
const stats = this.extractStats(meta.sourcePath);
|
|
2426
|
-
return this.buildSessionData(meta,
|
|
2332
|
+
return this.buildSessionData(meta, builder, stats);
|
|
2427
2333
|
}
|
|
2428
2334
|
getSessionDataFromWire(meta) {
|
|
2429
2335
|
const wirePath = meta.wireFile ?? join6(meta.sourcePath, "wire.jsonl");
|
|
2430
2336
|
if (!existsSync6(wirePath)) throw new Error("wire.jsonl is missing");
|
|
2431
|
-
const content =
|
|
2432
|
-
const
|
|
2433
|
-
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
2337
|
+
const content = readFileSync3(wirePath, "utf-8");
|
|
2338
|
+
const builder = new TranscriptBuilder();
|
|
2434
2339
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
2435
2340
|
const openToolArgumentBuffer = /* @__PURE__ */ new Map();
|
|
2436
|
-
let currentAssistantIndex = null;
|
|
2437
2341
|
let openToolCallId = null;
|
|
2438
2342
|
let seq = 0;
|
|
2439
2343
|
for (const record of parseJsonlLines(content)) {
|
|
@@ -2450,19 +2354,13 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2450
2354
|
const inputTokens = Number(usage["input_tokens"] ?? 0);
|
|
2451
2355
|
const outputTokens = Number(usage["output_tokens"] ?? 0);
|
|
2452
2356
|
if (inputTokens || outputTokens) {
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
msg.cost = cost;
|
|
2461
|
-
msg.cost_source = "estimated";
|
|
2462
|
-
}
|
|
2463
|
-
break;
|
|
2464
|
-
}
|
|
2465
|
-
}
|
|
2357
|
+
const tokens = { input: inputTokens, output: outputTokens };
|
|
2358
|
+
const cost = estimateTokenCost(this.defaultModel, tokens);
|
|
2359
|
+
builder.attachUsageToLatestAssistant(tokens, {
|
|
2360
|
+
model: this.defaultModel,
|
|
2361
|
+
cost: cost ?? void 0,
|
|
2362
|
+
costSource: cost === null ? void 0 : "estimated"
|
|
2363
|
+
});
|
|
2466
2364
|
}
|
|
2467
2365
|
}
|
|
2468
2366
|
if (msgType === "TurnBegin") {
|
|
@@ -2470,37 +2368,37 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2470
2368
|
if (Array.isArray(userInput) && userInput.length > 0) {
|
|
2471
2369
|
const text = cleanInternalText(kimiContentText(userInput));
|
|
2472
2370
|
if (text) {
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
})
|
|
2480
|
-
);
|
|
2371
|
+
builder.appendMessage({
|
|
2372
|
+
id: `wire-${seq}`,
|
|
2373
|
+
role: "user",
|
|
2374
|
+
timestampMs,
|
|
2375
|
+
parts: [{ type: "text", text, time_created: timestampMs }]
|
|
2376
|
+
});
|
|
2481
2377
|
}
|
|
2482
2378
|
}
|
|
2483
|
-
|
|
2379
|
+
builder.beginTurn();
|
|
2484
2380
|
openToolCallId = null;
|
|
2485
2381
|
continue;
|
|
2486
2382
|
}
|
|
2487
2383
|
if (msgType === "ContentPart") {
|
|
2488
|
-
currentAssistantIndex = this.getOrCreateWireAssistant(
|
|
2489
|
-
messages,
|
|
2490
|
-
currentAssistantIndex,
|
|
2491
|
-
`wire-${seq}`
|
|
2492
|
-
);
|
|
2493
|
-
const assistant = messages[currentAssistantIndex];
|
|
2494
2384
|
const partType = String(payload.type ?? "");
|
|
2495
2385
|
if (partType === "think") {
|
|
2496
2386
|
const text = cleanInternalText(String(payload.think ?? ""));
|
|
2497
2387
|
if (text) {
|
|
2498
|
-
|
|
2388
|
+
builder.appendAssistantPart(
|
|
2389
|
+
{ type: "reasoning", text, time_created: timestampMs },
|
|
2390
|
+
{ id: `wire-${seq}`, timestampMs: 0, agent: "kimi" },
|
|
2391
|
+
{ grouping: "current" }
|
|
2392
|
+
);
|
|
2499
2393
|
}
|
|
2500
2394
|
} else if (partType === "text") {
|
|
2501
2395
|
const text = cleanInternalText(String(payload.text ?? ""));
|
|
2502
2396
|
if (text) {
|
|
2503
|
-
|
|
2397
|
+
builder.appendAssistantPart(
|
|
2398
|
+
{ type: "text", text, time_created: timestampMs },
|
|
2399
|
+
{ id: `wire-${seq}`, timestampMs: 0, agent: "kimi" },
|
|
2400
|
+
{ grouping: "current" }
|
|
2401
|
+
);
|
|
2504
2402
|
}
|
|
2505
2403
|
}
|
|
2506
2404
|
continue;
|
|
@@ -2515,12 +2413,6 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2515
2413
|
continue;
|
|
2516
2414
|
}
|
|
2517
2415
|
if (!function_ || !callId || !toolName) continue;
|
|
2518
|
-
currentAssistantIndex = this.getOrCreateWireAssistant(
|
|
2519
|
-
messages,
|
|
2520
|
-
currentAssistantIndex,
|
|
2521
|
-
`wire-${seq}`
|
|
2522
|
-
);
|
|
2523
|
-
const assistant = messages[currentAssistantIndex];
|
|
2524
2416
|
const rawArgs = function_.arguments;
|
|
2525
2417
|
const normalizedArgs = normalizeToolArguments(rawArgs);
|
|
2526
2418
|
const buffer = typeof rawArgs === "string" && typeof normalizedArgs !== "string" ? rawArgs : null;
|
|
@@ -2532,10 +2424,11 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2532
2424
|
state: { arguments: normalizedArgs, output: null },
|
|
2533
2425
|
time_created: timestampMs
|
|
2534
2426
|
};
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2427
|
+
builder.appendToolCall(
|
|
2428
|
+
toolPart,
|
|
2429
|
+
{ id: `wire-${seq}`, timestampMs: 0, agent: "kimi" },
|
|
2430
|
+
{ markModeAsTool: true, target: "current" }
|
|
2431
|
+
);
|
|
2539
2432
|
openToolCallId = callId;
|
|
2540
2433
|
if (buffer !== null) {
|
|
2541
2434
|
openToolArgumentBuffer.set(callId, buffer);
|
|
@@ -2549,8 +2442,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2549
2442
|
argumentsPart,
|
|
2550
2443
|
openToolCallId,
|
|
2551
2444
|
openToolArgumentBuffer,
|
|
2552
|
-
|
|
2553
|
-
pendingToolCalls
|
|
2445
|
+
builder
|
|
2554
2446
|
);
|
|
2555
2447
|
continue;
|
|
2556
2448
|
}
|
|
@@ -2558,27 +2450,24 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2558
2450
|
const callId = String(payload.tool_call_id ?? "").trim();
|
|
2559
2451
|
if (callId && ignoredToolCallIds.has(callId)) continue;
|
|
2560
2452
|
const outputParts = normalizeWireToolOutputParts(payload.return_value, timestampMs);
|
|
2561
|
-
if (callId && this.backfillToolOutput(
|
|
2453
|
+
if (callId && this.backfillToolOutput(builder, callId, outputParts)) {
|
|
2562
2454
|
continue;
|
|
2563
2455
|
}
|
|
2564
2456
|
if (outputParts.length > 0) {
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
})
|
|
2572
|
-
);
|
|
2457
|
+
builder.appendMessage({
|
|
2458
|
+
id: `wire-${seq}`,
|
|
2459
|
+
role: "tool",
|
|
2460
|
+
timestampMs,
|
|
2461
|
+
parts: outputParts
|
|
2462
|
+
});
|
|
2573
2463
|
}
|
|
2574
2464
|
continue;
|
|
2575
2465
|
}
|
|
2576
2466
|
} catch {
|
|
2577
2467
|
}
|
|
2578
2468
|
}
|
|
2579
|
-
const filteredMessages = messages.filter((m) => m.parts.length > 0);
|
|
2580
2469
|
const stats = this.extractStats(meta.sourcePath);
|
|
2581
|
-
return this.buildSessionData(meta,
|
|
2470
|
+
return this.buildSessionData(meta, builder, stats);
|
|
2582
2471
|
}
|
|
2583
2472
|
// --- Helpers ---
|
|
2584
2473
|
sourceFingerprint(meta) {
|
|
@@ -2596,23 +2485,8 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2596
2485
|
fileMtime(meta.wireFile)
|
|
2597
2486
|
]);
|
|
2598
2487
|
}
|
|
2599
|
-
buildMessage(opts) {
|
|
2600
|
-
return {
|
|
2601
|
-
id: opts.messageId,
|
|
2602
|
-
role: opts.role,
|
|
2603
|
-
agent: opts.agent ?? null,
|
|
2604
|
-
time_created: opts.timestampMs,
|
|
2605
|
-
mode: opts.mode ?? null,
|
|
2606
|
-
model: opts.model ?? null,
|
|
2607
|
-
provider: opts.provider ?? null,
|
|
2608
|
-
tokens: opts.tokens ? opts.tokens : void 0,
|
|
2609
|
-
cost: opts.cost ?? 0,
|
|
2610
|
-
parts: opts.parts
|
|
2611
|
-
};
|
|
2612
|
-
}
|
|
2613
2488
|
buildContextAssistantMessage(record, seq, ignoredToolCallIds, fallbackTs) {
|
|
2614
2489
|
const parts = [];
|
|
2615
|
-
const toolIndexes = /* @__PURE__ */ new Map();
|
|
2616
2490
|
const content = record.content;
|
|
2617
2491
|
if (Array.isArray(content)) {
|
|
2618
2492
|
for (const item of content) {
|
|
@@ -2650,71 +2524,41 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2650
2524
|
state: { arguments: normalizeToolArguments(function_.arguments), output: null },
|
|
2651
2525
|
time_created: fallbackTs
|
|
2652
2526
|
};
|
|
2653
|
-
toolIndexes.set(callId, parts.length);
|
|
2654
2527
|
parts.push(part);
|
|
2655
2528
|
}
|
|
2656
2529
|
}
|
|
2657
2530
|
if (parts.length === 0) {
|
|
2658
|
-
return
|
|
2659
|
-
message: this.buildMessage({
|
|
2660
|
-
messageId: `context-${seq}`,
|
|
2661
|
-
role: "assistant",
|
|
2662
|
-
timestampMs: fallbackTs,
|
|
2663
|
-
parts: []
|
|
2664
|
-
}),
|
|
2665
|
-
toolIndexes
|
|
2666
|
-
};
|
|
2531
|
+
return null;
|
|
2667
2532
|
}
|
|
2668
2533
|
const allTools = parts.every((p) => p.type === "tool");
|
|
2669
|
-
|
|
2670
|
-
|
|
2534
|
+
return {
|
|
2535
|
+
id: `context-${seq}`,
|
|
2671
2536
|
role: "assistant",
|
|
2672
2537
|
timestampMs: fallbackTs,
|
|
2673
2538
|
parts,
|
|
2674
2539
|
agent: "kimi",
|
|
2675
2540
|
mode: allTools ? "tool" : void 0
|
|
2676
|
-
}
|
|
2677
|
-
return { message, toolIndexes };
|
|
2678
|
-
}
|
|
2679
|
-
getOrCreateWireAssistant(messages, currentIndex, messageId) {
|
|
2680
|
-
if (currentIndex !== null) return currentIndex;
|
|
2681
|
-
messages.push(
|
|
2682
|
-
this.buildMessage({
|
|
2683
|
-
messageId,
|
|
2684
|
-
role: "assistant",
|
|
2685
|
-
timestampMs: 0,
|
|
2686
|
-
parts: [],
|
|
2687
|
-
agent: "kimi"
|
|
2688
|
-
})
|
|
2689
|
-
);
|
|
2690
|
-
return messages.length - 1;
|
|
2541
|
+
};
|
|
2691
2542
|
}
|
|
2692
|
-
appendWireToolCallPart(argumentsPart, openCallId, buffer,
|
|
2693
|
-
if (!openCallId
|
|
2543
|
+
appendWireToolCallPart(argumentsPart, openCallId, buffer, builder) {
|
|
2544
|
+
if (!openCallId) return;
|
|
2694
2545
|
const existing = buffer.get(openCallId) ?? "";
|
|
2695
2546
|
const combined = existing + argumentsPart;
|
|
2696
2547
|
try {
|
|
2697
|
-
const
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2548
|
+
const parsed = JSON.parse(combined);
|
|
2549
|
+
if (builder.updateToolCall(openCallId, (part) => {
|
|
2550
|
+
const state = part.state ?? (part.state = {});
|
|
2551
|
+
state.arguments = parsed;
|
|
2552
|
+
})) {
|
|
2553
|
+
buffer.delete(openCallId);
|
|
2703
2554
|
}
|
|
2704
|
-
buffer.delete(openCallId);
|
|
2705
2555
|
} catch {
|
|
2706
2556
|
buffer.set(openCallId, combined);
|
|
2707
2557
|
}
|
|
2708
2558
|
}
|
|
2709
|
-
backfillToolOutput(
|
|
2559
|
+
backfillToolOutput(builder, callId, outputParts) {
|
|
2710
2560
|
if (!outputParts.length || !callId) return false;
|
|
2711
|
-
|
|
2712
|
-
if (!location) return false;
|
|
2713
|
-
const part = messages[location[0]]?.parts[location[1]];
|
|
2714
|
-
if (!part) return false;
|
|
2715
|
-
if (!part.state) part.state = {};
|
|
2716
|
-
part.state.output = [...outputParts];
|
|
2717
|
-
return true;
|
|
2561
|
+
return builder.resolveToolCall(callId, { output: [...outputParts] });
|
|
2718
2562
|
}
|
|
2719
2563
|
extractStats(sessionDir) {
|
|
2720
2564
|
let totalCost = 0;
|
|
@@ -2728,7 +2572,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2728
2572
|
const wirePath = join6(sessionDir, "wire.jsonl");
|
|
2729
2573
|
if (!existsSync6(wirePath)) return stats;
|
|
2730
2574
|
try {
|
|
2731
|
-
const content =
|
|
2575
|
+
const content = readFileSync3(wirePath, "utf-8");
|
|
2732
2576
|
for (const line of content.split("\n").filter((l) => l.trim())) {
|
|
2733
2577
|
try {
|
|
2734
2578
|
const data = JSON.parse(line);
|
|
@@ -2752,7 +2596,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2752
2596
|
const rawPath = existsSync6(contextPath) ? contextPath : wirePath;
|
|
2753
2597
|
if (!existsSync6(rawPath)) return stats;
|
|
2754
2598
|
try {
|
|
2755
|
-
const rawContent =
|
|
2599
|
+
const rawContent = readFileSync3(rawPath, "utf-8");
|
|
2756
2600
|
for (const line of rawContent.split("\n").filter((l) => l.trim())) {
|
|
2757
2601
|
try {
|
|
2758
2602
|
const data = JSON.parse(line);
|
|
@@ -2770,14 +2614,8 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2770
2614
|
}
|
|
2771
2615
|
return stats;
|
|
2772
2616
|
}
|
|
2773
|
-
buildSessionData(meta,
|
|
2774
|
-
const
|
|
2775
|
-
stats.message_count = cleanedMessages.length;
|
|
2776
|
-
const totalCost = cleanedMessages.reduce((sum, message) => sum + (message.cost ?? 0), 0);
|
|
2777
|
-
if (totalCost > 0) {
|
|
2778
|
-
stats.total_cost = Number(totalCost.toFixed(8));
|
|
2779
|
-
stats.cost_source = "estimated";
|
|
2780
|
-
}
|
|
2617
|
+
buildSessionData(meta, builder, stats) {
|
|
2618
|
+
const transcript = builder.finish(stats);
|
|
2781
2619
|
return {
|
|
2782
2620
|
id: meta.id,
|
|
2783
2621
|
title: meta.title,
|
|
@@ -2785,8 +2623,8 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2785
2623
|
directory: meta.cwd,
|
|
2786
2624
|
time_created: meta.createdAt,
|
|
2787
2625
|
time_updated: meta.createdAt,
|
|
2788
|
-
stats,
|
|
2789
|
-
messages:
|
|
2626
|
+
stats: transcript.stats,
|
|
2627
|
+
messages: transcript.messages
|
|
2790
2628
|
};
|
|
2791
2629
|
}
|
|
2792
2630
|
};
|
|
@@ -2974,36 +2812,6 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
2974
2812
|
return false;
|
|
2975
2813
|
}
|
|
2976
2814
|
}
|
|
2977
|
-
scan(options) {
|
|
2978
|
-
if (!this.basePath) return [];
|
|
2979
|
-
const scanMarker = perf.start("codex:scan");
|
|
2980
|
-
const indexMarker = perf.start("loadSessionIndex");
|
|
2981
|
-
this.loadSessionIndex();
|
|
2982
|
-
perf.end(indexMarker);
|
|
2983
|
-
const heads = [];
|
|
2984
|
-
const listMarker = perf.start("listRolloutFiles");
|
|
2985
|
-
const files = this.listRolloutFiles(options);
|
|
2986
|
-
perf.end(listMarker);
|
|
2987
|
-
options?.onProgress?.({ total: files.length, processed: 0, sessions: 0 });
|
|
2988
|
-
let processed = 0;
|
|
2989
|
-
for (const file of files) {
|
|
2990
|
-
try {
|
|
2991
|
-
const parseMarker = perf.start(`parseSessionHead:${basename5(file)}`);
|
|
2992
|
-
const head = getParsedSession(this.parseSessionHeadResult(file, options));
|
|
2993
|
-
perf.end(parseMarker);
|
|
2994
|
-
if (head) {
|
|
2995
|
-
heads.push(head);
|
|
2996
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, file));
|
|
2997
|
-
}
|
|
2998
|
-
} catch {
|
|
2999
|
-
} finally {
|
|
3000
|
-
processed += 1;
|
|
3001
|
-
options?.onProgress?.({ total: files.length, processed, sessions: heads.length });
|
|
3002
|
-
}
|
|
3003
|
-
}
|
|
3004
|
-
perf.end(scanMarker);
|
|
3005
|
-
return heads;
|
|
3006
|
-
}
|
|
3007
2815
|
listSessionSources(options) {
|
|
3008
2816
|
if (!this.basePath) return [];
|
|
3009
2817
|
this.loadSessionIndex();
|
|
@@ -3013,9 +2821,9 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3013
2821
|
fingerprint: this.sourceFingerprint(file)
|
|
3014
2822
|
}));
|
|
3015
2823
|
}
|
|
3016
|
-
scanSessionSource(sourcePath) {
|
|
2824
|
+
scanSessionSource(sourcePath, options) {
|
|
3017
2825
|
this.loadSessionIndex();
|
|
3018
|
-
const head = getParsedSession(this.parseSessionHeadResult(sourcePath));
|
|
2826
|
+
const head = getParsedSession(this.parseSessionHeadResult(sourcePath, options));
|
|
3019
2827
|
if (head) {
|
|
3020
2828
|
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, sourcePath));
|
|
3021
2829
|
}
|
|
@@ -3025,15 +2833,11 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3025
2833
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
3026
2834
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
3027
2835
|
if (!existsSync7(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
3028
|
-
const
|
|
3029
|
-
const messages = [];
|
|
3030
|
-
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
2836
|
+
const transcript = new TranscriptBuilder();
|
|
3031
2837
|
let totalInputTokens = 0;
|
|
3032
2838
|
let totalOutputTokens = 0;
|
|
3033
2839
|
let totalCacheReadTokens = 0;
|
|
3034
2840
|
let totalCost = 0;
|
|
3035
|
-
let currentAssistantIndex = null;
|
|
3036
|
-
let latestAssistantTextIndex = null;
|
|
3037
2841
|
let pendingPlan = null;
|
|
3038
2842
|
let activeModel = meta.model;
|
|
3039
2843
|
let prevCumulativeTotal = 0;
|
|
@@ -3041,31 +2845,14 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3041
2845
|
let prevOutput = 0;
|
|
3042
2846
|
let prevReasoning = 0;
|
|
3043
2847
|
let prevCachedInput = 0;
|
|
3044
|
-
for (const record of
|
|
2848
|
+
for (const record of readJsonlFile(meta.sourcePath)) {
|
|
3045
2849
|
try {
|
|
3046
2850
|
const recordType = String(record["type"] ?? "");
|
|
3047
2851
|
if (recordType === "turn_context") {
|
|
3048
2852
|
const payload = record["payload"] ?? {};
|
|
3049
2853
|
activeModel = extractModelName(payload["model"]) ?? activeModel;
|
|
3050
2854
|
}
|
|
3051
|
-
|
|
3052
|
-
record,
|
|
3053
|
-
messages,
|
|
3054
|
-
pendingToolCalls,
|
|
3055
|
-
meta.id,
|
|
3056
|
-
currentAssistantIndex,
|
|
3057
|
-
latestAssistantTextIndex,
|
|
3058
|
-
pendingPlan
|
|
3059
|
-
);
|
|
3060
|
-
currentAssistantIndex = result.currentAssistantIndex;
|
|
3061
|
-
latestAssistantTextIndex = result.latestAssistantTextIndex;
|
|
3062
|
-
pendingPlan = result.pendingPlan;
|
|
3063
|
-
if (currentAssistantIndex !== null && activeModel) {
|
|
3064
|
-
const message = messages[currentAssistantIndex];
|
|
3065
|
-
if (message?.role === "assistant" && !message.model) {
|
|
3066
|
-
message.model = activeModel;
|
|
3067
|
-
}
|
|
3068
|
-
}
|
|
2855
|
+
pendingPlan = this.convertRecord(record, transcript, pendingPlan, activeModel);
|
|
3069
2856
|
if (recordType === "event_msg") {
|
|
3070
2857
|
const payload = record["payload"] ?? {};
|
|
3071
2858
|
if (String(payload["type"] ?? "") === "token_count") {
|
|
@@ -3101,24 +2888,19 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3101
2888
|
totalInputTokens += totalInput;
|
|
3102
2889
|
totalOutputTokens += outputTokens + reasoningTokens;
|
|
3103
2890
|
totalCacheReadTokens += totalCacheRead;
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
totalCost += cost;
|
|
3118
|
-
}
|
|
3119
|
-
break;
|
|
3120
|
-
}
|
|
3121
|
-
}
|
|
2891
|
+
const tokens = {
|
|
2892
|
+
input: totalInput,
|
|
2893
|
+
output: outputTokens,
|
|
2894
|
+
reasoning: reasoningTokens || void 0,
|
|
2895
|
+
cache_read: totalCacheRead || void 0
|
|
2896
|
+
};
|
|
2897
|
+
const cost = estimateTokenCost(activeModel, tokens);
|
|
2898
|
+
transcript.attachUsageToLatestAssistant(tokens, {
|
|
2899
|
+
model: activeModel,
|
|
2900
|
+
cost: cost ?? void 0,
|
|
2901
|
+
costSource: cost === null ? void 0 : "estimated"
|
|
2902
|
+
});
|
|
2903
|
+
totalCost += cost ?? 0;
|
|
3122
2904
|
}
|
|
3123
2905
|
}
|
|
3124
2906
|
}
|
|
@@ -3126,10 +2908,15 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3126
2908
|
} catch {
|
|
3127
2909
|
}
|
|
3128
2910
|
}
|
|
3129
|
-
if (pendingPlan
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
2911
|
+
if (pendingPlan) transcript.appendToCurrentAssistant(pendingPlan);
|
|
2912
|
+
const result = transcript.finish({
|
|
2913
|
+
message_count: 0,
|
|
2914
|
+
total_input_tokens: totalInputTokens,
|
|
2915
|
+
total_output_tokens: totalOutputTokens,
|
|
2916
|
+
total_cache_read_tokens: totalCacheReadTokens || void 0,
|
|
2917
|
+
total_cost: totalCost,
|
|
2918
|
+
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
2919
|
+
});
|
|
3133
2920
|
return {
|
|
3134
2921
|
id: meta.id,
|
|
3135
2922
|
title: meta.title,
|
|
@@ -3137,15 +2924,8 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3137
2924
|
directory: meta.directory,
|
|
3138
2925
|
time_created: meta.createdAt,
|
|
3139
2926
|
time_updated: meta.updatedAt,
|
|
3140
|
-
stats:
|
|
3141
|
-
|
|
3142
|
-
total_input_tokens: totalInputTokens,
|
|
3143
|
-
total_output_tokens: totalOutputTokens,
|
|
3144
|
-
total_cache_read_tokens: totalCacheReadTokens || void 0,
|
|
3145
|
-
total_cost: totalCost,
|
|
3146
|
-
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
3147
|
-
},
|
|
3148
|
-
messages: cleanedMessages
|
|
2927
|
+
stats: result.stats,
|
|
2928
|
+
messages: result.messages
|
|
3149
2929
|
};
|
|
3150
2930
|
}
|
|
3151
2931
|
// ---- File listing ----
|
|
@@ -3229,7 +3009,7 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3229
3009
|
this.sessionIndexMtime = mtime;
|
|
3230
3010
|
if (mtime === null) return;
|
|
3231
3011
|
try {
|
|
3232
|
-
const content =
|
|
3012
|
+
const content = readFileSync4(indexPath, "utf-8");
|
|
3233
3013
|
for (const record of parseJsonlLines(content)) {
|
|
3234
3014
|
const sid = String(record["id"] ?? "").trim();
|
|
3235
3015
|
const threadName = String(record["thread_name"] ?? "").trim();
|
|
@@ -3246,13 +3026,13 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3246
3026
|
}
|
|
3247
3027
|
// ---- Session head parsing ----
|
|
3248
3028
|
readFilePrefix(filePath, bytes = 64 * 1024) {
|
|
3249
|
-
const fd =
|
|
3029
|
+
const fd = openSync2(filePath, "r");
|
|
3250
3030
|
try {
|
|
3251
3031
|
const buffer = Buffer.alloc(bytes);
|
|
3252
|
-
const bytesRead =
|
|
3032
|
+
const bytesRead = readSync2(fd, buffer, 0, bytes, 0);
|
|
3253
3033
|
return buffer.subarray(0, bytesRead).toString("utf-8");
|
|
3254
3034
|
} finally {
|
|
3255
|
-
|
|
3035
|
+
closeSync2(fd);
|
|
3256
3036
|
}
|
|
3257
3037
|
}
|
|
3258
3038
|
parseSessionHead(filePath, options) {
|
|
@@ -3262,23 +3042,12 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3262
3042
|
if (options?.fast) {
|
|
3263
3043
|
return this.parseFastSessionHeadResult(filePath);
|
|
3264
3044
|
}
|
|
3265
|
-
const content = readFileSync5(filePath, "utf-8");
|
|
3266
|
-
const lines = content.split("\n").filter((l) => l.trim());
|
|
3267
|
-
if (lines.length === 0) return skippedSession("empty file");
|
|
3268
3045
|
const sessionId = extractSessionId(filePath);
|
|
3269
|
-
let
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
}
|
|
3275
|
-
const payload = firstRecord["payload"] ?? {};
|
|
3276
|
-
const createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(payload) || statSync4(filePath).mtimeMs;
|
|
3277
|
-
const indexTitle = this.getTitleForSession(sessionId);
|
|
3278
|
-
const messageTitle = this.extractTitleFromLines(lines);
|
|
3279
|
-
const directoryTitle = basenameTitle(payload["cwd"] ? String(payload["cwd"]) : null);
|
|
3280
|
-
const title = resolveSessionTitle(indexTitle, messageTitle, directoryTitle);
|
|
3281
|
-
let updatedAt = createdAt;
|
|
3046
|
+
let firstPayload = {};
|
|
3047
|
+
let createdAt = 0;
|
|
3048
|
+
let lineCount = 0;
|
|
3049
|
+
const titleLines = [];
|
|
3050
|
+
let updatedAt = 0;
|
|
3282
3051
|
let messageCount = 0;
|
|
3283
3052
|
let model = null;
|
|
3284
3053
|
let activeModel = null;
|
|
@@ -3294,18 +3063,31 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3294
3063
|
let scanPrevCachedInput = 0;
|
|
3295
3064
|
const COUNTED_TYPES = /* @__PURE__ */ new Set(["message", "function_call", "function_call_output"]);
|
|
3296
3065
|
let hasNonInternalRecord = false;
|
|
3297
|
-
for (const line of
|
|
3066
|
+
for (const line of readJsonlFileLines(filePath)) {
|
|
3067
|
+
lineCount += 1;
|
|
3068
|
+
if (lineCount === 1) {
|
|
3069
|
+
let firstRecord;
|
|
3070
|
+
try {
|
|
3071
|
+
firstRecord = JSON.parse(line);
|
|
3072
|
+
} catch {
|
|
3073
|
+
return skippedSession("malformed first record");
|
|
3074
|
+
}
|
|
3075
|
+
firstPayload = firstRecord["payload"] ?? {};
|
|
3076
|
+
createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(firstPayload) || statSync4(filePath).mtimeMs;
|
|
3077
|
+
updatedAt = createdAt;
|
|
3078
|
+
}
|
|
3079
|
+
if (titleLines.length < 20) titleLines.push(line);
|
|
3298
3080
|
try {
|
|
3299
3081
|
const data = JSON.parse(line);
|
|
3300
3082
|
const recordType = String(data["type"] ?? "");
|
|
3301
|
-
const
|
|
3302
|
-
const payloadType = String(
|
|
3083
|
+
const payload = data["payload"] ?? {};
|
|
3084
|
+
const payloadType = String(payload["type"] ?? "");
|
|
3303
3085
|
if (isInternalEventType2(recordType) || isInternalEventType2(payloadType)) continue;
|
|
3304
3086
|
hasNonInternalRecord = true;
|
|
3305
3087
|
const recordTs = parseTimestampMs2(data) || parseTimestampMs2(data["payload"] ?? {});
|
|
3306
3088
|
if (recordTs > updatedAt) updatedAt = recordTs;
|
|
3307
3089
|
if (recordType === "session_meta" || recordType === "turn_context") {
|
|
3308
|
-
const nextModel = extractModelName(
|
|
3090
|
+
const nextModel = extractModelName(payload["model"]);
|
|
3309
3091
|
if (nextModel) {
|
|
3310
3092
|
activeModel = nextModel;
|
|
3311
3093
|
model ??= nextModel;
|
|
@@ -3313,7 +3095,7 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3313
3095
|
continue;
|
|
3314
3096
|
}
|
|
3315
3097
|
if (recordType === "response_item") {
|
|
3316
|
-
const p =
|
|
3098
|
+
const p = payload;
|
|
3317
3099
|
const pType = String(p["type"] ?? "");
|
|
3318
3100
|
if (COUNTED_TYPES.has(pType)) {
|
|
3319
3101
|
messageCount++;
|
|
@@ -3375,8 +3157,12 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3375
3157
|
} catch {
|
|
3376
3158
|
}
|
|
3377
3159
|
}
|
|
3160
|
+
if (lineCount === 0) return skippedSession("empty file");
|
|
3378
3161
|
if (!hasNonInternalRecord) return filteredSession("internal events only");
|
|
3379
|
-
const
|
|
3162
|
+
const indexTitle = this.getTitleForSession(sessionId);
|
|
3163
|
+
const messageTitle = this.extractTitleFromLines(titleLines);
|
|
3164
|
+
const directory = firstPayload["cwd"] ? String(firstPayload["cwd"]) : "";
|
|
3165
|
+
const title = resolveSessionTitle(indexTitle, messageTitle, basenameTitle(directory || null));
|
|
3380
3166
|
return parsedSession({
|
|
3381
3167
|
id: sessionId,
|
|
3382
3168
|
slug: `codex/${sessionId}`,
|
|
@@ -3458,22 +3244,16 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3458
3244
|
return null;
|
|
3459
3245
|
}
|
|
3460
3246
|
// ---- Record conversion ----
|
|
3461
|
-
convertRecord(data,
|
|
3247
|
+
convertRecord(data, transcript, pendingPlan, activeModel) {
|
|
3462
3248
|
const recordType = String(data["type"] ?? "");
|
|
3463
|
-
if (isInternalEventType2(recordType))
|
|
3464
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3465
|
-
}
|
|
3249
|
+
if (isInternalEventType2(recordType)) return pendingPlan;
|
|
3466
3250
|
if (recordType === "session_meta" || recordType === "event_msg") {
|
|
3467
|
-
return
|
|
3468
|
-
}
|
|
3469
|
-
if (recordType !== "response_item") {
|
|
3470
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3251
|
+
return pendingPlan;
|
|
3471
3252
|
}
|
|
3253
|
+
if (recordType !== "response_item") return pendingPlan;
|
|
3472
3254
|
const payload = data["payload"] ?? {};
|
|
3473
3255
|
const payloadType = String(payload["type"] ?? "");
|
|
3474
|
-
if (isInternalEventType2(payloadType))
|
|
3475
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3476
|
-
}
|
|
3256
|
+
if (isInternalEventType2(payloadType)) return pendingPlan;
|
|
3477
3257
|
const timestampMs = parseTimestampMs2(data) || parseTimestampMs2(payload);
|
|
3478
3258
|
switch (payloadType) {
|
|
3479
3259
|
case "message": {
|
|
@@ -3481,60 +3261,39 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3481
3261
|
if (role === "assistant") {
|
|
3482
3262
|
return this.convertAssistantMessage(
|
|
3483
3263
|
payload,
|
|
3484
|
-
|
|
3264
|
+
transcript,
|
|
3485
3265
|
timestampMs,
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
pendingPlan
|
|
3266
|
+
pendingPlan,
|
|
3267
|
+
activeModel
|
|
3489
3268
|
);
|
|
3490
3269
|
}
|
|
3491
3270
|
if (role === "user") {
|
|
3492
|
-
return this.convertUserMessage(
|
|
3493
|
-
payload,
|
|
3494
|
-
messages,
|
|
3495
|
-
timestampMs,
|
|
3496
|
-
currentAssistantIndex,
|
|
3497
|
-
latestAssistantTextIndex,
|
|
3498
|
-
pendingPlan
|
|
3499
|
-
);
|
|
3271
|
+
return this.convertUserMessage(payload, transcript, timestampMs, pendingPlan);
|
|
3500
3272
|
}
|
|
3501
3273
|
break;
|
|
3502
3274
|
}
|
|
3503
3275
|
case "reasoning":
|
|
3504
|
-
|
|
3276
|
+
this.convertReasoning(payload, transcript, timestampMs, activeModel);
|
|
3277
|
+
return null;
|
|
3505
3278
|
case "function_call":
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
messages,
|
|
3509
|
-
pendingToolCalls,
|
|
3510
|
-
timestampMs,
|
|
3511
|
-
currentAssistantIndex,
|
|
3512
|
-
latestAssistantTextIndex
|
|
3513
|
-
);
|
|
3279
|
+
this.convertFunctionCall(payload, transcript, timestampMs, activeModel);
|
|
3280
|
+
return null;
|
|
3514
3281
|
case "function_call_output":
|
|
3515
|
-
this.
|
|
3516
|
-
return
|
|
3282
|
+
this.convertToolCallOutput(payload, transcript, timestampMs);
|
|
3283
|
+
return pendingPlan;
|
|
3517
3284
|
case "custom_tool_call":
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
messages,
|
|
3521
|
-
pendingToolCalls,
|
|
3522
|
-
timestampMs,
|
|
3523
|
-
currentAssistantIndex,
|
|
3524
|
-
latestAssistantTextIndex
|
|
3525
|
-
);
|
|
3285
|
+
this.convertCustomToolCall(payload, transcript, timestampMs, activeModel);
|
|
3286
|
+
return null;
|
|
3526
3287
|
case "custom_tool_call_output":
|
|
3527
|
-
this.
|
|
3528
|
-
return
|
|
3288
|
+
this.convertToolCallOutput(payload, transcript, timestampMs);
|
|
3289
|
+
return pendingPlan;
|
|
3529
3290
|
}
|
|
3530
|
-
return
|
|
3291
|
+
return pendingPlan;
|
|
3531
3292
|
}
|
|
3532
3293
|
// ---- Assistant message ----
|
|
3533
|
-
convertAssistantMessage(payload,
|
|
3294
|
+
convertAssistantMessage(payload, transcript, timestampMs, pendingPlan, activeModel) {
|
|
3534
3295
|
const content = payload["content"];
|
|
3535
|
-
if (!Array.isArray(content))
|
|
3536
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3537
|
-
}
|
|
3296
|
+
if (!Array.isArray(content)) return pendingPlan;
|
|
3538
3297
|
const textParts = [];
|
|
3539
3298
|
for (const item of content) {
|
|
3540
3299
|
if (typeof item !== "object" || item === null) continue;
|
|
@@ -3544,9 +3303,7 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3544
3303
|
if (text.trim()) textParts.push(text);
|
|
3545
3304
|
}
|
|
3546
3305
|
}
|
|
3547
|
-
if (textParts.length === 0)
|
|
3548
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3549
|
-
}
|
|
3306
|
+
if (textParts.length === 0) return pendingPlan;
|
|
3550
3307
|
const fullText = textParts.join("\n");
|
|
3551
3308
|
const planMatch = fullText.match(PROPOSED_PLAN_PATTERN);
|
|
3552
3309
|
if (planMatch) {
|
|
@@ -3560,61 +3317,34 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3560
3317
|
pendingPlan = planPart;
|
|
3561
3318
|
}
|
|
3562
3319
|
const displayText = cleanInternalText(fullText.replace(PROPOSED_PLAN_PATTERN, ""));
|
|
3563
|
-
if (!displayText)
|
|
3564
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3565
|
-
}
|
|
3320
|
+
if (!displayText) return pendingPlan;
|
|
3566
3321
|
const textPart = { type: "text", text: displayText, time_created: timestampMs };
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
}
|
|
3575
|
-
}
|
|
3576
|
-
messages.push(
|
|
3577
|
-
this.buildMessage({
|
|
3578
|
-
messageId: "",
|
|
3579
|
-
role: "assistant",
|
|
3580
|
-
timestampMs,
|
|
3581
|
-
parts: [textPart],
|
|
3582
|
-
agent: "codex"
|
|
3583
|
-
})
|
|
3584
|
-
);
|
|
3585
|
-
currentAssistantIndex = messages.length - 1;
|
|
3586
|
-
latestAssistantTextIndex = currentAssistantIndex;
|
|
3587
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3322
|
+
transcript.appendAssistantPart(textPart, {
|
|
3323
|
+
id: "",
|
|
3324
|
+
timestampMs,
|
|
3325
|
+
agent: "codex",
|
|
3326
|
+
model: activeModel
|
|
3327
|
+
});
|
|
3328
|
+
return pendingPlan;
|
|
3588
3329
|
}
|
|
3589
3330
|
// ---- User message ----
|
|
3590
|
-
convertUserMessage(payload,
|
|
3331
|
+
convertUserMessage(payload, transcript, timestampMs, pendingPlan) {
|
|
3591
3332
|
const content = payload["content"];
|
|
3592
3333
|
const text = Array.isArray(content) ? content.map(
|
|
3593
3334
|
(c) => typeof c === "object" && c !== null ? String(c["text"] ?? "") : String(c ?? "")
|
|
3594
3335
|
).join(" ") : String(content ?? "");
|
|
3595
3336
|
const visibleText = cleanInternalText(text);
|
|
3596
|
-
if (!visibleText)
|
|
3597
|
-
|
|
3598
|
-
}
|
|
3599
|
-
if (isDeveloperLikeUserMessage(visibleText)) {
|
|
3600
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3601
|
-
}
|
|
3337
|
+
if (!visibleText) return pendingPlan;
|
|
3338
|
+
if (isDeveloperLikeUserMessage(visibleText)) return pendingPlan;
|
|
3602
3339
|
if (visibleText.trimStart().startsWith(PLAN_APPROVAL_PREFIX)) {
|
|
3603
|
-
if (pendingPlan
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
timestampMs,
|
|
3612
|
-
parts: [{ type: "text", text: visibleText, time_created: timestampMs }]
|
|
3613
|
-
})
|
|
3614
|
-
);
|
|
3615
|
-
currentAssistantIndex = null;
|
|
3616
|
-
latestAssistantTextIndex = null;
|
|
3617
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3340
|
+
if (pendingPlan) transcript.appendToCurrentAssistant(pendingPlan);
|
|
3341
|
+
transcript.appendMessage({
|
|
3342
|
+
id: "",
|
|
3343
|
+
role: "user",
|
|
3344
|
+
timestampMs,
|
|
3345
|
+
parts: [{ type: "text", text: visibleText, time_created: timestampMs }]
|
|
3346
|
+
});
|
|
3347
|
+
return null;
|
|
3618
3348
|
}
|
|
3619
3349
|
const subagentMatch = visibleText.match(SUBAGENT_NOTIFICATION_PATTERN);
|
|
3620
3350
|
if (subagentMatch) {
|
|
@@ -3628,41 +3358,32 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3628
3358
|
text: completedText || `Subagent ${nickname} completed`,
|
|
3629
3359
|
time_created: timestampMs
|
|
3630
3360
|
};
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
currentAssistantIndex = null;
|
|
3643
|
-
latestAssistantTextIndex = null;
|
|
3644
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3361
|
+
transcript.appendMessage({
|
|
3362
|
+
id: "",
|
|
3363
|
+
role: "assistant",
|
|
3364
|
+
timestampMs,
|
|
3365
|
+
parts: [textPart],
|
|
3366
|
+
agent: "codex",
|
|
3367
|
+
subagentId: agentId || void 0,
|
|
3368
|
+
nickname: nickname || void 0
|
|
3369
|
+
});
|
|
3370
|
+
transcript.beginTurn();
|
|
3371
|
+
return pendingPlan;
|
|
3645
3372
|
} catch {
|
|
3646
3373
|
}
|
|
3647
3374
|
}
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
);
|
|
3656
|
-
currentAssistantIndex = null;
|
|
3657
|
-
latestAssistantTextIndex = null;
|
|
3658
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan };
|
|
3375
|
+
transcript.appendMessage({
|
|
3376
|
+
id: "",
|
|
3377
|
+
role: "user",
|
|
3378
|
+
timestampMs,
|
|
3379
|
+
parts: [{ type: "text", text: visibleText, time_created: timestampMs }]
|
|
3380
|
+
});
|
|
3381
|
+
return pendingPlan;
|
|
3659
3382
|
}
|
|
3660
3383
|
// ---- Reasoning ----
|
|
3661
|
-
convertReasoning(payload,
|
|
3384
|
+
convertReasoning(payload, transcript, timestampMs, activeModel) {
|
|
3662
3385
|
const summary = payload["summary"];
|
|
3663
|
-
if (!Array.isArray(summary))
|
|
3664
|
-
return { currentAssistantIndex, latestAssistantTextIndex: null, pendingPlan: null };
|
|
3665
|
-
}
|
|
3386
|
+
if (!Array.isArray(summary)) return;
|
|
3666
3387
|
const texts = [];
|
|
3667
3388
|
for (const item of summary) {
|
|
3668
3389
|
if (typeof item === "object" && item !== null) {
|
|
@@ -3673,42 +3394,25 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3673
3394
|
}
|
|
3674
3395
|
}
|
|
3675
3396
|
}
|
|
3676
|
-
if (texts.length === 0)
|
|
3677
|
-
return { currentAssistantIndex, latestAssistantTextIndex: null, pendingPlan: null };
|
|
3678
|
-
}
|
|
3397
|
+
if (texts.length === 0) return;
|
|
3679
3398
|
const reasoningText = texts.join("\n");
|
|
3680
3399
|
const part = { type: "reasoning", text: reasoningText, time_created: timestampMs };
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
if (!hasText && !hasTool) {
|
|
3686
|
-
message.parts.push(part);
|
|
3687
|
-
return { currentAssistantIndex, latestAssistantTextIndex: null, pendingPlan: null };
|
|
3688
|
-
}
|
|
3689
|
-
}
|
|
3690
|
-
messages.push(
|
|
3691
|
-
this.buildMessage({
|
|
3692
|
-
messageId: "",
|
|
3693
|
-
role: "assistant",
|
|
3400
|
+
transcript.appendAssistantPart(
|
|
3401
|
+
part,
|
|
3402
|
+
{
|
|
3403
|
+
id: "",
|
|
3694
3404
|
timestampMs,
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
}
|
|
3405
|
+
agent: "codex",
|
|
3406
|
+
model: activeModel
|
|
3407
|
+
},
|
|
3408
|
+
{ resetLatestText: true }
|
|
3698
3409
|
);
|
|
3699
|
-
return {
|
|
3700
|
-
currentAssistantIndex: messages.length - 1,
|
|
3701
|
-
latestAssistantTextIndex: null,
|
|
3702
|
-
pendingPlan: null
|
|
3703
|
-
};
|
|
3704
3410
|
}
|
|
3705
3411
|
// ---- Function call ----
|
|
3706
|
-
convertFunctionCall(payload,
|
|
3412
|
+
convertFunctionCall(payload, transcript, timestampMs, activeModel) {
|
|
3707
3413
|
const callId = String(payload["call_id"] ?? "").trim();
|
|
3708
3414
|
const name = String(payload["name"] ?? "").trim();
|
|
3709
|
-
if (!name)
|
|
3710
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan: null };
|
|
3711
|
-
}
|
|
3415
|
+
if (!name) return;
|
|
3712
3416
|
const toolIdentity = resolveToolIdentity(name, payload["namespace"]);
|
|
3713
3417
|
const arguments_ = normalizeToolArguments2(payload["arguments"]);
|
|
3714
3418
|
const toolPart = {
|
|
@@ -3723,59 +3427,27 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3723
3427
|
},
|
|
3724
3428
|
time_created: timestampMs
|
|
3725
3429
|
};
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
message.parts.push(toolPart);
|
|
3731
|
-
message.mode = "tool";
|
|
3732
|
-
if (callId) {
|
|
3733
|
-
pendingToolCalls.set(callId, [targetIndex, partIndex]);
|
|
3734
|
-
}
|
|
3735
|
-
return {
|
|
3736
|
-
currentAssistantIndex: targetIndex,
|
|
3737
|
-
latestAssistantTextIndex: targetIndex,
|
|
3738
|
-
pendingPlan: null
|
|
3739
|
-
};
|
|
3740
|
-
}
|
|
3741
|
-
messages.push(
|
|
3742
|
-
this.buildMessage({
|
|
3743
|
-
messageId: "",
|
|
3744
|
-
role: "assistant",
|
|
3745
|
-
timestampMs,
|
|
3746
|
-
parts: [toolPart],
|
|
3747
|
-
agent: "codex",
|
|
3748
|
-
mode: "tool"
|
|
3749
|
-
})
|
|
3430
|
+
transcript.appendToolCall(
|
|
3431
|
+
toolPart,
|
|
3432
|
+
{ id: "", timestampMs, agent: "codex", model: activeModel },
|
|
3433
|
+
{ markModeAsTool: true }
|
|
3750
3434
|
);
|
|
3751
|
-
const newIndex = messages.length - 1;
|
|
3752
|
-
if (callId) {
|
|
3753
|
-
pendingToolCalls.set(callId, [newIndex, 0]);
|
|
3754
|
-
}
|
|
3755
|
-
return { currentAssistantIndex: newIndex, latestAssistantTextIndex: null, pendingPlan: null };
|
|
3756
3435
|
}
|
|
3757
3436
|
// ---- Function call output ----
|
|
3758
|
-
|
|
3437
|
+
convertToolCallOutput(payload, transcript, timestampMs) {
|
|
3759
3438
|
const callId = String(payload["call_id"] ?? "").trim();
|
|
3760
3439
|
if (!callId) return;
|
|
3761
|
-
const location = pendingToolCalls.get(callId);
|
|
3762
|
-
if (!location) return;
|
|
3763
3440
|
const outputText = cleanInternalText(String(payload["output"] ?? ""));
|
|
3764
3441
|
const outputParts = outputText ? [{ type: "text", text: outputText, time_created: timestampMs }] : [];
|
|
3765
|
-
const [msgIndex, partIndex] = location;
|
|
3766
|
-
const state = messages[msgIndex].parts[partIndex].state ?? (messages[msgIndex].parts[partIndex].state = {});
|
|
3767
3442
|
if (outputParts.length > 0) {
|
|
3768
|
-
|
|
3769
|
-
state.status = "completed";
|
|
3443
|
+
transcript.resolveToolCall(callId, { output: outputParts, status: "completed" });
|
|
3770
3444
|
}
|
|
3771
3445
|
}
|
|
3772
3446
|
// ---- Custom tool call ----
|
|
3773
|
-
convertCustomToolCall(payload,
|
|
3447
|
+
convertCustomToolCall(payload, transcript, timestampMs, activeModel) {
|
|
3774
3448
|
const callId = String(payload["call_id"] ?? "").trim();
|
|
3775
3449
|
const name = String(payload["name"] ?? "").trim();
|
|
3776
|
-
if (!name)
|
|
3777
|
-
return { currentAssistantIndex, latestAssistantTextIndex, pendingPlan: null };
|
|
3778
|
-
}
|
|
3450
|
+
if (!name) return;
|
|
3779
3451
|
const toolIdentity = resolveToolIdentity(name, payload["namespace"]);
|
|
3780
3452
|
const rawInput = payload["input"];
|
|
3781
3453
|
const normalizedInput = normalizeCustomToolArguments(name, rawInput);
|
|
@@ -3791,70 +3463,87 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3791
3463
|
},
|
|
3792
3464
|
time_created: timestampMs
|
|
3793
3465
|
};
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
message.parts.push(toolPart);
|
|
3799
|
-
message.mode = "tool";
|
|
3800
|
-
if (callId) {
|
|
3801
|
-
pendingToolCalls.set(callId, [targetIndex, partIndex]);
|
|
3802
|
-
}
|
|
3803
|
-
return {
|
|
3804
|
-
currentAssistantIndex: targetIndex,
|
|
3805
|
-
latestAssistantTextIndex: targetIndex,
|
|
3806
|
-
pendingPlan: null
|
|
3807
|
-
};
|
|
3808
|
-
}
|
|
3809
|
-
messages.push(
|
|
3810
|
-
this.buildMessage({
|
|
3811
|
-
messageId: "",
|
|
3812
|
-
role: "assistant",
|
|
3813
|
-
timestampMs,
|
|
3814
|
-
parts: [toolPart],
|
|
3815
|
-
agent: "codex",
|
|
3816
|
-
mode: "tool"
|
|
3817
|
-
})
|
|
3466
|
+
transcript.appendToolCall(
|
|
3467
|
+
toolPart,
|
|
3468
|
+
{ id: "", timestampMs, agent: "codex", model: activeModel },
|
|
3469
|
+
{ markModeAsTool: true }
|
|
3818
3470
|
);
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3471
|
+
}
|
|
3472
|
+
};
|
|
3473
|
+
var PerfTracer = class {
|
|
3474
|
+
rootMarkers = [];
|
|
3475
|
+
activeStack = [];
|
|
3476
|
+
enabled = false;
|
|
3477
|
+
enable() {
|
|
3478
|
+
this.enabled = true;
|
|
3479
|
+
}
|
|
3480
|
+
start(name) {
|
|
3481
|
+
const marker = {
|
|
3482
|
+
name,
|
|
3483
|
+
startTime: performance.now(),
|
|
3484
|
+
children: []
|
|
3485
|
+
};
|
|
3486
|
+
if (!this.enabled) return marker;
|
|
3487
|
+
const parent = this.activeStack[this.activeStack.length - 1];
|
|
3488
|
+
if (parent) {
|
|
3489
|
+
marker.parent = parent;
|
|
3490
|
+
parent.children.push(marker);
|
|
3491
|
+
} else {
|
|
3492
|
+
this.rootMarkers.push(marker);
|
|
3822
3493
|
}
|
|
3823
|
-
|
|
3494
|
+
this.activeStack.push(marker);
|
|
3495
|
+
return marker;
|
|
3824
3496
|
}
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
const
|
|
3828
|
-
if (!
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
const state = messages[msgIndex].parts[partIndex].state ?? (messages[msgIndex].parts[partIndex].state = {});
|
|
3835
|
-
if (outputParts.length > 0) {
|
|
3836
|
-
state.output = [...outputParts];
|
|
3837
|
-
state.status = "completed";
|
|
3497
|
+
end(marker) {
|
|
3498
|
+
if (!this.enabled) return;
|
|
3499
|
+
const target = marker ?? this.activeStack[this.activeStack.length - 1];
|
|
3500
|
+
if (!target) return;
|
|
3501
|
+
target.endTime = performance.now();
|
|
3502
|
+
target.duration = target.endTime - target.startTime;
|
|
3503
|
+
while (this.activeStack.length > 0) {
|
|
3504
|
+
const popped = this.activeStack.pop();
|
|
3505
|
+
if (popped === target) break;
|
|
3838
3506
|
}
|
|
3839
3507
|
}
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3508
|
+
measure(name, fn) {
|
|
3509
|
+
const marker = this.start(name);
|
|
3510
|
+
try {
|
|
3511
|
+
return fn();
|
|
3512
|
+
} finally {
|
|
3513
|
+
this.end(marker);
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
async measureAsync(name, fn) {
|
|
3517
|
+
const marker = this.start(name);
|
|
3518
|
+
try {
|
|
3519
|
+
return await fn();
|
|
3520
|
+
} finally {
|
|
3521
|
+
this.end(marker);
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
getReport() {
|
|
3525
|
+
if (!this.enabled) return "Performance tracing disabled";
|
|
3526
|
+
const lines = [];
|
|
3527
|
+
lines.push("\n=== Performance Report ===\n");
|
|
3528
|
+
for (const marker of this.rootMarkers) {
|
|
3529
|
+
this.formatMarker(marker, 0, lines);
|
|
3530
|
+
}
|
|
3531
|
+
return lines.join("\n");
|
|
3532
|
+
}
|
|
3533
|
+
formatMarker(marker, depth, lines) {
|
|
3534
|
+
const indent = " ".repeat(depth);
|
|
3535
|
+
const duration = marker.duration?.toFixed(2) ?? "?";
|
|
3536
|
+
lines.push(`${indent}${marker.name}: ${duration}ms`);
|
|
3537
|
+
for (const child of marker.children) {
|
|
3538
|
+
this.formatMarker(child, depth + 1, lines);
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3541
|
+
reset() {
|
|
3542
|
+
this.rootMarkers = [];
|
|
3543
|
+
this.activeStack = [];
|
|
3856
3544
|
}
|
|
3857
3545
|
};
|
|
3546
|
+
var perf = new PerfTracer();
|
|
3858
3547
|
var CURSOR_TOOL_TITLE_MAP = {
|
|
3859
3548
|
read_file_v2: "read",
|
|
3860
3549
|
edit_file_v2: "edit",
|
|
@@ -4008,7 +3697,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4008
3697
|
if (!existsSync8(wsJsonPath)) continue;
|
|
4009
3698
|
let workspacePath;
|
|
4010
3699
|
try {
|
|
4011
|
-
const data = JSON.parse(
|
|
3700
|
+
const data = JSON.parse(readFileSync5(wsJsonPath, "utf-8"));
|
|
4012
3701
|
const uri = data.folder ?? data.workspace ?? "";
|
|
4013
3702
|
if (!uri) continue;
|
|
4014
3703
|
workspacePath = normalize(decodeURIComponent(uri.replace(/^file:\/\//, "")));
|
|
@@ -4022,12 +3711,12 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4022
3711
|
try {
|
|
4023
3712
|
const row = wsDb.prepare("SELECT value FROM ItemTable WHERE key = 'composer.composerData'").get();
|
|
4024
3713
|
if (!row?.value) continue;
|
|
4025
|
-
const
|
|
3714
|
+
const parsed = JSON.parse(row.value);
|
|
4026
3715
|
let composers;
|
|
4027
|
-
if (
|
|
4028
|
-
composers =
|
|
4029
|
-
} else if (Array.isArray(
|
|
4030
|
-
composers =
|
|
3716
|
+
if (parsed !== null && typeof parsed === "object" && "allComposers" in parsed && Array.isArray(parsed["allComposers"])) {
|
|
3717
|
+
composers = parsed.allComposers;
|
|
3718
|
+
} else if (Array.isArray(parsed)) {
|
|
3719
|
+
composers = parsed;
|
|
4031
3720
|
} else {
|
|
4032
3721
|
continue;
|
|
4033
3722
|
}
|
|
@@ -4406,10 +4095,10 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4406
4095
|
if (toolData.result !== void 0) {
|
|
4407
4096
|
if (typeof toolData.result === "string") {
|
|
4408
4097
|
try {
|
|
4409
|
-
const
|
|
4410
|
-
state.output =
|
|
4411
|
-
if (
|
|
4412
|
-
state.error =
|
|
4098
|
+
const parsed = JSON.parse(toolData.result);
|
|
4099
|
+
state.output = parsed;
|
|
4100
|
+
if (parsed.error || parsed.message || parsed.stderr) {
|
|
4101
|
+
state.error = parsed.error || parsed.message || parsed.stderr;
|
|
4413
4102
|
state.status = "error";
|
|
4414
4103
|
}
|
|
4415
4104
|
} catch {
|
|
@@ -4531,20 +4220,6 @@ function normalizeTextParts(content, timestampMs) {
|
|
|
4531
4220
|
const text = cleanInternalText(contentToText(content));
|
|
4532
4221
|
return text ? [{ type: "text", text, time_created: timestampMs }] : [];
|
|
4533
4222
|
}
|
|
4534
|
-
function buildMessage(params) {
|
|
4535
|
-
return {
|
|
4536
|
-
id: params.id,
|
|
4537
|
-
role: params.role,
|
|
4538
|
-
agent: params.agent,
|
|
4539
|
-
time_created: params.timestampMs,
|
|
4540
|
-
provider: params.provider,
|
|
4541
|
-
model: params.model,
|
|
4542
|
-
tokens: params.tokens,
|
|
4543
|
-
cost: params.cost,
|
|
4544
|
-
cost_source: params.costSource,
|
|
4545
|
-
parts: params.parts
|
|
4546
|
-
};
|
|
4547
|
-
}
|
|
4548
4223
|
function getEntryTimestamp(entry) {
|
|
4549
4224
|
return parseTimestampMs3(entry["timestamp"]);
|
|
4550
4225
|
}
|
|
@@ -4588,29 +4263,6 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4588
4263
|
if (!this.basePath) return false;
|
|
4589
4264
|
return this.listSessionFiles().length > 0;
|
|
4590
4265
|
}
|
|
4591
|
-
scan(options) {
|
|
4592
|
-
if (!this.basePath) return [];
|
|
4593
|
-
const scanMarker = perf.start("pi:scan");
|
|
4594
|
-
const files = this.listSessionFiles(options);
|
|
4595
|
-
options?.onProgress?.({ total: files.length, processed: 0, sessions: 0 });
|
|
4596
|
-
const heads = [];
|
|
4597
|
-
let processed = 0;
|
|
4598
|
-
for (const file of files) {
|
|
4599
|
-
try {
|
|
4600
|
-
const head = getParsedSession(this.parseSessionHeadResult(file));
|
|
4601
|
-
if (head) {
|
|
4602
|
-
heads.push(head);
|
|
4603
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, file));
|
|
4604
|
-
}
|
|
4605
|
-
} catch {
|
|
4606
|
-
} finally {
|
|
4607
|
-
processed += 1;
|
|
4608
|
-
options?.onProgress?.({ total: files.length, processed, sessions: heads.length });
|
|
4609
|
-
}
|
|
4610
|
-
}
|
|
4611
|
-
perf.end(scanMarker);
|
|
4612
|
-
return heads;
|
|
4613
|
-
}
|
|
4614
4266
|
listSessionSources(options) {
|
|
4615
4267
|
if (!this.basePath) return [];
|
|
4616
4268
|
return this.listSessionFiles(options).map((file) => ({
|
|
@@ -4630,9 +4282,8 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4630
4282
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
4631
4283
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
4632
4284
|
if (!existsSync9(meta.sourcePath)) throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
4633
|
-
const
|
|
4634
|
-
const state = this.convertEntries(
|
|
4635
|
-
const cleanedMessages = cleanParsedMessages(state.messages);
|
|
4285
|
+
const parsed = this.parsePiFile(meta.sourcePath);
|
|
4286
|
+
const state = this.convertEntries(parsed.pathEntries);
|
|
4636
4287
|
return {
|
|
4637
4288
|
id: meta.id,
|
|
4638
4289
|
title: meta.title,
|
|
@@ -4641,7 +4292,7 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4641
4292
|
time_created: meta.createdAt,
|
|
4642
4293
|
time_updated: meta.updatedAt,
|
|
4643
4294
|
stats: {
|
|
4644
|
-
message_count:
|
|
4295
|
+
message_count: state.messages.length,
|
|
4645
4296
|
total_input_tokens: state.totalInputTokens,
|
|
4646
4297
|
total_output_tokens: state.totalOutputTokens,
|
|
4647
4298
|
total_cache_read_tokens: state.totalCacheReadTokens || void 0,
|
|
@@ -4649,7 +4300,7 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4649
4300
|
total_cost: state.totalCost,
|
|
4650
4301
|
cost_source: state.totalCost > 0 ? "recorded" : void 0
|
|
4651
4302
|
},
|
|
4652
|
-
messages:
|
|
4303
|
+
messages: state.messages
|
|
4653
4304
|
};
|
|
4654
4305
|
}
|
|
4655
4306
|
listSessionFiles(options) {
|
|
@@ -4693,18 +4344,18 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4693
4344
|
return JSON.stringify([HEAD_INDEX_VERSION3, PARSER_VERSION2, stat.mtimeMs, stat.size]);
|
|
4694
4345
|
}
|
|
4695
4346
|
parseSessionHeadResult(filePath) {
|
|
4696
|
-
const
|
|
4697
|
-
const state = this.convertEntries(
|
|
4347
|
+
const parsed = this.parsePiFile(filePath);
|
|
4348
|
+
const state = this.convertEntries(parsed.pathEntries);
|
|
4698
4349
|
const messageCount = state.messages.length;
|
|
4699
4350
|
if (messageCount === 0) return filteredSession("no visible messages");
|
|
4700
4351
|
const modelUsage = Object.keys(state.modelUsage).length > 0 ? state.modelUsage : void 0;
|
|
4701
4352
|
return parsedSession({
|
|
4702
|
-
id:
|
|
4703
|
-
slug: `pi/${
|
|
4704
|
-
title:
|
|
4705
|
-
directory:
|
|
4706
|
-
time_created:
|
|
4707
|
-
time_updated:
|
|
4353
|
+
id: parsed.sessionId,
|
|
4354
|
+
slug: `pi/${parsed.sessionId}`,
|
|
4355
|
+
title: parsed.title,
|
|
4356
|
+
directory: parsed.directory,
|
|
4357
|
+
time_created: parsed.createdAt,
|
|
4358
|
+
time_updated: parsed.updatedAt,
|
|
4708
4359
|
stats: {
|
|
4709
4360
|
message_count: messageCount,
|
|
4710
4361
|
total_input_tokens: state.totalInputTokens,
|
|
@@ -4718,7 +4369,7 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4718
4369
|
});
|
|
4719
4370
|
}
|
|
4720
4371
|
parsePiFile(filePath) {
|
|
4721
|
-
const records = Array.from(parseJsonlLines(
|
|
4372
|
+
const records = Array.from(parseJsonlLines(readFileSync6(filePath, "utf-8")));
|
|
4722
4373
|
if (records.length === 0) throw new Error("empty file");
|
|
4723
4374
|
const header = records.find((record) => record["type"] === "session");
|
|
4724
4375
|
if (!header) throw new Error("missing session header");
|
|
@@ -4766,74 +4417,52 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4766
4417
|
return null;
|
|
4767
4418
|
}
|
|
4768
4419
|
convertEntries(entries) {
|
|
4769
|
-
const
|
|
4770
|
-
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
4420
|
+
const builder = new TranscriptBuilder({ messageDefaults: "sparse" });
|
|
4771
4421
|
const modelUsage = {};
|
|
4772
|
-
let totalInputTokens = 0;
|
|
4773
|
-
let totalOutputTokens = 0;
|
|
4774
|
-
let totalCacheReadTokens = 0;
|
|
4775
|
-
let totalCacheCreateTokens = 0;
|
|
4776
|
-
let totalCost = 0;
|
|
4777
4422
|
for (const entry of entries) {
|
|
4778
4423
|
const timestampMs = getEntryTimestamp(entry);
|
|
4779
4424
|
const type = String(entry["type"] ?? "");
|
|
4780
4425
|
if (type === "message") {
|
|
4781
4426
|
const message = entry["message"];
|
|
4782
4427
|
if (!isObject(message)) continue;
|
|
4783
|
-
const
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
messages.length,
|
|
4789
|
-
messages
|
|
4790
|
-
);
|
|
4791
|
-
if (!result) continue;
|
|
4792
|
-
if (result.message) messages.push(result.message);
|
|
4793
|
-
totalInputTokens += result.inputTokens;
|
|
4794
|
-
totalOutputTokens += result.outputTokens;
|
|
4795
|
-
totalCacheReadTokens += result.cacheReadTokens;
|
|
4796
|
-
totalCacheCreateTokens += result.cacheCreateTokens;
|
|
4797
|
-
totalCost += result.cost;
|
|
4798
|
-
if (result.model && result.totalTokens > 0) {
|
|
4799
|
-
modelUsage[result.model] = (modelUsage[result.model] ?? 0) + result.totalTokens;
|
|
4428
|
+
const result2 = this.convertAgentMessage(entry, message, timestampMs, builder);
|
|
4429
|
+
if (!result2) continue;
|
|
4430
|
+
if (result2.message) builder.appendMessage(result2.message);
|
|
4431
|
+
if (result2.model && result2.totalTokens > 0) {
|
|
4432
|
+
modelUsage[result2.model] = (modelUsage[result2.model] ?? 0) + result2.totalTokens;
|
|
4800
4433
|
}
|
|
4801
4434
|
continue;
|
|
4802
4435
|
}
|
|
4803
4436
|
const summary = this.convertSummaryEntry(entry, timestampMs);
|
|
4804
|
-
if (summary)
|
|
4437
|
+
if (summary) builder.appendMessage(summary);
|
|
4805
4438
|
}
|
|
4439
|
+
const result = builder.finish();
|
|
4806
4440
|
return {
|
|
4807
|
-
messages,
|
|
4808
|
-
totalInputTokens,
|
|
4809
|
-
totalOutputTokens,
|
|
4810
|
-
totalCacheReadTokens,
|
|
4811
|
-
totalCacheCreateTokens,
|
|
4812
|
-
totalCost,
|
|
4441
|
+
messages: result.messages,
|
|
4442
|
+
totalInputTokens: result.stats.total_input_tokens,
|
|
4443
|
+
totalOutputTokens: result.stats.total_output_tokens,
|
|
4444
|
+
totalCacheReadTokens: result.stats.total_cache_read_tokens ?? 0,
|
|
4445
|
+
totalCacheCreateTokens: result.stats.total_cache_create_tokens ?? 0,
|
|
4446
|
+
totalCost: result.stats.total_cost,
|
|
4813
4447
|
modelUsage
|
|
4814
4448
|
};
|
|
4815
4449
|
}
|
|
4816
|
-
convertAgentMessage(entry, message, timestampMs,
|
|
4450
|
+
convertAgentMessage(entry, message, timestampMs, builder) {
|
|
4817
4451
|
const id = String(entry["id"] ?? "");
|
|
4818
4452
|
const role = String(message["role"] ?? "");
|
|
4819
4453
|
if (role === "user") {
|
|
4820
4454
|
const parts = normalizeTextParts(message["content"], timestampMs);
|
|
4821
4455
|
if (parts.length === 0) return null;
|
|
4822
|
-
return this.emptyUsageResult(
|
|
4456
|
+
return this.emptyUsageResult({ id, role: "user", timestampMs, parts });
|
|
4823
4457
|
}
|
|
4824
4458
|
if (role === "assistant") {
|
|
4825
|
-
const parts = this.normalizeAssistantParts(
|
|
4826
|
-
message["content"],
|
|
4827
|
-
timestampMs,
|
|
4828
|
-
pendingToolCalls,
|
|
4829
|
-
nextMessageIndex
|
|
4830
|
-
);
|
|
4459
|
+
const parts = this.normalizeAssistantParts(message["content"], timestampMs);
|
|
4831
4460
|
if (parts.length === 0) return null;
|
|
4832
4461
|
const usage = this.normalizeUsage(message["usage"]);
|
|
4833
4462
|
const model = typeof message["model"] === "string" ? message["model"].trim() : null;
|
|
4834
4463
|
const cost = usage.cost ?? estimateTokenCost(model, usage.tokens) ?? 0;
|
|
4835
4464
|
return {
|
|
4836
|
-
message:
|
|
4465
|
+
message: {
|
|
4837
4466
|
id,
|
|
4838
4467
|
role: "assistant",
|
|
4839
4468
|
agent: "pi",
|
|
@@ -4844,18 +4473,13 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4844
4473
|
tokens: usage.tokens,
|
|
4845
4474
|
cost: cost || void 0,
|
|
4846
4475
|
costSource: cost > 0 ? "recorded" : void 0
|
|
4847
|
-
}
|
|
4848
|
-
inputTokens: usage.inputTokens,
|
|
4849
|
-
outputTokens: usage.outputTokens,
|
|
4850
|
-
cacheReadTokens: usage.cacheReadTokens,
|
|
4851
|
-
cacheCreateTokens: usage.cacheCreateTokens,
|
|
4476
|
+
},
|
|
4852
4477
|
totalTokens: usage.totalTokens,
|
|
4853
|
-
cost,
|
|
4854
4478
|
model
|
|
4855
4479
|
};
|
|
4856
4480
|
}
|
|
4857
4481
|
if (role === "toolResult") {
|
|
4858
|
-
this.attachToolResult(message, timestampMs,
|
|
4482
|
+
this.attachToolResult(message, timestampMs, builder);
|
|
4859
4483
|
return this.emptyUsageResult();
|
|
4860
4484
|
}
|
|
4861
4485
|
if (role === "bashExecution") {
|
|
@@ -4864,24 +4488,22 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4864
4488
|
if (role === "custom" && message["display"] === true) {
|
|
4865
4489
|
const parts = normalizeTextParts(message["content"], timestampMs);
|
|
4866
4490
|
if (parts.length === 0) return null;
|
|
4867
|
-
return this.emptyUsageResult(
|
|
4491
|
+
return this.emptyUsageResult({ id, role: "user", timestampMs, parts });
|
|
4868
4492
|
}
|
|
4869
4493
|
if (role === "branchSummary" || role === "compactionSummary") {
|
|
4870
4494
|
const summary = String(message["summary"] ?? "").trim();
|
|
4871
4495
|
if (!summary) return null;
|
|
4872
|
-
return this.emptyUsageResult(
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
})
|
|
4880
|
-
);
|
|
4496
|
+
return this.emptyUsageResult({
|
|
4497
|
+
id,
|
|
4498
|
+
role: "assistant",
|
|
4499
|
+
agent: "pi",
|
|
4500
|
+
timestampMs,
|
|
4501
|
+
parts: [{ type: "text", text: summary, time_created: timestampMs }]
|
|
4502
|
+
});
|
|
4881
4503
|
}
|
|
4882
4504
|
return null;
|
|
4883
4505
|
}
|
|
4884
|
-
normalizeAssistantParts(content, timestampMs
|
|
4506
|
+
normalizeAssistantParts(content, timestampMs) {
|
|
4885
4507
|
if (!Array.isArray(content)) return [];
|
|
4886
4508
|
const parts = [];
|
|
4887
4509
|
for (const item of content) {
|
|
@@ -4912,29 +4534,26 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4912
4534
|
}
|
|
4913
4535
|
};
|
|
4914
4536
|
parts.push(toolPart);
|
|
4915
|
-
if (callId) pendingToolCalls.set(callId, [messageIndex, parts.length - 1]);
|
|
4916
4537
|
}
|
|
4917
4538
|
}
|
|
4918
4539
|
return parts;
|
|
4919
4540
|
}
|
|
4920
|
-
attachToolResult(message, timestampMs,
|
|
4541
|
+
attachToolResult(message, timestampMs, builder) {
|
|
4921
4542
|
const callId = String(message["toolCallId"] ?? "").trim();
|
|
4922
4543
|
const output = normalizeTextParts(message["content"], timestampMs);
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
target.state.metadata = message["details"];
|
|
4931
|
-
pendingToolCalls.delete(callId);
|
|
4544
|
+
if (!callId) return;
|
|
4545
|
+
builder.resolveToolCall(callId, {
|
|
4546
|
+
output,
|
|
4547
|
+
status: message["isError"] === true ? "error" : "completed",
|
|
4548
|
+
metadata: message["details"],
|
|
4549
|
+
consume: true
|
|
4550
|
+
});
|
|
4932
4551
|
}
|
|
4933
4552
|
convertBashExecution(id, message, timestampMs) {
|
|
4934
4553
|
const command = String(message["command"] ?? "");
|
|
4935
4554
|
const output = String(message["output"] ?? "");
|
|
4936
4555
|
const isError = Number(message["exitCode"] ?? 0) !== 0 || message["cancelled"] === true;
|
|
4937
|
-
return
|
|
4556
|
+
return {
|
|
4938
4557
|
id,
|
|
4939
4558
|
role: "tool",
|
|
4940
4559
|
timestampMs,
|
|
@@ -4957,7 +4576,7 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4957
4576
|
}
|
|
4958
4577
|
}
|
|
4959
4578
|
]
|
|
4960
|
-
}
|
|
4579
|
+
};
|
|
4961
4580
|
}
|
|
4962
4581
|
convertSummaryEntry(entry, timestampMs) {
|
|
4963
4582
|
const type = entry["type"];
|
|
@@ -4968,13 +4587,13 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4968
4587
|
const rawText = type === "custom_message" ? contentToText(entry["content"]) : String(entry["summary"] ?? "");
|
|
4969
4588
|
const text = cleanInternalText(rawText);
|
|
4970
4589
|
if (!text) return null;
|
|
4971
|
-
return
|
|
4590
|
+
return {
|
|
4972
4591
|
id: String(entry["id"] ?? ""),
|
|
4973
4592
|
role: type === "custom_message" ? "user" : "assistant",
|
|
4974
4593
|
agent: type === "custom_message" ? void 0 : "pi",
|
|
4975
4594
|
timestampMs,
|
|
4976
4595
|
parts: [{ type: "text", text, time_created: timestampMs }]
|
|
4977
|
-
}
|
|
4596
|
+
};
|
|
4978
4597
|
}
|
|
4979
4598
|
normalizeUsage(raw) {
|
|
4980
4599
|
const usage = isObject(raw) ? raw : {};
|
|
@@ -5004,12 +4623,7 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
5004
4623
|
emptyUsageResult(message) {
|
|
5005
4624
|
return {
|
|
5006
4625
|
message,
|
|
5007
|
-
inputTokens: 0,
|
|
5008
|
-
outputTokens: 0,
|
|
5009
|
-
cacheReadTokens: 0,
|
|
5010
|
-
cacheCreateTokens: 0,
|
|
5011
4626
|
totalTokens: 0,
|
|
5012
|
-
cost: 0,
|
|
5013
4627
|
model: null
|
|
5014
4628
|
};
|
|
5015
4629
|
}
|
|
@@ -5030,44 +4644,30 @@ var ZCodeAgent = class extends OpenCodeSqliteAgent {
|
|
|
5030
4644
|
}
|
|
5031
4645
|
};
|
|
5032
4646
|
registerAgent({
|
|
5033
|
-
name: "claudecode",
|
|
5034
|
-
displayName: "Claude Code",
|
|
5035
4647
|
icon: "/icon/agent/claudecode.svg",
|
|
5036
4648
|
create: () => new ClaudeCodeAgent()
|
|
5037
4649
|
});
|
|
5038
4650
|
registerAgent({
|
|
5039
|
-
name: "opencode",
|
|
5040
|
-
displayName: "OpenCode",
|
|
5041
4651
|
icon: "/icon/agent/opencode.svg",
|
|
5042
4652
|
create: () => new OpenCodeAgent()
|
|
5043
4653
|
});
|
|
5044
4654
|
registerAgent({
|
|
5045
|
-
name: "zcode",
|
|
5046
|
-
displayName: "ZCode",
|
|
5047
4655
|
icon: "/icon/agent/zcode.svg",
|
|
5048
4656
|
create: () => new ZCodeAgent()
|
|
5049
4657
|
});
|
|
5050
4658
|
registerAgent({
|
|
5051
|
-
name: "kimi",
|
|
5052
|
-
displayName: "Kimi-Cli",
|
|
5053
4659
|
icon: "/icon/agent/kimi.svg",
|
|
5054
4660
|
create: () => new KimiAgent()
|
|
5055
4661
|
});
|
|
5056
4662
|
registerAgent({
|
|
5057
|
-
name: "codex",
|
|
5058
|
-
displayName: "Codex",
|
|
5059
4663
|
icon: "/icon/agent/codex.svg",
|
|
5060
4664
|
create: () => new CodexAgent()
|
|
5061
4665
|
});
|
|
5062
4666
|
registerAgent({
|
|
5063
|
-
name: "pi",
|
|
5064
|
-
displayName: "Pi",
|
|
5065
4667
|
icon: "/icon/agent/pi.svg",
|
|
5066
4668
|
create: () => new PiAgent()
|
|
5067
4669
|
});
|
|
5068
4670
|
registerAgent({
|
|
5069
|
-
name: "cursor",
|
|
5070
|
-
displayName: "Cursor",
|
|
5071
4671
|
icon: "/icon/agent/cursor.svg",
|
|
5072
4672
|
create: () => new CursorAgent()
|
|
5073
4673
|
});
|
|
@@ -5083,7 +4683,7 @@ var realFs = {
|
|
|
5083
4683
|
},
|
|
5084
4684
|
readText(path2) {
|
|
5085
4685
|
try {
|
|
5086
|
-
return
|
|
4686
|
+
return readFileSync7(path2, "utf8");
|
|
5087
4687
|
} catch {
|
|
5088
4688
|
return null;
|
|
5089
4689
|
}
|
|
@@ -5096,43 +4696,6 @@ var realFs = {
|
|
|
5096
4696
|
};
|
|
5097
4697
|
}
|
|
5098
4698
|
};
|
|
5099
|
-
function getAgentName(session) {
|
|
5100
|
-
return session.slug.split("/")[0]?.toLowerCase() || "unknown";
|
|
5101
|
-
}
|
|
5102
|
-
function buildProjectGroups(sessions) {
|
|
5103
|
-
const groups = /* @__PURE__ */ new Map();
|
|
5104
|
-
for (const session of sessions) {
|
|
5105
|
-
const identity = session.project_identity;
|
|
5106
|
-
if (!identity) continue;
|
|
5107
|
-
const activity = session.time_updated ?? session.time_created;
|
|
5108
|
-
const groupKey = `${identity.kind}:${identity.key}`;
|
|
5109
|
-
const current = groups.get(groupKey);
|
|
5110
|
-
if (current) {
|
|
5111
|
-
current.sources.add(getAgentName(session));
|
|
5112
|
-
current.sessionCount += 1;
|
|
5113
|
-
current.lastActivity = Math.max(current.lastActivity, activity);
|
|
5114
|
-
} else {
|
|
5115
|
-
groups.set(groupKey, {
|
|
5116
|
-
identity,
|
|
5117
|
-
sources: /* @__PURE__ */ new Set([getAgentName(session)]),
|
|
5118
|
-
sessionCount: 1,
|
|
5119
|
-
lastActivity: activity
|
|
5120
|
-
});
|
|
5121
|
-
}
|
|
5122
|
-
}
|
|
5123
|
-
return [...groups.values()].map((group) => ({
|
|
5124
|
-
identityKind: group.identity.kind,
|
|
5125
|
-
identityKey: group.identity.key,
|
|
5126
|
-
displayName: group.identity.displayName,
|
|
5127
|
-
sources: [...group.sources].sort(),
|
|
5128
|
-
sessionCount: group.sessionCount,
|
|
5129
|
-
lastActivity: group.lastActivity || null
|
|
5130
|
-
})).sort((a, b) => {
|
|
5131
|
-
if (a.identityKind === "loose" && b.identityKind !== "loose") return 1;
|
|
5132
|
-
if (b.identityKind === "loose" && a.identityKind !== "loose") return -1;
|
|
5133
|
-
return (b.lastActivity ?? 0) - (a.lastActivity ?? 0);
|
|
5134
|
-
});
|
|
5135
|
-
}
|
|
5136
4699
|
var MANIFESTS = [
|
|
5137
4700
|
"package.json",
|
|
5138
4701
|
"Cargo.toml",
|
|
@@ -5145,6 +4708,23 @@ var MANIFESTS = [
|
|
|
5145
4708
|
var PARSEABLE_MANIFESTS = ["package.json", "Cargo.toml", "pyproject.toml"];
|
|
5146
4709
|
var LOOSE_DIRS = /* @__PURE__ */ new Set(["/tmp", "/private/tmp"]);
|
|
5147
4710
|
var LOOSE_HOME_DIRS = ["Desktop", "Downloads", "Documents"];
|
|
4711
|
+
var PROJECT_IDENTITY_KINDS = /* @__PURE__ */ new Set([
|
|
4712
|
+
"git_remote",
|
|
4713
|
+
"git_common_dir",
|
|
4714
|
+
"manifest_path",
|
|
4715
|
+
"synthetic",
|
|
4716
|
+
"path",
|
|
4717
|
+
"loose"
|
|
4718
|
+
]);
|
|
4719
|
+
function isProjectIdentityKind(value) {
|
|
4720
|
+
return PROJECT_IDENTITY_KINDS.has(value);
|
|
4721
|
+
}
|
|
4722
|
+
function getProjectIdentityKey(identity) {
|
|
4723
|
+
return `${identity.kind}:${identity.key}`;
|
|
4724
|
+
}
|
|
4725
|
+
function matchesProjectIdentity(identity, expected) {
|
|
4726
|
+
return identity?.kind === expected.kind && identity.key === expected.key;
|
|
4727
|
+
}
|
|
5148
4728
|
function normalizeGitRemote(url) {
|
|
5149
4729
|
if (!url) return null;
|
|
5150
4730
|
let value = url.trim().replace(/\.git$/, "");
|
|
@@ -5275,15 +4855,53 @@ function parseManifestName(file, text) {
|
|
|
5275
4855
|
}
|
|
5276
4856
|
return null;
|
|
5277
4857
|
}
|
|
4858
|
+
function getAgentName(session) {
|
|
4859
|
+
return session.slug.split("/")[0]?.toLowerCase() || "unknown";
|
|
4860
|
+
}
|
|
4861
|
+
function buildProjectGroups(sessions) {
|
|
4862
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4863
|
+
for (const session of sessions) {
|
|
4864
|
+
const identity = session.project_identity;
|
|
4865
|
+
if (!identity) continue;
|
|
4866
|
+
const activity = session.time_updated ?? session.time_created;
|
|
4867
|
+
const groupKey = getProjectIdentityKey(identity);
|
|
4868
|
+
const current = groups.get(groupKey);
|
|
4869
|
+
if (current) {
|
|
4870
|
+
current.sources.add(getAgentName(session));
|
|
4871
|
+
current.sessionCount += 1;
|
|
4872
|
+
current.lastActivity = Math.max(current.lastActivity, activity);
|
|
4873
|
+
} else {
|
|
4874
|
+
groups.set(groupKey, {
|
|
4875
|
+
identity,
|
|
4876
|
+
sources: /* @__PURE__ */ new Set([getAgentName(session)]),
|
|
4877
|
+
sessionCount: 1,
|
|
4878
|
+
lastActivity: activity
|
|
4879
|
+
});
|
|
4880
|
+
}
|
|
4881
|
+
}
|
|
4882
|
+
return [...groups.values()].map((group) => ({
|
|
4883
|
+
identityKind: group.identity.kind,
|
|
4884
|
+
identityKey: group.identity.key,
|
|
4885
|
+
displayName: group.identity.displayName,
|
|
4886
|
+
sources: [...group.sources].sort(),
|
|
4887
|
+
sessionCount: group.sessionCount,
|
|
4888
|
+
lastActivity: group.lastActivity || null
|
|
4889
|
+
})).sort((a, b) => {
|
|
4890
|
+
if (a.identityKind === "loose" && b.identityKind !== "loose") return 1;
|
|
4891
|
+
if (b.identityKind === "loose" && a.identityKind !== "loose") return -1;
|
|
4892
|
+
return (b.lastActivity ?? 0) - (a.lastActivity ?? 0);
|
|
4893
|
+
});
|
|
4894
|
+
}
|
|
5278
4895
|
function createProjectScopeMatcher(queryPath, fs = realFs) {
|
|
4896
|
+
const identity = computeIdentity(queryPath, fs);
|
|
5279
4897
|
return {
|
|
5280
|
-
|
|
4898
|
+
identity: { kind: identity.kind, key: identity.key },
|
|
5281
4899
|
path: normalizeScopePath(queryPath)
|
|
5282
4900
|
};
|
|
5283
4901
|
}
|
|
5284
4902
|
function matchesProjectScope(session, scope) {
|
|
5285
4903
|
if (!session.directory) return false;
|
|
5286
|
-
if (session.project_identity
|
|
4904
|
+
if (matchesProjectIdentity(session.project_identity, scope.identity)) return true;
|
|
5287
4905
|
return isPathScopeMatch(scope.path, session.directory);
|
|
5288
4906
|
}
|
|
5289
4907
|
function filterSessionsByProjectScope(sessions, queryPath, fs) {
|
|
@@ -6039,7 +5657,7 @@ function buildSessionContentFromMessages(title, messages) {
|
|
|
6039
5657
|
}
|
|
6040
5658
|
return chunks.join("\n");
|
|
6041
5659
|
}
|
|
6042
|
-
var CACHE_SCHEMA_VERSION =
|
|
5660
|
+
var CACHE_SCHEMA_VERSION = 14;
|
|
6043
5661
|
function withCacheDb(fn) {
|
|
6044
5662
|
const cachePath = getCachePath2();
|
|
6045
5663
|
const db = openDb(cachePath);
|
|
@@ -6312,6 +5930,7 @@ function createSearchTables(db) {
|
|
|
6312
5930
|
activity_time INTEGER NOT NULL,
|
|
6313
5931
|
content_text TEXT NOT NULL,
|
|
6314
5932
|
content_hash TEXT NOT NULL,
|
|
5933
|
+
indexed_message_count INTEGER NOT NULL,
|
|
6315
5934
|
indexed_at INTEGER NOT NULL,
|
|
6316
5935
|
UNIQUE(agent_name, session_id)
|
|
6317
5936
|
);
|
|
@@ -6345,6 +5964,24 @@ function createSearchTriggers(db) {
|
|
|
6345
5964
|
END;
|
|
6346
5965
|
`);
|
|
6347
5966
|
}
|
|
5967
|
+
function addIndexedMessageCount(db) {
|
|
5968
|
+
if (!tableExists(db, "session_documents")) return;
|
|
5969
|
+
if (!columnExists(db, "session_documents", "indexed_message_count")) {
|
|
5970
|
+
db.exec(
|
|
5971
|
+
"ALTER TABLE session_documents ADD COLUMN indexed_message_count INTEGER NOT NULL DEFAULT 0"
|
|
5972
|
+
);
|
|
5973
|
+
}
|
|
5974
|
+
if (!tableExists(db, "messages")) return;
|
|
5975
|
+
db.exec(`
|
|
5976
|
+
UPDATE session_documents
|
|
5977
|
+
SET indexed_message_count = (
|
|
5978
|
+
SELECT COUNT(*)
|
|
5979
|
+
FROM messages
|
|
5980
|
+
WHERE messages.agent_name = session_documents.agent_name
|
|
5981
|
+
AND messages.session_id = session_documents.session_id
|
|
5982
|
+
)
|
|
5983
|
+
`);
|
|
5984
|
+
}
|
|
6348
5985
|
function dropSearchTriggers(db) {
|
|
6349
5986
|
db.exec(`
|
|
6350
5987
|
DROP TRIGGER IF EXISTS session_documents_ai;
|
|
@@ -6450,6 +6087,9 @@ function readLegacyCacheVersion(db) {
|
|
|
6450
6087
|
return Number(versionRow?.value ?? 0);
|
|
6451
6088
|
}
|
|
6452
6089
|
function inferCacheSchemaVersion(db) {
|
|
6090
|
+
if (columnExists(db, "session_documents", "indexed_message_count")) {
|
|
6091
|
+
return 14;
|
|
6092
|
+
}
|
|
6453
6093
|
if (tableExists(db, "message_tools")) {
|
|
6454
6094
|
return 11;
|
|
6455
6095
|
}
|
|
@@ -6882,39 +6522,16 @@ function ensureSchema(db, dbPath) {
|
|
|
6882
6522
|
version: 12,
|
|
6883
6523
|
migrate(db2) {
|
|
6884
6524
|
refreshProjectIdentities(db2);
|
|
6885
|
-
}
|
|
6886
|
-
},
|
|
6887
|
-
{ version: 13, migrate: createCacheTables }
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
}
|
|
6895
|
-
function shouldBulkSyncSearchIndex(options, changedCount) {
|
|
6896
|
-
if (options.isBulk != null) {
|
|
6897
|
-
return options.isBulk;
|
|
6898
|
-
}
|
|
6899
|
-
const threshold = options.bulkThreshold ?? SEARCH_INDEX_BULK_SYNC_THRESHOLD;
|
|
6900
|
-
return threshold > 0 && changedCount >= threshold;
|
|
6901
|
-
}
|
|
6902
|
-
function sessionContentHash(session) {
|
|
6903
|
-
return JSON.stringify([
|
|
6904
|
-
session.slug,
|
|
6905
|
-
session.title,
|
|
6906
|
-
session.directory,
|
|
6907
|
-
session.time_created,
|
|
6908
|
-
session.time_updated ?? session.time_created,
|
|
6909
|
-
session.stats.message_count,
|
|
6910
|
-
session.stats.total_input_tokens,
|
|
6911
|
-
session.stats.total_output_tokens,
|
|
6912
|
-
session.stats.total_cache_read_tokens ?? 0,
|
|
6913
|
-
session.stats.total_cache_create_tokens ?? 0,
|
|
6914
|
-
session.stats.total_cost,
|
|
6915
|
-
session.stats.cost_source ?? "",
|
|
6916
|
-
session.stats.total_tokens ?? 0
|
|
6917
|
-
]);
|
|
6525
|
+
}
|
|
6526
|
+
},
|
|
6527
|
+
{ version: 13, migrate: createCacheTables },
|
|
6528
|
+
{ version: 14, migrate: addIndexedMessageCount }
|
|
6529
|
+
]
|
|
6530
|
+
});
|
|
6531
|
+
createLatestCacheSchema(db);
|
|
6532
|
+
if (getUserVersion(db) <= CACHE_SCHEMA_VERSION) {
|
|
6533
|
+
setCacheSchemaVersion(db);
|
|
6534
|
+
}
|
|
6918
6535
|
}
|
|
6919
6536
|
function escapeFtsTerm(value) {
|
|
6920
6537
|
return value.replaceAll('"', '""');
|
|
@@ -6938,9 +6555,7 @@ function splitSearchTokens(input) {
|
|
|
6938
6555
|
}
|
|
6939
6556
|
token += char;
|
|
6940
6557
|
}
|
|
6941
|
-
if (token)
|
|
6942
|
-
tokens.push(token);
|
|
6943
|
-
}
|
|
6558
|
+
if (token) tokens.push(token);
|
|
6944
6559
|
return tokens;
|
|
6945
6560
|
}
|
|
6946
6561
|
function unwrapSearchValue(value) {
|
|
@@ -7000,7 +6615,10 @@ function parseSearchQuery(input) {
|
|
|
7000
6615
|
if (key === "agent") filters.agent = value.toLowerCase();
|
|
7001
6616
|
else if (key === "project") filters.project = value;
|
|
7002
6617
|
else if (key === "projectkey" || key === "project-key") filters.projectKey = value;
|
|
7003
|
-
else if (key === "
|
|
6618
|
+
else if (key === "projectkind" || key === "project-kind") {
|
|
6619
|
+
if (isProjectIdentityKind(value)) filters.projectKind = value;
|
|
6620
|
+
else consumed = false;
|
|
6621
|
+
} else if (key === "cwd") filters.cwd = value;
|
|
7004
6622
|
else if (key === "tool") filters.tools = appendUnique(filters.tools, value.toLowerCase());
|
|
7005
6623
|
else if (key === "file" || key === "path") filters.file = value;
|
|
7006
6624
|
else if (key === "kind" || key === "filekind" || key === "file-kind") {
|
|
@@ -7011,21 +6629,15 @@ function parseSearchQuery(input) {
|
|
|
7011
6629
|
}
|
|
7012
6630
|
} else if (key === "tag" || key === "signal") {
|
|
7013
6631
|
const tag = value.toLowerCase();
|
|
7014
|
-
if (isSmartTag(tag))
|
|
7015
|
-
|
|
7016
|
-
} else {
|
|
7017
|
-
consumed = false;
|
|
7018
|
-
}
|
|
6632
|
+
if (isSmartTag(tag)) filters.tags = appendUnique(filters.tags, tag);
|
|
6633
|
+
else consumed = false;
|
|
7019
6634
|
} else if (key === "cost") {
|
|
7020
6635
|
parseCostQualifier(value, filters);
|
|
7021
6636
|
} else {
|
|
7022
6637
|
consumed = false;
|
|
7023
6638
|
}
|
|
7024
|
-
if (consumed)
|
|
7025
|
-
|
|
7026
|
-
} else {
|
|
7027
|
-
textTokens.push(token);
|
|
7028
|
-
}
|
|
6639
|
+
if (consumed) hasQualifiers = true;
|
|
6640
|
+
else textTokens.push(token);
|
|
7029
6641
|
}
|
|
7030
6642
|
return {
|
|
7031
6643
|
text: textTokens.join(" ").trim(),
|
|
@@ -7035,18 +6647,86 @@ function parseSearchQuery(input) {
|
|
|
7035
6647
|
}
|
|
7036
6648
|
function toFtsQuery(input) {
|
|
7037
6649
|
const tokens = splitSearchTokens(input);
|
|
7038
|
-
|
|
7039
|
-
if (/^OR$/i.test(token))
|
|
7040
|
-
return "OR";
|
|
7041
|
-
}
|
|
6650
|
+
return tokens.map((token) => {
|
|
6651
|
+
if (/^OR$/i.test(token)) return "OR";
|
|
7042
6652
|
if (token.startsWith('"') && token.endsWith('"')) {
|
|
7043
6653
|
return `"${escapeFtsTerm(token.slice(1, -1))}"`;
|
|
7044
6654
|
}
|
|
7045
6655
|
return `"${escapeFtsTerm(token)}"`;
|
|
7046
6656
|
}).filter(
|
|
7047
6657
|
(token, index, values) => token !== "OR" || index > 0 && index < values.length - 1 && values[index - 1] !== "OR" && values[index + 1] !== "OR"
|
|
7048
|
-
);
|
|
7049
|
-
|
|
6658
|
+
).join(" ");
|
|
6659
|
+
}
|
|
6660
|
+
var SEARCH_INDEX_STATE_BATCH_SIZE = 900;
|
|
6661
|
+
function shouldBulkSyncSearchIndex(options, changedCount) {
|
|
6662
|
+
if (options.isBulk != null) {
|
|
6663
|
+
return options.isBulk;
|
|
6664
|
+
}
|
|
6665
|
+
const threshold = options.bulkThreshold ?? SEARCH_INDEX_BULK_SYNC_THRESHOLD;
|
|
6666
|
+
return threshold > 0 && changedCount >= threshold;
|
|
6667
|
+
}
|
|
6668
|
+
function sessionContentHash(session) {
|
|
6669
|
+
return JSON.stringify([
|
|
6670
|
+
session.slug,
|
|
6671
|
+
session.title,
|
|
6672
|
+
session.directory,
|
|
6673
|
+
session.time_created,
|
|
6674
|
+
session.time_updated ?? session.time_created,
|
|
6675
|
+
session.stats.message_count,
|
|
6676
|
+
session.stats.total_input_tokens,
|
|
6677
|
+
session.stats.total_output_tokens,
|
|
6678
|
+
session.stats.total_cache_read_tokens ?? 0,
|
|
6679
|
+
session.stats.total_cache_create_tokens ?? 0,
|
|
6680
|
+
session.stats.total_cost,
|
|
6681
|
+
session.stats.cost_source ?? "",
|
|
6682
|
+
session.stats.total_tokens ?? 0
|
|
6683
|
+
]);
|
|
6684
|
+
}
|
|
6685
|
+
function searchIndexStateFromRows(indexedRows, messageCountRows) {
|
|
6686
|
+
return {
|
|
6687
|
+
contentHashBySessionId: new Map(
|
|
6688
|
+
indexedRows.map((row) => [String(row.session_id), String(row.content_hash ?? "")])
|
|
6689
|
+
),
|
|
6690
|
+
indexedMessageCountBySessionId: new Map(
|
|
6691
|
+
indexedRows.map((row) => [String(row.session_id), Number(row.indexed_message_count ?? 0)])
|
|
6692
|
+
),
|
|
6693
|
+
messageCountBySessionId: new Map(
|
|
6694
|
+
messageCountRows.map((row) => [String(row.session_id), Number(row.value ?? 0)])
|
|
6695
|
+
)
|
|
6696
|
+
};
|
|
6697
|
+
}
|
|
6698
|
+
function readSearchIndexState(db, agentName, sessionIds) {
|
|
6699
|
+
const rows = [];
|
|
6700
|
+
const uniqueSessionIds = [...new Set(sessionIds)];
|
|
6701
|
+
for (let offset = 0; offset < uniqueSessionIds.length; offset += SEARCH_INDEX_STATE_BATCH_SIZE) {
|
|
6702
|
+
const batch = uniqueSessionIds.slice(offset, offset + SEARCH_INDEX_STATE_BATCH_SIZE);
|
|
6703
|
+
const requestedRows = batch.map(() => "(?)").join(", ");
|
|
6704
|
+
const batchRows = db.prepare(
|
|
6705
|
+
`
|
|
6706
|
+
WITH requested_session_ids(session_id) AS (VALUES ${requestedRows})
|
|
6707
|
+
SELECT
|
|
6708
|
+
requested.session_id,
|
|
6709
|
+
documents.content_hash,
|
|
6710
|
+
documents.indexed_message_count,
|
|
6711
|
+
COUNT(messages.message_index) AS value
|
|
6712
|
+
FROM requested_session_ids AS requested
|
|
6713
|
+
LEFT JOIN session_documents AS documents
|
|
6714
|
+
ON documents.agent_name = ? AND documents.session_id = requested.session_id
|
|
6715
|
+
LEFT JOIN messages
|
|
6716
|
+
ON messages.agent_name = ? AND messages.session_id = requested.session_id
|
|
6717
|
+
GROUP BY
|
|
6718
|
+
requested.session_id,
|
|
6719
|
+
documents.content_hash,
|
|
6720
|
+
documents.indexed_message_count
|
|
6721
|
+
`
|
|
6722
|
+
).all(...batch, agentName, agentName);
|
|
6723
|
+
rows.push(...batchRows);
|
|
6724
|
+
}
|
|
6725
|
+
return searchIndexStateFromRows(rows, rows);
|
|
6726
|
+
}
|
|
6727
|
+
function searchIndexEntryNeedsUpdate(state, session) {
|
|
6728
|
+
const sessionId = session.id;
|
|
6729
|
+
return state.contentHashBySessionId.get(sessionId) !== sessionContentHash(session) || state.indexedMessageCountBySessionId.get(sessionId) !== (state.messageCountBySessionId.get(sessionId) ?? 0);
|
|
7050
6730
|
}
|
|
7051
6731
|
function loadSearchIndexEntry(agentName, change, loadSessionData) {
|
|
7052
6732
|
try {
|
|
@@ -7071,6 +6751,12 @@ function loadSearchIndexEntry(agentName, change, loadSessionData) {
|
|
|
7071
6751
|
return null;
|
|
7072
6752
|
}
|
|
7073
6753
|
}
|
|
6754
|
+
function* loadSearchIndexEntries(agentName, changes, loadSessionData) {
|
|
6755
|
+
for (const change of changes) {
|
|
6756
|
+
const entry = loadSearchIndexEntry(agentName, change, loadSessionData);
|
|
6757
|
+
if (entry) yield entry;
|
|
6758
|
+
}
|
|
6759
|
+
}
|
|
7074
6760
|
function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
7075
6761
|
const deleteRow = db.prepare(
|
|
7076
6762
|
"DELETE FROM session_documents WHERE agent_name = ? AND session_id = ?"
|
|
@@ -7142,8 +6828,9 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7142
6828
|
activity_time,
|
|
7143
6829
|
content_text,
|
|
7144
6830
|
content_hash,
|
|
6831
|
+
indexed_message_count,
|
|
7145
6832
|
indexed_at
|
|
7146
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
6833
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
7147
6834
|
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
7148
6835
|
slug = excluded.slug,
|
|
7149
6836
|
title = excluded.title,
|
|
@@ -7156,6 +6843,7 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7156
6843
|
activity_time = excluded.activity_time,
|
|
7157
6844
|
content_text = excluded.content_text,
|
|
7158
6845
|
content_hash = excluded.content_hash,
|
|
6846
|
+
indexed_message_count = excluded.indexed_message_count,
|
|
7159
6847
|
indexed_at = excluded.indexed_at
|
|
7160
6848
|
`);
|
|
7161
6849
|
for (const sessionId of new Set(removedSessionIds)) {
|
|
@@ -7164,6 +6852,7 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7164
6852
|
deleteMessageTools.run(agentName, sessionId, 0);
|
|
7165
6853
|
deleteMessages.run(agentName, sessionId, 0);
|
|
7166
6854
|
}
|
|
6855
|
+
let indexed = 0;
|
|
7167
6856
|
for (const entry of entries) {
|
|
7168
6857
|
const activityTime = entry.session.time_updated ?? entry.session.time_created;
|
|
7169
6858
|
upsertSessionRow(upsertIndexedSession, agentName, entry.session, null, entry.sortIndex, null);
|
|
@@ -7211,44 +6900,47 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7211
6900
|
activityTime,
|
|
7212
6901
|
entry.contentText,
|
|
7213
6902
|
entry.contentHash,
|
|
6903
|
+
entry.messages.length,
|
|
7214
6904
|
Date.now()
|
|
7215
6905
|
);
|
|
6906
|
+
indexed += 1;
|
|
7216
6907
|
}
|
|
6908
|
+
return indexed;
|
|
7217
6909
|
}
|
|
7218
6910
|
function syncSessionSearchIndex(agentName, sessions, loadSessionData, options = {}) {
|
|
7219
6911
|
return withCacheDb((db) => {
|
|
7220
6912
|
ensureFtsConsistency(db);
|
|
7221
6913
|
const startedAt = performance.now();
|
|
7222
6914
|
const existingRows = db.prepare(
|
|
7223
|
-
"SELECT session_id, content_hash FROM session_documents WHERE agent_name = ? ORDER BY id"
|
|
6915
|
+
"SELECT session_id, content_hash, indexed_message_count FROM session_documents WHERE agent_name = ? ORDER BY id"
|
|
7224
6916
|
).all(agentName);
|
|
7225
|
-
const existingMap = new Map(
|
|
7226
|
-
existingRows.map((row) => [String(row.session_id), String(row.content_hash ?? "")])
|
|
7227
|
-
);
|
|
7228
6917
|
const sessionSortIndexMap = new Map(sessions.map((session, index) => [session.id, index]));
|
|
7229
6918
|
const messageCountRows = db.prepare(
|
|
7230
6919
|
"SELECT session_id, COUNT(*) AS value FROM messages WHERE agent_name = ? GROUP BY session_id"
|
|
7231
6920
|
).all(agentName);
|
|
7232
|
-
const
|
|
7233
|
-
messageCountRows.map((row) => [String(row.session_id), Number(row.value ?? 0)])
|
|
7234
|
-
);
|
|
6921
|
+
const searchIndexState = searchIndexStateFromRows(existingRows, messageCountRows);
|
|
7235
6922
|
const sessionMap = new Map(sessions.map((session) => [session.id, session]));
|
|
7236
6923
|
const toDelete = existingRows.map((row) => String(row.session_id)).filter((sessionId) => !sessionMap.has(sessionId));
|
|
7237
6924
|
const toUpsert = sessions.filter(
|
|
7238
|
-
(session) =>
|
|
6925
|
+
(session) => searchIndexEntryNeedsUpdate(searchIndexState, session)
|
|
7239
6926
|
);
|
|
7240
6927
|
const changedCount = toDelete.length + toUpsert.length;
|
|
7241
6928
|
const isBulk = shouldBulkSyncSearchIndex(options, changedCount);
|
|
7242
|
-
const
|
|
7243
|
-
|
|
6929
|
+
const changes = toUpsert.map((session) => ({
|
|
6930
|
+
session,
|
|
6931
|
+
sortIndex: sessionSortIndexMap.get(session.id) ?? 0
|
|
6932
|
+
}));
|
|
6933
|
+
let indexed = 0;
|
|
6934
|
+
const writeRows = () => {
|
|
6935
|
+
indexed = writeSearchIndexRows(
|
|
6936
|
+
db,
|
|
7244
6937
|
agentName,
|
|
7245
|
-
|
|
7246
|
-
loadSessionData
|
|
7247
|
-
)
|
|
7248
|
-
|
|
7249
|
-
const writeRows = () => writeSearchIndexRows(db, agentName, toDelete, loaded);
|
|
6938
|
+
toDelete,
|
|
6939
|
+
loadSearchIndexEntries(agentName, changes, loadSessionData)
|
|
6940
|
+
);
|
|
6941
|
+
};
|
|
7250
6942
|
let rebuildDurationMs;
|
|
7251
|
-
const needsRebuild = isBulk &&
|
|
6943
|
+
const needsRebuild = isBulk && changedCount > 0;
|
|
7252
6944
|
if (needsRebuild) {
|
|
7253
6945
|
db.transaction(() => {
|
|
7254
6946
|
dropSearchTriggers(db);
|
|
@@ -7270,8 +6962,8 @@ function syncSessionSearchIndex(agentName, sessions, loadSessionData, options =
|
|
|
7270
6962
|
sessions: sessions.length,
|
|
7271
6963
|
changed: toUpsert.length,
|
|
7272
6964
|
deleted: toDelete.length,
|
|
7273
|
-
indexed
|
|
7274
|
-
skipped: toUpsert.length -
|
|
6965
|
+
indexed,
|
|
6966
|
+
skipped: toUpsert.length - indexed,
|
|
7275
6967
|
durationMs: performance.now() - startedAt,
|
|
7276
6968
|
rebuildDurationMs
|
|
7277
6969
|
};
|
|
@@ -7293,24 +6985,28 @@ function syncSessionSearchIndexChanges(agentName, changes, removedSessionIds, lo
|
|
|
7293
6985
|
return withCacheDb((db) => {
|
|
7294
6986
|
ensureFtsConsistency(db);
|
|
7295
6987
|
const startedAt = performance.now();
|
|
7296
|
-
const
|
|
7297
|
-
|
|
6988
|
+
const searchIndexState = readSearchIndexState(
|
|
6989
|
+
db,
|
|
6990
|
+
agentName,
|
|
6991
|
+
changes.map(({ session }) => session.id)
|
|
7298
6992
|
);
|
|
7299
|
-
const
|
|
7300
|
-
|
|
6993
|
+
const toUpsert = changes.filter(
|
|
6994
|
+
({ session }) => searchIndexEntryNeedsUpdate(searchIndexState, session)
|
|
7301
6995
|
);
|
|
7302
|
-
const toUpsert = changes.filter(({ session }) => {
|
|
7303
|
-
const indexed = getIndexedRow.get(agentName, session.id);
|
|
7304
|
-
const messageCount = getMessageCount.get(agentName, session.id);
|
|
7305
|
-
return String(indexed?.content_hash ?? "") !== sessionContentHash(session) || Number(messageCount?.value ?? 0) !== session.stats.message_count;
|
|
7306
|
-
});
|
|
7307
6996
|
const uniqueRemovedSessionIds = Array.from(new Set(removedSessionIds));
|
|
7308
6997
|
const changedCount = uniqueRemovedSessionIds.length + toUpsert.length;
|
|
7309
6998
|
const isBulk = shouldBulkSyncSearchIndex(options, changedCount);
|
|
7310
|
-
|
|
7311
|
-
const writeRows = () =>
|
|
6999
|
+
let indexed = 0;
|
|
7000
|
+
const writeRows = () => {
|
|
7001
|
+
indexed = writeSearchIndexRows(
|
|
7002
|
+
db,
|
|
7003
|
+
agentName,
|
|
7004
|
+
uniqueRemovedSessionIds,
|
|
7005
|
+
loadSearchIndexEntries(agentName, toUpsert, loadSessionData)
|
|
7006
|
+
);
|
|
7007
|
+
};
|
|
7312
7008
|
let rebuildDurationMs;
|
|
7313
|
-
const needsRebuild = isBulk &&
|
|
7009
|
+
const needsRebuild = isBulk && changedCount > 0;
|
|
7314
7010
|
if (needsRebuild) {
|
|
7315
7011
|
db.transaction(() => {
|
|
7316
7012
|
dropSearchTriggers(db);
|
|
@@ -7332,8 +7028,8 @@ function syncSessionSearchIndexChanges(agentName, changes, removedSessionIds, lo
|
|
|
7332
7028
|
sessions: changes.length,
|
|
7333
7029
|
changed: toUpsert.length,
|
|
7334
7030
|
deleted: uniqueRemovedSessionIds.length,
|
|
7335
|
-
indexed
|
|
7336
|
-
skipped: toUpsert.length -
|
|
7031
|
+
indexed,
|
|
7032
|
+
skipped: toUpsert.length - indexed,
|
|
7337
7033
|
durationMs: performance.now() - startedAt,
|
|
7338
7034
|
rebuildDurationMs
|
|
7339
7035
|
};
|
|
@@ -7347,25 +7043,26 @@ function mergeSearchLists(left, right) {
|
|
|
7347
7043
|
return values.length > 0 ? [...new Set(values)] : void 0;
|
|
7348
7044
|
}
|
|
7349
7045
|
function mergeSearchQueryOptions(query, options) {
|
|
7350
|
-
const
|
|
7046
|
+
const parsed = parseSearchQuery(query);
|
|
7351
7047
|
return {
|
|
7352
|
-
text:
|
|
7048
|
+
text: parsed.text || (parsed.hasQualifiers ? "" : query.trim()),
|
|
7353
7049
|
options: {
|
|
7354
7050
|
...options,
|
|
7355
|
-
agent: options.agent ??
|
|
7356
|
-
project: options.project ??
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7051
|
+
agent: options.agent ?? parsed.filters.agent,
|
|
7052
|
+
project: options.project ?? parsed.filters.project,
|
|
7053
|
+
projectKind: options.projectKind ?? parsed.filters.projectKind,
|
|
7054
|
+
projectKey: options.projectKey ?? parsed.filters.projectKey,
|
|
7055
|
+
cwd: options.cwd ?? parsed.filters.cwd,
|
|
7056
|
+
tags: mergeSearchLists(options.tags, parsed.filters.tags),
|
|
7057
|
+
tools: mergeSearchLists(options.tools, parsed.filters.tools),
|
|
7058
|
+
file: options.file ?? parsed.filters.file,
|
|
7059
|
+
fileKind: options.fileKind ?? parsed.filters.fileKind,
|
|
7060
|
+
costMin: options.costMin ?? parsed.filters.costMin,
|
|
7061
|
+
costMax: options.costMax ?? parsed.filters.costMax,
|
|
7062
|
+
costMinExclusive: options.costMinExclusive ?? parsed.filters.costMinExclusive,
|
|
7063
|
+
costMaxExclusive: options.costMaxExclusive ?? parsed.filters.costMaxExclusive
|
|
7367
7064
|
},
|
|
7368
|
-
parsed
|
|
7065
|
+
parsed
|
|
7369
7066
|
};
|
|
7370
7067
|
}
|
|
7371
7068
|
function sessionMatchesSearchCost(session, options) {
|
|
@@ -7389,13 +7086,20 @@ function buildSessionSearchFilters(options) {
|
|
|
7389
7086
|
clauses.push("s.agent_name = ?");
|
|
7390
7087
|
params.push(options.agent);
|
|
7391
7088
|
}
|
|
7392
|
-
if (options.projectKey) {
|
|
7393
|
-
|
|
7394
|
-
|
|
7089
|
+
if (options.projectKind || options.projectKey) {
|
|
7090
|
+
if (options.projectKind && options.projectKey) {
|
|
7091
|
+
clauses.push("s.project_identity_kind = ? AND s.project_identity_key = ?");
|
|
7092
|
+
params.push(options.projectKind, options.projectKey);
|
|
7093
|
+
} else {
|
|
7094
|
+
clauses.push("0");
|
|
7095
|
+
}
|
|
7395
7096
|
}
|
|
7396
7097
|
if (options.cwd) {
|
|
7397
|
-
|
|
7398
|
-
|
|
7098
|
+
const identity = computeIdentity(options.cwd, realFs);
|
|
7099
|
+
clauses.push(
|
|
7100
|
+
"((s.project_identity_kind = ? AND s.project_identity_key = ?) OR LOWER(s.directory) LIKE ? ESCAPE '\\')"
|
|
7101
|
+
);
|
|
7102
|
+
params.push(identity.kind, identity.key, likePattern(options.cwd));
|
|
7399
7103
|
}
|
|
7400
7104
|
if (options.project) {
|
|
7401
7105
|
clauses.push(
|
|
@@ -7640,10 +7344,13 @@ function searchSessions(query, options = {}) {
|
|
|
7640
7344
|
}
|
|
7641
7345
|
function fileActivityFilters(options) {
|
|
7642
7346
|
const path2 = options.path ? normalizeFilePathSearch(options.path) : "";
|
|
7347
|
+
const cwdIdentity = options.cwd ? computeIdentity(options.cwd, realFs) : null;
|
|
7643
7348
|
return {
|
|
7349
|
+
projectKind: options.projectKind ?? null,
|
|
7644
7350
|
projectKey: options.projectKey ?? null,
|
|
7645
7351
|
projectLike: options.project ? likePattern(options.project) : null,
|
|
7646
|
-
|
|
7352
|
+
cwdKind: cwdIdentity?.kind ?? null,
|
|
7353
|
+
cwdKey: cwdIdentity?.key ?? null,
|
|
7647
7354
|
cwdLike: options.cwd ? likePattern(options.cwd) : null,
|
|
7648
7355
|
path: path2,
|
|
7649
7356
|
pathLike: path2 ? likePattern(path2) : null
|
|
@@ -7672,9 +7379,13 @@ function buildFileActivityWhere(options) {
|
|
|
7672
7379
|
clauses.push("fa.session_id = ?");
|
|
7673
7380
|
params.push(options.sessionId);
|
|
7674
7381
|
}
|
|
7675
|
-
if (filters.projectKey != null) {
|
|
7676
|
-
|
|
7677
|
-
|
|
7382
|
+
if (filters.projectKind != null || filters.projectKey != null) {
|
|
7383
|
+
if (filters.projectKind != null && filters.projectKey != null) {
|
|
7384
|
+
clauses.push("s.project_identity_kind = ? AND fa.project_identity_key = ?");
|
|
7385
|
+
params.push(filters.projectKind, filters.projectKey);
|
|
7386
|
+
} else {
|
|
7387
|
+
clauses.push("0");
|
|
7388
|
+
}
|
|
7678
7389
|
}
|
|
7679
7390
|
if (filters.projectLike != null) {
|
|
7680
7391
|
clauses.push(
|
|
@@ -7683,8 +7394,10 @@ function buildFileActivityWhere(options) {
|
|
|
7683
7394
|
params.push(filters.projectLike, filters.projectLike, filters.projectLike);
|
|
7684
7395
|
}
|
|
7685
7396
|
if (filters.cwdKey != null) {
|
|
7686
|
-
clauses.push(
|
|
7687
|
-
|
|
7397
|
+
clauses.push(
|
|
7398
|
+
"((s.project_identity_kind = ? AND s.project_identity_key = ?) OR LOWER(s.directory) LIKE ? ESCAPE '\\')"
|
|
7399
|
+
);
|
|
7400
|
+
params.push(filters.cwdKind, filters.cwdKey, filters.cwdLike);
|
|
7688
7401
|
}
|
|
7689
7402
|
if (filters.pathLike != null) {
|
|
7690
7403
|
const pathQuery = filePathFtsQuery(filters.path);
|
|
@@ -7782,6 +7495,7 @@ function searchFileActivitySessions(query, options = {}) {
|
|
|
7782
7495
|
if (!path2) return [];
|
|
7783
7496
|
const rows = listFileActivity({
|
|
7784
7497
|
agent: search.options.agent,
|
|
7498
|
+
projectKind: search.options.projectKind,
|
|
7785
7499
|
projectKey: search.options.projectKey,
|
|
7786
7500
|
project: search.options.project,
|
|
7787
7501
|
cwd: search.options.cwd,
|
|
@@ -8258,9 +7972,7 @@ function sessionSignature(session) {
|
|
|
8258
7972
|
]);
|
|
8259
7973
|
}
|
|
8260
7974
|
function sortSessions(sessions) {
|
|
8261
|
-
return
|
|
8262
|
-
(a, b) => (b.time_updated ?? b.time_created) - (a.time_updated ?? a.time_created)
|
|
8263
|
-
);
|
|
7975
|
+
return sortSessionsByActivity(sessions);
|
|
8264
7976
|
}
|
|
8265
7977
|
function computeSessionDiff(cachedSessions, updatedSessions, changedIds = [], signature = sessionSignature) {
|
|
8266
7978
|
const cachedMap = new Map(cachedSessions.map((session) => [session.id, session]));
|
|
@@ -8404,6 +8116,47 @@ async function ensureSessionTags(agent, sessions, workerUrl) {
|
|
|
8404
8116
|
return ensureSessionTagsSync(agent, sessions);
|
|
8405
8117
|
}
|
|
8406
8118
|
}
|
|
8119
|
+
async function finalizeAgentScan(agent, sessions, context) {
|
|
8120
|
+
const { finalization, options, timing, agentStart, onProgress } = context;
|
|
8121
|
+
const isIncremental = finalization.kind === "incremental";
|
|
8122
|
+
if (!isIncremental) {
|
|
8123
|
+
onProgress?.({ agent: agent.name, phase: "complete", newCount: sessions.length });
|
|
8124
|
+
}
|
|
8125
|
+
const identityStart = performance.now();
|
|
8126
|
+
const sessionsWithIdentity = attachMissingProjectIdentities(sessions);
|
|
8127
|
+
timing.identity = performance.now() - identityStart;
|
|
8128
|
+
let tagged = { sessions: sessionsWithIdentity, changed: false };
|
|
8129
|
+
if (finalization.kind !== "cache-only") {
|
|
8130
|
+
const tagsStart = performance.now();
|
|
8131
|
+
tagged = options.includeSmartTags === false ? tagged : await ensureSessionTags(agent, sessionsWithIdentity, options.smartTagWorkerUrl);
|
|
8132
|
+
timing.tags = performance.now() - tagsStart;
|
|
8133
|
+
}
|
|
8134
|
+
if (options.writeCache !== false) {
|
|
8135
|
+
if (finalization.kind === "incremental") {
|
|
8136
|
+
saveCachedSessionDiff(
|
|
8137
|
+
agent,
|
|
8138
|
+
finalization.cached.sessions,
|
|
8139
|
+
tagged.sessions,
|
|
8140
|
+
finalization.changedIds
|
|
8141
|
+
);
|
|
8142
|
+
} else if (finalization.kind === "unchanged" && tagged.changed) {
|
|
8143
|
+
saveCachedSessionDiff(agent, finalization.cached.sessions, tagged.sessions);
|
|
8144
|
+
}
|
|
8145
|
+
}
|
|
8146
|
+
if (isIncremental) {
|
|
8147
|
+
onProgress?.({ agent: agent.name, phase: "complete", newCount: tagged.sessions.length });
|
|
8148
|
+
}
|
|
8149
|
+
const heads = filterSessions(tagged.sessions, options);
|
|
8150
|
+
timing.total = performance.now() - agentStart;
|
|
8151
|
+
return {
|
|
8152
|
+
agent,
|
|
8153
|
+
heads,
|
|
8154
|
+
fromCache: true,
|
|
8155
|
+
...isIncremental ? { refreshed: true } : {},
|
|
8156
|
+
timing,
|
|
8157
|
+
cacheTimestamp: isIncremental ? finalization.cacheTimestamp : finalization.cached.timestamp
|
|
8158
|
+
};
|
|
8159
|
+
}
|
|
8407
8160
|
async function scanAgentSmart(agent, options, onProgress) {
|
|
8408
8161
|
const agentStart = performance.now();
|
|
8409
8162
|
const timing = { total: 0 };
|
|
@@ -8424,19 +8177,13 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
8424
8177
|
phase: "cache",
|
|
8425
8178
|
cachedCount: cached.sessions.length
|
|
8426
8179
|
});
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8430
|
-
timing.identity = performance.now() - t32;
|
|
8431
|
-
const filtered3 = filterSessions(cachedWithIdentity2, options);
|
|
8432
|
-
timing.total = performance.now() - agentStart;
|
|
8433
|
-
return {
|
|
8434
|
-
agent,
|
|
8435
|
-
heads: filtered3,
|
|
8436
|
-
fromCache: true,
|
|
8180
|
+
return finalizeAgentScan(agent, cached.sessions, {
|
|
8181
|
+
finalization: { kind: "cache-only", cached },
|
|
8182
|
+
options,
|
|
8437
8183
|
timing,
|
|
8438
|
-
|
|
8439
|
-
|
|
8184
|
+
agentStart,
|
|
8185
|
+
onProgress
|
|
8186
|
+
});
|
|
8440
8187
|
}
|
|
8441
8188
|
const isAvail = agent.isAvailable();
|
|
8442
8189
|
if (!isAvail) {
|
|
@@ -8464,55 +8211,26 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
8464
8211
|
agent.incrementalScan(cached.sessions, checkResult.changedIds || [])
|
|
8465
8212
|
);
|
|
8466
8213
|
timing.scan = performance.now() - t2;
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
agent,
|
|
8476
|
-
cached.sessions,
|
|
8477
|
-
tagged2.sessions,
|
|
8478
|
-
checkResult.changedIds ?? []
|
|
8479
|
-
);
|
|
8480
|
-
}
|
|
8481
|
-
onProgress?.({
|
|
8482
|
-
agent: agent.name,
|
|
8483
|
-
phase: "complete",
|
|
8484
|
-
newCount: tagged2.sessions.length
|
|
8485
|
-
});
|
|
8486
|
-
const filtered3 = filterSessions(tagged2.sessions, options);
|
|
8487
|
-
timing.total = performance.now() - agentStart;
|
|
8488
|
-
return {
|
|
8489
|
-
agent,
|
|
8490
|
-
heads: filtered3,
|
|
8491
|
-
fromCache: true,
|
|
8492
|
-
refreshed: true,
|
|
8214
|
+
return finalizeAgentScan(agent, updatedSessions, {
|
|
8215
|
+
finalization: {
|
|
8216
|
+
kind: "incremental",
|
|
8217
|
+
cached,
|
|
8218
|
+
changedIds: checkResult.changedIds ?? [],
|
|
8219
|
+
cacheTimestamp: checkResult.timestamp
|
|
8220
|
+
},
|
|
8221
|
+
options,
|
|
8493
8222
|
timing,
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
onProgress?.({ agent: agent.name, phase: "complete", newCount: cached.sessions.length });
|
|
8498
|
-
const t3 = performance.now();
|
|
8499
|
-
const cachedWithIdentity = attachMissingProjectIdentities(cached.sessions);
|
|
8500
|
-
timing.identity = performance.now() - t3;
|
|
8501
|
-
const t4 = performance.now();
|
|
8502
|
-
const tagged = options.includeSmartTags === false ? { sessions: cachedWithIdentity, changed: false } : await ensureSessionTags(agent, cachedWithIdentity, options.smartTagWorkerUrl);
|
|
8503
|
-
timing.tags = performance.now() - t4;
|
|
8504
|
-
if (tagged.changed && options.writeCache !== false) {
|
|
8505
|
-
saveCachedSessionDiff(agent, cached.sessions, tagged.sessions);
|
|
8223
|
+
agentStart,
|
|
8224
|
+
onProgress
|
|
8225
|
+
});
|
|
8506
8226
|
}
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
agent,
|
|
8511
|
-
heads: filtered2,
|
|
8512
|
-
fromCache: true,
|
|
8227
|
+
return finalizeAgentScan(agent, cached.sessions, {
|
|
8228
|
+
finalization: { kind: "unchanged", cached },
|
|
8229
|
+
options,
|
|
8513
8230
|
timing,
|
|
8514
|
-
|
|
8515
|
-
|
|
8231
|
+
agentStart,
|
|
8232
|
+
onProgress
|
|
8233
|
+
});
|
|
8516
8234
|
}
|
|
8517
8235
|
}
|
|
8518
8236
|
if (options.cacheOnly) {
|
|
@@ -8560,9 +8278,9 @@ async function scanAgentFull(agent, options, onProgress, timing = { total: 0 },
|
|
|
8560
8278
|
markAgentFullSyncCompleted(agent.name);
|
|
8561
8279
|
}
|
|
8562
8280
|
onProgress?.({ agent: agent.name, phase: "complete", newCount: tagged.sessions.length });
|
|
8563
|
-
const
|
|
8281
|
+
const filtered = filterSessions(tagged.sessions, options);
|
|
8564
8282
|
timing.total = performance.now() - agentStart;
|
|
8565
|
-
return { agent, heads:
|
|
8283
|
+
return { agent, heads: filtered, fromCache: false, timing };
|
|
8566
8284
|
} catch (err) {
|
|
8567
8285
|
console.error(`Error scanning ${agent.name}:`, err);
|
|
8568
8286
|
return { agent, heads: [], fromCache: false };
|
|
@@ -8610,67 +8328,35 @@ async function scanSessions(options = {}, onProgress) {
|
|
|
8610
8328
|
async function scanSessionsAsync(options = {}, onProgress) {
|
|
8611
8329
|
return scanSessions(options, onProgress);
|
|
8612
8330
|
}
|
|
8613
|
-
var
|
|
8614
|
-
var
|
|
8331
|
+
var STATE_DB_FILENAME = "state.db";
|
|
8332
|
+
var STATE_SCHEMA_VERSION = 2;
|
|
8615
8333
|
var MEMORY_STATE_STORE = "memory";
|
|
8616
|
-
var
|
|
8617
|
-
var BookmarkStorageUnavailableError = class extends Error {
|
|
8334
|
+
var StateStorageUnavailableError = class extends Error {
|
|
8618
8335
|
constructor() {
|
|
8619
8336
|
super("SQLite state database is unavailable");
|
|
8620
|
-
this.name = "
|
|
8337
|
+
this.name = "StateStorageUnavailableError";
|
|
8621
8338
|
}
|
|
8622
8339
|
};
|
|
8623
8340
|
function getStateDir() {
|
|
8624
|
-
if (process.env.CODESESH_STATE_DIR)
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
const p = platform2();
|
|
8628
|
-
if (p === "darwin") {
|
|
8341
|
+
if (process.env.CODESESH_STATE_DIR) return process.env.CODESESH_STATE_DIR;
|
|
8342
|
+
const currentPlatform = platform2();
|
|
8343
|
+
if (currentPlatform === "darwin") {
|
|
8629
8344
|
return join12(homedir5(), "Library", "Application Support", "codesesh");
|
|
8630
8345
|
}
|
|
8631
|
-
if (
|
|
8346
|
+
if (currentPlatform === "win32") {
|
|
8632
8347
|
const appData = process.env.APPDATA ?? process.env.LOCALAPPDATA;
|
|
8633
8348
|
return join12(appData ?? join12(homedir5(), "AppData", "Roaming"), "codesesh");
|
|
8634
8349
|
}
|
|
8635
8350
|
return join12(process.env.XDG_DATA_HOME ?? join12(homedir5(), ".local", "share"), "codesesh");
|
|
8636
8351
|
}
|
|
8637
8352
|
function getStateDbPath() {
|
|
8638
|
-
return join12(getStateDir(),
|
|
8353
|
+
return join12(getStateDir(), STATE_DB_FILENAME);
|
|
8639
8354
|
}
|
|
8640
8355
|
function useMemoryStateStore() {
|
|
8641
8356
|
return process.env.CODESESH_STATE_STORE === MEMORY_STATE_STORE;
|
|
8642
8357
|
}
|
|
8643
|
-
function
|
|
8644
|
-
return JSON.stringify([agentKey, sessionId]);
|
|
8645
|
-
}
|
|
8646
|
-
function getActivityTime(bookmark) {
|
|
8647
|
-
return bookmark.time_updated ?? bookmark.time_created;
|
|
8648
|
-
}
|
|
8649
|
-
function sortBookmarks(bookmarks) {
|
|
8650
|
-
return bookmarks.sort((a, b) => {
|
|
8651
|
-
const activityDelta = getActivityTime(b) - getActivityTime(a);
|
|
8652
|
-
return activityDelta || b.bookmarked_at - a.bookmarked_at;
|
|
8653
|
-
});
|
|
8654
|
-
}
|
|
8655
|
-
function listMemoryBookmarks() {
|
|
8656
|
-
return sortBookmarks(Array.from(memoryBookmarks.values()));
|
|
8657
|
-
}
|
|
8658
|
-
function upsertMemoryBookmark(bookmark) {
|
|
8659
|
-
const key = getBookmarkKey(bookmark.agentKey, bookmark.sessionId);
|
|
8660
|
-
const saved = {
|
|
8661
|
-
...bookmark,
|
|
8662
|
-
bookmarked_at: memoryBookmarks.get(key)?.bookmarked_at ?? Date.now()
|
|
8663
|
-
};
|
|
8664
|
-
memoryBookmarks.set(key, saved);
|
|
8665
|
-
return saved;
|
|
8666
|
-
}
|
|
8667
|
-
function createStateSchema(db) {
|
|
8358
|
+
function createBookmarksTable(db) {
|
|
8668
8359
|
db.exec(`
|
|
8669
|
-
CREATE TABLE IF NOT EXISTS state_meta (
|
|
8670
|
-
key TEXT PRIMARY KEY,
|
|
8671
|
-
value TEXT NOT NULL
|
|
8672
|
-
);
|
|
8673
|
-
|
|
8674
8360
|
CREATE TABLE IF NOT EXISTS bookmarks (
|
|
8675
8361
|
agent_name TEXT NOT NULL,
|
|
8676
8362
|
session_id TEXT NOT NULL,
|
|
@@ -8685,6 +8371,27 @@ function createStateSchema(db) {
|
|
|
8685
8371
|
);
|
|
8686
8372
|
`);
|
|
8687
8373
|
}
|
|
8374
|
+
function createSessionAliasesTable(db) {
|
|
8375
|
+
db.exec(`
|
|
8376
|
+
CREATE TABLE IF NOT EXISTS session_aliases (
|
|
8377
|
+
agent_name TEXT NOT NULL,
|
|
8378
|
+
session_id TEXT NOT NULL,
|
|
8379
|
+
alias TEXT NOT NULL,
|
|
8380
|
+
updated_at INTEGER NOT NULL,
|
|
8381
|
+
PRIMARY KEY (agent_name, session_id)
|
|
8382
|
+
);
|
|
8383
|
+
`);
|
|
8384
|
+
}
|
|
8385
|
+
function createStateSchema(db) {
|
|
8386
|
+
db.exec(`
|
|
8387
|
+
CREATE TABLE IF NOT EXISTS state_meta (
|
|
8388
|
+
key TEXT PRIMARY KEY,
|
|
8389
|
+
value TEXT NOT NULL
|
|
8390
|
+
);
|
|
8391
|
+
`);
|
|
8392
|
+
createBookmarksTable(db);
|
|
8393
|
+
createSessionAliasesTable(db);
|
|
8394
|
+
}
|
|
8688
8395
|
function readLegacyStateVersion(db) {
|
|
8689
8396
|
if (!tableExists(db, "state_meta") || !columnExists(db, "state_meta", "key") || !columnExists(db, "state_meta", "value")) {
|
|
8690
8397
|
return 0;
|
|
@@ -8694,13 +8401,9 @@ function readLegacyStateVersion(db) {
|
|
|
8694
8401
|
}
|
|
8695
8402
|
function getCurrentStateSchemaVersion(db) {
|
|
8696
8403
|
const userVersion = getUserVersion(db);
|
|
8697
|
-
if (userVersion > 0)
|
|
8698
|
-
return userVersion;
|
|
8699
|
-
}
|
|
8404
|
+
if (userVersion > 0) return userVersion;
|
|
8700
8405
|
const legacyVersion = readLegacyStateVersion(db);
|
|
8701
|
-
if (legacyVersion > 0)
|
|
8702
|
-
return legacyVersion;
|
|
8703
|
-
}
|
|
8406
|
+
if (legacyVersion > 0) return legacyVersion;
|
|
8704
8407
|
return tableExists(db, "bookmarks") ? 1 : 0;
|
|
8705
8408
|
}
|
|
8706
8409
|
function hasAnyStateSchema(db) {
|
|
@@ -8708,14 +8411,14 @@ function hasAnyStateSchema(db) {
|
|
|
8708
8411
|
}
|
|
8709
8412
|
function setStateSchemaVersion(db) {
|
|
8710
8413
|
createStateSchema(db);
|
|
8711
|
-
setUserVersion(db,
|
|
8414
|
+
setUserVersion(db, STATE_SCHEMA_VERSION);
|
|
8712
8415
|
db.prepare(
|
|
8713
8416
|
`
|
|
8714
8417
|
INSERT INTO state_meta(key, value)
|
|
8715
8418
|
VALUES ('version', ?)
|
|
8716
8419
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
8717
8420
|
`
|
|
8718
|
-
).run(String(
|
|
8421
|
+
).run(String(STATE_SCHEMA_VERSION));
|
|
8719
8422
|
}
|
|
8720
8423
|
function ensureSchema2(db, dbPath) {
|
|
8721
8424
|
const currentVersion = getCurrentStateSchemaVersion(db);
|
|
@@ -8726,22 +8429,22 @@ function ensureSchema2(db, dbPath) {
|
|
|
8726
8429
|
runSchemaMigrations(db, {
|
|
8727
8430
|
dbPath,
|
|
8728
8431
|
currentVersion,
|
|
8729
|
-
targetVersion:
|
|
8432
|
+
targetVersion: STATE_SCHEMA_VERSION,
|
|
8730
8433
|
backupLabel: "state-migration",
|
|
8731
|
-
backupTables: ["bookmarks"],
|
|
8732
|
-
migrations: [
|
|
8434
|
+
backupTables: ["bookmarks", "session_aliases"],
|
|
8435
|
+
migrations: [
|
|
8436
|
+
{ version: 1, migrate: createBookmarksTable },
|
|
8437
|
+
{ version: 2, migrate: createSessionAliasesTable }
|
|
8438
|
+
]
|
|
8733
8439
|
});
|
|
8734
|
-
|
|
8735
|
-
if (getUserVersion(db) <= BOOKMARK_SCHEMA_VERSION) {
|
|
8440
|
+
if (currentVersion <= STATE_SCHEMA_VERSION) {
|
|
8736
8441
|
setStateSchemaVersion(db);
|
|
8737
8442
|
}
|
|
8738
8443
|
}
|
|
8739
8444
|
function withStateDb(fn) {
|
|
8740
8445
|
const statePath = getStateDbPath();
|
|
8741
8446
|
const db = openDb(statePath);
|
|
8742
|
-
if (!db)
|
|
8743
|
-
throw new BookmarkStorageUnavailableError();
|
|
8744
|
-
}
|
|
8447
|
+
if (!db) throw new StateStorageUnavailableError();
|
|
8745
8448
|
try {
|
|
8746
8449
|
ensureSchema2(db, statePath);
|
|
8747
8450
|
return fn(db);
|
|
@@ -8749,6 +8452,31 @@ function withStateDb(fn) {
|
|
|
8749
8452
|
db.close();
|
|
8750
8453
|
}
|
|
8751
8454
|
}
|
|
8455
|
+
var memoryBookmarks = /* @__PURE__ */ new Map();
|
|
8456
|
+
function getBookmarkKey(agentKey, sessionId) {
|
|
8457
|
+
return JSON.stringify([agentKey, sessionId]);
|
|
8458
|
+
}
|
|
8459
|
+
function getActivityTime(bookmark) {
|
|
8460
|
+
return bookmark.time_updated ?? bookmark.time_created;
|
|
8461
|
+
}
|
|
8462
|
+
function sortBookmarks(bookmarks) {
|
|
8463
|
+
return bookmarks.sort((a, b) => {
|
|
8464
|
+
const activityDelta = getActivityTime(b) - getActivityTime(a);
|
|
8465
|
+
return activityDelta || b.bookmarked_at - a.bookmarked_at;
|
|
8466
|
+
});
|
|
8467
|
+
}
|
|
8468
|
+
function listMemoryBookmarks() {
|
|
8469
|
+
return sortBookmarks(Array.from(memoryBookmarks.values()));
|
|
8470
|
+
}
|
|
8471
|
+
function upsertMemoryBookmark(bookmark) {
|
|
8472
|
+
const key = getBookmarkKey(bookmark.agentKey, bookmark.sessionId);
|
|
8473
|
+
const saved = {
|
|
8474
|
+
...bookmark,
|
|
8475
|
+
bookmarked_at: memoryBookmarks.get(key)?.bookmarked_at ?? Date.now()
|
|
8476
|
+
};
|
|
8477
|
+
memoryBookmarks.set(key, saved);
|
|
8478
|
+
return saved;
|
|
8479
|
+
}
|
|
8752
8480
|
function toBookmarkRecord(row) {
|
|
8753
8481
|
return {
|
|
8754
8482
|
agentKey: String(row.agent_name ?? ""),
|
|
@@ -8921,6 +8649,78 @@ function deleteBookmark(agentKey, sessionId) {
|
|
|
8921
8649
|
).run(agentKey, sessionId);
|
|
8922
8650
|
});
|
|
8923
8651
|
}
|
|
8652
|
+
var SESSION_ALIAS_MAX_LENGTH = 160;
|
|
8653
|
+
var memoryAliases = /* @__PURE__ */ new Map();
|
|
8654
|
+
function getAliasKey(agentKey, sessionId) {
|
|
8655
|
+
return JSON.stringify([agentKey, sessionId]);
|
|
8656
|
+
}
|
|
8657
|
+
function toSessionAlias(row) {
|
|
8658
|
+
return {
|
|
8659
|
+
agentKey: String(row.agent_name ?? ""),
|
|
8660
|
+
sessionId: String(row.session_id ?? ""),
|
|
8661
|
+
alias: String(row.alias ?? ""),
|
|
8662
|
+
updated_at: Number(row.updated_at ?? 0)
|
|
8663
|
+
};
|
|
8664
|
+
}
|
|
8665
|
+
function normalizeSessionAlias(value) {
|
|
8666
|
+
const alias = value.trim();
|
|
8667
|
+
if (!alias || alias.length > SESSION_ALIAS_MAX_LENGTH) return null;
|
|
8668
|
+
return alias;
|
|
8669
|
+
}
|
|
8670
|
+
function listSessionAliases() {
|
|
8671
|
+
if (useMemoryStateStore()) return [...memoryAliases.values()];
|
|
8672
|
+
return withStateDb(
|
|
8673
|
+
(db) => db.prepare(
|
|
8674
|
+
`
|
|
8675
|
+
SELECT agent_name, session_id, alias, updated_at
|
|
8676
|
+
FROM session_aliases
|
|
8677
|
+
ORDER BY updated_at DESC
|
|
8678
|
+
`
|
|
8679
|
+
).all().map(toSessionAlias)
|
|
8680
|
+
);
|
|
8681
|
+
}
|
|
8682
|
+
function upsertSessionAlias(agentKey, sessionId, alias) {
|
|
8683
|
+
const normalizedAlias = normalizeSessionAlias(alias);
|
|
8684
|
+
if (!normalizedAlias) {
|
|
8685
|
+
throw new TypeError("Invalid session alias");
|
|
8686
|
+
}
|
|
8687
|
+
const saved = {
|
|
8688
|
+
agentKey,
|
|
8689
|
+
sessionId,
|
|
8690
|
+
alias: normalizedAlias,
|
|
8691
|
+
updated_at: Date.now()
|
|
8692
|
+
};
|
|
8693
|
+
if (useMemoryStateStore()) {
|
|
8694
|
+
memoryAliases.set(getAliasKey(agentKey, sessionId), saved);
|
|
8695
|
+
return saved;
|
|
8696
|
+
}
|
|
8697
|
+
return withStateDb((db) => {
|
|
8698
|
+
db.prepare(
|
|
8699
|
+
`
|
|
8700
|
+
INSERT INTO session_aliases(agent_name, session_id, alias, updated_at)
|
|
8701
|
+
VALUES (?, ?, ?, ?)
|
|
8702
|
+
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
8703
|
+
alias = excluded.alias,
|
|
8704
|
+
updated_at = excluded.updated_at
|
|
8705
|
+
`
|
|
8706
|
+
).run(saved.agentKey, saved.sessionId, saved.alias, saved.updated_at);
|
|
8707
|
+
return saved;
|
|
8708
|
+
});
|
|
8709
|
+
}
|
|
8710
|
+
function deleteSessionAlias(agentKey, sessionId) {
|
|
8711
|
+
if (useMemoryStateStore()) {
|
|
8712
|
+
memoryAliases.delete(getAliasKey(agentKey, sessionId));
|
|
8713
|
+
return;
|
|
8714
|
+
}
|
|
8715
|
+
withStateDb((db) => {
|
|
8716
|
+
db.prepare(
|
|
8717
|
+
`
|
|
8718
|
+
DELETE FROM session_aliases
|
|
8719
|
+
WHERE agent_name = ? AND session_id = ?
|
|
8720
|
+
`
|
|
8721
|
+
).run(agentKey, sessionId);
|
|
8722
|
+
});
|
|
8723
|
+
}
|
|
8924
8724
|
var DASHBOARD_RECENT_LIMIT = 10;
|
|
8925
8725
|
function getTotalTokens(stats) {
|
|
8926
8726
|
return stats.total_tokens ?? stats.total_input_tokens + stats.total_output_tokens;
|
|
@@ -8974,10 +8774,11 @@ function buildDashboard(sessions, options) {
|
|
|
8974
8774
|
for (const session of sessions) {
|
|
8975
8775
|
const agentName = getSessionAgentName(session);
|
|
8976
8776
|
if (scope.agent && agentName !== scope.agent) continue;
|
|
8977
|
-
if (scope.projectKey) {
|
|
8777
|
+
if (scope.projectKind || scope.projectKey) {
|
|
8978
8778
|
const identity = session.project_identity;
|
|
8979
|
-
if (!identity || identity.key !== scope.projectKey)
|
|
8980
|
-
|
|
8779
|
+
if (!identity || !scope.projectKind || !scope.projectKey || identity.kind !== scope.projectKind || identity.key !== scope.projectKey) {
|
|
8780
|
+
continue;
|
|
8781
|
+
}
|
|
8981
8782
|
}
|
|
8982
8783
|
const activity = getSessionActivityTime(session);
|
|
8983
8784
|
if (from != null && activity < from) continue;
|
|
@@ -9076,6 +8877,90 @@ function buildDashboard(sessions, options) {
|
|
|
9076
8877
|
recentSessions
|
|
9077
8878
|
};
|
|
9078
8879
|
}
|
|
8880
|
+
function executeSessionSearch(query, options, snapshot) {
|
|
8881
|
+
const merged = mergeSearchQueryOptions(query, options);
|
|
8882
|
+
if (!needsIndexedSearch(merged.text, merged.options)) {
|
|
8883
|
+
return searchRecentSessions(snapshot, merged.options);
|
|
8884
|
+
}
|
|
8885
|
+
return searchIndexedSessions(query, merged.text, merged.parsed, merged.options);
|
|
8886
|
+
}
|
|
8887
|
+
function needsIndexedSearch(textQuery, options) {
|
|
8888
|
+
return Boolean(textQuery || options.file || options.fileKind || options.tools?.length);
|
|
8889
|
+
}
|
|
8890
|
+
function filterSessionsByActivityWindow(sessions, from, to) {
|
|
8891
|
+
if (from == null && to == null) return sessions;
|
|
8892
|
+
return sessions.filter((session) => {
|
|
8893
|
+
const activity = getSessionActivityTime(session);
|
|
8894
|
+
if (from != null && activity < from) return false;
|
|
8895
|
+
if (to != null && activity > to) return false;
|
|
8896
|
+
return true;
|
|
8897
|
+
});
|
|
8898
|
+
}
|
|
8899
|
+
function matchesRecentSearchFilters(session, options, projectScope) {
|
|
8900
|
+
if (options.projectKind || options.projectKey) {
|
|
8901
|
+
if (!options.projectKind || !options.projectKey || !matchesProjectIdentity(session.project_identity, {
|
|
8902
|
+
kind: options.projectKind,
|
|
8903
|
+
key: options.projectKey
|
|
8904
|
+
})) {
|
|
8905
|
+
return false;
|
|
8906
|
+
}
|
|
8907
|
+
}
|
|
8908
|
+
if (projectScope && !matchesProjectScope(session, projectScope)) return false;
|
|
8909
|
+
if (options.project) {
|
|
8910
|
+
const projectNeedle = options.project.toLowerCase();
|
|
8911
|
+
const projectText = [
|
|
8912
|
+
session.project_identity?.key,
|
|
8913
|
+
session.project_identity?.displayName,
|
|
8914
|
+
session.directory
|
|
8915
|
+
].filter(Boolean).join("\n").toLowerCase();
|
|
8916
|
+
if (!projectText.includes(projectNeedle)) return false;
|
|
8917
|
+
}
|
|
8918
|
+
if (options.tags?.length && !options.tags.every((tag) => session.smart_tags?.includes(tag))) {
|
|
8919
|
+
return false;
|
|
8920
|
+
}
|
|
8921
|
+
if (!sessionMatchesSearchCost(session, options)) return false;
|
|
8922
|
+
return true;
|
|
8923
|
+
}
|
|
8924
|
+
function searchRecentSessions(snapshot, options) {
|
|
8925
|
+
const projectScope = options.cwd ? createProjectScopeMatcher(options.cwd) : null;
|
|
8926
|
+
const entries = options.agent ? [[options.agent, snapshot.byAgent[options.agent] ?? []]] : Object.entries(snapshot.byAgent);
|
|
8927
|
+
return entries.flatMap(
|
|
8928
|
+
([agentName, sessions]) => filterSessionsByActivityWindow(sessions, options.from, options.to).filter((session) => matchesRecentSearchFilters(session, options, projectScope)).map((session) => ({ agentName, session }))
|
|
8929
|
+
).sort(
|
|
8930
|
+
(a, b) => (b.session.time_updated ?? b.session.time_created) - (a.session.time_updated ?? a.session.time_created)
|
|
8931
|
+
).slice(0, options.limit ?? 50).map(({ agentName, session }) => ({
|
|
8932
|
+
agentName,
|
|
8933
|
+
session,
|
|
8934
|
+
snippet: `Recent session \xB7 ${session.directory}`,
|
|
8935
|
+
matchType: "recent"
|
|
8936
|
+
}));
|
|
8937
|
+
}
|
|
8938
|
+
function deriveFileQuery(query, parsed, options) {
|
|
8939
|
+
return options.file ?? (!parsed.text ? parsed.filters.file : void 0) ?? (!parsed.hasQualifiers && query ? parsed.text || query : "");
|
|
8940
|
+
}
|
|
8941
|
+
function mergeSearchResultSources(results, limit) {
|
|
8942
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8943
|
+
const merged = [];
|
|
8944
|
+
for (const result of results) {
|
|
8945
|
+
const key = `${result.agentName}/${result.session.id}`;
|
|
8946
|
+
if (seen.has(key)) continue;
|
|
8947
|
+
seen.add(key);
|
|
8948
|
+
merged.push(result);
|
|
8949
|
+
if (merged.length >= limit) break;
|
|
8950
|
+
}
|
|
8951
|
+
return merged;
|
|
8952
|
+
}
|
|
8953
|
+
function canSkipSessionsSearch(fileQuery, textQuery, options) {
|
|
8954
|
+
return Boolean(
|
|
8955
|
+
fileQuery && !textQuery && !options.tools?.length && !options.tags?.length && options.from == null && options.to == null
|
|
8956
|
+
);
|
|
8957
|
+
}
|
|
8958
|
+
function searchIndexedSessions(query, textQuery, parsed, options) {
|
|
8959
|
+
const fileQuery = deriveFileQuery(query, parsed, options);
|
|
8960
|
+
const fileResults = fileQuery ? searchFileActivitySessions(fileQuery, options) : [];
|
|
8961
|
+
const sessionResults = canSkipSessionsSearch(fileQuery, textQuery, options) ? [] : searchSessions(query, options);
|
|
8962
|
+
return mergeSearchResultSources([...fileResults, ...sessionResults], options.limit ?? 50);
|
|
8963
|
+
}
|
|
9079
8964
|
|
|
9080
8965
|
export {
|
|
9081
8966
|
registerAgent,
|
|
@@ -9101,16 +8986,12 @@ export {
|
|
|
9101
8986
|
normalizeTitleText,
|
|
9102
8987
|
basenameTitle,
|
|
9103
8988
|
resolveSessionTitle,
|
|
9104
|
-
parsed,
|
|
9105
|
-
skipped,
|
|
9106
|
-
filtered,
|
|
9107
8989
|
cleanInternalText,
|
|
9108
8990
|
cleanMessagePart,
|
|
9109
8991
|
cleanMessageParts,
|
|
9110
8992
|
cleanParsedMessage,
|
|
9111
8993
|
cleanParsedMessages,
|
|
9112
8994
|
firstUserMessageTitle,
|
|
9113
|
-
perf,
|
|
9114
8995
|
getPricingRegistry,
|
|
9115
8996
|
hasBillablePricing,
|
|
9116
8997
|
refreshPricingCache,
|
|
@@ -9123,11 +9004,15 @@ export {
|
|
|
9123
9004
|
openDbReadOnly,
|
|
9124
9005
|
openDb,
|
|
9125
9006
|
isSqliteAvailable,
|
|
9007
|
+
perf,
|
|
9126
9008
|
fallbackDisplayName,
|
|
9127
9009
|
realFs,
|
|
9128
|
-
|
|
9010
|
+
isProjectIdentityKind,
|
|
9011
|
+
getProjectIdentityKey,
|
|
9012
|
+
matchesProjectIdentity,
|
|
9129
9013
|
normalizeGitRemote,
|
|
9130
9014
|
computeIdentity,
|
|
9015
|
+
buildProjectGroups,
|
|
9131
9016
|
createProjectScopeMatcher,
|
|
9132
9017
|
matchesProjectScope,
|
|
9133
9018
|
filterSessionsByProjectScope,
|
|
@@ -9141,6 +9026,7 @@ export {
|
|
|
9141
9026
|
parseSearchQuery,
|
|
9142
9027
|
syncSessionSearchIndex,
|
|
9143
9028
|
syncSessionSearchIndexChanges,
|
|
9029
|
+
mergeSearchQueryOptions,
|
|
9144
9030
|
searchSessions,
|
|
9145
9031
|
listFileActivity,
|
|
9146
9032
|
listSessionFileActivity,
|
|
@@ -9165,17 +9051,23 @@ export {
|
|
|
9165
9051
|
ensureSessionTagsSync,
|
|
9166
9052
|
scanSessions,
|
|
9167
9053
|
scanSessionsAsync,
|
|
9168
|
-
|
|
9054
|
+
StateStorageUnavailableError,
|
|
9169
9055
|
listBookmarks,
|
|
9170
9056
|
upsertBookmark,
|
|
9171
9057
|
importBookmarks,
|
|
9172
9058
|
deleteBookmark,
|
|
9059
|
+
SESSION_ALIAS_MAX_LENGTH,
|
|
9060
|
+
normalizeSessionAlias,
|
|
9061
|
+
listSessionAliases,
|
|
9062
|
+
upsertSessionAlias,
|
|
9063
|
+
deleteSessionAlias,
|
|
9173
9064
|
DASHBOARD_RECENT_LIMIT,
|
|
9174
9065
|
getTotalTokens,
|
|
9175
9066
|
getSessionAgentName,
|
|
9176
9067
|
getSessionActivityTime,
|
|
9177
9068
|
toLocalDateKey,
|
|
9178
9069
|
startOfLocalDay,
|
|
9179
|
-
buildDashboard
|
|
9070
|
+
buildDashboard,
|
|
9071
|
+
executeSessionSearch
|
|
9180
9072
|
};
|
|
9181
|
-
//# sourceMappingURL=chunk-
|
|
9073
|
+
//# sourceMappingURL=chunk-VRVZJDNL.js.map
|