@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,136 @@
|
|
|
1
|
+
import { type JSX, useMemo } from "react";
|
|
2
|
+
import { cn } from "../../lib/utils";
|
|
3
|
+
import type { StopReason } from "../../lib/stopReason";
|
|
4
|
+
import { getCrabVariant, getInteriorCrabVariant } from "../ui/crab-variants";
|
|
5
|
+
|
|
6
|
+
export type ThreadConnectorProps = {
|
|
7
|
+
stopReason: StopReason;
|
|
8
|
+
isPending: boolean;
|
|
9
|
+
isFirst: boolean;
|
|
10
|
+
/** True when this entry starts a new turn (first overall, or after end_turn/stop). */
|
|
11
|
+
isTurnStart: boolean;
|
|
12
|
+
/** True when this entry is the turn's only request. */
|
|
13
|
+
isOnlyEntry?: boolean;
|
|
14
|
+
/** Seed for crab variant selection (0-11). */
|
|
15
|
+
crabIndex?: number;
|
|
16
|
+
/** When true the crab is clickable (collapse / expand a turn). */
|
|
17
|
+
collapsible?: boolean;
|
|
18
|
+
onToggle?: () => void;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Vertical timeline connector. Top spacer uses a calc() that
|
|
23
|
+
* mixes rem (py-1 + half-line) and px (border) so the crab
|
|
24
|
+
* centre-line matches the LogEntry `#id` index regardless of the
|
|
25
|
+
* root font-size. The bottom spacer is flex-1 so the outgoing
|
|
26
|
+
* line adapts to whatever height the LogEntry occupies.
|
|
27
|
+
*/
|
|
28
|
+
export function ThreadConnector({
|
|
29
|
+
stopReason,
|
|
30
|
+
isPending,
|
|
31
|
+
isFirst,
|
|
32
|
+
isTurnStart,
|
|
33
|
+
isOnlyEntry = false,
|
|
34
|
+
crabIndex = 0,
|
|
35
|
+
collapsible = false,
|
|
36
|
+
onToggle,
|
|
37
|
+
}: ThreadConnectorProps): JSX.Element {
|
|
38
|
+
const isBoundary = stopReason === "end_turn" || stopReason === "stop";
|
|
39
|
+
const isFusedBoundary = isOnlyEntry && isTurnStart && isBoundary;
|
|
40
|
+
const isRunning = isPending && !isBoundary;
|
|
41
|
+
const Crab = useMemo(() => getCrabVariant(crabIndex), [crabIndex]);
|
|
42
|
+
const FusedCrab = useMemo(() => getInteriorCrabVariant(crabIndex), [crabIndex]);
|
|
43
|
+
|
|
44
|
+
const interactiveProps =
|
|
45
|
+
collapsible && onToggle
|
|
46
|
+
? ({
|
|
47
|
+
role: "button" as const,
|
|
48
|
+
tabIndex: 0,
|
|
49
|
+
className: "cursor-pointer",
|
|
50
|
+
onClick: (e: React.MouseEvent) => {
|
|
51
|
+
e.stopPropagation();
|
|
52
|
+
onToggle();
|
|
53
|
+
},
|
|
54
|
+
onKeyDown: (e: React.KeyboardEvent) => {
|
|
55
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
56
|
+
e.preventDefault();
|
|
57
|
+
onToggle();
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
} as const)
|
|
61
|
+
: ({} as const);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="flex flex-col items-center w-6 shrink-0 pt-0.5 pb-0.5">
|
|
65
|
+
{/* Top spacer — crab centre must land on the LogEntry index midline.
|
|
66
|
+
* Index midline from container top = border(1px) + py-1(0.25rem) + ½line(0.5rem)
|
|
67
|
+
* = 1px + 0.75rem. Crab centre = pt(2px) + spacer + 7px(half crab).
|
|
68
|
+
* ∴ spacer = 0.75rem - 8px. (at 16px root: 12-8=4px; 2+4+7=13px). */}
|
|
69
|
+
<div className="flex justify-center h-[calc(0.75rem-8px)]">
|
|
70
|
+
{!isFirst && <div className="w-0.5 bg-muted-foreground/30 h-full" />}
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
{/* Crab — pinned to the fixed offset above so it never drifts
|
|
74
|
+
* when the LogEntry expands / collapses. Clickable when the
|
|
75
|
+
* turn is collapsible (replaces the old chevron toggle). */}
|
|
76
|
+
{isFusedBoundary ? (
|
|
77
|
+
<span title="Start and end of turn">
|
|
78
|
+
<FusedCrab
|
|
79
|
+
className={cn(
|
|
80
|
+
"size-3.5 text-[#80FF00]",
|
|
81
|
+
"animate-crab-settle",
|
|
82
|
+
"drop-shadow-[0_0_4px_rgba(128,255,0,0.5)]",
|
|
83
|
+
)}
|
|
84
|
+
/>
|
|
85
|
+
</span>
|
|
86
|
+
) : isBoundary ? (
|
|
87
|
+
<span
|
|
88
|
+
title={stopReason === "end_turn" ? "End of Turn (Anthropic)" : "End of Turn (OpenAI)"}
|
|
89
|
+
{...interactiveProps}
|
|
90
|
+
>
|
|
91
|
+
<Crab
|
|
92
|
+
className={cn(
|
|
93
|
+
"size-3.5 text-amber-400",
|
|
94
|
+
"animate-crab-settle",
|
|
95
|
+
"drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]",
|
|
96
|
+
)}
|
|
97
|
+
/>
|
|
98
|
+
</span>
|
|
99
|
+
) : isTurnStart ? (
|
|
100
|
+
<span title="Start of turn" {...interactiveProps}>
|
|
101
|
+
<Crab
|
|
102
|
+
className={cn(
|
|
103
|
+
"size-3.5 text-emerald-400",
|
|
104
|
+
"animate-crab-appear",
|
|
105
|
+
"drop-shadow-[0_0_4px_rgba(52,211,153,0.5)]",
|
|
106
|
+
)}
|
|
107
|
+
/>
|
|
108
|
+
</span>
|
|
109
|
+
) : isRunning ? (
|
|
110
|
+
<span title="Processing…">
|
|
111
|
+
<Crab className={cn("size-3.5 text-amber-300/80", "animate-crab-crawl")} />
|
|
112
|
+
</span>
|
|
113
|
+
) : (
|
|
114
|
+
<span>
|
|
115
|
+
<Crab className="size-3.5 text-muted-foreground/40" />
|
|
116
|
+
</span>
|
|
117
|
+
)}
|
|
118
|
+
|
|
119
|
+
{/* Bottom spacer — flex-1 fills whatever height the LogEntry
|
|
120
|
+
* consumes (header + expanded content). Turn boundaries have
|
|
121
|
+
* no outgoing line. */}
|
|
122
|
+
<div className="flex-1 flex justify-center min-h-0">
|
|
123
|
+
{!isBoundary && (
|
|
124
|
+
<div
|
|
125
|
+
className={cn(
|
|
126
|
+
"w-0.5 h-full",
|
|
127
|
+
isPending
|
|
128
|
+
? "border-dashed bg-transparent border-l-2 border-muted-foreground/20"
|
|
129
|
+
: "bg-muted-foreground/30",
|
|
130
|
+
)}
|
|
131
|
+
/>
|
|
132
|
+
)}
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { type JSX, memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { AlertTriangle, ChevronRight, Clock, Zap } from "lucide-react";
|
|
3
|
+
import { isTurnBoundary } from "../../lib/stopReason";
|
|
4
|
+
import { cn, formatTokens } from "../../lib/utils";
|
|
5
|
+
import type { CapturedLog } from "../../proxy/schemas";
|
|
6
|
+
import { getCrabVariant } from "../ui/crab-variants";
|
|
7
|
+
import { ProviderLogo, detectProvider, type Provider } from "../providers/ProviderLogo";
|
|
8
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip";
|
|
9
|
+
import type { CacheTrendEntry } from "./cacheTrend";
|
|
10
|
+
import { LogEntry } from "./LogEntry";
|
|
11
|
+
import { ThreadConnector } from "./ThreadConnector";
|
|
12
|
+
import { isTurnCollapsible, type TurnEntry } from "./viewerState";
|
|
13
|
+
|
|
14
|
+
function formatElapsed(ms: number): string {
|
|
15
|
+
if (ms < 1000) return `${ms}ms`;
|
|
16
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type TurnGroupProps = {
|
|
20
|
+
entries: TurnEntry[];
|
|
21
|
+
viewMode: "simple" | "full";
|
|
22
|
+
strip: boolean;
|
|
23
|
+
slowResponseThresholdSeconds: number;
|
|
24
|
+
cacheTrends?: Map<number, CacheTrendEntry>;
|
|
25
|
+
onCompareWithPrevious: (log: CapturedLog) => void;
|
|
26
|
+
comparisonPredecessors: Map<number, CapturedLog>;
|
|
27
|
+
turnIndex?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const TurnGroup = memo(function TurnGroup({
|
|
31
|
+
entries,
|
|
32
|
+
viewMode,
|
|
33
|
+
strip,
|
|
34
|
+
slowResponseThresholdSeconds,
|
|
35
|
+
cacheTrends,
|
|
36
|
+
onCompareWithPrevious,
|
|
37
|
+
comparisonPredecessors,
|
|
38
|
+
turnIndex = 0,
|
|
39
|
+
}: TurnGroupProps): JSX.Element {
|
|
40
|
+
const lastIdx = entries.length - 1;
|
|
41
|
+
const lastStop = entries[lastIdx]?.stopReason ?? null;
|
|
42
|
+
const isComplete = lastStop !== null ? isTurnBoundary(lastStop) : false;
|
|
43
|
+
const isPending = entries[lastIdx]?.log.responseStatus === null;
|
|
44
|
+
const isSingleLog = entries.length === 1;
|
|
45
|
+
const collapsible = isTurnCollapsible(entries.length, isComplete, isPending);
|
|
46
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
47
|
+
|
|
48
|
+
// Auto-collapse when the turn finishes (transitions from incomplete → complete)
|
|
49
|
+
const prevCompleteRef = useRef(false);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (!collapsible) {
|
|
52
|
+
setCollapsed(false);
|
|
53
|
+
} else if (isComplete && !prevCompleteRef.current) {
|
|
54
|
+
setCollapsed(true);
|
|
55
|
+
}
|
|
56
|
+
prevCompleteRef.current = isComplete;
|
|
57
|
+
}, [collapsible, isComplete]);
|
|
58
|
+
|
|
59
|
+
const toggleCollapse = useCallback(() => {
|
|
60
|
+
if (collapsible) setCollapsed((prev) => !prev);
|
|
61
|
+
}, [collapsible]);
|
|
62
|
+
|
|
63
|
+
// Aggregate totals for collapsed summary
|
|
64
|
+
const aggregate = useMemo(() => {
|
|
65
|
+
let totalInput = 0;
|
|
66
|
+
let totalOutput = 0;
|
|
67
|
+
let maxElapsed: number | null = null;
|
|
68
|
+
let hasTokens = false;
|
|
69
|
+
for (const e of entries) {
|
|
70
|
+
if (e.log.inputTokens !== null) {
|
|
71
|
+
totalInput += e.log.inputTokens;
|
|
72
|
+
hasTokens = true;
|
|
73
|
+
}
|
|
74
|
+
if (e.log.outputTokens !== null) {
|
|
75
|
+
totalOutput += e.log.outputTokens;
|
|
76
|
+
hasTokens = true;
|
|
77
|
+
}
|
|
78
|
+
if (e.log.elapsedMs !== null) {
|
|
79
|
+
maxElapsed = maxElapsed === null ? e.log.elapsedMs : Math.max(maxElapsed, e.log.elapsedMs);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
totalInput,
|
|
84
|
+
totalOutput,
|
|
85
|
+
hasTokens,
|
|
86
|
+
maxElapsed,
|
|
87
|
+
};
|
|
88
|
+
}, [entries, lastIdx]);
|
|
89
|
+
|
|
90
|
+
// Unique providers across all entries (for collapsed model logos)
|
|
91
|
+
const uniqueProviders = useMemo(() => {
|
|
92
|
+
const seen = new Set<Provider>();
|
|
93
|
+
for (const e of entries) {
|
|
94
|
+
const p = detectProvider(e.log.model);
|
|
95
|
+
if (p !== "unknown") seen.add(p);
|
|
96
|
+
}
|
|
97
|
+
return [...seen];
|
|
98
|
+
}, [entries]);
|
|
99
|
+
|
|
100
|
+
// Crab variant creators for the dual-crab collapsed layout
|
|
101
|
+
const StartCrab = useMemo(() => getCrabVariant(entries[0]?.log.id ?? 0), [entries]);
|
|
102
|
+
const EndCrab = useMemo(() => getCrabVariant(entries[lastIdx]?.log.id ?? 0), [entries, lastIdx]);
|
|
103
|
+
|
|
104
|
+
const bgClass = turnIndex % 2 === 0 ? "bg-muted/10" : "bg-muted/25";
|
|
105
|
+
const aggregateIsSlow =
|
|
106
|
+
aggregate.maxElapsed !== null &&
|
|
107
|
+
slowResponseThresholdSeconds > 0 &&
|
|
108
|
+
aggregate.maxElapsed > slowResponseThresholdSeconds * 1000;
|
|
109
|
+
|
|
110
|
+
// ResizeObserver → re-render connectors when any LogEntry height changes
|
|
111
|
+
const [layoutVersion, setLayoutVersion] = useState(0);
|
|
112
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const el = containerRef.current;
|
|
115
|
+
if (!el) return;
|
|
116
|
+
let raf = 0;
|
|
117
|
+
const ro = new ResizeObserver(() => {
|
|
118
|
+
window.cancelAnimationFrame(raf);
|
|
119
|
+
raf = window.requestAnimationFrame(() => setLayoutVersion((v) => v + 1));
|
|
120
|
+
});
|
|
121
|
+
ro.observe(el);
|
|
122
|
+
return () => {
|
|
123
|
+
ro.disconnect();
|
|
124
|
+
window.cancelAnimationFrame(raf);
|
|
125
|
+
};
|
|
126
|
+
}, []);
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div
|
|
130
|
+
ref={containerRef}
|
|
131
|
+
className={cn("border rounded-lg", isPending ? "border-amber-500/10" : "border-transparent")}
|
|
132
|
+
>
|
|
133
|
+
{collapsed ? (
|
|
134
|
+
/* ---- Collapsed: dual-crab (+ summary card for multi-log turns) ---- */
|
|
135
|
+
<div
|
|
136
|
+
data-nav-id={`turn-collapsed-${entries[0]?.log.id ?? turnIndex}`}
|
|
137
|
+
data-nav-action="expand"
|
|
138
|
+
role="button"
|
|
139
|
+
tabIndex={0}
|
|
140
|
+
className="flex items-stretch cursor-pointer focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none rounded-lg"
|
|
141
|
+
onClick={toggleCollapse}
|
|
142
|
+
onKeyDown={(e) => {
|
|
143
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
toggleCollapse();
|
|
146
|
+
}
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{/* Turn number */}
|
|
150
|
+
<div className="w-5 shrink-0 flex items-start pt-1.5">
|
|
151
|
+
<span className="text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none">
|
|
152
|
+
{turnIndex + 1}
|
|
153
|
+
</span>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
{/* Dual-crab connector for collapsed multi-request turns. */}
|
|
157
|
+
<div className="w-6 shrink-0 flex flex-col items-center pt-0.5 pb-0.5">
|
|
158
|
+
<div className="flex justify-center h-[calc(0.75rem-8px)]" />
|
|
159
|
+
<span
|
|
160
|
+
role="button"
|
|
161
|
+
tabIndex={0}
|
|
162
|
+
title="Start of turn — click to expand"
|
|
163
|
+
className="cursor-pointer"
|
|
164
|
+
onClick={(e) => {
|
|
165
|
+
e.stopPropagation();
|
|
166
|
+
toggleCollapse();
|
|
167
|
+
}}
|
|
168
|
+
onKeyDown={(e) => {
|
|
169
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
170
|
+
e.preventDefault();
|
|
171
|
+
toggleCollapse();
|
|
172
|
+
}
|
|
173
|
+
}}
|
|
174
|
+
>
|
|
175
|
+
<StartCrab
|
|
176
|
+
className={cn(
|
|
177
|
+
"size-3.5 text-emerald-400",
|
|
178
|
+
"animate-crab-appear drop-shadow-[0_0_4px_rgba(52,211,153,0.5)]",
|
|
179
|
+
)}
|
|
180
|
+
/>
|
|
181
|
+
</span>
|
|
182
|
+
|
|
183
|
+
<div className="flex-1 flex justify-center min-h-0">
|
|
184
|
+
<div className="w-0.5 bg-muted-foreground/30 h-full" />
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
<span
|
|
188
|
+
role="button"
|
|
189
|
+
tabIndex={0}
|
|
190
|
+
title="End of Turn — click to expand"
|
|
191
|
+
className="cursor-pointer"
|
|
192
|
+
onClick={(e) => {
|
|
193
|
+
e.stopPropagation();
|
|
194
|
+
toggleCollapse();
|
|
195
|
+
}}
|
|
196
|
+
onKeyDown={(e) => {
|
|
197
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
198
|
+
e.preventDefault();
|
|
199
|
+
toggleCollapse();
|
|
200
|
+
}
|
|
201
|
+
}}
|
|
202
|
+
>
|
|
203
|
+
<EndCrab
|
|
204
|
+
className={cn(
|
|
205
|
+
"size-3.5 text-amber-400",
|
|
206
|
+
"animate-crab-settle drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]",
|
|
207
|
+
)}
|
|
208
|
+
/>
|
|
209
|
+
</span>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
{/* Summary content — hidden for single-log turns. */}
|
|
213
|
+
{entries.length > 1 && (
|
|
214
|
+
<div
|
|
215
|
+
className={cn(
|
|
216
|
+
"flex-1 min-w-0 mb-0.5 rounded-lg border border-border py-1 px-3 flex items-center gap-3 text-xs",
|
|
217
|
+
bgClass,
|
|
218
|
+
)}
|
|
219
|
+
>
|
|
220
|
+
{/* ID range */}
|
|
221
|
+
<span className="text-blue-400/80 font-mono font-semibold tabular-nums shrink-0">
|
|
222
|
+
#{entries[0]?.log.id ?? "?"} ~ #{entries[lastIdx]?.log.id ?? "?"}
|
|
223
|
+
</span>
|
|
224
|
+
|
|
225
|
+
{/* Request count */}
|
|
226
|
+
<span className="text-muted-foreground shrink-0">
|
|
227
|
+
{entries.length} request{entries.length > 1 ? "s" : ""}
|
|
228
|
+
</span>
|
|
229
|
+
|
|
230
|
+
{/* Model logos — one per unique provider */}
|
|
231
|
+
{uniqueProviders.length > 0 && (
|
|
232
|
+
<span className="flex items-center gap-0.5 shrink-0">
|
|
233
|
+
{uniqueProviders.map((p) => (
|
|
234
|
+
<ProviderLogo key={p} provider={p} className="size-4" />
|
|
235
|
+
))}
|
|
236
|
+
</span>
|
|
237
|
+
)}
|
|
238
|
+
|
|
239
|
+
{/* Elapsed — slowest single request in the turn (not the sum) */}
|
|
240
|
+
{aggregate.maxElapsed !== null && (
|
|
241
|
+
<TooltipProvider>
|
|
242
|
+
<Tooltip>
|
|
243
|
+
<TooltipTrigger asChild>
|
|
244
|
+
<span
|
|
245
|
+
className={cn(
|
|
246
|
+
"flex items-center gap-1 shrink-0",
|
|
247
|
+
aggregateIsSlow ? "text-amber-400" : "text-muted-foreground",
|
|
248
|
+
)}
|
|
249
|
+
>
|
|
250
|
+
<Clock className="size-3" />
|
|
251
|
+
<span className="font-mono tabular-nums">
|
|
252
|
+
{formatElapsed(aggregate.maxElapsed)}
|
|
253
|
+
</span>
|
|
254
|
+
{aggregateIsSlow && (
|
|
255
|
+
<AlertTriangle className="size-3" aria-label="Slow response" />
|
|
256
|
+
)}
|
|
257
|
+
</span>
|
|
258
|
+
</TooltipTrigger>
|
|
259
|
+
<TooltipContent>
|
|
260
|
+
{aggregateIsSlow
|
|
261
|
+
? `Slow response: ${formatElapsed(
|
|
262
|
+
aggregate.maxElapsed,
|
|
263
|
+
)} exceeds ${formatElapsed(slowResponseThresholdSeconds * 1000)}`
|
|
264
|
+
: "Slowest request in this turn"}
|
|
265
|
+
</TooltipContent>
|
|
266
|
+
</Tooltip>
|
|
267
|
+
</TooltipProvider>
|
|
268
|
+
)}
|
|
269
|
+
|
|
270
|
+
{/* Tokens */}
|
|
271
|
+
{aggregate.hasTokens && (
|
|
272
|
+
<span className="flex items-center gap-1 shrink-0">
|
|
273
|
+
<Zap className="size-3 text-muted-foreground" />
|
|
274
|
+
<span className="font-mono tabular-nums">
|
|
275
|
+
<span className="text-blue-400">IN {formatTokens(aggregate.totalInput)}</span>
|
|
276
|
+
{" / "}
|
|
277
|
+
<span className="text-amber-400">
|
|
278
|
+
OUT {formatTokens(aggregate.totalOutput)}
|
|
279
|
+
</span>
|
|
280
|
+
</span>
|
|
281
|
+
</span>
|
|
282
|
+
)}
|
|
283
|
+
|
|
284
|
+
{/* Spacer */}
|
|
285
|
+
<span className="flex-1 min-w-0" />
|
|
286
|
+
|
|
287
|
+
{/* Expand chevron */}
|
|
288
|
+
<ChevronRight className="size-4 text-muted-foreground shrink-0" />
|
|
289
|
+
</div>
|
|
290
|
+
)}
|
|
291
|
+
</div>
|
|
292
|
+
) : (
|
|
293
|
+
/* ---- Expanded: full entries ---- */
|
|
294
|
+
entries.map((entry, visibleIdx) => {
|
|
295
|
+
const { log, stopReason: reason } = entry;
|
|
296
|
+
const isTurnStart = visibleIdx === 0;
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<div key={log.id} className="flex items-stretch">
|
|
300
|
+
{isTurnStart ? (
|
|
301
|
+
<div className="w-5 shrink-0 flex items-start pt-1.5">
|
|
302
|
+
<span className="text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none">
|
|
303
|
+
{turnIndex + 1}
|
|
304
|
+
</span>
|
|
305
|
+
</div>
|
|
306
|
+
) : (
|
|
307
|
+
<div className="w-5 shrink-0" />
|
|
308
|
+
)}
|
|
309
|
+
<ThreadConnector
|
|
310
|
+
stopReason={reason}
|
|
311
|
+
isPending={log.responseStatus === null}
|
|
312
|
+
isFirst={visibleIdx === 0}
|
|
313
|
+
isTurnStart={isTurnStart}
|
|
314
|
+
isOnlyEntry={isSingleLog}
|
|
315
|
+
crabIndex={log.id % 12}
|
|
316
|
+
collapsible={collapsible && isTurnStart}
|
|
317
|
+
onToggle={toggleCollapse}
|
|
318
|
+
/>
|
|
319
|
+
<div className={cn("flex-1 min-w-0 mb-0.5 rounded-lg", bgClass)}>
|
|
320
|
+
<LogEntry
|
|
321
|
+
log={log}
|
|
322
|
+
viewMode={viewMode}
|
|
323
|
+
strip={strip}
|
|
324
|
+
slowResponseThresholdSeconds={slowResponseThresholdSeconds}
|
|
325
|
+
cacheTrend={cacheTrends?.get(log.id) ?? null}
|
|
326
|
+
onCompareWithPrevious={
|
|
327
|
+
comparisonPredecessors.has(log.id) ? onCompareWithPrevious : undefined
|
|
328
|
+
}
|
|
329
|
+
/>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
})
|
|
334
|
+
)}
|
|
335
|
+
</div>
|
|
336
|
+
);
|
|
337
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Info } from "lucide-react";
|
|
2
|
+
import { type JSX, useMemo } from "react";
|
|
3
|
+
import { cn, formatTokens } from "../../../lib/utils";
|
|
4
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../../ui/tooltip";
|
|
5
|
+
import { SegmentBar } from "./SegmentBar";
|
|
6
|
+
import type { AnatomySegment } from "./types";
|
|
7
|
+
|
|
8
|
+
export type RequestAnatomyProps = {
|
|
9
|
+
/** Parsed request body, or `null` if it cannot be parsed. */
|
|
10
|
+
parsed: unknown | null;
|
|
11
|
+
/** Server-reported input token count, or `null` if unknown. */
|
|
12
|
+
inputTokens: number | null;
|
|
13
|
+
/** Optional callback fired when the user activates a segment. */
|
|
14
|
+
onSegmentActivate?: (segment: AnatomySegment) => void;
|
|
15
|
+
/** Pre-computed segments; if provided, `parsed` is ignored. */
|
|
16
|
+
segments?: AnatomySegment[] | null;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const DIVERGENCE_AMBER_THRESHOLD = 0.25;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Render the request anatomy view: a one-line summary plus the
|
|
23
|
+
* stacked segment bar. Returns `null` when the request cannot be
|
|
24
|
+
* parsed or the segment list is empty.
|
|
25
|
+
*/
|
|
26
|
+
export function RequestAnatomy({
|
|
27
|
+
parsed,
|
|
28
|
+
inputTokens,
|
|
29
|
+
onSegmentActivate,
|
|
30
|
+
segments: providedSegments,
|
|
31
|
+
}: RequestAnatomyProps): JSX.Element | null {
|
|
32
|
+
// Caller is expected to pre-compute segments in production (see
|
|
33
|
+
// LogEntry). When called standalone, fall back to deriving from
|
|
34
|
+
// `parsed` directly using the unknown adapter is not appropriate —
|
|
35
|
+
// the caller is responsible for choosing the right adapter.
|
|
36
|
+
const segments = useMemo(() => providedSegments ?? null, [providedSegments]);
|
|
37
|
+
const total = useMemo(() => (segments ?? []).reduce((sum, s) => sum + s.size, 0), [segments]);
|
|
38
|
+
|
|
39
|
+
// Show divergence warning when both estimate and server numbers exist.
|
|
40
|
+
const showDivergenceWarning = useMemo(() => {
|
|
41
|
+
if (segments === null || segments === undefined) return false;
|
|
42
|
+
if (inputTokens === null) return false;
|
|
43
|
+
if (total === 0) return false;
|
|
44
|
+
const ratio = Math.abs(inputTokens - total) / Math.max(inputTokens, total);
|
|
45
|
+
return ratio >= DIVERGENCE_AMBER_THRESHOLD;
|
|
46
|
+
}, [inputTokens, segments, total]);
|
|
47
|
+
|
|
48
|
+
const summaryColorClass = useMemo(() => {
|
|
49
|
+
if (inputTokens === null) return "text-muted-foreground";
|
|
50
|
+
if (showDivergenceWarning) return "text-amber-400";
|
|
51
|
+
return "text-muted-foreground";
|
|
52
|
+
}, [inputTokens, showDivergenceWarning]);
|
|
53
|
+
|
|
54
|
+
if (segments === null || segments === undefined) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (segments.length === 0) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (parsed === null && providedSegments === undefined) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<TooltipProvider delayDuration={150}>
|
|
66
|
+
<div className="px-4 py-3 space-y-2" data-testid="anatomy-root">
|
|
67
|
+
<div className="flex items-center gap-2 text-xs">
|
|
68
|
+
<span className="font-mono tabular-nums text-foreground">
|
|
69
|
+
~{formatTokens(total)} tokens
|
|
70
|
+
</span>
|
|
71
|
+
{inputTokens !== null && (
|
|
72
|
+
<span className={cn("font-mono tabular-nums", summaryColorClass)}>
|
|
73
|
+
(server: {formatTokens(inputTokens)})
|
|
74
|
+
</span>
|
|
75
|
+
)}
|
|
76
|
+
{showDivergenceWarning && (
|
|
77
|
+
<Tooltip>
|
|
78
|
+
<TooltipTrigger asChild>
|
|
79
|
+
<button
|
|
80
|
+
type="button"
|
|
81
|
+
className="inline-flex items-center text-amber-400 hover:text-amber-300"
|
|
82
|
+
aria-label="Token estimate diverges from server"
|
|
83
|
+
>
|
|
84
|
+
<Info className="size-3.5" />
|
|
85
|
+
</button>
|
|
86
|
+
</TooltipTrigger>
|
|
87
|
+
<TooltipContent className="max-w-xs text-xs">
|
|
88
|
+
Bar uses a token estimate heuristic (~4 ASCII chars / token, ~1 CJK / emoji char per
|
|
89
|
+
token). The server's reported value is the source of truth for cost.
|
|
90
|
+
</TooltipContent>
|
|
91
|
+
</Tooltip>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
<SegmentBar segments={segments} totalTokens={total} onActivate={onSegmentActivate} />
|
|
95
|
+
</div>
|
|
96
|
+
</TooltipProvider>
|
|
97
|
+
);
|
|
98
|
+
}
|