@cadraos/sdk 0.1.3 → 0.1.4

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