@tonyclaw/agent-inspector 2.1.6 → 2.1.7

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.
Files changed (31) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-Djazxhew.js → CompareDrawer-BLS7CVvA.js} +1 -1
  3. package/.output/public/assets/{ProxyViewerContainer-BC-z9RPh.js → ProxyViewerContainer-BFZ1WG07.js} +33 -33
  4. package/.output/public/assets/{ReplayDialog-BaD4R6ac.js → ReplayDialog-DIGF807X.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-w6uGdqYp.js → RequestAnatomy-C3Nlvp9P.js} +1 -1
  6. package/.output/public/assets/{ResponseView-VPed-Yif.js → ResponseView-BbZywqHe.js} +1 -1
  7. package/.output/public/assets/{StreamingChunkSequence-CPB8FgHI.js → StreamingChunkSequence-Dk27PfPn.js} +1 -1
  8. package/.output/public/assets/_sessionId-BHpX8AVf.js +1 -0
  9. package/.output/public/assets/index-BXHM9OMb.js +1 -0
  10. package/.output/public/assets/{main-CIV3Chb5.js → main-BxHXMs6B.js} +2 -2
  11. package/.output/server/{_sessionId-CIXQGim9.mjs → _sessionId-D5HNwb9A.mjs} +3 -3
  12. package/.output/server/_ssr/{CompareDrawer-_uFgjkB2.mjs → CompareDrawer-QNk1twf8.mjs} +3 -3
  13. package/.output/server/_ssr/{ProxyViewerContainer-D8YjQDNj.mjs → ProxyViewerContainer-DGhc2le_.mjs} +37 -10
  14. package/.output/server/_ssr/{ReplayDialog-CFwKM7jc.mjs → ReplayDialog-D8wg4VNL.mjs} +4 -4
  15. package/.output/server/_ssr/{RequestAnatomy-2Vr84GxT.mjs → RequestAnatomy-BtJHKVl2.mjs} +3 -3
  16. package/.output/server/_ssr/{ResponseView-CSBWBHRJ.mjs → ResponseView-CndDItiU.mjs} +3 -3
  17. package/.output/server/_ssr/{StreamingChunkSequence-Dr4JApqs.mjs → StreamingChunkSequence-D5Ywpbr7.mjs} +3 -3
  18. package/.output/server/_ssr/{index-WnsY1fh_.mjs → index-DtcUTHAX.mjs} +3 -3
  19. package/.output/server/_ssr/index.mjs +2 -2
  20. package/.output/server/_ssr/{router-sy38X5AX.mjs → router-C0apgqfC.mjs} +541 -120
  21. package/.output/server/{_tanstack-start-manifest_v-Dde-eACV.mjs → _tanstack-start-manifest_v-BoTPaB-V.mjs} +1 -1
  22. package/.output/server/index.mjs +62 -62
  23. package/package.json +1 -1
  24. package/src/components/groups/GroupsDialog.tsx +9 -4
  25. package/src/components/providers/SettingsDialog.tsx +20 -0
  26. package/src/proxy/sessionArchive.ts +410 -0
  27. package/src/proxy/store.ts +121 -6
  28. package/src/routes/api/logs.ts +2 -0
  29. package/src/routes/api/sessions.ts +2 -2
  30. package/.output/public/assets/_sessionId-mnr0O0Ri.js +0 -1
  31. package/.output/public/assets/index-DH1DjJOl.js +0 -1
@@ -40,6 +40,15 @@ import type { CapturedLog } from "./schemas";
40
40
  import { CapturedLogSchema } from "./schemas";
41
41
  import { apiFormatForPath } from "./formats/registry";
42
42
  import type { ImportedLogDraft } from "./logImporter";
