@stigmer/react 3.12.4 → 3.12.6
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/agent/AgentDetailView.js +2 -2
- package/agent/AgentDetailView.js.map +1 -1
- package/agent/index.d.ts +0 -1
- package/agent/index.d.ts.map +1 -1
- package/agent/index.js +0 -1
- package/agent/index.js.map +1 -1
- package/agent/useUpdateAgent.d.ts +3 -3
- package/agent/useUpdateAgent.js +3 -3
- package/agent-instance/AgentInstanceDetailPanel.d.ts.map +1 -1
- package/agent-instance/AgentInstanceDetailPanel.js +5 -7
- package/agent-instance/AgentInstanceDetailPanel.js.map +1 -1
- package/agent-instance/useUpdateAgentInstance.d.ts +1 -1
- package/agent-instance/useUpdateAgentInstance.js +1 -1
- package/attachment/AttachmentChipList.js +16 -6
- package/attachment/AttachmentChipList.js.map +1 -1
- package/attachment/index.d.ts +1 -1
- package/attachment/index.d.ts.map +1 -1
- package/attachment/index.js.map +1 -1
- package/attachment/useAttachments.d.ts +51 -8
- package/attachment/useAttachments.d.ts.map +1 -1
- package/attachment/useAttachments.js +74 -3
- package/attachment/useAttachments.js.map +1 -1
- package/channel/AgentChannelsPanel.d.ts.map +1 -1
- package/channel/AgentChannelsPanel.js +10 -5
- package/channel/AgentChannelsPanel.js.map +1 -1
- package/channel/ChannelRunConfigDialog.d.ts +51 -0
- package/channel/ChannelRunConfigDialog.d.ts.map +1 -0
- package/channel/ChannelRunConfigDialog.js +121 -0
- package/channel/ChannelRunConfigDialog.js.map +1 -0
- package/channel/index.d.ts +2 -0
- package/channel/index.d.ts.map +1 -1
- package/channel/index.js +1 -0
- package/channel/index.js.map +1 -1
- package/channel/useSaveAgentChannel.d.ts.map +1 -1
- package/channel/useSaveAgentChannel.js +6 -3
- package/channel/useSaveAgentChannel.js.map +1 -1
- package/channel-app/ChannelAppDetailPanel.js +9 -9
- package/channel-app/ChannelAppDetailPanel.js.map +1 -1
- package/composer/ComposerToolbar.d.ts +6 -1
- package/composer/ComposerToolbar.d.ts.map +1 -1
- package/composer/ComposerToolbar.js +2 -2
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/SessionComposer.d.ts +9 -0
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +39 -13
- package/composer/SessionComposer.js.map +1 -1
- package/conversation/ConversationComposer.d.ts +19 -3
- package/conversation/ConversationComposer.d.ts.map +1 -1
- package/conversation/ConversationComposer.js +4 -4
- package/conversation/ConversationComposer.js.map +1 -1
- package/conversation/ConversationTemplatePickerDialog.d.ts +63 -0
- package/conversation/ConversationTemplatePickerDialog.d.ts.map +1 -0
- package/conversation/ConversationTemplatePickerDialog.js +151 -0
- package/conversation/ConversationTemplatePickerDialog.js.map +1 -0
- package/conversation/ConversationsWorkbench.d.ts.map +1 -1
- package/conversation/ConversationsWorkbench.js +16 -5
- package/conversation/ConversationsWorkbench.js.map +1 -1
- package/conversation/index.d.ts +3 -1
- package/conversation/index.d.ts.map +1 -1
- package/conversation/index.js +1 -0
- package/conversation/index.js.map +1 -1
- package/conversation/useConversationParticipation.d.ts +41 -4
- package/conversation/useConversationParticipation.d.ts.map +1 -1
- package/conversation/useConversationParticipation.js +13 -2
- package/conversation/useConversationParticipation.js.map +1 -1
- package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -1
- package/cursor-accounts/CursorAccountsConsole.js +12 -3
- package/cursor-accounts/CursorAccountsConsole.js.map +1 -1
- package/cursor-accounts/cursor-account-coverage.d.ts +30 -1
- package/cursor-accounts/cursor-account-coverage.d.ts.map +1 -1
- package/cursor-accounts/cursor-account-coverage.js +21 -0
- package/cursor-accounts/cursor-account-coverage.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +15 -0
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +5 -0
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/identity-account/AccountPreferencesPanel.d.ts +36 -0
- package/identity-account/AccountPreferencesPanel.d.ts.map +1 -0
- package/identity-account/AccountPreferencesPanel.js +202 -0
- package/identity-account/AccountPreferencesPanel.js.map +1 -0
- package/identity-account/index.d.ts +8 -0
- package/identity-account/index.d.ts.map +1 -1
- package/identity-account/index.js +4 -0
- package/identity-account/index.js.map +1 -1
- package/identity-account/useAccountExecutionDefaults.d.ts +36 -0
- package/identity-account/useAccountExecutionDefaults.d.ts.map +1 -0
- package/identity-account/useAccountExecutionDefaults.js +46 -0
- package/identity-account/useAccountExecutionDefaults.js.map +1 -0
- package/identity-account/useMyIdentityAccount.d.ts +55 -0
- package/identity-account/useMyIdentityAccount.d.ts.map +1 -0
- package/identity-account/useMyIdentityAccount.js +37 -0
- package/identity-account/useMyIdentityAccount.js.map +1 -0
- package/identity-account/useUpdateIdentityAccount.d.ts +42 -0
- package/identity-account/useUpdateIdentityAccount.d.ts.map +1 -0
- package/identity-account/useUpdateIdentityAccount.js +53 -0
- package/identity-account/useUpdateIdentityAccount.js.map +1 -0
- package/identity-provider/IdentityProviderDetailPanel.d.ts.map +1 -1
- package/identity-provider/IdentityProviderDetailPanel.js +16 -15
- package/identity-provider/IdentityProviderDetailPanel.js.map +1 -1
- package/index.d.ts +15 -13
- package/index.d.ts.map +1 -1
- package/index.js +11 -8
- package/index.js.map +1 -1
- package/internal/StandingContextField.d.ts +37 -0
- package/internal/StandingContextField.d.ts.map +1 -0
- package/internal/StandingContextField.js +24 -0
- package/internal/StandingContextField.js.map +1 -0
- package/library/InstanceVisibilitySelector.d.ts.map +1 -1
- package/library/InstanceVisibilitySelector.js +9 -2
- package/library/InstanceVisibilitySelector.js.map +1 -1
- package/library/ResourceVisibilityControl.d.ts +13 -3
- package/library/ResourceVisibilityControl.d.ts.map +1 -1
- package/library/ResourceVisibilityControl.js +33 -5
- package/library/ResourceVisibilityControl.js.map +1 -1
- package/library/VisibilityOptionRow.d.ts +6 -0
- package/library/VisibilityOptionRow.d.ts.map +1 -1
- package/library/VisibilityOptionRow.js +11 -1
- package/library/VisibilityOptionRow.js.map +1 -1
- package/library/VisibilitySelector.d.ts.map +1 -1
- package/library/VisibilitySelector.js +14 -2
- package/library/VisibilitySelector.js.map +1 -1
- package/library/index.d.ts +4 -2
- package/library/index.d.ts.map +1 -1
- package/library/index.js +2 -1
- package/library/index.js.map +1 -1
- package/library/useCanSetPublicVisibility.d.ts +30 -0
- package/library/useCanSetPublicVisibility.d.ts.map +1 -0
- package/library/useCanSetPublicVisibility.js +41 -0
- package/library/useCanSetPublicVisibility.js.map +1 -0
- package/library/visibilityLevels.d.ts +32 -1
- package/library/visibilityLevels.d.ts.map +1 -1
- package/library/visibilityLevels.js +34 -14
- package/library/visibilityLevels.js.map +1 -1
- package/mcp-server/McpServerDetailView.js +2 -2
- package/mcp-server/McpServerDetailView.js.map +1 -1
- package/mcp-server/index.d.ts +0 -1
- package/mcp-server/index.d.ts.map +1 -1
- package/mcp-server/index.js +0 -1
- package/mcp-server/index.js.map +1 -1
- package/mcp-server/internal/definitionDrift.d.ts +14 -12
- package/mcp-server/internal/definitionDrift.d.ts.map +1 -1
- package/mcp-server/internal/definitionDrift.js +22 -19
- package/mcp-server/internal/definitionDrift.js.map +1 -1
- package/mcp-server/useMcpServerConnect.d.ts +8 -5
- package/mcp-server/useMcpServerConnect.d.ts.map +1 -1
- package/mcp-server/useMcpServerConnect.js +2 -1
- package/mcp-server/useMcpServerConnect.js.map +1 -1
- package/mcp-server/useMcpServerOAuthConnect.d.ts.map +1 -1
- package/mcp-server/useMcpServerOAuthConnect.js +7 -2
- package/mcp-server/useMcpServerOAuthConnect.js.map +1 -1
- package/mcp-server/useUpdateMcpServer.d.ts +3 -3
- package/mcp-server/useUpdateMcpServer.js +3 -3
- package/models/ModelSelector.d.ts +33 -1
- package/models/ModelSelector.d.ts.map +1 -1
- package/models/ModelSelector.js +37 -4
- package/models/ModelSelector.js.map +1 -1
- package/models/index.d.ts +2 -0
- package/models/index.d.ts.map +1 -1
- package/models/index.js +1 -0
- package/models/index.js.map +1 -1
- package/models/registry.d.ts +16 -0
- package/models/registry.d.ts.map +1 -1
- package/models/registry.js +13 -0
- package/models/registry.js.map +1 -1
- package/models/thinking-mode.d.ts +27 -0
- package/models/thinking-mode.d.ts.map +1 -0
- package/models/thinking-mode.js +32 -0
- package/models/thinking-mode.js.map +1 -0
- package/oauth-app/CreateOAuthAppForm.d.ts +2 -2
- package/oauth-app/CreateOAuthAppForm.d.ts.map +1 -1
- package/oauth-app/CreateOAuthAppForm.js +13 -4
- package/oauth-app/CreateOAuthAppForm.js.map +1 -1
- package/oauth-app/OAuthAppDetailPanel.d.ts.map +1 -1
- package/oauth-app/OAuthAppDetailPanel.js +50 -22
- package/oauth-app/OAuthAppDetailPanel.js.map +1 -1
- package/organization/OrgPreferencesPanel.d.ts +32 -0
- package/organization/OrgPreferencesPanel.d.ts.map +1 -0
- package/organization/OrgPreferencesPanel.js +90 -0
- package/organization/OrgPreferencesPanel.js.map +1 -0
- package/organization/OrgProfilePanel.d.ts.map +1 -1
- package/organization/OrgProfilePanel.js +5 -4
- package/organization/OrgProfilePanel.js.map +1 -1
- package/organization/index.d.ts +2 -0
- package/organization/index.d.ts.map +1 -1
- package/organization/index.js +1 -0
- package/organization/index.js.map +1 -1
- package/package.json +4 -4
- package/platform-client/PlatformClientDetailPanel.d.ts.map +1 -1
- package/platform-client/PlatformClientDetailPanel.js +12 -13
- package/platform-client/PlatformClientDetailPanel.js.map +1 -1
- package/schedule/ScheduleDetailView.d.ts.map +1 -1
- package/schedule/ScheduleDetailView.js +21 -10
- package/schedule/ScheduleDetailView.js.map +1 -1
- package/schedule/ScheduleForm.d.ts.map +1 -1
- package/schedule/ScheduleForm.js +13 -3
- package/schedule/ScheduleForm.js.map +1 -1
- package/session/NewSessionViewer.d.ts +10 -1
- package/session/NewSessionViewer.d.ts.map +1 -1
- package/session/NewSessionViewer.js +2 -1
- package/session/NewSessionViewer.js.map +1 -1
- package/session/run-config.d.ts +9 -0
- package/session/run-config.d.ts.map +1 -1
- package/session/run-config.js +5 -0
- package/session/run-config.js.map +1 -1
- package/session/useNewSessionFlow.d.ts +27 -1
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +51 -8
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/useSessionConversation.d.ts +12 -1
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +15 -24
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +14 -10
- package/session/useSessionPageFlow.js.map +1 -1
- package/session/useUpdateSession.d.ts +18 -24
- package/session/useUpdateSession.d.ts.map +1 -1
- package/session/useUpdateSession.js +18 -24
- package/session/useUpdateSession.js.map +1 -1
- package/settings/AccountPreferencesSection.d.ts +3 -0
- package/settings/AccountPreferencesSection.d.ts.map +1 -0
- package/settings/AccountPreferencesSection.js +10 -0
- package/settings/AccountPreferencesSection.js.map +1 -0
- package/settings/OrgPreferencesSection.d.ts +3 -0
- package/settings/OrgPreferencesSection.d.ts.map +1 -0
- package/settings/OrgPreferencesSection.js +13 -0
- package/settings/OrgPreferencesSection.js.map +1 -0
- package/settings/index.d.ts +2 -0
- package/settings/index.d.ts.map +1 -1
- package/settings/index.js +2 -0
- package/settings/index.js.map +1 -1
- package/settings/settings-nav.d.ts.map +1 -1
- package/settings/settings-nav.js +17 -1
- package/settings/settings-nav.js.map +1 -1
- package/src/agent/AgentDetailView.tsx +2 -2
- package/src/agent/index.ts +0 -2
- package/src/agent/useUpdateAgent.ts +3 -3
- package/src/agent-instance/AgentInstanceDetailPanel.tsx +5 -7
- package/src/agent-instance/__tests__/AgentInstanceDetailPanel.test.tsx +75 -0
- package/src/agent-instance/useUpdateAgentInstance.ts +1 -1
- package/src/attachment/AttachmentChipList.tsx +19 -8
- package/src/attachment/__tests__/AttachmentChipList.test.tsx +12 -0
- package/src/attachment/__tests__/useAttachments.test.ts +293 -0
- package/src/attachment/index.ts +1 -0
- package/src/attachment/useAttachments.ts +143 -11
- package/src/channel/AgentChannelsPanel.tsx +26 -1
- package/src/channel/ChannelRunConfigDialog.tsx +351 -0
- package/src/channel/__tests__/ChannelRunConfigDialog.test.tsx +210 -0
- package/src/channel/index.ts +2 -0
- package/src/channel/useSaveAgentChannel.ts +6 -3
- package/src/channel-app/ChannelAppDetailPanel.tsx +9 -9
- package/src/channel-app/__tests__/channelAppWhatsApp.test.tsx +2 -0
- package/src/channel-app/__tests__/channelAppWipeSafety.test.tsx +87 -0
- package/src/composer/ComposerToolbar.tsx +9 -0
- package/src/composer/SessionComposer.tsx +59 -16
- package/src/composer/__tests__/SessionComposer-thinkingMode.test.tsx +176 -0
- package/src/conversation/ConversationComposer.tsx +38 -4
- package/src/conversation/ConversationTemplatePickerDialog.tsx +441 -0
- package/src/conversation/ConversationsWorkbench.tsx +40 -4
- package/src/conversation/__tests__/ConversationComposer.test.tsx +56 -1
- package/src/conversation/__tests__/ConversationTemplatePickerDialog.test.tsx +224 -0
- package/src/conversation/__tests__/useConversationParticipation.test.tsx +45 -4
- package/src/conversation/index.ts +4 -0
- package/src/conversation/useConversationParticipation.ts +58 -6
- package/src/cursor-accounts/CursorAccountsConsole.tsx +40 -5
- package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +32 -0
- package/src/cursor-accounts/__tests__/cursor-account-coverage.test.ts +58 -1
- package/src/cursor-accounts/cursor-account-coverage.ts +55 -0
- package/src/execution/useCreateAgentExecution.ts +19 -0
- package/src/identity-account/AccountPreferencesPanel.tsx +532 -0
- package/src/identity-account/__tests__/AccountPreferencesPanel.test.tsx +267 -0
- package/src/identity-account/__tests__/useAccountExecutionDefaults.test.tsx +116 -0
- package/src/identity-account/__tests__/useMyIdentityAccount.test.tsx +121 -0
- package/src/identity-account/index.ts +11 -0
- package/src/identity-account/useAccountExecutionDefaults.ts +63 -0
- package/src/identity-account/useMyIdentityAccount.ts +75 -0
- package/src/identity-account/useUpdateIdentityAccount.ts +73 -0
- package/src/identity-provider/IdentityProviderDetailPanel.tsx +18 -15
- package/src/identity-provider/__tests__/IdentityProviderDetailPanel.test.tsx +80 -0
- package/src/index.ts +26 -4
- package/src/internal/StandingContextField.tsx +92 -0
- package/src/library/InstanceVisibilitySelector.tsx +9 -2
- package/src/library/ResourceVisibilityControl.tsx +46 -13
- package/src/library/VisibilityOptionRow.tsx +28 -6
- package/src/library/VisibilitySelector.tsx +14 -2
- package/src/library/__tests__/VisibilitySelector.test.tsx +48 -1
- package/src/library/__tests__/visibilityLevels.test.ts +67 -1
- package/src/library/index.ts +6 -1
- package/src/library/useCanSetPublicVisibility.ts +56 -0
- package/src/library/visibilityLevels.ts +65 -14
- package/src/mcp-server/McpServerDetailView.tsx +2 -2
- package/src/mcp-server/__tests__/useMcpServerConnect.asyncLane.test.tsx +117 -0
- package/src/mcp-server/index.ts +0 -2
- package/src/mcp-server/internal/definitionDrift.ts +22 -20
- package/src/mcp-server/useMcpServerConnect.ts +9 -6
- package/src/mcp-server/useMcpServerOAuthConnect.ts +7 -2
- package/src/mcp-server/useUpdateMcpServer.ts +3 -3
- package/src/models/ModelSelector.tsx +94 -1
- package/src/models/index.ts +2 -0
- package/src/models/registry.ts +29 -0
- package/src/models/thinking-mode.ts +48 -0
- package/src/oauth-app/CreateOAuthAppForm.tsx +58 -3
- package/src/oauth-app/OAuthAppDetailPanel.tsx +103 -20
- package/src/oauth-app/__tests__/OAuthAppDetailPanel.test.tsx +94 -0
- package/src/organization/OrgPreferencesPanel.tsx +221 -0
- package/src/organization/OrgProfilePanel.tsx +5 -4
- package/src/organization/__tests__/OrgPreferencesPanel.test.tsx +145 -0
- package/src/organization/__tests__/OrgProfilePanel.test.tsx +108 -0
- package/src/organization/index.ts +2 -0
- package/src/platform-client/PlatformClientDetailPanel.tsx +14 -13
- package/src/platform-client/__tests__/PlatformClientDetailPanel.test.tsx +82 -0
- package/src/schedule/ScheduleDetailView.tsx +29 -4
- package/src/schedule/ScheduleForm.tsx +14 -1
- package/src/session/NewSessionViewer.tsx +12 -0
- package/src/session/__tests__/useNewSessionFlow.test.tsx +151 -0
- package/src/session/run-config.ts +16 -0
- package/src/session/useNewSessionFlow.ts +82 -9
- package/src/session/useSessionConversation.ts +32 -40
- package/src/session/useSessionPageFlow.ts +14 -11
- package/src/session/useUpdateSession.ts +18 -24
- package/src/settings/AccountPreferencesSection.tsx +25 -0
- package/src/settings/OrgPreferencesSection.tsx +35 -0
- package/src/settings/__tests__/settings-nav.test.ts +27 -0
- package/src/settings/index.ts +2 -0
- package/src/settings/settings-nav.ts +18 -0
- package/src/workflow/ApprovalFormBuilder.tsx +20 -27
- package/src/workflow/WorkflowDetailView.tsx +2 -2
- package/src/workflow/__tests__/WorkflowInstanceDetailPanel.test.tsx +83 -0
- package/src/workflow/index.ts +0 -2
- package/src/workflow/instance/WorkflowInstanceDetailPanel.tsx +6 -7
- package/src/workflow/useUpdateWorkflow.ts +3 -3
- package/styles.css +1 -1
- package/workflow/ApprovalFormBuilder.d.ts.map +1 -1
- package/workflow/ApprovalFormBuilder.js +10 -5
- package/workflow/ApprovalFormBuilder.js.map +1 -1
- package/workflow/WorkflowDetailView.js +2 -2
- package/workflow/WorkflowDetailView.js.map +1 -1
- package/workflow/index.d.ts +0 -1
- package/workflow/index.d.ts.map +1 -1
- package/workflow/index.js +0 -1
- package/workflow/index.js.map +1 -1
- package/workflow/instance/WorkflowInstanceDetailPanel.d.ts.map +1 -1
- package/workflow/instance/WorkflowInstanceDetailPanel.js +6 -7
- package/workflow/instance/WorkflowInstanceDetailPanel.js.map +1 -1
- package/workflow/useUpdateWorkflow.d.ts +3 -3
- package/workflow/useUpdateWorkflow.js +3 -3
- package/agent/internal/agentToInput.d.ts +0 -15
- package/agent/internal/agentToInput.d.ts.map +0 -1
- package/agent/internal/agentToInput.js +0 -84
- package/agent/internal/agentToInput.js.map +0 -1
- package/mcp-server/internal/mcpServerToInput.d.ts +0 -12
- package/mcp-server/internal/mcpServerToInput.d.ts.map +0 -1
- package/mcp-server/internal/mcpServerToInput.js +0 -84
- package/mcp-server/internal/mcpServerToInput.js.map +0 -1
- package/session/session-spec-converters.d.ts +0 -24
- package/session/session-spec-converters.d.ts.map +0 -1
- package/session/session-spec-converters.js +0 -72
- package/session/session-spec-converters.js.map +0 -1
- package/src/agent/internal/__tests__/agentToInput.test.ts +0 -145
- package/src/agent/internal/agentToInput.ts +0 -102
- package/src/mcp-server/__tests__/mcpServerToInput.test.ts +0 -201
- package/src/mcp-server/internal/mcpServerToInput.ts +0 -98
- package/src/session/__tests__/session-spec-converters.test.ts +0 -183
- package/src/session/session-spec-converters.ts +0 -86
- package/src/workflow/internal/workflowToInput.ts +0 -96
- package/workflow/internal/workflowToInput.d.ts +0 -12
- package/workflow/internal/workflowToInput.d.ts.map +0 -1
- package/workflow/internal/workflowToInput.js +0 -73
- package/workflow/internal/workflowToInput.js.map +0 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, fireEvent, waitFor, cleanup } from "@testing-library/react";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import {
|
|
5
|
+
ChannelAppSchema,
|
|
6
|
+
type ChannelApp,
|
|
7
|
+
} from "@stigmer/protos/ai/stigmer/agentic/channelapp/v1/api_pb";
|
|
8
|
+
import type { ChannelAppInput } from "@stigmer/sdk";
|
|
9
|
+
import { StigmerContext } from "../../context";
|
|
10
|
+
import { ChannelAppDetailPanel } from "../ChannelAppDetailPanel";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Regression suite for the full-spec-replace wipe bug: both detail forms
|
|
14
|
+
* must spread `toChannelAppUpdateInput` and override only their provider
|
|
15
|
+
* arm, so identity fields and any future spec field survive the save.
|
|
16
|
+
* Secrets follow the redaction contract: the form is seeded with the
|
|
17
|
+
* server's redaction marker, and sending the marker back means "keep the
|
|
18
|
+
* stored value" (per-field, verified by the backend's
|
|
19
|
+
* TestRedactAndEncryptCoverEveryProviderArm).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const REDACTED = "***REDACTED***";
|
|
23
|
+
|
|
24
|
+
const SLACK_APP: ChannelApp = create(ChannelAppSchema, {
|
|
25
|
+
metadata: {
|
|
26
|
+
id: "chapp_1",
|
|
27
|
+
name: "Acme Slack",
|
|
28
|
+
slug: "acme-slack",
|
|
29
|
+
org: "acme",
|
|
30
|
+
labels: { team: "support" },
|
|
31
|
+
},
|
|
32
|
+
spec: {
|
|
33
|
+
providerConfig: {
|
|
34
|
+
case: "slack",
|
|
35
|
+
value: {
|
|
36
|
+
clientId: "1234.5678",
|
|
37
|
+
clientSecret: REDACTED,
|
|
38
|
+
signingSecret: REDACTED,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
function renderPanel(update: ReturnType<typeof vi.fn>) {
|
|
45
|
+
const client = {
|
|
46
|
+
baseUrl: "https://api.stigmer.ai",
|
|
47
|
+
channelapp: { update, delete: vi.fn(async () => ({})) },
|
|
48
|
+
} as never;
|
|
49
|
+
return render(
|
|
50
|
+
<StigmerContext.Provider value={client}>
|
|
51
|
+
<ChannelAppDetailPanel
|
|
52
|
+
channelApp={SLACK_APP}
|
|
53
|
+
consoleOrigin="https://console.acme.example"
|
|
54
|
+
/>
|
|
55
|
+
</StigmerContext.Provider>,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
afterEach(cleanup);
|
|
60
|
+
|
|
61
|
+
describe("ChannelAppDetailPanel save payload (slack arm)", () => {
|
|
62
|
+
it("spreads the complete mapped input and overrides only the slack config", async () => {
|
|
63
|
+
const update = vi.fn(async (_input: ChannelAppInput) => SLACK_APP);
|
|
64
|
+
renderPanel(update);
|
|
65
|
+
|
|
66
|
+
const clientId = await screen.findByLabelText(/client id/i);
|
|
67
|
+
fireEvent.change(clientId, { target: { value: "9999.0000" } });
|
|
68
|
+
fireEvent.click(screen.getByRole("button", { name: /save credentials/i }));
|
|
69
|
+
|
|
70
|
+
await waitFor(() => expect(update).toHaveBeenCalledTimes(1));
|
|
71
|
+
const input = update.mock.calls[0]![0];
|
|
72
|
+
|
|
73
|
+
// The edited field, plus per-field keep-markers for untouched secrets.
|
|
74
|
+
expect(input.slack).toEqual({
|
|
75
|
+
clientId: "9999.0000",
|
|
76
|
+
clientSecret: REDACTED,
|
|
77
|
+
signingSecret: REDACTED,
|
|
78
|
+
});
|
|
79
|
+
// Identity and unrendered metadata survive via the mapper spread.
|
|
80
|
+
expect(input.name).toBe("Acme Slack");
|
|
81
|
+
expect(input.org).toBe("acme");
|
|
82
|
+
expect(input.slug).toBe("acme-slack");
|
|
83
|
+
expect(input.labels).toEqual({ team: "support" });
|
|
84
|
+
// The other provider arm stays absent — the oneof is preserved.
|
|
85
|
+
expect(input.whatsapp).toBeUndefined();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -13,6 +13,7 @@ import { ModelSelector } from "../models/ModelSelector.js";
|
|
|
13
13
|
import { HarnessSelector } from "../models/HarnessSelector.js";
|
|
14
14
|
import type { HarnessOption } from "../models/harness.js";
|
|
15
15
|
import type { ServiceTierOption } from "../models/service-tier.js";
|
|
16
|
+
import type { ThinkingModeOption } from "../models/thinking-mode.js";
|
|
16
17
|
import { InteractionModePicker, type InteractionModeOption } from "./InteractionModePicker.js";
|
|
17
18
|
import {
|
|
18
19
|
PaperclipIcon,
|
|
@@ -53,6 +54,10 @@ export interface ComposerToolbarProps {
|
|
|
53
54
|
readonly serviceTier?: ServiceTierOption;
|
|
54
55
|
/** Enables the fast-tier switch inside the model selector's options area. */
|
|
55
56
|
readonly onServiceTierChange?: (tier: ServiceTierOption) => void;
|
|
57
|
+
/** Current thinking mode for the selected model (#772). */
|
|
58
|
+
readonly thinkingMode?: ThinkingModeOption;
|
|
59
|
+
/** Enables the thinking switch inside the model selector's options area. */
|
|
60
|
+
readonly onThinkingModeChange?: (mode: ThinkingModeOption) => void;
|
|
56
61
|
|
|
57
62
|
// -- Right group: Secondary actions (icon-only) ---------------------------
|
|
58
63
|
|
|
@@ -128,6 +133,8 @@ export function ComposerToolbar({
|
|
|
128
133
|
onModelChange,
|
|
129
134
|
serviceTier,
|
|
130
135
|
onServiceTierChange,
|
|
136
|
+
thinkingMode,
|
|
137
|
+
onThinkingModeChange,
|
|
131
138
|
}: ComposerToolbarProps) {
|
|
132
139
|
const showHarnessSeparate = showHarnessSelector && !showModelSelector;
|
|
133
140
|
|
|
@@ -161,6 +168,8 @@ export function ComposerToolbar({
|
|
|
161
168
|
onHarnessChange={showHarnessSelector ? onHarnessChange : undefined}
|
|
162
169
|
serviceTier={serviceTier}
|
|
163
170
|
onServiceTierChange={onServiceTierChange}
|
|
171
|
+
thinkingMode={thinkingMode}
|
|
172
|
+
onThinkingModeChange={onThinkingModeChange}
|
|
164
173
|
disabled={disabled}
|
|
165
174
|
/>
|
|
166
175
|
)}
|
|
@@ -8,6 +8,7 @@ import { ComposerToolbar } from "./ComposerToolbar.js";
|
|
|
8
8
|
import { type ConfigureMenuItem } from "./ConfigureMenu.js";
|
|
9
9
|
import type { HarnessOption } from "../models/harness.js";
|
|
10
10
|
import { FAST_SERVICE_TIER, type ServiceTierOption } from "../models/service-tier.js";
|
|
11
|
+
import type { ThinkingModeOption } from "../models/thinking-mode.js";
|
|
11
12
|
import type { InteractionModeOption } from "./InteractionModePicker.js";
|
|
12
13
|
import { parseModelKey } from "../models/registry.js";
|
|
13
14
|
import { useModelRegistry } from "../models/useModelRegistry.js";
|
|
@@ -31,7 +32,6 @@ import type { UseGitHubConnectionReturn } from "../github/useGitHubConnection.js
|
|
|
31
32
|
import { useAttachments } from "../attachment/useAttachments.js";
|
|
32
33
|
import { AttachmentChipList } from "../attachment/AttachmentChipList.js";
|
|
33
34
|
import { extractClipboardFiles } from "../attachment/clipboard.js";
|
|
34
|
-
import { prepareImageForVision } from "../attachment/prepare-image.js";
|
|
35
35
|
import { useFileReferences } from "../file-reference/useFileReferences.js";
|
|
36
36
|
import { FileReferenceChipList } from "../file-reference/FileReferenceChipList.js";
|
|
37
37
|
import { FILE_REF_MIME } from "../internal/file-tree/index.js";
|
|
@@ -165,6 +165,14 @@ export interface SessionComposerSubmitContext {
|
|
|
165
165
|
* Pass to execution creation as `execution_config.service_tier`.
|
|
166
166
|
*/
|
|
167
167
|
readonly serviceTier?: ServiceTierOption;
|
|
168
|
+
/**
|
|
169
|
+
* Thinking mode the user actively selected for this execution
|
|
170
|
+
* (stigmer/stigmer#772). Only ever `"enabled"` — an untouched mode means
|
|
171
|
+
* "platform default" and is carried as `undefined`, the same
|
|
172
|
+
* unspecified-vs-explicit contract as {@link serviceTier}.
|
|
173
|
+
* Pass to execution creation as `execution_config.thinking_mode`.
|
|
174
|
+
*/
|
|
175
|
+
readonly thinkingMode?: ThinkingModeOption;
|
|
168
176
|
/**
|
|
169
177
|
* Workspace-relative file paths the user referenced via drag-to-reference.
|
|
170
178
|
*
|
|
@@ -664,6 +672,9 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
664
672
|
// selection below (#663) — state here records the user's intent only.
|
|
665
673
|
const [serviceTier, setServiceTier] = useState<ServiceTierOption>("standard");
|
|
666
674
|
|
|
675
|
+
// Thinking mode (#772): the tier's twin, same intent-vs-effective split.
|
|
676
|
+
const [thinkingMode, setThinkingMode] = useState<ThinkingModeOption>("disabled");
|
|
677
|
+
|
|
667
678
|
// Active harness mirror: controlled hosts (both viewers) keep the
|
|
668
679
|
// `harness` prop current, but with `showHarnessSelector` the dropdown
|
|
669
680
|
// lives inside ModelSelector and an uncontrolled host would leave the
|
|
@@ -838,10 +849,26 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
838
849
|
? "fast"
|
|
839
850
|
: "standard";
|
|
840
851
|
|
|
841
|
-
|
|
852
|
+
// "enabled" rides under the identical rule, keyed on the capability
|
|
853
|
+
// (cursor-harness models only — v1 has no native thinking mapping, #772).
|
|
854
|
+
const effectiveThinkingMode: ThinkingModeOption =
|
|
855
|
+
thinkingMode === "enabled"
|
|
856
|
+
&& effectiveModel !== undefined
|
|
857
|
+
&& effectiveModel.harness === "cursor"
|
|
858
|
+
&& effectiveModel.thinkingCapable === true
|
|
859
|
+
? "enabled"
|
|
860
|
+
: "disabled";
|
|
861
|
+
|
|
862
|
+
return {
|
|
863
|
+
modelId: effectiveModelId,
|
|
864
|
+
model: effectiveModel,
|
|
865
|
+
serviceTier: effectiveServiceTier,
|
|
866
|
+
thinkingMode: effectiveThinkingMode,
|
|
867
|
+
};
|
|
842
868
|
}, [
|
|
843
869
|
modelId,
|
|
844
870
|
serviceTier,
|
|
871
|
+
thinkingMode,
|
|
845
872
|
showModelSelector,
|
|
846
873
|
isRegistryLoading,
|
|
847
874
|
registryGetByKey,
|
|
@@ -855,11 +882,17 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
855
882
|
const visionNotice = useMemo(() => {
|
|
856
883
|
if (!enableAttachments || attachments.entries.length === 0) return null;
|
|
857
884
|
const preflight = assessVisionPreflight(
|
|
858
|
-
attachments.entries
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
885
|
+
attachments.entries
|
|
886
|
+
// A `preparing` entry still carries pre-downscale bytes — judging
|
|
887
|
+
// those would flash a false "too large" that vanishes when
|
|
888
|
+
// preparation lands. Same tri-state discipline as the module
|
|
889
|
+
// itself: silent while the real size is unknown.
|
|
890
|
+
.filter((e) => e.phase !== "preparing")
|
|
891
|
+
.map((e) => ({
|
|
892
|
+
name: e.file.name,
|
|
893
|
+
sizeBytes: e.file.size,
|
|
894
|
+
contentType: e.contentType,
|
|
895
|
+
})),
|
|
863
896
|
{ model: effective.model, limits: visionLimits },
|
|
864
897
|
);
|
|
865
898
|
return visionPreflightMessage(preflight, {
|
|
@@ -963,12 +996,11 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
963
996
|
e.preventDefault();
|
|
964
997
|
|
|
965
998
|
// Pasted images (and only pasted — see prepare-image.ts) are bounded
|
|
966
|
-
// to provider resolution before upload.
|
|
967
|
-
//
|
|
999
|
+
// to provider resolution before upload. The hook owns the whole
|
|
1000
|
+
// pipeline: chips appear immediately in the `preparing` phase
|
|
1001
|
+
// (stigmer/stigmer#369), then upload.
|
|
968
1002
|
const addFiles = attachments.addFiles;
|
|
969
|
-
|
|
970
|
-
addFiles(prepared);
|
|
971
|
-
});
|
|
1003
|
+
addFiles(files, { prepareImages: true });
|
|
972
1004
|
},
|
|
973
1005
|
[enableAttachments, isDisabled, attachments.addFiles],
|
|
974
1006
|
);
|
|
@@ -1050,15 +1082,19 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
1050
1082
|
// explicit distinction is load-bearing telemetry (#357).
|
|
1051
1083
|
const submitServiceTier =
|
|
1052
1084
|
effective.serviceTier === "fast" ? effective.serviceTier : undefined;
|
|
1085
|
+
// The tier's #772 twin: only an in-effect "enabled" is carried.
|
|
1086
|
+
const submitThinkingMode =
|
|
1087
|
+
effective.thinkingMode === "enabled" ? effective.thinkingMode : undefined;
|
|
1053
1088
|
|
|
1054
1089
|
const context: SessionComposerSubmitContext | undefined =
|
|
1055
1090
|
hasEnv || hasAttachments || effectiveMode || hasFileRefs || buildFromPlan
|
|
1056
|
-
|| submitServiceTier
|
|
1091
|
+
|| submitServiceTier || submitThinkingMode
|
|
1057
1092
|
? {
|
|
1058
1093
|
runtimeEnv: hasEnv ? env : undefined,
|
|
1059
1094
|
attachments: hasAttachments ? attachmentInputs : undefined,
|
|
1060
1095
|
interactionMode: effectiveMode,
|
|
1061
1096
|
serviceTier: submitServiceTier,
|
|
1097
|
+
thinkingMode: submitThinkingMode,
|
|
1062
1098
|
workspaceFileRefs: hasFileRefs ? [...fileRefs.refs] : undefined,
|
|
1063
1099
|
buildFromPlan,
|
|
1064
1100
|
}
|
|
@@ -1376,11 +1412,12 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
1376
1412
|
// ---------------------------------------------------------------------------
|
|
1377
1413
|
|
|
1378
1414
|
const mcpBlocked = showMcp && !mcpSetup.allReady;
|
|
1379
|
-
// Send waits for in-flight
|
|
1415
|
+
// Send waits for in-flight work: toAttachmentInputs() carries only
|
|
1380
1416
|
// "ready" entries, so a send racing an upload would silently drop the
|
|
1381
1417
|
// file the user just pasted — the worst failure for "what's wrong in
|
|
1382
1418
|
// this screenshot?". Errored entries never gate (their chip offers
|
|
1383
|
-
// retry/remove);
|
|
1419
|
+
// retry/remove); isUploading covers both the preparing and uploading
|
|
1420
|
+
// phases (see its doc — same drop class).
|
|
1384
1421
|
const uploadsBlocked = enableAttachments && attachments.isUploading;
|
|
1385
1422
|
const canSend = composer.canSubmit && !mcpBlocked && !uploadsBlocked;
|
|
1386
1423
|
|
|
@@ -1758,7 +1795,11 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
1758
1795
|
className="stg:mx-3 stg:mb-2 stg:flex stg:items-center stg:gap-2 stg:rounded-md stg:bg-muted stg:px-2.5 stg:py-1.5 stg:text-xs stg:text-muted-foreground"
|
|
1759
1796
|
>
|
|
1760
1797
|
<ChipSpinner />
|
|
1761
|
-
<span>
|
|
1798
|
+
<span>
|
|
1799
|
+
{attachments.isPreparing
|
|
1800
|
+
? "Waiting for attachments to finish preparing…"
|
|
1801
|
+
: "Waiting for attachments to finish uploading…"}
|
|
1802
|
+
</span>
|
|
1762
1803
|
</div>
|
|
1763
1804
|
)}
|
|
1764
1805
|
|
|
@@ -1885,6 +1926,8 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
1885
1926
|
onModelChange={handleModelChange}
|
|
1886
1927
|
serviceTier={serviceTier}
|
|
1887
1928
|
onServiceTierChange={setServiceTier}
|
|
1929
|
+
thinkingMode={thinkingMode}
|
|
1930
|
+
onThinkingModeChange={setThinkingMode}
|
|
1888
1931
|
/>
|
|
1889
1932
|
</div>
|
|
1890
1933
|
</div>
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach, beforeAll } from "vitest";
|
|
2
|
+
import { render, screen, cleanup, fireEvent, waitFor } from "@testing-library/react";
|
|
3
|
+
import { type ReactNode } from "react";
|
|
4
|
+
import type { Stigmer } from "@stigmer/sdk";
|
|
5
|
+
import { StigmerContext } from "../../context";
|
|
6
|
+
import { ModelRegistryContext } from "../../models/ModelRegistryContext";
|
|
7
|
+
import type { ModelInfo } from "../../models/registry";
|
|
8
|
+
import { SessionComposer } from "../SessionComposer";
|
|
9
|
+
|
|
10
|
+
// Shim ResizeObserver for Base UI's positioner (the
|
|
11
|
+
// scheduleCreation.test.tsx pattern).
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
if (!("ResizeObserver" in globalThis)) {
|
|
14
|
+
(globalThis as unknown as { ResizeObserver: unknown }).ResizeObserver =
|
|
15
|
+
class {
|
|
16
|
+
observe() {}
|
|
17
|
+
unobserve() {}
|
|
18
|
+
disconnect() {}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* End-to-end composer wiring for the thinking mode (stigmer/stigmer#772) —
|
|
25
|
+
* the SessionComposer-serviceTier twin. The toolbar's ModelSelector toggle
|
|
26
|
+
* must reach composer state, and an active enabled selection must ride the
|
|
27
|
+
* submit context to execution create. This seam (SessionComposer →
|
|
28
|
+
* ComposerToolbar props → submit context) is exactly where the #357 tier
|
|
29
|
+
* toggle originally shipped dead while every layer unit-tested green.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const THINKING_CAPABLE: ModelInfo = {
|
|
33
|
+
modelId: "claude-haiku-4-5",
|
|
34
|
+
provider: "anthropic",
|
|
35
|
+
displayName: "Haiku 4.5",
|
|
36
|
+
shortDescription: "Fast Claude with optional thinking",
|
|
37
|
+
speedTier: "fast",
|
|
38
|
+
costTier: "economy",
|
|
39
|
+
harness: "cursor",
|
|
40
|
+
featured: true,
|
|
41
|
+
serviceTiers: [],
|
|
42
|
+
thinkingCapable: true,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const NOT_CAPABLE: ModelInfo = {
|
|
46
|
+
modelId: "composer-2.5",
|
|
47
|
+
provider: "cursor",
|
|
48
|
+
displayName: "Composer 2.5",
|
|
49
|
+
shortDescription: "Fast agentic model",
|
|
50
|
+
speedTier: "fastest",
|
|
51
|
+
costTier: "economy",
|
|
52
|
+
harness: "cursor",
|
|
53
|
+
featured: true,
|
|
54
|
+
serviceTiers: ["fast"],
|
|
55
|
+
thinkingCapable: false,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function createMinimalStigmerMock(): Stigmer {
|
|
59
|
+
return {
|
|
60
|
+
agentExecution: { uploadAttachment: vi.fn() },
|
|
61
|
+
environment: { getPersonal: vi.fn().mockResolvedValue(null) },
|
|
62
|
+
baseUrl: "http://localhost:8080",
|
|
63
|
+
getAuthCredential: vi.fn().mockResolvedValue("test-token"),
|
|
64
|
+
config: {
|
|
65
|
+
baseUrl: "http://localhost:8080",
|
|
66
|
+
getAccessToken: vi.fn().mockResolvedValue(""),
|
|
67
|
+
},
|
|
68
|
+
} as unknown as Stigmer;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function createWrapper(client: Stigmer) {
|
|
72
|
+
return function Wrapper({ children }: { children: ReactNode }) {
|
|
73
|
+
return (
|
|
74
|
+
<StigmerContext.Provider value={client}>
|
|
75
|
+
<ModelRegistryContext.Provider
|
|
76
|
+
value={{
|
|
77
|
+
models: [THINKING_CAPABLE, NOT_CAPABLE],
|
|
78
|
+
isLoading: false,
|
|
79
|
+
error: null,
|
|
80
|
+
refetch: vi.fn(),
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
{children}
|
|
84
|
+
</ModelRegistryContext.Provider>
|
|
85
|
+
</StigmerContext.Provider>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function renderComposer(defaultModelId = "claude-haiku-4-5") {
|
|
91
|
+
const client = createMinimalStigmerMock();
|
|
92
|
+
const onSubmit = vi.fn();
|
|
93
|
+
|
|
94
|
+
const result = render(
|
|
95
|
+
<SessionComposer
|
|
96
|
+
onSubmit={onSubmit}
|
|
97
|
+
harness="cursor"
|
|
98
|
+
defaultModelId={defaultModelId}
|
|
99
|
+
/>,
|
|
100
|
+
{ wrapper: createWrapper(client) },
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return { ...result, onSubmit };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function submitMessage(message: string) {
|
|
107
|
+
const textarea = screen.getByRole("textbox");
|
|
108
|
+
fireEvent.change(textarea, { target: { value: message } });
|
|
109
|
+
fireEvent.keyDown(textarea, { key: "Enter", shiftKey: false });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Open the model selector popover (Base UI portals mount asynchronously in
|
|
114
|
+
* jsdom — the switch is awaited) and flip the thinking switch in the
|
|
115
|
+
* options area, then close the popover (toggling keeps it open).
|
|
116
|
+
*/
|
|
117
|
+
async function toggleThinking(triggerName: RegExp = /Haiku 4\.5/) {
|
|
118
|
+
fireEvent.click(screen.getByRole("button", { name: triggerName }));
|
|
119
|
+
const toggle = await screen.findByRole("switch", { name: "Thinking" });
|
|
120
|
+
fireEvent.click(toggle);
|
|
121
|
+
fireEvent.keyDown(document.activeElement ?? document.body, { key: "Escape" });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
afterEach(cleanup);
|
|
125
|
+
|
|
126
|
+
describe("SessionComposer — thinking mode submit contract", () => {
|
|
127
|
+
it("renders the thinking switch through the toolbar for a capable model", async () => {
|
|
128
|
+
renderComposer();
|
|
129
|
+
|
|
130
|
+
fireEvent.click(screen.getByRole("button", { name: /Haiku 4\.5/ }));
|
|
131
|
+
expect(await screen.findByRole("switch", { name: "Thinking" })).toBeTruthy();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("renders no thinking switch for a model without the capability", async () => {
|
|
135
|
+
renderComposer("composer-2.5");
|
|
136
|
+
|
|
137
|
+
fireEvent.click(screen.getByRole("button", { name: /Composer 2\.5/ }));
|
|
138
|
+
// The fast switch proves the options area itself rendered — only the
|
|
139
|
+
// thinking row must be absent (no dead controls).
|
|
140
|
+
await screen.findByRole("switch", { name: "Fast tier" });
|
|
141
|
+
expect(screen.queryByRole("switch", { name: "Thinking" })).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("carries thinkingMode 'enabled' on the submit context after toggling", async () => {
|
|
145
|
+
const { onSubmit } = renderComposer();
|
|
146
|
+
|
|
147
|
+
await toggleThinking();
|
|
148
|
+
submitMessage("Think hard about this");
|
|
149
|
+
|
|
150
|
+
await waitFor(() => expect(onSubmit).toHaveBeenCalledOnce());
|
|
151
|
+
const context = onSubmit.mock.calls[0][2];
|
|
152
|
+
expect(context?.thinkingMode).toBe("enabled");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("leaves thinkingMode undefined when the switch is untouched", async () => {
|
|
156
|
+
const { onSubmit } = renderComposer();
|
|
157
|
+
|
|
158
|
+
submitMessage("Just answer");
|
|
159
|
+
|
|
160
|
+
await waitFor(() => expect(onSubmit).toHaveBeenCalledOnce());
|
|
161
|
+
const context = onSubmit.mock.calls[0][2];
|
|
162
|
+
expect(context?.thinkingMode).toBeUndefined();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("leaves thinkingMode undefined after toggling on and back off", async () => {
|
|
166
|
+
const { onSubmit } = renderComposer();
|
|
167
|
+
|
|
168
|
+
await toggleThinking();
|
|
169
|
+
await toggleThinking();
|
|
170
|
+
submitMessage("Changed my mind");
|
|
171
|
+
|
|
172
|
+
await waitFor(() => expect(onSubmit).toHaveBeenCalledOnce());
|
|
173
|
+
const context = onSubmit.mock.calls[0][2];
|
|
174
|
+
expect(context?.thinkingMode).toBeUndefined();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useId, useState } from "react";
|
|
4
|
-
import { Send, TriangleAlert } from "lucide-react";
|
|
4
|
+
import { LayoutTemplate, Send, TriangleAlert } from "lucide-react";
|
|
5
5
|
import { cn } from "@stigmer/theme";
|
|
6
6
|
import { getUserMessage } from "@stigmer/sdk";
|
|
7
7
|
import { ChannelSendOutcome } from "@stigmer/protos/ai/stigmer/agentic/agentchannel/v1/message_io_pb";
|
|
@@ -9,13 +9,29 @@ import type { SendChannelMessageOutput } from "@stigmer/protos/ai/stigmer/agenti
|
|
|
9
9
|
import { Button } from "../button/Button.js";
|
|
10
10
|
import { SpinnerIcon } from "../internal/SpinnerIcon.js";
|
|
11
11
|
import { useComposer } from "../composer/useComposer.js";
|
|
12
|
+
import type { ConversationReplyPayload } from "./useConversationParticipation.js";
|
|
12
13
|
|
|
13
14
|
/** Props for {@link ConversationComposer}. */
|
|
14
15
|
export interface ConversationComposerProps {
|
|
15
|
-
/**
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Send a staff reply (from `useConversationParticipation().reply`).
|
|
18
|
+
* The composer's own textarea always submits the `text` lane; hosts
|
|
19
|
+
* wiring a template picker send the `template` lane through the same
|
|
20
|
+
* seam — one command, one busy state, one outcome contract.
|
|
21
|
+
*/
|
|
22
|
+
readonly onSend: (payload: ConversationReplyPayload) => Promise<SendChannelMessageOutput>;
|
|
17
23
|
/** `true` while a reply is in flight. */
|
|
18
24
|
readonly isSending: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Opens the host's template picker. When present, a persistent
|
|
27
|
+
* template affordance renders beside Send — templates are the one
|
|
28
|
+
* lane WhatsApp offers for a closed window, so the surface must
|
|
29
|
+
* offer the door, not just the advisory's warning (hosts should
|
|
30
|
+
* point at the button in the advisory copy; the advisory itself
|
|
31
|
+
* stays interaction-free — it is the input's description, F-18).
|
|
32
|
+
* Omit on channels whose provider has no template registry.
|
|
33
|
+
*/
|
|
34
|
+
readonly onOpenTemplatePicker?: () => void;
|
|
19
35
|
/**
|
|
20
36
|
* Why replying is unavailable, when it is — rendered in place of the
|
|
21
37
|
* input (a senderless provider, or a conversation the customer has
|
|
@@ -52,6 +68,7 @@ export interface ConversationComposerProps {
|
|
|
52
68
|
export function ConversationComposer({
|
|
53
69
|
onSend,
|
|
54
70
|
isSending,
|
|
71
|
+
onOpenTemplatePicker,
|
|
55
72
|
disabledReason,
|
|
56
73
|
advisory,
|
|
57
74
|
className,
|
|
@@ -66,7 +83,7 @@ export function ConversationComposer({
|
|
|
66
83
|
const composer = useComposer({
|
|
67
84
|
onSubmit: (message) => {
|
|
68
85
|
setNotice(null);
|
|
69
|
-
onSend(message).then(
|
|
86
|
+
onSend({ kind: "text", body: message }).then(
|
|
70
87
|
(output) => {
|
|
71
88
|
if (output.outcome === ChannelSendOutcome.refused) {
|
|
72
89
|
// The words never left; keep them so the user can adjust.
|
|
@@ -110,6 +127,12 @@ export function ConversationComposer({
|
|
|
110
127
|
className="stg:mb-2 stg:flex stg:items-start stg:gap-1.5 stg:rounded-md stg:bg-status-degraded-subtle stg:px-2.5 stg:py-1.5 stg:text-xs stg:text-status-degraded"
|
|
111
128
|
>
|
|
112
129
|
<TriangleAlert aria-hidden="true" className="stg:mt-0.5 stg:size-3.5 stg:shrink-0" />
|
|
130
|
+
{/* Interactive content is deliberately NOT allowed in here:
|
|
131
|
+
this element is the input's aria-describedby target with
|
|
132
|
+
zero tab stops (F-18). The way forward the advisory names
|
|
133
|
+
(the template lane) lives on the persistent template
|
|
134
|
+
button beside Send — hosts point at it in the advisory
|
|
135
|
+
COPY, never with an embedded control. */}
|
|
113
136
|
<span className="stg:min-w-0">{advisory}</span>
|
|
114
137
|
</p>
|
|
115
138
|
)}
|
|
@@ -137,6 +160,17 @@ export function ConversationComposer({
|
|
|
137
160
|
"stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-ring",
|
|
138
161
|
)}
|
|
139
162
|
/>
|
|
163
|
+
{onOpenTemplatePicker && (
|
|
164
|
+
<Button
|
|
165
|
+
variant="outline"
|
|
166
|
+
size="sm"
|
|
167
|
+
onClick={onOpenTemplatePicker}
|
|
168
|
+
disabled={isSending}
|
|
169
|
+
aria-label="Send a template reply"
|
|
170
|
+
>
|
|
171
|
+
<LayoutTemplate aria-hidden="true" className="stg:size-4" />
|
|
172
|
+
</Button>
|
|
173
|
+
)}
|
|
140
174
|
<Button
|
|
141
175
|
variant="primary"
|
|
142
176
|
size="sm"
|