@tonyclaw/agent-inspector 3.0.29 → 3.0.30

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 (152) hide show
  1. package/.output/cli.js +791 -191
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/CompareDrawer-B6cXJohL.js +1 -0
  4. package/.output/public/assets/InspectorPet-mAzeGmEE.js +4108 -0
  5. package/.output/public/assets/ProxyViewerContainer-CkFWv_Nm.js +126 -0
  6. package/.output/public/assets/{ReplayDialog-CctnITbt.js → ReplayDialog-DodiTL6E.js} +1 -1
  7. package/.output/public/assets/{RequestAnatomy-BrN0RBbL.js → RequestAnatomy-B3IauToI.js} +1 -1
  8. package/.output/public/assets/ResponseView-Hn-5ordK.js +3 -0
  9. package/.output/public/assets/StreamingChunkSequence-vLDOVQM_.js +1 -0
  10. package/.output/public/assets/_sessionId-BFH4TOaI.js +1 -0
  11. package/.output/public/assets/{_sessionId-DKM91udC.js → _sessionId-Cj_HIFZx.js} +1 -1
  12. package/.output/public/assets/{index-Bq-nqolG.js → index-Bmj2lcWE.js} +1 -1
  13. package/.output/public/assets/index-CgJj-HBC.css +1 -0
  14. package/.output/public/assets/index-Ch7uqnCT.js +1 -0
  15. package/.output/public/assets/index-DkDdKvLl.js +76 -0
  16. package/.output/public/assets/{index-DPiESBo2.js → index-ZlhqCrSg.js} +6 -6
  17. package/.output/public/assets/{json-viewer-CfaDJwx8.js → json-viewer-BdrnVQu-.js} +1 -1
  18. package/.output/public/assets/jszip.min-CS_nt_o1.js +2 -0
  19. package/.output/public/assets/qwen-mMn3f5ul.webp +0 -0
  20. package/.output/server/_libs/jszip.mjs +35 -9
  21. package/.output/server/_libs/lucide-react.mjs +86 -86
  22. package/.output/server/_libs/radix-ui__react-dialog.mjs +3 -3
  23. package/.output/server/_libs/tanstack__react-router.mjs +1 -1
  24. package/.output/server/{_sessionId-CwGXPD4C.mjs → _sessionId-C5S3pGZd.mjs} +15 -15
  25. package/.output/server/_sessionId-g6D69lKq.mjs +81 -0
  26. package/.output/server/_ssr/{CompareDrawer-R_P1-VNr.mjs → CompareDrawer-CqUmGHal.mjs} +138 -103
  27. package/.output/server/_ssr/InspectorPet-DI0UJd01.mjs +925 -0
  28. package/.output/server/_ssr/{ProxyViewerContainer-DUgxRNbj.mjs → ProxyViewerContainer-CvsUmbJ3.mjs} +1157 -1613
  29. package/.output/server/_ssr/{ReplayDialog-De9wKKzS.mjs → ReplayDialog-DS48dpFG.mjs} +18 -18
  30. package/.output/server/_ssr/{RequestAnatomy-Db4MxUYo.mjs → RequestAnatomy-DxYVQU1C.mjs} +58 -58
  31. package/.output/server/_ssr/{ResponseView-8rDXOiNB.mjs → ResponseView-BLXW73eq.mjs} +60 -60
  32. package/.output/server/_ssr/{StreamingChunkSequence-N9teZHkA.mjs → StreamingChunkSequence-DD0iFuy0.mjs} +16 -16
  33. package/.output/server/_ssr/{index-D4DvByuW.mjs → index-B10LIaK0.mjs} +15 -15
  34. package/.output/server/_ssr/index-RHVJnU2p.mjs +81 -0
  35. package/.output/server/_ssr/index.mjs +2 -2
  36. package/.output/server/_ssr/{json-viewer-BjoHRbjS.mjs → json-viewer-LDu2XBaK.mjs} +58 -58
  37. package/.output/server/_ssr/{router-BYlePDj8.mjs → router-GKvrwifJ.mjs} +9005 -6488
  38. package/.output/server/_tanstack-start-manifest_v-Df2n4mbT.mjs +4 -0
  39. package/.output/server/index.mjs +103 -75
  40. package/.output/workers/logFinalizer.worker.js +16908 -0
  41. package/.output/workers/sessionWorkerEntry.js +16904 -0
  42. package/README.md +21 -5
  43. package/package.json +6 -2
  44. package/src/assets/logos/qwen.webp +0 -0
  45. package/src/cli.ts +85 -10
  46. package/src/components/OnboardingBanner.tsx +41 -21
  47. package/src/components/ProxyViewer.tsx +198 -269
  48. package/src/components/ProxyViewerContainer.tsx +128 -17
  49. package/src/components/ecosystem/AgentLabDialog.tsx +166 -19
  50. package/src/components/errors/SafeErrorBoundary.tsx +201 -0
  51. package/src/components/pi-agent/PiAgentPanel.tsx +4 -1
  52. package/src/components/providers/ImportWizardDialog.tsx +18 -42
  53. package/src/components/providers/ProviderLogo.tsx +1 -1
  54. package/src/components/providers/ProvidersPanel.tsx +8 -5
  55. package/src/components/providers/SettingsDialog.tsx +34 -11
  56. package/src/components/proxy-viewer/ApplicationBar.tsx +63 -0
  57. package/src/components/proxy-viewer/CompareDrawer.tsx +92 -49
  58. package/src/components/proxy-viewer/LiveConnectionStatus.tsx +186 -0
  59. package/src/components/proxy-viewer/LogEntry.tsx +283 -275
  60. package/src/components/proxy-viewer/RawExportWarning.tsx +77 -0
  61. package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +61 -0
  62. package/src/components/proxy-viewer/ThreadConnector.tsx +16 -1
  63. package/src/components/proxy-viewer/TurnGroup.tsx +13 -2
  64. package/src/components/proxy-viewer/accessibility.ts +8 -0
  65. package/src/components/proxy-viewer/lazy.ts +4 -0
  66. package/src/components/proxy-viewer/liveConnectionState.ts +158 -0
  67. package/src/components/proxy-viewer/proxyViewerLogic.ts +10 -0
  68. package/src/components/proxy-viewer/viewerState.ts +2 -6
  69. package/src/components/ui/select.tsx +1 -1
  70. package/src/components/ui/tabs.tsx +3 -3
  71. package/src/components/ui/transient-toast.tsx +1 -1
  72. package/src/lib/apiClient.ts +17 -2
  73. package/src/lib/ecosystemContract.ts +34 -0
  74. package/src/lib/export-logs.ts +1 -1
  75. package/src/lib/providerContract.ts +70 -4
  76. package/src/lib/providerImportContract.ts +27 -0
  77. package/src/lib/providerModelMetadata.ts +16 -7
  78. package/src/lib/resourceLimits.ts +152 -0
  79. package/src/lib/safeDiagnostic.ts +38 -0
  80. package/src/lib/useProviders.ts +4 -4
  81. package/src/mcp/server.ts +39 -6
  82. package/src/mcp/toolHandlers.ts +131 -4
  83. package/src/proxy/chunkStorage.ts +20 -6
  84. package/src/proxy/config.ts +20 -6
  85. package/src/proxy/dataDir.ts +3 -0
  86. package/src/proxy/ecosystemExecutionConfirmation.ts +114 -0
  87. package/src/proxy/ecosystemExecutionRoute.ts +116 -0
  88. package/src/proxy/ecosystemTasks.ts +48 -0
  89. package/src/proxy/evidenceExporter.ts +23 -9
  90. package/src/proxy/groupEvidenceExporter.ts +26 -5
  91. package/src/proxy/groupStore.ts +13 -3
  92. package/src/proxy/handler.ts +103 -20
  93. package/src/proxy/identityProxy.ts +333 -14
  94. package/src/proxy/logFinalizer.ts +73 -6
  95. package/src/proxy/logImportUpload.ts +128 -0
  96. package/src/proxy/logImporter.ts +321 -70
  97. package/src/proxy/logIndex.ts +16 -6
  98. package/src/proxy/logger.ts +294 -41
  99. package/src/proxy/privateDataPath.ts +183 -0
  100. package/src/proxy/providerScanStore.ts +87 -0
  101. package/src/proxy/providerSecretStore.ts +58 -19
  102. package/src/proxy/providers.ts +175 -58
  103. package/src/proxy/rawStreamCapture.ts +66 -5
  104. package/src/proxy/runStore.ts +13 -3
  105. package/src/proxy/runtimeAdmission.ts +52 -0
  106. package/src/proxy/runtimeHealth.ts +206 -0
  107. package/src/proxy/runtimeShutdown.ts +75 -0
  108. package/src/proxy/sessionArchive.ts +15 -2
  109. package/src/proxy/sessionProcess.ts +19 -0
  110. package/src/proxy/sessionRuntime.ts +7 -0
  111. package/src/proxy/shutdownCoordinator.ts +90 -0
  112. package/src/proxy/sqliteLogIndex.ts +18 -2
  113. package/src/proxy/store.ts +18 -2
  114. package/src/routes/__root.tsx +18 -1
  115. package/src/routes/api/alerts.summary.ts +12 -8
  116. package/src/routes/api/alerts.ts +27 -9
  117. package/src/routes/api/ecosystem.packages.$packageId.help.ts +3 -8
  118. package/src/routes/api/ecosystem.packages.$packageId.install.ts +3 -8
  119. package/src/routes/api/ecosystem.packages.$packageId.runner-presets.ts +3 -11
  120. package/src/routes/api/ecosystem.packages.$packageId.upgrade.ts +3 -8
  121. package/src/routes/api/ecosystem.recipes.$recipeId.run.ts +3 -8
  122. package/src/routes/api/health.ts +9 -2
  123. package/src/routes/api/knowledge.project-context.ts +11 -0
  124. package/src/routes/api/knowledge.search.ts +33 -2
  125. package/src/routes/api/logs.$id.body.ts +16 -2
  126. package/src/routes/api/logs.import.ts +7 -18
  127. package/src/routes/api/logs.stream.ts +147 -69
  128. package/src/routes/api/logs.ts +55 -14
  129. package/src/routes/api/providers.$providerId.model-metadata.ts +22 -4
  130. package/src/routes/api/providers.$providerId.ts +37 -6
  131. package/src/routes/api/providers.export.ts +34 -16
  132. package/src/routes/api/providers.import.ts +42 -8
  133. package/src/routes/api/providers.scan.ts +13 -8
  134. package/src/routes/api/providers.ts +36 -5
  135. package/src/routes/api/runs.ts +12 -12
  136. package/src/routes/api/sessions.ts +15 -8
  137. package/src/routes/index.tsx +6 -0
  138. package/src/routes/livez.ts +13 -0
  139. package/src/routes/readyz.ts +18 -0
  140. package/src/routes/session/$sessionId.tsx +6 -0
  141. package/styles/globals.css +19 -3
  142. package/.output/public/assets/CompareDrawer-Cb5ccPeX.js +0 -1
  143. package/.output/public/assets/ProxyViewerContainer-COBe5GTT.js +0 -4234
  144. package/.output/public/assets/ResponseView-Cqq87lX_.js +0 -3
  145. package/.output/public/assets/StreamingChunkSequence-CnTa9mCr.js +0 -1
  146. package/.output/public/assets/index-DTLNVcgc.js +0 -76
  147. package/.output/public/assets/index-DliqmwUw.css +0 -1
  148. package/.output/public/assets/qwen-CONDcHqt.png +0 -0
  149. package/.output/server/_tanstack-start-manifest_v-DMCA8KK3.mjs +0 -4
  150. package/src/assets/logos/mcp.png +0 -0
  151. package/src/assets/logos/qwen.png +0 -0
  152. package/src/components/ui/mcp-logo.tsx +0 -20
