@tonyclaw/agent-inspector 3.0.27 → 3.0.29
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-BVCAAbbu.js → CompareDrawer-Cb5ccPeX.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer-CYOXB81n.js → ProxyViewerContainer-COBe5GTT.js} +198 -198
- package/.output/public/assets/{ReplayDialog-DMX23O5G.js → ReplayDialog-CctnITbt.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-S17pYDRc.js → RequestAnatomy-BrN0RBbL.js} +1 -1
- package/.output/public/assets/{ResponseView-Cf1XrawD.js → ResponseView-Cqq87lX_.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-BdOrVlCi.js → StreamingChunkSequence-CnTa9mCr.js} +1 -1
- package/.output/public/assets/{_sessionId-BnKGmJrR.js → _sessionId-DKM91udC.js} +1 -1
- package/.output/public/assets/{index-gY3I-7-D.js → index-Bq-nqolG.js} +1 -1
- package/.output/public/assets/{index-9fMLCU4d.js → index-DPiESBo2.js} +1 -1
- package/.output/public/assets/{index-B1atk19i.js → index-DTLNVcgc.js} +7 -7
- package/.output/public/assets/index-DliqmwUw.css +1 -0
- package/.output/public/assets/{json-viewer-CpyYpPjx.js → json-viewer-CfaDJwx8.js} +1 -1
- package/.output/server/_libs/lucide-react.mjs +39 -39
- package/.output/server/{_sessionId-eA_h2_s6.mjs → _sessionId-CwGXPD4C.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-K2k9G7r1.mjs → CompareDrawer-R_P1-VNr.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-CbyBpCAj.mjs → ProxyViewerContainer-DUgxRNbj.mjs} +344 -191
- package/.output/server/_ssr/{ReplayDialog-DacI2Iv0.mjs → ReplayDialog-De9wKKzS.mjs} +4 -4
- package/.output/server/_ssr/{RequestAnatomy-gh-pAdkO.mjs → RequestAnatomy-Db4MxUYo.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-BRJKN50-.mjs → ResponseView-8rDXOiNB.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-BQLVEuWY.mjs → StreamingChunkSequence-N9teZHkA.mjs} +2 -2
- package/.output/server/_ssr/{index-DuSWrjbH.mjs → index-D4DvByuW.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-DNs1ZjKI.mjs → json-viewer-BjoHRbjS.mjs} +3 -3
- package/.output/server/_ssr/{router-Dgx6-ot3.mjs → router-BYlePDj8.mjs} +138 -162
- package/.output/server/{_tanstack-start-manifest_v-CoGjnkQD.mjs → _tanstack-start-manifest_v-DMCA8KK3.mjs} +1 -1
- package/.output/server/index.mjs +68 -68
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +57 -1
- package/src/components/clients/ClientLogo.tsx +51 -9
- package/src/components/inspector-pet/InspectorPet.tsx +0 -7
- package/src/components/proxy-viewer/ConversationGroup.tsx +1 -1
- package/src/components/proxy-viewer/ConversationGroupList.tsx +251 -155
- package/src/components/proxy-viewer/ConversationHeader.tsx +43 -23
- package/src/lib/jsonFile.ts +46 -0
- package/src/lib/utils.ts +5 -1
- package/src/proxy/evidenceAnalysis.ts +116 -117
- package/src/proxy/groupEvidenceExporter.ts +4 -8
- package/src/proxy/groupStore.ts +3 -10
- package/src/proxy/providerSecretStore.ts +4 -8
- package/src/proxy/runFailures.ts +4 -10
- package/src/proxy/runStore.ts +4 -11
- package/.output/public/assets/index-CmeF5XXS.css +0 -1
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import { type JSX, useCallback, useMemo, useState } from "react";
|
|
1
|
+
import { type JSX, useCallback, useEffect, useMemo, useState } from "react";
|
|
2
2
|
import { ChevronDown, ChevronRight, Cpu, FolderOpen, Loader2, Monitor } from "lucide-react";
|
|
3
3
|
import type { CapturedLog } from "../../contracts";
|
|
4
4
|
import { cn, formatTokens } from "../../lib/utils";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
ClientLogo,
|
|
7
|
+
clientAppLabel,
|
|
8
|
+
clientAppTypeLabel,
|
|
9
|
+
detectClientApp,
|
|
10
|
+
type ClientApp,
|
|
11
|
+
} from "../clients/ClientLogo";
|
|
6
12
|
import { ConversationGroup, type ConversationGroupProps } from "./ConversationGroup";
|
|
7
13
|
import type { ConversationGroupData } from "./ConversationHeader";
|
|
8
14
|
|
|
9
15
|
const VIRTUALIZE_GROUP_THRESHOLD = 30;
|
|
16
|
+
const COLLAPSED_CLIENTS_STORAGE_KEY = "agent-inspector.hierarchy.collapsedClients";
|
|
17
|
+
const COLLAPSED_PROCESSES_STORAGE_KEY = "agent-inspector.hierarchy.collapsedProcesses";
|
|
10
18
|
|
|
11
19
|
type ConversationGroupListProps = Omit<
|
|
12
20
|
ConversationGroupProps,
|
|
@@ -23,7 +31,7 @@ export type ProcessGroupData = {
|
|
|
23
31
|
logs: CapturedLog[];
|
|
24
32
|
};
|
|
25
33
|
|
|
26
|
-
export type
|
|
34
|
+
export type ClientGroupData = {
|
|
27
35
|
id: string;
|
|
28
36
|
client: ClientApp;
|
|
29
37
|
label: string;
|
|
@@ -40,7 +48,7 @@ type MutableProcessGroup = {
|
|
|
40
48
|
logs: CapturedLog[];
|
|
41
49
|
};
|
|
42
50
|
|
|
43
|
-
type
|
|
51
|
+
type MutableClientGroup = {
|
|
44
52
|
id: string;
|
|
45
53
|
client: ClientApp;
|
|
46
54
|
label: string;
|
|
@@ -109,9 +117,9 @@ function groupClient(group: ConversationGroupData): ClientApp {
|
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
function fallbackClientLabel(userAgent: string | null): string {
|
|
112
|
-
if (userAgent === null || userAgent === "") return "Unknown
|
|
120
|
+
if (userAgent === null || userAgent === "") return "Unknown client";
|
|
113
121
|
const firstToken = userAgent.split(/[ /;]/)[0] ?? "";
|
|
114
|
-
if (firstToken === "") return "Unknown
|
|
122
|
+
if (firstToken === "") return "Unknown client";
|
|
115
123
|
return firstToken;
|
|
116
124
|
}
|
|
117
125
|
|
|
@@ -139,28 +147,56 @@ function sortLogsByTimestamp(logs: readonly CapturedLog[]): CapturedLog[] {
|
|
|
139
147
|
return [...logs].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
140
148
|
}
|
|
141
149
|
|
|
142
|
-
function
|
|
143
|
-
|
|
150
|
+
function latestTimestamp(logs: readonly CapturedLog[]): string {
|
|
151
|
+
let latest = "";
|
|
152
|
+
for (const log of logs) {
|
|
153
|
+
if (log.timestamp > latest) latest = log.timestamp;
|
|
154
|
+
}
|
|
155
|
+
return latest;
|
|
144
156
|
}
|
|
145
157
|
|
|
146
|
-
function
|
|
147
|
-
|
|
158
|
+
export function operationalPriority(logs: readonly CapturedLog[]): number {
|
|
159
|
+
if (hasPendingLogs(logs)) return 0;
|
|
160
|
+
if (
|
|
161
|
+
logs.some(
|
|
162
|
+
(log) =>
|
|
163
|
+
(log.error !== null && log.error !== undefined) ||
|
|
164
|
+
(log.responseStatus !== null &&
|
|
165
|
+
log.responseStatus !== undefined &&
|
|
166
|
+
log.responseStatus >= 400),
|
|
167
|
+
)
|
|
168
|
+
) {
|
|
169
|
+
return 1;
|
|
170
|
+
}
|
|
171
|
+
return 2;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function sortByOperationalPriority<T extends { id: string; logs: readonly CapturedLog[] }>(
|
|
175
|
+
items: readonly T[],
|
|
176
|
+
): T[] {
|
|
177
|
+
return [...items].sort((left, right) => {
|
|
178
|
+
const priorityDifference = operationalPriority(left.logs) - operationalPriority(right.logs);
|
|
179
|
+
if (priorityDifference !== 0) return priorityDifference;
|
|
180
|
+
const recencyDifference = latestTimestamp(right.logs).localeCompare(latestTimestamp(left.logs));
|
|
181
|
+
if (recencyDifference !== 0) return recencyDifference;
|
|
182
|
+
return left.id.localeCompare(right.id);
|
|
183
|
+
});
|
|
148
184
|
}
|
|
149
185
|
|
|
150
186
|
export function buildConversationHierarchy(
|
|
151
187
|
groups: readonly ConversationGroupData[],
|
|
152
|
-
):
|
|
153
|
-
const
|
|
188
|
+
): ClientGroupData[] {
|
|
189
|
+
const clientGroups = new Map<string, MutableClientGroup>();
|
|
154
190
|
|
|
155
191
|
for (const group of groups) {
|
|
156
192
|
const userAgent = firstUserAgent(group.logs);
|
|
157
193
|
const projectFolder = firstClientProjectFolder(group.logs);
|
|
158
194
|
const client = groupClient(group);
|
|
159
195
|
const currentIdeKey = ideKey(client, userAgent, projectFolder);
|
|
160
|
-
let
|
|
196
|
+
let clientGroup = clientGroups.get(currentIdeKey);
|
|
161
197
|
|
|
162
|
-
if (
|
|
163
|
-
|
|
198
|
+
if (clientGroup === undefined) {
|
|
199
|
+
clientGroup = {
|
|
164
200
|
id: currentIdeKey,
|
|
165
201
|
client,
|
|
166
202
|
label: clientLabel(client, userAgent),
|
|
@@ -168,14 +204,14 @@ export function buildConversationHierarchy(
|
|
|
168
204
|
processes: new Map(),
|
|
169
205
|
logs: [],
|
|
170
206
|
};
|
|
171
|
-
|
|
207
|
+
clientGroups.set(currentIdeKey, clientGroup);
|
|
172
208
|
}
|
|
173
209
|
|
|
174
|
-
|
|
210
|
+
clientGroup.logs.push(...group.logs);
|
|
175
211
|
|
|
176
212
|
const clientPid = firstClientPid(group.logs);
|
|
177
213
|
const currentProcessKey = `${currentIdeKey}:${processKey(clientPid, projectFolder)}`;
|
|
178
|
-
let processGroup =
|
|
214
|
+
let processGroup = clientGroup.processes.get(currentProcessKey);
|
|
179
215
|
|
|
180
216
|
if (processGroup === undefined) {
|
|
181
217
|
processGroup = {
|
|
@@ -185,37 +221,37 @@ export function buildConversationHierarchy(
|
|
|
185
221
|
sessions: [],
|
|
186
222
|
logs: [],
|
|
187
223
|
};
|
|
188
|
-
|
|
224
|
+
clientGroup.processes.set(currentProcessKey, processGroup);
|
|
189
225
|
}
|
|
190
226
|
|
|
191
227
|
processGroup.sessions.push(group);
|
|
192
228
|
processGroup.logs.push(...group.logs);
|
|
193
229
|
}
|
|
194
230
|
|
|
195
|
-
const hierarchy:
|
|
231
|
+
const hierarchy: ClientGroupData[] = [];
|
|
196
232
|
|
|
197
|
-
for (const
|
|
198
|
-
const processes: ProcessGroupData[] =
|
|
199
|
-
[...
|
|
233
|
+
for (const clientGroup of clientGroups.values()) {
|
|
234
|
+
const processes: ProcessGroupData[] = sortByOperationalPriority(
|
|
235
|
+
[...clientGroup.processes.values()].map((processGroup) => ({
|
|
200
236
|
id: processGroup.id,
|
|
201
237
|
clientPid: processGroup.clientPid,
|
|
202
238
|
clientProjectFolder: processGroup.clientProjectFolder,
|
|
203
|
-
sessions:
|
|
239
|
+
sessions: sortByOperationalPriority(processGroup.sessions),
|
|
204
240
|
logs: sortLogsByTimestamp(processGroup.logs),
|
|
205
241
|
})),
|
|
206
242
|
);
|
|
207
243
|
|
|
208
244
|
hierarchy.push({
|
|
209
|
-
id:
|
|
210
|
-
client:
|
|
211
|
-
label:
|
|
212
|
-
userAgent:
|
|
245
|
+
id: clientGroup.id,
|
|
246
|
+
client: clientGroup.client,
|
|
247
|
+
label: clientGroup.label,
|
|
248
|
+
userAgent: clientGroup.userAgent,
|
|
213
249
|
processes,
|
|
214
|
-
logs: sortLogsByTimestamp(
|
|
250
|
+
logs: sortLogsByTimestamp(clientGroup.logs),
|
|
215
251
|
});
|
|
216
252
|
}
|
|
217
253
|
|
|
218
|
-
return
|
|
254
|
+
return sortByOperationalPriority(hierarchy);
|
|
219
255
|
}
|
|
220
256
|
|
|
221
257
|
function formatTokenCount(count: number): string {
|
|
@@ -248,6 +284,51 @@ function countSessions(processes: readonly ProcessGroupData[]): number {
|
|
|
248
284
|
return sessions;
|
|
249
285
|
}
|
|
250
286
|
|
|
287
|
+
function readStoredIdSet(storageKey: string): ReadonlySet<string> | null {
|
|
288
|
+
if (typeof window === "undefined") return null;
|
|
289
|
+
try {
|
|
290
|
+
const storedValue = window.localStorage.getItem(storageKey);
|
|
291
|
+
if (storedValue === null) return null;
|
|
292
|
+
const parsed: unknown = JSON.parse(storedValue);
|
|
293
|
+
if (!Array.isArray(parsed)) return null;
|
|
294
|
+
return new Set(parsed.filter((value): value is string => typeof value === "string"));
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function persistIdSet(storageKey: string, values: ReadonlySet<string>): void {
|
|
301
|
+
try {
|
|
302
|
+
window.localStorage.setItem(storageKey, JSON.stringify([...values]));
|
|
303
|
+
} catch {
|
|
304
|
+
// Local storage can be unavailable in hardened browser profiles.
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function removeIds(values: ReadonlySet<string>, ids: ReadonlySet<string>): ReadonlySet<string> {
|
|
309
|
+
let changed = false;
|
|
310
|
+
const next = new Set(values);
|
|
311
|
+
for (const id of ids) {
|
|
312
|
+
if (next.delete(id)) changed = true;
|
|
313
|
+
}
|
|
314
|
+
return changed ? next : values;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function buildDefaultCollapsedProcessIds(
|
|
318
|
+
hierarchy: readonly ClientGroupData[],
|
|
319
|
+
): ReadonlySet<string> {
|
|
320
|
+
const collapsedIds = new Set<string>();
|
|
321
|
+
for (const clientGroup of hierarchy) {
|
|
322
|
+
for (let index = 1; index < clientGroup.processes.length; index++) {
|
|
323
|
+
const processGroup = clientGroup.processes[index];
|
|
324
|
+
if (processGroup !== undefined && !hasPendingLogs(processGroup.logs)) {
|
|
325
|
+
collapsedIds.add(processGroup.id);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return collapsedIds;
|
|
330
|
+
}
|
|
331
|
+
|
|
251
332
|
function shortProjectPath(projectFolder: string | null): string | null {
|
|
252
333
|
if (projectFolder === null || projectFolder === "") return null;
|
|
253
334
|
const parts = projectFolder
|
|
@@ -273,44 +354,30 @@ function SummaryChip({
|
|
|
273
354
|
return (
|
|
274
355
|
<span
|
|
275
356
|
className={cn(
|
|
276
|
-
"
|
|
357
|
+
"inline-flex h-7 items-center gap-1 rounded-md bg-[var(--inspector-surface-low)] px-2 text-xs text-muted-foreground",
|
|
277
358
|
className,
|
|
278
359
|
)}
|
|
279
360
|
>
|
|
280
|
-
<span className="font-mono text-[10px] uppercase
|
|
281
|
-
|
|
282
|
-
</span>
|
|
283
|
-
<span className="font-mono tabular-nums text-slate-100">{value}</span>
|
|
361
|
+
<span className="font-mono text-[10px] uppercase text-muted-foreground/70">{label}</span>
|
|
362
|
+
<span className="font-mono tabular-nums text-foreground">{value}</span>
|
|
284
363
|
</span>
|
|
285
364
|
);
|
|
286
365
|
}
|
|
287
366
|
|
|
288
|
-
function
|
|
289
|
-
count
|
|
290
|
-
label = "Turn open",
|
|
291
|
-
}: {
|
|
292
|
-
count: number;
|
|
293
|
-
label?: string;
|
|
294
|
-
}): JSX.Element {
|
|
367
|
+
function AncestorActivityIndicator({ count }: { count: number }): JSX.Element {
|
|
368
|
+
const label = `${count} open turn${count === 1 ? "" : "s"}`;
|
|
295
369
|
return (
|
|
296
370
|
<span
|
|
297
|
-
className="inline-flex
|
|
298
|
-
|
|
371
|
+
className="relative inline-flex size-6 shrink-0 items-center justify-center rounded-md bg-[var(--inspector-surface-medium)] text-[var(--inspector-status-watch)] ring-1 ring-border/50"
|
|
372
|
+
aria-label={label}
|
|
373
|
+
title={label}
|
|
299
374
|
>
|
|
300
375
|
<Loader2 className="size-3 animate-spin" />
|
|
301
|
-
{
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function LoadingSweep(): JSX.Element {
|
|
308
|
-
return (
|
|
309
|
-
<span
|
|
310
|
-
className="pointer-events-none absolute inset-x-3 bottom-0 h-px overflow-hidden rounded-full bg-white/[0.06]"
|
|
311
|
-
aria-hidden="true"
|
|
312
|
-
>
|
|
313
|
-
<span className="absolute inset-y-0 left-0 w-1/3 animate-pulse rounded-full bg-amber-200/65 shadow-[0_0_14px_rgba(252,211,77,0.32)]" />
|
|
376
|
+
{count > 1 && (
|
|
377
|
+
<span className="absolute translate-x-2 -translate-y-2 rounded bg-card px-0.5 font-mono text-[8px] tabular-nums text-[var(--inspector-status-watch)]">
|
|
378
|
+
{count}
|
|
379
|
+
</span>
|
|
380
|
+
)}
|
|
314
381
|
</span>
|
|
315
382
|
);
|
|
316
383
|
}
|
|
@@ -320,13 +387,45 @@ export function ConversationGroupList({
|
|
|
320
387
|
...groupProps
|
|
321
388
|
}: ConversationGroupListProps): JSX.Element {
|
|
322
389
|
const hierarchy = useMemo(() => buildConversationHierarchy(groups), [groups]);
|
|
323
|
-
const [
|
|
324
|
-
const [collapsedProcessIds, setCollapsedProcessIds] = useState<ReadonlySet<string>>(
|
|
390
|
+
const [collapsedClientIds, setCollapsedClientIds] = useState<ReadonlySet<string>>(
|
|
325
391
|
() => new Set(),
|
|
326
392
|
);
|
|
393
|
+
const [collapsedProcessIds, setCollapsedProcessIds] = useState<ReadonlySet<string>>(() =>
|
|
394
|
+
buildDefaultCollapsedProcessIds(hierarchy),
|
|
395
|
+
);
|
|
396
|
+
const [storedCollapseStateLoaded, setStoredCollapseStateLoaded] = useState(false);
|
|
397
|
+
|
|
398
|
+
useEffect(() => {
|
|
399
|
+
const storedClientIds = readStoredIdSet(COLLAPSED_CLIENTS_STORAGE_KEY);
|
|
400
|
+
const storedProcessIds = readStoredIdSet(COLLAPSED_PROCESSES_STORAGE_KEY);
|
|
401
|
+
if (storedClientIds !== null) setCollapsedClientIds(storedClientIds);
|
|
402
|
+
if (storedProcessIds !== null) setCollapsedProcessIds(storedProcessIds);
|
|
403
|
+
setStoredCollapseStateLoaded(true);
|
|
404
|
+
}, []);
|
|
405
|
+
|
|
406
|
+
useEffect(() => {
|
|
407
|
+
if (!storedCollapseStateLoaded) return;
|
|
408
|
+
persistIdSet(COLLAPSED_CLIENTS_STORAGE_KEY, collapsedClientIds);
|
|
409
|
+
persistIdSet(COLLAPSED_PROCESSES_STORAGE_KEY, collapsedProcessIds);
|
|
410
|
+
}, [collapsedClientIds, collapsedProcessIds, storedCollapseStateLoaded]);
|
|
327
411
|
|
|
328
|
-
|
|
329
|
-
|
|
412
|
+
useEffect(() => {
|
|
413
|
+
const activeClientIds = new Set(
|
|
414
|
+
hierarchy.filter((group) => hasPendingLogs(group.logs)).map((group) => group.id),
|
|
415
|
+
);
|
|
416
|
+
const activeProcessIds = new Set(
|
|
417
|
+
hierarchy.flatMap((group) =>
|
|
418
|
+
group.processes
|
|
419
|
+
.filter((process) => hasPendingLogs(process.logs))
|
|
420
|
+
.map((process) => process.id),
|
|
421
|
+
),
|
|
422
|
+
);
|
|
423
|
+
setCollapsedClientIds((previous) => removeIds(previous, activeClientIds));
|
|
424
|
+
setCollapsedProcessIds((previous) => removeIds(previous, activeProcessIds));
|
|
425
|
+
}, [hierarchy]);
|
|
426
|
+
|
|
427
|
+
const toggleClient = useCallback((id: string) => {
|
|
428
|
+
setCollapsedClientIds((previous) => {
|
|
330
429
|
const next = new Set(previous);
|
|
331
430
|
if (next.has(id)) {
|
|
332
431
|
next.delete(id);
|
|
@@ -350,14 +449,14 @@ export function ConversationGroupList({
|
|
|
350
449
|
}, []);
|
|
351
450
|
|
|
352
451
|
return (
|
|
353
|
-
<div className="space-y-
|
|
354
|
-
{hierarchy.map((
|
|
355
|
-
<
|
|
356
|
-
key={
|
|
357
|
-
group={
|
|
358
|
-
collapsed={
|
|
452
|
+
<div className="space-y-2">
|
|
453
|
+
{hierarchy.map((clientGroup) => (
|
|
454
|
+
<ClientGroupSection
|
|
455
|
+
key={clientGroup.id}
|
|
456
|
+
group={clientGroup}
|
|
457
|
+
collapsed={collapsedClientIds.has(clientGroup.id)}
|
|
359
458
|
collapsedProcessIds={collapsedProcessIds}
|
|
360
|
-
|
|
459
|
+
onToggleClient={() => toggleClient(clientGroup.id)}
|
|
361
460
|
onToggleProcess={toggleProcess}
|
|
362
461
|
conversationProps={groupProps}
|
|
363
462
|
/>
|
|
@@ -366,18 +465,18 @@ export function ConversationGroupList({
|
|
|
366
465
|
);
|
|
367
466
|
}
|
|
368
467
|
|
|
369
|
-
function
|
|
468
|
+
function ClientGroupSection({
|
|
370
469
|
group,
|
|
371
470
|
collapsed,
|
|
372
471
|
collapsedProcessIds,
|
|
373
|
-
|
|
472
|
+
onToggleClient,
|
|
374
473
|
onToggleProcess,
|
|
375
474
|
conversationProps,
|
|
376
475
|
}: {
|
|
377
|
-
group:
|
|
476
|
+
group: ClientGroupData;
|
|
378
477
|
collapsed: boolean;
|
|
379
478
|
collapsedProcessIds: ReadonlySet<string>;
|
|
380
|
-
|
|
479
|
+
onToggleClient: () => void;
|
|
381
480
|
onToggleProcess: (id: string) => void;
|
|
382
481
|
conversationProps: Omit<ConversationGroupProps, "group" | "expanded" | "onExpandedChange">;
|
|
383
482
|
}): JSX.Element {
|
|
@@ -390,64 +489,64 @@ function IdeGroupSection({
|
|
|
390
489
|
return (
|
|
391
490
|
<section
|
|
392
491
|
className={cn(
|
|
393
|
-
"
|
|
394
|
-
isWorking && "bg-card/
|
|
492
|
+
"relative isolate overflow-hidden rounded-[8px] bg-card/65",
|
|
493
|
+
isWorking && "bg-card/80",
|
|
395
494
|
)}
|
|
396
495
|
data-ide-group={group.id}
|
|
397
496
|
>
|
|
398
497
|
<button
|
|
399
498
|
type="button"
|
|
400
|
-
className="
|
|
401
|
-
onClick={
|
|
499
|
+
className="grid w-full min-w-0 grid-cols-[2.25rem_minmax(0,1fr)_auto] items-center gap-2.5 px-3 py-2.5 text-left transition-colors hover:bg-[var(--inspector-surface-low)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
500
|
+
onClick={onToggleClient}
|
|
402
501
|
aria-expanded={!collapsed}
|
|
403
502
|
>
|
|
404
503
|
{group.client === "unknown" ? (
|
|
405
|
-
<span className="
|
|
406
|
-
<Monitor className="size-
|
|
504
|
+
<span className="inline-flex size-9 shrink-0 items-center justify-center rounded-md bg-[var(--inspector-surface-low)] text-muted-foreground">
|
|
505
|
+
<Monitor className="size-4" />
|
|
407
506
|
</span>
|
|
408
507
|
) : (
|
|
409
|
-
<ClientLogo client={group.client} className="size-
|
|
508
|
+
<ClientLogo client={group.client} className="size-9 rounded-md" decorative={true} />
|
|
410
509
|
)}
|
|
411
510
|
|
|
412
|
-
<span className="min-w-0
|
|
511
|
+
<span className="min-w-0">
|
|
413
512
|
<span className="flex min-w-0 items-center gap-2">
|
|
414
513
|
<span
|
|
415
|
-
className="truncate text-sm font-semibold text-
|
|
514
|
+
className="truncate text-sm font-semibold text-foreground"
|
|
416
515
|
title={group.userAgent ?? group.label}
|
|
417
516
|
>
|
|
418
517
|
{group.label}
|
|
419
518
|
</span>
|
|
420
|
-
<span className="rounded bg-
|
|
421
|
-
|
|
519
|
+
<span className="rounded bg-[var(--inspector-surface-medium)] px-1.5 py-0.5 font-mono text-[9px] uppercase text-muted-foreground">
|
|
520
|
+
{clientAppTypeLabel(group.client)}
|
|
422
521
|
</span>
|
|
423
|
-
{isWorking && <
|
|
522
|
+
{isWorking && <AncestorActivityIndicator count={pendingTurnCount} />}
|
|
424
523
|
</span>
|
|
425
|
-
<span className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-
|
|
524
|
+
<span className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-muted-foreground">
|
|
426
525
|
<span>
|
|
427
526
|
{group.processes.length} process{group.processes.length !== 1 ? "es" : ""}
|
|
428
527
|
</span>
|
|
429
528
|
<span>
|
|
430
529
|
{sessionCount} session{sessionCount !== 1 ? "s" : ""}
|
|
431
530
|
</span>
|
|
432
|
-
<span>
|
|
433
|
-
{logCount} log{logCount !== 1 ? "s" : ""}
|
|
434
|
-
</span>
|
|
531
|
+
<span>{logCount} req</span>
|
|
435
532
|
</span>
|
|
436
533
|
</span>
|
|
437
534
|
|
|
438
|
-
<span className="
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
535
|
+
<span className="flex shrink-0 items-center gap-2">
|
|
536
|
+
{collapsed && (
|
|
537
|
+
<span className="hidden shrink-0 items-center gap-2 md:flex">
|
|
538
|
+
<SummaryChip label="IN" value={formatTokenCount(totals.inputTokens)} />
|
|
539
|
+
<SummaryChip label="OUT" value={formatTokenCount(totals.outputTokens)} />
|
|
540
|
+
</span>
|
|
541
|
+
)}
|
|
542
|
+
<span className="inline-flex size-8 shrink-0 items-center justify-center rounded-md bg-[var(--inspector-surface-low)] text-muted-foreground transition-colors group-hover:bg-[var(--inspector-surface-medium)]">
|
|
543
|
+
{collapsed ? <ChevronRight className="size-4" /> : <ChevronDown className="size-4" />}
|
|
544
|
+
</span>
|
|
445
545
|
</span>
|
|
446
546
|
</button>
|
|
447
|
-
{isWorking && <LoadingSweep />}
|
|
448
547
|
|
|
449
548
|
{!collapsed && (
|
|
450
|
-
<div className="space-y-
|
|
549
|
+
<div className="space-y-0.5 px-2 pb-2 pt-0.5">
|
|
451
550
|
{group.processes.map((processGroup) => (
|
|
452
551
|
<ProcessGroupSection
|
|
453
552
|
key={processGroup.id}
|
|
@@ -483,74 +582,71 @@ function ProcessGroupSection({
|
|
|
483
582
|
const processLabel =
|
|
484
583
|
group.clientPid !== null && group.clientPid !== undefined
|
|
485
584
|
? `PID ${group.clientPid}`
|
|
486
|
-
: "
|
|
585
|
+
: "Process unavailable";
|
|
487
586
|
|
|
488
587
|
return (
|
|
489
|
-
<section className="relative pl-
|
|
490
|
-
<
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
<Cpu className="size-4" />
|
|
500
|
-
</span>
|
|
588
|
+
<section className="relative pl-3">
|
|
589
|
+
<button
|
|
590
|
+
type="button"
|
|
591
|
+
className="grid w-full min-w-0 grid-cols-[2rem_minmax(0,1fr)_auto] items-center gap-2.5 rounded-md bg-[var(--inspector-surface-low)] px-2.5 py-2 text-left transition-colors hover:bg-[var(--inspector-surface-medium)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
592
|
+
onClick={onToggle}
|
|
593
|
+
aria-expanded={!collapsed}
|
|
594
|
+
>
|
|
595
|
+
<span className="inline-flex size-8 shrink-0 items-center justify-center text-[var(--inspector-status-watch)]">
|
|
596
|
+
<Cpu className="size-4" />
|
|
597
|
+
</span>
|
|
501
598
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
</span>
|
|
507
|
-
{isWorking && <PendingTurnBadge count={pendingTurnCount} label="Turn open" />}
|
|
508
|
-
{projectLabel !== null && (
|
|
509
|
-
<span
|
|
510
|
-
className="inline-flex min-w-0 items-center gap-1 text-xs text-muted-foreground"
|
|
511
|
-
title={group.clientProjectFolder ?? projectLabel}
|
|
512
|
-
>
|
|
513
|
-
<FolderOpen className="size-3 shrink-0" />
|
|
514
|
-
<span className="truncate font-mono">{projectLabel}</span>
|
|
515
|
-
</span>
|
|
516
|
-
)}
|
|
599
|
+
<span className="min-w-0">
|
|
600
|
+
<span className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
|
|
601
|
+
<span className="font-mono text-xs font-semibold tabular-nums text-foreground">
|
|
602
|
+
{processLabel}
|
|
517
603
|
</span>
|
|
518
|
-
<
|
|
519
|
-
|
|
520
|
-
|
|
604
|
+
{isWorking && <AncestorActivityIndicator count={pendingTurnCount} />}
|
|
605
|
+
{projectLabel !== null && (
|
|
606
|
+
<span
|
|
607
|
+
className="inline-flex min-w-0 items-center gap-1 text-[11px] text-muted-foreground"
|
|
608
|
+
title={group.clientProjectFolder ?? projectLabel}
|
|
609
|
+
>
|
|
610
|
+
<FolderOpen className="size-3 shrink-0" />
|
|
611
|
+
<span className="truncate font-mono">{projectLabel}</span>
|
|
521
612
|
</span>
|
|
522
|
-
|
|
523
|
-
{logCount} log{logCount !== 1 ? "s" : ""}
|
|
524
|
-
</span>
|
|
525
|
-
</span>
|
|
613
|
+
)}
|
|
526
614
|
</span>
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
615
|
+
<span className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-muted-foreground">
|
|
616
|
+
<span>
|
|
617
|
+
{sessionCount} session{sessionCount !== 1 ? "s" : ""}
|
|
618
|
+
</span>
|
|
619
|
+
<span>{logCount} req</span>
|
|
531
620
|
</span>
|
|
621
|
+
</span>
|
|
532
622
|
|
|
533
|
-
|
|
623
|
+
<span className="flex shrink-0 items-center gap-2">
|
|
624
|
+
{collapsed && (
|
|
625
|
+
<span className="hidden shrink-0 items-center gap-2 md:flex">
|
|
626
|
+
<SummaryChip label="IN" value={formatTokenCount(totals.inputTokens)} />
|
|
627
|
+
<SummaryChip label="OUT" value={formatTokenCount(totals.outputTokens)} />
|
|
628
|
+
</span>
|
|
629
|
+
)}
|
|
630
|
+
<span className="inline-flex size-7 shrink-0 items-center justify-center rounded-md bg-[var(--inspector-surface-low)] text-muted-foreground transition-colors hover:bg-[var(--inspector-surface-medium)]">
|
|
534
631
|
{collapsed ? <ChevronRight className="size-4" /> : <ChevronDown className="size-4" />}
|
|
535
632
|
</span>
|
|
536
|
-
</
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
</div>
|
|
633
|
+
</span>
|
|
634
|
+
</button>
|
|
635
|
+
|
|
636
|
+
{!collapsed && (
|
|
637
|
+
<div className="space-y-1 pb-1 pl-3 pr-1 pt-1">
|
|
638
|
+
{group.sessions.map((session) => (
|
|
639
|
+
<ConversationGroup
|
|
640
|
+
key={session.id}
|
|
641
|
+
group={session}
|
|
642
|
+
{...conversationProps}
|
|
643
|
+
showClientIdentity={false}
|
|
644
|
+
showLogClientMetadata={false}
|
|
645
|
+
showProcessMetadata={false}
|
|
646
|
+
/>
|
|
647
|
+
))}
|
|
648
|
+
</div>
|
|
649
|
+
)}
|
|
554
650
|
</section>
|
|
555
651
|
);
|
|
556
652
|
}
|