@datalayer/agent-runtimes 1.3.70 → 1.3.72

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 (44) hide show
  1. package/lib/chat/ChatFloating.d.ts +16 -1
  2. package/lib/chat/ChatFloating.js +62 -17
  3. package/lib/chat/assistant/AssistantStage.d.ts +17 -2
  4. package/lib/chat/assistant/AssistantStage.js +3 -3
  5. package/lib/chat/assistant/BalloonParts.d.ts +55 -0
  6. package/lib/chat/assistant/BalloonParts.js +140 -0
  7. package/lib/chat/assistant/ConversationBalloon.d.ts +5 -0
  8. package/lib/chat/assistant/ConversationBalloon.js +5 -0
  9. package/lib/chat/assistant/SpeechBalloon.d.ts +23 -2
  10. package/lib/chat/assistant/SpeechBalloon.js +22 -8
  11. package/lib/chat/assistant/toolLine.d.ts +69 -0
  12. package/lib/chat/assistant/toolLine.js +118 -0
  13. package/lib/components/teams/A2ATeamGraph.d.ts +12 -0
  14. package/lib/components/teams/A2ATeamGraph.js +35 -8
  15. package/lib/components/teams/NotebookPreview.d.ts +39 -0
  16. package/lib/components/teams/NotebookPreview.js +68 -0
  17. package/lib/components/teams/NotebookPreviewView.d.ts +21 -0
  18. package/lib/components/teams/NotebookPreviewView.js +42 -0
  19. package/lib/components/teams/TeamNotebook.js +16 -1
  20. package/lib/components/teams/a2aTeamFlow.d.ts +5 -0
  21. package/lib/components/teams/a2aTeamFlow.js +11 -0
  22. package/lib/components/teams/index.d.ts +1 -0
  23. package/lib/components/teams/index.js +1 -0
  24. package/lib/components/teams/useA2ATeam.d.ts +8 -0
  25. package/lib/components/teams/useA2ATeam.js +38 -3
  26. package/lib/examples/AgentA2ATeamExample.d.ts +8 -4
  27. package/lib/examples/AgentA2ATeamExample.js +2 -2
  28. package/lib/examples/ChatAssistantExample.js +4 -2
  29. package/lib/examples/ChatAssistantGalleryExample.d.ts +5 -1
  30. package/lib/examples/ChatAssistantGalleryExample.js +40 -10
  31. package/lib/examples/utils/AssistantGalleryGrid.d.ts +14 -11
  32. package/lib/examples/utils/AssistantGalleryGrid.js +42 -1
  33. package/lib/examples/utils/assistantGallery.d.ts +49 -1
  34. package/lib/examples/utils/assistantGallery.js +52 -1
  35. package/lib/loop/apps/appspec.js +6 -0
  36. package/lib/loop/apps/checks.js +2 -0
  37. package/lib/loop/embed/AppEmbed.js +3 -1
  38. package/lib/loop/plugins/assistant/index.d.ts +14 -1
  39. package/lib/loop/plugins/assistant/index.js +39 -13
  40. package/lib/specs/appspecSchema.js +18 -0
  41. package/lib/stories/loop/ReferenceScreens.d.ts +1 -1
  42. package/lib/stories/loop/ReferenceScreens.js +20 -3
  43. package/lib/types/agentspecs.d.ts +8 -0
  44. package/package.json +1 -1
@@ -13,6 +13,12 @@
13
13
  * the runtime to ask (`decisions`), its balloon offers *Ask a decision*: a
14
14
  * typed decision asked of Jev through the runtime, answered there.
15
15
  *
16
+ * Its balloon shows the conversation as `balloon` says (LOOP T-23; the
17
+ * Appspec's `interface.balloon`): a peek of the newest words (`history`, the
18
+ * default), or the one thing being said or done now (`current`). Either way
19
+ * what the agent is doing while a tool runs — the turn's `activity` — is said
20
+ * there, and heard once as it starts.
21
+ *
16
22
  * The workspace's chat is the conversation: on a page layout, its side panel
17
23
  * is opened and closed; where the conversation is always on screen, a click
18
24
  * takes the person to its composer.
@@ -23,6 +29,7 @@ import type { JSX } from 'react';
23
29
  import { type AssistantState } from '../../../chat/assistant/state';
24
30
  import type { PresenceState } from '../../../chat/presence/presenceStatus';
25
31
  import { type ChatTurnSnapshot } from '../../core';
32
+ import type { BalloonDisplay } from '../../../chat/assistant/toolLine';
26
33
  export declare const LOOP_ASSISTANT_PLUGIN_NAME = "@datalayer/loop-plugin-assistant";
