@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
package/src/mcp-server/index.ts
CHANGED
|
@@ -95,8 +95,6 @@ export type { UseCreateMcpServerReturn } from "./useCreateMcpServer.js";
|
|
|
95
95
|
export { useUpdateMcpServer } from "./useUpdateMcpServer.js";
|
|
96
96
|
export type { UseUpdateMcpServerReturn } from "./useUpdateMcpServer.js";
|
|
97
97
|
|
|
98
|
-
export { mcpServerToInput } from "./internal/mcpServerToInput.js";
|
|
99
|
-
|
|
100
98
|
export { McpServerCreationWizard } from "./McpServerCreationWizard.js";
|
|
101
99
|
export type {
|
|
102
100
|
McpServerCreationWizardProps,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/api_pb";
|
|
2
|
-
import type
|
|
3
|
-
import { mcpServerToInput } from "./mcpServerToInput.js";
|
|
2
|
+
import { toMcpServerUpdateInput, type McpServerInput } from "@stigmer/sdk";
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Connection-defining field groups a marketplace definition can drift on.
|
|
@@ -24,9 +23,9 @@ export type DriftFieldId =
|
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
25
|
* Deep equality over the plain-object projections produced by
|
|
27
|
-
* `
|
|
28
|
-
* is not canonical); `undefined` values and absent keys are
|
|
29
|
-
* (the conversion uses `undefined` for proto defaults).
|
|
26
|
+
* `toMcpServerUpdateInput`. Key order is irrelevant (proto map iteration
|
|
27
|
+
* order is not canonical); `undefined` values and absent keys are
|
|
28
|
+
* equivalent (the conversion uses `undefined` for proto defaults).
|
|
30
29
|
*/
|
|
31
30
|
function deepEqual(a: unknown, b: unknown): boolean {
|
|
32
31
|
if (Object.is(a, b)) return true;
|
|
@@ -72,17 +71,18 @@ function functionalEnvShape(env: McpServerInput["env"]) {
|
|
|
72
71
|
* mirrors and reports which connection-defining field groups differ, or
|
|
73
72
|
* `null` when the connection-defining configuration matches.
|
|
74
73
|
*
|
|
75
|
-
* The comparison runs on `
|
|
76
|
-
* protos: that
|
|
77
|
-
* and its
|
|
78
|
-
*
|
|
74
|
+
* The comparison runs on `toMcpServerUpdateInput` projections rather than
|
|
75
|
+
* raw protos: that generated mapper is the SDK's canonical
|
|
76
|
+
* read-modify-write lens, and its completeness is guaranteed by codegen
|
|
77
|
+
* plus the SDK's schema-tripwire fixture suite — so a new spec field
|
|
78
|
+
* cannot silently bypass this comparison either.
|
|
79
79
|
*/
|
|
80
80
|
export function computeDefinitionDrift(
|
|
81
81
|
current: McpServer,
|
|
82
82
|
template: McpServer,
|
|
83
83
|
): readonly DriftFieldId[] | null {
|
|
84
|
-
const cur =
|
|
85
|
-
const tpl =
|
|
84
|
+
const cur = toMcpServerUpdateInput(current);
|
|
85
|
+
const tpl = toMcpServerUpdateInput(template);
|
|
86
86
|
|
|
87
87
|
const changed: DriftFieldId[] = [];
|
|
88
88
|
|
|
@@ -128,21 +128,22 @@ export function computeDefinitionDrift(
|
|
|
128
128
|
*
|
|
129
129
|
* Definition fields — transport, env declarations, auth, and the cosmetic
|
|
130
130
|
* description/icon/tags/repository metadata — come from the template.
|
|
131
|
-
* The user's own choices survive: identity (name/org/slug/labels
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
131
|
+
* The user's own choices survive: identity (name/org/slug/labels/
|
|
132
|
+
* visibility — the backend ignores visibility on update anyway;
|
|
133
|
+
* `updateVisibility` is its dedicated write path), tool enablement, and
|
|
134
|
+
* pinned approval policies.
|
|
135
135
|
*
|
|
136
|
-
* Built by merging two `
|
|
137
|
-
* set is the explicit override list below and nothing can be
|
|
138
|
-
* translation (the
|
|
136
|
+
* Built by merging two `toMcpServerUpdateInput` projections, so the
|
|
137
|
+
* preserved set is the explicit override list below and nothing can be
|
|
138
|
+
* lost in translation (the mapper is generated complete and guarded by
|
|
139
|
+
* the SDK's schema-tripwire fixture suite).
|
|
139
140
|
*/
|
|
140
141
|
export function buildRefreshInput(
|
|
141
142
|
current: McpServer,
|
|
142
143
|
template: McpServer,
|
|
143
144
|
): McpServerInput {
|
|
144
|
-
const cur =
|
|
145
|
-
const tpl =
|
|
145
|
+
const cur = toMcpServerUpdateInput(current);
|
|
146
|
+
const tpl = toMcpServerUpdateInput(template);
|
|
146
147
|
|
|
147
148
|
return {
|
|
148
149
|
...tpl,
|
|
@@ -151,6 +152,7 @@ export function buildRefreshInput(
|
|
|
151
152
|
org: cur.org,
|
|
152
153
|
slug: cur.slug,
|
|
153
154
|
labels: cur.labels,
|
|
155
|
+
visibility: cur.visibility,
|
|
154
156
|
// User policy: never reset by a definition refresh.
|
|
155
157
|
defaultEnabledTools: cur.defaultEnabledTools,
|
|
156
158
|
pinnedToolApprovals: cur.pinnedToolApprovals,
|
|
@@ -23,9 +23,9 @@ export interface UseUpdateMcpServerReturn {
|
|
|
23
23
|
*
|
|
24
24
|
* Wraps `stigmer.mcpServer.update(input)` with loading and error state.
|
|
25
25
|
* The caller must provide a **complete** `McpServerInput` — the backend
|
|
26
|
-
* performs full spec replacement. Use `
|
|
27
|
-
* the input from a fetched server, modify
|
|
28
|
-
* the result here.
|
|
26
|
+
* performs full spec replacement. Use `toMcpServerUpdateInput()` (from
|
|
27
|
+
* `@stigmer/sdk`) to reconstruct the input from a fetched server, modify
|
|
28
|
+
* the desired field, and pass the result here.
|
|
29
29
|
*/
|
|
30
30
|
export function useUpdateMcpServer(): UseUpdateMcpServerReturn {
|
|
31
31
|
const stigmer = useStigmer();
|
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { getUserMessage } from "@stigmer/sdk";
|
|
6
|
+
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
|
7
|
+
import type { Memory } from "@stigmer/protos/ai/stigmer/agentic/memory/v1/api_pb";
|
|
8
|
+
import { MemoryLifecycleState } from "@stigmer/protos/ai/stigmer/agentic/memory/v1/enum_pb";
|
|
9
|
+
import { formatRelativeTime } from "../activity/format-relative-time.js";
|
|
10
|
+
import { SpinnerIcon } from "../internal/SpinnerIcon.js";
|
|
11
|
+
import { formatMemoryProvenance, groupMemoriesByLifecycle } from "./memoryGroups.js";
|
|
12
|
+
import { useConfirmMemory } from "./useConfirmMemory.js";
|
|
13
|
+
import { useDeleteMemory } from "./useDeleteMemory.js";
|
|
14
|
+
import { useMemories } from "./useMemories.js";
|
|
15
|
+
import { useRejectMemory } from "./useRejectMemory.js";
|
|
16
|
+
import { useUpdateMemoryContent } from "./useUpdateMemoryContent.js";
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Public API
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/** Props for {@link MemoryListPanel}. */
|
|
23
|
+
export interface MemoryListPanelProps {
|
|
24
|
+
/** Organization whose memories to show. */
|
|
25
|
+
readonly org: string;
|
|
26
|
+
/** Additional CSS class names for the root container. */
|
|
27
|
+
readonly className?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Reference instant for relative timestamps. Defaults to the live
|
|
30
|
+
* clock. **Deterministic hosts (documentation embeds, video export)
|
|
31
|
+
* must pass a frozen instant.**
|
|
32
|
+
*/
|
|
33
|
+
readonly now?: Date;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Lists everything the platform remembers about the caller in an
|
|
38
|
+
* organization, grouped pending-proposals-first (DD-005 D4): proposals
|
|
39
|
+
* awaiting a decision, then confirmed facts, then rejected proposals
|
|
40
|
+
* kept for audit.
|
|
41
|
+
*
|
|
42
|
+
* Every fact is shown VERBATIM — the exact stored text is what future
|
|
43
|
+
* prompts inject, byte for byte, so the review surface never paraphrases
|
|
44
|
+
* (DD-005 D6). Provenance renders beside each agent-proposed fact.
|
|
45
|
+
* Actions per state: confirm/reject/delete on proposals (reject is
|
|
46
|
+
* one-click — no confirmation dialog), edit/delete on confirmed facts,
|
|
47
|
+
* delete on rejected ones. Delete asks inline; it works in any state.
|
|
48
|
+
*
|
|
49
|
+
* All visual properties flow through `--stgm-*` design tokens.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```tsx
|
|
53
|
+
* <MemoryListPanel org="acme" />
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export function MemoryListPanel({ org, className, now }: MemoryListPanelProps) {
|
|
57
|
+
const { memories, isLoading, error, refetch } = useMemories(org);
|
|
58
|
+
// At most one row is mid-interaction at a time — the ApiKeyListPanel
|
|
59
|
+
// single-confirmation posture, extended with an editing mode.
|
|
60
|
+
const [rowState, setRowState] = useState<
|
|
61
|
+
| { mode: "idle" }
|
|
62
|
+
| { mode: "confirming-delete"; id: string }
|
|
63
|
+
| { mode: "editing"; id: string }
|
|
64
|
+
>({ mode: "idle" });
|
|
65
|
+
|
|
66
|
+
const resetRow = useCallback(() => setRowState({ mode: "idle" }), []);
|
|
67
|
+
const handleChanged = useCallback(() => {
|
|
68
|
+
setRowState({ mode: "idle" });
|
|
69
|
+
refetch();
|
|
70
|
+
}, [refetch]);
|
|
71
|
+
|
|
72
|
+
if (isLoading) {
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
className={cn("stg:space-y-2", className)}
|
|
76
|
+
aria-busy="true"
|
|
77
|
+
aria-label="Loading memories"
|
|
78
|
+
>
|
|
79
|
+
{Array.from({ length: 2 }, (_, i) => (
|
|
80
|
+
<div
|
|
81
|
+
key={i}
|
|
82
|
+
className="stg:bg-muted-subtle stg:h-14 stg:animate-pulse stg:rounded-lg"
|
|
83
|
+
/>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (error) {
|
|
90
|
+
return (
|
|
91
|
+
<p className={cn("stg:text-destructive stg:text-xs", className)} role="alert">
|
|
92
|
+
{getUserMessage(error)}
|
|
93
|
+
</p>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (memories.length === 0) {
|
|
98
|
+
return (
|
|
99
|
+
<p
|
|
100
|
+
className={cn(
|
|
101
|
+
"stg:text-muted-foreground stg:py-4 stg:text-center stg:text-xs",
|
|
102
|
+
className,
|
|
103
|
+
)}
|
|
104
|
+
>
|
|
105
|
+
Nothing remembered yet. When an agent proposes a fact worth
|
|
106
|
+
keeping, it appears here for your decision.
|
|
107
|
+
</p>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const groups = groupMemoriesByLifecycle(memories);
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<div className={cn("stg:space-y-6", className)}>
|
|
115
|
+
{groups.proposed.length > 0 && (
|
|
116
|
+
<MemoryGroup
|
|
117
|
+
heading="Pending proposals"
|
|
118
|
+
description="Facts agents proposed to remember. Nothing is stored in your sessions until you confirm it."
|
|
119
|
+
memories={groups.proposed}
|
|
120
|
+
rowState={rowState}
|
|
121
|
+
setRowState={setRowState}
|
|
122
|
+
onChanged={handleChanged}
|
|
123
|
+
onCancel={resetRow}
|
|
124
|
+
now={now}
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
{groups.confirmed.length > 0 && (
|
|
128
|
+
<MemoryGroup
|
|
129
|
+
heading="Remembered"
|
|
130
|
+
description="Confirmed facts, shared with agents in your future sessions."
|
|
131
|
+
memories={groups.confirmed}
|
|
132
|
+
rowState={rowState}
|
|
133
|
+
setRowState={setRowState}
|
|
134
|
+
onChanged={handleChanged}
|
|
135
|
+
onCancel={resetRow}
|
|
136
|
+
now={now}
|
|
137
|
+
/>
|
|
138
|
+
)}
|
|
139
|
+
{groups.rejected.length > 0 && (
|
|
140
|
+
<MemoryGroup
|
|
141
|
+
heading="Rejected"
|
|
142
|
+
description="Proposals you declined, kept for reference. Delete them to clear the record."
|
|
143
|
+
memories={groups.rejected}
|
|
144
|
+
rowState={rowState}
|
|
145
|
+
setRowState={setRowState}
|
|
146
|
+
onChanged={handleChanged}
|
|
147
|
+
onCancel={resetRow}
|
|
148
|
+
now={now}
|
|
149
|
+
/>
|
|
150
|
+
)}
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Group + row (internal)
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
type RowState =
|
|
160
|
+
| { mode: "idle" }
|
|
161
|
+
| { mode: "confirming-delete"; id: string }
|
|
162
|
+
| { mode: "editing"; id: string };
|
|
163
|
+
|
|
164
|
+
function MemoryGroup({
|
|
165
|
+
heading,
|
|
166
|
+
description,
|
|
167
|
+
memories,
|
|
168
|
+
rowState,
|
|
169
|
+
setRowState,
|
|
170
|
+
onChanged,
|
|
171
|
+
onCancel,
|
|
172
|
+
now,
|
|
173
|
+
}: {
|
|
174
|
+
heading: string;
|
|
175
|
+
description: string;
|
|
176
|
+
memories: readonly Memory[];
|
|
177
|
+
rowState: RowState;
|
|
178
|
+
setRowState: (state: RowState) => void;
|
|
179
|
+
onChanged: () => void;
|
|
180
|
+
onCancel: () => void;
|
|
181
|
+
now?: Date;
|
|
182
|
+
}) {
|
|
183
|
+
return (
|
|
184
|
+
<div>
|
|
185
|
+
<h3 className="stg:text-foreground stg:text-xs stg:font-semibold">
|
|
186
|
+
{heading}
|
|
187
|
+
</h3>
|
|
188
|
+
<p className="stg:text-muted-foreground stg:mb-2 stg:text-xs">
|
|
189
|
+
{description}
|
|
190
|
+
</p>
|
|
191
|
+
<div className="stg:space-y-2" role="list" aria-label={heading}>
|
|
192
|
+
{memories.map((memory) => {
|
|
193
|
+
const id = memory.metadata?.id ?? "";
|
|
194
|
+
return (
|
|
195
|
+
<MemoryRow
|
|
196
|
+
key={id}
|
|
197
|
+
memory={memory}
|
|
198
|
+
now={now}
|
|
199
|
+
mode={
|
|
200
|
+
rowState.mode !== "idle" && rowState.id === id
|
|
201
|
+
? rowState.mode
|
|
202
|
+
: "idle"
|
|
203
|
+
}
|
|
204
|
+
onStartDelete={() => setRowState({ mode: "confirming-delete", id })}
|
|
205
|
+
onStartEdit={() => setRowState({ mode: "editing", id })}
|
|
206
|
+
onCancel={onCancel}
|
|
207
|
+
onChanged={onChanged}
|
|
208
|
+
/>
|
|
209
|
+
);
|
|
210
|
+
})}
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function MemoryRow({
|
|
217
|
+
memory,
|
|
218
|
+
now,
|
|
219
|
+
mode,
|
|
220
|
+
onStartDelete,
|
|
221
|
+
onStartEdit,
|
|
222
|
+
onCancel,
|
|
223
|
+
onChanged,
|
|
224
|
+
}: {
|
|
225
|
+
memory: Memory;
|
|
226
|
+
now?: Date;
|
|
227
|
+
mode: "idle" | "confirming-delete" | "editing";
|
|
228
|
+
onStartDelete: () => void;
|
|
229
|
+
onStartEdit: () => void;
|
|
230
|
+
onCancel: () => void;
|
|
231
|
+
onChanged: () => void;
|
|
232
|
+
}) {
|
|
233
|
+
const id = memory.metadata?.id ?? "";
|
|
234
|
+
const content = memory.spec?.content ?? "";
|
|
235
|
+
const state = memory.status?.lifecycleState;
|
|
236
|
+
const provenance = formatMemoryProvenance(memory);
|
|
237
|
+
const stateChangedAt = memory.status?.stateChangedAt;
|
|
238
|
+
|
|
239
|
+
const { confirmMemory, isConfirming, error: confirmError } = useConfirmMemory();
|
|
240
|
+
const { rejectMemory, isRejecting, error: rejectError } = useRejectMemory();
|
|
241
|
+
const { deleteMemory, isDeleting, error: deleteError } = useDeleteMemory();
|
|
242
|
+
|
|
243
|
+
const actionError = confirmError ?? rejectError ?? deleteError;
|
|
244
|
+
const isBusy = isConfirming || isRejecting || isDeleting;
|
|
245
|
+
|
|
246
|
+
const act = useCallback(
|
|
247
|
+
async (action: (id: string) => Promise<Memory>) => {
|
|
248
|
+
try {
|
|
249
|
+
await action(id);
|
|
250
|
+
onChanged();
|
|
251
|
+
} catch {
|
|
252
|
+
// error state is surfaced via the hooks
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
[id, onChanged],
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
if (mode === "editing") {
|
|
259
|
+
return (
|
|
260
|
+
<MemoryEditRow memory={memory} onCancel={onCancel} onSaved={onChanged} />
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (mode === "confirming-delete") {
|
|
265
|
+
return (
|
|
266
|
+
<div
|
|
267
|
+
role="listitem"
|
|
268
|
+
className="stg:flex stg:items-center stg:justify-between stg:gap-3 stg:rounded-lg stg:border stg:border-destructive/30 stg:bg-destructive-subtle stg:px-3 stg:py-2.5"
|
|
269
|
+
>
|
|
270
|
+
<div className="stg:min-w-0 stg:flex-1">
|
|
271
|
+
<p className="stg:text-xs stg:text-foreground">
|
|
272
|
+
Forget this permanently? <q className="stg:italic">{content}</q>
|
|
273
|
+
</p>
|
|
274
|
+
{deleteError && (
|
|
275
|
+
<p className="stg:mt-0.5 stg:text-[0.65rem] stg:text-destructive">
|
|
276
|
+
{getUserMessage(deleteError)}
|
|
277
|
+
</p>
|
|
278
|
+
)}
|
|
279
|
+
</div>
|
|
280
|
+
<div className="stg:flex stg:shrink-0 stg:items-center stg:gap-1.5">
|
|
281
|
+
<button
|
|
282
|
+
type="button"
|
|
283
|
+
onClick={() => void act(deleteMemory)}
|
|
284
|
+
disabled={isDeleting}
|
|
285
|
+
className={cn(
|
|
286
|
+
"stg:inline-flex stg:items-center stg:gap-1 stg:rounded-md stg:px-2.5 stg:py-1 stg:text-xs stg:font-medium",
|
|
287
|
+
"stg:bg-destructive stg:text-destructive-foreground stg:hover:bg-destructive-hover",
|
|
288
|
+
"stg:disabled:pointer-events-none stg:disabled:opacity-50",
|
|
289
|
+
)}
|
|
290
|
+
>
|
|
291
|
+
{isDeleting && <SpinnerIcon size={12} />}
|
|
292
|
+
Delete
|
|
293
|
+
</button>
|
|
294
|
+
<button
|
|
295
|
+
type="button"
|
|
296
|
+
onClick={onCancel}
|
|
297
|
+
disabled={isDeleting}
|
|
298
|
+
className={cn(
|
|
299
|
+
"stg:rounded-md stg:px-2.5 stg:py-1 stg:text-xs",
|
|
300
|
+
"stg:text-muted-foreground stg:hover:text-foreground stg:hover:bg-accent-hover",
|
|
301
|
+
"stg:disabled:pointer-events-none stg:disabled:opacity-50",
|
|
302
|
+
)}
|
|
303
|
+
>
|
|
304
|
+
Cancel
|
|
305
|
+
</button>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<div
|
|
313
|
+
role="listitem"
|
|
314
|
+
className="stg:rounded-lg stg:border stg:border-border-muted stg:px-3 stg:py-2.5 stg:hover:border-border stg:transition-colors"
|
|
315
|
+
>
|
|
316
|
+
<div className="stg:flex stg:items-start stg:justify-between stg:gap-3">
|
|
317
|
+
<div className="stg:min-w-0 stg:flex-1">
|
|
318
|
+
{/* The exact stored text, verbatim — what is confirmed is what
|
|
319
|
+
future prompts inject, byte for byte (DD-005 D6). */}
|
|
320
|
+
<p className="stg:text-sm stg:text-foreground stg:whitespace-pre-wrap">
|
|
321
|
+
{content}
|
|
322
|
+
</p>
|
|
323
|
+
<p className="stg:mt-1 stg:text-xs stg:text-muted-foreground">
|
|
324
|
+
{provenance ? `${provenance} · ` : ""}
|
|
325
|
+
{formatStateStamp(state, stateChangedAt ? timestampDate(stateChangedAt) : undefined, now)}
|
|
326
|
+
</p>
|
|
327
|
+
{actionError && (
|
|
328
|
+
<p className="stg:mt-0.5 stg:text-[0.65rem] stg:text-destructive" role="alert">
|
|
329
|
+
{getUserMessage(actionError)}
|
|
330
|
+
</p>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<div className="stg:flex stg:shrink-0 stg:items-center stg:gap-1.5">
|
|
335
|
+
{state === MemoryLifecycleState.lifecycle_state_confirmed && (
|
|
336
|
+
<RowButton label={`Edit memory: ${content}`} onClick={onStartEdit} disabled={isBusy}>
|
|
337
|
+
Edit
|
|
338
|
+
</RowButton>
|
|
339
|
+
)}
|
|
340
|
+
{isProposed(state) && (
|
|
341
|
+
<>
|
|
342
|
+
<RowButton
|
|
343
|
+
label={`Confirm memory: ${content}`}
|
|
344
|
+
onClick={() => void act(confirmMemory)}
|
|
345
|
+
disabled={isBusy}
|
|
346
|
+
emphasis="primary"
|
|
347
|
+
>
|
|
348
|
+
{isConfirming && <SpinnerIcon size={12} />}
|
|
349
|
+
Confirm
|
|
350
|
+
</RowButton>
|
|
351
|
+
<RowButton
|
|
352
|
+
label={`Reject memory: ${content}`}
|
|
353
|
+
onClick={() => void act(rejectMemory)}
|
|
354
|
+
disabled={isBusy}
|
|
355
|
+
>
|
|
356
|
+
{isRejecting && <SpinnerIcon size={12} />}
|
|
357
|
+
Reject
|
|
358
|
+
</RowButton>
|
|
359
|
+
</>
|
|
360
|
+
)}
|
|
361
|
+
<button
|
|
362
|
+
type="button"
|
|
363
|
+
onClick={onStartDelete}
|
|
364
|
+
disabled={isBusy}
|
|
365
|
+
aria-label={`Delete memory: ${content}`}
|
|
366
|
+
className={cn(
|
|
367
|
+
"stg:shrink-0 stg:rounded stg:p-1",
|
|
368
|
+
"stg:text-muted-foreground stg:hover:text-destructive stg:hover:bg-destructive-subtle",
|
|
369
|
+
"stg:transition-colors stg:disabled:pointer-events-none stg:disabled:opacity-50",
|
|
370
|
+
)}
|
|
371
|
+
>
|
|
372
|
+
<TrashIcon />
|
|
373
|
+
</button>
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// MemoryEditRow edits the fact text in place — the one field the
|
|
381
|
+
// subject owns. Saving maps the loaded record through the generated
|
|
382
|
+
// update mapper (wipe-safe) and overrides only content.
|
|
383
|
+
function MemoryEditRow({
|
|
384
|
+
memory,
|
|
385
|
+
onCancel,
|
|
386
|
+
onSaved,
|
|
387
|
+
}: {
|
|
388
|
+
memory: Memory;
|
|
389
|
+
onCancel: () => void;
|
|
390
|
+
onSaved: () => void;
|
|
391
|
+
}) {
|
|
392
|
+
const [draft, setDraft] = useState(memory.spec?.content ?? "");
|
|
393
|
+
const { updateContent, isUpdating, error } = useUpdateMemoryContent();
|
|
394
|
+
|
|
395
|
+
const trimmed = draft.trim();
|
|
396
|
+
const canSave = trimmed.length > 0 && trimmed.length <= 500 && !isUpdating;
|
|
397
|
+
|
|
398
|
+
const handleSave = async () => {
|
|
399
|
+
try {
|
|
400
|
+
await updateContent(memory, trimmed);
|
|
401
|
+
onSaved();
|
|
402
|
+
} catch {
|
|
403
|
+
// error state is surfaced via the hook
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
return (
|
|
408
|
+
<div
|
|
409
|
+
role="listitem"
|
|
410
|
+
className="stg:rounded-lg stg:border stg:border-border stg:px-3 stg:py-2.5"
|
|
411
|
+
>
|
|
412
|
+
<textarea
|
|
413
|
+
value={draft}
|
|
414
|
+
onChange={(e) => setDraft(e.target.value)}
|
|
415
|
+
rows={2}
|
|
416
|
+
maxLength={500}
|
|
417
|
+
aria-label="Edit memory text"
|
|
418
|
+
className={cn(
|
|
419
|
+
"stg:w-full stg:resize-y stg:rounded-md stg:border stg:border-input stg:bg-background stg:px-2 stg:py-1.5 stg:text-sm stg:text-foreground",
|
|
420
|
+
"stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-ring",
|
|
421
|
+
)}
|
|
422
|
+
/>
|
|
423
|
+
<div className="stg:mt-1.5 stg:flex stg:items-center stg:justify-between">
|
|
424
|
+
<span className="stg:text-[0.65rem] stg:text-muted-foreground">
|
|
425
|
+
{trimmed.length}/500
|
|
426
|
+
</span>
|
|
427
|
+
<div className="stg:flex stg:items-center stg:gap-1.5">
|
|
428
|
+
<RowButton
|
|
429
|
+
label="Save memory text"
|
|
430
|
+
onClick={() => void handleSave()}
|
|
431
|
+
disabled={!canSave}
|
|
432
|
+
emphasis="primary"
|
|
433
|
+
>
|
|
434
|
+
{isUpdating && <SpinnerIcon size={12} />}
|
|
435
|
+
Save
|
|
436
|
+
</RowButton>
|
|
437
|
+
<RowButton label="Cancel editing" onClick={onCancel} disabled={isUpdating}>
|
|
438
|
+
Cancel
|
|
439
|
+
</RowButton>
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
{error && (
|
|
443
|
+
<p className="stg:mt-1 stg:text-[0.65rem] stg:text-destructive" role="alert">
|
|
444
|
+
{getUserMessage(error)}
|
|
445
|
+
</p>
|
|
446
|
+
)}
|
|
447
|
+
</div>
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ---------------------------------------------------------------------------
|
|
452
|
+
// Presentation helpers (internal)
|
|
453
|
+
// ---------------------------------------------------------------------------
|
|
454
|
+
|
|
455
|
+
function isProposed(state: MemoryLifecycleState | undefined): boolean {
|
|
456
|
+
// The zero unspecified state should not exist (create stamps
|
|
457
|
+
// proposed); offering the decision actions is the safe rendering.
|
|
458
|
+
return (
|
|
459
|
+
state === undefined ||
|
|
460
|
+
state === MemoryLifecycleState.lifecycle_state_unspecified ||
|
|
461
|
+
state === MemoryLifecycleState.lifecycle_state_proposed
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function formatStateStamp(
|
|
466
|
+
state: MemoryLifecycleState | undefined,
|
|
467
|
+
changedAt: Date | undefined,
|
|
468
|
+
now: Date | undefined,
|
|
469
|
+
): string {
|
|
470
|
+
const verb =
|
|
471
|
+
state === MemoryLifecycleState.lifecycle_state_confirmed
|
|
472
|
+
? "Confirmed"
|
|
473
|
+
: state === MemoryLifecycleState.lifecycle_state_rejected
|
|
474
|
+
? "Rejected"
|
|
475
|
+
: "Proposed";
|
|
476
|
+
if (!changedAt) return verb;
|
|
477
|
+
return `${verb} ${formatRelativeTime(changedAt, now)}`;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function RowButton({
|
|
481
|
+
label,
|
|
482
|
+
onClick,
|
|
483
|
+
disabled,
|
|
484
|
+
emphasis,
|
|
485
|
+
children,
|
|
486
|
+
}: {
|
|
487
|
+
readonly label: string;
|
|
488
|
+
readonly onClick: () => void;
|
|
489
|
+
readonly disabled?: boolean;
|
|
490
|
+
readonly emphasis?: "primary";
|
|
491
|
+
readonly children: React.ReactNode;
|
|
492
|
+
}) {
|
|
493
|
+
return (
|
|
494
|
+
<button
|
|
495
|
+
type="button"
|
|
496
|
+
aria-label={label}
|
|
497
|
+
onClick={onClick}
|
|
498
|
+
disabled={disabled}
|
|
499
|
+
className={cn(
|
|
500
|
+
"stg:inline-flex stg:items-center stg:gap-1 stg:rounded-md stg:px-2 stg:py-1 stg:text-xs stg:font-medium",
|
|
501
|
+
emphasis === "primary"
|
|
502
|
+
? "stg:bg-primary stg:text-primary-foreground stg:hover:bg-primary-hover"
|
|
503
|
+
: "stg:border stg:border-input stg:bg-background stg:text-foreground stg:hover:bg-accent stg:hover:text-accent-foreground",
|
|
504
|
+
"stg:focus-visible:outline-none stg:focus-visible:ring-2 stg:focus-visible:ring-ring",
|
|
505
|
+
"stg:disabled:pointer-events-none stg:disabled:opacity-50",
|
|
506
|
+
)}
|
|
507
|
+
>
|
|
508
|
+
{children}
|
|
509
|
+
</button>
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function TrashIcon() {
|
|
514
|
+
return (
|
|
515
|
+
<svg
|
|
516
|
+
width="14"
|
|
517
|
+
height="14"
|
|
518
|
+
viewBox="0 0 16 16"
|
|
519
|
+
fill="none"
|
|
520
|
+
stroke="currentColor"
|
|
521
|
+
strokeWidth="1.5"
|
|
522
|
+
strokeLinecap="round"
|
|
523
|
+
strokeLinejoin="round"
|
|
524
|
+
aria-hidden="true"
|
|
525
|
+
>
|
|
526
|
+
<path d="M2.5 4h11M5.5 4V2.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1V4" />
|
|
527
|
+
<path d="M12.5 4v9a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1V4" />
|
|
528
|
+
<line x1="6.5" y1="7" x2="6.5" y2="11" />
|
|
529
|
+
<line x1="9.5" y1="7" x2="9.5" y2="11" />
|
|
530
|
+
</svg>
|
|
531
|
+
);
|
|
532
|
+
}
|