@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,148 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { APP_ACCENTS, APP_EMBED_MODES } from '../apps/appspec';
6
+ /** The element's name: the one a host writes. */
7
+ export const EMBED_TAG = 'datalayer-app';
8
+ /** Where the script is served on a Datalayer origin; the stylesheet beside it. */
9
+ export const EMBED_SCRIPT_PATH = '/embed/datalayer-app.js';
10
+ export const EMBED_STYLESHEET_PATH = '/embed/datalayer-app.css';
11
+ /**
12
+ * The element's attributes, each in a sentence — what the documentation and
13
+ * the element's own refusals say.
14
+ */
15
+ export const EMBED_ATTRIBUTES = {
16
+ app: 'The application’s id on Datalayer.',
17
+ origin: 'Where Datalayer is; the origin the script was loaded from by default.',
18
+ token: 'An embed token for an application that is not public, issued to your server for each visit.',
19
+ spec: 'The address of an Appspec (YAML or JSON) to run, in place of `app`.',
20
+ mode: 'inline, bubble, panel or assistant; the Appspec’s `deployment.embedded.mode` by default, then inline.',
21
+ accent: 'green, rose, sky, lime, sun or violet; the Appspec’s `interface.accent` by default.',
22
+ theme: 'light, dark or auto (the visitor’s system); auto by default.',
23
+ font: 'A CSS font family for the application’s text; Inter, then the system’s sans-serif, by default.',
24
+ server: 'An agent-runtimes server the application’s agent runs on; a Datalayer runtime by default.',
25
+ api: 'Datalayer’s API, where an application is read with its token; the platform’s by default.',
26
+ height: 'The height of an inline application, in pixels; 640 by default.',
27
+ };
28
+ /** Every attribute the element watches. */
29
+ export const EMBED_OBSERVED_ATTRIBUTES = Object.keys(EMBED_ATTRIBUTES);
30
+ /**
31
+ * What a host may set from its own stylesheet, on the element or above it
32
+ * (D-11): the accent, the face and the mode. An attribute says the same and
33
+ * wins over the variable.
34
+ */
35
+ export const EMBED_HOST_VARIABLES = {
36
+ accent: '--datalayer-app-accent',
37
+ font: '--datalayer-app-font',
38
+ theme: '--datalayer-app-theme',
39
+ };
40
+ export const EMBED_COLOR_MODES = ['light', 'dark', 'auto'];
41
+ /**
42
+ * The four modes (D-07), and the chat they float in: a **bubble** is the
43
+ * round button that opens a popup — the copilot pattern; a **panel** is the
44
+ * conversation at the page's right edge, at full height; the **assistant** is
45
+ * the character that speaks in a balloon (§6.9). **Inline** has no chat of
46
+ * its own to float: the application, its page and its conversation, sit in
47
+ * the host's page where the element is.
48
+ */
49
+ export const EMBED_FLOATING_VIEWS = {
50
+ bubble: 'floating-small',
51
+ panel: 'panel',
52
+ assistant: 'assistant',
53
+ };
54
+ /** The chat a mode floats in; none for inline. */
55
+ export function floatingViewOf(mode) {
56
+ return mode === 'inline' ? undefined : EMBED_FLOATING_VIEWS[mode];
57
+ }
58
+ /** A value the element refuses, said in a sentence for the page to show. */
59
+ export class EmbedAttributeError extends Error {
60
+ constructor(message) {
61
+ super(message);
62
+ this.name = 'EmbedAttributeError';
63
+ }
64
+ }
65
+ const listed = (values) => `${values.slice(0, -1).join(', ')} or ${values[values.length - 1]}`;
66
+ function oneOf(value, allowed, what) {
67
+ const text = value?.trim();
68
+ if (!text) {
69
+ return undefined;
70
+ }
71
+ if (!allowed.includes(text)) {
72
+ throw new EmbedAttributeError(`datalayer-app: "${text}" is not ${what}; it is ${listed(allowed)}.`);
73
+ }
74
+ return text;
75
+ }
76
+ /**
77
+ * A font family a host names, checked: names, quotes, commas and spaces, as
78
+ * a `font-family` is written — nothing that could close the declaration or
79
+ * fetch anything (`;`, braces, `url(`).
80
+ */
81
+ export function checkedFont(value) {
82
+ const text = value?.trim() ?? '';
83
+ if (!text) {
84
+ return '';
85
+ }
86
+ if (!/^[\w\s,"'.-]+$/u.test(text)) {
87
+ throw new EmbedAttributeError(`datalayer-app: the font "${text}" is not a font family; write it as a CSS font-family is written, such as "Georgia, serif".`);
88
+ }
89
+ return text;
90
+ }
91
+ /**
92
+ * What the element shows and how, from what the host wrote and what the
93
+ * application says: an attribute first, then the host's CSS variable, then
94
+ * the Appspec, then the default. A value that is not one is refused with a
95
+ * sentence (`EmbedAttributeError`), never quietly replaced.
96
+ */
97
+ export function embedLookOf(inputs, app) {
98
+ const { attributes, variables = {} } = inputs;
99
+ const mode = oneOf(attributes.mode, APP_EMBED_MODES, 'a mode') ??
100
+ app?.deployment?.embedded?.mode ??
101
+ 'inline';
102
+ const accent = oneOf(attributes.accent, APP_ACCENTS, 'an accent') ??
103
+ oneOf(variables.accent, APP_ACCENTS, 'an accent') ??
104
+ app?.interface?.accent ??
105
+ 'green';
106
+ const colorMode = oneOf(attributes.theme, EMBED_COLOR_MODES, 'a theme') ??
107
+ oneOf(variables.theme, EMBED_COLOR_MODES, 'a theme') ??
108
+ 'auto';
109
+ const font = checkedFont(attributes.font) || checkedFont(variables.font);
110
+ return { mode, accent, colorMode, font };
111
+ }
112
+ /** The height of an inline application, in pixels. */
113
+ export function inlineHeightOf(value) {
114
+ const height = Number.parseInt(value ?? '', 10);
115
+ return Number.isFinite(height) && height >= 240 ? height : 640;
116
+ }
117
+ const attributeValue = (value) => value
118
+ .replace(/&/g, '&')
119
+ .replace(/"/g, '"')
120
+ .replace(/</g, '&lt;')
121
+ .replace(/>/g, '&gt;');
122
+ /**
123
+ * The snippet a host pastes (D-08): one script tag and one element. A public
124
+ * application needs no token; a private one takes the token its owner was
125
+ * issued for it, written as given, so the snippet works for as long as the
126
+ * token lives — a host's own page writes the one its server is issued for
127
+ * each visit.
128
+ */
129
+ export function embedSnippetOf(options) {
130
+ const origin = options.origin.replace(/\/+$/, '');
131
+ const attributes = [
132
+ ['app', options.app],
133
+ ['origin', origin],
134
+ ['mode', options.mode],
135
+ ['accent', options.accent],
136
+ ['theme', options.theme],
137
+ ['font', options.font ? checkedFont(options.font) : undefined],
138
+ ['token', options.token],
139
+ ];
140
+ const written = attributes
141
+ .filter((entry) => Boolean(entry[1]))
142
+ .map(([name, value]) => ` ${name}="${attributeValue(value)}"`)
143
+ .join('');
144
+ return [
145
+ `<script src="${attributeValue(origin + EMBED_SCRIPT_PATH)}" async></script>`,
146
+ `<${EMBED_TAG}${written}></${EMBED_TAG}>`,
147
+ ].join('\n');
148
+ }
@@ -0,0 +1,24 @@
1
+ import { type ThemeStyles } from '@datalayer/primer-addons';
2
+ import type { AppAccent } from '../../types/agentspecs';
3
+ /**
4
+ * The `loop` theme as the embed wears it: the application's accent over it
5
+ * in both modes (T-05), and the host's face when it named one.
6
+ */
7
+ export declare function embedThemeStyles({ accent, font, }: {
8
+ accent: AppAccent;
9
+ font?: string;
10
+ }): ThemeStyles;
11
+ /**
12
+ * The element's own stylesheet, written into its shadow root.
13
+ *
14
+ * `all: initial` on the host: nothing the host page sets — a font, a colour,
15
+ * a line height, a text alignment — is inherited by the application. The
16
+ * theme's custom properties are set again inside, on the provider's element,
17
+ * so a host's variable of the same name never reaches what the application
18
+ * draws. A floating mode takes no room in the page: what it draws is fixed to
19
+ * the viewport.
20
+ *
21
+ * With the theme's own stylesheet, scoped to the provider's element in this
22
+ * root.
23
+ */
24
+ export declare function embedShadowCss(themeCss?: string): string;
@@ -0,0 +1,83 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { loopAccentStyles, loopFontFamily, loopThemeStyles, scopeThemeCss, THEME_SCOPE_ATTRIBUTE, } from '@datalayer/primer-addons';
6
+ /** One mode's properties, with the theme's face replaced by the host's. */
7
+ function withFace(styles, font) {
8
+ if (!font) {
9
+ return styles;
10
+ }
11
+ return Object.fromEntries(Object.entries(styles).map(([name, value]) => [
12
+ name,
13
+ typeof value === 'string'
14
+ ? value.split(loopFontFamily).join(font)
15
+ : value,
16
+ ]));
17
+ }
18
+ /**
19
+ * The `loop` theme as the embed wears it: the application's accent over it
20
+ * in both modes (T-05), and the host's face when it named one.
21
+ */
22
+ export function embedThemeStyles({ accent, font = '', }) {
23
+ const mode = (which) => withFace({
24
+ ...loopThemeStyles[which],
25
+ ...loopAccentStyles(accent, which),
26
+ }, font);
27
+ return {
28
+ ...loopThemeStyles,
29
+ light: mode('light'),
30
+ dark: mode('dark'),
31
+ };
32
+ }
33
+ /**
34
+ * The element's own stylesheet, written into its shadow root.
35
+ *
36
+ * `all: initial` on the host: nothing the host page sets — a font, a colour,
37
+ * a line height, a text alignment — is inherited by the application. The
38
+ * theme's custom properties are set again inside, on the provider's element,
39
+ * so a host's variable of the same name never reaches what the application
40
+ * draws. A floating mode takes no room in the page: what it draws is fixed to
41
+ * the viewport.
42
+ *
43
+ * With the theme's own stylesheet, scoped to the provider's element in this
44
+ * root.
45
+ */
46
+ export function embedShadowCss(themeCss) {
47
+ const own = `
48
+ :host {
49
+ all: initial;
50
+ display: block;
51
+ position: relative;
52
+ width: 100%;
53
+ }
54
+ :host([hidden]) {
55
+ display: none;
56
+ }
57
+ :host(:not([data-embed-mode="inline"])) {
58
+ width: 0;
59
+ height: 0;
60
+ }
61
+ .datalayer-app-root,
62
+ .datalayer-app-portal {
63
+ font-family: var(--fontStack-sansSerif, system-ui, sans-serif);
64
+ }
65
+ .datalayer-app-said {
66
+ font: 14px/1.5 system-ui, sans-serif;
67
+ padding: 12px 16px;
68
+ border: 1px solid #d0d7de;
69
+ border-radius: 12px;
70
+ color: #1f2328;
71
+ background: #ffffff;
72
+ }
73
+ iframe.datalayer-app-frame {
74
+ display: block;
75
+ width: 100%;
76
+ border: 0;
77
+ min-height: 240px;
78
+ }
79
+ `;
80
+ return themeCss
81
+ ? `${own}\n${scopeThemeCss(themeCss, [`[${THEME_SCOPE_ATTRIBUTE}]`])}`
82
+ : own;
83
+ }
@@ -16,7 +16,7 @@ export { ChatHeaderPlugin, CHAT_HEADER_PLUGIN_NAME, } from './plugins/chat-heade
16
16
  export { InputPromptPlugin, INPUT_PROMPT_PLUGIN_NAME, type InputPromptPluginConfig, } from './plugins/input-prompt';
17
17
  export { createChatExtrasPlugin, CHAT_EXTRAS_PLUGIN_NAME, type ChatExtrasHandle, } from './plugins/chat-extras';
18
18
  export { LoopChatExtras, LoopChatLayout, LoopChatTurn, LoopOpeningDocument, LoopOpeningNotebook, LoopPromptPanel, type ChatLayoutContribution, type ChatLayoutParts, type ChatTurnContribution, type ChatTurnSnapshot, type ChatTurnStatus, type LoopChatExtrasValue, type OpeningDocumentContribution, type OpeningNotebookContribution, type PromptPanelContribution, } from './core';
19
- export { LoopPageLayoutPlugin, LOOP_PAGE_LAYOUT_PLUGIN_NAME, openConversationPanel, pageLayoutSheet, pageLayoutPanelOpen, setPageLayoutChatMode, type LoopPageLayoutConfig, } from './plugins/page-layout';
19
+ export { LoopPageLayoutPlugin, LOOP_PAGE_LAYOUT_PLUGIN_NAME, openConversationPanel, pageLayoutSheet, pageLayoutPanelOpen, setPageLayoutChatMode, SplitLayout, type LoopPageLayoutConfig, } from './plugins/page-layout';
20
20
  export { useOptionalTeamSelection } from './plugins/agents/useTeamSelection';
21
21
  export type { TeamMember, TeamSelection } from './plugins/agents/team';
22
22
  export { ChatViewPlugin, CHAT_VIEW_PLUGIN_NAME } from './plugins/chat-view';
@@ -50,6 +50,7 @@ export { SANDBOX_CAPACITIES, SandboxCapacityPlugins, } from './plugins/agent-cod
50
50
  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
+ 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';
53
54
  export { GraphViewPlugin, GRAPH_PLUGIN_NAME, GRAPH_VIEW_TYPE, } from './plugins/graph';
54
55
  export { PluginsPanelPlugin, PLUGINS_PANEL_PLUGIN_NAME, PluginsPanel, } from './plugins/plugins-panel';
55
56
  export { AGENTS_PLUGIN_NAME, AgentsPlugin, SandboxSelector, browserSource, createBrowserSandboxService, createServerSandboxService, createSwitchableSandboxService, suppliedSource, useOptionalSandboxService, useSandboxService, type AgentsConfig, type AgentsOutput, type SandboxExecution, type SandboxService, type SandboxTarget, type ServiceManagerSource, type SwitchableSandboxService, } from './plugins/agents';
@@ -63,7 +64,10 @@ export { WindowFrame, WindowFramePlugin, WINDOW_ACTIONS_SLOT, WINDOW_CONTROLS_SL
63
64
  export { LoopCommandsPlugin, COMMANDS_PLUGIN_NAME } from './plugins/commands';
64
65
  export { PromptPlugin, PROMPT_PLUGIN_NAME } from './plugins/prompt';
65
66
  export { LoopEmbed, type LoopEmbedProps } from './embed/LoopEmbed';
67
+ export { AppEmbed, type AppEmbedProps } from './embed/AppEmbed';
68
+ export { defineDatalayerAppElement, type DatalayerAppElementOptions, } from './embed/element';
69
+ export { EMBED_ATTRIBUTES, EMBED_HOST_VARIABLES, EMBED_TAG, embedLookOf, embedSnippetOf, floatingViewOf, type EmbedLook, type EmbedSnippetOptions, } from './embed/embedConfig';
66
70
  export { SubagentActivityPlugin, SubagentPulse, SUBAGENT_ACTIVITY_PLUGIN_NAME, } from './plugins/subagent-activity';
67
71
  export { loopPlugins, type LoopPresetOptions } from './presets';
68
72
  export { DocumentExtension, NotebookExtension } from './extensions';
69
- 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, type AppCheck, defineAppPlugin, type AppRendererProps, type AppInstance, readAppspecYaml, writeAppspecYaml, type ParsedAppspec, } from './apps';
73
+ 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, type AppCheck, defineAppPlugin, type AppRendererProps, type AppInstance, appLayoutOptions, readAppspecYaml, writeAppspecYaml, type ParsedAppspec, } from './apps';
package/lib/loop/index.js CHANGED
@@ -29,7 +29,7 @@ export { LoopChatExtras, LoopChatLayout, LoopChatTurn, LoopOpeningDocument, Loop
29
29
  export { LoopPageLayoutPlugin, LOOP_PAGE_LAYOUT_PLUGIN_NAME, openConversationPanel, pageLayoutSheet, pageLayoutPanelOpen,
30
30
  // How a host moves the composer: `sidebar` stands it in the conversation
31
31
  // panel, under what was said, and opens the panel to show it.
32
- setPageLayoutChatMode, } from './plugins/page-layout';
32
+ setPageLayoutChatMode, SplitLayout, } from './plugins/page-layout';
33
33
  export { useOptionalTeamSelection } from './plugins/agents/useTeamSelection';
34
34
  export { ChatViewPlugin, CHAT_VIEW_PLUGIN_NAME } from './plugins/chat-view';
35
35
  export { NotebookViewPlugin, NOTEBOOK_VIEW_PLUGIN_NAME, } from './plugins/notebook-view';
@@ -63,6 +63,7 @@ export { SANDBOX_CAPACITIES, SandboxCapacityPlugins, } from './plugins/agent-cod
63
63
  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
+ export { APP_KIND_PATHS, APP_PAGE_PLUGIN_NAME, APP_PAGE_SURFACE, AppPage, appKindPaths, appPageAction, appPageData, defineAppPagePlugin, hasAppPage, surfaceUnshown, } from './plugins/app-page';
66
67
  export { GraphViewPlugin, GRAPH_PLUGIN_NAME, GRAPH_VIEW_TYPE, } from './plugins/graph';
67
68
  export { PluginsPanelPlugin, PLUGINS_PANEL_PLUGIN_NAME, PluginsPanel, } from './plugins/plugins-panel';
68
69
  export { AGENTS_PLUGIN_NAME, AgentsPlugin, SandboxSelector, browserSource, createBrowserSandboxService, createServerSandboxService, createSwitchableSandboxService, suppliedSource, useOptionalSandboxService, useSandboxService, } from './plugins/agents';
@@ -81,6 +82,11 @@ export { LoopCommandsPlugin, COMMANDS_PLUGIN_NAME } from './plugins/commands';
81
82
  export { PromptPlugin, PROMPT_PLUGIN_NAME } from './plugins/prompt';
82
83
  // Loop, ready to drop into somebody else's page.
83
84
  export { LoopEmbed } from './embed/LoopEmbed';
85
+ // An application in another product's page: the React component, the
86
+ // `<datalayer-app>` element, and the snippet a host pastes (LOOP D-07 to D-09).
87
+ export { AppEmbed } from './embed/AppEmbed';
88
+ export { defineDatalayerAppElement, } from './embed/element';
89
+ export { EMBED_ATTRIBUTES, EMBED_HOST_VARIABLES, EMBED_TAG, embedLookOf, embedSnippetOf, floatingViewOf, } from './embed/embedConfig';
84
90
  export { SubagentActivityPlugin, SubagentPulse, SUBAGENT_ACTIVITY_PLUGIN_NAME, } from './plugins/subagent-activity';
85
91
  // The set a workspace normally has, so a host starts from a working Loop
86
92
  // rather than assembling one — and never from the example.
@@ -90,4 +96,4 @@ export { loopPlugins } from './presets';
90
96
  export { DocumentExtension, NotebookExtension } from './extensions';
91
97
  // Applications: what one does when its agent calls a tool — the rules a
92
98
  // person wrote, decided on what the tool does.
93
- 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, defineAppPlugin, readAppspecYaml, writeAppspecYaml, } from './apps';
99
+ 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, defineAppPlugin, appLayoutOptions, readAppspecYaml, writeAppspecYaml, } from './apps';
@@ -8,15 +8,33 @@
8
8
  * follows, and what keeps a generated form from being the one light card in
