@tonyclaw/agent-inspector 3.0.3 → 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-CufpM7_P.js → CompareDrawer-Co-35_Xw.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-DPv1rBo7.js +106 -0
- package/.output/public/assets/{ReplayDialog-ClDmxv57.js → ReplayDialog-DghbvKdy.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-DcuIghmp.js → RequestAnatomy-CRXE5v8X.js} +1 -1
- package/.output/public/assets/{ResponseView-C7Tl47j3.js → ResponseView-CzxoQRjp.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-DBK_R540.js → StreamingChunkSequence-DnFyF_Hr.js} +1 -1
- package/.output/public/assets/{_sessionId-D8Ke-EcI.js → _sessionId-DHRhWzKe.js} +1 -1
- package/.output/public/assets/index-D8cruW0P.css +1 -0
- package/.output/public/assets/index-DZyTpd2w.js +1 -0
- package/.output/public/assets/{index-CpWH7b2O.js → index-Dl1oki9E.js} +1 -1
- package/.output/public/assets/{json-viewer-BEifdKM_.js → json-viewer-B84f7oiG.js} +1 -1
- package/.output/public/assets/{main-DDfExS3_.js → main-DViDnJ9X.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +2 -2
- package/.output/server/{_sessionId-Di7z893V.mjs → _sessionId-C43vEAWe.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-ABv-8FMB.mjs → CompareDrawer-DOL6q0jD.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-CGX-1aUr.mjs → ProxyViewerContainer-S9izw3ND.mjs} +572 -272
- package/.output/server/_ssr/{ReplayDialog-DD31ftzG.mjs → ReplayDialog-tqthwJh0.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-54kNHAjH.mjs → RequestAnatomy-CgWDjFPR.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-CRnibtlX.mjs → ResponseView-Dk8Op622.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-Cb5WGfMm.mjs → StreamingChunkSequence-AlS7PK8f.mjs} +2 -2
- package/.output/server/_ssr/{index-BJ-8DomR.mjs → index-BGwvMN2M.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-DB5188I7.mjs → json-viewer-CKHkh5U3.mjs} +2 -2
- package/.output/server/_ssr/{router-BKf70uDr.mjs → router-BJMaHkr1.mjs} +9 -9
- package/.output/server/{_tanstack-start-manifest_v-DUuf3Rgf.mjs → _tanstack-start-manifest_v-C_HvRzgP.mjs} +1 -1
- package/.output/server/index.mjs +66 -66
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +92 -25
- package/src/components/ProxyViewerContainer.tsx +154 -55
- package/src/components/providers/ProviderCard.tsx +413 -230
- package/src/components/proxy-viewer/ConversationGroup.tsx +1 -1
- package/src/components/proxy-viewer/ConversationHeader.tsx +4 -5
- package/src/lib/stopReason.ts +4 -0
- package/.output/public/assets/ProxyViewerContainer-Dppr2zGE.js +0 -106
- package/.output/public/assets/index-BskTYcAH.css +0 -1
- package/.output/public/assets/index-DRHfTEM5.js +0 -1
|
@@ -47,6 +47,8 @@ const LogCursorPageSchema = z.object({
|
|
|
47
47
|
|
|
48
48
|
type LogCursorPage = z.infer<typeof LogCursorPageSchema>;
|
|
49
49
|
|
|
50
|
+
const SessionsResponseSchema = z.array(z.string());
|
|
51
|
+
|
|
50
52
|
type SessionPageRequest =
|
|
51
53
|
| { kind: "newest" }
|
|
52
54
|
| { kind: "oldest" }
|
|
@@ -114,6 +116,8 @@ const DEBOUNCE_MS = 100;
|
|
|
114
116
|
const HASH_SCROLL_ATTEMPTS = 12;
|
|
115
117
|
const HASH_HIGHLIGHT_MS = 1800;
|
|
116
118
|
const SESSION_PAGE_LIMIT = 100;
|
|
119
|
+
const SESSION_RELOAD_PAGE_LIMIT = 500;
|
|
120
|
+
const MAX_SESSION_RELOAD_PAGES = 200;
|
|
117
121
|
|
|
118
122
|
function buildLogsStreamUrl(sessionId: string | undefined): string {
|
|
119
123
|
const params = new URLSearchParams();
|
|
@@ -125,11 +129,15 @@ function buildLogsStreamUrl(sessionId: string | undefined): string {
|
|
|
125
129
|
return query.length > 0 ? `/api/logs/stream?${query}` : "/api/logs/stream";
|
|
126
130
|
}
|
|
127
131
|
|
|
128
|
-
function buildSessionLogsPageUrl(
|
|
132
|
+
function buildSessionLogsPageUrl(
|
|
133
|
+
sessionId: string,
|
|
134
|
+
request: SessionPageRequest,
|
|
135
|
+
limit = SESSION_PAGE_LIMIT,
|
|
136
|
+
): string {
|
|
129
137
|
const params = new URLSearchParams({
|
|
130
138
|
cursor: "1",
|
|
131
139
|
compact: "1",
|
|
132
|
-
limit: String(
|
|
140
|
+
limit: String(limit),
|
|
133
141
|
sessionId,
|
|
134
142
|
});
|
|
135
143
|
|
|
@@ -174,6 +182,20 @@ export function mergeLogsById(
|
|
|
174
182
|
return [...byId.values()].sort((left, right) => left.id - right.id);
|
|
175
183
|
}
|
|
176
184
|
|
|
185
|
+
export function mergeSessionIds(
|
|
186
|
+
previousSessions: readonly string[],
|
|
187
|
+
incomingSessions: readonly string[],
|
|
188
|
+
): string[] {
|
|
189
|
+
const sessions = new Set<string>();
|
|
190
|
+
for (const session of previousSessions) {
|
|
191
|
+
if (session !== "") sessions.add(session);
|
|
192
|
+
}
|
|
193
|
+
for (const session of incomingSessions) {
|
|
194
|
+
if (session !== "") sessions.add(session);
|
|
195
|
+
}
|
|
196
|
+
return [...sessions].sort();
|
|
197
|
+
}
|
|
198
|
+
|
|
177
199
|
export function ProxyViewerContainer({
|
|
178
200
|
initialSessionId,
|
|
179
201
|
}: {
|
|
@@ -195,6 +217,7 @@ export function ProxyViewerContainer({
|
|
|
195
217
|
const [allLogs, setAllLogs] = useState<CapturedLog[]>([]);
|
|
196
218
|
const [selectedSession, setSelectedSession] = useState(initialSessionId ?? "__all__");
|
|
197
219
|
const [selectedModel, setSelectedModel] = useState("__all__");
|
|
220
|
+
const [knownSessions, setKnownSessions] = useState<string[]>([]);
|
|
198
221
|
const { notice, showToast, dismissToast } = useTransientToast();
|
|
199
222
|
const [streamInitialized, setStreamInitialized] = useState(initialSessionId === undefined);
|
|
200
223
|
const [logPage, setLogPage] = useState<LogCursorPage | null>(null);
|
|
@@ -225,9 +248,22 @@ export function ProxyViewerContainer({
|
|
|
225
248
|
() => filterLogs(allLogs, selectedSession, selectedModel),
|
|
226
249
|
[allLogs, selectedSession, selectedModel],
|
|
227
250
|
);
|
|
228
|
-
const sessions = useMemo(
|
|
251
|
+
const sessions = useMemo(
|
|
252
|
+
() => mergeSessionIds(knownSessions, extractSessions(allLogs)),
|
|
253
|
+
[allLogs, knownSessions],
|
|
254
|
+
);
|
|
229
255
|
const models = useMemo(() => extractModels(allLogs), [allLogs]);
|
|
230
256
|
|
|
257
|
+
const refreshSessions = useCallback(() => {
|
|
258
|
+
void fetchJson("/api/sessions", SessionsResponseSchema)
|
|
259
|
+
.then((sessionIds) => {
|
|
260
|
+
setKnownSessions((prev) => mergeSessionIds(prev, sessionIds));
|
|
261
|
+
})
|
|
262
|
+
.catch(() => {
|
|
263
|
+
// Session discovery is best-effort. Live SSE and session reloads still populate this list.
|
|
264
|
+
});
|
|
265
|
+
}, []);
|
|
266
|
+
|
|
231
267
|
const flushUpdates = useCallback(() => {
|
|
232
268
|
flushTimerRef.current = null;
|
|
233
269
|
const updates = pendingUpdatesRef.current;
|
|
@@ -286,9 +322,14 @@ export function ProxyViewerContainer({
|
|
|
286
322
|
logIndexRef.current = buildLogIndex(nextLogs);
|
|
287
323
|
return nextLogs;
|
|
288
324
|
});
|
|
325
|
+
setKnownSessions((prev) => mergeSessionIds(prev, extractSessions(update.logs)));
|
|
289
326
|
setStreamInitialized(true);
|
|
290
327
|
dismissToast();
|
|
291
328
|
} else if (update.type === "update") {
|
|
329
|
+
setKnownSessions((prev) => {
|
|
330
|
+
if (update.log.sessionId === null || update.log.sessionId === "") return prev;
|
|
331
|
+
return mergeSessionIds(prev, [update.log.sessionId]);
|
|
332
|
+
});
|
|
292
333
|
scheduleUpdate(update.log);
|
|
293
334
|
}
|
|
294
335
|
} catch {
|
|
@@ -338,6 +379,64 @@ export function ProxyViewerContainer({
|
|
|
338
379
|
[dismissToast, initialSessionId, showErrorToast],
|
|
339
380
|
);
|
|
340
381
|
|
|
382
|
+
const reloadSessionLogs = useCallback(
|
|
383
|
+
(sessionId: string) => {
|
|
384
|
+
const requestId = sessionPageRequestIdRef.current + 1;
|
|
385
|
+
sessionPageRequestIdRef.current = requestId;
|
|
386
|
+
setSessionPageLoading(true);
|
|
387
|
+
dismissToast();
|
|
388
|
+
|
|
389
|
+
void (async () => {
|
|
390
|
+
const collected: CapturedLog[] = [];
|
|
391
|
+
let page = await fetchJson(
|
|
392
|
+
buildSessionLogsPageUrl(sessionId, { kind: "oldest" }, SESSION_RELOAD_PAGE_LIMIT),
|
|
393
|
+
LogCursorPageSchema,
|
|
394
|
+
);
|
|
395
|
+
collected.push(...page.logs);
|
|
396
|
+
|
|
397
|
+
let loadedPages = 1;
|
|
398
|
+
while (
|
|
399
|
+
page.hasNewer &&
|
|
400
|
+
page.newestLogId !== null &&
|
|
401
|
+
loadedPages < MAX_SESSION_RELOAD_PAGES
|
|
402
|
+
) {
|
|
403
|
+
page = await fetchJson(
|
|
404
|
+
buildSessionLogsPageUrl(
|
|
405
|
+
sessionId,
|
|
406
|
+
{ kind: "newer", afterLogId: page.newestLogId },
|
|
407
|
+
SESSION_RELOAD_PAGE_LIMIT,
|
|
408
|
+
),
|
|
409
|
+
LogCursorPageSchema,
|
|
410
|
+
);
|
|
411
|
+
collected.push(...page.logs);
|
|
412
|
+
loadedPages += 1;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (sessionPageRequestIdRef.current !== requestId) return;
|
|
416
|
+
setAllLogs((prev) => {
|
|
417
|
+
const next = mergeLogsById(prev, collected);
|
|
418
|
+
logIndexRef.current = buildLogIndex(next);
|
|
419
|
+
return next;
|
|
420
|
+
});
|
|
421
|
+
setKnownSessions((prev) => mergeSessionIds(prev, [sessionId]));
|
|
422
|
+
setSessionPageLoading(false);
|
|
423
|
+
if (page.hasNewer) {
|
|
424
|
+
showToast({
|
|
425
|
+
message: `Loaded ${String(collected.length)} logs for this session. More history is available.`,
|
|
426
|
+
tone: "success",
|
|
427
|
+
});
|
|
428
|
+
} else {
|
|
429
|
+
dismissToast();
|
|
430
|
+
}
|
|
431
|
+
})().catch((err: unknown) => {
|
|
432
|
+
if (sessionPageRequestIdRef.current !== requestId) return;
|
|
433
|
+
setSessionPageLoading(false);
|
|
434
|
+
showErrorToast(err instanceof Error ? err.message : "Failed to reload session logs");
|
|
435
|
+
});
|
|
436
|
+
},
|
|
437
|
+
[dismissToast, showErrorToast, showToast],
|
|
438
|
+
);
|
|
439
|
+
|
|
341
440
|
useEffect(() => {
|
|
342
441
|
if (initialSessionId === undefined) {
|
|
343
442
|
setSessionMemberships([]);
|
|
@@ -365,11 +464,21 @@ export function ProxyViewerContainer({
|
|
|
365
464
|
};
|
|
366
465
|
}, [initialSessionId]);
|
|
367
466
|
|
|
467
|
+
useEffect(() => {
|
|
468
|
+
refreshSessions();
|
|
469
|
+
}, [refreshSessions]);
|
|
470
|
+
|
|
368
471
|
useEffect(() => {
|
|
369
472
|
if (initialSessionId === undefined) return;
|
|
370
473
|
loadSessionPage({ kind: "newest" });
|
|
371
474
|
}, [initialSessionId, loadSessionPage]);
|
|
372
475
|
|
|
476
|
+
useEffect(() => {
|
|
477
|
+
if (initialSessionId !== undefined) return;
|
|
478
|
+
if (selectedSession === "__all__") return;
|
|
479
|
+
reloadSessionLogs(selectedSession);
|
|
480
|
+
}, [initialSessionId, reloadSessionLogs, selectedSession]);
|
|
481
|
+
|
|
373
482
|
useEffect(() => {
|
|
374
483
|
if (initialSessionId !== undefined) return undefined;
|
|
375
484
|
connectSSE();
|
|
@@ -443,64 +552,45 @@ export function ProxyViewerContainer({
|
|
|
443
552
|
}, [logs.length]);
|
|
444
553
|
|
|
445
554
|
const handleClearAll = useCallback(() => {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
? {}
|
|
457
|
-
: {
|
|
458
|
-
headers: { "Content-Type": "application/json" },
|
|
459
|
-
body,
|
|
460
|
-
}),
|
|
461
|
-
});
|
|
462
|
-
if (!res.ok) {
|
|
463
|
-
showErrorToast("Failed to clear logs");
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
logIndexRef.current.clear();
|
|
467
|
-
setAllLogs([]);
|
|
468
|
-
dismissToast();
|
|
469
|
-
} catch (err) {
|
|
470
|
-
showErrorToast(err instanceof Error ? err.message : "Unknown error clearing logs");
|
|
471
|
-
}
|
|
472
|
-
})();
|
|
473
|
-
}, [allLogs, dismissToast, initialSessionId, showErrorToast]);
|
|
555
|
+
const clearedSessions = extractSessions(allLogs);
|
|
556
|
+
logIndexRef.current.clear();
|
|
557
|
+
setAllLogs([]);
|
|
558
|
+
setLogPage(null);
|
|
559
|
+
setKnownSessions((prev) => mergeSessionIds(prev, clearedSessions));
|
|
560
|
+
showToast({
|
|
561
|
+
message: "Cleared the current view. Stored logs are still reloadable.",
|
|
562
|
+
tone: "success",
|
|
563
|
+
});
|
|
564
|
+
}, [allLogs, showToast]);
|
|
474
565
|
|
|
475
566
|
const handleClearGroup = useCallback(
|
|
476
567
|
(ids: number[]) => {
|
|
477
568
|
if (ids.length === 0) return;
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
const idSet = new Set(ids);
|
|
490
|
-
setAllLogs((prev) => {
|
|
491
|
-
const remaining = prev.filter((l) => !idSet.has(l.id));
|
|
492
|
-
logIndexRef.current = buildLogIndex(remaining);
|
|
493
|
-
return remaining;
|
|
494
|
-
});
|
|
495
|
-
dismissToast();
|
|
496
|
-
} catch (err) {
|
|
497
|
-
showErrorToast(err instanceof Error ? err.message : "Unknown error clearing group");
|
|
498
|
-
}
|
|
499
|
-
})();
|
|
569
|
+
const idSet = new Set(ids);
|
|
570
|
+
setAllLogs((prev) => {
|
|
571
|
+
const removed = prev.filter((log) => idSet.has(log.id));
|
|
572
|
+
const remaining = prev.filter((log) => !idSet.has(log.id));
|
|
573
|
+
setKnownSessions((known) => mergeSessionIds(known, extractSessions(removed)));
|
|
574
|
+
logIndexRef.current = buildLogIndex(remaining);
|
|
575
|
+
return remaining;
|
|
576
|
+
});
|
|
577
|
+
showToast({ message: "Cleared this group from the current view.", tone: "success" });
|
|
500
578
|
},
|
|
501
|
-
[
|
|
579
|
+
[showToast],
|
|
502
580
|
);
|
|
503
581
|
|
|
582
|
+
const handleReloadSession = useCallback(() => {
|
|
583
|
+
if (initialSessionId !== undefined) {
|
|
584
|
+
loadSessionPage({ kind: "newest" });
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
if (selectedSession === "__all__") {
|
|
588
|
+
refreshSessions();
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
reloadSessionLogs(selectedSession);
|
|
592
|
+
}, [initialSessionId, loadSessionPage, refreshSessions, reloadSessionLogs, selectedSession]);
|
|
593
|
+
|
|
504
594
|
const handleImportLogs = useCallback(
|
|
505
595
|
async (file: File): Promise<ImportLogsResponse> => {
|
|
506
596
|
const formData = new FormData();
|
|
@@ -588,10 +678,19 @@ export function ProxyViewerContainer({
|
|
|
588
678
|
onSessionChange={setSelectedSession}
|
|
589
679
|
onModelChange={setSelectedModel}
|
|
590
680
|
onClearAll={handleClearAll}
|
|
681
|
+
onReloadSession={
|
|
682
|
+
initialSessionId !== undefined || selectedSession !== "__all__"
|
|
683
|
+
? handleReloadSession
|
|
684
|
+
: undefined
|
|
685
|
+
}
|
|
591
686
|
onClearGroup={handleClearGroup}
|
|
592
687
|
onNotify={showToast}
|
|
593
688
|
onImportLogs={initialSessionId === undefined ? handleImportLogs : undefined}
|
|
594
|
-
isLoading={
|
|
689
|
+
isLoading={
|
|
690
|
+
initialSessionId === undefined
|
|
691
|
+
? !streamInitialized || sessionPageLoading
|
|
692
|
+
: sessionPageLoading
|
|
693
|
+
}
|
|
595
694
|
pagination={pagination}
|
|
596
695
|
sessionMemberships={sessionMemberships}
|
|
597
696
|
viewMode={viewMode}
|