@flanksource/clicky-ui 0.3.46 → 0.3.47
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/NOTICE.md +11 -0
- package/dist/ai.cjs +13 -8
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.js +7 -2
- package/dist/ai.js.map +1 -1
- package/dist/components/ErrorWrapper.cjs +2 -3
- package/dist/components/ErrorWrapper.cjs.map +1 -1
- package/dist/components/ErrorWrapper.d.ts.map +1 -1
- package/dist/components/ErrorWrapper.js +3 -4
- package/dist/components/ErrorWrapper.js.map +1 -1
- package/dist/data/Clicky.cjs +3 -0
- package/dist/data/Clicky.cjs.map +1 -1
- package/dist/data/Clicky.js +3 -0
- package/dist/data/Clicky.js.map +1 -1
- package/dist/data/FilterPill.cjs +15 -2
- package/dist/data/FilterPill.cjs.map +1 -1
- package/dist/data/FilterPill.d.ts +1 -1
- package/dist/data/FilterPill.d.ts.map +1 -1
- package/dist/data/FilterPill.js +15 -2
- package/dist/data/FilterPill.js.map +1 -1
- package/dist/data/LogViewer.cjs +27 -8
- package/dist/data/LogViewer.cjs.map +1 -1
- package/dist/data/LogViewer.d.ts +11 -3
- package/dist/data/LogViewer.d.ts.map +1 -1
- package/dist/data/LogViewer.js +27 -8
- package/dist/data/LogViewer.js.map +1 -1
- package/dist/data/Properties.cjs +2 -15
- package/dist/data/Properties.cjs.map +1 -1
- package/dist/data/Properties.d.ts +4 -0
- package/dist/data/Properties.d.ts.map +1 -1
- package/dist/data/Properties.js +2 -15
- package/dist/data/Properties.js.map +1 -1
- package/dist/data/PropertyValue.cjs +37 -0
- package/dist/data/PropertyValue.cjs.map +1 -0
- package/dist/data/PropertyValue.d.ts +6 -0
- package/dist/data/PropertyValue.d.ts.map +1 -0
- package/dist/data/PropertyValue.js +37 -0
- package/dist/data/PropertyValue.js.map +1 -0
- package/dist/data/Tree.cjs +4 -3
- package/dist/data/Tree.cjs.map +1 -1
- package/dist/data/Tree.d.ts +3 -1
- package/dist/data/Tree.d.ts.map +1 -1
- package/dist/data/Tree.js +4 -3
- package/dist/data/Tree.js.map +1 -1
- package/dist/data/ai/ChatFab.cjs +1 -1
- package/dist/data/ai/ChatFab.cjs.map +1 -1
- package/dist/data/ai/ChatFab.js +1 -1
- package/dist/data/ai/ChatFab.js.map +1 -1
- package/dist/data/ai/ChatWindow.cjs +106 -171
- package/dist/data/ai/ChatWindow.cjs.map +1 -1
- package/dist/data/ai/ChatWindow.d.ts +6 -2
- package/dist/data/ai/ChatWindow.d.ts.map +1 -1
- package/dist/data/ai/ChatWindow.header.cjs +104 -0
- package/dist/data/ai/ChatWindow.header.cjs.map +1 -0
- package/dist/data/ai/ChatWindow.header.d.ts +29 -0
- package/dist/data/ai/ChatWindow.header.d.ts.map +1 -0
- package/dist/data/ai/ChatWindow.header.js +104 -0
- package/dist/data/ai/ChatWindow.header.js.map +1 -0
- package/dist/data/ai/ChatWindow.js +107 -172
- package/dist/data/ai/ChatWindow.js.map +1 -1
- package/dist/data/ai/ChatWindow.placement.cjs +95 -0
- package/dist/data/ai/ChatWindow.placement.cjs.map +1 -0
- package/dist/data/ai/ChatWindow.placement.d.ts +32 -0
- package/dist/data/ai/ChatWindow.placement.d.ts.map +1 -0
- package/dist/data/ai/ChatWindow.placement.js +95 -0
- package/dist/data/ai/ChatWindow.placement.js.map +1 -0
- package/dist/data/ai/SessionChatComposer.cjs +3 -1
- package/dist/data/ai/SessionChatComposer.cjs.map +1 -1
- package/dist/data/ai/SessionChatComposer.d.ts +4 -1
- package/dist/data/ai/SessionChatComposer.d.ts.map +1 -1
- package/dist/data/ai/SessionChatComposer.js +3 -1
- package/dist/data/ai/SessionChatComposer.js.map +1 -1
- package/dist/data/ai/SessionInspector.cjs +68 -72
- package/dist/data/ai/SessionInspector.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.composer-slot.cjs +8 -0
- package/dist/data/ai/SessionInspector.composer-slot.cjs.map +1 -0
- package/dist/data/ai/SessionInspector.composer-slot.d.ts +9 -0
- package/dist/data/ai/SessionInspector.composer-slot.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.composer-slot.js +8 -0
- package/dist/data/ai/SessionInspector.composer-slot.js.map +1 -0
- package/dist/data/ai/SessionInspector.d.ts +6 -1
- package/dist/data/ai/SessionInspector.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.js +69 -73
- package/dist/data/ai/SessionInspector.js.map +1 -1
- package/dist/data/ai/SessionInspector.legacy.cjs +41 -42
- package/dist/data/ai/SessionInspector.legacy.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.legacy.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.legacy.js +41 -42
- package/dist/data/ai/SessionInspector.legacy.js.map +1 -1
- package/dist/data/ai/SessionInspector.remote-view.cjs +14 -9
- package/dist/data/ai/SessionInspector.remote-view.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.remote-view.d.ts +6 -2
- package/dist/data/ai/SessionInspector.remote-view.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.remote-view.js +14 -9
- package/dist/data/ai/SessionInspector.remote-view.js.map +1 -1
- package/dist/data/ai/SessionInspector.remote.cjs +84 -16
- package/dist/data/ai/SessionInspector.remote.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.remote.d.ts +42 -7
- package/dist/data/ai/SessionInspector.remote.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.remote.js +86 -18
- package/dist/data/ai/SessionInspector.remote.js.map +1 -1
- package/dist/data/ai/SessionInspector.transcript.cjs +5 -0
- package/dist/data/ai/SessionInspector.transcript.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.transcript.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.transcript.js +5 -0
- package/dist/data/ai/SessionInspector.transcript.js.map +1 -1
- package/dist/data/ai/SessionTail.cjs +63 -0
- package/dist/data/ai/SessionTail.cjs.map +1 -0
- package/dist/data/ai/SessionTail.d.ts +14 -0
- package/dist/data/ai/SessionTail.d.ts.map +1 -0
- package/dist/data/ai/SessionTail.js +63 -0
- package/dist/data/ai/SessionTail.js.map +1 -0
- package/dist/data/ai/SessionViewer.cjs +10 -3
- package/dist/data/ai/SessionViewer.cjs.map +1 -1
- package/dist/data/ai/SessionViewer.d.ts +7 -1
- package/dist/data/ai/SessionViewer.d.ts.map +1 -1
- package/dist/data/ai/SessionViewer.js +10 -3
- package/dist/data/ai/SessionViewer.js.map +1 -1
- package/dist/data/ai/SessionViewer.unified.d.ts +14 -0
- package/dist/data/ai/SessionViewer.unified.d.ts.map +1 -1
- package/dist/data/ai/index.d.ts +4 -3
- package/dist/data/ai/index.d.ts.map +1 -1
- package/dist/data/ai/use-session-scroll.cjs +30 -6
- package/dist/data/ai/use-session-scroll.cjs.map +1 -1
- package/dist/data/ai/use-session-scroll.d.ts +7 -0
- package/dist/data/ai/use-session-scroll.d.ts.map +1 -1
- package/dist/data/ai/use-session-scroll.js +30 -6
- package/dist/data/ai/use-session-scroll.js.map +1 -1
- package/dist/data/chat/Chat.cjs +1 -1
- package/dist/data/chat/Chat.cjs.map +1 -1
- package/dist/data/chat/Chat.js +1 -1
- package/dist/data/chat/Chat.js.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.cjs +4 -4
- package/dist/data/chat/ChatRuntimeToolbar.cjs.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.d.ts.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.js +4 -4
- package/dist/data/chat/ChatRuntimeToolbar.js.map +1 -1
- package/dist/data/chat/PromptInput.cjs +4 -2
- package/dist/data/chat/PromptInput.cjs.map +1 -1
- package/dist/data/chat/PromptInput.d.ts +5 -1
- package/dist/data/chat/PromptInput.d.ts.map +1 -1
- package/dist/data/chat/PromptInput.js +4 -2
- package/dist/data/chat/PromptInput.js.map +1 -1
- package/dist/data/diagnostics/ErrorDetails.cjs +13 -2
- package/dist/data/diagnostics/ErrorDetails.cjs.map +1 -1
- package/dist/data/diagnostics/ErrorDetails.d.ts +3 -1
- package/dist/data/diagnostics/ErrorDetails.d.ts.map +1 -1
- package/dist/data/diagnostics/ErrorDetails.js +14 -3
- package/dist/data/diagnostics/ErrorDetails.js.map +1 -1
- package/dist/data/diagnostics/FrameSourceWindow.cjs +3 -2
- package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -1
- package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -1
- package/dist/data/diagnostics/FrameSourceWindow.js +3 -2
- package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -1
- package/dist/data/diagnostics/error-diagnostics.cjs +5 -0
- package/dist/data/diagnostics/error-diagnostics.cjs.map +1 -1
- package/dist/data/diagnostics/error-diagnostics.d.ts +1 -0
- package/dist/data/diagnostics/error-diagnostics.d.ts.map +1 -1
- package/dist/data/diagnostics/error-diagnostics.js +6 -1
- package/dist/data/diagnostics/error-diagnostics.js.map +1 -1
- package/dist/data/har/HarPanel.cjs +3 -1
- package/dist/data/har/HarPanel.cjs.map +1 -1
- package/dist/data/har/HarPanel.d.ts +4 -1
- package/dist/data/har/HarPanel.d.ts.map +1 -1
- package/dist/data/har/HarPanel.js +3 -1
- package/dist/data/har/HarPanel.js.map +1 -1
- package/dist/data/test-runner/Expectation.cjs +57 -0
- package/dist/data/test-runner/Expectation.cjs.map +1 -0
- package/dist/data/test-runner/Expectation.d.ts +19 -0
- package/dist/data/test-runner/Expectation.d.ts.map +1 -0
- package/dist/data/test-runner/Expectation.js +57 -0
- package/dist/data/test-runner/Expectation.js.map +1 -0
- package/dist/data/test-runner/MarkIcons.cjs +38 -0
- package/dist/data/test-runner/MarkIcons.cjs.map +1 -0
- package/dist/data/test-runner/MarkIcons.d.ts +15 -0
- package/dist/data/test-runner/MarkIcons.d.ts.map +1 -0
- package/dist/data/test-runner/MarkIcons.js +38 -0
- package/dist/data/test-runner/MarkIcons.js.map +1 -0
- package/dist/data/test-runner/TestDetailPanel.cjs +74 -42
- package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -1
- package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -1
- package/dist/data/test-runner/TestDetailPanel.js +76 -44
- package/dist/data/test-runner/TestDetailPanel.js.map +1 -1
- package/dist/data/test-runner/TestFailureDetail.cjs +23 -27
- package/dist/data/test-runner/TestFailureDetail.cjs.map +1 -1
- package/dist/data/test-runner/TestFailureDetail.d.ts +2 -2
- package/dist/data/test-runner/TestFailureDetail.d.ts.map +1 -1
- package/dist/data/test-runner/TestFailureDetail.js +23 -27
- package/dist/data/test-runner/TestFailureDetail.js.map +1 -1
- package/dist/data/test-runner/TestFilterBar.cjs +87 -93
- package/dist/data/test-runner/TestFilterBar.cjs.map +1 -1
- package/dist/data/test-runner/TestFilterBar.d.ts +3 -2
- package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -1
- package/dist/data/test-runner/TestFilterBar.js +89 -95
- package/dist/data/test-runner/TestFilterBar.js.map +1 -1
- package/dist/data/test-runner/TestRunSummary.cjs +35 -21
- package/dist/data/test-runner/TestRunSummary.cjs.map +1 -1
- package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -1
- package/dist/data/test-runner/TestRunSummary.js +35 -21
- package/dist/data/test-runner/TestRunSummary.js.map +1 -1
- package/dist/data/test-runner/TestRunner.cjs +52 -30
- package/dist/data/test-runner/TestRunner.cjs.map +1 -1
- package/dist/data/test-runner/TestRunner.d.ts +3 -2
- package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
- package/dist/data/test-runner/TestRunner.js +52 -30
- package/dist/data/test-runner/TestRunner.js.map +1 -1
- package/dist/data/test-runner/TestTree.cjs +5 -2
- package/dist/data/test-runner/TestTree.cjs.map +1 -1
- package/dist/data/test-runner/TestTree.d.ts +5 -2
- package/dist/data/test-runner/TestTree.d.ts.map +1 -1
- package/dist/data/test-runner/TestTree.js +5 -2
- package/dist/data/test-runner/TestTree.js.map +1 -1
- package/dist/data/test-runner/TestTreeNode.cjs +27 -19
- package/dist/data/test-runner/TestTreeNode.cjs.map +1 -1
- package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -1
- package/dist/data/test-runner/TestTreeNode.js +28 -20
- package/dist/data/test-runner/TestTreeNode.js.map +1 -1
- package/dist/data/test-runner/adapter.cjs +11 -0
- package/dist/data/test-runner/adapter.cjs.map +1 -1
- package/dist/data/test-runner/adapter.d.ts +18 -7
- package/dist/data/test-runner/adapter.d.ts.map +1 -1
- package/dist/data/test-runner/adapter.js +11 -0
- package/dist/data/test-runner/adapter.js.map +1 -1
- package/dist/data/test-runner/copy.cjs +114 -0
- package/dist/data/test-runner/copy.cjs.map +1 -0
- package/dist/data/test-runner/copy.d.ts +47 -0
- package/dist/data/test-runner/copy.d.ts.map +1 -0
- package/dist/data/test-runner/copy.js +114 -0
- package/dist/data/test-runner/copy.js.map +1 -0
- package/dist/data/test-runner/index.d.ts +5 -2
- package/dist/data/test-runner/index.d.ts.map +1 -1
- package/dist/data/test-runner/marks.cjs +143 -0
- package/dist/data/test-runner/marks.cjs.map +1 -0
- package/dist/data/test-runner/marks.d.ts +30 -0
- package/dist/data/test-runner/marks.d.ts.map +1 -0
- package/dist/data/test-runner/marks.js +143 -0
- package/dist/data/test-runner/marks.js.map +1 -0
- package/dist/data/test-runner/status.cjs +27 -44
- package/dist/data/test-runner/status.cjs.map +1 -1
- package/dist/data/test-runner/status.d.ts +12 -3
- package/dist/data/test-runner/status.d.ts.map +1 -1
- package/dist/data/test-runner/status.js +27 -44
- package/dist/data/test-runner/status.js.map +1 -1
- package/dist/data/verification/adapterViews.cjs +16 -41
- package/dist/data/verification/adapterViews.cjs.map +1 -1
- package/dist/data/verification/adapterViews.d.ts.map +1 -1
- package/dist/data/verification/adapterViews.js +16 -41
- package/dist/data/verification/adapterViews.js.map +1 -1
- package/dist/data/verification/adapters.cjs +115 -3
- package/dist/data/verification/adapters.cjs.map +1 -1
- package/dist/data/verification/adapters.d.ts.map +1 -1
- package/dist/data/verification/adapters.js +115 -3
- package/dist/data/verification/adapters.js.map +1 -1
- package/dist/data/verification/verifyReportTests.cjs.map +1 -1
- package/dist/data/verification/verifyReportTests.js.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/data.cjs +44 -3
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +2 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +45 -4
- package/dist/data.js.map +1 -1
- package/dist/icons/components/UiLogoEslint.cjs +10 -0
- package/dist/icons/components/UiLogoEslint.cjs.map +1 -0
- package/dist/icons/components/UiLogoEslint.d.ts +9 -0
- package/dist/icons/components/UiLogoEslint.d.ts.map +1 -0
- package/dist/icons/components/UiLogoEslint.js +10 -0
- package/dist/icons/components/UiLogoEslint.js.map +1 -0
- package/dist/icons/components/UiLogoGo.cjs +10 -0
- package/dist/icons/components/UiLogoGo.cjs.map +1 -0
- package/dist/icons/components/UiLogoGo.d.ts +9 -0
- package/dist/icons/components/UiLogoGo.d.ts.map +1 -0
- package/dist/icons/components/UiLogoGo.js +10 -0
- package/dist/icons/components/UiLogoGo.js.map +1 -0
- package/dist/icons/components/UiLogoJest.cjs +10 -0
- package/dist/icons/components/UiLogoJest.cjs.map +1 -0
- package/dist/icons/components/UiLogoJest.d.ts +9 -0
- package/dist/icons/components/UiLogoJest.d.ts.map +1 -0
- package/dist/icons/components/UiLogoJest.js +10 -0
- package/dist/icons/components/UiLogoJest.js.map +1 -0
- package/dist/icons/components/UiLogoMarkdown.cjs +10 -0
- package/dist/icons/components/UiLogoMarkdown.cjs.map +1 -0
- package/dist/icons/components/UiLogoMarkdown.d.ts +9 -0
- package/dist/icons/components/UiLogoMarkdown.d.ts.map +1 -0
- package/dist/icons/components/UiLogoMarkdown.js +10 -0
- package/dist/icons/components/UiLogoMarkdown.js.map +1 -0
- package/dist/icons/components/UiLogoOxc.cjs +10 -0
- package/dist/icons/components/UiLogoOxc.cjs.map +1 -0
- package/dist/icons/components/UiLogoOxc.d.ts +9 -0
- package/dist/icons/components/UiLogoOxc.d.ts.map +1 -0
- package/dist/icons/components/UiLogoOxc.js +10 -0
- package/dist/icons/components/UiLogoOxc.js.map +1 -0
- package/dist/icons/components/UiLogoPlaywright.cjs +10 -0
- package/dist/icons/components/UiLogoPlaywright.cjs.map +1 -0
- package/dist/icons/components/UiLogoPlaywright.d.ts +9 -0
- package/dist/icons/components/UiLogoPlaywright.d.ts.map +1 -0
- package/dist/icons/components/UiLogoPlaywright.js +10 -0
- package/dist/icons/components/UiLogoPlaywright.js.map +1 -0
- package/dist/icons/components/UiLogoPython.cjs +10 -0
- package/dist/icons/components/UiLogoPython.cjs.map +1 -0
- package/dist/icons/components/UiLogoPython.d.ts +9 -0
- package/dist/icons/components/UiLogoPython.d.ts.map +1 -0
- package/dist/icons/components/UiLogoPython.js +10 -0
- package/dist/icons/components/UiLogoPython.js.map +1 -0
- package/dist/icons/components/UiLogoReact.cjs +10 -0
- package/dist/icons/components/UiLogoReact.cjs.map +1 -0
- package/dist/icons/components/UiLogoReact.d.ts +9 -0
- package/dist/icons/components/UiLogoReact.d.ts.map +1 -0
- package/dist/icons/components/UiLogoReact.js +10 -0
- package/dist/icons/components/UiLogoReact.js.map +1 -0
- package/dist/icons/components/UiLogoRuff.cjs +10 -0
- package/dist/icons/components/UiLogoRuff.cjs.map +1 -0
- package/dist/icons/components/UiLogoRuff.d.ts +9 -0
- package/dist/icons/components/UiLogoRuff.d.ts.map +1 -0
- package/dist/icons/components/UiLogoRuff.js +10 -0
- package/dist/icons/components/UiLogoRuff.js.map +1 -0
- package/dist/icons/components/UiLogoTypescript.cjs +10 -0
- package/dist/icons/components/UiLogoTypescript.cjs.map +1 -0
- package/dist/icons/components/UiLogoTypescript.d.ts +9 -0
- package/dist/icons/components/UiLogoTypescript.d.ts.map +1 -0
- package/dist/icons/components/UiLogoTypescript.js +10 -0
- package/dist/icons/components/UiLogoTypescript.js.map +1 -0
- package/dist/icons/components/UiLogoVitest.cjs +10 -0
- package/dist/icons/components/UiLogoVitest.cjs.map +1 -0
- package/dist/icons/components/UiLogoVitest.d.ts +9 -0
- package/dist/icons/components/UiLogoVitest.d.ts.map +1 -0
- package/dist/icons/components/UiLogoVitest.js +10 -0
- package/dist/icons/components/UiLogoVitest.js.map +1 -0
- package/dist/icons/index.d.ts +11 -0
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/icons/registry.cjs +22 -0
- package/dist/icons/registry.cjs.map +1 -1
- package/dist/icons/registry.d.ts.map +1 -1
- package/dist/icons/registry.js +22 -0
- package/dist/icons/registry.js.map +1 -1
- package/dist/icons.cjs +22 -0
- package/dist/icons.cjs.map +1 -1
- package/dist/icons.js +22 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.cjs +44 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +45 -4
- package/dist/index.js.map +1 -1
- package/dist/overlay/DropdownMenuSubmenu.cjs +4 -4
- package/dist/overlay/DropdownMenuSubmenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenuSubmenu.js +4 -4
- package/dist/overlay/DropdownMenuSubmenu.js.map +1 -1
- package/dist/rpc/InlineError.cjs +12 -43
- package/dist/rpc/InlineError.cjs.map +1 -1
- package/dist/rpc/InlineError.d.ts.map +1 -1
- package/dist/rpc/InlineError.js +13 -44
- package/dist/rpc/InlineError.js.map +1 -1
- package/dist/rpc/OperationScheduleDialog.cjs +63 -10
- package/dist/rpc/OperationScheduleDialog.cjs.map +1 -1
- package/dist/rpc/OperationScheduleDialog.d.ts +4 -0
- package/dist/rpc/OperationScheduleDialog.d.ts.map +1 -1
- package/dist/rpc/OperationScheduleDialog.js +63 -10
- package/dist/rpc/OperationScheduleDialog.js.map +1 -1
- package/dist/rpc/OperationSchedules.cjs +12 -2
- package/dist/rpc/OperationSchedules.cjs.map +1 -1
- package/dist/rpc/OperationSchedules.d.ts.map +1 -1
- package/dist/rpc/OperationSchedules.js +12 -2
- package/dist/rpc/OperationSchedules.js.map +1 -1
- package/dist/rpc/operation-schedule-model.cjs +2 -0
- package/dist/rpc/operation-schedule-model.cjs.map +1 -1
- package/dist/rpc/operation-schedule-model.d.ts.map +1 -1
- package/dist/rpc/operation-schedule-model.js +2 -0
- package/dist/rpc/operation-schedule-model.js.map +1 -1
- package/dist/rpc/operation-schedule-types.d.ts +7 -1
- package/dist/rpc/operation-schedule-types.d.ts.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/data/test-runner/frameworkIcon.cjs +0 -27
- package/dist/data/test-runner/frameworkIcon.cjs.map +0 -1
- package/dist/data/test-runner/frameworkIcon.d.ts +0 -3
- package/dist/data/test-runner/frameworkIcon.d.ts.map +0 -1
- package/dist/data/test-runner/frameworkIcon.js +0 -27
- package/dist/data/test-runner/frameworkIcon.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionInspector.remote.js","sources":["../../../src/data/ai/SessionInspector.remote.ts"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n useEventSourceFactory,\n type EventSourceFactory,\n type EventSourceLike,\n} from \"../../hooks/event-source\";\nimport type { SessionCollectionInput } from \"./SessionInspector.collection-types\";\nimport type {\n SessionUIMessage,\n UnifiedSessionInput,\n} from \"./SessionViewer.unified\";\n\n// SessionInspector.remote loads a session from a URL and follows it live.\n// The contract (captain `SessionHandler`):\n// GET {src} Accept: application/json → one UnifiedSessionInput; a non-2xx\n// body is `{error, …}` JSON or plain text, surfaced verbatim.\n// GET {src}?follow=1 (SSE) → `entry` frames (one SessionUIMessage, upserted\n// by id), `state` frames ({revision, lifecycleStatus,\n// activityState, facets}), `error` frames ({error}) and `: ping`\n// comments. The server closes the stream once the session is\n// terminal.\n// A `state` frame with a higher revision means aggregate facets (plan,\n// approvals, files, …) changed, so the aggregate is refetched; entries\n// streamed while it was in flight stay merged on top. `facets` is an opaque\n// fingerprint of the non-message session facets (plan, todos, files, approval\n// requests): the first frame's value is the baseline (no refetch from it\n// alone), and any later frame whose `facets` differs from the last one seen\n// also triggers a refetch, independently of `revision`.\n\nexport const TERMINAL_SESSION_LIFECYCLES = [\n \"succeeded\",\n \"partial\",\n \"failed\",\n \"cancelled\",\n \"interrupted\",\n \"completed\",\n] as const;\n\nexport function isTerminalSessionLifecycle(status: string | undefined) {\n return (\n status !== undefined &&\n (TERMINAL_SESSION_LIFECYCLES as readonly string[]).includes(status)\n );\n}\n\nexport interface RemoteSessionOptions {\n /** Force (`true`) or suppress (`false`) the live stream. By default a\n * session follows only while it reports a non-terminal `lifecycleStatus`;\n * a session that reports none is not followed. */\n follow?: boolean;\n}\n\nexport interface RemoteSessionState {\n session?: UnifiedSessionInput;\n error?: string;\n loading: boolean;\n}\n\n/** The payload of a follow stream's `state` frame. */\nexport interface SessionFollowState {\n revision: number;\n lifecycleStatus: string;\n activityState: string;\n /** Opaque fingerprint of non-message session facets (plan, todos, files,\n * approval requests). Always sent by captain — see the module doc above\n * for how it's used to decide whether to refetch. */\n facets: string;\n}\n\nexport interface RemoteSessionHandle {\n /** Settles with the first fetched session, or rejects with the load error\n * (which `onChange` has already reported as `error`). */\n loaded: Promise<UnifiedSessionInput>;\n close: () => void;\n}\n\n/** `src` with `follow=1` appended to its query, keeping any fragment last. */\nexport function sessionFollowUrl(src: string) {\n const hashAt = src.indexOf(\"#\");\n const base = hashAt === -1 ? src : src.slice(0, hashAt);\n const hash = hashAt === -1 ? \"\" : src.slice(hashAt);\n const separator = !base.includes(\"?\")\n ? \"?\"\n : base.endsWith(\"?\") || base.endsWith(\"&\")\n ? \"\"\n : \"&\";\n return `${base}${separator}follow=1${hash}`;\n}\n\nexport async function fetchRemoteSession(\n src: string,\n signal?: AbortSignal,\n): Promise<UnifiedSessionInput> {\n const response = await fetch(src, {\n headers: { Accept: \"application/json\" },\n ...(signal ? { signal } : {}),\n });\n const body = await response.text();\n if (!response.ok) {\n throw new Error(\n `Failed to load session ${src} (HTTP ${response.status}): ${errorDetail(body)}`,\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch (cause) {\n throw new Error(\n `Session ${src} returned invalid JSON (${String(cause)}): ${body.slice(0, 200)}`,\n );\n }\n if (!isRecord(parsed)) {\n throw new Error(\n `Session ${src} returned ${Array.isArray(parsed) ? \"an array\" : String(parsed)}, expected a session object`,\n );\n }\n return parsed as UnifiedSessionInput;\n}\n\n/** Upserts messages by id: a known id is replaced in place, a new one appended. */\nexport function mergeSessionMessages(\n base: readonly SessionUIMessage[] | undefined,\n incoming: readonly SessionUIMessage[],\n): SessionUIMessage[] {\n const merged = [...(base ?? [])];\n for (const message of incoming) {\n const index = merged.findIndex((existing) => existing.id === message.id);\n if (index === -1) merged.push(message);\n else merged[index] = message;\n }\n return merged;\n}\n\n/** Fetches `src`, then follows it when wanted, reporting every change through\n * `onChange`. Nothing is reported after `close()`. */\nexport function openRemoteSession(\n src: string,\n {\n follow,\n eventSourceFactory,\n onChange,\n }: RemoteSessionOptions & {\n eventSourceFactory: EventSourceFactory;\n onChange: (state: RemoteSessionState) => void;\n },\n): RemoteSessionHandle {\n const url = sessionFollowUrl(src);\n const aborter = new AbortController();\n const streamed = new Map<string, { message: SessionUIMessage; seq: number }>();\n let closed = false;\n let source: EventSourceLike | undefined;\n let state: RemoteSessionState = { loading: true };\n let revision: number | undefined;\n let facets: string | undefined;\n let seq = 0;\n let generation = 0;\n\n const emit = (next: RemoteSessionState) => {\n if (closed) return;\n state = next;\n onChange(next);\n };\n const stopFollowing = () => {\n source?.close();\n source = undefined;\n };\n const fail = (message: string) => {\n stopFollowing();\n emit({ ...state, loading: false, error: message });\n };\n\n const refresh = async () => {\n const current = ++generation;\n const startedAt = seq;\n const fetched = await fetchRemoteSession(src, aborter.signal);\n if (closed || current !== generation) return undefined;\n const fetchedIds = new Set(fetched.messages?.map((message) => message.id));\n const keep = [...streamed.values()]\n .filter((entry) => entry.seq > startedAt || !fetchedIds.has(entry.message.id))\n .map((entry) => entry.message);\n const session = {\n ...fetched,\n messages: mergeSessionMessages(fetched.messages, keep),\n };\n if (typeof fetched.revision === \"number\") {\n revision = Math.max(revision ?? fetched.revision, fetched.revision);\n }\n emit({ ...state, session, loading: false });\n return session;\n };\n\n const readFrame = (event: Event): unknown => {\n try {\n return JSON.parse((event as MessageEvent).data as string);\n } catch (cause) {\n fail(`Unparseable ${event.type} frame from ${url}: ${String(cause)}`);\n return undefined;\n }\n };\n\n const onEntry = (event: Event) => {\n const message = readFrame(event);\n if (message === undefined) return;\n if (!isRecord(message) || typeof message.id !== \"string\" || !message.id) {\n fail(`Entry frame from ${url} has no message id: ${String((event as MessageEvent).data)}`);\n return;\n }\n const entry = message as unknown as SessionUIMessage;\n streamed.set(message.id, { message: entry, seq: ++seq });\n const session = state.session!;\n emit({\n ...state,\n session: { ...session, messages: mergeSessionMessages(session.messages, [entry]) },\n });\n };\n\n const onState = (event: Event) => {\n const frame = readFrame(event);\n if (frame === undefined) return;\n if (\n !isRecord(frame) ||\n typeof frame.revision !== \"number\" ||\n typeof frame.lifecycleStatus !== \"string\" ||\n typeof frame.facets !== \"string\"\n ) {\n fail(`State frame from ${url} lacks a numeric revision, a lifecycleStatus, and facets: ${String((event as MessageEvent).data)}`);\n return;\n }\n const next = frame as unknown as SessionFollowState;\n emit({\n ...state,\n session: {\n ...state.session,\n lifecycleStatus: next.lifecycleStatus,\n ...(typeof next.activityState === \"string\" ? { activityState: next.activityState } : {}),\n },\n });\n const revisionIncreased = revision === undefined || next.revision > revision;\n // The first state frame's facets is the baseline: it never triggers a\n // refetch by itself, only a higher revision does (as before facets\n // existed). Any later frame whose facets differ from the last one seen\n // does trigger a refetch, independently of revision.\n const facetsChanged = facets !== undefined && next.facets !== facets;\n facets = next.facets;\n if (revisionIncreased) revision = next.revision;\n if (revisionIncreased || facetsChanged) {\n refresh().catch((reason: unknown) => {\n if (!closed) fail(errorMessage(reason));\n });\n }\n if (isTerminalSessionLifecycle(next.lifecycleStatus)) stopFollowing();\n };\n\n const startFollowing = () => {\n const es = eventSourceFactory(url);\n source = es;\n let received = false;\n const onError = (event: Event) => {\n const data = (event as MessageEvent).data;\n if (typeof data === \"string\") {\n const frame = readFrame(event);\n if (frame === undefined) return;\n const reported = isRecord(frame) && typeof frame.error === \"string\" ? frame.error : data;\n fail(`Session stream ${url} reported an error: ${reported}`);\n return;\n }\n // After data has arrived, a dropped connection reconnects on its own and\n // the server replays; entries upsert by id, so the replay is idempotent.\n if (received && es.readyState !== 2) return;\n fail(\n received\n ? `Lost the session stream ${url}`\n : `Could not connect to the session stream ${url}`,\n );\n };\n es.addEventListener(\"entry\", (event) => {\n received = true;\n onEntry(event);\n });\n es.addEventListener(\"state\", (event) => {\n received = true;\n onState(event);\n });\n es.addEventListener(\"error\", onError);\n };\n\n const loaded = refresh().then(\n (session) => {\n if (!session) throw new Error(`Session ${src} was closed before it loaded`);\n const lifecycle = session.lifecycleStatus;\n const wanted =\n follow ?? (lifecycle !== undefined && !isTerminalSessionLifecycle(lifecycle));\n if (wanted && !closed) startFollowing();\n return session;\n },\n (reason: unknown) => {\n if (!closed) fail(errorMessage(reason));\n throw reason;\n },\n );\n\n return {\n loaded,\n close: () => {\n closed = true;\n aborter.abort();\n stopFollowing();\n },\n };\n}\n\nconst IDLE: RemoteSessionState = { loading: false };\nconst LOADING: RemoteSessionState = { loading: true };\n\n/** Loads `src` (undefined loads nothing) and follows it on demand; see\n * `RemoteSessionOptions.follow`. Changing `src` or `follow`, or unmounting,\n * closes the previous stream. */\nexport function useRemoteSession(\n src: string | undefined,\n { follow }: RemoteSessionOptions = {},\n): RemoteSessionState {\n const eventSourceFactory = useEventSourceFactory();\n const factoryRef = useRef(eventSourceFactory);\n factoryRef.current = eventSourceFactory;\n const [snapshot, setSnapshot] = useState<{\n src: string;\n state: RemoteSessionState;\n }>();\n\n useEffect(() => {\n if (src === undefined) return undefined;\n setSnapshot({ src, state: LOADING });\n const handle = openRemoteSession(src, {\n ...(follow === undefined ? {} : { follow }),\n eventSourceFactory: factoryRef.current,\n onChange: (state) => setSnapshot({ src, state }),\n });\n // A load failure already reached the state through onChange as `error`.\n handle.loaded.catch(() => undefined);\n return handle.close;\n }, [src, follow]);\n\n if (src === undefined) return IDLE;\n return snapshot?.src === src ? snapshot.state : LOADING;\n}\n\nexport interface RemoteCollectionState {\n /** The collection with its current item's session loaded, once it is. */\n collection?: SessionCollectionInput;\n error?: string;\n loading: boolean;\n}\n\n/** Loads the current item of a collection when it carries only `src`, so the\n * hierarchy (which requires a loaded current session) can render. */\nexport function useRemoteCollection(\n collection: SessionCollectionInput | undefined,\n options: RemoteSessionOptions = {},\n): RemoteCollectionState {\n const current = collection?.sessions.find(\n (item) => item.id === collection.currentSessionId,\n );\n const src = current && !current.session ? current.src : undefined;\n const remote = useRemoteSession(src, options);\n return useMemo(() => {\n if (!collection) return IDLE;\n if (src === undefined || !current) return { collection, loading: false };\n const status = {\n loading: remote.loading,\n ...(remote.error ? { error: remote.error } : {}),\n };\n if (!remote.session) return status;\n const session = remote.session;\n return {\n ...status,\n collection: {\n ...collection,\n sessions: collection.sessions.map((item) =>\n item === current ? { ...item, session } : item,\n ),\n },\n };\n }, [collection, current, src, remote]);\n}\n\nfunction errorDetail(body: string) {\n const text = body.trim();\n if (!text) return \"empty response body\";\n try {\n const parsed: unknown = JSON.parse(text);\n if (isRecord(parsed) && typeof parsed.error === \"string\") return parsed.error;\n } catch {\n // Not JSON: the text itself is the detail.\n }\n return text;\n}\n\nfunction errorMessage(reason: unknown) {\n return reason instanceof Error ? reason.message : String(reason);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionInspector.remote.js","sources":["../../../src/data/ai/SessionInspector.remote.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n useEventSourceFactory,\n type EventSourceFactory,\n type EventSourceLike,\n} from \"../../hooks/event-source\";\nimport type { SessionCollectionInput } from \"./SessionInspector.collection-types\";\nimport type {\n SessionUIMessage,\n UnifiedSessionInput,\n} from \"./SessionViewer.unified\";\n\n// SessionInspector.remote loads a session from a URL and follows it live,\n// asking only for the transcript's tail. The contract (captain\n// `SessionHandler`):\n// GET {src}?tail=N Accept: application/json → one UnifiedSessionInput with\n// the newest N messages, and `window` (the full counts) when older\n// ones were left out; a non-2xx body is `{error, …}` JSON or plain\n// text, surfaced verbatim.\n// GET {src}?before=<id>&limit=N → the N messages ahead of <id>: one page of\n// history, loaded on demand (`loadOlder`) and prepended.\n// GET {src}?follow=1&after=<id> (SSE) → `entry` frames for the messages\n// after <id> and every later change (one SessionUIMessage, upserted\n// by id), `state` frames ({revision, lifecycleStatus,\n// activityState, facets}), `error` frames ({error}) and `: ping`\n// comments. The server closes the stream once the session is\n// terminal.\n// A `state` frame with a higher revision means aggregate facets (plan,\n// approvals, files, …) changed, so the tail is refetched and merged into the\n// messages already held; entries streamed while it was in flight stay merged\n// on top. `facets` is an opaque\n// fingerprint of the non-message session facets (plan, todos, files, approval\n// requests): the first frame's value is the baseline (no refetch from it\n// alone), and any later frame whose `facets` differs from the last one seen\n// also triggers a refetch, independently of `revision`.\n\nexport const TERMINAL_SESSION_LIFECYCLES = [\n \"succeeded\",\n \"partial\",\n \"failed\",\n \"cancelled\",\n \"interrupted\",\n \"completed\",\n] as const;\n\nexport function isTerminalSessionLifecycle(status: string | undefined) {\n return (\n status !== undefined &&\n (TERMINAL_SESSION_LIFECYCLES as readonly string[]).includes(status)\n );\n}\n\n/** Messages asked for by the first read and by each page of history. */\nexport const DEFAULT_SESSION_PAGE_SIZE = 100;\n\nexport interface RemoteSessionOptions {\n /** Force (`true`) or suppress (`false`) the live stream. By default a\n * session follows only while it reports a non-terminal `lifecycleStatus`;\n * a session that reports none is not followed. */\n follow?: boolean;\n /** Messages per read: the first read's `tail` and each history page's\n * `limit`. Defaults to DEFAULT_SESSION_PAGE_SIZE. */\n pageSize?: number;\n}\n\nexport interface RemoteSessionState {\n session?: UnifiedSessionInput;\n error?: string;\n loading: boolean;\n /** Older messages exist that `loadOlder` has not fetched yet. */\n hasOlder?: boolean;\n /** A `loadOlder` page is in flight. */\n loadingOlder?: boolean;\n}\n\n/** The payload of a follow stream's `state` frame. */\nexport interface SessionFollowState {\n revision: number;\n lifecycleStatus: string;\n activityState: string;\n /** Opaque fingerprint of non-message session facets (plan, todos, files,\n * approval requests). Always sent by captain — see the module doc above\n * for how it's used to decide whether to refetch. */\n facets: string;\n}\n\nexport interface RemoteSessionHandle {\n /** Settles with the first fetched session, or rejects with the load error\n * (which `onChange` has already reported as `error`). */\n loaded: Promise<UnifiedSessionInput>;\n /** Fetches the page of history before the oldest message held and\n * prepends it; a no-op while one is in flight or nothing older remains. */\n loadOlder: () => Promise<void>;\n close: () => void;\n}\n\n/** `src` with `params` set in its query (replacing any of the same name),\n * keeping any fragment last. */\nexport function sessionUrl(src: string, params: Record<string, string | number>) {\n const hashAt = src.indexOf(\"#\");\n const base = hashAt === -1 ? src : src.slice(0, hashAt);\n const hash = hashAt === -1 ? \"\" : src.slice(hashAt);\n const queryAt = base.indexOf(\"?\");\n const query = new URLSearchParams(queryAt === -1 ? \"\" : base.slice(queryAt + 1));\n for (const [name, value] of Object.entries(params)) query.set(name, String(value));\n return `${queryAt === -1 ? base : base.slice(0, queryAt)}?${query}${hash}`;\n}\n\nexport async function fetchRemoteSession(\n src: string,\n signal?: AbortSignal,\n): Promise<UnifiedSessionInput> {\n const response = await fetch(src, {\n headers: { Accept: \"application/json\" },\n ...(signal ? { signal } : {}),\n });\n const body = await response.text();\n if (!response.ok) {\n throw new Error(\n `Failed to load session ${src} (HTTP ${response.status}): ${errorDetail(body)}`,\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(body);\n } catch (cause) {\n throw new Error(\n `Session ${src} returned invalid JSON (${String(cause)}): ${body.slice(0, 200)}`,\n );\n }\n if (!isRecord(parsed)) {\n throw new Error(\n `Session ${src} returned ${Array.isArray(parsed) ? \"an array\" : String(parsed)}, expected a session object`,\n );\n }\n return parsed as UnifiedSessionInput;\n}\n\n/** Upserts messages by id: a known id is replaced in place, a new one appended.\n * Placeholders stand in for a transcript that does not exist yet; the first\n * real message replaces them all, since its ids never match theirs. */\nexport function mergeSessionMessages(\n base: readonly SessionUIMessage[] | undefined,\n incoming: readonly SessionUIMessage[],\n): SessionUIMessage[] {\n const real = incoming.some((message) => !message.placeholder);\n const merged = (base ?? []).filter((message) => !(real && message.placeholder));\n for (const message of incoming) {\n const index = merged.findIndex((existing) => existing.id === message.id);\n if (index === -1) merged.push(message);\n else merged[index] = message;\n }\n return merged;\n}\n\n/** Fetches `src`, then follows it when wanted, reporting every change through\n * `onChange`. Nothing is reported after `close()`. */\nexport function openRemoteSession(\n src: string,\n {\n follow,\n pageSize = DEFAULT_SESSION_PAGE_SIZE,\n eventSourceFactory,\n onChange,\n }: RemoteSessionOptions & {\n eventSourceFactory: EventSourceFactory;\n onChange: (state: RemoteSessionState) => void;\n },\n): RemoteSessionHandle {\n let url = sessionUrl(src, { follow: 1 });\n let olderPage: Promise<void> | undefined;\n let pageError: string | undefined;\n const aborter = new AbortController();\n const streamed = new Map<string, { message: SessionUIMessage; seq: number }>();\n let closed = false;\n let source: EventSourceLike | undefined;\n let state: RemoteSessionState = { loading: true };\n let revision: number | undefined;\n let facets: string | undefined;\n let seq = 0;\n let generation = 0;\n\n const emit = (next: RemoteSessionState) => {\n if (closed) return;\n state = next;\n onChange(next);\n };\n const stopFollowing = () => {\n source?.close();\n source = undefined;\n };\n const fail = (message: string) => {\n stopFollowing();\n emit({ ...state, loading: false, error: message });\n };\n\n // Reads the tail and merges it into the messages already held — older pages\n // and streamed entries — so a refetch never drops history the reader loaded.\n const refresh = async () => {\n const current = ++generation;\n const startedAt = seq;\n const fetched = await fetchRemoteSession(sessionUrl(src, { tail: pageSize }), aborter.signal);\n if (closed || current !== generation) return undefined;\n const fetchedIds = new Set(fetched.messages?.map((message) => message.id));\n const keep = [...streamed.values()]\n .filter((entry) => entry.seq > startedAt || !fetchedIds.has(entry.message.id))\n .map((entry) => entry.message);\n const held = mergeSessionMessages(state.session?.messages, fetched.messages ?? []);\n const session = {\n ...fetched,\n messages: mergeSessionMessages(held, keep),\n };\n if (typeof fetched.revision === \"number\") {\n revision = Math.max(revision ?? fetched.revision, fetched.revision);\n }\n const hasOlder =\n state.session === undefined\n ? (fetched.window?.messages ?? 0) > (fetched.messages?.length ?? 0)\n : state.hasOlder;\n emit({ ...state, session, loading: false, hasOlder: hasOlder ?? false });\n return session;\n };\n\n const loadOlder = (): Promise<void> => {\n const oldest = state.session?.messages?.[0]?.id;\n if (closed || olderPage || !state.hasOlder || !oldest) return olderPage ?? Promise.resolve();\n emit({ ...state, loadingOlder: true });\n olderPage = fetchRemoteSession(sessionUrl(src, { before: oldest, limit: pageSize }), aborter.signal)\n .then((page) => {\n const older = page.messages ?? [];\n // A page that loads clears the alert a failed page raised, never a\n // stream's terminal error.\n const { error, ...rest } = state;\n const session = state.session!;\n emit({\n ...rest,\n ...(error !== undefined && error !== pageError ? { error } : {}),\n loadingOlder: false,\n hasOlder: older.length === pageSize,\n session: { ...session, messages: mergeSessionMessages(older, session.messages ?? []) },\n });\n pageError = undefined;\n })\n .catch((reason: unknown) => {\n if (closed) return;\n pageError = errorMessage(reason);\n emit({ ...state, loadingOlder: false, error: pageError });\n })\n .finally(() => {\n olderPage = undefined;\n });\n return olderPage;\n };\n\n const readFrame = (event: Event): unknown => {\n try {\n return JSON.parse((event as MessageEvent).data as string);\n } catch (cause) {\n fail(`Unparseable ${event.type} frame from ${url}: ${String(cause)}`);\n return undefined;\n }\n };\n\n const onEntry = (event: Event) => {\n const message = readFrame(event);\n if (message === undefined) return;\n if (!isRecord(message) || typeof message.id !== \"string\" || !message.id) {\n fail(`Entry frame from ${url} has no message id: ${String((event as MessageEvent).data)}`);\n return;\n }\n const entry = message as unknown as SessionUIMessage;\n streamed.set(message.id, { message: entry, seq: ++seq });\n const session = state.session!;\n emit({\n ...state,\n session: { ...session, messages: mergeSessionMessages(session.messages, [entry]) },\n });\n };\n\n const onState = (event: Event) => {\n const frame = readFrame(event);\n if (frame === undefined) return;\n if (\n !isRecord(frame) ||\n typeof frame.revision !== \"number\" ||\n typeof frame.lifecycleStatus !== \"string\" ||\n typeof frame.facets !== \"string\"\n ) {\n fail(`State frame from ${url} lacks a numeric revision, a lifecycleStatus, and facets: ${String((event as MessageEvent).data)}`);\n return;\n }\n const next = frame as unknown as SessionFollowState;\n emit({\n ...state,\n session: {\n ...state.session,\n lifecycleStatus: next.lifecycleStatus,\n ...(typeof next.activityState === \"string\" ? { activityState: next.activityState } : {}),\n },\n });\n const revisionIncreased = revision === undefined || next.revision > revision;\n // The first state frame's facets is the baseline: it never triggers a\n // refetch by itself, only a higher revision does (as before facets\n // existed). Any later frame whose facets differ from the last one seen\n // does trigger a refetch, independently of revision.\n const facetsChanged = facets !== undefined && next.facets !== facets;\n facets = next.facets;\n if (revisionIncreased) revision = next.revision;\n if (revisionIncreased || facetsChanged) {\n refresh().catch((reason: unknown) => {\n if (!closed) fail(errorMessage(reason));\n });\n }\n if (isTerminalSessionLifecycle(next.lifecycleStatus)) stopFollowing();\n };\n\n // Resumes after the newest real message held, so the server replays only\n // what came later; a placeholder is no position in the transcript, so a\n // reader holding only placeholders follows from the start. A reconnect\n // reuses this URL; replayed entries upsert by id.\n const startFollowing = () => {\n const newest = state.session?.messages?.filter((message) => !message.placeholder).at(-1)?.id;\n url = sessionUrl(src, newest ? { follow: 1, after: newest } : { follow: 1 });\n const es = eventSourceFactory(url);\n source = es;\n let received = false;\n const onError = (event: Event) => {\n const data = (event as MessageEvent).data;\n if (typeof data === \"string\") {\n const frame = readFrame(event);\n if (frame === undefined) return;\n const reported = isRecord(frame) && typeof frame.error === \"string\" ? frame.error : data;\n fail(`Session stream ${url} reported an error: ${reported}`);\n return;\n }\n // After data has arrived, a dropped connection reconnects on its own and\n // the server replays; entries upsert by id, so the replay is idempotent.\n if (received && es.readyState !== 2) return;\n fail(\n received\n ? `Lost the session stream ${url}`\n : `Could not connect to the session stream ${url}`,\n );\n };\n es.addEventListener(\"entry\", (event) => {\n received = true;\n onEntry(event);\n });\n es.addEventListener(\"state\", (event) => {\n received = true;\n onState(event);\n });\n es.addEventListener(\"error\", onError);\n };\n\n const loaded = refresh().then(\n (session) => {\n if (!session) throw new Error(`Session ${src} was closed before it loaded`);\n const lifecycle = session.lifecycleStatus;\n const wanted =\n follow ?? (lifecycle !== undefined && !isTerminalSessionLifecycle(lifecycle));\n if (wanted && !closed) startFollowing();\n return session;\n },\n (reason: unknown) => {\n if (!closed) fail(errorMessage(reason));\n throw reason;\n },\n );\n\n return {\n loaded,\n loadOlder,\n close: () => {\n closed = true;\n aborter.abort();\n stopFollowing();\n },\n };\n}\n\nconst IDLE: RemoteSessionState = { loading: false };\nconst LOADING: RemoteSessionState = { loading: true };\n\nexport interface RemoteSessionView extends RemoteSessionState {\n /** See RemoteSessionHandle.loadOlder; a no-op before the session loads. */\n loadOlder: () => Promise<void>;\n}\n\n/** Loads `src` (undefined loads nothing) and follows it on demand; see\n * `RemoteSessionOptions`. Changing `src`, `follow` or `pageSize`, or\n * unmounting, closes the previous stream. */\nexport function useRemoteSession(\n src: string | undefined,\n { follow, pageSize }: RemoteSessionOptions = {},\n): RemoteSessionView {\n const eventSourceFactory = useEventSourceFactory();\n const factoryRef = useRef(eventSourceFactory);\n factoryRef.current = eventSourceFactory;\n const handleRef = useRef<RemoteSessionHandle>(undefined);\n const [snapshot, setSnapshot] = useState<{\n src: string;\n state: RemoteSessionState;\n }>();\n\n useEffect(() => {\n if (src === undefined) return undefined;\n setSnapshot({ src, state: LOADING });\n const handle = openRemoteSession(src, {\n ...(follow === undefined ? {} : { follow }),\n ...(pageSize === undefined ? {} : { pageSize }),\n eventSourceFactory: factoryRef.current,\n onChange: (state) => setSnapshot({ src, state }),\n });\n handleRef.current = handle;\n // A load failure already reached the state through onChange as `error`.\n handle.loaded.catch(() => undefined);\n return () => {\n handle.close();\n if (handleRef.current === handle) handleRef.current = undefined;\n };\n }, [src, follow, pageSize]);\n\n const loadOlder = useCallback(() => handleRef.current?.loadOlder() ?? Promise.resolve(), []);\n const state = src === undefined ? IDLE : snapshot?.src === src ? snapshot.state : LOADING;\n return useMemo(() => ({ ...state, loadOlder }), [state, loadOlder]);\n}\n\n/** What a host learns about a `src`-backed inspector's load. `error` carries\n * the server's message (a 404 body's `error` field or plain text) or the\n * follow stream's terminal error frame. */\nexport type SessionInspectorLoadState =\n | { status: \"loading\" | \"ready\" }\n | { status: \"error\"; error: Error };\n\n/** Reports `state` to `onChange` whenever its status or error changes; never\n * when `enabled` is false (in-memory sessions have no load to report). */\nexport function useReportLoadState({\n state,\n enabled,\n onChange,\n}: {\n state: RemoteSessionState;\n enabled: boolean;\n onChange: ((state: SessionInspectorLoadState) => void) | undefined;\n}) {\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n const { loading, error } = state;\n useEffect(() => {\n if (!enabled) return;\n onChangeRef.current?.(\n error !== undefined\n ? { status: \"error\", error: new Error(error) }\n : { status: loading ? \"loading\" : \"ready\" },\n );\n }, [enabled, loading, error]);\n}\n\nexport interface RemoteCollectionState {\n /** The collection with its current item's session loaded, once it is. */\n collection?: SessionCollectionInput;\n error?: string;\n loading: boolean;\n}\n\n/** Loads the current item of a collection when it carries only `src`, so the\n * hierarchy (which requires a loaded current session) can render. */\nexport function useRemoteCollection(\n collection: SessionCollectionInput | undefined,\n options: RemoteSessionOptions = {},\n): RemoteCollectionState {\n const current = collection?.sessions.find(\n (item) => item.id === collection.currentSessionId,\n );\n const src = current && !current.session ? current.src : undefined;\n const remote = useRemoteSession(src, options);\n return useMemo(() => {\n if (!collection) return IDLE;\n if (src === undefined || !current) return { collection, loading: false };\n const status = {\n loading: remote.loading,\n ...(remote.error ? { error: remote.error } : {}),\n };\n if (!remote.session) return status;\n const session = remote.session;\n return {\n ...status,\n collection: {\n ...collection,\n sessions: collection.sessions.map((item) =>\n item === current ? { ...item, session } : item,\n ),\n },\n };\n }, [collection, current, src, remote]);\n}\n\nfunction errorDetail(body: string) {\n const text = body.trim();\n if (!text) return \"empty response body\";\n try {\n const parsed: unknown = JSON.parse(text);\n if (isRecord(parsed) && typeof parsed.error === \"string\") return parsed.error;\n } catch {\n // Not JSON: the text itself is the detail.\n }\n return text;\n}\n\nfunction errorMessage(reason: unknown) {\n return reason instanceof Error ? reason.message : String(reason);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],"names":["state"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const react = require("react");
|
|
5
|
+
const SessionInspector_panelParts = require("./SessionInspector.panel-parts.cjs");
|
|
5
6
|
const utils = require("../../lib/utils.cjs");
|
|
6
7
|
const SessionInspector_model = require("./SessionInspector.model.cjs");
|
|
7
8
|
const SessionViewer = require("./SessionViewer.cjs");
|
|
@@ -19,6 +20,7 @@ function SessionTranscript({
|
|
|
19
20
|
session,
|
|
20
21
|
viewerProps
|
|
21
22
|
}) {
|
|
23
|
+
var _a, _b;
|
|
22
24
|
const turns = session.turns ?? [];
|
|
23
25
|
const palette = react.useMemo(
|
|
24
26
|
() => SessionInspector_model.createTurnPalette(
|
|
@@ -37,6 +39,9 @@ function SessionTranscript({
|
|
|
37
39
|
);
|
|
38
40
|
const viewerClassName = utils.cn("min-h-0 flex-1", viewerProps == null ? void 0 : viewerProps.className);
|
|
39
41
|
const scrollResetKey = session.id ?? (viewerProps == null ? void 0 : viewerProps.scrollResetKey);
|
|
42
|
+
if (session.detailAvailable === false && !((_a = session.messages) == null ? void 0 : _a.length) && !((_b = session.events) == null ? void 0 : _b.length)) {
|
|
43
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SessionInspector_panelParts.EmptyState, { children: "Transcript unavailable" });
|
|
44
|
+
}
|
|
40
45
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full min-h-0 flex-col", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
41
46
|
SessionViewer.SessionViewer,
|
|
42
47
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionInspector.transcript.cjs","sources":["../../../src/data/ai/SessionInspector.transcript.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport {\n createTurnPalette,\n shortSessionId,\n type TurnPalette,\n} from \"./SessionInspector.model\";\nimport {\n SessionViewer,\n type SessionEvent,\n type SessionViewerProps,\n} from \"./SessionViewer\";\nimport type { SessionTurn, UnifiedSessionInput } from \"./SessionViewer.unified\";\n\ntype TurnBadge = {\n id: string;\n label: string;\n color: TurnPalette;\n className: string;\n};\n\nconst TURN_COLOR_CLASS: Record<TurnPalette, string> = {\n sky: \"border-sky-300 bg-sky-100 text-sky-800 [[data-theme=dark]_&]:border-sky-700 [[data-theme=dark]_&]:bg-sky-950/60 [[data-theme=dark]_&]:text-sky-300\",\n violet:\n \"border-violet-300 bg-violet-100 text-violet-800 [[data-theme=dark]_&]:border-violet-700 [[data-theme=dark]_&]:bg-violet-950/60 [[data-theme=dark]_&]:text-violet-300\",\n emerald:\n \"border-emerald-300 bg-emerald-100 text-emerald-800 [[data-theme=dark]_&]:border-emerald-700 [[data-theme=dark]_&]:bg-emerald-950/60 [[data-theme=dark]_&]:text-emerald-300\",\n orange:\n \"border-orange-300 bg-orange-100 text-orange-800 [[data-theme=dark]_&]:border-orange-700 [[data-theme=dark]_&]:bg-orange-950/60 [[data-theme=dark]_&]:text-orange-300\",\n rose: \"border-rose-300 bg-rose-100 text-rose-800 [[data-theme=dark]_&]:border-rose-700 [[data-theme=dark]_&]:bg-rose-950/60 [[data-theme=dark]_&]:text-rose-300\",\n indigo:\n \"border-indigo-300 bg-indigo-100 text-indigo-800 [[data-theme=dark]_&]:border-indigo-700 [[data-theme=dark]_&]:bg-indigo-950/60 [[data-theme=dark]_&]:text-indigo-300\",\n teal: \"border-teal-300 bg-teal-100 text-teal-800 [[data-theme=dark]_&]:border-teal-700 [[data-theme=dark]_&]:bg-teal-950/60 [[data-theme=dark]_&]:text-teal-300\",\n fuchsia:\n \"border-fuchsia-300 bg-fuchsia-100 text-fuchsia-800 [[data-theme=dark]_&]:border-fuchsia-700 [[data-theme=dark]_&]:bg-fuchsia-950/60 [[data-theme=dark]_&]:text-fuchsia-300\",\n};\n\nexport function SessionTranscript({\n session,\n viewerProps,\n}: {\n session: UnifiedSessionInput;\n viewerProps?: Omit<SessionViewerProps, \"session\">;\n}) {\n const turns = session.turns ?? [];\n const palette = useMemo(\n () =>\n createTurnPalette(\n session.id,\n turns.map((turn) => turn.id)
|
|
1
|
+
{"version":3,"file":"SessionInspector.transcript.cjs","sources":["../../../src/data/ai/SessionInspector.transcript.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { EmptyState } from \"./SessionInspector.panel-parts\";\nimport { cn } from \"../../lib/utils\";\nimport {\n createTurnPalette,\n shortSessionId,\n type TurnPalette,\n} from \"./SessionInspector.model\";\nimport {\n SessionViewer,\n type SessionEvent,\n type SessionViewerProps,\n} from \"./SessionViewer\";\nimport type { SessionTurn, UnifiedSessionInput } from \"./SessionViewer.unified\";\n\ntype TurnBadge = {\n id: string;\n label: string;\n color: TurnPalette;\n className: string;\n};\n\nconst TURN_COLOR_CLASS: Record<TurnPalette, string> = {\n sky: \"border-sky-300 bg-sky-100 text-sky-800 [[data-theme=dark]_&]:border-sky-700 [[data-theme=dark]_&]:bg-sky-950/60 [[data-theme=dark]_&]:text-sky-300\",\n violet:\n \"border-violet-300 bg-violet-100 text-violet-800 [[data-theme=dark]_&]:border-violet-700 [[data-theme=dark]_&]:bg-violet-950/60 [[data-theme=dark]_&]:text-violet-300\",\n emerald:\n \"border-emerald-300 bg-emerald-100 text-emerald-800 [[data-theme=dark]_&]:border-emerald-700 [[data-theme=dark]_&]:bg-emerald-950/60 [[data-theme=dark]_&]:text-emerald-300\",\n orange:\n \"border-orange-300 bg-orange-100 text-orange-800 [[data-theme=dark]_&]:border-orange-700 [[data-theme=dark]_&]:bg-orange-950/60 [[data-theme=dark]_&]:text-orange-300\",\n rose: \"border-rose-300 bg-rose-100 text-rose-800 [[data-theme=dark]_&]:border-rose-700 [[data-theme=dark]_&]:bg-rose-950/60 [[data-theme=dark]_&]:text-rose-300\",\n indigo:\n \"border-indigo-300 bg-indigo-100 text-indigo-800 [[data-theme=dark]_&]:border-indigo-700 [[data-theme=dark]_&]:bg-indigo-950/60 [[data-theme=dark]_&]:text-indigo-300\",\n teal: \"border-teal-300 bg-teal-100 text-teal-800 [[data-theme=dark]_&]:border-teal-700 [[data-theme=dark]_&]:bg-teal-950/60 [[data-theme=dark]_&]:text-teal-300\",\n fuchsia:\n \"border-fuchsia-300 bg-fuchsia-100 text-fuchsia-800 [[data-theme=dark]_&]:border-fuchsia-700 [[data-theme=dark]_&]:bg-fuchsia-950/60 [[data-theme=dark]_&]:text-fuchsia-300\",\n};\n\nexport function SessionTranscript({\n session,\n viewerProps,\n}: {\n session: UnifiedSessionInput;\n viewerProps?: Omit<SessionViewerProps, \"session\">;\n}) {\n const turns = session.turns ?? [];\n const palette = useMemo(\n () =>\n createTurnPalette(\n session.id,\n turns.map((turn) => turn.id)\n ),\n [session.id, turns]\n );\n const turnBadges = useMemo(\n () => buildTurnBadges(turns, palette),\n [palette, turns]\n );\n const turnBadgeById = useMemo(\n () => new Map(turnBadges.map((badge) => [badge.id, badge])),\n [turnBadges]\n );\n const viewerClassName = cn(\"min-h-0 flex-1\", viewerProps?.className);\n const scrollResetKey = session.id ?? viewerProps?.scrollResetKey;\n\n if (\n session.detailAvailable === false &&\n !session.messages?.length &&\n !session.events?.length\n ) {\n return <EmptyState>Transcript unavailable</EmptyState>;\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <SessionViewer\n session={session}\n scrollable\n {...viewerProps}\n windowSize={viewerProps?.windowSize ?? 10}\n batchSize={viewerProps?.batchSize ?? 10}\n {...(scrollResetKey !== undefined ? { scrollResetKey } : {})}\n className={viewerClassName}\n showHeader={false}\n showContextMeter={false}\n showRowMetadata={false}\n renderMessageBadge={(event: SessionEvent) => {\n const badge = event.turnId\n ? turnBadgeById.get(event.turnId)\n : undefined;\n return badge ? (\n <TranscriptTurnBadge badge={badge} placement=\"message\" />\n ) : null;\n }}\n headerActions={\n <>\n {viewerProps?.headerActions}\n <TurnBadgeLegend badges={turnBadges} />\n </>\n }\n />\n </div>\n );\n}\n\nfunction buildTurnBadges(\n turns: SessionTurn[],\n palette: Record<string, TurnPalette>\n) {\n return turns.map((turn): TurnBadge => {\n const color = palette[turn.id] ?? \"sky\";\n return {\n id: turn.id,\n label: shortSessionId(turn.id),\n color,\n className: TURN_COLOR_CLASS[color],\n };\n });\n}\n\nfunction TurnBadgeLegend({ badges }: { badges: TurnBadge[] }) {\n return (\n <div className=\"flex max-w-full flex-wrap gap-1\" aria-label=\"Turn colors\">\n {badges.map((badge) => (\n <TranscriptTurnBadge key={badge.id} badge={badge} placement=\"legend\" />\n ))}\n </div>\n );\n}\n\nfunction TranscriptTurnBadge({\n badge,\n placement,\n}: {\n badge: TurnBadge;\n placement: \"legend\" | \"message\";\n}) {\n return (\n <span\n data-testid={\n placement === \"legend\" ? \"transcript-turn-badge\" : \"message-turn-badge\"\n }\n data-turn-color={badge.color}\n title={`Turn ${badge.id}`}\n className={cn(\n \"inline-flex shrink-0 rounded border px-1 py-px font-mono text-[9px] font-semibold leading-none\",\n placement === \"message\" && \"mt-1\",\n badge.className\n )}\n >\n {badge.label}\n </span>\n );\n}\n"],"names":["useMemo","createTurnPalette","cn","jsx","EmptyState","SessionViewer","jsxs","Fragment","shortSessionId"],"mappings":";;;;;;;;AAsBA,MAAM,mBAAgD;AAAA,EACpD,KAAK;AAAA,EACL,QACE;AAAA,EACF,SACE;AAAA,EACF,QACE;AAAA,EACF,MAAM;AAAA,EACN,QACE;AAAA,EACF,MAAM;AAAA,EACN,SACE;AACJ;AAEO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AACF,GAGG;;AACD,QAAM,QAAQ,QAAQ,SAAS,CAAA;AAC/B,QAAM,UAAUA,MAAAA;AAAAA,IACd,MACEC,uBAAAA;AAAAA,MACE,QAAQ;AAAA,MACR,MAAM,IAAI,CAAC,SAAS,KAAK,EAAE;AAAA,IAAA;AAAA,IAE/B,CAAC,QAAQ,IAAI,KAAK;AAAA,EAAA;AAEpB,QAAM,aAAaD,MAAAA;AAAAA,IACjB,MAAM,gBAAgB,OAAO,OAAO;AAAA,IACpC,CAAC,SAAS,KAAK;AAAA,EAAA;AAEjB,QAAM,gBAAgBA,MAAAA;AAAAA,IACpB,MAAM,IAAI,IAAI,WAAW,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AAAA,IAC1D,CAAC,UAAU;AAAA,EAAA;AAEb,QAAM,kBAAkBE,MAAAA,GAAG,kBAAkB,2CAAa,SAAS;AACnE,QAAM,iBAAiB,QAAQ,OAAM,2CAAa;AAElD,MACE,QAAQ,oBAAoB,SAC5B,GAAC,aAAQ,aAAR,mBAAkB,WACnB,GAAC,aAAQ,WAAR,mBAAgB,SACjB;AACA,WAAOC,2BAAAA,IAACC,4BAAAA,cAAW,UAAA,yBAAA,CAAsB;AAAA,EAC3C;AAEA,SACED,2BAAAA,IAAC,OAAA,EAAI,WAAU,gCACb,UAAAA,2BAAAA;AAAAA,IAACE,cAAAA;AAAAA,IAAA;AAAA,MACC;AAAA,MACA,YAAU;AAAA,MACT,GAAG;AAAA,MACJ,aAAY,2CAAa,eAAc;AAAA,MACvC,YAAW,2CAAa,cAAa;AAAA,MACpC,GAAI,mBAAmB,SAAY,EAAE,eAAA,IAAmB,CAAA;AAAA,MACzD,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,kBAAkB;AAAA,MAClB,iBAAiB;AAAA,MACjB,oBAAoB,CAAC,UAAwB;AAC3C,cAAM,QAAQ,MAAM,SAChB,cAAc,IAAI,MAAM,MAAM,IAC9B;AACJ,eAAO,QACLF,2BAAAA,IAAC,qBAAA,EAAoB,OAAc,WAAU,WAAU,IACrD;AAAA,MACN;AAAA,MACA,eACEG,2BAAAA,KAAAC,qBAAA,EACG,UAAA;AAAA,QAAA,2CAAa;AAAA,QACdJ,2BAAAA,IAAC,iBAAA,EAAgB,QAAQ,WAAA,CAAY;AAAA,MAAA,EAAA,CACvC;AAAA,IAAA;AAAA,EAAA,GAGN;AAEJ;AAEA,SAAS,gBACP,OACA,SACA;AACA,SAAO,MAAM,IAAI,CAAC,SAAoB;AACpC,UAAM,QAAQ,QAAQ,KAAK,EAAE,KAAK;AAClC,WAAO;AAAA,MACL,IAAI,KAAK;AAAA,MACT,OAAOK,uBAAAA,eAAe,KAAK,EAAE;AAAA,MAC7B;AAAA,MACA,WAAW,iBAAiB,KAAK;AAAA,IAAA;AAAA,EAErC,CAAC;AACH;AAEA,SAAS,gBAAgB,EAAE,UAAmC;AAC5D,wCACG,OAAA,EAAI,WAAU,mCAAkC,cAAW,eACzD,iBAAO,IAAI,CAAC,UACXL,2BAAAA,IAAC,uBAAmC,OAAc,WAAU,YAAlC,MAAM,EAAqC,CACtE,GACH;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AACF,GAGG;AACD,SACEA,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,eACE,cAAc,WAAW,0BAA0B;AAAA,MAErD,mBAAiB,MAAM;AAAA,MACvB,OAAO,QAAQ,MAAM,EAAE;AAAA,MACvB,WAAWD,MAAAA;AAAAA,QACT;AAAA,QACA,cAAc,aAAa;AAAA,QAC3B,MAAM;AAAA,MAAA;AAAA,MAGP,UAAA,MAAM;AAAA,IAAA;AAAA,EAAA;AAGb;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionInspector.transcript.d.ts","sourceRoot":"","sources":["../../../src/data/ai/SessionInspector.transcript.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SessionInspector.transcript.d.ts","sourceRoot":"","sources":["../../../src/data/ai/SessionInspector.transcript.tsx"],"names":[],"mappings":"AAQA,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAyBhF,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,WAAW,GACZ,EAAE;IACD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,WAAW,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;CACnD,+BA2DA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
|
+
import { EmptyState } from "./SessionInspector.panel-parts.js";
|
|
3
4
|
import { cn } from "../../lib/utils.js";
|
|
4
5
|
import { createTurnPalette, shortSessionId } from "./SessionInspector.model.js";
|
|
5
6
|
import { SessionViewer } from "./SessionViewer.js";
|
|
@@ -17,6 +18,7 @@ function SessionTranscript({
|
|
|
17
18
|
session,
|
|
18
19
|
viewerProps
|
|
19
20
|
}) {
|
|
21
|
+
var _a, _b;
|
|
20
22
|
const turns = session.turns ?? [];
|
|
21
23
|
const palette = useMemo(
|
|
22
24
|
() => createTurnPalette(
|
|
@@ -35,6 +37,9 @@ function SessionTranscript({
|
|
|
35
37
|
);
|
|
36
38
|
const viewerClassName = cn("min-h-0 flex-1", viewerProps == null ? void 0 : viewerProps.className);
|
|
37
39
|
const scrollResetKey = session.id ?? (viewerProps == null ? void 0 : viewerProps.scrollResetKey);
|
|
40
|
+
if (session.detailAvailable === false && !((_a = session.messages) == null ? void 0 : _a.length) && !((_b = session.events) == null ? void 0 : _b.length)) {
|
|
41
|
+
return /* @__PURE__ */ jsx(EmptyState, { children: "Transcript unavailable" });
|
|
42
|
+
}
|
|
38
43
|
return /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-0 flex-col", children: /* @__PURE__ */ jsx(
|
|
39
44
|
SessionViewer,
|
|
40
45
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionInspector.transcript.js","sources":["../../../src/data/ai/SessionInspector.transcript.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport {\n createTurnPalette,\n shortSessionId,\n type TurnPalette,\n} from \"./SessionInspector.model\";\nimport {\n SessionViewer,\n type SessionEvent,\n type SessionViewerProps,\n} from \"./SessionViewer\";\nimport type { SessionTurn, UnifiedSessionInput } from \"./SessionViewer.unified\";\n\ntype TurnBadge = {\n id: string;\n label: string;\n color: TurnPalette;\n className: string;\n};\n\nconst TURN_COLOR_CLASS: Record<TurnPalette, string> = {\n sky: \"border-sky-300 bg-sky-100 text-sky-800 [[data-theme=dark]_&]:border-sky-700 [[data-theme=dark]_&]:bg-sky-950/60 [[data-theme=dark]_&]:text-sky-300\",\n violet:\n \"border-violet-300 bg-violet-100 text-violet-800 [[data-theme=dark]_&]:border-violet-700 [[data-theme=dark]_&]:bg-violet-950/60 [[data-theme=dark]_&]:text-violet-300\",\n emerald:\n \"border-emerald-300 bg-emerald-100 text-emerald-800 [[data-theme=dark]_&]:border-emerald-700 [[data-theme=dark]_&]:bg-emerald-950/60 [[data-theme=dark]_&]:text-emerald-300\",\n orange:\n \"border-orange-300 bg-orange-100 text-orange-800 [[data-theme=dark]_&]:border-orange-700 [[data-theme=dark]_&]:bg-orange-950/60 [[data-theme=dark]_&]:text-orange-300\",\n rose: \"border-rose-300 bg-rose-100 text-rose-800 [[data-theme=dark]_&]:border-rose-700 [[data-theme=dark]_&]:bg-rose-950/60 [[data-theme=dark]_&]:text-rose-300\",\n indigo:\n \"border-indigo-300 bg-indigo-100 text-indigo-800 [[data-theme=dark]_&]:border-indigo-700 [[data-theme=dark]_&]:bg-indigo-950/60 [[data-theme=dark]_&]:text-indigo-300\",\n teal: \"border-teal-300 bg-teal-100 text-teal-800 [[data-theme=dark]_&]:border-teal-700 [[data-theme=dark]_&]:bg-teal-950/60 [[data-theme=dark]_&]:text-teal-300\",\n fuchsia:\n \"border-fuchsia-300 bg-fuchsia-100 text-fuchsia-800 [[data-theme=dark]_&]:border-fuchsia-700 [[data-theme=dark]_&]:bg-fuchsia-950/60 [[data-theme=dark]_&]:text-fuchsia-300\",\n};\n\nexport function SessionTranscript({\n session,\n viewerProps,\n}: {\n session: UnifiedSessionInput;\n viewerProps?: Omit<SessionViewerProps, \"session\">;\n}) {\n const turns = session.turns ?? [];\n const palette = useMemo(\n () =>\n createTurnPalette(\n session.id,\n turns.map((turn) => turn.id)
|
|
1
|
+
{"version":3,"file":"SessionInspector.transcript.js","sources":["../../../src/data/ai/SessionInspector.transcript.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { EmptyState } from \"./SessionInspector.panel-parts\";\nimport { cn } from \"../../lib/utils\";\nimport {\n createTurnPalette,\n shortSessionId,\n type TurnPalette,\n} from \"./SessionInspector.model\";\nimport {\n SessionViewer,\n type SessionEvent,\n type SessionViewerProps,\n} from \"./SessionViewer\";\nimport type { SessionTurn, UnifiedSessionInput } from \"./SessionViewer.unified\";\n\ntype TurnBadge = {\n id: string;\n label: string;\n color: TurnPalette;\n className: string;\n};\n\nconst TURN_COLOR_CLASS: Record<TurnPalette, string> = {\n sky: \"border-sky-300 bg-sky-100 text-sky-800 [[data-theme=dark]_&]:border-sky-700 [[data-theme=dark]_&]:bg-sky-950/60 [[data-theme=dark]_&]:text-sky-300\",\n violet:\n \"border-violet-300 bg-violet-100 text-violet-800 [[data-theme=dark]_&]:border-violet-700 [[data-theme=dark]_&]:bg-violet-950/60 [[data-theme=dark]_&]:text-violet-300\",\n emerald:\n \"border-emerald-300 bg-emerald-100 text-emerald-800 [[data-theme=dark]_&]:border-emerald-700 [[data-theme=dark]_&]:bg-emerald-950/60 [[data-theme=dark]_&]:text-emerald-300\",\n orange:\n \"border-orange-300 bg-orange-100 text-orange-800 [[data-theme=dark]_&]:border-orange-700 [[data-theme=dark]_&]:bg-orange-950/60 [[data-theme=dark]_&]:text-orange-300\",\n rose: \"border-rose-300 bg-rose-100 text-rose-800 [[data-theme=dark]_&]:border-rose-700 [[data-theme=dark]_&]:bg-rose-950/60 [[data-theme=dark]_&]:text-rose-300\",\n indigo:\n \"border-indigo-300 bg-indigo-100 text-indigo-800 [[data-theme=dark]_&]:border-indigo-700 [[data-theme=dark]_&]:bg-indigo-950/60 [[data-theme=dark]_&]:text-indigo-300\",\n teal: \"border-teal-300 bg-teal-100 text-teal-800 [[data-theme=dark]_&]:border-teal-700 [[data-theme=dark]_&]:bg-teal-950/60 [[data-theme=dark]_&]:text-teal-300\",\n fuchsia:\n \"border-fuchsia-300 bg-fuchsia-100 text-fuchsia-800 [[data-theme=dark]_&]:border-fuchsia-700 [[data-theme=dark]_&]:bg-fuchsia-950/60 [[data-theme=dark]_&]:text-fuchsia-300\",\n};\n\nexport function SessionTranscript({\n session,\n viewerProps,\n}: {\n session: UnifiedSessionInput;\n viewerProps?: Omit<SessionViewerProps, \"session\">;\n}) {\n const turns = session.turns ?? [];\n const palette = useMemo(\n () =>\n createTurnPalette(\n session.id,\n turns.map((turn) => turn.id)\n ),\n [session.id, turns]\n );\n const turnBadges = useMemo(\n () => buildTurnBadges(turns, palette),\n [palette, turns]\n );\n const turnBadgeById = useMemo(\n () => new Map(turnBadges.map((badge) => [badge.id, badge])),\n [turnBadges]\n );\n const viewerClassName = cn(\"min-h-0 flex-1\", viewerProps?.className);\n const scrollResetKey = session.id ?? viewerProps?.scrollResetKey;\n\n if (\n session.detailAvailable === false &&\n !session.messages?.length &&\n !session.events?.length\n ) {\n return <EmptyState>Transcript unavailable</EmptyState>;\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <SessionViewer\n session={session}\n scrollable\n {...viewerProps}\n windowSize={viewerProps?.windowSize ?? 10}\n batchSize={viewerProps?.batchSize ?? 10}\n {...(scrollResetKey !== undefined ? { scrollResetKey } : {})}\n className={viewerClassName}\n showHeader={false}\n showContextMeter={false}\n showRowMetadata={false}\n renderMessageBadge={(event: SessionEvent) => {\n const badge = event.turnId\n ? turnBadgeById.get(event.turnId)\n : undefined;\n return badge ? (\n <TranscriptTurnBadge badge={badge} placement=\"message\" />\n ) : null;\n }}\n headerActions={\n <>\n {viewerProps?.headerActions}\n <TurnBadgeLegend badges={turnBadges} />\n </>\n }\n />\n </div>\n );\n}\n\nfunction buildTurnBadges(\n turns: SessionTurn[],\n palette: Record<string, TurnPalette>\n) {\n return turns.map((turn): TurnBadge => {\n const color = palette[turn.id] ?? \"sky\";\n return {\n id: turn.id,\n label: shortSessionId(turn.id),\n color,\n className: TURN_COLOR_CLASS[color],\n };\n });\n}\n\nfunction TurnBadgeLegend({ badges }: { badges: TurnBadge[] }) {\n return (\n <div className=\"flex max-w-full flex-wrap gap-1\" aria-label=\"Turn colors\">\n {badges.map((badge) => (\n <TranscriptTurnBadge key={badge.id} badge={badge} placement=\"legend\" />\n ))}\n </div>\n );\n}\n\nfunction TranscriptTurnBadge({\n badge,\n placement,\n}: {\n badge: TurnBadge;\n placement: \"legend\" | \"message\";\n}) {\n return (\n <span\n data-testid={\n placement === \"legend\" ? \"transcript-turn-badge\" : \"message-turn-badge\"\n }\n data-turn-color={badge.color}\n title={`Turn ${badge.id}`}\n className={cn(\n \"inline-flex shrink-0 rounded border px-1 py-px font-mono text-[9px] font-semibold leading-none\",\n placement === \"message\" && \"mt-1\",\n badge.className\n )}\n >\n {badge.label}\n </span>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const react = require("react");
|
|
5
|
+
const utils = require("../../lib/utils.cjs");
|
|
6
|
+
const Icon = require("../Icon.cjs");
|
|
7
|
+
const sessionCategories = require("./session-categories.cjs");
|
|
8
|
+
const SessionInspector_remote = require("./SessionInspector.remote.cjs");
|
|
9
|
+
const SessionViewer_model = require("./SessionViewer.model.cjs");
|
|
10
|
+
const SessionViewer_input = require("./SessionViewer.input.cjs");
|
|
11
|
+
const UiLoader = require("../../icons/components/UiLoader.cjs");
|
|
12
|
+
const UiSparkles = require("../../icons/components/UiSparkles.cjs");
|
|
13
|
+
const TAIL_PAGE_SIZE = 20;
|
|
14
|
+
function latestTailEvent(events) {
|
|
15
|
+
return events.findLast(
|
|
16
|
+
(event) => event.kind === "tool" || event.kind === "assistant" && Boolean(event.text)
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function ToolLine({ event }) {
|
|
20
|
+
const tool = event.tool ?? "";
|
|
21
|
+
const summary = SessionViewer_input.summarizeToolInput(tool, event.toolInput, event.cwd);
|
|
22
|
+
const category = sessionCategories.classifyToolCategory(tool, event.toolInput);
|
|
23
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
24
|
+
"span",
|
|
25
|
+
{
|
|
26
|
+
"data-testid": "session-tail-tool",
|
|
27
|
+
title: summary ? `${tool} ${summary}` : tool,
|
|
28
|
+
className: "flex min-w-0 items-center gap-1.5",
|
|
29
|
+
children: [
|
|
30
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: sessionCategories.CATEGORY_ICONS[category], className: "size-3.5 shrink-0" }),
|
|
31
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-medium text-foreground", children: tool }),
|
|
32
|
+
summary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate font-mono", children: summary })
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
function TextLine({ text }) {
|
|
38
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
39
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiSparkles.UiSparkles, className: "size-3.5 shrink-0" }),
|
|
40
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": "session-tail-text", title: text, className: "min-w-0 truncate", children: text })
|
|
41
|
+
] });
|
|
42
|
+
}
|
|
43
|
+
function SessionTail({ src, follow = true, className, emptyLabel }) {
|
|
44
|
+
const { session, error } = SessionInspector_remote.useRemoteSession(src, { follow, pageSize: TAIL_PAGE_SIZE });
|
|
45
|
+
const latest = react.useMemo(
|
|
46
|
+
() => session ? latestTailEvent(SessionViewer_model.normalizeMessages(session.messages ?? [])) : void 0,
|
|
47
|
+
[session]
|
|
48
|
+
);
|
|
49
|
+
const following = follow && !SessionInspector_remote.isTerminalSessionLifecycle(session == null ? void 0 : session.lifecycleStatus);
|
|
50
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51
|
+
"div",
|
|
52
|
+
{
|
|
53
|
+
"data-session-tail": true,
|
|
54
|
+
className: utils.cn("flex min-w-0 items-center gap-2 text-xs text-muted-foreground", className),
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: error !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", title: error, className: "block truncate text-destructive", children: error }) : (latest == null ? void 0 : latest.kind) === "tool" ? /* @__PURE__ */ jsxRuntime.jsx(ToolLine, { event: latest }) : (latest == null ? void 0 : latest.text) ? /* @__PURE__ */ jsxRuntime.jsx(TextLine, { text: latest.text }) : emptyLabel !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate", children: emptyLabel }) }),
|
|
57
|
+
following && /* @__PURE__ */ jsxRuntime.jsx("span", { role: "status", "aria-label": "Following session", className: "shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiLoader.UiLoader, className: "size-3.5 animate-spin" }) })
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
exports.SessionTail = SessionTail;
|
|
63
|
+
//# sourceMappingURL=SessionTail.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionTail.cjs","sources":["../../../src/data/ai/SessionTail.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiLoader, UiSparkles } from \"../../icons\";\nimport { CATEGORY_ICONS, classifyToolCategory } from \"./session-categories\";\nimport { isTerminalSessionLifecycle, useRemoteSession } from \"./SessionInspector.remote\";\nimport { normalizeMessages, type SessionEvent } from \"./SessionViewer.model\";\nimport { summarizeToolInput } from \"./SessionViewer.input\";\n\n/** Messages read for the first load: enough to reach past a run of reasoning\n * or empty messages to the latest text or tool call. */\nconst TAIL_PAGE_SIZE = 20;\n\nexport interface SessionTailProps {\n /** Captain session URL, e.g. `/api/captain/sessions/{id}`. */\n src: string;\n /** Follow the live stream (default true). When false the session is read\n * once and no spinner is shown. */\n follow?: boolean;\n className?: string;\n /** Shown until the session has a text message or tool call. */\n emptyLabel?: string;\n}\n\n/** The newest assistant text or tool call; reasoning, user, system and error\n * rows are not part of the tail. */\nfunction latestTailEvent(events: SessionEvent[]): SessionEvent | undefined {\n return events.findLast(\n (event) => event.kind === \"tool\" || (event.kind === \"assistant\" && Boolean(event.text)),\n );\n}\n\nfunction ToolLine({ event }: { event: SessionEvent }) {\n const tool = event.tool ?? \"\";\n const summary = summarizeToolInput(tool, event.toolInput, event.cwd);\n const category = classifyToolCategory(tool, event.toolInput);\n return (\n <span\n data-testid=\"session-tail-tool\"\n title={summary ? `${tool} ${summary}` : tool}\n className=\"flex min-w-0 items-center gap-1.5\"\n >\n <Icon icon={CATEGORY_ICONS[category]} className=\"size-3.5 shrink-0\" />\n <span className=\"shrink-0 font-medium text-foreground\">{tool}</span>\n {summary && <span className=\"min-w-0 truncate font-mono\">{summary}</span>}\n </span>\n );\n}\n\nfunction TextLine({ text }: { text: string }) {\n return (\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <Icon icon={UiSparkles} className=\"size-3.5 shrink-0\" />\n <span data-testid=\"session-tail-text\" title={text} className=\"min-w-0 truncate\">\n {text}\n </span>\n </span>\n );\n}\n\n/** One line showing only the newest entry of a captain session: the latest\n * assistant text or the latest tool call, with a spinner while following. */\nexport function SessionTail({ src, follow = true, className, emptyLabel }: SessionTailProps) {\n const { session, error } = useRemoteSession(src, { follow, pageSize: TAIL_PAGE_SIZE });\n const latest = useMemo(\n () => (session ? latestTailEvent(normalizeMessages(session.messages ?? [])) : undefined),\n [session],\n );\n const following = follow && !isTerminalSessionLifecycle(session?.lifecycleStatus);\n\n return (\n <div\n data-session-tail\n className={cn(\"flex min-w-0 items-center gap-2 text-xs text-muted-foreground\", className)}\n >\n <div className=\"min-w-0 flex-1\">\n {error !== undefined ? (\n <span role=\"alert\" title={error} className=\"block truncate text-destructive\">\n {error}\n </span>\n ) : latest?.kind === \"tool\" ? (\n <ToolLine event={latest} />\n ) : latest?.text ? (\n <TextLine text={latest.text} />\n ) : (\n emptyLabel !== undefined && <span className=\"block truncate\">{emptyLabel}</span>\n )}\n </div>\n {following && (\n <span role=\"status\" aria-label=\"Following session\" className=\"shrink-0\">\n <Icon icon={UiLoader} className=\"size-3.5 animate-spin\" />\n </span>\n )}\n </div>\n );\n}\n"],"names":["summarizeToolInput","classifyToolCategory","jsxs","jsx","Icon","CATEGORY_ICONS","UiSparkles","useRemoteSession","useMemo","normalizeMessages","isTerminalSessionLifecycle","cn","UiLoader"],"mappings":";;;;;;;;;;;;AAWA,MAAM,iBAAiB;AAevB,SAAS,gBAAgB,QAAkD;AACzE,SAAO,OAAO;AAAA,IACZ,CAAC,UAAU,MAAM,SAAS,UAAW,MAAM,SAAS,eAAe,QAAQ,MAAM,IAAI;AAAA,EAAA;AAEzF;AAEA,SAAS,SAAS,EAAE,SAAkC;AACpD,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,UAAUA,oBAAAA,mBAAmB,MAAM,MAAM,WAAW,MAAM,GAAG;AACnE,QAAM,WAAWC,kBAAAA,qBAAqB,MAAM,MAAM,SAAS;AAC3D,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY;AAAA,MACZ,OAAO,UAAU,GAAG,IAAI,IAAI,OAAO,KAAK;AAAA,MACxC,WAAU;AAAA,MAEV,UAAA;AAAA,QAAAC,+BAACC,KAAAA,QAAK,MAAMC,kBAAAA,eAAe,QAAQ,GAAG,WAAU,qBAAoB;AAAA,QACpEF,2BAAAA,IAAC,QAAA,EAAK,WAAU,wCAAwC,UAAA,MAAK;AAAA,QAC5D,WAAWA,2BAAAA,IAAC,QAAA,EAAK,WAAU,8BAA8B,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGxE;AAEA,SAAS,SAAS,EAAE,QAA0B;AAC5C,SACED,2BAAAA,KAAC,QAAA,EAAK,WAAU,qCACd,UAAA;AAAA,IAAAC,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAME,WAAAA,YAAY,WAAU,qBAAoB;AAAA,IACtDH,2BAAAA,IAAC,UAAK,eAAY,qBAAoB,OAAO,MAAM,WAAU,oBAC1D,UAAA,KAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAIO,SAAS,YAAY,EAAE,KAAK,SAAS,MAAM,WAAW,cAAgC;AAC3F,QAAM,EAAE,SAAS,MAAA,IAAUI,wBAAAA,iBAAiB,KAAK,EAAE,QAAQ,UAAU,gBAAgB;AACrF,QAAM,SAASC,MAAAA;AAAAA,IACb,MAAO,UAAU,gBAAgBC,oBAAAA,kBAAkB,QAAQ,YAAY,CAAA,CAAE,CAAC,IAAI;AAAA,IAC9E,CAAC,OAAO;AAAA,EAAA;AAEV,QAAM,YAAY,UAAU,CAACC,wBAAAA,2BAA2B,mCAAS,eAAe;AAEhF,SACER,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,qBAAiB;AAAA,MACjB,WAAWS,MAAAA,GAAG,iEAAiE,SAAS;AAAA,MAExF,UAAA;AAAA,QAAAR,+BAAC,SAAI,WAAU,kBACZ,UAAA,UAAU,wCACR,QAAA,EAAK,MAAK,SAAQ,OAAO,OAAO,WAAU,mCACxC,iBACH,KACE,iCAAQ,UAAS,SACnBA,2BAAAA,IAAC,UAAA,EAAS,OAAO,QAAQ,KACvB,iCAAQ,QACVA,2BAAAA,IAAC,YAAS,MAAM,OAAO,MAAM,IAE7B,eAAe,UAAaA,2BAAAA,IAAC,UAAK,WAAU,kBAAkB,sBAAW,GAE7E;AAAA,QACC,aACCA,2BAAAA,IAAC,QAAA,EAAK,MAAK,UAAS,cAAW,qBAAoB,WAAU,YAC3D,yCAACC,KAAAA,MAAA,EAAK,MAAMQ,SAAAA,UAAU,WAAU,yBAAwB,EAAA,CAC1D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;;"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SessionTailProps {
|
|
2
|
+
/** Captain session URL, e.g. `/api/captain/sessions/{id}`. */
|
|
3
|
+
src: string;
|
|
4
|
+
/** Follow the live stream (default true). When false the session is read
|
|
5
|
+
* once and no spinner is shown. */
|
|
6
|
+
follow?: boolean;
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Shown until the session has a text message or tool call. */
|
|
9
|
+
emptyLabel?: string;
|
|
10
|
+
}
|
|
11
|
+
/** One line showing only the newest entry of a captain session: the latest
|
|
12
|
+
* assistant text or the latest tool call, with a spinner while following. */
|
|
13
|
+
export declare function SessionTail({ src, follow, className, emptyLabel }: SessionTailProps): import("react").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=SessionTail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionTail.d.ts","sourceRoot":"","sources":["../../../src/data/ai/SessionTail.tsx"],"names":[],"mappings":"AAaA,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ;wCACoC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAsCD;8EAC8E;AAC9E,wBAAgB,WAAW,CAAC,EAAE,GAAG,EAAE,MAAa,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,gBAAgB,+BAiC1F"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { Icon } from "../Icon.js";
|
|
5
|
+
import { classifyToolCategory, CATEGORY_ICONS } from "./session-categories.js";
|
|
6
|
+
import { useRemoteSession, isTerminalSessionLifecycle } from "./SessionInspector.remote.js";
|
|
7
|
+
import { normalizeMessages } from "./SessionViewer.model.js";
|
|
8
|
+
import { summarizeToolInput } from "./SessionViewer.input.js";
|
|
9
|
+
import { UiLoader } from "../../icons/components/UiLoader.js";
|
|
10
|
+
import { UiSparkles } from "../../icons/components/UiSparkles.js";
|
|
11
|
+
const TAIL_PAGE_SIZE = 20;
|
|
12
|
+
function latestTailEvent(events) {
|
|
13
|
+
return events.findLast(
|
|
14
|
+
(event) => event.kind === "tool" || event.kind === "assistant" && Boolean(event.text)
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
function ToolLine({ event }) {
|
|
18
|
+
const tool = event.tool ?? "";
|
|
19
|
+
const summary = summarizeToolInput(tool, event.toolInput, event.cwd);
|
|
20
|
+
const category = classifyToolCategory(tool, event.toolInput);
|
|
21
|
+
return /* @__PURE__ */ jsxs(
|
|
22
|
+
"span",
|
|
23
|
+
{
|
|
24
|
+
"data-testid": "session-tail-tool",
|
|
25
|
+
title: summary ? `${tool} ${summary}` : tool,
|
|
26
|
+
className: "flex min-w-0 items-center gap-1.5",
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ jsx(Icon, { icon: CATEGORY_ICONS[category], className: "size-3.5 shrink-0" }),
|
|
29
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 font-medium text-foreground", children: tool }),
|
|
30
|
+
summary && /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-mono", children: summary })
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function TextLine({ text }) {
|
|
36
|
+
return /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
37
|
+
/* @__PURE__ */ jsx(Icon, { icon: UiSparkles, className: "size-3.5 shrink-0" }),
|
|
38
|
+
/* @__PURE__ */ jsx("span", { "data-testid": "session-tail-text", title: text, className: "min-w-0 truncate", children: text })
|
|
39
|
+
] });
|
|
40
|
+
}
|
|
41
|
+
function SessionTail({ src, follow = true, className, emptyLabel }) {
|
|
42
|
+
const { session, error } = useRemoteSession(src, { follow, pageSize: TAIL_PAGE_SIZE });
|
|
43
|
+
const latest = useMemo(
|
|
44
|
+
() => session ? latestTailEvent(normalizeMessages(session.messages ?? [])) : void 0,
|
|
45
|
+
[session]
|
|
46
|
+
);
|
|
47
|
+
const following = follow && !isTerminalSessionLifecycle(session == null ? void 0 : session.lifecycleStatus);
|
|
48
|
+
return /* @__PURE__ */ jsxs(
|
|
49
|
+
"div",
|
|
50
|
+
{
|
|
51
|
+
"data-session-tail": true,
|
|
52
|
+
className: cn("flex min-w-0 items-center gap-2 text-xs text-muted-foreground", className),
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: error !== void 0 ? /* @__PURE__ */ jsx("span", { role: "alert", title: error, className: "block truncate text-destructive", children: error }) : (latest == null ? void 0 : latest.kind) === "tool" ? /* @__PURE__ */ jsx(ToolLine, { event: latest }) : (latest == null ? void 0 : latest.text) ? /* @__PURE__ */ jsx(TextLine, { text: latest.text }) : emptyLabel !== void 0 && /* @__PURE__ */ jsx("span", { className: "block truncate", children: emptyLabel }) }),
|
|
55
|
+
following && /* @__PURE__ */ jsx("span", { role: "status", "aria-label": "Following session", className: "shrink-0", children: /* @__PURE__ */ jsx(Icon, { icon: UiLoader, className: "size-3.5 animate-spin" }) })
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
SessionTail
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=SessionTail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionTail.js","sources":["../../../src/data/ai/SessionTail.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiLoader, UiSparkles } from \"../../icons\";\nimport { CATEGORY_ICONS, classifyToolCategory } from \"./session-categories\";\nimport { isTerminalSessionLifecycle, useRemoteSession } from \"./SessionInspector.remote\";\nimport { normalizeMessages, type SessionEvent } from \"./SessionViewer.model\";\nimport { summarizeToolInput } from \"./SessionViewer.input\";\n\n/** Messages read for the first load: enough to reach past a run of reasoning\n * or empty messages to the latest text or tool call. */\nconst TAIL_PAGE_SIZE = 20;\n\nexport interface SessionTailProps {\n /** Captain session URL, e.g. `/api/captain/sessions/{id}`. */\n src: string;\n /** Follow the live stream (default true). When false the session is read\n * once and no spinner is shown. */\n follow?: boolean;\n className?: string;\n /** Shown until the session has a text message or tool call. */\n emptyLabel?: string;\n}\n\n/** The newest assistant text or tool call; reasoning, user, system and error\n * rows are not part of the tail. */\nfunction latestTailEvent(events: SessionEvent[]): SessionEvent | undefined {\n return events.findLast(\n (event) => event.kind === \"tool\" || (event.kind === \"assistant\" && Boolean(event.text)),\n );\n}\n\nfunction ToolLine({ event }: { event: SessionEvent }) {\n const tool = event.tool ?? \"\";\n const summary = summarizeToolInput(tool, event.toolInput, event.cwd);\n const category = classifyToolCategory(tool, event.toolInput);\n return (\n <span\n data-testid=\"session-tail-tool\"\n title={summary ? `${tool} ${summary}` : tool}\n className=\"flex min-w-0 items-center gap-1.5\"\n >\n <Icon icon={CATEGORY_ICONS[category]} className=\"size-3.5 shrink-0\" />\n <span className=\"shrink-0 font-medium text-foreground\">{tool}</span>\n {summary && <span className=\"min-w-0 truncate font-mono\">{summary}</span>}\n </span>\n );\n}\n\nfunction TextLine({ text }: { text: string }) {\n return (\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <Icon icon={UiSparkles} className=\"size-3.5 shrink-0\" />\n <span data-testid=\"session-tail-text\" title={text} className=\"min-w-0 truncate\">\n {text}\n </span>\n </span>\n );\n}\n\n/** One line showing only the newest entry of a captain session: the latest\n * assistant text or the latest tool call, with a spinner while following. */\nexport function SessionTail({ src, follow = true, className, emptyLabel }: SessionTailProps) {\n const { session, error } = useRemoteSession(src, { follow, pageSize: TAIL_PAGE_SIZE });\n const latest = useMemo(\n () => (session ? latestTailEvent(normalizeMessages(session.messages ?? [])) : undefined),\n [session],\n );\n const following = follow && !isTerminalSessionLifecycle(session?.lifecycleStatus);\n\n return (\n <div\n data-session-tail\n className={cn(\"flex min-w-0 items-center gap-2 text-xs text-muted-foreground\", className)}\n >\n <div className=\"min-w-0 flex-1\">\n {error !== undefined ? (\n <span role=\"alert\" title={error} className=\"block truncate text-destructive\">\n {error}\n </span>\n ) : latest?.kind === \"tool\" ? (\n <ToolLine event={latest} />\n ) : latest?.text ? (\n <TextLine text={latest.text} />\n ) : (\n emptyLabel !== undefined && <span className=\"block truncate\">{emptyLabel}</span>\n )}\n </div>\n {following && (\n <span role=\"status\" aria-label=\"Following session\" className=\"shrink-0\">\n <Icon icon={UiLoader} className=\"size-3.5 animate-spin\" />\n </span>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAWA,MAAM,iBAAiB;AAevB,SAAS,gBAAgB,QAAkD;AACzE,SAAO,OAAO;AAAA,IACZ,CAAC,UAAU,MAAM,SAAS,UAAW,MAAM,SAAS,eAAe,QAAQ,MAAM,IAAI;AAAA,EAAA;AAEzF;AAEA,SAAS,SAAS,EAAE,SAAkC;AACpD,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,UAAU,mBAAmB,MAAM,MAAM,WAAW,MAAM,GAAG;AACnE,QAAM,WAAW,qBAAqB,MAAM,MAAM,SAAS;AAC3D,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAY;AAAA,MACZ,OAAO,UAAU,GAAG,IAAI,IAAI,OAAO,KAAK;AAAA,MACxC,WAAU;AAAA,MAEV,UAAA;AAAA,QAAA,oBAAC,QAAK,MAAM,eAAe,QAAQ,GAAG,WAAU,qBAAoB;AAAA,QACpE,oBAAC,QAAA,EAAK,WAAU,wCAAwC,UAAA,MAAK;AAAA,QAC5D,WAAW,oBAAC,QAAA,EAAK,WAAU,8BAA8B,UAAA,QAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGxE;AAEA,SAAS,SAAS,EAAE,QAA0B;AAC5C,SACE,qBAAC,QAAA,EAAK,WAAU,qCACd,UAAA;AAAA,IAAA,oBAAC,MAAA,EAAK,MAAM,YAAY,WAAU,qBAAoB;AAAA,IACtD,oBAAC,UAAK,eAAY,qBAAoB,OAAO,MAAM,WAAU,oBAC1D,UAAA,KAAA,CACH;AAAA,EAAA,GACF;AAEJ;AAIO,SAAS,YAAY,EAAE,KAAK,SAAS,MAAM,WAAW,cAAgC;AAC3F,QAAM,EAAE,SAAS,MAAA,IAAU,iBAAiB,KAAK,EAAE,QAAQ,UAAU,gBAAgB;AACrF,QAAM,SAAS;AAAA,IACb,MAAO,UAAU,gBAAgB,kBAAkB,QAAQ,YAAY,CAAA,CAAE,CAAC,IAAI;AAAA,IAC9E,CAAC,OAAO;AAAA,EAAA;AAEV,QAAM,YAAY,UAAU,CAAC,2BAA2B,mCAAS,eAAe;AAEhF,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,qBAAiB;AAAA,MACjB,WAAW,GAAG,iEAAiE,SAAS;AAAA,MAExF,UAAA;AAAA,QAAA,oBAAC,SAAI,WAAU,kBACZ,UAAA,UAAU,6BACR,QAAA,EAAK,MAAK,SAAQ,OAAO,OAAO,WAAU,mCACxC,iBACH,KACE,iCAAQ,UAAS,SACnB,oBAAC,UAAA,EAAS,OAAO,QAAQ,KACvB,iCAAQ,QACV,oBAAC,YAAS,MAAM,OAAO,MAAM,IAE7B,eAAe,UAAa,oBAAC,UAAK,WAAU,kBAAkB,sBAAW,GAE7E;AAAA,QACC,aACC,oBAAC,QAAA,EAAK,MAAK,UAAS,cAAW,qBAAoB,WAAU,YAC3D,8BAAC,MAAA,EAAK,MAAM,UAAU,WAAU,yBAAwB,EAAA,CAC1D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;"}
|
|
@@ -41,6 +41,8 @@ function SessionViewer({
|
|
|
41
41
|
windowSize = 60,
|
|
42
42
|
batchSize = 40,
|
|
43
43
|
scrollResetKey,
|
|
44
|
+
onReachStart,
|
|
45
|
+
loadingOlder = false,
|
|
44
46
|
showRowMetadata = false,
|
|
45
47
|
showRaw = false,
|
|
46
48
|
renderMessageBadge,
|
|
@@ -98,7 +100,9 @@ function SessionViewer({
|
|
|
98
100
|
enabled: scrollable,
|
|
99
101
|
windowSize,
|
|
100
102
|
batchSize,
|
|
101
|
-
resetKey
|
|
103
|
+
resetKey,
|
|
104
|
+
...allEvents[0] ? { firstKey: allEvents[0].id } : {},
|
|
105
|
+
...onReachStart ? { onReachStart } : {}
|
|
102
106
|
});
|
|
103
107
|
if (allEvents.length === 0) {
|
|
104
108
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -237,13 +241,16 @@ function SessionViewer({
|
|
|
237
241
|
}
|
|
238
242
|
),
|
|
239
243
|
scrollable ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative min-h-0 flex-1", children: [
|
|
240
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "h-full overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.
|
|
244
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "h-full overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
241
245
|
"div",
|
|
242
246
|
{
|
|
243
247
|
ref: contentRef,
|
|
244
248
|
"data-session-viewer-content": true,
|
|
245
249
|
className: "p-density-3 @min-[48rem]/session-viewer:p-density-4",
|
|
246
|
-
children:
|
|
250
|
+
children: [
|
|
251
|
+
loadingOlder ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", className: "pb-density-2 text-center text-xs text-muted-foreground", children: "Loading earlier messages…" }) : null,
|
|
252
|
+
list
|
|
253
|
+
]
|
|
247
254
|
}
|
|
248
255
|
) }),
|
|
249
256
|
!following && /* @__PURE__ */ jsxRuntime.jsxs(
|