9
9
  * a dark workspace.
10
10
  *
11
+ * The same renderer draws an application's page (`loop/plugins/app-page`):
12
+ * there the surface is fed live `data` by path as the conversation moves, and
13
+ * an action is handed the surface it came from, so that what a block wrote
14
+ * into its data model can be read when a button is pressed.
15
+ *
11
16
  * @module loop/plugins/a2ui-surface/InlineSurface
12
17
  */
13
18
  import type { JSX } from 'react';
14
- import { type A2uiClientAction, type A2uiMessage } from '@a2ui/web_core/v0_9';
19
+ import { type ReactComponentImplementation } from '@a2ui/react/v0_9';
20
+ import { type A2uiClientAction, type A2uiMessage, type SurfaceModel } from '@a2ui/web_core/v0_9';
21
+ /** A surface as the renderer holds it: its data model is `dataModel`. */
22
+ export type InlineSurfaceModel = SurfaceModel<ReactComponentImplementation>;
23
+ type Surface = InlineSurfaceModel;
15
24
  /** The catalogue a surface is rewritten to before it is drawn. */
16
25
  export declare const SURFACE_CATALOG_ID: string;
17
- export declare function InlineSurface({ messages, onAction, validationError, }: {
26
+ /** Values to publish into a surface, by path. */
27
+ export type InlineSurfaceData = Record<string, unknown>;
28
+ export declare function InlineSurface({ messages, onAction, validationError, data, }: {
18
29
  messages: A2uiMessage[];
19
- onAction: (action: A2uiClientAction) => void;
30
+ /** Told of an action, with the surface it came from. */
31
+ onAction: (action: A2uiClientAction, surface?: Surface) => void;
20
32
  validationError?: string | null;
33
+ /**
34
+ * Values published into every surface's data model, by path, after the
35
+ * messages are processed and whenever one changes. Only a path whose value
36
+ * changed is written: what a person typed elsewhere in the model stays.
37
+ */
38
+ data?: InlineSurfaceData;
21
39
  }): JSX.Element | null;
22
40
  export default InlineSurface;
@@ -12,12 +12,14 @@ const INHERIT_THEME = {
12
12
  ['--a2ui-color-primary']: 'var(--fgColor-accent)',
13
13
  ['--a2ui-color-outline']: 'var(--borderColor-default)',
14
14
  };
15
- export function InlineSurface({ messages, onAction, validationError, }) {
15
+ export function InlineSurface({ messages, onAction, validationError, data, }) {
16
16
  // Reached through a ref: the processor is built once, and the handler it
17
17
  // was built with must not go stale when the host's does not.
18
18
  const onActionRef = useRef(onAction);
19
19
  onActionRef.current = onAction;
20
- const processor = useMemo(() => new MessageProcessor([basicCatalog], action => onActionRef.current(action)), []);
20
+ const processor = useMemo(() => new MessageProcessor([basicCatalog], action => onActionRef.current(action, processorRef.current?.model.getSurface(action.surfaceId))), []);
21
+ const processorRef = useRef(undefined);
22
+ processorRef.current = processor;
21
23
  const [surfaces, setSurfaces] = useState([]);
22
24
  useEffect(() => {
23
25
  const created = processor.onSurfaceCreated(surface => setSurfaces(previous => [...previous, surface]));
@@ -28,15 +30,41 @@ export function InlineSurface({ messages, onAction, validationError, }) {
28
30
  };
29
31
  }, [processor]);
30
32
  // Once: the messages are one tool result, processed when it arrives.
33
+ // A tree the catalog refuses is said, where it would have been drawn,
34
+ // rather than thrown out of an effect and taking the page down with it.
31
35
  const processedRef = useRef(false);
36
+ const [processError, setProcessError] = useState(null);
32
37
  useEffect(() => {
33
38
  if (processedRef.current) {
34
39
  return;
35
40
  }
36
41
  processedRef.current = true;
37
- processor.processMessages(messages);
42
+ try {
43
+ processor.processMessages(messages);
44
+ }
45
+ catch (error) {
46
+ setProcessError(`This surface could not be drawn: ${error instanceof Error ? error.message : String(error)}`);
47
+ }
38
48
  }, [messages, processor]);
39
- if (surfaces.length === 0) {
49
+ // After the messages: the surfaces exist by then, created synchronously.
50
+ const publishedRef = useRef({});
51
+ useEffect(() => {
52
+ if (!data) {
53
+ return;
54
+ }
55
+ const published = publishedRef.current;
56
+ for (const [path, value] of Object.entries(data)) {
57
+ if (Object.is(published[path], value)) {
58
+ continue;
59
+ }
60
+ published[path] = value;
61
+ for (const surface of processor.model.surfacesMap.values()) {
62
+ surface.dataModel.set(path, value);
63
+ }
64
+ }
65
+ }, [data, processor]);
66
+ const notice = validationError || processError;
67
+ if (surfaces.length === 0 && !notice) {
40
68
  return null;
41
69
  }
42
70
  return (_jsx(A2uiMarkdownProvider, { children: _jsxs(Box, { style: INHERIT_THEME, sx: {
@@ -44,7 +72,7 @@ export function InlineSurface({ messages, onAction, validationError, }) {
44
72
  display: 'flex',
45
73
  flexDirection: 'column',
46
74
  gap: 2,
47
- }, children: [validationError ? (_jsx(Box, { role: "alert", sx: {
75
+ }, children: [notice ? (_jsx(Box, { role: "alert", sx: {
48
76
  px: 3,
49
77
  py: 2,
50
78
  borderRadius: 2,
@@ -53,7 +81,7 @@ export function InlineSurface({ messages, onAction, validationError, }) {
53
81
  borderColor: 'danger.muted',
54
82
  color: 'danger.fg',
55
83
  fontSize: 1,
56
- }, children: validationError })) : null, surfaces.map(surface => (_jsx(Box, { sx: {
84
+ }, children: notice })) : null, surfaces.map(surface => (_jsx(Box, { sx: {
57
85
  border: '1px solid',
58
86
  borderColor: 'border.default',
59
87
  borderRadius: 2,
@@ -34,6 +34,10 @@ export type AgentCapacityOptions = {
34
34
  createPayload?: Record<string, unknown>;
35
35
  /** The openers the empty chat offers for this capacity. */
36
36
  suggestions?: ChatSuggestionItem[];
37
+ /** What the agent is told on top of its spec's prompt when the page turns it. */
38
+ instructions?: string;
39
+ /** The model it runs on in the page, in place of its spec's. */
40
+ model?: string;
37
41
  };
38
42
  export declare function defineAgentCapacityPlugin(options: AgentCapacityOptions): ReactorPlugin<Record<string, never>, unknown, unknown>;
39
43
  export default defineAgentCapacityPlugin;
@@ -23,7 +23,7 @@
23
23
  import { contribution, definePlugin, } from '@datalayer/reactor';
24
24
  import { LoopAgentBlueprint, LoopChatSuggestion, } from '../../core';
25
25
  export function defineAgentCapacityPlugin(options) {
26
- const { key, displayName, description, specId, octicon, emoji, codemode, createPayload, suggestions, } = options;
26
+ const { key, displayName, description, specId, octicon, emoji, codemode, createPayload, suggestions, instructions, model, } = options;
27
27
  return definePlugin({
28
28
  name: `@datalayer/loop-plugin-agent-${key}`,
29
29
  displayName,
@@ -40,6 +40,8 @@ export function defineAgentCapacityPlugin(options) {
40
40
  enable_codemode: codemode ?? false,
41
41
  ...createPayload,
42
42
  },
43
+ ...(instructions ? { instructions } : {}),
44
+ ...(model ? { model } : {}),
43
45
  }, { id: key }),
44
46
  ...(suggestions && suggestions.length > 0
45
47
  ? [
@@ -105,3 +105,10 @@ export type SwitchableConfig = {
105
105
  kernelSource?: ServiceManagerSource;
106
106
  };
107
107
  export declare function createSwitchableSandboxService({ serverUrl, initialTarget, kernelSource, localAgent, blueprint, }: SwitchableConfig): SwitchableSandboxService;
108
+ /**
109
+ * Create the agent whose per-agent Jupyter sandbox backs Local, on the
110
+ * agent-runtimes server at `serverUrl`, unless it is there already — then
111
+ * only its sandbox is asked for. Exported for a host that addresses the
112
+ * agent without a workspace: the embed's floating modes (`AppEmbed`).
113
+ */
114
+ export declare function ensureServerAgent(serverUrl: string, agentId: string | undefined, createPayload: Record<string, unknown>): Promise<void>;