@datalayer/agent-runtimes 1.5.44 → 1.5.46
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/dist-embed/chunks/{ChatView-kCmBbnDa.js → ChatView-Dy-IgGFL.js} +1 -1
- package/dist-embed/datalayer-app-main.js +99 -94
- package/lib/apps/plugins/chat/ChatView.js +2 -2
- package/lib/chat/ChatFloating.js +1 -1
- package/lib/chat/base/ChatBase.js +23 -8
- package/lib/chat/messages/ChatMessageList.d.ts +7 -1
- package/lib/chat/messages/ChatMessageList.js +8 -2
- package/lib/portable/hosts/__tests__/fixtures/lock-with-jupyter.json +40 -0
- package/lib/portable/hosts/__tests__/fixtures/npm-ls-with-jupyter.json +21 -0
- package/lib/portable/hosts/agentClient.d.ts +167 -0
- package/lib/portable/hosts/agentClient.js +346 -0
- package/lib/portable/hosts/approvalRenderers.d.ts +43 -0
- package/lib/portable/hosts/approvalRenderers.js +180 -0
- package/lib/portable/hosts/client.d.ts +95 -0
- package/lib/portable/hosts/client.js +174 -0
- package/lib/portable/hosts/contracts.d.ts +199 -0
- package/lib/portable/hosts/contracts.js +235 -0
- package/lib/portable/hosts/contracts.json +381 -0
- package/lib/portable/hosts/deadLetters.d.ts +41 -0
- package/lib/portable/hosts/deadLetters.js +124 -0
- package/lib/portable/hosts/deliveries.d.ts +76 -0
- package/lib/portable/hosts/deliveries.js +147 -0
- package/lib/portable/hosts/forbiddenGraph.d.ts +77 -0
- package/lib/portable/hosts/forbiddenGraph.js +174 -0
- package/lib/portable/hosts/health.d.ts +35 -0
- package/lib/portable/hosts/health.js +92 -0
- package/lib/portable/hosts/index.d.ts +40 -0
- package/lib/portable/hosts/index.js +45 -0
- package/lib/portable/hosts/observability.d.ts +67 -0
- package/lib/portable/hosts/observability.js +171 -0
- package/lib/portable/hosts/postgresQueue.d.ts +55 -0
- package/lib/portable/hosts/postgresQueue.js +217 -0
- package/lib/portable/hosts/queue.d.ts +47 -0
- package/lib/portable/hosts/queue.js +63 -0
- package/lib/portable/hosts/quotas.d.ts +81 -0
- package/lib/portable/hosts/quotas.js +147 -0
- package/lib/portable/hosts/rateLimit.d.ts +13 -0
- package/lib/portable/hosts/rateLimit.js +8 -0
- package/lib/portable/hosts/resolution.d.ts +54 -0
- package/lib/portable/hosts/resolution.js +46 -0
- package/lib/portable/hosts/schema.d.ts +19 -0
- package/lib/portable/hosts/schema.js +81 -0
- package/lib/portable/hosts/secretCipher.d.ts +23 -0
- package/lib/portable/hosts/secretCipher.js +45 -0
- package/lib/portable/hosts/sessionUid.d.ts +7 -0
- package/lib/portable/hosts/sessionUid.js +28 -0
- package/lib/portable/hosts/turnState.d.ts +45 -0
- package/lib/portable/hosts/turnState.js +113 -0
- package/lib/portable/index.d.ts +2 -0
- package/lib/portable/index.js +3 -0
- package/lib/portable/semantic-events.json +480 -0
- package/lib/portable/semantic.d.ts +78 -0
- package/lib/portable/semantic.js +100 -0
- package/lib/portable/semanticOfAgUi.d.ts +41 -0
- package/lib/portable/semanticOfAgUi.js +108 -0
- package/lib/protocols/A2AAdapter.js +19 -0
- package/lib/protocols/VercelAIAdapter.js +11 -0
- package/lib/specs/voices.js +62 -0
- package/lib/test-setup.js +17 -14
- package/lib/types/chat.d.ts +10 -0
- package/lib/voice/ComposerVoice.js +12 -3
- package/lib/voice/captions.d.ts +31 -0
- package/lib/voice/captions.js +156 -0
- package/lib/voice/handsFree.d.ts +12 -0
- package/lib/voice/handsFree.js +18 -0
- package/lib/voice/serverHearing.d.ts +29 -0
- package/lib/voice/serverHearing.js +78 -2
- package/package.json +1 -1
- package/scripts/voice/measure.py +77 -17
|
@@ -1702,8 +1702,8 @@ export default function ChatView({ workspace }) {
|
|
|
1702
1702
|
// opening stops it, and *Stop speaking* while it speaks.
|
|
1703
1703
|
speaking: speaking, onStopSpeaking: () => controlsRef.current?.stopSpeaking?.(),
|
|
1704
1704
|
// Hands-free (VO-12, VO-13): the run in progress, stopped when
|
|
1705
|
-
// the person speaks over it.
|
|
1706
|
-
running: busy, onStopRun: () => controlsRef.current?.stop?.(), onInterimChange: setVoiceInterim,
|
|
1705
|
+
// the person speaks over it, and the answer marked interrupted.
|
|
1706
|
+
running: busy, onStopRun: () => controlsRef.current?.stop?.(), onInterrupted: () => controlsRef.current?.markInterrupted(), onInterimChange: setVoiceInterim,
|
|
1707
1707
|
// The menu offers when answers are heard where the
|
|
1708
1708
|
// application lets them be (VO-21, VO-30).
|
|
1709
1709
|
answers: Boolean(config?.voice?.output &&
|
package/lib/chat/ChatFloating.js
CHANGED
|
@@ -1030,7 +1030,7 @@ export function ChatFloating({ endpoint, protocol: protocolProp, useStore: useSt
|
|
|
1030
1030
|
...poweredByProps,
|
|
1031
1031
|
}, description: description, onStateUpdate: onStateUpdate, onNewChat: onNewChat, suggestions: suggestions, submitOnSuggestionClick: submitOnSuggestionClick, hideMessagesAfterToolUI: hideMessagesAfterToolUI, avatarConfig: {
|
|
1032
1032
|
showAvatars: true,
|
|
1033
|
-
}, placeholder: "Type a message...", backgroundColor: "canvas.subtle", frontendTools: frontendTools, showModelSelector: showModelSelector, availableModels: availableModels, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, showTokenUsage: showTokenUsage, showContextRing: showContextRing, runtimeId: runtimeId, historyEndpoint: historyEndpoint, historyAuthToken: historyAuthToken, pendingPrompt: pendingPrompt ?? suggestionPrompt, inspector: inspector, showInformation: showInformation, onInformationClick: onInformationClick, onToolCallStart: onToolCallStart, onToolCallComplete: onToolCallComplete, showToolApprovalBanner: showToolApprovalBanner, pendingApprovals: pendingApprovals, onApproveApproval: onApproveApproval, onRejectApproval: onRejectApproval, voice: voice, voiceSpeaking: speakerState.speaking, onStopSpeaking: speaker ? stopSpeaking : undefined, onReadAloud: speaker ? readAnswerAloud : undefined, ...panelProps, ...assistantChat, onLoadingChange: handleLoadingChange, onDisplayItemsChange: handleDisplayItemsChange, onSendReady: handleSendReady, children: conversationBalloon && currentOpen ? (
|
|
1033
|
+
}, placeholder: "Type a message...", backgroundColor: "canvas.subtle", frontendTools: frontendTools, showModelSelector: showModelSelector, availableModels: availableModels, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, showTokenUsage: showTokenUsage, showContextRing: showContextRing, runtimeId: runtimeId, historyEndpoint: historyEndpoint, historyAuthToken: historyAuthToken, pendingPrompt: pendingPrompt ?? suggestionPrompt, inspector: inspector, showInformation: showInformation, onInformationClick: onInformationClick, onToolCallStart: onToolCallStart, onToolCallComplete: onToolCallComplete, showToolApprovalBanner: showToolApprovalBanner, pendingApprovals: pendingApprovals, onApproveApproval: onApproveApproval, onRejectApproval: onRejectApproval, voice: voice, voiceSpeaking: speakerState.speaking, voiceSentence: speakerState.sentence, onStopSpeaking: speaker ? stopSpeaking : undefined, onReadAloud: speaker ? readAnswerAloud : undefined, ...panelProps, ...assistantChat, onLoadingChange: handleLoadingChange, onDisplayItemsChange: handleDisplayItemsChange, onSendReady: handleSendReady, children: conversationBalloon && currentOpen ? (
|
|
1034
1034
|
// Current: the one thing said or done now, over the composer.
|
|
1035
1035
|
_jsx(Box, { px: 3, pt: 3, pb: 2, overflow: "hidden", children: _jsx(CurrentBalloonBody, { text: toolSaid ? undefined : (currentWords ?? description), fullText: toolSaid ? undefined : currentWhole, tool: toolSaid, busy: atWork, speaking: answering && atWork, waiting: atWork && !answering }) })) : (children) }))] }));
|
|
1036
1036
|
/*
|
|
@@ -52,11 +52,13 @@ import { EphemeralSurfaceControl } from '../EphemeralSurfaceControl';
|
|
|
52
52
|
import { ChatMessageList, } from '../messages/ChatMessageList';
|
|
53
53
|
import { InputPrompt } from '../prompt/InputPrompt';
|
|
54
54
|
import { ComposerVoice, spokenOf } from '../../voice/ComposerVoice';
|
|
55
|
+
import { answerSpokenOver } from '../../voice/handsFree';
|
|
55
56
|
import { ReadAloud, readsAloud } from '../../voice/ReadAloud';
|
|
56
57
|
import { settledVoice } from '../../voice/chatVoice';
|
|
57
58
|
import { useVoiceSettings } from '../../voice/useVoiceSettings';
|
|
58
59
|
import { useVoiceSpeaker } from '../../voice/useVoiceSpeaker';
|
|
59
60
|
import { useSpokenAnswers } from '../../voice/useSpokenAnswers';
|
|
61
|
+
import { saidMessageId, useSaidSentence } from '../../voice/captions';
|
|
60
62
|
import { lineToSay } from '../../voice/sayAloud';
|
|
61
63
|
import { isFloatingChatViewMode } from '../viewModes';
|
|
62
64
|
import { ToolApprovalBanner, ToolApprovalDialog, } from '../tools';
|
|
@@ -424,7 +426,7 @@ function ChatBaseInner({ title, subtitle, showHeader = false, showTokenUsage = t
|
|
|
424
426
|
// Mode selection
|
|
425
427
|
useStore: useStoreMode = true, protocol: protocolRaw, onSendMessage, onSendReady, onLoadingChange, onSpeakingChange, onContextSnapshot, enableStreaming = false,
|
|
426
428
|
// Extended props
|
|
427
|
-
brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderHeader, showTurnFooter = true, notebookToolSurfacesId, footerContent, welcome, trailingContent, voice, voiceSpeaking = false, onStopSpeaking, onReadAloud, showInformation = false, onInformationClick, headerContent, children, borderRadius, backgroundColor, border, boxShadow, compact = false, placeholder, description = 'Start a conversation with the AI agent.', onStateUpdate, onNewChat, onClear, onMessagesChange, onItemsChange, onDisplayItemsChange, autoFocus = false, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, focusTrigger, frontendTools: frontendToolsProp, enableEphemeralNotebook = false, initialEphemeralNotebookOpen = true, onEphemeralNotebookOpenChange, enableEphemeralDocument = false, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed = false, onExpandFromCollapsed, ephemeralNotebookToolbar, ephemeralNotebookToolbarExtraItems, ephemeralDocumentToolbarExtraItems, ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration, ephemeralRuntimeOverride,
|
|
429
|
+
brandIcon, avatarConfig, headerButtons, showPoweredBy = false, poweredByProps, emptyState, renderHeader, showTurnFooter = true, notebookToolSurfacesId, footerContent, welcome, trailingContent, voice, voiceSpeaking = false, voiceSentence, onStopSpeaking, onReadAloud, showInformation = false, onInformationClick, headerContent, children, borderRadius, backgroundColor, border, boxShadow, compact = false, placeholder, description = 'Start a conversation with the AI agent.', onStateUpdate, onNewChat, onClear, onMessagesChange, onItemsChange, onDisplayItemsChange, autoFocus = false, suggestions, submitOnSuggestionClick = true, hideMessagesAfterToolUI = false, focusTrigger, frontendTools: frontendToolsProp, enableEphemeralNotebook = false, initialEphemeralNotebookOpen = true, onEphemeralNotebookOpenChange, enableEphemeralDocument = false, initialEphemeralSurfaceMode, onEphemeralSurfaceModeChange, collapsed = false, onExpandFromCollapsed, ephemeralNotebookToolbar, ephemeralNotebookToolbarExtraItems, ephemeralDocumentToolbarExtraItems, ephemeralNotebookCollaborationProvider, ephemeralNotebookCollaborationDocumentId, ephemeralDocumentCollaboration, ephemeralRuntimeOverride,
|
|
428
430
|
// Tool invocation hooks
|
|
429
431
|
onToolCallStart, onToolCallComplete, inspector: inspectorGiven,
|
|
430
432
|
// Identity/Authorization props
|
|
@@ -2974,6 +2976,10 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
2974
2976
|
const ownVoice = useVoiceSpeaker(settled, !hostSpeaks);
|
|
2975
2977
|
useSpokenAnswers(displayItems, isLoading, !hostSpeaks && settled?.output === 'always', ownVoice.speaker);
|
|
2976
2978
|
const speakingNow = voiceSpeaking || ownVoice.state.speaking;
|
|
2979
|
+
// The sentence being said, marked in the conversation (VO-65).
|
|
2980
|
+
const sentenceSaid = hostSpeaks ? voiceSentence : ownVoice.state.sentence;
|
|
2981
|
+
const saidConversationRef = useRef(null);
|
|
2982
|
+
useSaidSentence(saidConversationRef, sentenceSaid, displayItems);
|
|
2977
2983
|
/*
|
|
2978
2984
|
* A line the agent wants heard, not written (VO-35): a caption while the
|
|
2979
2985
|
* run goes on, said by the speaker where answers are said by themselves.
|
|
@@ -3001,6 +3007,14 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3001
3007
|
}, [isLoading]);
|
|
3002
3008
|
// Answers spoken over, hands-free (VOICE.md VO-13): marked *interrupted*.
|
|
3003
3009
|
const [interruptedIds, setInterruptedIds] = useState(() => new Set());
|
|
3010
|
+
// The conversation as shown, read when the person speaks over an answer.
|
|
3011
|
+
const shownItemsRef = useRef([]);
|
|
3012
|
+
const markInterrupted = useCallback(() => {
|
|
3013
|
+
const spokenOver = answerSpokenOver(shownItemsRef.current.filter((item) => !isToolCallMessage(item)));
|
|
3014
|
+
if (spokenOver) {
|
|
3015
|
+
setInterruptedIds(ids => new Set([...ids, spokenOver]));
|
|
3016
|
+
}
|
|
3017
|
+
}, []);
|
|
3004
3018
|
const stopSpeaking = onStopSpeaking ?? (ownVoice.speaker ? ownVoice.stop : undefined);
|
|
3005
3019
|
const readAnswerAloud = onReadAloud ?? (ownVoice.speaker ? ownVoice.read : undefined);
|
|
3006
3020
|
const handleSend = useCallback(async (messageOverride,
|
|
@@ -3370,6 +3384,8 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3370
3384
|
thread: () => threadIdRef.current,
|
|
3371
3385
|
// Its speaker, for a host whose composer holds the microphone (VO-10).
|
|
3372
3386
|
stopSpeaking,
|
|
3387
|
+
// The answer spoken over, marked for a host's own composer (VO-13).
|
|
3388
|
+
markInterrupted,
|
|
3373
3389
|
});
|
|
3374
3390
|
return () => {
|
|
3375
3391
|
onSendReady(null);
|
|
@@ -3382,6 +3398,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3382
3398
|
handleStop,
|
|
3383
3399
|
handleNewChat,
|
|
3384
3400
|
stopSpeaking,
|
|
3401
|
+
markInterrupted,
|
|
3385
3402
|
]);
|
|
3386
3403
|
// Streaming state, for a host that draws the prompt.
|
|
3387
3404
|
useEffect(() => {
|
|
@@ -3748,6 +3765,8 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3748
3765
|
...displayItems,
|
|
3749
3766
|
]
|
|
3750
3767
|
: displayItems;
|
|
3768
|
+
shownItemsRef.current = welcomedItems;
|
|
3769
|
+
const saidId = saidMessageId(welcomedItems, sentenceSaid);
|
|
3751
3770
|
/*
|
|
3752
3771
|
* *Read aloud* under each answer the agent has finished (VOICE.md VO-21),
|
|
3753
3772
|
* when the host reads answers aloud on request.
|
|
@@ -3763,7 +3782,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3763
3782
|
return (_jsxs(_Fragment, { children: [interruptedIds.has(message.id) ? (_jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', fontStyle: 'italic' }, "data-interrupted": "", children: "interrupted" })) : null, readAnswerAloud && readsAloud(message, text, writingId) ? (_jsx(ReadAloud, { onRead: () => readAnswerAloud(text) })) : null] }));
|
|
3764
3783
|
}
|
|
3765
3784
|
: undefined;
|
|
3766
|
-
const messagesContent = children ? (children) : (_jsxs(Box, { display: "flex", flexDirection: "column", minHeight: 0,
|
|
3785
|
+
const messagesContent = children ? (children) : (_jsxs(Box, { ref: saidConversationRef, display: "flex", flexDirection: "column", minHeight: 0,
|
|
3767
3786
|
/*
|
|
3768
3787
|
A reading column, not a full-bleed sheet.
|
|
3769
3788
|
|
|
@@ -3776,16 +3795,12 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
|
|
|
3776
3795
|
// Tool calls are drawn by the page's Reactor plugins; a named
|
|
3777
3796
|
// notebook's cell surfaces stand in for the default row of its tools,
|
|
3778
3797
|
// so every chat with a headless notebook gets the same transcript.
|
|
3779
|
-
notebookToolSurfacesId: notebookToolSurfacesId, approvalConfig: approvalConfig, messagesEndRef: messagesEndRef, onRespond: handleRespond, showTurnFooters: showTurnFooter, renderMessageFooter: readAloudFooter, agentUsage: agentUsage, onRemoveItems: handleRemoveItems, mcpServers: filteredMcpServers, emptyContent: launching ? null : (_jsx(ChatEmptyState, { emptyState: emptyState, brandIcon: brandIcon, description: resolvedDescription, suggestions: resolvedSuggestions, submitOnSuggestionClick: submitOnSuggestionClick, onSuggestionSubmit: handleSuggestionSubmit, onSuggestionFill: handleSuggestionFill })) }), trailingContent] }));
|
|
3798
|
+
notebookToolSurfacesId: notebookToolSurfacesId, approvalConfig: approvalConfig, messagesEndRef: messagesEndRef, onRespond: handleRespond, showTurnFooters: showTurnFooter, renderMessageFooter: readAloudFooter, saidMessageId: saidId, agentUsage: agentUsage, onRemoveItems: handleRemoveItems, mcpServers: filteredMcpServers, emptyContent: launching ? null : (_jsx(ChatEmptyState, { emptyState: emptyState, brandIcon: brandIcon, description: resolvedDescription, suggestions: resolvedSuggestions, submitOnSuggestionClick: submitOnSuggestionClick, onSuggestionSubmit: handleSuggestionSubmit, onSuggestionFill: handleSuggestionFill })) }), trailingContent] }));
|
|
3780
3799
|
// The microphone, in the composer's footer (VO-10, VO-11).
|
|
3781
3800
|
const voiceControl = settled && settled.input !== 'off' ? (_jsx(ComposerVoice, { voice: settled, input: input, setInput: setInput, disabled: disabled || launching, speaking: speakingNow, onStopSpeaking: stopSpeaking,
|
|
3782
3801
|
// Hands-free (VO-12, VO-13): the run in progress, stopped when the
|
|
3783
3802
|
// person speaks over it, and the answer marked interrupted.
|
|
3784
|
-
running: isLoading, onStopRun: handleStop, onInterrupted:
|
|
3785
|
-
if (writingId) {
|
|
3786
|
-
setInterruptedIds(ids => new Set([...ids, writingId]));
|
|
3787
|
-
}
|
|
3788
|
-
}, onInterimChange: setVoiceInterim,
|
|
3803
|
+
running: isLoading, onStopRun: handleStop, onInterrupted: markInterrupted, onInterimChange: setVoiceInterim,
|
|
3789
3804
|
// The menu offers when answers are heard where the application
|
|
3790
3805
|
// lets them be (VO-21, VO-30).
|
|
3791
3806
|
answers: Boolean(voice?.output && voice.output !== 'off' && voice.speechUrl), onSpoken: metadata => {
|
|
@@ -78,6 +78,12 @@ export interface ChatMessageListProps {
|
|
|
78
78
|
* `note`, which has no footer of its own.
|
|
79
79
|
*/
|
|
80
80
|
renderMessageFooter?: (message: ChatMessage) => ReactNode;
|
|
81
|
+
/**
|
|
82
|
+
* The answer a sentence is being said from (VOICE.md VO-65): its row is
|
|
83
|
+
* marked `aria-current` and `data-voice-saying`, with a bar beside it —
|
|
84
|
+
* the sentence itself is underlined by `useSaidSentence`.
|
|
85
|
+
*/
|
|
86
|
+
saidMessageId?: string;
|
|
81
87
|
/**
|
|
82
88
|
* The MCP servers the agent has, with the tools each serves: a tool call is
|
|
83
89
|
* drawn with the marks of the server it belongs to. Skills, frontend tools
|
|
@@ -109,5 +115,5 @@ export interface SubagentChatPanelProps {
|
|
|
109
115
|
* approval decisions are sent over the shared monitoring socket.
|
|
110
116
|
*/
|
|
111
117
|
export declare function SubagentChatPanel({ toolCallId, subagentName: subagentNameHint, height, avatarConfig, }: SubagentChatPanelProps): React.ReactElement | null;
|
|
112
|
-
export declare function ChatMessageList({ displayItems, actOn, upload, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, notebookToolSurfacesId, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters, agentUsage, onRemoveItems, renderMessageFooter, mcpServers, density, }: ChatMessageListProps): import("react").JSX.Element;
|
|
118
|
+
export declare function ChatMessageList({ displayItems, actOn, upload, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, notebookToolSurfacesId, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters, agentUsage, onRemoveItems, renderMessageFooter, saidMessageId, mcpServers, density, }: ChatMessageListProps): import("react").JSX.Element;
|
|
113
119
|
export {};
|
|
@@ -366,7 +366,7 @@ function DefaultToolCallRenderer({ item, onRespond, mcpServers, density, }) {
|
|
|
366
366
|
// ---------------------------------------------------------------------------
|
|
367
367
|
// Component
|
|
368
368
|
// ---------------------------------------------------------------------------
|
|
369
|
-
export function ChatMessageList({ displayItems, actOn, upload, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, notebookToolSurfacesId, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters = false, agentUsage, onRemoveItems, renderMessageFooter, mcpServers, density = 'comfortable', }) {
|
|
369
|
+
export function ChatMessageList({ displayItems, actOn, upload, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, notebookToolSurfacesId, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters = false, agentUsage, onRemoveItems, renderMessageFooter, saidMessageId, mcpServers, density = 'comfortable', }) {
|
|
370
370
|
/*
|
|
371
371
|
* Who draws a tool call: the Reactor plugins of the page, when it has a
|
|
372
372
|
* platform. Each plugin binds its own tools (`ToolBindings`) and renders its
|
|
@@ -541,7 +541,13 @@ export function ChatMessageList({ displayItems, actOn, upload, isLoading, isStre
|
|
|
541
541
|
}
|
|
542
542
|
// The host's footer, asked once: a row only when it draws something.
|
|
543
543
|
const messageFooter = renderMessageFooter?.(message);
|
|
544
|
-
|
|
544
|
+
// The answer being said (VO-65): marked by a bar and for assistive tech.
|
|
545
|
+
const said = saidMessageId !== undefined && message.id === saidMessageId;
|
|
546
|
+
return (_jsx(Box, { "data-chat-message": message.role, "data-voice-saying": said ? '' : undefined, "aria-current": said ? 'true' : undefined, sx: said
|
|
547
|
+
? {
|
|
548
|
+
boxShadow: 'inset 3px 0 0 var(--borderColor-accent-emphasis, #0969da)',
|
|
549
|
+
}
|
|
550
|
+
: undefined, display: "flex", flexDirection: "column", alignItems: isUser ? 'flex-end' : 'flex-start', px: padding, py: compact ? '2px' : 1, children: _jsxs(Box, { display: "flex", gap: 2, flexDirection: isUser ? 'row-reverse' : 'row', alignItems: "flex-start",
|
|
545
551
|
/*
|
|
546
552
|
Full width, always.
|
|
547
553
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@datalayer/slack",
|
|
3
|
+
"lockfileVersion": 3,
|
|
4
|
+
"packages": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "@datalayer/slack",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@slack/bolt": "5.1.0",
|
|
9
|
+
"@datalayer/agent-runtimes": "1.5.44"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": { "vitest": "4.1.11" }
|
|
12
|
+
},
|
|
13
|
+
"node_modules/@slack/bolt": {
|
|
14
|
+
"version": "5.1.0",
|
|
15
|
+
"dependencies": { "express": "5.0.0" }
|
|
16
|
+
},
|
|
17
|
+
"node_modules/express": { "version": "5.0.0" },
|
|
18
|
+
"node_modules/@datalayer/agent-runtimes": {
|
|
19
|
+
"version": "1.5.44",
|
|
20
|
+
"dependencies": { "@datalayer/jupyter-react": "2.0.0" }
|
|
21
|
+
},
|
|
22
|
+
"node_modules/@datalayer/jupyter-react": {
|
|
23
|
+
"version": "2.0.0",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@jupyterlab/services": "7.0.0",
|
|
26
|
+
"@lumino/widgets": "2.0.0"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"node_modules/@datalayer/jupyter-react/node_modules/@jupyterlab/services": {
|
|
30
|
+
"version": "7.0.0"
|
|
31
|
+
},
|
|
32
|
+
"node_modules/@lumino/widgets": { "version": "2.0.0" },
|
|
33
|
+
"node_modules/vitest": {
|
|
34
|
+
"version": "4.1.11",
|
|
35
|
+
"dev": true,
|
|
36
|
+
"dependencies": { "react-dom": "19.0.0" }
|
|
37
|
+
},
|
|
38
|
+
"node_modules/react-dom": { "version": "19.0.0", "dev": true }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@datalayer/slack",
|
|
3
|
+
"dependencies": {
|
|
4
|
+
"@slack/bolt": {
|
|
5
|
+
"version": "5.1.0",
|
|
6
|
+
"dependencies": { "express": { "version": "5.0.0" } }
|
|
7
|
+
},
|
|
8
|
+
"@datalayer/agent-runtimes": {
|
|
9
|
+
"version": "1.5.44",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@datalayer/jupyter-react": {
|
|
12
|
+
"version": "2.0.0",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@jupyterlab/services": { "version": "7.0.0" },
|
|
15
|
+
"react-dom": { "version": "19.0.0" }
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { type AgUiEvent } from '../agui';
|
|
2
|
+
import type { SemanticEvent } from '../semantic';
|
|
3
|
+
import { type HostExchange } from './contracts';
|
|
4
|
+
/**
|
|
5
|
+
* A turn a host asks for. `provenance` is the host's own thread key (Slack's
|
|
6
|
+
* `ThreadKey`, Discord's): opaque here, read only through the `HostShape`.
|
|
7
|
+
*/
|
|
8
|
+
export type HostTurnRequest<P> = Readonly<{
|
|
9
|
+
/** The deployment the turn runs: one the installation allows. */
|
|
10
|
+
applicationId: string;
|
|
11
|
+
externalEventId: string;
|
|
12
|
+
/** The host user the host speaks for: what the credential exchange names. */
|
|
13
|
+
externalUserId: string;
|
|
14
|
+
principalId: string;
|
|
15
|
+
prompt: string;
|
|
16
|
+
provenance: P;
|
|
17
|
+
sessionId?: string;
|
|
18
|
+
/**
|
|
19
|
+
* How many sessions the thread had before (an explicit start over or
|
|
20
|
+
* another application): a new session's uid is derived with it.
|
|
21
|
+
*/
|
|
22
|
+
sessionGeneration?: number;
|
|
23
|
+
}>;
|
|
24
|
+
export type TurnStream = Readonly<{
|
|
25
|
+
/**
|
|
26
|
+
* The run the stream is of, as its `loop.turn` event names it: known once
|
|
27
|
+
* the stream has said it (what a Stop button names).
|
|
28
|
+
*/
|
|
29
|
+
run: {
|
|
30
|
+
id?: string;
|
|
31
|
+
};
|
|
32
|
+
sessionId: string;
|
|
33
|
+
updates: AsyncIterable<SemanticEvent>;
|
|
34
|
+
}>;
|
|
35
|
+
export interface AgentClient<P> {
|
|
36
|
+
startTurn(request: HostTurnRequest<P>): Promise<TurnStream>;
|
|
37
|
+
stopTurn(sessionId: string, request: HostTurnRequest<P>): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
/** Where a host asks from (`exchange.optional_request.place`): a channel, or a direct conversation. */
|
|
40
|
+
export type Place = Readonly<{
|
|
41
|
+
kind: 'channel' | 'direct';
|
|
42
|
+
id: string;
|
|
43
|
+
}>;
|
|
44
|
+
/**
|
|
45
|
+
* What is the host's own in a turn: the session uid its thread derives
|
|
46
|
+
* (`hostSessionUid`), the place Datalayer checks the channel policy against,
|
|
47
|
+
* the delivery target handed back with every delivery, and what is said when
|
|
48
|
+
* its installation holds no host key.
|
|
49
|
+
*/
|
|
50
|
+
export type HostShape<P> = Readonly<{
|
|
51
|
+
sessionUidOf(request: HostTurnRequest<P>): string;
|
|
52
|
+
placeOf(request: HostTurnRequest<P>): Place;
|
|
53
|
+
targetOf(request: HostTurnRequest<P>): Record<string, unknown>;
|
|
54
|
+
notBound: string;
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* What a turn runs with: a short-lived credential for the linked person and
|
|
58
|
+
* where the deployment's session API is. A bot token is never one.
|
|
59
|
+
*/
|
|
60
|
+
export type HostSession = Readonly<{
|
|
61
|
+
accessToken: string;
|
|
62
|
+
agent: string;
|
|
63
|
+
deploymentUid: string;
|
|
64
|
+
/** Whether the session can be mapped to its host thread for deliveries. */
|
|
65
|
+
bindable: boolean;
|
|
66
|
+
sessionApiUrl: string;
|
|
67
|
+
userToken?: string;
|
|
68
|
+
}>;
|
|
69
|
+
export interface HostCredentials<P> {
|
|
70
|
+
forTurn(request: HostTurnRequest<P>): Promise<HostSession>;
|
|
71
|
+
}
|
|
72
|
+
/** What the installation's key is, for a turn: `undefined` when it was never bound. */
|
|
73
|
+
export type HostKeyLookup<P> = (request: HostTurnRequest<P>) => Promise<string | undefined>;
|
|
74
|
+
/**
|
|
75
|
+
* The host credential exchange (SLACK §4.3, 1): the installation's key and
|
|
76
|
+
* the host user it speaks for, exchanged at ai-agents for a fifteen-minute
|
|
77
|
+
* host token of the linked person and one deployment.
|
|
78
|
+
*/
|
|
79
|
+
export declare class HostKeyCredentials<P> implements HostCredentials<P> {
|
|
80
|
+
readonly apiUrl: string;
|
|
81
|
+
readonly hostKeyFor: HostKeyLookup<P>;
|
|
82
|
+
readonly shape: HostShape<P>;
|
|
83
|
+
readonly fetchImplementation: typeof fetch;
|
|
84
|
+
constructor(apiUrl: string, hostKeyFor: HostKeyLookup<P>, shape: HostShape<P>, fetchImplementation?: typeof fetch);
|
|
85
|
+
exchange(request: HostTurnRequest<P>): Promise<HostExchange>;
|
|
86
|
+
/** The exchange for one host user and deployment, with a key in hand (a delivery, an action). */
|
|
87
|
+
exchangeFor(input: {
|
|
88
|
+
deploymentUid: string;
|
|
89
|
+
externalUserId: string;
|
|
90
|
+
hostKey: string;
|
|
91
|
+
/** Where the turn is asked from: Datalayer refuses a denied channel at once. */
|
|
92
|
+
place?: Place;
|
|
93
|
+
}): Promise<HostExchange>;
|
|
94
|
+
forTurn(request: HostTurnRequest<P>): Promise<HostSession>;
|
|
95
|
+
}
|
|
96
|
+
export declare function hostSessionOf(exchanged: HostExchange): HostSession;
|
|
97
|
+
/**
|
|
98
|
+
* A turn's credentials: the exchange when its installation holds a host
|
|
99
|
+
* key; otherwise, in development only, the developer's own (`fallback`);
|
|
100
|
+
* otherwise a refusal saying the installation is not connected.
|
|
101
|
+
*/
|
|
102
|
+
export declare class InstallationCredentials<P> implements HostCredentials<P> {
|
|
103
|
+
readonly keyed: HostKeyCredentials<P>;
|
|
104
|
+
readonly fallback?: HostCredentials<P> | undefined;
|
|
105
|
+
constructor(keyed: HostKeyCredentials<P>, fallback?: HostCredentials<P> | undefined);
|
|
106
|
+
forTurn(request: HostTurnRequest<P>): Promise<HostSession>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Development only: the developer's own Datalayer key, and their own kept
|
|
110
|
+
* deployment, read from ai-agents as its owner. Never in production.
|
|
111
|
+
*/
|
|
112
|
+
export declare class DevelopmentCredentials<P> implements HostCredentials<P> {
|
|
113
|
+
readonly apiUrl: string;
|
|
114
|
+
readonly token: string;
|
|
115
|
+
readonly fetchImplementation: typeof fetch;
|
|
116
|
+
constructor(apiUrl: string, token: string, fetchImplementation?: typeof fetch);
|
|
117
|
+
forTurn(request: HostTurnRequest<P>): Promise<HostSession>;
|
|
118
|
+
}
|
|
119
|
+
/** One of an application's starters (SLACK S-104): the words shown, and the message it suggests asking. */
|
|
120
|
+
export type Starter = Readonly<{
|
|
121
|
+
label: string;
|
|
122
|
+
message: string;
|
|
123
|
+
}>;
|
|
124
|
+
/** Where an application's starters are read, as one linked person of one installation. */
|
|
125
|
+
export interface StarterSource<K> {
|
|
126
|
+
startersOf(key: K, externalUserId: string, applicationId: string): Promise<readonly Starter[]>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* An application's starters (SLACK S-104), read from its runtime as the
|
|
130
|
+
* linked person: the host credential exchange (no place: what is suggested
|
|
131
|
+
* is private to them), then `GET /api/v1/apps/current` beside the session
|
|
132
|
+
* API it answers. A runtime that does not answer starters is refused.
|
|
133
|
+
*/
|
|
134
|
+
export declare class HostStarters<K, P> implements StarterSource<K> {
|
|
135
|
+
readonly apiUrl: string;
|
|
136
|
+
readonly keyed: HostKeyCredentials<P>;
|
|
137
|
+
readonly hostKeyOf: (key: K) => Promise<string | undefined>;
|
|
138
|
+
readonly fetchImplementation: typeof fetch;
|
|
139
|
+
constructor(apiUrl: string, keyed: HostKeyCredentials<P>, hostKeyOf: (key: K) => Promise<string | undefined>, fetchImplementation?: typeof fetch);
|
|
140
|
+
startersOf(key: K, externalUserId: string, applicationId: string): Promise<readonly Starter[]>;
|
|
141
|
+
}
|
|
142
|
+
/** Each AG-UI event of a run's `text/event-stream` body, as it arrives (`AgUiFrames`). */
|
|
143
|
+
export declare function agUiEventsOf(body: ReadableStream<Uint8Array>): AsyncIterable<AgUiEvent>;
|
|
144
|
+
/**
|
|
145
|
+
* The host's Datalayer client: a turn is started or sent on the deployment's
|
|
146
|
+
* session API (idempotent by the host's event id, SLACK §4.3, 2), its AG-UI
|
|
147
|
+
* stream reduced to semantic events, and its session mapped to the host's
|
|
148
|
+
* thread so that approvals and completions are delivered (§4.3, 3).
|
|
149
|
+
*/
|
|
150
|
+
export declare class HttpAgentClient<P> implements AgentClient<P> {
|
|
151
|
+
#private;
|
|
152
|
+
readonly apiUrl: string;
|
|
153
|
+
readonly credentials: HostCredentials<P>;
|
|
154
|
+
readonly shape: HostShape<P>;
|
|
155
|
+
readonly fetchImplementation: typeof fetch;
|
|
156
|
+
readonly hostKeyFor?: HostKeyLookup<P> | undefined;
|
|
157
|
+
constructor(apiUrl: string, credentials: HostCredentials<P>, shape: HostShape<P>, fetchImplementation?: typeof fetch, hostKeyFor?: HostKeyLookup<P> | undefined);
|
|
158
|
+
startTurn(request: HostTurnRequest<P>): Promise<TurnStream>;
|
|
159
|
+
stopTurn(sessionId: string, request: HostTurnRequest<P>): Promise<void>;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The session's last answer, read with the host token (what a replayed turn
|
|
163
|
+
* and a `turn.completed` delivery show); `null` when the session is not read.
|
|
164
|
+
*/
|
|
165
|
+
export declare function lastAnswer(fetchImplementation: typeof fetch, host: HostSession, sessionId: string): Promise<string | undefined | null>;
|
|
166
|
+
/** A session API or stream URL Datalayer answered: HTTPS, on Datalayer's own origin. */
|
|
167
|
+
export declare function assertTrustedStreamUrl(baseUrl: string, streamUrl: string): void;
|