@datalayer/agent-runtimes 1.5.89 → 1.5.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.
Files changed (87) hide show
  1. package/README.md +29 -297
  2. package/dist-embed/datalayer-app-main.js +40 -36
  3. package/lib/apps/embed/element.js +20 -5
  4. package/lib/apps/embed/visitorApp.d.ts +23 -45
  5. package/lib/apps/embed/visitorApp.js +65 -13
  6. package/lib/components/teams/A2ATeamGraph.d.ts +58 -152
  7. package/lib/components/teams/A2ATeamGraph.js +169 -410
  8. package/lib/components/teams/SceneTranscript.d.ts +5 -1
  9. package/lib/components/teams/SceneTranscript.js +2 -2
  10. package/lib/components/teams/SceneView.d.ts +17 -5
  11. package/lib/components/teams/SceneView.js +7 -3
  12. package/lib/components/teams/a2aTeamFlow.d.ts +16 -7
  13. package/lib/components/teams/a2aTeamFlow.js +17 -14
  14. package/lib/components/teams/backdrops/catalogue.d.ts +35 -0
  15. package/lib/components/teams/backdrops/catalogue.js +34 -0
  16. package/lib/components/teams/backdrops/edges.d.ts +20 -0
  17. package/lib/components/teams/backdrops/edges.js +29 -0
  18. package/lib/components/teams/backdrops/faces.d.ts +24 -0
  19. package/lib/components/teams/backdrops/faces.js +39 -0
  20. package/lib/components/teams/backdrops/halo.d.ts +14 -0
  21. package/lib/components/teams/backdrops/halo.js +5 -0
  22. package/lib/components/teams/backdrops/hero.d.ts +14 -0
  23. package/lib/components/teams/backdrops/hero.js +178 -0
  24. package/lib/components/teams/backdrops/index.d.ts +22 -0
  25. package/lib/components/teams/backdrops/index.js +38 -0
  26. package/lib/components/teams/backdrops/nodes.d.ts +123 -0
  27. package/lib/components/teams/backdrops/nodes.js +265 -0
  28. package/lib/components/teams/backdrops/plain.d.ts +14 -0
  29. package/lib/components/teams/backdrops/plain.js +116 -0
  30. package/lib/components/teams/backdrops/studio.d.ts +17 -0
  31. package/lib/components/teams/backdrops/studio.js +122 -0
  32. package/lib/components/teams/backdrops/theme.d.ts +14 -0
  33. package/lib/components/teams/backdrops/theme.js +19 -0
  34. package/lib/components/teams/backdrops/types.d.ts +214 -0
  35. package/lib/components/teams/backdrops/types.js +5 -0
  36. package/lib/components/teams/index.d.ts +2 -0
  37. package/lib/components/teams/index.js +2 -0
  38. package/lib/components/teams/sceneTranscriptRules.d.ts +32 -1
  39. package/lib/components/teams/sceneTranscriptRules.js +104 -2
  40. package/lib/components/teams/transcript/RecordingLabel.d.ts +17 -0
  41. package/lib/components/teams/transcript/RecordingLabel.js +7 -0
  42. package/lib/components/teams/transcript/SequenceDiagram.d.ts +39 -0
  43. package/lib/components/teams/transcript/SequenceDiagram.js +190 -0
  44. package/lib/components/teams/transcript/TranscriptDetail.d.ts +27 -0
  45. package/lib/components/teams/transcript/TranscriptDetail.js +48 -0
  46. package/lib/components/teams/transcript/TranscriptTable.d.ts +23 -0
  47. package/lib/components/teams/transcript/TranscriptTable.js +112 -0
  48. package/lib/components/teams/transcript/TranscriptViews.d.ts +66 -0
  49. package/lib/components/teams/transcript/TranscriptViews.js +135 -0
  50. package/lib/components/teams/transcript/TranscriptViewsTypes.d.ts +13 -0
  51. package/lib/components/teams/transcript/TranscriptViewsTypes.js +5 -0
  52. package/lib/components/teams/transcript/index.d.ts +23 -0
  53. package/lib/components/teams/transcript/index.js +27 -0
  54. package/lib/components/teams/transcript/recordingPlayback.d.ts +52 -0
  55. package/lib/components/teams/transcript/recordingPlayback.js +146 -0
  56. package/lib/components/teams/transcript/selection.d.ts +59 -0
  57. package/lib/components/teams/transcript/selection.js +79 -0
  58. package/lib/components/teams/transcript/sequenceDiagramRules.d.ts +35 -0
  59. package/lib/components/teams/transcript/sequenceDiagramRules.js +158 -0
  60. package/lib/components/teams/transcript/transcriptDetailRules.d.ts +80 -0
  61. package/lib/components/teams/transcript/transcriptDetailRules.js +208 -0
  62. package/lib/components/teams/transcript/transcriptExport.d.ts +31 -0
  63. package/lib/components/teams/transcript/transcriptExport.js +82 -0
  64. package/lib/components/teams/transcript/transcriptRows.d.ts +76 -0
  65. package/lib/components/teams/transcript/transcriptRows.js +187 -0
  66. package/lib/components/teams/transcript/useSceneRecording.d.ts +45 -0
  67. package/lib/components/teams/transcript/useSceneRecording.js +96 -0
  68. package/lib/examples/ScenesExample.js +16 -3
  69. package/lib/portable/hosts/agentClient.d.ts +8 -0
  70. package/lib/portable/hosts/agentClient.js +2 -0
  71. package/lib/portable/hosts/approvalRenderers.d.ts +10 -2
  72. package/lib/portable/hosts/approvalRenderers.js +81 -2
  73. package/lib/portable/hosts/contracts.d.ts +1 -0
  74. package/lib/portable/hosts/contracts.js +2 -0
  75. package/lib/portable/hosts/contracts.json +56 -2
  76. package/lib/specs/actions.js +2 -0
  77. package/lib/specs/apps.d.ts +1 -0
  78. package/lib/specs/apps.js +219 -0
  79. package/lib/specs/sceneRecordings.d.ts +14 -0
  80. package/lib/specs/sceneRecordings.js +12 -0
  81. package/lib/specs/sceneSchema.js +11 -0
  82. package/lib/specs/scenes.d.ts +1 -0
  83. package/lib/specs/scenes.js +159 -0
  84. package/lib/types/scenes.d.ts +54 -0
  85. package/package.json +2 -1
  86. package/scripts/codegen/generate_mcp_servers.py +5 -16
  87. package/scripts/codegen/generate_scenes.py +80 -0
