@tonyclaw/agent-inspector 2.0.38 → 2.0.40

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 (35) hide show
  1. package/.output/cli.js +528 -24
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-hc5OoAls.js → CompareDrawer-p0uYxW7_.js} +1 -1
  4. package/.output/public/assets/{ProxyViewerContainer-BMKBs8Qz.js → ProxyViewerContainer-BGqeiYKD.js} +26 -26
  5. package/.output/public/assets/{ReplayDialog-CN-KM2nu.js → ReplayDialog-TTTVCMe9.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-B1kTlAUk.js → RequestAnatomy-c8ru0pS-.js} +1 -1
  7. package/.output/public/assets/{ResponseView-BchUjKjt.js → ResponseView-D59jTMy4.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-BQOxI9Hi.js → StreamingChunkSequence-UlAMVF9j.js} +1 -1
  9. package/.output/public/assets/_sessionId-EHPexdPl.js +1 -0
  10. package/.output/public/assets/index-CZP-XfpB.js +1 -0
  11. package/.output/public/assets/{main-Blykwzsn.js → main-CKYe6UUv.js} +2 -2
  12. package/.output/server/{_sessionId-BUNZz2zZ.mjs → _sessionId-CkwhOzdS.mjs} +2 -2
  13. package/.output/server/_ssr/{CompareDrawer-VaDYPM5G.mjs → CompareDrawer-BHDiuYMw.mjs} +2 -2
  14. package/.output/server/_ssr/{ProxyViewerContainer-CpdWs7Pk.mjs → ProxyViewerContainer-0c2_DBMX.mjs} +57 -6
  15. package/.output/server/_ssr/{ReplayDialog-DbfIjJhb.mjs → ReplayDialog-CTjwzDDH.mjs} +3 -3
  16. package/.output/server/_ssr/{RequestAnatomy-CrD8h6As.mjs → RequestAnatomy-D36oujv2.mjs} +2 -2
  17. package/.output/server/_ssr/{ResponseView-BalvDwtU.mjs → ResponseView-ClZ3oODE.mjs} +2 -2
  18. package/.output/server/_ssr/{StreamingChunkSequence-RxatDbPV.mjs → StreamingChunkSequence-Ck5l4p1m.mjs} +2 -2
  19. package/.output/server/_ssr/{index-11qAtV9d.mjs → index-DKBYH0TC.mjs} +2 -2
  20. package/.output/server/_ssr/index.mjs +2 -2
  21. package/.output/server/_ssr/{router-C4OdRnqX.mjs → router-D4I3ZyVS.mjs} +434 -6
  22. package/.output/server/{_tanstack-start-manifest_v-F6bwcl33.mjs → _tanstack-start-manifest_v-D4vSn_wD.mjs} +1 -1
  23. package/.output/server/index.mjs +54 -54
  24. package/README.md +7 -0
  25. package/package.json +1 -1
  26. package/src/cli/onboard.ts +629 -21
  27. package/src/components/providers/SettingsDialog.tsx +30 -0
  28. package/src/components/proxy-viewer/LogEntry.tsx +15 -1
  29. package/src/components/proxy-viewer/LogEntryHeader.tsx +20 -0
  30. package/src/contracts/log.ts +1 -0
  31. package/src/proxy/logFinalizer.ts +58 -0
  32. package/src/proxy/logSearch.ts +1 -0
  33. package/src/proxy/toolSchemaWarnings.ts +515 -0
  34. package/.output/public/assets/_sessionId-CVw5e8TK.js +0 -1
  35. package/.output/public/assets/index-BdUTMQL6.js +0 -1
@@ -293,6 +293,18 @@ function OnboardingSettingsTab(): JSX.Element {
293
293
  label: "Codex prompt",
294
294
  value: "Use the agent-inspector-onboard skill",
295
295
  },
296
+ {
297
+ id: "opencode-config",
298
+ label: "OpenCode config",
299
+ value: "agent-inspector onboard --opencode-only",
300
+ },
301
+ {
302
+ id: "opencode-status",
303
+ label: "OpenCode status",
304
+ value: "agent-inspector onboard --status --opencode-only",
305
+ },
306
+ { id: "opencode-verify", label: "OpenCode verify", value: "opencode mcp list" },
307
+ { id: "opencode-proxy", label: "OpenCode proxy", value: `LLM_BASE_URL=${origin}/proxy` },
296
308
  { id: "mcp", label: "MCP URL", value: `${origin}/api/mcp` },
297
309
  { id: "proxy", label: "Proxy URL", value: `${origin}/proxy` },
298
310
  { id: "anthropic", label: "Anthropic base", value: `ANTHROPIC_BASE_URL=${origin}/proxy` },
