@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.
Files changed (168) hide show
  1. package/lib/chat/ChatFloating.d.ts +5 -4
  2. package/lib/chat/ChatFloating.js +8 -6
  3. package/lib/chat/ChatLanguage.d.ts +17 -0
  4. package/lib/chat/ChatLanguage.js +29 -0
  5. package/lib/chat/assistant/AssistantStage.d.ts +4 -4
  6. package/lib/chat/assistant/AssistantStage.js +4 -2
  7. package/lib/chat/assistant/BalloonParts.d.ts +2 -1
  8. package/lib/chat/assistant/BalloonParts.js +12 -5
  9. package/lib/chat/assistant/BalloonVisual.js +3 -1
  10. package/lib/chat/assistant/ConversationBalloon.d.ts +1 -1
  11. package/lib/chat/assistant/ConversationBalloon.js +3 -2
  12. package/lib/chat/assistant/SpeechBalloon.d.ts +1 -1
  13. package/lib/chat/assistant/SpeechBalloon.js +10 -7
  14. package/lib/chat/assistant/toolLine.d.ts +8 -4
  15. package/lib/chat/assistant/toolLine.js +13 -9
  16. package/lib/chat/base/ChatBase.js +30 -11
  17. package/lib/chat/base/loopElement.d.ts +53 -0
  18. package/lib/chat/base/loopElement.js +78 -0
  19. package/lib/chat/base/loopWindow.d.ts +31 -0
  20. package/lib/chat/base/loopWindow.js +74 -0
  21. package/lib/chat/display/EmptyState.js +3 -1
  22. package/lib/chat/index.d.ts +2 -0
  23. package/lib/chat/index.js +3 -0
  24. package/lib/chat/messages/ChatMessages.js +3 -1
  25. package/lib/chat/messages/TurnFooter.js +3 -1
  26. package/lib/chat/prompt/InputPrompt.js +3 -1
  27. package/lib/chat/prompt/InputPromptLexical.js +3 -1
  28. package/lib/chat/prompt/footer/InPromptFooter.js +3 -1
  29. package/lib/chat/tools/ToolApprovalBanner.js +5 -3
  30. package/lib/chat/tools/ToolApprovalDialog.js +5 -3
  31. package/lib/chat/tools/ToolCallDisplay.js +29 -25
  32. package/lib/chat/words.d.ts +102 -0
  33. package/lib/chat/words.js +460 -0
  34. package/lib/components/a2ui/custom/index.d.ts +39 -0
  35. package/lib/components/a2ui/custom/index.js +95 -0
  36. package/lib/components/a2ui/custom/sandbox.d.ts +73 -0
  37. package/lib/components/a2ui/custom/sandbox.js +188 -0
  38. package/lib/components/a2ui/datalayer/FileUpload.d.ts +10 -2
  39. package/lib/components/a2ui/datalayer/FileUpload.js +10 -7
  40. package/lib/components/a2ui/datalayer/Form.d.ts +2 -0
  41. package/lib/components/a2ui/datalayer/Form.js +9 -2
  42. package/lib/components/a2ui/datalayer/formWidgets.d.ts +19 -0
  43. package/lib/components/a2ui/datalayer/formWidgets.js +51 -0
  44. package/lib/components/a2ui/datalayer/index.d.ts +1 -1
  45. package/lib/components/a2ui/datalayer/index.js +1 -1
  46. package/lib/components/a2ui/datalayer/schema.d.ts +10 -0
  47. package/lib/components/a2ui/datalayer/schema.js +12 -1
  48. package/lib/components/a2ui/index.d.ts +1 -0
  49. package/lib/components/a2ui/index.js +1 -0
  50. package/lib/components/a2ui/visibleWhen.d.ts +12 -1
  51. package/lib/components/a2ui/visibleWhen.js +24 -3
  52. package/lib/components/inspector/AgentInspector.d.ts +25 -44
  53. package/lib/components/inspector/AgentInspector.js +27 -215
  54. package/lib/components/inspector/AgentInspectorDialog.d.ts +6 -6
  55. package/lib/components/inspector/AgentInspectorDialog.js +2 -2
  56. package/lib/components/inspector/a2aSpans.d.ts +51 -0
  57. package/lib/components/inspector/a2aSpans.js +373 -0
  58. package/lib/components/inspector/agentSpans.d.ts +120 -0
  59. package/lib/components/inspector/agentSpans.js +253 -0
  60. package/lib/components/inspector/chatSpans.d.ts +36 -0
  61. package/lib/components/inspector/chatSpans.js +50 -0
  62. package/lib/components/inspector/classify.d.ts +11 -8
  63. package/lib/components/inspector/classify.js +23 -24
  64. package/lib/components/inspector/index.d.ts +7 -7
  65. package/lib/components/inspector/index.js +7 -7
  66. package/lib/components/teams/A2ATeamGraph.d.ts +4 -4
  67. package/lib/components/teams/SceneTranscript.d.ts +29 -0
  68. package/lib/components/teams/SceneTranscript.js +54 -0
  69. package/lib/components/teams/SceneView.d.ts +41 -0
  70. package/lib/components/teams/SceneView.js +28 -0
  71. package/lib/components/teams/index.d.ts +3 -0
  72. package/lib/components/teams/index.js +3 -0
  73. package/lib/components/teams/sceneTranscript.d.ts +74 -0
  74. package/lib/components/teams/sceneTranscript.js +294 -0
  75. package/lib/components/teams/teamConnections.d.ts +2 -2
  76. package/lib/components/teams/teamConnections.js +3 -3
  77. package/lib/components/teams/useA2ATeam.d.ts +4 -4
  78. package/lib/components/teams/useA2ATeam.js +50 -46
  79. package/lib/examples/AgentA2ATeamExample.d.ts +10 -9
  80. package/lib/examples/AgentA2ATeamExample.js +70 -60
  81. package/lib/examples/AssistantExample.js +2 -2
  82. package/lib/loop/apps/AppComposer.d.ts +40 -4
  83. package/lib/loop/apps/AppComposer.js +194 -19
  84. package/lib/loop/apps/AppRenderer.d.ts +21 -3
  85. package/lib/loop/apps/AppRenderer.js +88 -48
  86. package/lib/loop/apps/appspec.d.ts +13 -1
  87. package/lib/loop/apps/appspec.js +305 -12
  88. package/lib/loop/apps/checks.d.ts +14 -0
  89. package/lib/loop/apps/checks.js +276 -5
  90. package/lib/loop/apps/composer.d.ts +21 -10
  91. package/lib/loop/apps/composer.js +41 -8
  92. package/lib/loop/apps/customComponents.d.ts +41 -0
  93. package/lib/loop/apps/customComponents.js +260 -0
  94. package/lib/loop/apps/hostTools.d.ts +8 -1
  95. package/lib/loop/apps/hostTools.js +9 -0
  96. package/lib/loop/apps/index.d.ts +1 -0
  97. package/lib/loop/apps/index.js +1 -0
  98. package/lib/loop/apps/language.d.ts +68 -0
  99. package/lib/loop/apps/language.js +275 -0
  100. package/lib/loop/apps/settingsInputs.d.ts +29 -0
  101. package/lib/loop/apps/settingsInputs.js +147 -0
  102. package/lib/loop/apps/uploads.d.ts +43 -0
  103. package/lib/loop/apps/uploads.js +66 -0
  104. package/lib/loop/core/a2uiComponents.d.ts +10 -0
  105. package/lib/loop/core/a2uiComponents.js +15 -0
  106. package/lib/loop/core/index.d.ts +15 -1
  107. package/lib/loop/core/index.js +3 -0
  108. package/lib/loop/embed/AppEmbed.d.ts +25 -3
  109. package/lib/loop/embed/AppEmbed.js +106 -18
  110. package/lib/loop/embed/element.d.ts +5 -0
  111. package/lib/loop/embed/element.js +84 -5
  112. package/lib/loop/embed/embedConfig.d.ts +8 -0
  113. package/lib/loop/embed/embedConfig.js +18 -0
  114. package/lib/loop/embed/embedSession.d.ts +14 -0
  115. package/lib/loop/embed/embedSession.js +34 -0
  116. package/lib/loop/embed/hostBridge.d.ts +50 -3
  117. package/lib/loop/embed/hostBridge.js +22 -2
  118. package/lib/loop/index.d.ts +4 -3
  119. package/lib/loop/index.js +4 -3
  120. package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +7 -1
  121. package/lib/loop/plugins/a2ui-surface/InlineSurface.js +7 -2
  122. package/lib/loop/plugins/a2ui-surface/SurfaceToolResult.js +4 -1
  123. package/lib/loop/plugins/a2ui-surface/ownComponents.d.ts +6 -0
  124. package/lib/loop/plugins/a2ui-surface/ownComponents.js +30 -0
  125. package/lib/loop/plugins/app-components/index.d.ts +26 -0
  126. package/lib/loop/plugins/app-components/index.js +56 -0
  127. package/lib/loop/plugins/app-elements/AppElements.d.ts +27 -0
  128. package/lib/loop/plugins/app-elements/AppElements.js +79 -0
  129. package/lib/loop/plugins/app-elements/index.d.ts +20 -0
  130. package/lib/loop/plugins/app-elements/index.js +43 -0
  131. package/lib/loop/plugins/app-page/AppPage.d.ts +9 -0
  132. package/lib/loop/plugins/app-page/AppPage.js +106 -8
  133. package/lib/loop/plugins/app-page/appPageModel.d.ts +17 -1
  134. package/lib/loop/plugins/app-page/appPageModel.js +122 -4
  135. package/lib/loop/plugins/app-page/index.d.ts +1 -0
  136. package/lib/loop/plugins/app-page/index.js +1 -0
  137. package/lib/loop/plugins/app-page/pageRun.d.ts +46 -0
  138. package/lib/loop/plugins/app-page/pageRun.js +111 -0
  139. package/lib/loop/plugins/assistant/index.js +8 -6
  140. package/lib/loop/plugins/chat/ChatView.js +16 -6
  141. package/lib/loop/plugins/chat/index.d.ts +1 -1
  142. package/lib/loop/plugins/chat/index.js +2 -1
  143. package/lib/models/PageTag.d.ts +1 -1
  144. package/lib/protocols/BrowserAgentAdapter.d.ts +8 -6
  145. package/lib/protocols/BrowserAgentAdapter.js +13 -10
  146. package/lib/runtimes/browser/a2aPeer.d.ts +2 -1
  147. package/lib/runtimes/browser/protocol.d.ts +1 -1
  148. package/lib/specs/actions.js +78 -1
  149. package/lib/specs/apps.d.ts +5 -0
  150. package/lib/specs/apps.js +1518 -216
  151. package/lib/specs/appspecSchema.js +630 -3
  152. package/lib/specs/teams/teams.d.ts +3 -0
  153. package/lib/specs/teams/teams.js +249 -0
  154. package/lib/specs/uiPlugins.js +6 -1
  155. package/lib/types/agentspecs.d.ts +200 -1
  156. package/lib/types/chat.d.ts +6 -5
  157. package/lib/types/teams.d.ts +13 -2
  158. package/package.json +2 -2
  159. package/scripts/codegen/generate_apps.py +20 -2
  160. package/scripts/codegen/generate_teams.py +15 -2
  161. package/lib/components/inspector/JsonTree.d.ts +0 -24
  162. package/lib/components/inspector/JsonTree.js +0 -89
  163. package/lib/components/inspector/a2aInspect.d.ts +0 -56
  164. package/lib/components/inspector/a2aInspect.js +0 -478
  165. package/lib/components/inspector/agentInspector.d.ts +0 -179
  166. package/lib/components/inspector/agentInspector.js +0 -267
  167. package/lib/components/inspector/chatInspect.d.ts +0 -38
  168. package/lib/components/inspector/chatInspect.js +0 -65
