@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,294 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { connectionOfTool } from './a2aTeamFlow';
|
|
6
|
+
/** The person in the scene, as the transcript names them. */
|
|
7
|
+
export const PERSON = 'You';
|
|
8
|
+
/** What a tool that is no connection's reaches, by where it comes from. */
|
|
9
|
+
const TOOL_HOMES = {
|
|
10
|
+
frontend: 'the page',
|
|
11
|
+
skill: 'a skill',
|
|
12
|
+
codemode: 'codemode',
|
|
13
|
+
runtime: 'its runtime',
|
|
14
|
+
};
|
|
15
|
+
const text = (value) => typeof value === 'string' ? value : '';
|
|
16
|
+
/** The text of the first message of a GenAI messages attribute (JSON). */
|
|
17
|
+
function messageText(value) {
|
|
18
|
+
if (typeof value !== 'string') {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(value);
|
|
23
|
+
return (parsed[0]?.parts ?? [])
|
|
24
|
+
.map(part => (typeof part.content === 'string' ? part.content : ''))
|
|
25
|
+
.join('')
|
|
26
|
+
.trim();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return '';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** A time as the span writes it, in milliseconds. */
|
|
33
|
+
function millis(iso, fallback = 0) {
|
|
34
|
+
const parsed = iso ? Date.parse(iso) : Number.NaN;
|
|
35
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
36
|
+
}
|
|
37
|
+
/** The member a service name is: by name, then by id; else the name itself. */
|
|
38
|
+
function memberNamed(service, members) {
|
|
39
|
+
return (members.find(member => member.name === service) ??
|
|
40
|
+
members.find(member => member.id === service) ?? {
|
|
41
|
+
id: service,
|
|
42
|
+
name: service,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** Where a tool call goes: the connection it is of (`Odoo`), else its home by kind. */
|
|
46
|
+
function calledOf(tool, kind, member) {
|
|
47
|
+
const connection = connectionOfTool(tool, [...(member.connections ?? [])]);
|
|
48
|
+
return connection?.label ?? TOOL_HOMES[kind] ?? TOOL_HOMES.runtime;
|
|
49
|
+
}
|
|
50
|
+
const isA2A = (span, attributes) => attributes['rpc.service'] === 'a2a' || span.span_name.startsWith('a2a ');
|
|
51
|
+
/** The lines in the order they happened: by time, a tie kept as given. */
|
|
52
|
+
function inOrder(lines) {
|
|
53
|
+
return lines
|
|
54
|
+
.map((line, index) => ({ line, index }))
|
|
55
|
+
.sort((a, b) => a.line.at - b.line.at || a.index - b.index)
|
|
56
|
+
.map(({ line }) => line);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The same thing said twice is said once: a member's answer comes back as
|
|
60
|
+
* the A2A request's events and, on a runtime whose own spans are read, as
|
|
61
|
+
* its `invoke_agent` span too. The first one stands.
|
|
62
|
+
*/
|
|
63
|
+
function onceEach(lines) {
|
|
64
|
+
const seen = new Set();
|
|
65
|
+
return lines.filter(line => {
|
|
66
|
+
// Words said are the same words whoever they were said to.
|
|
67
|
+
const said = `${line.kind}\u0000${line.from}\u0000${line.kind === 'said' ? '' : (line.to ?? '')}\u0000${line.text}`;
|
|
68
|
+
if (seen.has(said)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
seen.add(said);
|
|
72
|
+
return true;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The transcript of a scene from the spans recorded while it works (the
|
|
77
|
+
* Agent Inspector's, one tracer for every member).
|
|
78
|
+
*/
|
|
79
|
+
export function transcriptOfSpans(spans, members) {
|
|
80
|
+
const lines = [];
|
|
81
|
+
// A tool call under which an A2A request sits is the asking itself: the
|
|
82
|
+
// request says it, with its words.
|
|
83
|
+
const asking = new Set();
|
|
84
|
+
for (const span of spans) {
|
|
85
|
+
if (span.parent_span_id && isA2A(span, span.attributes ?? {})) {
|
|
86
|
+
asking.add(span.parent_span_id);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
for (const span of spans) {
|
|
90
|
+
const attributes = span.attributes ?? {};
|
|
91
|
+
const start = millis(span.start_time);
|
|
92
|
+
const end = millis(span.end_time, start);
|
|
93
|
+
const operation = attributes['gen_ai.operation.name'];
|
|
94
|
+
const failed = span.status_code === 'ERROR';
|
|
95
|
+
if (operation === 'invoke_agent') {
|
|
96
|
+
const agent = memberNamed(text(attributes['gen_ai.agent.name']) || span.service_name, members);
|
|
97
|
+
const asked = messageText(attributes['gen_ai.input.messages']);
|
|
98
|
+
// The person's question, at the top of a turn; a turn asked by another
|
|
99
|
+
// member is told by its A2A request.
|
|
100
|
+
if (asked && !span.parent_span_id) {
|
|
101
|
+
lines.push({
|
|
102
|
+
key: `${span.span_id}:asked`,
|
|
103
|
+
at: start,
|
|
104
|
+
from: PERSON,
|
|
105
|
+
to: agent.name,
|
|
106
|
+
kind: 'asked',
|
|
107
|
+
text: asked,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const said = messageText(attributes['gen_ai.output.messages']);
|
|
111
|
+
if (said) {
|
|
112
|
+
lines.push({
|
|
113
|
+
key: `${span.span_id}:said`,
|
|
114
|
+
at: end,
|
|
115
|
+
from: agent.name,
|
|
116
|
+
kind: 'said',
|
|
117
|
+
text: said,
|
|
118
|
+
...(failed ? { failed } : {}),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else if (failed) {
|
|
122
|
+
lines.push({
|
|
123
|
+
key: `${span.span_id}:said`,
|
|
124
|
+
at: end,
|
|
125
|
+
from: agent.name,
|
|
126
|
+
kind: 'said',
|
|
127
|
+
text: `could not answer${span.status_message ? `: ${span.status_message}` : ''}`,
|
|
128
|
+
failed,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (operation === 'execute_tool') {
|
|
134
|
+
if (asking.has(span.span_id)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const agent = memberNamed(text(attributes['gen_ai.agent.name']) || span.service_name, members);
|
|
138
|
+
const tool = text(attributes['gen_ai.tool.name']) || span.span_name;
|
|
139
|
+
lines.push({
|
|
140
|
+
key: `${span.span_id}:called`,
|
|
141
|
+
at: start,
|
|
142
|
+
from: agent.name,
|
|
143
|
+
to: calledOf(tool, text(attributes['datalayer.tool.kind']), agent),
|
|
144
|
+
kind: 'called',
|
|
145
|
+
text: tool,
|
|
146
|
+
...(span.in_progress ? { open: true } : {}),
|
|
147
|
+
...(failed ? { failed } : {}),
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (isA2A(span, attributes)) {
|
|
152
|
+
const request = text(attributes['a2a.message.text']).trim();
|
|
153
|
+
if (!request) {
|
|
154
|
+
// The agent card, a task read: nothing said.
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const asker = memberNamed(span.service_name, members);
|
|
158
|
+
const peer = memberNamed(text(attributes['peer.service']), members);
|
|
159
|
+
lines.push({
|
|
160
|
+
key: `${span.span_id}:asked`,
|
|
161
|
+
at: start,
|
|
162
|
+
from: asker.name,
|
|
163
|
+
to: peer.name,
|
|
164
|
+
kind: 'asked',
|
|
165
|
+
text: request,
|
|
166
|
+
...(span.in_progress ? { open: true } : {}),
|
|
167
|
+
});
|
|
168
|
+
// What came back: the answer, or the last words of its events.
|
|
169
|
+
let answer = text(attributes['a2a.answer.text']).trim();
|
|
170
|
+
let answeredAt = end;
|
|
171
|
+
if (!answer) {
|
|
172
|
+
for (const event of span.events ?? []) {
|
|
173
|
+
if (event.name === 'a2a.message' ||
|
|
174
|
+
event.name === 'a2a.artifact_update') {
|
|
175
|
+
const words = text(event.attributes?.['a2a.message.text']).trim();
|
|
176
|
+
if (words) {
|
|
177
|
+
answer = words;
|
|
178
|
+
answeredAt = millis(event.timestamp, end);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (answer) {
|
|
184
|
+
lines.push({
|
|
185
|
+
key: `${span.span_id}:said`,
|
|
186
|
+
at: answeredAt,
|
|
187
|
+
from: peer.name,
|
|
188
|
+
to: asker.name,
|
|
189
|
+
kind: 'said',
|
|
190
|
+
text: answer,
|
|
191
|
+
...(failed ? { failed } : {}),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
else if (failed) {
|
|
195
|
+
lines.push({
|
|
196
|
+
key: `${span.span_id}:said`,
|
|
197
|
+
at: end,
|
|
198
|
+
from: peer.name,
|
|
199
|
+
to: asker.name,
|
|
200
|
+
kind: 'said',
|
|
201
|
+
text: `could not answer${span.status_message ? `: ${span.status_message}` : ''}`,
|
|
202
|
+
failed,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return onceEach(inOrder(lines));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The transcript of one member's run once it is over, from its record: a
|
|
211
|
+
* `turn` is what the person asked and what it answered, a `tool_call` what
|
|
212
|
+
* it called in between. The calls of a turn are recorded before the turn
|
|
213
|
+
* itself (at its close), so the question is placed where the turn began.
|
|
214
|
+
*/
|
|
215
|
+
export function transcriptOfRecord(entries, member) {
|
|
216
|
+
const ordered = [...entries].sort((a, b) => millis(a.createdAt) - millis(b.createdAt) || a.version - b.version);
|
|
217
|
+
const lines = [];
|
|
218
|
+
let pending = [];
|
|
219
|
+
let answered = false;
|
|
220
|
+
for (const entry of ordered) {
|
|
221
|
+
const at = millis(entry.createdAt);
|
|
222
|
+
if (entry.kind === 'tool_call') {
|
|
223
|
+
const tool = text(entry.payload.tool) || entry.summary;
|
|
224
|
+
pending.push({
|
|
225
|
+
key: `${entry.uid}:called`,
|
|
226
|
+
at,
|
|
227
|
+
from: member.name,
|
|
228
|
+
to: calledOf(tool, '', member),
|
|
229
|
+
kind: 'called',
|
|
230
|
+
text: tool,
|
|
231
|
+
...(entry.payload.error ? { failed: true } : {}),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
else if (entry.kind === 'turn') {
|
|
235
|
+
const asked = text(entry.payload.asked) || entry.summary;
|
|
236
|
+
const said = text(entry.payload.answered);
|
|
237
|
+
lines.push({
|
|
238
|
+
key: `${entry.uid}:asked`,
|
|
239
|
+
at: pending[0]?.at ?? at,
|
|
240
|
+
from: PERSON,
|
|
241
|
+
to: member.name,
|
|
242
|
+
kind: 'asked',
|
|
243
|
+
text: asked,
|
|
244
|
+
});
|
|
245
|
+
lines.push(...pending);
|
|
246
|
+
pending = [];
|
|
247
|
+
if (said) {
|
|
248
|
+
lines.push({
|
|
249
|
+
key: `${entry.uid}:said`,
|
|
250
|
+
at,
|
|
251
|
+
from: member.name,
|
|
252
|
+
kind: 'said',
|
|
253
|
+
text: said,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
answered = true;
|
|
257
|
+
}
|
|
258
|
+
else if (entry.kind === 'output' && !answered) {
|
|
259
|
+
// A record kept without its conversations: the answer's summary.
|
|
260
|
+
pending.push({
|
|
261
|
+
key: `${entry.uid}:said`,
|
|
262
|
+
at,
|
|
263
|
+
from: member.name,
|
|
264
|
+
kind: 'said',
|
|
265
|
+
text: entry.summary,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
lines.push(...pending);
|
|
270
|
+
return lines;
|
|
271
|
+
}
|
|
272
|
+
/** A line's time, as a clock reads it: `14:03:27`. */
|
|
273
|
+
export function timeText(at) {
|
|
274
|
+
const date = new Date(at);
|
|
275
|
+
const two = (value) => String(value).padStart(2, '0');
|
|
276
|
+
return `${two(date.getHours())}:${two(date.getMinutes())}:${two(date.getSeconds())}`;
|
|
277
|
+
}
|
|
278
|
+
/** Who a line is from and to: `Sales`, `Sales → Accounting`, `Accounting → Odoo`. */
|
|
279
|
+
export function lineHeading(line) {
|
|
280
|
+
return line.to && line.kind !== 'said'
|
|
281
|
+
? `${line.from} → ${line.to}`
|
|
282
|
+
: line.from;
|
|
283
|
+
}
|
|
284
|
+
/** A line in words: `Sales → Accounting: Which invoices are open?`. */
|
|
285
|
+
export function lineText(line) {
|
|
286
|
+
const tail = line.open ? '…' : line.failed ? ' (failed)' : '';
|
|
287
|
+
return `${lineHeading(line)}: ${line.text}${tail}`;
|
|
288
|
+
}
|
|
289
|
+
/** The transcript as text, one line each with its time: what *Copy as text* copies. */
|
|
290
|
+
export function transcriptText(lines) {
|
|
291
|
+
return lines
|
|
292
|
+
.map(line => `${timeText(line.at)} ${lineText(line)}`)
|
|
293
|
+
.join('\n');
|
|
294
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AppSpec } from '../../types/agentspecs';
|
|
2
2
|
import { type A2ATeamConnection } from './a2aTeamFlow';
|
|
3
|
-
import type { ToolClass } from '../inspector/
|
|
3
|
+
import type { ToolClass } from '../inspector/agentSpans';
|
|
4
4
|
/**
|
|
5
5
|
* The connections of an application, each with its server's name and mark
|
|
6
6
|
* and its tools. A connection to a server the catalogue does not have is
|
|
@@ -8,7 +8,7 @@ import type { ToolClass } from '../inspector/agentInspector';
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function teamConnectionsOf(app: Pick<AppSpec, 'connections'>): A2ATeamConnection[];
|
|
10
10
|
/**
|
|
11
|
-
* Where a member's tool call comes from, for
|
|
11
|
+
* Where a member's tool call comes from, for its span: a call to
|
|
12
12
|
* one of its connections is an MCP call, with the server's mark; any other
|
|
13
13
|
* is `fallback`'s (`classifyToolCall`, by the catalogues) or a tool its
|
|
14
14
|
* runtime runs (`write_notebook`).
|
|
@@ -49,7 +49,7 @@ export function teamConnectionsOf(app) {
|
|
|
49
49
|
return connections;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
* Where a member's tool call comes from, for
|
|
52
|
+
* Where a member's tool call comes from, for its span: a call to
|
|
53
53
|
* one of its connections is an MCP call, with the server's mark; any other
|
|
54
54
|
* is `fallback`'s (`classifyToolCall`, by the catalogues) or a tool its
|
|
55
55
|
* runtime runs (`write_notebook`).
|
|
@@ -59,11 +59,11 @@ export function teamToolClassifier(connections, fallback) {
|
|
|
59
59
|
const connection = connectionOfTool(name, connections);
|
|
60
60
|
if (connection) {
|
|
61
61
|
return {
|
|
62
|
-
|
|
62
|
+
kind: 'mcp',
|
|
63
63
|
...(connection.icon ? { icon: connection.icon } : {}),
|
|
64
64
|
...(connection.emoji ? { emoji: connection.emoji } : {}),
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
-
return fallback?.(name, args) ?? {
|
|
67
|
+
return fallback?.(name, args) ?? { kind: 'runtime' };
|
|
68
68
|
};
|
|
69
69
|
}
|
|
@@ -3,7 +3,7 @@ import type { DisplayItem } from '../../types/chat';
|
|
|
3
3
|
import { type BrowserModelOptions } from '../../runtimes/browser/model';
|
|
4
4
|
import { type A2APeer, type A2APeerArtifact } from '../../runtimes/browser/a2aPeer';
|
|
5
5
|
import type { AppSpec } from '../../types/agentspecs';
|
|
6
|
-
import type {
|
|
6
|
+
import type { OtelLiveTracer } from '@datalayer/core/lib/otel/live';
|
|
7
7
|
import { type A2ATeamCall, type A2ATeamConnection, type A2ATeamFlow } from './a2aTeamFlow';
|
|
8
8
|
import { type BalloonToolLine } from '../../chat/assistant/toolLine';
|
|
9
9
|
/** What one member's character does: its state, its balloon, and whether it was sent away. */
|
|
@@ -38,36 +38,104 @@ export declare function answeredLine(artifacts: A2APeerArtifact[]): string;
|
|
|
38
38
|
export declare function notebookAmong(artifacts: A2APeerArtifact[]): A2APeerArtifact | null;
|
|
39
39
|
/** A line short enough for a balloon. */
|
|
40
40
|
export declare function balloonLine(text: string, length?: number): string;
|
|
41
|
+
/** The name of the entry's tool for a peer, as its instructions call it: `ask_<peer id>`. */
|
|
42
|
+
export declare function askToolOf(peerId: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* What a member says when it is asked, before it works: the books when it
|
|
45
|
+
* reaches Odoo, the system it reaches otherwise, or just that it is on it.
|
|
46
|
+
*/
|
|
47
|
+
export declare function askedLine(connections: readonly A2ATeamConnection[]): string;
|
|
48
|
+
/**
|
|
49
|
+
* The entry's instructions for a team of more than one peer: its own, and
|
|
50
|
+
* which tool asks whom, so that it names each. A team of two keeps the
|
|
51
|
+
* entry's instructions as they are.
|
|
52
|
+
*/
|
|
53
|
+
export declare function teamInstructions(entry: Pick<AppSpec, 'instructions'>, peers: readonly {
|
|
54
|
+
app: Pick<AppSpec, 'name'>;
|
|
55
|
+
askTool: string;
|
|
56
|
+
}[]): string;
|
|
57
|
+
/** A member the entry asks over A2A. */
|
|
58
|
+
export type A2ATeamPeerOptions = {
|
|
59
|
+
/** The member asked. */
|
|
60
|
+
app: AppSpec;
|
|
61
|
+
/** The peer, once its card is read; `null` until then. */
|
|
62
|
+
peer: A2APeer | null;
|
|
63
|
+
/** The name of the entry's tool for it; `ask_<its id>` unsaid. */
|
|
64
|
+
askTool?: string;
|
|
65
|
+
/** Its connections (`teamConnectionsOf(app)`): the calls to them are kept. */
|
|
66
|
+
connections?: A2ATeamConnection[];
|
|
67
|
+
/**
|
|
68
|
+
* What its balloon says before it is asked (a scene's `opens_first`, its
|
|
69
|
+
* persona's line); unsaid, it waits quietly.
|
|
70
|
+
*/
|
|
71
|
+
greeting?: string;
|
|
72
|
+
};
|
|
41
73
|
export type UseA2ATeamOptions = {
|
|
42
74
|
/** The member that talks to the person and asks: it runs in this page. */
|
|
43
75
|
entry: AppSpec;
|
|
44
|
-
/**
|
|
45
|
-
peerApp
|
|
46
|
-
/**
|
|
47
|
-
peer
|
|
76
|
+
/** A team of two: the member it asks over A2A. */
|
|
77
|
+
peerApp?: AppSpec;
|
|
78
|
+
/** A team of two: the peer, once its card is read; `null` until then. */
|
|
79
|
+
peer?: A2APeer | null;
|
|
80
|
+
/** A team of two: the name of the entry's tool, as its instructions call it: `ask_<peer id>`. */
|
|
81
|
+
askTool?: string;
|
|
82
|
+
/** A team of two: the peer's connections (`teamConnectionsOf(peerApp)`). */
|
|
83
|
+
peerConnections?: A2ATeamConnection[];
|
|
84
|
+
/**
|
|
85
|
+
* A team of N: the members the entry asks, in the order its tools are
|
|
86
|
+
* offered. Give the same array while nothing changed (memoised): a new
|
|
87
|
+
* one makes the entry's agent again.
|
|
88
|
+
*/
|
|
89
|
+
peers?: readonly A2ATeamPeerOptions[];
|
|
90
|
+
/**
|
|
91
|
+
* The entry itself, when it runs on a runtime and is asked over A2A from
|
|
92
|
+
* the page: no agent turns in the browser, `send` asks it directly, and
|
|
93
|
+
* its persona follows what it tells (asked, working, its tools, answered).
|
|
94
|
+
* `null` until its card is read.
|
|
95
|
+
*/
|
|
96
|
+
entryPeer?: A2APeer | null;
|
|
48
97
|
/** Where the entry's model is asked, and with whose token. */
|
|
49
98
|
inference: Omit<BrowserModelOptions, 'model'>;
|
|
50
|
-
/** The name of the entry's tool, as its instructions call it: `ask_<peer id>`. */
|
|
51
|
-
askTool?: string;
|
|
52
99
|
/** The most steps of one turn. */
|
|
53
100
|
maxSteps?: number;
|
|
54
|
-
/** The peer's connections (`teamConnectionsOf(peerApp)`): the calls to them are kept. */
|
|
55
|
-
peerConnections?: A2ATeamConnection[];
|
|
56
101
|
/**
|
|
57
|
-
* The media types the page accepts from the
|
|
102
|
+
* The media types the page accepts from the peers (`acceptedOutputModes`):
|
|
58
103
|
* words, and the formats it can show, such as a notebook. Unsaid, words alone.
|
|
59
104
|
*/
|
|
60
105
|
accept?: readonly string[];
|
|
61
106
|
/**
|
|
62
|
-
* The Agent Inspector's
|
|
63
|
-
* are recorded there
|
|
107
|
+
* The Agent Inspector's tracer: the entry's turns and its calls to the
|
|
108
|
+
* peers are recorded there as spans.
|
|
64
109
|
*/
|
|
65
|
-
inspector?:
|
|
110
|
+
inspector?: OtelLiveTracer | null;
|
|
111
|
+
};
|
|
112
|
+
/** One peer of the team, as the team keeps it. */
|
|
113
|
+
export type A2ATeamPeer = {
|
|
114
|
+
/** Its id: its application's. */
|
|
115
|
+
id: string;
|
|
116
|
+
app: AppSpec;
|
|
117
|
+
/** The name of the entry's tool for it. */
|
|
118
|
+
askTool: string;
|
|
119
|
+
connections: A2ATeamConnection[];
|
|
120
|
+
/** Whether it is reached: its card was read. */
|
|
121
|
+
connected: boolean;
|
|
122
|
+
persona: A2ATeamPersona;
|
|
123
|
+
/** What it was asked, the tools it called and what it answered. */
|
|
124
|
+
history: DisplayItem[];
|
|
125
|
+
/** Its last answer, as it gave it. */
|
|
126
|
+
report: string | null;
|
|
127
|
+
/** What it gave besides words with its last answer. */
|
|
128
|
+
artifacts: A2APeerArtifact[];
|
|
129
|
+
/** Which way its link carries a message now. */
|
|
130
|
+
flow: A2ATeamFlow;
|
|
131
|
+
setAway: (away: boolean) => void;
|
|
66
132
|
};
|
|
67
133
|
export type A2ATeam = {
|
|
68
134
|
entryPersona: A2ATeamPersona;
|
|
135
|
+
/** The first peer's persona: the peer of a team of two. */
|
|
69
136
|
peerPersona: A2ATeamPersona;
|
|
70
137
|
setEntryAway: (away: boolean) => void;
|
|
138
|
+
/** Sends the first peer away, or calls it back. */
|
|
71
139
|
setPeerAway: (away: boolean) => void;
|
|
72
140
|
turns: A2ATeamTurn[];
|
|
73
141
|
/**
|
|
@@ -76,26 +144,30 @@ export type A2ATeam = {
|
|
|
76
144
|
*/
|
|
77
145
|
entryHistory: DisplayItem[];
|
|
78
146
|
/**
|
|
79
|
-
* What the peer was asked, the tools it called and what it
|
|
80
|
-
* the chat's model (messages and tool calls).
|
|
147
|
+
* What the first peer was asked, the tools it called and what it
|
|
148
|
+
* answered, in the chat's model (messages and tool calls).
|
|
81
149
|
*/
|
|
82
150
|
peerHistory: DisplayItem[];
|
|
83
|
-
/** The
|
|
151
|
+
/** The peers, in their order, each as the team keeps it. */
|
|
152
|
+
peers: A2ATeamPeer[];
|
|
153
|
+
/** Which way each peer's link carries a message now, by the peer's id. */
|
|
154
|
+
flows: Record<string, A2ATeamFlow>;
|
|
155
|
+
/** The last answer a peer gave, as it gave it. */
|
|
84
156
|
report: string | null;
|
|
85
|
-
/** What
|
|
157
|
+
/** What a peer gave besides words with the last answer, by media type. */
|
|
86
158
|
artifacts: A2APeerArtifact[];
|
|
87
|
-
/** The latest notebook
|
|
159
|
+
/** The latest notebook a peer gave, kept until another one comes. */
|
|
88
160
|
notebook: A2APeerArtifact | null;
|
|
89
|
-
/** Which way the link carries a message now. */
|
|
161
|
+
/** Which way the first peer's link carries a message now. */
|
|
90
162
|
flow: A2ATeamFlow;
|
|
91
|
-
/** The tool calls the
|
|
163
|
+
/** The tool calls the peers' connections are answering now. */
|
|
92
164
|
calls: A2ATeamCall[];
|
|
93
165
|
/** Whether a turn is under way. */
|
|
94
166
|
busy: boolean;
|
|
95
|
-
/** Whether the entry can be asked:
|
|
167
|
+
/** Whether the entry can be asked: every peer is connected, or the entry is. */
|
|
96
168
|
ready: boolean;
|
|
97
169
|
send: (text: string) => Promise<void>;
|
|
98
170
|
stop: () => void;
|
|
99
171
|
};
|
|
100
|
-
/** Run a team
|
|
172
|
+
/** Run a team over A2A in the page. */
|
|
101
173
|
export declare function useA2ATeam(options: UseA2ATeamOptions): A2ATeam;
|