codesesh 0.14.0 → 0.16.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 +1 -0
- package/dist/chunk-EWC6IODK.js +166 -0
- package/dist/chunk-EWC6IODK.js.map +1 -0
- package/dist/{chunk-VRVZJDNL.js → chunk-MWSJTNOW.js} +1044 -386
- package/dist/chunk-MWSJTNOW.js.map +1 -0
- package/dist/{dist-C3KC3XKJ.js → dist-AIDCOQZO.js} +20 -2
- package/dist/index.js +131 -239
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +2 -1
- package/dist/scan-refresh-worker.js.map +1 -1
- package/dist/search-index-worker.js +11 -2
- package/dist/search-index-worker.js.map +1 -1
- package/dist/smart-tag-worker.js +2 -1
- package/dist/smart-tag-worker.js.map +1 -1
- package/dist/web/assets/index-BpmbwB2_.js +113 -0
- package/dist/web/assets/index-Bxe686Vr.css +2 -0
- package/dist/web/assets/{syntax-C_RLNYQn.js → syntax-mZXQuVcd.js} +1 -1
- package/dist/web/assets/vendor-BVE7Fps8.js +41 -0
- package/dist/web/icon/agent/cursor.svg +1 -12
- package/dist/web/icon/agent/pi.svg +2 -2
- package/dist/web/index.html +55 -4
- package/package.json +1 -1
- package/dist/chunk-VRVZJDNL.js.map +0 -1
- package/dist/web/assets/index-BNLxu3RT.js +0 -113
- package/dist/web/assets/index-BYElqfX-.css +0 -2
- package/dist/web/assets/vendor-Lx2mL9a6.js +0 -40
- /package/dist/{dist-C3KC3XKJ.js.map → dist-AIDCOQZO.js.map} +0 -0
|
@@ -78,6 +78,7 @@ function getAgentInfoMap(sessionsByAgent) {
|
|
|
78
78
|
name: agent.name,
|
|
79
79
|
displayName: agent.displayName,
|
|
80
80
|
icon: registration.icon,
|
|
81
|
+
iconColored: registration.iconColored,
|
|
81
82
|
count: sessionsByAgent[agent.name] ?? 0
|
|
82
83
|
};
|
|
83
84
|
});
|
|
@@ -85,6 +86,23 @@ function getAgentInfoMap(sessionsByAgent) {
|
|
|
85
86
|
function getAgentByName(name) {
|
|
86
87
|
return registrations.find((registration) => registration.create().name === name);
|
|
87
88
|
}
|
|
89
|
+
var diagnostics = null;
|
|
90
|
+
function toSafeSink(sink) {
|
|
91
|
+
return {
|
|
92
|
+
warn(event, detail) {
|
|
93
|
+
try {
|
|
94
|
+
sink.warn(event, detail);
|
|
95
|
+
} catch {
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function setCoreDiagnostics(next) {
|
|
101
|
+
diagnostics = next ? toSafeSink(next) : null;
|
|
102
|
+
}
|
|
103
|
+
function getCoreDiagnostics() {
|
|
104
|
+
return diagnostics;
|
|
105
|
+
}
|
|
88
106
|
function parsedSession(session) {
|
|
89
107
|
return { status: "parsed", data: session };
|
|
90
108
|
}
|
|
@@ -117,7 +135,12 @@ var FileSystemSessionSource = class extends BaseAgent {
|
|
|
117
135
|
try {
|
|
118
136
|
const session = this.scanSessionSource(source.sourcePath, options);
|
|
119
137
|
if (session) sessions.push(session);
|
|
120
|
-
} catch {
|
|
138
|
+
} catch (error) {
|
|
139
|
+
getCoreDiagnostics()?.warn("agent.session_parse_failed", {
|
|
140
|
+
agentName: this.name,
|
|
141
|
+
sourcePath: source.sourcePath,
|
|
142
|
+
message: error instanceof Error ? error.message : String(error)
|
|
143
|
+
});
|
|
121
144
|
continue;
|
|
122
145
|
} finally {
|
|
123
146
|
options?.onProgress?.({
|
|
@@ -156,18 +179,20 @@ var FileSystemSessionSource = class extends BaseAgent {
|
|
|
156
179
|
return {
|
|
157
180
|
hasChanges: changedIdList.length > 0,
|
|
158
181
|
changedIds: changedIdList,
|
|
159
|
-
timestamp: Date.now()
|
|
182
|
+
timestamp: Date.now(),
|
|
183
|
+
refs: currentRefs
|
|
160
184
|
};
|
|
161
185
|
}
|
|
162
186
|
/**
|
|
163
187
|
* 增量扫描:对变更/新增源调用 scanSessionSource 重解析,
|
|
164
188
|
* 删除已消失的源,合并回 cachedSessions。
|
|
189
|
+
* refs 未传时回退为自行枚举,供独立调用方(如测试)沿用旧行为。
|
|
165
190
|
*/
|
|
166
|
-
incrementalScan(cachedSessions, changedIds) {
|
|
191
|
+
incrementalScan(cachedSessions, changedIds, refs) {
|
|
167
192
|
const sessionMap = new Map(cachedSessions.map((session) => [session.id, session]));
|
|
168
193
|
const changedSet = new Set(changedIds);
|
|
169
194
|
const currentIds = /* @__PURE__ */ new Set();
|
|
170
|
-
for (const ref of this.listSessionSources()) {
|
|
195
|
+
for (const ref of refs ?? this.listSessionSources()) {
|
|
171
196
|
currentIds.add(ref.sessionId);
|
|
172
197
|
if (!changedSet.has(ref.sessionId)) continue;
|
|
173
198
|
const head = this.scanSessionSource(ref.sourcePath);
|
|
@@ -283,14 +308,21 @@ function getZCodeDataPath() {
|
|
|
283
308
|
}
|
|
284
309
|
var READ_CHUNK_BYTES = 1 << 20;
|
|
285
310
|
function* parseJsonlLines(content) {
|
|
311
|
+
let total = 0;
|
|
312
|
+
let skipped = 0;
|
|
286
313
|
for (const line of content.split("\n")) {
|
|
287
314
|
const trimmed = line.trim();
|
|
288
315
|
if (!trimmed) continue;
|
|
316
|
+
total += 1;
|
|
289
317
|
try {
|
|
290
318
|
yield JSON.parse(trimmed);
|
|
291
319
|
} catch {
|
|
320
|
+
skipped += 1;
|
|
292
321
|
}
|
|
293
322
|
}
|
|
323
|
+
if (skipped > 0) {
|
|
324
|
+
getCoreDiagnostics()?.warn("agent.jsonl_lines_skipped", { skipped, total });
|
|
325
|
+
}
|
|
294
326
|
}
|
|
295
327
|
function* readJsonlFileLines(filePath, chunkBytes = READ_CHUNK_BYTES) {
|
|
296
328
|
const fd = openSync(filePath, "r");
|
|
@@ -315,12 +347,19 @@ function* readJsonlFileLines(filePath, chunkBytes = READ_CHUNK_BYTES) {
|
|
|
315
347
|
}
|
|
316
348
|
}
|
|
317
349
|
function* readJsonlFile(filePath) {
|
|
350
|
+
let total = 0;
|
|
351
|
+
let skipped = 0;
|
|
318
352
|
for (const line of readJsonlFileLines(filePath)) {
|
|
353
|
+
total += 1;
|
|
319
354
|
try {
|
|
320
355
|
yield JSON.parse(line);
|
|
321
356
|
} catch {
|
|
357
|
+
skipped += 1;
|
|
322
358
|
}
|
|
323
359
|
}
|
|
360
|
+
if (skipped > 0) {
|
|
361
|
+
getCoreDiagnostics()?.warn("agent.jsonl_lines_skipped", { skipped, total, filePath });
|
|
362
|
+
}
|
|
324
363
|
}
|
|
325
364
|
var INTERNAL_TAGS = [
|
|
326
365
|
"command-message",
|
|
@@ -794,6 +833,43 @@ function withEstimatedSessionCost(stats, model) {
|
|
|
794
833
|
function estimateTokenCost(model, tokens) {
|
|
795
834
|
return estimateCostForTokens(model, tokens)?.cost ?? null;
|
|
796
835
|
}
|
|
836
|
+
function asRecord(value) {
|
|
837
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
838
|
+
return value;
|
|
839
|
+
}
|
|
840
|
+
return void 0;
|
|
841
|
+
}
|
|
842
|
+
function asString(value) {
|
|
843
|
+
return typeof value === "string" ? value : void 0;
|
|
844
|
+
}
|
|
845
|
+
function asNumber(value) {
|
|
846
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
847
|
+
}
|
|
848
|
+
function asArray(value) {
|
|
849
|
+
return Array.isArray(value) ? value : void 0;
|
|
850
|
+
}
|
|
851
|
+
function safeParseJsonRecord(json) {
|
|
852
|
+
let raw;
|
|
853
|
+
try {
|
|
854
|
+
raw = JSON.parse(json);
|
|
855
|
+
} catch {
|
|
856
|
+
return void 0;
|
|
857
|
+
}
|
|
858
|
+
return asRecord(raw);
|
|
859
|
+
}
|
|
860
|
+
var reportedFieldMismatches = /* @__PURE__ */ new Set();
|
|
861
|
+
function reportFieldMismatch(agentName, field) {
|
|
862
|
+
const key = `${agentName}\0${field}`;
|
|
863
|
+
if (reportedFieldMismatches.has(key)) return;
|
|
864
|
+
reportedFieldMismatches.add(key);
|
|
865
|
+
getCoreDiagnostics()?.warn("agent.field_shape_mismatch", { agentName, field });
|
|
866
|
+
}
|
|
867
|
+
function narrowField(agent, field, value, narrow) {
|
|
868
|
+
if (value === void 0 || value === null) return void 0;
|
|
869
|
+
const result = narrow(value);
|
|
870
|
+
if (result === void 0) reportFieldMismatch(agent, field);
|
|
871
|
+
return result;
|
|
872
|
+
}
|
|
797
873
|
var TranscriptBuilder = class {
|
|
798
874
|
constructor(options = {}) {
|
|
799
875
|
this.options = options;
|
|
@@ -969,31 +1045,48 @@ var TranscriptBuilder = class {
|
|
|
969
1045
|
};
|
|
970
1046
|
var HEAD_INDEX_VERSION = "claudecode-head-v2";
|
|
971
1047
|
function parseTimestampMs(data) {
|
|
972
|
-
const raw =
|
|
973
|
-
|
|
1048
|
+
const raw = data["timestamp"];
|
|
1049
|
+
const value = asString(raw);
|
|
1050
|
+
if (value === void 0) {
|
|
1051
|
+
if (raw !== void 0 && raw !== null) reportFieldMismatch("claudecode", "timestamp");
|
|
1052
|
+
return 0;
|
|
1053
|
+
}
|
|
1054
|
+
const trimmed = value.trim();
|
|
1055
|
+
if (!trimmed) return 0;
|
|
974
1056
|
try {
|
|
975
|
-
return new Date(
|
|
1057
|
+
return new Date(trimmed.includes("Z") ? trimmed : trimmed + "Z").getTime();
|
|
976
1058
|
} catch {
|
|
977
1059
|
return 0;
|
|
978
1060
|
}
|
|
979
1061
|
}
|
|
980
|
-
function
|
|
981
|
-
|
|
1062
|
+
function readUsageNumber(usage, field) {
|
|
1063
|
+
const raw = usage[field];
|
|
1064
|
+
if (raw === void 0) return 0;
|
|
1065
|
+
const value = asNumber(raw);
|
|
1066
|
+
if (value === void 0) {
|
|
1067
|
+
reportFieldMismatch("claudecode", `message.usage.${field}`);
|
|
1068
|
+
return 0;
|
|
1069
|
+
}
|
|
1070
|
+
return value;
|
|
982
1071
|
}
|
|
983
1072
|
function extractClaudeUsage(data, msg) {
|
|
984
|
-
const
|
|
985
|
-
if (
|
|
986
|
-
const
|
|
1073
|
+
const rawUsage = msg["usage"];
|
|
1074
|
+
if (rawUsage === void 0 || rawUsage === null) return null;
|
|
1075
|
+
const usage = asRecord(rawUsage);
|
|
1076
|
+
if (!usage) {
|
|
1077
|
+
reportFieldMismatch("claudecode", "message.usage");
|
|
1078
|
+
return null;
|
|
1079
|
+
}
|
|
987
1080
|
const requestId = typeof data["requestId"] === "string" ? data["requestId"].trim() : "";
|
|
988
1081
|
const uuid = typeof data["uuid"] === "string" ? data["uuid"].trim() : "";
|
|
989
1082
|
const key = requestId || uuid;
|
|
990
1083
|
if (!key) return null;
|
|
991
1084
|
return {
|
|
992
1085
|
key,
|
|
993
|
-
input:
|
|
994
|
-
output:
|
|
995
|
-
cacheRead:
|
|
996
|
-
cacheCreate:
|
|
1086
|
+
input: readUsageNumber(usage, "input_tokens"),
|
|
1087
|
+
output: readUsageNumber(usage, "output_tokens"),
|
|
1088
|
+
cacheRead: readUsageNumber(usage, "cache_read_input_tokens"),
|
|
1089
|
+
cacheCreate: readUsageNumber(usage, "cache_creation_input_tokens")
|
|
997
1090
|
};
|
|
998
1091
|
}
|
|
999
1092
|
var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
@@ -1025,17 +1118,20 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1025
1118
|
if (!this.basePath) return [];
|
|
1026
1119
|
const refs = [];
|
|
1027
1120
|
for (const projectDir of this.listProjectDirs()) {
|
|
1121
|
+
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1028
1122
|
for (const file of this.listJsonlFiles(projectDir)) {
|
|
1123
|
+
let stat;
|
|
1029
1124
|
try {
|
|
1030
|
-
|
|
1125
|
+
stat = statSync2(file);
|
|
1031
1126
|
} catch {
|
|
1032
1127
|
continue;
|
|
1033
1128
|
}
|
|
1129
|
+
if (!matchesScanWindow(stat.mtimeMs, options)) continue;
|
|
1034
1130
|
const sessionId = basename2(file, ".jsonl");
|
|
1035
1131
|
refs.push({
|
|
1036
1132
|
sessionId,
|
|
1037
1133
|
sourcePath: file,
|
|
1038
|
-
fingerprint: this.sourceFingerprint(
|
|
1134
|
+
fingerprint: this.sourceFingerprint(stat, indexPath)
|
|
1039
1135
|
});
|
|
1040
1136
|
}
|
|
1041
1137
|
}
|
|
@@ -1059,18 +1155,11 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1059
1155
|
}
|
|
1060
1156
|
const content = readFileSync2(meta.sourcePath, "utf-8");
|
|
1061
1157
|
const builder = new TranscriptBuilder();
|
|
1062
|
-
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
1063
1158
|
const assistantUuidToToolCalls = /* @__PURE__ */ new Map();
|
|
1064
1159
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
1065
1160
|
for (const record of parseJsonlLines(content)) {
|
|
1066
1161
|
try {
|
|
1067
|
-
this.convertRecord(
|
|
1068
|
-
record,
|
|
1069
|
-
builder,
|
|
1070
|
-
ignoredToolCallIds,
|
|
1071
|
-
assistantUuidToToolCalls,
|
|
1072
|
-
countedUsageKeys
|
|
1073
|
-
);
|
|
1162
|
+
this.convertRecord(record, builder, assistantUuidToToolCalls, countedUsageKeys);
|
|
1074
1163
|
} catch {
|
|
1075
1164
|
}
|
|
1076
1165
|
}
|
|
@@ -1105,12 +1194,13 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1105
1194
|
}
|
|
1106
1195
|
buildSessionMeta(head, file, projectDir) {
|
|
1107
1196
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1197
|
+
const stat = statSync2(file);
|
|
1108
1198
|
return {
|
|
1109
1199
|
id: head.id,
|
|
1110
1200
|
title: head.title,
|
|
1111
1201
|
sourcePath: file,
|
|
1112
|
-
sourceFingerprint: this.sourceFingerprint(
|
|
1113
|
-
sourceMtimeMs:
|
|
1202
|
+
sourceFingerprint: this.sourceFingerprint(stat, indexPath),
|
|
1203
|
+
sourceMtimeMs: stat.mtimeMs,
|
|
1114
1204
|
indexPath: existsSync4(indexPath) ? indexPath : null,
|
|
1115
1205
|
indexMtimeMs: this.getFileMtimeMs(indexPath),
|
|
1116
1206
|
headIndexVersion: HEAD_INDEX_VERSION,
|
|
@@ -1121,9 +1211,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1121
1211
|
updatedAt: head.time_updated ?? head.time_created
|
|
1122
1212
|
};
|
|
1123
1213
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1214
|
+
/** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
|
|
1215
|
+
sourceFingerprint(stat, indexPath) {
|
|
1127
1216
|
return JSON.stringify([
|
|
1128
1217
|
HEAD_INDEX_VERSION,
|
|
1129
1218
|
stat.mtimeMs,
|
|
@@ -1195,7 +1284,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1195
1284
|
let totalCost = 0;
|
|
1196
1285
|
const modelUsageMap = {};
|
|
1197
1286
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
1198
|
-
|
|
1287
|
+
let messageTitle = null;
|
|
1288
|
+
for (const [lineIndex, line] of lines.entries()) {
|
|
1199
1289
|
try {
|
|
1200
1290
|
const data = JSON.parse(line);
|
|
1201
1291
|
if (isInternalEventType(data["type"])) continue;
|
|
@@ -1204,15 +1294,25 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1204
1294
|
if (!cwd && data["cwd"] && typeof data["cwd"] === "string") {
|
|
1205
1295
|
cwd = data["cwd"];
|
|
1206
1296
|
}
|
|
1207
|
-
const msg = data["message"];
|
|
1208
|
-
if (msg &&
|
|
1209
|
-
|
|
1210
|
-
|
|
1297
|
+
const msg = asRecord(data["message"]);
|
|
1298
|
+
if (!msg && data["message"] !== void 0 && data["message"] !== null) {
|
|
1299
|
+
reportFieldMismatch("claudecode", "message");
|
|
1300
|
+
}
|
|
1301
|
+
if (msg) {
|
|
1302
|
+
const role = asString(msg["role"]);
|
|
1303
|
+
if (msg["role"] !== void 0 && role === void 0) {
|
|
1304
|
+
reportFieldMismatch("claudecode", "message.role");
|
|
1305
|
+
}
|
|
1306
|
+
if (role?.trim()) {
|
|
1211
1307
|
messageCount++;
|
|
1212
1308
|
}
|
|
1213
1309
|
if (!model) {
|
|
1214
|
-
const m = msg["model"];
|
|
1215
|
-
if (
|
|
1310
|
+
const m = asString(msg["model"]);
|
|
1311
|
+
if (m?.trim()) model = m.trim();
|
|
1312
|
+
}
|
|
1313
|
+
if (messageTitle === null && lineIndex < 20 && role === "user") {
|
|
1314
|
+
const candidate = this.extractUserMessageTitle(msg["content"]);
|
|
1315
|
+
if (candidate) messageTitle = candidate;
|
|
1216
1316
|
}
|
|
1217
1317
|
if (role === "assistant") {
|
|
1218
1318
|
const usage = extractClaudeUsage(data, msg);
|
|
@@ -1226,8 +1326,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1226
1326
|
totalOutputTokens += outputTokens;
|
|
1227
1327
|
totalCacheReadTokens += cacheRead;
|
|
1228
1328
|
totalCacheCreateTokens += cacheCreate;
|
|
1229
|
-
const m = msg["model"];
|
|
1230
|
-
if (
|
|
1329
|
+
const m = asString(msg["model"]);
|
|
1330
|
+
if (m?.trim()) {
|
|
1231
1331
|
const name = m.trim();
|
|
1232
1332
|
const msgTotal = inputTokens + cacheRead + cacheCreate + outputTokens;
|
|
1233
1333
|
modelUsageMap[name] = (modelUsageMap[name] ?? 0) + msgTotal;
|
|
@@ -1246,7 +1346,6 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1246
1346
|
}
|
|
1247
1347
|
}
|
|
1248
1348
|
const directory = cwd ?? projectDir;
|
|
1249
|
-
const messageTitle = this.extractTitle(lines);
|
|
1250
1349
|
const directoryTitle = basenameTitle(directory) || basenameTitle(projectDir);
|
|
1251
1350
|
const title = resolveSessionTitle(explicitTitle, messageTitle, directoryTitle);
|
|
1252
1351
|
const hasModelUsage = Object.keys(modelUsageMap).length > 0;
|
|
@@ -1270,113 +1369,93 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1270
1369
|
model_usage: hasModelUsage ? modelUsageMap : void 0
|
|
1271
1370
|
});
|
|
1272
1371
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
if (Array.isArray(content)) {
|
|
1288
|
-
const texts = content.filter((item) => typeof item === "object" && item !== null && "text" in item).map((item) => String(item["text"] ?? "")).join(" ");
|
|
1289
|
-
const title = normalizeTitleText(texts);
|
|
1290
|
-
if (title) return title;
|
|
1291
|
-
}
|
|
1292
|
-
} catch {
|
|
1293
|
-
}
|
|
1372
|
+
/** Mirrors the title-candidate extraction previously done in a second JSON.parse pass. */
|
|
1373
|
+
extractUserMessageTitle(content) {
|
|
1374
|
+
if (!content) return null;
|
|
1375
|
+
if (typeof content === "string") {
|
|
1376
|
+
const title = normalizeTitleText(content);
|
|
1377
|
+
return title || null;
|
|
1378
|
+
}
|
|
1379
|
+
if (Array.isArray(content)) {
|
|
1380
|
+
const texts = content.filter((item) => {
|
|
1381
|
+
const record = asRecord(item);
|
|
1382
|
+
return record !== void 0 && "text" in record;
|
|
1383
|
+
}).map((item) => String(item["text"] ?? "")).join(" ");
|
|
1384
|
+
const title = normalizeTitleText(texts);
|
|
1385
|
+
return title || null;
|
|
1294
1386
|
}
|
|
1295
1387
|
return null;
|
|
1296
1388
|
}
|
|
1297
1389
|
// --- Record conversion ---
|
|
1298
|
-
convertRecord(data, builder,
|
|
1390
|
+
convertRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys) {
|
|
1299
1391
|
if (data["isMeta"] === true) return;
|
|
1300
1392
|
const msgType = String(data["type"] ?? "");
|
|
1301
1393
|
if (isInternalEventType(msgType)) return;
|
|
1302
1394
|
if (msgType === "assistant") {
|
|
1303
|
-
this.convertAssistantRecord(
|
|
1304
|
-
data,
|
|
1305
|
-
builder,
|
|
1306
|
-
ignoredToolCallIds,
|
|
1307
|
-
assistantUuidToToolCalls,
|
|
1308
|
-
countedUsageKeys
|
|
1309
|
-
);
|
|
1395
|
+
this.convertAssistantRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys);
|
|
1310
1396
|
} else if (msgType === "user") {
|
|
1311
|
-
this.convertUserRecord(data, builder,
|
|
1397
|
+
this.convertUserRecord(data, builder, assistantUuidToToolCalls);
|
|
1312
1398
|
} else if (msgType === "tool_result") {
|
|
1313
1399
|
this.convertToolResultRecord(data, builder);
|
|
1314
1400
|
}
|
|
1315
1401
|
}
|
|
1316
|
-
convertAssistantRecord(data, builder,
|
|
1317
|
-
const msg = data["message"] ?? {};
|
|
1402
|
+
convertAssistantRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys) {
|
|
1403
|
+
const msg = asRecord(data["message"]) ?? {};
|
|
1318
1404
|
const timestampMs = parseTimestampMs(data);
|
|
1319
|
-
const rawContent = msg["content"] ?? [];
|
|
1405
|
+
const rawContent = asArray(msg["content"]) ?? [];
|
|
1320
1406
|
const uuid = String(data["uuid"] ?? "");
|
|
1321
1407
|
const toolCallIds = [];
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
this.applyAssistantMetadata(message2, data, msg, countedUsageKeys);
|
|
1336
|
-
}
|
|
1337
|
-
continue;
|
|
1338
|
-
}
|
|
1339
|
-
if (partType === "text") {
|
|
1340
|
-
const text = cleanInternalText(String(part["text"] ?? ""));
|
|
1341
|
-
if (text) {
|
|
1342
|
-
const message2 = builder.appendAssistantPart(
|
|
1343
|
-
this.buildTextPart(text, timestampMs),
|
|
1344
|
-
{
|
|
1345
|
-
id: uuid,
|
|
1346
|
-
timestampMs,
|
|
1347
|
-
agent: "claude"
|
|
1348
|
-
},
|
|
1349
|
-
{ deduplicateTail: true }
|
|
1350
|
-
);
|
|
1351
|
-
this.applyAssistantMetadata(message2, data, msg, countedUsageKeys);
|
|
1352
|
-
}
|
|
1353
|
-
continue;
|
|
1354
|
-
}
|
|
1355
|
-
if (partType !== "tool_use") continue;
|
|
1356
|
-
const toolName = String(part["name"] ?? "").trim();
|
|
1357
|
-
const toolCallId = String(part["id"] ?? "").trim();
|
|
1358
|
-
if (toolName && toolCallId && this.shouldIgnoreTool(toolName)) {
|
|
1359
|
-
ignoredToolCallIds.add(toolCallId);
|
|
1360
|
-
continue;
|
|
1408
|
+
for (const item of rawContent) {
|
|
1409
|
+
const part = asRecord(item);
|
|
1410
|
+
if (!part) continue;
|
|
1411
|
+
const partType = String(part["type"] ?? "");
|
|
1412
|
+
if (partType === "thinking") {
|
|
1413
|
+
const text = cleanInternalText(String(part["thinking"] ?? ""));
|
|
1414
|
+
if (text) {
|
|
1415
|
+
const message2 = builder.appendAssistantPart(
|
|
1416
|
+
this.buildReasoningPart(text, timestampMs),
|
|
1417
|
+
{ id: uuid, timestampMs, agent: "claude" },
|
|
1418
|
+
{ deduplicateTail: true }
|
|
1419
|
+
);
|
|
1420
|
+
this.applyAssistantMetadata(message2, data, msg, countedUsageKeys);
|
|
1361
1421
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
if (partType === "text") {
|
|
1425
|
+
const text = cleanInternalText(String(part["text"] ?? ""));
|
|
1426
|
+
if (text) {
|
|
1427
|
+
const message2 = builder.appendAssistantPart(
|
|
1428
|
+
this.buildTextPart(text, timestampMs),
|
|
1429
|
+
{
|
|
1430
|
+
id: uuid,
|
|
1431
|
+
timestampMs,
|
|
1432
|
+
agent: "claude"
|
|
1433
|
+
},
|
|
1434
|
+
{ deduplicateTail: true }
|
|
1435
|
+
);
|
|
1436
|
+
this.applyAssistantMetadata(message2, data, msg, countedUsageKeys);
|
|
1371
1437
|
}
|
|
1438
|
+
continue;
|
|
1439
|
+
}
|
|
1440
|
+
if (partType !== "tool_use") continue;
|
|
1441
|
+
const toolCallId = String(part["id"] ?? "").trim();
|
|
1442
|
+
const toolPart = this.buildToolPart(part, timestampMs);
|
|
1443
|
+
const message = builder.appendToolCall(
|
|
1444
|
+
toolPart,
|
|
1445
|
+
{ id: uuid, timestampMs, agent: "claude" },
|
|
1446
|
+
{ modeOnCreate: "tool" }
|
|
1447
|
+
);
|
|
1448
|
+
this.applyAssistantMetadata(message, data, msg, countedUsageKeys);
|
|
1449
|
+
if (toolCallId) {
|
|
1450
|
+
toolCallIds.push(toolCallId);
|
|
1372
1451
|
}
|
|
1373
1452
|
}
|
|
1374
1453
|
if (toolCallIds.length > 0) {
|
|
1375
1454
|
assistantUuidToToolCalls.set(uuid, toolCallIds);
|
|
1376
1455
|
}
|
|
1377
1456
|
}
|
|
1378
|
-
convertUserRecord(data, builder,
|
|
1379
|
-
const msg = data["message"] ?? {};
|
|
1457
|
+
convertUserRecord(data, builder, assistantUuidToToolCalls) {
|
|
1458
|
+
const msg = asRecord(data["message"]) ?? {};
|
|
1380
1459
|
const timestampMs = parseTimestampMs(data);
|
|
1381
1460
|
const content = msg["content"] ?? "";
|
|
1382
1461
|
const uuid = String(data["uuid"] ?? "");
|
|
@@ -1396,11 +1475,9 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1396
1475
|
const visibleParts = this.normalizeUserTextParts(content, timestampMs);
|
|
1397
1476
|
const toolStateUpdates = this.extractToolStateUpdates(data["toolUseResult"]);
|
|
1398
1477
|
for (const item of content) {
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
if (ci["type"] !== "tool_result") continue;
|
|
1478
|
+
const ci = asRecord(item);
|
|
1479
|
+
if (!ci || ci["type"] !== "tool_result") continue;
|
|
1402
1480
|
const toolCallId = this.resolveToolCallId(data, ci, assistantUuidToToolCalls);
|
|
1403
|
-
if (toolCallId && ignoredToolCallIds.has(toolCallId)) continue;
|
|
1404
1481
|
const outputParts = this.normalizeClaudeToolOutput(ci["content"], timestampMs);
|
|
1405
1482
|
if (this.backfillToolOutput(builder, toolCallId, outputParts, toolStateUpdates)) {
|
|
1406
1483
|
continue;
|
|
@@ -1420,7 +1497,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1420
1497
|
}
|
|
1421
1498
|
convertToolResultRecord(data, builder) {
|
|
1422
1499
|
const timestampMs = parseTimestampMs(data);
|
|
1423
|
-
const msg = data["message"] ?? {};
|
|
1500
|
+
const msg = asRecord(data["message"]) ?? {};
|
|
1424
1501
|
const outputParts = this.normalizeClaudeToolOutput(msg["content"], timestampMs);
|
|
1425
1502
|
const uuid = String(data["uuid"] ?? "");
|
|
1426
1503
|
const fallback = this.buildFallbackToolMessage({
|
|
@@ -1482,8 +1559,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1482
1559
|
if (!Array.isArray(content)) return [];
|
|
1483
1560
|
const parts = [];
|
|
1484
1561
|
for (const item of content) {
|
|
1485
|
-
|
|
1486
|
-
|
|
1562
|
+
const ci = asRecord(item);
|
|
1563
|
+
if (ci) {
|
|
1487
1564
|
if (ci["type"] === "tool_result") continue;
|
|
1488
1565
|
const text = cleanInternalText(String(ci["text"] ?? ""));
|
|
1489
1566
|
if (text) parts.push(this.buildTextPart(text, timestampMs));
|
|
@@ -1503,10 +1580,19 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1503
1580
|
if (Array.isArray(content)) {
|
|
1504
1581
|
const parts = [];
|
|
1505
1582
|
for (const item of content) {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
)
|
|
1583
|
+
const itemRecord = asRecord(item);
|
|
1584
|
+
if (itemRecord) {
|
|
1585
|
+
const rawSource = itemRecord["source"];
|
|
1586
|
+
if (itemRecord["type"] === "image" && rawSource) {
|
|
1587
|
+
const source = asRecord(rawSource);
|
|
1588
|
+
const data = asString(source?.["data"]) ?? "";
|
|
1589
|
+
const mimeType = asString(source?.["media_type"]) ?? "";
|
|
1590
|
+
if (data && mimeType.startsWith("image/")) {
|
|
1591
|
+
parts.push({ type: "image", data, mime_type: mimeType, time_created: timestampMs });
|
|
1592
|
+
}
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
const text2 = String(itemRecord["text"] ?? itemRecord["content"] ?? "");
|
|
1510
1596
|
const cleaned = cleanInternalText(text2);
|
|
1511
1597
|
if (cleaned) parts.push(this.buildTextPart(cleaned, timestampMs));
|
|
1512
1598
|
} else if (typeof item === "string") {
|
|
@@ -1544,8 +1630,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1544
1630
|
return "";
|
|
1545
1631
|
}
|
|
1546
1632
|
extractToolStateUpdates(toolUseResult) {
|
|
1547
|
-
|
|
1548
|
-
|
|
1633
|
+
const result = asRecord(toolUseResult);
|
|
1634
|
+
if (!result) return {};
|
|
1549
1635
|
const updates = {};
|
|
1550
1636
|
const success = result["success"];
|
|
1551
1637
|
if (typeof success === "boolean") {
|
|
@@ -1567,17 +1653,21 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1567
1653
|
parts: opts.outputParts
|
|
1568
1654
|
};
|
|
1569
1655
|
}
|
|
1570
|
-
// --- Utilities ---
|
|
1571
|
-
shouldIgnoreTool(toolName) {
|
|
1572
|
-
return toolName === "TodoWrite";
|
|
1573
|
-
}
|
|
1574
1656
|
};
|
|
1575
1657
|
var DatabaseConstructor = null;
|
|
1658
|
+
var loadErrorMessage = null;
|
|
1576
1659
|
try {
|
|
1577
1660
|
const require2 = createRequire(import.meta.url);
|
|
1578
1661
|
const mod = require2("better-sqlite3");
|
|
1579
1662
|
DatabaseConstructor = typeof mod === "function" ? mod : mod.default;
|
|
1580
|
-
} catch {
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
loadErrorMessage = error instanceof Error ? error.message : String(error);
|
|
1665
|
+
}
|
|
1666
|
+
var unavailableReported = false;
|
|
1667
|
+
function reportUnavailableOnce() {
|
|
1668
|
+
if (unavailableReported) return;
|
|
1669
|
+
unavailableReported = true;
|
|
1670
|
+
getCoreDiagnostics()?.warn("sqlite.unavailable", { message: loadErrorMessage });
|
|
1581
1671
|
}
|
|
1582
1672
|
function quoteIdentifier(value) {
|
|
1583
1673
|
return `"${value.replaceAll('"', '""')}"`;
|
|
@@ -1672,16 +1762,27 @@ function runSchemaMigrations(db, options) {
|
|
|
1672
1762
|
return backups;
|
|
1673
1763
|
}
|
|
1674
1764
|
function openDbReadOnly(dbPath) {
|
|
1675
|
-
if (!DatabaseConstructor)
|
|
1765
|
+
if (!DatabaseConstructor) {
|
|
1766
|
+
reportUnavailableOnce();
|
|
1767
|
+
return null;
|
|
1768
|
+
}
|
|
1676
1769
|
try {
|
|
1677
1770
|
const db = DatabaseConstructor(dbPath, { readonly: true });
|
|
1678
1771
|
return db;
|
|
1679
|
-
} catch {
|
|
1772
|
+
} catch (error) {
|
|
1773
|
+
getCoreDiagnostics()?.warn("sqlite.open_failed", {
|
|
1774
|
+
dbPath,
|
|
1775
|
+
readonly: true,
|
|
1776
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1777
|
+
});
|
|
1680
1778
|
return null;
|
|
1681
1779
|
}
|
|
1682
1780
|
}
|
|
1683
1781
|
function openDb(dbPath) {
|
|
1684
|
-
if (!DatabaseConstructor)
|
|
1782
|
+
if (!DatabaseConstructor) {
|
|
1783
|
+
reportUnavailableOnce();
|
|
1784
|
+
return null;
|
|
1785
|
+
}
|
|
1685
1786
|
try {
|
|
1686
1787
|
mkdirSync2(dirname2(dbPath), { recursive: true });
|
|
1687
1788
|
const db = DatabaseConstructor(dbPath);
|
|
@@ -1689,16 +1790,42 @@ function openDb(dbPath) {
|
|
|
1689
1790
|
db.pragma("journal_mode = WAL");
|
|
1690
1791
|
db.pragma("synchronous = NORMAL");
|
|
1691
1792
|
db.pragma("foreign_keys = ON");
|
|
1793
|
+
db.pragma("busy_timeout = 5000");
|
|
1692
1794
|
} catch {
|
|
1693
1795
|
}
|
|
1694
1796
|
return db;
|
|
1695
|
-
} catch {
|
|
1797
|
+
} catch (error) {
|
|
1798
|
+
getCoreDiagnostics()?.warn("sqlite.open_failed", {
|
|
1799
|
+
dbPath,
|
|
1800
|
+
readonly: false,
|
|
1801
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1802
|
+
});
|
|
1696
1803
|
return null;
|
|
1697
1804
|
}
|
|
1698
1805
|
}
|
|
1699
1806
|
function isSqliteAvailable() {
|
|
1700
1807
|
return DatabaseConstructor !== null;
|
|
1701
1808
|
}
|
|
1809
|
+
var MESSAGE_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool"]);
|
|
1810
|
+
function parseJsonRecord(raw, agentName, field) {
|
|
1811
|
+
const parsed = asRecord(JSON.parse(String(raw ?? "{}")));
|
|
1812
|
+
if (parsed) return parsed;
|
|
1813
|
+
reportFieldMismatch(agentName, field);
|
|
1814
|
+
return {};
|
|
1815
|
+
}
|
|
1816
|
+
function narrowMessageRole(value) {
|
|
1817
|
+
const role = asString(value);
|
|
1818
|
+
return role !== void 0 && MESSAGE_ROLES.has(role) ? role : void 0;
|
|
1819
|
+
}
|
|
1820
|
+
function parseMessageRole(value, agentName) {
|
|
1821
|
+
return narrowField(agentName, "message.role", value, narrowMessageRole) ?? "assistant";
|
|
1822
|
+
}
|
|
1823
|
+
function parseTokens(value, agentName) {
|
|
1824
|
+
return narrowField(agentName, "message.tokens", value, asRecord);
|
|
1825
|
+
}
|
|
1826
|
+
function parseModel(value, agentName) {
|
|
1827
|
+
return narrowField(agentName, "message.modelID", value, asString) ?? null;
|
|
1828
|
+
}
|
|
1702
1829
|
var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
1703
1830
|
constructor(config) {
|
|
1704
1831
|
super();
|
|
@@ -1830,7 +1957,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
1830
1957
|
).all(cutoffTime);
|
|
1831
1958
|
}
|
|
1832
1959
|
parsePartRow(partRow) {
|
|
1833
|
-
const partData =
|
|
1960
|
+
const partData = parseJsonRecord(partRow.data, this.name, "part.data");
|
|
1834
1961
|
const partType = String(partData.type ?? "");
|
|
1835
1962
|
if (isInternalEventType(partType)) return null;
|
|
1836
1963
|
if (partType === "text" || partType === "reasoning") {
|
|
@@ -1848,7 +1975,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
1848
1975
|
tool: String(partData.tool ?? ""),
|
|
1849
1976
|
callID: String(partData.callID ?? ""),
|
|
1850
1977
|
title: cleanInternalText(String(partData.title ?? "")),
|
|
1851
|
-
state: partData.state ?? {},
|
|
1978
|
+
state: asRecord(partData.state) ?? {},
|
|
1852
1979
|
time_created: Number(partRow.time_created ?? 0)
|
|
1853
1980
|
};
|
|
1854
1981
|
}
|
|
@@ -1880,7 +2007,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
1880
2007
|
for (const row of messageRows) {
|
|
1881
2008
|
const sessionId = String(row.session_id ?? "");
|
|
1882
2009
|
if (!sessionId) continue;
|
|
1883
|
-
const msgData =
|
|
2010
|
+
const msgData = parseJsonRecord(row.data, this.name, "message.data");
|
|
1884
2011
|
if (isInternalEventType(msgData.type)) continue;
|
|
1885
2012
|
const parts = partsByMessage.get(String(row.id ?? "")) ?? [];
|
|
1886
2013
|
if (parts.length === 0) continue;
|
|
@@ -1898,10 +2025,10 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
1898
2025
|
contexts.set(sessionId, context);
|
|
1899
2026
|
}
|
|
1900
2027
|
const cost = Number(msgData.cost ?? 0);
|
|
1901
|
-
const tokens = msgData.tokens;
|
|
2028
|
+
const tokens = parseTokens(msgData.tokens, this.name);
|
|
1902
2029
|
const inputTokens = Number(tokens?.input ?? 0);
|
|
1903
2030
|
const outputTokens = Number(tokens?.output ?? 0);
|
|
1904
|
-
const model = msgData.modelID
|
|
2031
|
+
const model = parseModel(msgData.modelID, this.name);
|
|
1905
2032
|
const estimatedCost = cost > 0 ? null : estimateTokenCost(model, { input: inputTokens, output: outputTokens });
|
|
1906
2033
|
if (estimatedCost !== null) context.stats.cost_source = "estimated";
|
|
1907
2034
|
context.stats.total_cost += cost || estimatedCost || 0;
|
|
@@ -1955,24 +2082,24 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
1955
2082
|
let hasEstimatedCost = false;
|
|
1956
2083
|
const msgRows = db.prepare("SELECT * FROM message WHERE session_id = ? ORDER BY time_created ASC").all(sessionId);
|
|
1957
2084
|
for (const msgRow of msgRows) {
|
|
1958
|
-
const msgData =
|
|
2085
|
+
const msgData = parseJsonRecord(msgRow.data, this.name, "message.data");
|
|
1959
2086
|
if (isInternalEventType(msgData.type)) continue;
|
|
1960
2087
|
const cost = Number(msgData.cost ?? 0);
|
|
1961
|
-
const tokens = msgData.tokens;
|
|
2088
|
+
const tokens = parseTokens(msgData.tokens, this.name);
|
|
1962
2089
|
const inputTokens = Number(tokens?.input ?? 0);
|
|
1963
2090
|
const outputTokens = Number(tokens?.output ?? 0);
|
|
1964
|
-
const model = msgData.modelID
|
|
2091
|
+
const model = parseModel(msgData.modelID, this.name);
|
|
1965
2092
|
const estimatedCost = cost > 0 ? null : estimateTokenCost(model, { input: inputTokens, output: outputTokens });
|
|
1966
2093
|
const resolvedCost = cost || estimatedCost || 0;
|
|
1967
2094
|
const parts = this.readMessageParts(db, msgRow.id);
|
|
1968
2095
|
if (parts.length === 0) continue;
|
|
1969
2096
|
messages.push({
|
|
1970
2097
|
id: String(msgRow.id ?? ""),
|
|
1971
|
-
role:
|
|
1972
|
-
agent: msgData.agent ?? null,
|
|
1973
|
-
mode: msgData.mode ?? null,
|
|
2098
|
+
role: parseMessageRole(msgData.role, this.name),
|
|
2099
|
+
agent: asString(msgData.agent) ?? null,
|
|
2100
|
+
mode: asString(msgData.mode) ?? null,
|
|
1974
2101
|
model,
|
|
1975
|
-
provider: msgData.providerID ?? null,
|
|
2102
|
+
provider: asString(msgData.providerID) ?? null,
|
|
1976
2103
|
time_created: Number(msgRow.time_created ?? 0),
|
|
1977
2104
|
tokens: tokens ? { input: inputTokens, output: outputTokens } : void 0,
|
|
1978
2105
|
cost: resolvedCost,
|
|
@@ -1997,7 +2124,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
1997
2124
|
title,
|
|
1998
2125
|
slug,
|
|
1999
2126
|
directory,
|
|
2000
|
-
version: sessionRow.version ?? void 0,
|
|
2127
|
+
version: asString(sessionRow.version) ?? void 0,
|
|
2001
2128
|
time_created: timeCreated,
|
|
2002
2129
|
time_updated: timeUpdated,
|
|
2003
2130
|
summary_files: sessionRow.summary_files ?? void 0,
|
|
@@ -2041,6 +2168,15 @@ var KIMI_IGNORED_TOOLS = /* @__PURE__ */ new Set(["SetTodoList"]);
|
|
|
2041
2168
|
function mapToolTitle(toolName) {
|
|
2042
2169
|
return KIMI_TOOL_TITLE_MAP[toolName] ?? toolName;
|
|
2043
2170
|
}
|
|
2171
|
+
function readWireMtime(record) {
|
|
2172
|
+
return narrowField("kimi", "session.wire_mtime", record.wire_mtime, asNumber) ?? null;
|
|
2173
|
+
}
|
|
2174
|
+
function readWireTimestamp(record) {
|
|
2175
|
+
return narrowField("kimi", "wire.timestamp", record.timestamp, asNumber) ?? 0;
|
|
2176
|
+
}
|
|
2177
|
+
function extractTokenField(usage, field) {
|
|
2178
|
+
return narrowField("kimi", `usage.${field}`, usage[field], asNumber) ?? 0;
|
|
2179
|
+
}
|
|
2044
2180
|
function normalizeToolArguments(raw) {
|
|
2045
2181
|
if (typeof raw === "string") {
|
|
2046
2182
|
try {
|
|
@@ -2059,8 +2195,9 @@ function normalizeToolOutputParts(content, timestampMs) {
|
|
|
2059
2195
|
if (Array.isArray(content)) {
|
|
2060
2196
|
const parts = [];
|
|
2061
2197
|
for (const item of content) {
|
|
2062
|
-
|
|
2063
|
-
|
|
2198
|
+
const record = asRecord(item);
|
|
2199
|
+
if (record && "text" in record) {
|
|
2200
|
+
const text2 = String(record.text ?? "");
|
|
2064
2201
|
const cleaned = cleanInternalText(text2);
|
|
2065
2202
|
if (cleaned) parts.push({ type: "text", text: cleaned, time_created: timestampMs });
|
|
2066
2203
|
} else if (typeof item === "string") {
|
|
@@ -2092,10 +2229,8 @@ function kimiContentText(content) {
|
|
|
2092
2229
|
if (!Array.isArray(content)) return "";
|
|
2093
2230
|
return content.map((item) => {
|
|
2094
2231
|
if (typeof item === "string") return item;
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
return String(record.text ?? record.content ?? "");
|
|
2098
|
-
}
|
|
2232
|
+
const record = asRecord(item);
|
|
2233
|
+
if (record) return String(record.text ?? record.content ?? "");
|
|
2099
2234
|
return "";
|
|
2100
2235
|
}).join(" ");
|
|
2101
2236
|
}
|
|
@@ -2111,9 +2246,9 @@ function extractFirstUserTitle(contextFile, wireFile) {
|
|
|
2111
2246
|
if (wireFile && existsSync6(wireFile)) {
|
|
2112
2247
|
const content = readFileSync3(wireFile, "utf-8");
|
|
2113
2248
|
for (const record of parseJsonlLines(content)) {
|
|
2114
|
-
const message = record.message ?? {};
|
|
2249
|
+
const message = asRecord(record.message) ?? {};
|
|
2115
2250
|
if (message.type !== "TurnBegin") continue;
|
|
2116
|
-
const payload = message.payload ?? {};
|
|
2251
|
+
const payload = asRecord(message.payload) ?? {};
|
|
2117
2252
|
const userInput = payload.user_input;
|
|
2118
2253
|
if (!Array.isArray(userInput)) continue;
|
|
2119
2254
|
const title = normalizeTitleText(kimiContentText(userInput));
|
|
@@ -2143,12 +2278,12 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2143
2278
|
}
|
|
2144
2279
|
if (!existsSync6(configPath)) return;
|
|
2145
2280
|
try {
|
|
2146
|
-
const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
2147
|
-
const workDirs = raw?.work_dirs;
|
|
2148
|
-
if (!
|
|
2281
|
+
const raw = asRecord(JSON.parse(readFileSync3(configPath, "utf-8")));
|
|
2282
|
+
const workDirs = asArray(raw?.work_dirs);
|
|
2283
|
+
if (!workDirs) return;
|
|
2149
2284
|
for (const wd of workDirs) {
|
|
2150
|
-
const path2 = wd
|
|
2151
|
-
if (
|
|
2285
|
+
const path2 = asString(asRecord(wd)?.path);
|
|
2286
|
+
if (!path2) continue;
|
|
2152
2287
|
const hash = createHash("md5").update(path2).digest("hex");
|
|
2153
2288
|
this.projectMap.set(hash, path2);
|
|
2154
2289
|
}
|
|
@@ -2207,14 +2342,14 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2207
2342
|
let wireMtime = null;
|
|
2208
2343
|
let metaFile = "";
|
|
2209
2344
|
if (existsSync6(statePath)) {
|
|
2210
|
-
const state = JSON.parse(readFileSync3(statePath, "utf-8"));
|
|
2345
|
+
const state = asRecord(JSON.parse(readFileSync3(statePath, "utf-8"))) ?? {};
|
|
2211
2346
|
title = String(state.custom_title ?? "");
|
|
2212
|
-
wireMtime =
|
|
2347
|
+
wireMtime = readWireMtime(state);
|
|
2213
2348
|
metaFile = statePath;
|
|
2214
2349
|
} else if (existsSync6(metaPath)) {
|
|
2215
|
-
const meta = JSON.parse(readFileSync3(metaPath, "utf-8"));
|
|
2350
|
+
const meta = asRecord(JSON.parse(readFileSync3(metaPath, "utf-8"))) ?? {};
|
|
2216
2351
|
title = String(meta.title ?? "");
|
|
2217
|
-
wireMtime =
|
|
2352
|
+
wireMtime = readWireMtime(meta);
|
|
2218
2353
|
metaFile = metaPath;
|
|
2219
2354
|
}
|
|
2220
2355
|
const cwd = this.projectMap.get(projectHash) || "";
|
|
@@ -2343,16 +2478,15 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2343
2478
|
for (const record of parseJsonlLines(content)) {
|
|
2344
2479
|
seq++;
|
|
2345
2480
|
try {
|
|
2346
|
-
const message = record.message ?? {};
|
|
2347
|
-
const msgType =
|
|
2481
|
+
const message = asRecord(record.message) ?? {};
|
|
2482
|
+
const msgType = asString(message.type) ?? "";
|
|
2348
2483
|
if (isInternalEventType(msgType)) continue;
|
|
2349
|
-
const payload = message.payload ?? {};
|
|
2350
|
-
const
|
|
2351
|
-
const
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
const
|
|
2355
|
-
const outputTokens = Number(usage["output_tokens"] ?? 0);
|
|
2484
|
+
const payload = asRecord(message.payload) ?? {};
|
|
2485
|
+
const timestampMs = Math.floor(readWireTimestamp(record) * 1e3);
|
|
2486
|
+
const usage = asRecord(message["usage"]);
|
|
2487
|
+
if (usage) {
|
|
2488
|
+
const inputTokens = extractTokenField(usage, "input_tokens");
|
|
2489
|
+
const outputTokens = extractTokenField(usage, "output_tokens");
|
|
2356
2490
|
if (inputTokens || outputTokens) {
|
|
2357
2491
|
const tokens = { input: inputTokens, output: outputTokens };
|
|
2358
2492
|
const cost = estimateTokenCost(this.defaultModel, tokens);
|
|
@@ -2404,7 +2538,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2404
2538
|
continue;
|
|
2405
2539
|
}
|
|
2406
2540
|
if (msgType === "ToolCall") {
|
|
2407
|
-
const function_ = payload.function;
|
|
2541
|
+
const function_ = asRecord(payload.function);
|
|
2408
2542
|
const toolName = String(function_?.name ?? "").trim();
|
|
2409
2543
|
const callId = String(payload.id ?? "").trim();
|
|
2410
2544
|
if (toolName && callId && KIMI_IGNORED_TOOLS.has(toolName)) {
|
|
@@ -2490,8 +2624,8 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2490
2624
|
const content = record.content;
|
|
2491
2625
|
if (Array.isArray(content)) {
|
|
2492
2626
|
for (const item of content) {
|
|
2493
|
-
|
|
2494
|
-
|
|
2627
|
+
const ci = asRecord(item);
|
|
2628
|
+
if (!ci) continue;
|
|
2495
2629
|
const partType = String(ci.type ?? "");
|
|
2496
2630
|
if (partType === "think") {
|
|
2497
2631
|
const text = cleanInternalText(String(ci.think ?? ""));
|
|
@@ -2502,15 +2636,14 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2502
2636
|
}
|
|
2503
2637
|
}
|
|
2504
2638
|
}
|
|
2505
|
-
const toolCalls = record.tool_calls;
|
|
2506
|
-
if (
|
|
2639
|
+
const toolCalls = asArray(record.tool_calls);
|
|
2640
|
+
if (toolCalls) {
|
|
2507
2641
|
for (const tc of toolCalls) {
|
|
2508
|
-
|
|
2509
|
-
const
|
|
2510
|
-
const function_ = tcRecord.function;
|
|
2642
|
+
const tcRecord = asRecord(tc);
|
|
2643
|
+
const function_ = asRecord(tcRecord?.function);
|
|
2511
2644
|
if (!function_) continue;
|
|
2512
2645
|
const toolName = String(function_.name ?? "").trim();
|
|
2513
|
-
const callId = String(tcRecord
|
|
2646
|
+
const callId = String(tcRecord?.id ?? "").trim();
|
|
2514
2647
|
if (toolName && callId && KIMI_IGNORED_TOOLS.has(toolName)) {
|
|
2515
2648
|
ignoredToolCallIds.add(callId);
|
|
2516
2649
|
continue;
|
|
@@ -2575,11 +2708,11 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2575
2708
|
const content = readFileSync3(wirePath, "utf-8");
|
|
2576
2709
|
for (const line of content.split("\n").filter((l) => l.trim())) {
|
|
2577
2710
|
try {
|
|
2578
|
-
const data = JSON.parse(line);
|
|
2579
|
-
const tokenUsage = data
|
|
2711
|
+
const data = asRecord(JSON.parse(line));
|
|
2712
|
+
const tokenUsage = asRecord(asRecord(data?.message)?.usage);
|
|
2580
2713
|
if (!tokenUsage) continue;
|
|
2581
|
-
const inputTokens =
|
|
2582
|
-
const outputTokens =
|
|
2714
|
+
const inputTokens = extractTokenField(tokenUsage, "input_tokens");
|
|
2715
|
+
const outputTokens = extractTokenField(tokenUsage, "output_tokens");
|
|
2583
2716
|
stats.total_input_tokens += inputTokens;
|
|
2584
2717
|
stats.total_output_tokens += outputTokens;
|
|
2585
2718
|
const cost = estimateTokenCost(this.defaultModel, {
|
|
@@ -2599,10 +2732,14 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2599
2732
|
const rawContent = readFileSync3(rawPath, "utf-8");
|
|
2600
2733
|
for (const line of rawContent.split("\n").filter((l) => l.trim())) {
|
|
2601
2734
|
try {
|
|
2602
|
-
const data = JSON.parse(line);
|
|
2603
|
-
if (data
|
|
2604
|
-
|
|
2735
|
+
const data = asRecord(JSON.parse(line));
|
|
2736
|
+
if (data?.role !== "_usage") continue;
|
|
2737
|
+
const tokenCount = asNumber(data.token_count);
|
|
2738
|
+
if (tokenCount === void 0) {
|
|
2739
|
+
reportFieldMismatch("kimi", "usage.token_count");
|
|
2740
|
+
continue;
|
|
2605
2741
|
}
|
|
2742
|
+
stats.total_tokens = tokenCount;
|
|
2606
2743
|
} catch {
|
|
2607
2744
|
}
|
|
2608
2745
|
}
|
|
@@ -2628,11 +2765,268 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2628
2765
|
};
|
|
2629
2766
|
}
|
|
2630
2767
|
};
|
|
2768
|
+
var PARSE_FAIL = /* @__PURE__ */ Symbol("parse-fail");
|
|
2769
|
+
var EXEC_OUTPUT_ENVELOPE_RE = /^Script completed\nWall time [^\n]*\nOutput:\n?/;
|
|
2770
|
+
function stripExecOutputEnvelope(text) {
|
|
2771
|
+
return text.replace(EXEC_OUTPUT_ENVELOPE_RE, "");
|
|
2772
|
+
}
|
|
2773
|
+
function splitExecToolName(name) {
|
|
2774
|
+
if (name.startsWith("mcp__")) {
|
|
2775
|
+
const separator = name.lastIndexOf("__");
|
|
2776
|
+
if (separator > 0 && separator + 2 < name.length) {
|
|
2777
|
+
return { name: name.slice(separator + 2), namespace: name.slice(0, separator + 2) };
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
return { name };
|
|
2781
|
+
}
|
|
2782
|
+
function pickExecOutputTarget(calls) {
|
|
2783
|
+
for (let index = calls.length - 1; index >= 0; index -= 1) {
|
|
2784
|
+
const { name } = splitExecToolName(calls[index].name);
|
|
2785
|
+
if (name !== "apply_patch" && name !== "update_plan") return index;
|
|
2786
|
+
}
|
|
2787
|
+
return calls.length - 1;
|
|
2788
|
+
}
|
|
2789
|
+
function getExecPatchText(args) {
|
|
2790
|
+
if (typeof args === "string") return args;
|
|
2791
|
+
if (args && typeof args === "object") {
|
|
2792
|
+
const patch = args["patch"];
|
|
2793
|
+
if (typeof patch === "string") return patch;
|
|
2794
|
+
}
|
|
2795
|
+
return "";
|
|
2796
|
+
}
|
|
2797
|
+
function decodeExecCalls(input) {
|
|
2798
|
+
if (typeof input !== "string" || !input.includes("tools.")) return [];
|
|
2799
|
+
const scope = collectStringVars(input);
|
|
2800
|
+
const calls = [];
|
|
2801
|
+
const callRe = /tools\.([A-Za-z_$][\w$]*)\s*\(/g;
|
|
2802
|
+
let match;
|
|
2803
|
+
while ((match = callRe.exec(input)) !== null) {
|
|
2804
|
+
const reader = new JsValueReader(input, callRe.lastIndex, scope);
|
|
2805
|
+
const args = reader.parseValue();
|
|
2806
|
+
if (args !== PARSE_FAIL) {
|
|
2807
|
+
calls.push({ name: match[1], args });
|
|
2808
|
+
callRe.lastIndex = reader.pos;
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
return calls;
|
|
2812
|
+
}
|
|
2813
|
+
function collectStringVars(input) {
|
|
2814
|
+
const scope = /* @__PURE__ */ new Map();
|
|
2815
|
+
const assignRe = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*/g;
|
|
2816
|
+
let match;
|
|
2817
|
+
while ((match = assignRe.exec(input)) !== null) {
|
|
2818
|
+
const reader = new JsValueReader(input, assignRe.lastIndex, scope);
|
|
2819
|
+
const value = reader.parseValue();
|
|
2820
|
+
if (value !== PARSE_FAIL) {
|
|
2821
|
+
if (typeof value === "string") scope.set(match[1], value);
|
|
2822
|
+
assignRe.lastIndex = reader.pos;
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
return scope;
|
|
2826
|
+
}
|
|
2827
|
+
var IDENT_START_RE = /[A-Za-z_$]/;
|
|
2828
|
+
var IDENT_PART_RE = /[\w$]/;
|
|
2829
|
+
var JsValueReader = class {
|
|
2830
|
+
pos;
|
|
2831
|
+
src;
|
|
2832
|
+
scope;
|
|
2833
|
+
constructor(src, start, scope) {
|
|
2834
|
+
this.src = src;
|
|
2835
|
+
this.pos = start;
|
|
2836
|
+
this.scope = scope;
|
|
2837
|
+
}
|
|
2838
|
+
parseValue() {
|
|
2839
|
+
this.skipTrivia();
|
|
2840
|
+
const char = this.src[this.pos];
|
|
2841
|
+
if (char === void 0) return PARSE_FAIL;
|
|
2842
|
+
if (char === "{") return this.parseObject();
|
|
2843
|
+
if (char === "[") return this.parseArray();
|
|
2844
|
+
if (char === '"' || char === "'" || char === "`") return this.parseString(char);
|
|
2845
|
+
if (char === "-" || char === "+" || char >= "0" && char <= "9") return this.parseNumber();
|
|
2846
|
+
if (IDENT_START_RE.test(char)) return this.parseIdentifierValue();
|
|
2847
|
+
return PARSE_FAIL;
|
|
2848
|
+
}
|
|
2849
|
+
parseObject() {
|
|
2850
|
+
this.pos++;
|
|
2851
|
+
const result = {};
|
|
2852
|
+
this.skipTrivia();
|
|
2853
|
+
if (this.src[this.pos] === "}") {
|
|
2854
|
+
this.pos++;
|
|
2855
|
+
return result;
|
|
2856
|
+
}
|
|
2857
|
+
while (this.pos < this.src.length) {
|
|
2858
|
+
this.skipTrivia();
|
|
2859
|
+
const key = this.parseKey();
|
|
2860
|
+
if (key === PARSE_FAIL) return PARSE_FAIL;
|
|
2861
|
+
this.skipTrivia();
|
|
2862
|
+
if (this.src[this.pos] === ":") {
|
|
2863
|
+
this.pos++;
|
|
2864
|
+
const value = this.parseValue();
|
|
2865
|
+
if (value === PARSE_FAIL) return PARSE_FAIL;
|
|
2866
|
+
result[key] = value;
|
|
2867
|
+
} else {
|
|
2868
|
+
result[key] = this.resolveIdentifier(key);
|
|
2869
|
+
}
|
|
2870
|
+
this.skipTrivia();
|
|
2871
|
+
const next = this.src[this.pos];
|
|
2872
|
+
if (next === ",") {
|
|
2873
|
+
this.pos++;
|
|
2874
|
+
this.skipTrivia();
|
|
2875
|
+
if (this.src[this.pos] === "}") {
|
|
2876
|
+
this.pos++;
|
|
2877
|
+
return result;
|
|
2878
|
+
}
|
|
2879
|
+
continue;
|
|
2880
|
+
}
|
|
2881
|
+
if (next === "}") {
|
|
2882
|
+
this.pos++;
|
|
2883
|
+
return result;
|
|
2884
|
+
}
|
|
2885
|
+
return PARSE_FAIL;
|
|
2886
|
+
}
|
|
2887
|
+
return PARSE_FAIL;
|
|
2888
|
+
}
|
|
2889
|
+
parseArray() {
|
|
2890
|
+
this.pos++;
|
|
2891
|
+
const result = [];
|
|
2892
|
+
this.skipTrivia();
|
|
2893
|
+
if (this.src[this.pos] === "]") {
|
|
2894
|
+
this.pos++;
|
|
2895
|
+
return result;
|
|
2896
|
+
}
|
|
2897
|
+
while (this.pos < this.src.length) {
|
|
2898
|
+
const value = this.parseValue();
|
|
2899
|
+
if (value === PARSE_FAIL) return PARSE_FAIL;
|
|
2900
|
+
result.push(value);
|
|
2901
|
+
this.skipTrivia();
|
|
2902
|
+
const next = this.src[this.pos];
|
|
2903
|
+
if (next === ",") {
|
|
2904
|
+
this.pos++;
|
|
2905
|
+
this.skipTrivia();
|
|
2906
|
+
if (this.src[this.pos] === "]") {
|
|
2907
|
+
this.pos++;
|
|
2908
|
+
return result;
|
|
2909
|
+
}
|
|
2910
|
+
continue;
|
|
2911
|
+
}
|
|
2912
|
+
if (next === "]") {
|
|
2913
|
+
this.pos++;
|
|
2914
|
+
return result;
|
|
2915
|
+
}
|
|
2916
|
+
return PARSE_FAIL;
|
|
2917
|
+
}
|
|
2918
|
+
return PARSE_FAIL;
|
|
2919
|
+
}
|
|
2920
|
+
parseKey() {
|
|
2921
|
+
const char = this.src[this.pos];
|
|
2922
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
2923
|
+
const value = this.parseString(char);
|
|
2924
|
+
return typeof value === "string" ? value : PARSE_FAIL;
|
|
2925
|
+
}
|
|
2926
|
+
if (char !== void 0 && IDENT_START_RE.test(char)) return this.readIdentifier();
|
|
2927
|
+
return PARSE_FAIL;
|
|
2928
|
+
}
|
|
2929
|
+
parseString(quote) {
|
|
2930
|
+
this.pos++;
|
|
2931
|
+
let out = "";
|
|
2932
|
+
while (this.pos < this.src.length) {
|
|
2933
|
+
const char = this.src[this.pos++];
|
|
2934
|
+
if (char === "\\") {
|
|
2935
|
+
out += this.readEscape();
|
|
2936
|
+
continue;
|
|
2937
|
+
}
|
|
2938
|
+
if (char === quote) break;
|
|
2939
|
+
out += char;
|
|
2940
|
+
}
|
|
2941
|
+
return out;
|
|
2942
|
+
}
|
|
2943
|
+
readEscape() {
|
|
2944
|
+
const char = this.src[this.pos++];
|
|
2945
|
+
switch (char) {
|
|
2946
|
+
case "n":
|
|
2947
|
+
return "\n";
|
|
2948
|
+
case "t":
|
|
2949
|
+
return " ";
|
|
2950
|
+
case "r":
|
|
2951
|
+
return "\r";
|
|
2952
|
+
case "b":
|
|
2953
|
+
return "\b";
|
|
2954
|
+
case "f":
|
|
2955
|
+
return "\f";
|
|
2956
|
+
case "v":
|
|
2957
|
+
return "\v";
|
|
2958
|
+
case "0":
|
|
2959
|
+
return "\0";
|
|
2960
|
+
case "u": {
|
|
2961
|
+
const hex = this.src.slice(this.pos, this.pos + 4);
|
|
2962
|
+
if (/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
2963
|
+
this.pos += 4;
|
|
2964
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
2965
|
+
}
|
|
2966
|
+
return "u";
|
|
2967
|
+
}
|
|
2968
|
+
case "x": {
|
|
2969
|
+
const hex = this.src.slice(this.pos, this.pos + 2);
|
|
2970
|
+
if (/^[0-9a-fA-F]{2}$/.test(hex)) {
|
|
2971
|
+
this.pos += 2;
|
|
2972
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
2973
|
+
}
|
|
2974
|
+
return "x";
|
|
2975
|
+
}
|
|
2976
|
+
default:
|
|
2977
|
+
return char ?? "";
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
parseNumber() {
|
|
2981
|
+
const numberRe = /[-+]?(?:0[xX][0-9a-fA-F]+|(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?)/y;
|
|
2982
|
+
numberRe.lastIndex = this.pos;
|
|
2983
|
+
const match = numberRe.exec(this.src);
|
|
2984
|
+
if (!match) return PARSE_FAIL;
|
|
2985
|
+
this.pos += match[0].length;
|
|
2986
|
+
return Number(match[0]);
|
|
2987
|
+
}
|
|
2988
|
+
parseIdentifierValue() {
|
|
2989
|
+
const name = this.readIdentifier();
|
|
2990
|
+
if (name === "true") return true;
|
|
2991
|
+
if (name === "false") return false;
|
|
2992
|
+
if (name === "null") return null;
|
|
2993
|
+
if (name === "undefined") return void 0;
|
|
2994
|
+
return this.resolveIdentifier(name);
|
|
2995
|
+
}
|
|
2996
|
+
resolveIdentifier(name) {
|
|
2997
|
+
return this.scope.has(name) ? this.scope.get(name) : void 0;
|
|
2998
|
+
}
|
|
2999
|
+
readIdentifier() {
|
|
3000
|
+
const start = this.pos;
|
|
3001
|
+
while (this.pos < this.src.length && IDENT_PART_RE.test(this.src[this.pos])) this.pos++;
|
|
3002
|
+
return this.src.slice(start, this.pos);
|
|
3003
|
+
}
|
|
3004
|
+
skipTrivia() {
|
|
3005
|
+
while (this.pos < this.src.length) {
|
|
3006
|
+
const char = this.src[this.pos];
|
|
3007
|
+
if (char === " " || char === " " || char === "\n" || char === "\r") {
|
|
3008
|
+
this.pos++;
|
|
3009
|
+
continue;
|
|
3010
|
+
}
|
|
3011
|
+
if (char === "/" && this.src[this.pos + 1] === "/") {
|
|
3012
|
+
const newline = this.src.indexOf("\n", this.pos + 2);
|
|
3013
|
+
this.pos = newline === -1 ? this.src.length : newline + 1;
|
|
3014
|
+
continue;
|
|
3015
|
+
}
|
|
3016
|
+
if (char === "/" && this.src[this.pos + 1] === "*") {
|
|
3017
|
+
const close = this.src.indexOf("*/", this.pos + 2);
|
|
3018
|
+
this.pos = close === -1 ? this.src.length : close + 2;
|
|
3019
|
+
continue;
|
|
3020
|
+
}
|
|
3021
|
+
break;
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
};
|
|
2631
3025
|
var PROPOSED_PLAN_PATTERN = /<proposed_plan>\s*([\s\S]*?)\s*<\/proposed_plan>/;
|
|
2632
3026
|
var PLAN_APPROVAL_PREFIX = "PLEASE IMPLEMENT THIS PLAN";
|
|
2633
3027
|
var SUBAGENT_NOTIFICATION_PATTERN = /<subagent_notification>\s*([\s\S]*?)\s*<\/subagent_notification>/;
|
|
2634
3028
|
var HEAD_INDEX_VERSION2 = "codex-head-v1";
|
|
2635
|
-
var PARSER_VERSION = "codex-parser-
|
|
3029
|
+
var PARSER_VERSION = "codex-parser-v4";
|
|
2636
3030
|
var DEVELOPER_LIKE_USER_MARKERS = [
|
|
2637
3031
|
"agents.md instructions for",
|
|
2638
3032
|
"<instructions>",
|
|
@@ -2675,6 +3069,19 @@ function extractCachedInputTokens(usage) {
|
|
|
2675
3069
|
if (!usage) return 0;
|
|
2676
3070
|
return Number(usage["cached_input_tokens"] ?? usage["cache_read_input_tokens"] ?? 0);
|
|
2677
3071
|
}
|
|
3072
|
+
function narrowRecordField(value, field) {
|
|
3073
|
+
return narrowField("codex", field, value, asRecord);
|
|
3074
|
+
}
|
|
3075
|
+
function extractPayload(data) {
|
|
3076
|
+
return narrowRecordField(data["payload"], "payload") ?? {};
|
|
3077
|
+
}
|
|
3078
|
+
function extractTokenUsage(payload) {
|
|
3079
|
+
const info = narrowRecordField(payload["info"], "token_count.info");
|
|
3080
|
+
return {
|
|
3081
|
+
totalUsage: info ? narrowRecordField(info["total_token_usage"], "token_count.total_token_usage") : void 0,
|
|
3082
|
+
lastUsage: info ? narrowRecordField(info["last_token_usage"], "token_count.last_token_usage") : void 0
|
|
3083
|
+
};
|
|
3084
|
+
}
|
|
2678
3085
|
function resolveToolIdentity(name, namespace) {
|
|
2679
3086
|
const mappedName = CODEX_TOOL_TITLE_MAP[name];
|
|
2680
3087
|
if (mappedName) return { tool: mappedName };
|
|
@@ -2709,6 +3116,17 @@ function normalizeCustomToolArguments(toolName, input) {
|
|
|
2709
3116
|
}
|
|
2710
3117
|
return input;
|
|
2711
3118
|
}
|
|
3119
|
+
function flattenOutputText(output) {
|
|
3120
|
+
if (typeof output === "string") return output;
|
|
3121
|
+
if (Array.isArray(output)) {
|
|
3122
|
+
return output.map((item) => {
|
|
3123
|
+
if (typeof item === "string") return item;
|
|
3124
|
+
const record = asRecord(item);
|
|
3125
|
+
return record ? asString(record["text"]) ?? "" : "";
|
|
3126
|
+
}).join("");
|
|
3127
|
+
}
|
|
3128
|
+
return "";
|
|
3129
|
+
}
|
|
2712
3130
|
var PATCH_BEGIN_RE = /\*\*\* Begin Patch/;
|
|
2713
3131
|
var PATCH_END_RE = /\*\*\* End Patch/;
|
|
2714
3132
|
var PATCH_HEADER_RE = /\*\*\*\s+(Add|Delete|Update|Move)\s+File:\s*(.+)/;
|
|
@@ -2815,10 +3233,10 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
2815
3233
|
listSessionSources(options) {
|
|
2816
3234
|
if (!this.basePath) return [];
|
|
2817
3235
|
this.loadSessionIndex();
|
|
2818
|
-
return this.listRolloutFiles(options).map((file) => ({
|
|
3236
|
+
return this.listRolloutFiles(options).map(({ file, stat }) => ({
|
|
2819
3237
|
sessionId: extractSessionId(file),
|
|
2820
3238
|
sourcePath: file,
|
|
2821
|
-
fingerprint: this.sourceFingerprint(file)
|
|
3239
|
+
fingerprint: this.sourceFingerprint(file, stat)
|
|
2822
3240
|
}));
|
|
2823
3241
|
}
|
|
2824
3242
|
scanSessionSource(sourcePath, options) {
|
|
@@ -2849,20 +3267,18 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
2849
3267
|
try {
|
|
2850
3268
|
const recordType = String(record["type"] ?? "");
|
|
2851
3269
|
if (recordType === "turn_context") {
|
|
2852
|
-
const payload = record
|
|
3270
|
+
const payload = extractPayload(record);
|
|
2853
3271
|
activeModel = extractModelName(payload["model"]) ?? activeModel;
|
|
2854
3272
|
}
|
|
2855
3273
|
pendingPlan = this.convertRecord(record, transcript, pendingPlan, activeModel);
|
|
2856
3274
|
if (recordType === "event_msg") {
|
|
2857
|
-
const payload = record
|
|
3275
|
+
const payload = extractPayload(record);
|
|
2858
3276
|
if (String(payload["type"] ?? "") === "token_count") {
|
|
2859
|
-
const
|
|
2860
|
-
const totalUsage = info?.["total_token_usage"];
|
|
3277
|
+
const { totalUsage, lastUsage } = extractTokenUsage(payload);
|
|
2861
3278
|
const cumulativeTotal = Number(totalUsage?.["total_tokens"] ?? 0);
|
|
2862
3279
|
if (cumulativeTotal > 0 && cumulativeTotal === prevCumulativeTotal) {
|
|
2863
3280
|
} else {
|
|
2864
3281
|
prevCumulativeTotal = cumulativeTotal;
|
|
2865
|
-
const lastUsage = info?.["last_token_usage"];
|
|
2866
3282
|
let inputTokens = 0;
|
|
2867
3283
|
let outputTokens = 0;
|
|
2868
3284
|
let reasoningTokens = 0;
|
|
@@ -2937,6 +3353,7 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
2937
3353
|
return [];
|
|
2938
3354
|
}
|
|
2939
3355
|
}
|
|
3356
|
+
/** Stats each rollout file once during the walk; caller reuses it for the scan window check and the fingerprint. */
|
|
2940
3357
|
walkDirForRolloutFiles(dir, options) {
|
|
2941
3358
|
const files = [];
|
|
2942
3359
|
try {
|
|
@@ -2945,14 +3362,14 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
2945
3362
|
if (entry.isDirectory()) {
|
|
2946
3363
|
files.push(...this.walkDirForRolloutFiles(fullPath, options));
|
|
2947
3364
|
} else if (entry.name.endsWith(".jsonl") && entry.name.startsWith("rollout-")) {
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
}
|
|
3365
|
+
let stat;
|
|
3366
|
+
try {
|
|
3367
|
+
stat = statSync4(fullPath);
|
|
3368
|
+
} catch {
|
|
3369
|
+
continue;
|
|
2954
3370
|
}
|
|
2955
|
-
|
|
3371
|
+
if (!matchesScanWindow(stat.mtimeMs, options)) continue;
|
|
3372
|
+
files.push({ file: fullPath, stat });
|
|
2956
3373
|
}
|
|
2957
3374
|
}
|
|
2958
3375
|
} catch {
|
|
@@ -2961,12 +3378,13 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
2961
3378
|
}
|
|
2962
3379
|
buildSessionMeta(head, file) {
|
|
2963
3380
|
const indexPath = this.getSessionIndexPath();
|
|
3381
|
+
const stat = statSync4(file);
|
|
2964
3382
|
return {
|
|
2965
3383
|
id: head.id,
|
|
2966
3384
|
title: head.title,
|
|
2967
3385
|
sourcePath: file,
|
|
2968
|
-
sourceFingerprint: this.sourceFingerprint(file),
|
|
2969
|
-
sourceMtimeMs:
|
|
3386
|
+
sourceFingerprint: this.sourceFingerprint(file, stat),
|
|
3387
|
+
sourceMtimeMs: stat.mtimeMs,
|
|
2970
3388
|
indexPath: existsSync7(indexPath) ? indexPath : null,
|
|
2971
3389
|
indexMtimeMs: this.getFileMtimeMs(indexPath),
|
|
2972
3390
|
headIndexVersion: HEAD_INDEX_VERSION2,
|
|
@@ -2978,8 +3396,8 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
2978
3396
|
updatedAt: head.time_updated ?? head.time_created
|
|
2979
3397
|
};
|
|
2980
3398
|
}
|
|
2981
|
-
|
|
2982
|
-
|
|
3399
|
+
/** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
|
|
3400
|
+
sourceFingerprint(file, stat) {
|
|
2983
3401
|
const sessionId = extractSessionId(file);
|
|
2984
3402
|
return JSON.stringify([
|
|
2985
3403
|
HEAD_INDEX_VERSION2,
|
|
@@ -3046,7 +3464,7 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3046
3464
|
let firstPayload = {};
|
|
3047
3465
|
let createdAt = 0;
|
|
3048
3466
|
let lineCount = 0;
|
|
3049
|
-
|
|
3467
|
+
let messageTitle = null;
|
|
3050
3468
|
let updatedAt = 0;
|
|
3051
3469
|
let messageCount = 0;
|
|
3052
3470
|
let model = null;
|
|
@@ -3072,20 +3490,23 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3072
3490
|
} catch {
|
|
3073
3491
|
return skippedSession("malformed first record");
|
|
3074
3492
|
}
|
|
3075
|
-
firstPayload = firstRecord
|
|
3493
|
+
firstPayload = extractPayload(firstRecord);
|
|
3076
3494
|
createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(firstPayload) || statSync4(filePath).mtimeMs;
|
|
3077
3495
|
updatedAt = createdAt;
|
|
3078
3496
|
}
|
|
3079
|
-
if (titleLines.length < 20) titleLines.push(line);
|
|
3080
3497
|
try {
|
|
3081
3498
|
const data = JSON.parse(line);
|
|
3082
3499
|
const recordType = String(data["type"] ?? "");
|
|
3083
|
-
const payload = data
|
|
3500
|
+
const payload = extractPayload(data);
|
|
3084
3501
|
const payloadType = String(payload["type"] ?? "");
|
|
3085
3502
|
if (isInternalEventType2(recordType) || isInternalEventType2(payloadType)) continue;
|
|
3086
3503
|
hasNonInternalRecord = true;
|
|
3087
|
-
const recordTs = parseTimestampMs2(data) || parseTimestampMs2(
|
|
3504
|
+
const recordTs = parseTimestampMs2(data) || parseTimestampMs2(payload);
|
|
3088
3505
|
if (recordTs > updatedAt) updatedAt = recordTs;
|
|
3506
|
+
if (messageTitle === null && lineCount <= 20) {
|
|
3507
|
+
const candidate = this.extractCodexRecordTitle(data);
|
|
3508
|
+
if (candidate) messageTitle = candidate;
|
|
3509
|
+
}
|
|
3089
3510
|
if (recordType === "session_meta" || recordType === "turn_context") {
|
|
3090
3511
|
const nextModel = extractModelName(payload["model"]);
|
|
3091
3512
|
if (nextModel) {
|
|
@@ -3100,7 +3521,7 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3100
3521
|
if (COUNTED_TYPES.has(pType)) {
|
|
3101
3522
|
messageCount++;
|
|
3102
3523
|
}
|
|
3103
|
-
const info = p["info"];
|
|
3524
|
+
const info = narrowRecordField(p["info"], "response_item.info");
|
|
3104
3525
|
const m = info?.["model"] ?? p["model"];
|
|
3105
3526
|
if (typeof m === "string" && m.trim()) {
|
|
3106
3527
|
activeModel = m.trim();
|
|
@@ -3108,14 +3529,11 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3108
3529
|
}
|
|
3109
3530
|
}
|
|
3110
3531
|
if (recordType === "event_msg") {
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
const info = p["info"];
|
|
3114
|
-
const totalUsage = info?.["total_token_usage"];
|
|
3532
|
+
if (String(payload["type"] ?? "") === "token_count") {
|
|
3533
|
+
const { totalUsage, lastUsage } = extractTokenUsage(payload);
|
|
3115
3534
|
const cumulativeTotal = Number(totalUsage?.["total_tokens"] ?? 0);
|
|
3116
3535
|
if (cumulativeTotal > 0 && cumulativeTotal !== scanPrevCumulativeTotal) {
|
|
3117
3536
|
scanPrevCumulativeTotal = cumulativeTotal;
|
|
3118
|
-
const lastUsage = info?.["last_token_usage"];
|
|
3119
3537
|
let inputTokens = 0;
|
|
3120
3538
|
let outputTokens = 0;
|
|
3121
3539
|
let reasoningTokens = 0;
|
|
@@ -3160,7 +3578,6 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3160
3578
|
if (lineCount === 0) return skippedSession("empty file");
|
|
3161
3579
|
if (!hasNonInternalRecord) return filteredSession("internal events only");
|
|
3162
3580
|
const indexTitle = this.getTitleForSession(sessionId);
|
|
3163
|
-
const messageTitle = this.extractTitleFromLines(titleLines);
|
|
3164
3581
|
const directory = firstPayload["cwd"] ? String(firstPayload["cwd"]) : "";
|
|
3165
3582
|
const title = resolveSessionTitle(indexTitle, messageTitle, basenameTitle(directory || null));
|
|
3166
3583
|
return parsedSession({
|
|
@@ -3195,7 +3612,7 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3195
3612
|
} catch {
|
|
3196
3613
|
return skippedSession("malformed first record");
|
|
3197
3614
|
}
|
|
3198
|
-
const payload = firstRecord
|
|
3615
|
+
const payload = extractPayload(firstRecord);
|
|
3199
3616
|
const stat = statSync4(filePath);
|
|
3200
3617
|
const createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(payload) || stat.mtimeMs;
|
|
3201
3618
|
const indexTitle = this.getTitleForSession(sessionId);
|
|
@@ -3218,31 +3635,42 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3218
3635
|
}
|
|
3219
3636
|
});
|
|
3220
3637
|
}
|
|
3638
|
+
/** Fast path only: parses each of the first 20 lines to find a title (no full stats pass available). */
|
|
3221
3639
|
extractTitleFromLines(lines) {
|
|
3222
3640
|
for (const line of lines.slice(0, 20)) {
|
|
3223
3641
|
try {
|
|
3224
|
-
const
|
|
3225
|
-
const recordType = String(data["type"] ?? "");
|
|
3226
|
-
if (recordType !== "response_item" || isInternalEventType2(recordType)) continue;
|
|
3227
|
-
const payload = data["payload"] ?? {};
|
|
3228
|
-
const pType = String(payload["type"] ?? "");
|
|
3229
|
-
if (pType !== "message" || isInternalEventType2(pType)) continue;
|
|
3230
|
-
if (String(payload["role"] ?? "") !== "user") continue;
|
|
3231
|
-
const content = payload["content"];
|
|
3232
|
-
let text = null;
|
|
3233
|
-
if (Array.isArray(content)) {
|
|
3234
|
-
text = content.filter((item) => typeof item === "object" && item !== null && "text" in item).map((item) => String(item["text"] ?? "")).join(" ");
|
|
3235
|
-
} else if (typeof content === "string") {
|
|
3236
|
-
text = content;
|
|
3237
|
-
}
|
|
3238
|
-
if (!text || isDeveloperLikeUserMessage(text)) continue;
|
|
3239
|
-
const title = normalizeTitleText(text);
|
|
3642
|
+
const title = this.extractCodexRecordTitle(JSON.parse(line));
|
|
3240
3643
|
if (title) return title;
|
|
3241
3644
|
} catch {
|
|
3242
3645
|
}
|
|
3243
3646
|
}
|
|
3244
3647
|
return null;
|
|
3245
3648
|
}
|
|
3649
|
+
/**
|
|
3650
|
+
* Title candidate from a single already-parsed record, if it's a visible
|
|
3651
|
+
* user message. Shared by the main streaming pass (which parses every line
|
|
3652
|
+
* once) and the fast path's extractTitleFromLines().
|
|
3653
|
+
*/
|
|
3654
|
+
extractCodexRecordTitle(data) {
|
|
3655
|
+
const recordType = String(data["type"] ?? "");
|
|
3656
|
+
if (recordType !== "response_item" || isInternalEventType2(recordType)) return null;
|
|
3657
|
+
const payload = extractPayload(data);
|
|
3658
|
+
const pType = String(payload["type"] ?? "");
|
|
3659
|
+
if (pType !== "message" || isInternalEventType2(pType)) return null;
|
|
3660
|
+
if (String(payload["role"] ?? "") !== "user") return null;
|
|
3661
|
+
const content = payload["content"];
|
|
3662
|
+
let text = null;
|
|
3663
|
+
if (Array.isArray(content)) {
|
|
3664
|
+
text = content.map((item) => {
|
|
3665
|
+
const record = asRecord(item);
|
|
3666
|
+
return record ? String(record["text"] ?? "") : "";
|
|
3667
|
+
}).join(" ");
|
|
3668
|
+
} else if (typeof content === "string") {
|
|
3669
|
+
text = content;
|
|
3670
|
+
}
|
|
3671
|
+
if (!text || isDeveloperLikeUserMessage(text)) return null;
|
|
3672
|
+
return normalizeTitleText(text) || null;
|
|
3673
|
+
}
|
|
3246
3674
|
// ---- Record conversion ----
|
|
3247
3675
|
convertRecord(data, transcript, pendingPlan, activeModel) {
|
|
3248
3676
|
const recordType = String(data["type"] ?? "");
|
|
@@ -3251,7 +3679,7 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3251
3679
|
return pendingPlan;
|
|
3252
3680
|
}
|
|
3253
3681
|
if (recordType !== "response_item") return pendingPlan;
|
|
3254
|
-
const payload = data
|
|
3682
|
+
const payload = extractPayload(data);
|
|
3255
3683
|
const payloadType = String(payload["type"] ?? "");
|
|
3256
3684
|
if (isInternalEventType2(payloadType)) return pendingPlan;
|
|
3257
3685
|
const timestampMs = parseTimestampMs2(data) || parseTimestampMs2(payload);
|
|
@@ -3296,8 +3724,8 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3296
3724
|
if (!Array.isArray(content)) return pendingPlan;
|
|
3297
3725
|
const textParts = [];
|
|
3298
3726
|
for (const item of content) {
|
|
3299
|
-
|
|
3300
|
-
|
|
3727
|
+
const ci = asRecord(item);
|
|
3728
|
+
if (!ci) continue;
|
|
3301
3729
|
if (String(ci["type"] ?? "") === "output_text") {
|
|
3302
3730
|
const text = String(ci["text"] ?? "");
|
|
3303
3731
|
if (text.trim()) textParts.push(text);
|
|
@@ -3330,9 +3758,11 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3330
3758
|
// ---- User message ----
|
|
3331
3759
|
convertUserMessage(payload, transcript, timestampMs, pendingPlan) {
|
|
3332
3760
|
const content = payload["content"];
|
|
3333
|
-
const text = Array.isArray(content) ? content.map(
|
|
3334
|
-
|
|
3335
|
-
|
|
3761
|
+
const text = Array.isArray(content) ? content.map((c) => {
|
|
3762
|
+
if (Array.isArray(c)) return "";
|
|
3763
|
+
const record = asRecord(c);
|
|
3764
|
+
return record ? String(record["text"] ?? "") : String(c ?? "");
|
|
3765
|
+
}).join(" ") : String(content ?? "");
|
|
3336
3766
|
const visibleText = cleanInternalText(text);
|
|
3337
3767
|
if (!visibleText) return pendingPlan;
|
|
3338
3768
|
if (isDeveloperLikeUserMessage(visibleText)) return pendingPlan;
|
|
@@ -3348,8 +3778,13 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3348
3778
|
}
|
|
3349
3779
|
const subagentMatch = visibleText.match(SUBAGENT_NOTIFICATION_PATTERN);
|
|
3350
3780
|
if (subagentMatch) {
|
|
3781
|
+
let notifPayload;
|
|
3351
3782
|
try {
|
|
3352
|
-
|
|
3783
|
+
notifPayload = asRecord(JSON.parse(subagentMatch[1]));
|
|
3784
|
+
} catch {
|
|
3785
|
+
notifPayload = void 0;
|
|
3786
|
+
}
|
|
3787
|
+
if (notifPayload) {
|
|
3353
3788
|
const agentId = String(notifPayload["agent_id"] ?? "");
|
|
3354
3789
|
const nickname = String(notifPayload["nickname"] ?? "");
|
|
3355
3790
|
const completedText = String(notifPayload["completed"] ?? "");
|
|
@@ -3369,7 +3804,6 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3369
3804
|
});
|
|
3370
3805
|
transcript.beginTurn();
|
|
3371
3806
|
return pendingPlan;
|
|
3372
|
-
} catch {
|
|
3373
3807
|
}
|
|
3374
3808
|
}
|
|
3375
3809
|
transcript.appendMessage({
|
|
@@ -3386,12 +3820,11 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3386
3820
|
if (!Array.isArray(summary)) return;
|
|
3387
3821
|
const texts = [];
|
|
3388
3822
|
for (const item of summary) {
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
}
|
|
3823
|
+
const ci = asRecord(item);
|
|
3824
|
+
if (!ci) continue;
|
|
3825
|
+
if (String(ci["type"] ?? "") === "summary_text") {
|
|
3826
|
+
const text = String(ci["text"] ?? "");
|
|
3827
|
+
if (text.trim()) texts.push(text);
|
|
3395
3828
|
}
|
|
3396
3829
|
}
|
|
3397
3830
|
if (texts.length === 0) return;
|
|
@@ -3437,7 +3870,9 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3437
3870
|
convertToolCallOutput(payload, transcript, timestampMs) {
|
|
3438
3871
|
const callId = String(payload["call_id"] ?? "").trim();
|
|
3439
3872
|
if (!callId) return;
|
|
3440
|
-
const outputText = cleanInternalText(
|
|
3873
|
+
const outputText = cleanInternalText(
|
|
3874
|
+
stripExecOutputEnvelope(flattenOutputText(payload["output"]))
|
|
3875
|
+
);
|
|
3441
3876
|
const outputParts = outputText ? [{ type: "text", text: outputText, time_created: timestampMs }] : [];
|
|
3442
3877
|
if (outputParts.length > 0) {
|
|
3443
3878
|
transcript.resolveToolCall(callId, { output: outputParts, status: "completed" });
|
|
@@ -3448,6 +3883,13 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3448
3883
|
const callId = String(payload["call_id"] ?? "").trim();
|
|
3449
3884
|
const name = String(payload["name"] ?? "").trim();
|
|
3450
3885
|
if (!name) return;
|
|
3886
|
+
if (name === "exec") {
|
|
3887
|
+
const decoded = decodeExecCalls(payload["input"]);
|
|
3888
|
+
if (decoded.length > 0) {
|
|
3889
|
+
this.appendDecodedExecCalls(decoded, callId, transcript, timestampMs, activeModel);
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3451
3893
|
const toolIdentity = resolveToolIdentity(name, payload["namespace"]);
|
|
3452
3894
|
const rawInput = payload["input"];
|
|
3453
3895
|
const normalizedInput = normalizeCustomToolArguments(name, rawInput);
|
|
@@ -3469,6 +3911,36 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3469
3911
|
{ markModeAsTool: true }
|
|
3470
3912
|
);
|
|
3471
3913
|
}
|
|
3914
|
+
// ---- Decoded code-mode exec calls ----
|
|
3915
|
+
appendDecodedExecCalls(calls, callId, transcript, timestampMs, activeModel) {
|
|
3916
|
+
const outputIndex = pickExecOutputTarget(calls);
|
|
3917
|
+
calls.forEach((call, index) => {
|
|
3918
|
+
const partCallId = index === outputIndex ? callId : `${callId}#${index}`;
|
|
3919
|
+
this.appendDecodedExecCall(call, partCallId, transcript, timestampMs, activeModel);
|
|
3920
|
+
});
|
|
3921
|
+
}
|
|
3922
|
+
appendDecodedExecCall(call, callId, transcript, timestampMs, activeModel) {
|
|
3923
|
+
const { name, namespace } = splitExecToolName(call.name);
|
|
3924
|
+
const toolIdentity = resolveToolIdentity(name, namespace);
|
|
3925
|
+
const arguments_ = name === "apply_patch" ? parseApplyPatchInput(getExecPatchText(call.args)) : call.args;
|
|
3926
|
+
const toolPart = {
|
|
3927
|
+
type: "tool",
|
|
3928
|
+
tool: toolIdentity.tool,
|
|
3929
|
+
callID: callId,
|
|
3930
|
+
title: `Tool: ${toolIdentity.tool}`,
|
|
3931
|
+
state: {
|
|
3932
|
+
arguments: arguments_,
|
|
3933
|
+
output: null,
|
|
3934
|
+
metadata: toolIdentity.metadata
|
|
3935
|
+
},
|
|
3936
|
+
time_created: timestampMs
|
|
3937
|
+
};
|
|
3938
|
+
transcript.appendToolCall(
|
|
3939
|
+
toolPart,
|
|
3940
|
+
{ id: "", timestampMs, agent: "codex", model: activeModel },
|
|
3941
|
+
{ markModeAsTool: true }
|
|
3942
|
+
);
|
|
3943
|
+
}
|
|
3472
3944
|
};
|
|
3473
3945
|
var PerfTracer = class {
|
|
3474
3946
|
rootMarkers = [];
|
|
@@ -3544,6 +4016,106 @@ var PerfTracer = class {
|
|
|
3544
4016
|
}
|
|
3545
4017
|
};
|
|
3546
4018
|
var perf = new PerfTracer();
|
|
4019
|
+
function narrowString(field, value) {
|
|
4020
|
+
return narrowField("cursor", field, value, asString);
|
|
4021
|
+
}
|
|
4022
|
+
function narrowNumber(field, value) {
|
|
4023
|
+
return narrowField("cursor", field, value, asNumber);
|
|
4024
|
+
}
|
|
4025
|
+
function parseSubagentInfos(value) {
|
|
4026
|
+
const arr = asArray(value);
|
|
4027
|
+
if (arr === void 0) return void 0;
|
|
4028
|
+
const infos = [];
|
|
4029
|
+
for (const item of arr) {
|
|
4030
|
+
const record = asRecord(item);
|
|
4031
|
+
if (!record) continue;
|
|
4032
|
+
infos.push({
|
|
4033
|
+
id: narrowString("subagentInfo.id", record.id),
|
|
4034
|
+
composerId: narrowString("subagentInfo.composerId", record.composerId),
|
|
4035
|
+
title: narrowString("subagentInfo.title", record.title),
|
|
4036
|
+
nickname: narrowString("subagentInfo.nickname", record.nickname)
|
|
4037
|
+
});
|
|
4038
|
+
}
|
|
4039
|
+
return infos;
|
|
4040
|
+
}
|
|
4041
|
+
function parseChatMessages(value) {
|
|
4042
|
+
const arr = asArray(value);
|
|
4043
|
+
if (arr === void 0) return void 0;
|
|
4044
|
+
const messages = [];
|
|
4045
|
+
for (const item of arr) {
|
|
4046
|
+
const record = asRecord(item);
|
|
4047
|
+
if (!record) continue;
|
|
4048
|
+
const role = narrowString("chatMessage.role", record.role);
|
|
4049
|
+
if (role === void 0) continue;
|
|
4050
|
+
messages.push({ ...record, role, text: narrowString("chatMessage.text", record.text) });
|
|
4051
|
+
}
|
|
4052
|
+
return messages;
|
|
4053
|
+
}
|
|
4054
|
+
function parseComposerRow(value) {
|
|
4055
|
+
const record = safeParseJsonRecord(value);
|
|
4056
|
+
if (!record) return null;
|
|
4057
|
+
const modelConfig = asRecord(record.modelConfig);
|
|
4058
|
+
return {
|
|
4059
|
+
id: narrowString("composer.id", record.id),
|
|
4060
|
+
composerId: narrowString("composer.composerId", record.composerId),
|
|
4061
|
+
text: narrowString("composer.text", record.text),
|
|
4062
|
+
name: narrowString("composer.name", record.name),
|
|
4063
|
+
title: narrowString("composer.title", record.title),
|
|
4064
|
+
createdAt: narrowNumber("composer.createdAt", record.createdAt),
|
|
4065
|
+
updatedAt: narrowNumber("composer.updatedAt", record.updatedAt),
|
|
4066
|
+
lastSendTime: narrowNumber("composer.lastSendTime", record.lastSendTime),
|
|
4067
|
+
lastUpdatedAt: narrowNumber("composer.lastUpdatedAt", record.lastUpdatedAt),
|
|
4068
|
+
model: narrowString("composer.model", record.model),
|
|
4069
|
+
modelConfig: modelConfig ? { modelName: narrowString("composer.modelConfig.modelName", modelConfig.modelName) } : void 0,
|
|
4070
|
+
inputTokenCount: narrowNumber("composer.inputTokenCount", record.inputTokenCount),
|
|
4071
|
+
outputTokenCount: narrowNumber("composer.outputTokenCount", record.outputTokenCount),
|
|
4072
|
+
subagentInfos: parseSubagentInfos(record.subagentInfos),
|
|
4073
|
+
chatMessages: parseChatMessages(record.chatMessages)
|
|
4074
|
+
};
|
|
4075
|
+
}
|
|
4076
|
+
function parseBubbleRow(value) {
|
|
4077
|
+
const record = safeParseJsonRecord(value);
|
|
4078
|
+
if (!record) return null;
|
|
4079
|
+
const timingInfo = asRecord(record.timingInfo);
|
|
4080
|
+
const tokenCount = asRecord(record.tokenCount);
|
|
4081
|
+
const modelInfo = asRecord(record.modelInfo);
|
|
4082
|
+
const toolFormerData = asRecord(record.toolFormerData);
|
|
4083
|
+
return {
|
|
4084
|
+
...record,
|
|
4085
|
+
id: narrowString("bubble.id", record.id),
|
|
4086
|
+
composerId: narrowString("bubble.composerId", record.composerId),
|
|
4087
|
+
chatMessages: parseChatMessages(record.chatMessages),
|
|
4088
|
+
type: narrowNumber("bubble.type", record.type),
|
|
4089
|
+
text: narrowString("bubble.text", record.text),
|
|
4090
|
+
requestId: narrowString("bubble.requestId", record.requestId),
|
|
4091
|
+
createdAt: narrowNumber("bubble.createdAt", record.createdAt),
|
|
4092
|
+
timestamp: narrowNumber("bubble.timestamp", record.timestamp),
|
|
4093
|
+
timingInfo: timingInfo ? {
|
|
4094
|
+
clientRpcSendTime: narrowNumber(
|
|
4095
|
+
"bubble.timingInfo.clientRpcSendTime",
|
|
4096
|
+
timingInfo.clientRpcSendTime
|
|
4097
|
+
),
|
|
4098
|
+
clientSettleTime: narrowNumber(
|
|
4099
|
+
"bubble.timingInfo.clientSettleTime",
|
|
4100
|
+
timingInfo.clientSettleTime
|
|
4101
|
+
),
|
|
4102
|
+
clientEndTime: narrowNumber("bubble.timingInfo.clientEndTime", timingInfo.clientEndTime)
|
|
4103
|
+
} : void 0,
|
|
4104
|
+
tokenCount: tokenCount ? {
|
|
4105
|
+
inputTokens: narrowNumber("bubble.tokenCount.inputTokens", tokenCount.inputTokens),
|
|
4106
|
+
outputTokens: narrowNumber("bubble.tokenCount.outputTokens", tokenCount.outputTokens)
|
|
4107
|
+
} : void 0,
|
|
4108
|
+
modelInfo: modelInfo ? { modelName: narrowString("bubble.modelInfo.modelName", modelInfo.modelName) } : void 0,
|
|
4109
|
+
toolFormerData: toolFormerData ? {
|
|
4110
|
+
name: narrowString("bubble.toolFormerData.name", toolFormerData.name),
|
|
4111
|
+
toolCallId: narrowString("bubble.toolFormerData.toolCallId", toolFormerData.toolCallId),
|
|
4112
|
+
status: narrowString("bubble.toolFormerData.status", toolFormerData.status),
|
|
4113
|
+
params: toolFormerData.params,
|
|
4114
|
+
result: toolFormerData.result,
|
|
4115
|
+
additionalData: asRecord(toolFormerData.additionalData)
|
|
4116
|
+
} : void 0
|
|
4117
|
+
};
|
|
4118
|
+
}
|
|
3547
4119
|
var CURSOR_TOOL_TITLE_MAP = {
|
|
3548
4120
|
read_file_v2: "read",
|
|
3549
4121
|
edit_file_v2: "edit",
|
|
@@ -3564,9 +4136,8 @@ function normalizeToolOutputParts2(output, timestampMs) {
|
|
|
3564
4136
|
const parts = [];
|
|
3565
4137
|
for (const item of output) {
|
|
3566
4138
|
if (typeof item === "object" && item !== null) {
|
|
3567
|
-
const
|
|
3568
|
-
|
|
3569
|
-
);
|
|
4139
|
+
const record = asRecord(item);
|
|
4140
|
+
const text2 = String(record?.text ?? record?.content ?? "");
|
|
3570
4141
|
const cleaned = cleanInternalText(text2);
|
|
3571
4142
|
if (cleaned) parts.push({ type: "text", text: cleaned, time_created: timestampMs });
|
|
3572
4143
|
} else if (typeof item === "string") {
|
|
@@ -3606,9 +4177,9 @@ function buildToolState(action) {
|
|
|
3606
4177
|
}
|
|
3607
4178
|
if (!state.status) {
|
|
3608
4179
|
if (typeof action.output === "object" && action.output !== null) {
|
|
3609
|
-
const out = action.output;
|
|
3610
|
-
if (out
|
|
3611
|
-
else if (out
|
|
4180
|
+
const out = asRecord(action.output);
|
|
4181
|
+
if (out?.success === true) state.status = "completed";
|
|
4182
|
+
else if (out?.success === false) state.status = "error";
|
|
3612
4183
|
else state.status = "completed";
|
|
3613
4184
|
} else if (action.output != null) {
|
|
3614
4185
|
state.status = "completed";
|
|
@@ -3697,8 +4268,8 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
3697
4268
|
if (!existsSync8(wsJsonPath)) continue;
|
|
3698
4269
|
let workspacePath;
|
|
3699
4270
|
try {
|
|
3700
|
-
const data = JSON.parse(readFileSync5(wsJsonPath, "utf-8"));
|
|
3701
|
-
const uri =
|
|
4271
|
+
const data = asRecord(JSON.parse(readFileSync5(wsJsonPath, "utf-8")));
|
|
4272
|
+
const uri = narrowString("workspaceJson.folder", data?.folder) ?? narrowString("workspaceJson.workspace", data?.workspace) ?? "";
|
|
3702
4273
|
if (!uri) continue;
|
|
3703
4274
|
workspacePath = normalize(decodeURIComponent(uri.replace(/^file:\/\//, "")));
|
|
3704
4275
|
} catch {
|
|
@@ -3712,16 +4283,11 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
3712
4283
|
const row = wsDb.prepare("SELECT value FROM ItemTable WHERE key = 'composer.composerData'").get();
|
|
3713
4284
|
if (!row?.value) continue;
|
|
3714
4285
|
const parsed = JSON.parse(row.value);
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
} else {
|
|
3721
|
-
continue;
|
|
3722
|
-
}
|
|
3723
|
-
for (const c of composers) {
|
|
3724
|
-
const id = c.composerId ?? c.id;
|
|
4286
|
+
const composers = asArray(asRecord(parsed)?.allComposers) ?? asArray(parsed) ?? [];
|
|
4287
|
+
for (const item of composers) {
|
|
4288
|
+
const composer = asRecord(item);
|
|
4289
|
+
if (!composer) continue;
|
|
4290
|
+
const id = narrowString("workspaceComposer.composerId", composer.composerId) ?? narrowString("workspaceComposer.id", composer.id);
|
|
3725
4291
|
if (id) map.set(id, workspacePath);
|
|
3726
4292
|
}
|
|
3727
4293
|
} catch {
|
|
@@ -3752,8 +4318,8 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
3752
4318
|
let processed = 0;
|
|
3753
4319
|
for (const row of rows) {
|
|
3754
4320
|
try {
|
|
3755
|
-
const composer =
|
|
3756
|
-
if (!composer.id && !composer.composerId) continue;
|
|
4321
|
+
const composer = parseComposerRow(row.value);
|
|
4322
|
+
if (!composer || !composer.id && !composer.composerId) continue;
|
|
3757
4323
|
const composerId = composer.id || composer.composerId || "";
|
|
3758
4324
|
const createdAt = composer.createdAt ?? 0;
|
|
3759
4325
|
const updatedAt = composer.updatedAt ?? composer.lastUpdatedAt ?? composer.lastSendTime ?? createdAt;
|
|
@@ -3954,8 +4520,8 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
3954
4520
|
const rows = db.prepare("SELECT value FROM cursorDiskKV WHERE key LIKE ? ORDER BY key").all(`bubbleId:${composerId}:%`);
|
|
3955
4521
|
for (const row of rows) {
|
|
3956
4522
|
try {
|
|
3957
|
-
const bubble =
|
|
3958
|
-
if (bubble
|
|
4523
|
+
const bubble = parseBubbleRow(row.value);
|
|
4524
|
+
if (bubble?.requestId?.trim()) {
|
|
3959
4525
|
return bubble.requestId.trim();
|
|
3960
4526
|
}
|
|
3961
4527
|
} catch {
|
|
@@ -3971,8 +4537,8 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
3971
4537
|
const rows = db.prepare("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'bubbleId:%' AND value LIKE ?").all(`%"requestId":"${requestId}"%`);
|
|
3972
4538
|
for (const row of rows) {
|
|
3973
4539
|
try {
|
|
3974
|
-
const bubble =
|
|
3975
|
-
if (bubble
|
|
4540
|
+
const bubble = parseBubbleRow(row.value);
|
|
4541
|
+
if (bubble?.requestId === requestId) {
|
|
3976
4542
|
const keyParts = row.key.split(":");
|
|
3977
4543
|
if (keyParts.length >= 2 && keyParts[1]) {
|
|
3978
4544
|
return keyParts[1];
|
|
@@ -3998,8 +4564,8 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
3998
4564
|
let count = 0;
|
|
3999
4565
|
for (const row of rows) {
|
|
4000
4566
|
try {
|
|
4001
|
-
const bubble =
|
|
4002
|
-
if (bubble
|
|
4567
|
+
const bubble = parseBubbleRow(row.value);
|
|
4568
|
+
if (bubble?.type === 1 || bubble?.type === 2) {
|
|
4003
4569
|
count++;
|
|
4004
4570
|
}
|
|
4005
4571
|
} catch {
|
|
@@ -4019,8 +4585,8 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4019
4585
|
let messageIndex = 0;
|
|
4020
4586
|
for (const row of rows) {
|
|
4021
4587
|
try {
|
|
4022
|
-
const bubble =
|
|
4023
|
-
if (isInternalBubble(bubble)) continue;
|
|
4588
|
+
const bubble = parseBubbleRow(row.value);
|
|
4589
|
+
if (!bubble || isInternalBubble(bubble)) continue;
|
|
4024
4590
|
const bubbleId = row.key.split(":").pop() || String(messageIndex);
|
|
4025
4591
|
const role = bubble.type === 2 ? "assistant" : "user";
|
|
4026
4592
|
let timestampMs = 0;
|
|
@@ -4109,7 +4675,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4109
4675
|
}
|
|
4110
4676
|
}
|
|
4111
4677
|
if (toolName === "create_plan") {
|
|
4112
|
-
const planText =
|
|
4678
|
+
const planText = asRecord(state.input)?.plan;
|
|
4113
4679
|
return {
|
|
4114
4680
|
type: "plan",
|
|
4115
4681
|
title: "Plan",
|
|
@@ -4131,20 +4697,12 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4131
4697
|
loadComposer(db, sessionId) {
|
|
4132
4698
|
const row = db.prepare("SELECT value FROM cursorDiskKV WHERE key = ?").get(`composerData:${sessionId}`);
|
|
4133
4699
|
if (!row) return null;
|
|
4134
|
-
|
|
4135
|
-
return JSON.parse(row.value);
|
|
4136
|
-
} catch {
|
|
4137
|
-
return null;
|
|
4138
|
-
}
|
|
4700
|
+
return parseComposerRow(row.value);
|
|
4139
4701
|
}
|
|
4140
4702
|
loadBubble(db, sessionId) {
|
|
4141
4703
|
const row = db.prepare("SELECT value FROM cursorDiskKV WHERE key = ?").get(`bubble:${sessionId}`);
|
|
4142
4704
|
if (!row) return null;
|
|
4143
|
-
|
|
4144
|
-
return JSON.parse(row.value);
|
|
4145
|
-
} catch {
|
|
4146
|
-
return null;
|
|
4147
|
-
}
|
|
4705
|
+
return parseBubbleRow(row.value);
|
|
4148
4706
|
}
|
|
4149
4707
|
appendSubagentMessages(db, composer, messages) {
|
|
4150
4708
|
const subagentInfos = composer.subagentInfos;
|
|
@@ -4198,6 +4756,17 @@ function parseTimestampMs3(value) {
|
|
|
4198
4756
|
const ts = Date.parse(text);
|
|
4199
4757
|
return Number.isNaN(ts) ? 0 : ts;
|
|
4200
4758
|
}
|
|
4759
|
+
function narrowPiField(field, value, narrow) {
|
|
4760
|
+
return narrowField("pi", field, value, narrow);
|
|
4761
|
+
}
|
|
4762
|
+
function narrowTimestampMs(field, value) {
|
|
4763
|
+
const shaped = narrowPiField(
|
|
4764
|
+
field,
|
|
4765
|
+
value,
|
|
4766
|
+
(v) => typeof v === "number" || typeof v === "string" ? v : void 0
|
|
4767
|
+
);
|
|
4768
|
+
return shaped === void 0 ? 0 : parseTimestampMs3(shaped);
|
|
4769
|
+
}
|
|
4201
4770
|
function extractSessionIdFromFilename(filePath) {
|
|
4202
4771
|
const stem = basename6(filePath, ".jsonl");
|
|
4203
4772
|
const underscore = stem.indexOf("_");
|
|
@@ -4221,7 +4790,7 @@ function normalizeTextParts(content, timestampMs) {
|
|
|
4221
4790
|
return text ? [{ type: "text", text, time_created: timestampMs }] : [];
|
|
4222
4791
|
}
|
|
4223
4792
|
function getEntryTimestamp(entry) {
|
|
4224
|
-
return
|
|
4793
|
+
return narrowTimestampMs("entry.timestamp", entry["timestamp"]);
|
|
4225
4794
|
}
|
|
4226
4795
|
function chooseLeafEntry(entries) {
|
|
4227
4796
|
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
@@ -4265,10 +4834,10 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4265
4834
|
}
|
|
4266
4835
|
listSessionSources(options) {
|
|
4267
4836
|
if (!this.basePath) return [];
|
|
4268
|
-
return this.
|
|
4837
|
+
return this.walkJsonlFiles(this.basePath, options).map(({ file, stat }) => ({
|
|
4269
4838
|
sessionId: extractSessionIdFromFilename(file),
|
|
4270
4839
|
sourcePath: file,
|
|
4271
|
-
fingerprint: this.sourceFingerprint(
|
|
4840
|
+
fingerprint: this.sourceFingerprint(stat)
|
|
4272
4841
|
}));
|
|
4273
4842
|
}
|
|
4274
4843
|
scanSessionSource(sourcePath) {
|
|
@@ -4305,8 +4874,9 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4305
4874
|
}
|
|
4306
4875
|
listSessionFiles(options) {
|
|
4307
4876
|
if (!this.basePath) return [];
|
|
4308
|
-
return this.walkJsonlFiles(this.basePath, options);
|
|
4877
|
+
return this.walkJsonlFiles(this.basePath, options).map(({ file }) => file);
|
|
4309
4878
|
}
|
|
4879
|
+
/** Stats each file once during the walk; caller reuses it for both the scan window check and the fingerprint. */
|
|
4310
4880
|
walkJsonlFiles(dir, options) {
|
|
4311
4881
|
const files = [];
|
|
4312
4882
|
try {
|
|
@@ -4317,20 +4887,27 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4317
4887
|
continue;
|
|
4318
4888
|
}
|
|
4319
4889
|
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
4320
|
-
|
|
4321
|
-
|
|
4890
|
+
let stat;
|
|
4891
|
+
try {
|
|
4892
|
+
stat = statSync6(fullPath);
|
|
4893
|
+
} catch {
|
|
4894
|
+
continue;
|
|
4895
|
+
}
|
|
4896
|
+
if (!matchesScanWindow(stat.mtimeMs, options)) continue;
|
|
4897
|
+
files.push({ file: fullPath, stat });
|
|
4322
4898
|
}
|
|
4323
4899
|
} catch {
|
|
4324
4900
|
}
|
|
4325
4901
|
return files;
|
|
4326
4902
|
}
|
|
4327
4903
|
buildSessionMeta(head, file) {
|
|
4904
|
+
const stat = statSync6(file);
|
|
4328
4905
|
return {
|
|
4329
4906
|
id: head.id,
|
|
4330
4907
|
title: head.title,
|
|
4331
4908
|
sourcePath: file,
|
|
4332
|
-
sourceFingerprint: this.sourceFingerprint(
|
|
4333
|
-
sourceMtimeMs:
|
|
4909
|
+
sourceFingerprint: this.sourceFingerprint(stat),
|
|
4910
|
+
sourceMtimeMs: stat.mtimeMs,
|
|
4334
4911
|
headIndexVersion: HEAD_INDEX_VERSION3,
|
|
4335
4912
|
parserVersion: PARSER_VERSION2,
|
|
4336
4913
|
directory: head.directory,
|
|
@@ -4339,8 +4916,8 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4339
4916
|
updatedAt: head.time_updated ?? head.time_created
|
|
4340
4917
|
};
|
|
4341
4918
|
}
|
|
4342
|
-
|
|
4343
|
-
|
|
4919
|
+
/** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
|
|
4920
|
+
sourceFingerprint(stat) {
|
|
4344
4921
|
return JSON.stringify([HEAD_INDEX_VERSION3, PARSER_VERSION2, stat.mtimeMs, stat.size]);
|
|
4345
4922
|
}
|
|
4346
4923
|
parseSessionHeadResult(filePath) {
|
|
@@ -4380,7 +4957,7 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4380
4957
|
if (!sessionId) throw new Error("missing session id");
|
|
4381
4958
|
const stat = statSync6(filePath);
|
|
4382
4959
|
const directory = String(header["cwd"] ?? "").trim() || basename6(filePath, ".jsonl");
|
|
4383
|
-
const createdAt =
|
|
4960
|
+
const createdAt = narrowTimestampMs("session.timestamp", header["timestamp"]) || stat.mtimeMs;
|
|
4384
4961
|
const updatedAt = pathEntries.reduce(
|
|
4385
4962
|
(max, entry) => Math.max(max, getEntryTimestamp(entry)),
|
|
4386
4963
|
createdAt
|
|
@@ -4423,8 +5000,8 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4423
5000
|
const timestampMs = getEntryTimestamp(entry);
|
|
4424
5001
|
const type = String(entry["type"] ?? "");
|
|
4425
5002
|
if (type === "message") {
|
|
4426
|
-
const message = entry["message"];
|
|
4427
|
-
if (!
|
|
5003
|
+
const message = narrowPiField("entry.message", entry["message"], asRecord);
|
|
5004
|
+
if (!message) continue;
|
|
4428
5005
|
const result2 = this.convertAgentMessage(entry, message, timestampMs, builder);
|
|
4429
5006
|
if (!result2) continue;
|
|
4430
5007
|
if (result2.message) builder.appendMessage(result2.message);
|
|
@@ -4448,8 +5025,8 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4448
5025
|
};
|
|
4449
5026
|
}
|
|
4450
5027
|
convertAgentMessage(entry, message, timestampMs, builder) {
|
|
4451
|
-
const id =
|
|
4452
|
-
const role =
|
|
5028
|
+
const id = narrowPiField("message.id", entry["id"], asString) ?? "";
|
|
5029
|
+
const role = narrowPiField("message.role", message["role"], asString) ?? "";
|
|
4453
5030
|
if (role === "user") {
|
|
4454
5031
|
const parts = normalizeTextParts(message["content"], timestampMs);
|
|
4455
5032
|
if (parts.length === 0) return null;
|
|
@@ -4588,7 +5165,7 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4588
5165
|
const text = cleanInternalText(rawText);
|
|
4589
5166
|
if (!text) return null;
|
|
4590
5167
|
return {
|
|
4591
|
-
id:
|
|
5168
|
+
id: narrowPiField("summary.id", entry["id"], asString) ?? "",
|
|
4592
5169
|
role: type === "custom_message" ? "user" : "assistant",
|
|
4593
5170
|
agent: type === "custom_message" ? void 0 : "pi",
|
|
4594
5171
|
timestampMs,
|
|
@@ -4596,14 +5173,12 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4596
5173
|
};
|
|
4597
5174
|
}
|
|
4598
5175
|
normalizeUsage(raw) {
|
|
4599
|
-
const usage =
|
|
4600
|
-
const inputTokens =
|
|
4601
|
-
const outputTokens =
|
|
4602
|
-
const cacheReadTokens =
|
|
4603
|
-
const cacheCreateTokens =
|
|
4604
|
-
const totalTokens =
|
|
4605
|
-
usage["totalTokens"] ?? inputTokens + outputTokens + cacheReadTokens + cacheCreateTokens
|
|
4606
|
-
);
|
|
5176
|
+
const usage = narrowPiField("message.usage", raw, asRecord) ?? {};
|
|
5177
|
+
const inputTokens = narrowPiField("message.usage.input", usage["input"], asNumber) ?? 0;
|
|
5178
|
+
const outputTokens = narrowPiField("message.usage.output", usage["output"], asNumber) ?? 0;
|
|
5179
|
+
const cacheReadTokens = narrowPiField("message.usage.cacheRead", usage["cacheRead"], asNumber) ?? 0;
|
|
5180
|
+
const cacheCreateTokens = narrowPiField("message.usage.cacheWrite", usage["cacheWrite"], asNumber) ?? 0;
|
|
5181
|
+
const totalTokens = narrowPiField("message.usage.totalTokens", usage["totalTokens"], asNumber) ?? inputTokens + outputTokens + cacheReadTokens + cacheCreateTokens;
|
|
4607
5182
|
const cost = isObject(usage["cost"]) ? Number(usage["cost"]["total"] ?? 0) : null;
|
|
4608
5183
|
return {
|
|
4609
5184
|
inputTokens: inputTokens + cacheReadTokens + cacheCreateTokens,
|
|
@@ -4645,6 +5220,7 @@ var ZCodeAgent = class extends OpenCodeSqliteAgent {
|
|
|
4645
5220
|
};
|
|
4646
5221
|
registerAgent({
|
|
4647
5222
|
icon: "/icon/agent/claudecode.svg",
|
|
5223
|
+
iconColored: true,
|
|
4648
5224
|
create: () => new ClaudeCodeAgent()
|
|
4649
5225
|
});
|
|
4650
5226
|
registerAgent({
|
|
@@ -4734,7 +5310,23 @@ function normalizeGitRemote(url) {
|
|
|
4734
5310
|
if (!value.includes("/")) return null;
|
|
4735
5311
|
return value.toLowerCase();
|
|
4736
5312
|
}
|
|
5313
|
+
var IDENTITY_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
5314
|
+
var identityCache = /* @__PURE__ */ new Map();
|
|
5315
|
+
function clearIdentityCache() {
|
|
5316
|
+
identityCache.clear();
|
|
5317
|
+
}
|
|
4737
5318
|
function computeIdentity(cwd, fs) {
|
|
5319
|
+
if (fs !== realFs) return resolveIdentity(cwd, fs);
|
|
5320
|
+
const key = cwd ?? "";
|
|
5321
|
+
const cached = identityCache.get(key);
|
|
5322
|
+
if (cached && Date.now() - cached.resolvedAt < IDENTITY_CACHE_TTL_MS) {
|
|
5323
|
+
return cached.identity;
|
|
5324
|
+
}
|
|
5325
|
+
const identity = resolveIdentity(cwd, fs);
|
|
5326
|
+
identityCache.set(key, { identity, resolvedAt: Date.now() });
|
|
5327
|
+
return identity;
|
|
5328
|
+
}
|
|
5329
|
+
function resolveIdentity(cwd, fs) {
|
|
4738
5330
|
if (!cwd) return loose();
|
|
4739
5331
|
const pathOps = getPathOps(cwd);
|
|
4740
5332
|
const absoluteCwd = pathOps.resolve(cwd);
|
|
@@ -5668,7 +6260,10 @@ function withCacheDb(fn) {
|
|
|
5668
6260
|
setSchemaEnsuredPath(cachePath);
|
|
5669
6261
|
}
|
|
5670
6262
|
return fn(db);
|
|
5671
|
-
} catch {
|
|
6263
|
+
} catch (error) {
|
|
6264
|
+
getCoreDiagnostics()?.warn("cache.write_failed", {
|
|
6265
|
+
message: error instanceof Error ? error.message : String(error)
|
|
6266
|
+
});
|
|
5672
6267
|
return null;
|
|
5673
6268
|
} finally {
|
|
5674
6269
|
db.close();
|
|
@@ -5711,6 +6306,12 @@ function createCacheTables(db) {
|
|
|
5711
6306
|
index_version TEXT NOT NULL,
|
|
5712
6307
|
last_sync_at INTEGER NOT NULL
|
|
5713
6308
|
);
|
|
6309
|
+
|
|
6310
|
+
CREATE TABLE IF NOT EXISTS pending_reindex (
|
|
6311
|
+
agent_name TEXT NOT NULL,
|
|
6312
|
+
session_id TEXT NOT NULL,
|
|
6313
|
+
PRIMARY KEY (agent_name, session_id)
|
|
6314
|
+
);
|
|
5714
6315
|
`);
|
|
5715
6316
|
}
|
|
5716
6317
|
function createSessionTables(db) {
|
|
@@ -6408,6 +7009,20 @@ function invalidateSearchContentHashes(db) {
|
|
|
6408
7009
|
db.exec("UPDATE session_documents SET content_hash = ''");
|
|
6409
7010
|
}
|
|
6410
7011
|
}
|
|
7012
|
+
var CODEX_EXEC_DECODE_MIGRATION_KEY = "codex_exec_decode_migrated_v3";
|
|
7013
|
+
function migrateCodexExecDecode(db) {
|
|
7014
|
+
if (!tableExists(db, "cache_meta")) return;
|
|
7015
|
+
const done = db.prepare("SELECT value FROM cache_meta WHERE key = ?").get(CODEX_EXEC_DECODE_MIGRATION_KEY);
|
|
7016
|
+
if (done) return;
|
|
7017
|
+
if (tableExists(db, "sessions") && tableExists(db, "pending_reindex")) {
|
|
7018
|
+
db.exec(
|
|
7019
|
+
"INSERT OR IGNORE INTO pending_reindex(agent_name, session_id) SELECT agent_name, session_id FROM sessions WHERE agent_name = 'codex'"
|
|
7020
|
+
);
|
|
7021
|
+
}
|
|
7022
|
+
db.prepare(
|
|
7023
|
+
"INSERT INTO cache_meta(key, value) VALUES (?, '1') ON CONFLICT(key) DO UPDATE SET value = '1'"
|
|
7024
|
+
).run(CODEX_EXEC_DECODE_MIGRATION_KEY);
|
|
7025
|
+
}
|
|
6411
7026
|
function rebuildSearchIndex(db) {
|
|
6412
7027
|
if (!tableExists(db, "session_documents_fts")) {
|
|
6413
7028
|
return;
|
|
@@ -6465,6 +7080,7 @@ function ensureSchema(db, dbPath) {
|
|
|
6465
7080
|
if (currentVersion === 0 && !hasAnyCacheSchema(db)) {
|
|
6466
7081
|
createLatestCacheSchema(db);
|
|
6467
7082
|
setCacheSchemaVersion(db);
|
|
7083
|
+
migrateCodexExecDecode(db);
|
|
6468
7084
|
return;
|
|
6469
7085
|
}
|
|
6470
7086
|
runSchemaMigrations(db, {
|
|
@@ -6532,6 +7148,7 @@ function ensureSchema(db, dbPath) {
|
|
|
6532
7148
|
if (getUserVersion(db) <= CACHE_SCHEMA_VERSION) {
|
|
6533
7149
|
setCacheSchemaVersion(db);
|
|
6534
7150
|
}
|
|
7151
|
+
migrateCodexExecDecode(db);
|
|
6535
7152
|
}
|
|
6536
7153
|
function escapeFtsTerm(value) {
|
|
6537
7154
|
return value.replaceAll('"', '""');
|
|
@@ -6657,6 +7274,10 @@ function toFtsQuery(input) {
|
|
|
6657
7274
|
(token, index, values) => token !== "OR" || index > 0 && index < values.length - 1 && values[index - 1] !== "OR" && values[index + 1] !== "OR"
|
|
6658
7275
|
).join(" ");
|
|
6659
7276
|
}
|
|
7277
|
+
function readPendingReindexIds(db, agentName) {
|
|
7278
|
+
const rows = db.prepare("SELECT session_id FROM pending_reindex WHERE agent_name = ?").all(agentName);
|
|
7279
|
+
return new Set(rows.map((row) => String(row.session_id)));
|
|
7280
|
+
}
|
|
6660
7281
|
var SEARCH_INDEX_STATE_BATCH_SIZE = 900;
|
|
6661
7282
|
function shouldBulkSyncSearchIndex(options, changedCount) {
|
|
6662
7283
|
if (options.isBulk != null) {
|
|
@@ -6682,7 +7303,7 @@ function sessionContentHash(session) {
|
|
|
6682
7303
|
session.stats.total_tokens ?? 0
|
|
6683
7304
|
]);
|
|
6684
7305
|
}
|
|
6685
|
-
function searchIndexStateFromRows(indexedRows, messageCountRows) {
|
|
7306
|
+
function searchIndexStateFromRows(indexedRows, messageCountRows, pendingReindexSessionIds = /* @__PURE__ */ new Set()) {
|
|
6686
7307
|
return {
|
|
6687
7308
|
contentHashBySessionId: new Map(
|
|
6688
7309
|
indexedRows.map((row) => [String(row.session_id), String(row.content_hash ?? "")])
|
|
@@ -6692,7 +7313,8 @@ function searchIndexStateFromRows(indexedRows, messageCountRows) {
|
|
|
6692
7313
|
),
|
|
6693
7314
|
messageCountBySessionId: new Map(
|
|
6694
7315
|
messageCountRows.map((row) => [String(row.session_id), Number(row.value ?? 0)])
|
|
6695
|
-
)
|
|
7316
|
+
),
|
|
7317
|
+
pendingReindexSessionIds
|
|
6696
7318
|
};
|
|
6697
7319
|
}
|
|
6698
7320
|
function readSearchIndexState(db, agentName, sessionIds) {
|
|
@@ -6722,11 +7344,11 @@ function readSearchIndexState(db, agentName, sessionIds) {
|
|
|
6722
7344
|
).all(...batch, agentName, agentName);
|
|
6723
7345
|
rows.push(...batchRows);
|
|
6724
7346
|
}
|
|
6725
|
-
return searchIndexStateFromRows(rows, rows);
|
|
7347
|
+
return searchIndexStateFromRows(rows, rows, readPendingReindexIds(db, agentName));
|
|
6726
7348
|
}
|
|
6727
7349
|
function searchIndexEntryNeedsUpdate(state, session) {
|
|
6728
7350
|
const sessionId = session.id;
|
|
6729
|
-
return state.contentHashBySessionId.get(sessionId) !== sessionContentHash(session) || state.indexedMessageCountBySessionId.get(sessionId) !== (state.messageCountBySessionId.get(sessionId) ?? 0);
|
|
7351
|
+
return state.pendingReindexSessionIds.has(sessionId) || state.contentHashBySessionId.get(sessionId) !== sessionContentHash(session) || state.indexedMessageCountBySessionId.get(sessionId) !== (state.messageCountBySessionId.get(sessionId) ?? 0);
|
|
6730
7352
|
}
|
|
6731
7353
|
function loadSearchIndexEntry(agentName, change, loadSessionData) {
|
|
6732
7354
|
try {
|
|
@@ -6846,11 +7468,15 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
6846
7468
|
indexed_message_count = excluded.indexed_message_count,
|
|
6847
7469
|
indexed_at = excluded.indexed_at
|
|
6848
7470
|
`);
|
|
7471
|
+
const clearPendingReindex = db.prepare(
|
|
7472
|
+
"DELETE FROM pending_reindex WHERE agent_name = ? AND session_id = ?"
|
|
7473
|
+
);
|
|
6849
7474
|
for (const sessionId of new Set(removedSessionIds)) {
|
|
6850
7475
|
deleteRow.run(agentName, sessionId);
|
|
6851
7476
|
deleteFileActivity.run(agentName, sessionId);
|
|
6852
7477
|
deleteMessageTools.run(agentName, sessionId, 0);
|
|
6853
7478
|
deleteMessages.run(agentName, sessionId, 0);
|
|
7479
|
+
clearPendingReindex.run(agentName, sessionId);
|
|
6854
7480
|
}
|
|
6855
7481
|
let indexed = 0;
|
|
6856
7482
|
for (const entry of entries) {
|
|
@@ -6858,6 +7484,7 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
6858
7484
|
upsertSessionRow(upsertIndexedSession, agentName, entry.session, null, entry.sortIndex, null);
|
|
6859
7485
|
deleteFileActivity.run(agentName, entry.session.id);
|
|
6860
7486
|
deleteMessageTools.run(agentName, entry.session.id, 0);
|
|
7487
|
+
clearPendingReindex.run(agentName, entry.session.id);
|
|
6861
7488
|
writeFileActivityRows(insertFileActivity, entry.fileActivity);
|
|
6862
7489
|
for (const message of entry.messages) {
|
|
6863
7490
|
upsertMessage.run(
|
|
@@ -6918,7 +7545,11 @@ function syncSessionSearchIndex(agentName, sessions, loadSessionData, options =
|
|
|
6918
7545
|
const messageCountRows = db.prepare(
|
|
6919
7546
|
"SELECT session_id, COUNT(*) AS value FROM messages WHERE agent_name = ? GROUP BY session_id"
|
|
6920
7547
|
).all(agentName);
|
|
6921
|
-
const searchIndexState = searchIndexStateFromRows(
|
|
7548
|
+
const searchIndexState = searchIndexStateFromRows(
|
|
7549
|
+
existingRows,
|
|
7550
|
+
messageCountRows,
|
|
7551
|
+
readPendingReindexIds(db, agentName)
|
|
7552
|
+
);
|
|
6922
7553
|
const sessionMap = new Map(sessions.map((session) => [session.id, session]));
|
|
6923
7554
|
const toDelete = existingRows.map((row) => String(row.session_id)).filter((sessionId) => !sessionMap.has(sessionId));
|
|
6924
7555
|
const toUpsert = sessions.filter(
|
|
@@ -7523,6 +8154,14 @@ function searchFileActivitySessions(query, options = {}) {
|
|
|
7523
8154
|
return results;
|
|
7524
8155
|
}
|
|
7525
8156
|
var CACHE_INITIALIZATION_VERSION = "session-cache-v2";
|
|
8157
|
+
function parseCachedSessionMeta(value) {
|
|
8158
|
+
if (!value) return null;
|
|
8159
|
+
try {
|
|
8160
|
+
return JSON.parse(value);
|
|
8161
|
+
} catch {
|
|
8162
|
+
return null;
|
|
8163
|
+
}
|
|
8164
|
+
}
|
|
7526
8165
|
function deleteLegacyCacheFile() {
|
|
7527
8166
|
const legacyPath = getLegacyCachePath();
|
|
7528
8167
|
if (!existsSync12(legacyPath)) {
|
|
@@ -7641,7 +8280,7 @@ function markAgentFullSyncCompleted(agentName) {
|
|
|
7641
8280
|
).run(Date.now(), agentName);
|
|
7642
8281
|
});
|
|
7643
8282
|
}
|
|
7644
|
-
function
|
|
8283
|
+
function loadCachedSessionDataEntry(agentName, sessionId) {
|
|
7645
8284
|
if (!hasCacheStorage()) {
|
|
7646
8285
|
return null;
|
|
7647
8286
|
}
|
|
@@ -7679,7 +8318,8 @@ function loadCachedSessionData(agentName, sessionId) {
|
|
|
7679
8318
|
if (!row) {
|
|
7680
8319
|
return null;
|
|
7681
8320
|
}
|
|
7682
|
-
const
|
|
8321
|
+
const pendingReindex = db.prepare("SELECT 1 FROM pending_reindex WHERE agent_name = ? AND session_id = ?").get(agentName, sessionId) != null;
|
|
8322
|
+
const messageRows = pendingReindex ? [] : db.prepare(
|
|
7683
8323
|
`
|
|
7684
8324
|
SELECT
|
|
7685
8325
|
message_id,
|
|
@@ -7712,12 +8352,18 @@ function loadCachedSessionData(agentName, sessionId) {
|
|
|
7712
8352
|
`
|
|
7713
8353
|
).all(agentName, sessionId);
|
|
7714
8354
|
return {
|
|
7715
|
-
|
|
7716
|
-
|
|
7717
|
-
|
|
8355
|
+
data: {
|
|
8356
|
+
...head,
|
|
8357
|
+
messages: messageRows.map((messageRow) => messageFromCachedRow(messageRow)),
|
|
8358
|
+
file_activity: fileActivityRows.map((activityRow) => fileActivityFromRow(activityRow))
|
|
8359
|
+
},
|
|
8360
|
+
meta: parseCachedSessionMeta(row.meta_json)
|
|
7718
8361
|
};
|
|
7719
8362
|
});
|
|
7720
8363
|
}
|
|
8364
|
+
function loadCachedSessionData(agentName, sessionId) {
|
|
8365
|
+
return loadCachedSessionDataEntry(agentName, sessionId)?.data ?? null;
|
|
8366
|
+
}
|
|
7721
8367
|
function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
7722
8368
|
withCacheDb((db) => {
|
|
7723
8369
|
const deleteAgent = db.prepare("DELETE FROM agent_cache WHERE agent_name = ?");
|
|
@@ -7907,9 +8553,8 @@ function listCachedProjectGroups(sessions) {
|
|
|
7907
8553
|
if (!hasCacheStorage()) {
|
|
7908
8554
|
return [];
|
|
7909
8555
|
}
|
|
7910
|
-
const
|
|
7911
|
-
|
|
7912
|
-
`
|
|
8556
|
+
const queryRows = (db) => db.prepare(
|
|
8557
|
+
`
|
|
7913
8558
|
SELECT identity_kind, identity_key, display_name, sources_csv, session_count, last_activity
|
|
7914
8559
|
FROM project_groups_v
|
|
7915
8560
|
ORDER BY
|
|
@@ -7917,34 +8562,24 @@ function listCachedProjectGroups(sessions) {
|
|
|
7917
8562
|
last_activity IS NULL,
|
|
7918
8563
|
last_activity DESC
|
|
7919
8564
|
`
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
const cache = /* @__PURE__ */ new Map();
|
|
7934
|
-
return (directory) => {
|
|
7935
|
-
const key = directory || "";
|
|
7936
|
-
const cached = cache.get(key);
|
|
7937
|
-
if (cached) return cached;
|
|
7938
|
-
const identity = computeIdentity(directory, realFs);
|
|
7939
|
-
cache.set(key, identity);
|
|
7940
|
-
return identity;
|
|
7941
|
-
};
|
|
8565
|
+
).all();
|
|
8566
|
+
let rows = withCacheDbReadOnly(queryRows);
|
|
8567
|
+
if (rows == null) {
|
|
8568
|
+
rows = withCacheDb(queryRows);
|
|
8569
|
+
}
|
|
8570
|
+
return (rows ?? []).map((row) => ({
|
|
8571
|
+
identityKind: row.identity_kind ?? "path",
|
|
8572
|
+
identityKey: String(row.identity_key ?? ""),
|
|
8573
|
+
displayName: String(row.display_name ?? ""),
|
|
8574
|
+
sources: String(row.sources_csv ?? "").split(",").filter(Boolean).sort(),
|
|
8575
|
+
sessionCount: Number(row.session_count ?? 0),
|
|
8576
|
+
lastActivity: row.last_activity == null ? null : Number(row.last_activity)
|
|
8577
|
+
}));
|
|
7942
8578
|
}
|
|
7943
8579
|
function attachMissingProjectIdentities(sessions) {
|
|
7944
|
-
const resolveIdentity = createIdentityResolver();
|
|
7945
8580
|
return sessions.map((session) => {
|
|
7946
8581
|
if (session.project_identity) return session;
|
|
7947
|
-
return { ...session, project_identity:
|
|
8582
|
+
return { ...session, project_identity: computeIdentity(session.directory, realFs) };
|
|
7948
8583
|
});
|
|
7949
8584
|
}
|
|
7950
8585
|
function buildAgentCacheMeta(agent, sessionIds) {
|
|
@@ -7974,7 +8609,7 @@ function sessionSignature(session) {
|
|
|
7974
8609
|
function sortSessions(sessions) {
|
|
7975
8610
|
return sortSessionsByActivity(sessions);
|
|
7976
8611
|
}
|
|
7977
|
-
function computeSessionDiff(cachedSessions, updatedSessions, changedIds = [], signature = sessionSignature) {
|
|
8612
|
+
function computeSessionDiff(cachedSessions, updatedSessions, changedIds = [], signature = sessionSignature, signatureCache) {
|
|
7978
8613
|
const cachedMap = new Map(cachedSessions.map((session) => [session.id, session]));
|
|
7979
8614
|
const updatedIds = new Set(updatedSessions.map((session) => session.id));
|
|
7980
8615
|
const changedIdSet = new Set(changedIds);
|
|
@@ -7987,10 +8622,13 @@ function computeSessionDiff(cachedSessions, updatedSessions, changedIds = [], si
|
|
|
7987
8622
|
if (!cached) {
|
|
7988
8623
|
newCount += 1;
|
|
7989
8624
|
changes.push({ session, sortIndex });
|
|
8625
|
+
signatureCache?.set(session.id, signature(session));
|
|
7990
8626
|
return;
|
|
7991
8627
|
}
|
|
7992
|
-
const
|
|
7993
|
-
|
|
8628
|
+
const cachedSignature = signatureCache?.get(cached.id) ?? signature(cached);
|
|
8629
|
+
const updatedSignature = signature(session);
|
|
8630
|
+
signatureCache?.set(session.id, updatedSignature);
|
|
8631
|
+
if (changedIdSet.has(session.id) || cachedSignature !== updatedSignature) {
|
|
7994
8632
|
updatedCount += 1;
|
|
7995
8633
|
changes.push({ session, sortIndex });
|
|
7996
8634
|
}
|
|
@@ -8208,7 +8846,7 @@ async function scanAgentSmart(agent, options, onProgress) {
|
|
|
8208
8846
|
});
|
|
8209
8847
|
const t2 = performance.now();
|
|
8210
8848
|
const updatedSessions = await Promise.resolve(
|
|
8211
|
-
agent.incrementalScan(cached.sessions, checkResult.changedIds || [])
|
|
8849
|
+
agent.incrementalScan(cached.sessions, checkResult.changedIds || [], checkResult.refs)
|
|
8212
8850
|
);
|
|
8213
8851
|
timing.scan = performance.now() - t2;
|
|
8214
8852
|
return finalizeAgentScan(agent, updatedSessions, {
|
|
@@ -8272,10 +8910,12 @@ async function scanAgentFull(agent, options, onProgress, timing = { total: 0 },
|
|
|
8272
8910
|
const tagged = options.includeSmartTags === false ? { sessions: headsWithIdentity, changed: false } : await ensureSessionTags(agent, headsWithIdentity, options.smartTagWorkerUrl);
|
|
8273
8911
|
timing.tags = performance.now() - t2;
|
|
8274
8912
|
const meta = buildAgentCacheMeta(agent);
|
|
8275
|
-
if (options.writeCache !== false
|
|
8276
|
-
|
|
8913
|
+
if (options.writeCache !== false) {
|
|
8914
|
+
if (options.from == null && options.to == null) {
|
|
8915
|
+
saveCachedSessions(agent.name, tagged.sessions, meta);
|
|
8916
|
+
markAgentFullSyncCompleted(agent.name);
|
|
8917
|
+
}
|
|
8277
8918
|
markAgentCacheInitialized(agent.name);
|
|
8278
|
-
markAgentFullSyncCompleted(agent.name);
|
|
8279
8919
|
}
|
|
8280
8920
|
onProgress?.({ agent: agent.name, phase: "complete", newCount: tagged.sessions.length });
|
|
8281
8921
|
const filtered = filterSessions(tagged.sessions, options);
|
|
@@ -8331,6 +8971,13 @@ async function scanSessionsAsync(options = {}, onProgress) {
|
|
|
8331
8971
|
var STATE_DB_FILENAME = "state.db";
|
|
8332
8972
|
var STATE_SCHEMA_VERSION = 2;
|
|
8333
8973
|
var MEMORY_STATE_STORE = "memory";
|
|
8974
|
+
var stateSchemaEnsuredPath = null;
|
|
8975
|
+
function getStateSchemaEnsuredPath() {
|
|
8976
|
+
return stateSchemaEnsuredPath;
|
|
8977
|
+
}
|
|
8978
|
+
function setStateSchemaEnsuredPath(path2) {
|
|
8979
|
+
stateSchemaEnsuredPath = path2;
|
|
8980
|
+
}
|
|
8334
8981
|
var StateStorageUnavailableError = class extends Error {
|
|
8335
8982
|
constructor() {
|
|
8336
8983
|
super("SQLite state database is unavailable");
|
|
@@ -8437,7 +9084,7 @@ function ensureSchema2(db, dbPath) {
|
|
|
8437
9084
|
{ version: 2, migrate: createSessionAliasesTable }
|
|
8438
9085
|
]
|
|
8439
9086
|
});
|
|
8440
|
-
if (currentVersion
|
|
9087
|
+
if (currentVersion < STATE_SCHEMA_VERSION) {
|
|
8441
9088
|
setStateSchemaVersion(db);
|
|
8442
9089
|
}
|
|
8443
9090
|
}
|
|
@@ -8446,7 +9093,10 @@ function withStateDb(fn) {
|
|
|
8446
9093
|
const db = openDb(statePath);
|
|
8447
9094
|
if (!db) throw new StateStorageUnavailableError();
|
|
8448
9095
|
try {
|
|
8449
|
-
|
|
9096
|
+
if (getStateSchemaEnsuredPath() !== statePath) {
|
|
9097
|
+
ensureSchema2(db, statePath);
|
|
9098
|
+
setStateSchemaEnsuredPath(statePath);
|
|
9099
|
+
}
|
|
8450
9100
|
return fn(db);
|
|
8451
9101
|
} finally {
|
|
8452
9102
|
db.close();
|
|
@@ -8847,6 +9497,7 @@ function buildDashboard(sessions, options) {
|
|
|
8847
9497
|
name,
|
|
8848
9498
|
displayName: info?.displayName ?? name,
|
|
8849
9499
|
icon: info?.icon ?? "",
|
|
9500
|
+
iconColored: info?.iconColored,
|
|
8850
9501
|
sessions: metrics.sessions,
|
|
8851
9502
|
messages: metrics.messages,
|
|
8852
9503
|
tokens: metrics.tokens
|
|
@@ -8887,15 +9538,6 @@ function executeSessionSearch(query, options, snapshot) {
|
|
|
8887
9538
|
function needsIndexedSearch(textQuery, options) {
|
|
8888
9539
|
return Boolean(textQuery || options.file || options.fileKind || options.tools?.length);
|
|
8889
9540
|
}
|
|
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
9541
|
function matchesRecentSearchFilters(session, options, projectScope) {
|
|
8900
9542
|
if (options.projectKind || options.projectKey) {
|
|
8901
9543
|
if (!options.projectKind || !options.projectKey || !matchesProjectIdentity(session.project_identity, {
|
|
@@ -8921,11 +9563,18 @@ function matchesRecentSearchFilters(session, options, projectScope) {
|
|
|
8921
9563
|
if (!sessionMatchesSearchCost(session, options)) return false;
|
|
8922
9564
|
return true;
|
|
8923
9565
|
}
|
|
9566
|
+
function matchesSessionSearchFilters(agentName, session, options, projectScope = null) {
|
|
9567
|
+
if (options.agent && agentName !== options.agent) return false;
|
|
9568
|
+
const activity = getSessionActivityTime(session);
|
|
9569
|
+
if (options.from != null && activity < options.from) return false;
|
|
9570
|
+
if (options.to != null && activity > options.to) return false;
|
|
9571
|
+
return matchesRecentSearchFilters(session, options, projectScope);
|
|
9572
|
+
}
|
|
8924
9573
|
function searchRecentSessions(snapshot, options) {
|
|
8925
9574
|
const projectScope = options.cwd ? createProjectScopeMatcher(options.cwd) : null;
|
|
8926
9575
|
const entries = options.agent ? [[options.agent, snapshot.byAgent[options.agent] ?? []]] : Object.entries(snapshot.byAgent);
|
|
8927
9576
|
return entries.flatMap(
|
|
8928
|
-
([agentName, sessions]) =>
|
|
9577
|
+
([agentName, sessions]) => sessions.filter((session) => matchesSessionSearchFilters(agentName, session, options, projectScope)).map((session) => ({ agentName, session }))
|
|
8929
9578
|
).sort(
|
|
8930
9579
|
(a, b) => (b.session.time_updated ?? b.session.time_created) - (a.session.time_updated ?? a.session.time_created)
|
|
8931
9580
|
).slice(0, options.limit ?? 50).map(({ agentName, session }) => ({
|
|
@@ -8968,6 +9617,7 @@ export {
|
|
|
8968
9617
|
getRegisteredAgents,
|
|
8969
9618
|
getAgentInfoMap,
|
|
8970
9619
|
getAgentByName,
|
|
9620
|
+
setCoreDiagnostics,
|
|
8971
9621
|
parsedSession,
|
|
8972
9622
|
skippedSession,
|
|
8973
9623
|
filteredSession,
|
|
@@ -9001,6 +9651,11 @@ export {
|
|
|
9001
9651
|
applyMessageCosts,
|
|
9002
9652
|
withEstimatedSessionCost,
|
|
9003
9653
|
estimateTokenCost,
|
|
9654
|
+
asRecord,
|
|
9655
|
+
asString,
|
|
9656
|
+
asNumber,
|
|
9657
|
+
asArray,
|
|
9658
|
+
reportFieldMismatch,
|
|
9004
9659
|
openDbReadOnly,
|
|
9005
9660
|
openDb,
|
|
9006
9661
|
isSqliteAvailable,
|
|
@@ -9011,6 +9666,7 @@ export {
|
|
|
9011
9666
|
getProjectIdentityKey,
|
|
9012
9667
|
matchesProjectIdentity,
|
|
9013
9668
|
normalizeGitRemote,
|
|
9669
|
+
clearIdentityCache,
|
|
9014
9670
|
computeIdentity,
|
|
9015
9671
|
buildProjectGroups,
|
|
9016
9672
|
createProjectScopeMatcher,
|
|
@@ -9036,6 +9692,7 @@ export {
|
|
|
9036
9692
|
markAgentCacheInitialized,
|
|
9037
9693
|
getAgentLastFullSyncAt,
|
|
9038
9694
|
markAgentFullSyncCompleted,
|
|
9695
|
+
loadCachedSessionDataEntry,
|
|
9039
9696
|
loadCachedSessionData,
|
|
9040
9697
|
saveCachedSessions,
|
|
9041
9698
|
saveCachedSessionChanges,
|
|
@@ -9068,6 +9725,7 @@ export {
|
|
|
9068
9725
|
toLocalDateKey,
|
|
9069
9726
|
startOfLocalDay,
|
|
9070
9727
|
buildDashboard,
|
|
9071
|
-
executeSessionSearch
|
|
9728
|
+
executeSessionSearch,
|
|
9729
|
+
matchesSessionSearchFilters
|
|
9072
9730
|
};
|
|
9073
|
-
//# sourceMappingURL=chunk-
|
|
9731
|
+
//# sourceMappingURL=chunk-MWSJTNOW.js.map
|