@datalayer/agent-runtimes 1.3.83 → 1.3.85
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.
- package/lib/chat/ChatFloating.d.ts +5 -4
- package/lib/chat/ChatFloating.js +8 -6
- package/lib/chat/ChatLanguage.d.ts +17 -0
- package/lib/chat/ChatLanguage.js +29 -0
- package/lib/chat/assistant/AssistantStage.d.ts +4 -4
- package/lib/chat/assistant/AssistantStage.js +4 -2
- package/lib/chat/assistant/BalloonParts.d.ts +2 -1
- package/lib/chat/assistant/BalloonParts.js +12 -5
- package/lib/chat/assistant/BalloonVisual.js +3 -1
- package/lib/chat/assistant/ConversationBalloon.d.ts +1 -1
- package/lib/chat/assistant/ConversationBalloon.js +3 -2
- package/lib/chat/assistant/SpeechBalloon.d.ts +1 -1
- package/lib/chat/assistant/SpeechBalloon.js +10 -7
- package/lib/chat/assistant/toolLine.d.ts +8 -4
- package/lib/chat/assistant/toolLine.js +13 -9
- package/lib/chat/base/ChatBase.js +30 -11
- package/lib/chat/base/loopElement.d.ts +53 -0
- package/lib/chat/base/loopElement.js +78 -0
- package/lib/chat/base/loopWindow.d.ts +31 -0
- package/lib/chat/base/loopWindow.js +74 -0
- package/lib/chat/display/EmptyState.js +3 -1
- package/lib/chat/index.d.ts +2 -0
- package/lib/chat/index.js +3 -0
- package/lib/chat/messages/ChatMessages.js +3 -1
- package/lib/chat/messages/TurnFooter.js +3 -1
- package/lib/chat/prompt/InputPrompt.js +3 -1
- package/lib/chat/prompt/InputPromptLexical.js +3 -1
- package/lib/chat/prompt/footer/InPromptFooter.js +3 -1
- package/lib/chat/tools/ToolApprovalBanner.js +5 -3
- package/lib/chat/tools/ToolApprovalDialog.js +5 -3
- package/lib/chat/tools/ToolCallDisplay.js +29 -25
- package/lib/chat/words.d.ts +102 -0
- package/lib/chat/words.js +460 -0
- package/lib/components/a2ui/custom/index.d.ts +39 -0
- package/lib/components/a2ui/custom/index.js +95 -0
- package/lib/components/a2ui/custom/sandbox.d.ts +73 -0
- package/lib/components/a2ui/custom/sandbox.js +188 -0
- package/lib/components/a2ui/datalayer/FileUpload.d.ts +10 -2
- package/lib/components/a2ui/datalayer/FileUpload.js +10 -7
- package/lib/components/a2ui/datalayer/Form.d.ts +2 -0
- package/lib/components/a2ui/datalayer/Form.js +9 -2
- package/lib/components/a2ui/datalayer/formWidgets.d.ts +19 -0
- package/lib/components/a2ui/datalayer/formWidgets.js +51 -0
- package/lib/components/a2ui/datalayer/index.d.ts +1 -1
- package/lib/components/a2ui/datalayer/index.js +1 -1
- package/lib/components/a2ui/datalayer/schema.d.ts +10 -0
- package/lib/components/a2ui/datalayer/schema.js +12 -1
- package/lib/components/a2ui/index.d.ts +1 -0
- package/lib/components/a2ui/index.js +1 -0
- package/lib/components/a2ui/visibleWhen.d.ts +12 -1
- package/lib/components/a2ui/visibleWhen.js +24 -3
- package/lib/components/inspector/AgentInspector.d.ts +25 -44
- package/lib/components/inspector/AgentInspector.js +27 -215
- package/lib/components/inspector/AgentInspectorDialog.d.ts +6 -6
- package/lib/components/inspector/AgentInspectorDialog.js +2 -2
- package/lib/components/inspector/a2aSpans.d.ts +51 -0
- package/lib/components/inspector/a2aSpans.js +373 -0
- package/lib/components/inspector/agentSpans.d.ts +120 -0
- package/lib/components/inspector/agentSpans.js +253 -0
- package/lib/components/inspector/chatSpans.d.ts +36 -0
- package/lib/components/inspector/chatSpans.js +50 -0
- package/lib/components/inspector/classify.d.ts +11 -8
- package/lib/components/inspector/classify.js +23 -24
- package/lib/components/inspector/index.d.ts +7 -7
- package/lib/components/inspector/index.js +7 -7
- package/lib/components/teams/A2ATeamGraph.d.ts +4 -4
- package/lib/components/teams/SceneTranscript.d.ts +29 -0
- package/lib/components/teams/SceneTranscript.js +54 -0
- package/lib/components/teams/SceneView.d.ts +41 -0
- package/lib/components/teams/SceneView.js +28 -0
- package/lib/components/teams/index.d.ts +3 -0
- package/lib/components/teams/index.js +3 -0
- package/lib/components/teams/sceneTranscript.d.ts +74 -0
- package/lib/components/teams/sceneTranscript.js +294 -0
- package/lib/components/teams/teamConnections.d.ts +2 -2
- package/lib/components/teams/teamConnections.js +3 -3
- package/lib/components/teams/useA2ATeam.d.ts +4 -4
- package/lib/components/teams/useA2ATeam.js +50 -46
- package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
- package/lib/examples/AgentA2ATeamExample.js +70 -60
- package/lib/examples/AssistantExample.js +2 -2
- package/lib/loop/apps/AppComposer.d.ts +40 -4
- package/lib/loop/apps/AppComposer.js +194 -19
- package/lib/loop/apps/AppRenderer.d.ts +21 -3
- package/lib/loop/apps/AppRenderer.js +88 -48
- package/lib/loop/apps/appspec.d.ts +13 -1
- package/lib/loop/apps/appspec.js +305 -12
- package/lib/loop/apps/checks.d.ts +14 -0
- package/lib/loop/apps/checks.js +276 -5
- package/lib/loop/apps/composer.d.ts +21 -10
- package/lib/loop/apps/composer.js +41 -8
- package/lib/loop/apps/customComponents.d.ts +41 -0
- package/lib/loop/apps/customComponents.js +260 -0
- package/lib/loop/apps/hostTools.d.ts +8 -1
- package/lib/loop/apps/hostTools.js +9 -0
- package/lib/loop/apps/index.d.ts +1 -0
- package/lib/loop/apps/index.js +1 -0
- package/lib/loop/apps/language.d.ts +68 -0
- package/lib/loop/apps/language.js +275 -0
- package/lib/loop/apps/settingsInputs.d.ts +29 -0
- package/lib/loop/apps/settingsInputs.js +147 -0
- package/lib/loop/apps/uploads.d.ts +43 -0
- package/lib/loop/apps/uploads.js +66 -0
- package/lib/loop/core/a2uiComponents.d.ts +10 -0
- package/lib/loop/core/a2uiComponents.js +15 -0
- package/lib/loop/core/index.d.ts +15 -1
- package/lib/loop/core/index.js +3 -0
- package/lib/loop/embed/AppEmbed.d.ts +25 -3
- package/lib/loop/embed/AppEmbed.js +106 -18
- package/lib/loop/embed/element.d.ts +5 -0
- package/lib/loop/embed/element.js +84 -5
- package/lib/loop/embed/embedConfig.d.ts +8 -0
- package/lib/loop/embed/embedConfig.js +18 -0
- package/lib/loop/embed/embedSession.d.ts +14 -0
- package/lib/loop/embed/embedSession.js +34 -0
- package/lib/loop/embed/hostBridge.d.ts +50 -3
- package/lib/loop/embed/hostBridge.js +22 -2
- package/lib/loop/index.d.ts +4 -3
- package/lib/loop/index.js +4 -3
- package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +7 -1
- package/lib/loop/plugins/a2ui-surface/InlineSurface.js +7 -2
- package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +4 -1
- package/lib/loop/plugins/a2ui-surface/ownComponents.d.ts +6 -0
- package/lib/loop/plugins/a2ui-surface/ownComponents.js +30 -0
- package/lib/loop/plugins/app-components/index.d.ts +26 -0
- package/lib/loop/plugins/app-components/index.js +56 -0
- package/lib/loop/plugins/app-elements/AppElements.d.ts +27 -0
- package/lib/loop/plugins/app-elements/AppElements.js +79 -0
- package/lib/loop/plugins/app-elements/index.d.ts +20 -0
- package/lib/loop/plugins/app-elements/index.js +43 -0
- package/lib/loop/plugins/app-page/AppPage.d.ts +9 -0
- package/lib/loop/plugins/app-page/AppPage.js +106 -8
- package/lib/loop/plugins/app-page/appPageModel.d.ts +17 -1
- package/lib/loop/plugins/app-page/appPageModel.js +122 -4
- package/lib/loop/plugins/app-page/index.d.ts +1 -0
- package/lib/loop/plugins/app-page/index.js +1 -0
- package/lib/loop/plugins/app-page/pageRun.d.ts +46 -0
- package/lib/loop/plugins/app-page/pageRun.js +111 -0
- package/lib/loop/plugins/assistant/index.js +8 -6
- package/lib/loop/plugins/chat/ChatView.js +16 -6
- package/lib/loop/plugins/chat/index.d.ts +1 -1
- package/lib/loop/plugins/chat/index.js +2 -1
- package/lib/models/PageTag.d.ts +1 -1
- package/lib/protocols/BrowserAgentAdapter.d.ts +8 -6
- package/lib/protocols/BrowserAgentAdapter.js +13 -10
- package/lib/runtimes/browser/a2aPeer.d.ts +2 -1
- package/lib/runtimes/browser/protocol.d.ts +1 -1
- package/lib/specs/actions.js +78 -1
- package/lib/specs/apps.d.ts +5 -0
- package/lib/specs/apps.js +1518 -216
- package/lib/specs/appspecSchema.js +630 -3
- package/lib/specs/teams/teams.d.ts +3 -0
- package/lib/specs/teams/teams.js +249 -0
- package/lib/specs/uiPlugins.js +6 -1
- package/lib/types/agentspecs.d.ts +200 -1
- package/lib/types/chat.d.ts +6 -5
- package/lib/types/teams.d.ts +13 -2
- package/package.json +2 -2
- package/scripts/codegen/generate_apps.py +20 -2
- package/scripts/codegen/generate_teams.py +15 -2
- package/lib/components/inspector/JsonTree.d.ts +0 -24
- package/lib/components/inspector/JsonTree.js +0 -89
- package/lib/components/inspector/a2aInspect.d.ts +0 -56
- package/lib/components/inspector/a2aInspect.js +0 -478
- package/lib/components/inspector/agentInspector.d.ts +0 -179
- package/lib/components/inspector/agentInspector.js +0 -267
- package/lib/components/inspector/chatInspect.d.ts +0 -38
- package/lib/components/inspector/chatInspect.js +0 -65
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* The frame a component its developer wrote is drawn in (LOOP P-17), and
|
|
7
|
+
* what passes between it and the page.
|
|
8
|
+
*
|
|
9
|
+
* A hosted application lives on the origin where people are signed in, and
|
|
10
|
+
* no script an application supplies reaches that page (LOOP D-22). A custom
|
|
11
|
+
* component's module is such a script, so it never runs in the page: it runs
|
|
12
|
+
* in an `<iframe sandbox="allow-scripts">` — no `allow-same-origin`, so the
|
|
13
|
+
* frame has an origin of its own that is no one's: no cookie, no storage, no
|
|
14
|
+
* access to the page, its DOM or its tokens — whose document allows nothing
|
|
15
|
+
* but its own bootstrap and the module it is handed (`default-src 'none'`):
|
|
16
|
+
* no request leaves the frame, no form, no navigation of the page.
|
|
17
|
+
*
|
|
18
|
+
* The page fetches the module — without credentials — checks it against the
|
|
19
|
+
* hash it was reviewed with when the spec gives one (`integrity`), and hands
|
|
20
|
+
* its text to the frame, which imports it from a `blob:` address. Its
|
|
21
|
+
* default export draws it: `export default function (root, {props, send})`,
|
|
22
|
+
* returning `{update(props)}` to be told of new properties. What it is given
|
|
23
|
+
* is its properties and what it shows; what it gives back is what it sends,
|
|
24
|
+
* by name, each refused unless declared.
|
|
25
|
+
*
|
|
26
|
+
* @module components/a2ui/custom/sandbox
|
|
27
|
+
*/
|
|
28
|
+
/** What the frame of a custom component may do: run scripts, nothing else. */
|
|
29
|
+
export const CUSTOM_FRAME_SANDBOX = 'allow-scripts';
|
|
30
|
+
/** The largest module the page hands to a frame, in bytes. */
|
|
31
|
+
export const MAX_MODULE_BYTES = 2 * 1024 * 1024;
|
|
32
|
+
/** The tag every message between the page and a frame carries. */
|
|
33
|
+
export const FRAME_TAG = 'loop.component';
|
|
34
|
+
/** The policy of the frame's document: its bootstrap, the module it is handed, nothing else. */
|
|
35
|
+
export function customFramePolicy(nonce) {
|
|
36
|
+
return [
|
|
37
|
+
"default-src 'none'",
|
|
38
|
+
`script-src 'nonce-${nonce}' blob:`,
|
|
39
|
+
"style-src 'unsafe-inline'",
|
|
40
|
+
'img-src data: blob:',
|
|
41
|
+
'font-src data:',
|
|
42
|
+
'media-src data: blob:',
|
|
43
|
+
"form-action 'none'",
|
|
44
|
+
"base-uri 'none'",
|
|
45
|
+
].join('; ');
|
|
46
|
+
}
|
|
47
|
+
/** A nonce for one frame's bootstrap. */
|
|
48
|
+
export function frameNonce() {
|
|
49
|
+
const bytes = new Uint8Array(16);
|
|
50
|
+
crypto.getRandomValues(bytes);
|
|
51
|
+
return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The document of a custom component's frame (its `srcdoc`): the policy,
|
|
55
|
+
* the root it draws into, and the bootstrap that imports the module it is
|
|
56
|
+
* handed and passes properties in and what it sends out.
|
|
57
|
+
*/
|
|
58
|
+
export function customFrameDocument(nonce, label) {
|
|
59
|
+
const escaped = label.replace(/[&<>"]/g, char => `&#${char.charCodeAt(0)};`);
|
|
60
|
+
return `<!doctype html>
|
|
61
|
+
<html lang="en">
|
|
62
|
+
<head>
|
|
63
|
+
<meta charset="utf-8">
|
|
64
|
+
<meta http-equiv="Content-Security-Policy" content="${customFramePolicy(nonce)}">
|
|
65
|
+
<title>${escaped}</title>
|
|
66
|
+
<style>html,body{margin:0;padding:0;background:transparent;color-scheme:light dark;font-family:system-ui,-apple-system,sans-serif}#root{box-sizing:border-box;min-height:100vh}</style>
|
|
67
|
+
</head>
|
|
68
|
+
<body>
|
|
69
|
+
<div id="root"></div>
|
|
70
|
+
<script nonce="${nonce}">
|
|
71
|
+
(() => {
|
|
72
|
+
const TAG = ${JSON.stringify(FRAME_TAG)};
|
|
73
|
+
const root = document.getElementById('root');
|
|
74
|
+
let props = {};
|
|
75
|
+
let drawn = null;
|
|
76
|
+
let started = false;
|
|
77
|
+
const say = message => parent.postMessage(Object.assign({ tag: TAG }, message), '*');
|
|
78
|
+
const send = (name, value) => say({ kind: 'send', name: String(name), value: value });
|
|
79
|
+
addEventListener('message', async event => {
|
|
80
|
+
if (event.source !== parent) return;
|
|
81
|
+
const data = event.data;
|
|
82
|
+
if (!data || data.tag !== TAG) return;
|
|
83
|
+
if (data.kind === 'props') {
|
|
84
|
+
props = data.props || {};
|
|
85
|
+
if (drawn && typeof drawn.update === 'function') drawn.update(props);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (data.kind !== 'module' || started) return;
|
|
89
|
+
started = true;
|
|
90
|
+
try {
|
|
91
|
+
const url = URL.createObjectURL(new Blob([data.code], { type: 'text/javascript' }));
|
|
92
|
+
const module = await import(url);
|
|
93
|
+
URL.revokeObjectURL(url);
|
|
94
|
+
if (typeof module.default !== 'function') {
|
|
95
|
+
throw new Error('its module has no default export that draws it');
|
|
96
|
+
}
|
|
97
|
+
drawn = (await module.default(root, { props: props, send: send })) || {};
|
|
98
|
+
say({ kind: 'drawn' });
|
|
99
|
+
} catch (error) {
|
|
100
|
+
say({ kind: 'failed', message: String((error && error.message) || error) });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
say({ kind: 'ready' });
|
|
104
|
+
})();
|
|
105
|
+
</script>
|
|
106
|
+
</body>
|
|
107
|
+
</html>`;
|
|
108
|
+
}
|
|
109
|
+
/** What a frame said, when it is a message of the frame drawn here; null otherwise. */
|
|
110
|
+
export function fromFrame(event, frame) {
|
|
111
|
+
if (!frame || event.source !== frame) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const data = event.data;
|
|
115
|
+
if (!data || typeof data !== 'object' || data.tag !== FRAME_TAG) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
switch (data.kind) {
|
|
119
|
+
case 'ready':
|
|
120
|
+
case 'drawn':
|
|
121
|
+
return { kind: data.kind };
|
|
122
|
+
case 'failed':
|
|
123
|
+
return { kind: 'failed', message: String(data.message ?? '') };
|
|
124
|
+
case 'send':
|
|
125
|
+
return typeof data.name === 'string'
|
|
126
|
+
? { kind: 'send', name: data.name, value: data.value }
|
|
127
|
+
: null;
|
|
128
|
+
default:
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const ALGORITHMS = {
|
|
133
|
+
sha256: 'SHA-256',
|
|
134
|
+
sha384: 'SHA-384',
|
|
135
|
+
sha512: 'SHA-512',
|
|
136
|
+
};
|
|
137
|
+
function base64(bytes) {
|
|
138
|
+
let binary = '';
|
|
139
|
+
for (const byte of new Uint8Array(bytes)) {
|
|
140
|
+
binary += String.fromCharCode(byte);
|
|
141
|
+
}
|
|
142
|
+
return btoa(binary);
|
|
143
|
+
}
|
|
144
|
+
/** Whether a module's bytes are the ones its Subresource Integrity hash names. */
|
|
145
|
+
export async function integrityHolds(bytes, integrity) {
|
|
146
|
+
const at = integrity.indexOf('-');
|
|
147
|
+
const algorithm = ALGORITHMS[integrity.slice(0, at)];
|
|
148
|
+
if (at < 0 || !algorithm) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
const digest = await crypto.subtle.digest(algorithm, bytes);
|
|
152
|
+
return base64(digest) === integrity.slice(at + 1);
|
|
153
|
+
}
|
|
154
|
+
/** The modules fetched, by address and hash: each fetched once per page. */
|
|
155
|
+
const MODULES = new Map();
|
|
156
|
+
/**
|
|
157
|
+
* The text of a custom component's module, fetched without credentials and
|
|
158
|
+
* checked against its hash when one is given. It throws, with a sentence,
|
|
159
|
+
* for a module that cannot be fetched, is too large, or is not the one
|
|
160
|
+
* reviewed.
|
|
161
|
+
*/
|
|
162
|
+
export function customModule(source, integrity, fetcher = fetch) {
|
|
163
|
+
const key = `${source}\n${integrity}`;
|
|
164
|
+
let module = MODULES.get(key);
|
|
165
|
+
if (!module) {
|
|
166
|
+
module = (async () => {
|
|
167
|
+
const response = await fetcher(source, {
|
|
168
|
+
credentials: 'omit',
|
|
169
|
+
mode: 'cors',
|
|
170
|
+
});
|
|
171
|
+
if (!response.ok) {
|
|
172
|
+
throw new Error(`Its module could not be fetched from ${source} (${response.status}).`);
|
|
173
|
+
}
|
|
174
|
+
const bytes = await response.arrayBuffer();
|
|
175
|
+
if (bytes.byteLength > MAX_MODULE_BYTES) {
|
|
176
|
+
throw new Error(`Its module is larger than ${MAX_MODULE_BYTES / 1024 / 1024} MB.`);
|
|
177
|
+
}
|
|
178
|
+
if (integrity && !(await integrityHolds(bytes, integrity))) {
|
|
179
|
+
throw new Error('Its module is not the one reviewed: its hash differs from its integrity.');
|
|
180
|
+
}
|
|
181
|
+
return new TextDecoder().decode(bytes);
|
|
182
|
+
})();
|
|
183
|
+
// A failure is not kept: the next drawing tries again.
|
|
184
|
+
module.catch(() => MODULES.delete(key));
|
|
185
|
+
MODULES.set(key, module);
|
|
186
|
+
}
|
|
187
|
+
return module;
|
|
188
|
+
}
|
|
@@ -14,8 +14,16 @@ export type GivenFile = {
|
|
|
14
14
|
size: number;
|
|
15
15
|
data_url: string;
|
|
16
16
|
};
|
|
17
|
-
/**
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Why a file is refused, or `null` when it is taken: its kinds are
|
|
19
|
+
* extensions (`.csv`), media types (`application/pdf`) or families
|
|
20
|
+
* (`image/*`), as the Appspec's uploads say them (LOOP P-21).
|
|
21
|
+
*/
|
|
22
|
+
export declare function refusal(file: Pick<File, 'name' | 'size'> & {
|
|
23
|
+
type?: string;
|
|
24
|
+
}, accept: string[] | undefined, maxMb: number): string | null;
|
|
25
|
+
/** A file as the application receives it: its content as a data URL. */
|
|
26
|
+
export declare const readFile: (file: File) => Promise<GivenFile>;
|
|
19
27
|
export declare function FileUploadView({ props }: {
|
|
20
28
|
props: FileUploadProps;
|
|
21
29
|
}): import("react").JSX.Element;
|
|
@@ -21,22 +21,25 @@ import { UploadIcon } from '@primer/octicons-react';
|
|
|
21
21
|
import { Box } from '@datalayer/primer-addons';
|
|
22
22
|
import { BlockFrame, CARD_RADIUS, Problem, Quiet, isRecord } from './parts';
|
|
23
23
|
import { ownImplementation } from './implementation';
|
|
24
|
+
import { acceptsFile } from '../../../loop/apps/uploads';
|
|
24
25
|
/** The default the catalog gives `max_mb`. */
|
|
25
26
|
const MAX_MB = 25;
|
|
26
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Why a file is refused, or `null` when it is taken: its kinds are
|
|
29
|
+
* extensions (`.csv`), media types (`application/pdf`) or families
|
|
30
|
+
* (`image/*`), as the Appspec's uploads say them (LOOP P-21).
|
|
31
|
+
*/
|
|
27
32
|
export function refusal(file, accept, maxMb) {
|
|
28
|
-
if (accept
|
|
29
|
-
|
|
30
|
-
if (!accept.some(extension => lower.endsWith(extension.toLowerCase()))) {
|
|
31
|
-
return `${file.name} is not one it takes (${accept.join(', ')}).`;
|
|
32
|
-
}
|
|
33
|
+
if (!acceptsFile(accept, { name: file.name, type: file.type ?? '' })) {
|
|
34
|
+
return `${file.name} is not one it takes (${(accept ?? []).join(', ')}).`;
|
|
33
35
|
}
|
|
34
36
|
if (file.size > maxMb * 1024 * 1024) {
|
|
35
37
|
return `${file.name} is larger than ${maxMb} MB.`;
|
|
36
38
|
}
|
|
37
39
|
return null;
|
|
38
40
|
}
|
|
39
|
-
|
|
41
|
+
/** A file as the application receives it: its content as a data URL. */
|
|
42
|
+
export const readFile = (file) => new Promise((resolve, reject) => {
|
|
40
43
|
const reader = new FileReader();
|
|
41
44
|
reader.onload = () => resolve({
|
|
42
45
|
name: file.name,
|
|
@@ -2,6 +2,8 @@ import { type OwnCommon } from './implementation';
|
|
|
2
2
|
export type FormProps = OwnCommon & {
|
|
3
3
|
title?: string;
|
|
4
4
|
schema: Record<string, unknown>;
|
|
5
|
+
/** How its fields are drawn: a uiSchema by field name (P-20). */
|
|
6
|
+
ui?: Record<string, unknown>;
|
|
5
7
|
submit_label?: string;
|
|
6
8
|
values?: unknown;
|
|
7
9
|
setValues: (values: Record<string, unknown>) => void;
|
|
@@ -17,6 +17,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
17
17
|
* written where `values` points as they are filled, for the next run to take;
|
|
18
18
|
* the runtime checks them against the same schema.
|
|
19
19
|
*
|
|
20
|
+
* Its `ui` says how its fields are drawn (LOOP P-20): a uiSchema by field
|
|
21
|
+
* name, `ui:widget` one of primer-rjsf's widgets or of `switch` and `tags`
|
|
22
|
+
* (`formWidgets`) — the nine inputs a setting is drawn with.
|
|
23
|
+
*
|
|
20
24
|
* @module components/a2ui/datalayer/Form
|
|
21
25
|
*/
|
|
22
26
|
import { useEffect, useMemo, useState } from 'react';
|
|
@@ -24,6 +28,7 @@ import { Form as SchemaForm } from '@datalayer/primer-rjsf';
|
|
|
24
28
|
import validator from '@rjsf/validator-ajv8';
|
|
25
29
|
import { BlockFrame, Problem, isRecord } from './parts';
|
|
26
30
|
import { ownImplementation } from './implementation';
|
|
31
|
+
import { FORM_WIDGETS } from './formWidgets';
|
|
27
32
|
/** The default the catalog gives `submit_label`. */
|
|
28
33
|
const SUBMIT_LABEL = 'Send';
|
|
29
34
|
export function FormView({ props }) {
|
|
@@ -41,15 +46,17 @@ export function FormView({ props }) {
|
|
|
41
46
|
}, [shownKey]);
|
|
42
47
|
// Without an action, nothing is sent: no button, the values written as filled.
|
|
43
48
|
const live = action === undefined;
|
|
49
|
+
const drawn = JSON.stringify(props.ui ?? {});
|
|
44
50
|
const uiSchema = useMemo(() => ({
|
|
51
|
+
...JSON.parse(drawn),
|
|
45
52
|
'ui:submitButtonOptions': live
|
|
46
53
|
? { norender: true }
|
|
47
54
|
: { submitText: submitLabel },
|
|
48
|
-
}), [live, submitLabel]);
|
|
55
|
+
}), [drawn, live, submitLabel]);
|
|
49
56
|
const problem = schema.type !== undefined && schema.type !== 'object'
|
|
50
57
|
? 'Its fields are not an object schema: a form asks for several named values.'
|
|
51
58
|
: null;
|
|
52
|
-
return (_jsx(BlockFrame, { title: title, label: "Form", weight: props.weight, testId: "a2ui-form", children: problem ? (_jsx(Problem, { children: problem })) : (_jsx(SchemaForm, { schema: schema, uiSchema: uiSchema, formData: values, validator: validator, liveValidate: touched, onBlur: () => setTouched(true), onError: () => setTouched(true), showErrorList: false, onChange: event => {
|
|
59
|
+
return (_jsx(BlockFrame, { title: title, label: "Form", weight: props.weight, testId: "a2ui-form", children: problem ? (_jsx(Problem, { children: problem })) : (_jsx(SchemaForm, { schema: schema, uiSchema: uiSchema, widgets: FORM_WIDGETS, formData: values, validator: validator, liveValidate: touched, onBlur: () => setTouched(true), onError: () => setTouched(true), showErrorList: false, onChange: event => {
|
|
53
60
|
const filled = (event.formData ?? {});
|
|
54
61
|
setLocal(filled);
|
|
55
62
|
if (live) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** What rjsf gives a widget, of what these two read. */
|
|
2
|
+
export type WidgetProps = {
|
|
3
|
+
id: string;
|
|
4
|
+
value: unknown;
|
|
5
|
+
label: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
readonly?: boolean;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
onChange: (value: unknown) => void;
|
|
10
|
+
};
|
|
11
|
+
/** A `boolean` as a toggle switch. */
|
|
12
|
+
export declare function SwitchWidget({ id, value, label, disabled, readonly, onChange, }: WidgetProps): import("react").JSX.Element;
|
|
13
|
+
/** An `array` of `string` items as tokens: typed, then Enter or a comma. */
|
|
14
|
+
export declare function TagsWidget({ id, value, label, disabled, readonly, placeholder, onChange, }: WidgetProps): import("react").JSX.Element;
|
|
15
|
+
/** What a form's uiSchema may name besides primer-rjsf's own widgets. */
|
|
16
|
+
export declare const FORM_WIDGETS: {
|
|
17
|
+
switch: typeof SwitchWidget;
|
|
18
|
+
tags: typeof TagsWidget;
|
|
19
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } 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
|
+
* The two inputs of the nine (LOOP P-20) that `@datalayer/primer-rjsf` does
|
|
8
|
+
* not draw itself, as rjsf widgets a form's uiSchema names (`ui:widget`):
|
|
9
|
+
*
|
|
10
|
+
* - `switch` — a `boolean` as Primer's toggle switch;
|
|
11
|
+
* - `tags` — an `array` of `string` items as words in a field, each a token:
|
|
12
|
+
* Enter or a comma adds what is typed, a token's cross takes it away.
|
|
13
|
+
*
|
|
14
|
+
* The seven others are primer-rjsf's: Select (an `enum`), Slider (`range`),
|
|
15
|
+
* TextInput (a `string`, or `textarea`), Checkbox (a `boolean`), DatePicker
|
|
16
|
+
* (`format: date`), MultiSelect (an `array` of `enum` items, or
|
|
17
|
+
* `checkboxes`) and RadioGroup (`radio`).
|
|
18
|
+
*
|
|
19
|
+
* @module components/a2ui/datalayer/formWidgets
|
|
20
|
+
*/
|
|
21
|
+
import { useState } from 'react';
|
|
22
|
+
import { Box, FormControl, Text, TextInputWithTokens, ToggleSwitch, } from '@primer/react';
|
|
23
|
+
/** A `boolean` as a toggle switch. */
|
|
24
|
+
export function SwitchWidget({ id, value, label, disabled, readonly, onChange, }) {
|
|
25
|
+
return (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 1 }, children: [_jsx(Text, { as: "span", id: `${id}-label`, sx: { fontWeight: 'semibold' }, children: label }), _jsx(ToggleSwitch, { size: "small", "aria-labelledby": `${id}-label`, checked: Boolean(value), disabled: disabled || readonly, onClick: () => onChange(!value) })] }));
|
|
26
|
+
}
|
|
27
|
+
/** An `array` of `string` items as tokens: typed, then Enter or a comma. */
|
|
28
|
+
export function TagsWidget({ id, value, label, disabled, readonly, placeholder, onChange, }) {
|
|
29
|
+
const tags = Array.isArray(value) ? value.map(String) : [];
|
|
30
|
+
const [typed, setTyped] = useState('');
|
|
31
|
+
const add = () => {
|
|
32
|
+
const word = typed.trim();
|
|
33
|
+
if (word && !tags.includes(word)) {
|
|
34
|
+
onChange([...tags, word]);
|
|
35
|
+
}
|
|
36
|
+
setTyped('');
|
|
37
|
+
};
|
|
38
|
+
return (_jsxs(FormControl, { id: id, disabled: disabled || readonly, children: [_jsx(FormControl.Label, { children: label }), _jsx(TextInputWithTokens, { block: true, placeholder: placeholder, tokens: tags.map((text, index) => ({ id: index, text })), value: typed, onChange: event => setTyped(event.target.value.replace(',', '')), onKeyDown: event => {
|
|
39
|
+
if (event.key === 'Enter' || event.key === ',') {
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
add();
|
|
42
|
+
}
|
|
43
|
+
}, onBlur: add, onTokenRemove: index => {
|
|
44
|
+
onChange(tags.filter((_, at) => at !== index));
|
|
45
|
+
} })] }));
|
|
46
|
+
}
|
|
47
|
+
/** What a form's uiSchema may name besides primer-rjsf's own widgets. */
|
|
48
|
+
export const FORM_WIDGETS = {
|
|
49
|
+
switch: SwitchWidget,
|
|
50
|
+
tags: TagsWidget,
|
|
51
|
+
};
|
|
@@ -14,7 +14,7 @@ import type { ReactComponentImplementation } from '@a2ui/react/v0_9';
|
|
|
14
14
|
* a renderer for one it no longer has, fails at once.
|
|
15
15
|
*/
|
|
16
16
|
export declare const OWN_COMPONENTS: ReactComponentImplementation[];
|
|
17
|
-
export { OWN_COMPONENT_IDS, ownComponentSchema } from './schema';
|
|
17
|
+
export { OWN_COMPONENT_IDS, componentSchemaOf, ownComponentSchema, } from './schema';
|
|
18
18
|
export { DRAWN_OWN_COMPONENTS, type DrawnOwnComponent } from './drawn';
|
|
19
19
|
export { chartOption, chartPoints, chartSeries } from './Chart';
|
|
20
20
|
export { chatItems } from './Chat';
|
|
@@ -35,7 +35,7 @@ export const OWN_COMPONENTS = (() => {
|
|
|
35
35
|
}
|
|
36
36
|
return OWN_COMPONENT_IDS.map(id => RENDERERS[id]);
|
|
37
37
|
})();
|
|
38
|
-
export { OWN_COMPONENT_IDS, ownComponentSchema } from './schema';
|
|
38
|
+
export { OWN_COMPONENT_IDS, componentSchemaOf, ownComponentSchema, } from './schema';
|
|
39
39
|
export { DRAWN_OWN_COMPONENTS } from './drawn';
|
|
40
40
|
export { chartOption, chartPoints, chartSeries } from './Chart';
|
|
41
41
|
export { chatItems } from './Chat';
|
|
@@ -32,3 +32,13 @@ export declare function ownComponentSchema(id: string): z.ZodObject<Record<strin
|
|
|
32
32
|
}, {
|
|
33
33
|
[x: string]: any;
|
|
34
34
|
}>;
|
|
35
|
+
/**
|
|
36
|
+
* The A2UI schema of a component of Datalayer's own kind, from its entry in
|
|
37
|
+
* the catalog — one of Datalayer's own, or one an application's developer
|
|
38
|
+
* wrote (LOOP P-17), listed as the catalog lists a component.
|
|
39
|
+
*/
|
|
40
|
+
export declare function componentSchemaOf(component: ComponentSpec): z.ZodObject<Record<string, z.ZodTypeAny>, "strict", z.ZodTypeAny, {
|
|
41
|
+
[x: string]: any;
|
|
42
|
+
}, {
|
|
43
|
+
[x: string]: any;
|
|
44
|
+
}>;
|
|
@@ -83,7 +83,18 @@ export function bindingNames(component) {
|
|
|
83
83
|
* catalog's are: a key it does not declare refuses the tree.
|
|
84
84
|
*/
|
|
85
85
|
export function ownComponentSchema(id) {
|
|
86
|
-
|
|
86
|
+
return componentSchemaOf(ownComponent(id));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The A2UI schema of a component of Datalayer's own kind, from its entry in
|
|
90
|
+
* the catalog — one of Datalayer's own, or one an application's developer
|
|
91
|
+
* wrote (LOOP P-17), listed as the catalog lists a component.
|
|
92
|
+
*/
|
|
93
|
+
export function componentSchemaOf(component) {
|
|
94
|
+
const id = component.id;
|
|
95
|
+
if (!component.bindings) {
|
|
96
|
+
throw new Error(`${id} says nothing of what it shows or sends.`);
|
|
97
|
+
}
|
|
87
98
|
const json = component.properties;
|
|
88
99
|
const required = new Set(json.required ?? []);
|
|
89
100
|
const shape = {};
|
|
@@ -22,5 +22,16 @@ export declare const datalayerCatalog: Catalog<ReactComponentImplementation, imp
|
|
|
22
22
|
* plugins contribute as blocks (LOOP R-01b, `loop.canvas.block`), so that an
|
|
23
23
|
* application's page draws what its Canvas can place and nothing else. Under
|
|
24
24
|
* the same id; a name the catalog does not draw is an error.
|
|
25
|
+
*
|
|
26
|
+
* `own` are the renderers of the components the application's developer
|
|
27
|
+
* wrote (LOOP P-17, `customImplementations`): drawn for that application's
|
|
28
|
+
* page alone, each behind `visible_when` as the catalog's are. One that
|
|
29
|
+
* takes the name of a component of the catalog is an error.
|
|
25
30
|
*/
|
|
26
|
-
export declare function catalogOfBlocks(names: readonly string[]): Catalog<ReactComponentImplementation>;
|
|
31
|
+
export declare function catalogOfBlocks(names: readonly string[], own?: readonly ReactComponentImplementation[]): Catalog<ReactComponentImplementation>;
|
|
32
|
+
/**
|
|
33
|
+
* Datalayer's whole catalog with the components an application's developer
|
|
34
|
+
* wrote (LOOP P-17): what its answers and the elements its code shows are
|
|
35
|
+
* drawn with (P-04, P-18). Datalayer's catalog itself when it wrote none.
|
|
36
|
+
*/
|
|
37
|
+
export declare function catalogWithOwn(own: readonly ReactComponentImplementation[]): Catalog<ReactComponentImplementation>;
|
|
@@ -60,11 +60,32 @@ export const datalayerCatalog = new Catalog(basicCatalog.id, basicCatalog.protoc
|
|
|
60
60
|
* plugins contribute as blocks (LOOP R-01b, `loop.canvas.block`), so that an
|
|
61
61
|
* application's page draws what its Canvas can place and nothing else. Under
|
|
62
62
|
* the same id; a name the catalog does not draw is an error.
|
|
63
|
+
*
|
|
64
|
+
* `own` are the renderers of the components the application's developer
|
|
65
|
+
* wrote (LOOP P-17, `customImplementations`): drawn for that application's
|
|
66
|
+
* page alone, each behind `visible_when` as the catalog's are. One that
|
|
67
|
+
* takes the name of a component of the catalog is an error.
|
|
63
68
|
*/
|
|
64
|
-
export function catalogOfBlocks(names) {
|
|
65
|
-
const
|
|
69
|
+
export function catalogOfBlocks(names, own = []) {
|
|
70
|
+
const custom = new Map(own.map(component => [component.name, withVisibleWhen(component)]));
|
|
71
|
+
const taken = [...custom.keys()].filter(name => datalayerCatalog.components.has(name));
|
|
72
|
+
if (taken.length > 0) {
|
|
73
|
+
throw new Error(`${taken.join(', ')} ${taken.length === 1 ? 'is a component' : 'are components'} of the catalog: a component of an application's own is named otherwise.`);
|
|
74
|
+
}
|
|
75
|
+
const unknown = names.filter(name => !datalayerCatalog.components.has(name) && !custom.has(name));
|
|
66
76
|
if (unknown.length > 0) {
|
|
67
77
|
throw new Error(`No renderer draws ${unknown.join(', ')}: a block is contributed by a plugin whose component the catalog draws.`);
|
|
68
78
|
}
|
|
69
|
-
return new Catalog(datalayerCatalog.id, datalayerCatalog.protocolVersion, names.map(name => datalayerCatalog.components.get(name)), [...datalayerCatalog.functions.values()], datalayerCatalog.themeSchema, datalayerCatalog.instructions);
|
|
79
|
+
return new Catalog(datalayerCatalog.id, datalayerCatalog.protocolVersion, names.map(name => datalayerCatalog.components.get(name) ?? custom.get(name)), [...datalayerCatalog.functions.values()], datalayerCatalog.themeSchema, datalayerCatalog.instructions);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Datalayer's whole catalog with the components an application's developer
|
|
83
|
+
* wrote (LOOP P-17): what its answers and the elements its code shows are
|
|
84
|
+
* drawn with (P-04, P-18). Datalayer's catalog itself when it wrote none.
|
|
85
|
+
*/
|
|
86
|
+
export function catalogWithOwn(own) {
|
|
87
|
+
if (own.length === 0) {
|
|
88
|
+
return datalayerCatalog;
|
|
89
|
+
}
|
|
90
|
+
return catalogOfBlocks([...datalayerCatalog.components.keys(), ...own.map(each => each.name)], own);
|
|
70
91
|
}
|
|
@@ -1,59 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Agent Inspector: what agents do,
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The Agent Inspector: what agents do, as the OpenTelemetry spans the page
|
|
3
|
+
* records (`agentSpans`, `chatSpans`, `a2aSpans`), drawn by core's OTEL view
|
|
4
|
+
* (`OtelLiveSpans`): each turn a trace, its tool calls and its A2A requests
|
|
5
|
+
* under it, newest last; a span opens to its attributes (a call's arguments
|
|
6
|
+
* and result, a request whole), its events (what the peer answered, event by
|
|
7
|
+
* event) and its trace tree.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Fed by a sink (`useAgentInspector`) or given its entries.
|
|
9
|
+
* One agent's own (`agent`): what it did, and what was sent to it — one A2A
|
|
10
|
+
* request shows in the asker's and in the peer's. Filters by source (A2A,
|
|
11
|
+
* MCP, frontend tool, skill, codemode, runtime tool, model), operation and
|
|
12
|
+
* agent; *Pause*, *Clear*, *Export* as the OTEL view has them.
|
|
13
13
|
*
|
|
14
14
|
* @module components/inspector/AgentInspector
|
|
15
15
|
*/
|
|
16
16
|
import type { JSX } from 'react';
|
|
17
|
-
import
|
|
17
|
+
import type { OtelLiveTracer } from '@datalayer/core/lib/otel/live';
|
|
18
|
+
import type { OtelSpan } from '@datalayer/core/lib/otel/types';
|
|
19
|
+
import { type OtelSpanFacet } from '@datalayer/core/lib/otel/views/OtelLiveSpans';
|
|
18
20
|
export type AgentInspectorProps = {
|
|
19
21
|
/** The record to show, kept current. */
|
|
20
|
-
|
|
21
|
-
/** Or its
|
|
22
|
-
|
|
23
|
-
/**
|
|
22
|
+
tracer?: OtelLiveTracer | null;
|
|
23
|
+
/** Or its spans, as given. */
|
|
24
|
+
spans?: readonly OtelSpan[];
|
|
25
|
+
/** One agent's own record: what it did, and what was sent to it. */
|
|
26
|
+
agent?: string;
|
|
27
|
+
/** How tall the list grows before it scrolls. */
|
|
24
28
|
maxHeight?: number | string;
|
|
25
|
-
/**
|
|
26
|
-
|
|
29
|
+
/** Narrower, the detail under the list: beside other content. */
|
|
30
|
+
compact?: boolean;
|
|
27
31
|
/** What is said while nothing is recorded. */
|
|
28
32
|
emptyText?: string;
|
|
29
|
-
/** The file
|
|
33
|
+
/** The file the spans are exported as, without `.json`. */
|
|
30
34
|
exportName?: string;
|
|
31
|
-
/**
|
|
32
|
-
* One agent's own record: what it did (`actor`), and the A2A messages it
|
|
33
|
-
* sent or received — one message shows in the sender's and in the
|
|
34
|
-
* receiver's, the same entry, as sent in one and received in the other.
|
|
35
|
-
*/
|
|
36
|
-
agent?: string;
|
|
37
|
-
};
|
|
38
|
-
/** Whether an entry is one agent's: done by it, or sent to or by it. */
|
|
39
|
-
export declare function isAgentsEntry(entry: AgentInspectorEntry, agent: string): boolean;
|
|
40
|
-
/** Which way an entry went, from one agent's side: `→ Accounting`, `← Accounting`. */
|
|
41
|
-
export declare function directionFrom(entry: AgentInspectorEntry, agent: string | undefined): string;
|
|
42
|
-
/** A time of day, to the millisecond: `15:44:31.299`. */
|
|
43
|
-
export declare function clockTime(at: number): string;
|
|
44
|
-
/** Time since the first entry: `+1.204s`. */
|
|
45
|
-
export declare function sinceFirst(at: number, first: number): string;
|
|
46
|
-
/** How long a call took: `412 ms`, `3.4 s`. */
|
|
47
|
-
export declare function durationText(ms: number): string;
|
|
48
|
-
export type AgentInspectorFilter = {
|
|
49
|
-
sources: ReadonlySet<AgentInspectorSource>;
|
|
50
|
-
kinds: ReadonlySet<string>;
|
|
51
|
-
actors: ReadonlySet<string>;
|
|
52
35
|
};
|
|
53
|
-
/** The
|
|
54
|
-
export declare
|
|
55
|
-
/** A session, as JSON. */
|
|
56
|
-
export declare function exportEntries(entries: readonly AgentInspectorEntry[]): AgentInspectorExport;
|
|
36
|
+
/** The Inspector's filters: where a span comes from, what it does, which agent. */
|
|
37
|
+
export declare const AGENT_INSPECTOR_FACETS: readonly OtelSpanFacet[];
|
|
57
38
|
/** The Agent Inspector. */
|
|
58
|
-
export declare function AgentInspector({
|
|
39
|
+
export declare function AgentInspector({ tracer, spans, agent, maxHeight, compact, emptyText, exportName, }: AgentInspectorProps): JSX.Element;
|
|
59
40
|
export default AgentInspector;
|