@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.
@@ -848,12 +848,23 @@ var index_post_default3 = defineController(async ({ req, env }) => {
848
848
  });
849
849
 
850
850
  // src/api/threads/index.ts
851
+ function resolveIconUrl(icon, origin) {
852
+ if (!icon) return void 0;
853
+ if (icon.startsWith("http://") || icon.startsWith("https://")) {
854
+ return icon;
855
+ }
856
+ if (icon.startsWith("/")) {
857
+ return `${origin}${icon}`;
858
+ }
859
+ return icon;
860
+ }
851
861
  var threads_default = defineController(async ({ req, env }) => {
852
862
  if (req.method !== "GET") {
853
863
  return new Response("Method Not Allowed", { status: 405 });
854
864
  }
855
865
  try {
856
866
  const url = new URL(req.url);
867
+ const origin = url.origin;
857
868
  const limit = parseInt(url.searchParams.get("limit") || "50", 10);
858
869
  const offset = parseInt(url.searchParams.get("offset") || "0", 10);
859
870
  const agentName = url.searchParams.get("agent_id");
@@ -872,11 +883,14 @@ var threads_default = defineController(async ({ req, env }) => {
872
883
  try {
873
884
  const agentDef = await agentBuilder.loadAgent(thread.agent_name);
874
885
  agentDetails = {
875
- title: agentDef.title,
876
- type: agentDef.type || "ai_human"
886
+ name: thread.agent_name,
887
+ title: agentDef.title || thread.agent_name,
888
+ type: agentDef.type || "ai_human",
889
+ description: agentDef.description,
890
+ icon: resolveIconUrl(agentDef.icon, origin)
877
891
  };
878
892
  } catch {
879
- agentDetails = { title: thread.agent_name, type: "unknown" };
893
+ agentDetails = { name: thread.agent_name, title: thread.agent_name, type: "unknown" };
880
894
  }
881
895
  return {
882
896
  id: thread.id,
@@ -1447,6 +1461,16 @@ var id_patch_default = defineController(async ({ req, env, params }) => {
1447
1461
  });
1448
1462
 
1449
1463
  // src/api/threads/[id].ts
1464
+ function resolveIconUrl2(icon, origin) {
1465
+ if (!icon) return void 0;
1466
+ if (icon.startsWith("http://") || icon.startsWith("https://")) {
1467
+ return icon;
1468
+ }
1469
+ if (icon.startsWith("/")) {
1470
+ return `${origin}${icon}`;
1471
+ }
1472
+ return icon;
1473
+ }
1450
1474
  var id_default = defineController(async ({ req, params, env }) => {
1451
1475
  const threadId = params.id;
1452
1476
  if (!threadId) {
@@ -1462,6 +1486,10 @@ var id_default = defineController(async ({ req, params, env }) => {
1462
1486
  { status: 404 }
1463
1487
  );
1464
1488
  }
1489
+ if (doData.agent?.icon) {
1490
+ const origin = new URL(req.url).origin;
1491
+ doData.agent.icon = resolveIconUrl2(doData.agent.icon, origin);
1492
+ }
1465
1493
  return Response.json(doData);
1466
1494
  } catch (error) {
1467
1495
  console.error(`Error fetching thread ${threadId}:`, error);
@@ -1979,6 +2007,66 @@ var cost_get_default = defineController(async ({ req, params, env }) => {
1979
2007
  }
1980
2008
  });
1981
2009
 
2010
+ // src/api/threads/[id]/fs/index.ts
2011
+ var fs_default = defineController(async ({ req, params, env }) => {
2012
+ const threadId = params.id;
2013
+ if (!threadId) {
2014
+ return Response.json({ error: "Thread ID required" }, { status: 400 });
2015
+ }
2016
+ if (req.method !== "GET") {
2017
+ return Response.json(
2018
+ { error: `Method ${req.method} not allowed` },
2019
+ { status: 405 }
2020
+ );
2021
+ }
2022
+ const url = new URL(req.url);
2023
+ try {
2024
+ const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
2025
+ const stub = env.AGENT_BUILDER_THREAD.get(durableId);
2026
+ if (url.searchParams.has("stats")) {
2027
+ const result2 = await stub.getFileStats();
2028
+ if (!result2.success) {
2029
+ return Response.json({ error: result2.error }, { status: 500 });
2030
+ }
2031
+ return Response.json(result2.stats);
2032
+ }
2033
+ const grepPattern = url.searchParams.get("grep");
2034
+ if (grepPattern) {
2035
+ const path = url.searchParams.get("path") || void 0;
2036
+ const limit = parseInt(url.searchParams.get("limit") || "100", 10);
2037
+ const result2 = await stub.grepFiles(grepPattern, { path, limit });
2038
+ if (!result2.success) {
2039
+ return Response.json({ error: result2.error }, { status: 500 });
2040
+ }
2041
+ return Response.json({ results: result2.results, pattern: grepPattern });
2042
+ }
2043
+ const findPattern = url.searchParams.get("find");
2044
+ if (findPattern) {
2045
+ const type = url.searchParams.get("type");
2046
+ const limit = parseInt(url.searchParams.get("limit") || "100", 10);
2047
+ const result2 = await stub.findFiles(findPattern, {
2048
+ type: type || "all",
2049
+ limit
2050
+ });
2051
+ if (!result2.success) {
2052
+ return Response.json({ error: result2.error }, { status: 500 });
2053
+ }
2054
+ return Response.json({ files: result2.files, pattern: findPattern });
2055
+ }
2056
+ const result = await stub.readdirFile("/");
2057
+ if (!result.success) {
2058
+ return Response.json({ error: result.error }, { status: 500 });
2059
+ }
2060
+ return Response.json({ files: result.files, path: "/" });
2061
+ } catch (error) {
2062
+ console.error(`Error in fs root API for thread ${threadId}:`, error);
2063
+ return Response.json(
2064
+ { error: error.message || "File operation failed" },
2065
+ { status: 500 }
2066
+ );
2067
+ }
2068
+ });
2069
+
1982
2070
  // src/api/threads/[id]/logs.get.ts
1983
2071
  var logs_get_default = defineController(async ({ req, params, env }) => {
1984
2072
  const url = new URL(req.url);
@@ -2017,8 +2105,19 @@ var logs_get_default = defineController(async ({ req, params, env }) => {
2017
2105
  }
2018
2106
  });
2019
2107
 
2020
- // src/api/threads/[id]/message.post.ts
2021
- var message_post_default = defineController(async ({ req, params, env }) => {
2108
+ // src/api/threads/[id]/messages.post.ts
2109
+ function needsProcessing(base64Data, mimeType) {
2110
+ const binarySize = Math.ceil(base64Data.length * 3 / 4);
2111
+ const MAX_SIZE = 2 * 1024 * 1024;
2112
+ if (binarySize > MAX_SIZE) {
2113
+ return true;
2114
+ }
2115
+ if (mimeType !== "image/jpeg" && mimeType !== "image/png") {
2116
+ return true;
2117
+ }
2118
+ return false;
2119
+ }
2120
+ var messages_post_default = defineController(async ({ req, params, env }) => {
2022
2121
  const threadId = params.id;
2023
2122
  if (!threadId) {
2024
2123
  return Response.json({ error: "Thread ID required" }, { status: 400 });
@@ -2034,16 +2133,83 @@ var message_post_default = defineController(async ({ req, params, env }) => {
2034
2133
  );
2035
2134
  }
2036
2135
  const body = await req.json();
2037
- const { content, role } = body;
2038
- if (!content || typeof content !== "string") {
2136
+ const { content = "", role, attachments, silent } = body;
2137
+ const hasContent = content && typeof content === "string" && content.trim().length > 0;
2138
+ const hasAttachments = attachments && Array.isArray(attachments) && attachments.length > 0;
2139
+ if (!hasContent && !hasAttachments) {
2039
2140
  return Response.json(
2040
- { error: "Message content is required" },
2141
+ { error: "Message content or attachments required" },
2041
2142
  { status: 400 }
2042
2143
  );
2043
2144
  }
2044
2145
  const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
2045
2146
  const stub = env.AGENT_BUILDER_THREAD.get(durableId);
2046
- return await stub.sendMessage(threadId, content, role);
2147
+ let attachmentRefs;
2148
+ if (attachments && attachments.length > 0) {
2149
+ attachmentRefs = [];
2150
+ for (const attachment of attachments) {
2151
+ const attachmentId = crypto.randomUUID();
2152
+ const timestamp = Date.now();
2153
+ let fileData = attachment.data;
2154
+ let mimeType = attachment.mimeType;
2155
+ let width = attachment.width;
2156
+ let height = attachment.height;
2157
+ if (mimeType.startsWith("image/") && needsProcessing(fileData, mimeType)) {
2158
+ try {
2159
+ const originalSize = Math.ceil(fileData.length * 3 / 4);
2160
+ const processed = await stub.processImage(fileData, mimeType);
2161
+ if (processed.success && processed.data) {
2162
+ fileData = processed.data;
2163
+ mimeType = processed.mimeType;
2164
+ width = processed.width;
2165
+ height = processed.height;
2166
+ const newSize = Math.ceil(fileData.length * 3 / 4);
2167
+ console.log(
2168
+ `[image-processing] Processed ${attachment.name}: ${(originalSize / 1024 / 1024).toFixed(2)}MB \u2192 ${(newSize / 1024 / 1024).toFixed(2)}MB, ${processed.width}x${processed.height}, ${processed.mimeType}`
2169
+ );
2170
+ } else {
2171
+ console.error(`[image-processing] Failed to process ${attachment.name}:`, processed.error);
2172
+ }
2173
+ } catch (err) {
2174
+ console.error(`[image-processing] Failed to process ${attachment.name}:`, err);
2175
+ }
2176
+ }
2177
+ const ext = mimeType === "image/png" ? "png" : mimeType === "image/jpeg" ? "jpg" : attachment.name.split(".").pop() || "bin";
2178
+ const path = `/attachments/${timestamp}-${attachmentId}.${ext}`;
2179
+ const metadata = {};
2180
+ if (width) metadata.width = width;
2181
+ if (height) metadata.height = height;
2182
+ const base64Size = fileData.length;
2183
+ const binarySize = Math.ceil(base64Size * 3 / 4);
2184
+ console.log(`[writeFile] Storing ${attachment.name}: ${(binarySize / 1024 / 1024).toFixed(2)}MB binary (${(base64Size / 1024 / 1024).toFixed(2)}MB base64) to ${path}`);
2185
+ const result = await stub.writeFile(
2186
+ path,
2187
+ fileData,
2188
+ mimeType,
2189
+ {
2190
+ metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
2191
+ thumbnail: attachment.thumbnail
2192
+ }
2193
+ );
2194
+ console.log(`[writeFile] Result:`, result.success ? "success" : result.error);
2195
+ if (!result.success) {
2196
+ console.error(`Failed to store attachment ${attachment.name}:`, result.error);
2197
+ continue;
2198
+ }
2199
+ const ref = {
2200
+ id: attachmentId,
2201
+ type: "file",
2202
+ path,
2203
+ name: attachment.name,
2204
+ mimeType
2205
+ };
2206
+ if (width) ref.width = width;
2207
+ if (height) ref.height = height;
2208
+ attachmentRefs.push(ref);
2209
+ }
2210
+ }
2211
+ const attachmentsJson = attachmentRefs && attachmentRefs.length > 0 ? JSON.stringify(attachmentRefs) : void 0;
2212
+ return await stub.sendMessage(threadId, content, role, attachmentsJson);
2047
2213
  } catch (error) {
2048
2214
  console.error(`Error sending message to thread ${threadId}:`, error);
2049
2215
  return Response.json(
@@ -2184,6 +2350,190 @@ var stream_default = defineController(async ({ req, params, env }) => {
2184
2350
  }
2185
2351
  });
2186
2352
 
2353
+ // src/api/threads/[id]/fs/[...path].ts
2354
+ function toAttachmentRef(file) {
2355
+ const metadata = file.metadata;
2356
+ return {
2357
+ id: file.path,
2358
+ // Use path as unique ID
2359
+ type: "file",
2360
+ path: file.path,
2361
+ name: file.name,
2362
+ mimeType: file.mimeType,
2363
+ size: file.size,
2364
+ ...metadata?.width && { width: metadata.width },
2365
+ ...metadata?.height && { height: metadata.height }
2366
+ };
2367
+ }
2368
+ var path_default = defineController(async ({ req, params, env }) => {
2369
+ console.log("[fs] params received:", JSON.stringify(params));
2370
+ const threadId = params.id;
2371
+ const pathParam = params._ || params.path || params["*"] || "";
2372
+ if (!threadId) {
2373
+ return Response.json({ error: "Thread ID required" }, { status: 400 });
2374
+ }
2375
+ console.log("[fs] threadId:", threadId, "pathParam:", pathParam);
2376
+ const path = pathParam ? `/${pathParam}` : "/";
2377
+ const url = new URL(req.url);
2378
+ try {
2379
+ const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
2380
+ const stub = env.AGENT_BUILDER_THREAD.get(durableId);
2381
+ switch (req.method) {
2382
+ case "GET":
2383
+ return handleGet(stub, path, url);
2384
+ case "PUT":
2385
+ return handlePut(stub, path, req);
2386
+ case "DELETE":
2387
+ return handleDelete(stub, path);
2388
+ default:
2389
+ return Response.json(
2390
+ { error: `Method ${req.method} not allowed` },
2391
+ { status: 405 }
2392
+ );
2393
+ }
2394
+ } catch (error) {
2395
+ console.error(`Error in fs API for thread ${threadId}:`, error);
2396
+ return Response.json(
2397
+ { error: error.message || "File operation failed" },
2398
+ { status: 500 }
2399
+ );
2400
+ }
2401
+ });
2402
+ async function handleGet(stub, path, url) {
2403
+ if (url.searchParams.has("stats")) {
2404
+ const result2 = await stub.getFileStats();
2405
+ if (!result2.success) {
2406
+ return Response.json({ error: result2.error }, { status: 500 });
2407
+ }
2408
+ return Response.json(result2.stats);
2409
+ }
2410
+ if (url.searchParams.has("thumbnail")) {
2411
+ const result2 = await stub.getFileThumbnail(path);
2412
+ if (!result2.success) {
2413
+ return Response.json({ error: result2.error }, { status: 404 });
2414
+ }
2415
+ const binary2 = atob(result2.data);
2416
+ const bytes2 = new Uint8Array(binary2.length);
2417
+ for (let i = 0; i < binary2.length; i++) {
2418
+ bytes2[i] = binary2.charCodeAt(i);
2419
+ }
2420
+ return new Response(bytes2, {
2421
+ headers: {
2422
+ "Content-Type": "image/jpeg",
2423
+ // Thumbnails are typically JPEG
2424
+ "Cache-Control": "public, max-age=31536000"
2425
+ // Cache for 1 year
2426
+ }
2427
+ });
2428
+ }
2429
+ const statResult = await stub.statFile(path);
2430
+ if (!statResult.success) {
2431
+ return Response.json({ error: "Not found" }, { status: 404 });
2432
+ }
2433
+ const file = statResult.file;
2434
+ if (file.isDirectory) {
2435
+ const result2 = await stub.readdirFile(path);
2436
+ if (!result2.success) {
2437
+ return Response.json({ error: result2.error }, { status: 500 });
2438
+ }
2439
+ return Response.json({ files: result2.files, path });
2440
+ }
2441
+ if (file.storage !== "local") {
2442
+ return Response.json({
2443
+ file,
2444
+ external: true,
2445
+ location: file.location
2446
+ });
2447
+ }
2448
+ const acceptHeader = url.searchParams.get("format") || "auto";
2449
+ if (acceptHeader === "json") {
2450
+ return Response.json({ file });
2451
+ }
2452
+ const result = await stub.readFile(path);
2453
+ if (!result.success) {
2454
+ return Response.json({ error: result.error }, { status: 404 });
2455
+ }
2456
+ const binary = atob(result.data);
2457
+ const bytes = new Uint8Array(binary.length);
2458
+ for (let i = 0; i < binary.length; i++) {
2459
+ bytes[i] = binary.charCodeAt(i);
2460
+ }
2461
+ return new Response(bytes, {
2462
+ headers: {
2463
+ "Content-Type": file.mimeType,
2464
+ "Content-Length": String(file.size),
2465
+ "Content-Disposition": `inline; filename="${file.name}"`
2466
+ }
2467
+ });
2468
+ }
2469
+ async function handlePut(stub, path, req) {
2470
+ const contentType = req.headers.get("Content-Type") || "";
2471
+ if (contentType.includes("application/json")) {
2472
+ const body = await req.json();
2473
+ if (body.type === "directory") {
2474
+ const result2 = await stub.mkdirFile(path);
2475
+ if (!result2.success) {
2476
+ return Response.json({ error: result2.error }, { status: 400 });
2477
+ }
2478
+ return Response.json({ directory: result2.directory }, { status: 201 });
2479
+ }
2480
+ if (body.location) {
2481
+ const result2 = await stub.linkFile(path, body.location, {
2482
+ mimeType: body.mimeType,
2483
+ size: body.size,
2484
+ metadata: body.metadata
2485
+ });
2486
+ if (!result2.success) {
2487
+ return Response.json({ error: result2.error }, { status: 400 });
2488
+ }
2489
+ return Response.json(toAttachmentRef(result2.file), { status: 201 });
2490
+ }
2491
+ if (body.data) {
2492
+ const result2 = await stub.writeFile(path, body.data, body.mimeType || "application/octet-stream", {
2493
+ metadata: body.metadata,
2494
+ thumbnail: body.thumbnail
2495
+ });
2496
+ if (!result2.success) {
2497
+ return Response.json({ error: result2.error }, { status: 400 });
2498
+ }
2499
+ return Response.json(toAttachmentRef(result2.file), { status: 201 });
2500
+ }
2501
+ return Response.json({ error: "Invalid request body" }, { status: 400 });
2502
+ }
2503
+ const data = await req.arrayBuffer();
2504
+ const mimeType = contentType.split(";")[0].trim() || "application/octet-stream";
2505
+ const bytes = new Uint8Array(data);
2506
+ let binary = "";
2507
+ for (let i = 0; i < bytes.length; i++) {
2508
+ binary += String.fromCharCode(bytes[i]);
2509
+ }
2510
+ const base64 = btoa(binary);
2511
+ const result = await stub.writeFile(path, base64, mimeType, {});
2512
+ if (!result.success) {
2513
+ return Response.json({ error: result.error }, { status: 400 });
2514
+ }
2515
+ return Response.json(toAttachmentRef(result.file), { status: 201 });
2516
+ }
2517
+ async function handleDelete(stub, path) {
2518
+ const statResult = await stub.statFile(path);
2519
+ if (!statResult.success) {
2520
+ return Response.json({ error: "Not found" }, { status: 404 });
2521
+ }
2522
+ const file = statResult.file;
2523
+ if (file.isDirectory) {
2524
+ const result = await stub.rmdirFile(path);
2525
+ if (!result.success) {
2526
+ return Response.json({ error: result.error }, { status: 400 });
2527
+ }
2528
+ } else {
2529
+ const result = await stub.unlinkFile(path);
2530
+ if (!result.success) {
2531
+ return Response.json({ error: result.error }, { status: 400 });
2532
+ }
2533
+ }
2534
+ return new Response(null, { status: 204 });
2535
+ }
2536
+
2187
2537
  // src/api/threads/[id]/logs/[logId].get.ts
2188
2538
  var logId_get_default = defineController(async ({ req, params, env }) => {
2189
2539
  const threadId = params.id;
@@ -2344,12 +2694,14 @@ var routeHandlers = {
2344
2694
  "POST:/models/available": available_post_default,
2345
2695
  "POST:/models/endpoints": endpoints_post_default,
2346
2696
  "GET:/threads/:id/cost": cost_get_default,
2697
+ "GET:/threads/:id/fs": fs_default,
2347
2698
  "GET:/threads/:id/logs": logs_get_default,
2348
- "POST:/threads/:id/message": message_post_default,
2699
+ "POST:/threads/:id/messages": messages_post_default,
2349
2700
  "GET:/threads/:id/messages": messages_get_default,
2350
2701
  "POST:/threads/:id/rpc": rpc_post_default,
2351
2702
  "POST:/threads/:id/stop": stop_post_default,
2352
2703
  "GET:/threads/:id/stream": stream_default,
2704
+ "GET:/threads/:id/fs/**": path_default,
2353
2705
  "GET:/threads/:id/logs/:logId": logId_get_default,
2354
2706
  "DELETE:/threads/:id/messages/:messageId": messageId_delete_default,
2355
2707
  "PATCH:/threads/:id/messages/:messageId": messageId_patch_default