@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,209 @@
1
+ import { z } from "zod";
2
+
3
+ // Import JsonValueSchema from shared location to avoid circular dependency
4
+ import { JsonValueSchema, type JsonValue } from "./formats/jsonSchema";
5
+
6
+ export { JsonValueSchema };
7
+ export type { JsonValue };
8
+
9
+ export type RequestFormat = "anthropic" | "openai" | "unknown";
10
+
11
+ import { safeGetOwnProperty } from "../lib/objectUtils";
12
+
13
+ /**
14
+ * A single SSE event from a streaming response.
15
+ */
16
+ export const StreamingChunkSchema = z.object({
17
+ index: z.number(),
18
+ timestamp: z.number(),
19
+ type: z.string(),
20
+ data: JsonValueSchema,
21
+ });
22
+
23
+ export type StreamingChunk = z.infer<typeof StreamingChunkSchema>;
24
+
25
+ const StreamingChunksArraySchema = z.object({
26
+ chunks: z.array(StreamingChunkSchema),
27
+ truncated: z.boolean().optional().default(false),
28
+ });
29
+
30
+ export const CapturedLogSchema = z.object({
31
+ id: z.number(),
32
+ timestamp: z.string(),
33
+ method: z.string(),
34
+ path: z.string(),
35
+ model: z.string().nullable(),
36
+ sessionId: z.string().nullable(),
37
+ rawRequestBody: z.string().nullable(),
38
+ responseStatus: z.number().nullable(),
39
+ responseText: z.string().nullable(),
40
+ inputTokens: z.number().nullable(),
41
+ outputTokens: z.number().nullable(),
42
+ cacheCreationInputTokens: z.number().nullable(),
43
+ cacheReadInputTokens: z.number().nullable(),
44
+ elapsedMs: z.number().nullable(),
45
+ streaming: z.boolean(),
46
+ userAgent: z.string().nullable(),
47
+ origin: z.string().nullable(),
48
+ rawHeaders: z.record(z.string(), z.string()).optional(),
49
+ /** Headers sent to the upstream LLM */
50
+ headers: z.record(z.string(), z.string()).optional(),
51
+ apiFormat: z.enum(["anthropic", "openai", "unknown"]).default("unknown"),
52
+ isTest: z.boolean().optional().default(false),
53
+ providerName: z.string().nullable().optional(),
54
+ clientPort: z.number().nullable().optional(),
55
+ clientPid: z.number().nullable().optional(),
56
+ clientCwd: z.string().nullable().optional(),
57
+ clientProjectFolder: z.string().nullable().optional(),
58
+ streamingChunks: StreamingChunksArraySchema.optional(),
59
+ streamingChunksPath: z.string().nullable().optional(),
60
+ /** Error message from streaming response (e.g., SSE error event) */
61
+ error: z.string().nullable().optional(),
62
+ });
63
+
64
+ export type CapturedLog = z.infer<typeof CapturedLogSchema>;
65
+
66
+ export type TokenUsage = {
67
+ inputTokens: number | null;
68
+ outputTokens: number | null;
69
+ cacheCreationInputTokens: number | null;
70
+ cacheReadInputTokens: number | null;
71
+ };
72
+
73
+ // ============================================================
74
+ // Import types and schemas from formats
75
+ // ============================================================
76
+
77
+ // Import types and schemas from formats for internal use
78
+ import { InspectorRequestSchema, type InspectorRequest } from "./formats/anthropic/schemas";
79
+
80
+ // Re-export types and schemas from formats for backward compatibility
81
+ export {
82
+ InspectorRequestSchema,
83
+ InspectorResponseSchema,
84
+ SseEventSchema,
85
+ } from "./formats/anthropic/schemas";
86
+ export type {
87
+ InspectorRequest,
88
+ InspectorResponse,
89
+ ResponseContentBlockType,
90
+ } from "./formats/anthropic/schemas";
91
+
92
+ export {
93
+ OpenAIRequestSchema,
94
+ OpenAIResponseSchema,
95
+ OpenAISSERawChunkSchema,
96
+ type OpenAIResponse,
97
+ type OpenAIToolCall,
98
+ parseOpenAIResponse,
99
+ } from "./formats/openai/schemas";
100
+
101
+ // ============================================================
102
+ // Utility functions
103
+ // ============================================================
104
+
105
+ function detectFormat(rawBody: string | null): RequestFormat {
106
+ if (rawBody === null) return "unknown";
107
+ try {
108
+ const json: unknown = JSON.parse(rawBody);
109
+ if (typeof json === "object" && json !== null && !Array.isArray(json)) {
110
+ const hasModel = safeGetOwnProperty(json, "model") !== undefined;
111
+ const hasMessages = safeGetOwnProperty(json, "messages") !== undefined;
112
+ if (hasModel && hasMessages) {
113
+ // Anthropic requests put `system` as a top-level key; OpenAI does not.
114
+ // Both formats can have `tools`, so we check `system` as the discriminator.
115
+ if (safeGetOwnProperty(json, "system") !== undefined) {
116
+ return "anthropic";
117
+ }
118
+ const msgVal = safeGetOwnProperty(json, "messages");
119
+ if (Array.isArray(msgVal)) {
120
+ return "openai";
121
+ }
122
+ }
123
+ }
124
+ return "unknown";
125
+ } catch {
126
+ return "unknown";
127
+ }
128
+ }
129
+
130
+ // Schema for model name in request body (used for routing)
131
+ const RequestModelSchema = z.object({
132
+ model: z.string(),
133
+ });
134
+
135
+ export function extractModelFromBody(body: string): string | null {
136
+ try {
137
+ const json: unknown = JSON.parse(body);
138
+ const parsed = RequestModelSchema.safeParse(json);
139
+ if (parsed.success) {
140
+ return parsed.data.model;
141
+ }
142
+ return null;
143
+ } catch {
144
+ return null;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Loose request schema used for routing + log metadata. Tolerant of missing
150
+ * fields so it can extract what it can from a partially-valid or unparseable
151
+ * body without throwing.
152
+ */
153
+ export const LooseRequestSchema = z.object({
154
+ model: z.string().optional(),
155
+ metadata: z.object({ user_id: z.string().optional() }).passthrough().optional(),
156
+ });
157
+
158
+ export type RequestMetadata = {
159
+ model: string | null;
160
+ sessionId: string | null;
161
+ };
162
+
163
+ function extractSessionIdFromHeaders(headers: Headers): string | null {
164
+ return headers.get("x-agent-inspector-session-id") ?? headers.get("x-session-affinity");
165
+ }
166
+
167
+ /**
168
+ * Parse a request body exactly once and pull out everything the handler needs
169
+ * for routing + log creation: the model (used for provider selection) and the
170
+ * session id (from `metadata.user_id` or the `x-session-affinity` header).
171
+ *
172
+ * Returning `null` for either field is a valid outcome — callers must handle
173
+ * "unknown" gracefully. Callers that already have the body string should use
174
+ * this instead of re-parsing with `extractModelFromBody` and a second pass
175
+ * for the session id.
176
+ */
177
+ export function extractRequestMetadata(body: string | null, headers: Headers): RequestMetadata {
178
+ const headerSessionId = extractSessionIdFromHeaders(headers);
179
+ if (body === null) return { model: null, sessionId: headerSessionId };
180
+ try {
181
+ const json: unknown = JSON.parse(body);
182
+ const loose = LooseRequestSchema.safeParse(json);
183
+ if (loose.success) {
184
+ return {
185
+ model: loose.data.model ?? null,
186
+ sessionId: loose.data.metadata?.user_id ?? headerSessionId,
187
+ };
188
+ }
189
+ } catch {
190
+ // body not valid JSON
191
+ }
192
+ return { model: null, sessionId: headerSessionId };
193
+ }
194
+
195
+ /**
196
+ * Parse a raw request body into an InspectorRequest.
197
+ * @deprecated Use FormatHandler.parseRequest() instead
198
+ */
199
+ export function parseRequest(rawBody: string | null): InspectorRequest | null {
200
+ if (rawBody === null) return null;
201
+ try {
202
+ const json: unknown = JSON.parse(rawBody);
203
+ const result = InspectorRequestSchema.safeParse(json);
204
+ if (result.success) return result.data;
205
+ return null;
206
+ } catch {
207
+ return null;
208
+ }
209
+ }
@@ -0,0 +1,140 @@
1
+ import { spawn, type ChildProcess } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import { logger } from "./logger";
5
+ import type { FinalizeLogJob, FinalizeLogResult } from "./logFinalizer";
6
+
7
+ const IDLE_TIMEOUT_MS = Number(process.env["SESSION_PROCESS_IDLE_MS"]) || 5 * 60 * 1000; // 5 min default
8
+ const MAX_RESTARTS = 3;
9
+
10
+ const _processes = new Map<string, SessionProcess>();
11
+
12
+ type PendingJob = {
13
+ resolve: (result: FinalizeLogResult) => void;
14
+ reject: (err: Error) => void;
15
+ };
16
+
17
+ function resolveSessionWorkerPath(): string {
18
+ return fileURLToPath(new URL("./sessionWorkerEntry.ts", import.meta.url));
19
+ }
20
+
21
+ export function isSessionProcessAvailable(): boolean {
22
+ return existsSync(resolveSessionWorkerPath());
23
+ }
24
+
25
+ export class SessionProcess {
26
+ private child: ChildProcess | null = null;
27
+ private pending = new Map<string, PendingJob>();
28
+ private nextId = 0;
29
+ private idleTimer: ReturnType<typeof setTimeout> | null = null;
30
+ private restartCount = 0;
31
+ private destroyed = false;
32
+
33
+ constructor(private sessionId: string) {}
34
+
35
+ /** Number of outstanding jobs sent to this process. */
36
+ get pendingCount(): number {
37
+ return this.pending.size;
38
+ }
39
+
40
+ private ensureRunning(): ChildProcess {
41
+ if (this.child !== null && this.child.connected) return this.child;
42
+
43
+ const resolvedPath = resolveSessionWorkerPath();
44
+
45
+ this.child = spawn(process.execPath, [...process.execArgv, resolvedPath], {
46
+ stdio: ["pipe", "pipe", "pipe", "ipc"],
47
+ windowsHide: true,
48
+ });
49
+
50
+ this.restartCount += 1;
51
+
52
+ this.child.on("message", (msg: { id: string; result: FinalizeLogResult }) => {
53
+ const pending = this.pending.get(msg.id);
54
+ if (pending !== undefined) {
55
+ this.pending.delete(msg.id);
56
+ pending.resolve(msg.result);
57
+ }
58
+ this.resetIdleTimer();
59
+ });
60
+
61
+ this.child.on("error", (err) => {
62
+ logger.error(`[sessionProcess] Session ${this.sessionId} process error:`, err.message);
63
+ });
64
+
65
+ this.child.on("exit", (code, signal) => {
66
+ const wasConnected = this.child !== null;
67
+ this.child = null;
68
+
69
+ if (this.destroyed) return;
70
+
71
+ // Reject all pending jobs on unexpected exit
72
+ for (const [, pending] of this.pending) {
73
+ pending.reject(
74
+ new Error(
75
+ `Session process exited with code ${code ?? signal}, session=${this.sessionId}`,
76
+ ),
77
+ );
78
+ }
79
+ this.pending.clear();
80
+
81
+ if (wasConnected && this.restartCount <= MAX_RESTARTS) {
82
+ logger.warn(
83
+ `[sessionProcess] Session ${this.sessionId} worker exited (code=${code ?? signal}), ` +
84
+ `restart ${this.restartCount}/${MAX_RESTARTS}`,
85
+ );
86
+ }
87
+ });
88
+
89
+ this.resetIdleTimer();
90
+ return this.child;
91
+ }
92
+
93
+ enqueue(job: FinalizeLogJob): Promise<FinalizeLogResult> {
94
+ return new Promise((resolve, reject) => {
95
+ const child = this.ensureRunning();
96
+ const id = String(++this.nextId);
97
+ this.pending.set(id, { resolve, reject });
98
+ child.send({ id, job });
99
+ });
100
+ }
101
+
102
+ private resetIdleTimer(): void {
103
+ if (this.idleTimer !== null) clearTimeout(this.idleTimer);
104
+ this.idleTimer = setTimeout(() => {
105
+ if (this.pending.size === 0) {
106
+ this.destroy();
107
+ }
108
+ }, IDLE_TIMEOUT_MS);
109
+ }
110
+
111
+ destroy(): void {
112
+ this.destroyed = true;
113
+ if (this.idleTimer !== null) {
114
+ clearTimeout(this.idleTimer);
115
+ this.idleTimer = null;
116
+ }
117
+ if (this.child !== null) {
118
+ this.child.kill();
119
+ this.child = null;
120
+ }
121
+ _processes.delete(this.sessionId);
122
+ }
123
+ }
124
+
125
+ /** Get or create a dedicated child process for a session. */
126
+ export function getSessionProcess(sessionId: string): SessionProcess {
127
+ const existing = _processes.get(sessionId);
128
+ if (existing !== undefined) return existing;
129
+
130
+ const sp = new SessionProcess(sessionId);
131
+ _processes.set(sessionId, sp);
132
+ return sp;
133
+ }
134
+
135
+ /** Forcefully tear down all session processes (e.g. on server shutdown). */
136
+ export function destroyAllSessionProcesses(): void {
137
+ for (const [, sp] of _processes) {
138
+ sp.destroy();
139
+ }
140
+ }
@@ -0,0 +1,85 @@
1
+ import { logger } from "./logger";
2
+ import { executeFinalizeLogJob, type FinalizeLogJob } from "./logFinalizer";
3
+ import type { CapturedLog } from "./schemas";
4
+ import {
5
+ getLogSessionId,
6
+ markSessionTaskFinished,
7
+ markSessionTaskQueued,
8
+ markSessionTaskStarted,
9
+ } from "./sessionSupervisor";
10
+
11
+ const UNASSIGNED_SESSION_QUEUE = "__unassigned__";
12
+
13
+ export type SessionTaskName = "finalize-log";
14
+
15
+ type SessionTask<T> = () => Promise<T> | T;
16
+
17
+ const queues = new Map<string, Promise<void>>();
18
+
19
+ function queueKeyForSession(sessionId: string | null): string {
20
+ return sessionId ?? UNASSIGNED_SESSION_QUEUE;
21
+ }
22
+
23
+ function errorMessage(err: unknown): string {
24
+ return err instanceof Error ? err.message : String(err);
25
+ }
26
+
27
+ async function runSessionTask<T>(
28
+ sessionId: string | null,
29
+ taskName: SessionTaskName,
30
+ task: SessionTask<T>,
31
+ ): Promise<T> {
32
+ markSessionTaskStarted(sessionId);
33
+ try {
34
+ const result = await task();
35
+ markSessionTaskFinished(sessionId, null);
36
+ return result;
37
+ } catch (err) {
38
+ const message = errorMessage(err);
39
+ logger.error(`[sessionRuntime] ${taskName} failed for session ${sessionId ?? "unassigned"}`);
40
+ markSessionTaskFinished(sessionId, message);
41
+ return Promise.reject(err);
42
+ }
43
+ }
44
+
45
+ export function enqueueSessionTask<T>(
46
+ sessionId: string | null,
47
+ taskName: SessionTaskName,
48
+ task: SessionTask<T>,
49
+ ): Promise<T> {
50
+ const queueKey = queueKeyForSession(sessionId);
51
+ const previous = queues.get(queueKey) ?? Promise.resolve();
52
+
53
+ markSessionTaskQueued(sessionId);
54
+
55
+ const current = previous
56
+ .catch(() => undefined)
57
+ .then(() => runSessionTask(sessionId, taskName, task));
58
+ const settled = current.then(
59
+ () => undefined,
60
+ () => undefined,
61
+ );
62
+
63
+ queues.set(queueKey, settled);
64
+ void settled.then(() => {
65
+ if (queues.get(queueKey) === settled) {
66
+ queues.delete(queueKey);
67
+ }
68
+ });
69
+
70
+ return current;
71
+ }
72
+
73
+ export function enqueueLogTask<T>(
74
+ log: CapturedLog,
75
+ taskName: SessionTaskName,
76
+ task: SessionTask<T>,
77
+ ): Promise<T> {
78
+ return enqueueSessionTask(getLogSessionId(log), taskName, task);
79
+ }
80
+
81
+ export function enqueueFinalizeLogJob(job: FinalizeLogJob): Promise<void> {
82
+ return enqueueSessionTask(getLogSessionId(job.log), "finalize-log", () =>
83
+ executeFinalizeLogJob(job),
84
+ );
85
+ }
@@ -0,0 +1,283 @@
1
+ import type { CapturedLog } from "./schemas";
2
+ import { isSessionProcessAvailable } from "./sessionProcess";
3
+
4
+ export const PROVIDER_TEST_SESSION_ID = "provider-test";
5
+
6
+ export type SessionClientInfo = {
7
+ port: number | null;
8
+ pid: number | null;
9
+ cwd: string | null;
10
+ projectFolder: string | null;
11
+ };
12
+
13
+ export type SessionSource = "explicit" | "client-process" | "client-connection" | "provider-test";
14
+
15
+ export type SessionIdentity = {
16
+ id: string | null;
17
+ source: SessionSource | null;
18
+ };
19
+
20
+ export type SessionRuntimeMode = "in-process" | "worker-thread" | "child-process";
21
+
22
+ function getRuntimeMode(): SessionRuntimeMode {
23
+ if (process.env["FINALIZER_USE_WORKER"] === "0") return "in-process";
24
+ const mode = process.env["FINALIZER_RUNTIME"];
25
+ if (mode === "process") return "child-process";
26
+ if (mode === "worker") return "worker-thread";
27
+ if (mode === "inline") return "in-process";
28
+ return isSessionProcessAvailable() ? "child-process" : "in-process";
29
+ }
30
+
31
+ export type SessionSnapshot = {
32
+ id: string;
33
+ source: SessionSource;
34
+ runtimeMode: SessionRuntimeMode;
35
+ createdAt: string;
36
+ updatedAt: string;
37
+ requestCount: number;
38
+ activeRequests: number;
39
+ completedRequests: number;
40
+ failedRequests: number;
41
+ queuedTasks: number;
42
+ runningTasks: number;
43
+ lastTaskError: string | null;
44
+ lastLogId: number | null;
45
+ lastModel: string | null;
46
+ clientPid: number | null;
47
+ clientProjectFolder: string | null;
48
+ };
49
+
50
+ type SessionRecord = SessionSnapshot;
51
+
52
+ const sessions = new Map<string, SessionRecord>();
53
+ const knownLogSessions = new Map<number, string>();
54
+ const completedLogIds = new Set<number>();
55
+ const failedLogIds = new Set<number>();
56
+
57
+ function normalizeExplicitSessionId(sessionId: string | null | undefined): string | null {
58
+ if (sessionId === null || sessionId === undefined) return null;
59
+ const trimmed = sessionId.trim();
60
+ return trimmed.length > 0 ? trimmed : null;
61
+ }
62
+
63
+ function buildClientSessionId(clientInfo: SessionClientInfo): SessionIdentity {
64
+ const projectFolder = normalizeExplicitSessionId(clientInfo.projectFolder);
65
+
66
+ if (clientInfo.pid !== null) {
67
+ return {
68
+ id:
69
+ projectFolder !== null
70
+ ? `process:${clientInfo.pid}:${projectFolder}`
71
+ : `process:${clientInfo.pid}`,
72
+ source: "client-process",
73
+ };
74
+ }
75
+
76
+ if (clientInfo.port !== null) {
77
+ return {
78
+ id: `connection:${clientInfo.port}`,
79
+ source: "client-connection",
80
+ };
81
+ }
82
+
83
+ return { id: null, source: null };
84
+ }
85
+
86
+ export function resolveSessionIdentity(input: {
87
+ explicitSessionId: string | null;
88
+ clientInfo?: SessionClientInfo;
89
+ isTest?: boolean;
90
+ }): SessionIdentity {
91
+ if (input.isTest === true) {
92
+ return { id: PROVIDER_TEST_SESSION_ID, source: "provider-test" };
93
+ }
94
+
95
+ const explicit = normalizeExplicitSessionId(input.explicitSessionId);
96
+ if (explicit !== null) {
97
+ return { id: explicit, source: "explicit" };
98
+ }
99
+
100
+ if (input.clientInfo !== undefined) {
101
+ return buildClientSessionId(input.clientInfo);
102
+ }
103
+
104
+ return { id: null, source: null };
105
+ }
106
+
107
+ export function getLogSessionIdentity(log: CapturedLog): SessionIdentity {
108
+ return resolveSessionIdentity({
109
+ explicitSessionId: log.sessionId,
110
+ clientInfo: {
111
+ port: log.clientPort ?? null,
112
+ pid: log.clientPid ?? null,
113
+ cwd: log.clientCwd ?? null,
114
+ projectFolder: log.clientProjectFolder ?? null,
115
+ },
116
+ isTest: log.isTest,
117
+ });
118
+ }
119
+
120
+ export function getLogSessionId(log: CapturedLog): string | null {
121
+ return getLogSessionIdentity(log).id;
122
+ }
123
+
124
+ function getTimestamp(log: CapturedLog): string {
125
+ return log.timestamp.length > 0 ? log.timestamp : new Date().toISOString();
126
+ }
127
+
128
+ function createSessionRecord(id: string, source: SessionSource, log: CapturedLog): SessionRecord {
129
+ const timestamp = getTimestamp(log);
130
+ return {
131
+ id,
132
+ source,
133
+ runtimeMode: getRuntimeMode(),
134
+ createdAt: timestamp,
135
+ updatedAt: timestamp,
136
+ requestCount: 0,
137
+ activeRequests: 0,
138
+ completedRequests: 0,
139
+ failedRequests: 0,
140
+ queuedTasks: 0,
141
+ runningTasks: 0,
142
+ lastTaskError: null,
143
+ lastLogId: null,
144
+ lastModel: null,
145
+ clientPid: log.clientPid ?? null,
146
+ clientProjectFolder: log.clientProjectFolder ?? null,
147
+ };
148
+ }
149
+
150
+ function upsertSession(
151
+ log: CapturedLog,
152
+ sourceOverride?: SessionSource | null,
153
+ ): SessionRecord | null {
154
+ const identity = getLogSessionIdentity(log);
155
+ if (identity.id === null) return null;
156
+
157
+ const source = sourceOverride ?? identity.source;
158
+ if (source === null) return null;
159
+
160
+ const existing = sessions.get(identity.id);
161
+ const session = existing ?? createSessionRecord(identity.id, source, log);
162
+ session.updatedAt = new Date().toISOString();
163
+ session.lastLogId = log.id;
164
+ session.lastModel = log.model ?? session.lastModel;
165
+ session.clientPid = log.clientPid ?? session.clientPid;
166
+ session.clientProjectFolder = log.clientProjectFolder ?? session.clientProjectFolder;
167
+
168
+ sessions.set(identity.id, session);
169
+ return session;
170
+ }
171
+
172
+ function rememberLogSession(log: CapturedLog): boolean {
173
+ const sessionId = getLogSessionId(log);
174
+ if (sessionId === null) return false;
175
+
176
+ const knownSessionId = knownLogSessions.get(log.id);
177
+ if (knownSessionId === sessionId) return false;
178
+
179
+ knownLogSessions.set(log.id, sessionId);
180
+ return true;
181
+ }
182
+
183
+ function isFailedLog(log: CapturedLog): boolean {
184
+ const hasError = log.error !== null && log.error !== undefined && log.error.length > 0;
185
+ const hasErrorStatus = log.responseStatus !== null && log.responseStatus >= 400;
186
+ return hasError || hasErrorStatus;
187
+ }
188
+
189
+ export function markSessionStarted(log: CapturedLog, sourceOverride?: SessionSource | null): void {
190
+ const session = upsertSession(log, sourceOverride);
191
+ if (session === null) return;
192
+
193
+ const isNewLog = rememberLogSession(log);
194
+ if (isNewLog) {
195
+ session.requestCount += 1;
196
+ }
197
+ session.activeRequests += 1;
198
+ }
199
+
200
+ export function markSessionFinished(log: CapturedLog, sourceOverride?: SessionSource | null): void {
201
+ const session = upsertSession(log, sourceOverride);
202
+ if (session === null) return;
203
+
204
+ if (session.activeRequests > 0) {
205
+ session.activeRequests -= 1;
206
+ }
207
+
208
+ if (completedLogIds.has(log.id) === false) {
209
+ completedLogIds.add(log.id);
210
+ session.completedRequests += 1;
211
+ }
212
+
213
+ if (isFailedLog(log) && failedLogIds.has(log.id) === false) {
214
+ failedLogIds.add(log.id);
215
+ session.failedRequests += 1;
216
+ }
217
+ }
218
+
219
+ export function markSessionTaskQueued(sessionId: string | null): void {
220
+ if (sessionId === null) return;
221
+ const session = sessions.get(sessionId);
222
+ if (session === undefined) return;
223
+ session.queuedTasks += 1;
224
+ session.updatedAt = new Date().toISOString();
225
+ }
226
+
227
+ export function markSessionTaskStarted(sessionId: string | null): void {
228
+ if (sessionId === null) return;
229
+ const session = sessions.get(sessionId);
230
+ if (session === undefined) return;
231
+ if (session.queuedTasks > 0) {
232
+ session.queuedTasks -= 1;
233
+ }
234
+ session.runningTasks += 1;
235
+ session.updatedAt = new Date().toISOString();
236
+ }
237
+
238
+ export function markSessionTaskFinished(sessionId: string | null, error: string | null): void {
239
+ if (sessionId === null) return;
240
+ const session = sessions.get(sessionId);
241
+ if (session === undefined) return;
242
+ if (session.runningTasks > 0) {
243
+ session.runningTasks -= 1;
244
+ }
245
+ session.lastTaskError = error;
246
+ session.updatedAt = new Date().toISOString();
247
+ }
248
+
249
+ export function observeSessionLog(log: CapturedLog, sourceOverride?: SessionSource | null): void {
250
+ const session = upsertSession(log, sourceOverride);
251
+ if (session === null) return;
252
+
253
+ const isNewLog = rememberLogSession(log);
254
+ if (isNewLog) {
255
+ session.requestCount += 1;
256
+ }
257
+
258
+ if (log.responseStatus !== null || log.responseText !== null) {
259
+ markSessionFinished(log, sourceOverride);
260
+ }
261
+ }
262
+
263
+ export function clearSessionRegistry(): void {
264
+ sessions.clear();
265
+ knownLogSessions.clear();
266
+ completedLogIds.clear();
267
+ failedLogIds.clear();
268
+ }
269
+
270
+ export function rebuildSessionRegistry(logs: Iterable<CapturedLog>): void {
271
+ clearSessionRegistry();
272
+ for (const log of logs) {
273
+ observeSessionLog(log);
274
+ }
275
+ }
276
+
277
+ export function getSessionIds(): string[] {
278
+ return [...sessions.values()].map((session) => session.id);
279
+ }
280
+
281
+ export function getSessionSnapshots(): SessionSnapshot[] {
282
+ return [...sessions.values()].map((session) => ({ ...session }));
283
+ }