@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,80 @@
1
+ import type { KnowledgeCandidate, RedactionMetadata } from "./types";
2
+
3
+ type RedactionPattern = {
4
+ name: string;
5
+ pattern: RegExp;
6
+ };
7
+
8
+ const REDACTION_PATTERNS: RedactionPattern[] = [
9
+ { name: "authorization-header", pattern: /\bAuthorization\s*:\s*[^\s,;]+/gi },
10
+ { name: "bearer-token", pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi },
11
+ { name: "api-key", pattern: /\b(?:sk|ak)-[A-Za-z0-9_-]{12,}\b/gi },
12
+ { name: "cookie", pattern: /\bCookie\s*:\s*[^;\n]+(?:;[^\n]+)?/gi },
13
+ {
14
+ name: "secret-assignment",
15
+ pattern: /\b(?:api[_-]?key|token|secret|password)\s*=\s*["']?[^"'\s,;]{8,}["']?/gi,
16
+ },
17
+ ];
18
+
19
+ type RedactedText = {
20
+ text: string;
21
+ patterns: string[];
22
+ };
23
+
24
+ function unique(values: string[]): string[] {
25
+ return [...new Set(values)];
26
+ }
27
+
28
+ export function redactText(input: string): RedactedText {
29
+ let text = input;
30
+ const patterns: string[] = [];
31
+ for (const item of REDACTION_PATTERNS) {
32
+ let matched = false;
33
+ text = text.replace(item.pattern, () => {
34
+ matched = true;
35
+ return `[REDACTED:${item.name}]`;
36
+ });
37
+ if (matched) {
38
+ patterns.push(item.name);
39
+ }
40
+ }
41
+ return { text, patterns: unique(patterns) };
42
+ }
43
+
44
+ function combineRedaction(patterns: string[]): RedactionMetadata {
45
+ const uniquePatterns = unique(patterns);
46
+ return {
47
+ redacted: uniquePatterns.length > 0,
48
+ patterns: uniquePatterns,
49
+ };
50
+ }
51
+
52
+ export function redactCandidate(candidate: KnowledgeCandidate): KnowledgeCandidate {
53
+ const title = redactText(candidate.title);
54
+ const content = redactText(candidate.content);
55
+ const tagResults = candidate.tags.map(redactText);
56
+ const evidenceProject =
57
+ candidate.evidence.project === null ? null : redactText(candidate.evidence.project);
58
+ const evidenceModels = candidate.evidence.models.map(redactText);
59
+ const patterns = [
60
+ ...title.patterns,
61
+ ...content.patterns,
62
+ ...tagResults.flatMap((result) => result.patterns),
63
+ ...(evidenceProject === null ? [] : evidenceProject.patterns),
64
+ ...evidenceModels.flatMap((result) => result.patterns),
65
+ ...candidate.redaction.patterns,
66
+ ];
67
+
68
+ return {
69
+ ...candidate,
70
+ title: title.text,
71
+ content: content.text,
72
+ tags: tagResults.map((result) => result.text),
73
+ evidence: {
74
+ ...candidate.evidence,
75
+ project: evidenceProject === null ? null : evidenceProject.text,
76
+ models: evidenceModels.map((result) => result.text),
77
+ },
78
+ redaction: combineRedaction(patterns),
79
+ };
80
+ }
@@ -0,0 +1,84 @@
1
+ import { z } from "zod";
2
+
3
+ export const KnowledgeCandidateTypeSchema = z.enum([
4
+ "episode",
5
+ "procedure",
6
+ "preference",
7
+ "project-fact",
8
+ ]);
9
+
10
+ export type KnowledgeCandidateType = z.infer<typeof KnowledgeCandidateTypeSchema>;
11
+
12
+ export const KnowledgeCandidateStatusSchema = z.enum(["draft", "promoted", "failed"]);
13
+
14
+ export type KnowledgeCandidateStatus = z.infer<typeof KnowledgeCandidateStatusSchema>;
15
+
16
+ export const KnowledgeEvidenceSchema = z.object({
17
+ source: z.literal("agent-inspector"),
18
+ sessionId: z.string(),
19
+ logIds: z.array(z.number().int().positive()),
20
+ project: z.string().nullable(),
21
+ models: z.array(z.string()),
22
+ });
23
+
24
+ export type KnowledgeEvidence = z.infer<typeof KnowledgeEvidenceSchema>;
25
+
26
+ export const RedactionMetadataSchema = z.object({
27
+ redacted: z.boolean(),
28
+ patterns: z.array(z.string()),
29
+ });
30
+
31
+ export type RedactionMetadata = z.infer<typeof RedactionMetadataSchema>;
32
+
33
+ export const KnowledgeCandidateSchema = z.object({
34
+ id: z.string(),
35
+ type: KnowledgeCandidateTypeSchema,
36
+ title: z.string(),
37
+ content: z.string(),
38
+ tags: z.array(z.string()),
39
+ source: z.literal("agent-inspector"),
40
+ sessionId: z.string(),
41
+ logIds: z.array(z.number().int().positive()),
42
+ evidence: KnowledgeEvidenceSchema,
43
+ status: KnowledgeCandidateStatusSchema,
44
+ createdAt: z.string(),
45
+ updatedAt: z.string(),
46
+ openClawMemoryId: z.string().nullable(),
47
+ error: z.string().nullable(),
48
+ redaction: RedactionMetadataSchema,
49
+ });
50
+
51
+ export type KnowledgeCandidate = z.infer<typeof KnowledgeCandidateSchema>;
52
+
53
+ export const OpenClawMemoryPayloadSchema = z.object({
54
+ idempotencyKey: z.string(),
55
+ type: KnowledgeCandidateTypeSchema,
56
+ title: z.string(),
57
+ content: z.string(),
58
+ tags: z.array(z.string()),
59
+ source: z.literal("agent-inspector"),
60
+ project: z.string().nullable(),
61
+ evidence: KnowledgeEvidenceSchema,
62
+ });
63
+
64
+ export type OpenClawMemoryPayload = z.infer<typeof OpenClawMemoryPayloadSchema>;
65
+
66
+ export const KnowledgeSearchResultSchema = z.object({
67
+ id: z.string(),
68
+ type: KnowledgeCandidateTypeSchema.or(z.string()),
69
+ title: z.string(),
70
+ content: z.string(),
71
+ score: z.number().nullable(),
72
+ source: z.string().nullable(),
73
+ project: z.string().nullable(),
74
+ evidence: z.unknown().optional(),
75
+ });
76
+
77
+ export type KnowledgeSearchResult = z.infer<typeof KnowledgeSearchResultSchema>;
78
+
79
+ export const KnowledgeSearchResponseSchema = z.object({
80
+ results: z.array(KnowledgeSearchResultSchema),
81
+ warning: z.string().nullable(),
82
+ });
83
+
84
+ export type KnowledgeSearchResponse = z.infer<typeof KnowledgeSearchResponseSchema>;
@@ -0,0 +1,49 @@
1
+ /* eslint-disable functional/no-throw-statements */
2
+ import { z } from "zod";
3
+
4
+ const ApiErrorSchema = z.object({
5
+ error: z.string(),
6
+ });
7
+
8
+ /**
9
+ * Parse and validate a JSON response at the browser/server trust boundary.
10
+ *
11
+ * Keeping this in one place makes response validation consistent without
12
+ * coupling callers to a specific API or domain type.
13
+ */
14
+ export async function parseJsonResponse<T>(response: Response, schema: z.ZodType<T>): Promise<T> {
15
+ const data: unknown = await response.json();
16
+ return schema.parse(data);
17
+ }
18
+
19
+ /**
20
+ * Read the conventional `{ error: string }` API response when available.
21
+ * Malformed or non-JSON error bodies deliberately fall back to caller-owned
22
+ * text so each screen retains its existing user-facing message.
23
+ */
24
+ export async function readApiError(response: Response, fallback: string): Promise<string> {
25
+ try {
26
+ const data: unknown = await response.json();
27
+ const result = ApiErrorSchema.safeParse(data);
28
+ return result.success ? result.data.error : fallback;
29
+ } catch {
30
+ return fallback;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Fetch a JSON resource and validate its successful response.
36
+ */
37
+ export async function fetchJson<T>(
38
+ input: RequestInfo | URL,
39
+ schema: z.ZodType<T>,
40
+ init?: RequestInit,
41
+ errorFallback?: (response: Response) => string,
42
+ ): Promise<T> {
43
+ const response = await fetch(input, init);
44
+ if (!response.ok) {
45
+ const fallback = errorFallback?.(response) ?? `Request failed with status ${response.status}`;
46
+ throw new Error(await readApiError(response, fallback));
47
+ }
48
+ return parseJsonResponse(response, schema);
49
+ }
@@ -0,0 +1,51 @@
1
+ import JSZip from "jszip";
2
+ import type { CapturedLog } from "../proxy/schemas";
3
+
4
+ async function fetchStreamingChunks(logId: number): Promise<string | null> {
5
+ try {
6
+ const response = await fetch(`/api/logs/${logId}/chunks`);
7
+ if (!response.ok) return null;
8
+ const data: unknown = await response.json();
9
+ return JSON.stringify(data, null, 2);
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+
15
+ export async function exportLogsAsZip(logs: CapturedLog[]): Promise<void> {
16
+ const zip = new JSZip();
17
+
18
+ // Add regular request/response files
19
+ for (const log of logs) {
20
+ if (log.rawRequestBody !== null) {
21
+ zip.file(`#${log.id}.Request.json`, log.rawRequestBody);
22
+ }
23
+ if (log.responseText !== null) {
24
+ zip.file(`#${log.id}.Response.json`, log.responseText);
25
+ }
26
+ }
27
+
28
+ // Fetch SSE chunks in parallel for streaming logs
29
+ const streamingLogs = logs.filter((log) => log.streaming);
30
+ await Promise.allSettled(
31
+ streamingLogs.map(async (log) => {
32
+ const chunks = await fetchStreamingChunks(log.id);
33
+ if (chunks !== null) {
34
+ zip.file(`#${log.id}.SSE.Response.json`, chunks);
35
+ }
36
+ }),
37
+ );
38
+
39
+ // Generate and download ZIP
40
+ const blob = await zip.generateAsync({ type: "blob" });
41
+ const url = URL.createObjectURL(blob);
42
+
43
+ const anchor = document.createElement("a");
44
+ anchor.href = url;
45
+ anchor.download = `agent-inspector-export-${new Date().toISOString().slice(0, 10)}.zip`;
46
+ document.body.appendChild(anchor);
47
+ anchor.click();
48
+ document.body.removeChild(anchor);
49
+
50
+ URL.revokeObjectURL(url);
51
+ }
@@ -0,0 +1,4 @@
1
+ export function maskApiKey(apiKey: string): string {
2
+ if (apiKey.length <= 8) return "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
3
+ return apiKey.slice(0, 4) + "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" + apiKey.slice(-4);
4
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Generic object utilities shared across protocol modules.
3
+ * Only truly protocol-agnostic helpers belong here — Anthropic/OpenAI-specific
4
+ * checks stay in their respective format modules.
5
+ */
6
+
7
+ /**
8
+ * Returns true if `val` is a plain object (not null, not an array).
9
+ */
10
+ export function isPlainRecord(val: unknown): val is Record<string, unknown> {
11
+ return typeof val === "object" && val !== null && !Array.isArray(val);
12
+ }
13
+
14
+ /**
15
+ * Safe property access via Object.getOwnPropertyDescriptor.
16
+ * Returns `undefined` when obj is not an object or the property does not exist.
17
+ */
18
+ export function safeGetOwnProperty(obj: unknown, key: string): unknown {
19
+ if (obj === null || typeof obj !== "object" || Array.isArray(obj)) return undefined;
20
+ const desc = Object.getOwnPropertyDescriptor(obj, key);
21
+ return desc?.value;
22
+ }
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Persisted provider shape shared by the server store and browser API clients.
5
+ *
6
+ * Keep this module free of filesystem and process-level imports so client code
7
+ * can validate API responses without pulling the provider store into the bundle.
8
+ */
9
+ export const ProviderConfigSchema = z.object({
10
+ id: z.string(),
11
+ name: z.string(),
12
+ apiKey: z.string(),
13
+ model: z.string().optional(),
14
+ models: z.array(z.string()).min(1),
15
+ format: z.enum(["anthropic", "openai"]).optional(),
16
+ baseUrl: z.string().optional(),
17
+ anthropicBaseUrl: z.string().optional(),
18
+ openaiBaseUrl: z.string().optional(),
19
+ authHeader: z.enum(["bearer", "x-api-key"]).optional().default("bearer"),
20
+ apiDocsUrl: z.string().optional(),
21
+ source: z.enum(["company", "personal"]).optional(),
22
+ createdAt: z.string(),
23
+ updatedAt: z.string(),
24
+ });
25
+
26
+ export type ProviderConfig = z.infer<typeof ProviderConfigSchema>;
@@ -0,0 +1,107 @@
1
+ import { z } from "zod";
2
+ import { StreamingChunkSchema } from "../proxy/schemas";
3
+
4
+ export const ProviderTestErrorTypeSchema = z.enum([
5
+ "timeout",
6
+ "network_unreachable",
7
+ "connection_refused",
8
+ "auth_failed",
9
+ "rate_limited",
10
+ "server_error",
11
+ "invalid_response",
12
+ "unknown",
13
+ ]);
14
+
15
+ const ProviderTestErrorSchema = z.object({
16
+ message: z.string(),
17
+ type: ProviderTestErrorTypeSchema,
18
+ details: z.string().optional(),
19
+ hint: z.string().optional(),
20
+ });
21
+
22
+ const ProviderTestContentSchema = z.object({
23
+ type: z.enum(["text", "thinking"]),
24
+ text: z.string().optional(),
25
+ thinking: z.string().optional(),
26
+ });
27
+
28
+ export const ProviderTestResultSchema = z.object({
29
+ success: z.boolean(),
30
+ error: ProviderTestErrorSchema.optional(),
31
+ model: z.string().optional(),
32
+ inputTokens: z.number().optional(),
33
+ outputTokens: z.number().optional(),
34
+ cacheCreationInputTokens: z.number().optional(),
35
+ cacheReadInputTokens: z.number().optional(),
36
+ latencyMs: z.number().optional(),
37
+ content: z.array(ProviderTestContentSchema).optional(),
38
+ rawResponse: z.string().optional(),
39
+ streaming: z.boolean().optional(),
40
+ streamingChunks: z
41
+ .object({
42
+ chunks: z.array(StreamingChunkSchema),
43
+ truncated: z.boolean().default(false),
44
+ })
45
+ .optional(),
46
+ requestHeaders: z.record(z.string(), z.string()).optional(),
47
+ });
48
+
49
+ export const ProviderTestStateSchema = z.union([
50
+ ProviderTestResultSchema,
51
+ z.object({ notConfigured: z.literal(true) }),
52
+ z.object({ testing: z.literal(true) }),
53
+ ]);
54
+
55
+ const ProviderFormatTestResultsSchema = z.object({
56
+ nonStreaming: ProviderTestStateSchema,
57
+ streaming: ProviderTestStateSchema,
58
+ });
59
+
60
+ const ModelTestResultsSchema = z.object({
61
+ anthropic: ProviderFormatTestResultsSchema,
62
+ openai: ProviderFormatTestResultsSchema,
63
+ });
64
+
65
+ export const ProviderTestResultsSchema = z.object({
66
+ anthropic: ProviderFormatTestResultsSchema,
67
+ openai: ProviderFormatTestResultsSchema,
68
+ models: z.record(z.string(), ModelTestResultsSchema).optional(),
69
+ testedAt: z.string().optional(),
70
+ });
71
+
72
+ export type ProviderTestErrorType = z.infer<typeof ProviderTestErrorTypeSchema>;
73
+ export type ProviderTestResult = z.infer<typeof ProviderTestResultSchema>;
74
+ export type ProviderTestState = z.infer<typeof ProviderTestStateSchema>;
75
+ export type ProviderTestResults = z.infer<typeof ProviderTestResultsSchema>;
76
+ export type ModelTestResults = z.infer<typeof ModelTestResultsSchema>;
77
+
78
+ export function createPendingProviderTestResults(): ProviderTestResults {
79
+ return {
80
+ anthropic: {
81
+ nonStreaming: { testing: true },
82
+ streaming: { testing: true },
83
+ },
84
+ openai: {
85
+ nonStreaming: { testing: true },
86
+ streaming: { testing: true },
87
+ },
88
+ };
89
+ }
90
+
91
+ export function createFailedProviderTestResults(
92
+ message: string,
93
+ type: ProviderTestErrorType,
94
+ ): ProviderTestResults {
95
+ const createFormatResult = () => ({
96
+ nonStreaming: {
97
+ success: false as const,
98
+ error: { message, type },
99
+ },
100
+ streaming: { notConfigured: true as const },
101
+ });
102
+
103
+ return {
104
+ anthropic: createFormatResult(),
105
+ openai: createFormatResult(),
106
+ };
107
+ }
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+
3
+ export const DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS = 10;
4
+ export const MAX_SLOW_RESPONSE_THRESHOLD_SECONDS = 600;
5
+
6
+ /**
7
+ * Schema for the runtime proxy config. Shared between server
8
+ * (src/proxy/config.ts) and client (src/lib/useStripConfig.ts) so that
9
+ * the wire format is validated end-to-end with a single source of truth.
10
+ *
11
+ * Keep this file free of side effects and Node-only imports so it is
12
+ * safe to bundle into the client.
13
+ */
14
+ export const RuntimeConfigSchema = z.object({
15
+ stripClaudeCodeBillingHeader: z.boolean(),
16
+ hasSeenOnboarding: z.boolean().default(false),
17
+ slowResponseThresholdSeconds: z
18
+ .number()
19
+ .int()
20
+ .min(0)
21
+ .max(MAX_SLOW_RESPONSE_THRESHOLD_SECONDS)
22
+ .default(DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS),
23
+ });
24
+
25
+ export type RuntimeConfig = z.infer<typeof RuntimeConfigSchema>;
@@ -0,0 +1,41 @@
1
+ /* eslint-disable functional/no-throw-statements */
2
+ /**
3
+ * Tracks the HTTP port the inspector server is bound to, so internal
4
+ * components (currently the MCP server's loopback fetch) can build
5
+ * `http://127.0.0.1:<port>/...` URLs without env-var coupling.
6
+ *
7
+ * Initialization order:
8
+ * 1. `setCurrentPort(port)` — explicit override (used by tests, or any
9
+ * code that has direct knowledge of the bound port).
10
+ * 2. `process.env.PORT` — fallback. The CLI (`src/cli.ts`) sets this
11
+ * before spawning the server process, so in normal operation this
12
+ * branch is taken and no explicit `setCurrentPort` call is needed.
13
+ *
14
+ * `getCurrentPort()` throws if neither source is available.
15
+ */
16
+
17
+ let overridePort: number | null = null;
18
+
19
+ export function setCurrentPort(port: number): void {
20
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) {
21
+ throw new Error(`setCurrentPort: invalid port ${port}`);
22
+ }
23
+ overridePort = port;
24
+ }
25
+
26
+ export function getCurrentPort(): number {
27
+ if (overridePort !== null) return overridePort;
28
+ const envPort = process.env["PORT"];
29
+ if (envPort !== undefined && envPort !== "") {
30
+ const n = Number(envPort);
31
+ if (Number.isInteger(n) && n > 0 && n <= 65535) return n;
32
+ }
33
+ throw new Error(
34
+ "Inspector server port not initialized: PORT env var is unset and setCurrentPort() has not been called",
35
+ );
36
+ }
37
+
38
+ /** Reset for tests. */
39
+ export function _resetForTests(): void {
40
+ overridePort = null;
41
+ }
@@ -0,0 +1,44 @@
1
+ const B64URL_RE = /^[A-Za-z0-9_-]+$/;
2
+
3
+ function bytesToBinary(bytes: Uint8Array): string {
4
+ let binary = "";
5
+ for (const byte of bytes) {
6
+ binary += String.fromCharCode(byte);
7
+ }
8
+ return binary;
9
+ }
10
+
11
+ function binaryToBytes(binary: string): Uint8Array {
12
+ const bytes = new Uint8Array(binary.length);
13
+ for (let i = 0; i < binary.length; i++) {
14
+ bytes[i] = binary.charCodeAt(i);
15
+ }
16
+ return bytes;
17
+ }
18
+
19
+ export function encodeSessionIdForPath(sessionId: string): string {
20
+ const bytes = new TextEncoder().encode(sessionId);
21
+ const base64 = btoa(bytesToBinary(bytes));
22
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
23
+ }
24
+
25
+ export function decodeSessionIdFromPath(encoded: string): string {
26
+ if (encoded.startsWith("%")) {
27
+ return decodeURIComponent(encoded);
28
+ }
29
+ if (!B64URL_RE.test(encoded)) {
30
+ return encoded;
31
+ }
32
+ try {
33
+ const padded = encoded.padEnd(Math.ceil(encoded.length / 4) * 4, "=");
34
+ const base64 = padded.replace(/-/g, "+").replace(/_/g, "/");
35
+ const binary = atob(base64);
36
+ return new TextDecoder().decode(binaryToBytes(binary));
37
+ } catch {
38
+ return encoded;
39
+ }
40
+ }
41
+
42
+ export function getSessionPath(sessionId: string): string {
43
+ return `/session/${encodeSessionIdForPath(sessionId)}`;
44
+ }
@@ -0,0 +1,58 @@
1
+ import type { CapturedLog } from "../proxy/schemas";
2
+
3
+ export type StopReason = "end_turn" | "tool_use" | "stop" | null;
4
+
5
+ function isRecord(value: unknown): value is Record<string, unknown> {
6
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7
+ }
8
+
9
+ /**
10
+ * Extracts the stop/finish reason from a captured log's response text.
11
+ * Detects the response body shape (Anthropic or OpenAI) independently of
12
+ * `log.apiFormat`, since the body shape may not match the declared format
13
+ * (e.g. an Anthropic-compatible endpoint returning Anthropic-shaped JSON
14
+ * while the request was classified as OpenAI).
15
+ */
16
+ export function extractStopReason(log: CapturedLog): StopReason {
17
+ if (log.responseText === null) return null;
18
+
19
+ try {
20
+ let json: unknown = JSON.parse(log.responseText);
21
+ // Handle double-encoded JSON
22
+ if (typeof json === "string") {
23
+ json = JSON.parse(json);
24
+ }
25
+ if (!isRecord(json)) return null;
26
+
27
+ // Anthropic shape: { stop_reason: "end_turn" | "tool_use" | ... }
28
+ if (typeof json.stop_reason === "string") {
29
+ if (json.stop_reason === "end_turn" || json.stop_reason === "tool_use") {
30
+ return json.stop_reason;
31
+ }
32
+ return null;
33
+ }
34
+
35
+ // OpenAI shape: { choices: [{ finish_reason: "stop" | ... }] }
36
+ if (
37
+ Array.isArray(json.choices) &&
38
+ json.choices.length > 0 &&
39
+ isRecord(json.choices[0]) &&
40
+ typeof json.choices[0].finish_reason === "string" &&
41
+ json.choices[0].finish_reason === "stop"
42
+ ) {
43
+ return "stop";
44
+ }
45
+
46
+ return null;
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Returns true when the stop reason indicates the assistant completed its
54
+ * turn naturally (Anthropic end_turn or OpenAI stop).
55
+ */
56
+ export function isTurnBoundary(stopReason: StopReason): boolean {
57
+ return stopReason === "end_turn" || stopReason === "stop";
58
+ }
@@ -0,0 +1,80 @@
1
+ import useSWR, { type SWRResponse, useSWRConfig } from "swr";
2
+ import { fetchJson } from "./apiClient";
3
+ import {
4
+ DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
5
+ RuntimeConfigSchema,
6
+ type RuntimeConfig,
7
+ } from "./runtimeConfig";
8
+
9
+ export const ONBOARDING_SWR_KEY = "/api/config";
10
+
11
+ async function fetcher(url: string): Promise<RuntimeConfig> {
12
+ return fetchJson(
13
+ url,
14
+ RuntimeConfigSchema,
15
+ undefined,
16
+ (response) => `Failed to fetch ${url}: ${response.status}`,
17
+ );
18
+ }
19
+
20
+ async function patchRuntimeConfig(patch: Partial<RuntimeConfig>): Promise<RuntimeConfig> {
21
+ return fetchJson(
22
+ ONBOARDING_SWR_KEY,
23
+ RuntimeConfigSchema,
24
+ {
25
+ method: "PATCH",
26
+ headers: { "content-type": "application/json" },
27
+ body: JSON.stringify(patch),
28
+ },
29
+ (response) => `PATCH failed with status ${response.status}`,
30
+ );
31
+ }
32
+
33
+ export type UseOnboarding = {
34
+ hasSeenOnboarding: boolean;
35
+ isLoading: boolean;
36
+ markSeen: () => Promise<void>;
37
+ };
38
+
39
+ /**
40
+ * Hook for the first-launch onboarding banner. The server's
41
+ * `hasSeenOnboarding` flag is the source of truth — set to `true` on
42
+ * dismissal and persisted in the server config file so it survives
43
+ * across browser sessions.
44
+ *
45
+ * `hasSeenOnboarding` defaults to `false` until the SWR fetch resolves,
46
+ * which means the banner shows briefly on first load. That is
47
+ * acceptable: the fetch is local and resolves within milliseconds.
48
+ */
49
+ export function useOnboarding(): UseOnboarding {
50
+ const response: SWRResponse<RuntimeConfig, Error> = useSWR<RuntimeConfig, Error>(
51
+ ONBOARDING_SWR_KEY,
52
+ fetcher,
53
+ {
54
+ revalidateOnFocus: false,
55
+ revalidateIfStale: false,
56
+ },
57
+ );
58
+ const { mutate: globalMutate } = useSWRConfig();
59
+
60
+ const hasSeenOnboarding = response.data?.hasSeenOnboarding ?? false;
61
+
62
+ const markSeen = async (): Promise<void> => {
63
+ await globalMutate(ONBOARDING_SWR_KEY, patchRuntimeConfig({ hasSeenOnboarding: true }), {
64
+ optimisticData: {
65
+ stripClaudeCodeBillingHeader: response.data?.stripClaudeCodeBillingHeader ?? false,
66
+ hasSeenOnboarding: true,
67
+ slowResponseThresholdSeconds:
68
+ response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
69
+ },
70
+ rollbackOnError: true,
71
+ revalidate: false,
72
+ });
73
+ };
74
+
75
+ return {
76
+ hasSeenOnboarding,
77
+ isLoading: response.isLoading,
78
+ markSeen,
79
+ };
80
+ }