27
34
  export type LoopAssistantConfig = {
28
35
  /** The character the application's Appspec names; it wins. */
@@ -45,6 +52,12 @@ export type LoopAssistantConfig = {
45
52
  serverUrl: string;
46
53
  token?: string;
47
54
  };
55
+ /**
56
+ * How the balloon shows the conversation (T-23): a peek of the newest
57
+ * words (`history`, the default), or only what is said or done now
58
+ * (`current`). The Appspec's `interface.balloon`.
59
+ */
60
+ balloon?: BalloonDisplay;
48
61
  };
49
62
  /**
50
63
  * What the character acts out (T-22), from what the application is doing as
@@ -55,6 +68,6 @@ export declare function assistantStateOfTurn(turn: ChatTurnSnapshot, presence: P
55
68
  arriving?: boolean;
56
69
  leaving?: boolean;
57
70
  }): AssistantState;
58
- export declare function LoopAssistant({ app, person, appId, decisions, }: LoopAssistantConfig): JSX.Element | null;
71
+ export declare function LoopAssistant({ app, person, appId, decisions, balloon: display, }: LoopAssistantConfig): JSX.Element | null;
59
72
  export declare const LoopAssistantPlugin: import("@datalayer/reactor").ReactorPlugin<LoopAssistantConfig, unknown, unknown>;
60
73
  export default LoopAssistantPlugin;
@@ -69,7 +69,7 @@ function focusComposer() {
69
69
  const composer = document.querySelector('[data-chat-composer] textarea, [data-chat-composer] [contenteditable="true"]');
70
70
  composer?.focus();
71
71
  }
72
- export function LoopAssistant({ app, person, appId, decisions, }) {
72
+ export function LoopAssistant({ app, person, appId, decisions, balloon: display = 'history', }) {
73
73
  const contributed = useContributions(LoopAssistantCharacter).map(entry => entry.value);
74
74
  const chosen = assistantCharacterFor(contributed, { app, person });
75
75
  const turnEntries = useContributions(LoopChatTurn);
@@ -136,25 +136,50 @@ export function LoopAssistant({ app, person, appId, decisions, }) {
136
136
  }
137
137
  const state = assistantStateOfTurn(turn, presence, { arriving, leaving });
138
138
  const unheard = !!saying && saying.id !== heardId;
139
+ // What the agent does while a tool runs, in its own words (T-23).
140
+ const tool = turn.activity
141
+ ? {
142
+ id: `${turn.id}:${turn.activity}`,
143
+ tool: '',
144
+ name: '',
145
+ phase: 'running',
146
+ words: turn.activity,
147
+ }
148
+ : undefined;
149
+ const atWork = turn.status === 'streaming' || turn.status === 'thinking';
150
+ const current = display === 'current';
139
151
  const balloon = state === 'paused'
140
152
  ? { text: ASSISTANT_WORDS.paused }
141
153
  : approval
142
154
  ? { text: ASSISTANT_WORDS.approval, approval }
143
155
  : presence === 'waiting'
144
156
  ? { text: ASSISTANT_WORDS.waiting }
145
- : unheard && saying
146
- ? {
147
- // A peek: the first words; the rest is in the conversation.
148
- ...peekLine(saying.text),
149
- onDismiss: () => {
150
- setHeardId(saying.id);
151
- setFresh(false);
152
- },
153
- }
154
- : { text: 'Click me to open the conversation.' };
157
+ : tool
158
+ ? { text: turn.activity ?? '', tool, busy: true }
159
+ : current && (atWork || unheard)
160
+ ? {
161
+ text: turn.status === 'thinking'
162
+ ? 'Thinking…'
163
+ : (saying?.text ?? 'Thinking…'),
164
+ more: saying?.more,
165
+ speaking: turn.status === 'streaming',
166
+ busy: atWork,
167
+ }
168
+ : unheard && saying
169
+ ? {
170
+ // A peek: the first words; the rest is in the conversation.
171
+ ...peekLine(saying.text),
172
+ onDismiss: () => {
173
+ setHeardId(saying.id);
174
+ setFresh(false);
175
+ },
176
+ }
177
+ : { text: 'Click me to open the conversation.' };
155
178
  const insist = state === 'greeting' ||
156
179
  presence === 'waiting' ||
157
180
  (!!approval && state !== 'paused') ||
181
+ !!tool ||
182
+ (current && atWork) ||
158
183
  (unheard && (state === 'speaking' || fresh));
159
184
  const onToggle = () => {
160
185
  if (onPageLayout) {
@@ -174,7 +199,7 @@ export function LoopAssistant({ app, person, appId, decisions, }) {
174
199
  };
175
200
  return (_jsxs(_Fragment, { children: [appId && signedIn ? (_jsx(WaitingApprovals, { appId: appId, onWaiting: setApproval })) : null, _jsx(AssistantStage, { character: chosen.character, state: state, size: SIZE, place: drag.position
176
201
  ? { left: `${drag.position.left}px`, top: `${drag.position.top}px` }
177
- : { right: '24px', bottom: '24px' }, stageRef: stageRef, onDragStart: drag.onHandlePointerDown, open: open, onToggle: onToggle, balloon: balloon, insist: insist, onDismiss: onDismiss, decide: decide })] }));
202
+ : { right: '24px', bottom: '24px' }, stageRef: stageRef, onDragStart: drag.onHandlePointerDown, open: open, onToggle: onToggle, balloon: balloon, balloonDisplay: display, insist: insist, onDismiss: onDismiss, decide: decide })] }));
178
203
  }
179
204
  export const LoopAssistantPlugin = definePlugin({
180
205
  name: LOOP_ASSISTANT_PLUGIN_NAME,
@@ -183,12 +208,13 @@ export const LoopAssistantPlugin = definePlugin({
183
208
  person: undefined,
184
209
  appId: undefined,
185
210
  decisions: undefined,
211
+ balloon: undefined,
186
212
  },
187
213
  displayName: 'Floating assistant',
188
214
  description: 'The chat as a character on the page: the application’s own, or the one the person chose, from the characters the enabled plugins contribute.',
189
215
  octicon: 'paperclip',
190
216
  build: ({ config }) => {
191
- const Configured = () => (_jsx(LoopAssistant, { app: config.app, person: config.person, appId: config.appId, decisions: config.decisions }));
217
+ const Configured = () => (_jsx(LoopAssistant, { app: config.app, person: config.person, appId: config.appId, decisions: config.decisions, balloon: config.balloon }));
192
218
  return {
193
219
  components: [
194
220
  { id: 'loop-assistant', slot: LoopSlots.root, Component: Configured },
@@ -326,6 +326,18 @@ export const APPSPEC_SCHEMA = {
326
326
  description: "The character its floating assistant shows, by the id a plugin contributes it under (lowercase letters and digits, words joined by a hyphen): Datalayer's are `paperclip`, `wizard`, `cat` and `eyes`. The paper clip when unsaid; an id no enabled plugin contributes is refused where the plugins are known, the runtime and the page. Said here, it wins over a person's own choice in their settings",
327
327
  title: 'Assistant',
328
328
  },
329
+ balloon: {
330
+ anyOf: [
331
+ {
332
+ $ref: '#/$defs/BalloonDisplay',
333
+ },
334
+ {
335
+ type: 'null',
336
+ },
337
+ ],
338
+ default: null,
339
+ description: "How its floating assistant's balloon shows the conversation: `history` (every message, scrolled, the composer last) or `current` (only what it says or does now, the answer being written or the tool it calls, in one balloon). The page's own when unsaid: `history` for the floating chat",
340
+ },
329
341
  voice: {
330
342
  $ref: '#/$defs/AppVoice',
331
343
  description: 'Its voice: whether it listens and speaks, with which voice, in which language (off unless said)',
@@ -772,6 +784,12 @@ export const APPSPEC_SCHEMA = {
772
784
  title: 'AppVoice',
773
785
  type: 'object',
774
786
  },
787
+ BalloonDisplay: {
788
+ description: "How a floating assistant's balloon shows the conversation (LOOP T-23).",
789
+ enum: ['history', 'current'],
790
+ title: 'BalloonDisplay',
791
+ type: 'string',
792
+ },
775
793
  Behaviour: {
776
794
  description: 'What an application does when it meets an action: the four a person chooses from.',
777
795
  enum: ['do_it', 'if_asked', 'ask_first', 'leave_to_me'],
@@ -8,7 +8,7 @@ export type ReferenceScreen = (typeof REFERENCE_SCREENS)[number];
8
8
  * where it has something to say — stepped aside, and open, the conversation
9
9
  * as its balloon (T-23).
10
10
  */