@@ -345,6 +357,24 @@ function OnboardingSettingsTab(): JSX.Element {
345
357
  instead. Configure only one entry for the same Inspector.
346
358
  </span>
347
359
  </div>
360
+ <div className="flex items-start gap-2">
361
+ <Check className="mt-0.5 size-3.5 text-emerald-500" />
362
+ <span>
363
+ OpenCode onboarding safely merges{" "}
364
+ <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px] text-foreground">
365
+ mcp.agent-inspector
366
+ </code>{" "}
367
+ into{" "}
368
+ <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px] text-foreground">
369
+ opencode.json
370
+ </code>
371
+ , then verify with{" "}
372
+ <code className="rounded bg-muted px-1 py-0.5 font-mono text-[11px] text-foreground">
373
+ opencode mcp list
374
+ </code>
375
+ .
376
+ </span>
377
+ </div>
348
378
  <div className="flex items-start gap-2">
349
379
  <Check className="mt-0.5 size-3.5 text-emerald-500" />
350
380
  <span>
@@ -1,4 +1,4 @@
1
- import { GitCompareArrows } from "lucide-react";
1
+ import { AlertTriangle, GitCompareArrows } from "lucide-react";
2
2
  import { Suspense, type JSX } from "react";
3
3
  import { useEffect, useMemo, useRef, useState, memo } from "react";
4
4
  import type { CapturedLog } from "../../contracts";
