@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
@@ -38,100 +38,100 @@ const assets = {
38
38
  "/assets/alibaba-TTwafVwX.svg": {
39
39
  "type": "image/svg+xml",
40
40
  "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
41
- "mtime": "2026-07-01T04:58:19.201Z",
41
+ "mtime": "2026-07-01T07:15:57.121Z",
42
42
  "size": 5915,
43
43
  "path": "../public/assets/alibaba-TTwafVwX.svg"
44
44
  },
45
- "/assets/CompareDrawer-Bg2mo0NZ.js": {
45
+ "/assets/CompareDrawer-hc5OoAls.js": {
46
46
  "type": "text/javascript; charset=utf-8",
47
- "etag": '"49fe-RCFPReNqO+29uyWqCSboc4eRJiw"',
48
- "mtime": "2026-07-01T04:58:19.202Z",
47
+ "etag": '"49fe-BHtwU/NjhFJzLdwy953kVTz0OMs"',
48
+ "mtime": "2026-07-01T07:15:57.121Z",
49
49
  "size": 18942,
50
- "path": "../public/assets/CompareDrawer-Bg2mo0NZ.js"
50
+ "path": "../public/assets/CompareDrawer-hc5OoAls.js"
51
51
  },
52
- "/assets/index-BfGJEb-2.css": {
53
- "type": "text/css; charset=utf-8",
54
- "etag": '"1acc9-JrjZs7fIRGuBr66cqP9miLHcLKk"',
55
- "mtime": "2026-07-01T04:58:19.202Z",
56
- "size": 109769,
57
- "path": "../public/assets/index-BfGJEb-2.css"
52
+ "/assets/index-BdUTMQL6.js": {
53
+ "type": "text/javascript; charset=utf-8",
54
+ "etag": '"74-62iPuuX47jLXNHf/ZV6ct/elkTk"',
55
+ "mtime": "2026-07-01T07:15:57.121Z",
56
+ "size": 116,
57
+ "path": "../public/assets/index-BdUTMQL6.js"
58
58
  },
59
59
  "/assets/minimax-BPMzvuL-.jpeg": {
60
60
  "type": "image/jpeg",
61
61
  "etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
62
- "mtime": "2026-07-01T04:58:19.201Z",
62
+ "mtime": "2026-07-01T07:15:57.121Z",
63
63
  "size": 6918,
64
64
  "path": "../public/assets/minimax-BPMzvuL-.jpeg"
65
65
  },
66
- "/assets/index-sGphugFR.js": {
67
- "type": "text/javascript; charset=utf-8",
68
- "etag": '"74-U99mmMNtKqojd/yOIx20tXTHvv0"',
69
- "mtime": "2026-07-01T04:58:19.202Z",
70
- "size": 116,
71
- "path": "../public/assets/index-sGphugFR.js"
72
- },
73
- "/assets/ReplayDialog-Bns-ayOg.js": {
74
- "type": "text/javascript; charset=utf-8",
75
- "etag": '"318c-PpbkTF95foTOdbkrWMMu2juYBfw"',
76
- "mtime": "2026-07-01T04:58:19.202Z",
77
- "size": 12684,
78
- "path": "../public/assets/ReplayDialog-Bns-ayOg.js"
79
- },
80
- "/assets/RequestAnatomy-BBcPAIWx.js": {
81
- "type": "text/javascript; charset=utf-8",
82
- "etag": '"4485-1vjOocGpUTcM93Vi7PdBPHFM1B4"',
83
- "mtime": "2026-07-01T04:58:19.203Z",
84
- "size": 17541,
85
- "path": "../public/assets/RequestAnatomy-BBcPAIWx.js"
66
+ "/assets/index-BfGJEb-2.css": {
67
+ "type": "text/css; charset=utf-8",
68
+ "etag": '"1acc9-JrjZs7fIRGuBr66cqP9miLHcLKk"',
69
+ "mtime": "2026-07-01T07:15:57.121Z",
70
+ "size": 109769,
71
+ "path": "../public/assets/index-BfGJEb-2.css"
86
72
  },
87
- "/assets/main-BVKRHBUl.js": {
73
+ "/assets/main-Blykwzsn.js": {
88
74
  "type": "text/javascript; charset=utf-8",
89
- "etag": '"517d9-JlNEG3a6o9EldCqhShuLgqml6as"',
90
- "mtime": "2026-07-01T04:58:19.202Z",
75
+ "etag": '"517d9-1RW0upmMLsMivL5FlKjWPPFMbb8"',
76
+ "mtime": "2026-07-01T07:15:57.121Z",
91
77
  "size": 333785,
92
- "path": "../public/assets/main-BVKRHBUl.js"
78
+ "path": "../public/assets/main-Blykwzsn.js"
93
79
  },
94
- "/assets/ResponseView-DSRS3rHt.js": {
80
+ "/assets/ReplayDialog-CN-KM2nu.js": {
95
81
  "type": "text/javascript; charset=utf-8",
96
- "etag": '"30d0-10JA2gOO6is7MsXilPPj6fWA4d8"',
97
- "mtime": "2026-07-01T04:58:19.203Z",
98
- "size": 12496,
99
- "path": "../public/assets/ResponseView-DSRS3rHt.js"
82
+ "etag": '"318c-bHppq4F90VcBylI8MEq9phZKKYY"',
83
+ "mtime": "2026-07-01T07:15:57.121Z",
84
+ "size": 12684,
85
+ "path": "../public/assets/ReplayDialog-CN-KM2nu.js"
100
86
  },
101
- "/assets/_sessionId-DurHDwM1.js": {
87
+ "/assets/StreamingChunkSequence-BQOxI9Hi.js": {
102
88
  "type": "text/javascript; charset=utf-8",
103
- "etag": '"d2-QnuU9hk4W1Z+guk3DjKKJHPPTuo"',
104
- "mtime": "2026-07-01T04:58:19.202Z",
105
- "size": 210,
106
- "path": "../public/assets/_sessionId-DurHDwM1.js"
89
+ "etag": '"d57-VudeFeolN8IfFVek6HMLmXTmPQY"',
90
+ "mtime": "2026-07-01T07:15:57.121Z",
91
+ "size": 3415,
92
+ "path": "../public/assets/StreamingChunkSequence-BQOxI9Hi.js"
107
93
  },
108
94
  "/assets/zhipuai-BPNAnxo-.svg": {
109
95
  "type": "image/svg+xml",
110
96
  "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
111
- "mtime": "2026-07-01T04:58:19.202Z",
97
+ "mtime": "2026-07-01T07:15:57.121Z",
112
98
  "size": 11256,
113
99
  "path": "../public/assets/zhipuai-BPNAnxo-.svg"
114
100
  },
101
+ "/assets/RequestAnatomy-B1kTlAUk.js": {
102
+ "type": "text/javascript; charset=utf-8",
103
+ "etag": '"4485-zOrU17hrJUbuuYLZW8HdqHozpuE"',
104
+ "mtime": "2026-07-01T07:15:57.121Z",
105
+ "size": 17541,
106
+ "path": "../public/assets/RequestAnatomy-B1kTlAUk.js"
107
+ },
108
+ "/assets/_sessionId-CVw5e8TK.js": {
109
+ "type": "text/javascript; charset=utf-8",
110
+ "etag": '"d2-cV8zzbK+/RUFmfT5dWvK333bSe8"',
111
+ "mtime": "2026-07-01T07:15:57.121Z",
112
+ "size": 210,
113
+ "path": "../public/assets/_sessionId-CVw5e8TK.js"
114
+ },
115
+ "/assets/ResponseView-BchUjKjt.js": {
116
+ "type": "text/javascript; charset=utf-8",
117
+ "etag": '"30d0-i5IJUGuKCFfLsaiZy5PG5wFw9NM"',
118
+ "mtime": "2026-07-01T07:15:57.121Z",
119
+ "size": 12496,
120
+ "path": "../public/assets/ResponseView-BchUjKjt.js"
121
+ },
115
122
  "/assets/qwen-CONDcHqt.png": {
116
123
  "type": "image/png",
117
124
  "etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
118
- "mtime": "2026-07-01T04:58:19.202Z",
125
+ "mtime": "2026-07-01T07:15:57.121Z",
119
126
  "size": 357059,
120
127
  "path": "../public/assets/qwen-CONDcHqt.png"
121
128
  },
122
- "/assets/StreamingChunkSequence-CzZxKE5-.js": {
123
- "type": "text/javascript; charset=utf-8",
124
- "etag": '"d57-tTBqrJv/i9tGh9Lr7MzVvOm88mA"',
125
- "mtime": "2026-07-01T04:58:19.203Z",
126
- "size": 3415,
127
- "path": "../public/assets/StreamingChunkSequence-CzZxKE5-.js"
128
- },
129
- "/assets/ProxyViewerContainer-C_Lq5T5V.js": {
129
+ "/assets/ProxyViewerContainer-BMKBs8Qz.js": {
130
130
  "type": "text/javascript; charset=utf-8",
131
- "etag": '"b580f-2yWjKUIWYUjOcG8rUV1CazfX92Y"',
132
- "mtime": "2026-07-01T04:58:19.203Z",
133
- "size": 743439,
134
- "path": "../public/assets/ProxyViewerContainer-C_Lq5T5V.js"
131
+ "etag": '"b5a7d-eALnI6HNEToSu0yqva+VFivYgJA"',
132
+ "mtime": "2026-07-01T07:15:57.121Z",
133
+ "size": 744061,
134
+ "path": "../public/assets/ProxyViewerContainer-BMKBs8Qz.js"
135
135
  }
136
136
  };
137
137
  function readAsset(id) {
package/README.md CHANGED
@@ -139,7 +139,7 @@ needed for the session timeline, request/response views, token usage, provider
139
139
  routing, errors, and knowledge workflows, but it does not retain raw request
140
140
  headers, upstream headers, or detailed streaming chunk artifacts.
141
141
 
142
- Use `full` mode when you need protocol-level diagnostics:
142
+ Use `full` mode when you need API-format-level diagnostics:
143
143
 
144
144
  ```bash
145
145
  agent-inspector --mode full
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/agent-inspector",
3
- "version": "2.0.36",
3
+ "version": "2.0.38",
4
4
  "type": "module",
5
5
  "description": "Agent observability and knowledge capture layer for AI coding tools.",
6
6
  "license": "MIT",
@@ -1,7 +1,17 @@
1
1
  import { type JSX, useCallback, useEffect, useMemo, useRef, useState, Suspense } from "react";
2
- import { ArrowDownRight, ArrowLeft, ArrowUpRight, Check, Copy, Download, Plus } from "lucide-react";
2
+ import {
3
+ ArrowDownRight,
4
+ ArrowLeft,
5
+ ArrowUpRight,
6
+ Check,
7
+ Copy,
8
+ Download,
9
+ FileJson,
10
+ Plus,
11
+ Trash2,
12
+ } from "lucide-react";
3
13
 
4
- import type { CapturedLog } from "../proxy/schemas";
14
+ import type { CapturedLog } from "../contracts";
5
15
  import { exportLogsAsZip, type ExportMode } from "../lib/export-logs";
6
16
  import type { CaptureMode, TimeDisplayFormat } from "../lib/runtimeConfig";
7
17
  import { formatTimestampRange } from "../lib/timeDisplay";
@@ -43,6 +53,22 @@ export function shouldShowRawExport(captureMode: CaptureMode): boolean {
43
53
  return captureMode === "full";
44
54
  }
45
55
 
56
+ export type ExportActionVisibility = {
57
+ redacted: boolean;
58
+ raw: boolean;
59
+ };
60
+
61
+ export function getExportActionVisibility(
62
+ captureMode: CaptureMode,
63
+ logCount: number,
64
+ ): ExportActionVisibility {
65
+ const hasLogs = logCount > 0;
66
+ return {
67
+ redacted: hasLogs,
68
+ raw: hasLogs && shouldShowRawExport(captureMode),
69
+ };
70
+ }
71
+
46
72
  function computeTokenSummary(logs: CapturedLog[]): { totalIn: number; totalOut: number } {
47
73
  let totalIn = 0;
48
74
  let totalOut = 0;
@@ -714,7 +740,7 @@ export function ProxyViewer({
714
740
  );
715
741
  const cacheTrends = useMemo(() => computeCacheTrends(groups), [groups]);
716
742
  const comparisonPredecessors = useMemo(() => buildValidPredecessors(groups), [groups]);
717
- const showRawExport = shouldShowRawExport(captureMode);
743
+ const exportActionVisibility = getExportActionVisibility(captureMode, logs.length);
718
744
  const handleCompareWithPrevious = useCallback(
719
745
  (log: CapturedLog) => {
720
746
  const predecessor = comparisonPredecessors.get(log.id);
@@ -881,7 +907,7 @@ export function ProxyViewer({
881
907
  : ""}
882
908
  </span>
883
909
  )}
884
- {logs.length > 0 && showRawExport && (
910
+ {exportActionVisibility.redacted && (
885
911
  <button
886
912
  type="button"
887
913
  onClick={() => {
@@ -901,26 +927,34 @@ export function ProxyViewer({
901
927
  )}
902
928
  </button>
903
929
  )}
904
- {logs.length > 0 && (
930
+ {exportActionVisibility.raw && (
905
931
  <button
906
932
  type="button"
907
933
  onClick={() => {
908
934
  void handleExport("raw");
909
935
  }}
910
936
  disabled={exporting !== null}
911
- className="h-8 px-2 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed rounded-md hover:bg-muted"
937
+ className="h-8 px-2.5 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5 rounded-md hover:bg-muted"
912
938
  title="Export raw logs without redaction"
913
939
  >
914
- {exporting === "raw" ? "Exporting..." : "Raw"}
940
+ {exporting === "raw" ? (
941
+ <span>Exporting...</span>
942
+ ) : (
943
+ <>
944
+ <FileJson className="size-3.5" />
945
+ <span>Raw</span>
946
+ </>
947
+ )}
915
948
  </button>
916
949
  )}
917
950
  <button
918
951
  type="button"
919
952
  onClick={onClearAll}
920
- className="h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer rounded-md hover:bg-muted"
953
+ className="h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer inline-flex items-center gap-1.5 rounded-md hover:bg-muted"
921
954
  title="Clear all logs"
922
955
  >
923
- Clear
956
+ <Trash2 className="size-3.5" />
957
+ <span>Clear</span>
924
958
  </button>
925
959
  </div>
926
960
  {exportError !== null && (
@@ -1,6 +1,6 @@
1
1
  import { useState, useEffect, useCallback, useRef, useMemo, type JSX } from "react";
2
2
  import { z } from "zod";
3
- import { CapturedLogSchema, type CapturedLog } from "../proxy/schemas";
3
+ import { CapturedLogSchema, type CapturedLog } from "../contracts";
4
4
  import { useStripConfig } from "../lib/useStripConfig";
5
5
  import { OnboardingBanner } from "./OnboardingBanner";
6
6
  import { ProxyViewer } from "./ProxyViewer";
@@ -845,7 +845,7 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
845
845
  Anthropic Format
846
846
  </button>
847
847
  </TooltipTrigger>
848
- <TooltipContent>Anthropic Messages API protocol</TooltipContent>
848
+ <TooltipContent>Anthropic Messages API format</TooltipContent>
849
849
  </Tooltip>
850
850
  </TooltipProvider>
851
851
  <TooltipProvider>
@@ -863,7 +863,7 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
863
863
  OpenAI Format
864
864
  </button>
865
865
  </TooltipTrigger>
866
- <TooltipContent>OpenAI Chat Completions API protocol</TooltipContent>
866
+ <TooltipContent>OpenAI Chat Completions API format</TooltipContent>
867
867
  </Tooltip>
868
868
  </TooltipProvider>
869
869
  </div>
@@ -29,7 +29,7 @@ import {
29
29
  type KnowledgeCandidate,
30
30
  type KnowledgeCandidateStatus,
31
31
  } from "../../knowledge/types";
32
- import type { CapturedLog } from "../../proxy/schemas";
32
+ import type { CapturedLog } from "../../contracts";
33
33
  import { Badge } from "../ui/badge";
34
34
  import { Button } from "../ui/button";
35
35
  import { buildTraceSummary, extractToolTraceEvents, type TraceSummary } from "./viewerState";
@@ -13,7 +13,7 @@ import {
13
13
  X,
14
14
  } from "lucide-react";
15
15
  import { cn, formatTokens } from "../../lib/utils";
16
- import type { CapturedLog } from "../../proxy/schemas";
16
+ import type { CapturedLog } from "../../contracts";
17
17
  import {
18
18
  type DiffOp,
19
19
  type JsonNode,
@@ -1,7 +1,7 @@
1
1
  import { useState, memo, useCallback, useEffect, useMemo } from "react";
2
2
  import type { JSX } from "react";
3
3
  import type { TimeDisplayFormat } from "../../lib/runtimeConfig";
4
- import type { CapturedLog } from "../../proxy/schemas";
4
+ import type { CapturedLog } from "../../contracts";
5
5
  import {
6
6
  ConversationHeader,
7
7
  getGroupApiFormat,
@@ -1,6 +1,6 @@
1
1
  import { type JSX, useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { useWindowVirtualizer } from "@tanstack/react-virtual";
3
- import type { CapturedLog } from "../../proxy/schemas";
3
+ import type { CapturedLog } from "../../contracts";
4
4
  import { ConversationGroup, type ConversationGroupProps } from "./ConversationGroup";
5
5
  import type { ConversationGroupData } from "./ConversationHeader";
6
6
  import { LOG_FOCUS_REQUEST_EVENT, readLogFocusRequest } from "./logFocus";
@@ -15,9 +15,10 @@ import { getSessionPath } from "../../lib/sessionUrl";
15
15
  import type { TimeDisplayFormat } from "../../lib/runtimeConfig";
16
16
  import { formatTimestampRange } from "../../lib/timeDisplay";
17
17
  import { cn, formatTokens } from "../../lib/utils";
18
- import type { CapturedLog } from "../../proxy/schemas";
18
+ import type { CapturedLog } from "../../contracts";
19
19
  import { Badge } from "../ui/badge";
20
20
  import { ConfirmDialog } from "../ui/confirm-dialog";
21
+ import { resolveLogFormat } from "./log-formats";
21
22
 
22
23
  const API_FORMAT_LABELS: Record<"anthropic" | "openai" | "unknown", string> = {
23
24
  anthropic: "Anthropic",
@@ -231,11 +232,13 @@ export type ConversationGroupData = {
231
232
  };
232
233
 
233
234
  export function getGroupApiFormat(logs: CapturedLog[]): "anthropic" | "openai" | "unknown" {
234
- const firstNonUnknown = logs.find((l) => l.apiFormat !== "unknown");
235
- if (firstNonUnknown !== undefined) {
236
- return firstNonUnknown.apiFormat;
235
+ for (const log of logs) {
236
+ const apiFormat = resolveLogFormat(log);
237
+ if (apiFormat !== "unknown") {
238
+ return apiFormat;
239
+ }
237
240
  }
238
- return logs[0]?.apiFormat ?? "unknown";
241
+ return "unknown";
239
242
  }
240
243
 
241
244
  /**
@@ -248,8 +251,9 @@ export function hasMixedApiFormat(logs: CapturedLog[]): boolean {
248
251
  let hasAnthropic = false;
249
252
  let hasOpenai = false;
250
253
  for (const log of logs) {
251
- if (log.apiFormat === "anthropic") hasAnthropic = true;
252
- else if (log.apiFormat === "openai") hasOpenai = true;
254
+ const apiFormat = resolveLogFormat(log);
255
+ if (apiFormat === "anthropic") hasAnthropic = true;
256
+ else if (apiFormat === "openai") hasOpenai = true;
253
257
  if (hasAnthropic && hasOpenai) return true;
254
258
  }
255
259
  return false;
@@ -1,7 +1,7 @@
1
1
  import { GitCompareArrows } from "lucide-react";
2
2
  import { Suspense, type JSX } from "react";
3
3
  import { useEffect, useMemo, useRef, useState, memo } from "react";
4
- import type { CapturedLog } from "../../proxy/schemas";
4
+ import type { CapturedLog } from "../../contracts";
5
5
  import type { TimeDisplayFormat } from "../../lib/runtimeConfig";
6
6
  import { stripClaudeCodeBillingHeader } from "../../proxy/claudeCodeStrip";
7
7
  import { Button } from "../ui/button";
@@ -27,7 +27,7 @@ import { memo } from "react";
27
27
  import type { TimeDisplayFormat } from "../../lib/runtimeConfig";
28
28
  import { formatTimestamp } from "../../lib/timeDisplay";
29
29
  import { cn, formatTokens, getStatusCategory, type StatusCategory } from "../../lib/utils";
30
- import type { CapturedLog } from "../../proxy/schemas";
30
+ import type { CapturedLog } from "../../contracts";
31
31
  import { Badge } from "../ui/badge";
32
32
  import { Button } from "../ui/button";
33
33
  import { ProviderLogo, detectProvider } from "../providers/ProviderLogo";
@@ -8,7 +8,7 @@ import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/
8
8
  import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog";
9
9
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
10
10
  import { ResponseView } from "./ResponseView";
11
- import { CapturedLogSchema, type CapturedLog } from "../../proxy/schemas";
11
+ import { CapturedLogSchema, type CapturedLog } from "../../contracts";
12
12
  import { dispatchLogFocusRequest } from "./logFocus";
13
13
  import {
14
14
  buildReplayComparisons,
@@ -3,7 +3,7 @@ import type { JSX } from "react";
3
3
  import { memo } from "react";
4
4
  import { useMemo } from "react";
5
5
  import { cn, formatTokens, getStatusCategory, type StatusCategory } from "../../lib/utils";
6
- import type { RequestFormat } from "../../proxy/schemas";
6
+ import type { ApiFormat } from "../../contracts";
7
7
  import { AnswerMarkdown } from "./AnswerMarkdown";
8
8
  import { formatViewFor } from "./formats";
9
9
  import { getLogFormatAdapter } from "./log-formats";
@@ -16,7 +16,7 @@ export type ResponseViewProps = {
16
16
  outputTokens: number | null;
17
17
  cacheCreationInputTokens?: number | null;
18
18
  cacheReadInputTokens?: number | null;
19
- apiFormat?: RequestFormat;
19
+ apiFormat?: ApiFormat;
20
20
  /** SSE error message from streaming response */
21
21
  error?: string | null;
22
22
  };
@@ -3,7 +3,7 @@ import { ChevronDown, ChevronRight, Loader2 } from "lucide-react";
3
3
  import { Badge } from "../ui/badge";
4
4
  import { JsonViewer } from "../ui/json-viewer";
5
5
  import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
6
- import type { StreamingChunk } from "../../proxy/schemas";
6
+ import type { StreamingChunk } from "../../contracts";
7
7
 
8
8
  export type StreamingChunkSequenceProps = {
9
9
  logId: number;
@@ -3,7 +3,7 @@ import { AlertTriangle, ChevronRight, Clock, Zap } from "lucide-react";
3
3
  import { isTurnBoundary } from "../../lib/stopReason";
4
4
  import type { TimeDisplayFormat } from "../../lib/runtimeConfig";
5
5
  import { cn, formatTokens } from "../../lib/utils";
6
- import type { CapturedLog } from "../../proxy/schemas";
6
+ import type { CapturedLog } from "../../contracts";
7
7
  import { getCrabVariant } from "../ui/crab-variants";
8
8
  import { ProviderLogo, detectProvider, type Provider } from "../providers/ProviderLogo";
9
9
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip";
@@ -1,5 +1,5 @@
1
1
  import type { ProviderConfig } from "../../../lib/providerContract";
2
- import type { CapturedLog } from "../../../proxy/schemas";
2
+ import type { CapturedLog } from "../../../contracts";
3
3
  import { getLogFormatAdapter, resolveLogFormat } from "../log-formats";
4
4
  import {
5
5
  analyzeContextIntelligence,
@@ -1,6 +1,6 @@
1
1
  import { Brain, ChevronDown, ChevronRight, Terminal } from "lucide-react";
2
2
  import { type JSX, memo, useState } from "react";
3
- import type { ResponseContentBlockType } from "../../../../proxy/schemas";
3
+ import type { ResponseContentBlockType } from "../../../../contracts/anthropic";
4
4
  import { AnswerMarkdown } from "../../AnswerMarkdown";
5
5
  import { Badge } from "../../../ui/badge";
6
6
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../../ui/collapsible";
@@ -1,6 +1,6 @@
1
1
  import { StopCircle, Zap } from "lucide-react";
2
2
  import { memo, type JSX } from "react";
3
- import type { InspectorResponse } from "../../../../proxy/schemas";
3
+ import type { AnthropicResponse } from "../../../../contracts/anthropic";
4
4
  import { formatTokens } from "../../../../lib/utils";
5
5
  import { Badge } from "../../../ui/badge";
6
6
  import { Separator } from "../../../ui/separator";
@@ -9,7 +9,7 @@ import { ResponseContentBlockRenderer } from "./ContentBlocks";
9
9
  export const StructuredResponseViewAnthropic = memo(function StructuredResponseViewAnthropic({
10
10
  response,
11
11
  }: {
12
- response: InspectorResponse;
12
+ response: AnthropicResponse;
13
13
  }): JSX.Element {
14
14
  return (
15
15
  <div className="space-y-3">
@@ -1,5 +1,7 @@
1
1
  import type { JSX } from "react";
2
- import type { InspectorResponse, OpenAIResponse, RequestFormat } from "../../../proxy/schemas";
2
+ import type { ApiFormat } from "../../../contracts";
3
+ import type { AnthropicResponse } from "../../../contracts/anthropic";
4
+ import type { OpenAIResponse } from "../../../contracts/openai";
3
5
  import { StructuredResponseViewAnthropic } from "./anthropic/ResponseView";
4
6
  import { OpenAIResponseView } from "./openai/ResponseView";
5
7
 
@@ -15,12 +17,12 @@ function isOpenAIResponse(response: unknown): response is OpenAIResponse {
15
17
  return isRecord(response) && response.object === "chat.completion";
16
18
  }
17
19
 
18
- function isAnthropicResponse(response: unknown): response is InspectorResponse {
20
+ function isAnthropicResponse(response: unknown): response is AnthropicResponse {
19
21
  return isRecord(response) && response.type === "message" && Array.isArray(response.content);
20
22
  }
21
23
 
22
24
  export function formatViewFor(
23
- apiFormat: RequestFormat | undefined,
25
+ apiFormat: ApiFormat | undefined,
24
26
  response: unknown,
25
27
  ): JSX.Element | null {
26
28
  if (apiFormat === "openai" && isOpenAIResponse(response)) {
@@ -1,6 +1,6 @@
1
1
  import { StopCircle, Terminal, Zap } from "lucide-react";
2
2
  import { memo, useState, type JSX } from "react";
3
- import type { OpenAIResponse, OpenAIToolCall } from "../../../../proxy/schemas";
3
+ import type { OpenAIResponse, OpenAIToolCall } from "../../../../contracts/openai";
4
4
  import { formatTokens } from "../../../../lib/utils";
5
5
  import { AnswerMarkdown } from "../../AnswerMarkdown";
6
6
  import { Badge } from "../../../ui/badge";
@@ -1,7 +1,4 @@
1
- import {
2
- AnthropicRequestSchema,
3
- AnthropicResponseSchema,
4
- } from "../../../proxy/formats/anthropic/schemas";
1
+ import { AnthropicRequestSchema, AnthropicResponseSchema } from "../../../contracts/anthropic";
5
2
  import type { AnatomySegment } from "../anatomy/types";
6
3
  import { countCharacters, estimateTokens } from "../anatomy/tokenEstimate";
7
4
  import type { LogFormatAdapter } from "./types";
@@ -1,22 +1,22 @@
1
- import { requestFormatForPath } from "../../../proxy/formats";
2
- import type { CapturedLog, RequestFormat } from "../../../proxy/schemas";
1
+ import { apiFormatForPath } from "../../../proxy/formats";
2
+ import type { ApiFormat, CapturedLog } from "../../../contracts";
3
3
  import { anthropicLogFormatAdapter } from "./anthropic";
4
4
  import { openAILogFormatAdapter } from "./openai";
5
5
  import type { LogFormatAdapter } from "./types";
6
6
  import { unknownLogFormatAdapter } from "./unknown";
7
7
 
8
- const ADAPTERS: Record<RequestFormat, LogFormatAdapter> = {
8
+ const ADAPTERS: Record<ApiFormat, LogFormatAdapter> = {
9
9
  anthropic: anthropicLogFormatAdapter,
10
10
  openai: openAILogFormatAdapter,
11
11
  unknown: unknownLogFormatAdapter,
12
12
  };
13
13
 
14
- export function getLogFormatAdapter(format: RequestFormat): LogFormatAdapter {
14
+ export function getLogFormatAdapter(format: ApiFormat): LogFormatAdapter {
15
15
  return ADAPTERS[format];
16
16
  }
17
17
 
18
- export function resolveLogFormat(log: Pick<CapturedLog, "path" | "apiFormat">): RequestFormat {
19
- const pathFormat = requestFormatForPath(log.path);
18
+ export function resolveLogFormat(log: Pick<CapturedLog, "path" | "apiFormat">): ApiFormat {
19
+ const pathFormat = apiFormatForPath(log.path);
20
20
  return pathFormat === "unknown" ? log.apiFormat : pathFormat;
21
21
  }
22
22
 
@@ -1,4 +1,4 @@
1
- import { OpenAIRequestSchema, parseOpenAIResponse } from "../../../proxy/formats/openai/schemas";
1
+ import { OpenAIRequestSchema, parseOpenAIResponse } from "../../../contracts/openai";
2
2
  import type { AnatomySegment } from "../anatomy/types";
3
3
  import { countCharacters, estimateTokens } from "../anatomy/tokenEstimate";
4
4
  import type { LogFormatAdapter } from "./types";
@@ -1,4 +1,4 @@
1
- import type { RequestFormat } from "../../../proxy/schemas";
1
+ import type { ApiFormat } from "../../../contracts";
2
2
  import type { AnatomySegment } from "../anatomy/types";
3
3
 
4
4
  export type RequestAnalysis = {
@@ -14,7 +14,7 @@ export type ResponseAnalysis = {
14
14
  };
15
15
 
16
16
  export type LogFormatAdapter = {
17
- readonly format: RequestFormat;
17
+ readonly format: ApiFormat;
18
18
  analyzeRequest(rawBody: string | null): RequestAnalysis;
19
19
  analyzeResponse(responseText: string | null): ResponseAnalysis;
20
20
  /**
@@ -1,6 +1,6 @@
1
1
  import { extractStopReason, isTurnBoundary, type StopReason } from "../../lib/stopReason";
2
2
  import { safeGetOwnProperty } from "../../lib/objectUtils";
3
- import type { CapturedLog } from "../../proxy/schemas";
3
+ import type { CapturedLog } from "../../contracts";
4
4
  import { resolveLogFormat } from "./log-formats";
5
5
 
6
6
  export type TurnEntry = {