11
- export declare const ASSISTANT_PICTURES: readonly ["idle", "thinking", "working", "waiting", "paused", "speaking", "aside", "open"];
11
+ export declare const ASSISTANT_PICTURES: readonly ["idle", "thinking", "working", "waiting", "paused", "speaking", "aside", "open", "current"];
12
12
  export type AssistantPicture = (typeof ASSISTANT_PICTURES)[number];
13
13
  /**
14
14
  * The characters pictured idle, each in light and dark (T-25, T-24):
@@ -30,6 +30,7 @@ import { AssistantStage, } from '../../chat/assistant/AssistantStage';
30
30
  import { ASSISTANT_WORDS, } from '../../chat/assistant/state';
31
31
  import { CONVERSATION_BALLOON_WIDTH, ConversationBalloonClose, balloonTailAt, conversationBalloonHeight, conversationBalloonSx, peekLine, } from '../../chat/assistant/ConversationBalloon';
32
32
  import { DecisionAsk } from '../../chat/assistant/DecisionAsk';
33
+ import { ConversationBalloonHeader } from '../../chat/assistant/BalloonParts';
33
34
  import { buildReactorFromPlugins } from '@datalayer/reactor';
34
35
  import { AssistantCharactersPlugin, assistantCharacterNamed, } from '../../loop/plugins/assistant-characters';
35
36
  import { OwlCharacterPlugin } from '../../examples/utils/owlCharacterPlugin';
@@ -55,6 +56,7 @@ export const ASSISTANT_PICTURES = [
55
56
  'speaking',
56
57
  'aside',
57
58
  'open',
59
+ 'current',
58
60
  ];
59
61
  /**
60
62
  * The characters pictured idle, each in light and dark (T-25, T-24):
@@ -257,6 +259,17 @@ const BALLOONS = {
257
259
  },
258
260
  },
259
261
  paused: { text: ASSISTANT_WORDS.paused },
262
+ // Current (T-23): only what it does now — the tool it calls.
263
+ current: {
264
+ text: 'Using list_invoices…',
265
+ tool: {
266
+ id: 'picture-tool',
267
+ tool: 'list_invoices',
268
+ name: 'list_invoices',
269
+ phase: 'running',
270
+ },
271
+ busy: true,
272
+ },
260
273
  };
261
274
  /**
262
275
  * The paper clip in one state, in the page's corner. `aside` puts a dialog
@@ -265,7 +278,11 @@ const BALLOONS = {
265
278
  */