@@ -295,6 +295,7 @@ export const LogEntry = memo(function ({
295
295
  () => parseRequestTools(requestExpansion.parsedData),
296
296
  [requestExpansion.parsedData],
297
297
  );
298
+ const warnings = log.warnings ?? [];
298
299
  const anatomyPaths = useMemo(() => {
299
300
  if (anatomySegments === null) return undefined;
300
301
  return new Set(anatomySegments.map((s) => s.path));
@@ -392,6 +393,19 @@ export const LogEntry = memo(function ({
392
393
  <TabsContent value="request">
393
394
  {activeTab === "request" && (
394
395
  <div className="px-4 pt-1 pb-3">
396
+ {warnings.length > 0 && (
397
+ <div className="mb-3 rounded border border-amber-500/30 bg-amber-500/10 p-3 text-xs">
398
+ <div className="flex items-center gap-2 font-semibold text-amber-300">
399
+ <AlertTriangle className="size-3.5" />
400
+ Tool schema warnings
401
+ </div>
402
+ <ul className="mt-2 space-y-1 text-muted-foreground">
403
+ {warnings.map((warning) => (
404
+ <li key={warning}>{warning}</li>
405
+ ))}
406
+ </ul>
407
+ </div>
408
+ )}
395
409
  <RequestTools
396
410
  summary={requestTools}
397
411
  calledToolNames={responseAnalysis.toolNames ?? []}
@@ -159,6 +159,7 @@ export const LogEntryHeader = memo(function ({
159
159
  const firstChunkLabel = firstChunkMs === null ? null : formatElapsed(firstChunkMs);
160
160
  const tokenRateLabel =
161
161
  tokensPerSecond === null || tokensPerSecond <= 0 ? null : formatTokenRate(tokensPerSecond);
162
+ const warningCount = log.warnings?.length ?? 0;
162
163
 
163
164
  return (
164
165
  <TooltipProvider>
@@ -229,6 +230,25 @@ export const LogEntryHeader = memo(function ({
229
230
  </Badge>
230
231
  )}
231
232
 
233
+ {warningCount > 0 && (
234
+ <Tooltip>
235
+ <TooltipTrigger asChild>
236
+ <Badge
237
+ variant="outline"
238
+ className="text-[10px] px-1.5 py-0 h-5 font-mono tabular-nums gap-1 bg-amber-500/15 text-amber-400 border-amber-500/25"
239
+ >
240
+ <AlertTriangle className="size-3" aria-label="Tool schema warnings" />
241
+ {warningCount}
242
+ </Badge>
243
+ </TooltipTrigger>
244
+ <TooltipContent>
245
+ {warningCount === 1
246
+ ? "1 tool schema warning"
247
+ : `${warningCount} tool schema warnings`}
248
+ </TooltipContent>
249
+ </Tooltip>
250
+ )}
251
+
232
252
  {/* Elapsed time */}
233
253
  {log.elapsedMs !== null && (
234
254
  <Tooltip>
@@ -58,6 +58,7 @@ export const CapturedLogSchema = z.object({
58
58
  rawRequestBodyBytes: z.number().int().nonnegative().nullable().optional(),
59
59
  responseTextBytes: z.number().int().nonnegative().nullable().optional(),
60
60
  bodyContentMode: z.enum(["full", "compact", "truncated"]).optional(),
61
+ warnings: z.array(z.string()).optional(),
61
62
  /** Error message from streaming response (e.g., SSE error event) */
62
63
  error: z.string().nullable().optional(),
63
64
  });
@@ -9,6 +9,7 @@ import type { CapturedLog } from "./schemas";
9
9
  import { getSessionProcess, isSessionProcessAvailable } from "./sessionProcess";
10
10
  import { finalizeLogUpdate } from "./store";
11
11
  import { computeTokensPerSecond } from "./streamTiming";
12
+ import { analyzeToolSchemaWarnings } from "./toolSchemaWarnings";
12
13
 
13
14
  type BaseFinalizeLogJob = {
14
15
  log: CapturedLog;
@@ -91,6 +92,7 @@ export function buildFileLogEntry(log: CapturedLog, upstreamUrl: string): Record
91
92
  clientProjectFolder: log.clientProjectFolder,
92
93
  streamingChunks: log.streamingChunks,
93
94
  streamingChunksPath: log.streamingChunksPath,
95
+ warnings: log.warnings,
94
96
  error: log.error,
95
97
  };
96
98
  }
@@ -152,6 +154,57 @@ function applyOptionalStreamingTiming(log: CapturedLog, job: FinalizeLogJob): vo
152
154
  }
153
155
  }
154
156
 
157
+ type ToolSchemaWarningAnalysisJob = {
158
+ log: CapturedLog;
159
+ upstreamUrl: string;
160
+ error: string | null;
161
+ };
162
+
163
+ let toolSchemaWarningQueue: Promise<void> = Promise.resolve();
164
+
165
+ function deferToolSchemaWarningAnalysis(): Promise<void> {
166
+ return new Promise((resolve) => {
167
+ setTimeout(resolve, 0);
168
+ });
169
+ }
170
+
171
+ function withToolSchemaWarnings(log: CapturedLog): CapturedLog | null {
172
+ const warnings = analyzeToolSchemaWarnings({
173
+ rawRequestBody: log.rawRequestBody,
174
+ responseText: log.responseText,
175
+ apiFormat: log.apiFormat,
176
+ });
177
+ if (warnings.length === 0) return null;
178
+ const mergedWarnings = Array.from(new Set([...(log.warnings ?? []), ...warnings]));
179
+ if (mergedWarnings.length === (log.warnings?.length ?? 0)) return null;
180
+ return { ...log, warnings: mergedWarnings };
181
+ }
182
+
183
+ async function runToolSchemaWarningAnalysis(job: ToolSchemaWarningAnalysisJob): Promise<void> {
184
+ await deferToolSchemaWarningAnalysis();
185
+ const updatedLog = withToolSchemaWarnings(job.log);
186
+ if (updatedLog === null) return;
187
+
188
+ appendLogEntry({ ...buildFileLogEntry(updatedLog, job.upstreamUrl), error: job.error });
189
+ finalizeLogUpdate(updatedLog);
190
+ }
191
+
192
+ function scheduleToolSchemaWarningAnalysis(job: ToolSchemaWarningAnalysisJob): void {
193
+ toolSchemaWarningQueue = toolSchemaWarningQueue
194
+ .catch(() => undefined)
195
+ .then(() => runToolSchemaWarningAnalysis(job))
196
+ .catch((err: unknown) => {
197
+ logger.warn(
198
+ `[logFinalizer] Tool schema warning analysis failed for log #${job.log.id}:`,
199
+ errorMessage(err),
200
+ );
201
+ });
202
+ }
203
+
204
+ export async function flushToolSchemaWarningAnalysis(): Promise<void> {
205
+ await toolSchemaWarningQueue;
206
+ }
207
+
155
208
  function finalizeNonStreaming(
156
209
  job: FinalizeNonStreamingLogJob,
157
210
  log: CapturedLog,
@@ -384,6 +437,11 @@ export function buildFinalizeLogResult(job: FinalizeLogJob): FinalizeLogResult {
384
437
  export function commitFinalizeLogResult(result: FinalizeLogResult): void {
385
438
  appendLogEntry({ ...buildFileLogEntry(result.log, result.upstreamUrl), error: result.error });
386
439
  finalizeLogUpdate(result.log);
440
+ scheduleToolSchemaWarningAnalysis({
441
+ log: cloneLog(result.log),
442
+ upstreamUrl: result.upstreamUrl,
443
+ error: result.error,
444
+ });
387
445
  if (result.cleanupRawStreamPath !== undefined && result.cleanupRawStreamPath !== null) {
388
446
  try {
389
447
  rmSync(result.cleanupRawStreamPath, { force: true });
@@ -70,6 +70,7 @@ function searchableFields(log: CapturedLog): string[] {
70
70
  optionalText(log.clientCwd),
71
71
  optionalText(log.clientProjectFolder),
72
72
  optionalText(log.error),
73
+ ...(log.warnings ?? []),
73
74
  ].filter((value): value is string => value !== null && value.length > 0);
74
75
  }
75
76