@yourgpt/copilot-sdk 0.1.0
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 +61 -0
- package/dist/chunk-2ZCWVAAK.cjs +1915 -0
- package/dist/chunk-2ZCWVAAK.cjs.map +1 -0
- package/dist/chunk-N4OA2J32.js +1844 -0
- package/dist/chunk-N4OA2J32.js.map +1 -0
- package/dist/chunk-QUGTRQSS.js +3100 -0
- package/dist/chunk-QUGTRQSS.js.map +1 -0
- package/dist/chunk-W6KQT7YZ.cjs +3152 -0
- package/dist/chunk-W6KQT7YZ.cjs.map +1 -0
- package/dist/core/index.cjs +288 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +870 -0
- package/dist/core/index.d.ts +870 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/react/index.cjs +137 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +1824 -0
- package/dist/react/index.d.ts +1824 -0
- package/dist/react/index.js +4 -0
- package/dist/react/index.js.map +1 -0
- package/dist/styles.css +48 -0
- package/dist/thread-C2FjuGLb.d.cts +1225 -0
- package/dist/thread-C2FjuGLb.d.ts +1225 -0
- package/dist/ui/index.cjs +4116 -0
- package/dist/ui/index.cjs.map +1 -0
- package/dist/ui/index.d.cts +1143 -0
- package/dist/ui/index.d.ts +1143 -0
- package/dist/ui/index.js +4019 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +100 -0
|
@@ -0,0 +1,1844 @@
|
|
|
1
|
+
// src/core/tools/screenshot.ts
|
|
2
|
+
var isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
3
|
+
var DEFAULT_OPTIONS = {
|
|
4
|
+
element: isBrowser ? document.body : null,
|
|
5
|
+
quality: 0.8,
|
|
6
|
+
format: "png",
|
|
7
|
+
maxWidth: 1920,
|
|
8
|
+
maxHeight: 1080,
|
|
9
|
+
includeCursor: false
|
|
10
|
+
};
|
|
11
|
+
var html2canvasPromise = null;
|
|
12
|
+
async function getHtml2Canvas() {
|
|
13
|
+
if (!html2canvasPromise) {
|
|
14
|
+
html2canvasPromise = import('html2canvas').then((mod) => mod.default);
|
|
15
|
+
}
|
|
16
|
+
return html2canvasPromise;
|
|
17
|
+
}
|
|
18
|
+
async function captureScreenshot(options = {}) {
|
|
19
|
+
if (!isBrowser) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
"Screenshot capture is only available in browser environment"
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
25
|
+
const element = opts.element || document.body;
|
|
26
|
+
const rect = element.getBoundingClientRect();
|
|
27
|
+
let width = rect.width || window.innerWidth;
|
|
28
|
+
let height = rect.height || window.innerHeight;
|
|
29
|
+
const scale = Math.min(opts.maxWidth / width, opts.maxHeight / height, 1);
|
|
30
|
+
width = Math.round(width * scale);
|
|
31
|
+
height = Math.round(height * scale);
|
|
32
|
+
let canvas;
|
|
33
|
+
try {
|
|
34
|
+
const html2canvas = await getHtml2Canvas();
|
|
35
|
+
canvas = await html2canvas(element, {
|
|
36
|
+
scale,
|
|
37
|
+
useCORS: true,
|
|
38
|
+
// Enable cross-origin images
|
|
39
|
+
allowTaint: false,
|
|
40
|
+
// Don't allow tainted canvas
|
|
41
|
+
backgroundColor: null,
|
|
42
|
+
// Transparent background (uses element's bg)
|
|
43
|
+
logging: false,
|
|
44
|
+
// Disable internal logging
|
|
45
|
+
width: rect.width,
|
|
46
|
+
height: rect.height,
|
|
47
|
+
windowWidth: window.innerWidth,
|
|
48
|
+
windowHeight: window.innerHeight,
|
|
49
|
+
scrollX: 0,
|
|
50
|
+
scrollY: 0,
|
|
51
|
+
x: rect.left + window.scrollX,
|
|
52
|
+
y: rect.top + window.scrollY
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
canvas = document.createElement("canvas");
|
|
56
|
+
canvas.width = width;
|
|
57
|
+
canvas.height = height;
|
|
58
|
+
const ctx = canvas.getContext("2d");
|
|
59
|
+
if (ctx) {
|
|
60
|
+
createPlaceholder(ctx, width, height, element, String(error));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const mimeType = `image/${opts.format === "jpeg" ? "jpeg" : opts.format}`;
|
|
64
|
+
let data;
|
|
65
|
+
try {
|
|
66
|
+
data = canvas.toDataURL(mimeType, opts.quality);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
if (e instanceof DOMException && e.name === "SecurityError") {
|
|
69
|
+
console.warn(
|
|
70
|
+
"[Copilot SDK] Canvas tainted by cross-origin content. Creating placeholder."
|
|
71
|
+
);
|
|
72
|
+
const cleanCanvas = document.createElement("canvas");
|
|
73
|
+
cleanCanvas.width = width;
|
|
74
|
+
cleanCanvas.height = height;
|
|
75
|
+
const cleanCtx = cleanCanvas.getContext("2d");
|
|
76
|
+
if (cleanCtx) {
|
|
77
|
+
createPlaceholder(
|
|
78
|
+
cleanCtx,
|
|
79
|
+
width,
|
|
80
|
+
height,
|
|
81
|
+
element,
|
|
82
|
+
"Cross-origin content blocked"
|
|
83
|
+
);
|
|
84
|
+
data = cleanCanvas.toDataURL(mimeType, opts.quality);
|
|
85
|
+
} else {
|
|
86
|
+
throw new Error("Failed to create placeholder canvas");
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
throw e;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
data,
|
|
94
|
+
format: opts.format,
|
|
95
|
+
width: canvas.width,
|
|
96
|
+
height: canvas.height,
|
|
97
|
+
timestamp: Date.now()
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function createPlaceholder(ctx, width, height, element, errorMessage) {
|
|
101
|
+
ctx.fillStyle = "#f0f0f0";
|
|
102
|
+
ctx.fillRect(0, 0, width, height);
|
|
103
|
+
ctx.strokeStyle = "#ddd";
|
|
104
|
+
ctx.lineWidth = 2;
|
|
105
|
+
ctx.strokeRect(2, 2, width - 4, height - 4);
|
|
106
|
+
ctx.fillStyle = "#666";
|
|
107
|
+
ctx.font = "14px system-ui, sans-serif";
|
|
108
|
+
ctx.textAlign = "center";
|
|
109
|
+
ctx.textBaseline = "middle";
|
|
110
|
+
const tagName = element.tagName.toLowerCase();
|
|
111
|
+
const id = element.id ? `#${element.id}` : "";
|
|
112
|
+
const className = element.className ? `.${String(element.className).split(" ")[0]}` : "";
|
|
113
|
+
ctx.fillText(
|
|
114
|
+
`Screenshot: <${tagName}${id}${className}>`,
|
|
115
|
+
width / 2,
|
|
116
|
+
height / 2 - 20
|
|
117
|
+
);
|
|
118
|
+
ctx.fillText(
|
|
119
|
+
`${Math.round(width)}\xD7${Math.round(height)}px`,
|
|
120
|
+
width / 2,
|
|
121
|
+
height / 2
|
|
122
|
+
);
|
|
123
|
+
if (errorMessage) {
|
|
124
|
+
ctx.fillStyle = "#999";
|
|
125
|
+
ctx.font = "12px system-ui, sans-serif";
|
|
126
|
+
ctx.fillText(
|
|
127
|
+
`Error: ${errorMessage.slice(0, 50)}`,
|
|
128
|
+
width / 2,
|
|
129
|
+
height / 2 + 20
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function isScreenshotSupported() {
|
|
134
|
+
return isBrowser && typeof document.createElement === "function";
|
|
135
|
+
}
|
|
136
|
+
async function resizeScreenshot(screenshot, maxWidth, maxHeight) {
|
|
137
|
+
if (!isBrowser) {
|
|
138
|
+
throw new Error("Resize is only available in browser environment");
|
|
139
|
+
}
|
|
140
|
+
return new Promise((resolve, reject) => {
|
|
141
|
+
const img = new Image();
|
|
142
|
+
img.onload = () => {
|
|
143
|
+
const scale = Math.min(maxWidth / img.width, maxHeight / img.height, 1);
|
|
144
|
+
const width = Math.round(img.width * scale);
|
|
145
|
+
const height = Math.round(img.height * scale);
|
|
146
|
+
const canvas = document.createElement("canvas");
|
|
147
|
+
canvas.width = width;
|
|
148
|
+
canvas.height = height;
|
|
149
|
+
const ctx = canvas.getContext("2d");
|
|
150
|
+
if (!ctx) {
|
|
151
|
+
reject(new Error("Failed to create canvas context"));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
155
|
+
const data = canvas.toDataURL(`image/${screenshot.format}`, 0.8);
|
|
156
|
+
resolve({
|
|
157
|
+
...screenshot,
|
|
158
|
+
data,
|
|
159
|
+
width,
|
|
160
|
+
height,
|
|
161
|
+
timestamp: Date.now()
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
img.onerror = () => reject(new Error("Failed to load screenshot"));
|
|
165
|
+
img.src = screenshot.data;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/core/tools/console.ts
|
|
170
|
+
var isBrowser2 = typeof window !== "undefined" && typeof console !== "undefined";
|
|
171
|
+
var capturedLogs = [];
|
|
172
|
+
var isCapturing = false;
|
|
173
|
+
var captureOptions;
|
|
174
|
+
var originalMethods = {
|
|
175
|
+
log: console.log,
|
|
176
|
+
info: console.info,
|
|
177
|
+
warn: console.warn,
|
|
178
|
+
error: console.error,
|
|
179
|
+
debug: console.debug
|
|
180
|
+
};
|
|
181
|
+
var DEFAULT_OPTIONS2 = {
|
|
182
|
+
types: ["log", "info", "warn", "error", "debug"],
|
|
183
|
+
limit: 100,
|
|
184
|
+
filter: () => true
|
|
185
|
+
};
|
|
186
|
+
function argsToMessage(args) {
|
|
187
|
+
return args.map((arg) => {
|
|
188
|
+
if (arg === null) return "null";
|
|
189
|
+
if (arg === void 0) return "undefined";
|
|
190
|
+
if (typeof arg === "string") return arg;
|
|
191
|
+
if (arg instanceof Error) return `${arg.name}: ${arg.message}`;
|
|
192
|
+
try {
|
|
193
|
+
return JSON.stringify(arg, null, 2);
|
|
194
|
+
} catch {
|
|
195
|
+
return String(arg);
|
|
196
|
+
}
|
|
197
|
+
}).join(" ");
|
|
198
|
+
}
|
|
199
|
+
function getStackTrace() {
|
|
200
|
+
try {
|
|
201
|
+
const error = new Error();
|
|
202
|
+
const stack = error.stack?.split("\n").slice(3).join("\n");
|
|
203
|
+
return stack;
|
|
204
|
+
} catch {
|
|
205
|
+
return void 0;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function createInterceptor(type) {
|
|
209
|
+
return (...args) => {
|
|
210
|
+
originalMethods[type].apply(console, args);
|
|
211
|
+
if (!captureOptions.types.includes(type)) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const entry = {
|
|
215
|
+
type,
|
|
216
|
+
message: argsToMessage(args),
|
|
217
|
+
args: args.length > 1 ? args : void 0,
|
|
218
|
+
timestamp: Date.now()
|
|
219
|
+
};
|
|
220
|
+
if (type === "error" || type === "warn") {
|
|
221
|
+
entry.stack = getStackTrace();
|
|
222
|
+
}
|
|
223
|
+
if (args[0] instanceof Error) {
|
|
224
|
+
entry.stack = args[0].stack;
|
|
225
|
+
}
|
|
226
|
+
if (!captureOptions.filter(entry)) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
capturedLogs.push(entry);
|
|
230
|
+
while (capturedLogs.length > captureOptions.limit) {
|
|
231
|
+
capturedLogs.shift();
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function startConsoleCapture(options = {}) {
|
|
236
|
+
if (!isBrowser2) {
|
|
237
|
+
console.warn("Console capture is only available in browser environment");
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (isCapturing) {
|
|
241
|
+
console.warn("Console capture already active");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
captureOptions = { ...DEFAULT_OPTIONS2, ...options };
|
|
245
|
+
isCapturing = true;
|
|
246
|
+
Object.keys(originalMethods).forEach((type) => {
|
|
247
|
+
console[type] = createInterceptor(type);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
function stopConsoleCapture() {
|
|
251
|
+
if (!isCapturing) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
isCapturing = false;
|
|
255
|
+
Object.keys(originalMethods).forEach((type) => {
|
|
256
|
+
console[type] = originalMethods[type];
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
function getConsoleLogs(options = {}) {
|
|
260
|
+
const opts = { ...DEFAULT_OPTIONS2, ...options };
|
|
261
|
+
let logs = [...capturedLogs];
|
|
262
|
+
if (opts.types.length < Object.keys(originalMethods).length) {
|
|
263
|
+
logs = logs.filter((entry) => opts.types.includes(entry.type));
|
|
264
|
+
}
|
|
265
|
+
if (opts.filter !== DEFAULT_OPTIONS2.filter) {
|
|
266
|
+
logs = logs.filter(opts.filter);
|
|
267
|
+
}
|
|
268
|
+
const totalCaptured = logs.length;
|
|
269
|
+
logs = logs.slice(-opts.limit);
|
|
270
|
+
return {
|
|
271
|
+
logs,
|
|
272
|
+
totalCaptured
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function clearConsoleLogs() {
|
|
276
|
+
capturedLogs = [];
|
|
277
|
+
}
|
|
278
|
+
function isConsoleCaptureActive() {
|
|
279
|
+
return isCapturing;
|
|
280
|
+
}
|
|
281
|
+
function getConsoleErrors(limit = 50) {
|
|
282
|
+
return getConsoleLogs({
|
|
283
|
+
types: ["error"],
|
|
284
|
+
limit
|
|
285
|
+
}).logs;
|
|
286
|
+
}
|
|
287
|
+
function getConsoleWarnings(limit = 50) {
|
|
288
|
+
return getConsoleLogs({
|
|
289
|
+
types: ["warn"],
|
|
290
|
+
limit
|
|
291
|
+
}).logs;
|
|
292
|
+
}
|
|
293
|
+
function formatLogsForAI(logs) {
|
|
294
|
+
if (logs.length === 0) {
|
|
295
|
+
return "No console logs captured.";
|
|
296
|
+
}
|
|
297
|
+
const formatted = logs.map((entry, index) => {
|
|
298
|
+
const time = new Date(entry.timestamp).toISOString();
|
|
299
|
+
const typeIcon = {
|
|
300
|
+
error: "\u274C",
|
|
301
|
+
warn: "\u26A0\uFE0F",
|
|
302
|
+
log: "\u{1F4DD}",
|
|
303
|
+
info: "\u2139\uFE0F",
|
|
304
|
+
debug: "\u{1F50D}"
|
|
305
|
+
}[entry.type];
|
|
306
|
+
let text = `[${index + 1}] ${typeIcon} [${entry.type.toUpperCase()}] ${time}
|
|
307
|
+
`;
|
|
308
|
+
text += ` ${entry.message}`;
|
|
309
|
+
if (entry.stack) {
|
|
310
|
+
const stackLines = entry.stack.split("\n").slice(0, 3);
|
|
311
|
+
text += `
|
|
312
|
+
Stack:
|
|
313
|
+
${stackLines.map((l) => ` ${l.trim()}`).join("\n")}`;
|
|
314
|
+
}
|
|
315
|
+
return text;
|
|
316
|
+
});
|
|
317
|
+
return `Console Logs (${logs.length} entries):
|
|
318
|
+
|
|
319
|
+
${formatted.join("\n\n")}`;
|
|
320
|
+
}
|
|
321
|
+
function captureCurrentLogs(options = {}) {
|
|
322
|
+
if (isCapturing) {
|
|
323
|
+
return getConsoleLogs(options);
|
|
324
|
+
}
|
|
325
|
+
return getConsoleLogs(options);
|
|
326
|
+
}
|
|
327
|
+
if (isBrowser2) {
|
|
328
|
+
window.addEventListener("beforeunload", () => {
|
|
329
|
+
stopConsoleCapture();
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/core/tools/network.ts
|
|
334
|
+
var isBrowser3 = typeof window !== "undefined";
|
|
335
|
+
var capturedRequests = [];
|
|
336
|
+
var isCapturing2 = false;
|
|
337
|
+
var captureOptions2;
|
|
338
|
+
var originalFetch = isBrowser3 ? window.fetch.bind(window) : null;
|
|
339
|
+
var originalXHROpen = isBrowser3 ? XMLHttpRequest.prototype.open : null;
|
|
340
|
+
var originalXHRSend = isBrowser3 ? XMLHttpRequest.prototype.send : null;
|
|
341
|
+
var DEFAULT_OPTIONS3 = {
|
|
342
|
+
limit: 50,
|
|
343
|
+
failedOnly: true,
|
|
344
|
+
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"],
|
|
345
|
+
includeUrls: [],
|
|
346
|
+
excludeUrls: [],
|
|
347
|
+
captureRequestBody: true,
|
|
348
|
+
captureResponseBody: true,
|
|
349
|
+
maxBodySize: 1e4
|
|
350
|
+
// 10KB
|
|
351
|
+
};
|
|
352
|
+
function sanitizeHeaders(headers) {
|
|
353
|
+
const sensitiveHeaders = [
|
|
354
|
+
"authorization",
|
|
355
|
+
"cookie",
|
|
356
|
+
"set-cookie",
|
|
357
|
+
"x-api-key",
|
|
358
|
+
"api-key",
|
|
359
|
+
"x-auth-token"
|
|
360
|
+
];
|
|
361
|
+
const result = {};
|
|
362
|
+
if (!headers) {
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
const entries = headers instanceof Headers ? Array.from(headers.entries()) : Object.entries(headers);
|
|
366
|
+
for (const [key, value] of entries) {
|
|
367
|
+
if (sensitiveHeaders.includes(key.toLowerCase())) {
|
|
368
|
+
result[key] = "[REDACTED]";
|
|
369
|
+
} else {
|
|
370
|
+
result[key] = value;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return result;
|
|
374
|
+
}
|
|
375
|
+
function truncateBody(body, maxSize) {
|
|
376
|
+
if (body === null || body === void 0) {
|
|
377
|
+
return body;
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
const str = typeof body === "string" ? body : JSON.stringify(body);
|
|
381
|
+
if (str.length > maxSize) {
|
|
382
|
+
return `${str.slice(0, maxSize)}... [truncated, total: ${str.length} chars]`;
|
|
383
|
+
}
|
|
384
|
+
return body;
|
|
385
|
+
} catch {
|
|
386
|
+
return "[Unable to serialize body]";
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function matchesPatterns(url, patterns) {
|
|
390
|
+
if (patterns.length === 0) return false;
|
|
391
|
+
return patterns.some((pattern) => pattern.test(url));
|
|
392
|
+
}
|
|
393
|
+
function shouldCapture(url, method, failed) {
|
|
394
|
+
if (captureOptions2.failedOnly && !failed) {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
if (!captureOptions2.methods.includes(method)) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
if (matchesPatterns(url, captureOptions2.excludeUrls)) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
if (captureOptions2.includeUrls.length > 0 && !matchesPatterns(url, captureOptions2.includeUrls)) {
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
function addCapturedRequest(entry) {
|
|
409
|
+
capturedRequests.push(entry);
|
|
410
|
+
while (capturedRequests.length > captureOptions2.limit) {
|
|
411
|
+
capturedRequests.shift();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function createFetchInterceptor() {
|
|
415
|
+
return async function interceptedFetch(input, init) {
|
|
416
|
+
const startTime = Date.now();
|
|
417
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
418
|
+
const method = init?.method?.toUpperCase() || "GET";
|
|
419
|
+
let requestBody;
|
|
420
|
+
if (captureOptions2.captureRequestBody && init?.body) {
|
|
421
|
+
try {
|
|
422
|
+
if (typeof init.body === "string") {
|
|
423
|
+
requestBody = JSON.parse(init.body);
|
|
424
|
+
} else {
|
|
425
|
+
requestBody = init.body;
|
|
426
|
+
}
|
|
427
|
+
} catch {
|
|
428
|
+
requestBody = init.body;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
try {
|
|
432
|
+
const response = await originalFetch(input, init);
|
|
433
|
+
const duration = Date.now() - startTime;
|
|
434
|
+
const failed = !response.ok;
|
|
435
|
+
if (shouldCapture(url, method, failed)) {
|
|
436
|
+
let responseBody;
|
|
437
|
+
if (captureOptions2.captureResponseBody && failed) {
|
|
438
|
+
try {
|
|
439
|
+
const clone = response.clone();
|
|
440
|
+
const text = await clone.text();
|
|
441
|
+
try {
|
|
442
|
+
responseBody = JSON.parse(text);
|
|
443
|
+
} catch {
|
|
444
|
+
responseBody = text;
|
|
445
|
+
}
|
|
446
|
+
} catch {
|
|
447
|
+
responseBody = "[Unable to read response body]";
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
const entry = {
|
|
451
|
+
url,
|
|
452
|
+
method,
|
|
453
|
+
status: response.status,
|
|
454
|
+
statusText: response.statusText,
|
|
455
|
+
failed,
|
|
456
|
+
requestHeaders: sanitizeHeaders(
|
|
457
|
+
init?.headers
|
|
458
|
+
),
|
|
459
|
+
responseHeaders: sanitizeHeaders(response.headers),
|
|
460
|
+
requestBody: truncateBody(requestBody, captureOptions2.maxBodySize),
|
|
461
|
+
responseBody: truncateBody(responseBody, captureOptions2.maxBodySize),
|
|
462
|
+
duration,
|
|
463
|
+
timestamp: startTime
|
|
464
|
+
};
|
|
465
|
+
addCapturedRequest(entry);
|
|
466
|
+
}
|
|
467
|
+
return response;
|
|
468
|
+
} catch (error) {
|
|
469
|
+
const duration = Date.now() - startTime;
|
|
470
|
+
if (shouldCapture(url, method, true)) {
|
|
471
|
+
const entry = {
|
|
472
|
+
url,
|
|
473
|
+
method,
|
|
474
|
+
status: 0,
|
|
475
|
+
statusText: "Network Error",
|
|
476
|
+
failed: true,
|
|
477
|
+
requestBody: truncateBody(requestBody, captureOptions2.maxBodySize),
|
|
478
|
+
duration,
|
|
479
|
+
timestamp: startTime,
|
|
480
|
+
error: error instanceof Error ? error.message : String(error)
|
|
481
|
+
};
|
|
482
|
+
addCapturedRequest(entry);
|
|
483
|
+
}
|
|
484
|
+
throw error;
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function startNetworkCapture(options = {}) {
|
|
489
|
+
if (!isBrowser3) {
|
|
490
|
+
console.warn("Network capture is only available in browser environment");
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
if (isCapturing2) {
|
|
494
|
+
console.warn("Network capture already active");
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
captureOptions2 = { ...DEFAULT_OPTIONS3, ...options };
|
|
498
|
+
isCapturing2 = true;
|
|
499
|
+
window.fetch = createFetchInterceptor();
|
|
500
|
+
XMLHttpRequest.prototype.open = function(method, url, async = true, username, password) {
|
|
501
|
+
this._captureData = {
|
|
502
|
+
method: method.toUpperCase(),
|
|
503
|
+
url: url.toString(),
|
|
504
|
+
startTime: 0
|
|
505
|
+
};
|
|
506
|
+
return originalXHROpen.call(
|
|
507
|
+
this,
|
|
508
|
+
method,
|
|
509
|
+
url,
|
|
510
|
+
async,
|
|
511
|
+
username,
|
|
512
|
+
password
|
|
513
|
+
);
|
|
514
|
+
};
|
|
515
|
+
XMLHttpRequest.prototype.send = function(body) {
|
|
516
|
+
const xhr = this;
|
|
517
|
+
if (xhr._captureData) {
|
|
518
|
+
xhr._captureData.startTime = Date.now();
|
|
519
|
+
if (captureOptions2.captureRequestBody && body) {
|
|
520
|
+
try {
|
|
521
|
+
if (typeof body === "string") {
|
|
522
|
+
xhr._captureData.requestBody = JSON.parse(body);
|
|
523
|
+
} else {
|
|
524
|
+
xhr._captureData.requestBody = body;
|
|
525
|
+
}
|
|
526
|
+
} catch {
|
|
527
|
+
xhr._captureData.requestBody = body;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const originalOnReadyStateChange = xhr.onreadystatechange;
|
|
531
|
+
xhr.onreadystatechange = function(event) {
|
|
532
|
+
if (xhr.readyState === XMLHttpRequest.DONE) {
|
|
533
|
+
const duration = Date.now() - xhr._captureData.startTime;
|
|
534
|
+
const failed = xhr.status === 0 || xhr.status >= 400;
|
|
535
|
+
if (shouldCapture(xhr._captureData.url, xhr._captureData.method, failed)) {
|
|
536
|
+
let responseBody;
|
|
537
|
+
if (captureOptions2.captureResponseBody && failed) {
|
|
538
|
+
try {
|
|
539
|
+
responseBody = xhr.responseType === "" || xhr.responseType === "text" ? JSON.parse(xhr.responseText) : xhr.response;
|
|
540
|
+
} catch {
|
|
541
|
+
responseBody = xhr.responseText || xhr.response;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
const entry = {
|
|
545
|
+
url: xhr._captureData.url,
|
|
546
|
+
method: xhr._captureData.method,
|
|
547
|
+
status: xhr.status,
|
|
548
|
+
statusText: xhr.statusText,
|
|
549
|
+
failed,
|
|
550
|
+
requestBody: truncateBody(
|
|
551
|
+
xhr._captureData.requestBody,
|
|
552
|
+
captureOptions2.maxBodySize
|
|
553
|
+
),
|
|
554
|
+
responseBody: truncateBody(
|
|
555
|
+
responseBody,
|
|
556
|
+
captureOptions2.maxBodySize
|
|
557
|
+
),
|
|
558
|
+
duration,
|
|
559
|
+
timestamp: xhr._captureData.startTime
|
|
560
|
+
};
|
|
561
|
+
addCapturedRequest(entry);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (originalOnReadyStateChange) {
|
|
565
|
+
originalOnReadyStateChange.call(xhr, event);
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
return originalXHRSend.call(this, body);
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function stopNetworkCapture() {
|
|
573
|
+
if (!isBrowser3 || !isCapturing2) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
isCapturing2 = false;
|
|
577
|
+
window.fetch = originalFetch;
|
|
578
|
+
XMLHttpRequest.prototype.open = originalXHROpen;
|
|
579
|
+
XMLHttpRequest.prototype.send = originalXHRSend;
|
|
580
|
+
}
|
|
581
|
+
function getNetworkRequests(options = {}) {
|
|
582
|
+
const opts = { ...DEFAULT_OPTIONS3, ...options };
|
|
583
|
+
let requests = [...capturedRequests];
|
|
584
|
+
if (opts.failedOnly) {
|
|
585
|
+
requests = requests.filter((r) => r.failed);
|
|
586
|
+
}
|
|
587
|
+
if (opts.methods.length < DEFAULT_OPTIONS3.methods.length) {
|
|
588
|
+
requests = requests.filter((r) => opts.methods.includes(r.method));
|
|
589
|
+
}
|
|
590
|
+
if (opts.includeUrls.length > 0) {
|
|
591
|
+
requests = requests.filter((r) => matchesPatterns(r.url, opts.includeUrls));
|
|
592
|
+
}
|
|
593
|
+
if (opts.excludeUrls.length > 0) {
|
|
594
|
+
requests = requests.filter(
|
|
595
|
+
(r) => !matchesPatterns(r.url, opts.excludeUrls)
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
const totalCaptured = requests.length;
|
|
599
|
+
requests = requests.slice(-opts.limit);
|
|
600
|
+
return {
|
|
601
|
+
requests,
|
|
602
|
+
totalCaptured
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
function clearNetworkRequests() {
|
|
606
|
+
capturedRequests = [];
|
|
607
|
+
}
|
|
608
|
+
function isNetworkCaptureActive() {
|
|
609
|
+
return isCapturing2;
|
|
610
|
+
}
|
|
611
|
+
function getFailedRequests(limit = 20) {
|
|
612
|
+
return getNetworkRequests({ failedOnly: true, limit }).requests;
|
|
613
|
+
}
|
|
614
|
+
function formatRequestsForAI(requests) {
|
|
615
|
+
if (requests.length === 0) {
|
|
616
|
+
return "No network requests captured.";
|
|
617
|
+
}
|
|
618
|
+
const formatted = requests.map((req, index) => {
|
|
619
|
+
const time = new Date(req.timestamp).toISOString();
|
|
620
|
+
const statusIcon = req.failed ? "\u274C" : "\u2705";
|
|
621
|
+
let text = `[${index + 1}] ${statusIcon} ${req.method} ${req.url}
|
|
622
|
+
`;
|
|
623
|
+
text += ` Status: ${req.status} ${req.statusText}
|
|
624
|
+
`;
|
|
625
|
+
text += ` Duration: ${req.duration}ms
|
|
626
|
+
`;
|
|
627
|
+
text += ` Time: ${time}`;
|
|
628
|
+
if (req.error) {
|
|
629
|
+
text += `
|
|
630
|
+
Error: ${req.error}`;
|
|
631
|
+
}
|
|
632
|
+
if (req.responseBody && req.failed) {
|
|
633
|
+
const bodyStr = typeof req.responseBody === "string" ? req.responseBody : JSON.stringify(req.responseBody, null, 2);
|
|
634
|
+
text += `
|
|
635
|
+
Response Body:
|
|
636
|
+
${bodyStr.split("\n").join("\n ")}`;
|
|
637
|
+
}
|
|
638
|
+
return text;
|
|
639
|
+
});
|
|
640
|
+
return `Network Requests (${requests.length} entries):
|
|
641
|
+
|
|
642
|
+
${formatted.join("\n\n")}`;
|
|
643
|
+
}
|
|
644
|
+
if (isBrowser3) {
|
|
645
|
+
window.addEventListener("beforeunload", () => {
|
|
646
|
+
stopNetworkCapture();
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// src/core/tools/intentDetector.ts
|
|
651
|
+
var SCREENSHOT_KEYWORDS = [
|
|
652
|
+
// Visual references
|
|
653
|
+
"see",
|
|
654
|
+
"seeing",
|
|
655
|
+
"look",
|
|
656
|
+
"looking",
|
|
657
|
+
"looks",
|
|
658
|
+
"appear",
|
|
659
|
+
"appears",
|
|
660
|
+
"show",
|
|
661
|
+
"showing",
|
|
662
|
+
"shows",
|
|
663
|
+
"display",
|
|
664
|
+
"displays",
|
|
665
|
+
"displayed",
|
|
666
|
+
"visible",
|
|
667
|
+
"invisible",
|
|
668
|
+
// UI elements
|
|
669
|
+
"screen",
|
|
670
|
+
"page",
|
|
671
|
+
"ui",
|
|
672
|
+
"interface",
|
|
673
|
+
"layout",
|
|
674
|
+
"design",
|
|
675
|
+
"button",
|
|
676
|
+
"input",
|
|
677
|
+
"form",
|
|
678
|
+
"modal",
|
|
679
|
+
"popup",
|
|
680
|
+
"menu",
|
|
681
|
+
"dropdown",
|
|
682
|
+
"sidebar",
|
|
683
|
+
"header",
|
|
684
|
+
"footer",
|
|
685
|
+
"navbar",
|
|
686
|
+
"navigation",
|
|
687
|
+
"image",
|
|
688
|
+
"icon",
|
|
689
|
+
"text",
|
|
690
|
+
"font",
|
|
691
|
+
"color",
|
|
692
|
+
"style",
|
|
693
|
+
"css",
|
|
694
|
+
"alignment",
|
|
695
|
+
"position",
|
|
696
|
+
"spacing",
|
|
697
|
+
// Visual issues
|
|
698
|
+
"broken",
|
|
699
|
+
"misaligned",
|
|
700
|
+
"overlapping",
|
|
701
|
+
"cutoff",
|
|
702
|
+
"overflow",
|
|
703
|
+
"blank",
|
|
704
|
+
"empty",
|
|
705
|
+
"missing",
|
|
706
|
+
"wrong",
|
|
707
|
+
"weird",
|
|
708
|
+
"strange",
|
|
709
|
+
"different",
|
|
710
|
+
"changed"
|
|
711
|
+
];
|
|
712
|
+
var CONSOLE_KEYWORDS = [
|
|
713
|
+
// Errors
|
|
714
|
+
"error",
|
|
715
|
+
"errors",
|
|
716
|
+
"exception",
|
|
717
|
+
"exceptions",
|
|
718
|
+
"crash",
|
|
719
|
+
"crashed",
|
|
720
|
+
"crashing",
|
|
721
|
+
"bug",
|
|
722
|
+
"bugs",
|
|
723
|
+
"buggy",
|
|
724
|
+
"broken",
|
|
725
|
+
"break",
|
|
726
|
+
"breaks",
|
|
727
|
+
// Issues
|
|
728
|
+
"issue",
|
|
729
|
+
"issues",
|
|
730
|
+
"problem",
|
|
731
|
+
"problems",
|
|
732
|
+
"not working",
|
|
733
|
+
"doesnt work",
|
|
734
|
+
"doesn't work",
|
|
735
|
+
"stopped working",
|
|
736
|
+
"fails",
|
|
737
|
+
"failed",
|
|
738
|
+
"failing",
|
|
739
|
+
"failure",
|
|
740
|
+
// Debug references
|
|
741
|
+
"debug",
|
|
742
|
+
"debugging",
|
|
743
|
+
"console",
|
|
744
|
+
"log",
|
|
745
|
+
"logs",
|
|
746
|
+
"warning",
|
|
747
|
+
"warnings",
|
|
748
|
+
"warn",
|
|
749
|
+
"stack",
|
|
750
|
+
"stacktrace",
|
|
751
|
+
"trace",
|
|
752
|
+
"traceback",
|
|
753
|
+
// State issues
|
|
754
|
+
"undefined",
|
|
755
|
+
"null",
|
|
756
|
+
"nan",
|
|
757
|
+
"typeerror",
|
|
758
|
+
"referenceerror",
|
|
759
|
+
"syntaxerror",
|
|
760
|
+
"unexpected",
|
|
761
|
+
"uncaught"
|
|
762
|
+
];
|
|
763
|
+
var NETWORK_KEYWORDS = [
|
|
764
|
+
// API references
|
|
765
|
+
"api",
|
|
766
|
+
"apis",
|
|
767
|
+
"endpoint",
|
|
768
|
+
"endpoints",
|
|
769
|
+
"request",
|
|
770
|
+
"requests",
|
|
771
|
+
"response",
|
|
772
|
+
"responses",
|
|
773
|
+
"fetch",
|
|
774
|
+
"fetching",
|
|
775
|
+
// HTTP
|
|
776
|
+
"http",
|
|
777
|
+
"https",
|
|
778
|
+
"rest",
|
|
779
|
+
"graphql",
|
|
780
|
+
"post",
|
|
781
|
+
"get",
|
|
782
|
+
"put",
|
|
783
|
+
"delete",
|
|
784
|
+
"patch",
|
|
785
|
+
// Issues
|
|
786
|
+
"timeout",
|
|
787
|
+
"timeouts",
|
|
788
|
+
"timed out",
|
|
789
|
+
"slow",
|
|
790
|
+
"loading",
|
|
791
|
+
"loads",
|
|
792
|
+
"load",
|
|
793
|
+
"forever",
|
|
794
|
+
// Status
|
|
795
|
+
"404",
|
|
796
|
+
"500",
|
|
797
|
+
"401",
|
|
798
|
+
"403",
|
|
799
|
+
"400",
|
|
800
|
+
"not found",
|
|
801
|
+
"unauthorized",
|
|
802
|
+
"forbidden",
|
|
803
|
+
"server error",
|
|
804
|
+
"bad request",
|
|
805
|
+
// Data
|
|
806
|
+
"data",
|
|
807
|
+
"json",
|
|
808
|
+
"payload",
|
|
809
|
+
"body",
|
|
810
|
+
"headers",
|
|
811
|
+
"cors",
|
|
812
|
+
"network",
|
|
813
|
+
"connection",
|
|
814
|
+
"backend",
|
|
815
|
+
"server"
|
|
816
|
+
];
|
|
817
|
+
var DEFAULT_THRESHOLDS = {
|
|
818
|
+
suggest: 0.3};
|
|
819
|
+
function normalizeText(text) {
|
|
820
|
+
return text.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
821
|
+
}
|
|
822
|
+
function findMatches(text, keywords) {
|
|
823
|
+
const normalizedText = normalizeText(text);
|
|
824
|
+
const words = new Set(normalizedText.split(" "));
|
|
825
|
+
const matches = [];
|
|
826
|
+
for (const keyword of keywords) {
|
|
827
|
+
const normalizedKeyword = normalizeText(keyword);
|
|
828
|
+
if (words.has(normalizedKeyword)) {
|
|
829
|
+
matches.push(keyword);
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
if (normalizedKeyword.includes(" ") && normalizedText.includes(normalizedKeyword)) {
|
|
833
|
+
matches.push(keyword);
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
const pattern = new RegExp(`\\b${normalizedKeyword}`, "i");
|
|
837
|
+
if (pattern.test(normalizedText)) {
|
|
838
|
+
matches.push(keyword);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return [...new Set(matches)];
|
|
842
|
+
}
|
|
843
|
+
function calculateConfidence(matches, totalKeywords, textLength) {
|
|
844
|
+
if (matches.length === 0) return 0;
|
|
845
|
+
const matchRatio = Math.min(matches.length / 5, 1);
|
|
846
|
+
const multiMatchBonus = matches.length > 2 ? 0.2 : matches.length > 1 ? 0.1 : 0;
|
|
847
|
+
const lengthPenalty = textLength < 20 ? 0.2 : 0;
|
|
848
|
+
return Math.min(matchRatio * 0.7 + multiMatchBonus - lengthPenalty, 1);
|
|
849
|
+
}
|
|
850
|
+
function detectIntent(message) {
|
|
851
|
+
const screenshotMatches = findMatches(message, SCREENSHOT_KEYWORDS);
|
|
852
|
+
const consoleMatches = findMatches(message, CONSOLE_KEYWORDS);
|
|
853
|
+
const networkMatches = findMatches(message, NETWORK_KEYWORDS);
|
|
854
|
+
const textLength = message.length;
|
|
855
|
+
const confidence = {
|
|
856
|
+
screenshot: calculateConfidence(
|
|
857
|
+
screenshotMatches,
|
|
858
|
+
SCREENSHOT_KEYWORDS.length,
|
|
859
|
+
textLength
|
|
860
|
+
),
|
|
861
|
+
console: calculateConfidence(
|
|
862
|
+
consoleMatches,
|
|
863
|
+
CONSOLE_KEYWORDS.length,
|
|
864
|
+
textLength
|
|
865
|
+
),
|
|
866
|
+
network: calculateConfidence(
|
|
867
|
+
networkMatches,
|
|
868
|
+
NETWORK_KEYWORDS.length,
|
|
869
|
+
textLength
|
|
870
|
+
)
|
|
871
|
+
};
|
|
872
|
+
const suggestedTools = [];
|
|
873
|
+
if (confidence.screenshot >= DEFAULT_THRESHOLDS.suggest) {
|
|
874
|
+
suggestedTools.push("screenshot");
|
|
875
|
+
}
|
|
876
|
+
if (confidence.console >= DEFAULT_THRESHOLDS.suggest) {
|
|
877
|
+
suggestedTools.push("console");
|
|
878
|
+
}
|
|
879
|
+
if (confidence.network >= DEFAULT_THRESHOLDS.suggest) {
|
|
880
|
+
suggestedTools.push("network");
|
|
881
|
+
}
|
|
882
|
+
suggestedTools.sort((a, b) => confidence[b] - confidence[a]);
|
|
883
|
+
return {
|
|
884
|
+
suggestedTools,
|
|
885
|
+
confidence,
|
|
886
|
+
matchedKeywords: {
|
|
887
|
+
screenshot: screenshotMatches,
|
|
888
|
+
console: consoleMatches,
|
|
889
|
+
network: networkMatches
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
function hasToolSuggestions(message) {
|
|
894
|
+
const result = detectIntent(message);
|
|
895
|
+
return result.suggestedTools.length > 0;
|
|
896
|
+
}
|
|
897
|
+
function getPrimaryTool(message) {
|
|
898
|
+
const result = detectIntent(message);
|
|
899
|
+
return result.suggestedTools[0] || null;
|
|
900
|
+
}
|
|
901
|
+
function generateSuggestionReason(result) {
|
|
902
|
+
if (result.suggestedTools.length === 0) {
|
|
903
|
+
return "";
|
|
904
|
+
}
|
|
905
|
+
const reasons = [];
|
|
906
|
+
if (result.suggestedTools.includes("screenshot")) {
|
|
907
|
+
const keywords = result.matchedKeywords.screenshot.slice(0, 3);
|
|
908
|
+
reasons.push(`visual elements mentioned (${keywords.join(", ")})`);
|
|
909
|
+
}
|
|
910
|
+
if (result.suggestedTools.includes("console")) {
|
|
911
|
+
const keywords = result.matchedKeywords.console.slice(0, 3);
|
|
912
|
+
reasons.push(`potential errors indicated (${keywords.join(", ")})`);
|
|
913
|
+
}
|
|
914
|
+
if (result.suggestedTools.includes("network")) {
|
|
915
|
+
const keywords = result.matchedKeywords.network.slice(0, 3);
|
|
916
|
+
reasons.push(`API/network issues suggested (${keywords.join(", ")})`);
|
|
917
|
+
}
|
|
918
|
+
return `Based on your message: ${reasons.join("; ")}.`;
|
|
919
|
+
}
|
|
920
|
+
function createCustomDetector(customKeywords) {
|
|
921
|
+
const extendedScreenshot = [
|
|
922
|
+
...SCREENSHOT_KEYWORDS,
|
|
923
|
+
...customKeywords.screenshot || []
|
|
924
|
+
];
|
|
925
|
+
const extendedConsole = [
|
|
926
|
+
...CONSOLE_KEYWORDS,
|
|
927
|
+
...customKeywords.console || []
|
|
928
|
+
];
|
|
929
|
+
const extendedNetwork = [
|
|
930
|
+
...NETWORK_KEYWORDS,
|
|
931
|
+
...customKeywords.network || []
|
|
932
|
+
];
|
|
933
|
+
return function detectCustomIntent(message) {
|
|
934
|
+
const screenshotMatches = findMatches(message, extendedScreenshot);
|
|
935
|
+
const consoleMatches = findMatches(message, extendedConsole);
|
|
936
|
+
const networkMatches = findMatches(message, extendedNetwork);
|
|
937
|
+
const textLength = message.length;
|
|
938
|
+
const confidence = {
|
|
939
|
+
screenshot: calculateConfidence(
|
|
940
|
+
screenshotMatches,
|
|
941
|
+
extendedScreenshot.length,
|
|
942
|
+
textLength
|
|
943
|
+
),
|
|
944
|
+
console: calculateConfidence(
|
|
945
|
+
consoleMatches,
|
|
946
|
+
extendedConsole.length,
|
|
947
|
+
textLength
|
|
948
|
+
),
|
|
949
|
+
network: calculateConfidence(
|
|
950
|
+
networkMatches,
|
|
951
|
+
extendedNetwork.length,
|
|
952
|
+
textLength
|
|
953
|
+
)
|
|
954
|
+
};
|
|
955
|
+
const suggestedTools = [];
|
|
956
|
+
if (confidence.screenshot >= DEFAULT_THRESHOLDS.suggest) {
|
|
957
|
+
suggestedTools.push("screenshot");
|
|
958
|
+
}
|
|
959
|
+
if (confidence.console >= DEFAULT_THRESHOLDS.suggest) {
|
|
960
|
+
suggestedTools.push("console");
|
|
961
|
+
}
|
|
962
|
+
if (confidence.network >= DEFAULT_THRESHOLDS.suggest) {
|
|
963
|
+
suggestedTools.push("network");
|
|
964
|
+
}
|
|
965
|
+
suggestedTools.sort((a, b) => confidence[b] - confidence[a]);
|
|
966
|
+
return {
|
|
967
|
+
suggestedTools,
|
|
968
|
+
confidence,
|
|
969
|
+
matchedKeywords: {
|
|
970
|
+
screenshot: screenshotMatches,
|
|
971
|
+
console: consoleMatches,
|
|
972
|
+
network: networkMatches
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// src/core/types/tools.ts
|
|
979
|
+
function tool(config) {
|
|
980
|
+
return {
|
|
981
|
+
description: config.description,
|
|
982
|
+
location: config.location ?? "client",
|
|
983
|
+
// Display configuration
|
|
984
|
+
title: config.title,
|
|
985
|
+
executingTitle: config.executingTitle,
|
|
986
|
+
completedTitle: config.completedTitle,
|
|
987
|
+
// Schema and handlers
|
|
988
|
+
inputSchema: config.inputSchema ?? {
|
|
989
|
+
type: "object",
|
|
990
|
+
properties: {},
|
|
991
|
+
required: []
|
|
992
|
+
},
|
|
993
|
+
handler: config.handler,
|
|
994
|
+
render: config.render,
|
|
995
|
+
available: config.available,
|
|
996
|
+
needsApproval: config.needsApproval,
|
|
997
|
+
approvalMessage: config.approvalMessage,
|
|
998
|
+
aiResponseMode: config.aiResponseMode,
|
|
999
|
+
aiContext: config.aiContext
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
function toolToOpenAIFormat(tool2) {
|
|
1003
|
+
return {
|
|
1004
|
+
type: "function",
|
|
1005
|
+
function: {
|
|
1006
|
+
name: tool2.name,
|
|
1007
|
+
description: tool2.description,
|
|
1008
|
+
parameters: tool2.inputSchema
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
function toolToAnthropicFormat(tool2) {
|
|
1013
|
+
return {
|
|
1014
|
+
name: tool2.name,
|
|
1015
|
+
description: tool2.description,
|
|
1016
|
+
input_schema: tool2.inputSchema
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
function createToolResult(toolCallId, response) {
|
|
1020
|
+
return {
|
|
1021
|
+
toolCallId,
|
|
1022
|
+
content: JSON.stringify(response),
|
|
1023
|
+
success: response.success,
|
|
1024
|
+
error: response.error
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
function success(data, message) {
|
|
1028
|
+
return {
|
|
1029
|
+
success: true,
|
|
1030
|
+
data,
|
|
1031
|
+
message
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
function failure(error) {
|
|
1035
|
+
return {
|
|
1036
|
+
success: false,
|
|
1037
|
+
error
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// src/core/tools/builtin/screenshot.ts
|
|
1042
|
+
var screenshotTool = tool({
|
|
1043
|
+
description: "Capture a screenshot of the user's current screen/viewport. Use this when the user asks you to look at their screen, see what they're seeing, help with visual issues, or debug UI problems.",
|
|
1044
|
+
location: "client",
|
|
1045
|
+
inputSchema: {
|
|
1046
|
+
type: "object",
|
|
1047
|
+
properties: {
|
|
1048
|
+
quality: {
|
|
1049
|
+
type: "number",
|
|
1050
|
+
description: "Image quality from 0 to 1 (default: 0.8)",
|
|
1051
|
+
minimum: 0,
|
|
1052
|
+
maximum: 1
|
|
1053
|
+
}
|
|
1054
|
+
},
|
|
1055
|
+
required: []
|
|
1056
|
+
},
|
|
1057
|
+
needsApproval: true,
|
|
1058
|
+
approvalMessage: "Allow AI to capture a screenshot of your screen?",
|
|
1059
|
+
handler: async (params) => {
|
|
1060
|
+
if (!isScreenshotSupported()) {
|
|
1061
|
+
return failure("Screenshot capture is not supported in this environment");
|
|
1062
|
+
}
|
|
1063
|
+
try {
|
|
1064
|
+
const options = {};
|
|
1065
|
+
if (params.quality !== void 0) {
|
|
1066
|
+
options.quality = params.quality;
|
|
1067
|
+
}
|
|
1068
|
+
const result = await captureScreenshot(options);
|
|
1069
|
+
return {
|
|
1070
|
+
success: true,
|
|
1071
|
+
message: `Screenshot captured (${result.width}x${result.height}). The image is shared in the conversation.`,
|
|
1072
|
+
addAsUserMessage: true,
|
|
1073
|
+
data: {
|
|
1074
|
+
attachment: {
|
|
1075
|
+
type: "image",
|
|
1076
|
+
data: result.data,
|
|
1077
|
+
mimeType: `image/${result.format}`,
|
|
1078
|
+
filename: "screenshot.png"
|
|
1079
|
+
},
|
|
1080
|
+
dimensions: {
|
|
1081
|
+
width: result.width,
|
|
1082
|
+
height: result.height
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
} catch (error) {
|
|
1087
|
+
return failure(
|
|
1088
|
+
error instanceof Error ? error.message : "Screenshot capture failed"
|
|
1089
|
+
);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
function createScreenshotTool(options) {
|
|
1094
|
+
return tool({
|
|
1095
|
+
...screenshotTool,
|
|
1096
|
+
needsApproval: options?.needsApproval ?? true,
|
|
1097
|
+
approvalMessage: options?.approvalMessage ?? "Allow AI to capture a screenshot of your screen?",
|
|
1098
|
+
handler: async (params) => {
|
|
1099
|
+
if (!isScreenshotSupported()) {
|
|
1100
|
+
return failure(
|
|
1101
|
+
"Screenshot capture is not supported in this environment"
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
try {
|
|
1105
|
+
const result = await captureScreenshot({
|
|
1106
|
+
quality: params.quality ?? options?.defaultQuality ?? 0.8
|
|
1107
|
+
});
|
|
1108
|
+
return {
|
|
1109
|
+
success: true,
|
|
1110
|
+
message: `Screenshot captured (${result.width}x${result.height}). The image is shared in the conversation.`,
|
|
1111
|
+
addAsUserMessage: true,
|
|
1112
|
+
data: {
|
|
1113
|
+
attachment: {
|
|
1114
|
+
type: "image",
|
|
1115
|
+
data: result.data,
|
|
1116
|
+
mimeType: `image/${result.format}`,
|
|
1117
|
+
filename: "screenshot.png"
|
|
1118
|
+
},
|
|
1119
|
+
dimensions: {
|
|
1120
|
+
width: result.width,
|
|
1121
|
+
height: result.height
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
} catch (error) {
|
|
1126
|
+
return failure(
|
|
1127
|
+
error instanceof Error ? error.message : "Screenshot capture failed"
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// src/core/tools/builtin/console.ts
|
|
1135
|
+
var consoleLogsTool = tool({
|
|
1136
|
+
description: "Get recent console logs from the browser. Use this when debugging JavaScript errors, checking for warnings, or understanding what's happening in the application.",
|
|
1137
|
+
location: "client",
|
|
1138
|
+
inputSchema: {
|
|
1139
|
+
type: "object",
|
|
1140
|
+
properties: {
|
|
1141
|
+
limit: {
|
|
1142
|
+
type: "number",
|
|
1143
|
+
description: "Maximum number of logs to return (default: 50)"
|
|
1144
|
+
},
|
|
1145
|
+
types: {
|
|
1146
|
+
type: "array",
|
|
1147
|
+
description: "Filter by log types: 'error', 'warn', 'info', 'log', 'debug'",
|
|
1148
|
+
items: {
|
|
1149
|
+
type: "string",
|
|
1150
|
+
enum: ["error", "warn", "info", "log", "debug"]
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
},
|
|
1154
|
+
required: []
|
|
1155
|
+
},
|
|
1156
|
+
needsApproval: true,
|
|
1157
|
+
approvalMessage: "Allow AI to access browser console logs?",
|
|
1158
|
+
handler: async (params) => {
|
|
1159
|
+
try {
|
|
1160
|
+
if (!isConsoleCaptureActive()) {
|
|
1161
|
+
startConsoleCapture();
|
|
1162
|
+
}
|
|
1163
|
+
const logs = getConsoleLogs({
|
|
1164
|
+
limit: params.limit || 50,
|
|
1165
|
+
types: params.types
|
|
1166
|
+
});
|
|
1167
|
+
const formattedLogs = formatLogsForAI(logs.logs);
|
|
1168
|
+
return success(
|
|
1169
|
+
{
|
|
1170
|
+
logs: formattedLogs,
|
|
1171
|
+
count: logs.logs.length,
|
|
1172
|
+
totalCaptured: logs.totalCaptured
|
|
1173
|
+
},
|
|
1174
|
+
`Retrieved ${logs.logs.length} console logs`
|
|
1175
|
+
);
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
return failure(
|
|
1178
|
+
error instanceof Error ? error.message : "Failed to get console logs"
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
});
|
|
1183
|
+
function createConsoleLogsTool(options) {
|
|
1184
|
+
return tool({
|
|
1185
|
+
...consoleLogsTool,
|
|
1186
|
+
needsApproval: options?.needsApproval ?? true,
|
|
1187
|
+
approvalMessage: options?.approvalMessage ?? "Allow AI to access browser console logs?",
|
|
1188
|
+
handler: async (params) => {
|
|
1189
|
+
try {
|
|
1190
|
+
if (!isConsoleCaptureActive()) {
|
|
1191
|
+
startConsoleCapture();
|
|
1192
|
+
}
|
|
1193
|
+
const logs = getConsoleLogs({
|
|
1194
|
+
limit: params.limit || options?.defaultLimit || 50,
|
|
1195
|
+
types: params.types
|
|
1196
|
+
});
|
|
1197
|
+
const formattedLogs = formatLogsForAI(logs.logs);
|
|
1198
|
+
return success(
|
|
1199
|
+
{
|
|
1200
|
+
logs: formattedLogs,
|
|
1201
|
+
count: logs.logs.length,
|
|
1202
|
+
totalCaptured: logs.totalCaptured
|
|
1203
|
+
},
|
|
1204
|
+
`Retrieved ${logs.logs.length} console logs`
|
|
1205
|
+
);
|
|
1206
|
+
} catch (error) {
|
|
1207
|
+
return failure(
|
|
1208
|
+
error instanceof Error ? error.message : "Failed to get console logs"
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// src/core/tools/builtin/network.ts
|
|
1216
|
+
var networkRequestsTool = tool({
|
|
1217
|
+
description: "Get recent network requests from the browser. Use this when debugging API calls, checking for failed requests, analyzing network activity, or troubleshooting connectivity issues.",
|
|
1218
|
+
location: "client",
|
|
1219
|
+
inputSchema: {
|
|
1220
|
+
type: "object",
|
|
1221
|
+
properties: {
|
|
1222
|
+
limit: {
|
|
1223
|
+
type: "number",
|
|
1224
|
+
description: "Maximum number of requests to return (default: 20)"
|
|
1225
|
+
},
|
|
1226
|
+
failedOnly: {
|
|
1227
|
+
type: "boolean",
|
|
1228
|
+
description: "Only return failed requests (status >= 400)"
|
|
1229
|
+
}
|
|
1230
|
+
},
|
|
1231
|
+
required: []
|
|
1232
|
+
},
|
|
1233
|
+
needsApproval: true,
|
|
1234
|
+
approvalMessage: "Allow AI to access network request history?",
|
|
1235
|
+
handler: async (params) => {
|
|
1236
|
+
try {
|
|
1237
|
+
if (!isNetworkCaptureActive()) {
|
|
1238
|
+
startNetworkCapture();
|
|
1239
|
+
}
|
|
1240
|
+
const requests = getNetworkRequests({
|
|
1241
|
+
limit: params.limit || 20,
|
|
1242
|
+
failedOnly: params.failedOnly
|
|
1243
|
+
});
|
|
1244
|
+
const formattedRequests = formatRequestsForAI(requests.requests);
|
|
1245
|
+
return success(
|
|
1246
|
+
{
|
|
1247
|
+
requests: formattedRequests,
|
|
1248
|
+
count: requests.requests.length,
|
|
1249
|
+
totalCaptured: requests.totalCaptured
|
|
1250
|
+
},
|
|
1251
|
+
`Retrieved ${requests.requests.length} network requests`
|
|
1252
|
+
);
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
return failure(
|
|
1255
|
+
error instanceof Error ? error.message : "Failed to get network requests"
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
});
|
|
1260
|
+
function createNetworkRequestsTool(options) {
|
|
1261
|
+
return tool({
|
|
1262
|
+
...networkRequestsTool,
|
|
1263
|
+
needsApproval: options?.needsApproval ?? true,
|
|
1264
|
+
approvalMessage: options?.approvalMessage ?? "Allow AI to access network request history?",
|
|
1265
|
+
handler: async (params) => {
|
|
1266
|
+
try {
|
|
1267
|
+
if (!isNetworkCaptureActive()) {
|
|
1268
|
+
startNetworkCapture();
|
|
1269
|
+
}
|
|
1270
|
+
const requests = getNetworkRequests({
|
|
1271
|
+
limit: params.limit || options?.defaultLimit || 20,
|
|
1272
|
+
failedOnly: params.failedOnly
|
|
1273
|
+
});
|
|
1274
|
+
const formattedRequests = formatRequestsForAI(requests.requests);
|
|
1275
|
+
return success(
|
|
1276
|
+
{
|
|
1277
|
+
requests: formattedRequests,
|
|
1278
|
+
count: requests.requests.length,
|
|
1279
|
+
totalCaptured: requests.totalCaptured
|
|
1280
|
+
},
|
|
1281
|
+
`Retrieved ${requests.requests.length} network requests`
|
|
1282
|
+
);
|
|
1283
|
+
} catch (error) {
|
|
1284
|
+
return failure(
|
|
1285
|
+
error instanceof Error ? error.message : "Failed to get network requests"
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// src/core/tools/builtin/index.ts
|
|
1293
|
+
var builtinTools = {
|
|
1294
|
+
capture_screenshot: {
|
|
1295
|
+
name: "capture_screenshot",
|
|
1296
|
+
...screenshotTool
|
|
1297
|
+
},
|
|
1298
|
+
get_console_logs: {
|
|
1299
|
+
name: "get_console_logs",
|
|
1300
|
+
...consoleLogsTool
|
|
1301
|
+
},
|
|
1302
|
+
get_network_requests: {
|
|
1303
|
+
name: "get_network_requests",
|
|
1304
|
+
...networkRequestsTool
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
|
|
1308
|
+
// src/core/utils/id.ts
|
|
1309
|
+
function generateId(prefix = "id") {
|
|
1310
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
1311
|
+
}
|
|
1312
|
+
function generateMessageId() {
|
|
1313
|
+
return generateId("msg");
|
|
1314
|
+
}
|
|
1315
|
+
function generateThreadId() {
|
|
1316
|
+
return generateId("thread");
|
|
1317
|
+
}
|
|
1318
|
+
function generateToolCallId() {
|
|
1319
|
+
return generateId("call");
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// src/core/types/message.ts
|
|
1323
|
+
function parseToolCallArgs(toolCall) {
|
|
1324
|
+
try {
|
|
1325
|
+
return JSON.parse(toolCall.function.arguments);
|
|
1326
|
+
} catch {
|
|
1327
|
+
return {};
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function createToolCall(id, name, args) {
|
|
1331
|
+
return {
|
|
1332
|
+
id,
|
|
1333
|
+
type: "function",
|
|
1334
|
+
function: {
|
|
1335
|
+
name,
|
|
1336
|
+
arguments: JSON.stringify(args)
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
function createMessage(partial) {
|
|
1341
|
+
return {
|
|
1342
|
+
id: partial.id ?? generateMessageId(),
|
|
1343
|
+
thread_id: partial.thread_id,
|
|
1344
|
+
role: partial.role,
|
|
1345
|
+
content: partial.content ?? null,
|
|
1346
|
+
tool_calls: partial.tool_calls,
|
|
1347
|
+
tool_call_id: partial.tool_call_id,
|
|
1348
|
+
metadata: partial.metadata,
|
|
1349
|
+
created_at: partial.created_at ?? /* @__PURE__ */ new Date()
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
function createUserMessage(content, options) {
|
|
1353
|
+
return createMessage({
|
|
1354
|
+
id: options?.id,
|
|
1355
|
+
thread_id: options?.thread_id,
|
|
1356
|
+
role: "user",
|
|
1357
|
+
content,
|
|
1358
|
+
metadata: options?.attachments ? { attachments: options.attachments } : void 0
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
function createAssistantMessage(content, options) {
|
|
1362
|
+
const metadata = {};
|
|
1363
|
+
if (options?.thinking) metadata.thinking = options.thinking;
|
|
1364
|
+
if (options?.sources) metadata.sources = options.sources;
|
|
1365
|
+
if (options?.model) metadata.model = options.model;
|
|
1366
|
+
return createMessage({
|
|
1367
|
+
id: options?.id,
|
|
1368
|
+
thread_id: options?.thread_id,
|
|
1369
|
+
role: "assistant",
|
|
1370
|
+
content,
|
|
1371
|
+
tool_calls: options?.tool_calls,
|
|
1372
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
function createToolMessage(toolCallId, result, options) {
|
|
1376
|
+
return createMessage({
|
|
1377
|
+
id: options?.id,
|
|
1378
|
+
thread_id: options?.thread_id,
|
|
1379
|
+
role: "tool",
|
|
1380
|
+
content: JSON.stringify(result),
|
|
1381
|
+
tool_call_id: toolCallId
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
function hasToolCalls(message) {
|
|
1385
|
+
return message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
|
|
1386
|
+
}
|
|
1387
|
+
function isToolResult(message) {
|
|
1388
|
+
return message.role === "tool" && !!message.tool_call_id;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// src/core/types/config.ts
|
|
1392
|
+
var DEFAULT_MODELS = {
|
|
1393
|
+
openai: "gpt-4o",
|
|
1394
|
+
anthropic: "claude-3-5-sonnet-latest",
|
|
1395
|
+
google: "gemini-pro",
|
|
1396
|
+
groq: "llama-3.1-70b-versatile",
|
|
1397
|
+
ollama: "llama3",
|
|
1398
|
+
custom: "default"
|
|
1399
|
+
};
|
|
1400
|
+
function getDefaultModel(provider) {
|
|
1401
|
+
return DEFAULT_MODELS[provider] || "default";
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// src/core/types/actions.ts
|
|
1405
|
+
function actionToTool(action) {
|
|
1406
|
+
const properties = {};
|
|
1407
|
+
const required = [];
|
|
1408
|
+
if (action.parameters) {
|
|
1409
|
+
for (const [name, param] of Object.entries(action.parameters)) {
|
|
1410
|
+
properties[name] = parameterToJsonSchema(param);
|
|
1411
|
+
if (param.required) {
|
|
1412
|
+
required.push(name);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
type: "function",
|
|
1418
|
+
function: {
|
|
1419
|
+
name: action.name,
|
|
1420
|
+
description: action.description,
|
|
1421
|
+
parameters: {
|
|
1422
|
+
type: "object",
|
|
1423
|
+
properties,
|
|
1424
|
+
required: required.length > 0 ? required : void 0
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
function parameterToJsonSchema(param) {
|
|
1430
|
+
const schema = {
|
|
1431
|
+
type: param.type
|
|
1432
|
+
};
|
|
1433
|
+
if (param.description) {
|
|
1434
|
+
schema.description = param.description;
|
|
1435
|
+
}
|
|
1436
|
+
if (param.enum) {
|
|
1437
|
+
schema.enum = param.enum;
|
|
1438
|
+
}
|
|
1439
|
+
if (param.default !== void 0) {
|
|
1440
|
+
schema.default = param.default;
|
|
1441
|
+
}
|
|
1442
|
+
if (param.properties) {
|
|
1443
|
+
schema.properties = {};
|
|
1444
|
+
for (const [name, prop] of Object.entries(param.properties)) {
|
|
1445
|
+
schema.properties[name] = parameterToJsonSchema(prop);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
if (param.items) {
|
|
1449
|
+
schema.items = parameterToJsonSchema(param.items);
|
|
1450
|
+
}
|
|
1451
|
+
return schema;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// src/core/types/events.ts
|
|
1455
|
+
function parseStreamEvent(data) {
|
|
1456
|
+
try {
|
|
1457
|
+
return JSON.parse(data);
|
|
1458
|
+
} catch {
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
function serializeStreamEvent(event) {
|
|
1463
|
+
return JSON.stringify(event);
|
|
1464
|
+
}
|
|
1465
|
+
function formatSSE(event) {
|
|
1466
|
+
return `data: ${serializeStreamEvent(event)}
|
|
1467
|
+
|
|
1468
|
+
`;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// src/core/types/thread.ts
|
|
1472
|
+
function generateThreadTitle(content) {
|
|
1473
|
+
const trimmed = content.trim();
|
|
1474
|
+
if (trimmed.length <= 50) return trimmed;
|
|
1475
|
+
return trimmed.substring(0, 47) + "...";
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// src/core/utils/stream.ts
|
|
1479
|
+
function parseSSELine(line) {
|
|
1480
|
+
if (!line.startsWith("data: ")) {
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
const data = line.slice(6).trim();
|
|
1484
|
+
if (data === "[DONE]") {
|
|
1485
|
+
return { type: "done" };
|
|
1486
|
+
}
|
|
1487
|
+
try {
|
|
1488
|
+
return JSON.parse(data);
|
|
1489
|
+
} catch {
|
|
1490
|
+
return null;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
async function* streamSSE(response) {
|
|
1494
|
+
if (!response.body) {
|
|
1495
|
+
throw new Error("Response body is null");
|
|
1496
|
+
}
|
|
1497
|
+
const reader = response.body.getReader();
|
|
1498
|
+
const decoder = new TextDecoder();
|
|
1499
|
+
let buffer = "";
|
|
1500
|
+
try {
|
|
1501
|
+
while (true) {
|
|
1502
|
+
const { done, value } = await reader.read();
|
|
1503
|
+
if (done) {
|
|
1504
|
+
break;
|
|
1505
|
+
}
|
|
1506
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1507
|
+
const lines = buffer.split("\n");
|
|
1508
|
+
buffer = lines.pop() || "";
|
|
1509
|
+
for (const line of lines) {
|
|
1510
|
+
const event = parseSSELine(line);
|
|
1511
|
+
if (event) {
|
|
1512
|
+
yield event;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
if (buffer.trim()) {
|
|
1517
|
+
const event = parseSSELine(buffer);
|
|
1518
|
+
if (event) {
|
|
1519
|
+
yield event;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
} finally {
|
|
1523
|
+
reader.releaseLock();
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
function createSSEStream(generator) {
|
|
1527
|
+
const encoder = new TextEncoder();
|
|
1528
|
+
return new ReadableStream({
|
|
1529
|
+
async start(controller) {
|
|
1530
|
+
try {
|
|
1531
|
+
for await (const event of generator) {
|
|
1532
|
+
const data = `data: ${JSON.stringify(event)}
|
|
1533
|
+
|
|
1534
|
+
`;
|
|
1535
|
+
controller.enqueue(encoder.encode(data));
|
|
1536
|
+
}
|
|
1537
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
1538
|
+
controller.close();
|
|
1539
|
+
} catch (error) {
|
|
1540
|
+
const errorEvent = {
|
|
1541
|
+
type: "error",
|
|
1542
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
1543
|
+
};
|
|
1544
|
+
controller.enqueue(
|
|
1545
|
+
encoder.encode(`data: ${JSON.stringify(errorEvent)}
|
|
1546
|
+
|
|
1547
|
+
`)
|
|
1548
|
+
);
|
|
1549
|
+
controller.close();
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
// src/core/utils/zod-to-json-schema.ts
|
|
1556
|
+
function isZodSchema(value) {
|
|
1557
|
+
return value !== null && typeof value === "object" && "_def" in value && typeof value._def === "object";
|
|
1558
|
+
}
|
|
1559
|
+
function getZodTypeName(schema) {
|
|
1560
|
+
if (!isZodSchema(schema)) return "unknown";
|
|
1561
|
+
const def = schema._def;
|
|
1562
|
+
return def.typeName || "unknown";
|
|
1563
|
+
}
|
|
1564
|
+
function getZodDescription(schema) {
|
|
1565
|
+
if (!isZodSchema(schema)) return void 0;
|
|
1566
|
+
const def = schema._def;
|
|
1567
|
+
return def.description;
|
|
1568
|
+
}
|
|
1569
|
+
function zodToJsonSchema(schema) {
|
|
1570
|
+
if (!isZodSchema(schema)) {
|
|
1571
|
+
return { type: "string" };
|
|
1572
|
+
}
|
|
1573
|
+
const typeName = getZodTypeName(schema);
|
|
1574
|
+
const description = getZodDescription(schema);
|
|
1575
|
+
const def = schema._def;
|
|
1576
|
+
switch (typeName) {
|
|
1577
|
+
case "ZodString": {
|
|
1578
|
+
const result2 = { type: "string" };
|
|
1579
|
+
if (description) result2.description = description;
|
|
1580
|
+
const checks = def.checks;
|
|
1581
|
+
if (checks) {
|
|
1582
|
+
for (const check of checks) {
|
|
1583
|
+
if (check.kind === "min") result2.minLength = check.value;
|
|
1584
|
+
if (check.kind === "max") result2.maxLength = check.value;
|
|
1585
|
+
if (check.kind === "regex") result2.pattern = String(check.value);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
return result2;
|
|
1589
|
+
}
|
|
1590
|
+
case "ZodNumber": {
|
|
1591
|
+
const result2 = { type: "number" };
|
|
1592
|
+
if (description) result2.description = description;
|
|
1593
|
+
const checks = def.checks;
|
|
1594
|
+
if (checks) {
|
|
1595
|
+
for (const check of checks) {
|
|
1596
|
+
if (check.kind === "min") result2.minimum = check.value;
|
|
1597
|
+
if (check.kind === "max") result2.maximum = check.value;
|
|
1598
|
+
if (check.kind === "int") result2.type = "integer";
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
return result2;
|
|
1602
|
+
}
|
|
1603
|
+
case "ZodBoolean": {
|
|
1604
|
+
const result2 = { type: "boolean" };
|
|
1605
|
+
if (description) result2.description = description;
|
|
1606
|
+
return result2;
|
|
1607
|
+
}
|
|
1608
|
+
case "ZodEnum": {
|
|
1609
|
+
const values = def.values;
|
|
1610
|
+
const result2 = {
|
|
1611
|
+
type: typeof values[0] === "number" ? "number" : "string",
|
|
1612
|
+
enum: values
|
|
1613
|
+
};
|
|
1614
|
+
if (description) result2.description = description;
|
|
1615
|
+
return result2;
|
|
1616
|
+
}
|
|
1617
|
+
case "ZodArray": {
|
|
1618
|
+
const innerType = def.type;
|
|
1619
|
+
const result2 = {
|
|
1620
|
+
type: "array",
|
|
1621
|
+
items: zodToJsonSchema(innerType)
|
|
1622
|
+
};
|
|
1623
|
+
if (description) result2.description = description;
|
|
1624
|
+
return result2;
|
|
1625
|
+
}
|
|
1626
|
+
case "ZodObject": {
|
|
1627
|
+
const shape = def.shape;
|
|
1628
|
+
const shapeObj = typeof shape === "function" ? shape() : shape;
|
|
1629
|
+
const properties = {};
|
|
1630
|
+
const required = [];
|
|
1631
|
+
for (const [key, value] of Object.entries(shapeObj)) {
|
|
1632
|
+
properties[key] = zodToJsonSchema(value);
|
|
1633
|
+
const fieldTypeName = getZodTypeName(value);
|
|
1634
|
+
if (fieldTypeName !== "ZodOptional" && fieldTypeName !== "ZodNullable") {
|
|
1635
|
+
required.push(key);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
const result2 = {
|
|
1639
|
+
type: "object",
|
|
1640
|
+
properties
|
|
1641
|
+
};
|
|
1642
|
+
if (required.length > 0) result2.required = required;
|
|
1643
|
+
if (description) result2.description = description;
|
|
1644
|
+
return result2;
|
|
1645
|
+
}
|
|
1646
|
+
case "ZodOptional":
|
|
1647
|
+
case "ZodNullable": {
|
|
1648
|
+
const innerType = def.innerType;
|
|
1649
|
+
return zodToJsonSchema(innerType);
|
|
1650
|
+
}
|
|
1651
|
+
case "ZodDefault": {
|
|
1652
|
+
const innerType = def.innerType;
|
|
1653
|
+
const defaultValue = def.defaultValue;
|
|
1654
|
+
const result2 = zodToJsonSchema(innerType);
|
|
1655
|
+
result2.default = typeof defaultValue === "function" ? defaultValue() : defaultValue;
|
|
1656
|
+
return result2;
|
|
1657
|
+
}
|
|
1658
|
+
case "ZodLiteral": {
|
|
1659
|
+
const value = def.value;
|
|
1660
|
+
return {
|
|
1661
|
+
type: typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : "string",
|
|
1662
|
+
enum: [value],
|
|
1663
|
+
description
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
case "ZodUnion": {
|
|
1667
|
+
const options = def.options;
|
|
1668
|
+
if (options && options.length > 0) {
|
|
1669
|
+
return zodToJsonSchema(options[0]);
|
|
1670
|
+
}
|
|
1671
|
+
return { type: "string" };
|
|
1672
|
+
}
|
|
1673
|
+
default:
|
|
1674
|
+
const result = { type: "string" };
|
|
1675
|
+
if (description) result.description = description;
|
|
1676
|
+
return result;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
function zodObjectToInputSchema(schema) {
|
|
1680
|
+
const jsonSchema = zodToJsonSchema(schema);
|
|
1681
|
+
if (jsonSchema.type !== "object" || !jsonSchema.properties) {
|
|
1682
|
+
const typeName = getZodTypeName(schema);
|
|
1683
|
+
throw new Error(
|
|
1684
|
+
`Expected a Zod object schema, got ${typeName}. Converted to: ${JSON.stringify(jsonSchema)}`
|
|
1685
|
+
);
|
|
1686
|
+
}
|
|
1687
|
+
return {
|
|
1688
|
+
type: "object",
|
|
1689
|
+
properties: jsonSchema.properties,
|
|
1690
|
+
required: jsonSchema.required
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
function defineTool(config) {
|
|
1694
|
+
return {
|
|
1695
|
+
name: config.name,
|
|
1696
|
+
description: config.description,
|
|
1697
|
+
location: config.location,
|
|
1698
|
+
inputSchema: zodObjectToInputSchema(config.schema),
|
|
1699
|
+
handler: config.handler,
|
|
1700
|
+
render: config.render,
|
|
1701
|
+
available: config.available,
|
|
1702
|
+
// Display config
|
|
1703
|
+
title: config.title,
|
|
1704
|
+
executingTitle: config.executingTitle,
|
|
1705
|
+
completedTitle: config.completedTitle,
|
|
1706
|
+
// AI response control
|
|
1707
|
+
aiResponseMode: config.aiResponseMode,
|
|
1708
|
+
aiContext: config.aiContext,
|
|
1709
|
+
// Approval
|
|
1710
|
+
needsApproval: config.needsApproval,
|
|
1711
|
+
approvalMessage: config.approvalMessage
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
function defineClientTool(config) {
|
|
1715
|
+
return defineTool({ ...config, location: "client" });
|
|
1716
|
+
}
|
|
1717
|
+
function defineServerTool(config) {
|
|
1718
|
+
return defineTool({ ...config, location: "server" });
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// src/core/system-prompt.ts
|
|
1722
|
+
function defaultSystemMessage(contextString, additionalInstructions) {
|
|
1723
|
+
return `
|
|
1724
|
+
You are a helpful, efficient, and professional AI assistant.
|
|
1725
|
+
|
|
1726
|
+
Help the user achieve their goals efficiently, without unnecessary fluff, while maintaining professionalism.
|
|
1727
|
+
Be polite, respectful, and prefer brevity over verbosity.
|
|
1728
|
+
|
|
1729
|
+
${contextString ? `The user has provided you with the following context:
|
|
1730
|
+
\`\`\`
|
|
1731
|
+
${contextString}
|
|
1732
|
+
\`\`\`
|
|
1733
|
+
|
|
1734
|
+
` : ""}You have access to functions you can call to take actions or get more information.
|
|
1735
|
+
|
|
1736
|
+
Assist the user as best you can. Ask clarifying questions if needed, but if you can reasonably fill in the blanks yourself, do so.
|
|
1737
|
+
|
|
1738
|
+
When calling a function, call it without extra commentary.
|
|
1739
|
+
If a function returns an error:
|
|
1740
|
+
- If the error is from incorrect parameters, you may retry with corrected arguments.
|
|
1741
|
+
- If the error source is unclear, do not retry.
|
|
1742
|
+
` + (additionalInstructions ? `
|
|
1743
|
+
${additionalInstructions}` : "");
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// src/core/services/storage.ts
|
|
1747
|
+
var CLOUD_MAX_FILE_SIZE = 25 * 1024 * 1024;
|
|
1748
|
+
var DEFAULT_YOURGPT_ENDPOINT = "https://api.yourgpt.ai";
|
|
1749
|
+
function createCloudStorage(config) {
|
|
1750
|
+
const endpoint = config.endpoint || DEFAULT_YOURGPT_ENDPOINT;
|
|
1751
|
+
const maxFileSize = config.maxFileSize || CLOUD_MAX_FILE_SIZE;
|
|
1752
|
+
return {
|
|
1753
|
+
/**
|
|
1754
|
+
* Check if cloud storage is available
|
|
1755
|
+
* Premium API keys start with "ygpt_"
|
|
1756
|
+
*/
|
|
1757
|
+
isAvailable() {
|
|
1758
|
+
return Boolean(config.apiKey?.startsWith("ygpt_"));
|
|
1759
|
+
},
|
|
1760
|
+
/**
|
|
1761
|
+
* Upload file to managed cloud storage
|
|
1762
|
+
*/
|
|
1763
|
+
async upload(file, options) {
|
|
1764
|
+
if (file.size > maxFileSize) {
|
|
1765
|
+
const sizeMB = (maxFileSize / (1024 * 1024)).toFixed(0);
|
|
1766
|
+
throw new Error(`File size exceeds ${sizeMB}MB limit`);
|
|
1767
|
+
}
|
|
1768
|
+
const presignResponse = await fetch(`${endpoint}/v1/storage/upload-url`, {
|
|
1769
|
+
method: "POST",
|
|
1770
|
+
headers: {
|
|
1771
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
1772
|
+
"Content-Type": "application/json"
|
|
1773
|
+
},
|
|
1774
|
+
body: JSON.stringify({
|
|
1775
|
+
filename: file.name,
|
|
1776
|
+
mimeType: file.type,
|
|
1777
|
+
size: file.size
|
|
1778
|
+
})
|
|
1779
|
+
});
|
|
1780
|
+
if (!presignResponse.ok) {
|
|
1781
|
+
const error = await presignResponse.text();
|
|
1782
|
+
throw new Error(`Failed to get upload URL: ${error}`);
|
|
1783
|
+
}
|
|
1784
|
+
const { uploadUrl, publicUrl, expiresIn } = await presignResponse.json();
|
|
1785
|
+
const uploadResponse = await fetch(uploadUrl, {
|
|
1786
|
+
method: "PUT",
|
|
1787
|
+
body: file,
|
|
1788
|
+
headers: {
|
|
1789
|
+
"Content-Type": file.type
|
|
1790
|
+
}
|
|
1791
|
+
});
|
|
1792
|
+
if (!uploadResponse.ok) {
|
|
1793
|
+
throw new Error(`Failed to upload file: ${uploadResponse.statusText}`);
|
|
1794
|
+
}
|
|
1795
|
+
return {
|
|
1796
|
+
url: publicUrl,
|
|
1797
|
+
expiresAt: expiresIn ? Date.now() + expiresIn * 1e3 : void 0
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
function getAttachmentTypeFromMime(mimeType) {
|
|
1803
|
+
if (mimeType.startsWith("image/")) return "image";
|
|
1804
|
+
if (mimeType.startsWith("audio/")) return "audio";
|
|
1805
|
+
if (mimeType.startsWith("video/")) return "video";
|
|
1806
|
+
return "file";
|
|
1807
|
+
}
|
|
1808
|
+
async function processFileToAttachment(file, storage) {
|
|
1809
|
+
const type = getAttachmentTypeFromMime(file.type);
|
|
1810
|
+
if (storage?.isAvailable()) {
|
|
1811
|
+
const { url } = await storage.upload(file);
|
|
1812
|
+
return {
|
|
1813
|
+
type,
|
|
1814
|
+
url,
|
|
1815
|
+
mimeType: file.type,
|
|
1816
|
+
filename: file.name
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1819
|
+
const data = await fileToBase64(file);
|
|
1820
|
+
return {
|
|
1821
|
+
type,
|
|
1822
|
+
data,
|
|
1823
|
+
mimeType: file.type,
|
|
1824
|
+
filename: file.name
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
function fileToBase64(file) {
|
|
1828
|
+
return new Promise((resolve, reject) => {
|
|
1829
|
+
const reader = new FileReader();
|
|
1830
|
+
reader.onload = () => {
|
|
1831
|
+
if (typeof reader.result === "string") {
|
|
1832
|
+
resolve(reader.result);
|
|
1833
|
+
} else {
|
|
1834
|
+
reject(new Error("Failed to read file as base64"));
|
|
1835
|
+
}
|
|
1836
|
+
};
|
|
1837
|
+
reader.onerror = () => reject(new Error("Failed to read file"));
|
|
1838
|
+
reader.readAsDataURL(file);
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
export { CLOUD_MAX_FILE_SIZE, DEFAULT_MODELS, DEFAULT_YOURGPT_ENDPOINT, actionToTool, builtinTools, captureCurrentLogs, captureScreenshot, clearConsoleLogs, clearNetworkRequests, consoleLogsTool, createAssistantMessage, createCloudStorage, createConsoleLogsTool, createCustomDetector, createMessage, createNetworkRequestsTool, createSSEStream, createScreenshotTool, createToolCall, createToolMessage, createToolResult, createUserMessage, defaultSystemMessage, defineClientTool, defineServerTool, defineTool, detectIntent, failure, formatLogsForAI, formatRequestsForAI, formatSSE, generateId, generateMessageId, generateSuggestionReason, generateThreadId, generateThreadTitle, generateToolCallId, getAttachmentTypeFromMime, getConsoleErrors, getConsoleLogs, getConsoleWarnings, getDefaultModel, getFailedRequests, getNetworkRequests, getPrimaryTool, hasToolCalls, hasToolSuggestions, isConsoleCaptureActive, isNetworkCaptureActive, isScreenshotSupported, isToolResult, networkRequestsTool, parseSSELine, parseStreamEvent, parseToolCallArgs, processFileToAttachment, resizeScreenshot, screenshotTool, serializeStreamEvent, startConsoleCapture, startNetworkCapture, stopConsoleCapture, stopNetworkCapture, streamSSE, success, tool, toolToAnthropicFormat, toolToOpenAIFormat, zodObjectToInputSchema, zodToJsonSchema };
|
|
1843
|
+
//# sourceMappingURL=chunk-N4OA2J32.js.map
|
|
1844
|
+
//# sourceMappingURL=chunk-N4OA2J32.js.map
|