codesesh 0.15.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.
@@ -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 = String(data["timestamp"] ?? "").trim();
973
- if (!raw) return 0;
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(raw.includes("Z") ? raw : raw + "Z").getTime();
1057
+ return new Date(trimmed.includes("Z") ? trimmed : trimmed + "Z").getTime();
976
1058
  } catch {
977
1059
  return 0;
978
1060
  }
979
1061
  }
980
- function numericUsage(value) {
981
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
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 usage = msg["usage"];
985
- if (!usage || typeof usage !== "object") return null;
986
- const u = usage;
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: numericUsage(u["input_tokens"]),
994
- output: numericUsage(u["output_tokens"]),
995
- cacheRead: numericUsage(u["cache_read_input_tokens"]),
996
- cacheCreate: numericUsage(u["cache_creation_input_tokens"])
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
- if (!matchesScanWindow(statSync2(file).mtimeMs, options)) continue;
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(file, projectDir)
1134
+ fingerprint: this.sourceFingerprint(stat, indexPath)
1039
1135
  });
1040
1136
  }
1041
1137
  }
@@ -1098,12 +1194,13 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1098
1194
  }
1099
1195
  buildSessionMeta(head, file, projectDir) {
1100
1196
  const indexPath = this.getSessionsIndexPath(projectDir);
1197
+ const stat = statSync2(file);
1101
1198
  return {
1102
1199
  id: head.id,
1103
1200
  title: head.title,
1104
1201
  sourcePath: file,
1105
- sourceFingerprint: this.sourceFingerprint(file, projectDir),
1106
- sourceMtimeMs: statSync2(file).mtimeMs,
1202
+ sourceFingerprint: this.sourceFingerprint(stat, indexPath),
1203
+ sourceMtimeMs: stat.mtimeMs,
1107
1204
  indexPath: existsSync4(indexPath) ? indexPath : null,
1108
1205
  indexMtimeMs: this.getFileMtimeMs(indexPath),
1109
1206
  headIndexVersion: HEAD_INDEX_VERSION,
@@ -1114,9 +1211,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1114
1211
  updatedAt: head.time_updated ?? head.time_created
1115
1212
  };
1116
1213
  }
1117
- sourceFingerprint(file, projectDir) {
1118
- const stat = statSync2(file);
1119
- const indexPath = this.getSessionsIndexPath(projectDir);
1214
+ /** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
1215
+ sourceFingerprint(stat, indexPath) {
1120
1216
  return JSON.stringify([
1121
1217
  HEAD_INDEX_VERSION,
1122
1218
  stat.mtimeMs,
@@ -1188,7 +1284,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1188
1284
  let totalCost = 0;
1189
1285
  const modelUsageMap = {};
1190
1286
  const countedUsageKeys = /* @__PURE__ */ new Set();
1191
- for (const line of lines) {
1287
+ let messageTitle = null;
1288
+ for (const [lineIndex, line] of lines.entries()) {
1192
1289
  try {
1193
1290
  const data = JSON.parse(line);
1194
1291
  if (isInternalEventType(data["type"])) continue;
@@ -1197,15 +1294,25 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1197
1294
  if (!cwd && data["cwd"] && typeof data["cwd"] === "string") {
1198
1295
  cwd = data["cwd"];
1199
1296
  }
1200
- const msg = data["message"];
1201
- if (msg && typeof msg === "object") {
1202
- const role = msg["role"];
1203
- if (typeof role === "string" && role.trim()) {
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()) {
1204
1307
  messageCount++;
1205
1308
  }
1206
1309
  if (!model) {
1207
- const m = msg["model"];
1208
- if (typeof m === "string" && m.trim()) model = m.trim();
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;
1209
1316
  }
1210
1317
  if (role === "assistant") {
1211
1318
  const usage = extractClaudeUsage(data, msg);
@@ -1219,8 +1326,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1219
1326
  totalOutputTokens += outputTokens;
1220
1327
  totalCacheReadTokens += cacheRead;
1221
1328
  totalCacheCreateTokens += cacheCreate;
1222
- const m = msg["model"];
1223
- if (typeof m === "string" && m.trim()) {
1329
+ const m = asString(msg["model"]);
1330
+ if (m?.trim()) {
1224
1331
  const name = m.trim();
1225
1332
  const msgTotal = inputTokens + cacheRead + cacheCreate + outputTokens;
1226
1333
  modelUsageMap[name] = (modelUsageMap[name] ?? 0) + msgTotal;
@@ -1239,7 +1346,6 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1239
1346
  }
1240
1347
  }
1241
1348
  const directory = cwd ?? projectDir;
1242
- const messageTitle = this.extractTitle(lines);
1243
1349
  const directoryTitle = basenameTitle(directory) || basenameTitle(projectDir);
1244
1350
  const title = resolveSessionTitle(explicitTitle, messageTitle, directoryTitle);
1245
1351
  const hasModelUsage = Object.keys(modelUsageMap).length > 0;
@@ -1263,27 +1369,20 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1263
1369
  model_usage: hasModelUsage ? modelUsageMap : void 0
1264
1370
  });
1265
1371
  }
1266
- extractTitle(lines) {
1267
- for (const line of lines.slice(0, 20)) {
1268
- try {
1269
- const data = JSON.parse(line);
1270
- if (isInternalEventType(data["type"])) continue;
1271
- const msg = data["message"];
1272
- if (!msg || typeof msg !== "object") continue;
1273
- if (msg["role"] !== "user") continue;
1274
- const content = msg["content"];
1275
- if (!content) continue;
1276
- if (typeof content === "string") {
1277
- const title = normalizeTitleText(content);
1278
- if (title) return title;
1279
- }
1280
- if (Array.isArray(content)) {
1281
- const texts = content.filter((item) => typeof item === "object" && item !== null && "text" in item).map((item) => String(item["text"] ?? "")).join(" ");
1282
- const title = normalizeTitleText(texts);
1283
- if (title) return title;
1284
- }
1285
- } catch {
1286
- }
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;
1287
1386
  }
1288
1387
  return null;
1289
1388
  }
@@ -1301,56 +1400,54 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1301
1400
  }
1302
1401
  }
1303
1402
  convertAssistantRecord(data, builder, assistantUuidToToolCalls, countedUsageKeys) {
1304
- const msg = data["message"] ?? {};
1403
+ const msg = asRecord(data["message"]) ?? {};
1305
1404
  const timestampMs = parseTimestampMs(data);
1306
- const rawContent = msg["content"] ?? [];
1405
+ const rawContent = asArray(msg["content"]) ?? [];
1307
1406
  const uuid = String(data["uuid"] ?? "");
1308
1407
  const toolCallIds = [];
1309
- if (Array.isArray(rawContent)) {
1310
- for (const item of rawContent) {
1311
- if (!item || typeof item !== "object") continue;
1312
- const part = item;
1313
- const partType = String(part["type"] ?? "");
1314
- if (partType === "thinking") {
1315
- const text = cleanInternalText(String(part["thinking"] ?? ""));
1316
- if (text) {
1317
- const message2 = builder.appendAssistantPart(
1318
- this.buildReasoningPart(text, timestampMs),
1319
- { id: uuid, timestampMs, agent: "claude" },
1320
- { deduplicateTail: true }
1321
- );
1322
- this.applyAssistantMetadata(message2, data, msg, countedUsageKeys);
1323
- }
1324
- continue;
1325
- }
1326
- if (partType === "text") {
1327
- const text = cleanInternalText(String(part["text"] ?? ""));
1328
- if (text) {
1329
- const message2 = builder.appendAssistantPart(
1330
- this.buildTextPart(text, timestampMs),
1331
- {
1332
- id: uuid,
1333
- timestampMs,
1334
- agent: "claude"
1335
- },
1336
- { deduplicateTail: true }
1337
- );
1338
- this.applyAssistantMetadata(message2, data, msg, countedUsageKeys);
1339
- }
1340
- 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);
1341
1421
  }
1342
- if (partType !== "tool_use") continue;
1343
- const toolCallId = String(part["id"] ?? "").trim();
1344
- const toolPart = this.buildToolPart(part, timestampMs);
1345
- const message = builder.appendToolCall(
1346
- toolPart,
1347
- { id: uuid, timestampMs, agent: "claude" },
1348
- { modeOnCreate: "tool" }
1349
- );
1350
- this.applyAssistantMetadata(message, data, msg, countedUsageKeys);
1351
- if (toolCallId) {
1352
- toolCallIds.push(toolCallId);
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);
1353
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);
1354
1451
  }
1355
1452
  }
1356
1453
  if (toolCallIds.length > 0) {
@@ -1358,7 +1455,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1358
1455
  }
1359
1456
  }
