@tonyclaw/agent-inspector 2.0.36 → 2.0.38

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 (86) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-Bg2mo0NZ.js → CompareDrawer-hc5OoAls.js} +1 -1
  3. package/.output/public/assets/{ProxyViewerContainer-C_Lq5T5V.js → ProxyViewerContainer-BMKBs8Qz.js} +35 -35
  4. package/.output/public/assets/{ReplayDialog-Bns-ayOg.js → ReplayDialog-CN-KM2nu.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-BBcPAIWx.js → RequestAnatomy-B1kTlAUk.js} +1 -1
  6. package/.output/public/assets/{ResponseView-DSRS3rHt.js → ResponseView-BchUjKjt.js} +1 -1
  7. package/.output/public/assets/{StreamingChunkSequence-CzZxKE5-.js → StreamingChunkSequence-BQOxI9Hi.js} +1 -1
  8. package/.output/public/assets/_sessionId-CVw5e8TK.js +1 -0
  9. package/.output/public/assets/index-BdUTMQL6.js +1 -0
  10. package/.output/public/assets/{main-BVKRHBUl.js → main-Blykwzsn.js} +2 -2
  11. package/.output/server/_libs/lucide-react.mjs +114 -94
  12. package/.output/server/{_sessionId-Dsd36Yhc.mjs → _sessionId-BUNZz2zZ.mjs} +2 -2
  13. package/.output/server/_ssr/{CompareDrawer-BbJ5UdYD.mjs → CompareDrawer-VaDYPM5G.mjs} +3 -3
  14. package/.output/server/_ssr/{ProxyViewerContainer-7igCY291.mjs → ProxyViewerContainer-CpdWs7Pk.mjs} +801 -785
  15. package/.output/server/_ssr/{ReplayDialog-CgWweshq.mjs → ReplayDialog-DbfIjJhb.mjs} +4 -4
  16. package/.output/server/_ssr/{RequestAnatomy-BAyM_j7a.mjs → RequestAnatomy-CrD8h6As.mjs} +3 -3
  17. package/.output/server/_ssr/{ResponseView-iZQH7EYu.mjs → ResponseView-BalvDwtU.mjs} +3 -3
  18. package/.output/server/_ssr/{StreamingChunkSequence-BU6hyHMg.mjs → StreamingChunkSequence-RxatDbPV.mjs} +2 -2
  19. package/.output/server/_ssr/{index-D7QF4E6v.mjs → index-11qAtV9d.mjs} +2 -2
  20. package/.output/server/_ssr/index.mjs +2 -2
  21. package/.output/server/_ssr/{router-DLLY6gv_.mjs → router-C4OdRnqX.mjs} +138 -138
  22. package/.output/server/{_tanstack-start-manifest_v-CHmIZaVV.mjs → _tanstack-start-manifest_v-F6bwcl33.mjs} +1 -1
  23. package/.output/server/index.mjs +60 -60
  24. package/README.md +1 -1
  25. package/package.json +1 -1
  26. package/src/components/ProxyViewer.tsx +43 -9
  27. package/src/components/ProxyViewerContainer.tsx +1 -1
  28. package/src/components/providers/ProviderForm.tsx +2 -2
  29. package/src/components/proxy-viewer/AgentTraceSummary.tsx +1 -1
  30. package/src/components/proxy-viewer/CompareDrawer.tsx +1 -1
  31. package/src/components/proxy-viewer/ConversationGroup.tsx +1 -1
  32. package/src/components/proxy-viewer/ConversationGroupList.tsx +1 -1
  33. package/src/components/proxy-viewer/ConversationHeader.tsx +11 -7
  34. package/src/components/proxy-viewer/LogEntry.tsx +1 -1
  35. package/src/components/proxy-viewer/LogEntryHeader.tsx +1 -1
  36. package/src/components/proxy-viewer/ReplayDialog.tsx +1 -1
  37. package/src/components/proxy-viewer/ResponseView.tsx +2 -2
  38. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +1 -1
  39. package/src/components/proxy-viewer/TurnGroup.tsx +1 -1
  40. package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +1 -1
  41. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +1 -1
  42. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +2 -2
  43. package/src/components/proxy-viewer/formats/index.tsx +5 -3
  44. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +1 -1
  45. package/src/components/proxy-viewer/log-formats/anthropic.ts +1 -4
  46. package/src/components/proxy-viewer/log-formats/index.ts +6 -6
  47. package/src/components/proxy-viewer/log-formats/openai.ts +1 -1
  48. package/src/components/proxy-viewer/log-formats/types.ts +2 -2
  49. package/src/components/proxy-viewer/viewerState.ts +1 -1
  50. package/src/contracts/anthropic.ts +235 -0
  51. package/src/contracts/index.ts +27 -0
  52. package/src/contracts/json.ts +32 -0
  53. package/src/contracts/log.ts +72 -0
  54. package/src/contracts/openai.ts +176 -0
  55. package/src/knowledge/distiller.ts +1 -1
  56. package/src/lib/export-logs.ts +1 -1
  57. package/src/lib/groupContract.ts +1 -1
  58. package/src/lib/objectUtils.ts +3 -3
  59. package/src/lib/providerTestContract.ts +1 -1
  60. package/src/lib/runContract.ts +1 -1
  61. package/src/lib/stopReason.ts +1 -1
  62. package/src/mcp/previewExtractor.ts +4 -7
  63. package/src/mcp/server.ts +1 -1
  64. package/src/mcp/toolHandlers.ts +1 -1
  65. package/src/proxy/formats/anthropic/anthropicProvider.ts +2 -2
  66. package/src/proxy/formats/anthropic/schemas.ts +12 -235
  67. package/src/proxy/formats/handler.ts +2 -2
  68. package/src/proxy/formats/index.ts +1 -1
  69. package/src/proxy/formats/jsonSchema.ts +2 -36
  70. package/src/proxy/formats/openai/alibabaProvider.ts +2 -2
  71. package/src/proxy/formats/openai/provider.ts +2 -2
  72. package/src/proxy/formats/openai/schemas.ts +14 -187
  73. package/src/proxy/formats/openai/zhipuProvider.ts +2 -2
  74. package/src/proxy/formats/{protocol.ts → providerAdapter.ts} +5 -5
  75. package/src/proxy/formats/providerRegistry.ts +7 -7
  76. package/src/proxy/formats/providers/index.ts +1 -1
  77. package/src/proxy/formats/registry.ts +20 -11
  78. package/src/proxy/handler.ts +1 -1
  79. package/src/proxy/openaiOrphanToolStrip.ts +1 -1
  80. package/src/proxy/providers.ts +1 -1
  81. package/src/proxy/schemas.ts +18 -80
  82. package/src/proxy/store.ts +14 -11
  83. package/src/proxy/upstream.ts +1 -1
  84. package/src/routes/api/logs.stream.ts +1 -1
  85. package/.output/public/assets/_sessionId-DurHDwM1.js +0 -1
  86. package/.output/public/assets/index-sGphugFR.js +0 -1