@@ -72,7 +72,7 @@ import { AppEmbed, embedAssistantCharacter } from './AppEmbed.js';
72
72
  import { WINDOW_NOT_YET } from './embedSession.js';
73
73
  import { EMBED_HOST_VARIABLES, EMBED_OBSERVED_ATTRIBUTES, EMBED_TAG, EMBED_TOKEN_NAMES_NO_DEPLOYMENT, EmbedAttributeError, adoptPropertiesSetEarly, embedLookOf, embedTokenDeployment, inlineHeightOf, languageOf, resumeOf, runnable, } from './embedConfig.js';
74
74
  import { embedShadowCss } from './embedTheme.js';
75
- import { DEFAULT_API, readVisitorApp } from './visitorApp.js';
75
+ import { DEFAULT_API, DEFAULT_INFERENCE, readVisitorApp } from './visitorApp.js';
76
76
  export { runnable };
77
77
  /** What the framed run page and the element say to each other. */
78
78
  export const EMBED_MESSAGES = {
@@ -383,7 +383,11 @@ export function defineDatalayerAppElement(options = {}) {
383
383
  // A public application, as a visitor without an account (D-07):
384
384
  // an example kept warm or an address open to visitors, run on the
385
385
  // visitors' runtime; a decision framed; a refusal said.
386
- const visitor = await readVisitorApp({ api: this.agents(), app });
386
+ const visitor = await readVisitorApp({
387
+ api: this.agents(),
388
+ inference: this.inference(),
389
+ app,
390
+ });
387
391
  if (visitor.kind === 'decision') {
388
392
  return { kind: 'frame', src: frameSrcOf(this.origin(), visitor.app) };
389
393
  }
@@ -410,15 +414,26 @@ export function defineDatalayerAppElement(options = {}) {
410
414
  }
411
415
  /**
412
416
  * ai-agents, where a visitor's address is read and beside which the
413
- * visitors' runtime and ai-inference answer: the `api` attribute, else
414
- * the platform's, else r1's. Not Spacer's address (`api()`), which lives
415
- * on another plane.
417
+ * visitors' runtime answers: the `api` attribute, else the platform's,
418
+ * else the AI plane's (`DEFAULT_API`). Not Spacer's address (`api()`),
419
+ * which lives on another plane.
416
420
  */
417
421
  agents() {
418
422
  return (this.getAttribute('api') ||
419
423
  coreStore.getState().configuration?.aiAgentsUrl ||
420
424
  DEFAULT_API).replace(/\/+$/, '');
421
425
  }
426
+ /**
427
+ * ai-inference, which mints a visitor's token: the `api` attribute, else
428
+ * the platform's, else the AI plane's (`DEFAULT_INFERENCE`) — never the
429
+ * control plane, where no ai-inference answers (28 October 2026: a bare
430
+ * `<datalayer-app app="…">` asked prod1 and got "Failed to fetch").
431
+ */
432
+ inference() {
433
+ return (this.getAttribute('api') ||
434
+ coreStore.getState().configuration?.aiInferenceUrl ||
435
+ DEFAULT_INFERENCE).replace(/\/+$/, '');
436
+ }
422
437
  /** Spacer, where an application is read with its embed token: the `api` attribute, else the platform's, else prod1's. */
423
438
  api() {
424
439
  return (this.getAttribute('api') ||
@@ -1,39 +1,3 @@
1
- /**
2
- * A public application in the element, for a visitor without an account
3
- * (D-07, R-30).
4
- *
5
- * `<datalayer-app app="…">` with no token names a public application. The
6
- * element reads what it is without anybody's credentials, as the landing's
7
- * hosted page does for whoever is signed out:
8
- *
9
- * - **an example Datalayer keeps warm for visitors** (`web-research`,
10
- * `customer-interview`): its Appspec from the catalogue this bundle
11
- * carries; the visitors' runtime answers its id;
12
- * - **an application at its address** (`demo-accounting`): ai-agents'
13
- * `GET /apps/deployments/at/<slug>` without a token says what its page is
14
- * drawn from — the Appspec of the version it runs — and whether somebody
15
- * signed out may talk to it (`session.opens`); the visitors' runtime
16
- * answers `at:<slug>`, making the address's agent when first talked to —
17
- * or, for a version with code of its own (`runs_code`), the runtime its
18
- * deployment is kept on (`kept.url`), which runs that code;
19
- * - **a decision**, named by its id (`01M…`): framed from Datalayer's run
20
- * page, as before.
21
- *
22
- * The conversation then runs as the landing's signed-out one does: the chat
23
- * mints the visitor's token itself from ai-inference (`useVisitorToken`,
24
- * `POST /anonymous/token {app}`), minted again before it runs out, and talks
25
- * to the visitors' runtime with it — in every mode, inline or floating.
26
- *
27
- * What a visitor may not have is said in one sentence, before anything is
28
- * drawn (fail fast): a private address (ai-agents answers *No application is
29
- * at this address*), one that acts through its owner's connections or
30
- * documents (ai-agents' own reason), an example the visitors' runtime does
31
- * not keep. Each says the embed token is what an embed of it takes.
32
- *
33
- * Pure but for the one request, given or `fetch`.
34
- *
35
- * @module apps/embed/visitorApp
36
- */
37
1
  import type { AppSpec } from '../../types/agentspecs';
38
2
  import type { DatalayerVisitors } from '../apps/visitorToken';
39
3
  /** Where the visitors' runtime is published, on ai-agents' host (its chart). */
@@ -45,12 +9,21 @@ export declare const AT_PREFIX = "at:";
45
9
  /**
46
10
  * Datalayer's services for a visitor, where the element names none (`api`)
47
11
  * and the platform's configuration says none: ai-agents, where an address is
48
- * read, and beside it the visitors' runtime and ai-inference, which mints the
49
- * visitor's token. Not Spacer's address, which reads an application with its
50
- * embed token: the two live on different planes (ai-agents on r1, Spacer on
51
- * prod1), found 15 October 2026 on datalayer.ai's embed.
12
+ * read, and beside it the visitors' runtime — the AI plane, as core's plane
13
+ * table says it, never a literal of this bundle's own. Not Spacer's address,
14
+ * which reads an application with its embed token: the two live on different
15
+ * planes (ai-agents on r1, Spacer on prod1), found 15 October 2026 on
16
+ * datalayer.ai's embed; a bundle whose literal still said prod1 failed to
17
+ * mint a visitor's token there on 28 October 2026 ("Failed to fetch"),
18
+ * ai-inference being on r1 with the agents that call it.
19
+ */
20
+ export declare const DEFAULT_API: string;
21
+ /**
22
+ * ai-inference, which mints a visitor's token, where the element names none
23
+ * (`api`) and the platform's configuration says none: the AI plane, as core's
24
+ * plane table says it.
52
25
  */
53
- export declare const DEFAULT_API = "https://r1.datalayer.run";
26
+ export declare const DEFAULT_INFERENCE: string;
54
27
  /** An application's id on Datalayer: a ULID. */
55
28
  export declare const isAppUid: (value: string) => boolean;
56
29
  /** What `app` with no token resolves to. */
@@ -74,9 +47,10 @@ export type VisitorApp =
74
47
  /**
75
48
  * Where a visitor's conversation with `app` runs: the visitors' runtime beside
76
49
  * ai-agents — or `runtime`, the one a deployment with code of its own is
77
- * kept on (R-30) — its token from ai-inference.
50
+ * kept on (R-30) — its token from ai-inference, at `inference` when it is
51
+ * named, else beside ai-agents.
78
52
  */
79
- export declare function visitorsAt(api: string, app: string, runtime?: string): DatalayerVisitors;
53
+ export declare function visitorsAt(api: string, app: string, runtime?: string, inference?: string): DatalayerVisitors;
80
54
  /** What an embed without a token is told it takes. */
81
55
  export declare const TOKEN_HINT = "Embedded for a visitor it refuses, it takes its embed token (the \"token\" attribute).";
82
56
  /** The sentence for an example the visitors' runtime does not keep. */
@@ -87,13 +61,17 @@ export declare const refusedSentence: (reason: string) => string;
87
61
  * Read what `app` is for a visitor without an account: a decision to frame,
88
62
  * an example or an address to run on the visitors' runtime, or a sentence.
89
63
  *
90
- * @param api - Datalayer's services (ai-agents, ai-inference): the `api` attribute.
64
+ * @param api - Datalayer's services (ai-agents, and ai-inference unless
65
+ * `inference` names it): the `api` attribute; the AI plane unless given.
66
+ * @param inference - ai-inference, which mints the visitor's token: `api`
67
+ * when that is given, else the AI plane.
91
68
  * @param app - The `app` attribute: a decision's id, an example's id or an address.
92
69
  * @param fetcher - What asks ai-agents; `fetch` unless given.
93
70
  * @param catalogue - The examples; the bundle's catalogue unless given.
94
71
  */
95
- export declare function readVisitorApp({ api, app, fetcher, catalogue, }: {
72
+ export declare function readVisitorApp({ api, inference, app, fetcher, catalogue, }: {
96
73
  api?: string;
74
+ inference?: string;
97
75
  app: string;
98
76
  fetcher?: typeof fetch;
99
77
  catalogue?: (ref: string) => AppSpec | undefined;
@@ -2,6 +2,43 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
+ /**
6
+ * A public application in the element, for a visitor without an account
7
+ * (D-07, R-30).
8
+ *
9
+ * `<datalayer-app app="…">` with no token names a public application. The
10
+ * element reads what it is without anybody's credentials, as the landing's
11
+ * hosted page does for whoever is signed out:
12
+ *
13
+ * - **an example Datalayer keeps warm for visitors** (`web-research`,
14
+ * `customer-interview`): its Appspec from the catalogue this bundle
15
+ * carries; the visitors' runtime answers its id;
16
+ * - **an application at its address** (`demo-accounting`): ai-agents'
17
+ * `GET /apps/deployments/at/<slug>` without a token says what its page is
18
+ * drawn from — the Appspec of the version it runs — and whether somebody
19
+ * signed out may talk to it (`session.opens`); the visitors' runtime
20
+ * answers `at:<slug>`, making the address's agent when first talked to —
21
+ * or, for a version with code of its own (`runs_code`), the runtime its
22
+ * deployment is kept on (`kept.url`), which runs that code;
23
+ * - **a decision**, named by its id (`01M…`): framed from Datalayer's run
24
+ * page, as before.
25
+ *
26
+ * The conversation then runs as the landing's signed-out one does: the chat
27
+ * mints the visitor's token itself from ai-inference (`useVisitorToken`,
28
+ * `POST /anonymous/token {app}`), minted again before it runs out, and talks
29
+ * to the visitors' runtime with it — in every mode, inline or floating.
30
+ *
31
+ * What a visitor may not have is said in one sentence, before anything is
32
+ * drawn (fail fast): a private address (ai-agents answers *No application is
33
+ * at this address*), one that acts through its owner's connections or
34
+ * documents (ai-agents' own reason), an example the visitors' runtime does
35
+ * not keep. Each says the embed token is what an embed of it takes.
36
+ *
37
+ * Pure but for the one request, given or `fetch`.
38
+ *
39
+ * @module apps/embed/visitorApp
40
+ */
41
+ import { DEFAULT_PLANE_URLS } from '@datalayer/core/lib/config/planes.js';
5
42
  import { getApp } from '../../specs/apps.js';
6
43
  import { parseAppspec } from '../apps/appspec.js';
7
44
  import { runnable } from './embedConfig.js';
@@ -17,12 +54,21 @@ export const AT_PREFIX = 'at:';
17
54
  /**
18
55
  * Datalayer's services for a visitor, where the element names none (`api`)
19
56
  * and the platform's configuration says none: ai-agents, where an address is
20
- * read, and beside it the visitors' runtime and ai-inference, which mints the
21
- * visitor's token. Not Spacer's address, which reads an application with its
22
- * embed token: the two live on different planes (ai-agents on r1, Spacer on
23
- * prod1), found 15 October 2026 on datalayer.ai's embed.
57
+ * read, and beside it the visitors' runtime — the AI plane, as core's plane
58
+ * table says it, never a literal of this bundle's own. Not Spacer's address,
59
+ * which reads an application with its embed token: the two live on different
60
+ * planes (ai-agents on r1, Spacer on prod1), found 15 October 2026 on
61
+ * datalayer.ai's embed; a bundle whose literal still said prod1 failed to
62
+ * mint a visitor's token there on 28 October 2026 ("Failed to fetch"),
63
+ * ai-inference being on r1 with the agents that call it.
64
+ */
65
+ export const DEFAULT_API = DEFAULT_PLANE_URLS.aiAgentsUrl;
66
+ /**
67
+ * ai-inference, which mints a visitor's token, where the element names none
68
+ * (`api`) and the platform's configuration says none: the AI plane, as core's
69
+ * plane table says it.
24
70
  */
25
- export const DEFAULT_API = 'https://r1.datalayer.run';
71
+ export const DEFAULT_INFERENCE = DEFAULT_PLANE_URLS.aiInferenceUrl;
26
72
  /** An application's id on Datalayer: a ULID. */
27
73
  export const isAppUid = (value) => /^[0-9A-HJKMNP-TV-Z]{26}$/.test(value);
28
74
  /** An address, as ai-agents spells one. */
@@ -30,14 +76,15 @@ const SLUG = /^[a-z0-9][a-z0-9-]{0,62}$/;
30
76
  /**
31
77
  * Where a visitor's conversation with `app` runs: the visitors' runtime beside
32
78
  * ai-agents — or `runtime`, the one a deployment with code of its own is
33
- * kept on (R-30) — its token from ai-inference.
79
+ * kept on (R-30) — its token from ai-inference, at `inference` when it is
80
+ * named, else beside ai-agents.
34
81
  */
35
- export function visitorsAt(api, app, runtime) {
82
+ export function visitorsAt(api, app, runtime, inference) {
36
83
  const base = api.replace(/\/+$/, '');
37
84
  return {
38
85
  url: runtime ? runtime.replace(/\/+$/, '') : `${base}${VISITORS_PATH}`,
39
86
  app,
40
- inferenceUrl: base,
87
+ inferenceUrl: (inference || base).replace(/\/+$/, ''),
41
88
  };
42
89
  }
43
90
  /** What an embed without a token is told it takes. */
@@ -50,12 +97,17 @@ export const refusedSentence = (reason) => `datalayer-app: ${reason.trim().repla
50
97
  * Read what `app` is for a visitor without an account: a decision to frame,
51
98
  * an example or an address to run on the visitors' runtime, or a sentence.
52
99
  *
53
- * @param api - Datalayer's services (ai-agents, ai-inference): the `api` attribute.
100
+ * @param api - Datalayer's services (ai-agents, and ai-inference unless
101
+ * `inference` names it): the `api` attribute; the AI plane unless given.
102
+ * @param inference - ai-inference, which mints the visitor's token: `api`
103
+ * when that is given, else the AI plane.
54
104
  * @param app - The `app` attribute: a decision's id, an example's id or an address.
55
105
  * @param fetcher - What asks ai-agents; `fetch` unless given.
56
106
  * @param catalogue - The examples; the bundle's catalogue unless given.
57
107
  */
58
- export async function readVisitorApp({ api = DEFAULT_API, app, fetcher = fetch, catalogue = getApp, }) {
108
+ export async function readVisitorApp({ api, inference, app, fetcher = fetch, catalogue = getApp, }) {
109
+ const agents = api || DEFAULT_API;
110
+ const mints = inference || api || DEFAULT_INFERENCE;
59
111
  const named = app.trim();
60
112
  if (isAppUid(named)) {
61
113
  return { kind: 'decision', app: named };
@@ -71,7 +123,7 @@ export async function readVisitorApp({ api = DEFAULT_API, app, fetcher = fetch,
71
123
  return {
72
124
  kind: 'visitors',
73
125
  app: runnable({ app: example, problems: [] }),
74
- visitors: visitorsAt(api, example.id),
126
+ visitors: visitorsAt(agents, example.id, undefined, mints),
75
127
  };
76
128
  }
77
129
  if (!SLUG.test(named)) {
@@ -82,7 +134,7 @@ export async function readVisitorApp({ api = DEFAULT_API, app, fetcher = fetch,
82
134
  }
83
135
  let response;
84
136
  try {
85
- response = await fetcher(`${api.replace(/\/+$/, '')}/api/ai-agents/v1/apps/deployments/at/${encodeURIComponent(named)}`);
137
+ response = await fetcher(`${agents.replace(/\/+$/, '')}/api/ai-agents/v1/apps/deployments/at/${encodeURIComponent(named)}`);
86
138
  }
87
139
  catch (error) {
88
140
  return {
@@ -117,7 +169,7 @@ export async function readVisitorApp({ api = DEFAULT_API, app, fetcher = fetch,
117
169
  return {
118
170
  kind: 'visitors',
119
171
  app: runnable({ app: { ...parsed.app, name }, problems: parsed.problems }),
120
- visitors: visitorsAt(api, `${AT_PREFIX}${named}`, runtime.url),
172
+ visitors: visitorsAt(agents, `${AT_PREFIX}${named}`, runtime.url, mints),
121
173
  };
122
174
  }
123
175
  /**
@@ -21,114 +21,38 @@
21
21
  *
22
22
  * A member's connections — the MCP servers it reaches, Odoo for Accounting —
23
23
  * stand to its right, each a node half a member's size with the server's mark
24
- * (`SpecMark`), linked to it by an edge of its own. That edge is still until
25
- * the member calls one of the connection's tools: then it flows toward the
26
- * connection for as long as the call runs ({@link A2ATeamCall}), with the
27
- * tool's name on it; under reduced motion, the arrow and the words alone.
24
+ * (`SpecMark`), linked to it by an edge of its own, `MCP`, drawn as the A2A
25
+ * link is: still until the member calls one of the connection's tools, then
26
+ * flowing toward the connection for as long as the call runs
27
+ * ({@link A2ATeamCall}), `MCP · list invoices`, and back with its result for
28
+ * a moment, `MCP · Odoo answers` (a screen reader hears who calls whom);
29
+ * under reduced motion, the arrow and the words alone.
28
30
  *
29
31
  * The graph is a picture, not an editor: nothing is dragged, panned or
30
32
  * zoomed, and the wheel scrolls the page. Its viewport is worked out from its
31
33
  * box, so it fits a phone as it fits a desk.
32
34
  *
35
+ * How it is drawn is its *backdrop* (`backdrop`, the scene's
36
+ * `stage.backdrop`; {@link SceneBackdrop}): `studio`, the drawing above —
37
+ * characters, balloons, flowing edges — `hero`, faces on a deep tint with
38
+ * a dot that flies with each message, or `plain`, boxes and arrows that do
39
+ * not move. The graph works out the same things for every backdrop — who
40
+ * stands where, which way each link carries a message, which connection
41
+ * answers a call, the words a screen reader hears — and reads the node
42
+ * types, the edge types, the colours and the layout from the backdrop.
43
+ *
33
44
  * @module components/teams/A2ATeamGraph
34
45
  */
35
- import type { AssistantModel3D } from '../../chat/assistant/avatar3dModel';
36
46
  import type { JSX } from 'react';
37
- import { type Edge, type EdgeProps, type Viewport } from '@xyflow/react';
47
+ import { type Viewport } from '@xyflow/react';
38
48
  import '@xyflow/react/dist/base.css';
39
- import { type AssistantAbout } from '../../chat/assistant/AssistantStage';
40
- import type { BalloonSuggestion } from '../../chat/assistant/SpeechBalloon';
41
- import type { DisplayItem } from '../../types/chat';
42
- import type { AssistantMenuItem } from '../../chat/assistant/AssistantContextMenu';
43
- import type { AssistantSandbox } from '../../chat/assistant/assistantDetails';
44
- import type { OtelLiveTracer } from '@datalayer/core/lib/otel/live';
45
49
  import { type A2ATeamCall, type A2ATeamConnection, type A2ATeamFlow } from './a2aTeamFlow';
46
- import type { A2ATeamPersona } from './useA2ATeam';
47
- import type { BalloonExpandTarget } from '../../chat/assistant/BalloonVisual';
48
- import type { BalloonDisplay } from '../../chat/assistant/toolLine';
49
- /** One member of the team, as the graph draws it. */
50
- export type A2ATeamGraphMember = {
51
- id: string;
52
- name: string;
53
- emoji?: string;
54
- /** Its avatar, as its Appspec's `interface.avatar` names it: an id, or a 3D model. */
55
- character: string | AssistantModel3D;
56
- /** Where it runs, in a few words: "in your browser", "on a runtime". */
57
- where: string;
58
- persona: A2ATeamPersona;
59
- /** A click on the character. */
60
- onToggle?: () => void;
61
- /** It was sent away, or called back. */
62
- onAway?: (away: boolean) => void;
63
- /** The MCP servers it reaches, drawn to its right (`teamConnectionsOf`). */
64
- connections?: A2ATeamConnection[];
65
- /**
66
- * How its balloon shows what it says (T-23): only what it says or
67
- * does now (`current`, a team's default) — the words, the tool it calls,
68
- * a notebook it was given — or a peek (`history`).
69
- */
70
- balloonDisplay?: BalloonDisplay;
71
- /**
72
- * The notebook in its balloon was clicked: by default, it is expanded —
73
- * into `expandTarget`, scrolled to and focused, or in a dialog.
74
- */
75
- onNotebookOpen?: () => void;
76
- /**
77
- * Where the notebook it was given runs (`TeamNotebook`, editable, on the
78
- * browser sandbox): an element of the page — the area under the graph —
79
- * where it is drawn as it arrives and when it is expanded; without one,
80
- * *Expand* opens it in a dialog over the page.
81
- */
82
- expandTarget?: BalloonExpandTarget;
83
- /** What its notebook is called: `Accounting's notebook`. */
84
- notebookTitle?: string;
85
- /**
86
- * What the person may ask it: chips in its balloon while it waits for a
87
- * question, and a group of its menu; one chosen is sent (`onSuggestion`).
88
- */
89
- suggestions?: readonly BalloonSuggestion[];
90
- onSuggestion?: (suggestion: BalloonSuggestion) => void;
91
- /**
92
- * The team's Agent Inspector's tracer: its menu offers *Inspect the
93
- * agent…*, its own spans (what it did, and the A2A requests sent to it).
94
- */
95
- inspector?: OtelLiveTracer | null;
96
- /** Stops its turn, from its menu, while it works. */
97
- onStop?: () => void;
98
- /** What its menu's *About* and *Agent Details…* say. */
99
- about?: AssistantAbout;
100
- /**
101
- * Its code sandbox, for *Code Sandbox Details…*. The entry — the agent in
102
- * the page — has the kernel of the notebook open under the graph, while
103
- * one is.
104
- */
105
- sandbox?: AssistantSandbox;
106
- /** The host's own entries of its menu. */
107
- contextMenu?: readonly AssistantMenuItem[];
108
- /**
109
- * What its menu calls a click on it (`onToggle`): `Ask Sales` where it
110
- * takes the person to the composer. Without `onToggle`, its menu has no
111
- * such entry.
112
- */
113
- conversationLabel?: string;
114
- /**
115
- * What it said and did, as its `history` balloon lists it (`useA2ATeam`'s
116
- * `entryHistory`, `peerHistory`): shown when its menu chooses *History*.
117
- */
118
- history?: readonly DisplayItem[];
119
- /** Its balloon's display was chosen in its menu: the page may keep it. */
120
- onBalloonDisplayChange?: (display: BalloonDisplay) => void;
121
- };
122
- /** Who talks to whom over A2A: an edge of the graph. */
123
- export type A2ATeamGraphLink = {
124
- from: string;
125
- to: string;
126
- };
127
- /** Where a member stands: fractions of the box, as a scene's stage says. */
128
- export type A2ATeamGraphPosition = {
129
- x: number;
130
- y: number;
131
- };
50
+ import type { A2ATeamGraphLink, A2ATeamGraphMember, A2ATeamGraphPosition, SceneBackdropId } from './backdrops/types';
51
+ import type { SelectedEdge } from './transcript/selection';
52
+ export type { A2ATeamGraphLink, A2ATeamGraphMember, A2ATeamGraphPosition, } from './backdrops/types';
53
+ export { MEMBER_HANDLES, clampOffset, connectionDetails, connectionLabel, memberLabel, type MemberBounds, type MemberOffset, } from './backdrops/nodes';
54
+ export { arrowPath } from './backdrops/edges';
55
+ export { CallEdge, LinkEdge, MemberNode, ConnectionNode, } from './backdrops/studio';
132
56
  export type A2ATeamGraphProps = {
133
57
  /** The member that asks: drawn on the left. */
134
58
  entry: A2ATeamGraphMember;
@@ -164,25 +88,32 @@ export type A2ATeamGraphProps = {
164
88
  * The least room above the members for their balloons, in pixels at full
165
89
  * scale: the graph keeps what its balloons take now (`useBalloonRoom`) —
166
90
  * suggestions, *more* unfolded — and never less than this. A notebook in
167
- * a balloon gets at least its own.
91
+ * a balloon gets at least its own. Unsaid, the backdrop's.
168
92
  */
169
93
  balloonRoom?: number;
94
+ /**
95
+ * How the stage is drawn (`SceneStageSpec.backdrop`): `studio`, the
96
+ * drawing every scene had first, `hero` or `plain`. `studio` when unsaid,
97
+ * and for an id the graph does not know.
98
+ */
99
+ backdrop?: SceneBackdropId;
100
+ /**
101
+ * The exchange selected in the transcript, as the edge it is on
102
+ * (`selectedEdgeOf`, SCENES S3-02): drawn with a halo on every backdrop —
103
+ * the edge by its id, or by its ends either way, since an answer travels
104
+ * the asker's edge back.
105
+ */
106
+ selected?: SelectedEdge | null;
107
+ /**
108
+ * An edge was clicked: the page selects its latest exchange
109
+ * (`latestLineOnEdge`). With it, the edges take the pointer.
110
+ */
111
+ onSelectEdge?: (edge: A2ATeamGraphEdge) => void;
170
112
  };
171
- /**
172
- * The handles an edge leaves and reaches a member by, named: the middle of
173
- * its character on each side, chosen by where the other member stands.
174
- */
175
- export declare const MEMBER_HANDLES: {
176
- readonly inLeft: "in-left";
177
- readonly outRight: "out-right";
178
- readonly inTop: "in-top";
179
- readonly outBottom: "out-bottom";
180
- readonly inRight: "in-right";
181
- readonly outLeft: "out-left";
182
- readonly inBottom: "in-bottom";
183
- readonly outTop: "out-top";
184
- readonly connections: "connections";
185
- };
113
+ /** An edge of the graph, as a click names it: `selectedEdgeOf`'s shape without the selection's key. */
114
+ export type A2ATeamGraphEdge = Omit<SelectedEdge, 'key'>;
115
+ /** Whether a selection is on the edge between two ends: by its id, or by its ends either way. */
116
+ export declare function edgeSelected(selected: SelectedEdge | null | undefined, from: string, to: string): boolean;
186
117
  /**
187
118
  * The handles for an edge from one place to another: rightward across,
188
119
  * leftward across, or down or up when the other member is mostly below or
@@ -198,47 +129,22 @@ export declare function handlesBetween(from: {
198
129
  sourceHandle: string;
199
130
  targetHandle: string;
200
131
  };
201
- /** How far a member was moved from its place, in the graph's coordinates. */
202
- export type MemberOffset = {
203
- dx: number;
204
- dy: number;
205
- };
206
- /** How far a member may move: its node stays inside the graph's box. */
207
- export type MemberBounds = {
208
- minDx: number;
209
- maxDx: number;
210
- minDy: number;
211
- maxDy: number;
212
- };
213
- /** An offset kept inside its bounds. */
214
- export declare function clampOffset(offset: MemberOffset, bounds: MemberBounds): MemberOffset;
215
- /** A member's accessible name: its name, and where it runs. */
216
- export declare function memberLabel(member: Pick<A2ATeamGraphMember, 'name' | 'where'>): string;
217
- /** A connection's accessible name: `Odoo, Accounting's MCP connection`. */
218
- export declare function connectionLabel(connection: Pick<A2ATeamConnection, 'label'>, member: string | undefined): string;
219
- /** What a connection's details say: the server, who reaches it, and the call running now. */
220
- export declare function connectionDetails(connection: Pick<A2ATeamConnection, 'name' | 'tools'>, member: string | undefined, calling?: string): string;
221
132
  /** The words on the edge for a flow. */
222
133
  export declare function flowWords(flow: A2ATeamFlow, entry: string, peer: string): string;
134
+ /** What a link to a connection says at rest, as `A2A` says the members' link. */
135
+ export declare const MCP_LABEL = "MCP";
223
136
  /**
224
- * An arrowhead at the end a message goes to — the target's when `forward`,
225
- * the source's otherwise — pointing along the edge.
137
+ * What a link to a connection says while it carries a call, short enough
138
+ * for the room beside the member: the tool going out, `MCP · list invoices`,
139
+ * then `MCP · Odoo answers` as its result comes back.
226
140
  */
227
- export declare function arrowPath(source: {
228
- x: number;
229
- y: number;
230
- }, target: {
231
- x: number;
232
- y: number;
233
- }, forward: boolean): string;
234
- type CallData = {
235
- /** The tool being called, in words (`list invoices`); empty while none is. */
236
- calling: string;
237
- };
238
- /** A member's link to a connection: still, or flowing toward it while a call runs. */
239
- export declare const CallEdge: import("react").NamedExoticComponent<EdgeProps<Edge<CallData>>>;
240
- /** What a member's call says, on its edge and to a screen reader. */
241
- export declare function callWords(member: string, connection: A2ATeamConnection, tool: string): string;
141
+ export declare function callEdgeWords(connection: A2ATeamConnection, tool: string, flow: A2ATeamFlow): string;
142
+ /**
143
+ * What a member's call says, on its edge and to a screen reader: the call
144
+ * going out, `Accounting calls Odoo · list invoices`, then its result
145
+ * coming back, `Odoo answers Accounting · list invoices`.
146
+ */
147
+ export declare function callWords(member: string, connection: A2ATeamConnection, tool: string, flow?: A2ATeamFlow): string;
242
148
  /** The viewport that shows a graph `drawn` wide whole in a box `width` wide, never larger than drawn. */
243
149
  export declare function teamViewport(width: number, drawn?: number): Viewport;
244
150
  /** Where a member is laid out: its column and row, from the positions or the default. */
@@ -260,5 +166,5 @@ export declare function placeMembers(entryId: string, peerIds: readonly string[]
260
166
  /** The one flow the graph reports for all its edges: asking before answering before still. */
261
167
  export declare function flowOfAll(flows: readonly A2ATeamFlow[]): A2ATeamFlow;
262
168
  /** Draw a team over A2A, each link moving with what travels on it. */
263
- export declare function A2ATeamGraph({ entry, peer, peers: more, flow, flows, connected, label, labels, links, positions, size, calls, balloonRoom, }: A2ATeamGraphProps): JSX.Element;
169
+ export declare function A2ATeamGraph({ entry, peer, peers: more, flow, flows, connected, label, labels, links, positions, size, calls, balloonRoom, backdrop, selected, onSelectEdge, }: A2ATeamGraphProps): JSX.Element;
264
170
  export default A2ATeamGraph;