@data-club/ai-hub 0.0.1
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/dist/auth/passwordPolicy.d.ts +55 -0
- package/dist/auth/passwordPolicy.d.ts.map +1 -0
- package/dist/auth/passwordPolicy.js +63 -0
- package/dist/auth/passwordPolicy.js.map +1 -0
- package/dist/auth/texts.d.ts +815 -0
- package/dist/auth/texts.d.ts.map +1 -0
- package/dist/auth/texts.js +429 -0
- package/dist/auth/texts.js.map +1 -0
- package/dist/client/AIHubClient.d.ts +1058 -0
- package/dist/client/AIHubClient.d.ts.map +1 -0
- package/dist/client/AIHubClient.js +950 -0
- package/dist/client/AIHubClient.js.map +1 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +9 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/sseParser.d.ts +41 -0
- package/dist/client/sseParser.d.ts.map +1 -0
- package/dist/client/sseParser.js +153 -0
- package/dist/client/sseParser.js.map +1 -0
- package/dist/client/tokenStorage.d.ts +56 -0
- package/dist/client/tokenStorage.d.ts.map +1 -0
- package/dist/client/tokenStorage.js +116 -0
- package/dist/client/tokenStorage.js.map +1 -0
- package/dist/components/AgentsPicker/index.d.ts +70 -0
- package/dist/components/AgentsPicker/index.d.ts.map +1 -0
- package/dist/components/AgentsPicker/index.js +63 -0
- package/dist/components/AgentsPicker/index.js.map +1 -0
- package/dist/components/AuthGate/index.d.ts +79 -0
- package/dist/components/AuthGate/index.d.ts.map +1 -0
- package/dist/components/AuthGate/index.js +61 -0
- package/dist/components/AuthGate/index.js.map +1 -0
- package/dist/components/CacheBadge/index.d.ts +88 -0
- package/dist/components/CacheBadge/index.d.ts.map +1 -0
- package/dist/components/CacheBadge/index.js +127 -0
- package/dist/components/CacheBadge/index.js.map +1 -0
- package/dist/components/ChangePassword/index.d.ts +109 -0
- package/dist/components/ChangePassword/index.d.ts.map +1 -0
- package/dist/components/ChangePassword/index.js +142 -0
- package/dist/components/ChangePassword/index.js.map +1 -0
- package/dist/components/ChatShell/ShellHeader.d.ts +44 -0
- package/dist/components/ChatShell/ShellHeader.d.ts.map +1 -0
- package/dist/components/ChatShell/ShellHeader.js +14 -0
- package/dist/components/ChatShell/ShellHeader.js.map +1 -0
- package/dist/components/ChatShell/UserMenu.d.ts +48 -0
- package/dist/components/ChatShell/UserMenu.d.ts.map +1 -0
- package/dist/components/ChatShell/UserMenu.js +86 -0
- package/dist/components/ChatShell/UserMenu.js.map +1 -0
- package/dist/components/ChatShell/index.d.ts +98 -0
- package/dist/components/ChatShell/index.d.ts.map +1 -0
- package/dist/components/ChatShell/index.js +124 -0
- package/dist/components/ChatShell/index.js.map +1 -0
- package/dist/components/DataClubAIHub/index.d.ts +132 -0
- package/dist/components/DataClubAIHub/index.d.ts.map +1 -0
- package/dist/components/DataClubAIHub/index.js +124 -0
- package/dist/components/DataClubAIHub/index.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts +89 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js +199 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts +33 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js +137 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts +50 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.js +93 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts +49 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js +94 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts +37 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js +44 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts +56 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js +158 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts +29 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js +183 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts +38 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js +294 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts +36 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js +373 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts +47 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.js +233 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/index.d.ts +87 -0
- package/dist/components/DataClubAIHubAdmin/index.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/index.js +137 -0
- package/dist/components/DataClubAIHubAdmin/index.js.map +1 -0
- package/dist/components/DataClubAIHubLogin/index.d.ts +113 -0
- package/dist/components/DataClubAIHubLogin/index.d.ts.map +1 -0
- package/dist/components/DataClubAIHubLogin/index.js +123 -0
- package/dist/components/DataClubAIHubLogin/index.js.map +1 -0
- package/dist/components/EffortControl/index.d.ts +57 -0
- package/dist/components/EffortControl/index.d.ts.map +1 -0
- package/dist/components/EffortControl/index.js +84 -0
- package/dist/components/EffortControl/index.js.map +1 -0
- package/dist/components/Layout/index.d.ts +63 -0
- package/dist/components/Layout/index.d.ts.map +1 -0
- package/dist/components/Layout/index.js +45 -0
- package/dist/components/Layout/index.js.map +1 -0
- package/dist/components/MainColumn/index.d.ts +69 -0
- package/dist/components/MainColumn/index.d.ts.map +1 -0
- package/dist/components/MainColumn/index.js +47 -0
- package/dist/components/MainColumn/index.js.map +1 -0
- package/dist/components/MarkdownRenderer/index.d.ts +66 -0
- package/dist/components/MarkdownRenderer/index.d.ts.map +1 -0
- package/dist/components/MarkdownRenderer/index.js +153 -0
- package/dist/components/MarkdownRenderer/index.js.map +1 -0
- package/dist/components/MessageInput/index.d.ts +85 -0
- package/dist/components/MessageInput/index.d.ts.map +1 -0
- package/dist/components/MessageInput/index.js +139 -0
- package/dist/components/MessageInput/index.js.map +1 -0
- package/dist/components/MessageList/index.d.ts +156 -0
- package/dist/components/MessageList/index.d.ts.map +1 -0
- package/dist/components/MessageList/index.js +352 -0
- package/dist/components/MessageList/index.js.map +1 -0
- package/dist/components/ReasoningSelector/index.d.ts +77 -0
- package/dist/components/ReasoningSelector/index.d.ts.map +1 -0
- package/dist/components/ReasoningSelector/index.js +156 -0
- package/dist/components/ReasoningSelector/index.js.map +1 -0
- package/dist/components/ReasoningView/index.d.ts +94 -0
- package/dist/components/ReasoningView/index.d.ts.map +1 -0
- package/dist/components/ReasoningView/index.js +83 -0
- package/dist/components/ReasoningView/index.js.map +1 -0
- package/dist/components/ScrollToPresentButton/index.d.ts +54 -0
- package/dist/components/ScrollToPresentButton/index.d.ts.map +1 -0
- package/dist/components/ScrollToPresentButton/index.js +44 -0
- package/dist/components/ScrollToPresentButton/index.js.map +1 -0
- package/dist/components/SessionsPanel/SessionTitleModal.d.ts +79 -0
- package/dist/components/SessionsPanel/SessionTitleModal.d.ts.map +1 -0
- package/dist/components/SessionsPanel/SessionTitleModal.js +102 -0
- package/dist/components/SessionsPanel/SessionTitleModal.js.map +1 -0
- package/dist/components/SessionsPanel/index.d.ts +100 -0
- package/dist/components/SessionsPanel/index.d.ts.map +1 -0
- package/dist/components/SessionsPanel/index.js +176 -0
- package/dist/components/SessionsPanel/index.js.map +1 -0
- package/dist/components/SystemNotice/index.d.ts +66 -0
- package/dist/components/SystemNotice/index.d.ts.map +1 -0
- package/dist/components/SystemNotice/index.js +81 -0
- package/dist/components/SystemNotice/index.js.map +1 -0
- package/dist/components/ThemeSwitcher/index.d.ts +66 -0
- package/dist/components/ThemeSwitcher/index.d.ts.map +1 -0
- package/dist/components/ThemeSwitcher/index.js +73 -0
- package/dist/components/ThemeSwitcher/index.js.map +1 -0
- package/dist/components/ToolCallView/formatContent.d.ts +36 -0
- package/dist/components/ToolCallView/formatContent.d.ts.map +1 -0
- package/dist/components/ToolCallView/formatContent.js +53 -0
- package/dist/components/ToolCallView/formatContent.js.map +1 -0
- package/dist/components/ToolCallView/index.d.ts +120 -0
- package/dist/components/ToolCallView/index.d.ts.map +1 -0
- package/dist/components/ToolCallView/index.js +104 -0
- package/dist/components/ToolCallView/index.js.map +1 -0
- package/dist/components/TpmBanner/index.d.ts +53 -0
- package/dist/components/TpmBanner/index.d.ts.map +1 -0
- package/dist/components/TpmBanner/index.js +116 -0
- package/dist/components/TpmBanner/index.js.map +1 -0
- package/dist/components/TurnStatusIndicator/index.d.ts +77 -0
- package/dist/components/TurnStatusIndicator/index.d.ts.map +1 -0
- package/dist/components/TurnStatusIndicator/index.js +122 -0
- package/dist/components/TurnStatusIndicator/index.js.map +1 -0
- package/dist/components/internal/PasswordInput.d.ts +62 -0
- package/dist/components/internal/PasswordInput.d.ts.map +1 -0
- package/dist/components/internal/PasswordInput.js +42 -0
- package/dist/components/internal/PasswordInput.js.map +1 -0
- package/dist/context/AIHubContext.d.ts +220 -0
- package/dist/context/AIHubContext.d.ts.map +1 -0
- package/dist/context/AIHubContext.js +19 -0
- package/dist/context/AIHubContext.js.map +1 -0
- package/dist/context/AIHubProvider.d.ts +75 -0
- package/dist/context/AIHubProvider.d.ts.map +1 -0
- package/dist/context/AIHubProvider.js +954 -0
- package/dist/context/AIHubProvider.js.map +1 -0
- package/dist/context/index.d.ts +9 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +9 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/theme.d.ts +43 -0
- package/dist/context/theme.d.ts.map +1 -0
- package/dist/context/theme.js +70 -0
- package/dist/context/theme.js.map +1 -0
- package/dist/context/useAIHub.d.ts +104 -0
- package/dist/context/useAIHub.d.ts.map +1 -0
- package/dist/context/useAIHub.js +63 -0
- package/dist/context/useAIHub.js.map +1 -0
- package/dist/context/useAIHubAuth.d.ts +44 -0
- package/dist/context/useAIHubAuth.d.ts.map +1 -0
- package/dist/context/useAIHubAuth.js +35 -0
- package/dist/context/useAIHubAuth.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/primitives/index.d.ts +99 -0
- package/dist/primitives/index.d.ts.map +1 -0
- package/dist/primitives/index.js +97 -0
- package/dist/primitives/index.js.map +1 -0
- package/dist/state/affordances.d.ts +182 -0
- package/dist/state/affordances.d.ts.map +1 -0
- package/dist/state/affordances.js +189 -0
- package/dist/state/affordances.js.map +1 -0
- package/dist/state/stateReducer.d.ts +72 -0
- package/dist/state/stateReducer.d.ts.map +1 -0
- package/dist/state/stateReducer.js +122 -0
- package/dist/state/stateReducer.js.map +1 -0
- package/dist/stories/EffortControl.stories.d.ts +14 -0
- package/dist/stories/EffortControl.stories.d.ts.map +1 -0
- package/dist/stories/EffortControl.stories.js +43 -0
- package/dist/stories/EffortControl.stories.js.map +1 -0
- package/dist/stories/MarkdownRenderer.stories.d.ts +25 -0
- package/dist/stories/MarkdownRenderer.stories.d.ts.map +1 -0
- package/dist/stories/MarkdownRenderer.stories.js +178 -0
- package/dist/stories/MarkdownRenderer.stories.js.map +1 -0
- package/dist/stories/MessageBubble.stories.d.ts +21 -0
- package/dist/stories/MessageBubble.stories.d.ts.map +1 -0
- package/dist/stories/MessageBubble.stories.js +59 -0
- package/dist/stories/MessageBubble.stories.js.map +1 -0
- package/dist/stories/ReasoningView.stories.d.ts +18 -0
- package/dist/stories/ReasoningView.stories.d.ts.map +1 -0
- package/dist/stories/ReasoningView.stories.js +62 -0
- package/dist/stories/ReasoningView.stories.js.map +1 -0
- package/dist/stories/ScrollToPresentButton.stories.d.ts +18 -0
- package/dist/stories/ScrollToPresentButton.stories.d.ts.map +1 -0
- package/dist/stories/ScrollToPresentButton.stories.js +34 -0
- package/dist/stories/ScrollToPresentButton.stories.js.map +1 -0
- package/dist/stories/SystemNotice.stories.d.ts +17 -0
- package/dist/stories/SystemNotice.stories.d.ts.map +1 -0
- package/dist/stories/SystemNotice.stories.js +42 -0
- package/dist/stories/SystemNotice.stories.js.map +1 -0
- package/dist/stories/ThemeSwitcher.stories.d.ts +15 -0
- package/dist/stories/ThemeSwitcher.stories.d.ts.map +1 -0
- package/dist/stories/ThemeSwitcher.stories.js +35 -0
- package/dist/stories/ThemeSwitcher.stories.js.map +1 -0
- package/dist/stories/ToolCallView.stories.d.ts +20 -0
- package/dist/stories/ToolCallView.stories.d.ts.map +1 -0
- package/dist/stories/ToolCallView.stories.js +65 -0
- package/dist/stories/ToolCallView.stories.js.map +1 -0
- package/dist/stories/TpmBanner.stories.d.ts +24 -0
- package/dist/stories/TpmBanner.stories.d.ts.map +1 -0
- package/dist/stories/TpmBanner.stories.js +63 -0
- package/dist/stories/TpmBanner.stories.js.map +1 -0
- package/dist/stories/TurnStatusIndicator.stories.d.ts +25 -0
- package/dist/stories/TurnStatusIndicator.stories.d.ts.map +1 -0
- package/dist/stories/TurnStatusIndicator.stories.js +56 -0
- package/dist/stories/TurnStatusIndicator.stories.js.map +1 -0
- package/dist/stories/_mockProvider.d.ts +44 -0
- package/dist/stories/_mockProvider.d.ts.map +1 -0
- package/dist/stories/_mockProvider.js +84 -0
- package/dist/stories/_mockProvider.js.map +1 -0
- package/dist/types.d.ts +787 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/webcomponent/defineWebComponent.d.ts +106 -0
- package/dist/webcomponent/defineWebComponent.d.ts.map +1 -0
- package/dist/webcomponent/defineWebComponent.js +126 -0
- package/dist/webcomponent/defineWebComponent.js.map +1 -0
- package/dist/webcomponent/index.d.ts +7 -0
- package/dist/webcomponent/index.d.ts.map +1 -0
- package/dist/webcomponent/index.js +7 -0
- package/dist/webcomponent/index.js.map +1 -0
- package/package.json +63 -0
- package/src/__lint-canary__/step1-lint-canary.ts.disabled +28 -0
- package/src/auth/passwordPolicy.ts +75 -0
- package/src/auth/texts.ts +1294 -0
- package/src/client/AIHubClient.ts +1718 -0
- package/src/client/index.ts +30 -0
- package/src/client/sseParser.ts +168 -0
- package/src/client/tokenStorage.ts +131 -0
- package/src/components/AgentsPicker/index.tsx +147 -0
- package/src/components/AuthGate/index.tsx +132 -0
- package/src/components/CacheBadge/index.tsx +245 -0
- package/src/components/ChangePassword/index.tsx +375 -0
- package/src/components/ChatShell/ShellHeader.tsx +80 -0
- package/src/components/ChatShell/UserMenu.tsx +196 -0
- package/src/components/ChatShell/index.tsx +307 -0
- package/src/components/DataClubAIHub/index.tsx +300 -0
- package/src/components/DataClubAIHubAdmin/AgentEditorModal.tsx +505 -0
- package/src/components/DataClubAIHubAdmin/AgentsPage.tsx +342 -0
- package/src/components/DataClubAIHubAdmin/CreateUserModal.tsx +214 -0
- package/src/components/DataClubAIHubAdmin/PasswordRevealModal.tsx +204 -0
- package/src/components/DataClubAIHubAdmin/ResetPasswordModal.tsx +122 -0
- package/src/components/DataClubAIHubAdmin/RotatingMessageEditorModal.tsx +408 -0
- package/src/components/DataClubAIHubAdmin/RotatingMessagesPage.tsx +373 -0
- package/src/components/DataClubAIHubAdmin/SessionsOverviewPage.tsx +639 -0
- package/src/components/DataClubAIHubAdmin/SettingsPage.tsx +979 -0
- package/src/components/DataClubAIHubAdmin/UsersPage.tsx +495 -0
- package/src/components/DataClubAIHubAdmin/index.tsx +277 -0
- package/src/components/DataClubAIHubLogin/index.tsx +282 -0
- package/src/components/EffortControl/index.tsx +138 -0
- package/src/components/Layout/index.tsx +92 -0
- package/src/components/MainColumn/index.tsx +131 -0
- package/src/components/MarkdownRenderer/index.tsx +256 -0
- package/src/components/MessageInput/index.tsx +283 -0
- package/src/components/MessageList/index.tsx +753 -0
- package/src/components/ReasoningSelector/index.tsx +268 -0
- package/src/components/ReasoningView/index.tsx +183 -0
- package/src/components/ScrollToPresentButton/index.tsx +85 -0
- package/src/components/SessionsPanel/SessionTitleModal.tsx +256 -0
- package/src/components/SessionsPanel/index.tsx +406 -0
- package/src/components/SystemNotice/index.tsx +151 -0
- package/src/components/ThemeSwitcher/index.tsx +136 -0
- package/src/components/ToolCallView/formatContent.ts +50 -0
- package/src/components/ToolCallView/index.tsx +274 -0
- package/src/components/TpmBanner/index.tsx +172 -0
- package/src/components/TurnStatusIndicator/index.tsx +203 -0
- package/src/components/internal/PasswordInput.tsx +145 -0
- package/src/context/AIHubContext.ts +249 -0
- package/src/context/AIHubProvider.tsx +1084 -0
- package/src/context/index.ts +9 -0
- package/src/context/theme.ts +76 -0
- package/src/context/useAIHub.ts +151 -0
- package/src/context/useAIHubAuth.ts +65 -0
- package/src/index.ts +275 -0
- package/src/primitives/index.ts +101 -0
- package/src/state/affordances.ts +321 -0
- package/src/state/stateReducer.ts +131 -0
- package/src/stories/EffortControl.stories.tsx +63 -0
- package/src/stories/MarkdownRenderer.stories.tsx +201 -0
- package/src/stories/MessageBubble.stories.tsx +101 -0
- package/src/stories/ReasoningView.stories.tsx +81 -0
- package/src/stories/ScrollToPresentButton.stories.tsx +52 -0
- package/src/stories/SystemNotice.stories.tsx +59 -0
- package/src/stories/ThemeSwitcher.stories.tsx +55 -0
- package/src/stories/ToolCallView.stories.tsx +95 -0
- package/src/stories/TpmBanner.stories.tsx +93 -0
- package/src/stories/TurnStatusIndicator.stories.tsx +86 -0
- package/src/stories/_mockProvider.tsx +116 -0
- package/src/styles/CLASSES.md +356 -0
- package/src/styles/default.css +2937 -0
- package/src/types.ts +867 -0
- package/src/webcomponent/defineWebComponent.tsx +193 -0
- package/src/webcomponent/index.ts +11 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Affordance helpers — the pure-function half of Step 10.
|
|
3
|
+
*
|
|
4
|
+
* Two concerns live here:
|
|
5
|
+
*
|
|
6
|
+
* 1. **§8.4 precedence resolution.** `resolveAffordanceFlags()` composes
|
|
7
|
+
* the three layers — server toggles (highest), React prop overrides,
|
|
8
|
+
* built-in defaults — into one flat record of booleans. The monolith
|
|
9
|
+
* `<DataClubAIHub />` calls this once and passes the resolved flags
|
|
10
|
+
* down to layout / affordance primitives, so each primitive doesn't
|
|
11
|
+
* have to re-implement the merge.
|
|
12
|
+
*
|
|
13
|
+
* 2. **§10.4 predicate table.** `chatAffordances()` is a pure function of
|
|
14
|
+
* `(status, phase)` returning the predicate booleans the table calls
|
|
15
|
+
* for ("Stop button visible", "Input disabled", "TPM banner",
|
|
16
|
+
* "Streaming dots", etc.). Primitives consume the returned record
|
|
17
|
+
* directly so the same predicates are tested in one place and never
|
|
18
|
+
* drift between e.g. the stop button and the streaming-cursor
|
|
19
|
+
* indicator.
|
|
20
|
+
*
|
|
21
|
+
* Neither function reads context — both are testable in isolation against
|
|
22
|
+
* the cross-package SSE fixtures. The provider's React surface is
|
|
23
|
+
* `useAIHub().featureToggles` (raw) + the helpers here.
|
|
24
|
+
*
|
|
25
|
+
* @see SPECIFICATIONS.md §8.4 — feature toggles
|
|
26
|
+
* @see SPECIFICATIONS.md §10.4 — affordances bound to (status, phase)
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import type {
|
|
30
|
+
AIHubFeatureToggles,
|
|
31
|
+
AIHubState,
|
|
32
|
+
RunningPhase,
|
|
33
|
+
TurnStatus,
|
|
34
|
+
} from "../types.js";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The flat, resolved (after-precedence) affordance flag record.
|
|
38
|
+
*
|
|
39
|
+
* Field names are camelCase here (React-side); the wire shape is
|
|
40
|
+
* snake_case (`AIHubFeatureToggles`). The mapping between the two is
|
|
41
|
+
* locked down by `resolveAffordanceFlags` below.
|
|
42
|
+
*/
|
|
43
|
+
export interface AIHubAffordanceFlags {
|
|
44
|
+
/** Whether the sessions panel renders at all. Default true. */
|
|
45
|
+
sessionsPanelEnabled: boolean;
|
|
46
|
+
/** Whether the sessions-panel handle is draggable. Default true. */
|
|
47
|
+
sessionsPanelResizable: boolean;
|
|
48
|
+
/** Whether the sessions panel can be collapsed. Default true. */
|
|
49
|
+
sessionsPanelCollapsible: boolean;
|
|
50
|
+
/** Whether the "+ New chat" button renders on the panel. Default true. */
|
|
51
|
+
createSessionButtonEnabled: boolean;
|
|
52
|
+
/** Whether the input area renders (read-only when false). Default true. */
|
|
53
|
+
userInputEnabled: boolean;
|
|
54
|
+
/** Whether the stop button is exposed while a turn is running. Default true. */
|
|
55
|
+
stopButtonEnabled: boolean;
|
|
56
|
+
/** Whether tool-call cards render in assistant bubbles. Default true. */
|
|
57
|
+
toolCallsVisible: boolean;
|
|
58
|
+
/** Whether reasoning blocks render in assistant bubbles. Default true. */
|
|
59
|
+
reasoningVisible: boolean;
|
|
60
|
+
/** Whether system messages render in the list. Default true. */
|
|
61
|
+
systemMessagesVisible: boolean;
|
|
62
|
+
/** Whether the TPM-recovery banner renders during waiting_provider. Default true. */
|
|
63
|
+
tpmNoticeVisible: boolean;
|
|
64
|
+
/** Whether the effort dropdown renders in the input toolbar. Default true. */
|
|
65
|
+
effortControlVisible: boolean;
|
|
66
|
+
/** Whether the scroll-to-present button renders when the user scrolled up. Default true. */
|
|
67
|
+
scrollToPresentVisible: boolean;
|
|
68
|
+
/** Whether the turn-status indicator renders in the chat header. Default true. */
|
|
69
|
+
turnStatusVisible: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Built-in defaults — applied when neither server nor React prop sets a value. */
|
|
73
|
+
export const DEFAULT_AFFORDANCE_FLAGS: AIHubAffordanceFlags = Object.freeze({
|
|
74
|
+
sessionsPanelEnabled: true,
|
|
75
|
+
sessionsPanelResizable: true,
|
|
76
|
+
sessionsPanelCollapsible: true,
|
|
77
|
+
createSessionButtonEnabled: true,
|
|
78
|
+
userInputEnabled: true,
|
|
79
|
+
stopButtonEnabled: true,
|
|
80
|
+
toolCallsVisible: true,
|
|
81
|
+
reasoningVisible: true,
|
|
82
|
+
systemMessagesVisible: true,
|
|
83
|
+
tpmNoticeVisible: true,
|
|
84
|
+
effortControlVisible: true,
|
|
85
|
+
scrollToPresentVisible: true,
|
|
86
|
+
turnStatusVisible: true,
|
|
87
|
+
}) as AIHubAffordanceFlags;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Per-flag mapping from the wire key (`AIHubFeatureToggles`) to the
|
|
91
|
+
* camelCase field on `AIHubAffordanceFlags`. Exported so tests can
|
|
92
|
+
* iterate the §8.4 mapping without duplicating it in the test file.
|
|
93
|
+
*/
|
|
94
|
+
export const FEATURE_TOGGLE_KEY_MAP: ReadonlyArray<
|
|
95
|
+
readonly [keyof AIHubFeatureToggles, keyof AIHubAffordanceFlags]
|
|
96
|
+
> = Object.freeze([
|
|
97
|
+
["sessions_panel_enabled", "sessionsPanelEnabled"],
|
|
98
|
+
["sessions_panel_resizable", "sessionsPanelResizable"],
|
|
99
|
+
["sessions_panel_collapsible", "sessionsPanelCollapsible"],
|
|
100
|
+
["create_session_button_enabled", "createSessionButtonEnabled"],
|
|
101
|
+
["user_input_enabled", "userInputEnabled"],
|
|
102
|
+
["stop_button_enabled", "stopButtonEnabled"],
|
|
103
|
+
["tool_calls_visible", "toolCallsVisible"],
|
|
104
|
+
["reasoning_visible", "reasoningVisible"],
|
|
105
|
+
["system_messages_visible", "systemMessagesVisible"],
|
|
106
|
+
["tpm_notice_visible", "tpmNoticeVisible"],
|
|
107
|
+
["effort_control_visible", "effortControlVisible"],
|
|
108
|
+
["scroll_to_present_visible", "scrollToPresentVisible"],
|
|
109
|
+
["turn_status_visible", "turnStatusVisible"],
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Resolves the precedence stack for one toggle.
|
|
114
|
+
*
|
|
115
|
+
* **Server toggle > React prop > built-in default.** Documented in §8.4
|
|
116
|
+
* and enforced by this function alone — every caller that wants the
|
|
117
|
+
* effective state must go through here.
|
|
118
|
+
*
|
|
119
|
+
* @param serverValue - the server-set value (`undefined` when the
|
|
120
|
+
* super_admin hasn't set this toggle explicitly).
|
|
121
|
+
* @param propValue - the React-prop override (`undefined` when the
|
|
122
|
+
* wrapper didn't supply one).
|
|
123
|
+
* @param defaultValue - the built-in default for this flag.
|
|
124
|
+
* @returns the effective boolean.
|
|
125
|
+
*/
|
|
126
|
+
function resolveOne(
|
|
127
|
+
serverValue: boolean | undefined,
|
|
128
|
+
propValue: boolean | undefined,
|
|
129
|
+
defaultValue: boolean,
|
|
130
|
+
): boolean {
|
|
131
|
+
if (typeof serverValue === "boolean") return serverValue;
|
|
132
|
+
if (typeof propValue === "boolean") return propValue;
|
|
133
|
+
return defaultValue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Composes the three layers into a flat resolved record of all 13
|
|
138
|
+
* affordance booleans.
|
|
139
|
+
*
|
|
140
|
+
* Per §8.4: server toggle wins; falling through, the React prop wins;
|
|
141
|
+
* falling through that, the built-in default wins. Adding a new toggle
|
|
142
|
+
* requires updating `FEATURE_TOGGLE_KEY_MAP` + the schema in
|
|
143
|
+
* `routes/admin/settings.ts` + the field on `AIHubFeatureToggles` — the
|
|
144
|
+
* compile-time exhaustiveness check on the for-loop will surface any
|
|
145
|
+
* missed addition.
|
|
146
|
+
*
|
|
147
|
+
* @param serverToggles - the wire blob from `GET /feature-toggles`
|
|
148
|
+
* (`null` while the request is in flight; treat as an empty blob).
|
|
149
|
+
* @param propOverrides - React-prop overrides keyed by the camelCase
|
|
150
|
+
* field name (`AIHubAffordanceFlags`). All fields optional.
|
|
151
|
+
* @returns the fully-resolved record. Every field is a boolean.
|
|
152
|
+
*/
|
|
153
|
+
export function resolveAffordanceFlags(
|
|
154
|
+
serverToggles: AIHubFeatureToggles | null,
|
|
155
|
+
propOverrides: Partial<AIHubAffordanceFlags>,
|
|
156
|
+
): AIHubAffordanceFlags {
|
|
157
|
+
const out = { ...DEFAULT_AFFORDANCE_FLAGS };
|
|
158
|
+
const server = serverToggles ?? {};
|
|
159
|
+
for (const [wireKey, fieldKey] of FEATURE_TOGGLE_KEY_MAP) {
|
|
160
|
+
out[fieldKey] = resolveOne(
|
|
161
|
+
server[wireKey],
|
|
162
|
+
propOverrides[fieldKey],
|
|
163
|
+
DEFAULT_AFFORDANCE_FLAGS[fieldKey],
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The §10.4 predicate table — pure function of `(status, phase)` (lifted
|
|
171
|
+
* from `AIHubState`). Each field maps to one row of the table; primitives
|
|
172
|
+
* consume the booleans directly so the same predicate is sourced from one
|
|
173
|
+
* place.
|
|
174
|
+
*
|
|
175
|
+
* Some predicates depend on transient affordance flags (e.g. the stop
|
|
176
|
+
* button is suppressed when `stopButtonEnabled === false` from server
|
|
177
|
+
* config). For those the caller AND-merges the predicate result with the
|
|
178
|
+
* flag — keeping this function pure (no closure over flags) avoids
|
|
179
|
+
* coupling the §8.4 layer to the §10.4 layer.
|
|
180
|
+
*/
|
|
181
|
+
export interface AIHubChatAffordances {
|
|
182
|
+
/** §10.4 row "Stop button visible" — predicate only; ignore stopButtonEnabled here. */
|
|
183
|
+
stopButtonVisible: boolean;
|
|
184
|
+
/** §10.4 row "Input disabled". */
|
|
185
|
+
inputDisabled: boolean;
|
|
186
|
+
/**
|
|
187
|
+
* §10.4 row "Scroll-to-present button" — true when the *predicate* fires,
|
|
188
|
+
* which is `status === "running"`. The component additionally requires
|
|
189
|
+
* `userScrolledAwayFromBottom`, which is local state, not table state.
|
|
190
|
+
*/
|
|
191
|
+
scrollToPresentVisible: boolean;
|
|
192
|
+
/** §10.4 row "Streaming dots" — `running && phase === model_text`. */
|
|
193
|
+
streamingDots: boolean;
|
|
194
|
+
/** §10.4 row "Reasoning spinner / panel" — `running && phase === model_reasoning`. */
|
|
195
|
+
reasoningSpinner: boolean;
|
|
196
|
+
/**
|
|
197
|
+
* §10.4 row "Tool-call card 'preparing'" — `phase === model_tool_input`.
|
|
198
|
+
* Note: spec says the predicate is just `phase === model_tool_input`,
|
|
199
|
+
* not `status === running` — `phase` is only meaningful when `status ===
|
|
200
|
+
* running` anyway, so the AND is implicit.
|
|
201
|
+
*/
|
|
202
|
+
toolCallPreparing: boolean;
|
|
203
|
+
/** §10.4 row "Tool-call card 'running' with spinner" — `phase === tool_executing`. */
|
|
204
|
+
toolCallRunning: boolean;
|
|
205
|
+
/** §10.4 row "TPM banner with countdown" — `phase === waiting_provider`. */
|
|
206
|
+
tpmBannerVisible: boolean;
|
|
207
|
+
/** §10.4 row "'Stopped' stamp" — `status === cancelled`. */
|
|
208
|
+
cancelledStamp: boolean;
|
|
209
|
+
/** §10.4 row "Retry button on assistant turn" — `status === failed`. */
|
|
210
|
+
retryButtonVisible: boolean;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Computes the §10.4 predicate booleans for a given chat state.
|
|
215
|
+
*
|
|
216
|
+
* Pure function — `(state) => predicates`. Used by every affordance-aware
|
|
217
|
+
* component (`MessageInput`, `MessageList`, `ScrollToPresentButton`,
|
|
218
|
+
* `TurnStatusIndicator`, …) so the table lives in exactly one place.
|
|
219
|
+
*
|
|
220
|
+
* @param state - the current `AIHubState` from the reducer.
|
|
221
|
+
* @returns the predicate record.
|
|
222
|
+
*/
|
|
223
|
+
export function chatAffordances(state: AIHubState): AIHubChatAffordances {
|
|
224
|
+
const isRunning = state.status === "running";
|
|
225
|
+
return {
|
|
226
|
+
stopButtonVisible: isRunning,
|
|
227
|
+
inputDisabled: isRunning,
|
|
228
|
+
scrollToPresentVisible: isRunning,
|
|
229
|
+
streamingDots: isRunning && state.phase === "model_text",
|
|
230
|
+
reasoningSpinner: isRunning && state.phase === "model_reasoning",
|
|
231
|
+
toolCallPreparing: isRunning && state.phase === "model_tool_input",
|
|
232
|
+
toolCallRunning: isRunning && state.phase === "tool_executing",
|
|
233
|
+
tpmBannerVisible: isRunning && state.phase === "waiting_provider",
|
|
234
|
+
cancelledStamp: state.status === "cancelled",
|
|
235
|
+
retryButtonVisible: state.status === "failed",
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The §10.4 turn-status indicator table — pure function of `(status,
|
|
241
|
+
* phase)` returning the user-visible label + the dot variant the
|
|
242
|
+
* `TurnStatusIndicator` primitive renders.
|
|
243
|
+
*
|
|
244
|
+
* Three dot variants exist:
|
|
245
|
+
*
|
|
246
|
+
* - `"idle"` — gray, static
|
|
247
|
+
* - `"running"` — accent, pulsing
|
|
248
|
+
* - `"warning"` — warning yellow, pulsing (rate-limit wait)
|
|
249
|
+
* - `"error"` — error red, static
|
|
250
|
+
*
|
|
251
|
+
* Mapping is exactly the table from §10.4. The transient flashes ("Done"
|
|
252
|
+
* before settling to "Ready", "Cancelled" before settling to "Ready")
|
|
253
|
+
* are not encoded here — those are handled in the component via
|
|
254
|
+
* `useEffect` + a short timer.
|
|
255
|
+
*/
|
|
256
|
+
export interface AIHubTurnStatusVisual {
|
|
257
|
+
/** Short human-readable label rendered next to the dot. */
|
|
258
|
+
label: string;
|
|
259
|
+
/** Dot variant — drives the BEM modifier on the dot element. */
|
|
260
|
+
dot: "idle" | "running" | "warning" | "error";
|
|
261
|
+
/** Whether the dot pulses (suppressed under `prefers-reduced-motion`). */
|
|
262
|
+
pulse: boolean;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Computes the §10.4 turn-status indicator's `(label, dot, pulse)` from
|
|
267
|
+
* a `(status, phase)` pair.
|
|
268
|
+
*
|
|
269
|
+
* Pure. Drives the BEM modifier on the dot + the label text inside the
|
|
270
|
+
* indicator. The "Done" flash and the "Cancelled → Ready" auto-clear
|
|
271
|
+
* timings are local to the component.
|
|
272
|
+
*
|
|
273
|
+
* @param status - the turn status.
|
|
274
|
+
* @param phase - the running phase (only meaningful when status === running).
|
|
275
|
+
* @returns the visual triple.
|
|
276
|
+
*/
|
|
277
|
+
export function turnStatusVisual(
|
|
278
|
+
status: TurnStatus,
|
|
279
|
+
phase: RunningPhase | null,
|
|
280
|
+
): AIHubTurnStatusVisual {
|
|
281
|
+
if (status === "running") {
|
|
282
|
+
switch (phase) {
|
|
283
|
+
case "model_text":
|
|
284
|
+
return { label: "Generating…", dot: "running", pulse: true };
|
|
285
|
+
case "model_reasoning":
|
|
286
|
+
return { label: "Reasoning…", dot: "running", pulse: true };
|
|
287
|
+
case "model_tool_input":
|
|
288
|
+
return {
|
|
289
|
+
label: "Preparing tool call…",
|
|
290
|
+
dot: "running",
|
|
291
|
+
pulse: true,
|
|
292
|
+
};
|
|
293
|
+
case "tool_executing":
|
|
294
|
+
return { label: "Running tool…", dot: "running", pulse: true };
|
|
295
|
+
case "waiting_provider":
|
|
296
|
+
return {
|
|
297
|
+
label: "Waiting (rate limit)…",
|
|
298
|
+
dot: "warning",
|
|
299
|
+
pulse: true,
|
|
300
|
+
};
|
|
301
|
+
case "awaiting_user":
|
|
302
|
+
// Not exercised by the predicate table (the spec doesn't surface
|
|
303
|
+
// a label for awaiting_user); fall back to "Ready" so the dot
|
|
304
|
+
// doesn't strobe.
|
|
305
|
+
return { label: "Ready", dot: "idle", pulse: false };
|
|
306
|
+
case null:
|
|
307
|
+
return { label: "Generating…", dot: "running", pulse: true };
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (status === "failed") {
|
|
311
|
+
return { label: "Failed", dot: "error", pulse: false };
|
|
312
|
+
}
|
|
313
|
+
if (status === "cancelled") {
|
|
314
|
+
return { label: "Cancelled", dot: "idle", pulse: false };
|
|
315
|
+
}
|
|
316
|
+
if (status === "done") {
|
|
317
|
+
return { label: "Done", dot: "idle", pulse: false };
|
|
318
|
+
}
|
|
319
|
+
// idle
|
|
320
|
+
return { label: "Ready", dot: "idle", pulse: false };
|
|
321
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side state reducer for `AIHubState`.
|
|
3
|
+
*
|
|
4
|
+
* The ai-hub server is the authoritative source of state (§6.6, §10). Every
|
|
5
|
+
* SSE event carries a `state: { status, phase }` snapshot at emission time
|
|
6
|
+
* (§9); the client's job is to trust that snapshot and enrich it with the
|
|
7
|
+
* cross-event fields the UI needs to render — `last_seq` (for the resumption
|
|
8
|
+
* cursor), `turn_id` (from `stream_start`), `session_id`, and `tpm_retry_at`
|
|
9
|
+
* (the countdown deadline read from the latest `system_notice {tpm_wait}`).
|
|
10
|
+
*
|
|
11
|
+
* Because the server emits the `(status, phase)` directly on every event, the
|
|
12
|
+
* reducer does **not** re-derive the transitions from §10.2 — it just lifts
|
|
13
|
+
* the snapshot. The §10.2 arrows are tested via the cross-package SSE
|
|
14
|
+
* fixtures (`packages/ai-hub-server/__tests__/fixtures/sse/*.json`): each
|
|
15
|
+
* fixture replays the bytes the server emits and the reducer's per-event
|
|
16
|
+
* output is asserted to match the expected `(status, phase)` for that arrow.
|
|
17
|
+
*
|
|
18
|
+
* @see SPECIFICATIONS.md §6.6 — server-authoritative state
|
|
19
|
+
* @see SPECIFICATIONS.md §7.5 — state on the client
|
|
20
|
+
* @see SPECIFICATIONS.md §9 — wire protocol
|
|
21
|
+
* @see SPECIFICATIONS.md §10 — state machine
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { AIHubEvent, AIHubState } from "../types.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The reducer's starting state, before any event has been seen.
|
|
28
|
+
*
|
|
29
|
+
* `last_seq: -1` is the convention used as the `from_seq` cursor on the
|
|
30
|
+
* resumption endpoint (`/chat/{turn_id}/events?from_seq=N`) — the server
|
|
31
|
+
* matches with `seq > N`, so `-1` replays every event from `seq: 0`
|
|
32
|
+
* forward (`server/routes/chat.ts`).
|
|
33
|
+
*
|
|
34
|
+
* @returns the initial state.
|
|
35
|
+
*/
|
|
36
|
+
export function initialAIHubState(): AIHubState {
|
|
37
|
+
return {
|
|
38
|
+
status: "idle",
|
|
39
|
+
phase: null,
|
|
40
|
+
last_seq: -1,
|
|
41
|
+
turn_id: null,
|
|
42
|
+
session_id: null,
|
|
43
|
+
tpm_retry_at: null,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Reduces one `AIHubEvent` into the next `AIHubState`.
|
|
49
|
+
*
|
|
50
|
+
* Pure. Re-runnable. The provider replays the same event sequence on
|
|
51
|
+
* reconnect-by-replay and lands on the same state every time — that's the
|
|
52
|
+
* load-bearing property the resumption flow depends on.
|
|
53
|
+
*
|
|
54
|
+
* Behavioural notes (each maps to a §10.2 arrow or §10.3 rule, and is locked
|
|
55
|
+
* down by the SSE fixtures consumed in `__tests__/state/stateReducer.test.ts`):
|
|
56
|
+
*
|
|
57
|
+
* - `stream_start` → captures `turn_id` + `session_id`; clears any leftover
|
|
58
|
+
* `tpm_retry_at` from a previous turn (defensive; a fresh turn should
|
|
59
|
+
* never inherit one).
|
|
60
|
+
* - `system_notice {code: "tpm_wait"}` → stores `retry_at` so the TPM banner
|
|
61
|
+
* (Step 10) can render its countdown. The `(status, phase)` snapshot on
|
|
62
|
+
* the event is already `running / waiting_provider` per §10.2.
|
|
63
|
+
* - `system_notice {code: "resumed"}` → clears `tpm_retry_at`. Snapshot is
|
|
64
|
+
* back to `running / model_text` (or whatever the recovery resumes into).
|
|
65
|
+
* - `error` → server already emitted `(status: failed, phase: null)`; the
|
|
66
|
+
* reducer just lifts it and clears `tpm_retry_at` so a Retry button
|
|
67
|
+
* doesn't render a stale countdown.
|
|
68
|
+
* - `stream_end` → terminal `(status, phase)` is already on the snapshot;
|
|
69
|
+
* `tpm_retry_at` is cleared for the same reason as `error`.
|
|
70
|
+
* - All other events → `(status, phase)` + `last_seq` are lifted from the
|
|
71
|
+
* snapshot; nothing else changes. The §10.3 tool-error-vs-agent-error
|
|
72
|
+
* rule is honored entirely by the server's snapshot (a `tool_call_result`
|
|
73
|
+
* with an `error` field carries `phase: model_text` and `status: running`;
|
|
74
|
+
* only an `error` event carries `status: failed`).
|
|
75
|
+
*
|
|
76
|
+
* @param state - the previous `AIHubState`.
|
|
77
|
+
* @param event - the next event from the SSE stream.
|
|
78
|
+
* @returns the next `AIHubState`.
|
|
79
|
+
*
|
|
80
|
+
* @see SPECIFICATIONS.md §10.2 — transitions
|
|
81
|
+
* @see SPECIFICATIONS.md §10.3 — tool error vs. agent error
|
|
82
|
+
*/
|
|
83
|
+
export function reduceAIHubState(
|
|
84
|
+
state: AIHubState,
|
|
85
|
+
event: AIHubEvent,
|
|
86
|
+
): AIHubState {
|
|
87
|
+
// Lift the snapshot + advance the resumption cursor. Every branch below
|
|
88
|
+
// refines this base value.
|
|
89
|
+
const next: AIHubState = {
|
|
90
|
+
...state,
|
|
91
|
+
status: event.state.status,
|
|
92
|
+
phase: event.state.phase,
|
|
93
|
+
last_seq: event.seq,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
switch (event.event) {
|
|
97
|
+
case "stream_start":
|
|
98
|
+
next.turn_id = event.data.turn_id;
|
|
99
|
+
next.session_id = event.data.session_id;
|
|
100
|
+
next.tpm_retry_at = null;
|
|
101
|
+
return next;
|
|
102
|
+
|
|
103
|
+
case "system_notice":
|
|
104
|
+
if (event.data.code === "tpm_wait") {
|
|
105
|
+
// `retry_at` is optional on the wire; absent → no countdown, the
|
|
106
|
+
// banner renders without a timer. We still keep the `waiting_provider`
|
|
107
|
+
// phase from the snapshot so the affordance flips.
|
|
108
|
+
next.tpm_retry_at = event.data.retry_at ?? null;
|
|
109
|
+
} else if (event.data.code === "resumed") {
|
|
110
|
+
next.tpm_retry_at = null;
|
|
111
|
+
}
|
|
112
|
+
return next;
|
|
113
|
+
|
|
114
|
+
case "error":
|
|
115
|
+
case "stream_end":
|
|
116
|
+
next.tpm_retry_at = null;
|
|
117
|
+
return next;
|
|
118
|
+
|
|
119
|
+
case "heartbeat":
|
|
120
|
+
case "assistant_delta":
|
|
121
|
+
case "reasoning_delta":
|
|
122
|
+
case "assistant_message_complete":
|
|
123
|
+
case "tool_call_started":
|
|
124
|
+
case "tool_call_input_delta":
|
|
125
|
+
case "tool_call_dispatched":
|
|
126
|
+
case "tool_call_progress":
|
|
127
|
+
case "tool_call_result":
|
|
128
|
+
case "usage":
|
|
129
|
+
return next;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stories for `<EffortControl />` — reasoning-effort dropdown.
|
|
3
|
+
*
|
|
4
|
+
* @see SPECIFICATIONS.md §7.4 — reasoning effort
|
|
5
|
+
*/
|
|
6
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
7
|
+
|
|
8
|
+
import { EffortControl } from "../components/EffortControl/index.js";
|
|
9
|
+
import type { AIHubEffort } from "../client/AIHubClient.js";
|
|
10
|
+
|
|
11
|
+
import { MockProvider } from "./_mockProvider.js";
|
|
12
|
+
|
|
13
|
+
function withEffort(effort: AIHubEffort) {
|
|
14
|
+
return (Story: () => React.ReactNode) => (
|
|
15
|
+
<MockProvider opts={{ effort }}>
|
|
16
|
+
<Story />
|
|
17
|
+
</MockProvider>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const meta: Meta<typeof EffortControl> = {
|
|
22
|
+
title: "Affordances/EffortControl",
|
|
23
|
+
component: EffortControl,
|
|
24
|
+
parameters: {
|
|
25
|
+
docs: {
|
|
26
|
+
description: {
|
|
27
|
+
component:
|
|
28
|
+
"Reasoning-effort dropdown mirroring Letta's ADE enum (None | Minimal | Low | Medium | High | Extra High). Per-message; persists in provider state until changed.",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export default meta;
|
|
34
|
+
|
|
35
|
+
type Story = StoryObj<typeof EffortControl>;
|
|
36
|
+
|
|
37
|
+
export const DefaultMedium: Story = {
|
|
38
|
+
decorators: [withEffort("medium")],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const SetToNone: Story = {
|
|
42
|
+
decorators: [withEffort("none")],
|
|
43
|
+
parameters: {
|
|
44
|
+
docs: {
|
|
45
|
+
description: {
|
|
46
|
+
story:
|
|
47
|
+
"`none` is the off-state for reasoning; there is no separate reasoning toggle.",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const Disabled: Story = {
|
|
54
|
+
decorators: [withEffort("medium")],
|
|
55
|
+
args: { disabled: true },
|
|
56
|
+
parameters: {
|
|
57
|
+
docs: {
|
|
58
|
+
description: {
|
|
59
|
+
story: "Disabled while a turn is streaming.",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|