@@ -1,26 +1,26 @@
1
1
  import type { FormatHandler } from "./handler";
2
- import type { RequestFormat } from "../schemas";
2
+ import type { ApiFormat } from "../schemas";
3
3
  import { PATH_V1_CHAT_COMPLETIONS, PATH_CHAT_COMPLETIONS, PATH_V1_MESSAGES } from "../constants";
4
4
 
5
5
  /**
6
- * Format registry maps request paths and formats to their FormatHandler.
6
+ * API format registry maps request paths and API formats to their FormatHandler.
7
7
  *
8
8
  * Registered at module load time by each format's init module.
9
9
  */
10
10
  class FormatRegistryImpl {
11
- private handlers = new Map<RequestFormat, FormatHandler>();
12
- private pathMap = new Map<string, RequestFormat>();
11
+ private handlers = new Map<ApiFormat, FormatHandler>();
12
+ private pathMap = new Map<string, ApiFormat>();
13
13
 
14
14
  register(handler: FormatHandler): void {
15
15
  this.handlers.set(handler.format, handler);
16
16
  }
17
17
 
18
- registerPath(path: string, format: RequestFormat): void {
18
+ registerPath(path: string, format: ApiFormat): void {
19
19
  this.pathMap.set(path, format);
20
20
  }
21
21
 
22
22
  /** Get handler by format identifier */
23
- get(format: RequestFormat): FormatHandler | undefined {
23
+ get(format: ApiFormat): FormatHandler | undefined {
24
24
  return this.handlers.get(format);
25
25
  }
26
26
 
@@ -31,8 +31,14 @@ class FormatRegistryImpl {
31
31
  return format === undefined ? undefined : this.handlers.get(format);
32
32
  }
33
33
 
34
- /** Detect format from request body content */
35
- detectFormat(rawBody: string | null): RequestFormat {
34
+ /** Get the API format matching a request path without requiring handlers. */
35
+ getApiFormatByPath(path: string): ApiFormat | undefined {
36
+ const messagesPath = path.split("?")[0] ?? "";
37
+ return this.pathMap.get(messagesPath);
38
+ }
39
+
40
+ /** Detect API format from request body content */
41
+ detectFormat(rawBody: string | null): ApiFormat {
36
42
  if (rawBody === null) return "unknown";
37
43
  for (const handler of this.handlers.values()) {
38
44
  if (handler.detectFormat(rawBody)) return handler.format;
@@ -52,14 +58,17 @@ export function formatForPath(path: string): FormatHandler | null {
52
58
  }
53
59
 
54
60
  /**
55
- * Resolve the wire format from the request endpoint. The endpoint is
61
+ * Resolve the API format from the request endpoint. The endpoint is
56
62
  * authoritative because Anthropic and OpenAI request bodies overlap for
57
63
  * simple message-only calls.
58
64
  */
59
- export function requestFormatForPath(path: string): RequestFormat {
60
- return formatForPath(path)?.format ?? "unknown";
65
+ export function apiFormatForPath(path: string): ApiFormat {
66
+ return formatRegistry.getApiFormatByPath(path) ?? "unknown";
61
67
  }
62
68
 
69
+ /** Backward-compatible alias for older internal imports. */
70
+ export const requestFormatForPath = apiFormatForPath;
71
+
63
72
  // Register known paths
64
73
  formatRegistry.registerPath(PATH_V1_MESSAGES, "anthropic");
65
74
  formatRegistry.registerPath(PATH_V1_CHAT_COMPLETIONS, "openai");
@@ -247,7 +247,7 @@ export async function handleProxy(req: Request): Promise<Response> {
247
247
  }
248
248
  }
249
249
 
250
- // Always-on protocol guard: OpenAI Chat Completions requires every
250
+ // Always-on OpenAI Chat Completions format guard: every
251
251
  // `role: "tool"` message to reference a `tool_call_id` present in a
252
252
  // preceding assistant message's `tool_calls`. Buggy clients sometimes
253
253
  // record a tool result without the matching assistant tool_call, which
@@ -1,5 +1,5 @@
1
1
  /**
2
- * OpenAI Chat Completions protocol guard: orphan `tool` messages.
2
+ * OpenAI Chat Completions format guard: orphan `tool` messages.
3
3
  *
4
4
  * The OpenAI Chat Completions spec requires every message with
5
5
  * `role: "tool"` to reference a `tool_call_id` that appears in a preceding
@@ -232,7 +232,7 @@ migrateFromLegacyConfLocation(store);
232
232
  * Expands the old `{ format, baseUrl }` representation into the matching
233
233
  * format-specific URL. Once either format-specific field exists, the record is
234
234
  * already on the current schema and must be preserved exactly: an empty or
235
- * missing counterpart means that protocol was intentionally not configured.
235
+ * missing counterpart means that API format was intentionally not configured.
236
236
  */
237
237
  export function migrateProvider(p: ProviderConfig): ProviderConfig {
238
238
  if (p.anthropicBaseUrl !== undefined || p.openaiBaseUrl !== undefined) {
@@ -1,94 +1,32 @@
1
1
  import { z } from "zod";
2
2
 
3
- // Import JsonValueSchema from shared location to avoid circular dependency
4
- import { JsonValueSchema, type JsonValue } from "./formats/jsonSchema";
5
-
6
- export { JsonValueSchema };
7
- export type { JsonValue };
8
-
9
- export type RequestFormat = "anthropic" | "openai" | "unknown";
10
-
11
- /**
12
- * A single SSE event from a streaming response.
13
- */
14
- export const StreamingChunkSchema = z.object({
15
- index: z.number(),
16
- timestamp: z.number(),
17
- type: z.string(),
18
- data: JsonValueSchema,
19
- });
20
-
21
- export type StreamingChunk = z.infer<typeof StreamingChunkSchema>;
22
-
23
- const StreamingChunksArraySchema = z.object({
24
- chunks: z.array(StreamingChunkSchema),
25
- truncated: z.boolean().optional().default(false),
26
- });
27
-
28
- export const CapturedLogSchema = z.object({
29
- id: z.number(),
30
- timestamp: z.string(),
31
- method: z.string(),
32
- path: z.string(),
33
- model: z.string().nullable(),
34
- sessionId: z.string().nullable(),
35
- rawRequestBody: z.string().nullable(),
36
- responseStatus: z.number().nullable(),
37
- responseText: z.string().nullable(),
38
- inputTokens: z.number().nullable(),
39
- outputTokens: z.number().nullable(),
40
- cacheCreationInputTokens: z.number().nullable(),
41
- cacheReadInputTokens: z.number().nullable(),
42
- elapsedMs: z.number().nullable(),
43
- firstChunkMs: z.number().nullable().optional(),
44
- totalStreamMs: z.number().nullable().optional(),
45
- tokensPerSecond: z.number().nullable().optional(),
46
- streaming: z.boolean(),
47
- userAgent: z.string().nullable(),
48
- origin: z.string().nullable(),
49
- rawHeaders: z.record(z.string(), z.string()).optional(),
50
- /** Headers sent to the upstream LLM */
51
- headers: z.record(z.string(), z.string()).optional(),
52
- apiFormat: z.enum(["anthropic", "openai", "unknown"]).default("unknown"),
53
- isTest: z.boolean().optional().default(false),
54
- replayOfLogId: z.number().nullable().optional(),
55
- providerName: z.string().nullable().optional(),
56
- clientPort: z.number().nullable().optional(),
57
- clientPid: z.number().nullable().optional(),
58
- clientCwd: z.string().nullable().optional(),
59
- clientProjectFolder: z.string().nullable().optional(),
60
- streamingChunks: StreamingChunksArraySchema.optional(),
61
- streamingChunksPath: z.string().nullable().optional(),
62
- rawRequestBodyBytes: z.number().int().nonnegative().nullable().optional(),
63
- responseTextBytes: z.number().int().nonnegative().nullable().optional(),
64
- bodyContentMode: z.enum(["full", "compact", "truncated"]).optional(),
65
- /** Error message from streaming response (e.g., SSE error event) */
66
- error: z.string().nullable().optional(),
67
- });
68
-
69
- export type CapturedLog = z.infer<typeof CapturedLogSchema>;
70
-
71
- export type TokenUsage = {
72
- inputTokens: number | null;
73
- outputTokens: number | null;
74
- cacheCreationInputTokens: number | null;
75
- cacheReadInputTokens: number | null;
76
- };
3
+ export { CapturedLogSchema, JsonValueSchema, StreamingChunkSchema } from "../contracts";
4
+ export type {
5
+ CapturedLog,
6
+ ApiFormat,
7
+ JsonValue,
8
+ RequestFormat,
9
+ StreamingChunk,
10
+ TokenUsage,
11
+ } from "../contracts";
77
12
 
78
13
  // ============================================================
79
- // Import types and schemas from formats
14
+ // Import types and schemas from shared contracts
80
15
  // ============================================================
81
16
 
82
- // Re-export types and schemas from formats for backward compatibility
17
+ // Re-export types and schemas from shared contracts for backward compatibility
83
18
  export {
19
+ AnthropicRequestSchema,
20
+ AnthropicResponseSchema,
84
21
  InspectorRequestSchema,
85
22
  InspectorResponseSchema,
86
23
  SseEventSchema,
87
- } from "./formats/anthropic/schemas";
24
+ } from "../contracts/anthropic";
88
25
  export type {
26
+ AnthropicResponse,
89
27
  InspectorResponse,
90
28
  ResponseContentBlockType,
91
- } from "./formats/anthropic/schemas";
29
+ } from "../contracts/anthropic";
92
30
 
93
31
  export {
94
32
  OpenAIRequestSchema,
@@ -97,7 +35,7 @@ export {
97
35
  type OpenAIResponse,
98
36
  type OpenAIToolCall,
99
37
  parseOpenAIResponse,
100
- } from "./formats/openai/schemas";
38
+ } from "../contracts/openai";
101
39
 
102
40
  // Schema for model name in request body (used for routing)
103
41
  const RequestModelSchema = z.object({
@@ -141,7 +79,7 @@ function extractSessionIdFromHeaders(headers: Headers): string | null {
141
79
  * for routing + log creation: the model (used for provider selection) and the
142
80
  * session id (from `metadata.user_id` or the `x-session-affinity` header).
143
81
  *
144
- * Returning `null` for either field is a valid outcome callers must handle
82
+ * Returning `null` for either field is a valid outcome; callers must handle
145
83
  * "unknown" gracefully. Callers that already have the body string should use
146
84
  * this instead of re-parsing with `extractModelFromBody` and a second pass
147
85
  * for the session id.
@@ -34,6 +34,7 @@ import type { SessionInfo, SessionLogSummary } from "../lib/sessionInfoContract"
34
34
  import { buildSessionInfo, buildSessionLogSummary } from "./sessionInfo";
35
35
  import type { CapturedLog } from "./schemas";
36
36
  import { CapturedLogSchema } from "./schemas";
37
+ import { apiFormatForPath } from "./formats/registry";
37
38
  import {
38
39
  clearSessionRegistry,
39
40
  getLogSessionId,
@@ -109,14 +110,16 @@ function evictOldestIfNeeded(): void {
109
110
  }
110
111
  }
111
112
 
112
- function normalizeLogSession(log: CapturedLog): CapturedLog {
113
+ function normalizeLog(log: CapturedLog): CapturedLog {
113
114
  const sessionId = getLogSessionId(log);
114
- if (sessionId === log.sessionId) return log;
115
- return { ...log, sessionId };
115
+ const pathFormat = apiFormatForPath(log.path);
116
+ const apiFormat = pathFormat === "unknown" ? log.apiFormat : pathFormat;
117
+ if (sessionId === log.sessionId && apiFormat === log.apiFormat) return log;
118
+ return { ...log, sessionId, apiFormat };
116
119
  }
117
120
 
118
121
  function addToCache(log: CapturedLog): void {
119
- const cachedLog = normalizeLogSession(log);
122
+ const cachedLog = normalizeLog(log);
120
123
  // If updating an existing entry, remove first to reset insertion order
121
124
  if (memoryCache.has(cachedLog.id)) {
122
125
  memoryCache.delete(cachedLog.id);
@@ -174,12 +177,12 @@ export async function addTestLogEntry(entry: Omit<CapturedLog, "id">): Promise<C
174
177
  isTest: entry.isTest,
175
178
  });
176
179
 
177
- const log: CapturedLog = {
180
+ const log: CapturedLog = normalizeLog({
178
181
  id,
179
182
  ...entry,
180
183
  sessionId: session.id,
181
184
  streamingChunksPath,
182
- };
185
+ });
183
186
 
184
187
  await runWithLogWriteLock(async () => {
185
188
  const logFile = getCurrentLogFile();
@@ -218,7 +221,7 @@ export async function createLog(
218
221
  clientInfo,
219
222
  });
220
223
 
221
- const log: CapturedLog = {
224
+ const log: CapturedLog = normalizeLog({
222
225
  id,
223
226
  timestamp: new Date().toISOString(),
224
227
  method,
@@ -249,7 +252,7 @@ export async function createLog(
249
252
  clientCwd: clientInfo?.cwd ?? null,
250
253
  clientProjectFolder: clientInfo?.projectFolder ?? null,
251
254
  streamingChunksPath: null,
252
- };
255
+ });
253
256
 
254
257
  // Write to disk and update index
255
258
  await runWithLogWriteLock(async () => {
@@ -308,7 +311,7 @@ export async function getLogById(id: number): Promise<CapturedLog | null> {
308
311
  const parsed: unknown = JSON.parse(line);
309
312
  const result = CapturedLogSchema.safeParse(parsed);
310
313
  if (result.success) {
311
- const log = normalizeLogSession(result.data);
314
+ const log = normalizeLog(result.data);
312
315
  addToCache(log);
313
316
  observeSessionLog(log);
314
317
  return log;
@@ -348,7 +351,7 @@ async function scanLogFileForId(filePath: string, id: number): Promise<CapturedL
348
351
  if (desc !== undefined && typeof desc.value === "number" && desc.value === id) {
349
352
  const result = CapturedLogSchema.safeParse(parsed);
350
353
  if (result.success) {
351
- lastMatch = normalizeLogSession(result.data);
354
+ lastMatch = normalizeLog(result.data);
352
355
  if (result.data.responseStatus !== null) {
353
356
  return lastMatch;
354
357
  }
@@ -412,7 +415,7 @@ async function visitPersistedLogFile(
412
415
  const parsed: unknown = JSON.parse(line);
413
416
  const result = CapturedLogSchema.safeParse(parsed);
414
417
  if (result.success) {
415
- const log = normalizeLogSession(result.data);
418
+ const log = normalizeLog(result.data);
416
419
  visitor(log);
417
420
  }
418
421
  } catch {
@@ -18,7 +18,7 @@ export type ApiRoute = {
18
18
  };
19
19
 
20
20
  /**
21
- * Describes the protocol implied by the client-facing path.
21
+ * Describes the API route implied by the client-facing path.
22
22
  *
23
23
  * The path is the source of truth for request/response format. ProviderConfig.format
24
24
  * is only a fallback for old provider records that do not have a format-specific URL.
@@ -1,6 +1,6 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { compactLogForList, onLogUpdate, getLogSessionId, listLogsPage } from "../../proxy/store";
3
- import { type CapturedLog } from "../../proxy/schemas";
3
+ import { type CapturedLog } from "../../contracts";
4
4
 
5
5
  const INITIAL_STREAM_LOG_LIMIT = 100;
6
6
 
@@ -1 +0,0 @@
1
- import{R as s,j as e}from"./main-BVKRHBUl.js";import{P as i}from"./ProxyViewerContainer-C_Lq5T5V.js";function t(){const{sessionId:o}=s.useParams();return e.jsx(i,{initialSessionId:o},o)}export{t as component};
@@ -1 +0,0 @@
1
- import{P as o}from"./ProxyViewerContainer-C_Lq5T5V.js";import"./main-BVKRHBUl.js";const r=o;export{r as component};