266
279
  export function AssistantScreen({ picture, }) {
267
280
  const stageRef = useRef(null);
268
- const state = picture === 'aside' || picture === 'open' ? 'idle' : picture;
281
+ const state = picture === 'aside' || picture === 'open'
282
+ ? 'idle'
283
+ : picture === 'current'
284
+ ? 'working'
285
+ : picture;
269
286
  const balloon = BALLOONS[picture];
270
287
  return (_jsxs(Box, { sx: { flex: 1, position: 'relative' }, children: [picture === 'open' && _jsx(OpenConversation, {}), picture === 'aside' && (_jsxs(Box, { role: "dialog", "aria-label": "A dialog over the assistant", sx: {
271
288
  position: 'fixed',
@@ -280,7 +297,7 @@ export function AssistantScreen({ picture, }) {
280
297
  borderColor: 'border.default',
281
298
  borderRadius: 'var(--theme-radius-card, 6px)',
282
299
  boxShadow: 'var(--theme-shadow, none)',
283
- }, 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, insist: !!balloon, onDismiss: () => undefined })] }));
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 })] }));
284
301
  }
285
302
  /** What the open picture's conversation holds. */
286
303
  const OPEN_CONVERSATION = [
@@ -316,7 +333,7 @@ function OpenConversation() {
316
333
  edge: 'bottom',
317
334
  at: balloonTailAt(window.innerWidth - 48 - 44, left),
318
335
  }, 'canvas.subtle'),
319
- }, children: [_jsx(ConversationBalloonClose, { onClose: () => undefined }), _jsxs(Box, { sx: {
336
+ }, children: [_jsx(ConversationBalloonClose, { onClose: () => undefined }), _jsx(ConversationBalloonHeader, { count: OPEN_CONVERSATION.length - 1 }), _jsxs(Box, { sx: {
320
337
  flex: 1,
321
338
  minHeight: 0,
322
339
  display: 'flex',
@@ -392,6 +392,8 @@ export type AppAccent = 'green' | 'rose' | 'sky' | 'lime' | 'sun' | 'violet';
392
392
  * known only where the plugins are.
393
393
  */
394
394
  export type AppAssistantCharacter = string;
395
+ /** How a floating assistant's balloon shows the conversation (LOOP T-23). */
396
+ export type AppBalloonDisplay = 'history' | 'current';
395
397
  /** How an application sits in another product's page (LOOP D-07). */
396
398
  export type AppEmbedMode = 'inline' | 'bubble' | 'panel' | 'assistant';
397
399
  /** Something an application reaches. */
@@ -479,6 +481,12 @@ export interface AppInterfaceSpec {
479
481
  * Said, it wins over the one the person chose in their settings.
480
482
  */
481
483
  assistant?: AppAssistantCharacter;
484
+ /**
485
+ * How its floating assistant's balloon shows the conversation: `history`
486
+ * (every message, the composer last) or `current` (only what it says or
487
+ * does now). The page's own when unsaid.
488
+ */
489
+ balloon?: AppBalloonDisplay;
482
490
  /** Its voice: off unless said (VO-41); absent from a spec made before voice. */
483
491
  voice?: AppVoiceSpec;
484
492
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/agent-runtimes",
3
- "version": "1.3.70",
3
+ "version": "1.3.72",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",