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