@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.
Files changed (69) hide show
  1. package/dist-embed/chunks/{ChatView-kCmBbnDa.js → ChatView-Dy-IgGFL.js} +1 -1
  2. package/dist-embed/datalayer-app-main.js +99 -94
  3. package/lib/apps/plugins/chat/ChatView.js +2 -2
  4. package/lib/chat/ChatFloating.js +1 -1
  5. package/lib/chat/base/ChatBase.js +23 -8
  6. package/lib/chat/messages/ChatMessageList.d.ts +7 -1
  7. package/lib/chat/messages/ChatMessageList.js +8 -2
  8. package/lib/portable/hosts/__tests__/fixtures/lock-with-jupyter.json +40 -0
  9. package/lib/portable/hosts/__tests__/fixtures/npm-ls-with-jupyter.json +21 -0
  10. package/lib/portable/hosts/agentClient.d.ts +167 -0
  11. package/lib/portable/hosts/agentClient.js +346 -0
  12. package/lib/portable/hosts/approvalRenderers.d.ts +43 -0
  13. package/lib/portable/hosts/approvalRenderers.js +180 -0
  14. package/lib/portable/hosts/client.d.ts +95 -0
  15. package/lib/portable/hosts/client.js +174 -0
  16. package/lib/portable/hosts/contracts.d.ts +199 -0
  17. package/lib/portable/hosts/contracts.js +235 -0
  18. package/lib/portable/hosts/contracts.json +381 -0
  19. package/lib/portable/hosts/deadLetters.d.ts +41 -0
  20. package/lib/portable/hosts/deadLetters.js +124 -0
  21. package/lib/portable/hosts/deliveries.d.ts +76 -0
  22. package/lib/portable/hosts/deliveries.js +147 -0
  23. package/lib/portable/hosts/forbiddenGraph.d.ts +77 -0
  24. package/lib/portable/hosts/forbiddenGraph.js +174 -0
  25. package/lib/portable/hosts/health.d.ts +35 -0
  26. package/lib/portable/hosts/health.js +92 -0
  27. package/lib/portable/hosts/index.d.ts +40 -0
  28. package/lib/portable/hosts/index.js +45 -0
  29. package/lib/portable/hosts/observability.d.ts +67 -0
  30. package/lib/portable/hosts/observability.js +171 -0
  31. package/lib/portable/hosts/postgresQueue.d.ts +55 -0
  32. package/lib/portable/hosts/postgresQueue.js +217 -0
  33. package/lib/portable/hosts/queue.d.ts +47 -0
  34. package/lib/portable/hosts/queue.js +63 -0
  35. package/lib/portable/hosts/quotas.d.ts +81 -0
  36. package/lib/portable/hosts/quotas.js +147 -0
  37. package/lib/portable/hosts/rateLimit.d.ts +13 -0
  38. package/lib/portable/hosts/rateLimit.js +8 -0
  39. package/lib/portable/hosts/resolution.d.ts +54 -0
  40. package/lib/portable/hosts/resolution.js +46 -0
  41. package/lib/portable/hosts/schema.d.ts +19 -0
  42. package/lib/portable/hosts/schema.js +81 -0
  43. package/lib/portable/hosts/secretCipher.d.ts +23 -0
  44. package/lib/portable/hosts/secretCipher.js +45 -0
  45. package/lib/portable/hosts/sessionUid.d.ts +7 -0
  46. package/lib/portable/hosts/sessionUid.js +28 -0
  47. package/lib/portable/hosts/turnState.d.ts +45 -0
  48. package/lib/portable/hosts/turnState.js +113 -0
  49. package/lib/portable/index.d.ts +2 -0
  50. package/lib/portable/index.js +3 -0
  51. package/lib/portable/semantic-events.json +480 -0
  52. package/lib/portable/semantic.d.ts +78 -0
  53. package/lib/portable/semantic.js +100 -0
  54. package/lib/portable/semanticOfAgUi.d.ts +41 -0
  55. package/lib/portable/semanticOfAgUi.js +108 -0
  56. package/lib/protocols/A2AAdapter.js +19 -0
  57. package/lib/protocols/VercelAIAdapter.js +11 -0
  58. package/lib/specs/voices.js +62 -0
  59. package/lib/test-setup.js +17 -14
  60. package/lib/types/chat.d.ts +10 -0
  61. package/lib/voice/ComposerVoice.js +12 -3
  62. package/lib/voice/captions.d.ts +31 -0
  63. package/lib/voice/captions.js +156 -0
  64. package/lib/voice/handsFree.d.ts +12 -0
  65. package/lib/voice/handsFree.js +18 -0
  66. package/lib/voice/serverHearing.d.ts +29 -0
  67. package/lib/voice/serverHearing.js +78 -2
  68. package/package.json +1 -1
  69. 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 &&
@@ -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
- return (_jsx(Box, { "data-chat-message": message.role, 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",
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;