@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.
- package/.output/cli.js +1611 -0
- package/.output/nitro.json +17 -0
- package/.output/public/assets/CompareDrawer-CU5ZrWcL.js +1 -0
- package/.output/public/assets/ProxyViewerContainer-pEBqVp1d.js +101 -0
- package/.output/public/assets/ReplayDialog-F58yNg5j.js +1 -0
- package/.output/public/assets/RequestAnatomy-C9lT0qE_.js +1 -0
- package/.output/public/assets/ResponseView-DHJq6bnz.js +1 -0
- package/.output/public/assets/StreamingChunkSequence-BTgfpFUT.js +1 -0
- package/.output/public/assets/_sessionId-DsNRbnNm.js +1 -0
- package/.output/public/assets/alibaba-TTwafVwX.svg +1 -0
- package/.output/public/assets/index-CpWG2hFn.css +1 -0
- package/.output/public/assets/index-DmBV8Gve.js +1 -0
- package/.output/public/assets/json-viewer-CZVYLR8j.js +14 -0
- package/.output/public/assets/main-DHs7FBK3.js +18 -0
- package/.output/public/assets/minimax-BPMzvuL-.jpeg +0 -0
- package/.output/public/assets/qwen-CONDcHqt.png +0 -0
- package/.output/public/assets/zhipuai-BPNAnxo-.svg +219 -0
- package/.output/server/_chunks/ssr-renderer.mjs +22 -0
- package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +210 -0
- package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +2 -0
- package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +2 -0
- package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +2 -0
- package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +11 -0
- package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +69 -0
- package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +17 -0
- package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +6 -0
- package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +34 -0
- package/.output/server/_libs/ajv-formats.mjs +330 -0
- package/.output/server/_libs/ajv.mjs +11444 -0
- package/.output/server/_libs/aria-hidden.mjs +122 -0
- package/.output/server/_libs/atomically.mjs +152 -0
- package/.output/server/_libs/bail.mjs +8 -0
- package/.output/server/_libs/cfworker__json-schema.mjs +1 -0
- package/.output/server/_libs/character-entities.mjs +2130 -0
- package/.output/server/_libs/class-variance-authority.mjs +44 -0
- package/.output/server/_libs/clsx.mjs +16 -0
- package/.output/server/_libs/comma-separated-tokens.mjs +10 -0
- package/.output/server/_libs/conf.mjs +635 -0
- package/.output/server/_libs/cookie-es.mjs +58 -0
- package/.output/server/_libs/core-util-is.mjs +75 -0
- package/.output/server/_libs/croner.mjs +1 -0
- package/.output/server/_libs/crossws.mjs +1 -0
- package/.output/server/_libs/debounce-fn.mjs +69 -0
- package/.output/server/_libs/decode-named-character-reference+[...].mjs +8 -0
- package/.output/server/_libs/dequal.mjs +27 -0
- package/.output/server/_libs/detect-node-es.mjs +1 -0
- package/.output/server/_libs/devlop.mjs +8 -0
- package/.output/server/_libs/diff.mjs +320 -0
- package/.output/server/_libs/dot-prop.mjs +265 -0
- package/.output/server/_libs/env-paths.mjs +57 -0
- package/.output/server/_libs/estree-util-is-identifier-name.mjs +11 -0
- package/.output/server/_libs/extend.mjs +97 -0
- package/.output/server/_libs/fast-deep-equal.mjs +38 -0
- package/.output/server/_libs/fast-uri.mjs +812 -0
- package/.output/server/_libs/floating-ui__core.mjs +725 -0
- package/.output/server/_libs/floating-ui__dom.mjs +622 -0
- package/.output/server/_libs/floating-ui__react-dom.mjs +292 -0
- package/.output/server/_libs/floating-ui__utils.mjs +320 -0
- package/.output/server/_libs/get-nonce.mjs +9 -0
- package/.output/server/_libs/h3-v2.mjs +276 -0
- package/.output/server/_libs/h3.mjs +408 -0
- package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +388 -0
- package/.output/server/_libs/hast-util-whitespace.mjs +10 -0
- package/.output/server/_libs/hookable.mjs +1 -0
- package/.output/server/_libs/html-url-attributes.mjs +26 -0
- package/.output/server/_libs/immediate.mjs +74 -0
- package/.output/server/_libs/inherits.mjs +50 -0
- package/.output/server/_libs/inline-style-parser.mjs +142 -0
- package/.output/server/_libs/is-plain-obj.mjs +10 -0
- package/.output/server/_libs/isarray.mjs +14 -0
- package/.output/server/_libs/isbot.mjs +20 -0
- package/.output/server/_libs/json-schema-traverse.mjs +180 -0
- package/.output/server/_libs/jszip.mjs +3051 -0
- package/.output/server/_libs/lie.mjs +273 -0
- package/.output/server/_libs/lucide-react.mjs +492 -0
- package/.output/server/_libs/mdast-util-from-markdown.mjs +717 -0
- package/.output/server/_libs/mdast-util-to-hast.mjs +710 -0
- package/.output/server/_libs/mdast-util-to-string.mjs +38 -0
- package/.output/server/_libs/micromark-core-commonmark.mjs +2259 -0
- package/.output/server/_libs/micromark-factory-destination.mjs +94 -0
- package/.output/server/_libs/micromark-factory-label.mjs +63 -0
- package/.output/server/_libs/micromark-factory-space.mjs +24 -0
- package/.output/server/_libs/micromark-factory-title.mjs +65 -0
- package/.output/server/_libs/micromark-factory-whitespace.mjs +22 -0
- package/.output/server/_libs/micromark-util-character.mjs +44 -0
- package/.output/server/_libs/micromark-util-chunked.mjs +36 -0
- package/.output/server/_libs/micromark-util-classify-character+[...].mjs +12 -0
- package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +41 -0
- package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +19 -0
- package/.output/server/_libs/micromark-util-decode-string.mjs +21 -0
- package/.output/server/_libs/micromark-util-encode.mjs +1 -0
- package/.output/server/_libs/micromark-util-html-tag-name.mjs +69 -0
- package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +6 -0
- package/.output/server/_libs/micromark-util-resolve-all.mjs +15 -0
- package/.output/server/_libs/micromark-util-sanitize-uri.mjs +41 -0
- package/.output/server/_libs/micromark-util-subtokenize.mjs +346 -0
- package/.output/server/_libs/micromark.mjs +906 -0
- package/.output/server/_libs/mimic-function.mjs +47 -0
- package/.output/server/_libs/modelcontextprotocol__server.mjs +9738 -0
- package/.output/server/_libs/ocache.mjs +1 -0
- package/.output/server/_libs/ohash.mjs +1 -0
- package/.output/server/_libs/pako.mjs +4223 -0
- package/.output/server/_libs/process-nextick-args.mjs +48 -0
- package/.output/server/_libs/property-information.mjs +1209 -0
- package/.output/server/_libs/radix-ui.mjs +1 -0
- package/.output/server/_libs/radix-ui__number.mjs +6 -0
- package/.output/server/_libs/radix-ui__primitive.mjs +11 -0
- package/.output/server/_libs/radix-ui__react-accordion.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
- package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-avatar.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-checkbox.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-collapsible.mjs +144 -0
- package/.output/server/_libs/radix-ui__react-collection.mjs +69 -0
- package/.output/server/_libs/radix-ui__react-compose-refs.mjs +39 -0
- package/.output/server/_libs/radix-ui__react-context-menu.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-context.mjs +78 -0
- package/.output/server/_libs/radix-ui__react-dialog.mjs +325 -0
- package/.output/server/_libs/radix-ui__react-direction.mjs +9 -0
- package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-focus-guards.mjs +29 -0
- package/.output/server/_libs/radix-ui__react-focus-scope.mjs +206 -0
- package/.output/server/_libs/radix-ui__react-form.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-hover-card.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-id.mjs +14 -0
- package/.output/server/_libs/radix-ui__react-label.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-menu.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-menubar.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-popover.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-popper.mjs +286 -0
- package/.output/server/_libs/radix-ui__react-portal.mjs +16 -0
- package/.output/server/_libs/radix-ui__react-presence.mjs +128 -0
- package/.output/server/_libs/radix-ui__react-primitive.mjs +42 -0
- package/.output/server/_libs/radix-ui__react-progress.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-radio-group.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-roving-focus.mjs +224 -0
- package/.output/server/_libs/radix-ui__react-scroll-area.mjs +721 -0
- package/.output/server/_libs/radix-ui__react-select.mjs +1163 -0
- package/.output/server/_libs/radix-ui__react-separator.mjs +28 -0
- package/.output/server/_libs/radix-ui__react-slider.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-slot.mjs +99 -0
- package/.output/server/_libs/radix-ui__react-switch.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-tabs.mjs +189 -0
- package/.output/server/_libs/radix-ui__react-toast.mjs +2 -0
- package/.output/server/_libs/radix-ui__react-toggle-group.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-toggle.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-toolbar.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-tooltip.mjs +495 -0
- package/.output/server/_libs/radix-ui__react-use-previous.mjs +14 -0
- package/.output/server/_libs/radix-ui__react-use-size.mjs +39 -0
- package/.output/server/_libs/react-dom.mjs +10781 -0
- package/.output/server/_libs/react-markdown.mjs +147 -0
- package/.output/server/_libs/react-remove-scroll-bar.mjs +82 -0
- package/.output/server/_libs/react-remove-scroll.mjs +328 -0
- package/.output/server/_libs/react-style-singleton.mjs +69 -0
- package/.output/server/_libs/react.mjs +515 -0
- package/.output/server/_libs/readable-stream.mjs +1518 -0
- package/.output/server/_libs/remark-parse.mjs +19 -0
- package/.output/server/_libs/remark-rehype.mjs +21 -0
- package/.output/server/_libs/rou3.mjs +14 -0
- package/.output/server/_libs/safe-buffer.mjs +64 -0
- package/.output/server/_libs/semver.mjs +1938 -0
- package/.output/server/_libs/seroval-plugins.mjs +58 -0
- package/.output/server/_libs/seroval.mjs +1765 -0
- package/.output/server/_libs/setimmediate.mjs +152 -0
- package/.output/server/_libs/space-separated-tokens.mjs +6 -0
- package/.output/server/_libs/srvx.mjs +1029 -0
- package/.output/server/_libs/stubborn-fs.mjs +91 -0
- package/.output/server/_libs/stubborn-utils.mjs +66 -0
- package/.output/server/_libs/style-to-js.mjs +72 -0
- package/.output/server/_libs/style-to-object.mjs +38 -0
- package/.output/server/_libs/swr.mjs +939 -0
- package/.output/server/_libs/tailwind-merge.mjs +3010 -0
- package/.output/server/_libs/tanstack__history.mjs +217 -0
- package/.output/server/_libs/tanstack__react-router.mjs +1480 -0
- package/.output/server/_libs/tanstack__react-store.mjs +1 -0
- package/.output/server/_libs/tanstack__react-virtual.mjs +44 -0
- package/.output/server/_libs/tanstack__router-core.mjs +4827 -0
- package/.output/server/_libs/tanstack__store.mjs +1 -0
- package/.output/server/_libs/tanstack__virtual-core.mjs +1225 -0
- package/.output/server/_libs/tiny-invariant.mjs +12 -0
- package/.output/server/_libs/tiny-warning.mjs +5 -0
- package/.output/server/_libs/trim-lines.mjs +41 -0
- package/.output/server/_libs/trough.mjs +85 -0
- package/.output/server/_libs/tslib.mjs +1 -0
- package/.output/server/_libs/ufo.mjs +54 -0
- package/.output/server/_libs/uint8array-extras.mjs +69 -0
- package/.output/server/_libs/ungap__structured-clone.mjs +212 -0
- package/.output/server/_libs/unified.mjs +661 -0
- package/.output/server/_libs/unist-util-is.mjs +100 -0
- package/.output/server/_libs/unist-util-position.mjs +27 -0
- package/.output/server/_libs/unist-util-stringify-position.mjs +27 -0
- package/.output/server/_libs/unist-util-visit-parents.mjs +82 -0
- package/.output/server/_libs/unist-util-visit.mjs +24 -0
- package/.output/server/_libs/unstorage.mjs +1 -0
- package/.output/server/_libs/use-callback-ref.mjs +66 -0
- package/.output/server/_libs/use-sidecar.mjs +106 -0
- package/.output/server/_libs/use-sync-external-store.mjs +64 -0
- package/.output/server/_libs/util-deprecate.mjs +12 -0
- package/.output/server/_libs/vfile-message.mjs +138 -0
- package/.output/server/_libs/vfile.mjs +467 -0
- package/.output/server/_libs/when-exit.mjs +53 -0
- package/.output/server/_libs/zod.mjs +4524 -0
- package/.output/server/_sessionId-wMLPvC5g.mjs +123 -0
- package/.output/server/_ssr/CompareDrawer-BU4V0uVf.mjs +1041 -0
- package/.output/server/_ssr/ProxyViewerContainer-BnRwFEnn.mjs +5972 -0
- package/.output/server/_ssr/ReplayDialog-C7dn9pd_.mjs +322 -0
- package/.output/server/_ssr/RequestAnatomy-C1rWpe9-.mjs +353 -0
- package/.output/server/_ssr/ResponseView-hGpPaYsf.mjs +602 -0
- package/.output/server/_ssr/StreamingChunkSequence-BRWI1r_G.mjs +302 -0
- package/.output/server/_ssr/index-BKURLVPz.mjs +118 -0
- package/.output/server/_ssr/index.mjs +1184 -0
- package/.output/server/_ssr/json-viewer-BBd2DtQP.mjs +515 -0
- package/.output/server/_ssr/router-BcZ0D6AB.mjs +6317 -0
- package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
- package/.output/server/_tanstack-start-manifest_v-1y8ZVxRI.mjs +4 -0
- package/.output/server/index.mjs +436 -0
- package/.output/server/node_modules/tslib/modules/index.js +70 -0
- package/.output/server/node_modules/tslib/modules/package.json +3 -0
- package/.output/server/node_modules/tslib/package.json +47 -0
- package/.output/server/node_modules/tslib/tslib.js +484 -0
- package/.output/server/package.json +9 -0
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/package.json +110 -0
- package/src/assets/favicon.svg +31 -0
- package/src/assets/logos/alibaba.svg +1 -0
- package/src/assets/logos/anthropic.svg +1 -0
- package/src/assets/logos/claude-code.svg +4 -0
- package/src/assets/logos/deepseek.svg +1 -0
- package/src/assets/logos/mcp.png +0 -0
- package/src/assets/logos/minimax.jpeg +0 -0
- package/src/assets/logos/openai.svg +1 -0
- package/src/assets/logos/opencode.svg +4 -0
- package/src/assets/logos/qwen.png +0 -0
- package/src/assets/logos/zhipuai.svg +219 -0
- package/src/cli/detect-tools.ts +147 -0
- package/src/cli/doctor.ts +521 -0
- package/src/cli/onboard.ts +224 -0
- package/src/cli/templates/command-onboard.ts +17 -0
- package/src/cli/templates/skill-onboard.ts +547 -0
- package/src/cli.ts +345 -0
- package/src/components/OnboardingBanner.tsx +67 -0
- package/src/components/ProxyViewer.tsx +545 -0
- package/src/components/ProxyViewerContainer.tsx +363 -0
- package/src/components/providers/ImportWizardDialog.tsx +349 -0
- package/src/components/providers/ProviderCard.tsx +474 -0
- package/src/components/providers/ProviderForm.tsx +494 -0
- package/src/components/providers/ProviderLogo.tsx +117 -0
- package/src/components/providers/ProvidersPanel.tsx +619 -0
- package/src/components/providers/SettingsDialog.tsx +202 -0
- package/src/components/proxy-viewer/CompareDrawer.tsx +893 -0
- package/src/components/proxy-viewer/ConversationGroup.tsx +107 -0
- package/src/components/proxy-viewer/ConversationHeader.tsx +300 -0
- package/src/components/proxy-viewer/LogEntry.tsx +543 -0
- package/src/components/proxy-viewer/LogEntryHeader.tsx +501 -0
- package/src/components/proxy-viewer/ReplayDialog.tsx +218 -0
- package/src/components/proxy-viewer/ResponseView.tsx +171 -0
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +188 -0
- package/src/components/proxy-viewer/ThreadConnector.tsx +136 -0
- package/src/components/proxy-viewer/TurnGroup.tsx +337 -0
- package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +98 -0
- package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +196 -0
- package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +53 -0
- package/src/components/proxy-viewer/anatomy/types.ts +39 -0
- package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +114 -0
- package/src/components/proxy-viewer/cacheTrend.ts +50 -0
- package/src/components/proxy-viewer/diff/DiffView.tsx +321 -0
- package/src/components/proxy-viewer/diff/computeDiff.ts +178 -0
- package/src/components/proxy-viewer/diff/index.ts +3 -0
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +157 -0
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +66 -0
- package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +21 -0
- package/src/components/proxy-viewer/formats/index.tsx +33 -0
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +170 -0
- package/src/components/proxy-viewer/index.ts +9 -0
- package/src/components/proxy-viewer/lazy.ts +37 -0
- package/src/components/proxy-viewer/log-formats/anthropic.ts +194 -0
- package/src/components/proxy-viewer/log-formats/index.ts +23 -0
- package/src/components/proxy-viewer/log-formats/openai.ts +167 -0
- package/src/components/proxy-viewer/log-formats/types.ts +40 -0
- package/src/components/proxy-viewer/log-formats/unknown.ts +18 -0
- package/src/components/proxy-viewer/logEntryVisibility.ts +39 -0
- package/src/components/proxy-viewer/requestDiff.ts +277 -0
- package/src/components/proxy-viewer/useCopyFeedback.ts +36 -0
- package/src/components/proxy-viewer/useKeyboardNavigation.ts +190 -0
- package/src/components/proxy-viewer/viewerState.ts +66 -0
- package/src/components/ui/badge.tsx +47 -0
- package/src/components/ui/button.tsx +47 -0
- package/src/components/ui/collapsible.tsx +21 -0
- package/src/components/ui/confirm-dialog.tsx +51 -0
- package/src/components/ui/crab-logo.tsx +95 -0
- package/src/components/ui/crab-variants.tsx +467 -0
- package/src/components/ui/dialog.tsx +129 -0
- package/src/components/ui/json-expansion-button.tsx +56 -0
- package/src/components/ui/json-viewer-bulk.ts +97 -0
- package/src/components/ui/json-viewer.tsx +494 -0
- package/src/components/ui/mcp-logo.tsx +20 -0
- package/src/components/ui/scroll-area.tsx +54 -0
- package/src/components/ui/select.tsx +178 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/tabs.tsx +88 -0
- package/src/components/ui/tooltip.tsx +51 -0
- package/src/index.css +11 -0
- package/src/knowledge/candidateStore.ts +63 -0
- package/src/knowledge/distiller.ts +98 -0
- package/src/knowledge/openclawClient.ts +118 -0
- package/src/knowledge/redactor.ts +80 -0
- package/src/knowledge/types.ts +84 -0
- package/src/lib/apiClient.ts +49 -0
- package/src/lib/export-logs.ts +51 -0
- package/src/lib/mask.ts +4 -0
- package/src/lib/objectUtils.ts +22 -0
- package/src/lib/providerContract.ts +26 -0
- package/src/lib/providerTestContract.ts +107 -0
- package/src/lib/runtimeConfig.ts +25 -0
- package/src/lib/serverPort.ts +41 -0
- package/src/lib/sessionUrl.ts +44 -0
- package/src/lib/stopReason.ts +58 -0
- package/src/lib/useOnboarding.ts +80 -0
- package/src/lib/useProviders.ts +30 -0
- package/src/lib/useStripConfig.ts +108 -0
- package/src/lib/utils.ts +21 -0
- package/src/mcp/loopback.ts +76 -0
- package/src/mcp/previewExtractor.ts +166 -0
- package/src/mcp/server.ts +396 -0
- package/src/mcp/toolHandlers.ts +341 -0
- package/src/proxy/chunkStorage.ts +112 -0
- package/src/proxy/claudeCodeStrip.ts +99 -0
- package/src/proxy/config.ts +172 -0
- package/src/proxy/constants.ts +47 -0
- package/src/proxy/dataDir.ts +86 -0
- package/src/proxy/formats/anthropic/anthropicProvider.ts +75 -0
- package/src/proxy/formats/anthropic/handler.ts +71 -0
- package/src/proxy/formats/anthropic/index.ts +14 -0
- package/src/proxy/formats/anthropic/register.ts +4 -0
- package/src/proxy/formats/anthropic/schemas.ts +237 -0
- package/src/proxy/formats/anthropic/stream.ts +205 -0
- package/src/proxy/formats/handler.ts +46 -0
- package/src/proxy/formats/index.ts +12 -0
- package/src/proxy/formats/jsonSchema.ts +36 -0
- package/src/proxy/formats/openai/alibabaProvider.ts +38 -0
- package/src/proxy/formats/openai/handler.ts +96 -0
- package/src/proxy/formats/openai/index.ts +25 -0
- package/src/proxy/formats/openai/provider.ts +50 -0
- package/src/proxy/formats/openai/register.ts +4 -0
- package/src/proxy/formats/openai/schemas.ts +187 -0
- package/src/proxy/formats/openai/stream.ts +206 -0
- package/src/proxy/formats/protocol.ts +50 -0
- package/src/proxy/formats/providerRegistry.ts +51 -0
- package/src/proxy/formats/providers/index.ts +3 -0
- package/src/proxy/formats/registry.ts +66 -0
- package/src/proxy/handler.ts +334 -0
- package/src/proxy/logFinalizer.ts +305 -0
- package/src/proxy/logFinalizer.worker.ts +24 -0
- package/src/proxy/logIndex.ts +268 -0
- package/src/proxy/logger.ts +179 -0
- package/src/proxy/openaiOrphanToolStrip.ts +142 -0
- package/src/proxy/providerImporters.ts +491 -0
- package/src/proxy/providers.ts +613 -0
- package/src/proxy/schemas.ts +209 -0
- package/src/proxy/sessionProcess.ts +140 -0
- package/src/proxy/sessionRuntime.ts +85 -0
- package/src/proxy/sessionSupervisor.ts +283 -0
- package/src/proxy/sessionWorkerEntry.ts +26 -0
- package/src/proxy/socketTracker.ts +255 -0
- package/src/proxy/store.ts +412 -0
- package/src/proxy/upstream.ts +90 -0
- package/src/router.tsx +16 -0
- package/src/routes/__root.tsx +45 -0
- package/src/routes/api/config.paths.ts +14 -0
- package/src/routes/api/config.ts +53 -0
- package/src/routes/api/health.ts +15 -0
- package/src/routes/api/knowledge.candidates.$candidateId.promote.ts +32 -0
- package/src/routes/api/knowledge.candidates.ts +10 -0
- package/src/routes/api/knowledge.project-context.ts +18 -0
- package/src/routes/api/knowledge.search.ts +31 -0
- package/src/routes/api/knowledge.sessions.$sessionId.candidates.ts +16 -0
- package/src/routes/api/logs.$id.chunks.ts +36 -0
- package/src/routes/api/logs.$id.replay.ts +191 -0
- package/src/routes/api/logs.$id.ts +22 -0
- package/src/routes/api/logs.stream.ts +74 -0
- package/src/routes/api/logs.ts +59 -0
- package/src/routes/api/mcp.ts +25 -0
- package/src/routes/api/models.ts +10 -0
- package/src/routes/api/providers.$providerId.test.log.ts +293 -0
- package/src/routes/api/providers.$providerId.ts +50 -0
- package/src/routes/api/providers.export.ts +26 -0
- package/src/routes/api/providers.import.ts +47 -0
- package/src/routes/api/providers.scan.ts +23 -0
- package/src/routes/api/providers.ts +45 -0
- package/src/routes/api/sessions.ts +17 -0
- package/src/routes/index.tsx +6 -0
- package/src/routes/proxy/$.ts +15 -0
- package/src/routes/session/$sessionId.tsx +23 -0
- package/styles/globals.css +188 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { StopCircle, Zap } from "lucide-react";
|
|
2
|
+
import { memo, type JSX } from "react";
|
|
3
|
+
import type { InspectorResponse } from "../../../../proxy/schemas";
|
|
4
|
+
import { formatTokens } from "../../../../lib/utils";
|
|
5
|
+
import { Badge } from "../../../ui/badge";
|
|
6
|
+
import { Separator } from "../../../ui/separator";
|
|
7
|
+
import { ResponseContentBlockRenderer } from "./ContentBlocks";
|
|
8
|
+
|
|
9
|
+
export const StructuredResponseViewAnthropic = memo(function StructuredResponseViewAnthropic({
|
|
10
|
+
response,
|
|
11
|
+
}: {
|
|
12
|
+
response: InspectorResponse;
|
|
13
|
+
}): JSX.Element {
|
|
14
|
+
return (
|
|
15
|
+
<div className="space-y-3">
|
|
16
|
+
<div className="flex items-center gap-2 flex-wrap">
|
|
17
|
+
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 h-5 font-mono">
|
|
18
|
+
{response.model}
|
|
19
|
+
</Badge>
|
|
20
|
+
|
|
21
|
+
{response.stop_reason !== null && (
|
|
22
|
+
<Badge
|
|
23
|
+
variant="outline"
|
|
24
|
+
className="text-[10px] px-1.5 py-0 h-5 font-mono flex items-center gap-1"
|
|
25
|
+
>
|
|
26
|
+
<StopCircle className="size-2.5" />
|
|
27
|
+
{response.stop_reason}
|
|
28
|
+
</Badge>
|
|
29
|
+
)}
|
|
30
|
+
|
|
31
|
+
<span className="flex items-center gap-1 text-muted-foreground text-xs">
|
|
32
|
+
<Zap className="size-3" />
|
|
33
|
+
<span className="font-mono tabular-nums">
|
|
34
|
+
{formatTokens(response.usage.input_tokens ?? 0)} in /{" "}
|
|
35
|
+
{formatTokens(response.usage.output_tokens ?? 0)} out
|
|
36
|
+
</span>
|
|
37
|
+
{response.usage.cache_creation_input_tokens !== undefined &&
|
|
38
|
+
response.usage.cache_creation_input_tokens !== null &&
|
|
39
|
+
response.usage.cache_creation_input_tokens > 0 && (
|
|
40
|
+
<span className="font-mono tabular-nums text-emerald-400">
|
|
41
|
+
Cache +{formatTokens(response.usage.cache_creation_input_tokens)}
|
|
42
|
+
</span>
|
|
43
|
+
)}
|
|
44
|
+
{response.usage.cache_read_input_tokens !== undefined &&
|
|
45
|
+
response.usage.cache_read_input_tokens !== null &&
|
|
46
|
+
response.usage.cache_read_input_tokens > 0 && (
|
|
47
|
+
<span className="font-mono tabular-nums text-purple-400">
|
|
48
|
+
Cache ~{formatTokens(response.usage.cache_read_input_tokens)}
|
|
49
|
+
</span>
|
|
50
|
+
)}
|
|
51
|
+
</span>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<Separator className="opacity-50" />
|
|
55
|
+
|
|
56
|
+
<div className="space-y-2">
|
|
57
|
+
{response.content.map((block, i) => (
|
|
58
|
+
<ResponseContentBlockRenderer key={i} block={block} />
|
|
59
|
+
))}
|
|
60
|
+
{response.content.length === 0 && (
|
|
61
|
+
<p className="text-xs text-muted-foreground italic">Empty response content</p>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Regex to extract content wrapped in <think>...</thinking> tags (MiniMax format: <think>...</think>)
|
|
2
|
+
const THINKING_TAG_REGEX = /<think>([\s\S]*?)<\/think>/gi;
|
|
3
|
+
const THINKING_TAG_REGEX_SINGLE = /<think>([\s\S]*?)<\/think>/i;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Extract thinking content wrapped in <think> tags (thought balloon emoji) from text.
|
|
7
|
+
* Returns { thinking, remainingText } where thinking is the extracted content
|
|
8
|
+
* and remainingText is the original text with thinking tags removed.
|
|
9
|
+
*/
|
|
10
|
+
export function extractThinkingFromContent(text: string): {
|
|
11
|
+
thinking: string | null;
|
|
12
|
+
remainingText: string;
|
|
13
|
+
} {
|
|
14
|
+
const match = THINKING_TAG_REGEX_SINGLE.exec(text);
|
|
15
|
+
if (!match || match[1] === undefined) {
|
|
16
|
+
return { thinking: null, remainingText: text };
|
|
17
|
+
}
|
|
18
|
+
const thinking = match[1].trim();
|
|
19
|
+
const remainingText = text.replace(THINKING_TAG_REGEX, "").trim();
|
|
20
|
+
return { thinking, remainingText };
|
|
21
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { JSX } from "react";
|
|
2
|
+
import type { InspectorResponse, OpenAIResponse, RequestFormat } from "../../../proxy/schemas";
|
|
3
|
+
import { StructuredResponseViewAnthropic } from "./anthropic/ResponseView";
|
|
4
|
+
import { OpenAIResponseView } from "./openai/ResponseView";
|
|
5
|
+
|
|
6
|
+
export { StructuredResponseViewAnthropic } from "./anthropic/ResponseView";
|
|
7
|
+
export { OpenAIResponseView } from "./openai/ResponseView";
|
|
8
|
+
export { ResponseContentBlockRenderer } from "./anthropic/ContentBlocks";
|
|
9
|
+
|
|
10
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
11
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isOpenAIResponse(response: unknown): response is OpenAIResponse {
|
|
15
|
+
return isRecord(response) && response.object === "chat.completion";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function isAnthropicResponse(response: unknown): response is InspectorResponse {
|
|
19
|
+
return isRecord(response) && response.type === "message" && Array.isArray(response.content);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function formatViewFor(
|
|
23
|
+
apiFormat: RequestFormat | undefined,
|
|
24
|
+
response: unknown,
|
|
25
|
+
): JSX.Element | null {
|
|
26
|
+
if (apiFormat === "openai" && isOpenAIResponse(response)) {
|
|
27
|
+
return <OpenAIResponseView response={response} />;
|
|
28
|
+
}
|
|
29
|
+
if (apiFormat === "anthropic" && isAnthropicResponse(response)) {
|
|
30
|
+
return <StructuredResponseViewAnthropic response={response} />;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { StopCircle, Terminal, Zap } from "lucide-react";
|
|
2
|
+
import { memo, useState, type JSX } from "react";
|
|
3
|
+
import ReactMarkdown from "react-markdown";
|
|
4
|
+
import type { OpenAIResponse, OpenAIToolCall } from "../../../../proxy/schemas";
|
|
5
|
+
import { formatTokens } from "../../../../lib/utils";
|
|
6
|
+
import { Badge } from "../../../ui/badge";
|
|
7
|
+
import { JsonViewer } from "../../../ui/json-viewer";
|
|
8
|
+
import { safeJsonValue } from "../../../ui/json-viewer-bulk";
|
|
9
|
+
import { ScrollArea } from "../../../ui/scroll-area";
|
|
10
|
+
import { Separator } from "../../../ui/separator";
|
|
11
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../../ui/collapsible";
|
|
12
|
+
import { ChevronDown, ChevronRight } from "lucide-react";
|
|
13
|
+
import { ThinkingBlock } from "../anthropic/ContentBlocks";
|
|
14
|
+
import { extractThinkingFromContent } from "../anthropic/thinkingExtract";
|
|
15
|
+
|
|
16
|
+
// Re-export for use in other components
|
|
17
|
+
export { extractThinkingFromContent } from "../anthropic/thinkingExtract";
|
|
18
|
+
|
|
19
|
+
/** Best-effort JSON parse of an OpenAI `function.arguments` string. Returns
|
|
20
|
+
* the parsed object, or `null` on parse failure so the renderer can fall
|
|
21
|
+
* back to showing the raw string. */
|
|
22
|
+
function parseToolArguments(raw: string | undefined): unknown {
|
|
23
|
+
if (raw === undefined || raw === "") return {};
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(raw);
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** One collapsible tool_use row, mirroring the Anthropic ToolUseBlock visual
|
|
32
|
+
* treatment (Terminal icon, blue accent, name as a Badge, JSON input in a
|
|
33
|
+
* scrollable JsonViewer). */
|
|
34
|
+
function OpenAIToolCallBlock({ call }: { call: OpenAIToolCall }): JSX.Element {
|
|
35
|
+
const [open, setOpen] = useState(false);
|
|
36
|
+
const name = call.function.name ?? "(unnamed tool)";
|
|
37
|
+
const parsed = parseToolArguments(call.function.arguments);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Collapsible open={open} onOpenChange={setOpen}>
|
|
41
|
+
<div className="border-l-2 border-blue-500/40 my-1">
|
|
42
|
+
<CollapsibleTrigger className="flex items-center gap-1.5 px-3 py-1 w-full text-left cursor-pointer hover:bg-blue-500/5 transition-colors rounded-r-sm group">
|
|
43
|
+
<Terminal className="size-3.5 text-blue-400 shrink-0" />
|
|
44
|
+
<Badge variant="outline" className="text-[10px] font-mono px-1.5 py-0 h-4">
|
|
45
|
+
{name}
|
|
46
|
+
</Badge>
|
|
47
|
+
{call.id !== undefined && call.id !== "" && (
|
|
48
|
+
<span className="text-[10px] font-mono text-muted-foreground/60 truncate">
|
|
49
|
+
{call.id}
|
|
50
|
+
</span>
|
|
51
|
+
)}
|
|
52
|
+
<span className="flex-1" />
|
|
53
|
+
{open ? (
|
|
54
|
+
<ChevronDown className="size-3 text-muted-foreground" />
|
|
55
|
+
) : (
|
|
56
|
+
<ChevronRight className="size-3 text-muted-foreground" />
|
|
57
|
+
)}
|
|
58
|
+
</CollapsibleTrigger>
|
|
59
|
+
<CollapsibleContent>
|
|
60
|
+
<div className="px-3 pb-2">
|
|
61
|
+
<ScrollArea className="max-h-[60vh]">
|
|
62
|
+
{parsed === null ? (
|
|
63
|
+
// JSON.parse failed — show the raw string so the user can
|
|
64
|
+
// still see what the model tried to call.
|
|
65
|
+
<pre className="font-mono text-xs whitespace-pre-wrap break-words text-rose-300/90">
|
|
66
|
+
{call.function.arguments}
|
|
67
|
+
</pre>
|
|
68
|
+
) : (
|
|
69
|
+
<JsonViewer data={safeJsonValue(parsed)} defaultExpandDepth={0} />
|
|
70
|
+
)}
|
|
71
|
+
</ScrollArea>
|
|
72
|
+
</div>
|
|
73
|
+
</CollapsibleContent>
|
|
74
|
+
</div>
|
|
75
|
+
</Collapsible>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const OpenAIResponseView = memo(function OpenAIResponseView({
|
|
80
|
+
response,
|
|
81
|
+
}: {
|
|
82
|
+
response: OpenAIResponse;
|
|
83
|
+
}): JSX.Element {
|
|
84
|
+
const choice = response.choices[0];
|
|
85
|
+
const message = choice?.message;
|
|
86
|
+
const toolCalls = message?.tool_calls ?? [];
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className="space-y-3">
|
|
90
|
+
<div className="flex items-center gap-2 flex-wrap">
|
|
91
|
+
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 h-5 font-mono">
|
|
92
|
+
{response.model}
|
|
93
|
+
</Badge>
|
|
94
|
+
|
|
95
|
+
{choice?.finish_reason !== null && choice?.finish_reason !== undefined && (
|
|
96
|
+
<Badge
|
|
97
|
+
variant="outline"
|
|
98
|
+
className="text-[10px] px-1.5 py-0 h-5 font-mono flex items-center gap-1"
|
|
99
|
+
>
|
|
100
|
+
<StopCircle className="size-2.5" />
|
|
101
|
+
{choice.finish_reason}
|
|
102
|
+
</Badge>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
<span className="flex items-center gap-1 text-muted-foreground text-xs">
|
|
106
|
+
<Zap className="size-3" />
|
|
107
|
+
<span className="font-mono tabular-nums">
|
|
108
|
+
{formatTokens(response.usage.prompt_tokens ?? 0)} in /{" "}
|
|
109
|
+
{formatTokens(response.usage.completion_tokens ?? 0)} out
|
|
110
|
+
</span>
|
|
111
|
+
</span>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<Separator className="opacity-50" />
|
|
115
|
+
|
|
116
|
+
<div className="space-y-2">
|
|
117
|
+
{message?.reasoning_content !== null &&
|
|
118
|
+
message?.reasoning_content !== undefined &&
|
|
119
|
+
message.reasoning_content.length > 0 && (
|
|
120
|
+
<ThinkingBlock thinking={message.reasoning_content} />
|
|
121
|
+
)}
|
|
122
|
+
{message?.content !== null &&
|
|
123
|
+
message?.content !== undefined &&
|
|
124
|
+
message.content.length > 0 &&
|
|
125
|
+
(() => {
|
|
126
|
+
// Extract thinking content from tag-wrapped content if no reasoning_content field
|
|
127
|
+
const hasReasoningField =
|
|
128
|
+
message.reasoning_content !== null &&
|
|
129
|
+
message.reasoning_content !== undefined &&
|
|
130
|
+
message.reasoning_content.length > 0;
|
|
131
|
+
const { thinking, remainingText } = extractThinkingFromContent(message.content);
|
|
132
|
+
return (
|
|
133
|
+
<div className="space-y-2">
|
|
134
|
+
{/* Show thinking from tags only if no reasoning_content field */}
|
|
135
|
+
{thinking !== null && !hasReasoningField && <ThinkingBlock thinking={thinking} />}
|
|
136
|
+
{remainingText.length > 0 && (
|
|
137
|
+
<div className="prose prose-sm dark:prose-invert max-w-none [&_pre]:bg-muted [&_pre]:text-foreground [&_code]:text-[0.8em] [&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1">
|
|
138
|
+
<ReactMarkdown>{remainingText}</ReactMarkdown>
|
|
139
|
+
</div>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
})()}
|
|
144
|
+
{toolCalls.map((call, i) => (
|
|
145
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: tool_calls is the positionally stable list from the response
|
|
146
|
+
<OpenAIToolCallBlock key={call.id ?? `tc-${i}`} call={call} />
|
|
147
|
+
))}
|
|
148
|
+
{message?.function_call !== null && message?.function_call !== undefined && (
|
|
149
|
+
<div className="border border-blue-500/30 rounded-md p-3 bg-blue-500/5">
|
|
150
|
+
<div className="text-xs text-blue-400 font-mono mb-1">function_call</div>
|
|
151
|
+
<div className="font-mono text-xs">
|
|
152
|
+
<span className="text-blue-300">{message.function_call.name}</span>
|
|
153
|
+
<span className="text-muted-foreground">({message.function_call.arguments})</span>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
)}
|
|
157
|
+
{(message?.content === null ||
|
|
158
|
+
message?.content === undefined ||
|
|
159
|
+
message.content.length === 0) &&
|
|
160
|
+
(message?.reasoning_content === null ||
|
|
161
|
+
message?.reasoning_content === undefined ||
|
|
162
|
+
message.reasoning_content.length === 0) &&
|
|
163
|
+
(message?.function_call === null || message?.function_call === undefined) &&
|
|
164
|
+
toolCalls.length === 0 && (
|
|
165
|
+
<p className="text-xs text-muted-foreground italic">Empty response content</p>
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { ConversationGroup } from "./ConversationGroup";
|
|
2
|
+
export {
|
|
3
|
+
ConversationHeader,
|
|
4
|
+
getConversationId,
|
|
5
|
+
groupLogsByConversation,
|
|
6
|
+
} from "./ConversationHeader";
|
|
7
|
+
export type { ConversationGroupData, ViewMode } from "./ConversationHeader";
|
|
8
|
+
export { LogEntry } from "./LogEntry";
|
|
9
|
+
export { ThreadConnector } from "./ThreadConnector";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { lazy } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lazy-loaded heavy leaf components. Each is split into its own chunk by Vite
|
|
5
|
+
* (named after the source file) so the main bundle stays small. Imports use
|
|
6
|
+
* the `.then(m => ({ default: m.X }))` adapter so the original modules can
|
|
7
|
+
* keep their named exports without needing a default export.
|
|
8
|
+
*/
|
|
9
|
+
export const LazyCompareDrawer = lazy(() =>
|
|
10
|
+
import("./CompareDrawer").then((m) => ({ default: m.CompareDrawer })),
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export const LazyReplayDialog = lazy(() =>
|
|
14
|
+
import("./ReplayDialog").then((m) => ({ default: m.ReplayDialog })),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export const LazyRequestAnatomy = lazy(() =>
|
|
18
|
+
import("./anatomy/RequestAnatomy").then((m) => ({ default: m.RequestAnatomy })),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export const LazyResponseView = lazy(() =>
|
|
22
|
+
import("./ResponseView").then((m) => ({ default: m.ResponseView })),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const LazyStreamingChunkSequence = lazy(() =>
|
|
26
|
+
import("./StreamingChunkSequence").then((m) => ({
|
|
27
|
+
default: m.StreamingChunkSequence,
|
|
28
|
+
})),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export const LazyJsonViewer = lazy(() =>
|
|
32
|
+
import("../ui/json-viewer").then((m) => ({ default: m.JsonViewer })),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export const LazyJsonViewerFromString = lazy(() =>
|
|
36
|
+
import("../ui/json-viewer").then((m) => ({ default: m.JsonViewerFromString })),
|
|
37
|
+
);
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AnthropicRequestSchema,
|
|
3
|
+
AnthropicResponseSchema,
|
|
4
|
+
} from "../../../proxy/formats/anthropic/schemas";
|
|
5
|
+
import type { AnatomySegment } from "../anatomy/types";
|
|
6
|
+
import { countCharacters, estimateTokens } from "../anatomy/tokenEstimate";
|
|
7
|
+
import type { LogFormatAdapter } from "./types";
|
|
8
|
+
import { emptyRequestAnalysis, EMPTY_RESPONSE_ANALYSIS } from "./types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Flatten an Anthropic `content` field to the text used for token
|
|
12
|
+
* estimation. The function never throws: unknown content shapes are
|
|
13
|
+
* treated as empty. We deliberately do NOT use JSON.stringify on the
|
|
14
|
+
* content because base64 image data would dominate the size.
|
|
15
|
+
*/
|
|
16
|
+
function contentToText(content: unknown): string {
|
|
17
|
+
if (typeof content === "string") return content;
|
|
18
|
+
if (!Array.isArray(content)) return "";
|
|
19
|
+
const parts: string[] = [];
|
|
20
|
+
for (const block of content) {
|
|
21
|
+
if (block === null || typeof block !== "object") continue;
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
23
|
+
const b = block as Record<string, unknown>;
|
|
24
|
+
const type = b.type;
|
|
25
|
+
switch (type) {
|
|
26
|
+
case "text":
|
|
27
|
+
if (typeof b.text === "string") parts.push(b.text);
|
|
28
|
+
break;
|
|
29
|
+
case "thinking":
|
|
30
|
+
case "think":
|
|
31
|
+
if (typeof b.thinking === "string") parts.push(b.thinking);
|
|
32
|
+
break;
|
|
33
|
+
case "tool_use": {
|
|
34
|
+
const name = typeof b.name === "string" ? b.name : "";
|
|
35
|
+
const input = b.input !== undefined ? JSON.stringify(b.input) : "";
|
|
36
|
+
parts.push(`${name} ${input}`.trim());
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
case "tool_result": {
|
|
40
|
+
const inner = b.content;
|
|
41
|
+
if (typeof inner === "string") {
|
|
42
|
+
parts.push(inner);
|
|
43
|
+
} else if (Array.isArray(inner)) {
|
|
44
|
+
for (const item of inner) {
|
|
45
|
+
if (item !== null && typeof item === "object") {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
47
|
+
const t = (item as Record<string, unknown>).text;
|
|
48
|
+
if (typeof t === "string") parts.push(t);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case "image":
|
|
55
|
+
// Skip base64 payload but record the image type so the segment is not empty.
|
|
56
|
+
parts.push("[image]");
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return parts.join("\n");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Flatten an Anthropic `system` value to its text. The API accepts
|
|
64
|
+
* either a string or an array of text blocks. */
|
|
65
|
+
function systemToText(system: unknown): string {
|
|
66
|
+
if (typeof system === "string") return system;
|
|
67
|
+
if (!Array.isArray(system)) return "";
|
|
68
|
+
const parts: string[] = [];
|
|
69
|
+
for (const block of system) {
|
|
70
|
+
if (block !== null && typeof block === "object") {
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
72
|
+
const t = (block as { type?: unknown; text?: unknown }).text;
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
74
|
+
const type = (block as { type?: unknown }).type;
|
|
75
|
+
if (type === "text" && typeof t === "string") parts.push(t);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return parts.join("\n");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Flatten Anthropic `tools` array to its schema text. */
|
|
82
|
+
function toolsToText(tools: unknown): string {
|
|
83
|
+
if (!Array.isArray(tools)) return "";
|
|
84
|
+
const parts: string[] = [];
|
|
85
|
+
for (const tool of tools) {
|
|
86
|
+
if (tool === null || typeof tool !== "object") continue;
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
88
|
+
const t = tool as { name?: unknown; description?: unknown; input_schema?: unknown };
|
|
89
|
+
if (typeof t.name === "string") parts.push(t.name);
|
|
90
|
+
if (typeof t.description === "string") parts.push(t.description);
|
|
91
|
+
if (t.input_schema !== undefined) parts.push(JSON.stringify(t.input_schema));
|
|
92
|
+
}
|
|
93
|
+
return parts.join("\n");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Build one segment from a piece of text + a stable label and path. */
|
|
97
|
+
function segment(
|
|
98
|
+
role: AnatomySegment["role"],
|
|
99
|
+
label: string,
|
|
100
|
+
text: string,
|
|
101
|
+
path: string,
|
|
102
|
+
): AnatomySegment {
|
|
103
|
+
return {
|
|
104
|
+
role,
|
|
105
|
+
label,
|
|
106
|
+
text,
|
|
107
|
+
size: estimateTokens(text),
|
|
108
|
+
characters: countCharacters(text),
|
|
109
|
+
path,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const anthropicLogFormatAdapter: LogFormatAdapter = {
|
|
114
|
+
format: "anthropic",
|
|
115
|
+
|
|
116
|
+
analyzeRequest(rawBody) {
|
|
117
|
+
if (rawBody === null) return emptyRequestAnalysis(rawBody);
|
|
118
|
+
try {
|
|
119
|
+
const result = AnthropicRequestSchema.safeParse(JSON.parse(rawBody));
|
|
120
|
+
if (!result.success) return emptyRequestAnalysis(rawBody);
|
|
121
|
+
return {
|
|
122
|
+
parsed: result.data,
|
|
123
|
+
comparisonValue: result.data,
|
|
124
|
+
messageCount: result.data.messages.length,
|
|
125
|
+
toolCount:
|
|
126
|
+
result.data.tools !== undefined && result.data.tools.length > 0
|
|
127
|
+
? result.data.tools.length
|
|
128
|
+
: null,
|
|
129
|
+
};
|
|
130
|
+
} catch {
|
|
131
|
+
return emptyRequestAnalysis(rawBody);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
analyzeResponse(responseText) {
|
|
136
|
+
if (responseText === null) return EMPTY_RESPONSE_ANALYSIS;
|
|
137
|
+
try {
|
|
138
|
+
let json: unknown = JSON.parse(responseText);
|
|
139
|
+
if (typeof json === "string") json = JSON.parse(json);
|
|
140
|
+
const result = AnthropicResponseSchema.safeParse(json);
|
|
141
|
+
if (!result.success) return EMPTY_RESPONSE_ANALYSIS;
|
|
142
|
+
const toolNames = result.data.content
|
|
143
|
+
.filter((block) => block.type === "tool_use")
|
|
144
|
+
.map((block) => block.name);
|
|
145
|
+
return {
|
|
146
|
+
parsed: result.data,
|
|
147
|
+
toolNames: toolNames.length > 0 ? toolNames : null,
|
|
148
|
+
};
|
|
149
|
+
} catch {
|
|
150
|
+
return EMPTY_RESPONSE_ANALYSIS;
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
anatomySegments(parsed) {
|
|
155
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
156
|
+
// We deliberately skip AnthropicRequestSchema validation here:
|
|
157
|
+
// real Anthropic requests accept `system` as either a string or
|
|
158
|
+
// an array, and we want the Anatomy view to render even when the
|
|
159
|
+
// body shape is slightly off-schema (e.g. system: "string" vs
|
|
160
|
+
// system: [{type:"text",text:"..."}]).
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
162
|
+
const body = parsed as {
|
|
163
|
+
system?: unknown;
|
|
164
|
+
messages?: unknown;
|
|
165
|
+
tools?: unknown;
|
|
166
|
+
};
|
|
167
|
+
const segments: AnatomySegment[] = [];
|
|
168
|
+
|
|
169
|
+
if (body.system !== undefined) {
|
|
170
|
+
const text = systemToText(body.system);
|
|
171
|
+
if (text.length > 0) {
|
|
172
|
+
segments.push(segment("system", "system", text, "/system"));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (Array.isArray(body.messages)) {
|
|
177
|
+
body.messages.forEach((message, index) => {
|
|
178
|
+
if (message === null || typeof message !== "object") return;
|
|
179
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
180
|
+
const m = message as { role?: unknown; content?: unknown };
|
|
181
|
+
const role = m.role === "user" || m.role === "assistant" ? m.role : "user";
|
|
182
|
+
const text = contentToText(m.content);
|
|
183
|
+
segments.push(segment(role, `[${index}] ${role}`, text, `/messages/${index}`));
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (Array.isArray(body.tools) && body.tools.length > 0) {
|
|
188
|
+
const text = toolsToText(body.tools);
|
|
189
|
+
segments.push(segment("tools", "tools", text, "/tools"));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return segments.length > 0 ? segments : null;
|
|
193
|
+
},
|
|
194
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { requestFormatForPath } from "../../../proxy/formats";
|
|
2
|
+
import type { CapturedLog, RequestFormat } from "../../../proxy/schemas";
|
|
3
|
+
import { anthropicLogFormatAdapter } from "./anthropic";
|
|
4
|
+
import { openAILogFormatAdapter } from "./openai";
|
|
5
|
+
import type { LogFormatAdapter } from "./types";
|
|
6
|
+
import { unknownLogFormatAdapter } from "./unknown";
|
|
7
|
+
|
|
8
|
+
const ADAPTERS: Record<RequestFormat, LogFormatAdapter> = {
|
|
9
|
+
anthropic: anthropicLogFormatAdapter,
|
|
10
|
+
openai: openAILogFormatAdapter,
|
|
11
|
+
unknown: unknownLogFormatAdapter,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function getLogFormatAdapter(format: RequestFormat): LogFormatAdapter {
|
|
15
|
+
return ADAPTERS[format];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveLogFormat(log: Pick<CapturedLog, "path" | "apiFormat">): RequestFormat {
|
|
19
|
+
const pathFormat = requestFormatForPath(log.path);
|
|
20
|
+
return pathFormat === "unknown" ? log.apiFormat : pathFormat;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type { LogFormatAdapter, RequestAnalysis, ResponseAnalysis } from "./types";
|