@amaster.ai/copilot-client 1.0.0-beta.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amaster Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,338 @@
1
+ # @amaster.ai/copilot-client
2
+
3
+ AI copilot client with full A2A Agent-to-Agent protocol support and streaming chat capabilities.
4
+
5
+ ## Features
6
+
7
+ - 💬 **Streaming Chat**: Real-time streaming responses with SSE
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
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pnpm add @amaster.ai/copilot-client @amaster.ai/http-client axios @a2a-js/sdk eventsource-parser
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { createCopilotClient } from "@amaster.ai/copilot-client";
24
+
25
+ const copilot = createCopilotClient();
26
+
27
+ // Stream chat responses
28
+ for await (const chunk of copilot.chat([
29
+ { role: "user", content: "Hello, how are you?" }
30
+ ])) {
31
+ console.log(chunk.text); // Print each chunk as it arrives
32
+
33
+ if (chunk.isFinal) {
34
+ console.log("Chat completed with status:", chunk.status);
35
+ }
36
+ }
37
+ ```
38
+
39
+ ## Architecture
40
+
41
+ This client implements the **A2A (Agent-to-Agent)** protocol for AI interactions:
42
+
43
+ - **JSON-RPC 2.0**: Standard protocol for method calls
44
+ - **Server-Sent Events (SSE)**: Streaming responses
45
+ - **Task-based**: Each conversation is a trackable task
46
+ - **Context-aware**: Automatically links to app context
47
+
48
+ ## API Reference
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>`
60
+
61
+ ```typescript
62
+ const messages = [
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
+ }
73
+ ```
74
+
75
+ ### Types
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
+ }
93
+
94
+ type TaskState = "RUNNING" | "SUCCEEDED" | "FAILED" | "CANCELLED";
95
+ ```
96
+
97
+ ### `cancelChat(taskId)`
98
+
99
+ Cancel an ongoing chat task.
100
+
101
+ ```typescript
102
+ const taskId = "task-123";
103
+ const result = await copilot.cancelChat(taskId);
104
+
105
+ if (result.data) {
106
+ console.log("Chat cancelled successfully");
107
+ }
108
+ ```
109
+
110
+ ### `getChatStatus(taskId)`
111
+
112
+ Get current status of a chat task.
113
+
114
+ ```typescript
115
+ const result = await copilot.getChatStatus("task-123");
116
+
117
+ if (result.data) {
118
+ console.log("Task status:", result.data.result.status.state);
119
+ }
120
+ ```
121
+
122
+ ## Usage Examples
123
+
124
+ ### Multi-turn Conversation
125
+
126
+ ```typescript
127
+ const messages: ChatMessage[] = [];
128
+
129
+ // First message
130
+ messages.push({ role: "user", content: "What is React?" });
131
+
132
+ let response = "";
133
+ for await (const chunk of copilot.chat(messages)) {
134
+ response += chunk.text;
135
+ }
136
+
137
+ messages.push({ role: "assistant", content: response });
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
168
+
169
+ ```typescript
170
+ // Start a chat
171
+ let taskId: string;
172
+ for await (const chunk of copilot.chat([
173
+ { role: "user", content: "Tell me a long story" }
174
+ ])) {
175
+ taskId = chunk.taskId;
176
+ console.log(chunk.text);
177
+ }
178
+
179
+ // Later, continue the same task
180
+ for await (const chunk of copilot.chat([
181
+ { role: "user", content: "What happened next?" }
182
+ ], { taskId })) {
183
+ console.log(chunk.text);
184
+ }
185
+ ```
186
+
187
+ ### Cancel Long-running Chat
188
+
189
+ ```typescript
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
+ })();
200
+
201
+ // Cancel after 5 seconds
202
+ setTimeout(async () => {
203
+ if (taskId) {
204
+ await copilot.cancelChat(taskId);
205
+ console.log("Chat cancelled");
206
+ }
207
+ }, 5000);
208
+ ```
209
+
210
+ ### React Integration
211
+
212
+ ```typescript
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:
269
+
270
+ ```typescript
271
+ import { createHttpClient } from "@amaster.ai/http-client";
272
+ import axios from "axios";
273
+
274
+ // Configure axios instance
275
+ const axiosInstance = axios.create({
276
+ baseURL: "https://api.example.com",
277
+ timeout: 30000,
278
+ headers: {
279
+ "X-Custom-Header": "value"
280
+ }
281
+ });
282
+
283
+ // Create HTTP client with custom axios
284
+ const httpClient = createHttpClient(axiosInstance);
285
+
286
+ // Create copilot client with custom HTTP client
287
+ const copilot = createCopilotClient(httpClient);
288
+ ```
289
+
290
+ ## Error Handling
291
+
292
+ ```typescript
293
+ try {
294
+ for await (const chunk of copilot.chat([
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
+ }
308
+ ```
309
+
310
+ ## How It Works
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
330
+
331
+ Uses `eventsource-parser` for robust SSE parsing:
332
+ - Handles reconnections
333
+ - Parses JSON payloads
334
+ - Manages stream lifecycle
335
+
336
+ ## License
337
+
338
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,237 @@
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
237
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/copilot-client.ts"],"names":["createParser","createHttpClient","AGENT_CARD_PATH"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n AgentCard,\n CancelTaskRequest,\n CancelTaskResponse,\n GetTaskRequest,\n GetTaskResponse,\n SendMessageRequest,\n SendMessageResponse,\n SendStreamingMessageRequest,\n SendStreamingMessageResponse,\n TaskResubscriptionRequest,\n TaskState,\n} from \"@a2a-js/sdk\";\nimport { AGENT_CARD_PATH } from \"@a2a-js/sdk\";\nimport { createParser } from \"eventsource-parser\";\nimport { type ClientResult, createHttpClient, type HttpClient } from \"@amaster.ai/http-client\";\n\n// ============ UUID Generator with fallback ============\n\n/**\n * Generate a UUID v4 with better browser compatibility\n * Falls back to crypto.getRandomValues if crypto.randomUUID is not available\n */\nfunction generateUUID(): string {\n // Try using crypto.randomUUID if available (modern browsers)\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n // Fallback: use crypto.getRandomValues (better compatibility)\n if (typeof crypto !== \"undefined\" && typeof crypto.getRandomValues === \"function\") {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const randomValues = crypto.getRandomValues(new Uint8Array(1));\n const r = (randomValues[0] ?? 0) % 16;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Extract app_id from current page URL or domain\n * Method 1: From URL path - /app/{app_id}/...\n * Method 2: From domain (subdomain) - {app_id}-{env}.amaster.local\n */\nfunction extractAppIdFromUrl(): string | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n try {\n // Method 1: Try to extract from URL path first\n const url = window.location.href;\n const pathMatch = /\\/app\\/([\\da-f-]+)(?:\\/|$)/.exec(url);\n if (pathMatch && pathMatch[1]) {\n return pathMatch[1];\n }\n\n // Method 2: Try to extract from domain (subdomain)\n const hostname = window.location.hostname;\n const domainMatch = /^([\\da-f-]+)(?:-[^.]+)?\\.amaster\\.(?:local|ai)$/.exec(hostname);\n if (domainMatch && domainMatch[1]) {\n return domainMatch[1];\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n// ============ Simplified API Types ============\n\nexport interface ChatMessage {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n}\n\nexport interface ChatOptions {\n taskId?: string;\n}\n\nexport interface ChatChunk {\n taskId: string;\n text: string;\n status: TaskState;\n isFinal: boolean | undefined;\n}\n\n// ============ Client Types ============\n\n// Internal client with all A2A methods\ntype InternalCopilotClient = {\n getAgentCard(): Promise<ClientResult<AgentCard>>;\n sendMessage(request: SendMessageRequest): Promise<ClientResult<SendMessageResponse>>;\n sendMessageStream(\n request: SendStreamingMessageRequest\n ): AsyncGenerator<SendStreamingMessageResponse, void, unknown>;\n cancelTask(request: CancelTaskRequest): Promise<ClientResult<CancelTaskResponse>>;\n getTask(request: GetTaskRequest): Promise<ClientResult<GetTaskResponse>>;\n resubscribeTask(\n request: TaskResubscriptionRequest\n ): AsyncGenerator<SendStreamingMessageResponse, void, unknown>;\n chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;\n cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;\n getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;\n};\n\n// Public client with only simplified API\nexport type CopilotClient = {\n chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;\n cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;\n getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;\n};\n\nasync function* streamSSEResponse(\n response: Response\n): AsyncGenerator<SendStreamingMessageResponse, void, unknown> {\n if (!response.body) {\n return;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n const queue: SendStreamingMessageResponse[] = [];\n\n const parser = createParser({\n onEvent: (event) => {\n try {\n queue.push(JSON.parse(event.data));\n } catch {\n // Ignore parse errors for malformed SSE data\n }\n },\n });\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n parser.feed(decoder.decode(value, { stream: true }));\n while (queue.length > 0) {\n const item = queue.shift();\n if (item) {\n yield item;\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction extractTextFromResponse(response: SendStreamingMessageResponse): string {\n if (\"error\" in response) {\n return \"\";\n }\n const result = response.result;\n if (!result) {\n return \"\";\n }\n\n // TaskStatusUpdateEvent has kind: 'status-update'\n if (\"kind\" in result && result.kind === \"status-update\") {\n const parts = result.status?.message?.parts;\n if (!parts) {\n return \"\";\n }\n return parts\n .filter((part): part is { kind: \"text\"; text: string } => part.kind === \"text\")\n .map((part) => part.text)\n .join(\"\");\n }\n return \"\";\n}\n\nfunction parseChunk(response: SendStreamingMessageResponse): ChatChunk | null {\n if (\"error\" in response) {\n return null;\n }\n const result = response.result;\n if (!result) {\n return null;\n }\n\n // TaskStatusUpdateEvent has kind: 'status-update'\n if (\"kind\" in result && result.kind === \"status-update\") {\n return {\n taskId: result.taskId,\n text: extractTextFromResponse(response),\n status: result.status.state,\n isFinal: result.final ?? false,\n };\n }\n return null;\n}\n\nexport function createCopilotClient(http: HttpClient = createHttpClient()): CopilotClient {\n const baseUrl = \"/api/proxy/builtin/platform/copilot\";\n\n const internal: InternalCopilotClient = {\n getAgentCard() {\n return http.request<AgentCard>({\n url: `${baseUrl}/${AGENT_CARD_PATH}`,\n method: \"GET\",\n });\n },\n\n sendMessage(request) {\n return http.request<SendMessageResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n async *sendMessageStream(request) {\n try {\n const response = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Stream request failed: ${response.statusText}`);\n }\n\n yield* streamSSEResponse(response);\n } catch {\n // Ignore stream errors - caller should handle connection issues\n }\n },\n\n cancelTask(request) {\n return http.request<CancelTaskResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n getTask(request) {\n return http.request<GetTaskResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n async *resubscribeTask(request) {\n try {\n const response = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Stream request failed: ${response.statusText}`);\n }\n\n yield* streamSSEResponse(response);\n } catch {\n // Ignore resubscribe errors - caller should handle reconnection\n }\n },\n\n // ============ Simplified API Implementation ============\n\n async *chat(messages, options = {}) {\n const { taskId } = options;\n\n const systemMsg = messages.find((m) => m.role === \"system\");\n const nonSystemMsgs = messages.filter((m) => m.role !== \"system\");\n const lastUserMsg = nonSystemMsgs[nonSystemMsgs.length - 1];\n\n const request: SendStreamingMessageRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"message/stream\",\n params: {\n message: {\n contextId: extractAppIdFromUrl() || generateUUID(),\n kind: \"message\",\n messageId: generateUUID(),\n role: \"user\",\n parts: [{ kind: \"text\", text: lastUserMsg?.content ?? \"\" }],\n ...(taskId && { taskId }),\n ...(systemMsg && {\n metadata: { systemPrompt: systemMsg.content },\n }),\n },\n },\n };\n\n for await (const response of this.sendMessageStream(request)) {\n const chunk = parseChunk(response);\n if (chunk) {\n yield chunk;\n }\n }\n },\n\n cancelChat(taskId) {\n const request: CancelTaskRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"tasks/cancel\",\n params: { id: taskId },\n };\n return this.cancelTask(request);\n },\n\n getChatStatus(taskId) {\n const request: GetTaskRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"tasks/get\",\n params: { id: taskId },\n };\n return this.getTask(request);\n },\n };\n\n // Return only the public API\n return {\n chat: internal.chat.bind(internal),\n cancelChat: internal.cancelChat.bind(internal),\n getChatStatus: internal.getChatStatus.bind(internal),\n };\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import { TaskState, CancelTaskResponse, GetTaskResponse } from '@a2a-js/sdk';
2
+ import { ClientResult, HttpClient } from '@amaster.ai/http-client';
3
+
4
+ interface ChatMessage {
5
+ role: "system" | "user" | "assistant";
6
+ content: string;
7
+ }
8
+ interface ChatOptions {
9
+ taskId?: string;
10
+ }
11
+ interface ChatChunk {
12
+ taskId: string;
13
+ text: string;
14
+ status: TaskState;
15
+ isFinal: boolean | undefined;
16
+ }
17
+ type CopilotClient = {
18
+ chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;
19
+ cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;
20
+ getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;
21
+ };
22
+ declare function createCopilotClient(http?: HttpClient): CopilotClient;
23
+
24
+ export { type ChatChunk, type ChatMessage, type ChatOptions, type CopilotClient, createCopilotClient };
@@ -0,0 +1,24 @@
1
+ import { TaskState, CancelTaskResponse, GetTaskResponse } from '@a2a-js/sdk';
2
+ import { ClientResult, HttpClient } from '@amaster.ai/http-client';
3
+
4
+ interface ChatMessage {
5
+ role: "system" | "user" | "assistant";
6
+ content: string;
7
+ }
8
+ interface ChatOptions {
9
+ taskId?: string;
10
+ }
11
+ interface ChatChunk {
12
+ taskId: string;
13
+ text: string;
14
+ status: TaskState;
15
+ isFinal: boolean | undefined;
16
+ }
17
+ type CopilotClient = {
18
+ chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;
19
+ cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;
20
+ getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;
21
+ };
22
+ declare function createCopilotClient(http?: HttpClient): CopilotClient;
23
+
24
+ export { type ChatChunk, type ChatMessage, type ChatOptions, type CopilotClient, createCopilotClient };
package/dist/index.js ADDED
@@ -0,0 +1,235 @@
1
+ import { AGENT_CARD_PATH } from '@a2a-js/sdk';
2
+ import { createParser } from 'eventsource-parser';
3
+ import { createHttpClient } from '@amaster.ai/http-client';
4
+
5
+ // src/copilot-client.ts
6
+ function generateUUID() {
7
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
8
+ return crypto.randomUUID();
9
+ }
10
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
11
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
12
+ const randomValues = crypto.getRandomValues(new Uint8Array(1));
13
+ const r = (randomValues[0] ?? 0) % 16;
14
+ const v = c === "x" ? r : r & 3 | 8;
15
+ return v.toString(16);
16
+ });
17
+ }
18
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
19
+ const r = Math.random() * 16 | 0;
20
+ const v = c === "x" ? r : r & 3 | 8;
21
+ return v.toString(16);
22
+ });
23
+ }
24
+ function extractAppIdFromUrl() {
25
+ if (typeof window === "undefined") {
26
+ return null;
27
+ }
28
+ try {
29
+ const url = window.location.href;
30
+ const pathMatch = /\/app\/([\da-f-]+)(?:\/|$)/.exec(url);
31
+ if (pathMatch && pathMatch[1]) {
32
+ return pathMatch[1];
33
+ }
34
+ const hostname = window.location.hostname;
35
+ const domainMatch = /^([\da-f-]+)(?:-[^.]+)?\.amaster\.(?:local|ai)$/.exec(hostname);
36
+ if (domainMatch && domainMatch[1]) {
37
+ return domainMatch[1];
38
+ }
39
+ return null;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+ async function* streamSSEResponse(response) {
45
+ if (!response.body) {
46
+ return;
47
+ }
48
+ const reader = response.body.getReader();
49
+ const decoder = new TextDecoder();
50
+ const queue = [];
51
+ const parser = createParser({
52
+ onEvent: (event) => {
53
+ try {
54
+ queue.push(JSON.parse(event.data));
55
+ } catch {
56
+ }
57
+ }
58
+ });
59
+ try {
60
+ while (true) {
61
+ const { done, value } = await reader.read();
62
+ if (done) {
63
+ break;
64
+ }
65
+ parser.feed(decoder.decode(value, { stream: true }));
66
+ while (queue.length > 0) {
67
+ const item = queue.shift();
68
+ if (item) {
69
+ yield item;
70
+ }
71
+ }
72
+ }
73
+ } finally {
74
+ reader.releaseLock();
75
+ }
76
+ }
77
+ function extractTextFromResponse(response) {
78
+ if ("error" in response) {
79
+ return "";
80
+ }
81
+ const result = response.result;
82
+ if (!result) {
83
+ return "";
84
+ }
85
+ if ("kind" in result && result.kind === "status-update") {
86
+ const parts = result.status?.message?.parts;
87
+ if (!parts) {
88
+ return "";
89
+ }
90
+ return parts.filter((part) => part.kind === "text").map((part) => part.text).join("");
91
+ }
92
+ return "";
93
+ }
94
+ function parseChunk(response) {
95
+ if ("error" in response) {
96
+ return null;
97
+ }
98
+ const result = response.result;
99
+ if (!result) {
100
+ return null;
101
+ }
102
+ if ("kind" in result && result.kind === "status-update") {
103
+ return {
104
+ taskId: result.taskId,
105
+ text: extractTextFromResponse(response),
106
+ status: result.status.state,
107
+ isFinal: result.final ?? false
108
+ };
109
+ }
110
+ return null;
111
+ }
112
+ function createCopilotClient(http = createHttpClient()) {
113
+ const baseUrl = "/api/proxy/builtin/platform/copilot";
114
+ const internal = {
115
+ getAgentCard() {
116
+ return http.request({
117
+ url: `${baseUrl}/${AGENT_CARD_PATH}`,
118
+ method: "GET"
119
+ });
120
+ },
121
+ sendMessage(request) {
122
+ return http.request({
123
+ url: baseUrl,
124
+ method: "POST",
125
+ data: request
126
+ });
127
+ },
128
+ async *sendMessageStream(request) {
129
+ try {
130
+ const response = await fetch(baseUrl, {
131
+ method: "POST",
132
+ headers: {
133
+ "Content-Type": "application/json"
134
+ },
135
+ credentials: "include",
136
+ body: JSON.stringify(request)
137
+ });
138
+ if (!response.ok) {
139
+ throw new Error(`Stream request failed: ${response.statusText}`);
140
+ }
141
+ yield* streamSSEResponse(response);
142
+ } catch {
143
+ }
144
+ },
145
+ cancelTask(request) {
146
+ return http.request({
147
+ url: baseUrl,
148
+ method: "POST",
149
+ data: request
150
+ });
151
+ },
152
+ getTask(request) {
153
+ return http.request({
154
+ url: baseUrl,
155
+ method: "POST",
156
+ data: request
157
+ });
158
+ },
159
+ async *resubscribeTask(request) {
160
+ try {
161
+ const response = await fetch(baseUrl, {
162
+ method: "POST",
163
+ headers: {
164
+ "Content-Type": "application/json"
165
+ },
166
+ credentials: "include",
167
+ body: JSON.stringify(request)
168
+ });
169
+ if (!response.ok) {
170
+ throw new Error(`Stream request failed: ${response.statusText}`);
171
+ }
172
+ yield* streamSSEResponse(response);
173
+ } catch {
174
+ }
175
+ },
176
+ // ============ Simplified API Implementation ============
177
+ async *chat(messages, options = {}) {
178
+ const { taskId } = options;
179
+ const systemMsg = messages.find((m) => m.role === "system");
180
+ const nonSystemMsgs = messages.filter((m) => m.role !== "system");
181
+ const lastUserMsg = nonSystemMsgs[nonSystemMsgs.length - 1];
182
+ const request = {
183
+ jsonrpc: "2.0",
184
+ id: generateUUID(),
185
+ method: "message/stream",
186
+ params: {
187
+ message: {
188
+ contextId: extractAppIdFromUrl() || generateUUID(),
189
+ kind: "message",
190
+ messageId: generateUUID(),
191
+ role: "user",
192
+ parts: [{ kind: "text", text: lastUserMsg?.content ?? "" }],
193
+ ...taskId && { taskId },
194
+ ...systemMsg && {
195
+ metadata: { systemPrompt: systemMsg.content }
196
+ }
197
+ }
198
+ }
199
+ };
200
+ for await (const response of this.sendMessageStream(request)) {
201
+ const chunk = parseChunk(response);
202
+ if (chunk) {
203
+ yield chunk;
204
+ }
205
+ }
206
+ },
207
+ cancelChat(taskId) {
208
+ const request = {
209
+ jsonrpc: "2.0",
210
+ id: generateUUID(),
211
+ method: "tasks/cancel",
212
+ params: { id: taskId }
213
+ };
214
+ return this.cancelTask(request);
215
+ },
216
+ getChatStatus(taskId) {
217
+ const request = {
218
+ jsonrpc: "2.0",
219
+ id: generateUUID(),
220
+ method: "tasks/get",
221
+ params: { id: taskId }
222
+ };
223
+ return this.getTask(request);
224
+ }
225
+ };
226
+ return {
227
+ chat: internal.chat.bind(internal),
228
+ cancelChat: internal.cancelChat.bind(internal),
229
+ getChatStatus: internal.getChatStatus.bind(internal)
230
+ };
231
+ }
232
+
233
+ export { createCopilotClient };
234
+ //# sourceMappingURL=index.js.map
235
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/copilot-client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n AgentCard,\n CancelTaskRequest,\n CancelTaskResponse,\n GetTaskRequest,\n GetTaskResponse,\n SendMessageRequest,\n SendMessageResponse,\n SendStreamingMessageRequest,\n SendStreamingMessageResponse,\n TaskResubscriptionRequest,\n TaskState,\n} from \"@a2a-js/sdk\";\nimport { AGENT_CARD_PATH } from \"@a2a-js/sdk\";\nimport { createParser } from \"eventsource-parser\";\nimport { type ClientResult, createHttpClient, type HttpClient } from \"@amaster.ai/http-client\";\n\n// ============ UUID Generator with fallback ============\n\n/**\n * Generate a UUID v4 with better browser compatibility\n * Falls back to crypto.getRandomValues if crypto.randomUUID is not available\n */\nfunction generateUUID(): string {\n // Try using crypto.randomUUID if available (modern browsers)\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n // Fallback: use crypto.getRandomValues (better compatibility)\n if (typeof crypto !== \"undefined\" && typeof crypto.getRandomValues === \"function\") {\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const randomValues = crypto.getRandomValues(new Uint8Array(1));\n const r = (randomValues[0] ?? 0) % 16;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n }\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Extract app_id from current page URL or domain\n * Method 1: From URL path - /app/{app_id}/...\n * Method 2: From domain (subdomain) - {app_id}-{env}.amaster.local\n */\nfunction extractAppIdFromUrl(): string | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n try {\n // Method 1: Try to extract from URL path first\n const url = window.location.href;\n const pathMatch = /\\/app\\/([\\da-f-]+)(?:\\/|$)/.exec(url);\n if (pathMatch && pathMatch[1]) {\n return pathMatch[1];\n }\n\n // Method 2: Try to extract from domain (subdomain)\n const hostname = window.location.hostname;\n const domainMatch = /^([\\da-f-]+)(?:-[^.]+)?\\.amaster\\.(?:local|ai)$/.exec(hostname);\n if (domainMatch && domainMatch[1]) {\n return domainMatch[1];\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n// ============ Simplified API Types ============\n\nexport interface ChatMessage {\n role: \"system\" | \"user\" | \"assistant\";\n content: string;\n}\n\nexport interface ChatOptions {\n taskId?: string;\n}\n\nexport interface ChatChunk {\n taskId: string;\n text: string;\n status: TaskState;\n isFinal: boolean | undefined;\n}\n\n// ============ Client Types ============\n\n// Internal client with all A2A methods\ntype InternalCopilotClient = {\n getAgentCard(): Promise<ClientResult<AgentCard>>;\n sendMessage(request: SendMessageRequest): Promise<ClientResult<SendMessageResponse>>;\n sendMessageStream(\n request: SendStreamingMessageRequest\n ): AsyncGenerator<SendStreamingMessageResponse, void, unknown>;\n cancelTask(request: CancelTaskRequest): Promise<ClientResult<CancelTaskResponse>>;\n getTask(request: GetTaskRequest): Promise<ClientResult<GetTaskResponse>>;\n resubscribeTask(\n request: TaskResubscriptionRequest\n ): AsyncGenerator<SendStreamingMessageResponse, void, unknown>;\n chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;\n cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;\n getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;\n};\n\n// Public client with only simplified API\nexport type CopilotClient = {\n chat(messages: ChatMessage[], options?: ChatOptions): AsyncGenerator<ChatChunk, void, unknown>;\n cancelChat(taskId: string): Promise<ClientResult<CancelTaskResponse>>;\n getChatStatus(taskId: string): Promise<ClientResult<GetTaskResponse>>;\n};\n\nasync function* streamSSEResponse(\n response: Response\n): AsyncGenerator<SendStreamingMessageResponse, void, unknown> {\n if (!response.body) {\n return;\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n const queue: SendStreamingMessageResponse[] = [];\n\n const parser = createParser({\n onEvent: (event) => {\n try {\n queue.push(JSON.parse(event.data));\n } catch {\n // Ignore parse errors for malformed SSE data\n }\n },\n });\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n parser.feed(decoder.decode(value, { stream: true }));\n while (queue.length > 0) {\n const item = queue.shift();\n if (item) {\n yield item;\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction extractTextFromResponse(response: SendStreamingMessageResponse): string {\n if (\"error\" in response) {\n return \"\";\n }\n const result = response.result;\n if (!result) {\n return \"\";\n }\n\n // TaskStatusUpdateEvent has kind: 'status-update'\n if (\"kind\" in result && result.kind === \"status-update\") {\n const parts = result.status?.message?.parts;\n if (!parts) {\n return \"\";\n }\n return parts\n .filter((part): part is { kind: \"text\"; text: string } => part.kind === \"text\")\n .map((part) => part.text)\n .join(\"\");\n }\n return \"\";\n}\n\nfunction parseChunk(response: SendStreamingMessageResponse): ChatChunk | null {\n if (\"error\" in response) {\n return null;\n }\n const result = response.result;\n if (!result) {\n return null;\n }\n\n // TaskStatusUpdateEvent has kind: 'status-update'\n if (\"kind\" in result && result.kind === \"status-update\") {\n return {\n taskId: result.taskId,\n text: extractTextFromResponse(response),\n status: result.status.state,\n isFinal: result.final ?? false,\n };\n }\n return null;\n}\n\nexport function createCopilotClient(http: HttpClient = createHttpClient()): CopilotClient {\n const baseUrl = \"/api/proxy/builtin/platform/copilot\";\n\n const internal: InternalCopilotClient = {\n getAgentCard() {\n return http.request<AgentCard>({\n url: `${baseUrl}/${AGENT_CARD_PATH}`,\n method: \"GET\",\n });\n },\n\n sendMessage(request) {\n return http.request<SendMessageResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n async *sendMessageStream(request) {\n try {\n const response = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Stream request failed: ${response.statusText}`);\n }\n\n yield* streamSSEResponse(response);\n } catch {\n // Ignore stream errors - caller should handle connection issues\n }\n },\n\n cancelTask(request) {\n return http.request<CancelTaskResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n getTask(request) {\n return http.request<GetTaskResponse>({\n url: baseUrl,\n method: \"POST\",\n data: request,\n });\n },\n\n async *resubscribeTask(request) {\n try {\n const response = await fetch(baseUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n credentials: \"include\",\n body: JSON.stringify(request),\n });\n\n if (!response.ok) {\n throw new Error(`Stream request failed: ${response.statusText}`);\n }\n\n yield* streamSSEResponse(response);\n } catch {\n // Ignore resubscribe errors - caller should handle reconnection\n }\n },\n\n // ============ Simplified API Implementation ============\n\n async *chat(messages, options = {}) {\n const { taskId } = options;\n\n const systemMsg = messages.find((m) => m.role === \"system\");\n const nonSystemMsgs = messages.filter((m) => m.role !== \"system\");\n const lastUserMsg = nonSystemMsgs[nonSystemMsgs.length - 1];\n\n const request: SendStreamingMessageRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"message/stream\",\n params: {\n message: {\n contextId: extractAppIdFromUrl() || generateUUID(),\n kind: \"message\",\n messageId: generateUUID(),\n role: \"user\",\n parts: [{ kind: \"text\", text: lastUserMsg?.content ?? \"\" }],\n ...(taskId && { taskId }),\n ...(systemMsg && {\n metadata: { systemPrompt: systemMsg.content },\n }),\n },\n },\n };\n\n for await (const response of this.sendMessageStream(request)) {\n const chunk = parseChunk(response);\n if (chunk) {\n yield chunk;\n }\n }\n },\n\n cancelChat(taskId) {\n const request: CancelTaskRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"tasks/cancel\",\n params: { id: taskId },\n };\n return this.cancelTask(request);\n },\n\n getChatStatus(taskId) {\n const request: GetTaskRequest = {\n jsonrpc: \"2.0\",\n id: generateUUID(),\n method: \"tasks/get\",\n params: { id: taskId },\n };\n return this.getTask(request);\n },\n };\n\n // Return only the public API\n return {\n chat: internal.chat.bind(internal),\n cancelChat: internal.cancelChat.bind(internal),\n getChatStatus: internal.getChatStatus.bind(internal),\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@amaster.ai/copilot-client",
3
+ "version": "1.0.0-beta.0",
4
+ "description": "AI copilot chat client with streaming support",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs",
13
+ "types": "./dist/index.d.ts"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "keywords": [
21
+ "ai",
22
+ "copilot",
23
+ "chat",
24
+ "streaming",
25
+ "typescript"
26
+ ],
27
+ "author": "Amaster Team",
28
+ "license": "MIT",
29
+ "publishConfig": {
30
+ "access": "public",
31
+ "registry": "https://registry.npmjs.org/"
32
+ },
33
+ "dependencies": {
34
+ "@amaster.ai/http-client": "1.0.0-beta.0"
35
+ },
36
+ "peerDependencies": {
37
+ "@a2a-js/sdk": "^0.3.7",
38
+ "axios": "^1.11.0",
39
+ "eventsource-parser": "^3.0.6"
40
+ },
41
+ "devDependencies": {
42
+ "@a2a-js/sdk": "^0.3.7",
43
+ "axios": "^1.11.0",
44
+ "eventsource-parser": "^3.0.6",
45
+ "tsup": "^8.3.5",
46
+ "typescript": "~5.7.2"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "dev": "tsup --watch",
51
+ "clean": "rm -rf dist *.tsbuildinfo",
52
+ "type-check": "tsc --noEmit"
53
+ }
54
+ }