@stigmer/react 3.12.5 → 3.12.7
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/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 +24 -1
- package/composer/ComposerToolbar.d.ts.map +1 -1
- package/composer/ComposerToolbar.js +7 -2
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/SessionComposer.d.ts +19 -0
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +24 -5
- package/composer/SessionComposer.js.map +1 -1
- package/composer/index.d.ts +1 -0
- package/composer/index.d.ts.map +1 -1
- package/composer/index.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/MessageThread.d.ts.map +1 -1
- package/execution/MessageThread.js +8 -11
- package/execution/MessageThread.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/execution/useFileChangeContent.d.ts +2 -6
- package/execution/useFileChangeContent.d.ts.map +1 -1
- package/execution/useFileChangeContent.js +4 -12
- package/execution/useFileChangeContent.js.map +1 -1
- package/identity-account/AccountPreferencesPanel.d.ts +2 -1
- package/identity-account/AccountPreferencesPanel.d.ts.map +1 -1
- package/identity-account/AccountPreferencesPanel.js +33 -18
- package/identity-account/AccountPreferencesPanel.js.map +1 -1
- 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 +10 -7
- package/index.d.ts.map +1 -1
- package/index.js +8 -4
- package/index.js.map +1 -1
- package/internal/download.d.ts +8 -0
- package/internal/download.d.ts.map +1 -0
- package/internal/download.js +25 -0
- package/internal/download.js.map +1 -0
- package/library/useExportResource.d.ts.map +1 -1
- package/library/useExportResource.js +3 -17
- package/library/useExportResource.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/useUpdateMcpServer.d.ts +3 -3
- package/mcp-server/useUpdateMcpServer.js +3 -3
- package/memory/MemoryListPanel.d.ts +35 -0
- package/memory/MemoryListPanel.d.ts.map +1 -0
- package/memory/MemoryListPanel.js +141 -0
- package/memory/MemoryListPanel.js.map +1 -0
- package/memory/index.d.ts +8 -0
- package/memory/index.d.ts.map +1 -0
- package/memory/index.js +12 -0
- package/memory/index.js.map +1 -0
- package/memory/memoryGroups.d.ts +31 -0
- package/memory/memoryGroups.d.ts.map +1 -0
- package/memory/memoryGroups.js +52 -0
- package/memory/memoryGroups.js.map +1 -0
- package/memory/useConfirmMemory.d.ts +31 -0
- package/memory/useConfirmMemory.d.ts.map +1 -0
- package/memory/useConfirmMemory.js +43 -0
- package/memory/useConfirmMemory.js.map +1 -0
- package/memory/useDeleteMemory.d.ts +30 -0
- package/memory/useDeleteMemory.d.ts.map +1 -0
- package/memory/useDeleteMemory.js +42 -0
- package/memory/useDeleteMemory.js.map +1 -0
- package/memory/useMemories.d.ts +33 -0
- package/memory/useMemories.d.ts.map +1 -0
- package/memory/useMemories.js +42 -0
- package/memory/useMemories.js.map +1 -0
- package/memory/useRejectMemory.d.ts +34 -0
- package/memory/useRejectMemory.d.ts.map +1 -0
- package/memory/useRejectMemory.js +46 -0
- package/memory/useRejectMemory.js.map +1 -0
- package/memory/useUpdateMemoryContent.d.ts +32 -0
- package/memory/useUpdateMemoryContent.d.ts.map +1 -0
- package/memory/useUpdateMemoryContent.js +48 -0
- package/memory/useUpdateMemoryContent.js.map +1 -0
- 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/OAuthAppDetailPanel.d.ts.map +1 -1
- package/oauth-app/OAuthAppDetailPanel.js +21 -20
- package/oauth-app/OAuthAppDetailPanel.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.map +1 -1
- package/session/NewSessionViewer.js +9 -1
- package/session/NewSessionViewer.js.map +1 -1
- package/session/SessionViewer.d.ts +14 -1
- package/session/SessionViewer.d.ts.map +1 -1
- package/session/SessionViewer.js +21 -25
- package/session/SessionViewer.js.map +1 -1
- package/session/TranscriptExportMenu.d.ts +24 -0
- package/session/TranscriptExportMenu.d.ts.map +1 -0
- package/session/TranscriptExportMenu.js +26 -0
- package/session/TranscriptExportMenu.js.map +1 -0
- package/session/index.d.ts +4 -0
- package/session/index.d.ts.map +1 -1
- package/session/index.js +2 -0
- package/session/index.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/useExportTranscript.d.ts +44 -0
- package/session/useExportTranscript.d.ts.map +1 -0
- package/session/useExportTranscript.js +109 -0
- package/session/useExportTranscript.js.map +1 -0
- package/session/useNewSessionFlow.d.ts +11 -0
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +20 -6
- 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 +21 -42
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionExecutions.d.ts +2 -14
- package/session/useSessionExecutions.d.ts.map +1 -1
- package/session/useSessionExecutions.js +4 -26
- package/session/useSessionExecutions.js.map +1 -1
- package/session/useSessionPageFlow.d.ts +23 -10
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +75 -19
- 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/MemorySection.d.ts +12 -0
- package/settings/MemorySection.d.ts.map +1 -0
- package/settings/MemorySection.js +21 -0
- package/settings/MemorySection.js.map +1 -0
- package/settings/index.d.ts +1 -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.d.ts.map +1 -1
- package/settings/settings-nav.js +2 -1
- package/settings/settings-nav.js.map +1 -1
- package/src/__tests__/public-api.test.ts +21 -0
- 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/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 +67 -0
- package/src/composer/SessionComposer.tsx +49 -3
- package/src/composer/__tests__/ComposerToolbar-autoApprove.test.tsx +80 -0
- package/src/composer/__tests__/SessionComposer-thinkingMode.test.tsx +176 -0
- package/src/composer/index.ts +1 -0
- 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/MessageThread.tsx +8 -11
- package/src/execution/useCreateAgentExecution.ts +19 -0
- package/src/execution/useFileChangeContent.ts +5 -12
- package/src/identity-account/AccountPreferencesPanel.tsx +154 -68
- package/src/identity-account/__tests__/AccountPreferencesPanel.test.tsx +36 -2
- package/src/identity-provider/IdentityProviderDetailPanel.tsx +18 -15
- package/src/identity-provider/__tests__/IdentityProviderDetailPanel.test.tsx +80 -0
- package/src/index.ts +36 -2
- package/src/internal/download.ts +29 -0
- package/src/library/useExportResource.ts +3 -19
- package/src/mcp-server/McpServerDetailView.tsx +2 -2
- package/src/mcp-server/index.ts +0 -2
- package/src/mcp-server/internal/definitionDrift.ts +22 -20
- package/src/mcp-server/useUpdateMcpServer.ts +3 -3
- package/src/memory/MemoryListPanel.tsx +532 -0
- package/src/memory/__tests__/MemoryListPanel.test.tsx +259 -0
- package/src/memory/__tests__/memoryHooks.test.tsx +220 -0
- package/src/memory/index.ts +19 -0
- package/src/memory/memoryGroups.ts +64 -0
- package/src/memory/useConfirmMemory.ts +62 -0
- package/src/memory/useDeleteMemory.ts +61 -0
- package/src/memory/useMemories.ts +66 -0
- package/src/memory/useRejectMemory.ts +65 -0
- package/src/memory/useUpdateMemoryContent.ts +67 -0
- 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/OAuthAppDetailPanel.tsx +23 -21
- package/src/oauth-app/__tests__/OAuthAppDetailPanel.test.tsx +94 -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 +15 -1
- package/src/session/SessionViewer.tsx +48 -49
- package/src/session/TranscriptExportMenu.tsx +78 -0
- package/src/session/__tests__/SessionViewer-autoApproveToggle.test.tsx +178 -0
- package/src/session/__tests__/SessionViewer-transcriptExport.test.tsx +151 -0
- package/src/session/__tests__/TranscriptExportMenu.test.tsx +97 -0
- package/src/session/__tests__/useExportTranscript.test.tsx +187 -0
- package/src/session/__tests__/useNewSessionFlow.test.tsx +33 -0
- package/src/session/__tests__/useSessionPageFlow.autoApprove.test.tsx +176 -2
- package/src/session/index.ts +9 -0
- package/src/session/run-config.ts +16 -0
- package/src/session/useExportTranscript.ts +171 -0
- package/src/session/useNewSessionFlow.ts +34 -7
- package/src/session/useSessionConversation.ts +42 -56
- package/src/session/useSessionExecutions.ts +5 -26
- package/src/session/useSessionPageFlow.ts +98 -32
- package/src/session/useUpdateSession.ts +18 -24
- package/src/settings/MemorySection.tsx +45 -0
- package/src/settings/index.ts +1 -0
- package/src/settings/settings-nav.ts +2 -0
- package/src/usage/useExportCSV.ts +2 -14
- package/src/workflow/ApprovalFormBuilder.tsx +20 -5
- 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/usage/useExportCSV.d.ts.map +1 -1
- package/usage/useExportCSV.js +2 -13
- package/usage/useExportCSV.js.map +1 -1
- package/workflow/ApprovalFormBuilder.d.ts.map +1 -1
- package/workflow/ApprovalFormBuilder.js +10 -8
- 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,210 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeAll, afterEach } from "vitest";
|
|
2
|
+
import { render, screen, fireEvent, waitFor, cleanup } from "@testing-library/react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { ServiceTier, ThinkingMode } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
5
|
+
import type { AgentChannelInput } from "@stigmer/sdk";
|
|
6
|
+
import { StigmerContext } from "../../context";
|
|
7
|
+
import { ModelRegistryContext } from "../../models/ModelRegistryContext";
|
|
8
|
+
import type { ModelInfo } from "../../models/registry";
|
|
9
|
+
import { ChannelRunConfigDialog } from "../ChannelRunConfigDialog";
|
|
10
|
+
|
|
11
|
+
// happy-dom does not implement the native dialog show/close methods.
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
HTMLDialogElement.prototype.showModal = function showModal() {
|
|
14
|
+
this.open = true;
|
|
15
|
+
};
|
|
16
|
+
HTMLDialogElement.prototype.close = function close() {
|
|
17
|
+
this.open = false;
|
|
18
|
+
};
|
|
19
|
+
if (!("ResizeObserver" in globalThis)) {
|
|
20
|
+
(globalThis as unknown as { ResizeObserver: unknown }).ResizeObserver =
|
|
21
|
+
class {
|
|
22
|
+
observe() {}
|
|
23
|
+
unobserve() {}
|
|
24
|
+
disconnect() {}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(cleanup);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The console's first run_config editor (stigmer/stigmer#792). The pinned
|
|
33
|
+
* behaviors are the edit-surface contracts the rest of the platform relies
|
|
34
|
+
* on: `max_tool_rounds` (the operator knob this editor never renders)
|
|
35
|
+
* survives every save verbatim, an all-empty draft clears the block
|
|
36
|
+
* ("empty = inherit"), and untouched variant switches stay off the wire
|
|
37
|
+
* (the #357/#772 unspecified-vs-explicit ledger distinction).
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
const MODELS: ModelInfo[] = [
|
|
41
|
+
{
|
|
42
|
+
modelId: "claude-haiku-4-5",
|
|
43
|
+
provider: "anthropic",
|
|
44
|
+
displayName: "Haiku 4.5",
|
|
45
|
+
shortDescription: "Fast Claude with optional thinking",
|
|
46
|
+
speedTier: "fast",
|
|
47
|
+
costTier: "economy",
|
|
48
|
+
harness: "cursor",
|
|
49
|
+
featured: true,
|
|
50
|
+
serviceTiers: [],
|
|
51
|
+
thinkingCapable: true,
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
function makeChannel(runConfig?: {
|
|
56
|
+
modelName?: string;
|
|
57
|
+
maxCostUsd?: number;
|
|
58
|
+
maxToolRounds?: number;
|
|
59
|
+
serviceTier?: ServiceTier;
|
|
60
|
+
thinkingMode?: ThinkingMode;
|
|
61
|
+
}) {
|
|
62
|
+
return {
|
|
63
|
+
metadata: {
|
|
64
|
+
id: "ach_1",
|
|
65
|
+
name: "Support Slack",
|
|
66
|
+
slug: "support-slack",
|
|
67
|
+
org: "acme",
|
|
68
|
+
labels: {},
|
|
69
|
+
},
|
|
70
|
+
spec: {
|
|
71
|
+
agentRef: { org: "acme", slug: "support-agent" },
|
|
72
|
+
enabled: true,
|
|
73
|
+
providerConfig: { case: "slack", value: {} },
|
|
74
|
+
environmentRefs: [],
|
|
75
|
+
...(runConfig
|
|
76
|
+
? {
|
|
77
|
+
runConfig: {
|
|
78
|
+
modelName: runConfig.modelName ?? "",
|
|
79
|
+
maxCostUsd: runConfig.maxCostUsd ?? 0,
|
|
80
|
+
maxToolRounds: runConfig.maxToolRounds ?? 0,
|
|
81
|
+
serviceTier: runConfig.serviceTier ?? ServiceTier.UNSPECIFIED,
|
|
82
|
+
thinkingMode: runConfig.thinkingMode ?? ThinkingMode.UNSPECIFIED,
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
: {}),
|
|
86
|
+
},
|
|
87
|
+
status: { installState: 2 },
|
|
88
|
+
} as never;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function createMockStigmer() {
|
|
92
|
+
const apply = vi.fn().mockResolvedValue({});
|
|
93
|
+
const client = { agentChannel: { apply } } as never;
|
|
94
|
+
return { client, apply };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function Providers({
|
|
98
|
+
client,
|
|
99
|
+
children,
|
|
100
|
+
}: {
|
|
101
|
+
client: unknown;
|
|
102
|
+
children: ReactNode;
|
|
103
|
+
}) {
|
|
104
|
+
return (
|
|
105
|
+
<StigmerContext.Provider value={client as never}>
|
|
106
|
+
<ModelRegistryContext.Provider
|
|
107
|
+
value={{ models: MODELS, isLoading: false, error: null, refetch: vi.fn() }}
|
|
108
|
+
>
|
|
109
|
+
{children}
|
|
110
|
+
</ModelRegistryContext.Provider>
|
|
111
|
+
</StigmerContext.Provider>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function renderDialog(channel: unknown) {
|
|
116
|
+
const { client, apply } = createMockStigmer();
|
|
117
|
+
const onSaved = vi.fn();
|
|
118
|
+
render(
|
|
119
|
+
<Providers client={client}>
|
|
120
|
+
<ChannelRunConfigDialog
|
|
121
|
+
open
|
|
122
|
+
onOpenChange={vi.fn()}
|
|
123
|
+
channel={channel as never}
|
|
124
|
+
onSaved={onSaved}
|
|
125
|
+
/>
|
|
126
|
+
</Providers>,
|
|
127
|
+
);
|
|
128
|
+
return { apply, onSaved };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function save() {
|
|
132
|
+
fireEvent.click(screen.getByRole("button", { name: "Save" }));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
describe("ChannelRunConfigDialog", () => {
|
|
136
|
+
it("preserves max_tool_rounds verbatim even though it never renders", async () => {
|
|
137
|
+
const { apply } = renderDialog(
|
|
138
|
+
makeChannel({ modelName: "claude-haiku-4-5", maxToolRounds: 25 }),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
expect(screen.queryByText(/tool round/i)).toBeNull();
|
|
142
|
+
await save();
|
|
143
|
+
|
|
144
|
+
await waitFor(() => expect(apply).toHaveBeenCalledOnce());
|
|
145
|
+
const input = apply.mock.calls[0][0] as AgentChannelInput;
|
|
146
|
+
expect(input.runConfig?.maxToolRounds).toBe(25);
|
|
147
|
+
expect(input.runConfig?.modelName).toBe("claude-haiku-4-5");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("clears run_config entirely when the draft is emptied (empty = inherit)", async () => {
|
|
151
|
+
const { apply } = renderDialog(
|
|
152
|
+
makeChannel({ modelName: "claude-haiku-4-5", maxCostUsd: 2 }),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
fireEvent.click(
|
|
156
|
+
screen.getByRole("button", { name: "Reset to platform default" }),
|
|
157
|
+
);
|
|
158
|
+
fireEvent.change(screen.getByLabelText(/Budget per run/), {
|
|
159
|
+
target: { value: "" },
|
|
160
|
+
});
|
|
161
|
+
await save();
|
|
162
|
+
|
|
163
|
+
await waitFor(() => expect(apply).toHaveBeenCalledOnce());
|
|
164
|
+
const input = apply.mock.calls[0][0] as AgentChannelInput;
|
|
165
|
+
expect(input.runConfig).toBeUndefined();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("carries an actively enabled thinking mode, and only then", async () => {
|
|
169
|
+
const { apply } = renderDialog(makeChannel({ modelName: "claude-haiku-4-5" }));
|
|
170
|
+
|
|
171
|
+
fireEvent.click(screen.getByRole("button", { name: /Haiku 4\.5/ }));
|
|
172
|
+
fireEvent.click(await screen.findByRole("switch", { name: "Thinking" }));
|
|
173
|
+
fireEvent.keyDown(document.activeElement ?? document.body, { key: "Escape" });
|
|
174
|
+
await save();
|
|
175
|
+
|
|
176
|
+
await waitFor(() => expect(apply).toHaveBeenCalledOnce());
|
|
177
|
+
const input = apply.mock.calls[0][0] as AgentChannelInput;
|
|
178
|
+
expect(input.runConfig?.thinkingMode).toBe(ThinkingMode.ENABLED);
|
|
179
|
+
// The untouched tier stays absent — never stamped as an explicit
|
|
180
|
+
// STANDARD by a save (#357 ledger distinction).
|
|
181
|
+
expect(input.runConfig?.serviceTier).toBeUndefined();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("round-trips a stored thinking pin into the switch state", async () => {
|
|
185
|
+
renderDialog(
|
|
186
|
+
makeChannel({
|
|
187
|
+
modelName: "claude-haiku-4-5",
|
|
188
|
+
thinkingMode: ThinkingMode.ENABLED,
|
|
189
|
+
}),
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
fireEvent.click(screen.getByRole("button", { name: /Haiku 4\.5/ }));
|
|
193
|
+
const toggle = await screen.findByRole("switch", { name: "Thinking" });
|
|
194
|
+
expect(toggle.getAttribute("aria-checked")).toBe("true");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("collects a budget without requiring a model pin", async () => {
|
|
198
|
+
const { apply } = renderDialog(makeChannel());
|
|
199
|
+
|
|
200
|
+
fireEvent.change(screen.getByLabelText(/Budget per run/), {
|
|
201
|
+
target: { value: "1.5" },
|
|
202
|
+
});
|
|
203
|
+
await save();
|
|
204
|
+
|
|
205
|
+
await waitFor(() => expect(apply).toHaveBeenCalledOnce());
|
|
206
|
+
const input = apply.mock.calls[0][0] as AgentChannelInput;
|
|
207
|
+
expect(input.runConfig?.maxCostUsd).toBe(1.5);
|
|
208
|
+
expect(input.runConfig?.modelName).toBeUndefined();
|
|
209
|
+
});
|
|
210
|
+
});
|
package/src/channel/index.ts
CHANGED
|
@@ -69,6 +69,8 @@ export type { ConnectWhatsAppDialogProps } from "./ConnectWhatsAppDialog.js";
|
|
|
69
69
|
|
|
70
70
|
export { ChannelCredentialsDialog } from "./ChannelCredentialsDialog.js";
|
|
71
71
|
export type { ChannelCredentialsDialogProps } from "./ChannelCredentialsDialog.js";
|
|
72
|
+
export { ChannelRunConfigDialog } from "./ChannelRunConfigDialog.js";
|
|
73
|
+
export type { ChannelRunConfigDialogProps } from "./ChannelRunConfigDialog.js";
|
|
72
74
|
|
|
73
75
|
export { ChannelToolCredentials } from "./ChannelToolCredentials.js";
|
|
74
76
|
export type { ChannelToolCredentialsProps } from "./ChannelToolCredentials.js";
|
|
@@ -66,9 +66,11 @@ export function agentChannelToInput(channel: AgentChannel): AgentChannelInput {
|
|
|
66
66
|
})),
|
|
67
67
|
}
|
|
68
68
|
: {}),
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
69
|
+
// run_config rides through verbatim so a settings save or
|
|
70
|
+
// pause/resume toggle never wipes an override set elsewhere (the
|
|
71
|
+
// ChannelRunConfigDialog — the one surface that edits it, #792 —
|
|
72
|
+
// overrides this copy with its own draft; CLI/API overrides survive
|
|
73
|
+
// every other save).
|
|
72
74
|
...(spec?.runConfig
|
|
73
75
|
? {
|
|
74
76
|
runConfig: {
|
|
@@ -76,6 +78,7 @@ export function agentChannelToInput(channel: AgentChannel): AgentChannelInput {
|
|
|
76
78
|
maxCostUsd: spec.runConfig.maxCostUsd,
|
|
77
79
|
maxToolRounds: spec.runConfig.maxToolRounds,
|
|
78
80
|
serviceTier: spec.runConfig.serviceTier,
|
|
81
|
+
thinkingMode: spec.runConfig.thinkingMode,
|
|
79
82
|
},
|
|
80
83
|
}
|
|
81
84
|
: {}),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { type FormEvent, useCallback, useId, useMemo, useState } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
-
import { getUserMessage } from "@stigmer/sdk";
|
|
5
|
+
import { getUserMessage, toChannelAppUpdateInput } from "@stigmer/sdk";
|
|
6
6
|
import type { ChannelApp } from "@stigmer/protos/ai/stigmer/agentic/channelapp/v1/api_pb";
|
|
7
7
|
import { useStigmer } from "../hooks.js";
|
|
8
8
|
import { useUpdateChannelApp } from "./useUpdateChannelApp.js";
|
|
@@ -229,10 +229,10 @@ function SlackAppDetail({
|
|
|
229
229
|
|
|
230
230
|
clearError();
|
|
231
231
|
try {
|
|
232
|
+
// Full-spec-replace safety: spread the complete mapped input and
|
|
233
|
+
// override only the slack arm this form owns.
|
|
232
234
|
const updated = await update({
|
|
233
|
-
|
|
234
|
-
org: channelApp.metadata?.org ?? "",
|
|
235
|
-
...(channelApp.metadata?.slug ? { slug: channelApp.metadata.slug } : {}),
|
|
235
|
+
...toChannelAppUpdateInput(channelApp),
|
|
236
236
|
slack: {
|
|
237
237
|
clientId: clientId.trim(),
|
|
238
238
|
// The redaction marker means "keep the stored value" — the
|
|
@@ -246,7 +246,7 @@ function SlackAppDetail({
|
|
|
246
246
|
// error state is managed by useUpdateChannelApp
|
|
247
247
|
}
|
|
248
248
|
},
|
|
249
|
-
[canSave, clearError, update,
|
|
249
|
+
[canSave, clearError, update, channelApp, clientId, clientSecret, signingSecret, onUpdated],
|
|
250
250
|
);
|
|
251
251
|
|
|
252
252
|
return (
|
|
@@ -379,10 +379,10 @@ function WhatsAppAppDetail({
|
|
|
379
379
|
|
|
380
380
|
clearError();
|
|
381
381
|
try {
|
|
382
|
+
// Full-spec-replace safety: spread the complete mapped input and
|
|
383
|
+
// override only the whatsapp arm this form owns.
|
|
382
384
|
const updated = await update({
|
|
383
|
-
|
|
384
|
-
org: channelApp.metadata?.org ?? "",
|
|
385
|
-
...(channelApp.metadata?.slug ? { slug: channelApp.metadata.slug } : {}),
|
|
385
|
+
...toChannelAppUpdateInput(channelApp),
|
|
386
386
|
whatsapp: {
|
|
387
387
|
appId: metaAppId.trim(),
|
|
388
388
|
// The redaction marker means "keep the stored value" — the
|
|
@@ -397,7 +397,7 @@ function WhatsAppAppDetail({
|
|
|
397
397
|
// error state is managed by useUpdateChannelApp
|
|
398
398
|
}
|
|
399
399
|
},
|
|
400
|
-
[canSave, clearError, update,
|
|
400
|
+
[canSave, clearError, update, channelApp, metaAppId, appSecret, accessToken, verifyToken, onUpdated],
|
|
401
401
|
);
|
|
402
402
|
|
|
403
403
|
return (
|
|
@@ -228,6 +228,8 @@ describe("ChannelAppDetailPanel — WhatsApp branch", () => {
|
|
|
228
228
|
|
|
229
229
|
await waitFor(() => expect(update).toHaveBeenCalled());
|
|
230
230
|
expect(update).toHaveBeenCalledWith({
|
|
231
|
+
// The generated mapper carries metadata.id for exact update addressing.
|
|
232
|
+
id: "chapp_1",
|
|
231
233
|
name: "Acme WhatsApp",
|
|
232
234
|
org: "acme",
|
|
233
235
|
slug: "acme-whatsapp",
|
|
@@ -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
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useId } from "react";
|
|
3
4
|
import { cn } from "@stigmer/theme";
|
|
4
5
|
import {
|
|
5
6
|
Tooltip,
|
|
@@ -7,12 +8,14 @@ import {
|
|
|
7
8
|
TooltipProvider,
|
|
8
9
|
TooltipTrigger,
|
|
9
10
|
} from "../internal/tooltip.js";
|
|
11
|
+
import { Switch } from "../switch/Switch.js";
|
|
10
12
|
import { ContextPopover } from "./ContextPopover.js";
|
|
11
13
|
import { ConfigureMenu, type ConfigureMenuItem } from "./ConfigureMenu.js";
|
|
12
14
|
import { ModelSelector } from "../models/ModelSelector.js";
|
|
13
15
|
import { HarnessSelector } from "../models/HarnessSelector.js";
|
|
14
16
|
import type { HarnessOption } from "../models/harness.js";
|
|
15
17
|
import type { ServiceTierOption } from "../models/service-tier.js";
|
|
18
|
+
import type { ThinkingModeOption } from "../models/thinking-mode.js";
|
|
16
19
|
import { InteractionModePicker, type InteractionModeOption } from "./InteractionModePicker.js";
|
|
17
20
|
import {
|
|
18
21
|
PaperclipIcon,
|
|
@@ -22,6 +25,18 @@ import {
|
|
|
22
25
|
} from "./icons.js";
|
|
23
26
|
import { SpinnerIcon } from "../internal/SpinnerIcon.js";
|
|
24
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Wiring for the always-available "auto-approve tool calls" toggle
|
|
30
|
+
* (stigmer/stigmer#816). Presence renders the control; omission keeps the
|
|
31
|
+
* toolbar byte-identical for consumers that don't wire it (DD-011).
|
|
32
|
+
*/
|
|
33
|
+
export interface ComposerAutoApproveProps {
|
|
34
|
+
/** Whether auto-approve is currently armed for this conversation. */
|
|
35
|
+
readonly armed: boolean;
|
|
36
|
+
/** Called with the next value when the user flips the toggle. */
|
|
37
|
+
readonly onChange: (armed: boolean) => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
export interface ComposerToolbarProps {
|
|
26
41
|
readonly disabled: boolean;
|
|
27
42
|
readonly isSubmitting: boolean;
|
|
@@ -46,6 +61,14 @@ export interface ComposerToolbarProps {
|
|
|
46
61
|
readonly interactionMode?: InteractionModeOption;
|
|
47
62
|
readonly onInteractionModeChange: (mode: InteractionModeOption) => void;
|
|
48
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Always-available auto-approve toggle (#816). Deliberately NOT bound to
|
|
66
|
+
* {@link disabled}: the walk-away user flips it exactly while a turn
|
|
67
|
+
* streams, so it stays interactive under the composer lock — the same
|
|
68
|
+
* exemption the Stop button carries.
|
|
69
|
+
*/
|
|
70
|
+
readonly autoApprove?: ComposerAutoApproveProps;
|
|
71
|
+
|
|
49
72
|
readonly showModelSelector: boolean;
|
|
50
73
|
readonly modelId?: string;
|
|
51
74
|
readonly onModelChange: (id: string) => void;
|
|
@@ -53,6 +76,10 @@ export interface ComposerToolbarProps {
|
|
|
53
76
|
readonly serviceTier?: ServiceTierOption;
|
|
54
77
|
/** Enables the fast-tier switch inside the model selector's options area. */
|
|
55
78
|
readonly onServiceTierChange?: (tier: ServiceTierOption) => void;
|
|
79
|
+
/** Current thinking mode for the selected model (#772). */
|
|
80
|
+
readonly thinkingMode?: ThinkingModeOption;
|
|
81
|
+
/** Enables the thinking switch inside the model selector's options area. */
|
|
82
|
+
readonly onThinkingModeChange?: (mode: ThinkingModeOption) => void;
|
|
56
83
|
|
|
57
84
|
// -- Right group: Secondary actions (icon-only) ---------------------------
|
|
58
85
|
|
|
@@ -123,13 +150,17 @@ export function ComposerToolbar({
|
|
|
123
150
|
showInteractionModePicker,
|
|
124
151
|
interactionMode,
|
|
125
152
|
onInteractionModeChange,
|
|
153
|
+
autoApprove,
|
|
126
154
|
showModelSelector,
|
|
127
155
|
modelId,
|
|
128
156
|
onModelChange,
|
|
129
157
|
serviceTier,
|
|
130
158
|
onServiceTierChange,
|
|
159
|
+
thinkingMode,
|
|
160
|
+
onThinkingModeChange,
|
|
131
161
|
}: ComposerToolbarProps) {
|
|
132
162
|
const showHarnessSeparate = showHarnessSelector && !showModelSelector;
|
|
163
|
+
const autoApproveLabelId = useId();
|
|
133
164
|
|
|
134
165
|
return (
|
|
135
166
|
<div className="stg:flex stg:items-center stg:justify-between stg:gap-2 stg:border-t stg:border-border-muted stg:px-3 stg:py-2">
|
|
@@ -161,9 +192,45 @@ export function ComposerToolbar({
|
|
|
161
192
|
onHarnessChange={showHarnessSelector ? onHarnessChange : undefined}
|
|
162
193
|
serviceTier={serviceTier}
|
|
163
194
|
onServiceTierChange={onServiceTierChange}
|
|
195
|
+
thinkingMode={thinkingMode}
|
|
196
|
+
onThinkingModeChange={onThinkingModeChange}
|
|
164
197
|
disabled={disabled}
|
|
165
198
|
/>
|
|
166
199
|
)}
|
|
200
|
+
|
|
201
|
+
{/* Auto-approve (#816): primary state like Mode and Model — armed
|
|
202
|
+
vs gated is something the user glances at before walking away.
|
|
203
|
+
Never bound to `disabled` (see ComposerAutoApproveProps): the
|
|
204
|
+
whole point is flipping it while a turn streams. Scoped provider:
|
|
205
|
+
this tooltip does not share the icon cluster's hover-delay group. */}
|
|
206
|
+
{autoApprove && (
|
|
207
|
+
<TooltipProvider>
|
|
208
|
+
<Tooltip>
|
|
209
|
+
<TooltipTrigger render={<span className="stg:inline-flex" />}>
|
|
210
|
+
<span className="stg:flex stg:shrink-0 stg:items-center stg:gap-1.5 stg:px-1">
|
|
211
|
+
<label
|
|
212
|
+
id={autoApproveLabelId}
|
|
213
|
+
htmlFor={`${autoApproveLabelId}-switch`}
|
|
214
|
+
className="stg:cursor-pointer stg:select-none stg:text-xs stg:text-muted-foreground"
|
|
215
|
+
>
|
|
216
|
+
Auto-approve
|
|
217
|
+
</label>
|
|
218
|
+
<Switch
|
|
219
|
+
id={`${autoApproveLabelId}-switch`}
|
|
220
|
+
checked={autoApprove.armed}
|
|
221
|
+
onCheckedChange={autoApprove.onChange}
|
|
222
|
+
aria-labelledby={autoApproveLabelId}
|
|
223
|
+
/>
|
|
224
|
+
</span>
|
|
225
|
+
</TooltipTrigger>
|
|
226
|
+
<TooltipContent side="top">
|
|
227
|
+
{autoApprove.armed
|
|
228
|
+
? "Tool calls run without asking — this conversation only. Turn off to be asked again."
|
|
229
|
+
: "Run this conversation's tool calls without asking for approval, including the run in progress."}
|
|
230
|
+
</TooltipContent>
|
|
231
|
+
</Tooltip>
|
|
232
|
+
</TooltipProvider>
|
|
233
|
+
)}
|
|
167
234
|
</div>
|
|
168
235
|
|
|
169
236
|
{/* ---- Right group: Secondary actions (icon-only) + Send ----
|
|
@@ -4,10 +4,11 @@ import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo,
|
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
5
|
import { getUserMessage, type AttachmentInput, type EnvVarInput, type McpServerUsageInput, type ResourceRef } from "@stigmer/sdk";
|
|
6
6
|
import { useComposer } from "./useComposer.js";
|
|
7
|
-
import { ComposerToolbar } from "./ComposerToolbar.js";
|
|
7
|
+
import { ComposerToolbar, type ComposerAutoApproveProps } 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";
|
|
@@ -164,6 +165,14 @@ export interface SessionComposerSubmitContext {
|
|
|
164
165
|
* Pass to execution creation as `execution_config.service_tier`.
|
|
165
166
|
*/
|
|
166
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;
|
|
167
176
|
/**
|
|
168
177
|
* Workspace-relative file paths the user referenced via drag-to-reference.
|
|
169
178
|
*
|
|
@@ -291,6 +300,16 @@ export interface SessionComposerProps {
|
|
|
291
300
|
/** Show the model selector. @default true */
|
|
292
301
|
readonly showModelSelector?: boolean;
|
|
293
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Always-visible "auto-approve tool calls" toggle in the toolbar
|
|
305
|
+
* (stigmer/stigmer#816). Presence renders it; omission keeps the composer
|
|
306
|
+
* byte-identical (DD-011). Stays interactive while the composer is
|
|
307
|
+
* `disabled` — the walk-away user flips it exactly while a turn streams —
|
|
308
|
+
* so consumers must only wire it on surfaces whose viewer may submit
|
|
309
|
+
* approvals (never guest/observer). See {@link ComposerAutoApproveProps}.
|
|
310
|
+
*/
|
|
311
|
+
readonly autoApprove?: ComposerAutoApproveProps;
|
|
312
|
+
|
|
294
313
|
/**
|
|
295
314
|
* Workspace state managed by {@link useWorkspaceEntries}.
|
|
296
315
|
* When provided, renders a workspace trigger in the toolbar
|
|
@@ -608,6 +627,7 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
608
627
|
defaultModelId,
|
|
609
628
|
onModelChange,
|
|
610
629
|
showModelSelector = true,
|
|
630
|
+
autoApprove,
|
|
611
631
|
workspace,
|
|
612
632
|
gitHubConnection,
|
|
613
633
|
enableGitHub = true,
|
|
@@ -663,6 +683,9 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
663
683
|
// selection below (#663) — state here records the user's intent only.
|
|
664
684
|
const [serviceTier, setServiceTier] = useState<ServiceTierOption>("standard");
|
|
665
685
|
|
|
686
|
+
// Thinking mode (#772): the tier's twin, same intent-vs-effective split.
|
|
687
|
+
const [thinkingMode, setThinkingMode] = useState<ThinkingModeOption>("disabled");
|
|
688
|
+
|
|
666
689
|
// Active harness mirror: controlled hosts (both viewers) keep the
|
|
667
690
|
// `harness` prop current, but with `showHarnessSelector` the dropdown
|
|
668
691
|
// lives inside ModelSelector and an uncontrolled host would leave the
|
|
@@ -837,10 +860,26 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
837
860
|
? "fast"
|
|
838
861
|
: "standard";
|
|
839
862
|
|
|
840
|
-
|
|
863
|
+
// "enabled" rides under the identical rule, keyed on the capability
|
|
864
|
+
// (cursor-harness models only — v1 has no native thinking mapping, #772).
|
|
865
|
+
const effectiveThinkingMode: ThinkingModeOption =
|
|
866
|
+
thinkingMode === "enabled"
|
|
867
|
+
&& effectiveModel !== undefined
|
|
868
|
+
&& effectiveModel.harness === "cursor"
|
|
869
|
+
&& effectiveModel.thinkingCapable === true
|
|
870
|
+
? "enabled"
|
|
871
|
+
: "disabled";
|
|
872
|
+
|
|
873
|
+
return {
|
|
874
|
+
modelId: effectiveModelId,
|
|
875
|
+
model: effectiveModel,
|
|
876
|
+
serviceTier: effectiveServiceTier,
|
|
877
|
+
thinkingMode: effectiveThinkingMode,
|
|
878
|
+
};
|
|
841
879
|
}, [
|
|
842
880
|
modelId,
|
|
843
881
|
serviceTier,
|
|
882
|
+
thinkingMode,
|
|
844
883
|
showModelSelector,
|
|
845
884
|
isRegistryLoading,
|
|
846
885
|
registryGetByKey,
|
|
@@ -1054,15 +1093,19 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
1054
1093
|
// explicit distinction is load-bearing telemetry (#357).
|
|
1055
1094
|
const submitServiceTier =
|
|
1056
1095
|
effective.serviceTier === "fast" ? effective.serviceTier : undefined;
|
|
1096
|
+
// The tier's #772 twin: only an in-effect "enabled" is carried.
|
|
1097
|
+
const submitThinkingMode =
|
|
1098
|
+
effective.thinkingMode === "enabled" ? effective.thinkingMode : undefined;
|
|
1057
1099
|
|
|
1058
1100
|
const context: SessionComposerSubmitContext | undefined =
|
|
1059
1101
|
hasEnv || hasAttachments || effectiveMode || hasFileRefs || buildFromPlan
|
|
1060
|
-
|| submitServiceTier
|
|
1102
|
+
|| submitServiceTier || submitThinkingMode
|
|
1061
1103
|
? {
|
|
1062
1104
|
runtimeEnv: hasEnv ? env : undefined,
|
|
1063
1105
|
attachments: hasAttachments ? attachmentInputs : undefined,
|
|
1064
1106
|
interactionMode: effectiveMode,
|
|
1065
1107
|
serviceTier: submitServiceTier,
|
|
1108
|
+
thinkingMode: submitThinkingMode,
|
|
1066
1109
|
workspaceFileRefs: hasFileRefs ? [...fileRefs.refs] : undefined,
|
|
1067
1110
|
buildFromPlan,
|
|
1068
1111
|
}
|
|
@@ -1888,12 +1931,15 @@ const SessionComposerInner = forwardRef<SessionComposerHandle, SessionComposerPr
|
|
|
1888
1931
|
interactionMode={interactionMode}
|
|
1889
1932
|
onInteractionModeChange={handleInteractionModeChange}
|
|
1890
1933
|
showModelSelector={showModelSelector}
|
|
1934
|
+
autoApprove={autoApprove}
|
|
1891
1935
|
// The pill renders the effective selection — the same value the
|
|
1892
1936
|
// submit payload carries (#663), never a fallback of its own.
|
|
1893
1937
|
modelId={effective.modelId}
|
|
1894
1938
|
onModelChange={handleModelChange}
|
|
1895
1939
|
serviceTier={serviceTier}
|
|
1896
1940
|
onServiceTierChange={setServiceTier}
|
|
1941
|
+
thinkingMode={thinkingMode}
|
|
1942
|
+
onThinkingModeChange={setThinkingMode}
|
|
1897
1943
|
/>
|
|
1898
1944
|
</div>
|
|
1899
1945
|
</div>
|