@@ -1,478 +0,0 @@
1
- /*
2
- * Copyright (c) 2025-2026 Datalayer, Inc.
3
- * Distributed under the terms of the Modified BSD License.
4
- */
5
- import { argumentsLine, formatBytes, jsonBytes, shortLine, } from './agentInspector';
6
- const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
7
- /** The text of an A2A 1.0 message's or artifact's parts, as JSON carries them. */
8
- export function partsText(parts) {
9
- return Array.isArray(parts)
10
- ? parts
11
- .map(part => isObject(part) && typeof part.text === 'string' ? part.text : '')
12
- .join('')
13
- : '';
14
- }
15
- /** `TASK_STATE_WORKING` is `working`. */
16
- export function stateWord(state) {
17
- return typeof state === 'string'
18
- ? state
19
- .replace(/^TASK_STATE_/, '')
20
- .toLowerCase()
21
- .replace(/_/g, ' ')
22
- : '';
23
- }
24
- /** A JSON-RPC request's body, if the request carries one. */
25
- function rpcOf(body) {
26
- if (typeof body !== 'string') {
27
- return null;
28
- }
29
- try {
30
- const parsed = JSON.parse(body);
31
- return isObject(parsed) && parsed.jsonrpc === '2.0' ? parsed : null;
32
- }
33
- catch {
34
- return null;
35
- }
36
- }
37
- /** What a request asks, in a line. */
38
- function requestSummary(method, params) {
39
- const message = isObject(params) ? params.message : undefined;
40
- const text = isObject(message) ? partsText(message.parts) : '';
41
- if (text) {
42
- return shortLine(text);
43
- }
44
- if (isObject(params) && typeof params.id === 'string') {
45
- return `${method} ${params.id}`;
46
- }
47
- return method;
48
- }
49
- /** The step a status message carries: a tool call or its end. */
50
- function stepOf(message) {
51
- if (!isObject(message) || !Array.isArray(message.parts)) {
52
- return undefined;
53
- }
54
- for (const part of message.parts) {
55
- const data = isObject(part) ? part.data : undefined;
56
- if (!isObject(data)) {
57
- continue;
58
- }
59
- const call = isObject(data.tool_call) ? data.tool_call : undefined;
60
- const done = isObject(data.tool_result) ? data.tool_result : undefined;
61
- const told = call ?? done;
62
- if (told && typeof told.name === 'string') {
63
- return {
64
- ...(typeof told.id === 'string' ? { toolId: told.id } : {}),
65
- name: told.name,
66
- ended: !call,
67
- ...(call && isObject(call.arguments)
68
- ? { args: call.arguments }
69
- : {}),
70
- ...(done && 'result' in done ? { result: done.result } : {}),
71
- ...(done && done.error ? { error: String(done.error) } : {}),
72
- ...(typeof told.note === 'string' && told.note
73
- ? { note: told.note }
74
- : {}),
75
- };
76
- }
77
- }
78
- return undefined;
79
- }
80
- /** Records the stream of one request, event by event. */
81
- class StreamRecorder {
82
- options;
83
- method;
84
- rpcId;
85
- /** The entry of each artifact being streamed, by its id. */
86
- artifacts = new Map();
87
- constructor(options, method, rpcId) {
88
- this.options = options;
89
- this.method = method;
90
- this.rpcId = rpcId;
91
- }
92
- classify(name, args) {
93
- return (this.options.classifyTool?.(name, args) ?? { source: 'backend-tool' });
94
- }
95
- /** One JSON-RPC message of the response: a response, or an event of a stream. */
96
- record(message, streamed) {
97
- const { sink, asker, peer } = this.options;
98
- const direction = { from: peer, to: asker };
99
- const base = { actor: peer, source: 'a2a', direction };
100
- if (!isObject(message)) {
101
- return;
102
- }
103
- if (isObject(message.error)) {
104
- const error = message.error;
105
- sink.push({
106
- ...base,
107
- kind: 'error',
108
- name: this.method,
109
- status: 'failed',
110
- summary: shortLine(`${String(error.code ?? '')} ${String(error.message ?? '')}`),
111
- error: String(error.message ?? ''),
112
- a2a: { method: this.method, rpcId: this.rpcId },
113
- payload: message,
114
- });
115
- return;
116
- }
117
- const result = isObject(message.result) ? message.result : undefined;
118
- if (!result) {
119
- sink.push({
120
- ...base,
121
- kind: 'response',
122
- name: this.method,
123
- summary: this.method,
124
- a2a: { method: this.method, rpcId: this.rpcId },
125
- payload: message,
126
- });
127
- return;
128
- }
129
- const a2a = { method: this.method, rpcId: this.rpcId };
130
- if (isObject(result.task)) {
131
- const task = result.task;
132
- const status = isObject(task.status) ? task.status : {};
133
- sink.push({
134
- ...base,
135
- kind: 'task',
136
- summary: `Task ${stateWord(status.state)}`,
137
- a2a: {
138
- ...a2a,
139
- taskId: String(task.id ?? ''),
140
- contextId: String(task.contextId ?? ''),
141
- state: String(status.state ?? ''),
142
- },
143
- payload: message,
144
- });
145
- }
146
- else if (isObject(result.statusUpdate)) {
147
- this.status(result.statusUpdate, message, a2a);
148
- }
149
- else if (isObject(result.artifactUpdate)) {
150
- this.artifact(result.artifactUpdate, message, a2a);
151
- }
152
- else if (isObject(result.message)) {
153
- const reply = result.message;
154
- sink.push({
155
- ...base,
156
- kind: 'message',
157
- summary: shortLine(partsText(reply.parts)) || 'A message',
158
- a2a: {
159
- ...a2a,
160
- taskId: String(reply.taskId ?? ''),
161
- contextId: String(reply.contextId ?? ''),
162
- },
163
- payload: message,
164
- });
165
- }
166
- else {
167
- sink.push({
168
- ...base,
169
- kind: streamed ? 'event' : 'response',
170
- summary: this.method,
171
- a2a,
172
- payload: message,
173
- });
174
- }
175
- }
176
- status(update, message, a2a) {
177
- const { sink, asker, peer } = this.options;
178
- const status = isObject(update.status) ? update.status : {};
179
- const taskId = String(update.taskId ?? '');
180
- const state = String(status.state ?? '');
181
- const step = stepOf(status.message);
182
- const text = partsText(isObject(status.message) ? status.message.parts : undefined);
183
- const summary = step
184
- ? step.ended
185
- ? `${stateWord(state)} · ${step.error ? `${step.name} failed` : `${step.name} returned`}`
186
- : `${stateWord(state)} · calls ${step.name}(${argumentsLine(step.args)})`
187
- : `${stateWord(state)}${text ? ` · ${shortLine(text)}` : ''}`;
188
- const failed = /FAILED|REJECTED|CANCELED/.test(state);
189
- const id = sink.push({
190
- actor: peer,
191
- source: 'a2a',
192
- direction: { from: peer, to: asker },
193
- kind: 'status-update',
194
- summary,
195
- ...(failed ? { status: 'failed' } : {}),
196
- a2a: {
197
- ...a2a,
198
- taskId,
199
- contextId: String(update.contextId ?? ''),
200
- state,
201
- ...(step
202
- ? {
203
- step: { toolId: step.toolId, name: step.name, ended: step.ended },
204
- }
205
- : {}),
206
- },
207
- payload: message,
208
- });
209
- if (!step) {
210
- return;
211
- }
212
- // The call, as one entry of its own, the peer's: from its start to its end.
213
- const key = `a2a:${taskId}:${step.toolId ?? step.name}`;
214
- if (!step.ended) {
215
- const kind = this.classify(step.name, step.args);
216
- sink.start({
217
- key,
218
- actor: peer,
219
- source: kind.source,
220
- ...(kind.icon ? { icon: kind.icon } : {}),
221
- ...(kind.emoji ? { emoji: kind.emoji } : {}),
222
- kind: 'tool-call',
223
- name: step.name,
224
- summary: step.note ?? argumentsLine(step.args),
225
- payload: step.args ?? {},
226
- links: [id],
227
- });
228
- }
229
- else if (sink.running(key)) {
230
- sink.end(key, {
231
- status: step.error ? 'failed' : 'done',
232
- result: step.result,
233
- ...(step.error ? { error: step.error } : {}),
234
- links: [id],
235
- });
236
- }
237
- }
238
- artifact(update, message, a2a) {
239
- const { sink, asker, peer } = this.options;
240
- const artifact = isObject(update.artifact) ? update.artifact : {};
241
- const artifactId = String(artifact.artifactId ?? '');
242
- const text = partsText(artifact.parts);
243
- const parts = Array.isArray(artifact.parts) ? artifact.parts : [];
244
- const media = parts
245
- .map(part => (isObject(part) ? part.mediaType : undefined))
246
- .find(type => typeof type === 'string' && type && !type.startsWith('text/'));
247
- const known = this.artifacts.get(artifactId);
248
- if (known && update.append && !update.lastChunk) {
249
- // A chunk of an artifact being streamed: one entry, its text growing.
250
- known.text += text;
251
- known.events.push(message);
252
- const chunks = known.events.length;
253
- const words = known.text;
254
- sink.update(known.id, entry => ({
255
- summary: `${String(artifact.name ?? 'artifact')} · ${shortLine(words)}`,
256
- payload: [...known.events],
257
- a2a: { ...entry.a2a, chunks },
258
- }));
259
- return;
260
- }
261
- const name = String(artifact.name ?? (artifactId || 'artifact'));
262
- const summary = media
263
- ? `${name} · ${String(media)}, ${formatBytes(jsonBytes(message))}`
264
- : `${name} · ${shortLine(text)}`;
265
- const id = sink.push({
266
- actor: peer,
267
- source: 'a2a',
268
- direction: { from: peer, to: asker },
269
- kind: 'artifact-update',
270
- name,
271
- summary,
272
- a2a: {
273
- ...a2a,
274
- taskId: String(update.taskId ?? ''),
275
- contextId: String(update.contextId ?? ''),
276
- artifactId,
277
- chunks: 1,
278
- },
279
- payload: message,
280
- });
281
- if (update.append && !update.lastChunk) {
282
- this.artifacts.set(artifactId, { id, text, events: [message] });
283
- }
284
- else {
285
- this.artifacts.delete(artifactId);
286
- }
287
- }
288
- }
289
- /** The JSON of each event of a server-sent stream, as it arrives. */
290
- async function readEvents(body, onEvent) {
291
- const reader = body.getReader();
292
- const decoder = new TextDecoder();
293
- let buffer = '';
294
- const flush = (block) => {
295
- const data = block
296
- .split(/\r?\n/)
297
- .filter(line => line.startsWith('data:'))
298
- .map(line => line.slice(5).replace(/^ /, ''))
299
- .join('\n');
300
- if (!data) {
301
- return;
302
- }
303
- try {
304
- onEvent(JSON.parse(data));
305
- }
306
- catch {
307
- onEvent({ unparsed: data });
308
- }
309
- };
310
- for (;;) {
311
- const { done, value } = await reader.read();
312
- if (done) {
313
- break;
314
- }
315
- buffer += decoder.decode(value, { stream: true });
316
- let at;
317
- while ((at = buffer.search(/\r?\n\r?\n/)) >= 0) {
318
- const block = buffer.slice(0, at);
319
- buffer = buffer.slice(at).replace(/^\r?\n\r?\n/, '');
320
- flush(block);
321
- }
322
- }
323
- if (buffer.trim()) {
324
- flush(buffer);
325
- }
326
- }
327
- /**
328
- * A fetch that records the A2A traffic it carries into `options.sink`, and
329
- * hands the SDK what the network gave.
330
- */
331
- export function inspectA2AFetch(base, options) {
332
- const { sink, asker, peer } = options;
333
- // What the asker makes of the events (`A2APeerEvent`) is linked to them.
334
- sink.addLinker(a2aEventLinker);
335
- return async (input, init) => {
336
- const rpc = rpcOf(init?.body);
337
- const method = rpc ? String(rpc.method ?? '') : '';
338
- const rpcId = rpc ? rpc.id : undefined;
339
- const url = typeof input === 'string'
340
- ? input
341
- : input instanceof URL
342
- ? input.href
343
- : input.url;
344
- if (rpc) {
345
- sink.push({
346
- actor: asker,
347
- source: 'a2a',
348
- direction: { from: asker, to: peer },
349
- kind: 'request',
350
- name: method,
351
- summary: requestSummary(method, rpc.params),
352
- a2a: { method, rpcId },
353
- payload: rpc,
354
- });
355
- }
356
- let response;
357
- try {
358
- response = await base(input, init);
359
- }
360
- catch (reason) {
361
- sink.push({
362
- actor: asker,
363
- source: 'a2a',
364
- direction: { from: asker, to: peer },
365
- kind: 'error',
366
- name: method || undefined,
367
- status: 'failed',
368
- summary: shortLine(`${method || url}: ${reason instanceof Error ? reason.message : String(reason)}`),
369
- error: reason instanceof Error ? reason.message : String(reason),
370
- a2a: { method, rpcId },
371
- });
372
- throw reason;
373
- }
374
- const type = response.headers.get('content-type') ?? '';
375
- const recorder = new StreamRecorder(options, method, rpcId);
376
- if (!response.ok) {
377
- const copy = response.clone();
378
- void copy.text().then(text => sink.push({
379
- actor: peer,
380
- source: 'a2a',
381
- direction: { from: peer, to: asker },
382
- kind: 'error',
383
- name: method || undefined,
384
- status: 'failed',
385
- summary: shortLine(`${response.status} ${response.statusText} ${text}`),
386
- error: `${response.status} ${response.statusText}`,
387
- a2a: { method, rpcId },
388
- payload: { status: response.status, body: text },
389
- }));
390
- return response;
391
- }
392
- if (type.includes('text/event-stream') && response.body) {
393
- const [mine, theirs] = response.body.tee();
394
- void readEvents(mine, data => recorder.record(data, true)).catch(() => undefined);
395
- return new Response(theirs, {
396
- status: response.status,
397
- statusText: response.statusText,
398
- headers: response.headers,
399
- });
400
- }
401
- if (type.includes('json')) {
402
- const copy = response.clone();
403
- void copy
404
- .json()
405
- .then(data => {
406
- if (rpc) {
407
- recorder.record(data, false);
408
- }
409
- else if (isObject(data) && Array.isArray(data.skills)) {
410
- // The agent card.
411
- sink.push({
412
- actor: peer,
413
- source: 'a2a',
414
- direction: { from: peer, to: asker },
415
- kind: 'agent-card',
416
- name: String(data.name ?? peer),
417
- summary: shortLine(`${String(data.name ?? peer)} · ${data.skills
418
- .map(skill => isObject(skill) ? String(skill.name ?? skill.id) : '')
419
- .join(', ')}`),
420
- payload: data,
421
- });
422
- }
423
- })
424
- .catch(() => undefined);
425
- }
426
- return response;
427
- };
428
- }
429
- /** The entry an event of the asker's came from. */
430
- function matchOf(event) {
431
- switch (event.phase) {
432
- case 'asked':
433
- return entry => entry.source === 'a2a' &&
434
- entry.kind === 'request' &&
435
- /Message/.test(entry.a2a?.method ?? '') &&
436
- entry.events.length === 0;
437
- case 'working':
438
- if (event.tool) {
439
- const tool = event.tool;
440
- return entry => entry.kind === 'status-update' &&
441
- entry.a2a?.taskId === event.taskId &&
442
- entry.a2a?.step?.name === tool.name &&
443
- entry.a2a?.step?.ended === tool.ended &&
444
- (tool.id === undefined || entry.a2a?.step?.toolId === tool.id);
445
- }
446
- if (event.note) {
447
- return entry => entry.kind === 'status-update' &&
448
- entry.a2a?.taskId === event.taskId &&
449
- !entry.a2a?.step &&
450
- entry.events.length === 0;
451
- }
452
- return entry => (entry.kind === 'task' || entry.kind === 'status-update') &&
453
- entry.a2a?.taskId === event.taskId &&
454
- entry.events.length === 0;
455
- case 'answered':
456
- return entry => entry.kind === 'status-update' &&
457
- entry.a2a?.taskId === event.taskId &&
458
- entry.a2a?.state === 'TASK_STATE_COMPLETED';
459
- default:
460
- return entry => entry.source === 'a2a' && entry.direction?.to !== undefined;
461
- }
462
- }
463
- /** Whether a value is an `A2APeerEvent`. */
464
- function isPeerEvent(event) {
465
- return (isObject(event) &&
466
- ['asked', 'working', 'answered', 'failed'].includes(String(event.phase)));
467
- }
468
- /**
469
- * The linker the A2A capture adds to its sink: an `A2APeerEvent` noted
470
- * (`sink.note`) is attached to the entry it came from.
471
- */
472
- export function a2aEventLinker(event) {
473
- return isPeerEvent(event) ? matchOf(event) : undefined;
474
- }
475
- /** Attach what the asker made of an event to the A2A entry it came from. */
476
- export function linkA2APeerEvent(sink, event) {
477
- sink.attach(matchOf(event), event);
478
- }
@@ -1,179 +0,0 @@
1
- /**
2
- * The Agent Inspector's record: what an agent does, as it does it, one entry
3
- * a thing — an A2A request or event, an MCP tool call, a skill, a frontend or
4
- * backend tool, a model turn, an approval — each with its time, the agent
5
- * that did it (`actor`), where it came from (`source`), its kind, its name,
6
- * its status and, once it ends, its duration, a line that says it, and its
7
- * payload whole.
8
- *
9
- * A call's start and its end are one entry: {@link AgentInspectorSink.start}
10
- * opens it under a key (a tool call's id), {@link AgentInspectorSink.end}
11
- * closes it with its result. Anything can push into a sink: the A2A capture
12
- * (`inspectA2AFetch`), a team (`useA2ATeam`'s `inspector`), a chat
13
- * (`ChatBase`, under {@link AgentInspectorProvider}). `AgentInspector` draws
14
- * it.
15
- *
16
- * @module components/inspector/agentInspector
17
- */
18
- import { type JSX, type ReactNode } from 'react';
19
- /** Where an entry comes from. */
20
- export type AgentInspectorSource = 'a2a' | 'mcp' | 'skill' | 'frontend-tool' | 'backend-tool' | 'model' | 'approval';
21
- /** Every source, in the order the filters list them. */
22
- export declare const AGENT_INSPECTOR_SOURCES: readonly AgentInspectorSource[];
23
- /** A source, in words. */
24
- export declare const AGENT_INSPECTOR_SOURCE_LABELS: Record<AgentInspectorSource, string>;
25
- export type AgentInspectorStatus = 'running' | 'done' | 'failed';
26
- /** What A2A says of an entry: its JSON-RPC method and id, its task and context, its state. */
27
- export type AgentInspectorA2A = {
28
- method?: string;
29
- rpcId?: string | number;
30
- taskId?: string;
31
- contextId?: string;
32
- /** `TASK_STATE_WORKING`, … */
33
- state?: string;
34
- /** The tool step a status carries: its call's id, and whether it is its end. */
35
- step?: {
36
- toolId?: string;
37
- name: string;
38
- ended: boolean;
39
- };
40
- /** The artifact an artifact update is a chunk of. */
41
- artifactId?: string;
42
- /** How many chunks an artifact entry holds. */
43
- chunks?: number;
44
- };
45
- export type AgentInspectorEntry = {
46
- id: string;
47
- /** When it happened, or started: ms since the epoch. */
48
- at: number;
49
- /** When it ended, for a call. */
50
- endedAt?: number;
51
- /** Which agent did it. */
52
- actor: string;
53
- source: AgentInspectorSource;
54
- /** `request`, `status-update`, `artifact-update`, `task`, `message`, `tool-call`, `turn`, … */
55
- kind: string;
56
- /** The tool's, skill's or method's name. */
57
- name?: string;
58
- /** Its mark, as `SpecMark` draws it. */
59
- icon?: string;
60
- emoji?: string;
61
- status?: AgentInspectorStatus;
62
- /** One line that says it. */
63
- summary: string;
64
- /** Which way it went, between two agents. */
65
- direction?: {
66
- from: string;
67
- to: string;
68
- };
69
- a2a?: AgentInspectorA2A;
70
- /** What was sent: a message, or a call's arguments. */
71
- payload?: unknown;
72
- /** What came back, for a call. */
73
- result?: unknown;
74
- error?: string;
75
- /** How large `payload` and `result` are, as JSON, in bytes. */
76
- bytes: number;
77
- /** The key a call's start and end share. */
78
- key?: string;
79
- /** Entries this one goes with: a tool call's A2A statuses, … */
80
- links: string[];
81
- /** What a caller made of it: the semantic events attached (`A2APeerEvent`, …). */
82
- events: unknown[];
83
- };
84
- /** What is pushed: an entry, less what the sink works out. */
85
- export type AgentInspectorInput = Omit<AgentInspectorEntry, 'id' | 'at' | 'bytes' | 'links' | 'events'> & {
86
- at?: number;
87
- links?: string[];
88
- };
89
- /** How a call ended. */
90
- export type AgentInspectorOutcome = {
91
- status?: Exclude<AgentInspectorStatus, 'running'>;
92
- result?: unknown;
93
- error?: string;
94
- at?: number;
95
- summary?: string;
96
- links?: string[];
97
- };
98
- /** A session, exported. */
99
- export type AgentInspectorExport = {
100
- exportedAt: string;
101
- entries: AgentInspectorEntry[];
102
- };
103
- export interface AgentInspectorSink {
104
- /** Record something that happened at once; returns its id. */
105
- push(input: AgentInspectorInput): string;
106
- /**
107
- * Open a call under `key`; a call already open under it has its payload
108
- * and summary updated instead (arguments that stream in).
109
- */
110
- start(input: AgentInspectorInput & {
111
- key: string;
112
- }): string;
113
- /** Close the call open under `key`; `undefined` when none is. */
114
- end(key: string, outcome?: AgentInspectorOutcome): string | undefined;
115
- /** Change an entry. */
116
- update(id: string, patch: (entry: AgentInspectorEntry) => Partial<AgentInspectorEntry>): void;
117
- /**
118
- * Attach a semantic event to the newest entry `match` takes, or to the
119
- * first that arrives and does (a few are kept waiting).
120
- */
121
- attach(match: (entry: AgentInspectorEntry) => boolean, event: unknown): void;
122
- /** Whether a call is open under `key`. */
123
- running(key: string): boolean;
124
- /**
125
- * Say what a caller made of something (an `A2APeerEvent`): it is attached
126
- * to the entry it came from, as the first linker that knows it says
127
- * (`addLinker`; the A2A capture adds one). Nothing knows it: dropped.
128
- */
129
- note(event: unknown): void;
130
- /** Teach the sink to link a kind of event to its entry; returns its removal. */
131
- addLinker(linker: AgentInspectorLinker): () => void;
132
- clear(): void;
133
- entries(): readonly AgentInspectorEntry[];
134
- subscribe(listener: () => void): () => void;
135
- exportSession(): AgentInspectorExport;
136
- }
137
- /** The entry an event came from, as a matcher; `undefined` for an event it does not know. */
138
- export type AgentInspectorLinker = (event: unknown) => ((entry: AgentInspectorEntry) => boolean) | undefined;
139
- /** How large a value is, as JSON, in bytes. */
140
- export declare function jsonBytes(value: unknown): number;
141
- /** A size, in words: `812 B`, `12.3 kB`, `1.2 MB`. */
142
- export declare function formatBytes(bytes: number): string;
143
- /** A line short enough for a row. */
144
- export declare function shortLine(text: string, length?: number): string;
145
- export type AgentInspectorOptions = {
146
- /** The clock: `Date.now` unless a test sets it. */
147
- now?: () => number;
148
- /** The most entries kept: the oldest go first. */
149
- limit?: number;
150
- };
151
- /** Make a sink: the record, and who listens to it. */
152
- export declare function createAgentInspector(options?: AgentInspectorOptions): AgentInspectorSink;
153
- /** A sink's entries, kept current; none without a sink. */
154
- export declare function useAgentInspectorEntries(sink: AgentInspectorSink | null | undefined): readonly AgentInspectorEntry[];
155
- /**
156
- * A sink for a page, made once (or the one given), and its entries: what a
157
- * page passes to its team, its chat and its `AgentInspector`.
158
- */
159
- export declare function useAgentInspector(given?: AgentInspectorSink | null): {
160
- sink: AgentInspectorSink;
161
- entries: readonly AgentInspectorEntry[];
162
- };
163
- /** The sink the chats under it push into. */
164
- export declare const AgentInspectorContext: import("react").Context<AgentInspectorSink | null>;
165
- /** The sink of the nearest {@link AgentInspectorProvider}, if any. */
166
- export declare function useAgentInspectorSink(): AgentInspectorSink | null;
167
- /** Every chat under it records what its agent does into `sink`. */
168
- export declare function AgentInspectorProvider({ sink, children, }: {
169
- sink: AgentInspectorSink;
170
- children?: ReactNode;
171
- }): JSX.Element;
172
- /** What a tool call is, for the inspector: its source and its mark. */
173
- export type ToolClass = {
174
- source: AgentInspectorSource;
175
- icon?: string;
176
- emoji?: string;
177
- };
178
- /** A call's arguments, in a few words: `kind=customer, limit=100`. */
179
- export declare function argumentsLine(args: unknown, length?: number): string;