veryfront 0.0.72 → 0.0.74

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/ai/index.js CHANGED
@@ -79,19 +79,69 @@ function getEnvironmentVariable(name) {
79
79
  }
80
80
  return void 0;
81
81
  }
82
+ function isProductionEnvironment() {
83
+ return getEnvironmentVariable("NODE_ENV") === "production";
84
+ }
82
85
 
83
86
  // src/core/utils/logger/logger.ts
84
87
  var cachedLogLevel;
88
+ var cachedLogFormat;
85
89
  function resolveLogLevel(force = false) {
86
90
  if (force || cachedLogLevel === void 0) {
87
91
  cachedLogLevel = getDefaultLevel();
88
92
  }
89
93
  return cachedLogLevel;
90
94
  }
91
- var ConsoleLogger = class {
92
- constructor(prefix, level = resolveLogLevel()) {
95
+ function resolveLogFormat(force = false) {
96
+ if (force || cachedLogFormat === void 0) {
97
+ cachedLogFormat = getDefaultFormat();
98
+ }
99
+ return cachedLogFormat;
100
+ }
101
+ function getDefaultFormat() {
102
+ const envFormat = getEnvironmentVariable("LOG_FORMAT");
103
+ if (envFormat === "json" || envFormat === "text") {
104
+ return envFormat;
105
+ }
106
+ return isProductionEnvironment() ? "json" : "text";
107
+ }
108
+ function serializeError(err) {
109
+ if (err instanceof Error) {
110
+ return {
111
+ name: err.name,
112
+ message: err.message,
113
+ stack: err.stack
114
+ };
115
+ }
116
+ if (err !== void 0 && err !== null) {
117
+ return {
118
+ name: "UnknownError",
119
+ message: String(err)
120
+ };
121
+ }
122
+ return void 0;
123
+ }
124
+ function extractContext(args) {
125
+ let context;
126
+ let error;
127
+ for (const arg of args) {
128
+ if (arg instanceof Error) {
129
+ error = serializeError(arg);
130
+ } else if (typeof arg === "object" && arg !== null && !Array.isArray(arg)) {
131
+ context = { ...context, ...arg };
132
+ }
133
+ }
134
+ return { context, error };
135
+ }
136
+ var ConsoleLogger = class _ConsoleLogger {
137
+ constructor(prefix, level = resolveLogLevel(), format = resolveLogFormat(), boundContext) {
93
138
  this.prefix = prefix;
94
139
  this.level = level;
140
+ this.format = format;
141
+ this.boundContext = {};
142
+ if (boundContext) {
143
+ this.boundContext = boundContext;
144
+ }
95
145
  }
96
146
  setLevel(level) {
97
147
  this.level = level;
@@ -99,36 +149,88 @@ var ConsoleLogger = class {
99
149
  getLevel() {
100
150
  return this.level;
101
151
  }
102
- debug(message, ...args) {
103
- if (this.level <= 0 /* DEBUG */) {
104
- console.debug(`[${this.prefix}] DEBUG: ${message}`, ...args);
152
+ setFormat(format) {
153
+ this.format = format;
154
+ }
155
+ getFormat() {
156
+ return this.format;
157
+ }
158
+ /**
159
+ * Create a child logger with additional bound context.
160
+ */
161
+ child(context) {
162
+ return new _ConsoleLogger(this.prefix, this.level, this.format, {
163
+ ...this.boundContext,
164
+ ...context
165
+ });
166
+ }
167
+ formatJson(level, message, args) {
168
+ const { context, error } = extractContext(args);
169
+ const entry = {
170
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
171
+ level,
172
+ service: this.prefix.toLowerCase(),
173
+ message
174
+ };
175
+ const mergedContext = { ...this.boundContext, ...context };
176
+ if (Object.keys(mergedContext).length > 0) {
177
+ if ("requestId" in mergedContext) {
178
+ entry.requestId = String(mergedContext.requestId);
179
+ delete mergedContext.requestId;
180
+ }
181
+ if ("traceId" in mergedContext) {
182
+ entry.traceId = String(mergedContext.traceId);
183
+ delete mergedContext.traceId;
184
+ }
185
+ if ("projectSlug" in mergedContext) {
186
+ entry.projectSlug = String(mergedContext.projectSlug);
187
+ delete mergedContext.projectSlug;
188
+ }
189
+ if ("durationMs" in mergedContext) {
190
+ entry.durationMs = Number(mergedContext.durationMs);
191
+ delete mergedContext.durationMs;
192
+ }
193
+ if (Object.keys(mergedContext).length > 0) {
194
+ entry.context = mergedContext;
195
+ }
196
+ }
197
+ if (error) {
198
+ entry.error = error;
105
199
  }
200
+ return JSON.stringify(entry);
106
201
  }
107
- info(message, ...args) {
108
- if (this.level <= 1 /* INFO */) {
109
- console.log(`[${this.prefix}] ${message}`, ...args);
202
+ log(level, logLevel, consoleFn, message, args) {
203
+ if (this.level > logLevel)
204
+ return;
205
+ if (this.format === "json") {
206
+ consoleFn(this.formatJson(level, message, args));
207
+ } else {
208
+ const prefix = level === "info" ? "" : ` ${level.toUpperCase()}:`;
209
+ consoleFn(`[${this.prefix}]${prefix} ${message}`, ...args);
110
210
  }
111
211
  }
212
+ debug(message, ...args) {
213
+ this.log("debug", 0 /* DEBUG */, console.debug, message, args);
214
+ }
215
+ info(message, ...args) {
216
+ this.log("info", 1 /* INFO */, console.log, message, args);
217
+ }
112
218
  warn(message, ...args) {
113
- if (this.level <= 2 /* WARN */) {
114
- console.warn(`[${this.prefix}] WARN: ${message}`, ...args);
115
- }
219
+ this.log("warn", 2 /* WARN */, console.warn, message, args);
116
220
  }
117
221
  error(message, ...args) {
118
- if (this.level <= 3 /* ERROR */) {
119
- console.error(`[${this.prefix}] ERROR: ${message}`, ...args);
120
- }
222
+ this.log("error", 3 /* ERROR */, console.error, message, args);
121
223
  }
122
224
  async time(label, fn) {
123
225
  const start = performance.now();
124
226
  try {
125
227
  const result = await fn();
126
- const end = performance.now();
127
- this.debug(`${label} completed in ${(end - start).toFixed(2)}ms`);
228
+ const durationMs = performance.now() - start;
229
+ this.debug(`${label} completed`, { durationMs: Math.round(durationMs) });
128
230
  return result;
129
231
  } catch (error) {
130
- const end = performance.now();
131
- this.error(`${label} failed after ${(end - start).toFixed(2)}ms`, error);
232
+ const durationMs = performance.now() - start;
233
+ this.error(`${label} failed`, { durationMs: Math.round(durationMs) }, error);
132
234
  throw error;
133
235
  }
134
236
  }
@@ -171,6 +273,7 @@ var serverLogger = createLogger("SERVER");
171
273
  var rendererLogger = createLogger("RENDERER");
172
274
  var bundlerLogger = createLogger("BUNDLER");
173
275
  var agentLogger = createLogger("AGENT");
276
+ var proxyLogger = createLogger("PROXY");
174
277
  var logger = createLogger("VERYFRONT");
175
278
 
176
279
  // src/ai/providers/base.ts
@@ -191,6 +294,29 @@ var OpenAIStreamChunkSchema = z.object({
191
294
  finish_reason: z.string().nullable()
192
295
  })).min(1)
193
296
  });
297
+ var OpenAICompletionResponseSchema = z.object({
298
+ id: z.string(),
299
+ choices: z.array(z.object({
300
+ message: z.object({
301
+ role: z.string(),
302
+ content: z.string().nullable().optional(),
303
+ tool_calls: z.array(z.object({
304
+ id: z.string(),
305
+ type: z.literal("function"),
306
+ function: z.object({
307
+ name: z.string(),
308
+ arguments: z.string()
309
+ })
310
+ })).optional()
311
+ }),
312
+ finish_reason: z.string().nullable()
313
+ })).min(1),
314
+ usage: z.object({
315
+ prompt_tokens: z.number(),
316
+ completion_tokens: z.number(),
317
+ total_tokens: z.number()
318
+ }).optional()
319
+ });
194
320
  var BaseProvider = class {
195
321
  constructor(config) {
196
322
  this.config = config;
@@ -230,7 +356,17 @@ var BaseProvider = class {
230
356
  }));
231
357
  }
232
358
  const data = await response.json();
233
- return this.transformResponse(data);
359
+ const parseResult = OpenAICompletionResponseSchema.safeParse(data);
360
+ if (!parseResult.success) {
361
+ agentLogger.warn(`${this.name}: Invalid response structure`, {
362
+ errors: parseResult.error.flatten()
363
+ });
364
+ throw toError(createError({
365
+ type: "agent",
366
+ message: `${this.name}: Invalid response structure from provider`
367
+ }));
368
+ }
369
+ return this.transformResponse(parseResult.data);
234
370
  }
235
371
  /**
236
372
  * Stream a completion
@@ -455,6 +591,7 @@ var OpenAIProvider = class extends BaseProvider {
455
591
  parameters: tool3.parameters
456
592
  }
457
593
  }));
594
+ body.parallel_tool_calls = false;
458
595
  }
459
596
  return body;
460
597
  }
@@ -905,7 +1042,7 @@ var GoogleProvider = class extends BaseProvider {
905
1042
  };
906
1043
 
907
1044
  // src/platform/compat/runtime.ts
908
- var isDeno = typeof Deno !== "undefined";
1045
+ var isDeno = typeof Deno !== "undefined" && typeof Deno.version === "object";
909
1046
  var isNode = typeof globalThis.process !== "undefined" && globalThis.process?.versions?.node !== void 0;
910
1047
  var isBun = typeof globalThis.Bun !== "undefined";
911
1048
  var isCloudflare = typeof globalThis !== "undefined" && "caches" in globalThis && "WebSocketPair" in globalThis;
@@ -1932,6 +2069,9 @@ var veryfrontConfigSchema = z5.object({
1932
2069
  }).partial().optional(),
1933
2070
  router: z5.enum(["app", "pages"]).optional(),
1934
2071
  defaultLayout: z5.string().optional(),
2072
+ layout: z5.string().optional(),
2073
+ provider: z5.string().optional(),
2074
+ app: z5.string().optional(),
1935
2075
  theme: z5.object({ colors: z5.record(z5.string()).optional() }).partial().optional(),
1936
2076
  build: z5.object({
1937
2077
  outDir: z5.string().optional(),
@@ -2027,6 +2167,8 @@ var veryfrontConfigSchema = z5.object({
2027
2167
  apiBaseUrl: z5.string().url(),
2028
2168
  apiToken: z5.string(),
2029
2169
  projectSlug: z5.string(),
2170
+ proxyMode: z5.boolean().optional(),
2171
+ productionMode: z5.boolean().optional(),
2030
2172
  cache: z5.object({
2031
2173
  enabled: z5.boolean().optional(),
2032
2174
  ttl: z5.number().int().positive().optional(),
@@ -2088,6 +2230,9 @@ function findUnknownTopLevelKeys(input) {
2088
2230
  "experimental",
2089
2231
  "router",
2090
2232
  "defaultLayout",
2233
+ "layout",
2234
+ "provider",
2235
+ "app",
2091
2236
  "theme",
2092
2237
  "build",
2093
2238
  "cache",
@@ -2105,12 +2250,12 @@ function findUnknownTopLevelKeys(input) {
2105
2250
  }
2106
2251
 
2107
2252
  // src/core/config/loader.ts
2108
- import { join } from "node:path";
2253
+ import { dirname, join } from "node:path";
2109
2254
 
2110
2255
  // deno.json
2111
2256
  var deno_default = {
2112
2257
  name: "veryfront",
2113
- version: "0.0.71",
2258
+ version: "0.0.74",
2114
2259
  nodeModulesDir: "auto",
2115
2260
  exclude: [
2116
2261
  "npm/",
@@ -2195,12 +2340,12 @@ var deno_default = {
2195
2340
  csstype: "https://esm.sh/csstype@3.2.3",
2196
2341
  "@types/react": "https://esm.sh/@types/react@18.3.27?deps=csstype@3.2.3",
2197
2342
  "@types/react-dom": "https://esm.sh/@types/react-dom@18.3.7?deps=csstype@3.2.3",
2198
- react: "https://esm.sh/react@18.3.1",
2199
- "react-dom": "https://esm.sh/react-dom@18.3.1",
2200
- "react-dom/server": "https://esm.sh/react-dom@18.3.1/server",
2201
- "react-dom/client": "https://esm.sh/react-dom@18.3.1/client",
2202
- "react/jsx-runtime": "https://esm.sh/react@18.3.1/jsx-runtime",
2203
- "react/jsx-dev-runtime": "https://esm.sh/react@18.3.1/jsx-dev-runtime",
2343
+ react: "npm:react@18.3.1",
2344
+ "react-dom": "npm:react-dom@18.3.1",
2345
+ "react-dom/server": "npm:react-dom@18.3.1/server.node",
2346
+ "react-dom/client": "npm:react-dom@18.3.1/client",
2347
+ "react/jsx-runtime": "npm:react@18.3.1/jsx-runtime",
2348
+ "react/jsx-dev-runtime": "npm:react@18.3.1/jsx-dev-runtime",
2204
2349
  "@mdx-js/mdx": "npm:@mdx-js/mdx@3.0.0",
2205
2350
  "@mdx-js/react": "npm:@mdx-js/react@3.0.0",
2206
2351
  "unist-util-visit": "npm:unist-util-visit@5.0.0",
@@ -2210,27 +2355,36 @@ var deno_default = {
2210
2355
  "remark-frontmatter": "npm:remark-frontmatter@5.0.0",
2211
2356
  "rehype-highlight": "npm:rehype-highlight@7.0.2",
2212
2357
  "rehype-slug": "npm:rehype-slug@6.0.0",
2213
- esbuild: "https://deno.land/x/esbuild@v0.20.1/wasm.js",
2214
- "esbuild/mod.js": "https://deno.land/x/esbuild@v0.20.1/mod.js",
2358
+ esbuild: "npm:esbuild@0.20.2",
2359
+ "esbuild/mod.js": "npm:esbuild@0.20.2",
2215
2360
  "es-module-lexer": "npm:es-module-lexer@1.5.0",
2216
- zod: "npm:zod@3.23.8",
2361
+ zod: "npm:zod@3.25.76",
2217
2362
  "mime-types": "npm:mime-types@2.1.35",
2218
2363
  mdast: "npm:@types/mdast@4.0.3",
2219
2364
  hast: "npm:@types/hast@3.0.3",
2220
2365
  unist: "npm:@types/unist@3.0.2",
2221
2366
  unified: "npm:unified@11.0.5",
2222
- ai: "https://esm.sh/ai@5.0.76?deps=react@18.3.1,react-dom@18.3.1",
2223
- "ai/react": "https://esm.sh/@ai-sdk/react@2.0.1?deps=react@18.3.1,react-dom@18.3.1",
2224
- "@ai-sdk/react": "https://esm.sh/@ai-sdk/react@2.0.1?deps=react@18.3.1,react-dom@18.3.1",
2367
+ ai: "npm:ai@5.0.76",
2368
+ "ai/react": "npm:@ai-sdk/react@2.0.1",
2369
+ "@ai-sdk/react": "npm:@ai-sdk/react@2.0.1",
2225
2370
  "@ai-sdk/openai": "https://esm.sh/@ai-sdk/openai@2.0.1",
2226
2371
  "@ai-sdk/anthropic": "https://esm.sh/@ai-sdk/anthropic@2.0.1",
2227
2372
  unocss: "https://esm.sh/unocss@0.59.0",
2228
2373
  "@unocss/core": "https://esm.sh/@unocss/core@0.59.0",
2229
2374
  "@unocss/preset-wind": "https://esm.sh/@unocss/preset-wind@0.59.0",
2375
+ "next-themes": "npm:next-themes@0.3.0",
2230
2376
  redis: "npm:redis",
2231
2377
  pg: "npm:pg",
2232
2378
  "@opentelemetry/api": "npm:@opentelemetry/api@1",
2233
- "@opentelemetry/core": "npm:@opentelemetry/core@1"
2379
+ "@opentelemetry/core": "npm:@opentelemetry/core@1",
2380
+ "@opentelemetry/sdk-trace-base": "npm:@opentelemetry/sdk-trace-base@1",
2381
+ "@opentelemetry/exporter-trace-otlp-http": "npm:@opentelemetry/exporter-trace-otlp-http@0.57",
2382
+ "@opentelemetry/resources": "npm:@opentelemetry/resources@1",
2383
+ "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@1",
2384
+ "@babel/parser": "npm:@babel/parser@7.26.3",
2385
+ "@babel/traverse": "npm:@babel/traverse@7.26.3",
2386
+ "@babel/generator": "npm:@babel/generator@7.26.3",
2387
+ "@babel/types": "npm:@babel/types@7.26.3"
2234
2388
  },
2235
2389
  compilerOptions: {
2236
2390
  jsx: "react-jsx",
@@ -2238,7 +2392,7 @@ var deno_default = {
2238
2392
  strict: true,
2239
2393
  noImplicitAny: true,
2240
2394
  noUncheckedIndexedAccess: true,
2241
- types: [],
2395
+ types: ["npm:@types/react@18"],
2242
2396
  lib: [
2243
2397
  "deno.window",
2244
2398
  "dom",
@@ -2253,9 +2407,9 @@ var deno_default = {
2253
2407
  build: "deno compile --allow-all --output ../../bin/veryfront src/cli/main.ts",
2254
2408
  "build:npm": "deno run -A scripts/build-npm.ts",
2255
2409
  release: "deno run -A scripts/release.ts",
2256
- test: "DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --unstable-worker-options --unstable-net",
2257
- "test:unit": "DENO_JOBS=1 deno test --parallel --allow-all --v8-flags=--max-old-space-size=8192 --ignore=tests --unstable-worker-options --unstable-net",
2258
- "test:integration": "DENO_JOBS=1 deno test --parallel --fail-fast --allow-all tests --unstable-worker-options --unstable-net",
2410
+ test: "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --unstable-worker-options --unstable-net",
2411
+ "test:unit": "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --allow-all --v8-flags=--max-old-space-size=8192 --ignore=tests,src/ai/workflow/__tests__ --unstable-worker-options --unstable-net",
2412
+ "test:integration": "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --fail-fast --allow-all tests --unstable-worker-options --unstable-net",
2259
2413
  "test:coverage": "rm -rf coverage && DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --coverage=coverage --unstable-worker-options --unstable-net || exit 1",
2260
2414
  "test:coverage:unit": "rm -rf coverage && DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --coverage=coverage --ignore=tests --unstable-worker-options --unstable-net || exit 1",
2261
2415
  "test:coverage:integration": "rm -rf coverage && DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --coverage=coverage tests --unstable-worker-options --unstable-net || exit 1",
@@ -2317,6 +2471,7 @@ var deno_default = {
2317
2471
 
2318
2472
  // src/core/utils/version.ts
2319
2473
  var VERSION = getEnv("VERYFRONT_VERSION") || (typeof deno_default.version === "string" ? deno_default.version : "0.0.0");
2474
+ var SERVER_START_TIME = Date.now();
2320
2475
 
2321
2476
  // src/core/utils/constants/cdn.ts
2322
2477
  var ESM_CDN_BASE = "https://esm.sh";
@@ -2432,6 +2587,166 @@ var DEV_SERVER_ENDPOINTS = {
2432
2587
  ERROR_OVERLAY: INTERNAL_ENDPOINTS.ERROR_OVERLAY
2433
2588
  };
2434
2589
 
2590
+ // src/platform/compat/fs.ts
2591
+ var NodeFileSystem = class {
2592
+ constructor() {
2593
+ this.fs = null;
2594
+ this.os = null;
2595
+ this.path = null;
2596
+ this.initialized = false;
2597
+ }
2598
+ async ensureInitialized() {
2599
+ if (this.initialized)
2600
+ return;
2601
+ if (!isNode) {
2602
+ throw toError(createError({
2603
+ type: "not_supported",
2604
+ message: "Node.js fs modules not available",
2605
+ feature: "Node.js"
2606
+ }));
2607
+ }
2608
+ const [fsModule, osModule, pathModule] = await Promise.all([
2609
+ import("node:fs/promises"),
2610
+ import("node:os"),
2611
+ import("node:path")
2612
+ ]);
2613
+ this.fs = fsModule;
2614
+ this.os = osModule;
2615
+ this.path = pathModule;
2616
+ this.initialized = true;
2617
+ }
2618
+ async readTextFile(path) {
2619
+ await this.ensureInitialized();
2620
+ return await this.fs.readFile(path, { encoding: "utf8" });
2621
+ }
2622
+ async readFile(path) {
2623
+ await this.ensureInitialized();
2624
+ return await this.fs.readFile(path);
2625
+ }
2626
+ async writeTextFile(path, data) {
2627
+ await this.ensureInitialized();
2628
+ await this.fs.writeFile(path, data, { encoding: "utf8" });
2629
+ }
2630
+ async writeFile(path, data) {
2631
+ await this.ensureInitialized();
2632
+ await this.fs.writeFile(path, data);
2633
+ }
2634
+ async exists(path) {
2635
+ await this.ensureInitialized();
2636
+ try {
2637
+ await this.fs.access(path);
2638
+ return true;
2639
+ } catch (error) {
2640
+ if (error.code === "ENOENT") {
2641
+ return false;
2642
+ }
2643
+ throw error;
2644
+ }
2645
+ }
2646
+ async stat(path) {
2647
+ await this.ensureInitialized();
2648
+ const stat = await this.fs.stat(path);
2649
+ return {
2650
+ isFile: stat.isFile(),
2651
+ isDirectory: stat.isDirectory(),
2652
+ isSymlink: stat.isSymbolicLink(),
2653
+ size: stat.size,
2654
+ mtime: stat.mtime
2655
+ };
2656
+ }
2657
+ async mkdir(path, options) {
2658
+ await this.ensureInitialized();
2659
+ await this.fs.mkdir(path, { recursive: options?.recursive ?? false });
2660
+ }
2661
+ async *readDir(path) {
2662
+ await this.ensureInitialized();
2663
+ const entries = await this.fs.readdir(path, { withFileTypes: true });
2664
+ for (const entry of entries) {
2665
+ yield {
2666
+ name: entry.name,
2667
+ isFile: entry.isFile(),
2668
+ isDirectory: entry.isDirectory()
2669
+ };
2670
+ }
2671
+ }
2672
+ async remove(path, options) {
2673
+ await this.ensureInitialized();
2674
+ await this.fs.rm(path, {
2675
+ recursive: options?.recursive ?? false,
2676
+ force: options?.recursive ?? false
2677
+ });
2678
+ }
2679
+ async makeTempDir(options) {
2680
+ await this.ensureInitialized();
2681
+ const tempDir = this.path.join(
2682
+ this.os.tmpdir(),
2683
+ `${options?.prefix ?? "tmp-"}${Math.random().toString(36).substring(2, 8)}`
2684
+ );
2685
+ await this.fs.mkdir(tempDir, { recursive: true });
2686
+ return tempDir;
2687
+ }
2688
+ };
2689
+ var DenoFileSystem = class {
2690
+ async readTextFile(path) {
2691
+ return await Deno.readTextFile(path);
2692
+ }
2693
+ async readFile(path) {
2694
+ return await Deno.readFile(path);
2695
+ }
2696
+ async writeTextFile(path, data) {
2697
+ await Deno.writeTextFile(path, data);
2698
+ }
2699
+ async writeFile(path, data) {
2700
+ await Deno.writeFile(path, data);
2701
+ }
2702
+ async exists(path) {
2703
+ try {
2704
+ await Deno.stat(path);
2705
+ return true;
2706
+ } catch (error) {
2707
+ if (error instanceof Deno.errors.NotFound) {
2708
+ return false;
2709
+ }
2710
+ throw error;
2711
+ }
2712
+ }
2713
+ async stat(path) {
2714
+ const stat = await Deno.stat(path);
2715
+ return {
2716
+ isFile: stat.isFile,
2717
+ isDirectory: stat.isDirectory,
2718
+ isSymlink: stat.isSymlink,
2719
+ size: stat.size,
2720
+ mtime: stat.mtime
2721
+ };
2722
+ }
2723
+ async mkdir(path, options) {
2724
+ await Deno.mkdir(path, { recursive: options?.recursive ?? false });
2725
+ }
2726
+ async *readDir(path) {
2727
+ for await (const entry of Deno.readDir(path)) {
2728
+ yield {
2729
+ name: entry.name,
2730
+ isFile: entry.isFile,
2731
+ isDirectory: entry.isDirectory
2732
+ };
2733
+ }
2734
+ }
2735
+ async remove(path, options) {
2736
+ await Deno.remove(path, { recursive: options?.recursive ?? false });
2737
+ }
2738
+ async makeTempDir(options) {
2739
+ return await Deno.makeTempDir({ prefix: options?.prefix });
2740
+ }
2741
+ };
2742
+ function createFileSystem() {
2743
+ if (isDeno) {
2744
+ return new DenoFileSystem();
2745
+ } else {
2746
+ return new NodeFileSystem();
2747
+ }
2748
+ }
2749
+
2435
2750
  // src/core/config/loader.ts
2436
2751
  function getDefaultImportMapForConfig() {
2437
2752
  return { imports: getReactImportMap(REACT_DEFAULT_VERSION) };
@@ -2568,7 +2883,62 @@ var ConfigValidationError = class extends Error {
2568
2883
  this.name = "ConfigValidationError";
2569
2884
  }
2570
2885
  };
2571
- async function loadAndMergeConfig(configPath, projectDir) {
2886
+ function isVirtualFilesystem(adapter) {
2887
+ const wrappedAdapter = adapter?.fs?.fsAdapter;
2888
+ const adapterName = wrappedAdapter?.constructor?.name;
2889
+ return adapterName === "VeryfrontFSAdapter";
2890
+ }
2891
+ async function loadConfigFromVirtualFS(configPath, projectDir, adapter) {
2892
+ const fs2 = createFileSystem();
2893
+ const content = await adapter.fs.readFile(configPath);
2894
+ const source = typeof content === "string" ? content : new TextDecoder().decode(content);
2895
+ serverLogger.debug(`[CONFIG] Loading config from virtual FS: ${configPath}`);
2896
+ const isTsx = configPath.endsWith(".tsx");
2897
+ const loader = isTsx ? "tsx" : configPath.endsWith(".ts") ? "ts" : "js";
2898
+ const { build } = await import("esbuild");
2899
+ const result = await build({
2900
+ bundle: false,
2901
+ // Config files shouldn't need bundling
2902
+ write: false,
2903
+ format: "esm",
2904
+ platform: "neutral",
2905
+ target: "es2022",
2906
+ stdin: {
2907
+ contents: source,
2908
+ loader,
2909
+ resolveDir: dirname(configPath),
2910
+ sourcefile: configPath
2911
+ }
2912
+ });
2913
+ if (result.errors && result.errors.length > 0) {
2914
+ const first = result.errors[0]?.text || "unknown error";
2915
+ throw new ConfigValidationError(`Failed to transpile config: ${first}`);
2916
+ }
2917
+ const js = result.outputFiles?.[0]?.text ?? "export default {}";
2918
+ const tempDir = await fs2.makeTempDir({ prefix: "vf-config-" });
2919
+ const tempFile = join(tempDir, "config.mjs");
2920
+ try {
2921
+ await fs2.writeTextFile(tempFile, js);
2922
+ const configModule = await import(`file://${tempFile}?v=${Date.now()}`);
2923
+ const userConfig = configModule.default || configModule;
2924
+ if (userConfig === null || typeof userConfig !== "object" || Array.isArray(userConfig)) {
2925
+ throw new ConfigValidationError(
2926
+ `Expected object, received ${userConfig === null ? "null" : typeof userConfig}`
2927
+ );
2928
+ }
2929
+ validateCorsConfig(userConfig);
2930
+ validateConfigShape(userConfig);
2931
+ const merged = mergeConfigs(userConfig);
2932
+ configCacheByProject.set(projectDir, { revision: cacheRevision, config: merged });
2933
+ return merged;
2934
+ } finally {
2935
+ await fs2.remove(tempDir, { recursive: true });
2936
+ }
2937
+ }
2938
+ async function loadAndMergeConfig(configPath, projectDir, adapter) {
2939
+ if (isVirtualFilesystem(adapter)) {
2940
+ return loadConfigFromVirtualFS(configPath, projectDir, adapter);
2941
+ }
2572
2942
  try {
2573
2943
  const configUrl = `file://${configPath}?t=${Date.now()}-${crypto.randomUUID()}`;
2574
2944
  const configModule = await import(configUrl);
@@ -2604,7 +2974,7 @@ async function getConfig(projectDir, adapter) {
2604
2974
  if (!exists)
2605
2975
  continue;
2606
2976
  try {
2607
- const merged = await loadAndMergeConfig(configPath, projectDir);
2977
+ const merged = await loadAndMergeConfig(configPath, projectDir, adapter);
2608
2978
  if (merged)
2609
2979
  return merged;
2610
2980
  } catch (error) {
@@ -2777,166 +3147,6 @@ function createMockAdapter() {
2777
3147
  };
2778
3148
  }
2779
3149
 
2780
- // src/platform/compat/fs.ts
2781
- var NodeFileSystem = class {
2782
- constructor() {
2783
- this.fs = null;
2784
- this.os = null;
2785
- this.path = null;
2786
- this.initialized = false;
2787
- }
2788
- async ensureInitialized() {
2789
- if (this.initialized)
2790
- return;
2791
- if (!isNode) {
2792
- throw toError(createError({
2793
- type: "not_supported",
2794
- message: "Node.js fs modules not available",
2795
- feature: "Node.js"
2796
- }));
2797
- }
2798
- const [fsModule, osModule, pathModule] = await Promise.all([
2799
- import("node:fs/promises"),
2800
- import("node:os"),
2801
- import("node:path")
2802
- ]);
2803
- this.fs = fsModule;
2804
- this.os = osModule;
2805
- this.path = pathModule;
2806
- this.initialized = true;
2807
- }
2808
- async readTextFile(path) {
2809
- await this.ensureInitialized();
2810
- return await this.fs.readFile(path, { encoding: "utf8" });
2811
- }
2812
- async readFile(path) {
2813
- await this.ensureInitialized();
2814
- return await this.fs.readFile(path);
2815
- }
2816
- async writeTextFile(path, data) {
2817
- await this.ensureInitialized();
2818
- await this.fs.writeFile(path, data, { encoding: "utf8" });
2819
- }
2820
- async writeFile(path, data) {
2821
- await this.ensureInitialized();
2822
- await this.fs.writeFile(path, data);
2823
- }
2824
- async exists(path) {
2825
- await this.ensureInitialized();
2826
- try {
2827
- await this.fs.access(path);
2828
- return true;
2829
- } catch (error) {
2830
- if (error.code === "ENOENT") {
2831
- return false;
2832
- }
2833
- throw error;
2834
- }
2835
- }
2836
- async stat(path) {
2837
- await this.ensureInitialized();
2838
- const stat = await this.fs.stat(path);
2839
- return {
2840
- isFile: stat.isFile(),
2841
- isDirectory: stat.isDirectory(),
2842
- isSymlink: stat.isSymbolicLink(),
2843
- size: stat.size,
2844
- mtime: stat.mtime
2845
- };
2846
- }
2847
- async mkdir(path, options) {
2848
- await this.ensureInitialized();
2849
- await this.fs.mkdir(path, { recursive: options?.recursive ?? false });
2850
- }
2851
- async *readDir(path) {
2852
- await this.ensureInitialized();
2853
- const entries = await this.fs.readdir(path, { withFileTypes: true });
2854
- for (const entry of entries) {
2855
- yield {
2856
- name: entry.name,
2857
- isFile: entry.isFile(),
2858
- isDirectory: entry.isDirectory()
2859
- };
2860
- }
2861
- }
2862
- async remove(path, options) {
2863
- await this.ensureInitialized();
2864
- await this.fs.rm(path, {
2865
- recursive: options?.recursive ?? false,
2866
- force: options?.recursive ?? false
2867
- });
2868
- }
2869
- async makeTempDir(options) {
2870
- await this.ensureInitialized();
2871
- const tempDir = this.path.join(
2872
- this.os.tmpdir(),
2873
- `${options?.prefix ?? "tmp-"}${Math.random().toString(36).substring(2, 8)}`
2874
- );
2875
- await this.fs.mkdir(tempDir, { recursive: true });
2876
- return tempDir;
2877
- }
2878
- };
2879
- var DenoFileSystem = class {
2880
- async readTextFile(path) {
2881
- return await Deno.readTextFile(path);
2882
- }
2883
- async readFile(path) {
2884
- return await Deno.readFile(path);
2885
- }
2886
- async writeTextFile(path, data) {
2887
- await Deno.writeTextFile(path, data);
2888
- }
2889
- async writeFile(path, data) {
2890
- await Deno.writeFile(path, data);
2891
- }
2892
- async exists(path) {
2893
- try {
2894
- await Deno.stat(path);
2895
- return true;
2896
- } catch (error) {
2897
- if (error instanceof Deno.errors.NotFound) {
2898
- return false;
2899
- }
2900
- throw error;
2901
- }
2902
- }
2903
- async stat(path) {
2904
- const stat = await Deno.stat(path);
2905
- return {
2906
- isFile: stat.isFile,
2907
- isDirectory: stat.isDirectory,
2908
- isSymlink: stat.isSymlink,
2909
- size: stat.size,
2910
- mtime: stat.mtime
2911
- };
2912
- }
2913
- async mkdir(path, options) {
2914
- await Deno.mkdir(path, { recursive: options?.recursive ?? false });
2915
- }
2916
- async *readDir(path) {
2917
- for await (const entry of Deno.readDir(path)) {
2918
- yield {
2919
- name: entry.name,
2920
- isFile: entry.isFile,
2921
- isDirectory: entry.isDirectory
2922
- };
2923
- }
2924
- }
2925
- async remove(path, options) {
2926
- await Deno.remove(path, { recursive: options?.recursive ?? false });
2927
- }
2928
- async makeTempDir(options) {
2929
- return await Deno.makeTempDir({ prefix: options?.prefix });
2930
- }
2931
- };
2932
- function createFileSystem() {
2933
- if (isDeno) {
2934
- return new DenoFileSystem();
2935
- } else {
2936
- return new NodeFileSystem();
2937
- }
2938
- }
2939
-
2940
3150
  // src/platform/compat/path-helper.ts
2941
3151
  import nodePath from "node:path";
2942
3152
  var pathMod = null;
@@ -2952,7 +3162,7 @@ function getPathMod() {
2952
3162
  return pathMod;
2953
3163
  return nodePath;
2954
3164
  }
2955
- var dirname = (path) => getPathMod().dirname(path);
3165
+ var dirname2 = (path) => getPathMod().dirname(path);
2956
3166
  var join2 = (...paths) => getPathMod().join(...paths);
2957
3167
  var resolve = (...paths) => getPathMod().resolve(...paths);
2958
3168
  var extname = (path) => getPathMod().extname(path);
@@ -2999,7 +3209,7 @@ function createFsAdapterPlugin(fsAdapter) {
2999
3209
  build.onResolve(
3000
3210
  { filter: /^\.\.?\// },
3001
3211
  async (args) => {
3002
- const importerDir = args.importer ? dirname(args.importer) : args.resolveDir;
3212
+ const importerDir = args.importer ? dirname2(args.importer) : args.resolveDir;
3003
3213
  const basePath = resolve(importerDir, args.path);
3004
3214
  const resolvedPath = await resolveWithExtensions(basePath);
3005
3215
  if (resolvedPath) {
@@ -3026,7 +3236,7 @@ function createFsAdapterPlugin(fsAdapter) {
3026
3236
  contents: content,
3027
3237
  loader,
3028
3238
  // Set resolveDir for nested imports from this file
3029
- resolveDir: dirname(args.path)
3239
+ resolveDir: dirname2(args.path)
3030
3240
  };
3031
3241
  } catch (error) {
3032
3242
  return {
@@ -3061,7 +3271,7 @@ async function importModule(file, context) {
3061
3271
  const isJsx = filePath.endsWith(".jsx");
3062
3272
  const loader = isTsx ? "tsx" : isJsx ? "jsx" : filePath.endsWith(".ts") ? "ts" : "js";
3063
3273
  const { build } = await import("esbuild");
3064
- const fileDir = dirname(filePath);
3274
+ const fileDir = dirname2(filePath);
3065
3275
  const relativeImports = [];
3066
3276
  if (isDeno) {
3067
3277
  const relativeImportPattern = /from\s+["'](\.\.[^"']+)["']/g;
@@ -3812,6 +4022,9 @@ var InMemoryBundleManifestStore = class {
3812
4022
  };
3813
4023
  var manifestStore = new InMemoryBundleManifestStore();
3814
4024
 
4025
+ // src/core/utils/perf-timer.ts
4026
+ var enabled = typeof process !== "undefined" ? process.env?.VERYFRONT_PERF === "1" : typeof Deno !== "undefined" ? Deno.env.get("VERYFRONT_PERF") === "1" : false;
4027
+
3815
4028
  // src/observability/tracing/config.ts
3816
4029
  var DEFAULT_CONFIG2 = {
3817
4030
  enabled: false,
@@ -4108,7 +4321,27 @@ async function withSpan(name, fn, options = {}) {
4108
4321
  );
4109
4322
  }
4110
4323
 
4111
- // src/ai/agent/runtime.ts
4324
+ // src/ai/config/defaults.ts
4325
+ var AGENT_DEFAULTS = {
4326
+ /** Maximum tokens for completion */
4327
+ maxTokens: 4096,
4328
+ /** Default temperature for generation */
4329
+ temperature: 0.7,
4330
+ /** Maximum agent loop steps */
4331
+ maxSteps: 20,
4332
+ /** Default memory type */
4333
+ memoryType: "conversation",
4334
+ /** Default memory max tokens */
4335
+ memoryMaxTokens: 4e3
4336
+ };
4337
+ var STREAMING_DEFAULTS = {
4338
+ /** Maximum buffer size for streaming (1MB) */
4339
+ maxBufferSize: 1024 * 1024,
4340
+ /** Chunk size for stream processing */
4341
+ chunkSize: 16384
4342
+ };
4343
+
4344
+ // src/ai/agent/streaming/stream-events.ts
4112
4345
  import { z as z6 } from "zod";
4113
4346
  var AgentStreamEventSchema = z6.discriminatedUnion("type", [
4114
4347
  z6.object({
@@ -4148,8 +4381,8 @@ var AgentStreamEventSchema = z6.discriminatedUnion("type", [
4148
4381
  })
4149
4382
  })
4150
4383
  ]);
4151
- var DEFAULT_MAX_TOKENS = 4096;
4152
- var DEFAULT_TEMPERATURE = 0.7;
4384
+
4385
+ // src/ai/agent/message-converter.ts
4153
4386
  function convertMessageToProvider(msg) {
4154
4387
  const content = getTextFromParts(msg.parts);
4155
4388
  const providerMsg = {
@@ -4179,6 +4412,11 @@ function convertMessageToProvider(msg) {
4179
4412
  }
4180
4413
  return providerMsg;
4181
4414
  }
4415
+
4416
+ // src/ai/agent/runtime.ts
4417
+ var DEFAULT_MAX_TOKENS = AGENT_DEFAULTS.maxTokens;
4418
+ var DEFAULT_TEMPERATURE = AGENT_DEFAULTS.temperature;
4419
+ var MAX_STREAM_BUFFER_SIZE = STREAMING_DEFAULTS.maxBufferSize;
4182
4420
  var AgentRuntime = class {
4183
4421
  constructor(id, config) {
4184
4422
  this.status = "idle";
@@ -4609,6 +4847,10 @@ var AgentRuntime = class {
4609
4847
  if (done)
4610
4848
  break;
4611
4849
  partial += decoder.decode(value, { stream: true });
4850
+ if (partial.length > MAX_STREAM_BUFFER_SIZE) {
4851
+ serverLogger.warn("[AGENT] Stream buffer exceeded max size, truncating");
4852
+ partial = partial.slice(-MAX_STREAM_BUFFER_SIZE / 2);
4853
+ }
4612
4854
  const segments = partial.split("\n");
4613
4855
  partial = segments.pop() ?? "";
4614
4856
  const lines = segments.filter((line) => line.trim());
@@ -5460,7 +5702,6 @@ import {
5460
5702
  createIdGenerator,
5461
5703
  embed,
5462
5704
  embedMany,
5463
- experimental_createMCPClient,
5464
5705
  experimental_generateImage,
5465
5706
  experimental_generateSpeech,
5466
5707
  experimental_transcribe,
@@ -6935,9 +7176,16 @@ var DAGExecutor = class {
6935
7176
  context,
6936
7177
  nodeStates
6937
7178
  );
7179
+ case "loop":
7180
+ return await this.executeLoopNode(
7181
+ node,
7182
+ config,
7183
+ context,
7184
+ nodeStates
7185
+ );
6938
7186
  default:
6939
7187
  throw new Error(
6940
- `Unknown node type "${config.type}" for node "${node.id}". Valid types are: step, parallel, map, branch, wait, subWorkflow`
7188
+ `Unknown node type "${config.type}" for node "${node.id}". Valid types are: step, parallel, map, branch, wait, subWorkflow, loop`
6941
7189
  );
6942
7190
  }
6943
7191
  }
@@ -7095,6 +7343,155 @@ var DAGExecutor = class {
7095
7343
  waiting: result.waiting
7096
7344
  };
7097
7345
  }
7346
+ /**
7347
+ * Execute a loop node
7348
+ */
7349
+ async executeLoopNode(node, config, context, nodeStates) {
7350
+ const startTime = Date.now();
7351
+ const previousResults = [];
7352
+ let iteration = 0;
7353
+ let exitReason = "condition";
7354
+ let lastError;
7355
+ const existingLoopState = context[`${node.id}_loop_state`];
7356
+ if (existingLoopState) {
7357
+ iteration = existingLoopState.iteration;
7358
+ previousResults.push(...existingLoopState.previousResults);
7359
+ }
7360
+ while (iteration < config.maxIterations) {
7361
+ const loopContext = {
7362
+ iteration,
7363
+ totalIterations: iteration,
7364
+ previousResults: [...previousResults],
7365
+ isFirstIteration: iteration === 0,
7366
+ isLastAllowedIteration: iteration === config.maxIterations - 1
7367
+ };
7368
+ const shouldContinue = await config.while(context, loopContext);
7369
+ if (!shouldContinue) {
7370
+ exitReason = "condition";
7371
+ break;
7372
+ }
7373
+ const steps = typeof config.steps === "function" ? config.steps(context, loopContext) : config.steps;
7374
+ const result = await this.execute(steps, {
7375
+ id: `${node.id}_iter_${iteration}`,
7376
+ workflowId: "",
7377
+ status: "running",
7378
+ input: context.input,
7379
+ nodeStates: {},
7380
+ currentNodes: [],
7381
+ context: { ...context, _loop: loopContext },
7382
+ checkpoints: [],
7383
+ pendingApprovals: [],
7384
+ createdAt: /* @__PURE__ */ new Date()
7385
+ });
7386
+ if (result.waiting) {
7387
+ Object.assign(nodeStates, result.nodeStates);
7388
+ const state2 = {
7389
+ nodeId: node.id,
7390
+ status: "running",
7391
+ output: {
7392
+ iteration,
7393
+ waiting: true,
7394
+ previousResults
7395
+ },
7396
+ attempt: 1,
7397
+ startedAt: new Date(startTime)
7398
+ };
7399
+ return {
7400
+ state: state2,
7401
+ contextUpdates: {
7402
+ ...result.context,
7403
+ [`${node.id}_loop_state`]: { iteration, previousResults }
7404
+ },
7405
+ waiting: true
7406
+ };
7407
+ }
7408
+ if (result.error) {
7409
+ lastError = result.error;
7410
+ exitReason = "error";
7411
+ break;
7412
+ }
7413
+ previousResults.push(result.context);
7414
+ Object.assign(context, result.context);
7415
+ Object.assign(nodeStates, result.nodeStates);
7416
+ if (config.delay && iteration < config.maxIterations - 1) {
7417
+ const delayMs = typeof config.delay === "number" ? config.delay : this.parseDuration(config.delay);
7418
+ await this.sleep(delayMs);
7419
+ }
7420
+ iteration++;
7421
+ }
7422
+ if (iteration >= config.maxIterations && exitReason !== "condition") {
7423
+ exitReason = "maxIterations";
7424
+ }
7425
+ const finalLoopContext = {
7426
+ iteration,
7427
+ totalIterations: iteration,
7428
+ previousResults,
7429
+ isFirstIteration: false,
7430
+ isLastAllowedIteration: true
7431
+ };
7432
+ let completionUpdates = {};
7433
+ if (exitReason === "maxIterations" && config.onMaxIterations) {
7434
+ completionUpdates = await config.onMaxIterations(context, finalLoopContext);
7435
+ } else if (exitReason === "condition" && config.onComplete) {
7436
+ completionUpdates = await config.onComplete(context, finalLoopContext);
7437
+ }
7438
+ const output = {
7439
+ exitReason,
7440
+ iterations: iteration,
7441
+ previousResults,
7442
+ ...completionUpdates
7443
+ };
7444
+ const state = {
7445
+ nodeId: node.id,
7446
+ status: exitReason === "error" ? "failed" : "completed",
7447
+ output,
7448
+ error: lastError,
7449
+ attempt: 1,
7450
+ startedAt: new Date(startTime),
7451
+ completedAt: /* @__PURE__ */ new Date()
7452
+ };
7453
+ this.config.onNodeComplete?.(node.id, state);
7454
+ return {
7455
+ state,
7456
+ contextUpdates: {
7457
+ [node.id]: output,
7458
+ ...completionUpdates
7459
+ },
7460
+ waiting: false
7461
+ };
7462
+ }
7463
+ /**
7464
+ * Parse duration string to milliseconds
7465
+ */
7466
+ parseDuration(duration) {
7467
+ if (typeof duration === "number")
7468
+ return duration;
7469
+ const match = duration.match(/^(\d+)(ms|s|m|h|d)$/);
7470
+ if (!match)
7471
+ return 0;
7472
+ const value = parseInt(match[1], 10);
7473
+ const unit = match[2];
7474
+ switch (unit) {
7475
+ case "ms":
7476
+ return value;
7477
+ case "s":
7478
+ return value * 1e3;
7479
+ case "m":
7480
+ return value * 60 * 1e3;
7481
+ case "h":
7482
+ return value * 60 * 60 * 1e3;
7483
+ case "d":
7484
+ return value * 24 * 60 * 60 * 1e3;
7485
+ default:
7486
+ return 0;
7487
+ }
7488
+ }
7489
+ /**
7490
+ * Sleep for specified milliseconds
7491
+ */
7492
+ sleep(ms) {
7493
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
7494
+ }
7098
7495
  /**
7099
7496
  * Execute a step node
7100
7497
  */
@@ -7489,6 +7886,12 @@ var CheckpointManager = class {
7489
7886
  };
7490
7887
 
7491
7888
  // src/ai/workflow/executor/step-executor.ts
7889
+ var DEFAULT_RETRY = {
7890
+ maxAttempts: 1,
7891
+ backoff: "exponential",
7892
+ initialDelay: 1e3,
7893
+ maxDelay: 3e4
7894
+ };
7492
7895
  var DEFAULT_STEP_TIMEOUT_MS = 5 * 60 * 1e3;
7493
7896
  var StepExecutor = class {
7494
7897
  constructor(config = {}) {
@@ -7498,7 +7901,7 @@ var StepExecutor = class {
7498
7901
  };
7499
7902
  }
7500
7903
  /**
7501
- * Execute a step node
7904
+ * Execute a step node with retry support
7502
7905
  */
7503
7906
  async execute(node, context) {
7504
7907
  const startTime = Date.now();
@@ -7508,30 +7911,96 @@ var StepExecutor = class {
7508
7911
  `StepExecutor can only execute 'step' nodes, but node "${node.id}" has type '${config.type}'. This is likely a bug in the DAG executor routing.`
7509
7912
  );
7510
7913
  }
7511
- try {
7512
- const resolvedInput = await this.resolveInput(config.input, context);
7513
- this.config.onStepStart?.(node.id, resolvedInput);
7514
- const timeout = config.timeout ? parseDuration(config.timeout) : this.config.defaultTimeout;
7515
- const output = await this.executeWithTimeout(
7516
- () => this.executeStep(config, resolvedInput, context),
7517
- timeout,
7518
- node.id
7519
- );
7520
- this.config.onStepComplete?.(node.id, output);
7521
- return {
7522
- success: true,
7523
- output,
7524
- executionTime: Date.now() - startTime
7525
- };
7526
- } catch (error) {
7527
- const errorMessage = error instanceof Error ? error.message : String(error);
7528
- this.config.onStepError?.(node.id, error);
7529
- return {
7530
- success: false,
7531
- error: errorMessage,
7532
- executionTime: Date.now() - startTime
7533
- };
7914
+ const retryConfig = { ...DEFAULT_RETRY, ...config.retry };
7915
+ const maxAttempts = retryConfig.maxAttempts ?? 1;
7916
+ let lastError;
7917
+ let attempt = 0;
7918
+ while (attempt < maxAttempts) {
7919
+ attempt++;
7920
+ try {
7921
+ const resolvedInput = await this.resolveInput(config.input, context);
7922
+ this.config.onStepStart?.(node.id, resolvedInput);
7923
+ const timeout = config.timeout ? parseDuration(config.timeout) : this.config.defaultTimeout;
7924
+ const output = await this.executeWithTimeout(
7925
+ () => this.executeStep(config, resolvedInput, context),
7926
+ timeout,
7927
+ node.id
7928
+ );
7929
+ this.config.onStepComplete?.(node.id, output);
7930
+ return {
7931
+ success: true,
7932
+ output,
7933
+ executionTime: Date.now() - startTime
7934
+ };
7935
+ } catch (error) {
7936
+ lastError = error instanceof Error ? error : new Error(String(error));
7937
+ const shouldRetry = attempt < maxAttempts && this.isRetryableError(lastError, retryConfig);
7938
+ if (shouldRetry) {
7939
+ const delay2 = this.calculateRetryDelay(attempt, retryConfig);
7940
+ await this.sleep(delay2);
7941
+ continue;
7942
+ }
7943
+ this.config.onStepError?.(node.id, lastError);
7944
+ return {
7945
+ success: false,
7946
+ error: lastError.message,
7947
+ executionTime: Date.now() - startTime
7948
+ };
7949
+ }
7950
+ }
7951
+ return {
7952
+ success: false,
7953
+ error: lastError?.message ?? "Unknown error",
7954
+ executionTime: Date.now() - startTime
7955
+ };
7956
+ }
7957
+ /**
7958
+ * Check if error is retryable
7959
+ */
7960
+ isRetryableError(error, config) {
7961
+ if (config.retryIf) {
7962
+ return config.retryIf(error);
7963
+ }
7964
+ const retryablePatterns = [
7965
+ /timeout/i,
7966
+ /ECONNRESET/i,
7967
+ /ECONNREFUSED/i,
7968
+ /ETIMEDOUT/i,
7969
+ /rate limit/i,
7970
+ /429/,
7971
+ /503/,
7972
+ /502/
7973
+ ];
7974
+ return retryablePatterns.some((pattern) => pattern.test(error.message));
7975
+ }
7976
+ /**
7977
+ * Calculate retry delay based on backoff strategy
7978
+ */
7979
+ calculateRetryDelay(attempt, config) {
7980
+ const initialDelay = config.initialDelay ?? 1e3;
7981
+ const maxDelay = config.maxDelay ?? 3e4;
7982
+ let delay2;
7983
+ switch (config.backoff) {
7984
+ case "exponential":
7985
+ delay2 = initialDelay * Math.pow(2, attempt - 1);
7986
+ break;
7987
+ case "linear":
7988
+ delay2 = initialDelay * attempt;
7989
+ break;
7990
+ case "fixed":
7991
+ default:
7992
+ delay2 = initialDelay;
7993
+ break;
7534
7994
  }
7995
+ const jitter = delay2 * 0.1 * (Math.random() * 2 - 1);
7996
+ delay2 = Math.min(delay2 + jitter, maxDelay);
7997
+ return Math.floor(delay2);
7998
+ }
7999
+ /**
8000
+ * Sleep for specified milliseconds
8001
+ */
8002
+ sleep(ms) {
8003
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
7535
8004
  }
7536
8005
  /**
7537
8006
  * Resolve step input from context
@@ -8341,7 +8810,35 @@ var WorkflowClient = class {
8341
8810
  this.executor = new WorkflowExecutor({
8342
8811
  backend: this.backend,
8343
8812
  debug: this.debug,
8344
- ...config.executor
8813
+ ...config.executor,
8814
+ onWaiting: async (run, nodeId) => {
8815
+ const nodeState = run.nodeStates[nodeId];
8816
+ if (!nodeState?.input) {
8817
+ if (this.debug) {
8818
+ console.log(`[WorkflowClient] No wait config found for node: ${nodeId}`);
8819
+ }
8820
+ return;
8821
+ }
8822
+ const input = nodeState.input;
8823
+ if (input.type !== "approval") {
8824
+ return;
8825
+ }
8826
+ const waitConfig = {
8827
+ type: "wait",
8828
+ waitType: "approval",
8829
+ message: input.message,
8830
+ payload: input.payload
8831
+ };
8832
+ try {
8833
+ await this.approvalManager.createApproval(run, nodeId, waitConfig, run.context);
8834
+ if (this.debug) {
8835
+ console.log(`[WorkflowClient] Created approval for node: ${nodeId}`);
8836
+ }
8837
+ } catch (error) {
8838
+ console.error(`[WorkflowClient] Failed to create approval:`, error);
8839
+ }
8840
+ config.executor?.onWaiting?.(run, nodeId);
8841
+ }
8345
8842
  });
8346
8843
  this.approvalManager = new ApprovalManager({
8347
8844
  backend: this.backend,
@@ -8546,7 +9043,6 @@ export {
8546
9043
  dynamicTool,
8547
9044
  embed,
8548
9045
  embedMany,
8549
- experimental_createMCPClient,
8550
9046
  experimental_generateImage,
8551
9047
  experimental_generateSpeech,
8552
9048
  experimental_transcribe,