@datalayer/agent-runtimes 1.3.84 → 1.3.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +14 -11
- 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/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 +117 -22
- package/lib/components/teams/A2ATeamGraph.js +217 -97
- 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 +4 -0
- package/lib/components/teams/index.js +4 -0
- package/lib/components/teams/sceneStage.d.ts +120 -0
- package/lib/components/teams/sceneStage.js +170 -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 +94 -22
- package/lib/components/teams/useA2ATeam.js +356 -140
- package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
- package/lib/examples/AgentA2ATeamExample.js +70 -60
- package/lib/examples/AssistantExample.js +2 -2
- package/lib/examples/ScenesExample.d.ts +26 -0
- package/lib/examples/ScenesExample.js +241 -0
- package/lib/examples/example-selector.js +1 -0
- package/lib/examples/exampleGroups.js +3 -0
- package/lib/examples/index.d.ts +1 -0
- package/lib/examples/index.js +1 -0
- package/lib/examples/main.js +4 -0
- package/lib/loop/apps/AppComposer.d.ts +2 -1
- package/lib/loop/apps/AppComposer.js +6 -3
- package/lib/loop/apps/AppRenderer.d.ts +2 -1
- package/lib/loop/apps/AppRenderer.js +34 -25
- package/lib/loop/apps/appspec.d.ts +11 -1
- package/lib/loop/apps/appspec.js +115 -1
- package/lib/loop/apps/checks.d.ts +8 -0
- package/lib/loop/apps/checks.js +106 -4
- 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/core/a2uiComponents.d.ts +10 -0
- package/lib/loop/core/a2uiComponents.js +15 -0
- package/lib/loop/core/index.d.ts +1 -0
- package/lib/loop/core/index.js +3 -0
- package/lib/loop/embed/AppEmbed.d.ts +11 -3
- package/lib/loop/embed/AppEmbed.js +42 -16
- package/lib/loop/embed/element.d.ts +5 -0
- package/lib/loop/embed/element.js +61 -5
- package/lib/loop/embed/embedConfig.d.ts +8 -0
- package/lib/loop/embed/embedConfig.js +18 -0
- package/lib/loop/embed/hostBridge.d.ts +28 -2
- 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.js +4 -1
- 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 +120 -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 +6 -4
- 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/runtimes/browser/a2aPeer.d.ts +2 -1
- package/lib/specs/actions.js +78 -1
- package/lib/specs/apps.d.ts +5 -0
- package/lib/specs/apps.js +1384 -231
- package/lib/specs/appspecSchema.js +191 -1
- package/lib/specs/index.d.ts +1 -0
- package/lib/specs/index.js +1 -0
- package/lib/specs/mcpServers.js +8 -2
- package/lib/specs/scenes.d.ts +21 -0
- package/lib/specs/scenes.js +1191 -0
- package/lib/specs/teams/teams.d.ts +3 -0
- package/lib/specs/teams/teams.js +249 -0
- package/lib/types/agentspecs.d.ts +70 -0
- package/lib/types/chat.d.ts +6 -5
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/scenes.d.ts +207 -0
- package/lib/types/scenes.js +5 -0
- package/lib/types/teams.d.ts +13 -2
- package/package.json +4 -4
- package/scripts/codegen/generate_agents.py +1 -0
- package/scripts/codegen/generate_apps.py +4 -2
- package/scripts/codegen/generate_scenes.py +301 -0
- 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,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transcript of a scene, drawn (LOOP A-06): the lines
|
|
3
|
+
* {@link transcriptOfSpans} or {@link transcriptOfRecord} built, each with
|
|
4
|
+
* its time, who speaks or acts and to whom, and the words or the tool —
|
|
5
|
+
* and *Copy as text*, which copies them with their times
|
|
6
|
+
* ({@link transcriptText}).
|
|
7
|
+
*
|
|
8
|
+
* @module components/teams/SceneTranscript
|
|
9
|
+
*/
|
|
10
|
+
import type { JSX } from 'react';
|
|
11
|
+
import type { OtelLiveTracer } from '@datalayer/core/lib/otel/live';
|
|
12
|
+
import { type SceneTranscriptLine, type SceneTranscriptMember } from './sceneTranscript';
|
|
13
|
+
/** The transcript of a tracer's spans, kept current as they change. */
|
|
14
|
+
export declare function useSceneTranscript(tracer: OtelLiveTracer | null | undefined, members: readonly SceneTranscriptMember[]): SceneTranscriptLine[];
|
|
15
|
+
export type SceneTranscriptProps = {
|
|
16
|
+
/** The lines, in order. */
|
|
17
|
+
lines: readonly SceneTranscriptLine[];
|
|
18
|
+
/** What is said while there is no line yet. */
|
|
19
|
+
emptyText?: string;
|
|
20
|
+
/** How tall it grows before it scrolls, in pixels. */
|
|
21
|
+
maxHeight?: number;
|
|
22
|
+
/** Whether *Copy as text* is offered (it is, when there is a line). */
|
|
23
|
+
copy?: boolean;
|
|
24
|
+
/** The clipboard: `navigator.clipboard.writeText` unless a test gives one. */
|
|
25
|
+
writeText?: (text: string) => Promise<void>;
|
|
26
|
+
};
|
|
27
|
+
/** The transcript as a list of lines, newest last, with *Copy as text*. */
|
|
28
|
+
export declare function SceneTranscript({ lines, emptyText, maxHeight, copy, writeText, }: SceneTranscriptProps): JSX.Element;
|
|
29
|
+
export default SceneTranscript;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import { Button, Text } from '@primer/react';
|
|
4
|
+
import { Box } from '@datalayer/primer-addons';
|
|
5
|
+
import { useOtelLiveSpans } from '@datalayer/core/lib/otel/live';
|
|
6
|
+
import { lineHeading, timeText, transcriptOfSpans, transcriptText, } from './sceneTranscript';
|
|
7
|
+
/** The transcript of a tracer's spans, kept current as they change. */
|
|
8
|
+
export function useSceneTranscript(tracer, members) {
|
|
9
|
+
const spans = useOtelLiveSpans(tracer);
|
|
10
|
+
return useMemo(() => transcriptOfSpans(spans, members), [spans, members]);
|
|
11
|
+
}
|
|
12
|
+
/** How long *Copied* stays, in milliseconds. */
|
|
13
|
+
const COPIED_MS = 1500;
|
|
14
|
+
/** The transcript as a list of lines, newest last, with *Copy as text*. */
|
|
15
|
+
export function SceneTranscript({ lines, emptyText = 'What the scene says and does shows here, line by line.', maxHeight, copy = true, writeText, }) {
|
|
16
|
+
const [copied, setCopied] = useState(false);
|
|
17
|
+
const onCopy = async () => {
|
|
18
|
+
const write = writeText ??
|
|
19
|
+
(text => globalThis.navigator?.clipboard?.writeText(text) ?? Promise.resolve());
|
|
20
|
+
try {
|
|
21
|
+
await write(transcriptText(lines));
|
|
22
|
+
setCopied(true);
|
|
23
|
+
setTimeout(() => setCopied(false), COPIED_MS);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// The clipboard refused: nothing to say but the text, still on screen.
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs(Box, { "data-scene-transcript": "", "data-scene-lines": lines.length, children: [lines.length === 0 ? (_jsx(Text, { as: "p", sx: { color: 'fg.muted', fontSize: 1, m: 0 }, children: emptyText })) : (_jsx(Box, { as: "ol", "aria-label": "Transcript", sx: {
|
|
30
|
+
listStyle: 'none',
|
|
31
|
+
m: 0,
|
|
32
|
+
p: 0,
|
|
33
|
+
fontSize: 1,
|
|
34
|
+
...(maxHeight ? { maxHeight, overflowY: 'auto' } : {}),
|
|
35
|
+
}, children: lines.map(line => (_jsxs(Box, { as: "li", "data-transcript-line": line.kind, "data-transcript-from": line.from, "data-transcript-to": line.to, "aria-busy": line.open ? 'true' : undefined, sx: {
|
|
36
|
+
display: 'grid',
|
|
37
|
+
gridTemplateColumns: 'auto 1fr',
|
|
38
|
+
gap: 2,
|
|
39
|
+
py: 1,
|
|
40
|
+
borderBottom: '1px solid',
|
|
41
|
+
borderColor: 'border.muted',
|
|
42
|
+
opacity: line.open ? 0.75 : 1,
|
|
43
|
+
}, children: [_jsx(Text, { as: "time", sx: {
|
|
44
|
+
color: 'fg.muted',
|
|
45
|
+
fontFamily: 'mono',
|
|
46
|
+
fontSize: 0,
|
|
47
|
+
pt: '2px',
|
|
48
|
+
whiteSpace: 'nowrap',
|
|
49
|
+
}, dateTime: new Date(line.at).toISOString(), children: timeText(line.at) }), _jsxs(Box, { sx: { minWidth: 0, overflowWrap: 'anywhere' }, children: [_jsxs(Text, { sx: { fontWeight: 600 }, children: [lineHeading(line), ": "] }), _jsxs(Text, { sx: {
|
|
50
|
+
fontFamily: line.kind === 'called' ? 'mono' : undefined,
|
|
51
|
+
color: line.failed ? 'danger.fg' : undefined,
|
|
52
|
+
}, children: [line.text, line.open ? '…' : '', line.failed ? ' (failed)' : ''] })] })] }, line.key))) })), copy && lines.length > 0 && (_jsx(Box, { sx: { mt: 2 }, children: _jsx(Button, { size: "small", onClick: () => void onCopy(), "data-transcript-copy": "", children: copied ? 'Copied' : 'Copy as text' }) }))] }));
|
|
53
|
+
}
|
|
54
|
+
export default SceneTranscript;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scene on a page: its graph (`A2ATeamGraph`) and its transcript
|
|
3
|
+
* ({@link SceneTranscript}), one shown at a time behind a *Graph · Transcript*
|
|
4
|
+
* toggle (LOOP A-06). The graph stays mounted while the transcript shows, so
|
|
5
|
+
* that what its members were doing is still there when it comes back.
|
|
6
|
+
*
|
|
7
|
+
* The lines come from the Agent Inspector's tracer the graph's members
|
|
8
|
+
* record into (`tracer`, with the `members` to name them), or are given
|
|
9
|
+
* (`lines`: a finished run's, from its record).
|
|
10
|
+
*
|
|
11
|
+
* @module components/teams/SceneView
|
|
12
|
+
*/
|
|
13
|
+
import type { JSX, ReactNode } from 'react';
|
|
14
|
+
import type { OtelLiveTracer } from '@datalayer/core/lib/otel/live';
|
|
15
|
+
import type { SceneTranscriptLine, SceneTranscriptMember } from './sceneTranscript';
|
|
16
|
+
/** Which of the two the scene shows. */
|
|
17
|
+
export type SceneViewChoice = 'graph' | 'transcript';
|
|
18
|
+
export type SceneViewProps = {
|
|
19
|
+
/** The graph: an `A2ATeamGraph`, or whatever draws the scene. */
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
/** The members, to name the spans' services and their connections. */
|
|
22
|
+
members?: readonly SceneTranscriptMember[];
|
|
23
|
+
/** The tracer the members record into: the transcript is read from it. */
|
|
24
|
+
tracer?: OtelLiveTracer | null;
|
|
25
|
+
/** The lines, given: a finished run's. Wins over `tracer`. */
|
|
26
|
+
lines?: readonly SceneTranscriptLine[];
|
|
27
|
+
/** What shows first: the graph unless said. */
|
|
28
|
+
defaultView?: SceneViewChoice;
|
|
29
|
+
/** The choice, held by the page; with `onViewChange`. */
|
|
30
|
+
view?: SceneViewChoice;
|
|
31
|
+
onViewChange?: (view: SceneViewChoice) => void;
|
|
32
|
+
/** What the transcript says while there is no line yet. */
|
|
33
|
+
emptyText?: string;
|
|
34
|
+
/** How tall the transcript grows before it scrolls, in pixels. */
|
|
35
|
+
maxHeight?: number;
|
|
36
|
+
/** Something of the page's beside the toggle: a clock, a line. */
|
|
37
|
+
aside?: ReactNode;
|
|
38
|
+
};
|
|
39
|
+
/** The graph and the transcript of a scene, behind a *Graph · Transcript* toggle. */
|
|
40
|
+
export declare function SceneView({ children, members, tracer, lines, defaultView, view, onViewChange, emptyText, maxHeight, aside, }: SceneViewProps): JSX.Element;
|
|
41
|
+
export default SceneView;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { SegmentedControl } from '@primer/react';
|
|
4
|
+
import { Box } from '@datalayer/primer-addons';
|
|
5
|
+
import { SceneTranscript, useSceneTranscript } from './SceneTranscript';
|
|
6
|
+
const NO_MEMBERS = [];
|
|
7
|
+
/** The graph and the transcript of a scene, behind a *Graph · Transcript* toggle. */
|
|
8
|
+
export function SceneView({ children, members = NO_MEMBERS, tracer, lines, defaultView = 'graph', view, onViewChange, emptyText, maxHeight = 360, aside, }) {
|
|
9
|
+
const [own, setOwn] = useState(defaultView);
|
|
10
|
+
const shown = view ?? own;
|
|
11
|
+
const choose = (next) => {
|
|
12
|
+
setOwn(next);
|
|
13
|
+
onViewChange?.(next);
|
|
14
|
+
};
|
|
15
|
+
const live = useSceneTranscript(lines ? null : tracer, members);
|
|
16
|
+
const transcript = lines ?? live;
|
|
17
|
+
return (_jsxs(Box, { "data-scene-view": shown, children: [_jsxs(Box, { sx: {
|
|
18
|
+
display: 'flex',
|
|
19
|
+
alignItems: 'center',
|
|
20
|
+
justifyContent: 'space-between',
|
|
21
|
+
gap: 2,
|
|
22
|
+
flexWrap: 'wrap',
|
|
23
|
+
mb: 2,
|
|
24
|
+
}, children: [_jsxs(SegmentedControl, { "aria-label": "Scene view", size: "small", children: [_jsx(SegmentedControl.Button, { selected: shown === 'graph', onClick: () => choose('graph'), "data-scene-choice": "graph", children: "Graph" }), _jsx(SegmentedControl.Button, { selected: shown === 'transcript', onClick: () => choose('transcript'), "data-scene-choice": "transcript", children: transcript.length
|
|
25
|
+
? `Transcript (${transcript.length})`
|
|
26
|
+
: 'Transcript' })] }), aside] }), _jsx(Box, { hidden: shown !== 'graph', "data-scene-graph": "", children: children }), shown === 'transcript' && (_jsx(SceneTranscript, { lines: transcript, emptyText: emptyText, maxHeight: maxHeight }))] }));
|
|
27
|
+
}
|
|
28
|
+
export default SceneView;
|
|
@@ -13,3 +13,7 @@ export * from './usePeerSandbox';
|
|
|
13
13
|
export * from './A2ATeamGraph';
|
|
14
14
|
export * from './TeamNotebook';
|
|
15
15
|
export * from './NotebookPreview';
|
|
16
|
+
export * from './sceneTranscript';
|
|
17
|
+
export * from './SceneTranscript';
|
|
18
|
+
export * from './SceneView';
|
|
19
|
+
export * from './sceneStage';
|
|
@@ -17,3 +17,7 @@ export * from './usePeerSandbox';
|
|
|
17
17
|
export * from './A2ATeamGraph';
|
|
18
18
|
export * from './TeamNotebook';
|
|
19
19
|
export * from './NotebookPreview';
|
|
20
|
+
export * from './sceneTranscript';
|
|
21
|
+
export * from './SceneTranscript';
|
|
22
|
+
export * from './SceneView';
|
|
23
|
+
export * from './sceneStage';
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scene as a page stages it (LOOP A-13, A-15), read from the scene spec
|
|
3
|
+
* and its team: who is on stage and where each one stands, who asks whom
|
|
4
|
+
* over A2A, what the entry suggests, and where the members on a runtime are
|
|
5
|
+
* reached when the scene plays against local servers.
|
|
6
|
+
*
|
|
7
|
+
* Pure, so the examples app, the landing and the tests read a scene the
|
|
8
|
+
* same way.
|
|
9
|
+
*
|
|
10
|
+
* @module components/teams/sceneStage
|
|
11
|
+
*/
|
|
12
|
+
import type { AppSpec } from '../../types/agentspecs';
|
|
13
|
+
import type { SceneCastMemberSpec, SceneSpec } from '../../types/scenes';
|
|
14
|
+
import type { TeamSpec } from '../../types/teams';
|
|
15
|
+
/** Who talks to whom over A2A: an edge of the team's graph. */
|
|
16
|
+
export type A2ATeamLink = {
|
|
17
|
+
/** The member that asks, by its id. */
|
|
18
|
+
from: string;
|
|
19
|
+
/** The member asked. */
|
|
20
|
+
to: string;
|
|
21
|
+
};
|
|
22
|
+
/** A reference without its version: `accounting:0.0.1` is `accounting`. */
|
|
23
|
+
export declare function refId(ref: string): string;
|
|
24
|
+
/** The application a cast member is, from the catalogue, or undefined. */
|
|
25
|
+
export declare function appOfMember(member: Pick<SceneCastMemberSpec, 'app'>): AppSpec | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* The links of a team over A2A, in the order its members say them: each
|
|
28
|
+
* member's `talks_to … over: a2a`. A link to a server (over `mcp`) is a
|
|
29
|
+
* connection, drawn under the member, not an edge between members.
|
|
30
|
+
*/
|
|
31
|
+
export declare function teamLinksOf(team: Pick<TeamSpec, 'agents'> | Pick<SceneSpec, 'cast'>): A2ATeamLink[];
|
|
32
|
+
/** A suggestion the entry offers: a label on its chip, and what is sent. */
|
|
33
|
+
export type SceneSuggestion = {
|
|
34
|
+
label: string;
|
|
35
|
+
prompt: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The entry's suggestions: the script's cues, each labelled by the starter
|
|
39
|
+
* of the entry's Appspec that says the same words, or by its first words.
|
|
40
|
+
* Without a scene, the starters themselves.
|
|
41
|
+
*/
|
|
42
|
+
export declare function sceneCuesOf(scene: Pick<SceneSpec, 'script'> | undefined, starters: readonly {
|
|
43
|
+
label: string;
|
|
44
|
+
message: string;
|
|
45
|
+
}[]): SceneSuggestion[];
|
|
46
|
+
/** One member of a scene, as a page stages it. */
|
|
47
|
+
export type SceneStageMember = {
|
|
48
|
+
/** Its id in the cast. */
|
|
49
|
+
id: string;
|
|
50
|
+
/** The application it is. */
|
|
51
|
+
app: AppSpec;
|
|
52
|
+
cast: SceneCastMemberSpec;
|
|
53
|
+
/** Whether it is the member the audience talks to. */
|
|
54
|
+
entry: boolean;
|
|
55
|
+
/** Where its loop turns. */
|
|
56
|
+
runsIn: 'browser' | 'runtime';
|
|
57
|
+
/** The variable its address is read from, when it runs on a runtime. */
|
|
58
|
+
addressVariable?: string;
|
|
59
|
+
/** Where it stands: fractions of the box, from the stage directions. */
|
|
60
|
+
position?: {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
};
|
|
64
|
+
/** What its application needs that is not set up, in sentences. */
|
|
65
|
+
setup: string[];
|
|
66
|
+
};
|
|
67
|
+
/** A scene as a page stages it. */
|
|
68
|
+
export type SceneStage = {
|
|
69
|
+
scene: SceneSpec;
|
|
70
|
+
entry: SceneStageMember;
|
|
71
|
+
/** The members the entry asks over A2A, in the cast's order. */
|
|
72
|
+
peers: SceneStageMember[];
|
|
73
|
+
/** Every member, the entry first. */
|
|
74
|
+
members: SceneStageMember[];
|
|
75
|
+
/** The edges between members: the team's `talks_to` over A2A. */
|
|
76
|
+
links: A2ATeamLink[];
|
|
77
|
+
/** Where each member stands, by its id. */
|
|
78
|
+
positions: Record<string, {
|
|
79
|
+
x: number;
|
|
80
|
+
y: number;
|
|
81
|
+
}>;
|
|
82
|
+
/** Whose balloon opens first. */
|
|
83
|
+
opensFirst: string;
|
|
84
|
+
/** What the audience is told: the line under the title. */
|
|
85
|
+
assumes: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Read a scene's stage: its members with their applications, who the entry
|
|
89
|
+
* is and whom it asks, the links, the positions. Refused when a cast member
|
|
90
|
+
* is an application the catalogue does not have, or the entry is not in the
|
|
91
|
+
* cast: a page cannot stage what it cannot draw.
|
|
92
|
+
*/
|
|
93
|
+
export declare function sceneStageOf(scene: SceneSpec): SceneStage;
|
|
94
|
+
/** The port the examples serve Accounting on, as `npm run examples` starts it. */
|
|
95
|
+
export declare const ACCOUNTING_PORT = 8767;
|
|
96
|
+
/** The first port `serve_scenes.py` starts a runtime on for the other members. */
|
|
97
|
+
export declare const SCENE_PORTS_FROM = 8768;
|
|
98
|
+
/**
|
|
99
|
+
* The members on a runtime across the catalogue's scenes, each once, in
|
|
100
|
+
* the catalogue's order (a scene's cast in its order): what
|
|
101
|
+
* `examples/sales-accounting-a2a/serve_scenes.py` serves, in that order.
|
|
102
|
+
*/
|
|
103
|
+
export declare function sceneRuntimeMembers(scenes?: readonly SceneSpec[]): string[];
|
|
104
|
+
/**
|
|
105
|
+
* The port each runtime member is served on locally: Accounting on
|
|
106
|
+
* {@link ACCOUNTING_PORT}, the others from {@link SCENE_PORTS_FROM} on, in
|
|
107
|
+
* the order of {@link sceneRuntimeMembers}. `serve_scenes.py` gives the
|
|
108
|
+
* same ports by the same rule.
|
|
109
|
+
*/
|
|
110
|
+
export declare function sceneRuntimePorts(scenes?: readonly SceneSpec[]): Record<string, number>;
|
|
111
|
+
/** The variable the examples read a member's local address from: `VITE_A2A_CHANGE_DETECTION_URL`. */
|
|
112
|
+
export declare function sceneAddressVariable(appId: string): string;
|
|
113
|
+
/** Where a member is served over A2A on a local runtime at `port`. */
|
|
114
|
+
export declare function localA2AUrl(appId: string, port: number): string;
|
|
115
|
+
/**
|
|
116
|
+
* Where a member on a runtime is reached by the examples: its variable
|
|
117
|
+
* (`VITE_A2A_<MEMBER>_URL`) when set in `env`, else the local runtime
|
|
118
|
+
* `serve_scenes.py` serves it on.
|
|
119
|
+
*/
|
|
120
|
+
export declare function sceneMemberUrl(appId: string, env: Record<string, string | undefined>, ports?: Record<string, number>): string;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { APP_CATALOGUE } from '../../specs/apps';
|
|
6
|
+
import { listSceneSpecs } from '../../specs/scenes';
|
|
7
|
+
/** A reference without its version: `accounting:0.0.1` is `accounting`. */
|
|
8
|
+
export function refId(ref) {
|
|
9
|
+
const at = ref.lastIndexOf(':');
|
|
10
|
+
return at > 0 && ref.slice(at + 1).includes('.') ? ref.slice(0, at) : ref;
|
|
11
|
+
}
|
|
12
|
+
/** The application a cast member is, from the catalogue, or undefined. */
|
|
13
|
+
export function appOfMember(member) {
|
|
14
|
+
const id = refId(member.app);
|
|
15
|
+
return id && Object.prototype.hasOwnProperty.call(APP_CATALOGUE, id)
|
|
16
|
+
? APP_CATALOGUE[id]
|
|
17
|
+
: undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The links of a team over A2A, in the order its members say them: each
|
|
21
|
+
* member's `talks_to … over: a2a`. A link to a server (over `mcp`) is a
|
|
22
|
+
* connection, drawn under the member, not an edge between members.
|
|
23
|
+
*/
|
|
24
|
+
export function teamLinksOf(team) {
|
|
25
|
+
const members = 'cast' in team
|
|
26
|
+
? team.cast.map(member => ({
|
|
27
|
+
id: member.member,
|
|
28
|
+
talksTo: member.talksTo,
|
|
29
|
+
}))
|
|
30
|
+
: team.agents;
|
|
31
|
+
return members.flatMap(member => (member.talksTo ?? [])
|
|
32
|
+
.filter(link => link.over === 'a2a')
|
|
33
|
+
.map(link => ({ from: member.id, to: link.member })));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The entry's suggestions: the script's cues, each labelled by the starter
|
|
37
|
+
* of the entry's Appspec that says the same words, or by its first words.
|
|
38
|
+
* Without a scene, the starters themselves.
|
|
39
|
+
*/
|
|
40
|
+
export function sceneCuesOf(scene, starters) {
|
|
41
|
+
const fromStarters = starters.map(starter => ({
|
|
42
|
+
label: starter.label,
|
|
43
|
+
prompt: starter.message,
|
|
44
|
+
}));
|
|
45
|
+
if (!scene) {
|
|
46
|
+
return fromStarters;
|
|
47
|
+
}
|
|
48
|
+
return scene.script
|
|
49
|
+
.map(beat => beat.cue.say.trim())
|
|
50
|
+
.filter(Boolean)
|
|
51
|
+
.map(prompt => ({
|
|
52
|
+
label: fromStarters.find(starter => starter.prompt === prompt)?.label ??
|
|
53
|
+
prompt.split(/\s+/).slice(0, 4).join(' '),
|
|
54
|
+
prompt,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Read a scene's stage: its members with their applications, who the entry
|
|
59
|
+
* is and whom it asks, the links, the positions. Refused when a cast member
|
|
60
|
+
* is an application the catalogue does not have, or the entry is not in the
|
|
61
|
+
* cast: a page cannot stage what it cannot draw.
|
|
62
|
+
*/
|
|
63
|
+
export function sceneStageOf(scene) {
|
|
64
|
+
const members = scene.cast
|
|
65
|
+
.filter(member => member.app)
|
|
66
|
+
.map((member) => {
|
|
67
|
+
const app = appOfMember(member);
|
|
68
|
+
if (!app) {
|
|
69
|
+
throw new Error(`The scene ${scene.id} casts ${member.member} as ${member.app}, which the catalogue does not have.`);
|
|
70
|
+
}
|
|
71
|
+
const position = scene.stage.positions[member.member];
|
|
72
|
+
const variable = scene.deployment.addresses[member.member];
|
|
73
|
+
return {
|
|
74
|
+
id: member.member,
|
|
75
|
+
app,
|
|
76
|
+
cast: member,
|
|
77
|
+
entry: member.member === scene.entry,
|
|
78
|
+
runsIn: member.runsIn ?? 'runtime',
|
|
79
|
+
...(variable ? { addressVariable: variable } : {}),
|
|
80
|
+
...(position ? { position } : {}),
|
|
81
|
+
setup: app.setup ?? [],
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
const entry = members.find(member => member.entry);
|
|
85
|
+
if (!entry) {
|
|
86
|
+
throw new Error(`The scene ${scene.id} says its entry is ${scene.entry}, which is not in its cast.`);
|
|
87
|
+
}
|
|
88
|
+
const links = teamLinksOf(scene);
|
|
89
|
+
const asked = links
|
|
90
|
+
.filter(link => link.from === entry.id)
|
|
91
|
+
.map(link => link.to);
|
|
92
|
+
const peers = members.filter(member => !member.entry && asked.includes(member.id));
|
|
93
|
+
return {
|
|
94
|
+
scene,
|
|
95
|
+
entry,
|
|
96
|
+
peers,
|
|
97
|
+
members: [entry, ...members.filter(member => !member.entry)],
|
|
98
|
+
links,
|
|
99
|
+
positions: scene.stage.positions,
|
|
100
|
+
opensFirst: scene.stage.opensFirst || entry.id,
|
|
101
|
+
assumes: scene.setting.assumes,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** The port the examples serve Accounting on, as `npm run examples` starts it. */
|
|
105
|
+
export const ACCOUNTING_PORT = 8767;
|
|
106
|
+
/** The first port `serve_scenes.py` starts a runtime on for the other members. */
|
|
107
|
+
export const SCENE_PORTS_FROM = 8768;
|
|
108
|
+
/**
|
|
109
|
+
* The members on a runtime across the catalogue's scenes, each once, in
|
|
110
|
+
* the catalogue's order (a scene's cast in its order): what
|
|
111
|
+
* `examples/sales-accounting-a2a/serve_scenes.py` serves, in that order.
|
|
112
|
+
*/
|
|
113
|
+
export function sceneRuntimeMembers(scenes = listSceneSpecs()) {
|
|
114
|
+
const seen = [];
|
|
115
|
+
for (const scene of scenes) {
|
|
116
|
+
for (const member of scene.cast) {
|
|
117
|
+
const id = refId(member.app);
|
|
118
|
+
if (id &&
|
|
119
|
+
(member.runsIn ?? 'runtime') === 'runtime' &&
|
|
120
|
+
!seen.includes(id)) {
|
|
121
|
+
seen.push(id);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return seen;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The port each runtime member is served on locally: Accounting on
|
|
129
|
+
* {@link ACCOUNTING_PORT}, the others from {@link SCENE_PORTS_FROM} on, in
|
|
130
|
+
* the order of {@link sceneRuntimeMembers}. `serve_scenes.py` gives the
|
|
131
|
+
* same ports by the same rule.
|
|
132
|
+
*/
|
|
133
|
+
export function sceneRuntimePorts(scenes = listSceneSpecs()) {
|
|
134
|
+
const ports = {};
|
|
135
|
+
let next = SCENE_PORTS_FROM;
|
|
136
|
+
for (const id of sceneRuntimeMembers(scenes)) {
|
|
137
|
+
if (id === 'accounting') {
|
|
138
|
+
ports[id] = ACCOUNTING_PORT;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
ports[id] = next;
|
|
142
|
+
next += 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return ports;
|
|
146
|
+
}
|
|
147
|
+
/** The variable the examples read a member's local address from: `VITE_A2A_CHANGE_DETECTION_URL`. */
|
|
148
|
+
export function sceneAddressVariable(appId) {
|
|
149
|
+
return `VITE_A2A_${appId.replace(/-/g, '_').toUpperCase()}_URL`;
|
|
150
|
+
}
|
|
151
|
+
/** Where a member is served over A2A on a local runtime at `port`. */
|
|
152
|
+
export function localA2AUrl(appId, port) {
|
|
153
|
+
return `http://127.0.0.1:${port}/api/v1/a2a/agents/${appId}`;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Where a member on a runtime is reached by the examples: its variable
|
|
157
|
+
* (`VITE_A2A_<MEMBER>_URL`) when set in `env`, else the local runtime
|
|
158
|
+
* `serve_scenes.py` serves it on.
|
|
159
|
+
*/
|
|
160
|
+
export function sceneMemberUrl(appId, env, ports = sceneRuntimePorts()) {
|
|
161
|
+
const port = ports[appId];
|
|
162
|
+
const set = env[sceneAddressVariable(appId)];
|
|
163
|
+
if (set) {
|
|
164
|
+
return set;
|
|
165
|
+
}
|
|
166
|
+
if (port === undefined) {
|
|
167
|
+
throw new Error(`${appId} runs on no local runtime the examples serve: set ${sceneAddressVariable(appId)}.`);
|
|
168
|
+
}
|
|
169
|
+
return localA2AUrl(appId, port);
|
|
170
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transcript of a scene (LOOP A-06): what happens in it, as lines, in
|
|
3
|
+
* the order it happened — *Sales: …*, *Sales → Accounting: …*,
|
|
4
|
+
* *Accounting → Odoo: odoo_accounting_aged_balance* — each with its time.
|
|
5
|
+
*
|
|
6
|
+
* One source, read two ways. Live, the lines are read from the OpenTelemetry
|
|
7
|
+
* spans the Agent Inspector reads (core's live tracer): a turn is an
|
|
8
|
+
* `invoke_agent` span (what the person asked, what the agent answered), a
|
|
9
|
+
* tool call an `execute_tool` span (the member and, for an MCP call, the
|
|
10
|
+
* server it reaches: a connection of the member's), and a request to
|
|
11
|
+
* another member an `a2a` span (who asked whom, in what words, and what came
|
|
12
|
+
* back as its events). Once a run is over, the same lines are read from its
|
|
13
|
+
* record (`RecordEntry`: a `turn`, a `tool_call`), the session's entries as
|
|
14
|
+
* ai-agents kept them.
|
|
15
|
+
*
|
|
16
|
+
* Pure: spans or entries in, lines out, so that a test reads a scripted
|
|
17
|
+
* scene and a page draws a live one the same way ({@link SceneTranscript}).
|
|
18
|
+
*
|
|
19
|
+
* @module components/teams/sceneTranscript
|
|
20
|
+
*/
|
|
21
|
+
import type { OtelSpan } from '@datalayer/core/lib/otel/types';
|
|
22
|
+
import type { RecordEntry } from '../../loop/apps/records';
|
|
23
|
+
import { type A2ATeamConnection } from './a2aTeamFlow';
|
|
24
|
+
/** A member of the scene, as the transcript names it. */
|
|
25
|
+
export type SceneTranscriptMember = {
|
|
26
|
+
/** Its id in the team: `accounting`. */
|
|
27
|
+
id: string;
|
|
28
|
+
/** Its name, the service its spans are done by: `Accounting`. */
|
|
29
|
+
name: string;
|
|
30
|
+
/** The MCP servers it reaches: a call to one of their tools is `Member → Server: tool`. */
|
|
31
|
+
connections?: readonly A2ATeamConnection[];
|
|
32
|
+
};
|
|
33
|
+
/** What a line is: words said, a question asked, a tool called. */
|
|
34
|
+
export type SceneTranscriptKind = 'said' | 'asked' | 'called';
|
|
35
|
+
/** One line of the transcript. */
|
|
36
|
+
export type SceneTranscriptLine = {
|
|
37
|
+
/** Stable, for a list: the span's or the entry's id, and which of its lines. */
|
|
38
|
+
key: string;
|
|
39
|
+
/** When, in milliseconds since the epoch. */
|
|
40
|
+
at: number;
|
|
41
|
+
/** Who speaks or acts: a member's name, or the person ({@link PERSON}). */
|
|
42
|
+
from: string;
|
|
43
|
+
/** Who is asked or called: a member, or a server (`Odoo`); none when words are said to all. */
|
|
44
|
+
to?: string;
|
|
45
|
+
kind: SceneTranscriptKind;
|
|
46
|
+
/** The words, or the tool's name. */
|
|
47
|
+
text: string;
|
|
48
|
+
/** Still happening: the call runs, the answer has not come. */
|
|
49
|
+
open?: boolean;
|
|
50
|
+
/** It failed: the span's status, the task's state. */
|
|
51
|
+
failed?: boolean;
|
|
52
|
+
};
|
|
53
|
+
/** The person in the scene, as the transcript names them. */
|
|
54
|
+
export declare const PERSON = "You";
|
|
55
|
+
/**
|
|
56
|
+
* The transcript of a scene from the spans recorded while it works (the
|
|
57
|
+
* Agent Inspector's, one tracer for every member).
|
|
58
|
+
*/
|
|
59
|
+
export declare function transcriptOfSpans(spans: readonly OtelSpan[], members: readonly SceneTranscriptMember[]): SceneTranscriptLine[];
|
|
60
|
+
/**
|
|
61
|
+
* The transcript of one member's run once it is over, from its record: a
|
|
62
|
+
* `turn` is what the person asked and what it answered, a `tool_call` what
|
|
63
|
+
* it called in between. The calls of a turn are recorded before the turn
|
|
64
|
+
* itself (at its close), so the question is placed where the turn began.
|
|
65
|
+
*/
|
|
66
|
+
export declare function transcriptOfRecord(entries: readonly RecordEntry[], member: SceneTranscriptMember): SceneTranscriptLine[];
|
|
67
|
+
/** A line's time, as a clock reads it: `14:03:27`. */
|
|
68
|
+
export declare function timeText(at: number): string;
|
|
69
|
+
/** Who a line is from and to: `Sales`, `Sales → Accounting`, `Accounting → Odoo`. */
|
|
70
|
+
export declare function lineHeading(line: Pick<SceneTranscriptLine, 'from' | 'to' | 'kind'>): string;
|
|
71
|
+
/** A line in words: `Sales → Accounting: Which invoices are open?`. */
|
|
72
|
+
export declare function lineText(line: SceneTranscriptLine): string;
|
|
73
|
+
/** The transcript as text, one line each with its time: what *Copy as text* copies. */
|
|
74
|
+
export declare function transcriptText(lines: readonly SceneTranscriptLine[]): string;
|