1360
1457
  convertUserRecord(data, builder, assistantUuidToToolCalls) {
1361
- const msg = data["message"] ?? {};
1458
+ const msg = asRecord(data["message"]) ?? {};
1362
1459
  const timestampMs = parseTimestampMs(data);
1363
1460
  const content = msg["content"] ?? "";
1364
1461
  const uuid = String(data["uuid"] ?? "");
@@ -1378,9 +1475,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1378
1475
  const visibleParts = this.normalizeUserTextParts(content, timestampMs);
1379
1476
  const toolStateUpdates = this.extractToolStateUpdates(data["toolUseResult"]);
1380
1477
  for (const item of content) {
1381
- if (!item || typeof item !== "object") continue;
1382
- const ci = item;
1383
- if (ci["type"] !== "tool_result") continue;
1478
+ const ci = asRecord(item);
1479
+ if (!ci || ci["type"] !== "tool_result") continue;
1384
1480
  const toolCallId = this.resolveToolCallId(data, ci, assistantUuidToToolCalls);
1385
1481
  const outputParts = this.normalizeClaudeToolOutput(ci["content"], timestampMs);
1386
1482
  if (this.backfillToolOutput(builder, toolCallId, outputParts, toolStateUpdates)) {
@@ -1401,7 +1497,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1401
1497
  }
1402
1498
  convertToolResultRecord(data, builder) {
1403
1499
  const timestampMs = parseTimestampMs(data);
1404
- const msg = data["message"] ?? {};
1500
+ const msg = asRecord(data["message"]) ?? {};
1405
1501
  const outputParts = this.normalizeClaudeToolOutput(msg["content"], timestampMs);
1406
1502
  const uuid = String(data["uuid"] ?? "");
1407
1503
  const fallback = this.buildFallbackToolMessage({
@@ -1463,8 +1559,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1463
1559
  if (!Array.isArray(content)) return [];
1464
1560
  const parts = [];
1465
1561
  for (const item of content) {
1466
- if (typeof item === "object" && item !== null) {
1467
- const ci = item;
1562
+ const ci = asRecord(item);
1563
+ if (ci) {
1468
1564
  if (ci["type"] === "tool_result") continue;
1469
1565
  const text = cleanInternalText(String(ci["text"] ?? ""));
1470
1566
  if (text) parts.push(this.buildTextPart(text, timestampMs));
@@ -1484,12 +1580,13 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1484
1580
  if (Array.isArray(content)) {
1485
1581
  const parts = [];
1486
1582
  for (const item of content) {
1487
- if (typeof item === "object" && item !== null) {
1488
- const itemRecord = item;
1489
- const source = itemRecord["source"];
1490
- if (itemRecord["type"] === "image" && source) {
1491
- const data = typeof source["data"] === "string" ? source["data"] : "";
1492
- const mimeType = typeof source["media_type"] === "string" ? source["media_type"] : "";
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"]) ?? "";
1493
1590
  if (data && mimeType.startsWith("image/")) {
1494
1591
  parts.push({ type: "image", data, mime_type: mimeType, time_created: timestampMs });
1495
1592
  }
@@ -1533,8 +1630,8 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1533
1630
  return "";
1534
1631
  }
1535
1632
  extractToolStateUpdates(toolUseResult) {
1536
- if (!toolUseResult || typeof toolUseResult !== "object") return {};
1537
- const result = toolUseResult;
1633
+ const result = asRecord(toolUseResult);
1634
+ if (!result) return {};
1538
1635
  const updates = {};
1539
1636
  const success = result["success"];
1540
1637
  if (typeof success === "boolean") {
@@ -1558,11 +1655,19 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
1558
1655
  }
1559
1656
  };
1560
1657
  var DatabaseConstructor = null;
1658
+ var loadErrorMessage = null;
1561
1659
  try {
1562
1660
  const require2 = createRequire(import.meta.url);
1563
1661
  const mod = require2("better-sqlite3");
1564
1662
  DatabaseConstructor = typeof mod === "function" ? mod : mod.default;
1565
- } 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 });
1566
1671
  }
1567
1672
  function quoteIdentifier(value) {
1568
1673
  return `"${value.replaceAll('"', '""')}"`;
@@ -1657,16 +1762,27 @@ function runSchemaMigrations(db, options) {
1657
1762
  return backups;
1658
1763
  }
1659
1764
  function openDbReadOnly(dbPath) {
1660
- if (!DatabaseConstructor) return null;
1765
+ if (!DatabaseConstructor) {
1766
+ reportUnavailableOnce();
1767
+ return null;
1768
+ }
1661
1769
  try {
1662
1770
  const db = DatabaseConstructor(dbPath, { readonly: true });
1663
1771
  return db;
1664
- } 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
+ });
1665
1778
  return null;
1666
1779
  }
1667
1780
  }
1668
1781
  function openDb(dbPath) {
1669
- if (!DatabaseConstructor) return null;
1782
+ if (!DatabaseConstructor) {
1783
+ reportUnavailableOnce();
1784
+ return null;
1785
+ }
1670
1786
  try {
1671
1787
  mkdirSync2(dirname2(dbPath), { recursive: true });
1672
1788
  const db = DatabaseConstructor(dbPath);
@@ -1674,16 +1790,42 @@ function openDb(dbPath) {
1674
1790
  db.pragma("journal_mode = WAL");
1675
1791
  db.pragma("synchronous = NORMAL");
1676
1792
  db.pragma("foreign_keys = ON");
1793
+ db.pragma("busy_timeout = 5000");
1677
1794
  } catch {
1678
1795
  }
1679
1796
  return db;
1680
- } 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
+ });
1681
1803
  return null;
1682
1804
  }
1683
1805
  }
1684
1806
  function isSqliteAvailable() {
1685
1807
  return DatabaseConstructor !== null;
1686
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
+ }
1687
1829
  var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
1688
1830
  constructor(config) {
1689
1831
  super();
@@ -1815,7 +1957,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
1815
1957
  ).all(cutoffTime);
1816
1958
  }
1817
1959
  parsePartRow(partRow) {
1818
- const partData = JSON.parse(String(partRow.data ?? "{}"));
1960
+ const partData = parseJsonRecord(partRow.data, this.name, "part.data");
1819
1961
  const partType = String(partData.type ?? "");
1820
1962
  if (isInternalEventType(partType)) return null;
1821
1963
  if (partType === "text" || partType === "reasoning") {
@@ -1833,7 +1975,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
1833
1975
  tool: String(partData.tool ?? ""),
1834
1976
  callID: String(partData.callID ?? ""),
1835
1977
  title: cleanInternalText(String(partData.title ?? "")),
1836
- state: partData.state ?? {},
1978
+ state: asRecord(partData.state) ?? {},
1837
1979
  time_created: Number(partRow.time_created ?? 0)
1838
1980
  };
1839
1981
  }
@@ -1865,7 +2007,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
1865
2007
  for (const row of messageRows) {
1866
2008
  const sessionId = String(row.session_id ?? "");
1867
2009
  if (!sessionId) continue;
1868
- const msgData = JSON.parse(String(row.data ?? "{}"));
2010
+ const msgData = parseJsonRecord(row.data, this.name, "message.data");
1869
2011
  if (isInternalEventType(msgData.type)) continue;
1870
2012
  const parts = partsByMessage.get(String(row.id ?? "")) ?? [];
1871
2013
  if (parts.length === 0) continue;
@@ -1883,10 +2025,10 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
1883
2025
  contexts.set(sessionId, context);
1884
2026
  }
1885
2027
  const cost = Number(msgData.cost ?? 0);
1886
- const tokens = msgData.tokens;
2028
+ const tokens = parseTokens(msgData.tokens, this.name);
1887
2029
  const inputTokens = Number(tokens?.input ?? 0);
1888
2030
  const outputTokens = Number(tokens?.output ?? 0);
1889
- const model = msgData.modelID ?? null;
2031
+ const model = parseModel(msgData.modelID, this.name);
1890
2032
  const estimatedCost = cost > 0 ? null : estimateTokenCost(model, { input: inputTokens, output: outputTokens });
1891
2033
  if (estimatedCost !== null) context.stats.cost_source = "estimated";
1892
2034
  context.stats.total_cost += cost || estimatedCost || 0;
@@ -1940,24 +2082,24 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
1940
2082
  let hasEstimatedCost = false;
1941
2083
  const msgRows = db.prepare("SELECT * FROM message WHERE session_id = ? ORDER BY time_created ASC").all(sessionId);
1942
2084
  for (const msgRow of msgRows) {
1943
- const msgData = JSON.parse(String(msgRow.data ?? "{}"));
2085
+ const msgData = parseJsonRecord(msgRow.data, this.name, "message.data");
1944
2086
  if (isInternalEventType(msgData.type)) continue;
1945
2087
  const cost = Number(msgData.cost ?? 0);
1946
- const tokens = msgData.tokens;
2088
+ const tokens = parseTokens(msgData.tokens, this.name);
1947
2089
  const inputTokens = Number(tokens?.input ?? 0);
1948
2090
  const outputTokens = Number(tokens?.output ?? 0);
1949
- const model = msgData.modelID ?? null;
2091
+ const model = parseModel(msgData.modelID, this.name);
1950
2092
  const estimatedCost = cost > 0 ? null : estimateTokenCost(model, { input: inputTokens, output: outputTokens });
1951
2093
  const resolvedCost = cost || estimatedCost || 0;
1952
2094
  const parts = this.readMessageParts(db, msgRow.id);
1953
2095
  if (parts.length === 0) continue;
1954
2096
  messages.push({
1955
2097
  id: String(msgRow.id ?? ""),
1956
- role: String(msgData.role ?? "assistant"),
1957
- agent: msgData.agent ?? null,
1958
- mode: msgData.mode ?? null,
2098
+ role: parseMessageRole(msgData.role, this.name),
2099
+ agent: asString(msgData.agent) ?? null,
2100
+ mode: asString(msgData.mode) ?? null,
1959
2101
  model,
1960
- provider: msgData.providerID ?? null,
2102
+ provider: asString(msgData.providerID) ?? null,
1961
2103
  time_created: Number(msgRow.time_created ?? 0),
1962
2104
  tokens: tokens ? { input: inputTokens, output: outputTokens } : void 0,
1963
2105
  cost: resolvedCost,
@@ -1982,7 +2124,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
1982
2124
  title,
1983
2125
  slug,
1984
2126
  directory,
1985
- version: sessionRow.version ?? void 0,
2127
+ version: asString(sessionRow.version) ?? void 0,
1986
2128
  time_created: timeCreated,
1987
2129
  time_updated: timeUpdated,
1988
2130
  summary_files: sessionRow.summary_files ?? void 0,
@@ -2026,6 +2168,15 @@ var KIMI_IGNORED_TOOLS = /* @__PURE__ */ new Set(["SetTodoList"]);
2026
2168
  function mapToolTitle(toolName) {
2027
2169
  return KIMI_TOOL_TITLE_MAP[toolName] ?? toolName;
2028
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
+ }
2029
2180
  function normalizeToolArguments(raw) {
2030
2181
  if (typeof raw === "string") {
2031
2182
  try {
@@ -2044,8 +2195,9 @@ function normalizeToolOutputParts(content, timestampMs) {
2044
2195
  if (Array.isArray(content)) {
2045
2196
  const parts = [];
2046
2197
  for (const item of content) {
2047
- if (typeof item === "object" && item !== null && "text" in item) {
2048
- const text2 = String(item.text ?? "");
2198
+ const record = asRecord(item);
2199
+ if (record && "text" in record) {
2200
+ const text2 = String(record.text ?? "");
2049
2201
  const cleaned = cleanInternalText(text2);
2050
2202
  if (cleaned) parts.push({ type: "text", text: cleaned, time_created: timestampMs });
2051
2203
  } else if (typeof item === "string") {
@@ -2077,10 +2229,8 @@ function kimiContentText(content) {
2077
2229
  if (!Array.isArray(content)) return "";
2078
2230
  return content.map((item) => {
2079
2231
  if (typeof item === "string") return item;
2080
- if (typeof item === "object" && item !== null) {
2081
- const record = item;
2082
- return String(record.text ?? record.content ?? "");
2083
- }
2232
+ const record = asRecord(item);
2233
+ if (record) return String(record.text ?? record.content ?? "");
2084
2234
  return "";
2085
2235
  }).join(" ");
2086
2236
  }
@@ -2096,9 +2246,9 @@ function extractFirstUserTitle(contextFile, wireFile) {
2096
2246
  if (wireFile && existsSync6(wireFile)) {
2097
2247
  const content = readFileSync3(wireFile, "utf-8");
2098
2248
  for (const record of parseJsonlLines(content)) {
2099
- const message = record.message ?? {};
2249
+ const message = asRecord(record.message) ?? {};
2100
2250
  if (message.type !== "TurnBegin") continue;
2101
- const payload = message.payload ?? {};
2251
+ const payload = asRecord(message.payload) ?? {};
2102
2252
  const userInput = payload.user_input;
2103
2253
  if (!Array.isArray(userInput)) continue;
2104
2254
  const title = normalizeTitleText(kimiContentText(userInput));
@@ -2128,12 +2278,12 @@ var KimiAgent = class extends FileSystemSessionSource {
2128
2278
  }
2129
2279
  if (!existsSync6(configPath)) return;
2130
2280
  try {
2131
- const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
2132
- const workDirs = raw?.work_dirs;
2133
- if (!Array.isArray(workDirs)) return;
2281
+ const raw = asRecord(JSON.parse(readFileSync3(configPath, "utf-8")));
2282
+ const workDirs = asArray(raw?.work_dirs);
2283
+ if (!workDirs) return;
2134
2284
  for (const wd of workDirs) {
2135
- const path2 = wd.path;
2136
- if (typeof path2 !== "string") continue;
2285
+ const path2 = asString(asRecord(wd)?.path);
2286
+ if (!path2) continue;
2137
2287
  const hash = createHash("md5").update(path2).digest("hex");
2138
2288
  this.projectMap.set(hash, path2);
2139
2289
  }
@@ -2192,14 +2342,14 @@ var KimiAgent = class extends FileSystemSessionSource {
2192
2342
  let wireMtime = null;
2193
2343
  let metaFile = "";
2194
2344
  if (existsSync6(statePath)) {
2195
- const state = JSON.parse(readFileSync3(statePath, "utf-8"));
2345
+ const state = asRecord(JSON.parse(readFileSync3(statePath, "utf-8"))) ?? {};
2196
2346
  title = String(state.custom_title ?? "");
2197
- wireMtime = typeof state.wire_mtime === "number" ? state.wire_mtime : null;
2347
+ wireMtime = readWireMtime(state);
2198
2348
  metaFile = statePath;
2199
2349
  } else if (existsSync6(metaPath)) {
2200
- const meta = JSON.parse(readFileSync3(metaPath, "utf-8"));
2350
+ const meta = asRecord(JSON.parse(readFileSync3(metaPath, "utf-8"))) ?? {};
2201
2351
  title = String(meta.title ?? "");
2202
- wireMtime = typeof meta.wire_mtime === "number" ? meta.wire_mtime : null;
2352
+ wireMtime = readWireMtime(meta);
2203
2353
  metaFile = metaPath;
2204
2354
  }
2205
2355
  const cwd = this.projectMap.get(projectHash) || "";
@@ -2328,16 +2478,15 @@ var KimiAgent = class extends FileSystemSessionSource {
2328
2478
  for (const record of parseJsonlLines(content)) {
2329
2479
  seq++;
2330
2480
  try {
2331
- const message = record.message ?? {};
2332
- const msgType = String(message.type ?? "");
2481
+ const message = asRecord(record.message) ?? {};
2482
+ const msgType = asString(message.type) ?? "";
2333
2483
  if (isInternalEventType(msgType)) continue;
2334
- const payload = message.payload ?? {};
2335
- const timestamp = Number(record.timestamp ?? 0);
2336
- const timestampMs = Number.isFinite(timestamp) ? Math.floor(timestamp * 1e3) : 0;
2337
- const usage = message["usage"];
2338
- if (usage && typeof usage === "object") {
2339
- const inputTokens = Number(usage["input_tokens"] ?? 0);
2340
- 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");
2341
2490
  if (inputTokens || outputTokens) {
2342
2491
  const tokens = { input: inputTokens, output: outputTokens };
2343
2492
  const cost = estimateTokenCost(this.defaultModel, tokens);
@@ -2389,7 +2538,7 @@ var KimiAgent = class extends FileSystemSessionSource {
2389
2538
  continue;
2390
2539
  }
2391
2540
  if (msgType === "ToolCall") {
2392
- const function_ = payload.function;
2541
+ const function_ = asRecord(payload.function);
2393
2542
  const toolName = String(function_?.name ?? "").trim();
2394
2543
  const callId = String(payload.id ?? "").trim();
2395
2544
  if (toolName && callId && KIMI_IGNORED_TOOLS.has(toolName)) {
@@ -2475,8 +2624,8 @@ var KimiAgent = class extends FileSystemSessionSource {
2475
2624
  const content = record.content;
2476
2625
  if (Array.isArray(content)) {
2477
2626
  for (const item of content) {
2478
- if (typeof item !== "object" || item === null) continue;
2479
- const ci = item;
2627
+ const ci = asRecord(item);
2628
+ if (!ci) continue;
2480
2629
  const partType = String(ci.type ?? "");
2481
2630
  if (partType === "think") {
2482
2631
  const text = cleanInternalText(String(ci.think ?? ""));
@@ -2487,15 +2636,14 @@ var KimiAgent = class extends FileSystemSessionSource {
2487
2636
  }
2488
2637
  }
2489
2638
  }
2490
- const toolCalls = record.tool_calls;
2491
- if (Array.isArray(toolCalls)) {
2639
+ const toolCalls = asArray(record.tool_calls);
2640
+ if (toolCalls) {
2492
2641
  for (const tc of toolCalls) {
2493
- if (typeof tc !== "object" || tc === null) continue;
2494
- const tcRecord = tc;
2495
- const function_ = tcRecord.function;
2642
+ const tcRecord = asRecord(tc);
2643
+ const function_ = asRecord(tcRecord?.function);
2496
2644
  if (!function_) continue;
2497
2645
  const toolName = String(function_.name ?? "").trim();
2498
- const callId = String(tcRecord.id ?? "").trim();
2646
+ const callId = String(tcRecord?.id ?? "").trim();
2499
2647
  if (toolName && callId && KIMI_IGNORED_TOOLS.has(toolName)) {
2500
2648
  ignoredToolCallIds.add(callId);
2501
2649
  continue;
@@ -2560,11 +2708,11 @@ var KimiAgent = class extends FileSystemSessionSource {
2560
2708
  const content = readFileSync3(wirePath, "utf-8");
2561
2709
  for (const line of content.split("\n").filter((l) => l.trim())) {
2562
2710
  try {
2563
- const data = JSON.parse(line);
2564
- const tokenUsage = data.message?.usage;
2711
+ const data = asRecord(JSON.parse(line));
2712
+ const tokenUsage = asRecord(asRecord(data?.message)?.usage);
2565
2713
  if (!tokenUsage) continue;
2566
- const inputTokens = Number(tokenUsage.input_tokens ?? 0);
2567
- const outputTokens = Number(tokenUsage.output_tokens ?? 0);
2714
+ const inputTokens = extractTokenField(tokenUsage, "input_tokens");
2715
+ const outputTokens = extractTokenField(tokenUsage, "output_tokens");
2568
2716
  stats.total_input_tokens += inputTokens;
2569
2717
  stats.total_output_tokens += outputTokens;
2570
2718
  const cost = estimateTokenCost(this.defaultModel, {
@@ -2584,10 +2732,14 @@ var KimiAgent = class extends FileSystemSessionSource {
2584
2732
  const rawContent = readFileSync3(rawPath, "utf-8");
2585
2733
  for (const line of rawContent.split("\n").filter((l) => l.trim())) {
2586
2734
  try {
2587
- const data = JSON.parse(line);
2588
- if (data.role === "_usage" && typeof data.token_count === "number") {
2589
- stats.total_tokens = data.token_count;
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;
2590
2741
  }
2742
+ stats.total_tokens = tokenCount;
2591
2743
  } catch {
2592
2744
  }
2593
2745
  }
@@ -2917,6 +3069,19 @@ function extractCachedInputTokens(usage) {
2917
3069
  if (!usage) return 0;
2918
3070
  return Number(usage["cached_input_tokens"] ?? usage["cache_read_input_tokens"] ?? 0);
2919
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
+ }
2920
3085
  function resolveToolIdentity(name, namespace) {
2921
3086
  const mappedName = CODEX_TOOL_TITLE_MAP[name];
2922
3087
  if (mappedName) return { tool: mappedName };
@@ -2956,11 +3121,8 @@ function flattenOutputText(output) {
2956
3121
  if (Array.isArray(output)) {
2957
3122
  return output.map((item) => {
2958
3123
  if (typeof item === "string") return item;
2959
- if (item && typeof item === "object") {
2960
- const text = item["text"];
2961
- if (typeof text === "string") return text;
2962
- }
2963
- return "";
3124
+ const record = asRecord(item);
3125
+ return record ? asString(record["text"]) ?? "" : "";
2964
3126
  }).join("");
2965
3127
  }
2966
3128
  return "";
@@ -3071,10 +3233,10 @@ var CodexAgent = class extends FileSystemSessionSource {
3071
3233
  listSessionSources(options) {
3072
3234
  if (!this.basePath) return [];
3073
3235
  this.loadSessionIndex();
3074
- return this.listRolloutFiles(options).map((file) => ({
3236
+ return this.listRolloutFiles(options).map(({ file, stat }) => ({
3075
3237
  sessionId: extractSessionId(file),
3076
3238
  sourcePath: file,
3077
- fingerprint: this.sourceFingerprint(file)
3239
+ fingerprint: this.sourceFingerprint(file, stat)
3078
3240
  }));
3079
3241
  }
3080
3242
  scanSessionSource(sourcePath, options) {
@@ -3105,20 +3267,18 @@ var CodexAgent = class extends FileSystemSessionSource {
3105
3267
  try {
3106
3268
  const recordType = String(record["type"] ?? "");
3107
3269
  if (recordType === "turn_context") {
3108
- const payload = record["payload"] ?? {};
3270
+ const payload = extractPayload(record);
3109
3271
  activeModel = extractModelName(payload["model"]) ?? activeModel;
3110
3272
  }
3111
3273
  pendingPlan = this.convertRecord(record, transcript, pendingPlan, activeModel);
3112
3274
  if (recordType === "event_msg") {
3113
- const payload = record["payload"] ?? {};
3275
+ const payload = extractPayload(record);
3114
3276
  if (String(payload["type"] ?? "") === "token_count") {
3115
- const info = payload["info"];
3116
- const totalUsage = info?.["total_token_usage"];
3277
+ const { totalUsage, lastUsage } = extractTokenUsage(payload);
3117
3278
  const cumulativeTotal = Number(totalUsage?.["total_tokens"] ?? 0);
3118
3279
  if (cumulativeTotal > 0 && cumulativeTotal === prevCumulativeTotal) {
3119
3280
  } else {
3120
3281
  prevCumulativeTotal = cumulativeTotal;
3121
- const lastUsage = info?.["last_token_usage"];
3122
3282
  let inputTokens = 0;
3123
3283
  let outputTokens = 0;
3124
3284
  let reasoningTokens = 0;
@@ -3193,6 +3353,7 @@ var CodexAgent = class extends FileSystemSessionSource {
3193
3353
  return [];
3194
3354
  }
3195
3355
  }
3356
+ /** Stats each rollout file once during the walk; caller reuses it for the scan window check and the fingerprint. */
3196
3357
  walkDirForRolloutFiles(dir, options) {
3197
3358
  const files = [];
3198
3359
  try {
@@ -3201,14 +3362,14 @@ var CodexAgent = class extends FileSystemSessionSource {
3201
3362
  if (entry.isDirectory()) {
3202
3363
  files.push(...this.walkDirForRolloutFiles(fullPath, options));
3203
3364
  } else if (entry.name.endsWith(".jsonl") && entry.name.startsWith("rollout-")) {
3204
- if (options?.from != null || options?.to != null) {
3205
- try {
3206
- if (!matchesScanWindow(statSync4(fullPath).mtimeMs, options)) continue;
3207
- } catch {
3208
- continue;
3209
- }
3365
+ let stat;
3366
+ try {
3367
+ stat = statSync4(fullPath);
3368
+ } catch {
3369
+ continue;
3210
3370
  }
3211
- files.push(fullPath);
3371
+ if (!matchesScanWindow(stat.mtimeMs, options)) continue;
3372
+ files.push({ file: fullPath, stat });
3212
3373
  }
3213
3374
  }
3214
3375
  } catch {
@@ -3217,12 +3378,13 @@ var CodexAgent = class extends FileSystemSessionSource {
3217
3378
  }
3218
3379
  buildSessionMeta(head, file) {
3219
3380
  const indexPath = this.getSessionIndexPath();
3381
+ const stat = statSync4(file);
3220
3382
  return {
3221
3383
  id: head.id,
3222
3384
  title: head.title,
3223
3385
  sourcePath: file,
3224
- sourceFingerprint: this.sourceFingerprint(file),
3225
- sourceMtimeMs: statSync4(file).mtimeMs,
3386
+ sourceFingerprint: this.sourceFingerprint(file, stat),
3387
+ sourceMtimeMs: stat.mtimeMs,
3226
3388
  indexPath: existsSync7(indexPath) ? indexPath : null,
3227
3389
  indexMtimeMs: this.getFileMtimeMs(indexPath),
3228
3390
  headIndexVersion: HEAD_INDEX_VERSION2,
@@ -3234,8 +3396,8 @@ var CodexAgent = class extends FileSystemSessionSource {
3234
3396
  updatedAt: head.time_updated ?? head.time_created
3235
3397
  };
3236
3398
  }
3237
- sourceFingerprint(file) {
3238
- const stat = statSync4(file);
3399
+ /** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
3400
+ sourceFingerprint(file, stat) {
3239
3401
  const sessionId = extractSessionId(file);
3240
3402
  return JSON.stringify([
3241
3403
  HEAD_INDEX_VERSION2,
@@ -3302,7 +3464,7 @@ var CodexAgent = class extends FileSystemSessionSource {
3302
3464
  let firstPayload = {};
3303
3465
  let createdAt = 0;
3304
3466
  let lineCount = 0;
3305
- const titleLines = [];
3467
+ let messageTitle = null;
3306
3468
  let updatedAt = 0;
3307
3469
  let messageCount = 0;
3308
3470
  let model = null;
@@ -3328,20 +3490,23 @@ var CodexAgent = class extends FileSystemSessionSource {
3328
3490
  } catch {
3329
3491
  return skippedSession("malformed first record");
3330
3492
  }
3331
- firstPayload = firstRecord["payload"] ?? {};
3493
+ firstPayload = extractPayload(firstRecord);
3332
3494
  createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(firstPayload) || statSync4(filePath).mtimeMs;
3333
3495
  updatedAt = createdAt;
3334
3496
  }
3335
- if (titleLines.length < 20) titleLines.push(line);
3336
3497
  try {
3337
3498
  const data = JSON.parse(line);
3338
3499
  const recordType = String(data["type"] ?? "");
3339
- const payload = data["payload"] ?? {};
3500
+ const payload = extractPayload(data);
3340
3501
  const payloadType = String(payload["type"] ?? "");
3341
3502
  if (isInternalEventType2(recordType) || isInternalEventType2(payloadType)) continue;
3342
3503
  hasNonInternalRecord = true;
3343
- const recordTs = parseTimestampMs2(data) || parseTimestampMs2(data["payload"] ?? {});
3504
+ const recordTs = parseTimestampMs2(data) || parseTimestampMs2(payload);
3344
3505
  if (recordTs > updatedAt) updatedAt = recordTs;
3506
+ if (messageTitle === null && lineCount <= 20) {
3507
+ const candidate = this.extractCodexRecordTitle(data);
3508
+ if (candidate) messageTitle = candidate;
3509
+ }
3345
3510
  if (recordType === "session_meta" || recordType === "turn_context") {
3346
3511
  const nextModel = extractModelName(payload["model"]);
3347
3512
  if (nextModel) {
@@ -3356,7 +3521,7 @@ var CodexAgent = class extends FileSystemSessionSource {
3356
3521
  if (COUNTED_TYPES.has(pType)) {
3357
3522
  messageCount++;
3358
3523
  }
3359
- const info = p["info"];
3524
+ const info = narrowRecordField(p["info"], "response_item.info");
3360
3525
  const m = info?.["model"] ?? p["model"];
3361
3526
  if (typeof m === "string" && m.trim()) {
3362
3527
  activeModel = m.trim();
@@ -3364,14 +3529,11 @@ var CodexAgent = class extends FileSystemSessionSource {
3364
3529
  }
3365
3530
  }
3366
3531
  if (recordType === "event_msg") {
3367
- const p = data["payload"] ?? {};
3368
- if (String(p["type"] ?? "") === "token_count") {
3369
- const info = p["info"];
3370
- const totalUsage = info?.["total_token_usage"];
3532
+ if (String(payload["type"] ?? "") === "token_count") {
3533
+ const { totalUsage, lastUsage } = extractTokenUsage(payload);
3371
3534
  const cumulativeTotal = Number(totalUsage?.["total_tokens"] ?? 0);
3372
3535
  if (cumulativeTotal > 0 && cumulativeTotal !== scanPrevCumulativeTotal) {
3373
3536
  scanPrevCumulativeTotal = cumulativeTotal;
3374
- const lastUsage = info?.["last_token_usage"];
3375
3537
  let inputTokens = 0;
3376
3538
  let outputTokens = 0;
3377
3539
  let reasoningTokens = 0;
@@ -3416,7 +3578,6 @@ var CodexAgent = class extends FileSystemSessionSource {
3416
3578
  if (lineCount === 0) return skippedSession("empty file");
3417
3579
  if (!hasNonInternalRecord) return filteredSession("internal events only");
3418
3580
  const indexTitle = this.getTitleForSession(sessionId);
3419
- const messageTitle = this.extractTitleFromLines(titleLines);
3420
3581
  const directory = firstPayload["cwd"] ? String(firstPayload["cwd"]) : "";
3421
3582
  const title = resolveSessionTitle(indexTitle, messageTitle, basenameTitle(directory || null));
3422
3583
  return parsedSession({
@@ -3451,7 +3612,7 @@ var CodexAgent = class extends FileSystemSessionSource {
3451
3612
  } catch {
3452
3613
  return skippedSession("malformed first record");
3453
3614
  }
3454
- const payload = firstRecord["payload"] ?? {};
3615
+ const payload = extractPayload(firstRecord);
3455
3616
  const stat = statSync4(filePath);
3456
3617
  const createdAt = parseTimestampMs2(firstRecord) || parseTimestampMs2(payload) || stat.mtimeMs;
3457
3618
  const indexTitle = this.getTitleForSession(sessionId);
@@ -3474,31 +3635,42 @@ var CodexAgent = class extends FileSystemSessionSource {
3474
3635
  }
3475
3636
  });
3476
3637
  }
3638
+ /** Fast path only: parses each of the first 20 lines to find a title (no full stats pass available). */
3477
3639
  extractTitleFromLines(lines) {
3478
3640
  for (const line of lines.slice(0, 20)) {
3479
3641
  try {
3480
- const data = JSON.parse(line);
3481
- const recordType = String(data["type"] ?? "");
3482
- if (recordType !== "response_item" || isInternalEventType2(recordType)) continue;
3483
- const payload = data["payload"] ?? {};
3484
- const pType = String(payload["type"] ?? "");
3485
- if (pType !== "message" || isInternalEventType2(pType)) continue;
3486
- if (String(payload["role"] ?? "") !== "user") continue;
3487
- const content = payload["content"];
3488
- let text = null;
3489
- if (Array.isArray(content)) {
3490
- text = content.filter((item) => typeof item === "object" && item !== null && "text" in item).map((item) => String(item["text"] ?? "")).join(" ");
3491
- } else if (typeof content === "string") {
3492
- text = content;
3493
- }
3494
- if (!text || isDeveloperLikeUserMessage(text)) continue;
3495
- const title = normalizeTitleText(text);
3642
+ const title = this.extractCodexRecordTitle(JSON.parse(line));
3496
3643
  if (title) return title;
3497
3644
  } catch {
3498
3645
  }
3499
3646
  }
3500
3647
  return null;
3501
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
+ }
3502
3674
  // ---- Record conversion ----
3503
3675
  convertRecord(data, transcript, pendingPlan, activeModel) {
3504
3676
  const recordType = String(data["type"] ?? "");
@@ -3507,7 +3679,7 @@ var CodexAgent = class extends FileSystemSessionSource {
3507
3679
  return pendingPlan;
3508
3680
  }
3509
3681
  if (recordType !== "response_item") return pendingPlan;
3510
- const payload = data["payload"] ?? {};
3682
+ const payload = extractPayload(data);
3511
3683
  const payloadType = String(payload["type"] ?? "");
3512
3684
  if (isInternalEventType2(payloadType)) return pendingPlan;
3513
3685
  const timestampMs = parseTimestampMs2(data) || parseTimestampMs2(payload);
@@ -3552,8 +3724,8 @@ var CodexAgent = class extends FileSystemSessionSource {
3552
3724
  if (!Array.isArray(content)) return pendingPlan;
3553
3725
  const textParts = [];
3554
3726
  for (const item of content) {
3555
- if (typeof item !== "object" || item === null) continue;
3556
- const ci = item;
3727
+ const ci = asRecord(item);
3728
+ if (!ci) continue;
3557
3729
  if (String(ci["type"] ?? "") === "output_text") {
3558
3730
  const text = String(ci["text"] ?? "");
3559
3731
  if (text.trim()) textParts.push(text);
@@ -3586,9 +3758,11 @@ var CodexAgent = class extends FileSystemSessionSource {
3586
3758
  // ---- User message ----
3587
3759
  convertUserMessage(payload, transcript, timestampMs, pendingPlan) {
3588
3760
  const content = payload["content"];
3589
- const text = Array.isArray(content) ? content.map(
3590
- (c) => typeof c === "object" && c !== null ? String(c["text"] ?? "") : String(c ?? "")
3591
- ).join(" ") : String(content ?? "");
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 ?? "");
3592
3766
  const visibleText = cleanInternalText(text);
3593
3767
  if (!visibleText) return pendingPlan;
3594
3768
  if (isDeveloperLikeUserMessage(visibleText)) return pendingPlan;
@@ -3604,8 +3778,13 @@ var CodexAgent = class extends FileSystemSessionSource {
3604
3778
  }
3605
3779
  const subagentMatch = visibleText.match(SUBAGENT_NOTIFICATION_PATTERN);
3606
3780
  if (subagentMatch) {
3781
+ let notifPayload;
3607
3782
  try {
3608
- const notifPayload = JSON.parse(subagentMatch[1]);
3783
+ notifPayload = asRecord(JSON.parse(subagentMatch[1]));
3784
+ } catch {
3785
+ notifPayload = void 0;
3786
+ }
3787
+ if (notifPayload) {
3609
3788
  const agentId = String(notifPayload["agent_id"] ?? "");
3610
3789
  const nickname = String(notifPayload["nickname"] ?? "");
3611
3790
  const completedText = String(notifPayload["completed"] ?? "");
@@ -3625,7 +3804,6 @@ var CodexAgent = class extends FileSystemSessionSource {
3625
3804
  });
3626
3805
  transcript.beginTurn();
3627
3806
  return pendingPlan;
3628
- } catch {
3629
3807
  }
3630
3808
  }
3631
3809
  transcript.appendMessage({
@@ -3642,12 +3820,11 @@ var CodexAgent = class extends FileSystemSessionSource {
3642
3820
  if (!Array.isArray(summary)) return;
3643
3821
  const texts = [];
3644
3822
  for (const item of summary) {
3645
- if (typeof item === "object" && item !== null) {
3646
- const ci = item;
3647
- if (String(ci["type"] ?? "") === "summary_text") {
3648
- const text = String(ci["text"] ?? "");
3649
- if (text.trim()) texts.push(text);
3650
- }
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);
3651
3828
  }
3652
3829
  }
3653
3830
  if (texts.length === 0) return;
@@ -3839,6 +4016,106 @@ var PerfTracer = class {
3839
4016
  }
3840
4017
  };
3841
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
+ }
3842
4119
  var CURSOR_TOOL_TITLE_MAP = {
3843
4120
  read_file_v2: "read",
3844
4121
  edit_file_v2: "edit",
@@ -3859,9 +4136,8 @@ function normalizeToolOutputParts2(output, timestampMs) {
3859
4136
  const parts = [];
3860
4137
  for (const item of output) {
3861
4138
  if (typeof item === "object" && item !== null) {
3862
- const text2 = String(
3863
- item.text ?? item.content ?? ""
3864
- );
4139
+ const record = asRecord(item);
4140
+ const text2 = String(record?.text ?? record?.content ?? "");
3865
4141
  const cleaned = cleanInternalText(text2);
3866
4142
  if (cleaned) parts.push({ type: "text", text: cleaned, time_created: timestampMs });
3867
4143
  } else if (typeof item === "string") {
@@ -3901,9 +4177,9 @@ function buildToolState(action) {
3901
4177
  }
3902
4178
  if (!state.status) {
3903
4179
  if (typeof action.output === "object" && action.output !== null) {
3904
- const out = action.output;
3905
- if (out.success === true) state.status = "completed";
3906
- else if (out.success === false) state.status = "error";
4180
+ const out = asRecord(action.output);
4181
+ if (out?.success === true) state.status = "completed";
4182
+ else if (out?.success === false) state.status = "error";
3907
4183
  else state.status = "completed";
3908
4184
  } else if (action.output != null) {
3909
4185
  state.status = "completed";
@@ -3992,8 +4268,8 @@ var CursorAgent = class extends DatabaseSessionSource {
3992
4268
  if (!existsSync8(wsJsonPath)) continue;
3993
4269
  let workspacePath;
3994
4270
  try {
3995
- const data = JSON.parse(readFileSync5(wsJsonPath, "utf-8"));
3996
- const uri = data.folder ?? data.workspace ?? "";
4271
+ const data = asRecord(JSON.parse(readFileSync5(wsJsonPath, "utf-8")));
4272
+ const uri = narrowString("workspaceJson.folder", data?.folder) ?? narrowString("workspaceJson.workspace", data?.workspace) ?? "";
3997
4273
  if (!uri) continue;
3998
4274
  workspacePath = normalize(decodeURIComponent(uri.replace(/^file:\/\//, "")));
3999
4275
  } catch {
@@ -4007,16 +4283,11 @@ var CursorAgent = class extends DatabaseSessionSource {
4007
4283
  const row = wsDb.prepare("SELECT value FROM ItemTable WHERE key = 'composer.composerData'").get();
4008
4284
  if (!row?.value) continue;
4009
4285
  const parsed = JSON.parse(row.value);
4010
- let composers;
4011
- if (parsed !== null && typeof parsed === "object" && "allComposers" in parsed && Array.isArray(parsed["allComposers"])) {
4012
- composers = parsed.allComposers;
4013
- } else if (Array.isArray(parsed)) {
4014
- composers = parsed;
4015
- } else {
4016
- continue;
4017
- }
4018
- for (const c of composers) {
4019
- 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);
4020
4291
  if (id) map.set(id, workspacePath);
4021
4292
  }
4022
4293
  } catch {
@@ -4047,8 +4318,8 @@ var CursorAgent = class extends DatabaseSessionSource {
4047
4318
  let processed = 0;
4048
4319
  for (const row of rows) {
4049
4320
  try {
4050
- const composer = JSON.parse(row.value);
4051
- if (!composer.id && !composer.composerId) continue;
4321
+ const composer = parseComposerRow(row.value);
4322
+ if (!composer || !composer.id && !composer.composerId) continue;
4052
4323
  const composerId = composer.id || composer.composerId || "";
4053
4324
  const createdAt = composer.createdAt ?? 0;
4054
4325
  const updatedAt = composer.updatedAt ?? composer.lastUpdatedAt ?? composer.lastSendTime ?? createdAt;
@@ -4249,8 +4520,8 @@ var CursorAgent = class extends DatabaseSessionSource {
4249
4520
  const rows = db.prepare("SELECT value FROM cursorDiskKV WHERE key LIKE ? ORDER BY key").all(`bubbleId:${composerId}:%`);
4250
4521
  for (const row of rows) {
4251
4522
  try {
4252
- const bubble = JSON.parse(row.value);
4253
- if (bubble.requestId && typeof bubble.requestId === "string" && bubble.requestId.trim()) {
4523
+ const bubble = parseBubbleRow(row.value);
4524
+ if (bubble?.requestId?.trim()) {
4254
4525
  return bubble.requestId.trim();
4255
4526
  }
4256
4527
  } catch {
@@ -4266,8 +4537,8 @@ var CursorAgent = class extends DatabaseSessionSource {
4266
4537
  const rows = db.prepare("SELECT key, value FROM cursorDiskKV WHERE key LIKE 'bubbleId:%' AND value LIKE ?").all(`%"requestId":"${requestId}"%`);
4267
4538
  for (const row of rows) {
4268
4539
  try {
4269
- const bubble = JSON.parse(row.value);
4270
- if (bubble.requestId === requestId) {
4540
+ const bubble = parseBubbleRow(row.value);
4541
+ if (bubble?.requestId === requestId) {
4271
4542
  const keyParts = row.key.split(":");
4272
4543
  if (keyParts.length >= 2 && keyParts[1]) {
4273
4544
  return keyParts[1];
@@ -4293,8 +4564,8 @@ var CursorAgent = class extends DatabaseSessionSource {
4293
4564
  let count = 0;
4294
4565
  for (const row of rows) {
4295
4566
  try {
4296
- const bubble = JSON.parse(row.value);
4297
- if (bubble.type === 1 || bubble.type === 2) {
4567
+ const bubble = parseBubbleRow(row.value);
4568
+ if (bubble?.type === 1 || bubble?.type === 2) {
4298
4569
  count++;
4299
4570
  }
4300
4571
  } catch {
@@ -4314,8 +4585,8 @@ var CursorAgent = class extends DatabaseSessionSource {
4314
4585
  let messageIndex = 0;
4315
4586
  for (const row of rows) {
4316
4587
  try {
4317
- const bubble = JSON.parse(row.value);
4318
- if (isInternalBubble(bubble)) continue;
4588
+ const bubble = parseBubbleRow(row.value);
4589
+ if (!bubble || isInternalBubble(bubble)) continue;
4319
4590
  const bubbleId = row.key.split(":").pop() || String(messageIndex);
4320
4591
  const role = bubble.type === 2 ? "assistant" : "user";
4321
4592
  let timestampMs = 0;
@@ -4404,7 +4675,7 @@ var CursorAgent = class extends DatabaseSessionSource {
4404
4675
  }
4405
4676
  }
4406
4677
  if (toolName === "create_plan") {
4407
- const planText = typeof state.input === "object" && state.input !== null ? state.input.plan : void 0;
4678
+ const planText = asRecord(state.input)?.plan;
4408
4679
  return {
4409
4680
  type: "plan",
4410
4681
  title: "Plan",
@@ -4426,20 +4697,12 @@ var CursorAgent = class extends DatabaseSessionSource {
4426
4697
  loadComposer(db, sessionId) {
4427
4698
  const row = db.prepare("SELECT value FROM cursorDiskKV WHERE key = ?").get(`composerData:${sessionId}`);
4428
4699
  if (!row) return null;
4429
- try {
4430
- return JSON.parse(row.value);
4431
- } catch {
4432
- return null;
4433
- }
4700
+ return parseComposerRow(row.value);
4434
4701
  }
4435
4702
  loadBubble(db, sessionId) {
4436
4703
  const row = db.prepare("SELECT value FROM cursorDiskKV WHERE key = ?").get(`bubble:${sessionId}`);
4437
4704
  if (!row) return null;
4438
- try {
4439
- return JSON.parse(row.value);
4440
- } catch {
4441
- return null;
4442
- }
4705
+ return parseBubbleRow(row.value);
4443
4706
  }
4444
4707
  appendSubagentMessages(db, composer, messages) {
4445
4708
  const subagentInfos = composer.subagentInfos;
@@ -4493,6 +4756,17 @@ function parseTimestampMs3(value) {
4493
4756
  const ts = Date.parse(text);
4494
4757
  return Number.isNaN(ts) ? 0 : ts;
4495
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
+ }
4496
4770
  function extractSessionIdFromFilename(filePath) {
4497
4771
  const stem = basename6(filePath, ".jsonl");
4498
4772
  const underscore = stem.indexOf("_");
@@ -4516,7 +4790,7 @@ function normalizeTextParts(content, timestampMs) {
4516
4790
  return text ? [{ type: "text", text, time_created: timestampMs }] : [];
4517
4791
  }
4518
4792
  function getEntryTimestamp(entry) {
4519
- return parseTimestampMs3(entry["timestamp"]);
4793
+ return narrowTimestampMs("entry.timestamp", entry["timestamp"]);
4520
4794
  }
4521
4795
  function chooseLeafEntry(entries) {
4522
4796
  for (let index = entries.length - 1; index >= 0; index -= 1) {
@@ -4560,10 +4834,10 @@ var PiAgent = class extends FileSystemSessionSource {
4560
4834
  }
4561
4835
  listSessionSources(options) {
4562
4836
  if (!this.basePath) return [];
4563
- return this.listSessionFiles(options).map((file) => ({
4837
+ return this.walkJsonlFiles(this.basePath, options).map(({ file, stat }) => ({
4564
4838
  sessionId: extractSessionIdFromFilename(file),
4565
4839
  sourcePath: file,
4566
- fingerprint: this.sourceFingerprint(file)
4840
+ fingerprint: this.sourceFingerprint(stat)
4567
4841
  }));
4568
4842
  }
4569
4843
  scanSessionSource(sourcePath) {
@@ -4600,8 +4874,9 @@ var PiAgent = class extends FileSystemSessionSource {
4600
4874
  }
4601
4875
  listSessionFiles(options) {
4602
4876
  if (!this.basePath) return [];
4603
- return this.walkJsonlFiles(this.basePath, options);
4877
+ return this.walkJsonlFiles(this.basePath, options).map(({ file }) => file);
4604
4878
  }
4879
+ /** Stats each file once during the walk; caller reuses it for both the scan window check and the fingerprint. */
4605
4880
  walkJsonlFiles(dir, options) {
4606
4881
  const files = [];
4607
4882
  try {
@@ -4612,20 +4887,27 @@ var PiAgent = class extends FileSystemSessionSource {
4612
4887
  continue;
4613
4888
  }
4614
4889
  if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
4615
- if (!matchesScanWindow(statSync6(fullPath).mtimeMs, options)) continue;
4616
- files.push(fullPath);
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 });
4617
4898
  }
4618
4899
  } catch {
4619
4900
  }
4620
4901
  return files;
4621
4902
  }
4622
4903
  buildSessionMeta(head, file) {
4904
+ const stat = statSync6(file);
4623
4905
  return {
4624
4906
  id: head.id,
4625
4907
  title: head.title,
4626
4908
  sourcePath: file,
4627
- sourceFingerprint: this.sourceFingerprint(file),
4628
- sourceMtimeMs: statSync6(file).mtimeMs,
4909
+ sourceFingerprint: this.sourceFingerprint(stat),
4910
+ sourceMtimeMs: stat.mtimeMs,
4629
4911
  headIndexVersion: HEAD_INDEX_VERSION3,
4630
4912
  parserVersion: PARSER_VERSION2,
4631
4913
  directory: head.directory,
@@ -4634,8 +4916,8 @@ var PiAgent = class extends FileSystemSessionSource {
4634
4916
  updatedAt: head.time_updated ?? head.time_created
4635
4917
  };
4636
4918
  }
4637
- sourceFingerprint(file) {
4638
- const stat = statSync6(file);
4919
+ /** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
4920
+ sourceFingerprint(stat) {
4639
4921
  return JSON.stringify([HEAD_INDEX_VERSION3, PARSER_VERSION2, stat.mtimeMs, stat.size]);
4640
4922
  }
4641
4923
  parseSessionHeadResult(filePath) {
@@ -4675,7 +4957,7 @@ var PiAgent = class extends FileSystemSessionSource {
4675
4957
  if (!sessionId) throw new Error("missing session id");
4676
4958
  const stat = statSync6(filePath);
4677
4959
  const directory = String(header["cwd"] ?? "").trim() || basename6(filePath, ".jsonl");
4678
- const createdAt = parseTimestampMs3(header["timestamp"]) || stat.mtimeMs;
4960
+ const createdAt = narrowTimestampMs("session.timestamp", header["timestamp"]) || stat.mtimeMs;
4679
4961
  const updatedAt = pathEntries.reduce(
4680
4962
  (max, entry) => Math.max(max, getEntryTimestamp(entry)),
4681
4963
  createdAt
@@ -4718,8 +5000,8 @@ var PiAgent = class extends FileSystemSessionSource {
4718
5000
  const timestampMs = getEntryTimestamp(entry);
4719
5001
  const type = String(entry["type"] ?? "");
4720
5002
  if (type === "message") {
4721
- const message = entry["message"];
4722
- if (!isObject(message)) continue;
5003
+ const message = narrowPiField("entry.message", entry["message"], asRecord);
5004
+ if (!message) continue;
4723
5005
  const result2 = this.convertAgentMessage(entry, message, timestampMs, builder);
4724
5006
  if (!result2) continue;
4725
5007
  if (result2.message) builder.appendMessage(result2.message);
@@ -4743,8 +5025,8 @@ var PiAgent = class extends FileSystemSessionSource {
4743
5025
  };
4744
5026
  }
4745
5027
  convertAgentMessage(entry, message, timestampMs, builder) {
4746
- const id = String(entry["id"] ?? "");
4747
- const role = String(message["role"] ?? "");
5028
+ const id = narrowPiField("message.id", entry["id"], asString) ?? "";
5029
+ const role = narrowPiField("message.role", message["role"], asString) ?? "";
4748
5030
  if (role === "user") {
4749
5031
  const parts = normalizeTextParts(message["content"], timestampMs);
4750
5032
  if (parts.length === 0) return null;
@@ -4883,7 +5165,7 @@ var PiAgent = class extends FileSystemSessionSource {
4883
5165
  const text = cleanInternalText(rawText);
4884
5166
  if (!text) return null;
4885
5167
  return {
4886
- id: String(entry["id"] ?? ""),
5168
+ id: narrowPiField("summary.id", entry["id"], asString) ?? "",
4887
5169
  role: type === "custom_message" ? "user" : "assistant",
4888
5170
  agent: type === "custom_message" ? void 0 : "pi",
4889
5171
  timestampMs,
@@ -4891,14 +5173,12 @@ var PiAgent = class extends FileSystemSessionSource {
4891
5173
  };
4892
5174
  }
4893
5175
  normalizeUsage(raw) {
4894
- const usage = isObject(raw) ? raw : {};
4895
- const inputTokens = Number(usage["input"] ?? 0);
4896
- const outputTokens = Number(usage["output"] ?? 0);
4897
- const cacheReadTokens = Number(usage["cacheRead"] ?? 0);
4898
- const cacheCreateTokens = Number(usage["cacheWrite"] ?? 0);
4899
- const totalTokens = Number(
4900
- usage["totalTokens"] ?? inputTokens + outputTokens + cacheReadTokens + cacheCreateTokens
4901
- );
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;
4902
5182
  const cost = isObject(usage["cost"]) ? Number(usage["cost"]["total"] ?? 0) : null;
4903
5183
  return {
4904
5184
  inputTokens: inputTokens + cacheReadTokens + cacheCreateTokens,
@@ -4940,6 +5220,7 @@ var ZCodeAgent = class extends OpenCodeSqliteAgent {
4940
5220
  };
4941
5221
  registerAgent({
4942
5222
  icon: "/icon/agent/claudecode.svg",
5223
+ iconColored: true,
4943
5224
  create: () => new ClaudeCodeAgent()
4944
5225
  });
4945
5226
  registerAgent({
@@ -5029,7 +5310,23 @@ function normalizeGitRemote(url) {
5029
5310
  if (!value.includes("/")) return null;
5030
5311
  return value.toLowerCase();
5031
5312
  }
5313
+ var IDENTITY_CACHE_TTL_MS = 10 * 60 * 1e3;
5314
+ var identityCache = /* @__PURE__ */ new Map();
5315
+ function clearIdentityCache() {
5316
+ identityCache.clear();
5317
+ }
5032
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) {
5033
5330
  if (!cwd) return loose();
5034
5331
  const pathOps = getPathOps(cwd);
5035
5332
  const absoluteCwd = pathOps.resolve(cwd);
@@ -5963,7 +6260,10 @@ function withCacheDb(fn) {
5963
6260
  setSchemaEnsuredPath(cachePath);
5964
6261
  }
5965
6262
  return fn(db);
5966
- } catch {
6263
+ } catch (error) {
6264
+ getCoreDiagnostics()?.warn("cache.write_failed", {
6265
+ message: error instanceof Error ? error.message : String(error)
6266
+ });
5967
6267
  return null;
5968
6268
  } finally {
5969
6269
  db.close();
@@ -7854,6 +8154,14 @@ function searchFileActivitySessions(query, options = {}) {
7854
8154
  return results;
7855
8155
  }
7856
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
+ }
7857
8165
  function deleteLegacyCacheFile() {
7858
8166
  const legacyPath = getLegacyCachePath();
7859
8167
  if (!existsSync12(legacyPath)) {
@@ -7972,7 +8280,7 @@ function markAgentFullSyncCompleted(agentName) {
7972
8280
  ).run(Date.now(), agentName);
7973
8281
  });
7974
8282
  }
7975
- function loadCachedSessionData(agentName, sessionId) {
8283
+ function loadCachedSessionDataEntry(agentName, sessionId) {
7976
8284
  if (!hasCacheStorage()) {
7977
8285
  return null;
7978
8286
  }
@@ -8044,12 +8352,18 @@ function loadCachedSessionData(agentName, sessionId) {
8044
8352
  `
8045
8353
  ).all(agentName, sessionId);
8046
8354
  return {
8047
- ...head,
8048
- messages: messageRows.map((messageRow) => messageFromCachedRow(messageRow)),
8049
- file_activity: fileActivityRows.map((activityRow) => fileActivityFromRow(activityRow))
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)
8050
8361
  };
8051
8362
  });
8052
8363
  }
8364
+ function loadCachedSessionData(agentName, sessionId) {
8365
+ return loadCachedSessionDataEntry(agentName, sessionId)?.data ?? null;
8366
+ }
8053
8367
  function saveCachedSessions(agentName, sessions, meta = {}) {
8054
8368
  withCacheDb((db) => {
8055
8369
  const deleteAgent = db.prepare("DELETE FROM agent_cache WHERE agent_name = ?");
@@ -8239,9 +8553,8 @@ function listCachedProjectGroups(sessions) {
8239
8553
  if (!hasCacheStorage()) {
8240
8554
  return [];
8241
8555
  }
8242
- const groups = withCacheDb((db) => {
8243
- const rows = db.prepare(
8244
- `
8556
+ const queryRows = (db) => db.prepare(
8557
+ `
8245
8558
  SELECT identity_kind, identity_key, display_name, sources_csv, session_count, last_activity
8246
8559
  FROM project_groups_v
8247
8560
  ORDER BY
@@ -8249,34 +8562,24 @@ function listCachedProjectGroups(sessions) {
8249
8562
  last_activity IS NULL,
8250
8563
  last_activity DESC
8251
8564
  `
8252
- ).all();
8253
- return rows.map((row) => ({
8254
- identityKind: row.identity_kind ?? "path",
8255
- identityKey: String(row.identity_key ?? ""),
8256
- displayName: String(row.display_name ?? ""),
8257
- sources: String(row.sources_csv ?? "").split(",").filter(Boolean).sort(),
8258
- sessionCount: Number(row.session_count ?? 0),
8259
- lastActivity: row.last_activity == null ? null : Number(row.last_activity)
8260
- }));
8261
- });
8262
- return groups ?? [];
8263
- }
8264
- function createIdentityResolver() {
8265
- const cache = /* @__PURE__ */ new Map();
8266
- return (directory) => {
8267
- const key = directory || "";
8268
- const cached = cache.get(key);
8269
- if (cached) return cached;
8270
- const identity = computeIdentity(directory, realFs);
8271
- cache.set(key, identity);
8272
- return identity;
8273
- };
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
+ }));
8274
8578
  }
8275
8579
  function attachMissingProjectIdentities(sessions) {
8276
- const resolveIdentity = createIdentityResolver();
8277
8580
  return sessions.map((session) => {
8278
8581
  if (session.project_identity) return session;
8279
- return { ...session, project_identity: resolveIdentity(session.directory) };
8582
+ return { ...session, project_identity: computeIdentity(session.directory, realFs) };
8280
8583
  });
8281
8584
  }
8282
8585
  function buildAgentCacheMeta(agent, sessionIds) {
@@ -8306,7 +8609,7 @@ function sessionSignature(session) {
8306
8609
  function sortSessions(sessions) {
8307
8610
  return sortSessionsByActivity(sessions);
8308
8611
  }
8309
- function computeSessionDiff(cachedSessions, updatedSessions, changedIds = [], signature = sessionSignature) {
8612
+ function computeSessionDiff(cachedSessions, updatedSessions, changedIds = [], signature = sessionSignature, signatureCache) {
8310
8613
  const cachedMap = new Map(cachedSessions.map((session) => [session.id, session]));
8311
8614
  const updatedIds = new Set(updatedSessions.map((session) => session.id));
8312
8615
  const changedIdSet = new Set(changedIds);
@@ -8319,10 +8622,13 @@ function computeSessionDiff(cachedSessions, updatedSessions, changedIds = [], si
8319
8622
  if (!cached) {
8320
8623
  newCount += 1;
8321
8624
  changes.push({ session, sortIndex });
8625
+ signatureCache?.set(session.id, signature(session));
8322
8626
  return;
8323
8627
  }
8324
- const hasSignatureChange = signature(cached) !== signature(session);
8325
- if (changedIdSet.has(session.id) || hasSignatureChange) {
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) {
8326
8632
  updatedCount += 1;
8327
8633
  changes.push({ session, sortIndex });
8328
8634
  }
@@ -8540,7 +8846,7 @@ async function scanAgentSmart(agent, options, onProgress) {
8540
8846
  });
8541
8847
  const t2 = performance.now();
8542
8848
  const updatedSessions = await Promise.resolve(
8543
- agent.incrementalScan(cached.sessions, checkResult.changedIds || [])
8849
+ agent.incrementalScan(cached.sessions, checkResult.changedIds || [], checkResult.refs)
8544
8850
  );
8545
8851
  timing.scan = performance.now() - t2;
8546
8852
  return finalizeAgentScan(agent, updatedSessions, {
@@ -8604,10 +8910,12 @@ async function scanAgentFull(agent, options, onProgress, timing = { total: 0 },
8604
8910
  const tagged = options.includeSmartTags === false ? { sessions: headsWithIdentity, changed: false } : await ensureSessionTags(agent, headsWithIdentity, options.smartTagWorkerUrl);
8605
8911
  timing.tags = performance.now() - t2;
8606
8912
  const meta = buildAgentCacheMeta(agent);
8607
- if (options.writeCache !== false && options.from == null && options.to == null) {
8608
- saveCachedSessions(agent.name, tagged.sessions, meta);
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
+ }
8609
8918
  markAgentCacheInitialized(agent.name);
8610
- markAgentFullSyncCompleted(agent.name);
8611
8919
  }
8612
8920
  onProgress?.({ agent: agent.name, phase: "complete", newCount: tagged.sessions.length });
8613
8921
  const filtered = filterSessions(tagged.sessions, options);
@@ -8663,6 +8971,13 @@ async function scanSessionsAsync(options = {}, onProgress) {
8663
8971
  var STATE_DB_FILENAME = "state.db";
8664
8972
  var STATE_SCHEMA_VERSION = 2;
8665
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
+ }
8666
8981
  var StateStorageUnavailableError = class extends Error {
8667
8982
  constructor() {
8668
8983
  super("SQLite state database is unavailable");
@@ -8769,7 +9084,7 @@ function ensureSchema2(db, dbPath) {
8769
9084
  { version: 2, migrate: createSessionAliasesTable }
8770
9085
  ]
8771
9086
  });
8772
- if (currentVersion <= STATE_SCHEMA_VERSION) {
9087
+ if (currentVersion < STATE_SCHEMA_VERSION) {
8773
9088
  setStateSchemaVersion(db);
8774
9089
  }
8775
9090
  }
@@ -8778,7 +9093,10 @@ function withStateDb(fn) {
8778
9093
  const db = openDb(statePath);
8779
9094
  if (!db) throw new StateStorageUnavailableError();
8780
9095
  try {
8781
- ensureSchema2(db, statePath);
9096
+ if (getStateSchemaEnsuredPath() !== statePath) {
9097
+ ensureSchema2(db, statePath);
9098
+ setStateSchemaEnsuredPath(statePath);
9099
+ }
8782
9100
  return fn(db);
8783
9101
  } finally {
8784
9102
  db.close();
@@ -9179,6 +9497,7 @@ function buildDashboard(sessions, options) {
9179
9497
  name,
9180
9498
  displayName: info?.displayName ?? name,
9181
9499
  icon: info?.icon ?? "",
9500
+ iconColored: info?.iconColored,
9182
9501
  sessions: metrics.sessions,
9183
9502
  messages: metrics.messages,
9184
9503
  tokens: metrics.tokens
@@ -9219,15 +9538,6 @@ function executeSessionSearch(query, options, snapshot) {
9219
9538
  function needsIndexedSearch(textQuery, options) {
9220
9539
  return Boolean(textQuery || options.file || options.fileKind || options.tools?.length);
9221
9540
  }
9222
- function filterSessionsByActivityWindow(sessions, from, to) {
9223
- if (from == null && to == null) return sessions;
9224
- return sessions.filter((session) => {
9225
- const activity = getSessionActivityTime(session);
9226
- if (from != null && activity < from) return false;
9227
- if (to != null && activity > to) return false;
9228
- return true;
9229
- });
9230
- }
9231
9541
  function matchesRecentSearchFilters(session, options, projectScope) {
9232
9542
  if (options.projectKind || options.projectKey) {
9233
9543
  if (!options.projectKind || !options.projectKey || !matchesProjectIdentity(session.project_identity, {
@@ -9253,11 +9563,18 @@ function matchesRecentSearchFilters(session, options, projectScope) {
9253
9563
  if (!sessionMatchesSearchCost(session, options)) return false;
9254
9564
  return true;
9255
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
+ }
9256
9573
  function searchRecentSessions(snapshot, options) {
9257
9574
  const projectScope = options.cwd ? createProjectScopeMatcher(options.cwd) : null;
9258
9575
  const entries = options.agent ? [[options.agent, snapshot.byAgent[options.agent] ?? []]] : Object.entries(snapshot.byAgent);
9259
9576
  return entries.flatMap(
9260
- ([agentName, sessions]) => filterSessionsByActivityWindow(sessions, options.from, options.to).filter((session) => matchesRecentSearchFilters(session, options, projectScope)).map((session) => ({ agentName, session }))
9577
+ ([agentName, sessions]) => sessions.filter((session) => matchesSessionSearchFilters(agentName, session, options, projectScope)).map((session) => ({ agentName, session }))
9261
9578
  ).sort(
9262
9579
  (a, b) => (b.session.time_updated ?? b.session.time_created) - (a.session.time_updated ?? a.session.time_created)
9263
9580
  ).slice(0, options.limit ?? 50).map(({ agentName, session }) => ({
@@ -9300,6 +9617,7 @@ export {
9300
9617
  getRegisteredAgents,
9301
9618
  getAgentInfoMap,
9302
9619
  getAgentByName,
9620
+ setCoreDiagnostics,
9303
9621
  parsedSession,
9304
9622
  skippedSession,
9305
9623
  filteredSession,
@@ -9333,6 +9651,11 @@ export {
9333
9651
  applyMessageCosts,
9334
9652
  withEstimatedSessionCost,
9335
9653
  estimateTokenCost,
9654
+ asRecord,
9655
+ asString,
9656
+ asNumber,
9657
+ asArray,
9658
+ reportFieldMismatch,
9336
9659
  openDbReadOnly,
9337
9660
  openDb,
9338
9661
  isSqliteAvailable,
@@ -9343,6 +9666,7 @@ export {
9343
9666
  getProjectIdentityKey,
9344
9667
  matchesProjectIdentity,
9345
9668
  normalizeGitRemote,
9669
+ clearIdentityCache,
9346
9670
  computeIdentity,
9347
9671
  buildProjectGroups,
9348
9672
  createProjectScopeMatcher,
@@ -9368,6 +9692,7 @@ export {
9368
9692
  markAgentCacheInitialized,
9369
9693
  getAgentLastFullSyncAt,
9370
9694
  markAgentFullSyncCompleted,
9695
+ loadCachedSessionDataEntry,
9371
9696
  loadCachedSessionData,
9372
9697
  saveCachedSessions,
9373
9698
  saveCachedSessionChanges,
@@ -9400,6 +9725,7 @@ export {
9400
9725
  toLocalDateKey,
9401
9726
  startOfLocalDay,
9402
9727
  buildDashboard,
9403
- executeSessionSearch
9728
+ executeSessionSearch,
9729
+ matchesSessionSearchFilters
9404
9730
  };
9405
- //# sourceMappingURL=chunk-NBCLV4CX.js.map
9731
+ //# sourceMappingURL=chunk-MWSJTNOW.js.map