@standardagents/builder 0.9.17 → 0.10.1-dev.616ec2e
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/dist/built-in-routes.js +362 -10
- package/dist/built-in-routes.js.map +1 -1
- package/dist/client/assets/index.css +1 -1
- package/dist/client/index.js +19 -19
- package/dist/client/vendor.js +1 -1
- package/dist/client/vue.js +1 -1
- package/dist/image-processing.d.ts +44 -0
- package/dist/image-processing.js +174 -0
- package/dist/image-processing.js.map +1 -0
- package/dist/index.d.ts +790 -3
- package/dist/index.js +1616 -41
- package/dist/index.js.map +1 -1
- package/dist/plugin.js +55 -6
- package/dist/plugin.js.map +1 -1
- package/package.json +11 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { probe, sip } from '@standardagents/sip';
|
|
2
|
+
import { decode as decode$1, encode } from '@jsquash/png';
|
|
3
|
+
import { decode } from '@jsquash/avif';
|
|
1
4
|
import fs2 from 'fs';
|
|
2
5
|
import path3 from 'path';
|
|
3
6
|
import { fileURLToPath } from 'url';
|
|
7
|
+
import { createRequire } from 'module';
|
|
4
8
|
import { DurableObject } from 'cloudflare:workers';
|
|
5
9
|
|
|
6
10
|
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
14
|
var __esm = (fn, res) => function __init() {
|
|
9
15
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
16
|
};
|
|
@@ -12,6 +18,15 @@ var __export = (target, all) => {
|
|
|
12
18
|
for (var name in all)
|
|
13
19
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
30
|
|
|
16
31
|
// src/agents/types.ts
|
|
17
32
|
var MAX_TURNS, MAX_RETRIES_PER_MODEL, TIMESTAMP_MULTIPLIER, STREAM_COOLDOWN, BACKOFF_BASE;
|
|
@@ -250,9 +265,27 @@ var init_BaseProvider = __esm({
|
|
|
250
265
|
/**
|
|
251
266
|
* Helper: Update log with actual request body before making API call
|
|
252
267
|
* This ensures we have the complete request even if the call fails
|
|
268
|
+
*
|
|
269
|
+
* NOTE: Only updates if request_body is NULL - LLMRequest may have already
|
|
270
|
+
* set a transformed request_body (with image paths instead of base64 data URLs)
|
|
271
|
+
*
|
|
272
|
+
* We check if request_body is already set BEFORE serializing to avoid
|
|
273
|
+
* processing megabytes of base64 image data unnecessarily.
|
|
253
274
|
*/
|
|
254
275
|
async logActualRequest(request, logId, state) {
|
|
255
276
|
try {
|
|
277
|
+
let needsUpdate = false;
|
|
278
|
+
await state.stream.waitFor(async () => {
|
|
279
|
+
const result = await state.storage.sql.exec(
|
|
280
|
+
`SELECT COUNT(*) as cnt FROM logs WHERE id = ?1 AND request_body IS NULL`,
|
|
281
|
+
logId
|
|
282
|
+
);
|
|
283
|
+
const rows = result.toArray();
|
|
284
|
+
needsUpdate = rows.length > 0 && rows[0].cnt > 0;
|
|
285
|
+
});
|
|
286
|
+
if (!needsUpdate) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
256
289
|
const requestBody = JSON.stringify(request);
|
|
257
290
|
await state.stream.waitFor(async () => {
|
|
258
291
|
await state.storage.sql.exec(
|
|
@@ -324,6 +357,81 @@ ${errorStack}` : ""}`,
|
|
|
324
357
|
console.error(`[${this.name}] Failed to log error:`, logError);
|
|
325
358
|
}
|
|
326
359
|
}
|
|
360
|
+
/**
|
|
361
|
+
* Helper: Check if a MIME type is an image
|
|
362
|
+
*/
|
|
363
|
+
isImageMimeType(mimeType) {
|
|
364
|
+
return mimeType.startsWith("image/");
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Helper: Create image content part for OpenAI/OpenRouter format
|
|
368
|
+
* Converts base64 data to a data URL
|
|
369
|
+
*/
|
|
370
|
+
createImageContentPart(base64Data, mimeType, detail = "auto") {
|
|
371
|
+
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
372
|
+
return {
|
|
373
|
+
type: "image_url",
|
|
374
|
+
image_url: {
|
|
375
|
+
url: dataUrl,
|
|
376
|
+
detail
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Helper: Create text content part
|
|
382
|
+
*/
|
|
383
|
+
createTextContentPart(text) {
|
|
384
|
+
return {
|
|
385
|
+
type: "text",
|
|
386
|
+
text
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Helper: Convert message content and attachments to multimodal format
|
|
391
|
+
* Returns multimodal content array if there are images, otherwise returns string
|
|
392
|
+
*
|
|
393
|
+
* @param textContent The text content of the message
|
|
394
|
+
* @param attachments Array of attachment references with resolved base64 data
|
|
395
|
+
* @returns MessageContent - either string or multimodal array
|
|
396
|
+
*/
|
|
397
|
+
buildMultimodalContent(textContent, attachments) {
|
|
398
|
+
if (!attachments || attachments.length === 0) {
|
|
399
|
+
return textContent || "";
|
|
400
|
+
}
|
|
401
|
+
const imageAttachments = attachments.filter(
|
|
402
|
+
(a) => this.isImageMimeType(a.ref.mimeType)
|
|
403
|
+
);
|
|
404
|
+
if (imageAttachments.length === 0) {
|
|
405
|
+
return textContent || "";
|
|
406
|
+
}
|
|
407
|
+
const parts = [];
|
|
408
|
+
if (textContent) {
|
|
409
|
+
parts.push(this.createTextContentPart(textContent));
|
|
410
|
+
}
|
|
411
|
+
for (const attachment of imageAttachments) {
|
|
412
|
+
parts.push(
|
|
413
|
+
this.createImageContentPart(attachment.base64, attachment.ref.mimeType)
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
return parts;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Helper: Convert MessageContent to string for logging/display
|
|
420
|
+
* Extracts text from multimodal content, describes images
|
|
421
|
+
*/
|
|
422
|
+
contentToString(content) {
|
|
423
|
+
if (!content) return "";
|
|
424
|
+
if (typeof content === "string") return content;
|
|
425
|
+
const parts = [];
|
|
426
|
+
for (const part of content) {
|
|
427
|
+
if (part.type === "text") {
|
|
428
|
+
parts.push(part.text);
|
|
429
|
+
} else if (part.type === "image_url") {
|
|
430
|
+
parts.push("[Image]");
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return parts.join(" ");
|
|
434
|
+
}
|
|
327
435
|
};
|
|
328
436
|
}
|
|
329
437
|
});
|
|
@@ -762,6 +870,11 @@ var init_TestScript = __esm({
|
|
|
762
870
|
});
|
|
763
871
|
|
|
764
872
|
// src/agents/providers/TestProvider.ts
|
|
873
|
+
function contentToString(content) {
|
|
874
|
+
if (!content) return "";
|
|
875
|
+
if (typeof content === "string") return content;
|
|
876
|
+
return content.filter((part) => part.type === "text").map((part) => part.text).join(" ");
|
|
877
|
+
}
|
|
765
878
|
var TestProvider;
|
|
766
879
|
var init_TestProvider = __esm({
|
|
767
880
|
"src/agents/providers/TestProvider.ts"() {
|
|
@@ -804,8 +917,9 @@ var init_TestProvider = __esm({
|
|
|
804
917
|
const responses = this.script.getResponses();
|
|
805
918
|
if (this.responseIndex >= responses.length) {
|
|
806
919
|
const lastMessage = context.messages.slice(-1)[0];
|
|
920
|
+
const lastContent = contentToString(lastMessage?.content);
|
|
807
921
|
throw new Error(
|
|
808
|
-
`TestProvider: Script exhausted after ${this.responseIndex} responses. Expected ${responses.length} total requests. Received request with ${context.messages.length} messages. Last message role: "${lastMessage?.role}", content: "${
|
|
922
|
+
`TestProvider: Script exhausted after ${this.responseIndex} responses. Expected ${responses.length} total requests. Received request with ${context.messages.length} messages. Last message role: "${lastMessage?.role}", content: "${lastContent.substring(0, 100)}..."`
|
|
809
923
|
);
|
|
810
924
|
}
|
|
811
925
|
const scripted = responses[this.responseIndex];
|
|
@@ -882,8 +996,9 @@ var init_TestProvider = __esm({
|
|
|
882
996
|
if (expectations.containsMessage) {
|
|
883
997
|
const pattern = expectations.containsMessage;
|
|
884
998
|
const found = context.messages.some((m) => {
|
|
885
|
-
|
|
886
|
-
|
|
999
|
+
const content = contentToString(m.content);
|
|
1000
|
+
if (!content) return false;
|
|
1001
|
+
return typeof pattern === "string" ? content.includes(pattern) : pattern.test(content);
|
|
887
1002
|
});
|
|
888
1003
|
if (!found) {
|
|
889
1004
|
throw new Error(
|
|
@@ -895,8 +1010,9 @@ var init_TestProvider = __esm({
|
|
|
895
1010
|
const { toolName, resultContains } = expectations.containsToolResult;
|
|
896
1011
|
const found = context.messages.some((m) => {
|
|
897
1012
|
if (m.role !== "tool") return false;
|
|
898
|
-
|
|
899
|
-
|
|
1013
|
+
const content = contentToString(m.content);
|
|
1014
|
+
if (!content) return false;
|
|
1015
|
+
return !resultContains || content.includes(resultContains);
|
|
900
1016
|
});
|
|
901
1017
|
if (!found) {
|
|
902
1018
|
throw new Error(
|
|
@@ -907,10 +1023,11 @@ var init_TestProvider = __esm({
|
|
|
907
1023
|
if (expectations.systemPromptContains) {
|
|
908
1024
|
const pattern = expectations.systemPromptContains;
|
|
909
1025
|
const systemMessage = context.messages.find((m) => m.role === "system");
|
|
910
|
-
|
|
1026
|
+
const systemContent = contentToString(systemMessage?.content);
|
|
1027
|
+
if (!systemContent) {
|
|
911
1028
|
throw new Error(`TestProvider: No system message found`);
|
|
912
1029
|
}
|
|
913
|
-
const matches = typeof pattern === "string" ?
|
|
1030
|
+
const matches = typeof pattern === "string" ? systemContent.includes(pattern) : pattern.test(systemContent);
|
|
914
1031
|
if (!matches) {
|
|
915
1032
|
throw new Error(
|
|
916
1033
|
`TestProvider: System prompt does not contain "${pattern}"`
|
|
@@ -923,7 +1040,7 @@ var init_TestProvider = __esm({
|
|
|
923
1040
|
*/
|
|
924
1041
|
estimateTokens(messages) {
|
|
925
1042
|
return messages.reduce((sum, m) => {
|
|
926
|
-
const content = m.content
|
|
1043
|
+
const content = contentToString(m.content);
|
|
927
1044
|
return sum + Math.ceil(content.length / 4);
|
|
928
1045
|
}, 0);
|
|
929
1046
|
}
|
|
@@ -1131,6 +1248,37 @@ var init_providers = __esm({
|
|
|
1131
1248
|
});
|
|
1132
1249
|
|
|
1133
1250
|
// src/agents/LLMRequest.ts
|
|
1251
|
+
function transformMessagesForLog(messages, imagePathMap) {
|
|
1252
|
+
console.log("[DEBUG] transformMessagesForLog called, imagePathMap size:", imagePathMap?.size ?? 0);
|
|
1253
|
+
if (!imagePathMap || imagePathMap.size === 0) {
|
|
1254
|
+
console.log("[DEBUG] No imagePathMap, returning original");
|
|
1255
|
+
return messages;
|
|
1256
|
+
}
|
|
1257
|
+
const result = messages.map((msg) => {
|
|
1258
|
+
if (!Array.isArray(msg.content)) {
|
|
1259
|
+
return msg;
|
|
1260
|
+
}
|
|
1261
|
+
return {
|
|
1262
|
+
...msg,
|
|
1263
|
+
content: msg.content.map((part) => {
|
|
1264
|
+
if (typeof part === "object" && part !== null && "type" in part && part.type === "image_url" && "image_url" in part && part.image_url?.url) {
|
|
1265
|
+
const path4 = imagePathMap.get(part.image_url.url);
|
|
1266
|
+
console.log("[DEBUG] Found image_url, lookup result:", path4 ? path4 : "NOT FOUND");
|
|
1267
|
+
if (path4) {
|
|
1268
|
+
return {
|
|
1269
|
+
...part,
|
|
1270
|
+
image_url: { ...part.image_url, url: path4 }
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return part;
|
|
1275
|
+
})
|
|
1276
|
+
};
|
|
1277
|
+
});
|
|
1278
|
+
const hasDataUrl = JSON.stringify(result).includes("data:image");
|
|
1279
|
+
console.log("[DEBUG] After transform, still has data URL:", hasDataUrl);
|
|
1280
|
+
return result;
|
|
1281
|
+
}
|
|
1134
1282
|
var LLMRequest;
|
|
1135
1283
|
var init_LLMRequest = __esm({
|
|
1136
1284
|
"src/agents/LLMRequest.ts"() {
|
|
@@ -1278,6 +1426,23 @@ var init_LLMRequest = __esm({
|
|
|
1278
1426
|
} catch (err) {
|
|
1279
1427
|
console.error("Failed to fetch model name:", err);
|
|
1280
1428
|
}
|
|
1429
|
+
const messagesForLog = transformMessagesForLog(context.messages, context.imagePathMap);
|
|
1430
|
+
const requestBodyObj = {
|
|
1431
|
+
model: modelName || modelId,
|
|
1432
|
+
messages: messagesForLog,
|
|
1433
|
+
tools: context.tools,
|
|
1434
|
+
stream: context.stream ?? true
|
|
1435
|
+
};
|
|
1436
|
+
const requestBodyStr = JSON.stringify(requestBodyObj);
|
|
1437
|
+
const hasDataUrlInFinal = requestBodyStr.includes("data:image");
|
|
1438
|
+
console.log("[DEBUG] request_body has data URL:", hasDataUrlInFinal);
|
|
1439
|
+
if (hasDataUrlInFinal) {
|
|
1440
|
+
console.log("[DEBUG] PROBLEM! Data URL still in request_body after transform");
|
|
1441
|
+
console.log(
|
|
1442
|
+
"[DEBUG] messagesForLog first user msg content type:",
|
|
1443
|
+
messagesForLog.find((m) => m.role === "user")?.content
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1281
1446
|
const logData = {
|
|
1282
1447
|
id,
|
|
1283
1448
|
message_id: state.rootMessageId || crypto.randomUUID(),
|
|
@@ -1285,13 +1450,7 @@ var init_LLMRequest = __esm({
|
|
|
1285
1450
|
model: modelId,
|
|
1286
1451
|
model_name: modelName ?? void 0,
|
|
1287
1452
|
endpoint: "chat.completions",
|
|
1288
|
-
request_body:
|
|
1289
|
-
model: modelName || modelId,
|
|
1290
|
-
// Use actual model name sent to API, fallback to ID
|
|
1291
|
-
messages: context.messages,
|
|
1292
|
-
tools: context.tools,
|
|
1293
|
-
stream: context.stream ?? true
|
|
1294
|
-
}),
|
|
1453
|
+
request_body: requestBodyStr,
|
|
1295
1454
|
tools_available: context.tools ? context.tools.length : 0,
|
|
1296
1455
|
message_history_length: context.messages.length,
|
|
1297
1456
|
prompt_name: context.promptName ?? void 0,
|
|
@@ -2543,6 +2702,112 @@ ${errorStack}` : ""}`;
|
|
|
2543
2702
|
}
|
|
2544
2703
|
});
|
|
2545
2704
|
|
|
2705
|
+
// src/agents/context.ts
|
|
2706
|
+
function hasImageAttachments(message) {
|
|
2707
|
+
if (!message.attachments) return false;
|
|
2708
|
+
try {
|
|
2709
|
+
const attachments = typeof message.attachments === "string" ? JSON.parse(message.attachments) : message.attachments;
|
|
2710
|
+
return attachments.some((att) => att.mimeType.startsWith("image/"));
|
|
2711
|
+
} catch {
|
|
2712
|
+
return false;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
function getUnsummarizedImageAttachments(message) {
|
|
2716
|
+
if (!message.attachments) return [];
|
|
2717
|
+
try {
|
|
2718
|
+
const attachments = typeof message.attachments === "string" ? JSON.parse(message.attachments) : message.attachments;
|
|
2719
|
+
return attachments.filter(
|
|
2720
|
+
(att) => att.mimeType.startsWith("image/") && !att.description
|
|
2721
|
+
);
|
|
2722
|
+
} catch {
|
|
2723
|
+
return [];
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
function getMessagesToSummarize(messages, config = DEFAULT_CONFIG) {
|
|
2727
|
+
const indices = [];
|
|
2728
|
+
const threshold = messages.length - config.recentMessageThreshold;
|
|
2729
|
+
for (let i = 0; i < threshold; i++) {
|
|
2730
|
+
const msg = messages[i];
|
|
2731
|
+
if (msg.role === "user" && hasImageAttachments(msg)) {
|
|
2732
|
+
indices.push(i);
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
return indices;
|
|
2736
|
+
}
|
|
2737
|
+
function buildImageDescription(attachment) {
|
|
2738
|
+
if (attachment.description) {
|
|
2739
|
+
return `[Image: ${attachment.name}] ${attachment.description}`;
|
|
2740
|
+
}
|
|
2741
|
+
const dimensions = attachment.width && attachment.height ? ` (${attachment.width}x${attachment.height})` : "";
|
|
2742
|
+
return `[Image: ${attachment.name}${dimensions}]`;
|
|
2743
|
+
}
|
|
2744
|
+
function replaceImagesWithDescriptions(content, attachments) {
|
|
2745
|
+
if (typeof content === "string") {
|
|
2746
|
+
const imageDescriptions2 = attachments.filter((att) => att.mimeType.startsWith("image/")).map(buildImageDescription).join("\n");
|
|
2747
|
+
if (!imageDescriptions2) return content;
|
|
2748
|
+
if (!content) return imageDescriptions2;
|
|
2749
|
+
return `${imageDescriptions2}
|
|
2750
|
+
|
|
2751
|
+
${content}`;
|
|
2752
|
+
}
|
|
2753
|
+
const textParts = content.filter((part) => part.type === "text");
|
|
2754
|
+
const imageAttachments = attachments.filter(
|
|
2755
|
+
(att) => att.mimeType.startsWith("image/")
|
|
2756
|
+
);
|
|
2757
|
+
if (imageAttachments.length === 0) {
|
|
2758
|
+
return content;
|
|
2759
|
+
}
|
|
2760
|
+
const imageDescriptions = imageAttachments.map(buildImageDescription).join("\n");
|
|
2761
|
+
if (textParts.length > 0) {
|
|
2762
|
+
const existingText = textParts.map((p) => p.text).join(" ");
|
|
2763
|
+
return `${imageDescriptions}
|
|
2764
|
+
|
|
2765
|
+
${existingText}`;
|
|
2766
|
+
}
|
|
2767
|
+
return imageDescriptions;
|
|
2768
|
+
}
|
|
2769
|
+
function optimizeImageContext(messages, config = DEFAULT_CONFIG) {
|
|
2770
|
+
const messagesToSummarize = getMessagesToSummarize(messages, config);
|
|
2771
|
+
if (messagesToSummarize.length === 0) {
|
|
2772
|
+
return messages;
|
|
2773
|
+
}
|
|
2774
|
+
return messages.map((msg, index) => {
|
|
2775
|
+
if (!messagesToSummarize.includes(index)) {
|
|
2776
|
+
return msg;
|
|
2777
|
+
}
|
|
2778
|
+
let attachments;
|
|
2779
|
+
try {
|
|
2780
|
+
attachments = typeof msg.attachments === "string" ? JSON.parse(msg.attachments) : msg.attachments || [];
|
|
2781
|
+
} catch {
|
|
2782
|
+
return msg;
|
|
2783
|
+
}
|
|
2784
|
+
const optimizedContent = replaceImagesWithDescriptions(
|
|
2785
|
+
msg.content || "",
|
|
2786
|
+
attachments
|
|
2787
|
+
);
|
|
2788
|
+
return {
|
|
2789
|
+
...msg,
|
|
2790
|
+
content: typeof optimizedContent === "string" ? optimizedContent : JSON.stringify(optimizedContent),
|
|
2791
|
+
// Clear attachments since we've inlined descriptions
|
|
2792
|
+
attachments: JSON.stringify(
|
|
2793
|
+
attachments.filter((att) => !att.mimeType.startsWith("image/"))
|
|
2794
|
+
)
|
|
2795
|
+
};
|
|
2796
|
+
});
|
|
2797
|
+
}
|
|
2798
|
+
async function generateImageDescription(_imageBase64, _mimeType, _state) {
|
|
2799
|
+
return null;
|
|
2800
|
+
}
|
|
2801
|
+
var DEFAULT_CONFIG;
|
|
2802
|
+
var init_context = __esm({
|
|
2803
|
+
"src/agents/context.ts"() {
|
|
2804
|
+
DEFAULT_CONFIG = {
|
|
2805
|
+
recentMessageThreshold: 10,
|
|
2806
|
+
descriptionPrompt: "Describe this image concisely in 1-2 sentences, focusing on the key visual elements and any text visible."
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2809
|
+
});
|
|
2810
|
+
|
|
2546
2811
|
// src/agents/FlowEngine.ts
|
|
2547
2812
|
var FlowEngine_exports = {};
|
|
2548
2813
|
__export(FlowEngine_exports, {
|
|
@@ -2555,6 +2820,7 @@ var init_FlowEngine = __esm({
|
|
|
2555
2820
|
init_StreamManager();
|
|
2556
2821
|
init_LLMRequest();
|
|
2557
2822
|
init_ToolExecutor();
|
|
2823
|
+
init_context();
|
|
2558
2824
|
FlowEngine = class {
|
|
2559
2825
|
/**
|
|
2560
2826
|
* Main execution entry point
|
|
@@ -3014,6 +3280,7 @@ var init_FlowEngine = __esm({
|
|
|
3014
3280
|
*/
|
|
3015
3281
|
static async assembleContext(state) {
|
|
3016
3282
|
const messages = [];
|
|
3283
|
+
const imagePathMap = /* @__PURE__ */ new Map();
|
|
3017
3284
|
const model = state.prompt.model;
|
|
3018
3285
|
const promptName = state.prompt.name;
|
|
3019
3286
|
const parallelToolCalls = state.prompt.parallel_tool_calls;
|
|
@@ -3042,6 +3309,9 @@ var init_FlowEngine = __esm({
|
|
|
3042
3309
|
toolCallsWithResponses.add(msg.tool_call_id);
|
|
3043
3310
|
}
|
|
3044
3311
|
}
|
|
3312
|
+
const recentMessageThreshold = state.prompt.recentImageThreshold ?? 10;
|
|
3313
|
+
const oldMessageThreshold = completedMessages.length - recentMessageThreshold;
|
|
3314
|
+
let messageIndex = 0;
|
|
3045
3315
|
for (const msg of completedMessages) {
|
|
3046
3316
|
if (!includePastTools && msg.role === "tool") {
|
|
3047
3317
|
continue;
|
|
@@ -3071,7 +3341,36 @@ var init_FlowEngine = __esm({
|
|
|
3071
3341
|
role = state.currentSide === "a" ? "user" : "assistant";
|
|
3072
3342
|
}
|
|
3073
3343
|
}
|
|
3074
|
-
|
|
3344
|
+
let messageContent;
|
|
3345
|
+
const hasAttachments = msg.attachments && msg.attachments !== "[]";
|
|
3346
|
+
const isOldMessage = messageIndex < oldMessageThreshold;
|
|
3347
|
+
if (hasAttachments && msg.role === "user") {
|
|
3348
|
+
const attachments = JSON.parse(msg.attachments);
|
|
3349
|
+
const imageAttachments = attachments.filter(
|
|
3350
|
+
(a) => a.mimeType.startsWith("image/")
|
|
3351
|
+
);
|
|
3352
|
+
if (isOldMessage && imageAttachments.length > 0) {
|
|
3353
|
+
const imageDescriptions = imageAttachments.map(buildImageDescription).join("\n");
|
|
3354
|
+
const nonImageFiles = attachments.filter((a) => !a.mimeType.startsWith("image/")).map((a) => a.name);
|
|
3355
|
+
const nonImageList = nonImageFiles.length > 0 ? `
|
|
3356
|
+
[Other files: ${nonImageFiles.join(", ")}]` : "";
|
|
3357
|
+
messageContent = msg.content ? `${imageDescriptions}${nonImageList}
|
|
3358
|
+
|
|
3359
|
+
${msg.content}` : `${imageDescriptions}${nonImageList}`;
|
|
3360
|
+
} else {
|
|
3361
|
+
if (imageAttachments.length > 0) {
|
|
3362
|
+
messageContent = await this.resolveAttachmentsToContent(msg, state, imagePathMap);
|
|
3363
|
+
} else {
|
|
3364
|
+
const fileList = attachments.map((a) => a.name).join(", ");
|
|
3365
|
+
messageContent = msg.content ? `${msg.content}
|
|
3366
|
+
|
|
3367
|
+
[Attached files: ${fileList}]` : `[Attached files: ${fileList}]`;
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
} else {
|
|
3371
|
+
messageContent = msg.content || "";
|
|
3372
|
+
}
|
|
3373
|
+
messageIndex++;
|
|
3075
3374
|
const messageToAdd = {
|
|
3076
3375
|
role,
|
|
3077
3376
|
content: messageContent,
|
|
@@ -3170,7 +3469,8 @@ var init_FlowEngine = __esm({
|
|
|
3170
3469
|
// Retries now apply to all prompts (top-level and sub-prompts), only for provider errors
|
|
3171
3470
|
parallel_tool_calls: parallelToolCalls,
|
|
3172
3471
|
tool_choice: finalToolChoice,
|
|
3173
|
-
reasoning
|
|
3472
|
+
reasoning,
|
|
3473
|
+
imagePathMap: imagePathMap.size > 0 ? imagePathMap : void 0
|
|
3174
3474
|
};
|
|
3175
3475
|
}
|
|
3176
3476
|
/**
|
|
@@ -4039,6 +4339,7 @@ var init_FlowEngine = __esm({
|
|
|
4039
4339
|
"role",
|
|
4040
4340
|
"content",
|
|
4041
4341
|
"name",
|
|
4342
|
+
"attachments",
|
|
4042
4343
|
...includeToolCalls ? ["tool_calls", "tool_call_id", "tool_status"] : [],
|
|
4043
4344
|
"log_id",
|
|
4044
4345
|
"created_at",
|
|
@@ -4070,6 +4371,7 @@ var init_FlowEngine = __esm({
|
|
|
4070
4371
|
role: row.role,
|
|
4071
4372
|
content: row.content,
|
|
4072
4373
|
name: row.name,
|
|
4374
|
+
attachments: row.attachments,
|
|
4073
4375
|
tool_calls: row.tool_calls,
|
|
4074
4376
|
tool_call_id: row.tool_call_id,
|
|
4075
4377
|
log_id: row.log_id,
|
|
@@ -4148,8 +4450,691 @@ ${errorStack}` : ""}`,
|
|
|
4148
4450
|
console.error("Failed to log error to database:", logError);
|
|
4149
4451
|
}
|
|
4150
4452
|
}
|
|
4453
|
+
/**
|
|
4454
|
+
* Check if a MIME type is an image
|
|
4455
|
+
*/
|
|
4456
|
+
static isImageMimeType(mimeType) {
|
|
4457
|
+
return mimeType.startsWith("image/");
|
|
4458
|
+
}
|
|
4459
|
+
/**
|
|
4460
|
+
* Resolve attachments from a message and build multimodal content
|
|
4461
|
+
* Returns the message content (string or multimodal array)
|
|
4462
|
+
*
|
|
4463
|
+
* @param msg - Message with potential attachments
|
|
4464
|
+
* @param state - Flow state with storage access
|
|
4465
|
+
* @returns MessageContent - either string or multimodal array
|
|
4466
|
+
*/
|
|
4467
|
+
static async resolveAttachmentsToContent(msg, state, imagePathMap) {
|
|
4468
|
+
const textContent = msg.content || "";
|
|
4469
|
+
if (!msg.attachments) {
|
|
4470
|
+
return textContent;
|
|
4471
|
+
}
|
|
4472
|
+
let attachments;
|
|
4473
|
+
try {
|
|
4474
|
+
attachments = JSON.parse(msg.attachments);
|
|
4475
|
+
} catch (e) {
|
|
4476
|
+
console.error(`Failed to parse attachments for message ${msg.id}:`, e);
|
|
4477
|
+
return textContent;
|
|
4478
|
+
}
|
|
4479
|
+
if (!attachments || attachments.length === 0) {
|
|
4480
|
+
return textContent;
|
|
4481
|
+
}
|
|
4482
|
+
const imageAttachments = attachments.filter(
|
|
4483
|
+
(a) => this.isImageMimeType(a.mimeType)
|
|
4484
|
+
);
|
|
4485
|
+
if (imageAttachments.length === 0) {
|
|
4486
|
+
const nonImageFiles2 = attachments.filter(
|
|
4487
|
+
(a) => !this.isImageMimeType(a.mimeType)
|
|
4488
|
+
);
|
|
4489
|
+
if (nonImageFiles2.length > 0) {
|
|
4490
|
+
const fileList = nonImageFiles2.map((a) => a.name).join(", ");
|
|
4491
|
+
return `${textContent}
|
|
4492
|
+
|
|
4493
|
+
[Attached files: ${fileList}]`;
|
|
4494
|
+
}
|
|
4495
|
+
return textContent;
|
|
4496
|
+
}
|
|
4497
|
+
const multimodalParts = [];
|
|
4498
|
+
if (textContent) {
|
|
4499
|
+
multimodalParts.push({
|
|
4500
|
+
type: "text",
|
|
4501
|
+
text: textContent
|
|
4502
|
+
});
|
|
4503
|
+
}
|
|
4504
|
+
for (const attachment of imageAttachments) {
|
|
4505
|
+
try {
|
|
4506
|
+
const result = await state.thread.instance.readFile(
|
|
4507
|
+
attachment.path
|
|
4508
|
+
);
|
|
4509
|
+
if (result.success && result.data) {
|
|
4510
|
+
const dataUrl = `data:${attachment.mimeType};base64,${result.data}`;
|
|
4511
|
+
if (imagePathMap) {
|
|
4512
|
+
imagePathMap.set(dataUrl, attachment.path);
|
|
4513
|
+
}
|
|
4514
|
+
multimodalParts.push({
|
|
4515
|
+
type: "image_url",
|
|
4516
|
+
image_url: {
|
|
4517
|
+
url: dataUrl,
|
|
4518
|
+
detail: "auto"
|
|
4519
|
+
}
|
|
4520
|
+
});
|
|
4521
|
+
} else {
|
|
4522
|
+
console.warn(
|
|
4523
|
+
`Failed to load image attachment ${attachment.path}:`,
|
|
4524
|
+
result.error
|
|
4525
|
+
);
|
|
4526
|
+
multimodalParts.push({
|
|
4527
|
+
type: "text",
|
|
4528
|
+
text: `[Image not available: ${attachment.name}]`
|
|
4529
|
+
});
|
|
4530
|
+
}
|
|
4531
|
+
} catch (error) {
|
|
4532
|
+
console.error(`Error loading image attachment ${attachment.path}:`, error);
|
|
4533
|
+
multimodalParts.push({
|
|
4534
|
+
type: "text",
|
|
4535
|
+
text: `[Error loading image: ${attachment.name}]`
|
|
4536
|
+
});
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
const nonImageFiles = attachments.filter(
|
|
4540
|
+
(a) => !this.isImageMimeType(a.mimeType)
|
|
4541
|
+
);
|
|
4542
|
+
if (nonImageFiles.length > 0) {
|
|
4543
|
+
const fileList = nonImageFiles.map((a) => a.name).join(", ");
|
|
4544
|
+
multimodalParts.push({
|
|
4545
|
+
type: "text",
|
|
4546
|
+
text: `
|
|
4547
|
+
|
|
4548
|
+
[Attached files: ${fileList}]`
|
|
4549
|
+
});
|
|
4550
|
+
}
|
|
4551
|
+
return multimodalParts;
|
|
4552
|
+
}
|
|
4553
|
+
};
|
|
4554
|
+
}
|
|
4555
|
+
});
|
|
4556
|
+
|
|
4557
|
+
// src/durable-objects/files.ts
|
|
4558
|
+
var files_exports = {};
|
|
4559
|
+
__export(files_exports, {
|
|
4560
|
+
FileStorage: () => FileStorage,
|
|
4561
|
+
basename: () => basename,
|
|
4562
|
+
detectStorageBackend: () => detectStorageBackend,
|
|
4563
|
+
dirname: () => dirname,
|
|
4564
|
+
isTextMimeType: () => isTextMimeType,
|
|
4565
|
+
normalizePath: () => normalizePath,
|
|
4566
|
+
rowToFileRecord: () => rowToFileRecord
|
|
4567
|
+
});
|
|
4568
|
+
function isTextMimeType(mimeType) {
|
|
4569
|
+
return TEXT_MIME_TYPES.some((prefix) => mimeType.startsWith(prefix));
|
|
4570
|
+
}
|
|
4571
|
+
function detectStorageBackend(location) {
|
|
4572
|
+
if (location.startsWith("s3://")) return "s3";
|
|
4573
|
+
if (location.startsWith("r2://")) return "r2";
|
|
4574
|
+
if (location.startsWith("http://") || location.startsWith("https://"))
|
|
4575
|
+
return "url";
|
|
4576
|
+
return "local";
|
|
4577
|
+
}
|
|
4578
|
+
function basename(path4) {
|
|
4579
|
+
const parts = path4.split("/").filter(Boolean);
|
|
4580
|
+
return parts[parts.length - 1] || "";
|
|
4581
|
+
}
|
|
4582
|
+
function normalizePath(path4) {
|
|
4583
|
+
let normalized = path4.replace(/\/+/g, "/");
|
|
4584
|
+
if (!normalized.startsWith("/")) normalized = "/" + normalized;
|
|
4585
|
+
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
4586
|
+
normalized = normalized.slice(0, -1);
|
|
4587
|
+
}
|
|
4588
|
+
return normalized;
|
|
4589
|
+
}
|
|
4590
|
+
function dirname(path4) {
|
|
4591
|
+
const normalized = normalizePath(path4);
|
|
4592
|
+
const lastSlash = normalized.lastIndexOf("/");
|
|
4593
|
+
if (lastSlash <= 0) return "/";
|
|
4594
|
+
return normalized.slice(0, lastSlash);
|
|
4595
|
+
}
|
|
4596
|
+
function rowToFileRecord(row) {
|
|
4597
|
+
return {
|
|
4598
|
+
path: row.path,
|
|
4599
|
+
name: row.name,
|
|
4600
|
+
mimeType: row.mime_type,
|
|
4601
|
+
storage: row.storage,
|
|
4602
|
+
location: row.location,
|
|
4603
|
+
size: row.size,
|
|
4604
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : null,
|
|
4605
|
+
isDirectory: row.is_directory === 1,
|
|
4606
|
+
createdAt: row.created_at
|
|
4607
|
+
};
|
|
4608
|
+
}
|
|
4609
|
+
function inferMimeType(filename) {
|
|
4610
|
+
const ext = filename.split(".").pop()?.toLowerCase();
|
|
4611
|
+
const mimeTypes = {
|
|
4612
|
+
// Text
|
|
4613
|
+
txt: "text/plain",
|
|
4614
|
+
md: "text/markdown",
|
|
4615
|
+
html: "text/html",
|
|
4616
|
+
css: "text/css",
|
|
4617
|
+
csv: "text/csv",
|
|
4618
|
+
// Code
|
|
4619
|
+
js: "application/javascript",
|
|
4620
|
+
ts: "application/typescript",
|
|
4621
|
+
json: "application/json",
|
|
4622
|
+
xml: "application/xml",
|
|
4623
|
+
yaml: "application/yaml",
|
|
4624
|
+
yml: "application/yaml",
|
|
4625
|
+
// Images
|
|
4626
|
+
jpg: "image/jpeg",
|
|
4627
|
+
jpeg: "image/jpeg",
|
|
4628
|
+
png: "image/png",
|
|
4629
|
+
gif: "image/gif",
|
|
4630
|
+
webp: "image/webp",
|
|
4631
|
+
svg: "image/svg+xml",
|
|
4632
|
+
ico: "image/x-icon",
|
|
4633
|
+
// Documents
|
|
4634
|
+
pdf: "application/pdf",
|
|
4635
|
+
// Archives
|
|
4636
|
+
zip: "application/zip",
|
|
4637
|
+
tar: "application/x-tar",
|
|
4638
|
+
gz: "application/gzip"
|
|
4639
|
+
};
|
|
4640
|
+
return mimeTypes[ext || ""] || "application/octet-stream";
|
|
4641
|
+
}
|
|
4642
|
+
var TEXT_MIME_TYPES, FileStorage;
|
|
4643
|
+
var init_files = __esm({
|
|
4644
|
+
"src/durable-objects/files.ts"() {
|
|
4645
|
+
TEXT_MIME_TYPES = [
|
|
4646
|
+
"text/",
|
|
4647
|
+
"application/json",
|
|
4648
|
+
"application/javascript",
|
|
4649
|
+
"application/xml",
|
|
4650
|
+
"application/x-yaml",
|
|
4651
|
+
"application/yaml"
|
|
4652
|
+
];
|
|
4653
|
+
FileStorage = class {
|
|
4654
|
+
constructor(sql) {
|
|
4655
|
+
this.sql = sql;
|
|
4656
|
+
}
|
|
4657
|
+
/**
|
|
4658
|
+
* Write a file to storage
|
|
4659
|
+
*/
|
|
4660
|
+
async writeFile(path4, data, mimeType, options) {
|
|
4661
|
+
const normalizedPath = normalizePath(path4);
|
|
4662
|
+
const name = basename(normalizedPath);
|
|
4663
|
+
const isText = isTextMimeType(mimeType);
|
|
4664
|
+
const now = Date.now();
|
|
4665
|
+
let content = null;
|
|
4666
|
+
let blobData = null;
|
|
4667
|
+
let size;
|
|
4668
|
+
if (isText) {
|
|
4669
|
+
content = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
4670
|
+
size = new TextEncoder().encode(content).length;
|
|
4671
|
+
} else {
|
|
4672
|
+
blobData = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
4673
|
+
size = blobData.byteLength;
|
|
4674
|
+
}
|
|
4675
|
+
const metadataJson = options?.metadata ? JSON.stringify(options.metadata) : null;
|
|
4676
|
+
await this.sql.exec(
|
|
4677
|
+
`INSERT OR REPLACE INTO files (path, name, mime_type, storage, location, data, content, size, metadata, thumbnail, is_directory, created_at)
|
|
4678
|
+
VALUES (?, ?, ?, 'local', NULL, ?, ?, ?, ?, ?, 0, ?)`,
|
|
4679
|
+
normalizedPath,
|
|
4680
|
+
name,
|
|
4681
|
+
mimeType,
|
|
4682
|
+
blobData,
|
|
4683
|
+
content,
|
|
4684
|
+
size,
|
|
4685
|
+
metadataJson,
|
|
4686
|
+
options?.thumbnail || null,
|
|
4687
|
+
now
|
|
4688
|
+
);
|
|
4689
|
+
await this.updateStats();
|
|
4690
|
+
return {
|
|
4691
|
+
path: normalizedPath,
|
|
4692
|
+
name,
|
|
4693
|
+
mimeType,
|
|
4694
|
+
storage: "local",
|
|
4695
|
+
location: null,
|
|
4696
|
+
size,
|
|
4697
|
+
metadata: options?.metadata || null,
|
|
4698
|
+
isDirectory: false,
|
|
4699
|
+
createdAt: now
|
|
4700
|
+
};
|
|
4701
|
+
}
|
|
4702
|
+
/**
|
|
4703
|
+
* Link to an external file (URL, S3, R2)
|
|
4704
|
+
*/
|
|
4705
|
+
async linkFile(path4, location, options) {
|
|
4706
|
+
const normalizedPath = normalizePath(path4);
|
|
4707
|
+
const name = basename(normalizedPath);
|
|
4708
|
+
const storage = detectStorageBackend(location);
|
|
4709
|
+
const now = Date.now();
|
|
4710
|
+
const mimeType = options?.mimeType || inferMimeType(name);
|
|
4711
|
+
const metadataJson = options?.metadata ? JSON.stringify(options.metadata) : null;
|
|
4712
|
+
await this.sql.exec(
|
|
4713
|
+
`INSERT OR REPLACE INTO files (path, name, mime_type, storage, location, data, content, size, metadata, thumbnail, is_directory, created_at)
|
|
4714
|
+
VALUES (?, ?, ?, ?, ?, NULL, NULL, ?, ?, NULL, 0, ?)`,
|
|
4715
|
+
normalizedPath,
|
|
4716
|
+
name,
|
|
4717
|
+
mimeType,
|
|
4718
|
+
storage,
|
|
4719
|
+
location,
|
|
4720
|
+
options?.size || 0,
|
|
4721
|
+
metadataJson,
|
|
4722
|
+
now
|
|
4723
|
+
);
|
|
4724
|
+
await this.updateStats();
|
|
4725
|
+
return {
|
|
4726
|
+
path: normalizedPath,
|
|
4727
|
+
name,
|
|
4728
|
+
mimeType,
|
|
4729
|
+
storage,
|
|
4730
|
+
location,
|
|
4731
|
+
size: options?.size || 0,
|
|
4732
|
+
metadata: options?.metadata || null,
|
|
4733
|
+
isDirectory: false,
|
|
4734
|
+
createdAt: now
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
/**
|
|
4738
|
+
* Read file data from storage
|
|
4739
|
+
* Returns null if file doesn't exist or is external
|
|
4740
|
+
*/
|
|
4741
|
+
async readFile(path4) {
|
|
4742
|
+
const normalizedPath = normalizePath(path4);
|
|
4743
|
+
const cursor = await this.sql.exec(
|
|
4744
|
+
`SELECT data, content, storage FROM files WHERE path = ? AND is_directory = 0`,
|
|
4745
|
+
normalizedPath
|
|
4746
|
+
);
|
|
4747
|
+
const rows = cursor.toArray();
|
|
4748
|
+
if (rows.length === 0) return null;
|
|
4749
|
+
const row = rows[0];
|
|
4750
|
+
if (row.storage !== "local") return null;
|
|
4751
|
+
if (row.content !== null) {
|
|
4752
|
+
return new TextEncoder().encode(row.content);
|
|
4753
|
+
}
|
|
4754
|
+
return row.data;
|
|
4755
|
+
}
|
|
4756
|
+
/**
|
|
4757
|
+
* Read text file content
|
|
4758
|
+
*/
|
|
4759
|
+
async readTextFile(path4) {
|
|
4760
|
+
const normalizedPath = normalizePath(path4);
|
|
4761
|
+
const cursor = await this.sql.exec(
|
|
4762
|
+
`SELECT content, data, storage FROM files WHERE path = ? AND is_directory = 0`,
|
|
4763
|
+
normalizedPath
|
|
4764
|
+
);
|
|
4765
|
+
const rows = cursor.toArray();
|
|
4766
|
+
if (rows.length === 0) return null;
|
|
4767
|
+
const row = rows[0];
|
|
4768
|
+
if (row.storage !== "local") return null;
|
|
4769
|
+
if (row.content !== null) return row.content;
|
|
4770
|
+
if (row.data !== null) {
|
|
4771
|
+
return new TextDecoder().decode(row.data);
|
|
4772
|
+
}
|
|
4773
|
+
return null;
|
|
4774
|
+
}
|
|
4775
|
+
/**
|
|
4776
|
+
* Get file metadata (stat)
|
|
4777
|
+
*/
|
|
4778
|
+
async stat(path4) {
|
|
4779
|
+
const normalizedPath = normalizePath(path4);
|
|
4780
|
+
const cursor = await this.sql.exec(
|
|
4781
|
+
`SELECT path, name, mime_type, storage, location, size, metadata, is_directory, created_at
|
|
4782
|
+
FROM files WHERE path = ?`,
|
|
4783
|
+
normalizedPath
|
|
4784
|
+
);
|
|
4785
|
+
const rows = cursor.toArray();
|
|
4786
|
+
if (rows.length === 0) return null;
|
|
4787
|
+
return rowToFileRecord(rows[0]);
|
|
4788
|
+
}
|
|
4789
|
+
/**
|
|
4790
|
+
* Check if file or directory exists
|
|
4791
|
+
*/
|
|
4792
|
+
async exists(path4) {
|
|
4793
|
+
const normalizedPath = normalizePath(path4);
|
|
4794
|
+
const cursor = await this.sql.exec(
|
|
4795
|
+
`SELECT COUNT(*) as count FROM files WHERE path = ?`,
|
|
4796
|
+
normalizedPath
|
|
4797
|
+
);
|
|
4798
|
+
return cursor.one().count > 0;
|
|
4799
|
+
}
|
|
4800
|
+
/**
|
|
4801
|
+
* Delete a file
|
|
4802
|
+
*/
|
|
4803
|
+
async unlink(path4) {
|
|
4804
|
+
const normalizedPath = normalizePath(path4);
|
|
4805
|
+
await this.sql.exec(
|
|
4806
|
+
`DELETE FROM files WHERE path = ? AND is_directory = 0`,
|
|
4807
|
+
normalizedPath
|
|
4808
|
+
);
|
|
4809
|
+
await this.updateStats();
|
|
4810
|
+
}
|
|
4811
|
+
/**
|
|
4812
|
+
* Create a directory marker
|
|
4813
|
+
*/
|
|
4814
|
+
async mkdir(path4) {
|
|
4815
|
+
const normalizedPath = normalizePath(path4);
|
|
4816
|
+
const name = basename(normalizedPath);
|
|
4817
|
+
const now = Date.now();
|
|
4818
|
+
await this.sql.exec(
|
|
4819
|
+
`INSERT OR IGNORE INTO files (path, name, mime_type, storage, location, data, content, size, metadata, thumbnail, is_directory, created_at)
|
|
4820
|
+
VALUES (?, ?, 'inode/directory', 'local', NULL, NULL, NULL, 0, NULL, NULL, 1, ?)`,
|
|
4821
|
+
normalizedPath,
|
|
4822
|
+
name,
|
|
4823
|
+
now
|
|
4824
|
+
);
|
|
4825
|
+
return {
|
|
4826
|
+
path: normalizedPath,
|
|
4827
|
+
name,
|
|
4828
|
+
mimeType: "inode/directory",
|
|
4829
|
+
storage: "local",
|
|
4830
|
+
location: null,
|
|
4831
|
+
size: 0,
|
|
4832
|
+
metadata: null,
|
|
4833
|
+
isDirectory: true,
|
|
4834
|
+
createdAt: now
|
|
4835
|
+
};
|
|
4836
|
+
}
|
|
4837
|
+
/**
|
|
4838
|
+
* List directory contents
|
|
4839
|
+
*/
|
|
4840
|
+
async readdir(path4) {
|
|
4841
|
+
const normalizedPath = normalizePath(path4);
|
|
4842
|
+
const prefix = normalizedPath === "/" ? "/" : normalizedPath + "/";
|
|
4843
|
+
const cursor = await this.sql.exec(
|
|
4844
|
+
`SELECT path, name, mime_type, storage, location, size, metadata, is_directory, created_at
|
|
4845
|
+
FROM files
|
|
4846
|
+
WHERE path LIKE ? || '%'
|
|
4847
|
+
AND path != ?
|
|
4848
|
+
AND SUBSTR(path, LENGTH(?) + 1) NOT LIKE '%/%'
|
|
4849
|
+
ORDER BY is_directory DESC, name ASC`,
|
|
4850
|
+
prefix,
|
|
4851
|
+
normalizedPath,
|
|
4852
|
+
prefix
|
|
4853
|
+
);
|
|
4854
|
+
return cursor.toArray().map(rowToFileRecord);
|
|
4855
|
+
}
|
|
4856
|
+
/**
|
|
4857
|
+
* Remove empty directory
|
|
4858
|
+
*/
|
|
4859
|
+
async rmdir(path4) {
|
|
4860
|
+
const normalizedPath = normalizePath(path4);
|
|
4861
|
+
const prefix = normalizedPath + "/";
|
|
4862
|
+
const cursor = await this.sql.exec(
|
|
4863
|
+
`SELECT COUNT(*) as count FROM files WHERE path LIKE ? || '%'`,
|
|
4864
|
+
prefix
|
|
4865
|
+
);
|
|
4866
|
+
if (cursor.one().count > 0) {
|
|
4867
|
+
throw new Error("Directory not empty");
|
|
4868
|
+
}
|
|
4869
|
+
await this.sql.exec(
|
|
4870
|
+
`DELETE FROM files WHERE path = ? AND is_directory = 1`,
|
|
4871
|
+
normalizedPath
|
|
4872
|
+
);
|
|
4873
|
+
}
|
|
4874
|
+
/**
|
|
4875
|
+
* Get storage statistics
|
|
4876
|
+
*/
|
|
4877
|
+
async getFileStats() {
|
|
4878
|
+
const cursor = await this.sql.exec(`SELECT total_size, file_count FROM file_stats WHERE id = 1`);
|
|
4879
|
+
const rows = cursor.toArray();
|
|
4880
|
+
if (rows.length === 0) {
|
|
4881
|
+
return { totalSize: 0, fileCount: 0 };
|
|
4882
|
+
}
|
|
4883
|
+
return {
|
|
4884
|
+
totalSize: rows[0].total_size,
|
|
4885
|
+
fileCount: rows[0].file_count
|
|
4886
|
+
};
|
|
4887
|
+
}
|
|
4888
|
+
/**
|
|
4889
|
+
* Update file statistics (call after writes/deletes)
|
|
4890
|
+
*/
|
|
4891
|
+
async updateStats() {
|
|
4892
|
+
await this.sql.exec(`
|
|
4893
|
+
UPDATE file_stats SET
|
|
4894
|
+
total_size = (SELECT COALESCE(SUM(size), 0) FROM files WHERE is_directory = 0),
|
|
4895
|
+
file_count = (SELECT COUNT(*) FROM files WHERE is_directory = 0)
|
|
4896
|
+
WHERE id = 1
|
|
4897
|
+
`);
|
|
4898
|
+
}
|
|
4899
|
+
/**
|
|
4900
|
+
* Get thumbnail for an image
|
|
4901
|
+
*/
|
|
4902
|
+
async getThumbnail(path4) {
|
|
4903
|
+
const normalizedPath = normalizePath(path4);
|
|
4904
|
+
const cursor = await this.sql.exec(
|
|
4905
|
+
`SELECT thumbnail FROM files WHERE path = ? AND is_directory = 0`,
|
|
4906
|
+
normalizedPath
|
|
4907
|
+
);
|
|
4908
|
+
const rows = cursor.toArray();
|
|
4909
|
+
if (rows.length === 0) return null;
|
|
4910
|
+
return rows[0].thumbnail;
|
|
4911
|
+
}
|
|
4912
|
+
/**
|
|
4913
|
+
* Search file contents using FTS5
|
|
4914
|
+
*/
|
|
4915
|
+
async grep(pattern, options) {
|
|
4916
|
+
const limit = options?.limit || 100;
|
|
4917
|
+
let query = `
|
|
4918
|
+
SELECT files.path, files.name, snippet(files_fts, 2, '<mark>', '</mark>', '...', 32) as snippet
|
|
4919
|
+
FROM files_fts
|
|
4920
|
+
JOIN files ON files.rowid = files_fts.rowid
|
|
4921
|
+
WHERE files_fts MATCH ?
|
|
4922
|
+
`;
|
|
4923
|
+
const params = [pattern];
|
|
4924
|
+
if (options?.path) {
|
|
4925
|
+
query += ` AND files.path LIKE ? || '%'`;
|
|
4926
|
+
params.push(normalizePath(options.path));
|
|
4927
|
+
}
|
|
4928
|
+
query += ` LIMIT ?`;
|
|
4929
|
+
params.push(limit);
|
|
4930
|
+
const cursor = await this.sql.exec(query, ...params);
|
|
4931
|
+
return cursor.toArray();
|
|
4932
|
+
}
|
|
4933
|
+
/**
|
|
4934
|
+
* Find files by path pattern (glob-like)
|
|
4935
|
+
*/
|
|
4936
|
+
async find(pattern, options) {
|
|
4937
|
+
const limit = options?.limit || 100;
|
|
4938
|
+
const type = options?.type || "all";
|
|
4939
|
+
let sqlPattern = pattern.replace(/\*\*/g, "%").replace(/\*/g, "%").replace(/\?/g, "_");
|
|
4940
|
+
if (!sqlPattern.startsWith("/")) {
|
|
4941
|
+
sqlPattern = "%" + sqlPattern;
|
|
4942
|
+
}
|
|
4943
|
+
let typeClause = "";
|
|
4944
|
+
if (type === "file") typeClause = " AND is_directory = 0";
|
|
4945
|
+
if (type === "directory") typeClause = " AND is_directory = 1";
|
|
4946
|
+
const cursor = await this.sql.exec(
|
|
4947
|
+
`SELECT path, name, mime_type, storage, location, size, metadata, is_directory, created_at
|
|
4948
|
+
FROM files
|
|
4949
|
+
WHERE path LIKE ?${typeClause}
|
|
4950
|
+
ORDER BY path
|
|
4951
|
+
LIMIT ?`,
|
|
4952
|
+
sqlPattern,
|
|
4953
|
+
limit
|
|
4954
|
+
);
|
|
4955
|
+
return cursor.toArray().map(rowToFileRecord);
|
|
4956
|
+
}
|
|
4957
|
+
};
|
|
4958
|
+
}
|
|
4959
|
+
});
|
|
4960
|
+
|
|
4961
|
+
// src/image-processing/index.ts
|
|
4962
|
+
var image_processing_exports = {};
|
|
4963
|
+
__export(image_processing_exports, {
|
|
4964
|
+
arrayBufferToBase64: () => arrayBufferToBase64,
|
|
4965
|
+
base64ToArrayBuffer: () => base64ToArrayBuffer,
|
|
4966
|
+
needsProcessing: () => needsProcessing,
|
|
4967
|
+
processImage: () => processImage
|
|
4968
|
+
});
|
|
4969
|
+
async function processImage(input, inputMimeType) {
|
|
4970
|
+
if (input.byteLength > MAX_INPUT_SIZE) {
|
|
4971
|
+
throw new Error(`Image too large: ${input.byteLength} bytes exceeds ${MAX_INPUT_SIZE} byte limit`);
|
|
4972
|
+
}
|
|
4973
|
+
const probeResult = probe(input);
|
|
4974
|
+
const format = probeResult.format !== "unknown" ? probeResult.format : detectFormat(input, inputMimeType);
|
|
4975
|
+
const hasAlpha = probeResult.hasAlpha;
|
|
4976
|
+
if (hasAlpha && (format === "png" || format === "avif" || format === "webp")) {
|
|
4977
|
+
return await processPngWithAlpha(input, format);
|
|
4978
|
+
}
|
|
4979
|
+
try {
|
|
4980
|
+
const result = await sip.process(input, {
|
|
4981
|
+
maxWidth: MAX_DIMENSION,
|
|
4982
|
+
maxHeight: MAX_DIMENSION,
|
|
4983
|
+
maxBytes: MAX_SIZE,
|
|
4984
|
+
quality: 85
|
|
4985
|
+
});
|
|
4986
|
+
return {
|
|
4987
|
+
data: result.data,
|
|
4988
|
+
mimeType: "image/jpeg",
|
|
4989
|
+
width: result.width,
|
|
4990
|
+
height: result.height
|
|
4991
|
+
};
|
|
4992
|
+
} catch (err) {
|
|
4993
|
+
console.error("[sip] Processing failed, falling back:", err);
|
|
4994
|
+
throw err;
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
async function processPngWithAlpha(input, format) {
|
|
4998
|
+
let imageData;
|
|
4999
|
+
if (format === "avif") {
|
|
5000
|
+
imageData = await decode(input);
|
|
5001
|
+
} else {
|
|
5002
|
+
imageData = await decode$1(input);
|
|
5003
|
+
}
|
|
5004
|
+
const originalWidth = imageData.width;
|
|
5005
|
+
const originalHeight = imageData.height;
|
|
5006
|
+
let encoded = await encode(imageData);
|
|
5007
|
+
if (encoded.byteLength <= MAX_SIZE) {
|
|
5008
|
+
return {
|
|
5009
|
+
data: encoded,
|
|
5010
|
+
mimeType: "image/png",
|
|
5011
|
+
width: originalWidth,
|
|
5012
|
+
height: originalHeight
|
|
4151
5013
|
};
|
|
4152
5014
|
}
|
|
5015
|
+
let scale = 0.9;
|
|
5016
|
+
while (encoded.byteLength > MAX_SIZE && scale > 0.15) {
|
|
5017
|
+
const newWidth = Math.floor(originalWidth * scale);
|
|
5018
|
+
const newHeight = Math.floor(originalHeight * scale);
|
|
5019
|
+
if (newWidth < 100 || newHeight < 100) {
|
|
5020
|
+
scale *= 0.9;
|
|
5021
|
+
continue;
|
|
5022
|
+
}
|
|
5023
|
+
const resized = resizeRgba(
|
|
5024
|
+
new Uint8ClampedArray(imageData.data),
|
|
5025
|
+
originalWidth,
|
|
5026
|
+
originalHeight,
|
|
5027
|
+
newWidth,
|
|
5028
|
+
newHeight
|
|
5029
|
+
);
|
|
5030
|
+
const resizedImageData = { data: resized, width: newWidth, height: newHeight };
|
|
5031
|
+
encoded = await encode(resizedImageData);
|
|
5032
|
+
if (encoded.byteLength <= MAX_SIZE) {
|
|
5033
|
+
return {
|
|
5034
|
+
data: encoded,
|
|
5035
|
+
mimeType: "image/png",
|
|
5036
|
+
width: newWidth,
|
|
5037
|
+
height: newHeight
|
|
5038
|
+
};
|
|
5039
|
+
}
|
|
5040
|
+
scale *= 0.9;
|
|
5041
|
+
}
|
|
5042
|
+
const finalWidth = Math.floor(originalWidth * 0.15);
|
|
5043
|
+
const finalHeight = Math.floor(originalHeight * 0.15);
|
|
5044
|
+
const finalResized = resizeRgba(
|
|
5045
|
+
new Uint8ClampedArray(imageData.data),
|
|
5046
|
+
originalWidth,
|
|
5047
|
+
originalHeight,
|
|
5048
|
+
finalWidth,
|
|
5049
|
+
finalHeight
|
|
5050
|
+
);
|
|
5051
|
+
encoded = await encode({ data: finalResized, width: finalWidth, height: finalHeight });
|
|
5052
|
+
return {
|
|
5053
|
+
data: encoded,
|
|
5054
|
+
mimeType: "image/png",
|
|
5055
|
+
width: finalWidth,
|
|
5056
|
+
height: finalHeight
|
|
5057
|
+
};
|
|
5058
|
+
}
|
|
5059
|
+
function resizeRgba(src, srcWidth, srcHeight, dstWidth, dstHeight) {
|
|
5060
|
+
const dst = new Uint8ClampedArray(dstWidth * dstHeight * 4);
|
|
5061
|
+
const xScale = srcWidth / dstWidth;
|
|
5062
|
+
const yScale = srcHeight / dstHeight;
|
|
5063
|
+
for (let dstY = 0; dstY < dstHeight; dstY++) {
|
|
5064
|
+
for (let dstX = 0; dstX < dstWidth; dstX++) {
|
|
5065
|
+
const srcXFloat = dstX * xScale;
|
|
5066
|
+
const srcYFloat = dstY * yScale;
|
|
5067
|
+
const srcX0 = Math.floor(srcXFloat);
|
|
5068
|
+
const srcY0 = Math.floor(srcYFloat);
|
|
5069
|
+
const srcX1 = Math.min(srcX0 + 1, srcWidth - 1);
|
|
5070
|
+
const srcY1 = Math.min(srcY0 + 1, srcHeight - 1);
|
|
5071
|
+
const tx = srcXFloat - srcX0;
|
|
5072
|
+
const ty = srcYFloat - srcY0;
|
|
5073
|
+
const idx00 = (srcY0 * srcWidth + srcX0) * 4;
|
|
5074
|
+
const idx10 = (srcY0 * srcWidth + srcX1) * 4;
|
|
5075
|
+
const idx01 = (srcY1 * srcWidth + srcX0) * 4;
|
|
5076
|
+
const idx11 = (srcY1 * srcWidth + srcX1) * 4;
|
|
5077
|
+
const dstIdx = (dstY * dstWidth + dstX) * 4;
|
|
5078
|
+
for (let c = 0; c < 4; c++) {
|
|
5079
|
+
const v00 = src[idx00 + c];
|
|
5080
|
+
const v10 = src[idx10 + c];
|
|
5081
|
+
const v01 = src[idx01 + c];
|
|
5082
|
+
const v11 = src[idx11 + c];
|
|
5083
|
+
const top = v00 * (1 - tx) + v10 * tx;
|
|
5084
|
+
const bottom = v01 * (1 - tx) + v11 * tx;
|
|
5085
|
+
dst[dstIdx + c] = Math.round(top * (1 - ty) + bottom * ty);
|
|
5086
|
+
}
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
return dst;
|
|
5090
|
+
}
|
|
5091
|
+
function detectFormat(data, mimeType) {
|
|
5092
|
+
const bytes = new Uint8Array(data.slice(0, 12));
|
|
5093
|
+
if (bytes[4] === 102 && bytes[5] === 116 && bytes[6] === 121 && bytes[7] === 112) {
|
|
5094
|
+
const brand = String.fromCharCode(...bytes.slice(8, 12));
|
|
5095
|
+
if (brand === "avif" || brand === "avis") return "avif";
|
|
5096
|
+
}
|
|
5097
|
+
if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71) {
|
|
5098
|
+
return "png";
|
|
5099
|
+
}
|
|
5100
|
+
if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) {
|
|
5101
|
+
return "jpeg";
|
|
5102
|
+
}
|
|
5103
|
+
if (bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80) {
|
|
5104
|
+
return "webp";
|
|
5105
|
+
}
|
|
5106
|
+
if (mimeType.includes("png")) return "png";
|
|
5107
|
+
if (mimeType.includes("webp")) return "webp";
|
|
5108
|
+
if (mimeType.includes("avif")) return "avif";
|
|
5109
|
+
return "jpeg";
|
|
5110
|
+
}
|
|
5111
|
+
function needsProcessing(data, mimeType) {
|
|
5112
|
+
const binaryLength = Math.ceil(data.length * 3 / 4);
|
|
5113
|
+
return binaryLength > MAX_SIZE || mimeType.includes("avif") || mimeType.includes("webp");
|
|
5114
|
+
}
|
|
5115
|
+
function base64ToArrayBuffer(base64) {
|
|
5116
|
+
const binaryString = atob(base64);
|
|
5117
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
5118
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
5119
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
5120
|
+
}
|
|
5121
|
+
return bytes.buffer;
|
|
5122
|
+
}
|
|
5123
|
+
function arrayBufferToBase64(buffer) {
|
|
5124
|
+
const bytes = new Uint8Array(buffer);
|
|
5125
|
+
let binary = "";
|
|
5126
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
5127
|
+
binary += String.fromCharCode(bytes[i]);
|
|
5128
|
+
}
|
|
5129
|
+
return btoa(binary);
|
|
5130
|
+
}
|
|
5131
|
+
var MAX_SIZE, MAX_DIMENSION, MAX_INPUT_SIZE;
|
|
5132
|
+
var init_image_processing = __esm({
|
|
5133
|
+
"src/image-processing/index.ts"() {
|
|
5134
|
+
MAX_SIZE = 1.5 * 1024 * 1024;
|
|
5135
|
+
MAX_DIMENSION = 4096;
|
|
5136
|
+
MAX_INPUT_SIZE = 20 * 1024 * 1024;
|
|
5137
|
+
}
|
|
4153
5138
|
});
|
|
4154
5139
|
var TSCONFIG_CONTENT = `{
|
|
4155
5140
|
"compilerOptions": {
|
|
@@ -5134,7 +6119,7 @@ function generateAgentFile(data) {
|
|
|
5134
6119
|
if (data.type && data.type !== "ai_human") {
|
|
5135
6120
|
lines.push(` type: '${data.type}',`);
|
|
5136
6121
|
}
|
|
5137
|
-
if (data.maxSessionTurns !== void 0) {
|
|
6122
|
+
if (data.maxSessionTurns !== void 0 && data.maxSessionTurns !== null) {
|
|
5138
6123
|
lines.push(` maxSessionTurns: ${data.maxSessionTurns},`);
|
|
5139
6124
|
}
|
|
5140
6125
|
lines.push(` sideA: ${formatSideConfig(data.sideA)},`);
|
|
@@ -5777,7 +6762,7 @@ function validateAgentData(data) {
|
|
|
5777
6762
|
if (data.exposeAsTool && !data.toolDescription) {
|
|
5778
6763
|
return "toolDescription is required when exposeAsTool is true";
|
|
5779
6764
|
}
|
|
5780
|
-
if (data.maxSessionTurns !== void 0) {
|
|
6765
|
+
if (data.maxSessionTurns !== void 0 && data.maxSessionTurns !== null) {
|
|
5781
6766
|
if (typeof data.maxSessionTurns !== "number" || data.maxSessionTurns <= 0) {
|
|
5782
6767
|
return "maxSessionTurns must be a positive number";
|
|
5783
6768
|
}
|
|
@@ -5796,6 +6781,7 @@ function validateAgentData(data) {
|
|
|
5796
6781
|
}
|
|
5797
6782
|
|
|
5798
6783
|
// src/plugin.ts
|
|
6784
|
+
createRequire(import.meta.url);
|
|
5799
6785
|
var VIRTUAL_TOOLS_ID = "virtual:@standardagents-tools";
|
|
5800
6786
|
var RESOLVED_VIRTUAL_TOOLS_ID = "\0" + VIRTUAL_TOOLS_ID;
|
|
5801
6787
|
var VIRTUAL_ROUTES_ID = "virtual:@standardagents-routes";
|
|
@@ -6204,13 +7190,28 @@ function agentbuilder(options = {}) {
|
|
|
6204
7190
|
const depsToExclude = [
|
|
6205
7191
|
"@standardagents/builder",
|
|
6206
7192
|
"@standardagents/builder/runtime",
|
|
6207
|
-
"@standardagents/builder/built-in-routes"
|
|
7193
|
+
"@standardagents/builder/built-in-routes",
|
|
7194
|
+
"@standardagents/builder/image-processing",
|
|
7195
|
+
// WASM image processing deps - must be excluded to avoid pre-bundle cache issues
|
|
7196
|
+
"@cf-wasm/photon",
|
|
7197
|
+
"@cf-wasm/photon/workerd",
|
|
7198
|
+
"@jsquash/avif",
|
|
7199
|
+
"@jsquash/jpeg",
|
|
7200
|
+
"@jsquash/png",
|
|
7201
|
+
"@jsquash/webp",
|
|
7202
|
+
"@standardagents/sip"
|
|
7203
|
+
];
|
|
7204
|
+
const depsToInclude = [
|
|
7205
|
+
"zod",
|
|
7206
|
+
"openai"
|
|
6208
7207
|
];
|
|
6209
7208
|
return {
|
|
6210
7209
|
optimizeDeps: {
|
|
6211
7210
|
// Exclude our packages from pre-bundling - they contain cloudflare:workers imports
|
|
6212
7211
|
// that cannot be resolved during dependency optimization
|
|
6213
|
-
exclude: depsToExclude
|
|
7212
|
+
exclude: depsToExclude,
|
|
7213
|
+
// Include common deps upfront to prevent re-optimization during dev
|
|
7214
|
+
include: depsToInclude
|
|
6214
7215
|
},
|
|
6215
7216
|
ssr: {
|
|
6216
7217
|
// noExternal ensures Vite transforms these instead of leaving as external
|
|
@@ -6223,18 +7224,35 @@ function agentbuilder(options = {}) {
|
|
|
6223
7224
|
}
|
|
6224
7225
|
};
|
|
6225
7226
|
},
|
|
6226
|
-
// Apply exclusions to ALL environments including Cloudflare worker
|
|
7227
|
+
// Apply exclusions and inclusions to ALL environments including Cloudflare worker
|
|
6227
7228
|
configEnvironment(name, config) {
|
|
6228
7229
|
const depsToExclude = [
|
|
6229
7230
|
"@standardagents/builder",
|
|
6230
7231
|
"@standardagents/builder/runtime",
|
|
6231
|
-
"@standardagents/builder/built-in-routes"
|
|
7232
|
+
"@standardagents/builder/built-in-routes",
|
|
7233
|
+
"@standardagents/builder/image-processing",
|
|
7234
|
+
// WASM image processing deps
|
|
7235
|
+
"@cf-wasm/photon",
|
|
7236
|
+
"@cf-wasm/photon/workerd",
|
|
7237
|
+
"@jsquash/avif",
|
|
7238
|
+
"@jsquash/jpeg",
|
|
7239
|
+
"@jsquash/png",
|
|
7240
|
+
"@jsquash/webp",
|
|
7241
|
+
"@standardagents/sip"
|
|
7242
|
+
];
|
|
7243
|
+
const depsToInclude = [
|
|
7244
|
+
"zod",
|
|
7245
|
+
"openai"
|
|
6232
7246
|
];
|
|
6233
7247
|
config.optimizeDeps = config.optimizeDeps || {};
|
|
6234
7248
|
config.optimizeDeps.exclude = [
|
|
6235
7249
|
...config.optimizeDeps.exclude || [],
|
|
6236
7250
|
...depsToExclude.filter((dep) => !config.optimizeDeps?.exclude?.includes(dep))
|
|
6237
7251
|
];
|
|
7252
|
+
config.optimizeDeps.include = [
|
|
7253
|
+
...config.optimizeDeps.include || [],
|
|
7254
|
+
...depsToInclude.filter((dep) => !config.optimizeDeps?.include?.includes(dep))
|
|
7255
|
+
];
|
|
6238
7256
|
},
|
|
6239
7257
|
resolveId(id) {
|
|
6240
7258
|
if (id === VIRTUAL_TOOLS_ID) {
|
|
@@ -6672,6 +7690,11 @@ export const agentNames = ${JSON.stringify(agents.filter((a) => !a.error).map((a
|
|
|
6672
7690
|
import { DurableThread as _BaseDurableThread } from '@standardagents/builder/runtime';
|
|
6673
7691
|
import { DurableAgentBuilder as _BaseDurableAgentBuilder } from '@standardagents/builder/runtime';
|
|
6674
7692
|
|
|
7693
|
+
// Import sip WASM module and initializer
|
|
7694
|
+
// Static import allows workerd to pre-compile the WASM at bundle time
|
|
7695
|
+
import _sipWasm from '@standardagents/sip/dist/sip.wasm';
|
|
7696
|
+
import { initWithWasmModule as _initSipWasm } from '@standardagents/sip';
|
|
7697
|
+
|
|
6675
7698
|
// Re-export router from virtual:@standardagents-routes
|
|
6676
7699
|
export { router } from 'virtual:@standardagents-routes';
|
|
6677
7700
|
|
|
@@ -6701,6 +7724,16 @@ ${agentsCode}
|
|
|
6701
7724
|
* Simply extend this class in your agents/Thread.ts file.
|
|
6702
7725
|
*/
|
|
6703
7726
|
export class DurableThread extends _BaseDurableThread {
|
|
7727
|
+
constructor(ctx, env) {
|
|
7728
|
+
super(ctx, env);
|
|
7729
|
+
// Initialize sip WASM in DO constructor
|
|
7730
|
+
// blockConcurrencyWhile ensures WASM is ready before handling any requests
|
|
7731
|
+
// Pass the statically imported WASM module for workerd to pre-compile
|
|
7732
|
+
ctx.blockConcurrencyWhile(async () => {
|
|
7733
|
+
await _initSipWasm(_sipWasm);
|
|
7734
|
+
});
|
|
7735
|
+
}
|
|
7736
|
+
|
|
6704
7737
|
tools() {
|
|
6705
7738
|
return _tools;
|
|
6706
7739
|
}
|
|
@@ -8116,8 +9149,88 @@ var migration16 = {
|
|
|
8116
9149
|
}
|
|
8117
9150
|
};
|
|
8118
9151
|
|
|
9152
|
+
// src/durable-objects/migrations/017_add_files.ts
|
|
9153
|
+
var migration17 = {
|
|
9154
|
+
version: 17,
|
|
9155
|
+
async up(sql) {
|
|
9156
|
+
await sql.exec(`
|
|
9157
|
+
CREATE TABLE files (
|
|
9158
|
+
path TEXT PRIMARY KEY,
|
|
9159
|
+
name TEXT NOT NULL,
|
|
9160
|
+
mime_type TEXT NOT NULL,
|
|
9161
|
+
|
|
9162
|
+
-- Storage backend: 'local' | 'url' | 's3' | 'r2'
|
|
9163
|
+
storage TEXT NOT NULL DEFAULT 'local',
|
|
9164
|
+
-- External reference: URL, s3://bucket/key, r2://bucket/key
|
|
9165
|
+
location TEXT,
|
|
9166
|
+
|
|
9167
|
+
-- Local storage (NULL if external)
|
|
9168
|
+
data BLOB,
|
|
9169
|
+
content TEXT,
|
|
9170
|
+
|
|
9171
|
+
size INTEGER NOT NULL DEFAULT 0,
|
|
9172
|
+
metadata TEXT,
|
|
9173
|
+
thumbnail BLOB,
|
|
9174
|
+
is_directory INTEGER NOT NULL DEFAULT 0,
|
|
9175
|
+
created_at INTEGER NOT NULL
|
|
9176
|
+
)
|
|
9177
|
+
`);
|
|
9178
|
+
await sql.exec(`
|
|
9179
|
+
CREATE VIRTUAL TABLE files_fts USING fts5(
|
|
9180
|
+
path,
|
|
9181
|
+
name,
|
|
9182
|
+
content,
|
|
9183
|
+
content='files',
|
|
9184
|
+
content_rowid='rowid'
|
|
9185
|
+
)
|
|
9186
|
+
`);
|
|
9187
|
+
await sql.exec(`
|
|
9188
|
+
CREATE TRIGGER files_ai AFTER INSERT ON files BEGIN
|
|
9189
|
+
INSERT INTO files_fts(rowid, path, name, content)
|
|
9190
|
+
VALUES (new.rowid, new.path, new.name, new.content);
|
|
9191
|
+
END
|
|
9192
|
+
`);
|
|
9193
|
+
await sql.exec(`
|
|
9194
|
+
CREATE TRIGGER files_ad AFTER DELETE ON files BEGIN
|
|
9195
|
+
INSERT INTO files_fts(files_fts, rowid, path, name, content)
|
|
9196
|
+
VALUES('delete', old.rowid, old.path, old.name, old.content);
|
|
9197
|
+
END
|
|
9198
|
+
`);
|
|
9199
|
+
await sql.exec(`
|
|
9200
|
+
CREATE TRIGGER files_au AFTER UPDATE ON files BEGIN
|
|
9201
|
+
INSERT INTO files_fts(files_fts, rowid, path, name, content)
|
|
9202
|
+
VALUES('delete', old.rowid, old.path, old.name, old.content);
|
|
9203
|
+
INSERT INTO files_fts(rowid, path, name, content)
|
|
9204
|
+
VALUES (new.rowid, new.path, new.name, new.content);
|
|
9205
|
+
END
|
|
9206
|
+
`);
|
|
9207
|
+
await sql.exec(`
|
|
9208
|
+
CREATE TABLE file_stats (
|
|
9209
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
9210
|
+
total_size INTEGER DEFAULT 0,
|
|
9211
|
+
file_count INTEGER DEFAULT 0
|
|
9212
|
+
)
|
|
9213
|
+
`);
|
|
9214
|
+
await sql.exec(`
|
|
9215
|
+
INSERT INTO file_stats (id, total_size, file_count) VALUES (1, 0, 0)
|
|
9216
|
+
`);
|
|
9217
|
+
await sql.exec(`
|
|
9218
|
+
ALTER TABLE messages ADD COLUMN attachments TEXT
|
|
9219
|
+
`);
|
|
9220
|
+
await sql.exec(`
|
|
9221
|
+
CREATE INDEX idx_files_created_at ON files(created_at)
|
|
9222
|
+
`);
|
|
9223
|
+
await sql.exec(`
|
|
9224
|
+
CREATE INDEX idx_files_mime_type ON files(mime_type)
|
|
9225
|
+
`);
|
|
9226
|
+
await sql.exec(`
|
|
9227
|
+
UPDATE _metadata SET value = '17' WHERE key = 'schema_version'
|
|
9228
|
+
`);
|
|
9229
|
+
}
|
|
9230
|
+
};
|
|
9231
|
+
|
|
8119
9232
|
// src/durable-objects/migrations/index.ts
|
|
8120
|
-
var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16];
|
|
9233
|
+
var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16, migration17];
|
|
8121
9234
|
var LATEST_SCHEMA_VERSION = migrations.length;
|
|
8122
9235
|
|
|
8123
9236
|
// src/durable-objects/DurableThread.ts
|
|
@@ -8327,7 +9440,7 @@ var DurableThread = class extends DurableObject {
|
|
|
8327
9440
|
);
|
|
8328
9441
|
break;
|
|
8329
9442
|
case "processMessage":
|
|
8330
|
-
await this.processMessage(args.threadId, args.content, args.role);
|
|
9443
|
+
await this.processMessage(args.threadId, args.content, args.role, args.attachments);
|
|
8331
9444
|
break;
|
|
8332
9445
|
case "testOperation":
|
|
8333
9446
|
await this.testOperation(args.id, args.label, args.expectedOrder);
|
|
@@ -8518,9 +9631,9 @@ var DurableThread = class extends DurableObject {
|
|
|
8518
9631
|
* Each migration is run in order, starting from the current version + 1.
|
|
8519
9632
|
*/
|
|
8520
9633
|
async runMigrations(fromVersion) {
|
|
8521
|
-
for (const
|
|
8522
|
-
if (
|
|
8523
|
-
await
|
|
9634
|
+
for (const migration19 of migrations) {
|
|
9635
|
+
if (migration19.version > fromVersion) {
|
|
9636
|
+
await migration19.up(this.ctx.storage.sql);
|
|
8524
9637
|
}
|
|
8525
9638
|
}
|
|
8526
9639
|
}
|
|
@@ -8578,7 +9691,7 @@ var DurableThread = class extends DurableObject {
|
|
|
8578
9691
|
* Send a new message to the thread (RPC method)
|
|
8579
9692
|
* Enqueues the message processing to be handled by the alarm handler
|
|
8580
9693
|
*/
|
|
8581
|
-
async sendMessage(threadId, content, role = "user") {
|
|
9694
|
+
async sendMessage(threadId, content, role = "user", attachments) {
|
|
8582
9695
|
await this.ensureMigrated();
|
|
8583
9696
|
try {
|
|
8584
9697
|
const queueId = await this.alarmQueue.enqueue(
|
|
@@ -8586,7 +9699,8 @@ var DurableThread = class extends DurableObject {
|
|
|
8586
9699
|
{
|
|
8587
9700
|
threadId,
|
|
8588
9701
|
content,
|
|
8589
|
-
role
|
|
9702
|
+
role,
|
|
9703
|
+
attachments
|
|
8590
9704
|
},
|
|
8591
9705
|
1
|
|
8592
9706
|
// Execute almost immediately (1ms)
|
|
@@ -8797,7 +9911,7 @@ var DurableThread = class extends DurableObject {
|
|
|
8797
9911
|
const total = countResult.one().total;
|
|
8798
9912
|
const result = await this.ctx.storage.sql.exec(
|
|
8799
9913
|
`
|
|
8800
|
-
SELECT id, role, content, name, tool_calls, tool_call_id, tool_status, log_id, created_at, silent, parent_id, depth, status, reasoning_content, reasoning_details
|
|
9914
|
+
SELECT id, role, content, name, tool_calls, tool_call_id, tool_status, log_id, created_at, silent, parent_id, depth, status, reasoning_content, reasoning_details, attachments
|
|
8801
9915
|
FROM messages
|
|
8802
9916
|
${whereClause}
|
|
8803
9917
|
ORDER BY created_at ${order === "ASC" ? "ASC" : "DESC"}
|
|
@@ -8821,7 +9935,8 @@ var DurableThread = class extends DurableObject {
|
|
|
8821
9935
|
depth: row.depth,
|
|
8822
9936
|
status: row.status,
|
|
8823
9937
|
reasoning_content: row.reasoning_content,
|
|
8824
|
-
reasoning_details: row.reasoning_details
|
|
9938
|
+
reasoning_details: row.reasoning_details,
|
|
9939
|
+
attachments: row.attachments ? JSON.parse(row.attachments) : null
|
|
8825
9940
|
}));
|
|
8826
9941
|
if (order === "DESC") {
|
|
8827
9942
|
messages = messages.reverse();
|
|
@@ -9104,6 +10219,8 @@ var DurableThread = class extends DurableObject {
|
|
|
9104
10219
|
id: threadMetadata.agent_name,
|
|
9105
10220
|
title: agentDef.title || threadMetadata.agent_name,
|
|
9106
10221
|
type: agentDef.type,
|
|
10222
|
+
description: agentDef.description,
|
|
10223
|
+
icon: agentDef.icon,
|
|
9107
10224
|
side_a_label: agentDef.sideA?.label,
|
|
9108
10225
|
side_b_label: agentDef.sideB?.label
|
|
9109
10226
|
};
|
|
@@ -9526,7 +10643,7 @@ var DurableThread = class extends DurableObject {
|
|
|
9526
10643
|
* Internal method: Process a message (called by alarm queue)
|
|
9527
10644
|
* This is the actual message processing logic, separate from the public sendMessage() RPC method
|
|
9528
10645
|
*/
|
|
9529
|
-
async processMessage(threadId, content, role = "user") {
|
|
10646
|
+
async processMessage(threadId, content, role = "user", attachments) {
|
|
9530
10647
|
if (role === "user") {
|
|
9531
10648
|
await this.ctx.storage.sql.exec(
|
|
9532
10649
|
`UPDATE execution_state SET value = 'false' WHERE key = 'stopped'`
|
|
@@ -9576,6 +10693,7 @@ var DurableThread = class extends DurableObject {
|
|
|
9576
10693
|
id: messageId,
|
|
9577
10694
|
role,
|
|
9578
10695
|
content,
|
|
10696
|
+
attachments: attachments || null,
|
|
9579
10697
|
created_at: timestamp
|
|
9580
10698
|
};
|
|
9581
10699
|
const state = {
|
|
@@ -9591,16 +10709,18 @@ var DurableThread = class extends DurableObject {
|
|
|
9591
10709
|
const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
|
|
9592
10710
|
message = await FlowEngine2.runBeforeCreateMessageHook(state, message);
|
|
9593
10711
|
await this.ctx.storage.sql.exec(
|
|
9594
|
-
`INSERT INTO messages (id, role, content, created_at) VALUES (?, ?, ?, ?)`,
|
|
10712
|
+
`INSERT INTO messages (id, role, content, attachments, created_at) VALUES (?, ?, ?, ?, ?)`,
|
|
9595
10713
|
message.id,
|
|
9596
10714
|
message.role,
|
|
9597
10715
|
message.content,
|
|
10716
|
+
message.attachments,
|
|
9598
10717
|
message.created_at
|
|
9599
10718
|
);
|
|
9600
10719
|
this.broadcastMessage({
|
|
9601
10720
|
id: message.id,
|
|
9602
10721
|
role: message.role,
|
|
9603
10722
|
content: message.content,
|
|
10723
|
+
attachments: message.attachments,
|
|
9604
10724
|
created_at: message.created_at
|
|
9605
10725
|
});
|
|
9606
10726
|
const nextSide = role === "assistant" ? "b" : "a";
|
|
@@ -9769,10 +10889,297 @@ var DurableThread = class extends DurableObject {
|
|
|
9769
10889
|
now
|
|
9770
10890
|
);
|
|
9771
10891
|
}
|
|
10892
|
+
// ============================================================
|
|
10893
|
+
// File Storage Methods (RPC)
|
|
10894
|
+
// ============================================================
|
|
10895
|
+
/**
|
|
10896
|
+
* Get file storage helper instance
|
|
10897
|
+
*/
|
|
10898
|
+
getFileStorage() {
|
|
10899
|
+
const { FileStorage: FileStorage2 } = (init_files(), __toCommonJS(files_exports));
|
|
10900
|
+
return new FileStorage2(this.ctx.storage.sql);
|
|
10901
|
+
}
|
|
10902
|
+
/**
|
|
10903
|
+
* Write a file to storage (RPC method)
|
|
10904
|
+
*/
|
|
10905
|
+
async writeFile(path4, data, mimeType, options) {
|
|
10906
|
+
await this.ensureMigrated();
|
|
10907
|
+
try {
|
|
10908
|
+
const fs4 = this.getFileStorage();
|
|
10909
|
+
const binaryString = atob(data);
|
|
10910
|
+
const dataBuffer = new Uint8Array(binaryString.length);
|
|
10911
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
10912
|
+
dataBuffer[i] = binaryString.charCodeAt(i);
|
|
10913
|
+
}
|
|
10914
|
+
let thumbnailBuffer;
|
|
10915
|
+
if (options?.thumbnail) {
|
|
10916
|
+
const thumbBinary = atob(options.thumbnail);
|
|
10917
|
+
thumbnailBuffer = new Uint8Array(thumbBinary.length);
|
|
10918
|
+
for (let i = 0; i < thumbBinary.length; i++) {
|
|
10919
|
+
thumbnailBuffer[i] = thumbBinary.charCodeAt(i);
|
|
10920
|
+
}
|
|
10921
|
+
}
|
|
10922
|
+
const record = await fs4.writeFile(path4, dataBuffer, mimeType, {
|
|
10923
|
+
metadata: options?.metadata,
|
|
10924
|
+
thumbnail: thumbnailBuffer
|
|
10925
|
+
});
|
|
10926
|
+
return { success: true, file: record };
|
|
10927
|
+
} catch (error) {
|
|
10928
|
+
console.error("Error in writeFile:", error);
|
|
10929
|
+
return { success: false, error: error.message };
|
|
10930
|
+
}
|
|
10931
|
+
}
|
|
10932
|
+
/**
|
|
10933
|
+
* Write a text file to storage (RPC method)
|
|
10934
|
+
*/
|
|
10935
|
+
async writeTextFile(path4, content, mimeType = "text/plain", options) {
|
|
10936
|
+
await this.ensureMigrated();
|
|
10937
|
+
try {
|
|
10938
|
+
const fs4 = this.getFileStorage();
|
|
10939
|
+
const record = await fs4.writeFile(path4, content, mimeType, options);
|
|
10940
|
+
return { success: true, file: record };
|
|
10941
|
+
} catch (error) {
|
|
10942
|
+
console.error("Error in writeTextFile:", error);
|
|
10943
|
+
return { success: false, error: error.message };
|
|
10944
|
+
}
|
|
10945
|
+
}
|
|
10946
|
+
/**
|
|
10947
|
+
* Process an image using sip (WASM-based image processing)
|
|
10948
|
+
* This runs inside the DO where WASM is properly initialized.
|
|
10949
|
+
*
|
|
10950
|
+
* @param data - base64 encoded image data
|
|
10951
|
+
* @param mimeType - MIME type of the input image
|
|
10952
|
+
* @returns Processed image data, dimensions, and mimeType
|
|
10953
|
+
*/
|
|
10954
|
+
async processImage(data, mimeType) {
|
|
10955
|
+
try {
|
|
10956
|
+
const { processImage: processImage2, base64ToArrayBuffer: base64ToArrayBuffer2, arrayBufferToBase64: arrayBufferToBase642 } = await Promise.resolve().then(() => (init_image_processing(), image_processing_exports));
|
|
10957
|
+
const buffer = base64ToArrayBuffer2(data);
|
|
10958
|
+
const processed = await processImage2(buffer, mimeType);
|
|
10959
|
+
return {
|
|
10960
|
+
success: true,
|
|
10961
|
+
data: arrayBufferToBase642(processed.data),
|
|
10962
|
+
mimeType: processed.mimeType,
|
|
10963
|
+
width: processed.width,
|
|
10964
|
+
height: processed.height
|
|
10965
|
+
};
|
|
10966
|
+
} catch (error) {
|
|
10967
|
+
console.error("[DurableThread.processImage] Error:", error);
|
|
10968
|
+
return { success: false, error: error.message };
|
|
10969
|
+
}
|
|
10970
|
+
}
|
|
10971
|
+
/**
|
|
10972
|
+
* Link to an external file (RPC method)
|
|
10973
|
+
*/
|
|
10974
|
+
async linkFile(path4, location, options) {
|
|
10975
|
+
await this.ensureMigrated();
|
|
10976
|
+
try {
|
|
10977
|
+
const fs4 = this.getFileStorage();
|
|
10978
|
+
const record = await fs4.linkFile(path4, location, options);
|
|
10979
|
+
return { success: true, file: record };
|
|
10980
|
+
} catch (error) {
|
|
10981
|
+
console.error("Error in linkFile:", error);
|
|
10982
|
+
return { success: false, error: error.message };
|
|
10983
|
+
}
|
|
10984
|
+
}
|
|
10985
|
+
/**
|
|
10986
|
+
* Read a file from storage (RPC method)
|
|
10987
|
+
* Returns base64-encoded data
|
|
10988
|
+
*/
|
|
10989
|
+
async readFile(path4) {
|
|
10990
|
+
await this.ensureMigrated();
|
|
10991
|
+
try {
|
|
10992
|
+
const fs4 = this.getFileStorage();
|
|
10993
|
+
const data = await fs4.readFile(path4);
|
|
10994
|
+
if (data === null) {
|
|
10995
|
+
return { success: false, error: "File not found or external" };
|
|
10996
|
+
}
|
|
10997
|
+
const bytes = new Uint8Array(data);
|
|
10998
|
+
let binary = "";
|
|
10999
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
11000
|
+
binary += String.fromCharCode(bytes[i]);
|
|
11001
|
+
}
|
|
11002
|
+
const base64 = btoa(binary);
|
|
11003
|
+
return { success: true, data: base64 };
|
|
11004
|
+
} catch (error) {
|
|
11005
|
+
console.error("Error in readFile:", error);
|
|
11006
|
+
return { success: false, error: error.message };
|
|
11007
|
+
}
|
|
11008
|
+
}
|
|
11009
|
+
/**
|
|
11010
|
+
* Read a text file from storage (RPC method)
|
|
11011
|
+
*/
|
|
11012
|
+
async readTextFile(path4) {
|
|
11013
|
+
await this.ensureMigrated();
|
|
11014
|
+
try {
|
|
11015
|
+
const fs4 = this.getFileStorage();
|
|
11016
|
+
const content = await fs4.readTextFile(path4);
|
|
11017
|
+
if (content === null) {
|
|
11018
|
+
return { success: false, error: "File not found or external" };
|
|
11019
|
+
}
|
|
11020
|
+
return { success: true, content };
|
|
11021
|
+
} catch (error) {
|
|
11022
|
+
console.error("Error in readTextFile:", error);
|
|
11023
|
+
return { success: false, error: error.message };
|
|
11024
|
+
}
|
|
11025
|
+
}
|
|
11026
|
+
/**
|
|
11027
|
+
* Get file metadata (RPC method)
|
|
11028
|
+
*/
|
|
11029
|
+
async statFile(path4) {
|
|
11030
|
+
await this.ensureMigrated();
|
|
11031
|
+
try {
|
|
11032
|
+
const fs4 = this.getFileStorage();
|
|
11033
|
+
const record = await fs4.stat(path4);
|
|
11034
|
+
if (record === null) {
|
|
11035
|
+
return { success: false, error: "File not found" };
|
|
11036
|
+
}
|
|
11037
|
+
return { success: true, file: record };
|
|
11038
|
+
} catch (error) {
|
|
11039
|
+
console.error("Error in statFile:", error);
|
|
11040
|
+
return { success: false, error: error.message };
|
|
11041
|
+
}
|
|
11042
|
+
}
|
|
11043
|
+
/**
|
|
11044
|
+
* Check if file exists (RPC method)
|
|
11045
|
+
*/
|
|
11046
|
+
async fileExists(path4) {
|
|
11047
|
+
await this.ensureMigrated();
|
|
11048
|
+
try {
|
|
11049
|
+
const fs4 = this.getFileStorage();
|
|
11050
|
+
const exists2 = await fs4.exists(path4);
|
|
11051
|
+
return { success: true, exists: exists2 };
|
|
11052
|
+
} catch (error) {
|
|
11053
|
+
console.error("Error in fileExists:", error);
|
|
11054
|
+
return { success: false, error: error.message };
|
|
11055
|
+
}
|
|
11056
|
+
}
|
|
11057
|
+
/**
|
|
11058
|
+
* Delete a file (RPC method)
|
|
11059
|
+
*/
|
|
11060
|
+
async unlinkFile(path4) {
|
|
11061
|
+
await this.ensureMigrated();
|
|
11062
|
+
try {
|
|
11063
|
+
const fs4 = this.getFileStorage();
|
|
11064
|
+
await fs4.unlink(path4);
|
|
11065
|
+
return { success: true };
|
|
11066
|
+
} catch (error) {
|
|
11067
|
+
console.error("Error in unlinkFile:", error);
|
|
11068
|
+
return { success: false, error: error.message };
|
|
11069
|
+
}
|
|
11070
|
+
}
|
|
11071
|
+
/**
|
|
11072
|
+
* Create a directory (RPC method)
|
|
11073
|
+
*/
|
|
11074
|
+
async mkdirFile(path4) {
|
|
11075
|
+
await this.ensureMigrated();
|
|
11076
|
+
try {
|
|
11077
|
+
const fs4 = this.getFileStorage();
|
|
11078
|
+
const record = await fs4.mkdir(path4);
|
|
11079
|
+
return { success: true, directory: record };
|
|
11080
|
+
} catch (error) {
|
|
11081
|
+
console.error("Error in mkdirFile:", error);
|
|
11082
|
+
return { success: false, error: error.message };
|
|
11083
|
+
}
|
|
11084
|
+
}
|
|
11085
|
+
/**
|
|
11086
|
+
* List directory contents (RPC method)
|
|
11087
|
+
*/
|
|
11088
|
+
async readdirFile(path4) {
|
|
11089
|
+
await this.ensureMigrated();
|
|
11090
|
+
try {
|
|
11091
|
+
const fs4 = this.getFileStorage();
|
|
11092
|
+
const files = await fs4.readdir(path4);
|
|
11093
|
+
return { success: true, files };
|
|
11094
|
+
} catch (error) {
|
|
11095
|
+
console.error("Error in readdirFile:", error);
|
|
11096
|
+
return { success: false, error: error.message };
|
|
11097
|
+
}
|
|
11098
|
+
}
|
|
11099
|
+
/**
|
|
11100
|
+
* Remove empty directory (RPC method)
|
|
11101
|
+
*/
|
|
11102
|
+
async rmdirFile(path4) {
|
|
11103
|
+
await this.ensureMigrated();
|
|
11104
|
+
try {
|
|
11105
|
+
const fs4 = this.getFileStorage();
|
|
11106
|
+
await fs4.rmdir(path4);
|
|
11107
|
+
return { success: true };
|
|
11108
|
+
} catch (error) {
|
|
11109
|
+
console.error("Error in rmdirFile:", error);
|
|
11110
|
+
return { success: false, error: error.message };
|
|
11111
|
+
}
|
|
11112
|
+
}
|
|
11113
|
+
/**
|
|
11114
|
+
* Get file storage statistics (RPC method)
|
|
11115
|
+
*/
|
|
11116
|
+
async getFileStats() {
|
|
11117
|
+
await this.ensureMigrated();
|
|
11118
|
+
try {
|
|
11119
|
+
const fs4 = this.getFileStorage();
|
|
11120
|
+
const stats = await fs4.getFileStats();
|
|
11121
|
+
return { success: true, stats };
|
|
11122
|
+
} catch (error) {
|
|
11123
|
+
console.error("Error in getFileStats:", error);
|
|
11124
|
+
return { success: false, error: error.message };
|
|
11125
|
+
}
|
|
11126
|
+
}
|
|
11127
|
+
/**
|
|
11128
|
+
* Get thumbnail for an image (RPC method)
|
|
11129
|
+
* Returns base64-encoded data
|
|
11130
|
+
*/
|
|
11131
|
+
async getFileThumbnail(path4) {
|
|
11132
|
+
await this.ensureMigrated();
|
|
11133
|
+
try {
|
|
11134
|
+
const fs4 = this.getFileStorage();
|
|
11135
|
+
const data = await fs4.getThumbnail(path4);
|
|
11136
|
+
if (data === null) {
|
|
11137
|
+
return { success: false, error: "Thumbnail not found" };
|
|
11138
|
+
}
|
|
11139
|
+
const bytes = new Uint8Array(data);
|
|
11140
|
+
let binary = "";
|
|
11141
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
11142
|
+
binary += String.fromCharCode(bytes[i]);
|
|
11143
|
+
}
|
|
11144
|
+
const base64 = btoa(binary);
|
|
11145
|
+
return { success: true, data: base64 };
|
|
11146
|
+
} catch (error) {
|
|
11147
|
+
console.error("Error in getFileThumbnail:", error);
|
|
11148
|
+
return { success: false, error: error.message };
|
|
11149
|
+
}
|
|
11150
|
+
}
|
|
11151
|
+
/**
|
|
11152
|
+
* Search file contents using FTS5 (RPC method)
|
|
11153
|
+
*/
|
|
11154
|
+
async grepFiles(pattern, options) {
|
|
11155
|
+
await this.ensureMigrated();
|
|
11156
|
+
try {
|
|
11157
|
+
const fs4 = this.getFileStorage();
|
|
11158
|
+
const results = await fs4.grep(pattern, options);
|
|
11159
|
+
return { success: true, results };
|
|
11160
|
+
} catch (error) {
|
|
11161
|
+
console.error("Error in grepFiles:", error);
|
|
11162
|
+
return { success: false, error: error.message };
|
|
11163
|
+
}
|
|
11164
|
+
}
|
|
11165
|
+
/**
|
|
11166
|
+
* Find files by path pattern (RPC method)
|
|
11167
|
+
*/
|
|
11168
|
+
async findFiles(pattern, options) {
|
|
11169
|
+
await this.ensureMigrated();
|
|
11170
|
+
try {
|
|
11171
|
+
const fs4 = this.getFileStorage();
|
|
11172
|
+
const files = await fs4.find(pattern, options);
|
|
11173
|
+
return { success: true, files };
|
|
11174
|
+
} catch (error) {
|
|
11175
|
+
console.error("Error in findFiles:", error);
|
|
11176
|
+
return { success: false, error: error.message };
|
|
11177
|
+
}
|
|
11178
|
+
}
|
|
9772
11179
|
};
|
|
9773
11180
|
|
|
9774
11181
|
// src/durable-objects/agentbuilder-migrations/0001_initial.ts
|
|
9775
|
-
var
|
|
11182
|
+
var migration18 = {
|
|
9776
11183
|
version: 1,
|
|
9777
11184
|
async up(sql) {
|
|
9778
11185
|
sql.exec(`
|
|
@@ -9871,7 +11278,7 @@ var migration17 = {
|
|
|
9871
11278
|
};
|
|
9872
11279
|
|
|
9873
11280
|
// src/durable-objects/agentbuilder-migrations/index.ts
|
|
9874
|
-
var migrations2 = [
|
|
11281
|
+
var migrations2 = [migration18];
|
|
9875
11282
|
var LATEST_SCHEMA_VERSION2 = 1;
|
|
9876
11283
|
|
|
9877
11284
|
// src/durable-objects/DurableAgentBuilder.ts
|
|
@@ -9961,9 +11368,9 @@ var DurableAgentBuilder = class extends DurableObject {
|
|
|
9961
11368
|
}
|
|
9962
11369
|
}
|
|
9963
11370
|
async runMigrations(fromVersion) {
|
|
9964
|
-
for (const
|
|
9965
|
-
if (
|
|
9966
|
-
await
|
|
11371
|
+
for (const migration19 of migrations2) {
|
|
11372
|
+
if (migration19.version > fromVersion) {
|
|
11373
|
+
await migration19.up(this.ctx.storage.sql);
|
|
9967
11374
|
}
|
|
9968
11375
|
}
|
|
9969
11376
|
}
|
|
@@ -10731,6 +12138,12 @@ function definePrompt(options) {
|
|
|
10731
12138
|
`Invalid reasoning.effort '${options.reasoning.effort}'. Must be one of: low, medium, high`
|
|
10732
12139
|
);
|
|
10733
12140
|
}
|
|
12141
|
+
if (options.maxImagePixels !== void 0 && (options.maxImagePixels <= 0 || !Number.isInteger(options.maxImagePixels))) {
|
|
12142
|
+
throw new Error("maxImagePixels must be a positive integer");
|
|
12143
|
+
}
|
|
12144
|
+
if (options.recentImageThreshold !== void 0 && (options.recentImageThreshold <= 0 || !Number.isInteger(options.recentImageThreshold))) {
|
|
12145
|
+
throw new Error("recentImageThreshold must be a positive integer");
|
|
12146
|
+
}
|
|
10734
12147
|
return options;
|
|
10735
12148
|
}
|
|
10736
12149
|
|
|
@@ -10769,7 +12182,7 @@ function defineAgent(options) {
|
|
|
10769
12182
|
if (options.sideB?.maxTurns !== void 0 && options.sideB.maxTurns <= 0) {
|
|
10770
12183
|
throw new Error("sideB.maxTurns must be a positive number");
|
|
10771
12184
|
}
|
|
10772
|
-
if (options.maxSessionTurns !== void 0 && options.maxSessionTurns <= 0) {
|
|
12185
|
+
if (options.maxSessionTurns !== void 0 && options.maxSessionTurns !== null && options.maxSessionTurns <= 0) {
|
|
10773
12186
|
throw new Error("maxSessionTurns must be a positive number");
|
|
10774
12187
|
}
|
|
10775
12188
|
if (!["ai_human", "dual_ai"].includes(type)) {
|
|
@@ -10923,6 +12336,165 @@ async function updateThread(thread, params) {
|
|
|
10923
12336
|
}
|
|
10924
12337
|
return result.thread;
|
|
10925
12338
|
}
|
|
12339
|
+
async function writeFile(flow, path4, data, mimeType, options) {
|
|
12340
|
+
const instance = flow.thread.instance;
|
|
12341
|
+
let base64Data;
|
|
12342
|
+
if (typeof data === "string") {
|
|
12343
|
+
base64Data = btoa(data);
|
|
12344
|
+
} else {
|
|
12345
|
+
const bytes = new Uint8Array(data);
|
|
12346
|
+
let binary = "";
|
|
12347
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
12348
|
+
binary += String.fromCharCode(bytes[i]);
|
|
12349
|
+
}
|
|
12350
|
+
base64Data = btoa(binary);
|
|
12351
|
+
}
|
|
12352
|
+
let base64Thumbnail;
|
|
12353
|
+
if (options?.thumbnail) {
|
|
12354
|
+
const bytes = new Uint8Array(options.thumbnail);
|
|
12355
|
+
let binary = "";
|
|
12356
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
12357
|
+
binary += String.fromCharCode(bytes[i]);
|
|
12358
|
+
}
|
|
12359
|
+
base64Thumbnail = btoa(binary);
|
|
12360
|
+
}
|
|
12361
|
+
const result = await instance.writeFile(path4, base64Data, mimeType, {
|
|
12362
|
+
metadata: options?.metadata,
|
|
12363
|
+
thumbnail: base64Thumbnail
|
|
12364
|
+
});
|
|
12365
|
+
if (!result.success) {
|
|
12366
|
+
throw new Error(result.error || "Failed to write file");
|
|
12367
|
+
}
|
|
12368
|
+
return result.file;
|
|
12369
|
+
}
|
|
12370
|
+
async function writeImage(flow, path4, data, mimeType, options) {
|
|
12371
|
+
return writeFile(flow, path4, data, mimeType, options);
|
|
12372
|
+
}
|
|
12373
|
+
async function linkFile(flow, path4, location, options) {
|
|
12374
|
+
const instance = flow.thread.instance;
|
|
12375
|
+
const result = await instance.linkFile(path4, location, options);
|
|
12376
|
+
if (!result.success) {
|
|
12377
|
+
throw new Error(result.error || "Failed to link file");
|
|
12378
|
+
}
|
|
12379
|
+
return result.file;
|
|
12380
|
+
}
|
|
12381
|
+
async function readFile(flow, path4) {
|
|
12382
|
+
const instance = flow.thread.instance;
|
|
12383
|
+
const result = await instance.readFile(path4);
|
|
12384
|
+
if (!result.success) {
|
|
12385
|
+
return null;
|
|
12386
|
+
}
|
|
12387
|
+
const binary = atob(result.data);
|
|
12388
|
+
const bytes = new Uint8Array(binary.length);
|
|
12389
|
+
for (let i = 0; i < binary.length; i++) {
|
|
12390
|
+
bytes[i] = binary.charCodeAt(i);
|
|
12391
|
+
}
|
|
12392
|
+
return bytes.buffer;
|
|
12393
|
+
}
|
|
12394
|
+
async function stat(flow, path4) {
|
|
12395
|
+
const instance = flow.thread.instance;
|
|
12396
|
+
const result = await instance.statFile(path4);
|
|
12397
|
+
if (!result.success) {
|
|
12398
|
+
return null;
|
|
12399
|
+
}
|
|
12400
|
+
return result.file;
|
|
12401
|
+
}
|
|
12402
|
+
async function exists(flow, path4) {
|
|
12403
|
+
const instance = flow.thread.instance;
|
|
12404
|
+
const result = await instance.fileExists(path4);
|
|
12405
|
+
return result.success && result.exists;
|
|
12406
|
+
}
|
|
12407
|
+
async function unlink(flow, path4) {
|
|
12408
|
+
const instance = flow.thread.instance;
|
|
12409
|
+
const result = await instance.unlinkFile(path4);
|
|
12410
|
+
if (!result.success) {
|
|
12411
|
+
throw new Error(result.error || "Failed to delete file");
|
|
12412
|
+
}
|
|
12413
|
+
}
|
|
12414
|
+
async function mkdir(flow, path4) {
|
|
12415
|
+
const instance = flow.thread.instance;
|
|
12416
|
+
const result = await instance.mkdirFile(path4);
|
|
12417
|
+
if (!result.success) {
|
|
12418
|
+
throw new Error(result.error || "Failed to create directory");
|
|
12419
|
+
}
|
|
12420
|
+
return result.directory;
|
|
12421
|
+
}
|
|
12422
|
+
async function readdir(flow, path4) {
|
|
12423
|
+
const instance = flow.thread.instance;
|
|
12424
|
+
const result = await instance.readdirFile(path4);
|
|
12425
|
+
if (!result.success) {
|
|
12426
|
+
throw new Error(result.error || "Failed to read directory");
|
|
12427
|
+
}
|
|
12428
|
+
return result.files;
|
|
12429
|
+
}
|
|
12430
|
+
async function rmdir(flow, path4) {
|
|
12431
|
+
const instance = flow.thread.instance;
|
|
12432
|
+
const result = await instance.rmdirFile(path4);
|
|
12433
|
+
if (!result.success) {
|
|
12434
|
+
throw new Error(result.error || "Failed to remove directory");
|
|
12435
|
+
}
|
|
12436
|
+
}
|
|
12437
|
+
async function getFileStats(flow) {
|
|
12438
|
+
const instance = flow.thread.instance;
|
|
12439
|
+
const result = await instance.getFileStats();
|
|
12440
|
+
if (!result.success) {
|
|
12441
|
+
throw new Error(result.error || "Failed to get file stats");
|
|
12442
|
+
}
|
|
12443
|
+
return result.stats;
|
|
12444
|
+
}
|
|
12445
|
+
async function getThumbnail(flow, path4) {
|
|
12446
|
+
const instance = flow.thread.instance;
|
|
12447
|
+
const result = await instance.getFileThumbnail(path4);
|
|
12448
|
+
if (!result.success) {
|
|
12449
|
+
return null;
|
|
12450
|
+
}
|
|
12451
|
+
const binary = atob(result.data);
|
|
12452
|
+
const bytes = new Uint8Array(binary.length);
|
|
12453
|
+
for (let i = 0; i < binary.length; i++) {
|
|
12454
|
+
bytes[i] = binary.charCodeAt(i);
|
|
12455
|
+
}
|
|
12456
|
+
return bytes.buffer;
|
|
12457
|
+
}
|
|
12458
|
+
async function cat(flow, path4) {
|
|
12459
|
+
const instance = flow.thread.instance;
|
|
12460
|
+
const result = await instance.readTextFile(path4);
|
|
12461
|
+
if (!result.success) {
|
|
12462
|
+
return null;
|
|
12463
|
+
}
|
|
12464
|
+
return result.content;
|
|
12465
|
+
}
|
|
12466
|
+
async function head(flow, path4, lines = 10) {
|
|
12467
|
+
const content = await cat(flow, path4);
|
|
12468
|
+
if (content === null) return null;
|
|
12469
|
+
const allLines = content.split("\n");
|
|
12470
|
+
return allLines.slice(0, lines).join("\n");
|
|
12471
|
+
}
|
|
12472
|
+
async function tail(flow, path4, lines = 10) {
|
|
12473
|
+
const content = await cat(flow, path4);
|
|
12474
|
+
if (content === null) return null;
|
|
12475
|
+
const allLines = content.split("\n");
|
|
12476
|
+
return allLines.slice(-lines).join("\n");
|
|
12477
|
+
}
|
|
12478
|
+
async function grep(flow, pattern, options) {
|
|
12479
|
+
const instance = flow.thread.instance;
|
|
12480
|
+
const result = await instance.grepFiles(pattern, options);
|
|
12481
|
+
if (!result.success) {
|
|
12482
|
+
throw new Error(result.error || "Search failed");
|
|
12483
|
+
}
|
|
12484
|
+
return result.results.map((r) => ({
|
|
12485
|
+
path: r.path,
|
|
12486
|
+
name: r.name,
|
|
12487
|
+
matches: [r.snippet]
|
|
12488
|
+
}));
|
|
12489
|
+
}
|
|
12490
|
+
async function find(flow, pattern, options) {
|
|
12491
|
+
const instance = flow.thread.instance;
|
|
12492
|
+
const result = await instance.findFiles(pattern, options);
|
|
12493
|
+
if (!result.success) {
|
|
12494
|
+
throw new Error(result.error || "Find failed");
|
|
12495
|
+
}
|
|
12496
|
+
return result.files;
|
|
12497
|
+
}
|
|
10926
12498
|
|
|
10927
12499
|
// src/agents/FlowStateSdk.ts
|
|
10928
12500
|
init_types();
|
|
@@ -11257,6 +12829,9 @@ function enhanceFlowState(flow) {
|
|
|
11257
12829
|
return enhanced;
|
|
11258
12830
|
}
|
|
11259
12831
|
|
|
12832
|
+
// src/index.ts
|
|
12833
|
+
init_context();
|
|
12834
|
+
|
|
11260
12835
|
// src/github/GitHubClient.ts
|
|
11261
12836
|
var GITHUB_API_BASE = "https://api.github.com";
|
|
11262
12837
|
var GitHubClient = class _GitHubClient {
|
|
@@ -11494,6 +13069,6 @@ var GitHubApiError = class extends Error {
|
|
|
11494
13069
|
}
|
|
11495
13070
|
};
|
|
11496
13071
|
|
|
11497
|
-
export { DurableAgentBuilder, DurableThread, FlowStateSdk, GitHubApiError, GitHubClient, agentbuilder, authenticate, defineAgent, defineController, defineHook, defineModel, definePrompt, defineThreadEndpoint, defineTool, emitThreadEvent, enhanceFlowState, forceTurn, generateAgentFile, generateModelFile, generatePromptFile, getMessages, injectMessage, queueTool, reloadHistory, requireAdmin, requireAuth, updateThread };
|
|
13072
|
+
export { DurableAgentBuilder, DurableThread, FlowStateSdk, GitHubApiError, GitHubClient, agentbuilder, authenticate, buildImageDescription, cat, defineAgent, defineController, defineHook, defineModel, definePrompt, defineThreadEndpoint, defineTool, emitThreadEvent, enhanceFlowState, exists, find, forceTurn, generateAgentFile, generateImageDescription, generateModelFile, generatePromptFile, getFileStats, getMessages, getMessagesToSummarize, getThumbnail, getUnsummarizedImageAttachments, grep, hasImageAttachments, head, injectMessage, linkFile, mkdir, optimizeImageContext, queueTool, readFile, readdir, reloadHistory, replaceImagesWithDescriptions, requireAdmin, requireAuth, rmdir, stat, tail, unlink, updateThread, writeFile, writeImage };
|
|
11498
13073
|
//# sourceMappingURL=index.js.map
|
|
11499
13074
|
//# sourceMappingURL=index.js.map
|