@tonyclaw/agent-inspector 2.0.0

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 (400) hide show
  1. package/.output/cli.js +1611 -0
  2. package/.output/nitro.json +17 -0
  3. package/.output/public/assets/CompareDrawer-CU5ZrWcL.js +1 -0
  4. package/.output/public/assets/ProxyViewerContainer-pEBqVp1d.js +101 -0
  5. package/.output/public/assets/ReplayDialog-F58yNg5j.js +1 -0
  6. package/.output/public/assets/RequestAnatomy-C9lT0qE_.js +1 -0
  7. package/.output/public/assets/ResponseView-DHJq6bnz.js +1 -0
  8. package/.output/public/assets/StreamingChunkSequence-BTgfpFUT.js +1 -0
  9. package/.output/public/assets/_sessionId-DsNRbnNm.js +1 -0
  10. package/.output/public/assets/alibaba-TTwafVwX.svg +1 -0
  11. package/.output/public/assets/index-CpWG2hFn.css +1 -0
  12. package/.output/public/assets/index-DmBV8Gve.js +1 -0
  13. package/.output/public/assets/json-viewer-CZVYLR8j.js +14 -0
  14. package/.output/public/assets/main-DHs7FBK3.js +18 -0
  15. package/.output/public/assets/minimax-BPMzvuL-.jpeg +0 -0
  16. package/.output/public/assets/qwen-CONDcHqt.png +0 -0
  17. package/.output/public/assets/zhipuai-BPNAnxo-.svg +219 -0
  18. package/.output/server/_chunks/ssr-renderer.mjs +22 -0
  19. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +1 -0
  20. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +210 -0
  21. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +2 -0
  22. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +2 -0
  23. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +2 -0
  24. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +11 -0
  25. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +69 -0
  26. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +1 -0
  27. package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +17 -0
  28. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +1 -0
  29. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +6 -0
  30. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +34 -0
  31. package/.output/server/_libs/ajv-formats.mjs +330 -0
  32. package/.output/server/_libs/ajv.mjs +11444 -0
  33. package/.output/server/_libs/aria-hidden.mjs +122 -0
  34. package/.output/server/_libs/atomically.mjs +152 -0
  35. package/.output/server/_libs/bail.mjs +8 -0
  36. package/.output/server/_libs/cfworker__json-schema.mjs +1 -0
  37. package/.output/server/_libs/character-entities.mjs +2130 -0
  38. package/.output/server/_libs/class-variance-authority.mjs +44 -0
  39. package/.output/server/_libs/clsx.mjs +16 -0
  40. package/.output/server/_libs/comma-separated-tokens.mjs +10 -0
  41. package/.output/server/_libs/conf.mjs +635 -0
  42. package/.output/server/_libs/cookie-es.mjs +58 -0
  43. package/.output/server/_libs/core-util-is.mjs +75 -0
  44. package/.output/server/_libs/croner.mjs +1 -0
  45. package/.output/server/_libs/crossws.mjs +1 -0
  46. package/.output/server/_libs/debounce-fn.mjs +69 -0
  47. package/.output/server/_libs/decode-named-character-reference+[...].mjs +8 -0
  48. package/.output/server/_libs/dequal.mjs +27 -0
  49. package/.output/server/_libs/detect-node-es.mjs +1 -0
  50. package/.output/server/_libs/devlop.mjs +8 -0
  51. package/.output/server/_libs/diff.mjs +320 -0
  52. package/.output/server/_libs/dot-prop.mjs +265 -0
  53. package/.output/server/_libs/env-paths.mjs +57 -0
  54. package/.output/server/_libs/estree-util-is-identifier-name.mjs +11 -0
  55. package/.output/server/_libs/extend.mjs +97 -0
  56. package/.output/server/_libs/fast-deep-equal.mjs +38 -0
  57. package/.output/server/_libs/fast-uri.mjs +812 -0
  58. package/.output/server/_libs/floating-ui__core.mjs +725 -0
  59. package/.output/server/_libs/floating-ui__dom.mjs +622 -0
  60. package/.output/server/_libs/floating-ui__react-dom.mjs +292 -0
  61. package/.output/server/_libs/floating-ui__utils.mjs +320 -0
  62. package/.output/server/_libs/get-nonce.mjs +9 -0
  63. package/.output/server/_libs/h3-v2.mjs +276 -0
  64. package/.output/server/_libs/h3.mjs +408 -0
  65. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +388 -0
  66. package/.output/server/_libs/hast-util-whitespace.mjs +10 -0
  67. package/.output/server/_libs/hookable.mjs +1 -0
  68. package/.output/server/_libs/html-url-attributes.mjs +26 -0
  69. package/.output/server/_libs/immediate.mjs +74 -0
  70. package/.output/server/_libs/inherits.mjs +50 -0
  71. package/.output/server/_libs/inline-style-parser.mjs +142 -0
  72. package/.output/server/_libs/is-plain-obj.mjs +10 -0
  73. package/.output/server/_libs/isarray.mjs +14 -0
  74. package/.output/server/_libs/isbot.mjs +20 -0
  75. package/.output/server/_libs/json-schema-traverse.mjs +180 -0
  76. package/.output/server/_libs/jszip.mjs +3051 -0
  77. package/.output/server/_libs/lie.mjs +273 -0
  78. package/.output/server/_libs/lucide-react.mjs +492 -0
  79. package/.output/server/_libs/mdast-util-from-markdown.mjs +717 -0
  80. package/.output/server/_libs/mdast-util-to-hast.mjs +710 -0
  81. package/.output/server/_libs/mdast-util-to-string.mjs +38 -0
  82. package/.output/server/_libs/micromark-core-commonmark.mjs +2259 -0
  83. package/.output/server/_libs/micromark-factory-destination.mjs +94 -0
  84. package/.output/server/_libs/micromark-factory-label.mjs +63 -0
  85. package/.output/server/_libs/micromark-factory-space.mjs +24 -0
  86. package/.output/server/_libs/micromark-factory-title.mjs +65 -0
  87. package/.output/server/_libs/micromark-factory-whitespace.mjs +22 -0
  88. package/.output/server/_libs/micromark-util-character.mjs +44 -0
  89. package/.output/server/_libs/micromark-util-chunked.mjs +36 -0
  90. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +12 -0
  91. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +41 -0
  92. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +19 -0
  93. package/.output/server/_libs/micromark-util-decode-string.mjs +21 -0
  94. package/.output/server/_libs/micromark-util-encode.mjs +1 -0
  95. package/.output/server/_libs/micromark-util-html-tag-name.mjs +69 -0
  96. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +6 -0
  97. package/.output/server/_libs/micromark-util-resolve-all.mjs +15 -0
  98. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +41 -0
  99. package/.output/server/_libs/micromark-util-subtokenize.mjs +346 -0
  100. package/.output/server/_libs/micromark.mjs +906 -0
  101. package/.output/server/_libs/mimic-function.mjs +47 -0
  102. package/.output/server/_libs/modelcontextprotocol__server.mjs +9738 -0
  103. package/.output/server/_libs/ocache.mjs +1 -0
  104. package/.output/server/_libs/ohash.mjs +1 -0
  105. package/.output/server/_libs/pako.mjs +4223 -0
  106. package/.output/server/_libs/process-nextick-args.mjs +48 -0
  107. package/.output/server/_libs/property-information.mjs +1209 -0
  108. package/.output/server/_libs/radix-ui.mjs +1 -0
  109. package/.output/server/_libs/radix-ui__number.mjs +6 -0
  110. package/.output/server/_libs/radix-ui__primitive.mjs +11 -0
  111. package/.output/server/_libs/radix-ui__react-accordion.mjs +1 -0
  112. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +1 -0
  113. package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
  114. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +1 -0
  115. package/.output/server/_libs/radix-ui__react-avatar.mjs +1 -0
  116. package/.output/server/_libs/radix-ui__react-checkbox.mjs +1 -0
  117. package/.output/server/_libs/radix-ui__react-collapsible.mjs +144 -0
  118. package/.output/server/_libs/radix-ui__react-collection.mjs +69 -0
  119. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +39 -0
  120. package/.output/server/_libs/radix-ui__react-context-menu.mjs +1 -0
  121. package/.output/server/_libs/radix-ui__react-context.mjs +78 -0
  122. package/.output/server/_libs/radix-ui__react-dialog.mjs +325 -0
  123. package/.output/server/_libs/radix-ui__react-direction.mjs +9 -0
  124. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +1 -0
  125. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +29 -0
  126. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +206 -0
  127. package/.output/server/_libs/radix-ui__react-form.mjs +1 -0
  128. package/.output/server/_libs/radix-ui__react-hover-card.mjs +1 -0
  129. package/.output/server/_libs/radix-ui__react-id.mjs +14 -0
  130. package/.output/server/_libs/radix-ui__react-label.mjs +1 -0
  131. package/.output/server/_libs/radix-ui__react-menu.mjs +1 -0
  132. package/.output/server/_libs/radix-ui__react-menubar.mjs +1 -0
  133. package/.output/server/_libs/radix-ui__react-popover.mjs +1 -0
  134. package/.output/server/_libs/radix-ui__react-popper.mjs +286 -0
  135. package/.output/server/_libs/radix-ui__react-portal.mjs +16 -0
  136. package/.output/server/_libs/radix-ui__react-presence.mjs +128 -0
  137. package/.output/server/_libs/radix-ui__react-primitive.mjs +42 -0
  138. package/.output/server/_libs/radix-ui__react-progress.mjs +1 -0
  139. package/.output/server/_libs/radix-ui__react-radio-group.mjs +1 -0
  140. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +224 -0
  141. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +721 -0
  142. package/.output/server/_libs/radix-ui__react-select.mjs +1163 -0
  143. package/.output/server/_libs/radix-ui__react-separator.mjs +28 -0
  144. package/.output/server/_libs/radix-ui__react-slider.mjs +1 -0
  145. package/.output/server/_libs/radix-ui__react-slot.mjs +99 -0
  146. package/.output/server/_libs/radix-ui__react-switch.mjs +1 -0
  147. package/.output/server/_libs/radix-ui__react-tabs.mjs +189 -0
  148. package/.output/server/_libs/radix-ui__react-toast.mjs +2 -0
  149. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +1 -0
  150. package/.output/server/_libs/radix-ui__react-toggle.mjs +1 -0
  151. package/.output/server/_libs/radix-ui__react-toolbar.mjs +1 -0
  152. package/.output/server/_libs/radix-ui__react-tooltip.mjs +495 -0
  153. package/.output/server/_libs/radix-ui__react-use-previous.mjs +14 -0
  154. package/.output/server/_libs/radix-ui__react-use-size.mjs +39 -0
  155. package/.output/server/_libs/react-dom.mjs +10781 -0
  156. package/.output/server/_libs/react-markdown.mjs +147 -0
  157. package/.output/server/_libs/react-remove-scroll-bar.mjs +82 -0
  158. package/.output/server/_libs/react-remove-scroll.mjs +328 -0
  159. package/.output/server/_libs/react-style-singleton.mjs +69 -0
  160. package/.output/server/_libs/react.mjs +515 -0
  161. package/.output/server/_libs/readable-stream.mjs +1518 -0
  162. package/.output/server/_libs/remark-parse.mjs +19 -0
  163. package/.output/server/_libs/remark-rehype.mjs +21 -0
  164. package/.output/server/_libs/rou3.mjs +14 -0
  165. package/.output/server/_libs/safe-buffer.mjs +64 -0
  166. package/.output/server/_libs/semver.mjs +1938 -0
  167. package/.output/server/_libs/seroval-plugins.mjs +58 -0
  168. package/.output/server/_libs/seroval.mjs +1765 -0
  169. package/.output/server/_libs/setimmediate.mjs +152 -0
  170. package/.output/server/_libs/space-separated-tokens.mjs +6 -0
  171. package/.output/server/_libs/srvx.mjs +1029 -0
  172. package/.output/server/_libs/stubborn-fs.mjs +91 -0
  173. package/.output/server/_libs/stubborn-utils.mjs +66 -0
  174. package/.output/server/_libs/style-to-js.mjs +72 -0
  175. package/.output/server/_libs/style-to-object.mjs +38 -0
  176. package/.output/server/_libs/swr.mjs +939 -0
  177. package/.output/server/_libs/tailwind-merge.mjs +3010 -0
  178. package/.output/server/_libs/tanstack__history.mjs +217 -0
  179. package/.output/server/_libs/tanstack__react-router.mjs +1480 -0
  180. package/.output/server/_libs/tanstack__react-store.mjs +1 -0
  181. package/.output/server/_libs/tanstack__react-virtual.mjs +44 -0
  182. package/.output/server/_libs/tanstack__router-core.mjs +4827 -0
  183. package/.output/server/_libs/tanstack__store.mjs +1 -0
  184. package/.output/server/_libs/tanstack__virtual-core.mjs +1225 -0
  185. package/.output/server/_libs/tiny-invariant.mjs +12 -0
  186. package/.output/server/_libs/tiny-warning.mjs +5 -0
  187. package/.output/server/_libs/trim-lines.mjs +41 -0
  188. package/.output/server/_libs/trough.mjs +85 -0
  189. package/.output/server/_libs/tslib.mjs +1 -0
  190. package/.output/server/_libs/ufo.mjs +54 -0
  191. package/.output/server/_libs/uint8array-extras.mjs +69 -0
  192. package/.output/server/_libs/ungap__structured-clone.mjs +212 -0
  193. package/.output/server/_libs/unified.mjs +661 -0
  194. package/.output/server/_libs/unist-util-is.mjs +100 -0
  195. package/.output/server/_libs/unist-util-position.mjs +27 -0
  196. package/.output/server/_libs/unist-util-stringify-position.mjs +27 -0
  197. package/.output/server/_libs/unist-util-visit-parents.mjs +82 -0
  198. package/.output/server/_libs/unist-util-visit.mjs +24 -0
  199. package/.output/server/_libs/unstorage.mjs +1 -0
  200. package/.output/server/_libs/use-callback-ref.mjs +66 -0
  201. package/.output/server/_libs/use-sidecar.mjs +106 -0
  202. package/.output/server/_libs/use-sync-external-store.mjs +64 -0
  203. package/.output/server/_libs/util-deprecate.mjs +12 -0
  204. package/.output/server/_libs/vfile-message.mjs +138 -0
  205. package/.output/server/_libs/vfile.mjs +467 -0
  206. package/.output/server/_libs/when-exit.mjs +53 -0
  207. package/.output/server/_libs/zod.mjs +4524 -0
  208. package/.output/server/_sessionId-wMLPvC5g.mjs +123 -0
  209. package/.output/server/_ssr/CompareDrawer-BU4V0uVf.mjs +1041 -0
  210. package/.output/server/_ssr/ProxyViewerContainer-BnRwFEnn.mjs +5972 -0
  211. package/.output/server/_ssr/ReplayDialog-C7dn9pd_.mjs +322 -0
  212. package/.output/server/_ssr/RequestAnatomy-C1rWpe9-.mjs +353 -0
  213. package/.output/server/_ssr/ResponseView-hGpPaYsf.mjs +602 -0
  214. package/.output/server/_ssr/StreamingChunkSequence-BRWI1r_G.mjs +302 -0
  215. package/.output/server/_ssr/index-BKURLVPz.mjs +118 -0
  216. package/.output/server/_ssr/index.mjs +1184 -0
  217. package/.output/server/_ssr/json-viewer-BBd2DtQP.mjs +515 -0
  218. package/.output/server/_ssr/router-BcZ0D6AB.mjs +6317 -0
  219. package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  220. package/.output/server/_tanstack-start-manifest_v-1y8ZVxRI.mjs +4 -0
  221. package/.output/server/index.mjs +436 -0
  222. package/.output/server/node_modules/tslib/modules/index.js +70 -0
  223. package/.output/server/node_modules/tslib/modules/package.json +3 -0
  224. package/.output/server/node_modules/tslib/package.json +47 -0
  225. package/.output/server/node_modules/tslib/tslib.js +484 -0
  226. package/.output/server/package.json +9 -0
  227. package/LICENSE +21 -0
  228. package/README.md +52 -0
  229. package/package.json +110 -0
  230. package/src/assets/favicon.svg +31 -0
  231. package/src/assets/logos/alibaba.svg +1 -0
  232. package/src/assets/logos/anthropic.svg +1 -0
  233. package/src/assets/logos/claude-code.svg +4 -0
  234. package/src/assets/logos/deepseek.svg +1 -0
  235. package/src/assets/logos/mcp.png +0 -0
  236. package/src/assets/logos/minimax.jpeg +0 -0
  237. package/src/assets/logos/openai.svg +1 -0
  238. package/src/assets/logos/opencode.svg +4 -0
  239. package/src/assets/logos/qwen.png +0 -0
  240. package/src/assets/logos/zhipuai.svg +219 -0
  241. package/src/cli/detect-tools.ts +147 -0
  242. package/src/cli/doctor.ts +521 -0
  243. package/src/cli/onboard.ts +224 -0
  244. package/src/cli/templates/command-onboard.ts +17 -0
  245. package/src/cli/templates/skill-onboard.ts +547 -0
  246. package/src/cli.ts +345 -0
  247. package/src/components/OnboardingBanner.tsx +67 -0
  248. package/src/components/ProxyViewer.tsx +545 -0
  249. package/src/components/ProxyViewerContainer.tsx +363 -0
  250. package/src/components/providers/ImportWizardDialog.tsx +349 -0
  251. package/src/components/providers/ProviderCard.tsx +474 -0
  252. package/src/components/providers/ProviderForm.tsx +494 -0
  253. package/src/components/providers/ProviderLogo.tsx +117 -0
  254. package/src/components/providers/ProvidersPanel.tsx +619 -0
  255. package/src/components/providers/SettingsDialog.tsx +202 -0
  256. package/src/components/proxy-viewer/CompareDrawer.tsx +893 -0
  257. package/src/components/proxy-viewer/ConversationGroup.tsx +107 -0
  258. package/src/components/proxy-viewer/ConversationHeader.tsx +300 -0
  259. package/src/components/proxy-viewer/LogEntry.tsx +543 -0
  260. package/src/components/proxy-viewer/LogEntryHeader.tsx +501 -0
  261. package/src/components/proxy-viewer/ReplayDialog.tsx +218 -0
  262. package/src/components/proxy-viewer/ResponseView.tsx +171 -0
  263. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +188 -0
  264. package/src/components/proxy-viewer/ThreadConnector.tsx +136 -0
  265. package/src/components/proxy-viewer/TurnGroup.tsx +337 -0
  266. package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +98 -0
  267. package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +196 -0
  268. package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +53 -0
  269. package/src/components/proxy-viewer/anatomy/types.ts +39 -0
  270. package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +114 -0
  271. package/src/components/proxy-viewer/cacheTrend.ts +50 -0
  272. package/src/components/proxy-viewer/diff/DiffView.tsx +321 -0
  273. package/src/components/proxy-viewer/diff/computeDiff.ts +178 -0
  274. package/src/components/proxy-viewer/diff/index.ts +3 -0
  275. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +157 -0
  276. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +66 -0
  277. package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +21 -0
  278. package/src/components/proxy-viewer/formats/index.tsx +33 -0
  279. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +170 -0
  280. package/src/components/proxy-viewer/index.ts +9 -0
  281. package/src/components/proxy-viewer/lazy.ts +37 -0
  282. package/src/components/proxy-viewer/log-formats/anthropic.ts +194 -0
  283. package/src/components/proxy-viewer/log-formats/index.ts +23 -0
  284. package/src/components/proxy-viewer/log-formats/openai.ts +167 -0
  285. package/src/components/proxy-viewer/log-formats/types.ts +40 -0
  286. package/src/components/proxy-viewer/log-formats/unknown.ts +18 -0
  287. package/src/components/proxy-viewer/logEntryVisibility.ts +39 -0
  288. package/src/components/proxy-viewer/requestDiff.ts +277 -0
  289. package/src/components/proxy-viewer/useCopyFeedback.ts +36 -0
  290. package/src/components/proxy-viewer/useKeyboardNavigation.ts +190 -0
  291. package/src/components/proxy-viewer/viewerState.ts +66 -0
  292. package/src/components/ui/badge.tsx +47 -0
  293. package/src/components/ui/button.tsx +47 -0
  294. package/src/components/ui/collapsible.tsx +21 -0
  295. package/src/components/ui/confirm-dialog.tsx +51 -0
  296. package/src/components/ui/crab-logo.tsx +95 -0
  297. package/src/components/ui/crab-variants.tsx +467 -0
  298. package/src/components/ui/dialog.tsx +129 -0
  299. package/src/components/ui/json-expansion-button.tsx +56 -0
  300. package/src/components/ui/json-viewer-bulk.ts +97 -0
  301. package/src/components/ui/json-viewer.tsx +494 -0
  302. package/src/components/ui/mcp-logo.tsx +20 -0
  303. package/src/components/ui/scroll-area.tsx +54 -0
  304. package/src/components/ui/select.tsx +178 -0
  305. package/src/components/ui/separator.tsx +28 -0
  306. package/src/components/ui/tabs.tsx +88 -0
  307. package/src/components/ui/tooltip.tsx +51 -0
  308. package/src/index.css +11 -0
  309. package/src/knowledge/candidateStore.ts +63 -0
  310. package/src/knowledge/distiller.ts +98 -0
  311. package/src/knowledge/openclawClient.ts +118 -0
  312. package/src/knowledge/redactor.ts +80 -0
  313. package/src/knowledge/types.ts +84 -0
  314. package/src/lib/apiClient.ts +49 -0
  315. package/src/lib/export-logs.ts +51 -0
  316. package/src/lib/mask.ts +4 -0
  317. package/src/lib/objectUtils.ts +22 -0
  318. package/src/lib/providerContract.ts +26 -0
  319. package/src/lib/providerTestContract.ts +107 -0
  320. package/src/lib/runtimeConfig.ts +25 -0
  321. package/src/lib/serverPort.ts +41 -0
  322. package/src/lib/sessionUrl.ts +44 -0
  323. package/src/lib/stopReason.ts +58 -0
  324. package/src/lib/useOnboarding.ts +80 -0
  325. package/src/lib/useProviders.ts +30 -0
  326. package/src/lib/useStripConfig.ts +108 -0
  327. package/src/lib/utils.ts +21 -0
  328. package/src/mcp/loopback.ts +76 -0
  329. package/src/mcp/previewExtractor.ts +166 -0
  330. package/src/mcp/server.ts +396 -0
  331. package/src/mcp/toolHandlers.ts +341 -0
  332. package/src/proxy/chunkStorage.ts +112 -0
  333. package/src/proxy/claudeCodeStrip.ts +99 -0
  334. package/src/proxy/config.ts +172 -0
  335. package/src/proxy/constants.ts +47 -0
  336. package/src/proxy/dataDir.ts +86 -0
  337. package/src/proxy/formats/anthropic/anthropicProvider.ts +75 -0
  338. package/src/proxy/formats/anthropic/handler.ts +71 -0
  339. package/src/proxy/formats/anthropic/index.ts +14 -0
  340. package/src/proxy/formats/anthropic/register.ts +4 -0
  341. package/src/proxy/formats/anthropic/schemas.ts +237 -0
  342. package/src/proxy/formats/anthropic/stream.ts +205 -0
  343. package/src/proxy/formats/handler.ts +46 -0
  344. package/src/proxy/formats/index.ts +12 -0
  345. package/src/proxy/formats/jsonSchema.ts +36 -0
  346. package/src/proxy/formats/openai/alibabaProvider.ts +38 -0
  347. package/src/proxy/formats/openai/handler.ts +96 -0
  348. package/src/proxy/formats/openai/index.ts +25 -0
  349. package/src/proxy/formats/openai/provider.ts +50 -0
  350. package/src/proxy/formats/openai/register.ts +4 -0
  351. package/src/proxy/formats/openai/schemas.ts +187 -0
  352. package/src/proxy/formats/openai/stream.ts +206 -0
  353. package/src/proxy/formats/protocol.ts +50 -0
  354. package/src/proxy/formats/providerRegistry.ts +51 -0
  355. package/src/proxy/formats/providers/index.ts +3 -0
  356. package/src/proxy/formats/registry.ts +66 -0
  357. package/src/proxy/handler.ts +334 -0
  358. package/src/proxy/logFinalizer.ts +305 -0
  359. package/src/proxy/logFinalizer.worker.ts +24 -0
  360. package/src/proxy/logIndex.ts +268 -0
  361. package/src/proxy/logger.ts +179 -0
  362. package/src/proxy/openaiOrphanToolStrip.ts +142 -0
  363. package/src/proxy/providerImporters.ts +491 -0
  364. package/src/proxy/providers.ts +613 -0
  365. package/src/proxy/schemas.ts +209 -0
  366. package/src/proxy/sessionProcess.ts +140 -0
  367. package/src/proxy/sessionRuntime.ts +85 -0
  368. package/src/proxy/sessionSupervisor.ts +283 -0
  369. package/src/proxy/sessionWorkerEntry.ts +26 -0
  370. package/src/proxy/socketTracker.ts +255 -0
  371. package/src/proxy/store.ts +412 -0
  372. package/src/proxy/upstream.ts +90 -0
  373. package/src/router.tsx +16 -0
  374. package/src/routes/__root.tsx +45 -0
  375. package/src/routes/api/config.paths.ts +14 -0
  376. package/src/routes/api/config.ts +53 -0
  377. package/src/routes/api/health.ts +15 -0
  378. package/src/routes/api/knowledge.candidates.$candidateId.promote.ts +32 -0
  379. package/src/routes/api/knowledge.candidates.ts +10 -0
  380. package/src/routes/api/knowledge.project-context.ts +18 -0
  381. package/src/routes/api/knowledge.search.ts +31 -0
  382. package/src/routes/api/knowledge.sessions.$sessionId.candidates.ts +16 -0
  383. package/src/routes/api/logs.$id.chunks.ts +36 -0
  384. package/src/routes/api/logs.$id.replay.ts +191 -0
  385. package/src/routes/api/logs.$id.ts +22 -0
  386. package/src/routes/api/logs.stream.ts +74 -0
  387. package/src/routes/api/logs.ts +59 -0
  388. package/src/routes/api/mcp.ts +25 -0
  389. package/src/routes/api/models.ts +10 -0
  390. package/src/routes/api/providers.$providerId.test.log.ts +293 -0
  391. package/src/routes/api/providers.$providerId.ts +50 -0
  392. package/src/routes/api/providers.export.ts +26 -0
  393. package/src/routes/api/providers.import.ts +47 -0
  394. package/src/routes/api/providers.scan.ts +23 -0
  395. package/src/routes/api/providers.ts +45 -0
  396. package/src/routes/api/sessions.ts +17 -0
  397. package/src/routes/index.tsx +6 -0
  398. package/src/routes/proxy/$.ts +15 -0
  399. package/src/routes/session/$sessionId.tsx +23 -0
  400. package/styles/globals.css +188 -0
