@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
package/src/internal/useFetch.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { type DependencyList, useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { useFetchCache } from "./FetchCacheProvider";
|
|
4
5
|
import { toError } from "./toError";
|
|
5
6
|
|
|
6
7
|
/** Options for {@link useFetch}. */
|
|
@@ -13,6 +14,21 @@ export interface UseFetchOptions {
|
|
|
13
14
|
* request piling on slow connections.
|
|
14
15
|
*/
|
|
15
16
|
readonly refetchInterval?: number | false;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Stable string key for cross-mount caching.
|
|
20
|
+
*
|
|
21
|
+
* When provided (and a {@link FetchCacheProvider} is mounted above
|
|
22
|
+
* this component), the hook reads cached data on mount to avoid a
|
|
23
|
+
* loading skeleton, and writes fresh data to the cache on every
|
|
24
|
+
* successful fetch.
|
|
25
|
+
*
|
|
26
|
+
* Pass `undefined` to opt out of caching for a given call.
|
|
27
|
+
*
|
|
28
|
+
* @example `session:${id}`
|
|
29
|
+
* @example `session-executions:${sessionId}`
|
|
30
|
+
*/
|
|
31
|
+
readonly cacheKey?: string;
|
|
16
32
|
}
|
|
17
33
|
|
|
18
34
|
/** Return value of {@link useFetch}. */
|
|
@@ -59,14 +75,33 @@ export function useFetch<T>(
|
|
|
59
75
|
initialData: T,
|
|
60
76
|
options?: UseFetchOptions,
|
|
61
77
|
): UseFetchReturn<T> {
|
|
62
|
-
const
|
|
78
|
+
const cache = useFetchCache();
|
|
79
|
+
const cacheKey = options?.cacheKey;
|
|
80
|
+
|
|
81
|
+
// Resolve initial state from cache when available. The initializer
|
|
82
|
+
// function runs once on mount — exactly the right time to seed state
|
|
83
|
+
// from a previous mount's result and skip the loading skeleton.
|
|
84
|
+
const [data, setData] = useState<T>(() => {
|
|
85
|
+
if (cacheKey && cache) {
|
|
86
|
+
const cached = cache.get<T>(cacheKey);
|
|
87
|
+
if (cached !== undefined) return cached;
|
|
88
|
+
}
|
|
89
|
+
return initialData;
|
|
90
|
+
});
|
|
63
91
|
const [error, setError] = useState<Error | null>(null);
|
|
64
92
|
const [fetchKey, setFetchKey] = useState(0);
|
|
65
93
|
|
|
66
|
-
const hasDataRef = useRef(
|
|
94
|
+
const hasDataRef = useRef(
|
|
95
|
+
cacheKey && cache ? cache.has(cacheKey) : false,
|
|
96
|
+
);
|
|
67
97
|
const isFetchingRef = useRef(false);
|
|
68
98
|
const [isFetching, setIsFetching] = useState(false);
|
|
69
99
|
|
|
100
|
+
// Stable ref for cache — avoids adding cache to effect deps while
|
|
101
|
+
// still letting the effect body access the current instance.
|
|
102
|
+
const cacheRef = useRef(cache);
|
|
103
|
+
cacheRef.current = cache;
|
|
104
|
+
|
|
70
105
|
const refetch = useCallback(() => setFetchKey((k) => k + 1), []);
|
|
71
106
|
|
|
72
107
|
useEffect(() => {
|
|
@@ -79,6 +114,17 @@ export function useFetch<T>(
|
|
|
79
114
|
return;
|
|
80
115
|
}
|
|
81
116
|
|
|
117
|
+
// On dep change (without remount), check cache for the new key so
|
|
118
|
+
// we can show cached data immediately rather than stale data from
|
|
119
|
+
// a different identity (e.g. session A's data while session B loads).
|
|
120
|
+
if (cacheKey && cacheRef.current) {
|
|
121
|
+
const cached = cacheRef.current.get<T>(cacheKey);
|
|
122
|
+
if (cached !== undefined) {
|
|
123
|
+
setData(cached);
|
|
124
|
+
hasDataRef.current = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
82
128
|
const cancelled = { current: false };
|
|
83
129
|
setIsFetching(true);
|
|
84
130
|
isFetchingRef.current = true;
|
|
@@ -91,6 +137,9 @@ export function useFetch<T>(
|
|
|
91
137
|
hasDataRef.current = true;
|
|
92
138
|
setIsFetching(false);
|
|
93
139
|
isFetchingRef.current = false;
|
|
140
|
+
if (cacheKey && cacheRef.current) {
|
|
141
|
+
cacheRef.current.set(cacheKey, result);
|
|
142
|
+
}
|
|
94
143
|
},
|
|
95
144
|
(err) => {
|
|
96
145
|
if (cancelled.current) return;
|
|
@@ -190,6 +190,28 @@ describe("useNewSessionFlow", () => {
|
|
|
190
190
|
// DEFAULT_MODEL_ID (anthropic) is not in cursor registry
|
|
191
191
|
expect(result.current.modelId).not.toBe(DEFAULT_MODEL_ID);
|
|
192
192
|
});
|
|
193
|
+
|
|
194
|
+
it("strips compound keys before persisting to localStorage", () => {
|
|
195
|
+
localStorage.setItem(STORAGE_KEY_HARNESS, "cursor");
|
|
196
|
+
const { result } = renderHook(() => useNewSessionFlow(defaultOptions()));
|
|
197
|
+
|
|
198
|
+
// Simulate compound key from unified mode ModelSelector
|
|
199
|
+
act(() => result.current.setModelId("cursor/default"));
|
|
200
|
+
|
|
201
|
+
// Should store plain modelId, not compound key
|
|
202
|
+
expect(localStorage.getItem(STORAGE_KEY_MODEL_CURSOR)).toBe("default");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("restores compound keys from localStorage as plain modelId", () => {
|
|
206
|
+
localStorage.setItem(STORAGE_KEY_HARNESS, "cursor");
|
|
207
|
+
// Legacy: compound key was stored before fix
|
|
208
|
+
localStorage.setItem(STORAGE_KEY_MODEL_CURSOR, "cursor/default");
|
|
209
|
+
|
|
210
|
+
const { result } = renderHook(() => useNewSessionFlow(defaultOptions()));
|
|
211
|
+
|
|
212
|
+
// Should extract plain modelId and validate against registry
|
|
213
|
+
expect(result.current.modelId).toBe(DEFAULT_CURSOR_MODEL_ID);
|
|
214
|
+
});
|
|
193
215
|
});
|
|
194
216
|
|
|
195
217
|
describe("submit with harness", () => {
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { renderHook, act } from "@testing-library/react";
|
|
3
|
+
import { usePersistedModel } from "../usePersistedModel";
|
|
4
|
+
import { DEFAULT_MODEL_ID, DEFAULT_CURSOR_MODEL_ID } from "../../models/registry";
|
|
5
|
+
|
|
6
|
+
const STORAGE_KEY_NATIVE = "stigmer:session:model";
|
|
7
|
+
const STORAGE_KEY_CURSOR = "stigmer:session:model:cursor";
|
|
8
|
+
|
|
9
|
+
describe("usePersistedModel", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
localStorage.clear();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
localStorage.clear();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("basic persistence", () => {
|
|
19
|
+
it("returns undefined when localStorage is empty", () => {
|
|
20
|
+
const { result } = renderHook(() => usePersistedModel({ harness: "native" }));
|
|
21
|
+
expect(result.current[0]).toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("restores a valid model from localStorage", () => {
|
|
25
|
+
localStorage.setItem(STORAGE_KEY_NATIVE, DEFAULT_MODEL_ID);
|
|
26
|
+
const { result } = renderHook(() => usePersistedModel({ harness: "native" }));
|
|
27
|
+
expect(result.current[0]).toBe(DEFAULT_MODEL_ID);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("returns undefined for an invalid model in localStorage", () => {
|
|
31
|
+
localStorage.setItem(STORAGE_KEY_NATIVE, "nonexistent-model-xyz");
|
|
32
|
+
const { result } = renderHook(() => usePersistedModel({ harness: "native" }));
|
|
33
|
+
expect(result.current[0]).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("persists model on change", () => {
|
|
37
|
+
const { result } = renderHook(() => usePersistedModel({ harness: "native" }));
|
|
38
|
+
|
|
39
|
+
act(() => result.current[1](DEFAULT_MODEL_ID));
|
|
40
|
+
|
|
41
|
+
expect(localStorage.getItem(STORAGE_KEY_NATIVE)).toBe(DEFAULT_MODEL_ID);
|
|
42
|
+
expect(result.current[0]).toBe(DEFAULT_MODEL_ID);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("uses cursor-specific key for cursor harness", () => {
|
|
46
|
+
localStorage.setItem(STORAGE_KEY_CURSOR, DEFAULT_CURSOR_MODEL_ID);
|
|
47
|
+
const { result } = renderHook(() => usePersistedModel({ harness: "cursor" }));
|
|
48
|
+
expect(result.current[0]).toBe(DEFAULT_CURSOR_MODEL_ID);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("compound key handling", () => {
|
|
53
|
+
it("extracts plain modelId from compound key in localStorage", () => {
|
|
54
|
+
localStorage.setItem(STORAGE_KEY_CURSOR, "cursor/default");
|
|
55
|
+
const { result } = renderHook(() => usePersistedModel({ harness: "cursor" }));
|
|
56
|
+
expect(result.current[0]).toBe(DEFAULT_CURSOR_MODEL_ID);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("extracts plain modelId from native compound key", () => {
|
|
60
|
+
localStorage.setItem(STORAGE_KEY_NATIVE, `native/${DEFAULT_MODEL_ID}`);
|
|
61
|
+
const { result } = renderHook(() => usePersistedModel({ harness: "native" }));
|
|
62
|
+
expect(result.current[0]).toBe(DEFAULT_MODEL_ID);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("handles non-compound values unchanged", () => {
|
|
66
|
+
localStorage.setItem(STORAGE_KEY_CURSOR, DEFAULT_CURSOR_MODEL_ID);
|
|
67
|
+
const { result } = renderHook(() => usePersistedModel({ harness: "cursor" }));
|
|
68
|
+
expect(result.current[0]).toBe(DEFAULT_CURSOR_MODEL_ID);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("harness transition (key change re-sync)", () => {
|
|
73
|
+
it("re-reads from new localStorage key when harness changes", () => {
|
|
74
|
+
localStorage.setItem(STORAGE_KEY_NATIVE, DEFAULT_MODEL_ID);
|
|
75
|
+
localStorage.setItem(STORAGE_KEY_CURSOR, DEFAULT_CURSOR_MODEL_ID);
|
|
76
|
+
|
|
77
|
+
const { result, rerender } = renderHook(
|
|
78
|
+
({ harness }: { harness: "native" | "cursor" }) => usePersistedModel({ harness }),
|
|
79
|
+
{ initialProps: { harness: "native" } },
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(result.current[0]).toBe(DEFAULT_MODEL_ID);
|
|
83
|
+
|
|
84
|
+
rerender({ harness: "cursor" });
|
|
85
|
+
|
|
86
|
+
expect(result.current[0]).toBe(DEFAULT_CURSOR_MODEL_ID);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("returns undefined after harness change when new key has no stored value", () => {
|
|
90
|
+
localStorage.setItem(STORAGE_KEY_NATIVE, DEFAULT_MODEL_ID);
|
|
91
|
+
|
|
92
|
+
const { result, rerender } = renderHook(
|
|
93
|
+
({ harness }: { harness: "native" | "cursor" }) => usePersistedModel({ harness }),
|
|
94
|
+
{ initialProps: { harness: "native" } },
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(result.current[0]).toBe(DEFAULT_MODEL_ID);
|
|
98
|
+
|
|
99
|
+
rerender({ harness: "cursor" });
|
|
100
|
+
|
|
101
|
+
expect(result.current[0]).toBeUndefined();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("handles compound key in new storage key after harness transition", () => {
|
|
105
|
+
localStorage.setItem(STORAGE_KEY_CURSOR, "cursor/default");
|
|
106
|
+
|
|
107
|
+
const { result, rerender } = renderHook(
|
|
108
|
+
({ harness }: { harness: "native" | "cursor" }) => usePersistedModel({ harness }),
|
|
109
|
+
{ initialProps: { harness: "native" } },
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
rerender({ harness: "cursor" });
|
|
113
|
+
|
|
114
|
+
expect(result.current[0]).toBe(DEFAULT_CURSOR_MODEL_ID);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { renderHook, act } from "@testing-library/react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { create } from "@bufbuild/protobuf";
|
|
5
|
+
import {
|
|
6
|
+
SessionSchema,
|
|
7
|
+
type Session,
|
|
8
|
+
} from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
|
|
9
|
+
import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
|
|
10
|
+
import type { Stigmer } from "@stigmer/sdk";
|
|
11
|
+
import { StigmerContext } from "../../context";
|
|
12
|
+
import { FetchCacheContext } from "../../internal/FetchCacheProvider";
|
|
13
|
+
import { FetchCache } from "../../internal/fetch-cache";
|
|
14
|
+
import { useSession } from "../useSession";
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Helpers
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
function makeSession(id: string): Session {
|
|
21
|
+
const session = create(SessionSchema);
|
|
22
|
+
const metadata = create(ApiResourceMetadataSchema);
|
|
23
|
+
metadata.id = id;
|
|
24
|
+
session.metadata = metadata;
|
|
25
|
+
return session;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function createMockStigmer(sessionGet: ReturnType<typeof vi.fn>): Stigmer {
|
|
29
|
+
return {
|
|
30
|
+
session: { get: sessionGet },
|
|
31
|
+
} as unknown as Stigmer;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function flush(): Promise<void> {
|
|
35
|
+
await act(async () => {
|
|
36
|
+
await Promise.resolve();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Wrapper that provides both the Stigmer client and a shared FetchCache
|
|
42
|
+
* instance via context. Sharing the cache instance across renderHook
|
|
43
|
+
* calls mirrors the production layout where FetchCacheProvider sits
|
|
44
|
+
* above the key-based remount boundary.
|
|
45
|
+
*/
|
|
46
|
+
function createWrapper(client: Stigmer, cache: FetchCache) {
|
|
47
|
+
return function Wrapper({ children }: { children: ReactNode }) {
|
|
48
|
+
return (
|
|
49
|
+
<FetchCacheContext.Provider value={cache}>
|
|
50
|
+
<StigmerContext.Provider value={client}>
|
|
51
|
+
{children}
|
|
52
|
+
</StigmerContext.Provider>
|
|
53
|
+
</FetchCacheContext.Provider>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Tests
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
describe("useSession — cache behavior", () => {
|
|
63
|
+
it("first visit shows loading then resolves data", async () => {
|
|
64
|
+
const session = makeSession("ses_1");
|
|
65
|
+
const sessionGet = vi.fn().mockResolvedValue(session);
|
|
66
|
+
const cache = new FetchCache();
|
|
67
|
+
const wrapper = createWrapper(createMockStigmer(sessionGet), cache);
|
|
68
|
+
|
|
69
|
+
const { result } = renderHook(() => useSession("ses_1"), { wrapper });
|
|
70
|
+
|
|
71
|
+
expect(result.current.isLoading).toBe(true);
|
|
72
|
+
expect(result.current.session).toBeNull();
|
|
73
|
+
|
|
74
|
+
await flush();
|
|
75
|
+
|
|
76
|
+
expect(result.current.isLoading).toBe(false);
|
|
77
|
+
expect(result.current.session).toBe(session);
|
|
78
|
+
expect(sessionGet).toHaveBeenCalledOnce();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("remount serves cached data instantly (no isLoading)", async () => {
|
|
82
|
+
const session1 = makeSession("ses_1");
|
|
83
|
+
const sessionGet = vi.fn().mockResolvedValue(session1);
|
|
84
|
+
const client = createMockStigmer(sessionGet);
|
|
85
|
+
const cache = new FetchCache();
|
|
86
|
+
const wrapper = createWrapper(client, cache);
|
|
87
|
+
|
|
88
|
+
// First mount — populates the cache.
|
|
89
|
+
const { result: r1, unmount } = renderHook(
|
|
90
|
+
() => useSession("ses_1"),
|
|
91
|
+
{ wrapper },
|
|
92
|
+
);
|
|
93
|
+
await flush();
|
|
94
|
+
expect(r1.current.session).toBe(session1);
|
|
95
|
+
unmount();
|
|
96
|
+
|
|
97
|
+
// Second mount (simulates remount after key={activeSessionId} change).
|
|
98
|
+
const freshSession = makeSession("ses_1");
|
|
99
|
+
sessionGet.mockResolvedValue(freshSession);
|
|
100
|
+
|
|
101
|
+
const { result: r2 } = renderHook(
|
|
102
|
+
() => useSession("ses_1"),
|
|
103
|
+
{ wrapper },
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// Cached data is served synchronously — no loading skeleton.
|
|
107
|
+
expect(r2.current.isLoading).toBe(false);
|
|
108
|
+
expect(r2.current.session).toBe(session1);
|
|
109
|
+
expect(r2.current.isRefetching).toBe(true);
|
|
110
|
+
|
|
111
|
+
// Background fetch completes with fresh data.
|
|
112
|
+
await flush();
|
|
113
|
+
expect(r2.current.session).toBe(freshSession);
|
|
114
|
+
expect(r2.current.isRefetching).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("different session IDs have independent cache entries", async () => {
|
|
118
|
+
const sessionA = makeSession("ses_A");
|
|
119
|
+
const sessionB = makeSession("ses_B");
|
|
120
|
+
const sessionGet = vi.fn()
|
|
121
|
+
.mockResolvedValueOnce(sessionA)
|
|
122
|
+
.mockResolvedValueOnce(sessionB)
|
|
123
|
+
.mockResolvedValue(sessionA);
|
|
124
|
+
const cache = new FetchCache();
|
|
125
|
+
const wrapper = createWrapper(createMockStigmer(sessionGet), cache);
|
|
126
|
+
|
|
127
|
+
// Mount session A.
|
|
128
|
+
const { unmount: unmountA } = renderHook(
|
|
129
|
+
() => useSession("ses_A"),
|
|
130
|
+
{ wrapper },
|
|
131
|
+
);
|
|
132
|
+
await flush();
|
|
133
|
+
unmountA();
|
|
134
|
+
|
|
135
|
+
// Mount session B.
|
|
136
|
+
const { unmount: unmountB } = renderHook(
|
|
137
|
+
() => useSession("ses_B"),
|
|
138
|
+
{ wrapper },
|
|
139
|
+
);
|
|
140
|
+
await flush();
|
|
141
|
+
unmountB();
|
|
142
|
+
|
|
143
|
+
// Remount session A — should get A's cached data, not B's.
|
|
144
|
+
const { result } = renderHook(
|
|
145
|
+
() => useSession("ses_A"),
|
|
146
|
+
{ wrapper },
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(result.current.isLoading).toBe(false);
|
|
150
|
+
expect(result.current.session?.metadata?.id).toBe("ses_A");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("works without FetchCacheProvider (standard loading flow)", async () => {
|
|
154
|
+
const session = makeSession("ses_1");
|
|
155
|
+
const sessionGet = vi.fn().mockResolvedValue(session);
|
|
156
|
+
const client = createMockStigmer(sessionGet);
|
|
157
|
+
|
|
158
|
+
function NoCacheWrapper({ children }: { children: ReactNode }) {
|
|
159
|
+
return (
|
|
160
|
+
<StigmerContext.Provider value={client}>
|
|
161
|
+
{children}
|
|
162
|
+
</StigmerContext.Provider>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const { result } = renderHook(() => useSession("ses_1"), {
|
|
167
|
+
wrapper: NoCacheWrapper,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(result.current.isLoading).toBe(true);
|
|
171
|
+
await flush();
|
|
172
|
+
expect(result.current.session).toBe(session);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("null id skips fetching and caching", async () => {
|
|
176
|
+
const sessionGet = vi.fn();
|
|
177
|
+
const cache = new FetchCache();
|
|
178
|
+
const wrapper = createWrapper(createMockStigmer(sessionGet), cache);
|
|
179
|
+
|
|
180
|
+
const { result } = renderHook(() => useSession(null), { wrapper });
|
|
181
|
+
|
|
182
|
+
expect(result.current.isLoading).toBe(false);
|
|
183
|
+
expect(result.current.session).toBeNull();
|
|
184
|
+
expect(sessionGet).not.toHaveBeenCalled();
|
|
185
|
+
expect(cache.size).toBe(0);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -5,6 +5,7 @@ import { getUserMessage, type McpServerUsageInput, type ResourceRef } from "@sti
|
|
|
5
5
|
import type { AgentResolution } from "../agent";
|
|
6
6
|
import { useDefaultAgent } from "../agent";
|
|
7
7
|
import { useModelRegistry } from "../models";
|
|
8
|
+
import { parseModelKey } from "../models/registry";
|
|
8
9
|
import { DEFAULT_HARNESS, type HarnessOption } from "../models/harness";
|
|
9
10
|
import { useWorkspaceEntries, type UseWorkspaceEntriesReturn } from "../workspace";
|
|
10
11
|
import { useSessionVariables, type UseSessionVariablesReturn } from "../execution/useSessionVariables";
|
|
@@ -184,9 +185,9 @@ export function useNewSessionFlow(
|
|
|
184
185
|
const setHarness = useCallback(
|
|
185
186
|
(h: HarnessOption) => {
|
|
186
187
|
setHarnessRaw(h);
|
|
187
|
-
// Restore per-harness model preference, or clear if none stored
|
|
188
188
|
const storedModel = localStorage.getItem(modelStorageKey(h));
|
|
189
|
-
|
|
189
|
+
const plain = storedModel ? (parseModelKey(storedModel)?.modelId ?? storedModel) : undefined;
|
|
190
|
+
setModelId(plain);
|
|
190
191
|
},
|
|
191
192
|
[],
|
|
192
193
|
);
|
|
@@ -194,15 +195,20 @@ export function useNewSessionFlow(
|
|
|
194
195
|
// Restore persisted model on mount (using current harness key)
|
|
195
196
|
useEffect(() => {
|
|
196
197
|
const stored = localStorage.getItem(modelStorageKey(harness));
|
|
197
|
-
if (stored
|
|
198
|
-
|
|
198
|
+
if (stored) {
|
|
199
|
+
const plain = parseModelKey(stored)?.modelId ?? stored;
|
|
200
|
+
if (getModel(plain)) {
|
|
201
|
+
setModelId(plain);
|
|
202
|
+
}
|
|
199
203
|
}
|
|
200
204
|
}, [getModel, harness]);
|
|
201
205
|
|
|
202
|
-
// Persist model on change (using current harness key)
|
|
206
|
+
// Persist model on change (using current harness key).
|
|
207
|
+
// Strip compound keys (e.g. "cursor/default") to plain modelId before storing.
|
|
203
208
|
useEffect(() => {
|
|
204
209
|
if (modelId) {
|
|
205
|
-
|
|
210
|
+
const plain = parseModelKey(modelId)?.modelId ?? modelId;
|
|
211
|
+
localStorage.setItem(modelStorageKey(harness), plain);
|
|
206
212
|
}
|
|
207
213
|
}, [modelId, harness]);
|
|
208
214
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState } from "react";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { useModelRegistry } from "../models";
|
|
5
|
+
import { parseModelKey } from "../models/registry";
|
|
5
6
|
import type { HarnessOption } from "../models/harness";
|
|
6
7
|
|
|
7
8
|
/** Options for {@link usePersistedModel}. */
|
|
@@ -28,6 +29,16 @@ function storageKey(harness?: HarnessOption): string {
|
|
|
28
29
|
: "stigmer:session:model";
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Extract the plain modelId from a value that might be a compound key
|
|
34
|
+
* (e.g. `"cursor/default"` → `"default"`). Returns the value unchanged
|
|
35
|
+
* if it's already a plain ID.
|
|
36
|
+
*/
|
|
37
|
+
function extractPlainModelId(value: string): string {
|
|
38
|
+
const parsed = parseModelKey(value);
|
|
39
|
+
return parsed ? parsed.modelId : value;
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
/**
|
|
32
43
|
* Model selection with localStorage persistence.
|
|
33
44
|
*
|
|
@@ -39,6 +50,9 @@ function storageKey(harness?: HarnessOption): string {
|
|
|
39
50
|
* When `options.harness` is provided, the stored model is read from a
|
|
40
51
|
* harness-specific key and validated against the harness-filtered registry.
|
|
41
52
|
*
|
|
53
|
+
* Handles legacy compound keys (`"cursor/default"`) gracefully by
|
|
54
|
+
* extracting the plain modelId portion before validation.
|
|
55
|
+
*
|
|
42
56
|
* Used by both the session launcher (new session) and session page
|
|
43
57
|
* (follow-up messages) to maintain a consistent model preference.
|
|
44
58
|
*/
|
|
@@ -48,12 +62,24 @@ export function usePersistedModel(
|
|
|
48
62
|
const harness = options?.harness;
|
|
49
63
|
const { getModel } = useModelRegistry({ harness });
|
|
50
64
|
const key = storageKey(harness);
|
|
65
|
+
const prevKeyRef = useRef(key);
|
|
51
66
|
|
|
52
67
|
const [modelId, setModelId] = useState<string | undefined>(() => {
|
|
53
68
|
if (typeof window === "undefined") return undefined;
|
|
54
|
-
|
|
69
|
+
const raw = localStorage.getItem(key);
|
|
70
|
+
return raw ? extractPlainModelId(raw) : undefined;
|
|
55
71
|
});
|
|
56
72
|
|
|
73
|
+
// Re-read from localStorage when the storage key changes (harness transition).
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (prevKeyRef.current === key) return;
|
|
76
|
+
prevKeyRef.current = key;
|
|
77
|
+
|
|
78
|
+
if (typeof window === "undefined") return;
|
|
79
|
+
const raw = localStorage.getItem(key);
|
|
80
|
+
setModelId(raw ? extractPlainModelId(raw) : undefined);
|
|
81
|
+
}, [key]);
|
|
82
|
+
|
|
57
83
|
useEffect(() => {
|
|
58
84
|
if (modelId) {
|
|
59
85
|
localStorage.setItem(key, modelId);
|
|
@@ -62,6 +62,7 @@ export function useSession(id: string | null): UseSessionReturn {
|
|
|
62
62
|
id ? () => stigmer.session.get(id) : null,
|
|
63
63
|
[id, stigmer],
|
|
64
64
|
null as Session | null,
|
|
65
|
+
{ cacheKey: id ? `session:${id}` : undefined },
|
|
65
66
|
);
|
|
66
67
|
|
|
67
68
|
return { session, isLoading, isRefetching, error, refetch };
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
} from "@stigmer/sdk";
|
|
18
18
|
import { isTerminalPhase } from "../execution/execution-phases";
|
|
19
19
|
import { useStigmer } from "../hooks";
|
|
20
|
+
import { useConversationStoreRef } from "../internal/store";
|
|
20
21
|
import { useCreateAgentExecution } from "../execution/useCreateAgentExecution";
|
|
21
22
|
import { useExecutionStream } from "../execution/useExecutionStream";
|
|
22
23
|
import { useSubmitApproval } from "../execution/useSubmitApproval";
|
|
@@ -248,7 +249,14 @@ export function useSessionConversation(
|
|
|
248
249
|
|
|
249
250
|
const activeExecutionId = pendingExecutionId ?? listActiveId;
|
|
250
251
|
|
|
251
|
-
|
|
252
|
+
// The conversation store is shared between useExecutionStream (which
|
|
253
|
+
// ingests snapshots with structural sharing + rAF coalescing) and the
|
|
254
|
+
// rendering tree. This eliminates the duplicate structuralShare that
|
|
255
|
+
// was previously done in this hook.
|
|
256
|
+
const conversationStore = useConversationStoreRef();
|
|
257
|
+
const stream = useExecutionStream(activeExecutionId, {
|
|
258
|
+
store: conversationStore,
|
|
259
|
+
});
|
|
252
260
|
|
|
253
261
|
// Clear pendingExecutionId once the execution appears in the fetched list
|
|
254
262
|
useEffect(() => {
|
|
@@ -291,7 +299,8 @@ export function useSessionConversation(
|
|
|
291
299
|
);
|
|
292
300
|
}, [executions, activeExecutionId]);
|
|
293
301
|
|
|
294
|
-
const activeStreamExecution =
|
|
302
|
+
const activeStreamExecution =
|
|
303
|
+
stream.execution ?? fetchedActiveExecution;
|
|
295
304
|
|
|
296
305
|
const activePhase = useMemo<ExecutionPhase | null>(() => {
|
|
297
306
|
if (!activeExecutionId) return null;
|
|
@@ -253,7 +253,7 @@ export function useSessionPageFlow(
|
|
|
253
253
|
|
|
254
254
|
sessionVariables.clear();
|
|
255
255
|
},
|
|
256
|
-
[conv, modelId, workspace, mcpServerUsages, skillRefs, sessionVariables, resolution, agentRef, sessionInstanceId, stigmer],
|
|
256
|
+
[conv.sendFollowUp, modelId, workspace, mcpServerUsages, skillRefs, sessionVariables.clear, resolution, agentRef, sessionInstanceId, stigmer],
|
|
257
257
|
);
|
|
258
258
|
|
|
259
259
|
// -------------------------------------------------------------------------
|