@flanksource/clicky-ui 0.3.45 → 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/README.md +22 -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/AccessMark.cjs +23 -0
- package/dist/data/AccessMark.cjs.map +1 -0
- package/dist/data/AccessMark.d.ts +11 -0
- package/dist/data/AccessMark.d.ts.map +1 -0
- package/dist/data/AccessMark.js +23 -0
- package/dist/data/AccessMark.js.map +1 -0
- 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/CodeDiff.cjs +116 -23
- package/dist/data/CodeDiff.cjs.map +1 -1
- package/dist/data/CodeDiff.d.ts +26 -0
- package/dist/data/CodeDiff.d.ts.map +1 -1
- package/dist/data/CodeDiff.js +116 -23
- package/dist/data/CodeDiff.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/GraphDiagram.cjs +65 -13
- package/dist/data/GraphDiagram.cjs.map +1 -1
- package/dist/data/GraphDiagram.d.ts +9 -2
- package/dist/data/GraphDiagram.d.ts.map +1 -1
- package/dist/data/GraphDiagram.js +66 -14
- package/dist/data/GraphDiagram.js.map +1 -1
- package/dist/data/GraphDiagramGroups.cjs +100 -0
- package/dist/data/GraphDiagramGroups.cjs.map +1 -0
- package/dist/data/GraphDiagramGroups.d.ts +24 -0
- package/dist/data/GraphDiagramGroups.d.ts.map +1 -0
- package/dist/data/GraphDiagramGroups.js +100 -0
- package/dist/data/GraphDiagramGroups.js.map +1 -0
- package/dist/data/GraphDiagramNodes.cjs +17 -31
- package/dist/data/GraphDiagramNodes.cjs.map +1 -1
- package/dist/data/GraphDiagramNodes.d.ts +6 -13
- package/dist/data/GraphDiagramNodes.d.ts.map +1 -1
- package/dist/data/GraphDiagramNodes.js +17 -31
- package/dist/data/GraphDiagramNodes.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/TreeNode.cjs +1 -1
- package/dist/data/TreeNode.cjs.map +1 -1
- package/dist/data/TreeNode.d.ts.map +1 -1
- package/dist/data/TreeNode.js +1 -1
- package/dist/data/TreeNode.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/call-graph/CallGraph.cjs +50 -31
- package/dist/data/call-graph/CallGraph.cjs.map +1 -1
- package/dist/data/call-graph/CallGraph.d.ts +10 -1
- package/dist/data/call-graph/CallGraph.d.ts.map +1 -1
- package/dist/data/call-graph/CallGraph.js +52 -33
- package/dist/data/call-graph/CallGraph.js.map +1 -1
- package/dist/data/call-graph/CallGraphMembers.cjs +5 -4
- package/dist/data/call-graph/CallGraphMembers.cjs.map +1 -1
- package/dist/data/call-graph/CallGraphMembers.d.ts +1 -1
- package/dist/data/call-graph/CallGraphMembers.d.ts.map +1 -1
- package/dist/data/call-graph/CallGraphMembers.js +5 -4
- package/dist/data/call-graph/CallGraphMembers.js.map +1 -1
- package/dist/data/call-graph/CallGraphToolbar.cjs +17 -5
- package/dist/data/call-graph/CallGraphToolbar.cjs.map +1 -1
- package/dist/data/call-graph/CallGraphToolbar.d.ts +10 -0
- package/dist/data/call-graph/CallGraphToolbar.d.ts.map +1 -1
- package/dist/data/call-graph/CallGraphToolbar.js +18 -6
- package/dist/data/call-graph/CallGraphToolbar.js.map +1 -1
- package/dist/data/call-graph/call-graph-model.cjs +169 -18
- package/dist/data/call-graph/call-graph-model.cjs.map +1 -1
- package/dist/data/call-graph/call-graph-model.d.ts +40 -4
- package/dist/data/call-graph/call-graph-model.d.ts.map +1 -1
- package/dist/data/call-graph/call-graph-model.js +170 -19
- package/dist/data/call-graph/call-graph-model.js.map +1 -1
- package/dist/data/call-graph/call-graph-vocabulary.cjs +19 -1
- package/dist/data/call-graph/call-graph-vocabulary.cjs.map +1 -1
- package/dist/data/call-graph/call-graph-vocabulary.d.ts +4 -0
- package/dist/data/call-graph/call-graph-vocabulary.d.ts.map +1 -1
- package/dist/data/call-graph/call-graph-vocabulary.js +19 -1
- package/dist/data/call-graph/call-graph-vocabulary.js.map +1 -1
- package/dist/data/call-graph/call-graph.fixtures.d.ts.map +1 -1
- package/dist/data/call-graph/index.d.ts +2 -2
- package/dist/data/call-graph/index.d.ts.map +1 -1
- package/dist/data/call-graph/types.cjs.map +1 -1
- package/dist/data/call-graph/types.d.ts +7 -2
- package/dist/data/call-graph/types.d.ts.map +1 -1
- package/dist/data/call-graph/types.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/data-access.cjs +8 -0
- package/dist/data/data-access.cjs.map +1 -0
- package/dist/data/data-access.d.ts +5 -0
- package/dist/data/data-access.d.ts.map +1 -0
- package/dist/data/data-access.js +8 -0
- package/dist/data/data-access.js.map +1 -0
- 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/git/GitChangeList.cjs +6 -2
- package/dist/data/git/GitChangeList.cjs.map +1 -1
- package/dist/data/git/GitChangeList.d.ts +6 -1
- package/dist/data/git/GitChangeList.d.ts.map +1 -1
- package/dist/data/git/GitChangeList.js +6 -2
- package/dist/data/git/GitChangeList.js.map +1 -1
- package/dist/data/graph-columns-layout.cjs +47 -16
- package/dist/data/graph-columns-layout.cjs.map +1 -1
- package/dist/data/graph-columns-layout.d.ts +34 -0
- package/dist/data/graph-columns-layout.d.ts.map +1 -1
- package/dist/data/graph-columns-layout.js +47 -16
- package/dist/data/graph-columns-layout.js.map +1 -1
- package/dist/data/graph-diagram-model.cjs +5 -0
- package/dist/data/graph-diagram-model.cjs.map +1 -1
- package/dist/data/graph-diagram-model.d.ts +19 -0
- package/dist/data/graph-diagram-model.d.ts.map +1 -1
- package/dist/data/graph-diagram-model.js +5 -0
- package/dist/data/graph-diagram-model.js.map +1 -1
- package/dist/data/graph-group-window.cjs +60 -0
- package/dist/data/graph-group-window.cjs.map +1 -0
- package/dist/data/graph-group-window.d.ts +33 -0
- package/dist/data/graph-group-window.d.ts.map +1 -0
- package/dist/data/graph-group-window.js +60 -0
- package/dist/data/graph-group-window.js.map +1 -0
- 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/use-group-windows.cjs +50 -0
- package/dist/data/use-group-windows.cjs.map +1 -0
- package/dist/data/use-group-windows.d.ts +27 -0
- package/dist/data/use-group-windows.d.ts.map +1 -0
- package/dist/data/use-group-windows.js +50 -0
- package/dist/data/use-group-windows.js.map +1 -0
- 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 +54 -3
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +6 -2
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +58 -7
- package/dist/data.js.map +1 -1
- package/dist/hooks/event-hub.cjs +229 -0
- package/dist/hooks/event-hub.cjs.map +1 -0
- package/dist/hooks/event-hub.d.ts +26 -0
- package/dist/hooks/event-hub.d.ts.map +1 -0
- package/dist/hooks/event-hub.js +229 -0
- package/dist/hooks/event-hub.js.map +1 -0
- package/dist/hooks.cjs +2 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +2 -0
- package/dist/hooks.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 +56 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +60 -7
- 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/overlay/modalStack.cjs +13 -6
- package/dist/overlay/modalStack.cjs.map +1 -1
- package/dist/overlay/modalStack.d.ts.map +1 -1
- package/dist/overlay/modalStack.js +13 -6
- package/dist/overlay/modalStack.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.cjs","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":["useEventSourceFactory","useRef","useState","useEffect","useMemo"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionInspector.remote.cjs","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":["useEventSourceFactory","useRef","useState","useEffect","state","useCallback","useMemo"],"mappings":"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"}
|
|
@@ -3,16 +3,25 @@ import { SessionCollectionInput } from './SessionInspector.collection-types.js';
|
|
|
3
3
|
import { SessionUIMessage, UnifiedSessionInput } from './SessionViewer.unified.js';
|
|
4
4
|
export declare const TERMINAL_SESSION_LIFECYCLES: readonly ["succeeded", "partial", "failed", "cancelled", "interrupted", "completed"];
|
|
5
5
|
export declare function isTerminalSessionLifecycle(status: string | undefined): boolean;
|
|
6
|
+
/** Messages asked for by the first read and by each page of history. */
|
|
7
|
+
export declare const DEFAULT_SESSION_PAGE_SIZE = 100;
|
|
6
8
|
export interface RemoteSessionOptions {
|
|
7
9
|
/** Force (`true`) or suppress (`false`) the live stream. By default a
|
|
8
10
|
* session follows only while it reports a non-terminal `lifecycleStatus`;
|
|
9
11
|
* a session that reports none is not followed. */
|
|
10
12
|
follow?: boolean;
|
|
13
|
+
/** Messages per read: the first read's `tail` and each history page's
|
|
14
|
+
* `limit`. Defaults to DEFAULT_SESSION_PAGE_SIZE. */
|
|
15
|
+
pageSize?: number;
|
|
11
16
|
}
|
|
12
17
|
export interface RemoteSessionState {
|
|
13
18
|
session?: UnifiedSessionInput;
|
|
14
19
|
error?: string;
|
|
15
20
|
loading: boolean;
|
|
21
|
+
/** Older messages exist that `loadOlder` has not fetched yet. */
|
|
22
|
+
hasOlder?: boolean;
|
|
23
|
+
/** A `loadOlder` page is in flight. */
|
|
24
|
+
loadingOlder?: boolean;
|
|
16
25
|
}
|
|
17
26
|
/** The payload of a follow stream's `state` frame. */
|
|
18
27
|
export interface SessionFollowState {
|
|
@@ -28,23 +37,49 @@ export interface RemoteSessionHandle {
|
|
|
28
37
|
/** Settles with the first fetched session, or rejects with the load error
|
|
29
38
|
* (which `onChange` has already reported as `error`). */
|
|
30
39
|
loaded: Promise<UnifiedSessionInput>;
|
|
40
|
+
/** Fetches the page of history before the oldest message held and
|
|
41
|
+
* prepends it; a no-op while one is in flight or nothing older remains. */
|
|
42
|
+
loadOlder: () => Promise<void>;
|
|
31
43
|
close: () => void;
|
|
32
44
|
}
|
|
33
|
-
/** `src` with `
|
|
34
|
-
|
|
45
|
+
/** `src` with `params` set in its query (replacing any of the same name),
|
|
46
|
+
* keeping any fragment last. */
|
|
47
|
+
export declare function sessionUrl(src: string, params: Record<string, string | number>): string;
|
|
35
48
|
export declare function fetchRemoteSession(src: string, signal?: AbortSignal): Promise<UnifiedSessionInput>;
|
|
36
|
-
/** Upserts messages by id: a known id is replaced in place, a new one appended.
|
|
49
|
+
/** Upserts messages by id: a known id is replaced in place, a new one appended.
|
|
50
|
+
* Placeholders stand in for a transcript that does not exist yet; the first
|
|
51
|
+
* real message replaces them all, since its ids never match theirs. */
|
|
37
52
|
export declare function mergeSessionMessages(base: readonly SessionUIMessage[] | undefined, incoming: readonly SessionUIMessage[]): SessionUIMessage[];
|
|
38
53
|
/** Fetches `src`, then follows it when wanted, reporting every change through
|
|
39
54
|
* `onChange`. Nothing is reported after `close()`. */
|
|
40
|
-
export declare function openRemoteSession(src: string, { follow, eventSourceFactory, onChange, }: RemoteSessionOptions & {
|
|
55
|
+
export declare function openRemoteSession(src: string, { follow, pageSize, eventSourceFactory, onChange, }: RemoteSessionOptions & {
|
|
41
56
|
eventSourceFactory: EventSourceFactory;
|
|
42
57
|
onChange: (state: RemoteSessionState) => void;
|
|
43
58
|
}): RemoteSessionHandle;
|
|
59
|
+
export interface RemoteSessionView extends RemoteSessionState {
|
|
60
|
+
/** See RemoteSessionHandle.loadOlder; a no-op before the session loads. */
|
|
61
|
+
loadOlder: () => Promise<void>;
|
|
62
|
+
}
|
|
44
63
|
/** Loads `src` (undefined loads nothing) and follows it on demand; see
|
|
45
|
-
* `RemoteSessionOptions
|
|
46
|
-
* closes the previous stream. */
|
|
47
|
-
export declare function useRemoteSession(src: string | undefined, { follow }?: RemoteSessionOptions):
|
|
64
|
+
* `RemoteSessionOptions`. Changing `src`, `follow` or `pageSize`, or
|
|
65
|
+
* unmounting, closes the previous stream. */
|
|
66
|
+
export declare function useRemoteSession(src: string | undefined, { follow, pageSize }?: RemoteSessionOptions): RemoteSessionView;
|
|
67
|
+
/** What a host learns about a `src`-backed inspector's load. `error` carries
|
|
68
|
+
* the server's message (a 404 body's `error` field or plain text) or the
|
|
69
|
+
* follow stream's terminal error frame. */
|
|
70
|
+
export type SessionInspectorLoadState = {
|
|
71
|
+
status: "loading" | "ready";
|
|
72
|
+
} | {
|
|
73
|
+
status: "error";
|
|
74
|
+
error: Error;
|
|
75
|
+
};
|
|
76
|
+
/** Reports `state` to `onChange` whenever its status or error changes; never
|
|
77
|
+
* when `enabled` is false (in-memory sessions have no load to report). */
|
|
78
|
+
export declare function useReportLoadState({ state, enabled, onChange, }: {
|
|
79
|
+
state: RemoteSessionState;
|
|
80
|
+
enabled: boolean;
|
|
81
|
+
onChange: ((state: SessionInspectorLoadState) => void) | undefined;
|
|
82
|
+
}): void;
|
|
48
83
|
export interface RemoteCollectionState {
|
|
49
84
|
/** The collection with its current item's session loaded, once it is. */
|
|
50
85
|
collection?: SessionCollectionInput;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionInspector.remote.d.ts","sourceRoot":"","sources":["../../../src/data/ai/SessionInspector.remote.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,KAAK,EACV,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"SessionInspector.remote.d.ts","sourceRoot":"","sources":["../../../src/data/ai/SessionInspector.remote.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef, useState, useEffect, useMemo } from "react";
|
|
1
|
+
import { useRef, useState, useEffect, useCallback, useMemo } from "react";
|
|
2
2
|
import { useEventSourceFactory } from "../../hooks/event-source.js";
|
|
3
3
|
const TERMINAL_SESSION_LIFECYCLES = [
|
|
4
4
|
"succeeded",
|
|
@@ -11,12 +11,15 @@ const TERMINAL_SESSION_LIFECYCLES = [
|
|
|
11
11
|
function isTerminalSessionLifecycle(status) {
|
|
12
12
|
return status !== void 0 && TERMINAL_SESSION_LIFECYCLES.includes(status);
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
const DEFAULT_SESSION_PAGE_SIZE = 100;
|
|
15
|
+
function sessionUrl(src, params) {
|
|
15
16
|
const hashAt = src.indexOf("#");
|
|
16
17
|
const base = hashAt === -1 ? src : src.slice(0, hashAt);
|
|
17
18
|
const hash = hashAt === -1 ? "" : src.slice(hashAt);
|
|
18
|
-
const
|
|
19
|
-
|
|
19
|
+
const queryAt = base.indexOf("?");
|
|
20
|
+
const query = new URLSearchParams(queryAt === -1 ? "" : base.slice(queryAt + 1));
|
|
21
|
+
for (const [name, value] of Object.entries(params)) query.set(name, String(value));
|
|
22
|
+
return `${queryAt === -1 ? base : base.slice(0, queryAt)}?${query}${hash}`;
|
|
20
23
|
}
|
|
21
24
|
async function fetchRemoteSession(src, signal) {
|
|
22
25
|
const response = await fetch(src, {
|
|
@@ -45,7 +48,8 @@ async function fetchRemoteSession(src, signal) {
|
|
|
45
48
|
return parsed;
|
|
46
49
|
}
|
|
47
50
|
function mergeSessionMessages(base, incoming) {
|
|
48
|
-
const
|
|
51
|
+
const real = incoming.some((message) => !message.placeholder);
|
|
52
|
+
const merged = (base ?? []).filter((message) => !(real && message.placeholder));
|
|
49
53
|
for (const message of incoming) {
|
|
50
54
|
const index = merged.findIndex((existing) => existing.id === message.id);
|
|
51
55
|
if (index === -1) merged.push(message);
|
|
@@ -55,10 +59,13 @@ function mergeSessionMessages(base, incoming) {
|
|
|
55
59
|
}
|
|
56
60
|
function openRemoteSession(src, {
|
|
57
61
|
follow,
|
|
62
|
+
pageSize = DEFAULT_SESSION_PAGE_SIZE,
|
|
58
63
|
eventSourceFactory,
|
|
59
64
|
onChange
|
|
60
65
|
}) {
|
|
61
|
-
|
|
66
|
+
let url = sessionUrl(src, { follow: 1 });
|
|
67
|
+
let olderPage;
|
|
68
|
+
let pageError;
|
|
62
69
|
const aborter = new AbortController();
|
|
63
70
|
const streamed = /* @__PURE__ */ new Map();
|
|
64
71
|
let closed = false;
|
|
@@ -82,23 +89,51 @@ function openRemoteSession(src, {
|
|
|
82
89
|
emit({ ...state, loading: false, error: message });
|
|
83
90
|
};
|
|
84
91
|
const refresh = async () => {
|
|
85
|
-
var _a;
|
|
92
|
+
var _a, _b, _c, _d;
|
|
86
93
|
const current = ++generation;
|
|
87
94
|
const startedAt = seq;
|
|
88
|
-
const fetched = await fetchRemoteSession(src, aborter.signal);
|
|
95
|
+
const fetched = await fetchRemoteSession(sessionUrl(src, { tail: pageSize }), aborter.signal);
|
|
89
96
|
if (closed || current !== generation) return void 0;
|
|
90
97
|
const fetchedIds = new Set((_a = fetched.messages) == null ? void 0 : _a.map((message) => message.id));
|
|
91
98
|
const keep = [...streamed.values()].filter((entry) => entry.seq > startedAt || !fetchedIds.has(entry.message.id)).map((entry) => entry.message);
|
|
99
|
+
const held = mergeSessionMessages((_b = state.session) == null ? void 0 : _b.messages, fetched.messages ?? []);
|
|
92
100
|
const session = {
|
|
93
101
|
...fetched,
|
|
94
|
-
messages: mergeSessionMessages(
|
|
102
|
+
messages: mergeSessionMessages(held, keep)
|
|
95
103
|
};
|
|
96
104
|
if (typeof fetched.revision === "number") {
|
|
97
105
|
revision = Math.max(revision ?? fetched.revision, fetched.revision);
|
|
98
106
|
}
|
|
99
|
-
|
|
107
|
+
const hasOlder = state.session === void 0 ? (((_c = fetched.window) == null ? void 0 : _c.messages) ?? 0) > (((_d = fetched.messages) == null ? void 0 : _d.length) ?? 0) : state.hasOlder;
|
|
108
|
+
emit({ ...state, session, loading: false, hasOlder: hasOlder ?? false });
|
|
100
109
|
return session;
|
|
101
110
|
};
|
|
111
|
+
const loadOlder = () => {
|
|
112
|
+
var _a, _b, _c;
|
|
113
|
+
const oldest = (_c = (_b = (_a = state.session) == null ? void 0 : _a.messages) == null ? void 0 : _b[0]) == null ? void 0 : _c.id;
|
|
114
|
+
if (closed || olderPage || !state.hasOlder || !oldest) return olderPage ?? Promise.resolve();
|
|
115
|
+
emit({ ...state, loadingOlder: true });
|
|
116
|
+
olderPage = fetchRemoteSession(sessionUrl(src, { before: oldest, limit: pageSize }), aborter.signal).then((page) => {
|
|
117
|
+
const older = page.messages ?? [];
|
|
118
|
+
const { error, ...rest } = state;
|
|
119
|
+
const session = state.session;
|
|
120
|
+
emit({
|
|
121
|
+
...rest,
|
|
122
|
+
...error !== void 0 && error !== pageError ? { error } : {},
|
|
123
|
+
loadingOlder: false,
|
|
124
|
+
hasOlder: older.length === pageSize,
|
|
125
|
+
session: { ...session, messages: mergeSessionMessages(older, session.messages ?? []) }
|
|
126
|
+
});
|
|
127
|
+
pageError = void 0;
|
|
128
|
+
}).catch((reason) => {
|
|
129
|
+
if (closed) return;
|
|
130
|
+
pageError = errorMessage(reason);
|
|
131
|
+
emit({ ...state, loadingOlder: false, error: pageError });
|
|
132
|
+
}).finally(() => {
|
|
133
|
+
olderPage = void 0;
|
|
134
|
+
});
|
|
135
|
+
return olderPage;
|
|
136
|
+
};
|
|
102
137
|
const readFrame = (event) => {
|
|
103
138
|
try {
|
|
104
139
|
return JSON.parse(event.data);
|
|
@@ -150,6 +185,9 @@ function openRemoteSession(src, {
|
|
|
150
185
|
if (isTerminalSessionLifecycle(next.lifecycleStatus)) stopFollowing();
|
|
151
186
|
};
|
|
152
187
|
const startFollowing = () => {
|
|
188
|
+
var _a, _b, _c;
|
|
189
|
+
const newest = (_c = (_b = (_a = state.session) == null ? void 0 : _a.messages) == null ? void 0 : _b.filter((message) => !message.placeholder).at(-1)) == null ? void 0 : _c.id;
|
|
190
|
+
url = sessionUrl(src, newest ? { follow: 1, after: newest } : { follow: 1 });
|
|
153
191
|
const es = eventSourceFactory(url);
|
|
154
192
|
source = es;
|
|
155
193
|
let received = false;
|
|
@@ -192,6 +230,7 @@ function openRemoteSession(src, {
|
|
|
192
230
|
);
|
|
193
231
|
return {
|
|
194
232
|
loaded,
|
|
233
|
+
loadOlder,
|
|
195
234
|
close: () => {
|
|
196
235
|
closed = true;
|
|
197
236
|
aborter.abort();
|
|
@@ -201,24 +240,51 @@ function openRemoteSession(src, {
|
|
|
201
240
|
}
|
|
202
241
|
const IDLE = { loading: false };
|
|
203
242
|
const LOADING = { loading: true };
|
|
204
|
-
function useRemoteSession(src, { follow } = {}) {
|
|
243
|
+
function useRemoteSession(src, { follow, pageSize } = {}) {
|
|
205
244
|
const eventSourceFactory = useEventSourceFactory();
|
|
206
245
|
const factoryRef = useRef(eventSourceFactory);
|
|
207
246
|
factoryRef.current = eventSourceFactory;
|
|
247
|
+
const handleRef = useRef(void 0);
|
|
208
248
|
const [snapshot, setSnapshot] = useState();
|
|
209
249
|
useEffect(() => {
|
|
210
250
|
if (src === void 0) return void 0;
|
|
211
251
|
setSnapshot({ src, state: LOADING });
|
|
212
252
|
const handle = openRemoteSession(src, {
|
|
213
253
|
...follow === void 0 ? {} : { follow },
|
|
254
|
+
...pageSize === void 0 ? {} : { pageSize },
|
|
214
255
|
eventSourceFactory: factoryRef.current,
|
|
215
|
-
onChange: (
|
|
256
|
+
onChange: (state2) => setSnapshot({ src, state: state2 })
|
|
216
257
|
});
|
|
258
|
+
handleRef.current = handle;
|
|
217
259
|
handle.loaded.catch(() => void 0);
|
|
218
|
-
return
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
260
|
+
return () => {
|
|
261
|
+
handle.close();
|
|
262
|
+
if (handleRef.current === handle) handleRef.current = void 0;
|
|
263
|
+
};
|
|
264
|
+
}, [src, follow, pageSize]);
|
|
265
|
+
const loadOlder = useCallback(() => {
|
|
266
|
+
var _a;
|
|
267
|
+
return ((_a = handleRef.current) == null ? void 0 : _a.loadOlder()) ?? Promise.resolve();
|
|
268
|
+
}, []);
|
|
269
|
+
const state = src === void 0 ? IDLE : (snapshot == null ? void 0 : snapshot.src) === src ? snapshot.state : LOADING;
|
|
270
|
+
return useMemo(() => ({ ...state, loadOlder }), [state, loadOlder]);
|
|
271
|
+
}
|
|
272
|
+
function useReportLoadState({
|
|
273
|
+
state,
|
|
274
|
+
enabled,
|
|
275
|
+
onChange
|
|
276
|
+
}) {
|
|
277
|
+
const onChangeRef = useRef(onChange);
|
|
278
|
+
onChangeRef.current = onChange;
|
|
279
|
+
const { loading, error } = state;
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
var _a;
|
|
282
|
+
if (!enabled) return;
|
|
283
|
+
(_a = onChangeRef.current) == null ? void 0 : _a.call(
|
|
284
|
+
onChangeRef,
|
|
285
|
+
error !== void 0 ? { status: "error", error: new Error(error) } : { status: loading ? "loading" : "ready" }
|
|
286
|
+
);
|
|
287
|
+
}, [enabled, loading, error]);
|
|
222
288
|
}
|
|
223
289
|
function useRemoteCollection(collection, options = {}) {
|
|
224
290
|
const current = collection == null ? void 0 : collection.sessions.find(
|
|
@@ -263,13 +329,15 @@ function isRecord(value) {
|
|
|
263
329
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
264
330
|
}
|
|
265
331
|
export {
|
|
332
|
+
DEFAULT_SESSION_PAGE_SIZE,
|
|
266
333
|
TERMINAL_SESSION_LIFECYCLES,
|
|
267
334
|
fetchRemoteSession,
|
|
268
335
|
isTerminalSessionLifecycle,
|
|
269
336
|
mergeSessionMessages,
|
|
270
337
|
openRemoteSession,
|
|
271
|
-
|
|
338
|
+
sessionUrl,
|
|
272
339
|
useRemoteCollection,
|
|
273
|
-
useRemoteSession
|
|
340
|
+
useRemoteSession,
|
|
341
|
+
useReportLoadState
|
|
274
342
|
};
|
|
275
343
|
//# sourceMappingURL=SessionInspector.remote.js.map
|
|
@@ -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":"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"}
|
|
@@ -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"}
|