@kuntur/a2a-carbon-chat-adapter 0.1.0

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/dist/index.cjs ADDED
@@ -0,0 +1,2281 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ var AgentContext = react.createContext(null);
7
+ function useAgentContext() {
8
+ const context = react.useContext(AgentContext);
9
+ if (!context) {
10
+ throw new Error("useAgentContext must be used within an AgentProvider");
11
+ }
12
+ return context;
13
+ }
14
+ function useAgentContextOptional() {
15
+ return react.useContext(AgentContext);
16
+ }
17
+ function AgentProvider({
18
+ children,
19
+ agent,
20
+ agents: agentsProp,
21
+ registry,
22
+ defaultAgentId,
23
+ persistSelection = false,
24
+ storageKey = "a2a-chat-selected-agent"
25
+ }) {
26
+ const agents = react.useMemo(() => {
27
+ if (registry) {
28
+ return Object.values(registry.agents);
29
+ }
30
+ if (agentsProp) {
31
+ return agentsProp;
32
+ }
33
+ if (agent) {
34
+ return [agent];
35
+ }
36
+ return [];
37
+ }, [agent, agentsProp, registry]);
38
+ const getInitialAgentId = () => {
39
+ if (persistSelection && typeof window !== "undefined") {
40
+ const saved = localStorage.getItem(storageKey);
41
+ if (saved && agents.some((a) => a.id === saved)) {
42
+ return saved;
43
+ }
44
+ }
45
+ if (defaultAgentId && agents.some((a) => a.id === defaultAgentId)) {
46
+ return defaultAgentId;
47
+ }
48
+ if (registry?.defaultAgentId && agents.some((a) => a.id === registry.defaultAgentId)) {
49
+ return registry.defaultAgentId;
50
+ }
51
+ return agents[0]?.id ?? null;
52
+ };
53
+ const [currentAgentId, setCurrentAgentId] = react.useState(getInitialAgentId);
54
+ react.useEffect(() => {
55
+ if (persistSelection && currentAgentId && typeof window !== "undefined") {
56
+ localStorage.setItem(storageKey, currentAgentId);
57
+ }
58
+ }, [currentAgentId, persistSelection, storageKey]);
59
+ const currentAgent = react.useMemo(
60
+ () => agents.find((a) => a.id === currentAgentId) ?? null,
61
+ [agents, currentAgentId]
62
+ );
63
+ const selectAgent = react.useCallback(
64
+ (agentId) => {
65
+ if (agents.some((a) => a.id === agentId)) {
66
+ setCurrentAgentId(agentId);
67
+ } else {
68
+ console.warn(`[AgentProvider] Agent "${agentId}" not found in registry`);
69
+ }
70
+ },
71
+ [agents]
72
+ );
73
+ const getAgent = react.useCallback((agentId) => agents.find((a) => a.id === agentId), [agents]);
74
+ const hasAgent = react.useCallback((agentId) => agents.some((a) => a.id === agentId), [agents]);
75
+ const value = react.useMemo(
76
+ () => ({
77
+ currentAgent,
78
+ agents,
79
+ selectAgent,
80
+ getAgent,
81
+ hasAgent
82
+ }),
83
+ [currentAgent, agents, selectAgent, getAgent, hasAgent]
84
+ );
85
+ return /* @__PURE__ */ jsxRuntime.jsx(AgentContext.Provider, { value, children });
86
+ }
87
+
88
+ // src/lib/a2a/extension-uris.ts
89
+ var EXTENSION_URIS = {
90
+ // Service Extensions
91
+ LLM: "https://a2a-extensions.agentstack.beeai.dev/services/llm/v1",
92
+ EMBEDDING: "https://a2a-extensions.agentstack.beeai.dev/services/embedding/v1",
93
+ MCP: "https://a2a-extensions.agentstack.beeai.dev/services/mcp/v1",
94
+ OAUTH_PROVIDER: "https://a2a-extensions.agentstack.beeai.dev/services/oauth-provider/v1",
95
+ SECRETS: "https://a2a-extensions.agentstack.beeai.dev/services/secrets/v1",
96
+ PLATFORM_API: "https://a2a-extensions.agentstack.beeai.dev/services/platform-api/v1",
97
+ FORM_SERVICE: "https://a2a-extensions.agentstack.beeai.dev/services/form/v1",
98
+ // UI Extensions (Agent -> Platform)
99
+ CITATION: "https://a2a-extensions.agentstack.beeai.dev/ui/citation/v1",
100
+ TRAJECTORY: "https://a2a-extensions.agentstack.beeai.dev/ui/trajectory/v1",
101
+ ERROR: "https://a2a-extensions.agentstack.beeai.dev/ui/error/v1",
102
+ FORM_REQUEST: "https://a2a-extensions.agentstack.beeai.dev/ui/form-request/v1",
103
+ CANVAS: "https://a2a-extensions.agentstack.beeai.dev/ui/canvas/v1",
104
+ AGENT_DETAIL: "https://a2a-extensions.agentstack.beeai.dev/ui/agent-detail/v1",
105
+ OAUTH_REQUEST: "https://a2a-extensions.agentstack.beeai.dev/ui/oauth/v1",
106
+ SETTINGS: "https://a2a-extensions.agentstack.beeai.dev/ui/settings/v1"
107
+ };
108
+
109
+ // src/lib/a2a/extension-handler.ts
110
+ function extractDemandsFromAgentCard(agentCard) {
111
+ const extensions = agentCard.extensions || [];
112
+ const demands = {
113
+ llmDemands: null,
114
+ embeddingDemands: null,
115
+ mcpDemands: null,
116
+ oauthDemands: null,
117
+ settingsDemands: null,
118
+ secretDemands: null,
119
+ formDemands: null
120
+ };
121
+ for (const extUri of extensions) {
122
+ const hashIndex = extUri.indexOf("#");
123
+ const uri = hashIndex >= 0 ? extUri.substring(0, hashIndex) : extUri;
124
+ const paramsStr = hashIndex >= 0 ? extUri.substring(hashIndex + 1) : null;
125
+ let params = {};
126
+ if (paramsStr) {
127
+ try {
128
+ params = JSON.parse(decodeURIComponent(paramsStr));
129
+ } catch {
130
+ console.warn(`Failed to parse extension params for ${uri}:`, paramsStr);
131
+ }
132
+ }
133
+ switch (uri) {
134
+ case EXTENSION_URIS.LLM:
135
+ demands.llmDemands = params;
136
+ break;
137
+ case EXTENSION_URIS.EMBEDDING:
138
+ demands.embeddingDemands = params;
139
+ break;
140
+ case EXTENSION_URIS.MCP:
141
+ demands.mcpDemands = params;
142
+ break;
143
+ case EXTENSION_URIS.OAUTH_PROVIDER:
144
+ demands.oauthDemands = params;
145
+ break;
146
+ case EXTENSION_URIS.SETTINGS:
147
+ demands.settingsDemands = params;
148
+ break;
149
+ case EXTENSION_URIS.SECRETS:
150
+ demands.secretDemands = params;
151
+ break;
152
+ case EXTENSION_URIS.FORM_SERVICE:
153
+ demands.formDemands = params;
154
+ break;
155
+ }
156
+ }
157
+ return demands;
158
+ }
159
+ function buildFulfillments(config) {
160
+ const fulfillments = {};
161
+ if (config.llm) {
162
+ const llmConfig = config.llm;
163
+ fulfillments.llm = async (demands) => {
164
+ const result = {};
165
+ for (const [key, demand] of Object.entries(demands.llm_demands)) {
166
+ const model = demand.suggested?.[0] || llmConfig.defaultModel;
167
+ result[key] = {
168
+ identifier: "custom_llm",
169
+ api_base: llmConfig.apiBase,
170
+ api_key: llmConfig.apiKey,
171
+ api_model: model
172
+ };
173
+ }
174
+ return { llm_fulfillments: result };
175
+ };
176
+ }
177
+ if (config.embedding) {
178
+ const embeddingConfig = config.embedding;
179
+ fulfillments.embedding = async (demands) => {
180
+ const result = {};
181
+ for (const [key, demand] of Object.entries(demands.embedding_demands)) {
182
+ const model = demand.suggested?.[0] || embeddingConfig.defaultModel;
183
+ result[key] = {
184
+ api_base: embeddingConfig.apiBase,
185
+ api_key: embeddingConfig.apiKey,
186
+ api_model: model
187
+ };
188
+ }
189
+ return { embedding_fulfillments: result };
190
+ };
191
+ }
192
+ if (config.mcp) {
193
+ const mcpConfig = config.mcp;
194
+ fulfillments.mcp = async (demands) => {
195
+ const result = {};
196
+ for (const [key, demand] of Object.entries(demands.mcp_demands)) {
197
+ const serverName = demand.suggested?.[0];
198
+ if (serverName && mcpConfig.servers[serverName]) {
199
+ result[key] = mcpConfig.servers[serverName];
200
+ }
201
+ }
202
+ return { mcp_fulfillments: result };
203
+ };
204
+ }
205
+ if (config.secrets) {
206
+ const secretsConfig = config.secrets;
207
+ fulfillments.secrets = async (demands) => {
208
+ const result = {};
209
+ for (const key of Object.keys(demands.secret_demands)) {
210
+ if (secretsConfig[key]) {
211
+ result[key] = { secret: secretsConfig[key] };
212
+ }
213
+ }
214
+ return { secret_fulfillments: result };
215
+ };
216
+ }
217
+ if (config.settings) {
218
+ const settingsConfig = config.settings;
219
+ fulfillments.settings = async (_demands) => {
220
+ return { values: settingsConfig };
221
+ };
222
+ }
223
+ if (config.oauth) {
224
+ const oauthConfig = config.oauth;
225
+ fulfillments.oauth = async (demands) => {
226
+ const result = {};
227
+ for (const key of Object.keys(demands.oauth_demands)) {
228
+ if (oauthConfig.providers[key]) {
229
+ result[key] = { access_token: oauthConfig.providers[key].accessToken };
230
+ }
231
+ }
232
+ return { oauth_fulfillments: result };
233
+ };
234
+ fulfillments.oauthRedirectUri = () => oauthConfig.redirectUri;
235
+ } else {
236
+ fulfillments.oauthRedirectUri = () => null;
237
+ }
238
+ fulfillments.form = async (_demands) => {
239
+ return { values: {} };
240
+ };
241
+ fulfillments.getContextToken = () => ({ token: "", expires_at: null });
242
+ return fulfillments;
243
+ }
244
+ async function resolveMetadata(demands, fulfillments) {
245
+ const metadata = {};
246
+ if (demands.llmDemands && fulfillments.llm) {
247
+ const llmFulfillment = await fulfillments.llm(demands.llmDemands);
248
+ metadata[EXTENSION_URIS.LLM] = llmFulfillment;
249
+ }
250
+ if (demands.embeddingDemands && fulfillments.embedding) {
251
+ const embeddingFulfillment = await fulfillments.embedding(demands.embeddingDemands);
252
+ metadata[EXTENSION_URIS.EMBEDDING] = embeddingFulfillment;
253
+ }
254
+ if (demands.mcpDemands && fulfillments.mcp) {
255
+ const mcpFulfillment = await fulfillments.mcp(demands.mcpDemands);
256
+ metadata[EXTENSION_URIS.MCP] = mcpFulfillment;
257
+ }
258
+ if (demands.oauthDemands && fulfillments.oauth) {
259
+ const oauthFulfillment = await fulfillments.oauth(demands.oauthDemands);
260
+ metadata[EXTENSION_URIS.OAUTH_PROVIDER] = oauthFulfillment;
261
+ }
262
+ if (demands.settingsDemands && fulfillments.settings) {
263
+ const settingsFulfillment = await fulfillments.settings(demands.settingsDemands);
264
+ metadata[EXTENSION_URIS.SETTINGS] = settingsFulfillment;
265
+ }
266
+ if (demands.secretDemands && fulfillments.secrets) {
267
+ const secretsFulfillment = await fulfillments.secrets(demands.secretDemands);
268
+ metadata[EXTENSION_URIS.SECRETS] = secretsFulfillment;
269
+ }
270
+ if (demands.formDemands && fulfillments.form) {
271
+ const formFulfillment = await fulfillments.form(demands.formDemands);
272
+ metadata[EXTENSION_URIS.FORM_SERVICE] = formFulfillment;
273
+ }
274
+ if (fulfillments.oauthRedirectUri) {
275
+ const redirectUri = fulfillments.oauthRedirectUri();
276
+ if (redirectUri) {
277
+ metadata[EXTENSION_URIS.OAUTH_REQUEST] = { redirect_uri: redirectUri };
278
+ }
279
+ }
280
+ if (fulfillments.getContextToken) {
281
+ const token = fulfillments.getContextToken();
282
+ if (token.token) {
283
+ metadata[EXTENSION_URIS.PLATFORM_API] = { context_token: token.token };
284
+ }
285
+ }
286
+ return metadata;
287
+ }
288
+ function handleAgentCard(agentCard) {
289
+ const demands = extractDemandsFromAgentCard(agentCard);
290
+ return {
291
+ demands,
292
+ resolveMetadata: (fulfillments) => resolveMetadata(demands, fulfillments)
293
+ };
294
+ }
295
+
296
+ // src/lib/a2a/client.ts
297
+ function generateUUID() {
298
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
299
+ return crypto.randomUUID();
300
+ }
301
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
302
+ const r = (crypto.getRandomValues(new Uint8Array(1))[0] & 15) >> (c === "x" ? 0 : 3);
303
+ return (c === "x" ? r : r & 3 | 8).toString(16);
304
+ });
305
+ }
306
+ var A2AClient = class {
307
+ constructor(agentUrl, config = {}) {
308
+ this.agentCard = null;
309
+ this.platformConfig = null;
310
+ this.extractedDemands = null;
311
+ this.fulfillments = null;
312
+ this.baseUrl = agentUrl.replace(/\/$/, "");
313
+ this.jsonRpcUrl = `${this.baseUrl}/jsonrpc/`;
314
+ this.apiKey = config.apiKey || "";
315
+ this.extensions = config.extensions || {};
316
+ this.platformConfig = config.platformConfig || null;
317
+ }
318
+ /**
319
+ * Get configured extensions
320
+ */
321
+ getExtensions() {
322
+ return this.extensions;
323
+ }
324
+ /**
325
+ * Update extension configuration
326
+ */
327
+ setExtensions(extensions) {
328
+ this.extensions = extensions;
329
+ }
330
+ async initialize() {
331
+ const response = await fetch(this.baseUrl, {
332
+ method: "GET",
333
+ headers: {
334
+ "Accept": "application/json"
335
+ }
336
+ });
337
+ if (!response.ok) {
338
+ const fallbackUrl = `${this.baseUrl}/.well-known/agent-card.json`;
339
+ const fallbackResponse = await fetch(fallbackUrl, {
340
+ method: "GET",
341
+ headers: {
342
+ "Accept": "application/json"
343
+ }
344
+ });
345
+ if (!fallbackResponse.ok) {
346
+ throw new Error(`Failed to fetch agent card: ${response.status} ${response.statusText}`);
347
+ }
348
+ this.agentCard = await fallbackResponse.json();
349
+ } else {
350
+ this.agentCard = await response.json();
351
+ }
352
+ if (!this.agentCard?.name || !this.agentCard?.url) {
353
+ throw new Error("Invalid agent card: missing required fields");
354
+ }
355
+ this.extractedDemands = extractDemandsFromAgentCard(this.agentCard);
356
+ if (this.platformConfig) {
357
+ this.fulfillments = buildFulfillments(this.platformConfig);
358
+ }
359
+ return this.agentCard;
360
+ }
361
+ getAgentCard() {
362
+ return this.agentCard;
363
+ }
364
+ getJsonRpcUrl() {
365
+ return this.jsonRpcUrl;
366
+ }
367
+ /**
368
+ * Get resolved metadata for A2A requests
369
+ * Call this after initialize() to get metadata that fulfills agent demands
370
+ */
371
+ async getResolvedMetadata() {
372
+ if (!this.extractedDemands) {
373
+ return {};
374
+ }
375
+ if (!this.fulfillments) {
376
+ console.warn("No fulfillments configured. Agent may fall back to defaults.");
377
+ return {};
378
+ }
379
+ return resolveMetadata(this.extractedDemands, this.fulfillments);
380
+ }
381
+ /**
382
+ * Get extracted demands from agent card
383
+ */
384
+ getExtractedDemands() {
385
+ return this.extractedDemands;
386
+ }
387
+ /**
388
+ * Update platform configuration after initialization
389
+ */
390
+ setPlatformConfig(config) {
391
+ this.platformConfig = config;
392
+ this.fulfillments = buildFulfillments(config);
393
+ }
394
+ /**
395
+ * Get current platform configuration
396
+ */
397
+ getPlatformConfig() {
398
+ return this.platformConfig;
399
+ }
400
+ /**
401
+ * Build the base params object for A2A requests
402
+ */
403
+ async buildRequestParams(message) {
404
+ const resolvedMetadata = await this.getResolvedMetadata();
405
+ const params = {
406
+ message: {
407
+ role: "user",
408
+ messageId: generateUUID(),
409
+ parts: [{ kind: "text", text: message }]
410
+ }
411
+ };
412
+ if (Object.keys(this.extensions).length > 0) {
413
+ params.extensions = this.extensions;
414
+ }
415
+ const hasResolvedMetadata = Object.keys(resolvedMetadata).length > 0;
416
+ if (hasResolvedMetadata) {
417
+ params.metadata = {
418
+ ...resolvedMetadata,
419
+ ...params.extensions || {}
420
+ };
421
+ }
422
+ return params;
423
+ }
424
+ async sendMessage(message) {
425
+ const params = await this.buildRequestParams(message);
426
+ const payload = {
427
+ jsonrpc: "2.0",
428
+ method: "message/send",
429
+ params,
430
+ id: generateUUID()
431
+ };
432
+ const headers = {
433
+ "Content-Type": "application/json"
434
+ };
435
+ if (this.apiKey) {
436
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
437
+ }
438
+ const response = await fetch(this.jsonRpcUrl, {
439
+ method: "POST",
440
+ headers,
441
+ body: JSON.stringify(payload)
442
+ });
443
+ if (!response.ok) {
444
+ throw new Error(`Request failed: ${response.status} ${response.statusText}`);
445
+ }
446
+ const result = await response.json();
447
+ if (result.error) {
448
+ const error = result.error;
449
+ throw new Error(`Agent error: ${error.message} (code: ${error.code})`);
450
+ }
451
+ return result.result;
452
+ }
453
+ async streamMessage(message, onChunk, onComplete) {
454
+ const params = await this.buildRequestParams(message);
455
+ const payload = {
456
+ jsonrpc: "2.0",
457
+ method: "message/stream",
458
+ params,
459
+ id: generateUUID()
460
+ };
461
+ const headers = {
462
+ "Content-Type": "application/json",
463
+ "Accept": "text/event-stream"
464
+ };
465
+ if (this.apiKey) {
466
+ headers["Authorization"] = `Bearer ${this.apiKey}`;
467
+ }
468
+ const response = await fetch(this.jsonRpcUrl, {
469
+ method: "POST",
470
+ headers,
471
+ body: JSON.stringify(payload)
472
+ });
473
+ if (!response.ok) {
474
+ throw new Error(`Stream request failed: ${response.status} ${response.statusText}`);
475
+ }
476
+ if (!response.body) {
477
+ throw new Error("Response body is null");
478
+ }
479
+ const reader = response.body.getReader();
480
+ const decoder = new TextDecoder();
481
+ let buffer = "";
482
+ try {
483
+ while (true) {
484
+ const { done, value } = await reader.read();
485
+ if (done) break;
486
+ buffer += decoder.decode(value, { stream: true });
487
+ const lines = buffer.split("\n");
488
+ buffer = lines.pop() || "";
489
+ for (const line of lines) {
490
+ const trimmedLine = line.trim();
491
+ if (trimmedLine.startsWith("data: ")) {
492
+ const dataStr = trimmedLine.slice(6);
493
+ if (!dataStr || dataStr === "[DONE]") continue;
494
+ try {
495
+ const data = JSON.parse(dataStr);
496
+ if (data.error) {
497
+ const error = data.error;
498
+ throw new Error(`Stream error: ${error.message} (code: ${error.code})`);
499
+ }
500
+ if (data.result) {
501
+ const chunk = data.result;
502
+ await onChunk(chunk);
503
+ if (chunk.final === true && onComplete) {
504
+ await onComplete();
505
+ }
506
+ }
507
+ } catch (parseError) {
508
+ if (parseError instanceof SyntaxError) {
509
+ console.warn("Failed to parse SSE data:", dataStr);
510
+ } else {
511
+ throw parseError;
512
+ }
513
+ }
514
+ }
515
+ }
516
+ }
517
+ if (buffer.trim().startsWith("data: ")) {
518
+ const dataStr = buffer.trim().slice(6);
519
+ if (dataStr && dataStr !== "[DONE]") {
520
+ try {
521
+ const data = JSON.parse(dataStr);
522
+ if (data.result) {
523
+ const chunk = data.result;
524
+ await onChunk(chunk);
525
+ if (chunk.final === true && onComplete) {
526
+ await onComplete();
527
+ }
528
+ }
529
+ } catch (e) {
530
+ console.warn("Failed to parse final SSE data:", dataStr);
531
+ }
532
+ }
533
+ }
534
+ } finally {
535
+ reader.releaseLock();
536
+ }
537
+ }
538
+ };
539
+
540
+ // src/lib/a2a/ui-extension-parser.ts
541
+ function parseUIExtensions(metadata) {
542
+ if (!metadata) return {};
543
+ const result = {};
544
+ const citationData = metadata[EXTENSION_URIS.CITATION];
545
+ if (citationData && typeof citationData === "object") {
546
+ result.citations = citationData;
547
+ }
548
+ const trajectoryData = metadata[EXTENSION_URIS.TRAJECTORY];
549
+ if (trajectoryData && typeof trajectoryData === "object") {
550
+ result.trajectory = trajectoryData;
551
+ }
552
+ const errorData = metadata[EXTENSION_URIS.ERROR];
553
+ if (errorData && typeof errorData === "object") {
554
+ result.error = errorData;
555
+ }
556
+ const formData = metadata[EXTENSION_URIS.FORM_REQUEST];
557
+ if (formData && typeof formData === "object") {
558
+ result.formRequest = formData;
559
+ }
560
+ const canvasData = metadata[EXTENSION_URIS.CANVAS];
561
+ if (canvasData && typeof canvasData === "object") {
562
+ result.canvas = canvasData;
563
+ }
564
+ const agentDetailData = metadata[EXTENSION_URIS.AGENT_DETAIL];
565
+ if (agentDetailData && typeof agentDetailData === "object") {
566
+ result.agentDetail = agentDetailData;
567
+ }
568
+ return result;
569
+ }
570
+ function extractCitations(metadata) {
571
+ const parsed = parseUIExtensions(metadata);
572
+ return parsed.citations?.citations || [];
573
+ }
574
+ function extractTrajectory(metadata) {
575
+ const parsed = parseUIExtensions(metadata);
576
+ return parsed.trajectory || null;
577
+ }
578
+ function extractError(metadata) {
579
+ const parsed = parseUIExtensions(metadata);
580
+ return parsed.error || null;
581
+ }
582
+ function extractFormRequest(metadata) {
583
+ const parsed = parseUIExtensions(metadata);
584
+ return parsed.formRequest || null;
585
+ }
586
+
587
+ // src/lib/translator/a2a-to-carbon.ts
588
+ var MessageResponseTypes = {
589
+ TEXT: "text",
590
+ USER_DEFINED: "user_defined",
591
+ INLINE_ERROR: "inline_error",
592
+ OPTION: "option"
593
+ };
594
+ var ChainOfThoughtStepStatus = /* @__PURE__ */ ((ChainOfThoughtStepStatus2) => {
595
+ ChainOfThoughtStepStatus2["IN_PROGRESS"] = "in_progress";
596
+ ChainOfThoughtStepStatus2["SUCCESS"] = "success";
597
+ ChainOfThoughtStepStatus2["ERROR"] = "error";
598
+ return ChainOfThoughtStepStatus2;
599
+ })(ChainOfThoughtStepStatus || {});
600
+ var ReasoningStepOpenState = /* @__PURE__ */ ((ReasoningStepOpenState2) => {
601
+ ReasoningStepOpenState2["OPEN"] = "open";
602
+ ReasoningStepOpenState2["CLOSE"] = "close";
603
+ ReasoningStepOpenState2["DEFAULT"] = "default";
604
+ return ReasoningStepOpenState2;
605
+ })(ReasoningStepOpenState || {});
606
+ var UserType = /* @__PURE__ */ ((UserType2) => {
607
+ UserType2["HUMAN"] = "human";
608
+ UserType2["BOT"] = "bot";
609
+ UserType2["WATSONX"] = "watsonx";
610
+ return UserType2;
611
+ })(UserType || {});
612
+ var A2AToCarbonTranslator = class {
613
+ constructor(agentProfile) {
614
+ this.agentProfile = {
615
+ id: agentProfile?.id || "a2a-agent",
616
+ nickname: agentProfile?.nickname || "AI Assistant",
617
+ user_type: agentProfile?.user_type || "bot" /* BOT */,
618
+ profile_picture_url: agentProfile?.profile_picture_url
619
+ };
620
+ this.state = this.createInitialState();
621
+ }
622
+ /**
623
+ * Create initial translator state
624
+ */
625
+ createInitialState() {
626
+ return {
627
+ responseId: this.generateResponseId(),
628
+ itemId: "1",
629
+ accumulatedText: "",
630
+ reasoningSteps: [],
631
+ chainOfThought: [],
632
+ pendingToolCalls: /* @__PURE__ */ new Map(),
633
+ hasStartedStreaming: false,
634
+ shellMessageSent: false
635
+ };
636
+ }
637
+ /**
638
+ * Generate unique response ID
639
+ */
640
+ generateResponseId() {
641
+ return `response-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
642
+ }
643
+ /**
644
+ * Reset state for new message
645
+ */
646
+ reset() {
647
+ this.state = this.createInitialState();
648
+ }
649
+ /**
650
+ * Get current response ID
651
+ */
652
+ getResponseId() {
653
+ return this.state.responseId;
654
+ }
655
+ /**
656
+ * Get current item ID
657
+ */
658
+ getItemId() {
659
+ return this.state.itemId;
660
+ }
661
+ /**
662
+ * Update agent profile
663
+ */
664
+ setAgentProfile(profile) {
665
+ this.agentProfile = { ...this.agentProfile, ...profile };
666
+ }
667
+ /**
668
+ * Get current agent profile
669
+ */
670
+ getAgentProfile() {
671
+ return this.agentProfile;
672
+ }
673
+ /**
674
+ * Check if shell message has been sent
675
+ */
676
+ hasShellBeenSent() {
677
+ return this.state.shellMessageSent;
678
+ }
679
+ /**
680
+ * Mark shell message as sent
681
+ */
682
+ markShellSent() {
683
+ this.state.shellMessageSent = true;
684
+ }
685
+ /**
686
+ * Get accumulated text
687
+ */
688
+ getAccumulatedText() {
689
+ return this.state.accumulatedText;
690
+ }
691
+ /**
692
+ * Get current reasoning steps
693
+ */
694
+ getReasoningSteps() {
695
+ return [...this.state.reasoningSteps];
696
+ }
697
+ /**
698
+ * Get current chain of thought
699
+ */
700
+ getChainOfThought() {
701
+ return [...this.state.chainOfThought];
702
+ }
703
+ /**
704
+ * Check if streaming has started
705
+ */
706
+ hasStartedStreaming() {
707
+ return this.state.hasStartedStreaming;
708
+ }
709
+ /**
710
+ * Get current state (for debugging/inspection)
711
+ */
712
+ getState() {
713
+ return { ...this.state };
714
+ }
715
+ // ===========================================================================
716
+ // SHELL MESSAGE CREATION
717
+ // ===========================================================================
718
+ /**
719
+ * Create shell message to initialize Carbon's reducer
720
+ *
721
+ * IMPORTANT: This MUST be called before sending any other chunks.
722
+ * It seeds the message shell so reducers create the container before
723
+ * reasoning steps or text stream in.
724
+ */
725
+ createShellMessage(includeReasoning = true) {
726
+ this.state.shellMessageSent = true;
727
+ const chunk = {
728
+ partial_item: {
729
+ response_type: MessageResponseTypes.TEXT,
730
+ text: "",
731
+ streaming_metadata: {
732
+ id: this.state.itemId
733
+ }
734
+ },
735
+ partial_response: {
736
+ message_options: {
737
+ response_user_profile: this.agentProfile
738
+ }
739
+ },
740
+ streaming_metadata: {
741
+ response_id: this.state.responseId
742
+ }
743
+ };
744
+ if (includeReasoning) {
745
+ chunk.partial_response.message_options.reasoning = { steps: [] };
746
+ }
747
+ return chunk;
748
+ }
749
+ // ===========================================================================
750
+ // A2A PART TRANSLATION
751
+ // ===========================================================================
752
+ /**
753
+ * Translate a single streaming A2A part to a legacy Carbon message
754
+ * Used for real-time streaming updates (backward compatibility with EnhancedChatWrapper)
755
+ * @param part The A2A part to translate
756
+ * @param metadata Optional extension metadata from the artifact
757
+ */
758
+ translateStreamingPart(part, metadata) {
759
+ const contentType = part.metadata?.content_type;
760
+ if ((contentType === "thinking" || contentType === "reasoning_step") && part.kind === "text" && part.text) {
761
+ return {
762
+ response_type: "reasoning_steps",
763
+ reasoning_steps: {
764
+ steps: [{ content: part.text }],
765
+ openState: "closed"
766
+ },
767
+ metadata
768
+ };
769
+ }
770
+ if (part.kind === "data" && part.data) {
771
+ const dataType = part.data.type;
772
+ if (dataType === "tool_call") {
773
+ const toolData = part.data;
774
+ return {
775
+ response_type: "chain_of_thought",
776
+ chain_of_thought: {
777
+ steps: [{
778
+ title: toolData.tool_name || "Tool Call",
779
+ description: `Calling ${toolData.tool_name || "tool"}`,
780
+ tool_name: toolData.tool_name,
781
+ request: { args: toolData.args },
782
+ status: "in_progress" /* IN_PROGRESS */
783
+ }]
784
+ },
785
+ metadata
786
+ };
787
+ }
788
+ if (dataType === "tool_result") {
789
+ const resultData = part.data;
790
+ return {
791
+ response_type: "chain_of_thought",
792
+ chain_of_thought: {
793
+ steps: [{
794
+ title: resultData.tool_name || "Tool Result",
795
+ description: `Result from ${resultData.tool_name || "tool"}`,
796
+ tool_name: resultData.tool_name,
797
+ response: { content: resultData.result_preview },
798
+ status: resultData.success === false ? "error" /* ERROR */ : "success" /* SUCCESS */
799
+ }]
800
+ },
801
+ metadata
802
+ };
803
+ }
804
+ }
805
+ if (part.kind === "text" && part.text) {
806
+ return {
807
+ response_type: "text",
808
+ text: part.text,
809
+ metadata
810
+ };
811
+ }
812
+ if (part.kind === "file" && part.file) {
813
+ return {
814
+ response_type: "user_defined",
815
+ user_defined: {
816
+ type: "file_attachment",
817
+ fileName: part.file.name,
818
+ mimeType: part.file.mimeType,
819
+ downloadUrl: part.file.uri || `data:${part.file.mimeType};base64,${part.file.bytes}`
820
+ },
821
+ metadata
822
+ };
823
+ }
824
+ return null;
825
+ }
826
+ /**
827
+ * Translate an A2A message part to Carbon format
828
+ *
829
+ * @param part - The A2A message part to translate
830
+ * @param artifactMetadata - Optional metadata from artifact level
831
+ * @returns Carbon stream chunk or null if no action needed
832
+ */
833
+ translatePart(part, artifactMetadata) {
834
+ const contentType = part.metadata?.content_type;
835
+ if ((contentType === "thinking" || contentType === "reasoning_step") && part.kind === "text" && part.text) {
836
+ return this.translateThinkingPart(part.text, part.metadata);
837
+ }
838
+ const partExtensions = parseUIExtensions(part.metadata);
839
+ if (partExtensions.trajectory) {
840
+ return this.translateTrajectoryPart(partExtensions.trajectory);
841
+ }
842
+ if (part.kind === "data" && part.data) {
843
+ const dataType = part.data.type;
844
+ if (dataType === "tool_call") {
845
+ return this.translateToolCallPart(part.data);
846
+ }
847
+ if (dataType === "tool_result") {
848
+ return this.translateToolResultPart(part.data);
849
+ }
850
+ }
851
+ if (part.kind === "text" && part.text) {
852
+ if (contentType === "thinking" || contentType === "reasoning_step") {
853
+ return null;
854
+ }
855
+ return this.translateTextPart(part.text, artifactMetadata);
856
+ }
857
+ if (part.kind === "file" && part.file) {
858
+ return this.translateFilePart(part.file);
859
+ }
860
+ return null;
861
+ }
862
+ /**
863
+ * Translate thinking/reasoning content to Carbon reasoning step
864
+ */
865
+ translateThinkingPart(text, metadata) {
866
+ const stepNumber = metadata?.step;
867
+ const stepTitle = metadata?.title;
868
+ const newStep = {
869
+ title: stepTitle || `Thinking Step ${(stepNumber ?? this.state.reasoningSteps.length) + 1}`,
870
+ content: text,
871
+ open_state: "default" /* DEFAULT */
872
+ };
873
+ this.state.reasoningSteps.push(newStep);
874
+ return {
875
+ partial_item: {
876
+ response_type: MessageResponseTypes.TEXT,
877
+ text: "",
878
+ streaming_metadata: {
879
+ id: this.state.itemId,
880
+ cancellable: true
881
+ }
882
+ },
883
+ partial_response: {
884
+ message_options: {
885
+ response_user_profile: this.agentProfile,
886
+ reasoning: {
887
+ steps: this.state.reasoningSteps,
888
+ open_state: "default" /* DEFAULT */
889
+ // Auto-expand during streaming
890
+ }
891
+ }
892
+ },
893
+ streaming_metadata: {
894
+ response_id: this.state.responseId
895
+ }
896
+ };
897
+ }
898
+ /**
899
+ * Translate trajectory extension to Carbon reasoning step
900
+ */
901
+ translateTrajectoryPart(trajectory) {
902
+ const newStep = {
903
+ title: trajectory.title || "Processing",
904
+ content: trajectory.content || "",
905
+ open_state: "default" /* DEFAULT */
906
+ };
907
+ this.state.reasoningSteps.push(newStep);
908
+ return {
909
+ partial_item: {
910
+ response_type: MessageResponseTypes.TEXT,
911
+ text: "",
912
+ streaming_metadata: {
913
+ id: this.state.itemId,
914
+ cancellable: true
915
+ }
916
+ },
917
+ partial_response: {
918
+ message_options: {
919
+ response_user_profile: this.agentProfile,
920
+ reasoning: {
921
+ steps: this.state.reasoningSteps,
922
+ open_state: "default" /* DEFAULT */
923
+ // Auto-expand during streaming
924
+ }
925
+ }
926
+ },
927
+ streaming_metadata: {
928
+ response_id: this.state.responseId
929
+ }
930
+ };
931
+ }
932
+ /**
933
+ * Translate tool call to Carbon chain of thought (IN_PROGRESS)
934
+ */
935
+ translateToolCallPart(toolData) {
936
+ const toolName = toolData.tool_name || "tool";
937
+ const cotStep = {
938
+ title: toolName,
939
+ description: `Calling ${toolName}`,
940
+ tool_name: toolName,
941
+ request: { args: toolData.args },
942
+ status: "in_progress" /* IN_PROGRESS */
943
+ };
944
+ const index = this.state.chainOfThought.length;
945
+ this.state.chainOfThought.push(cotStep);
946
+ this.state.pendingToolCalls.set(toolName, index);
947
+ return {
948
+ partial_item: {
949
+ response_type: MessageResponseTypes.TEXT,
950
+ text: "",
951
+ streaming_metadata: {
952
+ id: this.state.itemId,
953
+ cancellable: true
954
+ }
955
+ },
956
+ partial_response: {
957
+ message_options: {
958
+ response_user_profile: this.agentProfile,
959
+ chain_of_thought: this.state.chainOfThought
960
+ }
961
+ },
962
+ streaming_metadata: {
963
+ response_id: this.state.responseId
964
+ }
965
+ };
966
+ }
967
+ /**
968
+ * Translate tool result to Carbon chain of thought (SUCCESS/ERROR)
969
+ */
970
+ translateToolResultPart(resultData) {
971
+ const toolName = resultData.tool_name || "tool";
972
+ const index = this.state.pendingToolCalls.get(toolName);
973
+ if (index !== void 0 && index < this.state.chainOfThought.length) {
974
+ this.state.chainOfThought[index] = {
975
+ ...this.state.chainOfThought[index],
976
+ response: { content: resultData.result_preview },
977
+ status: resultData.success === false ? "error" /* ERROR */ : "success" /* SUCCESS */
978
+ };
979
+ this.state.pendingToolCalls.delete(toolName);
980
+ } else {
981
+ const cotStep = {
982
+ title: toolName,
983
+ description: `Result from ${toolName}`,
984
+ tool_name: toolName,
985
+ response: { content: resultData.result_preview },
986
+ status: resultData.success === false ? "error" /* ERROR */ : "success" /* SUCCESS */
987
+ };
988
+ this.state.chainOfThought.push(cotStep);
989
+ }
990
+ return {
991
+ partial_item: {
992
+ response_type: MessageResponseTypes.TEXT,
993
+ text: "",
994
+ streaming_metadata: {
995
+ id: this.state.itemId,
996
+ cancellable: true
997
+ }
998
+ },
999
+ partial_response: {
1000
+ message_options: {
1001
+ response_user_profile: this.agentProfile,
1002
+ chain_of_thought: this.state.chainOfThought
1003
+ }
1004
+ },
1005
+ streaming_metadata: {
1006
+ response_id: this.state.responseId
1007
+ }
1008
+ };
1009
+ }
1010
+ /**
1011
+ * Translate text part to Carbon partial item
1012
+ */
1013
+ translateTextPart(text, _artifactMetadata) {
1014
+ this.state.accumulatedText += text;
1015
+ this.state.hasStartedStreaming = true;
1016
+ return {
1017
+ partial_item: {
1018
+ response_type: MessageResponseTypes.TEXT,
1019
+ text,
1020
+ // Send just the new text chunk
1021
+ streaming_metadata: {
1022
+ id: this.state.itemId,
1023
+ cancellable: true
1024
+ }
1025
+ },
1026
+ partial_response: {
1027
+ message_options: {
1028
+ response_user_profile: this.agentProfile
1029
+ }
1030
+ },
1031
+ streaming_metadata: {
1032
+ response_id: this.state.responseId
1033
+ }
1034
+ };
1035
+ }
1036
+ /**
1037
+ * Translate file part to Carbon user defined item
1038
+ */
1039
+ translateFilePart(file) {
1040
+ return {
1041
+ partial_item: {
1042
+ response_type: MessageResponseTypes.USER_DEFINED,
1043
+ user_defined: {
1044
+ type: "file_attachment",
1045
+ fileName: file.name,
1046
+ mimeType: file.mimeType,
1047
+ downloadUrl: file.uri || `data:${file.mimeType};base64,${file.bytes}`
1048
+ },
1049
+ streaming_metadata: {
1050
+ id: `file-${Date.now()}`
1051
+ }
1052
+ },
1053
+ streaming_metadata: {
1054
+ response_id: this.state.responseId
1055
+ }
1056
+ };
1057
+ }
1058
+ // ===========================================================================
1059
+ // COMPLETE AND FINAL RESPONSE
1060
+ // ===========================================================================
1061
+ /**
1062
+ * Create complete item chunk
1063
+ *
1064
+ * Call this to finalize a specific item while other items may still be streaming.
1065
+ * Optional but useful for accessibility and corrections.
1066
+ */
1067
+ createCompleteItem(wasStopped = false) {
1068
+ return {
1069
+ complete_item: {
1070
+ response_type: MessageResponseTypes.TEXT,
1071
+ text: this.state.accumulatedText,
1072
+ streaming_metadata: {
1073
+ id: this.state.itemId,
1074
+ stream_stopped: wasStopped
1075
+ }
1076
+ },
1077
+ partial_response: {
1078
+ message_options: {
1079
+ response_user_profile: this.agentProfile,
1080
+ reasoning: this.state.reasoningSteps.length > 0 ? {
1081
+ steps: this.state.reasoningSteps,
1082
+ open_state: "close" /* CLOSE */
1083
+ // Collapse after completion
1084
+ } : void 0,
1085
+ chain_of_thought: this.state.chainOfThought.length > 0 ? this.state.chainOfThought : void 0
1086
+ }
1087
+ },
1088
+ streaming_metadata: {
1089
+ response_id: this.state.responseId
1090
+ }
1091
+ };
1092
+ }
1093
+ /**
1094
+ * Create final response chunk
1095
+ *
1096
+ * CRITICAL: This MUST be called to end streaming and clear the typing indicator.
1097
+ * Without this, the UI will remain in a loading state.
1098
+ */
1099
+ createFinalResponse() {
1100
+ return {
1101
+ final_response: {
1102
+ id: this.state.responseId,
1103
+ output: {
1104
+ generic: [
1105
+ {
1106
+ response_type: MessageResponseTypes.TEXT,
1107
+ text: this.state.accumulatedText,
1108
+ streaming_metadata: {
1109
+ id: this.state.itemId
1110
+ }
1111
+ }
1112
+ ]
1113
+ },
1114
+ message_options: {
1115
+ response_user_profile: this.agentProfile,
1116
+ reasoning: this.state.reasoningSteps.length > 0 ? {
1117
+ steps: this.state.reasoningSteps,
1118
+ open_state: "close" /* CLOSE */
1119
+ // Collapse after completion
1120
+ } : void 0,
1121
+ chain_of_thought: this.state.chainOfThought.length > 0 ? this.state.chainOfThought : void 0
1122
+ }
1123
+ }
1124
+ };
1125
+ }
1126
+ /**
1127
+ * Create error response
1128
+ */
1129
+ createErrorResponse(errorMessage) {
1130
+ return {
1131
+ final_response: {
1132
+ id: this.state.responseId,
1133
+ output: {
1134
+ generic: [
1135
+ {
1136
+ response_type: MessageResponseTypes.TEXT,
1137
+ text: errorMessage,
1138
+ streaming_metadata: {
1139
+ id: this.state.itemId
1140
+ }
1141
+ }
1142
+ ]
1143
+ },
1144
+ message_options: {
1145
+ response_user_profile: this.agentProfile
1146
+ }
1147
+ }
1148
+ };
1149
+ }
1150
+ // ===========================================================================
1151
+ // CITATION HANDLING
1152
+ // ===========================================================================
1153
+ /**
1154
+ * Create citations message
1155
+ *
1156
+ * Call this after the main response to add a sources list.
1157
+ */
1158
+ createCitationsMessage(citations) {
1159
+ return {
1160
+ output: {
1161
+ generic: [
1162
+ {
1163
+ response_type: MessageResponseTypes.USER_DEFINED,
1164
+ user_defined: {
1165
+ type: "sources_list",
1166
+ citations
1167
+ }
1168
+ }
1169
+ ]
1170
+ },
1171
+ message_options: {
1172
+ response_user_profile: this.agentProfile
1173
+ }
1174
+ };
1175
+ }
1176
+ // ===========================================================================
1177
+ // FULL A2A STREAM CHUNK TRANSLATION
1178
+ // ===========================================================================
1179
+ /**
1180
+ * Translate a complete A2A stream chunk to Carbon format
1181
+ *
1182
+ * This is the main entry point for processing A2A SSE events.
1183
+ * Returns an array of Carbon chunks since one A2A event may produce
1184
+ * multiple Carbon updates (e.g., multiple parts in a message).
1185
+ */
1186
+ translateStreamChunk(chunk) {
1187
+ const results = [];
1188
+ if (!this.state.shellMessageSent) {
1189
+ results.push(this.createShellMessage());
1190
+ }
1191
+ if (chunk.kind === "status-update" && chunk.status?.message) {
1192
+ const message = chunk.status.message;
1193
+ for (const part of message.parts) {
1194
+ const carbonChunk = this.translatePart(
1195
+ part,
1196
+ message.metadata
1197
+ );
1198
+ if (carbonChunk) {
1199
+ results.push(carbonChunk);
1200
+ }
1201
+ }
1202
+ }
1203
+ if (chunk.kind === "artifact-update" && chunk.artifact?.parts) {
1204
+ for (const part of chunk.artifact.parts) {
1205
+ const carbonChunk = this.translatePart(
1206
+ part,
1207
+ chunk.artifact.metadata
1208
+ );
1209
+ if (carbonChunk) {
1210
+ results.push(carbonChunk);
1211
+ }
1212
+ }
1213
+ }
1214
+ if (chunk.final === true || chunk.status?.state === "completed") {
1215
+ results.push(this.createFinalResponse());
1216
+ }
1217
+ return results;
1218
+ }
1219
+ };
1220
+ function createTranslator(agentName, agentIconUrl) {
1221
+ return new A2AToCarbonTranslator({
1222
+ nickname: agentName,
1223
+ profile_picture_url: agentIconUrl
1224
+ });
1225
+ }
1226
+ function isFinalResponse(chunk) {
1227
+ return "final_response" in chunk;
1228
+ }
1229
+ function isPartialItem(chunk) {
1230
+ return "partial_item" in chunk;
1231
+ }
1232
+ function isCompleteItem(chunk) {
1233
+ return "complete_item" in chunk;
1234
+ }
1235
+
1236
+ // src/components/renderers/citation-utils.ts
1237
+ function generateCitationId() {
1238
+ return `cite-${Math.random().toString(36).substring(2, 9)}`;
1239
+ }
1240
+ function processCitations(text, citations) {
1241
+ const sortedCitations = [...citations].filter((c) => c.start_index != null && c.end_index != null).sort((a, b) => (a.start_index ?? 0) - (b.start_index ?? 0));
1242
+ return sortedCitations.map((citation, index) => ({
1243
+ ...citation,
1244
+ id: generateCitationId(),
1245
+ number: index + 1,
1246
+ highlightedText: text.substring(citation.start_index ?? 0, citation.end_index ?? text.length)
1247
+ }));
1248
+ }
1249
+ function segmentTextWithCitations(text, citations) {
1250
+ const processedCitations = processCitations(text, citations);
1251
+ if (processedCitations.length === 0) {
1252
+ return [{ text, isCited: false }];
1253
+ }
1254
+ const segments = [];
1255
+ let currentIndex = 0;
1256
+ for (const citation of processedCitations) {
1257
+ const startIndex = citation.start_index ?? 0;
1258
+ const endIndex = citation.end_index ?? text.length;
1259
+ if (currentIndex < startIndex) {
1260
+ segments.push({
1261
+ text: text.substring(currentIndex, startIndex),
1262
+ isCited: false
1263
+ });
1264
+ }
1265
+ segments.push({
1266
+ text: text.substring(startIndex, endIndex),
1267
+ citation,
1268
+ isCited: true
1269
+ });
1270
+ currentIndex = endIndex;
1271
+ }
1272
+ if (currentIndex < text.length) {
1273
+ segments.push({
1274
+ text: text.substring(currentIndex),
1275
+ isCited: false
1276
+ });
1277
+ }
1278
+ return segments;
1279
+ }
1280
+ function getUniqueCitations(citations) {
1281
+ const seen = /* @__PURE__ */ new Set();
1282
+ return citations.filter((citation) => {
1283
+ if (!citation.url || seen.has(citation.url)) {
1284
+ return false;
1285
+ }
1286
+ seen.add(citation.url);
1287
+ return true;
1288
+ });
1289
+ }
1290
+ var CitationTooltip = ({ citation, position }) => {
1291
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1292
+ "div",
1293
+ {
1294
+ className: "fixed z-50 max-w-xs p-3 bg-gray-900 text-white rounded-lg shadow-lg text-sm",
1295
+ style: {
1296
+ left: position.x,
1297
+ top: position.y + 20,
1298
+ transform: "translateX(-50%)"
1299
+ },
1300
+ children: [
1301
+ citation.title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: citation.title }),
1302
+ citation.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-300 text-xs mb-2", children: citation.description }),
1303
+ citation.url && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-300 text-xs truncate", children: citation.url }),
1304
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-2 left-1/2 transform -translate-x-1/2 border-8 border-transparent border-b-gray-900" })
1305
+ ]
1306
+ }
1307
+ );
1308
+ };
1309
+ var CitationHighlight = ({ segment, onHover }) => {
1310
+ if (!segment.isCited || !segment.citation) {
1311
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: segment.text });
1312
+ }
1313
+ const handleMouseEnter = (e) => {
1314
+ onHover(segment.citation, e);
1315
+ };
1316
+ const handleMouseLeave = () => {
1317
+ onHover(null, {});
1318
+ };
1319
+ const handleClick = () => {
1320
+ if (segment.citation?.url) {
1321
+ window.open(segment.citation.url, "_blank", "noopener,noreferrer");
1322
+ }
1323
+ };
1324
+ const handleKeyDown = (e) => {
1325
+ if (e.key === "Enter" || e.key === " ") {
1326
+ e.preventDefault();
1327
+ handleClick();
1328
+ }
1329
+ };
1330
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1331
+ "span",
1332
+ {
1333
+ className: "bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 cursor-pointer hover:bg-blue-200 dark:hover:bg-blue-800/40 transition-colors rounded px-0.5",
1334
+ onMouseEnter: handleMouseEnter,
1335
+ onMouseLeave: handleMouseLeave,
1336
+ onClick: handleClick,
1337
+ onKeyDown: handleKeyDown,
1338
+ role: "button",
1339
+ tabIndex: 0,
1340
+ "aria-label": `Citation ${segment.citation.number}: ${segment.citation.title || "Source"}`,
1341
+ children: [
1342
+ segment.text,
1343
+ /* @__PURE__ */ jsxRuntime.jsxs("sup", { className: "text-xs font-semibold ml-0.5", children: [
1344
+ "[",
1345
+ segment.citation.number,
1346
+ "]"
1347
+ ] })
1348
+ ]
1349
+ }
1350
+ );
1351
+ };
1352
+ var SourcesList = ({ citations }) => {
1353
+ const uniqueCitations = getUniqueCitations(citations);
1354
+ if (uniqueCitations.length === 0) {
1355
+ return null;
1356
+ }
1357
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 pt-4 border-t border-gray-200 dark:border-gray-700", children: [
1358
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold text-gray-600 dark:text-gray-400 mb-2", children: "Sources" }),
1359
+ /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "list-decimal list-inside space-y-1", children: uniqueCitations.map((citation) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "text-sm", children: [
1360
+ /* @__PURE__ */ jsxRuntime.jsx(
1361
+ "a",
1362
+ {
1363
+ href: citation.url || "#",
1364
+ target: "_blank",
1365
+ rel: "noopener noreferrer",
1366
+ className: "text-blue-600 dark:text-blue-400 hover:underline",
1367
+ children: citation.title || citation.url || `Source ${citation.number}`
1368
+ }
1369
+ ),
1370
+ citation.description && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-gray-500 dark:text-gray-400 ml-2", children: [
1371
+ "\u2014 ",
1372
+ citation.description
1373
+ ] })
1374
+ ] }, citation.id)) })
1375
+ ] });
1376
+ };
1377
+ var CitationRenderer = ({ text, citations, className = "" }) => {
1378
+ const [hoveredCitation, setHoveredCitation] = react.useState(null);
1379
+ const [tooltipPosition, setTooltipPosition] = react.useState({ x: 0, y: 0 });
1380
+ const segments = segmentTextWithCitations(text, citations);
1381
+ const processedCitations = processCitations(text, citations);
1382
+ const handleHover = react.useCallback((citation, event) => {
1383
+ setHoveredCitation(citation);
1384
+ if (citation && event.currentTarget) {
1385
+ const rect = event.currentTarget.getBoundingClientRect();
1386
+ setTooltipPosition({
1387
+ x: rect.left + rect.width / 2,
1388
+ y: rect.bottom
1389
+ });
1390
+ }
1391
+ }, []);
1392
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative ${className}`, children: [
1393
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "prose dark:prose-invert max-w-none", children: segments.map((segment, index) => /* @__PURE__ */ jsxRuntime.jsx(CitationHighlight, { segment, onHover: handleHover }, index)) }),
1394
+ hoveredCitation && /* @__PURE__ */ jsxRuntime.jsx(CitationTooltip, { citation: hoveredCitation, position: tooltipPosition }),
1395
+ /* @__PURE__ */ jsxRuntime.jsx(SourcesList, { citations: processedCitations })
1396
+ ] });
1397
+ };
1398
+ var ErrorRenderer = ({ error, className = "" }) => {
1399
+ const [showDetails, setShowDetails] = react.useState(false);
1400
+ const hasDetails = error.stacktrace || error.context;
1401
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `border border-red-200 bg-red-50 dark:bg-red-900/20 dark:border-red-800 rounded-lg p-4 ${className}`, children: [
1402
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
1403
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 text-red-500 dark:text-red-400", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-5 h-5", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsxRuntime.jsx(
1404
+ "path",
1405
+ {
1406
+ fillRule: "evenodd",
1407
+ d: "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z",
1408
+ clipRule: "evenodd"
1409
+ }
1410
+ ) }) }),
1411
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
1412
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1413
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold text-red-800 dark:text-red-200", children: error.code ? `Error: ${error.code}` : "Error" }),
1414
+ hasDetails && /* @__PURE__ */ jsxRuntime.jsx(
1415
+ "button",
1416
+ {
1417
+ onClick: () => setShowDetails(!showDetails),
1418
+ className: "text-xs text-red-600 dark:text-red-400 hover:underline",
1419
+ children: showDetails ? "Hide details" : "Show details"
1420
+ }
1421
+ )
1422
+ ] }),
1423
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-red-700 dark:text-red-300", children: error.message })
1424
+ ] })
1425
+ ] }),
1426
+ hasDetails && showDetails && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 pt-4 border-t border-red-200 dark:border-red-800", children: [
1427
+ error.stacktrace && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3", children: [
1428
+ /* @__PURE__ */ jsxRuntime.jsx("h5", { className: "text-xs font-semibold text-red-600 dark:text-red-400 mb-1", children: "Stack Trace" }),
1429
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs bg-red-100 dark:bg-red-900/40 p-3 rounded overflow-x-auto text-red-800 dark:text-red-200", children: error.stacktrace })
1430
+ ] }),
1431
+ error.context && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1432
+ /* @__PURE__ */ jsxRuntime.jsx("h5", { className: "text-xs font-semibold text-red-600 dark:text-red-400 mb-1", children: "Context" }),
1433
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs bg-red-100 dark:bg-red-900/40 p-3 rounded overflow-x-auto text-red-800 dark:text-red-200", children: JSON.stringify(error.context, null, 2) })
1434
+ ] })
1435
+ ] })
1436
+ ] });
1437
+ };
1438
+ var FormFieldRenderer = ({ field, value, onChange }) => {
1439
+ const handleChange = react.useCallback(
1440
+ (newValue) => {
1441
+ onChange(field.id, newValue);
1442
+ },
1443
+ [field.id, onChange]
1444
+ );
1445
+ const baseInputClasses = "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none";
1446
+ switch (field.type) {
1447
+ case "text":
1448
+ return /* @__PURE__ */ jsxRuntime.jsx(
1449
+ "input",
1450
+ {
1451
+ type: "text",
1452
+ id: field.id,
1453
+ value: value || "",
1454
+ onChange: (e) => handleChange(e.target.value),
1455
+ placeholder: field.description,
1456
+ required: field.required,
1457
+ className: baseInputClasses
1458
+ }
1459
+ );
1460
+ case "date":
1461
+ return /* @__PURE__ */ jsxRuntime.jsx(
1462
+ "input",
1463
+ {
1464
+ type: "date",
1465
+ id: field.id,
1466
+ value: value || "",
1467
+ onChange: (e) => handleChange(e.target.value),
1468
+ required: field.required,
1469
+ className: baseInputClasses
1470
+ }
1471
+ );
1472
+ case "file":
1473
+ return /* @__PURE__ */ jsxRuntime.jsx(
1474
+ "input",
1475
+ {
1476
+ type: "file",
1477
+ id: field.id,
1478
+ onChange: (e) => {
1479
+ const file = e.target.files?.[0];
1480
+ if (file) {
1481
+ const reader = new FileReader();
1482
+ reader.onload = () => {
1483
+ handleChange({
1484
+ name: file.name,
1485
+ type: file.type,
1486
+ size: file.size,
1487
+ data: reader.result
1488
+ });
1489
+ };
1490
+ reader.readAsDataURL(file);
1491
+ }
1492
+ },
1493
+ required: field.required,
1494
+ className: baseInputClasses
1495
+ }
1496
+ );
1497
+ case "single_select":
1498
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1499
+ "select",
1500
+ {
1501
+ id: field.id,
1502
+ value: value || "",
1503
+ onChange: (e) => handleChange(e.target.value),
1504
+ required: field.required,
1505
+ className: baseInputClasses,
1506
+ children: [
1507
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select..." }),
1508
+ field.options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, children: option.label }, option.value))
1509
+ ]
1510
+ }
1511
+ );
1512
+ case "multi_select":
1513
+ return /* @__PURE__ */ jsxRuntime.jsx(
1514
+ "select",
1515
+ {
1516
+ id: field.id,
1517
+ multiple: true,
1518
+ value: value || [],
1519
+ onChange: (e) => {
1520
+ const selected = Array.from(e.target.selectedOptions, (option) => option.value);
1521
+ handleChange(selected);
1522
+ },
1523
+ required: field.required,
1524
+ className: `${baseInputClasses} min-h-[100px]`,
1525
+ children: field.options?.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, children: option.label }, option.value))
1526
+ }
1527
+ );
1528
+ case "checkbox":
1529
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [
1530
+ /* @__PURE__ */ jsxRuntime.jsx(
1531
+ "input",
1532
+ {
1533
+ type: "checkbox",
1534
+ id: field.id,
1535
+ checked: Boolean(value),
1536
+ onChange: (e) => handleChange(e.target.checked),
1537
+ required: field.required,
1538
+ className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
1539
+ }
1540
+ ),
1541
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: field.description })
1542
+ ] });
1543
+ case "checkbox_group":
1544
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: field.options?.map((option) => {
1545
+ const selectedValues = value || [];
1546
+ const isChecked = selectedValues.includes(option.value);
1547
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 cursor-pointer", children: [
1548
+ /* @__PURE__ */ jsxRuntime.jsx(
1549
+ "input",
1550
+ {
1551
+ type: "checkbox",
1552
+ checked: isChecked,
1553
+ onChange: (e) => {
1554
+ const newValues = e.target.checked ? [...selectedValues, option.value] : selectedValues.filter((v) => v !== option.value);
1555
+ handleChange(newValues);
1556
+ },
1557
+ className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
1558
+ }
1559
+ ),
1560
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: option.label })
1561
+ ] }, option.value);
1562
+ }) });
1563
+ default:
1564
+ return /* @__PURE__ */ jsxRuntime.jsx(
1565
+ "input",
1566
+ {
1567
+ type: "text",
1568
+ id: field.id,
1569
+ value: value || "",
1570
+ onChange: (e) => handleChange(e.target.value),
1571
+ className: baseInputClasses
1572
+ }
1573
+ );
1574
+ }
1575
+ };
1576
+ var FormRenderer = ({ form, onSubmit, onCancel, className = "" }) => {
1577
+ const [values, setValues] = react.useState(() => {
1578
+ const initial = {};
1579
+ form.fields.forEach((field) => {
1580
+ if (field.default_value !== void 0) {
1581
+ initial[field.id] = field.default_value;
1582
+ }
1583
+ });
1584
+ return initial;
1585
+ });
1586
+ const [isSubmitting, setIsSubmitting] = react.useState(false);
1587
+ const handleFieldChange = react.useCallback((fieldId, value) => {
1588
+ setValues((prev) => ({ ...prev, [fieldId]: value }));
1589
+ }, []);
1590
+ const handleSubmit = react.useCallback(
1591
+ async (e) => {
1592
+ e.preventDefault();
1593
+ setIsSubmitting(true);
1594
+ try {
1595
+ await onSubmit(values);
1596
+ } finally {
1597
+ setIsSubmitting(false);
1598
+ }
1599
+ },
1600
+ [values, onSubmit]
1601
+ );
1602
+ const gridCols = form.columns || 1;
1603
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 p-6 ${className}`, children: [
1604
+ (form.title || form.description) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
1605
+ form.title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-gray-100", children: form.title }),
1606
+ form.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: form.description })
1607
+ ] }),
1608
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [
1609
+ /* @__PURE__ */ jsxRuntime.jsx(
1610
+ "div",
1611
+ {
1612
+ className: "grid gap-4",
1613
+ style: {
1614
+ gridTemplateColumns: `repeat(${gridCols}, minmax(0, 1fr))`
1615
+ },
1616
+ children: form.fields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs(
1617
+ "div",
1618
+ {
1619
+ style: {
1620
+ gridColumn: field.col_span ? `span ${field.col_span}` : void 0
1621
+ },
1622
+ children: [
1623
+ field.type !== "checkbox" && /* @__PURE__ */ jsxRuntime.jsxs(
1624
+ "label",
1625
+ {
1626
+ htmlFor: field.id,
1627
+ className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1",
1628
+ children: [
1629
+ field.label,
1630
+ field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 ml-1", children: "*" })
1631
+ ]
1632
+ }
1633
+ ),
1634
+ /* @__PURE__ */ jsxRuntime.jsx(FormFieldRenderer, { field, value: values[field.id], onChange: handleFieldChange }),
1635
+ field.description && field.type !== "checkbox" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: field.description })
1636
+ ]
1637
+ },
1638
+ field.id
1639
+ ))
1640
+ }
1641
+ ),
1642
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 flex items-center justify-end gap-3", children: [
1643
+ onCancel && /* @__PURE__ */ jsxRuntime.jsx(
1644
+ "button",
1645
+ {
1646
+ type: "button",
1647
+ onClick: onCancel,
1648
+ disabled: isSubmitting,
1649
+ className: "px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50",
1650
+ children: "Cancel"
1651
+ }
1652
+ ),
1653
+ /* @__PURE__ */ jsxRuntime.jsx(
1654
+ "button",
1655
+ {
1656
+ type: "submit",
1657
+ disabled: isSubmitting,
1658
+ className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50",
1659
+ children: isSubmitting ? "Submitting..." : form.submit_label || "Submit"
1660
+ }
1661
+ )
1662
+ ] })
1663
+ ] })
1664
+ ] });
1665
+ };
1666
+ function A2AChat({
1667
+ // Agent configuration
1668
+ agent: agentProp,
1669
+ agentId,
1670
+ agentUrl,
1671
+ apiKey,
1672
+ // Display options
1673
+ layout = "fullscreen",
1674
+ allowLayoutSwitch = false,
1675
+ defaultOpen = true,
1676
+ className = "",
1677
+ agentName,
1678
+ agentIconUrl,
1679
+ // Behavior options
1680
+ showThinking = true,
1681
+ showChainOfThought = true,
1682
+ allowCancel = true,
1683
+ extensions,
1684
+ // Callbacks
1685
+ onOpen,
1686
+ onClose,
1687
+ onSend,
1688
+ onResponse,
1689
+ onConnectionChange,
1690
+ onError,
1691
+ onDisconnect,
1692
+ // Custom renderers
1693
+ renderCitations,
1694
+ renderError,
1695
+ renderForm,
1696
+ renderUserDefined
1697
+ }) {
1698
+ const agentContext = useAgentContextOptional();
1699
+ const agent = react.useMemo(() => {
1700
+ if (agentProp) {
1701
+ return agentProp;
1702
+ }
1703
+ if (agentId && agentContext) {
1704
+ return agentContext.getAgent(agentId) ?? null;
1705
+ }
1706
+ if (!agentProp && !agentId && agentContext?.currentAgent) {
1707
+ return agentContext.currentAgent;
1708
+ }
1709
+ if (agentUrl) {
1710
+ return {
1711
+ id: "default",
1712
+ name: agentName ?? "AI Assistant",
1713
+ url: agentUrl,
1714
+ apiKey,
1715
+ iconUrl: agentIconUrl
1716
+ };
1717
+ }
1718
+ return null;
1719
+ }, [agentProp, agentId, agentUrl, apiKey, agentName, agentIconUrl, agentContext]);
1720
+ const [connectionState, setConnectionState] = react.useState("disconnected");
1721
+ const [isStreaming, setIsStreaming] = react.useState(false);
1722
+ const [currentFormRequest, setCurrentFormRequest] = react.useState(null);
1723
+ const [pendingTaskId, setPendingTaskId] = react.useState(null);
1724
+ const [carbonLoaded, setCarbonLoaded] = react.useState(false);
1725
+ const [CarbonComponents, setCarbonComponents] = react.useState({});
1726
+ const instanceRef = react.useRef(null);
1727
+ const translatorRef = react.useRef(null);
1728
+ const abortControllerRef = react.useRef(null);
1729
+ react.useEffect(() => {
1730
+ if (typeof window !== "undefined") {
1731
+ import('@carbon/ai-chat').then((mod) => {
1732
+ setCarbonComponents({
1733
+ ChatCustomElement: mod.ChatCustomElement,
1734
+ ChatContainer: mod.ChatContainer
1735
+ });
1736
+ setCarbonLoaded(true);
1737
+ }).catch((err) => {
1738
+ console.error("[A2AChat] Failed to load @carbon/ai-chat:", err);
1739
+ });
1740
+ }
1741
+ }, []);
1742
+ react.useEffect(() => {
1743
+ onConnectionChange?.(connectionState);
1744
+ }, [connectionState, onConnectionChange]);
1745
+ const handleSendMessage = react.useCallback(
1746
+ async (message) => {
1747
+ if (!agent) {
1748
+ console.error("[A2AChat] No agent configured");
1749
+ return;
1750
+ }
1751
+ const instance = instanceRef.current;
1752
+ if (!instance) {
1753
+ console.error("[A2AChat] Chat instance not ready");
1754
+ return;
1755
+ }
1756
+ onSend?.(message);
1757
+ setIsStreaming(true);
1758
+ setConnectionState("streaming");
1759
+ translatorRef.current = createTranslator(
1760
+ agent.name,
1761
+ agent.iconUrl ?? "/bot.svg"
1762
+ );
1763
+ abortControllerRef.current = new AbortController();
1764
+ try {
1765
+ const response = await fetch("/api/agent/chat", {
1766
+ method: "POST",
1767
+ headers: { "Content-Type": "application/json" },
1768
+ body: JSON.stringify({
1769
+ agentUrl: agent.url,
1770
+ apiKey: agent.apiKey,
1771
+ message,
1772
+ extensions: extensions ?? agent.extensions
1773
+ }),
1774
+ signal: abortControllerRef.current.signal
1775
+ });
1776
+ if (!response.ok) {
1777
+ throw new Error(`Request failed: ${response.status}`);
1778
+ }
1779
+ if (!response.body) {
1780
+ throw new Error("Response body is null");
1781
+ }
1782
+ const reader = response.body.getReader();
1783
+ const decoder = new TextDecoder();
1784
+ let buffer = "";
1785
+ while (true) {
1786
+ const { done, value } = await reader.read();
1787
+ if (done) break;
1788
+ buffer += decoder.decode(value, { stream: true });
1789
+ const lines = buffer.split("\n");
1790
+ buffer = lines.pop() || "";
1791
+ for (const line of lines) {
1792
+ const trimmedLine = line.trim();
1793
+ if (trimmedLine.startsWith("data: ")) {
1794
+ const dataStr = trimmedLine.slice(6);
1795
+ if (!dataStr || dataStr === "[DONE]") continue;
1796
+ try {
1797
+ const data = JSON.parse(dataStr);
1798
+ if (data.result) {
1799
+ const carbonChunks = translatorRef.current?.translateStreamChunk(data.result) ?? [];
1800
+ for (const chunk of carbonChunks) {
1801
+ await instance.messaging.addMessageChunk(chunk);
1802
+ }
1803
+ if (data.result.status?.state === "input-required") {
1804
+ const formRequest = extractFormRequest2(data.result);
1805
+ if (formRequest) {
1806
+ setCurrentFormRequest(formRequest);
1807
+ setPendingTaskId(data.result.taskId);
1808
+ }
1809
+ }
1810
+ }
1811
+ } catch (e) {
1812
+ console.warn("[A2AChat] Failed to parse SSE data:", e);
1813
+ }
1814
+ }
1815
+ }
1816
+ }
1817
+ setConnectionState("connected");
1818
+ onResponse?.(translatorRef.current?.getState?.() ?? {});
1819
+ } catch (error) {
1820
+ if (error.name === "AbortError") {
1821
+ console.log("[A2AChat] Request cancelled");
1822
+ } else {
1823
+ console.error("[A2AChat] Error:", error);
1824
+ setConnectionState("error");
1825
+ onError?.(error);
1826
+ }
1827
+ } finally {
1828
+ setIsStreaming(false);
1829
+ abortControllerRef.current = null;
1830
+ }
1831
+ },
1832
+ [agent, extensions, onSend, onResponse, onError]
1833
+ );
1834
+ react.useCallback(() => {
1835
+ abortControllerRef.current?.abort();
1836
+ setIsStreaming(false);
1837
+ setConnectionState("connected");
1838
+ }, []);
1839
+ const renderCustomResponse = react.useCallback(
1840
+ (state, _instance) => {
1841
+ const messageItem = state.messageItem;
1842
+ const userDefined = messageItem?.user_defined;
1843
+ if (!userDefined) return null;
1844
+ if (renderUserDefined) {
1845
+ return renderUserDefined(userDefined, messageItem);
1846
+ }
1847
+ if (userDefined.type === "text_with_citations") {
1848
+ if (renderCitations) {
1849
+ return renderCitations(userDefined.citations || [], userDefined.text);
1850
+ }
1851
+ return /* @__PURE__ */ jsxRuntime.jsx(CitationRenderer, { text: userDefined.text, citations: userDefined.citations || [] });
1852
+ }
1853
+ if (userDefined.type === "sources_list" && userDefined.citations) {
1854
+ const citations = userDefined.citations;
1855
+ if (citations.length === 0) return null;
1856
+ const uniqueCitations = citations.filter(
1857
+ (c, i, arr) => c.url && arr.findIndex((x) => x.url === c.url) === i
1858
+ );
1859
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "a2a-sources-list", children: [
1860
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "a2a-sources-list__title", children: "Sources" }),
1861
+ /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "a2a-sources-list__items", children: uniqueCitations.map((citation, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "a2a-sources-list__item", children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: citation.url ?? "#", target: "_blank", rel: "noopener noreferrer", children: citation.title || citation.url }) }, idx)) })
1862
+ ] });
1863
+ }
1864
+ if (userDefined.type === "error" && userDefined.error) {
1865
+ if (renderError) {
1866
+ return renderError(userDefined.error);
1867
+ }
1868
+ return /* @__PURE__ */ jsxRuntime.jsx(ErrorRenderer, { error: userDefined.error });
1869
+ }
1870
+ return null;
1871
+ },
1872
+ [renderCitations, renderError, renderUserDefined]
1873
+ );
1874
+ const handleFormSubmit = react.useCallback(
1875
+ async (values) => {
1876
+ if (!pendingTaskId) return;
1877
+ setCurrentFormRequest(null);
1878
+ const taskId = pendingTaskId;
1879
+ setPendingTaskId(null);
1880
+ const formResponseMessage = JSON.stringify({
1881
+ type: "form_response",
1882
+ taskId,
1883
+ values
1884
+ });
1885
+ await handleSendMessage(formResponseMessage);
1886
+ },
1887
+ [pendingTaskId, handleSendMessage]
1888
+ );
1889
+ const handleFormCancel = react.useCallback(() => {
1890
+ setCurrentFormRequest(null);
1891
+ setPendingTaskId(null);
1892
+ }, []);
1893
+ const chatConfig = react.useMemo(
1894
+ () => ({
1895
+ layout: {
1896
+ showCloseAndRestartButton: layout !== "fullscreen",
1897
+ hasContentMaxWidth: true
1898
+ },
1899
+ header: {
1900
+ title: agent?.name ?? "AI Assistant",
1901
+ icons: {
1902
+ agent: agent?.iconUrl ?? "/bot.svg"
1903
+ }
1904
+ },
1905
+ input: {
1906
+ placeholder: "Type a message..."
1907
+ },
1908
+ messaging: {
1909
+ customSendMessage: async (request, options, instance) => {
1910
+ instanceRef.current = instance;
1911
+ const text = request?.input?.text;
1912
+ if (text) {
1913
+ await handleSendMessage(text);
1914
+ }
1915
+ },
1916
+ skipWelcome: true
1917
+ },
1918
+ userDefinedResponses: {
1919
+ text_with_citations: renderCustomResponse,
1920
+ sources_list: renderCustomResponse,
1921
+ error: renderCustomResponse
1922
+ }
1923
+ }),
1924
+ [agent, layout, handleSendMessage, renderCustomResponse]
1925
+ );
1926
+ if (!agent) {
1927
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `a2a-chat a2a-chat--error ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "No agent configured. Provide `agent`, `agentId`, or `agentUrl` prop." }) });
1928
+ }
1929
+ if (!carbonLoaded) {
1930
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `a2a-chat a2a-chat--loading ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "a2a-chat__spinner" }) });
1931
+ }
1932
+ const { ChatCustomElement, ChatContainer } = CarbonComponents;
1933
+ const formOverlay = currentFormRequest && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "a2a-chat__form-overlay", children: renderForm ? renderForm(currentFormRequest, handleFormSubmit) : /* @__PURE__ */ jsxRuntime.jsx(
1934
+ FormRenderer,
1935
+ {
1936
+ form: currentFormRequest,
1937
+ onSubmit: handleFormSubmit,
1938
+ onCancel: handleFormCancel
1939
+ }
1940
+ ) });
1941
+ if (layout === "float" && ChatContainer) {
1942
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `a2a-chat a2a-chat--float ${className}`, children: [
1943
+ /* @__PURE__ */ jsxRuntime.jsx(ChatContainer, { config: chatConfig }),
1944
+ formOverlay
1945
+ ] });
1946
+ }
1947
+ if (ChatCustomElement) {
1948
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1949
+ "div",
1950
+ {
1951
+ className: `a2a-chat a2a-chat--${layout} ${className}`,
1952
+ style: {
1953
+ height: layout === "fullscreen" ? "100vh" : void 0,
1954
+ width: layout === "sidebar" ? "400px" : void 0
1955
+ },
1956
+ children: [
1957
+ /* @__PURE__ */ jsxRuntime.jsx(ChatCustomElement, { config: chatConfig }),
1958
+ formOverlay
1959
+ ]
1960
+ }
1961
+ );
1962
+ }
1963
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `a2a-chat a2a-chat--loading ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "a2a-chat__spinner" }) });
1964
+ }
1965
+ function extractFormRequest2(chunk) {
1966
+ const metadata = chunk.status?.message?.metadata;
1967
+ if (!metadata) return null;
1968
+ const formData = metadata["https://a2a-extensions.agentstack.beeai.dev/ui/form-request/v1"];
1969
+ if (!formData) return null;
1970
+ return formData;
1971
+ }
1972
+ function AgentSwitcher({
1973
+ agents,
1974
+ currentAgentId,
1975
+ onSelect,
1976
+ variant = "dropdown",
1977
+ showDescriptions = false,
1978
+ showIcons = true,
1979
+ className = "",
1980
+ disabled = false,
1981
+ label = "Select Agent"
1982
+ }) {
1983
+ if (agents.length === 0) {
1984
+ return null;
1985
+ }
1986
+ if (variant === "tabs") {
1987
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `a2a-agent-switcher a2a-agent-switcher--tabs ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "a2a-agent-switcher__tabs", role: "tablist", "aria-label": label, children: agents.map((agent) => /* @__PURE__ */ jsxRuntime.jsxs(
1988
+ "button",
1989
+ {
1990
+ role: "tab",
1991
+ "aria-selected": agent.id === currentAgentId,
1992
+ className: `a2a-agent-switcher__tab ${agent.id === currentAgentId ? "a2a-agent-switcher__tab--active" : ""}`,
1993
+ onClick: () => onSelect(agent.id),
1994
+ disabled,
1995
+ children: [
1996
+ showIcons && agent.iconUrl && /* @__PURE__ */ jsxRuntime.jsx(
1997
+ "img",
1998
+ {
1999
+ src: agent.iconUrl,
2000
+ alt: "",
2001
+ className: "a2a-agent-switcher__icon",
2002
+ width: 20,
2003
+ height: 20
2004
+ }
2005
+ ),
2006
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: agent.name })
2007
+ ]
2008
+ },
2009
+ agent.id
2010
+ )) }) });
2011
+ }
2012
+ if (variant === "cards") {
2013
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `a2a-agent-switcher a2a-agent-switcher--cards ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { disabled, children: [
2014
+ /* @__PURE__ */ jsxRuntime.jsx("legend", { className: "a2a-agent-switcher__label", children: label }),
2015
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "a2a-agent-switcher__cards", children: agents.map((agent) => /* @__PURE__ */ jsxRuntime.jsxs(
2016
+ "button",
2017
+ {
2018
+ className: `a2a-agent-switcher__card ${agent.id === currentAgentId ? "a2a-agent-switcher__card--active" : ""}`,
2019
+ onClick: () => onSelect(agent.id),
2020
+ children: [
2021
+ showIcons && agent.iconUrl && /* @__PURE__ */ jsxRuntime.jsx(
2022
+ "img",
2023
+ {
2024
+ src: agent.iconUrl,
2025
+ alt: "",
2026
+ className: "a2a-agent-switcher__card-icon",
2027
+ width: 32,
2028
+ height: 32
2029
+ }
2030
+ ),
2031
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "a2a-agent-switcher__card-content", children: [
2032
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "a2a-agent-switcher__card-name", children: agent.name }),
2033
+ showDescriptions && agent.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "a2a-agent-switcher__card-description", children: agent.description })
2034
+ ] })
2035
+ ]
2036
+ },
2037
+ agent.id
2038
+ )) })
2039
+ ] }) });
2040
+ }
2041
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `a2a-agent-switcher a2a-agent-switcher--dropdown ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "a2a-agent-switcher__label", children: [
2042
+ label,
2043
+ /* @__PURE__ */ jsxRuntime.jsx(
2044
+ "select",
2045
+ {
2046
+ value: currentAgentId ?? "",
2047
+ onChange: (e) => onSelect(e.target.value),
2048
+ disabled,
2049
+ className: "a2a-agent-switcher__select",
2050
+ children: agents.map((agent) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: agent.id, children: agent.name }, agent.id))
2051
+ }
2052
+ )
2053
+ ] }) });
2054
+ }
2055
+ function useA2AAgent(options = {}) {
2056
+ const { agent: agentProp, agentId, autoConnect = false, onConnect, onDisconnect, onError, onMessage } = options;
2057
+ const agentContext = useAgentContextOptional();
2058
+ const agent = agentProp ?? (agentId ? agentContext?.getAgent(agentId) : agentContext?.currentAgent) ?? null;
2059
+ const [connectionState, setConnectionState] = react.useState("disconnected");
2060
+ const [isStreaming, setIsStreaming] = react.useState(false);
2061
+ const [error, setError] = react.useState(null);
2062
+ const [taskId, setTaskId] = react.useState();
2063
+ const [contextId, setContextId] = react.useState();
2064
+ const abortControllerRef = react.useRef(null);
2065
+ const translatorRef = react.useRef(null);
2066
+ const historyRef = react.useRef([]);
2067
+ const sendMessage = react.useCallback(
2068
+ async (message) => {
2069
+ if (!agent) {
2070
+ const err = new Error("No agent configured");
2071
+ setError(err);
2072
+ onError?.(err);
2073
+ throw err;
2074
+ }
2075
+ setIsStreaming(true);
2076
+ setConnectionState("streaming");
2077
+ setError(null);
2078
+ translatorRef.current = createTranslator(agent.name, agent.iconUrl);
2079
+ abortControllerRef.current = new AbortController();
2080
+ try {
2081
+ const response = await fetch("/api/agent/chat", {
2082
+ method: "POST",
2083
+ headers: { "Content-Type": "application/json" },
2084
+ body: JSON.stringify({
2085
+ agentUrl: agent.url,
2086
+ apiKey: agent.apiKey,
2087
+ message,
2088
+ extensions: agent.extensions
2089
+ }),
2090
+ signal: abortControllerRef.current.signal
2091
+ });
2092
+ if (!response.ok) {
2093
+ throw new Error(`Request failed: ${response.status}`);
2094
+ }
2095
+ if (!response.body) {
2096
+ throw new Error("Response body is null");
2097
+ }
2098
+ const reader = response.body.getReader();
2099
+ const decoder = new TextDecoder();
2100
+ let buffer = "";
2101
+ let result = null;
2102
+ while (true) {
2103
+ const { done, value } = await reader.read();
2104
+ if (done) break;
2105
+ buffer += decoder.decode(value, { stream: true });
2106
+ const lines = buffer.split("\n");
2107
+ buffer = lines.pop() || "";
2108
+ for (const line of lines) {
2109
+ if (line.trim().startsWith("data: ")) {
2110
+ const dataStr = line.trim().slice(6);
2111
+ if (!dataStr || dataStr === "[DONE]") continue;
2112
+ try {
2113
+ const data = JSON.parse(dataStr);
2114
+ if (data.result) {
2115
+ result = data.result;
2116
+ setTaskId(data.result.taskId);
2117
+ setContextId(data.result.contextId);
2118
+ onMessage?.(data.result);
2119
+ }
2120
+ } catch (e) {
2121
+ }
2122
+ }
2123
+ }
2124
+ }
2125
+ setConnectionState("connected");
2126
+ onConnect?.();
2127
+ return result;
2128
+ } catch (err) {
2129
+ if (err.name !== "AbortError") {
2130
+ setError(err);
2131
+ setConnectionState("error");
2132
+ onError?.(err);
2133
+ }
2134
+ throw err;
2135
+ } finally {
2136
+ setIsStreaming(false);
2137
+ abortControllerRef.current = null;
2138
+ }
2139
+ },
2140
+ [agent, onConnect, onError, onMessage]
2141
+ );
2142
+ const cancelStream = react.useCallback(() => {
2143
+ abortControllerRef.current?.abort();
2144
+ setIsStreaming(false);
2145
+ setConnectionState("connected");
2146
+ }, []);
2147
+ const disconnect = react.useCallback(() => {
2148
+ cancelStream();
2149
+ setConnectionState("disconnected");
2150
+ setTaskId(void 0);
2151
+ setContextId(void 0);
2152
+ onDisconnect?.();
2153
+ }, [cancelStream, onDisconnect]);
2154
+ const reconnect = react.useCallback(() => {
2155
+ disconnect();
2156
+ setConnectionState("connecting");
2157
+ setConnectionState("connected");
2158
+ }, [disconnect]);
2159
+ const clearHistory = react.useCallback(() => {
2160
+ historyRef.current = [];
2161
+ setTaskId(void 0);
2162
+ setContextId(void 0);
2163
+ }, []);
2164
+ react.useEffect(() => {
2165
+ if (autoConnect && agent && connectionState === "disconnected") {
2166
+ setConnectionState("connected");
2167
+ onConnect?.();
2168
+ }
2169
+ }, [autoConnect, agent, connectionState, onConnect]);
2170
+ return {
2171
+ agent,
2172
+ state: {
2173
+ connectionState,
2174
+ error: error ?? void 0,
2175
+ taskId,
2176
+ contextId
2177
+ },
2178
+ sendMessage,
2179
+ cancelStream,
2180
+ disconnect,
2181
+ reconnect,
2182
+ clearHistory,
2183
+ isStreaming,
2184
+ isConnected: connectionState === "connected" || connectionState === "streaming",
2185
+ error
2186
+ };
2187
+ }
2188
+ function useMultiAgent(options = {}) {
2189
+ const { agents: initialAgents = [], defaultAgentId, onAgentChange } = options;
2190
+ const [agents, setAgents] = react.useState(initialAgents);
2191
+ const [currentAgentId, setCurrentAgentId] = react.useState(
2192
+ defaultAgentId ?? initialAgents[0]?.id ?? null
2193
+ );
2194
+ const currentAgent = react.useMemo(
2195
+ () => agents.find((a) => a.id === currentAgentId) ?? null,
2196
+ [agents, currentAgentId]
2197
+ );
2198
+ const switchAgent = react.useCallback(
2199
+ (agentId) => {
2200
+ const agent = agents.find((a) => a.id === agentId);
2201
+ if (agent) {
2202
+ setCurrentAgentId(agentId);
2203
+ onAgentChange?.(agent);
2204
+ } else {
2205
+ console.warn(`[useMultiAgent] Agent "${agentId}" not found`);
2206
+ }
2207
+ },
2208
+ [agents, onAgentChange]
2209
+ );
2210
+ const registerAgent = react.useCallback((agent) => {
2211
+ setAgents((prev) => {
2212
+ if (prev.some((a) => a.id === agent.id)) {
2213
+ console.warn(`[useMultiAgent] Agent "${agent.id}" already exists, updating`);
2214
+ return prev.map((a) => a.id === agent.id ? agent : a);
2215
+ }
2216
+ return [...prev, agent];
2217
+ });
2218
+ }, []);
2219
+ const unregisterAgent = react.useCallback(
2220
+ (agentId) => {
2221
+ setAgents((prev) => prev.filter((a) => a.id !== agentId));
2222
+ if (currentAgentId === agentId) {
2223
+ setCurrentAgentId(agents[0]?.id ?? null);
2224
+ }
2225
+ },
2226
+ [currentAgentId, agents]
2227
+ );
2228
+ const updateAgent = react.useCallback((agentId, updates) => {
2229
+ setAgents(
2230
+ (prev) => prev.map((a) => a.id === agentId ? { ...a, ...updates, id: agentId } : a)
2231
+ );
2232
+ }, []);
2233
+ const getAgent = react.useCallback((agentId) => agents.find((a) => a.id === agentId), [agents]);
2234
+ const hasAgent = react.useCallback((agentId) => agents.some((a) => a.id === agentId), [agents]);
2235
+ return {
2236
+ agents,
2237
+ currentAgent,
2238
+ switchAgent,
2239
+ registerAgent,
2240
+ unregisterAgent,
2241
+ updateAgent,
2242
+ getAgent,
2243
+ hasAgent
2244
+ };
2245
+ }
2246
+
2247
+ exports.A2AChat = A2AChat;
2248
+ exports.A2AClient = A2AClient;
2249
+ exports.A2AToCarbonTranslator = A2AToCarbonTranslator;
2250
+ exports.AgentProvider = AgentProvider;
2251
+ exports.AgentSwitcher = AgentSwitcher;
2252
+ exports.ChainOfThoughtStepStatus = ChainOfThoughtStepStatus;
2253
+ exports.CitationRenderer = CitationRenderer;
2254
+ exports.EXTENSION_URIS = EXTENSION_URIS;
2255
+ exports.ErrorRenderer = ErrorRenderer;
2256
+ exports.FormRenderer = FormRenderer;
2257
+ exports.MessageResponseTypes = MessageResponseTypes;
2258
+ exports.ReasoningStepOpenState = ReasoningStepOpenState;
2259
+ exports.UserType = UserType;
2260
+ exports.buildFulfillments = buildFulfillments;
2261
+ exports.createTranslator = createTranslator;
2262
+ exports.extractCitations = extractCitations;
2263
+ exports.extractDemandsFromAgentCard = extractDemandsFromAgentCard;
2264
+ exports.extractError = extractError;
2265
+ exports.extractFormRequest = extractFormRequest;
2266
+ exports.extractTrajectory = extractTrajectory;
2267
+ exports.getUniqueCitations = getUniqueCitations;
2268
+ exports.handleAgentCard = handleAgentCard;
2269
+ exports.isCompleteItem = isCompleteItem;
2270
+ exports.isFinalResponse = isFinalResponse;
2271
+ exports.isPartialItem = isPartialItem;
2272
+ exports.parseUIExtensions = parseUIExtensions;
2273
+ exports.processCitations = processCitations;
2274
+ exports.resolveMetadata = resolveMetadata;
2275
+ exports.segmentTextWithCitations = segmentTextWithCitations;
2276
+ exports.useA2AAgent = useA2AAgent;
2277
+ exports.useAgentContext = useAgentContext;
2278
+ exports.useAgentContextOptional = useAgentContextOptional;
2279
+ exports.useMultiAgent = useMultiAgent;
2280
+ //# sourceMappingURL=index.cjs.map
2281
+ //# sourceMappingURL=index.cjs.map