@gr33n-ai/jade-sdk-rn-client 0.1.0 → 0.1.2

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.
@@ -1,2784 +0,0 @@
1
- import { createContext, useState, useEffect, useMemo, useContext, useRef, useCallback } from 'react';
2
- import { AppState } from 'react-native';
3
- import { jsx } from 'react/jsx-runtime';
4
-
5
- var __create = Object.create;
6
- var __defProp = Object.defineProperty;
7
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
- var __getOwnPropNames = Object.getOwnPropertyNames;
9
- var __getProtoOf = Object.getPrototypeOf;
10
- var __hasOwnProp = Object.prototype.hasOwnProperty;
11
- var __esm = (fn, res) => function __init() {
12
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
- };
14
- var __commonJS = (cb, mod) => function __require() {
15
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
- };
17
- var __export = (target, all) => {
18
- for (var name in all)
19
- __defProp(target, name, { get: all[name], enumerable: true });
20
- };
21
- var __copyProps = (to, from, except, desc) => {
22
- if (from && typeof from === "object" || typeof from === "function") {
23
- for (let key of __getOwnPropNames(from))
24
- if (!__hasOwnProp.call(to, key) && key !== except)
25
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
26
- }
27
- return to;
28
- };
29
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
30
- // If the importer is in node compatibility mode or this is not an ESM
31
- // file that has been converted to a CommonJS file using a Babel-
32
- // compatible transform (i.e. "__esModule" has not been set), then set
33
- // "default" to the CommonJS "module.exports" for node compatibility.
34
- !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
35
- mod
36
- ));
37
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
-
39
- // ../../node_modules/.pnpm/react-native-sse@1.2.1/node_modules/react-native-sse/src/EventSource.js
40
- var EventSource_exports = {};
41
- __export(EventSource_exports, {
42
- default: () => EventSource_default
43
- });
44
- var XMLReadyStateMap, EventSource, EventSource_default;
45
- var init_EventSource = __esm({
46
- "../../node_modules/.pnpm/react-native-sse@1.2.1/node_modules/react-native-sse/src/EventSource.js"() {
47
- XMLReadyStateMap = [
48
- "UNSENT",
49
- "OPENED",
50
- "HEADERS_RECEIVED",
51
- "LOADING",
52
- "DONE"
53
- ];
54
- EventSource = class {
55
- ERROR = -1;
56
- CONNECTING = 0;
57
- OPEN = 1;
58
- CLOSED = 2;
59
- CRLF = "\r\n";
60
- LF = "\n";
61
- CR = "\r";
62
- constructor(url, options = {}) {
63
- this.lastEventId = null;
64
- this.status = this.CONNECTING;
65
- this.eventHandlers = {
66
- open: [],
67
- message: [],
68
- error: [],
69
- close: []
70
- };
71
- this.method = options.method || "GET";
72
- this.timeout = options.timeout ?? 0;
73
- this.timeoutBeforeConnection = options.timeoutBeforeConnection ?? 500;
74
- this.withCredentials = options.withCredentials || false;
75
- this.headers = options.headers || {};
76
- this.body = options.body || void 0;
77
- this.debug = options.debug || false;
78
- this.interval = options.pollingInterval ?? 5e3;
79
- this.lineEndingCharacter = options.lineEndingCharacter || null;
80
- this._xhr = null;
81
- this._pollTimer = null;
82
- this._lastIndexProcessed = 0;
83
- if (!url || typeof url !== "string" && typeof url.toString !== "function") {
84
- throw new SyntaxError("[EventSource] Invalid URL argument.");
85
- }
86
- if (typeof url.toString === "function") {
87
- this.url = url.toString();
88
- } else {
89
- this.url = url;
90
- }
91
- this._pollAgain(this.timeoutBeforeConnection, true);
92
- }
93
- _pollAgain(time, allowZero) {
94
- if (time > 0 || allowZero) {
95
- this._logDebug(`[EventSource] Will open new connection in ${time} ms.`);
96
- this._pollTimer = setTimeout(() => {
97
- this.open();
98
- }, time);
99
- }
100
- }
101
- open() {
102
- try {
103
- this.status = this.CONNECTING;
104
- this._lastIndexProcessed = 0;
105
- this._xhr = new XMLHttpRequest();
106
- this._xhr.open(this.method, this.url, true);
107
- if (this.withCredentials) {
108
- this._xhr.withCredentials = true;
109
- }
110
- this._xhr.setRequestHeader("Accept", "text/event-stream");
111
- this._xhr.setRequestHeader("Cache-Control", "no-cache");
112
- this._xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
113
- if (this.headers) {
114
- for (const [key, value] of Object.entries(this.headers)) {
115
- this._xhr.setRequestHeader(key, value);
116
- }
117
- }
118
- if (this.lastEventId !== null) {
119
- this._xhr.setRequestHeader("Last-Event-ID", this.lastEventId);
120
- }
121
- this._xhr.timeout = this.timeout;
122
- this._xhr.onreadystatechange = () => {
123
- if (this.status === this.CLOSED) {
124
- return;
125
- }
126
- const xhr = this._xhr;
127
- this._logDebug(`[EventSource][onreadystatechange] ReadyState: ${XMLReadyStateMap[xhr.readyState] || "Unknown"}(${xhr.readyState}), status: ${xhr.status}`);
128
- if (![XMLHttpRequest.DONE, XMLHttpRequest.LOADING].includes(xhr.readyState)) {
129
- return;
130
- }
131
- if (xhr.status >= 200 && xhr.status < 400) {
132
- if (this.status === this.CONNECTING) {
133
- this.status = this.OPEN;
134
- this.dispatch("open", { type: "open" });
135
- this._logDebug("[EventSource][onreadystatechange][OPEN] Connection opened.");
136
- }
137
- this._handleEvent(xhr.responseText || "");
138
- if (xhr.readyState === XMLHttpRequest.DONE) {
139
- this._logDebug("[EventSource][onreadystatechange][DONE] Operation done.");
140
- this._pollAgain(this.interval, false);
141
- }
142
- } else if (xhr.status !== 0) {
143
- this.status = this.ERROR;
144
- this.dispatch("error", {
145
- type: "error",
146
- message: xhr.responseText,
147
- xhrStatus: xhr.status,
148
- xhrState: xhr.readyState
149
- });
150
- if (xhr.readyState === XMLHttpRequest.DONE) {
151
- this._logDebug("[EventSource][onreadystatechange][ERROR] Response status error.");
152
- this._pollAgain(this.interval, false);
153
- }
154
- }
155
- };
156
- this._xhr.onerror = () => {
157
- if (this.status === this.CLOSED) {
158
- return;
159
- }
160
- this.status = this.ERROR;
161
- this.dispatch("error", {
162
- type: "error",
163
- message: this._xhr.responseText,
164
- xhrStatus: this._xhr.status,
165
- xhrState: this._xhr.readyState
166
- });
167
- };
168
- if (this.body) {
169
- this._xhr.send(this.body);
170
- } else {
171
- this._xhr.send();
172
- }
173
- if (this.timeout > 0) {
174
- setTimeout(() => {
175
- if (this._xhr.readyState === XMLHttpRequest.LOADING) {
176
- this.dispatch("error", { type: "timeout" });
177
- this.close();
178
- }
179
- }, this.timeout);
180
- }
181
- } catch (e) {
182
- this.status = this.ERROR;
183
- this.dispatch("error", {
184
- type: "exception",
185
- message: e.message,
186
- error: e
187
- });
188
- }
189
- }
190
- _logDebug(...msg) {
191
- if (this.debug) {
192
- console.debug(...msg);
193
- }
194
- }
195
- _handleEvent(response) {
196
- if (this.lineEndingCharacter === null) {
197
- const detectedNewlineChar = this._detectNewlineChar(response);
198
- if (detectedNewlineChar !== null) {
199
- this._logDebug(`[EventSource] Automatically detected lineEndingCharacter: ${JSON.stringify(detectedNewlineChar).slice(1, -1)}`);
200
- this.lineEndingCharacter = detectedNewlineChar;
201
- } else {
202
- console.warn("[EventSource] Unable to identify the line ending character. Ensure your server delivers a standard line ending character: \\r\\n, \\n, \\r, or specify your custom character using the 'lineEndingCharacter' option.");
203
- return;
204
- }
205
- }
206
- const indexOfDoubleNewline = this._getLastDoubleNewlineIndex(response);
207
- if (indexOfDoubleNewline <= this._lastIndexProcessed) {
208
- return;
209
- }
210
- const parts = response.substring(this._lastIndexProcessed, indexOfDoubleNewline).split(this.lineEndingCharacter);
211
- this._lastIndexProcessed = indexOfDoubleNewline;
212
- let type = void 0;
213
- let id = null;
214
- let data = [];
215
- let retry = 0;
216
- let line = "";
217
- for (let i = 0; i < parts.length; i++) {
218
- line = parts[i].trim();
219
- if (line.startsWith("event")) {
220
- type = line.replace(/event:?\s*/, "");
221
- } else if (line.startsWith("retry")) {
222
- retry = parseInt(line.replace(/retry:?\s*/, ""), 10);
223
- if (!isNaN(retry)) {
224
- this.interval = retry;
225
- }
226
- } else if (line.startsWith("data")) {
227
- data.push(line.replace(/data:?\s*/, ""));
228
- } else if (line.startsWith("id")) {
229
- id = line.replace(/id:?\s*/, "");
230
- if (id !== "") {
231
- this.lastEventId = id;
232
- } else {
233
- this.lastEventId = null;
234
- }
235
- } else if (line === "") {
236
- if (data.length > 0) {
237
- const eventType = type || "message";
238
- const event = {
239
- type: eventType,
240
- data: data.join("\n"),
241
- url: this.url,
242
- lastEventId: this.lastEventId
243
- };
244
- this.dispatch(eventType, event);
245
- data = [];
246
- type = void 0;
247
- }
248
- }
249
- }
250
- }
251
- _detectNewlineChar(response) {
252
- const supportedLineEndings = [this.CRLF, this.LF, this.CR];
253
- for (const char of supportedLineEndings) {
254
- if (response.includes(char)) {
255
- return char;
256
- }
257
- }
258
- return null;
259
- }
260
- _getLastDoubleNewlineIndex(response) {
261
- const doubleLineEndingCharacter = this.lineEndingCharacter + this.lineEndingCharacter;
262
- const lastIndex = response.lastIndexOf(doubleLineEndingCharacter);
263
- if (lastIndex === -1) {
264
- return -1;
265
- }
266
- return lastIndex + doubleLineEndingCharacter.length;
267
- }
268
- addEventListener(type, listener) {
269
- if (this.eventHandlers[type] === void 0) {
270
- this.eventHandlers[type] = [];
271
- }
272
- this.eventHandlers[type].push(listener);
273
- }
274
- removeEventListener(type, listener) {
275
- if (this.eventHandlers[type] !== void 0) {
276
- this.eventHandlers[type] = this.eventHandlers[type].filter((handler) => handler !== listener);
277
- }
278
- }
279
- removeAllEventListeners(type) {
280
- const availableTypes = Object.keys(this.eventHandlers);
281
- if (type === void 0) {
282
- for (const eventType of availableTypes) {
283
- this.eventHandlers[eventType] = [];
284
- }
285
- } else {
286
- if (!availableTypes.includes(type)) {
287
- throw Error(`[EventSource] '${type}' type is not supported event type.`);
288
- }
289
- this.eventHandlers[type] = [];
290
- }
291
- }
292
- dispatch(type, data) {
293
- const availableTypes = Object.keys(this.eventHandlers);
294
- if (!availableTypes.includes(type)) {
295
- return;
296
- }
297
- for (const handler of Object.values(this.eventHandlers[type])) {
298
- handler(data);
299
- }
300
- }
301
- close() {
302
- if (this.status !== this.CLOSED) {
303
- this.status = this.CLOSED;
304
- this.dispatch("close", { type: "close" });
305
- }
306
- clearTimeout(this._pollTimer);
307
- if (this._xhr) {
308
- this._xhr.abort();
309
- }
310
- }
311
- };
312
- EventSource_default = EventSource;
313
- }
314
- });
315
-
316
- // ../../node_modules/.pnpm/react-native-sse@1.2.1/node_modules/react-native-sse/index.js
317
- var require_react_native_sse = __commonJS({
318
- "../../node_modules/.pnpm/react-native-sse@1.2.1/node_modules/react-native-sse/index.js"(exports$1, module) {
319
- var EventSource3 = (init_EventSource(), __toCommonJS(EventSource_exports));
320
- module.exports = EventSource3;
321
- }
322
- });
323
-
324
- // ../ts-client/dist/index.js
325
- async function getBytes(stream, onChunk) {
326
- const reader = stream.getReader();
327
- let result;
328
- while (!(result = await reader.read()).done) {
329
- onChunk(result.value);
330
- }
331
- }
332
- function getLines(onLine) {
333
- let buffer;
334
- let position;
335
- let fieldLength;
336
- let discardTrailingNewline = false;
337
- return function onChunk(arr) {
338
- if (buffer === void 0) {
339
- buffer = arr;
340
- position = 0;
341
- fieldLength = -1;
342
- } else {
343
- buffer = concat(buffer, arr);
344
- }
345
- const bufLength = buffer.length;
346
- let lineStart = 0;
347
- while (position < bufLength) {
348
- if (discardTrailingNewline) {
349
- if (buffer[position] === 10) {
350
- lineStart = ++position;
351
- }
352
- discardTrailingNewline = false;
353
- }
354
- let lineEnd = -1;
355
- for (; position < bufLength && lineEnd === -1; ++position) {
356
- switch (buffer[position]) {
357
- case 58:
358
- if (fieldLength === -1) {
359
- fieldLength = position - lineStart;
360
- }
361
- break;
362
- case 13:
363
- discardTrailingNewline = true;
364
- case 10:
365
- lineEnd = position;
366
- break;
367
- }
368
- }
369
- if (lineEnd === -1) {
370
- break;
371
- }
372
- onLine(buffer.subarray(lineStart, lineEnd), fieldLength);
373
- lineStart = position;
374
- fieldLength = -1;
375
- }
376
- if (lineStart === bufLength) {
377
- buffer = void 0;
378
- } else if (lineStart !== 0) {
379
- buffer = buffer.subarray(lineStart);
380
- position -= lineStart;
381
- }
382
- };
383
- }
384
- function getMessages(onId, onRetry, onMessage) {
385
- let message = newMessage();
386
- const decoder = new TextDecoder();
387
- return function onLine(line, fieldLength) {
388
- if (line.length === 0) {
389
- onMessage === null || onMessage === void 0 ? void 0 : onMessage(message);
390
- message = newMessage();
391
- } else if (fieldLength > 0) {
392
- const field = decoder.decode(line.subarray(0, fieldLength));
393
- const valueOffset = fieldLength + (line[fieldLength + 1] === 32 ? 2 : 1);
394
- const value = decoder.decode(line.subarray(valueOffset));
395
- switch (field) {
396
- case "data":
397
- message.data = message.data ? message.data + "\n" + value : value;
398
- break;
399
- case "event":
400
- message.event = value;
401
- break;
402
- case "id":
403
- onId(message.id = value);
404
- break;
405
- case "retry":
406
- const retry = parseInt(value, 10);
407
- if (!isNaN(retry)) {
408
- onRetry(message.retry = retry);
409
- }
410
- break;
411
- }
412
- }
413
- };
414
- }
415
- function concat(a, b) {
416
- const res = new Uint8Array(a.length + b.length);
417
- res.set(a);
418
- res.set(b, a.length);
419
- return res;
420
- }
421
- function newMessage() {
422
- return {
423
- data: "",
424
- event: "",
425
- id: "",
426
- retry: void 0
427
- };
428
- }
429
- var __rest = function(s, e) {
430
- var t = {};
431
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
432
- t[p] = s[p];
433
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
434
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
435
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
436
- t[p[i]] = s[p[i]];
437
- }
438
- return t;
439
- };
440
- var EventStreamContentType = "text/event-stream";
441
- var DefaultRetryInterval = 1e3;
442
- var LastEventId = "last-event-id";
443
- function fetchEventSource(input, _a) {
444
- var { signal: inputSignal, headers: inputHeaders, onopen: inputOnOpen, onmessage, onclose, onerror, openWhenHidden, fetch: inputFetch } = _a, rest = __rest(_a, ["signal", "headers", "onopen", "onmessage", "onclose", "onerror", "openWhenHidden", "fetch"]);
445
- return new Promise((resolve, reject) => {
446
- const headers = Object.assign({}, inputHeaders);
447
- if (!headers.accept) {
448
- headers.accept = EventStreamContentType;
449
- }
450
- let curRequestController;
451
- function onVisibilityChange() {
452
- curRequestController.abort();
453
- if (!document.hidden) {
454
- create();
455
- }
456
- }
457
- if (!openWhenHidden) {
458
- document.addEventListener("visibilitychange", onVisibilityChange);
459
- }
460
- let retryInterval = DefaultRetryInterval;
461
- let retryTimer = 0;
462
- function dispose() {
463
- document.removeEventListener("visibilitychange", onVisibilityChange);
464
- window.clearTimeout(retryTimer);
465
- curRequestController.abort();
466
- }
467
- inputSignal === null || inputSignal === void 0 ? void 0 : inputSignal.addEventListener("abort", () => {
468
- dispose();
469
- resolve();
470
- });
471
- const fetch2 = inputFetch !== null && inputFetch !== void 0 ? inputFetch : window.fetch;
472
- const onopen = inputOnOpen !== null && inputOnOpen !== void 0 ? inputOnOpen : defaultOnOpen;
473
- async function create() {
474
- var _a2;
475
- curRequestController = new AbortController();
476
- try {
477
- const response = await fetch2(input, Object.assign(Object.assign({}, rest), { headers, signal: curRequestController.signal }));
478
- await onopen(response);
479
- await getBytes(response.body, getLines(getMessages((id) => {
480
- if (id) {
481
- headers[LastEventId] = id;
482
- } else {
483
- delete headers[LastEventId];
484
- }
485
- }, (retry) => {
486
- retryInterval = retry;
487
- }, onmessage)));
488
- onclose === null || onclose === void 0 ? void 0 : onclose();
489
- dispose();
490
- resolve();
491
- } catch (err) {
492
- if (!curRequestController.signal.aborted) {
493
- try {
494
- const interval = (_a2 = onerror === null || onerror === void 0 ? void 0 : onerror(err)) !== null && _a2 !== void 0 ? _a2 : retryInterval;
495
- window.clearTimeout(retryTimer);
496
- retryTimer = window.setTimeout(create, interval);
497
- } catch (innerErr) {
498
- dispose();
499
- reject(innerErr);
500
- }
501
- }
502
- }
503
- }
504
- create();
505
- });
506
- }
507
- function defaultOnOpen(response) {
508
- const contentType = response.headers.get("content-type");
509
- if (!(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith(EventStreamContentType))) {
510
- throw new Error(`Expected content-type to be ${EventStreamContentType}, Actual: ${contentType}`);
511
- }
512
- }
513
- var AgentEventEmitter = class {
514
- constructor() {
515
- this.handlers = /* @__PURE__ */ new Map();
516
- }
517
- /**
518
- * Subscribe to an event.
519
- * @returns Unsubscribe function
520
- */
521
- on(event, handler) {
522
- if (!this.handlers.has(event)) {
523
- this.handlers.set(event, /* @__PURE__ */ new Set());
524
- }
525
- this.handlers.get(event).add(handler);
526
- return () => this.off(event, handler);
527
- }
528
- /**
529
- * Unsubscribe from an event.
530
- */
531
- off(event, handler) {
532
- this.handlers.get(event)?.delete(handler);
533
- }
534
- /**
535
- * Emit an event to all handlers.
536
- */
537
- emit(event, data) {
538
- this.handlers.get(event)?.forEach((handler) => {
539
- try {
540
- handler(data);
541
- } catch (err) {
542
- console.error(`[AgentEventEmitter] Error in ${event} handler:`, err);
543
- }
544
- });
545
- }
546
- /**
547
- * Remove all event handlers.
548
- */
549
- removeAllListeners() {
550
- this.handlers.clear();
551
- }
552
- };
553
- var AgentClientError = class extends Error {
554
- constructor(message, cause) {
555
- super(message);
556
- this.cause = cause;
557
- this.name = "AgentClientError";
558
- }
559
- };
560
- var AuthenticationError = class extends AgentClientError {
561
- constructor(message = "Authentication failed") {
562
- super(message);
563
- this.name = "AuthenticationError";
564
- }
565
- };
566
- var SessionNotFoundError = class extends AgentClientError {
567
- constructor(sessionId) {
568
- super(`Session not found: ${sessionId}`);
569
- this.sessionId = sessionId;
570
- this.name = "SessionNotFoundError";
571
- }
572
- };
573
- var SkillNotFoundError = class extends AgentClientError {
574
- constructor(skillName) {
575
- super(`Skill not found: ${skillName}`);
576
- this.skillName = skillName;
577
- this.name = "SkillNotFoundError";
578
- }
579
- };
580
- var RequestError = class extends AgentClientError {
581
- constructor(message, statusCode) {
582
- super(message);
583
- this.statusCode = statusCode;
584
- this.name = "RequestError";
585
- }
586
- };
587
- var FatalSSEError = class extends Error {
588
- constructor(message) {
589
- super(message);
590
- this.name = "FatalSSEError";
591
- }
592
- };
593
- var MAX_RETRIES = 5;
594
- var INITIAL_RETRY_DELAY = 1e3;
595
- var MAX_RETRY_DELAY = 1e4;
596
- async function createSSEStream(options) {
597
- const {
598
- url,
599
- method,
600
- headers,
601
- body,
602
- emitter,
603
- signal,
604
- onOpen,
605
- onClose,
606
- lastEventId
607
- } = options;
608
- const processedIds = /* @__PURE__ */ new Set();
609
- let retryCount = 0;
610
- try {
611
- await fetchEventSource(url, {
612
- method,
613
- headers: {
614
- "Content-Type": "application/json",
615
- ...lastEventId ? { "Last-Event-ID": lastEventId } : {},
616
- ...headers
617
- },
618
- body: JSON.stringify(body),
619
- signal,
620
- async onopen(response) {
621
- if (response.ok && response.headers.get("content-type")?.includes(EventStreamContentType)) {
622
- onOpen?.();
623
- return;
624
- }
625
- if (response.status === 401 || response.status === 403) {
626
- throw new FatalSSEError(`Authentication failed: ${response.status}`);
627
- }
628
- if (response.status === 404) {
629
- throw new FatalSSEError("Session not found or not active");
630
- }
631
- if (response.status === 503) {
632
- const text = await response.text().catch(() => "Service unavailable");
633
- throw new FatalSSEError(`Server unavailable: ${text}`);
634
- }
635
- if (response.status >= 400 && response.status < 500) {
636
- const text = await response.text().catch(() => `Error ${response.status}`);
637
- throw new FatalSSEError(text);
638
- }
639
- throw new Error(`Failed to connect: ${response.status}`);
640
- },
641
- onmessage(event) {
642
- if (event.id && processedIds.has(event.id)) {
643
- return;
644
- }
645
- if (event.id) {
646
- processedIds.add(event.id);
647
- }
648
- if (!event.data) {
649
- return;
650
- }
651
- try {
652
- const data = JSON.parse(event.data);
653
- const eventType = event.event || "message";
654
- switch (eventType) {
655
- case "init":
656
- case "session":
657
- emitter.emit("session", { session_id: data.session_id });
658
- break;
659
- case "entry":
660
- emitter.emit("entry", { entry: data });
661
- break;
662
- case "stream_event":
663
- handleStreamEvent(data, emitter);
664
- break;
665
- case "content_block_start":
666
- emitter.emit("content_block_start", data);
667
- break;
668
- case "content_block_delta":
669
- emitter.emit("content_block_delta", data);
670
- break;
671
- case "content_block_stop":
672
- emitter.emit("content_block_stop", data);
673
- break;
674
- case "complete":
675
- emitter.emit("complete", data);
676
- break;
677
- case "error":
678
- emitter.emit("error", { error: data.error });
679
- break;
680
- case "heartbeat":
681
- emitter.emit("heartbeat", {});
682
- break;
683
- default:
684
- break;
685
- }
686
- } catch (e) {
687
- console.error("[SSE] Failed to parse event:", event.data, e);
688
- }
689
- },
690
- onerror(err) {
691
- if (signal?.aborted) {
692
- throw err;
693
- }
694
- if (err instanceof FatalSSEError) {
695
- emitter.emit("error", { error: err.message });
696
- throw err;
697
- }
698
- retryCount++;
699
- if (retryCount > MAX_RETRIES) {
700
- const error = `Connection failed after ${MAX_RETRIES} retries`;
701
- emitter.emit("error", { error });
702
- throw new FatalSSEError(error);
703
- }
704
- const delay = Math.min(INITIAL_RETRY_DELAY * Math.pow(2, retryCount - 1), MAX_RETRY_DELAY);
705
- console.log(`[SSE] Retry ${retryCount}/${MAX_RETRIES} in ${delay}ms`);
706
- return delay;
707
- },
708
- onclose() {
709
- onClose?.();
710
- },
711
- // Keep connection open when tab is hidden - heartbeats keep it alive
712
- openWhenHidden: true
713
- });
714
- } catch (error) {
715
- if (error instanceof FatalSSEError) {
716
- if (error.message.includes("Authentication")) {
717
- throw new AuthenticationError(error.message);
718
- }
719
- throw new RequestError(error.message, 500);
720
- }
721
- if (error instanceof Error) {
722
- emitter.emit("error", { error: error.message });
723
- }
724
- throw error;
725
- }
726
- }
727
- function handleStreamEvent(data, emitter) {
728
- const streamEvent = data.event || data;
729
- const eventData = streamEvent;
730
- switch (eventData.type) {
731
- case "content_block_start":
732
- emitter.emit("content_block_start", eventData);
733
- break;
734
- case "content_block_delta":
735
- emitter.emit("content_block_delta", eventData);
736
- break;
737
- case "content_block_stop":
738
- emitter.emit("content_block_stop", eventData);
739
- break;
740
- }
741
- }
742
- var AgentClient = class {
743
- constructor(config) {
744
- this.activeStreams = /* @__PURE__ */ new Map();
745
- this.config = {
746
- apiVersion: "/v1",
747
- timeout: 3e4,
748
- ...config
749
- };
750
- }
751
- // ===========================================================================
752
- // Public getters
753
- // ===========================================================================
754
- /** Whether org context is configured (orgId is set) */
755
- get hasOrgContext() {
756
- return !!this.config.orgId;
757
- }
758
- // ===========================================================================
759
- // Private helpers
760
- // ===========================================================================
761
- get baseUrl() {
762
- return `${this.config.endpoint}${this.config.apiVersion}`;
763
- }
764
- async getHeaders() {
765
- const headers = {
766
- "Content-Type": "application/json"
767
- };
768
- const token = await this.config.getAuthToken();
769
- if (token) {
770
- headers["Authorization"] = `Bearer ${token}`;
771
- }
772
- return headers;
773
- }
774
- async request(path, options = {}) {
775
- const url = `${this.baseUrl}${path}`;
776
- const headers = await this.getHeaders();
777
- const controller = new AbortController();
778
- const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
779
- try {
780
- const response = await fetch(url, {
781
- ...options,
782
- headers: { ...headers, ...options.headers || {} },
783
- signal: controller.signal
784
- });
785
- if (!response.ok) {
786
- await this.handleErrorResponse(response, path);
787
- }
788
- return response.json();
789
- } finally {
790
- clearTimeout(timeoutId);
791
- }
792
- }
793
- async handleErrorResponse(response, path) {
794
- if (response.status === 401 || response.status === 403) {
795
- throw new AuthenticationError();
796
- }
797
- if (response.status === 404) {
798
- const text = await response.text();
799
- if (text.toLowerCase().includes("session")) {
800
- const sessionId = path.split("/").pop() || "";
801
- throw new SessionNotFoundError(sessionId);
802
- }
803
- if (text.toLowerCase().includes("skill")) {
804
- const skillName = path.split("/").pop() || "";
805
- throw new SkillNotFoundError(skillName);
806
- }
807
- }
808
- throw new RequestError(`Request failed: ${response.status}`, response.status);
809
- }
810
- // ===========================================================================
811
- // Messages API
812
- // ===========================================================================
813
- /**
814
- * Send a message and stream responses.
815
- * @returns Object with event emitter and abort function
816
- */
817
- stream(request) {
818
- const emitter = new AgentEventEmitter();
819
- const controller = new AbortController();
820
- const streamId = request.session_id || `stream-${Date.now()}`;
821
- this.activeStreams.set(streamId, controller);
822
- (async () => {
823
- try {
824
- const headers = await this.getHeaders();
825
- await createSSEStream({
826
- url: `${this.baseUrl}/messages`,
827
- method: "POST",
828
- headers,
829
- body: { ...request, stream: true },
830
- emitter,
831
- signal: controller.signal
832
- });
833
- } catch (error) {
834
- emitter.emit("error", {
835
- error: error instanceof Error ? error.message : "Unknown error"
836
- });
837
- } finally {
838
- this.activeStreams.delete(streamId);
839
- }
840
- })();
841
- return {
842
- emitter,
843
- abort: () => controller.abort()
844
- };
845
- }
846
- // ===========================================================================
847
- // Sessions API
848
- // ===========================================================================
849
- /**
850
- * List all sessions for the authenticated user.
851
- */
852
- async listSessions() {
853
- return this.request("/sessions");
854
- }
855
- /**
856
- * Get session content in display format.
857
- */
858
- async getSession(sessionId) {
859
- return this.request(`/sessions/${sessionId}`);
860
- }
861
- /**
862
- * Get session content as raw JSONL.
863
- */
864
- async getSessionRaw(sessionId) {
865
- return this.request(`/sessions/${sessionId}?format=raw`);
866
- }
867
- /**
868
- * Check if a session is actively streaming.
869
- */
870
- async getSessionStatus(sessionId) {
871
- return this.request(`/sessions/${sessionId}/status`);
872
- }
873
- /**
874
- * Update session metadata (name).
875
- */
876
- async updateSession(sessionId, updates) {
877
- return this.request(`/sessions/${sessionId}`, {
878
- method: "PATCH",
879
- body: JSON.stringify(updates)
880
- });
881
- }
882
- /**
883
- * Delete a session.
884
- */
885
- async deleteSession(sessionId) {
886
- return this.request(`/sessions/${sessionId}`, {
887
- method: "DELETE"
888
- });
889
- }
890
- /**
891
- * Cancel an active session query.
892
- */
893
- async cancelSession(sessionId) {
894
- return this.request(`/sessions/${sessionId}/cancel`, {
895
- method: "POST"
896
- });
897
- }
898
- /**
899
- * Reconnect to an active streaming session.
900
- * @returns Object with event emitter and abort function
901
- */
902
- reconnect(sessionId, lastEventId = -1) {
903
- const emitter = new AgentEventEmitter();
904
- const controller = new AbortController();
905
- (async () => {
906
- try {
907
- const headers = await this.getHeaders();
908
- await createSSEStream({
909
- url: `${this.baseUrl}/sessions/${sessionId}/reconnect`,
910
- method: "POST",
911
- headers,
912
- body: { last_event_id: lastEventId },
913
- emitter,
914
- signal: controller.signal
915
- });
916
- } catch (error) {
917
- emitter.emit("error", {
918
- error: error instanceof Error ? error.message : "Unknown error"
919
- });
920
- }
921
- })();
922
- return {
923
- emitter,
924
- abort: () => controller.abort()
925
- };
926
- }
927
- // ===========================================================================
928
- // Skills API - Personal
929
- // ===========================================================================
930
- /**
931
- * List personal skills.
932
- */
933
- async listSkills() {
934
- return this.request("/skills");
935
- }
936
- /**
937
- * Get personal skill content.
938
- */
939
- async getSkill(name) {
940
- const response = await this.request(`/skills/${name}`);
941
- return this.decodeBase64(response.content);
942
- }
943
- /**
944
- * Create or update a personal skill.
945
- */
946
- async saveSkill(skill) {
947
- const content = this.encodeBase64(skill.content);
948
- return this.request("/skills", {
949
- method: "POST",
950
- body: JSON.stringify({ ...skill, content })
951
- });
952
- }
953
- /**
954
- * Delete a personal skill.
955
- */
956
- async deleteSkill(name) {
957
- return this.request(`/skills/${name}`, {
958
- method: "DELETE"
959
- });
960
- }
961
- // ===========================================================================
962
- // Skills API - Organization
963
- // ===========================================================================
964
- /**
965
- * List organization skills.
966
- */
967
- async listOrgSkills() {
968
- return this.request("/skills/org");
969
- }
970
- /**
971
- * Get organization skill content.
972
- */
973
- async getOrgSkill(name) {
974
- const response = await this.request(
975
- `/skills/org/${name}`
976
- );
977
- return this.decodeBase64(response.content);
978
- }
979
- /**
980
- * Create or update an organization skill.
981
- */
982
- async saveOrgSkill(skill) {
983
- const content = this.encodeBase64(skill.content);
984
- return this.request("/skills/org", {
985
- method: "POST",
986
- body: JSON.stringify({ ...skill, content })
987
- });
988
- }
989
- /**
990
- * Delete an organization skill.
991
- */
992
- async deleteOrgSkill(name) {
993
- return this.request(`/skills/org/${name}`, {
994
- method: "DELETE"
995
- });
996
- }
997
- // ===========================================================================
998
- // Plugins API
999
- // ===========================================================================
1000
- /**
1001
- * Get plugin manifest for client sync.
1002
- */
1003
- async getPluginManifest() {
1004
- return this.request("/plugins/manifest");
1005
- }
1006
- /**
1007
- * Download plugin bundle.
1008
- */
1009
- async getPluginBundle(skills) {
1010
- const query = skills?.length ? `?skills=${skills.join(",")}` : "";
1011
- return this.request(`/plugins/bundle${query}`);
1012
- }
1013
- // ===========================================================================
1014
- // Utility methods
1015
- // ===========================================================================
1016
- encodeBase64(data) {
1017
- if (typeof data === "string") {
1018
- const encoder = new TextEncoder();
1019
- data = encoder.encode(data);
1020
- }
1021
- return btoa(String.fromCharCode(...data));
1022
- }
1023
- decodeBase64(base64) {
1024
- return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
1025
- }
1026
- };
1027
- function parseToolResultContent(content) {
1028
- try {
1029
- const pythonMatch = content.match(/\[\{'type':\s*'text',\s*'text':\s*'([\s\S]+)'\}\]$/);
1030
- if (pythonMatch) {
1031
- const innerText = pythonMatch[1].replace(/\\n/g, "\n").replace(/\\"/g, '"');
1032
- const parsed2 = JSON.parse(innerText);
1033
- if (parsed2 && typeof parsed2 === "object" && !Array.isArray(parsed2)) {
1034
- return parsed2;
1035
- }
1036
- }
1037
- const parsed = JSON.parse(content);
1038
- if (Array.isArray(parsed) && parsed.length > 0 && parsed[0].type === "text" && parsed[0].text) {
1039
- const unescapedText = parsed[0].text.replace(/\\n/g, "\n").replace(/\\"/g, '"');
1040
- const innerParsed = JSON.parse(unescapedText);
1041
- if (innerParsed && typeof innerParsed === "object" && !Array.isArray(innerParsed)) {
1042
- return innerParsed;
1043
- }
1044
- }
1045
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1046
- return parsed;
1047
- }
1048
- } catch {
1049
- }
1050
- const jadeUrlMatch = content.match(/https?:\/\/jade\.gr33n\.ai\/media\/([a-zA-Z0-9_-]+)/);
1051
- const falUrl = extractFalMediaUrl(content);
1052
- if (jadeUrlMatch || falUrl) {
1053
- const request_id = jadeUrlMatch ? jadeUrlMatch[1] : "unknown";
1054
- const output_cdn_urls = falUrl ? [falUrl] : [];
1055
- return {
1056
- request_id,
1057
- output_cdn_urls,
1058
- status: "completed"
1059
- };
1060
- }
1061
- return null;
1062
- }
1063
- function extractFalMediaUrl(content) {
1064
- const urlMatch = content.match(/https?:\/\/[^\/]*fal\.media\/files\/[^\s"')]+/);
1065
- return urlMatch ? urlMatch[0] : null;
1066
- }
1067
- function extractMediaInfoFromToolResult(content) {
1068
- const parsed = parseToolResultContent(content);
1069
- if (!parsed) return null;
1070
- const urls = parsed.output_cdn_urls || parsed.urls;
1071
- if (!parsed.request_id || typeof parsed.request_id !== "string" || !Array.isArray(urls) || urls.length === 0) {
1072
- return null;
1073
- }
1074
- return {
1075
- requestId: parsed.request_id,
1076
- urls,
1077
- fileType: parsed.file_type,
1078
- metadata: parsed.metadata,
1079
- originalSource: parsed.original_source
1080
- };
1081
- }
1082
- function createMediaParseResult(displayType) {
1083
- return (content) => {
1084
- const mediaInfo = extractMediaInfoFromToolResult(content);
1085
- if (mediaInfo) {
1086
- return {
1087
- type: displayType,
1088
- data: { mediaInfo }
1089
- };
1090
- }
1091
- return null;
1092
- };
1093
- }
1094
- function extractMediaInfo(content) {
1095
- try {
1096
- const parsed = parseToolResultContent(content);
1097
- if (parsed?.request_id) {
1098
- const urls2 = parsed.output_cdn_urls || parsed.urls;
1099
- if (Array.isArray(urls2) && urls2.length > 0) {
1100
- return {
1101
- requestId: parsed.request_id,
1102
- urls: urls2
1103
- };
1104
- }
1105
- }
1106
- } catch {
1107
- }
1108
- const jadeUrlMatch = content.match(/https?:\/\/jade\.gr33n\.ai\/media\/([a-zA-Z0-9_-]+)/);
1109
- if (!jadeUrlMatch) return null;
1110
- const requestId = jadeUrlMatch[1];
1111
- const cdnUrlMatches = content.matchAll(/https?:\/\/[^\/]*fal\.media\/files\/[^\s"')]+/g);
1112
- const urls = Array.from(cdnUrlMatches, (match) => match[0]);
1113
- if (urls.length === 0) return null;
1114
- return {
1115
- requestId,
1116
- urls
1117
- };
1118
- }
1119
- var TOOL_REGISTRY = {
1120
- "Skill": {
1121
- name: "Skill",
1122
- displayName: "Skill",
1123
- iconName: "BookOpenText",
1124
- category: "system",
1125
- consumesResult: true,
1126
- parseInput: (input) => {
1127
- const inputObj = input;
1128
- const command = inputObj?.skill || inputObj?.command;
1129
- if (!command) return null;
1130
- return {
1131
- type: "skill",
1132
- data: { command }
1133
- };
1134
- },
1135
- parseResult: (content) => ({
1136
- type: "skill_result",
1137
- data: { content }
1138
- })
1139
- },
1140
- "Bash": {
1141
- name: "Bash",
1142
- displayName: "Bash",
1143
- iconName: "Terminal",
1144
- category: "system",
1145
- consumesResult: true,
1146
- parseInput: (input) => {
1147
- const inputObj = input;
1148
- return {
1149
- type: "bash",
1150
- data: {
1151
- command: inputObj?.command,
1152
- description: inputObj?.description
1153
- }
1154
- };
1155
- },
1156
- parseResult: (content) => ({
1157
- type: "bash_output",
1158
- data: { output: content }
1159
- })
1160
- },
1161
- "TodoWrite": {
1162
- name: "TodoWrite",
1163
- displayName: "Todo List",
1164
- iconName: "ListTodo",
1165
- category: "system",
1166
- consumesResult: true,
1167
- parseInput: (input) => {
1168
- const inputObj = input;
1169
- if (!inputObj?.todos) return null;
1170
- return {
1171
- type: "todo_list",
1172
- data: { todos: inputObj.todos }
1173
- };
1174
- },
1175
- parseResult: (content) => ({
1176
- type: "todo_result",
1177
- data: { content }
1178
- })
1179
- },
1180
- "Read": {
1181
- name: "Read",
1182
- displayName: "Read",
1183
- iconName: "BookOpenCheck",
1184
- category: "system",
1185
- consumesResult: true,
1186
- parseInput: (input) => {
1187
- const inputObj = input;
1188
- if (!inputObj?.file_path) return null;
1189
- return {
1190
- type: "read",
1191
- data: {
1192
- file_path: inputObj.file_path,
1193
- offset: inputObj.offset,
1194
- limit: inputObj.limit
1195
- }
1196
- };
1197
- },
1198
- parseResult: (content) => ({
1199
- type: "read_output",
1200
- data: { output: content }
1201
- })
1202
- },
1203
- "Glob": {
1204
- name: "Glob",
1205
- displayName: "File Search",
1206
- iconName: "FolderSearch",
1207
- category: "system",
1208
- consumesResult: true,
1209
- parseInput: (input) => {
1210
- const inputObj = input;
1211
- if (!inputObj?.pattern) return null;
1212
- return {
1213
- type: "glob",
1214
- data: {
1215
- pattern: inputObj.pattern,
1216
- path: inputObj.path
1217
- }
1218
- };
1219
- },
1220
- parseResult: (content) => {
1221
- const files = content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
1222
- return {
1223
- type: "glob_output",
1224
- data: { files }
1225
- };
1226
- }
1227
- },
1228
- "WebSearch": {
1229
- name: "WebSearch",
1230
- displayName: "Web Search",
1231
- iconName: "Search",
1232
- category: "system",
1233
- consumesResult: true,
1234
- parseInput: (input) => {
1235
- const inputObj = input;
1236
- if (!inputObj?.query) return null;
1237
- return {
1238
- type: "web_search",
1239
- data: {
1240
- query: inputObj.query,
1241
- allowed_domains: inputObj.allowed_domains,
1242
- blocked_domains: inputObj.blocked_domains
1243
- }
1244
- };
1245
- },
1246
- parseResult: (content) => ({
1247
- type: "web_search_output",
1248
- data: { output: content }
1249
- })
1250
- },
1251
- "Edit": {
1252
- name: "Edit",
1253
- displayName: "Edit File",
1254
- iconName: "Pencil",
1255
- category: "system",
1256
- consumesResult: true,
1257
- parseInput: (input) => {
1258
- const inputObj = input;
1259
- if (!inputObj?.file_path) return null;
1260
- return {
1261
- type: "edit",
1262
- data: {
1263
- file_path: inputObj.file_path,
1264
- old_string: inputObj.old_string,
1265
- new_string: inputObj.new_string,
1266
- replace_all: inputObj.replace_all
1267
- }
1268
- };
1269
- },
1270
- parseResult: (content) => ({
1271
- type: "edit_result",
1272
- data: { output: content }
1273
- })
1274
- },
1275
- "Write": {
1276
- name: "Write",
1277
- displayName: "Write File",
1278
- iconName: "FilePlus",
1279
- category: "system",
1280
- consumesResult: true,
1281
- parseInput: (input) => {
1282
- const inputObj = input;
1283
- if (!inputObj?.file_path) return null;
1284
- return {
1285
- type: "write",
1286
- data: {
1287
- file_path: inputObj.file_path,
1288
- content: inputObj.content
1289
- }
1290
- };
1291
- },
1292
- parseResult: (content) => ({
1293
- type: "write_result",
1294
- data: { output: content }
1295
- })
1296
- },
1297
- "WebFetch": {
1298
- name: "WebFetch",
1299
- displayName: "Web Fetch",
1300
- iconName: "Globe",
1301
- category: "system",
1302
- consumesResult: true,
1303
- parseInput: (input) => {
1304
- const inputObj = input;
1305
- if (!inputObj?.url) return null;
1306
- return {
1307
- type: "web_fetch",
1308
- data: {
1309
- url: inputObj.url,
1310
- prompt: inputObj.prompt
1311
- }
1312
- };
1313
- },
1314
- parseResult: (content) => ({
1315
- type: "web_fetch_result",
1316
- data: { response: content }
1317
- })
1318
- },
1319
- "mcp__jade__request_status": {
1320
- name: "mcp__jade__request_status",
1321
- displayName: "Request Status",
1322
- iconName: "Loader",
1323
- category: "utility",
1324
- consumesResult: true,
1325
- parseInput: (input) => {
1326
- const inputObj = input;
1327
- return {
1328
- type: "request_status_check",
1329
- data: { requestId: inputObj?.request_id }
1330
- };
1331
- },
1332
- parseResult: (content) => {
1333
- const mediaInfo = extractMediaInfoFromToolResult(content);
1334
- if (mediaInfo) {
1335
- return {
1336
- type: "request_status_completed",
1337
- data: { mediaInfo }
1338
- };
1339
- }
1340
- try {
1341
- const parsed = parseToolResultContent(content);
1342
- if (parsed?.request_id && parsed?.status === "processing") {
1343
- return {
1344
- type: "request_status_processing",
1345
- data: { requestId: parsed.request_id }
1346
- };
1347
- }
1348
- } catch {
1349
- }
1350
- return null;
1351
- }
1352
- },
1353
- "mcp__jade__generative_audio": {
1354
- name: "mcp__jade__generative_audio",
1355
- displayName: "Generative Audio",
1356
- iconName: "AudioLines",
1357
- iconVariant: (input) => {
1358
- const inputObj = input;
1359
- const mode = inputObj?.mode;
1360
- if (mode === "narration") return "Speech";
1361
- if (mode === "sound_effects") return "AudioLines";
1362
- if (mode === "music") return "Music";
1363
- return "AudioLines";
1364
- },
1365
- category: "generation",
1366
- consumesResult: true,
1367
- parseInput: (input) => {
1368
- const inputObj = input;
1369
- return {
1370
- type: "audio_generation",
1371
- data: {
1372
- textInput: inputObj?.text_input,
1373
- videoUrl: inputObj?.video_url,
1374
- mode: inputObj?.mode,
1375
- duration: inputObj?.duration
1376
- }
1377
- };
1378
- },
1379
- parseResult: createMediaParseResult("audio_generation_complete")
1380
- },
1381
- "mcp__jade__generative_image": {
1382
- name: "mcp__jade__generative_image",
1383
- displayName: "Generative Image",
1384
- iconName: "Image",
1385
- category: "generation",
1386
- consumesResult: true,
1387
- parseInput: (input) => {
1388
- const inputObj = input;
1389
- const additionalUrls = inputObj?.additional_image_urls ? typeof inputObj.additional_image_urls === "string" ? inputObj.additional_image_urls.split(",").map((url) => url.trim()).filter(Boolean) : inputObj.additional_image_urls : [];
1390
- const inputImageUrls = [
1391
- inputObj?.image_url,
1392
- ...additionalUrls
1393
- ].filter((url) => url && url !== "");
1394
- return {
1395
- type: "image_generation",
1396
- data: {
1397
- prompt: inputObj?.prompt,
1398
- inputImageUrls,
1399
- model: inputObj?.model,
1400
- aspectRatio: inputObj?.aspect_ratio,
1401
- numImages: inputObj?.num_images
1402
- }
1403
- };
1404
- },
1405
- parseResult: createMediaParseResult("image_generation_complete")
1406
- },
1407
- "mcp__jade__generative_video": {
1408
- name: "mcp__jade__generative_video",
1409
- displayName: "Generative Video",
1410
- iconName: "Video",
1411
- category: "generation",
1412
- consumesResult: true,
1413
- parseInput: (input) => {
1414
- const inputObj = input;
1415
- const inputMediaUrls = [];
1416
- if (inputObj?.input_url) {
1417
- inputMediaUrls.push(inputObj.input_url);
1418
- }
1419
- const lastFrameUrl = inputObj?.last_frame_url;
1420
- if (lastFrameUrl) {
1421
- inputMediaUrls.push(lastFrameUrl);
1422
- }
1423
- const elements = inputObj?.elements;
1424
- if (elements && Array.isArray(elements)) {
1425
- for (const element of elements) {
1426
- if (Array.isArray(element)) {
1427
- for (const url of element) {
1428
- if (url && typeof url === "string") {
1429
- inputMediaUrls.push(url);
1430
- }
1431
- }
1432
- }
1433
- }
1434
- }
1435
- return {
1436
- type: "video_generation",
1437
- data: {
1438
- prompt: inputObj?.prompt,
1439
- inputMediaUrls,
1440
- lastFrameUrl,
1441
- elements,
1442
- model: inputObj?.model_id,
1443
- aspectRatio: inputObj?.aspect_ratio,
1444
- numVideos: inputObj?.num_videos || 1
1445
- }
1446
- };
1447
- },
1448
- parseResult: createMediaParseResult("video_generation_complete")
1449
- },
1450
- "mcp__jade__background_removal": {
1451
- name: "mcp__jade__background_removal",
1452
- displayName: "Background Removal",
1453
- iconName: "ImageOff",
1454
- category: "processing",
1455
- consumesResult: true,
1456
- parseInput: (input) => {
1457
- const inputObj = input;
1458
- return {
1459
- type: "background_removal",
1460
- data: { inputUrl: inputObj?.input_url }
1461
- };
1462
- },
1463
- parseResult: createMediaParseResult("background_removal_complete")
1464
- },
1465
- "mcp__jade__upscale": {
1466
- name: "mcp__jade__upscale",
1467
- displayName: "Upscale",
1468
- iconName: "Maximize2",
1469
- category: "processing",
1470
- consumesResult: true,
1471
- parseInput: (input) => {
1472
- const inputObj = input;
1473
- return {
1474
- type: "upscale",
1475
- data: {
1476
- inputUrl: inputObj?.input_url,
1477
- upscaleFactor: inputObj?.upscale_factor,
1478
- targetResolution: inputObj?.target_resolution
1479
- }
1480
- };
1481
- },
1482
- parseResult: createMediaParseResult("upscale_complete")
1483
- },
1484
- "mcp__jade__generative_character": {
1485
- name: "mcp__jade__generative_character",
1486
- displayName: "Generative Character",
1487
- iconName: "SquareUser",
1488
- category: "generation",
1489
- consumesResult: true,
1490
- parseInput: (input) => {
1491
- const inputObj = input;
1492
- return {
1493
- type: "character_generation",
1494
- data: {
1495
- inputImageUrl: inputObj?.image_url,
1496
- inputAudioUrl: inputObj?.audio_url
1497
- }
1498
- };
1499
- },
1500
- parseResult: createMediaParseResult("character_generation_complete")
1501
- },
1502
- "mcp__jade__import_media": {
1503
- name: "mcp__jade__import_media",
1504
- displayName: "Import Media",
1505
- iconName: "CloudDownload",
1506
- category: "utility",
1507
- consumesResult: true,
1508
- parseInput: (input) => {
1509
- const inputObj = input;
1510
- return {
1511
- type: "media_import",
1512
- data: { source: inputObj?.source }
1513
- };
1514
- },
1515
- parseResult: createMediaParseResult("media_import_complete")
1516
- },
1517
- "mcp__jade__captions_highlights": {
1518
- name: "mcp__jade__captions_highlights",
1519
- displayName: "Captions & Highlights",
1520
- iconName: "Captions",
1521
- category: "processing",
1522
- consumesResult: true,
1523
- parseInput: (input) => {
1524
- const inputObj = input;
1525
- return {
1526
- type: "captions_highlights",
1527
- data: {
1528
- inputUrl: inputObj?.input_url,
1529
- style: inputObj?.style
1530
- }
1531
- };
1532
- },
1533
- parseResult: createMediaParseResult("captions_highlights_complete")
1534
- },
1535
- "mcp__olive__save_skill": {
1536
- name: "mcp__olive__save_skill",
1537
- displayName: "Save Skill",
1538
- iconName: "BookPlus",
1539
- category: "utility",
1540
- consumesResult: true,
1541
- parseInput: (input) => {
1542
- const inputObj = input;
1543
- return {
1544
- type: "save_skill",
1545
- data: {
1546
- source: inputObj?.source,
1547
- name: inputObj?.name,
1548
- scope: inputObj?.scope || "personal",
1549
- edit: inputObj?.edit
1550
- }
1551
- };
1552
- },
1553
- parseResult: (content) => {
1554
- const parsed = parseToolResultContent(content);
1555
- return {
1556
- type: "save_skill_result",
1557
- data: {
1558
- name: parsed?.name,
1559
- description: parsed?.description,
1560
- scope: parsed?.scope,
1561
- action: parsed?.action,
1562
- hasElements: parsed?.has_elements,
1563
- sourceType: parsed?.source_type,
1564
- sizeKb: parsed?.size_kb,
1565
- elements: parsed?.elements || []
1566
- }
1567
- };
1568
- }
1569
- }
1570
- };
1571
- function getToolDefinition(toolName) {
1572
- return TOOL_REGISTRY[toolName];
1573
- }
1574
- var SUGGESTION_REGEX = /<gr3\.suggestion>([\s\S]*?)<\/gr3\.suggestion>/g;
1575
- function parseSuggestions(text) {
1576
- const suggestions = [];
1577
- const segments = [];
1578
- let lastIndex = 0;
1579
- let cleanText = "";
1580
- let match;
1581
- const regex = new RegExp(SUGGESTION_REGEX.source, "g");
1582
- while ((match = regex.exec(text)) !== null) {
1583
- const suggestionText = match[1].trim();
1584
- const matchStart = match.index;
1585
- const matchEnd = match.index + match[0].length;
1586
- if (matchStart > lastIndex) {
1587
- const textBefore = text.slice(lastIndex, matchStart);
1588
- segments.push({ type: "text", content: textBefore });
1589
- cleanText += textBefore;
1590
- }
1591
- const startIndex = cleanText.length;
1592
- cleanText += suggestionText;
1593
- const endIndex = cleanText.length;
1594
- suggestions.push({
1595
- text: suggestionText,
1596
- startIndex,
1597
- endIndex
1598
- });
1599
- segments.push({ type: "suggestion", content: suggestionText });
1600
- lastIndex = matchEnd;
1601
- }
1602
- if (lastIndex < text.length) {
1603
- const remainingText = text.slice(lastIndex);
1604
- segments.push({ type: "text", content: remainingText });
1605
- cleanText += remainingText;
1606
- }
1607
- return { cleanText, suggestions, segments };
1608
- }
1609
- function hasSuggestions(text) {
1610
- return SUGGESTION_REGEX.test(text);
1611
- }
1612
- function processConversation(conversation, options = {}) {
1613
- const {
1614
- skipSkillContext = true,
1615
- pairToolResults = true,
1616
- extractMedia: _extractMedia = true
1617
- } = options;
1618
- const processed = [];
1619
- const consumed = /* @__PURE__ */ new Set();
1620
- const resultsByToolUseId = /* @__PURE__ */ new Map();
1621
- const mediaUrlsByRequestId = /* @__PURE__ */ new Map();
1622
- const metadataByRequestId = /* @__PURE__ */ new Map();
1623
- for (let i = 0; i < conversation.length; i++) {
1624
- const entry = conversation[i];
1625
- if (entry.type === "tool_result" && entry.tool_use_id) {
1626
- resultsByToolUseId.set(entry.tool_use_id, i);
1627
- if (typeof entry.content === "string") {
1628
- const parsed = parseToolResultContent(entry.content);
1629
- if (parsed?.request_id && parsed?.status === "completed" && Array.isArray(parsed?.output_cdn_urls) && parsed.output_cdn_urls.length > 0) {
1630
- mediaUrlsByRequestId.set(parsed.request_id, parsed.output_cdn_urls);
1631
- }
1632
- if (parsed?.request_id && parsed?.status === "completed" && Array.isArray(parsed?.urls) && parsed.urls.length > 0) {
1633
- mediaUrlsByRequestId.set(parsed.request_id, parsed.urls);
1634
- }
1635
- }
1636
- }
1637
- }
1638
- for (let i = 0; i < conversation.length; i++) {
1639
- if (consumed.has(i)) continue;
1640
- const entry = conversation[i];
1641
- if (entry.type === "tool_call" && entry.tool_name) {
1642
- const toolDef = getToolDefinition(entry.tool_name);
1643
- const parsedInput = toolDef?.parseInput?.(entry.tool_input) || null;
1644
- let parsedResult = null;
1645
- if (pairToolResults && toolDef?.consumesResult && toolDef.parseResult && entry.tool_use_id) {
1646
- const resultIndex = resultsByToolUseId.get(entry.tool_use_id);
1647
- if (resultIndex !== void 0) {
1648
- const resultEntry = conversation[resultIndex];
1649
- if (resultEntry.type === "tool_result" && typeof resultEntry.content === "string") {
1650
- parsedResult = toolDef.parseResult(resultEntry.content);
1651
- consumed.add(resultIndex);
1652
- const immediateResult = parseToolResultContent(resultEntry.content);
1653
- const requestId = immediateResult?.request_id;
1654
- if (requestId) {
1655
- const isGenerativeTool = entry.tool_name?.includes("generative_");
1656
- if (isGenerativeTool && entry.tool_use_id && parsedInput?.data) {
1657
- metadataByRequestId.set(requestId, {
1658
- toolUseId: entry.tool_use_id,
1659
- toolName: entry.tool_name,
1660
- prompt: parsedInput.data.prompt,
1661
- model: parsedInput.data.model,
1662
- aspectRatio: parsedInput.data.aspectRatio,
1663
- inputMediaUrls: parsedInput.data.inputMediaUrls || parsedInput.data.inputImageUrls,
1664
- lastFrameUrl: parsedInput.data.lastFrameUrl,
1665
- elements: parsedInput.data.elements
1666
- });
1667
- }
1668
- const eventualUrls = mediaUrlsByRequestId.get(requestId);
1669
- if (eventualUrls) {
1670
- if (!parsedResult) {
1671
- const mediaType = entry.tool_name?.includes("video") ? "video" : entry.tool_name?.includes("audio") ? "audio" : "image";
1672
- parsedResult = {
1673
- type: `${mediaType}_generation_complete`,
1674
- data: { mediaInfo: { urls: eventualUrls, requestId } }
1675
- };
1676
- } else if (!parsedResult.data?.mediaInfo) {
1677
- parsedResult.data.mediaInfo = { urls: eventualUrls, requestId };
1678
- }
1679
- }
1680
- }
1681
- if (toolDef.consumeExtra && toolDef.consumeExtra > 0) {
1682
- for (let j = 1; j <= toolDef.consumeExtra; j++) {
1683
- const extraIndex = resultIndex + j;
1684
- if (extraIndex < conversation.length) {
1685
- consumed.add(extraIndex);
1686
- }
1687
- }
1688
- }
1689
- if (parsedResult?.type === "request_status_completed") {
1690
- const mediaInfo = parsedResult.data?.mediaInfo;
1691
- if (mediaInfo?.requestId) {
1692
- const originalMetadata = metadataByRequestId.get(mediaInfo.requestId);
1693
- if (originalMetadata) {
1694
- mediaInfo.originalToolUseId = originalMetadata.toolUseId;
1695
- mediaInfo.originalToolName = originalMetadata.toolName;
1696
- mediaInfo.prompt = originalMetadata.prompt;
1697
- mediaInfo.model = originalMetadata.model;
1698
- mediaInfo.aspectRatio = originalMetadata.aspectRatio;
1699
- mediaInfo.inputMediaUrls = originalMetadata.inputMediaUrls;
1700
- mediaInfo.lastFrameUrl = originalMetadata.lastFrameUrl;
1701
- mediaInfo.elements = originalMetadata.elements;
1702
- }
1703
- }
1704
- }
1705
- }
1706
- }
1707
- }
1708
- processed.push({
1709
- entry,
1710
- originalType: "tool_call",
1711
- displayType: parsedInput?.type || "unknown_tool",
1712
- iconName: toolDef?.iconName || "Wrench",
1713
- iconData: entry.tool_input,
1714
- title: toolDef && entry.tool_name.startsWith("mcp__jade__") ? toolDef.displayName : void 0,
1715
- data: {
1716
- toolName: entry.tool_name,
1717
- toolUseId: entry.tool_use_id,
1718
- input: parsedInput,
1719
- result: parsedResult
1720
- },
1721
- timestamp: entry.timestamp,
1722
- toolDefinition: toolDef,
1723
- parsedInput: parsedInput || void 0,
1724
- parsedResult: parsedResult || void 0
1725
- });
1726
- continue;
1727
- }
1728
- if (entry.type === "tool_result" && typeof entry.content === "string") {
1729
- try {
1730
- const parsed = parseToolResultContent(entry.content);
1731
- if (parsed?.request_id && parsed?.status && parsed?.message) {
1732
- processed.push({
1733
- entry,
1734
- originalType: "tool_result",
1735
- displayType: "simple_message",
1736
- iconName: "Wrench",
1737
- data: { message: parsed.message },
1738
- timestamp: entry.timestamp
1739
- });
1740
- continue;
1741
- }
1742
- } catch {
1743
- }
1744
- const mediaInfo = extractMediaInfo(entry.content);
1745
- if (mediaInfo) {
1746
- processed.push({
1747
- entry,
1748
- originalType: "tool_result",
1749
- displayType: "media_carousel",
1750
- iconName: "Wrench",
1751
- data: {
1752
- mediaInfo: {
1753
- requestId: mediaInfo.requestId,
1754
- urls: mediaInfo.urls
1755
- }
1756
- },
1757
- timestamp: entry.timestamp
1758
- });
1759
- continue;
1760
- }
1761
- processed.push({
1762
- entry,
1763
- originalType: "tool_result",
1764
- displayType: "raw_content",
1765
- iconName: "Wrench",
1766
- data: { content: entry.content },
1767
- timestamp: entry.timestamp
1768
- });
1769
- continue;
1770
- }
1771
- if (skipSkillContext) {
1772
- if (entry.type === "user_text" && entry.text?.startsWith("Base directory for this skill:")) {
1773
- continue;
1774
- }
1775
- if (entry.type === "user_text" && entry.text?.startsWith("Caveat:")) {
1776
- continue;
1777
- }
1778
- }
1779
- if (entry.type === "user_text" && entry.text) {
1780
- const isCompactSummary = entry.text.startsWith("This session is being continued from a previous conversation");
1781
- if (isCompactSummary) {
1782
- processed.push({
1783
- entry,
1784
- originalType: "compact",
1785
- displayType: "compact",
1786
- iconName: "Sparkles",
1787
- data: {
1788
- isCompacting: false,
1789
- summary: entry.text
1790
- },
1791
- timestamp: entry.timestamp
1792
- });
1793
- continue;
1794
- }
1795
- processed.push({
1796
- entry,
1797
- originalType: "user_text",
1798
- displayType: "user_text",
1799
- iconName: "User",
1800
- data: { text: entry.text },
1801
- timestamp: entry.timestamp
1802
- });
1803
- continue;
1804
- }
1805
- if (entry.type === "assistant_text" && entry.text) {
1806
- const processedEntry = {
1807
- entry,
1808
- originalType: "assistant_text",
1809
- displayType: "markdown_text",
1810
- iconName: "Bot",
1811
- data: { text: entry.text },
1812
- timestamp: entry.timestamp
1813
- };
1814
- if (hasSuggestions(entry.text)) {
1815
- const { cleanText, suggestions, segments } = parseSuggestions(entry.text);
1816
- processedEntry.data = { text: cleanText };
1817
- processedEntry.suggestions = suggestions;
1818
- processedEntry.textSegments = segments;
1819
- }
1820
- processed.push(processedEntry);
1821
- continue;
1822
- }
1823
- if (entry.type === "compact_boundary") {
1824
- const nextEntry = conversation[i + 1];
1825
- const isNextSummary = nextEntry?.type === "compact_summary" || nextEntry?.type === "user_text" && nextEntry.text?.startsWith("This session is being continued from a previous conversation");
1826
- if (isNextSummary) {
1827
- consumed.add(i + 1);
1828
- processed.push({
1829
- entry,
1830
- originalType: "compact",
1831
- displayType: "compact",
1832
- iconName: "Sparkles",
1833
- data: {
1834
- isCompacting: false,
1835
- summary: nextEntry.text || ""
1836
- },
1837
- timestamp: entry.timestamp
1838
- });
1839
- } else {
1840
- processed.push({
1841
- entry,
1842
- originalType: "compact",
1843
- displayType: "compact",
1844
- iconName: "Sparkles",
1845
- data: {
1846
- isCompacting: true,
1847
- summary: null
1848
- },
1849
- timestamp: entry.timestamp
1850
- });
1851
- }
1852
- continue;
1853
- }
1854
- if (entry.type === "compact_summary") {
1855
- processed.push({
1856
- entry,
1857
- originalType: "compact",
1858
- displayType: "compact",
1859
- iconName: "Sparkles",
1860
- data: {
1861
- isCompacting: false,
1862
- summary: entry.text || ""
1863
- },
1864
- timestamp: entry.timestamp
1865
- });
1866
- continue;
1867
- }
1868
- if (entry.type === "error") {
1869
- processed.push({
1870
- entry,
1871
- originalType: "error",
1872
- displayType: "error",
1873
- iconName: "AlertTriangle",
1874
- data: {
1875
- text: entry.text || "Unknown error"
1876
- },
1877
- timestamp: entry.timestamp
1878
- });
1879
- continue;
1880
- }
1881
- }
1882
- return processed;
1883
- }
1884
- var ACCEPTED_EXTENSIONS = {
1885
- image: [".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp", ".svg"],
1886
- video: [".mp4", ".mov", ".webm", ".avi", ".mkv", ".m4v"],
1887
- audio: [".mp3", ".wav", ".m4a", ".aac", ".ogg", ".oga", ".mpeg"],
1888
- document: [".pdf"]
1889
- };
1890
- function getMediaTypeFromExtension(filename) {
1891
- const normalizedFilename = filename.toLowerCase();
1892
- for (const [type, extensions] of Object.entries(ACCEPTED_EXTENSIONS)) {
1893
- if (extensions.some((ext) => normalizedFilename.endsWith(ext))) {
1894
- return type;
1895
- }
1896
- }
1897
- return null;
1898
- }
1899
- function getMediaTypeFromUrl(url) {
1900
- try {
1901
- const urlObj = new URL(url);
1902
- const pathname = urlObj.pathname;
1903
- const filename = pathname.split("/").pop() || "";
1904
- return getMediaTypeFromExtension(filename);
1905
- } catch {
1906
- const cleanUrl = url.split("?")[0];
1907
- return getMediaTypeFromExtension(cleanUrl);
1908
- }
1909
- }
1910
- function extractMedia(conversation) {
1911
- const urlMap = /* @__PURE__ */ new Map();
1912
- const processed = processConversation(conversation);
1913
- for (const entry of processed) {
1914
- if (entry.originalType === "tool_call") {
1915
- const data = entry.data;
1916
- const result = data?.result;
1917
- const resultData = result?.data;
1918
- const mediaInfo = resultData?.mediaInfo;
1919
- const urls = mediaInfo?.urls;
1920
- if (urls && Array.isArray(urls)) {
1921
- const input = data?.input;
1922
- const inputData = input?.data;
1923
- const toolName = data?.toolName;
1924
- const toolUseId = data?.toolUseId;
1925
- for (const url of urls) {
1926
- if (typeof url !== "string") continue;
1927
- const detectedType = getMediaTypeFromUrl(url);
1928
- const type = detectedType === "video" ? "video" : detectedType === "audio" ? "audio" : "image";
1929
- const existing = urlMap.get(url);
1930
- const hasInputMetadata = !!(inputData?.prompt || inputData?.model);
1931
- const existingHasMetadata = !!(existing?.prompt || existing?.model);
1932
- if (!existing || hasInputMetadata && !existingHasMetadata) {
1933
- urlMap.set(url, {
1934
- url,
1935
- type,
1936
- timestamp: entry.timestamp,
1937
- toolName,
1938
- toolUseId,
1939
- prompt: inputData?.prompt,
1940
- model: inputData?.model,
1941
- aspectRatio: inputData?.aspectRatio,
1942
- inputMediaUrls: inputData?.inputImageUrls || inputData?.inputMediaUrls
1943
- });
1944
- }
1945
- }
1946
- continue;
1947
- }
1948
- }
1949
- if (entry.originalType === "tool_result" && entry.displayType === "media_carousel") {
1950
- const data = entry.data;
1951
- const mediaInfo = data?.mediaInfo;
1952
- const urls = mediaInfo?.urls;
1953
- if (urls && Array.isArray(urls)) {
1954
- for (const url of urls) {
1955
- if (typeof url !== "string") continue;
1956
- const detectedType = getMediaTypeFromUrl(url);
1957
- const type = detectedType === "video" ? "video" : detectedType === "audio" ? "audio" : "image";
1958
- if (!urlMap.has(url)) {
1959
- urlMap.set(url, {
1960
- url,
1961
- type,
1962
- timestamp: entry.timestamp
1963
- });
1964
- }
1965
- }
1966
- }
1967
- }
1968
- }
1969
- const mediaArray = Array.from(urlMap.values());
1970
- mediaArray.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
1971
- return mediaArray;
1972
- }
1973
-
1974
- // src/react-native/sse-adapter.ts
1975
- var import_react_native_sse = __toESM(require_react_native_sse());
1976
- var MAX_RETRIES2 = 5;
1977
- var INITIAL_RETRY_DELAY2 = 1e3;
1978
- var MAX_RETRY_DELAY2 = 1e4;
1979
- async function createRNSSEStream(options) {
1980
- const {
1981
- url,
1982
- headers,
1983
- body,
1984
- emitter,
1985
- signal,
1986
- onOpen,
1987
- onClose,
1988
- lastEventId
1989
- } = options;
1990
- const processedIds = /* @__PURE__ */ new Set();
1991
- let retryCount = 0;
1992
- return new Promise((resolve, reject) => {
1993
- let es = null;
1994
- let isClosed = false;
1995
- const cleanup = () => {
1996
- if (!isClosed) {
1997
- isClosed = true;
1998
- es?.close();
1999
- onClose?.();
2000
- resolve();
2001
- }
2002
- };
2003
- const abortHandler = () => {
2004
- cleanup();
2005
- };
2006
- signal?.addEventListener("abort", abortHandler);
2007
- const connect = () => {
2008
- if (isClosed || signal?.aborted) {
2009
- return;
2010
- }
2011
- es = new import_react_native_sse.default(url, {
2012
- method: "POST",
2013
- headers: {
2014
- "Content-Type": "application/json",
2015
- ...lastEventId ? { "Last-Event-ID": lastEventId } : {},
2016
- ...headers
2017
- },
2018
- body: JSON.stringify({ ...body, stream: true }),
2019
- pollingInterval: 0
2020
- // Disable polling, we handle reconnection manually
2021
- });
2022
- es.addEventListener("open", () => {
2023
- retryCount = 0;
2024
- onOpen?.();
2025
- });
2026
- const eventTypes = [
2027
- "init",
2028
- "session",
2029
- "entry",
2030
- "stream_event",
2031
- "content_block_start",
2032
- "content_block_delta",
2033
- "content_block_stop",
2034
- "complete",
2035
- "error",
2036
- "heartbeat"
2037
- ];
2038
- for (const eventType of eventTypes) {
2039
- es.addEventListener(eventType, (event) => {
2040
- console.log(`[RN-SSE] Received ${eventType} event:`, event.data?.substring?.(0, 100) || event.data);
2041
- const eventData = {
2042
- data: event.data,
2043
- lastEventId: event.lastEventId ?? void 0
2044
- };
2045
- handleSSEEvent(eventType, eventData, emitter, processedIds);
2046
- });
2047
- }
2048
- es.addEventListener("message", (event) => {
2049
- console.log("[RN-SSE] Received message event:", event.data?.substring?.(0, 100) || event.data);
2050
- const eventData = {
2051
- data: event.data,
2052
- lastEventId: event.lastEventId ?? void 0
2053
- };
2054
- handleSSEEvent("message", eventData, emitter, processedIds);
2055
- });
2056
- es.addEventListener("error", (event) => {
2057
- console.log("[RN-SSE] Error event:", JSON.stringify(event));
2058
- if (signal?.aborted || isClosed) {
2059
- cleanup();
2060
- return;
2061
- }
2062
- const errorMessage = event.message || "Connection error";
2063
- const status = event.xhrStatus;
2064
- if (status === 401 || status === 403 || errorMessage.includes("401") || errorMessage.includes("403") || errorMessage.includes("Authentication")) {
2065
- const authError = `Authentication failed (${status || "unknown"}): ${errorMessage}`;
2066
- console.log("[RN-SSE] Auth error detected:", authError);
2067
- emitter.emit("error", { error: authError });
2068
- cleanup();
2069
- reject(new Error(authError));
2070
- return;
2071
- }
2072
- if (errorMessage.includes("404")) {
2073
- emitter.emit("error", { error: "Session not found or not active" });
2074
- cleanup();
2075
- reject(new Error("Session not found or not active"));
2076
- return;
2077
- }
2078
- if (errorMessage.includes("503")) {
2079
- emitter.emit("error", { error: `Server unavailable: ${errorMessage}` });
2080
- cleanup();
2081
- reject(new Error(errorMessage));
2082
- return;
2083
- }
2084
- retryCount++;
2085
- if (retryCount > MAX_RETRIES2) {
2086
- const error = `Connection failed after ${MAX_RETRIES2} retries`;
2087
- emitter.emit("error", { error });
2088
- cleanup();
2089
- reject(new Error(error));
2090
- return;
2091
- }
2092
- const delay = Math.min(
2093
- INITIAL_RETRY_DELAY2 * Math.pow(2, retryCount - 1),
2094
- MAX_RETRY_DELAY2
2095
- );
2096
- console.log(`[RN-SSE] Retry ${retryCount}/${MAX_RETRIES2} in ${delay}ms`);
2097
- es?.close();
2098
- setTimeout(connect, delay);
2099
- });
2100
- es.addEventListener("close", () => {
2101
- cleanup();
2102
- });
2103
- };
2104
- connect();
2105
- });
2106
- }
2107
- function handleSSEEvent(eventType, event, emitter, processedIds) {
2108
- if (event.lastEventId && processedIds.has(event.lastEventId)) {
2109
- return;
2110
- }
2111
- if (event.lastEventId) {
2112
- processedIds.add(event.lastEventId);
2113
- }
2114
- if (!event.data) {
2115
- return;
2116
- }
2117
- try {
2118
- const data = JSON.parse(event.data);
2119
- switch (eventType) {
2120
- case "init":
2121
- case "session":
2122
- emitter.emit("session", { session_id: data.session_id });
2123
- break;
2124
- case "entry":
2125
- emitter.emit("entry", { entry: data });
2126
- break;
2127
- case "stream_event":
2128
- handleStreamEvent2(data, emitter);
2129
- break;
2130
- case "content_block_start":
2131
- emitter.emit("content_block_start", data);
2132
- break;
2133
- case "content_block_delta":
2134
- emitter.emit("content_block_delta", data);
2135
- break;
2136
- case "content_block_stop":
2137
- emitter.emit("content_block_stop", data);
2138
- break;
2139
- case "complete":
2140
- emitter.emit("complete", data);
2141
- break;
2142
- case "error":
2143
- emitter.emit("error", { error: data.error });
2144
- break;
2145
- case "heartbeat":
2146
- emitter.emit("heartbeat", {});
2147
- break;
2148
- case "message":
2149
- if (data.type) {
2150
- handleSSEEvent(data.type, { data: JSON.stringify(data) }, emitter, processedIds);
2151
- }
2152
- break;
2153
- default:
2154
- break;
2155
- }
2156
- } catch (e) {
2157
- console.error("[RN-SSE] Failed to parse event:", event.data, e);
2158
- }
2159
- }
2160
- function handleStreamEvent2(data, emitter) {
2161
- const streamEvent = data.event || data;
2162
- const eventData = streamEvent;
2163
- switch (eventData.type) {
2164
- case "content_block_start":
2165
- emitter.emit("content_block_start", eventData);
2166
- break;
2167
- case "content_block_delta":
2168
- emitter.emit("content_block_delta", eventData);
2169
- break;
2170
- case "content_block_stop":
2171
- emitter.emit("content_block_stop", eventData);
2172
- break;
2173
- }
2174
- }
2175
-
2176
- // src/react-native/client.ts
2177
- var RNAgentClient = class {
2178
- config;
2179
- baseClient;
2180
- activeStreams = /* @__PURE__ */ new Map();
2181
- constructor(config) {
2182
- this.config = {
2183
- apiVersion: "/v1",
2184
- timeout: 3e4,
2185
- ...config
2186
- };
2187
- this.baseClient = new AgentClient(config);
2188
- }
2189
- // ===========================================================================
2190
- // Public getters
2191
- // ===========================================================================
2192
- /** Whether org context is configured (orgId is set) */
2193
- get hasOrgContext() {
2194
- return this.baseClient.hasOrgContext;
2195
- }
2196
- // ===========================================================================
2197
- // Private helpers
2198
- // ===========================================================================
2199
- get baseUrl() {
2200
- return `${this.config.endpoint}${this.config.apiVersion}`;
2201
- }
2202
- async getHeaders() {
2203
- const headers = {
2204
- "Content-Type": "application/json"
2205
- };
2206
- const token = await this.config.getAuthToken();
2207
- if (token) {
2208
- headers["Authorization"] = `Bearer ${token}`;
2209
- }
2210
- return headers;
2211
- }
2212
- // ===========================================================================
2213
- // Messages API - React Native specific SSE implementation
2214
- // ===========================================================================
2215
- /**
2216
- * Send a message and stream responses using React Native SSE.
2217
- * @returns Object with event emitter and abort function
2218
- */
2219
- stream(request) {
2220
- const emitter = new AgentEventEmitter();
2221
- const controller = new AbortController();
2222
- const streamId = request.session_id || `stream-${Date.now()}`;
2223
- this.activeStreams.set(streamId, controller);
2224
- (async () => {
2225
- try {
2226
- const headers = await this.getHeaders();
2227
- await createRNSSEStream({
2228
- url: `${this.baseUrl}/messages`,
2229
- method: "POST",
2230
- headers,
2231
- body: { ...request, stream: true },
2232
- emitter,
2233
- signal: controller.signal
2234
- });
2235
- } catch (error) {
2236
- emitter.emit("error", {
2237
- error: error instanceof Error ? error.message : "Unknown error"
2238
- });
2239
- } finally {
2240
- this.activeStreams.delete(streamId);
2241
- }
2242
- })();
2243
- return {
2244
- emitter,
2245
- abort: () => controller.abort()
2246
- };
2247
- }
2248
- /**
2249
- * Reconnect to an active streaming session using React Native SSE.
2250
- * @returns Object with event emitter and abort function
2251
- */
2252
- reconnect(sessionId, lastEventId = -1) {
2253
- const emitter = new AgentEventEmitter();
2254
- const controller = new AbortController();
2255
- (async () => {
2256
- try {
2257
- const headers = await this.getHeaders();
2258
- await createRNSSEStream({
2259
- url: `${this.baseUrl}/sessions/${sessionId}/reconnect`,
2260
- method: "POST",
2261
- headers,
2262
- body: { last_event_id: lastEventId },
2263
- emitter,
2264
- signal: controller.signal
2265
- });
2266
- } catch (error) {
2267
- emitter.emit("error", {
2268
- error: error instanceof Error ? error.message : "Unknown error"
2269
- });
2270
- }
2271
- })();
2272
- return {
2273
- emitter,
2274
- abort: () => controller.abort()
2275
- };
2276
- }
2277
- // ===========================================================================
2278
- // Sessions API - Delegate to base client
2279
- // ===========================================================================
2280
- /**
2281
- * List all sessions for the authenticated user.
2282
- */
2283
- async listSessions() {
2284
- return this.baseClient.listSessions();
2285
- }
2286
- /**
2287
- * Get session content in display format.
2288
- */
2289
- async getSession(sessionId) {
2290
- return this.baseClient.getSession(sessionId);
2291
- }
2292
- /**
2293
- * Get session content as raw JSONL.
2294
- */
2295
- async getSessionRaw(sessionId) {
2296
- return this.baseClient.getSessionRaw(sessionId);
2297
- }
2298
- /**
2299
- * Check if a session is actively streaming.
2300
- */
2301
- async getSessionStatus(sessionId) {
2302
- return this.baseClient.getSessionStatus(sessionId);
2303
- }
2304
- /**
2305
- * Update session metadata (name).
2306
- */
2307
- async updateSession(sessionId, updates) {
2308
- return this.baseClient.updateSession(sessionId, updates);
2309
- }
2310
- /**
2311
- * Delete a session.
2312
- */
2313
- async deleteSession(sessionId) {
2314
- return this.baseClient.deleteSession(sessionId);
2315
- }
2316
- /**
2317
- * Cancel an active session query.
2318
- */
2319
- async cancelSession(sessionId) {
2320
- return this.baseClient.cancelSession(sessionId);
2321
- }
2322
- // ===========================================================================
2323
- // Skills API - Personal - Delegate to base client
2324
- // ===========================================================================
2325
- /**
2326
- * List personal skills.
2327
- */
2328
- async listSkills() {
2329
- return this.baseClient.listSkills();
2330
- }
2331
- /**
2332
- * Get personal skill content.
2333
- */
2334
- async getSkill(name) {
2335
- return this.baseClient.getSkill(name);
2336
- }
2337
- /**
2338
- * Create or update a personal skill.
2339
- */
2340
- async saveSkill(skill) {
2341
- return this.baseClient.saveSkill(skill);
2342
- }
2343
- /**
2344
- * Delete a personal skill.
2345
- */
2346
- async deleteSkill(name) {
2347
- return this.baseClient.deleteSkill(name);
2348
- }
2349
- // ===========================================================================
2350
- // Skills API - Organization - Delegate to base client
2351
- // ===========================================================================
2352
- /**
2353
- * List organization skills.
2354
- */
2355
- async listOrgSkills() {
2356
- return this.baseClient.listOrgSkills();
2357
- }
2358
- /**
2359
- * Get organization skill content.
2360
- */
2361
- async getOrgSkill(name) {
2362
- return this.baseClient.getOrgSkill(name);
2363
- }
2364
- /**
2365
- * Create or update an organization skill.
2366
- */
2367
- async saveOrgSkill(skill) {
2368
- return this.baseClient.saveOrgSkill(skill);
2369
- }
2370
- /**
2371
- * Delete an organization skill.
2372
- */
2373
- async deleteOrgSkill(name) {
2374
- return this.baseClient.deleteOrgSkill(name);
2375
- }
2376
- // ===========================================================================
2377
- // Plugins API - Delegate to base client
2378
- // ===========================================================================
2379
- /**
2380
- * Get plugin manifest for client sync.
2381
- */
2382
- async getPluginManifest() {
2383
- return this.baseClient.getPluginManifest();
2384
- }
2385
- /**
2386
- * Download plugin bundle.
2387
- */
2388
- async getPluginBundle(skills) {
2389
- return this.baseClient.getPluginBundle(skills);
2390
- }
2391
- };
2392
- var RNAgentContext = createContext(null);
2393
- function RNAgentProvider({
2394
- children,
2395
- config,
2396
- storage,
2397
- pauseOnBackground = true
2398
- }) {
2399
- const [appState, setAppState] = useState(AppState.currentState);
2400
- useEffect(() => {
2401
- if (!pauseOnBackground) return;
2402
- const subscription = AppState.addEventListener("change", (nextState) => {
2403
- setAppState(nextState);
2404
- });
2405
- return () => subscription.remove();
2406
- }, [pauseOnBackground]);
2407
- const client = useMemo(
2408
- () => new RNAgentClient(config),
2409
- // Only recreate client if endpoint changes
2410
- [config.endpoint, config.apiVersion, config.timeout]
2411
- );
2412
- const value = useMemo(
2413
- () => ({ client, storage, appState }),
2414
- [client, storage, appState]
2415
- );
2416
- return /* @__PURE__ */ jsx(RNAgentContext.Provider, { value, children });
2417
- }
2418
- function useRNAgentClient() {
2419
- const context = useContext(RNAgentContext);
2420
- if (!context) {
2421
- throw new Error("useRNAgentClient must be used within an RNAgentProvider");
2422
- }
2423
- return context.client;
2424
- }
2425
- function useStorage() {
2426
- const context = useContext(RNAgentContext);
2427
- if (!context) {
2428
- throw new Error("useStorage must be used within an RNAgentProvider");
2429
- }
2430
- return context.storage;
2431
- }
2432
- function useAppState() {
2433
- const context = useContext(RNAgentContext);
2434
- if (!context) {
2435
- throw new Error("useAppState must be used within an RNAgentProvider");
2436
- }
2437
- return context.appState;
2438
- }
2439
- function useRNAgentSession(options = {}) {
2440
- const client = useRNAgentClient();
2441
- const appState = useAppState();
2442
- const {
2443
- initialSessionId,
2444
- initialConversation,
2445
- onMediaGenerated,
2446
- pauseOnBackground = true
2447
- } = options;
2448
- const [session, setSession] = useState({
2449
- sessionId: initialSessionId,
2450
- conversation: initialConversation ?? [],
2451
- isStreaming: false
2452
- });
2453
- const abortRef = useRef(null);
2454
- const cancelInProgressRef = useRef(false);
2455
- const accumulatedTextRef = useRef({});
2456
- useEffect(() => {
2457
- if (pauseOnBackground && appState !== "active" && session.isStreaming) {
2458
- console.log("[RNAgentSession] App went to background while streaming");
2459
- }
2460
- }, [appState, pauseOnBackground, session.isStreaming]);
2461
- const setupEventHandlers = useCallback(
2462
- (emitter) => {
2463
- emitter.on("session", (data) => {
2464
- setSession((prev) => ({ ...prev, sessionId: data.session_id }));
2465
- });
2466
- emitter.on("content_block_start", (data) => {
2467
- const block = data.content_block;
2468
- if (block?.type === "text") {
2469
- accumulatedTextRef.current[data.index] = "";
2470
- setSession((prev) => ({ ...prev, showTinkering: false }));
2471
- } else if (block?.type === "tool_use") {
2472
- setSession((prev) => ({
2473
- ...prev,
2474
- streamingToolCall: {
2475
- tool_name: block.name || "",
2476
- tool_use_id: block.id || "",
2477
- block_index: data.index,
2478
- accumulated_input: ""
2479
- },
2480
- showTinkering: false
2481
- }));
2482
- }
2483
- });
2484
- emitter.on("content_block_delta", (data) => {
2485
- const delta = data.delta;
2486
- if (delta?.type === "text_delta" && delta.text) {
2487
- const idx = data.index ?? 0;
2488
- accumulatedTextRef.current[idx] = (accumulatedTextRef.current[idx] || "") + delta.text;
2489
- setSession((prev) => ({
2490
- ...prev,
2491
- streamingText: accumulatedTextRef.current[idx],
2492
- streamingBlockIndex: idx,
2493
- showTinkering: false
2494
- }));
2495
- } else if (delta?.type === "input_json_delta" && delta.partial_json) {
2496
- setSession((prev) => {
2497
- if (!prev.streamingToolCall) return prev;
2498
- return {
2499
- ...prev,
2500
- streamingToolCall: {
2501
- ...prev.streamingToolCall,
2502
- accumulated_input: (prev.streamingToolCall.accumulated_input || "") + delta.partial_json
2503
- }
2504
- };
2505
- });
2506
- }
2507
- });
2508
- emitter.on("content_block_stop", (data) => {
2509
- const idx = data.index ?? 0;
2510
- if (accumulatedTextRef.current[idx] !== void 0) {
2511
- delete accumulatedTextRef.current[idx];
2512
- setSession((prev) => ({
2513
- ...prev,
2514
- streamingText: void 0,
2515
- streamingBlockIndex: void 0
2516
- }));
2517
- }
2518
- });
2519
- emitter.on("entry", (data) => {
2520
- const entry = data.entry;
2521
- if (entry.type === "tool_result" && onMediaGenerated) {
2522
- const media = extractMediaFromEntry(entry);
2523
- if (media) {
2524
- onMediaGenerated(media.urls, media.type);
2525
- }
2526
- }
2527
- if (entry.type === "tool_call") {
2528
- setSession((prev) => ({
2529
- ...prev,
2530
- conversation: [...prev.conversation, entry],
2531
- streamingToolCall: void 0
2532
- }));
2533
- } else {
2534
- setSession((prev) => ({
2535
- ...prev,
2536
- conversation: [...prev.conversation, entry]
2537
- }));
2538
- }
2539
- });
2540
- emitter.on("complete", (data) => {
2541
- setSession((prev) => ({
2542
- ...prev,
2543
- sessionId: data.session_id || prev.sessionId,
2544
- isStreaming: false,
2545
- showTinkering: false
2546
- }));
2547
- abortRef.current = null;
2548
- });
2549
- emitter.on("error", (data) => {
2550
- setSession((prev) => ({
2551
- ...prev,
2552
- conversation: [
2553
- ...prev.conversation,
2554
- {
2555
- type: "error",
2556
- text: data.error,
2557
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2558
- }
2559
- ],
2560
- isStreaming: false,
2561
- showTinkering: false
2562
- }));
2563
- abortRef.current = null;
2564
- });
2565
- },
2566
- [onMediaGenerated]
2567
- );
2568
- const sendMessage = useCallback(
2569
- async (prompt, skills) => {
2570
- if (!prompt.trim() || session.isStreaming) return;
2571
- const userEntry = {
2572
- type: "user_text",
2573
- text: prompt,
2574
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2575
- };
2576
- setSession((prev) => ({
2577
- ...prev,
2578
- conversation: [...prev.conversation, userEntry],
2579
- isStreaming: true,
2580
- showTinkering: true
2581
- }));
2582
- accumulatedTextRef.current = {};
2583
- const { emitter, abort } = client.stream({
2584
- prompt,
2585
- session_id: session.sessionId,
2586
- skills
2587
- });
2588
- abortRef.current = abort;
2589
- setupEventHandlers(emitter);
2590
- },
2591
- [client, session.sessionId, session.isStreaming, setupEventHandlers]
2592
- );
2593
- const cancel = useCallback(async () => {
2594
- if (cancelInProgressRef.current) {
2595
- return;
2596
- }
2597
- cancelInProgressRef.current = true;
2598
- try {
2599
- if (session.sessionId) {
2600
- try {
2601
- await client.cancelSession(session.sessionId);
2602
- } catch (error) {
2603
- console.error("[RNAgentSession] Server-side cancel failed:", error);
2604
- }
2605
- } else {
2606
- console.warn("[RNAgentSession] No sessionId available for server-side cancel");
2607
- }
2608
- if (abortRef.current) {
2609
- abortRef.current();
2610
- abortRef.current = null;
2611
- }
2612
- setSession((prev) => ({
2613
- ...prev,
2614
- isStreaming: false,
2615
- showTinkering: false,
2616
- streamingText: void 0,
2617
- streamingToolCall: void 0
2618
- }));
2619
- } finally {
2620
- cancelInProgressRef.current = false;
2621
- }
2622
- }, [client, session.sessionId]);
2623
- const clear = useCallback(() => {
2624
- setSession({
2625
- conversation: [],
2626
- isStreaming: false
2627
- });
2628
- }, []);
2629
- const setSessionId = useCallback((id) => {
2630
- setSession((prev) => ({ ...prev, sessionId: id }));
2631
- }, []);
2632
- const setConversation = useCallback(
2633
- (entries, sessionId) => {
2634
- setSession((prev) => ({
2635
- ...prev,
2636
- conversation: entries,
2637
- sessionId: sessionId ?? prev.sessionId,
2638
- isStreaming: false,
2639
- streamingText: void 0,
2640
- streamingToolCall: void 0,
2641
- showTinkering: false
2642
- }));
2643
- },
2644
- []
2645
- );
2646
- const reconnect = useCallback(
2647
- async (targetSessionId, freshConversation, streamingPrompt) => {
2648
- let conversation = freshConversation ?? [];
2649
- if (streamingPrompt) {
2650
- conversation = [
2651
- ...conversation,
2652
- {
2653
- type: "user_text",
2654
- text: streamingPrompt,
2655
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2656
- }
2657
- ];
2658
- }
2659
- setSession((prev) => ({
2660
- ...prev,
2661
- sessionId: targetSessionId,
2662
- conversation,
2663
- isStreaming: true,
2664
- showTinkering: true
2665
- }));
2666
- accumulatedTextRef.current = {};
2667
- const { emitter, abort } = client.reconnect(targetSessionId, -1);
2668
- abortRef.current = abort;
2669
- setupEventHandlers(emitter);
2670
- },
2671
- [client, setupEventHandlers]
2672
- );
2673
- const loadSession = useCallback(
2674
- async (sessionId) => {
2675
- const response = await client.getSession(sessionId);
2676
- return response.conversation;
2677
- },
2678
- [client]
2679
- );
2680
- return {
2681
- sessionId: session.sessionId,
2682
- conversation: session.conversation,
2683
- isStreaming: session.isStreaming,
2684
- streamingText: session.streamingText,
2685
- streamingToolCall: session.streamingToolCall,
2686
- showTinkering: session.showTinkering ?? false,
2687
- sendMessage,
2688
- cancel,
2689
- clear,
2690
- reconnect,
2691
- setSessionId,
2692
- setConversation,
2693
- loadSession
2694
- };
2695
- }
2696
- function detectMediaType(url) {
2697
- if (/\.(mp4|mov|webm|avi|mkv|m4v)(\?|$)/i.test(url)) return "video";
2698
- if (/\.(mp3|wav|m4a|aac|ogg)(\?|$)/i.test(url)) return "audio";
2699
- return "image";
2700
- }
2701
- function extractMediaFromEntry(entry) {
2702
- if (entry.type !== "tool_result") return null;
2703
- let result = null;
2704
- if (typeof entry.content === "string") {
2705
- try {
2706
- result = JSON.parse(entry.content);
2707
- } catch {
2708
- return null;
2709
- }
2710
- }
2711
- if (!result) return null;
2712
- const mediaInfo = result.mediaInfo || result.data?.mediaInfo;
2713
- if (mediaInfo?.urls && mediaInfo.urls.length > 0) {
2714
- const type = detectMediaType(mediaInfo.urls[0]);
2715
- return { urls: mediaInfo.urls, type };
2716
- }
2717
- const cdnUrls = result.output_cdn_urls || result.data?.output_cdn_urls;
2718
- if (cdnUrls && cdnUrls.length > 0) {
2719
- const type = detectMediaType(cdnUrls[0]);
2720
- return { urls: cdnUrls, type };
2721
- }
2722
- return null;
2723
- }
2724
- function useJadeSession(options = {}) {
2725
- const {
2726
- skipSkillContext = true,
2727
- processingOptions,
2728
- ...agentOptions
2729
- } = options;
2730
- const session = useRNAgentSession(agentOptions);
2731
- const processedConversation = useMemo(() => {
2732
- return processConversation(session.conversation, {
2733
- skipSkillContext,
2734
- pairToolResults: true,
2735
- extractMedia: true,
2736
- ...processingOptions
2737
- });
2738
- }, [session.conversation, skipSkillContext, processingOptions]);
2739
- const media = useMemo(() => {
2740
- return extractMedia(session.conversation);
2741
- }, [session.conversation]);
2742
- return {
2743
- ...session,
2744
- processedConversation,
2745
- media
2746
- };
2747
- }
2748
-
2749
- // src/react-native/storage-adapter.ts
2750
- function createAsyncStorageAdapter(asyncStorage) {
2751
- return {
2752
- getItem: (key) => asyncStorage.getItem(key),
2753
- setItem: (key, value) => asyncStorage.setItem(key, value),
2754
- removeItem: (key) => asyncStorage.removeItem(key)
2755
- };
2756
- }
2757
- function createMMKVAdapter(mmkv) {
2758
- return {
2759
- getItem: async (key) => mmkv.getString(key) ?? null,
2760
- setItem: async (key, value) => mmkv.set(key, value),
2761
- removeItem: async (key) => mmkv.delete(key)
2762
- };
2763
- }
2764
- function createMemoryStorageAdapter() {
2765
- const store = /* @__PURE__ */ new Map();
2766
- return {
2767
- getItem: async (key) => store.get(key) ?? null,
2768
- setItem: async (key, value) => {
2769
- store.set(key, value);
2770
- },
2771
- removeItem: async (key) => {
2772
- store.delete(key);
2773
- }
2774
- };
2775
- }
2776
- var STORAGE_KEYS = {
2777
- AUTH_TOKEN: "@gr33n-ai/auth-token",
2778
- ENDPOINT: "@gr33n-ai/endpoint",
2779
- ORG_ID: "@gr33n-ai/org-id",
2780
- LAST_SESSION_ID: "@gr33n-ai/last-session-id",
2781
- USER_PREFERENCES: "@gr33n-ai/user-preferences"
2782
- };
2783
-
2784
- export { RNAgentProvider, STORAGE_KEYS, createAsyncStorageAdapter, createMMKVAdapter, createMemoryStorageAdapter, useAppState, useJadeSession, useRNAgentClient, useRNAgentSession, useStorage };