@amaster.ai/copilot-client 1.0.0-beta.0 → 1.0.0-beta.10
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 +82 -284
- package/dist/index.cjs +2 -236
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -12
- package/dist/index.d.ts +50 -12
- package/dist/index.js +2 -234
- package/dist/index.js.map +1 -1
- package/package.json +5 -7
package/README.md
CHANGED
|
@@ -1,337 +1,135 @@
|
|
|
1
1
|
# @amaster.ai/copilot-client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A2A to A2UI conversion SDK for Copilot API.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- 🔄 **Task Management**: Cancel, monitor, and resubscribe to tasks
|
|
9
|
-
- 🤖 **Multi-turn Conversations**: Support for conversation history
|
|
10
|
-
- 🎯 **A2A Protocol**: Full JSON-RPC 2.0 implementation
|
|
11
|
-
- 📡 **Auto Context**: Automatically extracts app context from URL
|
|
12
|
-
- 🌳 **Tree-shakeable**: ESM/CJS builds with TypeScript support
|
|
7
|
+
This SDK converts [A2A protocol](https://github.com/google-a2a/A2A) responses from Copilot Agent to [A2UI](https://a2ui.org/) format for rendering.
|
|
13
8
|
|
|
14
|
-
## Installation
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
pnpm add @amaster.ai/copilot-client @amaster.ai/http-client axios @a2a-js/sdk eventsource-parser
|
|
18
9
|
```
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
console.log("Chat completed with status:", chunk.status);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
10
|
+
┌─────────────────┐ ┌───────────────────────────┐ ┌─────────────────┐
|
|
11
|
+
│ Your App │ │ copilot-client │ │ Copilot Agent │
|
|
12
|
+
│ │ │ │ │ (A2A Server) │
|
|
13
|
+
│ ChatMessage ──────────► A2A Request ───────────────► │
|
|
14
|
+
│ │ │ │ │ │
|
|
15
|
+
│ │ │ ┌───────────────────┐ │ │ A2A Response │
|
|
16
|
+
│ ServerToClient │ │ │ A2A → A2UI │ │ │ parts[]: │
|
|
17
|
+
│ Message[] ◄──────────────│ Conversion │◄────────── - TextPart │
|
|
18
|
+
│ │ │ │ │ │ │ │ - FilePart │
|
|
19
|
+
│ │ │ │ │ TextPart → Text │ │ │ - DataPart │
|
|
20
|
+
│ │ │ │ │ FilePart → Image │ │ │ │
|
|
21
|
+
│ ▼ │ │ │ DataPart → pass │ │ │ │
|
|
22
|
+
│ @a2ui/lit │ │ └───────────────────┘ │ └─────────────────┘
|
|
23
|
+
│ Renderer │ └───────────────────────────┘
|
|
24
|
+
└─────────────────┘
|
|
37
25
|
```
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
This client implements the **A2A (Agent-to-Agent)** protocol for AI interactions:
|
|
27
|
+
**A2A → A2UI Conversion:**
|
|
42
28
|
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
29
|
+
- `TextPart` → A2UI `Text` component
|
|
30
|
+
- `FilePart` (image) → A2UI `Image` component
|
|
31
|
+
- `DataPart` (thought) → A2UI `Card` with `Text` (💭 subject + description)
|
|
32
|
+
- `DataPart` (toolCall) → A2UI `Card` with `Icon` + `Text` (tool name + status)
|
|
33
|
+
- `DataPart` (mimeType: `application/json+a2ui`) → pass through as-is
|
|
47
34
|
|
|
48
|
-
##
|
|
49
|
-
|
|
50
|
-
### `chat(messages, options?)`
|
|
51
|
-
|
|
52
|
-
Stream chat responses using simplified API.
|
|
53
|
-
|
|
54
|
-
**Parameters:**
|
|
55
|
-
- `messages`: Array of chat messages
|
|
56
|
-
- `options`: Optional configuration
|
|
57
|
-
- `taskId`: Continue an existing task (optional)
|
|
58
|
-
|
|
59
|
-
**Returns:** `AsyncGenerator<ChatChunk>`
|
|
35
|
+
## Installation
|
|
60
36
|
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
{ role: "system", content: "You are a helpful assistant" },
|
|
64
|
-
{ role: "user", content: "What is TypeScript?" },
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
for await (const chunk of copilot.chat(messages)) {
|
|
68
|
-
process.stdout.write(chunk.text); // Stream output
|
|
69
|
-
|
|
70
|
-
// chunk.status: Current state (RUNNING, SUCCEEDED, FAILED, CANCELLED)
|
|
71
|
-
// chunk.isFinal: true when chat is complete
|
|
72
|
-
}
|
|
37
|
+
```bash
|
|
38
|
+
pnpm add @amaster.ai/copilot-client
|
|
73
39
|
```
|
|
74
40
|
|
|
75
|
-
###
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
interface ChatMessage {
|
|
79
|
-
role: "system" | "user" | "assistant";
|
|
80
|
-
content: string;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
interface ChatChunk {
|
|
84
|
-
taskId: string; // Unique task identifier
|
|
85
|
-
text: string; // Chunk of response text
|
|
86
|
-
status: TaskState; // RUNNING | SUCCEEDED | FAILED | CANCELLED
|
|
87
|
-
isFinal: boolean | undefined; // Whether this is the final chunk
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
interface ChatOptions {
|
|
91
|
-
taskId?: string; // Optional: provide your own task ID to resume
|
|
92
|
-
}
|
|
41
|
+
### Peer Dependencies
|
|
93
42
|
|
|
94
|
-
|
|
43
|
+
```bash
|
|
44
|
+
pnpm add @a2a-js/sdk @a2ui/lit
|
|
95
45
|
```
|
|
96
46
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
Cancel an ongoing chat task.
|
|
47
|
+
## Usage
|
|
100
48
|
|
|
101
49
|
```typescript
|
|
102
|
-
|
|
103
|
-
const result = await copilot.cancelChat(taskId);
|
|
50
|
+
import { createCopilotA2UIClient, Data } from "@amaster.ai/copilot-client";
|
|
104
51
|
|
|
105
|
-
|
|
106
|
-
console.log("Chat cancelled successfully");
|
|
107
|
-
}
|
|
108
|
-
```
|
|
52
|
+
const client = createCopilotA2UIClient();
|
|
109
53
|
|
|
110
|
-
|
|
54
|
+
// Create A2UI message processor
|
|
55
|
+
const processor = Data.createSignalA2uiMessageProcessor();
|
|
111
56
|
|
|
112
|
-
|
|
57
|
+
// Stream and render
|
|
58
|
+
for await (const messages of client.chat([{ role: "user", content: "Hello" }])) {
|
|
59
|
+
// messages is ServerToClientMessage[] (A2UI standard type)
|
|
60
|
+
processor.processMessages(messages);
|
|
113
61
|
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
console.log("Task status:", result.data.result.status.state);
|
|
62
|
+
const surfaces = processor.getSurfaces();
|
|
63
|
+
for (const [surfaceId, surface] of surfaces.entries()) {
|
|
64
|
+
// Render with @a2ui/lit
|
|
65
|
+
}
|
|
119
66
|
}
|
|
120
67
|
```
|
|
121
68
|
|
|
122
|
-
##
|
|
69
|
+
## API
|
|
123
70
|
|
|
124
|
-
###
|
|
71
|
+
### `createCopilotA2UIClient(http?)`
|
|
125
72
|
|
|
126
|
-
|
|
127
|
-
const messages: ChatMessage[] = [];
|
|
73
|
+
Creates a Copilot A2UI client.
|
|
128
74
|
|
|
129
|
-
|
|
130
|
-
messages.push({ role: "user", content: "What is React?" });
|
|
75
|
+
### `client.chat(messages, options?)`
|
|
131
76
|
|
|
132
|
-
|
|
133
|
-
for await (const chunk of copilot.chat(messages)) {
|
|
134
|
-
response += chunk.text;
|
|
135
|
-
}
|
|
77
|
+
Streams A2UI messages converted from A2A response.
|
|
136
78
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// Follow-up message
|
|
140
|
-
messages.push({ role: "user", content: "Can you give me an example?" });
|
|
141
|
-
|
|
142
|
-
response = "";
|
|
143
|
-
for await (const chunk of copilot.chat(messages)) {
|
|
144
|
-
response += chunk.text;
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### With System Prompt
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
const messages = [
|
|
152
|
-
{
|
|
153
|
-
role: "system",
|
|
154
|
-
content: "You are a coding assistant specialized in TypeScript."
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
role: "user",
|
|
158
|
-
content: "How do I define an interface?"
|
|
159
|
-
},
|
|
160
|
-
];
|
|
161
|
-
|
|
162
|
-
for await (const chunk of copilot.chat(messages)) {
|
|
163
|
-
console.log(chunk.text);
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### Resume Existing Task
|
|
79
|
+
**Returns:** `AsyncGenerator<ServerToClientMessage[]>`
|
|
168
80
|
|
|
169
81
|
```typescript
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
{ role: "user", content: "Tell me a long story" }
|
|
174
|
-
])) {
|
|
175
|
-
taskId = chunk.taskId;
|
|
176
|
-
console.log(chunk.text);
|
|
82
|
+
interface ChatMessage {
|
|
83
|
+
role: "system" | "user" | "assistant";
|
|
84
|
+
content: string | MessageContent[];
|
|
177
85
|
}
|
|
178
86
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
{ role: "user", content: "What happened next?" }
|
|
182
|
-
], { taskId })) {
|
|
183
|
-
console.log(chunk.text);
|
|
87
|
+
interface ChatOptions {
|
|
88
|
+
taskId?: string;
|
|
184
89
|
}
|
|
185
90
|
```
|
|
186
91
|
|
|
187
|
-
###
|
|
92
|
+
### `client.cancelChat(taskId)`
|
|
188
93
|
|
|
189
|
-
|
|
190
|
-
let taskId: string;
|
|
191
|
-
|
|
192
|
-
const chatPromise = (async () => {
|
|
193
|
-
for await (const chunk of copilot.chat(
|
|
194
|
-
[{ role: "user", content: "Write a long essay" }]
|
|
195
|
-
)) {
|
|
196
|
-
taskId = chunk.taskId;
|
|
197
|
-
console.log(chunk.text);
|
|
198
|
-
}
|
|
199
|
-
})();
|
|
94
|
+
Cancels an ongoing chat task.
|
|
200
95
|
|
|
201
|
-
|
|
202
|
-
setTimeout(async () => {
|
|
203
|
-
if (taskId) {
|
|
204
|
-
await copilot.cancelChat(taskId);
|
|
205
|
-
console.log("Chat cancelled");
|
|
206
|
-
}
|
|
207
|
-
}, 5000);
|
|
208
|
-
```
|
|
96
|
+
### `client.getChatStatus(taskId)`
|
|
209
97
|
|
|
210
|
-
|
|
98
|
+
Gets the current status of a chat task.
|
|
211
99
|
|
|
212
|
-
|
|
213
|
-
import { useState } from "react";
|
|
214
|
-
import { createCopilotClient } from "@amaster.ai/copilot-client";
|
|
215
|
-
|
|
216
|
-
const copilot = createCopilotClient();
|
|
217
|
-
|
|
218
|
-
function ChatComponent() {
|
|
219
|
-
const [message, setMessage] = useState("");
|
|
220
|
-
const [response, setResponse] = useState("");
|
|
221
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
222
|
-
const [taskId, setTaskId] = useState<string>();
|
|
223
|
-
|
|
224
|
-
const handleSend = async () => {
|
|
225
|
-
setIsLoading(true);
|
|
226
|
-
setResponse("");
|
|
227
|
-
|
|
228
|
-
try {
|
|
229
|
-
for await (const chunk of copilot.chat([
|
|
230
|
-
{ role: "user", content: message }
|
|
231
|
-
])) {
|
|
232
|
-
setTaskId(chunk.taskId);
|
|
233
|
-
setResponse(prev => prev + chunk.text);
|
|
234
|
-
}
|
|
235
|
-
} finally {
|
|
236
|
-
setIsLoading(false);
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
const handleCancel = async () => {
|
|
241
|
-
if (taskId) {
|
|
242
|
-
await copilot.cancelChat(taskId);
|
|
243
|
-
setIsLoading(false);
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
return (
|
|
248
|
-
<div>
|
|
249
|
-
<input
|
|
250
|
-
value={message}
|
|
251
|
-
onChange={e => setMessage(e.target.value)}
|
|
252
|
-
disabled={isLoading}
|
|
253
|
-
/>
|
|
254
|
-
<button onClick={handleSend} disabled={isLoading}>
|
|
255
|
-
Send
|
|
256
|
-
</button>
|
|
257
|
-
{isLoading && (
|
|
258
|
-
<button onClick={handleCancel}>Cancel</button>
|
|
259
|
-
)}
|
|
260
|
-
<div>{response}</div>
|
|
261
|
-
</div>
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
## Custom HTTP Client
|
|
267
|
-
|
|
268
|
-
You can provide your own pre-configured HTTP client:
|
|
100
|
+
## Multimodal Input
|
|
269
101
|
|
|
270
102
|
```typescript
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const httpClient = createHttpClient(axiosInstance);
|
|
285
|
-
|
|
286
|
-
// Create copilot client with custom HTTP client
|
|
287
|
-
const copilot = createCopilotClient(httpClient);
|
|
103
|
+
// Text
|
|
104
|
+
{ role: "user", content: "Hello" }
|
|
105
|
+
|
|
106
|
+
// Image
|
|
107
|
+
{ role: "user", content: [
|
|
108
|
+
{ type: "text", text: "What's this?" },
|
|
109
|
+
{ type: "image", url: "https://..." }
|
|
110
|
+
]}
|
|
111
|
+
|
|
112
|
+
// File
|
|
113
|
+
{ role: "user", content: [
|
|
114
|
+
{ type: "file", url: "https://...", name: "doc.pdf" }
|
|
115
|
+
]}
|
|
288
116
|
```
|
|
289
117
|
|
|
290
|
-
##
|
|
118
|
+
## Types
|
|
119
|
+
|
|
120
|
+
All output types are standard A2UI types from `@a2ui/lit`:
|
|
291
121
|
|
|
292
122
|
```typescript
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
{ role: "user", content: "Hello" }
|
|
296
|
-
])) {
|
|
297
|
-
console.log(chunk.text);
|
|
298
|
-
|
|
299
|
-
// Check for errors in status
|
|
300
|
-
if (chunk.status === "FAILED" && chunk.isFinal) {
|
|
301
|
-
console.error("Chat failed");
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
} catch (error) {
|
|
306
|
-
console.error("Chat error:", error);
|
|
307
|
-
}
|
|
123
|
+
import type { ServerToClientMessage, Surface, MessageProcessor } from "@amaster.ai/copilot-client";
|
|
124
|
+
import { Data, Types } from "@amaster.ai/copilot-client";
|
|
308
125
|
```
|
|
309
126
|
|
|
310
|
-
##
|
|
311
|
-
|
|
312
|
-
### A2A Protocol
|
|
313
|
-
|
|
314
|
-
The client implements the A2A (Agent-to-Agent) protocol using JSON-RPC 2.0:
|
|
315
|
-
|
|
316
|
-
1. **Message Streaming**: `method: "message/stream"` - Send message and receive streaming response
|
|
317
|
-
2. **Task Cancellation**: `method: "tasks/cancel"` - Cancel an ongoing task
|
|
318
|
-
3. **Task Status**: `method: "tasks/get"` - Get task status
|
|
319
|
-
4. **Task Resubscription**: Reconnect to an existing task stream
|
|
320
|
-
|
|
321
|
-
### Auto Context Extraction
|
|
322
|
-
|
|
323
|
-
The client automatically extracts `contextId` from:
|
|
324
|
-
1. URL path: `/app/{app_id}/...`
|
|
325
|
-
2. Domain subdomain: `{app_id}-{env}.amaster.local`
|
|
326
|
-
|
|
327
|
-
This links conversations to the current app context.
|
|
328
|
-
|
|
329
|
-
### SSE Streaming
|
|
127
|
+
## References
|
|
330
128
|
|
|
331
|
-
|
|
332
|
-
-
|
|
333
|
-
-
|
|
334
|
-
-
|
|
129
|
+
- [A2UI Documentation](https://a2ui.org/)
|
|
130
|
+
- [A2UI Components](https://a2ui.org/reference/components/)
|
|
131
|
+
- [A2UI Messages](https://a2ui.org/reference/messages/)
|
|
132
|
+
- [A2A Protocol](https://github.com/google-a2a/A2A)
|
|
335
133
|
|
|
336
134
|
## License
|
|
337
135
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,237 +1,3 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var sdk = require('@a2a-js/sdk');
|
|
4
|
-
var eventsourceParser = require('eventsource-parser');
|
|
5
|
-
var httpClient = require('@amaster.ai/http-client');
|
|
6
|
-
|
|
7
|
-
// src/copilot-client.ts
|
|
8
|
-
function generateUUID() {
|
|
9
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
10
|
-
return crypto.randomUUID();
|
|
11
|
-
}
|
|
12
|
-
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
|
|
13
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
14
|
-
const randomValues = crypto.getRandomValues(new Uint8Array(1));
|
|
15
|
-
const r = (randomValues[0] ?? 0) % 16;
|
|
16
|
-
const v = c === "x" ? r : r & 3 | 8;
|
|
17
|
-
return v.toString(16);
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
21
|
-
const r = Math.random() * 16 | 0;
|
|
22
|
-
const v = c === "x" ? r : r & 3 | 8;
|
|
23
|
-
return v.toString(16);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
function extractAppIdFromUrl() {
|
|
27
|
-
if (typeof window === "undefined") {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
const url = window.location.href;
|
|
32
|
-
const pathMatch = /\/app\/([\da-f-]+)(?:\/|$)/.exec(url);
|
|
33
|
-
if (pathMatch && pathMatch[1]) {
|
|
34
|
-
return pathMatch[1];
|
|
35
|
-
}
|
|
36
|
-
const hostname = window.location.hostname;
|
|
37
|
-
const domainMatch = /^([\da-f-]+)(?:-[^.]+)?\.amaster\.(?:local|ai)$/.exec(hostname);
|
|
38
|
-
if (domainMatch && domainMatch[1]) {
|
|
39
|
-
return domainMatch[1];
|
|
40
|
-
}
|
|
41
|
-
return null;
|
|
42
|
-
} catch {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
async function* streamSSEResponse(response) {
|
|
47
|
-
if (!response.body) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const reader = response.body.getReader();
|
|
51
|
-
const decoder = new TextDecoder();
|
|
52
|
-
const queue = [];
|
|
53
|
-
const parser = eventsourceParser.createParser({
|
|
54
|
-
onEvent: (event) => {
|
|
55
|
-
try {
|
|
56
|
-
queue.push(JSON.parse(event.data));
|
|
57
|
-
} catch {
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
try {
|
|
62
|
-
while (true) {
|
|
63
|
-
const { done, value } = await reader.read();
|
|
64
|
-
if (done) {
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
parser.feed(decoder.decode(value, { stream: true }));
|
|
68
|
-
while (queue.length > 0) {
|
|
69
|
-
const item = queue.shift();
|
|
70
|
-
if (item) {
|
|
71
|
-
yield item;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
} finally {
|
|
76
|
-
reader.releaseLock();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function extractTextFromResponse(response) {
|
|
80
|
-
if ("error" in response) {
|
|
81
|
-
return "";
|
|
82
|
-
}
|
|
83
|
-
const result = response.result;
|
|
84
|
-
if (!result) {
|
|
85
|
-
return "";
|
|
86
|
-
}
|
|
87
|
-
if ("kind" in result && result.kind === "status-update") {
|
|
88
|
-
const parts = result.status?.message?.parts;
|
|
89
|
-
if (!parts) {
|
|
90
|
-
return "";
|
|
91
|
-
}
|
|
92
|
-
return parts.filter((part) => part.kind === "text").map((part) => part.text).join("");
|
|
93
|
-
}
|
|
94
|
-
return "";
|
|
95
|
-
}
|
|
96
|
-
function parseChunk(response) {
|
|
97
|
-
if ("error" in response) {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
const result = response.result;
|
|
101
|
-
if (!result) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
if ("kind" in result && result.kind === "status-update") {
|
|
105
|
-
return {
|
|
106
|
-
taskId: result.taskId,
|
|
107
|
-
text: extractTextFromResponse(response),
|
|
108
|
-
status: result.status.state,
|
|
109
|
-
isFinal: result.final ?? false
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
function createCopilotClient(http = httpClient.createHttpClient()) {
|
|
115
|
-
const baseUrl = "/api/proxy/builtin/platform/copilot";
|
|
116
|
-
const internal = {
|
|
117
|
-
getAgentCard() {
|
|
118
|
-
return http.request({
|
|
119
|
-
url: `${baseUrl}/${sdk.AGENT_CARD_PATH}`,
|
|
120
|
-
method: "GET"
|
|
121
|
-
});
|
|
122
|
-
},
|
|
123
|
-
sendMessage(request) {
|
|
124
|
-
return http.request({
|
|
125
|
-
url: baseUrl,
|
|
126
|
-
method: "POST",
|
|
127
|
-
data: request
|
|
128
|
-
});
|
|
129
|
-
},
|
|
130
|
-
async *sendMessageStream(request) {
|
|
131
|
-
try {
|
|
132
|
-
const response = await fetch(baseUrl, {
|
|
133
|
-
method: "POST",
|
|
134
|
-
headers: {
|
|
135
|
-
"Content-Type": "application/json"
|
|
136
|
-
},
|
|
137
|
-
credentials: "include",
|
|
138
|
-
body: JSON.stringify(request)
|
|
139
|
-
});
|
|
140
|
-
if (!response.ok) {
|
|
141
|
-
throw new Error(`Stream request failed: ${response.statusText}`);
|
|
142
|
-
}
|
|
143
|
-
yield* streamSSEResponse(response);
|
|
144
|
-
} catch {
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
cancelTask(request) {
|
|
148
|
-
return http.request({
|
|
149
|
-
url: baseUrl,
|
|
150
|
-
method: "POST",
|
|
151
|
-
data: request
|
|
152
|
-
});
|
|
153
|
-
},
|
|
154
|
-
getTask(request) {
|
|
155
|
-
return http.request({
|
|
156
|
-
url: baseUrl,
|
|
157
|
-
method: "POST",
|
|
158
|
-
data: request
|
|
159
|
-
});
|
|
160
|
-
},
|
|
161
|
-
async *resubscribeTask(request) {
|
|
162
|
-
try {
|
|
163
|
-
const response = await fetch(baseUrl, {
|
|
164
|
-
method: "POST",
|
|
165
|
-
headers: {
|
|
166
|
-
"Content-Type": "application/json"
|
|
167
|
-
},
|
|
168
|
-
credentials: "include",
|
|
169
|
-
body: JSON.stringify(request)
|
|
170
|
-
});
|
|
171
|
-
if (!response.ok) {
|
|
172
|
-
throw new Error(`Stream request failed: ${response.statusText}`);
|
|
173
|
-
}
|
|
174
|
-
yield* streamSSEResponse(response);
|
|
175
|
-
} catch {
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
// ============ Simplified API Implementation ============
|
|
179
|
-
async *chat(messages, options = {}) {
|
|
180
|
-
const { taskId } = options;
|
|
181
|
-
const systemMsg = messages.find((m) => m.role === "system");
|
|
182
|
-
const nonSystemMsgs = messages.filter((m) => m.role !== "system");
|
|
183
|
-
const lastUserMsg = nonSystemMsgs[nonSystemMsgs.length - 1];
|
|
184
|
-
const request = {
|
|
185
|
-
jsonrpc: "2.0",
|
|
186
|
-
id: generateUUID(),
|
|
187
|
-
method: "message/stream",
|
|
188
|
-
params: {
|
|
189
|
-
message: {
|
|
190
|
-
contextId: extractAppIdFromUrl() || generateUUID(),
|
|
191
|
-
kind: "message",
|
|
192
|
-
messageId: generateUUID(),
|
|
193
|
-
role: "user",
|
|
194
|
-
parts: [{ kind: "text", text: lastUserMsg?.content ?? "" }],
|
|
195
|
-
...taskId && { taskId },
|
|
196
|
-
...systemMsg && {
|
|
197
|
-
metadata: { systemPrompt: systemMsg.content }
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
for await (const response of this.sendMessageStream(request)) {
|
|
203
|
-
const chunk = parseChunk(response);
|
|
204
|
-
if (chunk) {
|
|
205
|
-
yield chunk;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
cancelChat(taskId) {
|
|
210
|
-
const request = {
|
|
211
|
-
jsonrpc: "2.0",
|
|
212
|
-
id: generateUUID(),
|
|
213
|
-
method: "tasks/cancel",
|
|
214
|
-
params: { id: taskId }
|
|
215
|
-
};
|
|
216
|
-
return this.cancelTask(request);
|
|
217
|
-
},
|
|
218
|
-
getChatStatus(taskId) {
|
|
219
|
-
const request = {
|
|
220
|
-
jsonrpc: "2.0",
|
|
221
|
-
id: generateUUID(),
|
|
222
|
-
method: "tasks/get",
|
|
223
|
-
params: { id: taskId }
|
|
224
|
-
};
|
|
225
|
-
return this.getTask(request);
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
return {
|
|
229
|
-
chat: internal.chat.bind(internal),
|
|
230
|
-
cancelChat: internal.cancelChat.bind(internal),
|
|
231
|
-
getChatStatus: internal.getChatStatus.bind(internal)
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
exports.createCopilotClient = createCopilotClient;
|
|
236
|
-
//# sourceMappingURL=index.cjs.map
|
|
1
|
+
'use strict';var httpClient=require('@amaster.ai/http-client'),eventsourceParser=require('eventsource-parser'),_0_8=require('@a2ui/lit/0.8');function l(){let t=typeof globalThis<"u"?globalThis.crypto:void 0;return t&&typeof t.randomUUID=="function"?t.randomUUID():t&&typeof t.getRandomValues=="function"?"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let n=(t.getRandomValues(new Uint8Array(1))[0]??0)%16;return (e==="x"?n:n&3|8).toString(16)}):"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let i=Math.random()*16|0;return (e==="x"?i:i&3|8).toString(16)})}function h(){if(typeof window>"u")return null;try{let t=window.location.href,e=/\/app\/([\da-f-]+)(?:\/|$)/.exec(t);if(e&&e[1])return e[1];let i=window.location.hostname,n=/^([\da-f-]+)(?:-[^.]+)?\.[\w-]+\.(?:local|ai)$/.exec(i);return n&&n[1]?n[1]:null}catch{return null}}function S(t){return typeof t=="string"?[{kind:"text",text:t}]:t.map(e=>{switch(e.type){case "text":return {kind:"text",text:e.text};case "image":return e.data?{kind:"file",file:{bytes:e.data,mimeType:e.mimeType||"image/png"}}:e.url?{kind:"file",file:{uri:e.url,mimeType:e.mimeType||"image/png"}}:null;case "file":return e.data?{kind:"file",file:{bytes:e.data,...e.mimeType&&{mimeType:e.mimeType},...e.name&&{name:e.name}}}:e.url?{kind:"file",file:{uri:e.url,...e.mimeType&&{mimeType:e.mimeType},...e.name&&{name:e.name}}}:null;default:return null}}).filter(e=>e!==null)}function M(t){return typeof t=="string"?t:t.filter(e=>e.type==="text").map(e=>e.text).join(`
|
|
2
|
+
`)}var k="application/json+a2ui",R="main",A=0;function p(t){return `${t}-${++A}`}function d(t,e){let i=p("text");return {surfaceUpdate:{surfaceId:e,components:[{id:i,component:{Text:{text:{literalString:t},usageHint:"body"}}}]}}}function I(t,e){let i=t.mimeType||"";if(!i.startsWith("image/"))return null;let n=p("image"),s=t.uri||(t.bytes?`data:${i};base64,${t.bytes}`:null);return s?{surfaceUpdate:{surfaceId:e,components:[{id:n,component:{Image:{url:{literalString:s}}}}]}}:null}function U(t,e){if(!t.subject&&!t.description)return null;let i=p("thought"),n=[];n.push({id:i,component:{Card:{child:`${i}-content`}}});let s=[];return t.subject&&s.push(`${i}-subject`),t.description&&s.push(`${i}-desc`),n.push({id:`${i}-content`,component:{Column:{children:{explicitList:s}}}}),t.subject&&n.push({id:`${i}-subject`,component:{Text:{text:{literalString:`\u{1F4AD} ${t.subject}`},usageHint:"h4"}}}),t.description&&n.push({id:`${i}-desc`,component:{Text:{text:{literalString:t.description},usageHint:"caption"}}}),{surfaceUpdate:{surfaceId:e,components:n}}}function b(t,e){let i=t.tool?.displayName||t.tool?.name||t.request?.name||"Tool",n=t.status||"running",s=p("tool"),r=[];r.push({id:s,component:{Card:{child:`${s}-row`}}}),r.push({id:`${s}-row`,component:{Row:{alignment:"center",children:{explicitList:[`${s}-icon`,`${s}-info`]}}}});let o=n==="completed"?"check_circle":n==="failed"?"error":"build";r.push({id:`${s}-icon`,component:{Icon:{name:{literalString:o}}}}),r.push({id:`${s}-info`,component:{Column:{children:{explicitList:[`${s}-name`,`${s}-status`]}}}}),r.push({id:`${s}-name`,component:{Text:{text:{literalString:i},usageHint:"body"}}});let a=n==="completed"?"\u2713 Completed":n==="failed"?"\u2717 Failed":n==="pending"?"\u23F3 Pending...":"\u2699\uFE0F Running...";return r.push({id:`${s}-status`,component:{Text:{text:{literalString:a},usageHint:"caption"}}}),{surfaceUpdate:{surfaceId:e,components:r}}}var u={THOUGHT:"thought",TOOL_CALL_UPDATE:"tool-call-update",TOOL_CALL_CONFIRMATION:"tool-call-confirmation",TEXT_CONTENT:"text-content"};function w(t,e,i=R){let n=[];for(let s of t)if(s.kind==="text")n.push(d(s.text,i));else if(s.kind==="file"&&"file"in s){let r=s.file,o=I(r,i);o&&n.push(o);}else if(s.kind==="data"){let r=s.data;if(s.metadata?.mimeType===k)n.push(r);else if(e===u.THOUGHT){let o=r;if(o.subject){let a=U(o,i);a&&n.push(a);}else o.description&&n.push(d(o.description,i));}else if(e===u.TOOL_CALL_UPDATE||e===u.TOOL_CALL_CONFIRMATION){let o=b(r,i);o&&n.push(o);}else if(e===u.TEXT_CONTENT){let o=r;o.description&&n.push(d(o.description,i));}}return n}function O(t){if("error"in t)return [];let e=t.result;if(!e||!("kind"in e)||e.kind!=="status-update")return [];let i=e.status?.message?.parts;if(!i||i.length===0)return [];let s=e.metadata?.coderAgent?.kind;return w(i,s)}async function*v(t){if(!t.body)return;let e=t.body.getReader(),i=new TextDecoder,n=[],s=eventsourceParser.createParser({onEvent:r=>{try{n.push(JSON.parse(r.data));}catch{}}});try{for(;;){let{done:r,value:o}=await e.read();if(r)break;for(s.feed(i.decode(o,{stream:!0}));n.length>0;){let a=n.shift();a&&(yield a);}}}finally{e.releaseLock();}}function $(t=httpClient.createHttpClient()){let e="/api/proxy/builtin/platform/copilot";async function*i(n){try{let s=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(n)});if(!s.ok)throw new Error(`Stream request failed: ${s.statusText}`);yield*v(s);}catch{}}return {async*chat(n,s={}){let{taskId:r}=s,o=n.find(c=>c.role==="system"),a=n.filter(c=>c.role!=="system"),m=a[a.length-1],f=m?S(m.content):[{kind:"text",text:""}],g=o?M(o.content):void 0,T={jsonrpc:"2.0",id:l(),method:"message/stream",params:{message:{contextId:h()||l(),kind:"message",messageId:l(),role:"user",parts:f,...r&&{taskId:r},...g&&{metadata:{systemPrompt:g}}}}};for await(let c of i(T)){let x=O(c);x.length>0&&(yield x);}},cancelChat(n){let s={jsonrpc:"2.0",id:l(),method:"tasks/cancel",params:{id:n}};return t.request({url:e,method:"POST",data:s})},getChatStatus(n){let s={jsonrpc:"2.0",id:l(),method:"tasks/get",params:{id:n}};return t.request({url:e,method:"POST",data:s})}}}Object.defineProperty(exports,"Data",{enumerable:true,get:function(){return _0_8.Data}});Object.defineProperty(exports,"Types",{enumerable:true,get:function(){return _0_8.Types}});exports.createCopilotA2UIClient=$;//# sourceMappingURL=index.cjs.map
|
|
237
3
|
//# sourceMappingURL=index.cjs.map
|