@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.
- package/README.md +13 -13
- package/dist/index.d.mts +339 -6
- package/dist/index.d.ts +339 -6
- package/dist/index.js +30 -55
- package/dist/index.mjs +27 -36
- package/package.json +17 -23
- package/dist/react-native/index.d.mts +0 -339
- package/dist/react-native/index.d.ts +0 -339
- package/dist/react-native/index.js +0 -2795
- package/dist/react-native/index.mjs +0 -2784
|
@@ -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 };
|