@cadraos/sdk 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/index.cjs +2874 -102
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +2624 -480
- package/dist/chat/index.d.ts +2624 -480
- package/dist/chat/index.js +2874 -102
- package/dist/chat/index.js.map +1 -1
- package/dist/index.cjs +14 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +14 -9
- package/dist/index.js.map +1 -1
- package/package.json +10 -6
package/dist/chat/index.js
CHANGED
|
@@ -3,77 +3,2293 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
3
3
|
import ReactMarkdown from 'react-markdown';
|
|
4
4
|
import remarkGfm from 'remark-gfm';
|
|
5
5
|
|
|
6
|
-
// src/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
// src/resources/base.ts
|
|
7
|
+
var BaseResourceAPI = class {
|
|
8
|
+
http;
|
|
9
|
+
basePath;
|
|
10
|
+
constructor(http, basePath) {
|
|
11
|
+
this.http = http;
|
|
12
|
+
this.basePath = basePath;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build query parameters from list options
|
|
16
|
+
*/
|
|
17
|
+
buildListQuery(options) {
|
|
18
|
+
if (!options) return {};
|
|
19
|
+
return {
|
|
20
|
+
limit: options.limit,
|
|
21
|
+
offset: options.offset,
|
|
22
|
+
sort: options.sort,
|
|
23
|
+
order: options.order
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* HTTP GET request helper
|
|
28
|
+
*/
|
|
29
|
+
async httpGet(path, query) {
|
|
30
|
+
return this.http.get(`${this.basePath}${path}`, query);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* HTTP POST request helper
|
|
34
|
+
*/
|
|
35
|
+
async httpPost(path, body, options) {
|
|
36
|
+
return this.http.post(`${this.basePath}${path}`, body, options);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* HTTP PATCH request helper
|
|
40
|
+
*/
|
|
41
|
+
async httpPatch(path, body, options) {
|
|
42
|
+
return this.http.patch(`${this.basePath}${path}`, body, options);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* HTTP DELETE request helper
|
|
46
|
+
*/
|
|
47
|
+
async httpDelete(path) {
|
|
48
|
+
return this.http.delete(`${this.basePath}${path}`);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* List resources with pagination
|
|
52
|
+
*/
|
|
53
|
+
async listResources(path, options) {
|
|
54
|
+
const query = this.buildListQuery(options);
|
|
55
|
+
return this.httpGet(path, query);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// src/errors/sdk-errors.ts
|
|
60
|
+
var AISaaSError = class extends Error {
|
|
61
|
+
/** Error code */
|
|
62
|
+
code;
|
|
63
|
+
/** HTTP status code */
|
|
64
|
+
status;
|
|
65
|
+
/** Additional error details */
|
|
66
|
+
details;
|
|
67
|
+
/** Request ID from server (for debugging) */
|
|
68
|
+
requestId;
|
|
69
|
+
constructor(message, code, status, details, requestId) {
|
|
70
|
+
super(message);
|
|
71
|
+
this.name = "AISaaSError";
|
|
72
|
+
this.code = code;
|
|
73
|
+
this.status = status;
|
|
74
|
+
this.details = details;
|
|
75
|
+
this.requestId = requestId;
|
|
76
|
+
if (Error.captureStackTrace) {
|
|
77
|
+
Error.captureStackTrace(this, this.constructor);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var AuthenticationError = class extends AISaaSError {
|
|
82
|
+
constructor(message = "Authentication failed", code = "authentication_error", details, requestId) {
|
|
83
|
+
super(message, code, 401, details, requestId);
|
|
84
|
+
this.name = "AuthenticationError";
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var AuthorizationError = class extends AISaaSError {
|
|
88
|
+
constructor(message = "Access denied", code = "authorization_error", details, requestId) {
|
|
89
|
+
super(message, code, 403, details, requestId);
|
|
90
|
+
this.name = "AuthorizationError";
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
var NotFoundError = class extends AISaaSError {
|
|
94
|
+
constructor(message = "Resource not found", code = "not_found", details, requestId) {
|
|
95
|
+
super(message, code, 404, details, requestId);
|
|
96
|
+
this.name = "NotFoundError";
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var ValidationError = class extends AISaaSError {
|
|
100
|
+
/** Field-level validation errors */
|
|
101
|
+
errors;
|
|
102
|
+
constructor(message = "Validation failed", errors = [], details, requestId) {
|
|
103
|
+
super(message, "validation_error", 400, details, requestId);
|
|
104
|
+
this.name = "ValidationError";
|
|
105
|
+
this.errors = errors;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var RateLimitError = class extends AISaaSError {
|
|
109
|
+
/** Seconds until rate limit resets */
|
|
110
|
+
retryAfter;
|
|
111
|
+
constructor(message = "Rate limit exceeded", retryAfter, details, requestId) {
|
|
112
|
+
super(message, "rate_limit_error", 429, details, requestId);
|
|
113
|
+
this.name = "RateLimitError";
|
|
114
|
+
this.retryAfter = retryAfter;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var ServerError = class extends AISaaSError {
|
|
118
|
+
constructor(message = "Server error", status = 500, details, requestId) {
|
|
119
|
+
super(message, "server_error", status, details, requestId);
|
|
120
|
+
this.name = "ServerError";
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var ConflictError = class extends AISaaSError {
|
|
124
|
+
constructor(message = "Resource conflict", details, requestId) {
|
|
125
|
+
super(message, "conflict", 409, details, requestId);
|
|
126
|
+
this.name = "ConflictError";
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var TimeoutError = class extends AISaaSError {
|
|
130
|
+
constructor(message = "Request timed out", details, requestId) {
|
|
131
|
+
super(message, "timeout", 408, details, requestId);
|
|
132
|
+
this.name = "TimeoutError";
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var NetworkError = class extends AISaaSError {
|
|
136
|
+
constructor(message = "Network error", details) {
|
|
137
|
+
super(message, "network_error", 0, details);
|
|
138
|
+
this.name = "NetworkError";
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
function parseApiError(status, body, requestId) {
|
|
142
|
+
const errorBody = body;
|
|
143
|
+
const message = errorBody?.error?.message ?? "An error occurred";
|
|
144
|
+
const code = errorBody?.error?.code ?? "unknown_error";
|
|
145
|
+
const details = errorBody?.error?.details;
|
|
146
|
+
switch (status) {
|
|
147
|
+
case 400:
|
|
148
|
+
case 422: {
|
|
149
|
+
const fieldErrors = details?.errors;
|
|
150
|
+
if (fieldErrors) {
|
|
151
|
+
const errors = [];
|
|
152
|
+
for (const [field, messages] of Object.entries(fieldErrors)) {
|
|
153
|
+
for (const msg of messages) {
|
|
154
|
+
errors.push({ field, message: msg });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return new ValidationError(message, errors, details, requestId);
|
|
158
|
+
}
|
|
159
|
+
return new ValidationError(message, [], details, requestId);
|
|
160
|
+
}
|
|
161
|
+
case 401:
|
|
162
|
+
return new AuthenticationError(message, code, details, requestId);
|
|
163
|
+
case 403:
|
|
164
|
+
return new AuthorizationError(message, code, details, requestId);
|
|
165
|
+
case 404:
|
|
166
|
+
return new NotFoundError(message, code, details, requestId);
|
|
167
|
+
case 409:
|
|
168
|
+
return new ConflictError(message, details, requestId);
|
|
169
|
+
case 429: {
|
|
170
|
+
const retryAfter = details?.retryAfter;
|
|
171
|
+
return new RateLimitError(message, retryAfter, details, requestId);
|
|
172
|
+
}
|
|
173
|
+
default:
|
|
174
|
+
if (status >= 500) {
|
|
175
|
+
return new ServerError(message, status, details, requestId);
|
|
176
|
+
}
|
|
177
|
+
return new AISaaSError(message, code, status, details, requestId);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/utils/retry.ts
|
|
182
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
183
|
+
maxRetries: 3,
|
|
184
|
+
baseDelayMs: 1e3,
|
|
185
|
+
maxDelayMs: 3e4,
|
|
186
|
+
jitterFactor: 0.1
|
|
187
|
+
};
|
|
188
|
+
function shouldRetry(error, attempt, maxRetries) {
|
|
189
|
+
if (attempt >= maxRetries) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (error instanceof RateLimitError) {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
if (error instanceof ServerError) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
if (error instanceof Error && error.name === "NetworkError") {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
function calculateRetryDelay(attempt, config, retryAfter) {
|
|
207
|
+
if (retryAfter !== void 0 && retryAfter > 0) {
|
|
208
|
+
return retryAfter * 1e3;
|
|
209
|
+
}
|
|
210
|
+
const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt);
|
|
211
|
+
const cappedDelay = Math.min(exponentialDelay, config.maxDelayMs);
|
|
212
|
+
const jitter = cappedDelay * config.jitterFactor * Math.random();
|
|
213
|
+
return cappedDelay + jitter;
|
|
214
|
+
}
|
|
215
|
+
function sleep(ms) {
|
|
216
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
217
|
+
}
|
|
218
|
+
async function withRetry(operation, config = {}) {
|
|
219
|
+
const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
|
|
220
|
+
let lastError;
|
|
221
|
+
for (let attempt = 0; attempt <= fullConfig.maxRetries; attempt++) {
|
|
222
|
+
try {
|
|
223
|
+
return await operation();
|
|
224
|
+
} catch (error) {
|
|
225
|
+
lastError = error;
|
|
226
|
+
if (!shouldRetry(error, attempt, fullConfig.maxRetries)) {
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
const retryAfter = error instanceof RateLimitError ? error.retryAfter : void 0;
|
|
230
|
+
const delay = calculateRetryDelay(attempt, fullConfig, retryAfter);
|
|
231
|
+
await sleep(delay);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
throw lastError;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/utils/http.ts
|
|
238
|
+
var HttpClient = class {
|
|
239
|
+
config;
|
|
240
|
+
tokenRefreshAttempts = 0;
|
|
241
|
+
maxTokenRefreshAttempts = 2;
|
|
242
|
+
constructor(config) {
|
|
243
|
+
this.config = config;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get authorization header value
|
|
247
|
+
*/
|
|
248
|
+
async getAuthHeader() {
|
|
249
|
+
if (this.config.apiKey) {
|
|
250
|
+
return `Bearer ${this.config.apiKey}`;
|
|
251
|
+
}
|
|
252
|
+
if (this.config.tokenProvider) {
|
|
253
|
+
const token = await this.config.tokenProvider();
|
|
254
|
+
return `Bearer ${token}`;
|
|
255
|
+
}
|
|
256
|
+
if (this.config.token) {
|
|
257
|
+
return `Bearer ${this.config.token}`;
|
|
258
|
+
}
|
|
259
|
+
throw new Error("No authentication configured. Provide apiKey, token, or tokenProvider.");
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Build full URL with query parameters
|
|
263
|
+
*/
|
|
264
|
+
buildUrl(path, query) {
|
|
265
|
+
const url = new URL(path, this.config.baseUrl);
|
|
266
|
+
if (query) {
|
|
267
|
+
for (const [key, value] of Object.entries(query)) {
|
|
268
|
+
if (value !== void 0) {
|
|
269
|
+
url.searchParams.set(key, String(value));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return url.toString();
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Make an HTTP request
|
|
277
|
+
*/
|
|
278
|
+
async request(options) {
|
|
279
|
+
const makeRequest = async () => {
|
|
280
|
+
const url = this.buildUrl(options.path, options.query);
|
|
281
|
+
const authHeader = await this.getAuthHeader();
|
|
282
|
+
const headers = {
|
|
283
|
+
"Authorization": authHeader,
|
|
284
|
+
"X-Org-Id": String(this.config.orgId),
|
|
285
|
+
"Content-Type": "application/json",
|
|
286
|
+
...this.config.headers,
|
|
287
|
+
...options.headers
|
|
288
|
+
};
|
|
289
|
+
if (options.idempotencyKey) {
|
|
290
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
291
|
+
}
|
|
292
|
+
const controller = new AbortController();
|
|
293
|
+
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
|
|
294
|
+
try {
|
|
295
|
+
const response = await fetch(url, {
|
|
296
|
+
method: options.method,
|
|
297
|
+
headers,
|
|
298
|
+
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
299
|
+
signal: controller.signal
|
|
300
|
+
});
|
|
301
|
+
clearTimeout(timeoutId);
|
|
302
|
+
const requestId = response.headers.get("X-Request-Id") ?? void 0;
|
|
303
|
+
let body;
|
|
304
|
+
const contentType = response.headers.get("Content-Type");
|
|
305
|
+
if (contentType?.includes("application/json")) {
|
|
306
|
+
body = await response.json();
|
|
307
|
+
} else {
|
|
308
|
+
body = await response.text();
|
|
309
|
+
}
|
|
310
|
+
if (!response.ok) {
|
|
311
|
+
if (response.status === 401 && this.config.tokenProvider) {
|
|
312
|
+
this.tokenRefreshAttempts++;
|
|
313
|
+
if (this.tokenRefreshAttempts < this.maxTokenRefreshAttempts) {
|
|
314
|
+
return this.request(options);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
throw parseApiError(response.status, body, requestId);
|
|
318
|
+
}
|
|
319
|
+
this.tokenRefreshAttempts = 0;
|
|
320
|
+
const apiResponse = body;
|
|
321
|
+
if ("success" in apiResponse && apiResponse.success) {
|
|
322
|
+
return apiResponse.data;
|
|
323
|
+
}
|
|
324
|
+
return body;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
clearTimeout(timeoutId);
|
|
327
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
328
|
+
throw new TimeoutError(`Request timed out after ${this.config.timeout}ms`);
|
|
329
|
+
}
|
|
330
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
331
|
+
throw new NetworkError(`Network error: ${error.message}`);
|
|
332
|
+
}
|
|
333
|
+
throw error;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
if (options.skipRetry) {
|
|
337
|
+
return makeRequest();
|
|
338
|
+
}
|
|
339
|
+
return withRetry(makeRequest, this.config.retry);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* GET request
|
|
343
|
+
*/
|
|
344
|
+
async get(path, query) {
|
|
345
|
+
return this.request({
|
|
346
|
+
method: "GET",
|
|
347
|
+
path,
|
|
348
|
+
query
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* POST request
|
|
353
|
+
*/
|
|
354
|
+
async post(path, body, options) {
|
|
355
|
+
return this.request({
|
|
356
|
+
method: "POST",
|
|
357
|
+
path,
|
|
358
|
+
body,
|
|
359
|
+
idempotencyKey: options?.idempotencyKey,
|
|
360
|
+
skipRetry: options?.skipRetry
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* PATCH request
|
|
365
|
+
*/
|
|
366
|
+
async patch(path, body, options) {
|
|
367
|
+
return this.request({
|
|
368
|
+
method: "PATCH",
|
|
369
|
+
path,
|
|
370
|
+
body,
|
|
371
|
+
idempotencyKey: options?.idempotencyKey
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* DELETE request
|
|
376
|
+
*/
|
|
377
|
+
async delete(path) {
|
|
378
|
+
return this.request({
|
|
379
|
+
method: "DELETE",
|
|
380
|
+
path,
|
|
381
|
+
skipRetry: true
|
|
382
|
+
// Don't retry deletes
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Update configuration
|
|
387
|
+
*/
|
|
388
|
+
updateConfig(config) {
|
|
389
|
+
this.config = { ...this.config, ...config };
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
function generateIdempotencyKey() {
|
|
393
|
+
const timestamp = Date.now().toString(36);
|
|
394
|
+
const random = Math.random().toString(36).substring(2, 15);
|
|
395
|
+
return `idem_${timestamp}_${random}`;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/streaming/sse-client.ts
|
|
399
|
+
var SSEClient = class {
|
|
400
|
+
config;
|
|
401
|
+
abortController = null;
|
|
402
|
+
connected = false;
|
|
403
|
+
constructor(config) {
|
|
404
|
+
this.config = config;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Connect to SSE endpoint
|
|
408
|
+
*/
|
|
409
|
+
async connect() {
|
|
410
|
+
this.abortController = new AbortController();
|
|
411
|
+
console.log("[SDK:SSE] Connecting to:", this.config.url);
|
|
412
|
+
console.log("[SDK:SSE] Auth header:", this.config.authHeader ? `${this.config.authHeader.substring(0, 30)}...` : "MISSING");
|
|
413
|
+
console.log("[SDK:SSE] Org ID:", this.config.orgId);
|
|
414
|
+
try {
|
|
415
|
+
const response = await fetch(this.config.url, {
|
|
416
|
+
method: "GET",
|
|
417
|
+
headers: {
|
|
418
|
+
"Authorization": this.config.authHeader,
|
|
419
|
+
"X-Org-Id": String(this.config.orgId),
|
|
420
|
+
"Accept": "text/event-stream",
|
|
421
|
+
"Cache-Control": "no-cache"
|
|
422
|
+
},
|
|
423
|
+
signal: this.abortController.signal
|
|
424
|
+
});
|
|
425
|
+
console.log("[SDK:SSE] Response status:", response.status, response.statusText);
|
|
426
|
+
console.log("[SDK:SSE] Response headers:", {
|
|
427
|
+
contentType: response.headers.get("content-type"),
|
|
428
|
+
cacheControl: response.headers.get("cache-control")
|
|
429
|
+
});
|
|
430
|
+
if (!response.ok) {
|
|
431
|
+
let errorBody = "";
|
|
432
|
+
try {
|
|
433
|
+
errorBody = await response.text();
|
|
434
|
+
console.error("[SDK:SSE] Error response body:", errorBody);
|
|
435
|
+
} catch (e) {
|
|
436
|
+
console.error("[SDK:SSE] Could not read error body");
|
|
437
|
+
}
|
|
438
|
+
throw new Error(`SSE connection failed: ${response.status} ${response.statusText} - ${errorBody}`);
|
|
439
|
+
}
|
|
440
|
+
if (!response.body) {
|
|
441
|
+
throw new Error("Response body not available");
|
|
442
|
+
}
|
|
443
|
+
console.log("[SDK:SSE] Connection successful, starting stream parsing");
|
|
444
|
+
this.connected = true;
|
|
445
|
+
this.config.onConnectionChange(true);
|
|
446
|
+
await this.parseStream(response.body);
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
449
|
+
console.log("[SDK:SSE] Connection aborted (intentional)");
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
console.error("[SDK:SSE] Connection error:", error);
|
|
453
|
+
this.config.onError(error instanceof Error ? error : new Error(String(error)));
|
|
454
|
+
} finally {
|
|
455
|
+
this.connected = false;
|
|
456
|
+
this.config.onConnectionChange(false);
|
|
457
|
+
console.log("[SDK:SSE] Connection closed");
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Disconnect from SSE endpoint
|
|
462
|
+
*/
|
|
463
|
+
disconnect() {
|
|
464
|
+
if (this.abortController) {
|
|
465
|
+
this.abortController.abort();
|
|
466
|
+
this.abortController = null;
|
|
467
|
+
}
|
|
468
|
+
this.connected = false;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Check if connected
|
|
472
|
+
*/
|
|
473
|
+
isConnected() {
|
|
474
|
+
return this.connected;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Parse SSE stream
|
|
478
|
+
*/
|
|
479
|
+
async parseStream(body) {
|
|
480
|
+
const reader = body.getReader();
|
|
481
|
+
const decoder = new TextDecoder();
|
|
482
|
+
let buffer = "";
|
|
483
|
+
let currentEvent = {};
|
|
484
|
+
let chunkCount = 0;
|
|
485
|
+
let eventCount = 0;
|
|
486
|
+
console.log("[SDK:SSE] Starting to parse stream");
|
|
487
|
+
try {
|
|
488
|
+
while (true) {
|
|
489
|
+
const { done, value } = await reader.read();
|
|
490
|
+
if (done) {
|
|
491
|
+
console.log("[SDK:SSE] Stream reading complete. Total chunks:", chunkCount, "events:", eventCount);
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
chunkCount++;
|
|
495
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
496
|
+
buffer += chunk;
|
|
497
|
+
if (chunkCount <= 5) {
|
|
498
|
+
console.log(`[SDK:SSE] Chunk ${chunkCount} (${value.length} bytes):`, chunk.substring(0, 200));
|
|
499
|
+
}
|
|
500
|
+
const lines = buffer.split("\n");
|
|
501
|
+
buffer = lines.pop() ?? "";
|
|
502
|
+
for (const line of lines) {
|
|
503
|
+
if (line === "") {
|
|
504
|
+
if (currentEvent.type && currentEvent.data !== void 0) {
|
|
505
|
+
eventCount++;
|
|
506
|
+
console.log(`[SDK:SSE] Event ${eventCount}:`, currentEvent.type, "- data length:", currentEvent.data.length);
|
|
507
|
+
this.dispatchEvent(currentEvent);
|
|
508
|
+
}
|
|
509
|
+
currentEvent = {};
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (line.startsWith(":")) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
const colonIndex = line.indexOf(":");
|
|
516
|
+
if (colonIndex === -1) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
const field = line.substring(0, colonIndex);
|
|
520
|
+
let value2 = line.substring(colonIndex + 1);
|
|
521
|
+
if (value2.startsWith(" ")) {
|
|
522
|
+
value2 = value2.substring(1);
|
|
523
|
+
}
|
|
524
|
+
switch (field) {
|
|
525
|
+
case "event":
|
|
526
|
+
currentEvent.type = value2;
|
|
527
|
+
break;
|
|
528
|
+
case "id":
|
|
529
|
+
currentEvent.id = value2;
|
|
530
|
+
break;
|
|
531
|
+
case "data":
|
|
532
|
+
currentEvent.data = currentEvent.data ? currentEvent.data + "\n" + value2 : value2;
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
} finally {
|
|
538
|
+
console.log("[SDK:SSE] parseStream finished. Releasing reader lock.");
|
|
539
|
+
reader.releaseLock();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Dispatch parsed event
|
|
544
|
+
*/
|
|
545
|
+
dispatchEvent(event) {
|
|
546
|
+
console.log("[SDK:SSE] dispatchEvent called:", event.type, "id:", event.id);
|
|
547
|
+
try {
|
|
548
|
+
let data;
|
|
549
|
+
try {
|
|
550
|
+
data = JSON.parse(event.data);
|
|
551
|
+
console.log("[SDK:SSE] Parsed event data:", typeof data, data && typeof data === "object" ? Object.keys(data) : "");
|
|
552
|
+
} catch {
|
|
553
|
+
data = event.data;
|
|
554
|
+
}
|
|
555
|
+
const sseEvent = {
|
|
556
|
+
type: event.type,
|
|
557
|
+
id: event.id,
|
|
558
|
+
data
|
|
559
|
+
};
|
|
560
|
+
this.config.onEvent(sseEvent);
|
|
561
|
+
} catch (error) {
|
|
562
|
+
this.config.onError(
|
|
563
|
+
error instanceof Error ? error : new Error(`Failed to parse event: ${String(error)}`)
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// src/streaming/stream-controller.ts
|
|
570
|
+
var DEFAULT_STREAMING_CONFIG = {
|
|
571
|
+
maxReconnectAttempts: 5,
|
|
572
|
+
reconnectBaseDelayMs: 1e3,
|
|
573
|
+
reconnectMaxDelayMs: 1e4,
|
|
574
|
+
activityTimeoutMs: 3e4,
|
|
575
|
+
pollingIntervalMs: 2e3
|
|
576
|
+
};
|
|
577
|
+
var ExecutionStreamControllerImpl = class {
|
|
578
|
+
options;
|
|
579
|
+
config;
|
|
580
|
+
sseClient = null;
|
|
581
|
+
status = "connecting";
|
|
582
|
+
reconnectAttempts = 0;
|
|
583
|
+
seenEventIds = /* @__PURE__ */ new Set();
|
|
584
|
+
_lastEventId = null;
|
|
585
|
+
activityTimeoutId = null;
|
|
586
|
+
pollingIntervalId = null;
|
|
587
|
+
finalExecution = null;
|
|
588
|
+
finalError = null;
|
|
589
|
+
// Event callbacks
|
|
590
|
+
listeners = {
|
|
591
|
+
log: [],
|
|
592
|
+
toolCall: [],
|
|
593
|
+
toolResult: [],
|
|
594
|
+
plan: [],
|
|
595
|
+
paused: [],
|
|
596
|
+
started: [],
|
|
597
|
+
progress: [],
|
|
598
|
+
taskListUpdate: [],
|
|
599
|
+
delegationStarted: [],
|
|
600
|
+
delegationCompleted: [],
|
|
601
|
+
textDelta: [],
|
|
602
|
+
complete: [],
|
|
603
|
+
error: [],
|
|
604
|
+
connectionChange: []
|
|
605
|
+
};
|
|
606
|
+
// Promise for wait()
|
|
607
|
+
waitPromise = null;
|
|
608
|
+
waitResolve = null;
|
|
609
|
+
waitReject = null;
|
|
610
|
+
constructor(options) {
|
|
611
|
+
this.options = options;
|
|
612
|
+
this.config = { ...DEFAULT_STREAMING_CONFIG, ...options.config };
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Get the last event ID (for resume support)
|
|
616
|
+
*/
|
|
617
|
+
get lastEventId() {
|
|
618
|
+
return this._lastEventId;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Get the execution ID
|
|
622
|
+
*/
|
|
623
|
+
get executionId() {
|
|
624
|
+
return this.options.executionId;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Get the session ID (if available)
|
|
628
|
+
*/
|
|
629
|
+
get sessionId() {
|
|
630
|
+
return this.options.sessionId;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Connect to the execution stream
|
|
634
|
+
*/
|
|
635
|
+
async connect() {
|
|
636
|
+
this.status = "connecting";
|
|
637
|
+
this.reconnectAttempts = 0;
|
|
638
|
+
try {
|
|
639
|
+
await this.connectSSE();
|
|
640
|
+
} catch (error) {
|
|
641
|
+
await this.handleConnectionError(error);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Register event listener
|
|
646
|
+
*/
|
|
647
|
+
on(event, callback) {
|
|
648
|
+
const listeners = this.listeners[event];
|
|
649
|
+
if (listeners) {
|
|
650
|
+
listeners.push(callback);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Cancel the stream
|
|
655
|
+
*/
|
|
656
|
+
cancel() {
|
|
657
|
+
this.status = "closed";
|
|
658
|
+
this.stopActivityTimeout();
|
|
659
|
+
this.stopPolling();
|
|
660
|
+
if (this.sseClient) {
|
|
661
|
+
this.sseClient.disconnect();
|
|
662
|
+
this.sseClient = null;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Wait for completion
|
|
667
|
+
*/
|
|
668
|
+
wait() {
|
|
669
|
+
if (this.finalExecution) {
|
|
670
|
+
return Promise.resolve(this.finalExecution);
|
|
671
|
+
}
|
|
672
|
+
if (this.finalError) {
|
|
673
|
+
return Promise.reject(new AISaaSError(
|
|
674
|
+
this.finalError.error,
|
|
675
|
+
this.finalError.errorCode ?? "execution_error",
|
|
676
|
+
500
|
|
677
|
+
));
|
|
678
|
+
}
|
|
679
|
+
if (!this.waitPromise) {
|
|
680
|
+
this.waitPromise = new Promise((resolve, reject) => {
|
|
681
|
+
this.waitResolve = resolve;
|
|
682
|
+
this.waitReject = reject;
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
return this.waitPromise;
|
|
686
|
+
}
|
|
687
|
+
// ===== Private methods =====
|
|
688
|
+
/**
|
|
689
|
+
* Connect to SSE endpoint
|
|
690
|
+
*/
|
|
691
|
+
async connectSSE() {
|
|
692
|
+
const url = `${this.options.baseUrl}/agents/runs/${this.options.executionId}/stream`;
|
|
693
|
+
let authToken = this.options.apiKey;
|
|
694
|
+
if (!authToken && this.options.tokenProvider) {
|
|
695
|
+
authToken = await this.options.tokenProvider();
|
|
696
|
+
}
|
|
697
|
+
console.log("[SDK:StreamController] Connecting to SSE");
|
|
698
|
+
console.log("[SDK:StreamController] URL:", url);
|
|
699
|
+
console.log("[SDK:StreamController] baseUrl:", this.options.baseUrl);
|
|
700
|
+
console.log("[SDK:StreamController] executionId:", this.options.executionId);
|
|
701
|
+
console.log("[SDK:StreamController] orgId:", this.options.orgId);
|
|
702
|
+
console.log("[SDK:StreamController] apiKey:", authToken ? `${authToken.substring(0, 20)}...` : "MISSING");
|
|
703
|
+
this.sseClient = new SSEClient({
|
|
704
|
+
url,
|
|
705
|
+
authHeader: `Bearer ${authToken}`,
|
|
706
|
+
orgId: this.options.orgId,
|
|
707
|
+
onEvent: (event) => this.handleEvent(event),
|
|
708
|
+
onError: (error) => {
|
|
709
|
+
console.error("[SDK:StreamController] SSE error:", error);
|
|
710
|
+
this.handleConnectionError(error);
|
|
711
|
+
},
|
|
712
|
+
onConnectionChange: (connected) => {
|
|
713
|
+
console.log("[SDK:StreamController] SSE connection change:", connected);
|
|
714
|
+
if (connected) {
|
|
715
|
+
this.status = "connected";
|
|
716
|
+
this.reconnectAttempts = 0;
|
|
717
|
+
this.resetActivityTimeout();
|
|
718
|
+
}
|
|
719
|
+
this.emit("connectionChange", { connected, usingFallback: false });
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
try {
|
|
723
|
+
await this.sseClient.connect();
|
|
724
|
+
console.log("[SDK:StreamController] SSE connect() completed");
|
|
725
|
+
} catch (error) {
|
|
726
|
+
console.error("[SDK:StreamController] SSE connect() threw:", error);
|
|
727
|
+
throw error;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Handle incoming SSE event
|
|
732
|
+
*/
|
|
733
|
+
handleEvent(event) {
|
|
734
|
+
console.log("[SDK:StreamController] handleEvent called:", event.type, "id:", event.id);
|
|
735
|
+
this.resetActivityTimeout();
|
|
736
|
+
if (event.id) {
|
|
737
|
+
if (this.seenEventIds.has(event.id)) {
|
|
738
|
+
console.log("[SDK:StreamController] Skipping duplicate event:", event.id);
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
this.seenEventIds.add(event.id);
|
|
742
|
+
this._lastEventId = event.id;
|
|
743
|
+
if (this.seenEventIds.size > 1e3) {
|
|
744
|
+
const ids = Array.from(this.seenEventIds);
|
|
745
|
+
this.seenEventIds = new Set(ids.slice(-500));
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
const data = event.data;
|
|
749
|
+
console.log("[SDK:StreamController] Event data keys:", Object.keys(data));
|
|
750
|
+
switch (event.type) {
|
|
751
|
+
case "log": {
|
|
752
|
+
const rawLog = data.log;
|
|
753
|
+
if (!rawLog) {
|
|
754
|
+
console.warn("[StreamController] Received log event without log data");
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
757
|
+
const logTypeValue = rawLog.logType || rawLog.type || "info";
|
|
758
|
+
const transformedLog = {
|
|
759
|
+
uuid: rawLog.uuid || `log-${rawLog.stepIndex || Date.now()}`,
|
|
760
|
+
type: logTypeValue,
|
|
761
|
+
title: rawLog.message || rawLog.title || "",
|
|
762
|
+
content: rawLog.content,
|
|
763
|
+
metadata: {
|
|
764
|
+
...rawLog.metadata || {},
|
|
765
|
+
// Include stepIndex in metadata for deduplication
|
|
766
|
+
stepIndex: rawLog.stepIndex ?? 0
|
|
767
|
+
},
|
|
768
|
+
timestamp: rawLog.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
769
|
+
durationMs: rawLog.durationMs
|
|
770
|
+
};
|
|
771
|
+
this.emit("log", transformedLog);
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
case "tool_call":
|
|
775
|
+
this.emit("toolCall", {
|
|
776
|
+
toolCallId: data.toolCallId || `tool-call-${Date.now()}`,
|
|
777
|
+
toolName: data.toolName || "Unknown Tool",
|
|
778
|
+
inputs: data.args || {},
|
|
779
|
+
stepIndex: data.stepIndex ?? 0
|
|
780
|
+
});
|
|
781
|
+
break;
|
|
782
|
+
case "tool_result":
|
|
783
|
+
this.emit("toolResult", {
|
|
784
|
+
toolCallId: data.toolCallId || `tool-result-${Date.now()}`,
|
|
785
|
+
toolName: data.toolName || "Unknown Tool",
|
|
786
|
+
result: data.result,
|
|
787
|
+
durationMs: data.durationMs ?? 0
|
|
788
|
+
});
|
|
789
|
+
break;
|
|
790
|
+
case "execution_paused":
|
|
791
|
+
this.emit("paused", {
|
|
792
|
+
question: data.question || data.output || "",
|
|
793
|
+
options: data.options,
|
|
794
|
+
reason: "waiting_user_input"
|
|
795
|
+
});
|
|
796
|
+
break;
|
|
797
|
+
case "complete":
|
|
798
|
+
this.handleComplete(data);
|
|
799
|
+
break;
|
|
800
|
+
case "error":
|
|
801
|
+
this.handleError({
|
|
802
|
+
error: data.error,
|
|
803
|
+
errorCode: data.errorCode
|
|
804
|
+
});
|
|
805
|
+
break;
|
|
806
|
+
case "failed":
|
|
807
|
+
this.handleError({
|
|
808
|
+
error: data.error || data.message || "Execution failed",
|
|
809
|
+
errorCode: data.errorCode || "execution_failed"
|
|
810
|
+
});
|
|
811
|
+
break;
|
|
812
|
+
case "timeout":
|
|
813
|
+
if (data.retryable) {
|
|
814
|
+
this.handleConnectionError(new Error("Stream timeout"));
|
|
815
|
+
} else {
|
|
816
|
+
this.handleError({
|
|
817
|
+
error: data.error,
|
|
818
|
+
errorCode: "timeout"
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
break;
|
|
822
|
+
// Handle started event - immediate feedback that execution began
|
|
823
|
+
case "started":
|
|
824
|
+
this.emit("started", {
|
|
825
|
+
executionId: data.executionId,
|
|
826
|
+
agentUuid: data.agentUuid,
|
|
827
|
+
status: "initializing",
|
|
828
|
+
currentStep: data.currentStep
|
|
829
|
+
});
|
|
830
|
+
break;
|
|
831
|
+
// Handle progress event - status updates during execution
|
|
832
|
+
case "progress":
|
|
833
|
+
this.emit("progress", {
|
|
834
|
+
status: data.status,
|
|
835
|
+
iteration: data.iteration ?? 0,
|
|
836
|
+
currentStep: data.currentStep,
|
|
837
|
+
tokenUsage: data.tokenUsage,
|
|
838
|
+
cost: data.cost,
|
|
839
|
+
toolCalls: data.toolCalls
|
|
840
|
+
});
|
|
841
|
+
break;
|
|
842
|
+
// Handle task list update event from TaskTracker
|
|
843
|
+
case "task_list_update":
|
|
844
|
+
this.emit("taskListUpdate", {
|
|
845
|
+
executionId: data.executionId,
|
|
846
|
+
tasks: data.tasks,
|
|
847
|
+
timestamp: data.timestamp
|
|
848
|
+
});
|
|
849
|
+
break;
|
|
850
|
+
// Handle delegation started event (handoff_to_agent initiated)
|
|
851
|
+
case "delegation_started":
|
|
852
|
+
this.emit("delegationStarted", {
|
|
853
|
+
type: "delegation_started",
|
|
854
|
+
handoffId: data.handoffId,
|
|
855
|
+
targetAgentUuid: data.targetAgentUuid,
|
|
856
|
+
targetAgentName: data.targetAgentName,
|
|
857
|
+
taskPreview: data.taskPreview,
|
|
858
|
+
timestamp: data.timestamp || (/* @__PURE__ */ new Date()).toISOString()
|
|
859
|
+
});
|
|
860
|
+
break;
|
|
861
|
+
// Handle delegation completed event (handoff_to_agent finished)
|
|
862
|
+
case "delegation_completed":
|
|
863
|
+
this.emit("delegationCompleted", {
|
|
864
|
+
type: "delegation_completed",
|
|
865
|
+
handoffId: data.handoffId,
|
|
866
|
+
status: data.status,
|
|
867
|
+
resultPreview: data.resultPreview,
|
|
868
|
+
error: data.error,
|
|
869
|
+
durationMs: data.durationMs,
|
|
870
|
+
timestamp: data.timestamp || (/* @__PURE__ */ new Date()).toISOString()
|
|
871
|
+
});
|
|
872
|
+
break;
|
|
873
|
+
// Handle status_update event - immediate feedback (e.g., "thinking")
|
|
874
|
+
// Map to progress event for consistency
|
|
875
|
+
case "status_update":
|
|
876
|
+
this.emit("progress", {
|
|
877
|
+
status: data.status || "thinking",
|
|
878
|
+
iteration: 0,
|
|
879
|
+
currentStep: data.message
|
|
880
|
+
});
|
|
881
|
+
break;
|
|
882
|
+
// Handle text_delta event - token-by-token streaming
|
|
883
|
+
case "text_delta":
|
|
884
|
+
this.emit("textDelta", {
|
|
885
|
+
delta: data.delta,
|
|
886
|
+
executionId: data.executionId,
|
|
887
|
+
timestamp: data.timestamp || (/* @__PURE__ */ new Date()).toISOString()
|
|
888
|
+
});
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Handle completion
|
|
894
|
+
*/
|
|
895
|
+
handleComplete(data) {
|
|
896
|
+
this.status = "closed";
|
|
897
|
+
this.stopActivityTimeout();
|
|
898
|
+
const sessionId = data.sessionId ?? this.options.sessionId;
|
|
899
|
+
const execution = {
|
|
900
|
+
uuid: this.options.executionId,
|
|
901
|
+
agentUuid: "",
|
|
902
|
+
sessionId,
|
|
903
|
+
status: "completed",
|
|
904
|
+
input: { task: "" },
|
|
905
|
+
output: data.output,
|
|
906
|
+
logs: [],
|
|
907
|
+
tokenUsage: data.tokenUsage,
|
|
908
|
+
cost: data.cost,
|
|
909
|
+
queuedAt: "",
|
|
910
|
+
...data
|
|
911
|
+
};
|
|
912
|
+
this.finalExecution = execution;
|
|
913
|
+
this.emit("complete", { execution });
|
|
914
|
+
if (this.waitResolve) {
|
|
915
|
+
this.waitResolve(execution);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Handle error
|
|
920
|
+
*/
|
|
921
|
+
handleError(error) {
|
|
922
|
+
this.status = "closed";
|
|
923
|
+
this.stopActivityTimeout();
|
|
924
|
+
this.finalError = error;
|
|
925
|
+
this.emit("error", error);
|
|
926
|
+
if (this.waitReject) {
|
|
927
|
+
this.waitReject(new AISaaSError(
|
|
928
|
+
error.error,
|
|
929
|
+
error.errorCode ?? "execution_error",
|
|
930
|
+
500
|
|
931
|
+
));
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Handle connection error
|
|
936
|
+
*/
|
|
937
|
+
async handleConnectionError(error) {
|
|
938
|
+
if (this.status === "closed") {
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
if (this.reconnectAttempts < this.config.maxReconnectAttempts) {
|
|
942
|
+
this.status = "reconnecting";
|
|
943
|
+
this.reconnectAttempts++;
|
|
944
|
+
const delay = Math.min(
|
|
945
|
+
this.config.reconnectBaseDelayMs * Math.pow(2, this.reconnectAttempts),
|
|
946
|
+
this.config.reconnectMaxDelayMs
|
|
947
|
+
);
|
|
948
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
949
|
+
if (this.status === "closed") {
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
try {
|
|
953
|
+
await this.connectSSE();
|
|
954
|
+
} catch {
|
|
955
|
+
await this.handleConnectionError(error);
|
|
956
|
+
}
|
|
957
|
+
} else {
|
|
958
|
+
this.status = "fallback_polling";
|
|
959
|
+
this.emit("connectionChange", { connected: false, usingFallback: true });
|
|
960
|
+
this.startPolling();
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Start polling fallback
|
|
965
|
+
*/
|
|
966
|
+
startPolling() {
|
|
967
|
+
this.pollingIntervalId = setInterval(async () => {
|
|
968
|
+
try {
|
|
969
|
+
const execution = await this.options.getExecution(this.options.executionId);
|
|
970
|
+
for (const log of execution.logs) {
|
|
971
|
+
if (!this.seenEventIds.has(log.uuid)) {
|
|
972
|
+
this.seenEventIds.add(log.uuid);
|
|
973
|
+
this.emit("log", log);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
if (execution.status === "completed") {
|
|
977
|
+
this.stopPolling();
|
|
978
|
+
this.handleComplete(execution);
|
|
979
|
+
} else if (execution.status === "failed" || execution.status === "cancelled") {
|
|
980
|
+
this.stopPolling();
|
|
981
|
+
this.handleError({
|
|
982
|
+
error: execution.error ?? `Execution ${execution.status}`,
|
|
983
|
+
errorCode: execution.status
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
} catch (error) {
|
|
987
|
+
console.error("Polling error:", error);
|
|
988
|
+
}
|
|
989
|
+
}, this.config.pollingIntervalMs);
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Stop polling
|
|
993
|
+
*/
|
|
994
|
+
stopPolling() {
|
|
995
|
+
if (this.pollingIntervalId) {
|
|
996
|
+
clearInterval(this.pollingIntervalId);
|
|
997
|
+
this.pollingIntervalId = null;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Reset activity timeout
|
|
1002
|
+
*/
|
|
1003
|
+
resetActivityTimeout() {
|
|
1004
|
+
this.stopActivityTimeout();
|
|
1005
|
+
this.activityTimeoutId = setTimeout(() => {
|
|
1006
|
+
if (this.sseClient) {
|
|
1007
|
+
this.sseClient.disconnect();
|
|
1008
|
+
}
|
|
1009
|
+
this.handleConnectionError(new Error("Activity timeout"));
|
|
1010
|
+
}, this.config.activityTimeoutMs);
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Stop activity timeout
|
|
1014
|
+
*/
|
|
1015
|
+
stopActivityTimeout() {
|
|
1016
|
+
if (this.activityTimeoutId) {
|
|
1017
|
+
clearTimeout(this.activityTimeoutId);
|
|
1018
|
+
this.activityTimeoutId = null;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Emit event to listeners
|
|
1023
|
+
*/
|
|
1024
|
+
emit(event, data) {
|
|
1025
|
+
const listeners = this.listeners[event];
|
|
1026
|
+
console.log(`[SDK:StreamController] Emitting '${event}' to ${listeners?.length || 0} listeners`);
|
|
1027
|
+
if (listeners) {
|
|
1028
|
+
for (const listener of listeners) {
|
|
1029
|
+
try {
|
|
1030
|
+
listener(data);
|
|
1031
|
+
} catch (error) {
|
|
1032
|
+
console.error(`Error in ${event} listener:`, error);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
};
|
|
1038
|
+
function createExecutionStreamController(options) {
|
|
1039
|
+
return new ExecutionStreamControllerImpl(options);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// src/resources/agents.ts
|
|
1043
|
+
var AgentsAPI = class extends BaseResourceAPI {
|
|
1044
|
+
constructor(http, getStreamConfig) {
|
|
1045
|
+
super(http, "/agents");
|
|
1046
|
+
this.getStreamConfig = getStreamConfig;
|
|
1047
|
+
}
|
|
1048
|
+
// ===== LIST Operations =====
|
|
1049
|
+
/**
|
|
1050
|
+
* List all agents in the organization
|
|
1051
|
+
*
|
|
1052
|
+
* @param options - List options (pagination, filtering)
|
|
1053
|
+
* @returns Paginated list of agents
|
|
1054
|
+
*
|
|
1055
|
+
* @example
|
|
1056
|
+
* ```typescript
|
|
1057
|
+
* const { data: agents, pagination } = await client.agents.list({ limit: 10 });
|
|
1058
|
+
* ```
|
|
1059
|
+
*/
|
|
1060
|
+
async list(options) {
|
|
1061
|
+
const query = {
|
|
1062
|
+
...this.buildListQuery(options),
|
|
1063
|
+
status: options?.status,
|
|
1064
|
+
type: options?.type
|
|
1065
|
+
};
|
|
1066
|
+
return this.httpGet("", query);
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Get a single agent by UUID
|
|
1070
|
+
*
|
|
1071
|
+
* @param uuid - Agent UUID
|
|
1072
|
+
* @returns Agent with configuration
|
|
1073
|
+
*
|
|
1074
|
+
* @example
|
|
1075
|
+
* ```typescript
|
|
1076
|
+
* const agent = await client.agents.get('agent-uuid');
|
|
1077
|
+
* console.log(agent.config);
|
|
1078
|
+
* ```
|
|
1079
|
+
*/
|
|
1080
|
+
async get(uuid) {
|
|
1081
|
+
return this.httpGet(`/${uuid}`);
|
|
1082
|
+
}
|
|
1083
|
+
// ===== EXECUTE Operations =====
|
|
1084
|
+
/**
|
|
1085
|
+
* Start an agent execution (async)
|
|
1086
|
+
*
|
|
1087
|
+
* Returns immediately with execution ID. Use getExecution() to poll
|
|
1088
|
+
* for results, or use stream() for real-time updates.
|
|
1089
|
+
*
|
|
1090
|
+
* @param uuid - Agent UUID
|
|
1091
|
+
* @param input - Execution input (task, context, options)
|
|
1092
|
+
* @returns Execution ID and initial status
|
|
1093
|
+
*
|
|
1094
|
+
* @example
|
|
1095
|
+
* ```typescript
|
|
1096
|
+
* const { executionId, status } = await client.agents.execute('agent-uuid', {
|
|
1097
|
+
* task: 'Summarize the latest news about AI',
|
|
1098
|
+
* });
|
|
1099
|
+
* ```
|
|
1100
|
+
*/
|
|
1101
|
+
async execute(uuid, input) {
|
|
1102
|
+
const idempotencyKey = input.options?.idempotencyKey ?? generateIdempotencyKey();
|
|
1103
|
+
return this.httpPost(
|
|
1104
|
+
`/${uuid}/execute`,
|
|
1105
|
+
{
|
|
1106
|
+
task: input.task,
|
|
1107
|
+
context: input.context ?? {},
|
|
1108
|
+
attachmentIds: input.attachmentIds,
|
|
1109
|
+
options: input.options,
|
|
1110
|
+
...input.teamUuid ? { teamUuid: input.teamUuid } : {}
|
|
1111
|
+
},
|
|
1112
|
+
{ idempotencyKey }
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Execute an agent and wait for completion (sync)
|
|
1117
|
+
*
|
|
1118
|
+
* Polls the execution status until completion or failure.
|
|
1119
|
+
*
|
|
1120
|
+
* @param uuid - Agent UUID
|
|
1121
|
+
* @param input - Execution input
|
|
1122
|
+
* @param pollInterval - Polling interval in ms (default: 1000)
|
|
1123
|
+
* @returns Final execution result
|
|
1124
|
+
*
|
|
1125
|
+
* @example
|
|
1126
|
+
* ```typescript
|
|
1127
|
+
* const execution = await client.agents.executeSync('agent-uuid', {
|
|
1128
|
+
* task: 'Write a haiku about coding',
|
|
1129
|
+
* });
|
|
1130
|
+
* console.log(execution.output);
|
|
1131
|
+
* ```
|
|
1132
|
+
*/
|
|
1133
|
+
async executeSync(uuid, input, pollInterval = 1e3) {
|
|
1134
|
+
const { executionId } = await this.execute(uuid, input);
|
|
1135
|
+
return this.pollExecution(executionId, pollInterval);
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Stream an agent execution
|
|
1139
|
+
*
|
|
1140
|
+
* Returns a StreamController for real-time event handling.
|
|
1141
|
+
*
|
|
1142
|
+
* @param uuid - Agent UUID
|
|
1143
|
+
* @param input - Execution input
|
|
1144
|
+
* @returns Stream controller for event handling
|
|
1145
|
+
*
|
|
1146
|
+
* @example
|
|
1147
|
+
* ```typescript
|
|
1148
|
+
* const stream = await client.agents.stream('agent-uuid', {
|
|
1149
|
+
* task: 'Research climate change',
|
|
1150
|
+
* });
|
|
1151
|
+
*
|
|
1152
|
+
* stream.on('log', (log) => console.log(log.title));
|
|
1153
|
+
* stream.on('toolCall', ({ toolName }) => console.log(`Calling ${toolName}`));
|
|
1154
|
+
* stream.on('complete', ({ execution }) => console.log(execution.output));
|
|
1155
|
+
*
|
|
1156
|
+
* const result = await stream.wait();
|
|
1157
|
+
* ```
|
|
1158
|
+
*/
|
|
1159
|
+
async stream(uuid, input) {
|
|
1160
|
+
console.log("[SDK:Agents] stream() called for agent:", uuid);
|
|
1161
|
+
const { executionId, sessionId } = await this.execute(uuid, input);
|
|
1162
|
+
console.log("[SDK:Agents] Execution started:", { executionId, sessionId });
|
|
1163
|
+
const config = this.getStreamConfig();
|
|
1164
|
+
console.log("[SDK:Agents] Stream config:", {
|
|
1165
|
+
baseUrl: config.baseUrl,
|
|
1166
|
+
apiKey: config.apiKey ? `${config.apiKey.substring(0, 20)}...` : "MISSING",
|
|
1167
|
+
orgId: config.orgId
|
|
1168
|
+
});
|
|
1169
|
+
const controller = createExecutionStreamController({
|
|
1170
|
+
executionId,
|
|
1171
|
+
sessionId,
|
|
1172
|
+
baseUrl: config.baseUrl,
|
|
1173
|
+
apiKey: config.apiKey,
|
|
1174
|
+
tokenProvider: config.tokenProvider,
|
|
1175
|
+
orgId: config.orgId,
|
|
1176
|
+
getExecution: (id) => this.getExecution(id)
|
|
1177
|
+
});
|
|
1178
|
+
console.log("[SDK:Agents] Stream controller created - caller must call connect() after registering listeners");
|
|
1179
|
+
return controller;
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Connect to an existing execution's stream
|
|
1183
|
+
*
|
|
1184
|
+
* Use this when the execution was started externally (e.g., via tRPC for team executions)
|
|
1185
|
+
* and you need to connect to its SSE stream for real-time updates.
|
|
1186
|
+
*
|
|
1187
|
+
* @param executionId - Execution UUID (already running)
|
|
1188
|
+
* @param sessionId - Optional session ID for conversation continuity
|
|
1189
|
+
* @returns Stream controller for the execution
|
|
1190
|
+
*
|
|
1191
|
+
* @example
|
|
1192
|
+
* ```typescript
|
|
1193
|
+
* // Execution started via tRPC
|
|
1194
|
+
* const executionId = await trpc.executions.start.mutate({ teamUuid, task });
|
|
1195
|
+
*
|
|
1196
|
+
* // Connect to SSE stream for real-time updates
|
|
1197
|
+
* const stream = client.agents.streamExecution(executionId);
|
|
1198
|
+
*
|
|
1199
|
+
* stream.on('log', (log) => console.log(log.title));
|
|
1200
|
+
* stream.on('delegationStarted', (data) => console.log('Subagent:', data.targetAgentName));
|
|
1201
|
+
* stream.on('complete', ({ execution }) => console.log(execution.output));
|
|
1202
|
+
*
|
|
1203
|
+
* await stream.connect();
|
|
1204
|
+
* const result = await stream.wait();
|
|
1205
|
+
* ```
|
|
1206
|
+
*/
|
|
1207
|
+
streamExecution(executionId, sessionId) {
|
|
1208
|
+
console.log("[SDK:Agents] streamExecution() called for execution:", executionId);
|
|
1209
|
+
const config = this.getStreamConfig();
|
|
1210
|
+
console.log("[SDK:Agents] Stream config:", {
|
|
1211
|
+
baseUrl: config.baseUrl,
|
|
1212
|
+
apiKey: config.apiKey ? `${config.apiKey.substring(0, 20)}...` : "MISSING",
|
|
1213
|
+
orgId: config.orgId
|
|
1214
|
+
});
|
|
1215
|
+
const controller = createExecutionStreamController({
|
|
1216
|
+
executionId,
|
|
1217
|
+
sessionId,
|
|
1218
|
+
baseUrl: config.baseUrl,
|
|
1219
|
+
apiKey: config.apiKey,
|
|
1220
|
+
tokenProvider: config.tokenProvider,
|
|
1221
|
+
orgId: config.orgId,
|
|
1222
|
+
getExecution: (id) => this.getExecution(id)
|
|
1223
|
+
});
|
|
1224
|
+
console.log("[SDK:Agents] Stream controller created for existing execution - caller must call connect() after registering listeners");
|
|
1225
|
+
return controller;
|
|
1226
|
+
}
|
|
1227
|
+
// ===== RECEIVE Operations =====
|
|
1228
|
+
/**
|
|
1229
|
+
* Get execution result by ID
|
|
1230
|
+
*
|
|
1231
|
+
* @param executionId - Execution UUID
|
|
1232
|
+
* @returns Execution details including status, output, logs
|
|
1233
|
+
*
|
|
1234
|
+
* @example
|
|
1235
|
+
* ```typescript
|
|
1236
|
+
* const execution = await client.agents.getExecution('exec-uuid');
|
|
1237
|
+
* if (execution.status === 'completed') {
|
|
1238
|
+
* console.log(execution.output);
|
|
1239
|
+
* }
|
|
1240
|
+
* ```
|
|
1241
|
+
*/
|
|
1242
|
+
async getExecution(executionId) {
|
|
1243
|
+
return this.http.get(`/executions/${executionId}`);
|
|
1244
|
+
}
|
|
1245
|
+
// ===== EDIT Operations =====
|
|
1246
|
+
/**
|
|
1247
|
+
* Update agent details
|
|
1248
|
+
*
|
|
1249
|
+
* @param uuid - Agent UUID
|
|
1250
|
+
* @param data - Fields to update
|
|
1251
|
+
* @returns Updated agent
|
|
1252
|
+
*
|
|
1253
|
+
* @example
|
|
1254
|
+
* ```typescript
|
|
1255
|
+
* const agent = await client.agents.update('agent-uuid', {
|
|
1256
|
+
* name: 'My Updated Agent',
|
|
1257
|
+
* description: 'New description',
|
|
1258
|
+
* });
|
|
1259
|
+
* ```
|
|
1260
|
+
*/
|
|
1261
|
+
async update(uuid, data) {
|
|
1262
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1263
|
+
return this.httpPatch(`/${uuid}`, data, { idempotencyKey });
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Update agent configuration
|
|
1267
|
+
*
|
|
1268
|
+
* @param uuid - Agent UUID
|
|
1269
|
+
* @param config - Configuration fields to update
|
|
1270
|
+
* @returns Updated agent
|
|
1271
|
+
*
|
|
1272
|
+
* @example
|
|
1273
|
+
* ```typescript
|
|
1274
|
+
* const agent = await client.agents.updateConfig('agent-uuid', {
|
|
1275
|
+
* temperature: 0.7,
|
|
1276
|
+
* maxIterations: 5,
|
|
1277
|
+
* });
|
|
1278
|
+
* ```
|
|
1279
|
+
*/
|
|
1280
|
+
async updateConfig(uuid, config) {
|
|
1281
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1282
|
+
return this.httpPatch(`/${uuid}/config`, config, { idempotencyKey });
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Cancel a running execution
|
|
1286
|
+
*
|
|
1287
|
+
* @param executionId - Execution UUID
|
|
1288
|
+
* @returns Updated execution with cancelled status
|
|
1289
|
+
*
|
|
1290
|
+
* @example
|
|
1291
|
+
* ```typescript
|
|
1292
|
+
* const execution = await client.agents.cancelExecution('exec-uuid');
|
|
1293
|
+
* console.log(execution.status); // 'cancelled'
|
|
1294
|
+
* ```
|
|
1295
|
+
*/
|
|
1296
|
+
async cancelExecution(executionId) {
|
|
1297
|
+
return this.http.post(
|
|
1298
|
+
`/executions/${executionId}/cancel`,
|
|
1299
|
+
void 0,
|
|
1300
|
+
{ skipRetry: true }
|
|
1301
|
+
);
|
|
1302
|
+
}
|
|
1303
|
+
// ===== Private helpers =====
|
|
1304
|
+
/**
|
|
1305
|
+
* Poll execution until completion
|
|
1306
|
+
*/
|
|
1307
|
+
async pollExecution(executionId, pollInterval) {
|
|
1308
|
+
while (true) {
|
|
1309
|
+
const execution = await this.getExecution(executionId);
|
|
1310
|
+
if (execution.status === "completed" || execution.status === "failed" || execution.status === "cancelled") {
|
|
1311
|
+
return execution;
|
|
1312
|
+
}
|
|
1313
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
// src/resources/skills.ts
|
|
1319
|
+
var SkillsAPI = class extends BaseResourceAPI {
|
|
1320
|
+
constructor(http) {
|
|
1321
|
+
super(http, "/skills");
|
|
1322
|
+
}
|
|
1323
|
+
// ===== LIST Operations =====
|
|
1324
|
+
/**
|
|
1325
|
+
* List all skills in the organization
|
|
1326
|
+
*
|
|
1327
|
+
* @param options - List options (pagination, filtering by category/tags)
|
|
1328
|
+
* @returns Paginated list of skills
|
|
1329
|
+
*
|
|
1330
|
+
* @example
|
|
1331
|
+
* ```typescript
|
|
1332
|
+
* const { data: skills } = await client.skills.list({
|
|
1333
|
+
* category: 'writing',
|
|
1334
|
+
* tags: ['seo', 'blog'],
|
|
1335
|
+
* });
|
|
1336
|
+
* ```
|
|
1337
|
+
*/
|
|
1338
|
+
async list(options) {
|
|
1339
|
+
const query = {
|
|
1340
|
+
...this.buildListQuery(options),
|
|
1341
|
+
category: options?.category,
|
|
1342
|
+
tags: options?.tags?.join(",")
|
|
1343
|
+
};
|
|
1344
|
+
return this.httpGet("", query);
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* Get a single skill by UUID with full details
|
|
1348
|
+
*
|
|
1349
|
+
* @param uuid - Skill UUID
|
|
1350
|
+
* @returns Skill with prompt template and tools
|
|
1351
|
+
*
|
|
1352
|
+
* @example
|
|
1353
|
+
* ```typescript
|
|
1354
|
+
* const skill = await client.skills.get('skill-uuid');
|
|
1355
|
+
* console.log(skill.promptTemplate);
|
|
1356
|
+
* ```
|
|
1357
|
+
*/
|
|
1358
|
+
async get(uuid) {
|
|
1359
|
+
return this.httpGet(`/${uuid}`);
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Get tools associated with a skill
|
|
1363
|
+
*
|
|
1364
|
+
* @param uuid - Skill UUID
|
|
1365
|
+
* @returns Array of tools used by the skill
|
|
1366
|
+
*
|
|
1367
|
+
* @example
|
|
1368
|
+
* ```typescript
|
|
1369
|
+
* const tools = await client.skills.getTools('skill-uuid');
|
|
1370
|
+
* tools.forEach(tool => console.log(tool.name));
|
|
1371
|
+
* ```
|
|
1372
|
+
*/
|
|
1373
|
+
async getTools(uuid) {
|
|
1374
|
+
return this.httpGet(`/${uuid}/tools`);
|
|
1375
|
+
}
|
|
1376
|
+
// ===== EXECUTE Operations =====
|
|
1377
|
+
/**
|
|
1378
|
+
* Execute a skill with variables
|
|
1379
|
+
*
|
|
1380
|
+
* @param uuid - Skill UUID
|
|
1381
|
+
* @param input - Variables and context for execution
|
|
1382
|
+
* @returns Skill execution result
|
|
1383
|
+
*
|
|
1384
|
+
* @example
|
|
1385
|
+
* ```typescript
|
|
1386
|
+
* const result = await client.skills.execute('skill-uuid', {
|
|
1387
|
+
* variables: {
|
|
1388
|
+
* topic: 'AI in healthcare',
|
|
1389
|
+
* tone: 'professional',
|
|
1390
|
+
* },
|
|
1391
|
+
* });
|
|
1392
|
+
* console.log(result.output);
|
|
1393
|
+
* ```
|
|
1394
|
+
*/
|
|
1395
|
+
async execute(uuid, input) {
|
|
1396
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1397
|
+
return this.httpPost(
|
|
1398
|
+
`/${uuid}/execute`,
|
|
1399
|
+
{
|
|
1400
|
+
variables: input.variables,
|
|
1401
|
+
context: input.context
|
|
1402
|
+
},
|
|
1403
|
+
{ idempotencyKey }
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
// ===== RECEIVE Operations =====
|
|
1407
|
+
/**
|
|
1408
|
+
* Get skill execution result by ID
|
|
1409
|
+
*
|
|
1410
|
+
* @param executionId - Execution UUID
|
|
1411
|
+
* @returns Skill execution details
|
|
1412
|
+
*
|
|
1413
|
+
* @example
|
|
1414
|
+
* ```typescript
|
|
1415
|
+
* const execution = await client.skills.getExecution('exec-uuid');
|
|
1416
|
+
* ```
|
|
1417
|
+
*/
|
|
1418
|
+
async getExecution(executionId) {
|
|
1419
|
+
return this.http.get(`/executions/${executionId}`);
|
|
1420
|
+
}
|
|
1421
|
+
// ===== EDIT Operations =====
|
|
1422
|
+
/**
|
|
1423
|
+
* Update skill details
|
|
1424
|
+
*
|
|
1425
|
+
* @param uuid - Skill UUID
|
|
1426
|
+
* @param data - Fields to update
|
|
1427
|
+
* @returns Updated skill
|
|
1428
|
+
*
|
|
1429
|
+
* @example
|
|
1430
|
+
* ```typescript
|
|
1431
|
+
* const skill = await client.skills.update('skill-uuid', {
|
|
1432
|
+
* name: 'Updated Skill Name',
|
|
1433
|
+
* tags: ['new-tag'],
|
|
1434
|
+
* });
|
|
1435
|
+
* ```
|
|
1436
|
+
*/
|
|
1437
|
+
async update(uuid, data) {
|
|
1438
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1439
|
+
return this.httpPatch(`/${uuid}`, data, { idempotencyKey });
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Update skill prompt template
|
|
1443
|
+
*
|
|
1444
|
+
* @param uuid - Skill UUID
|
|
1445
|
+
* @param prompt - New prompt template
|
|
1446
|
+
* @returns Updated skill with new version
|
|
1447
|
+
*
|
|
1448
|
+
* @example
|
|
1449
|
+
* ```typescript
|
|
1450
|
+
* const skill = await client.skills.updatePrompt('skill-uuid',
|
|
1451
|
+
* 'Write a {{tone}} article about {{topic}}...'
|
|
1452
|
+
* );
|
|
1453
|
+
* console.log(skill.version); // Version incremented
|
|
1454
|
+
* ```
|
|
1455
|
+
*/
|
|
1456
|
+
async updatePrompt(uuid, prompt) {
|
|
1457
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1458
|
+
return this.httpPatch(`/${uuid}/prompt`, { promptTemplate: prompt }, { idempotencyKey });
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
|
|
1462
|
+
// src/resources/tools.ts
|
|
1463
|
+
var ToolsAPI = class extends BaseResourceAPI {
|
|
1464
|
+
constructor(http) {
|
|
1465
|
+
super(http, "/tools");
|
|
1466
|
+
}
|
|
1467
|
+
// ===== LIST Operations =====
|
|
1468
|
+
/**
|
|
1469
|
+
* List all tools in the organization
|
|
1470
|
+
*
|
|
1471
|
+
* @param options - List options (pagination, filtering)
|
|
1472
|
+
* @returns Paginated list of tools
|
|
1473
|
+
*
|
|
1474
|
+
* @example
|
|
1475
|
+
* ```typescript
|
|
1476
|
+
* const { data: tools } = await client.tools.list({
|
|
1477
|
+
* category: 'data',
|
|
1478
|
+
* isEnabled: true,
|
|
1479
|
+
* });
|
|
1480
|
+
* ```
|
|
1481
|
+
*/
|
|
1482
|
+
async list(options) {
|
|
1483
|
+
const query = {
|
|
1484
|
+
...this.buildListQuery(options),
|
|
1485
|
+
category: options?.category,
|
|
1486
|
+
isEnabled: options?.isEnabled
|
|
1487
|
+
};
|
|
1488
|
+
return this.httpGet("", query);
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Get a single tool by UUID with schema
|
|
1492
|
+
*
|
|
1493
|
+
* @param uuid - Tool UUID
|
|
1494
|
+
* @returns Tool with input/output schemas
|
|
1495
|
+
*
|
|
1496
|
+
* @example
|
|
1497
|
+
* ```typescript
|
|
1498
|
+
* const tool = await client.tools.get('tool-uuid');
|
|
1499
|
+
* console.log(tool.inputSchema);
|
|
1500
|
+
* ```
|
|
1501
|
+
*/
|
|
1502
|
+
async get(uuid) {
|
|
1503
|
+
return this.httpGet(`/${uuid}`);
|
|
1504
|
+
}
|
|
1505
|
+
// ===== EXECUTE Operations =====
|
|
1506
|
+
/**
|
|
1507
|
+
* Execute a tool directly
|
|
1508
|
+
*
|
|
1509
|
+
* @param uuid - Tool UUID
|
|
1510
|
+
* @param input - Tool input (validated against inputSchema)
|
|
1511
|
+
* @returns Tool execution result
|
|
1512
|
+
*
|
|
1513
|
+
* @example
|
|
1514
|
+
* ```typescript
|
|
1515
|
+
* const result = await client.tools.execute('tool-uuid', {
|
|
1516
|
+
* query: 'weather in London',
|
|
1517
|
+
* limit: 5,
|
|
1518
|
+
* });
|
|
1519
|
+
* console.log(result.output);
|
|
1520
|
+
* ```
|
|
1521
|
+
*/
|
|
1522
|
+
async execute(uuid, input) {
|
|
1523
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1524
|
+
return this.httpPost(`/${uuid}/execute`, { input }, { idempotencyKey });
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Validate input against tool schema
|
|
1528
|
+
*
|
|
1529
|
+
* @param uuid - Tool UUID
|
|
1530
|
+
* @param input - Input to validate
|
|
1531
|
+
* @returns Validation result with errors if invalid
|
|
1532
|
+
*
|
|
1533
|
+
* @example
|
|
1534
|
+
* ```typescript
|
|
1535
|
+
* const validation = await client.tools.validateInput('tool-uuid', {
|
|
1536
|
+
* query: 'test',
|
|
1537
|
+
* });
|
|
1538
|
+
* if (!validation.valid) {
|
|
1539
|
+
* console.log(validation.errors);
|
|
1540
|
+
* }
|
|
1541
|
+
* ```
|
|
1542
|
+
*/
|
|
1543
|
+
async validateInput(uuid, input) {
|
|
1544
|
+
return this.httpPost(`/${uuid}/validate`, { input }, { skipRetry: false });
|
|
1545
|
+
}
|
|
1546
|
+
// ===== RECEIVE Operations =====
|
|
1547
|
+
/**
|
|
1548
|
+
* Get tool execution result by ID
|
|
1549
|
+
*
|
|
1550
|
+
* @param executionId - Execution UUID
|
|
1551
|
+
* @returns Tool execution details
|
|
1552
|
+
*
|
|
1553
|
+
* @example
|
|
1554
|
+
* ```typescript
|
|
1555
|
+
* const execution = await client.tools.getExecution('exec-uuid');
|
|
1556
|
+
* ```
|
|
1557
|
+
*/
|
|
1558
|
+
async getExecution(executionId) {
|
|
1559
|
+
return this.http.get(`/executions/${executionId}`);
|
|
1560
|
+
}
|
|
1561
|
+
// ===== EDIT Operations =====
|
|
1562
|
+
/**
|
|
1563
|
+
* Update tool details
|
|
1564
|
+
*
|
|
1565
|
+
* @param uuid - Tool UUID
|
|
1566
|
+
* @param data - Fields to update
|
|
1567
|
+
* @returns Updated tool
|
|
1568
|
+
*
|
|
1569
|
+
* @example
|
|
1570
|
+
* ```typescript
|
|
1571
|
+
* const tool = await client.tools.update('tool-uuid', {
|
|
1572
|
+
* name: 'Updated Tool Name',
|
|
1573
|
+
* description: 'New description',
|
|
1574
|
+
* });
|
|
1575
|
+
* ```
|
|
1576
|
+
*/
|
|
1577
|
+
async update(uuid, data) {
|
|
1578
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1579
|
+
return this.httpPatch(`/${uuid}`, data, { idempotencyKey });
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Update tool configuration
|
|
1583
|
+
*
|
|
1584
|
+
* @param uuid - Tool UUID
|
|
1585
|
+
* @param config - Configuration to update
|
|
1586
|
+
* @returns Updated tool
|
|
1587
|
+
*
|
|
1588
|
+
* @example
|
|
1589
|
+
* ```typescript
|
|
1590
|
+
* const tool = await client.tools.updateConfig('tool-uuid', {
|
|
1591
|
+
* isEnabled: false,
|
|
1592
|
+
* config: { apiKey: 'new-key' },
|
|
1593
|
+
* });
|
|
1594
|
+
* ```
|
|
1595
|
+
*/
|
|
1596
|
+
async updateConfig(uuid, config) {
|
|
1597
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1598
|
+
return this.httpPatch(`/${uuid}/config`, config, { idempotencyKey });
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
|
|
1602
|
+
// src/resources/artifacts.ts
|
|
1603
|
+
var ArtifactsAPI = class extends BaseResourceAPI {
|
|
1604
|
+
constructor(http) {
|
|
1605
|
+
super(http, "/artifacts");
|
|
1606
|
+
}
|
|
1607
|
+
// ===== LIST Operations =====
|
|
1608
|
+
/**
|
|
1609
|
+
* List artifacts for an execution
|
|
1610
|
+
*
|
|
1611
|
+
* @param executionId - Execution UUID
|
|
1612
|
+
* @param options - List options (pagination, filtering by type)
|
|
1613
|
+
* @returns Paginated list of artifacts
|
|
1614
|
+
*
|
|
1615
|
+
* @example
|
|
1616
|
+
* ```typescript
|
|
1617
|
+
* const { data: artifacts } = await client.artifacts.list('exec-uuid', {
|
|
1618
|
+
* type: 'file',
|
|
1619
|
+
* });
|
|
1620
|
+
* ```
|
|
1621
|
+
*/
|
|
1622
|
+
async list(executionId, options) {
|
|
1623
|
+
const query = {
|
|
1624
|
+
...this.buildListQuery(options),
|
|
1625
|
+
type: options?.type
|
|
12
1626
|
};
|
|
13
|
-
|
|
14
|
-
|
|
1627
|
+
return this.http.get(`/executions/${executionId}/artifacts`, query);
|
|
1628
|
+
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Get artifact metadata by UUID
|
|
1631
|
+
*
|
|
1632
|
+
* @param uuid - Artifact UUID
|
|
1633
|
+
* @returns Artifact metadata
|
|
1634
|
+
*
|
|
1635
|
+
* @example
|
|
1636
|
+
* ```typescript
|
|
1637
|
+
* const artifact = await client.artifacts.get('artifact-uuid');
|
|
1638
|
+
* console.log(artifact.name, artifact.size);
|
|
1639
|
+
* ```
|
|
1640
|
+
*/
|
|
1641
|
+
async get(uuid) {
|
|
1642
|
+
return this.httpGet(`/${uuid}`);
|
|
1643
|
+
}
|
|
1644
|
+
// ===== RECEIVE Operations =====
|
|
1645
|
+
/**
|
|
1646
|
+
* Download artifact content
|
|
1647
|
+
*
|
|
1648
|
+
* In Node.js returns Buffer, in browser returns Blob.
|
|
1649
|
+
*
|
|
1650
|
+
* @param uuid - Artifact UUID
|
|
1651
|
+
* @returns Artifact content as Buffer (Node.js) or Blob (browser)
|
|
1652
|
+
*
|
|
1653
|
+
* @example
|
|
1654
|
+
* ```typescript
|
|
1655
|
+
* const content = await client.artifacts.download('artifact-uuid');
|
|
1656
|
+
* // Node.js: Buffer
|
|
1657
|
+
* // Browser: Blob
|
|
1658
|
+
* ```
|
|
1659
|
+
*/
|
|
1660
|
+
async download(uuid) {
|
|
1661
|
+
const response = await this.http.request({
|
|
1662
|
+
method: "GET",
|
|
1663
|
+
path: `${this.basePath}/${uuid}/download`,
|
|
1664
|
+
headers: {
|
|
1665
|
+
"Accept": "*/*"
|
|
1666
|
+
}
|
|
1667
|
+
});
|
|
1668
|
+
if (typeof window !== "undefined") {
|
|
1669
|
+
return response.blob();
|
|
1670
|
+
} else {
|
|
1671
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
1672
|
+
return Buffer.from(arrayBuffer);
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Download artifact as a stream
|
|
1677
|
+
*
|
|
1678
|
+
* In Node.js returns Readable stream, in browser returns ReadableStream.
|
|
1679
|
+
*
|
|
1680
|
+
* @param uuid - Artifact UUID
|
|
1681
|
+
* @returns Readable stream
|
|
1682
|
+
*
|
|
1683
|
+
* @example
|
|
1684
|
+
* ```typescript
|
|
1685
|
+
* const stream = await client.artifacts.downloadStream('artifact-uuid');
|
|
1686
|
+
* // Pipe to file or process incrementally
|
|
1687
|
+
* ```
|
|
1688
|
+
*/
|
|
1689
|
+
async downloadStream(uuid) {
|
|
1690
|
+
const response = await this.http.request({
|
|
1691
|
+
method: "GET",
|
|
1692
|
+
path: `${this.basePath}/${uuid}/stream`,
|
|
1693
|
+
headers: {
|
|
1694
|
+
"Accept": "*/*"
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
1697
|
+
if (!response.body) {
|
|
1698
|
+
throw new Error("Response body is not available");
|
|
1699
|
+
}
|
|
1700
|
+
return response.body;
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Get a signed URL for direct download
|
|
1704
|
+
*
|
|
1705
|
+
* @param uuid - Artifact UUID
|
|
1706
|
+
* @param options - Options including expiration time
|
|
1707
|
+
* @returns Signed URL and expiration time
|
|
1708
|
+
*
|
|
1709
|
+
* @example
|
|
1710
|
+
* ```typescript
|
|
1711
|
+
* const { url, expiresAt } = await client.artifacts.getSignedUrl('artifact-uuid', {
|
|
1712
|
+
* expiresIn: 3600, // 1 hour
|
|
1713
|
+
* });
|
|
1714
|
+
* // Use URL for direct download
|
|
1715
|
+
* ```
|
|
1716
|
+
*/
|
|
1717
|
+
async getSignedUrl(uuid, options) {
|
|
1718
|
+
return this.httpPost(`/${uuid}/signed-url`, {
|
|
1719
|
+
expiresIn: options?.expiresIn ?? 3600
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
// ===== EDIT Operations =====
|
|
1723
|
+
/**
|
|
1724
|
+
* Upload a new artifact for an execution
|
|
1725
|
+
*
|
|
1726
|
+
* @param executionId - Execution UUID
|
|
1727
|
+
* @param input - Upload input (name, content, mime type)
|
|
1728
|
+
* @returns Created artifact metadata
|
|
1729
|
+
*
|
|
1730
|
+
* @example
|
|
1731
|
+
* ```typescript
|
|
1732
|
+
* const artifact = await client.artifacts.upload('exec-uuid', {
|
|
1733
|
+
* name: 'report.pdf',
|
|
1734
|
+
* content: fileBuffer,
|
|
1735
|
+
* mimeType: 'application/pdf',
|
|
1736
|
+
* });
|
|
1737
|
+
* ```
|
|
1738
|
+
*/
|
|
1739
|
+
async upload(executionId, input) {
|
|
1740
|
+
const formData = new FormData();
|
|
1741
|
+
if (input.content instanceof Blob) {
|
|
1742
|
+
formData.append("file", input.content, input.name);
|
|
1743
|
+
} else if (typeof Buffer !== "undefined" && Buffer.isBuffer(input.content)) {
|
|
1744
|
+
const arrayBuffer = input.content.buffer.slice(
|
|
1745
|
+
input.content.byteOffset,
|
|
1746
|
+
input.content.byteOffset + input.content.byteLength
|
|
1747
|
+
);
|
|
1748
|
+
const blob = new Blob([arrayBuffer], { type: input.mimeType ?? "application/octet-stream" });
|
|
1749
|
+
formData.append("file", blob, input.name);
|
|
1750
|
+
} else if (input.content instanceof ReadableStream) {
|
|
1751
|
+
const chunks = [];
|
|
1752
|
+
const reader = input.content.getReader();
|
|
1753
|
+
while (true) {
|
|
1754
|
+
const { done, value } = await reader.read();
|
|
1755
|
+
if (done) break;
|
|
1756
|
+
chunks.push(new Uint8Array(value));
|
|
1757
|
+
}
|
|
1758
|
+
const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
1759
|
+
const combined = new Uint8Array(totalLength);
|
|
1760
|
+
let offset = 0;
|
|
1761
|
+
for (const chunk of chunks) {
|
|
1762
|
+
combined.set(chunk, offset);
|
|
1763
|
+
offset += chunk.length;
|
|
1764
|
+
}
|
|
1765
|
+
const blob = new Blob([combined.buffer], { type: input.mimeType ?? "application/octet-stream" });
|
|
1766
|
+
formData.append("file", blob, input.name);
|
|
1767
|
+
}
|
|
1768
|
+
if (input.type) {
|
|
1769
|
+
formData.append("type", input.type);
|
|
1770
|
+
}
|
|
1771
|
+
if (input.metadata) {
|
|
1772
|
+
formData.append("metadata", JSON.stringify(input.metadata));
|
|
1773
|
+
}
|
|
1774
|
+
return this.http.request({
|
|
1775
|
+
method: "POST",
|
|
1776
|
+
path: `/executions/${executionId}/artifacts`,
|
|
1777
|
+
body: formData,
|
|
1778
|
+
headers: {
|
|
1779
|
+
// Remove Content-Type to let fetch set it with proper boundary
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Delete an artifact
|
|
1785
|
+
*
|
|
1786
|
+
* @param uuid - Artifact UUID
|
|
1787
|
+
*
|
|
1788
|
+
* @example
|
|
1789
|
+
* ```typescript
|
|
1790
|
+
* await client.artifacts.delete('artifact-uuid');
|
|
1791
|
+
* ```
|
|
1792
|
+
*/
|
|
1793
|
+
async delete(uuid) {
|
|
1794
|
+
return this.httpDelete(`/${uuid}`);
|
|
1795
|
+
}
|
|
1796
|
+
};
|
|
1797
|
+
|
|
1798
|
+
// src/resources/sessions.ts
|
|
1799
|
+
var SessionsAPI = class extends BaseResourceAPI {
|
|
1800
|
+
constructor(http, getStreamConfig) {
|
|
1801
|
+
super(http, "/sessions");
|
|
1802
|
+
this.getStreamConfig = getStreamConfig;
|
|
1803
|
+
}
|
|
1804
|
+
// ===== LIST Operations =====
|
|
1805
|
+
/**
|
|
1806
|
+
* List all sessions in the organization
|
|
1807
|
+
*
|
|
1808
|
+
* @param options - List options (pagination, filtering by agent)
|
|
1809
|
+
* @returns Paginated list of sessions
|
|
1810
|
+
*
|
|
1811
|
+
* @example
|
|
1812
|
+
* ```typescript
|
|
1813
|
+
* const { data: sessions } = await client.sessions.list({
|
|
1814
|
+
* agentUuid: 'agent-uuid',
|
|
1815
|
+
* });
|
|
1816
|
+
* ```
|
|
1817
|
+
*/
|
|
1818
|
+
async list(options) {
|
|
1819
|
+
const query = {
|
|
1820
|
+
...this.buildListQuery(options),
|
|
1821
|
+
agentUuid: options?.agentUuid
|
|
1822
|
+
};
|
|
1823
|
+
return this.httpGet("", query);
|
|
1824
|
+
}
|
|
1825
|
+
/**
|
|
1826
|
+
* Get session details by ID
|
|
1827
|
+
*
|
|
1828
|
+
* @param sessionId - Session ID
|
|
1829
|
+
* @returns Session details
|
|
1830
|
+
*
|
|
1831
|
+
* @example
|
|
1832
|
+
* ```typescript
|
|
1833
|
+
* const session = await client.sessions.get('session-id');
|
|
1834
|
+
* console.log(session.messageCount);
|
|
1835
|
+
* ```
|
|
1836
|
+
*/
|
|
1837
|
+
async get(sessionId) {
|
|
1838
|
+
return this.httpGet(`/${sessionId}`);
|
|
1839
|
+
}
|
|
1840
|
+
// ===== EXECUTE Operations =====
|
|
1841
|
+
/**
|
|
1842
|
+
* Send a message to a session
|
|
1843
|
+
*
|
|
1844
|
+
* Triggers an agent execution in the context of the session.
|
|
1845
|
+
* Returns immediately with execution ID. Use getHistory() to get
|
|
1846
|
+
* the response, or use stream() for real-time updates.
|
|
1847
|
+
*
|
|
1848
|
+
* @param sessionId - Session ID
|
|
1849
|
+
* @param input - Message content and optional context
|
|
1850
|
+
* @returns Execution ID and status
|
|
1851
|
+
*
|
|
1852
|
+
* @example
|
|
1853
|
+
* ```typescript
|
|
1854
|
+
* const { executionId } = await client.sessions.sendMessage('session-id', {
|
|
1855
|
+
* message: 'What is the capital of France?',
|
|
1856
|
+
* });
|
|
1857
|
+
* ```
|
|
1858
|
+
*/
|
|
1859
|
+
async sendMessage(sessionId, input) {
|
|
1860
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
1861
|
+
return this.httpPost(
|
|
1862
|
+
`/${sessionId}/messages`,
|
|
1863
|
+
{
|
|
1864
|
+
message: input.message,
|
|
1865
|
+
context: input.context,
|
|
1866
|
+
// Pass attachment IDs for multi-modal messages (images, documents, etc.)
|
|
1867
|
+
attachmentIds: input.attachmentIds
|
|
1868
|
+
},
|
|
1869
|
+
{ idempotencyKey }
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Send a message and wait for response (sync)
|
|
1874
|
+
*
|
|
1875
|
+
* @param sessionId - Session ID
|
|
1876
|
+
* @param input - Message content and optional context
|
|
1877
|
+
* @param pollInterval - Polling interval in ms (default: 1000)
|
|
1878
|
+
* @returns Final execution result
|
|
1879
|
+
*
|
|
1880
|
+
* @example
|
|
1881
|
+
* ```typescript
|
|
1882
|
+
* const execution = await client.sessions.sendMessageSync('session-id', {
|
|
1883
|
+
* message: 'What is the capital of France?',
|
|
1884
|
+
* });
|
|
1885
|
+
* console.log(execution.output);
|
|
1886
|
+
* ```
|
|
1887
|
+
*/
|
|
1888
|
+
async sendMessageSync(sessionId, input, pollInterval = 1e3) {
|
|
1889
|
+
const { executionId } = await this.sendMessage(sessionId, input);
|
|
1890
|
+
return this.pollExecution(executionId, pollInterval);
|
|
1891
|
+
}
|
|
1892
|
+
/**
|
|
1893
|
+
* Stream a message response
|
|
1894
|
+
*
|
|
1895
|
+
* Returns a StreamController for real-time event handling.
|
|
1896
|
+
*
|
|
1897
|
+
* @param sessionId - Session ID
|
|
1898
|
+
* @param input - Message content and optional context
|
|
1899
|
+
* @returns Stream controller
|
|
1900
|
+
*
|
|
1901
|
+
* @example
|
|
1902
|
+
* ```typescript
|
|
1903
|
+
* const stream = await client.sessions.stream('session-id', {
|
|
1904
|
+
* message: 'Tell me more about that',
|
|
1905
|
+
* });
|
|
1906
|
+
*
|
|
1907
|
+
* stream.on('log', (log) => console.log(log.title));
|
|
1908
|
+
* stream.on('complete', ({ execution }) => console.log(execution.output));
|
|
1909
|
+
*
|
|
1910
|
+
* const result = await stream.wait();
|
|
1911
|
+
* ```
|
|
1912
|
+
*/
|
|
1913
|
+
async stream(sessionId, input) {
|
|
1914
|
+
const { executionId } = await this.sendMessage(sessionId, input);
|
|
1915
|
+
const config = this.getStreamConfig();
|
|
1916
|
+
const controller = createExecutionStreamController({
|
|
1917
|
+
executionId,
|
|
1918
|
+
sessionId,
|
|
1919
|
+
baseUrl: config.baseUrl,
|
|
1920
|
+
apiKey: config.apiKey,
|
|
1921
|
+
orgId: config.orgId,
|
|
1922
|
+
getExecution: (id) => this.getExecution(id)
|
|
1923
|
+
});
|
|
1924
|
+
await controller.connect();
|
|
1925
|
+
return controller;
|
|
1926
|
+
}
|
|
1927
|
+
// ===== RECEIVE Operations =====
|
|
1928
|
+
/**
|
|
1929
|
+
* Get session conversation history
|
|
1930
|
+
*
|
|
1931
|
+
* @param sessionId - Session ID
|
|
1932
|
+
* @returns Session with all messages
|
|
1933
|
+
*
|
|
1934
|
+
* @example
|
|
1935
|
+
* ```typescript
|
|
1936
|
+
* const history = await client.sessions.getHistory('session-id');
|
|
1937
|
+
* history.messages.forEach(msg => {
|
|
1938
|
+
* console.log(`${msg.role}: ${msg.content}`);
|
|
1939
|
+
* });
|
|
1940
|
+
* ```
|
|
1941
|
+
*/
|
|
1942
|
+
async getHistory(sessionId) {
|
|
1943
|
+
return this.httpGet(`/${sessionId}/history`);
|
|
1944
|
+
}
|
|
1945
|
+
/**
|
|
1946
|
+
* List artifacts for a session
|
|
1947
|
+
*
|
|
1948
|
+
* Returns all artifacts generated across all executions in the session.
|
|
1949
|
+
* Useful for retrieving images, files, and other outputs created during
|
|
1950
|
+
* agent interactions.
|
|
1951
|
+
*
|
|
1952
|
+
* @param sessionId - Session ID
|
|
1953
|
+
* @param options - List options (filtering by type, pagination)
|
|
1954
|
+
* @returns Paginated list of session artifacts
|
|
1955
|
+
*
|
|
1956
|
+
* @example
|
|
1957
|
+
* ```typescript
|
|
1958
|
+
* // Get all images from a session
|
|
1959
|
+
* const { data: artifacts } = await client.sessions.listArtifacts('session-id', {
|
|
1960
|
+
* type: 'image',
|
|
1961
|
+
* });
|
|
1962
|
+
*
|
|
1963
|
+
* artifacts.forEach(artifact => {
|
|
1964
|
+
* console.log(artifact.filename, artifact.url);
|
|
1965
|
+
* });
|
|
1966
|
+
* ```
|
|
1967
|
+
*/
|
|
1968
|
+
async listArtifacts(sessionId, options) {
|
|
1969
|
+
const query = {
|
|
1970
|
+
...this.buildListQuery(options),
|
|
1971
|
+
type: options?.type,
|
|
1972
|
+
includeVariants: options?.includeVariants
|
|
1973
|
+
};
|
|
1974
|
+
return this.httpGet(`/${sessionId}/artifacts`, query);
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Get execution result
|
|
1978
|
+
*/
|
|
1979
|
+
async getExecution(executionId) {
|
|
1980
|
+
return this.http.get(`/executions/${executionId}`);
|
|
1981
|
+
}
|
|
1982
|
+
// ===== EDIT Operations =====
|
|
1983
|
+
/**
|
|
1984
|
+
* Create a new session for an agent
|
|
1985
|
+
*
|
|
1986
|
+
* @param agentUuid - Agent UUID to associate with session
|
|
1987
|
+
* @param options - Optional system message
|
|
1988
|
+
* @returns Created session
|
|
1989
|
+
*
|
|
1990
|
+
* @example
|
|
1991
|
+
* ```typescript
|
|
1992
|
+
* const session = await client.sessions.create({
|
|
1993
|
+
* agentUuid: 'agent-uuid',
|
|
1994
|
+
* systemMessage: 'You are a helpful assistant.',
|
|
1995
|
+
* });
|
|
1996
|
+
* console.log(session.id);
|
|
1997
|
+
* ```
|
|
1998
|
+
*/
|
|
1999
|
+
async create(input) {
|
|
2000
|
+
const idempotencyKey = generateIdempotencyKey();
|
|
2001
|
+
return this.httpPost("", input, { idempotencyKey });
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* Delete a session and all its messages
|
|
2005
|
+
*
|
|
2006
|
+
* @param sessionId - Session ID
|
|
2007
|
+
*
|
|
2008
|
+
* @example
|
|
2009
|
+
* ```typescript
|
|
2010
|
+
* await client.sessions.delete('session-id');
|
|
2011
|
+
* ```
|
|
2012
|
+
*/
|
|
2013
|
+
async delete(sessionId) {
|
|
2014
|
+
return this.httpDelete(`/${sessionId}`);
|
|
2015
|
+
}
|
|
2016
|
+
// ===== Private helpers =====
|
|
2017
|
+
/**
|
|
2018
|
+
* Poll execution until completion
|
|
2019
|
+
*/
|
|
2020
|
+
async pollExecution(executionId, pollInterval) {
|
|
2021
|
+
while (true) {
|
|
2022
|
+
const execution = await this.getExecution(executionId);
|
|
2023
|
+
if (execution.status === "completed" || execution.status === "failed" || execution.status === "cancelled") {
|
|
2024
|
+
return execution;
|
|
2025
|
+
}
|
|
2026
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
15
2027
|
}
|
|
16
|
-
|
|
17
|
-
|
|
2028
|
+
}
|
|
2029
|
+
};
|
|
2030
|
+
|
|
2031
|
+
// src/client.ts
|
|
2032
|
+
var DEFAULT_BASE_URL = "https://api.aisaas.com/v1";
|
|
2033
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
2034
|
+
var AISaaSClient = class {
|
|
2035
|
+
httpClient;
|
|
2036
|
+
config;
|
|
2037
|
+
/**
|
|
2038
|
+
* Agent operations (list, execute, stream, update)
|
|
2039
|
+
*/
|
|
2040
|
+
agents;
|
|
2041
|
+
/**
|
|
2042
|
+
* Skill operations (list, get, execute, update)
|
|
2043
|
+
*/
|
|
2044
|
+
skills;
|
|
2045
|
+
/**
|
|
2046
|
+
* Tool operations (list, get, execute, validate, update)
|
|
2047
|
+
*/
|
|
2048
|
+
tools;
|
|
2049
|
+
/**
|
|
2050
|
+
* Artifact operations (list, get, download, upload, delete)
|
|
2051
|
+
*/
|
|
2052
|
+
artifacts;
|
|
2053
|
+
/**
|
|
2054
|
+
* Session operations (list, get, create, message, delete)
|
|
2055
|
+
*/
|
|
2056
|
+
sessions;
|
|
2057
|
+
/**
|
|
2058
|
+
* Create a new AI SaaS client
|
|
2059
|
+
*
|
|
2060
|
+
* @param options - Client configuration options
|
|
2061
|
+
*
|
|
2062
|
+
* @example
|
|
2063
|
+
* ```typescript
|
|
2064
|
+
* // API key authentication
|
|
2065
|
+
* const client = new AISaaSClient({
|
|
2066
|
+
* apiKey: 'sk_...',
|
|
2067
|
+
* orgId: 123,
|
|
2068
|
+
* });
|
|
2069
|
+
*
|
|
2070
|
+
* // OAuth token authentication
|
|
2071
|
+
* const client = new AISaaSClient({
|
|
2072
|
+
* token: 'eyJ...',
|
|
2073
|
+
* orgId: 123,
|
|
2074
|
+
* });
|
|
2075
|
+
*
|
|
2076
|
+
* // Token provider (for automatic refresh)
|
|
2077
|
+
* const client = new AISaaSClient({
|
|
2078
|
+
* tokenProvider: async () => myAuth.getAccessToken(),
|
|
2079
|
+
* orgId: 123,
|
|
2080
|
+
* });
|
|
2081
|
+
* ```
|
|
2082
|
+
*/
|
|
2083
|
+
constructor(options) {
|
|
2084
|
+
this.validateOptions(options);
|
|
2085
|
+
this.config = {
|
|
2086
|
+
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
2087
|
+
orgId: options.orgId,
|
|
2088
|
+
timeout: options.timeout ?? DEFAULT_TIMEOUT,
|
|
2089
|
+
headers: options.headers
|
|
2090
|
+
};
|
|
2091
|
+
if ("apiKey" in options) {
|
|
2092
|
+
this.config.apiKey = options.apiKey;
|
|
2093
|
+
} else if ("token" in options) {
|
|
2094
|
+
this.config.token = options.token;
|
|
2095
|
+
} else if ("tokenProvider" in options) {
|
|
2096
|
+
this.config.tokenProvider = options.tokenProvider;
|
|
18
2097
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
2098
|
+
const retryConfig = {
|
|
2099
|
+
...DEFAULT_RETRY_CONFIG,
|
|
2100
|
+
maxRetries: options.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries
|
|
2101
|
+
};
|
|
2102
|
+
const httpConfig = {
|
|
2103
|
+
baseUrl: this.config.baseUrl,
|
|
2104
|
+
apiKey: this.config.apiKey,
|
|
2105
|
+
token: this.config.token,
|
|
2106
|
+
tokenProvider: this.config.tokenProvider,
|
|
2107
|
+
orgId: this.config.orgId,
|
|
2108
|
+
timeout: this.config.timeout,
|
|
2109
|
+
retry: retryConfig,
|
|
2110
|
+
headers: this.config.headers
|
|
2111
|
+
};
|
|
2112
|
+
this.httpClient = new HttpClient(httpConfig);
|
|
2113
|
+
const getStreamConfig = () => ({
|
|
2114
|
+
baseUrl: this.config.baseUrl,
|
|
2115
|
+
apiKey: this.config.apiKey,
|
|
2116
|
+
token: this.config.token,
|
|
2117
|
+
tokenProvider: this.config.tokenProvider,
|
|
2118
|
+
orgId: this.config.orgId
|
|
27
2119
|
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
2120
|
+
this.agents = new AgentsAPI(this.httpClient, getStreamConfig);
|
|
2121
|
+
this.skills = new SkillsAPI(this.httpClient);
|
|
2122
|
+
this.tools = new ToolsAPI(this.httpClient);
|
|
2123
|
+
this.artifacts = new ArtifactsAPI(this.httpClient);
|
|
2124
|
+
this.sessions = new SessionsAPI(this.httpClient, getStreamConfig);
|
|
2125
|
+
}
|
|
2126
|
+
/**
|
|
2127
|
+
* Validate client options
|
|
2128
|
+
*/
|
|
2129
|
+
validateOptions(options) {
|
|
2130
|
+
const hasApiKey = "apiKey" in options && options.apiKey;
|
|
2131
|
+
const hasToken = "token" in options && options.token;
|
|
2132
|
+
const hasTokenProvider = "tokenProvider" in options && options.tokenProvider;
|
|
2133
|
+
if (!hasApiKey && !hasToken && !hasTokenProvider) {
|
|
2134
|
+
throw new Error(
|
|
2135
|
+
"Authentication required. Provide apiKey, token, or tokenProvider."
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
if (options.orgId === void 0 || options.orgId === null) {
|
|
2139
|
+
throw new Error("Organization ID (orgId) is required.");
|
|
2140
|
+
}
|
|
2141
|
+
if (typeof options.orgId !== "number") {
|
|
2142
|
+
throw new Error("Organization ID (orgId) must be a number.");
|
|
42
2143
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
2146
|
+
|
|
2147
|
+
// src/chat/adapters/default-adapter.ts
|
|
2148
|
+
function resolveClient(config) {
|
|
2149
|
+
if (config.client) return config.client;
|
|
2150
|
+
if (!config.baseUrl) {
|
|
2151
|
+
throw new Error("DefaultAdapterConfig requires either 'client' or 'baseUrl'");
|
|
2152
|
+
}
|
|
2153
|
+
if (config.orgId === void 0 || config.orgId === null) {
|
|
2154
|
+
throw new Error("DefaultAdapterConfig requires 'orgId' when 'client' is not provided");
|
|
2155
|
+
}
|
|
2156
|
+
if (config.apiKey) {
|
|
2157
|
+
return new AISaaSClient({
|
|
2158
|
+
apiKey: config.apiKey,
|
|
2159
|
+
orgId: config.orgId,
|
|
2160
|
+
baseUrl: config.baseUrl
|
|
2161
|
+
});
|
|
2162
|
+
}
|
|
2163
|
+
if (config.token) {
|
|
2164
|
+
return new AISaaSClient({
|
|
2165
|
+
token: config.token,
|
|
2166
|
+
orgId: config.orgId,
|
|
2167
|
+
baseUrl: config.baseUrl
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
2170
|
+
if (config.tokenProvider) {
|
|
2171
|
+
return new AISaaSClient({
|
|
2172
|
+
tokenProvider: config.tokenProvider,
|
|
2173
|
+
orgId: config.orgId,
|
|
2174
|
+
baseUrl: config.baseUrl
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
throw new Error(
|
|
2178
|
+
"DefaultAdapterConfig requires 'apiKey', 'token', or 'tokenProvider' when 'client' is not provided"
|
|
2179
|
+
);
|
|
2180
|
+
}
|
|
2181
|
+
function resolveBaseUrl(config) {
|
|
2182
|
+
if (config.baseUrl) return config.baseUrl;
|
|
2183
|
+
return "https://api.aisaas.com/v1";
|
|
2184
|
+
}
|
|
2185
|
+
async function getAuthHeaders(config) {
|
|
2186
|
+
const headers = {
|
|
2187
|
+
"Content-Type": "application/json"
|
|
2188
|
+
};
|
|
2189
|
+
if (config.apiKey) {
|
|
2190
|
+
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
2191
|
+
} else if (config.token) {
|
|
2192
|
+
headers["Authorization"] = `Bearer ${config.token}`;
|
|
2193
|
+
} else if (config.tokenProvider) {
|
|
2194
|
+
const token = await config.tokenProvider();
|
|
2195
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
2196
|
+
}
|
|
2197
|
+
if (config.orgId !== void 0) {
|
|
2198
|
+
headers["X-Org-Id"] = String(config.orgId);
|
|
2199
|
+
}
|
|
2200
|
+
return headers;
|
|
2201
|
+
}
|
|
2202
|
+
async function rawRequest(config, method, path, body) {
|
|
2203
|
+
const baseUrl = resolveBaseUrl(config);
|
|
2204
|
+
const url = `${baseUrl}${path}`;
|
|
2205
|
+
const headers = await getAuthHeaders(config);
|
|
2206
|
+
const response = await fetch(url, {
|
|
2207
|
+
method,
|
|
2208
|
+
headers,
|
|
2209
|
+
body: body ? JSON.stringify(body) : void 0
|
|
2210
|
+
});
|
|
2211
|
+
if (!response.ok) {
|
|
2212
|
+
const errorText = await response.text();
|
|
2213
|
+
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
|
|
2214
|
+
try {
|
|
2215
|
+
const errorData = JSON.parse(errorText);
|
|
2216
|
+
if (errorData.message) errorMessage = errorData.message;
|
|
2217
|
+
if (errorData.error?.message) errorMessage = errorData.error.message;
|
|
2218
|
+
} catch {
|
|
2219
|
+
if (errorText) errorMessage = errorText;
|
|
46
2220
|
}
|
|
47
|
-
|
|
2221
|
+
throw new Error(errorMessage);
|
|
48
2222
|
}
|
|
2223
|
+
const text = await response.text();
|
|
2224
|
+
if (!text) return void 0;
|
|
2225
|
+
const parsed = JSON.parse(text);
|
|
2226
|
+
if (parsed && typeof parsed === "object" && "success" in parsed && parsed.success && "data" in parsed) {
|
|
2227
|
+
return parsed.data;
|
|
2228
|
+
}
|
|
2229
|
+
return parsed;
|
|
2230
|
+
}
|
|
2231
|
+
function createDefaultAdapter(config) {
|
|
2232
|
+
const client = resolveClient(config);
|
|
2233
|
+
const baseUrl = resolveBaseUrl(config);
|
|
49
2234
|
return {
|
|
50
2235
|
// -------------------------------------------------------------------------
|
|
51
2236
|
// Execution Methods
|
|
52
2237
|
// -------------------------------------------------------------------------
|
|
53
2238
|
async startExecution(input) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
2239
|
+
const tenantContext = config.orgId ? { tenantOrgId: config.orgId } : {};
|
|
2240
|
+
if (input.sessionId) {
|
|
2241
|
+
const result2 = await client.sessions.sendMessage(input.sessionId, {
|
|
2242
|
+
message: input.content,
|
|
2243
|
+
attachmentIds: input.attachmentUuids,
|
|
2244
|
+
context: tenantContext
|
|
2245
|
+
});
|
|
2246
|
+
return {
|
|
2247
|
+
executionId: result2.executionId,
|
|
2248
|
+
sessionId: result2.sessionId ?? input.sessionId
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
const pathId = input.agentId || input.teamId || "";
|
|
2252
|
+
const result = await client.agents.execute(pathId, {
|
|
2253
|
+
task: input.content,
|
|
2254
|
+
context: tenantContext,
|
|
2255
|
+
attachmentIds: input.attachmentUuids,
|
|
2256
|
+
teamUuid: input.teamId
|
|
58
2257
|
});
|
|
2258
|
+
return {
|
|
2259
|
+
executionId: result.executionId,
|
|
2260
|
+
sessionId: result.sessionId ?? ""
|
|
2261
|
+
};
|
|
59
2262
|
},
|
|
60
2263
|
async sendMessage(input) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
2264
|
+
const result = await client.sessions.sendMessage(input.sessionId, {
|
|
2265
|
+
message: input.content,
|
|
2266
|
+
attachmentIds: input.attachmentUuids,
|
|
2267
|
+
// Always include tenantOrgId so follow-up messages use the correct
|
|
2268
|
+
// tenant org for tool calls (not cadra's internal org).
|
|
2269
|
+
context: config.orgId ? { tenantOrgId: config.orgId } : void 0
|
|
64
2270
|
});
|
|
2271
|
+
return {
|
|
2272
|
+
executionId: result.executionId,
|
|
2273
|
+
sessionId: result.sessionId ?? input.sessionId
|
|
2274
|
+
};
|
|
65
2275
|
},
|
|
66
2276
|
async cancelExecution(uuid) {
|
|
67
|
-
await
|
|
2277
|
+
await client.agents.cancelExecution(uuid);
|
|
68
2278
|
},
|
|
69
2279
|
async getExecution(uuid) {
|
|
70
|
-
|
|
2280
|
+
const exec = await client.agents.getExecution(uuid);
|
|
2281
|
+
return {
|
|
2282
|
+
uuid: exec.uuid,
|
|
2283
|
+
status: exec.status === "queued" ? "pending" : exec.status,
|
|
2284
|
+
result: exec.output,
|
|
2285
|
+
error: exec.error
|
|
2286
|
+
};
|
|
71
2287
|
},
|
|
72
2288
|
// -------------------------------------------------------------------------
|
|
73
|
-
// Attachment Methods
|
|
2289
|
+
// Attachment Methods (raw fetch — AISaaSClient has no AttachmentsAPI yet)
|
|
74
2290
|
// -------------------------------------------------------------------------
|
|
75
2291
|
async getUploadUrl(input) {
|
|
76
|
-
return
|
|
2292
|
+
return rawRequest(config, "POST", "/attachments/upload-url", {
|
|
77
2293
|
sessionId: input.sessionId,
|
|
78
2294
|
filename: input.filename,
|
|
79
2295
|
mimeType: input.mimeType,
|
|
@@ -81,37 +2297,77 @@ function createDefaultAdapter(config) {
|
|
|
81
2297
|
});
|
|
82
2298
|
},
|
|
83
2299
|
async confirmUpload(uuid) {
|
|
84
|
-
await
|
|
2300
|
+
await rawRequest(config, "POST", `/attachments/${uuid}/confirm`);
|
|
85
2301
|
},
|
|
86
2302
|
async getDownloadUrl(uuid) {
|
|
87
|
-
const result = await
|
|
2303
|
+
const result = await rawRequest(config, "GET", `/attachments/${uuid}/download-url`);
|
|
88
2304
|
return result.url;
|
|
89
2305
|
},
|
|
90
2306
|
// -------------------------------------------------------------------------
|
|
91
2307
|
// Artifact Methods
|
|
92
2308
|
// -------------------------------------------------------------------------
|
|
93
2309
|
async getSessionArtifacts(sessionId) {
|
|
94
|
-
const result = await
|
|
95
|
-
return result.data ?? []
|
|
2310
|
+
const result = await client.sessions.listArtifacts(sessionId);
|
|
2311
|
+
return (result.data ?? []).map((a) => ({
|
|
2312
|
+
uuid: a.uuid,
|
|
2313
|
+
type: a.type,
|
|
2314
|
+
filename: a.filename,
|
|
2315
|
+
mimeType: a.mimeType,
|
|
2316
|
+
url: a.url,
|
|
2317
|
+
createdAt: a.createdAt
|
|
2318
|
+
}));
|
|
96
2319
|
},
|
|
97
2320
|
// -------------------------------------------------------------------------
|
|
98
2321
|
// Session History Methods
|
|
99
2322
|
// -------------------------------------------------------------------------
|
|
100
2323
|
async getSessionHistory(sessionId) {
|
|
101
|
-
|
|
2324
|
+
const history = await client.sessions.getHistory(sessionId);
|
|
2325
|
+
return {
|
|
2326
|
+
sessionId: history.session.id,
|
|
2327
|
+
agentId: history.session.agentUuid,
|
|
2328
|
+
messages: history.messages.map((m) => ({
|
|
2329
|
+
uuid: m.id,
|
|
2330
|
+
role: m.role,
|
|
2331
|
+
content: m.content,
|
|
2332
|
+
timestamp: m.timestamp
|
|
2333
|
+
})),
|
|
2334
|
+
createdAt: history.session.createdAt,
|
|
2335
|
+
updatedAt: history.session.lastMessageAt
|
|
2336
|
+
};
|
|
2337
|
+
},
|
|
2338
|
+
// -------------------------------------------------------------------------
|
|
2339
|
+
// Session List Methods
|
|
2340
|
+
// -------------------------------------------------------------------------
|
|
2341
|
+
async listSessions(input) {
|
|
2342
|
+
const result = await client.sessions.list({
|
|
2343
|
+
agentUuid: input.agentId,
|
|
2344
|
+
limit: input.limit,
|
|
2345
|
+
offset: input.offset
|
|
2346
|
+
});
|
|
2347
|
+
return {
|
|
2348
|
+
sessions: result.data.map((s) => ({
|
|
2349
|
+
id: s.id,
|
|
2350
|
+
agentId: s.agentUuid,
|
|
2351
|
+
createdAt: s.createdAt,
|
|
2352
|
+
lastMessageAt: s.lastMessageAt,
|
|
2353
|
+
messageCount: s.messageCount,
|
|
2354
|
+
lastUserMessage: s.lastUserMessage
|
|
2355
|
+
})),
|
|
2356
|
+
total: result.pagination.total,
|
|
2357
|
+
hasMore: result.pagination.hasMore
|
|
2358
|
+
};
|
|
2359
|
+
},
|
|
2360
|
+
// -------------------------------------------------------------------------
|
|
2361
|
+
// Stream Methods
|
|
2362
|
+
// -------------------------------------------------------------------------
|
|
2363
|
+
createStream(executionId, sessionId) {
|
|
2364
|
+
return client.agents.streamExecution(executionId, sessionId);
|
|
102
2365
|
},
|
|
103
2366
|
// -------------------------------------------------------------------------
|
|
104
|
-
// SSE Stream URL
|
|
2367
|
+
// SSE Stream URL (legacy backward compat)
|
|
105
2368
|
// -------------------------------------------------------------------------
|
|
106
2369
|
getStreamUrl(executionId) {
|
|
107
|
-
|
|
108
|
-
if (config.apiKey) {
|
|
109
|
-
url.searchParams.set("apiKey", config.apiKey);
|
|
110
|
-
}
|
|
111
|
-
if (config.orgId) {
|
|
112
|
-
url.searchParams.set("orgId", String(config.orgId));
|
|
113
|
-
}
|
|
114
|
-
return url.toString();
|
|
2370
|
+
return `${baseUrl}/agents/runs/${executionId}/stream`;
|
|
115
2371
|
}
|
|
116
2372
|
};
|
|
117
2373
|
}
|
|
@@ -120,7 +2376,7 @@ function ChatConfigProvider({ config, children }) {
|
|
|
120
2376
|
const adapter = useMemo(
|
|
121
2377
|
() => createDefaultAdapter(config),
|
|
122
2378
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
123
|
-
[config.baseUrl, config.apiKey, config.orgId]
|
|
2379
|
+
[config.baseUrl, config.apiKey, config.token, config.tokenProvider, config.orgId, config.client]
|
|
124
2380
|
);
|
|
125
2381
|
const value = useMemo(() => ({ adapter }), [adapter]);
|
|
126
2382
|
return /* @__PURE__ */ jsx(ChatConfigContext.Provider, { value, children });
|
|
@@ -419,6 +2675,9 @@ function useExecutionStream(options) {
|
|
|
419
2675
|
}
|
|
420
2676
|
|
|
421
2677
|
// src/chat/hooks/useAgentChat.ts
|
|
2678
|
+
var SILENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
2679
|
+
"check_delegations"
|
|
2680
|
+
]);
|
|
422
2681
|
function generateId() {
|
|
423
2682
|
return `msg-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
424
2683
|
}
|
|
@@ -451,6 +2710,7 @@ function historyToMessage(historyMsg) {
|
|
|
451
2710
|
function useAgentChat(options) {
|
|
452
2711
|
const {
|
|
453
2712
|
agentId,
|
|
2713
|
+
teamId,
|
|
454
2714
|
sessionId: initialSessionId,
|
|
455
2715
|
adapter: propsAdapter,
|
|
456
2716
|
initialMessages = [],
|
|
@@ -460,6 +2720,7 @@ function useAgentChat(options) {
|
|
|
460
2720
|
onSessionStart,
|
|
461
2721
|
// Deprecated, but still supported
|
|
462
2722
|
onToolCall,
|
|
2723
|
+
onToolResult,
|
|
463
2724
|
onClarification
|
|
464
2725
|
} = options;
|
|
465
2726
|
const contextConfig = useChatConfigOptional();
|
|
@@ -469,6 +2730,15 @@ function useAgentChat(options) {
|
|
|
469
2730
|
"No adapter provided. Use ChatConfigProvider or pass adapter prop to AgentChat."
|
|
470
2731
|
);
|
|
471
2732
|
}
|
|
2733
|
+
const useStreamController = !!adapter.createStream;
|
|
2734
|
+
const onToolCallRef = useRef(onToolCall);
|
|
2735
|
+
const onToolResultRef = useRef(onToolResult);
|
|
2736
|
+
useEffect(() => {
|
|
2737
|
+
onToolCallRef.current = onToolCall;
|
|
2738
|
+
}, [onToolCall]);
|
|
2739
|
+
useEffect(() => {
|
|
2740
|
+
onToolResultRef.current = onToolResult;
|
|
2741
|
+
}, [onToolResult]);
|
|
472
2742
|
const [messages, setMessages] = useState(initialMessages);
|
|
473
2743
|
const [isLoading, setIsLoading] = useState(false);
|
|
474
2744
|
const [isLoadingHistory, setIsLoadingHistory] = useState(false);
|
|
@@ -476,11 +2746,15 @@ function useAgentChat(options) {
|
|
|
476
2746
|
const [currentSessionId, setCurrentSessionId] = useState(initialSessionId ?? null);
|
|
477
2747
|
const [currentExecutionId, setCurrentExecutionId] = useState(null);
|
|
478
2748
|
const [clarification, setClarification] = useState(null);
|
|
2749
|
+
const [isStreamControllerConnected, setIsStreamControllerConnected] = useState(false);
|
|
479
2750
|
const hasLoadedInitialSession = useRef(false);
|
|
2751
|
+
const internallyCreatedSessionRef = useRef(null);
|
|
480
2752
|
const streamingContentRef = useRef("");
|
|
481
2753
|
const streamingMessageIdRef = useRef(null);
|
|
482
2754
|
const pendingToolCallsRef = useRef(/* @__PURE__ */ new Map());
|
|
483
2755
|
const lastMessageContentRef = useRef("");
|
|
2756
|
+
const firedToolResultsRef = useRef(/* @__PURE__ */ new Set());
|
|
2757
|
+
const streamControllerRef = useRef(null);
|
|
484
2758
|
const updateStreamingMessage = useCallback((content) => {
|
|
485
2759
|
if (!streamingMessageIdRef.current) return;
|
|
486
2760
|
setMessages(
|
|
@@ -490,10 +2764,11 @@ function useAgentChat(options) {
|
|
|
490
2764
|
);
|
|
491
2765
|
}, []);
|
|
492
2766
|
const updateToolCall = useCallback((toolCall) => {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
(
|
|
496
|
-
|
|
2767
|
+
setMessages((prev) => {
|
|
2768
|
+
const targetId = streamingMessageIdRef.current ?? [...prev].reverse().find((m) => m.role === "assistant")?.id;
|
|
2769
|
+
if (!targetId) return prev;
|
|
2770
|
+
return prev.map((msg) => {
|
|
2771
|
+
if (msg.id !== targetId) return msg;
|
|
497
2772
|
const existingIndex = msg.toolCalls?.findIndex((tc) => tc.id === toolCall.id) ?? -1;
|
|
498
2773
|
if (existingIndex >= 0) {
|
|
499
2774
|
const newToolCalls = [...msg.toolCalls ?? []];
|
|
@@ -505,8 +2780,8 @@ function useAgentChat(options) {
|
|
|
505
2780
|
toolCalls: [...msg.toolCalls ?? [], toolCall]
|
|
506
2781
|
};
|
|
507
2782
|
}
|
|
508
|
-
})
|
|
509
|
-
);
|
|
2783
|
+
});
|
|
2784
|
+
});
|
|
510
2785
|
}, []);
|
|
511
2786
|
const finalizeStreamingMessage = useCallback(() => {
|
|
512
2787
|
if (!streamingMessageIdRef.current) return;
|
|
@@ -518,14 +2793,103 @@ function useAgentChat(options) {
|
|
|
518
2793
|
streamingMessageIdRef.current = null;
|
|
519
2794
|
streamingContentRef.current = "";
|
|
520
2795
|
pendingToolCallsRef.current.clear();
|
|
2796
|
+
firedToolResultsRef.current.clear();
|
|
521
2797
|
}, []);
|
|
2798
|
+
const handleExecutionLog = useCallback(
|
|
2799
|
+
(log) => {
|
|
2800
|
+
console.log("[useAgentChat] handleExecutionLog:", log.type, log.metadata);
|
|
2801
|
+
switch (log.type) {
|
|
2802
|
+
// Primary handlers for ai-saas-api worker log types
|
|
2803
|
+
case "llm_response": {
|
|
2804
|
+
const llmText = log.metadata?.text || "";
|
|
2805
|
+
if (llmText) {
|
|
2806
|
+
streamingContentRef.current = llmText;
|
|
2807
|
+
updateStreamingMessage(llmText);
|
|
2808
|
+
}
|
|
2809
|
+
break;
|
|
2810
|
+
}
|
|
2811
|
+
case "message": {
|
|
2812
|
+
const role = log.metadata?.role;
|
|
2813
|
+
const content = log.metadata?.content;
|
|
2814
|
+
if (role === "assistant" && content) {
|
|
2815
|
+
streamingContentRef.current = content;
|
|
2816
|
+
updateStreamingMessage(content);
|
|
2817
|
+
}
|
|
2818
|
+
break;
|
|
2819
|
+
}
|
|
2820
|
+
case "info":
|
|
2821
|
+
case "system":
|
|
2822
|
+
console.log("[useAgentChat] Status:", log.type, log.title);
|
|
2823
|
+
break;
|
|
2824
|
+
case "tool_call": {
|
|
2825
|
+
const toolName = log.metadata?.toolName;
|
|
2826
|
+
if (SILENT_TOOL_NAMES.has(toolName)) break;
|
|
2827
|
+
const toolCallId = log.metadata?.toolCallId || `tool-${Date.now()}`;
|
|
2828
|
+
const args = log.metadata?.inputs;
|
|
2829
|
+
const newToolCall = {
|
|
2830
|
+
id: toolCallId,
|
|
2831
|
+
name: toolName,
|
|
2832
|
+
args,
|
|
2833
|
+
status: "running"
|
|
2834
|
+
};
|
|
2835
|
+
pendingToolCallsRef.current.set(toolCallId, newToolCall);
|
|
2836
|
+
updateToolCall(newToolCall);
|
|
2837
|
+
onToolCallRef.current?.(toolName, args);
|
|
2838
|
+
break;
|
|
2839
|
+
}
|
|
2840
|
+
case "tool_result": {
|
|
2841
|
+
const resultToolName = log.metadata?.toolName;
|
|
2842
|
+
if (SILENT_TOOL_NAMES.has(resultToolName)) break;
|
|
2843
|
+
const resultToolCallId = log.metadata?.toolCallId;
|
|
2844
|
+
const result = log.metadata?.output;
|
|
2845
|
+
const durationMs = log.durationMs;
|
|
2846
|
+
let matchedToolCall = resultToolCallId ? pendingToolCallsRef.current.get(resultToolCallId) : void 0;
|
|
2847
|
+
if (!matchedToolCall) {
|
|
2848
|
+
for (const [, tc] of pendingToolCallsRef.current) {
|
|
2849
|
+
if (tc.name === resultToolName && tc.status === "running") {
|
|
2850
|
+
matchedToolCall = tc;
|
|
2851
|
+
break;
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
if (matchedToolCall) {
|
|
2856
|
+
const completedToolCall = {
|
|
2857
|
+
...matchedToolCall,
|
|
2858
|
+
result,
|
|
2859
|
+
status: "completed",
|
|
2860
|
+
durationMs
|
|
2861
|
+
};
|
|
2862
|
+
updateToolCall(completedToolCall);
|
|
2863
|
+
pendingToolCallsRef.current.delete(matchedToolCall.id);
|
|
2864
|
+
} else if (resultToolCallId) {
|
|
2865
|
+
const lateToolCall = {
|
|
2866
|
+
id: resultToolCallId,
|
|
2867
|
+
name: resultToolName || "unknown",
|
|
2868
|
+
args: {},
|
|
2869
|
+
result,
|
|
2870
|
+
status: "completed",
|
|
2871
|
+
durationMs
|
|
2872
|
+
};
|
|
2873
|
+
updateToolCall(lateToolCall);
|
|
2874
|
+
}
|
|
2875
|
+
if (resultToolCallId) {
|
|
2876
|
+
firedToolResultsRef.current.add(resultToolCallId);
|
|
2877
|
+
}
|
|
2878
|
+
onToolResultRef.current?.(resultToolName, result, durationMs);
|
|
2879
|
+
break;
|
|
2880
|
+
}
|
|
2881
|
+
default:
|
|
2882
|
+
console.log("[useAgentChat] Unhandled log type:", log.type, log);
|
|
2883
|
+
}
|
|
2884
|
+
},
|
|
2885
|
+
[updateStreamingMessage, updateToolCall]
|
|
2886
|
+
);
|
|
522
2887
|
const handleStreamLog = useCallback(
|
|
523
2888
|
(log) => {
|
|
524
2889
|
console.log("[useAgentChat] handleStreamLog:", log.logType, log.metadata);
|
|
525
2890
|
switch (log.logType) {
|
|
526
|
-
// Primary handlers for ai-saas-api worker log types
|
|
527
2891
|
case "llm_response": {
|
|
528
|
-
const llmText = log.metadata?.text ||
|
|
2892
|
+
const llmText = log.metadata?.text || "";
|
|
529
2893
|
if (llmText) {
|
|
530
2894
|
streamingContentRef.current = llmText;
|
|
531
2895
|
updateStreamingMessage(llmText);
|
|
@@ -545,7 +2909,6 @@ function useAgentChat(options) {
|
|
|
545
2909
|
case "system":
|
|
546
2910
|
console.log("[useAgentChat] Status:", log.logType, log.message);
|
|
547
2911
|
break;
|
|
548
|
-
// Legacy/fallback handlers
|
|
549
2912
|
case "response_text":
|
|
550
2913
|
case "text_chunk": {
|
|
551
2914
|
const text = log.metadata?.text || log.message || "";
|
|
@@ -555,6 +2918,7 @@ function useAgentChat(options) {
|
|
|
555
2918
|
}
|
|
556
2919
|
case "tool_call": {
|
|
557
2920
|
const toolName = log.metadata?.toolName;
|
|
2921
|
+
if (SILENT_TOOL_NAMES.has(toolName)) break;
|
|
558
2922
|
const toolCallId = log.metadata?.toolCallId || `tool-${Date.now()}`;
|
|
559
2923
|
const args = log.metadata?.inputs;
|
|
560
2924
|
const newToolCall = {
|
|
@@ -565,11 +2929,12 @@ function useAgentChat(options) {
|
|
|
565
2929
|
};
|
|
566
2930
|
pendingToolCallsRef.current.set(toolCallId, newToolCall);
|
|
567
2931
|
updateToolCall(newToolCall);
|
|
568
|
-
|
|
2932
|
+
onToolCallRef.current?.(toolName, args);
|
|
569
2933
|
break;
|
|
570
2934
|
}
|
|
571
2935
|
case "tool_result": {
|
|
572
2936
|
const resultToolName = log.metadata?.toolName;
|
|
2937
|
+
if (SILENT_TOOL_NAMES.has(resultToolName)) break;
|
|
573
2938
|
const resultToolCallId = log.metadata?.toolCallId;
|
|
574
2939
|
const result = log.metadata?.output;
|
|
575
2940
|
const durationMs = log.durationMs;
|
|
@@ -591,7 +2956,21 @@ function useAgentChat(options) {
|
|
|
591
2956
|
};
|
|
592
2957
|
updateToolCall(completedToolCall);
|
|
593
2958
|
pendingToolCallsRef.current.delete(matchedToolCall.id);
|
|
2959
|
+
} else if (resultToolCallId) {
|
|
2960
|
+
const lateToolCall = {
|
|
2961
|
+
id: resultToolCallId,
|
|
2962
|
+
name: resultToolName || "unknown",
|
|
2963
|
+
args: {},
|
|
2964
|
+
result,
|
|
2965
|
+
status: "completed",
|
|
2966
|
+
durationMs
|
|
2967
|
+
};
|
|
2968
|
+
updateToolCall(lateToolCall);
|
|
594
2969
|
}
|
|
2970
|
+
if (resultToolCallId) {
|
|
2971
|
+
firedToolResultsRef.current.add(resultToolCallId);
|
|
2972
|
+
}
|
|
2973
|
+
onToolResultRef.current?.(resultToolName, result, durationMs);
|
|
595
2974
|
break;
|
|
596
2975
|
}
|
|
597
2976
|
case "final_response": {
|
|
@@ -606,7 +2985,7 @@ function useAgentChat(options) {
|
|
|
606
2985
|
console.log("[useAgentChat] Unknown log type:", log.logType, log);
|
|
607
2986
|
}
|
|
608
2987
|
},
|
|
609
|
-
[updateStreamingMessage, updateToolCall
|
|
2988
|
+
[updateStreamingMessage, updateToolCall]
|
|
610
2989
|
);
|
|
611
2990
|
const handleClarification = useCallback(
|
|
612
2991
|
(data) => {
|
|
@@ -618,8 +2997,10 @@ function useAgentChat(options) {
|
|
|
618
2997
|
const handleComplete = useCallback(() => {
|
|
619
2998
|
console.log("[useAgentChat] handleComplete called");
|
|
620
2999
|
setIsLoading(false);
|
|
3000
|
+
setIsStreamControllerConnected(false);
|
|
621
3001
|
finalizeStreamingMessage();
|
|
622
3002
|
setCurrentExecutionId(null);
|
|
3003
|
+
streamControllerRef.current = null;
|
|
623
3004
|
}, [finalizeStreamingMessage]);
|
|
624
3005
|
const handleError = useCallback(
|
|
625
3006
|
(errorMsg) => {
|
|
@@ -631,21 +3012,184 @@ function useAgentChat(options) {
|
|
|
631
3012
|
};
|
|
632
3013
|
setError(chatError);
|
|
633
3014
|
setIsLoading(false);
|
|
3015
|
+
setIsStreamControllerConnected(false);
|
|
634
3016
|
finalizeStreamingMessage();
|
|
635
3017
|
setCurrentExecutionId(null);
|
|
3018
|
+
streamControllerRef.current = null;
|
|
636
3019
|
onError?.(chatError);
|
|
637
3020
|
},
|
|
638
3021
|
[finalizeStreamingMessage, onError]
|
|
639
3022
|
);
|
|
640
|
-
const
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
3023
|
+
const legacyExecutionId = useStreamController ? void 0 : currentExecutionId;
|
|
3024
|
+
const { isConnected: legacyIsConnected, isUsingFallback, disconnect } = useExecutionStream({
|
|
3025
|
+
executionId: legacyExecutionId ?? void 0,
|
|
3026
|
+
getStreamUrl: adapter.getStreamUrl ?? (() => ""),
|
|
3027
|
+
enabled: !!legacyExecutionId,
|
|
644
3028
|
onLog: handleStreamLog,
|
|
645
3029
|
onClarification: handleClarification,
|
|
646
3030
|
onComplete: handleComplete,
|
|
647
3031
|
onError: handleError
|
|
648
3032
|
});
|
|
3033
|
+
const pollingIntervalRef = useRef(null);
|
|
3034
|
+
const pollingExecutionIdRef = useRef(null);
|
|
3035
|
+
const stopPolling = useCallback(() => {
|
|
3036
|
+
if (pollingIntervalRef.current) {
|
|
3037
|
+
clearInterval(pollingIntervalRef.current);
|
|
3038
|
+
pollingIntervalRef.current = null;
|
|
3039
|
+
}
|
|
3040
|
+
pollingExecutionIdRef.current = null;
|
|
3041
|
+
}, []);
|
|
3042
|
+
const startPolling = useCallback((executionId) => {
|
|
3043
|
+
if (pollingExecutionIdRef.current === executionId) return;
|
|
3044
|
+
stopPolling();
|
|
3045
|
+
pollingExecutionIdRef.current = executionId;
|
|
3046
|
+
console.log("[useAgentChat] Starting polling fallback for:", executionId);
|
|
3047
|
+
const poll = async () => {
|
|
3048
|
+
if (pollingExecutionIdRef.current !== executionId) return;
|
|
3049
|
+
try {
|
|
3050
|
+
const execution = await adapter.getExecution(executionId);
|
|
3051
|
+
if (execution.status === "completed") {
|
|
3052
|
+
console.log("[useAgentChat] Polling: execution completed");
|
|
3053
|
+
if (execution.result) {
|
|
3054
|
+
streamingContentRef.current = execution.result;
|
|
3055
|
+
updateStreamingMessage(execution.result);
|
|
3056
|
+
}
|
|
3057
|
+
stopPolling();
|
|
3058
|
+
handleComplete();
|
|
3059
|
+
} else if (execution.status === "failed") {
|
|
3060
|
+
console.log("[useAgentChat] Polling: execution failed");
|
|
3061
|
+
stopPolling();
|
|
3062
|
+
handleError(execution.error || "Execution failed");
|
|
3063
|
+
}
|
|
3064
|
+
} catch (err) {
|
|
3065
|
+
console.warn("[useAgentChat] Polling error (will retry):", err);
|
|
3066
|
+
}
|
|
3067
|
+
};
|
|
3068
|
+
poll();
|
|
3069
|
+
pollingIntervalRef.current = setInterval(poll, 2e3);
|
|
3070
|
+
}, [adapter, stopPolling, updateStreamingMessage, handleComplete, handleError]);
|
|
3071
|
+
useEffect(() => {
|
|
3072
|
+
if (!useStreamController && isUsingFallback && currentExecutionId && isLoading) {
|
|
3073
|
+
console.log("[useAgentChat] SSE fallback detected, activating polling");
|
|
3074
|
+
startPolling(currentExecutionId);
|
|
3075
|
+
}
|
|
3076
|
+
}, [useStreamController, isUsingFallback, currentExecutionId, isLoading, startPolling]);
|
|
3077
|
+
const sseTimeoutRef = useRef(null);
|
|
3078
|
+
useEffect(() => {
|
|
3079
|
+
if (!useStreamController && currentExecutionId && isLoading && !legacyIsConnected && !isUsingFallback) {
|
|
3080
|
+
sseTimeoutRef.current = setTimeout(() => {
|
|
3081
|
+
if (!legacyIsConnected && currentExecutionId && isLoading) {
|
|
3082
|
+
console.log("[useAgentChat] SSE connection timeout, activating polling fallback");
|
|
3083
|
+
startPolling(currentExecutionId);
|
|
3084
|
+
}
|
|
3085
|
+
}, 1e4);
|
|
3086
|
+
}
|
|
3087
|
+
return () => {
|
|
3088
|
+
if (sseTimeoutRef.current) {
|
|
3089
|
+
clearTimeout(sseTimeoutRef.current);
|
|
3090
|
+
sseTimeoutRef.current = null;
|
|
3091
|
+
}
|
|
3092
|
+
};
|
|
3093
|
+
}, [useStreamController, currentExecutionId, isLoading, legacyIsConnected, isUsingFallback, startPolling]);
|
|
3094
|
+
useEffect(() => {
|
|
3095
|
+
if (!currentExecutionId) {
|
|
3096
|
+
stopPolling();
|
|
3097
|
+
}
|
|
3098
|
+
return () => {
|
|
3099
|
+
stopPolling();
|
|
3100
|
+
};
|
|
3101
|
+
}, [currentExecutionId, stopPolling]);
|
|
3102
|
+
const connectStreamController = useCallback(
|
|
3103
|
+
(executionId, sessionId) => {
|
|
3104
|
+
if (!adapter.createStream) return;
|
|
3105
|
+
if (streamControllerRef.current) {
|
|
3106
|
+
streamControllerRef.current.cancel();
|
|
3107
|
+
streamControllerRef.current = null;
|
|
3108
|
+
}
|
|
3109
|
+
console.log("[useAgentChat] Creating stream controller for:", executionId);
|
|
3110
|
+
const controller = adapter.createStream(executionId, sessionId);
|
|
3111
|
+
streamControllerRef.current = controller;
|
|
3112
|
+
controller.on("log", (log) => {
|
|
3113
|
+
handleExecutionLog(log);
|
|
3114
|
+
});
|
|
3115
|
+
controller.on("toolCall", (data) => {
|
|
3116
|
+
const toolCallId = data.toolCallId || `tool-${Date.now()}`;
|
|
3117
|
+
const toolName = data.toolName;
|
|
3118
|
+
if (SILENT_TOOL_NAMES.has(toolName)) return;
|
|
3119
|
+
if (!pendingToolCallsRef.current.has(toolCallId)) {
|
|
3120
|
+
const newToolCall = {
|
|
3121
|
+
id: toolCallId,
|
|
3122
|
+
name: toolName,
|
|
3123
|
+
args: data.inputs,
|
|
3124
|
+
status: "running"
|
|
3125
|
+
};
|
|
3126
|
+
pendingToolCallsRef.current.set(toolCallId, newToolCall);
|
|
3127
|
+
updateToolCall(newToolCall);
|
|
3128
|
+
onToolCallRef.current?.(toolName, data.inputs);
|
|
3129
|
+
}
|
|
3130
|
+
});
|
|
3131
|
+
controller.on("toolResult", (data) => {
|
|
3132
|
+
const toolCallId = data.toolCallId;
|
|
3133
|
+
const toolName = data.toolName;
|
|
3134
|
+
if (SILENT_TOOL_NAMES.has(toolName)) return;
|
|
3135
|
+
if (firedToolResultsRef.current.has(toolCallId)) return;
|
|
3136
|
+
const existing = pendingToolCallsRef.current.get(toolCallId);
|
|
3137
|
+
if (existing && existing.status === "completed") return;
|
|
3138
|
+
if (existing) {
|
|
3139
|
+
const completedToolCall = {
|
|
3140
|
+
...existing,
|
|
3141
|
+
result: data.result,
|
|
3142
|
+
status: "completed",
|
|
3143
|
+
durationMs: data.durationMs
|
|
3144
|
+
};
|
|
3145
|
+
updateToolCall(completedToolCall);
|
|
3146
|
+
pendingToolCallsRef.current.delete(toolCallId);
|
|
3147
|
+
} else {
|
|
3148
|
+
const lateToolCall = {
|
|
3149
|
+
id: toolCallId,
|
|
3150
|
+
name: toolName,
|
|
3151
|
+
args: {},
|
|
3152
|
+
result: data.result,
|
|
3153
|
+
status: "completed",
|
|
3154
|
+
durationMs: data.durationMs
|
|
3155
|
+
};
|
|
3156
|
+
updateToolCall(lateToolCall);
|
|
3157
|
+
}
|
|
3158
|
+
firedToolResultsRef.current.add(toolCallId);
|
|
3159
|
+
onToolResultRef.current?.(toolName, data.result, data.durationMs);
|
|
3160
|
+
});
|
|
3161
|
+
controller.on("textDelta", (data) => {
|
|
3162
|
+
streamingContentRef.current += data.delta;
|
|
3163
|
+
updateStreamingMessage(streamingContentRef.current);
|
|
3164
|
+
});
|
|
3165
|
+
controller.on("paused", (data) => {
|
|
3166
|
+
handleClarification({
|
|
3167
|
+
question: data.question,
|
|
3168
|
+
reason: data.reason,
|
|
3169
|
+
suggestedOptions: data.options
|
|
3170
|
+
});
|
|
3171
|
+
});
|
|
3172
|
+
controller.on("complete", ({ execution }) => {
|
|
3173
|
+
if (execution?.output && !streamingContentRef.current) {
|
|
3174
|
+
streamingContentRef.current = execution.output;
|
|
3175
|
+
updateStreamingMessage(execution.output);
|
|
3176
|
+
}
|
|
3177
|
+
handleComplete();
|
|
3178
|
+
});
|
|
3179
|
+
controller.on("error", (errorEvent) => {
|
|
3180
|
+
handleError(errorEvent.error);
|
|
3181
|
+
});
|
|
3182
|
+
controller.on("connectionChange", (data) => {
|
|
3183
|
+
setIsStreamControllerConnected(data.connected);
|
|
3184
|
+
});
|
|
3185
|
+
controller.connect().catch((err) => {
|
|
3186
|
+
console.error("[useAgentChat] Stream controller connect error:", err);
|
|
3187
|
+
handleError(err instanceof Error ? err.message : "Stream connection failed");
|
|
3188
|
+
});
|
|
3189
|
+
setIsStreamControllerConnected(true);
|
|
3190
|
+
},
|
|
3191
|
+
[adapter, handleExecutionLog, updateStreamingMessage, updateToolCall, handleClarification, handleComplete, handleError]
|
|
3192
|
+
);
|
|
649
3193
|
const sendMessage = useCallback(
|
|
650
3194
|
async (content) => {
|
|
651
3195
|
if (!content.trim()) return;
|
|
@@ -677,17 +3221,24 @@ function useAgentChat(options) {
|
|
|
677
3221
|
console.log("[useAgentChat] Starting execution...", { agentId, sessionId: currentSessionId });
|
|
678
3222
|
const result = await adapter.startExecution({
|
|
679
3223
|
agentId,
|
|
3224
|
+
teamId,
|
|
680
3225
|
sessionId: currentSessionId ?? void 0,
|
|
681
3226
|
content: content.trim()
|
|
682
3227
|
});
|
|
683
3228
|
console.log("[useAgentChat] Execution started:", result);
|
|
684
3229
|
if (!currentSessionId && result.sessionId) {
|
|
3230
|
+
internallyCreatedSessionRef.current = result.sessionId;
|
|
685
3231
|
setCurrentSessionId(result.sessionId);
|
|
686
3232
|
onSessionCreated?.(result.sessionId);
|
|
687
3233
|
onSessionStart?.(result.sessionId);
|
|
688
3234
|
}
|
|
689
|
-
|
|
690
|
-
|
|
3235
|
+
if (useStreamController) {
|
|
3236
|
+
connectStreamController(result.executionId, result.sessionId || currentSessionId || void 0);
|
|
3237
|
+
setCurrentExecutionId(result.executionId);
|
|
3238
|
+
} else {
|
|
3239
|
+
console.log("[useAgentChat] Setting executionId:", result.executionId);
|
|
3240
|
+
setCurrentExecutionId(result.executionId);
|
|
3241
|
+
}
|
|
691
3242
|
} catch (err) {
|
|
692
3243
|
console.error("[useAgentChat] Error starting execution:", err);
|
|
693
3244
|
const errorMessage = err instanceof Error ? err.message : "Failed to send message";
|
|
@@ -696,8 +3247,11 @@ function useAgentChat(options) {
|
|
|
696
3247
|
},
|
|
697
3248
|
[
|
|
698
3249
|
agentId,
|
|
3250
|
+
teamId,
|
|
699
3251
|
currentSessionId,
|
|
700
3252
|
adapter,
|
|
3253
|
+
useStreamController,
|
|
3254
|
+
connectStreamController,
|
|
701
3255
|
onMessage,
|
|
702
3256
|
onSessionCreated,
|
|
703
3257
|
onSessionStart,
|
|
@@ -718,8 +3272,13 @@ function useAgentChat(options) {
|
|
|
718
3272
|
await adapter.cancelExecution(currentExecutionId);
|
|
719
3273
|
} catch {
|
|
720
3274
|
}
|
|
3275
|
+
if (streamControllerRef.current) {
|
|
3276
|
+
streamControllerRef.current.cancel();
|
|
3277
|
+
streamControllerRef.current = null;
|
|
3278
|
+
}
|
|
721
3279
|
disconnect();
|
|
722
3280
|
setIsLoading(false);
|
|
3281
|
+
setIsStreamControllerConnected(false);
|
|
723
3282
|
finalizeStreamingMessage();
|
|
724
3283
|
setCurrentExecutionId(null);
|
|
725
3284
|
}
|
|
@@ -737,6 +3296,7 @@ function useAgentChat(options) {
|
|
|
737
3296
|
setMessages([]);
|
|
738
3297
|
setCurrentSessionId(null);
|
|
739
3298
|
hasLoadedInitialSession.current = false;
|
|
3299
|
+
internallyCreatedSessionRef.current = null;
|
|
740
3300
|
}, []);
|
|
741
3301
|
const loadSession = useCallback(
|
|
742
3302
|
async (sessionId) => {
|
|
@@ -753,8 +3313,67 @@ function useAgentChat(options) {
|
|
|
753
3313
|
setIsLoadingHistory(true);
|
|
754
3314
|
setError(null);
|
|
755
3315
|
try {
|
|
756
|
-
const history = await
|
|
3316
|
+
const [history, artifacts] = await Promise.all([
|
|
3317
|
+
adapter.getSessionHistory(sessionId),
|
|
3318
|
+
adapter.getSessionArtifacts?.(sessionId).catch(() => []) ?? Promise.resolve([])
|
|
3319
|
+
]);
|
|
757
3320
|
const loadedMessages = history.messages.map(historyToMessage);
|
|
3321
|
+
const imageArtifacts = artifacts.filter(
|
|
3322
|
+
(a) => a.type === "image" || a.mimeType?.startsWith("image/")
|
|
3323
|
+
);
|
|
3324
|
+
if (imageArtifacts.length > 0) {
|
|
3325
|
+
const assistantIndices = [];
|
|
3326
|
+
for (let i = 0; i < loadedMessages.length; i++) {
|
|
3327
|
+
if (loadedMessages[i].role === "assistant") {
|
|
3328
|
+
assistantIndices.push(i);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
if (assistantIndices.length > 0) {
|
|
3332
|
+
const artifactsByMsgIdx = /* @__PURE__ */ new Map();
|
|
3333
|
+
for (const artifact of imageArtifacts) {
|
|
3334
|
+
let targetIdx = assistantIndices[assistantIndices.length - 1];
|
|
3335
|
+
if (artifact.createdAt) {
|
|
3336
|
+
const artifactTime = new Date(artifact.createdAt).getTime();
|
|
3337
|
+
for (let i = assistantIndices.length - 1; i >= 0; i--) {
|
|
3338
|
+
const msgIdx = assistantIndices[i];
|
|
3339
|
+
const msg = loadedMessages[msgIdx];
|
|
3340
|
+
const msgTime = msg.timestamp ? new Date(msg.timestamp).getTime() : 0;
|
|
3341
|
+
if (msgTime <= artifactTime) {
|
|
3342
|
+
targetIdx = msgIdx;
|
|
3343
|
+
break;
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
const existing = artifactsByMsgIdx.get(targetIdx) ?? [];
|
|
3348
|
+
existing.push(artifact);
|
|
3349
|
+
artifactsByMsgIdx.set(targetIdx, existing);
|
|
3350
|
+
}
|
|
3351
|
+
for (const [msgIdx, msgArtifacts] of artifactsByMsgIdx) {
|
|
3352
|
+
const targetMsg = loadedMessages[msgIdx];
|
|
3353
|
+
const existingToolCalls = targetMsg.toolCalls ?? [];
|
|
3354
|
+
const imageToolCalls = msgArtifacts.map((artifact, i) => ({
|
|
3355
|
+
id: `history-artifact-${artifact.uuid}-${i}`,
|
|
3356
|
+
name: "generate_image",
|
|
3357
|
+
args: {},
|
|
3358
|
+
result: {
|
|
3359
|
+
success: true,
|
|
3360
|
+
images: [{ url: artifact.url }]
|
|
3361
|
+
},
|
|
3362
|
+
status: "completed"
|
|
3363
|
+
}));
|
|
3364
|
+
loadedMessages[msgIdx] = {
|
|
3365
|
+
id: targetMsg.id,
|
|
3366
|
+
role: targetMsg.role,
|
|
3367
|
+
content: targetMsg.content,
|
|
3368
|
+
timestamp: targetMsg.timestamp,
|
|
3369
|
+
isStreaming: targetMsg.isStreaming,
|
|
3370
|
+
attachments: targetMsg.attachments,
|
|
3371
|
+
thinkingBlocks: targetMsg.thinkingBlocks,
|
|
3372
|
+
toolCalls: [...existingToolCalls, ...imageToolCalls]
|
|
3373
|
+
};
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
758
3377
|
setMessages(loadedMessages);
|
|
759
3378
|
setCurrentSessionId(sessionId);
|
|
760
3379
|
} catch (err) {
|
|
@@ -774,14 +3393,28 @@ function useAgentChat(options) {
|
|
|
774
3393
|
);
|
|
775
3394
|
useEffect(() => {
|
|
776
3395
|
if (initialSessionId && !hasLoadedInitialSession.current) {
|
|
3396
|
+
if (internallyCreatedSessionRef.current === initialSessionId) {
|
|
3397
|
+
hasLoadedInitialSession.current = true;
|
|
3398
|
+
return;
|
|
3399
|
+
}
|
|
777
3400
|
hasLoadedInitialSession.current = true;
|
|
778
3401
|
loadSession(initialSessionId);
|
|
779
3402
|
}
|
|
780
3403
|
}, [initialSessionId, loadSession]);
|
|
781
|
-
|
|
782
|
-
() =>
|
|
783
|
-
|
|
784
|
-
|
|
3404
|
+
useEffect(() => {
|
|
3405
|
+
return () => {
|
|
3406
|
+
if (streamControllerRef.current) {
|
|
3407
|
+
streamControllerRef.current.cancel();
|
|
3408
|
+
streamControllerRef.current = null;
|
|
3409
|
+
}
|
|
3410
|
+
};
|
|
3411
|
+
}, []);
|
|
3412
|
+
const isStreaming = useMemo(() => {
|
|
3413
|
+
if (useStreamController) {
|
|
3414
|
+
return !!currentExecutionId && isStreamControllerConnected;
|
|
3415
|
+
}
|
|
3416
|
+
return !!currentExecutionId && legacyIsConnected;
|
|
3417
|
+
}, [useStreamController, currentExecutionId, isStreamControllerConnected, legacyIsConnected]);
|
|
785
3418
|
return {
|
|
786
3419
|
messages,
|
|
787
3420
|
isLoading,
|
|
@@ -1677,8 +4310,18 @@ function AskUserPrompt({
|
|
|
1677
4310
|
function cn5(...classes) {
|
|
1678
4311
|
return classes.filter(Boolean).join(" ");
|
|
1679
4312
|
}
|
|
1680
|
-
function MarkdownContent({
|
|
1681
|
-
|
|
4313
|
+
function MarkdownContent({
|
|
4314
|
+
content,
|
|
4315
|
+
className,
|
|
4316
|
+
components: customComponents,
|
|
4317
|
+
urlTransform,
|
|
4318
|
+
contentTransform
|
|
4319
|
+
}) {
|
|
4320
|
+
const transformedContent = useMemo(
|
|
4321
|
+
() => contentTransform ? contentTransform(content) : content,
|
|
4322
|
+
[content, contentTransform]
|
|
4323
|
+
);
|
|
4324
|
+
const defaultComponents = useMemo(
|
|
1682
4325
|
() => ({
|
|
1683
4326
|
// Headers
|
|
1684
4327
|
h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold mt-4 mb-2 first:mt-0", children }),
|
|
@@ -1768,7 +4411,19 @@ function MarkdownContent({ content, className }) {
|
|
|
1768
4411
|
}),
|
|
1769
4412
|
[]
|
|
1770
4413
|
);
|
|
1771
|
-
|
|
4414
|
+
const mergedComponents = useMemo(
|
|
4415
|
+
() => customComponents ? { ...defaultComponents, ...customComponents } : defaultComponents,
|
|
4416
|
+
[defaultComponents, customComponents]
|
|
4417
|
+
);
|
|
4418
|
+
return /* @__PURE__ */ jsx("div", { className: cn5("prose prose-sm dark:prose-invert max-w-none", className), children: /* @__PURE__ */ jsx(
|
|
4419
|
+
ReactMarkdown,
|
|
4420
|
+
{
|
|
4421
|
+
remarkPlugins: [remarkGfm],
|
|
4422
|
+
components: mergedComponents,
|
|
4423
|
+
...urlTransform ? { urlTransform } : {},
|
|
4424
|
+
children: transformedContent
|
|
4425
|
+
}
|
|
4426
|
+
) });
|
|
1772
4427
|
}
|
|
1773
4428
|
function cn6(...classes) {
|
|
1774
4429
|
return classes.filter(Boolean).join(" ");
|
|
@@ -1840,16 +4495,53 @@ function formatJsonPreview(data, maxLength = 100) {
|
|
|
1840
4495
|
return String(data);
|
|
1841
4496
|
}
|
|
1842
4497
|
}
|
|
4498
|
+
function extractImagesFromResult(result) {
|
|
4499
|
+
if (!result) return [];
|
|
4500
|
+
let data = result;
|
|
4501
|
+
if (typeof result === "string") {
|
|
4502
|
+
try {
|
|
4503
|
+
data = JSON.parse(result);
|
|
4504
|
+
} catch {
|
|
4505
|
+
if (/\.(png|jpg|jpeg|gif|webp|svg|bmp)(\?.*)?$/i.test(result) || result.startsWith("data:image/")) {
|
|
4506
|
+
return [{ url: result }];
|
|
4507
|
+
}
|
|
4508
|
+
return [];
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
if (typeof data !== "object" || data === null) return [];
|
|
4512
|
+
const obj = data;
|
|
4513
|
+
if (Array.isArray(obj.images)) {
|
|
4514
|
+
return obj.images.filter((img) => typeof img.url === "string").map((img) => ({
|
|
4515
|
+
url: img.url,
|
|
4516
|
+
thumbnailUrl: img.thumbnailUrl,
|
|
4517
|
+
prompt: img.prompt,
|
|
4518
|
+
alt: img.alt || img.prompt || void 0
|
|
4519
|
+
}));
|
|
4520
|
+
}
|
|
4521
|
+
if (typeof obj.url === "string" && /\.(png|jpg|jpeg|gif|webp|svg|bmp)(\?.*)?$/i.test(obj.url)) {
|
|
4522
|
+
return [{ url: obj.url, alt: obj.alt }];
|
|
4523
|
+
}
|
|
4524
|
+
if (typeof obj.imageUrl === "string") {
|
|
4525
|
+
return [{ url: obj.imageUrl, alt: obj.alt }];
|
|
4526
|
+
}
|
|
4527
|
+
return [];
|
|
4528
|
+
}
|
|
1843
4529
|
function ToolCallEntry({
|
|
1844
4530
|
toolCall,
|
|
1845
4531
|
className,
|
|
1846
|
-
showFullJson = true
|
|
4532
|
+
showFullJson = true,
|
|
4533
|
+
onImageClick
|
|
1847
4534
|
}) {
|
|
1848
4535
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
1849
4536
|
const formattedName = useMemo(
|
|
1850
4537
|
() => formatToolName(toolCall.name),
|
|
1851
4538
|
[toolCall.name]
|
|
1852
4539
|
);
|
|
4540
|
+
const resultImages = useMemo(
|
|
4541
|
+
() => toolCall.status === "completed" && toolCall.result ? extractImagesFromResult(toolCall.result) : [],
|
|
4542
|
+
[toolCall.status, toolCall.result]
|
|
4543
|
+
);
|
|
4544
|
+
const hasImages = resultImages.length > 0;
|
|
1853
4545
|
const statusIcon = useMemo(() => {
|
|
1854
4546
|
switch (toolCall.status) {
|
|
1855
4547
|
case "pending":
|
|
@@ -1902,6 +4594,47 @@ function ToolCallEntry({
|
|
|
1902
4594
|
]
|
|
1903
4595
|
}
|
|
1904
4596
|
),
|
|
4597
|
+
hasImages && /* @__PURE__ */ jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsx("div", { className: cn6(
|
|
4598
|
+
"grid gap-2",
|
|
4599
|
+
resultImages.length === 1 ? "grid-cols-1" : "grid-cols-2"
|
|
4600
|
+
), children: resultImages.map((img, idx) => {
|
|
4601
|
+
const altText = img.alt || img.prompt || `Generated image ${idx + 1}`;
|
|
4602
|
+
const imageElement = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4603
|
+
/* @__PURE__ */ jsx(
|
|
4604
|
+
"img",
|
|
4605
|
+
{
|
|
4606
|
+
src: img.thumbnailUrl || img.url,
|
|
4607
|
+
alt: altText,
|
|
4608
|
+
className: "w-full h-auto max-h-[300px] object-contain bg-gray-50 dark:bg-gray-900",
|
|
4609
|
+
loading: "lazy"
|
|
4610
|
+
}
|
|
4611
|
+
),
|
|
4612
|
+
img.prompt && /* @__PURE__ */ jsx("div", { className: "px-2 py-1 text-xs text-gray-500 dark:text-gray-400 truncate bg-gray-50 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700", children: img.prompt })
|
|
4613
|
+
] });
|
|
4614
|
+
if (onImageClick) {
|
|
4615
|
+
return /* @__PURE__ */ jsx(
|
|
4616
|
+
"button",
|
|
4617
|
+
{
|
|
4618
|
+
type: "button",
|
|
4619
|
+
onClick: () => onImageClick({ url: img.url, alt: altText, filename: altText }),
|
|
4620
|
+
className: "block rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:border-blue-400 dark:hover:border-blue-600 transition-colors cursor-pointer text-left",
|
|
4621
|
+
children: imageElement
|
|
4622
|
+
},
|
|
4623
|
+
idx
|
|
4624
|
+
);
|
|
4625
|
+
}
|
|
4626
|
+
return /* @__PURE__ */ jsx(
|
|
4627
|
+
"a",
|
|
4628
|
+
{
|
|
4629
|
+
href: img.url,
|
|
4630
|
+
target: "_blank",
|
|
4631
|
+
rel: "noopener noreferrer",
|
|
4632
|
+
className: "block rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700 hover:border-blue-400 dark:hover:border-blue-600 transition-colors",
|
|
4633
|
+
children: imageElement
|
|
4634
|
+
},
|
|
4635
|
+
idx
|
|
4636
|
+
);
|
|
4637
|
+
}) }) }),
|
|
1905
4638
|
isExpanded && /* @__PURE__ */ jsxs("div", { className: "border-t border-gray-200 dark:border-gray-700 px-3 py-2 space-y-2", children: [
|
|
1906
4639
|
toolCall.args && Object.keys(toolCall.args).length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
1907
4640
|
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-gray-500 dark:text-gray-400 mb-1", children: "Input" }),
|
|
@@ -1927,7 +4660,11 @@ function ChatMessage({
|
|
|
1927
4660
|
message,
|
|
1928
4661
|
enableMarkdown = true,
|
|
1929
4662
|
showToolCalls = true,
|
|
1930
|
-
className
|
|
4663
|
+
className,
|
|
4664
|
+
markdownComponents,
|
|
4665
|
+
markdownUrlTransform,
|
|
4666
|
+
markdownContentTransform,
|
|
4667
|
+
onImageClick
|
|
1931
4668
|
}) {
|
|
1932
4669
|
const isUser = message.role === "user";
|
|
1933
4670
|
const isAssistant = message.role === "assistant";
|
|
@@ -1987,6 +4724,14 @@ function ChatMessage({
|
|
|
1987
4724
|
},
|
|
1988
4725
|
block.id
|
|
1989
4726
|
)) }),
|
|
4727
|
+
showToolCalls && message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: message.toolCalls.map((toolCall) => /* @__PURE__ */ jsx(
|
|
4728
|
+
ToolCallEntry,
|
|
4729
|
+
{
|
|
4730
|
+
toolCall,
|
|
4731
|
+
onImageClick
|
|
4732
|
+
},
|
|
4733
|
+
toolCall.id
|
|
4734
|
+
)) }),
|
|
1990
4735
|
message.content && /* @__PURE__ */ jsx(
|
|
1991
4736
|
"div",
|
|
1992
4737
|
{
|
|
@@ -1994,16 +4739,17 @@ function ChatMessage({
|
|
|
1994
4739
|
"rounded-lg p-3",
|
|
1995
4740
|
isUser ? "bg-blue-500 text-white" : "bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
|
1996
4741
|
),
|
|
1997
|
-
children: isAssistant && enableMarkdown ? /* @__PURE__ */ jsx(
|
|
4742
|
+
children: isAssistant && enableMarkdown ? /* @__PURE__ */ jsx(
|
|
4743
|
+
MarkdownContent,
|
|
4744
|
+
{
|
|
4745
|
+
content: message.content,
|
|
4746
|
+
components: markdownComponents,
|
|
4747
|
+
urlTransform: markdownUrlTransform,
|
|
4748
|
+
contentTransform: markdownContentTransform
|
|
4749
|
+
}
|
|
4750
|
+
) : /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap", children: message.content })
|
|
1998
4751
|
}
|
|
1999
4752
|
),
|
|
2000
|
-
showToolCalls && message.toolCalls && message.toolCalls.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: message.toolCalls.map((toolCall) => /* @__PURE__ */ jsx(
|
|
2001
|
-
ToolCallEntry,
|
|
2002
|
-
{
|
|
2003
|
-
toolCall
|
|
2004
|
-
},
|
|
2005
|
-
toolCall.id
|
|
2006
|
-
)) }),
|
|
2007
4753
|
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: message.attachments.map((attachment) => /* @__PURE__ */ jsx(
|
|
2008
4754
|
"div",
|
|
2009
4755
|
{
|
|
@@ -2025,12 +4771,18 @@ function cn8(...classes) {
|
|
|
2025
4771
|
function MessageList({
|
|
2026
4772
|
messages,
|
|
2027
4773
|
welcomeMessage = "Hello! How can I help you today?",
|
|
4774
|
+
renderWelcome,
|
|
2028
4775
|
isLoading = false,
|
|
2029
4776
|
isStreaming = false,
|
|
2030
4777
|
isLoadingHistory = false,
|
|
2031
4778
|
enableMarkdown = true,
|
|
2032
4779
|
showToolCalls = true,
|
|
2033
|
-
className
|
|
4780
|
+
className,
|
|
4781
|
+
markdownComponents,
|
|
4782
|
+
markdownUrlTransform,
|
|
4783
|
+
markdownContentTransform,
|
|
4784
|
+
onImageClick,
|
|
4785
|
+
sendMessage
|
|
2034
4786
|
}) {
|
|
2035
4787
|
const scrollRef = useRef(null);
|
|
2036
4788
|
const messagesEndRef = useRef(null);
|
|
@@ -2052,13 +4804,18 @@ function MessageList({
|
|
|
2052
4804
|
/* @__PURE__ */ jsx(LoadingSpinner, { className: "h-6 w-6 mx-auto mb-2" }),
|
|
2053
4805
|
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "Loading conversation..." })
|
|
2054
4806
|
] }) }),
|
|
2055
|
-
messages.length === 0 && !isLoading && !isLoadingHistory &&
|
|
4807
|
+
messages.length === 0 && !isLoading && !isLoadingHistory && (renderWelcome ? /* @__PURE__ */ jsx(Fragment, { children: typeof renderWelcome === "function" ? renderWelcome({ sendMessage: sendMessage || (() => {
|
|
4808
|
+
}) }) : renderWelcome }) : /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full p-8", children: /* @__PURE__ */ jsx("div", { className: "text-center text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsx("p", { className: "text-lg", children: welcomeMessage }) }) })),
|
|
2056
4809
|
/* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100 dark:divide-gray-800", children: messages.map((message) => /* @__PURE__ */ jsx(
|
|
2057
4810
|
ChatMessage,
|
|
2058
4811
|
{
|
|
2059
4812
|
message,
|
|
2060
4813
|
enableMarkdown,
|
|
2061
|
-
showToolCalls
|
|
4814
|
+
showToolCalls,
|
|
4815
|
+
markdownComponents,
|
|
4816
|
+
markdownUrlTransform,
|
|
4817
|
+
markdownContentTransform,
|
|
4818
|
+
onImageClick
|
|
2062
4819
|
},
|
|
2063
4820
|
message.id
|
|
2064
4821
|
)) }),
|
|
@@ -2100,6 +4857,7 @@ function ErrorBanner({ error, onRetry, onDismiss }) {
|
|
|
2100
4857
|
}
|
|
2101
4858
|
function AgentChat({
|
|
2102
4859
|
agentId,
|
|
4860
|
+
teamId,
|
|
2103
4861
|
sessionId,
|
|
2104
4862
|
adapter,
|
|
2105
4863
|
onMessage,
|
|
@@ -2107,16 +4865,22 @@ function AgentChat({
|
|
|
2107
4865
|
onSessionCreated,
|
|
2108
4866
|
onSessionStart,
|
|
2109
4867
|
onToolCall,
|
|
4868
|
+
onToolResult,
|
|
2110
4869
|
onClarification,
|
|
2111
4870
|
initialMessages = [],
|
|
2112
4871
|
placeholder = "Type a message...",
|
|
2113
4872
|
welcomeMessage = "Hello! How can I help you today?",
|
|
4873
|
+
renderWelcome,
|
|
2114
4874
|
maxHeight = "600px",
|
|
2115
4875
|
className,
|
|
2116
4876
|
theme = "system",
|
|
2117
4877
|
enableAttachments = false,
|
|
2118
4878
|
enableMarkdown = true,
|
|
2119
|
-
showToolCalls = true
|
|
4879
|
+
showToolCalls = true,
|
|
4880
|
+
markdownComponents,
|
|
4881
|
+
markdownUrlTransform,
|
|
4882
|
+
markdownContentTransform,
|
|
4883
|
+
onImageClick
|
|
2120
4884
|
}) {
|
|
2121
4885
|
const {
|
|
2122
4886
|
messages,
|
|
@@ -2132,6 +4896,7 @@ function AgentChat({
|
|
|
2132
4896
|
clearError
|
|
2133
4897
|
} = useAgentChat({
|
|
2134
4898
|
agentId,
|
|
4899
|
+
teamId,
|
|
2135
4900
|
sessionId,
|
|
2136
4901
|
adapter,
|
|
2137
4902
|
initialMessages,
|
|
@@ -2140,6 +4905,7 @@ function AgentChat({
|
|
|
2140
4905
|
onSessionCreated,
|
|
2141
4906
|
onSessionStart,
|
|
2142
4907
|
onToolCall,
|
|
4908
|
+
onToolResult,
|
|
2143
4909
|
onClarification
|
|
2144
4910
|
});
|
|
2145
4911
|
return /* @__PURE__ */ jsxs(ChatContainer, { maxHeight, className, theme, children: [
|
|
@@ -2148,11 +4914,17 @@ function AgentChat({
|
|
|
2148
4914
|
{
|
|
2149
4915
|
messages,
|
|
2150
4916
|
welcomeMessage,
|
|
4917
|
+
renderWelcome,
|
|
2151
4918
|
isLoading,
|
|
2152
4919
|
isStreaming,
|
|
2153
4920
|
isLoadingHistory,
|
|
2154
4921
|
enableMarkdown,
|
|
2155
|
-
showToolCalls
|
|
4922
|
+
showToolCalls,
|
|
4923
|
+
markdownComponents,
|
|
4924
|
+
markdownUrlTransform,
|
|
4925
|
+
markdownContentTransform,
|
|
4926
|
+
onImageClick,
|
|
4927
|
+
sendMessage
|
|
2156
4928
|
}
|
|
2157
4929
|
),
|
|
2158
4930
|
clarification && /* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 p-4", children: /* @__PURE__ */ jsx(
|