@datalayer/agent-runtimes 1.3.89 → 1.3.91
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/lib/Agent.js +2 -18
- package/lib/AgentDocument.js +5 -55
- package/lib/AgentNode.js +31 -208
- package/lib/AgentNotebook.js +5 -59
- package/lib/agent-node/AgentNodeGallery.js +23 -111
- package/lib/agents/AgentDetails.js +36 -274
- package/lib/agents/AgentRuntimeChat.js +1 -8
- package/lib/chat/Chat.js +7 -46
- package/lib/chat/ChatFloating.js +15 -100
- package/lib/chat/ChatInline.js +5 -47
- package/lib/chat/ChatPopupStandalone.js +5 -53
- package/lib/chat/ChatSidebar.js +4 -51
- package/lib/chat/ChatStandalone.js +4 -45
- package/lib/chat/EphemeralSurfaceControl.js +5 -25
- package/lib/chat/assistant/AssistantContextMenu.d.ts +8 -0
- package/lib/chat/assistant/AssistantContextMenu.js +17 -1
- package/lib/chat/assistant/AssistantStage.js +6 -25
- package/lib/chat/assistant/BalloonParts.js +8 -40
- package/lib/chat/assistant/BalloonVisual.js +2 -2
- package/lib/chat/assistant/ConversationBalloon.js +2 -11
- package/lib/chat/assistant/DecisionAsk.js +3 -3
- package/lib/chat/assistant/SandboxDetailsDialog.js +3 -17
- package/lib/chat/assistant/SpeechBalloon.js +14 -78
- package/lib/chat/base/ChatBase.js +37 -172
- package/lib/chat/display/EmptyState.js +7 -35
- package/lib/chat/display/FloatingBrandButton.js +4 -34
- package/lib/chat/display/PoweredByTag.js +3 -14
- package/lib/chat/document/EphemeralDocument.js +3 -18
- package/lib/chat/header/ChatHeader.js +1 -17
- package/lib/chat/header/ChatHeaderBase.js +2 -24
- package/lib/chat/header/ChatViewModeToggle.js +7 -29
- package/lib/chat/indicators/McpStatusIndicator.js +3 -12
- package/lib/chat/indicators/SandboxStatusIndicator.js +6 -34
- package/lib/chat/indicators/SkillsStatusIndicator.js +3 -12
- package/lib/chat/indicators/TypingDots.js +1 -11
- package/lib/chat/messages/ChatMessageList.js +58 -185
- package/lib/chat/messages/ChatMessages.js +15 -79
- package/lib/chat/messages/FormSubmissionMessage.js +2 -33
- package/lib/chat/messages/NotebookToolSurfaces.js +12 -41
- package/lib/chat/messages/TurnFooter.js +4 -10
- package/lib/chat/notebook/EphemeralNotebook.js +2 -10
- package/lib/chat/parts/ReasoningPart.js +2 -16
- package/lib/chat/parts/TextPart.js +2 -12
- package/lib/chat/parts/ToolPart.js +16 -90
- package/lib/chat/presence/Presence.js +5 -33
- package/lib/chat/prompt/FloatingCard.js +1 -13
- package/lib/chat/prompt/InputPrompt.js +1 -6
- package/lib/chat/prompt/InputPromptBase.js +4 -17
- package/lib/chat/prompt/InputPromptLexical.js +44 -45
- package/lib/chat/prompt/InputPromptText.js +1 -1
- package/lib/chat/prompt/footer/InPromptFooter.js +1 -8
- package/lib/chat/prompt/menus/AgentsMenu.js +1 -1
- package/lib/chat/prompt/menus/InlineAgentsMenu.js +1 -1
- package/lib/chat/prompt/menus/ModelSelector.js +1 -5
- package/lib/chat/prompt/menus/SkillsMenu.js +3 -30
- package/lib/chat/prompt/menus/SuggestionsMenu.js +1 -1
- package/lib/chat/prompt/menus/ToolsMenu.js +4 -48
- package/lib/chat/prompt/plugins/AgentMentionPlugin.js +2 -9
- package/lib/chat/prompt/plugins/CommandPlugin.js +2 -9
- package/lib/chat/prompt/plugins/MentionChip.js +1 -15
- package/lib/chat/prompt/stack.js +1 -19
- package/lib/chat/tools/A2AAgentDialog.js +9 -42
- package/lib/chat/tools/SubagentActivity.js +5 -22
- package/lib/chat/tools/ToolApprovalBanner.js +4 -34
- package/lib/chat/tools/ToolApprovalDialog.js +4 -19
- package/lib/chat/tools/ToolCallDisplay.js +12 -97
- package/lib/chat/usage/ContextPie.js +4 -45
- package/lib/chat/words.d.ts +7 -0
- package/lib/chat/words.js +12 -0
- package/lib/components/a2ui/datalayer/Chart.js +4 -10
- package/lib/components/a2ui/datalayer/Chat.js +2 -10
- package/lib/components/a2ui/datalayer/Download.js +1 -1
- package/lib/components/a2ui/datalayer/Evidence.js +1 -15
- package/lib/components/a2ui/datalayer/FileUpload.js +1 -12
- package/lib/components/a2ui/datalayer/Table.js +9 -17
- package/lib/components/a2ui/datalayer/formWidgets.js +3 -2
- package/lib/components/a2ui/datalayer/parts.js +1 -16
- package/lib/components/a2ui/styles.js +16 -5
- package/lib/components/agents/AgentSummary.js +1 -51
- package/lib/components/cards/NotificationEventCard.js +6 -72
- package/lib/components/cards/OutputCard.js +4 -48
- package/lib/components/cards/ToolApprovalCard.js +2 -35
- package/lib/components/code-sandboxes/CodeSandboxCellVariables.js +1 -1
- package/lib/components/code-sandboxes/CodeSandboxEnvironmentSelect.js +4 -14
- package/lib/components/code-sandboxes/CodeSandboxPicker.js +16 -21
- package/lib/components/code-sandboxes/CodeSandboxVariables.js +2 -2
- package/lib/components/scheduler/ScheduleMenu.js +5 -10
- package/lib/components/specs/SpecPayloadPreview.js +1 -26
- package/lib/components/teams/A2ATeamGraph.d.ts +4 -3
- package/lib/components/teams/A2ATeamGraph.js +25 -71
- package/lib/components/teams/AnswerSurfacesView.js +2 -2
- package/lib/components/teams/NotebookPreview.js +1 -10
- package/lib/components/teams/NotebookPreviewView.js +2 -3
- package/lib/components/teams/SceneTranscript.js +3 -17
- package/lib/components/teams/SceneView.js +1 -8
- package/lib/components/teams/TeamNotebook.js +5 -13
- package/lib/components/teams/TeamNotebookView.js +2 -12
- package/lib/components/teams/index.d.ts +1 -0
- package/lib/components/teams/index.js +1 -0
- package/lib/components/teams/useBalloonRoom.d.ts +39 -0
- package/lib/components/teams/useBalloonRoom.js +89 -0
- package/lib/config/AgentConfiguration.js +22 -225
- package/lib/context/ContextDistribution.js +7 -41
- package/lib/context/ContextInspector.js +9 -107
- package/lib/context/ContextPanel.js +11 -86
- package/lib/context/ContextUsage.js +6 -34
- package/lib/context/CostTracker.js +7 -42
- package/lib/context/MessageHistory.js +3 -37
- package/lib/context/TurnGraphChart.js +9 -41
- package/lib/examples/A2UiAgentExample.js +6 -108
- package/lib/examples/A2UiComponentsGalleryExample.js +1 -1
- package/lib/examples/A2UiContactCardExample.js +1 -1
- package/lib/examples/A2UiJupyterOutputExample.js +25 -125
- package/lib/examples/A2UiRestaurantExample.js +1 -1
- package/lib/examples/A2UiViewerExample.js +1 -1
- package/lib/examples/AgUiAgenticExample.js +3 -35
- package/lib/examples/AgUiBackendToolRenderingExample.js +2 -25
- package/lib/examples/AgUiHaikuGenUiExample.js +2 -32
- package/lib/examples/AgUiHumanInTheLoopExample.js +5 -59
- package/lib/examples/AgUiSharedStateExample.js +6 -56
- package/lib/examples/AgUiToolsBasedGenUiExample.js +25 -120
- package/lib/examples/AgentA2AExample.js +1 -15
- package/lib/examples/AgentA2ATeamExample.js +6 -30
- package/lib/examples/AgentCheckpointsExample.js +11 -145
- package/lib/examples/AgentCodeSandboxExample.js +5 -108
- package/lib/examples/AgentCodeSandboxesExample.js +3 -30
- package/lib/examples/AgentCodemodeExample.js +9 -85
- package/lib/examples/AgentCompactionExample.js +3 -64
- package/lib/examples/AgentDecideExample.js +1 -13
- package/lib/examples/AgentEvalsExample.js +5 -60
- package/lib/examples/AgentGuardrailsExample.js +4 -28
- package/lib/examples/AgentHooksExample.js +1 -1
- package/lib/examples/AgentInferenceProviderExample.js +5 -96
- package/lib/examples/AgentMCPExample.js +7 -105
- package/lib/examples/AgentMemoryExample.js +5 -74
- package/lib/examples/AgentMonitoringExample.js +7 -53
- package/lib/examples/AgentNotificationsExample.js +3 -61
- package/lib/examples/AgentOtelExample.js +3 -72
- package/lib/examples/AgentOutputsExample.js +6 -81
- package/lib/examples/AgentParametersExample.js +2 -37
- package/lib/examples/AgentSkillsExample.js +3 -45
- package/lib/examples/AgentSubagentsExample.js +6 -56
- package/lib/examples/AgentToolApprovalsExample.js +1 -11
- package/lib/examples/AgentTriggersExample.js +29 -239
- package/lib/examples/AgentspecsExample.js +6 -60
- package/lib/examples/AssistantExample.js +5 -5
- package/lib/examples/AssistantGalleryExample.js +8 -65
- package/lib/examples/CellExample.js +1 -6
- package/lib/examples/ChatCustomExample.js +2 -31
- package/lib/examples/ChatExample.js +1 -9
- package/lib/examples/ChatStandaloneExample.js +3 -37
- package/lib/examples/CopilotKitLexicalExample.js +1 -24
- package/lib/examples/DecksAgent.js +1 -1
- package/lib/examples/DocumentPageAgent.js +1 -1
- package/lib/examples/HomeExample.js +14 -62
- package/lib/examples/Lexical2Example.js +3 -32
- package/lib/examples/LexicalAgentExample.js +2 -34
- package/lib/examples/LexicalAgentSidebarExample.js +3 -34
- package/lib/examples/LoopAppComputerExample.js +1 -1
- package/lib/examples/LoopShellExample.js +2 -2
- package/lib/examples/LoopStrategyExample.js +15 -87
- package/lib/examples/LoopWorkspaceEmbed.js +2 -18
- package/lib/examples/LoopWorkspaceExample.js +2 -2
- package/lib/examples/NotebookAgentExample.js +2 -33
- package/lib/examples/NotebookAgentSidebarExample.js +3 -46
- package/lib/examples/NotebookCollaborationExample.js +1 -31
- package/lib/examples/NotebookExample.js +1 -18
- package/lib/examples/NotebookPageAgent.js +1 -1
- package/lib/examples/ScenesExample.js +5 -24
- package/lib/examples/VoiceChatExample.js +1 -1
- package/lib/examples/components/AuthRequiredView.js +1 -7
- package/lib/examples/components/ErrorView.js +1 -8
- package/lib/examples/components/ExampleErrorBoundary.js +1 -14
- package/lib/examples/components/FooterMetrics.js +1 -7
- package/lib/examples/components/Header.js +12 -51
- package/lib/examples/components/HeaderControls.js +1 -9
- package/lib/examples/components/LexicalEditor.js +1 -8
- package/lib/examples/components/MainContent.js +1 -13
- package/lib/examples/components/MockFileBrowser.js +4 -40
- package/lib/examples/components/SessionTabs.js +1 -1
- package/lib/examples/components/TimeTravel.js +1 -13
- package/lib/examples/main.js +13 -81
- package/lib/examples/scenes/A2UiComponentsGalleryScene.js +3 -34
- package/lib/examples/scenes/A2UiContactCardScene.js +3 -35
- package/lib/examples/scenes/A2UiRestaurantScene.js +3 -53
- package/lib/examples/scenes/A2UiViewerScene.js +3 -34
- package/lib/examples/utils/AssistantGalleryGrid.js +6 -35
- package/lib/examples2/DatalayerNotebookExample.js +3 -33
- package/lib/examples2/NativeNavigationExample.js +4 -10
- package/lib/examples2/NotebookMutationsKernel.js +2 -1
- package/lib/examples2/NotebookMutationsServiceManager.js +2 -1
- package/lib/examples2/ReactRouterExample.js +14 -42
- package/lib/hooks/useToolApprovals.d.ts +2 -34
- package/lib/hooks/useToolApprovals.js +1 -64
- package/lib/identity/AgentIdentity.js +17 -62
- package/lib/identity/IdentityConnect.js +13 -59
- package/lib/identity/OAuthCallback.js +6 -44
- package/lib/lexical/ChatInlinePlugin.js +6 -14
- package/lib/loop/apps/AppFeedback.js +12 -3
- package/lib/loop/apps/AppRenderer.d.ts +4 -3
- package/lib/loop/apps/AppRenderer.js +11 -12
- package/lib/loop/apps/appspec.js +6 -3
- package/lib/loop/apps/feedback.d.ts +3 -2
- package/lib/loop/apps/feedback.js +4 -2
- package/lib/loop/core/index.d.ts +19 -0
- package/lib/loop/core/index.js +27 -0
- package/lib/loop/embed/AppEmbed.d.ts +1 -1
- package/lib/loop/embed/AppEmbed.js +2 -1
- package/lib/loop/embed/LoopEmbed.js +1 -7
- package/lib/loop/embed/embedConfig.d.ts +3 -3
- package/lib/loop/embed/embedConfig.js +2 -3
- package/lib/loop/embed/embedTheme.d.ts +4 -4
- package/lib/loop/embed/embedTheme.js +11 -7
- package/lib/loop/plugins/a2a-sidebar/A2ASidebar.js +7 -34
- package/lib/loop/plugins/a2ui/A2uiView.js +4 -12
- package/lib/loop/plugins/a2ui-surface/InlineSurface.js +1 -16
- package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +2 -12
- package/lib/loop/plugins/agents/AnonymousKeyBadge.js +3 -2
- package/lib/loop/plugins/agents/SandboxSelector.js +3 -7
- package/lib/loop/plugins/agents/SandboxView.js +4 -27
- package/lib/loop/plugins/agents/TeamMemberPicker.js +2 -2
- package/lib/loop/plugins/agentspecs/AgentspecPicker.js +3 -2
- package/lib/loop/plugins/app-activity/AppActivity.js +1 -1
- package/lib/loop/plugins/app-computer/AppComputer.js +32 -27
- package/lib/loop/plugins/app-elements/AppElements.js +2 -31
- package/lib/loop/plugins/app-page/AppPage.js +26 -8
- package/lib/loop/plugins/app-rules/AppRulesCard.js +4 -20
- package/lib/loop/plugins/assistant/index.js +1 -13
- package/lib/loop/plugins/chat/ChatView.js +89 -130
- package/lib/loop/plugins/checkpoints-sidebar/CheckpointsSidebar.js +2 -13
- package/lib/loop/plugins/decks/DeckSurface.js +2 -2
- package/lib/loop/plugins/document/DocumentView.js +4 -12
- package/lib/loop/plugins/document-toolbar/SandboxStatusItem.js +3 -8
- package/lib/loop/plugins/graph/GraphView.js +2 -2
- package/lib/loop/plugins/input-prompt/ComposerView.js +1 -1
- package/lib/loop/plugins/notebook/NotebookView.js +3 -11
- package/lib/loop/plugins/page-layout/SplitLayout.js +11 -34
- package/lib/loop/plugins/page-layout/TurnPanel.js +9 -35
- package/lib/loop/plugins/shell/useEditorPreviews.js +3 -2
- package/lib/loop/plugins/subagent-activity/SubagentPulse.js +1 -13
- package/lib/loop/plugins/window-frame/WindowFrame.js +10 -44
- package/lib/loop/plugins/window-frame/index.js +1 -1
- package/lib/loop/shell/LoopWorkspace.js +34 -77
- package/lib/loop/shell/PluginToggles.js +4 -15
- package/lib/loop/shell/SidebarRail.js +8 -40
- package/lib/loop/shell/ViewSwitcher.js +4 -24
- package/lib/loop-main.js +2 -3
- package/lib/mcp/McpServerManager.js +6 -37
- package/lib/portable/index.d.ts +11 -0
- package/lib/portable/index.js +20 -0
- package/lib/protocols/AGUIAdapter.js +2 -2
- package/lib/protocols/BaseProtocolAdapter.d.ts +2 -0
- package/lib/protocols/BaseProtocolAdapter.js +4 -0
- package/lib/specs/apps.js +6 -0
- package/lib/specs/appspecSchema.js +11 -4
- package/lib/stories/loop/ReferenceScreens.js +17 -91
- package/lib/stories/loop/pictures-main.js +1 -1
- package/lib/types/agentspecs.d.ts +5 -2
- package/lib/types/protocol.d.ts +2 -0
- package/lib/utils/approvals.d.ts +59 -0
- package/lib/utils/approvals.js +101 -0
- package/lib/voice/VoiceInput.js +3 -39
- package/package.json +2 -2
- package/lib/shims/primerAddonsBox.d.ts +0 -1
- package/lib/shims/primerAddonsBox.js +0 -7
|
@@ -256,52 +256,21 @@ export function McpServerManager({ baseUrl, agentId, enableCodemode = false, sel
|
|
|
256
256
|
}, [selectedServers, onSelectedServersChange]);
|
|
257
257
|
const isLoading = catalogQuery.isLoading;
|
|
258
258
|
const isMutating = enableMutation.isPending || disableMutation.isPending;
|
|
259
|
-
return (_jsxs(Box, { sx: { display:
|
|
260
|
-
|
|
261
|
-
alignItems: 'center',
|
|
262
|
-
justifyContent: 'space-between',
|
|
263
|
-
}, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(ToolsIcon, { size: 16 }), _jsx(Text, { sx: { fontWeight: 'semibold' }, children: "MCP Server Management" })] }), _jsx(IconButton, { icon: SyncIcon, "aria-label": "Refresh server lists", size: "small", onClick: handleRefresh, disabled: isLoading || isMutating })] }), error && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: error }) })), enableCodemode && (_jsx(Flash, { variant: "default", children: _jsxs(Text, { sx: { fontSize: 0 }, children: [_jsx("strong", { children: "Codemode enabled:" }), " Adding or removing MCP servers will regenerate the Codemode tool registry."] }) })), isLoading && (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, py: 3 }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Loading MCP servers..." })] })), !isLoading && assignedConfigServers.length > 0 && (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Assigned Configured Servers (", assignedConfigServers.length, ")"] }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "These servers are from ~/.datalayer/mcp.json and assigned to this agent." }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: assignedConfigServers.map(server => (_jsx(ServerCard, { server: server, variant: "config", disabled: disabled, isSelected: true, onSelect: onSelectedServersChange ? handleServerSelect : undefined, onRemove: () => handleRemoveServer(server.id, true) }, server.id))) })] })), !isLoading && assignedCatalogServers.length > 0 && (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Assigned Catalog Servers (", assignedCatalogServers.length, ")"] }), _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: assignedCatalogServers.map(server => (_jsx(ServerCard, { server: server, variant: "active", disabled: disabled || isMutating, isSelected: true, onSelect: onSelectedServersChange ? handleServerSelect : undefined, onRemove: () => handleDisableServer(server.id), isRemoving: disableMutation.isPending &&
|
|
264
|
-
disableMutation.variables === server.id }, server.id))) })] })), !isLoading && (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Available from Catalog (", availableCatalogServers.length, ")"] }), availableCatalogServers.length > 3 && (_jsx(TextInput, { leadingVisual: SearchIcon, placeholder: "Search servers...", value: searchQuery, onChange: e => setSearchQuery(e.target.value), sx: { width: '100%' } })), filteredCatalogServers.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: searchQuery
|
|
259
|
+
return (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 4, children: [_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, children: [_jsx(ToolsIcon, { size: 16 }), _jsx(Text, { sx: { fontWeight: 'semibold' }, children: "MCP Server Management" })] }), _jsx(IconButton, { icon: SyncIcon, "aria-label": "Refresh server lists", size: "small", onClick: handleRefresh, disabled: isLoading || isMutating })] }), error && (_jsx(Flash, { variant: "danger", children: _jsx(Text, { sx: { fontSize: 1 }, children: error }) })), enableCodemode && (_jsx(Flash, { variant: "default", children: _jsxs(Text, { sx: { fontSize: 0 }, children: [_jsx("strong", { children: "Codemode enabled:" }), " Adding or removing MCP servers will regenerate the Codemode tool registry."] }) })), isLoading && (_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, py: 3, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { color: 'fg.muted' }, children: "Loading MCP servers..." })] })), !isLoading && assignedConfigServers.length > 0 && (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Assigned Configured Servers (", assignedConfigServers.length, ")"] }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 0 }, children: "These servers are from ~/.datalayer/mcp.json and assigned to this agent." }), _jsx(Box, { display: "flex", flexDirection: "column", gap: 2, children: assignedConfigServers.map(server => (_jsx(ServerCard, { server: server, variant: "config", disabled: disabled, isSelected: true, onSelect: onSelectedServersChange ? handleServerSelect : undefined, onRemove: () => handleRemoveServer(server.id, true) }, server.id))) })] })), !isLoading && assignedCatalogServers.length > 0 && (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Assigned Catalog Servers (", assignedCatalogServers.length, ")"] }), _jsx(Box, { display: "flex", flexDirection: "column", gap: 2, children: assignedCatalogServers.map(server => (_jsx(ServerCard, { server: server, variant: "active", disabled: disabled || isMutating, isSelected: true, onSelect: onSelectedServersChange ? handleServerSelect : undefined, onRemove: () => handleDisableServer(server.id), isRemoving: disableMutation.isPending &&
|
|
260
|
+
disableMutation.variables === server.id }, server.id))) })] })), !isLoading && (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsxs(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: ["Available from Catalog (", availableCatalogServers.length, ")"] }), availableCatalogServers.length > 3 && (_jsx(TextInput, { leadingVisual: SearchIcon, placeholder: "Search servers...", value: searchQuery, onChange: e => setSearchQuery(e.target.value), sx: { width: '100%' } })), filteredCatalogServers.length === 0 ? (_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: searchQuery
|
|
265
261
|
? 'No servers match your search.'
|
|
266
|
-
: 'All catalog servers are already enabled.' })) : (_jsx(Box, {
|
|
262
|
+
: 'All catalog servers are already enabled.' })) : (_jsx(Box, { display: "flex", flexDirection: "column", gap: 2, children: filteredCatalogServers.map(server => (_jsx(ServerCard, { server: server, variant: "catalog", disabled: disabled || isMutating, onAdd: () => handleEnableServer(server.id), isAdding: enableMutation.isPending &&
|
|
267
263
|
enableMutation.variables === server.id }, server.id))) }))] }))] }));
|
|
268
264
|
}
|
|
269
265
|
function ServerCard({ server, variant, disabled = false, isSelected = false, onSelect, onAdd, onRemove, isAdding = false, isRemoving = false, }) {
|
|
270
266
|
// Check availability based on isAvailable field from the server
|
|
271
267
|
const isAvailable = server.isAvailable !== false;
|
|
272
268
|
const missingEnvVars = !isAvailable && server.requiredEnvVars && server.requiredEnvVars.length > 0;
|
|
273
|
-
return (_jsxs(Box, {
|
|
274
|
-
display: 'flex',
|
|
275
|
-
alignItems: 'flex-start',
|
|
276
|
-
gap: 2,
|
|
277
|
-
padding: 2,
|
|
278
|
-
borderRadius: 2,
|
|
279
|
-
backgroundColor: 'canvas.subtle',
|
|
280
|
-
border: '1px solid',
|
|
281
|
-
borderColor: isSelected ? 'accent.emphasis' : 'border.default',
|
|
282
|
-
opacity: disabled || !isAvailable ? 0.6 : 1,
|
|
283
|
-
cursor: onSelect && !disabled && isAvailable ? 'pointer' : 'default',
|
|
284
|
-
}, onClick: () => {
|
|
269
|
+
return (_jsxs(Box, { display: "flex", alignItems: "flex-start", gap: 2, padding: 2, borderRadius: 2, backgroundColor: "canvas.subtle", border: "1px solid", borderColor: isSelected ? 'accent.emphasis' : 'border.default', opacity: disabled || !isAvailable ? 0.6 : 1, cursor: onSelect && !disabled && isAvailable ? 'pointer' : 'default', onClick: () => {
|
|
285
270
|
if (onSelect && !disabled && isAvailable) {
|
|
286
271
|
onSelect(server, !isSelected);
|
|
287
272
|
}
|
|
288
|
-
}, children: [_jsx(Box, { sx: {
|
|
289
|
-
display: 'flex',
|
|
290
|
-
alignItems: 'center',
|
|
291
|
-
justifyContent: 'center',
|
|
292
|
-
width: 32,
|
|
293
|
-
height: 32,
|
|
294
|
-
borderRadius: 2,
|
|
295
|
-
backgroundColor: variant === 'config' ? 'attention.subtle' : 'accent.subtle',
|
|
296
|
-
flexShrink: 0,
|
|
297
|
-
}, children: _jsx(ServerIcon, { size: 16 }) }), _jsxs(Box, { sx: { flex: 1, minWidth: 0 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, mb: 1 }, children: [_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: server.name }), variant === 'active' && (_jsx(Label, { variant: "success", size: "small", children: "Active" })), variant === 'config' && (_jsx(Label, { variant: "attention", size: "small", children: "Config" })), !isAvailable && (_jsx(Label, { variant: "danger", size: "small", children: "Missing env vars" })), isSelected && (_jsxs(Label, { variant: "accent", size: "small", children: [_jsx(CheckIcon, { size: 12 }), " Selected"] }))] }), server.description && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', mb: 1 }, children: server.description })), missingEnvVars && (_jsxs(Box, { sx: {
|
|
298
|
-
fontSize: 0,
|
|
299
|
-
color: 'danger.fg',
|
|
300
|
-
mb: 1,
|
|
301
|
-
p: 1,
|
|
302
|
-
bg: 'danger.subtle',
|
|
303
|
-
borderRadius: 1,
|
|
304
|
-
}, children: [_jsx(Text, { sx: { fontWeight: 'semibold', display: 'block', mb: 1 }, children: "Missing environment variables:" }), server.requiredEnvVars.map(envVar => (_jsx(Text, { as: "code", sx: {
|
|
273
|
+
}, children: [_jsx(Box, { display: "flex", alignItems: "center", justifyContent: "center", width: 32, height: 32, borderRadius: 2, backgroundColor: variant === 'config' ? 'attention.subtle' : 'accent.subtle', flexShrink: 0, children: _jsx(ServerIcon, { size: 16 }) }), _jsxs(Box, { flex: 1, minWidth: 0, children: [_jsxs(Box, { display: "flex", alignItems: "center", gap: 2, mb: 1, children: [_jsx(Text, { sx: { fontWeight: 'semibold', fontSize: 1 }, children: server.name }), variant === 'active' && (_jsx(Label, { variant: "success", size: "small", children: "Active" })), variant === 'config' && (_jsx(Label, { variant: "attention", size: "small", children: "Config" })), !isAvailable && (_jsx(Label, { variant: "danger", size: "small", children: "Missing env vars" })), isSelected && (_jsxs(Label, { variant: "accent", size: "small", children: [_jsx(CheckIcon, { size: 12 }), " Selected"] }))] }), server.description && (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', mb: 1 }, children: server.description })), missingEnvVars && (_jsxs(Box, { fontSize: 0, color: "danger.fg", mb: 1, p: 1, bg: "danger.subtle", borderRadius: 1, children: [_jsx(Text, { sx: { fontWeight: 'semibold', display: 'block', mb: 1 }, children: "Missing environment variables:" }), server.requiredEnvVars.map(envVar => (_jsx(Text, { as: "code", sx: {
|
|
305
274
|
display: 'inline-block',
|
|
306
275
|
mr: 1,
|
|
307
276
|
mb: 1,
|
|
@@ -317,7 +286,7 @@ function ServerCard({ server, variant, disabled = false, isSelected = false, onS
|
|
|
317
286
|
overflow: 'hidden',
|
|
318
287
|
textOverflow: 'ellipsis',
|
|
319
288
|
whiteSpace: 'nowrap',
|
|
320
|
-
}, children: ["Tools: ", server.tools.map(t => t.name).join(', ')] })), server.transport && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Transport: ", server.transport] }))] }), _jsxs(Box, {
|
|
289
|
+
}, children: ["Tools: ", server.tools.map(t => t.name).join(', ')] })), server.transport && (_jsxs(Text, { sx: { fontSize: 0, color: 'fg.muted' }, children: ["Transport: ", server.transport] }))] }), _jsxs(Box, { display: "flex", alignItems: "center", gap: 1, flexShrink: 0, children: [variant === 'catalog' && onAdd && (_jsx(Button, { variant: "primary", size: "small", leadingVisual: isAdding ? Spinner : PlusIcon, onClick: e => {
|
|
321
290
|
e.stopPropagation();
|
|
322
291
|
onAdd();
|
|
323
292
|
}, disabled: disabled || !isAvailable || isAdding, children: "Add" })), variant === 'active' && onRemove && (_jsx(Button, { variant: "danger", size: "small", leadingVisual: isRemoving ? Spinner : TrashIcon, onClick: e => {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What any JavaScript runtime can import from Agent Runtimes: no React, DOM,
|
|
3
|
+
* Jupyter or Node built-in. Re-exports only; the mobile app reaches Agent
|
|
4
|
+
* Runtimes through this entry alone.
|
|
5
|
+
*
|
|
6
|
+
* @module portable
|
|
7
|
+
*/
|
|
8
|
+
export { messageOfApproval, normalizeApproval, whyAsked, type ApprovalArgs, type ApprovalRecord, type MessageAsked, } from '../utils/approvals';
|
|
9
|
+
export { AGUIAdapter, type AGUIAdapterConfig } from '../protocols/AGUIAdapter';
|
|
10
|
+
export { createActivityMessage, createAssistantMessage, createUserMessage, generateMessageId, type ChatMessage, type ChatMessageDelta, type ChatMessageInput, type ChatThread, type ContentPart, type MessageRole, type ToolCallStatus, } from '../types/messages';
|
|
11
|
+
export type { ProtocolAdapterConfig, ProtocolConnectionState, ProtocolEvent, ProtocolEventHandler, } from '../types/protocol';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/*
|
|
6
|
+
* Copyright (c) 2026 Datalayer, Inc.
|
|
7
|
+
* Distributed under the terms of the Modified BSD License.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* What any JavaScript runtime can import from Agent Runtimes: no React, DOM,
|
|
11
|
+
* Jupyter or Node built-in. Re-exports only; the mobile app reaches Agent
|
|
12
|
+
* Runtimes through this entry alone.
|
|
13
|
+
*
|
|
14
|
+
* @module portable
|
|
15
|
+
*/
|
|
16
|
+
export { messageOfApproval, normalizeApproval, whyAsked, } from '../utils/approvals';
|
|
17
|
+
// The AG-UI stream client and the messages it builds; a phone passes a
|
|
18
|
+
// streaming `fetch` in the adapter's config.
|
|
19
|
+
export { AGUIAdapter } from '../protocols/AGUIAdapter';
|
|
20
|
+
export { createActivityMessage, createAssistantMessage, createUserMessage, generateMessageId, } from '../types/messages';
|
|
@@ -99,7 +99,7 @@ export class AGUIAdapter extends BaseProtocolAdapter {
|
|
|
99
99
|
// e.g., http://localhost:8765/api/v1/ag-ui/default -> http://localhost:8765/api/v1/ag-ui/terminate
|
|
100
100
|
const baseUrl = new URL(this.aguiConfig.baseUrl);
|
|
101
101
|
const terminateUrl = `${baseUrl.origin}/api/v1/ag-ui/terminate`;
|
|
102
|
-
const response = await
|
|
102
|
+
const response = await this.fetchOf()(terminateUrl, {
|
|
103
103
|
method: 'POST',
|
|
104
104
|
headers: {
|
|
105
105
|
'Content-Type': 'application/json',
|
|
@@ -205,7 +205,7 @@ export class AGUIAdapter extends BaseProtocolAdapter {
|
|
|
205
205
|
console.log('[AGUIAdapter] Sending with identities:', options.identities.map(i => i.provider));
|
|
206
206
|
}
|
|
207
207
|
try {
|
|
208
|
-
const response = await
|
|
208
|
+
const response = await this.fetchOf()(this.aguiConfig.baseUrl, {
|
|
209
209
|
method: 'POST',
|
|
210
210
|
headers: this.buildHeaders({
|
|
211
211
|
Accept: 'text/event-stream',
|
|
@@ -70,6 +70,8 @@ export declare abstract class BaseProtocolAdapter implements ProtocolAdapter {
|
|
|
70
70
|
* Build headers for HTTP requests
|
|
71
71
|
*/
|
|
72
72
|
protected buildHeaders(additionalHeaders?: Record<string, string>): Record<string, string>;
|
|
73
|
+
/** The fetch the caller gave, else the global one; read per call so a test can stub the global. */
|
|
74
|
+
protected fetchOf(): typeof fetch;
|
|
73
75
|
/**
|
|
74
76
|
* Create abort signal with timeout
|
|
75
77
|
*/
|
|
@@ -110,6 +110,10 @@ export class BaseProtocolAdapter {
|
|
|
110
110
|
}
|
|
111
111
|
return { ...headers, ...additionalHeaders };
|
|
112
112
|
}
|
|
113
|
+
/** The fetch the caller gave, else the global one; read per call so a test can stub the global. */
|
|
114
|
+
fetchOf() {
|
|
115
|
+
return this.config.fetch ?? ((input, init) => fetch(input, init));
|
|
116
|
+
}
|
|
113
117
|
/**
|
|
114
118
|
* Create abort signal with timeout
|
|
115
119
|
*/
|
package/lib/specs/apps.js
CHANGED
|
@@ -3515,6 +3515,7 @@ export const APP_SOURCES = {
|
|
|
3515
3515
|
},
|
|
3516
3516
|
],
|
|
3517
3517
|
interface: {
|
|
3518
|
+
accent: 'green',
|
|
3518
3519
|
welcome: "Ask me for a report from the books: open invoices, aged balances, a trial balance or a customer's ledger. I read Odoo; I change nothing.",
|
|
3519
3520
|
starters: [
|
|
3520
3521
|
{
|
|
@@ -3869,6 +3870,7 @@ export const APP_SOURCES = {
|
|
|
3869
3870
|
agent: 'jupyter-data-analyst:0.0.1',
|
|
3870
3871
|
contents: ['The dataset under investigation'],
|
|
3871
3872
|
interface: {
|
|
3873
|
+
accent: 'green',
|
|
3872
3874
|
components: [
|
|
3873
3875
|
'Card',
|
|
3874
3876
|
'Column',
|
|
@@ -4230,6 +4232,7 @@ export const APP_SOURCES = {
|
|
|
4230
4232
|
},
|
|
4231
4233
|
],
|
|
4232
4234
|
interface: {
|
|
4235
|
+
accent: 'green',
|
|
4233
4236
|
welcome: 'I sort your mail and draft the replies. I ask before I send, and I never delete.',
|
|
4234
4237
|
starters: [
|
|
4235
4238
|
{
|
|
@@ -4321,6 +4324,7 @@ export const APP_SOURCES = {
|
|
|
4321
4324
|
'The Models page: how each model is billed (standard or credits) and who hosts it',
|
|
4322
4325
|
],
|
|
4323
4326
|
interface: {
|
|
4327
|
+
accent: 'green',
|
|
4324
4328
|
components: [
|
|
4325
4329
|
'Card',
|
|
4326
4330
|
'Column',
|
|
@@ -5247,6 +5251,7 @@ export const APP_SOURCES = {
|
|
|
5247
5251
|
agent: 'jupyter-data-analyst:0.0.1',
|
|
5248
5252
|
contents: ['The benchmark run: task results, traces, cost and latency'],
|
|
5249
5253
|
interface: {
|
|
5254
|
+
accent: 'green',
|
|
5250
5255
|
components: [
|
|
5251
5256
|
'Card',
|
|
5252
5257
|
'Column',
|
|
@@ -5354,6 +5359,7 @@ export const APP_SOURCES = {
|
|
|
5354
5359
|
agent: 'jupyter-data-analyst:0.0.1',
|
|
5355
5360
|
contents: ['Order history', 'Supplier price lists', 'Delivery records'],
|
|
5356
5361
|
interface: {
|
|
5362
|
+
accent: 'green',
|
|
5357
5363
|
components: [
|
|
5358
5364
|
'Card',
|
|
5359
5365
|
'Column',
|
|
@@ -435,9 +435,16 @@ export const APPSPEC_SCHEMA = {
|
|
|
435
435
|
description: "`chat`, `page` or `split`; the kind's own when unsaid",
|
|
436
436
|
},
|
|
437
437
|
accent: {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
438
|
+
anyOf: [
|
|
439
|
+
{
|
|
440
|
+
$ref: '#/$defs/Accent',
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
type: 'null',
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
default: null,
|
|
447
|
+
description: "The application's one colour, over whichever theme it runs in; unsaid, it wears the theme's own colours",
|
|
441
448
|
},
|
|
442
449
|
theme: {
|
|
443
450
|
anyOf: [
|
|
@@ -449,7 +456,7 @@ export const APPSPEC_SCHEMA = {
|
|
|
449
456
|
},
|
|
450
457
|
],
|
|
451
458
|
default: null,
|
|
452
|
-
description: "The theme it runs in by default, at its address, embedded, in the Studio's Preview and as an example: a `variant` and, optionally, a colour `mode`. The person's own when unsaid. Its `accent
|
|
459
|
+
description: "The theme it runs in by default, at its address, embedded, in the Studio's Preview and as an example: a `variant` and, optionally, a colour `mode`. The person's own when unsaid. Its `accent`, when it names one, colours it, whichever theme it is",
|
|
453
460
|
},
|
|
454
461
|
welcome: {
|
|
455
462
|
default: '',
|
|
@@ -20,8 +20,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
20
20
|
* @module stories/loop/ReferenceScreens
|
|
21
21
|
*/
|
|
22
22
|
import { useEffect, useRef, useState } from 'react';
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
23
|
+
import { Button, Heading, Text } from '@primer/react';
|
|
24
|
+
import { Box, DatalayerThemeProvider, setupPrimerPortals, themeAccentVars, themeConfigs, } from '@datalayer/primer-addons';
|
|
25
25
|
import { ChatBaseHeader } from '../../chat/header/ChatHeaderBase';
|
|
26
26
|
import { ChatMessageList } from '../../chat/messages/ChatMessageList';
|
|
27
27
|
import { InputPrompt } from '../../chat/prompt/InputPrompt';
|
|
@@ -84,29 +84,12 @@ export function ReferenceTheme({ theme, mode, children, }) {
|
|
|
84
84
|
document.documentElement.setAttribute('data-reference-mode', mode);
|
|
85
85
|
}, [theme, mode]);
|
|
86
86
|
return (_jsx(DatalayerThemeProvider, { colorMode: mode, theme: config.primerTheme, themeStyles: config.themeStyles, baseStyles: theme === 'loop'
|
|
87
|
-
?
|
|
88
|
-
: undefined, children: _jsx(Box, { "data-reference-stage": "",
|
|
89
|
-
position: 'fixed',
|
|
90
|
-
inset: 0,
|
|
91
|
-
p: 4,
|
|
92
|
-
bg: 'var(--loop-stage, var(--bgColor-muted))',
|
|
93
|
-
color: 'fg.default',
|
|
94
|
-
display: 'flex',
|
|
95
|
-
}, children: children }) }));
|
|
87
|
+
? themeAccentVars('green', mode)
|
|
88
|
+
: undefined, children: _jsx(Box, { "data-reference-stage": "", position: "fixed", inset: 0, p: 4, bg: theme === 'loop' ? 'var(--theme-stage)' : 'var(--bgColor-muted)', color: "fg.default", display: "flex", children: children }) }));
|
|
96
89
|
}
|
|
97
90
|
/** One rounded surface on the stage: the theme's frame. */
|
|
98
91
|
function Frame({ children }) {
|
|
99
|
-
return (_jsx(Box, {
|
|
100
|
-
flex: 1,
|
|
101
|
-
minWidth: 0,
|
|
102
|
-
display: 'flex',
|
|
103
|
-
overflow: 'hidden',
|
|
104
|
-
bg: 'canvas.default',
|
|
105
|
-
borderRadius: 'var(--theme-radius-frame, 12px)',
|
|
106
|
-
border: 'var(--theme-hairline, 1px) solid',
|
|
107
|
-
borderColor: 'border.muted',
|
|
108
|
-
boxShadow: 'var(--theme-shadow, none)',
|
|
109
|
-
}, children: children }));
|
|
92
|
+
return (_jsx(Box, { flex: 1, minWidth: 0, display: "flex", overflow: "hidden", bg: "canvas.default", borderRadius: "var(--theme-radius-frame, 12px)", border: "var(--theme-hairline, 1px) solid", borderColor: "border.muted", boxShadow: "var(--theme-shadow, none)", children: children }));
|
|
110
93
|
}
|
|
111
94
|
// ---------------------------------------------------------------------------
|
|
112
95
|
// Fixed data
|
|
@@ -188,47 +171,30 @@ const AVATARS = {
|
|
|
188
171
|
function Conversation({ name, face, presence, items, composer = true, mcpServers, }) {
|
|
189
172
|
const endRef = useRef(null);
|
|
190
173
|
const [input, setInput] = useState('');
|
|
191
|
-
return (_jsxs(Box, {
|
|
192
|
-
flex: 1,
|
|
193
|
-
minWidth: 0,
|
|
194
|
-
display: 'flex',
|
|
195
|
-
flexDirection: 'column',
|
|
196
|
-
height: '100%',
|
|
197
|
-
}, children: [_jsx(ChatBaseHeader, { title: name, brandIcon: _jsx(PresenceFace, { face: face, size: 20, state: presence }), headerContent: _jsx(PresenceLine, { state: presence }), padding: 3, messageCount: items.length, onNewChat: () => undefined, onClear: () => undefined }), _jsx(Box, { sx: { flex: 1, minHeight: 0, overflow: 'hidden' }, children: _jsx(ChatMessageList, { displayItems: items, isLoading: false, isStreaming: false, showLoadingIndicator: false, hideMessagesAfterToolUI: false, avatarConfig: AVATARS, padding: 3, emptyContent: null, messagesEndRef: endRef, onRespond: async () => undefined, mcpServers: mcpServers }) }), composer && (_jsx(InputPrompt, { input: input, setInput: setInput, isLoading: false, connectionConfirmed: true, placeholder: "Send a message", autoFocus: false, padding: 3, onSend: () => undefined, onStop: () => undefined, showTokenUsage: false, showModelSelector: false, showToolsMenu: false, showSkillsMenu: false, codemodeEnabled: false, hasConfigData: false, hasSkillsData: false, isA2AProtocol: false }))] }));
|
|
174
|
+
return (_jsxs(Box, { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", height: "100%", children: [_jsx(ChatBaseHeader, { title: name, brandIcon: _jsx(PresenceFace, { face: face, size: 20, state: presence }), headerContent: _jsx(PresenceLine, { state: presence }), padding: 3, messageCount: items.length, onNewChat: () => undefined, onClear: () => undefined }), _jsx(Box, { flex: 1, minHeight: 0, overflow: "hidden", children: _jsx(ChatMessageList, { displayItems: items, isLoading: false, isStreaming: false, showLoadingIndicator: false, hideMessagesAfterToolUI: false, avatarConfig: AVATARS, padding: 3, emptyContent: null, messagesEndRef: endRef, onRespond: async () => undefined, mcpServers: mcpServers }) }), composer && (_jsx(InputPrompt, { input: input, setInput: setInput, isLoading: false, connectionConfirmed: true, placeholder: "Send a message", autoFocus: false, padding: 3, onSend: () => undefined, onStop: () => undefined, showTokenUsage: false, showModelSelector: false, showToolsMenu: false, showSkillsMenu: false, codemodeEnabled: false, hasConfigData: false, hasSkillsData: false, isA2AProtocol: false }))] }));
|
|
198
175
|
}
|
|
199
176
|
// ---------------------------------------------------------------------------
|
|
200
177
|
// The four screens
|
|
201
178
|
// ---------------------------------------------------------------------------
|
|
202
179
|
/** A conversation. */
|
|
203
180
|
export function ConversationScreen() {
|
|
204
|
-
return (_jsx(Frame, { children: _jsx(Box, {
|
|
181
|
+
return (_jsx(Frame, { children: _jsx(Box, { flex: 1, display: "flex", justifyContent: "center", children: _jsx(Box, { width: "100%", maxWidth: 640, display: "flex", children: _jsx(Conversation, { name: "Support desk", face: "\uD83E\uDD8A", presence: "idle", items: CONVERSATION }) }) }) }));
|
|
205
182
|
}
|
|
206
183
|
/** A conversation beside its work: the split, one hairline between them. */
|
|
207
184
|
export function BesideWorkScreen() {
|
|
208
|
-
return (_jsxs(Frame, { children: [_jsx(Box, {
|
|
209
|
-
width: 'var(--theme-hairline, 1px)',
|
|
210
|
-
bg: 'border.muted',
|
|
211
|
-
flexShrink: 0,
|
|
212
|
-
} }), _jsxs(Box, { "data-reference-work": "", sx: {
|
|
213
|
-
flex: 1,
|
|
214
|
-
minWidth: 0,
|
|
215
|
-
p: 4,
|
|
216
|
-
display: 'flex',
|
|
217
|
-
flexDirection: 'column',
|
|
218
|
-
gap: 3,
|
|
219
|
-
}, children: [_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Draft \u00B7 reply" }), _jsx(Heading, { as: "h2", sx: { fontSize: 3, fontWeight: 600 }, children: "Your order 1042" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "To ada@example.com" }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsx(Text, { as: "p", sx: { m: 0 }, children: "Dear Ada," }), _jsx(Text, { as: "p", sx: { m: 0 }, children: "We are sorry your order arrived four days late. It left our warehouse on Monday and is with you by Thursday, 8 October." }), _jsx(Text, { as: "p", sx: { m: 0 }, children: "Your next order ships free, with no code to enter." }), _jsx(Text, { as: "p", sx: { m: 0 }, children: "The support desk" })] }), _jsxs(Box, { sx: { display: 'flex', gap: 2, mt: 'auto' }, children: [_jsx(Button, { variant: "primary", children: "Send" }), _jsx(Button, { children: "Edit" })] })] })] }));
|
|
185
|
+
return (_jsxs(Frame, { children: [_jsx(Box, { width: 400, flexShrink: 0, display: "flex", children: _jsx(Conversation, { name: "Support desk", face: "\uD83E\uDD8A", presence: "idle", items: BESIDE_WORK }) }), _jsx(Box, { width: "var(--theme-hairline, 1px)", bg: "border.muted", flexShrink: 0 }), _jsxs(Box, { "data-reference-work": "", flex: 1, minWidth: 0, p: 4, display: "flex", flexDirection: "column", gap: 3, children: [_jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "Draft \u00B7 reply" }), _jsx(Heading, { as: "h2", sx: { fontSize: 3, fontWeight: 600 }, children: "Your order 1042" }), _jsx(Text, { sx: { color: 'fg.muted', fontSize: 1 }, children: "To ada@example.com" }), _jsxs(Box, { display: "flex", flexDirection: "column", gap: 2, children: [_jsx(Text, { as: "p", sx: { m: 0 }, children: "Dear Ada," }), _jsx(Text, { as: "p", sx: { m: 0 }, children: "We are sorry your order arrived four days late. It left our warehouse on Monday and is with you by Thursday, 8 October." }), _jsx(Text, { as: "p", sx: { m: 0 }, children: "Your next order ships free, with no code to enter." }), _jsx(Text, { as: "p", sx: { m: 0 }, children: "The support desk" })] }), _jsxs(Box, { display: "flex", gap: 2, mt: "auto", children: [_jsx(Button, { variant: "primary", children: "Send" }), _jsx(Button, { children: "Edit" })] })] })] }));
|
|
220
186
|
}
|
|
221
187
|
/** The activity of a worker: what it did on its own, as it did it. */
|
|
222
188
|
export function WorkerActivityScreen() {
|
|
223
|
-
return (_jsx(Frame, { children: _jsx(Box, {
|
|
189
|
+
return (_jsx(Frame, { children: _jsx(Box, { flex: 1, display: "flex", justifyContent: "center", children: _jsx(Box, { width: "100%", maxWidth: 680, display: "flex", children: _jsx(Conversation, { name: "Inbox triage", face: "\uD83D\uDCEC", presence: "working", items: WORKER_ACTIVITY, composer: false }) }) }) }));
|
|
224
190
|
}
|
|
225
191
|
/** An approval: the action, the rule that asks, and the one decision. */
|
|
226
192
|
export function ApprovalScreen() {
|
|
227
|
-
return (_jsx(Frame, { children: _jsx(Box, {
|
|
193
|
+
return (_jsx(Frame, { children: _jsx(Box, { flex: 1, display: "flex", justifyContent: "center", children: _jsx(Box, { width: "100%", maxWidth: 640, display: "flex", children: _jsx(Conversation, { name: "Support desk", face: "\uD83E\uDD8A", presence: "waiting", items: APPROVAL }) }) }) }));
|
|
228
194
|
}
|
|
229
195
|
/** Tool calls, each led by the mark of whoever the tool belongs to. */
|
|
230
196
|
export function ToolMarksScreen() {
|
|
231
|
-
return (_jsx(Frame, { children: _jsx(Box, {
|
|
197
|
+
return (_jsx(Frame, { children: _jsx(Box, { flex: 1, display: "flex", justifyContent: "center", children: _jsx(Box, { width: "100%", maxWidth: 680, display: "flex", children: _jsx(Conversation, { name: "Bookkeeper", face: "\uD83E\uDDEE", presence: "idle", items: TOOL_MARKS, composer: false, mcpServers: TOOL_MARKS_SERVERS }) }) }) }));
|
|
232
198
|
}
|
|
233
199
|
export const REFERENCE_SCREEN_COMPONENTS = {
|
|
234
200
|
conversation: ConversationScreen,
|
|
@@ -284,20 +250,7 @@ export function AssistantScreen({ picture, }) {
|
|
|
284
250
|
? 'working'
|
|
285
251
|
: picture;
|
|
286
252
|
const balloon = BALLOONS[picture];
|
|
287
|
-
return (_jsxs(Box, {
|
|
288
|
-
position: 'fixed',
|
|
289
|
-
right: 24,
|
|
290
|
-
bottom: 24,
|
|
291
|
-
width: 280,
|
|
292
|
-
height: 160,
|
|
293
|
-
p: 3,
|
|
294
|
-
zIndex: 2000,
|
|
295
|
-
bg: 'canvas.overlay',
|
|
296
|
-
border: 'var(--theme-hairline, 1px) solid',
|
|
297
|
-
borderColor: 'border.default',
|
|
298
|
-
borderRadius: 'var(--theme-radius-card, 6px)',
|
|
299
|
-
boxShadow: 'var(--theme-shadow, none)',
|
|
300
|
-
}, children: [_jsx(Text, { sx: { fontWeight: 600 }, children: "A dialog" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1 }, children: "The assistant steps aside while this is open." })] })), _jsx(AssistantStage, { character: "paperclip", state: state, place: { right: 48, bottom: 48 }, stageRef: stageRef, onDragStart: () => undefined, open: picture === 'open', onToggle: () => undefined, balloon: balloon, balloonDisplay: picture === 'current' ? 'current' : 'history', insist: !!balloon, onDismiss: () => undefined })] }));
|
|
253
|
+
return (_jsxs(Box, { flex: 1, position: "relative", children: [picture === 'open' && _jsx(OpenConversation, {}), picture === 'aside' && (_jsxs(Box, { role: "dialog", "aria-label": "A dialog over the assistant", position: "fixed", right: 24, bottom: 24, width: 280, height: 160, p: 3, zIndex: 2000, bg: "canvas.overlay", border: "var(--theme-hairline, 1px) solid", borderColor: "border.default", borderRadius: "var(--theme-radius-card, 6px)", boxShadow: "var(--theme-shadow, none)", children: [_jsx(Text, { sx: { fontWeight: 600 }, children: "A dialog" }), _jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1 }, children: "The assistant steps aside while this is open." })] })), _jsx(AssistantStage, { character: "paperclip", state: state, place: { right: 48, bottom: 48 }, stageRef: stageRef, onDragStart: () => undefined, open: picture === 'open', onToggle: () => undefined, balloon: balloon, balloonDisplay: picture === 'current' ? 'current' : 'history', insist: !!balloon, onDismiss: () => undefined })] }));
|
|
301
254
|
}
|
|
302
255
|
/** What the open picture's conversation holds. */
|
|
303
256
|
const OPEN_CONVERSATION = [
|
|
@@ -316,37 +269,10 @@ function OpenConversation() {
|
|
|
316
269
|
const [input, setInput] = useState('');
|
|
317
270
|
const height = conversationBalloonHeight(window.innerHeight);
|
|
318
271
|
const left = window.innerWidth - 48 - CONVERSATION_BALLOON_WIDTH;
|
|
319
|
-
return (_jsxs(Box, { "data-conversation-balloon": "", sx: {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
width: `${CONVERSATION_BALLOON_WIDTH}px`,
|
|
324
|
-
height: `${height}px`,
|
|
325
|
-
zIndex: 1001,
|
|
326
|
-
display: 'flex',
|
|
327
|
-
flexDirection: 'column',
|
|
328
|
-
bg: 'canvas.default',
|
|
329
|
-
border: '1px solid',
|
|
330
|
-
borderColor: 'border.default',
|
|
331
|
-
boxShadow: 'shadow.extra-large',
|
|
332
|
-
...conversationBalloonSx({
|
|
333
|
-
edge: 'bottom',
|
|
334
|
-
at: balloonTailAt(window.innerWidth - 48 - 44, left),
|
|
335
|
-
}, 'canvas.subtle'),
|
|
336
|
-
}, children: [_jsx(ConversationBalloonClose, { onClose: () => undefined }), _jsx(ConversationBalloonHeader, { count: OPEN_CONVERSATION.length - 1 }), _jsxs(Box, { sx: {
|
|
337
|
-
flex: 1,
|
|
338
|
-
minHeight: 0,
|
|
339
|
-
display: 'flex',
|
|
340
|
-
flexDirection: 'column',
|
|
341
|
-
borderRadius: 'var(--theme-radius-bubble, 16px)',
|
|
342
|
-
overflow: 'hidden',
|
|
343
|
-
}, children: [_jsx(Box, { sx: { flex: 1, minHeight: 0, overflow: 'auto' }, children: _jsx(ChatMessageList, { displayItems: OPEN_CONVERSATION, isLoading: false, isStreaming: false, showLoadingIndicator: false, hideMessagesAfterToolUI: false, avatarConfig: { ...AVATARS, showAvatars: true }, padding: 2, emptyContent: null, messagesEndRef: endRef, onRespond: async () => undefined }) }), _jsx(Box, { sx: {
|
|
344
|
-
px: 3,
|
|
345
|
-
pb: 1,
|
|
346
|
-
display: 'flex',
|
|
347
|
-
justifyContent: 'flex-end',
|
|
348
|
-
fontSize: 0,
|
|
349
|
-
}, children: _jsx(DecisionAsk, { ask: async () => ({}) }) }), _jsx(InputPrompt, { input: input, setInput: setInput, isLoading: false, connectionConfirmed: true, placeholder: "Type a message...", autoFocus: false, padding: 2, promptVariant: "lexical", onSend: () => undefined, onStop: () => undefined, showTokenUsage: false, showModelSelector: false, showToolsMenu: false, showSkillsMenu: false, showAgentsMenu: false, codemodeEnabled: false, hasConfigData: false, hasSkillsData: false, isA2AProtocol: false })] })] }));
|
|
272
|
+
return (_jsxs(Box, { "data-conversation-balloon": "", position: "fixed", right: "48px", bottom: `${48 + 88 + 16}px`, width: `${CONVERSATION_BALLOON_WIDTH}px`, height: `${height}px`, zIndex: 1001, display: "flex", flexDirection: "column", bg: "canvas.default", border: "1px solid", borderColor: "border.default", boxShadow: "shadow.extra-large", sx: conversationBalloonSx({
|
|
273
|
+
edge: 'bottom',
|
|
274
|
+
at: balloonTailAt(window.innerWidth - 48 - 44, left),
|
|
275
|
+
}, 'canvas.subtle'), children: [_jsx(ConversationBalloonClose, { onClose: () => undefined }), _jsx(ConversationBalloonHeader, { count: OPEN_CONVERSATION.length - 1 }), _jsxs(Box, { flex: 1, minHeight: 0, display: "flex", flexDirection: "column", borderRadius: "var(--theme-radius-bubble, 16px)", overflow: "hidden", children: [_jsx(Box, { flex: 1, minHeight: 0, overflow: "auto", children: _jsx(ChatMessageList, { displayItems: OPEN_CONVERSATION, isLoading: false, isStreaming: false, showLoadingIndicator: false, hideMessagesAfterToolUI: false, avatarConfig: { ...AVATARS, showAvatars: true }, padding: 2, emptyContent: null, messagesEndRef: endRef, onRespond: async () => undefined }) }), _jsx(Box, { px: 3, pb: 1, display: "flex", justifyContent: "flex-end", fontSize: 0, children: _jsx(DecisionAsk, { ask: async () => ({}) }) }), _jsx(InputPrompt, { input: input, setInput: setInput, isLoading: false, connectionConfirmed: true, placeholder: "Type a message...", autoFocus: false, padding: 2, promptVariant: "lexical", onSend: () => undefined, onStop: () => undefined, showTokenUsage: false, showModelSelector: false, showToolsMenu: false, showSkillsMenu: false, showAgentsMenu: false, codemodeEnabled: false, hasConfigData: false, hasSkillsData: false, isA2AProtocol: false })] })] }));
|
|
350
276
|
}
|
|
351
277
|
// ---------------------------------------------------------------------------
|
|
352
278
|
// The characters (T-25, T-24)
|
|
@@ -364,5 +290,5 @@ characterReactor.start();
|
|
|
364
290
|
export function CharacterScreen({ character, ready, }) {
|
|
365
291
|
const stageRef = useRef(null);
|
|
366
292
|
const drawn = assistantCharacterNamed(characterReactor, character);
|
|
367
|
-
return (_jsxs(Box, {
|
|
293
|
+
return (_jsxs(Box, { flex: 1, position: "relative", children: [_jsx(AssistantStage, { character: drawn, state: "idle", place: { right: 48, bottom: 48 }, stageRef: stageRef, onDragStart: () => undefined, open: false, onToggle: () => undefined, onDismiss: () => undefined }), ready] }));
|
|
368
294
|
}
|
|
@@ -68,7 +68,7 @@ function Ready() {
|
|
|
68
68
|
*/
|
|
69
69
|
function GalleryScreen() {
|
|
70
70
|
const { characters, ready } = useGalleryCharacters();
|
|
71
|
-
return (_jsxs(Box, {
|
|
71
|
+
return (_jsxs(Box, { flex: 1, bg: "canvas.default", p: 2, overflow: "hidden", children: [_jsx(AssistantGalleryGrid, { characters: characters }), ready && _jsx(Ready, {})] }));
|
|
72
72
|
}
|
|
73
73
|
function Page() {
|
|
74
74
|
if (gallery) {
|
|
@@ -670,8 +670,11 @@ export interface AppUploadsSpec {
|
|
|
670
670
|
/** What the user of an application sees. */
|
|
671
671
|
export interface AppInterfaceSpec {
|
|
672
672
|
layout: AppLayout;
|
|
673
|
-
/**
|
|
674
|
-
|
|
673
|
+
/**
|
|
674
|
+
* Its one colour, over whichever theme it runs in; unsaid, it wears the
|
|
675
|
+
* theme's own colours (decided 2026-10-07).
|
|
676
|
+
*/
|
|
677
|
+
accent?: AppAccent;
|
|
675
678
|
/** The theme it runs in by default; the person's own when unsaid (T-30). */
|
|
676
679
|
theme?: AppThemeSpec;
|
|
677
680
|
welcome: string;
|
package/lib/types/protocol.d.ts
CHANGED
|
@@ -67,6 +67,8 @@ export interface ProtocolAdapterConfig {
|
|
|
67
67
|
maxReconnectAttempts?: number;
|
|
68
68
|
/** Request timeout in milliseconds */
|
|
69
69
|
timeout?: number;
|
|
70
|
+
/** The fetch to use; a phone passes one whose response body streams (e.g. `expo/fetch`). Default: the global `fetch`. */
|
|
71
|
+
fetch?: typeof fetch;
|
|
70
72
|
/** Additional protocol-specific options */
|
|
71
73
|
options?: Record<string, unknown>;
|
|
72
74
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalised approval record. Both snake_case (server-native) and
|
|
3
|
+
* camelCase (TypeScript-idiomatic) keys are present so existing UI
|
|
4
|
+
* consumers keep working regardless of which naming they read.
|
|
5
|
+
*/
|
|
6
|
+
export type ApprovalRecord = {
|
|
7
|
+
id: string;
|
|
8
|
+
agent_id: string;
|
|
9
|
+
agentId: string;
|
|
10
|
+
runtime_name: string;
|
|
11
|
+
runtimeName: string;
|
|
12
|
+
tool_name: string;
|
|
13
|
+
toolName: string;
|
|
14
|
+
tool_call_id?: string;
|
|
15
|
+
toolCallId?: string;
|
|
16
|
+
tool_args: Record<string, unknown>;
|
|
17
|
+
toolArgs: Record<string, unknown>;
|
|
18
|
+
status: string;
|
|
19
|
+
note?: string | null;
|
|
20
|
+
requester_uid?: string;
|
|
21
|
+
requesterUid?: string;
|
|
22
|
+
requested_by?: string;
|
|
23
|
+
requestedBy?: string;
|
|
24
|
+
resolved_by?: string;
|
|
25
|
+
resolvedBy?: string;
|
|
26
|
+
resolved_at?: string;
|
|
27
|
+
resolvedAt?: string;
|
|
28
|
+
created_at: string;
|
|
29
|
+
createdAt: string;
|
|
30
|
+
updated_at?: string;
|
|
31
|
+
updatedAt?: string;
|
|
32
|
+
read?: boolean;
|
|
33
|
+
};
|
|
34
|
+
/** An approval as the server or the `/ws` stream sends it; null without an id. */
|
|
35
|
+
export declare function normalizeApproval(raw: unknown): ApprovalRecord | null;
|
|
36
|
+
/** An approval's arguments, under either key: the web reads `tool_args`, mobile `toolArgs`. */
|
|
37
|
+
export type ApprovalArgs = {
|
|
38
|
+
tool_args?: Record<string, unknown>;
|
|
39
|
+
toolArgs?: Record<string, unknown>;
|
|
40
|
+
};
|
|
41
|
+
/** A message it asks to send, as the approval carries it, whole (LOOP W-05). */
|
|
42
|
+
export type MessageAsked = {
|
|
43
|
+
to: string;
|
|
44
|
+
cc: string;
|
|
45
|
+
bcc: string;
|
|
46
|
+
subject: string;
|
|
47
|
+
body: string;
|
|
48
|
+
/** It forwards a message received, named by its id. */
|
|
49
|
+
forwards: boolean;
|
|
50
|
+
/** A reply, in the conversation it names. */
|
|
51
|
+
replies: boolean;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The message a send asks for, to show whole before *Approve* (LOOP W-05):
|
|
55
|
+
* an approval whose arguments say to whom and what. `null` for anything else.
|
|
56
|
+
*/
|
|
57
|
+
export declare function messageOfApproval(approval: ApprovalArgs): MessageAsked | null;
|
|
58
|
+
/** Why it was asked: the rule (`_rule`) or the check (`_check`). */
|
|
59
|
+
export declare const whyAsked: (approval: ApprovalArgs) => string;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
function str(value) {
|
|
6
|
+
return typeof value === 'string' ? value : value == null ? '' : String(value);
|
|
7
|
+
}
|
|
8
|
+
/** An approval as the server or the `/ws` stream sends it; null without an id. */
|
|
9
|
+
export function normalizeApproval(raw) {
|
|
10
|
+
if (!raw || typeof raw !== 'object')
|
|
11
|
+
return null;
|
|
12
|
+
const rec = raw;
|
|
13
|
+
const idSource = rec.id ?? rec.approval_id ?? rec.approvalId;
|
|
14
|
+
const id = typeof idSource === 'string' ? idSource : undefined;
|
|
15
|
+
if (!id)
|
|
16
|
+
return null;
|
|
17
|
+
const agent = str(rec.agent_id ?? rec.agentId);
|
|
18
|
+
const pod = str(rec.runtime_name ?? rec.runtimeName);
|
|
19
|
+
const tool = str(rec.tool_name ?? rec.toolName ?? 'unknown');
|
|
20
|
+
const toolCall = rec.tool_call_id ?? rec.toolCallId;
|
|
21
|
+
const args = rec.tool_args ??
|
|
22
|
+
rec.toolArgs ??
|
|
23
|
+
{};
|
|
24
|
+
const created = str(rec.created_at ?? rec.createdAt);
|
|
25
|
+
const updated = str(rec.updated_at ?? rec.updatedAt);
|
|
26
|
+
const resolvedBy = str(rec.resolved_by ?? rec.resolvedBy);
|
|
27
|
+
const requesterUid = str(rec.requester_uid ?? rec.requesterUid);
|
|
28
|
+
const requestedBy = str(rec.requested_by ?? rec.requestedBy);
|
|
29
|
+
const resolvedAtRaw = str(rec.resolved_at ?? rec.resolvedAt);
|
|
30
|
+
const status = str(rec.status ?? 'pending');
|
|
31
|
+
// Keep reviewer metadata from WS payloads. If this is dropped during
|
|
32
|
+
// normalization, downstream pages receive status=approved/rejected but no
|
|
33
|
+
// reviewer/timestamp context and can incorrectly render "review pending".
|
|
34
|
+
// Some producers only set updated_at on decision; for non-pending statuses,
|
|
35
|
+
// fall back to updated_at so UI can still show a decision time.
|
|
36
|
+
const resolvedAt = resolvedAtRaw || (status !== 'pending' ? updated || undefined : undefined);
|
|
37
|
+
return {
|
|
38
|
+
id,
|
|
39
|
+
agent_id: agent,
|
|
40
|
+
agentId: agent,
|
|
41
|
+
runtime_name: pod,
|
|
42
|
+
runtimeName: pod,
|
|
43
|
+
tool_name: tool,
|
|
44
|
+
toolName: tool,
|
|
45
|
+
tool_call_id: typeof toolCall === 'string' ? toolCall : undefined,
|
|
46
|
+
toolCallId: typeof toolCall === 'string' ? toolCall : undefined,
|
|
47
|
+
tool_args: args,
|
|
48
|
+
toolArgs: args,
|
|
49
|
+
status,
|
|
50
|
+
note: typeof rec.note === 'string'
|
|
51
|
+
? rec.note
|
|
52
|
+
: rec.note === null
|
|
53
|
+
? null
|
|
54
|
+
: undefined,
|
|
55
|
+
requester_uid: requesterUid || undefined,
|
|
56
|
+
requesterUid: requesterUid || undefined,
|
|
57
|
+
requested_by: requestedBy || undefined,
|
|
58
|
+
requestedBy: requestedBy || undefined,
|
|
59
|
+
resolved_by: resolvedBy || undefined,
|
|
60
|
+
resolvedBy: resolvedBy || undefined,
|
|
61
|
+
resolved_at: resolvedAt,
|
|
62
|
+
resolvedAt: resolvedAt,
|
|
63
|
+
created_at: created,
|
|
64
|
+
createdAt: created,
|
|
65
|
+
updated_at: updated || undefined,
|
|
66
|
+
updatedAt: updated || undefined,
|
|
67
|
+
read: typeof rec.read === 'boolean' ? rec.read : undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const argsOf = (approval) => approval.tool_args ?? approval.toolArgs ?? {};
|
|
71
|
+
const said = (approval, key) => {
|
|
72
|
+
const value = argsOf(approval)[key];
|
|
73
|
+
return typeof value === 'string' ? value : '';
|
|
74
|
+
};
|
|
75
|
+
const addressed = (value) => Array.isArray(value)
|
|
76
|
+
? value.filter(item => typeof item === 'string').join(', ')
|
|
77
|
+
: typeof value === 'string'
|
|
78
|
+
? value
|
|
79
|
+
: '';
|
|
80
|
+
/**
|
|
81
|
+
* The message a send asks for, to show whole before *Approve* (LOOP W-05):
|
|
82
|
+
* an approval whose arguments say to whom and what. `null` for anything else.
|
|
83
|
+
*/
|
|
84
|
+
export function messageOfApproval(approval) {
|
|
85
|
+
const args = argsOf(approval);
|
|
86
|
+
const to = addressed(args.to);
|
|
87
|
+
if (!to || typeof args.body !== 'string') {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
to,
|
|
92
|
+
cc: addressed(args.cc),
|
|
93
|
+
bcc: addressed(args.bcc),
|
|
94
|
+
subject: said(approval, 'subject'),
|
|
95
|
+
body: args.body,
|
|
96
|
+
forwards: Boolean(said(approval, 'forward_message_id')),
|
|
97
|
+
replies: Boolean(said(approval, 'thread_id') || said(approval, 'in_reply_to')),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** Why it was asked: the rule (`_rule`) or the check (`_check`). */
|
|
101
|
+
export const whyAsked = (approval) => said(approval, '_rule') || said(approval, '_check');
|