@tonyclaw/agent-inspector 2.1.6 → 2.1.8

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 (39) hide show
  1. package/.output/cli.js +274 -140
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-Djazxhew.js → CompareDrawer-CHkT7kun.js} +1 -1
  4. package/.output/public/assets/{ProxyViewerContainer-BC-z9RPh.js → ProxyViewerContainer-BiZ2_tMC.js} +36 -36
  5. package/.output/public/assets/{ReplayDialog-BaD4R6ac.js → ReplayDialog-C85cakjx.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-w6uGdqYp.js → RequestAnatomy-Dn_pMMoX.js} +1 -1
  7. package/.output/public/assets/{ResponseView-VPed-Yif.js → ResponseView-DzcxCUBf.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-CPB8FgHI.js → StreamingChunkSequence-BamtvkgZ.js} +1 -1
  9. package/.output/public/assets/_sessionId-xBBbFH1M.js +1 -0
  10. package/.output/public/assets/index-6udjwQO1.js +1 -0
  11. package/.output/public/assets/{main-CIV3Chb5.js → main-Dc2qVWpb.js} +2 -2
  12. package/.output/server/{_sessionId-CIXQGim9.mjs → _sessionId-BnimGger.mjs} +3 -3
  13. package/.output/server/_ssr/{CompareDrawer-_uFgjkB2.mjs → CompareDrawer-CwVFykVF.mjs} +3 -3
  14. package/.output/server/_ssr/{ProxyViewerContainer-D8YjQDNj.mjs → ProxyViewerContainer-X3S2TWBJ.mjs} +236 -136
  15. package/.output/server/_ssr/{ReplayDialog-CFwKM7jc.mjs → ReplayDialog-BxC12XUj.mjs} +4 -4
  16. package/.output/server/_ssr/{RequestAnatomy-2Vr84GxT.mjs → RequestAnatomy-CWWS8Qh7.mjs} +3 -3
  17. package/.output/server/_ssr/{ResponseView-CSBWBHRJ.mjs → ResponseView-BewErHF-.mjs} +3 -3
  18. package/.output/server/_ssr/{StreamingChunkSequence-Dr4JApqs.mjs → StreamingChunkSequence-BlmCVryW.mjs} +3 -3
  19. package/.output/server/_ssr/{index-WnsY1fh_.mjs → index-5qbGuoFX.mjs} +3 -3
  20. package/.output/server/_ssr/index.mjs +2 -2
  21. package/.output/server/_ssr/{router-sy38X5AX.mjs → router-s78S54MU.mjs} +554 -124
  22. package/.output/server/{_tanstack-start-manifest_v-Dde-eACV.mjs → _tanstack-start-manifest_v-DTxpr3KE.mjs} +1 -1
  23. package/.output/server/index.mjs +61 -61
  24. package/README.md +10 -6
  25. package/package.json +1 -1
  26. package/src/cli/detect-tools.ts +2 -1
  27. package/src/cli/onboard.ts +334 -167
  28. package/src/components/groups/GroupsDialog.tsx +9 -4
  29. package/src/components/providers/SettingsDialog.tsx +67 -0
  30. package/src/components/proxy-viewer/ConversationGroup.tsx +28 -0
  31. package/src/components/proxy-viewer/ConversationHeader.tsx +34 -0
  32. package/src/components/proxy-viewer/LogEntryHeader.tsx +3 -12
  33. package/src/mcp/server.ts +18 -4
  34. package/src/proxy/sessionArchive.ts +410 -0
  35. package/src/proxy/store.ts +121 -6
  36. package/src/routes/api/logs.ts +2 -0
  37. package/src/routes/api/sessions.ts +2 -2
  38. package/.output/public/assets/_sessionId-mnr0O0Ri.js +0 -1
  39. package/.output/public/assets/index-DH1DjJOl.js +0 -1
