@standardagents/builder 0.10.0 → 0.10.1-dev.114898

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/index.js CHANGED
@@ -1,6 +1,10 @@
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;
@@ -264,13 +268,28 @@ var init_BaseProvider = __esm({
264
268
  *
265
269
  * NOTE: Only updates if request_body is NULL - LLMRequest may have already
266
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.
267
274
  */
268
275
  async logActualRequest(request, logId, state) {
269
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
+ }
270
289
  const requestBody = JSON.stringify(request);
271
290
  await state.stream.waitFor(async () => {
272
291
  await state.storage.sql.exec(
273
- `UPDATE logs SET request_body = ?1 WHERE id = ?2 AND request_body IS NULL`,
292
+ `UPDATE logs SET request_body = ?1 WHERE id = ?2`,
274
293
  requestBody,
275
294
  logId
276
295
  );
@@ -4938,6 +4957,185 @@ var init_files = __esm({
4938
4957
  };
4939
4958
  }
4940
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
5013
+ };
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
+ }
5138
+ });
4941
5139
  var TSCONFIG_CONTENT = `{
4942
5140
  "compilerOptions": {
4943
5141
  "composite": true,
@@ -5921,7 +6119,7 @@ function generateAgentFile(data) {
5921
6119
  if (data.type && data.type !== "ai_human") {
5922
6120
  lines.push(` type: '${data.type}',`);
5923
6121
  }
5924
- if (data.maxSessionTurns !== void 0) {
6122
+ if (data.maxSessionTurns !== void 0 && data.maxSessionTurns !== null) {
5925
6123
  lines.push(` maxSessionTurns: ${data.maxSessionTurns},`);
5926
6124
  }
5927
6125
  lines.push(` sideA: ${formatSideConfig(data.sideA)},`);
@@ -6564,7 +6762,7 @@ function validateAgentData(data) {
6564
6762
  if (data.exposeAsTool && !data.toolDescription) {
6565
6763
  return "toolDescription is required when exposeAsTool is true";
6566
6764
  }
6567
- if (data.maxSessionTurns !== void 0) {
6765
+ if (data.maxSessionTurns !== void 0 && data.maxSessionTurns !== null) {
6568
6766
  if (typeof data.maxSessionTurns !== "number" || data.maxSessionTurns <= 0) {
6569
6767
  return "maxSessionTurns must be a positive number";
6570
6768
  }
@@ -6583,6 +6781,7 @@ function validateAgentData(data) {
6583
6781
  }
6584
6782
 
6585
6783
  // src/plugin.ts
6784
+ createRequire(import.meta.url);
6586
6785
  var VIRTUAL_TOOLS_ID = "virtual:@standardagents-tools";
6587
6786
  var RESOLVED_VIRTUAL_TOOLS_ID = "\0" + VIRTUAL_TOOLS_ID;
6588
6787
  var VIRTUAL_ROUTES_ID = "virtual:@standardagents-routes";
@@ -6998,13 +7197,21 @@ function agentbuilder(options = {}) {
6998
7197
  "@cf-wasm/photon/workerd",
6999
7198
  "@jsquash/avif",
7000
7199
  "@jsquash/jpeg",
7001
- "@jsquash/png"
7200
+ "@jsquash/png",
7201
+ "@jsquash/webp",
7202
+ "@standardagents/sip"
7203
+ ];
7204
+ const depsToInclude = [
7205
+ "zod",
7206
+ "openai"
7002
7207
  ];
7003
7208
  return {
7004
7209
  optimizeDeps: {
7005
7210
  // Exclude our packages from pre-bundling - they contain cloudflare:workers imports
7006
7211
  // that cannot be resolved during dependency optimization
7007
- exclude: depsToExclude
7212
+ exclude: depsToExclude,
7213
+ // Include common deps upfront to prevent re-optimization during dev
7214
+ include: depsToInclude
7008
7215
  },
7009
7216
  ssr: {
7010
7217
  // noExternal ensures Vite transforms these instead of leaving as external
@@ -7017,7 +7224,7 @@ function agentbuilder(options = {}) {
7017
7224
  }
7018
7225
  };
7019
7226
  },
7020
- // Apply exclusions to ALL environments including Cloudflare worker
7227
+ // Apply exclusions and inclusions to ALL environments including Cloudflare worker
7021
7228
  configEnvironment(name, config) {
7022
7229
  const depsToExclude = [
7023
7230
  "@standardagents/builder",
@@ -7029,13 +7236,23 @@ function agentbuilder(options = {}) {
7029
7236
  "@cf-wasm/photon/workerd",
7030
7237
  "@jsquash/avif",
7031
7238
  "@jsquash/jpeg",
7032
- "@jsquash/png"
7239
+ "@jsquash/png",
7240
+ "@jsquash/webp",
7241
+ "@standardagents/sip"
7242
+ ];
7243
+ const depsToInclude = [
7244
+ "zod",
7245
+ "openai"
7033
7246
  ];
7034
7247
  config.optimizeDeps = config.optimizeDeps || {};
7035
7248
  config.optimizeDeps.exclude = [
7036
7249
  ...config.optimizeDeps.exclude || [],
7037
7250
  ...depsToExclude.filter((dep) => !config.optimizeDeps?.exclude?.includes(dep))
7038
7251
  ];
7252
+ config.optimizeDeps.include = [
7253
+ ...config.optimizeDeps.include || [],
7254
+ ...depsToInclude.filter((dep) => !config.optimizeDeps?.include?.includes(dep))
7255
+ ];
7039
7256
  },
7040
7257
  resolveId(id) {
7041
7258
  if (id === VIRTUAL_TOOLS_ID) {
@@ -7473,6 +7690,11 @@ export const agentNames = ${JSON.stringify(agents.filter((a) => !a.error).map((a
7473
7690
  import { DurableThread as _BaseDurableThread } from '@standardagents/builder/runtime';
7474
7691
  import { DurableAgentBuilder as _BaseDurableAgentBuilder } from '@standardagents/builder/runtime';
7475
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
+
7476
7698
  // Re-export router from virtual:@standardagents-routes
7477
7699
  export { router } from 'virtual:@standardagents-routes';
7478
7700
 
@@ -7502,6 +7724,16 @@ ${agentsCode}
7502
7724
  * Simply extend this class in your agents/Thread.ts file.
7503
7725
  */
7504
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
+
7505
7737
  tools() {
7506
7738
  return _tools;
7507
7739
  }
@@ -9987,6 +10219,8 @@ var DurableThread = class extends DurableObject {
9987
10219
  id: threadMetadata.agent_name,
9988
10220
  title: agentDef.title || threadMetadata.agent_name,
9989
10221
  type: agentDef.type,
10222
+ description: agentDef.description,
10223
+ icon: agentDef.icon,
9990
10224
  side_a_label: agentDef.sideA?.label,
9991
10225
  side_b_label: agentDef.sideB?.label
9992
10226
  };
@@ -10672,8 +10906,19 @@ var DurableThread = class extends DurableObject {
10672
10906
  await this.ensureMigrated();
10673
10907
  try {
10674
10908
  const fs4 = this.getFileStorage();
10675
- const dataBuffer = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
10676
- const thumbnailBuffer = options?.thumbnail ? Uint8Array.from(atob(options.thumbnail), (c) => c.charCodeAt(0)) : void 0;
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
+ }
10677
10922
  const record = await fs4.writeFile(path4, dataBuffer, mimeType, {
10678
10923
  metadata: options?.metadata,
10679
10924
  thumbnail: thumbnailBuffer
@@ -10698,6 +10943,31 @@ var DurableThread = class extends DurableObject {
10698
10943
  return { success: false, error: error.message };
10699
10944
  }
10700
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
+ }
10701
10971
  /**
10702
10972
  * Link to an external file (RPC method)
10703
10973
  */
@@ -11912,7 +12182,7 @@ function defineAgent(options) {
11912
12182
  if (options.sideB?.maxTurns !== void 0 && options.sideB.maxTurns <= 0) {
11913
12183
  throw new Error("sideB.maxTurns must be a positive number");
11914
12184
  }
11915
- if (options.maxSessionTurns !== void 0 && options.maxSessionTurns <= 0) {
12185
+ if (options.maxSessionTurns !== void 0 && options.maxSessionTurns !== null && options.maxSessionTurns <= 0) {
11916
12186
  throw new Error("maxSessionTurns must be a positive number");
11917
12187
  }
11918
12188
  if (!["ai_human", "dual_ai"].includes(type)) {