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