@kuntur/a2a-carbon-chat-adapter 0.1.17 → 1.0.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/README.md +942 -118
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/styles/index.css +0 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,19 +1,88 @@
|
|
|
1
1
|
# @kuntur/a2a-carbon-chat-adapter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@kuntur/a2a-carbon-chat-adapter)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
**Production-ready A2A protocol adapter for Carbon AI Chat** - Connect any Agent2Agent protocol agent to IBM Carbon Design System chat UI with full streaming support, multi-agent management, and extensible rendering.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 📋 Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Overview](#overview)
|
|
13
|
+
- [Why This Library?](#why-this-library)
|
|
14
|
+
- [Quick Start](#quick-start)
|
|
15
|
+
- [⚠️ Server Proxy Setup (Required)](#️-server-proxy-setup-required)
|
|
16
|
+
- [Core Concepts](#core-concepts)
|
|
17
|
+
- [API Reference](#api-reference)
|
|
18
|
+
- [Advanced Usage](#advanced-usage)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
- [Architecture](#architecture)
|
|
21
|
+
- [Troubleshooting](#troubleshooting)
|
|
22
|
+
- [Contributing](#contributing)
|
|
23
|
+
- [License](#license)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Overview
|
|
28
|
+
|
|
29
|
+
`@kuntur/a2a-carbon-chat-adapter` is a TypeScript library that bridges the [Agent2Agent (A2A) protocol](https://github.com/agentstack/a2a) with IBM's [Carbon AI Chat](https://github.com/carbon-design-system/carbon-for-ai) component. It provides:
|
|
30
|
+
|
|
31
|
+
- **Protocol Translation**: Converts A2A streaming responses to Carbon AI Chat's message format
|
|
32
|
+
- **Multi-Agent Support**: Built-in context provider for managing multiple agents
|
|
33
|
+
- **Extension System**: Handles A2A UI extensions (citations, forms, errors)
|
|
34
|
+
- **Server Utilities**: Ready-to-use API handlers for Next.js and other frameworks
|
|
35
|
+
- **Type Safety**: Full TypeScript support with comprehensive type definitions
|
|
36
|
+
|
|
37
|
+
This library is designed for production use in enterprise applications requiring AI agent integration with Carbon Design System.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Why This Library?
|
|
42
|
+
|
|
43
|
+
### The Problem
|
|
44
|
+
|
|
45
|
+
Integrating A2A protocol agents with Carbon AI Chat requires:
|
|
46
|
+
|
|
47
|
+
1. **Protocol Translation**: A2A uses Server-Sent Events (SSE) streaming; Carbon expects a specific message format
|
|
48
|
+
2. **CORS Handling**: Browser security prevents direct agent connections
|
|
49
|
+
3. **Extension Parsing**: A2A UI extensions need custom rendering logic
|
|
50
|
+
4. **State Management**: Multi-agent scenarios require context management
|
|
51
|
+
|
|
52
|
+
### The Solution
|
|
53
|
+
|
|
54
|
+
This library provides:
|
|
55
|
+
|
|
56
|
+
- ✅ **Drop-in Components**: `<A2AChat>` component with full A2A support
|
|
57
|
+
- ✅ **Server Proxy**: Pre-built API handlers for streaming proxy setup
|
|
58
|
+
- ✅ **Extension Renderers**: Built-in components for citations, forms, and errors
|
|
59
|
+
- ✅ **Multi-Agent Context**: `AgentProvider` for seamless agent switching
|
|
60
|
+
- ✅ **Programmatic API**: Hooks for custom implementations
|
|
61
|
+
|
|
62
|
+
### Benefits
|
|
63
|
+
|
|
64
|
+
- **Faster Development**: Skip protocol implementation, focus on your application
|
|
65
|
+
- **Production Ready**: Battle-tested streaming, error handling, and state management
|
|
66
|
+
- **Type Safe**: Full TypeScript support prevents runtime errors
|
|
67
|
+
- **Extensible**: Custom renderers and hooks for advanced use cases
|
|
68
|
+
- **Carbon Native**: Seamless integration with Carbon Design System
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Quick Start
|
|
73
|
+
|
|
74
|
+
### Installation
|
|
6
75
|
|
|
7
76
|
```bash
|
|
8
77
|
npm install @kuntur/a2a-carbon-chat-adapter @carbon/ai-chat react react-dom
|
|
9
78
|
```
|
|
10
79
|
|
|
11
|
-
|
|
80
|
+
### Basic Usage
|
|
12
81
|
|
|
13
82
|
```tsx
|
|
14
83
|
import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';
|
|
15
|
-
import '@kuntur/a2a-carbon-chat-adapter/styles';
|
|
16
|
-
import '@carbon/ai-chat/dist/styles.css';
|
|
84
|
+
import '@kuntur/a2a-carbon-chat-adapter/styles';
|
|
85
|
+
import '@carbon/ai-chat/dist/styles.css';
|
|
17
86
|
|
|
18
87
|
function App() {
|
|
19
88
|
return (
|
|
@@ -26,96 +95,223 @@ function App() {
|
|
|
26
95
|
}
|
|
27
96
|
```
|
|
28
97
|
|
|
29
|
-
|
|
98
|
+
**⚠️ Important**: This requires a server proxy to work. See [Server Proxy Setup](#️-server-proxy-setup-required) below.
|
|
30
99
|
|
|
31
|
-
|
|
32
|
-
import { AgentProvider, A2AChat, AgentSwitcher, useAgentContext } from '@kuntur/a2a-carbon-chat-adapter';
|
|
100
|
+
---
|
|
33
101
|
|
|
34
|
-
|
|
35
|
-
{ id: 'research', name: 'Research Agent', url: 'https://research.example.com' },
|
|
36
|
-
{ id: 'code', name: 'Code Agent', url: 'https://code.example.com' },
|
|
37
|
-
];
|
|
102
|
+
## ⚠️ Server Proxy Setup (Required)
|
|
38
103
|
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<AgentProvider agents={agents} defaultAgentId="research">
|
|
42
|
-
<ChatWithSwitcher />
|
|
43
|
-
</AgentProvider>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
104
|
+
### Why You Need a Server Proxy
|
|
46
105
|
|
|
47
|
-
|
|
48
|
-
const { agents, currentAgent, selectAgent } = useAgentContext();
|
|
106
|
+
**Direct browser connections to A2A agents are blocked by CORS (Cross-Origin Resource Sharing) security policies.** Browsers prevent JavaScript from making requests to different domains unless the server explicitly allows it.
|
|
49
107
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
```
|
|
108
|
+
Most A2A agents:
|
|
109
|
+
- Run on different domains than your frontend
|
|
110
|
+
- Don't configure CORS headers for browser access
|
|
111
|
+
- Use streaming protocols that require special handling
|
|
112
|
+
|
|
113
|
+
**Solution**: Route agent requests through your backend server, which:
|
|
114
|
+
1. Receives requests from your frontend (same origin = no CORS)
|
|
115
|
+
2. Forwards them to the A2A agent (server-to-server = no CORS restrictions)
|
|
116
|
+
3. Streams responses back to the frontend
|
|
63
117
|
|
|
64
|
-
|
|
118
|
+
### Next.js Setup (Recommended)
|
|
119
|
+
|
|
120
|
+
Create an API route that proxies requests to your A2A agent:
|
|
65
121
|
|
|
66
122
|
```typescript
|
|
67
123
|
// app/api/agent/chat/route.ts
|
|
68
124
|
import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';
|
|
69
125
|
|
|
70
126
|
export const POST = createA2AHandler({
|
|
71
|
-
|
|
127
|
+
// Security: Only allow requests to trusted agent URLs
|
|
128
|
+
allowedAgentUrls: [
|
|
129
|
+
'https://your-agent.example.com',
|
|
130
|
+
'https://another-agent.example.com',
|
|
131
|
+
],
|
|
132
|
+
|
|
133
|
+
// Optional: Set request timeout (default: 120000ms)
|
|
72
134
|
timeout: 120000,
|
|
73
135
|
});
|
|
74
136
|
|
|
137
|
+
// Required for streaming responses
|
|
75
138
|
export const runtime = 'nodejs';
|
|
76
139
|
export const dynamic = 'force-dynamic';
|
|
77
140
|
```
|
|
78
141
|
|
|
79
|
-
|
|
142
|
+
Then configure your frontend to use this proxy:
|
|
80
143
|
|
|
81
144
|
```tsx
|
|
82
|
-
import {
|
|
145
|
+
import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';
|
|
146
|
+
|
|
147
|
+
function App() {
|
|
148
|
+
return (
|
|
149
|
+
<A2AChat
|
|
150
|
+
agentUrl="https://your-agent.example.com"
|
|
151
|
+
agentName="My Agent"
|
|
152
|
+
// Point to your proxy endpoint
|
|
153
|
+
proxyUrl="/api/agent/chat"
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Express.js Setup
|
|
83
160
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
161
|
+
```typescript
|
|
162
|
+
import express from 'express';
|
|
163
|
+
import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';
|
|
164
|
+
|
|
165
|
+
const app = express();
|
|
166
|
+
|
|
167
|
+
// Create the handler
|
|
168
|
+
const handler = createA2AHandler({
|
|
169
|
+
allowedAgentUrls: ['https://your-agent.example.com'],
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Mount as Express middleware
|
|
173
|
+
app.post('/api/agent/chat', async (req, res) => {
|
|
174
|
+
const response = await handler(req);
|
|
175
|
+
|
|
176
|
+
// Copy headers
|
|
177
|
+
response.headers.forEach((value, key) => {
|
|
178
|
+
res.setHeader(key, value);
|
|
89
179
|
});
|
|
180
|
+
|
|
181
|
+
// Stream response
|
|
182
|
+
const reader = response.body?.getReader();
|
|
183
|
+
if (reader) {
|
|
184
|
+
while (true) {
|
|
185
|
+
const { done, value } = await reader.read();
|
|
186
|
+
if (done) break;
|
|
187
|
+
res.write(value);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
res.end();
|
|
191
|
+
});
|
|
192
|
+
```
|
|
90
193
|
|
|
91
|
-
|
|
92
|
-
await sendMessage('Hello, agent!');
|
|
93
|
-
};
|
|
194
|
+
### Other Frameworks
|
|
94
195
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
196
|
+
The `createA2AHandler` function returns a standard Web API `Response` object, making it compatible with:
|
|
197
|
+
|
|
198
|
+
- **Remix**: Use in `action` functions
|
|
199
|
+
- **SvelteKit**: Use in `+server.ts` endpoints
|
|
200
|
+
- **Astro**: Use in API routes
|
|
201
|
+
- **Cloudflare Workers**: Direct compatibility
|
|
202
|
+
- **Vercel Edge Functions**: Direct compatibility
|
|
203
|
+
|
|
204
|
+
Example for any framework:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';
|
|
208
|
+
|
|
209
|
+
const handler = createA2AHandler({
|
|
210
|
+
allowedAgentUrls: ['https://your-agent.example.com'],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// In your framework's request handler:
|
|
214
|
+
export async function POST(request: Request) {
|
|
215
|
+
return handler(request);
|
|
100
216
|
}
|
|
101
217
|
```
|
|
102
218
|
|
|
103
|
-
|
|
219
|
+
### Security Considerations
|
|
104
220
|
|
|
105
|
-
|
|
221
|
+
**Always use `allowedAgentUrls`** to prevent your proxy from being used to access arbitrary URLs:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
createA2AHandler({
|
|
225
|
+
// ✅ Good: Whitelist specific agents
|
|
226
|
+
allowedAgentUrls: [
|
|
227
|
+
'https://trusted-agent.example.com',
|
|
228
|
+
'https://another-trusted-agent.example.com',
|
|
229
|
+
],
|
|
230
|
+
|
|
231
|
+
// ❌ Bad: Allows any URL (security risk!)
|
|
232
|
+
// allowedAgentUrls: undefined,
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Core Concepts
|
|
239
|
+
|
|
240
|
+
### A2A Protocol
|
|
241
|
+
|
|
242
|
+
The [Agent2Agent (A2A) protocol](https://github.com/agentstack/a2a) is a standard for AI agent communication. Key features:
|
|
243
|
+
|
|
244
|
+
- **Streaming**: Real-time response delivery via Server-Sent Events (SSE)
|
|
245
|
+
- **Context Management**: Maintains conversation state across requests
|
|
246
|
+
- **UI Extensions**: Agents can request UI components (forms, citations, etc.)
|
|
247
|
+
- **Task Management**: Supports long-running tasks with progress updates
|
|
248
|
+
|
|
249
|
+
### Extension System
|
|
250
|
+
|
|
251
|
+
A2A agents can send UI extension requests using special URIs:
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
agentstack://extension/{type}/{id}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
This library automatically handles these extensions:
|
|
258
|
+
|
|
259
|
+
- **Citations**: `agentstack://extension/citation/*` → Rendered inline with text
|
|
260
|
+
- **Forms**: `agentstack://extension/form/*` → Interactive form components
|
|
261
|
+
- **Errors**: `agentstack://extension/error/*` → Formatted error displays
|
|
262
|
+
|
|
263
|
+
### Streaming Architecture
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
|
|
267
|
+
│ Browser │─────▶│ Your Server │─────▶│ A2A Agent │
|
|
268
|
+
│ (Frontend) │ │ (Proxy) │ │ │
|
|
269
|
+
└─────────────┘ └──────────────┘ └─────────────┘
|
|
270
|
+
│ │ │
|
|
271
|
+
│ 1. POST request │ │
|
|
272
|
+
│────────────────────▶│ │
|
|
273
|
+
│ │ 2. Forward request │
|
|
274
|
+
│ │─────────────────────▶│
|
|
275
|
+
│ │ │
|
|
276
|
+
│ │ 3. SSE stream │
|
|
277
|
+
│ │◀─────────────────────│
|
|
278
|
+
│ 4. SSE stream │ │
|
|
279
|
+
│◀────────────────────│ │
|
|
280
|
+
│ │ │
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
The library handles:
|
|
284
|
+
1. Converting Carbon messages to A2A format
|
|
285
|
+
2. Streaming A2A responses back to Carbon
|
|
286
|
+
3. Parsing and rendering UI extensions
|
|
287
|
+
4. Managing conversation context
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## API Reference
|
|
292
|
+
|
|
293
|
+
### Components
|
|
294
|
+
|
|
295
|
+
#### `<A2AChat>`
|
|
106
296
|
|
|
107
297
|
Main chat component that connects to A2A agents.
|
|
108
298
|
|
|
109
299
|
```tsx
|
|
300
|
+
import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';
|
|
301
|
+
|
|
110
302
|
<A2AChat
|
|
111
|
-
// Agent
|
|
112
|
-
agent={{ id: 'agent', name: 'Agent', url: '
|
|
303
|
+
// Agent Configuration (choose one)
|
|
304
|
+
agent={{ id: 'agent', name: 'Agent', url: 'https://...' }}
|
|
113
305
|
// OR
|
|
114
|
-
agentId="agent" //
|
|
306
|
+
agentId="agent" // Requires AgentProvider
|
|
115
307
|
// OR
|
|
116
|
-
agentUrl="https://..." //
|
|
308
|
+
agentUrl="https://..." // Simple URL-only mode
|
|
309
|
+
agentName="Agent Name" // Optional display name
|
|
117
310
|
|
|
118
|
-
//
|
|
311
|
+
// Server Proxy (required for CORS)
|
|
312
|
+
proxyUrl="/api/agent/chat"
|
|
313
|
+
|
|
314
|
+
// Display Options
|
|
119
315
|
layout="fullscreen" // 'fullscreen' | 'sidebar' | 'float'
|
|
120
316
|
className="my-chat"
|
|
121
317
|
|
|
@@ -125,23 +321,52 @@ Main chat component that connects to A2A agents.
|
|
|
125
321
|
allowCancel={true}
|
|
126
322
|
|
|
127
323
|
// Callbacks
|
|
128
|
-
onSend={(message) =>
|
|
129
|
-
onResponse={(response) =>
|
|
130
|
-
onError={(error) =>
|
|
324
|
+
onSend={(message) => console.log('Sent:', message)}
|
|
325
|
+
onResponse={(response) => console.log('Received:', response)}
|
|
326
|
+
onError={(error) => console.error('Error:', error)}
|
|
131
327
|
|
|
132
|
-
// Custom
|
|
133
|
-
renderCitations={(citations, text) => <
|
|
134
|
-
renderError={(error) => <
|
|
328
|
+
// Custom Renderers
|
|
329
|
+
renderCitations={(citations, text) => <CustomCitations />}
|
|
330
|
+
renderError={(error) => <CustomError />}
|
|
331
|
+
renderForm={(form) => <CustomForm />}
|
|
135
332
|
/>
|
|
136
333
|
```
|
|
137
334
|
|
|
138
|
-
|
|
335
|
+
**Props:**
|
|
336
|
+
|
|
337
|
+
| Prop | Type | Required | Description |
|
|
338
|
+
|------|------|----------|-------------|
|
|
339
|
+
| `agent` | `AgentConfig` | No* | Full agent configuration object |
|
|
340
|
+
| `agentId` | `string` | No* | Agent ID (requires `AgentProvider`) |
|
|
341
|
+
| `agentUrl` | `string` | No* | Direct agent URL |
|
|
342
|
+
| `agentName` | `string` | No | Display name for the agent |
|
|
343
|
+
| `proxyUrl` | `string` | No | Server proxy endpoint (default: `/api/agent/chat`) |
|
|
344
|
+
| `layout` | `'fullscreen' \| 'sidebar' \| 'float'` | No | Chat layout style |
|
|
345
|
+
| `className` | `string` | No | Additional CSS classes |
|
|
346
|
+
| `showThinking` | `boolean` | No | Show agent thinking indicators |
|
|
347
|
+
| `showChainOfThought` | `boolean` | No | Show reasoning steps |
|
|
348
|
+
| `allowCancel` | `boolean` | No | Allow canceling streaming responses |
|
|
349
|
+
| `onSend` | `(message: string) => void` | No | Callback when message is sent |
|
|
350
|
+
| `onResponse` | `(response: any) => void` | No | Callback when response received |
|
|
351
|
+
| `onError` | `(error: Error) => void` | No | Callback on error |
|
|
352
|
+
| `renderCitations` | `(citations, text) => ReactNode` | No | Custom citation renderer |
|
|
353
|
+
| `renderError` | `(error) => ReactNode` | No | Custom error renderer |
|
|
354
|
+
| `renderForm` | `(form) => ReactNode` | No | Custom form renderer |
|
|
355
|
+
|
|
356
|
+
*One of `agent`, `agentId`, or `agentUrl` is required.
|
|
357
|
+
|
|
358
|
+
#### `<AgentProvider>`
|
|
139
359
|
|
|
140
360
|
Context provider for multi-agent applications.
|
|
141
361
|
|
|
142
362
|
```tsx
|
|
363
|
+
import { AgentProvider } from '@kuntur/a2a-carbon-chat-adapter';
|
|
364
|
+
|
|
143
365
|
<AgentProvider
|
|
144
|
-
agents={[
|
|
366
|
+
agents={[
|
|
367
|
+
{ id: 'research', name: 'Research Agent', url: 'https://...' },
|
|
368
|
+
{ id: 'code', name: 'Code Agent', url: 'https://...' },
|
|
369
|
+
]}
|
|
145
370
|
defaultAgentId="research"
|
|
146
371
|
persistSelection={true}
|
|
147
372
|
storageKey="my-app-agent"
|
|
@@ -150,11 +375,22 @@ Context provider for multi-agent applications.
|
|
|
150
375
|
</AgentProvider>
|
|
151
376
|
```
|
|
152
377
|
|
|
153
|
-
|
|
378
|
+
**Props:**
|
|
379
|
+
|
|
380
|
+
| Prop | Type | Required | Description |
|
|
381
|
+
|------|------|----------|-------------|
|
|
382
|
+
| `agents` | `AgentConfig[]` | Yes | Array of agent configurations |
|
|
383
|
+
| `defaultAgentId` | `string` | No | Initially selected agent ID |
|
|
384
|
+
| `persistSelection` | `boolean` | No | Save selection to localStorage |
|
|
385
|
+
| `storageKey` | `string` | No | localStorage key (default: `selected-agent`) |
|
|
386
|
+
|
|
387
|
+
#### `<AgentSwitcher>`
|
|
154
388
|
|
|
155
389
|
UI component for switching between agents.
|
|
156
390
|
|
|
157
391
|
```tsx
|
|
392
|
+
import { AgentSwitcher } from '@kuntur/a2a-carbon-chat-adapter';
|
|
393
|
+
|
|
158
394
|
<AgentSwitcher
|
|
159
395
|
agents={agents}
|
|
160
396
|
currentAgentId={currentAgentId}
|
|
@@ -162,105 +398,693 @@ UI component for switching between agents.
|
|
|
162
398
|
variant="dropdown" // 'dropdown' | 'tabs' | 'cards'
|
|
163
399
|
showDescriptions={true}
|
|
164
400
|
showIcons={true}
|
|
401
|
+
className="my-switcher"
|
|
165
402
|
/>
|
|
166
403
|
```
|
|
167
404
|
|
|
168
|
-
|
|
405
|
+
**Props:**
|
|
406
|
+
|
|
407
|
+
| Prop | Type | Required | Description |
|
|
408
|
+
|------|------|----------|-------------|
|
|
409
|
+
| `agents` | `AgentConfig[]` | Yes | Available agents |
|
|
410
|
+
| `currentAgentId` | `string` | No | Currently selected agent ID |
|
|
411
|
+
| `onSelect` | `(agentId: string) => void` | Yes | Selection callback |
|
|
412
|
+
| `variant` | `'dropdown' \| 'tabs' \| 'cards'` | No | Display style |
|
|
413
|
+
| `showDescriptions` | `boolean` | No | Show agent descriptions |
|
|
414
|
+
| `showIcons` | `boolean` | No | Show agent icons |
|
|
415
|
+
| `className` | `string` | No | Additional CSS classes |
|
|
416
|
+
|
|
417
|
+
### Hooks
|
|
169
418
|
|
|
170
|
-
|
|
419
|
+
#### `useA2AAgent`
|
|
171
420
|
|
|
172
|
-
Programmatic agent interaction.
|
|
421
|
+
Programmatic agent interaction without UI components.
|
|
173
422
|
|
|
174
423
|
```tsx
|
|
424
|
+
import { useA2AAgent } from '@kuntur/a2a-carbon-chat-adapter';
|
|
425
|
+
|
|
175
426
|
const {
|
|
176
427
|
agent, // Current agent config
|
|
177
|
-
state, //
|
|
428
|
+
state, // Connection state
|
|
178
429
|
sendMessage, // Send a message
|
|
179
430
|
cancelStream, // Cancel ongoing stream
|
|
180
|
-
disconnect, // Disconnect
|
|
181
|
-
isStreaming, // Boolean
|
|
182
|
-
isConnected, // Boolean
|
|
183
|
-
error, // Last error
|
|
184
|
-
} = useA2AAgent(
|
|
431
|
+
disconnect, // Disconnect from agent
|
|
432
|
+
isStreaming, // Boolean: is currently streaming
|
|
433
|
+
isConnected, // Boolean: is connected
|
|
434
|
+
error, // Last error (if any)
|
|
435
|
+
} = useA2AAgent({
|
|
436
|
+
agent: { id: 'agent', name: 'Agent', url: 'https://...' },
|
|
437
|
+
proxyUrl: '/api/agent/chat',
|
|
438
|
+
onMessage: (message) => console.log('Message:', message),
|
|
439
|
+
onError: (error) => console.error('Error:', error),
|
|
440
|
+
onStreamStart: () => console.log('Stream started'),
|
|
441
|
+
onStreamEnd: () => console.log('Stream ended'),
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// Send a message
|
|
445
|
+
await sendMessage('Hello, agent!');
|
|
446
|
+
|
|
447
|
+
// Cancel streaming
|
|
448
|
+
cancelStream();
|
|
449
|
+
|
|
450
|
+
// Disconnect
|
|
451
|
+
disconnect();
|
|
185
452
|
```
|
|
186
453
|
|
|
187
|
-
|
|
454
|
+
**Options:**
|
|
455
|
+
|
|
456
|
+
| Option | Type | Required | Description |
|
|
457
|
+
|--------|------|----------|-------------|
|
|
458
|
+
| `agent` | `AgentConfig` | Yes | Agent configuration |
|
|
459
|
+
| `proxyUrl` | `string` | No | Server proxy endpoint |
|
|
460
|
+
| `onMessage` | `(message: any) => void` | No | Message received callback |
|
|
461
|
+
| `onError` | `(error: Error) => void` | No | Error callback |
|
|
462
|
+
| `onStreamStart` | `() => void` | No | Stream start callback |
|
|
463
|
+
| `onStreamEnd` | `() => void` | No | Stream end callback |
|
|
464
|
+
|
|
465
|
+
**Returns:**
|
|
188
466
|
|
|
189
|
-
|
|
467
|
+
| Property | Type | Description |
|
|
468
|
+
|----------|------|-------------|
|
|
469
|
+
| `agent` | `AgentConfig` | Current agent configuration |
|
|
470
|
+
| `state` | `AgentState` | Connection state object |
|
|
471
|
+
| `sendMessage` | `(message: string) => Promise<void>` | Send message function |
|
|
472
|
+
| `cancelStream` | `() => void` | Cancel streaming function |
|
|
473
|
+
| `disconnect` | `() => void` | Disconnect function |
|
|
474
|
+
| `isStreaming` | `boolean` | Currently streaming |
|
|
475
|
+
| `isConnected` | `boolean` | Currently connected |
|
|
476
|
+
| `error` | `Error \| null` | Last error |
|
|
477
|
+
|
|
478
|
+
#### `useMultiAgent`
|
|
479
|
+
|
|
480
|
+
Manage multiple agents without `AgentProvider`.
|
|
190
481
|
|
|
191
482
|
```tsx
|
|
483
|
+
import { useMultiAgent } from '@kuntur/a2a-carbon-chat-adapter';
|
|
484
|
+
|
|
192
485
|
const {
|
|
193
|
-
agents, // All agents
|
|
194
|
-
currentAgent, // Currently selected
|
|
195
|
-
switchAgent, // Switch by ID
|
|
486
|
+
agents, // All registered agents
|
|
487
|
+
currentAgent, // Currently selected agent
|
|
488
|
+
switchAgent, // Switch to agent by ID
|
|
196
489
|
registerAgent, // Add new agent
|
|
197
490
|
unregisterAgent, // Remove agent
|
|
198
|
-
} = useMultiAgent(
|
|
491
|
+
} = useMultiAgent({
|
|
492
|
+
agents: [
|
|
493
|
+
{ id: 'research', name: 'Research', url: 'https://...' },
|
|
494
|
+
{ id: 'code', name: 'Code', url: 'https://...' },
|
|
495
|
+
],
|
|
496
|
+
defaultAgentId: 'research',
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// Switch agents
|
|
500
|
+
switchAgent('code');
|
|
501
|
+
|
|
502
|
+
// Add new agent
|
|
503
|
+
registerAgent({ id: 'new', name: 'New Agent', url: 'https://...' });
|
|
504
|
+
|
|
505
|
+
// Remove agent
|
|
506
|
+
unregisterAgent('old');
|
|
199
507
|
```
|
|
200
508
|
|
|
201
|
-
|
|
509
|
+
#### `useAgentContext`
|
|
202
510
|
|
|
203
|
-
Access AgentProvider context.
|
|
511
|
+
Access `AgentProvider` context (must be used within `AgentProvider`).
|
|
204
512
|
|
|
205
513
|
```tsx
|
|
206
|
-
|
|
514
|
+
import { useAgentContext } from '@kuntur/a2a-carbon-chat-adapter';
|
|
515
|
+
|
|
516
|
+
const {
|
|
517
|
+
currentAgent, // Currently selected agent
|
|
518
|
+
agents, // All available agents
|
|
519
|
+
selectAgent, // Select agent by ID
|
|
520
|
+
getAgent, // Get agent by ID
|
|
521
|
+
hasAgent, // Check if agent exists
|
|
522
|
+
} = useAgentContext();
|
|
207
523
|
```
|
|
208
524
|
|
|
209
|
-
|
|
525
|
+
### Server Utilities
|
|
526
|
+
|
|
527
|
+
#### `createA2AHandler`
|
|
528
|
+
|
|
529
|
+
Create a server-side API handler for proxying A2A requests.
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';
|
|
210
533
|
|
|
211
|
-
|
|
534
|
+
const handler = createA2AHandler({
|
|
535
|
+
allowedAgentUrls: ['https://trusted-agent.example.com'],
|
|
536
|
+
timeout: 120000,
|
|
537
|
+
});
|
|
212
538
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
- `FormRenderer` - Renders dynamic forms from agent requests
|
|
539
|
+
// Returns a function: (request: Request) => Promise<Response>
|
|
540
|
+
```
|
|
216
541
|
|
|
217
|
-
|
|
542
|
+
**Options:**
|
|
218
543
|
|
|
219
|
-
|
|
544
|
+
| Option | Type | Required | Description |
|
|
545
|
+
|--------|------|----------|-------------|
|
|
546
|
+
| `allowedAgentUrls` | `string[]` | No | Whitelist of allowed agent URLs (security) |
|
|
547
|
+
| `timeout` | `number` | No | Request timeout in ms (default: 120000) |
|
|
548
|
+
|
|
549
|
+
**Returns:** `(request: Request) => Promise<Response>`
|
|
550
|
+
|
|
551
|
+
### Renderers
|
|
552
|
+
|
|
553
|
+
Built-in components for rendering A2A UI extensions.
|
|
554
|
+
|
|
555
|
+
#### `CitationRenderer`
|
|
556
|
+
|
|
557
|
+
Renders text with inline citations.
|
|
558
|
+
|
|
559
|
+
```tsx
|
|
560
|
+
import { CitationRenderer } from '@kuntur/a2a-carbon-chat-adapter';
|
|
561
|
+
|
|
562
|
+
<CitationRenderer
|
|
563
|
+
text="This is cited text [1]."
|
|
564
|
+
citations={[
|
|
565
|
+
{ id: '1', url: 'https://...', title: 'Source' }
|
|
566
|
+
]}
|
|
567
|
+
/>
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
#### `ErrorRenderer`
|
|
571
|
+
|
|
572
|
+
Renders formatted error messages.
|
|
573
|
+
|
|
574
|
+
```tsx
|
|
575
|
+
import { ErrorRenderer } from '@kuntur/a2a-carbon-chat-adapter';
|
|
576
|
+
|
|
577
|
+
<ErrorRenderer
|
|
578
|
+
error={{
|
|
579
|
+
message: 'Something went wrong',
|
|
580
|
+
code: 'ERROR_CODE',
|
|
581
|
+
stack: '...',
|
|
582
|
+
}}
|
|
583
|
+
showStack={true}
|
|
584
|
+
/>
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
#### `FormRenderer`
|
|
588
|
+
|
|
589
|
+
Renders dynamic forms from agent requests.
|
|
590
|
+
|
|
591
|
+
```tsx
|
|
592
|
+
import { FormRenderer } from '@kuntur/a2a-carbon-chat-adapter';
|
|
593
|
+
|
|
594
|
+
<FormRenderer
|
|
595
|
+
form={{
|
|
596
|
+
fields: [
|
|
597
|
+
{ name: 'email', type: 'email', label: 'Email', required: true },
|
|
598
|
+
{ name: 'message', type: 'textarea', label: 'Message' },
|
|
599
|
+
],
|
|
600
|
+
}}
|
|
601
|
+
onSubmit={(values) => console.log('Submitted:', values)}
|
|
602
|
+
/>
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
### Types
|
|
606
|
+
|
|
607
|
+
```typescript
|
|
608
|
+
import type {
|
|
609
|
+
AgentConfig,
|
|
610
|
+
AgentState,
|
|
611
|
+
A2AChatProps,
|
|
612
|
+
AgentProviderProps,
|
|
613
|
+
AgentSwitcherProps,
|
|
614
|
+
ExtensionResult,
|
|
615
|
+
Citation,
|
|
616
|
+
FormField,
|
|
617
|
+
// ... more types
|
|
618
|
+
} from '@kuntur/a2a-carbon-chat-adapter';
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
## Advanced Usage
|
|
624
|
+
|
|
625
|
+
### Multi-Agent Setup
|
|
220
626
|
|
|
221
627
|
```tsx
|
|
222
628
|
import {
|
|
223
|
-
// Components
|
|
224
|
-
A2AChat,
|
|
225
629
|
AgentProvider,
|
|
630
|
+
A2AChat,
|
|
226
631
|
AgentSwitcher,
|
|
227
|
-
CitationRenderer,
|
|
228
|
-
ErrorRenderer,
|
|
229
|
-
FormRenderer,
|
|
230
|
-
|
|
231
|
-
// Hooks
|
|
232
|
-
useA2AAgent,
|
|
233
|
-
useMultiAgent,
|
|
234
632
|
useAgentContext,
|
|
235
|
-
|
|
633
|
+
} from '@kuntur/a2a-carbon-chat-adapter';
|
|
236
634
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
635
|
+
const agents = [
|
|
636
|
+
{
|
|
637
|
+
id: 'research',
|
|
638
|
+
name: 'Research Agent',
|
|
639
|
+
url: 'https://research.example.com',
|
|
640
|
+
description: 'Specialized in research and analysis',
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
id: 'code',
|
|
644
|
+
name: 'Code Agent',
|
|
645
|
+
url: 'https://code.example.com',
|
|
646
|
+
description: 'Specialized in code generation',
|
|
647
|
+
},
|
|
648
|
+
];
|
|
240
649
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
650
|
+
function App() {
|
|
651
|
+
return (
|
|
652
|
+
<AgentProvider
|
|
653
|
+
agents={agents}
|
|
654
|
+
defaultAgentId="research"
|
|
655
|
+
persistSelection={true}
|
|
656
|
+
>
|
|
657
|
+
<ChatWithSwitcher />
|
|
658
|
+
</AgentProvider>
|
|
659
|
+
);
|
|
660
|
+
}
|
|
244
661
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
662
|
+
function ChatWithSwitcher() {
|
|
663
|
+
const { agents, currentAgent, selectAgent } = useAgentContext();
|
|
664
|
+
|
|
665
|
+
return (
|
|
666
|
+
<div className="app-container">
|
|
667
|
+
<AgentSwitcher
|
|
668
|
+
agents={agents}
|
|
669
|
+
currentAgentId={currentAgent?.id}
|
|
670
|
+
onSelect={selectAgent}
|
|
671
|
+
variant="tabs"
|
|
672
|
+
showDescriptions={true}
|
|
673
|
+
/>
|
|
674
|
+
<A2AChat proxyUrl="/api/agent/chat" />
|
|
675
|
+
</div>
|
|
676
|
+
);
|
|
677
|
+
}
|
|
250
678
|
```
|
|
251
679
|
|
|
252
|
-
###
|
|
680
|
+
### Custom Citation Renderer
|
|
253
681
|
|
|
254
682
|
```tsx
|
|
255
|
-
import {
|
|
683
|
+
import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';
|
|
684
|
+
|
|
685
|
+
function CustomCitationRenderer({ citations, text }) {
|
|
686
|
+
return (
|
|
687
|
+
<div className="custom-citations">
|
|
688
|
+
<p>{text}</p>
|
|
689
|
+
<ul>
|
|
690
|
+
{citations.map((citation) => (
|
|
691
|
+
<li key={citation.id}>
|
|
692
|
+
<a href={citation.url} target="_blank" rel="noopener noreferrer">
|
|
693
|
+
[{citation.id}] {citation.title}
|
|
694
|
+
</a>
|
|
695
|
+
</li>
|
|
696
|
+
))}
|
|
697
|
+
</ul>
|
|
698
|
+
</div>
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function App() {
|
|
703
|
+
return (
|
|
704
|
+
<A2AChat
|
|
705
|
+
agentUrl="https://agent.example.com"
|
|
706
|
+
renderCitations={(citations, text) => (
|
|
707
|
+
<CustomCitationRenderer citations={citations} text={text} />
|
|
708
|
+
)}
|
|
709
|
+
/>
|
|
710
|
+
);
|
|
711
|
+
}
|
|
256
712
|
```
|
|
257
713
|
|
|
258
|
-
###
|
|
714
|
+
### Programmatic Message Sending
|
|
259
715
|
|
|
260
716
|
```tsx
|
|
261
|
-
import '@kuntur/a2a-carbon-chat-adapter
|
|
717
|
+
import { useA2AAgent } from '@kuntur/a2a-carbon-chat-adapter';
|
|
718
|
+
import { useState } from 'react';
|
|
719
|
+
|
|
720
|
+
function CustomChat() {
|
|
721
|
+
const [messages, setMessages] = useState([]);
|
|
722
|
+
|
|
723
|
+
const { sendMessage, isStreaming, error } = useA2AAgent({
|
|
724
|
+
agent: {
|
|
725
|
+
id: 'agent',
|
|
726
|
+
name: 'My Agent',
|
|
727
|
+
url: 'https://agent.example.com',
|
|
728
|
+
},
|
|
729
|
+
proxyUrl: '/api/agent/chat',
|
|
730
|
+
onMessage: (message) => {
|
|
731
|
+
setMessages((prev) => [...prev, message]);
|
|
732
|
+
},
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
const handleSend = async (text: string) => {
|
|
736
|
+
setMessages((prev) => [...prev, { role: 'user', content: text }]);
|
|
737
|
+
await sendMessage(text);
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
return (
|
|
741
|
+
<div>
|
|
742
|
+
<div className="messages">
|
|
743
|
+
{messages.map((msg, i) => (
|
|
744
|
+
<div key={i} className={`message ${msg.role}`}>
|
|
745
|
+
{msg.content}
|
|
746
|
+
</div>
|
|
747
|
+
))}
|
|
748
|
+
</div>
|
|
749
|
+
{error && <div className="error">{error.message}</div>}
|
|
750
|
+
<input
|
|
751
|
+
type="text"
|
|
752
|
+
onKeyPress={(e) => {
|
|
753
|
+
if (e.key === 'Enter' && !isStreaming) {
|
|
754
|
+
handleSend(e.currentTarget.value);
|
|
755
|
+
e.currentTarget.value = '';
|
|
756
|
+
}
|
|
757
|
+
}}
|
|
758
|
+
disabled={isStreaming}
|
|
759
|
+
/>
|
|
760
|
+
</div>
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
### Extension URI Handling
|
|
766
|
+
|
|
767
|
+
The library automatically handles A2A extension URIs:
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
// Agent sends extension URI in response
|
|
771
|
+
{
|
|
772
|
+
"content": "agentstack://extension/citation/abc123",
|
|
773
|
+
"metadata": {
|
|
774
|
+
"extensions": {
|
|
775
|
+
"abc123": {
|
|
776
|
+
"type": "citation",
|
|
777
|
+
"data": {
|
|
778
|
+
"url": "https://example.com",
|
|
779
|
+
"title": "Example Source"
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Library automatically:
|
|
787
|
+
// 1. Detects extension URI
|
|
788
|
+
// 2. Looks up extension data in metadata
|
|
789
|
+
// 3. Renders using CitationRenderer
|
|
790
|
+
// 4. Replaces URI with rendered component
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### Error Handling
|
|
794
|
+
|
|
795
|
+
```tsx
|
|
796
|
+
import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';
|
|
797
|
+
|
|
798
|
+
function App() {
|
|
799
|
+
const handleError = (error: Error) => {
|
|
800
|
+
// Log to error tracking service
|
|
801
|
+
console.error('Agent error:', error);
|
|
802
|
+
|
|
803
|
+
// Show user-friendly message
|
|
804
|
+
if (error.message.includes('timeout')) {
|
|
805
|
+
alert('The agent is taking too long to respond. Please try again.');
|
|
806
|
+
} else if (error.message.includes('network')) {
|
|
807
|
+
alert('Network error. Please check your connection.');
|
|
808
|
+
} else {
|
|
809
|
+
alert('An error occurred. Please try again.');
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
return (
|
|
814
|
+
<A2AChat
|
|
815
|
+
agentUrl="https://agent.example.com"
|
|
816
|
+
onError={handleError}
|
|
817
|
+
/>
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
---
|
|
823
|
+
|
|
824
|
+
## Examples
|
|
825
|
+
|
|
826
|
+
For complete working examples, see the [examples repository](https://github.com/Xnvargas/a2a-carbon-chat-adapter-examples) (coming soon).
|
|
827
|
+
|
|
828
|
+
Example applications include:
|
|
829
|
+
|
|
830
|
+
- **Basic Chat**: Simple single-agent chat interface
|
|
831
|
+
- **Multi-Agent**: Agent switching with tabs and dropdown
|
|
832
|
+
- **Custom Renderers**: Custom citation and form renderers
|
|
833
|
+
- **Programmatic API**: Using hooks without UI components
|
|
834
|
+
- **Next.js Integration**: Full Next.js app with API routes
|
|
835
|
+
- **Express.js Integration**: Express server with proxy setup
|
|
836
|
+
|
|
837
|
+
---
|
|
838
|
+
|
|
839
|
+
## Architecture
|
|
840
|
+
|
|
841
|
+
### Protocol Flow
|
|
842
|
+
|
|
843
|
+
```
|
|
844
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
845
|
+
│ Browser (Frontend) │
|
|
846
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
847
|
+
│ │
|
|
848
|
+
│ ┌──────────────┐ ┌─────────────────────────────────┐ │
|
|
849
|
+
│ │ A2AChat │────────▶│ useA2AAgent Hook │ │
|
|
850
|
+
│ │ Component │ │ - Manages connection state │ │
|
|
851
|
+
│ └──────────────┘ │ - Handles streaming │ │
|
|
852
|
+
│ │ │ - Parses extensions │ │
|
|
853
|
+
│ │ └─────────────────────────────────┘ │
|
|
854
|
+
│ │ │ │
|
|
855
|
+
│ ▼ ▼ │
|
|
856
|
+
│ ┌──────────────┐ ┌─────────────────────────────────┐ │
|
|
857
|
+
│ │ Renderers │ │ A2AToCarbonTranslator │ │
|
|
858
|
+
│ │ - Citation │ │ - Converts A2A → Carbon format │ │
|
|
859
|
+
│ │ - Error │ │ - Handles metadata transform │ │
|
|
860
|
+
│ │ - Form │ └─────────────────────────────────┘ │
|
|
861
|
+
│ └──────────────┘ │ │
|
|
862
|
+
│ │ │
|
|
863
|
+
└────────────────────────────────────────┼────────────────────────┘
|
|
864
|
+
│
|
|
865
|
+
│ POST /api/agent/chat
|
|
866
|
+
│
|
|
867
|
+
┌────────────────────────────────────────┼────────────────────────┐
|
|
868
|
+
│ Your Server (Proxy) │ │
|
|
869
|
+
├────────────────────────────────────────┼────────────────────────┤
|
|
870
|
+
│ │ │
|
|
871
|
+
│ ┌─────────────▼──────────────┐ │
|
|
872
|
+
│ │ createA2AHandler │ │
|
|
873
|
+
│ │ - Validates agent URL │ │
|
|
874
|
+
│ │ - Forwards request │ │
|
|
875
|
+
│ │ - Streams response │ │
|
|
876
|
+
│ └─────────────┬──────────────┘ │
|
|
877
|
+
│ │ │
|
|
878
|
+
└────────────────────────────────────────┼────────────────────────┘
|
|
879
|
+
│
|
|
880
|
+
│ POST /chat
|
|
881
|
+
│
|
|
882
|
+
┌────────────────────────────────────────┼────────────────────────┐
|
|
883
|
+
│ A2A Agent │ │
|
|
884
|
+
├────────────────────────────────────────┼────────────────────────┤
|
|
885
|
+
│ │ │
|
|
886
|
+
│ ┌─────────────▼──────────────┐ │
|
|
887
|
+
│ │ A2A Protocol Handler │ │
|
|
888
|
+
│ │ - Processes message │ │
|
|
889
|
+
│ │ - Generates response │ │
|
|
890
|
+
│ │ - Streams via SSE │ │
|
|
891
|
+
│ └────────────────────────────┘ │
|
|
892
|
+
│ │
|
|
893
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
### Package Structure
|
|
897
|
+
|
|
898
|
+
```
|
|
899
|
+
@kuntur/a2a-carbon-chat-adapter/
|
|
900
|
+
├── dist/ # Built output
|
|
901
|
+
│ ├── index.js # ESM main entry
|
|
902
|
+
│ ├── index.cjs # CJS main entry
|
|
903
|
+
│ ├── index.d.ts # TypeScript definitions
|
|
904
|
+
│ ├── server.js # ESM server utilities
|
|
905
|
+
│ ├── server.cjs # CJS server utilities
|
|
906
|
+
│ ├── server.d.ts # Server TypeScript definitions
|
|
907
|
+
│ └── styles/
|
|
908
|
+
│ └── index.css # Component styles
|
|
909
|
+
├── src/
|
|
910
|
+
│ ├── components/ # React components
|
|
911
|
+
│ │ ├── A2AChat.tsx
|
|
912
|
+
│ │ ├── AgentProvider.tsx
|
|
913
|
+
│ │ ├── AgentSwitcher.tsx
|
|
914
|
+
│ │ └── renderers/ # Extension renderers
|
|
915
|
+
│ ├── hooks/ # React hooks
|
|
916
|
+
│ │ ├── useA2AAgent.ts
|
|
917
|
+
│ │ ├── useMultiAgent.ts
|
|
918
|
+
│ │ └── useAgentContext.ts
|
|
919
|
+
│ ├── lib/
|
|
920
|
+
│ │ ├── a2a/ # A2A protocol client
|
|
921
|
+
│ │ └── translator/ # Protocol translation
|
|
922
|
+
│ ├── server/ # Server utilities
|
|
923
|
+
│ │ └── create-api-handler.ts
|
|
924
|
+
│ └── types/ # TypeScript types
|
|
925
|
+
└── package.json
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
---
|
|
929
|
+
|
|
930
|
+
## Troubleshooting
|
|
931
|
+
|
|
932
|
+
### CORS Errors
|
|
933
|
+
|
|
934
|
+
**Problem**: Browser console shows CORS errors when connecting to agent.
|
|
935
|
+
|
|
936
|
+
**Solution**: You must use a server proxy. See [Server Proxy Setup](#️-server-proxy-setup-required).
|
|
937
|
+
|
|
938
|
+
```
|
|
939
|
+
❌ Access to fetch at 'https://agent.example.com' from origin 'http://localhost:3000'
|
|
940
|
+
has been blocked by CORS policy
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
### Streaming Not Working
|
|
944
|
+
|
|
945
|
+
**Problem**: Messages don't stream; they appear all at once.
|
|
946
|
+
|
|
947
|
+
**Solution**: Ensure your server proxy is configured correctly:
|
|
948
|
+
|
|
949
|
+
```typescript
|
|
950
|
+
// Next.js: Add these exports
|
|
951
|
+
export const runtime = 'nodejs';
|
|
952
|
+
export const dynamic = 'force-dynamic';
|
|
953
|
+
|
|
954
|
+
// Express: Ensure streaming is not buffered
|
|
955
|
+
app.use(express.json({ limit: '50mb' }));
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
### Extension URIs Not Rendering
|
|
959
|
+
|
|
960
|
+
**Problem**: Extension URIs appear as plain text instead of rendered components.
|
|
961
|
+
|
|
962
|
+
**Solution**: Ensure the agent includes extension metadata:
|
|
963
|
+
|
|
964
|
+
```json
|
|
965
|
+
{
|
|
966
|
+
"content": "See citation [1]",
|
|
967
|
+
"metadata": {
|
|
968
|
+
"extensions": {
|
|
969
|
+
"1": {
|
|
970
|
+
"type": "citation",
|
|
971
|
+
"data": {
|
|
972
|
+
"url": "https://example.com",
|
|
973
|
+
"title": "Example"
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
262
979
|
```
|
|
263
980
|
|
|
981
|
+
### TypeScript Errors
|
|
982
|
+
|
|
983
|
+
**Problem**: TypeScript can't find module declarations.
|
|
984
|
+
|
|
985
|
+
**Solution**: Ensure `@carbon/ai-chat` is installed and types are available:
|
|
986
|
+
|
|
987
|
+
```bash
|
|
988
|
+
npm install @carbon/ai-chat @types/react @types/react-dom
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
### Agent Not Responding
|
|
992
|
+
|
|
993
|
+
**Problem**: No response from agent after sending message.
|
|
994
|
+
|
|
995
|
+
**Checklist**:
|
|
996
|
+
1. ✅ Server proxy is running and accessible
|
|
997
|
+
2. ✅ Agent URL is correct and agent is running
|
|
998
|
+
3. ✅ Agent URL is in `allowedAgentUrls` (if configured)
|
|
999
|
+
4. ✅ Network tab shows request reaching `/api/agent/chat`
|
|
1000
|
+
5. ✅ Check server logs for errors
|
|
1001
|
+
|
|
1002
|
+
### Performance Issues
|
|
1003
|
+
|
|
1004
|
+
**Problem**: Chat feels slow or laggy.
|
|
1005
|
+
|
|
1006
|
+
**Solutions**:
|
|
1007
|
+
- Reduce `timeout` in `createA2AHandler` for faster failures
|
|
1008
|
+
- Implement message pagination for long conversations
|
|
1009
|
+
- Use `showThinking={false}` to reduce re-renders
|
|
1010
|
+
- Memoize custom renderers with `React.memo()`
|
|
1011
|
+
|
|
1012
|
+
---
|
|
1013
|
+
|
|
1014
|
+
## Contributing
|
|
1015
|
+
|
|
1016
|
+
Contributions are welcome! Please follow these guidelines:
|
|
1017
|
+
|
|
1018
|
+
### Development Setup
|
|
1019
|
+
|
|
1020
|
+
```bash
|
|
1021
|
+
# Clone repository
|
|
1022
|
+
git clone https://github.com/Xnvargas/a2a-carbon-chat-adapter.git
|
|
1023
|
+
cd a2a-carbon-chat-adapter
|
|
1024
|
+
|
|
1025
|
+
# Install dependencies
|
|
1026
|
+
npm install
|
|
1027
|
+
|
|
1028
|
+
# Run development build (watch mode)
|
|
1029
|
+
npm run dev
|
|
1030
|
+
|
|
1031
|
+
# Run type checking
|
|
1032
|
+
npm run typecheck
|
|
1033
|
+
|
|
1034
|
+
# Run linting
|
|
1035
|
+
npm run lint
|
|
1036
|
+
|
|
1037
|
+
# Build for production
|
|
1038
|
+
npm run build
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
### Submitting Changes
|
|
1042
|
+
|
|
1043
|
+
1. Fork the repository
|
|
1044
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
1045
|
+
3. Make your changes
|
|
1046
|
+
4. Add tests if applicable
|
|
1047
|
+
5. Run `npm run typecheck` and `npm run lint`
|
|
1048
|
+
6. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
1049
|
+
7. Push to the branch (`git push origin feature/amazing-feature`)
|
|
1050
|
+
8. Open a Pull Request
|
|
1051
|
+
|
|
1052
|
+
### Code Style
|
|
1053
|
+
|
|
1054
|
+
- Use TypeScript for all new code
|
|
1055
|
+
- Follow existing code formatting (ESLint config)
|
|
1056
|
+
- Add JSDoc comments for public APIs
|
|
1057
|
+
- Update documentation for user-facing changes
|
|
1058
|
+
|
|
1059
|
+
### Reporting Issues
|
|
1060
|
+
|
|
1061
|
+
When reporting issues, please include:
|
|
1062
|
+
|
|
1063
|
+
- Library version (`npm list @kuntur/a2a-carbon-chat-adapter`)
|
|
1064
|
+
- Framework and version (Next.js, React, etc.)
|
|
1065
|
+
- Minimal reproduction code
|
|
1066
|
+
- Expected vs actual behavior
|
|
1067
|
+
- Browser console errors (if applicable)
|
|
1068
|
+
|
|
1069
|
+
---
|
|
1070
|
+
|
|
264
1071
|
## License
|
|
265
1072
|
|
|
266
|
-
MIT
|
|
1073
|
+
MIT © [Xavier Vargas](https://github.com/Xnvargas)
|
|
1074
|
+
|
|
1075
|
+
See [LICENSE](LICENSE) file for details.
|
|
1076
|
+
|
|
1077
|
+
---
|
|
1078
|
+
|
|
1079
|
+
## Links
|
|
1080
|
+
|
|
1081
|
+
- **GitHub**: https://github.com/Xnvargas/a2a-carbon-chat-adapter
|
|
1082
|
+
- **npm**: https://www.npmjs.com/package/@kuntur/a2a-carbon-chat-adapter
|
|
1083
|
+
- **Issues**: https://github.com/Xnvargas/a2a-carbon-chat-adapter/issues
|
|
1084
|
+
- **Examples**: https://github.com/Xnvargas/a2a-carbon-chat-adapter-examples (coming soon)
|
|
1085
|
+
- **A2A Protocol**: https://github.com/agentstack/a2a
|
|
1086
|
+
- **Carbon AI Chat**: https://github.com/carbon-design-system/carbon-for-ai
|
|
1087
|
+
|
|
1088
|
+
---
|
|
1089
|
+
|
|
1090
|
+
**Made with ❤️ for the AI agent community**
|