@datalayer/agent-runtimes 1.3.84 → 1.3.86

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 (162) hide show
  1. package/lib/chat/ChatFloating.d.ts +5 -4
  2. package/lib/chat/ChatFloating.js +8 -6
  3. package/lib/chat/ChatLanguage.d.ts +17 -0
  4. package/lib/chat/ChatLanguage.js +29 -0
  5. package/lib/chat/assistant/AssistantStage.d.ts +4 -4
  6. package/lib/chat/assistant/AssistantStage.js +4 -2
  7. package/lib/chat/assistant/BalloonParts.d.ts +2 -1
  8. package/lib/chat/assistant/BalloonParts.js +12 -5
  9. package/lib/chat/assistant/BalloonVisual.js +3 -1
  10. package/lib/chat/assistant/ConversationBalloon.d.ts +1 -1
  11. package/lib/chat/assistant/ConversationBalloon.js +3 -2
  12. package/lib/chat/assistant/SpeechBalloon.d.ts +1 -1
  13. package/lib/chat/assistant/SpeechBalloon.js +10 -7
  14. package/lib/chat/assistant/toolLine.d.ts +8 -4
  15. package/lib/chat/assistant/toolLine.js +13 -9
  16. package/lib/chat/base/ChatBase.js +14 -11
  17. package/lib/chat/display/EmptyState.js +3 -1
  18. package/lib/chat/index.d.ts +2 -0
  19. package/lib/chat/index.js +3 -0
  20. package/lib/chat/messages/ChatMessages.js +3 -1
  21. package/lib/chat/messages/TurnFooter.js +3 -1
  22. package/lib/chat/prompt/InputPrompt.js +3 -1
  23. package/lib/chat/prompt/InputPromptLexical.js +3 -1
  24. package/lib/chat/prompt/footer/InPromptFooter.js +3 -1
  25. package/lib/chat/tools/ToolApprovalBanner.js +5 -3
  26. package/lib/chat/tools/ToolApprovalDialog.js +5 -3
  27. package/lib/chat/tools/ToolCallDisplay.js +29 -25
  28. package/lib/chat/words.d.ts +102 -0
  29. package/lib/chat/words.js +460 -0
  30. package/lib/components/a2ui/custom/index.d.ts +39 -0
  31. package/lib/components/a2ui/custom/index.js +95 -0
  32. package/lib/components/a2ui/custom/sandbox.d.ts +73 -0
  33. package/lib/components/a2ui/custom/sandbox.js +188 -0
  34. package/lib/components/a2ui/datalayer/index.d.ts +1 -1
  35. package/lib/components/a2ui/datalayer/index.js +1 -1
  36. package/lib/components/a2ui/datalayer/schema.d.ts +10 -0
  37. package/lib/components/a2ui/datalayer/schema.js +12 -1
  38. package/lib/components/a2ui/index.d.ts +1 -0
  39. package/lib/components/a2ui/index.js +1 -0
  40. package/lib/components/a2ui/visibleWhen.d.ts +12 -1
  41. package/lib/components/a2ui/visibleWhen.js +24 -3
  42. package/lib/components/inspector/AgentInspector.d.ts +25 -44
  43. package/lib/components/inspector/AgentInspector.js +27 -215
  44. package/lib/components/inspector/AgentInspectorDialog.d.ts +6 -6
  45. package/lib/components/inspector/AgentInspectorDialog.js +2 -2
  46. package/lib/components/inspector/a2aSpans.d.ts +51 -0
  47. package/lib/components/inspector/a2aSpans.js +373 -0
  48. package/lib/components/inspector/agentSpans.d.ts +120 -0
  49. package/lib/components/inspector/agentSpans.js +253 -0
  50. package/lib/components/inspector/chatSpans.d.ts +36 -0
  51. package/lib/components/inspector/chatSpans.js +50 -0
  52. package/lib/components/inspector/classify.d.ts +11 -8
  53. package/lib/components/inspector/classify.js +23 -24
  54. package/lib/components/inspector/index.d.ts +7 -7
  55. package/lib/components/inspector/index.js +7 -7
  56. package/lib/components/teams/A2ATeamGraph.d.ts +117 -22
  57. package/lib/components/teams/A2ATeamGraph.js +217 -97
  58. package/lib/components/teams/SceneTranscript.d.ts +29 -0
  59. package/lib/components/teams/SceneTranscript.js +54 -0
  60. package/lib/components/teams/SceneView.d.ts +41 -0
  61. package/lib/components/teams/SceneView.js +28 -0
  62. package/lib/components/teams/index.d.ts +4 -0
  63. package/lib/components/teams/index.js +4 -0
  64. package/lib/components/teams/sceneStage.d.ts +120 -0
  65. package/lib/components/teams/sceneStage.js +170 -0
  66. package/lib/components/teams/sceneTranscript.d.ts +74 -0
  67. package/lib/components/teams/sceneTranscript.js +294 -0
  68. package/lib/components/teams/teamConnections.d.ts +2 -2
  69. package/lib/components/teams/teamConnections.js +3 -3
  70. package/lib/components/teams/useA2ATeam.d.ts +94 -22
  71. package/lib/components/teams/useA2ATeam.js +356 -140
  72. package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
  73. package/lib/examples/AgentA2ATeamExample.js +70 -60
  74. package/lib/examples/AssistantExample.js +2 -2
  75. package/lib/examples/ScenesExample.d.ts +26 -0
  76. package/lib/examples/ScenesExample.js +241 -0
  77. package/lib/examples/example-selector.js +1 -0
  78. package/lib/examples/exampleGroups.js +3 -0
  79. package/lib/examples/index.d.ts +1 -0
  80. package/lib/examples/index.js +1 -0
  81. package/lib/examples/main.js +4 -0
  82. package/lib/loop/apps/AppComposer.d.ts +2 -1
  83. package/lib/loop/apps/AppComposer.js +6 -3
  84. package/lib/loop/apps/AppRenderer.d.ts +2 -1
  85. package/lib/loop/apps/AppRenderer.js +34 -25
  86. package/lib/loop/apps/appspec.d.ts +11 -1
  87. package/lib/loop/apps/appspec.js +115 -1
  88. package/lib/loop/apps/checks.d.ts +8 -0
  89. package/lib/loop/apps/checks.js +106 -4
  90. package/lib/loop/apps/customComponents.d.ts +41 -0
  91. package/lib/loop/apps/customComponents.js +260 -0
  92. package/lib/loop/apps/hostTools.d.ts +8 -1
  93. package/lib/loop/apps/hostTools.js +9 -0
  94. package/lib/loop/apps/index.d.ts +1 -0
  95. package/lib/loop/apps/index.js +1 -0
  96. package/lib/loop/core/a2uiComponents.d.ts +10 -0
  97. package/lib/loop/core/a2uiComponents.js +15 -0
  98. package/lib/loop/core/index.d.ts +1 -0
  99. package/lib/loop/core/index.js +3 -0
  100. package/lib/loop/embed/AppEmbed.d.ts +11 -3
  101. package/lib/loop/embed/AppEmbed.js +42 -16
  102. package/lib/loop/embed/element.d.ts +5 -0
  103. package/lib/loop/embed/element.js +61 -5
  104. package/lib/loop/embed/embedConfig.d.ts +8 -0
  105. package/lib/loop/embed/embedConfig.js +18 -0
  106. package/lib/loop/embed/hostBridge.d.ts +28 -2
  107. package/lib/loop/embed/hostBridge.js +22 -2
  108. package/lib/loop/index.d.ts +4 -3
  109. package/lib/loop/index.js +4 -3
  110. package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +7 -1
  111. package/lib/loop/plugins/a2ui-surface/InlineSurface.js +7 -2
  112. package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +4 -1
  113. package/lib/loop/plugins/a2ui-surface/ownComponents.d.ts +6 -0
  114. package/lib/loop/plugins/a2ui-surface/ownComponents.js +30 -0
  115. package/lib/loop/plugins/app-components/index.d.ts +26 -0
  116. package/lib/loop/plugins/app-components/index.js +56 -0
  117. package/lib/loop/plugins/app-elements/AppElements.js +4 -1
  118. package/lib/loop/plugins/app-page/AppPage.d.ts +9 -0
  119. package/lib/loop/plugins/app-page/AppPage.js +106 -8
  120. package/lib/loop/plugins/app-page/appPageModel.d.ts +17 -1
  121. package/lib/loop/plugins/app-page/appPageModel.js +120 -4
  122. package/lib/loop/plugins/app-page/index.d.ts +1 -0
  123. package/lib/loop/plugins/app-page/index.js +1 -0
  124. package/lib/loop/plugins/app-page/pageRun.d.ts +46 -0
  125. package/lib/loop/plugins/app-page/pageRun.js +111 -0
  126. package/lib/loop/plugins/assistant/index.js +8 -6
  127. package/lib/loop/plugins/chat/ChatView.js +6 -4
  128. package/lib/loop/plugins/chat/index.d.ts +1 -1
  129. package/lib/loop/plugins/chat/index.js +2 -1
  130. package/lib/models/PageTag.d.ts +1 -1
  131. package/lib/runtimes/browser/a2aPeer.d.ts +2 -1
  132. package/lib/specs/actions.js +78 -1
  133. package/lib/specs/apps.d.ts +5 -0
  134. package/lib/specs/apps.js +1384 -231
  135. package/lib/specs/appspecSchema.js +191 -1
  136. package/lib/specs/index.d.ts +1 -0
  137. package/lib/specs/index.js +1 -0
  138. package/lib/specs/mcpServers.js +8 -2
  139. package/lib/specs/scenes.d.ts +21 -0
  140. package/lib/specs/scenes.js +1191 -0
  141. package/lib/specs/teams/teams.d.ts +3 -0
  142. package/lib/specs/teams/teams.js +249 -0
  143. package/lib/types/agentspecs.d.ts +70 -0
  144. package/lib/types/chat.d.ts +6 -5
  145. package/lib/types/index.d.ts +1 -0
  146. package/lib/types/index.js +1 -0
  147. package/lib/types/scenes.d.ts +207 -0
  148. package/lib/types/scenes.js +5 -0
  149. package/lib/types/teams.d.ts +13 -2
  150. package/package.json +4 -4
  151. package/scripts/codegen/generate_agents.py +1 -0
  152. package/scripts/codegen/generate_apps.py +4 -2
  153. package/scripts/codegen/generate_scenes.py +301 -0
  154. package/scripts/codegen/generate_teams.py +15 -2
  155. package/lib/components/inspector/JsonTree.d.ts +0 -24
  156. package/lib/components/inspector/JsonTree.js +0 -89
  157. package/lib/components/inspector/a2aInspect.d.ts +0 -56
  158. package/lib/components/inspector/a2aInspect.js +0 -478
  159. package/lib/components/inspector/agentInspector.d.ts +0 -179
  160. package/lib/components/inspector/agentInspector.js +0 -267
  161. package/lib/components/inspector/chatInspect.d.ts +0 -38
  162. package/lib/components/inspector/chatInspect.js +0 -65
