@datalayer/agent-runtimes 1.3.28 → 1.3.30

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 (99) hide show
  1. package/lib/AgentNode.js +14 -33
  2. package/lib/chat/ChatFloating.d.ts +12 -2
  3. package/lib/chat/ChatFloating.js +196 -25
  4. package/lib/chat/assistant/AssistantStage.d.ts +70 -0
  5. package/lib/chat/assistant/AssistantStage.js +192 -0
  6. package/lib/chat/assistant/SpeechBalloon.d.ts +26 -0
  7. package/lib/chat/assistant/SpeechBalloon.js +46 -0
  8. package/lib/chat/assistant/SpriteCharacter.d.ts +26 -0
  9. package/lib/chat/assistant/SpriteCharacter.js +73 -0
  10. package/lib/chat/assistant/characters.d.ts +30 -0
  11. package/lib/chat/assistant/characters.js +41 -0
  12. package/lib/chat/assistant/formats/acs.d.ts +88 -0
  13. package/lib/chat/assistant/formats/acs.js +493 -0
  14. package/lib/chat/assistant/formats/agentCompression.d.ts +5 -0
  15. package/lib/chat/assistant/formats/agentCompression.js +92 -0
  16. package/lib/chat/assistant/formats/blobs.d.ts +6 -0
  17. package/lib/chat/assistant/formats/blobs.js +52 -0
  18. package/lib/chat/assistant/formats/clippy.d.ts +21 -0
  19. package/lib/chat/assistant/formats/clippy.js +167 -0
  20. package/lib/chat/assistant/formats/index.d.ts +13 -0
  21. package/lib/chat/assistant/formats/index.js +15 -0
  22. package/lib/chat/assistant/formats/objectLiteral.d.ts +23 -0
  23. package/lib/chat/assistant/formats/objectLiteral.js +233 -0
  24. package/lib/chat/assistant/formats/stateAnimations.d.ts +16 -0
  25. package/lib/chat/assistant/formats/stateAnimations.js +115 -0
  26. package/lib/chat/assistant/formats/types.d.ts +56 -0
  27. package/lib/chat/assistant/formats/types.js +11 -0
  28. package/lib/chat/assistant/state.d.ts +62 -0
  29. package/lib/chat/assistant/state.js +116 -0
  30. package/lib/chat/base/ChatBase.js +41 -32
  31. package/lib/chat/base/modelChoice.d.ts +13 -0
  32. package/lib/chat/base/modelChoice.js +13 -0
  33. package/lib/chat/header/ChatViewModeToggle.js +6 -3
  34. package/lib/{loop/plugins/chat → chat/presence}/Presence.js +1 -1
  35. package/lib/{loop/plugins/chat → chat/presence}/presenceStatus.d.ts +7 -1
  36. package/lib/{loop/plugins/chat → chat/presence}/presenceStatus.js +18 -0
  37. package/lib/chat/viewModes.d.ts +6 -0
  38. package/lib/chat/viewModes.js +10 -1
  39. package/lib/examples/ChatAssistantExample.d.ts +8 -0
  40. package/lib/examples/ChatAssistantExample.js +59 -0
  41. package/lib/examples/example-selector.js +1 -0
  42. package/lib/examples/main.js +3 -0
  43. package/lib/loop/apps/AppRenderer.d.ts +34 -2
  44. package/lib/loop/apps/AppRenderer.js +96 -29
  45. package/lib/loop/apps/agent.d.ts +15 -0
  46. package/lib/loop/apps/agent.js +42 -0
  47. package/lib/loop/apps/appspec.d.ts +5 -1
  48. package/lib/loop/apps/appspec.js +21 -1
  49. package/lib/loop/apps/checks.js +3 -1
  50. package/lib/loop/apps/index.d.ts +1 -0
  51. package/lib/loop/apps/index.js +1 -0
  52. package/lib/loop/core/index.d.ts +23 -0
  53. package/lib/loop/core/index.js +1 -0
  54. package/lib/loop/embed/AppEmbed.d.ts +42 -0
  55. package/lib/loop/embed/AppEmbed.js +205 -0
  56. package/lib/loop/embed/datalayer-app.d.ts +1 -0
  57. package/lib/loop/embed/datalayer-app.js +16 -0
  58. package/lib/loop/embed/element.d.ts +57 -0
  59. package/lib/loop/embed/element.js +405 -0
  60. package/lib/loop/embed/embedConfig.d.ts +120 -0
  61. package/lib/loop/embed/embedConfig.js +148 -0
  62. package/lib/loop/embed/embedTheme.d.ts +24 -0
  63. package/lib/loop/embed/embedTheme.js +83 -0
  64. package/lib/loop/index.d.ts +6 -2
  65. package/lib/loop/index.js +8 -2
  66. package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +21 -3
  67. package/lib/loop/plugins/a2ui-surface/InlineSurface.js +34 -6
  68. package/lib/loop/plugins/agent-capacity/index.d.ts +4 -0
  69. package/lib/loop/plugins/agent-capacity/index.js +3 -1
  70. package/lib/loop/plugins/agents/switchable.d.ts +7 -0
  71. package/lib/loop/plugins/agents/switchable.js +91 -88
  72. package/lib/loop/plugins/app-page/AppPage.d.ts +16 -0
  73. package/lib/loop/plugins/app-page/AppPage.js +53 -0
  74. package/lib/loop/plugins/app-page/appPage.d.ts +180 -0
  75. package/lib/loop/plugins/app-page/appPage.js +443 -0
  76. package/lib/loop/plugins/app-page/index.d.ts +8 -0
  77. package/lib/loop/plugins/app-page/index.js +34 -0
  78. package/lib/loop/plugins/assistant-characters/index.d.ts +30 -0
  79. package/lib/loop/plugins/assistant-characters/index.js +41 -0
  80. package/lib/loop/plugins/chat/ChatView.js +50 -39
  81. package/lib/loop/plugins/chat/index.d.ts +1 -1
  82. package/lib/loop/plugins/page-layout/SplitLayout.d.ts +31 -0
  83. package/lib/loop/plugins/page-layout/SplitLayout.js +129 -0
  84. package/lib/loop/plugins/page-layout/index.d.ts +13 -0
  85. package/lib/loop/plugins/page-layout/index.js +15 -2
  86. package/lib/loop/presets.d.ts +12 -0
  87. package/lib/loop/presets.js +5 -3
  88. package/lib/specs/actions.js +14 -0
  89. package/lib/specs/agents/agents.js +4 -0
  90. package/lib/specs/apps.d.ts +13 -1
  91. package/lib/specs/apps.js +2715 -373
  92. package/lib/specs/appspecSchema.js +21 -3
  93. package/lib/types/agentspecs.d.ts +21 -1
  94. package/lib/types/chat.d.ts +1 -1
  95. package/lib/types/config.d.ts +12 -0
  96. package/package.json +2 -1
  97. package/scripts/codegen/generate_agents.py +50 -2
  98. package/scripts/codegen/generate_apps.py +36 -6
  99. /package/lib/{loop/plugins/chat → chat/presence}/Presence.d.ts +0 -0