43
+ import {
44
+ archiveSessionLog,
45
+ clearSessionArchives,
46
+ deleteArchivedLogsByIds,
47
+ findArchivedLogById,
48
+ getSessionArchiveRoot,
49
+ listArchivedSessionLogs,
50
+ listArchivedSessionIds,
51
+ } from "./sessionArchive";
43
52
  import {
44
53
  clearSessionRegistry,
45
54
  getLogSessionId,
@@ -68,18 +77,23 @@ export type LogStorageStats = {
68
77
  chunkDir: string;
69
78
  chunkFileCount: number;
70
79
  chunkBytes: number;
80
+ sessionArchiveDir: string;
81
+ sessionArchiveFileCount: number;
82
+ sessionArchiveBytes: number;
71
83
  };
72
84
 
73
85
  export type ClearPersistedLogStorageResult = {
74
86
  cleared: number;
75
87
  logFilesDeleted: number;
76
88
  chunkFilesDeleted: number;
89
+ sessionArchiveFilesDeleted: number;
77
90
  };
78
91
 
79
92
  export type ClearPersistedLogsByIdsResult = {
80
93
  cleared: number;
81
94
  logFilesRewritten: number;
82
95
  chunkFilesDeleted: number;
96
+ sessionArchiveLogsDeleted: number;
83
97
  };
84
98
 
85
99
  export type ListLogsPageOptions = {
@@ -138,6 +152,7 @@ type LogCursorPageIndex = {
138
152
 
139
153
  const logCursorPageIndexes: Map<string, LogCursorPageIndex> = new Map();
140
154
  let logCursorPageIndexBuildCount = 0;
155
+ let sessionArchiveQueue: Promise<void> = Promise.resolve();
141
156
 
142
157
  function cursorPageIndexKey(
143
158
  options: Pick<ListLogsCursorPageOptions, "sessionId" | "model">,
@@ -222,6 +237,22 @@ function addToCache(log: CapturedLog): void {
222
237
  evictOldestIfNeeded();
223
238
  }
224
239
 
240
+ function archiveLogSnapshot(log: CapturedLog): void {
241
+ const archivedLog = normalizeLog(log);
242
+ sessionArchiveQueue = sessionArchiveQueue
243
+ .catch(() => undefined)
244
+ .then(async () => {
245
+ await archiveSessionLog(archivedLog);
246
+ })
247
+ .catch((err: unknown) => {
248
+ logger.warn("[store] Failed to archive session log:", String(err));
249
+ });
250
+ }
251
+
252
+ export async function flushSessionArchiveWrites(): Promise<void> {
253
+ await sessionArchiveQueue;
254
+ }
255
+
225
256
  function removeFromCache(id: number): void {
226
257
  memoryCache.delete(id);
227
258
  removeLogsFromCursorPageIndexes(new Set([id]));
@@ -325,6 +356,7 @@ export async function addTestLogEntry(entry: Omit<CapturedLog, "id">): Promise<C
325
356
  });
326
357
 
327
358
  addToCache(log);
359
+ archiveLogSnapshot(log);
328
360
  observeSessionLog(log, session.source);
329
361
  emitLogUpdate(log);
330
362
  return log;
@@ -414,6 +446,7 @@ export async function createLog(
414
446
 
415
447
  // Add to memory cache
416
448
  addToCache(log);
449
+ archiveLogSnapshot(log);
417
450
  markSessionStarted(log, session.source);
418
451
  emitLogUpdate(log);
419
452
 
@@ -422,10 +455,21 @@ export async function createLog(
422
455
 
423
456
  export function finalizeLogUpdate(log: CapturedLog): void {
424
457
  addToCache(log);
458
+ archiveLogSnapshot(log);
425
459
  markSessionFinished(log);
426
460
  emitLogUpdate(log);
427
461
  }
428
462
 
463
+ async function getArchivedLogByIdForStore(id: number): Promise<CapturedLog | null> {
464
+ await flushSessionArchiveWrites();
465
+ const archivedLog = await findArchivedLogById(id);
466
+ if (archivedLog === null) return null;
467
+ const normalized = normalizeLog(archivedLog);
468
+ addToCache(normalized);
469
+ observeSessionLog(normalized);
470
+ return normalized;
471
+ }
472
+
429
473
  /**
430
474
  * Get a log by ID, checking memory cache first then disk.
431
475
  * Uses byte-offset index for efficient single-line reads.
@@ -440,14 +484,14 @@ export async function getLogById(id: number): Promise<CapturedLog | null> {
440
484
  // Look up in index and load from disk
441
485
  const entry = await findInIndex(id);
442
486
  if (entry === null) {
443
- return null;
487
+ return await getArchivedLogByIdForStore(id);
444
488
  }
445
489
 
446
490
  // Load from disk using byte-offset index
447
491
  try {
448
492
  const filePath = join(resolveLogDir(), entry.file);
449
493
  if (!existsSync(filePath)) {
450
- return null;
494
+ return await getArchivedLogByIdForStore(id);
451
495
  }
452
496
 
453
497
  // Use byte offset for direct read when available
@@ -483,7 +527,7 @@ export async function getLogById(id: number): Promise<CapturedLog | null> {
483
527
  logger.error("[store] Failed to read log from disk:", String(err));
484
528
  }
485
529
 
486
- return null;
530
+ return await getArchivedLogByIdForStore(id);
487
531
  }
488
532
 
489
533
  async function scanLogFileForId(filePath: string, id: number): Promise<CapturedLog | null> {
@@ -578,6 +622,20 @@ async function visitPersistedLogFile(
578
622
  }
579
623
  }
580
624
 
625
+ async function visitArchivedSessionLogs(
626
+ sessionId: string | undefined,
627
+ visitor: PersistedLogVisitor,
628
+ ): Promise<void> {
629
+ if (sessionId === undefined) return;
630
+ await flushSessionArchiveWrites();
631
+ const logs = await listArchivedSessionLogs(sessionId);
632
+ for (const archivedLog of logs) {
633
+ const log = normalizeLog(archivedLog);
634
+ observeSessionLog(log);
635
+ visitor(log);
636
+ }
637
+ }
638
+
581
639
  function canUseIndexedListPage(options: ListLogsPageOptions): boolean {
582
640
  return options.sessionId === undefined && options.model === undefined;
583
641
  }
@@ -659,6 +717,8 @@ export async function listLogsPage(options: ListLogsPageOptions): Promise<ListLo
659
717
  visitLog(log);
660
718
  });
661
719
 
720
+ await visitArchivedSessionLogs(options.sessionId, visitLog);
721
+
662
722
  for (const log of memoryCache.values()) {
663
723
  visitLog(log);
664
724
  }
@@ -773,6 +833,18 @@ async function buildLogCursorPageIndexFromLogIndex(
773
833
  logsById.set(log.id, log);
774
834
  }
775
835
 
836
+ if (options.sessionId !== undefined) {
837
+ await flushSessionArchiveWrites();
838
+ const archivedLogs = await listArchivedSessionLogs(options.sessionId);
839
+ for (const archivedLog of archivedLogs) {
840
+ const log = normalizeLog(archivedLog);
841
+ if (!matchesLogFilters(log, options.sessionId, options.model)) continue;
842
+ observeSessionLog(log);
843
+ idsById.add(log.id);
844
+ logsById.set(log.id, log);
845
+ }
846
+ }
847
+
776
848
  return {
777
849
  key: cursorPageIndexKey(options),
778
850
  sessionId: options.sessionId,
@@ -803,6 +875,8 @@ async function buildLogCursorPageIndex(
803
875
  visitLog(log);
804
876
  });
805
877
 
878
+ await visitArchivedSessionLogs(options.sessionId, visitLog);
879
+
806
880
  for (const log of memoryCache.values()) {
807
881
  visitLog(log);
808
882
  }
@@ -923,6 +997,16 @@ async function collectSessionLogSummaries(
923
997
  summariesById.set(summary.id, summary);
924
998
  }
925
999
 
1000
+ await flushSessionArchiveWrites();
1001
+ const archivedLogs = await listArchivedSessionLogs(sessionId);
1002
+ for (const archivedLog of archivedLogs) {
1003
+ const log = normalizeLog(archivedLog);
1004
+ observeSessionLog(log);
1005
+ if (!summariesById.has(log.id)) {
1006
+ summariesById.set(log.id, buildSessionLogSummary(log));
1007
+ }
1008
+ }
1009
+
926
1010
  if (includeHistory && summariesById.size === 0) {
927
1011
  await visitPersistedLogs((log) => {
928
1012
  observeSessionLog(log);
@@ -1001,6 +1085,15 @@ export function getSessions(): string[] {
1001
1085
  return getSessionIds();
1002
1086
  }
1003
1087
 
1088
+ export async function listSessions(): Promise<string[]> {
1089
+ await flushSessionArchiveWrites();
1090
+ const sessionIds = new Set<string>(getSessionIds());
1091
+ for (const sessionId of await listArchivedSessionIds()) {
1092
+ sessionIds.add(sessionId);
1093
+ }
1094
+ return [...sessionIds].sort();
1095
+ }
1096
+
1004
1097
  export function getModels(): string[] {
1005
1098
  const set = new Set<string>();
1006
1099
  for (const l of memoryCache.values()) {
@@ -1050,8 +1143,13 @@ function collectDirectoryStats(
1050
1143
  export function getLogStorageStats(): LogStorageStats {
1051
1144
  const logDir = resolveLogDir();
1052
1145
  const chunkDir = getChunksDir();
1146
+ const sessionArchiveDir = getSessionArchiveRoot();
1053
1147
  const logStats = collectDirectoryStats(logDir, (fileName) => fileName.endsWith(".jsonl"));
1054
1148
  const chunkStats = collectDirectoryStats(chunkDir, (fileName) => fileName.endsWith(".json"));
1149
+ const sessionArchiveStats = collectDirectoryStats(
1150
+ sessionArchiveDir,
1151
+ (fileName) => fileName === "session.sqlite" || fileName.startsWith("session.sqlite-"),
1152
+ );
1055
1153
  return {
1056
1154
  memoryCount: memoryCache.size,
1057
1155
  logDir,
@@ -1060,6 +1158,9 @@ export function getLogStorageStats(): LogStorageStats {
1060
1158
  chunkDir,
1061
1159
  chunkFileCount: chunkStats.fileCount,
1062
1160
  chunkBytes: chunkStats.bytes,
1161
+ sessionArchiveDir,
1162
+ sessionArchiveFileCount: sessionArchiveStats.fileCount,
1163
+ sessionArchiveBytes: sessionArchiveStats.bytes,
1063
1164
  };
1064
1165
  }
1065
1166
 
@@ -1151,9 +1252,10 @@ async function deleteMatchingFiles(
1151
1252
  export async function clearPersistedLogStorage(): Promise<ClearPersistedLogStorageResult> {
1152
1253
  return await runWithFlushedLogWriteLock(async () => {
1153
1254
  await flushIndex();
1255
+ await flushSessionArchiveWrites();
1154
1256
 
1155
1257
  const result = clearAllLogs();
1156
- const [logFilesDeleted, chunkFilesDeleted] = await Promise.all([
1258
+ const [logFilesDeleted, chunkFilesDeleted, sessionArchiveFilesDeleted] = await Promise.all([
1157
1259
  deleteMatchingFiles(
1158
1260
  resolveLogDir(),
1159
1261
  (fileName) =>
@@ -1167,6 +1269,7 @@ export async function clearPersistedLogStorage(): Promise<ClearPersistedLogStora
1167
1269
  (fileName) =>
1168
1270
  fileName.endsWith(".json") || (fileName.startsWith(".") && fileName.endsWith(".tmp")),
1169
1271
  ),
1272
+ clearSessionArchives(),
1170
1273
  ]);
1171
1274
 
1172
1275
  await rebuildIndex();
@@ -1175,6 +1278,7 @@ export async function clearPersistedLogStorage(): Promise<ClearPersistedLogStora
1175
1278
  cleared: result.cleared,
1176
1279
  logFilesDeleted,
1177
1280
  chunkFilesDeleted,
1281
+ sessionArchiveFilesDeleted,
1178
1282
  };
1179
1283
  });
1180
1284
  }
@@ -1365,20 +1469,27 @@ export async function clearPersistedLogsByIds(
1365
1469
  ): Promise<ClearPersistedLogsByIdsResult> {
1366
1470
  const uniqueIds = new Set(ids);
1367
1471
  if (uniqueIds.size === 0) {
1368
- return { cleared: 0, logFilesRewritten: 0, chunkFilesDeleted: 0 };
1472
+ return {
1473
+ cleared: 0,
1474
+ logFilesRewritten: 0,
1475
+ chunkFilesDeleted: 0,
1476
+ sessionArchiveLogsDeleted: 0,
1477
+ };
1369
1478
  }
1370
1479
 
1371
1480
  return await runWithFlushedLogWriteLock(async () => {
1372
1481
  await flushIndex();
1482
+ await flushSessionArchiveWrites();
1373
1483
 
1374
1484
  const memoryRemovedIds = new Set<number>();
1375
1485
  for (const id of uniqueIds) {
1376
1486
  if (memoryCache.has(id)) memoryRemovedIds.add(id);
1377
1487
  }
1378
1488
  const result = clearLogsByIds([...uniqueIds]);
1379
- const [rewriteResult, chunkFilesDeleted] = await Promise.all([
1489
+ const [rewriteResult, chunkFilesDeleted, archivedRemovedIds] = await Promise.all([
1380
1490
  rewriteLogFilesWithoutIds(uniqueIds),
1381
1491
  deleteChunkFilesByIds(uniqueIds),
1492
+ deleteArchivedLogsByIds(uniqueIds),
1382
1493
  ]);
1383
1494
 
1384
1495
  await rebuildIndex();
@@ -1390,11 +1501,15 @@ export async function clearPersistedLogsByIds(
1390
1501
  clearedIds.add(id);
1391
1502
  }
1392
1503
  }
1504
+ for (const id of archivedRemovedIds) {
1505
+ clearedIds.add(id);
1506
+ }
1393
1507
 
1394
1508
  return {
1395
1509
  cleared: clearedIds.size,
1396
1510
  logFilesRewritten: rewriteResult.logFilesRewritten,
1397
1511
  chunkFilesDeleted,
1512
+ sessionArchiveLogsDeleted: archivedRemovedIds.size,
1398
1513
  };
1399
1514
  });
1400
1515
  }
@@ -133,6 +133,7 @@ export const Route = createFileRoute("/api/logs")({
133
133
  cleared: result.cleared,
134
134
  logFilesRewritten: result.logFilesRewritten,
135
135
  chunkFilesDeleted: result.chunkFilesDeleted,
136
+ sessionArchiveLogsDeleted: result.sessionArchiveLogsDeleted,
136
137
  });
137
138
  }
138
139
  const result = await clearPersistedLogStorage();
@@ -141,6 +142,7 @@ export const Route = createFileRoute("/api/logs")({
141
142
  cleared: result.cleared,
142
143
  logFilesDeleted: result.logFilesDeleted,
143
144
  chunkFilesDeleted: result.chunkFilesDeleted,
145
+ sessionArchiveFilesDeleted: result.sessionArchiveFilesDeleted,
144
146
  });
145
147
  },
146
148
  },
@@ -1,5 +1,5 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { getSessionInfo, getSessionSnapshots, getSessions } from "../../proxy/store";
2
+ import { getSessionInfo, getSessionSnapshots, listSessions } from "../../proxy/store";
3
3
 
4
4
  function parsePositiveInt(value: string | null): number | undefined {
5
5
  if (value === null) return undefined;
@@ -37,7 +37,7 @@ export const Route = createFileRoute("/api/sessions")({
37
37
  if (details === "true" || details === "1") {
38
38
  return Response.json(getSessionSnapshots());
39
39
  }
40
- return Response.json(getSessions());
40
+ return Response.json(await listSessions());
41
41
  },
42
42
  },
43
43
  },
@@ -1 +0,0 @@
1
- import{R as s,j as e}from"./main-CIV3Chb5.js";import{P as i}from"./ProxyViewerContainer-BC-z9RPh.js";function t(){const{sessionId:o}=s.useParams();return e.jsx(i,{initialSessionId:o},o)}export{t as component};
@@ -1 +0,0 @@
1
- import{P as o}from"./ProxyViewerContainer-BC-z9RPh.js";import"./main-CIV3Chb5.js";const r=o;export{r as component};