@@ -36,7 +36,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
36
36
  * agent calls as `host_open_ticket`, each as the rule naming it decides; the
37
37
  * element raises `message` when the application has answered and `action`
38
38
  * when it called a function of the page, beside `decision` and
39
- * `token-expired`.
39
+ * `token-expired`. Who its user is, when it matters (LOOP D-21): the page
40
+ * sets `user-token` (or `element.userToken`) to the token its server signed
41
+ * naming the user, sent with every run of an application that says
42
+ * `user: signed`.
40
43
  *
41
44
  * Window messages (LOOP P-25), on the host's own server: the element raises
42
45
  * `window-message` (`detail.data`) when the application's code tells the page
@@ -60,7 +63,7 @@ import { parseAppspec } from '../apps/appspec';
60
63
  import { readAppspecYaml } from '../apps/yaml';
61
64
  import { AppEmbed, embedAssistantCharacter } from './AppEmbed';
62
65
  import { WINDOW_NOT_YET } from './embedSession';
63
- import { EMBED_HOST_VARIABLES, EMBED_OBSERVED_ATTRIBUTES, EMBED_TAG, EmbedAttributeError, embedLookOf, inlineHeightOf, resumeOf, } from './embedConfig';
66
+ import { EMBED_HOST_VARIABLES, EMBED_OBSERVED_ATTRIBUTES, EMBED_TAG, EmbedAttributeError, embedLookOf, inlineHeightOf, languageOf, resumeOf, } from './embedConfig';
64
67
  import { embedShadowCss } from './embedTheme';
65
68
  /** What the framed run page and the element say to each other. */