@@ -1016,9 +1016,14 @@ function MemberRow({
1016
1016
  <ExternalLink className="size-3 shrink-0 text-muted-foreground" />
1017
1017
  </a>
1018
1018
  ) : (
1019
- <span
1020
- className="inline-flex max-w-full min-w-0 items-center gap-2 font-mono text-xs text-foreground"
1021
- title={`Evidence snapshot for ${sessionLabel}`}
1019
+ <button
1020
+ type="button"
1021
+ className="inline-flex max-w-full min-w-0 items-center gap-2 rounded-sm font-mono text-xs text-foreground outline-none hover:underline focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
1022
+ title={`Show evidence snapshot for ${sessionLabel}`}
1023
+ aria-label={`Show evidence snapshot for ${sessionLabel}`}
1024
+ onClick={() => {
1025
+ onToggleDetails(member.id);
1026
+ }}
1022
1027
  >
1023
1028
  <span className="min-w-0 truncate">{sessionLabel}</span>
1024
1029
  <Badge
@@ -1027,7 +1032,7 @@ function MemberRow({
1027
1032
  >
1028
1033
  evidence
1029
1034
  </Badge>
1030
- </span>
1035
+ </button>
1031
1036
  )}
1032
1037
  </div>
1033
1038
  <div className="mt-0.5 truncate text-[11px] text-muted-foreground">
@@ -123,6 +123,9 @@ type LogStorageStats = {
123
123
  chunkDir: string;
124
124
  chunkFileCount: number;
125
125
  chunkBytes: number;
126
+ sessionArchiveDir: string;
127
+ sessionArchiveFileCount: number;
128
+ sessionArchiveBytes: number;
126
129
  };
127
130
 
128
131
  const LogStorageStatsSchema = z.object({
@@ -133,6 +136,9 @@ const LogStorageStatsSchema = z.object({
133
136
  chunkDir: z.string(),
134
137
  chunkFileCount: z.number(),
135
138
  chunkBytes: z.number(),
139
+ sessionArchiveDir: z.string(),
140
+ sessionArchiveFileCount: z.number(),
141
+ sessionArchiveBytes: z.number(),
136
142
  });
137
143
 
138
144
  function formatBytes(bytes: number): string {
@@ -213,6 +219,13 @@ function StorageSettingsTab(): JSX.Element {
213
219
  copiedId={copiedId}
214
220
  onCopy={handleCopy}
215
221
  />
222
+ <CopyableSetupValue
223
+ id="session-archive-dir"
224
+ label="Session archive storage path"
225
+ value={stats.sessionArchiveDir}
226
+ copiedId={copiedId}
227
+ onCopy={handleCopy}
228
+ />
216
229
  <div className="rounded-md border border-border bg-muted/20 px-3 py-2">
217
230
  <div className="font-medium">In-memory logs</div>
218
231
  <div className="mt-1 font-mono text-muted-foreground">
@@ -231,6 +244,13 @@ function StorageSettingsTab(): JSX.Element {
231
244
  {stats.chunkFileCount.toLocaleString()} files / {formatBytes(stats.chunkBytes)}
232
245
  </div>
233
246
  </div>
247
+ <div className="rounded-md border border-border bg-muted/20 px-3 py-2">
248
+ <div className="font-medium">Session archives</div>
249
+ <div className="mt-1 font-mono text-muted-foreground">
250
+ {stats.sessionArchiveFileCount.toLocaleString()} files /{" "}
251
+ {formatBytes(stats.sessionArchiveBytes)}
252
+ </div>
253
+ </div>
234
254
  </div>
235
255
  )}
236
256
 
@@ -311,6 +331,18 @@ function OnboardingSettingsTab(): JSX.Element {
311
331
  },
312
332
  { id: "opencode-verify", label: "OpenCode verify", value: "opencode mcp list" },
313
333
  { id: "opencode-proxy", label: "OpenCode proxy", value: `LLM_BASE_URL=${origin}/proxy` },
334
+ {
335
+ id: "mimo-config",
336
+ label: "MiMo Code config",
337
+ value: "agent-inspector onboard --mimo-only",
338
+ },
339
+ {
340
+ id: "mimo-status",
341
+ label: "MiMo Code status",
342
+ value: "agent-inspector onboard --status --mimo-only",
343
+ },
344
+ { id: "mimo-verify", label: "MiMo Code verify", value: "mimo mcp list" },
345
+ { id: "mimo-proxy", label: "MiMo Code proxy", value: `OPENAI_BASE_URL=${origin}/proxy` },
314
346
  { id: "mcp", label: "MCP URL", value: `${origin}/api/mcp` },
315
347
  { id: "proxy", label: "Proxy URL", value: `${origin}/proxy` },
316
348
  { id: "anthropic", label: "Anthropic base", value: `ANTHROPIC_BASE_URL=${origin}/proxy` },
@@ -382,6 +414,24 @@ function OnboardingSettingsTab(): JSX.Element {
382
414
  .
383
415
  </span>
384
416
  </div>
417
+ <div className="flex items-start gap-2">
418
+ <Check className="mt-0.5 size-3.5 text-emerald-500" />
419
+ <span>
420
+ MiMo Code onboarding safely merges{" "}
421
+ <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px] text-foreground">
422
+ mcp.agent-inspector
423
+ </code>{" "}
424
+ into{" "}
425
+ <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px] text-foreground">
426
+ mimocode.jsonc
427
+ </code>
428
+ , then route OpenAI-format traffic with{" "}
429
+ <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px] text-foreground">
430
+ OPENAI_BASE_URL
431
+ </code>
432
+ .
433
+ </span>
434
+ </div>
385
435
  <div className="flex items-start gap-2">
386
436
  <Check className="mt-0.5 size-3.5 text-emerald-500" />
387
437
  <span>
@@ -478,6 +528,23 @@ function McpSettingsTab(): JSX.Element {
478
528
  2,
479
529
  ),
480
530
  },
531
+ {
532
+ id: "mimo-local",
533
+ label: "MiMo Code local MCP",
534
+ value: JSON.stringify(
535
+ {
536
+ mcp: {
537
+ "agent-inspector": {
538
+ type: "local",
539
+ command: ["agent-inspector-mcp", "stdio", "--url", endpoint],
540
+ enabled: true,
541
+ },
542
+ },
543
+ },
544
+ null,
545
+ 2,
546
+ ),
547
+ },
481
548
  {
482
549
  id: "codex-toml",
483
550
  label: "Codex TOML",
@@ -8,6 +8,7 @@ import {
8
8
  hasMixedApiFormat,
9
9
  type ConversationGroupData,
10
10
  } from "./ConversationHeader";
11
+ import { detectProvider } from "../providers/ProviderLogo";
11
12
  import { TurnGroup } from "./TurnGroup";
12
13
  import { AgentTraceSummary } from "./AgentTraceSummary";
13
14
  import type { CacheTrendEntry } from "./cacheTrend";
@@ -56,6 +57,31 @@ function computeStats(logs: CapturedLog[]): {
56
57
  return { totalInputTokens: totalInput, totalOutputTokens: totalOutput };
57
58
  }
58
59
 
60
+ /** Bucket each log's model under its detected provider and return one entry
61
+ * per provider with the deduplicated model names. Used by the session
62
+ * header to render one logo per provider used in the session. */
63
+ function collectProviders(logs: CapturedLog[]): {
64
+ provider: import("../providers/ProviderLogo").Provider;
65
+ models: string[];
66
+ }[] {
67
+ const buckets = new Map<import("../providers/ProviderLogo").Provider, Set<string>>();
68
+ for (const log of logs) {
69
+ if (log.model === null) continue;
70
+ const provider = detectProvider(log.model);
71
+ if (provider === "unknown") continue;
72
+ let models = buckets.get(provider);
73
+ if (models === undefined) {
74
+ models = new Set();
75
+ buckets.set(provider, models);
76
+ }
77
+ models.add(log.model);
78
+ }
79
+ return [...buckets.entries()].map(([provider, models]) => ({
80
+ provider,
81
+ models: [...models],
82
+ }));
83
+ }
84
+
59
85
  export const ConversationGroup = memo(function ({
60
86
  group,
61
87
  viewMode = "simple",
@@ -84,6 +110,7 @@ export const ConversationGroup = memo(function ({
84
110
  [onExpandedChange],
85
111
  );
86
112
  const stats = useMemo(() => computeStats(group.logs), [group.logs]);
113
+ const providers = useMemo(() => collectProviders(group.logs), [group.logs]);
87
114
  const startTime = group.logs[0]?.timestamp ?? new Date().toISOString();
88
115
  const endTime = group.logs[group.logs.length - 1]?.timestamp ?? new Date().toISOString();
89
116
  const mixed = hasMixedApiFormat(group.logs);
@@ -122,6 +149,7 @@ export const ConversationGroup = memo(function ({
122
149
  expanded={expanded}
123
150
  onToggle={() => setExpanded(!expanded)}
124
151
  hideApiFormat={mixed}
152
+ providers={providers}
125
153
  isLoading={isLoading}
126
154
  userAgent={group.logs[0]?.userAgent ?? null}
127
155
  timeDisplayFormat={timeDisplayFormat}
@@ -18,6 +18,8 @@ import { cn, formatTokens } from "../../lib/utils";
18
18
  import type { CapturedLog } from "../../contracts";
19
19
  import { Badge } from "../ui/badge";
20
20
  import { ConfirmDialog } from "../ui/confirm-dialog";
21
+ import { ProviderLogo, type Provider } from "../providers/ProviderLogo";
22
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip";
21
23
  import { resolveLogFormat } from "./log-formats";
22
24
 
23
25
  const API_FORMAT_LABELS: Record<"anthropic" | "openai" | "unknown", string> = {
@@ -39,6 +41,11 @@ export type ConversationHeaderProps = {
39
41
  /** Hide the API format badge on the header (used when the group contains
40
42
  * mixed formats - the per-log badges are shown instead). */
41
43
  hideApiFormat?: boolean;
44
+ /** Unique providers whose models appear in this group, with their distinct
45
+ * model names. Rendered as a row of provider logos next to the header —
46
+ * one per provider so model-switching inside a single session is visible
47
+ * at a glance. `undefined` hides the strip (default). */
48
+ providers?: { provider: Provider; models: string[] }[];
42
49
  /** When true and the group is collapsed, show a spinner instead of the
43
50
  * expand chevron to indicate an in-flight request inside the group. */
44
51
  isLoading?: boolean;
@@ -62,6 +69,7 @@ export function ConversationHeader({
62
69
  expanded,
63
70
  onToggle,
64
71
  hideApiFormat = false,
72
+ providers,
65
73
  isLoading = false,
66
74
  userAgent,
67
75
  timeDisplayFormat,
@@ -152,6 +160,32 @@ export function ConversationHeader({
152
160
  </Badge>
153
161
  )}
154
162
 
163
+ {/* Provider logos — one per unique provider used in this session. When
164
+ a session switched models mid-stream (e.g. claude-3-5 → deepseek-v4)
165
+ multiple logos appear side-by-side; tooltip lists every distinct
166
+ model name. */}
167
+ {providers !== undefined && providers.length > 0 && (
168
+ <TooltipProvider delayDuration={150}>
169
+ <span
170
+ className="flex items-center gap-0.5 shrink-0"
171
+ aria-label="Models used in this session"
172
+ >
173
+ {providers.map(({ provider, models }) => (
174
+ <Tooltip key={provider}>
175
+ <TooltipTrigger asChild>
176
+ <span className="shrink-0">
177
+ <ProviderLogo provider={provider} className="size-4" />
178
+ </span>
179
+ </TooltipTrigger>
180
+ <TooltipContent>
181
+ {models.length === 1 ? models[0] : models.join(", ")}
182
+ </TooltipContent>
183
+ </Tooltip>
184
+ ))}
185
+ </span>
186
+ </TooltipProvider>
187
+ )}
188
+
155
189
  {/* Time range */}
156
190
  <span className="flex items-center gap-1 text-muted-foreground text-xs shrink-0">
157
191
  <Clock className="size-3" />
@@ -30,7 +30,6 @@ import { cn, formatTokens, getStatusCategory, type StatusCategory } from "../../
30
30
  import type { CapturedLog } from "../../contracts";
31
31
  import { Badge } from "../ui/badge";
32
32
  import { Button } from "../ui/button";
33
- import { ProviderLogo, detectProvider } from "../providers/ProviderLogo";
34
33
  import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
35
34
  import type { CacheTrend } from "./cacheTrend";
36
35
 
@@ -199,17 +198,9 @@ export const LogEntryHeader = memo(function ({
199
198
  </span>
200
199
  </span>
201
200
 
202
- {/* Model logo icon only, model name in tooltip */}
203
- {log.model !== null && (
204
- <Tooltip>
205
- <TooltipTrigger asChild>
206
- <span className="shrink-0">
207
- <ProviderLogo provider={detectProvider(log.model)} className="size-4" />
208
- </span>
209
- </TooltipTrigger>
210
- <TooltipContent>{log.model}</TooltipContent>
211
- </Tooltip>
212
- )}
201
+ {/* Model logo moved to the session/conversation header per-log rows
202
+ no longer render the logo (sessions with many logs would show the
203
+ same provider logo dozens of times). */}
213
204
 
214
205
  {/* Response Status — only shown for non-200 or pending. Each category
215
206
  carries a distinct icon in addition to color so the meaning is
package/src/mcp/server.ts CHANGED
@@ -316,6 +316,15 @@ function mcpConfig(): Record<string, unknown> {
316
316
  },
317
317
  },
318
318
  },
319
+ mimoCodeLocal: {
320
+ mcp: {
321
+ "agent-inspector": {
322
+ type: "local",
323
+ command: ["agent-inspector-mcp", "stdio", "--url", endpoint],
324
+ enabled: true,
325
+ },
326
+ },
327
+ },
319
328
  networkNote:
320
329
  "If the MCP client runs in another container or host, replace localhost with an address reachable from that client. Configure only one transport for the same Inspector instance.",
321
330
  };
@@ -739,7 +748,11 @@ function registerTools(server: McpServer): void {
739
748
  title: z.string().min(1).optional().describe("Human-readable run title."),
740
749
  task: z.string().nullable().optional().describe("Task statement or benchmark case."),
741
750
  project: z.string().nullable().optional().describe("Project or repository under test."),
742
- agent: z.string().nullable().optional().describe("Coding agent name, e.g. Codex/OpenCode."),
751
+ agent: z
752
+ .string()
753
+ .nullable()
754
+ .optional()
755
+ .describe("Coding agent name, e.g. Codex/OpenCode/MiMo Code."),
743
756
  status: InspectorRunStatusSchema.optional().describe("Initial run status."),
744
757
  tags: z.array(z.string()).optional().describe("Free-form run labels."),
745
758
  metadata: z.record(z.string(), JsonValueSchema).optional().describe("JSON metadata."),
@@ -860,7 +873,7 @@ function registerTools(server: McpServer): void {
860
873
  {
861
874
  title: "Create or declare an Inspector group",
862
875
  description:
863
- "Creates an evaluation/batch group that external systems can use to aggregate multiple OpenCode/Codex sessions across models such as MiniMax, DeepSeek, or GLM.",
876
+ "Creates an evaluation/batch group that external systems can use to aggregate multiple Codex, OpenCode, or MiMo Code sessions across models such as MiniMax, DeepSeek, or GLM.",
864
877
  inputSchema: z.object({
865
878
  groupId: z.string().min(1).optional().describe("Stable group id. Generated when omitted."),
866
879
  title: z.string().min(1).optional().describe("Human-readable group title."),
@@ -922,7 +935,7 @@ function registerTools(server: McpServer): void {
922
935
  {
923
936
  title: "Attach a session to an Inspector group",
924
937
  description:
925
- "Adds or updates one group member. Use it after each OpenCode process discovers its session id, or while an evaluation matrix is running to attach model/provider labels.",
938
+ "Adds or updates one group member. Use it after each coding-agent process discovers its session id, or while an evaluation matrix is running to attach model/provider labels.",
926
939
  inputSchema: AddInspectorGroupSessionInputSchema.extend({
927
940
  groupId: z.string().min(1).describe("The Inspector group id."),
928
941
  }),
@@ -1204,7 +1217,8 @@ function registerResources(server: McpServer): void {
1204
1217
  "inspector://mcp/config",
1205
1218
  {
1206
1219
  title: "Inspector MCP client config",
1207
- description: "Streamable HTTP, stdio bridge, and OpenCode MCP configuration snippets.",
1220
+ description:
1221
+ "Streamable HTTP, stdio bridge, OpenCode, and MiMo Code MCP configuration snippets.",
1208
1222
  mimeType: "application/json",
1209
1223
  },
1210
1224
  (uri) => jsonResource(uri, mcpConfig()),