@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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { type FormEvent, type KeyboardEvent, useCallback, useId, useState } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
5
|
import { UNSTYLED_FIELDSET } from "../internal/element-resets.js";
|
|
6
|
-
import { getUserMessage } from "@stigmer/sdk";
|
|
6
|
+
import { getUserMessage, toPlatformClientUpdateInput } from "@stigmer/sdk";
|
|
7
7
|
import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
|
|
8
8
|
import { timestampDate, type Timestamp } from "@bufbuild/protobuf/wkt";
|
|
9
9
|
import type { PlatformClient } from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
|
|
@@ -176,21 +176,22 @@ export function PlatformClientDetailPanel({
|
|
|
176
176
|
e.preventDefault();
|
|
177
177
|
clearUpdateError();
|
|
178
178
|
try {
|
|
179
|
+
// Full-spec-replace safety: spread the complete mapped input and
|
|
180
|
+
// override only the edited fields, so unlisted spec fields (e.g.
|
|
181
|
+
// environment_refs, the embedded-assistant credential binding)
|
|
182
|
+
// survive the save. Cleared fields are set to undefined
|
|
183
|
+
// explicitly — omitting them would carry the stale mapped value.
|
|
179
184
|
const updated = await update({
|
|
180
|
-
|
|
181
|
-
slug: meta?.slug,
|
|
182
|
-
org: meta?.org ?? "",
|
|
185
|
+
...toPlatformClientUpdateInput(platformClient),
|
|
183
186
|
neverExpires,
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
expiresAt: new Date(expiresAt).toISOString(),
|
|
187
|
-
}),
|
|
187
|
+
expiresAt:
|
|
188
|
+
!neverExpires && expiresAt ? new Date(expiresAt) : undefined,
|
|
188
189
|
autoProvisionAccounts: autoProvision,
|
|
189
190
|
autoGrantOnOrg: autoGrant,
|
|
190
|
-
|
|
191
|
-
autoGrantRole !== IamRole.iam_role_unspecified
|
|
192
|
-
autoGrantRole
|
|
193
|
-
|
|
191
|
+
autoGrantRole:
|
|
192
|
+
autoGrant && autoGrantRole !== IamRole.iam_role_unspecified
|
|
193
|
+
? autoGrantRole
|
|
194
|
+
: undefined,
|
|
194
195
|
allowedOrigins: origins,
|
|
195
196
|
});
|
|
196
197
|
setMode("view");
|
|
@@ -200,7 +201,7 @@ export function PlatformClientDetailPanel({
|
|
|
200
201
|
}
|
|
201
202
|
},
|
|
202
203
|
[
|
|
203
|
-
|
|
204
|
+
platformClient,
|
|
204
205
|
neverExpires,
|
|
205
206
|
expiresAt,
|
|
206
207
|
autoProvision,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
2
|
+
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import { timestampFromDate } from "@bufbuild/protobuf/wkt";
|
|
5
|
+
import {
|
|
6
|
+
PlatformClientSchema,
|
|
7
|
+
type PlatformClient,
|
|
8
|
+
} from "@stigmer/protos/ai/stigmer/iam/platformclient/v1/api_pb";
|
|
9
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
10
|
+
import { IamRole } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
|
|
11
|
+
import type { PlatformClientInput } from "@stigmer/sdk";
|
|
12
|
+
import { StigmerContext } from "../../context";
|
|
13
|
+
import { PlatformClientDetailPanel } from "../PlatformClientDetailPanel";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Regression suite for the full-spec-replace wipe bug: before the
|
|
17
|
+
* generated-mapper migration, this panel hand-built its update input and
|
|
18
|
+
* NEVER sent `environment_refs` — saving any edit silently wiped the
|
|
19
|
+
* client's credential-delivery environment bindings. The panel must
|
|
20
|
+
* spread `toPlatformClientUpdateInput` and override only what it edits.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const PLATFORM_CLIENT: PlatformClient = create(PlatformClientSchema, {
|
|
24
|
+
metadata: {
|
|
25
|
+
id: "pc-1",
|
|
26
|
+
name: "Embed Client",
|
|
27
|
+
slug: "embed-client",
|
|
28
|
+
org: "acme",
|
|
29
|
+
},
|
|
30
|
+
spec: {
|
|
31
|
+
clientId: "client-abc",
|
|
32
|
+
expiresAt: timestampFromDate(new Date("2027-06-01T00:00:00Z")),
|
|
33
|
+
neverExpires: false,
|
|
34
|
+
autoProvisionAccounts: true,
|
|
35
|
+
autoGrantOnOrg: true,
|
|
36
|
+
autoGrantRole: IamRole.admin,
|
|
37
|
+
allowedOrigins: ["https://embed.acme.example"],
|
|
38
|
+
environmentRefs: [
|
|
39
|
+
{ org: "acme", slug: "prod", kind: ApiResourceKind.environment },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
function renderPanel(update: ReturnType<typeof vi.fn>) {
|
|
45
|
+
const client = {
|
|
46
|
+
platformclient: { update },
|
|
47
|
+
} as never;
|
|
48
|
+
return render(
|
|
49
|
+
<StigmerContext.Provider value={client}>
|
|
50
|
+
<PlatformClientDetailPanel platformClient={PLATFORM_CLIENT} />
|
|
51
|
+
</StigmerContext.Provider>,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
afterEach(cleanup);
|
|
56
|
+
|
|
57
|
+
describe("PlatformClientDetailPanel save payload", () => {
|
|
58
|
+
it("round-trips environment_refs on an origins-only edit (the wipe bug)", async () => {
|
|
59
|
+
const update = vi.fn(async (_input: PlatformClientInput) => PLATFORM_CLIENT);
|
|
60
|
+
renderPanel(update);
|
|
61
|
+
|
|
62
|
+
fireEvent.click(screen.getByRole("button", { name: "Edit" }));
|
|
63
|
+
fireEvent.click(screen.getByRole("button", { name: "Save changes" }));
|
|
64
|
+
|
|
65
|
+
await waitFor(() => expect(update).toHaveBeenCalledTimes(1));
|
|
66
|
+
const input = update.mock.calls[0]![0];
|
|
67
|
+
|
|
68
|
+
// The wipe-bug guard: the form does not render environment bindings,
|
|
69
|
+
// yet they must survive the save.
|
|
70
|
+
expect(input.environmentRefs).toEqual([
|
|
71
|
+
{ org: "acme", slug: "prod", kind: ApiResourceKind.environment },
|
|
72
|
+
]);
|
|
73
|
+
// Form-owned fields round-trip from the edit state.
|
|
74
|
+
expect(input.allowedOrigins).toEqual(["https://embed.acme.example"]);
|
|
75
|
+
expect(input.autoGrantRole).toBe(IamRole.admin);
|
|
76
|
+
expect(input.expiresAt).toBeInstanceOf(Date);
|
|
77
|
+
// Addressing fields for the update pipeline's org+slug lookup.
|
|
78
|
+
expect(input.org).toBe("acme");
|
|
79
|
+
expect(input.slug).toBe("embed-client");
|
|
80
|
+
expect(input.name).toBe("Embed Client");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
type RunConfig,
|
|
38
38
|
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/invocation_pb";
|
|
39
39
|
import { Harness } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
|
|
40
|
-
import { ServiceTier } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
40
|
+
import { ServiceTier, ThinkingMode } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
41
41
|
import type { WorkspaceEntry } from "@stigmer/protos/ai/stigmer/agentic/session/v1/workspace_pb";
|
|
42
42
|
import {
|
|
43
43
|
cadenceToCron,
|
|
@@ -60,6 +60,11 @@ import {
|
|
|
60
60
|
toProtoServiceTier,
|
|
61
61
|
type ServiceTierOption,
|
|
62
62
|
} from "../models/service-tier.js";
|
|
63
|
+
import {
|
|
64
|
+
fromProtoThinkingMode,
|
|
65
|
+
toProtoThinkingMode,
|
|
66
|
+
type ThinkingModeOption,
|
|
67
|
+
} from "../models/thinking-mode.js";
|
|
63
68
|
import { deriveScheduleState, formatNextFire } from "./scheduleState.js";
|
|
64
69
|
import {
|
|
65
70
|
ScheduleRunsCompactList,
|
|
@@ -599,10 +604,10 @@ export function ScheduleDetailView({
|
|
|
599
604
|
{editable && target ? (
|
|
600
605
|
<EngineModelInlineEditor
|
|
601
606
|
invocation={target}
|
|
602
|
-
onSave={(harness, modelName, serviceTier) =>
|
|
607
|
+
onSave={(harness, modelName, serviceTier, thinkingMode) =>
|
|
603
608
|
saveSpecField("engine-model", (s) => {
|
|
604
609
|
if (s.target.case === "agent") {
|
|
605
|
-
applyEngineModel(s.target.value, harness, modelName, serviceTier);
|
|
610
|
+
applyEngineModel(s.target.value, harness, modelName, serviceTier, thinkingMode);
|
|
606
611
|
}
|
|
607
612
|
})
|
|
608
613
|
}
|
|
@@ -1016,6 +1021,7 @@ function EngineModelInlineEditor({
|
|
|
1016
1021
|
harness: Harness,
|
|
1017
1022
|
modelName: string,
|
|
1018
1023
|
serviceTier: ServiceTierOption,
|
|
1024
|
+
thinkingMode: ThinkingModeOption,
|
|
1019
1025
|
) => Promise<boolean>;
|
|
1020
1026
|
readonly isSaving: boolean;
|
|
1021
1027
|
readonly error?: string;
|
|
@@ -1024,6 +1030,7 @@ function EngineModelInlineEditor({
|
|
|
1024
1030
|
const [modelName, setModelName] = useState("");
|
|
1025
1031
|
const [harness, setHarness] = useState<HarnessOption>("cursor");
|
|
1026
1032
|
const [serviceTier, setServiceTier] = useState<ServiceTierOption>("standard");
|
|
1033
|
+
const [thinkingMode, setThinkingMode] = useState<ThinkingModeOption>("disabled");
|
|
1027
1034
|
|
|
1028
1035
|
const startEdit = () => {
|
|
1029
1036
|
setModelName(invocation.runConfig?.modelName ?? "");
|
|
@@ -1035,6 +1042,9 @@ function EngineModelInlineEditor({
|
|
|
1035
1042
|
setServiceTier(
|
|
1036
1043
|
fromProtoServiceTier(invocation.runConfig?.serviceTier) ?? "standard",
|
|
1037
1044
|
);
|
|
1045
|
+
setThinkingMode(
|
|
1046
|
+
fromProtoThinkingMode(invocation.runConfig?.thinkingMode) ?? "disabled",
|
|
1047
|
+
);
|
|
1038
1048
|
setIsEditing(true);
|
|
1039
1049
|
};
|
|
1040
1050
|
|
|
@@ -1056,6 +1066,8 @@ function EngineModelInlineEditor({
|
|
|
1056
1066
|
onHarnessChange={setHarness}
|
|
1057
1067
|
serviceTier={serviceTier}
|
|
1058
1068
|
onServiceTierChange={setServiceTier}
|
|
1069
|
+
thinkingMode={thinkingMode}
|
|
1070
|
+
onThinkingModeChange={setThinkingMode}
|
|
1059
1071
|
placeholderLabel="Platform default"
|
|
1060
1072
|
disabled={isSaving}
|
|
1061
1073
|
/>
|
|
@@ -1065,6 +1077,7 @@ function EngineModelInlineEditor({
|
|
|
1065
1077
|
onClick={() => {
|
|
1066
1078
|
setModelName("");
|
|
1067
1079
|
setServiceTier("standard");
|
|
1080
|
+
setThinkingMode("disabled");
|
|
1068
1081
|
}}
|
|
1069
1082
|
disabled={isSaving}
|
|
1070
1083
|
className="stg:rounded-md stg:px-2 stg:py-1 stg:text-[0.65rem] stg:text-muted-foreground stg:hover:text-foreground stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-ring stg:disabled:pointer-events-none stg:disabled:opacity-50"
|
|
@@ -1084,6 +1097,7 @@ function EngineModelInlineEditor({
|
|
|
1084
1097
|
modelName !== "" ? toProtoHarness(harness) : Harness.UNSPECIFIED,
|
|
1085
1098
|
modelName,
|
|
1086
1099
|
serviceTier,
|
|
1100
|
+
thinkingMode,
|
|
1087
1101
|
);
|
|
1088
1102
|
if (ok) setIsEditing(false);
|
|
1089
1103
|
}}
|
|
@@ -1169,14 +1183,17 @@ function applyEngineModel(
|
|
|
1169
1183
|
harness: Harness,
|
|
1170
1184
|
modelName: string,
|
|
1171
1185
|
serviceTier: ServiceTierOption,
|
|
1186
|
+
thinkingMode: ThinkingModeOption,
|
|
1172
1187
|
): void {
|
|
1173
1188
|
invocation.harness = harness;
|
|
1174
1189
|
invocation.runConfig = normalizeRunConfig(
|
|
1175
1190
|
modelName,
|
|
1176
1191
|
invocation.runConfig?.maxCostUsd ?? 0,
|
|
1177
1192
|
invocation.runConfig?.maxToolRounds ?? 0,
|
|
1178
|
-
// The
|
|
1193
|
+
// The variant attributes ride the model choice: no model, no tier and
|
|
1194
|
+
// no thinking (#357/#772).
|
|
1179
1195
|
modelName.trim() !== "" ? serviceTier : "standard",
|
|
1196
|
+
modelName.trim() !== "" ? thinkingMode : "disabled",
|
|
1180
1197
|
);
|
|
1181
1198
|
}
|
|
1182
1199
|
|
|
@@ -1190,6 +1207,7 @@ function applyBudget(
|
|
|
1190
1207
|
maxCostUsd ?? 0,
|
|
1191
1208
|
invocation.runConfig?.maxToolRounds ?? 0,
|
|
1192
1209
|
fromProtoServiceTier(invocation.runConfig?.serviceTier) ?? "standard",
|
|
1210
|
+
fromProtoThinkingMode(invocation.runConfig?.thinkingMode) ?? "disabled",
|
|
1193
1211
|
);
|
|
1194
1212
|
}
|
|
1195
1213
|
|
|
@@ -1198,12 +1216,14 @@ function normalizeRunConfig(
|
|
|
1198
1216
|
maxCostUsd: number,
|
|
1199
1217
|
maxToolRounds: number,
|
|
1200
1218
|
serviceTier: ServiceTierOption,
|
|
1219
|
+
thinkingMode: ThinkingModeOption,
|
|
1201
1220
|
): RunConfig | undefined {
|
|
1202
1221
|
const fields: {
|
|
1203
1222
|
modelName?: string;
|
|
1204
1223
|
maxCostUsd?: number;
|
|
1205
1224
|
maxToolRounds?: number;
|
|
1206
1225
|
serviceTier?: ServiceTier;
|
|
1226
|
+
thinkingMode?: ThinkingMode;
|
|
1207
1227
|
} = {};
|
|
1208
1228
|
if (modelName.trim() !== "") fields.modelName = modelName.trim();
|
|
1209
1229
|
if (maxCostUsd > 0) fields.maxCostUsd = maxCostUsd;
|
|
@@ -1211,6 +1231,8 @@ function normalizeRunConfig(
|
|
|
1211
1231
|
// Only an active fast choice is carried — an untouched tier stays
|
|
1212
1232
|
// absent, preserving the unspecified-vs-explicit ledger distinction (#357).
|
|
1213
1233
|
if (serviceTier === "fast") fields.serviceTier = toProtoServiceTier(serviceTier);
|
|
1234
|
+
// The tier's #772 twin: only an active enabled choice is carried.
|
|
1235
|
+
if (thinkingMode === "enabled") fields.thinkingMode = toProtoThinkingMode(thinkingMode);
|
|
1214
1236
|
|
|
1215
1237
|
return Object.keys(fields).length > 0
|
|
1216
1238
|
? create(RunConfigSchema, fields)
|
|
@@ -1429,6 +1451,9 @@ function EngineModelSummary({
|
|
|
1429
1451
|
if (invocation?.runConfig?.serviceTier === ServiceTier.FAST) {
|
|
1430
1452
|
parts.push("Fast tier");
|
|
1431
1453
|
}
|
|
1454
|
+
if (invocation?.runConfig?.thinkingMode === ThinkingMode.ENABLED) {
|
|
1455
|
+
parts.push("Thinking");
|
|
1456
|
+
}
|
|
1432
1457
|
return <span className="stg:text-sm stg:text-foreground">{parts.join(" · ")}</span>;
|
|
1433
1458
|
}
|
|
1434
1459
|
|
|
@@ -16,6 +16,7 @@ import { useGitHubConnection } from "../github/useGitHubConnection.js";
|
|
|
16
16
|
import { ModelSelector } from "../models/ModelSelector.js";
|
|
17
17
|
import { toProtoHarness, type HarnessOption } from "../models/harness.js";
|
|
18
18
|
import { toProtoServiceTier, type ServiceTierOption } from "../models/service-tier.js";
|
|
19
|
+
import { toProtoThinkingMode, type ThinkingModeOption } from "../models/thinking-mode.js";
|
|
19
20
|
import { Switch } from "../switch/Switch.js";
|
|
20
21
|
import { useStigmerPortalContainer } from "../portal-container.js";
|
|
21
22
|
import { useWorkspaceEntries } from "../workspace/useWorkspaceEntries.js";
|
|
@@ -116,6 +117,8 @@ export function ScheduleForm({
|
|
|
116
117
|
// is picked. No model pinned = no tier pinned (fast requires a model,
|
|
117
118
|
// and the form's reset button clears both).
|
|
118
119
|
const [serviceTier, setServiceTier] = useState<ServiceTierOption>("standard");
|
|
120
|
+
// Thinking mode: the tier's #772 twin, capability-scoped the same way.
|
|
121
|
+
const [thinkingMode, setThinkingMode] = useState<ThinkingModeOption>("disabled");
|
|
119
122
|
|
|
120
123
|
const [budgetUsd, setBudgetUsd] = useState("");
|
|
121
124
|
|
|
@@ -152,7 +155,7 @@ export function ScheduleForm({
|
|
|
152
155
|
if (!canSubmit || !agentRef) return;
|
|
153
156
|
|
|
154
157
|
clearError();
|
|
155
|
-
const runConfig = buildRunConfig(modelName, budgetUsd, serviceTier);
|
|
158
|
+
const runConfig = buildRunConfig(modelName, budgetUsd, serviceTier, thinkingMode);
|
|
156
159
|
try {
|
|
157
160
|
const schedule = await create({
|
|
158
161
|
name: trimmedName,
|
|
@@ -195,6 +198,7 @@ export function ScheduleForm({
|
|
|
195
198
|
modelName,
|
|
196
199
|
modelHarness,
|
|
197
200
|
serviceTier,
|
|
201
|
+
thinkingMode,
|
|
198
202
|
budgetUsd,
|
|
199
203
|
workspace,
|
|
200
204
|
onComplete,
|
|
@@ -362,6 +366,8 @@ export function ScheduleForm({
|
|
|
362
366
|
onHarnessChange={setModelHarness}
|
|
363
367
|
serviceTier={serviceTier}
|
|
364
368
|
onServiceTierChange={setServiceTier}
|
|
369
|
+
thinkingMode={thinkingMode}
|
|
370
|
+
onThinkingModeChange={setThinkingMode}
|
|
365
371
|
placeholderLabel="Platform default"
|
|
366
372
|
disabled={isCreating}
|
|
367
373
|
/>
|
|
@@ -371,6 +377,7 @@ export function ScheduleForm({
|
|
|
371
377
|
onClick={() => {
|
|
372
378
|
setModelName("");
|
|
373
379
|
setServiceTier("standard");
|
|
380
|
+
setThinkingMode("disabled");
|
|
374
381
|
}}
|
|
375
382
|
disabled={isCreating}
|
|
376
383
|
className={cn(
|
|
@@ -514,6 +521,7 @@ function buildRunConfig(
|
|
|
514
521
|
modelName: string,
|
|
515
522
|
budgetUsd: string,
|
|
516
523
|
serviceTier: ServiceTierOption,
|
|
524
|
+
thinkingMode: ThinkingModeOption,
|
|
517
525
|
): RunConfigInput | undefined {
|
|
518
526
|
const model = modelName.trim();
|
|
519
527
|
const cost = Number.parseFloat(budgetUsd);
|
|
@@ -528,6 +536,11 @@ function buildRunConfig(
|
|
|
528
536
|
if (serviceTier === "fast" && model !== "") {
|
|
529
537
|
config.serviceTier = toProtoServiceTier(serviceTier);
|
|
530
538
|
}
|
|
539
|
+
// The tier's #772 twin: thinking is a per-model capability, same
|
|
540
|
+
// only-explicit-enabled + requires-a-model contract.
|
|
541
|
+
if (thinkingMode === "enabled" && model !== "") {
|
|
542
|
+
config.thinkingMode = toProtoThinkingMode(thinkingMode);
|
|
543
|
+
}
|
|
531
544
|
|
|
532
545
|
return Object.keys(config).length > 0 ? config : undefined;
|
|
533
546
|
}
|
|
@@ -6,7 +6,7 @@ import type { UseGitHubConnectionReturn } from "../github/useGitHubConnection.js
|
|
|
6
6
|
import type { WorkspaceFileLister } from "../workspace/WorkspaceFileLister.js";
|
|
7
7
|
import type { WorkspaceFileReader } from "../workspace/WorkspaceFileReader.js";
|
|
8
8
|
import type { WorkspaceContentSearcher } from "../workspace/WorkspaceContentSearcher.js";
|
|
9
|
-
import type { InteractionModeOption } from "../composer/index.js";
|
|
9
|
+
import type { ComposerAutoApproveProps, InteractionModeOption } from "../composer/index.js";
|
|
10
10
|
import { SessionComposer } from "../composer/index.js";
|
|
11
11
|
import type { HarnessOption } from "../models/harness.js";
|
|
12
12
|
import type { AccountExecutionDefaults } from "../identity-account/useAccountExecutionDefaults.js";
|
|
@@ -299,6 +299,19 @@ export function NewSessionViewer({
|
|
|
299
299
|
// SessionViewer derives (DD-016), gating the chip and the region together.
|
|
300
300
|
const panelEnabled = panelMode !== "none" && !isGuest;
|
|
301
301
|
|
|
302
|
+
// Always-visible auto-approve toggle (#816), present before the first
|
|
303
|
+
// message is ever typed — the walk-away user arms it here and the create
|
|
304
|
+
// carries spec.auto_approve_all server-side. Guests never get it (the
|
|
305
|
+
// operator-consent reasoning as the #302 host default). Memoized per
|
|
306
|
+
// DD-010 — the composer is memo'd and an inline object would defeat it.
|
|
307
|
+
const autoApprove = useMemo<ComposerAutoApproveProps | undefined>(
|
|
308
|
+
() =>
|
|
309
|
+
isGuest
|
|
310
|
+
? undefined
|
|
311
|
+
: { armed: flow.autoApproveAll, onChange: flow.setAutoApproveAll },
|
|
312
|
+
[isGuest, flow.autoApproveAll, flow.setAutoApproveAll],
|
|
313
|
+
);
|
|
314
|
+
|
|
302
315
|
// Guest agent binding is host configuration, not a picker interaction:
|
|
303
316
|
// the composer's agent machinery (picker, env-collection, personal
|
|
304
317
|
// environments — all org reads a guest token cannot make) stays fully
|
|
@@ -452,6 +465,7 @@ export function NewSessionViewer({
|
|
|
452
465
|
onInteractionModeChange={setInteractionMode}
|
|
453
466
|
showInteractionModePicker={!isGuest}
|
|
454
467
|
showModelSelector={modelSelectorVisible}
|
|
468
|
+
autoApprove={autoApprove}
|
|
455
469
|
enableAttachments={!isGuest}
|
|
456
470
|
defaultModelId={flow.modelId}
|
|
457
471
|
onModelChange={flow.setModelId}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
WorkspaceSurface,
|
|
18
18
|
type SurfaceVirtualDocument,
|
|
19
19
|
} from "../workspace/WorkspaceSurface.js";
|
|
20
|
-
import type { InteractionModeOption, SessionComposerHandle, SessionComposerSubmitContext } from "../composer/index.js";
|
|
20
|
+
import type { ComposerAutoApproveProps, InteractionModeOption, SessionComposerHandle, SessionComposerSubmitContext } from "../composer/index.js";
|
|
21
21
|
import type { ApplyResourceResult } from "../library/useApplyResource.js";
|
|
22
22
|
import { SessionViewerLayout } from "./SessionViewerLayout.js";
|
|
23
23
|
import { useWorkspaceEditors, isVirtualEntryId } from "../internal/store/index.js";
|
|
@@ -51,6 +51,7 @@ import { ArtifactDocument } from "../execution/ArtifactDocument.js";
|
|
|
51
51
|
import { useSessionRailViews } from "./useSessionRailViews.js";
|
|
52
52
|
import { useSessionPanel, type SessionPanelController } from "./useSessionPanel.js";
|
|
53
53
|
import { SessionPanelChip } from "./SessionPanelChip.js";
|
|
54
|
+
import { TranscriptExportMenu } from "./TranscriptExportMenu.js";
|
|
54
55
|
import { useSessionWriteBacks } from "./useSessionWriteBacks.js";
|
|
55
56
|
import { useWorkspaceReadRefs } from "./useWorkspaceReadRefs.js";
|
|
56
57
|
import {
|
|
@@ -293,6 +294,19 @@ export interface SessionViewerProps {
|
|
|
293
294
|
* unopinionated about Console auth (DD-004).
|
|
294
295
|
*/
|
|
295
296
|
readonly headerActions?: ReactNode;
|
|
297
|
+
/**
|
|
298
|
+
* Whether the viewer renders its built-in whole-conversation export
|
|
299
|
+
* control ({@link TranscriptExportMenu}) in the header corner
|
|
300
|
+
* (stigmer/stigmer#814). Default ON — a deliberate DD-011 divergence,
|
|
301
|
+
* owner-ratified: wherever a conversation can be viewed its transcript
|
|
302
|
+
* should be one click away, the control is an additive header button (no
|
|
303
|
+
* rendering-strategy or DOM-structure change to existing content), and
|
|
304
|
+
* the export carries only what the current caller can already read.
|
|
305
|
+
* Hosts that own their export surface (or inert demo scenes) opt out.
|
|
306
|
+
*
|
|
307
|
+
* @default true
|
|
308
|
+
*/
|
|
309
|
+
readonly transcriptExport?: boolean;
|
|
296
310
|
/**
|
|
297
311
|
* Host-injected access management control (e.g. the Console's
|
|
298
312
|
* `ManageAccessButton` with its own permission gating). Rendered inside
|
|
@@ -374,6 +388,7 @@ export function SessionViewer({
|
|
|
374
388
|
panelOpen,
|
|
375
389
|
onPanelOpenChange,
|
|
376
390
|
headerActions,
|
|
391
|
+
transcriptExport = true,
|
|
377
392
|
accessSlot,
|
|
378
393
|
onApplied,
|
|
379
394
|
threadSlots,
|
|
@@ -646,13 +661,23 @@ export function SessionViewer({
|
|
|
646
661
|
return (
|
|
647
662
|
<SessionViewerLayout
|
|
648
663
|
className={className}
|
|
649
|
-
// Top-right controls: host actions + the
|
|
650
|
-
// panel's always-mounted toggle; while
|
|
651
|
-
// pending-item count. Execution status
|
|
652
|
-
// chrome — the thread itself communicates
|
|
653
|
-
// a panel (guests — session configuration
|
|
654
|
-
// — or a host's panel="none") has no
|
|
655
|
-
|
|
664
|
+
// Top-right controls: host actions + the built-in transcript export +
|
|
665
|
+
// the panel chip. The chip is the panel's always-mounted toggle; while
|
|
666
|
+
// collapsed it carries only the pending-item count. Execution status
|
|
667
|
+
// is never surfaced as header chrome — the thread itself communicates
|
|
668
|
+
// run state. A viewer without a panel (guests — session configuration
|
|
669
|
+
// is not a visitor's business — or a host's panel="none") has no
|
|
670
|
+
// toggle: it is simply absent.
|
|
671
|
+
headerActions={
|
|
672
|
+
transcriptExport ? (
|
|
673
|
+
<>
|
|
674
|
+
{headerActions}
|
|
675
|
+
<TranscriptExportMenu sessionId={sessionId} />
|
|
676
|
+
</>
|
|
677
|
+
) : (
|
|
678
|
+
headerActions
|
|
679
|
+
)
|
|
680
|
+
}
|
|
656
681
|
chip={
|
|
657
682
|
panelEnabled ? (
|
|
658
683
|
<SessionPanelChip
|
|
@@ -824,6 +849,20 @@ const ConversationColumn = memo(function ConversationColumn({
|
|
|
824
849
|
void conv.stop();
|
|
825
850
|
}, [conv.stop]);
|
|
826
851
|
|
|
852
|
+
// Always-visible auto-approve toggle (#816), replacing the old armed-only
|
|
853
|
+
// indicator banner: the walk-away user needs a way ON before any gate
|
|
854
|
+
// exists, not just a way off after one. Guests never get it (they don't
|
|
855
|
+
// inherit the host default and this is the operator's consent surface);
|
|
856
|
+
// observers have no composer at all. Memoized per DD-010 — the composer
|
|
857
|
+
// is memo'd and an inline object would defeat it.
|
|
858
|
+
const autoApprove = useMemo<ComposerAutoApproveProps | undefined>(
|
|
859
|
+
() =>
|
|
860
|
+
isGuest
|
|
861
|
+
? undefined
|
|
862
|
+
: { armed: flow.autoApproveAll, onChange: flow.setAutoApproveAll },
|
|
863
|
+
[isGuest, flow.autoApproveAll, flow.setAutoApproveAll],
|
|
864
|
+
);
|
|
865
|
+
|
|
827
866
|
// Edit-and-resubmit: stop the in-flight turn, pre-fill the composer with
|
|
828
867
|
// the original text, and remember which execution is being edited. The
|
|
829
868
|
// append-only execution log is never rewritten — submitting while editing
|
|
@@ -948,9 +987,6 @@ const ConversationColumn = memo(function ConversationColumn({
|
|
|
948
987
|
/>
|
|
949
988
|
)}
|
|
950
989
|
{!isObserver && sendError && <SendErrorBanner error={sendError} />}
|
|
951
|
-
{!isObserver && flow.autoApproveAll && (
|
|
952
|
-
<AutoApproveIndicator onTurnOff={() => flow.setAutoApproveAll(false)} />
|
|
953
|
-
)}
|
|
954
990
|
{/* Pending file reviews dock here — pinned above the composer so the
|
|
955
991
|
decision the agent is blocked on can never scroll out of view. The
|
|
956
992
|
thread renders only observational rows (badges) and read-only
|
|
@@ -992,6 +1028,7 @@ const ConversationColumn = memo(function ConversationColumn({
|
|
|
992
1028
|
onInteractionModeChange={setInteractionMode}
|
|
993
1029
|
showInteractionModePicker={!isGuest}
|
|
994
1030
|
showModelSelector={modelSelectorVisible}
|
|
1031
|
+
autoApprove={autoApprove}
|
|
995
1032
|
enableAttachments={!isGuest}
|
|
996
1033
|
workspace={isGuest ? undefined : flow.workspace}
|
|
997
1034
|
gitHubConnection={isGuest ? undefined : gitHubConnection}
|
|
@@ -1386,36 +1423,6 @@ function SessionStarting() {
|
|
|
1386
1423
|
);
|
|
1387
1424
|
}
|
|
1388
1425
|
|
|
1389
|
-
/**
|
|
1390
|
-
* Low-weight, always-visible indicator shown while the session-scoped
|
|
1391
|
-
* auto-approve preference is active. The "Turn off" control reverts the
|
|
1392
|
-
* preference in one click — the safety affordance for "Approve & don't ask
|
|
1393
|
-
* again". Visible from the first render when the host pre-armed the
|
|
1394
|
-
* preference via `StigmerProvider`'s `approvalDefaults` (#302); otherwise
|
|
1395
|
-
* nothing about approvals appears until the user opts in at a gate. Either
|
|
1396
|
-
* way the user keeps the last word.
|
|
1397
|
-
*/
|
|
1398
|
-
function AutoApproveIndicator({ onTurnOff }: { onTurnOff: () => void }) {
|
|
1399
|
-
return (
|
|
1400
|
-
<div
|
|
1401
|
-
role="status"
|
|
1402
|
-
className="stg:flex stg:items-center stg:gap-2 stg:border-t stg:border-border-muted stg:px-4 stg:py-1.5 stg:text-xs stg:text-muted-foreground"
|
|
1403
|
-
>
|
|
1404
|
-
<ShieldCheckIcon />
|
|
1405
|
-
<span className="stg:min-w-0 stg:flex-1 stg:truncate">
|
|
1406
|
-
Auto-approving tool calls for this session
|
|
1407
|
-
</span>
|
|
1408
|
-
<button
|
|
1409
|
-
type="button"
|
|
1410
|
-
onClick={onTurnOff}
|
|
1411
|
-
className="stg:shrink-0 stg:rounded stg:font-medium stg:text-foreground stg:underline-offset-2 stg:hover:underline stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-ring"
|
|
1412
|
-
>
|
|
1413
|
-
Turn off
|
|
1414
|
-
</button>
|
|
1415
|
-
</div>
|
|
1416
|
-
);
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
1426
|
/**
|
|
1420
1427
|
* The plan document tab's empty state — reachable only when a streaming plan
|
|
1421
1428
|
* auto-opened the tab and its turn then ended without publishing (stopped or
|
|
@@ -1616,11 +1623,3 @@ function LoaderIcon() {
|
|
|
1616
1623
|
);
|
|
1617
1624
|
}
|
|
1618
1625
|
|
|
1619
|
-
function ShieldCheckIcon() {
|
|
1620
|
-
return (
|
|
1621
|
-
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="stg:shrink-0 stg:text-success" aria-hidden="true">
|
|
1622
|
-
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
|
|
1623
|
-
<path d="m9 12 2 2 4-4" />
|
|
1624
|
-
</svg>
|
|
1625
|
-
);
|
|
1626
|
-
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Copy, Download, FileJson, FileText } from "lucide-react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { ActionMenu } from "../action-menu/ActionMenu.js";
|
|
6
|
+
import { SpinnerIcon } from "../internal/SpinnerIcon.js";
|
|
7
|
+
import {
|
|
8
|
+
useExportTranscript,
|
|
9
|
+
type UseExportTranscriptOptions,
|
|
10
|
+
} from "./useExportTranscript.js";
|
|
11
|
+
|
|
12
|
+
/** Props for {@link TranscriptExportMenu}. */
|
|
13
|
+
export interface TranscriptExportMenuProps extends UseExportTranscriptOptions {
|
|
14
|
+
/** The session whose conversation is exported. */
|
|
15
|
+
readonly sessionId: string | null;
|
|
16
|
+
/** Additional CSS classes for the trigger button. */
|
|
17
|
+
readonly className?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The whole-conversation export control (stigmer/stigmer#814): a quiet
|
|
22
|
+
* icon-button menu offering **Copy transcript**, **Download Markdown**, and
|
|
23
|
+
* **Download JSON**, powered by {@link useExportTranscript} — the canonical
|
|
24
|
+
* transcript assembly, thinking and resolved tool outputs included.
|
|
25
|
+
*
|
|
26
|
+
* `SessionViewer` renders it in its header corner by default; it is exported
|
|
27
|
+
* so hosts can mount the same control on any other surface a conversation is
|
|
28
|
+
* reachable from (a session list's row menu, a custom header).
|
|
29
|
+
*
|
|
30
|
+
* The export serializes exactly what the viewer already shows the current
|
|
31
|
+
* caller — the RPCs behind it enforce the same `can_view` permission — so
|
|
32
|
+
* offering it to every audience adds convenience, not exposure.
|
|
33
|
+
*/
|
|
34
|
+
export function TranscriptExportMenu({
|
|
35
|
+
sessionId,
|
|
36
|
+
includeSuperseded,
|
|
37
|
+
className,
|
|
38
|
+
}: TranscriptExportMenuProps) {
|
|
39
|
+
const exporter = useExportTranscript(sessionId, { includeSuperseded });
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<ActionMenu>
|
|
43
|
+
<ActionMenu.Trigger
|
|
44
|
+
aria-label="Export transcript"
|
|
45
|
+
className={cn("stg:size-7", className)}
|
|
46
|
+
>
|
|
47
|
+
{exporter.isExporting ? (
|
|
48
|
+
<SpinnerIcon size={14} />
|
|
49
|
+
) : (
|
|
50
|
+
<Download className="stg:size-3.5" aria-hidden="true" />
|
|
51
|
+
)}
|
|
52
|
+
</ActionMenu.Trigger>
|
|
53
|
+
<ActionMenu.Content>
|
|
54
|
+
<ActionMenu.Item
|
|
55
|
+
icon={<Copy className="stg:size-3.5" aria-hidden="true" />}
|
|
56
|
+
onSelect={() => void exporter.copyMarkdown()}
|
|
57
|
+
disabled={exporter.isExporting}
|
|
58
|
+
>
|
|
59
|
+
Copy transcript
|
|
60
|
+
</ActionMenu.Item>
|
|
61
|
+
<ActionMenu.Item
|
|
62
|
+
icon={<FileText className="stg:size-3.5" aria-hidden="true" />}
|
|
63
|
+
onSelect={() => void exporter.downloadMarkdown()}
|
|
64
|
+
disabled={exporter.isExporting}
|
|
65
|
+
>
|
|
66
|
+
Download Markdown
|
|
67
|
+
</ActionMenu.Item>
|
|
68
|
+
<ActionMenu.Item
|
|
69
|
+
icon={<FileJson className="stg:size-3.5" aria-hidden="true" />}
|
|
70
|
+
onSelect={() => void exporter.downloadJson()}
|
|
71
|
+
disabled={exporter.isExporting}
|
|
72
|
+
>
|
|
73
|
+
Download JSON
|
|
74
|
+
</ActionMenu.Item>
|
|
75
|
+
</ActionMenu.Content>
|
|
76
|
+
</ActionMenu>
|
|
77
|
+
);
|
|
78
|
+
}
|