@tonyclaw/agent-inspector 3.0.26 → 3.0.28
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/cli.js +80 -11
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-SZRUDQc9.js → CompareDrawer-TghZr5t1.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer-BJvwOrSi.js → ProxyViewerContainer-fShBAZjj.js} +201 -201
- package/.output/public/assets/{ReplayDialog-DgfmqiM3.js → ReplayDialog-CZMApaF4.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-CS6c7Qfn.js → RequestAnatomy-DNWccR7O.js} +1 -1
- package/.output/public/assets/{ResponseView-C0Ri_5gm.js → ResponseView-d25MdFhY.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-7dYcHWyZ.js → StreamingChunkSequence-DIFZEnWe.js} +1 -1
- package/.output/public/assets/{_sessionId-D-V84cgw.js → _sessionId-DRfvuE2k.js} +1 -1
- package/.output/public/assets/{index-DgFWCxOs.js → index-B3aAuqWq.js} +1 -1
- package/.output/public/assets/index-BsxAGqMA.css +1 -0
- package/.output/public/assets/{index-DrVJsks2.js → index-D4HkS7Ct.js} +1 -1
- package/.output/public/assets/{index-CSIL7-MK.js → index-DzARL2Fi.js} +3 -3
- package/.output/public/assets/{json-viewer-C9Ji3gYN.js → json-viewer-wuJSKaed.js} +1 -1
- package/.output/server/_libs/lucide-react.mjs +39 -39
- package/.output/server/{_sessionId-BQ6YQOO1.mjs → _sessionId-BJTNiP_O.mjs} +4 -2
- package/.output/server/_ssr/{CompareDrawer-CqNzIeMR.mjs → CompareDrawer-BMRH3N97.mjs} +5 -3
- package/.output/server/_ssr/{ProxyViewerContainer-CMC7Vsbw.mjs → ProxyViewerContainer-AFwcHAIR.mjs} +330 -223
- package/.output/server/_ssr/{ReplayDialog-D9p_LaTr.mjs → ReplayDialog-CbWvRHiN.mjs} +6 -4
- package/.output/server/_ssr/{RequestAnatomy-B4ge3qNZ.mjs → RequestAnatomy-C4kcPyqN.mjs} +4 -2
- package/.output/server/_ssr/{ResponseView-BCqMKApI.mjs → ResponseView-D8VYt-gZ.mjs} +5 -3
- package/.output/server/_ssr/{StreamingChunkSequence-BA4zTLE4.mjs → StreamingChunkSequence-BUUo6vU0.mjs} +4 -2
- package/.output/server/_ssr/{index-CzxhDjlq.mjs → index-DTwMEEXO.mjs} +4 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-B3qV6iEJ.mjs → json-viewer-BqF1pSQD.mjs} +5 -3
- package/.output/server/_ssr/{router-CZe_R31M.mjs → router-C8BOns7y.mjs} +839 -368
- package/.output/server/{_tanstack-start-manifest_v-B50VTLrH.mjs → _tanstack-start-manifest_v-BBf2nRhb.mjs} +1 -1
- package/.output/server/index.mjs +70 -70
- package/README.md +8 -0
- package/package.json +4 -5
- package/src/cli.ts +43 -9
- package/src/components/ProxyViewer.tsx +57 -1
- package/src/components/clients/ClientLogo.tsx +43 -4
- package/src/components/inspector-pet/InspectorPet.tsx +0 -7
- package/src/components/providers/ProviderCard.tsx +3 -36
- package/src/components/providers/ProviderForm.tsx +13 -12
- package/src/components/providers/ProvidersPanel.tsx +2 -2
- package/src/components/providers/SettingsDialog.tsx +3 -3
- package/src/components/proxy-viewer/ConversationGroup.tsx +1 -1
- package/src/components/proxy-viewer/ConversationGroupList.tsx +233 -152
- package/src/components/proxy-viewer/ConversationHeader.tsx +29 -13
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -3
- package/src/contracts/anthropic.ts +0 -5
- package/src/contracts/index.ts +0 -4
- package/src/contracts/log.ts +0 -1
- package/src/contracts/openai.ts +0 -3
- package/src/knowledge/openclawClient.ts +22 -9
- package/src/knowledge/openclawGatewayClient.ts +20 -13
- package/src/lib/alertContract.ts +0 -3
- package/src/lib/ecosystemContract.ts +0 -3
- package/src/lib/export-logs.ts +1 -4
- package/src/lib/jsonFile.ts +46 -0
- package/src/lib/safeFetch.ts +100 -0
- package/src/lib/ssrfGuard.ts +133 -0
- package/src/lib/stopReason.ts +1 -4
- package/src/lib/unknown.ts +27 -0
- package/src/lib/useProviders.ts +1 -1
- package/src/mcp/mode.ts +3 -9
- package/src/mcp/server.ts +5 -14
- package/src/mcp/toolHandlers.ts +45 -54
- package/src/proxy/constants.ts +17 -0
- package/src/proxy/evidenceAnalysis.ts +116 -117
- package/src/proxy/formats/anthropic/handler.ts +5 -1
- package/src/proxy/formats/anthropic/schemas.ts +1 -7
- package/src/proxy/formats/index.ts +1 -1
- package/src/proxy/formats/openai/schemas.ts +0 -3
- package/src/proxy/formats/registry.ts +0 -3
- package/src/proxy/groupEvidenceExporter.ts +4 -8
- package/src/proxy/groupStore.ts +3 -10
- package/src/proxy/handler.ts +91 -12
- package/src/proxy/identityProxy.ts +74 -1
- package/src/proxy/logImporter.ts +82 -12
- package/src/proxy/logIndex.ts +1 -4
- package/src/proxy/providerSecretStore.ts +177 -0
- package/src/proxy/providers.ts +84 -11
- package/src/proxy/runFailures.ts +4 -10
- package/src/proxy/runStore.ts +4 -11
- package/src/proxy/schemas.ts +1 -8
- package/src/proxy/sqliteLogIndex.ts +0 -16
- package/src/proxy/store.ts +1 -5
- package/src/proxy/toolSchemaWarnings.ts +1 -9
- package/src/routes/api/logs.$id.replay.ts +17 -5
- package/src/routes/api/providers.$providerId.model-metadata.ts +11 -1
- package/src/routes/api/providers.$providerId.ts +18 -5
- package/src/routes/api/providers.ts +18 -7
- 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,34 @@ function countSessions(processes: readonly ProcessGroupData[]): number {
|
|
|
248
284
|
return sessions;
|
|
249
285
|
}
|
|
250
286
|
|
|
287
|
+
function readStoredIdSet(storageKey: string): ReadonlySet<string> {
|
|
288
|
+
if (typeof window === "undefined") return new Set();
|
|
289
|
+
try {
|
|
290
|
+
const parsed: unknown = JSON.parse(window.localStorage.getItem(storageKey) ?? "[]");
|
|
291
|
+
if (!Array.isArray(parsed)) return new Set();
|
|
292
|
+
return new Set(parsed.filter((value): value is string => typeof value === "string"));
|
|
293
|
+
} catch {
|
|
294
|
+
return new Set();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function persistIdSet(storageKey: string, values: ReadonlySet<string>): void {
|
|
299
|
+
try {
|
|
300
|
+
window.localStorage.setItem(storageKey, JSON.stringify([...values]));
|
|
301
|
+
} catch {
|
|
302
|
+
// Local storage can be unavailable in hardened browser profiles.
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function removeIds(values: ReadonlySet<string>, ids: ReadonlySet<string>): ReadonlySet<string> {
|
|
307
|
+
let changed = false;
|
|
308
|
+
const next = new Set(values);
|
|
309
|
+
for (const id of ids) {
|
|
310
|
+
if (next.delete(id)) changed = true;
|
|
311
|
+
}
|
|
312
|
+
return changed ? next : values;
|
|
313
|
+
}
|
|
314
|
+
|
|
251
315
|
function shortProjectPath(projectFolder: string | null): string | null {
|
|
252
316
|
if (projectFolder === null || projectFolder === "") return null;
|
|
253
317
|
const parts = projectFolder
|
|
@@ -273,44 +337,30 @@ function SummaryChip({
|
|
|
273
337
|
return (
|
|
274
338
|
<span
|
|
275
339
|
className={cn(
|
|
276
|
-
"bg-white/[0.
|
|
340
|
+
"bg-white/[0.04] inline-flex h-7 items-center gap-1 rounded-md px-2 text-xs text-muted-foreground",
|
|
277
341
|
className,
|
|
278
342
|
)}
|
|
279
343
|
>
|
|
280
|
-
<span className="font-mono text-[10px] uppercase
|
|
281
|
-
{label}
|
|
282
|
-
</span>
|
|
344
|
+
<span className="font-mono text-[10px] uppercase text-white/35">{label}</span>
|
|
283
345
|
<span className="font-mono tabular-nums text-slate-100">{value}</span>
|
|
284
346
|
</span>
|
|
285
347
|
);
|
|
286
348
|
}
|
|
287
349
|
|
|
288
|
-
function
|
|
289
|
-
count
|
|
290
|
-
label = "Turn open",
|
|
291
|
-
}: {
|
|
292
|
-
count: number;
|
|
293
|
-
label?: string;
|
|
294
|
-
}): JSX.Element {
|
|
350
|
+
function AncestorActivityIndicator({ count }: { count: number }): JSX.Element {
|
|
351
|
+
const label = `${count} open turn${count === 1 ? "" : "s"}`;
|
|
295
352
|
return (
|
|
296
353
|
<span
|
|
297
|
-
className="inline-flex
|
|
298
|
-
|
|
354
|
+
className="relative inline-flex size-6 shrink-0 items-center justify-center rounded-md bg-amber-300/[0.08] text-amber-100/90 ring-1 ring-amber-200/10"
|
|
355
|
+
aria-label={label}
|
|
356
|
+
title={label}
|
|
299
357
|
>
|
|
300
358
|
<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)]" />
|
|
359
|
+
{count > 1 && (
|
|
360
|
+
<span className="absolute translate-x-2 -translate-y-2 rounded bg-background px-0.5 font-mono text-[8px] tabular-nums text-amber-100">
|
|
361
|
+
{count}
|
|
362
|
+
</span>
|
|
363
|
+
)}
|
|
314
364
|
</span>
|
|
315
365
|
);
|
|
316
366
|
}
|
|
@@ -320,13 +370,43 @@ export function ConversationGroupList({
|
|
|
320
370
|
...groupProps
|
|
321
371
|
}: ConversationGroupListProps): JSX.Element {
|
|
322
372
|
const hierarchy = useMemo(() => buildConversationHierarchy(groups), [groups]);
|
|
323
|
-
const [
|
|
373
|
+
const [collapsedClientIds, setCollapsedClientIds] = useState<ReadonlySet<string>>(
|
|
374
|
+
() => new Set(),
|
|
375
|
+
);
|
|
324
376
|
const [collapsedProcessIds, setCollapsedProcessIds] = useState<ReadonlySet<string>>(
|
|
325
377
|
() => new Set(),
|
|
326
378
|
);
|
|
379
|
+
const [storedCollapseStateLoaded, setStoredCollapseStateLoaded] = useState(false);
|
|
380
|
+
|
|
381
|
+
useEffect(() => {
|
|
382
|
+
setCollapsedClientIds(readStoredIdSet(COLLAPSED_CLIENTS_STORAGE_KEY));
|
|
383
|
+
setCollapsedProcessIds(readStoredIdSet(COLLAPSED_PROCESSES_STORAGE_KEY));
|
|
384
|
+
setStoredCollapseStateLoaded(true);
|
|
385
|
+
}, []);
|
|
386
|
+
|
|
387
|
+
useEffect(() => {
|
|
388
|
+
if (!storedCollapseStateLoaded) return;
|
|
389
|
+
persistIdSet(COLLAPSED_CLIENTS_STORAGE_KEY, collapsedClientIds);
|
|
390
|
+
persistIdSet(COLLAPSED_PROCESSES_STORAGE_KEY, collapsedProcessIds);
|
|
391
|
+
}, [collapsedClientIds, collapsedProcessIds, storedCollapseStateLoaded]);
|
|
327
392
|
|
|
328
|
-
|
|
329
|
-
|
|
393
|
+
useEffect(() => {
|
|
394
|
+
const activeClientIds = new Set(
|
|
395
|
+
hierarchy.filter((group) => hasPendingLogs(group.logs)).map((group) => group.id),
|
|
396
|
+
);
|
|
397
|
+
const activeProcessIds = new Set(
|
|
398
|
+
hierarchy.flatMap((group) =>
|
|
399
|
+
group.processes
|
|
400
|
+
.filter((process) => hasPendingLogs(process.logs))
|
|
401
|
+
.map((process) => process.id),
|
|
402
|
+
),
|
|
403
|
+
);
|
|
404
|
+
setCollapsedClientIds((previous) => removeIds(previous, activeClientIds));
|
|
405
|
+
setCollapsedProcessIds((previous) => removeIds(previous, activeProcessIds));
|
|
406
|
+
}, [hierarchy]);
|
|
407
|
+
|
|
408
|
+
const toggleClient = useCallback((id: string) => {
|
|
409
|
+
setCollapsedClientIds((previous) => {
|
|
330
410
|
const next = new Set(previous);
|
|
331
411
|
if (next.has(id)) {
|
|
332
412
|
next.delete(id);
|
|
@@ -350,14 +430,14 @@ export function ConversationGroupList({
|
|
|
350
430
|
}, []);
|
|
351
431
|
|
|
352
432
|
return (
|
|
353
|
-
<div className="space-y-
|
|
354
|
-
{hierarchy.map((
|
|
355
|
-
<
|
|
356
|
-
key={
|
|
357
|
-
group={
|
|
358
|
-
collapsed={
|
|
433
|
+
<div className="space-y-2">
|
|
434
|
+
{hierarchy.map((clientGroup) => (
|
|
435
|
+
<ClientGroupSection
|
|
436
|
+
key={clientGroup.id}
|
|
437
|
+
group={clientGroup}
|
|
438
|
+
collapsed={collapsedClientIds.has(clientGroup.id)}
|
|
359
439
|
collapsedProcessIds={collapsedProcessIds}
|
|
360
|
-
|
|
440
|
+
onToggleClient={() => toggleClient(clientGroup.id)}
|
|
361
441
|
onToggleProcess={toggleProcess}
|
|
362
442
|
conversationProps={groupProps}
|
|
363
443
|
/>
|
|
@@ -366,18 +446,18 @@ export function ConversationGroupList({
|
|
|
366
446
|
);
|
|
367
447
|
}
|
|
368
448
|
|
|
369
|
-
function
|
|
449
|
+
function ClientGroupSection({
|
|
370
450
|
group,
|
|
371
451
|
collapsed,
|
|
372
452
|
collapsedProcessIds,
|
|
373
|
-
|
|
453
|
+
onToggleClient,
|
|
374
454
|
onToggleProcess,
|
|
375
455
|
conversationProps,
|
|
376
456
|
}: {
|
|
377
|
-
group:
|
|
457
|
+
group: ClientGroupData;
|
|
378
458
|
collapsed: boolean;
|
|
379
459
|
collapsedProcessIds: ReadonlySet<string>;
|
|
380
|
-
|
|
460
|
+
onToggleClient: () => void;
|
|
381
461
|
onToggleProcess: (id: string) => void;
|
|
382
462
|
conversationProps: Omit<ConversationGroupProps, "group" | "expanded" | "onExpandedChange">;
|
|
383
463
|
}): JSX.Element {
|
|
@@ -390,26 +470,30 @@ function IdeGroupSection({
|
|
|
390
470
|
return (
|
|
391
471
|
<section
|
|
392
472
|
className={cn(
|
|
393
|
-
"bg-card/
|
|
394
|
-
isWorking && "bg-card/
|
|
473
|
+
"bg-card/60 relative isolate overflow-hidden rounded-[8px] shadow-sm",
|
|
474
|
+
isWorking && "bg-card/70",
|
|
395
475
|
)}
|
|
396
476
|
data-ide-group={group.id}
|
|
397
477
|
>
|
|
398
478
|
<button
|
|
399
479
|
type="button"
|
|
400
|
-
className="
|
|
401
|
-
onClick={
|
|
480
|
+
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-white/[0.025] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-cyan-300/40"
|
|
481
|
+
onClick={onToggleClient}
|
|
402
482
|
aria-expanded={!collapsed}
|
|
403
483
|
>
|
|
404
484
|
{group.client === "unknown" ? (
|
|
405
|
-
<span className="bg-white/[0.
|
|
406
|
-
<Monitor className="size-
|
|
485
|
+
<span className="bg-white/[0.04] inline-flex size-9 shrink-0 items-center justify-center rounded-md text-slate-300">
|
|
486
|
+
<Monitor className="size-4" />
|
|
407
487
|
</span>
|
|
408
488
|
) : (
|
|
409
|
-
<ClientLogo
|
|
489
|
+
<ClientLogo
|
|
490
|
+
client={group.client}
|
|
491
|
+
className="size-9 rounded-md text-cyan-100"
|
|
492
|
+
decorative={true}
|
|
493
|
+
/>
|
|
410
494
|
)}
|
|
411
495
|
|
|
412
|
-
<span className="min-w-0
|
|
496
|
+
<span className="min-w-0">
|
|
413
497
|
<span className="flex min-w-0 items-center gap-2">
|
|
414
498
|
<span
|
|
415
499
|
className="truncate text-sm font-semibold text-slate-50"
|
|
@@ -417,37 +501,37 @@ function IdeGroupSection({
|
|
|
417
501
|
>
|
|
418
502
|
{group.label}
|
|
419
503
|
</span>
|
|
420
|
-
<span className="rounded bg-cyan-300/[0.
|
|
421
|
-
|
|
504
|
+
<span className="rounded bg-cyan-300/[0.05] px-1.5 py-0.5 font-mono text-[9px] uppercase text-cyan-100/70">
|
|
505
|
+
{clientAppTypeLabel(group.client)}
|
|
422
506
|
</span>
|
|
423
|
-
{isWorking && <
|
|
507
|
+
{isWorking && <AncestorActivityIndicator count={pendingTurnCount} />}
|
|
424
508
|
</span>
|
|
425
|
-
<span className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-
|
|
509
|
+
<span className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-muted-foreground">
|
|
426
510
|
<span>
|
|
427
511
|
{group.processes.length} process{group.processes.length !== 1 ? "es" : ""}
|
|
428
512
|
</span>
|
|
429
513
|
<span>
|
|
430
514
|
{sessionCount} session{sessionCount !== 1 ? "s" : ""}
|
|
431
515
|
</span>
|
|
432
|
-
<span>
|
|
433
|
-
{logCount} log{logCount !== 1 ? "s" : ""}
|
|
434
|
-
</span>
|
|
516
|
+
<span>{logCount} req</span>
|
|
435
517
|
</span>
|
|
436
518
|
</span>
|
|
437
519
|
|
|
438
|
-
<span className="
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
520
|
+
<span className="flex shrink-0 items-center gap-2">
|
|
521
|
+
{collapsed && (
|
|
522
|
+
<span className="hidden shrink-0 items-center gap-2 md:flex">
|
|
523
|
+
<SummaryChip label="IN" value={formatTokenCount(totals.inputTokens)} />
|
|
524
|
+
<SummaryChip label="OUT" value={formatTokenCount(totals.outputTokens)} />
|
|
525
|
+
</span>
|
|
526
|
+
)}
|
|
527
|
+
<span className="bg-white/[0.04] hover:bg-white/[0.07] inline-flex size-8 shrink-0 items-center justify-center rounded-md text-slate-300">
|
|
528
|
+
{collapsed ? <ChevronRight className="size-4" /> : <ChevronDown className="size-4" />}
|
|
529
|
+
</span>
|
|
445
530
|
</span>
|
|
446
531
|
</button>
|
|
447
|
-
{isWorking && <LoadingSweep />}
|
|
448
532
|
|
|
449
533
|
{!collapsed && (
|
|
450
|
-
<div className="space-y-
|
|
534
|
+
<div className="space-y-1 px-2 pb-2 pt-1">
|
|
451
535
|
{group.processes.map((processGroup) => (
|
|
452
536
|
<ProcessGroupSection
|
|
453
537
|
key={processGroup.id}
|
|
@@ -483,74 +567,71 @@ function ProcessGroupSection({
|
|
|
483
567
|
const processLabel =
|
|
484
568
|
group.clientPid !== null && group.clientPid !== undefined
|
|
485
569
|
? `PID ${group.clientPid}`
|
|
486
|
-
: "
|
|
570
|
+
: "PID unavailable";
|
|
487
571
|
|
|
488
572
|
return (
|
|
489
|
-
<section className="relative pl-
|
|
490
|
-
<
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
<Cpu className="size-4" />
|
|
500
|
-
</span>
|
|
573
|
+
<section className="relative pl-4">
|
|
574
|
+
<button
|
|
575
|
+
type="button"
|
|
576
|
+
className="bg-black/[0.07] grid w-full min-w-0 grid-cols-[2rem_minmax(0,1fr)_auto] items-center gap-2.5 rounded-md px-2.5 py-2 text-left transition-colors hover:bg-white/[0.03] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-cyan-300/35"
|
|
577
|
+
onClick={onToggle}
|
|
578
|
+
aria-expanded={!collapsed}
|
|
579
|
+
>
|
|
580
|
+
<span className="bg-white/[0.04] inline-flex size-8 shrink-0 items-center justify-center rounded-md text-amber-100/85">
|
|
581
|
+
<Cpu className="size-4" />
|
|
582
|
+
</span>
|
|
501
583
|
|
|
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
|
-
)}
|
|
584
|
+
<span className="min-w-0">
|
|
585
|
+
<span className="flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1">
|
|
586
|
+
<span className="font-mono text-xs font-semibold tabular-nums text-slate-100">
|
|
587
|
+
{processLabel}
|
|
517
588
|
</span>
|
|
518
|
-
<
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
589
|
+
{isWorking && <AncestorActivityIndicator count={pendingTurnCount} />}
|
|
590
|
+
{projectLabel !== null && (
|
|
591
|
+
<span
|
|
592
|
+
className="inline-flex min-w-0 items-center gap-1 text-[11px] text-muted-foreground"
|
|
593
|
+
title={group.clientProjectFolder ?? projectLabel}
|
|
594
|
+
>
|
|
595
|
+
<FolderOpen className="size-3 shrink-0" />
|
|
596
|
+
<span className="truncate font-mono">{projectLabel}</span>
|
|
524
597
|
</span>
|
|
525
|
-
|
|
598
|
+
)}
|
|
526
599
|
</span>
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
600
|
+
<span className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-muted-foreground">
|
|
601
|
+
<span>
|
|
602
|
+
{sessionCount} session{sessionCount !== 1 ? "s" : ""}
|
|
603
|
+
</span>
|
|
604
|
+
<span>{logCount} req</span>
|
|
531
605
|
</span>
|
|
606
|
+
</span>
|
|
532
607
|
|
|
533
|
-
|
|
608
|
+
<span className="flex shrink-0 items-center gap-2">
|
|
609
|
+
{collapsed && (
|
|
610
|
+
<span className="hidden shrink-0 items-center gap-2 md:flex">
|
|
611
|
+
<SummaryChip label="IN" value={formatTokenCount(totals.inputTokens)} />
|
|
612
|
+
<SummaryChip label="OUT" value={formatTokenCount(totals.outputTokens)} />
|
|
613
|
+
</span>
|
|
614
|
+
)}
|
|
615
|
+
<span className="bg-white/[0.04] hover:bg-white/[0.07] inline-flex size-7 shrink-0 items-center justify-center rounded-md text-slate-300">
|
|
534
616
|
{collapsed ? <ChevronRight className="size-4" /> : <ChevronDown className="size-4" />}
|
|
535
617
|
</span>
|
|
536
|
-
</
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
</div>
|
|
618
|
+
</span>
|
|
619
|
+
</button>
|
|
620
|
+
|
|
621
|
+
{!collapsed && (
|
|
622
|
+
<div className="space-y-1.5 pb-1 pl-4 pr-1 pt-1.5">
|
|
623
|
+
{group.sessions.map((session) => (
|
|
624
|
+
<ConversationGroup
|
|
625
|
+
key={session.id}
|
|
626
|
+
group={session}
|
|
627
|
+
{...conversationProps}
|
|
628
|
+
showClientIdentity={false}
|
|
629
|
+
showLogClientMetadata={false}
|
|
630
|
+
showProcessMetadata={false}
|
|
631
|
+
/>
|
|
632
|
+
))}
|
|
633
|
+
</div>
|
|
634
|
+
)}
|
|
554
635
|
</section>
|
|
555
636
|
);
|
|
556
637
|
}
|