@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,66 @@
1
+ import { StopCircle, Zap } from "lucide-react";
2
+ import { memo, type JSX } from "react";
3
+ import type { InspectorResponse } from "../../../../proxy/schemas";
4
+ import { formatTokens } from "../../../../lib/utils";
5
+ import { Badge } from "../../../ui/badge";
6
+ import { Separator } from "../../../ui/separator";
7
+ import { ResponseContentBlockRenderer } from "./ContentBlocks";
8
+
9
+ export const StructuredResponseViewAnthropic = memo(function StructuredResponseViewAnthropic({
10
+ response,
11
+ }: {
12
+ response: InspectorResponse;
13
+ }): JSX.Element {
14
+ return (
15
+ <div className="space-y-3">
16
+ <div className="flex items-center gap-2 flex-wrap">
17
+ <Badge variant="secondary" className="text-[10px] px-1.5 py-0 h-5 font-mono">
18
+ {response.model}
19
+ </Badge>
20
+
21
+ {response.stop_reason !== null && (
22
+ <Badge
23
+ variant="outline"
24
+ className="text-[10px] px-1.5 py-0 h-5 font-mono flex items-center gap-1"
25
+ >
26
+ <StopCircle className="size-2.5" />
27
+ {response.stop_reason}
28
+ </Badge>
29
+ )}
30
+
31
+ <span className="flex items-center gap-1 text-muted-foreground text-xs">
32
+ <Zap className="size-3" />
33
+ <span className="font-mono tabular-nums">
34
+ {formatTokens(response.usage.input_tokens ?? 0)} in /{" "}
35
+ {formatTokens(response.usage.output_tokens ?? 0)} out
36
+ </span>
37
+ {response.usage.cache_creation_input_tokens !== undefined &&
38
+ response.usage.cache_creation_input_tokens !== null &&
39
+ response.usage.cache_creation_input_tokens > 0 && (
40
+ <span className="font-mono tabular-nums text-emerald-400">
41
+ Cache +{formatTokens(response.usage.cache_creation_input_tokens)}
42
+ </span>
43
+ )}
44
+ {response.usage.cache_read_input_tokens !== undefined &&
45
+ response.usage.cache_read_input_tokens !== null &&
46
+ response.usage.cache_read_input_tokens > 0 && (
47
+ <span className="font-mono tabular-nums text-purple-400">
48
+ Cache ~{formatTokens(response.usage.cache_read_input_tokens)}
49
+ </span>
50
+ )}
51
+ </span>
52
+ </div>
53
+
54
+ <Separator className="opacity-50" />
55
+
56
+ <div className="space-y-2">
57
+ {response.content.map((block, i) => (
58
+ <ResponseContentBlockRenderer key={i} block={block} />
59
+ ))}
60
+ {response.content.length === 0 && (
61
+ <p className="text-xs text-muted-foreground italic">Empty response content</p>
62
+ )}
63
+ </div>
64
+ </div>
65
+ );
66
+ });
@@ -0,0 +1,21 @@
1
+ // Regex to extract content wrapped in <think>...</thinking> tags (MiniMax format: <think>...</think>)
2
+ const THINKING_TAG_REGEX = /<think>([\s\S]*?)<\/think>/gi;
3
+ const THINKING_TAG_REGEX_SINGLE = /<think>([\s\S]*?)<\/think>/i;
4
+
5
+ /**
6
+ * Extract thinking content wrapped in <think> tags (thought balloon emoji) from text.
7
+ * Returns { thinking, remainingText } where thinking is the extracted content
8
+ * and remainingText is the original text with thinking tags removed.
9
+ */
10
+ export function extractThinkingFromContent(text: string): {
11
+ thinking: string | null;
12
+ remainingText: string;
13
+ } {
14
+ const match = THINKING_TAG_REGEX_SINGLE.exec(text);
15
+ if (!match || match[1] === undefined) {
16
+ return { thinking: null, remainingText: text };
17
+ }
18
+ const thinking = match[1].trim();
19
+ const remainingText = text.replace(THINKING_TAG_REGEX, "").trim();
20
+ return { thinking, remainingText };
21
+ }
@@ -0,0 +1,33 @@
1
+ import type { JSX } from "react";
2
+ import type { InspectorResponse, OpenAIResponse, RequestFormat } from "../../../proxy/schemas";
3
+ import { StructuredResponseViewAnthropic } from "./anthropic/ResponseView";
4
+ import { OpenAIResponseView } from "./openai/ResponseView";
5
+
6
+ export { StructuredResponseViewAnthropic } from "./anthropic/ResponseView";
7
+ export { OpenAIResponseView } from "./openai/ResponseView";
8
+ export { ResponseContentBlockRenderer } from "./anthropic/ContentBlocks";
9
+
10
+ function isRecord(value: unknown): value is Record<string, unknown> {
11
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12
+ }
13
+
14
+ function isOpenAIResponse(response: unknown): response is OpenAIResponse {
15
+ return isRecord(response) && response.object === "chat.completion";
16
+ }
17
+
18
+ function isAnthropicResponse(response: unknown): response is InspectorResponse {
19
+ return isRecord(response) && response.type === "message" && Array.isArray(response.content);
20
+ }
21
+
22
+ export function formatViewFor(
23
+ apiFormat: RequestFormat | undefined,
24
+ response: unknown,
25
+ ): JSX.Element | null {
26
+ if (apiFormat === "openai" && isOpenAIResponse(response)) {
27
+ return <OpenAIResponseView response={response} />;
28
+ }
29
+ if (apiFormat === "anthropic" && isAnthropicResponse(response)) {
30
+ return <StructuredResponseViewAnthropic response={response} />;
31
+ }
32
+ return null;
33
+ }
@@ -0,0 +1,170 @@
1
+ import { StopCircle, Terminal, Zap } from "lucide-react";
2
+ import { memo, useState, type JSX } from "react";
3
+ import ReactMarkdown from "react-markdown";
4
+ import type { OpenAIResponse, OpenAIToolCall } from "../../../../proxy/schemas";
5
+ import { formatTokens } from "../../../../lib/utils";
6
+ import { Badge } from "../../../ui/badge";
7
+ import { JsonViewer } from "../../../ui/json-viewer";
8
+ import { safeJsonValue } from "../../../ui/json-viewer-bulk";
9
+ import { ScrollArea } from "../../../ui/scroll-area";
10
+ import { Separator } from "../../../ui/separator";
11
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../../ui/collapsible";
12
+ import { ChevronDown, ChevronRight } from "lucide-react";
13
+ import { ThinkingBlock } from "../anthropic/ContentBlocks";
14
+ import { extractThinkingFromContent } from "../anthropic/thinkingExtract";
15
+
16
+ // Re-export for use in other components
17
+ export { extractThinkingFromContent } from "../anthropic/thinkingExtract";
18
+
19
+ /** Best-effort JSON parse of an OpenAI `function.arguments` string. Returns
20
+ * the parsed object, or `null` on parse failure so the renderer can fall
21
+ * back to showing the raw string. */
22
+ function parseToolArguments(raw: string | undefined): unknown {
23
+ if (raw === undefined || raw === "") return {};
24
+ try {
25
+ return JSON.parse(raw);
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+
31
+ /** One collapsible tool_use row, mirroring the Anthropic ToolUseBlock visual
32
+ * treatment (Terminal icon, blue accent, name as a Badge, JSON input in a
33
+ * scrollable JsonViewer). */
34
+ function OpenAIToolCallBlock({ call }: { call: OpenAIToolCall }): JSX.Element {
35
+ const [open, setOpen] = useState(false);
36
+ const name = call.function.name ?? "(unnamed tool)";
37
+ const parsed = parseToolArguments(call.function.arguments);
38
+
39
+ return (
40
+ <Collapsible open={open} onOpenChange={setOpen}>
41
+ <div className="border-l-2 border-blue-500/40 my-1">
42
+ <CollapsibleTrigger className="flex items-center gap-1.5 px-3 py-1 w-full text-left cursor-pointer hover:bg-blue-500/5 transition-colors rounded-r-sm group">
43
+ <Terminal className="size-3.5 text-blue-400 shrink-0" />
44
+ <Badge variant="outline" className="text-[10px] font-mono px-1.5 py-0 h-4">
45
+ {name}
46
+ </Badge>
47
+ {call.id !== undefined && call.id !== "" && (
48
+ <span className="text-[10px] font-mono text-muted-foreground/60 truncate">
49
+ {call.id}
50
+ </span>
51
+ )}
52
+ <span className="flex-1" />
53
+ {open ? (
54
+ <ChevronDown className="size-3 text-muted-foreground" />
55
+ ) : (
56
+ <ChevronRight className="size-3 text-muted-foreground" />
57
+ )}
58
+ </CollapsibleTrigger>
59
+ <CollapsibleContent>
60
+ <div className="px-3 pb-2">
61
+ <ScrollArea className="max-h-[60vh]">
62
+ {parsed === null ? (
63
+ // JSON.parse failed — show the raw string so the user can
64
+ // still see what the model tried to call.
65
+ <pre className="font-mono text-xs whitespace-pre-wrap break-words text-rose-300/90">
66
+ {call.function.arguments}
67
+ </pre>
68
+ ) : (
69
+ <JsonViewer data={safeJsonValue(parsed)} defaultExpandDepth={0} />
70
+ )}
71
+ </ScrollArea>
72
+ </div>
73
+ </CollapsibleContent>
74
+ </div>
75
+ </Collapsible>
76
+ );
77
+ }
78
+
79
+ export const OpenAIResponseView = memo(function OpenAIResponseView({
80
+ response,
81
+ }: {
82
+ response: OpenAIResponse;
83
+ }): JSX.Element {
84
+ const choice = response.choices[0];
85
+ const message = choice?.message;
86
+ const toolCalls = message?.tool_calls ?? [];
87
+
88
+ return (
89
+ <div className="space-y-3">
90
+ <div className="flex items-center gap-2 flex-wrap">
91
+ <Badge variant="secondary" className="text-[10px] px-1.5 py-0 h-5 font-mono">
92
+ {response.model}
93
+ </Badge>
94
+
95
+ {choice?.finish_reason !== null && choice?.finish_reason !== undefined && (
96
+ <Badge
97
+ variant="outline"
98
+ className="text-[10px] px-1.5 py-0 h-5 font-mono flex items-center gap-1"
99
+ >
100
+ <StopCircle className="size-2.5" />
101
+ {choice.finish_reason}
102
+ </Badge>
103
+ )}
104
+
105
+ <span className="flex items-center gap-1 text-muted-foreground text-xs">
106
+ <Zap className="size-3" />
107
+ <span className="font-mono tabular-nums">
108
+ {formatTokens(response.usage.prompt_tokens ?? 0)} in /{" "}
109
+ {formatTokens(response.usage.completion_tokens ?? 0)} out
110
+ </span>
111
+ </span>
112
+ </div>
113
+
114
+ <Separator className="opacity-50" />
115
+
116
+ <div className="space-y-2">
117
+ {message?.reasoning_content !== null &&
118
+ message?.reasoning_content !== undefined &&
119
+ message.reasoning_content.length > 0 && (
120
+ <ThinkingBlock thinking={message.reasoning_content} />
121
+ )}
122
+ {message?.content !== null &&
123
+ message?.content !== undefined &&
124
+ message.content.length > 0 &&
125
+ (() => {
126
+ // Extract thinking content from tag-wrapped content if no reasoning_content field
127
+ const hasReasoningField =
128
+ message.reasoning_content !== null &&
129
+ message.reasoning_content !== undefined &&
130
+ message.reasoning_content.length > 0;
131
+ const { thinking, remainingText } = extractThinkingFromContent(message.content);
132
+ return (
133
+ <div className="space-y-2">
134
+ {/* Show thinking from tags only if no reasoning_content field */}
135
+ {thinking !== null && !hasReasoningField && <ThinkingBlock thinking={thinking} />}
136
+ {remainingText.length > 0 && (
137
+ <div className="prose prose-sm dark:prose-invert max-w-none [&_pre]:bg-muted [&_pre]:text-foreground [&_code]:text-[0.8em] [&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1">
138
+ <ReactMarkdown>{remainingText}</ReactMarkdown>
139
+ </div>
140
+ )}
141
+ </div>
142
+ );
143
+ })()}
144
+ {toolCalls.map((call, i) => (
145
+ // biome-ignore lint/suspicious/noArrayIndexKey: tool_calls is the positionally stable list from the response
146
+ <OpenAIToolCallBlock key={call.id ?? `tc-${i}`} call={call} />
147
+ ))}
148
+ {message?.function_call !== null && message?.function_call !== undefined && (
149
+ <div className="border border-blue-500/30 rounded-md p-3 bg-blue-500/5">
150
+ <div className="text-xs text-blue-400 font-mono mb-1">function_call</div>
151
+ <div className="font-mono text-xs">
152
+ <span className="text-blue-300">{message.function_call.name}</span>
153
+ <span className="text-muted-foreground">({message.function_call.arguments})</span>
154
+ </div>
155
+ </div>
156
+ )}
157
+ {(message?.content === null ||
158
+ message?.content === undefined ||
159
+ message.content.length === 0) &&
160
+ (message?.reasoning_content === null ||
161
+ message?.reasoning_content === undefined ||
162
+ message.reasoning_content.length === 0) &&
163
+ (message?.function_call === null || message?.function_call === undefined) &&
164
+ toolCalls.length === 0 && (
165
+ <p className="text-xs text-muted-foreground italic">Empty response content</p>
166
+ )}
167
+ </div>
168
+ </div>
169
+ );
170
+ });
@@ -0,0 +1,9 @@
1
+ export { ConversationGroup } from "./ConversationGroup";
2
+ export {
3
+ ConversationHeader,
4
+ getConversationId,
5
+ groupLogsByConversation,
6
+ } from "./ConversationHeader";
7
+ export type { ConversationGroupData, ViewMode } from "./ConversationHeader";
8
+ export { LogEntry } from "./LogEntry";
9
+ export { ThreadConnector } from "./ThreadConnector";
@@ -0,0 +1,37 @@
1
+ import { lazy } from "react";
2
+
3
+ /**
4
+ * Lazy-loaded heavy leaf components. Each is split into its own chunk by Vite
5
+ * (named after the source file) so the main bundle stays small. Imports use
6
+ * the `.then(m => ({ default: m.X }))` adapter so the original modules can
7
+ * keep their named exports without needing a default export.
8
+ */
9
+ export const LazyCompareDrawer = lazy(() =>
10
+ import("./CompareDrawer").then((m) => ({ default: m.CompareDrawer })),
11
+ );
12
+
13
+ export const LazyReplayDialog = lazy(() =>
14
+ import("./ReplayDialog").then((m) => ({ default: m.ReplayDialog })),
15
+ );
16
+
17
+ export const LazyRequestAnatomy = lazy(() =>
18
+ import("./anatomy/RequestAnatomy").then((m) => ({ default: m.RequestAnatomy })),
19
+ );
20
+
21
+ export const LazyResponseView = lazy(() =>
22
+ import("./ResponseView").then((m) => ({ default: m.ResponseView })),
23
+ );
24
+
25
+ export const LazyStreamingChunkSequence = lazy(() =>
26
+ import("./StreamingChunkSequence").then((m) => ({
27
+ default: m.StreamingChunkSequence,
28
+ })),
29
+ );
30
+
31
+ export const LazyJsonViewer = lazy(() =>
32
+ import("../ui/json-viewer").then((m) => ({ default: m.JsonViewer })),
33
+ );
34
+
35
+ export const LazyJsonViewerFromString = lazy(() =>
36
+ import("../ui/json-viewer").then((m) => ({ default: m.JsonViewerFromString })),
37
+ );
@@ -0,0 +1,194 @@
1
+ import {
2
+ AnthropicRequestSchema,
3
+ AnthropicResponseSchema,
4
+ } from "../../../proxy/formats/anthropic/schemas";
5
+ import type { AnatomySegment } from "../anatomy/types";
6
+ import { countCharacters, estimateTokens } from "../anatomy/tokenEstimate";
7
+ import type { LogFormatAdapter } from "./types";
8
+ import { emptyRequestAnalysis, EMPTY_RESPONSE_ANALYSIS } from "./types";
9
+
10
+ /**
11
+ * Flatten an Anthropic `content` field to the text used for token
12
+ * estimation. The function never throws: unknown content shapes are
13
+ * treated as empty. We deliberately do NOT use JSON.stringify on the
14
+ * content because base64 image data would dominate the size.
15
+ */
16
+ function contentToText(content: unknown): string {
17
+ if (typeof content === "string") return content;
18
+ if (!Array.isArray(content)) return "";
19
+ const parts: string[] = [];
20
+ for (const block of content) {
21
+ if (block === null || typeof block !== "object") continue;
22
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
23
+ const b = block as Record<string, unknown>;
24
+ const type = b.type;
25
+ switch (type) {
26
+ case "text":
27
+ if (typeof b.text === "string") parts.push(b.text);
28
+ break;
29
+ case "thinking":
30
+ case "think":
31
+ if (typeof b.thinking === "string") parts.push(b.thinking);
32
+ break;
33
+ case "tool_use": {
34
+ const name = typeof b.name === "string" ? b.name : "";
35
+ const input = b.input !== undefined ? JSON.stringify(b.input) : "";
36
+ parts.push(`${name} ${input}`.trim());
37
+ break;
38
+ }
39
+ case "tool_result": {
40
+ const inner = b.content;
41
+ if (typeof inner === "string") {
42
+ parts.push(inner);
43
+ } else if (Array.isArray(inner)) {
44
+ for (const item of inner) {
45
+ if (item !== null && typeof item === "object") {
46
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
47
+ const t = (item as Record<string, unknown>).text;
48
+ if (typeof t === "string") parts.push(t);
49
+ }
50
+ }
51
+ }
52
+ break;
53
+ }
54
+ case "image":
55
+ // Skip base64 payload but record the image type so the segment is not empty.
56
+ parts.push("[image]");
57
+ break;
58
+ }
59
+ }
60
+ return parts.join("\n");
61
+ }
62
+
63
+ /** Flatten an Anthropic `system` value to its text. The API accepts
64
+ * either a string or an array of text blocks. */
65
+ function systemToText(system: unknown): string {
66
+ if (typeof system === "string") return system;
67
+ if (!Array.isArray(system)) return "";
68
+ const parts: string[] = [];
69
+ for (const block of system) {
70
+ if (block !== null && typeof block === "object") {
71
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
72
+ const t = (block as { type?: unknown; text?: unknown }).text;
73
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
74
+ const type = (block as { type?: unknown }).type;
75
+ if (type === "text" && typeof t === "string") parts.push(t);
76
+ }
77
+ }
78
+ return parts.join("\n");
79
+ }
80
+
81
+ /** Flatten Anthropic `tools` array to its schema text. */
82
+ function toolsToText(tools: unknown): string {
83
+ if (!Array.isArray(tools)) return "";
84
+ const parts: string[] = [];
85
+ for (const tool of tools) {
86
+ if (tool === null || typeof tool !== "object") continue;
87
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
88
+ const t = tool as { name?: unknown; description?: unknown; input_schema?: unknown };
89
+ if (typeof t.name === "string") parts.push(t.name);
90
+ if (typeof t.description === "string") parts.push(t.description);
91
+ if (t.input_schema !== undefined) parts.push(JSON.stringify(t.input_schema));
92
+ }
93
+ return parts.join("\n");
94
+ }
95
+
96
+ /** Build one segment from a piece of text + a stable label and path. */
97
+ function segment(
98
+ role: AnatomySegment["role"],
99
+ label: string,
100
+ text: string,
101
+ path: string,
102
+ ): AnatomySegment {
103
+ return {
104
+ role,
105
+ label,
106
+ text,
107
+ size: estimateTokens(text),
108
+ characters: countCharacters(text),
109
+ path,
110
+ };
111
+ }
112
+
113
+ export const anthropicLogFormatAdapter: LogFormatAdapter = {
114
+ format: "anthropic",
115
+
116
+ analyzeRequest(rawBody) {
117
+ if (rawBody === null) return emptyRequestAnalysis(rawBody);
118
+ try {
119
+ const result = AnthropicRequestSchema.safeParse(JSON.parse(rawBody));
120
+ if (!result.success) return emptyRequestAnalysis(rawBody);
121
+ return {
122
+ parsed: result.data,
123
+ comparisonValue: result.data,
124
+ messageCount: result.data.messages.length,
125
+ toolCount:
126
+ result.data.tools !== undefined && result.data.tools.length > 0
127
+ ? result.data.tools.length
128
+ : null,
129
+ };
130
+ } catch {
131
+ return emptyRequestAnalysis(rawBody);
132
+ }
133
+ },
134
+
135
+ analyzeResponse(responseText) {
136
+ if (responseText === null) return EMPTY_RESPONSE_ANALYSIS;
137
+ try {
138
+ let json: unknown = JSON.parse(responseText);
139
+ if (typeof json === "string") json = JSON.parse(json);
140
+ const result = AnthropicResponseSchema.safeParse(json);
141
+ if (!result.success) return EMPTY_RESPONSE_ANALYSIS;
142
+ const toolNames = result.data.content
143
+ .filter((block) => block.type === "tool_use")
144
+ .map((block) => block.name);
145
+ return {
146
+ parsed: result.data,
147
+ toolNames: toolNames.length > 0 ? toolNames : null,
148
+ };
149
+ } catch {
150
+ return EMPTY_RESPONSE_ANALYSIS;
151
+ }
152
+ },
153
+
154
+ anatomySegments(parsed) {
155
+ if (parsed === null || typeof parsed !== "object") return null;
156
+ // We deliberately skip AnthropicRequestSchema validation here:
157
+ // real Anthropic requests accept `system` as either a string or
158
+ // an array, and we want the Anatomy view to render even when the
159
+ // body shape is slightly off-schema (e.g. system: "string" vs
160
+ // system: [{type:"text",text:"..."}]).
161
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
162
+ const body = parsed as {
163
+ system?: unknown;
164
+ messages?: unknown;
165
+ tools?: unknown;
166
+ };
167
+ const segments: AnatomySegment[] = [];
168
+
169
+ if (body.system !== undefined) {
170
+ const text = systemToText(body.system);
171
+ if (text.length > 0) {
172
+ segments.push(segment("system", "system", text, "/system"));
173
+ }
174
+ }
175
+
176
+ if (Array.isArray(body.messages)) {
177
+ body.messages.forEach((message, index) => {
178
+ if (message === null || typeof message !== "object") return;
179
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
180
+ const m = message as { role?: unknown; content?: unknown };
181
+ const role = m.role === "user" || m.role === "assistant" ? m.role : "user";
182
+ const text = contentToText(m.content);
183
+ segments.push(segment(role, `[${index}] ${role}`, text, `/messages/${index}`));
184
+ });
185
+ }
186
+
187
+ if (Array.isArray(body.tools) && body.tools.length > 0) {
188
+ const text = toolsToText(body.tools);
189
+ segments.push(segment("tools", "tools", text, "/tools"));
190
+ }
191
+
192
+ return segments.length > 0 ? segments : null;
193
+ },
194
+ };
@@ -0,0 +1,23 @@
1
+ import { requestFormatForPath } from "../../../proxy/formats";
2
+ import type { CapturedLog, RequestFormat } from "../../../proxy/schemas";
3
+ import { anthropicLogFormatAdapter } from "./anthropic";
4
+ import { openAILogFormatAdapter } from "./openai";
5
+ import type { LogFormatAdapter } from "./types";
6
+ import { unknownLogFormatAdapter } from "./unknown";
7
+
8
+ const ADAPTERS: Record<RequestFormat, LogFormatAdapter> = {
9
+ anthropic: anthropicLogFormatAdapter,
10
+ openai: openAILogFormatAdapter,
11
+ unknown: unknownLogFormatAdapter,
12
+ };
13
+
14
+ export function getLogFormatAdapter(format: RequestFormat): LogFormatAdapter {
15
+ return ADAPTERS[format];
16
+ }
17
+
18
+ export function resolveLogFormat(log: Pick<CapturedLog, "path" | "apiFormat">): RequestFormat {
19
+ const pathFormat = requestFormatForPath(log.path);
20
+ return pathFormat === "unknown" ? log.apiFormat : pathFormat;
21
+ }
22
+
23
+ export type { LogFormatAdapter, RequestAnalysis, ResponseAnalysis } from "./types";