@@ -10,6 +10,7 @@ import {
10
10
  import { ApiTimeoutError, fetchJsonWithTimeout } from "../../lib/apiClient";
11
11
  import type { TimeDisplayFormat } from "../../lib/runtimeConfig";
12
12
  import { cn } from "../../lib/utils";
13
+ import { LazyFeatureBoundary } from "../errors/SafeErrorBoundary";
13
14
  import { stripClaudeCodeBillingHeader } from "../../proxy/claudeCodeStrip";
14
15
  import { Button } from "../ui/button";
15
16
  import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
@@ -863,54 +864,92 @@ export const LogEntry = memo(function ({
863
864
  className="pointer-events-none absolute inset-x-0 top-0 h-12 bg-gradient-to-b from-white/[0.05] to-transparent"
864
865
  aria-hidden="true"
865
866
  />
866
- <Tabs value={activeTab} onValueChange={setActiveTab} className="gap-2">
867
- <TabsList
868
- variant="line"
869
- className="bg-white/[0.04] inspector-scrollbar relative mx-3 h-auto w-[calc(100%-1.5rem)] justify-start overflow-x-auto rounded-[8px] p-1"
870
- >
871
- {shouldShowRawHeadersTab(
872
- viewMode,
873
- displayLog.rawHeaders !== undefined &&
874
- Object.keys(displayLog.rawHeaders).length > 0,
875
- ) && (
876
- <TabsTrigger className={TAB_TRIGGER_CLASS} value="raw-headers">
877
- Raw Headers
878
- </TabsTrigger>
879
- )}
880
- {shouldShowHeadersTab(
881
- viewMode,
882
- displayLog.headers !== undefined && Object.keys(displayLog.headers).length > 0,
883
- ) && (
884
- <TabsTrigger className={TAB_TRIGGER_CLASS} value="headers">
885
- Headers
867
+ <LazyFeatureBoundary feature="Log details">
868
+ <Tabs value={activeTab} onValueChange={setActiveTab} className="gap-2">
869
+ <TabsList
870
+ variant="line"
871
+ className="bg-white/[0.04] inspector-scrollbar relative mx-3 h-auto w-[calc(100%-1.5rem)] justify-start overflow-x-auto rounded-[8px] p-1"
872
+ >
873
+ {shouldShowRawHeadersTab(
874
+ viewMode,
875
+ displayLog.rawHeaders !== undefined &&
876
+ Object.keys(displayLog.rawHeaders).length > 0,
877
+ ) && (
878
+ <TabsTrigger className={TAB_TRIGGER_CLASS} value="raw-headers">
879
+ Raw Headers
880
+ </TabsTrigger>
881
+ )}
882
+ {shouldShowHeadersTab(
883
+ viewMode,
884
+ displayLog.headers !== undefined && Object.keys(displayLog.headers).length > 0,
885
+ ) && (
886
+ <TabsTrigger className={TAB_TRIGGER_CLASS} value="headers">
887
+ Headers
888
+ </TabsTrigger>
889
+ )}
890
+ {anatomySegments !== null && (
891
+ <TabsTrigger className={TAB_TRIGGER_CLASS} value="anatomy">
892
+ Context
893
+ </TabsTrigger>
894
+ )}
895
+ {shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && (
896
+ <TabsTrigger className={TAB_TRIGGER_CLASS} value="raw-request">
897
+ Raw Request
898
+ </TabsTrigger>
899
+ )}
900
+ <TabsTrigger className={TAB_TRIGGER_CLASS} value="request">
901
+ Request
886
902
  </TabsTrigger>
887
- )}
888
- {anatomySegments !== null && (
889
- <TabsTrigger className={TAB_TRIGGER_CLASS} value="anatomy">
890
- Context
903
+ {viewMode === "full" && (
904
+ <TabsTrigger className={TAB_TRIGGER_CLASS} value="raw">
905
+ Raw Response
906
+ </TabsTrigger>
907
+ )}
908
+ <TabsTrigger className={TAB_TRIGGER_CLASS} value="parsed">
909
+ Response
891
910
  </TabsTrigger>
892
- )}
911
+ </TabsList>
912
+
893
913
  {shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && (
894
- <TabsTrigger className={TAB_TRIGGER_CLASS} value="raw-request">
895
- Raw Request
896
- </TabsTrigger>
897
- )}
898
- <TabsTrigger className={TAB_TRIGGER_CLASS} value="request">
899
- Request
900
- </TabsTrigger>
901
- {viewMode === "full" && (
902
- <TabsTrigger className={TAB_TRIGGER_CLASS} value="raw">
903
- Raw Response
904
- </TabsTrigger>
914
+ <TabsContent value="raw-request">
915
+ {activeTab === "raw-request" && (
916
+ <ExpandedPanel>
917
+ <FullBodyHydrationNotice
918
+ status={bodyHydrationStatus}
919
+ error={bodyHydrationError}
920
+ footprint={fullBodyHydrationFootprint}
921
+ onRetry={retryFullBodyHydration}
922
+ />
923
+ {useChunkedBody && (
924
+ <BodyPreviewNotice
925
+ label="Request"
926
+ state={requestPreview}
927
+ expectedBytes={log.rawRequestBodyBytes ?? null}
928
+ onLoadMore={() => loadBodyChunk("request", requestPreview.offset)}
929
+ />
930
+ )}
931
+ {rawRequestBodyForView === null ? (
932
+ <EmptyState>
933
+ {useChunkedBody ? "Request preview is loading" : "No request body"}
934
+ </EmptyState>
935
+ ) : rawRequestExpansion.parsedData !== null ? (
936
+ <Suspense fallback={<TabFallback />}>
937
+ <LazyJsonViewer
938
+ data={rawRequestExpansion.parsedData}
939
+ bulkDepth={rawRequestExpansion.bulkDepth}
940
+ bulkRevision={rawRequestExpansion.bulkRevision}
941
+ />
942
+ </Suspense>
943
+ ) : (
944
+ <RawTextBlock>{rawRequestBodyForView}</RawTextBlock>
945
+ )}
946
+ </ExpandedPanel>
947
+ )}
948
+ </TabsContent>
905
949
  )}
906
- <TabsTrigger className={TAB_TRIGGER_CLASS} value="parsed">
907
- Response
908
- </TabsTrigger>
909
- </TabsList>
910
950
 
911
- {shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && (
912
- <TabsContent value="raw-request">
913
- {activeTab === "raw-request" && (
951
+ <TabsContent value="request">
952
+ {activeTab === "request" && (
914
953
  <ExpandedPanel>
915
954
  <FullBodyHydrationNotice
916
955
  status={bodyHydrationStatus}
@@ -926,268 +965,237 @@ export const LogEntry = memo(function ({
926
965
  onLoadMore={() => loadBodyChunk("request", requestPreview.offset)}
927
966
  />
928
967
  )}
929
- {rawRequestBodyForView === null ? (
930
- <EmptyState>
931
- {useChunkedBody ? "Request preview is loading" : "No request body"}
932
- </EmptyState>
933
- ) : rawRequestExpansion.parsedData !== null ? (
934
- <Suspense fallback={<TabFallback />}>
935
- <LazyJsonViewer
936
- data={rawRequestExpansion.parsedData}
937
- bulkDepth={rawRequestExpansion.bulkDepth}
938
- bulkRevision={rawRequestExpansion.bulkRevision}
939
- />
940
- </Suspense>
968
+ {warnings.length > 0 && (
969
+ <NoticeBlock tone="warning">
970
+ <div className="flex items-center gap-2 font-semibold text-amber-300">
971
+ <AlertTriangle className="size-3.5" />
972
+ Tool schema warnings
973
+ </div>
974
+ <ul className="mt-2 space-y-1 text-muted-foreground">
975
+ {warnings.map((warning) => (
976
+ <li key={warning}>{warning}</li>
977
+ ))}
978
+ </ul>
979
+ </NoticeBlock>
980
+ )}
981
+ <RequestTools
982
+ summary={requestTools}
983
+ calledToolNames={responseAnalysis.toolNames ?? []}
984
+ />
985
+ {/* All tab actions (Copy, Expand, Diff with Raw, Diff with
986
+ Previous) live in the log header. The body is just
987
+ the diff or JSON view, depending on toggle state. */}
988
+ {requestDiff ? (
989
+ <RequestDiffContent
990
+ rawBody={displayLog.rawRequestBody}
991
+ displayedBody={displayedRequestBody}
992
+ emptyLabel="No transformation applied; raw and sent request bodies are identical."
993
+ />
941
994
  ) : (
942
- <RawTextBlock>{rawRequestBodyForView}</RawTextBlock>
995
+ <div ref={requestJsonRef}>
996
+ {displayedRequestBody === null ? (
997
+ <EmptyState>
998
+ {useChunkedBody ? "Request preview is loading" : "No request body"}
999
+ </EmptyState>
1000
+ ) : requestExpansion.parsedData !== null ? (
1001
+ <Suspense fallback={<TabFallback />}>
1002
+ <LazyJsonViewer
1003
+ data={requestExpansion.parsedData}
1004
+ bulkDepth={requestExpansion.bulkDepth}
1005
+ bulkRevision={requestExpansion.bulkRevision}
1006
+ anatomyPaths={anatomyPaths}
1007
+ expandToPath={expandToPath}
1008
+ />
1009
+ </Suspense>
1010
+ ) : (
1011
+ <RawTextBlock>{displayedRequestBody}</RawTextBlock>
1012
+ )}
1013
+ </div>
943
1014
  )}
944
1015
  </ExpandedPanel>
945
1016
  )}
946
1017
  </TabsContent>
947
- )}
948
1018
 
949
- <TabsContent value="request">
950
- {activeTab === "request" && (
951
- <ExpandedPanel>
952
- <FullBodyHydrationNotice
953
- status={bodyHydrationStatus}
954
- error={bodyHydrationError}
955
- footprint={fullBodyHydrationFootprint}
956
- onRetry={retryFullBodyHydration}
957
- />
958
- {useChunkedBody && (
959
- <BodyPreviewNotice
960
- label="Request"
961
- state={requestPreview}
962
- expectedBytes={log.rawRequestBodyBytes ?? null}
963
- onLoadMore={() => loadBodyChunk("request", requestPreview.offset)}
964
- />
965
- )}
966
- {warnings.length > 0 && (
967
- <NoticeBlock tone="warning">
968
- <div className="flex items-center gap-2 font-semibold text-amber-300">
969
- <AlertTriangle className="size-3.5" />
970
- Tool schema warnings
971
- </div>
972
- <ul className="mt-2 space-y-1 text-muted-foreground">
973
- {warnings.map((warning) => (
974
- <li key={warning}>{warning}</li>
975
- ))}
976
- </ul>
977
- </NoticeBlock>
1019
+ {anatomySegments !== null && (
1020
+ <TabsContent value="anatomy">
1021
+ {activeTab === "anatomy" && (
1022
+ <ExpandedPanel className="p-0">
1023
+ <Suspense fallback={<TabFallback />}>
1024
+ <LazyRequestAnatomy
1025
+ parsed={requestExpansion.parsedData}
1026
+ inputTokens={displayLog.inputTokens ?? null}
1027
+ model={displayLog.model}
1028
+ segments={anatomySegments}
1029
+ onSegmentActivate={jumpToAnatomySegment}
1030
+ />
1031
+ </Suspense>
1032
+ </ExpandedPanel>
978
1033
  )}
979
- <RequestTools
980
- summary={requestTools}
981
- calledToolNames={responseAnalysis.toolNames ?? []}
982
- />
983
- {/* All tab actions (Copy, Expand, Diff with Raw, Diff with
984
- Previous) live in the log header. The body is just
985
- the diff or JSON view, depending on toggle state. */}
986
- {requestDiff ? (
987
- <RequestDiffContent
988
- rawBody={displayLog.rawRequestBody}
989
- displayedBody={displayedRequestBody}
990
- emptyLabel="No transformation applied; raw and sent request bodies are identical."
991
- />
992
- ) : (
993
- <div ref={requestJsonRef}>
994
- {displayedRequestBody === null ? (
995
- <EmptyState>
996
- {useChunkedBody ? "Request preview is loading" : "No request body"}
997
- </EmptyState>
998
- ) : requestExpansion.parsedData !== null ? (
999
- <Suspense fallback={<TabFallback />}>
1000
- <LazyJsonViewer
1001
- data={requestExpansion.parsedData}
1002
- bulkDepth={requestExpansion.bulkDepth}
1003
- bulkRevision={requestExpansion.bulkRevision}
1004
- anatomyPaths={anatomyPaths}
1005
- expandToPath={expandToPath}
1034
+ </TabsContent>
1035
+ )}
1036
+
1037
+ {shouldShowHeadersTab(
1038
+ viewMode,
1039
+ displayLog.headers !== undefined && Object.keys(displayLog.headers).length > 0,
1040
+ ) && (
1041
+ <TabsContent value="headers">
1042
+ {activeTab === "headers" && (
1043
+ <ExpandedPanel>
1044
+ {/* Copy lives in the log header. */}
1045
+ <div className="flex justify-end gap-2 mb-2">
1046
+ {shouldShowHeadersDiffButton(
1047
+ viewMode,
1048
+ displayLog.rawHeaders !== undefined &&
1049
+ Object.keys(displayLog.rawHeaders).length > 0,
1050
+ ) && (
1051
+ <DiffToggleButton
1052
+ active={headersDiff}
1053
+ onClick={(e) => {
1054
+ e.stopPropagation();
1055
+ setHeadersDiff(!headersDiff);
1056
+ }}
1006
1057
  />
1007
- </Suspense>
1058
+ )}
1059
+ </div>
1060
+ {headersDiff ? (
1061
+ <HeadersDiffContent
1062
+ rawHeaders={displayLog.rawHeaders}
1063
+ headers={displayLog.headers}
1064
+ emptyLabel="No transformation applied; raw and processed headers are identical."
1065
+ />
1008
1066
  ) : (
1009
- <RawTextBlock>{displayedRequestBody}</RawTextBlock>
1067
+ <HeaderRows
1068
+ headers={displayLog.headers}
1069
+ emptyLabel="No headers captured"
1070
+ />
1010
1071
  )}
1011
- </div>
1072
+ </ExpandedPanel>
1012
1073
  )}
1013
- </ExpandedPanel>
1074
+ </TabsContent>
1014
1075
  )}
1015
- </TabsContent>
1016
1076
 
1017
- {anatomySegments !== null && (
1018
- <TabsContent value="anatomy">
1019
- {activeTab === "anatomy" && (
1020
- <ExpandedPanel className="p-0">
1021
- <Suspense fallback={<TabFallback />}>
1022
- <LazyRequestAnatomy
1023
- parsed={requestExpansion.parsedData}
1024
- inputTokens={displayLog.inputTokens ?? null}
1025
- model={displayLog.model}
1026
- segments={anatomySegments}
1027
- onSegmentActivate={jumpToAnatomySegment}
1077
+ {shouldShowRawHeadersTab(
1078
+ viewMode,
1079
+ displayLog.rawHeaders !== undefined &&
1080
+ Object.keys(displayLog.rawHeaders).length > 0,
1081
+ ) && (
1082
+ <TabsContent value="raw-headers">
1083
+ {activeTab === "raw-headers" && (
1084
+ <ExpandedPanel>
1085
+ {/* Copy lives in the log header. */}
1086
+ <HeaderRows
1087
+ headers={displayLog.rawHeaders}
1088
+ emptyLabel="No raw headers captured"
1028
1089
  />
1029
- </Suspense>
1030
- </ExpandedPanel>
1031
- )}
1032
- </TabsContent>
1033
- )}
1090
+ </ExpandedPanel>
1091
+ )}
1092
+ </TabsContent>
1093
+ )}
1034
1094
 
1035
- {shouldShowHeadersTab(
1036
- viewMode,
1037
- displayLog.headers !== undefined && Object.keys(displayLog.headers).length > 0,
1038
- ) && (
1039
- <TabsContent value="headers">
1040
- {activeTab === "headers" && (
1041
- <ExpandedPanel>
1042
- {/* Copy lives in the log header. */}
1043
- <div className="flex justify-end gap-2 mb-2">
1044
- {shouldShowHeadersDiffButton(
1045
- viewMode,
1046
- displayLog.rawHeaders !== undefined &&
1047
- Object.keys(displayLog.rawHeaders).length > 0,
1048
- ) && (
1049
- <DiffToggleButton
1050
- active={headersDiff}
1051
- onClick={(e) => {
1052
- e.stopPropagation();
1053
- setHeadersDiff(!headersDiff);
1054
- }}
1055
- />
1056
- )}
1057
- </div>
1058
- {headersDiff ? (
1059
- <HeadersDiffContent
1060
- rawHeaders={displayLog.rawHeaders}
1061
- headers={displayLog.headers}
1062
- emptyLabel="No transformation applied; raw and processed headers are identical."
1095
+ <TabsContent value="raw">
1096
+ {activeTab === "raw" && (
1097
+ <ExpandedPanel className="space-y-3">
1098
+ <FullBodyHydrationNotice
1099
+ status={bodyHydrationStatus}
1100
+ error={bodyHydrationError}
1101
+ footprint={fullBodyHydrationFootprint}
1102
+ onRetry={retryFullBodyHydration}
1103
+ />
1104
+ {useChunkedBody && (
1105
+ <BodyPreviewNotice
1106
+ label="Response"
1107
+ state={responsePreview}
1108
+ expectedBytes={log.responseTextBytes ?? null}
1109
+ onLoadMore={() => loadBodyChunk("response", responsePreview.offset)}
1063
1110
  />
1111
+ )}
1112
+ {displayLog.error !== undefined && displayLog.error !== null && (
1113
+ <NoticeBlock tone="danger" title="SSE Error">
1114
+ <span className="font-mono">{displayLog.error}</span>
1115
+ </NoticeBlock>
1116
+ )}
1117
+ {responseTextForView !== null && responsePreviewShouldParse ? (
1118
+ <Suspense fallback={<TabFallback />}>
1119
+ <LazyJsonViewerFromString
1120
+ text={responseTextForView}
1121
+ defaultExpandDepth={0}
1122
+ bulkDepth={responseExpansion.bulkDepth}
1123
+ bulkRevision={responseExpansion.bulkRevision}
1124
+ />
1125
+ </Suspense>
1126
+ ) : responseTextForView !== null ? (
1127
+ <RawTextBlock>{responseTextForView}</RawTextBlock>
1064
1128
  ) : (
1065
- <HeaderRows headers={displayLog.headers} emptyLabel="No headers captured" />
1129
+ <EmptyState>
1130
+ {useChunkedBody ? "Response preview is loading" : "No response"}
1131
+ </EmptyState>
1132
+ )}
1133
+ {displayLog.streaming === true && (
1134
+ <Suspense fallback={<TabFallback />}>
1135
+ <LazyStreamingChunkSequence
1136
+ logId={displayLog.id}
1137
+ truncated={displayLog.streamingChunksPath !== null}
1138
+ />
1139
+ </Suspense>
1066
1140
  )}
1067
1141
  </ExpandedPanel>
1068
1142
  )}
1069
1143
  </TabsContent>
1070
- )}
1071
1144
 
1072
- {shouldShowRawHeadersTab(
1073
- viewMode,
1074
- displayLog.rawHeaders !== undefined &&
1075
- Object.keys(displayLog.rawHeaders).length > 0,
1076
- ) && (
1077
- <TabsContent value="raw-headers">
1078
- {activeTab === "raw-headers" && (
1145
+ <TabsContent value="parsed">
1146
+ {activeTab === "parsed" && (
1079
1147
  <ExpandedPanel>
1080
- {/* Copy lives in the log header. */}
1081
- <HeaderRows
1082
- headers={displayLog.rawHeaders}
1083
- emptyLabel="No raw headers captured"
1148
+ <FullBodyHydrationNotice
1149
+ status={bodyHydrationStatus}
1150
+ error={bodyHydrationError}
1151
+ footprint={fullBodyHydrationFootprint}
1152
+ onRetry={retryFullBodyHydration}
1084
1153
  />
1154
+ {useChunkedBody && (
1155
+ <BodyPreviewNotice
1156
+ label="Response"
1157
+ state={responsePreview}
1158
+ expectedBytes={log.responseTextBytes ?? null}
1159
+ onLoadMore={() => loadBodyChunk("response", responsePreview.offset)}
1160
+ />
1161
+ )}
1162
+ {responsePreviewIsActive && !responsePreviewShouldParse ? (
1163
+ <RawTextBlock>{responseTextForView}</RawTextBlock>
1164
+ ) : (
1165
+ <Suspense fallback={<TabFallback />}>
1166
+ <LazyResponseView
1167
+ responseText={responseTextForView}
1168
+ responseStatus={displayLog.responseStatus}
1169
+ streaming={displayLog.streaming}
1170
+ inputTokens={displayLog.inputTokens}
1171
+ outputTokens={displayLog.outputTokens}
1172
+ cacheCreationInputTokens={displayLog.cacheCreationInputTokens}
1173
+ cacheReadInputTokens={displayLog.cacheReadInputTokens}
1174
+ apiFormat={resolvedFormat}
1175
+ error={displayLog.error}
1176
+ focusedToolIndex={focusedToolIndex}
1177
+ toolFocusNonce={toolFocusNonce}
1178
+ />
1179
+ </Suspense>
1180
+ )}
1085
1181
  </ExpandedPanel>
1086
1182
  )}
1087
1183
  </TabsContent>
1088
- )}
1089
-
1090
- <TabsContent value="raw">
1091
- {activeTab === "raw" && (
1092
- <ExpandedPanel className="space-y-3">
1093
- <FullBodyHydrationNotice
1094
- status={bodyHydrationStatus}
1095
- error={bodyHydrationError}
1096
- footprint={fullBodyHydrationFootprint}
1097
- onRetry={retryFullBodyHydration}
1098
- />
1099
- {useChunkedBody && (
1100
- <BodyPreviewNotice
1101
- label="Response"
1102
- state={responsePreview}
1103
- expectedBytes={log.responseTextBytes ?? null}
1104
- onLoadMore={() => loadBodyChunk("response", responsePreview.offset)}
1105
- />
1106
- )}
1107
- {displayLog.error !== undefined && displayLog.error !== null && (
1108
- <NoticeBlock tone="danger" title="SSE Error">
1109
- <span className="font-mono">{displayLog.error}</span>
1110
- </NoticeBlock>
1111
- )}
1112
- {responseTextForView !== null && responsePreviewShouldParse ? (
1113
- <Suspense fallback={<TabFallback />}>
1114
- <LazyJsonViewerFromString
1115
- text={responseTextForView}
1116
- defaultExpandDepth={0}
1117
- bulkDepth={responseExpansion.bulkDepth}
1118
- bulkRevision={responseExpansion.bulkRevision}
1119
- />
1120
- </Suspense>
1121
- ) : responseTextForView !== null ? (
1122
- <RawTextBlock>{responseTextForView}</RawTextBlock>
1123
- ) : (
1124
- <EmptyState>
1125
- {useChunkedBody ? "Response preview is loading" : "No response"}
1126
- </EmptyState>
1127
- )}
1128
- {displayLog.streaming === true && (
1129
- <Suspense fallback={<TabFallback />}>
1130
- <LazyStreamingChunkSequence
1131
- logId={displayLog.id}
1132
- truncated={displayLog.streamingChunksPath !== null}
1133
- />
1134
- </Suspense>
1135
- )}
1136
- </ExpandedPanel>
1137
- )}
1138
- </TabsContent>
1139
-
1140
- <TabsContent value="parsed">
1141
- {activeTab === "parsed" && (
1142
- <ExpandedPanel>
1143
- <FullBodyHydrationNotice
1144
- status={bodyHydrationStatus}
1145
- error={bodyHydrationError}
1146
- footprint={fullBodyHydrationFootprint}
1147
- onRetry={retryFullBodyHydration}
1148
- />
1149
- {useChunkedBody && (
1150
- <BodyPreviewNotice
1151
- label="Response"
1152
- state={responsePreview}
1153
- expectedBytes={log.responseTextBytes ?? null}
1154
- onLoadMore={() => loadBodyChunk("response", responsePreview.offset)}
1155
- />
1156
- )}
1157
- {responsePreviewIsActive && !responsePreviewShouldParse ? (
1158
- <RawTextBlock>{responseTextForView}</RawTextBlock>
1159
- ) : (
1160
- <Suspense fallback={<TabFallback />}>
1161
- <LazyResponseView
1162
- responseText={responseTextForView}
1163
- responseStatus={displayLog.responseStatus}
1164
- streaming={displayLog.streaming}
1165
- inputTokens={displayLog.inputTokens}
1166
- outputTokens={displayLog.outputTokens}
1167
- cacheCreationInputTokens={displayLog.cacheCreationInputTokens}
1168
- cacheReadInputTokens={displayLog.cacheReadInputTokens}
1169
- apiFormat={resolvedFormat}
1170
- error={displayLog.error}
1171
- focusedToolIndex={focusedToolIndex}
1172
- toolFocusNonce={toolFocusNonce}
1173
- />
1174
- </Suspense>
1175
- )}
1176
- </ExpandedPanel>
1177
- )}
1178
- </TabsContent>
1179
- </Tabs>
1184
+ </Tabs>
1185
+ </LazyFeatureBoundary>
1180
1186
  </div>
1181
1187
  )}
1182
1188
  </div>
1183
- <Suspense fallback={null}>
1184
- <LazyReplayDialog
1185
- log={displayLog}
1186
- displayNumber={displayNumber}
1187
- open={replayOpen}
1188
- onOpenChange={setReplayOpen}
1189
- />
1190
- </Suspense>
1189
+ <LazyFeatureBoundary feature="Replay">
1190
+ <Suspense fallback={null}>
1191
+ <LazyReplayDialog
1192
+ log={displayLog}
1193
+ displayNumber={displayNumber}
1194
+ open={replayOpen}
1195
+ onOpenChange={setReplayOpen}
1196
+ />
1197
+ </Suspense>
1198
+ </LazyFeatureBoundary>
1191
1199
  </TooltipProvider>
1192
1200
  );
1193
1201
  });