@@ -0,0 +1,545 @@
1
+ import { type JSX, useCallback, useEffect, useMemo, useRef, useState, Suspense } from "react";
2
+ import { ArrowLeft, Check, Copy, Download, Plus } from "lucide-react";
3
+
4
+ import type { CapturedLog } from "../proxy/schemas";
5
+ import { exportLogsAsZip } from "../lib/export-logs";
6
+ import { formatTokens } from "../lib/utils";
7
+ import packageJson from "../../package.json";
8
+ import { ConversationGroup, groupLogsByConversation } from "./proxy-viewer";
9
+
10
+ import { CrabLogo } from "./ui/crab-logo";
11
+ import { crabVariants } from "./ui/crab-variants";
12
+ import { McpLogo } from "./ui/mcp-logo";
13
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
14
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
15
+ import { SettingsDialog } from "./providers/SettingsDialog";
16
+ import { computeCacheTrends } from "./proxy-viewer/cacheTrend";
17
+ import { LazyCompareDrawer } from "./proxy-viewer/lazy";
18
+ import { buildValidPredecessors } from "./proxy-viewer/viewerState";
19
+ import { useKeyboardNavigation } from "./proxy-viewer/useKeyboardNavigation";
20
+
21
+ function truncateSessionId(id: string): string {
22
+ if (id.length <= 30) return id;
23
+ return id.slice(0, 12) + "…" + id.slice(-12);
24
+ }
25
+
26
+ function computeTokenSummary(logs: CapturedLog[]): { totalIn: number; totalOut: number } {
27
+ let totalIn = 0;
28
+ let totalOut = 0;
29
+ for (const log of logs) {
30
+ if (log.inputTokens !== null) totalIn += log.inputTokens;
31
+ if (log.outputTokens !== null) totalOut += log.outputTokens;
32
+ }
33
+ return { totalIn, totalOut };
34
+ }
35
+
36
+ function formatTimeRange(logs: CapturedLog[]): string | null {
37
+ const first = logs[0];
38
+ const last = logs[logs.length - 1];
39
+ if (first === undefined || last === undefined) return null;
40
+ const format = (iso: string): string =>
41
+ new Date(iso).toLocaleTimeString([], {
42
+ hour: "2-digit",
43
+ minute: "2-digit",
44
+ second: "2-digit",
45
+ });
46
+ return `${format(first.timestamp)} - ${format(last.timestamp)}`;
47
+ }
48
+
49
+ function getFirstUserAgent(logs: CapturedLog[]): string | null {
50
+ for (const log of logs) {
51
+ if (log.userAgent !== null && log.userAgent !== undefined && log.userAgent !== "") {
52
+ return log.userAgent;
53
+ }
54
+ }
55
+ return null;
56
+ }
57
+
58
+ function CopyableCommand({ command }: { command: string }): JSX.Element {
59
+ const [copied, setCopied] = useState(false);
60
+
61
+ const handleCopy = useCallback(() => {
62
+ void window.navigator.clipboard.writeText(command).then(() => {
63
+ setCopied(true);
64
+ setTimeout(() => setCopied(false), 2000);
65
+ });
66
+ }, [command]);
67
+
68
+ return (
69
+ <div className="inline-flex items-center gap-2 bg-muted rounded-md px-3 py-2">
70
+ <pre className="text-blue-500 font-mono text-sm m-0">{command}</pre>
71
+ <button
72
+ type="button"
73
+ onClick={handleCopy}
74
+ className="text-muted-foreground hover:text-foreground transition-colors shrink-0 cursor-pointer"
75
+ aria-label="Copy command"
76
+ >
77
+ {copied ? (
78
+ <svg
79
+ width="16"
80
+ height="16"
81
+ viewBox="0 0 24 24"
82
+ fill="none"
83
+ stroke="currentColor"
84
+ strokeWidth="2"
85
+ strokeLinecap="round"
86
+ strokeLinejoin="round"
87
+ >
88
+ <path d="M20 6 9 17l-5-5" />
89
+ </svg>
90
+ ) : (
91
+ <svg
92
+ width="16"
93
+ height="16"
94
+ viewBox="0 0 24 24"
95
+ fill="none"
96
+ stroke="currentColor"
97
+ strokeWidth="2"
98
+ strokeLinecap="round"
99
+ strokeLinejoin="round"
100
+ >
101
+ <rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
102
+ <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
103
+ </svg>
104
+ )}
105
+ </button>
106
+ </div>
107
+ );
108
+ }
109
+
110
+ function McpReadyBadge(): JSX.Element {
111
+ return (
112
+ <TooltipProvider>
113
+ <Tooltip>
114
+ <TooltipTrigger asChild>
115
+ <span className="inline-flex h-7 items-center gap-2 rounded-md border border-cyan-400/30 bg-cyan-500/10 px-2.5 font-mono text-[11px] font-medium text-cyan-300 shadow-[0_0_16px_rgba(34,211,238,0.08)]">
116
+ <span className="size-1.5 rounded-full bg-emerald-300 shadow-[0_0_8px_rgba(110,231,183,0.8)]" />
117
+ MCP Ready
118
+ <span className="hidden text-cyan-200/70 sm:inline">/api/mcp</span>
119
+ </span>
120
+ </TooltipTrigger>
121
+ <TooltipContent sideOffset={8} className="max-w-[320px] text-left leading-relaxed">
122
+ Coding agents can inspect logs, replay requests, test providers, and debug sessions
123
+ through MCP at /api/mcp.
124
+ </TooltipContent>
125
+ </Tooltip>
126
+ </TooltipProvider>
127
+ );
128
+ }
129
+
130
+ function SessionContextBar({
131
+ sessionId,
132
+ logs,
133
+ totalIn,
134
+ totalOut,
135
+ }: {
136
+ sessionId: string;
137
+ logs: CapturedLog[];
138
+ totalIn: number;
139
+ totalOut: number;
140
+ }): JSX.Element {
141
+ const [copied, setCopied] = useState(false);
142
+ const timeRange = useMemo(() => formatTimeRange(logs), [logs]);
143
+ const userAgent = useMemo(() => getFirstUserAgent(logs), [logs]);
144
+
145
+ const handleCopyLink = useCallback(() => {
146
+ void window.navigator.clipboard.writeText(window.location.href).then(() => {
147
+ setCopied(true);
148
+ setTimeout(() => setCopied(false), 2000);
149
+ });
150
+ }, []);
151
+
152
+ return (
153
+ <div className="mb-4 flex items-center gap-3 border border-border rounded-md bg-muted/20 px-3 py-2 text-xs">
154
+ <a
155
+ href="/"
156
+ className="inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
157
+ aria-label="Back to all sessions"
158
+ title="Back to all sessions"
159
+ >
160
+ <ArrowLeft className="size-3.5" />
161
+ </a>
162
+ <div className="min-w-0 flex-1">
163
+ <div className="flex min-w-0 items-center gap-2">
164
+ <span className="font-mono font-semibold text-purple-400/90 truncate" title={sessionId}>
165
+ {truncateSessionId(sessionId)}
166
+ </span>
167
+ {userAgent !== null && (
168
+ <span
169
+ className="font-mono text-muted-foreground truncate max-w-[220px]"
170
+ title={userAgent}
171
+ >
172
+ {userAgent}
173
+ </span>
174
+ )}
175
+ </div>
176
+ <div className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-muted-foreground">
177
+ <span>
178
+ {logs.length} request{logs.length !== 1 ? "s" : ""}
179
+ </span>
180
+ {timeRange !== null && <span>{timeRange}</span>}
181
+ {(totalIn > 0 || totalOut > 0) && (
182
+ <span className="font-mono">
183
+ {formatTokens(totalIn)} in / {formatTokens(totalOut)} out
184
+ </span>
185
+ )}
186
+ </div>
187
+ </div>
188
+ <button
189
+ type="button"
190
+ onClick={handleCopyLink}
191
+ className="inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
192
+ aria-label={copied ? "Copied session link" : "Copy session link"}
193
+ title={copied ? "Copied session link" : "Copy session link"}
194
+ >
195
+ {copied ? <Check className="size-3.5" /> : <Copy className="size-3.5" />}
196
+ </button>
197
+ </div>
198
+ );
199
+ }
200
+
201
+ export type ProxyViewerProps = {
202
+ logs: CapturedLog[];
203
+ sessions: string[];
204
+ models: string[];
205
+ selectedSession: string;
206
+ selectedModel: string;
207
+ onSessionChange: (session: string) => void;
208
+ onModelChange: (model: string) => void;
209
+ onClearAll: () => void;
210
+ /** Clear only the logs whose ids are passed. Called by the per-group
211
+ * Clear button on each conversation header. */
212
+ onClearGroup: (ids: number[]) => void;
213
+ viewMode: "simple" | "full";
214
+ onViewModeChange: (mode: "simple" | "full") => void;
215
+ /** Live strip-Claude-Code-billing-header flag, sourced once at the container. */
216
+ strip: boolean;
217
+ /** Slow-response threshold in seconds. `0` disables the warning indicator. */
218
+ slowResponseThresholdSeconds: number;
219
+ /** Hide the session filter dropdown. Used on `/session/$id` routes where
220
+ * the session is already pinned by the URL and the dropdown would just
221
+ * fight the URL state. */
222
+ hideSessionFilter?: boolean;
223
+ /** Session id pinned by a `/session/$id` route. Enables session-page chrome. */
224
+ pinnedSessionId?: string;
225
+ };
226
+
227
+ export function ProxyViewer({
228
+ logs,
229
+ sessions,
230
+ models,
231
+ selectedSession,
232
+ selectedModel,
233
+ onSessionChange,
234
+ onModelChange,
235
+ onClearAll,
236
+ onClearGroup,
237
+ viewMode,
238
+ onViewModeChange,
239
+ strip,
240
+ slowResponseThresholdSeconds,
241
+ hideSessionFilter = false,
242
+ pinnedSessionId,
243
+ }: ProxyViewerProps): JSX.Element {
244
+ const { totalIn, totalOut } = useMemo(() => computeTokenSummary(logs), [logs]);
245
+ const [exporting, setExporting] = useState(false);
246
+ const [comparePair, setComparePair] = useState<[CapturedLog, CapturedLog] | null>(null);
247
+ const [crabEntrancePhase, setCrabEntrancePhase] = useState<"hidden" | "playing" | "done">(
248
+ "hidden",
249
+ );
250
+ const logListRef = useRef<HTMLDivElement>(null);
251
+ const logListWrapperRef = useRef<HTMLDivElement>(null);
252
+ useKeyboardNavigation(logListRef, logListWrapperRef);
253
+
254
+ useEffect(() => {
255
+ const perCrabDuration = 400;
256
+ const startDelay = 50;
257
+ const playingDone = startDelay + crabVariants.length * perCrabDuration;
258
+ const t1 = setTimeout(() => setCrabEntrancePhase("playing"), startDelay);
259
+ const t2 = setTimeout(() => setCrabEntrancePhase("done"), playingDone);
260
+ return () => {
261
+ clearTimeout(t1);
262
+ clearTimeout(t2);
263
+ };
264
+ }, []);
265
+
266
+ useEffect(() => {
267
+ if (pinnedSessionId === undefined) {
268
+ document.title = "Agent Inspector";
269
+ return;
270
+ }
271
+ const requestLabel = logs.length === 1 ? "1 req" : `${logs.length} req`;
272
+ document.title = `${truncateSessionId(pinnedSessionId)} - ${requestLabel} - Agent Inspector`;
273
+ }, [logs.length, pinnedSessionId]);
274
+
275
+ const handleExport = useCallback(async () => {
276
+ setExporting(true);
277
+ try {
278
+ await exportLogsAsZip(logs);
279
+ } finally {
280
+ setExporting(false);
281
+ }
282
+ }, [logs]);
283
+ // Close the compare drawer when the user changes the session or model
284
+ // filter, since the predecessor relationship may no longer be meaningful.
285
+ useEffect(() => {
286
+ setComparePair(null);
287
+ }, [selectedSession, selectedModel]);
288
+
289
+ const closeCompare = useCallback(() => {
290
+ setComparePair(null);
291
+ }, []);
292
+
293
+ const groups = useMemo(() => groupLogsByConversation(logs), [logs]);
294
+ const cacheTrends = useMemo(() => computeCacheTrends(groups), [groups]);
295
+ const comparisonPredecessors = useMemo(() => buildValidPredecessors(groups), [groups]);
296
+ const handleCompareWithPrevious = useCallback(
297
+ (log: CapturedLog) => {
298
+ const predecessor = comparisonPredecessors.get(log.id);
299
+ if (predecessor !== undefined) setComparePair([predecessor, log]);
300
+ },
301
+ [comparisonPredecessors],
302
+ );
303
+
304
+ return (
305
+ <div className="max-w-[1400px] xl:max-w-[1600px] 2xl:max-w-[1800px] mx-auto px-6 pb-6">
306
+ {/* Sticky chrome — brand row, optional pinned-session context, and the
307
+ filter/control bar stay visible while the user scrolls through the
308
+ log list. `z-30` sits above the per-conversation sticky header
309
+ (`z-10`) but below modal-level overlays (dialogs, dropdowns,
310
+ tooltips — `z-50`). Background is fully opaque so logs don't bleed
311
+ through. */}
312
+ <div className="sticky top-0 z-30 bg-background pt-6">
313
+ {/* Brand row */}
314
+ <div className="grid grid-cols-[1fr_auto_1fr] items-start gap-3 pb-8">
315
+ <div />
316
+ <h1 className="flex min-w-0 flex-col items-center gap-2 text-center">
317
+ <span className="flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap">
318
+ {/* Crab family — hover to animate together */}
319
+ <span
320
+ className="flex shrink-0 items-end gap-1 group cursor-default"
321
+ aria-hidden="true"
322
+ >
323
+ <CrabLogo className="size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" />
324
+ <span className="hidden items-end gap-0.5 sm:flex">
325
+ {crabVariants.map((Crab, i) => {
326
+ const color = [
327
+ "text-amber-500",
328
+ "text-rose-500",
329
+ "text-sky-500",
330
+ "text-emerald-500",
331
+ "text-violet-500",
332
+ "text-orange-500",
333
+ "text-cyan-500",
334
+ "text-pink-500",
335
+ "text-lime-500",
336
+ "text-blue-500",
337
+ "text-yellow-500",
338
+ "text-fuchsia-500",
339
+ ][i];
340
+ const entranceClass =
341
+ crabEntrancePhase === "hidden"
342
+ ? "opacity-0 scale-0"
343
+ : crabEntrancePhase === "playing"
344
+ ? "animate-crab-piano-pop"
345
+ : "";
346
+ return (
347
+ <Crab
348
+ key={i}
349
+ className={`size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`}
350
+ style={{
351
+ transitionDelay: `${i * 50}ms`,
352
+ ...(crabEntrancePhase === "playing"
353
+ ? { animationDelay: `${i * 400}ms` }
354
+ : {}),
355
+ }}
356
+ />
357
+ );
358
+ })}
359
+ </span>
360
+ </span>
361
+ <span className="flex min-w-0 items-baseline gap-2 pl-1">
362
+ <span className="truncate text-lg font-bold">Agent Inspector</span>
363
+ <span className="shrink-0 font-mono text-xs font-semibold text-muted-foreground">
364
+ v{packageJson.version}
365
+ </span>
366
+ </span>
367
+ <Plus className="size-4 shrink-0 text-muted-foreground/70" aria-hidden="true" />
368
+ <McpLogo className="size-10 shrink-0" />
369
+ </span>
370
+ <McpReadyBadge />
371
+ </h1>
372
+ <div className="justify-self-end">
373
+ <SettingsDialog />
374
+ </div>
375
+ </div>
376
+
377
+ {pinnedSessionId !== undefined && (
378
+ <SessionContextBar
379
+ sessionId={pinnedSessionId}
380
+ logs={logs}
381
+ totalIn={totalIn}
382
+ totalOut={totalOut}
383
+ />
384
+ )}
385
+
386
+ {/* Controls + Filters */}
387
+ <div className="flex items-center gap-3 mb-4">
388
+ {!hideSessionFilter && (
389
+ <Select value={selectedSession} onValueChange={onSessionChange}>
390
+ <SelectTrigger className="flex-1 max-w-[350px] text-xs">
391
+ <SelectValue placeholder="All sessions" />
392
+ </SelectTrigger>
393
+ <SelectContent>
394
+ <SelectItem value="__all__">All sessions</SelectItem>
395
+ {sessions.map((s) => (
396
+ <SelectItem key={s} value={s}>
397
+ {truncateSessionId(s)}
398
+ </SelectItem>
399
+ ))}
400
+ </SelectContent>
401
+ </Select>
402
+ )}
403
+ <Select value={selectedModel} onValueChange={onModelChange}>
404
+ <SelectTrigger className="flex-1 max-w-[250px] text-xs">
405
+ <SelectValue placeholder="All models" />
406
+ </SelectTrigger>
407
+ <SelectContent>
408
+ <SelectItem value="__all__">All models</SelectItem>
409
+ {models.map((m) => (
410
+ <SelectItem key={m} value={m}>
411
+ {m}
412
+ </SelectItem>
413
+ ))}
414
+ </SelectContent>
415
+ </Select>
416
+ <div className="flex items-center border border-border rounded-md overflow-hidden">
417
+ <button
418
+ type="button"
419
+ onClick={() => onViewModeChange("simple")}
420
+ className={`h-8 px-3 cursor-pointer transition-colors text-xs ${
421
+ viewMode === "simple"
422
+ ? "bg-muted text-foreground"
423
+ : "text-muted-foreground hover:bg-muted/50"
424
+ }`}
425
+ >
426
+ Simple
427
+ </button>
428
+ <button
429
+ type="button"
430
+ onClick={() => onViewModeChange("full")}
431
+ className={`h-8 px-3 cursor-pointer transition-colors text-xs ${
432
+ viewMode === "full"
433
+ ? "bg-muted text-foreground"
434
+ : "text-muted-foreground hover:bg-muted/50"
435
+ }`}
436
+ >
437
+ Full
438
+ </button>
439
+ </div>
440
+ <div className="flex-1" />
441
+ <span className="text-muted-foreground text-xs font-mono">
442
+ {logs.length} request{logs.length !== 1 ? "s" : ""}
443
+ {totalIn > 0 || totalOut > 0
444
+ ? ` · ${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out`
445
+ : ""}
446
+ </span>
447
+ {logs.length > 0 && (
448
+ <button
449
+ type="button"
450
+ onClick={() => {
451
+ void handleExport();
452
+ }}
453
+ disabled={exporting}
454
+ className="h-8 px-3 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"
455
+ title="Export all logs as JSON ZIP"
456
+ >
457
+ {exporting ? (
458
+ <span>Exporting...</span>
459
+ ) : (
460
+ <>
461
+ <Download className="size-3.5" />
462
+ <span>Export</span>
463
+ </>
464
+ )}
465
+ </button>
466
+ )}
467
+ <button
468
+ type="button"
469
+ onClick={onClearAll}
470
+ className="h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer rounded-md hover:bg-muted"
471
+ title="Clear all logs"
472
+ >
473
+ Clear
474
+ </button>
475
+ </div>
476
+ </div>
477
+
478
+ {/* Log list */}
479
+ <div>
480
+ {logs.length === 0 ? (
481
+ selectedSession !== "__all__" ? (
482
+ <div className="text-center text-muted-foreground py-16 space-y-4">
483
+ <p className="text-sm font-medium">Session not found</p>
484
+ <p className="text-xs font-mono bg-muted px-3 py-1 rounded inline-block max-w-[500px] break-all">
485
+ {truncateSessionId(selectedSession)}
486
+ </p>
487
+ <p className="text-xs">
488
+ This session may have been cleared or never existed.{" "}
489
+ {hideSessionFilter ? (
490
+ <a href="/" className="underline hover:text-foreground transition-colors">
491
+ Back to all sessions
492
+ </a>
493
+ ) : (
494
+ <button
495
+ type="button"
496
+ onClick={() => onSessionChange("__all__")}
497
+ className="underline hover:text-foreground transition-colors"
498
+ >
499
+ Show all sessions
500
+ </button>
501
+ )}
502
+ </p>
503
+ </div>
504
+ ) : (
505
+ <div className="text-center text-muted-foreground py-16 space-y-4">
506
+ <p className="text-sm">No requests captured yet.</p>
507
+ <p className="text-xs">Route AI coding tools through the proxy:</p>
508
+ <CopyableCommand command="LLM_BASE_URL=http://localhost:25947/proxy <your-tool>" />
509
+ </div>
510
+ )
511
+ ) : (
512
+ <div
513
+ ref={logListWrapperRef}
514
+ tabIndex={0}
515
+ className="flex flex-col gap-2 focus:outline-none"
516
+ >
517
+ <div ref={logListRef}>
518
+ {groups.map((group) => (
519
+ <ConversationGroup
520
+ key={group.id}
521
+ group={group}
522
+ viewMode={viewMode}
523
+ strip={strip}
524
+ slowResponseThresholdSeconds={slowResponseThresholdSeconds}
525
+ cacheTrends={cacheTrends}
526
+ onCompareWithPrevious={handleCompareWithPrevious}
527
+ comparisonPredecessors={comparisonPredecessors}
528
+ onClearGroup={onClearGroup}
529
+ standalone={groups.length === 1}
530
+ />
531
+ ))}
532
+ </div>
533
+ </div>
534
+ )}
535
+ </div>
536
+
537
+ {/* Compare drawer — sibling of the log list, not a route change. */}
538
+ {comparePair !== null && (
539
+ <Suspense fallback={null}>
540
+ <LazyCompareDrawer left={comparePair[0]} right={comparePair[1]} onClose={closeCompare} />
541
+ </Suspense>
542
+ )}
543
+ </div>
544
+ );
545
+ }