@@ -0,0 +1,57 @@
1
+ import type { AppSpec } from '../../types/agentspecs';
2
+ import { type ParsedAppspec } from '../apps/appspec';
3
+ /** What the framed run page and the element say to each other. */
4
+ export declare const EMBED_MESSAGES: {
5
+ readonly size: "datalayer-app:size";
6
+ readonly decision: "datalayer-app:decision";
7
+ /** To the frame: an embed token, the first or a new one. */
8
+ readonly token: "datalayer-app:token";
9
+ /** From the frame: the token lapsed; a new one is needed. */
10
+ readonly tokenExpired: "datalayer-app:token-expired";
11
+ };
12
+ /** Where an application comes from, once read. */
13
+ export type EmbedSource = {
14
+ kind: 'spec';
15
+ app: AppSpec;
16
+ } | {
17
+ kind: 'frame';
18
+ src: string;
19
+ } | {
20
+ kind: 'said';
21
+ text: string;
22
+ expired?: boolean;
23
+ };
24
+ /** An Appspec from its text, YAML or JSON; refused in a sentence when it is neither. */
25
+ export declare function appspecOfText(text: string): AppSpec;
26
+ /**
27
+ * An application the embed can run, or a sentence saying why not: one with
28
+ * an id, and an agent to answer — what the spec tolerates in a draft, an
29
+ * embed cannot run.
30
+ */
31
+ export declare function runnable({ app, problems }: ParsedAppspec): AppSpec;
32
+ /** The run page an application on Datalayer is framed from. */
33
+ export declare function frameSrcOf(origin: string, app: string, token?: string): string;
34
+ /**
35
+ * An application on Datalayer, read with its embed token: what the space
36
+ * keeps of it (`model_s`, the stored definition), whose `spec` is the
37
+ * Appspec. A decision is framed; so is an application that has no spec.
38
+ */
39
+ export declare function readEmbeddedApp({ api, origin, app, token, fetcher, }: {
40
+ api: string;
41
+ origin: string;
42
+ app: string;
43
+ token: string;
44
+ fetcher?: typeof fetch;
45
+ }): Promise<EmbedSource>;
46
+ export type DatalayerAppElementOptions = {
47
+ /** The element's name; `datalayer-app` by default. */
48
+ tag?: string;
49
+ /** Where the script was loaded from: the default origin, and where its stylesheet is. */
50
+ scriptUrl?: string;
51
+ };
52
+ /**
53
+ * Define the element, once. Returns its class, or `undefined` where there is
54
+ * no `customElements` (a server render) — a second call returns the class
55
+ * already defined.
56
+ */
57
+ export declare function defineDatalayerAppElement(options?: DatalayerAppElementOptions): CustomElementConstructor | undefined;
@@ -0,0 +1,405 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ /**
7
+ * `<datalayer-app>`: an application in another product's page, as one
8
+ * script tag and one element (LOOP D-08), in the mode its Appspec says
9
+ * (D-07), its theme isolated from the host's and overridable in the three
10
+ * ways that matter (T-13, D-11).
11
+ *
12
+ * <script src="https://datalayer.app/embed/datalayer-app.js" async></script>
13
+ * <datalayer-app app="01M…" origin="https://datalayer.app"></datalayer-app>
14
+ *
15
+ * What it shows comes from one of three places, first found first:
16
+ *
17
+ * 1. an Appspec written in the element, as YAML or JSON
18
+ * (`<script type="application/yaml">…</script>`);
19
+ * 2. an Appspec at an address (`spec="https://…/support-desk.yaml"`);
20
+ * 3. an application on Datalayer (`app`), read with its embed token
21
+ * (`token`). A decision, or an application given without a token, is
22
+ * framed as before: Datalayer's run page in an iframe, sized to it, its
23
+ * `decision` and `token-expired` events raised on the element.
24
+ *
25
+ * Every other application is drawn here, in the element's shadow root, by
26
+ * `AppEmbed` — inline, or floating over the page as a bubble, a panel or the
27
+ * assistant. The bundle's stylesheet is linked inside that root, the
28
+ * styled-components rules are written into it, and Primer's overlays are
29
+ * drawn in it, so nothing of the application's styles reaches the page and
30
+ * nothing of the page's reaches the application.
31
+ *
32
+ * @module loop/embed/element
33
+ */
34
+ import { createRoot } from 'react-dom/client';
35
+ import { StyleSheetManager } from 'styled-components';
36
+ import { loopThemeStyles } from '@datalayer/primer-addons';
37
+ import { coreStore } from '@datalayer/core/lib/state/substates/CoreState';
38
+ import { parseAppspec } from '../apps/appspec';
39
+ import { readAppspecYaml } from '../apps/yaml';
40
+ import { AppEmbed } from './AppEmbed';
41
+ import { EMBED_HOST_VARIABLES, EMBED_OBSERVED_ATTRIBUTES, EMBED_TAG, EmbedAttributeError, embedLookOf, inlineHeightOf, } from './embedConfig';
42
+ import { embedShadowCss } from './embedTheme';
43
+ /** What the framed run page and the element say to each other. */
44
+ export const EMBED_MESSAGES = {
45
+ size: 'datalayer-app:size',
46
+ decision: 'datalayer-app:decision',
47
+ /** To the frame: an embed token, the first or a new one. */
48
+ token: 'datalayer-app:token',
49
+ /** From the frame: the token lapsed; a new one is needed. */
50
+ tokenExpired: 'datalayer-app:token-expired',
51
+ };
52
+ /** An Appspec from its text, YAML or JSON; refused in a sentence when it is neither. */
53
+ export function appspecOfText(text) {
54
+ // Written in a page, a document is indented with its markup: the
55
+ // indentation every line shares is the page's, not the document's.
56
+ const lines = text.replace(/\t/g, ' ').split('\n');
57
+ const indent = Math.min(...lines
58
+ .filter(line => line.trim())
59
+ .map(line => line.length - line.trimStart().length));
60
+ const trimmed = lines
61
+ .map(line => line.slice(Number.isFinite(indent) ? indent : 0))
62
+ .join('\n')
63
+ .trim();
64
+ if (!trimmed) {
65
+ throw new EmbedAttributeError('datalayer-app: the Appspec is empty.');
66
+ }
67
+ let parsed;
68
+ if (trimmed.startsWith('{')) {
69
+ let document;
70
+ try {
71
+ document = JSON.parse(trimmed);
72
+ }
73
+ catch (error) {
74
+ throw new EmbedAttributeError(`datalayer-app: the Appspec is not JSON (${error.message}).`);
75
+ }
76
+ parsed = parseAppspec(document);
77
+ }
78
+ else {
79
+ parsed = readAppspecYaml(trimmed);
80
+ }
81
+ return runnable(parsed);
82
+ }
83
+ /**
84
+ * An application the embed can run, or a sentence saying why not: one with
85
+ * an id, and an agent to answer — what the spec tolerates in a draft, an
86
+ * embed cannot run.
87
+ */
88
+ export function runnable({ app, problems }) {
89
+ if (!app.id) {
90
+ throw new EmbedAttributeError(`datalayer-app: the Appspec names no application${problems.length ? ` (${problems[0]})` : ''}.`);
91
+ }
92
+ if (app.kind !== 'decision' && !app.agent) {
93
+ throw new EmbedAttributeError(`datalayer-app: “${app.name || app.id}” names no agent to answer; an embedded application is run by an agent (a team is not supported yet).`);
94
+ }
95
+ return app;
96
+ }
97
+ /** The run page an application on Datalayer is framed from. */
98
+ export function frameSrcOf(origin, app, token = '') {
99
+ // The token in the fragment, which no server is sent: it reaches the page
100
+ // and no access log.
101
+ return `${origin.replace(/\/+$/, '')}/public/apps/${encodeURIComponent(app)}/run?embed=1${token ? `#token=${encodeURIComponent(token)}` : ''}`;
102
+ }
103
+ /**
104
+ * An application on Datalayer, read with its embed token: what the space
105
+ * keeps of it (`model_s`, the stored definition), whose `spec` is the
106
+ * Appspec. A decision is framed; so is an application that has no spec.
107
+ */
108
+ export async function readEmbeddedApp({ api, origin, app, token, fetcher = fetch, }) {
109
+ const response = await fetcher(`${api.replace(/\/+$/, '')}/api/spacer/v1/apps/${encodeURIComponent(app)}/embedded`, { headers: { Authorization: `Bearer ${token}` } });
110
+ if (response.status === 401) {
111
+ return {
112
+ kind: 'said',
113
+ text: 'datalayer-app: the embed token expired or is not for this application.',
114
+ expired: true,
115
+ };
116
+ }
117
+ if (!response.ok) {
118
+ return {
119
+ kind: 'said',
120
+ text: `datalayer-app: the application could not be read (${response.status}).`,
121
+ };
122
+ }
123
+ const body = (await response.json());
124
+ let definition;
125
+ try {
126
+ definition = JSON.parse(String(body.app?.model_s ?? '{}'));
127
+ }
128
+ catch {
129
+ definition = {};
130
+ }
131
+ if (!definition.spec) {
132
+ return { kind: 'frame', src: frameSrcOf(origin, app, token) };
133
+ }
134
+ const spec = runnable(parseAppspec(definition.spec));
135
+ return spec.kind === 'decision'
136
+ ? { kind: 'frame', src: frameSrcOf(origin, app, token) }
137
+ : { kind: 'spec', app: spec };
138
+ }
139
+ /** The script that is running, when it is a script tag. */
140
+ function currentScriptUrl() {
141
+ try {
142
+ const script = document.currentScript;
143
+ return script?.src ? new URL(script.src, location.href).href : '';
144
+ }
145
+ catch {
146
+ return '';
147
+ }
148
+ }
149
+ /**
150
+ * Define the element, once. Returns its class, or `undefined` where there is
151
+ * no `customElements` (a server render) — a second call returns the class
152
+ * already defined.
153
+ */
154
+ export function defineDatalayerAppElement(options = {}) {
155
+ if (typeof window === 'undefined' || !('customElements' in window)) {
156
+ return undefined;
157
+ }
158
+ const tag = options.tag ?? EMBED_TAG;
159
+ const existing = customElements.get(tag);
160
+ if (existing) {
161
+ return existing;
162
+ }
163
+ const scriptUrl = options.scriptUrl ?? currentScriptUrl();
164
+ const scriptOrigin = scriptUrl ? new URL(scriptUrl).origin : '';
165
+ // The bundle's stylesheet, beside its script.
166
+ const stylesheetUrl = scriptUrl
167
+ ? scriptUrl.replace(/\.js(\?.*)?$/, '.css')
168
+ : '';
169
+ class DatalayerAppElement extends HTMLElement {
170
+ static get observedAttributes() {
171
+ return EMBED_OBSERVED_ATTRIBUTES;
172
+ }
173
+ root = null;
174
+ frame = null;
175
+ source = null;
176
+ loading = 0;
177
+ constructor() {
178
+ super();
179
+ this.attachShadow({ mode: 'open' });
180
+ this.onMessage = this.onMessage.bind(this);
181
+ }
182
+ /** An embed token, the attribute's. Set it to hand a new one over. */
183
+ get token() {
184
+ return this.getAttribute('token') ?? '';
185
+ }
186
+ set token(value) {
187
+ if (value) {
188
+ this.setAttribute('token', String(value));
189
+ }
190
+ else {
191
+ this.removeAttribute('token');
192
+ }
193
+ }
194
+ connectedCallback() {
195
+ window.addEventListener('message', this.onMessage);
196
+ void this.load();
197
+ }
198
+ disconnectedCallback() {
199
+ window.removeEventListener('message', this.onMessage);
200
+ this.unmount();
201
+ }
202
+ attributeChangedCallback(name, before, after) {
203
+ if (!this.isConnected || before === after) {
204
+ return;
205
+ }
206
+ // A new token is handed to the framed page that is already there,
207
+ // which keeps what the person entered.
208
+ if (name === 'token' && this.frame?.contentWindow && after) {
209
+ this.frame.contentWindow.postMessage({ type: EMBED_MESSAGES.token, token: after }, this.origin());
210
+ return;
211
+ }
212
+ // How it looks is drawn again; what it is, read again.
213
+ if (['mode', 'accent', 'theme', 'font', 'height'].includes(name)) {
214
+ this.draw();
215
+ return;
216
+ }
217
+ void this.load();
218
+ }
219
+ origin() {
220
+ return (this.getAttribute('origin') ||
221
+ scriptOrigin ||
222
+ location.origin).replace(/\/+$/, '');
223
+ }
224
+ inputs() {
225
+ const attributes = {};
226
+ for (const name of EMBED_OBSERVED_ATTRIBUTES) {
227
+ attributes[name] = this.getAttribute(name);
228
+ }
229
+ const computed = getComputedStyle(this);
230
+ const variables = {};
231
+ for (const [key, property] of Object.entries(EMBED_HOST_VARIABLES)) {
232
+ const value = computed.getPropertyValue(property).trim();
233
+ if (value) {
234
+ variables[key] = value.replace(/^["']|["']$/g, '');
235
+ }
236
+ }
237
+ return { attributes, variables };
238
+ }
239
+ /** Where the application comes from: the element's own Appspec, an address, or Datalayer. */
240
+ async read() {
241
+ const inline = this.querySelector('script[type="application/yaml"], script[type="application/json"], script[type="text/yaml"]');
242
+ if (inline?.textContent?.trim()) {
243
+ return { kind: 'spec', app: appspecOfText(inline.textContent) };
244
+ }
245
+ const spec = this.getAttribute('spec');
246
+ if (spec) {
247
+ const response = await fetch(new URL(spec, location.href).href);
248
+ if (!response.ok) {
249
+ return {
250
+ kind: 'said',
251
+ text: `datalayer-app: the Appspec at ${spec} could not be read (${response.status}).`,
252
+ };
253
+ }
254
+ return { kind: 'spec', app: appspecOfText(await response.text()) };
255
+ }
256
+ const app = this.getAttribute('app');
257
+ if (!app) {
258
+ return {
259
+ kind: 'said',
260
+ text: 'datalayer-app: the "app" attribute names the application to show, or "spec" the address of its Appspec.',
261
+ };
262
+ }
263
+ const token = this.token;
264
+ if (!token) {
265
+ // A public application: Datalayer's run page, framed.
266
+ return { kind: 'frame', src: frameSrcOf(this.origin(), app) };
267
+ }
268
+ return readEmbeddedApp({
269
+ api: this.getAttribute('api') ||
270
+ coreStore.getState().configuration?.spacerUrl ||
271
+ 'https://prod1.datalayer.run',
272
+ origin: this.origin(),
273
+ app,
274
+ token,
275
+ });
276
+ }
277
+ async load() {
278
+ const turn = ++this.loading;
279
+ let source;
280
+ try {
281
+ source = await this.read();
282
+ }
283
+ catch (error) {
284
+ source = {
285
+ kind: 'said',
286
+ text: error instanceof Error
287
+ ? error.message
288
+ : `datalayer-app: ${String(error)}`,
289
+ };
290
+ }
291
+ // A later read wins over an earlier one still in flight.
292
+ if (turn !== this.loading || !this.isConnected) {
293
+ return;
294
+ }
295
+ this.source = source;
296
+ this.draw();
297
+ if (source.kind === 'said' && source.expired) {
298
+ this.dispatchEvent(new CustomEvent('token-expired', {
299
+ detail: { app: this.getAttribute('app') ?? '' },
300
+ bubbles: true,
301
+ composed: true,
302
+ }));
303
+ }
304
+ }
305
+ unmount() {
306
+ this.root?.unmount();
307
+ this.root = null;
308
+ this.frame = null;
309
+ }
310
+ /** The shadow root's skeleton: the bundle's stylesheet, the element's own, and what goes in it. */
311
+ skeleton(themeCss) {
312
+ const shadow = this.shadowRoot;
313
+ this.unmount();
314
+ shadow.replaceChildren();
315
+ if (stylesheetUrl) {
316
+ const link = document.createElement('link');
317
+ link.rel = 'stylesheet';
318
+ link.href = stylesheetUrl;
319
+ shadow.appendChild(link);
320
+ }
321
+ const style = document.createElement('style');
322
+ style.textContent = embedShadowCss(themeCss);
323
+ shadow.appendChild(style);
324
+ return shadow;
325
+ }
326
+ say(text) {
327
+ this.setAttribute('data-embed-mode', 'inline');
328
+ const shadow = this.skeleton();
329
+ const note = document.createElement('p');
330
+ note.className = 'datalayer-app-said';
331
+ note.setAttribute('role', 'status');
332
+ note.textContent = text;
333
+ shadow.appendChild(note);
334
+ }
335
+ draw() {
336
+ const source = this.source;
337
+ if (!source) {
338
+ return;
339
+ }
340
+ if (source.kind === 'said') {
341
+ this.say(source.text);
342
+ return;
343
+ }
344
+ if (source.kind === 'frame') {
345
+ this.setAttribute('data-embed-mode', 'inline');
346
+ const shadow = this.skeleton();
347
+ const frame = document.createElement('iframe');
348
+ frame.className = 'datalayer-app-frame';
349
+ frame.src = source.src;
350
+ frame.title = 'Datalayer app';
351
+ frame.style.height = `${inlineHeightOf(this.getAttribute('height'))}px`;
352
+ frame.setAttribute('allow', 'clipboard-write');
353
+ shadow.appendChild(frame);
354
+ this.frame = frame;
355
+ return;
356
+ }
357
+ let look;
358
+ try {
359
+ look = embedLookOf(this.inputs(), source.app);
360
+ }
361
+ catch (error) {
362
+ this.say(error.message);
363
+ return;
364
+ }
365
+ this.setAttribute('data-embed-mode', look.mode);
366
+ const shadow = this.skeleton(loopThemeStyles.css);
367
+ const mount = document.createElement('div');
368
+ mount.className = 'datalayer-app-root';
369
+ shadow.appendChild(mount);
370
+ this.root = createRoot(mount);
371
+ const server = this.getAttribute('server') || undefined;
372
+ 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, height: inlineHeightOf(this.getAttribute('height')), ownPortal: true }) }));
373
+ }
374
+ onMessage(event) {
375
+ if (!this.frame || event.source !== this.frame.contentWindow) {
376
+ return;
377
+ }
378
+ if (event.origin !== this.origin()) {
379
+ return;
380
+ }
381
+ const data = (event.data ?? {});
382
+ if (data.type === EMBED_MESSAGES.size &&
383
+ typeof data.height === 'number' &&
384
+ data.height > 0) {
385
+ this.frame.style.height = `${Math.ceil(data.height)}px`;
386
+ }
387
+ else if (data.type === EMBED_MESSAGES.decision) {
388
+ this.dispatchEvent(new CustomEvent('decision', {
389
+ detail: data.record,
390
+ bubbles: true,
391
+ composed: true,
392
+ }));
393
+ }
394
+ else if (data.type === EMBED_MESSAGES.tokenExpired) {
395
+ this.dispatchEvent(new CustomEvent('token-expired', {
396
+ detail: { app: this.getAttribute('app') ?? '' },
397
+ bubbles: true,
398
+ composed: true,
399
+ }));
400
+ }
401
+ }
402
+ }
403
+ customElements.define(tag, DatalayerAppElement);
404
+ return DatalayerAppElement;
405
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * An application in another product's page, as its host writes it (LOOP
3
+ * D-07, D-08, D-11, T-13): one script tag and one element, `<datalayer-app>`,
4
+ * whose attributes — or, for the theme, the host's CSS variables — say what
5
+ * it shows and how it looks.
6
+ *
7
+ * Pure: what the element's attributes mean, the four modes and the chat each
8
+ * floats in, what a host may override and how it is checked, and the snippet
9
+ * the *Ship* tab copies. Nothing here touches the DOM, so all of it is read
10
+ * and tested without one.
11
+ *
12
+ * @module loop/embed/embedConfig
13
+ */
14
+ import type { AppAccent, AppEmbedMode, AppSpec } from '../../types/agentspecs';
15
+ /** The element's name: the one a host writes. */
16
+ export declare const EMBED_TAG = "datalayer-app";
17
+ /** Where the script is served on a Datalayer origin; the stylesheet beside it. */
18
+ export declare const EMBED_SCRIPT_PATH = "/embed/datalayer-app.js";
19
+ export declare const EMBED_STYLESHEET_PATH = "/embed/datalayer-app.css";
20
+ /**
21
+ * The element's attributes, each in a sentence — what the documentation and
22
+ * the element's own refusals say.
23
+ */
24
+ export declare const EMBED_ATTRIBUTES: {
25
+ readonly app: "The application’s id on Datalayer.";
26
+ readonly origin: "Where Datalayer is; the origin the script was loaded from by default.";
27
+ readonly token: "An embed token for an application that is not public, issued to your server for each visit.";
28
+ readonly spec: "The address of an Appspec (YAML or JSON) to run, in place of `app`.";
29
+ readonly mode: "inline, bubble, panel or assistant; the Appspec’s `deployment.embedded.mode` by default, then inline.";
30
+ readonly accent: "green, rose, sky, lime, sun or violet; the Appspec’s `interface.accent` by default.";
31
+ readonly theme: "light, dark or auto (the visitor’s system); auto by default.";
32
+ readonly font: "A CSS font family for the application’s text; Inter, then the system’s sans-serif, by default.";
33
+ readonly server: "An agent-runtimes server the application’s agent runs on; a Datalayer runtime by default.";
34
+ readonly api: "Datalayer’s API, where an application is read with its token; the platform’s by default.";
35
+ readonly height: "The height of an inline application, in pixels; 640 by default.";
36
+ };
37
+ export type EmbedAttribute = keyof typeof EMBED_ATTRIBUTES;
38
+ /** Every attribute the element watches. */
39
+ export declare const EMBED_OBSERVED_ATTRIBUTES: EmbedAttribute[];
40
+ /**
41
+ * What a host may set from its own stylesheet, on the element or above it
42
+ * (D-11): the accent, the face and the mode. An attribute says the same and
43
+ * wins over the variable.
44
+ */
45
+ export declare const EMBED_HOST_VARIABLES: {
46
+ readonly accent: "--datalayer-app-accent";
47
+ readonly font: "--datalayer-app-font";
48
+ readonly theme: "--datalayer-app-theme";
49
+ };
50
+ export type EmbedColorMode = 'light' | 'dark' | 'auto';
51
+ export declare const EMBED_COLOR_MODES: EmbedColorMode[];
52
+ /** The chat each floating mode is drawn as (`ChatFloating`'s view modes). */
53
+ export type EmbedFloatingView = 'floating-small' | 'panel' | 'assistant';
54
+ /**
55
+ * The four modes (D-07), and the chat they float in: a **bubble** is the
56
+ * round button that opens a popup — the copilot pattern; a **panel** is the
57
+ * conversation at the page's right edge, at full height; the **assistant** is
58
+ * the character that speaks in a balloon (§6.9). **Inline** has no chat of
59
+ * its own to float: the application, its page and its conversation, sit in
60
+ * the host's page where the element is.
61
+ */
62
+ export declare const EMBED_FLOATING_VIEWS: Record<Exclude<AppEmbedMode, 'inline'>, EmbedFloatingView>;
63
+ /** The chat a mode floats in; none for inline. */
64
+ export declare function floatingViewOf(mode: AppEmbedMode): EmbedFloatingView | undefined;
65
+ /** A value the element refuses, said in a sentence for the page to show. */
66
+ export declare class EmbedAttributeError extends Error {
67
+ constructor(message: string);
68
+ }
69
+ /**
70
+ * A font family a host names, checked: names, quotes, commas and spaces, as
71
+ * a `font-family` is written — nothing that could close the declaration or
72
+ * fetch anything (`;`, braces, `url(`).
73
+ */
74
+ export declare function checkedFont(value: string | null | undefined): string;
75
+ /** What the element was told, as text: its attributes and the host's variables. */
76
+ export type EmbedInputs = {
77
+ attributes: Partial<Record<EmbedAttribute, string | null>>;
78
+ /** The host's CSS variables, read on the element (`EMBED_HOST_VARIABLES`). */
79
+ variables?: Partial<Record<keyof typeof EMBED_HOST_VARIABLES, string>>;
80
+ };
81
+ /** How an application is shown and dressed in a host's page. */
82
+ export type EmbedLook = {
83
+ mode: AppEmbedMode;
84
+ /** The accent the host chose, or the application's own. */
85
+ accent: AppAccent;
86
+ colorMode: EmbedColorMode;
87
+ /** The host's face; empty for the theme's own. */
88
+ font: string;
89
+ };
90
+ /**
91
+ * What the element shows and how, from what the host wrote and what the
92
+ * application says: an attribute first, then the host's CSS variable, then
93
+ * the Appspec, then the default. A value that is not one is refused with a
94
+ * sentence (`EmbedAttributeError`), never quietly replaced.
95
+ */
96
+ export declare function embedLookOf(inputs: EmbedInputs, app?: Pick<AppSpec, 'interface' | 'deployment'>): EmbedLook;
97
+ /** The height of an inline application, in pixels. */
98
+ export declare function inlineHeightOf(value: string | null | undefined): number;
99
+ /** What a snippet says beside the application's id. */
100
+ export type EmbedSnippetOptions = {
101
+ /** Where Datalayer is: the script's origin, and the element's. */
102
+ origin: string;
103
+ /** The application's id. */
104
+ app: string;
105
+ /** Written when given — the *Ship* tab writes the Appspec's own. */
106
+ mode?: AppEmbedMode;
107
+ /** An embed token, for an application that is not public. */
108
+ token?: string;
109
+ accent?: AppAccent;
110
+ theme?: EmbedColorMode;
111
+ font?: string;
112
+ };
113
+ /**
114
+ * The snippet a host pastes (D-08): one script tag and one element. A public
115
+ * application needs no token; a private one takes the token its owner was
116
+ * issued for it, written as given, so the snippet works for as long as the
117
+ * token lives — a host's own page writes the one its server is issued for
118
+ * each visit.
119
+ */
120
+ export declare function embedSnippetOf(options: EmbedSnippetOptions): string;