@stigmer/react 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/billing/AutoRechargeCard.d.ts +38 -0
- package/billing/AutoRechargeCard.d.ts.map +1 -0
- package/billing/AutoRechargeCard.js +90 -0
- package/billing/AutoRechargeCard.js.map +1 -0
- package/billing/BillingSection.d.ts +32 -0
- package/billing/BillingSection.d.ts.map +1 -0
- package/billing/BillingSection.js +81 -0
- package/billing/BillingSection.js.map +1 -0
- package/billing/CreditBalanceCard.d.ts +25 -0
- package/billing/CreditBalanceCard.d.ts.map +1 -0
- package/billing/CreditBalanceCard.js +28 -0
- package/billing/CreditBalanceCard.js.map +1 -0
- package/billing/CreditLedgerTable.d.ts +22 -0
- package/billing/CreditLedgerTable.d.ts.map +1 -0
- package/billing/CreditLedgerTable.js +75 -0
- package/billing/CreditLedgerTable.js.map +1 -0
- package/billing/CreditPackGrid.d.ts +31 -0
- package/billing/CreditPackGrid.d.ts.map +1 -0
- package/billing/CreditPackGrid.js +35 -0
- package/billing/CreditPackGrid.js.map +1 -0
- package/billing/LowBalanceBanner.d.ts +26 -0
- package/billing/LowBalanceBanner.d.ts.map +1 -0
- package/billing/LowBalanceBanner.js +33 -0
- package/billing/LowBalanceBanner.js.map +1 -0
- package/billing/PaymentMethodCard.d.ts +35 -0
- package/billing/PaymentMethodCard.d.ts.map +1 -0
- package/billing/PaymentMethodCard.js +48 -0
- package/billing/PaymentMethodCard.js.map +1 -0
- package/billing/credit-packs.d.ts +25 -0
- package/billing/credit-packs.d.ts.map +1 -0
- package/billing/credit-packs.js +39 -0
- package/billing/credit-packs.js.map +1 -0
- package/billing/format.d.ts +39 -0
- package/billing/format.d.ts.map +1 -0
- package/billing/format.js +90 -0
- package/billing/format.js.map +1 -0
- package/billing/index.d.ts +32 -0
- package/billing/index.d.ts.map +1 -0
- package/billing/index.js +21 -0
- package/billing/index.js.map +1 -0
- package/billing/useBillingAccount.d.ts +40 -0
- package/billing/useBillingAccount.d.ts.map +1 -0
- package/billing/useBillingAccount.js +35 -0
- package/billing/useBillingAccount.js.map +1 -0
- package/billing/useBillingUsageReport.d.ts +42 -0
- package/billing/useBillingUsageReport.d.ts.map +1 -0
- package/billing/useBillingUsageReport.js +43 -0
- package/billing/useBillingUsageReport.js.map +1 -0
- package/billing/useCreateBillingPortalSession.d.ts +35 -0
- package/billing/useCreateBillingPortalSession.d.ts.map +1 -0
- package/billing/useCreateBillingPortalSession.js +50 -0
- package/billing/useCreateBillingPortalSession.js.map +1 -0
- package/billing/useCreateCheckoutSession.d.ts +54 -0
- package/billing/useCreateCheckoutSession.d.ts.map +1 -0
- package/billing/useCreateCheckoutSession.js +58 -0
- package/billing/useCreateCheckoutSession.js.map +1 -0
- package/billing/useCreditLedger.d.ts +48 -0
- package/billing/useCreditLedger.d.ts.map +1 -0
- package/billing/useCreditLedger.js +39 -0
- package/billing/useCreditLedger.js.map +1 -0
- package/billing/useCustomerModelPricing.d.ts +41 -0
- package/billing/useCustomerModelPricing.d.ts.map +1 -0
- package/billing/useCustomerModelPricing.js +37 -0
- package/billing/useCustomerModelPricing.js.map +1 -0
- package/billing/useSetAutoRechargeConfig.d.ts +50 -0
- package/billing/useSetAutoRechargeConfig.d.ts.map +1 -0
- package/billing/useSetAutoRechargeConfig.js +53 -0
- package/billing/useSetAutoRechargeConfig.js.map +1 -0
- package/composer/ComposerToolbar.js +1 -1
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/SessionComposer.d.ts +1 -1
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +19 -4
- package/composer/SessionComposer.js.map +1 -1
- package/composer/__tests__/SessionComposer-memo.test.d.ts +2 -0
- package/composer/__tests__/SessionComposer-memo.test.d.ts.map +1 -0
- package/composer/__tests__/SessionComposer-memo.test.js +23 -0
- package/composer/__tests__/SessionComposer-memo.test.js.map +1 -0
- package/execution/ApprovalCard.d.ts +5 -1
- package/execution/ApprovalCard.d.ts.map +1 -1
- package/execution/ApprovalCard.js +7 -3
- package/execution/ApprovalCard.js.map +1 -1
- package/execution/ExecutionPhaseBadge.d.ts +1 -1
- package/execution/ExecutionPhaseBadge.d.ts.map +1 -1
- package/execution/ExecutionPhaseBadge.js +3 -2
- package/execution/ExecutionPhaseBadge.js.map +1 -1
- package/execution/MessageEntry.d.ts +7 -3
- package/execution/MessageEntry.d.ts.map +1 -1
- package/execution/MessageEntry.js +19 -8
- package/execution/MessageEntry.js.map +1 -1
- package/execution/MessageThread.d.ts +84 -3
- package/execution/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +113 -65
- package/execution/MessageThread.js.map +1 -1
- package/execution/SetupProgress.d.ts +1 -1
- package/execution/SetupProgress.d.ts.map +1 -1
- package/execution/SetupProgress.js +3 -3
- package/execution/SetupProgress.js.map +1 -1
- package/execution/SubAgentSection.d.ts +5 -1
- package/execution/SubAgentSection.d.ts.map +1 -1
- package/execution/SubAgentSection.js +13 -7
- package/execution/SubAgentSection.js.map +1 -1
- package/execution/ThreadSkeleton.d.ts +22 -0
- package/execution/ThreadSkeleton.d.ts.map +1 -0
- package/execution/ThreadSkeleton.js +26 -0
- package/execution/ThreadSkeleton.js.map +1 -0
- package/execution/ToolCallGroup.d.ts +16 -1
- package/execution/ToolCallGroup.d.ts.map +1 -1
- package/execution/ToolCallGroup.js +31 -3
- package/execution/ToolCallGroup.js.map +1 -1
- package/execution/UsageWidget.d.ts +1 -1
- package/execution/__tests__/message-entry.test.d.ts +2 -0
- package/execution/__tests__/message-entry.test.d.ts.map +1 -0
- package/execution/__tests__/message-entry.test.js +178 -0
- package/execution/__tests__/message-entry.test.js.map +1 -0
- package/execution/__tests__/thread-keys.test.d.ts +2 -0
- package/execution/__tests__/thread-keys.test.d.ts.map +1 -0
- package/execution/__tests__/thread-keys.test.js +289 -0
- package/execution/__tests__/thread-keys.test.js.map +1 -0
- package/execution/__tests__/thread-memoization.test.d.ts +2 -0
- package/execution/__tests__/thread-memoization.test.d.ts.map +1 -0
- package/execution/__tests__/thread-memoization.test.js +262 -0
- package/execution/__tests__/thread-memoization.test.js.map +1 -0
- package/execution/__tests__/thread-skeleton.test.d.ts +2 -0
- package/execution/__tests__/thread-skeleton.test.d.ts.map +1 -0
- package/execution/__tests__/thread-skeleton.test.js +35 -0
- package/execution/__tests__/thread-skeleton.test.js.map +1 -0
- package/execution/__tests__/useExecutionStream.test.js +73 -10
- package/execution/__tests__/useExecutionStream.test.js.map +1 -1
- package/execution/__tests__/useSessionVariables-stability.test.d.ts +2 -0
- package/execution/__tests__/useSessionVariables-stability.test.d.ts.map +1 -0
- package/execution/__tests__/useSessionVariables-stability.test.js +69 -0
- package/execution/__tests__/useSessionVariables-stability.test.js.map +1 -0
- package/execution/__tests__/virtualized-thread.test.d.ts +2 -0
- package/execution/__tests__/virtualized-thread.test.d.ts.map +1 -0
- package/execution/__tests__/virtualized-thread.test.js +274 -0
- package/execution/__tests__/virtualized-thread.test.js.map +1 -0
- package/execution/index.d.ts +2 -0
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js +1 -0
- package/execution/index.js.map +1 -1
- package/execution/useExecutionStream.d.ts +35 -10
- package/execution/useExecutionStream.d.ts.map +1 -1
- package/execution/useExecutionStream.js +79 -40
- package/execution/useExecutionStream.js.map +1 -1
- package/execution/useSessionVariables.d.ts.map +1 -1
- package/execution/useSessionVariables.js +4 -3
- package/execution/useSessionVariables.js.map +1 -1
- package/github/useGitHubConnection.d.ts.map +1 -1
- package/github/useGitHubConnection.js +5 -4
- package/github/useGitHubConnection.js.map +1 -1
- package/identity-account/index.d.ts +2 -0
- package/identity-account/index.d.ts.map +1 -0
- package/identity-account/index.js +2 -0
- package/identity-account/index.js.map +1 -0
- package/identity-account/useIdentityAccountGate.d.ts +81 -0
- package/identity-account/useIdentityAccountGate.d.ts.map +1 -0
- package/identity-account/useIdentityAccountGate.js +100 -0
- package/identity-account/useIdentityAccountGate.js.map +1 -0
- package/index.d.ts +10 -4
- package/index.d.ts.map +1 -1
- package/index.js +8 -2
- package/index.js.map +1 -1
- package/internal/FetchCacheProvider.d.ts +44 -0
- package/internal/FetchCacheProvider.d.ts.map +1 -0
- package/internal/FetchCacheProvider.js +61 -0
- package/internal/FetchCacheProvider.js.map +1 -0
- package/internal/JumpToLatestButton.d.ts +14 -0
- package/internal/JumpToLatestButton.d.ts.map +1 -0
- package/internal/JumpToLatestButton.js +19 -0
- package/internal/JumpToLatestButton.js.map +1 -0
- package/internal/ThreadItemWrapper.d.ts +20 -0
- package/internal/ThreadItemWrapper.d.ts.map +1 -0
- package/internal/ThreadItemWrapper.js +44 -0
- package/internal/ThreadItemWrapper.js.map +1 -0
- package/internal/VirtualizedThread.d.ts +25 -0
- package/internal/VirtualizedThread.d.ts.map +1 -0
- package/internal/VirtualizedThread.js +58 -0
- package/internal/VirtualizedThread.js.map +1 -0
- package/internal/__tests__/fetch-cache.test.d.ts +2 -0
- package/internal/__tests__/fetch-cache.test.d.ts.map +1 -0
- package/internal/__tests__/fetch-cache.test.js +182 -0
- package/internal/__tests__/fetch-cache.test.js.map +1 -0
- package/internal/__tests__/stream-controller.test.d.ts +2 -0
- package/internal/__tests__/stream-controller.test.d.ts.map +1 -0
- package/internal/__tests__/stream-controller.test.js +294 -0
- package/internal/__tests__/stream-controller.test.js.map +1 -0
- package/internal/__tests__/thread-animation.test.d.ts +2 -0
- package/internal/__tests__/thread-animation.test.d.ts.map +1 -0
- package/internal/__tests__/thread-animation.test.js +79 -0
- package/internal/__tests__/thread-animation.test.js.map +1 -0
- package/internal/__tests__/useAutoScroll.test.d.ts +2 -0
- package/internal/__tests__/useAutoScroll.test.d.ts.map +1 -0
- package/internal/__tests__/useAutoScroll.test.js +188 -0
- package/internal/__tests__/useAutoScroll.test.js.map +1 -0
- package/internal/__tests__/useFetch-cache.test.d.ts +2 -0
- package/internal/__tests__/useFetch-cache.test.d.ts.map +1 -0
- package/internal/__tests__/useFetch-cache.test.js +137 -0
- package/internal/__tests__/useFetch-cache.test.js.map +1 -0
- package/internal/dev/__tests__/use-key-stability.test.d.ts +2 -0
- package/internal/dev/__tests__/use-key-stability.test.d.ts.map +1 -0
- package/internal/dev/__tests__/use-key-stability.test.js +72 -0
- package/internal/dev/__tests__/use-key-stability.test.js.map +1 -0
- package/internal/dev/__tests__/use-render-tracer.test.d.ts +2 -0
- package/internal/dev/__tests__/use-render-tracer.test.d.ts.map +1 -0
- package/internal/dev/__tests__/use-render-tracer.test.js +55 -0
- package/internal/dev/__tests__/use-render-tracer.test.js.map +1 -0
- package/internal/dev/dom-counter.d.ts +14 -0
- package/internal/dev/dom-counter.d.ts.map +1 -0
- package/internal/dev/dom-counter.js +39 -0
- package/internal/dev/dom-counter.js.map +1 -0
- package/internal/dev/index.d.ts +6 -0
- package/internal/dev/index.d.ts.map +1 -0
- package/internal/dev/index.js +6 -0
- package/internal/dev/index.js.map +1 -0
- package/internal/dev/profiler-wrapper.d.ts +16 -0
- package/internal/dev/profiler-wrapper.d.ts.map +1 -0
- package/internal/dev/profiler-wrapper.js +31 -0
- package/internal/dev/profiler-wrapper.js.map +1 -0
- package/internal/dev/use-key-stability.d.ts +22 -0
- package/internal/dev/use-key-stability.d.ts.map +1 -0
- package/internal/dev/use-key-stability.js +67 -0
- package/internal/dev/use-key-stability.js.map +1 -0
- package/internal/dev/use-render-tracer.d.ts +13 -0
- package/internal/dev/use-render-tracer.d.ts.map +1 -0
- package/internal/dev/use-render-tracer.js +57 -0
- package/internal/dev/use-render-tracer.js.map +1 -0
- package/internal/dev/use-stream-rate.d.ts +23 -0
- package/internal/dev/use-stream-rate.d.ts.map +1 -0
- package/internal/dev/use-stream-rate.js +94 -0
- package/internal/dev/use-stream-rate.js.map +1 -0
- package/internal/fetch-cache.d.ts +72 -0
- package/internal/fetch-cache.d.ts.map +1 -0
- package/internal/fetch-cache.js +118 -0
- package/internal/fetch-cache.js.map +1 -0
- package/internal/store/__tests__/conversation-store.test.d.ts +2 -0
- package/internal/store/__tests__/conversation-store.test.d.ts.map +1 -0
- package/internal/store/__tests__/conversation-store.test.js +200 -0
- package/internal/store/__tests__/conversation-store.test.js.map +1 -0
- package/internal/store/__tests__/structural-share.test.d.ts +2 -0
- package/internal/store/__tests__/structural-share.test.d.ts.map +1 -0
- package/internal/store/__tests__/structural-share.test.js +368 -0
- package/internal/store/__tests__/structural-share.test.js.map +1 -0
- package/internal/store/conversation-store.d.ts +62 -0
- package/internal/store/conversation-store.d.ts.map +1 -0
- package/internal/store/conversation-store.js +95 -0
- package/internal/store/conversation-store.js.map +1 -0
- package/internal/store/index.d.ts +31 -0
- package/internal/store/index.d.ts.map +1 -0
- package/internal/store/index.js +54 -0
- package/internal/store/index.js.map +1 -0
- package/internal/store/structural-share.d.ts +13 -0
- package/internal/store/structural-share.d.ts.map +1 -0
- package/internal/store/structural-share.js +240 -0
- package/internal/store/structural-share.js.map +1 -0
- package/internal/stream-controller.d.ts +85 -0
- package/internal/stream-controller.d.ts.map +1 -0
- package/internal/stream-controller.js +146 -0
- package/internal/stream-controller.js.map +1 -0
- package/internal/useAutoScroll.d.ts +32 -0
- package/internal/useAutoScroll.d.ts.map +1 -0
- package/internal/useAutoScroll.js +97 -0
- package/internal/useAutoScroll.js.map +1 -0
- package/internal/useFetch.d.ts +14 -0
- package/internal/useFetch.d.ts.map +1 -1
- package/internal/useFetch.js +32 -2
- package/internal/useFetch.js.map +1 -1
- package/package.json +7 -5
- package/session/__tests__/useNewSessionFlow.test.js +16 -0
- package/session/__tests__/useNewSessionFlow.test.js.map +1 -1
- package/session/__tests__/usePersistedModel.test.d.ts +2 -0
- package/session/__tests__/usePersistedModel.test.d.ts.map +1 -0
- package/session/__tests__/usePersistedModel.test.js +82 -0
- package/session/__tests__/usePersistedModel.test.js.map +1 -0
- package/session/__tests__/useSession.test.d.ts +2 -0
- package/session/__tests__/useSession.test.d.ts.map +1 -0
- package/session/__tests__/useSession.test.js +130 -0
- package/session/__tests__/useSession.test.js.map +1 -0
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +12 -6
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/usePersistedModel.d.ts +3 -0
- package/session/usePersistedModel.d.ts.map +1 -1
- package/session/usePersistedModel.js +27 -2
- package/session/usePersistedModel.js.map +1 -1
- package/session/useSession.d.ts.map +1 -1
- package/session/useSession.js +1 -1
- package/session/useSession.js.map +1 -1
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +9 -1
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionExecutions.d.ts.map +1 -1
- package/session/useSessionExecutions.js +1 -1
- package/session/useSessionExecutions.js.map +1 -1
- package/session/useSessionPageFlow.js +1 -1
- package/session/useSessionPageFlow.js.map +1 -1
- package/session/useSessionUsage.d.ts +24 -40
- package/session/useSessionUsage.d.ts.map +1 -1
- package/session/useSessionUsage.js +64 -97
- package/session/useSessionUsage.js.map +1 -1
- package/settings/BillingSection.d.ts +3 -0
- package/settings/BillingSection.d.ts.map +1 -0
- package/settings/BillingSection.js +3 -0
- package/settings/BillingSection.js.map +1 -0
- package/settings/index.d.ts +2 -0
- package/settings/index.d.ts.map +1 -1
- package/settings/index.js +1 -0
- package/settings/index.js.map +1 -1
- package/settings/settings-nav.js +1 -1
- package/settings/settings-nav.js.map +1 -1
- package/src/billing/AutoRechargeCard.tsx +274 -0
- package/src/billing/BillingSection.tsx +255 -0
- package/src/billing/CreditBalanceCard.tsx +81 -0
- package/src/billing/CreditLedgerTable.tsx +281 -0
- package/src/billing/CreditPackGrid.tsx +132 -0
- package/src/billing/LowBalanceBanner.tsx +67 -0
- package/src/billing/PaymentMethodCard.tsx +133 -0
- package/src/billing/credit-packs.ts +54 -0
- package/src/billing/format.ts +97 -0
- package/src/billing/index.ts +51 -0
- package/src/billing/useBillingAccount.ts +64 -0
- package/src/billing/useBillingUsageReport.ts +73 -0
- package/src/billing/useCreateBillingPortalSession.ts +76 -0
- package/src/billing/useCreateCheckoutSession.ts +101 -0
- package/src/billing/useCreditLedger.ts +79 -0
- package/src/billing/useCustomerModelPricing.ts +67 -0
- package/src/billing/useSetAutoRechargeConfig.ts +90 -0
- package/src/composer/ComposerToolbar.tsx +1 -1
- package/src/composer/SessionComposer.tsx +22 -4
- package/src/composer/__tests__/SessionComposer-memo.test.ts +26 -0
- package/src/execution/ApprovalCard.tsx +7 -3
- package/src/execution/ExecutionPhaseBadge.tsx +3 -2
- package/src/execution/MessageEntry.tsx +27 -16
- package/src/execution/MessageThread.tsx +308 -131
- package/src/execution/SetupProgress.tsx +3 -3
- package/src/execution/SubAgentSection.tsx +14 -6
- package/src/execution/ThreadSkeleton.tsx +73 -0
- package/src/execution/ToolCallGroup.tsx +36 -3
- package/src/execution/UsageWidget.tsx +1 -1
- package/src/execution/__tests__/message-entry.test.tsx +236 -0
- package/src/execution/__tests__/thread-keys.test.ts +409 -0
- package/src/execution/__tests__/thread-memoization.test.ts +320 -0
- package/src/execution/__tests__/thread-skeleton.test.tsx +44 -0
- package/src/execution/__tests__/useExecutionStream.test.tsx +109 -12
- package/src/execution/__tests__/useSessionVariables-stability.test.ts +95 -0
- package/src/execution/__tests__/virtualized-thread.test.tsx +401 -0
- package/src/execution/index.ts +3 -0
- package/src/execution/useExecutionStream.ts +123 -48
- package/src/execution/useSessionVariables.ts +17 -12
- package/src/github/useGitHubConnection.ts +18 -13
- package/src/identity-account/index.ts +5 -0
- package/src/identity-account/useIdentityAccountGate.ts +163 -0
- package/src/index.ts +73 -0
- package/src/internal/FetchCacheProvider.tsx +74 -0
- package/src/internal/JumpToLatestButton.tsx +61 -0
- package/src/internal/ThreadItemWrapper.tsx +65 -0
- package/src/internal/VirtualizedThread.tsx +162 -0
- package/src/internal/__tests__/fetch-cache.test.ts +230 -0
- package/src/internal/__tests__/stream-controller.test.ts +395 -0
- package/src/internal/__tests__/thread-animation.test.tsx +121 -0
- package/src/internal/__tests__/useAutoScroll.test.tsx +261 -0
- package/src/internal/__tests__/useFetch-cache.test.ts +214 -0
- package/src/internal/dev/__tests__/use-key-stability.test.ts +124 -0
- package/src/internal/dev/__tests__/use-render-tracer.test.ts +78 -0
- package/src/internal/dev/dom-counter.ts +47 -0
- package/src/internal/dev/index.ts +5 -0
- package/src/internal/dev/profiler-wrapper.tsx +52 -0
- package/src/internal/dev/use-key-stability.ts +86 -0
- package/src/internal/dev/use-render-tracer.ts +70 -0
- package/src/internal/dev/use-stream-rate.ts +138 -0
- package/src/internal/fetch-cache.ts +155 -0
- package/src/internal/store/__tests__/conversation-store.test.ts +257 -0
- package/src/internal/store/__tests__/structural-share.test.ts +454 -0
- package/src/internal/store/conversation-store.ts +128 -0
- package/src/internal/store/index.ts +68 -0
- package/src/internal/store/structural-share.ts +318 -0
- package/src/internal/stream-controller.ts +201 -0
- package/src/internal/useAutoScroll.ts +121 -0
- package/src/internal/useFetch.ts +51 -2
- package/src/session/__tests__/useNewSessionFlow.test.tsx +22 -0
- package/src/session/__tests__/usePersistedModel.test.tsx +117 -0
- package/src/session/__tests__/useSession.test.tsx +187 -0
- package/src/session/useNewSessionFlow.ts +12 -6
- package/src/session/usePersistedModel.ts +28 -2
- package/src/session/useSession.ts +1 -0
- package/src/session/useSessionConversation.ts +11 -2
- package/src/session/useSessionExecutions.ts +1 -0
- package/src/session/useSessionPageFlow.ts +1 -1
- package/src/session/useSessionUsage.ts +102 -123
- package/src/settings/BillingSection.tsx +4 -0
- package/src/settings/index.ts +2 -0
- package/src/settings/settings-nav.ts +1 -1
- package/src/styles.css +31 -0
- package/src/usage/AgentBreakdownList.tsx +147 -0
- package/src/usage/CreditRunwayIndicator.tsx +71 -0
- package/src/usage/ExportButton.tsx +115 -0
- package/src/usage/HarnessSplitCard.tsx +103 -0
- package/src/usage/OrgUsagePanel.tsx +109 -45
- package/src/usage/index.ts +15 -0
- package/src/usage/useExportCSV.ts +115 -0
- package/src/usage/useOrgUsageReport.ts +2 -1
- package/src/workspace/__tests__/useWorkspaceEntries-stability.test.ts +76 -0
- package/src/workspace/useWorkspaceEntries.ts +16 -11
- package/styles.css +1 -1
- package/usage/AgentBreakdownList.d.ts +21 -0
- package/usage/AgentBreakdownList.d.ts.map +1 -0
- package/usage/AgentBreakdownList.js +44 -0
- package/usage/AgentBreakdownList.js.map +1 -0
- package/usage/CreditRunwayIndicator.d.ts +21 -0
- package/usage/CreditRunwayIndicator.d.ts.map +1 -0
- package/usage/CreditRunwayIndicator.js +38 -0
- package/usage/CreditRunwayIndicator.js.map +1 -0
- package/usage/ExportButton.d.ts +20 -0
- package/usage/ExportButton.d.ts.map +1 -0
- package/usage/ExportButton.js +36 -0
- package/usage/ExportButton.js.map +1 -0
- package/usage/HarnessSplitCard.d.ts +17 -0
- package/usage/HarnessSplitCard.d.ts.map +1 -0
- package/usage/HarnessSplitCard.js +38 -0
- package/usage/HarnessSplitCard.js.map +1 -0
- package/usage/OrgUsagePanel.d.ts.map +1 -1
- package/usage/OrgUsagePanel.js +30 -22
- package/usage/OrgUsagePanel.js.map +1 -1
- package/usage/index.d.ts +10 -0
- package/usage/index.d.ts.map +1 -1
- package/usage/index.js +5 -0
- package/usage/index.js.map +1 -1
- package/usage/useExportCSV.d.ts +23 -0
- package/usage/useExportCSV.d.ts.map +1 -0
- package/usage/useExportCSV.js +81 -0
- package/usage/useExportCSV.js.map +1 -0
- package/usage/useOrgUsageReport.d.ts +2 -1
- package/usage/useOrgUsageReport.d.ts.map +1 -1
- package/usage/useOrgUsageReport.js +2 -1
- package/usage/useOrgUsageReport.js.map +1 -1
- package/workspace/__tests__/useWorkspaceEntries-stability.test.d.ts +2 -0
- package/workspace/__tests__/useWorkspaceEntries-stability.test.d.ts.map +1 -0
- package/workspace/__tests__/useWorkspaceEntries-stability.test.js +57 -0
- package/workspace/__tests__/useWorkspaceEntries-stability.test.js.map +1 -0
- package/workspace/useWorkspaceEntries.d.ts.map +1 -1
- package/workspace/useWorkspaceEntries.js +5 -4
- package/workspace/useWorkspaceEntries.js.map +1 -1
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, cleanup } from "@testing-library/react";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import {
|
|
6
|
+
AgentExecutionSchema,
|
|
7
|
+
AgentExecutionStatusSchema,
|
|
8
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
9
|
+
import { AgentExecutionSpecSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/spec_pb";
|
|
10
|
+
import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
|
|
11
|
+
import { AgentMessageSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
12
|
+
import {
|
|
13
|
+
ExecutionPhase,
|
|
14
|
+
MessageType,
|
|
15
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
16
|
+
import {
|
|
17
|
+
buildThreadItems,
|
|
18
|
+
ThreadItemRenderer,
|
|
19
|
+
type ThreadItem,
|
|
20
|
+
} from "../MessageThread";
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Mock react-virtuoso — Virtuoso's internal scroll/resize machinery
|
|
24
|
+
// does not work in happy-dom. We mock the component to capture props
|
|
25
|
+
// and verify configuration.
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
let capturedVirtuosoProps: Record<string, unknown> = {};
|
|
29
|
+
|
|
30
|
+
vi.mock("react-virtuoso", () => ({
|
|
31
|
+
Virtuoso: React.forwardRef(function MockVirtuoso(
|
|
32
|
+
props: Record<string, unknown>,
|
|
33
|
+
ref: React.Ref<unknown>,
|
|
34
|
+
) {
|
|
35
|
+
capturedVirtuosoProps = props;
|
|
36
|
+
const data = props.data as ThreadItem[];
|
|
37
|
+
const itemContent = props.itemContent as (
|
|
38
|
+
index: number,
|
|
39
|
+
item: ThreadItem,
|
|
40
|
+
) => React.ReactNode;
|
|
41
|
+
|
|
42
|
+
React.useImperativeHandle(ref, () => ({
|
|
43
|
+
scrollToIndex: vi.fn(),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
const Scroller =
|
|
47
|
+
(
|
|
48
|
+
props.components as {
|
|
49
|
+
Scroller?: React.ComponentType<Record<string, unknown>>;
|
|
50
|
+
}
|
|
51
|
+
)?.Scroller ?? "div";
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Scroller data-testid="virtuoso-scroller">
|
|
55
|
+
{data.map((item, i) => (
|
|
56
|
+
<div key={item.key} data-testid={`virtuoso-item-${i}`}>
|
|
57
|
+
{itemContent(i, item)}
|
|
58
|
+
</div>
|
|
59
|
+
))}
|
|
60
|
+
</Scroller>
|
|
61
|
+
);
|
|
62
|
+
}),
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Observer / rAF mocks (required for useAutoScroll in non-virtualized path)
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
capturedVirtuosoProps = {};
|
|
71
|
+
|
|
72
|
+
vi.stubGlobal(
|
|
73
|
+
"IntersectionObserver",
|
|
74
|
+
vi.fn(() => ({
|
|
75
|
+
observe: vi.fn(),
|
|
76
|
+
unobserve: vi.fn(),
|
|
77
|
+
disconnect: vi.fn(),
|
|
78
|
+
takeRecords: vi.fn(() => []),
|
|
79
|
+
root: null,
|
|
80
|
+
rootMargin: "",
|
|
81
|
+
thresholds: [0],
|
|
82
|
+
})),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
vi.stubGlobal(
|
|
86
|
+
"ResizeObserver",
|
|
87
|
+
vi.fn(() => ({
|
|
88
|
+
observe: vi.fn(),
|
|
89
|
+
unobserve: vi.fn(),
|
|
90
|
+
disconnect: vi.fn(),
|
|
91
|
+
})),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
vi.stubGlobal(
|
|
95
|
+
"requestAnimationFrame",
|
|
96
|
+
vi.fn((cb: FrameRequestCallback) => {
|
|
97
|
+
cb(performance.now());
|
|
98
|
+
return 1;
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
vi.stubGlobal("cancelAnimationFrame", vi.fn());
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
afterEach(() => {
|
|
106
|
+
cleanup();
|
|
107
|
+
vi.restoreAllMocks();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// Helpers
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
function makeExecution(id: string, specMessage: string, aiContent: string) {
|
|
115
|
+
const exec = create(AgentExecutionSchema);
|
|
116
|
+
|
|
117
|
+
const meta = create(ApiResourceMetadataSchema);
|
|
118
|
+
meta.id = id;
|
|
119
|
+
exec.metadata = meta;
|
|
120
|
+
|
|
121
|
+
const spec = create(AgentExecutionSpecSchema);
|
|
122
|
+
spec.message = specMessage;
|
|
123
|
+
exec.spec = spec;
|
|
124
|
+
|
|
125
|
+
const status = create(AgentExecutionStatusSchema);
|
|
126
|
+
status.phase = ExecutionPhase.EXECUTION_COMPLETED;
|
|
127
|
+
const humanMsg = create(AgentMessageSchema);
|
|
128
|
+
humanMsg.type = MessageType.MESSAGE_HUMAN;
|
|
129
|
+
humanMsg.content = specMessage;
|
|
130
|
+
const aiMsg = create(AgentMessageSchema);
|
|
131
|
+
aiMsg.type = MessageType.MESSAGE_AI;
|
|
132
|
+
aiMsg.content = aiContent;
|
|
133
|
+
status.messages = [humanMsg, aiMsg];
|
|
134
|
+
exec.status = status;
|
|
135
|
+
|
|
136
|
+
return exec;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Lazy import — must import after mocks are set up
|
|
140
|
+
async function importMessageThread() {
|
|
141
|
+
const mod = await import("../MessageThread");
|
|
142
|
+
return mod.MessageThread;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// Tests: ThreadItemRenderer
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
describe("ThreadItemRenderer", () => {
|
|
150
|
+
it("renders a message item", () => {
|
|
151
|
+
const msg = create(AgentMessageSchema);
|
|
152
|
+
msg.type = MessageType.MESSAGE_HUMAN;
|
|
153
|
+
msg.content = "Hello world";
|
|
154
|
+
|
|
155
|
+
const item: ThreadItem = { kind: "message", message: msg, key: "test-1" };
|
|
156
|
+
|
|
157
|
+
render(<ThreadItemRenderer item={item} />);
|
|
158
|
+
expect(screen.getByText("Hello world")).toBeTruthy();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("renders a pending message with opacity", () => {
|
|
162
|
+
const msg = create(AgentMessageSchema);
|
|
163
|
+
msg.type = MessageType.MESSAGE_HUMAN;
|
|
164
|
+
msg.content = "Sending...";
|
|
165
|
+
|
|
166
|
+
const item: ThreadItem = {
|
|
167
|
+
kind: "message",
|
|
168
|
+
message: msg,
|
|
169
|
+
key: "pending",
|
|
170
|
+
isPending: true,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const { container } = render(<ThreadItemRenderer item={item} />);
|
|
174
|
+
const root = container.firstElementChild as HTMLElement;
|
|
175
|
+
expect(root?.className).toContain("opacity-70");
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("renders a phase badge item", () => {
|
|
179
|
+
const item: ThreadItem = {
|
|
180
|
+
kind: "phase-badge",
|
|
181
|
+
phase: ExecutionPhase.EXECUTION_FAILED,
|
|
182
|
+
key: "phase-1",
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
render(<ThreadItemRenderer item={item} />);
|
|
186
|
+
expect(screen.getByText(/failed/i)).toBeTruthy();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("renders a setup progress item", () => {
|
|
190
|
+
const item: ThreadItem = {
|
|
191
|
+
kind: "setup-progress",
|
|
192
|
+
workspaceEntries: [],
|
|
193
|
+
key: "setup",
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
render(<ThreadItemRenderer item={item} />);
|
|
197
|
+
expect(document.querySelector("[class]")).toBeTruthy();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// Tests: MessageThread with virtualized=false (default, regression)
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
describe("MessageThread (non-virtualized)", () => {
|
|
206
|
+
it("renders items in a role=log container by default", async () => {
|
|
207
|
+
const MessageThread = await importMessageThread();
|
|
208
|
+
const exec = makeExecution("e1", "Hello", "Hi there");
|
|
209
|
+
|
|
210
|
+
render(<MessageThread executions={[exec]} />);
|
|
211
|
+
|
|
212
|
+
const log = screen.getByRole("log");
|
|
213
|
+
expect(log).toBeTruthy();
|
|
214
|
+
expect(log.getAttribute("aria-live")).toBe("polite");
|
|
215
|
+
expect(log.getAttribute("aria-relevant")).toBe("additions");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("does not render Virtuoso when virtualized is false", async () => {
|
|
219
|
+
const MessageThread = await importMessageThread();
|
|
220
|
+
const exec = makeExecution("e1", "Hello", "Hi");
|
|
221
|
+
|
|
222
|
+
render(<MessageThread executions={[exec]} />);
|
|
223
|
+
expect(capturedVirtuosoProps.data).toBeUndefined();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
// Tests: MessageThread with virtualized=true
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
describe("MessageThread (virtualized)", () => {
|
|
232
|
+
it("renders via Virtuoso when virtualized=true", async () => {
|
|
233
|
+
const MessageThread = await importMessageThread();
|
|
234
|
+
const exec = makeExecution("e1", "Hello", "Hi there");
|
|
235
|
+
|
|
236
|
+
render(<MessageThread executions={[exec]} virtualized />);
|
|
237
|
+
|
|
238
|
+
// Wait for lazy import to resolve
|
|
239
|
+
await vi.waitFor(() => {
|
|
240
|
+
expect(capturedVirtuosoProps.data).toBeDefined();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const data = capturedVirtuosoProps.data as ThreadItem[];
|
|
244
|
+
expect(data.length).toBeGreaterThan(0);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("passes alignToBottom=true to Virtuoso", async () => {
|
|
248
|
+
const MessageThread = await importMessageThread();
|
|
249
|
+
const exec = makeExecution("e1", "Hello", "Hi");
|
|
250
|
+
|
|
251
|
+
render(<MessageThread executions={[exec]} virtualized />);
|
|
252
|
+
|
|
253
|
+
await vi.waitFor(() => {
|
|
254
|
+
expect(capturedVirtuosoProps.alignToBottom).toBe(true);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("passes followOutput callback to Virtuoso", async () => {
|
|
259
|
+
const MessageThread = await importMessageThread();
|
|
260
|
+
const exec = makeExecution("e1", "Hello", "Hi");
|
|
261
|
+
|
|
262
|
+
render(<MessageThread executions={[exec]} virtualized />);
|
|
263
|
+
|
|
264
|
+
await vi.waitFor(() => {
|
|
265
|
+
expect(typeof capturedVirtuosoProps.followOutput).toBe("function");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const followOutput = capturedVirtuosoProps.followOutput as (
|
|
269
|
+
atBottom: boolean,
|
|
270
|
+
) => string | false;
|
|
271
|
+
expect(followOutput(true)).toBe("smooth");
|
|
272
|
+
expect(followOutput(false)).toBe(false);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("uses stable semantic keys via computeItemKey", async () => {
|
|
276
|
+
const MessageThread = await importMessageThread();
|
|
277
|
+
const exec = makeExecution("exec-42", "Hello", "Hi");
|
|
278
|
+
|
|
279
|
+
render(<MessageThread executions={[exec]} virtualized />);
|
|
280
|
+
|
|
281
|
+
await vi.waitFor(() => {
|
|
282
|
+
expect(capturedVirtuosoProps.computeItemKey).toBeDefined();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const data = capturedVirtuosoProps.data as ThreadItem[];
|
|
286
|
+
const computeItemKey = capturedVirtuosoProps.computeItemKey as (
|
|
287
|
+
index: number,
|
|
288
|
+
item: ThreadItem,
|
|
289
|
+
) => string;
|
|
290
|
+
|
|
291
|
+
for (let i = 0; i < data.length; i++) {
|
|
292
|
+
expect(computeItemKey(i, data[i])).toBe(data[i].key);
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it("sets atBottomThreshold matching the non-virtualized 80px margin", async () => {
|
|
297
|
+
const MessageThread = await importMessageThread();
|
|
298
|
+
const exec = makeExecution("e1", "Hello", "Hi");
|
|
299
|
+
|
|
300
|
+
render(<MessageThread executions={[exec]} virtualized />);
|
|
301
|
+
|
|
302
|
+
await vi.waitFor(() => {
|
|
303
|
+
expect(capturedVirtuosoProps.atBottomThreshold).toBe(80);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("applies a11y attributes to the scroller", async () => {
|
|
308
|
+
const MessageThread = await importMessageThread();
|
|
309
|
+
const exec = makeExecution("e1", "Hello", "Hi");
|
|
310
|
+
|
|
311
|
+
render(<MessageThread executions={[exec]} virtualized />);
|
|
312
|
+
|
|
313
|
+
await vi.waitFor(() => {
|
|
314
|
+
const scroller = screen.getByTestId("virtuoso-scroller");
|
|
315
|
+
expect(scroller.getAttribute("role")).toBe("log");
|
|
316
|
+
expect(scroller.getAttribute("aria-live")).toBe("polite");
|
|
317
|
+
expect(scroller.getAttribute("aria-relevant")).toBe("additions");
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("renders actual thread item content through Virtuoso", async () => {
|
|
322
|
+
const MessageThread = await importMessageThread();
|
|
323
|
+
const exec = makeExecution("e1", "Hello from user", "Hello from AI");
|
|
324
|
+
|
|
325
|
+
render(<MessageThread executions={[exec]} virtualized />);
|
|
326
|
+
|
|
327
|
+
await vi.waitFor(() => {
|
|
328
|
+
expect(screen.getAllByText("Hello from user").length).toBeGreaterThan(0);
|
|
329
|
+
expect(screen.getByText("Hello from AI")).toBeTruthy();
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it("sets increaseViewportBy for overscan", async () => {
|
|
334
|
+
const MessageThread = await importMessageThread();
|
|
335
|
+
const exec = makeExecution("e1", "Hello", "Hi");
|
|
336
|
+
|
|
337
|
+
render(<MessageThread executions={[exec]} virtualized />);
|
|
338
|
+
|
|
339
|
+
await vi.waitFor(() => {
|
|
340
|
+
expect(capturedVirtuosoProps.increaseViewportBy).toEqual({
|
|
341
|
+
top: 200,
|
|
342
|
+
bottom: 200,
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it("builds the same items for both paths", async () => {
|
|
348
|
+
const exec = makeExecution("e1", "Hello", "Hi there");
|
|
349
|
+
const items = buildThreadItems([exec], null, null, false, undefined);
|
|
350
|
+
|
|
351
|
+
const MessageThread = await importMessageThread();
|
|
352
|
+
render(<MessageThread executions={[exec]} virtualized />);
|
|
353
|
+
|
|
354
|
+
await vi.waitFor(() => {
|
|
355
|
+
const data = capturedVirtuosoProps.data as ThreadItem[];
|
|
356
|
+
expect(data.map((d) => d.key)).toEqual(items.map((i) => i.key));
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("applies entry animation only to tail items (last 2)", async () => {
|
|
361
|
+
const execs = [
|
|
362
|
+
makeExecution("e1", "First", "Response 1"),
|
|
363
|
+
makeExecution("e2", "Second", "Response 2"),
|
|
364
|
+
makeExecution("e3", "Third", "Response 3"),
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
const MessageThread = await importMessageThread();
|
|
368
|
+
render(<MessageThread executions={execs} virtualized />);
|
|
369
|
+
|
|
370
|
+
await vi.waitFor(() => {
|
|
371
|
+
const data = capturedVirtuosoProps.data as ThreadItem[];
|
|
372
|
+
expect(data.length).toBeGreaterThan(2);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const data = capturedVirtuosoProps.data as ThreadItem[];
|
|
376
|
+
const itemContent = capturedVirtuosoProps.itemContent as (
|
|
377
|
+
index: number,
|
|
378
|
+
item: ThreadItem,
|
|
379
|
+
) => React.ReactNode;
|
|
380
|
+
const tailThreshold = data.length - 2;
|
|
381
|
+
|
|
382
|
+
// Non-tail item: no animation wrapper
|
|
383
|
+
const earlyResult = render(
|
|
384
|
+
<div data-testid="early">{itemContent(0, data[0])}</div>,
|
|
385
|
+
);
|
|
386
|
+
expect(
|
|
387
|
+
earlyResult.container.querySelector(".stgm-thread-item-enter"),
|
|
388
|
+
).toBeNull();
|
|
389
|
+
earlyResult.unmount();
|
|
390
|
+
|
|
391
|
+
// Tail item: has animation wrapper
|
|
392
|
+
const lastIdx = data.length - 1;
|
|
393
|
+
const tailResult = render(
|
|
394
|
+
<div data-testid="tail">{itemContent(lastIdx, data[lastIdx])}</div>,
|
|
395
|
+
);
|
|
396
|
+
expect(
|
|
397
|
+
tailResult.container.querySelector(".stgm-thread-item-enter"),
|
|
398
|
+
).toBeTruthy();
|
|
399
|
+
tailResult.unmount();
|
|
400
|
+
});
|
|
401
|
+
});
|
package/src/execution/index.ts
CHANGED
|
@@ -87,6 +87,9 @@ export type { MessageEntryProps } from "./MessageEntry";
|
|
|
87
87
|
export { MessageThread } from "./MessageThread";
|
|
88
88
|
export type { MessageThreadProps } from "./MessageThread";
|
|
89
89
|
|
|
90
|
+
export { ThreadSkeleton } from "./ThreadSkeleton";
|
|
91
|
+
export type { ThreadSkeletonProps } from "./ThreadSkeleton";
|
|
92
|
+
|
|
90
93
|
export { FollowUpInput } from "./FollowUpInput";
|
|
91
94
|
export type { FollowUpInputProps } from "./FollowUpInput";
|
|
92
95
|
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
startTransition,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
useSyncExternalStore,
|
|
11
|
+
} from "react";
|
|
4
12
|
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
5
13
|
import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
6
14
|
import { useStigmer } from "../hooks";
|
|
7
15
|
import { toError } from "../internal/toError";
|
|
16
|
+
import { useStreamRate } from "../internal/dev";
|
|
17
|
+
import {
|
|
18
|
+
StreamController,
|
|
19
|
+
type StreamControllerSink,
|
|
20
|
+
} from "../internal/stream-controller";
|
|
21
|
+
import { ConversationStore, type StreamState } from "../internal/store";
|
|
8
22
|
import { isTerminalPhase } from "./execution-phases";
|
|
9
23
|
|
|
10
24
|
/** Return value of {@link useExecutionStream}. */
|
|
@@ -14,9 +28,9 @@ export interface UseExecutionStreamReturn {
|
|
|
14
28
|
/**
|
|
15
29
|
* Convenience extraction of `execution.status.phase`.
|
|
16
30
|
*
|
|
17
|
-
* Derived from `execution`
|
|
18
|
-
*
|
|
19
|
-
*
|
|
31
|
+
* Derived from `execution` — always consistent with the current
|
|
32
|
+
* snapshot. Returns `EXECUTION_PHASE_UNSPECIFIED` when `execution`
|
|
33
|
+
* is `null`.
|
|
20
34
|
*/
|
|
21
35
|
readonly phase: ExecutionPhase;
|
|
22
36
|
/** `true` while receiving non-terminal updates from the server stream. */
|
|
@@ -35,17 +49,42 @@ export interface UseExecutionStreamReturn {
|
|
|
35
49
|
readonly reconnect: () => void;
|
|
36
50
|
}
|
|
37
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Options for {@link useExecutionStream}.
|
|
54
|
+
*/
|
|
55
|
+
export interface UseExecutionStreamOptions {
|
|
56
|
+
/**
|
|
57
|
+
* External `ConversationStore` to ingest snapshots into.
|
|
58
|
+
*
|
|
59
|
+
* When provided, the hook writes directly to this store and reads
|
|
60
|
+
* the execution snapshot back via `useSyncExternalStore`. This
|
|
61
|
+
* allows `useSessionConversation` to share a single store instance
|
|
62
|
+
* across the stream hook and the rendering tree.
|
|
63
|
+
*
|
|
64
|
+
* When omitted, an internal store is created automatically —
|
|
65
|
+
* preserving backward compatibility for standalone usage.
|
|
66
|
+
*/
|
|
67
|
+
readonly store?: ConversationStore;
|
|
68
|
+
}
|
|
69
|
+
|
|
38
70
|
/**
|
|
39
71
|
* Behavior hook that subscribes to real-time {@link AgentExecution}
|
|
40
72
|
* updates via `stigmer.agentExecution.subscribe()`.
|
|
41
73
|
*
|
|
42
|
-
* Manages the full subscription lifecycle
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
74
|
+
* Manages the full subscription lifecycle through a finite state
|
|
75
|
+
* machine: connection establishment, rAF-coalesced snapshot streaming,
|
|
76
|
+
* terminal-phase detection, error handling, and manual reconnection.
|
|
77
|
+
*
|
|
78
|
+
* **Performance characteristics:**
|
|
79
|
+
* - Non-terminal snapshots are coalesced via `requestAnimationFrame`
|
|
80
|
+
* so React commits at most once per display frame (~60Hz)
|
|
81
|
+
* - Terminal snapshots (complete/failed/cancelled) flush immediately
|
|
82
|
+
* - Store updates are wrapped in `startTransition` so thread renders
|
|
83
|
+
* don't block urgent interactions (e.g. composer typing)
|
|
46
84
|
*
|
|
47
|
-
* Pass `null` to skip subscribing (stable no-op).
|
|
48
|
-
* changes, the previous subscription is aborted
|
|
85
|
+
* Pass `null` as `executionId` to skip subscribing (stable no-op).
|
|
86
|
+
* When `executionId` changes, the previous subscription is aborted
|
|
87
|
+
* and a fresh one begins.
|
|
49
88
|
*
|
|
50
89
|
* @example
|
|
51
90
|
* ```tsx
|
|
@@ -69,82 +108,118 @@ export interface UseExecutionStreamReturn {
|
|
|
69
108
|
*/
|
|
70
109
|
export function useExecutionStream(
|
|
71
110
|
executionId: string | null,
|
|
111
|
+
options?: UseExecutionStreamOptions,
|
|
72
112
|
): UseExecutionStreamReturn {
|
|
73
113
|
const stigmer = useStigmer();
|
|
74
114
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
115
|
+
// -- Store setup ----------------------------------------------------------
|
|
116
|
+
// Use the externally provided store, or create a private one for
|
|
117
|
+
// standalone usage. The ref ensures the internal store is stable
|
|
118
|
+
// across re-renders.
|
|
119
|
+
const internalStoreRef = useRef<ConversationStore | null>(null);
|
|
120
|
+
if (!options?.store && !internalStoreRef.current) {
|
|
121
|
+
internalStoreRef.current = new ConversationStore();
|
|
122
|
+
}
|
|
123
|
+
const store = options?.store ?? internalStoreRef.current!;
|
|
124
|
+
|
|
125
|
+
// -- Controller setup -----------------------------------------------------
|
|
126
|
+
const controllerRef = useRef<StreamController | null>(null);
|
|
127
|
+
if (!controllerRef.current) {
|
|
128
|
+
const sink: StreamControllerSink = {
|
|
129
|
+
ingestSnapshot(snapshot) {
|
|
130
|
+
startTransition(() => {
|
|
131
|
+
store.ingestSnapshot(snapshot);
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
setStreamState(state) {
|
|
135
|
+
startTransition(() => {
|
|
136
|
+
store.setStreamState(state);
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
controllerRef.current = new StreamController(sink);
|
|
141
|
+
}
|
|
142
|
+
const controller = controllerRef.current;
|
|
82
143
|
|
|
144
|
+
// -- Reconnect ------------------------------------------------------------
|
|
145
|
+
const [connectKey, setConnectKey] = useState(0);
|
|
83
146
|
const reconnect = useCallback(() => {
|
|
84
|
-
setError(null);
|
|
85
147
|
setConnectKey((k) => k + 1);
|
|
86
148
|
}, []);
|
|
87
149
|
|
|
150
|
+
// -- Stream rate instrumentation ------------------------------------------
|
|
151
|
+
const streamRate = useStreamRate();
|
|
152
|
+
const streamRateRef = useRef(streamRate);
|
|
153
|
+
streamRateRef.current = streamRate;
|
|
154
|
+
|
|
155
|
+
// -- Subscription effect --------------------------------------------------
|
|
156
|
+
// Note: controller, store, and streamRate are ref-backed stable objects —
|
|
157
|
+
// they MUST NOT appear in the deps array. Including them would cause
|
|
158
|
+
// infinite re-renders because useStreamRate returns a new object per render.
|
|
88
159
|
useEffect(() => {
|
|
89
160
|
if (!executionId) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
setIsStreaming(false);
|
|
93
|
-
setError(null);
|
|
161
|
+
controller.reset();
|
|
162
|
+
store.reset();
|
|
94
163
|
return;
|
|
95
164
|
}
|
|
96
165
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
abortRef.current = controller;
|
|
100
|
-
|
|
101
|
-
setExecution(null);
|
|
102
|
-
setIsConnecting(true);
|
|
103
|
-
setIsStreaming(false);
|
|
104
|
-
setError(null);
|
|
166
|
+
const abortController = new AbortController();
|
|
167
|
+
controller.start(executionId);
|
|
105
168
|
|
|
106
169
|
(async () => {
|
|
107
170
|
try {
|
|
108
171
|
for await (const snapshot of stigmer.agentExecution.subscribe(
|
|
109
172
|
executionId,
|
|
110
|
-
|
|
173
|
+
abortController.signal,
|
|
111
174
|
)) {
|
|
112
|
-
if (
|
|
175
|
+
if (abortController.signal.aborted) return;
|
|
176
|
+
|
|
177
|
+
controller.handleSnapshot(snapshot);
|
|
178
|
+
streamRateRef.current.tick(
|
|
179
|
+
snapshot.status?.messages?.length ?? 0,
|
|
180
|
+
);
|
|
113
181
|
|
|
114
|
-
const
|
|
182
|
+
const phase =
|
|
115
183
|
snapshot.status?.phase ??
|
|
116
184
|
ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
setExecution(snapshot);
|
|
120
|
-
setIsConnecting(false);
|
|
121
|
-
setIsStreaming(!isTerminal);
|
|
122
|
-
|
|
123
|
-
if (isTerminal) break;
|
|
185
|
+
if (isTerminalPhase(phase)) break;
|
|
124
186
|
}
|
|
125
187
|
|
|
126
|
-
if (!
|
|
127
|
-
|
|
188
|
+
if (!abortController.signal.aborted) {
|
|
189
|
+
controller.handleStreamEnd();
|
|
190
|
+
streamRateRef.current.summary();
|
|
128
191
|
}
|
|
129
192
|
} catch (err) {
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
setError(toError(err));
|
|
133
|
-
setIsConnecting(false);
|
|
134
|
-
setIsStreaming(false);
|
|
193
|
+
if (abortController.signal.aborted) return;
|
|
194
|
+
controller.handleError(toError(err));
|
|
135
195
|
}
|
|
136
196
|
})();
|
|
137
197
|
|
|
138
198
|
return () => {
|
|
139
|
-
|
|
199
|
+
abortController.abort();
|
|
200
|
+
controller.reset();
|
|
201
|
+
store.reset();
|
|
140
202
|
};
|
|
141
203
|
}, [executionId, stigmer, connectKey]);
|
|
142
204
|
|
|
205
|
+
// -- Read from store via useSyncExternalStore ------------------------------
|
|
206
|
+
const execution = useSyncExternalStore(store.subscribe, store.getExecution);
|
|
207
|
+
const streamState = useSyncExternalStore(
|
|
208
|
+
store.subscribe,
|
|
209
|
+
store.getStreamState,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// -- Derive public return values ------------------------------------------
|
|
143
213
|
const phase = useMemo(
|
|
144
214
|
() =>
|
|
145
215
|
execution?.status?.phase ?? ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED,
|
|
146
216
|
[execution],
|
|
147
217
|
);
|
|
148
218
|
|
|
219
|
+
const isStreaming = streamState.stage === "streaming";
|
|
220
|
+
const isConnecting = streamState.stage === "connecting";
|
|
221
|
+
const error =
|
|
222
|
+
streamState.stage === "error" ? streamState.error : null;
|
|
223
|
+
|
|
149
224
|
return { execution, phase, isStreaming, isConnecting, error, reconnect };
|
|
150
225
|
}
|
|
@@ -180,16 +180,21 @@ export function useSessionVariables(): UseSessionVariablesReturn {
|
|
|
180
180
|
[entries],
|
|
181
181
|
);
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
183
|
+
const isEmpty = entries.length === 0;
|
|
184
|
+
|
|
185
|
+
return useMemo(
|
|
186
|
+
() => ({
|
|
187
|
+
entries,
|
|
188
|
+
addEntry,
|
|
189
|
+
removeEntry,
|
|
190
|
+
updateEntry,
|
|
191
|
+
clear,
|
|
192
|
+
isEmpty,
|
|
193
|
+
hasValidEntries,
|
|
194
|
+
toRuntimeEnv,
|
|
195
|
+
toSaveForFutureEnv,
|
|
196
|
+
hasSaveForFutureEntries,
|
|
197
|
+
}),
|
|
198
|
+
[entries, addEntry, removeEntry, updateEntry, clear, isEmpty, hasValidEntries, toRuntimeEnv, toSaveForFutureEnv, hasSaveForFutureEntries],
|
|
199
|
+
);
|
|
195
200
|
}
|