66
69
  export const EMBED_MESSAGES = {
@@ -122,6 +125,18 @@ export function frameSrcOf(origin, app, token = '') {
122
125
  // and no access log.
123
126
  return `${origin.replace(/\/+$/, '')}/public/apps/${encodeURIComponent(app)}/run?embed=1${token ? `#token=${encodeURIComponent(token)}` : ''}`;
124
127
  }
128
+ /**
129
+ * A framed run page told the visitor's language (P-26): `language=` in its
130
+ * query, before the fragment that holds the token.
131
+ */
132
+ export function frameInLanguage(src, language) {
133
+ if (!language) {
134
+ return src;
135
+ }
136
+ const at = src.indexOf('#');
137
+ const [page, fragment] = at < 0 ? [src, ''] : [src.slice(0, at), src.slice(at)];
138
+ return `${page}${page.includes('?') ? '&' : '?'}language=${encodeURIComponent(language)}${fragment}`;
139
+ }
125
140
  /**
126
141
  * An application on Datalayer, read with its embed token: what the space
127
142
  * keeps of it (`model_s`, the stored definition), whose `spec` is the
@@ -204,6 +219,8 @@ export function defineDatalayerAppElement(options = {}) {
204
219
  /** What the page gives the application, read at each call (D-10). */
205
220
  host = {
206
221
  context: () => this.passed,
222
+ // Read as each run is sent (D-21): a renewed one goes with the next.
223
+ userToken: () => this.getAttribute('user-token') || undefined,
207
224
  functions: this.offered,
208
225
  onWindowPort: post => {
209
226
  this.windowPort = post;
@@ -260,6 +277,22 @@ export function defineDatalayerAppElement(options = {}) {
260
277
  this.removeAttribute('token');
261
278
  }
262
279
  }
280
+ /**
281
+ * The token the host's server signed naming its user (LOOP D-21), the
282
+ * `user-token` attribute's: what an application that says `user: signed`
283
+ * opens its session with. Set it to hand a new one over.
284
+ */
285
+ get userToken() {
286
+ return this.getAttribute('user-token') ?? '';
287
+ }
288
+ set userToken(value) {
289
+ if (value) {
290
+ this.setAttribute('user-token', String(value));
291
+ }
292
+ else {
293
+ this.removeAttribute('user-token');
294
+ }
295
+ }
263
296
  connectedCallback() {
264
297
  window.addEventListener('message', this.onMessage);
265
298
  void this.load();
@@ -272,6 +305,11 @@ export function defineDatalayerAppElement(options = {}) {
272
305
  if (!this.isConnected || before === after) {
273
306
  return;
274
307
  }
308
+ // The user's signed token is read as each run is sent (D-21): nothing
309
+ // is drawn again for a new one.
310
+ if (name === 'user-token') {
311
+ return;
312
+ }
275
313
  // A new token is handed to the framed page that is already there,
276
314
  // which keeps what the person entered.
277
315
  if (name === 'token' && this.frame?.contentWindow && after) {
@@ -279,7 +317,15 @@ export function defineDatalayerAppElement(options = {}) {
279
317
  return;
280
318
  }
281
319
  // How it looks is drawn again; what it is, read again.
282
- if (['mode', 'accent', 'theme', 'font', 'height', 'resume'].includes(name)) {
320
+ if ([
321
+ 'mode',
322
+ 'accent',
323
+ 'theme',
324
+ 'font',
325
+ 'height',
326
+ 'resume',
327
+ 'language',
328
+ ].includes(name)) {
283
329
  this.draw();
284
330
  return;
285
331
  }
@@ -411,11 +457,19 @@ export function defineDatalayerAppElement(options = {}) {
411
457
  return;
412
458
  }
413
459
  if (source.kind === 'frame') {
460
+ let framedLanguage;
461
+ try {
462
+ framedLanguage = languageOf(this.getAttribute('language'));
463
+ }
464
+ catch (error) {
465
+ this.say(error.message);
466
+ return;
467
+ }
414
468
  this.setAttribute('data-embed-mode', 'inline');
415
469
  const shadow = this.skeleton();
416
470
  const frame = document.createElement('iframe');
417
471
  frame.className = 'datalayer-app-frame';
418
- frame.src = source.src;
472
+ frame.src = frameInLanguage(source.src, framedLanguage);
419
473
  frame.title = 'Datalayer app';
420
474
  frame.style.height = `${inlineHeightOf(this.getAttribute('height'))}px`;
421
475
  frame.setAttribute('allow', 'clipboard-write');
@@ -425,9 +479,11 @@ export function defineDatalayerAppElement(options = {}) {
425
479
  }
426
480
  let look;
427
481
  let resume;
482
+ let language;
428
483
  try {
429
484
  look = embedLookOf(this.inputs(), source.app);
430
485
  resume = resumeOf(this.getAttribute('resume'));
486
+ language = languageOf(this.getAttribute('language'));
431
487
  }
432
488
  catch (error) {
433
489
  this.say(error.message);
@@ -454,7 +510,7 @@ export function defineDatalayerAppElement(options = {}) {
454
510
  this.root.render(_jsx(StyleSheetManager, { target: shadow, children: _jsx(AppEmbed, { app: source.app, mode: look.mode, accent: look.accent, colorMode: look.colorMode, font: look.font, serverUrl: server, ...(server && this.token ? { embedToken: this.token } : {}), height: inlineHeightOf(this.getAttribute('height')), ownPortal: true, host: this.host,
455
511
  // The visit's session kept in the page's storage, and picked
456
512
  // up again after a reload, unless the host says not (D-13).
457
- resume: resume }) }));
513
+ resume: resume, ...(language ? { language } : {}) }) }));
458
514
  }
459
515
  onMessage(event) {
460
516
  if (!this.frame || event.source !== this.frame.contentWindow) {
@@ -34,6 +34,8 @@ export declare const EMBED_ATTRIBUTES: {
34
34
  readonly api: "Datalayer’s API, where an application is read with its token; the platform’s by default.";
35
35
  readonly height: "The height of an inline application, in pixels; 640 by default.";
36
36
  readonly resume: "true or false: whether a visit’s conversation is picked up again after the page reloads, kept in the page’s storage; true by default.";
37
+ readonly language: "The language the visitor reads the application in, as BCP 47 tags it (fr, pt-BR): its translation into it, and the chat’s own words; the visitor’s browser’s by default.";
38
+ readonly 'user-token': "A token your server signed with the deployment’s secret, naming your user (HS256: sub, name, exp at most an hour away): what an application that says user: signed opens its session with.";
37
39
  };
38
40
  export type EmbedAttribute = keyof typeof EMBED_ATTRIBUTES;
39
41
  /** Every attribute the element watches. */
@@ -103,6 +105,12 @@ export declare function embedLookOf(inputs: EmbedInputs, app?: Pick<AppSpec, 'in
103
105
  * refused in a sentence.
104
106
  */
105
107
  export declare function resumeOf(value: string | null | undefined): boolean;
108
+ /**
109
+ * The language a host says its visitor reads (LOOP P-26), checked: a BCP 47
110
+ * tag (`fr`, `pt-BR`), else refused in a sentence. Unsaid, none: the
111
+ * visitor's browser's.
112
+ */
113
+ export declare function languageOf(value: string | null | undefined): string | undefined;
106
114
  /** The height of an inline application, in pixels. */
107
115
  export declare function inlineHeightOf(value: string | null | undefined): number;
108
116
  /** What a snippet says beside the application's id. */
@@ -3,6 +3,7 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import { APP_ACCENTS, APP_EMBED_MODES } from '../apps/appspec';
6
+ import { LANGUAGE_TAG } from '../apps/language';
6
7
  /** The element's name: the one a host writes. */
7
8
  export const EMBED_TAG = 'datalayer-app';
8
9
  /** Where the script is served on a Datalayer origin; the stylesheet beside it. */
@@ -25,6 +26,8 @@ export const EMBED_ATTRIBUTES = {
25
26
  api: 'Datalayer’s API, where an application is read with its token; the platform’s by default.',
26
27
  height: 'The height of an inline application, in pixels; 640 by default.',
27
28
  resume: 'true or false: whether a visit’s conversation is picked up again after the page reloads, kept in the page’s storage; true by default.',
29
+ language: 'The language the visitor reads the application in, as BCP 47 tags it (fr, pt-BR): its translation into it, and the chat’s own words; the visitor’s browser’s by default.',
30
+ 'user-token': 'A token your server signed with the deployment’s secret, naming your user (HS256: sub, name, exp at most an hour away): what an application that says user: signed opens its session with.',
28
31
  };
29
32
  /** Every attribute the element watches. */
30
33
  export const EMBED_OBSERVED_ATTRIBUTES = Object.keys(EMBED_ATTRIBUTES);
@@ -120,6 +123,21 @@ export function embedLookOf(inputs, app) {
120
123
  export function resumeOf(value) {
121
124
  return (oneOf(value, ['true', 'false'], 'a value of resume') !== 'false');
122
125
  }
126
+ /**
127
+ * The language a host says its visitor reads (LOOP P-26), checked: a BCP 47
128
+ * tag (`fr`, `pt-BR`), else refused in a sentence. Unsaid, none: the
129
+ * visitor's browser's.
130
+ */
131
+ export function languageOf(value) {
132
+ const text = value?.trim();
133
+ if (!text) {
134
+ return undefined;
135
+ }
136
+ if (!LANGUAGE_TAG.test(text)) {
137
+ throw new EmbedAttributeError(`datalayer-app: "${text}" is not a language as BCP 47 tags it, such as fr or pt-BR.`);
138
+ }
139
+ return text;
140
+ }
123
141
  /** The height of an inline application, in pixels. */
124
142
  export function inlineHeightOf(value) {
125
143
  const height = Number.parseInt(value ?? '', 10);
@@ -16,6 +16,16 @@
16
16
  * the host's own server (`server`), and what it answers comes back as
17
17
  * `window-message` events.
18
18
  *
19
+ * Who its user is (LOOP D-21): what the page says as `user` is a claim.
20
+ * An application whose Appspec says `deployment.embedded.host.user: signed`
21
+ * takes only a token the host's **server** signed with the deployment's
22
+ * secret (HS256: `sub`, `name`, `exp` at most an hour away), which the page
23
+ * hands over as `userToken` (the element's `user-token`). It goes with every
24
+ * run as `forwardedProps.loop.user_token` (`hostUserRunProps`); the runtime
25
+ * verifies it where the session opens, refuses the session without it, and
26
+ * answers `host_context`'s `user` with the user it names. The secret never
27
+ * reaches the page.
28
+ *
19
29
  * Offered: the functions of the page its Appspec names
20
30
  * (`deployment.embedded.host.functions`), each called by its agent as the
21
31
  * tool `host_<name>`. Every one of these tools is decided in the page by
@@ -30,8 +40,9 @@
30
40
  */
31
41
  import type { AppSpec } from '../../types/agentspecs';
32
42
  import type { FrontendToolDefinition } from '../../types/tools';
33
- import { HOST_CONTEXT_TOOL, HOST_NAME, hostTool, hostToolsOf } from '../apps/hostTools';
34
- export { HOST_CONTEXT_TOOL, HOST_NAME, hostTool, hostToolsOf };
43
+ import type { RunPropsContribution } from '../core';
44
+ import { HOST_CONTEXT_TOOL, HOST_NAME, hostTool, hostToolsOf, signedUser } from '../apps/hostTools';
45
+ export { HOST_CONTEXT_TOOL, HOST_NAME, hostTool, hostToolsOf, signedUser };
35
46
  /** What the application tells the page it sits in. */
36
47
  export type HostEvent = {
37
48
  type: 'message';
@@ -64,6 +75,12 @@ export type HostFunction = (args: Record<string, unknown>) => unknown | Promise<
64
75
  export type AppEmbedHost = {
65
76
  /** What the page passes now: its visitor (`user`), itself (`page`), values of its own. */
66
77
  context?: () => Record<string, unknown>;
78
+ /**
79
+ * The token the host's server signed naming its user (LOOP D-21), read as
80
+ * each run is sent: what an application that says `user: signed` opens its
81
+ * session with. Never the deployment's secret.
82
+ */
83
+ userToken?: () => string | undefined;
67
84
  /** The page's functions, by the names the Appspec gives them. */
68
85
  functions?: Record<string, HostFunction>;
69
86
  /** Told what the application does: `message`, `action`. */
@@ -98,3 +115,12 @@ export declare const hostNotAllowed: (tool: string) => string;
98
115
  * `host` is read at each call, so that what the page passes is the newest.
99
116
  */
100
117
  export declare function hostFrontendTools(app: Pick<AppSpec, 'connections' | 'rules' | 'deployment'>, host: () => AppEmbedHost): FrontendToolDefinition[];
118
+ /**
119
+ * What goes with every run for the host's user (LOOP D-21): the token the
120
+ * host's server signed, as `forwardedProps.loop.user_token`, for an
121
+ * application that says `user: signed` — read at each run, so that the
122
+ * newest is sent; nothing when the page gives none, and the runtime refuses
123
+ * the session in a sentence. `undefined` for an application that takes what
124
+ * the page says.
125
+ */
126
+ export declare function hostUserRunProps(app: Pick<AppSpec, 'deployment'>, host: () => AppEmbedHost): RunPropsContribution | undefined;
@@ -3,8 +3,8 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import { behaviourFor } from '../apps/rules';
6
- import { HOST_CONTEXT_TOOL, HOST_NAME, hostTool, hostToolsOf, } from '../apps/hostTools';
7
- export { HOST_CONTEXT_TOOL, HOST_NAME, hostTool, hostToolsOf };
6
+ import { HOST_CONTEXT_TOOL, HOST_NAME, hostTool, hostToolsOf, signedUser, } from '../apps/hostTools';
7
+ export { HOST_CONTEXT_TOOL, HOST_NAME, hostTool, hostToolsOf, signedUser };
8
8
  /** The sentence a call is refused with when no rule names its tool. */
9
9
  export const hostRefused = (tool) => `No rule of this application names ${tool}: it is left to the person, and the page was not called.`;
10
10
  /** The sentence a call is refused with when the person did not allow it. */
@@ -102,3 +102,23 @@ export function hostFrontendTools(app, host) {
102
102
  }
103
103
  return tools;
104
104
  }
105
+ /**
106
+ * What goes with every run for the host's user (LOOP D-21): the token the
107
+ * host's server signed, as `forwardedProps.loop.user_token`, for an
108
+ * application that says `user: signed` — read at each run, so that the
109
+ * newest is sent; nothing when the page gives none, and the runtime refuses
110
+ * the session in a sentence. `undefined` for an application that takes what
111
+ * the page says.
112
+ */
113
+ export function hostUserRunProps(app, host) {
114
+ if (!signedUser(app.deployment.embedded?.host)) {
115
+ return undefined;
116
+ }
117
+ return {
118
+ id: 'host-user',
119
+ props: () => {
120
+ const token = host().userToken?.()?.trim();
121
+ return token ? { loop: { user_token: token } } : {};
122
+ },
123
+ };
124
+ }
@@ -51,6 +51,7 @@ export { AgentA2uiPlugin, AGENT_A2UI_PLUGIN_NAME } from './plugins/agent-a2ui';
51
51
  export { AgentA2uiJupyterOutputPlugin, AGENT_A2UI_JUPYTER_OUTPUT_PLUGIN_NAME, } from './plugins/agent-a2ui-jupyter-output';
52
52
  export { defineA2uiScenePlugin, type A2uiSceneOptions, } from './plugins/a2ui-scene';
53
53
  export { APP_KIND_PATHS, APP_PAGE_PLUGIN_NAME, APP_PAGE_SURFACE, AppPage, appKindPaths, appPageAction, appPageData, defineAppPagePlugin, hasAppPage, surfaceUnshown, type AppKindPaths, type AppPageAction, type AppPageKind, type AppPagePath, } from './plugins/app-page';
54
+ export { appComponentsPluginName, defineAppComponentsPlugin, hasCustomComponents, } from './plugins/app-components';
54
55
  export { APP_RULES_PLUGIN_NAME, APP_RULES_WORDS, AppRulesCard, defineAppRulesPlugin, } from './plugins/app-rules';
55
56
  export { APP_ACTIVITY_PLUGIN_NAME, APP_ACTIVITY_WORDS, AppActivity, defineAppActivityPlugin, } from './plugins/app-activity';
56
57
  export { APP_COMPUTER_PLUGIN_NAME, AppComputer, defineAppComputerPlugin, } from './plugins/app-computer';
@@ -69,10 +70,10 @@ export { PromptPlugin, PROMPT_PLUGIN_NAME } from './plugins/prompt';
69
70
  export { LoopEmbed, type LoopEmbedProps } from './embed/LoopEmbed';
70
71
  export { AppEmbed, AppFloating, embedAssistantCharacter, type AppEmbedProps, type AppFloatingProps, type EmbedSession, useEmbedSession, useHostBridge, } from './embed/AppEmbed';
71
72
  export { EMBED_SESSION_KEY_PREFIX, embedSessionKey, visitOfToken, } from './embed/embedSession';
72
- export { HOST_CONTEXT_TOOL, hostFrontendTools, hostNotAllowed, hostRefused, hostTool, hostToolsOf, type AppEmbedHost, type HostEvent, type HostFunction, type HostQuestion, } from './embed/hostBridge';
73
+ export { HOST_CONTEXT_TOOL, hostFrontendTools, hostNotAllowed, hostRefused, hostTool, hostToolsOf, hostUserRunProps, signedUser, type AppEmbedHost, type HostEvent, type HostFunction, type HostQuestion, } from './embed/hostBridge';
73
74
  export { defineDatalayerAppElement, type DatalayerAppElementOptions, } from './embed/element';
74
- export { EMBED_ATTRIBUTES, EMBED_HOST_VARIABLES, EMBED_TAG, embedLookOf, embedSnippetOf, floatingViewOf, resumeOf, type EmbedLook, type EmbedSnippetOptions, } from './embed/embedConfig';
75
+ export { EMBED_ATTRIBUTES, EMBED_HOST_VARIABLES, EMBED_TAG, embedLookOf, embedSnippetOf, floatingViewOf, languageOf, resumeOf, type EmbedLook, type EmbedSnippetOptions, } from './embed/embedConfig';
75
76
  export { SubagentActivityPlugin, SubagentPulse, SUBAGENT_ACTIVITY_PLUGIN_NAME, } from './plugins/subagent-activity';
76
77
  export { loopPlugins, type LoopPresetOptions } from './presets';
77
78
  export { DocumentExtension, NotebookExtension } from './extensions';
78
- export { BEHAVIOURS as APP_BEHAVIOUR_ORDER, DEFAULT_BEHAVIOURS as APP_DEFAULT_BEHAVIOURS, behaviourFor, classesOf, isReadOnly, toolBehaviours, toolEscalations, type BehaviourOptions, type ToolArguments, APP_SCHEMA, APPSPEC_KEY_ORDER, DEFAULT_LAYOUTS as APP_DEFAULT_LAYOUTS, dumpAppspec, emptyAppspec, parseAppspec, AppRenderer, checkApp, checkAppspec, isOrganizationFrame, ORGANIZATION_FRAME_PREFIX, type AppCheck, type CheckContext, defineAppPlugin, type AppRendererProps, type AppInstance, appLayoutOptions, readAppspecYaml, writeAppspecYaml, type ParsedAppspec, } from './apps';
79
+ export { BEHAVIOURS as APP_BEHAVIOUR_ORDER, DEFAULT_BEHAVIOURS as APP_DEFAULT_BEHAVIOURS, behaviourFor, classesOf, isReadOnly, toolBehaviours, toolEscalations, type BehaviourOptions, type ToolArguments, APP_SCHEMA, APPSPEC_KEY_ORDER, DEFAULT_LAYOUTS as APP_DEFAULT_LAYOUTS, dumpAppspec, emptyAppspec, parseAppspec, AppRenderer, checkApp, checkAppspec, isOrganizationFrame, ORGANIZATION_FRAME_PREFIX, type AppCheck, type CheckContext, defineAppPlugin, type AppRendererProps, type AppInstance, appLayoutOptions, readAppspecYaml, writeAppspecYaml, type ParsedAppspec, appComponents, customComponentEntry, customComponentOf, customComponentProblems, customComponentsProblems, pageOutputShows, } from './apps';
package/lib/loop/index.js CHANGED
@@ -64,6 +64,7 @@ export { AgentA2uiPlugin, AGENT_A2UI_PLUGIN_NAME } from './plugins/agent-a2ui';
64
64
  export { AgentA2uiJupyterOutputPlugin, AGENT_A2UI_JUPYTER_OUTPUT_PLUGIN_NAME, } from './plugins/agent-a2ui-jupyter-output';
65
65
  export { defineA2uiScenePlugin, } from './plugins/a2ui-scene';
66
66
  export { APP_KIND_PATHS, APP_PAGE_PLUGIN_NAME, APP_PAGE_SURFACE, AppPage, appKindPaths, appPageAction, appPageData, defineAppPagePlugin, hasAppPage, surfaceUnshown, } from './plugins/app-page';
67
+ export { appComponentsPluginName, defineAppComponentsPlugin, hasCustomComponents, } from './plugins/app-components';
67
68
  export { APP_RULES_PLUGIN_NAME, APP_RULES_WORDS, AppRulesCard, defineAppRulesPlugin, } from './plugins/app-rules';
68
69
  export { APP_ACTIVITY_PLUGIN_NAME, APP_ACTIVITY_WORDS, AppActivity, defineAppActivityPlugin, } from './plugins/app-activity';
69
70
  export { APP_COMPUTER_PLUGIN_NAME, AppComputer, defineAppComputerPlugin, } from './plugins/app-computer';
@@ -90,9 +91,9 @@ export { LoopEmbed } from './embed/LoopEmbed';
90
91
  export { AppEmbed, AppFloating, embedAssistantCharacter, useEmbedSession, useHostBridge, } from './embed/AppEmbed';
91
92
  export { EMBED_SESSION_KEY_PREFIX, embedSessionKey, visitOfToken, } from './embed/embedSession';
92
93
  // The host page and an embedded application, talking (LOOP D-10).
93
- export { HOST_CONTEXT_TOOL, hostFrontendTools, hostNotAllowed, hostRefused, hostTool, hostToolsOf, } from './embed/hostBridge';
94
+ export { HOST_CONTEXT_TOOL, hostFrontendTools, hostNotAllowed, hostRefused, hostTool, hostToolsOf, hostUserRunProps, signedUser, } from './embed/hostBridge';
94
95
  export { defineDatalayerAppElement, } from './embed/element';
95
- export { EMBED_ATTRIBUTES, EMBED_HOST_VARIABLES, EMBED_TAG, embedLookOf, embedSnippetOf, floatingViewOf, resumeOf, } from './embed/embedConfig';
96
+ export { EMBED_ATTRIBUTES, EMBED_HOST_VARIABLES, EMBED_TAG, embedLookOf, embedSnippetOf, floatingViewOf, languageOf, resumeOf, } from './embed/embedConfig';
96
97
  export { SubagentActivityPlugin, SubagentPulse, SUBAGENT_ACTIVITY_PLUGIN_NAME, } from './plugins/subagent-activity';
97
98
  // The set a workspace normally has, so a host starts from a working Loop
98
99
  // rather than assembling one — and never from the example.
@@ -102,4 +103,4 @@ export { loopPlugins } from './presets';
102
103
  export { DocumentExtension, NotebookExtension } from './extensions';
103
104
  // Applications: what one does when its agent calls a tool — the rules a
104
105
  // person wrote, decided on what the tool does.
105
- export { BEHAVIOURS as APP_BEHAVIOUR_ORDER, DEFAULT_BEHAVIOURS as APP_DEFAULT_BEHAVIOURS, behaviourFor, classesOf, isReadOnly, toolBehaviours, toolEscalations, APP_SCHEMA, APPSPEC_KEY_ORDER, DEFAULT_LAYOUTS as APP_DEFAULT_LAYOUTS, dumpAppspec, emptyAppspec, parseAppspec, AppRenderer, checkApp, checkAppspec, isOrganizationFrame, ORGANIZATION_FRAME_PREFIX, defineAppPlugin, appLayoutOptions, readAppspecYaml, writeAppspecYaml, } from './apps';
106
+ export { BEHAVIOURS as APP_BEHAVIOUR_ORDER, DEFAULT_BEHAVIOURS as APP_DEFAULT_BEHAVIOURS, behaviourFor, classesOf, isReadOnly, toolBehaviours, toolEscalations, APP_SCHEMA, APPSPEC_KEY_ORDER, DEFAULT_LAYOUTS as APP_DEFAULT_LAYOUTS, dumpAppspec, emptyAppspec, parseAppspec, AppRenderer, checkApp, checkAppspec, isOrganizationFrame, ORGANIZATION_FRAME_PREFIX, defineAppPlugin, appLayoutOptions, readAppspecYaml, writeAppspecYaml, appComponents, customComponentEntry, customComponentOf, customComponentProblems, customComponentsProblems, pageOutputShows, } from './apps';
@@ -28,7 +28,7 @@ type Surface = InlineSurfaceModel;
28
28
  export declare const SURFACE_CATALOG_ID: string;
29
29
  /** Values to publish into a surface, by path. */
30
30
  export type InlineSurfaceData = Record<string, unknown>;
31
- export declare function InlineSurface({ messages, onAction, validationError, data, catalog, }: {
31
+ export declare function InlineSurface({ messages, onAction, validationError, data, catalog, onSurface, }: {
32
32
  messages: A2uiMessage[];
33
33
  /** What it draws with, read once: Datalayer's catalog unless given. */
34
34
  catalog?: Catalog<ReactComponentImplementation>;
@@ -41,5 +41,11 @@ export declare function InlineSurface({ messages, onAction, validationError, dat
41
41
  * changed is written: what a person typed elsewhere in the model stays.
42
42
  */
43
43
  data?: InlineSurfaceData;
44
+ /**
45
+ * Told of each surface once it is created, for a host that reads what a
46
+ * person writes as they write it — a widget's page run again as an input
47
+ * changes (LOOP P-05) — rather than when a button is pressed.
48
+ */
49
+ onSurface?: (surface: Surface) => void;
44
50
  }): JSX.Element | null;
45
51
  export default InlineSurface;
@@ -11,7 +11,7 @@ const INHERIT_THEME = {
11
11
  ['--a2ui-color-primary']: 'var(--fgColor-accent)',
12
12
  ['--a2ui-color-outline']: 'var(--borderColor-default)',
13
13
  };
14
- export function InlineSurface({ messages, onAction, validationError, data, catalog = datalayerCatalog, }) {
14
+ export function InlineSurface({ messages, onAction, validationError, data, catalog = datalayerCatalog, onSurface, }) {
15
15
  // Reached through a ref: the processor is built once, and the handler it
16
16
  // was built with must not go stale when the host's does not.
17
17
  const onActionRef = useRef(onAction);
@@ -23,8 +23,13 @@ export function InlineSurface({ messages, onAction, validationError, data, catal
23
23
  const processorRef = useRef(undefined);
24
24
  processorRef.current = processor;
25
25
  const [surfaces, setSurfaces] = useState([]);
26
+ const onSurfaceRef = useRef(onSurface);
27
+ onSurfaceRef.current = onSurface;
26
28
  useEffect(() => {
27
- const created = processor.onSurfaceCreated(surface => setSurfaces(previous => [...previous, surface]));
29
+ const created = processor.onSurfaceCreated(surface => {
30
+ setSurfaces(previous => [...previous, surface]);
31
+ onSurfaceRef.current?.(surface);
32
+ });
28
33
  const deleted = processor.onSurfaceDeleted(id => setSurfaces(previous => previous.filter(surface => surface.id !== id)));
29
34
  return () => {
30
35
  created.unsubscribe();
@@ -4,6 +4,7 @@ import { Spinner, Text } from '@primer/react';
4
4
  import { Box } from '@datalayer/primer-addons';
5
5
  import { encodeFormSubmission } from '../../../chat/messages/formSubmission';
6
6
  import { InlineSurface, SURFACE_CATALOG_ID } from './InlineSurface';
7
+ import { useOwnCatalog } from './ownComponents';
7
8
  import { answerAction, readA2uiToolResult, validateA2uiSubmission, } from './toolResult';
8
9
  export function SurfaceToolResult({ context, send, onRendered, onSubmitted, }) {
9
10
  const parsed = useMemo(() => context.status === 'complete'
@@ -11,6 +12,8 @@ export function SurfaceToolResult({ context, send, onRendered, onSubmitted, }) {
11
12
  : null, [context.status, context.result]);
12
13
  const title = parsed?.title ?? 'the form';
13
14
  const [validationError, setValidationError] = useState(null);
15
+ // With the components the application's developer wrote (LOOP P-17).
16
+ const catalog = useOwnCatalog();
14
17
  // Told once per result, when it is there to be told about.
15
18
  useEffect(() => {
16
19
  if (parsed?.messages && parsed.messages.length > 0) {
@@ -65,6 +68,6 @@ export function SurfaceToolResult({ context, send, onRendered, onSubmitted, }) {
65
68
  gap: 2,
66
69
  }, children: [_jsx(Spinner, { size: "small" }), _jsx(Text, { sx: { fontSize: 1, color: 'fg.muted' }, children: "Rendering A2UI surface..." })] }));
67
70
  }
68
- return (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'bold' }, children: title }), _jsx(InlineSurface, { messages: parsed.messages, onAction: handleAction, validationError: validationError })] }));
71
+ return (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsx(Text, { sx: { fontWeight: 'bold' }, children: title }), _jsx(InlineSurface, { messages: parsed.messages, onAction: handleAction, validationError: validationError, catalog: catalog })] }));
69
72
  }
70
73
  export default SurfaceToolResult;
@@ -0,0 +1,6 @@
1
+ import type { ReactComponentImplementation } from '@a2ui/react/v0_9';
2
+ import type { Catalog } from '@a2ui/web_core/v0_9';
3
+ /** The renderers the workspace's application contributes, in contribution order. */
4
+ export declare function useOwnComponents(): ReactComponentImplementation[];
5
+ /** Datalayer's catalog with the workspace's application's own components. */
6
+ export declare function useOwnCatalog(): Catalog<ReactComponentImplementation>;
@@ -0,0 +1,30 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The components the application of this workspace adds to the catalog it
7
+ * is drawn with (LOOP P-17, `loop.a2ui.component`): what its answers, its
8
+ * elements and its page draw beside Datalayer's catalog. None outside an
9
+ * application's workspace, or in one whose developer wrote none.
10
+ *
11
+ * @module loop/plugins/a2ui-surface/ownComponents
12
+ */
13
+ import { useMemo } from 'react';
14
+ import { useOptionalReactorPlatform } from '@datalayer/reactor/react';
15
+ import { catalogWithOwn } from '../../../components/a2ui';
16
+ import { LoopA2uiComponent } from '../../core/a2uiComponents';
17
+ /** The renderers the workspace's application contributes, in contribution order. */
18
+ export function useOwnComponents() {
19
+ const reactor = useOptionalReactorPlatform();
20
+ return useMemo(() => reactor
21
+ ? reactor
22
+ .getContributions(LoopA2uiComponent)
23
+ .map(entry => entry.value.implementation)
24
+ : [], [reactor]);
25
+ }
26
+ /** Datalayer's catalog with the workspace's application's own components. */
27
+ export function useOwnCatalog() {
28
+ const own = useOwnComponents();
29
+ return useMemo(() => catalogWithOwn(own), [own]);
30
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `app-components` — the components an application's developer wrote (LOOP
3
+ * P-17), registered as that application's A2UI contributions: each a block
4
+ * of its Canvas's palette (`loop.canvas.block`, as a UI plugin's components
5
+ * are) and a renderer of the catalog its page, its elements and its answers
6
+ * are drawn with (`loop.a2ui.component`). One plugin per application, in its
7
+ * workspace's preset only: the catalog grows for it, it does not open.
8
+ *
9
+ * Its components are reviewed before they get here — `checkAppspec` refuses
10
+ * an invalid declaration — and drawn in a sandboxed frame of no origin
11
+ * (`components/a2ui/custom`).
12
+ *
13
+ * @module loop/plugins/app-components
14
+ */
15
+ import { type ReactorPlugin } from '@datalayer/reactor';
16
+ import type { AppSpec } from '../../../types/agentspecs';
17
+ /** The name of the plugin that contributes an application's own components. */
18
+ export declare const appComponentsPluginName: (appId: string) => string;
19
+ /** Whether an application's developer wrote components of its own. */
20
+ export declare const hasCustomComponents: (app: Pick<AppSpec, "interface">) => boolean;
21
+ /**
22
+ * The plugin contributing the components an application's developer wrote:
23
+ * a block and a renderer for each. `fetcher` is how their modules are
24
+ * fetched; the page's `fetch` unless given (tests).
25
+ */
26
+ export declare function defineAppComponentsPlugin(app: Pick<AppSpec, 'id' | 'name' | 'version' | 'interface'>, fetcher?: typeof fetch): ReactorPlugin<Record<string, never>, unknown, unknown>;
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * `app-components` — the components an application's developer wrote (LOOP
7
+ * P-17), registered as that application's A2UI contributions: each a block
8
+ * of its Canvas's palette (`loop.canvas.block`, as a UI plugin's components
9
+ * are) and a renderer of the catalog its page, its elements and its answers
10
+ * are drawn with (`loop.a2ui.component`). One plugin per application, in its
11
+ * workspace's preset only: the catalog grows for it, it does not open.
12
+ *
13
+ * Its components are reviewed before they get here — `checkAppspec` refuses
14
+ * an invalid declaration — and drawn in a sandboxed frame of no origin
15
+ * (`components/a2ui/custom`).
16
+ *
17
+ * @module loop/plugins/app-components
18
+ */
19
+ import { contribution, definePlugin, } from '@datalayer/reactor';
20
+ import { customImplementation } from '../../../components/a2ui/custom';
21
+ import { customComponentEntry } from '../../apps/customComponents';
22
+ import { LoopA2uiComponent } from '../../core/a2uiComponents';
23
+ import { LoopCanvasBlock } from '../../core/canvasBlocks';
24
+ /** The name of the plugin that contributes an application's own components. */
25
+ export const appComponentsPluginName = (appId) => `@datalayer/loop-plugin-app-components-${appId}`;
26
+ /** Whether an application's developer wrote components of its own. */
27
+ export const hasCustomComponents = (app) => (app.interface.customComponents ?? []).length > 0;
28
+ /**
29
+ * The plugin contributing the components an application's developer wrote:
30
+ * a block and a renderer for each. `fetcher` is how their modules are
31
+ * fetched; the page's `fetch` unless given (tests).
32
+ */
33
+ export function defineAppComponentsPlugin(app, fetcher) {
34
+ const own = app.interface.customComponents ?? [];
35
+ if (own.length === 0) {
36
+ throw new Error(`“${app.name}” has no component of its own to contribute.`);
37
+ }
38
+ return definePlugin({
39
+ name: appComponentsPluginName(app.id),
40
+ displayName: `${app.name}'s components`,
41
+ description: `The components ${app.name}'s developer wrote: ${own.map(component => component.name).join(', ')}.`,
42
+ octicon: 'apps',
43
+ contributes: own.flatMap(component => [
44
+ contribution(LoopCanvasBlock, {
45
+ id: component.name,
46
+ uiPlugin: `app:${app.id}`,
47
+ component: customComponentEntry(component, app.version),
48
+ }, { id: component.name }),
49
+ contribution(LoopA2uiComponent, {
50
+ id: component.name,
51
+ app: app.id,
52
+ implementation: customImplementation(component, app.version, fetcher),
53
+ }, { id: component.name }),
54
+ ]),
55
+ });
56
+ }
@@ -6,6 +6,7 @@ import { Box } from '@datalayer/primer-addons';
6
6
  import { closeLoopElement, onLoopElements, openLoopElements, } from '../../../chat/base/loopElement';
7
7
  import { InlineSurface, SURFACE_CATALOG_ID, } from '../a2ui-surface/InlineSurface';
8
8
  import { answerAction, readA2uiToolResult } from '../a2ui-surface/toolResult';
9
+ import { useOwnCatalog } from '../a2ui-surface/ownComponents';
9
10
  /** How wide the side panel is, at most. */
10
11
  export const APP_PANEL_WIDTH = 380;
11
12
  /** What is open now: the panels in the order they opened, and the last page. */
@@ -18,6 +19,8 @@ export function elementsByPlace(open) {
18
19
  }
19
20
  function ElementBody({ element, send, }) {
20
21
  const parsed = useMemo(() => readA2uiToolResult(element.shows, SURFACE_CATALOG_ID), [element.shows]);
22
+ // With the components its developer wrote (P-17).
23
+ const catalog = useOwnCatalog();
21
24
  const onAction = (action) => {
22
25
  // A button of what its code shows: its action goes to the application,
23
26
  // as a button of an answer does (LOOP P-04).
@@ -30,7 +33,7 @@ function ElementBody({ element, send, }) {
30
33
  }
31
34
  return (_jsx(InlineSurface
32
35
  // Drawn again whole when the code changes it.
33
- , { messages: parsed.messages, onAction: onAction }, JSON.stringify(element.shows)));
36
+ , { messages: parsed.messages, onAction: onAction, catalog: catalog }, JSON.stringify(element.shows)));
34
37
  }
35
38
  function ElementHeader({ element }) {
36
39
  return (_jsxs(Box, { sx: {
@@ -9,12 +9,21 @@
9
9
  * palette (`loop.canvas.block`, R-01b) and nothing else, so that what the
10
10
  * Canvas can place and what the page can draw are one list: a page that
11
11
  * uses a block no enabled plugin contributes says so, in place of the page.
12
+ * The components its developer wrote (LOOP P-17) are among them, contributed
13
+ * by its own plugin (`app-components`) with their renderers.
14
+ *
15
+ * A widget's page written in its code (LOOP P-05) runs in a session of its
16
+ * own, beside the conversation: as an input changes, the page sends them all
17
+ * to the session's `page` action and shows what its code answered at
18
+ * `/outputs/<name>`, in place — no message is sent (`./pageRun`).
12
19
  *
13
20
  * @module loop/plugins/app-page/AppPage
14
21
  */
15
22
  import type { JSX } from 'react';
16
23
  import type { AppSpec } from '../../../types/agentspecs';
17
24
  import { type LoopWorkspaceContext } from '../../core';
25
+ /** How long the page waits after an input changed before it runs (P-05). */
26
+ export declare const PAGE_RUN_DELAY_MS = 300;
18
27
  /** The components a page's messages place, each once. */
19
28
  export declare function componentsOnPage(messages: readonly unknown[]): string[];
20
29
  /**